From 5adc73aad31c8cf68716f4d6a5fa99d540f9553e Mon Sep 17 00:00:00 2001 From: "niklase@google.com" Date: Thu, 7 Jul 2011 08:46:41 +0000 Subject: [PATCH] git-svn-id: http://webrtc.googlecode.com/svn/trunk@166 4adac7df-926f-26a2-2b94-8c16560cd09d --- modules/audio_coding/NetEQ/OWNERS | 2 - .../NetEQ/main/interface/webrtc_neteq.h | 215 - .../main/interface/webrtc_neteq_help_macros.h | 365 - .../main/interface/webrtc_neteq_internal.h | 274 - .../audio_coding/NetEQ/main/source/Android.mk | 87 - .../NetEQ/main/source/accelerate.c | 489 - .../audio_coding/NetEQ/main/source/automode.c | 717 - .../audio_coding/NetEQ/main/source/automode.h | 243 - .../NetEQ/main/source/bgn_update.c | 247 - .../NetEQ/main/source/buffer_stats.h | 95 - .../NetEQ/main/source/bufstats_decision.c | 413 - .../NetEQ/main/source/cng_internal.c | 155 - .../audio_coding/NetEQ/main/source/codec_db.c | 737 - .../audio_coding/NetEQ/main/source/codec_db.h | 126 - .../NetEQ/main/source/codec_db_defines.h | 89 - .../NetEQ/main/source/correlator.c | 132 - .../NetEQ/main/source/delay_logging.h | 34 - modules/audio_coding/NetEQ/main/source/dsp.c | 523 - modules/audio_coding/NetEQ/main/source/dsp.h | 788 - .../NetEQ/main/source/dsp_helpfunctions.c | 120 - .../NetEQ/main/source/dsp_helpfunctions.h | 220 - .../NetEQ/main/source/dtmf_buffer.c | 232 - .../NetEQ/main/source/dtmf_buffer.h | 101 - .../NetEQ/main/source/dtmf_tonegen.c | 371 - .../NetEQ/main/source/dtmf_tonegen.h | 73 - .../audio_coding/NetEQ/main/source/expand.c | 1204 - modules/audio_coding/NetEQ/main/source/mcu.h | 255 - .../NetEQ/main/source/mcu_address_init.c | 33 - .../NetEQ/main/source/mcu_dsp_common.c | 37 - .../NetEQ/main/source/mcu_dsp_common.h | 61 - .../NetEQ/main/source/mcu_reset.c | 118 - .../audio_coding/NetEQ/main/source/merge.c | 548 - .../NetEQ/main/source/min_distortion.c | 55 - .../NetEQ/main/source/mix_voice_unvoice.c | 41 - .../NetEQ/main/source/mute_signal.c | 33 - .../audio_coding/NetEQ/main/source/neteq.gyp | 301 - .../NetEQ/main/source/neteq_defines.h | 343 - .../NetEQ/main/source/neteq_error_codes.h | 79 - .../NetEQ/main/source/neteq_statistics.h | 67 - .../audio_coding/NetEQ/main/source/normal.c | 279 - .../NetEQ/main/source/packet_buffer.c | 707 - .../NetEQ/main/source/packet_buffer.h | 206 - .../NetEQ/main/source/peak_detection.c | 232 - .../NetEQ/main/source/preemptive_expand.c | 525 - .../NetEQ/main/source/random_vector.c | 54 - .../audio_coding/NetEQ/main/source/recin.c | 517 - .../audio_coding/NetEQ/main/source/recout.c | 1427 - modules/audio_coding/NetEQ/main/source/rtcp.c | 134 - modules/audio_coding/NetEQ/main/source/rtcp.h | 102 - modules/audio_coding/NetEQ/main/source/rtp.c | 240 - modules/audio_coding/NetEQ/main/source/rtp.h | 78 - .../audio_coding/NetEQ/main/source/set_fs.c | 78 - .../NetEQ/main/source/signal_mcu.c | 838 - .../NetEQ/main/source/split_and_insert.c | 141 - .../NetEQ/main/source/unmute_signal.c | 41 - .../NetEQ/main/source/webrtc_neteq.c | 1761 - .../NetEQ/main/test/NETEQTEST_CodecClass.cc | 629 - .../NetEQ/main/test/NETEQTEST_CodecClass.h | 293 - .../NetEQ/main/test/NETEQTEST_NetEQClass.cc | 370 - .../NetEQ/main/test/NETEQTEST_NetEQClass.h | 91 - .../NetEQ/main/test/NETEQTEST_RTPpacket.cc | 766 - .../NetEQ/main/test/NETEQTEST_RTPpacket.h | 86 - .../NetEQ/main/test/NetEqRTPplay.cc | 1756 - .../NetEQ/main/test/PayloadTypes.h | 76 - .../NetEQ/main/test/RTPanalyze.cc | 64 - .../audio_coding/NetEQ/main/test/RTPcat.cc | 87 - .../audio_coding/NetEQ/main/test/RTPchange.cc | 138 - .../audio_coding/NetEQ/main/test/RTPencode.cc | 1985 - .../audio_coding/NetEQ/main/test/RTPjitter.cc | 198 - .../NetEQ/main/test/RTPtimeshift.cc | 95 - .../main/test/delay_tool/parse_delay_file.m | 191 - .../main/test/delay_tool/plot_neteq_delay.m | 187 - .../audio_coding/NetEQ/main/test/ptypes.txt | 47 - .../codecs/CNG/main/interface/webrtc_cng.h | 236 - .../codecs/CNG/main/source/Android.mk | 51 - .../codecs/CNG/main/source/cng.gyp | 42 - .../codecs/CNG/main/source/cng_helpfuns.c | 64 - .../codecs/CNG/main/source/cng_helpfuns.h | 28 - .../codecs/CNG/main/source/webrtc_cng.c | 735 - .../audio_coding/codecs/CNG/main/test/CNG.cpp | 225 - .../codecs/CNG/main/test/StdAfx.cpp | 18 - .../codecs/CNG/main/test/StdAfx.h | 32 - .../G711/main/interface/g711_interface.h | 148 - .../codecs/G711/main/source/Android.mk | 47 - .../codecs/G711/main/source/g711.c | 83 - .../codecs/G711/main/source/g711.gyp | 57 - .../codecs/G711/main/source/g711.h | 382 - .../codecs/G711/main/source/g711_interface.c | 171 - .../codecs/G711/main/testG711/testG711.cpp | 171 - .../G722/main/interface/g722_interface.h | 190 - .../codecs/G722/main/source/Android.mk | 53 - .../codecs/G722/main/source/g722.gyp | 56 - .../codecs/G722/main/source/g722_decode.c | 407 - .../codecs/G722/main/source/g722_enc_dec.h | 154 - .../codecs/G722/main/source/g722_encode.c | 433 - .../codecs/G722/main/source/g722_interface.c | 115 - .../codecs/G722/main/testG722/testG722.cpp | 157 - modules/audio_coding/codecs/OWNERS | 3 - .../codecs/PCM16B/main/interface/pcm16b.h | 106 - .../codecs/PCM16B/main/source/Android.mk | 51 - .../codecs/PCM16B/main/source/pcm16b.c | 100 - .../codecs/PCM16B/main/source/pcm16b.gyp | 37 - .../audio_coding/codecs/iLBC/ilbc_test.gyp | 35 - .../iLBC/main/documentation/rfc3951.txt | 10867 ----- .../iLBC/main/documentation/rfc3952.txt | 731 - .../codecs/iLBC/main/interface/ilbc.h | 260 - .../codecs/iLBC/main/source/Android.mk | 119 - .../codecs/iLBC/main/source/abs_quant.c | 80 - .../codecs/iLBC/main/source/abs_quant.h | 39 - .../codecs/iLBC/main/source/abs_quant_loop.c | 95 - .../codecs/iLBC/main/source/abs_quant_loop.h | 37 - .../iLBC/main/source/augmented_cb_corr.c | 63 - .../iLBC/main/source/augmented_cb_corr.h | 42 - .../codecs/iLBC/main/source/bw_expand.c | 42 - .../codecs/iLBC/main/source/bw_expand.h | 36 - .../codecs/iLBC/main/source/cb_construct.c | 67 - .../codecs/iLBC/main/source/cb_construct.h | 38 - .../codecs/iLBC/main/source/cb_mem_energy.c | 79 - .../codecs/iLBC/main/source/cb_mem_energy.h | 34 - .../main/source/cb_mem_energy_augmentation.c | 67 - .../main/source/cb_mem_energy_augmentation.h | 31 - .../iLBC/main/source/cb_mem_energy_calc.c | 65 - .../iLBC/main/source/cb_mem_energy_calc.h | 33 - .../codecs/iLBC/main/source/cb_search.c | 396 - .../codecs/iLBC/main/source/cb_search.h | 35 - .../codecs/iLBC/main/source/cb_search_core.c | 113 - .../codecs/iLBC/main/source/cb_search_core.h | 40 - .../iLBC/main/source/cb_update_best_index.c | 89 - .../iLBC/main/source/cb_update_best_index.h | 38 - .../codecs/iLBC/main/source/chebyshev.c | 82 - .../codecs/iLBC/main/source/chebyshev.h | 37 - .../codecs/iLBC/main/source/comp_corr.c | 49 - .../codecs/iLBC/main/source/comp_corr.h | 39 - .../iLBC/main/source/complexityMeasures.m | 49 - .../codecs/iLBC/main/source/constants.c | 666 - .../codecs/iLBC/main/source/constants.h | 92 - .../iLBC/main/source/create_augmented_vec.c | 57 - .../iLBC/main/source/create_augmented_vec.h | 36 - .../codecs/iLBC/main/source/decode.c | 244 - .../codecs/iLBC/main/source/decode.h | 37 - .../codecs/iLBC/main/source/decode_residual.c | 189 - .../codecs/iLBC/main/source/decode_residual.h | 38 - .../main/source/decoder_interpolate_lsf.c | 82 - .../main/source/decoder_interpolate_lsf.h | 38 - .../codecs/iLBC/main/source/defines.h | 219 - .../codecs/iLBC/main/source/do_plc.c | 308 - .../codecs/iLBC/main/source/do_plc.h | 41 - .../codecs/iLBC/main/source/encode.c | 518 - .../codecs/iLBC/main/source/encode.h | 35 - .../codecs/iLBC/main/source/energy_inverse.c | 46 - .../codecs/iLBC/main/source/energy_inverse.h | 32 - .../codecs/iLBC/main/source/enh_upsample.c | 110 - .../codecs/iLBC/main/source/enh_upsample.h | 33 - .../codecs/iLBC/main/source/enhancer.c | 52 - .../codecs/iLBC/main/source/enhancer.h | 41 - .../iLBC/main/source/enhancer_interface.c | 343 - .../iLBC/main/source/enhancer_interface.h | 34 - .../iLBC/main/source/filtered_cb_vecs.c | 48 - .../iLBC/main/source/filtered_cb_vecs.h | 38 - .../codecs/iLBC/main/source/frame_classify.c | 88 - .../codecs/iLBC/main/source/frame_classify.h | 29 - .../codecs/iLBC/main/source/gain_dequant.c | 45 - .../codecs/iLBC/main/source/gain_dequant.h | 36 - .../codecs/iLBC/main/source/gain_quant.c | 106 - .../codecs/iLBC/main/source/gain_quant.h | 35 - .../codecs/iLBC/main/source/get_cd_vec.c | 111 - .../codecs/iLBC/main/source/get_cd_vec.h | 30 - .../codecs/iLBC/main/source/get_lsp_poly.c | 83 - .../codecs/iLBC/main/source/get_lsp_poly.h | 47 - .../codecs/iLBC/main/source/get_sync_seq.c | 110 - .../codecs/iLBC/main/source/get_sync_seq.h | 42 - .../codecs/iLBC/main/source/hp_input.c | 88 - .../codecs/iLBC/main/source/hp_input.h | 34 - .../codecs/iLBC/main/source/hp_output.c | 89 - .../codecs/iLBC/main/source/hp_output.h | 34 - .../codecs/iLBC/main/source/ilbc.c | 255 - .../codecs/iLBC/main/source/ilbc.gyp | 177 - .../codecs/iLBC/main/source/index_conv_dec.c | 38 - .../codecs/iLBC/main/source/index_conv_dec.h | 28 - .../codecs/iLBC/main/source/index_conv_enc.c | 42 - .../codecs/iLBC/main/source/index_conv_enc.h | 32 - .../codecs/iLBC/main/source/init_decode.c | 98 - .../codecs/iLBC/main/source/init_decode.h | 35 - .../codecs/iLBC/main/source/init_encode.c | 72 - .../codecs/iLBC/main/source/init_encode.h | 33 - .../codecs/iLBC/main/source/interpolate.c | 48 - .../codecs/iLBC/main/source/interpolate.h | 35 - .../iLBC/main/source/interpolate_samples.c | 51 - .../iLBC/main/source/interpolate_samples.h | 34 - .../codecs/iLBC/main/source/lpc_encode.c | 60 - .../codecs/iLBC/main/source/lpc_encode.h | 39 - .../codecs/iLBC/main/source/lsf_check.c | 71 - .../codecs/iLBC/main/source/lsf_check.h | 33 - .../main/source/lsf_interpolate_to_poly_dec.c | 42 - .../main/source/lsf_interpolate_to_poly_dec.h | 37 - .../main/source/lsf_interpolate_to_poly_enc.c | 50 - .../main/source/lsf_interpolate_to_poly_enc.h | 40 - .../codecs/iLBC/main/source/lsf_to_lsp.c | 61 - .../codecs/iLBC/main/source/lsf_to_lsp.h | 34 - .../codecs/iLBC/main/source/lsf_to_poly.c | 86 - .../codecs/iLBC/main/source/lsf_to_poly.h | 33 - .../codecs/iLBC/main/source/lsp_to_lsf.c | 84 - .../codecs/iLBC/main/source/lsp_to_lsf.h | 35 - .../codecs/iLBC/main/source/my_corr.c | 51 - .../codecs/iLBC/main/source/my_corr.h | 36 - .../iLBC/main/source/nearest_neighbor.c | 50 - .../iLBC/main/source/nearest_neighbor.h | 39 - .../codecs/iLBC/main/source/pack_bits.c | 251 - .../codecs/iLBC/main/source/pack_bits.h | 34 - .../codecs/iLBC/main/source/poly_to_lsf.c | 31 - .../codecs/iLBC/main/source/poly_to_lsf.h | 33 - .../codecs/iLBC/main/source/poly_to_lsp.c | 156 - .../codecs/iLBC/main/source/poly_to_lsp.h | 36 - .../codecs/iLBC/main/source/refiner.c | 155 - .../codecs/iLBC/main/source/refiner.h | 45 - .../iLBC/main/source/simple_interpolate_lsf.c | 119 - .../iLBC/main/source/simple_interpolate_lsf.h | 46 - .../iLBC/main/source/simple_lpc_analysis.c | 94 - .../iLBC/main/source/simple_lpc_analysis.h | 35 - .../iLBC/main/source/simple_lsf_dequant.c | 60 - .../iLBC/main/source/simple_lsf_dequant.h | 34 - .../iLBC/main/source/simple_lsf_quant.c | 47 - .../iLBC/main/source/simple_lsf_quant.h | 37 - .../codecs/iLBC/main/source/smooth.c | 212 - .../codecs/iLBC/main/source/smooth.h | 36 - .../codecs/iLBC/main/source/smooth_out_data.c | 45 - .../codecs/iLBC/main/source/smooth_out_data.h | 35 - .../codecs/iLBC/main/source/sort_sq.c | 51 - .../codecs/iLBC/main/source/sort_sq.h | 36 - .../codecs/iLBC/main/source/split_vq.c | 61 - .../codecs/iLBC/main/source/split_vq.h | 38 - .../codecs/iLBC/main/source/state_construct.c | 111 - .../codecs/iLBC/main/source/state_construct.h | 35 - .../codecs/iLBC/main/source/state_search.c | 119 - .../codecs/iLBC/main/source/state_search.h | 38 - .../codecs/iLBC/main/source/swap_bytes.c | 36 - .../codecs/iLBC/main/source/swap_bytes.h | 33 - .../codecs/iLBC/main/source/unpack_bits.c | 239 - .../codecs/iLBC/main/source/unpack_bits.h | 34 - .../codecs/iLBC/main/source/vq3.c | 63 - .../codecs/iLBC/main/source/vq3.h | 36 - .../codecs/iLBC/main/source/vq4.c | 62 - .../codecs/iLBC/main/source/vq4.h | 36 - .../codecs/iLBC/main/source/window32_w32.c | 65 - .../codecs/iLBC/main/source/window32_w32.h | 35 - .../codecs/iLBC/main/source/xcorr_coef.c | 142 - .../codecs/iLBC/main/source/xcorr_coef.h | 38 - .../codecs/iLBC/main/test/iLBC_test.c | 239 - .../codecs/iLBC/main/test/iLBC_testLib.c | 211 - .../codecs/iLBC/main/test/iLBC_testprogram.c | 348 - .../codecs/iLBC/main/test/iLBCtestscript.txt | 69 - .../codecs/iSAC/fix/interface/isacfix.h | 621 - .../codecs/iSAC/fix/source/Android.mk | 75 - .../codecs/iSAC/fix/source/arith_routines.c | 124 - .../iSAC/fix/source/arith_routines_hist.c | 404 - .../iSAC/fix/source/arith_routines_logist.c | 404 - .../codecs/iSAC/fix/source/arith_routins.h | 160 - .../iSAC/fix/source/bandwidth_estimator.c | 1022 - .../iSAC/fix/source/bandwidth_estimator.h | 127 - .../codecs/iSAC/fix/source/codec.h | 141 - .../codecs/iSAC/fix/source/decode.c | 217 - .../codecs/iSAC/fix/source/decode_bwe.c | 69 - .../codecs/iSAC/fix/source/decode_plc.c | 830 - .../codecs/iSAC/fix/source/encode.c | 628 - .../codecs/iSAC/fix/source/entropy_coding.c | 2072 - .../codecs/iSAC/fix/source/entropy_coding.h | 111 - .../audio_coding/codecs/iSAC/fix/source/fft.c | 415 - .../audio_coding/codecs/iSAC/fix/source/fft.h | 41 - .../iSAC/fix/source/filterbank_tables.c | 64 - .../iSAC/fix/source/filterbank_tables.h | 41 - .../codecs/iSAC/fix/source/filterbanks.c | 326 - .../codecs/iSAC/fix/source/filters.c | 131 - .../codecs/iSAC/fix/source/initialize.c | 175 - .../codecs/iSAC/fix/source/isacfix.c | 1534 - .../codecs/iSAC/fix/source/isacfix.gyp | 84 - .../codecs/iSAC/fix/source/lattice.c | 293 - .../iSAC/fix/source/lpc_masking_model.c | 1034 - .../iSAC/fix/source/lpc_masking_model.h | 37 - .../codecs/iSAC/fix/source/lpc_tables.c | 1280 - .../codecs/iSAC/fix/source/lpc_tables.h | 98 - .../codecs/iSAC/fix/source/pitch_estimator.c | 519 - .../codecs/iSAC/fix/source/pitch_estimator.h | 55 - .../codecs/iSAC/fix/source/pitch_filter.c | 331 - .../iSAC/fix/source/pitch_gain_tables.c | 149 - .../iSAC/fix/source/pitch_gain_tables.h | 45 - .../codecs/iSAC/fix/source/pitch_lag_tables.c | 306 - .../codecs/iSAC/fix/source/pitch_lag_tables.h | 103 - .../codecs/iSAC/fix/source/settings.h | 205 - .../fix/source/spectrum_ar_model_tables.c | 193 - .../fix/source/spectrum_ar_model_tables.h | 96 - .../codecs/iSAC/fix/source/structs.h | 382 - .../codecs/iSAC/fix/source/transform.c | 296 - .../codecs/iSAC/fix/test/ISACHist.cpp | 173 - .../codecs/iSAC/fix/test/Isac_test.cpp | 260 - .../codecs/iSAC/fix/test/QA/ChannelFiles.txt | 3 - .../codecs/iSAC/fix/test/QA/InputFiles.txt | 31 - .../codecs/iSAC/fix/test/QA/InputFilesFew.txt | 6 - .../iSAC/fix/test/QA/ListOfTestCases.xls | Bin 152064 -> 0 bytes .../codecs/iSAC/fix/test/QA/diffiSAC.txt | 481 - .../codecs/iSAC/fix/test/QA/diffiSACPLC.txt | 20 - .../iSAC/fix/test/QA/runiSACLongtest.txt | 61 - .../codecs/iSAC/fix/test/QA/runiSACNB.txt | 45 - .../codecs/iSAC/fix/test/QA/runiSACPLC.txt | 23 - .../codecs/iSAC/fix/test/QA/runiSACRate.txt | 23 - .../codecs/iSAC/fix/test/QA/runiSACfault.txt | 40 - .../iSAC/fix/test/QA/runiSACfixfloat.txt | 47 - .../audio_coding/codecs/iSAC/fix/test/kenny.c | 799 - .../codecs/iSAC/fix/test/test_iSACfixfloat.c | 693 - .../audio_coding/codecs/iSAC/isac_test.gyp | 74 - .../audio_coding/codecs/iSAC/isacfix_test.gyp | 36 - .../codecs/iSAC/main/interface/isac.h | 729 - .../codecs/iSAC/main/source/Android.mk | 79 - .../codecs/iSAC/main/source/arith_routines.c | 60 - .../codecs/iSAC/main/source/arith_routines.h | 63 - .../iSAC/main/source/arith_routines_hist.c | 291 - .../iSAC/main/source/arith_routines_logist.c | 294 - .../iSAC/main/source/bandwidth_estimator.c | 1028 - .../iSAC/main/source/bandwidth_estimator.h | 180 - .../codecs/iSAC/main/source/codec.h | 292 - .../codecs/iSAC/main/source/crc.c | 110 - .../codecs/iSAC/main/source/crc.h | 46 - .../codecs/iSAC/main/source/decode.c | 330 - .../codecs/iSAC/main/source/decode_bwe.c | 88 - .../codecs/iSAC/main/source/encode.c | 1457 - .../codecs/iSAC/main/source/encode_lpc_swb.c | 708 - .../codecs/iSAC/main/source/encode_lpc_swb.h | 283 - .../codecs/iSAC/main/source/entropy_coding.c | 2762 -- .../codecs/iSAC/main/source/entropy_coding.h | 412 - .../codecs/iSAC/main/source/fft.c | 947 - .../codecs/iSAC/main/source/fft.h | 45 - .../iSAC/main/source/filter_functions.c | 271 - .../iSAC/main/source/filterbank_tables.c | 37 - .../iSAC/main/source/filterbank_tables.h | 46 - .../codecs/iSAC/main/source/filterbanks.c | 346 - .../codecs/iSAC/main/source/intialize.c | 175 - .../codecs/iSAC/main/source/isac.c | 2814 -- .../codecs/iSAC/main/source/isac.gyp | 93 - .../codecs/iSAC/main/source/lattice.c | 217 - .../codecs/iSAC/main/source/lpc_analysis.c | 508 - .../codecs/iSAC/main/source/lpc_analysis.h | 53 - .../iSAC/main/source/lpc_gain_swb_tables.c | 137 - .../iSAC/main/source/lpc_gain_swb_tables.h | 49 - .../iSAC/main/source/lpc_shape_swb12_tables.c | 159 - .../iSAC/main/source/lpc_shape_swb12_tables.h | 65 - .../iSAC/main/source/lpc_shape_swb16_tables.c | 248 - .../iSAC/main/source/lpc_shape_swb16_tables.h | 79 - .../codecs/iSAC/main/source/lpc_tables.c | 1129 - .../codecs/iSAC/main/source/lpc_tables.h | 114 - .../codecs/iSAC/main/source/pitch_estimator.c | 622 - .../codecs/iSAC/main/source/pitch_estimator.h | 71 - .../codecs/iSAC/main/source/pitch_filter.c | 502 - .../iSAC/main/source/pitch_gain_tables.c | 105 - .../iSAC/main/source/pitch_gain_tables.h | 45 - .../iSAC/main/source/pitch_lag_tables.c | 277 - .../iSAC/main/source/pitch_lag_tables.h | 114 - .../codecs/iSAC/main/source/settings.h | 199 - .../main/source/spectrum_ar_model_tables.c | 138 - .../main/source/spectrum_ar_model_tables.h | 76 - .../codecs/iSAC/main/source/structs.h | 478 - .../codecs/iSAC/main/source/transform.c | 165 - .../iSAC/main/test/QA/runiSACLongtest.txt | 433 - .../codecs/iSAC/main/test/QA/runiSACfault.txt | 80 - .../iSAC/main/test/QA/runiSACfixfloat.txt | 47 - .../test/ReleaseTest-API/ReleaseTest-API.cc | 1063 - .../SwitchingSampRate/SwitchingSampRate.cc | 442 - .../codecs/iSAC/main/test/debugUtility.h | 72 - .../codecs/iSAC/main/test/simpleKenny.c | 635 - .../codecs/iSAC/main/util/utility.c | 184 - .../codecs/iSAC/main/util/utility.h | 144 - modules/audio_coding/main/OWNERS | 3 - .../main/interface/audio_coding_module.h | 1076 - .../interface/audio_coding_module_typedefs.h | 245 - modules/audio_coding/main/source/Android.mk | 90 - modules/audio_coding/main/source/acm_amr.cc | 592 - modules/audio_coding/main/source/acm_amr.h | 97 - modules/audio_coding/main/source/acm_amrwb.cc | 590 - modules/audio_coding/main/source/acm_amrwb.h | 98 - modules/audio_coding/main/source/acm_cng.cc | 228 - modules/audio_coding/main/source/acm_cng.h | 87 - .../main/source/acm_codec_database.cc | 953 - .../main/source/acm_codec_database.h | 138 - .../main/source/acm_common_defs.h | 120 - .../main/source/acm_dtmf_detection.cc | 64 - .../main/source/acm_dtmf_detection.h | 47 - .../main/source/acm_dtmf_playout.cc | 268 - .../main/source/acm_dtmf_playout.h | 68 - modules/audio_coding/main/source/acm_g722.cc | 449 - modules/audio_coding/main/source/acm_g722.h | 87 - modules/audio_coding/main/source/acm_g7221.cc | 675 - modules/audio_coding/main/source/acm_g7221.h | 99 - .../audio_coding/main/source/acm_g7221c.cc | 685 - modules/audio_coding/main/source/acm_g7221c.h | 99 - modules/audio_coding/main/source/acm_g729.cc | 544 - modules/audio_coding/main/source/acm_g729.h | 86 - modules/audio_coding/main/source/acm_g7291.cc | 504 - modules/audio_coding/main/source/acm_g7291.h | 87 - .../main/source/acm_generic_codec.cc | 1551 - .../main/source/acm_generic_codec.h | 1333 - modules/audio_coding/main/source/acm_gsmfr.cc | 413 - modules/audio_coding/main/source/acm_gsmfr.h | 79 - modules/audio_coding/main/source/acm_ilbc.cc | 390 - modules/audio_coding/main/source/acm_ilbc.h | 79 - modules/audio_coding/main/source/acm_isac.cc | 1233 - modules/audio_coding/main/source/acm_isac.h | 153 - .../main/source/acm_isac_macros.h | 76 - modules/audio_coding/main/source/acm_neteq.cc | 1370 - modules/audio_coding/main/source/acm_neteq.h | 446 - modules/audio_coding/main/source/acm_opus.cc | 480 - modules/audio_coding/main/source/acm_opus.h | 84 - .../audio_coding/main/source/acm_pcm16b.cc | 337 - modules/audio_coding/main/source/acm_pcm16b.h | 70 - modules/audio_coding/main/source/acm_pcma.cc | 165 - modules/audio_coding/main/source/acm_pcma.h | 68 - modules/audio_coding/main/source/acm_pcmu.cc | 167 - modules/audio_coding/main/source/acm_pcmu.h | 68 - modules/audio_coding/main/source/acm_red.cc | 163 - modules/audio_coding/main/source/acm_red.h | 68 - .../audio_coding/main/source/acm_resampler.cc | 90 - .../audio_coding/main/source/acm_resampler.h | 47 - modules/audio_coding/main/source/acm_speex.cc | 661 - modules/audio_coding/main/source/acm_speex.h | 95 - .../main/source/audio_coding_module.cc | 184 - .../main/source/audio_coding_module.gyp | 138 - .../main/source/audio_coding_module_impl.cc | 2713 -- .../main/source/audio_coding_module_impl.h | 400 - modules/audio_coding/main/test/ACMTest.cpp | 16 - modules/audio_coding/main/test/ACMTest.h | 21 - modules/audio_coding/main/test/APITest.cpp | 1602 - modules/audio_coding/main/test/APITest.h | 174 - modules/audio_coding/main/test/Channel.cpp | 481 - modules/audio_coding/main/test/Channel.h | 125 - .../main/test/EncodeDecodeTest.cpp | 303 - .../audio_coding/main/test/EncodeDecodeTest.h | 64 - .../main/test/EncodeToFileTest.cpp | 188 - .../audio_coding/main/test/EncodeToFileTest.h | 79 - modules/audio_coding/main/test/PCMFile.cpp | 300 - modules/audio_coding/main/test/PCMFile.h | 76 - modules/audio_coding/main/test/RTPFile.cpp | 297 - modules/audio_coding/main/test/RTPFile.h | 99 - .../audio_coding/main/test/SpatialAudio.cpp | 239 - modules/audio_coding/main/test/SpatialAudio.h | 43 - .../audio_coding/main/test/TestAllCodecs.cpp | 858 - .../audio_coding/main/test/TestAllCodecs.h | 94 - modules/audio_coding/main/test/TestFEC.cpp | 627 - modules/audio_coding/main/test/TestFEC.h | 47 - modules/audio_coding/main/test/TestStereo.cpp | 553 - modules/audio_coding/main/test/TestStereo.h | 99 - modules/audio_coding/main/test/TestVADDTX.cpp | 503 - modules/audio_coding/main/test/TestVADDTX.h | 87 - modules/audio_coding/main/test/Tester.cpp | 131 - modules/audio_coding/main/test/TimedTrace.cpp | 77 - modules/audio_coding/main/test/TimedTrace.h | 38 - .../main/test/TwoWayCommunication.cpp | 503 - .../main/test/TwoWayCommunication.h | 62 - modules/audio_coding/main/test/iSACTest.cpp | 597 - modules/audio_coding/main/test/iSACTest.h | 100 - modules/audio_coding/main/test/utility.cpp | 431 - modules/audio_coding/main/test/utility.h | 202 - modules/audio_conference_mixer/OWNERS | 3 - .../interface/audio_conference_mixer.h | 86 - .../audio_conference_mixer_defines.h | 104 - .../audio_conference_mixer/source/Android.mk | 50 - .../source/audio_conference_mixer.gyp | 61 - .../source/audio_conference_mixer_impl.cc | 780 - .../source/audio_conference_mixer_impl.h | 176 - .../source/audio_frame_manipulator.cc | 30 - .../source/audio_frame_manipulator.h | 20 - .../source/level_indicator.cc | 76 - .../source/level_indicator.h | 37 - .../source/memory_pool.h | 122 - .../source/memory_pool_generic.h | 168 - .../source/memory_pool_windows.h | 199 - .../source/time_scheduler.cc | 102 - .../source/time_scheduler.h | 47 - .../test/FunctionTest/functionTest.cpp | 1098 - .../test/FunctionTest/functionTest.h | 276 - modules/audio_device/OWNERS | 4 - .../main/interface/audio_device.h | 216 - .../main/interface/audio_device_defines.h | 80 - modules/audio_device/main/source/Android.mk | 72 - .../Android/audio_device_android_jni.cc | 3058 -- .../source/Android/audio_device_android_jni.h | 268 - .../Android/audio_device_android_native.cc | 2235 - .../Android/audio_device_android_native.h | 310 - .../Android/audio_device_utility_android.cc | 53 - .../Android/audio_device_utility_android.h | 41 - .../voiceengine/AudioDeviceAndroid.java | 509 - .../main/source/Dummy/audio_device_dummy.cc | 1395 - .../main/source/Dummy/audio_device_dummy.h | 178 - .../main/source/Linux/alsasymboltable.cc | 39 - .../main/source/Linux/alsasymboltable.h | 144 - .../source/Linux/audio_device_linux_alsa.cc | 3690 -- .../source/Linux/audio_device_linux_alsa.h | 288 - .../source/Linux/audio_device_linux_pulse.cc | 3248 -- .../source/Linux/audio_device_linux_pulse.h | 385 - .../Linux/audio_device_utility_linux.cc | 57 - .../source/Linux/audio_device_utility_linux.h | 37 - .../Linux/audio_mixer_manager_linux_alsa.cc | 1339 - .../Linux/audio_mixer_manager_linux_alsa.h | 78 - .../Linux/audio_mixer_manager_linux_pulse.cc | 1301 - .../Linux/audio_mixer_manager_linux_pulse.h | 117 - .../source/Linux/latebindingsymboltable.cc | 116 - .../source/Linux/latebindingsymboltable.h | 198 - .../source/Linux/pulseaudiosymboltable.cc | 39 - .../main/source/Linux/pulseaudiosymboltable.h | 104 - .../main/source/Mac/audio_device_mac.cc | 3431 -- .../main/source/Mac/audio_device_mac.h | 400 - .../source/Mac/audio_device_utility_mac.cc | 56 - .../source/Mac/audio_device_utility_mac.h | 37 - .../source/Mac/audio_mixer_manager_mac.cc | 1188 - .../main/source/Mac/audio_mixer_manager_mac.h | 80 - .../source/Mac/portaudio/pa_memorybarrier.h | 127 - .../main/source/Mac/portaudio/pa_ringbuffer.c | 227 - .../main/source/Mac/portaudio/pa_ringbuffer.h | 233 - .../Windows/audio_device_utility_windows.cc | 233 - .../Windows/audio_device_utility_windows.h | 41 - .../Windows/audio_device_windows_core.cc | 4473 -- .../Windows/audio_device_windows_core.h | 313 - .../Windows/audio_device_windows_wave.cc | 3877 -- .../Windows/audio_device_windows_wave.h | 333 - .../source/Windows/audio_mixer_manager.cc | 2663 -- .../main/source/Windows/audio_mixer_manager.h | 139 - .../audio_device/main/source/audio_device.gyp | 203 - .../main/source/audio_device_buffer.cc | 644 - .../main/source/audio_device_buffer.h | 121 - .../main/source/audio_device_config.h | 44 - .../main/source/audio_device_generic.cc | 62 - .../main/source/audio_device_generic.h | 179 - .../main/source/audio_device_impl.cc | 2185 - .../main/source/audio_device_impl.h | 235 - .../main/source/audio_device_utility.cc | 138 - .../main/source/audio_device_utility.h | 36 - .../audio_device_android_test/.classpath | 7 - .../audio_device_android_test/.project | 33 - .../AndroidManifest.xml | 19 - .../default.properties | 11 - .../gen/org/webrtc/voiceengine/test/R.java | 26 - .../jni/audio_device_android_test.cc | 108 - ..._voiceengine_test_AudioDeviceAndroidTest.h | 29 - .../res/drawable/icon.png | Bin 2574 -> 0 bytes .../res/layout/main.xml | 9 - .../res/values/strings.xml | 6 - .../voiceengine/AudioDeviceAndroid.java | 1 - .../test/AudioDeviceAndroidTest.java | 69 - modules/audio_device/main/test/README.txt | 23 - .../main/test/audio_device_test_api.cc | 2238 - .../main/test/audio_device_test_defines.h | 117 - .../main/test/audio_device_test_func.cc | 156 - .../audio_device/main/test/audio_short16.pcm | Bin 643632 -> 0 bytes .../audio_device/main/test/audio_short44.pcm | Bin 1774010 -> 0 bytes .../audio_device/main/test/audio_short48.pcm | Bin 1930896 -> 0 bytes .../audio_device/main/test/audio_short8.pcm | Bin 321816 -> 0 bytes .../main/test/func_test_manager.cc | 2752 -- .../main/test/func_test_manager.h | 219 - .../audio_device/main/test/recordedRaw_48.pcm | Bin 1917120 -> 0 bytes modules/audio_processing/OWNERS | 2 - .../aec/main/interface/echo_cancellation.h | 260 - .../aec/main/matlab/fullaec.m | 953 - .../aec/main/source/Android.mk | 54 - .../audio_processing/aec/main/source/aec.gyp | 49 - .../aec/main/source/aec_core.c | 1456 - .../aec/main/source/aec_core.h | 193 - .../aec/main/source/aec_core_sse2.c | 435 - .../aec/main/source/aec_rdft.c | 522 - .../aec/main/source/aec_rdft.h | 23 - .../aec/main/source/aec_rdft_sse2.c | 209 - .../aec/main/source/echo_cancellation.c | 821 - .../aec/main/source/resampler.c | 235 - .../aec/main/source/resampler.h | 32 - .../aecm/main/interface/echo_control_mobile.h | 206 - .../aecm/main/matlab/compsup.m | 447 - .../aecm/main/matlab/getBspectrum.m | 22 - .../aecm/main/matlab/hisser2.m | 21 - .../audio_processing/aecm/main/matlab/main2.m | 19 - .../aecm/main/matlab/matlab/AECMobile.m | 269 - .../aecm/main/matlab/matlab/align.m | 98 - .../aecm/main/matlab/matlab/calcFilterGain.m | 88 - .../aecm/main/matlab/matlab/calcStepSize.m | 105 - .../aecm/main/matlab/matlab/fallerEstimator.m | 42 - .../aecm/main/matlab/matlab/getBspectrum.m | 22 - .../aecm/main/matlab/matlab/hisser2.m | 21 - .../aecm/main/matlab/matlab/mainProgram.m | 283 - .../aecm/main/matlab/matlab/simEnvironment.m | 15 - .../aecm/main/matlab/matlab/updateSettings.m | 94 - .../aecm/main/matlab/waitbar_j.m | 234 - .../aecm/main/source/Android.mk | 52 - .../aecm/main/source/aecm.gyp | 43 - .../aecm/main/source/aecm_core.c | 2452 - .../aecm/main/source/aecm_core.h | 313 - .../aecm/main/source/echo_control_mobile.c | 733 - .../agc/main/interface/gain_control.h | 273 - .../agc/main/matlab/getGains.m | 32 - .../agc/main/source/Android.mk | 46 - .../audio_processing/agc/main/source/agc.gyp | 43 - .../agc/main/source/analog_agc.c | 1700 - .../agc/main/source/analog_agc.h | 133 - .../agc/main/source/digital_agc.c | 780 - .../agc/main/source/digital_agc.h | 76 - modules/audio_processing/main/apm_tests.gyp | 60 - .../main/interface/audio_processing.h | 564 - .../audio_processing/main/source/Android.mk | 72 - modules/audio_processing/main/source/apm.gyp | 77 - .../main/source/audio_buffer.cc | 278 - .../main/source/audio_buffer.h | 68 - .../main/source/audio_processing_impl.cc | 636 - .../main/source/audio_processing_impl.h | 109 - .../main/source/echo_cancellation_impl.cc | 348 - .../main/source/echo_cancellation_impl.h | 72 - .../main/source/echo_control_mobile_impl.cc | 245 - .../main/source/echo_control_mobile_impl.h | 59 - .../main/source/gain_control_impl.cc | 391 - .../main/source/gain_control_impl.h | 80 - .../main/source/high_pass_filter_impl.cc | 180 - .../main/source/high_pass_filter_impl.h | 51 - .../main/source/level_estimator_impl.cc | 182 - .../main/source/level_estimator_impl.h | 53 - .../main/source/noise_suppression_impl.cc | 179 - .../main/source/noise_suppression_impl.h | 54 - .../main/source/processing_component.cc | 112 - .../main/source/processing_component.h | 63 - .../main/source/splitting_filter.cc | 33 - .../main/source/splitting_filter.h | 63 - .../main/source/voice_detection_impl.cc | 202 - .../main/source/voice_detection_impl.h | 63 - .../test/android/apmtest/AndroidManifest.xml | 30 - .../test/android/apmtest/default.properties | 11 - .../main/test/android/apmtest/jni/Android.mk | 26 - .../test/android/apmtest/jni/Application.mk | 1 - .../main/test/android/apmtest/jni/main.c | 307 - .../android/apmtest/res/values/strings.xml | 4 - .../main/test/process_test/Android.mk | 48 - .../main/test/process_test/apmtest.m | 360 - .../main/test/process_test/process_test.cc | 628 - .../main/test/unit_test/Android.mk | 49 - .../unit_test/audio_processing_unittest.pb.cc | 1111 - .../unit_test/audio_processing_unittest.pb.h | 862 - .../unit_test/audio_processing_unittest.proto | 33 - .../main/test/unit_test/unit_test.cc | 881 - .../ns/main/interface/noise_suppression.h | 124 - .../ns/main/interface/noise_suppression_x.h | 123 - .../ns/main/source/Android.mk | 50 - .../audio_processing/ns/main/source/defines.h | 53 - .../ns/main/source/noise_suppression.c | 69 - .../ns/main/source/noise_suppression_x.c | 74 - .../audio_processing/ns/main/source/ns.gyp | 67 - .../audio_processing/ns/main/source/ns_core.c | 1500 - .../audio_processing/ns/main/source/ns_core.h | 179 - .../ns/main/source/nsx_core.c | 2493 - .../ns/main/source/nsx_core.h | 169 - .../ns/main/source/nsx_defines.h | 58 - .../ns/main/source/windows_private.h | 573 - modules/audio_processing/utility/Android.mk | 49 - modules/audio_processing/utility/fft4g.c | 1356 - modules/audio_processing/utility/fft4g.h | 18 - .../audio_processing/utility/ring_buffer.c | 239 - .../audio_processing/utility/ring_buffer.h | 41 - modules/audio_processing/utility/util.gyp | 36 - modules/interface/module.h | 33 - modules/interface/module_common_types.h | 1012 - modules/media_file/OWNERS | 4 - modules/media_file/interface/media_file.h | 236 - .../media_file/interface/media_file_defines.h | 51 - modules/media_file/source/Android.mk | 52 - modules/media_file/source/avi_file.cc | 1700 - modules/media_file/source/avi_file.h | 276 - modules/media_file/source/media_file.gyp | 48 - modules/media_file/source/media_file_impl.cc | 1473 - modules/media_file/source/media_file_impl.h | 251 - .../media_file/source/media_file_utility.cc | 2659 -- .../media_file/source/media_file_utility.h | 349 - modules/rtp_rtcp/OWNERS | 6 - modules/rtp_rtcp/interface/rtp_rtcp.h | 980 - modules/rtp_rtcp/interface/rtp_rtcp_defines.h | 222 - modules/rtp_rtcp/source/Android.mk | 74 - modules/rtp_rtcp/source/Bitrate.h | 84 - .../rtp_rtcp/source/H264/bitstream_builder.cc | 580 - .../rtp_rtcp/source/H264/bitstream_builder.h | 52 - .../rtp_rtcp/source/H264/bitstream_parser.cc | 217 - .../rtp_rtcp/source/H264/bitstream_parser.h | 46 - .../rtp_rtcp/source/H264/h264_information.cc | 818 - .../rtp_rtcp/source/H264/h264_information.h | 170 - .../rtp_rtcp/source/H264/rtp_sender_h264.cc | 1280 - .../rtp_rtcp/source/H264/rtp_sender_h264.h | 179 - .../rtp_rtcp/source/bandwidth_management.cc | 309 - .../rtp_rtcp/source/bandwidth_management.h | 86 - modules/rtp_rtcp/source/bitrate.cc | 203 - modules/rtp_rtcp/source/bwe_defines.h | 56 - modules/rtp_rtcp/source/dtmf_queue.cc | 79 - modules/rtp_rtcp/source/dtmf_queue.h | 40 - modules/rtp_rtcp/source/fec_private_tables.h | 25741 ---------- .../source/forward_error_correction.cc | 835 - .../source/forward_error_correction.h | 180 - .../forward_error_correction_internal.cc | 340 - .../forward_error_correction_internal.h | 44 - modules/rtp_rtcp/source/h263_information.cc | 1472 - modules/rtp_rtcp/source/h263_information.h | 182 - modules/rtp_rtcp/source/overuse_detector.cc | 539 - modules/rtp_rtcp/source/overuse_detector.h | 91 - modules/rtp_rtcp/source/receiver_fec.cc | 336 - modules/rtp_rtcp/source/receiver_fec.h | 53 - .../rtp_rtcp/source/remote_rate_control.cc | 511 - modules/rtp_rtcp/source/remote_rate_control.h | 76 - modules/rtp_rtcp/source/rtcp_receiver.cc | 1496 - modules/rtp_rtcp/source/rtcp_receiver.h | 200 - modules/rtp_rtcp/source/rtcp_receiver_help.cc | 248 - modules/rtp_rtcp/source/rtcp_receiver_help.h | 122 - modules/rtp_rtcp/source/rtcp_sender.cc | 2002 - modules/rtp_rtcp/source/rtcp_sender.h | 215 - modules/rtp_rtcp/source/rtcp_utility.cc | 1350 - modules/rtp_rtcp/source/rtcp_utility.h | 415 - modules/rtp_rtcp/source/rtp_format_vp8.cc | 254 - modules/rtp_rtcp/source/rtp_format_vp8.h | 122 - .../source/rtp_format_vp8_unittest.cc | 353 - modules/rtp_rtcp/source/rtp_receiver.cc | 1668 - modules/rtp_rtcp/source/rtp_receiver.h | 236 - modules/rtp_rtcp/source/rtp_receiver_audio.cc | 602 - modules/rtp_rtcp/source/rtp_receiver_audio.h | 96 - modules/rtp_rtcp/source/rtp_receiver_video.cc | 680 - modules/rtp_rtcp/source/rtp_receiver_video.h | 155 - modules/rtp_rtcp/source/rtp_rtcp.gyp | 94 - modules/rtp_rtcp/source/rtp_rtcp_config.h | 46 - modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 2596 -- modules/rtp_rtcp/source/rtp_rtcp_impl.h | 572 - modules/rtp_rtcp/source/rtp_rtcp_private.h | 115 - modules/rtp_rtcp/source/rtp_rtcp_tests.gyp | 37 - modules/rtp_rtcp/source/rtp_sender.cc | 1549 - modules/rtp_rtcp/source/rtp_sender.h | 314 - modules/rtp_rtcp/source/rtp_sender_audio.cc | 670 - modules/rtp_rtcp/source/rtp_sender_audio.h | 131 - modules/rtp_rtcp/source/rtp_sender_video.cc | 1248 - modules/rtp_rtcp/source/rtp_sender_video.h | 167 - modules/rtp_rtcp/source/rtp_utility.cc | 995 - modules/rtp_rtcp/source/rtp_utility.h | 209 - modules/rtp_rtcp/source/ssrc_database.cc | 312 - modules/rtp_rtcp/source/ssrc_database.h | 68 - modules/rtp_rtcp/source/tmmbr_help.cc | 507 - modules/rtp_rtcp/source/tmmbr_help.h | 79 - .../rtp_rtcp/source/video_codec_information.h | 28 - .../test/BWEStandAlone/BWEConvergenceTest.cpp | 66 - .../test/BWEStandAlone/BWEConvergenceTest.h | 38 - .../test/BWEStandAlone/BWEStabilityTest.cpp | 84 - .../test/BWEStandAlone/BWEStabilityTest.h | 39 - .../test/BWEStandAlone/BWEStandAlone.cc | 200 - .../test/BWEStandAlone/BWETestBase.cpp | 453 - .../rtp_rtcp/test/BWEStandAlone/BWETestBase.h | 102 - .../rtp_rtcp/test/BWEStandAlone/BWETester.cpp | 274 - .../BWEStandAlone/BWETwoWayLimitFinding.cpp | 75 - .../BWEStandAlone/BWETwoWayLimitFinding.h | 41 - .../rtp_rtcp/test/BWEStandAlone/MatlabPlot.cc | 1071 - .../rtp_rtcp/test/BWEStandAlone/MatlabPlot.h | 170 - .../test/BWEStandAlone/TestLoadGenerator.cc | 438 - .../test/BWEStandAlone/TestLoadGenerator.h | 146 - .../test/BWEStandAlone/TestSenderReceiver.cc | 444 - .../test/BWEStandAlone/TestSenderReceiver.h | 166 - .../test/bitstreamTest/bitstreamTest.cpp | 538 - modules/rtp_rtcp/test/bwe_standalone.gyp | 138 - .../rtp_rtcp/test/testAPI/H263_CIF_IFRAME.bin | Bin 14849 -> 0 bytes .../rtp_rtcp/test/testAPI/H263_CIF_PFRAME.bin | Bin 3287 -> 0 bytes .../test/testAPI/H263_QCIF_IFRAME.bin | Bin 2020 -> 0 bytes modules/rtp_rtcp/test/testAPI/testAPI.cpp | 1239 - modules/rtp_rtcp/test/testFec/test_fec.cc | 569 - modules/rtp_rtcp/test/testFec/test_fec.gyp | 38 - .../testH263Parser/H263Foreman_CIF_Iframe.bin | Bin 14849 -> 0 bytes .../testH263Parser/H263Foreman_CIF_Pframe.bin | Bin 1406 -> 0 bytes .../test/testH263Parser/testH263Parser.cpp | 580 - .../test/testRateControl/testRateControl.cpp | 271 - .../test/testTMMBR/RTCPPacketTMMBR0.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR1.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR2.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR3.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR4.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR4_1.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR4_2.bin | Bin 28 -> 0 bytes .../test/testTMMBR/RTCPPacketTMMBR5.bin | Bin 28 -> 0 bytes modules/rtp_rtcp/test/testTMMBR/testTMMBR.cpp | 1034 - modules/rtp_rtcp/test/test_bwe/test_bwe.gyp | 38 - modules/rtp_rtcp/test/test_bwe/unit_test.cc | 59 - modules/udp_transport/OWNERS | 5 - .../udp_transport/interface/udp_transport.h | 386 - modules/udp_transport/source/Android.mk | 56 - .../source/traffic_control_windows.cc | 253 - .../source/traffic_control_windows.h | 99 - .../source/udp_socket2_manager_windows.cc | 665 - .../source/udp_socket2_manager_windows.h | 163 - .../source/udp_socket2_windows.cc | 1411 - .../source/udp_socket2_windows.h | 171 - .../udp_transport/source/udp_socket_linux.cc | 267 - .../udp_transport/source/udp_socket_linux.h | 91 - .../source/udp_socket_manager_linux.cc | 417 - .../source/udp_socket_manager_linux.h | 81 - .../source/udp_socket_manager_windows.cc | 271 - .../source/udp_socket_manager_windows.h | 66 - .../source/udp_socket_manager_wrapper.cc | 215 - .../source/udp_socket_manager_wrapper.h | 74 - .../source/udp_socket_windows.cc | 772 - .../udp_transport/source/udp_socket_windows.h | 103 - .../source/udp_socket_wrapper.cc | 160 - .../udp_transport/source/udp_socket_wrapper.h | 110 - .../udp_transport/source/udp_transport.gyp | 101 - .../source/udp_transport_impl.cc | 3097 -- .../udp_transport/source/udp_transport_impl.h | 251 - .../udp_transport/test/SocketManagerTest.cpp | 449 - modules/utility/OWNERS | 4 - modules/utility/interface/file_player.h | 107 - modules/utility/interface/file_recorder.h | 88 - modules/utility/interface/process_thread.h | 34 - modules/utility/interface/rtp_dump.h | 52 - modules/utility/source/Android.mk | 58 - modules/utility/source/coder.cc | 128 - modules/utility/source/coder.h | 69 - modules/utility/source/file_player_impl.cc | 725 - modules/utility/source/file_player_impl.h | 124 - modules/utility/source/file_recorder_impl.cc | 766 - modules/utility/source/file_recorder_impl.h | 164 - modules/utility/source/frame_scaler.cc | 219 - modules/utility/source/frame_scaler.h | 56 - modules/utility/source/process_thread_impl.cc | 194 - modules/utility/source/process_thread_impl.h | 47 - modules/utility/source/rtp_dump_impl.cc | 264 - modules/utility/source/rtp_dump_impl.h | 49 - modules/utility/source/utility.gyp | 65 - modules/utility/source/video_coder.cc | 167 - modules/utility/source/video_coder.h | 76 - modules/utility/source/video_frames_queue.cc | 150 - modules/utility/source/video_frames_queue.h | 62 - modules/utility/test/testAPI.cpp | 368 - modules/video_capture/OWNERS | 2 - .../main/interface/video_capture.h | 267 - .../main/interface/video_capture_defines.h | 126 - modules/video_capture/main/source/Android.mk | 61 - .../source/Android/device_info_android.cc | 381 - .../main/source/Android/device_info_android.h | 61 - .../videoengine/CaptureCapabilityAndroid.java | 17 - .../videoengine/VideoCaptureAndroid.java | 262 - .../VideoCaptureDeviceInfoAndroid.java | 432 - .../source/Android/video_capture_android.cc | 706 - .../source/Android/video_capture_android.h | 64 - .../main/source/Linux/device_info_linux.cc | 406 - .../main/source/Linux/device_info_linux.h | 52 - .../main/source/Linux/video_capture_linux.cc | 438 - .../main/source/Linux/video_capture_linux.h | 64 - .../source/Mac/QTKit/video_capture_qtkit.cc | 248 - .../source/Mac/QTKit/video_capture_qtkit.h | 84 - .../Mac/QTKit/video_capture_qtkit_info.cc | 144 - .../Mac/QTKit/video_capture_qtkit_info.h | 95 - .../Mac/QTKit/video_capture_qtkit_info_objc.h | 69 - .../QTKit/video_capture_qtkit_info_objc.mm | 202 - .../Mac/QTKit/video_capture_qtkit_objc.h | 103 - .../Mac/QTKit/video_capture_qtkit_objc.mm | 501 - .../Mac/QTKit/video_capture_qtkit_utility.h | 36 - .../Mac/QTKit/video_capture_recursive_lock.h | 32 - .../Mac/QTKit/video_capture_recursive_lock.mm | 33 - .../Mac/QuickTime/video_capture_quick_time.cc | 1410 - .../Mac/QuickTime/video_capture_quick_time.h | 133 - .../video_capture_quick_time_info.cc | 412 - .../QuickTime/video_capture_quick_time_info.h | 166 - .../main/source/Mac/video_capture_mac.cc | 574 - .../main/source/Mac/video_capture_mac.h | 121 - .../Windows/capture_delay_values_windows.h | 28 - .../source/Windows/device_info_windows.cc | 803 - .../main/source/Windows/device_info_windows.h | 105 - .../source/Windows/help_functions_windows.cc | 116 - .../source/Windows/help_functions_windows.h | 38 - .../source/Windows/sink_filter_windows.cc | 484 - .../main/source/Windows/sink_filter_windows.h | 100 - .../Windows/video_capture_factory_windows.cc | 46 - .../source/Windows/video_capture_windows.cc | 447 - .../source/Windows/video_capture_windows.h | 91 - .../main/source/device_info_impl.cc | 406 - .../main/source/device_info_impl.h | 65 - .../main/source/video_capture.gyp | 265 - .../main/source/video_capture_config.h | 34 - .../main/source/video_capture_delay.h | 36 - .../main/source/video_capture_impl.cc | 481 - .../main/source/video_capture_impl.h | 126 - .../main/source/vplib_conversions.cc | 51 - .../main/source/vplib_conversions.h | 25 - .../main/test/Android/.classpath | 9 - .../video_capture/main/test/Android/.project | 33 - .../main/test/Android/AndroidManifest.xml | 23 - .../main/test/Android/default.properties | 11 - .../webrtc/capturemoduleandroidtest/R.java | 33 - .../main/test/Android/jni/Android.mk | 19 - ...moduleandroidtest_VideoCaptureModuleTest.h | 40 - .../video_capture_module_android_test_jni.cc | 150 - .../test/Android/res/drawable-hdpi/icon.png | Bin 4147 -> 0 bytes .../test/Android/res/drawable-ldpi/icon.png | Bin 1723 -> 0 bytes .../test/Android/res/drawable-mdpi/icon.png | Bin 2574 -> 0 bytes .../main/test/Android/res/layout/main.xml | 18 - .../main/test/Android/res/values/strings.xml | 11 - .../VideoCaptureJavaTest.java | 58 - .../VideoCaptureModuleTest.java | 136 - .../main/test/testAPI/Logger.cpp | 301 - .../video_capture/main/test/testAPI/Logger.h | 29 - .../main/test/testAPI/Renderer.cpp | 406 - .../main/test/testAPI/Renderer.h | 77 - .../main/test/testAPI/cocoa_renderer.h | 31 - .../main/test/testAPI/cocoa_renderer.mm | 19 - .../main/test/testAPI/testAPI.cpp | 86 - .../main/test/testAPI/testCameraEncoder.cpp | 334 - .../main/test/testAPI/testCameraEncoder.h | 135 - .../main/test/testAPI/testDefines.h | 58 - .../main/test/testAPI/testExternalCapture.cpp | 170 - .../main/test/testAPI/testExternalCapture.h | 61 - .../test/testAPI/testPlatformDependent.cpp | 487 - .../main/test/testAPI/testPlatformDependent.h | 128 - modules/video_coding/codecs/OWNERS | 4 - .../codecs/i420/main/interface/i420.h | 166 - .../codecs/i420/main/source/Android.mk | 51 - .../codecs/i420/main/source/i420.cc | 281 - .../codecs/i420/main/source/i420.gyp | 39 - .../codecs/interface/video_codec_interface.h | 242 - .../codecs/interface/video_error_codes.h | 29 - .../codecs/test_framework/benchmark.cc | 304 - .../codecs/test_framework/benchmark.h | 40 - .../codecs/test_framework/exportfig.m | 500 - .../test_framework/normal_async_test.cc | 563 - .../codecs/test_framework/normal_async_test.h | 184 - .../codecs/test_framework/normal_test.cc | 246 - .../codecs/test_framework/normal_test.h | 46 - .../codecs/test_framework/packet_loss_test.cc | 244 - .../codecs/test_framework/packet_loss_test.h | 59 - .../codecs/test_framework/performance_test.cc | 290 - .../codecs/test_framework/performance_test.h | 54 - .../codecs/test_framework/plotBenchmark.m | 427 - .../codecs/test_framework/test.cc | 534 - .../video_coding/codecs/test_framework/test.h | 82 - .../codecs/test_framework/test_framework.gyp | 62 - .../codecs/test_framework/unit_test.cc | 815 - .../codecs/test_framework/unit_test.h | 133 - .../codecs/test_framework/video_buffer.cc | 319 - .../codecs/test_framework/video_buffer.h | 122 - .../codecs/test_framework/video_source.cc | 417 - .../codecs/test_framework/video_source.h | 110 - .../codecs/vp8/main/interface/vp8.h | 261 - .../codecs/vp8/main/source/Android.mk | 53 - .../codecs/vp8/main/source/vp8.cc | 1024 - .../codecs/vp8/main/source/vp8.gyp | 96 - .../codecs/vp8/main/test/benchmark.cc | 44 - .../codecs/vp8/main/test/benchmark.h | 28 - .../codecs/vp8/main/test/dual_decoder_test.cc | 219 - .../codecs/vp8/main/test/dual_decoder_test.h | 52 - .../codecs/vp8/main/test/normal_async_test.cc | 81 - .../codecs/vp8/main/test/normal_async_test.h | 33 - .../codecs/vp8/main/test/packet_loss_test.cc | 57 - .../codecs/vp8/main/test/packet_loss_test.h | 29 - .../codecs/vp8/main/test/tester.cc | 57 - .../codecs/vp8/main/test/unit_test.cc | 168 - .../codecs/vp8/main/test/unit_test.h | 40 - modules/video_coding/main/OWNERS | 4 - .../main/interface/video_coding.h | 497 - .../main/interface/video_coding_defines.h | 193 - modules/video_coding/main/source/Android.mk | 79 - .../main/source/codec_database.cc | 799 - .../video_coding/main/source/codec_database.h | 221 - .../video_coding/main/source/codec_timer.cc | 133 - .../video_coding/main/source/codec_timer.h | 61 - .../main/source/content_metrics_processing.cc | 213 - .../main/source/content_metrics_processing.h | 78 - .../video_coding/main/source/encoded_frame.cc | 201 - .../video_coding/main/source/encoded_frame.h | 112 - .../video_coding/main/source/er_tables_xor.h | 38728 ---------------- modules/video_coding/main/source/event.h | 63 - .../video_coding/main/source/exp_filter.cc | 60 - modules/video_coding/main/source/exp_filter.h | 58 - .../video_coding/main/source/fec_tables_xor.h | 6478 --- .../video_coding/main/source/frame_buffer.cc | 402 - .../video_coding/main/source/frame_buffer.h | 95 - .../video_coding/main/source/frame_dropper.cc | 331 - .../video_coding/main/source/frame_dropper.h | 94 - .../video_coding/main/source/frame_list.cc | 113 - modules/video_coding/main/source/frame_list.h | 66 - .../main/source/generic_decoder.cc | 203 - .../main/source/generic_decoder.h | 120 - .../main/source/generic_encoder.cc | 271 - .../main/source/generic_encoder.h | 147 - .../main/source/inter_frame_delay.cc | 120 - .../main/source/inter_frame_delay.h | 66 - .../main/source/internal_defines.h | 57 - .../video_coding/main/source/jitter_buffer.cc | 2015 - .../video_coding/main/source/jitter_buffer.h | 236 - .../main/source/jitter_buffer_common.h | 67 - .../main/source/jitter_estimator.cc | 422 - .../main/source/jitter_estimator.h | 155 - .../main/source/media_opt_util.cc | 943 - .../video_coding/main/source/media_opt_util.h | 392 - .../main/source/media_optimization.cc | 707 - .../main/source/media_optimization.h | 220 - .../main/source/nack_fec_tables.h | 226 - modules/video_coding/main/source/packet.cc | 77 - modules/video_coding/main/source/packet.h | 58 - modules/video_coding/main/source/qm_select.cc | 734 - modules/video_coding/main/source/qm_select.h | 182 - .../video_coding/main/source/qm_select_data.h | 185 - modules/video_coding/main/source/receiver.cc | 452 - modules/video_coding/main/source/receiver.h | 96 - .../video_coding/main/source/rtt_filter.cc | 214 - modules/video_coding/main/source/rtt_filter.h | 70 - .../video_coding/main/source/session_info.cc | 833 - .../video_coding/main/source/session_info.h | 102 - modules/video_coding/main/source/tick_time.h | 55 - .../main/source/timestamp_extrapolator.cc | 259 - .../main/source/timestamp_extrapolator.h | 59 - .../video_coding/main/source/timestamp_map.cc | 99 - .../video_coding/main/source/timestamp_map.h | 52 - modules/video_coding/main/source/timing.cc | 333 - modules/video_coding/main/source/timing.h | 110 - .../video_coding/main/source/video_coding.gyp | 104 - .../main/source/video_coding_impl.cc | 1346 - .../main/source/video_coding_impl.h | 276 - .../main/source/video_coding_test.gyp | 78 - .../main/test/codec_database_test.cc | 396 - .../main/test/codec_database_test.h | 54 - .../main/test/decode_from_storage_test.cc | 183 - .../main/test/generic_codec_test.cc | 584 - .../main/test/generic_codec_test.h | 100 - .../main/test/jitter_buffer_test.cc | 2252 - .../main/test/jitter_estimate_test.cc | 110 - .../main/test/jitter_estimate_test.h | 105 - .../video_coding/main/test/media_opt_test.cc | 614 - .../video_coding/main/test/media_opt_test.h | 133 - .../video_coding/main/test/mt_rx_tx_test.cc | 340 - modules/video_coding/main/test/normal_test.cc | 374 - modules/video_coding/main/test/normal_test.h | 145 - .../main/test/plotJitterEstimate.m | 52 - .../video_coding/main/test/plotReceiveTrace.m | 213 - .../video_coding/main/test/plotTimingTest.m | 62 - .../main/test/quality_modes_test.cc | 499 - .../main/test/quality_modes_test.h | 94 - .../video_coding/main/test/receiver_tests.h | 90 - .../main/test/receiver_timing_tests.cc | 227 - .../video_coding/main/test/release_test.cc | 46 - modules/video_coding/main/test/release_test.h | 17 - .../main/test/release_test_pt2.cc | 31 - .../video_coding/main/test/resampler_test.cc | 241 - modules/video_coding/main/test/rtp_player.cc | 466 - modules/video_coding/main/test/rtp_player.h | 106 - modules/video_coding/main/test/subfigure.m | 30 - modules/video_coding/main/test/test_macros.h | 45 - modules/video_coding/main/test/test_util.cc | 725 - modules/video_coding/main/test/test_util.h | 257 - modules/video_coding/main/test/tester_main.cc | 187 - .../video_coding/main/test/video_rtp_play.cc | 206 - .../main/test/video_rtp_play_mt.cc | 271 - .../video_coding/main/test/video_source.cc | 200 - modules/video_coding/main/test/video_source.h | 83 - modules/video_processing/main/OWNERS | 4 - .../main/interface/video_processing.h | 379 - .../main/interface/video_processing_defines.h | 40 - .../video_processing/main/source/Android.mk | 64 - .../main/source/brightness_detection.cc | 195 - .../main/source/brightness_detection.h | 46 - .../main/source/color_enhancement.cc | 68 - .../main/source/color_enhancement.h | 31 - .../main/source/color_enhancement_private.h | 273 - .../main/source/content_analysis.cc | 336 - .../main/source/content_analysis.h | 84 - .../main/source/deflickering.cc | 445 - .../main/source/deflickering.h | 69 - .../video_processing/main/source/denoising.cc | 180 - .../video_processing/main/source/denoising.h | 48 - .../main/source/frame_preprocessor.cc | 188 - .../main/source/frame_preprocessor.h | 82 - .../main/source/spatial_resampler.cc | 360 - .../main/source/spatial_resampler.h | 78 - .../main/source/video_decimator.cc | 235 - .../main/source/video_decimator.h | 65 - .../main/source/video_processing.gyp | 63 - .../main/source/video_processing_impl.cc | 368 - .../main/source/video_processing_impl.h | 106 - .../unit_test/brightness_detection_test.cc | 107 - .../test/unit_test/color_enhancement_test.cc | 109 - .../main/test/unit_test/createTable.m | 179 - .../main/test/unit_test/deflickering_test.cc | 81 - .../main/test/unit_test/denoising_test.cc | 118 - .../main/test/unit_test/readYUV420file.m | 45 - .../main/test/unit_test/unit_test.cc | 375 - .../main/test/unit_test/unit_test.h | 38 - .../main/test/unit_test/writeYUV420file.m | 22 - .../video_processing/main/test/vpm_tests.gyp | 55 - modules/video_render/OWNERS | 4 - .../main/interface/video_render.h | 291 - .../main/interface/video_render_defines.h | 92 - modules/video_render/main/source/Android.mk | 64 - .../webrtc/videoengine/ViEAndroidGLES20.java | 263 - .../org/webrtc/videoengine/ViERenderer.java | 58 - .../videoengine/ViESurfaceRenderer.java | 155 - .../Android/video_render_android_impl.cc | 404 - .../Android/video_render_android_impl.h | 162 - .../video_render_android_native_opengl2.cc | 496 - .../video_render_android_native_opengl2.h | 93 - .../video_render_android_surface_view.cc | 466 - .../video_render_android_surface_view.h | 96 - .../source/Android/video_render_opengles20.cc | 446 - .../source/Android/video_render_opengles20.h | 61 - .../external/video_render_external_impl.cc | 205 - .../external/video_render_external_impl.h | 134 - .../video_render/main/source/i_video_render.h | 133 - .../main/source/incoming_video_stream.cc | 412 - .../main/source/incoming_video_stream.h | 140 - .../source/linux/video_render_linux_impl.cc | 271 - .../source/linux/video_render_linux_impl.h | 136 - .../main/source/linux/video_x11_channel.cc | 317 - .../main/source/linux/video_x11_channel.h | 104 - .../main/source/linux/video_x11_render.cc | 154 - .../main/source/linux/video_x11_render.h | 58 - .../source/mac/cocoa_full_screen_window.h | 33 - .../source/mac/cocoa_full_screen_window.mm | 87 - .../main/source/mac/cocoa_render_view.h | 35 - .../main/source/mac/cocoa_render_view.mm | 56 - .../main/source/mac/video_render_agl.cc | 2005 - .../main/source/mac/video_render_agl.h | 185 - .../mac/video_render_mac_carbon_impl.cc | 299 - .../source/mac/video_render_mac_carbon_impl.h | 148 - .../source/mac/video_render_mac_cocoa_impl.cc | 271 - .../source/mac/video_render_mac_cocoa_impl.h | 143 - .../main/source/mac/video_render_nsopengl.cc | 1266 - .../main/source/mac/video_render_nsopengl.h | 191 - .../video_render/main/source/video_render.gyp | 177 - .../main/source/video_render_frames.cc | 209 - .../main/source/video_render_frames.h | 78 - .../main/source/video_render_impl.cc | 1088 - .../main/source/video_render_impl.h | 236 - .../main/source/windows/i_video_render_win.h | 118 - .../source/windows/video_render_direct3d9.cc | 1192 - .../source/windows/video_render_direct3d9.h | 267 - .../source/windows/video_render_directdraw.cc | 4022 -- .../source/windows/video_render_directdraw.h | 399 - .../windows/video_render_windows_impl.cc | 1013 - .../windows/video_render_windows_impl.h | 155 - .../main/test/testAPI/renderStartImage.bmp | Bin 304182 -> 0 bytes .../main/test/testAPI/testAPI.cpp | 721 - 1132 files changed, 408239 deletions(-) delete mode 100644 modules/audio_coding/NetEQ/OWNERS delete mode 100644 modules/audio_coding/NetEQ/main/interface/webrtc_neteq.h delete mode 100644 modules/audio_coding/NetEQ/main/interface/webrtc_neteq_help_macros.h delete mode 100644 modules/audio_coding/NetEQ/main/interface/webrtc_neteq_internal.h delete mode 100644 modules/audio_coding/NetEQ/main/source/Android.mk delete mode 100644 modules/audio_coding/NetEQ/main/source/accelerate.c delete mode 100644 modules/audio_coding/NetEQ/main/source/automode.c delete mode 100644 modules/audio_coding/NetEQ/main/source/automode.h delete mode 100644 modules/audio_coding/NetEQ/main/source/bgn_update.c delete mode 100644 modules/audio_coding/NetEQ/main/source/buffer_stats.h delete mode 100644 modules/audio_coding/NetEQ/main/source/bufstats_decision.c delete mode 100644 modules/audio_coding/NetEQ/main/source/cng_internal.c delete mode 100644 modules/audio_coding/NetEQ/main/source/codec_db.c delete mode 100644 modules/audio_coding/NetEQ/main/source/codec_db.h delete mode 100644 modules/audio_coding/NetEQ/main/source/codec_db_defines.h delete mode 100644 modules/audio_coding/NetEQ/main/source/correlator.c delete mode 100644 modules/audio_coding/NetEQ/main/source/delay_logging.h delete mode 100644 modules/audio_coding/NetEQ/main/source/dsp.c delete mode 100644 modules/audio_coding/NetEQ/main/source/dsp.h delete mode 100644 modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.c delete mode 100644 modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.h delete mode 100644 modules/audio_coding/NetEQ/main/source/dtmf_buffer.c delete mode 100644 modules/audio_coding/NetEQ/main/source/dtmf_buffer.h delete mode 100644 modules/audio_coding/NetEQ/main/source/dtmf_tonegen.c delete mode 100644 modules/audio_coding/NetEQ/main/source/dtmf_tonegen.h delete mode 100644 modules/audio_coding/NetEQ/main/source/expand.c delete mode 100644 modules/audio_coding/NetEQ/main/source/mcu.h delete mode 100644 modules/audio_coding/NetEQ/main/source/mcu_address_init.c delete mode 100644 modules/audio_coding/NetEQ/main/source/mcu_dsp_common.c delete mode 100644 modules/audio_coding/NetEQ/main/source/mcu_dsp_common.h delete mode 100644 modules/audio_coding/NetEQ/main/source/mcu_reset.c delete mode 100644 modules/audio_coding/NetEQ/main/source/merge.c delete mode 100644 modules/audio_coding/NetEQ/main/source/min_distortion.c delete mode 100644 modules/audio_coding/NetEQ/main/source/mix_voice_unvoice.c delete mode 100644 modules/audio_coding/NetEQ/main/source/mute_signal.c delete mode 100644 modules/audio_coding/NetEQ/main/source/neteq.gyp delete mode 100644 modules/audio_coding/NetEQ/main/source/neteq_defines.h delete mode 100644 modules/audio_coding/NetEQ/main/source/neteq_error_codes.h delete mode 100644 modules/audio_coding/NetEQ/main/source/neteq_statistics.h delete mode 100644 modules/audio_coding/NetEQ/main/source/normal.c delete mode 100644 modules/audio_coding/NetEQ/main/source/packet_buffer.c delete mode 100644 modules/audio_coding/NetEQ/main/source/packet_buffer.h delete mode 100644 modules/audio_coding/NetEQ/main/source/peak_detection.c delete mode 100644 modules/audio_coding/NetEQ/main/source/preemptive_expand.c delete mode 100644 modules/audio_coding/NetEQ/main/source/random_vector.c delete mode 100644 modules/audio_coding/NetEQ/main/source/recin.c delete mode 100644 modules/audio_coding/NetEQ/main/source/recout.c delete mode 100644 modules/audio_coding/NetEQ/main/source/rtcp.c delete mode 100644 modules/audio_coding/NetEQ/main/source/rtcp.h delete mode 100644 modules/audio_coding/NetEQ/main/source/rtp.c delete mode 100644 modules/audio_coding/NetEQ/main/source/rtp.h delete mode 100644 modules/audio_coding/NetEQ/main/source/set_fs.c delete mode 100644 modules/audio_coding/NetEQ/main/source/signal_mcu.c delete mode 100644 modules/audio_coding/NetEQ/main/source/split_and_insert.c delete mode 100644 modules/audio_coding/NetEQ/main/source/unmute_signal.c delete mode 100644 modules/audio_coding/NetEQ/main/source/webrtc_neteq.c delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.h delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.h delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.h delete mode 100644 modules/audio_coding/NetEQ/main/test/NetEqRTPplay.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/PayloadTypes.h delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPanalyze.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPcat.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPchange.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPencode.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPjitter.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/RTPtimeshift.cc delete mode 100644 modules/audio_coding/NetEQ/main/test/delay_tool/parse_delay_file.m delete mode 100644 modules/audio_coding/NetEQ/main/test/delay_tool/plot_neteq_delay.m delete mode 100644 modules/audio_coding/NetEQ/main/test/ptypes.txt delete mode 100644 modules/audio_coding/codecs/CNG/main/interface/webrtc_cng.h delete mode 100644 modules/audio_coding/codecs/CNG/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/CNG/main/source/cng.gyp delete mode 100644 modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.c delete mode 100644 modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.h delete mode 100644 modules/audio_coding/codecs/CNG/main/source/webrtc_cng.c delete mode 100644 modules/audio_coding/codecs/CNG/main/test/CNG.cpp delete mode 100644 modules/audio_coding/codecs/CNG/main/test/StdAfx.cpp delete mode 100644 modules/audio_coding/codecs/CNG/main/test/StdAfx.h delete mode 100644 modules/audio_coding/codecs/G711/main/interface/g711_interface.h delete mode 100644 modules/audio_coding/codecs/G711/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/G711/main/source/g711.c delete mode 100644 modules/audio_coding/codecs/G711/main/source/g711.gyp delete mode 100644 modules/audio_coding/codecs/G711/main/source/g711.h delete mode 100644 modules/audio_coding/codecs/G711/main/source/g711_interface.c delete mode 100644 modules/audio_coding/codecs/G711/main/testG711/testG711.cpp delete mode 100644 modules/audio_coding/codecs/G722/main/interface/g722_interface.h delete mode 100644 modules/audio_coding/codecs/G722/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/G722/main/source/g722.gyp delete mode 100644 modules/audio_coding/codecs/G722/main/source/g722_decode.c delete mode 100644 modules/audio_coding/codecs/G722/main/source/g722_enc_dec.h delete mode 100644 modules/audio_coding/codecs/G722/main/source/g722_encode.c delete mode 100644 modules/audio_coding/codecs/G722/main/source/g722_interface.c delete mode 100644 modules/audio_coding/codecs/G722/main/testG722/testG722.cpp delete mode 100644 modules/audio_coding/codecs/OWNERS delete mode 100644 modules/audio_coding/codecs/PCM16B/main/interface/pcm16b.h delete mode 100644 modules/audio_coding/codecs/PCM16B/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/PCM16B/main/source/pcm16b.c delete mode 100644 modules/audio_coding/codecs/PCM16B/main/source/pcm16b.gyp delete mode 100644 modules/audio_coding/codecs/iLBC/ilbc_test.gyp delete mode 100644 modules/audio_coding/codecs/iLBC/main/documentation/rfc3951.txt delete mode 100644 modules/audio_coding/codecs/iLBC/main/documentation/rfc3952.txt delete mode 100644 modules/audio_coding/codecs/iLBC/main/interface/ilbc.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/abs_quant.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/abs_quant.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/bw_expand.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/bw_expand.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_construct.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_construct.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy_augmentation.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy_augmentation.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy_calc.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_mem_energy_calc.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_search.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_search.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_search_core.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_search_core.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/chebyshev.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/chebyshev.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/comp_corr.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/comp_corr.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/complexityMeasures.m delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/constants.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/constants.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decode.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decode.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decode_residual.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decode_residual.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/defines.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/do_plc.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/do_plc.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/encode.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/encode.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/energy_inverse.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/energy_inverse.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enh_upsample.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enh_upsample.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enhancer.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enhancer.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enhancer_interface.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/enhancer_interface.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/filtered_cb_vecs.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/filtered_cb_vecs.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/frame_classify.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/frame_classify.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/gain_dequant.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/gain_dequant.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/gain_quant.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/gain_quant.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_cd_vec.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_cd_vec.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_lsp_poly.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_lsp_poly.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_sync_seq.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/get_sync_seq.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/hp_input.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/hp_input.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/hp_output.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/hp_output.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/ilbc.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/ilbc.gyp delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/init_decode.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/init_decode.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/init_encode.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/init_encode.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/interpolate.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/interpolate.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lpc_encode.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lpc_encode.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_check.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_check.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/my_corr.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/my_corr.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/nearest_neighbor.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/nearest_neighbor.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/pack_bits.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/pack_bits.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/refiner.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/refiner.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/smooth.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/smooth.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/sort_sq.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/sort_sq.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/split_vq.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/split_vq.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/state_construct.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/state_construct.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/state_search.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/state_search.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/swap_bytes.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/swap_bytes.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/unpack_bits.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/unpack_bits.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/vq3.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/vq3.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/vq4.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/vq4.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/window32_w32.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/window32_w32.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.h delete mode 100644 modules/audio_coding/codecs/iLBC/main/test/iLBC_test.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/test/iLBC_testLib.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/test/iLBC_testprogram.c delete mode 100644 modules/audio_coding/codecs/iLBC/main/test/iLBCtestscript.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/interface/isacfix.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/Android.mk delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/arith_routines.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/arith_routines_hist.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/arith_routines_logist.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/arith_routins.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/codec.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/decode.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/decode_bwe.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/decode_plc.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/encode.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/fft.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/fft.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/filterbanks.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/filters.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/initialize.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/isacfix.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/isacfix.gyp delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/lattice.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/lpc_masking_model.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/lpc_masking_model.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_filter.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/settings.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/structs.h delete mode 100644 modules/audio_coding/codecs/iSAC/fix/source/transform.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/ISACHist.cpp delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/Isac_test.cpp delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/ChannelFiles.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/InputFiles.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/InputFilesFew.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/ListOfTestCases.xls delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSAC.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSACPLC.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACLongtest.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACNB.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACPLC.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACRate.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfault.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfixfloat.txt delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/kenny.c delete mode 100644 modules/audio_coding/codecs/iSAC/fix/test/test_iSACfixfloat.c delete mode 100644 modules/audio_coding/codecs/iSAC/isac_test.gyp delete mode 100644 modules/audio_coding/codecs/iSAC/isacfix_test.gyp delete mode 100644 modules/audio_coding/codecs/iSAC/main/interface/isac.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/Android.mk delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/arith_routines.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/arith_routines.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/arith_routines_hist.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/arith_routines_logist.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/codec.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/crc.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/crc.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/decode.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/decode_bwe.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/encode.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/entropy_coding.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/entropy_coding.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/fft.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/fft.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/filter_functions.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/filterbank_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/filterbank_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/filterbanks.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/intialize.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/isac.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/isac.gyp delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lattice.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_analysis.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_analysis.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/lpc_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_filter.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/settings.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/structs.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/source/transform.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/QA/runiSACLongtest.txt delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfault.txt delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfixfloat.txt delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/ReleaseTest-API/ReleaseTest-API.cc delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/SwitchingSampRate/SwitchingSampRate.cc delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/debugUtility.h delete mode 100644 modules/audio_coding/codecs/iSAC/main/test/simpleKenny.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/util/utility.c delete mode 100644 modules/audio_coding/codecs/iSAC/main/util/utility.h delete mode 100644 modules/audio_coding/main/OWNERS delete mode 100644 modules/audio_coding/main/interface/audio_coding_module.h delete mode 100644 modules/audio_coding/main/interface/audio_coding_module_typedefs.h delete mode 100644 modules/audio_coding/main/source/Android.mk delete mode 100644 modules/audio_coding/main/source/acm_amr.cc delete mode 100644 modules/audio_coding/main/source/acm_amr.h delete mode 100644 modules/audio_coding/main/source/acm_amrwb.cc delete mode 100644 modules/audio_coding/main/source/acm_amrwb.h delete mode 100644 modules/audio_coding/main/source/acm_cng.cc delete mode 100644 modules/audio_coding/main/source/acm_cng.h delete mode 100644 modules/audio_coding/main/source/acm_codec_database.cc delete mode 100644 modules/audio_coding/main/source/acm_codec_database.h delete mode 100644 modules/audio_coding/main/source/acm_common_defs.h delete mode 100644 modules/audio_coding/main/source/acm_dtmf_detection.cc delete mode 100644 modules/audio_coding/main/source/acm_dtmf_detection.h delete mode 100644 modules/audio_coding/main/source/acm_dtmf_playout.cc delete mode 100644 modules/audio_coding/main/source/acm_dtmf_playout.h delete mode 100644 modules/audio_coding/main/source/acm_g722.cc delete mode 100644 modules/audio_coding/main/source/acm_g722.h delete mode 100644 modules/audio_coding/main/source/acm_g7221.cc delete mode 100644 modules/audio_coding/main/source/acm_g7221.h delete mode 100644 modules/audio_coding/main/source/acm_g7221c.cc delete mode 100644 modules/audio_coding/main/source/acm_g7221c.h delete mode 100644 modules/audio_coding/main/source/acm_g729.cc delete mode 100644 modules/audio_coding/main/source/acm_g729.h delete mode 100644 modules/audio_coding/main/source/acm_g7291.cc delete mode 100644 modules/audio_coding/main/source/acm_g7291.h delete mode 100644 modules/audio_coding/main/source/acm_generic_codec.cc delete mode 100644 modules/audio_coding/main/source/acm_generic_codec.h delete mode 100644 modules/audio_coding/main/source/acm_gsmfr.cc delete mode 100644 modules/audio_coding/main/source/acm_gsmfr.h delete mode 100644 modules/audio_coding/main/source/acm_ilbc.cc delete mode 100644 modules/audio_coding/main/source/acm_ilbc.h delete mode 100644 modules/audio_coding/main/source/acm_isac.cc delete mode 100644 modules/audio_coding/main/source/acm_isac.h delete mode 100644 modules/audio_coding/main/source/acm_isac_macros.h delete mode 100644 modules/audio_coding/main/source/acm_neteq.cc delete mode 100644 modules/audio_coding/main/source/acm_neteq.h delete mode 100644 modules/audio_coding/main/source/acm_opus.cc delete mode 100644 modules/audio_coding/main/source/acm_opus.h delete mode 100644 modules/audio_coding/main/source/acm_pcm16b.cc delete mode 100644 modules/audio_coding/main/source/acm_pcm16b.h delete mode 100644 modules/audio_coding/main/source/acm_pcma.cc delete mode 100644 modules/audio_coding/main/source/acm_pcma.h delete mode 100644 modules/audio_coding/main/source/acm_pcmu.cc delete mode 100644 modules/audio_coding/main/source/acm_pcmu.h delete mode 100644 modules/audio_coding/main/source/acm_red.cc delete mode 100644 modules/audio_coding/main/source/acm_red.h delete mode 100644 modules/audio_coding/main/source/acm_resampler.cc delete mode 100644 modules/audio_coding/main/source/acm_resampler.h delete mode 100644 modules/audio_coding/main/source/acm_speex.cc delete mode 100644 modules/audio_coding/main/source/acm_speex.h delete mode 100644 modules/audio_coding/main/source/audio_coding_module.cc delete mode 100644 modules/audio_coding/main/source/audio_coding_module.gyp delete mode 100644 modules/audio_coding/main/source/audio_coding_module_impl.cc delete mode 100644 modules/audio_coding/main/source/audio_coding_module_impl.h delete mode 100644 modules/audio_coding/main/test/ACMTest.cpp delete mode 100644 modules/audio_coding/main/test/ACMTest.h delete mode 100644 modules/audio_coding/main/test/APITest.cpp delete mode 100644 modules/audio_coding/main/test/APITest.h delete mode 100644 modules/audio_coding/main/test/Channel.cpp delete mode 100644 modules/audio_coding/main/test/Channel.h delete mode 100644 modules/audio_coding/main/test/EncodeDecodeTest.cpp delete mode 100644 modules/audio_coding/main/test/EncodeDecodeTest.h delete mode 100644 modules/audio_coding/main/test/EncodeToFileTest.cpp delete mode 100644 modules/audio_coding/main/test/EncodeToFileTest.h delete mode 100644 modules/audio_coding/main/test/PCMFile.cpp delete mode 100644 modules/audio_coding/main/test/PCMFile.h delete mode 100644 modules/audio_coding/main/test/RTPFile.cpp delete mode 100644 modules/audio_coding/main/test/RTPFile.h delete mode 100644 modules/audio_coding/main/test/SpatialAudio.cpp delete mode 100644 modules/audio_coding/main/test/SpatialAudio.h delete mode 100644 modules/audio_coding/main/test/TestAllCodecs.cpp delete mode 100644 modules/audio_coding/main/test/TestAllCodecs.h delete mode 100644 modules/audio_coding/main/test/TestFEC.cpp delete mode 100644 modules/audio_coding/main/test/TestFEC.h delete mode 100644 modules/audio_coding/main/test/TestStereo.cpp delete mode 100644 modules/audio_coding/main/test/TestStereo.h delete mode 100644 modules/audio_coding/main/test/TestVADDTX.cpp delete mode 100644 modules/audio_coding/main/test/TestVADDTX.h delete mode 100644 modules/audio_coding/main/test/Tester.cpp delete mode 100644 modules/audio_coding/main/test/TimedTrace.cpp delete mode 100644 modules/audio_coding/main/test/TimedTrace.h delete mode 100644 modules/audio_coding/main/test/TwoWayCommunication.cpp delete mode 100644 modules/audio_coding/main/test/TwoWayCommunication.h delete mode 100644 modules/audio_coding/main/test/iSACTest.cpp delete mode 100644 modules/audio_coding/main/test/iSACTest.h delete mode 100644 modules/audio_coding/main/test/utility.cpp delete mode 100644 modules/audio_coding/main/test/utility.h delete mode 100644 modules/audio_conference_mixer/OWNERS delete mode 100644 modules/audio_conference_mixer/interface/audio_conference_mixer.h delete mode 100644 modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h delete mode 100644 modules/audio_conference_mixer/source/Android.mk delete mode 100644 modules/audio_conference_mixer/source/audio_conference_mixer.gyp delete mode 100644 modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc delete mode 100644 modules/audio_conference_mixer/source/audio_conference_mixer_impl.h delete mode 100644 modules/audio_conference_mixer/source/audio_frame_manipulator.cc delete mode 100644 modules/audio_conference_mixer/source/audio_frame_manipulator.h delete mode 100644 modules/audio_conference_mixer/source/level_indicator.cc delete mode 100644 modules/audio_conference_mixer/source/level_indicator.h delete mode 100644 modules/audio_conference_mixer/source/memory_pool.h delete mode 100644 modules/audio_conference_mixer/source/memory_pool_generic.h delete mode 100644 modules/audio_conference_mixer/source/memory_pool_windows.h delete mode 100644 modules/audio_conference_mixer/source/time_scheduler.cc delete mode 100644 modules/audio_conference_mixer/source/time_scheduler.h delete mode 100644 modules/audio_conference_mixer/test/FunctionTest/functionTest.cpp delete mode 100644 modules/audio_conference_mixer/test/FunctionTest/functionTest.h delete mode 100644 modules/audio_device/OWNERS delete mode 100644 modules/audio_device/main/interface/audio_device.h delete mode 100644 modules/audio_device/main/interface/audio_device_defines.h delete mode 100644 modules/audio_device/main/source/Android.mk delete mode 100644 modules/audio_device/main/source/Android/audio_device_android_jni.cc delete mode 100644 modules/audio_device/main/source/Android/audio_device_android_jni.h delete mode 100644 modules/audio_device/main/source/Android/audio_device_android_native.cc delete mode 100644 modules/audio_device/main/source/Android/audio_device_android_native.h delete mode 100644 modules/audio_device/main/source/Android/audio_device_utility_android.cc delete mode 100644 modules/audio_device/main/source/Android/audio_device_utility_android.h delete mode 100644 modules/audio_device/main/source/Android/org/webrtc/voiceengine/AudioDeviceAndroid.java delete mode 100644 modules/audio_device/main/source/Dummy/audio_device_dummy.cc delete mode 100644 modules/audio_device/main/source/Dummy/audio_device_dummy.h delete mode 100644 modules/audio_device/main/source/Linux/alsasymboltable.cc delete mode 100644 modules/audio_device/main/source/Linux/alsasymboltable.h delete mode 100644 modules/audio_device/main/source/Linux/audio_device_linux_alsa.cc delete mode 100644 modules/audio_device/main/source/Linux/audio_device_linux_alsa.h delete mode 100644 modules/audio_device/main/source/Linux/audio_device_linux_pulse.cc delete mode 100644 modules/audio_device/main/source/Linux/audio_device_linux_pulse.h delete mode 100644 modules/audio_device/main/source/Linux/audio_device_utility_linux.cc delete mode 100644 modules/audio_device/main/source/Linux/audio_device_utility_linux.h delete mode 100644 modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.cc delete mode 100644 modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.h delete mode 100644 modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.cc delete mode 100644 modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.h delete mode 100644 modules/audio_device/main/source/Linux/latebindingsymboltable.cc delete mode 100644 modules/audio_device/main/source/Linux/latebindingsymboltable.h delete mode 100644 modules/audio_device/main/source/Linux/pulseaudiosymboltable.cc delete mode 100644 modules/audio_device/main/source/Linux/pulseaudiosymboltable.h delete mode 100644 modules/audio_device/main/source/Mac/audio_device_mac.cc delete mode 100644 modules/audio_device/main/source/Mac/audio_device_mac.h delete mode 100644 modules/audio_device/main/source/Mac/audio_device_utility_mac.cc delete mode 100644 modules/audio_device/main/source/Mac/audio_device_utility_mac.h delete mode 100644 modules/audio_device/main/source/Mac/audio_mixer_manager_mac.cc delete mode 100644 modules/audio_device/main/source/Mac/audio_mixer_manager_mac.h delete mode 100644 modules/audio_device/main/source/Mac/portaudio/pa_memorybarrier.h delete mode 100644 modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.c delete mode 100644 modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.h delete mode 100644 modules/audio_device/main/source/Windows/audio_device_utility_windows.cc delete mode 100644 modules/audio_device/main/source/Windows/audio_device_utility_windows.h delete mode 100644 modules/audio_device/main/source/Windows/audio_device_windows_core.cc delete mode 100644 modules/audio_device/main/source/Windows/audio_device_windows_core.h delete mode 100644 modules/audio_device/main/source/Windows/audio_device_windows_wave.cc delete mode 100644 modules/audio_device/main/source/Windows/audio_device_windows_wave.h delete mode 100644 modules/audio_device/main/source/Windows/audio_mixer_manager.cc delete mode 100644 modules/audio_device/main/source/Windows/audio_mixer_manager.h delete mode 100644 modules/audio_device/main/source/audio_device.gyp delete mode 100644 modules/audio_device/main/source/audio_device_buffer.cc delete mode 100644 modules/audio_device/main/source/audio_device_buffer.h delete mode 100644 modules/audio_device/main/source/audio_device_config.h delete mode 100644 modules/audio_device/main/source/audio_device_generic.cc delete mode 100644 modules/audio_device/main/source/audio_device_generic.h delete mode 100644 modules/audio_device/main/source/audio_device_impl.cc delete mode 100644 modules/audio_device/main/source/audio_device_impl.h delete mode 100644 modules/audio_device/main/source/audio_device_utility.cc delete mode 100644 modules/audio_device/main/source/audio_device_utility.h delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/.classpath delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/.project delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/AndroidManifest.xml delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/default.properties delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/gen/org/webrtc/voiceengine/test/R.java delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/jni/audio_device_android_test.cc delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/jni/org_webrtc_voiceengine_test_AudioDeviceAndroidTest.h delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/res/drawable/icon.png delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/res/layout/main.xml delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/res/values/strings.xml delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/AudioDeviceAndroid.java delete mode 100644 modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/test/AudioDeviceAndroidTest.java delete mode 100644 modules/audio_device/main/test/README.txt delete mode 100644 modules/audio_device/main/test/audio_device_test_api.cc delete mode 100644 modules/audio_device/main/test/audio_device_test_defines.h delete mode 100644 modules/audio_device/main/test/audio_device_test_func.cc delete mode 100644 modules/audio_device/main/test/audio_short16.pcm delete mode 100644 modules/audio_device/main/test/audio_short44.pcm delete mode 100644 modules/audio_device/main/test/audio_short48.pcm delete mode 100644 modules/audio_device/main/test/audio_short8.pcm delete mode 100644 modules/audio_device/main/test/func_test_manager.cc delete mode 100644 modules/audio_device/main/test/func_test_manager.h delete mode 100644 modules/audio_device/main/test/recordedRaw_48.pcm delete mode 100644 modules/audio_processing/OWNERS delete mode 100644 modules/audio_processing/aec/main/interface/echo_cancellation.h delete mode 100644 modules/audio_processing/aec/main/matlab/fullaec.m delete mode 100644 modules/audio_processing/aec/main/source/Android.mk delete mode 100644 modules/audio_processing/aec/main/source/aec.gyp delete mode 100644 modules/audio_processing/aec/main/source/aec_core.c delete mode 100644 modules/audio_processing/aec/main/source/aec_core.h delete mode 100644 modules/audio_processing/aec/main/source/aec_core_sse2.c delete mode 100644 modules/audio_processing/aec/main/source/aec_rdft.c delete mode 100644 modules/audio_processing/aec/main/source/aec_rdft.h delete mode 100644 modules/audio_processing/aec/main/source/aec_rdft_sse2.c delete mode 100644 modules/audio_processing/aec/main/source/echo_cancellation.c delete mode 100644 modules/audio_processing/aec/main/source/resampler.c delete mode 100644 modules/audio_processing/aec/main/source/resampler.h delete mode 100644 modules/audio_processing/aecm/main/interface/echo_control_mobile.h delete mode 100644 modules/audio_processing/aecm/main/matlab/compsup.m delete mode 100644 modules/audio_processing/aecm/main/matlab/getBspectrum.m delete mode 100644 modules/audio_processing/aecm/main/matlab/hisser2.m delete mode 100644 modules/audio_processing/aecm/main/matlab/main2.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/align.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/calcFilterGain.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/calcStepSize.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/fallerEstimator.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/getBspectrum.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/hisser2.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/mainProgram.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/simEnvironment.m delete mode 100644 modules/audio_processing/aecm/main/matlab/matlab/updateSettings.m delete mode 100644 modules/audio_processing/aecm/main/matlab/waitbar_j.m delete mode 100644 modules/audio_processing/aecm/main/source/Android.mk delete mode 100644 modules/audio_processing/aecm/main/source/aecm.gyp delete mode 100644 modules/audio_processing/aecm/main/source/aecm_core.c delete mode 100644 modules/audio_processing/aecm/main/source/aecm_core.h delete mode 100644 modules/audio_processing/aecm/main/source/echo_control_mobile.c delete mode 100644 modules/audio_processing/agc/main/interface/gain_control.h delete mode 100644 modules/audio_processing/agc/main/matlab/getGains.m delete mode 100644 modules/audio_processing/agc/main/source/Android.mk delete mode 100644 modules/audio_processing/agc/main/source/agc.gyp delete mode 100644 modules/audio_processing/agc/main/source/analog_agc.c delete mode 100644 modules/audio_processing/agc/main/source/analog_agc.h delete mode 100644 modules/audio_processing/agc/main/source/digital_agc.c delete mode 100644 modules/audio_processing/agc/main/source/digital_agc.h delete mode 100644 modules/audio_processing/main/apm_tests.gyp delete mode 100644 modules/audio_processing/main/interface/audio_processing.h delete mode 100644 modules/audio_processing/main/source/Android.mk delete mode 100644 modules/audio_processing/main/source/apm.gyp delete mode 100644 modules/audio_processing/main/source/audio_buffer.cc delete mode 100644 modules/audio_processing/main/source/audio_buffer.h delete mode 100644 modules/audio_processing/main/source/audio_processing_impl.cc delete mode 100644 modules/audio_processing/main/source/audio_processing_impl.h delete mode 100644 modules/audio_processing/main/source/echo_cancellation_impl.cc delete mode 100644 modules/audio_processing/main/source/echo_cancellation_impl.h delete mode 100644 modules/audio_processing/main/source/echo_control_mobile_impl.cc delete mode 100644 modules/audio_processing/main/source/echo_control_mobile_impl.h delete mode 100644 modules/audio_processing/main/source/gain_control_impl.cc delete mode 100644 modules/audio_processing/main/source/gain_control_impl.h delete mode 100644 modules/audio_processing/main/source/high_pass_filter_impl.cc delete mode 100644 modules/audio_processing/main/source/high_pass_filter_impl.h delete mode 100644 modules/audio_processing/main/source/level_estimator_impl.cc delete mode 100644 modules/audio_processing/main/source/level_estimator_impl.h delete mode 100644 modules/audio_processing/main/source/noise_suppression_impl.cc delete mode 100644 modules/audio_processing/main/source/noise_suppression_impl.h delete mode 100644 modules/audio_processing/main/source/processing_component.cc delete mode 100644 modules/audio_processing/main/source/processing_component.h delete mode 100644 modules/audio_processing/main/source/splitting_filter.cc delete mode 100644 modules/audio_processing/main/source/splitting_filter.h delete mode 100644 modules/audio_processing/main/source/voice_detection_impl.cc delete mode 100644 modules/audio_processing/main/source/voice_detection_impl.h delete mode 100644 modules/audio_processing/main/test/android/apmtest/AndroidManifest.xml delete mode 100644 modules/audio_processing/main/test/android/apmtest/default.properties delete mode 100644 modules/audio_processing/main/test/android/apmtest/jni/Android.mk delete mode 100644 modules/audio_processing/main/test/android/apmtest/jni/Application.mk delete mode 100644 modules/audio_processing/main/test/android/apmtest/jni/main.c delete mode 100644 modules/audio_processing/main/test/android/apmtest/res/values/strings.xml delete mode 100644 modules/audio_processing/main/test/process_test/Android.mk delete mode 100644 modules/audio_processing/main/test/process_test/apmtest.m delete mode 100644 modules/audio_processing/main/test/process_test/process_test.cc delete mode 100644 modules/audio_processing/main/test/unit_test/Android.mk delete mode 100644 modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.cc delete mode 100644 modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.h delete mode 100644 modules/audio_processing/main/test/unit_test/audio_processing_unittest.proto delete mode 100644 modules/audio_processing/main/test/unit_test/unit_test.cc delete mode 100644 modules/audio_processing/ns/main/interface/noise_suppression.h delete mode 100644 modules/audio_processing/ns/main/interface/noise_suppression_x.h delete mode 100644 modules/audio_processing/ns/main/source/Android.mk delete mode 100644 modules/audio_processing/ns/main/source/defines.h delete mode 100644 modules/audio_processing/ns/main/source/noise_suppression.c delete mode 100644 modules/audio_processing/ns/main/source/noise_suppression_x.c delete mode 100644 modules/audio_processing/ns/main/source/ns.gyp delete mode 100644 modules/audio_processing/ns/main/source/ns_core.c delete mode 100644 modules/audio_processing/ns/main/source/ns_core.h delete mode 100644 modules/audio_processing/ns/main/source/nsx_core.c delete mode 100644 modules/audio_processing/ns/main/source/nsx_core.h delete mode 100644 modules/audio_processing/ns/main/source/nsx_defines.h delete mode 100644 modules/audio_processing/ns/main/source/windows_private.h delete mode 100644 modules/audio_processing/utility/Android.mk delete mode 100644 modules/audio_processing/utility/fft4g.c delete mode 100644 modules/audio_processing/utility/fft4g.h delete mode 100644 modules/audio_processing/utility/ring_buffer.c delete mode 100644 modules/audio_processing/utility/ring_buffer.h delete mode 100644 modules/audio_processing/utility/util.gyp delete mode 100644 modules/interface/module.h delete mode 100644 modules/interface/module_common_types.h delete mode 100644 modules/media_file/OWNERS delete mode 100644 modules/media_file/interface/media_file.h delete mode 100644 modules/media_file/interface/media_file_defines.h delete mode 100644 modules/media_file/source/Android.mk delete mode 100644 modules/media_file/source/avi_file.cc delete mode 100644 modules/media_file/source/avi_file.h delete mode 100644 modules/media_file/source/media_file.gyp delete mode 100644 modules/media_file/source/media_file_impl.cc delete mode 100644 modules/media_file/source/media_file_impl.h delete mode 100644 modules/media_file/source/media_file_utility.cc delete mode 100644 modules/media_file/source/media_file_utility.h delete mode 100644 modules/rtp_rtcp/OWNERS delete mode 100644 modules/rtp_rtcp/interface/rtp_rtcp.h delete mode 100644 modules/rtp_rtcp/interface/rtp_rtcp_defines.h delete mode 100644 modules/rtp_rtcp/source/Android.mk delete mode 100644 modules/rtp_rtcp/source/Bitrate.h delete mode 100644 modules/rtp_rtcp/source/H264/bitstream_builder.cc delete mode 100644 modules/rtp_rtcp/source/H264/bitstream_builder.h delete mode 100644 modules/rtp_rtcp/source/H264/bitstream_parser.cc delete mode 100644 modules/rtp_rtcp/source/H264/bitstream_parser.h delete mode 100644 modules/rtp_rtcp/source/H264/h264_information.cc delete mode 100644 modules/rtp_rtcp/source/H264/h264_information.h delete mode 100644 modules/rtp_rtcp/source/H264/rtp_sender_h264.cc delete mode 100644 modules/rtp_rtcp/source/H264/rtp_sender_h264.h delete mode 100644 modules/rtp_rtcp/source/bandwidth_management.cc delete mode 100644 modules/rtp_rtcp/source/bandwidth_management.h delete mode 100644 modules/rtp_rtcp/source/bitrate.cc delete mode 100644 modules/rtp_rtcp/source/bwe_defines.h delete mode 100644 modules/rtp_rtcp/source/dtmf_queue.cc delete mode 100644 modules/rtp_rtcp/source/dtmf_queue.h delete mode 100644 modules/rtp_rtcp/source/fec_private_tables.h delete mode 100644 modules/rtp_rtcp/source/forward_error_correction.cc delete mode 100644 modules/rtp_rtcp/source/forward_error_correction.h delete mode 100644 modules/rtp_rtcp/source/forward_error_correction_internal.cc delete mode 100644 modules/rtp_rtcp/source/forward_error_correction_internal.h delete mode 100644 modules/rtp_rtcp/source/h263_information.cc delete mode 100644 modules/rtp_rtcp/source/h263_information.h delete mode 100644 modules/rtp_rtcp/source/overuse_detector.cc delete mode 100644 modules/rtp_rtcp/source/overuse_detector.h delete mode 100644 modules/rtp_rtcp/source/receiver_fec.cc delete mode 100644 modules/rtp_rtcp/source/receiver_fec.h delete mode 100644 modules/rtp_rtcp/source/remote_rate_control.cc delete mode 100644 modules/rtp_rtcp/source/remote_rate_control.h delete mode 100644 modules/rtp_rtcp/source/rtcp_receiver.cc delete mode 100644 modules/rtp_rtcp/source/rtcp_receiver.h delete mode 100644 modules/rtp_rtcp/source/rtcp_receiver_help.cc delete mode 100644 modules/rtp_rtcp/source/rtcp_receiver_help.h delete mode 100644 modules/rtp_rtcp/source/rtcp_sender.cc delete mode 100644 modules/rtp_rtcp/source/rtcp_sender.h delete mode 100644 modules/rtp_rtcp/source/rtcp_utility.cc delete mode 100644 modules/rtp_rtcp/source/rtcp_utility.h delete mode 100644 modules/rtp_rtcp/source/rtp_format_vp8.cc delete mode 100644 modules/rtp_rtcp/source/rtp_format_vp8.h delete mode 100644 modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc delete mode 100644 modules/rtp_rtcp/source/rtp_receiver.cc delete mode 100644 modules/rtp_rtcp/source/rtp_receiver.h delete mode 100644 modules/rtp_rtcp/source/rtp_receiver_audio.cc delete mode 100644 modules/rtp_rtcp/source/rtp_receiver_audio.h delete mode 100644 modules/rtp_rtcp/source/rtp_receiver_video.cc delete mode 100644 modules/rtp_rtcp/source/rtp_receiver_video.h delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp.gyp delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp_config.h delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp_impl.cc delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp_impl.h delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp_private.h delete mode 100644 modules/rtp_rtcp/source/rtp_rtcp_tests.gyp delete mode 100644 modules/rtp_rtcp/source/rtp_sender.cc delete mode 100644 modules/rtp_rtcp/source/rtp_sender.h delete mode 100644 modules/rtp_rtcp/source/rtp_sender_audio.cc delete mode 100644 modules/rtp_rtcp/source/rtp_sender_audio.h delete mode 100644 modules/rtp_rtcp/source/rtp_sender_video.cc delete mode 100644 modules/rtp_rtcp/source/rtp_sender_video.h delete mode 100644 modules/rtp_rtcp/source/rtp_utility.cc delete mode 100644 modules/rtp_rtcp/source/rtp_utility.h delete mode 100644 modules/rtp_rtcp/source/ssrc_database.cc delete mode 100644 modules/rtp_rtcp/source/ssrc_database.h delete mode 100644 modules/rtp_rtcp/source/tmmbr_help.cc delete mode 100644 modules/rtp_rtcp/source/tmmbr_help.h delete mode 100644 modules/rtp_rtcp/source/video_codec_information.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.cpp delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.cpp delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.cpp delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWETester.cpp delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.cpp delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.cc delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.cc delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.h delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc delete mode 100644 modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h delete mode 100644 modules/rtp_rtcp/test/bitstreamTest/bitstreamTest.cpp delete mode 100644 modules/rtp_rtcp/test/bwe_standalone.gyp delete mode 100644 modules/rtp_rtcp/test/testAPI/H263_CIF_IFRAME.bin delete mode 100644 modules/rtp_rtcp/test/testAPI/H263_CIF_PFRAME.bin delete mode 100644 modules/rtp_rtcp/test/testAPI/H263_QCIF_IFRAME.bin delete mode 100644 modules/rtp_rtcp/test/testAPI/testAPI.cpp delete mode 100644 modules/rtp_rtcp/test/testFec/test_fec.cc delete mode 100644 modules/rtp_rtcp/test/testFec/test_fec.gyp delete mode 100644 modules/rtp_rtcp/test/testH263Parser/H263Foreman_CIF_Iframe.bin delete mode 100644 modules/rtp_rtcp/test/testH263Parser/H263Foreman_CIF_Pframe.bin delete mode 100644 modules/rtp_rtcp/test/testH263Parser/testH263Parser.cpp delete mode 100644 modules/rtp_rtcp/test/testRateControl/testRateControl.cpp delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR0.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR1.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR2.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR3.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4_1.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4_2.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR5.bin delete mode 100644 modules/rtp_rtcp/test/testTMMBR/testTMMBR.cpp delete mode 100644 modules/rtp_rtcp/test/test_bwe/test_bwe.gyp delete mode 100644 modules/rtp_rtcp/test/test_bwe/unit_test.cc delete mode 100644 modules/udp_transport/OWNERS delete mode 100644 modules/udp_transport/interface/udp_transport.h delete mode 100644 modules/udp_transport/source/Android.mk delete mode 100644 modules/udp_transport/source/traffic_control_windows.cc delete mode 100644 modules/udp_transport/source/traffic_control_windows.h delete mode 100644 modules/udp_transport/source/udp_socket2_manager_windows.cc delete mode 100644 modules/udp_transport/source/udp_socket2_manager_windows.h delete mode 100644 modules/udp_transport/source/udp_socket2_windows.cc delete mode 100644 modules/udp_transport/source/udp_socket2_windows.h delete mode 100644 modules/udp_transport/source/udp_socket_linux.cc delete mode 100644 modules/udp_transport/source/udp_socket_linux.h delete mode 100644 modules/udp_transport/source/udp_socket_manager_linux.cc delete mode 100644 modules/udp_transport/source/udp_socket_manager_linux.h delete mode 100644 modules/udp_transport/source/udp_socket_manager_windows.cc delete mode 100644 modules/udp_transport/source/udp_socket_manager_windows.h delete mode 100644 modules/udp_transport/source/udp_socket_manager_wrapper.cc delete mode 100644 modules/udp_transport/source/udp_socket_manager_wrapper.h delete mode 100644 modules/udp_transport/source/udp_socket_windows.cc delete mode 100644 modules/udp_transport/source/udp_socket_windows.h delete mode 100644 modules/udp_transport/source/udp_socket_wrapper.cc delete mode 100644 modules/udp_transport/source/udp_socket_wrapper.h delete mode 100644 modules/udp_transport/source/udp_transport.gyp delete mode 100644 modules/udp_transport/source/udp_transport_impl.cc delete mode 100644 modules/udp_transport/source/udp_transport_impl.h delete mode 100644 modules/udp_transport/test/SocketManagerTest.cpp delete mode 100644 modules/utility/OWNERS delete mode 100644 modules/utility/interface/file_player.h delete mode 100644 modules/utility/interface/file_recorder.h delete mode 100644 modules/utility/interface/process_thread.h delete mode 100644 modules/utility/interface/rtp_dump.h delete mode 100644 modules/utility/source/Android.mk delete mode 100644 modules/utility/source/coder.cc delete mode 100644 modules/utility/source/coder.h delete mode 100644 modules/utility/source/file_player_impl.cc delete mode 100644 modules/utility/source/file_player_impl.h delete mode 100644 modules/utility/source/file_recorder_impl.cc delete mode 100644 modules/utility/source/file_recorder_impl.h delete mode 100644 modules/utility/source/frame_scaler.cc delete mode 100644 modules/utility/source/frame_scaler.h delete mode 100644 modules/utility/source/process_thread_impl.cc delete mode 100644 modules/utility/source/process_thread_impl.h delete mode 100644 modules/utility/source/rtp_dump_impl.cc delete mode 100644 modules/utility/source/rtp_dump_impl.h delete mode 100644 modules/utility/source/utility.gyp delete mode 100644 modules/utility/source/video_coder.cc delete mode 100644 modules/utility/source/video_coder.h delete mode 100644 modules/utility/source/video_frames_queue.cc delete mode 100644 modules/utility/source/video_frames_queue.h delete mode 100644 modules/utility/test/testAPI.cpp delete mode 100644 modules/video_capture/OWNERS delete mode 100644 modules/video_capture/main/interface/video_capture.h delete mode 100644 modules/video_capture/main/interface/video_capture_defines.h delete mode 100644 modules/video_capture/main/source/Android.mk delete mode 100644 modules/video_capture/main/source/Android/device_info_android.cc delete mode 100644 modules/video_capture/main/source/Android/device_info_android.h delete mode 100644 modules/video_capture/main/source/Android/java/org/webrtc/videoengine/CaptureCapabilityAndroid.java delete mode 100644 modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureAndroid.java delete mode 100644 modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java delete mode 100644 modules/video_capture/main/source/Android/video_capture_android.cc delete mode 100644 modules/video_capture/main/source/Android/video_capture_android.h delete mode 100644 modules/video_capture/main/source/Linux/device_info_linux.cc delete mode 100644 modules/video_capture/main/source/Linux/device_info_linux.h delete mode 100644 modules/video_capture/main/source/Linux/video_capture_linux.cc delete mode 100644 modules/video_capture/main/source/Linux/video_capture_linux.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.cc delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.cc delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.mm delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.mm delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_utility.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.h delete mode 100644 modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.mm delete mode 100644 modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.cc delete mode 100644 modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.h delete mode 100644 modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.cc delete mode 100644 modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.h delete mode 100644 modules/video_capture/main/source/Mac/video_capture_mac.cc delete mode 100644 modules/video_capture/main/source/Mac/video_capture_mac.h delete mode 100644 modules/video_capture/main/source/Windows/capture_delay_values_windows.h delete mode 100644 modules/video_capture/main/source/Windows/device_info_windows.cc delete mode 100644 modules/video_capture/main/source/Windows/device_info_windows.h delete mode 100644 modules/video_capture/main/source/Windows/help_functions_windows.cc delete mode 100644 modules/video_capture/main/source/Windows/help_functions_windows.h delete mode 100644 modules/video_capture/main/source/Windows/sink_filter_windows.cc delete mode 100644 modules/video_capture/main/source/Windows/sink_filter_windows.h delete mode 100644 modules/video_capture/main/source/Windows/video_capture_factory_windows.cc delete mode 100644 modules/video_capture/main/source/Windows/video_capture_windows.cc delete mode 100644 modules/video_capture/main/source/Windows/video_capture_windows.h delete mode 100644 modules/video_capture/main/source/device_info_impl.cc delete mode 100644 modules/video_capture/main/source/device_info_impl.h delete mode 100644 modules/video_capture/main/source/video_capture.gyp delete mode 100644 modules/video_capture/main/source/video_capture_config.h delete mode 100644 modules/video_capture/main/source/video_capture_delay.h delete mode 100644 modules/video_capture/main/source/video_capture_impl.cc delete mode 100644 modules/video_capture/main/source/video_capture_impl.h delete mode 100644 modules/video_capture/main/source/vplib_conversions.cc delete mode 100644 modules/video_capture/main/source/vplib_conversions.h delete mode 100644 modules/video_capture/main/test/Android/.classpath delete mode 100644 modules/video_capture/main/test/Android/.project delete mode 100644 modules/video_capture/main/test/Android/AndroidManifest.xml delete mode 100644 modules/video_capture/main/test/Android/default.properties delete mode 100644 modules/video_capture/main/test/Android/gen/org/webrtc/capturemoduleandroidtest/R.java delete mode 100644 modules/video_capture/main/test/Android/jni/Android.mk delete mode 100644 modules/video_capture/main/test/Android/jni/org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest.h delete mode 100644 modules/video_capture/main/test/Android/jni/video_capture_module_android_test_jni.cc delete mode 100644 modules/video_capture/main/test/Android/res/drawable-hdpi/icon.png delete mode 100644 modules/video_capture/main/test/Android/res/drawable-ldpi/icon.png delete mode 100644 modules/video_capture/main/test/Android/res/drawable-mdpi/icon.png delete mode 100644 modules/video_capture/main/test/Android/res/layout/main.xml delete mode 100644 modules/video_capture/main/test/Android/res/values/strings.xml delete mode 100644 modules/video_capture/main/test/Android/src/org/webrtc/capturemoduleandroidtest/VideoCaptureJavaTest.java delete mode 100644 modules/video_capture/main/test/Android/src/org/webrtc/capturemoduleandroidtest/VideoCaptureModuleTest.java delete mode 100644 modules/video_capture/main/test/testAPI/Logger.cpp delete mode 100644 modules/video_capture/main/test/testAPI/Logger.h delete mode 100644 modules/video_capture/main/test/testAPI/Renderer.cpp delete mode 100644 modules/video_capture/main/test/testAPI/Renderer.h delete mode 100644 modules/video_capture/main/test/testAPI/cocoa_renderer.h delete mode 100644 modules/video_capture/main/test/testAPI/cocoa_renderer.mm delete mode 100644 modules/video_capture/main/test/testAPI/testAPI.cpp delete mode 100644 modules/video_capture/main/test/testAPI/testCameraEncoder.cpp delete mode 100644 modules/video_capture/main/test/testAPI/testCameraEncoder.h delete mode 100644 modules/video_capture/main/test/testAPI/testDefines.h delete mode 100644 modules/video_capture/main/test/testAPI/testExternalCapture.cpp delete mode 100644 modules/video_capture/main/test/testAPI/testExternalCapture.h delete mode 100644 modules/video_capture/main/test/testAPI/testPlatformDependent.cpp delete mode 100644 modules/video_capture/main/test/testAPI/testPlatformDependent.h delete mode 100644 modules/video_coding/codecs/OWNERS delete mode 100644 modules/video_coding/codecs/i420/main/interface/i420.h delete mode 100644 modules/video_coding/codecs/i420/main/source/Android.mk delete mode 100644 modules/video_coding/codecs/i420/main/source/i420.cc delete mode 100644 modules/video_coding/codecs/i420/main/source/i420.gyp delete mode 100644 modules/video_coding/codecs/interface/video_codec_interface.h delete mode 100644 modules/video_coding/codecs/interface/video_error_codes.h delete mode 100644 modules/video_coding/codecs/test_framework/benchmark.cc delete mode 100644 modules/video_coding/codecs/test_framework/benchmark.h delete mode 100644 modules/video_coding/codecs/test_framework/exportfig.m delete mode 100644 modules/video_coding/codecs/test_framework/normal_async_test.cc delete mode 100644 modules/video_coding/codecs/test_framework/normal_async_test.h delete mode 100644 modules/video_coding/codecs/test_framework/normal_test.cc delete mode 100644 modules/video_coding/codecs/test_framework/normal_test.h delete mode 100644 modules/video_coding/codecs/test_framework/packet_loss_test.cc delete mode 100644 modules/video_coding/codecs/test_framework/packet_loss_test.h delete mode 100644 modules/video_coding/codecs/test_framework/performance_test.cc delete mode 100644 modules/video_coding/codecs/test_framework/performance_test.h delete mode 100644 modules/video_coding/codecs/test_framework/plotBenchmark.m delete mode 100644 modules/video_coding/codecs/test_framework/test.cc delete mode 100644 modules/video_coding/codecs/test_framework/test.h delete mode 100644 modules/video_coding/codecs/test_framework/test_framework.gyp delete mode 100644 modules/video_coding/codecs/test_framework/unit_test.cc delete mode 100644 modules/video_coding/codecs/test_framework/unit_test.h delete mode 100644 modules/video_coding/codecs/test_framework/video_buffer.cc delete mode 100644 modules/video_coding/codecs/test_framework/video_buffer.h delete mode 100644 modules/video_coding/codecs/test_framework/video_source.cc delete mode 100644 modules/video_coding/codecs/test_framework/video_source.h delete mode 100644 modules/video_coding/codecs/vp8/main/interface/vp8.h delete mode 100644 modules/video_coding/codecs/vp8/main/source/Android.mk delete mode 100644 modules/video_coding/codecs/vp8/main/source/vp8.cc delete mode 100644 modules/video_coding/codecs/vp8/main/source/vp8.gyp delete mode 100644 modules/video_coding/codecs/vp8/main/test/benchmark.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/benchmark.h delete mode 100644 modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h delete mode 100644 modules/video_coding/codecs/vp8/main/test/normal_async_test.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/normal_async_test.h delete mode 100644 modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/packet_loss_test.h delete mode 100644 modules/video_coding/codecs/vp8/main/test/tester.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/unit_test.cc delete mode 100644 modules/video_coding/codecs/vp8/main/test/unit_test.h delete mode 100644 modules/video_coding/main/OWNERS delete mode 100644 modules/video_coding/main/interface/video_coding.h delete mode 100644 modules/video_coding/main/interface/video_coding_defines.h delete mode 100644 modules/video_coding/main/source/Android.mk delete mode 100644 modules/video_coding/main/source/codec_database.cc delete mode 100644 modules/video_coding/main/source/codec_database.h delete mode 100644 modules/video_coding/main/source/codec_timer.cc delete mode 100644 modules/video_coding/main/source/codec_timer.h delete mode 100644 modules/video_coding/main/source/content_metrics_processing.cc delete mode 100644 modules/video_coding/main/source/content_metrics_processing.h delete mode 100644 modules/video_coding/main/source/encoded_frame.cc delete mode 100644 modules/video_coding/main/source/encoded_frame.h delete mode 100644 modules/video_coding/main/source/er_tables_xor.h delete mode 100644 modules/video_coding/main/source/event.h delete mode 100644 modules/video_coding/main/source/exp_filter.cc delete mode 100644 modules/video_coding/main/source/exp_filter.h delete mode 100644 modules/video_coding/main/source/fec_tables_xor.h delete mode 100644 modules/video_coding/main/source/frame_buffer.cc delete mode 100644 modules/video_coding/main/source/frame_buffer.h delete mode 100644 modules/video_coding/main/source/frame_dropper.cc delete mode 100644 modules/video_coding/main/source/frame_dropper.h delete mode 100644 modules/video_coding/main/source/frame_list.cc delete mode 100644 modules/video_coding/main/source/frame_list.h delete mode 100644 modules/video_coding/main/source/generic_decoder.cc delete mode 100644 modules/video_coding/main/source/generic_decoder.h delete mode 100644 modules/video_coding/main/source/generic_encoder.cc delete mode 100644 modules/video_coding/main/source/generic_encoder.h delete mode 100644 modules/video_coding/main/source/inter_frame_delay.cc delete mode 100644 modules/video_coding/main/source/inter_frame_delay.h delete mode 100644 modules/video_coding/main/source/internal_defines.h delete mode 100644 modules/video_coding/main/source/jitter_buffer.cc delete mode 100644 modules/video_coding/main/source/jitter_buffer.h delete mode 100644 modules/video_coding/main/source/jitter_buffer_common.h delete mode 100644 modules/video_coding/main/source/jitter_estimator.cc delete mode 100644 modules/video_coding/main/source/jitter_estimator.h delete mode 100644 modules/video_coding/main/source/media_opt_util.cc delete mode 100644 modules/video_coding/main/source/media_opt_util.h delete mode 100644 modules/video_coding/main/source/media_optimization.cc delete mode 100644 modules/video_coding/main/source/media_optimization.h delete mode 100644 modules/video_coding/main/source/nack_fec_tables.h delete mode 100644 modules/video_coding/main/source/packet.cc delete mode 100644 modules/video_coding/main/source/packet.h delete mode 100644 modules/video_coding/main/source/qm_select.cc delete mode 100644 modules/video_coding/main/source/qm_select.h delete mode 100644 modules/video_coding/main/source/qm_select_data.h delete mode 100644 modules/video_coding/main/source/receiver.cc delete mode 100644 modules/video_coding/main/source/receiver.h delete mode 100644 modules/video_coding/main/source/rtt_filter.cc delete mode 100644 modules/video_coding/main/source/rtt_filter.h delete mode 100644 modules/video_coding/main/source/session_info.cc delete mode 100644 modules/video_coding/main/source/session_info.h delete mode 100644 modules/video_coding/main/source/tick_time.h delete mode 100644 modules/video_coding/main/source/timestamp_extrapolator.cc delete mode 100644 modules/video_coding/main/source/timestamp_extrapolator.h delete mode 100644 modules/video_coding/main/source/timestamp_map.cc delete mode 100644 modules/video_coding/main/source/timestamp_map.h delete mode 100644 modules/video_coding/main/source/timing.cc delete mode 100644 modules/video_coding/main/source/timing.h delete mode 100644 modules/video_coding/main/source/video_coding.gyp delete mode 100644 modules/video_coding/main/source/video_coding_impl.cc delete mode 100644 modules/video_coding/main/source/video_coding_impl.h delete mode 100644 modules/video_coding/main/source/video_coding_test.gyp delete mode 100644 modules/video_coding/main/test/codec_database_test.cc delete mode 100644 modules/video_coding/main/test/codec_database_test.h delete mode 100644 modules/video_coding/main/test/decode_from_storage_test.cc delete mode 100644 modules/video_coding/main/test/generic_codec_test.cc delete mode 100644 modules/video_coding/main/test/generic_codec_test.h delete mode 100644 modules/video_coding/main/test/jitter_buffer_test.cc delete mode 100644 modules/video_coding/main/test/jitter_estimate_test.cc delete mode 100644 modules/video_coding/main/test/jitter_estimate_test.h delete mode 100644 modules/video_coding/main/test/media_opt_test.cc delete mode 100644 modules/video_coding/main/test/media_opt_test.h delete mode 100644 modules/video_coding/main/test/mt_rx_tx_test.cc delete mode 100644 modules/video_coding/main/test/normal_test.cc delete mode 100644 modules/video_coding/main/test/normal_test.h delete mode 100644 modules/video_coding/main/test/plotJitterEstimate.m delete mode 100644 modules/video_coding/main/test/plotReceiveTrace.m delete mode 100644 modules/video_coding/main/test/plotTimingTest.m delete mode 100644 modules/video_coding/main/test/quality_modes_test.cc delete mode 100644 modules/video_coding/main/test/quality_modes_test.h delete mode 100644 modules/video_coding/main/test/receiver_tests.h delete mode 100644 modules/video_coding/main/test/receiver_timing_tests.cc delete mode 100644 modules/video_coding/main/test/release_test.cc delete mode 100644 modules/video_coding/main/test/release_test.h delete mode 100644 modules/video_coding/main/test/release_test_pt2.cc delete mode 100644 modules/video_coding/main/test/resampler_test.cc delete mode 100644 modules/video_coding/main/test/rtp_player.cc delete mode 100644 modules/video_coding/main/test/rtp_player.h delete mode 100644 modules/video_coding/main/test/subfigure.m delete mode 100644 modules/video_coding/main/test/test_macros.h delete mode 100644 modules/video_coding/main/test/test_util.cc delete mode 100644 modules/video_coding/main/test/test_util.h delete mode 100644 modules/video_coding/main/test/tester_main.cc delete mode 100644 modules/video_coding/main/test/video_rtp_play.cc delete mode 100644 modules/video_coding/main/test/video_rtp_play_mt.cc delete mode 100644 modules/video_coding/main/test/video_source.cc delete mode 100644 modules/video_coding/main/test/video_source.h delete mode 100644 modules/video_processing/main/OWNERS delete mode 100644 modules/video_processing/main/interface/video_processing.h delete mode 100644 modules/video_processing/main/interface/video_processing_defines.h delete mode 100644 modules/video_processing/main/source/Android.mk delete mode 100644 modules/video_processing/main/source/brightness_detection.cc delete mode 100644 modules/video_processing/main/source/brightness_detection.h delete mode 100644 modules/video_processing/main/source/color_enhancement.cc delete mode 100644 modules/video_processing/main/source/color_enhancement.h delete mode 100644 modules/video_processing/main/source/color_enhancement_private.h delete mode 100644 modules/video_processing/main/source/content_analysis.cc delete mode 100644 modules/video_processing/main/source/content_analysis.h delete mode 100644 modules/video_processing/main/source/deflickering.cc delete mode 100644 modules/video_processing/main/source/deflickering.h delete mode 100644 modules/video_processing/main/source/denoising.cc delete mode 100644 modules/video_processing/main/source/denoising.h delete mode 100644 modules/video_processing/main/source/frame_preprocessor.cc delete mode 100644 modules/video_processing/main/source/frame_preprocessor.h delete mode 100644 modules/video_processing/main/source/spatial_resampler.cc delete mode 100644 modules/video_processing/main/source/spatial_resampler.h delete mode 100644 modules/video_processing/main/source/video_decimator.cc delete mode 100644 modules/video_processing/main/source/video_decimator.h delete mode 100644 modules/video_processing/main/source/video_processing.gyp delete mode 100644 modules/video_processing/main/source/video_processing_impl.cc delete mode 100644 modules/video_processing/main/source/video_processing_impl.h delete mode 100644 modules/video_processing/main/test/unit_test/brightness_detection_test.cc delete mode 100644 modules/video_processing/main/test/unit_test/color_enhancement_test.cc delete mode 100644 modules/video_processing/main/test/unit_test/createTable.m delete mode 100644 modules/video_processing/main/test/unit_test/deflickering_test.cc delete mode 100644 modules/video_processing/main/test/unit_test/denoising_test.cc delete mode 100644 modules/video_processing/main/test/unit_test/readYUV420file.m delete mode 100644 modules/video_processing/main/test/unit_test/unit_test.cc delete mode 100644 modules/video_processing/main/test/unit_test/unit_test.h delete mode 100644 modules/video_processing/main/test/unit_test/writeYUV420file.m delete mode 100644 modules/video_processing/main/test/vpm_tests.gyp delete mode 100644 modules/video_render/OWNERS delete mode 100644 modules/video_render/main/interface/video_render.h delete mode 100644 modules/video_render/main/interface/video_render_defines.h delete mode 100644 modules/video_render/main/source/Android.mk delete mode 100644 modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViEAndroidGLES20.java delete mode 100644 modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViERenderer.java delete mode 100644 modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViESurfaceRenderer.java delete mode 100644 modules/video_render/main/source/Android/video_render_android_impl.cc delete mode 100644 modules/video_render/main/source/Android/video_render_android_impl.h delete mode 100644 modules/video_render/main/source/Android/video_render_android_native_opengl2.cc delete mode 100644 modules/video_render/main/source/Android/video_render_android_native_opengl2.h delete mode 100644 modules/video_render/main/source/Android/video_render_android_surface_view.cc delete mode 100644 modules/video_render/main/source/Android/video_render_android_surface_view.h delete mode 100644 modules/video_render/main/source/Android/video_render_opengles20.cc delete mode 100644 modules/video_render/main/source/Android/video_render_opengles20.h delete mode 100644 modules/video_render/main/source/external/video_render_external_impl.cc delete mode 100644 modules/video_render/main/source/external/video_render_external_impl.h delete mode 100644 modules/video_render/main/source/i_video_render.h delete mode 100644 modules/video_render/main/source/incoming_video_stream.cc delete mode 100644 modules/video_render/main/source/incoming_video_stream.h delete mode 100644 modules/video_render/main/source/linux/video_render_linux_impl.cc delete mode 100644 modules/video_render/main/source/linux/video_render_linux_impl.h delete mode 100644 modules/video_render/main/source/linux/video_x11_channel.cc delete mode 100644 modules/video_render/main/source/linux/video_x11_channel.h delete mode 100644 modules/video_render/main/source/linux/video_x11_render.cc delete mode 100644 modules/video_render/main/source/linux/video_x11_render.h delete mode 100644 modules/video_render/main/source/mac/cocoa_full_screen_window.h delete mode 100644 modules/video_render/main/source/mac/cocoa_full_screen_window.mm delete mode 100644 modules/video_render/main/source/mac/cocoa_render_view.h delete mode 100644 modules/video_render/main/source/mac/cocoa_render_view.mm delete mode 100644 modules/video_render/main/source/mac/video_render_agl.cc delete mode 100644 modules/video_render/main/source/mac/video_render_agl.h delete mode 100644 modules/video_render/main/source/mac/video_render_mac_carbon_impl.cc delete mode 100644 modules/video_render/main/source/mac/video_render_mac_carbon_impl.h delete mode 100644 modules/video_render/main/source/mac/video_render_mac_cocoa_impl.cc delete mode 100644 modules/video_render/main/source/mac/video_render_mac_cocoa_impl.h delete mode 100644 modules/video_render/main/source/mac/video_render_nsopengl.cc delete mode 100644 modules/video_render/main/source/mac/video_render_nsopengl.h delete mode 100644 modules/video_render/main/source/video_render.gyp delete mode 100644 modules/video_render/main/source/video_render_frames.cc delete mode 100644 modules/video_render/main/source/video_render_frames.h delete mode 100644 modules/video_render/main/source/video_render_impl.cc delete mode 100644 modules/video_render/main/source/video_render_impl.h delete mode 100644 modules/video_render/main/source/windows/i_video_render_win.h delete mode 100644 modules/video_render/main/source/windows/video_render_direct3d9.cc delete mode 100644 modules/video_render/main/source/windows/video_render_direct3d9.h delete mode 100644 modules/video_render/main/source/windows/video_render_directdraw.cc delete mode 100644 modules/video_render/main/source/windows/video_render_directdraw.h delete mode 100644 modules/video_render/main/source/windows/video_render_windows_impl.cc delete mode 100644 modules/video_render/main/source/windows/video_render_windows_impl.h delete mode 100644 modules/video_render/main/test/testAPI/renderStartImage.bmp delete mode 100644 modules/video_render/main/test/testAPI/testAPI.cpp diff --git a/modules/audio_coding/NetEQ/OWNERS b/modules/audio_coding/NetEQ/OWNERS deleted file mode 100644 index 603aa40b6..000000000 --- a/modules/audio_coding/NetEQ/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -hlundin@google.com -tlegrand@google.com diff --git a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq.h b/modules/audio_coding/NetEQ/main/interface/webrtc_neteq.h deleted file mode 100644 index c25b0448f..000000000 --- a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the main API for NetEQ. Helper macros are located in webrtc_neteq_help_macros.h, - * while some internal API functions are found in webrtc_neteq_internal.h. - */ - -#include "typedefs.h" - -#ifndef WEBRTC_NETEQ_H -#define WEBRTC_NETEQ_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -/********************************************************** - * Definitions - */ - -enum WebRtcNetEQDecoder -{ - kDecoderReservedStart, - kDecoderPCMu, - kDecoderPCMa, - kDecoderILBC, - kDecoderISAC, - kDecoderISACswb, - kDecoderPCM16B, - kDecoderPCM16Bwb, - kDecoderPCM16Bswb32kHz, - kDecoderPCM16Bswb48kHz, - kDecoderG722, - kDecoderRED, - kDecoderAVT, - kDecoderCNG, - kDecoderArbitrary, - kDecoderG729, - kDecoderG729_1, - kDecoderG726_16, - kDecoderG726_24, - kDecoderG726_32, - kDecoderG726_40, - kDecoderG722_1_16, - kDecoderG722_1_24, - kDecoderG722_1_32, - kDecoderG722_1C_24, - kDecoderG722_1C_32, - kDecoderG722_1C_48, - kDecoderSPEEX_8, - kDecoderSPEEX_16, - kDecoderGSMFR, - kDecoderAMR, - kDecoderAMRWB, - kDecoderReservedEnd -}; - -enum WebRtcNetEQNetworkType -{ - kUDPNormal, - kUDPVideoSync, - kTCPNormal, - kTCPLargeJitter, - kTCPXLargeJitter -}; - -enum WebRtcNetEQOutputType -{ - kOutputNormal, - kOutputPLC, - kOutputCNG, - kOutputPLCtoCNG, - kOutputVADPassive -}; - -enum WebRtcNetEQPlayoutMode -{ - kPlayoutOn, kPlayoutOff, kPlayoutFax, kPlayoutStreaming -}; - -/* Available modes for background noise (inserted after long expands) */ -enum WebRtcNetEQBGNMode -{ - kBGNOn, /* default "normal" behavior with eternal noise */ - kBGNFade, /* noise fades to zero after some time */ - kBGNOff -/* background noise is always zero */ -}; - -/************************************************* - * Definitions of decoder calls and the default - * API function calls for each codec - */ - -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncDecode)(void* state, WebRtc_Word16* encoded, - WebRtc_Word16 len, WebRtc_Word16* decoded, - WebRtc_Word16* speechType); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncDecodePLC)(void* state, WebRtc_Word16* decoded, - WebRtc_Word16 frames); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncDecodeInit)(void* state); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncAddLatePkt)(void* state, WebRtc_Word16* encoded, - WebRtc_Word16 len); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncGetMDinfo)(void* state); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncGetPitchInfo)(void* state, WebRtc_Word16* encoded, - WebRtc_Word16* length); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncUpdBWEst)(void* state, const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts); -typedef WebRtc_Word16 (*WebRtcNetEQ_FuncGetErrorCode)(void* state); - -/********************************************************** - * Structures - */ - -typedef struct -{ - enum WebRtcNetEQDecoder codec; - WebRtc_Word16 payloadType; - WebRtcNetEQ_FuncDecode funcDecode; - WebRtcNetEQ_FuncDecode funcDecodeRCU; - WebRtcNetEQ_FuncDecodePLC funcDecodePLC; - WebRtcNetEQ_FuncDecodeInit funcDecodeInit; - WebRtcNetEQ_FuncAddLatePkt funcAddLatePkt; - WebRtcNetEQ_FuncGetMDinfo funcGetMDinfo; - WebRtcNetEQ_FuncGetPitchInfo funcGetPitch; - WebRtcNetEQ_FuncUpdBWEst funcUpdBWEst; - WebRtcNetEQ_FuncGetErrorCode funcGetErrorCode; - void* codec_state; - WebRtc_UWord16 codec_fs; -} WebRtcNetEQ_CodecDef; - -typedef struct -{ - WebRtc_UWord16 fraction_lost; - WebRtc_UWord32 cum_lost; - WebRtc_UWord32 ext_max; - WebRtc_UWord32 jitter; -} WebRtcNetEQ_RTCPStat; - -/********************************************************** - * NETEQ Functions - */ - -/* Info functions */ - -#define WEBRTC_NETEQ_MAX_ERROR_NAME 40 -int WebRtcNetEQ_GetVersion(WebRtc_Word8 *version); -int WebRtcNetEQ_GetErrorCode(void *inst); -int WebRtcNetEQ_GetErrorName(int errorCode, WebRtc_Word8 *errorName, int maxStrLen); - -/* Instance memory assign functions */ - -int WebRtcNetEQ_AssignSize(int *sizeinbytes); -int WebRtcNetEQ_Assign(void **inst, void *NETEQ_inst_Addr); -int WebRtcNetEQ_GetRecommendedBufferSize(void *inst, enum WebRtcNetEQDecoder *codec, - int noOfCodecs, enum WebRtcNetEQNetworkType nwType, - int *MaxNoOfPackets, int *sizeinbytes); -int WebRtcNetEQ_AssignBuffer(void *inst, int MaxNoOfPackets, void *NETEQ_Buffer_Addr, - int sizeinbytes); - -/* Init functions */ - -int WebRtcNetEQ_Init(void *inst, WebRtc_UWord16 fs); -int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon); -int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs); -int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode); -int WebRtcNetEQ_SetBGNMode(void *inst, enum WebRtcNetEQBGNMode bgnMode); -int WebRtcNetEQ_GetBGNMode(const void *inst, enum WebRtcNetEQBGNMode *bgnMode); - -/* Codec Database functions */ - -int WebRtcNetEQ_CodecDbReset(void *inst); -int WebRtcNetEQ_CodecDbAdd(void *inst, WebRtcNetEQ_CodecDef *codecInst); -int WebRtcNetEQ_CodecDbRemove(void *inst, enum WebRtcNetEQDecoder codec); -int WebRtcNetEQ_CodecDbGetSizeInfo(void *inst, WebRtc_Word16 *UsedEntries, - WebRtc_Word16 *MaxEntries); -int WebRtcNetEQ_CodecDbGetCodecInfo(void *inst, WebRtc_Word16 Entry, - enum WebRtcNetEQDecoder *codec); - -/* Real-time functions */ - -int WebRtcNetEQ_RecIn(void *inst, WebRtc_Word16 *p_w16datagramstart, WebRtc_Word16 w16_RTPlen, - WebRtc_UWord32 uw32_timeRec); -int WebRtcNetEQ_RecOut(void *inst, WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len); -int WebRtcNetEQ_GetRTCPStats(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst); -int WebRtcNetEQ_GetRTCPStatsNoReset(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst); -int WebRtcNetEQ_GetSpeechTimeStamp(void *inst, WebRtc_UWord32 *timestamp); -int WebRtcNetEQ_GetSpeechOutputType(void *inst, enum WebRtcNetEQOutputType *outputType); - -/* VQmon related functions */ -int WebRtcNetEQ_VQmonRecOutStatistics(void *inst, WebRtc_UWord16 *validVoiceDurationMs, - WebRtc_UWord16 *concealedVoiceDurationMs, - WebRtc_UWord8 *concealedVoiceFlags); -int WebRtcNetEQ_VQmonGetConfiguration(void *inst, WebRtc_UWord16 *absMaxDelayMs, - WebRtc_UWord8 *adaptationRate); -int WebRtcNetEQ_VQmonGetRxStatistics(void *inst, WebRtc_UWord16 *avgDelayMs, - WebRtc_UWord16 *maxDelayMs); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_help_macros.h b/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_help_macros.h deleted file mode 100644 index c63b3464d..000000000 --- a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_help_macros.h +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some helper macros that can be used when loading the - * NetEQ codec database. - */ - -#ifndef WEBRTC_NETEQ_HELP_MACROS_H -#define WEBRTC_NETEQ_HELP_MACROS_H - -#ifndef NULL -#define NULL 0 -#endif - -/********************************************************** - * Help macros for NetEQ initialization - */ - -#define SET_CODEC_PAR(inst,decoder,pt,state,fs) \ - inst.codec=decoder; \ - inst.payloadType=pt; \ - inst.codec_state=state; \ - inst.codec_fs=fs; - -#define SET_PCMU_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG711_DecodeU; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCMA_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG711_DecodeA; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_ILBC_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIlbcfix_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcIlbcfix_NetEqPlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIlbcfix_Decoderinit30Ms; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_ISAC_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsac_Decode; \ - inst.funcDecodeRCU=(WebRtcNetEQ_FuncDecode)WebRtcIsac_DecodeRcu; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsac_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsac_UpdateBwEstimate; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsac_GetErrorCode; - -#define SET_ISACfix_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsacfix_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsacfix_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsacfix_UpdateBwEstimate; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsacfix_GetErrorCode; - -#define SET_ISACSWB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcIsac_Decode; \ - inst.funcDecodeRCU=(WebRtcNetEQ_FuncDecode)WebRtcIsac_DecodeRcu; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcIsac_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcIsac_UpdateBwEstimate; \ - inst.funcGetErrorCode=(WebRtcNetEQ_FuncGetErrorCode)WebRtcIsac_GetErrorCode; - -#define SET_G729_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG729_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG729_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG729_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G729_1_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7291_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7291_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=(WebRtcNetEQ_FuncUpdBWEst)WebRtcG7291_DecodeBwe; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_WB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_PCM16B_SWB32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - - -#define SET_PCM16B_SWB48_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcPcm16b_DecodeW16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG722_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG722_DecoderInit;\ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_16_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc16; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit16; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc24; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221_Decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221_DecodePlc32; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221_DecoderInit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc24; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc32; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G722_1C_48_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG7221C_Decode48; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcG7221C_DecodePlc48; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG7221C_DecoderInit48; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AMR_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcAmr_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcAmr_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcAmr_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AMRWB_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcAmrWb_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcAmrWb_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcAmrWb_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_GSMFR_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcGSMFR_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcGSMFR_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcGSMFR_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_16_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode16; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit16; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_24_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode24; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit24; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_32_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode32; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit32; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_G726_40_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcG726_decode40; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcG726_decoderinit40; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_SPEEX_FUNCTIONS(inst) \ - inst.funcDecode=(WebRtcNetEQ_FuncDecode)WebRtcSpeex_Decode; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=(WebRtcNetEQ_FuncDecodePLC)WebRtcSpeex_DecodePlc; \ - inst.funcDecodeInit=(WebRtcNetEQ_FuncDecodeInit)WebRtcSpeex_DecoderInit; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_RED_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_AVT_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#define SET_CNG_FUNCTIONS(inst) \ - inst.funcDecode=NULL; \ - inst.funcDecodeRCU=NULL; \ - inst.funcDecodePLC=NULL; \ - inst.funcDecodeInit=NULL; \ - inst.funcAddLatePkt=NULL; \ - inst.funcGetMDinfo=NULL; \ - inst.funcGetPitch=NULL; \ - inst.funcUpdBWEst=NULL; \ - inst.funcGetErrorCode=NULL; - -#endif /* WEBRTC_NETEQ_HELP_MACROS_H */ - diff --git a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_internal.h b/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_internal.h deleted file mode 100644 index a00cd9618..000000000 --- a/modules/audio_coding/NetEQ/main/interface/webrtc_neteq_internal.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the internal API functions. - */ - -#include "typedefs.h" - -#ifndef WEBRTC_NETEQ_INTERNAL_H -#define WEBRTC_NETEQ_INTERNAL_H - -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct -{ - WebRtc_UWord8 payloadType; - WebRtc_UWord16 sequenceNumber; - WebRtc_UWord32 timeStamp; - WebRtc_UWord32 SSRC; - WebRtc_UWord8 markerBit; -} WebRtcNetEQ_RTPInfo; - -/**************************************************************************** - * WebRtcNetEQ_RecInRTPStruct(...) - * - * Alternative RecIn function, used when the RTP data has already been - * parsed into an RTP info struct (WebRtcNetEQ_RTPInfo). - * - * Input: - * - inst : NetEQ instance - * - rtpInfo : Pointer to RTP info - * - payloadPtr : Pointer to the RTP payload (first byte after header) - * - payloadLenBytes : Length (in bytes) of the payload in payloadPtr - * - timeRec : Receive time (in timestamps of the used codec) - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_RecInRTPStruct(void *inst, WebRtcNetEQ_RTPInfo *rtpInfo, - const WebRtc_UWord8 *payloadPtr, WebRtc_Word16 payloadLenBytes, - WebRtc_UWord32 timeRec); - -/**************************************************************************** - * WebRtcNetEQ_GetMasterSlaveInfoSize(...) - * - * Get size in bytes for master/slave struct msInfo used in - * WebRtcNetEQ_RecOutMasterSlave. - * - * Return value : Struct size in bytes - * - */ - -int WebRtcNetEQ_GetMasterSlaveInfoSize(); - -/**************************************************************************** - * WebRtcNetEQ_RecOutMasterSlave(...) - * - * RecOut function for running several NetEQ instances in master/slave mode. - * One master can be used to control several slaves. - * The MasterSlaveInfo struct must be allocated outside NetEQ. - * Use function WebRtcNetEQ_GetMasterSlaveInfoSize to get the size needed. - * - * Input: - * - inst : NetEQ instance - * - isMaster : Non-zero indicates that this is the master channel - * - msInfo : (slave only) Information from master - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - msInfo : (master only) Information to slave(s) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutMasterSlave(void *inst, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len, void *msInfo, - WebRtc_Word16 isMaster); - -typedef struct -{ - WebRtc_UWord16 currentBufferSize; /* current jitter buffer size in ms */ - WebRtc_UWord16 preferredBufferSize; /* preferred (optimal) buffer size in ms */ - WebRtc_UWord16 currentPacketLossRate; /* loss rate (network + late) (in Q14) */ - WebRtc_UWord16 currentDiscardRate; /* late loss rate (in Q14) */ - WebRtc_UWord16 currentExpandRate; /* fraction (of original stream) of synthesized speech - * inserted through expansion (in Q14) */ - WebRtc_UWord16 currentPreemptiveRate; /* fraction of synthesized speech inserted through - * pre-emptive expansion (in Q14) */ - WebRtc_UWord16 currentAccelerateRate; /* fraction of data removed through acceleration - * (in Q14) */ -} WebRtcNetEQ_NetworkStatistics; - -typedef struct -{ - WebRtc_UWord32 jbMinSize; /* smallest Jitter Buffer size during call in ms */ - WebRtc_UWord32 jbMaxSize; /* largest Jitter Buffer size during call in ms */ - WebRtc_UWord32 jbAvgSize; /* the average JB size, measured over time - ms */ - WebRtc_UWord32 jbChangeCount; /* number of times the Jitter Buffer changed - * (using Accelerate or Pre-emptive Expand) */ - WebRtc_UWord32 lateLossMs; /* amount (in ms) of audio data received late */ - WebRtc_UWord32 accelerateMs; /* milliseconds removed to reduce jitter buffer size */ - WebRtc_UWord32 flushedMs; /* milliseconds discarded through buffer flushing */ - WebRtc_UWord32 generatedSilentMs; /* milliseconds of generated silence */ - WebRtc_UWord32 interpolatedVoiceMs; /* milliseconds of synthetic audio data - * (non-background noise) */ - WebRtc_UWord32 interpolatedSilentMs; /* milliseconds of synthetic audio data - * (background noise level) */ - WebRtc_UWord32 countExpandMoreThan120ms; /* count of tiny expansions in output audio */ - WebRtc_UWord32 countExpandMoreThan250ms; /* count of small expansions in output audio */ - WebRtc_UWord32 countExpandMoreThan500ms; /* count of medium expansions in output audio */ - WebRtc_UWord32 countExpandMoreThan2000ms; /* count of long expansions in output audio */ - WebRtc_UWord32 longestExpandDurationMs; /* duration of longest audio drop-out */ - WebRtc_UWord32 countIAT500ms; /* count of times we got small network outage (inter-arrival - * time in [500, 1000) ms) */ - WebRtc_UWord32 countIAT1000ms; /* count of times we got medium network outage - * (inter-arrival time in [1000, 2000) ms) */ - WebRtc_UWord32 countIAT2000ms; /* count of times we got large network outage - * (inter-arrival time >= 2000 ms) */ - WebRtc_UWord32 longestIATms; /* longest packet inter-arrival time in ms */ - WebRtc_UWord32 minPacketDelayMs; /* min time incoming Packet "waited" to be played */ - WebRtc_UWord32 maxPacketDelayMs; /* max time incoming Packet "waited" to be played */ - WebRtc_UWord32 avgPacketDelayMs; /* avg time incoming Packet "waited" to be played */ -} WebRtcNetEQ_JitterStatistics; - -/* - * Get the "in-call" statistics from NetEQ. - * The statistics are reset after the query. - */ -int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *stats); - -/* - * Get the optimal buffer size calculated for the current network conditions. - */ -int WebRtcNetEQ_GetPreferredBufferSize(void *inst, WebRtc_UWord16 *preferredBufferSize); - -/* - * Get the current buffer size in ms. Return value is 0 if ok, -1 if error. - */ -int WebRtcNetEQ_GetCurrentDelay(const void *inst, WebRtc_UWord16 *currentDelayMs); - -/* - * Get the "post-call" jitter statistics from NetEQ. - * The statistics are not reset by the query. Use the function - * WebRtcNetEQ_ResetJitterStatistics to reset the statistics. - */ -int WebRtcNetEQ_GetJitterStatistics(void *inst, WebRtcNetEQ_JitterStatistics *jitterStats); - -/* - * Reset "post-call" jitter statistics. - */ -int WebRtcNetEQ_ResetJitterStatistics(void *inst); - -/***********************************************/ -/* Functions for post-decode VAD functionality */ -/***********************************************/ - -/* NetEQ must be compiled with the flag NETEQ_VAD enabled for these functions to work. */ - -/* - * VAD function pointer types - * - * These function pointers match the definitions of webrtc VAD functions WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in webrtc_vad.h. - */ -typedef WebRtc_Word16 (*WebRtcNetEQ_VADInitFunction)(void *VAD_inst); -typedef WebRtc_Word16 (*WebRtcNetEQ_VADSetmodeFunction)(void *VAD_inst, WebRtc_Word16 mode); -typedef WebRtc_Word16 (*WebRtcNetEQ_VADFunction)(void *VAD_inst, WebRtc_Word16 fs, - WebRtc_Word16 *frame, WebRtc_Word16 frameLen); - -/**************************************************************************** - * WebRtcNetEQ_SetVADInstance(...) - * - * Provide a pointer to an allocated VAD instance. If function is never - * called or it is called with NULL pointer as VAD_inst, the post-decode - * VAD functionality is disabled. Also provide pointers to init, setmode - * and VAD functions. These are typically pointers to WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in the - * interface file webrtc_vad.h. - * - * Input: - * - NetEQ_inst : NetEQ instance - * - VADinst : VAD instance - * - initFunction : Pointer to VAD init function - * - setmodeFunction : Pointer to VAD setmode function - * - VADfunction : Pointer to VAD function - * - * Output: - * - NetEQ_inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADInstance(void *NetEQ_inst, void *VAD_inst, - WebRtcNetEQ_VADInitFunction initFunction, - WebRtcNetEQ_VADSetmodeFunction setmodeFunction, - WebRtcNetEQ_VADFunction VADFunction); - -/**************************************************************************** - * WebRtcNetEQ_SetVADMode(...) - * - * Pass an aggressiveness mode parameter to the post-decode VAD instance. - * If this function is never called, mode 0 (quality mode) is used as default. - * - * Input: - * - inst : NetEQ instance - * - mode : mode parameter (same range as WebRtc VAD mode) - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADMode(void *NetEQ_inst, WebRtc_Word16 mode); - -/**************************************************************************** - * WebRtcNetEQ_RecOutNoDecode(...) - * - * Special RecOut that does not do any decoding. - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutNoDecode(void *inst, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_FlushBuffers(...) - * - * Flush packet and speech buffers. Does not reset codec database or - * jitter statistics. - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_FlushBuffers(void *inst); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/Android.mk b/modules/audio_coding/NetEQ/main/source/Android.mk deleted file mode 100644 index d0b60d129..000000000 --- a/modules/audio_coding/NetEQ/main/source/Android.mk +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_neteq -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := accelerate.c \ - automode.c \ - bgn_update.c \ - bufstats_decision.c \ - cng_internal.c \ - codec_db.c \ - correlator.c \ - dsp.c \ - dsp_helpfunctions.c \ - dtmf_buffer.c \ - dtmf_tonegen.c \ - expand.c \ - mcu_address_init.c \ - mcu_dsp_common.c \ - mcu_reset.c \ - merge.c \ - min_distortion.c \ - mix_voice_unvoice.c \ - mute_signal.c \ - normal.c \ - packet_buffer.c \ - peak_detection.c \ - preemptive_expand.c \ - random_vector.c \ - recin.c \ - recout.c \ - rtcp.c \ - rtp.c \ - set_fs.c \ - signal_mcu.c \ - split_and_insert.c \ - unmute_signal.c \ - webrtc_neteq.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DNETEQ_VOICEENGINE_CODECS' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../codecs/CNG/main/interface \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -#include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/NetEQ/main/source/accelerate.c b/modules/audio_coding/NetEQ/main/source/accelerate.c deleted file mode 100644 index edec9c6e9..000000000 --- a/modules/audio_coding/NetEQ/main/source/accelerate.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the Accelerate algorithm that is used to reduce - * the delay by removing a part of the audio stream. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define ACCELERATE_CORR_LEN 50 -#define ACCELERATE_MIN_LAG 10 -#define ACCELERATE_MAX_LAG 60 -#define ACCELERATE_DOWNSAMPLED_LEN (ACCELERATE_CORR_LEN + ACCELERATE_MAX_LAG) - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_downSampSpeech 110 0 109 - WebRtc_Word32 pw32_corr 2*50 110 209 - WebRtc_Word16 pw16_corr 50 0 49 - - Total: 110+2*50 - */ - -#define SCRATCH_PW16_DS_SPEECH 0 -#define SCRATCH_PW32_CORR ACCELERATE_DOWNSAMPLED_LEN -#define SCRATCH_PW16_CORR 0 - -/**************************************************************************** - * WebRtcNetEQ_Accelerate(...) - * - * This function tries to shorten the audio data by removing one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - BGNonly : If non-zero, Accelerate will only remove the last - * DEFAULT_TIME_ADJUST seconds of the input. - * No signal matching is done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Accelerate(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - const WebRtc_Word16 *pw16_decoded, int len, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly) -{ - -#ifdef SCRATCH - /* Use scratch memory for internal temporary vectors */ - WebRtc_Word16 *pw16_downSampSpeech = pw16_scratchPtr + SCRATCH_PW16_DS_SPEECH; - WebRtc_Word32 *pw32_corr = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_PW32_CORR); - WebRtc_Word16 *pw16_corr = pw16_scratchPtr + SCRATCH_PW16_CORR; -#else - /* Allocate memory for temporary vectors */ - WebRtc_Word16 pw16_downSampSpeech[ACCELERATE_DOWNSAMPLED_LEN]; - WebRtc_Word32 pw32_corr[ACCELERATE_CORR_LEN]; - WebRtc_Word16 pw16_corr[ACCELERATE_CORR_LEN]; -#endif - WebRtc_Word16 w16_decodedMax = 0; - WebRtc_Word16 w16_tmp; - WebRtc_Word16 w16_tmp2; - WebRtc_Word32 w32_tmp; - WebRtc_Word32 w32_tmp2; - - const WebRtc_Word16 w16_startLag = ACCELERATE_MIN_LAG; - const WebRtc_Word16 w16_endLag = ACCELERATE_MAX_LAG; - const WebRtc_Word16 w16_corrLen = ACCELERATE_CORR_LEN; - const WebRtc_Word16 *pw16_vec1, *pw16_vec2; - WebRtc_Word16 *pw16_vectmp; - WebRtc_Word16 w16_inc, w16_startfact; - WebRtc_Word16 w16_bestIndex, w16_bestVal; - WebRtc_Word16 w16_VAD = 1; - WebRtc_Word16 fsMult; - WebRtc_Word16 fsMult120; - WebRtc_Word32 w32_en1, w32_en2, w32_cc; - WebRtc_Word16 w16_en1, w16_en2; - WebRtc_Word16 w16_en1Scale, w16_en2Scale; - WebRtc_Word16 w16_sqrtEn1En2; - WebRtc_Word16 w16_bestCorr = 0; - int ok; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fsMult = WebRtcNetEQ_CalcFsMult(inst->fs); /* Calculate fs/8000 */ - - /* Pre-calculate common multiplication with fsMult */ - fsMult120 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fsMult, 120); /* 15 ms */ - - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* Sanity check for len variable; must be (almost) 30 ms - (120*fsMult + max(bestIndex)) */ - if (len < (WebRtc_Word16) WEBRTC_SPL_MUL_16_16((120 + 119), fsMult)) - { - /* Length of decoded data too short */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return NETEQ_OTHER_ERROR; - } - - /***********************************/ - /* Special operations for BGN only */ - /***********************************/ - - /* Check if "background noise only" flag is set */ - if (BGNonly) - { - /* special operation for BGN only; simply remove a chunk of data */ - w16_bestIndex = DEFAULT_TIME_ADJUST * WEBRTC_SPL_LSHIFT_W16(fsMult, 3); /* X*fs/1000 */ - - /* Sanity check for bestIndex */ - if (w16_bestIndex > len) - { /* not good, do nothing instead */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return NETEQ_OTHER_ERROR; - } - - /* set length parameter */ - *pw16_len = len - w16_bestIndex; /* we remove bestIndex samples */ - - /* copy to output */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, *pw16_len); - - /* set mode */ - inst->w16_mode = MODE_LOWEN_ACCELERATE; - - /* update statistics */ - inst->statInst.accelerateLength += w16_bestIndex; - - return 0; - } /* end of special code for BGN mode */ - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - if (msInfo->msMode != NETEQ_SLAVE) - { - /* Find correlation lag only for non-slave instances */ - -#endif - - /****************************************************************/ - /* Find the strongest correlation lag by downsampling to 4 kHz, */ - /* calculating correlation for downsampled signal and finding */ - /* the strongest correlation peak. */ - /****************************************************************/ - - /* find maximum absolute value */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (WebRtc_Word16) len); - - /* downsample the decoded speech to 4 kHz */ - ok = WebRtcNetEQ_DownSampleTo4kHz(pw16_decoded, len, inst->fs, pw16_downSampSpeech, - ACCELERATE_DOWNSAMPLED_LEN, 1 /* compensate delay*/); - if (ok != 0) - { - /* error */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - *pw16_len = len; - /* simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - return NETEQ_OTHER_ERROR; - } - - /* - * Set scaling factor for cross correlation to protect against overflow - * (log2(50) => 6) - */ - w16_tmp = 6 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* Perform correlation from lag 10 to lag 60 in 4 kHz domain */ - WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_downSampSpeech[w16_endLag], - &pw16_downSampSpeech[w16_endLag - w16_startLag], w16_corrLen, - (WebRtc_Word16) (w16_endLag - w16_startLag), w16_tmp, -1); - - /* Normalize correlation to 14 bits and put in a WebRtc_Word16 vector */ - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_corrLen); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_corrLen, pw32_corr, w16_tmp); - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, (WebRtc_Word16) w16_corrLen, 1, fsMult, - &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - if (msInfo->extraInfo == ACC_FAIL) - { - /* Master has signaled an unsuccessful accelerate */ - w16_bestIndex = 0; - } - else - { - /* Get best index from master */ - w16_bestIndex = msInfo->bestIndex; - } - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } - -#else /* NETEQ_STEREO */ - - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, (WebRtc_Word16) w16_corrLen, 1, fsMult, - &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - -#endif /* NETEQ_STEREO */ - -#ifdef NETEQ_STEREO - - if (msInfo->msMode != NETEQ_SLAVE) - { - /* Calculate correlation only for non-slave instances */ - -#endif /* NETEQ_STEREO */ - - /*****************************************************/ - /* Calculate correlation bestCorr for the found lag. */ - /* Also do a simple VAD decision. */ - /*****************************************************/ - - /* - * Calculate scaling to ensure that bestIndex samples can be square-summed - * without overflowing - */ - w16_tmp = (31 - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax))); - w16_tmp += (31 - WebRtcSpl_NormW32(w16_bestIndex)); - w16_tmp -= 31; - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Calculate energies for vec1 and vec2 */ - w32_en1 = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec1, - (WebRtc_Word16*) pw16_vec1, w16_bestIndex, w16_tmp); - w32_en2 = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec2, - (WebRtc_Word16*) pw16_vec2, w16_bestIndex, w16_tmp); - - /* Calculate cross-correlation at the found lag */ - w32_cc = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec1, (WebRtc_Word16*) pw16_vec2, - w16_bestIndex, w16_tmp); - - /* Check VAD constraint - ((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_en1 + w32_en2, 4); /* (en1+en2)/(2*8) */ - if (inst->BGNInst.w16_initialized == 1) - { - w32_tmp2 = inst->BGNInst.w32_energy; - } - else - { - /* if BGN parameters have not been estimated, use a fixed threshold */ - w32_tmp2 = 75000; - } - w16_tmp2 = 16 - WebRtcSpl_NormW32(w32_tmp2); - w16_tmp2 = WEBRTC_SPL_MAX(0, w16_tmp2); - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_tmp, w16_tmp2); - w16_tmp2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_tmp2, w16_tmp2); - w32_tmp2 = WEBRTC_SPL_MUL_16_16(w16_bestIndex, w16_tmp2); - - /* Scale w32_tmp properly before comparing with w32_tmp2 */ - /* (w16_tmp is scaling before energy calculation, thus 2*w16_tmp) */ - if (WebRtcSpl_NormW32(w32_tmp) < WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)) - { - /* Cannot scale only w32_tmp, must scale w32_temp2 too */ - WebRtc_Word16 tempshift = WebRtcSpl_NormW32(w32_tmp); - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, tempshift); - w32_tmp2 = WEBRTC_SPL_RSHIFT_W32(w32_tmp2, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1) - tempshift); - } - else - { - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)); - } - - if (w32_tmp <= w32_tmp2) /*((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - { - /* The signal seems to be passive speech */ - w16_VAD = 0; - w16_bestCorr = 0; /* Correlation does not matter */ - } - else - { - /* The signal is active speech */ - w16_VAD = 1; - - /* Calculate correlation (cc/sqrt(en1*en2)) */ - - /* Start with calculating scale values */ - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - w16_en1Scale += 1; - } - - /* Convert energies to WebRtc_Word16 */ - w16_en1 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - - /* Calculate energy product */ - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - - /* Calculate square-root of energy product */ - w16_sqrtEn1En2 = (WebRtc_Word16) WebRtcSpl_Sqrt(w32_tmp); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_tmp = 14 - WEBRTC_SPL_RSHIFT_W16(w16_en1Scale+w16_en2Scale, 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_tmp); - w32_cc = WEBRTC_SPL_MAX(0, w32_cc); /* Don't divide with negative number */ - w16_bestCorr = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_cc, w16_sqrtEn1En2); - w16_bestCorr = WEBRTC_SPL_MIN(16384, w16_bestCorr); /* set maximum to 1.0 */ - } - -#ifdef NETEQ_STEREO - - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - -#endif /* NETEQ_STEREO */ - - /************************************************/ - /* Check accelerate criteria and remove samples */ - /************************************************/ - - /* Check for strong correlation (>0.9) or passive speech */ -#ifdef NETEQ_STEREO - if ((((w16_bestCorr > 14746) || (w16_VAD == 0)) && (msInfo->msMode != NETEQ_SLAVE)) - || ((msInfo->msMode == NETEQ_SLAVE) && (msInfo->extraInfo != ACC_FAIL))) -#else - if ((w16_bestCorr > 14746) || (w16_VAD == 0)) -#endif - { - /* Do accelerate operation by overlap add */ - - /* - * Calculate cross-fading slope so that the fading factor goes from - * 1 (16384 in Q14) to 0 in one pitch period (bestIndex). - */ - w16_inc = (WebRtc_Word16) WebRtcSpl_DivW32W16((WebRtc_Word32) 16384, - (WebRtc_Word16) (w16_bestIndex + 1)); /* in Q14 */ - - /* Initiate fading factor */ - w16_startfact = 16384 - w16_inc; - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Copy unmodified part [0 to 15 ms minus 1 pitch period] */ - w16_tmp = (fsMult120 - w16_bestIndex); - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, w16_tmp); - - /* Generate interpolated part of length bestIndex (1 pitch period) */ - pw16_vectmp = pw16_outData + w16_tmp; /* start of interpolation output */ - /* Reuse mixing function from Expand */ - WebRtcNetEQ_MixVoiceUnvoice(pw16_vectmp, (WebRtc_Word16*) pw16_vec1, - (WebRtc_Word16*) pw16_vec2, &w16_startfact, w16_inc, w16_bestIndex); - - /* Move the last part (also unmodified) */ - /* Take from decoded at 15 ms + 1 pitch period */ - pw16_vec2 = &pw16_decoded[fsMult120 + w16_bestIndex]; - WEBRTC_SPL_MEMMOVE_W16(&pw16_outData[fsMult120], pw16_vec2, - (WebRtc_Word16) (len - fsMult120 - w16_bestIndex)); - - /* Set the mode flag */ - if (w16_VAD) - { - inst->w16_mode = MODE_SUCCESS_ACCELERATE; - } - else - { - inst->w16_mode = MODE_LOWEN_ACCELERATE; - } - - /* Calculate resulting length = original length - pitch period */ - *pw16_len = len - w16_bestIndex; - - /* Update in-call statistics */ - inst->statInst.accelerateLength += w16_bestIndex; - - return 0; - } - else - { - /* Accelerate not allowed */ - -#ifdef NETEQ_STEREO - /* Signal to slave(s) that this was unsuccessful */ - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->extraInfo = ACC_FAIL; - } -#endif - - /* Set mode flag to unsuccessful accelerate */ - inst->w16_mode = MODE_UNSUCCESS_ACCELERATE; - - /* Length is unmodified */ - *pw16_len = len; - - /* Simply move all data from decoded to outData */ - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return 0; - } -} - -#undef SCRATCH_PW16_DS_SPEECH -#undef SCRATCH_PW32_CORR -#undef SCRATCH_PW16_CORR diff --git a/modules/audio_coding/NetEQ/main/source/automode.c b/modules/audio_coding/NetEQ/main/source/automode.c deleted file mode 100644 index 7d9f03549..000000000 --- a/modules/audio_coding/NetEQ/main/source/automode.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the implementation of automatic buffer level optimization. - */ - -#include "automode.h" - -#include "signal_processing_library.h" - -#include "neteq_defines.h" - -#ifdef NETEQ_DELAY_LOGGING -/* special code for offline delay logging */ -#include -#include "delay_logging.h" - -extern FILE *delay_fid2; /* file pointer to delay log file */ -#endif /* NETEQ_DELAY_LOGGING */ - - -int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, - WebRtc_UWord16 seqNumber, WebRtc_UWord32 timeStamp, - WebRtc_Word32 fsHz, int mdCodec, int streamingMode) -{ - WebRtc_UWord32 timeIat; /* inter-arrival time */ - int i; - WebRtc_Word32 tempsum = 0; /* temp summation */ - WebRtc_Word32 tempvar; /* temporary variable */ - int retval = 0; /* return value */ - WebRtc_Word16 packetLenSamp; /* packet speech length in samples */ - - /****************/ - /* Sanity check */ - /****************/ - - if (maxBufLen <= 1 || fsHz <= 0) - { - /* maxBufLen must be at least 2 and fsHz must both be strictly positive */ - return -1; - } - - /****************************/ - /* Update packet statistics */ - /****************************/ - - /* Try calculating packet length from current and previous timestamps */ - if ((timeStamp <= inst->lastTimeStamp) || (seqNumber <= inst->lastSeqNo)) - { - /* Wrong timestamp or sequence order; revert to backup plan */ - packetLenSamp = inst->packetSpeechLenSamp; /* use stored value */ - } - else if (timeStamp > inst->lastTimeStamp) - { - /* calculate timestamps per packet */ - packetLenSamp = (WebRtc_Word16) WebRtcSpl_DivU32U16(timeStamp - inst->lastTimeStamp, - seqNumber - inst->lastSeqNo); - } - - /* Check that the packet size is positive; if not, the statistics cannot be updated. */ - if (packetLenSamp > 0) - { /* packet size ok */ - - /* calculate inter-arrival time in integer packets (rounding down) */ - timeIat = WebRtcSpl_DivW32W16(inst->packetIatCountSamp, packetLenSamp); - - /* Special operations for streaming mode */ - if (streamingMode != 0) - { - /* - * Calculate IAT in Q8, including fractions of a packet (i.e., more accurate - * than timeIat). - */ - WebRtc_Word16 timeIatQ8 = (WebRtc_Word16) WebRtcSpl_DivW32W16( - WEBRTC_SPL_LSHIFT_W32(inst->packetIatCountSamp, 8), packetLenSamp); - - /* - * Calculate cumulative sum iat with sequence number compensation (ideal arrival - * times makes this sum zero). - */ - inst->cSumIatQ8 += (timeIatQ8 - - WEBRTC_SPL_LSHIFT_W32(seqNumber - inst->lastSeqNo, 8)); - - /* subtract drift term */ - inst->cSumIatQ8 -= CSUM_IAT_DRIFT; - - /* ensure not negative */ - inst->cSumIatQ8 = WEBRTC_SPL_MAX(inst->cSumIatQ8, 0); - - /* remember max */ - if (inst->cSumIatQ8 > inst->maxCSumIatQ8) - { - inst->maxCSumIatQ8 = inst->cSumIatQ8; - inst->maxCSumUpdateTimer = 0; - } - - /* too long since the last maximum was observed; decrease max value */ - if (inst->maxCSumUpdateTimer > (WebRtc_UWord32) WEBRTC_SPL_MUL_32_16(fsHz, - MAX_STREAMING_PEAK_PERIOD)) - { - inst->maxCSumIatQ8 -= 4; /* remove 1000*4/256 = 15.6 ms/s */ - } - } /* end of streaming mode */ - - /* check for discontinuous packet sequence and re-ordering */ - if (seqNumber > inst->lastSeqNo + 1) - { - /* Compensate for gap in the sequence numbers. - * Reduce IAT with expected extra time due to lost packets, but ensure that - * the IAT is not negative. - */ - timeIat -= WEBRTC_SPL_MIN(timeIat, - (WebRtc_UWord32) (seqNumber - inst->lastSeqNo - 1)); - } - else if (seqNumber < inst->lastSeqNo) - { - /* compensate for re-ordering */ - timeIat += (WebRtc_UWord32) (inst->lastSeqNo + 1 - seqNumber); - } - - /* saturate IAT at maximum value */ - timeIat = WEBRTC_SPL_MIN( timeIat, MAX_IAT ); - - /* update iatProb = forgetting_factor * iatProb for all elements */ - for (i = 0; i <= MAX_IAT; i++) - { - WebRtc_Word32 tempHi, tempLo; /* Temporary variables */ - - /* - * Multiply iatProbFact (Q15) with iatProb (Q30) and right-shift 15 steps - * to come back to Q30. The operation is done in two steps: - */ - - /* - * 1) Multiply the high 16 bits (15 bits + sign) of iatProb. Shift iatProb - * 16 steps right to get the high 16 bits in a WebRtc_Word16 prior to - * multiplication, and left-shift with 1 afterwards to come back to - * Q30 = (Q15 * (Q30>>16)) << 1. - */ - tempHi = WEBRTC_SPL_MUL_16_16(inst->iatProbFact, - (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(inst->iatProb[i], 16)); - tempHi = WEBRTC_SPL_LSHIFT_W32(tempHi, 1); /* left-shift 1 step */ - - /* - * 2) Isolate and multiply the low 16 bits of iatProb. Right-shift 15 steps - * afterwards to come back to Q30 = (Q15 * Q30) >> 15. - */ - tempLo = inst->iatProb[i] & 0x0000FFFF; /* sift out the 16 low bits */ - tempLo = WEBRTC_SPL_MUL_16_U16(inst->iatProbFact, - (WebRtc_UWord16) tempLo); - tempLo = WEBRTC_SPL_RSHIFT_W32(tempLo, 15); - - /* Finally, add the high and low parts */ - inst->iatProb[i] = tempHi + tempLo; - - /* Sum all vector elements while we are at it... */ - tempsum += inst->iatProb[i]; - } - - /* - * Increase the probability for the currently observed inter-arrival time - * with 1 - iatProbFact. The factor is in Q15, iatProb in Q30; - * hence, left-shift 15 steps to obtain result in Q30. - */ - inst->iatProb[timeIat] += (32768 - inst->iatProbFact) << 15; - - tempsum += (32768 - inst->iatProbFact) << 15; /* add to vector sum */ - - /* - * Update iatProbFact (changes only during the first seconds after reset) - * The factor converges to IAT_PROB_FACT. - */ - inst->iatProbFact += (IAT_PROB_FACT - inst->iatProbFact + 3) >> 2; - - /* iatProb should sum up to 1 (in Q30). */ - tempsum -= 1 << 30; /* should be zero */ - - /* Check if it does, correct if it doesn't. */ - if (tempsum > 0) - { - /* tempsum too large => decrease a few values in the beginning */ - i = 0; - while (i <= MAX_IAT && tempsum > 0) - { - /* Remove iatProb[i] / 16 from iatProb, but not more than tempsum */ - tempvar = WEBRTC_SPL_MIN(tempsum, inst->iatProb[i] >> 4); - inst->iatProb[i++] -= tempvar; - tempsum -= tempvar; - } - } - else if (tempsum < 0) - { - /* tempsum too small => increase a few values in the beginning */ - i = 0; - while (i <= MAX_IAT && tempsum < 0) - { - /* Add iatProb[i] / 16 to iatProb, but not more than tempsum */ - tempvar = WEBRTC_SPL_MIN(-tempsum, inst->iatProb[i] >> 4); - inst->iatProb[i++] += tempvar; - tempsum += tempvar; - } - } - - /* Calculate optimal buffer level based on updated statistics */ - tempvar = (WebRtc_Word32) WebRtcNetEQ_CalcOptimalBufLvl(inst, fsHz, mdCodec, timeIat, - streamingMode); - if (tempvar > 0) - { - inst->optBufLevel = (WebRtc_UWord16) tempvar; - - if (streamingMode != 0) - { - inst->optBufLevel = WEBRTC_SPL_MAX(inst->optBufLevel, - inst->maxCSumIatQ8); - } - - /*********/ - /* Limit */ - /*********/ - - /* Subtract extra delay from maxBufLen */ - if (inst->extraDelayMs > 0 && inst->packetSpeechLenSamp > 0) - { - maxBufLen -= inst->extraDelayMs / inst->packetSpeechLenSamp * fsHz / 1000; - maxBufLen = WEBRTC_SPL_MAX(maxBufLen, 1); // sanity: at least one packet - } - - maxBufLen = WEBRTC_SPL_LSHIFT_W32(maxBufLen, 8); /* shift to Q8 */ - - /* Enforce upper limit; 75% of maxBufLen */ - inst->optBufLevel = (WebRtc_UWord16) WEBRTC_SPL_MIN( inst->optBufLevel, - (maxBufLen >> 1) + (maxBufLen >> 2) ); /* 1/2 + 1/4 = 75% */ - } - else - { - retval = (int) tempvar; - } - - } /* end if */ - - /*******************************/ - /* Update post-call statistics */ - /*******************************/ - - /* Calculate inter-arrival time in ms = packetIatCountSamp / (fsHz / 1000) */ - timeIat = WEBRTC_SPL_UDIV( - WEBRTC_SPL_UMUL_32_16(inst->packetIatCountSamp, (WebRtc_Word16) 1000), - (WebRtc_UWord32) fsHz); - - /* Increase counter corresponding to current inter-arrival time */ - if (timeIat > 2000) - { - inst->countIAT2000ms++; - } - else if (timeIat > 1000) - { - inst->countIAT1000ms++; - } - else if (timeIat > 500) - { - inst->countIAT500ms++; - } - - if (timeIat > inst->longestIATms) - { - /* update maximum value */ - inst->longestIATms = timeIat; - } - - /***********************************/ - /* Prepare for next packet arrival */ - /***********************************/ - - inst->packetIatCountSamp = 0; /* reset inter-arrival time counter */ - - inst->lastSeqNo = seqNumber; /* remember current sequence number */ - - inst->lastTimeStamp = timeStamp; /* remember current timestamp */ - - return retval; -} - - -WebRtc_Word16 WebRtcNetEQ_CalcOptimalBufLvl(AutomodeInst_t *inst, WebRtc_Word32 fsHz, - int mdCodec, WebRtc_UWord32 timeIatPkts, - int streamingMode) -{ - - WebRtc_Word32 sum1 = 1 << 30; /* assign to 1 in Q30 */ - WebRtc_Word16 B; - WebRtc_UWord16 Bopt; - int i; - WebRtc_Word32 betaInv; /* optimization parameter */ - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - int temp_var; -#endif - - /****************/ - /* Sanity check */ - /****************/ - - if (fsHz <= 0) - { - /* fsHz must be strictly positive */ - return -1; - } - - /***********************************************/ - /* Get betaInv parameter based on playout mode */ - /***********************************************/ - - if (streamingMode) - { - /* streaming (listen-only) mode */ - betaInv = AUTOMODE_STREAMING_BETA_INV_Q30; - } - else - { - /* normal mode */ - betaInv = AUTOMODE_BETA_INV_Q30; - } - - /*******************************************************************/ - /* Calculate optimal buffer level without considering jitter peaks */ - /*******************************************************************/ - - /* - * Find the B for which the probability of observing an inter-arrival time larger - * than or equal to B is less than or equal to betaInv. - */ - B = 0; /* start from the beginning of iatProb */ - sum1 -= inst->iatProb[B]; /* ensure that optimal level is not less than 1 */ - - do - { - /* - * Subtract the probabilities one by one until the sum is no longer greater - * than betaInv. - */ - sum1 -= inst->iatProb[++B]; - } - while ((sum1 > betaInv) && (B < MAX_IAT)); - - Bopt = B; /* This is our primary value for the optimal buffer level Bopt */ - - if (mdCodec) - { - /* - * Use alternative cost function when multiple description codec is in use. - * Do not have to re-calculate all points, just back off a few steps from - * previous value of B. - */ - WebRtc_Word32 sum2 = sum1; /* copy sum1 */ - - while ((sum2 <= betaInv + inst->iatProb[Bopt]) && (Bopt > 0)) - { - /* Go backwards in the sum until the modified cost function solution is found */ - sum2 += inst->iatProb[Bopt--]; - } - - Bopt++; /* This is the optimal level when using an MD codec */ - - /* Now, Bopt and B can have different values. */ - } - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF; - fwrite( &temp_var, sizeof(int), 1, delay_fid2 ); - temp_var = (int) (Bopt * inst->packetSpeechLenSamp); -#endif - - /******************************************************************/ - /* Make levelFiltFact adaptive: Larger B <=> larger levelFiltFact */ - /******************************************************************/ - - switch (B) - { - case 0: - case 1: - { - inst->levelFiltFact = 251; - break; - } - case 2: - case 3: - { - inst->levelFiltFact = 252; - break; - } - case 4: - case 5: - case 6: - case 7: - { - inst->levelFiltFact = 253; - break; - } - default: /* B > 7 */ - { - inst->levelFiltFact = 254; - break; - } - } - - /************************/ - /* Peak mode operations */ - /************************/ - - /* Compare current IAT with peak threshold - * - * If IAT > optimal level + threshold (+1 for MD codecs) - * or if IAT > 2 * optimal level (note: optimal level is in Q8): - */ - if (timeIatPkts > (WebRtc_UWord32) (Bopt + inst->peakThresholdPkt + (mdCodec != 0)) - || timeIatPkts > (WebRtc_UWord32) WEBRTC_SPL_LSHIFT_U16(Bopt, 1)) - { - /* A peak is observed */ - - if (inst->peakIndex == -1) - { - /* this is the first peak; prepare for next peak */ - inst->peakIndex = 0; - /* set the mode-disable counter */ - inst->peakModeDisabled = WEBRTC_SPL_LSHIFT_W16(1, NUM_PEAKS_REQUIRED-2); - } - else if (inst->peakIatCountSamp - <= - (WebRtc_UWord32) WEBRTC_SPL_MUL_32_16(fsHz, MAX_PEAK_PERIOD)) - { - /* This is not the first peak and the period time is valid */ - - /* store time elapsed since last peak */ - inst->peakPeriodSamp[inst->peakIndex] = inst->peakIatCountSamp; - - /* saturate height to 16 bits */ - inst->peakHeightPkt[inst->peakIndex] - = - (WebRtc_Word16) WEBRTC_SPL_MIN(timeIatPkts, WEBRTC_SPL_WORD16_MAX); - - /* increment peakIndex and wrap/modulo */ - inst->peakIndex = ++inst->peakIndex & PEAK_INDEX_MASK; - - /* process peak vectors */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - - for (i = 0; i < NUM_PEAKS; i++) - { - /* Find maximum of peak heights and peak periods */ - inst->curPeakHeight - = WEBRTC_SPL_MAX(inst->curPeakHeight, inst->peakHeightPkt[i]); - inst->curPeakPeriod - = WEBRTC_SPL_MAX(inst->curPeakPeriod, inst->peakPeriodSamp[i]); - - } - - inst->peakModeDisabled >>= 1; /* decrease mode-disable "counter" */ - - } - else if (inst->peakIatCountSamp > (WebRtc_UWord32) WEBRTC_SPL_MUL_32_16(fsHz, - WEBRTC_SPL_LSHIFT_W16(MAX_PEAK_PERIOD, 1))) - { - /* - * More than 2 * MAX_PEAK_PERIOD has elapsed since last peak; - * too long time => reset peak statistics - */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - for (i = 0; i < NUM_PEAKS; i++) - { - inst->peakHeightPkt[i] = 0; - inst->peakPeriodSamp[i] = 0; - } - - inst->peakIndex = -1; /* Next peak is first peak */ - inst->peakIatCountSamp = 0; - } - - inst->peakIatCountSamp = 0; /* Reset peak interval timer */ - } /* end if peak is observed */ - - /* Evaluate peak mode conditions */ - - /* - * If not disabled (enough peaks have been observed) and - * time since last peak is less than two peak periods. - */ - if ((!inst->peakModeDisabled) && (inst->peakIatCountSamp - <= WEBRTC_SPL_LSHIFT_W32(inst->curPeakPeriod , 1))) - { - /* Engage peak mode */ - - /* Set optimal buffer level to curPeakHeight (if it's not already larger) */ - Bopt = WEBRTC_SPL_MAX(Bopt, inst->curPeakHeight); - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - temp_var = (int) -(Bopt * inst->packetSpeechLenSamp); -#endif - } - - /* Scale Bopt to Q8 */ - Bopt = WEBRTC_SPL_LSHIFT_U16(Bopt,8); - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - fwrite( &temp_var, sizeof(int), 1, delay_fid2 ); -#endif - - /* Sanity check: Bopt must be strictly positive */ - if (Bopt <= 0) - { - Bopt = WEBRTC_SPL_LSHIFT_W16(1, 8); /* 1 in Q8 */ - } - - return Bopt; /* return value in Q8 */ -} - - -int WebRtcNetEQ_BufferLevelFilter(WebRtc_Word32 curSizeMs8, AutomodeInst_t *inst, - int sampPerCall, WebRtc_Word16 fsMult) -{ - - WebRtc_Word16 curSizeFrames; - - /****************/ - /* Sanity check */ - /****************/ - - if (sampPerCall <= 0 || fsMult <= 0) - { - /* sampPerCall and fsMult must both be strictly positive */ - return -1; - } - - /* Check if packet size has been detected */ - if (inst->packetSpeechLenSamp > 0) - { - /* - * Current buffer level in packet lengths - * = (curSizeMs8 * fsMult) / packetSpeechLenSamp - */ - curSizeFrames = (WebRtc_Word16) WebRtcSpl_DivW32W16( - WEBRTC_SPL_MUL_32_16(curSizeMs8, fsMult), inst->packetSpeechLenSamp); - } - else - { - curSizeFrames = 0; - } - - /* Filter buffer level */ - if (inst->levelFiltFact > 0) /* check that filter factor is set */ - { - /* Filter: - * buffLevelFilt = levelFiltFact * buffLevelFilt - * + (1-levelFiltFact) * curSizeFrames - * - * levelFiltFact is in Q8 - */ - inst->buffLevelFilt = (WebRtc_UWord16) (WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_U16(inst->levelFiltFact, inst->buffLevelFilt), 8) - + WEBRTC_SPL_MUL_16_16(256 - inst->levelFiltFact, curSizeFrames)); - } - - /* Account for time-scale operations (accelerate and pre-emptive expand) */ - if (inst->prevTimeScale) - { - /* - * Time-scaling has been performed since last filter update. - * Subtract the sampleMemory from buffLevelFilt after converting sampleMemory - * from samples to packets in Q8. Make sure that the filtered value is - * non-negative. - */ - inst->buffLevelFilt = (WebRtc_UWord16) WEBRTC_SPL_MAX( inst->buffLevelFilt - - WebRtcSpl_DivW32W16( - WEBRTC_SPL_LSHIFT_W32(inst->sampleMemory, 8), /* sampleMemory in Q8 */ - inst->packetSpeechLenSamp ), /* divide by packetSpeechLenSamp */ - 0); - - /* - * Reset flag and set timescaleHoldOff timer to prevent further time-scaling - * for some time. - */ - inst->prevTimeScale = 0; - inst->timescaleHoldOff = AUTOMODE_TIMESCALE_LIMIT; - } - - /* Update time counters and HoldOff timer */ - inst->packetIatCountSamp += sampPerCall; /* packet inter-arrival time */ - inst->peakIatCountSamp += sampPerCall; /* peak inter-arrival time */ - inst->timescaleHoldOff >>= 1; /* time-scaling limiter */ - inst->maxCSumUpdateTimer += sampPerCall; /* cumulative-sum timer */ - - return 0; - -} - - -int WebRtcNetEQ_SetPacketSpeechLen(AutomodeInst_t *inst, WebRtc_Word16 newLenSamp, - WebRtc_Word32 fsHz) -{ - - /* Sanity check for newLenSamp and fsHz */ - if (newLenSamp <= 0 || fsHz <= 0) - { - return -1; - } - - inst->packetSpeechLenSamp = newLenSamp; /* Store packet size in instance */ - - /* Make NetEQ wait for first regular packet before starting the timer */ - inst->lastPackCNGorDTMF = 1; - - inst->packetIatCountSamp = 0; /* Reset packet time counter */ - - /* - * Calculate peak threshold from packet size. The threshold is defined as - * the (fractional) number of packets that corresponds to PEAK_HEIGHT - * (in Q8 seconds). That is, threshold = PEAK_HEIGHT/256 * fsHz / packLen. - */ - inst->peakThresholdPkt = (WebRtc_UWord16) WebRtcSpl_DivW32W16ResW16( - WEBRTC_SPL_MUL_16_16_RSFT(PEAK_HEIGHT, - (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(fsHz, 6), 2), inst->packetSpeechLenSamp); - - return 0; -} - - -int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets) -{ - - int i; - WebRtc_UWord16 tempprob = 0x4002; /* 16384 + 2 = 100000000000010 binary; */ - - /* Sanity check for maxBufLenPackets */ - if (maxBufLenPackets <= 1) - { - /* Invalid value; set to 10 instead (arbitary small number) */ - maxBufLenPackets = 10; - } - - /* Reset filtered buffer level */ - inst->buffLevelFilt = 0; - - /* Reset packet size to unknown */ - inst->packetSpeechLenSamp = 0; - - /* - * Flag that last packet was special payload, so that automode will treat the next speech - * payload as the first payload received. - */ - inst->lastPackCNGorDTMF = 1; - - /* Reset peak detection parameters */ - inst->peakModeDisabled = 1; /* disable peak mode */ - inst->peakIatCountSamp = 0; - inst->peakIndex = -1; /* indicates that no peak is registered */ - inst->curPeakHeight = 0; - inst->curPeakPeriod = 0; - for (i = 0; i < NUM_PEAKS; i++) - { - inst->peakHeightPkt[i] = 0; - inst->peakPeriodSamp[i] = 0; - } - - /* - * Set the iatProb PDF vector to an exponentially decaying distribution - * iatProb[i] = 0.5^(i+1), i = 0, 1, 2, ... - * iatProb is in Q30. - */ - for (i = 0; i <= MAX_IAT; i++) - { - /* iatProb[i] = 0.5^(i+1) = iatProb[i-1] / 2 */ - tempprob = WEBRTC_SPL_RSHIFT_U16(tempprob, 1); - /* store in PDF vector */ - inst->iatProb[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) tempprob, 16); - } - - /* - * Calculate the optimal buffer level corresponing to the initial PDF. - * No need to call WebRtcNetEQ_CalcOptimalBufLvl() since we have just hard-coded - * all the variables that the buffer level depends on => we know the result - */ - inst->optBufLevel = WEBRTC_SPL_MIN(4, - (maxBufLenPackets >> 1) + (maxBufLenPackets >> 1)); /* 75% of maxBufLenPackets */ - inst->levelFiltFact = 253; - - /* - * Reset the iat update forgetting factor to 0 to make the impact of the first - * incoming packets greater. - */ - inst->iatProbFact = 0; - - /* Reset packet inter-arrival time counter */ - inst->packetIatCountSamp = 0; - - /* Clear time-scaling related variables */ - inst->prevTimeScale = 0; - inst->timescaleHoldOff = AUTOMODE_TIMESCALE_LIMIT; /* don't allow time-scaling immediately */ - - inst->cSumIatQ8 = 0; - inst->maxCSumIatQ8 = 0; - - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/automode.h b/modules/audio_coding/NetEQ/main/source/automode.h deleted file mode 100644 index 672098a30..000000000 --- a/modules/audio_coding/NetEQ/main/source/automode.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the functionality for automatic buffer level optimization. - */ - -#ifndef AUTOMODE_H -#define AUTOMODE_H - -#include "typedefs.h" - -/*************/ -/* Constants */ -/*************/ - -/* The beta parameter defines the trade-off between delay and underrun probability. */ -/* It is defined through its inverse in Q30 */ -#define AUTOMODE_BETA_INV_Q30 53687091 /* 1/20 in Q30 */ -#define AUTOMODE_STREAMING_BETA_INV_Q30 536871 /* 1/2000 in Q30 */ - -/* Forgetting factor for the inter-arrival time statistics */ -#define IAT_PROB_FACT 32745 /* 0.9993 in Q15 */ - -/* Maximum inter-arrival time to register (in "packet-times") */ -#define MAX_IAT 64 -#define PEAK_HEIGHT 20 /* 0.08s in Q8 */ - -/* The value (1<<5) sets maximum accelerate "speed" to about 100 ms/s */ -#define AUTOMODE_TIMESCALE_LIMIT (1<<5) - -/* Peak mode related parameters */ -/* Number of peaks in peak vector; must be a power of 2 */ -#define NUM_PEAKS 8 - -/* Must be NUM_PEAKS-1 */ -#define PEAK_INDEX_MASK 0x0007 - -/* Longest accepted peak distance */ -#define MAX_PEAK_PERIOD 10 -#define MAX_STREAMING_PEAK_PERIOD 600 /* 10 minutes */ - -/* Number of peaks required before peak mode can be engaged */ -#define NUM_PEAKS_REQUIRED 3 - -/* Drift term for cumulative sum */ -#define CSUM_IAT_DRIFT 2 - -/*******************/ -/* Automode struct */ -/*******************/ - -/* The automode struct is a sub-struct of the - bufstats-struct (BufstatsInst_t). */ - -typedef struct -{ - - /* Filtered current buffer level */ - WebRtc_UWord16 levelFiltFact; /* filter forgetting factor in Q8 */ - WebRtc_UWord16 buffLevelFilt; /* filtered buffer level in Q8 */ - - /* Inter-arrival time (iat) statistics */ - WebRtc_Word32 iatProb[MAX_IAT + 1]; /* iat probabilities in Q30 */ - WebRtc_Word16 iatProbFact; /* iat forgetting factor in Q15 */ - WebRtc_UWord32 packetIatCountSamp; /* time (in timestamps) elapsed since last - packet arrival, based on RecOut calls */ - WebRtc_UWord16 optBufLevel; /* current optimal buffer level in Q8 */ - - /* Packet related information */ - WebRtc_Word16 packetSpeechLenSamp; /* speech samples per incoming packet */ - WebRtc_Word16 lastPackCNGorDTMF; /* indicates that the last received packet - contained special information */ - WebRtc_UWord16 lastSeqNo; /* sequence number for last packet received */ - WebRtc_UWord32 lastTimeStamp; /* timestamp for the last packet received */ - WebRtc_Word32 sampleMemory; /* memory position for keeping track of how many - samples we cut during expand */ - WebRtc_Word16 prevTimeScale; /* indicates that the last mode was an accelerate - or pre-emptive expand operation */ - WebRtc_UWord32 timescaleHoldOff; /* counter that is shifted one step right each - RecOut call; time-scaling allowed when it has - reached 0 */ - WebRtc_Word16 extraDelayMs; /* extra delay for sync with video */ - - /* Peak-detection */ - /* vector with the latest peak periods (peak spacing in samples) */ - WebRtc_UWord32 peakPeriodSamp[NUM_PEAKS]; - /* vector with the latest peak heights (in packets) */ - WebRtc_Word16 peakHeightPkt[NUM_PEAKS]; - WebRtc_Word16 peakIndex; /* index for the vectors peakPeriodSamp and peakHeightPkt; - -1 if still waiting for first peak */ - WebRtc_UWord16 peakThresholdPkt; /* definition of peak (in packets); - calculated from PEAK_HEIGHT */ - WebRtc_UWord32 peakIatCountSamp; /* samples elapsed since last peak was observed */ - WebRtc_UWord32 curPeakPeriod; /* current maximum of peakPeriodSamp vector */ - WebRtc_Word16 curPeakHeight; /* derived from peakHeightPkt vector; - used as optimal buffer level in peak mode */ - WebRtc_Word16 peakModeDisabled; /* ==0 if peak mode can be engaged; >0 if not */ - - /* Post-call statistics */ - WebRtc_UWord32 countIAT500ms; /* number of times we got small network outage */ - WebRtc_UWord32 countIAT1000ms; /* number of times we got medium network outage */ - WebRtc_UWord32 countIAT2000ms; /* number of times we got large network outage */ - WebRtc_UWord32 longestIATms; /* mSec duration of longest network outage */ - - WebRtc_Word16 cSumIatQ8; /* cumulative sum of inter-arrival times */ - WebRtc_Word16 maxCSumIatQ8; /* max cumulative sum IAT */ - WebRtc_UWord32 maxCSumUpdateTimer;/* time elapsed since maximum was observed */ - -} AutomodeInst_t; - -/*************/ -/* Functions */ -/*************/ - -/**************************************************************************** - * WebRtcNetEQ_UpdateIatStatistics(...) - * - * Update the packet inter-arrival time statistics when a new packet arrives. - * This function should be called for every arriving packet, with some - * exceptions when using DTX/VAD and DTMF. A new optimal buffer level is - * calculated after the update. - * - * Input: - * - inst : Automode instance - * - maxBufLen : Maximum number of packets the buffer can hold - * - seqNumber : RTP sequence number of incoming packet - * - timeStamp : RTP timestamp of incoming packet - * - fsHz : Sample rate in Hz - * - mdCodec : Non-zero if the current codec is a multiple- - * description codec - * - streamingMode : A non-zero value will increase jitter robustness (and delay) - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen, - WebRtc_UWord16 seqNumber, WebRtc_UWord32 timeStamp, - WebRtc_Word32 fsHz, int mdCodec, int streamingMode); - -/**************************************************************************** - * WebRtcNetEQ_CalcOptimalBufLvl(...) - * - * Calculate the optimal buffer level based on packet inter-arrival time - * statistics. - * - * Input: - * - inst : Automode instance - * - fsHz : Sample rate in Hz - * - mdCodec : Non-zero if the current codec is a multiple- - * description codec - * - timeIatPkts : Currently observed inter-arrival time in packets - * - streamingMode : A non-zero value will increase jitter robustness (and delay) - * - * Output: - * - inst : Updated automode instance - * - * Return value : >0 - Optimal buffer level - * <0 - Error - */ - -WebRtc_Word16 WebRtcNetEQ_CalcOptimalBufLvl(AutomodeInst_t *inst, WebRtc_Word32 fsHz, - int mdCodec, WebRtc_UWord32 timeIatPkts, - int streamingMode); - -/**************************************************************************** - * WebRtcNetEQ_BufferLevelFilter(...) - * - * Update filtered buffer level. The function must be called once for each - * RecOut call, since the timing of automode hinges on counters that are - * updated by this function. - * - * Input: - * - curSizeMs8 : Total length of unused speech data in packet buffer - * and sync buffer, in ms * 8 - * - inst : Automode instance - * - sampPerCall : Number of samples per RecOut call - * - fsMult : Sample rate in Hz divided by 8000 - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * : <0 - Error - */ - -int WebRtcNetEQ_BufferLevelFilter(WebRtc_Word32 curSizeMs8, AutomodeInst_t *inst, - int sampPerCall, WebRtc_Word16 fsMult); - -/**************************************************************************** - * WebRtcNetEQ_SetPacketSpeechLen(...) - * - * Provide the number of speech samples extracted from a packet to the - * automode instance. Several of the calculations within automode depend - * on knowing the packet size. - * - * - * Input: - * - inst : Automode instance - * - newLenSamp : Number of samples per RecOut call - * - fsHz : Sample rate in Hz - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_SetPacketSpeechLen(AutomodeInst_t *inst, WebRtc_Word16 newLenSamp, - WebRtc_Word32 fsHz); - -/**************************************************************************** - * WebRtcNetEQ_ResetAutomode(...) - * - * Reset the automode instance. - * - * - * Input: - * - inst : Automode instance - * - maxBufLenPackets : Maximum number of packets that the packet - * buffer can hold (>1) - * - * Output: - * - inst : Updated automode instance - * - * Return value : 0 - Ok - */ - -int WebRtcNetEQ_ResetAutomode(AutomodeInst_t *inst, int maxBufLenPackets); - -#endif /* AUTOMODE_H */ diff --git a/modules/audio_coding/NetEQ/main/source/bgn_update.c b/modules/audio_coding/NetEQ/main/source/bgn_update.c deleted file mode 100644 index 115168a0b..000000000 --- a/modules/audio_coding/NetEQ/main/source/bgn_update.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for updating the background noise estimate. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - Designed for BGN_LPC_ORDER <= 10 - - Type Name size startpos endpos - WebRtc_Word32 pw32_autoCorr 22 0 21 (Length (BGN_LPC_ORDER + 1)*2) - WebRtc_Word16 pw16_tempVec 10 22 31 (Length BGN_LPC_ORDER) - WebRtc_Word16 pw16_rc 10 32 41 (Length BGN_LPC_ORDER) - WebRtc_Word16 pw16_outVec 74 0 73 (Length BGN_LPC_ORDER + 64) - - Total: 74 - */ - -#if (BGN_LPC_ORDER > 10) && (defined SCRATCH) -#error BGN_LPC_ORDER is too large for current scratch memory allocation -#endif - -#define SCRATCH_PW32_AUTO_CORR 0 -#define SCRATCH_PW16_TEMP_VEC 22 -#define SCRATCH_PW16_RC 32 -#define SCRATCH_PW16_OUT_VEC 0 - -#define NETEQFIX_BGNFRAQINCQ16 229 /* 0.0035 in Q16 */ - -/**************************************************************************** - * WebRtcNetEQ_BGNUpdate(...) - * - * This function updates the background noise parameter estimates. - * - * Input: - * - inst : NetEQ instance, where the speech history is stored. - * - scratchPtr : Pointer to scratch vector. - * - * Output: - * - inst : Updated information about the BGN characteristics. - * - * Return value : No return value - */ - -void WebRtcNetEQ_BGNUpdate( -#ifdef SCRATCH - DSPInst_t *inst, WebRtc_Word16 *pw16_scratchPtr -#else - DSPInst_t *inst -#endif -) -{ - const WebRtc_Word16 w16_vecLen = 256; - BGNInst_t *BGN_Inst = &(inst->BGNInst); -#ifdef SCRATCH - WebRtc_Word32 *pw32_autoCorr = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_PW32_AUTO_CORR); - WebRtc_Word16 *pw16_tempVec = pw16_scratchPtr + SCRATCH_PW16_TEMP_VEC; - WebRtc_Word16 *pw16_rc = pw16_scratchPtr + SCRATCH_PW16_RC; - WebRtc_Word16 *pw16_outVec = pw16_scratchPtr + SCRATCH_PW16_OUT_VEC; -#else - WebRtc_Word32 pw32_autoCorr[BGN_LPC_ORDER + 1]; - WebRtc_Word16 pw16_tempVec[BGN_LPC_ORDER]; - WebRtc_Word16 pw16_outVec[BGN_LPC_ORDER + 64]; - WebRtc_Word16 pw16_rc[BGN_LPC_ORDER]; -#endif - WebRtc_Word16 pw16_A[BGN_LPC_ORDER + 1]; - WebRtc_Word32 w32_tmp; - WebRtc_Word16 *pw16_vec; - WebRtc_Word16 w16_maxSample; - WebRtc_Word16 w16_tmp, w16_tmp2; - WebRtc_Word16 w16_enSampleShift; - WebRtc_Word32 w32_en, w32_enBGN; - WebRtc_Word32 w32_enUpdateThreashold; - WebRtc_Word16 stability; - - pw16_vec = inst->pw16_speechHistory + inst->w16_speechHistoryLen - w16_vecLen; - -#ifdef NETEQ_VAD - if( !inst->VADInst.VADEnabled /* we are not using post-decode VAD */ - || inst->VADInst.VADDecision == 0 ) - { /* ... or, post-decode VAD says passive speaker */ -#endif /* NETEQ_VAD */ - - /*Insert zeros to guarantee that boundary values do not distort autocorrelation */ - WEBRTC_SPL_MEMCPY_W16(pw16_tempVec, pw16_vec - BGN_LPC_ORDER, BGN_LPC_ORDER); - WebRtcSpl_MemSetW16(pw16_vec - BGN_LPC_ORDER, 0, BGN_LPC_ORDER); - - w16_maxSample = WebRtcSpl_MaxAbsValueW16(pw16_vec, w16_vecLen); - w16_tmp = 8 /* log2(w16_veclen) = 8 */ - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_maxSample, w16_maxSample)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcNetEQ_CrossCorr(pw32_autoCorr, pw16_vec, pw16_vec, w16_vecLen, BGN_LPC_ORDER + 1, - w16_tmp, -1); - - /* Copy back data */ - WEBRTC_SPL_MEMCPY_W16(pw16_vec - BGN_LPC_ORDER, pw16_tempVec, BGN_LPC_ORDER); - - w16_enSampleShift = 8 - w16_tmp; /* Number of shifts to get energy/sample */ - /* pw32_autoCorr[0]>>w16_enSampleShift */ - w32_en = WEBRTC_SPL_RSHIFT_W32(pw32_autoCorr[0], w16_enSampleShift); - if ((w32_en < BGN_Inst->w32_energyUpdate -#ifdef NETEQ_VAD - /* post-decode VAD disabled and w32_en sufficiently low */ - && !inst->VADInst.VADEnabled) - /* ... or, post-decode VAD says passive speaker */ - || (inst->VADInst.VADEnabled && inst->VADInst.VADDecision == 0) -#else - ) /* just close the extra parenthesis */ -#endif /* NETEQ_VAD */ - ) - { - /* Generate LPC coefficients */ - if (pw32_autoCorr[0] > 0) - { - /* regardless of whether the filter is actually updated or not, - update energy threshold levels, since we have in fact observed - a low energy signal */ - if (w32_en < BGN_Inst->w32_energyUpdate) - { - /* Never get under 1.0 in average sample energy */ - BGN_Inst->w32_energyUpdate = WEBRTC_SPL_MAX(w32_en, 1); - BGN_Inst->w32_energyUpdateLow = 0; - } - - stability = WebRtcSpl_LevinsonDurbin(pw32_autoCorr, pw16_A, pw16_rc, BGN_LPC_ORDER); - /* Only update BGN if filter is stable */ - if (stability != 1) - { - return; - } - } - else - { - /* Do not update */ - return; - } - /* Generate the CNG gain factor by looking at the energy of the residual */ - WebRtcSpl_FilterMAFastQ12(pw16_vec + w16_vecLen - 64, pw16_outVec, pw16_A, - BGN_LPC_ORDER + 1, 64); - w32_enBGN = WebRtcNetEQ_DotW16W16(pw16_outVec, pw16_outVec, 64, 0); - /* Dot product should never overflow since it is BGN and residual! */ - - /* - * Check spectral flatness - * Comparing the residual variance with the input signal variance tells - * if the spectrum is flat or not. - * (20*w32_enBGN) >= (w32_en<<6) - * Also ensure that the energy is non-zero. - */ - if ((WEBRTC_SPL_MUL_32_16(w32_enBGN, 20) >= WEBRTC_SPL_LSHIFT_W32(w32_en, 6)) - && (w32_en > 0)) - { - /* spectrum is flat enough; save filter parameters */ - - WEBRTC_SPL_MEMCPY_W16(BGN_Inst->pw16_filter, pw16_A, BGN_LPC_ORDER+1); - WEBRTC_SPL_MEMCPY_W16(BGN_Inst->pw16_filterState, - pw16_vec + w16_vecLen - BGN_LPC_ORDER, BGN_LPC_ORDER); - - /* Save energy level */ - BGN_Inst->w32_energy = WEBRTC_SPL_MAX(w32_en, 1); - - /* Update energy threshold levels */ - /* Never get under 1.0 in average sample energy */ - BGN_Inst->w32_energyUpdate = WEBRTC_SPL_MAX(w32_en, 1); - BGN_Inst->w32_energyUpdateLow = 0; - - /* Normalize w32_enBGN to 29 or 30 bits before sqrt */ - w16_tmp2 = WebRtcSpl_NormW32(w32_enBGN) - 1; - if (w16_tmp2 & 0x1) - { - w16_tmp2 -= 1; /* Even number of shifts required */ - } - w32_enBGN = WEBRTC_SPL_SHIFT_W32(w32_enBGN, w16_tmp2); - - /* Calculate scale and shift factor */ - BGN_Inst->w16_scale = (WebRtc_Word16) WebRtcSpl_Sqrt(w32_enBGN); - BGN_Inst->w16_scaleShift = 13 + ((6 + w16_tmp2) >> 1); /* RANDN table is in Q13, */ - /* 6=log2(64) */ - - BGN_Inst->w16_initialized = 1; - } - - } - else - { - /* - * Will only happen if post-decode VAD is disabled and w32_en is not low enough. - * Increase the threshold for update so that it increases by a factor 4 in four - * seconds. - * energy = energy * 1.0035 - */ - w32_tmp = WEBRTC_SPL_MUL_16_16_RSFT(NETEQFIX_BGNFRAQINCQ16, - BGN_Inst->w32_energyUpdateLow, 16); - w32_tmp += WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (WebRtc_Word16)(BGN_Inst->w32_energyUpdate & 0xFF)); - w32_tmp += (WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (WebRtc_Word16)((BGN_Inst->w32_energyUpdate>>8) & 0xFF)) << 8); - BGN_Inst->w32_energyUpdateLow += w32_tmp; - - BGN_Inst->w32_energyUpdate += WEBRTC_SPL_MUL_16_16(NETEQFIX_BGNFRAQINCQ16, - (WebRtc_Word16)(BGN_Inst->w32_energyUpdate>>16)); - BGN_Inst->w32_energyUpdate += BGN_Inst->w32_energyUpdateLow >> 16; - BGN_Inst->w32_energyUpdateLow = (BGN_Inst->w32_energyUpdateLow & 0x0FFFF); - - /* Update maximum energy */ - /* Decrease by a factor 1/1024 each time */ - BGN_Inst->w32_energyMax = BGN_Inst->w32_energyMax - (BGN_Inst->w32_energyMax >> 10); - if (w32_en > BGN_Inst->w32_energyMax) - { - BGN_Inst->w32_energyMax = w32_en; - } - - /* Set update level to at the minimum 60.21dB lower then the maximum energy */ - w32_enUpdateThreashold = (BGN_Inst->w32_energyMax + 524288) >> 20; - if (w32_enUpdateThreashold > BGN_Inst->w32_energyUpdate) - { - BGN_Inst->w32_energyUpdate = w32_enUpdateThreashold; - } - } - -#ifdef NETEQ_VAD -} /* closing initial if-statement */ -#endif /* NETEQ_VAD */ - - return; -} - -#undef SCRATCH_PW32_AUTO_CORR -#undef SCRATCH_PW16_TEMP_VEC -#undef SCRATCH_PW16_RC -#undef SCRATCH_PW16_OUT_VEC - diff --git a/modules/audio_coding/NetEQ/main/source/buffer_stats.h b/modules/audio_coding/NetEQ/main/source/buffer_stats.h deleted file mode 100644 index 9820519b4..000000000 --- a/modules/audio_coding/NetEQ/main/source/buffer_stats.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Calculates and stores the packet buffer statistics. - */ - -#ifndef BUFFER_STATS_H -#define BUFFER_STATS_H - -#include "automode.h" -#include "webrtc_neteq.h" /* to define enum WebRtcNetEQPlayoutMode */ - -/* NetEQ related decisions */ -#define BUFSTATS_DO_NORMAL 0 -#define BUFSTATS_DO_ACCELERATE 1 -#define BUFSTATS_DO_MERGE 2 -#define BUFSTATS_DO_EXPAND 3 -#define BUFSTAT_REINIT 4 -#define BUFSTATS_DO_RFC3389CNG_PACKET 5 -#define BUFSTATS_DO_RFC3389CNG_NOPACKET 6 -#define BUFSTATS_DO_INTERNAL_CNG_NOPACKET 7 -#define BUFSTATS_DO_PREEMPTIVE_EXPAND 8 -#define BUFSTAT_REINIT_DECODER 9 -#define BUFSTATS_DO_DTMF_ONLY 10 -/* Decisions related to when NetEQ is switched off (or in FAX mode) */ -#define BUFSTATS_DO_ALTERNATIVE_PLC 11 -#define BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS 12 -#define BUFSTATS_DO_AUDIO_REPETITION 13 -#define BUFSTATS_DO_AUDIO_REPETITION_INC_TS 14 - -/* Reinit decoder states after this number of expands (upon arrival of new packet) */ -#define REINIT_AFTER_EXPANDS 100 - -/* Wait no longer than this number of RecOut calls before using an "early" packet */ -#define MAX_WAIT_FOR_PACKET 10 - -/* CNG modes */ -#define CNG_OFF 0 -#define CNG_RFC3389_ON 1 -#define CNG_INTERNAL_ON 2 - -typedef struct -{ - - /* store statistical data here */ - WebRtc_Word16 w16_cngOn; /* remember if CNG is interrupted by other event (e.g. DTMF) */ - WebRtc_Word16 w16_noExpand; - WebRtc_Word32 uw32_CNGplayedTS; - - /* VQmon data */ - WebRtc_UWord16 avgDelayMsQ8; - WebRtc_Word16 maxDelayMs; - - AutomodeInst_t Automode_inst; - -} BufstatsInst_t; - -/**************************************************************************** - * WebRtcNetEQ_BufstatsDecision() - * - * Gives a decision about what action that is currently desired - * - * - * Input: - * inst: The bufstat instance - * cur_size: Current buffer size in ms in Q3 domain - * targetTS: The desired timestamp to start playout from - * availableTS: The closest future value available in buffer - * noPacket 1 if no packet is available, makes availableTS undefined - * prevPlayMode mode of last NetEq playout - * timestampsPerCall number of timestamp for 10ms - * - * Output: - * Returns: A decision, as defined above (see top of file) - * - */ - -WebRtc_UWord16 WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, WebRtc_Word16 frameSize, - WebRtc_Word32 cur_size, WebRtc_UWord32 targetTS, - WebRtc_UWord32 availableTS, int noPacket, - int cngPacket, int prevPlayMode, - enum WebRtcNetEQPlayoutMode playoutMode, - int timestampsPerCall, int NoOfExpandCalls, - WebRtc_Word16 fs_mult, - WebRtc_Word16 lastModeBGNonly, int playDtmf); - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/bufstats_decision.c b/modules/audio_coding/NetEQ/main/source/bufstats_decision.c deleted file mode 100644 index 9dae39389..000000000 --- a/modules/audio_coding/NetEQ/main/source/bufstats_decision.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function where the main decision logic for buffer level - * adaptation happens. - */ - -#include "buffer_stats.h" - -#include - -#include "signal_processing_library.h" - -#include "automode.h" -#include "neteq_defines.h" -#include "neteq_error_codes.h" -#include "webrtc_neteq.h" - -#define NETEQ_BUFSTAT_20MS_Q7 2560 /* = 20 ms in Q7 */ - -WebRtc_UWord16 WebRtcNetEQ_BufstatsDecision(BufstatsInst_t *inst, WebRtc_Word16 frameSize, - WebRtc_Word32 cur_size, WebRtc_UWord32 targetTS, - WebRtc_UWord32 availableTS, int noPacket, - int cngPacket, int prevPlayMode, - enum WebRtcNetEQPlayoutMode playoutMode, - int timestampsPerCall, int NoOfExpandCalls, - WebRtc_Word16 fs_mult, - WebRtc_Word16 lastModeBGNonly, int playDtmf) -{ - - int currentDelayMs; - WebRtc_Word32 currSizeSamples = cur_size; - WebRtc_Word16 extraDelayPacketsQ8 = 0; - - /* Avoid overflow if the buffer size should be really large (cur_size is limited 256ms) */ - WebRtc_Word32 curr_sizeQ7 = WEBRTC_SPL_LSHIFT_W32(cur_size, 4); - WebRtc_UWord16 level_limit_hi, level_limit_lo; - - inst->Automode_inst.prevTimeScale &= (prevPlayMode == MODE_SUCCESS_ACCELERATE - || prevPlayMode == MODE_LOWEN_ACCELERATE || prevPlayMode == MODE_SUCCESS_PREEMPTIVE - || prevPlayMode == MODE_LOWEN_PREEMPTIVE); - - if ((prevPlayMode != MODE_RFC3389CNG) && (prevPlayMode != MODE_CODEC_INTERNAL_CNG)) - { - /* - * Do not update buffer history if currently playing CNG - * since it will bias the filtered buffer level. - */ - WebRtcNetEQ_BufferLevelFilter(cur_size, &(inst->Automode_inst), timestampsPerCall, - fs_mult); - } - else - { - /* only update time counters */ - inst->Automode_inst.packetIatCountSamp += timestampsPerCall; /* packet inter-arrival time */ - inst->Automode_inst.peakIatCountSamp += timestampsPerCall; /* peak inter-arrival time */ - inst->Automode_inst.timescaleHoldOff >>= 1; /* time-scaling limiter */ - } - cur_size = WEBRTC_SPL_MIN(curr_sizeQ7, WEBRTC_SPL_WORD16_MAX); - - /* Calculate VQmon related variables */ - /* avgDelay = avgDelay*(511/512) + currentDelay*(1/512) (sample ms delay in Q8) */ - inst->avgDelayMsQ8 = (WebRtc_Word16) (WEBRTC_SPL_MUL_16_16_RSFT(inst->avgDelayMsQ8,511,9) - + (cur_size >> 9)); - - /* Update maximum delay if needed */ - currentDelayMs = (curr_sizeQ7 >> 7); - if (currentDelayMs > inst->maxDelayMs) - { - inst->maxDelayMs = currentDelayMs; - } - - /* NetEQ is on with normal or steaming mode */ - if (playoutMode == kPlayoutOn || playoutMode == kPlayoutStreaming) - { - /* Guard for errors, so that it should not get stuck in error mode */ - if (prevPlayMode == MODE_ERROR) - { - if (noPacket) - { - return BUFSTATS_DO_EXPAND; - } - else - { - return BUFSTAT_REINIT; - } - } - - if (prevPlayMode != MODE_EXPAND && prevPlayMode != MODE_FADE_TO_BGN) - { - inst->w16_noExpand = 1; - } - else - { - inst->w16_noExpand = 0; - } - - if (cngPacket) - { - /* signed difference between wanted and available TS */ - WebRtc_Word32 diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS; - - if ((diffTS) < 0 && (prevPlayMode == MODE_RFC3389CNG)) - { - /* Not time to play this packet yet. Wait another round before using this - * packet. Keep on playing CNG from previous CNG parameters. */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - - /* otherwise, go for the CNG packet now */ - return BUFSTATS_DO_RFC3389CNG_PACKET; - } - - /*Check for expand/cng */ - if (noPacket) - { - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - /* keep on playing CNG */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - /* keep on playing internal CNG */ - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else if (playDtmf == 1) - { - /* we have not audio data, but can play DTMF */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - /* nothing to play => do Expand */ - return BUFSTATS_DO_EXPAND; - } - } - - /* - * If the expand period was very long, reset NetEQ since it is likely that the - * sender was restarted. - */ - if (NoOfExpandCalls > REINIT_AFTER_EXPANDS) return BUFSTAT_REINIT_DECODER; - - /* Calculate extra delay in Q8 packets */ - if (inst->Automode_inst.extraDelayMs > 0 && inst->Automode_inst.packetSpeechLenSamp - > 0) - { - extraDelayPacketsQ8 = WebRtcSpl_DivW32W16ResW16( - (WEBRTC_SPL_MUL(inst->Automode_inst.extraDelayMs, 8 * fs_mult) << 8), - inst->Automode_inst.packetSpeechLenSamp); - /* (extra delay in samples in Q8) */ - } - - /* Check if needed packet is available */ - if (targetTS == availableTS) - { - - /* If last mode was not expand, and there is no DTMF to play */ - if (inst->w16_noExpand == 1 && playDtmf == 0) - { - /* If so check for accelerate */ - - level_limit_lo = ((inst->Automode_inst.optBufLevel) >> 1) /* 50 % */ - + ((inst->Automode_inst.optBufLevel) >> 2); /* ... + 25% = 75% */ - - /* set upper limit to optBufLevel, but make sure that window is at least 20ms */ - level_limit_hi = WEBRTC_SPL_MAX(inst->Automode_inst.optBufLevel, - level_limit_lo + - WebRtcSpl_DivW32W16ResW16((WEBRTC_SPL_MUL(20*8, fs_mult) << 8), - inst->Automode_inst.packetSpeechLenSamp)); - - /* if extra delay is non-zero, add it */ - if (extraDelayPacketsQ8 > 0) - { - level_limit_hi += extraDelayPacketsQ8; - level_limit_lo += extraDelayPacketsQ8; - } - - if (((inst->Automode_inst.buffLevelFilt >= level_limit_hi) && - (inst->Automode_inst.timescaleHoldOff == 0)) || - (inst->Automode_inst.buffLevelFilt >= level_limit_hi << 2)) - { - /* - * Buffer level higher than limit and time-scaling allowed, - * OR buffer level _really_ high. - */ - return BUFSTATS_DO_ACCELERATE; - } - else if ((inst->Automode_inst.buffLevelFilt < level_limit_lo) - && (inst->Automode_inst.timescaleHoldOff == 0)) - { - return BUFSTATS_DO_PREEMPTIVE_EXPAND; - } - } - return BUFSTATS_DO_NORMAL; - } - - /* Check for Merge */ - else if (availableTS > targetTS) - { - - /* Check that we do not play a packet "too early" */ - if ((prevPlayMode == MODE_EXPAND) - && (availableTS - targetTS - < (WebRtc_UWord32) WEBRTC_SPL_MUL_16_16((WebRtc_Word16)timestampsPerCall, - (WebRtc_Word16)REINIT_AFTER_EXPANDS)) - && (NoOfExpandCalls < MAX_WAIT_FOR_PACKET) - && (availableTS - > targetTS - + WEBRTC_SPL_MUL_16_16((WebRtc_Word16)timestampsPerCall, - (WebRtc_Word16)NoOfExpandCalls)) - && (inst->Automode_inst.buffLevelFilt <= inst->Automode_inst.optBufLevel - + extraDelayPacketsQ8)) - { - if (playDtmf == 1) - { - /* we still have DTMF to play, so do not perform expand */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - /* nothing to play */ - return BUFSTATS_DO_EXPAND; - } - } - - /* If previous was CNG period or BGNonly then no merge is needed */ - if ((prevPlayMode == MODE_RFC3389CNG) || (prevPlayMode == MODE_CODEC_INTERNAL_CNG) - || lastModeBGNonly) - { - /* - * Keep the same delay as before the CNG (or maximum 70 ms in buffer as safety - * precaution), but make sure that the number of samples in buffer is no - * higher than 4 times the optimal level. - */ - WebRtc_Word32 diffTS = (inst->uw32_CNGplayedTS + targetTS) - availableTS; - if (diffTS >= 0 - || (WEBRTC_SPL_MUL_16_16_RSFT( inst->Automode_inst.optBufLevel - + extraDelayPacketsQ8, - inst->Automode_inst.packetSpeechLenSamp, 6) < currSizeSamples)) - { - /* it is time to play this new packet */ - return BUFSTATS_DO_NORMAL; - } - else - { - /* it is too early to play this new packet => keep on playing CNG */ - if (prevPlayMode == MODE_RFC3389CNG) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (prevPlayMode == MODE_CODEC_INTERNAL_CNG) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else if (playDtmf == 1) - { - /* we have not audio data, but can play DTMF */ - return BUFSTATS_DO_DTMF_ONLY; - } - else /* lastModeBGNonly */ - { - /* signal expand, but this will result in BGN again */ - return BUFSTATS_DO_EXPAND; - } - } - } - - /* Do not merge unless we have done a Expand before (for complexity reasons) */ - if ((inst->w16_noExpand == 0) || ((frameSize < timestampsPerCall) && (cur_size - > NETEQ_BUFSTAT_20MS_Q7))) - { - return BUFSTATS_DO_MERGE; - } - else if (playDtmf == 1) - { - /* play DTMF instead of expand */ - return BUFSTATS_DO_DTMF_ONLY; - } - else - { - return BUFSTATS_DO_EXPAND; - } - } - } - else - { /* kPlayoutOff or kPlayoutFax */ - if (cngPacket) - { - if (((WebRtc_Word32) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0) - { - /* time to play this packet now */ - return BUFSTATS_DO_RFC3389CNG_PACKET; - } - else - { - /* wait before playing this packet */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - } - if (noPacket) - { - /* - * No packet => - * 1. If in CNG mode play as usual - * 2. Otherwise use other method to generate data and hold TS value - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - /* keep on playing CNG */ - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - /* keep on playing internal CNG */ - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* nothing to play => invent some data to play out */ - if (playoutMode == kPlayoutOff) - { - return BUFSTATS_DO_ALTERNATIVE_PLC; - } - else if (playoutMode == kPlayoutFax) - { - return BUFSTATS_DO_AUDIO_REPETITION; - } - else - { - /* UNDEFINED, should not get here... */ - assert(0); - return BUFSTAT_REINIT; - } - } - } - else if (targetTS == availableTS) - { - return BUFSTATS_DO_NORMAL; - } - else - { - if (((WebRtc_Word32) ((inst->uw32_CNGplayedTS + targetTS) - availableTS)) >= 0) - { - return BUFSTATS_DO_NORMAL; - } - else if (playoutMode == kPlayoutOff) - { - /* - * If currently playing CNG, continue with that. Don't increase TS - * since uw32_CNGplayedTS will be increased. - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* - * Otherwise, do PLC and increase TS while waiting for the time to - * play this packet. - */ - return BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS; - } - } - else if (playoutMode == kPlayoutFax) - { - /* - * If currently playing CNG, continue with that don't increase TS since - * uw32_CNGplayedTS will be increased. - */ - if (inst->w16_cngOn == CNG_RFC3389_ON) - { - return BUFSTATS_DO_RFC3389CNG_NOPACKET; - } - else if (inst->w16_cngOn == CNG_INTERNAL_ON) - { - return BUFSTATS_DO_INTERNAL_CNG_NOPACKET; - } - else - { - /* - * Otherwise, do audio repetition and increase TS while waiting for the - * time to play this packet. - */ - return BUFSTATS_DO_AUDIO_REPETITION_INC_TS; - } - } - else - { - /* UNDEFINED, should not get here... */ - assert(0); - return BUFSTAT_REINIT; - } - } - } - /* We should not get here (but sometimes we do anyway...) */ - return BUFSTAT_REINIT; -} - diff --git a/modules/audio_coding/NetEQ/main/source/cng_internal.c b/modules/audio_coding/NetEQ/main/source/cng_internal.c deleted file mode 100644 index f3a10dcf0..000000000 --- a/modules/audio_coding/NetEQ/main/source/cng_internal.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for obtaining comfort noise from noise parameters - * according to IETF RFC 3389. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" -#include "webrtc_cng.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -/**************************************************************************** - * WebRtcNetEQ_Cng(...) - * - * This function produces CNG according to RFC 3389. - * - * Input: - * - inst : NetEQ DSP instance - * - len : Number of samples to produce (max 640 or - * 640 - fsHz*5/8000 for first-time CNG, governed by - * the definition of WEBRTC_CNG_MAX_OUTSIZE_ORDER in - * webrtc_cng.h) - * - * Output: - * - pw16_outData : Output CNG - * - * Return value : 0 - Ok - * <0 - Error - */ - -#ifdef NETEQ_CNG_CODEC -/* Must compile NetEQ with CNG support to enable this function */ - -int WebRtcNetEQ_Cng(DSPInst_t *inst, WebRtc_Word16 *pw16_outData, int len) -{ - WebRtc_Word16 w16_winMute = 0; /* mixing factor for overlap data */ - WebRtc_Word16 w16_winUnMute = 0; /* mixing factor for comfort noise */ - WebRtc_Word16 w16_winMuteInc = 0; /* mixing factor increment (negative) */ - WebRtc_Word16 w16_winUnMuteInc = 0; /* mixing factor increment */ - int i; - - /* - * Check if last RecOut call was other than RFC3389, - * that is, this call is the first of a CNG period. - */ - if (inst->w16_mode != MODE_RFC3389CNG) - { - /* Reset generation and overlap slightly with old data */ - - /* Generate len samples + overlap */ - if (WebRtcCng_Generate(inst->CNG_Codec_inst, pw16_outData, - (WebRtc_Word16) (len + inst->ExpandInst.w16_overlap), 1) < 0) - { - /* error returned */ - return -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - } - - /* Set windowing parameters depending on sample rate */ - if (inst->fs == 8000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_8KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_8KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_8KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs == 16000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_16KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_16KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_16KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs == 32000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_32KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_32KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_32KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else if (inst->fs == 48000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_48KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_48KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_48KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC; -#endif - } - else - { - /* Unsupported sample rate (should not be possible) */ - return NETEQ_OTHER_ERROR; - } - - /* Do overlap add between new vector and overlap */ - for (i = 0; i < inst->ExpandInst.w16_overlap; i++) - { - /* overlapVec[i] = WinMute * overlapVec[i] + WinUnMute * outData[i] */ - inst->ExpandInst.pw16_overlapVec[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16( - inst->ExpandInst.pw16_overlapVec[i], w16_winMute) + - WEBRTC_SPL_MUL_16_16(pw16_outData[i], w16_winUnMute) - + 16384, 15); /* shift with proper rounding */ - - w16_winMute += w16_winMuteInc; /* decrease mute factor (inc<0) */ - w16_winUnMute += w16_winUnMuteInc; /* increase unmute factor (inc>0) */ - - } - - /* - * Shift the contents of the outData buffer by overlap samples, since we - * already used these first samples in the overlapVec above - */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_outData+inst->ExpandInst.w16_overlap, len); - - } - else - { - /* This is a subsequent CNG call; no special overlap needed */ - - /* Generate len samples */ - if (WebRtcCng_Generate(inst->CNG_Codec_inst, pw16_outData, (WebRtc_Word16) len, 0) < 0) - { - /* error returned */ - return -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - } - } - - return 0; - -} - -#endif /* NETEQ_CNG_CODEC */ - diff --git a/modules/audio_coding/NetEQ/main/source/codec_db.c b/modules/audio_coding/NetEQ/main/source/codec_db.c deleted file mode 100644 index c15306d3b..000000000 --- a/modules/audio_coding/NetEQ/main/source/codec_db.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the codec database. - */ - -#include "codec_db.h" - -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -/* - * Resets the codec database. - */ - -int WebRtcNetEQ_DbReset(CodecDbInst_t *inst) -{ - int i; - - WebRtcSpl_MemSetW16((WebRtc_Word16*) inst, 0, - sizeof(CodecDbInst_t) / sizeof(WebRtc_Word16)); - - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - inst->position[i] = -1; - } - - for (i = 0; i < NUM_CODECS; i++) - { - inst->payloadType[i] = -1; - } - - for (i = 0; i < NUM_CNG_CODECS; i++) - { - inst->CNGpayloadType[i] = -1; - } - - return 0; -} - -/* - * Adds a new codec to the database. - */ - -int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - WebRtc_Word16 payloadType, FuncDecode funcDecode, - FuncDecode funcDecodeRCU, FuncDecodePLC funcDecodePLC, - FuncDecodeInit funcDecodeInit, FuncAddLatePkt funcAddLatePkt, - FuncGetMDinfo funcGetMDinfo, FuncGetPitchInfo funcGetPitch, - FuncUpdBWEst funcUpdBWEst, FuncGetErrorCode funcGetErrorCode, - void* codec_state, WebRtc_UWord16 codec_fs) -{ - - int temp; - int insertCNGcodec = 0, overwriteCNGcodec = 0, CNGpos = -1; - -#ifndef NETEQ_RED_CODEC - if (codec == kDecoderRED) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } -#endif - if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec - >= (int) kDecoderReservedEnd)) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } - - if ((codec_fs != 8000) -#ifdef NETEQ_WIDEBAND - &&(codec_fs!=16000) -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - &&(codec_fs!=32000) -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - &&(codec_fs!=48000) -#endif - ) - { - return CODEC_DB_UNSUPPORTED_FS; - } - - /* Ensure that the codec type is supported */ - switch (codec) - { -#ifdef NETEQ_PCM16B_CODEC - case kDecoderPCM16B : -#endif -#ifdef NETEQ_G711_CODEC - case kDecoderPCMu : - case kDecoderPCMa : -#endif -#ifdef NETEQ_ILBC_CODEC - case kDecoderILBC : -#endif -#ifdef NETEQ_ISAC_CODEC - case kDecoderISAC : -#endif -#ifdef NETEQ_ISAC_SWB_CODEC - case kDecoderISACswb : -#endif -#ifdef NETEQ_G722_CODEC - case kDecoderG722 : -#endif -#ifdef NETEQ_WIDEBAND - case kDecoderPCM16Bwb : -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case kDecoderPCM16Bswb32kHz : -#endif -#ifdef NETEQ_CNG_CODEC - case kDecoderCNG : -#endif -#ifdef NETEQ_ATEVENT_DECODE - case kDecoderAVT : -#endif -#ifdef NETEQ_RED_CODEC - case kDecoderRED : -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case kDecoderPCM16Bswb48kHz : -#endif -#ifdef NETEQ_ARBITRARY_CODEC - case kDecoderArbitrary: -#endif -#ifdef NETEQ_G729_CODEC - case kDecoderG729: -#endif -#ifdef NETEQ_G729_1_CODEC - case kDecoderG729_1 : -#endif -#ifdef NETEQ_G726_CODEC - case kDecoderG726_16 : - case kDecoderG726_24 : - case kDecoderG726_32 : - case kDecoderG726_40 : -#endif -#ifdef NETEQ_G722_1_CODEC - case kDecoderG722_1_16 : - case kDecoderG722_1_24 : - case kDecoderG722_1_32 : -#endif -#ifdef NETEQ_G722_1C_CODEC - case kDecoderG722_1C_24 : - case kDecoderG722_1C_32 : - case kDecoderG722_1C_48 : -#endif -#ifdef NETEQ_SPEEX_CODEC - case kDecoderSPEEX_8 : - case kDecoderSPEEX_16 : -#endif -#ifdef NETEQ_GSMFR_CODEC - case kDecoderGSMFR : -#endif -#ifdef NETEQ_AMR_CODEC - case kDecoderAMR : -#endif -#ifdef NETEQ_AMRWB_CODEC - case kDecoderAMRWB : -#endif - { - /* If we end up here, the inserted codec is supported => Do nothing */ - break; - } - default: - { - /* If we get to this point, the inserted codec is not supported */ - return CODEC_DB_UNSUPPORTED_CODEC; - } - } - - /* Check to see if payload type is taken */ - if (WebRtcNetEQ_DbGetCodec(inst, payloadType) > 0) - { - return CODEC_DB_PAYLOAD_TAKEN; - } - - /* Special case for CNG codecs */ - if (codec == kDecoderCNG) - { - /* check if this is first CNG codec to be registered */ - if (WebRtcNetEQ_DbGetPayload(inst, codec) == CODEC_DB_NOT_EXIST2) - { - /* no other CNG codec found */ - insertCNGcodec = 1; - } - - /* find the appropriate insert position in CNG payload vector */ - switch (codec_fs) - { -#ifdef NETEQ_WIDEBAND - case 16000: - CNGpos = 1; - break; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - CNGpos = 2; - break; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - CNGpos = 3; - break; -#endif - default: /* 8000 Hz case */ - CNGpos = 0; - /* - * The 8 kHz CNG payload type is the one associated with the regular codec DB - * should override any other setting. - * Overwrite if this isn't the first CNG - */ - overwriteCNGcodec = !insertCNGcodec; - break; - } - - /* insert CNG payload type */ - inst->CNGpayloadType[CNGpos] = payloadType; - - } - - if ((codec != kDecoderCNG) || (insertCNGcodec == 1) || (overwriteCNGcodec == 1)) - { - /* Check if we have reached the maximum numbers of simultaneous codecs */ - if (inst->nrOfCodecs == NUM_CODECS) return CODEC_DB_FULL; - - /* Check that codec has not already been initialized to DB => - remove it and reinitialize according to new spec */ - if ((inst->position[codec] != -1) && (overwriteCNGcodec != 1)) - { /* if registering multiple CNG codecs, don't remove, just overwrite */ - WebRtcNetEQ_DbRemove(inst, codec); - } - - if (overwriteCNGcodec == 1) - { - temp = inst->position[codec]; - } - else - { - temp = inst->nrOfCodecs; /* Store this codecs position */ - inst->position[codec] = temp; - inst->nrOfCodecs++; - } - - inst->payloadType[temp] = payloadType; - - /* Copy to database */ - inst->codec_state[temp] = codec_state; - inst->funcDecode[temp] = funcDecode; - inst->funcDecodeRCU[temp] = funcDecodeRCU; - inst->funcAddLatePkt[temp] = funcAddLatePkt; - inst->funcDecodeInit[temp] = funcDecodeInit; - inst->funcDecodePLC[temp] = funcDecodePLC; - inst->funcGetMDinfo[temp] = funcGetMDinfo; - inst->funcGetPitch[temp] = funcGetPitch; - inst->funcUpdBWEst[temp] = funcUpdBWEst; - inst->funcGetErrorCode[temp] = funcGetErrorCode; - inst->codec_fs[temp] = codec_fs; - - } - - return 0; -} - -/* - * Removes a codec from the database. - */ - -int WebRtcNetEQ_DbRemove(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec) -{ - int i; - int pos = -1; - -#ifndef NETEQ_RED_CODEC - if (codec == kDecoderRED) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } -#endif - if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec - >= (int) kDecoderReservedEnd)) - { - return CODEC_DB_UNSUPPORTED_CODEC; - } - - pos = inst->position[codec]; - if (pos == -1) - { - return CODEC_DB_NOT_EXIST4; - } - else - { - /* Remove this codec */ - inst->position[codec] = -1; - for (i = pos; i < (inst->nrOfCodecs - 1); i++) - { - inst->payloadType[i] = inst->payloadType[i + 1]; - inst->codec_state[i] = inst->codec_state[i + 1]; - inst->funcDecode[i] = inst->funcDecode[i + 1]; - inst->funcDecodeRCU[i] = inst->funcDecodeRCU[i + 1]; - inst->funcAddLatePkt[i] = inst->funcAddLatePkt[i + 1]; - inst->funcDecodeInit[i] = inst->funcDecodeInit[i + 1]; - inst->funcDecodePLC[i] = inst->funcDecodePLC[i + 1]; - inst->funcGetMDinfo[i] = inst->funcGetMDinfo[i + 1]; - inst->funcGetPitch[i] = inst->funcGetPitch[i + 1]; - inst->funcUpdBWEst[i] = inst->funcUpdBWEst[i + 1]; - inst->funcGetErrorCode[i] = inst->funcGetErrorCode[i + 1]; - inst->codec_fs[i] = inst->codec_fs[i + 1]; - } - inst->payloadType[i] = -1; - inst->codec_state[i] = NULL; - inst->funcDecode[i] = NULL; - inst->funcDecodeRCU[i] = NULL; - inst->funcAddLatePkt[i] = NULL; - inst->funcDecodeInit[i] = NULL; - inst->funcDecodePLC[i] = NULL; - inst->funcGetMDinfo[i] = NULL; - inst->funcGetPitch[i] = NULL; - inst->funcUpdBWEst[i] = NULL; - inst->funcGetErrorCode[i] = NULL; - inst->codec_fs[i] = 0; - /* Move down all the codecs above this one */ - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - if (inst->position[i] >= pos) - { - inst->position[i] = inst->position[i] - 1; - } - } - inst->nrOfCodecs--; - - if (codec == kDecoderCNG) - { - /* also remove all registered CNG payload types */ - for (i = 0; i < NUM_CNG_CODECS; i++) - { - inst->CNGpayloadType[i] = -1; - } - } - } - return 0; -} - -/* - * Get the decoder function pointers for a codec. - */ - -int WebRtcNetEQ_DbGetPtrs(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - CodecFuncInst_t *ptr_inst) -{ - - int pos = inst->position[codec]; - if ((codec <= kDecoderReservedStart) || (codec >= kDecoderReservedEnd) || (codec - > NUM_TOTAL_CODECS)) - { - /* ERROR */ - pos = -1; - } - if (pos >= 0) - { - ptr_inst->codec_state = inst->codec_state[pos]; - ptr_inst->funcAddLatePkt = inst->funcAddLatePkt[pos]; - ptr_inst->funcDecode = inst->funcDecode[pos]; - ptr_inst->funcDecodeRCU = inst->funcDecodeRCU[pos]; - ptr_inst->funcDecodeInit = inst->funcDecodeInit[pos]; - ptr_inst->funcDecodePLC = inst->funcDecodePLC[pos]; - ptr_inst->funcGetMDinfo = inst->funcGetMDinfo[pos]; - ptr_inst->funcUpdBWEst = inst->funcUpdBWEst[pos]; - ptr_inst->funcGetErrorCode = inst->funcGetErrorCode[pos]; - ptr_inst->codec_fs = inst->codec_fs[pos]; - return 0; - } - else - { - WebRtcSpl_MemSetW16((WebRtc_Word16*) ptr_inst, 0, - sizeof(CodecFuncInst_t) / sizeof(WebRtc_Word16)); - return CODEC_DB_NOT_EXIST1; - } -} - -/* - * Returns payload number given a codec identifier. - */ - -int WebRtcNetEQ_DbGetPayload(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codecID) -{ - if (inst->position[codecID] == -1) - return CODEC_DB_NOT_EXIST2; - else - return (inst->payloadType[inst->position[codecID]]); - -} - -/* - * Returns codec identifier given a payload number. - * Returns -1 if the payload type does not exist. - */ - -int WebRtcNetEQ_DbGetCodec(CodecDbInst_t *inst, int payloadType) -{ - int i, pos; - - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - pos = inst->position[i]; - if (pos != -1) - { - if (inst->payloadType[pos] == payloadType) return i; - } - } - - /* did not find payload type */ - /* check if it's a CNG codec */ - if (WebRtcNetEQ_DbIsCNGPayload(inst, payloadType)) - { - return kDecoderCNG; - } - - /* found no match */ - return CODEC_DB_NOT_EXIST3; -} - -/* - * Extracts the Payload Split information of the codec with the specified payloadType. - */ - -int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecID, - int codedsize) -{ - - switch (codecID) - { -#ifdef NETEQ_ISAC_CODEC - case kDecoderISAC: -#endif -#ifdef NETEQ_ISAC_SWB_CODEC - case kDecoderISACswb: -#endif -#ifdef NETEQ_ARBITRARY_CODEC - case kDecoderArbitrary: -#endif -#ifdef NETEQ_AMR_CODEC - case kDecoderAMR: -#endif -#ifdef NETEQ_AMRWB_CODEC - case kDecoderAMRWB: -#endif -#ifdef NETEQ_G726_CODEC - /* Treat G726 as non-splittable to simplify the implementation */ - case kDecoderG726_16: - case kDecoderG726_24: - case kDecoderG726_32: - case kDecoderG726_40: -#endif -#ifdef NETEQ_SPEEX_CODEC - case kDecoderSPEEX_8: - case kDecoderSPEEX_16: -#endif -#ifdef NETEQ_G729_1_CODEC - case kDecoderG729_1: -#endif - { - /* These codecs' payloads are not splittable */ - inst->deltaBytes = NO_SPLIT; - return 0; - } - - /* - * Sample based coders are a special case. - * In this case, deltaTime signals the number of bytes per timestamp unit times 2 - * in log2 domain. - */ -#if (defined NETEQ_G711_CODEC) - case kDecoderPCMu: - case kDecoderPCMa: - { - inst->deltaBytes = -12; - inst->deltaTime = 1; - return 0; - } -#endif -#if (defined NETEQ_G722_CODEC) - case kDecoderG722: - { - inst->deltaBytes = -14; - inst->deltaTime = 0; - return 0; - } -#endif -#if (defined NETEQ_PCM16B_CODEC) - case kDecoderPCM16B: - { - inst->deltaBytes = -12; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_WIDEBAND)) - case kDecoderPCM16Bwb: - { - inst->deltaBytes = -14; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_32KHZ_WIDEBAND)) - case kDecoderPCM16Bswb32kHz: - { - inst->deltaBytes = -18; - inst->deltaTime = 2; - return 0; - } -#endif -#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_48KHZ_WIDEBAND)) - case kDecoderPCM16Bswb48kHz: - { - inst->deltaBytes = -22; - inst->deltaTime = 2; - return 0; - } -#endif - - /* Splittable payloads */ -#ifdef NETEQ_G722_1_CODEC - case kDecoderG722_1_16: - { - inst->deltaBytes = 40; - inst->deltaTime = 320; - return 0; - } - case kDecoderG722_1_24: - { - inst->deltaBytes = 60; - inst->deltaTime = 320; - return 0; - } - case kDecoderG722_1_32: - { - inst->deltaBytes = 80; - inst->deltaTime = 320; - return 0; - } -#endif -#ifdef NETEQ_G722_1C_CODEC - case kDecoderG722_1C_24: - { - inst->deltaBytes = 60; - inst->deltaTime = 640; - return 0; - } - case kDecoderG722_1C_32: - { - inst->deltaBytes = 80; - inst->deltaTime = 640; - return 0; - } - case kDecoderG722_1C_48: - { - inst->deltaBytes = 120; - inst->deltaTime = 640; - return 0; - } -#endif -#ifdef NETEQ_G729_CODEC - case kDecoderG729: - { - inst->deltaBytes = 10; - inst->deltaTime = 80; - return 0; - } -#endif -#ifdef NETEQ_ILBC_CODEC - case kDecoderILBC: - { - /* Check for splitting of iLBC packets. - * If payload size is a multiple of 50 bytes it should be split into 30ms frames. - * If payload size is a multiple of 38 bytes it should be split into 20ms frames. - * Least common multiplier between 38 and 50 is 950, so the payload size must be less than - * 950 bytes in order to resolve the frames unambiguously. - * Currently max 12 frames in one bundle. - */ - switch (codedsize) - { - case 50: - case 100: - case 150: - case 200: - case 250: - case 300: - case 350: - case 400: - case 450: - case 500: - case 550: - case 600: - { - inst->deltaBytes = 50; - inst->deltaTime = 240; - break; - } - case 38: - case 76: - case 114: - case 152: - case 190: - case 228: - case 266: - case 304: - case 342: - case 380: - case 418: - case 456: - { - inst->deltaBytes = 38; - inst->deltaTime = 160; - break; - } - default: - { - return AMBIGUOUS_ILBC_FRAME_SIZE; /* Something not supported... */ - } - } - return 0; - } -#endif -#ifdef NETEQ_GSMFR_CODEC - case kDecoderGSMFR: - { - inst->deltaBytes = 33; - inst->deltaTime = 160; - return 0; - } -#endif - default: - { /*Unknown codec */ - inst->deltaBytes = NO_SPLIT; - return CODEC_DB_UNKNOWN_CODEC; - } - } /* end of switch */ -} - -/* - * Returns 1 if codec is multiple description, 0 otherwise. - * NOTE: This function is a stub, since there currently are no MD codecs. - */ -int WebRtcNetEQ_DbIsMDCodec(enum WebRtcNetEQDecoder codecID) -{ - if (0) /* Add test for MD codecs here */ - return 1; - else - return 0; -} - -/* - * Returns 1 if payload type is registered as a CNG codec, 0 otherwise - */ -int WebRtcNetEQ_DbIsCNGPayload(CodecDbInst_t *inst, int payloadType) -{ -#ifdef NETEQ_CNG_CODEC - int i; - - for(i=0; iCNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType) ) - { - return 1; - } - } -#endif - - return 0; - -} - -/* - * Return the sample rate for the codec with the given payload type, 0 if error - */ -WebRtc_UWord16 WebRtcNetEQ_DbGetSampleRate(CodecDbInst_t *inst, int payloadType) -{ - int i; - CodecFuncInst_t codecInst; - - /* Sanity */ - if (inst == NULL) - { - /* return 0 Hz */ - return 0; - } - - /* Check among CNG payloads */ - for (i = 0; i < NUM_CNG_CODECS; i++) - { - if ((inst->CNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType)) - { - switch (i) - { - case 1: - return 16000; - case 2: - return 32000; - case 3: - return 48000; - default: - return 8000; - } - } - } - - /* Not a CNG payload, check the other payloads */ - i = WebRtcNetEQ_DbGetCodec(inst, payloadType); - if (i >= 0) - { - if (WebRtcNetEQ_DbGetPtrs(inst, (enum WebRtcNetEQDecoder) i, &codecInst) != 0) - { - /* Unexpected error, return 0 Hz */ - return 0; - } - return codecInst.codec_fs; - } - - /* If we end up here, we got an error, return 0 Hz */ - return 0; - -} - diff --git a/modules/audio_coding/NetEQ/main/source/codec_db.h b/modules/audio_coding/NetEQ/main/source/codec_db.h deleted file mode 100644 index 7f429807c..000000000 --- a/modules/audio_coding/NetEQ/main/source/codec_db.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Interface for the codec database. - */ - -#ifndef CODEC_DB_H -#define CODEC_DB_H - -#include "typedefs.h" - -#include "webrtc_neteq.h" -#include "codec_db_defines.h" -#include "neteq_defines.h" - -#if defined(NETEQ_48KHZ_WIDEBAND) - #define NUM_CNG_CODECS 4 -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define NUM_CNG_CODECS 3 -#elif defined(NETEQ_WIDEBAND) - #define NUM_CNG_CODECS 2 -#else - #define NUM_CNG_CODECS 1 -#endif - -typedef struct -{ - - WebRtc_Word16 position[NUM_TOTAL_CODECS]; - WebRtc_Word16 nrOfCodecs; - - WebRtc_Word16 payloadType[NUM_CODECS]; - FuncDecode funcDecode[NUM_CODECS]; - FuncDecode funcDecodeRCU[NUM_CODECS]; - FuncDecodePLC funcDecodePLC[NUM_CODECS]; - FuncDecodeInit funcDecodeInit[NUM_CODECS]; - FuncAddLatePkt funcAddLatePkt[NUM_CODECS]; - FuncGetMDinfo funcGetMDinfo[NUM_CODECS]; - FuncGetPitchInfo funcGetPitch[NUM_CODECS]; - FuncUpdBWEst funcUpdBWEst[NUM_CODECS]; - FuncGetErrorCode funcGetErrorCode[NUM_CODECS]; - void * codec_state[NUM_CODECS]; - WebRtc_UWord16 codec_fs[NUM_CODECS]; - WebRtc_Word16 CNGpayloadType[NUM_CNG_CODECS]; - -} CodecDbInst_t; - -#define NO_SPLIT -1 /* codec payload cannot be split */ - -typedef struct -{ - WebRtc_Word16 deltaBytes; - WebRtc_Word16 deltaTime; -} SplitInfo_t; - -/* - * Resets the codec database. - */ -int WebRtcNetEQ_DbReset(CodecDbInst_t *inst); - -/* - * Adds a new codec to the database. - */ -int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec, - WebRtc_Word16 payloadType, FuncDecode funcDecode, - FuncDecode funcDecodeRCU, FuncDecodePLC funcDecodePLC, - FuncDecodeInit funcDecodeInit, FuncAddLatePkt funcAddLatePkt, - FuncGetMDinfo funcGetMDinfo, FuncGetPitchInfo funcGetPitch, - FuncUpdBWEst funcUpdBWEst, FuncGetErrorCode funcGetErrorCode, - void* codec_state, WebRtc_UWord16 codec_fs); - -/* - * Removes a codec from the database. - */ -int WebRtcNetEQ_DbRemove(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec); - -/* - * Get the decoder function pointers for a codec. - */ -int WebRtcNetEQ_DbGetPtrs(CodecDbInst_t *inst, enum WebRtcNetEQDecoder, - CodecFuncInst_t *ptr_inst); - -/* - * Returns payload number given a codec identifier. - */ - -int WebRtcNetEQ_DbGetPayload(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codecID); - -/* - * Returns codec identifier given a payload number. - */ - -int WebRtcNetEQ_DbGetCodec(CodecDbInst_t *inst, int payloadType); - -/* - * Extracts the Payload Split information of the codec with the specified payloadType. - */ - -int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecID, - int codedsize); - -/* - * Returns 1 if codec is multiple description type, 0 otherwise. - */ -int WebRtcNetEQ_DbIsMDCodec(enum WebRtcNetEQDecoder codecID); - -/* - * Returns 1 if payload type is registered as a CNG codec, 0 otherwise. - */ -int WebRtcNetEQ_DbIsCNGPayload(CodecDbInst_t *inst, int payloadType); - -/* - * Return the sample rate for the codec with the given payload type, 0 if error. - */ -WebRtc_UWord16 WebRtcNetEQ_DbGetSampleRate(CodecDbInst_t *inst, int payloadType); - -#endif - diff --git a/modules/audio_coding/NetEQ/main/source/codec_db_defines.h b/modules/audio_coding/NetEQ/main/source/codec_db_defines.h deleted file mode 100644 index 9b78b862f..000000000 --- a/modules/audio_coding/NetEQ/main/source/codec_db_defines.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Some definitions related to the codec database. - */ - -#ifndef CODEC_DB_DEFINES_H -#define CODEC_DB_DEFINES_H - -#include "typedefs.h" - -#define NUM_CODECS 47 /* probably too large with the limited set of supported codecs*/ -#define NUM_TOTAL_CODECS kDecoderReservedEnd - -/* - * Pointer to decoder function. - */ -typedef WebRtc_Word16 (*FuncDecode)(void* state, WebRtc_Word16* encoded, WebRtc_Word16 len, - WebRtc_Word16* decoded, WebRtc_Word16* speechType); - -/* - * Pointer to PLC function. - */ -typedef WebRtc_Word16 (*FuncDecodePLC)(void* state, WebRtc_Word16* decodec, - WebRtc_Word16 frames); - -/* - * Pointer to decoder init function. - */ -typedef WebRtc_Word16 (*FuncDecodeInit)(void* state); - -/* - * Pointer to add late packet function. - */ -typedef WebRtc_Word16 - (*FuncAddLatePkt)(void* state, WebRtc_Word16* encoded, WebRtc_Word16 len); - -/* - * Pointer to get MD infofunction. - */ -typedef WebRtc_Word16 (*FuncGetMDinfo)(void* state); - -/* - * Pointer to pitch info function. - * Return 0 for unvoiced, -1 if pitch not availiable. - */ -typedef WebRtc_Word16 (*FuncGetPitchInfo)(void* state, WebRtc_Word16* encoded, - WebRtc_Word16* length); - -/* - * Pointer to the update bandwidth estimate function - */ -typedef WebRtc_Word16 (*FuncUpdBWEst)(void* state, const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts); - -/* - * Pointer to error code function - */ -typedef WebRtc_Word16 (*FuncGetErrorCode)(void* state); - -typedef struct CodecFuncInst_t_ -{ - - FuncDecode funcDecode; - FuncDecode funcDecodeRCU; - FuncDecodePLC funcDecodePLC; - FuncDecodeInit funcDecodeInit; - FuncAddLatePkt funcAddLatePkt; - FuncGetMDinfo funcGetMDinfo; - FuncUpdBWEst funcUpdBWEst; /* Currently in use for the ISAC family (without LC) only*/ - FuncGetErrorCode funcGetErrorCode; - void * codec_state; - WebRtc_UWord16 codec_fs; - WebRtc_UWord32 timeStamp; - -} CodecFuncInst_t; - -#endif - diff --git a/modules/audio_coding/NetEQ/main/source/correlator.c b/modules/audio_coding/NetEQ/main/source/correlator.c deleted file mode 100644 index 97c41da82..000000000 --- a/modules/audio_coding/NetEQ/main/source/correlator.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_corrVec 62 0 61 - WebRtc_Word16 pw16_data_ds 124 0 123 - WebRtc_Word32 pw32_corr 2*54 124 231 - - Total: 232 - */ - -#define SCRATCH_pw16_corrVec 0 -#define SCRATCH_pw16_data_ds 0 -#define SCRATCH_pw32_corr 124 - -#define NETEQ_CORRELATOR_DSVECLEN 124 /* 124 = 60 + 10 + 54 */ - -WebRtc_Word16 WebRtcNetEQ_Correlator(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_data, - WebRtc_Word16 w16_dataLen, - WebRtc_Word16 *pw16_corrOut, - WebRtc_Word16 *pw16_corrScale) -{ - WebRtc_Word16 w16_corrLen = 60; -#ifdef SCRATCH - WebRtc_Word16 *pw16_data_ds = pw16_scratchPtr + SCRATCH_pw16_corrVec; - WebRtc_Word32 *pw32_corr = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_pw32_corr); - /* WebRtc_Word16 *pw16_corrVec = pw16_scratchPtr + SCRATCH_pw16_corrVec;*/ -#else - WebRtc_Word16 pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN]; - WebRtc_Word32 pw32_corr[54]; - /* WebRtc_Word16 pw16_corrVec[4+54+4];*/ -#endif - /* WebRtc_Word16 *pw16_corr=&pw16_corrVec[4];*/ - WebRtc_Word16 w16_maxVal; - WebRtc_Word32 w32_maxVal; - WebRtc_Word16 w16_normVal; - WebRtc_Word16 w16_normVal2; - /* WebRtc_Word16 w16_corrUpsLen;*/ - WebRtc_Word16 *pw16_B = NULL; - WebRtc_Word16 w16_Blen = 0; - WebRtc_Word16 w16_factor = 0; - - /* Set constants depending on frequency used */ - if (inst->fs == 8000) - { - w16_Blen = 3; - w16_factor = 2; - pw16_B = (WebRtc_Word16*) WebRtcNetEQ_kDownsample8kHzTbl; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs==16000) - { - w16_Blen = 5; - w16_factor = 4; - pw16_B = (WebRtc_Word16*)WebRtcNetEQ_kDownsample16kHzTbl; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs==32000) - { - w16_Blen = 7; - w16_factor = 8; - pw16_B = (WebRtc_Word16*)WebRtcNetEQ_kDownsample32kHzTbl; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if inst->fs==48000 */ - { - w16_Blen = 7; - w16_factor = 12; - pw16_B = (WebRtc_Word16*)WebRtcNetEQ_kDownsample48kHzTbl; -#endif - } - - /* Downsample data in order to work on a 4 kHz sampled signal */ - WebRtcSpl_DownsampleFast( - pw16_data + w16_dataLen - (NETEQ_CORRELATOR_DSVECLEN * w16_factor), - (WebRtc_Word16) (NETEQ_CORRELATOR_DSVECLEN * w16_factor), pw16_data_ds, - NETEQ_CORRELATOR_DSVECLEN, pw16_B, w16_Blen, w16_factor, (WebRtc_Word16) 0); - - /* Normalize downsampled vector to using entire 16 bit */ - w16_maxVal = WebRtcSpl_MaxAbsValueW16(pw16_data_ds, 124); - w16_normVal = 16 - WebRtcSpl_NormW32((WebRtc_Word32) w16_maxVal); - WebRtcSpl_VectorBitShiftW16(pw16_data_ds, NETEQ_CORRELATOR_DSVECLEN, pw16_data_ds, - w16_normVal); - - /* Correlate from lag 10 to lag 60 (20..120 in NB and 40..240 in WB) */ - - WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN - w16_corrLen], - &pw16_data_ds[NETEQ_CORRELATOR_DSVECLEN - w16_corrLen - 10], 60, 54, - 6 /*maxValue... shifts*/, -1); - - /* - * Move data from w32 to w16 vector. - * Normalize downsampled vector to using all 14 bits - */ - w32_maxVal = WebRtcSpl_MaxAbsValueW32(pw32_corr, 54); - w16_normVal2 = 18 - WebRtcSpl_NormW32(w32_maxVal); - w16_normVal2 = WEBRTC_SPL_MAX(w16_normVal2, 0); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corrOut, 54, pw32_corr, w16_normVal2); - - /* Total scale factor (right shifts) of correlation value */ - *pw16_corrScale = 2 * w16_normVal + 6 + w16_normVal2; - - return (50 + 1); -} - -#undef SCRATCH_pw16_corrVec -#undef SCRATCH_pw16_data_ds -#undef SCRATCH_pw32_corr - diff --git a/modules/audio_coding/NetEQ/main/source/delay_logging.h b/modules/audio_coding/NetEQ/main/source/delay_logging.h deleted file mode 100644 index 04b1c4015..000000000 --- a/modules/audio_coding/NetEQ/main/source/delay_logging.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Contains definitions for the delay logging functionality. Only used for debugging and - * tracing purposes. - */ - -#ifndef DELAY_LOGGING_H -#define DELAY_LOGGING_H - -#define NETEQ_DELAY_LOGGING_VERSION_STRING "2.0" - -#define NETEQ_DELAY_LOGGING_SIGNAL_RECIN 1 -#define NETEQ_DELAY_LOGGING_SIGNAL_FLUSH 2 -#define NETEQ_DELAY_LOGGING_SIGNAL_CLOCK 3 -#define NETEQ_DELAY_LOGGING_SIGNAL_EOF 4 -#define NETEQ_DELAY_LOGGING_SIGNAL_DECODE 5 -#define NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS 6 -#define NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO 7 -#define NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO 8 -#define NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO 9 -#define NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO 10 -#define NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF 11 -#define NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC 12 - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/dsp.c b/modules/audio_coding/NetEQ/main/source/dsp.c deleted file mode 100644 index 73396b2f5..000000000 --- a/modules/audio_coding/NetEQ/main/source/dsp.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some DSP initialization functions and - * constant table definitions. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -/* Filter coefficients used when downsampling from the indicated - sample rates (8, 16, 32, 48 kHz) to 4 kHz. - Coefficients are in Q12. */ - -/* {0.3, 0.4, 0.3} */ -const WebRtc_Word16 WebRtcNetEQ_kDownsample8kHzTbl[] = { 1229, 1638, 1229 }; - -#ifdef NETEQ_WIDEBAND -/* {0.15, 0.2, 0.3, 0.2, 0.15} */ -const WebRtc_Word16 WebRtcNetEQ_kDownsample16kHzTbl[] = -{ 614, 819, 1229, 819, 614}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -/* {0.1425, 0.1251, 0.1525, 0.1628, 0.1525, 0.1251, 0.1425} */ -const WebRtc_Word16 WebRtcNetEQ_kDownsample32kHzTbl[] = -{ 584, 512, 625, 667, 625, 512, 584}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -/* {0.2487, 0.0952, 0.1042, 0.1074, 0.1042, 0.0952, 0.2487} */ -const WebRtc_Word16 WebRtcNetEQ_kDownsample48kHzTbl[] = -{ 1019, 390, 427, 440, 427, 390, 1019}; -#endif - -/* Constants used in expand function WebRtcNetEQ_Expand */ - -/* Q12: -1.264421 + 4.8659148*x - 4.0092827*x^2 + 1.4100529*x^3 */ -const WebRtc_Word16 WebRtcNetEQ_kMixFractionFuncTbl[4] = { -5179, 19931, -16422, 5776 }; - -/* Tabulated divisions to save complexity */ -/* 1049/{0, .., 6} */ -const WebRtc_Word16 WebRtcNetEQ_k1049div[7] = { 0, 1049, 524, 349, 262, 209, 174 }; - -/* 2097/{0, .., 6} */ -const WebRtc_Word16 WebRtcNetEQ_k2097div[7] = { 0, 2097, 1048, 699, 524, 419, 349 }; - -/* 5243/{0, .., 6} */ -const WebRtc_Word16 WebRtcNetEQ_k5243div[7] = { 0, 5243, 2621, 1747, 1310, 1048, 873 }; - -#ifdef WEBRTC_NETEQ_40BITACC_TEST -/* - * Run NetEQ with simulated 40-bit accumulator to run bit-exact to a DSP - * implementation where the main (spl and NetEQ) functions have been - * 40-bit optimized. For testing purposes. - */ - -/**************************************************************************** - * WebRtcNetEQ_40BitAccCrossCorr(...) - * - * Calculates the Cross correlation between two sequences seq1 and seq2. Seq1 - * is fixed and seq2 slides as the pointer is increased with step - * - * Input: - * - seq1 : First sequence (fixed throughout the correlation) - * - seq2 : Second sequence (slided step_seq2 for each - * new correlation) - * - dimSeq : Number of samples to use in the cross correlation. - * Should be no larger than 1024 to avoid overflow. - * - dimCrossCorr : Number of CrossCorrelations to calculate (start - * position for seq2 is updated for each new one) - * - rShift : Number of right shifts to use - * - step_seq2 : How many (positive or negative) steps the seq2 - * pointer should be updated for each new cross - * correlation value - * - * Output: - * - crossCorr : The cross correlation in Q-rShift - */ - -void WebRtcNetEQ_40BitAccCrossCorr(WebRtc_Word32 *crossCorr, - WebRtc_Word16 *seq1, - WebRtc_Word16 *seq2, - WebRtc_Word16 dimSeq, - WebRtc_Word16 dimCrossCorr, - WebRtc_Word16 rShift, - WebRtc_Word16 step_seq2) -{ - int i, j; - WebRtc_Word16 *seq1Ptr, *seq2Ptr; - WebRtc_Word64 acc; - - for (i = 0; i < dimCrossCorr; i++) - { - /* Set the pointer to the static vector, set the pointer to - the sliding vector and initialize crossCorr */ - seq1Ptr = seq1; - seq2Ptr = seq2 + (step_seq2 * i); - acc = 0; - - /* Perform the cross correlation */ - for (j = 0; j < dimSeq; j++) - { - acc += WEBRTC_SPL_MUL_16_16((*seq1Ptr), (*seq2Ptr)); - seq1Ptr++; - seq2Ptr++; - } - - (*crossCorr) = (WebRtc_Word32) (acc >> rShift); - crossCorr++; - } -} - -/**************************************************************************** - * WebRtcNetEQ_40BitAccDotW16W16(...) - * - * Calculates the dot product between two vectors (WebRtc_Word16) - * - * Input: - * - vector1 : Vector 1 - * - vector2 : Vector 2 - * - len : Number of samples in vector - * Should be no larger than 1024 to avoid overflow. - * - scaling : The number of left shifts required to avoid overflow - * in the dot product - * Return value : The dot product - */ - -WebRtc_Word32 WebRtcNetEQ_40BitAccDotW16W16(WebRtc_Word16 *vector1, - WebRtc_Word16 *vector2, - int len, - int scaling) -{ - WebRtc_Word32 sum; - int i; - WebRtc_Word64 acc; - - acc = 0; - for (i = 0; i < len; i++) - { - acc += WEBRTC_SPL_MUL_16_16(*vector1++, *vector2++); - } - - sum = (WebRtc_Word32) (acc >> scaling); - - return(sum); -} - -#endif /* WEBRTC_NETEQ_40BITACC_TEST */ - -/**************************************************************************** - * WebRtcNetEQ_DSPInit(...) - * - * Initializes DSP side of NetEQ. - * - * Input: - * - inst : NetEq DSP instance - * - fs : Initial sample rate (may change when decoding data) - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_DSPInit(DSPInst_t *inst, WebRtc_UWord16 fs) -{ - - int res = 0; - WebRtc_Word16 fs_mult; - - /* Pointers and values to save before clearing the instance */ -#ifdef NETEQ_CNG_CODEC - void *savedPtr1 = inst->CNG_Codec_inst; -#endif - void *savedPtr2 = inst->pw16_readAddress; - void *savedPtr3 = inst->pw16_writeAddress; - void *savedPtr4 = inst->main_inst; -#ifdef NETEQ_VAD - void *savedVADptr = inst->VADInst.VADState; - VADInitFunction savedVADinit = inst->VADInst.initFunction; - VADSetmodeFunction savedVADsetmode = inst->VADInst.setmodeFunction; - VADFunction savedVADfunc = inst->VADInst.VADFunction; - WebRtc_Word16 savedVADEnabled = inst->VADInst.VADEnabled; - WebRtc_Word16 savedVADMode = inst->VADInst.VADMode; -#endif /* NETEQ_VAD */ - DSPStats_t saveStats; - WebRtc_Word16 saveMsPerCall = inst->millisecondsPerCall; - enum BGNMode saveBgnMode = inst->BGNInst.bgnMode; -#ifdef NETEQ_STEREO - MasterSlaveInfo saveMSinfo; -#endif - - /* copy contents of statInst to avoid clearing */WEBRTC_SPL_MEMCPY_W16(&saveStats, &(inst->statInst), - sizeof(DSPStats_t)/sizeof(WebRtc_Word16)); - -#ifdef NETEQ_STEREO - /* copy contents of msInfo to avoid clearing */WEBRTC_SPL_MEMCPY_W16(&saveMSinfo, &(inst->msInfo), - sizeof(MasterSlaveInfo)/sizeof(WebRtc_Word16)); -#endif - - /* check that the sample rate is valid */ - if ((fs != 8000) -#ifdef NETEQ_WIDEBAND - &&(fs!=16000) -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - &&(fs!=32000) -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - &&(fs!=48000) -#endif - ) - { - /* invalid rate */ - return (CODEC_DB_UNSUPPORTED_FS); - } - - /* calcualte fs/8000 */ - fs_mult = WebRtcSpl_DivW32W16ResW16(fs, 8000); - - /* Set everything to zero since most variables should be zero at start */ - WebRtcSpl_MemSetW16((WebRtc_Word16 *) inst, 0, sizeof(DSPInst_t) / sizeof(WebRtc_Word16)); - - /* Restore saved pointers */ -#ifdef NETEQ_CNG_CODEC - inst->CNG_Codec_inst = (CNG_dec_inst *)savedPtr1; -#endif - inst->pw16_readAddress = (WebRtc_Word16 *) savedPtr2; - inst->pw16_writeAddress = (WebRtc_Word16 *) savedPtr3; - inst->main_inst = savedPtr4; -#ifdef NETEQ_VAD - inst->VADInst.VADState = savedVADptr; - inst->VADInst.initFunction = savedVADinit; - inst->VADInst.setmodeFunction = savedVADsetmode; - inst->VADInst.VADFunction = savedVADfunc; - inst->VADInst.VADEnabled = savedVADEnabled; - inst->VADInst.VADMode = savedVADMode; -#endif /* NETEQ_VAD */ - - /* Initialize main part */ - inst->fs = fs; - inst->millisecondsPerCall = saveMsPerCall; - inst->timestampsPerCall = inst->millisecondsPerCall * 8 * fs_mult; - inst->ExpandInst.w16_overlap = 5 * fs_mult; - inst->endPosition = 565 * fs_mult; - inst->curPosition = inst->endPosition - inst->ExpandInst.w16_overlap; - inst->w16_seedInc = 1; - inst->uw16_seed = 777; - inst->w16_muteFactor = 16384; /* 1.0 in Q14 */ - inst->w16_frameLen = 3 * inst->timestampsPerCall; /* Dummy initialize to 30ms */ - - inst->w16_speechHistoryLen = 256 * fs_mult; - inst->pw16_speechHistory = &inst->speechBuffer[inst->endPosition - - inst->w16_speechHistoryLen]; - inst->ExpandInst.pw16_overlapVec = &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - - inst->ExpandInst.w16_overlap]); - - /* Reusage of memory in speechBuffer inside Expand */ - inst->ExpandInst.pw16_expVecs[0] = &inst->speechBuffer[0]; - inst->ExpandInst.pw16_expVecs[1] = &inst->speechBuffer[126 * fs_mult]; - inst->ExpandInst.pw16_arState = &inst->speechBuffer[2 * 126 * fs_mult]; - inst->ExpandInst.pw16_arFilter = &inst->speechBuffer[2 * 126 * fs_mult - + UNVOICED_LPC_ORDER]; - /* Ends at 2*126*fs_mult+UNVOICED_LPC_ORDER+(UNVOICED_LPC_ORDER+1) */ - - inst->ExpandInst.w16_expandMuteFactor = 16384; /* 1.0 in Q14 */ - - /* Initialize BGN part */ - inst->BGNInst.pw16_filter[0] = 4096; - inst->BGNInst.w16_scale = 20000; - inst->BGNInst.w16_scaleShift = 24; - inst->BGNInst.w32_energyUpdate = 500000; - inst->BGNInst.w32_energyUpdateLow = 0; - inst->BGNInst.w32_energy = 2500; - inst->BGNInst.w16_initialized = 0; - inst->BGNInst.bgnMode = saveBgnMode; - - /* Recreate statistics counters */WEBRTC_SPL_MEMCPY_W16(&(inst->statInst), &saveStats, - sizeof(DSPStats_t)/sizeof(WebRtc_Word16)); - -#ifdef NETEQ_STEREO - /* Recreate MSinfo */WEBRTC_SPL_MEMCPY_W16(&(inst->msInfo), &saveMSinfo, - sizeof(MasterSlaveInfo)/sizeof(WebRtc_Word16)); -#endif - -#ifdef NETEQ_CNG_CODEC - if (inst->CNG_Codec_inst!=NULL) - { - /* initialize comfort noise generator */ - res |= WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif - -#ifdef NETEQ_VAD - /* initialize PostDecode VAD instance - (don't bother checking for NULL instance, this is done inside init function) */ - res |= WebRtcNetEQ_InitVAD(&inst->VADInst, fs); -#endif /* NETEQ_VAD */ - - return (res); -} - -/**************************************************************************** - * WebRtcNetEQ_AddressInit(...) - * - * Initializes the shared-memory communication on the DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - data2McuAddress : Pointer to memory where DSP writes / MCU reads - * - data2DspAddress : Pointer to memory where MCU writes / DSP reads - * - mainInst : NetEQ main instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_AddressInit(DSPInst_t *inst, const void *data2McuAddress, - const void *data2DspAddress, const void *mainInst) -{ - - /* set shared-memory addresses in the DSP instance */ - inst->pw16_readAddress = (WebRtc_Word16 *) data2DspAddress; - inst->pw16_writeAddress = (WebRtc_Word16 *) data2McuAddress; - - /* set pointer to main NetEQ instance */ - inst->main_inst = (void *) mainInst; - - /* set output frame size to 10 ms = 80 samples in narrowband */ - inst->millisecondsPerCall = 10; - inst->timestampsPerCall = 80; - - return (0); - -} - -/**************************************************************************** - * NETEQDSP_clearInCallStats(...) - * - * Reset in-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearInCallStats(DSPInst_t *inst) -{ - - /* Reset statistics counters */ - inst->statInst.accelerateLength = 0; - inst->statInst.expandLength = 0; - inst->statInst.preemptiveLength = 0; - - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_ClearPostCallStats(...) - * - * Reset post-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearPostCallStats(DSPInst_t *inst) -{ - - /* Reset statistics counters */ - inst->statInst.expandedVoiceSamples = 0; - inst->statInst.expandedNoiseSamples = 0; - - return (0); -} - -#ifdef NETEQ_VAD - -/**************************************************************************** - * WebRtcNetEQ_InitVAD(...) - * - * Initializes post-decode VAD instance. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - fs : Initial sample rate - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_InitVAD(PostDecodeVAD_t *VADInst, WebRtc_UWord16 fs) -{ - - int res = 0; - - /* initially, disable the post-decode VAD */ - VADInst->VADEnabled = 0; - - if (VADInst->VADState != NULL /* if VAD state is provided */ - && VADInst->initFunction != NULL /* and all function ... */ - && VADInst->setmodeFunction != NULL /* ... pointers ... */ - && VADInst->VADFunction != NULL) /* ... are defined */ - { - res = (int) VADInst->initFunction( VADInst->VADState ); /* call VAD init function */ - res |= WebRtcNetEQ_SetVADModeInternal( VADInst, VADInst->VADMode ); - - if (res!=0) - { - /* something is wrong; play it safe and set the VADstate to NULL */ - VADInst->VADState = NULL; - } - else if (fs<=16000) - { - /* enable VAD if NB or WB (VAD cannot handle SWB) */ - VADInst->VADEnabled = 1; - } - } - - /* reset SID/CNG interval counter */ - VADInst->SIDintervalCounter = 0; - - /* initialize with active-speaker decision */ - VADInst->VADDecision = 1; - - return(res); - -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADModeInternal(...) - * - * Set the VAD mode in the VAD struct, and communicate it to the VAD instance - * if it exists. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - mode : Mode number passed on to the VAD function - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADModeInternal(PostDecodeVAD_t *VADInst, - WebRtc_Word16 mode) -{ - - int res = 0; - - VADInst->VADMode = mode; - - if (VADInst->VADState != NULL) - { - /* call setmode function */ - res = (int) VADInst->setmodeFunction(VADInst->VADState, mode); - } - - return(res); - -} - -#endif /* NETEQ_VAD */ - -/**************************************************************************** - * WebRtcNetEQ_FlushSpeechBuffer(...) - * - * Flush the speech buffer. - * - * Input: - * - inst : NetEq DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_FlushSpeechBuffer(DSPInst_t *inst) -{ - WebRtc_Word16 fs_mult; - - /* calcualte fs/8000 */ - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - - /* clear buffer */ - WebRtcSpl_MemSetW16(inst->speechBuffer, 0, SPEECH_BUF_SIZE); - inst->endPosition = 565 * fs_mult; - inst->curPosition = inst->endPosition - inst->ExpandInst.w16_overlap; - - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/dsp.h b/modules/audio_coding/NetEQ/main/source/dsp.h deleted file mode 100644 index 0b0422939..000000000 --- a/modules/audio_coding/NetEQ/main/source/dsp.h +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some DSP initialization functions, - * constant table definitions and other parameters. - * Also contains definitions of all DSP-side data structures. - */ - - -#ifndef DSP_H -#define DSP_H - -#include "typedefs.h" - -#include "webrtc_cng.h" - -#include "codec_db_defines.h" -#include "neteq_defines.h" -#include "neteq_statistics.h" - -#ifdef NETEQ_ATEVENT_DECODE -#include "dtmf_tonegen.h" -#endif - - - -/*****************************/ -/* Pre-processor definitions */ -/*****************************/ - -/* FSMULT is the sample rate divided by 8000 */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define FSMULT 6 -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define FSMULT 4 -#elif defined(NETEQ_WIDEBAND) - #define FSMULT 2 -#else - #define FSMULT 1 -#endif - -/* Size of the speech buffer (or synchronization buffer). */ -/* 60 ms decoding + 10 ms syncbuff + 0.625ms lookahead */ -#define SPEECH_BUF_SIZE (565 * FSMULT) - -/* Misc definitions */ -#define BGN_LPC_ORDER (4 + FSMULT) /* 5, 6, 8, or 10 */ -#define UNVOICED_LPC_ORDER 6 -#define RANDVEC_NO_OF_SAMPLES 256 - -/* Number of milliseconds to remove/add during accelerate/pre-emptive expand - under BGNonly operation */ -#define DEFAULT_TIME_ADJUST 8 - -/* Number of RecOut calls without CNG/SID before re-enabling post-decode VAD */ -#define POST_DECODE_VAD_AUTO_ENABLE 3000 - -/* 8kHz windowing in Q15 (over 5 samples) */ -#define NETEQ_OVERLAP_WINMUTE_8KHZ_START 27307 -#define NETEQ_OVERLAP_WINMUTE_8KHZ_INC -5461 -#define NETEQ_OVERLAP_WINUNMUTE_8KHZ_START 5461 -#define NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC 5461 -/* 16kHz windowing in Q15 (over 10 samples) */ -#define NETEQ_OVERLAP_WINMUTE_16KHZ_START 29789 -#define NETEQ_OVERLAP_WINMUTE_16KHZ_INC -2979 -#define NETEQ_OVERLAP_WINUNMUTE_16KHZ_START 2979 -#define NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC 2979 -/* 32kHz windowing in Q15 (over 20 samples) */ -#define NETEQ_OVERLAP_WINMUTE_32KHZ_START 31208 -#define NETEQ_OVERLAP_WINMUTE_32KHZ_INC -1560 -#define NETEQ_OVERLAP_WINUNMUTE_32KHZ_START 1560 -#define NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC 1560 -/* 48kHz windowing in Q15 (over 30 samples) */ -#define NETEQ_OVERLAP_WINMUTE_48KHZ_START 31711 -#define NETEQ_OVERLAP_WINMUTE_48KHZ_INC -1057 -#define NETEQ_OVERLAP_WINUNMUTE_48KHZ_START 1057 -#define NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC 1057 - -/* Fade BGN towards zero after this many Expand calls */ -#define FADE_BGN_TIME 200 - - -/*******************/ -/* Constant tables */ -/*******************/ - -extern const WebRtc_Word16 WebRtcNetEQ_kDownsample8kHzTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_kDownsample16kHzTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_kDownsample32kHzTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_kDownsample48kHzTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_kRandnTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_kMixFractionFuncTbl[]; -extern const WebRtc_Word16 WebRtcNetEQ_k1049div[]; -extern const WebRtc_Word16 WebRtcNetEQ_k2097div[]; -extern const WebRtc_Word16 WebRtcNetEQ_k5243div[]; - - - -/************/ -/* Typedefs */ -/************/ - -enum BGNMode -{ - BGN_ON, /* default "normal" behavior with eternal noise */ - BGN_FADE, /* noise fades to zero after some time */ - BGN_OFF /* background noise is always zero */ -}; - -#ifdef NETEQ_STEREO -enum MasterSlaveMode -{ - NETEQ_MONO, /* stand-alone instance */ - NETEQ_MASTER, /* master instance in a spatial/stereo configuration */ - NETEQ_SLAVE /* slave instance in a spatial/stereo configuration */ -}; - -enum MasterSlaveExtraInfo -{ - NO_INFO, /* no info to convey */ - ACC_FAIL, /* signal that accelerate failed */ - PE_EXP_FAIL, /* signal that pre-emptive expand failed */ - DTMF_OVERDUB, /* signal that DTMF overdub is generated */ - DTMF_ONLY /* signal that DTMF only is played */ -}; -#endif - -/****************************/ -/* DSP-side data structures */ -/****************************/ - -/* Background noise (BGN) instance for storing BGN parameters - (sub-instance of NETEQDSP_inst) */ -typedef struct BGNInst_t_ -{ - - WebRtc_Word32 w32_energy; - WebRtc_Word32 w32_energyMax; - WebRtc_Word32 w32_energyUpdate; - WebRtc_Word32 w32_energyUpdateLow; - WebRtc_Word16 pw16_filterState[BGN_LPC_ORDER]; - WebRtc_Word16 pw16_filter[BGN_LPC_ORDER + 1]; - WebRtc_Word16 w16_mutefactor; - WebRtc_Word16 w16_scale; - WebRtc_Word16 w16_scaleShift; - WebRtc_Word16 w16_initialized; - enum BGNMode bgnMode; - -} BGNInst_t; - -/* Expansion instance (sub-instance of NETEQDSP_inst) */ -typedef struct ExpandInst_t_ -{ - - WebRtc_Word16 w16_overlap; /* Constant, 5 for NB and 10 for WB */ - WebRtc_Word16 w16_consecExp; /* Number of consecutive expand calls */ - WebRtc_Word16 *pw16_arFilter; /* length [UNVOICED_LPC_ORDER+1] */ - WebRtc_Word16 *pw16_arState; /* length [UNVOICED_LPC_ORDER] */ - WebRtc_Word16 w16_arGain; - WebRtc_Word16 w16_arGainScale; - WebRtc_Word16 w16_vFraction; /* Q14 */ - WebRtc_Word16 w16_currentVFraction; /* Q14 */ - WebRtc_Word16 *pw16_expVecs[2]; - WebRtc_Word16 w16_lags[3]; - WebRtc_Word16 w16_maxLag; - WebRtc_Word16 *pw16_overlapVec; /* last samples of speech history */ - WebRtc_Word16 w16_lagsDirection; - WebRtc_Word16 w16_lagsPosition; - WebRtc_Word16 w16_expandMuteFactor; /* Q14 */ - WebRtc_Word16 w16_stopMuting; - WebRtc_Word16 w16_onset; - WebRtc_Word16 w16_muteSlope; /* Q20 */ - -} ExpandInst_t; - -#ifdef NETEQ_VAD - -/* - * VAD function pointer types, replicating the typedefs in webrtc_neteq_internal.h. - * These function pointers match the definitions of WebRtc VAD functions WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in webrtc_vad.h. - */ -typedef WebRtc_Word16 (*VADInitFunction)(void *VAD_inst); -typedef WebRtc_Word16 (*VADSetmodeFunction)(void *VAD_inst, WebRtc_Word16 mode); -typedef WebRtc_Word16 (*VADFunction)(void *VAD_inst, WebRtc_Word16 fs, WebRtc_Word16 *frame, - WebRtc_Word16 frameLen); - -/* Post-decode VAD instance (sub-instance of NETEQDSP_inst) */ -typedef struct PostDecodeVAD_t_ -{ - - void *VADState; /* pointer to a VAD instance */ - - WebRtc_Word16 VADEnabled; /* 1 if enabled, 0 if disabled */ - WebRtc_Word16 VADMode; /* mode parameter to pass to the VAD function */ - WebRtc_Word16 VADDecision; /* 1 for active, 0 for passive */ - WebRtc_Word16 SIDintervalCounter; /* reset when decoding CNG/SID frame, - increment for each recout call */ - - /* Function pointers */ - VADInitFunction initFunction; /* VAD init function */ - VADSetmodeFunction setmodeFunction; /* VAD setmode function */ - VADFunction VADFunction; /* VAD function */ - -} PostDecodeVAD_t; - -#endif /* NETEQ_VAD */ - -#ifdef NETEQ_STEREO -#define MAX_MS_DECODES 10 - -typedef struct -{ - /* Stand-alone, master, or slave */ - enum MasterSlaveMode msMode; - - enum MasterSlaveExtraInfo extraInfo; - - WebRtc_UWord16 instruction; - WebRtc_Word16 distLag; - WebRtc_Word16 corrLag; - WebRtc_Word16 bestIndex; - - WebRtc_UWord32 endTimestamp; - WebRtc_UWord16 samplesLeftWithOverlap; - -} MasterSlaveInfo; -#endif - - -/* "Main" NetEQ DSP instance */ -typedef struct DSPInst_t_ -{ - - /* MCU/DSP Communication layer */ - WebRtc_Word16 *pw16_readAddress; - WebRtc_Word16 *pw16_writeAddress; - void *main_inst; - - /* Output frame size in ms and samples */ - WebRtc_Word16 millisecondsPerCall; - WebRtc_Word16 timestampsPerCall; - - /* - * Example of speech buffer - * - * ----------------------------------------------------------- - * | History T-60 to T | Future | - * ----------------------------------------------------------- - * ^ ^ - * | | - * curPosition endPosition - * - * History is gradually shifted out to the left when inserting - * new data at the end. - */ - - WebRtc_Word16 speechBuffer[SPEECH_BUF_SIZE]; /* History/future speech buffer */ - int curPosition; /* Next sample to play */ - int endPosition; /* Position that ends future data */ - WebRtc_UWord32 endTimestamp; /* Timestamp value at end of future data */ - WebRtc_UWord32 videoSyncTimestamp; /* (Estimated) timestamp of the last - played sample (usually same as - endTimestamp-(endPosition-curPosition) - except during Expand and CNG) */ - WebRtc_UWord16 fs; /* sample rate in Hz */ - WebRtc_Word16 w16_frameLen; /* decoder frame length in samples */ - WebRtc_Word16 w16_mode; /* operation used during last RecOut call */ - WebRtc_Word16 w16_muteFactor; /* speech mute factor in Q14 */ - WebRtc_Word16 *pw16_speechHistory; /* beginning of speech history during Expand */ - WebRtc_Word16 w16_speechHistoryLen; /* 256 for NB and 512 for WB */ - - /* random noise seed parameters */ - WebRtc_Word16 w16_seedInc; - WebRtc_UWord32 uw16_seed; - - /* VQmon related variable */ - WebRtc_Word16 w16_concealedTS; - - /*****************/ - /* Sub-instances */ - /*****************/ - - /* Decoder data */ - CodecFuncInst_t codec_ptr_inst; - -#ifdef NETEQ_CNG_CODEC - /* CNG "decoder" instance */ - CNG_dec_inst *CNG_Codec_inst; -#endif /* NETEQ_CNG_CODEC */ - -#ifdef NETEQ_ATEVENT_DECODE - /* DTMF generator instance */ - dtmf_tone_inst_t DTMFInst; -#endif /* NETEQ_CNG_CODEC */ - -#ifdef NETEQ_VAD - /* Post-decode VAD instance */ - PostDecodeVAD_t VADInst; -#endif /* NETEQ_VAD */ - - /* Expand instance (defined above) */ - ExpandInst_t ExpandInst; - - /* Background noise instance (defined above) */ - BGNInst_t BGNInst; - - /* Internal statistics instance */ - DSPStats_t statInst; - -#ifdef NETEQ_STEREO - /* Pointer to Master/Slave info */ - MasterSlaveInfo *msInfo; -#endif - -} DSPInst_t; - - -/*************************/ -/* Function declarations */ -/*************************/ - -/**************************************************************************** - * WebRtcNetEQ_DSPInit(...) - * - * Initializes DSP side of NetEQ. - * - * Input: - * - inst : NetEq DSP instance - * - fs : Initial sample rate (may change when decoding data) - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_DSPInit(DSPInst_t *inst, WebRtc_UWord16 fs); - -/**************************************************************************** - * WebRtcNetEQ_AddressInit(...) - * - * Initializes the shared-memory communication on the DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - data2McuAddress : Pointer to memory where DSP writes / MCU reads - * - data2DspAddress : Pointer to memory where MCU writes / DSP reads - * - mainInst : NetEQ main instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_AddressInit(DSPInst_t *inst, const void *data2McuAddress, - const void *data2DspAddress, const void *mainInst); - -/**************************************************************************** - * WebRtcNetEQ_ClearInCallStats(...) - * - * Reset in-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearInCallStats(DSPInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ClearPostCallStats(...) - * - * Reset post-call statistics variables on DSP side. - * - * Input: - * - inst : NetEQ DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - */ - -int WebRtcNetEQ_ClearPostCallStats(DSPInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_RecOutInternal(...) - * - * This function asks NetEQ for more speech/audio data. - * - * Input: - * - inst : NetEQ instance, i.e. the user that requests more - * speech/audio data. - * - outdata : Pointer to a memory space where the output data - * should be stored. - * - BGNonly : If non-zero, RecOut will only produce background - * noise. It will still draw packets from the packet - * buffer, but they will never be decoded. - * - * Output: - * - inst : Updated user information - * - len : Number of samples that were outputted from NetEq - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutInternal(DSPInst_t *inst, WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_Normal(...) - * - * This function has the possibility to modify data that is played out in Normal - * mode, for example adjust the gain of the signal. The length of the signal - * can not be changed. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - decoded : Pointer to vector of new data from decoder - * - len : Number of input samples - * - * Output: - * - inst : Updated user information - * - pw16_len : Pointer to varibale where the number of samples - * produced will be written - * - * Return value : >=0 - Number of samples written to outData - * -1 - Error - */ - -int WebRtcNetEQ_Normal(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_decoded, WebRtc_Word16 len, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_Expand(...) - * - * This function produces one "chunk" of expansion data (PLC audio). The - * lenght of the produced audio depends on the speech history. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - BGNonly : If non-zero, Expand will only produce background - * noise. - * - pw16_len : Desired number of samples (only for BGN mode). - * - * Output: - * - inst : Updated user information - * - outdata : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples that were outputted from NetEq - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Expand(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_GenerateBGN(...) - * - * This function generates and writes len samples of background noise to the - * output vector. The Expand function will be called repeteadly until the - * correct number of samples is produced. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector - * - len : Desired length of produced BGN. - * - * - * Output: - * - pw16_outData : Pointer to a memory space where the output data - * should be stored - * - * Return value : >=0 - Number of noise samples produced and written - * to output - * -1 - Error - */ - -int WebRtcNetEQ_GenerateBGN(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_outData, WebRtc_Word16 len); - -/**************************************************************************** - * WebRtcNetEQ_PreEmptiveExpand(...) - * - * This function tries to extend the audio data by repeating one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. The algorithm is the - * reciprocal of the Accelerate algorithm. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - oldDataLen : Length of the part of decoded that has already been played out. - * - BGNonly : If non-zero, Pre-emptive Expand will only copy - * the first DEFAULT_TIME_ADJUST seconds of the - * input and append to the end. No signal matching is - * done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored. The vector must be at least - * min(len + 120*fs/8000, NETEQ_MAX_OUTPUT_SIZE) - * elements long. - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PreEmptiveExpand(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - const WebRtc_Word16 *pw16_decoded, int len, int oldDataLen, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_Accelerate(...) - * - * This function tries to shorten the audio data by removing one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - BGNonly : If non-zero, Accelerate will only remove the last - * DEFAULT_TIME_ADJUST seconds of the intput. - * No signal matching is done. - * - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Accelerate(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - const WebRtc_Word16 *pw16_decoded, int len, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly); - -/**************************************************************************** - * WebRtcNetEQ_Merge(...) - * - * This function is used to merge new data from the decoder to the exisiting - * stream in the synchronization buffer. The merge operation is typically - * done after a packet loss, where the end of the expanded data does not - * fit naturally with the new decoded data. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to new decoded speech. - * - len : Number of samples in pw16_decoded. - * - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to pw16_outData - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Merge(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_decoded, int len, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len); - -/**************************************************************************** - * WebRtcNetEQ_Cng(...) - * - * This function produces CNG according to RFC 3389 - * - * Input: - * - inst : NetEQ DSP instance - * - len : Number of samples to produce - * - * Output: - * - pw16_outData : Output CNG - * - * Return value : 0 - Ok - * <0 - Error - */ - -#ifdef NETEQ_CNG_CODEC -/* Must compile NetEQ with CNG support to enable this function */ - -int WebRtcNetEQ_Cng(DSPInst_t *inst, WebRtc_Word16 *pw16_outData, int len); - -#endif /* NETEQ_CNG_CODEC */ - -/**************************************************************************** - * WebRtcNetEQ_BGNUpdate(...) - * - * This function updates the background noise parameter estimates. - * - * Input: - * - inst : NetEQ instance, where the speech history is stored. - * - scratchPtr : Pointer to scratch vector. - * - * Output: - * - inst : Updated information about the BGN characteristics. - * - * Return value : No return value - */ - -void WebRtcNetEQ_BGNUpdate( -#ifdef SCRATCH - DSPInst_t *inst, WebRtc_Word16 *pw16_scratchPtr -#else - DSPInst_t *inst -#endif - ); - -#ifdef NETEQ_VAD -/* Functions used by post-decode VAD */ - -/**************************************************************************** - * WebRtcNetEQ_InitVAD(...) - * - * Initializes post-decode VAD instance. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - fs : Initial sample rate - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_InitVAD(PostDecodeVAD_t *VADInst, WebRtc_UWord16 fs); - -/**************************************************************************** - * WebRtcNetEQ_SetVADModeInternal(...) - * - * Set the VAD mode in the VAD struct, and communicate it to the VAD instance - * if it exists. - * - * Input: - * - VADinst : PostDecodeVAD instance - * - mode : Mode number passed on to the VAD function - * - * Output: - * - VADinst : Updated instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADModeInternal(PostDecodeVAD_t *VADInst, WebRtc_Word16 mode); - -#endif /* NETEQ_VAD */ - -/**************************************************************************** - * WebRtcNetEQ_FlushSpeechBuffer(...) - * - * Flush the speech buffer. - * - * Input: - * - inst : NetEq DSP instance - * - * Output: - * - inst : Updated instance - * - * Return value : 0 - ok - * : non-zero - error - */ - -int WebRtcNetEQ_FlushSpeechBuffer(DSPInst_t *inst); - -#ifndef WEBRTC_NETEQ_40BITACC_TEST - -#include "signal_processing_library.h" -/* Map to regular SPL functions */ -#define WebRtcNetEQ_CrossCorr WebRtcSpl_CrossCorrelation -#define WebRtcNetEQ_DotW16W16 WebRtcSpl_DotProductWithScale - -#else /* WEBRTC_NETEQ_40BITACC_TEST defined */ -/* Run NetEQ with simulated 40-bit accumulator to run bit-exact to a DSP - implementation where the main (splib and NetEQ) functions have been - 40-bit optimized. */ - -/* Map to special 40-bit optimized functions, defined below */ -#define WebRtcNetEQ_CrossCorr WebRtcNetEQ_40BitAccCrossCorr -#define WebRtcNetEQ_DotW16W16 WebRtcNetEQ_40BitAccDotW16W16 - -/**************************************************************************** - * WebRtcNetEQ_40BitAccCrossCorr(...) - * - * Calculates the Cross correlation between two sequences seq1 and seq2. Seq1 - * is fixed and seq2 slides as the pointer is increased with step - * - * Input: - * - seq1 : First sequence (fixed throughout the correlation) - * - seq2 : Second sequence (slided step_seq2 for each - * new correlation) - * - dimSeq : Number of samples to use in the cross correlation. - * Should be no larger than 1024 to avoid overflow. - * - dimCrossCorr : Number of CrossCorrelations to calculate (start - * position for seq2 is updated for each new one) - * - rShift : Number of right shifts to use - * - step_seq2 : How many (positive or negative) steps the seq2 - * pointer should be updated for each new cross - * correlation value - * - * Output: - * - crossCorr : The cross correlation in Q-rShift - */ - -void WebRtcNetEQ_40BitAccCrossCorr(WebRtc_Word32 *crossCorr, WebRtc_Word16 *seq1, - WebRtc_Word16 *seq2, WebRtc_Word16 dimSeq, - WebRtc_Word16 dimCrossCorr, WebRtc_Word16 rShift, - WebRtc_Word16 step_seq2); - -/**************************************************************************** - * WebRtcNetEQ_40BitAccDotW16W16(...) - * - * Calculates the dot product between two vectors (WebRtc_Word16) - * - * Input: - * - vector1 : Vector 1 - * - vector2 : Vector 2 - * - len : Number of samples in vector - * Should be no larger than 1024 to avoid overflow. - * - scaling : The number of right shifts (after multiplication) - * required to avoid overflow in the dot product. - * Return value : The dot product - */ - -WebRtc_Word32 WebRtcNetEQ_40BitAccDotW16W16(WebRtc_Word16 *vector1, WebRtc_Word16 *vector2, - int len, int scaling); - -#endif /* WEBRTC_NETEQ_40BITACC_TEST */ - -#endif /* DSP_H */ diff --git a/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.c b/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.c deleted file mode 100644 index 6e9a28316..000000000 --- a/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains some help functions that did not fit elsewhere. - */ - -#include "dsp_helpfunctions.h" - - -WebRtc_Word16 WebRtcNetEQ_CalcFsMult(WebRtc_UWord16 fsHz) -{ - switch (fsHz) - { - case 8000: - { - return 1; - } - case 16000: - { - return 2; - } - case 32000: - { - return 4; - } - case 48000: - { - return 6; - } - default: - { - return 1; - } - } -} - - -int WebRtcNetEQ_DownSampleTo4kHz(const WebRtc_Word16 *in, int inLen, WebRtc_UWord16 inFsHz, - WebRtc_Word16 *out, int outLen, int compensateDelay) -{ - WebRtc_Word16 *B; /* filter coefficients */ - WebRtc_Word16 Blen; /* number of coefficients */ - WebRtc_Word16 filterDelay; /* phase delay in samples */ - WebRtc_Word16 factor; /* conversion rate (inFsHz/8000) */ - int ok; - - /* Set constants depending on frequency used */ - /* NOTE: The phase delay values are wrong compared to the true phase delay - of the filters. However, the error is preserved (through the +1 term) - for consistency. */ - switch (inFsHz) - { - case 8000: - { - Blen = 3; - factor = 2; - B = (WebRtc_Word16*) WebRtcNetEQ_kDownsample8kHzTbl; - filterDelay = 1 + 1; - break; - } -#ifdef NETEQ_WIDEBAND - case 16000: - { - Blen = 5; - factor = 4; - B = (WebRtc_Word16*) WebRtcNetEQ_kDownsample16kHzTbl; - filterDelay = 2 + 1; - break; - } -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - { - Blen = 7; - factor = 8; - B = (WebRtc_Word16*) WebRtcNetEQ_kDownsample32kHzTbl; - filterDelay = 3 + 1; - break; - } -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - { - Blen = 7; - factor = 12; - B = (WebRtc_Word16*) WebRtcNetEQ_kDownsample48kHzTbl; - filterDelay = 3 + 1; - break; - } -#endif - default: - { - /* unsupported or wrong sample rate */ - return -1; - } - } - - if (!compensateDelay) - { - /* disregard delay compensation */ - filterDelay = 0; - } - - ok = WebRtcSpl_DownsampleFast((WebRtc_Word16*) &in[Blen - 1], - (WebRtc_Word16) (inLen - (Blen - 1)), /* number of input samples */ - out, (WebRtc_Word16) outLen, /* number of output samples to produce */ - B, Blen, factor, filterDelay); /* filter parameters */ - - return ok; /* return value is -1 if input signal is too short */ - -} - diff --git a/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.h b/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.h deleted file mode 100644 index f728c0979..000000000 --- a/modules/audio_coding/NetEQ/main/source/dsp_helpfunctions.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Various help functions used by the DSP functions. - */ - -#ifndef DSP_HELPFUNCTIONS_H -#define DSP_HELPFUNCTIONS_H - -#include "typedefs.h" - -#include "dsp.h" - -/**************************************************************************** - * WebRtcNetEQ_Correlator(...) - * - * Calculate signal correlation. - * - * Input: - * - inst : DSP instance - * - data : Speech history to do expand from (older history in data[-4..-1]) - * - dataLen : Length of data - * - * Output: - * - corrOut : CC of downsampled signal - * - corrScale : Scale factor for correlation (-Qdomain) - * - * Return value : Length of correlated data - */ - -WebRtc_Word16 WebRtcNetEQ_Correlator(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_data, WebRtc_Word16 w16_dataLen, - WebRtc_Word16 *pw16_corrOut, - WebRtc_Word16 *pw16_corrScale); - -/**************************************************************************** - * WebRtcNetEQ_PeakDetection(...) - * - * Peak detection with parabolic fit. - * - * Input: - * - data : Data sequence for peak detection - * - dataLen : Length of data - * - nmbPeaks : Number of peaks to detect - * - fs_mult : Sample rate multiplier - * - * Output: - * - corrIndex : Index of the peak - * - winner : Value of the peak - * - * Return value : 0 for ok - */ - -WebRtc_Word16 WebRtcNetEQ_PeakDetection(WebRtc_Word16 *pw16_data, WebRtc_Word16 w16_dataLen, - WebRtc_Word16 w16_nmbPeaks, WebRtc_Word16 fs_mult, - WebRtc_Word16 *pw16_corrIndex, - WebRtc_Word16 *pw16_winners); - -/**************************************************************************** - * WebRtcNetEQ_PrblFit(...) - * - * Three-point parbola fit. - * - * Input: - * - 3pts : Three input samples - * - fs_mult : Sample rate multiplier - * - * Output: - * - Ind : Index of the peak - * - outVal : Value of the peak - * - * Return value : 0 for ok - */ - -WebRtc_Word16 WebRtcNetEQ_PrblFit(WebRtc_Word16 *pw16_3pts, WebRtc_Word16 *pw16_Ind, - WebRtc_Word16 *pw16_outVal, WebRtc_Word16 fs_mult); - -/**************************************************************************** - * WebRtcNetEQ_MinDistortion(...) - * - * Find the lag that results in minimum distortion. - * - * Input: - * - data : Start of speech to perform distortion on, second vector is assumed - * to be data[-Lag] - * - minLag : Start lag - * - maxLag : End lag - * - len : Length to correlate - * - * Output: - * - dist : Distorion value - * - * Return value : Lag for minimum distortion - */ - -WebRtc_Word16 WebRtcNetEQ_MinDistortion(const WebRtc_Word16 *pw16_data, - WebRtc_Word16 w16_minLag, WebRtc_Word16 w16_maxLag, - WebRtc_Word16 len, WebRtc_Word32 *pw16_dist); - -/**************************************************************************** - * WebRtcNetEQ_RandomVec(...) - * - * Generate random vector. - * - * Input: - * - seed : Current seed (input/output) - * - len : Number of samples to generate - * - incVal : Jump step - * - * Output: - * - randVec : Generated random vector - */ - -void WebRtcNetEQ_RandomVec(WebRtc_UWord32 *w32_seed, WebRtc_Word16 *pw16_randVec, - WebRtc_Word16 w16_len, WebRtc_Word16 w16_incval); - -/**************************************************************************** - * WebRtcNetEQ_MixVoiceUnvoice(...) - * - * Mix voiced and unvoiced signal. - * - * Input: - * - voicedVec : Voiced input signal - * - unvoicedVec : Unvoiced input signal - * - current_vfraction : Current mixing factor - * - vfraction_change : Mixing factor change per sample - * - N : Number of samples - * - * Output: - * - outData : Mixed signal - */ - -void WebRtcNetEQ_MixVoiceUnvoice(WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_voicedVec, - WebRtc_Word16 *pw16_unvoicedVec, - WebRtc_Word16 *w16_current_vfraction, - WebRtc_Word16 w16_vfraction_change, WebRtc_Word16 N); - -/**************************************************************************** - * WebRtcNetEQ_UnmuteSignal(...) - * - * Gradually reduce attenuation. - * - * Input: - * - inVec : Input signal - * - startMuteFact : Starting attenuation - * - unmuteFact : Factor to "unmute" with (Q20) - * - N : Number of samples - * - * Output: - * - outVec : Output signal - */ - -void WebRtcNetEQ_UnmuteSignal(WebRtc_Word16 *pw16_inVec, WebRtc_Word16 *startMuteFact, - WebRtc_Word16 *pw16_outVec, WebRtc_Word16 unmuteFact, - WebRtc_Word16 N); - -/**************************************************************************** - * WebRtcNetEQ_MuteSignal(...) - * - * Gradually increase attenuation. - * - * Input: - * - inout : Input/output signal - * - muteSlope : Slope of muting - * - N : Number of samples - */ - -void WebRtcNetEQ_MuteSignal(WebRtc_Word16 *pw16_inout, WebRtc_Word16 muteSlope, - WebRtc_Word16 N); - -/**************************************************************************** - * WebRtcNetEQ_CalcFsMult(...) - * - * Calculate the sample rate divided by 8000. - * - * Input: - * - fsHz : Sample rate in Hz in {8000, 16000, 32000, 48000}. - * - * Return value : fsHz/8000 for the valid values, 1 for other inputs - */ - -WebRtc_Word16 WebRtcNetEQ_CalcFsMult(WebRtc_UWord16 fsHz); - -/**************************************************************************** - * WebRtcNetEQ_DownSampleTo4kHz(...) - * - * Lowpass filter and downsample a signal to 4 kHz sample rate. - * - * Input: - * - in : Input signal samples. - * - inLen : Number of input samples. - * - inFsHz : Input sample rate in Hz. - * - outLen : Desired number of samples in decimated signal. - * - compensateDelay : If non-zero, compensate for the phase delay of - * of the anti-alias filter. - * - * Output: - * - out : Output signal samples. - * - * Return value : 0 - Ok - * -1 - Error - * - */ - -int WebRtcNetEQ_DownSampleTo4kHz(const WebRtc_Word16 *in, int inLen, WebRtc_UWord16 inFsHz, - WebRtc_Word16 *out, int outLen, int compensateDelay); - -#endif - diff --git a/modules/audio_coding/NetEQ/main/source/dtmf_buffer.c b/modules/audio_coding/NetEQ/main/source/dtmf_buffer.c deleted file mode 100644 index f00f9c994..000000000 --- a/modules/audio_coding/NetEQ/main/source/dtmf_buffer.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of packet buffer for DTMF messages. - */ - -#include "dtmf_buffer.h" - -#include "typedefs.h" /* to define endianness */ -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - - -#ifdef NETEQ_ATEVENT_DECODE - -WebRtc_Word16 WebRtcNetEQ_DtmfRemoveEvent(dtmf_inst_t *DTMFdec_inst) -{ - - int i; - for (i = 0; i < 3; i++) - { - DTMFdec_inst->EventQueue[i] = DTMFdec_inst->EventQueue[i + 1]; - DTMFdec_inst->EventQueueVolume[i] = DTMFdec_inst->EventQueueVolume[i + 1]; - DTMFdec_inst->EventQueueEnded[i] = DTMFdec_inst->EventQueueEnded[i + 1]; - DTMFdec_inst->EventQueueStartTime[i] = DTMFdec_inst->EventQueueStartTime[i + 1]; - DTMFdec_inst->EventQueueEndTime[i] = DTMFdec_inst->EventQueueEndTime[i + 1]; - } - DTMFdec_inst->EventBufferSize--; - DTMFdec_inst->EventQueue[3] = -1; - DTMFdec_inst->EventQueueVolume[3] = 0; - DTMFdec_inst->EventQueueEnded[3] = 0; - DTMFdec_inst->EventQueueStartTime[3] = 0; - DTMFdec_inst->EventQueueEndTime[3] = 0; - - return 0; -} - -WebRtc_Word16 WebRtcNetEQ_DtmfDecoderInit(dtmf_inst_t *DTMFdec_inst, WebRtc_UWord16 fs, - WebRtc_Word16 MaxPLCtime) -{ - int i; - if (((fs != 8000) && (fs != 16000) && (fs != 32000) && (fs != 48000)) || (MaxPLCtime < 0)) - { - return DTMF_DEC_PARAMETER_ERROR; - } - if (fs == 8000) - DTMFdec_inst->framelen = 80; - else if (fs == 16000) - DTMFdec_inst->framelen = 160; - else if (fs == 32000) - DTMFdec_inst->framelen = 320; - else - /* fs == 48000 */ - DTMFdec_inst->framelen = 480; - - DTMFdec_inst->MaxPLCtime = MaxPLCtime; - DTMFdec_inst->CurrentPLCtime = 0; - DTMFdec_inst->EventBufferSize = 0; - for (i = 0; i < 4; i++) - { - DTMFdec_inst->EventQueue[i] = -1; - DTMFdec_inst->EventQueueVolume[i] = 0; - DTMFdec_inst->EventQueueEnded[i] = 0; - DTMFdec_inst->EventQueueStartTime[i] = 0; - DTMFdec_inst->EventQueueEndTime[i] = 0; - } - return 0; -} - -WebRtc_Word16 WebRtcNetEQ_DtmfInsertEvent(dtmf_inst_t *DTMFdec_inst, - const WebRtc_Word16 *encoded, WebRtc_Word16 len, - WebRtc_UWord32 timeStamp) -{ - - int i; - WebRtc_Word16 value; - const WebRtc_Word16 *EventStart; - WebRtc_Word16 endEvent; - WebRtc_Word16 Volume; - WebRtc_Word16 Duration; - WebRtc_Word16 position = -1; - - /* Extract event */ - if (len == 4) - { - EventStart = encoded; -#ifdef WEBRTC_BIG_ENDIAN - value=((*EventStart)>>8); - endEvent=((*EventStart)&0x80)>>7; - Volume=((*EventStart)&0x3F); - Duration=EventStart[1]; -#else - value = ((*EventStart) & 0xFF); - endEvent = ((*EventStart) & 0x8000) >> 15; - Volume = ((*EventStart) & 0x3F00) >> 8; - Duration = (((((WebRtc_UWord16) EventStart[1]) >> 8) & 0xFF) - | (((WebRtc_UWord16) (EventStart[1] & 0xFF)) << 8)); -#endif - /* Only events between 0-15 are supported (DTMF tones) */ - if ((value < 0) || (value > 15)) - { - return 0; - } - - /* Discard all DTMF tones with really low volume (<-36dbm0) */ - if (Volume > 36) - { - return 0; - } - - /*Are there any unended events of the same type? */ - for (i = 0; i < DTMFdec_inst->EventBufferSize; i++) - { - /* Going through the whole queue even when we have found a match will - ensure that we add to the latest applicable event */ - if ((DTMFdec_inst->EventQueue[i] == value) && (!DTMFdec_inst->EventQueueEnded[i] - || endEvent)) position = i; - } - if (position > -1) - { - DTMFdec_inst->EventQueueVolume[position] = Volume; - if ((timeStamp + Duration) > DTMFdec_inst->EventQueueEndTime[position]) DTMFdec_inst->EventQueueEndTime[position] - = DTMFdec_inst->EventQueueStartTime[position] + Duration; - if (endEvent) DTMFdec_inst->EventQueueEnded[position] = 1; - } - else - { - if (DTMFdec_inst->EventBufferSize == MAX_DTMF_QUEUE_SIZE) - { /* Buffer full */ - /* Remove one event */ - DTMFdec_inst->EventBufferSize--; - } - /* Store data in the instance on a new position*/ - DTMFdec_inst->EventQueue[DTMFdec_inst->EventBufferSize] = value; - DTMFdec_inst->EventQueueVolume[DTMFdec_inst->EventBufferSize] = Volume; - DTMFdec_inst->EventQueueEnded[DTMFdec_inst->EventBufferSize] = endEvent; - DTMFdec_inst->EventQueueStartTime[DTMFdec_inst->EventBufferSize] = timeStamp; - DTMFdec_inst->EventQueueEndTime[DTMFdec_inst->EventBufferSize] = timeStamp - + Duration; - DTMFdec_inst->EventBufferSize++; - } - return 0; - } - return DTMF_INSERT_ERROR; -} - -WebRtc_Word16 WebRtcNetEQ_DtmfDecode(dtmf_inst_t *DTMFdec_inst, WebRtc_Word16 *event, - WebRtc_Word16 *volume, WebRtc_UWord32 currTimeStamp) -{ - - if (DTMFdec_inst->EventBufferSize < 1) return 0; /* No events to play */ - - /* We have events, is it time to play them? */ - if (currTimeStamp < DTMFdec_inst->EventQueueStartTime[0]) - { - /*No, just return zero */ - return 0; - } - - /* Continue on the event that is currently ongoing */ - *event = DTMFdec_inst->EventQueue[0]; - *volume = DTMFdec_inst->EventQueueVolume[0]; - - if (DTMFdec_inst->EventQueueEndTime[0] >= (currTimeStamp + DTMFdec_inst->framelen)) - { - - /* Still at least framLen to play */ - - DTMFdec_inst->CurrentPLCtime = 0; - if ((DTMFdec_inst->EventQueueEndTime[0] == (currTimeStamp + DTMFdec_inst->framelen)) - && (DTMFdec_inst->EventQueueEnded[0])) - { /* We are done */ - /*Remove the event from Queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - } - return DTMFdec_inst->framelen; - - } - else - { - if ((DTMFdec_inst->EventQueueEnded[0]) || (DTMFdec_inst->EventQueue[1] > -1)) - { - /* - * Less than frameLen to play and end of event or already received next event. - * Give our a whole frame size of audio to simplify things. - */ - - /*Remove the event from Queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - - return DTMFdec_inst->framelen; - - } - else - { - /* Less than frameLen to play and not end of event. */ - DTMFdec_inst->CurrentPLCtime = (WebRtc_Word16) (currTimeStamp - - DTMFdec_inst->EventQueueEndTime[0]); - - if ((DTMFdec_inst->CurrentPLCtime > DTMFdec_inst->MaxPLCtime) - || (DTMFdec_inst->CurrentPLCtime < -DTMFdec_inst->MaxPLCtime)) - { - /*Remove the event from queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - } - - /* If we have a new event that it's time to play */ - if ((DTMFdec_inst->EventQueue[1] > -1) && (DTMFdec_inst->EventQueueStartTime[1] - >= (currTimeStamp + DTMFdec_inst->framelen))) - { - /*Remove the event from queue*/ - WebRtcNetEQ_DtmfRemoveEvent(DTMFdec_inst); - DTMFdec_inst->CurrentPLCtime = 0; - } - - return DTMFdec_inst->framelen; - } - } -} - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/dtmf_buffer.h b/modules/audio_coding/NetEQ/main/source/dtmf_buffer.h deleted file mode 100644 index e185411e2..000000000 --- a/modules/audio_coding/NetEQ/main/source/dtmf_buffer.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Packet buffer for DTMF messages. - */ - -#ifndef DTMF_BUFFER_H -#define DTMF_BUFFER_H - -#include "typedefs.h" - -#include "neteq_defines.h" - -/* Include this code only if ATEVENT (DTMF) is defined in */ -#ifdef NETEQ_ATEVENT_DECODE - -#define MAX_DTMF_QUEUE_SIZE 4 - -typedef struct dtmf_inst_t_ -{ - WebRtc_Word16 MaxPLCtime; - WebRtc_Word16 CurrentPLCtime; - WebRtc_Word16 EventQueue[MAX_DTMF_QUEUE_SIZE]; - WebRtc_Word16 EventQueueVolume[MAX_DTMF_QUEUE_SIZE]; - WebRtc_Word16 EventQueueEnded[MAX_DTMF_QUEUE_SIZE]; - WebRtc_UWord32 EventQueueStartTime[MAX_DTMF_QUEUE_SIZE]; - WebRtc_UWord32 EventQueueEndTime[MAX_DTMF_QUEUE_SIZE]; - WebRtc_Word16 EventBufferSize; - WebRtc_Word16 framelen; -} dtmf_inst_t; - -/**************************************************************************** - * WebRtcNetEQ_DtmfDecoderInit(...) - * - * This function initializes a DTMF instance. - * - * Input: - * - DTMF_decinst_t : DTMF instance - * - fs : The sample rate used for the DTMF - * - MaxPLCtime : Maximum length for a PLC before zeros should be inserted - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcNetEQ_DtmfDecoderInit(dtmf_inst_t *DTMFdec_inst, WebRtc_UWord16 fs, - WebRtc_Word16 MaxPLCtime); - -/**************************************************************************** - * WebRtcNetEQ_DtmfInsertEvent(...) - * - * This function decodes a packet with DTMF frames. - * - * Input: - * - DTMFdec_inst : DTMF instance - * - encoded : Encoded DTMF frame(s) - * - len : Bytes in encoded vector - * - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcNetEQ_DtmfInsertEvent(dtmf_inst_t *DTMFdec_inst, - const WebRtc_Word16 *encoded, WebRtc_Word16 len, - WebRtc_UWord32 timeStamp); - -/**************************************************************************** - * WebRtcNetEQ_DtmfDecode(...) - * - * This function decodes a packet with DTMF frame(s). Output will be the - * event that should be played for next 10 ms. - * - * Input: - * - DTMFdec_inst : DTMF instance - * - currTimeStamp : The current playout timestamp - * - * Output: - * - event : Event number to be played - * - volume : Event volume to be played - * - * Return value : >0 - There is a event to be played - * 0 - No event to be played - * -1 - Error - */ - -WebRtc_Word16 WebRtcNetEQ_DtmfDecode(dtmf_inst_t *DTMFdec_inst, WebRtc_Word16 *event, - WebRtc_Word16 *volume, WebRtc_UWord32 currTimeStamp); - -#endif /* NETEQ_ATEVENT_DECODE */ - -#endif /* DTMF_BUFFER_H */ - diff --git a/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.c b/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.c deleted file mode 100644 index a52f9bc63..000000000 --- a/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the DTMF tone generator and its parameters. - * - * A sinusoid is generated using the recursive oscillator model - * - * y[n] = sin(w*n + phi) = 2*cos(w) * y[n-1] - y[n-2] - * = a * y[n-1] - y[n-2] - * - * initialized with - * y[-2] = 0 - * y[-1] = sin(w) - * - * A DTMF signal is a combination of two sinusoids, depending - * on which event is sent (i.e, which key is pressed). The following - * table maps each key (event codes in parentheses) into two tones: - * - * 1209 Hz 1336 Hz 1477 Hz 1633 Hz - * 697 Hz 1 (ev. 1) 2 (ev. 2) 3 (ev. 3) A (ev. 12) - * 770 Hz 4 (ev. 4) 5 (ev. 5) 6 (ev. 6) B (ev. 13) - * 852 Hz 7 (ev. 7) 8 (ev. 8) 9 (ev. 9) C (ev. 14) - * 941 Hz * (ev. 10) 0 (ev. 0) # (ev. 11) D (ev. 15) - * - * The two tones are added to form the DTMF signal. - * - */ - -#include "dtmf_tonegen.h" - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -#ifdef NETEQ_ATEVENT_DECODE -/* Must compile NetEQ with DTMF support to enable the functionality */ - -/*******************/ -/* Constant tables */ -/*******************/ - -/* - * All tables corresponding to the oscillator model are organized so that - * the coefficients for a specific frequency is found in the same position - * in every table. The positions for the tones follow this layout: - * - * dummyVector[8] = - * { - * 697 Hz, 770 Hz, 852 Hz, 941 Hz, - * 1209 Hz, 1336 Hz, 1477 Hz, 1633 Hz - * }; - */ - -/* - * Tables for the constant a = 2*cos(w) = 2*cos(2*pi*f/fs) - * in the oscillator model, for 8, 16, 32 and 48 kHz sample rate. - * Table values in Q14. - */ - -const WebRtc_Word16 WebRtcNetEQ_dtfm_aTbl8Khz[8] = -{ - 27980, 26956, 25701, 24219, - 19073, 16325, 13085, 9315 -}; - -#ifdef NETEQ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_aTbl16Khz[8]= -{ - 31548, 31281, 30951, 30556, - 29144, 28361, 27409, 26258 -}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_aTbl32Khz[8]= -{ - 32462, 32394, 32311, 32210, - 31849, 31647, 31400, 31098 -}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_aTbl48Khz[8]= -{ - 32632, 32602, 32564, 32520, - 32359, 32268, 32157, 32022 -}; -#endif - -/* - * Initialization values y[-1] = sin(w) = sin(2*pi*f/fs), for 8, 16, 32 and 48 kHz sample rate. - * Table values in Q14. - */ - -const WebRtc_Word16 WebRtcNetEQ_dtfm_yInitTab8Khz[8] = -{ - 8528, 9315, 10163, 11036, - 13323, 14206,15021, 15708 -}; - -#ifdef NETEQ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_yInitTab16Khz[8]= -{ - 4429, 4879, 5380, 5918, - 7490, 8207, 8979, 9801 -}; -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_yInitTab32Khz[8]= -{ - 2235, 2468, 2728, 3010, - 3853, 4249, 4685, 5164 -}; -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND -const WebRtc_Word16 WebRtcNetEQ_dtfm_yInitTab48Khz[8]= -{ - 1493, 1649, 1823, 2013, - 2582, 2851, 3148, 3476 -}; -#endif - -/* Volume in dBm0 from 0 to -63, where 0 is the first table entry. - Everything below -36 is discarded, wherefore the table stops at -36. - Table entries are in Q14. - */ - -const WebRtc_Word16 WebRtcNetEQ_dtfm_dBm0[37] = { 16141, 14386, 12821, 11427, 10184, 9077, 8090, - 7210, 6426, 5727, 5104, 4549, 4054, 3614, - 3221, 2870, 2558, 2280, 2032, 1811, 1614, - 1439, 1282, 1143, 1018, 908, 809, 721, 643, - 573, 510, 455, 405, 361, 322, 287, 256 }; - -/**************************************************************************** - * WebRtcNetEQ_DTMFGenerate(...) - * - * Generate 10 ms DTMF signal according to input parameters. - * - * Input: - * - DTMFdecInst : DTMF instance - * - value : DTMF event number (0-15) - * - volume : Volume of generated signal (0-36) - * Volume is given in negative dBm0, i.e., volume == 0 - * means 0 dBm0 while volume == 36 mean -36 dBm0. - * - sampFreq : Sample rate in Hz - * - * Output: - * - signal : Pointer to vector where DTMF signal is stored; - * Vector must be at least sampFreq/100 samples long. - * - DTMFdecInst : Updated DTMF instance - * - * Return value : >0 - Number of samples written to signal - * : <0 - error - */ - -WebRtc_Word16 WebRtcNetEQ_DTMFGenerate(dtmf_tone_inst_t *DTMFdecInst, WebRtc_Word16 value, - WebRtc_Word16 volume, WebRtc_Word16 *signal, - WebRtc_UWord16 sampFreq, WebRtc_Word16 extFrameLen) -{ - const WebRtc_Word16 *aTbl; /* pointer to a-coefficient table */ - const WebRtc_Word16 *yInitTable; /* pointer to initialization value table */ - WebRtc_Word16 a1 = 0; /* a-coefficient for first tone (low tone) */ - WebRtc_Word16 a2 = 0; /* a-coefficient for second tone (high tone) */ - int i; - int frameLen; /* number of samples to generate */ - int lowIndex; - int highIndex; - WebRtc_Word32 tempVal; - WebRtc_Word16 tempValLow; - WebRtc_Word16 tempValHigh; - - /* Sanity check for volume */ - if ((volume < 0) || (volume > 36)) - { - return DTMF_DEC_PARAMETER_ERROR; - } - - /* Sanity check for extFrameLen */ - if (extFrameLen < -1) - { - return DTMF_DEC_PARAMETER_ERROR; - } - - /* Select oscillator coefficient tables based on sample rate */ - if (sampFreq == 8000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl8Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab8Khz; - frameLen = 80; -#ifdef NETEQ_WIDEBAND - } - else if (sampFreq == 16000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl16Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab16Khz; - frameLen = 160; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (sampFreq == 32000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl32Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab32Khz; - frameLen = 320; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else if (sampFreq == 48000) - { - aTbl = WebRtcNetEQ_dtfm_aTbl48Khz; - yInitTable = WebRtcNetEQ_dtfm_yInitTab48Khz; - frameLen = 480; -#endif - } - else - { - /* unsupported sample rate */ - return DTMF_GEN_UNKNOWN_SAMP_FREQ; - } - - if (extFrameLen >= 0) - { - frameLen = extFrameLen; - } - - /* select low frequency based on event value */ - switch (value) - { - case 1: - case 2: - case 3: - case 12: /* first row on keypad */ - { - lowIndex = 0; /* low frequency: 697 Hz */ - break; - } - case 4: - case 5: - case 6: - case 13: /* second row on keypad */ - { - lowIndex = 1; /* low frequency: 770 Hz */ - break; - } - case 7: - case 8: - case 9: - case 14: /* third row on keypad */ - { - lowIndex = 2; /* low frequency: 852 Hz */ - break; - } - case 0: - case 10: - case 11: - case 15: /* fourth row on keypad */ - { - lowIndex = 3; /* low frequency: 941 Hz */ - break; - } - default: - { - return DTMF_DEC_PARAMETER_ERROR; - } - } /* end switch */ - - /* select high frequency based on event value */ - switch (value) - { - case 1: - case 4: - case 7: - case 10: /* first column on keypad */ - { - highIndex = 4; /* high frequency: 1209 Hz */ - break; - } - case 2: - case 5: - case 8: - case 0: /* second column on keypad */ - { - highIndex = 5;/* high frequency: 1336 Hz */ - break; - } - case 3: - case 6: - case 9: - case 11: /* third column on keypad */ - { - highIndex = 6;/* high frequency: 1477 Hz */ - break; - } - case 12: - case 13: - case 14: - case 15: /* fourth column on keypad (special) */ - { - highIndex = 7;/* high frequency: 1633 Hz */ - break; - } - default: - { - return DTMF_DEC_PARAMETER_ERROR; - } - } /* end switch */ - - /* select coefficients based on results from switches above */ - a1 = aTbl[lowIndex]; /* coefficient for first (low) tone */ - a2 = aTbl[highIndex]; /* coefficient for second (high) tone */ - - if (DTMFdecInst->reinit) - { - /* set initial values for the recursive model */ - DTMFdecInst->oldOutputLow[0] = yInitTable[lowIndex]; - DTMFdecInst->oldOutputLow[1] = 0; - DTMFdecInst->oldOutputHigh[0] = yInitTable[highIndex]; - DTMFdecInst->oldOutputHigh[1] = 0; - - /* reset reinit flag */ - DTMFdecInst->reinit = 0; - } - - /* generate signal sample by sample */ - for (i = 0; i < frameLen; i++) - { - - /* Use rescursion formula y[n] = a*y[n-1] - y[n-2] */ - tempValLow - = (WebRtc_Word16) (((WEBRTC_SPL_MUL_16_16(a1, DTMFdecInst->oldOutputLow[1]) - + 8192) >> 14) - DTMFdecInst->oldOutputLow[0]); - tempValHigh - = (WebRtc_Word16) (((WEBRTC_SPL_MUL_16_16(a2, DTMFdecInst->oldOutputHigh[1]) - + 8192) >> 14) - DTMFdecInst->oldOutputHigh[0]); - - /* Update recursion memory */ - DTMFdecInst->oldOutputLow[0] = DTMFdecInst->oldOutputLow[1]; - DTMFdecInst->oldOutputLow[1] = tempValLow; - DTMFdecInst->oldOutputHigh[0] = DTMFdecInst->oldOutputHigh[1]; - DTMFdecInst->oldOutputHigh[1] = tempValHigh; - - /* scale high tone with 32768 (15 left shifts) - and low tone with 23171 (3dB lower than high tone) */ - tempVal = WEBRTC_SPL_MUL_16_16(DTMF_AMP_LOW, tempValLow) - + WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)tempValHigh, 15); - - /* Norm the signal to Q14 (with proper rounding) */ - tempVal = (tempVal + 16384) >> 15; - - /* Scale the signal to correct dbM0 value */ - signal[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(tempVal, WebRtcNetEQ_dtfm_dBm0[volume]) - + 8192), 14); /* volume value is in Q14; use proper rounding */ - } - - return frameLen; - -} - -#endif /* NETEQ_ATEVENT_DECODE */ - diff --git a/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.h b/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.h deleted file mode 100644 index add6eb1d1..000000000 --- a/modules/audio_coding/NetEQ/main/source/dtmf_tonegen.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the DTMF tone generator function. - */ - -#ifndef DTMF_TONEGEN_H -#define DTMF_TONEGEN_H - -#include "typedefs.h" - -#include "neteq_defines.h" - -#ifdef NETEQ_ATEVENT_DECODE -/* Must compile NetEQ with DTMF support to enable the functionality */ - -#define DTMF_AMP_LOW 23171 /* 3 dB lower than the high frequency */ - -/* The DTMF generator struct (part of DSP main struct DSPInst_t) */ -typedef struct dtmf_tone_inst_t_ -{ - - WebRtc_Word16 reinit; /* non-zero if the oscillator model should - be reinitialized for next event */ - WebRtc_Word16 oldOutputLow[2]; /* oscillator recursion history (low tone) */ - WebRtc_Word16 oldOutputHigh[2]; /* oscillator recursion history (high tone) */ - - int lastDtmfSample; /* index to the first non-DTMF sample in the - speech history, if non-negative */ -}dtmf_tone_inst_t; - -/**************************************************************************** - * WebRtcNetEQ_DTMFGenerate(...) - * - * Generate 10 ms DTMF signal according to input parameters. - * - * Input: - * - DTMFdecInst : DTMF instance - * - value : DTMF event number (0-15) - * - volume : Volume of generated signal (0-36) - * Volume is given in negative dBm0, i.e., volume == 0 - * means 0 dBm0 while volume == 36 mean -36 dBm0. - * - sampFreq : Sample rate in Hz - * - * Output: - * - signal : Pointer to vector where DTMF signal is stored; - * Vector must be at least sampFreq/100 samples long. - * - DTMFdecInst : Updated DTMF instance - * - * Return value : >0 - Number of samples written to signal - * : <0 - Error - */ - -WebRtc_Word16 WebRtcNetEQ_DTMFGenerate(dtmf_tone_inst_t *DTMFdecInst, - WebRtc_Word16 value, - WebRtc_Word16 volume, - WebRtc_Word16 *signal, - WebRtc_UWord16 sampFreq, - WebRtc_Word16 frameLen -); - -#endif /* NETEQ_ATEVENT_DECODE */ - -#endif /* DTMF_TONEGEN_H */ - diff --git a/modules/audio_coding/NetEQ/main/source/expand.c b/modules/audio_coding/NetEQ/main/source/expand.c deleted file mode 100644 index 2520f6382..000000000 --- a/modules/audio_coding/NetEQ/main/source/expand.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the function to expand from the speech history, to produce concealment data or - * increasing delay. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define CHECK_NO_OF_CORRMAX 3 -#define DISTLEN 20 -#define LPCANALASYSLEN 160 - -/* Scratch usage: - - Type Name size startpos endpos - (First part of first expand) - WebRtc_Word16 pw16_bestCorrIndex 3 0 2 - WebRtc_Word16 pw16_bestCorr 3 3 5 - WebRtc_Word16 pw16_bestDistIndex 3 6 8 - WebRtc_Word16 pw16_bestDist 3 9 11 - WebRtc_Word16 pw16_corrVec 102*fs/8000 12 11+102*fs/8000 - func WebRtcNetEQ_Correlator 232 12+102*fs/8000 243+102*fs/8000 - - (Second part of first expand) - WebRtc_Word32 pw32_corr2 99*fs/8000+1 0 99*fs/8000 - WebRtc_Word32 pw32_autoCorr 2*7 0 13 - WebRtc_Word16 pw16_rc 6 14 19 - - Signal combination: - WebRtc_Word16 pw16_randVec 30+120*fs/8000 0 29+120*fs/8000 - WebRtc_Word16 pw16_scaledRandVec 125*fs/8000 30+120*fs/8000 29+245*fs/8000 - WebRtc_Word16 pw16_unvoicedVecSpace 10+125*fs/8000 30+245*fs/8000 39+370*fs/8000 - - Total: 40+370*fs/8000 (size depends on UNVOICED_LPC_ORDER and BGN_LPC_ORDER) - */ - -#if ((BGN_LPC_ORDER > 10) || (UNVOICED_LPC_ORDER > 10)) && (defined SCRATCH) -#error BGN_LPC_ORDER and/or BGN_LPC_ORDER are too large for current scratch memory allocation -#endif - -#define SCRATCH_PW16_BEST_CORR_INDEX 0 -#define SCRATCH_PW16_BEST_CORR 3 -#define SCRATCH_PW16_BEST_DIST_INDEX 6 -#define SCRATCH_PW16_BEST_DIST 9 -#define SCRATCH_PW16_CORR_VEC 12 -#define SCRATCH_PW16_CORR2 0 -#define SCRATCH_PW32_AUTO_CORR 0 -#define SCRATCH_PW16_RC 14 -#define SCRATCH_PW16_RAND_VEC 0 - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 624 -#define SCRATCH_PW16_SCALED_RAND_VEC 750 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 1500 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 420 -#define SCRATCH_PW16_SCALED_RAND_VEC 510 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 1010 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_NETEQDSP_CORRELATOR 216 -#define SCRATCH_PW16_SCALED_RAND_VEC 270 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 520 -#else /* NB */ -#define SCRATCH_NETEQDSP_CORRELATOR 114 -#define SCRATCH_PW16_SCALED_RAND_VEC 150 -#define SCRATCH_PW16_UNVOICED_VEC_SPACE 275 -#endif - -/**************************************************************************** - * WebRtcNetEQ_Expand(...) - * - * This function produces one "chunk" of expansion data (PLC audio). The - * length of the produced audio depends on the speech history. - * - * Input: - * - inst : DSP instance - * - scratchPtr : Pointer to scratch vector - * - outdata : Pointer to a memory space where the output data - * should be stored - * - BGNonly : If non-zero, "expand" will only produce background noise. - * - pw16_len : Desired number of samples (only for BGN mode). - * - * Output: - * - inst : Updated instance - * - pw16_len : Number of samples that were output from NetEq - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_Expand(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly) -{ - - WebRtc_Word16 fs_mult; - ExpandInst_t *ExpandState = &(inst->ExpandInst); - BGNInst_t *BGNState = &(inst->BGNInst); - int i; -#ifdef SCRATCH - WebRtc_Word16 *pw16_randVec = pw16_scratchPtr + SCRATCH_PW16_RAND_VEC; - WebRtc_Word16 *pw16_scaledRandVec = pw16_scratchPtr + SCRATCH_PW16_SCALED_RAND_VEC; - WebRtc_Word16 *pw16_unvoicedVecSpace = pw16_scratchPtr + SCRATCH_PW16_UNVOICED_VEC_SPACE; -#else - WebRtc_Word16 pw16_randVec[FSMULT * 120 + 30]; /* 125 for NB and 250 for WB */ - WebRtc_Word16 pw16_scaledRandVec[FSMULT * 125]; /* 125 for NB and 250 for WB */ - WebRtc_Word16 pw16_unvoicedVecSpace[BGN_LPC_ORDER + FSMULT * 125]; -#endif - /* 125 for NB and 250 for WB etc. Reuse pw16_outData[] for this vector */ - WebRtc_Word16 *pw16_voicedVecStorage = pw16_outData; - WebRtc_Word16 *pw16_voicedVec = &pw16_voicedVecStorage[ExpandState->w16_overlap]; - WebRtc_Word16 *pw16_unvoicedVec = pw16_unvoicedVecSpace + UNVOICED_LPC_ORDER; - WebRtc_Word16 *pw16_cngVec = pw16_unvoicedVecSpace + BGN_LPC_ORDER; - WebRtc_Word16 w16_expVecsLen, w16_lag = 0, w16_expVecPos; - WebRtc_Word16 w16_randLen; - WebRtc_Word16 w16_vfractionChange; /* in Q14 */ - WebRtc_Word16 w16_winMute = 0, w16_winMuteInc = 0, w16_winUnMute = 0, w16_winUnMuteInc = 0; - WebRtc_Word32 w32_tmp; - WebRtc_Word16 w16_tmp, w16_tmp2; - WebRtc_Word16 stability; - enum BGNMode bgnMode = inst->BGNInst.bgnMode; - - /* Pre-calculate common multiplications with fs_mult */ - WebRtc_Word16 fsMult4; - WebRtc_Word16 fsMult20; - WebRtc_Word16 fsMult120; - WebRtc_Word16 fsMultDistLen; - WebRtc_Word16 fsMultLPCAnalasysLen; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - /* fs is WebRtc_UWord16 (to hold fs=48000) */ - fs_mult = WebRtcNetEQ_CalcFsMult(inst->fs); /* calculate fs/8000 */ - - /* Pre-calculate common multiplications with fs_mult */ - fsMult4 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fs_mult, 4); - fsMult20 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fs_mult, 20); - fsMult120 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fs_mult, 120); - fsMultDistLen = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fs_mult, DISTLEN); - fsMultLPCAnalasysLen = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fs_mult, LPCANALASYSLEN); - - /* - * Perform all the initial setup if it's the first expansion. - * If background noise (BGN) only, this setup is not needed. - */ - if (ExpandState->w16_consecExp == 0 && !BGNonly) - { - /* Setup more variables */ -#ifdef SCRATCH - WebRtc_Word32 *pw32_autoCorr = (WebRtc_Word32*) (pw16_scratchPtr - + SCRATCH_PW32_AUTO_CORR); - WebRtc_Word16 *pw16_rc = pw16_scratchPtr + SCRATCH_PW16_RC; - WebRtc_Word16 *pw16_bestCorrIndex = pw16_scratchPtr + SCRATCH_PW16_BEST_CORR_INDEX; - WebRtc_Word16 *pw16_bestCorr = pw16_scratchPtr + SCRATCH_PW16_BEST_CORR; - WebRtc_Word16 *pw16_bestDistIndex = pw16_scratchPtr + SCRATCH_PW16_BEST_DIST_INDEX; - WebRtc_Word16 *pw16_bestDist = pw16_scratchPtr + SCRATCH_PW16_BEST_DIST; - WebRtc_Word16 *pw16_corrVec = pw16_scratchPtr + SCRATCH_PW16_CORR_VEC; - WebRtc_Word32 *pw32_corr2 = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_PW16_CORR2); -#else - WebRtc_Word32 pw32_autoCorr[UNVOICED_LPC_ORDER+1]; - WebRtc_Word16 pw16_rc[UNVOICED_LPC_ORDER]; - WebRtc_Word16 pw16_corrVec[FSMULT*102]; /* 102 for NB */ - WebRtc_Word16 pw16_bestCorrIndex[CHECK_NO_OF_CORRMAX]; - WebRtc_Word16 pw16_bestCorr[CHECK_NO_OF_CORRMAX]; - WebRtc_Word16 pw16_bestDistIndex[CHECK_NO_OF_CORRMAX]; - WebRtc_Word16 pw16_bestDist[CHECK_NO_OF_CORRMAX]; - WebRtc_Word32 pw32_corr2[(99*FSMULT)+1]; -#endif - WebRtc_Word32 pw32_bestDist[CHECK_NO_OF_CORRMAX]; - WebRtc_Word16 w16_ind = 0; - WebRtc_Word16 w16_corrVecLen; - WebRtc_Word16 w16_corrScale; - WebRtc_Word16 w16_distScale; - WebRtc_Word16 w16_indMin, w16_indMax; - WebRtc_Word16 w16_len; - WebRtc_Word32 w32_en1, w32_en2, w32_cc; - WebRtc_Word16 w16_en1Scale, w16_en2Scale; - WebRtc_Word16 w16_en1, w16_en2; - WebRtc_Word32 w32_en1_mul_en2; - WebRtc_Word16 w16_sqrt_en1en2; - WebRtc_Word16 w16_ccShiftL; - WebRtc_Word16 w16_bestcorr; /* Correlation in Q14 */ - WebRtc_Word16 *pw16_vec1, *pw16_vec2; - WebRtc_Word16 w16_factor; - WebRtc_Word16 w16_DistLag, w16_CorrLag, w16_diffLag; - WebRtc_Word16 w16_energyLen; - WebRtc_Word16 w16_slope; - WebRtc_Word16 w16_startInd; - WebRtc_Word16 w16_noOfcorr2; - WebRtc_Word16 w16_scale; - - /* Initialize some variables */ - ExpandState->w16_lagsDirection = 1; - ExpandState->w16_lagsPosition = -1; - ExpandState->w16_expandMuteFactor = 16384; /* Start from 1.0 (Q14) */ - BGNState->w16_mutefactor = 0; /* Start with 0 gain for BGN (value in Q14) */ - inst->w16_seedInc = 1; - -#ifdef NETEQ_STEREO - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - /* - * Do not calculate correlations for slave instance(s) - * unless lag info from master is corrupt - */ - if ((msInfo->msMode != NETEQ_SLAVE) - || ((msInfo->distLag <= 0) || (msInfo->corrLag <= 0))) - { -#endif - /* Calculate correlation vector in downsampled domain (4 kHz sample rate) */ - w16_corrVecLen = WebRtcNetEQ_Correlator(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQDSP_CORRELATOR, -#endif - inst->pw16_speechHistory, inst->w16_speechHistoryLen, pw16_corrVec, - &w16_corrScale); - - /* Find peaks in correlation vector using parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corrVec, w16_corrVecLen, CHECK_NO_OF_CORRMAX, fs_mult, - pw16_bestCorrIndex, pw16_bestCorr); - - /* - * Adjust peak locations; cross-correlation lags start at 2.5 ms - * (20*fs_mult samples) - */ - pw16_bestCorrIndex[0] += fsMult20; - pw16_bestCorrIndex[1] += fsMult20; - pw16_bestCorrIndex[2] += fsMult20; - - /* Calculate distortion around the 3 (CHECK_NO_OF_CORRMAX) best lags */ - w16_distScale = 0; - for (i = 0; i < CHECK_NO_OF_CORRMAX; i++) - { - w16_tmp = fsMult20; - w16_tmp2 = pw16_bestCorrIndex[i] - fsMult4; - w16_indMin = WEBRTC_SPL_MAX(w16_tmp, w16_tmp2); - w16_tmp = fsMult120 - 1; - w16_tmp2 = pw16_bestCorrIndex[i] + fsMult4; - w16_indMax = WEBRTC_SPL_MIN(w16_tmp, w16_tmp2); - - pw16_bestDistIndex[i] = WebRtcNetEQ_MinDistortion( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - fsMultDistLen]), - w16_indMin, w16_indMax, fsMultDistLen, &pw32_bestDist[i]); - - w16_distScale - = WEBRTC_SPL_MAX(16 - WebRtcSpl_NormW32(pw32_bestDist[i]), w16_distScale); - - } - - /* Shift the distortion values to fit in WebRtc_Word16 */ - WebRtcSpl_VectorBitShiftW32ToW16(pw16_bestDist, CHECK_NO_OF_CORRMAX, pw32_bestDist, - w16_distScale); - - /* - * Find index of maximum criteria, where crit[i] = bestCorr[i])/(bestDist[i]) - * Do this by a cross multiplication. - */ - - w32_en1 = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[0],pw16_bestDist[1]); - w32_en2 = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[1],pw16_bestDist[0]); - if (w32_en1 >= w32_en2) - { - /* 0 wins over 1 */ - w32_en1 - = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[0], pw16_bestDist[2]); - w32_en2 - = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[2], pw16_bestDist[0]); - if (w32_en1 >= w32_en2) - { - /* 0 wins over 2 */ - w16_ind = 0; - } - else - { - /* 2 wins over 0 */ - w16_ind = 2; - } - } - else - { - /* 1 wins over 0 */ - w32_en1 - = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[1],pw16_bestDist[2]); - w32_en2 - = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) pw16_bestCorr[2],pw16_bestDist[1]); - if ((WebRtc_Word32) w32_en1 >= (WebRtc_Word32) w32_en2) - { - /* 1 wins over 2 */ - w16_ind = 1; - } - else - { - /* 2 wins over 1 */ - w16_ind = 2; - } - } - -#ifdef NETEQ_STEREO - } - - /* Store DistLag and CorrLag of the position with highest criteria */ - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO) - || ((msInfo->msMode == NETEQ_SLAVE) && (msInfo->distLag <= 0 || msInfo->corrLag - <= 0))) - { - /* lags not provided externally */ - w16_DistLag = pw16_bestDistIndex[w16_ind]; - w16_CorrLag = pw16_bestCorrIndex[w16_ind]; - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->distLag = w16_DistLag; - msInfo->corrLag = w16_CorrLag; - } - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* lags provided externally (from master) */ - w16_DistLag = msInfo->distLag; - w16_CorrLag = msInfo->corrLag; - - /* sanity for lag values */ - if ((w16_DistLag <= 0) || (w16_CorrLag <= 0)) - { - return MASTER_SLAVE_ERROR; - } - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } -#else /* not NETEQ_STEREO */ - w16_DistLag = pw16_bestDistIndex[w16_ind]; - w16_CorrLag = pw16_bestCorrIndex[w16_ind]; -#endif - - ExpandState->w16_maxLag = WEBRTC_SPL_MAX(w16_DistLag, w16_CorrLag); - - /* Calculate the exact best correlation (in the range within CorrLag-DistLag) */ - w16_len = w16_DistLag + 10; - w16_len = WEBRTC_SPL_MIN(w16_len, fsMult120); - w16_len = WEBRTC_SPL_MAX(w16_len, 60 * fs_mult); - - w16_startInd = WEBRTC_SPL_MIN(w16_DistLag, w16_CorrLag); - w16_noOfcorr2 = WEBRTC_SPL_ABS_W16((w16_DistLag-w16_CorrLag)) + 1; - /* w16_noOfcorr2 maximum value is 99*fs_mult + 1 */ - - /* Calculate suitable scaling */ - w16_tmp - = WebRtcSpl_MaxAbsValueW16( - &inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_startInd - - w16_noOfcorr2], - (WebRtc_Word16) (w16_len + w16_startInd + w16_noOfcorr2 - 1)); - w16_corrScale = ((31 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_tmp, w16_tmp))) - + (31 - WebRtcSpl_NormW32(w16_len))) - 31; - w16_corrScale = WEBRTC_SPL_MAX(0, w16_corrScale); - - /* - * Perform the correlation, store in pw32_corr2 - */ - - WebRtcNetEQ_CrossCorr(pw32_corr2, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_startInd]), - w16_len, w16_noOfcorr2, w16_corrScale, -1); - - /* Find maximizing index */ - w16_ind = WebRtcSpl_MaxIndexW32(pw32_corr2, w16_noOfcorr2); - w32_cc = pw32_corr2[w16_ind]; /* this is maximum correlation */ - w16_ind = w16_ind + w16_startInd; /* correct index for start offset */ - - /* Calculate energies */ - w32_en1 = WebRtcNetEQ_DotW16W16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len]), w16_len, - w16_corrScale); - w32_en2 = WebRtcNetEQ_DotW16W16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_ind]), - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_len - w16_ind]), - w16_len, w16_corrScale); - - /* Calculate the correlation value w16_bestcorr */ - if ((w32_en1 > 0) && (w32_en2 > 0)) - { - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - /* if sum is odd */ - w16_en1Scale += 1; - } - w16_en1 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - w32_en1_mul_en2 = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - w16_sqrt_en1en2 = (WebRtc_Word16) WebRtcSpl_Sqrt(w32_en1_mul_en2); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_ccShiftL = 14 - ((w16_en1Scale + w16_en2Scale) >> 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_ccShiftL); - w16_bestcorr = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_cc, w16_sqrt_en1en2); - w16_bestcorr = WEBRTC_SPL_MIN(16384, w16_bestcorr); /* set maximum to 1.0 */ - - } - else - { - /* if either en1 or en2 is zero */ - w16_bestcorr = 0; - } - - /* - * Extract the two vectors, pw16_expVecs[0][] and pw16_expVecs[1][], - * from the SpeechHistory[] - */ - w16_expVecsLen = ExpandState->w16_maxLag + ExpandState->w16_overlap; - pw16_vec1 = &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - w16_expVecsLen]); - pw16_vec2 = pw16_vec1 - w16_DistLag; - /* Normalize the second vector to the same energy as the first */ - w32_en1 = WebRtcNetEQ_DotW16W16(pw16_vec1, pw16_vec1, w16_expVecsLen, w16_corrScale); - w32_en2 = WebRtcNetEQ_DotW16W16(pw16_vec2, pw16_vec2, w16_expVecsLen, w16_corrScale); - - /* - * Confirm that energy factor sqrt(w32_en1/w32_en2) is within difference 0.5 - 2.0 - * w32_en1/w32_en2 within 0.25 - 4 - */ - if (((w32_en1 >> 2) < w32_en2) && ((w32_en1) > (w32_en2 >> 2))) - { - - /* Energy constraint fulfilled => use both vectors and scale them accordingly */ - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - w16_en1Scale = w16_en2Scale - 13; - - /* calculate w32_en1/w32_en2 in Q13 */ - w32_en1_mul_en2 = WebRtcSpl_DivW32W16( - WEBRTC_SPL_SHIFT_W32(w32_en1, -w16_en1Scale), - (WebRtc_Word16) (WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale))); - - /* calculate factor in Q13 (sqrt of en1/en2 in Q26) */ - w16_factor = (WebRtc_Word16) WebRtcSpl_Sqrt( - WEBRTC_SPL_LSHIFT_W32(w32_en1_mul_en2, 13)); - - /* Copy the two vectors and give them the same energy */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[0], pw16_vec1, w16_expVecsLen); - WebRtcSpl_AffineTransformVector(ExpandState->pw16_expVecs[1], pw16_vec2, - w16_factor, 4096, 13, w16_expVecsLen); - - } - else - { - /* Energy change constraint not fulfilled => only use last vector */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[0], pw16_vec1, w16_expVecsLen); - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_expVecs[1], ExpandState->pw16_expVecs[0], - w16_expVecsLen); - - /* Set the w16_factor since it is used by muting slope */ - if (((w32_en1 >> 2) < w32_en2) || (w32_en2 == 0)) - { - w16_factor = 4096; /* 0.5 in Q13*/ - } - else - { - w16_factor = 16384; /* 2.0 in Q13*/ - } - } - - /* Set the 3 lag values */ - w16_diffLag = w16_DistLag - w16_CorrLag; - if (w16_diffLag == 0) - { - /* DistLag and CorrLag are equal */ - ExpandState->w16_lags[0] = w16_DistLag; - ExpandState->w16_lags[1] = w16_DistLag; - ExpandState->w16_lags[2] = w16_DistLag; - } - else - { - /* DistLag and CorrLag are not equal; use different combinations of the two */ - ExpandState->w16_lags[0] = w16_DistLag; /* DistLag only */ - ExpandState->w16_lags[1] = ((w16_DistLag + w16_CorrLag) >> 1); /* 50/50 */ - /* Third lag, move one half-step towards CorrLag (in both cases) */ - if (w16_diffLag > 0) - { - ExpandState->w16_lags[2] = (w16_DistLag + w16_CorrLag - 1) >> 1; - } - else - { - ExpandState->w16_lags[2] = (w16_DistLag + w16_CorrLag + 1) >> 1; - } - } - - /************************************************* - * Calculate the LPC and the gain of the filters * - *************************************************/ - - /* Calculate scale value needed for autocorrelation */ - w16_tmp = WebRtcSpl_MaxAbsValueW16( - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - fsMultLPCAnalasysLen]), - fsMultLPCAnalasysLen); - - w16_tmp = 16 - WebRtcSpl_NormW32(w16_tmp); - w16_tmp = WEBRTC_SPL_MIN(w16_tmp,0); - w16_tmp = (w16_tmp << 1) + 7; - w16_tmp = WEBRTC_SPL_MAX(w16_tmp,0); - - /* set w16_ind to simplify the following expressions */ - w16_ind = inst->w16_speechHistoryLen - fsMultLPCAnalasysLen - UNVOICED_LPC_ORDER; - - /* store first UNVOICED_LPC_ORDER samples in pw16_rc */ - - WEBRTC_SPL_MEMCPY_W16(pw16_rc, &inst->pw16_speechHistory[w16_ind], UNVOICED_LPC_ORDER); - - /* set first samples to zero */ - WebRtcSpl_MemSetW16(&inst->pw16_speechHistory[w16_ind], 0, UNVOICED_LPC_ORDER); - - /* Calculate UNVOICED_LPC_ORDER+1 lags of the ACF */ - - WebRtcNetEQ_CrossCorr( - pw32_autoCorr, &(inst->pw16_speechHistory[w16_ind + UNVOICED_LPC_ORDER]), - &(inst->pw16_speechHistory[w16_ind + UNVOICED_LPC_ORDER]), fsMultLPCAnalasysLen, - UNVOICED_LPC_ORDER + 1, w16_tmp, -1); - - /* Recover the stored samples from pw16_rc */ - - WEBRTC_SPL_MEMCPY_W16(&inst->pw16_speechHistory[w16_ind], pw16_rc, UNVOICED_LPC_ORDER); - - if (pw32_autoCorr[0] > 0) - { /* check that variance is positive */ - - /* estimate AR filter parameters using Levinson-Durbin algorithm - (UNVOICED_LPC_ORDER+1 filter coefficients) */ - stability = WebRtcSpl_LevinsonDurbin(pw32_autoCorr, ExpandState->pw16_arFilter, - pw16_rc, UNVOICED_LPC_ORDER); - - /* Only update BGN if filter is stable */ - if (stability != 1) - { - /* Set first coefficient to 4096 (1.0 in Q12)*/ - ExpandState->pw16_arFilter[0] = 4096; - /* Set remaining UNVOICED_LPC_ORDER coefficients to zero */ - WebRtcSpl_MemSetW16(ExpandState->pw16_arFilter + 1, 0, UNVOICED_LPC_ORDER); - } - - } - - if (w16_DistLag < 40) - { - w16_energyLen = 2 * w16_DistLag; - } - else - { - w16_energyLen = w16_DistLag; - } - w16_randLen = w16_energyLen + 30; /* Startup part */ - - /* Extract a noise segment */ - if (w16_randLen <= RANDVEC_NO_OF_SAMPLES) - { - WEBRTC_SPL_MEMCPY_W16(pw16_randVec, - (WebRtc_Word16*) WebRtcNetEQ_kRandnTbl, w16_randLen); - } - else - { /* only applies to SWB where length could be larger than 256 */ - WEBRTC_SPL_MEMCPY_W16(pw16_randVec, (WebRtc_Word16*) WebRtcNetEQ_kRandnTbl, - RANDVEC_NO_OF_SAMPLES); - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, &pw16_randVec[RANDVEC_NO_OF_SAMPLES], - (WebRtc_Word16) (w16_randLen - RANDVEC_NO_OF_SAMPLES), inst->w16_seedInc); - } - - /* Set up state vector and calculate scale factor for unvoiced filtering */ - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_arState, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - WEBRTC_SPL_MEMCPY_W16(pw16_unvoicedVec - UNVOICED_LPC_ORDER, - &(inst->pw16_speechHistory[inst->w16_speechHistoryLen - 128 - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - WebRtcSpl_FilterMAFastQ12(&inst->pw16_speechHistory[inst->w16_speechHistoryLen - 128], - pw16_unvoicedVec, ExpandState->pw16_arFilter, UNVOICED_LPC_ORDER + 1, 128); - if (WebRtcSpl_MaxAbsValueW16(pw16_unvoicedVec, 128) > 4000) - { - w16_scale = 4; - } - else - { - w16_scale = 0; - } - w32_tmp = WebRtcNetEQ_DotW16W16(pw16_unvoicedVec, pw16_unvoicedVec, 128, w16_scale); - - /* Normalize w32_tmp to 28 or 29 bits to preserve sqrt() accuracy */ - w16_tmp = WebRtcSpl_NormW32(w32_tmp) - 3; - w16_tmp += ((w16_tmp & 0x1) ^ 0x1); /* Make sure we do an odd number of shifts since we - from earlier have 7 shifts from dividing with 128.*/ - w32_tmp = WEBRTC_SPL_SHIFT_W32(w32_tmp, w16_tmp); - w32_tmp = WebRtcSpl_Sqrt(w32_tmp); - ExpandState->w16_arGainScale = 13 + ((w16_tmp + 7 - w16_scale) >> 1); - ExpandState->w16_arGain = (WebRtc_Word16) w32_tmp; - - /******************************************************************** - * Calculate vfraction from bestcorr * - * if (bestcorr>0.480665) * - * vfraction = ((bestcorr-0.4)/(1-0.4)).^2 * - * else vfraction = 0 * - * * - * approximation (coefficients in Q12): * - * if (x>0.480665) (y(x)<0.3) * - * y(x) = -1.264421 + 4.8659148*x - 4.0092827*x^2 + 1.4100529*x^3 * - * else y(x) = 0; * - ********************************************************************/ - - if (w16_bestcorr > 7875) - { - /* if x>0.480665 */ - WebRtc_Word16 w16_x1, w16_x2, w16_x3; - w16_x1 = w16_bestcorr; - w32_tmp = WEBRTC_SPL_MUL_16_16((WebRtc_Word32) w16_x1, w16_x1); - w16_x2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 14); - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_x1, w16_x2); - w16_x3 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 14); - w32_tmp - = (WebRtc_Word32) WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) WebRtcNetEQ_kMixFractionFuncTbl[0], 14); - w32_tmp - += (WebRtc_Word32) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[1], w16_x1); - w32_tmp - += (WebRtc_Word32) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[2], w16_x2); - w32_tmp - += (WebRtc_Word32) WEBRTC_SPL_MUL_16_16(WebRtcNetEQ_kMixFractionFuncTbl[3], w16_x3); - ExpandState->w16_vFraction = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 12); - ExpandState->w16_vFraction = WEBRTC_SPL_MIN(ExpandState->w16_vFraction, 16384); - ExpandState->w16_vFraction = WEBRTC_SPL_MAX(ExpandState->w16_vFraction, 0); - } - else - { - ExpandState->w16_vFraction = 0; - } - - /*********************************************************************** - * Calculate muting slope, reuse value from earlier scaling of ExpVecs * - ***********************************************************************/ - w16_slope = w16_factor; - - if (w16_slope > 12288) - { - /* w16_slope > 1.5 ? */ - /* Calculate (1-(1/slope))/w16_DistLag = (slope-1)/(w16_DistLag*slope) */ - w32_tmp = w16_slope - 8192; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 12); /* Value in Q25 (13+12=25) */ - w16_tmp = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(w16_DistLag, - w16_slope, 8); /* Value in Q5 (13-8=5) */ - w16_tmp = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_tmp, - w16_tmp); /* Res in Q20 (25-5=20) */ - - if (w16_slope > 14746) - { /* w16_slope > 1.8 ? */ - ExpandState->w16_muteSlope = (w16_tmp + 1) >> 1; - } - else - { - ExpandState->w16_muteSlope = (w16_tmp + 4) >> 3; - } - ExpandState->w16_onset = 1; - } - else if (ExpandState->w16_vFraction > 13107) - { - /* w16_vFraction > 0.8 ? */ - if (w16_slope > 8028) - { - /* w16_vFraction > 0.98 ? */ - ExpandState->w16_muteSlope = 0; - } - else - { - /* Calculate (1-slope)/w16_DistLag */ - w32_tmp = 8192 - w16_slope; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 7); /* Value in Q20 (13+7=20) */ - ExpandState->w16_muteSlope = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_tmp, - w16_DistLag); /* Res in Q20 (20-0=20) */ - } - ExpandState->w16_onset = 0; - } - else - { - /* - * Use the minimum of 0.005 (0.9 on 50 samples in NB and the slope) - * and ((1-slope)/w16_DistLag) - */ - w32_tmp = 8192 - w16_slope; - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, 7); /* Value in Q20 (13+7=20) */ - w32_tmp = WEBRTC_SPL_MAX(w32_tmp, 0); - ExpandState->w16_muteSlope = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_tmp, - w16_DistLag); /* Res in Q20 (20-0=20) */ - w16_tmp = WebRtcNetEQ_k5243div[fs_mult]; /* 0.005/fs_mult = 5243/fs_mult */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(w16_tmp, ExpandState->w16_muteSlope); - ExpandState->w16_onset = 0; - } - } - else - { - /* This is not the first Expansion, parameters are already estimated. */ - - /* Extract a noise segment */ - if (BGNonly) /* If we should produce nothing but background noise */ - { - if (*pw16_len > 0) - { - /* - * Set length to input parameter length, but not more than length - * of pw16_randVec - */ - w16_lag = WEBRTC_SPL_MIN(*pw16_len, FSMULT * 120 + 30); - } - else - { - /* set length to 15 ms */ - w16_lag = fsMult120; - } - w16_randLen = w16_lag; - } - else - { - w16_randLen = ExpandState->w16_maxLag; - } - - if (w16_randLen <= RANDVEC_NO_OF_SAMPLES) - { - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, pw16_randVec, w16_randLen, - inst->w16_seedInc); - } - else - { /* only applies to SWB where length could be larger than 256 */ - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, pw16_randVec, RANDVEC_NO_OF_SAMPLES, - inst->w16_seedInc); - inst->w16_seedInc = (inst->w16_seedInc + 2) & (RANDVEC_NO_OF_SAMPLES - 1); - WebRtcNetEQ_RandomVec(&inst->uw16_seed, &pw16_randVec[RANDVEC_NO_OF_SAMPLES], - (WebRtc_Word16) (w16_randLen - RANDVEC_NO_OF_SAMPLES), inst->w16_seedInc); - } - } /* end if(first expand or BGNonly) ... else ... */ - - if (!BGNonly) /* Voiced and unvoiced parts not used if generating BGN only */ - { - - /************************************************* - * Generate signal * - *************************************************/ - - /* - * Voiced part - */ - - /* Linearly mute the use_vfraction value from 1 to vfraction */ - if (ExpandState->w16_consecExp == 0) - { - ExpandState->w16_currentVFraction = 16384; /* 1.0 in Q14 */ - } - - ExpandState->w16_lagsPosition = ExpandState->w16_lagsPosition - + ExpandState->w16_lagsDirection; - - /* Change direction if needed */ - if (ExpandState->w16_lagsPosition == 0) - { - ExpandState->w16_lagsDirection = 1; - } - if (ExpandState->w16_lagsPosition == 2) - { - ExpandState->w16_lagsDirection = -1; - } - - /* Generate a weighted vector with the selected lag */ - w16_expVecsLen = ExpandState->w16_maxLag + ExpandState->w16_overlap; - w16_lag = ExpandState->w16_lags[ExpandState->w16_lagsPosition]; - /* Copy lag+overlap data */ - w16_expVecPos = w16_expVecsLen - w16_lag - ExpandState->w16_overlap; - w16_tmp = w16_lag + ExpandState->w16_overlap; - if (ExpandState->w16_lagsPosition == 0) - { - WEBRTC_SPL_MEMCPY_W16(pw16_voicedVecStorage, - &(ExpandState->pw16_expVecs[0][w16_expVecPos]), w16_tmp); - } - else if (ExpandState->w16_lagsPosition == 1) - { - WebRtcSpl_ScaleAndAddVectorsWithRound(&ExpandState->pw16_expVecs[0][w16_expVecPos], 3, - &ExpandState->pw16_expVecs[1][w16_expVecPos], 1, 2, pw16_voicedVecStorage, - w16_tmp); - - } - else if (ExpandState->w16_lagsPosition == 2) - { - WebRtcSpl_ScaleAndAddVectorsWithRound(&ExpandState->pw16_expVecs[0][w16_expVecPos], 1, - &ExpandState->pw16_expVecs[1][w16_expVecPos], 1, 1, pw16_voicedVecStorage, - w16_tmp); - } - - if (inst->fs == 8000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_8KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_8KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_8KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_8KHZ_INC; -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs == 16000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_16KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_16KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_16KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_16KHZ_INC; -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs == 32000) - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_32KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_32KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_32KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_32KHZ_INC; -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if (inst->fs==48000) */ - { - /* Windowing in Q15 */ - w16_winMute = NETEQ_OVERLAP_WINMUTE_48KHZ_START; - w16_winMuteInc = NETEQ_OVERLAP_WINMUTE_48KHZ_INC; - w16_winUnMute = NETEQ_OVERLAP_WINUNMUTE_48KHZ_START; - w16_winUnMuteInc = NETEQ_OVERLAP_WINUNMUTE_48KHZ_INC; -#endif - } - - /* Smooth the expanded if it has not been muted to or vfraction is larger than 0.5 */ - if ((ExpandState->w16_expandMuteFactor > 819) && (ExpandState->w16_currentVFraction - > 8192)) - { - for (i = 0; i < ExpandState->w16_overlap; i++) - { - /* Do overlap add between new vector and overlap */ - ExpandState->pw16_overlapVec[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16(ExpandState->pw16_overlapVec[i], w16_winMute) + - WEBRTC_SPL_MUL_16_16( - WEBRTC_SPL_MUL_16_16_RSFT(ExpandState->w16_expandMuteFactor, - pw16_voicedVecStorage[i], 14), w16_winUnMute) + 16384, 15); - w16_winMute += w16_winMuteInc; - w16_winUnMute += w16_winUnMuteInc; - } - } - else if (ExpandState->w16_expandMuteFactor == 0 -#ifdef NETEQ_STEREO - && msInfo->msMode == NETEQ_MONO /* only if mono mode is selected */ -#endif - ) - { - /* if ExpandState->w16_expandMuteFactor = 0 => all is CNG component - set the output length to 15ms (for best CNG production) */ - w16_tmp = fsMult120; - ExpandState->w16_maxLag = w16_tmp; - ExpandState->w16_lags[0] = w16_tmp; - ExpandState->w16_lags[1] = w16_tmp; - ExpandState->w16_lags[2] = w16_tmp; - } - - /* - * Unvoiced part - */ - - WEBRTC_SPL_MEMCPY_W16(pw16_unvoicedVec - UNVOICED_LPC_ORDER, - ExpandState->pw16_arState, - UNVOICED_LPC_ORDER); - if (ExpandState->w16_arGainScale > 0) - { - w32_tmp = ((WebRtc_Word32) 1) << (ExpandState->w16_arGainScale - 1); - } - else - { - w32_tmp = 0; - } - - /* Note that shift value can be >16 which complicates things for some DSPs */ - WebRtcSpl_AffineTransformVector(pw16_scaledRandVec, pw16_randVec, - ExpandState->w16_arGain, w32_tmp, ExpandState->w16_arGainScale, w16_lag); - - WebRtcSpl_FilterARFastQ12(pw16_scaledRandVec, pw16_unvoicedVec, - ExpandState->pw16_arFilter, UNVOICED_LPC_ORDER + 1, w16_lag); - - WEBRTC_SPL_MEMCPY_W16(ExpandState->pw16_arState, - &(pw16_unvoicedVec[w16_lag - UNVOICED_LPC_ORDER]), - UNVOICED_LPC_ORDER); - - /* - * Voiced + Unvoiced - */ - - /* For lag = - <=31*fs_mult => go from 1 to 0 in about 8 ms - (>=31..<=63)*fs_mult => go from 1 to 0 in about 16 ms - >=64*fs_mult => go from 1 to 0 in about 32 ms - */ - w16_tmp = (31 - WebRtcSpl_NormW32(ExpandState->w16_maxLag)) - 5; /* getbits(w16_maxLag) -5 */ - w16_vfractionChange = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(256, w16_tmp); - if (ExpandState->w16_stopMuting == 1) - { - w16_vfractionChange = 0; - } - - /* Create combined signal (unmuted) by shifting in more and more of unvoiced part */ - w16_tmp = 8 - w16_tmp; /* getbits(w16_vfractionChange) */ - w16_tmp = (ExpandState->w16_currentVFraction - ExpandState->w16_vFraction) >> w16_tmp; - w16_tmp = WEBRTC_SPL_MIN(w16_tmp, w16_lag); - WebRtcNetEQ_MixVoiceUnvoice(pw16_outData, pw16_voicedVec, pw16_unvoicedVec, - &ExpandState->w16_currentVFraction, w16_vfractionChange, w16_tmp); - - if (w16_tmp < w16_lag) - { - if (w16_vfractionChange != 0) - { - ExpandState->w16_currentVFraction = ExpandState->w16_vFraction; - } - w16_tmp2 = 16384 - ExpandState->w16_currentVFraction; - WebRtcSpl_ScaleAndAddVectorsWithRound(pw16_voicedVec + w16_tmp, - ExpandState->w16_currentVFraction, pw16_unvoicedVec + w16_tmp, w16_tmp2, 14, - pw16_outData + w16_tmp, (WebRtc_Word16) (w16_lag - w16_tmp)); - } - - /* Select muting factor */ - if (ExpandState->w16_consecExp == 3) - { - /* 0.95 on 50 samples in NB (0.0010/fs_mult in Q20) */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(ExpandState->w16_muteSlope, - WebRtcNetEQ_k1049div[fs_mult]); - } - if (ExpandState->w16_consecExp == 7) - { - /* 0.90 on 50 samples in NB (0.0020/fs_mult in Q20) */ - ExpandState->w16_muteSlope = WEBRTC_SPL_MAX(ExpandState->w16_muteSlope, - WebRtcNetEQ_k2097div[fs_mult]); - } - - /* Mute segment according to slope value */ - if ((ExpandState->w16_consecExp != 0) || (ExpandState->w16_onset != 1)) - { - /* Mute to the previous level, then continue with the muting */ - WebRtcSpl_AffineTransformVector(pw16_outData, pw16_outData, - ExpandState->w16_expandMuteFactor, 8192, 14, w16_lag); - - if ((ExpandState->w16_stopMuting != 1)) - { - WebRtcNetEQ_MuteSignal(pw16_outData, ExpandState->w16_muteSlope, w16_lag); - - w16_tmp = 16384 - (WebRtc_Word16) ((WEBRTC_SPL_MUL_16_16(w16_lag, - ExpandState->w16_muteSlope) + 8192) >> 6); /* 20-14 = 6 */ - w16_tmp = (WebRtc_Word16) ((WEBRTC_SPL_MUL_16_16(w16_tmp, - ExpandState->w16_expandMuteFactor) + 8192) >> 14); - - /* Guard against getting stuck with very small (but sometimes audible) gain */ - if ((ExpandState->w16_consecExp > 3) && (w16_tmp - >= ExpandState->w16_expandMuteFactor)) - { - ExpandState->w16_expandMuteFactor = 0; - } - else - { - ExpandState->w16_expandMuteFactor = w16_tmp; - } - } - } - - } /* end if(!BGNonly) */ - - /* - * BGN - */ - - if (BGNState->w16_initialized == 1) - { - /* BGN parameters are initialized; use them */ - - WEBRTC_SPL_MEMCPY_W16(pw16_cngVec - BGN_LPC_ORDER, - BGNState->pw16_filterState, - BGN_LPC_ORDER); - - if (BGNState->w16_scaleShift > 1) - { - w32_tmp = ((WebRtc_Word32) 1) << (BGNState->w16_scaleShift - 1); - } - else - { - w32_tmp = 0; - } - - /* Scale random vector to correct energy level */ - /* Note that shift value can be >16 which complicates things for some DSPs */ - WebRtcSpl_AffineTransformVector(pw16_scaledRandVec, pw16_randVec, - BGNState->w16_scale, w32_tmp, BGNState->w16_scaleShift, w16_lag); - - WebRtcSpl_FilterARFastQ12(pw16_scaledRandVec, pw16_cngVec, BGNState->pw16_filter, - BGN_LPC_ORDER + 1, w16_lag); - - WEBRTC_SPL_MEMCPY_W16(BGNState->pw16_filterState, - &(pw16_cngVec[w16_lag-BGN_LPC_ORDER]), - BGN_LPC_ORDER); - - /* Unmute the insertion of background noise */ - - if (bgnMode == BGN_FADE && ExpandState->w16_consecExp >= FADE_BGN_TIME - && BGNState->w16_mutefactor > 0) - { - /* fade BGN to zero */ - /* calculate muting slope, approx 2^18/fsHz */ - WebRtc_Word16 muteFactor; - if (fs_mult == 1) - { - muteFactor = -32; - } - else if (fs_mult == 2) - { - muteFactor = -16; - } - else if (fs_mult == 4) - { - muteFactor = -8; - } - else - { - muteFactor = -5; - } - /* use UnmuteSignal function with negative slope */ - WebRtcNetEQ_UnmuteSignal(pw16_cngVec, &BGNState->w16_mutefactor, /* In Q14 */ - pw16_cngVec, muteFactor, /* In Q20 */ - w16_lag); - } - else if (BGNState->w16_mutefactor < 16384 && !BGNonly) - { - /* if (w16_mutefactor < 1) and not BGN only (since then we use no muting) */ - - /* - * If BGN_OFF, or if BNG_FADE has started fading, - * mutefactor should not be increased. - */ - if (ExpandState->w16_stopMuting != 1 && bgnMode != BGN_OFF && !(bgnMode - == BGN_FADE && ExpandState->w16_consecExp >= FADE_BGN_TIME)) - { - WebRtcNetEQ_UnmuteSignal(pw16_cngVec, &BGNState->w16_mutefactor, /* In Q14 */ - pw16_cngVec, ExpandState->w16_muteSlope, /* In Q20 */ - w16_lag); - } - else - { - /* BGN_ON and stop muting, or - * BGN_OFF (mute factor is always 0), or - * BGN_FADE has reached 0 */ - WebRtcSpl_AffineTransformVector(pw16_cngVec, pw16_cngVec, - BGNState->w16_mutefactor, 8192, 14, w16_lag); - } - } - } - else - { - /* BGN parameters have not been initialized; use zero noise */ - WebRtcSpl_MemSetW16(pw16_cngVec, 0, w16_lag); - } - - if (BGNonly) - { - /* Copy BGN to outdata */ - for (i = 0; i < w16_lag; i++) - { - pw16_outData[i] = pw16_cngVec[i]; - } - } - else - { - /* Add CNG vector to the Voiced + Unvoiced vectors */ - for (i = 0; i < w16_lag; i++) - { - pw16_outData[i] = pw16_outData[i] + pw16_cngVec[i]; - } - - /* increase call number */ - ExpandState->w16_consecExp = ExpandState->w16_consecExp + 1; - if (ExpandState->w16_consecExp < 0) /* Guard against overflow */ - ExpandState->w16_consecExp = FADE_BGN_TIME; /* "Arbitrary" large num of expands */ - } - - inst->w16_mode = MODE_EXPAND; - *pw16_len = w16_lag; - - /* Update in-call and post-call statistics */ - if (ExpandState->w16_stopMuting != 1 || BGNonly) - { - /* - * Only do this if StopMuting != 1 or if explicitly BGNonly, otherwise Expand is - * called from Merge or Normal and special measures must be taken. - */ - inst->statInst.expandLength += (WebRtc_UWord32) *pw16_len; - if (ExpandState->w16_expandMuteFactor == 0 || BGNonly) - { - /* Only noise expansion */ - inst->statInst.expandedNoiseSamples += *pw16_len; - } - else - { - /* Voice expand (note: not necessarily _voiced_) */ - inst->statInst.expandedVoiceSamples += *pw16_len; - } - } - - return 0; -} - -/**************************************************************************** - * WebRtcNetEQ_GenerateBGN(...) - * - * This function generates and writes len samples of background noise to the - * output vector. The Expand function will be called repeatedly until the - * correct number of samples is produced. - * - * Input: - * - inst : NetEq instance, i.e. the user that requests more - * speech/audio data - * - scratchPtr : Pointer to scratch vector - * - len : Desired length of produced BGN. - * - * - * Output: - * - pw16_outData : Pointer to a memory space where the output data - * should be stored - * - * Return value : >=0 - Number of noise samples produced and written - * to output - * -1 - Error - */ - -int WebRtcNetEQ_GenerateBGN(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_outData, WebRtc_Word16 len) -{ - - WebRtc_Word16 pos = 0; - WebRtc_Word16 tempLen = len; - - while (tempLen > 0) - { - /* while we still need more noise samples, call Expand to obtain background noise */ - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr, -#endif - &pw16_outData[pos], &tempLen, 1 /*BGNonly*/); - - pos += tempLen; /* we got this many samples */ - tempLen = len - pos; /* this is the number of samples we still need */ - } - - return pos; -} - -#undef SCRATCH_PW16_BEST_CORR_INDEX -#undef SCRATCH_PW16_BEST_CORR -#undef SCRATCH_PW16_BEST_DIST_INDEX -#undef SCRATCH_PW16_BEST_DIST -#undef SCRATCH_PW16_CORR_VEC -#undef SCRATCH_PW16_CORR2 -#undef SCRATCH_PW32_AUTO_CORR -#undef SCRATCH_PW16_RC -#undef SCRATCH_PW16_RAND_VEC -#undef SCRATCH_NETEQDSP_CORRELATOR -#undef SCRATCH_PW16_SCALED_RAND_VEC -#undef SCRATCH_PW16_UNVOICED_VEC_SPACE - diff --git a/modules/audio_coding/NetEQ/main/source/mcu.h b/modules/audio_coding/NetEQ/main/source/mcu.h deleted file mode 100644 index 2e759aa04..000000000 --- a/modules/audio_coding/NetEQ/main/source/mcu.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * MCU struct and functions related to the MCU side operations. - */ - -#ifndef MCU_H -#define MCU_H - -#include "typedefs.h" - -#include "codec_db.h" -#include "rtcp.h" -#include "packet_buffer.h" -#include "buffer_stats.h" -#include "neteq_statistics.h" - -#ifdef NETEQ_ATEVENT_DECODE -#include "dtmf_buffer.h" -#endif - -#define MAX_ONE_DESC 5 /* cannot do more than this many consecutive one-descriptor decodings */ -#define MAX_LOSS_REPORT_PERIOD 60 /* number of seconds between auto-reset */ - -enum TsScaling -{ - kTSnoScaling = 0, - kTSscalingTwo, - kTSscalingTwoThirds, - kTSscalingFourThirds -}; - -typedef struct -{ - - WebRtc_Word16 current_Codec; - WebRtc_Word16 current_Payload; - WebRtc_UWord32 timeStamp; /* Next timestamp that should be played */ - WebRtc_Word16 millisecondsPerCall; - WebRtc_UWord16 timestampsPerCall; /* Output chunk size */ - WebRtc_UWord16 fs; - WebRtc_UWord32 ssrc; /* Current ssrc */ - WebRtc_Word16 new_codec; - WebRtc_Word16 first_packet; - - /* MCU/DSP Communication layer */ - WebRtc_Word16 *pw16_readAddress; - WebRtc_Word16 *pw16_writeAddress; - void *main_inst; - - CodecDbInst_t codec_DB_inst; /* Information about all the codecs, i.e. which - functions to use and which codpoints that - have been assigned */ - SplitInfo_t PayloadSplit_inst; /* Information about how the current codec - payload should be splitted */ - WebRtcNetEQ_RTCP_t RTCP_inst; /* RTCP statistics */ - PacketBuf_t PacketBuffer_inst; /* The packet buffer */ - BufstatsInst_t BufferStat_inst; /* Statistics that are used to make decision - for what the DSP should perform */ -#ifdef NETEQ_ATEVENT_DECODE - dtmf_inst_t DTMF_inst; -#endif - int NoOfExpandCalls; - WebRtc_Word16 AVT_PlayoutOn; - enum WebRtcNetEQPlayoutMode NetEqPlayoutMode; - - WebRtc_Word16 one_desc; /* Number of times running on one desc */ - - WebRtc_UWord32 lostTS; /* Number of timestamps lost */ - WebRtc_UWord32 lastReportTS; /* Timestamp elapsed since last report was given */ - - WebRtc_UWord32 externalTS; - WebRtc_UWord32 internalTS; - WebRtc_Word16 TSscalingInitialized; - enum TsScaling scalingFactor; - - MCUStats_t statInst; - -#ifdef NETEQ_STEREO - int usingStereo; -#endif - -} MCUInst_t; - -/**************************************************************************** - * WebRtcNetEQ_McuReset(...) - * - * Reset the MCU instance. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuReset(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ResetMcuInCallStats(...) - * - * Reset MCU-side statistics variables for the in-call statistics. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_ResetMcuJitterStat(...) - * - * Reset MCU-side statistics variables for the post-call statistics. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_ResetMcuJitterStat(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_McuAddressInit(...) - * - * Initializes MCU with read address and write address. - * - * Input: - * - inst : MCU instance - * - Data2McuAddress : Pointer to MCU address - * - Data2DspAddress : Pointer to DSP address - * - main_inst : Pointer to NetEQ main instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuAddressInit(MCUInst_t *inst, void * Data2McuAddress, - void * Data2DspAddress, void *main_inst); - -/**************************************************************************** - * WebRtcNetEQ_McuSetFs(...) - * - * Initializes MCU with read address and write address. - * - * Input: - * - inst : MCU instance - * - fs_hz : Sample rate in Hz -- 8000, 16000, 32000, (48000) - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_McuSetFs(MCUInst_t *inst, WebRtc_UWord16 fs_hz); - -/**************************************************************************** - * WebRtcNetEQ_SignalMcu(...) - * - * Signal the MCU that data is available and ask for a RecOut decision. - * - * Input: - * - inst : MCU instance - * - * Return value : 0 - Ok - * <0 - Error - */ -int WebRtcNetEQ_SignalMcu(MCUInst_t *inst); - -/**************************************************************************** - * WebRtcNetEQ_RecInInternal(...) - * - * This function inserts a packet into the jitter buffer. - * - * Input: - * - MCU_inst : MCU instance - * - RTPpacket : The RTP packet, parsed into NetEQ's internal RTP struct - * - uw32_timeRec : Time stamp for the arrival of the packet (not RTP timestamp) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecInInternal(MCUInst_t *MCU_inst, RTPPacket_t *RTPpacket, - WebRtc_UWord32 uw32_timeRec); - -/**************************************************************************** - * WebRtcNetEQ_RecInInternal(...) - * - * Split the packet according to split_inst and inserts the parts into - * Buffer_inst. - * - * Input: - * - MCU_inst : MCU instance - * - RTPpacket : The RTP packet, parsed into NetEQ's internal RTP struct - * - uw32_timeRec : Time stamp for the arrival of the packet (not RTP timestamp) - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_SplitAndInsertPayload(RTPPacket_t *packet, PacketBuf_t *Buffer_inst, - SplitInfo_t *split_inst, WebRtc_Word16 *flushed); - -/**************************************************************************** - * WebRtcNetEQ_GetTimestampScaling(...) - * - * Update information about timestamp scaling for a payload type - * in MCU_inst->scalingFactor. - * - * Input: - * - MCU_inst : MCU instance - * - rtpPayloadType : RTP payload number - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType); - -/**************************************************************************** - * WebRtcNetEQ_ScaleTimestampExternalToInternal(...) - * - * Convert from external to internal timestamp using current scaling info. - * - * Input: - * - MCU_inst : MCU instance - * - externalTS : External timestamp - * - * Return value : Internal timestamp - */ - -WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampExternalToInternal(const MCUInst_t *MCU_inst, - WebRtc_UWord32 externalTS); - -/**************************************************************************** - * WebRtcNetEQ_ScaleTimestampInternalToExternal(...) - * - * Convert from external to internal timestamp using current scaling info. - * - * Input: - * - MCU_inst : MCU instance - * - externalTS : Internal timestamp - * - * Return value : External timestamp - */ - -WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampInternalToExternal(const MCUInst_t *MCU_inst, - WebRtc_UWord32 internalTS); -#endif diff --git a/modules/audio_coding/NetEQ/main/source/mcu_address_init.c b/modules/audio_coding/NetEQ/main/source/mcu_address_init.c deleted file mode 100644 index 0306a853c..000000000 --- a/modules/audio_coding/NetEQ/main/source/mcu_address_init.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "mcu.h" - -#include /* to define NULL */ - -/* - * Initializes MCU with read address and write address - */ -int WebRtcNetEQ_McuAddressInit(MCUInst_t *inst, void * Data2McuAddress, - void * Data2DspAddress, void *main_inst) -{ - - inst->pw16_readAddress = (WebRtc_Word16*) Data2McuAddress; - inst->pw16_writeAddress = (WebRtc_Word16*) Data2DspAddress; - inst->main_inst = main_inst; - - inst->millisecondsPerCall = 10; - - /* Do expansions in the beginning */ - if (inst->pw16_writeAddress != NULL) inst->pw16_writeAddress[0] = DSP_INSTR_EXPAND; - - return (0); -} - diff --git a/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.c b/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.c deleted file mode 100644 index 13025d43e..000000000 --- a/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Communication between MCU and DSP sides. - */ - -#include "mcu_dsp_common.h" - -#include - -/* Initialize instances with read and write address */ -int WebRtcNetEQ_DSPinit(MainInst_t *inst) -{ - int res = 0; - - res |= WebRtcNetEQ_AddressInit(&inst->DSPinst, NULL, NULL, inst); - res |= WebRtcNetEQ_McuAddressInit(&inst->MCUinst, NULL, NULL, inst); - - return res; - -} - -/* The DSP side will call this function to interrupt the MCU side */ -int WebRtcNetEQ_DSP2MCUinterrupt(MainInst_t *inst, WebRtc_Word16 *pw16_shared_mem) -{ - inst->MCUinst.pw16_readAddress = pw16_shared_mem; - inst->MCUinst.pw16_writeAddress = pw16_shared_mem; - return WebRtcNetEQ_SignalMcu(&inst->MCUinst); -} diff --git a/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.h b/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.h deleted file mode 100644 index e3f42137e..000000000 --- a/modules/audio_coding/NetEQ/main/source/mcu_dsp_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * The main NetEQ instance, which is where the DSP and MCU sides join. - */ - -#ifndef MCU_DSP_COMMON_H -#define MCU_DSP_COMMON_H - -#include "typedefs.h" - -#include "dsp.h" -#include "mcu.h" - -/* Define size of shared memory area. */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define SHARED_MEM_SIZE (6*640) -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define SHARED_MEM_SIZE (4*640) -#elif defined(NETEQ_WIDEBAND) - #define SHARED_MEM_SIZE (2*640) -#else - #define SHARED_MEM_SIZE 640 -#endif - -/* Struct to hold the NetEQ instance */ -typedef struct -{ - DSPInst_t DSPinst; /* DSP part of the NetEQ instance */ - MCUInst_t MCUinst; /* MCU part of the NetEQ instance */ - WebRtc_Word16 ErrorCode; /* Store last error code */ -#ifdef NETEQ_STEREO - WebRtc_Word16 masterSlave; /* 0 = not set, 1 = master, 2 = slave */ -#endif /* NETEQ_STEREO */ -} MainInst_t; - -/* Struct used for communication between DSP and MCU sides of NetEQ */ -typedef struct -{ - WebRtc_UWord32 playedOutTS; /* Timestamp position at end of DSP data */ - WebRtc_UWord16 samplesLeft; /* Number of samples stored */ - WebRtc_Word16 MD; /* Multiple description codec information */ - WebRtc_Word16 lastMode; /* Latest mode of NetEQ playout */ - WebRtc_Word16 frameLen; /* Frame length of previously decoded packet */ -} DSP2MCU_info_t; - -/* Initialize instances with read and write address */ -int WebRtcNetEQ_DSPinit(MainInst_t *inst); - -/* The DSP side will call this function to interrupt the MCU side */ -int WebRtcNetEQ_DSP2MCUinterrupt(MainInst_t *inst, WebRtc_Word16 *pw16_shared_mem); - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/mcu_reset.c b/modules/audio_coding/NetEQ/main/source/mcu_reset.c deleted file mode 100644 index ef97296cf..000000000 --- a/modules/audio_coding/NetEQ/main/source/mcu_reset.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Reset MCU side data. - */ - -#include "mcu.h" - -#include - -#include "automode.h" - -int WebRtcNetEQ_McuReset(MCUInst_t *inst) -{ - -#ifdef NETEQ_ATEVENT_DECODE - int ok; -#endif - - /* MCU/DSP Communication layer */ - inst->pw16_readAddress = NULL; - inst->pw16_writeAddress = NULL; - inst->main_inst = NULL; - inst->one_desc = 0; - inst->BufferStat_inst.Automode_inst.extraDelayMs = 0; - inst->NetEqPlayoutMode = kPlayoutOn; - - WebRtcNetEQ_DbReset(&inst->codec_DB_inst); - memset(&inst->PayloadSplit_inst, 0, sizeof(SplitInfo_t)); - - /* Clear the Packet buffer and the pointer to memory storage */ - WebRtcNetEQ_PacketBufferFlush(&inst->PacketBuffer_inst); - inst->PacketBuffer_inst.memorySizeW16 = 0; - inst->PacketBuffer_inst.maxInsertPositions = 0; - - /* Clear the decision and delay history */ - memset(&inst->BufferStat_inst, 0, sizeof(BufstatsInst_t)); -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 8000, 560); - if (ok != 0) - { - return ok; - } -#endif - inst->NoOfExpandCalls = 0; - inst->current_Codec = -1; - inst->current_Payload = -1; - - inst->millisecondsPerCall = 10; - inst->timestampsPerCall = inst->millisecondsPerCall * 8; - inst->fs = 8000; - inst->first_packet = 1; - - WebRtcNetEQ_ResetMcuInCallStats(inst); - - WebRtcNetEQ_ResetMcuJitterStat(inst); - - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - - return 0; -} - -/* - * Reset MCU-side statistics variables for the in-call statistics. - */ - -int WebRtcNetEQ_ResetMcuInCallStats(MCUInst_t *inst) -{ - inst->lostTS = 0; - inst->lastReportTS = 0; - inst->PacketBuffer_inst.discardedPackets = 0; - - return 0; -} - -/* - * Reset all MCU-side statistics variables for the post-call statistics. - */ - -int WebRtcNetEQ_ResetMcuJitterStat(MCUInst_t *inst) -{ - inst->statInst.jbAvgCount = 0; - inst->statInst.jbAvgSizeQ16 = 0; - inst->statInst.jbMaxSize = 0; - inst->statInst.jbMinSize = 0xFFFFFFFF; - inst->statInst.avgPacketCount = 0; - inst->statInst.avgPacketDelayMs = 0; - inst->statInst.minPacketDelayMs = 0xFFFFFFFF; - inst->statInst.maxPacketDelayMs = 0; - inst->statInst.jbChangeCount = 0; - inst->statInst.generatedSilentMs = 0; - inst->statInst.countExpandMoreThan120ms = 0; - inst->statInst.countExpandMoreThan250ms = 0; - inst->statInst.countExpandMoreThan500ms = 0; - inst->statInst.countExpandMoreThan2000ms = 0; - inst->statInst.longestExpandDurationMs = 0; - inst->statInst.accelerateMs = 0; - - inst->PacketBuffer_inst.totalDiscardedPackets = 0; - inst->PacketBuffer_inst.totalFlushedPackets = 0; - - inst->BufferStat_inst.Automode_inst.countIAT500ms = 0; - inst->BufferStat_inst.Automode_inst.countIAT1000ms = 0; - inst->BufferStat_inst.Automode_inst.countIAT2000ms = 0; - inst->BufferStat_inst.Automode_inst.longestIATms = 0; - - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/merge.c b/modules/audio_coding/NetEQ/main/source/merge.c deleted file mode 100644 index 28e31c4dc..000000000 --- a/modules/audio_coding/NetEQ/main/source/merge.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This is the function to merge a new packet with expanded data after a packet loss. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -/**************************************************************************** - * WebRtcNetEQ_Merge(...) - * - * This function... - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to new decoded speech. - * - len : Number of samples in pw16_decoded. - * - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Number of samples written to pw16_outData - * - * Return value : 0 - Ok - * <0 - Error - */ - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_expanded 210*fs/8000 0 209*fs/8000 - WebRtc_Word16 pw16_expandedLB 100 210*fs/8000 99+210*fs/8000 - WebRtc_Word16 pw16_decodedLB 40 100+210*fs/8000 139+210*fs/8000 - WebRtc_Word32 pw32_corr 2*60 140+210*fs/8000 260+210*fs/8000 - WebRtc_Word16 pw16_corrVec 68 210*fs/8000 67+210*fs/8000 - - [gap in scratch vector] - - func WebRtcNetEQ_Expand 40+370*fs/8000 126*fs/8000 39+496*fs/8000 - - Total: 40+496*fs/8000 - */ - -#define SCRATCH_pw16_expanded 0 -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 1260 -#define SCRATCH_pw16_decodedLB 1360 -#define SCRATCH_pw32_corr 1400 -#define SCRATCH_pw16_corrVec 1260 -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 840 -#define SCRATCH_pw16_decodedLB 940 -#define SCRATCH_pw32_corr 980 -#define SCRATCH_pw16_corrVec 840 -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_pw16_expandedLB 420 -#define SCRATCH_pw16_decodedLB 520 -#define SCRATCH_pw32_corr 560 -#define SCRATCH_pw16_corrVec 420 -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_pw16_expandedLB 210 -#define SCRATCH_pw16_decodedLB 310 -#define SCRATCH_pw32_corr 350 -#define SCRATCH_pw16_corrVec 210 -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -int WebRtcNetEQ_Merge(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_decoded, int len, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len) -{ - - WebRtc_Word16 fs_mult; - WebRtc_Word16 fs_shift; - WebRtc_Word32 w32_En_new_frame, w32_En_old_frame; - WebRtc_Word16 w16_expmax, w16_newmax; - WebRtc_Word16 w16_tmp, w16_tmp2; - WebRtc_Word32 w32_tmp; -#ifdef SCRATCH - WebRtc_Word16 *pw16_expanded = pw16_scratchPtr + SCRATCH_pw16_expanded; - WebRtc_Word16 *pw16_expandedLB = pw16_scratchPtr + SCRATCH_pw16_expandedLB; - WebRtc_Word16 *pw16_decodedLB = pw16_scratchPtr + SCRATCH_pw16_decodedLB; - WebRtc_Word32 *pw32_corr = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_pw32_corr); - WebRtc_Word16 *pw16_corrVec = pw16_scratchPtr + SCRATCH_pw16_corrVec; -#else - WebRtc_Word16 pw16_expanded[(125+80+5)*FSMULT]; - WebRtc_Word16 pw16_expandedLB[100]; - WebRtc_Word16 pw16_decodedLB[40]; - WebRtc_Word32 pw32_corr[60]; - WebRtc_Word16 pw16_corrVec[4+60+4]; -#endif - WebRtc_Word16 *pw16_corr = &pw16_corrVec[4]; - WebRtc_Word16 w16_stopPos, w16_bestIndex, w16_interpLen; - WebRtc_Word16 w16_bestVal; /* bestVal is dummy */ - WebRtc_Word16 w16_startfact, w16_inc; - WebRtc_Word16 w16_expandedLen; - WebRtc_Word16 w16_startPos; - WebRtc_Word16 w16_expLen, w16_newLen = 0; - WebRtc_Word16 *pw16_decodedOut; - WebRtc_Word16 w16_muted; - - int w16_decodedLen = len; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); /* Note that this is not "exact" for 48kHz */ - - /************************************* - * Generate data to merge with - *************************************/ - /* - * Check how much data that is left since earlier - * (at least there should be the overlap)... - */ - w16_startPos = inst->endPosition - inst->curPosition; - /* Get one extra expansion to merge and overlap with */ - inst->ExpandInst.w16_stopMuting = 1; - inst->ExpandInst.w16_lagsDirection = 1; /* make sure we get the "optimal" lag */ - inst->ExpandInst.w16_lagsPosition = -1; /* out of the 3 possible ones */ - w16_expandedLen = 0; /* Does not fill any function currently */ - - if (w16_startPos >= 210 * FSMULT) - { - /* - * The number of samples available in the sync buffer is more than what fits in - * pw16_expanded.Keep the first 210*FSMULT samples, but shift them towards the end of - * the buffer. This is ok, since all of the buffer will be expand data anyway, so as - * long as the beginning is left untouched, we're fine. - */ - - w16_tmp = w16_startPos - 210 * FSMULT; /* length difference */ - - WEBRTC_SPL_MEMMOVE_W16(&inst->speechBuffer[inst->curPosition+w16_tmp] , - &inst->speechBuffer[inst->curPosition], 210*FSMULT); - - inst->curPosition += w16_tmp; /* move start position of sync buffer accordingly */ - w16_startPos = 210 * FSMULT; /* this is the truncated length */ - } - - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_expanded, /* let Expand write to beginning of pw16_expanded to avoid overflow */ - &w16_newLen, 0); - - /* - * Now shift the data in pw16_expanded to where it belongs. - * Truncate all that ends up outside the vector. - */ - - WEBRTC_SPL_MEMMOVE_W16(&pw16_expanded[w16_startPos], pw16_expanded, - WEBRTC_SPL_MIN(w16_newLen, - WEBRTC_SPL_MAX(210*FSMULT - w16_startPos, 0) ) ); - - inst->ExpandInst.w16_stopMuting = 0; - - /* Copy what is left since earlier into the expanded vector */ - - WEBRTC_SPL_MEMCPY_W16(pw16_expanded, &inst->speechBuffer[inst->curPosition], w16_startPos); - - /* - * Do "ugly" copy and paste from the expanded in order to generate more data - * to correlate (but not interpolate) with. - */ - w16_expandedLen = (120 + 80 + 2) * fs_mult; - w16_expLen = w16_startPos + w16_newLen; - - if (w16_expLen < w16_expandedLen) - { - while ((w16_expLen + w16_newLen) < w16_expandedLen) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_expanded[w16_expLen], &pw16_expanded[w16_startPos], - w16_newLen); - w16_expLen += w16_newLen; - } - - /* Copy last part (fraction of a whole expansion) */ - - WEBRTC_SPL_MEMCPY_W16(&pw16_expanded[w16_expLen], &pw16_expanded[w16_startPos], - (w16_expandedLen-w16_expLen)); - } - w16_expLen = w16_expandedLen; - - /* Adjust muting factor (main muting factor times expand muting factor) */ - inst->w16_muteFactor - = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(inst->w16_muteFactor, - inst->ExpandInst.w16_expandMuteFactor, 14); - - /* Adjust muting factor if new vector is more or less of the BGN energy */ - len = WEBRTC_SPL_MIN(64*fs_mult, w16_decodedLen); - w16_expmax = WebRtcSpl_MaxAbsValueW16(pw16_expanded, (WebRtc_Word16) len); - w16_newmax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (WebRtc_Word16) len); - - /* Calculate energy of old data */ - w16_tmp = 6 + fs_shift - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_expmax, w16_expmax)); - w16_tmp = WEBRTC_SPL_MAX(w16_tmp,0); - w32_En_old_frame = WebRtcNetEQ_DotW16W16(pw16_expanded, pw16_expanded, len, w16_tmp); - - /* Calculate energy of new data */ - w16_tmp2 = 6 + fs_shift - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_newmax, w16_newmax)); - w16_tmp2 = WEBRTC_SPL_MAX(w16_tmp2,0); - w32_En_new_frame = WebRtcNetEQ_DotW16W16(pw16_decoded, pw16_decoded, len, w16_tmp2); - - /* Align to same Q-domain */ - if (w16_tmp2 > w16_tmp) - { - w32_En_old_frame = WEBRTC_SPL_RSHIFT_W32(w32_En_old_frame, (w16_tmp2-w16_tmp)); - } - else - { - w32_En_new_frame = WEBRTC_SPL_RSHIFT_W32(w32_En_new_frame, (w16_tmp-w16_tmp2)); - } - - /* Calculate muting factor to use for new frame */ - if (w32_En_new_frame > w32_En_old_frame) - { - /* Normalize w32_En_new_frame to 14 bits */ - w16_tmp = WebRtcSpl_NormW32(w32_En_new_frame) - 17; - w32_En_new_frame = WEBRTC_SPL_SHIFT_W32(w32_En_new_frame, w16_tmp); - - /* - * Put w32_En_old_frame in a domain 14 higher, so that - * w32_En_old_frame/w32_En_new_frame is in Q14 - */ - w16_tmp = w16_tmp + 14; - w32_En_old_frame = WEBRTC_SPL_SHIFT_W32(w32_En_old_frame, w16_tmp); - w16_tmp - = WebRtcSpl_DivW32W16ResW16(w32_En_old_frame, (WebRtc_Word16) w32_En_new_frame); - /* Calculate sqrt(w32_En_old_frame/w32_En_new_frame) in Q14 */ - w16_muted = (WebRtc_Word16) WebRtcSpl_Sqrt( - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)w16_tmp,14)); - } - else - { - w16_muted = 16384; /* Set = 1.0 when old frame has higher energy than new */ - } - - /* Set the raise the continued muting factor w16_muted if w16_muteFactor is lower */ - if (w16_muted > inst->w16_muteFactor) - { - inst->w16_muteFactor = WEBRTC_SPL_MIN(w16_muted, 16384); - } - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - /* do not downsample and calculate correlations for slave instance(s) */ - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { -#endif - - /********************************************* - * Downsample to 4kHz and find best overlap - *********************************************/ - - /* Downsample to 4 kHz */ - if (inst->fs == 8000) - { - WebRtcSpl_DownsampleFast(&pw16_expanded[2], (WebRtc_Word16) (w16_expandedLen - 2), - pw16_expandedLB, (WebRtc_Word16) (100), - (WebRtc_Word16*) WebRtcNetEQ_kDownsample8kHzTbl, (WebRtc_Word16) 3, - (WebRtc_Word16) 2, (WebRtc_Word16) 0); - if (w16_decodedLen <= 80) - { - /* Not quite long enough, so we have to cheat a bit... */ - WebRtcSpl_DownsampleFast(&pw16_decoded[2], (WebRtc_Word16) 80, pw16_decodedLB, - (WebRtc_Word16) (40), (WebRtc_Word16*) WebRtcNetEQ_kDownsample8kHzTbl, - (WebRtc_Word16) 3, (WebRtc_Word16) 2, (WebRtc_Word16) 0); - w16_tmp = ((w16_decodedLen - 2) >> 1); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40 - w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast(&pw16_decoded[2], - (WebRtc_Word16) (w16_decodedLen - 2), pw16_decodedLB, - (WebRtc_Word16) (40), (WebRtc_Word16*) WebRtcNetEQ_kDownsample8kHzTbl, - (WebRtc_Word16) 3, (WebRtc_Word16) 2, (WebRtc_Word16) 0); - } -#ifdef NETEQ_WIDEBAND - } - else if (inst->fs==16000) - { - WebRtcSpl_DownsampleFast( - &pw16_expanded[4], (WebRtc_Word16)(w16_expandedLen-4), - pw16_expandedLB, (WebRtc_Word16)(100), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample16kHzTbl, (WebRtc_Word16)5, - (WebRtc_Word16)4, (WebRtc_Word16)0); - if (w16_decodedLen<=160) - { - /* Not quite long enough, so we have to cheat a bit... */ - WebRtcSpl_DownsampleFast( - &pw16_decoded[4], (WebRtc_Word16)160, - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample16kHzTbl, (WebRtc_Word16)5, - (WebRtc_Word16)4, (WebRtc_Word16)0); - w16_tmp = ((w16_decodedLen-4)>>2); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[4], (WebRtc_Word16)(w16_decodedLen-4), - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample16kHzTbl, (WebRtc_Word16)5, - (WebRtc_Word16)4, (WebRtc_Word16)0); - } -#endif -#ifdef NETEQ_32KHZ_WIDEBAND - } - else if (inst->fs==32000) - { - WebRtcSpl_DownsampleFast( - &pw16_expanded[6], (WebRtc_Word16)(w16_expandedLen-6), - pw16_expandedLB, (WebRtc_Word16)(100), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample32kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)8, (WebRtc_Word16)0); - if (w16_decodedLen<=320) - { - /* Not quite long enough, so we have to cheat a bit... */ - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (WebRtc_Word16)320, - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample32kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)8, (WebRtc_Word16)0); - w16_tmp = ((w16_decodedLen-6)>>3); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (WebRtc_Word16)(w16_decodedLen-6), - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample32kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)8, (WebRtc_Word16)0); - } -#endif -#ifdef NETEQ_48KHZ_WIDEBAND - } - else /* if (inst->fs==48000) */ - { - WebRtcSpl_DownsampleFast( - &pw16_expanded[6], (WebRtc_Word16)(w16_expandedLen-6), - pw16_expandedLB, (WebRtc_Word16)(100), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample48kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)12, (WebRtc_Word16)0); - if (w16_decodedLen<=320) - { - /* Not quite long enough, so we have to cheat a bit... */ - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (WebRtc_Word16)320, - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample48kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)12, (WebRtc_Word16)0); - w16_tmp = ((w16_decodedLen-6)>>3); - WebRtcSpl_MemSetW16(&pw16_decodedLB[w16_tmp], 0, (40-w16_tmp)); - } - else - { - WebRtcSpl_DownsampleFast( - &pw16_decoded[6], (WebRtc_Word16)(w16_decodedLen-6), - pw16_decodedLB, (WebRtc_Word16)(40), - (WebRtc_Word16*)WebRtcNetEQ_kDownsample48kHzTbl, (WebRtc_Word16)7, - (WebRtc_Word16)12, (WebRtc_Word16)0); - } -#endif - } - - /* Calculate correlation without any normalization (40 samples) */ - w16_tmp = WebRtcSpl_DivW32W16ResW16((WebRtc_Word32) inst->ExpandInst.w16_maxLag, - (WebRtc_Word16) (fs_mult * 2)) + 1; - w16_stopPos = WEBRTC_SPL_MIN(60, w16_tmp); - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_expmax, w16_newmax); - if (w32_tmp > 26843546) - { - w16_tmp = 3; - } - else - { - w16_tmp = 0; - } - - WebRtcNetEQ_CrossCorr(pw32_corr, pw16_decodedLB, pw16_expandedLB, 40, - (WebRtc_Word16) w16_stopPos, w16_tmp, 1); - - /* Normalize correlation to 14 bits and put in a WebRtc_Word16 vector */ - WebRtcSpl_MemSetW16(pw16_corrVec, 0, (4 + 60 + 4)); - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_stopPos); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_stopPos, pw32_corr, w16_tmp); - - /* Calculate allowed starting point for peak finding. - The peak location bestIndex must fulfill two criteria: - (1) w16_bestIndex+w16_decodedLen < inst->timestampsPerCall+inst->ExpandInst.w16_overlap - (2) w16_bestIndex+w16_decodedLen < w16_startPos */ - w16_tmp = WEBRTC_SPL_MAX(0, WEBRTC_SPL_MAX(w16_startPos, - inst->timestampsPerCall+inst->ExpandInst.w16_overlap) - w16_decodedLen); - /* Downscale starting index to 4kHz domain */ - w16_tmp2 = WebRtcSpl_DivW32W16ResW16((WebRtc_Word32) w16_tmp, - (WebRtc_Word16) (fs_mult << 1)); - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* This is master or mono instance; find peak */ - WebRtcNetEQ_PeakDetection(&pw16_corr[w16_tmp2], w16_stopPos, 1, fs_mult, &w16_bestIndex, - &w16_bestVal); - w16_bestIndex += w16_tmp; /* compensate for modified starting index */ - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* Get peak location from master instance */ - w16_bestIndex = msInfo->bestIndex; - } - else - { - /* Invalid mode */ - return MASTER_SLAVE_ERROR; - } - -#else /* NETEQ_STEREO */ - - /* Find peak */ - WebRtcNetEQ_PeakDetection(&pw16_corr[w16_tmp2], w16_stopPos, 1, fs_mult, &w16_bestIndex, - &w16_bestVal); - w16_bestIndex += w16_tmp; /* compensate for modified starting index */ - -#endif /* NETEQ_STEREO */ - - /* - * Ensure that underrun does not occur for 10ms case => we have to get at least - * 10ms + overlap . (This should never happen thanks to the above modification of - * peak-finding starting point.) - * */ - while ((w16_bestIndex + w16_decodedLen) < (inst->timestampsPerCall - + inst->ExpandInst.w16_overlap) || w16_bestIndex + w16_decodedLen < w16_startPos) - { - w16_bestIndex += w16_newLen; /* Jump one lag ahead */ - } - pw16_decodedOut = pw16_outData + w16_bestIndex; - - /* Mute the new decoded data if needed (and unmute it linearly) */ - w16_interpLen = WEBRTC_SPL_MIN(60*fs_mult, - w16_expandedLen-w16_bestIndex); /* this is the overlapping part of pw16_expanded */ - w16_interpLen = WEBRTC_SPL_MIN(w16_interpLen, w16_decodedLen); - w16_inc = WebRtcSpl_DivW32W16ResW16(4194, - fs_mult); /* in Q20, 0.004 for NB and 0.002 for WB */ - if (inst->w16_muteFactor < 16384) - { - WebRtcNetEQ_UnmuteSignal(pw16_decoded, &inst->w16_muteFactor, pw16_decoded, w16_inc, - (WebRtc_Word16) w16_interpLen); - WebRtcNetEQ_UnmuteSignal(&pw16_decoded[w16_interpLen], &inst->w16_muteFactor, - &pw16_decodedOut[w16_interpLen], w16_inc, - (WebRtc_Word16) (w16_decodedLen - w16_interpLen)); - } - else - { - /* No muting needed */ - - WEBRTC_SPL_MEMMOVE_W16(&pw16_decodedOut[w16_interpLen], &pw16_decoded[w16_interpLen], - (w16_decodedLen-w16_interpLen)); - } - - /* Do overlap and interpolate linearly */ - w16_inc = WebRtcSpl_DivW32W16ResW16(16384, (WebRtc_Word16) (w16_interpLen + 1)); /* Q14 */ - w16_startfact = (16384 - w16_inc); - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_expanded, w16_bestIndex); - WebRtcNetEQ_MixVoiceUnvoice(pw16_decodedOut, &pw16_expanded[w16_bestIndex], pw16_decoded, - &w16_startfact, w16_inc, w16_interpLen); - - inst->w16_mode = MODE_MERGE; - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* New added length (w16_startPos samples were borrowed) */ - *pw16_len = w16_bestIndex + w16_decodedLen - w16_startPos; - - /* Update VQmon parameter */ - inst->w16_concealedTS += (*pw16_len - w16_decodedLen); - inst->w16_concealedTS = WEBRTC_SPL_MAX(0, inst->w16_concealedTS); - - /* Update in-call and post-call statistics */ - if (inst->ExpandInst.w16_expandMuteFactor == 0) - { - /* expansion generates noise only */ - inst->statInst.expandedNoiseSamples += (*pw16_len - w16_decodedLen); - } - else - { - /* expansion generates more than only noise */ - inst->statInst.expandedVoiceSamples += (*pw16_len - w16_decodedLen); - } - inst->statInst.expandLength += (*pw16_len - w16_decodedLen); - - - /* Copy back the first part of the data to the speechHistory */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->curPosition], pw16_outData, w16_startPos); - - - /* Move data to within outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, &pw16_outData[w16_startPos], (*pw16_len)); - - return 0; -} - -#undef SCRATCH_pw16_expanded -#undef SCRATCH_pw16_expandedLB -#undef SCRATCH_pw16_decodedLB -#undef SCRATCH_pw32_corr -#undef SCRATCH_pw16_corrVec -#undef SCRATCH_NETEQ_EXPAND diff --git a/modules/audio_coding/NetEQ/main/source/min_distortion.c b/modules/audio_coding/NetEQ/main/source/min_distortion.c deleted file mode 100644 index 4c9ee1cd0..000000000 --- a/modules/audio_coding/NetEQ/main/source/min_distortion.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Calculate best overlap fit according to distortion measure. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -WebRtc_Word16 WebRtcNetEQ_MinDistortion(const WebRtc_Word16 *pw16_data, - WebRtc_Word16 w16_minLag, WebRtc_Word16 w16_maxLag, - WebRtc_Word16 len, WebRtc_Word32 *pw16_dist) -{ - int i, j; - const WebRtc_Word16 *pw16_data1; - const WebRtc_Word16 *pw16_data2; - WebRtc_Word32 w32_diff; - WebRtc_Word32 w32_sumdiff; - WebRtc_Word16 bestIndex = -1; - WebRtc_Word32 minDist = WEBRTC_SPL_WORD32_MAX; - - for (i = w16_minLag; i <= w16_maxLag; i++) - { - w32_sumdiff = 0; - pw16_data1 = pw16_data; - pw16_data2 = pw16_data - i; - - for (j = 0; j < len; j++) - { - w32_diff = pw16_data1[j] - pw16_data2[j]; - w32_sumdiff += WEBRTC_SPL_ABS_W32(w32_diff); - } - - /* Compare with previous minimum */ - if (w32_sumdiff < minDist) - { - minDist = w32_sumdiff; - bestIndex = i; - } - } - - *pw16_dist = minDist; - - return bestIndex; -} - diff --git a/modules/audio_coding/NetEQ/main/source/mix_voice_unvoice.c b/modules/audio_coding/NetEQ/main/source/mix_voice_unvoice.c deleted file mode 100644 index 98956307b..000000000 --- a/modules/audio_coding/NetEQ/main/source/mix_voice_unvoice.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function mixes a voiced signal with an unvoiced signal and - * updates the weight on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -void WebRtcNetEQ_MixVoiceUnvoice(WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_voicedVec, - WebRtc_Word16 *pw16_unvoicedVec, - WebRtc_Word16 *w16_current_vfraction, - WebRtc_Word16 w16_vfraction_change, WebRtc_Word16 N) -{ - int i; - WebRtc_Word16 w16_tmp2; - WebRtc_Word16 vfraction = *w16_current_vfraction; - - w16_tmp2 = 16384 - vfraction; - for (i = 0; i < N; i++) - { - pw16_outData[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16(vfraction, pw16_voicedVec[i]) + - WEBRTC_SPL_MUL_16_16(w16_tmp2, pw16_unvoicedVec[i]) + 8192, - 14); - vfraction -= w16_vfraction_change; - w16_tmp2 += w16_vfraction_change; - } - *w16_current_vfraction = vfraction; -} - diff --git a/modules/audio_coding/NetEQ/main/source/mute_signal.c b/modules/audio_coding/NetEQ/main/source/mute_signal.c deleted file mode 100644 index ee899cf25..000000000 --- a/modules/audio_coding/NetEQ/main/source/mute_signal.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function mutes a signal linearly on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -void WebRtcNetEQ_MuteSignal(WebRtc_Word16 *pw16_inout, WebRtc_Word16 muteSlope, - WebRtc_Word16 N) -{ - int i; - WebRtc_Word32 w32_tmp = 1048608; /* (16384<<6 + 32) */ - - for (i = 0; i < N; i++) - { - pw16_inout[i] - = (WebRtc_Word16) ((WEBRTC_SPL_MUL_16_16((WebRtc_Word16)(w32_tmp>>6), pw16_inout[i]) - + 8192) >> 14); - w32_tmp -= muteSlope; - } -} - diff --git a/modules/audio_coding/NetEQ/main/source/neteq.gyp b/modules/audio_coding/NetEQ/main/source/neteq.gyp deleted file mode 100644 index a31fece45..000000000 --- a/modules/audio_coding/NetEQ/main/source/neteq.gyp +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'NetEq', - 'type': '<(library)', - 'dependencies': [ - '../../../codecs/CNG/main/source/cng.gyp:CNG', - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'defines': [ - 'NETEQ_VOICEENGINE_CODECS', # TODO: Should create a Chrome define which specifies a subset of codecs to support - 'SCRATCH', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/webrtc_neteq.h', - '../interface/webrtc_neteq_help_macros.h', - '../interface/webrtc_neteq_internal.h', - 'accelerate.c', - 'automode.c', - 'automode.h', - 'bgn_update.c', - 'buffer_stats.h', - 'bufstats_decision.c', - 'cng_internal.c', - 'codec_db.c', - 'codec_db.h', - 'codec_db_defines.h', - 'correlator.c', - 'delay_logging.h', - 'dsp.c', - 'dsp.h', - 'dsp_helpfunctions.c', - 'dsp_helpfunctions.h', - 'dtmf_buffer.c', - 'dtmf_buffer.h', - 'dtmf_tonegen.c', - 'dtmf_tonegen.h', - 'expand.c', - 'mcu.h', - 'mcu_address_init.c', - 'mcu_dsp_common.c', - 'mcu_dsp_common.h', - 'mcu_reset.c', - 'merge.c', - 'min_distortion.c', - 'mix_voice_unvoice.c', - 'mute_signal.c', - 'neteq_defines.h', - 'neteq_error_codes.h', - 'neteq_statistics.h', - 'normal.c', - 'packet_buffer.c', - 'packet_buffer.h', - 'peak_detection.c', - 'preemptive_expand.c', - 'random_vector.c', - 'recin.c', - 'recout.c', - 'rtcp.c', - 'rtcp.h', - 'rtp.c', - 'rtp.h', - 'set_fs.c', - 'signal_mcu.c', - 'split_and_insert.c', - 'unmute_signal.c', - 'webrtc_neteq.c', - ], - }, - { - 'target_name': 'NetEqRTPplay', - 'type': 'executable', - 'dependencies': [ - 'NetEq', # NetEQ library defined above - 'NetEqTestTools',# Test helpers - '../../../codecs/G711/main/source/g711.gyp:G711', - '../../../codecs/G722/main/source/g722.gyp:G722', - '../../../codecs/PCM16B/main/source/pcm16b.gyp:PCM16B', - '../../../codecs/iLBC/main/source/ilbc.gyp:iLBC', - '../../../codecs/iSAC/main/source/isac.gyp:iSAC', - '../../../codecs/CNG/main/source/cng.gyp:CNG', - ], - 'defines': [ - # TODO: Make codec selection conditional on definitions in target NetEq - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], - 'include_dirs': [ - '../source', - '../test', - ], - 'sources': [ - '../test/NetEqRTPplay.cc', - ], - }, - { - 'target_name': 'RTPencode', - 'type': 'executable', - 'dependencies': [ - 'NetEqTestTools',# Test helpers - '../../../codecs/G711/main/source/g711.gyp:G711', - '../../../codecs/G722/main/source/g722.gyp:G722', - '../../../codecs/PCM16B/main/source/pcm16b.gyp:PCM16B', - '../../../codecs/iLBC/main/source/ilbc.gyp:iLBC', - '../../../codecs/iSAC/main/source/isac.gyp:iSAC', - '../../../codecs/CNG/main/source/cng.gyp:CNG', - '../../../../../common_audio/vad/main/source/vad.gyp:vad', - ], - 'defines': [ - # TODO: Make codec selection conditional on definitions in target NetEq - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], - 'include_dirs': [ - '../interface', - '../test', - ], - 'sources': [ - '../test/RTPencode.cc', - ], - }, - - { - 'target_name': 'RTPjitter', - 'type': 'executable', - 'dependencies': [ - ], - 'defines': [ - ], - 'include_dirs': [ - ], - 'sources': [ - '../test/RTPjitter.cc', - ], - }, - - { - 'target_name': 'RTPanalyze', - 'type': 'executable', - 'dependencies': [ - 'NetEqTestTools', - ], - 'defines': [ - ], - 'include_dirs': [ - ], - 'sources': [ - '../test/RTPanalyze.cc', - ], - }, - - { - 'target_name': 'RTPchange', - 'type': 'executable', - 'dependencies': [ - 'NetEqTestTools', - ], - 'defines': [ - ], - 'include_dirs': [ - ], - 'sources': [ - '../test/RTPchange.cc', - ], - }, - - { - 'target_name': 'RTPtimeshift', - 'type': 'executable', - 'dependencies': [ - 'NetEqTestTools', - ], - 'defines': [ - ], - 'include_dirs': [ - ], - 'sources': [ - '../test/RTPtimeshift.cc', - ], - }, - - { - 'target_name': 'RTPcat', - 'type': 'executable', - 'dependencies': [ - 'NetEqTestTools', - ], - 'defines': [ - ], - 'include_dirs': [ - ], - 'sources': [ - '../test/RTPcat.cc', - ], - }, - - { - 'target_name': 'NetEqTestTools', - # Collection of useful functions used in other tests - 'type': '<(library)', - 'dependencies': [ - '../../../codecs/G711/main/source/g711.gyp:G711', - '../../../codecs/G722/main/source/g722.gyp:G722', - '../../../codecs/PCM16B/main/source/pcm16b.gyp:PCM16B', - '../../../codecs/iLBC/main/source/ilbc.gyp:iLBC', - '../../../codecs/iSAC/main/source/isac.gyp:iSAC', - '../../../codecs/CNG/main/source/cng.gyp:CNG', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../test', - '../interface', - ], - }, - 'defines': [ - # TODO: Make codec selection conditional on definitions in target NetEq - 'CODEC_ILBC', - 'CODEC_PCM16B', - 'CODEC_G711', - 'CODEC_G722', - 'CODEC_ISAC', - 'CODEC_PCM16B_WB', - 'CODEC_ISAC_SWB', - 'CODEC_PCM16B_32KHZ', - 'CODEC_CNGCODEC8', - 'CODEC_CNGCODEC16', - 'CODEC_CNGCODEC32', - 'CODEC_ATEVENT_DECODE', - 'CODEC_RED', - ], - 'include_dirs': [ - '../source', - '../interface', - '../test', - ], - 'sources': [ - '../test/NETEQTEST_NetEQClass.cc', - '../test/NETEQTEST_RTPpacket.cc', - '../test/NETEQTEST_CodecClass.cc', - '../test/NETEQTEST_NetEQClass.h', - '../test/NETEQTEST_RTPpacket.h', - '../test/NETEQTEST_CodecClass.h', - ], - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }], - ], - }, - - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/NetEQ/main/source/neteq_defines.h b/modules/audio_coding/NetEQ/main/source/neteq_defines.h deleted file mode 100644 index 7d4c97fa1..000000000 --- a/modules/audio_coding/NetEQ/main/source/neteq_defines.h +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/***************************************************************************************** - * - * Compilation flags in NetEQ: - * - ***************************************************************************************** - * - ***** Platform flags ****** - * - * SCRATCH Run NetEQ with "Scratch memory" to save some stack memory. - * Definition can be used on all platforms - * - ***** Summary flags ****** - * - * NETEQ_ALL_SPECIAL_CODECS Add support for special codecs (CN/RED/DTMF) - * - * NETEQ_ALL_NB_CODECS Add support for all NB codecs (except CN/RED/DTMF) - * - * NETEQ_ALL_WB_CODECS Add support for all WB codecs (except CN/RED/DTMF) - * - * NETEQ_VOICEENGINE_CODECS Support for all NB, WB and SWB32 codecs and CN, RED and DTMF - * - * NETEQ_ALL_CODECS Support for all NB, WB, SWB 32kHz and SWB 48kHz as well as - * CN, RED and DTMF - * - ***** Sampling frequency ****** - * (Note: usually not needed when Summary flags are used) - * - * NETEQ_WIDEBAND Wideband enabled - * - * NETEQ_32KHZ_WIDEBAND Super wideband @ 32kHz enabled - * - * NETEQ_48KHZ_WIDEBAND Super wideband @ 48kHz enabled - * - ***** Special Codec ****** - * (Note: not needed if NETEQ_ALL_CODECS is used) - * - * NETEQ_RED_CODEC With this flag you enable NetEQ to understand redundancy in - * the RTP. NetEQ will use the redundancy if it's the same - * codec - * - * NETEQ_CNG_CODEC Enable DTX with the CN payload - * - * NETEQ_ATEVENT_DECODE Enable AVT event and play out the corresponding DTMF tone - * - ***** Speech Codecs ***** - * (Note: Not needed if Summary flags are used) - * - * NETEQ_G711_CODEC Enable G.711 u- and A-law - * - * NETEQ_PCM16B_CODEC Enable uncompressed 16-bit - * - * NETEQ_ILBC_CODEC Enable iLBC - * - * NETEQ_ISAC_CODEC Enable iSAC - * - * NETEQ_ISAC_SWB_CODEC Enable iSAC-SWB - * - * NETEQ_G722_CODEC Enable G.722 - * - * NETEQ_G729_CODEC Enable G.729 - * - * NETEQ_G729_1_CODEC Enable G.729.1 - * - * NETEQ_G726_CODEC Enable G.726 - * - * NETEQ_G722_1_CODEC Enable G722.1 - * - * NETEQ_G722_1C_CODEC Enable G722.1 Annex C - * - * NETEQ_SPEEX_CODEC Enable Speex (at 8 and 16 kHz sample rate) - * - * NETEQ_GSMFR_CODEC Enable GSM-FR - * - * NETEQ_AMR_CODEC Enable AMR (narrowband) - * - * NETEQ_AMRWB_CODEC Enable AMR-WB - * - * NETEQ_CNG_CODEC Enable DTX with the CNG payload - * - * NETEQ_ATEVENT_DECODE Enable AVT event and play out the corresponding DTMF tone - * - ***** Test flags ****** - * - * WEBRTC_NETEQ_40BITACC_TEST Run NetEQ with simulated 40-bit accumulator to run - * bit-exact to a DSP implementation where the main (splib - * and NetEQ) functions have been 40-bit optimized - * - ***************************************************************************************** - */ - -#if !defined NETEQ_DEFINES_H -#define NETEQ_DEFINES_H - -/* Data block structure for MCU to DSP communication: - * - * - * First 3 16-bit words are pre-header that contains instructions and timestamp update - * Fourth 16-bit word is length of data block 1 - * Rest is payload data - * - * 0 48 64 80 - * -------------...---------------------------------------------------------------------- - * | PreHeader ... | Length 1 | Payload data 1 ...... | Lenght 2| Data block 2.... | ... - * -------------...---------------------------------------------------------------------- - * - * - * Preheader: - * 4 MSB can be either of: - */ - -#define DSP_INSTR_NORMAL 0x1000 -/* Payload data will contain the encoded frames */ - -#define DSP_INSTR_MERGE 0x2000 -/* Payload data block 1 will contain the encoded frame */ -/* Info block will contain the number of missing samples */ - -#define DSP_INSTR_EXPAND 0x3000 -/* Payload data will be empty */ - -#define DSP_INSTR_ACCELERATE 0x4000 -/* Payload data will contain the encoded frame */ - -#define DSP_INSTR_DO_RFC3389CNG 0x5000 -/* Payload data will contain the SID frame if there is one*/ - -#define DSP_INSTR_DTMF_GENERATE 0x6000 -/* Payload data will be one WebRtc_Word16 with the current DTMF value and one - * WebRtc_Word16 with the current volume value - */ -#define DSP_INSTR_NORMAL_ONE_DESC 0x7000 -/* No encoded frames */ - -#define DSP_INSTR_DO_CODEC_INTERNAL_CNG 0x8000 -/* Codec has a built-in VAD/DTX scheme (use the above for "no transmission") */ - -#define DSP_INSTR_PREEMPTIVE_EXPAND 0x9000 -/* Payload data will contain the encoded frames, if any */ - -#define DSP_INSTR_DO_ALTERNATIVE_PLC 0xB000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS 0xC000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_AUDIO_REPETITION 0xD000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_DO_AUDIO_REPETITION_INC_TS 0xE000 -/* NetEQ switched off and packet missing... */ - -#define DSP_INSTR_FADE_TO_BGN 0xF000 -/* Exception handling: fade out to BGN (expand) */ - -/* - * Next 4 bits signal additional data that needs to be transmitted - */ - -#define DSP_CODEC_NO_CHANGE 0x0100 -#define DSP_CODEC_NEW_CODEC 0x0200 -#define DSP_CODEC_ADD_LATE_PKT 0x0300 -#define DSP_CODEC_RESET 0x0400 -#define DSP_DTMF_PAYLOAD 0x0010 - -/* - * The most significant bit of the payload-length - * is used to flag whether the associated payload - * is redundant payload. This currently useful only for - * iSAC, where redundant payloads have to be treated - * differently. Every time the length is read it must be - * masked by DSP_CODEC_MASK_RED_FLAG to ignore the flag. - * Use DSP_CODEC_RED_FLAG to set or retrieve the flag. - */ -#define DSP_CODEC_MASK_RED_FLAG 0x7FFF -#define DSP_CODEC_RED_FLAG 0x8000 - -/* - * The first block of payload data consist of decode function pointers, - * and then the speech blocks. - * - */ - - -/* - * The playout modes that NetEq produced (i.e. gives more info about if the - * Accelerate was successful or not) - */ - -#define MODE_NORMAL 0x0000 -#define MODE_EXPAND 0x0001 -#define MODE_MERGE 0x0002 -#define MODE_SUCCESS_ACCELERATE 0x0003 -#define MODE_UNSUCCESS_ACCELERATE 0x0004 -#define MODE_RFC3389CNG 0x0005 -#define MODE_LOWEN_ACCELERATE 0x0006 -#define MODE_DTMF 0x0007 -#define MODE_ONE_DESCRIPTOR 0x0008 -#define MODE_CODEC_INTERNAL_CNG 0x0009 -#define MODE_SUCCESS_PREEMPTIVE 0x000A -#define MODE_UNSUCCESS_PREEMPTIVE 0x000B -#define MODE_LOWEN_PREEMPTIVE 0x000C -#define MODE_FADE_TO_BGN 0x000D - -#define MODE_ERROR 0x0010 - -#define MODE_AWAITING_CODEC_PTR 0x0100 - -#define MODE_BGN_ONLY 0x0200 - -#define MODE_MASTER_DTMF_SIGNAL 0x0400 - -#define MODE_USING_STEREO 0x0800 - - - -/***********************/ -/* Group codec defines */ -/***********************/ - -#if (defined(NETEQ_ALL_SPECIAL_CODECS)) - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC -#endif - -#if (defined(NETEQ_ALL_NB_CODECS)) /* Except RED, DTMF and CNG */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - #define NETEQ_G729_CODEC - #define NETEQ_G726_CODEC - #define NETEQ_GSMFR_CODEC - #define NETEQ_AMR_CODEC -#endif - -#if (defined(NETEQ_ALL_WB_CODECS)) /* Except RED, DTMF and CNG */ - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - #define NETEQ_G722_1_CODEC - #define NETEQ_G729_1_CODEC - #define NETEQ_SPEEX_CODEC - #define NETEQ_AMRWB_CODEC - #define NETEQ_WIDEBAND -#endif - -#if (defined(NETEQ_ALL_WB32_CODECS)) /* AAC, RED, DTMF and CNG */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - #define NETEQ_G722_1C_CODEC -#endif - -#if (defined(NETEQ_VOICEENGINE_CODECS)) - /* Special codecs */ - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC - - /* Narrowband codecs */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - - /* Wideband codecs */ - #define NETEQ_WIDEBAND - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - - /* Super wideband 32kHz codecs */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - -#endif - -#if (defined(NETEQ_ALL_CODECS)) - /* Special codecs */ - #define NETEQ_CNG_CODEC - #define NETEQ_ATEVENT_DECODE - #define NETEQ_RED_CODEC - #define NETEQ_VAD - #define NETEQ_ARBITRARY_CODEC - - /* Narrowband codecs */ - #define NETEQ_PCM16B_CODEC - #define NETEQ_G711_CODEC - #define NETEQ_ILBC_CODEC - #define NETEQ_G729_CODEC - #define NETEQ_G726_CODEC - #define NETEQ_GSMFR_CODEC - #define NETEQ_AMR_CODEC - - /* Wideband codecs */ - #define NETEQ_WIDEBAND - #define NETEQ_ISAC_CODEC - #define NETEQ_G722_CODEC - #define NETEQ_G722_1_CODEC - #define NETEQ_G729_1_CODEC - #define NETEQ_SPEEX_CODEC - #define NETEQ_AMRWB_CODEC - - /* Super wideband 32kHz codecs */ - #define NETEQ_ISAC_SWB_CODEC - #define NETEQ_32KHZ_WIDEBAND - #define NETEQ_G722_1C_CODEC - - /* Super wideband 48kHz codecs */ - #define NETEQ_48KHZ_WIDEBAND -#endif - -/* Max output size from decoding one frame */ -#if defined(NETEQ_48KHZ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 2880 /* 60 ms super wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 3600 /* 60+15 ms super wideband (60 ms decoded + 15 ms for merge overlap) */ -#elif defined(NETEQ_32KHZ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 1920 /* 60 ms super wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 2400 /* 60+15 ms super wideband (60 ms decoded + 15 ms for merge overlap) */ -#elif defined(NETEQ_WIDEBAND) - #define NETEQ_MAX_FRAME_SIZE 960 /* 60 ms wideband */ - #define NETEQ_MAX_OUTPUT_SIZE 1200 /* 60+15 ms wideband (60 ms decoded + 10 ms for merge overlap) */ -#else - #define NETEQ_MAX_FRAME_SIZE 480 /* 60 ms narrowband */ - #define NETEQ_MAX_OUTPUT_SIZE 600 /* 60+15 ms narrowband (60 ms decoded + 10 ms for merge overlap) */ -#endif - - -/* Enable stereo */ -#define NETEQ_STEREO - -#endif /* #if !defined NETEQ_DEFINES_H */ - diff --git a/modules/audio_coding/NetEQ/main/source/neteq_error_codes.h b/modules/audio_coding/NetEQ/main/source/neteq_error_codes.h deleted file mode 100644 index 1ce468096..000000000 --- a/modules/audio_coding/NetEQ/main/source/neteq_error_codes.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Definition of error codes. - * - * NOTE: When modifying the error codes, - * also modify the function WebRtcNetEQ_GetErrorCode! - */ - -#if !defined NETEQ_ERROR_CODES_H -#define NETEQ_ERROR_CODES_H - -/* Misc Error */ -#define NETEQ_OTHER_ERROR -1000 - -/* Misc Recout Errors */ -#define FAULTY_INSTRUCTION -1001 -#define FAULTY_NETWORK_TYPE -1002 -#define FAULTY_DELAYVALUE -1003 -#define FAULTY_PLAYOUTMODE -1004 -#define CORRUPT_INSTANCE -1005 -#define ILLEGAL_MASTER_SLAVE_SWITCH -1006 -#define MASTER_SLAVE_ERROR -1007 - -/* Misc Recout problems */ -#define UNKNOWN_BUFSTAT_DECISION -2001 -#define RECOUT_ERROR_DECODING -2002 -#define RECOUT_ERROR_SAMPLEUNDERRUN -2003 -#define RECOUT_ERROR_DECODED_TOO_MUCH -2004 - -/* Misc RecIn problems */ -#define RECIN_CNG_ERROR -3001 -#define RECIN_UNKNOWNPAYLOAD -3002 -#define RECIN_BUFFERINSERT_ERROR -3003 - -/* PBUFFER/BUFSTAT ERRORS */ -#define PBUFFER_INIT_ERROR -4001 -#define PBUFFER_INSERT_ERROR1 -4002 -#define PBUFFER_INSERT_ERROR2 -4003 -#define PBUFFER_INSERT_ERROR3 -4004 -#define PBUFFER_INSERT_ERROR4 -4005 -#define PBUFFER_INSERT_ERROR5 -4006 -#define UNKNOWN_G723_HEADER -4007 -#define PBUFFER_NONEXISTING_PACKET -4008 -#define PBUFFER_NOT_INITIALIZED -4009 -#define AMBIGUOUS_ILBC_FRAME_SIZE -4010 - -/* CODEC DATABASE ERRORS */ -#define CODEC_DB_FULL -5001 -#define CODEC_DB_NOT_EXIST1 -5002 -#define CODEC_DB_NOT_EXIST2 -5003 -#define CODEC_DB_NOT_EXIST3 -5004 -#define CODEC_DB_NOT_EXIST4 -5005 -#define CODEC_DB_UNKNOWN_CODEC -5006 -#define CODEC_DB_PAYLOAD_TAKEN -5007 -#define CODEC_DB_UNSUPPORTED_CODEC -5008 -#define CODEC_DB_UNSUPPORTED_FS -5009 - -/* DTMF ERRORS */ -#define DTMF_DEC_PARAMETER_ERROR -6001 -#define DTMF_INSERT_ERROR -6002 -#define DTMF_GEN_UNKNOWN_SAMP_FREQ -6003 -#define DTMF_NOT_SUPPORTED -6004 - -/* RTP/PACKET ERRORS */ -#define RED_SPLIT_ERROR1 -7001 -#define RED_SPLIT_ERROR2 -7002 -#define RTP_TOO_SHORT_PACKET -7003 -#define RTP_CORRUPT_PACKET -7004 - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/neteq_statistics.h b/modules/audio_coding/NetEQ/main/source/neteq_statistics.h deleted file mode 100644 index 5a68ec3bb..000000000 --- a/modules/audio_coding/NetEQ/main/source/neteq_statistics.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Definitions of statistics data structures for MCU and DSP sides. - */ - -#include "typedefs.h" - -#ifndef NETEQ_STATISTICS_H -#define NETEQ_STATISTICS_H - -/* - * Statistics struct on DSP side - */ -typedef struct -{ - - /* variables for in-call statistics; queried through WebRtcNetEQ_GetNetworkStatistics */ - WebRtc_UWord32 expandLength; /* number of samples produced through expand */ - WebRtc_UWord32 preemptiveLength; /* number of samples produced through pre-emptive - expand */ - WebRtc_UWord32 accelerateLength; /* number of samples removed through accelerate */ - - /* variables for post-call statistics; queried through WebRtcNetEQ_GetJitterStatistics */ - WebRtc_UWord32 expandedVoiceSamples; /* number of voice samples produced through expand */ - WebRtc_UWord32 expandedNoiseSamples; /* number of noise (background) samples produced - through expand */ - -} DSPStats_t; - -/* - * Statistics struct on MCU side - * All variables are for post-call statistics; queried through WebRtcNetEQ_GetJitterStatistics. - */ -typedef struct -{ - - WebRtc_UWord32 jbMinSize; /* smallest Jitter Buffer size during call in ms */ - WebRtc_UWord32 jbMaxSize; /* largest Jitter Buffer size during call in ms */ - WebRtc_UWord32 jbAvgSizeQ16; /* the average JB size, measured over time in ms (Q16) */ - WebRtc_UWord16 jbAvgCount; /* help counter for jbAveSize */ - WebRtc_UWord32 minPacketDelayMs; /* min time incoming packet "waited" to be played in ms */ - WebRtc_UWord32 maxPacketDelayMs; /* max time incoming packet "waited" to be played in ms */ - WebRtc_UWord16 avgPacketDelayMs; /* avg time incoming packet "waited" to be played in ms */ - WebRtc_UWord16 avgPacketCount; /* help counter for avgPacketDelayMs */ - WebRtc_UWord32 jbChangeCount; /* count number of successful accelerate and pre-emptive - expand operations */ - WebRtc_UWord32 generatedSilentMs; /* generated silence in ms */ - WebRtc_UWord32 countExpandMoreThan120ms; /* count of tiny expansions */ - WebRtc_UWord32 countExpandMoreThan250ms; /* count of small expansions */ - WebRtc_UWord32 countExpandMoreThan500ms; /* count of medium expansions */ - WebRtc_UWord32 countExpandMoreThan2000ms; /* count of large expansions */ - WebRtc_UWord32 longestExpandDurationMs; /* duration of longest expansions in ms */ - WebRtc_UWord32 accelerateMs; /* audio data removed through accelerate in ms */ - -} MCUStats_t; - -#endif - diff --git a/modules/audio_coding/NetEQ/main/source/normal.c b/modules/audio_coding/NetEQ/main/source/normal.c deleted file mode 100644 index c86505234..000000000 --- a/modules/audio_coding/NetEQ/main/source/normal.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the function for handling "normal" speech operation. - */ -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_expanded 125*fs/8000 0 125*fs/8000-1 - - func WebRtcNetEQ_Expand 40+370*fs/8000 125*fs/8000 39+495*fs/8000 - - Total: 40+495*fs/8000 - */ - -#define SCRATCH_PW16_EXPANDED 0 -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -/**************************************************************************** - * WebRtcNetEQ_Normal(...) - * - * This function has the possibility to modify data that is played out in Normal - * mode, for example adjust the gain of the signal. The length of the signal - * can not be changed. - * - * Input: - * - inst : NetEq instance, i.e. the user that requests more - * speech/audio data - * - scratchPtr : Pointer to scratch vector - * - decoded : Pointer to vector of new data from decoder - * (Vector contents may be altered by the function) - * - len : Number of input samples - * - * Output: - * - inst : Updated user information - * - outData : Pointer to a memory space where the output data - * should be stored - * - pw16_len : Pointer to variable where the number of samples - * produced will be written - * - * Return value : >=0 - Number of samples written to outData - * -1 - Error - */ - -int WebRtcNetEQ_Normal(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - WebRtc_Word16 *pw16_decoded, WebRtc_Word16 len, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len) -{ - - int i; - WebRtc_Word16 fs_mult; - WebRtc_Word16 fs_shift; - WebRtc_Word32 w32_En_speech; - WebRtc_Word16 enLen; - WebRtc_Word16 w16_muted; - WebRtc_Word16 w16_inc, w16_frac; - WebRtc_Word16 w16_tmp; - WebRtc_Word32 w32_tmp; - - /* Sanity check */ - if (len < 0) - { - /* Cannot have negative length of input vector */ - return (-1); - } - - if (len == 0) - { - /* Still got some data to play => continue with the same mode */ - *pw16_len = len; - return (len); - } - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - fs_shift = 30 - WebRtcSpl_NormW32(fs_mult); /* Note that this is not "exact" for 48kHz */ - - /* - * Check if last RecOut call resulted in an Expand or a FadeToBGN. If so, we have to take - * care of some cross-fading and unmuting. - */ - if (inst->w16_mode == MODE_EXPAND || inst->w16_mode == MODE_FADE_TO_BGN) - { - - /* Define memory where temporary result from Expand algorithm can be stored. */ -#ifdef SCRATCH - WebRtc_Word16 *pw16_expanded = pw16_scratchPtr + SCRATCH_PW16_EXPANDED; -#else - WebRtc_Word16 pw16_expanded[FSMULT * 125]; -#endif - WebRtc_Word16 expandedLen = 0; - WebRtc_Word16 w16_decodedMax; - - /* Find largest value in new data */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (WebRtc_Word16) len); - - /* Generate interpolation data using Expand */ - /* First, set Expand parameters to appropriate values. */ - inst->ExpandInst.w16_lagsPosition = 0; - inst->ExpandInst.w16_lagsDirection = 0; - inst->ExpandInst.w16_stopMuting = 1; /* Do not mute signal any more */ - - /* Call Expand */ - WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_expanded, &expandedLen, (WebRtc_Word16) (inst->w16_mode == MODE_FADE_TO_BGN)); - - inst->ExpandInst.w16_stopMuting = 0; /* Restore value */ - inst->ExpandInst.w16_consecExp = 0; /* Last was not Expand any more */ - - /* Adjust muting factor (main muting factor times expand muting factor) */ - if (inst->w16_mode == MODE_FADE_TO_BGN) - { - /* If last mode was FadeToBGN, the mute factor should be zero. */ - inst->w16_muteFactor = 0; - } - else - { - /* w16_muteFactor * w16_expandMuteFactor */ - inst->w16_muteFactor - = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(inst->w16_muteFactor, - inst->ExpandInst.w16_expandMuteFactor, 14); - } - - /* Adjust muting factor if needed (to BGN level) */ - enLen = WEBRTC_SPL_MIN(fs_mult<<6, len); /* min( fs_mult * 64, len ) */ - w16_tmp = 6 + fs_shift - WebRtcSpl_NormW32( - WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(w16_tmp, 0); - w32_En_speech = WebRtcNetEQ_DotW16W16(pw16_decoded, pw16_decoded, enLen, w16_tmp); - w32_En_speech = WebRtcSpl_DivW32W16(w32_En_speech, (WebRtc_Word16) (enLen >> w16_tmp)); - - if ((w32_En_speech != 0) && (w32_En_speech > inst->BGNInst.w32_energy)) - { - /* Normalize new frame energy to 15 bits */ - w16_tmp = WebRtcSpl_NormW32(w32_En_speech) - 16; - /* we want inst->BGNInst.energy/En_speech in Q14 */ - w32_tmp = WEBRTC_SPL_SHIFT_W32(inst->BGNInst.w32_energy, (w16_tmp+14)); - w16_tmp = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(w32_En_speech, w16_tmp); - w16_tmp = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_tmp, w16_tmp); - w16_muted = (WebRtc_Word16) WebRtcSpl_Sqrt( - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) w16_tmp, - 14)); /* w16_muted in Q14 (sqrt(Q28)) */ - } - else - { - w16_muted = 16384; /* 1.0 in Q14 */ - } - if (w16_muted > inst->w16_muteFactor) - { - inst->w16_muteFactor = WEBRTC_SPL_MIN(w16_muted, 16384); - } - - /* If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14) */ - w16_inc = WebRtcSpl_DivW32W16ResW16(64, fs_mult); - for (i = 0; i < len; i++) - { - /* scale with mute factor */ - w32_tmp = WEBRTC_SPL_MUL_16_16(pw16_decoded[i], inst->w16_muteFactor); - /* shift 14 with proper rounding */ - pw16_decoded[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32((w32_tmp + 8192), 14); - /* increase mute_factor towards 16384 */ - inst->w16_muteFactor = WEBRTC_SPL_MIN(16384, (inst->w16_muteFactor+w16_inc)); - } - - /* - * Interpolate the expanded data into the new vector - * (NB/WB/SWB32/SWB40 8/16/32/32 samples) - */ - fs_shift = WEBRTC_SPL_MIN(3, fs_shift); /* Set to 3 for >32kHz */ - w16_inc = 4 >> fs_shift; - w16_frac = w16_inc; - for (i = 0; i < 8 * fs_mult; i++) - { - pw16_decoded[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(w16_frac, pw16_decoded[i]) + - WEBRTC_SPL_MUL_16_16((32 - w16_frac), pw16_expanded[i]) + 8), - 5); - w16_frac += w16_inc; - } - -#ifdef NETEQ_CNG_CODEC - } - else if (inst->w16_mode==MODE_RFC3389CNG) - { /* previous was RFC 3389 CNG...*/ - WebRtc_Word16 pw16_CngInterp[32]; - /* Reset mute factor and start up fresh */ - inst->w16_muteFactor = 16384; - if (inst->CNG_Codec_inst != NULL) - { - /* Generate long enough for 32kHz */ - if(WebRtcCng_Generate(inst->CNG_Codec_inst,pw16_CngInterp, 32, 0)<0) - { - /* error returned; set return vector to all zeros */ - WebRtcSpl_MemSetW16(pw16_CngInterp, 0, 32); - } - } - else - { - /* - * If no CNG instance is defined, just copy from the decoded data. - * (This will result in interpolating the decoded with itself.) - */ - WEBRTC_SPL_MEMCPY_W16(pw16_CngInterp, pw16_decoded, fs_mult * 8); - } - /* - * Interpolate the CNG into the new vector - * (NB/WB/SWB32kHz/SWB48kHz 8/16/32/32 samples) - */ - fs_shift = WEBRTC_SPL_MIN(3, fs_shift); /* Set to 3 for >32kHz */ - w16_inc = 4>>fs_shift; - w16_frac = w16_inc; - for (i = 0; i < 8 * fs_mult; i++) - { - pw16_decoded[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(w16_frac, pw16_decoded[i]) + - WEBRTC_SPL_MUL_16_16((32-w16_frac), pw16_CngInterp[i]) + 8), - 5); - w16_frac += w16_inc; - } -#endif - - } - else if (inst->w16_muteFactor < 16384) - { - /* - * Previous was neither of Expand, FadeToBGN or RFC3389_CNG, but we are still - * ramping up from previous muting. - * If muted increase by 0.64 for every 20 ms (NB/WB 0.0040/0.0020 in Q14) - */ - w16_inc = WebRtcSpl_DivW32W16ResW16(64, fs_mult); - for (i = 0; i < len; i++) - { - /* scale with mute factor */ - w32_tmp = WEBRTC_SPL_MUL_16_16(pw16_decoded[i], inst->w16_muteFactor); - /* shift 14 with proper rounding */ - pw16_decoded[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32((w32_tmp + 8192), 14); - /* increase mute_factor towards 16384 */ - inst->w16_muteFactor = WEBRTC_SPL_MIN(16384, (inst->w16_muteFactor+w16_inc)); - } - } - - /* Copy data to other buffer */WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, len); - - inst->w16_mode = MODE_NORMAL; - *pw16_len = len; - return (len); - -} - -#undef SCRATCH_PW16_EXPANDED -#undef SCRATCH_NETEQ_EXPAND - diff --git a/modules/audio_coding/NetEQ/main/source/packet_buffer.c b/modules/audio_coding/NetEQ/main/source/packet_buffer.c deleted file mode 100644 index 95da7105f..000000000 --- a/modules/audio_coding/NetEQ/main/source/packet_buffer.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the actual packet buffer data structure. - */ - -#include "packet_buffer.h" - -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -#ifdef NETEQ_DELAY_LOGGING -/* special code for offline delay logging */ -#include "delay_logging.h" -#include - -extern FILE *delay_fid2; /* file pointer to delay log file */ -extern WebRtc_UWord32 tot_received_packets; -#endif /* NETEQ_DELAY_LOGGING */ - - -int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets, - WebRtc_Word16 *pw16_memory, int memorySize) -{ - int i; - int pos = 0; - - /* Sanity check */ - if ((memorySize < PBUFFER_MIN_MEMORY_SIZE) || (pw16_memory == NULL) - || (maxNoOfPackets < 2) || (maxNoOfPackets > 600)) - { - /* Invalid parameters */ - return (PBUFFER_INIT_ERROR); - } - - /* Clear the buffer instance */ - WebRtcSpl_MemSetW16((WebRtc_Word16*) bufferInst, 0, - sizeof(PacketBuf_t) / sizeof(WebRtc_Word16)); - - /* Clear the buffer memory */ - WebRtcSpl_MemSetW16((WebRtc_Word16*) pw16_memory, 0, memorySize); - - /* Set maximum number of packets */ - bufferInst->maxInsertPositions = maxNoOfPackets; - - /* Initialize array pointers */ - /* After each pointer has been set, the index pos is advanced to point immediately - * after the the recently allocated vector. Note that one step for the pos index - * corresponds to a WebRtc_Word16. - */ - - bufferInst->timeStamp = (WebRtc_UWord32*) &pw16_memory[pos]; - pos += maxNoOfPackets << 1; /* advance maxNoOfPackets * WebRtc_UWord32 */ - - bufferInst->payloadLocation = (WebRtc_Word16**) &pw16_memory[pos]; - pos += maxNoOfPackets * (sizeof(WebRtc_Word16*) / sizeof(WebRtc_Word16)); /* advance */ - - bufferInst->seqNumber = (WebRtc_UWord16*) &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * WebRtc_UWord16 */ - - bufferInst->payloadType = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * WebRtc_Word16 */ - - bufferInst->payloadLengthBytes = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * WebRtc_Word16 */ - - bufferInst->rcuPlCntr = &pw16_memory[pos]; - pos += maxNoOfPackets; /* advance maxNoOfPackets * WebRtc_Word16 */ - - /* The payload memory starts after the slot arrays */ - bufferInst->startPayloadMemory = &pw16_memory[pos]; - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->memorySizeW16 = (memorySize - pos); /* Remaining memory */ - - /* Initialize each payload slot as empty with infinite delay */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - bufferInst->payloadType[i] = -1; - } - - /* Reset buffer parameters */ - bufferInst->numPacketsInBuffer = 0; - bufferInst->packSizeSamples = 0; - bufferInst->insertPosition = 0; - - /* Reset buffer statistics */ - bufferInst->discardedPackets = 0; - bufferInst->totalDiscardedPackets = 0; - bufferInst->totalFlushedPackets = 0; - - return (0); -} - - -int WebRtcNetEQ_PacketBufferFlush(PacketBuf_t *bufferInst) -{ - int i; - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* Packet buffer has not been initialized */ - /* Don't do the flushing operation, since we do not - know the state of the struct variables */ - return (0); - } - - /* Increase flush counter */ - bufferInst->totalFlushedPackets += bufferInst->numPacketsInBuffer; - - /* Set all payload lengths to zero */ - WebRtcSpl_MemSetW16(bufferInst->payloadLengthBytes, 0, bufferInst->maxInsertPositions); - - /* Reset buffer variables */ - bufferInst->numPacketsInBuffer = 0; - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->insertPosition = 0; - - /* Clear all slots, starting with the last one */ - for (i = (bufferInst->maxInsertPositions - 1); i >= 0; i--) - { - bufferInst->payloadType[i] = -1; - bufferInst->timeStamp[i] = 0; - bufferInst->seqNumber[i] = 0; - } - - return (0); -} - - -int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *RTPpacket, - WebRtc_Word16 *flushed) -{ - int nextPos; - int i; - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - int temp_var; -#endif /* NETEQ_DELAY_LOGGING */ - - /* Initialize to "no flush" */ - *flushed = 0; - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* packet buffer has not been initialized */ - return (-1); - } - - /* Sanity check for payload length - (payloadLen in bytes and memory size in WebRtc_Word16) */ - if ((RTPpacket->payloadLen > (bufferInst->memorySizeW16 << 1)) || (RTPpacket->payloadLen - <= 0)) - { - /* faulty or too long payload length */ - return (-1); - } - - /* Find a position in the buffer for this packet */ - if (bufferInst->numPacketsInBuffer != 0) - { - /* Get the next slot */ - bufferInst->insertPosition++; - if (bufferInst->insertPosition >= bufferInst->maxInsertPositions) - { - /* "Wrap around" and start from the beginning */ - bufferInst->insertPosition = 0; - } - - /* Check if there is enough space for the new packet */ - if (bufferInst->currentMemoryPos + ((RTPpacket->payloadLen + 1) >> 1) - >= &bufferInst->startPayloadMemory[bufferInst->memorySizeW16]) - { - WebRtc_Word16 *tempMemAddress; - - /* - * Payload does not fit at the end of the memory, put it in the beginning - * instead - */ - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - - /* - * Now, we must search for the next non-empty payload, - * finding the one with the lowest start address for the payload - */ - tempMemAddress = &bufferInst->startPayloadMemory[bufferInst->memorySizeW16]; - nextPos = -1; - - /* Loop through all slots again */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - /* Look for the non-empty slot with the lowest - payload location address */ - if (bufferInst->payloadLengthBytes[i] != 0 && bufferInst->payloadLocation[i] - < tempMemAddress) - { - tempMemAddress = bufferInst->payloadLocation[i]; - nextPos = i; - } - } - - /* Check that we did find a previous payload */ - if (nextPos == -1) - { - /* The buffer is corrupt => flush and return error */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - return (-1); - } - } - else - { - /* Payload fits at the end of memory. */ - - /* Find the next non-empty slot. */ - nextPos = bufferInst->insertPosition + 1; - - /* Increase nextPos until a non-empty slot is found or end of array is encountered*/ - while ((bufferInst->payloadLengthBytes[nextPos] == 0) && (nextPos - < bufferInst->maxInsertPositions)) - { - nextPos++; - } - - if (nextPos == bufferInst->maxInsertPositions) - { - /* - * Reached the end of the array, so there must be a packet in the first - * position instead - */ - nextPos = 0; - - /* Increase nextPos until a non-empty slot is found */ - while (bufferInst->payloadLengthBytes[nextPos] == 0) - { - nextPos++; - } - } - } /* end if-else */ - - /* - * Check if the new payload will extend into a payload later in memory. - * If so, the buffer is full. - */ - if ((bufferInst->currentMemoryPos <= bufferInst->payloadLocation[nextPos]) - && ((&bufferInst->currentMemoryPos[(RTPpacket->payloadLen + 1) >> 1]) - > bufferInst->payloadLocation[nextPos])) - { - /* Buffer is full, so the buffer must be flushed */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - } - - if (bufferInst->payloadLengthBytes[bufferInst->insertPosition] != 0) - { - /* All positions are already taken and entire buffer should be flushed */ - WebRtcNetEQ_PacketBufferFlush(bufferInst); - *flushed = 1; - } - - } - else - { - /* Buffer is empty, just insert the packet at the beginning */ - bufferInst->currentMemoryPos = bufferInst->startPayloadMemory; - bufferInst->insertPosition = 0; - } - - /* Insert packet in the found position */ - if (RTPpacket->starts_byte1 == 0) - { - /* Payload is 16-bit aligned => just copy it */ - - WEBRTC_SPL_MEMCPY_W16(bufferInst->currentMemoryPos, - RTPpacket->payload, (RTPpacket->payloadLen + 1) >> 1); - } - else - { - /* Payload is not 16-bit aligned => align it during copy operation */ - for (i = 0; i < RTPpacket->payloadLen; i++) - { - /* copy the (i+1)-th byte to the i-th byte */ - - WEBRTC_SPL_SET_BYTE(bufferInst->currentMemoryPos, - (WEBRTC_SPL_GET_BYTE(RTPpacket->payload, (i + 1))), i); - } - } - - /* Copy the packet information */ - bufferInst->payloadLocation[bufferInst->insertPosition] = bufferInst->currentMemoryPos; - bufferInst->payloadLengthBytes[bufferInst->insertPosition] = RTPpacket->payloadLen; - bufferInst->payloadType[bufferInst->insertPosition] = RTPpacket->payloadType; - bufferInst->seqNumber[bufferInst->insertPosition] = RTPpacket->seqNumber; - bufferInst->timeStamp[bufferInst->insertPosition] = RTPpacket->timeStamp; - bufferInst->rcuPlCntr[bufferInst->insertPosition] = RTPpacket->rcuPlCntr; - /* Update buffer parameters */ - bufferInst->numPacketsInBuffer++; - bufferInst->currentMemoryPos += (RTPpacket->payloadLen + 1) >> 1; - -#ifdef NETEQ_DELAY_LOGGING - /* special code for offline delay logging */ - if (*flushed) - { - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_FLUSH; - fwrite( &temp_var, sizeof(int), 1, delay_fid2 ); - } - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_RECIN; - fwrite( &temp_var, sizeof(int), 1, delay_fid2 ); - fwrite( &RTPpacket->timeStamp, sizeof(WebRtc_UWord32), 1, delay_fid2 ); - fwrite( &RTPpacket->seqNumber, sizeof(WebRtc_UWord16), 1, delay_fid2 ); - fwrite( &RTPpacket->payloadType, sizeof(int), 1, delay_fid2 ); - fwrite( &RTPpacket->payloadLen, sizeof(WebRtc_Word16), 1, delay_fid2 ); - tot_received_packets++; -#endif /* NETEQ_DELAY_LOGGING */ - - return (0); -} - - -int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket, - int bufferPosition) -{ - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* packet buffer has not been initialized */ - return (PBUFFER_NOT_INITIALIZED); - } - - if (bufferPosition < 0 || bufferPosition >= bufferInst->maxInsertPositions) - { - /* buffer position is outside valid range */ - return (NETEQ_OTHER_ERROR); - } - - /* Check that there is a valid payload in the specified position */ - if (bufferInst->payloadLengthBytes[bufferPosition] <= 0) - { - /* The position does not contain a valid payload */ - RTPpacket->payloadLen = 0; /* Set zero length */ - return (PBUFFER_NONEXISTING_PACKET); /* Return error */ - } - - /* Payload exists => extract payload data */ - - /* Copy the actual data payload to RTP packet struct */ - - WEBRTC_SPL_MEMCPY_W16((WebRtc_Word16*) RTPpacket->payload, - bufferInst->payloadLocation[bufferPosition], - (bufferInst->payloadLengthBytes[bufferPosition] + 1) >> 1); /*length in WebRtc_Word16*/ - - /* Copy payload parameters */ - RTPpacket->payloadLen = bufferInst->payloadLengthBytes[bufferPosition]; - RTPpacket->payloadType = bufferInst->payloadType[bufferPosition]; - RTPpacket->seqNumber = bufferInst->seqNumber[bufferPosition]; - RTPpacket->timeStamp = bufferInst->timeStamp[bufferPosition]; - RTPpacket->rcuPlCntr = bufferInst->rcuPlCntr[bufferPosition]; - RTPpacket->starts_byte1 = 0; /* payload is 16-bit aligned */ - - /* Clear the position in the packet buffer */ - bufferInst->payloadType[bufferPosition] = -1; - bufferInst->payloadLengthBytes[bufferPosition] = 0; - bufferInst->seqNumber[bufferPosition] = 0; - bufferInst->timeStamp[bufferPosition] = 0; - bufferInst->payloadLocation[bufferPosition] = bufferInst->startPayloadMemory; - - /* Reduce packet counter with one */ - bufferInst->numPacketsInBuffer--; - - return (0); -} - - -int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t *bufferInst, - WebRtc_UWord32 currentTS, - WebRtc_UWord32 *timestamp, - int *bufferPosition, int eraseOldPkts, - WebRtc_Word16 *payloadType) -{ - WebRtc_Word32 timeStampDiff = WEBRTC_SPL_WORD32_MAX; /* Smallest diff found */ - WebRtc_Word32 newDiff; - int i; - WebRtc_Word16 rcuPlCntr; - - /* Sanity check */ - if (bufferInst->startPayloadMemory == NULL) - { - /* packet buffer has not been initialized */ - return (PBUFFER_NOT_INITIALIZED); - } - - /* Initialize all return values */ - *timestamp = 0; - *payloadType = -1; /* indicates that no packet was found */ - *bufferPosition = -1; /* indicates that no packet was found */ - rcuPlCntr = WEBRTC_SPL_WORD16_MAX; /* indicates that no packet was found */ - - /* Check if buffer is empty */ - if (bufferInst->numPacketsInBuffer <= 0) - { - /* Empty buffer */ - return (0); - } - - /* Loop through all slots in buffer */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - /* Calculate difference between this slot and currentTS */ - newDiff = (WebRtc_Word32) (bufferInst->timeStamp[i] - currentTS); - - /* Check if payload should be discarded */ - if ((newDiff < 0) /* payload is too old */ - && (newDiff > -30000) /* account for TS wrap-around */ - && (eraseOldPkts) /* old payloads should be discarded */ - && (bufferInst->payloadLengthBytes[i] > 0)) /* the payload exists */ - { - /* Throw away old packet */ - - /* Clear the position in the buffer */ - bufferInst->payloadType[i] = -1; - bufferInst->payloadLengthBytes[i] = 0; - - /* Reduce packet counter by one */ - bufferInst->numPacketsInBuffer--; - - /* Increase discard counter for in-call and post-call statistics */ - bufferInst->discardedPackets++; - bufferInst->totalDiscardedPackets++; - } - else if (((newDiff < timeStampDiff) || ((newDiff == timeStampDiff) - && (bufferInst->rcuPlCntr[i] < rcuPlCntr))) && (bufferInst->payloadLengthBytes[i] - > 0)) - { - /* - * New diff is smaller than previous diffs or we have a candidate with a timestamp - * as previous candidate but better RCU-counter; and the payload exists. - */ - - /* Save this position as the best candidate */ - *bufferPosition = i; - timeStampDiff = newDiff; - *payloadType = bufferInst->payloadType[i]; - rcuPlCntr = bufferInst->rcuPlCntr[i]; - } - } /* end of for loop */ - - /* check that we did find a real position */ - if (*bufferPosition >= 0) - { - /* get the timestamp for the best position */ - *timestamp = bufferInst->timeStamp[*bufferPosition]; - } - - return 0; -} - - -WebRtc_Word32 WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t *bufferInst) -{ - int i, count; - WebRtc_Word32 sizeSamples; - - count = 0; - - /* Loop through all slots in the buffer */ - for (i = 0; i < bufferInst->maxInsertPositions; i++) - { - /* Only count the packets with non-zero size */ - if (bufferInst->payloadLengthBytes[i] != 0) - { - count++; - } - } - - /* - * Calculate buffer size as number of packets times packet size - * (packet size is that of the latest decoded packet) - */ - sizeSamples = WEBRTC_SPL_MUL_16_16(bufferInst->packSizeSamples, count); - - /* Sanity check; size cannot be negative */ - if (sizeSamples < 0) - { - sizeSamples = 0; - } - - return sizeSamples; -} - - -int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID, - int noOfCodecs, int *maxBytes, int *maxSlots) -{ - int i; - int ok = 0; - WebRtc_Word16 w16_tmp; - WebRtc_Word16 codecBytes; - WebRtc_Word16 codecBuffers; - - /* Initialize return variables to zero */ - *maxBytes = 0; - *maxSlots = 0; - - /* Loop through all codecs supplied to function */ - for (i = 0; i < noOfCodecs; i++) - { - /* Find current codec and set parameters accordingly */ - - if (codecID[i] == kDecoderPCMu) - { - codecBytes = 1680; /* Up to 210ms @ 64kbps */ - codecBuffers = 30; /* Down to 5ms frames */ - } - else if (codecID[i] == kDecoderPCMa) - { - codecBytes = 1680; /* Up to 210ms @ 64kbps */ - codecBuffers = 30; /* Down to 5ms frames */ - } - else if (codecID[i] == kDecoderILBC) - { - codecBytes = 380; /* 200ms @ 15.2kbps (20ms frames) */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderISAC) - { - codecBytes = 960; /* 240ms @ 32kbps (60ms frames) */ - codecBuffers = 8; - } - else if (codecID[i] == kDecoderISACswb) - { - codecBytes = 1560; /* 240ms @ 52kbps (30ms frames) */ - codecBuffers = 8; - } - else if (codecID[i] == kDecoderPCM16B) - { - codecBytes = 3360; /* 210ms */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderPCM16Bwb) - { - codecBytes = 6720; /* 210ms */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderPCM16Bswb32kHz) - { - codecBytes = 13440; /* 210ms */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderPCM16Bswb48kHz) - { - codecBytes = 20160; /* 210ms */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderG722) - { - codecBytes = 1680; /* 210ms @ 64kbps */ - codecBuffers = 15; - } - else if (codecID[i] == kDecoderRED) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderAVT) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderCNG) - { - codecBytes = 0; /* Should not be max... */ - codecBuffers = 0; - } - else if (codecID[i] == kDecoderG729) - { - codecBytes = 210; /* 210ms @ 8kbps */ - codecBuffers = 20; /* max 200ms supported for 10ms frames */ - } - else if (codecID[i] == kDecoderG729_1) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; /* max 200ms supported for 20ms frames */ - } - else if (codecID[i] == kDecoderG726_16) - { - codecBytes = 400; /* 200ms @ 16kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_24) - { - codecBytes = 600; /* 200ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_32) - { - codecBytes = 800; /* 200ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG726_40) - { - codecBytes = 1000; /* 200ms @ 40kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_16) - { - codecBytes = 420; /* 210ms @ 16kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_24) - { - codecBytes = 630; /* 210ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1_32) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_24) - { - codecBytes = 630; /* 210ms @ 24kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_32) - { - codecBytes = 840; /* 210ms @ 32kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderG722_1C_48) - { - codecBytes = 1260; /* 210ms @ 48kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderSPEEX_8) - { - codecBytes = 1250; /* 210ms @ 50kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderSPEEX_16) - { - codecBytes = 1250; /* 210ms @ 50kbps */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderGSMFR) - { - codecBytes = 340; /* 200ms */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderAMR) - { - codecBytes = 384; /* 240ms @ 12.2kbps+headers (60ms frames) */ - codecBuffers = 10; - } - else if (codecID[i] == kDecoderAMRWB) - { - codecBytes = 744; - codecBuffers = 10; - } - else if (codecID[i] == kDecoderArbitrary) - { - codecBytes = 6720; /* Assume worst case uncompressed WB 210ms */ - codecBuffers = 15; - } - else - { - /*Unknow codec */ - codecBytes = 0; - codecBuffers = 0; - ok = CODEC_DB_UNKNOWN_CODEC; - } - - /* Update max variables */ - *maxBytes = WEBRTC_SPL_MAX((*maxBytes), codecBytes); - *maxSlots = WEBRTC_SPL_MAX((*maxSlots), codecBuffers); - - } /* end of for loop */ - - /* - * Add size needed by the additional pointers for each slot inside struct, - * as indicated on each line below. - */ - w16_tmp = (sizeof(WebRtc_UWord32) /* timeStamp */ - + sizeof(WebRtc_Word16*) /* payloadLocation */ - + sizeof(WebRtc_UWord16) /* seqNumber */ - + sizeof(WebRtc_Word16) /* payloadType */ - + sizeof(WebRtc_Word16) /* payloadLengthBytes */ - + sizeof(WebRtc_Word16)); /* rcuPlCntr */ - /* Add the extra size per slot to the memory count */ - *maxBytes += w16_tmp * (*maxSlots); - - return ok; -} - diff --git a/modules/audio_coding/NetEQ/main/source/packet_buffer.h b/modules/audio_coding/NetEQ/main/source/packet_buffer.h deleted file mode 100644 index a71a812ec..000000000 --- a/modules/audio_coding/NetEQ/main/source/packet_buffer.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Interface for the actual packet buffer data structure. - */ - -#ifndef PACKET_BUFFER_H -#define PACKET_BUFFER_H - -#include "typedefs.h" - -#include "webrtc_neteq.h" -#include "rtp.h" - -/* Define minimum allowed buffer memory, in 16-bit words */ -#define PBUFFER_MIN_MEMORY_SIZE 150 - -/****************************/ -/* The packet buffer struct */ -/****************************/ - -typedef struct -{ - - /* Variables common to the entire buffer */ - WebRtc_UWord16 packSizeSamples; /* packet size in samples of last decoded packet */ - WebRtc_Word16 *startPayloadMemory; /* pointer to the payload memory */ - int memorySizeW16; /* the size (in WebRtc_Word16) of the payload memory */ - WebRtc_Word16 *currentMemoryPos; /* The memory position to insert next payload */ - int numPacketsInBuffer; /* The number of packets in the buffer */ - int insertPosition; /* The position to insert next packet */ - int maxInsertPositions; /* Maximum number of packets allowed */ - - /* Arrays with one entry per packet slot */ - /* NOTE: If these are changed, the changes must be accounted for at the end of - the function WebRtcNetEQ_GetDefaultCodecSettings(). */ - WebRtc_UWord32 *timeStamp; /* Timestamp in slot n */ - WebRtc_Word16 **payloadLocation; /* Memory location of payload in slot n */ - WebRtc_UWord16 *seqNumber; /* Sequence number in slot n */ - WebRtc_Word16 *payloadType; /* Payload type of packet in slot n */ - WebRtc_Word16 *payloadLengthBytes; /* Payload length of packet in slot n */ - WebRtc_Word16 *rcuPlCntr; /* zero for non-RCU payload, 1 for main payload - 2 for redundant payload */ - - /* Statistics counters */ - WebRtc_UWord16 discardedPackets; /* Number of discarded packets */ - WebRtc_UWord32 totalDiscardedPackets; /* Total number of discarded packets */ - WebRtc_UWord32 totalFlushedPackets; /* Total number of flushed packets */ - -} PacketBuf_t; - -/*************************/ -/* Function declarations */ -/*************************/ - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferInit(...) - * - * This function initializes the packet buffer. - * - * Input: - * - bufferInst : Buffer instance to be initialized - * - noOfPackets : Maximum number of packets that buffer should hold - * - memory : Pointer to the storage memory for the payloads - * - memorySize : The size of the payload memory (in WebRtc_Word16) - * - * Output: - * - bufferInst : Updated buffer instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PacketBufferInit(PacketBuf_t *bufferInst, int maxNoOfPackets, - WebRtc_Word16 *pw16_memory, int memorySize); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferFlush(...) - * - * This function flushes all the packets in the buffer. - * - * Input: - * - bufferInst : Buffer instance to be flushed - * - * Output: - * - bufferInst : Flushed buffer instance - * - * Return value : 0 - Ok - */ - -int WebRtcNetEQ_PacketBufferFlush(PacketBuf_t *bufferInst); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferInsert(...) - * - * This function inserts an RTP packet into the packet buffer. - * - * Input: - * - bufferInst : Buffer instance - * - RTPpacket : An RTP packet struct (with payload, sequence - * number, etc.) - * - * Output: - * - bufferInst : Updated buffer instance - * - flushed : 1 if buffer was flushed, 0 otherwise - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_PacketBufferInsert(PacketBuf_t *bufferInst, const RTPPacket_t *RTPpacket, - WebRtc_Word16 *flushed); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferExtract(...) - * - * This function extracts a payload from the buffer. - * - * Input: - * - bufferInst : Buffer instance - * - bufferPosition: Position of the packet that should be extracted - * - * Output: - * - RTPpacket : An RTP packet struct (with payload, sequence - * number, etc) - * - bufferInst : Updated buffer instance - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PacketBufferExtract(PacketBuf_t *bufferInst, RTPPacket_t *RTPpacket, - int bufferPosition); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferFindLowestTimestamp(...) - * - * This function finds the next packet with the lowest timestamp. - * - * Input: - * - bufferInst : Buffer instance - * - currentTS : The timestamp to compare packet timestamps with - * - eraseOldPkts : If non-zero, erase packets older than currentTS - * - * Output: - * - timestamp : Lowest timestamp that was found - * - bufferPosition: Position of this packet (-1 if there are no packets - * in the buffer) - * - payloadType : Payload type of the found payload - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PacketBufferFindLowestTimestamp(PacketBuf_t *bufferInst, - WebRtc_UWord32 currentTS, - WebRtc_UWord32 *timestamp, - int *bufferPosition, int eraseOldPkts, - WebRtc_Word16 *payloadType); - -/**************************************************************************** - * WebRtcNetEQ_PacketBufferGetSize(...) - * - * Calculate and return an estimate of the total data length (in samples) - * currently in the buffer. The estimate is calculated as the number of - * packets currently in the buffer (which does not have any remaining waiting - * time), multiplied with the number of samples obtained from the last - * decoded packet. - * - * Input: - * - bufferInst : Buffer instance - * - * Return value : The buffer size in samples - */ - -WebRtc_Word32 WebRtcNetEQ_PacketBufferGetSize(const PacketBuf_t *bufferInst); - -/**************************************************************************** - * WebRtcNetEQ_GetDefaultCodecSettings(...) - * - * Calculates a recommended buffer size for a specific set of codecs. - * - * Input: - * - codecID : An array of codec types that will be used - * - noOfCodecs : Number of codecs in array codecID - * - * Output: - * - maxBytes : Recommended buffer memory size in bytes - * - maxSlots : Recommended number of slots in buffer - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_GetDefaultCodecSettings(const enum WebRtcNetEQDecoder *codecID, - int noOfCodecs, int *maxBytes, int *maxSlots); - -#endif /* PACKET_BUFFER_H */ diff --git a/modules/audio_coding/NetEQ/main/source/peak_detection.c b/modules/audio_coding/NetEQ/main/source/peak_detection.c deleted file mode 100644 index 678c7f91a..000000000 --- a/modules/audio_coding/NetEQ/main/source/peak_detection.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the peak detection used for finding correlation peaks. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - -/* Table of constants used in parabolic fit function WebRtcNetEQ_PrblFit */ -const WebRtc_Word16 WebRtcNetEQ_kPrblCf[17][3] = { { 120, 32, 64 }, { 140, 44, 75 }, - { 150, 50, 80 }, { 160, 57, 85 }, - { 180, 72, 96 }, { 200, 89, 107 }, - { 210, 98, 112 }, { 220, 108, 117 }, - { 240, 128, 128 }, { 260, 150, 139 }, - { 270, 162, 144 }, { 280, 174, 149 }, - { 300, 200, 160 }, { 320, 228, 171 }, - { 330, 242, 176 }, { 340, 257, 181 }, - { 360, 288, 192 } }; - -WebRtc_Word16 WebRtcNetEQ_PeakDetection(WebRtc_Word16 *pw16_data, WebRtc_Word16 w16_dataLen, - WebRtc_Word16 w16_nmbPeaks, WebRtc_Word16 fs_mult, - WebRtc_Word16 *pw16_winIndex, - WebRtc_Word16 *pw16_winValue) -{ - /* Local variables */ - int i; - WebRtc_Word16 w16_tmp; - WebRtc_Word16 w16_tmp2; - WebRtc_Word16 indMin = 0; - WebRtc_Word16 indMax = 0; - - /* Peak detection */ - - for (i = 0; i <= (w16_nmbPeaks - 1); i++) - { - if (w16_nmbPeaks == 1) - { - /* - * Single peak - * The parabola fit assumes that an extra point is available; worst case it gets - * a zero on the high end of the signal. - */ - w16_dataLen++; - } - - pw16_winIndex[i] = WebRtcSpl_MaxIndexW16(pw16_data, (WebRtc_Word16) (w16_dataLen - 1)); - - if (i != w16_nmbPeaks - 1) - { - w16_tmp = pw16_winIndex[i] - 2; /* *fs_mult; */ - indMin = WEBRTC_SPL_MAX(0, w16_tmp); - w16_tmp = pw16_winIndex[i] + 2; /* *fs_mult; */ - w16_tmp2 = w16_dataLen - 1; - indMax = WEBRTC_SPL_MIN(w16_tmp2, w16_tmp); - } - - if ((pw16_winIndex[i] != 0) && (pw16_winIndex[i] != (w16_dataLen - 2))) - { - /* Parabola fit*/ - WebRtcNetEQ_PrblFit(&(pw16_data[pw16_winIndex[i] - 1]), &(pw16_winIndex[i]), - &(pw16_winValue[i]), fs_mult); - } - else - { - if (pw16_winIndex[i] == (w16_dataLen - 2)) - { - if (pw16_data[pw16_winIndex[i]] > pw16_data[pw16_winIndex[i] + 1]) - { - WebRtcNetEQ_PrblFit(&(pw16_data[pw16_winIndex[i] - 1]), - &(pw16_winIndex[i]), &(pw16_winValue[i]), fs_mult); - } - else if (pw16_data[pw16_winIndex[i]] <= pw16_data[pw16_winIndex[i] + 1]) - { - pw16_winValue[i] = (pw16_data[pw16_winIndex[i]] - + pw16_data[pw16_winIndex[i] + 1]) >> 1; /* lin approx */ - pw16_winIndex[i] = (pw16_winIndex[i] * 2 + 1) * fs_mult; - } - } - else - { - pw16_winValue[i] = pw16_data[pw16_winIndex[i]]; - pw16_winIndex[i] = pw16_winIndex[i] * 2 * fs_mult; - } - } - - if (i != w16_nmbPeaks - 1) - { - WebRtcSpl_MemSetW16(&(pw16_data[indMin]), 0, (indMax - indMin + 1)); - /* for (j=indMin; j<=indMax; j++) pw16_data[j] = 0; */ - } - } - - return 0; -} - -WebRtc_Word16 WebRtcNetEQ_PrblFit(WebRtc_Word16 *pw16_3pts, WebRtc_Word16 *pw16_Ind, - WebRtc_Word16 *pw16_outVal, WebRtc_Word16 fs_mult) -{ - /* Variables */ - WebRtc_Word32 Num, Den; - WebRtc_Word32 temp; - WebRtc_Word16 flag, stp, strt, lmt; - WebRtc_UWord16 PFind[13]; - - if (fs_mult == 1) - { - PFind[0] = 0; - PFind[1] = 8; - PFind[2] = 16; - } - else if (fs_mult == 2) - { - PFind[0] = 0; - PFind[1] = 4; - PFind[2] = 8; - PFind[3] = 12; - PFind[4] = 16; - } - else if (fs_mult == 4) - { - PFind[0] = 0; - PFind[1] = 2; - PFind[2] = 4; - PFind[3] = 6; - PFind[4] = 8; - PFind[5] = 10; - PFind[6] = 12; - PFind[7] = 14; - PFind[8] = 16; - } - else - { - PFind[0] = 0; - PFind[1] = 1; - PFind[2] = 3; - PFind[3] = 4; - PFind[4] = 5; - PFind[5] = 7; - PFind[6] = 8; - PFind[7] = 9; - PFind[8] = 11; - PFind[9] = 12; - PFind[10] = 13; - PFind[11] = 15; - PFind[12] = 16; - } - - /* Num = -3*pw16_3pts[0] + 4*pw16_3pts[1] - pw16_3pts[2]; */ - /* Den = pw16_3pts[0] - 2*pw16_3pts[1] + pw16_3pts[2]; */ - Num = WEBRTC_SPL_MUL_16_16(pw16_3pts[0],-3) + WEBRTC_SPL_MUL_16_16(pw16_3pts[1],4) - - pw16_3pts[2]; - - Den = pw16_3pts[0] + WEBRTC_SPL_MUL_16_16(pw16_3pts[1],-2) + pw16_3pts[2]; - - temp = (WebRtc_Word32) WEBRTC_SPL_MUL(Num, (WebRtc_Word32)120); /* need 32_16 really */ - flag = 1; - stp = WebRtcNetEQ_kPrblCf[PFind[fs_mult]][0] - WebRtcNetEQ_kPrblCf[PFind[fs_mult - 1]][0]; - strt = (WebRtcNetEQ_kPrblCf[PFind[fs_mult]][0] - + WebRtcNetEQ_kPrblCf[PFind[fs_mult - 1]][0]) >> 1; - - if (temp < (WebRtc_Word32) WEBRTC_SPL_MUL(-Den,(WebRtc_Word32)strt)) - { - lmt = strt - stp; - while (flag) - { - if ((flag == fs_mult) || (temp - > (WebRtc_Word32) WEBRTC_SPL_MUL(-Den,(WebRtc_Word32)lmt))) - { - *pw16_outVal - = (WebRtc_Word16) - (((WebRtc_Word32) ((WebRtc_Word32) WEBRTC_SPL_MUL(Den,(WebRtc_Word32)WebRtcNetEQ_kPrblCf[PFind[fs_mult-flag]][1]) - + (WebRtc_Word32) WEBRTC_SPL_MUL(Num,(WebRtc_Word32)WebRtcNetEQ_kPrblCf[PFind[fs_mult-flag]][2]) - + WEBRTC_SPL_MUL_16_16(pw16_3pts[0],256))) >> 8); - *pw16_Ind = (*pw16_Ind) * (fs_mult << 1) - flag; - flag = 0; - } - else - { - flag++; - lmt -= stp; - } - } - } - else if (temp > (WebRtc_Word32) WEBRTC_SPL_MUL(-Den,(WebRtc_Word32)(strt+stp))) - { - lmt = strt + (stp << 1); - while (flag) - { - if ((flag == fs_mult) || (temp - < (WebRtc_Word32) WEBRTC_SPL_MUL(-Den,(WebRtc_Word32)lmt))) - { - WebRtc_Word32 temp_term_1, temp_term_2, temp_term_3; - - temp_term_1 = WEBRTC_SPL_MUL(Den, - (WebRtc_Word32) WebRtcNetEQ_kPrblCf[PFind[fs_mult+flag]][1]); - temp_term_2 = WEBRTC_SPL_MUL(Num, - (WebRtc_Word32) WebRtcNetEQ_kPrblCf[PFind[fs_mult+flag]][2]); - temp_term_3 = WEBRTC_SPL_MUL_16_16(pw16_3pts[0],256); - - *pw16_outVal - = (WebRtc_Word16) ((temp_term_1 + temp_term_2 + temp_term_3) >> 8); - - *pw16_Ind = (*pw16_Ind) * (fs_mult << 1) + flag; - flag = 0; - } - else - { - flag++; - lmt += stp; - } - } - - } - else - { - *pw16_outVal = pw16_3pts[1]; - *pw16_Ind = (*pw16_Ind) * 2 * fs_mult; - } - - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/preemptive_expand.c b/modules/audio_coding/NetEQ/main/source/preemptive_expand.c deleted file mode 100644 index 7867b36dc..000000000 --- a/modules/audio_coding/NetEQ/main/source/preemptive_expand.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the Pre-emptive Expand algorithm that is used to increase - * the delay by repeating a part of the audio stream. - */ - -#include "dsp.h" - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" - -#define PREEMPTIVE_CORR_LEN 50 -#define PREEMPTIVE_MIN_LAG 10 -#define PREEMPTIVE_MAX_LAG 60 -#define PREEMPTIVE_DOWNSAMPLED_LEN (PREEMPTIVE_CORR_LEN + PREEMPTIVE_MAX_LAG) - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_downSampSpeech 110 0 109 - WebRtc_Word32 pw32_corr 2*50 110 209 - WebRtc_Word16 pw16_corr 50 0 49 - - Total: 110+2*50 - */ - -#define SCRATCH_PW16_DS_SPEECH 0 -#define SCRATCH_PW32_CORR PREEMPTIVE_DOWNSAMPLED_LEN -#define SCRATCH_PW16_CORR 0 - -/**************************************************************************** - * WebRtcNetEQ_PreEmptiveExpand(...) - * - * This function tries to extend the audio data by repeating one or several - * pitch periods. The operation is only carried out if the correlation is - * strong or if the signal energy is very low. The algorithm is the - * reciprocal of the Accelerate algorithm. - * - * Input: - * - inst : NetEQ DSP instance - * - scratchPtr : Pointer to scratch vector. - * - decoded : Pointer to newly decoded speech. - * - len : Length of decoded speech. - * - oldDataLen : Length of the part of decoded that has already been played out. - * - BGNonly : If non-zero, Pre-emptive Expand will only copy - * the first DEFAULT_TIME_ADJUST seconds of the - * input and append to the end. No signal matching is - * done. - * - * Output: - * - inst : Updated instance - * - outData : Pointer to a memory space where the output data - * should be stored. The vector must be at least - * min(len + 120*fs/8000, NETEQ_MAX_OUTPUT_SIZE) - * elements long. - * - pw16_len : Number of samples written to outData. - * - * Return value : 0 - Ok - * <0 - Error - */ - -int WebRtcNetEQ_PreEmptiveExpand(DSPInst_t *inst, -#ifdef SCRATCH - WebRtc_Word16 *pw16_scratchPtr, -#endif - const WebRtc_Word16 *pw16_decoded, int len, int oldDataLen, - WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len, - WebRtc_Word16 BGNonly) -{ - -#ifdef SCRATCH - /* Use scratch memory for internal temporary vectors */ - WebRtc_Word16 *pw16_downSampSpeech = pw16_scratchPtr + SCRATCH_PW16_DS_SPEECH; - WebRtc_Word32 *pw32_corr = (WebRtc_Word32*) (pw16_scratchPtr + SCRATCH_PW32_CORR); - WebRtc_Word16 *pw16_corr = pw16_scratchPtr + SCRATCH_PW16_CORR; -#else - /* Allocate memory for temporary vectors */ - WebRtc_Word16 pw16_downSampSpeech[PREEMPTIVE_DOWNSAMPLED_LEN]; - WebRtc_Word32 pw32_corr[PREEMPTIVE_CORR_LEN]; - WebRtc_Word16 pw16_corr[PREEMPTIVE_CORR_LEN]; -#endif - WebRtc_Word16 w16_decodedMax = 0; - WebRtc_Word16 w16_tmp; - WebRtc_Word16 w16_tmp2; - WebRtc_Word32 w32_tmp; - WebRtc_Word32 w32_tmp2; - - const WebRtc_Word16 w16_startLag = PREEMPTIVE_MIN_LAG; - const WebRtc_Word16 w16_endLag = PREEMPTIVE_MAX_LAG; - const WebRtc_Word16 w16_corrLen = PREEMPTIVE_CORR_LEN; - const WebRtc_Word16 *pw16_vec1, *pw16_vec2; - WebRtc_Word16 *pw16_vectmp; - WebRtc_Word16 w16_inc, w16_startfact; - WebRtc_Word16 w16_bestIndex, w16_bestVal; - WebRtc_Word16 w16_VAD = 1; - WebRtc_Word16 fsMult; - WebRtc_Word16 fsMult120; - WebRtc_Word32 w32_en1, w32_en2, w32_cc; - WebRtc_Word16 w16_en1, w16_en2; - WebRtc_Word16 w16_en1Scale, w16_en2Scale; - WebRtc_Word16 w16_sqrtEn1En2; - WebRtc_Word16 w16_bestCorr = 0; - int ok; - -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - - fsMult = WebRtcNetEQ_CalcFsMult(inst->fs); /* Calculate fs/8000 */ - - /* Pre-calculate common multiplication with fsMult */ - fsMult120 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16(fsMult, 120); /* 15 ms */ - - inst->ExpandInst.w16_consecExp = 0; /* Last was not expand any more */ - - /* - * Sanity check for len variable; must be (almost) 30 ms (120*fsMult + max(bestIndex)). - * Also, the new part must be at least .625 ms (w16_overlap). - */ - if (len < (WebRtc_Word16) WEBRTC_SPL_MUL_16_16((120 + 119), fsMult) || oldDataLen >= len - - inst->ExpandInst.w16_overlap) - { - /* Length of decoded data too short */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return NETEQ_OTHER_ERROR; - } - - /***********************************/ - /* Special operations for BGN only */ - /***********************************/ - - /* Check if "background noise only" flag is set */ - if (BGNonly) - { - /* special operation for BGN only; simply insert a chunk of data */ - w16_bestIndex = DEFAULT_TIME_ADJUST * (fsMult << 3); /* X*fs/1000 */ - - /* Sanity check for bestIndex */ - if (w16_bestIndex > len) - { /* not good, do nothing instead */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return NETEQ_OTHER_ERROR; - } - - /* set length parameter */ - *pw16_len = len + w16_bestIndex; - - - /* copy to output */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, len); - WEBRTC_SPL_MEMCPY_W16(&pw16_outData[len], pw16_decoded, w16_bestIndex); - - /* set mode */ - inst->w16_mode = MODE_LOWEN_PREEMPTIVE; - - /* update statistics */ - inst->statInst.preemptiveLength += w16_bestIndex; - - return 0; - } /* end of special code for BGN mode */ - -#ifdef NETEQ_STEREO - - /* Sanity for msInfo */ - if (msInfo == NULL) - { - /* this should not happen here */ - return MASTER_SLAVE_ERROR; - } - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find correlation lag only for non-slave instances */ - -#endif - - /****************************************************************/ - /* Find the strongest correlation lag by downsampling to 4 kHz, */ - /* calculating correlation for downsampled signal and finding */ - /* the strongest correlation peak. */ - /****************************************************************/ - - /* find maximum absolute value */ - w16_decodedMax = WebRtcSpl_MaxAbsValueW16(pw16_decoded, (WebRtc_Word16) len); - - /* downsample the decoded speech to 4 kHz */ - ok = WebRtcNetEQ_DownSampleTo4kHz(pw16_decoded, len, inst->fs, pw16_downSampSpeech, - PREEMPTIVE_DOWNSAMPLED_LEN, 1 /* compensate delay*/); - if (ok != 0) - { - /* error */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - *pw16_len = len; - - - /* simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return NETEQ_OTHER_ERROR; - } - - /* - * Set scaling factor for cross correlation to protect against - * overflow (log2(50) => 6) - */ - w16_tmp = 6 - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax)); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* Perform correlation from lag 10 to lag 60 in 4 kHz domain */WebRtcNetEQ_CrossCorr( - pw32_corr, &pw16_downSampSpeech[w16_endLag], - &pw16_downSampSpeech[w16_endLag - w16_startLag], w16_corrLen, - (WebRtc_Word16) (w16_endLag - w16_startLag), w16_tmp, -1); - - /* Normalize correlation to 14 bits and put in a WebRtc_Word16 vector */ - w32_tmp = WebRtcSpl_MaxAbsValueW32(pw32_corr, w16_corrLen); - w16_tmp = 17 - WebRtcSpl_NormW32(w32_tmp); - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - WebRtcSpl_VectorBitShiftW32ToW16(pw16_corr, w16_corrLen, pw32_corr, w16_tmp); - - /* Find limits for peak finding, in order to avoid overful NetEQ algorithm buffer. */ - /* Calculate difference between MAX_OUTPUT_SIZE and len in 4 kHz domain. */ - w16_tmp = WebRtcSpl_DivW32W16ResW16((WebRtc_Word32) (NETEQ_MAX_OUTPUT_SIZE - len), - (WebRtc_Word16) (fsMult << 1)) - w16_startLag; - w16_tmp = WEBRTC_SPL_MIN(w16_corrLen, w16_tmp); /* no more than corrLen = 50 */ - -#ifdef NETEQ_STEREO - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, w16_tmp, 1, fsMult, &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*w16_tmp - 1)*fsMult <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - - msInfo->bestIndex = w16_bestIndex; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - if (msInfo->extraInfo == PE_EXP_FAIL) - { - /* Master has signaled an unsuccessful preemptive expand */ - w16_bestIndex = 0; - } - else - { - /* Get best index from master */ - w16_bestIndex = msInfo->bestIndex; - } - } - else - { - /* Invalid mode */ - return (MASTER_SLAVE_ERROR); - } - -#else /* NETEQ_STEREO */ - - /* Find the strongest correlation peak by using the parabolic fit method */ - WebRtcNetEQ_PeakDetection(pw16_corr, w16_tmp, 1, fsMult, &w16_bestIndex, &w16_bestVal); - /* 0 <= bestIndex <= (2*w16_tmp - 1)*fsMult <= (2*corrLen - 1)*fsMult = 99*fsMult */ - - /* Compensate bestIndex for displaced starting position */ - w16_bestIndex = w16_bestIndex + w16_startLag * WEBRTC_SPL_LSHIFT_W16(fsMult, 1); - /* 20*fsMult <= bestIndex <= 119*fsMult */ - -#endif /* NETEQ_STEREO */ - -#ifdef NETEQ_STEREO - - if ((msInfo->msMode == NETEQ_MASTER) || (msInfo->msMode == NETEQ_MONO)) - { - /* Calculate correlation only for non-slave instances */ - -#endif /* NETEQ_STEREO */ - - /*****************************************************/ - /* Calculate correlation bestCorr for the found lag. */ - /* Also do a simple VAD decision. */ - /*****************************************************/ - - /* - * Calculate scaling to ensure that bestIndex samples can be square-summed - * without overflowing - */ - w16_tmp = (31 - - WebRtcSpl_NormW32(WEBRTC_SPL_MUL_16_16(w16_decodedMax, w16_decodedMax))); - w16_tmp += (31 - WebRtcSpl_NormW32(w16_bestIndex)); - w16_tmp -= 31; - w16_tmp = WEBRTC_SPL_MAX(0, w16_tmp); - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[fsMult120 - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[fsMult120]; - - /* Calculate energies for vec1 and vec2 */ - w32_en1 = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec1, - (WebRtc_Word16*) pw16_vec1, w16_bestIndex, w16_tmp); - w32_en2 = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec2, - (WebRtc_Word16*) pw16_vec2, w16_bestIndex, w16_tmp); - - /* Calculate cross-correlation at the found lag */ - w32_cc = WebRtcNetEQ_DotW16W16((WebRtc_Word16*) pw16_vec1, (WebRtc_Word16*) pw16_vec2, - w16_bestIndex, w16_tmp); - - /* Check VAD constraint - ((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_en1 + w32_en2, 4); /* (en1+en2)/(2*8) */ - if (inst->BGNInst.w16_initialized == 1) - { - w32_tmp2 = inst->BGNInst.w32_energy; - } - else - { - /* if BGN parameters have not been estimated, use a fixed threshold */ - w32_tmp2 = 75000; - } - w16_tmp2 = 16 - WebRtcSpl_NormW32(w32_tmp2); - w16_tmp2 = WEBRTC_SPL_MAX(0, w16_tmp2); - w32_tmp = WEBRTC_SPL_RSHIFT_W32(w32_tmp, w16_tmp2); - w16_tmp2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_tmp2, w16_tmp2); - w32_tmp2 = WEBRTC_SPL_MUL_16_16(w16_bestIndex, w16_tmp2); - - /* Scale w32_tmp properly before comparing with w32_tmp2 */ - /* (w16_tmp is scaling before energy calculation, thus 2*w16_tmp) */ - if (WebRtcSpl_NormW32(w32_tmp) < WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)) - { - /* Cannot scale only w32_tmp, must scale w32_temp2 too */ - WebRtc_Word16 tempshift = WebRtcSpl_NormW32(w32_tmp); - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, tempshift); - w32_tmp2 = WEBRTC_SPL_RSHIFT_W32(w32_tmp2, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1) - tempshift); - } - else - { - w32_tmp = WEBRTC_SPL_LSHIFT_W32(w32_tmp, - WEBRTC_SPL_LSHIFT_W32(w16_tmp,1)); - } - - if (w32_tmp <= w32_tmp2) /*((en1+en2)/(2*bestIndex)) <= 8*inst->BGNInst.energy */ - { - /* The signal seems to be passive speech */ - w16_VAD = 0; - w16_bestCorr = 0; /* Correlation does not matter */ - - /* For low energy expansion, the new data can be less than 15 ms, - but we must ensure that bestIndex is not larger than the new data. */ - w16_bestIndex = WEBRTC_SPL_MIN( w16_bestIndex, len - oldDataLen ); - } - else - { - /* The signal is active speech */ - w16_VAD = 1; - - /* Calculate correlation (cc/sqrt(en1*en2)) */ - - /* Start with calculating scale values */ - w16_en1Scale = 16 - WebRtcSpl_NormW32(w32_en1); - w16_en1Scale = WEBRTC_SPL_MAX(0, w16_en1Scale); - w16_en2Scale = 16 - WebRtcSpl_NormW32(w32_en2); - w16_en2Scale = WEBRTC_SPL_MAX(0, w16_en2Scale); - - /* Make sure total scaling is even (to simplify scale factor after sqrt) */ - if ((w16_en1Scale + w16_en2Scale) & 1) - { - w16_en1Scale += 1; - } - - /* Convert energies to WebRtc_Word16 */ - w16_en1 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en1, w16_en1Scale); - w16_en2 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(w32_en2, w16_en2Scale); - - /* Calculate energy product */ - w32_tmp = WEBRTC_SPL_MUL_16_16(w16_en1, w16_en2); - - /* Calculate square-root of energy product */ - w16_sqrtEn1En2 = (WebRtc_Word16) WebRtcSpl_Sqrt(w32_tmp); - - /* Calculate cc/sqrt(en1*en2) in Q14 */ - w16_tmp = 14 - ((w16_en1Scale + w16_en2Scale) >> 1); - w32_cc = WEBRTC_SPL_SHIFT_W32(w32_cc, w16_tmp); - w32_cc = WEBRTC_SPL_MAX(0, w32_cc); /* Don't divide with negative number */ - w16_bestCorr = (WebRtc_Word16) WebRtcSpl_DivW32W16(w32_cc, w16_sqrtEn1En2); - w16_bestCorr = WEBRTC_SPL_MIN(16384, w16_bestCorr); /* set maximum to 1.0 */ - } - -#ifdef NETEQ_STEREO - - } /* end if (msInfo->msMode != NETEQ_SLAVE) */ - -#endif /* NETEQ_STEREO */ - - /*******************************************************/ - /* Check preemptive expand criteria and insert samples */ - /*******************************************************/ - - /* Check for strong correlation (>0.9) and at least 15 ms new data, - or passive speech */ -#ifdef NETEQ_STEREO - if (((((w16_bestCorr > 14746) && (oldDataLen <= fsMult120)) || (w16_VAD == 0)) - && (msInfo->msMode != NETEQ_SLAVE)) || ((msInfo->msMode == NETEQ_SLAVE) - && (msInfo->extraInfo != PE_EXP_FAIL))) -#else - if (((w16_bestCorr > 14746) && (oldDataLen <= fsMult120)) - || (w16_VAD == 0)) -#endif - { - /* Do expand operation by overlap add */ - - /* Set length of the first part, not to be modified */ - WebRtc_Word16 w16_startIndex = WEBRTC_SPL_MAX(oldDataLen, fsMult120); - - /* - * Calculate cross-fading slope so that the fading factor goes from - * 1 (16384 in Q14) to 0 in one pitch period (bestIndex). - */ - w16_inc = (WebRtc_Word16) WebRtcSpl_DivW32W16((WebRtc_Word32) 16384, - (WebRtc_Word16) (w16_bestIndex + 1)); /* in Q14 */ - - /* Initiate fading factor */ - w16_startfact = 16384 - w16_inc; - - /* vec1 starts at 15 ms minus one pitch period */ - pw16_vec1 = &pw16_decoded[w16_startIndex - w16_bestIndex]; - /* vec2 start at 15 ms */ - pw16_vec2 = &pw16_decoded[w16_startIndex]; - - - /* Copy unmodified part [0 to 15 ms] */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, w16_startIndex); - - /* Generate interpolated part of length bestIndex (1 pitch period) */ - pw16_vectmp = pw16_outData + w16_startIndex; - /* Reuse mixing function from Expand */ - WebRtcNetEQ_MixVoiceUnvoice(pw16_vectmp, (WebRtc_Word16*) pw16_vec2, - (WebRtc_Word16*) pw16_vec1, &w16_startfact, w16_inc, w16_bestIndex); - - /* Move the last part (also unmodified) */ - /* Take from decoded at 15 ms */ - pw16_vec2 = &pw16_decoded[w16_startIndex]; - WEBRTC_SPL_MEMMOVE_W16(&pw16_outData[w16_startIndex + w16_bestIndex], pw16_vec2, - (WebRtc_Word16) (len - w16_startIndex)); - - /* Set the mode flag */ - if (w16_VAD) - { - inst->w16_mode = MODE_SUCCESS_PREEMPTIVE; - } - else - { - inst->w16_mode = MODE_LOWEN_PREEMPTIVE; - } - - /* Calculate resulting length = original length + pitch period */ - *pw16_len = len + w16_bestIndex; - - /* Update in-call statistics */ - inst->statInst.preemptiveLength += w16_bestIndex; - - return 0; - } - else - { - /* Preemptive Expand not allowed */ - -#ifdef NETEQ_STEREO - /* Signal to slave(s) that this was unsuccessful */ - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->extraInfo = PE_EXP_FAIL; - } -#endif - - /* Set mode flag to unsuccessful preemptive expand */ - inst->w16_mode = MODE_UNSUCCESS_PREEMPTIVE; - - /* Length is unmodified */ - *pw16_len = len; - - - /* Simply move all data from decoded to outData */ - - WEBRTC_SPL_MEMMOVE_W16(pw16_outData, pw16_decoded, (WebRtc_Word16) len); - - return 0; - } -} - -#undef SCRATCH_PW16_DS_SPEECH -#undef SCRATCH_PW32_CORR -#undef SCRATCH_PW16_CORR - diff --git a/modules/audio_coding/NetEQ/main/source/random_vector.c b/modules/audio_coding/NetEQ/main/source/random_vector.c deleted file mode 100644 index 217bacda7..000000000 --- a/modules/audio_coding/NetEQ/main/source/random_vector.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function generates a pseudo-random vector. - */ - -#include "dsp_helpfunctions.h" - -/* - * Values are normalized so that - * sqrt(dot(pw16_NETEQFIX_RANDN_TBL,pw16_NETEQFIX_RANDN_TBL)/256)=2^13 - */ -const WebRtc_Word16 WebRtcNetEQ_kRandnTbl[RANDVEC_NO_OF_SAMPLES] = -{ - 2680, 5532, 441, 5520, 16170, -5146, -1024, -8733, 3115, 9598, -10380, -4959, -1280, -21716, 7133, -1522, - 13458, -3902, 2789, -675, 3441, 5016, -13599, -4003, -2739, 3922, -7209, 13352, -11617, -7241, 12905, -2314, - 5426, 10121, -9702, 11207, -13542, 1373, 816, -5934, -12504, 4798, 1811, 4112, -613, 201, -10367, -2960, - -2419, 3442, 4299, -6116, -6092, 1552, -1650, -480, -1237, 18720, -11858, -8303, -8212, 865, -2890, -16968, - 12052, -5845, -5912, 9777, -5665, -6294, 5426, -4737, -6335, 1652, 761, 3832, 641, -8552, -9084, -5753, - 8146, 12156, -4915, 15086, -1231, -1869, 11749, -9319, -6403, 11407, 6232, -1683, 24340, -11166, 4017, -10448, - 3153, -2936, 6212, 2891, -866, -404, -4807, -2324, -1917, -2388, -6470, -3895, -10300, 5323, -5403, 2205, - 4640, 7022, -21186, -6244, -882, -10031, -3395, -12885, 7155, -5339, 5079, -2645, -9515, 6622, 14651, 15852, - 359, 122, 8246, -3502, -6696, -3679, -13535, -1409, -704, -7403, -4007, 1798, 279, -420, -12796, -14219, - 1141, 3359, 11434, 7049, -6684, -7473, 14283, -4115, -9123, -8969, 4152, 4117, 13792, 5742, 16168, 8661, - -1609, -6095, 1881, 14380, -5588, 6758, -6425, -22969, -7269, 7031, 1119, -1611, -5850, -11281, 3559, -8952, - -10146, -4667, -16251, -1538, 2062, -1012, -13073, 227, -3142, -5265, 20, 5770, -7559, 4740, -4819, 992, - -8208, -7130, -4652, 6725, 7369, -1036, 13144, -1588, -5304, -2344, -449, -5705, -8894, 5205, -17904, -11188, - -1022, 4852, 10101, -5255, -4200, -752, 7941, -1543, 5959, 14719, 13346, 17045, -15605, -1678, -1600, -9230, - 68, 23348, 1172, 7750, 11212, -18227, 9956, 4161, 883, 3947, 4341, 1014, -4889, -2603, 1246, -5630, - -3596, -870, -1298, 2784, -3317, -6612, -20541, 4166, 4181, -8625, 3562, 12890, 4761, 3205, -12259, -8579 -}; - - -void WebRtcNetEQ_RandomVec(WebRtc_UWord32 *w32_seed, WebRtc_Word16 *pw16_randVec, - WebRtc_Word16 w16_len, WebRtc_Word16 w16_incval) -{ - int i; - WebRtc_Word16 w16_pos; - for (i = 0; i < w16_len; i++) - { - *w32_seed = (*w32_seed) + w16_incval; - w16_pos = (WebRtc_Word16) ((*w32_seed) & (RANDVEC_NO_OF_SAMPLES - 1)); - pw16_randVec[i] = WebRtcNetEQ_kRandnTbl[w16_pos]; - } -} - diff --git a/modules/audio_coding/NetEQ/main/source/recin.c b/modules/audio_coding/NetEQ/main/source/recin.c deleted file mode 100644 index b7fb5f273..000000000 --- a/modules/audio_coding/NetEQ/main/source/recin.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of the RecIn function, which is the main function for inserting RTP - * packets into NetEQ. - */ - -#include "mcu.h" - -#include - -#include "signal_processing_library.h" - -#include "automode.h" -#include "dtmf_buffer.h" -#include "neteq_defines.h" -#include "neteq_error_codes.h" - - -int WebRtcNetEQ_RecInInternal(MCUInst_t *MCU_inst, RTPPacket_t *RTPpacketInput, - WebRtc_UWord32 uw32_timeRec) -{ - RTPPacket_t RTPpacket[2]; - int i_k; - int i_ok = 0, i_No_Of_Payloads = 1; - WebRtc_Word16 flushed = 0; - WebRtc_Word16 codecPos; - WebRtc_UWord32 diffTS, uw32_tmp; - int curr_Codec; - WebRtc_Word16 isREDPayload = 0; - WebRtc_Word32 temp_bufsize = MCU_inst->PacketBuffer_inst.numPacketsInBuffer; -#ifdef NETEQ_RED_CODEC - RTPPacket_t* RTPpacketPtr[2]; /* Support for redundancy up to 2 payloads */ - RTPpacketPtr[0] = &RTPpacket[0]; - RTPpacketPtr[1] = &RTPpacket[1]; -#endif - - /* - * Copy from input RTP packet to local copy - * (mainly to enable multiple payloads using RED) - */ - - WEBRTC_SPL_MEMCPY_W8(&RTPpacket[0], RTPpacketInput, sizeof(RTPPacket_t)); - - /* Reinitialize NetEq if it's needed (changed SSRC or first call) */ - - if ((RTPpacket[0].ssrc != MCU_inst->ssrc) || (MCU_inst->first_packet == 1)) - { - WebRtcNetEQ_RTCPInit(&MCU_inst->RTCP_inst, RTPpacket[0].seqNumber); - MCU_inst->first_packet = 0; - - /* Flush the buffer */ - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - - /* Store new SSRC */ - MCU_inst->ssrc = RTPpacket[0].ssrc; - - /* Update codecs */ - MCU_inst->timeStamp = RTPpacket[0].timeStamp; - MCU_inst->current_Payload = RTPpacket[0].payloadType; - - /*Set MCU to update codec on next SignalMCU call */ - MCU_inst->new_codec = 1; - - /* Reset timestamp scaling */ - MCU_inst->TSscalingInitialized = 0; - - } - - /* Call RTCP statistics */ - i_ok |= WebRtcNetEQ_RTCPUpdate(&(MCU_inst->RTCP_inst), RTPpacket[0].seqNumber, - RTPpacket[0].timeStamp, uw32_timeRec); - - /* If Redundancy is supported and this is the redundancy payload, separate the payloads */ -#ifdef NETEQ_RED_CODEC - if (RTPpacket[0].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderRED)) - { - - /* Split the payload into a main and a redundancy payloads */ - i_ok = WebRtcNetEQ_RedundancySplit(RTPpacketPtr, 2, &i_No_Of_Payloads); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - - /* - * Only accept a few redundancies of the same type as the main data, - * AVT events and CNG. - */ - if ((i_No_Of_Payloads > 1) && (RTPpacket[0].payloadType != RTPpacket[1].payloadType) - && (RTPpacket[0].payloadType != WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderAVT)) && (RTPpacket[1].payloadType != WebRtcNetEQ_DbGetPayload( - &MCU_inst->codec_DB_inst, kDecoderAVT)) && (!WebRtcNetEQ_DbIsCNGPayload( - &MCU_inst->codec_DB_inst, RTPpacket[0].payloadType)) - && (!WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst, RTPpacket[1].payloadType))) - { - i_No_Of_Payloads = 1; - } - isREDPayload = 1; - } -#endif - - /* loop over the number of payloads */ - for (i_k = 0; i_k < i_No_Of_Payloads; i_k++) - { - - if (isREDPayload == 1) - { - RTPpacket[i_k].rcuPlCntr = i_k; - } - else - { - RTPpacket[i_k].rcuPlCntr = 0; - } - - /* Force update of SplitInfo if it's iLBC because of potential change between 20/30ms */ - if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderILBC)) - { - i_ok = WebRtcNetEQ_DbGetSplitInfo( - &MCU_inst->PayloadSplit_inst, - (enum WebRtcNetEQDecoder) WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType), RTPpacket[i_k].payloadLen); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - } - - /* Get information about timestamp scaling for this payload type */ - i_ok = WebRtcNetEQ_GetTimestampScaling(MCU_inst, RTPpacket[i_k].payloadType); - if (i_ok < 0) - { - /* error returned */ - return i_ok; - } - - if (MCU_inst->TSscalingInitialized == 0 && MCU_inst->scalingFactor != kTSnoScaling) - { - /* Must initialize scaling with current timestamps */ - MCU_inst->externalTS = RTPpacket[i_k].timeStamp; - MCU_inst->internalTS = RTPpacket[i_k].timeStamp; - MCU_inst->TSscalingInitialized = 1; - } - - /* Adjust timestamp if timestamp scaling is needed (e.g. SILK or G.722) */ - if (MCU_inst->TSscalingInitialized == 1) - { - WebRtc_UWord32 newTS = WebRtcNetEQ_ScaleTimestampExternalToInternal(MCU_inst, - RTPpacket[i_k].timeStamp); - - /* save the incoming timestamp for next time */ - MCU_inst->externalTS = RTPpacket[i_k].timeStamp; - - /* add the scaled difference to last scaled timestamp and save ... */ - MCU_inst->internalTS = newTS; - - RTPpacket[i_k].timeStamp = newTS; - } - - /* Is this a DTMF packet?*/ - if (RTPpacket[i_k].payloadType == WebRtcNetEQ_DbGetPayload(&MCU_inst->codec_DB_inst, - kDecoderAVT)) - { -#ifdef NETEQ_ATEVENT_DECODE - if (MCU_inst->AVT_PlayoutOn) - { - i_ok = WebRtcNetEQ_DtmfInsertEvent(&MCU_inst->DTMF_inst, - RTPpacket[i_k].payload, RTPpacket[i_k].payloadLen, - RTPpacket[i_k].timeStamp); - if (i_ok != 0) - { - return i_ok; - } - } -#endif -#ifdef NETEQ_STEREO - if (MCU_inst->usingStereo == 0) - { - /* do not set this for DTMF packets when using stereo mode */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; - } -#else - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; -#endif - } - else if (WebRtcNetEQ_DbIsCNGPayload(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType)) - { - /* Is this a CNG packet? how should we handle this?*/ -#ifdef NETEQ_CNG_CODEC - /* Get CNG sample rate */ - WebRtc_UWord16 fsCng = WebRtcNetEQ_DbGetSampleRate(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType); - if ((fsCng != MCU_inst->fs) && (fsCng > 8000)) - { - /* - * We have received CNG with a different sample rate from what we are using - * now (must be > 8000, since we may use only one CNG type (default) for all - * frequencies). Flush buffer and signal new codec. - */ - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - MCU_inst->new_codec = 1; - MCU_inst->current_Codec = -1; - } - i_ok = WebRtcNetEQ_PacketBufferInsert(&MCU_inst->PacketBuffer_inst, - &RTPpacket[i_k], &flushed); - if (i_ok < 0) - { - return RECIN_CNG_ERROR; - } - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 1; -#else /* NETEQ_CNG_CODEC not defined */ - return RECIN_UNKNOWNPAYLOAD; -#endif /* NETEQ_CNG_CODEC */ - } - else - { - /* Reinitialize the splitting if the payload and/or the payload length has changed */ - curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[i_k].payloadType); - if (curr_Codec != MCU_inst->current_Codec) - { - if (curr_Codec < 0) - { - return RECIN_UNKNOWNPAYLOAD; - } - MCU_inst->current_Codec = curr_Codec; - MCU_inst->current_Payload = RTPpacket[i_k].payloadType; - i_ok = WebRtcNetEQ_DbGetSplitInfo(&MCU_inst->PayloadSplit_inst, - (enum WebRtcNetEQDecoder) MCU_inst->current_Codec, - RTPpacket[i_k].payloadLen); - if (i_ok < 0) - { /* error returned */ - return i_ok; - } - WebRtcNetEQ_PacketBufferFlush(&MCU_inst->PacketBuffer_inst); - MCU_inst->new_codec = 1; - } - - /* update post-call statistics */ - if (MCU_inst->new_codec != 1) /* not if in codec change */ - { - diffTS = RTPpacket[i_k].timeStamp - MCU_inst->timeStamp; /* waiting time */ - if (diffTS < 0x0FFFFFFF) /* guard against re-ordering */ - { - /* waiting time in ms */ - diffTS = WEBRTC_SPL_UDIV(diffTS, - WEBRTC_SPL_UDIV((WebRtc_UWord32) MCU_inst->fs, 1000) ); - if (diffTS < MCU_inst->statInst.minPacketDelayMs) - { - /* new all-time low */ - MCU_inst->statInst.minPacketDelayMs = diffTS; - } - if (diffTS > MCU_inst->statInst.maxPacketDelayMs) - { - /* new all-time high */ - MCU_inst->statInst.maxPacketDelayMs = diffTS; - } - - /* Update avg waiting time: - * avgPacketDelayMs = - * (avgPacketCount * avgPacketDelayMs + diffTS)/(avgPacketCount+1) - * with proper rounding. - */ - uw32_tmp - = WEBRTC_SPL_UMUL((WebRtc_UWord32) MCU_inst->statInst.avgPacketCount, - (WebRtc_UWord32) MCU_inst->statInst.avgPacketDelayMs); - uw32_tmp - = WEBRTC_SPL_ADD_SAT_W32(uw32_tmp, - (diffTS + (MCU_inst->statInst.avgPacketCount>>1))); - uw32_tmp = WebRtcSpl_DivU32U16(uw32_tmp, - (WebRtc_UWord16) (MCU_inst->statInst.avgPacketCount + 1)); - MCU_inst->statInst.avgPacketDelayMs - = (WebRtc_UWord16) WEBRTC_SPL_MIN(uw32_tmp, (WebRtc_UWord32) 65535); - - /* increase counter, but not to more than 65534 */ - if (MCU_inst->statInst.avgPacketCount < (0xFFFF - 1)) - { - MCU_inst->statInst.avgPacketCount++; - } - } - } - - /* Parse the payload and insert it into the buffer */ - i_ok = WebRtcNetEQ_SplitAndInsertPayload(&RTPpacket[i_k], - &MCU_inst->PacketBuffer_inst, &MCU_inst->PayloadSplit_inst, &flushed); - if (i_ok < 0) - { - return i_ok; - } - if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF != 0) - { - /* first normal packet after CNG or DTMF */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = -1; - } - } - /* Reset DSP timestamp etc. if packet buffer flushed */ - if (flushed) - { - MCU_inst->new_codec = 1; - } - } - - /* - * Update Bandwidth Estimate - * Only send the main payload to BWE - */ - if ((curr_Codec = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, - RTPpacket[0].payloadType)) >= 0) - { - codecPos = MCU_inst->codec_DB_inst.position[curr_Codec]; - if (MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos] != NULL) /* codec has BWE function */ - { - if (RTPpacket[0].starts_byte1) /* check for shifted byte alignment */ - { - /* re-align to 16-bit alignment */ - for (i_k = 0; i_k < RTPpacket[0].payloadLen; i_k++) - { - WEBRTC_SPL_SET_BYTE(RTPpacket[0].payload, - WEBRTC_SPL_GET_BYTE(RTPpacket[0].payload, i_k+1), - i_k); - } - RTPpacket[0].starts_byte1 = 0; - } - - MCU_inst->codec_DB_inst.funcUpdBWEst[codecPos]( - MCU_inst->codec_DB_inst.codec_state[codecPos], - (G_CONST WebRtc_UWord16 *) RTPpacket[0].payload, - (WebRtc_Word32) RTPpacket[0].payloadLen, RTPpacket[0].seqNumber, - (WebRtc_UWord32) RTPpacket[0].timeStamp, (WebRtc_UWord32) uw32_timeRec); - } - } - - if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == 0) - { - /* Calculate the total speech length carried in each packet */ - temp_bufsize = MCU_inst->PacketBuffer_inst.numPacketsInBuffer - temp_bufsize; - temp_bufsize *= MCU_inst->PacketBuffer_inst.packSizeSamples; - - if ((temp_bufsize > 0) && (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF - == 0) && (temp_bufsize - != MCU_inst->BufferStat_inst.Automode_inst.packetSpeechLenSamp)) - { - /* Change the auto-mode parameters if packet length has changed */ - WebRtcNetEQ_SetPacketSpeechLen(&(MCU_inst->BufferStat_inst.Automode_inst), - (WebRtc_Word16) temp_bufsize, MCU_inst->fs); - } - - /* update statistics */ - if ((WebRtc_Word32) (RTPpacket[0].timeStamp - MCU_inst->timeStamp) >= 0 - && !MCU_inst->new_codec) - { - /* - * Only update statistics if incoming packet is not older than last played out - * packet, and if new codec flag is not set. - */ - WebRtcNetEQ_UpdateIatStatistics(&MCU_inst->BufferStat_inst.Automode_inst, - MCU_inst->PacketBuffer_inst.maxInsertPositions, RTPpacket[0].seqNumber, - RTPpacket[0].timeStamp, MCU_inst->fs, - WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) MCU_inst->current_Codec), - (MCU_inst->NetEqPlayoutMode == kPlayoutStreaming)); - } - } - else if (MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF == -1) - { - /* - * This is first "normal" packet after CNG or DTMF. - * Reset packet time counter and measure time until next packet, - * but don't update statistics. - */ - MCU_inst->BufferStat_inst.Automode_inst.lastPackCNGorDTMF = 0; - MCU_inst->BufferStat_inst.Automode_inst.packetIatCountSamp = 0; - } - return 0; - -} - -int WebRtcNetEQ_GetTimestampScaling(MCUInst_t *MCU_inst, int rtpPayloadType) -{ - enum WebRtcNetEQDecoder codec; - int codecNumber; - - codecNumber = WebRtcNetEQ_DbGetCodec(&MCU_inst->codec_DB_inst, rtpPayloadType); - if (codecNumber < 0) - { - /* error */ - return codecNumber; - } - - /* cast to enumerator */ - codec = (enum WebRtcNetEQDecoder) codecNumber; - - /* - * The factor obtained below is the number with which the RTP timestamp must be - * multiplied to get the true sample count. - */ - switch (codec) - { - case kDecoderG722: - { - /* Use timestamp scaling with factor 2 (two output samples per RTP timestamp) */ - MCU_inst->scalingFactor = kTSscalingTwo; - break; - } - case kDecoderAVT: - case kDecoderCNG: - { - /* do not change the timestamp scaling settings */ - break; - } - default: - { - /* do not use timestamp scaling */ - MCU_inst->scalingFactor = kTSnoScaling; - break; - } - } - return 0; -} - -WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampExternalToInternal(const MCUInst_t *MCU_inst, - WebRtc_UWord32 externalTS) -{ - WebRtc_Word32 timestampDiff; - WebRtc_UWord32 internalTS; - - /* difference between this and last incoming timestamp */ - timestampDiff = externalTS - MCU_inst->externalTS; - - switch (MCU_inst->scalingFactor) - { - case kTSscalingTwo: - { - /* multiply with 2 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingTwoThirds: - { - /* multiply with 2/3 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 1); - timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3); - break; - } - case kTSscalingFourThirds: - { - /* multiply with 4/3 */ - timestampDiff = WEBRTC_SPL_LSHIFT_W32(timestampDiff, 2); - timestampDiff = WebRtcSpl_DivW32W16(timestampDiff, 3); - break; - } - default: - { - /* no scaling */ - } - } - - /* add the scaled difference to last scaled timestamp and save ... */ - internalTS = MCU_inst->internalTS + timestampDiff; - - return internalTS; -} - -WebRtc_UWord32 WebRtcNetEQ_ScaleTimestampInternalToExternal(const MCUInst_t *MCU_inst, - WebRtc_UWord32 internalTS) -{ - WebRtc_Word32 timestampDiff; - WebRtc_UWord32 externalTS; - - /* difference between this and last incoming timestamp */ - timestampDiff = (WebRtc_Word32) internalTS - MCU_inst->internalTS; - - switch (MCU_inst->scalingFactor) - { - case kTSscalingTwo: - { - /* divide by 2 */ - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingTwoThirds: - { - /* multiply with 3/2 */ - timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3); - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 1); - break; - } - case kTSscalingFourThirds: - { - /* multiply with 3/4 */ - timestampDiff = WEBRTC_SPL_MUL_32_16(timestampDiff, 3); - timestampDiff = WEBRTC_SPL_RSHIFT_W32(timestampDiff, 2); - break; - } - default: - { - /* no scaling */ - } - } - - /* add the scaled difference to last scaled timestamp and save ... */ - externalTS = MCU_inst->externalTS + timestampDiff; - - return externalTS; -} diff --git a/modules/audio_coding/NetEQ/main/source/recout.c b/modules/audio_coding/NetEQ/main/source/recout.c deleted file mode 100644 index 63296f90e..000000000 --- a/modules/audio_coding/NetEQ/main/source/recout.c +++ /dev/null @@ -1,1427 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of RecOut function, which is the main function for the audio output - * process. This function must be called (through the NetEQ API) once every 10 ms. - */ - -#include "dsp.h" - -#include -#include /* to define NULL */ - -#include "signal_processing_library.h" - -#include "dsp_helpfunctions.h" -#include "neteq_error_codes.h" -#include "neteq_defines.h" -#include "mcu_dsp_common.h" - -/* Audio types */ -#define TYPE_SPEECH 1 -#define TYPE_CNG 2 - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#include -#pragma message("*******************************************************************") -#pragma message("You have specified to use NETEQ_DELAY_LOGGING in the NetEQ library.") -#pragma message("Make sure that your test application supports this.") -#pragma message("*******************************************************************") -#endif - -/* Scratch usage: - - Type Name size startpos endpos - WebRtc_Word16 pw16_NetEqAlgorithm_buffer 600*fs/8000 0 600*fs/8000-1 - struct dspInfo 6 600*fs/8000 605*fs/8000 - - func WebRtcNetEQ_Normal 40+495*fs/8000 0 39+495*fs/8000 - func WebRtcNetEQ_Merge 40+496*fs/8000 0 39+496*fs/8000 - func WebRtcNetEQ_Expand 40+370*fs/8000 126*fs/800 39+496*fs/8000 - func WebRtcNetEQ_Accelerate 210 240*fs/8000 209+240*fs/8000 - func WebRtcNetEQ_BGNUpdate 69 480*fs/8000 68+480*fs/8000 - - Total: 605*fs/8000 - */ - -#define SCRATCH_ALGORITHM_BUFFER 0 -#define SCRATCH_NETEQ_NORMAL 0 -#define SCRATCH_NETEQ_MERGE 0 - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SCRATCH_DSP_INFO 3600 -#define SCRATCH_NETEQ_ACCELERATE 1440 -#define SCRATCH_NETEQ_BGN_UPDATE 2880 -#define SCRATCH_NETEQ_EXPAND 756 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SCRATCH_DSP_INFO 2400 -#define SCRATCH_NETEQ_ACCELERATE 960 -#define SCRATCH_NETEQ_BGN_UPDATE 1920 -#define SCRATCH_NETEQ_EXPAND 504 -#elif (defined(NETEQ_WIDEBAND)) -#define SCRATCH_DSP_INFO 1200 -#define SCRATCH_NETEQ_ACCELERATE 480 -#define SCRATCH_NETEQ_BGN_UPDATE 960 -#define SCRATCH_NETEQ_EXPAND 252 -#else /* NB */ -#define SCRATCH_DSP_INFO 600 -#define SCRATCH_NETEQ_ACCELERATE 240 -#define SCRATCH_NETEQ_BGN_UPDATE 480 -#define SCRATCH_NETEQ_EXPAND 126 -#endif - -#if (defined(NETEQ_48KHZ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 3636 -#elif (defined(NETEQ_32KHZ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 2424 -#elif (defined(NETEQ_WIDEBAND)) -#define SIZE_SCRATCH_BUFFER 1212 -#else /* NB */ -#define SIZE_SCRATCH_BUFFER 606 -#endif - -#ifdef NETEQ_DELAY_LOGGING -extern FILE *delay_fid2; /* file pointer to delay log file */ -extern WebRtc_UWord32 tot_received_packets; -#endif - - -int WebRtcNetEQ_RecOutInternal(DSPInst_t *inst, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len, WebRtc_Word16 BGNonly) -{ - - WebRtc_Word16 blockLen, payloadLen, len = 0, pos; - WebRtc_Word16 w16_tmp1, w16_tmp2, w16_tmp3, DataEnough; - WebRtc_Word16 *blockPtr; - WebRtc_Word16 MD = 0; - - WebRtc_Word16 speechType = TYPE_SPEECH; - WebRtc_UWord16 instr; - WebRtc_UWord16 uw16_tmp; -#ifdef SCRATCH - char pw8_ScratchBuffer[((SIZE_SCRATCH_BUFFER + 1) * 2)]; - WebRtc_Word16 *pw16_scratchPtr = (WebRtc_Word16*) pw8_ScratchBuffer; - WebRtc_Word16 pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE]; - WebRtc_Word16 *pw16_NetEqAlgorithm_buffer = pw16_scratchPtr - + SCRATCH_ALGORITHM_BUFFER; - DSP2MCU_info_t *dspInfo = (DSP2MCU_info_t*) (pw16_scratchPtr + SCRATCH_DSP_INFO); -#else - WebRtc_Word16 pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE]; - WebRtc_Word16 pw16_NetEqAlgorithm_buffer[NETEQ_MAX_OUTPUT_SIZE]; - DSP2MCU_info_t dspInfoStruct; - DSP2MCU_info_t *dspInfo = &dspInfoStruct; -#endif - WebRtc_Word16 fs_mult; - int borrowedSamples; - int oldBorrowedSamples; - int return_value = 0; - WebRtc_Word16 lastModeBGNonly = (inst->w16_mode & MODE_BGN_ONLY) != 0; /* check BGN flag */ - void *mainInstBackup = inst->main_inst; - -#ifdef NETEQ_DELAY_LOGGING - int i, j, temp_var; -#endif - WebRtc_Word16 dtmfValue = -1; - WebRtc_Word16 dtmfVolume = -1; - int playDtmf = 0; - int dtmfSwitch = 0; -#ifdef NETEQ_STEREO - MasterSlaveInfo *msInfo = inst->msInfo; -#endif - WebRtc_Word16 *sharedMem = pw16_NetEqAlgorithm_buffer; /* Reuse memory SHARED_MEM_SIZE size */ - inst->pw16_readAddress = sharedMem; - inst->pw16_writeAddress = sharedMem; - - /* Get information about if there is one descriptor left */ - if (inst->codec_ptr_inst.funcGetMDinfo != NULL) - { - MD = inst->codec_ptr_inst.funcGetMDinfo(inst->codec_ptr_inst.codec_state); - if (MD > 0) - MD = 1; - else - MD = 0; - } - -#ifdef NETEQ_STEREO - if ((msInfo->msMode == NETEQ_SLAVE) && (inst->codec_ptr_inst.funcDecode != NULL)) - { - /* - * Valid function pointers indicate that we have decoded something, - * and that the timestamp information is correct. - */ - - /* Get the information from master to correct synchronization */ - WebRtc_UWord32 currentMasterTimestamp; - WebRtc_UWord32 currentSlaveTimestamp; - - currentMasterTimestamp = msInfo->endTimestamp - msInfo->samplesLeftWithOverlap; - currentSlaveTimestamp = inst->endTimestamp - (inst->endPosition - inst->curPosition); - - if (currentSlaveTimestamp < currentMasterTimestamp) - { - /* brute-force discard a number of samples to catch up */ - inst->curPosition += currentMasterTimestamp - currentSlaveTimestamp; - - /* make sure we have at least "overlap" samples left */ - inst->curPosition = WEBRTC_SPL_MIN(inst->curPosition, - inst->endPosition - inst->ExpandInst.w16_overlap); - } - else if (currentSlaveTimestamp > currentMasterTimestamp) - { - /* back off current position to slow down */ - inst->curPosition -= currentSlaveTimestamp - currentMasterTimestamp; - - /* make sure we do not end up outside the speech history */ - inst->curPosition = WEBRTC_SPL_MAX(inst->curPosition, 0); - } - } -#endif - - /* Write status data to shared memory */ - dspInfo->playedOutTS = inst->endTimestamp; - dspInfo->samplesLeft = inst->endPosition - inst->curPosition - - inst->ExpandInst.w16_overlap; - dspInfo->MD = MD; - dspInfo->lastMode = inst->w16_mode; - dspInfo->frameLen = inst->w16_frameLen; - - /* Force update of codec if codec function is NULL */ - if (inst->codec_ptr_inst.funcDecode == NULL) - { - dspInfo->lastMode |= MODE_AWAITING_CODEC_PTR; - } - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_SLAVE && (msInfo->extraInfo == DTMF_OVERDUB - || msInfo->extraInfo == DTMF_ONLY)) - { - /* Signal that the master instance generated DTMF tones */ - dspInfo->lastMode |= MODE_MASTER_DTMF_SIGNAL; - } - - if (msInfo->msMode != NETEQ_MONO) - { - /* We are using stereo mode; signal this to MCU side */ - dspInfo->lastMode |= MODE_USING_STEREO; - } -#endif - - WEBRTC_SPL_MEMCPY_W8(inst->pw16_writeAddress,dspInfo,sizeof(DSP2MCU_info_t)); - - /* Signal MCU with "interrupt" call to main inst*/ -#ifdef NETEQ_STEREO - assert(msInfo != NULL); - if (msInfo->msMode == NETEQ_MASTER) - { - /* clear info to slave */ - WebRtcSpl_MemSetW16((WebRtc_Word16 *) msInfo, 0, - sizeof(MasterSlaveInfo) / sizeof(WebRtc_Word16)); - /* re-set mode */ - msInfo->msMode = NETEQ_MASTER; - - /* Store some information to slave */ - msInfo->endTimestamp = inst->endTimestamp; - msInfo->samplesLeftWithOverlap = inst->endPosition - inst->curPosition; - } -#endif - - /* - * This call will trigger the MCU side to make a decision based on buffer contents and - * decision history. Instructions, encoded data and function pointers will be written - * to the shared memory. - */ - return_value = WebRtcNetEQ_DSP2MCUinterrupt((MainInst_t *) inst->main_inst, sharedMem); - - /* Read MCU data and instructions */ - instr = (WebRtc_UWord16) (inst->pw16_readAddress[0] & 0xf000); - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - msInfo->instruction = instr; - } - else if (msInfo->msMode == NETEQ_SLAVE) - { - /* Nothing to do */ - } -#endif - - /* check for error returned from MCU side, if so, return error */ - if (return_value < 0) - { - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return return_value; - } - - blockPtr = &((inst->pw16_readAddress)[3]); - - /* Check for DTMF payload flag */ - if ((inst->pw16_readAddress[0] & DSP_DTMF_PAYLOAD) != 0) - { - playDtmf = 1; - dtmfValue = blockPtr[1]; - dtmfVolume = blockPtr[2]; - blockPtr += 3; - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - /* signal to slave that master is using DTMF */ - msInfo->extraInfo = DTMF_OVERDUB; - } -#endif - } - - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of WebRtc_Word16 */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - - /* Do we have to change our decoder? */ - if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_NEW_CODEC) - { - WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1); - if (inst->codec_ptr_inst.codec_fs != 0) - { - return_value = WebRtcNetEQ_DSPInit(inst, inst->codec_ptr_inst.codec_fs); - if (return_value != 0) - { /* error returned */ - instr = DSP_INSTR_FADE_TO_BGN; /* emergency instruction */ - } -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - fwrite(&inst->fs, sizeof(WebRtc_UWord16), 1, delay_fid2); -#endif - } - - /* Copy it again since the init destroys this part */ - - WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1); - inst->endTimestamp = inst->codec_ptr_inst.timeStamp; - inst->videoSyncTimestamp = inst->codec_ptr_inst.timeStamp; - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - if (inst->codec_ptr_inst.funcDecodeInit != NULL) - { - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - -#ifdef NETEQ_CNG_CODEC - - /* Also update the CNG state as this might be uninitialized */ - - WEBRTC_SPL_MEMCPY_W16(&inst->CNG_Codec_inst,blockPtr,(payloadLen+1)>>1); - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - if (inst->CNG_Codec_inst != NULL) - { - WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif - } - else if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_RESET) - { - /* Reset the current codec (but not DSP struct) */ - if (inst->codec_ptr_inst.funcDecodeInit != NULL) - { - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - -#ifdef NETEQ_CNG_CODEC - /* And reset CNG */ - if (inst->CNG_Codec_inst != NULL) - { - WebRtcCng_InitDec(inst->CNG_Codec_inst); - } -#endif /*NETEQ_CNG_CODEC*/ - } - - fs_mult = WebRtcNetEQ_CalcFsMult(inst->fs); - - /* Add late packet? */ - if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_ADD_LATE_PKT) - { - if (inst->codec_ptr_inst.funcAddLatePkt != NULL) - { - /* Only do this if the codec has support for Add Late Pkt */ - inst->codec_ptr_inst.funcAddLatePkt(inst->codec_ptr_inst.codec_state, blockPtr, - payloadLen); - } - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - } - - /* Do we have to decode data? */ - if ((instr == DSP_INSTR_NORMAL) || (instr == DSP_INSTR_ACCELERATE) || (instr - == DSP_INSTR_MERGE) || (instr == DSP_INSTR_PREEMPTIVE_EXPAND)) - { - /* Do we need to update codec-internal PLC state? */ - if ((instr == DSP_INSTR_MERGE) && (inst->codec_ptr_inst.funcDecodePLC != NULL)) - { - len = 0; - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - &pw16_decoded_buffer[len], 1); - } - len = 0; - - /* Do decoding */ - while ((blockLen > 0) && (len < (240 * fs_mult))) /* Guard somewhat against overflow */ - { - if (inst->codec_ptr_inst.funcDecode != NULL) - { - WebRtc_Word16 dec_Len; - if (!BGNonly) - { - /* Do decoding as normal - * - * blockPtr is pointing to payload, at this point, - * the most significant bit of *(blockPtr - 1) is a flag if set to 1 - * indicates that the following payload is the redundant payload. - */ - if (((*(blockPtr - 1) & DSP_CODEC_RED_FLAG) != 0) - && (inst->codec_ptr_inst.funcDecodeRCU != NULL)) - { - dec_Len = inst->codec_ptr_inst.funcDecodeRCU( - inst->codec_ptr_inst.codec_state, blockPtr, payloadLen, - &pw16_decoded_buffer[len], &speechType); - } - else - { - dec_Len = inst->codec_ptr_inst.funcDecode( - inst->codec_ptr_inst.codec_state, blockPtr, payloadLen, - &pw16_decoded_buffer[len], &speechType); - } - } - else - { - /* - * Background noise mode: don't decode, just produce the same length BGN. - * Don't call Expand for BGN here, since Expand uses the memory where the - * bitstreams are stored (sharemem). - */ - dec_Len = inst->w16_frameLen; - } - - if (dec_Len > 0) - { - len += dec_Len; - /* Update frameLen */ - inst->w16_frameLen = dec_Len; - } - else if (dec_Len < 0) - { - /* Error */ - len = -1; - break; - } - /* - * Sanity check (although we might still write outside memory when this - * happens...) - */ - if (len > NETEQ_MAX_FRAME_SIZE) - { - WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall); - *pw16_len = inst->timestampsPerCall; - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return RECOUT_ERROR_DECODED_TOO_MUCH; - } - - /* Verify that instance was not corrupted by decoder */ - if (mainInstBackup != inst->main_inst) - { - /* Instance is corrupt */ - return CORRUPT_INSTANCE; - } - - } - blockPtr += blockLen; - blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */ - payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG); - blockPtr++; - } - - if (len < 0) - { - len = 0; - inst->endTimestamp += inst->w16_frameLen; /* advance one frame */ - if (inst->codec_ptr_inst.funcGetErrorCode != NULL) - { - return_value = -inst->codec_ptr_inst.funcGetErrorCode( - inst->codec_ptr_inst.codec_state); - } - else - { - return_value = RECOUT_ERROR_DECODING; - } - instr = DSP_INSTR_FADE_TO_BGN; - } - if (speechType != TYPE_CNG) - { - /* - * Don't increment timestamp if codec returned CNG speech type - * since in this case, the MCU side will increment the CNGplayedTS counter. - */ - inst->endTimestamp += len; - } - } - else if (instr == DSP_INSTR_NORMAL_ONE_DESC) - { - if (inst->codec_ptr_inst.funcDecode != NULL) - { - len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state, NULL, 0, - pw16_decoded_buffer, &speechType); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - fwrite(&inst->endTimestamp, sizeof(WebRtc_UWord32), 1, delay_fid2); - fwrite(&dspInfo->samplesLeft, sizeof(WebRtc_UWord16), 1, delay_fid2); - tot_received_packets++; -#endif - } - if (speechType != TYPE_CNG) - { - /* - * Don't increment timestamp if codec returned CNG speech type - * since in this case, the MCU side will increment the CNGplayedTS counter. - */ - inst->endTimestamp += len; - } - - /* Verify that instance was not corrupted by decoder */ - if (mainInstBackup != inst->main_inst) - { - /* Instance is corrupt */ - return CORRUPT_INSTANCE; - } - - if (len <= 0) - { - len = 0; - if (inst->codec_ptr_inst.funcGetErrorCode != NULL) - { - return_value = -inst->codec_ptr_inst.funcGetErrorCode( - inst->codec_ptr_inst.codec_state); - } - else - { - return_value = RECOUT_ERROR_DECODING; - } - if ((inst->codec_ptr_inst.funcDecodeInit != NULL) - && (inst->codec_ptr_inst.codec_state != NULL)) - { - /* Reinitialize codec state as something is obviously wrong */ - inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state); - } - inst->endTimestamp += inst->w16_frameLen; /* advance one frame */ - instr = DSP_INSTR_FADE_TO_BGN; - } - } - - if (len == 0 && lastModeBGNonly) /* no new data */ - { - BGNonly = 1; /* force BGN this time too */ - } - -#ifdef NETEQ_VAD - if ((speechType == TYPE_CNG) /* decoder responded with codec-internal CNG */ - || ((instr == DSP_INSTR_DO_RFC3389CNG) && (blockLen > 0)) /* ... or, SID frame */ - || (inst->fs > 16000)) /* ... or, if not NB or WB */ - { - /* disable post-decode VAD upon first sign of send-side DTX/VAD active, or if SWB */ - inst->VADInst.VADEnabled = 0; - inst->VADInst.VADDecision = 1; /* set to always active, just to be on the safe side */ - inst->VADInst.SIDintervalCounter = 0; /* reset SID interval counter */ - } - else if (!inst->VADInst.VADEnabled) /* VAD disabled and no SID/CNG data observed this time */ - { - inst->VADInst.SIDintervalCounter++; /* increase counter */ - } - - /* check for re-enabling the VAD */ - if (inst->VADInst.SIDintervalCounter >= POST_DECODE_VAD_AUTO_ENABLE) - { - /* - * It's been a while since the last CNG/SID frame was observed => re-enable VAD. - * (Do not care to look for a VAD instance, since this is done inside the init - * function) - */ - WebRtcNetEQ_InitVAD(&inst->VADInst, inst->fs); - } - - if (len > 0 /* if we decoded any data */ - && inst->VADInst.VADEnabled /* and VAD enabled */ - && inst->fs <= 16000) /* can only do VAD for NB and WB */ - { - int VADframeSize; /* VAD frame size in ms */ - int VADSamplePtr = 0; - - inst->VADInst.VADDecision = 0; - - if (inst->VADInst.VADFunction != NULL) /* make sure that VAD function is provided */ - { - /* divide the data into groups, as large as possible */ - for (VADframeSize = 30; VADframeSize >= 10; VADframeSize -= 10) - { - /* loop through 30, 20, 10 */ - - while (inst->VADInst.VADDecision == 0 - && len - VADSamplePtr >= VADframeSize * fs_mult * 8) - { - /* - * Only continue until first active speech found, and as long as there is - * one VADframeSize left. - */ - - /* call VAD with new decoded data */ - inst->VADInst.VADDecision |= inst->VADInst.VADFunction( - inst->VADInst.VADState, (WebRtc_Word16) inst->fs, - (WebRtc_Word16 *) &pw16_decoded_buffer[VADSamplePtr], - (WebRtc_Word16) (VADframeSize * fs_mult * 8)); - - VADSamplePtr += VADframeSize * fs_mult * 8; /* increment sample counter */ - } - } - } - else - { /* VAD function is NULL */ - inst->VADInst.VADDecision = 1; /* set decision to active */ - inst->VADInst.VADEnabled = 0; /* disable VAD since we have no VAD function */ - } - - } -#endif /* NETEQ_VAD */ - - /* Adjust timestamp if needed */ - uw16_tmp = (WebRtc_UWord16) inst->pw16_readAddress[1]; - inst->endTimestamp += (((WebRtc_UWord32) uw16_tmp) << 16); - uw16_tmp = (WebRtc_UWord16) inst->pw16_readAddress[2]; - inst->endTimestamp += uw16_tmp; - - if (BGNonly && len > 0) - { - /* - * If BGN mode, we did not produce any data at decoding. - * Do it now instead. - */ - - WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_decoded_buffer, len); - } - - /* Switch on the instruction received from the MCU side. */ - switch (instr) - { - case DSP_INSTR_NORMAL: - - /* Allow for signal processing to apply gain-back etc */ - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if ((speechType == TYPE_CNG) || ((inst->w16_mode == MODE_CODEC_INTERNAL_CNG) - && (len == 0))) - { - inst->w16_mode = MODE_CODEC_INTERNAL_CNG; - } - -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - case DSP_INSTR_NORMAL_ONE_DESC: - - /* Allow for signal processing to apply gain-back etc */ - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - inst->w16_mode = MODE_ONE_DESCRIPTOR; - break; - case DSP_INSTR_MERGE: -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = -len; -#endif - /* Call Merge with history*/ - return_value = WebRtcNetEQ_Merge(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_MERGE, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - - if (return_value < 0) - { - /* error */ - return return_value; - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var += len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_EXPAND: - len = 0; - pos = 0; - while ((inst->endPosition - inst->curPosition - inst->ExpandInst.w16_overlap + pos) - < (inst->timestampsPerCall)) - { - return_value = WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - if (return_value < 0) - { - /* error */ - return return_value; - } - - /* - * Update buffer, but only end part (otherwise expand state is destroyed - * since it reuses speechBuffer[] memory - */ - - WEBRTC_SPL_MEMMOVE_W16(inst->pw16_speechHistory, - inst->pw16_speechHistory + len, - (inst->w16_speechHistoryLen-len)); - WEBRTC_SPL_MEMCPY_W16(&inst->pw16_speechHistory[inst->w16_speechHistoryLen-len], - pw16_NetEqAlgorithm_buffer, len); - - inst->curPosition -= len; - - /* Update variables for VQmon */ - inst->w16_concealedTS += len; -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - len = 0; /* already written the data, so do not write it again further down. */ - } -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_ACCELERATE: - if (len < 3 * 80 * fs_mult) - { - /* We need to move data from the speechBuffer[] in order to get 30 ms */ - borrowedSamples = 3 * 80 * fs_mult - len; - - WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples], - pw16_decoded_buffer, len); - WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer, - &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - borrowedSamples); - - return_value = WebRtcNetEQ_Accelerate(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, 3 * inst->timestampsPerCall, - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - - /* Copy back samples to the buffer */ - if (len < borrowedSamples) - { - /* - * This destroys the beginning of the buffer, but will not cause any - * problems - */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples], - pw16_NetEqAlgorithm_buffer, len); - WEBRTC_SPL_MEMMOVE_W16(&inst->speechBuffer[borrowedSamples-len], - inst->speechBuffer, - (inst->endPosition-(borrowedSamples-len))); - - inst->curPosition += (borrowedSamples - len); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = 3 * inst->timestampsPerCall - len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - len = 0; - } - else - { - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples], - pw16_NetEqAlgorithm_buffer, borrowedSamples); - WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[borrowedSamples], - (len-borrowedSamples)); -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = 3 * inst->timestampsPerCall - len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - len = len - borrowedSamples; - } - - } - else - { -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = len; -#endif - return_value = WebRtcNetEQ_Accelerate(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var -= len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - } - /* If last packet was decoded as a inband CNG set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_DO_RFC3389CNG: -#ifdef NETEQ_CNG_CODEC - if (blockLen > 0) - { - if (WebRtcCng_UpdateSid(inst->CNG_Codec_inst, (WebRtc_UWord8*) blockPtr, - payloadLen) < 0) - { - /* error returned from CNG function */ - return_value = -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst); - len = inst->timestampsPerCall; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - break; - } - } - - if (BGNonly) - { - /* Get data from BGN function instead of CNG */ - len = WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, inst->timestampsPerCall); - if (len != inst->timestampsPerCall) - { - /* this is not good, treat this as an error */ - return_value = -1; - } - } - else - { - return_value = WebRtcNetEQ_Cng(inst, pw16_NetEqAlgorithm_buffer, - inst->timestampsPerCall); - } - len = inst->timestampsPerCall; - inst->ExpandInst.w16_consecExp = 0; - inst->w16_mode = MODE_RFC3389CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - - if (return_value < 0) - { - /* error returned */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - - break; -#else - return FAULTY_INSTRUCTION; -#endif - case DSP_INSTR_DO_CODEC_INTERNAL_CNG: - /* - * This represents the case when there is no transmission and the decoder should - * do internal CNG. - */ - len = 0; - if (inst->codec_ptr_inst.funcDecode != NULL && !BGNonly) - { - len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state, - blockPtr, 0, pw16_decoded_buffer, &speechType); - } - else - { - /* get BGN data */ - len = WebRtcNetEQ_GenerateBGN(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_decoded_buffer, inst->timestampsPerCall); - } - WebRtcNetEQ_Normal(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_NORMAL, -#endif - pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len); - inst->w16_mode = MODE_CODEC_INTERNAL_CNG; - inst->ExpandInst.w16_consecExp = 0; - break; - - case DSP_INSTR_DTMF_GENERATE: -#ifdef NETEQ_ATEVENT_DECODE - dtmfSwitch = 0; - if ((inst->w16_mode != MODE_DTMF) && (inst->DTMFInst.reinit == 0)) - { - /* Special case; see below. - * We must catch this before calling DTMFGenerate, - * since reinit is set to 0 in that call. - */ - dtmfSwitch = 1; - } - - len = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - pw16_NetEqAlgorithm_buffer, inst->fs, -1); - if (len < 0) - { - /* error occurred */ - return_value = len; - len = inst->timestampsPerCall; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - - if (dtmfSwitch == 1) - { - /* - * This is the special case where the previous operation was DTMF overdub. - * but the current instruction is "regular" DTMF. We must make sure that the - * DTMF does not have any discontinuities. The first DTMF sample that we - * generate now must be played out immediately, wherefore it must be copied to - * the speech buffer. - */ - - /* - * Generate extra DTMF data to fill the space between - * curPosition and endPosition - */ - WebRtc_Word16 tempLen; - - tempLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - &pw16_NetEqAlgorithm_buffer[len], inst->fs, - inst->endPosition - inst->curPosition); - if (tempLen < 0) - { - /* error occurred */ - return_value = tempLen; - len = inst->endPosition - inst->curPosition; - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, - inst->endPosition - inst->curPosition); - } - - /* Add to total length */ - len += tempLen; - - /* Overwrite the "future" part of the speech buffer with the new DTMF data */ - - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->curPosition], - pw16_NetEqAlgorithm_buffer, - inst->endPosition - inst->curPosition); - - /* Shuffle the remaining data to the beginning of algorithm buffer */ - len -= (inst->endPosition - inst->curPosition); - WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[inst->endPosition - inst->curPosition], - len); - } - - inst->endTimestamp += inst->timestampsPerCall; - inst->DTMFInst.reinit = 0; - inst->ExpandInst.w16_consecExp = 0; - inst->w16_mode = MODE_DTMF; - BGNonly = 0; /* override BGN only and let DTMF through */ - - playDtmf = 0; /* set to zero because the DTMF is already in the Algorithm buffer */ - /* - * If playDtmf is 1, an extra DTMF vector will be generated and overdubbed - * on the output. - */ - -#ifdef NETEQ_STEREO - if (msInfo->msMode == NETEQ_MASTER) - { - /* signal to slave that master is using DTMF only */ - msInfo->extraInfo = DTMF_ONLY; - } -#endif - - break; -#else - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; -#endif - - case DSP_INSTR_DO_ALTERNATIVE_PLC: - if (inst->codec_ptr_inst.funcDecodePLC != 0) - { - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - pw16_NetEqAlgorithm_buffer, 1); - } - else - { - len = inst->timestampsPerCall; - /* ZeroStuffing... */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - inst->ExpandInst.w16_consecExp = 0; - break; - case DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS: - if (inst->codec_ptr_inst.funcDecodePLC != 0) - { - len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state, - pw16_NetEqAlgorithm_buffer, 1); - } - else - { - len = inst->timestampsPerCall; - /* ZeroStuffing... */ - WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len); - } - inst->ExpandInst.w16_consecExp = 0; - inst->endTimestamp += len; - break; - case DSP_INSTR_DO_AUDIO_REPETITION: - len = inst->timestampsPerCall; - /* copy->paste... */ - WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer, - &inst->speechBuffer[inst->endPosition-len], len); - inst->ExpandInst.w16_consecExp = 0; - break; - case DSP_INSTR_DO_AUDIO_REPETITION_INC_TS: - len = inst->timestampsPerCall; - /* copy->paste... */ - WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer, - &inst->speechBuffer[inst->endPosition-len], len); - inst->ExpandInst.w16_consecExp = 0; - inst->endTimestamp += len; - break; - - case DSP_INSTR_PREEMPTIVE_EXPAND: - if (len < 3 * inst->timestampsPerCall) - { - /* borrow samples from sync buffer if necessary */ - borrowedSamples = 3 * inst->timestampsPerCall - len; /* borrow this many samples */ - /* calculate how many of these are already played out */ - oldBorrowedSamples = WEBRTC_SPL_MAX(0, - borrowedSamples - (inst->endPosition - inst->curPosition)); - WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples], - pw16_decoded_buffer, len); - WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer, - &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - borrowedSamples); - } - else - { - borrowedSamples = 0; - oldBorrowedSamples = 0; - } - -#ifdef NETEQ_DELAY_LOGGING - w16_tmp1 = len; -#endif - /* do the expand */ - return_value = WebRtcNetEQ_PreEmptiveExpand(inst, -#ifdef SCRATCH - /* use same scratch memory as Accelerate */ - pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE, -#endif - pw16_decoded_buffer, len + borrowedSamples, oldBorrowedSamples, - pw16_NetEqAlgorithm_buffer, &len, BGNonly); - - if (return_value < 0) - { - /* error */ - return return_value; - } - - if (borrowedSamples > 0) - { - /* return borrowed samples */ - - /* Copy back to last part of speechBuffer from beginning of output buffer */ - WEBRTC_SPL_MEMCPY_W16( &(inst->speechBuffer[inst->endPosition-borrowedSamples]), - pw16_NetEqAlgorithm_buffer, - borrowedSamples); - - len -= borrowedSamples; /* remove the borrowed samples from new total length */ - - /* Move to beginning of output buffer from end of output buffer */ - WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[borrowedSamples], - len); - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = len - w16_tmp1; /* number of samples added */ - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - /* If last packet was decoded as inband CNG, set mode to CNG instead */ - if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG; -#ifdef NETEQ_ATEVENT_DECODE - if (playDtmf == 0) - { - inst->DTMFInst.reinit = 1; - } -#endif - break; - - case DSP_INSTR_FADE_TO_BGN: - { - int tempReturnValue; - /* do not overwrite return_value, since it likely contains an error code */ - - /* calculate interpolation length */ - w16_tmp3 = WEBRTC_SPL_MIN(inst->endPosition - inst->curPosition, - inst->timestampsPerCall); - /* check that it will fit in pw16_NetEqAlgorithm_buffer */ - if (w16_tmp3 + inst->w16_frameLen > NETEQ_MAX_OUTPUT_SIZE) - { - w16_tmp3 = NETEQ_MAX_OUTPUT_SIZE - inst->w16_frameLen; - } - - /* call Expand */ - len = inst->timestampsPerCall + inst->ExpandInst.w16_overlap; - pos = 0; - - tempReturnValue = WebRtcNetEQ_Expand(inst, -#ifdef SCRATCH - pw16_scratchPtr + SCRATCH_NETEQ_EXPAND, -#endif - pw16_NetEqAlgorithm_buffer, &len, 1); - - if (tempReturnValue < 0) - { - /* error */ - /* this error value will override return_value */ - return tempReturnValue; - } - - pos += len; /* got len samples from expand */ - - /* copy to fill the demand */ - while (pos + len <= inst->w16_frameLen + w16_tmp3) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos], - pw16_NetEqAlgorithm_buffer, len); - pos += len; - } - - /* fill with fraction of the expand vector if needed */ - if (pos < inst->w16_frameLen + w16_tmp3) - { - WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos], pw16_NetEqAlgorithm_buffer, - inst->w16_frameLen + w16_tmp3 - pos); - } - - len = inst->w16_frameLen + w16_tmp3; /* truncate any surplus samples since we don't want these */ - - /* - * Mix with contents in sync buffer. Find largest power of two that is less than - * interpolate length divide 16384 with this number; result is in w16_tmp2. - */ - w16_tmp1 = 2; - w16_tmp2 = 16384; - while (w16_tmp1 <= w16_tmp3) - { - w16_tmp2 >>= 1; /* divide with 2 */ - w16_tmp1 <<= 1; /* increase with a factor of 2 */ - } - - w16_tmp1 = 0; - pos = 0; - while (w16_tmp1 < 16384) - { - inst->speechBuffer[inst->curPosition + pos] - = - (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL_16_16( inst->speechBuffer[inst->endPosition - w16_tmp3 + pos], - 16384-w16_tmp1 ) + - WEBRTC_SPL_MUL_16_16( pw16_NetEqAlgorithm_buffer[pos], w16_tmp1 ), - 14 ); - w16_tmp1 += w16_tmp2; - pos++; - } - - /* overwrite remainder of speech buffer */ - - WEBRTC_SPL_MEMCPY_W16( &inst->speechBuffer[inst->endPosition - w16_tmp3 + pos], - &pw16_NetEqAlgorithm_buffer[pos], w16_tmp3 - pos); - - len -= w16_tmp3; - /* shift algorithm buffer */ - - WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer, - &pw16_NetEqAlgorithm_buffer[w16_tmp3], - len ); - - /* Update variables for VQmon */ - inst->w16_concealedTS += len; - - inst->w16_mode = MODE_FADE_TO_BGN; -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); - temp_var = len; - fwrite(&temp_var, sizeof(int), 1, delay_fid2); -#endif - - break; - } - - default: - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; - } /* end of grand switch */ - - /* Copy data directly to output buffer */ - - w16_tmp2 = 0; - if ((inst->endPosition + len - inst->curPosition - inst->ExpandInst.w16_overlap) - >= inst->timestampsPerCall) - { - w16_tmp2 = inst->endPosition - inst->curPosition; - w16_tmp2 = WEBRTC_SPL_MAX(w16_tmp2, 0); /* Additional error protection, just in case */ - w16_tmp1 = WEBRTC_SPL_MIN(w16_tmp2, inst->timestampsPerCall); - w16_tmp2 = inst->timestampsPerCall - w16_tmp1; - WEBRTC_SPL_MEMCPY_W16(pw16_outData, &inst->speechBuffer[inst->curPosition], w16_tmp1); - WEBRTC_SPL_MEMCPY_W16(&pw16_outData[w16_tmp1], pw16_NetEqAlgorithm_buffer, w16_tmp2); - DataEnough = 1; - } - else - { - DataEnough = 0; - } - - if (playDtmf != 0) - { - WebRtc_Word16 outDataIndex = 0; - WebRtc_Word16 overdubLen = -1; /* default len */ - WebRtc_Word16 dtmfLen; - - /* - * Overdub the output with DTMF. Note that this is not executed if the - * DSP_INSTR_DTMF_GENERATE operation is performed above. - */ -#ifdef NETEQ_ATEVENT_DECODE - if (inst->DTMFInst.lastDtmfSample - inst->curPosition > 0) - { - /* special operation for transition from "DTMF only" to "DTMF overdub" */ - outDataIndex - = WEBRTC_SPL_MIN(inst->DTMFInst.lastDtmfSample - inst->curPosition, - inst->timestampsPerCall); - overdubLen = inst->timestampsPerCall - outDataIndex; - } - - dtmfLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume, - &pw16_outData[outDataIndex], inst->fs, overdubLen); - if (dtmfLen < 0) - { - /* error occurred */ - return_value = dtmfLen; - } - inst->DTMFInst.reinit = 0; -#else - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return FAULTY_INSTRUCTION; -#endif - } - - /* - * Shuffle speech buffer to allow more data. Move data from pw16_NetEqAlgorithm_buffer - * to speechBuffer. - */ - if (instr != DSP_INSTR_EXPAND) - { - w16_tmp1 = WEBRTC_SPL_MIN(inst->endPosition, len); - WEBRTC_SPL_MEMMOVE_W16(inst->speechBuffer, inst->speechBuffer + w16_tmp1, - (inst->endPosition-w16_tmp1)); - WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-w16_tmp1], - &pw16_NetEqAlgorithm_buffer[len-w16_tmp1], w16_tmp1); -#ifdef NETEQ_ATEVENT_DECODE - /* Update index to end of DTMF data in speech buffer */ - if (instr == DSP_INSTR_DTMF_GENERATE) - { - /* We have written DTMF data to the end of speech buffer */ - inst->DTMFInst.lastDtmfSample = inst->endPosition; - } - else if (inst->DTMFInst.lastDtmfSample > 0) - { - /* The end of DTMF data in speech buffer has been shuffled */ - inst->DTMFInst.lastDtmfSample -= w16_tmp1; - } -#endif - /* - * Update the BGN history if last operation was not expand (nor Merge, Accelerate - * or Pre-emptive expand, to save complexity). - */ - if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_MERGE) - && (inst->w16_mode != MODE_SUCCESS_ACCELERATE) && (inst->w16_mode - != MODE_LOWEN_ACCELERATE) && (inst->w16_mode != MODE_SUCCESS_PREEMPTIVE) - && (inst->w16_mode != MODE_LOWEN_PREEMPTIVE) && (inst->w16_mode - != MODE_FADE_TO_BGN) && (inst->w16_mode != MODE_DTMF) && (!BGNonly)) - { - WebRtcNetEQ_BGNUpdate(inst -#ifdef SCRATCH - , pw16_scratchPtr + SCRATCH_NETEQ_BGN_UPDATE -#endif - ); - } - } - else /* instr == DSP_INSTR_EXPAND */ - { - /* Nothing should be done since data is already copied to output. */ - } - - inst->curPosition -= len; - - /* - * Extra protection in case something should go totally wrong in terms of sizes... - * If everything is ok this should NEVER happen. - */ - if (inst->curPosition < -inst->timestampsPerCall) - { - inst->curPosition = -inst->timestampsPerCall; - } - - if ((instr != DSP_INSTR_EXPAND) && (instr != DSP_INSTR_MERGE) && (instr - != DSP_INSTR_FADE_TO_BGN)) - { - /* Reset concealed TS parameter if it does not seem to have been flushed */ - if (inst->w16_concealedTS > inst->timestampsPerCall) - { - inst->w16_concealedTS = 0; - } - } - - /* - * Double-check that we actually have 10 ms to play. If we haven't, there has been a - * serious error.The decoder might have returned way too few samples - */ - if (!DataEnough) - { - /* This should not happen. Set outdata to zeros, and return error. */ - WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall); - *pw16_len = inst->timestampsPerCall; - inst->w16_mode = MODE_ERROR; - dspInfo->lastMode = MODE_ERROR; - return RECOUT_ERROR_SAMPLEUNDERRUN; - } - - /* - * Update Videosync timestamp (this special timestamp is needed since the endTimestamp - * stops during CNG and Expand periods. - */ - if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_RFC3389CNG)) - { - WebRtc_UWord32 uw32_tmpTS; - uw32_tmpTS = inst->endTimestamp - (inst->endPosition - inst->curPosition); - if ((WebRtc_Word32) (uw32_tmpTS - inst->videoSyncTimestamp) > 0) - { - inst->videoSyncTimestamp = uw32_tmpTS; - } - } - else - { - inst->videoSyncTimestamp += inst->timestampsPerCall; - } - - /* After this, regardless of what has happened, deliver 10 ms of future data */ - inst->curPosition += inst->timestampsPerCall; - *pw16_len = inst->timestampsPerCall; - - /* Remember if BGNonly was used */ - if (BGNonly) - { - inst->w16_mode |= MODE_BGN_ONLY; - } - - return return_value; -} - -#undef SCRATCH_ALGORITHM_BUFFER -#undef SCRATCH_NETEQ_NORMAL -#undef SCRATCH_NETEQ_MERGE -#undef SCRATCH_NETEQ_BGN_UPDATE -#undef SCRATCH_NETEQ_EXPAND -#undef SCRATCH_DSP_INFO -#undef SCRATCH_NETEQ_ACCELERATE -#undef SIZE_SCRATCH_BUFFER diff --git a/modules/audio_coding/NetEQ/main/source/rtcp.c b/modules/audio_coding/NetEQ/main/source/rtcp.c deleted file mode 100644 index 35f73da84..000000000 --- a/modules/audio_coding/NetEQ/main/source/rtcp.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of RTCP statistics reporting. - */ - -#include "rtcp.h" - -#include - -#include "signal_processing_library.h" - -int WebRtcNetEQ_RTCPInit(WebRtcNetEQ_RTCP_t *RTCP_inst, WebRtc_UWord16 uw16_seqNo) -{ - /* - * Initialize everything to zero and then set the start values for the RTP packet stream. - */ - WebRtcSpl_MemSetW16((WebRtc_Word16*) RTCP_inst, 0, - sizeof(WebRtcNetEQ_RTCP_t) / sizeof(WebRtc_Word16)); - RTCP_inst->base_seq = uw16_seqNo; - RTCP_inst->max_seq = uw16_seqNo; - return 0; -} - -int WebRtcNetEQ_RTCPUpdate(WebRtcNetEQ_RTCP_t *RTCP_inst, WebRtc_UWord16 uw16_seqNo, - WebRtc_UWord32 uw32_timeStamp, WebRtc_UWord32 uw32_recTime) -{ - WebRtc_Word16 w16_SeqDiff; - WebRtc_Word32 w32_TimeDiff; - WebRtc_Word32 w32_JitterDiff; - - /* - * Update number of received packets, and largest packet number received. - */ - RTCP_inst->received++; - w16_SeqDiff = uw16_seqNo - RTCP_inst->max_seq; - if (w16_SeqDiff >= 0) - { - if (uw16_seqNo < RTCP_inst->max_seq) - { - /* Wrap around detected */ - RTCP_inst->cycles++; - } - RTCP_inst->max_seq = uw16_seqNo; - } - - /* Calculate Jitter, and update previous timestamps */ - /* Note that the value in RTCP_inst->jitter is in Q4. */ - if (RTCP_inst->received > 1) - { - w32_TimeDiff = (uw32_recTime - (uw32_timeStamp - RTCP_inst->transit)); - w32_TimeDiff = WEBRTC_SPL_ABS_W32(w32_TimeDiff); - w32_JitterDiff = WEBRTC_SPL_LSHIFT_W16(w32_TimeDiff, 4) - RTCP_inst->jitter; - RTCP_inst->jitter = RTCP_inst->jitter + WEBRTC_SPL_RSHIFT_W32((w32_JitterDiff + 8), 4); - } - RTCP_inst->transit = (uw32_timeStamp - uw32_recTime); - return 0; -} - -int WebRtcNetEQ_RTCPGetStats(WebRtcNetEQ_RTCP_t *RTCP_inst, - WebRtc_UWord16 *puw16_fraction_lost, - WebRtc_UWord32 *puw32_cum_lost, WebRtc_UWord32 *puw32_ext_max, - WebRtc_UWord32 *puw32_jitter, WebRtc_Word16 doNotReset) -{ - WebRtc_UWord32 uw32_exp_nr, uw32_exp_interval, uw32_rec_interval; - WebRtc_Word32 w32_lost; - - /* Extended highest sequence number received */ - *puw32_ext_max - = (WebRtc_UWord32) WEBRTC_SPL_LSHIFT_W32((WebRtc_UWord32)RTCP_inst->cycles, 16) - + RTCP_inst->max_seq; - - /* - * Calculate expected number of packets and compare it to the number of packets that - * were actually received => the cumulative number of packets lost can be extracted. - */ - uw32_exp_nr = *puw32_ext_max - RTCP_inst->base_seq + 1; - if (RTCP_inst->received == 0) - { - /* no packets received, assume none lost */ - *puw32_cum_lost = 0; - } - else if (uw32_exp_nr > RTCP_inst->received) - { - *puw32_cum_lost = uw32_exp_nr - RTCP_inst->received; - if (*puw32_cum_lost > (WebRtc_UWord32) 0xFFFFFF) - { - *puw32_cum_lost = 0xFFFFFF; - } - } - else - { - *puw32_cum_lost = 0; - } - - /* Fraction lost (Since last report) */ - uw32_exp_interval = uw32_exp_nr - RTCP_inst->exp_prior; - if (!doNotReset) - { - RTCP_inst->exp_prior = uw32_exp_nr; - } - uw32_rec_interval = RTCP_inst->received - RTCP_inst->rec_prior; - if (!doNotReset) - { - RTCP_inst->rec_prior = RTCP_inst->received; - } - w32_lost = (WebRtc_Word32) (uw32_exp_interval - uw32_rec_interval); - if (uw32_exp_interval == 0 || w32_lost <= 0 || RTCP_inst->received == 0) - { - *puw16_fraction_lost = 0; - } - else - { - *puw16_fraction_lost = (WebRtc_UWord16) (WEBRTC_SPL_LSHIFT_W32(w32_lost, 8) - / uw32_exp_interval); - } - if (*puw16_fraction_lost > 0xFF) - { - *puw16_fraction_lost = 0xFF; - } - - /* Inter-arrival jitter */ - *puw32_jitter = (RTCP_inst->jitter) >> 4; /* scaling from Q4 */ - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/rtcp.h b/modules/audio_coding/NetEQ/main/source/rtcp.h deleted file mode 100644 index 009e019ee..000000000 --- a/modules/audio_coding/NetEQ/main/source/rtcp.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * RTCP statistics reporting. - */ - -#ifndef RTCP_H -#define RTCP_H - -#include "typedefs.h" - -typedef struct -{ - WebRtc_UWord16 cycles; /* The number of wrap-arounds for the sequence number */ - WebRtc_UWord16 max_seq; /* The maximum sequence number received - (starts from 0 again after wrap around) */ - WebRtc_UWord16 base_seq; /* The sequence number of the first packet that arrived */ - WebRtc_UWord32 received; /* The number of packets that has been received */ - WebRtc_UWord32 rec_prior; /* Number of packets received when last report was generated */ - WebRtc_UWord32 exp_prior; /* Number of packets that should have been received if no - packets were lost. Stored value from last report. */ - WebRtc_UWord32 jitter; /* Jitter statistics at this instance (calculated according to RFC) */ - WebRtc_Word32 transit; /* Clock difference for previous packet (RTPtimestamp - LOCALtime_rec) */ -} WebRtcNetEQ_RTCP_t; - -/**************************************************************************** - * WebRtcNetEQ_RTCPInit(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - seqNo : Packet number of the first received frame. - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTCPInit(WebRtcNetEQ_RTCP_t *RTCP_inst, WebRtc_UWord16 uw16_seqNo); - -/**************************************************************************** - * WebRtcNetEQ_RTCPUpdate(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - seqNo : Packet number of the first received frame. - * - timeStamp : Time stamp from the RTP header. - * - recTime : Time (in RTP timestamps) when this packet was received. - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTCPUpdate(WebRtcNetEQ_RTCP_t *RTCP_inst, WebRtc_UWord16 uw16_seqNo, - WebRtc_UWord32 uw32_timeStamp, WebRtc_UWord32 uw32_recTime); - -/**************************************************************************** - * WebRtcNetEQ_RTCPGetStats(...) - * - * This function calculates the parameters that are needed for the RTCP - * report. - * - * Input: - * - RTCP_inst : RTCP instance, that contains information about the - * packets that have been received etc. - * - doNotReset : If non-zero, the fraction lost statistics will not - * be reset. - * - * Output: - * - RTCP_inst : Updated RTCP information (some statistics are - * reset when generating this report) - * - fraction_lost : Number of lost RTP packets divided by the number of - * expected packets, since the last RTCP Report. - * - cum_lost : Cumulative number of lost packets during this - * session. - * - ext_max : Extended highest sequence number received. - * - jitter : Inter-arrival jitter. - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTCPGetStats(WebRtcNetEQ_RTCP_t *RTCP_inst, - WebRtc_UWord16 *puw16_fraction_lost, - WebRtc_UWord32 *puw32_cum_lost, WebRtc_UWord32 *puw32_ext_max, - WebRtc_UWord32 *puw32_jitter, WebRtc_Word16 doNotReset); - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/rtp.c b/modules/audio_coding/NetEQ/main/source/rtp.c deleted file mode 100644 index bd4f9a226..000000000 --- a/modules/audio_coding/NetEQ/main/source/rtp.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * RTP related functions. - */ - -#include "rtp.h" - -#include "typedefs.h" /* to define endianness */ - -#include "neteq_error_codes.h" - -int WebRtcNetEQ_RTPPayloadInfo(WebRtc_Word16* pw16_Datagram, int i_DatagramLen, - RTPPacket_t* RTPheader) -{ - int i_P, i_X, i_CC, i_startPosition; - int i_IPver; - int i_extlength = -1; /* Default value is there is no extension */ - int i_padlength = 0; /* Default value if there is no padding */ - - if (i_DatagramLen < 12) - { - return RTP_TOO_SHORT_PACKET; - } - -#ifdef WEBRTC_BIG_ENDIAN - i_IPver = (((WebRtc_UWord16) (pw16_Datagram[0] & 0xC000)) >> 14); /* Extract the version */ - i_P = (((WebRtc_UWord16) (pw16_Datagram[0] & 0x2000)) >> 13); /* Extract the P bit */ - i_X = (((WebRtc_UWord16) (pw16_Datagram[0] & 0x1000)) >> 12); /* Extract the X bit */ - i_CC = ((WebRtc_UWord16) (pw16_Datagram[0] >> 8) & 0xF); /* Get the CC number */ - RTPheader->payloadType = pw16_Datagram[0] & 0x7F; /* Get the coder type */ - RTPheader->seqNumber = pw16_Datagram[1]; /* Get the sequence number */ - RTPheader->timeStamp = ((((WebRtc_UWord32) ((WebRtc_UWord16) pw16_Datagram[2])) << 16) - | (WebRtc_UWord16) (pw16_Datagram[3])); /* Get timestamp */ - RTPheader->ssrc = (((WebRtc_UWord32) pw16_Datagram[4]) << 16) - + (((WebRtc_UWord32) pw16_Datagram[5])); /* Get the SSRC */ - - if (i_X == 1) - { - /* Extension header exists. Find out how many WebRtc_Word32 it consists of. */ - i_extlength = pw16_Datagram[7 + 2 * i_CC]; - } - if (i_P == 1) - { - /* Padding exists. Find out how many bytes the padding consists of. */ - if (i_DatagramLen & 0x1) - { - /* odd number of bytes => last byte in higher byte */ - i_padlength = (((WebRtc_UWord16) pw16_Datagram[i_DatagramLen >> 1]) >> 8); - } - else - { - /* even number of bytes => last byte in lower byte */ - i_padlength = ((pw16_Datagram[(i_DatagramLen >> 1) - 1]) & 0xFF); - } - } -#else /* WEBRTC_LITTLE_ENDIAN */ - i_IPver = (((WebRtc_UWord16) (pw16_Datagram[0] & 0xC0)) >> 6); /* Extract the IP version */ - i_P = (((WebRtc_UWord16) (pw16_Datagram[0] & 0x20)) >> 5); /* Extract the P bit */ - i_X = (((WebRtc_UWord16) (pw16_Datagram[0] & 0x10)) >> 4); /* Extract the X bit */ - i_CC = (WebRtc_UWord16) (pw16_Datagram[0] & 0xF); /* Get the CC number */ - RTPheader->payloadType = (pw16_Datagram[0] >> 8) & 0x7F; /* Get the coder type */ - RTPheader->seqNumber = (((((WebRtc_UWord16) pw16_Datagram[1]) >> 8) & 0xFF) - | (((WebRtc_UWord16) (pw16_Datagram[1] & 0xFF)) << 8)); /* Get the packet number */ - RTPheader->timeStamp = ((((WebRtc_UWord16) pw16_Datagram[2]) & 0xFF) << 24) - | ((((WebRtc_UWord16) pw16_Datagram[2]) & 0xFF00) << 8) - | ((((WebRtc_UWord16) pw16_Datagram[3]) >> 8) & 0xFF) - | ((((WebRtc_UWord16) pw16_Datagram[3]) & 0xFF) << 8); /* Get timestamp */ - RTPheader->ssrc = ((((WebRtc_UWord16) pw16_Datagram[4]) & 0xFF) << 24) - | ((((WebRtc_UWord16) pw16_Datagram[4]) & 0xFF00) << 8) - | ((((WebRtc_UWord16) pw16_Datagram[5]) >> 8) & 0xFF) - | ((((WebRtc_UWord16) pw16_Datagram[5]) & 0xFF) << 8); /* Get the SSRC */ - - if (i_X == 1) - { - /* Extension header exists. Find out how many WebRtc_Word32 it consists of. */ - i_extlength = (((((WebRtc_UWord16) pw16_Datagram[7 + 2 * i_CC]) >> 8) & 0xFF) - | (((WebRtc_UWord16) (pw16_Datagram[7 + 2 * i_CC] & 0xFF)) << 8)); - } - if (i_P == 1) - { - /* Padding exists. Find out how many bytes the padding consists of. */ - if (i_DatagramLen & 0x1) - { - /* odd number of bytes => last byte in higher byte */ - i_padlength = (pw16_Datagram[i_DatagramLen >> 1] & 0xFF); - } - else - { - /* even number of bytes => last byte in lower byte */ - i_padlength = (((WebRtc_UWord16) pw16_Datagram[(i_DatagramLen >> 1) - 1]) >> 8); - } - } -#endif - - i_startPosition = 12 + 4 * (i_extlength + 1) + 4 * i_CC; - RTPheader->payload = &pw16_Datagram[i_startPosition >> 1]; - RTPheader->payloadLen = i_DatagramLen - i_startPosition - i_padlength; - RTPheader->starts_byte1 = 0; - - if ((i_IPver != 2) || (RTPheader->payloadLen <= 0) || (RTPheader->payloadLen >= 16000) - || (i_startPosition < 12) || (i_startPosition > i_DatagramLen)) - { - return RTP_CORRUPT_PACKET; - } - - return 0; -} - -#ifdef NETEQ_RED_CODEC - -int WebRtcNetEQ_RedundancySplit(RTPPacket_t* RTPheader[], int i_MaximumPayloads, - int *i_No_Of_Payloads) -{ - const WebRtc_Word16 *pw16_data = RTPheader[0]->payload; /* Pointer to the data */ - WebRtc_UWord16 uw16_offsetTimeStamp = 65535, uw16_secondPayload = 65535; - int i_blockLength, i_k; - int i_discardedBlockLength = 0; - int singlePayload = 0; - -#ifdef WEBRTC_BIG_ENDIAN - if ((pw16_data[0] & 0x8000) == 0) - { - /* Only one payload in this packet*/ - singlePayload = 1; - /* set the blocklength to -4 to deduce the non-existent 4-byte RED header */ - i_blockLength = -4; - RTPheader[0]->payloadType = ((((WebRtc_UWord16)pw16_data[0]) & 0x7F00) >> 8); - } - else - { - /* Discard all but the two last payloads. */ - while (((pw16_data[2] & 0x8000) == 1)&& - (pw16_data<((RTPheader[0]->payload)+((RTPheader[0]->payloadLen+1)>>1)))) - { - i_discardedBlockLength += (4+(((WebRtc_UWord16)pw16_data[1]) & 0x3FF)); - pw16_data+=2; - } - if (pw16_data>=(RTPheader[0]->payload+((RTPheader[0]->payloadLen+1)>>1))) - { - return RED_SPLIT_ERROR2; /* Error, we are outside the packet */ - } - singlePayload = 0; /* the packet contains more than one payload */ - uw16_secondPayload = ((((WebRtc_UWord16)pw16_data[0]) & 0x7F00) >> 8); - RTPheader[0]->payloadType = ((((WebRtc_UWord16)pw16_data[2]) & 0x7F00) >> 8); - uw16_offsetTimeStamp = ((((WebRtc_UWord16)pw16_data[0]) & 0xFF) << 6) + - ((((WebRtc_UWord16)pw16_data[1]) & 0xFC00) >> 10); - i_blockLength = (((WebRtc_UWord16)pw16_data[1]) & 0x3FF); - } -#else /* WEBRTC_LITTLE_ENDIAN */ - if ((pw16_data[0] & 0x80) == 0) - { - /* Only one payload in this packet */ - singlePayload = 1; - /* set the blocklength to -4 to deduce the non-existent 4-byte RED header */ - i_blockLength = -4; - RTPheader[0]->payloadType = (((WebRtc_UWord16) pw16_data[0]) & 0x7F); - } - else - { - /* Discard all but the two last payloads. */ - while (((pw16_data[2] & 0x80) == 1) && (pw16_data < ((RTPheader[0]->payload) - + ((RTPheader[0]->payloadLen + 1) >> 1)))) - { - i_discardedBlockLength += (4 + ((((WebRtc_UWord16) pw16_data[1]) & 0x3) << 8) - + ((((WebRtc_UWord16) pw16_data[1]) & 0xFF00) >> 8)); - pw16_data += 2; - } - if (pw16_data >= (RTPheader[0]->payload + ((RTPheader[0]->payloadLen + 1) >> 1))) - { - return RED_SPLIT_ERROR2; /* Error, we are outside the packet */; - } - singlePayload = 0; /* the packet contains more than one payload */ - uw16_secondPayload = (((WebRtc_UWord16) pw16_data[0]) & 0x7F); - RTPheader[0]->payloadType = (((WebRtc_UWord16) pw16_data[2]) & 0x7F); - uw16_offsetTimeStamp = ((((WebRtc_UWord16) pw16_data[0]) & 0xFF00) >> 2) - + ((((WebRtc_UWord16) pw16_data[1]) & 0xFC) >> 2); - i_blockLength = ((((WebRtc_UWord16) pw16_data[1]) & 0x3) << 8) - + ((((WebRtc_UWord16) pw16_data[1]) & 0xFF00) >> 8); - } -#endif - - if (i_MaximumPayloads < 2 || singlePayload == 1) - { - /* Reject the redundancy; or no redundant payload present. */ - for (i_k = 1; i_k < i_MaximumPayloads; i_k++) - { - RTPheader[i_k]->payloadType = -1; - RTPheader[i_k]->payloadLen = 0; - } - - /* update the pointer for the main data */ - pw16_data = &pw16_data[(5 + i_blockLength) >> 1]; - RTPheader[0]->starts_byte1 = (5 + i_blockLength) & 0x1; - RTPheader[0]->payloadLen = RTPheader[0]->payloadLen - (i_blockLength + 5) - - i_discardedBlockLength; - RTPheader[0]->payload = pw16_data; - - *i_No_Of_Payloads = 1; - - } - else - { - /* Redundancy accepted, put the redundancy in second RTPheader. */ - RTPheader[1]->payloadType = uw16_secondPayload; - RTPheader[1]->payload = &pw16_data[5 >> 1]; - RTPheader[1]->starts_byte1 = 5 & 0x1; - RTPheader[1]->seqNumber = RTPheader[0]->seqNumber; - RTPheader[1]->timeStamp = RTPheader[0]->timeStamp - uw16_offsetTimeStamp; - RTPheader[1]->ssrc = RTPheader[0]->ssrc; - RTPheader[1]->payloadLen = i_blockLength; - - /* Modify first RTP packet, so that it contains the main data. */ - RTPheader[0]->payload = &pw16_data[(5 + i_blockLength) >> 1]; - RTPheader[0]->starts_byte1 = (5 + i_blockLength) & 0x1; - RTPheader[0]->payloadLen = RTPheader[0]->payloadLen - (i_blockLength + 5) - - i_discardedBlockLength; - - /* Clear the following payloads. */ - for (i_k = 2; i_k < i_MaximumPayloads; i_k++) - { - RTPheader[i_k]->payloadType = -1; - RTPheader[i_k]->payloadLen = 0; - } - - *i_No_Of_Payloads = 2; - } - return 0; -} - -#endif - diff --git a/modules/audio_coding/NetEQ/main/source/rtp.h b/modules/audio_coding/NetEQ/main/source/rtp.h deleted file mode 100644 index 8490d62e8..000000000 --- a/modules/audio_coding/NetEQ/main/source/rtp.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * RTP data struct and related functions. - */ - -#ifndef RTP_H -#define RTP_H - -#include "typedefs.h" - -#include "codec_db.h" - -typedef struct -{ - WebRtc_UWord16 seqNumber; - WebRtc_UWord32 timeStamp; - WebRtc_UWord32 ssrc; - int payloadType; - const WebRtc_Word16 *payload; - WebRtc_Word16 payloadLen; - WebRtc_Word16 starts_byte1; - WebRtc_Word16 rcuPlCntr; -} RTPPacket_t; - -/**************************************************************************** - * WebRtcNetEQ_RTPPayloadInfo(...) - * - * Converts a datagram into an RTP header struct. - * - * Input: - * - Datagram : UDP datagram from the network - * - DatagramLen : Length in bytes of the datagram - * - * Output: - * - RTPheader : Structure with the datagram info - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RTPPayloadInfo(WebRtc_Word16* pw16_Datagram, int i_DatagramLen, - RTPPacket_t* RTPheader); - -/**************************************************************************** - * WebRtcNetEQ_RedundancySplit(...) - * - * Splits a Redundancy RTP struct into two RTP structs. User has to check - * that it's really the redundancy payload. No such check is done inside this - * function. - * - * Input: - * - RTPheader : First header holds the whole RTP packet (with the redundancy payload) - * - MaximumPayloads: - * The maximum number of RTP payloads that should be - * extracted (1+maximum_no_of_Redundancies). - * - * Output: - * - RTPheader : First header holds the main RTP data, while 2..N - * holds the redundancy data. - * - No_Of - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RedundancySplit(RTPPacket_t* RTPheader[], int i_MaximumPayloads, - int *i_No_Of_Payloads); - -#endif diff --git a/modules/audio_coding/NetEQ/main/source/set_fs.c b/modules/audio_coding/NetEQ/main/source/set_fs.c deleted file mode 100644 index b2ad5caba..000000000 --- a/modules/audio_coding/NetEQ/main/source/set_fs.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Function were the sample rate is set. - */ - -#include "mcu.h" - -#include "dtmf_buffer.h" -#include "neteq_error_codes.h" - -int WebRtcNetEQ_McuSetFs(MCUInst_t *inst, WebRtc_UWord16 fs) -{ - WebRtc_Word16 ok = 0; - - switch (fs) - { - case 8000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 8000, 560); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 8; - break; - } - -#ifdef NETEQ_WIDEBAND - case 16000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 16000, 1120); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 16; - break; - } -#endif - -#ifdef NETEQ_32KHZ_WIDEBAND - case 32000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 32000, 2240); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 32; - break; - } -#endif - -#ifdef NETEQ_48KHZ_WIDEBAND - case 48000: - { -#ifdef NETEQ_ATEVENT_DECODE - ok = WebRtcNetEQ_DtmfDecoderInit(&inst->DTMF_inst, 48000, 3360); -#endif - inst->timestampsPerCall = inst->millisecondsPerCall * 48; - break; - } -#endif - - default: - { - /* Not supported yet */ - return CODEC_DB_UNSUPPORTED_FS; - } - } /* end switch */ - - inst->fs = fs; - - return ok; -} diff --git a/modules/audio_coding/NetEQ/main/source/signal_mcu.c b/modules/audio_coding/NetEQ/main/source/signal_mcu.c deleted file mode 100644 index 92929e261..000000000 --- a/modules/audio_coding/NetEQ/main/source/signal_mcu.c +++ /dev/null @@ -1,838 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Signal the MCU that data is available and ask for a RecOut decision. - */ - -#include "mcu.h" - -#include - -#include "signal_processing_library.h" - -#include "automode.h" -#include "dtmf_buffer.h" -#include "mcu_dsp_common.h" -#include "neteq_error_codes.h" - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#include - -extern FILE *delay_fid2; /* file pointer to delay log file */ -#endif - - -/* - * Signals the MCU that DSP status data is available. - */ -int WebRtcNetEQ_SignalMcu(MCUInst_t *inst) -{ - - int i_bufferpos, i_res; - WebRtc_UWord16 uw16_instr; - DSP2MCU_info_t dspInfo; - WebRtc_Word16 *blockPtr, blockLen; - WebRtc_UWord32 uw32_availableTS; - RTPPacket_t temp_pkt; - WebRtc_Word32 w32_bufsize, w32_tmp; - WebRtc_Word16 payloadType = -1; - WebRtc_Word16 wantedNoOfTimeStamps; - WebRtc_Word32 totalTS; - WebRtc_Word16 oldPT, latePacketExist = 0; - WebRtc_UWord32 oldTS, prevTS, uw32_tmp; - WebRtc_UWord16 prevSeqNo; - WebRtc_Word16 nextSeqNoAvail; - WebRtc_Word16 fs_mult, w16_tmp; - WebRtc_Word16 lastModeBGNonly = 0; -#ifdef NETEQ_DELAY_LOGGING - int temp_var; -#endif - int playDtmf = 0; - - fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000); - - /* Increment counter since last statistics report */ - inst->lastReportTS += inst->timestampsPerCall; - - /* Read info from DSP so we now current status */ - - WEBRTC_SPL_MEMCPY_W8(&dspInfo,inst->pw16_readAddress,sizeof(DSP2MCU_info_t)); - - /* Set blockPtr to first payload block */ - blockPtr = &inst->pw16_writeAddress[3]; - - /* Clear instruction word and number of lost samples (2*WebRtc_Word16) */ - inst->pw16_writeAddress[0] = 0; - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[2] = 0; - - if ((dspInfo.lastMode & MODE_AWAITING_CODEC_PTR) != 0) - { - /* - * Make sure state is adjusted so that a codec update is - * performed when first packet arrives. - */ - if (inst->new_codec != 1) - { - inst->current_Codec = -1; - } - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_AWAITING_CODEC_PTR); - } - -#ifdef NETEQ_STEREO - if ((dspInfo.lastMode & MODE_MASTER_DTMF_SIGNAL) != 0) - { - playDtmf = 1; /* force DTMF decision */ - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_MASTER_DTMF_SIGNAL); - } - - if ((dspInfo.lastMode & MODE_USING_STEREO) != 0) - { - if (inst->usingStereo == 0) - { - /* stereo mode changed; reset automode instance to re-synchronize statistics */ - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - } - inst->usingStereo = 1; - dspInfo.lastMode = (dspInfo.lastMode ^ MODE_USING_STEREO); - } - else - { - inst->usingStereo = 0; - } -#endif - - /* detect if BGN_ONLY flag is set in lastMode */ - if ((dspInfo.lastMode & MODE_BGN_ONLY) != 0) - { - lastModeBGNonly = 1; /* remember flag */ - dspInfo.lastMode ^= MODE_BGN_ONLY; /* clear the flag */ - } - - if ((dspInfo.lastMode == MODE_RFC3389CNG) || (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG) - || (dspInfo.lastMode == MODE_EXPAND)) - { - /* - * If last mode was CNG (or Expand, since this could be covering up for a lost CNG - * packet), increase the CNGplayedTS counter. - */ - inst->BufferStat_inst.uw32_CNGplayedTS += inst->timestampsPerCall; - - if (dspInfo.lastMode == MODE_RFC3389CNG) - { - /* remember that RFC3389CNG is on (needed if CNG is interrupted by DTMF) */ - inst->BufferStat_inst.w16_cngOn = CNG_RFC3389_ON; - } - else if (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG) - { - /* remember that internal CNG is on (needed if CNG is interrupted by DTMF) */ - inst->BufferStat_inst.w16_cngOn = CNG_INTERNAL_ON; - } - - } - - /* Update packet size from previously decoded packet */ - if (dspInfo.frameLen > 0) - { - inst->PacketBuffer_inst.packSizeSamples = dspInfo.frameLen; - } - - /* Look for late packet (unless codec has changed) */ - if (inst->new_codec != 1) - { - if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec)) - { - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - inst->timeStamp, &uw32_availableTS, &i_bufferpos, 1, &payloadType); - if ((inst->new_codec != 1) && (inst->timeStamp == uw32_availableTS) - && (inst->timeStamp < dspInfo.playedOutTS) && (i_bufferpos != -1) - && (WebRtcNetEQ_DbGetPayload(&(inst->codec_DB_inst), - (enum WebRtcNetEQDecoder) inst->current_Codec) == payloadType)) - { - temp_pkt.payload = blockPtr + 1; - i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt, - i_bufferpos); - if (i_res < 0) - { /* error returned */ - return i_res; - } - *blockPtr = temp_pkt.payloadLen; - /* set the flag if this is a redundant payload */ - if (temp_pkt.rcuPlCntr > 0) - { - *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG); - } - blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1; - - /* - * Close the data with a zero size block, in case we will not write any - * more data. - */ - *blockPtr = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) - | DSP_CODEC_ADD_LATE_PKT; - latePacketExist = 1; - } - } - } - - i_res = WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0), - &payloadType); - if (i_res < 0) - { /* error returned */ - return i_res; - } - - if (inst->BufferStat_inst.w16_cngOn == CNG_RFC3389_ON) - { - /* - * Because of timestamp peculiarities, we have to "manually" disallow using a CNG - * packet with the same timestamp as the one that was last played. This can happen - * when using redundancy and will cause the timing to shift. - */ - while (i_bufferpos != -1 && WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, - payloadType) && dspInfo.playedOutTS >= uw32_availableTS) - { - - /* Don't use this packet, discard it */ - inst->PacketBuffer_inst.payloadType[i_bufferpos] = -1; - inst->PacketBuffer_inst.payloadLengthBytes[i_bufferpos] = 0; - inst->PacketBuffer_inst.numPacketsInBuffer--; - - /* Check buffer again */ - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0), - &payloadType); - } - } - - /* Check packet buffer */ - w32_bufsize = WebRtcNetEQ_PacketBufferGetSize(&inst->PacketBuffer_inst); - - if (dspInfo.lastMode == MODE_SUCCESS_ACCELERATE || dspInfo.lastMode - == MODE_LOWEN_ACCELERATE || dspInfo.lastMode == MODE_SUCCESS_PREEMPTIVE - || dspInfo.lastMode == MODE_LOWEN_PREEMPTIVE) - { - /* Subtract (dspInfo.samplesLeft + inst->timestampsPerCall) from sampleMemory */ - inst->BufferStat_inst.Automode_inst.sampleMemory -= dspInfo.samplesLeft - + inst->timestampsPerCall; - /* Update post-call statistics */ - inst->statInst.jbChangeCount++; - } - - /* calculate total current buffer size (in ms*8), including sync buffer */ - w32_bufsize = WebRtcSpl_DivW32W16((w32_bufsize + dspInfo.samplesLeft), fs_mult); - - if (((WebRtc_UWord32) w32_bufsize >> 3) < inst->statInst.jbMinSize) - { - /* new all-time low */ - inst->statInst.jbMinSize = ((WebRtc_UWord32) w32_bufsize >> 3); /* shift to ms */ - } - if (((WebRtc_UWord32) w32_bufsize >> 3) > inst->statInst.jbMaxSize) - { - /* new all-time high */ - inst->statInst.jbMaxSize = ((WebRtc_UWord32) w32_bufsize >> 3); /* shift to ms */ - } - - /* Update avg bufsize: - * jbAvgSize = (jbAvgCount * jbAvgSize + w32_bufsize/8)/(jbAvgCount+1) - * with proper rounding - */ - { - WebRtc_Word32 avgTmp; - - /* Simplify the above formula to: - * jbAvgSizeQ16 = - * jbAvgSizeQ16 + ( (w32_bufsize/8 << 16) - jbAvgSizeQ16 + d ) / (jbAvgCount+1) - * where d = jbAvgCount/2 for proper rounding. - */ - - avgTmp = (((WebRtc_UWord32) w32_bufsize >> 3) << 16) - inst->statInst.jbAvgSizeQ16; - avgTmp = WEBRTC_SPL_DIV( avgTmp + (inst->statInst.jbAvgCount>>1), - inst->statInst.jbAvgCount + 1 ); - inst->statInst.jbAvgSizeQ16 += avgTmp; - - if (inst->statInst.jbAvgCount < (0xFFFF - 1)) - { - inst->statInst.jbAvgCount++; - } - } - -#ifdef NETEQ_ATEVENT_DECODE - /* DTMF data will affect the decision */ - if (WebRtcNetEQ_DtmfDecode(&inst->DTMF_inst, blockPtr + 1, blockPtr + 2, - dspInfo.playedOutTS + inst->BufferStat_inst.uw32_CNGplayedTS) > 0) - { - playDtmf = 1; - - /* Flag DTMF payload */ - inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] | DSP_DTMF_PAYLOAD; - - /* Block Length in bytes */ - blockPtr[0] = 4; - /* Advance to next payload position */ - blockPtr += 3; - } -#endif - - /* Update statistics and make decision */ - uw16_instr = WebRtcNetEQ_BufstatsDecision(&inst->BufferStat_inst, - inst->PacketBuffer_inst.packSizeSamples, w32_bufsize, dspInfo.playedOutTS, - uw32_availableTS, i_bufferpos == -1, - WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType), dspInfo.lastMode, - inst->NetEqPlayoutMode, inst->timestampsPerCall, inst->NoOfExpandCalls, fs_mult, - lastModeBGNonly, playDtmf); - - /* Check if time to reset loss counter */ - if (inst->lastReportTS > WEBRTC_SPL_UMUL(inst->fs, MAX_LOSS_REPORT_PERIOD)) - { - /* reset loss counter */ - WebRtcNetEQ_ResetMcuInCallStats(inst); - } - - /* Check sync buffer size */ - if ((dspInfo.samplesLeft >= inst->timestampsPerCall) && (uw16_instr - != BUFSTATS_DO_ACCELERATE) && (uw16_instr != BUFSTATS_DO_MERGE) && (uw16_instr - != BUFSTATS_DO_PREEMPTIVE_EXPAND)) - { - *blockPtr = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_NORMAL; - return 0; - } - - if (uw16_instr == BUFSTATS_DO_EXPAND) - { - inst->NoOfExpandCalls++; - } - else - { - /* update post-call statistics */ - WebRtc_UWord32 - expandTime = - WEBRTC_SPL_UDIV(WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_UMUL_32_16((WebRtc_UWord32) inst->NoOfExpandCalls, - (WebRtc_UWord16) 1000), - inst->timestampsPerCall), inst->fs); /* expand time in ms */ - - if (expandTime > 2000) - { - inst->statInst.countExpandMoreThan2000ms++; - } - else if (expandTime > 500) - { - inst->statInst.countExpandMoreThan500ms++; - } - else if (expandTime > 250) - { - inst->statInst.countExpandMoreThan250ms++; - } - else if (expandTime > 120) - { - inst->statInst.countExpandMoreThan120ms++; - } - - if (expandTime > inst->statInst.longestExpandDurationMs) - { - inst->statInst.longestExpandDurationMs = expandTime; - } - - /* reset counter */ - inst->NoOfExpandCalls = 0; - } - - /* New codec or big change in packet number? */ - if (((inst->new_codec) || (uw16_instr == BUFSTAT_REINIT)) && (uw16_instr - != BUFSTATS_DO_EXPAND)) - { - CodecFuncInst_t cinst; - - /* Clear other instructions */ - blockPtr = &inst->pw16_writeAddress[3]; - /* Clear instruction word */ - inst->pw16_writeAddress[0] = 0; - - inst->timeStamp = uw32_availableTS; - dspInfo.playedOutTS = uw32_availableTS; - if (inst->current_Codec != -1) - { - i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst, - (enum WebRtcNetEQDecoder) inst->current_Codec, &cinst); - if (i_res < 0) - { /* error returned */ - return i_res; - } - } - else - { - /* The main codec has not been initialized yet (first packets are DTMF or CNG). */ - if (WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType)) - { - /* The currently extracted packet is CNG; get CNG fs */ - WebRtc_UWord16 tempFs; - - tempFs = WebRtcNetEQ_DbGetSampleRate(&inst->codec_DB_inst, payloadType); - if (tempFs > 0) - { - inst->fs = tempFs; - } - } - WebRtcSpl_MemSetW16((WebRtc_Word16*) &cinst, 0, - sizeof(CodecFuncInst_t) / sizeof(WebRtc_Word16)); - cinst.codec_fs = inst->fs; - } - cinst.timeStamp = inst->timeStamp; - blockLen = (sizeof(CodecFuncInst_t)) >> (sizeof(WebRtc_Word16) - 1); /* in Word16 */ - *blockPtr = blockLen * 2; - blockPtr++; - WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst,sizeof(CodecFuncInst_t)); - blockPtr += blockLen; - inst->new_codec = 0; - - /* Reinitialize the MCU fs */ - i_res = WebRtcNetEQ_McuSetFs(inst, cinst.codec_fs); - if (i_res < 0) - { /* error returned */ - return i_res; - } - - /* Set the packet size by guessing */ - inst->PacketBuffer_inst.packSizeSamples = inst->timestampsPerCall * 3; - - WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst), - inst->PacketBuffer_inst.maxInsertPositions); - -#ifdef NETEQ_CNG_CODEC - /* Also insert CNG state as this might be needed by DSP */ - i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst, kDecoderCNG, &cinst); - if ((i_res < 0) && (i_res != CODEC_DB_NOT_EXIST1)) - { - /* other error returned */ - /* (CODEC_DB_NOT_EXIST1 simply indicates that CNG is not used */ - return i_res; - } - else - { - /* CNG exists */ - blockLen = (sizeof(cinst.codec_state)) >> (sizeof(WebRtc_Word16) - 1); - *blockPtr = blockLen * 2; - blockPtr++; - WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst.codec_state,sizeof(cinst.codec_state)); - blockPtr += blockLen; - } -#endif - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) - | DSP_CODEC_NEW_CODEC; - - if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET) - { - /* - * Change decision to CNG packet, since we do have a CNG packet, but it was - * considered too early to use. Now, use it anyway. - */ - uw16_instr = BUFSTATS_DO_RFC3389CNG_PACKET; - } - else if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET) - { - uw16_instr = BUFSTATS_DO_NORMAL; - } - - /* reset loss counter */ - WebRtcNetEQ_ResetMcuInCallStats(inst); - } - - /* Should we just reset the decoder? */ - if (uw16_instr == BUFSTAT_REINIT_DECODER) - { - /* Change decision to normal and flag decoder reset */ - uw16_instr = BUFSTATS_DO_NORMAL; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) | DSP_CODEC_RESET; - } - - /* Expand requires no new packet */ - if (uw16_instr == BUFSTATS_DO_EXPAND) - { - - inst->timeStamp = dspInfo.playedOutTS; - - /* Have we got one descriptor left? */ - if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec) - && (dspInfo.MD || latePacketExist)) - { - - if (dspInfo.lastMode != MODE_ONE_DESCRIPTOR) - { - /* this is the first "consecutive" one-descriptor decoding; reset counter */ - inst->one_desc = 0; - } - if (inst->one_desc < MAX_ONE_DESC) - { - /* use that one descriptor */ - inst->one_desc++; /* increase counter */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL_ONE_DESC; - - /* decrease counter since we did no Expand */ - inst->NoOfExpandCalls = WEBRTC_SPL_MAX(inst->NoOfExpandCalls - 1, 0); - return 0; - } - else - { - /* too many consecutive one-descriptor decodings; do expand instead */ - inst->one_desc = 0; /* reset counter */ - } - - } - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_EXPAND; - return 0; - } - - /* Merge is not needed if we still have a descriptor */ - if ((uw16_instr == BUFSTATS_DO_MERGE) && (dspInfo.MD != 0)) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL_ONE_DESC; - *blockPtr = 0; - return 0; - } - - /* Do CNG without trying to extract any packets from buffer */ - if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_RFC3389CNG; - *blockPtr = 0; - return 0; - } - - /* Do built-in CNG without extracting any new packets from buffer */ - if (uw16_instr == BUFSTATS_DO_INTERNAL_CNG_NOPACKET) - { - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_CODEC_INTERNAL_CNG; - *blockPtr = 0; - return 0; - } - - /* Do DTMF without extracting any new packets from buffer */ - if (uw16_instr == BUFSTATS_DO_DTMF_ONLY) - { - WebRtc_UWord32 timeStampJump = 0; - - /* Update timestamp */ - if ((inst->BufferStat_inst.uw32_CNGplayedTS > 0) && (dspInfo.lastMode != MODE_DTMF)) - { - /* Jump in timestamps if needed */ - timeStampJump = inst->BufferStat_inst.uw32_CNGplayedTS; - inst->pw16_writeAddress[1] = (WebRtc_UWord16) (timeStampJump >> 16); - inst->pw16_writeAddress[2] = (WebRtc_UWord16) (timeStampJump & 0xFFFF); - } - - inst->timeStamp = dspInfo.playedOutTS + timeStampJump; - - /* update post-call statistics (since we will reset the CNG counter) */ - inst->statInst.generatedSilentMs - += WEBRTC_SPL_UDIV( - WEBRTC_SPL_UMUL_32_16(inst->BufferStat_inst.uw32_CNGplayedTS, (WebRtc_UWord16) 1000), - inst->fs); - - inst->BufferStat_inst.uw32_CNGplayedTS = 0; - inst->NoOfExpandCalls = 0; - - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DTMF_GENERATE; - *blockPtr = 0; - return 0; - } - - if (uw16_instr == BUFSTATS_DO_ACCELERATE) - { - /* In order to do a Accelerate we need at least 30 ms of data */ - if (dspInfo.samplesLeft >= (3 * 80 * fs_mult)) - { - /* Already have enough data, so we do not need to extract any more */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_ACCELERATE; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (WebRtc_Word32) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* Avoid decoding more data as it might overflow playout buffer */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL; - *blockPtr = 0; - return 0; - } - else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* For >= 30ms allow Accelerate with a decoding to avoid overflow in playout buffer */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult)) - { - /* We need to decode another 10 ms in order to do an Accelerate */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else - { - /* - * Build up decoded data by decoding at least 20 ms of data. - * Do not perform Accelerate yet, but wait until we only need to do one decoding. - */ - wantedNoOfTimeStamps = 2 * inst->timestampsPerCall; - uw16_instr = BUFSTATS_DO_NORMAL; - } - } - else if (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND) - { - /* In order to do a Preemptive Expand we need at least 30 ms of data */ - if (dspInfo.samplesLeft >= (3 * 80 * fs_mult)) - { - /* Already have enough data, so we do not need to extract any more */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (WebRtc_Word32) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* - * Avoid decoding more data as it might overflow playout buffer; - * still try Preemptive Expand though. - */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - *blockPtr = 0; - inst->BufferStat_inst.Automode_inst.sampleMemory - = (WebRtc_Word32) dspInfo.samplesLeft; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - return 0; - } - else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult)) - && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult))) - { - /* - * For >= 30ms allow Preemptive Expand with a decoding to avoid overflow in - * playout buffer - */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult)) - { - /* We need to decode another 10 ms in order to do an Preemptive Expand */ - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - else - { - /* - * Build up decoded data by decoding at least 20 ms of data, - * Still try to perform Preemptive Expand. - */ - wantedNoOfTimeStamps = 2 * inst->timestampsPerCall; - } - } - else - { - wantedNoOfTimeStamps = inst->timestampsPerCall; - } - - /* Otherwise get data from buffer, try to get at least 10ms */ - totalTS = 0; - oldTS = uw32_availableTS; - if ((i_bufferpos > -1) && (uw16_instr != BUFSTATS_DO_ALTERNATIVE_PLC) && (uw16_instr - != BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS) && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION) - && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION_INC_TS)) - { - uw32_tmp = (uw32_availableTS - dspInfo.playedOutTS); - inst->pw16_writeAddress[1] = (WebRtc_UWord16) (uw32_tmp >> 16); - inst->pw16_writeAddress[2] = (WebRtc_UWord16) (uw32_tmp & 0xFFFF); - if (inst->BufferStat_inst.w16_cngOn == CNG_OFF) - { - /* - * Adjustment of TS only corresponds to an actual packet loss - * if comfort noise is not played. If comfort noise was just played, - * this adjustment of TS is only done to get back in sync with the - * stream TS; no loss to report. - */ - inst->lostTS += uw32_tmp; - } - - if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET) - { - /* We are about to decode and use a non-CNG packet => CNG period is ended */ - inst->BufferStat_inst.w16_cngOn = CNG_OFF; - } - - /* update post-call statistics */ - inst->statInst.generatedSilentMs - += WEBRTC_SPL_UDIV( - WEBRTC_SPL_UMUL_32_16(inst->BufferStat_inst.uw32_CNGplayedTS, (WebRtc_UWord16) 1000), - inst->fs); - - /* - * Reset CNG timestamp as a new packet will be delivered. - * (Also if CNG packet, since playedOutTS is updated.) - */ - inst->BufferStat_inst.uw32_CNGplayedTS = 0; - - prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos]; - prevTS = inst->PacketBuffer_inst.timeStamp[i_bufferpos]; - oldPT = inst->PacketBuffer_inst.payloadType[i_bufferpos]; - - /* clear flag bits */ - inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] & 0xFF3F; - do - { - inst->timeStamp = uw32_availableTS; - /* Write directly to shared memory */ - temp_pkt.payload = blockPtr + 1; - i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt, - i_bufferpos); - - if (i_res < 0) - { - /* error returned */ - return i_res; - } - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE; - fwrite(&temp_var,sizeof(int),1,delay_fid2); - fwrite(&temp_pkt.timeStamp,sizeof(WebRtc_UWord32),1,delay_fid2); - fwrite(&dspInfo.samplesLeft, sizeof(WebRtc_UWord16), 1, delay_fid2); -#endif - - *blockPtr = temp_pkt.payloadLen; - /* set the flag if this is a redundant payload */ - if (temp_pkt.rcuPlCntr > 0) - { - *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG); - } - blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1; - - if (i_bufferpos > -1) - { - /* - * Store number of TS extracted (last extracted is assumed to be of - * packSizeSamples). - */ - totalTS = uw32_availableTS - oldTS + inst->PacketBuffer_inst.packSizeSamples; - } - /* Check what next packet is available */ - WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst, - inst->timeStamp, &uw32_availableTS, &i_bufferpos, 0, &payloadType); - - nextSeqNoAvail = 0; - if ((i_bufferpos > -1) && (oldPT - == inst->PacketBuffer_inst.payloadType[i_bufferpos])) - { - w16_tmp = inst->PacketBuffer_inst.seqNumber[i_bufferpos] - prevSeqNo; - w32_tmp = inst->PacketBuffer_inst.timeStamp[i_bufferpos] - prevTS; - if ((w16_tmp == 1) || /* Next packet */ - ((w16_tmp == 0) && (w32_tmp == inst->PacketBuffer_inst.packSizeSamples))) - { /* or packet split into frames */ - nextSeqNoAvail = 1; - } - prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos]; - } - - } - while ((totalTS < wantedNoOfTimeStamps) && (nextSeqNoAvail == 1)); - } - - if ((uw16_instr == BUFSTATS_DO_ACCELERATE) - || (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND)) - { - /* Check that we have enough data (30ms) to do the Accelearate */ - if ((totalTS + dspInfo.samplesLeft) < WEBRTC_SPL_MUL(3,inst->timestampsPerCall) - && (uw16_instr == BUFSTATS_DO_ACCELERATE)) - { - /* Not enough, do normal operation instead */ - uw16_instr = BUFSTATS_DO_NORMAL; - } - else - { - inst->BufferStat_inst.Automode_inst.sampleMemory - = (WebRtc_Word32) dspInfo.samplesLeft + totalTS; - inst->BufferStat_inst.Automode_inst.prevTimeScale = 1; - } - } - - /* Close the data with a zero size block */ - *blockPtr = 0; - - /* Write data to DSP */ - switch (uw16_instr) - { - case BUFSTATS_DO_NORMAL: - /* Normal with decoding included */ - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_NORMAL; - break; - case BUFSTATS_DO_ACCELERATE: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_ACCELERATE; - break; - case BUFSTATS_DO_MERGE: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_MERGE; - break; - case BUFSTATS_DO_RFC3389CNG_PACKET: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_RFC3389CNG; - break; - case BUFSTATS_DO_ALTERNATIVE_PLC: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_ALTERNATIVE_PLC; - break; - case BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS; - break; - case BUFSTATS_DO_AUDIO_REPETITION: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_AUDIO_REPETITION; - break; - case BUFSTATS_DO_AUDIO_REPETITION_INC_TS: - inst->pw16_writeAddress[1] = 0; - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_DO_AUDIO_REPETITION_INC_TS; - break; - case BUFSTATS_DO_PREEMPTIVE_EXPAND: - inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) - | DSP_INSTR_PREEMPTIVE_EXPAND; - break; - default: - return UNKNOWN_BUFSTAT_DECISION; - } - - inst->timeStamp = dspInfo.playedOutTS; - return 0; - -} - diff --git a/modules/audio_coding/NetEQ/main/source/split_and_insert.c b/modules/audio_coding/NetEQ/main/source/split_and_insert.c deleted file mode 100644 index 03c156928..000000000 --- a/modules/audio_coding/NetEQ/main/source/split_and_insert.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Split an RTP payload (if possible and suitable) and insert into packet buffer. - */ - -#include "mcu.h" - -#include - -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" - -int WebRtcNetEQ_SplitAndInsertPayload(RTPPacket_t *packet, PacketBuf_t *Buffer_inst, - SplitInfo_t *split_inst, WebRtc_Word16 *flushed) -{ - - int i_ok; - int len; - int i; - RTPPacket_t temp_packet; - WebRtc_Word16 localFlushed = 0; - const WebRtc_Word16 *pw16_startPayload; - *flushed = 0; - - len = packet->payloadLen; - - /* Copy to temp packet that can be modified. */ - - WEBRTC_SPL_MEMCPY_W8(&temp_packet,packet,sizeof(RTPPacket_t)); - - if (split_inst->deltaBytes == NO_SPLIT) - { - /* Not splittable codec */ - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, packet, &localFlushed); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR5; - } - } - else if (split_inst->deltaBytes < -10) - { - /* G711, PCM16B or G722, use "soft splitting" */ - int split_size = packet->payloadLen; - int mult = WEBRTC_SPL_ABS_W32(split_inst->deltaBytes) - 10; - - /* Find "chunk size" >= 20 ms and < 40 ms - * split_inst->deltaTime in this case contains the number of bytes per - * timestamp unit times 2 - */ - while (split_size >= ((80 << split_inst->deltaTime) * mult)) - { - split_size >>= 1; - } - - /* Make the size an even value. */ - if (split_size > 1) - { - split_size >>= 1; - split_size *= 2; - } - - temp_packet.payloadLen = split_size; - pw16_startPayload = temp_packet.payload; - i = 0; - while (len >= (2 * split_size)) - { - /* insert every chunk */ - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, &localFlushed); - *flushed |= localFlushed; - temp_packet.timeStamp += ((2 * split_size) >> split_inst->deltaTime); - i++; - temp_packet.payload = &(pw16_startPayload[(i * split_size) >> 1]); - temp_packet.starts_byte1 = temp_packet.starts_byte1 ^ (split_size & 0x1); - - len -= split_size; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR1; - } - } - - /* Insert the rest */ - temp_packet.payloadLen = len; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, &localFlushed); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR2; - } - } - else - { - /* Frame based codec, use hard splitting. */ - i = 0; - pw16_startPayload = temp_packet.payload; - while (len >= split_inst->deltaBytes) - { - - temp_packet.payloadLen = split_inst->deltaBytes; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, &localFlushed); - *flushed |= localFlushed; - i++; - temp_packet.payload = &(pw16_startPayload[(i * split_inst->deltaBytes) >> 1]); - temp_packet.timeStamp += split_inst->deltaTime; - temp_packet.starts_byte1 = temp_packet.starts_byte1 ^ (split_inst->deltaBytes - & 0x1); - - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR3; - } - len -= split_inst->deltaBytes; - - } - if (len > 0) - { - /* Must be a either an error or a SID frame at the end of the packet. */ - temp_packet.payloadLen = len; - i_ok = WebRtcNetEQ_PacketBufferInsert(Buffer_inst, &temp_packet, &localFlushed); - *flushed |= localFlushed; - if (i_ok < 0) - { - return PBUFFER_INSERT_ERROR4; - } - } - } - - return 0; -} - diff --git a/modules/audio_coding/NetEQ/main/source/unmute_signal.c b/modules/audio_coding/NetEQ/main/source/unmute_signal.c deleted file mode 100644 index ee9daa823..000000000 --- a/modules/audio_coding/NetEQ/main/source/unmute_signal.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This function "unmutes" a vector on a sample by sample basis. - */ - -#include "dsp_helpfunctions.h" - -#include "signal_processing_library.h" - - -void WebRtcNetEQ_UnmuteSignal(WebRtc_Word16 *pw16_inVec, WebRtc_Word16 *startMuteFact, - WebRtc_Word16 *pw16_outVec, WebRtc_Word16 unmuteFact, - WebRtc_Word16 N) -{ - int i; - WebRtc_UWord16 w16_tmp; - WebRtc_Word32 w32_tmp; - - w16_tmp = (WebRtc_UWord16) *startMuteFact; - w32_tmp = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)w16_tmp,6) + 32; - for (i = 0; i < N; i++) - { - pw16_outVec[i] - = (WebRtc_Word16) ((WEBRTC_SPL_MUL_16_16(w16_tmp, pw16_inVec[i]) + 8192) >> 14); - w32_tmp += unmuteFact; - w32_tmp = WEBRTC_SPL_MAX(0, w32_tmp); - w16_tmp = (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(w32_tmp, 6); /* 20 - 14 = 6 */ - w16_tmp = WEBRTC_SPL_MIN(16384, w16_tmp); - } - *startMuteFact = (WebRtc_Word16) w16_tmp; -} - diff --git a/modules/audio_coding/NetEQ/main/source/webrtc_neteq.c b/modules/audio_coding/NetEQ/main/source/webrtc_neteq.c deleted file mode 100644 index 29576d8ef..000000000 --- a/modules/audio_coding/NetEQ/main/source/webrtc_neteq.c +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Implementation of main NetEQ API. - */ - -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" - -#include - -#include "typedefs.h" -#include "signal_processing_library.h" - -#include "neteq_error_codes.h" -#include "mcu_dsp_common.h" -#include "rtcp.h" - -#define RETURN_ON_ERROR( macroExpr, macroInstPtr ) { \ - if ((macroExpr) != 0) { \ - if ((macroExpr) == -1) { \ - (macroInstPtr)->ErrorCode = - (NETEQ_OTHER_ERROR); \ - } else { \ - (macroInstPtr)->ErrorCode = -((WebRtc_Word16) (macroExpr)); \ - } \ - return(-1); \ - } } - -int WebRtcNetEQ_strncpy(WebRtc_Word8 *strDest, int numberOfElements, - const WebRtc_Word8 *strSource, int count) -{ - /* check vector lengths */ - if (count > numberOfElements) - { - strDest[0] = '\0'; - return (-1); - } - else - { - strncpy(strDest, strSource, count); - return (0); - } -} - -/********************************************************** - * NETEQ Functions - */ - -/***************************************** - * Info functions - */ - -int WebRtcNetEQ_GetVersion(WebRtc_Word8 *version) -{ - char versionString[] = "3.3.0\0 "; - char endChar[] = " "; - int i = 0; - while ((versionString[i] != endChar[0]) && (i <= 20)) - { - version[i] = versionString[i]; /* To avoid using strcpy */ - i++; - } - return (0); -} - -int WebRtcNetEQ_GetErrorCode(void *inst) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - return (NetEqMainInst->ErrorCode); -} - -int WebRtcNetEQ_GetErrorName(int errorCode, WebRtc_Word8 *errorName, int maxStrLen) -{ - if ((errorName == NULL) || (maxStrLen <= 0)) - { - return (-1); - } - - if (errorCode < 0) - { - errorCode = -errorCode; // absolute value - } - - switch (errorCode) - { - case 1: // could be -1 - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "OTHER_ERROR", maxStrLen); - break; - } - case 1001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_INSTRUCTION", maxStrLen); - break; - } - case 1002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_NETWORK_TYPE", maxStrLen); - break; - } - case 1003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_DELAYVALUE", maxStrLen); - break; - } - case 1004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "FAULTY_PLAYOUTMODE", maxStrLen); - break; - } - case 1005: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CORRUPT_INSTANCE", maxStrLen); - break; - } - case 1006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "ILLEGAL_MASTER_SLAVE_SWITCH", maxStrLen); - break; - } - case 1007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "MASTER_SLAVE_ERROR", maxStrLen); - break; - } - case 2001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_BUFSTAT_DECISION", maxStrLen); - break; - } - case 2002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_DECODING", maxStrLen); - break; - } - case 2003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_SAMPLEUNDERRUN", maxStrLen); - break; - } - case 2004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECOUT_ERROR_DECODED_TOO_MUCH", - maxStrLen); - break; - } - case 3001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_CNG_ERROR", maxStrLen); - break; - } - case 3002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_UNKNOWNPAYLOAD", maxStrLen); - break; - } - case 3003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RECIN_BUFFERINSERT_ERROR", maxStrLen); - break; - } - case 4001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_INIT_ERROR", maxStrLen); - break; - } - case 4002: - case 4003: - case 4004: - case 4005: - case 4006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_INSERT_ERROR1", maxStrLen); - break; - } - case 4007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_G723_HEADER", maxStrLen); - break; - } - case 4008: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_NONEXISTING_PACKET", maxStrLen); - break; - } - case 4009: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "PBUFFER_NOT_INITIALIZED", maxStrLen); - break; - } - case 4010: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "AMBIGUOUS_ILBC_FRAME_SIZE", maxStrLen); - break; - } - case 5001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_FULL", maxStrLen); - break; - } - case 5002: - case 5003: - case 5004: - case 5005: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_NOT_EXIST", maxStrLen); - break; - } - case 5006: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNKNOWN_CODEC", maxStrLen); - break; - } - case 5007: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_PAYLOAD_TAKEN", maxStrLen); - break; - } - case 5008: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNSUPPORTED_CODEC", maxStrLen); - break; - } - case 5009: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "CODEC_DB_UNSUPPORTED_FS", maxStrLen); - break; - } - case 6001: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_DEC_PARAMETER_ERROR", maxStrLen); - break; - } - case 6002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_INSERT_ERROR", maxStrLen); - break; - } - case 6003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_GEN_UNKNOWN_SAMP_FREQ", maxStrLen); - break; - } - case 6004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "DTMF_NOT_SUPPORTED", maxStrLen); - break; - } - case 7001: - case 7002: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RED_SPLIT_ERROR", maxStrLen); - break; - } - case 7003: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RTP_TOO_SHORT_PACKET", maxStrLen); - break; - } - case 7004: - { - WebRtcNetEQ_strncpy(errorName, maxStrLen, "RTP_CORRUPT_PACKET", maxStrLen); - break; - } - default: - { - /* check for decoder error ranges */ - if (errorCode >= 6010 && errorCode <= 6810) - { - /* iSAC error code */ - WebRtcNetEQ_strncpy(errorName, maxStrLen, "iSAC ERROR", maxStrLen); - break; - } - - WebRtcNetEQ_strncpy(errorName, maxStrLen, "UNKNOWN_ERROR", maxStrLen); - return (-1); - } - } - - return (0); -} - -/* Assign functions (create not allowed in order to avoid malloc in lib) */ -int WebRtcNetEQ_AssignSize(int *sizeinbytes) -{ - *sizeinbytes = (sizeof(MainInst_t) * 2) / sizeof(WebRtc_Word16); - return (0); -} - -int WebRtcNetEQ_Assign(void **inst, void *NETEQ_inst_Addr) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) NETEQ_inst_Addr; - *inst = NETEQ_inst_Addr; - if (*inst == NULL) return (-1); - /* Clear memory */ - WebRtcSpl_MemSetW16((WebRtc_Word16*) NetEqMainInst, 0, - (sizeof(MainInst_t) / sizeof(WebRtc_Word16))); - ok = WebRtcNetEQ_McuReset(&NetEqMainInst->MCUinst); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (0); -} - -int WebRtcNetEQ_GetRecommendedBufferSize(void *inst, enum WebRtcNetEQDecoder *codec, - int noOfCodecs, enum WebRtcNetEQNetworkType nwType, - int *MaxNoOfPackets, int *sizeinbytes) -{ - int ok = 0; - int multiplier; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *MaxNoOfPackets = 0; - *sizeinbytes = 0; - ok = WebRtcNetEQ_GetDefaultCodecSettings(codec, noOfCodecs, sizeinbytes, MaxNoOfPackets); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - if (nwType == kUDPNormal) - { - multiplier = 1; - } - else if (nwType == kUDPVideoSync) - { - multiplier = 4; - } - else if (nwType == kTCPNormal) - { - multiplier = 4; - } - else if (nwType == kTCPLargeJitter) - { - multiplier = 8; - } - else if (nwType == kTCPXLargeJitter) - { - multiplier = 20; - } - else - { - NetEqMainInst->ErrorCode = -FAULTY_NETWORK_TYPE; - return (-1); - } - *MaxNoOfPackets = (*MaxNoOfPackets) * multiplier; - *sizeinbytes = (*sizeinbytes) * multiplier; - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_AssignBuffer(void *inst, int MaxNoOfPackets, void *NETEQ_Buffer_Addr, - int sizeinbytes) -{ - int ok; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_PacketBufferInit(&NetEqMainInst->MCUinst.PacketBuffer_inst, - MaxNoOfPackets, (WebRtc_Word16*) NETEQ_Buffer_Addr, (sizeinbytes >> 1)); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/************************************************ - * Init functions - */ - -/**************************************************************************** - * WebRtcNetEQ_Init(...) - * - * Initialize NetEQ. - * - * Input: - * - inst : NetEQ instance - * - fs : Initial sample rate in Hz (may change with payload) - * - * Output: - * - inst : Initialized NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_Init(void *inst, WebRtc_UWord16 fs) -{ - int ok = 0; - - /* Typecast inst to internal instance format */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - /* Start out with no PostDecode VAD instance */ - NetEqMainInst->DSPinst.VADInst.VADState = NULL; - /* Also set all VAD function pointers to NULL */ - NetEqMainInst->DSPinst.VADInst.initFunction = NULL; - NetEqMainInst->DSPinst.VADInst.setmodeFunction = NULL; - NetEqMainInst->DSPinst.VADInst.VADFunction = NULL; -#endif /* NETEQ_VAD */ - - ok = WebRtcNetEQ_DSPinit(NetEqMainInst); /* Init addresses between MCU and DSP */ - RETURN_ON_ERROR(ok, NetEqMainInst); - - ok = WebRtcNetEQ_DSPInit(&NetEqMainInst->DSPinst, fs); /* Init dsp side */ - RETURN_ON_ERROR(ok, NetEqMainInst); - /* set BGN mode to default, since it is not cleared by DSP init function */ - NetEqMainInst->DSPinst.BGNInst.bgnMode = BGN_ON; - - /* init statistics functions and counters */ - ok = WebRtcNetEQ_ClearInCallStats(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - ok = WebRtcNetEQ_ClearPostCallStats(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - ok = WebRtcNetEQ_ResetMcuJitterStat(&NetEqMainInst->MCUinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* flush packet buffer */ - ok = WebRtcNetEQ_PacketBufferFlush(&NetEqMainInst->MCUinst.PacketBuffer_inst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* set some variables to initial values */ - NetEqMainInst->MCUinst.current_Codec = -1; - NetEqMainInst->MCUinst.current_Payload = -1; - NetEqMainInst->MCUinst.first_packet = 1; - NetEqMainInst->MCUinst.one_desc = 0; - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = 0; - NetEqMainInst->MCUinst.NoOfExpandCalls = 0; - NetEqMainInst->MCUinst.fs = fs; - -#ifdef NETEQ_ATEVENT_DECODE - /* init DTMF decoder */ - ok = WebRtcNetEQ_DtmfDecoderInit(&(NetEqMainInst->MCUinst.DTMF_inst),fs,560); - RETURN_ON_ERROR(ok, NetEqMainInst); -#endif - - /* init RTCP statistics */ - WebRtcNetEQ_RTCPInit(&(NetEqMainInst->MCUinst.RTCP_inst), 0); - - /* set BufferStat struct to zero */ - WebRtcSpl_MemSetW16((WebRtc_Word16*) &(NetEqMainInst->MCUinst.BufferStat_inst), 0, - sizeof(BufstatsInst_t) / sizeof(WebRtc_Word16)); - - /* reset automode */ - WebRtcNetEQ_ResetAutomode(&(NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst), - NetEqMainInst->MCUinst.PacketBuffer_inst.maxInsertPositions); - - NetEqMainInst->ErrorCode = 0; - -#ifdef NETEQ_STEREO - /* set master/slave info to undecided */ - NetEqMainInst->masterSlave = 0; -#endif - - return (ok); -} - -int WebRtcNetEQ_FlushBuffers(void *inst) -{ - int ok = 0; - - /* Typecast inst to internal instance format */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - - /* Flush packet buffer */ - ok = WebRtcNetEQ_PacketBufferFlush(&NetEqMainInst->MCUinst.PacketBuffer_inst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - /* Set MCU to wait for new codec */ - NetEqMainInst->MCUinst.first_packet = 1; - - /* Flush speech buffer */ - ok = WebRtcNetEQ_FlushSpeechBuffer(&NetEqMainInst->DSPinst); - RETURN_ON_ERROR(ok, NetEqMainInst); - - return 0; -} - -int WebRtcNetEQ_SetAVTPlayout(void *inst, int PlayoutAVTon) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); -#ifdef NETEQ_ATEVENT_DECODE - NetEqMainInst->MCUinst.AVT_PlayoutOn = PlayoutAVTon; - return(0); -#else - if (PlayoutAVTon != 0) - { - NetEqMainInst->ErrorCode = -DTMF_NOT_SUPPORTED; - return (-1); - } - else - { - return (0); - } -#endif -} - -int WebRtcNetEQ_SetExtraDelay(void *inst, int DelayInMs) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - if ((DelayInMs < 0) || (DelayInMs > 1000)) - { - NetEqMainInst->ErrorCode = -FAULTY_DELAYVALUE; - return (-1); - } - NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs = DelayInMs; - return (0); -} - -int WebRtcNetEQ_SetPlayoutMode(void *inst, enum WebRtcNetEQPlayoutMode playoutMode) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - if ((playoutMode != kPlayoutOn) && (playoutMode != kPlayoutOff) && (playoutMode - != kPlayoutFax) && (playoutMode != kPlayoutStreaming)) - { - NetEqMainInst->ErrorCode = -FAULTY_PLAYOUTMODE; - return (-1); - } - else - { - NetEqMainInst->MCUinst.NetEqPlayoutMode = playoutMode; - return (0); - } -} - -int WebRtcNetEQ_SetBGNMode(void *inst, enum WebRtcNetEQBGNMode bgnMode) -{ - - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - NetEqMainInst->DSPinst.BGNInst.bgnMode = (enum BGNMode) bgnMode; - - return (0); -} - -int WebRtcNetEQ_GetBGNMode(const void *inst, enum WebRtcNetEQBGNMode *bgnMode) -{ - - const MainInst_t *NetEqMainInst = (const MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - *bgnMode = (enum WebRtcNetEQBGNMode) NetEqMainInst->DSPinst.BGNInst.bgnMode; - - return (0); -} - -/************************************************ - * CodecDB functions - */ - -int WebRtcNetEQ_CodecDbReset(void *inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_DbReset(&NetEqMainInst->MCUinst.codec_DB_inst); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - /* set function pointers to NULL to prevent RecOut from using the codec */ - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeRCU = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcAddLatePkt = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeInit = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodePLC = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetMDinfo = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcUpdBWEst = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetErrorCode = NULL; - - return (0); -} - -int WebRtcNetEQ_CodecDbGetSizeInfo(void *inst, WebRtc_Word16 *UsedEntries, - WebRtc_Word16 *MaxEntries) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *MaxEntries = NUM_CODECS; - *UsedEntries = NetEqMainInst->MCUinst.codec_DB_inst.nrOfCodecs; - return (0); -} - -int WebRtcNetEQ_CodecDbGetCodecInfo(void *inst, WebRtc_Word16 Entry, - enum WebRtcNetEQDecoder *codec) -{ - int i; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *codec = (enum WebRtcNetEQDecoder) 0; - if ((Entry >= 0) && (Entry < NetEqMainInst->MCUinst.codec_DB_inst.nrOfCodecs)) - { - for (i = 0; i < NUM_TOTAL_CODECS; i++) - { - if (NetEqMainInst->MCUinst.codec_DB_inst.position[i] == Entry) - { - *codec = (enum WebRtcNetEQDecoder) i; - } - } - } - else - { - NetEqMainInst->ErrorCode = -(CODEC_DB_NOT_EXIST1); - return (-1); - } - return (0); -} - -int WebRtcNetEQ_CodecDbAdd(void *inst, WebRtcNetEQ_CodecDef *codecInst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_DbAdd(&NetEqMainInst->MCUinst.codec_DB_inst, codecInst->codec, - codecInst->payloadType, codecInst->funcDecode, codecInst->funcDecodeRCU, - codecInst->funcDecodePLC, codecInst->funcDecodeInit, codecInst->funcAddLatePkt, - codecInst->funcGetMDinfo, codecInst->funcGetPitch, codecInst->funcUpdBWEst, - codecInst->funcGetErrorCode, codecInst->codec_state, codecInst->codec_fs); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_CodecDbRemove(void *inst, enum WebRtcNetEQDecoder codec) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - /* check if currently used codec is being removed */ - if (NetEqMainInst->MCUinst.current_Codec == (WebRtc_Word16) codec) - { - /* set function pointers to NULL to prevent RecOut from using the codec */ - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeRCU = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcAddLatePkt = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecode = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodeInit = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcDecodePLC = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetMDinfo = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcUpdBWEst = NULL; - NetEqMainInst->DSPinst.codec_ptr_inst.funcGetErrorCode = NULL; - } - - ok = WebRtcNetEQ_DbRemove(&NetEqMainInst->MCUinst.codec_DB_inst, codec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/********************************* - * Real-time functions - */ - -int WebRtcNetEQ_RecIn(void *inst, WebRtc_Word16 *p_w16datagramstart, WebRtc_Word16 w16_RTPlen, - WebRtc_UWord32 uw32_timeRec) -{ - int ok = 0; - RTPPacket_t RTPpacket; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - /* Parse RTP header */ - ok = WebRtcNetEQ_RTPPayloadInfo(p_w16datagramstart, w16_RTPlen, &RTPpacket); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - ok = WebRtcNetEQ_RecInInternal(&NetEqMainInst->MCUinst, &RTPpacket, uw32_timeRec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/**************************************************************************** - * WebRtcNetEQ_RecInRTPStruct(...) - * - * Alternative RecIn function, used when the RTP data has already been - * parsed into an RTP info struct (WebRtcNetEQ_RTPInfo). - * - * Input: - * - inst : NetEQ instance - * - rtpInfo : Pointer to RTP info - * - payloadPtr : Pointer to the RTP payload (first byte after header) - * - payloadLenBytes : Length (in bytes) of the payload in payloadPtr - * - timeRec : Receive time (in timestamps of the used codec) - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNetEQ_RecInRTPStruct(void *inst, WebRtcNetEQ_RTPInfo *rtpInfo, - const WebRtc_UWord8 *payloadPtr, WebRtc_Word16 payloadLenBytes, - WebRtc_UWord32 uw32_timeRec) -{ - int ok = 0; - RTPPacket_t RTPpacket; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->MCUinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - /* Load NetEQ's RTP struct from Module RTP struct */ - RTPpacket.payloadType = rtpInfo->payloadType; - RTPpacket.seqNumber = rtpInfo->sequenceNumber; - RTPpacket.timeStamp = rtpInfo->timeStamp; - RTPpacket.ssrc = rtpInfo->SSRC; - RTPpacket.payload = (const WebRtc_Word16*) payloadPtr; - RTPpacket.payloadLen = payloadLenBytes; - RTPpacket.starts_byte1 = 0; - - ok = WebRtcNetEQ_RecInInternal(&NetEqMainInst->MCUinst, &RTPpacket, uw32_timeRec); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_RecOut(void *inst, WebRtc_Word16 *pw16_outData, WebRtc_Word16 *pw16_len) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; -#ifdef NETEQ_STEREO - MasterSlaveInfo msInfo; - msInfo.msMode = NETEQ_MONO; -#endif - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - -#ifdef NETEQ_STEREO - NetEqMainInst->DSPinst.msInfo = &msInfo; -#endif - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 0 /* not BGN only */); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -/**************************************************************************** - * WebRtcNetEQ_RecOutMasterSlave(...) - * - * RecOut function for running several NetEQ instances in master/slave mode. - * One master can be used to control several slaves. - * - * Input: - * - inst : NetEQ instance - * - isMaster : Non-zero indicates that this is the master channel - * - msInfo : (slave only) Information from master - * - * Output: - * - inst : Updated NetEQ instance - * - pw16_outData : Pointer to vector where output should be written - * - pw16_len : Pointer to variable where output length is returned - * - msInfo : (master only) Information to slave(s) - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_RecOutMasterSlave(void *inst, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len, void *msInfo, - WebRtc_Word16 isMaster) -{ -#ifndef NETEQ_STEREO - /* Stereo not supported */ - return(-1); -#else - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - - if (msInfo == NULL) - { - /* msInfo not provided */ - NetEqMainInst->ErrorCode = NETEQ_OTHER_ERROR; - return (-1); - } - - /* translate from external to internal Master/Slave information */ - NetEqMainInst->DSPinst.msInfo = (MasterSlaveInfo *) msInfo; - - /* check that we have not done a master/slave switch without first re-initializing */ - if ((NetEqMainInst->masterSlave == 1 && !isMaster) || /* switch from master to slave */ - (NetEqMainInst->masterSlave == 2 && isMaster)) /* switch from slave to master */ - { - NetEqMainInst->ErrorCode = ILLEGAL_MASTER_SLAVE_SWITCH; - return (-1); - } - - if (!isMaster) - { - /* this is the slave */ - NetEqMainInst->masterSlave = 2; - NetEqMainInst->DSPinst.msInfo->msMode = NETEQ_SLAVE; - } - else - { - NetEqMainInst->DSPinst.msInfo->msMode = NETEQ_MASTER; - } - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 0 /* not BGN only */); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - - if (isMaster) - { - /* this is the master */ - NetEqMainInst->masterSlave = 1; - } - - return (ok); -#endif -} - -int WebRtcNetEQ_GetMasterSlaveInfoSize() -{ -#ifdef NETEQ_STEREO - return (sizeof(MasterSlaveInfo)); -#else - return(-1); -#endif -} - -/* Special RecOut that does not do any decoding. */ -int WebRtcNetEQ_RecOutNoDecode(void *inst, WebRtc_Word16 *pw16_outData, - WebRtc_Word16 *pw16_len) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; -#ifdef NETEQ_STEREO - MasterSlaveInfo msInfo; -#endif - - if (NetEqMainInst == NULL) return (-1); - - /* Check for corrupt/cleared instance */ - if (NetEqMainInst->DSPinst.main_inst != NetEqMainInst) - { - /* Instance is corrupt */ - NetEqMainInst->ErrorCode = CORRUPT_INSTANCE; - return (-1); - } - -#ifdef NETEQ_STEREO - /* keep same mode as before */ - switch (NetEqMainInst->masterSlave) - { - case 1: - { - msInfo.msMode = NETEQ_MASTER; - break; - } - case 2: - { - msInfo.msMode = NETEQ_SLAVE; - break; - } - default: - { - msInfo.msMode = NETEQ_MONO; - break; - } - } - - NetEqMainInst->DSPinst.msInfo = &msInfo; -#endif - - ok = WebRtcNetEQ_RecOutInternal(&NetEqMainInst->DSPinst, pw16_outData, - pw16_len, 1 /* BGN only */); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetRTCPStats(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_RTCPGetStats(&NetEqMainInst->MCUinst.RTCP_inst, - &RTCP_inst->fraction_lost, &RTCP_inst->cum_lost, &RTCP_inst->ext_max, - &RTCP_inst->jitter, 0); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetRTCPStatsNoReset(void *inst, WebRtcNetEQ_RTCPStat *RTCP_inst) -{ - int ok = 0; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - ok = WebRtcNetEQ_RTCPGetStats(&NetEqMainInst->MCUinst.RTCP_inst, - &RTCP_inst->fraction_lost, &RTCP_inst->cum_lost, &RTCP_inst->ext_max, - &RTCP_inst->jitter, 1); - if (ok != 0) - { - NetEqMainInst->ErrorCode = -ok; - return (-1); - } - return (ok); -} - -int WebRtcNetEQ_GetSpeechTimeStamp(void *inst, WebRtc_UWord32 *timestamp) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - - if (NetEqMainInst->MCUinst.TSscalingInitialized) - { - *timestamp = WebRtcNetEQ_ScaleTimestampInternalToExternal(&NetEqMainInst->MCUinst, - NetEqMainInst->DSPinst.videoSyncTimestamp); - } - else - { - *timestamp = NetEqMainInst->DSPinst.videoSyncTimestamp; - } - - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_GetSpeechOutputType(...) - * - * Get the output type for the audio provided by the latest call to - * WebRtcNetEQ_RecOut(). - * - * kOutputNormal = normal audio (possibly processed) - * kOutputPLC = loss concealment through stretching audio - * kOutputCNG = comfort noise (codec-internal or RFC3389) - * kOutputPLCtoCNG = background noise only due to long expand or error - * kOutputVADPassive = PostDecode VAD signalling passive speaker - * - * Input: - * - inst : NetEQ instance - * - * Output: - * - outputType : Output type from enum list WebRtcNetEQOutputType - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_GetSpeechOutputType(void *inst, enum WebRtcNetEQOutputType *outputType) -{ - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - if (NetEqMainInst == NULL) - { - return (-1); - } - - if ((NetEqMainInst->DSPinst.w16_mode & MODE_BGN_ONLY) != 0) - { - /* If last mode was background noise only */ - *outputType = kOutputPLCtoCNG; - - } - else if ((NetEqMainInst->DSPinst.w16_mode == MODE_CODEC_INTERNAL_CNG) - || (NetEqMainInst->DSPinst.w16_mode == MODE_RFC3389CNG)) - { - /* If CN or internal CNG */ - *outputType = kOutputCNG; - -#ifdef NETEQ_VAD - } - else if ( NetEqMainInst->DSPinst.VADInst.VADDecision == 0 ) - { - /* post-decode VAD says passive speaker */ - *outputType = kOutputVADPassive; -#endif /* NETEQ_VAD */ - - } - else if ((NetEqMainInst->DSPinst.w16_mode == MODE_EXPAND) - && (NetEqMainInst->DSPinst.ExpandInst.w16_expandMuteFactor == 0)) - { - /* Expand mode has faded down to background noise only (very long expand) */ - *outputType = kOutputPLCtoCNG; - - } - else if (NetEqMainInst->DSPinst.w16_mode == MODE_EXPAND) - { - /* PLC mode */ - *outputType = kOutputPLC; - - } - else - { - /* Normal speech output type (can still be manipulated, e.g., accelerated) */ - *outputType = kOutputNormal; - } - - return (0); -} - -/********************************** - * Functions related to VQmon - */ - -#define WEBRTC_NETEQ_CONCEALMENTFLAG_LOST 0x01 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_DISCARDED 0x02 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_SUPRESS 0x04 -#define WEBRTC_NETEQ_CONCEALMENTFLAG_CNGACTIVE 0x80 - -int WebRtcNetEQ_VQmonRecOutStatistics(void *inst, WebRtc_UWord16 *validVoiceDurationMs, - WebRtc_UWord16 *concealedVoiceDurationMs, - WebRtc_UWord8 *concealedVoiceFlags) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - WebRtc_Word16 fs_mult; - WebRtc_Word16 ms_lost; - if (NetEqMainInst == NULL) return (-1); - fs_mult = WebRtcSpl_DivW32W16ResW16(NetEqMainInst->MCUinst.fs, 8000); - - ms_lost = WebRtcSpl_DivW32W16ResW16( - (WebRtc_Word32) NetEqMainInst->DSPinst.w16_concealedTS, (WebRtc_Word16) (8 * fs_mult)); - if (ms_lost > NetEqMainInst->DSPinst.millisecondsPerCall) ms_lost - = NetEqMainInst->DSPinst.millisecondsPerCall; - - *validVoiceDurationMs = NetEqMainInst->DSPinst.millisecondsPerCall - ms_lost; - *concealedVoiceDurationMs = ms_lost; - if (ms_lost > 0) - { - *concealedVoiceFlags = WEBRTC_NETEQ_CONCEALMENTFLAG_LOST; - } - else - { - *concealedVoiceFlags = 0; - } - NetEqMainInst->DSPinst.w16_concealedTS -= ms_lost * (8 * fs_mult); - - return (0); -} - -int WebRtcNetEQ_VQmonGetConfiguration(void *inst, WebRtc_UWord16 *absMaxDelayMs, - WebRtc_UWord8 *adaptationRate) -{ - /* Dummy check the inst, just to avoid compiler warnings. */ - if (inst == NULL) - { - /* Do nothing. */ - } - - /* Hardcoded variables that are used for VQmon as jitter buffer parameters */ - *absMaxDelayMs = 240; - *adaptationRate = 1; - return (0); -} - -int WebRtcNetEQ_VQmonGetRxStatistics(void *inst, WebRtc_UWord16 *avgDelayMs, - WebRtc_UWord16 *maxDelayMs) -{ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) return (-1); - *avgDelayMs = (WebRtc_UWord16) (NetEqMainInst->MCUinst.BufferStat_inst.avgDelayMsQ8 >> 8); - *maxDelayMs = (WebRtc_UWord16) NetEqMainInst->MCUinst.BufferStat_inst.maxDelayMs; - return (0); -} - -/************************************* - * Statistics functions - */ - -/* Get the "in-call" statistics from NetEQ. - * The statistics are reset after the query. */ -int WebRtcNetEQ_GetNetworkStatistics(void *inst, WebRtcNetEQ_NetworkStatistics *stats) - -{ - - WebRtc_UWord16 tempU16; - WebRtc_UWord32 tempU32, tempU32_2; - int numShift; - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - /*******************/ - /* Get buffer size */ - /*******************/ - - if (WebRtcNetEQ_GetCurrentDelay((void *) inst, &(stats->currentBufferSize)) != 0) - { - return (-1); - } - - /***************************/ - /* Get optimal buffer size */ - /***************************/ - - if (NetEqMainInst->MCUinst.fs != 0 && NetEqMainInst->MCUinst.fs <= WEBRTC_SPL_WORD16_MAX) - { - /* preferredBufferSize = Bopt * packSizeSamples / (fs/1000) */ - stats->preferredBufferSize - = (WebRtc_UWord16) WEBRTC_SPL_MUL_16_16( - (WebRtc_Word16) ((NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.optBufLevel) >> 8), /* optimal buffer level in packets shifted to Q0 */ - WebRtcSpl_DivW32W16ResW16( - (WebRtc_Word32) NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.packetSpeechLenSamp, /* samples per packet */ - WebRtcSpl_DivW32W16ResW16( (WebRtc_Word32) NetEqMainInst->MCUinst.fs, (WebRtc_Word16) 1000 ) /* samples per ms */ - ) ); - - /* add extra delay */ - if (NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs > 0) - { - stats->preferredBufferSize - += NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs; - } - } - else - { - /* sample rate not initialized */ - stats->preferredBufferSize = 0; - } - - /***********************/ - /* Calculate loss rate */ - /***********************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->MCUinst.lostTS == 0) - { - /* no losses */ - stats->currentPacketLossRate = 0; - } - else if (NetEqMainInst->MCUinst.lostTS < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->MCUinst.lostTS); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentPacketLossRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (WebRtc_UWord16) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( (WebRtc_UWord32) NetEqMainInst->MCUinst.lostTS, numShift); - - stats->currentPacketLossRate = (WebRtc_UWord16) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentPacketLossRate = 1 << 14; /* 1 in Q14 */ - } - - /**************************/ - /* Calculate discard rate */ - /**************************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - /* number of discarded samples */ - tempU32_2 - = WEBRTC_SPL_MUL_16_U16( (WebRtc_Word16) NetEqMainInst->MCUinst.PacketBuffer_inst.packSizeSamples, - NetEqMainInst->MCUinst.PacketBuffer_inst.discardedPackets); - - if (tempU32_2 == 0) - { - /* no discarded samples */ - stats->currentDiscardRate = 0; - } - else if (tempU32_2 < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(tempU32_2); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentDiscardRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (WebRtc_UWord16) tempU32; - - /* do the shift of numerator */ - tempU32 = WEBRTC_SPL_SHIFT_W32( tempU32_2, numShift); - - stats->currentDiscardRate = (WebRtc_UWord16) WebRtcSpl_DivU32U16(tempU32, tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentDiscardRate = 1 << 14; /* 1 in Q14 */ - } - - /*************************************************************/ - /* Calculate Accelerate, Expand and Pre-emptive Expand rates */ - /*************************************************************/ - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.accelerateLength == 0) - { - /* no accelerate */ - stats->currentAccelerateRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.accelerateLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.accelerateLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentAccelerateRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (WebRtc_UWord16) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.accelerateLength, numShift); - - stats->currentAccelerateRate = (WebRtc_UWord16) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentAccelerateRate = 1 << 14; /* 1 in Q14 */ - } - - /* also transfer measure to post-call statistics */ - NetEqMainInst->MCUinst.statInst.accelerateMs - += WebRtcSpl_DivU32U16( - WEBRTC_SPL_UMUL_32_16( NetEqMainInst->DSPinst.statInst.accelerateLength, (WebRtc_UWord16) 1000), - NetEqMainInst->MCUinst.fs); - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.expandLength == 0) - { - /* no expand */ - stats->currentExpandRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.expandLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.expandLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentExpandRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (WebRtc_UWord16) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.expandLength, numShift); - - stats->currentExpandRate = (WebRtc_UWord16) WebRtcSpl_DivU32U16(tempU32, tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentExpandRate = 1 << 14; /* 1 in Q14 */ - } - - /* timestamps elapsed since last report */ - tempU32 = NetEqMainInst->MCUinst.lastReportTS; - - if (NetEqMainInst->DSPinst.statInst.preemptiveLength == 0) - { - /* no pre-emptive expand */ - stats->currentPreemptiveRate = 0; - } - else if (NetEqMainInst->DSPinst.statInst.preemptiveLength < tempU32) - { - /* calculate shifts; we want the result in Q14 */ - numShift = WebRtcSpl_NormU32(NetEqMainInst->DSPinst.statInst.preemptiveLength); /* numerator shift for normalize */ - - if (numShift < 14) - { - /* cannot shift numerator 14 steps; shift denominator too */ - tempU32 = WEBRTC_SPL_RSHIFT_U32(tempU32, 14-numShift); /* right-shift */ - } - else - { - /* shift no more than 14 steps */ - numShift = 14; - } - - if (tempU32 == 0) - { - /* check for zero denominator; result should be zero in this case */ - stats->currentPreemptiveRate = 0; - } - else - { - /* check that denominator fits in signed 16-bit */ - while (tempU32 > WEBRTC_SPL_WORD16_MAX) - { - tempU32 >>= 1; /* right-shift 1 step */ - numShift--; /* compensate in numerator */ - } - tempU16 = (WebRtc_UWord16) tempU32; - - /* do the shift of numerator */ - tempU32 - = WEBRTC_SPL_SHIFT_W32( NetEqMainInst->DSPinst.statInst.preemptiveLength, numShift); - - stats->currentPreemptiveRate = (WebRtc_UWord16) WebRtcSpl_DivU32U16(tempU32, - tempU16); - } - } - else - { - /* lost count is larger than elapsed time count; probably timestamp wrap-around or something else wrong */ - /* set loss rate = 1 */ - stats->currentPreemptiveRate = 1 << 14; /* 1 in Q14 */ - } - - /* reset counters */ - WebRtcNetEQ_ResetMcuInCallStats(&(NetEqMainInst->MCUinst)); - WebRtcNetEQ_ClearInCallStats(&(NetEqMainInst->DSPinst)); - - return (0); -} - -/* Get the optimal buffer size calculated for the current network conditions. */ -int WebRtcNetEQ_GetPreferredBufferSize(void *inst, WebRtc_UWord16 *preferredBufferSize) -{ - - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /***************************/ - /* Get optimal buffer size */ - /***************************/ - - if (NetEqMainInst->MCUinst.fs != 0 && NetEqMainInst->MCUinst.fs <= WEBRTC_SPL_WORD16_MAX) - { - /* preferredBufferSize = Bopt * packSizeSamples / (fs/1000) */ - *preferredBufferSize - = (WebRtc_UWord16) WEBRTC_SPL_MUL_16_16( - (WebRtc_Word16) ((NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.optBufLevel) >> 8), /* optimal buffer level in packets shifted to Q0 */ - WebRtcSpl_DivW32W16ResW16( - (WebRtc_Word32) NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.packetSpeechLenSamp, /* samples per packet */ - WebRtcSpl_DivW32W16ResW16( (WebRtc_Word32) NetEqMainInst->MCUinst.fs, (WebRtc_Word16) 1000 ) /* samples per ms */ - ) ); - - /* add extra delay */ - if (NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs > 0) - { - *preferredBufferSize - += NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.extraDelayMs; - } - } - else - { - /* sample rate not initialized */ - *preferredBufferSize = 0; - } - - return (0); - -} - -/* Get the current buffer size in ms. Return value is 0 if ok, -1 if error. */ -int WebRtcNetEQ_GetCurrentDelay(const void *inst, WebRtc_UWord16 *currentDelayMs) -{ - WebRtc_Word32 temp32; - const MainInst_t *NetEqMainInst = (const MainInst_t*) inst; - - /* Instance sanity */ - if (NetEqMainInst == NULL) return (-1); - - /*******************/ - /* Get buffer size */ - /*******************/ - - if (NetEqMainInst->MCUinst.fs != 0 && NetEqMainInst->MCUinst.fs <= WEBRTC_SPL_WORD16_MAX) - { - /* query packet buffer for number of samples */ - temp32 = WebRtcNetEQ_PacketBufferGetSize(&NetEqMainInst->MCUinst.PacketBuffer_inst); - - /* divide by sample rate */ - *currentDelayMs = WebRtcSpl_DivW32W16ResW16(temp32 * 1000, /* multiply by 1000 to get ms */ - (WebRtc_Word16) NetEqMainInst->MCUinst.fs); /* divide by fs in samples per second */ - - /* add number of samples yet to play in sync buffer */ - temp32 = (WebRtc_Word32) (NetEqMainInst->DSPinst.endPosition - - NetEqMainInst->DSPinst.curPosition); - *currentDelayMs += WebRtcSpl_DivW32W16ResW16(temp32 * 1000, /* multiply by 1000 to get ms */ - (WebRtc_Word16) NetEqMainInst->MCUinst.fs); /* divide by fs in samples per second */ - } - else - { - /* sample rate not initialized */ - *currentDelayMs = 0; - } - - return 0; -} - -/* Get the "post-call" jitter statistics from NetEQ. - * The statistics are not reset by the query. Use the function WebRtcNetEQ_ResetJitterStatistics - * to reset the statistics. */ -int WebRtcNetEQ_GetJitterStatistics(void *inst, WebRtcNetEQ_JitterStatistics *jitterStats) -{ - - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - /* collect statistics not yet transfered to post-call statistics */ - NetEqMainInst->MCUinst.statInst.accelerateMs - += WebRtcSpl_DivU32U16( - WEBRTC_SPL_UMUL_32_16( NetEqMainInst->DSPinst.statInst.accelerateLength, (WebRtc_UWord16) 1000), - NetEqMainInst->MCUinst.fs); - - jitterStats->jbMinSize = NetEqMainInst->MCUinst.statInst.jbMinSize; /* smallest Jitter Buffer size during call - mSec */ - jitterStats->jbMaxSize = NetEqMainInst->MCUinst.statInst.jbMaxSize; /* largest Jitter Buffer size during call - mSec */ - jitterStats->jbAvgSize = NetEqMainInst->MCUinst.statInst.jbAvgSizeQ16 >> 16; /* the average JB size, measured over time - mSec */ - jitterStats->jbChangeCount = NetEqMainInst->MCUinst.statInst.jbChangeCount; /* number of times the Jitter Buffer changed */ - jitterStats->lateLossMs = (NetEqMainInst->MCUinst.PacketBuffer_inst.totalDiscardedPackets - * NetEqMainInst->MCUinst.PacketBuffer_inst.packSizeSamples * 1000) - / NetEqMainInst->MCUinst.fs; /* number of frames received late */ - jitterStats->accelerateMs = NetEqMainInst->MCUinst.statInst.accelerateMs; /* milliseconds removed to reduce jitter buffer size */ - jitterStats->flushedMs = (NetEqMainInst->MCUinst.PacketBuffer_inst.totalFlushedPackets - * NetEqMainInst->MCUinst.PacketBuffer_inst.packSizeSamples * 1000) - / NetEqMainInst->MCUinst.fs; - jitterStats->generatedSilentMs = NetEqMainInst->MCUinst.statInst.generatedSilentMs; /* number of generated silence frames */ - jitterStats->countExpandMoreThan120ms - = NetEqMainInst->MCUinst.statInst.countExpandMoreThan120ms; /* count of tiny BFI events output audio */ - jitterStats->countExpandMoreThan250ms - = NetEqMainInst->MCUinst.statInst.countExpandMoreThan250ms; /* count of small BFI events output audio */ - jitterStats->countExpandMoreThan500ms - = NetEqMainInst->MCUinst.statInst.countExpandMoreThan500ms; /* count of medium BFI events output audio */ - jitterStats->countExpandMoreThan2000ms - = NetEqMainInst->MCUinst.statInst.countExpandMoreThan2000ms; /* count of large BFI events output audio */ - jitterStats->longestExpandDurationMs - = NetEqMainInst->MCUinst.statInst.longestExpandDurationMs; /* mSec duration of longest audio drop-out */ - jitterStats->countIAT500ms - = NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.countIAT500ms; /* count of times we got small network outage */ - jitterStats->countIAT1000ms - = NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.countIAT1000ms; /* count of times we got medium network outage */ - jitterStats->countIAT2000ms - = NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.countIAT2000ms; /* count of times we got large network outage */ - jitterStats->longestIATms - = NetEqMainInst->MCUinst.BufferStat_inst.Automode_inst.longestIATms; /* mSec duration of longest network outage */ - jitterStats->minPacketDelayMs = NetEqMainInst->MCUinst.statInst.minPacketDelayMs; /* min time incoming frame "waited" to be played */ - jitterStats->maxPacketDelayMs = NetEqMainInst->MCUinst.statInst.maxPacketDelayMs; /* max time incoming frame "waited" to be played */ - jitterStats->avgPacketDelayMs = NetEqMainInst->MCUinst.statInst.avgPacketDelayMs; /* avg time incoming frame "waited" to be played */ - - jitterStats->interpolatedVoiceMs - = WebRtcSpl_DivU32U16( - WEBRTC_SPL_UMUL_32_16( NetEqMainInst->DSPinst.statInst.expandedVoiceSamples, (WebRtc_UWord16) 1000), - NetEqMainInst->MCUinst.fs); /* interpolated voice in ms */ - jitterStats->interpolatedSilentMs - = WebRtcSpl_DivU32U16( - WEBRTC_SPL_UMUL_32_16( NetEqMainInst->DSPinst.statInst.expandedNoiseSamples, (WebRtc_UWord16) 1000), - NetEqMainInst->MCUinst.fs); /* interpolated silence in ms */ - - return (0); -} - -/* Reset "post-call" jitter statistics. */ -int WebRtcNetEQ_ResetJitterStatistics(void *inst) -{ - - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - - WebRtcNetEQ_ResetMcuJitterStat(&(NetEqMainInst->MCUinst)); - - WebRtcNetEQ_ClearPostCallStats(&(NetEqMainInst->DSPinst)); - - return (0); -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADInstance(...) - * - * Provide a pointer to an allocated VAD instance. If function is never - * called or it is called with NULL pointer as VAD_inst, the post-decode - * VAD functionality is disabled. Also provide pointers to init, setmode - * and VAD functions. These are typically pointers to WebRtcVad_Init, - * WebRtcVad_set_mode and WebRtcVad_Process, respectively, all found in the - * interface file webrtc_vad.h. - * - * Input: - * - NetEQ_inst : NetEQ instance - * - VADinst : VAD instance - * - initFunction : Pointer to VAD init function - * - setmodeFunction : Pointer to VAD setmode function - * - VADfunction : Pointer to VAD function - * - * Output: - * - NetEQ_inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADInstance(void *NetEQ_inst, void *VAD_inst, - WebRtcNetEQ_VADInitFunction initFunction, - WebRtcNetEQ_VADSetmodeFunction setmodeFunction, - WebRtcNetEQ_VADFunction VADFunction) -{ - - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) NetEQ_inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - - /* Store pointer in PostDecode VAD struct */ - NetEqMainInst->DSPinst.VADInst.VADState = VAD_inst; - - /* Store function pointers */ - NetEqMainInst->DSPinst.VADInst.initFunction = initFunction; - NetEqMainInst->DSPinst.VADInst.setmodeFunction = setmodeFunction; - NetEqMainInst->DSPinst.VADInst.VADFunction = VADFunction; - - /* Call init function and return the result (ok or fail) */ - return(WebRtcNetEQ_InitVAD(&NetEqMainInst->DSPinst.VADInst, NetEqMainInst->DSPinst.fs)); - -#else /* NETEQ_VAD not defined */ - return (-1); -#endif /* NETEQ_VAD */ - -} - -/**************************************************************************** - * WebRtcNetEQ_SetVADMode(...) - * - * Pass an aggressiveness mode parameter to the post-decode VAD instance. - * If this function is never called, mode 0 (quality mode) is used as default. - * - * Input: - * - inst : NetEQ instance - * - mode : mode parameter (same range as WebRtc VAD mode) - * - * Output: - * - inst : Updated NetEQ instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -int WebRtcNetEQ_SetVADMode(void *inst, WebRtc_Word16 mode) -{ - - /* Typecast to internal instance type */ - MainInst_t *NetEqMainInst = (MainInst_t*) inst; - if (NetEqMainInst == NULL) - { - return (-1); - } - -#ifdef NETEQ_VAD - - /* Set mode and return result */ - return(WebRtcNetEQ_SetVADModeInternal(&NetEqMainInst->DSPinst.VADInst, mode)); - -#else /* NETEQ_VAD not defined */ - return (-1); -#endif /* NETEQ_VAD */ - -} diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.cc b/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.cc deleted file mode 100644 index acde4b8bb..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.cc +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "NETEQTEST_CodecClass.h" -#include "webrtc_neteq_help_macros.h" - -NETEQTEST_Decoder::NETEQTEST_Decoder(enum WebRtcNetEQDecoder type, WebRtc_UWord16 fs, const char * name, WebRtc_UWord8 pt) -: -_decoder(NULL), -_decoderType(type), -_pt(pt), -_fs(fs), -_name(name) -{ -} - -int NETEQTEST_Decoder::loadToNetEQ(NETEQTEST_NetEQClass & neteq, WebRtcNetEQ_CodecDef & codecInst) -{ - SET_CODEC_PAR(codecInst, _decoderType, _pt, _decoder, _fs); - int err = neteq.loadCodec(codecInst); - - if (err) - { - printf("Error loading codec %s into NetEQ database\n", _name.c_str()); - } - - return(err); -} - - -// iSAC -#ifdef CODEC_ISAC -#include "isac.h" - -decoder_iSAC::decoder_iSAC(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderISAC, 16000, "iSAC", pt) -{ - WebRtc_Word16 err = WebRtcIsac_Create((ISACStruct **) &_decoder); - if (err) - { - throw std::exception(); - } - - WebRtcIsac_EncoderInit((ISACStruct *) _decoder, 0); - WebRtcIsac_SetDecSampRate((ISACStruct *) _decoder, kIsacWideband); -} - - -decoder_iSAC::~decoder_iSAC() -{ - if (_decoder) - { - WebRtcIsac_Free((ISACStruct *) _decoder); - _decoder = NULL; - } -} - - -int decoder_iSAC::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ISAC_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} -#endif - -#ifdef CODEC_ISAC_SWB -decoder_iSACSWB::decoder_iSACSWB(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderISACswb, 32000, "iSAC swb", pt) -{ - WebRtc_Word16 err = WebRtcIsac_Create((ISACStruct **) &_decoder); - if (err) - { - throw std::exception(); - } - - WebRtcIsac_EncoderInit((ISACStruct *) _decoder, 0); - WebRtcIsac_SetDecSampRate((ISACStruct *) _decoder, kIsacSuperWideband); -} - -decoder_iSACSWB::~decoder_iSACSWB() -{ - if (_decoder) - { - WebRtcIsac_Free((ISACStruct *) _decoder); - _decoder = NULL; - } -} - -int decoder_iSACSWB::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ISACSWB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} -#endif - -// PCM u/A -#ifdef CODEC_G711 -#include "g711_interface.h" - -decoder_PCMU::decoder_PCMU(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderPCMu, 8000, "G.711-u", pt) -{ - // no state to crate or init -} - -int decoder_PCMU::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCMU_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); - -} - -decoder_PCMA::decoder_PCMA(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderPCMa, 8000, "G.711-A", pt) -{ - // no state to crate or init -} - -int decoder_PCMA::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCMA_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -// Linear PCM16b -#if (defined(CODEC_PCM16B) || defined(CODEC_PCM16B_WB) || \ - defined(CODEC_PCM16B_32KHZ) || defined(CODEC_PCM16B_48KHZ)) -#include "pcm16b.h" -#endif - -#ifdef CODEC_PCM16B -int decoder_PCM16B_NB::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_WB -int decoder_PCM16B_WB::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_WB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_32KHZ -int decoder_PCM16B_SWB32::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_SWB32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_PCM16B_48KHZ -int decoder_PCM16B_SWB48::loadToNetEQ(NETEQTEST_NetEQClass &neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_PCM16B_SWB48_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_ILBC -#include "ilbc.h" -decoder_ILBC::decoder_ILBC(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderILBC, 8000, "iLBC", pt) -{ - WebRtc_Word16 err = WebRtcIlbcfix_DecoderCreate((iLBC_decinst_t **) &_decoder); - if (err) - { - throw std::exception(); - } -} - -decoder_ILBC::~decoder_ILBC() -{ - WebRtcIlbcfix_DecoderFree((iLBC_decinst_t *) _decoder); -} - -int decoder_ILBC::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_ILBC_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G729 -#include "G729Interface.h" -decoder_G729::decoder_G729(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG729, 8000, "G.729", pt) -{ - WebRtc_Word16 err = WebRtcG729_CreateDec((G729_decinst_t **) &_decoder); - if (err) - { - throw std::exception(); - } -} - -decoder_G729::~decoder_G729() -{ - WebRtcG729_FreeDec((G729_decinst_t *) _decoder); -} - -int decoder_G729::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G729_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G729_1 -#include "G729_1Interface.h" -decoder_G729_1::decoder_G729_1(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG729_1, 16000, "G.729.1", pt) -{ - WebRtc_Word16 err = WebRtcG7291_Create((G729_1_inst_t **) &_decoder); - if (err) - { - throw std::exception(); - } -} - -decoder_G729_1::~decoder_G729_1() -{ - WebRtcG7291_Free((G729_1_inst_t *) _decoder); -} - -int decoder_G729_1::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G729_1_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722 -#include "g722_interface.h" -decoder_G722::decoder_G722(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722, 16000, "G.722", pt) -{ - WebRtc_Word16 err = WebRtcG722_CreateDecoder((G722DecInst **) &_decoder); - if (err) - { - throw std::exception(); - } -} - -decoder_G722::~decoder_G722() -{ - WebRtcG722_FreeDecoder((G722DecInst *) _decoder); -} - -int decoder_G722::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_G722_1_16) || defined(CODEC_G722_1_24) || \ - defined(CODEC_G722_1_32) || defined(CODEC_G722_1C_24) || \ - defined(CODEC_G722_1C_32) || defined(CODEC_G722_1C_48)) -#include "G722_1Interface.h" -#endif - -#ifdef CODEC_G722_1_16 -decoder_G722_1_16::decoder_G722_1_16(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1_16, 16000, "G.722.1 (16 kbps)", pt) -{ - if (WebRtcG7221_CreateDec16((G722_1_16_decinst_t **) &_decoder)) - { - throw std::exception(); - } -} - -decoder_G722_1_16::~decoder_G722_1_16() -{ - WebRtcG7221_FreeDec16((G722_1_16_decinst_t *) _decoder); -} - -int decoder_G722_1_16::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_16_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1_24 -decoder_G722_1_24::decoder_G722_1_24(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1_24, 16000, "G.722.1 (24 kbps)", pt) -{ - if (WebRtcG7221_CreateDec24((G722_1_24_decinst_t **) &_decoder)) - { - throw std::exception(); - } -} - -decoder_G722_1_24::~decoder_G722_1_24() -{ - WebRtcG7221_FreeDec24((G722_1_24_decinst_t *) _decoder); -} - -int decoder_G722_1_24::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_24_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1_32 -decoder_G722_1_32::decoder_G722_1_32(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1_32, 16000, "G.722.1 (32 kbps)", pt) -{ - if (WebRtcG7221_CreateDec32((G722_1_32_decinst_t **) &_decoder)) - { - throw std::exception(); - } -} - -decoder_G722_1_32::~decoder_G722_1_32() -{ - WebRtcG7221_FreeDec32((G722_1_32_decinst_t *) _decoder); -} - -int decoder_G722_1_32::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1_32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_24 -decoder_G722_1C_24::decoder_G722_1C_24(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_24, 32000, "G.722.1C (24 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec24((G722_1C_24_decinst_t **) &_decoder)) - throw std::exception(); -} - -decoder_G722_1C_24::~decoder_G722_1C_24() -{ - WebRtcG7221C_FreeDec24((G722_1C_24_decinst_t *) _decoder); -} - -int decoder_G722_1C_24::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_24_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_32 -decoder_G722_1C_32::decoder_G722_1C_32(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_32, 32000, "G.722.1C (32 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec32((G722_1C_32_decinst_t **) &_decoder)) - throw std::exception(); -} - -decoder_G722_1C_32::~decoder_G722_1C_32() -{ - WebRtcG7221C_FreeDec32((G722_1C_32_decinst_t *) _decoder); -} - -int decoder_G722_1C_32::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_32_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_G722_1C_48 -decoder_G722_1C_48::decoder_G722_1C_48(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderG722_1C_48, 32000, "G.722.1C (48 kbps)", pt) -{ - if (WebRtcG7221C_CreateDec48((G722_1C_48_decinst_t **) &_decoder)) - throw std::exception(); -} - -decoder_G722_1C_48::~decoder_G722_1C_48() -{ - WebRtcG7221C_FreeDec48((G722_1C_48_decinst_t *) _decoder); -} - -int decoder_G722_1C_48::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_G722_1C_48_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_AMR -#include "AMRInterface.h" -#include "AMRCreation.h" -decoder_AMR::decoder_AMR(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderAMR, 8000, "AMR", pt) -{ - if (WebRtcAmr_CreateDec((AMR_decinst_t **) &_decoder)) - throw std::exception(); - - WebRtcAmr_DecodeBitmode((AMR_decinst_t *) _decoder, AMRBandwidthEfficient); -} - -decoder_AMR::~decoder_AMR() -{ - WebRtcAmr_FreeDec((AMR_decinst_t *) _decoder); -} - -int decoder_AMR::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AMR_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_AMRWB -#include "AMRWBInterface.h" -#include "AMRWBCreation.h" -decoder_AMRWB::decoder_AMRWB(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderAMRWB, 16000, "AMR wb", pt) -{ - if (WebRtcAmrWb_CreateDec((AMRWB_decinst_t **) &_decoder)) - throw std::exception(); - - WebRtcAmrWb_DecodeBitmode((AMRWB_decinst_t *) _decoder, AMRBandwidthEfficient); -} - -decoder_AMRWB::~decoder_AMRWB() -{ - WebRtcAmrWb_FreeDec((AMRWB_decinst_t *) _decoder); -} - -int decoder_AMRWB::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AMRWB_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_GSMFR -#include "GSMFRInterface.h" -#include "GSMFRCreation.h" -decoder_GSMFR::decoder_GSMFR(WebRtc_UWord8 pt) -: -NETEQTEST_Decoder(kDecoderGSMFR, 8000, "GSM-FR", pt) -{ - if (WebRtcGSMFR_CreateDec((GSMFR_decinst_t **) &_decoder)) - throw std::exception(); -} - -decoder_GSMFR::~decoder_GSMFR() -{ - WebRtcGSMFR_FreeDec((GSMFR_decinst_t *) _decoder); -} - -int decoder_GSMFR::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_GSMFR_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_SPEEX_8) || defined (CODEC_SPEEX_16)) -#include "SpeexInterface.h" -decoder_SPEEX::decoder_SPEEX(WebRtc_UWord8 pt, WebRtc_UWord16 fs) -: -NETEQTEST_Decoder(fs == 8000 ? kDecoderSPEEX_8 : kDecoderSPEEX_16, - fs, "SPEEX " + fs/1000, pt) -{ - if (fs != 8000 && fs != 16000) - throw std::exception("Wrong sample rate for SPEEX"); - - if (WebRtcSpeex_CreateDec((SPEEX_decinst_t **) &_decoder, fs, 1)) - throw std::exception(); -} - -decoder_SPEEX::~decoder_SPEEX() -{ - WebRtcSpeex_FreeDec((SPEEX_decinst_t *) _decoder); -} - -int decoder_SPEEX::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_SPEEX_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_RED -int decoder_RED::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_RED_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#ifdef CODEC_ATEVENT_DECODE -int decoder_AVT::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_AVT_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) -#include "webrtc_cng.h" -decoder_CNG::decoder_CNG(WebRtc_UWord8 pt, WebRtc_UWord16 fs) -: -NETEQTEST_Decoder(kDecoderCNG, fs, "CNG " + fs/1000, pt) -{ - if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) - throw std::exception(); - - if (WebRtcCng_CreateDec((CNG_dec_inst **) &_decoder)) - throw std::exception(); -} - -decoder_CNG::~decoder_CNG() -{ - WebRtcCng_FreeDec((CNG_dec_inst *) _decoder); -} - -int decoder_CNG::loadToNetEQ(NETEQTEST_NetEQClass & neteq) -{ - WebRtcNetEQ_CodecDef codecInst; - - SET_CNG_FUNCTIONS(codecInst); - - return(NETEQTEST_Decoder::loadToNetEQ(neteq, codecInst)); -} -#endif diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.h b/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.h deleted file mode 100644 index 61896f4a0..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_CODECCLASS_H -#define NETEQTEST_CODECCLASS_H - -#include -#include - -#include "typedefs.h" -#include "webrtc_neteq.h" -#include "NETEQTEST_NetEQClass.h" - -class NETEQTEST_Decoder -{ -public: - NETEQTEST_Decoder(enum WebRtcNetEQDecoder type, WebRtc_UWord16 fs, const char * name, WebRtc_UWord8 pt = 0); - virtual ~NETEQTEST_Decoder() {}; - - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) = 0; - - int getName(char * name, int maxLen) const { strncpy( name, _name.c_str(), maxLen ); return 0;}; - - void setPT(WebRtc_UWord8 pt) { _pt = pt; }; - WebRtc_UWord16 getFs() const { return (_fs); }; - enum WebRtcNetEQDecoder getType() const { return (_decoderType); }; - WebRtc_UWord8 getPT() const { return (_pt); }; - -protected: - int loadToNetEQ(NETEQTEST_NetEQClass & neteq, WebRtcNetEQ_CodecDef & codecInst); - - void * _decoder; - enum WebRtcNetEQDecoder _decoderType; - WebRtc_UWord8 _pt; - WebRtc_UWord16 _fs; - std::string _name; - -private: -}; - - -class decoder_iSAC : public NETEQTEST_Decoder -{ -public: - decoder_iSAC(WebRtc_UWord8 pt = 0); - virtual ~decoder_iSAC(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_iSACSWB : public NETEQTEST_Decoder -{ -public: - decoder_iSACSWB(WebRtc_UWord8 pt = 0); - virtual ~decoder_iSACSWB(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_PCMU : public NETEQTEST_Decoder -{ -public: - decoder_PCMU(WebRtc_UWord8 pt = 0); - virtual ~decoder_PCMU() {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_PCMA : public NETEQTEST_Decoder -{ -public: - decoder_PCMA(WebRtc_UWord8 pt = 0); - virtual ~decoder_PCMA() {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_PCM16B_NB : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_NB(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderPCM16B, 8000, "PCM16 nb", pt) {}; - virtual ~decoder_PCM16B_NB() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; -class decoder_PCM16B_WB : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_WB(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bwb, 16000, "PCM16 wb", pt) {}; - virtual ~decoder_PCM16B_WB() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; -class decoder_PCM16B_SWB32 : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_SWB32(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bswb32kHz, 32000, "PCM16 swb32", pt) {}; - virtual ~decoder_PCM16B_SWB32() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; -class decoder_PCM16B_SWB48 : public NETEQTEST_Decoder -{ -public: - decoder_PCM16B_SWB48(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderPCM16Bswb48kHz, 48000, "PCM16 swb48", pt) {}; - virtual ~decoder_PCM16B_SWB48() {}; - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_ILBC : public NETEQTEST_Decoder -{ -public: - decoder_ILBC(WebRtc_UWord8 pt = 0); - virtual ~decoder_ILBC(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_G729 : public NETEQTEST_Decoder -{ -public: - decoder_G729(WebRtc_UWord8 pt = 0); - virtual ~decoder_G729(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G729_1 : public NETEQTEST_Decoder -{ -public: - decoder_G729_1(WebRtc_UWord8 pt = 0); - virtual ~decoder_G729_1(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_G722 : public NETEQTEST_Decoder -{ -public: - decoder_G722(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_G722_1_16 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_16(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1_16(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G722_1_24 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_24(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1_24(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G722_1_32 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1_32(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1_32(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_G722_1C_24 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_24(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1C_24(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G722_1C_32 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_32(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1C_32(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G722_1C_48 : public NETEQTEST_Decoder -{ -public: - decoder_G722_1C_48(WebRtc_UWord8 pt = 0); - virtual ~decoder_G722_1C_48(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_AMR : public NETEQTEST_Decoder -{ -public: - decoder_AMR(WebRtc_UWord8 pt = 0); - virtual ~decoder_AMR(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_AMRWB : public NETEQTEST_Decoder -{ -public: - decoder_AMRWB(WebRtc_UWord8 pt = 0); - virtual ~decoder_AMRWB(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_GSMFR : public NETEQTEST_Decoder -{ -public: - decoder_GSMFR(WebRtc_UWord8 pt = 0); - virtual ~decoder_GSMFR(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G726 : public NETEQTEST_Decoder -{ -public: - //virtual decoder_G726(WebRtc_UWord8 pt = 0) = 0; - decoder_G726(enum WebRtcNetEQDecoder type, const char * name, WebRtc_UWord8 pt = 0); - virtual ~decoder_G726(); - virtual int loadToNetEQ(NETEQTEST_NetEQClass & neteq) = 0; -}; - -class decoder_G726_16 : public decoder_G726 -{ -public: - decoder_G726_16(WebRtc_UWord8 pt = 0) : decoder_G726(kDecoderG726_16, "G.726 (16 kbps)", pt) {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G726_24 : public decoder_G726 -{ -public: - decoder_G726_24(WebRtc_UWord8 pt = 0) : decoder_G726(kDecoderG726_24, "G.726 (24 kbps)", pt) {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G726_32 : public decoder_G726 -{ -public: - decoder_G726_32(WebRtc_UWord8 pt = 0) : decoder_G726(kDecoderG726_32, "G.726 (32 kbps)", pt) {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_G726_40 : public decoder_G726 -{ -public: - decoder_G726_40(WebRtc_UWord8 pt = 0) : decoder_G726(kDecoderG726_40, "G.726 (40 kbps)", pt) {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_SPEEX : public NETEQTEST_Decoder -{ -public: - decoder_SPEEX(WebRtc_UWord8 pt = 0, WebRtc_UWord16 fs = 8000); - virtual ~decoder_SPEEX(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_RED : public NETEQTEST_Decoder -{ -public: - decoder_RED(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderRED, 8000, "RED", pt) {}; - virtual ~decoder_RED() {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -class decoder_AVT : public NETEQTEST_Decoder -{ -public: - decoder_AVT(WebRtc_UWord8 pt = 0) : NETEQTEST_Decoder(kDecoderAVT, 8000, "AVT", pt) {}; - virtual ~decoder_AVT() {}; - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - - -class decoder_CNG : public NETEQTEST_Decoder -{ -public: - decoder_CNG(WebRtc_UWord8 pt = 0, WebRtc_UWord16 fs = 8000); - virtual ~decoder_CNG(); - int loadToNetEQ(NETEQTEST_NetEQClass & neteq); -}; - -#endif //NETEQTEST_CODECCLASS_H diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.cc b/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.cc deleted file mode 100644 index e85819331..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.cc +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "NETEQTEST_NetEQClass.h" - - -NETEQTEST_NetEQClass::NETEQTEST_NetEQClass() - : - _inst(NULL), - _instMem(NULL), - _bufferMem(NULL), - _preparseRTP(false), - _fsmult(1), - _isMaster(true) -{ -#ifdef WINDOWS_TIMING - _totTimeRecIn.QuadPart = 0; - _totTimeRecOut.QuadPart = 0; -#endif -} - -NETEQTEST_NetEQClass::NETEQTEST_NetEQClass(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, - WebRtc_UWord16 fs, WebRtcNetEQNetworkType nwType) - : - _inst(NULL), - _instMem(NULL), - _bufferMem(NULL), - _preparseRTP(false), - _fsmult(1), - _isMaster(true) -{ -#ifdef WINDOWS_TIMING - _totTimeRecIn.QuadPart = 0; - _totTimeRecOut.QuadPart = 0; -#endif - - if (assign() == 0) - { - if (init(fs) == 0) - { - assignBuffer(usedCodec, noOfCodecs, nwType); - } - } -} - - -NETEQTEST_NetEQClass::~NETEQTEST_NetEQClass() -{ - if (_instMem) - { - delete [] _instMem; - _instMem = NULL; - } - - if (_bufferMem) - { - delete [] _bufferMem; - _bufferMem = NULL; - } - - _inst = NULL; -} - -int NETEQTEST_NetEQClass::assign() -{ - int memSize; - - WebRtcNetEQ_AssignSize(&memSize); - - if (_instMem) - { - delete [] _instMem; - _instMem = NULL; - } - - _instMem = new WebRtc_Word8[memSize]; - - int ret = WebRtcNetEQ_Assign(&_inst, _instMem); - - if (ret) - { - printError(); - } - - return (ret); -} - - -int NETEQTEST_NetEQClass::init(WebRtc_UWord16 fs) -{ - int ret; - - if (!_inst) - { - // not assigned - ret = assign(); - - if (ret != 0) - { - printError(); - return (ret); - } - } - - ret = WebRtcNetEQ_Init(_inst, fs); - - if (ret != 0) - { - printError(); - } - - return (ret); - -} - - -int NETEQTEST_NetEQClass::assignBuffer(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, WebRtcNetEQNetworkType nwType) -{ - int numPackets, memSize, ret; - - if (!_inst) - { - // not assigned - ret = assign(); - - if (ret != 0) - { - printError(); - return (ret); - } - - ret = init(); - - if (ret != 0) - { - printError(); - return (ret); - } - } - - ret = WebRtcNetEQ_GetRecommendedBufferSize(_inst, usedCodec, noOfCodecs, nwType, &numPackets, &memSize); - - if (ret != 0) - { - printError(); - return (ret); - } - - if (_bufferMem) - { - delete [] _bufferMem; - _bufferMem = NULL; - } - - _bufferMem = new WebRtc_Word8[memSize]; - - memset(_bufferMem, -1, memSize); - - ret = WebRtcNetEQ_AssignBuffer(_inst, numPackets, _bufferMem, memSize); - - if (ret != 0) - { - printError(); - } - - return (ret); -} - -int NETEQTEST_NetEQClass::loadCodec(WebRtcNetEQ_CodecDef &codecInst) -{ - int err = WebRtcNetEQ_CodecDbAdd(_inst, &codecInst); - - if (err) - { - printError(); - } - - return (err); -} - -void NETEQTEST_NetEQClass::printError() -{ - if (_inst) - { - int errorCode = WebRtcNetEQ_GetErrorCode(_inst); - - if (errorCode) - { - char errorName[WEBRTC_NETEQ_MAX_ERROR_NAME]; - - WebRtcNetEQ_GetErrorName(errorCode, errorName, WEBRTC_NETEQ_MAX_ERROR_NAME); - - printf("Error %i: %s\n", errorCode, errorName); - } - } -} - -void NETEQTEST_NetEQClass::printError(NETEQTEST_RTPpacket &rtp) -{ - // print regular error info - printError(); - - // print extra info from packet - printf("\tRTP: TS=%lu, SN=%u, PT=%u, M=%i, len=%i\n", - rtp.timeStamp(), rtp.sequenceNumber(), rtp.payloadType(), - rtp.markerBit(), rtp.payloadLen()); - -} - -int NETEQTEST_NetEQClass::recIn(NETEQTEST_RTPpacket &rtp) -{ - - int err; -#ifdef WINDOWS_TIMING - LARGE_INTEGER countA, countB; -#endif - - if (_preparseRTP) - { - WebRtcNetEQ_RTPInfo rtpInfo; - // parse RTP header - rtp.parseHeader(rtpInfo); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - err = WebRtcNetEQ_RecInRTPStruct(_inst, &rtpInfo, rtp.payload(), rtp.payloadLen(), rtp.time() * _fsmult * 8); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecIn.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - } - else - { - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - err = WebRtcNetEQ_RecIn(_inst, (WebRtc_Word16 *) rtp.datagram(), rtp.dataLen(), rtp.time() * _fsmult * 8); - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecIn.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - } - - if (err) - { - printError(rtp); - } - - return (err); - -} - - -WebRtc_Word16 NETEQTEST_NetEQClass::recOut(WebRtc_Word16 *outData, void *msInfo, enum WebRtcNetEQOutputType *outputType) -{ - int err; - WebRtc_Word16 outLen = 0; -#ifdef WINDOWS_TIMING - LARGE_INTEGER countA, countB; -#endif - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countA); // get start count for processor -#endif - - if (!msInfo) - { - // no msInfo given, do mono mode - err = WebRtcNetEQ_RecOut(_inst, outData, &outLen); - } - else - { - // master/slave mode - err = WebRtcNetEQ_RecOutMasterSlave(_inst, outData, &outLen, msInfo, static_cast(_isMaster)); - } - -#ifdef WINDOWS_TIMING - QueryPerformanceCounter(&countB); // get stop count for processor - _totTimeRecOut.QuadPart += (countB.QuadPart - countA.QuadPart); -#endif - - if (err) - { - printError(); - } - else - { - int newfsmult = static_cast(outLen / 80); - - if (newfsmult != _fsmult) - { - printf("Warning: output sample rate changed\n"); - _fsmult = newfsmult; - } - } - - if (outputType != NULL) - { - err = WebRtcNetEQ_GetSpeechOutputType(_inst, outputType); - - if (err) - { - printError(); - } - } - - return (outLen); -} - - -WebRtc_UWord32 NETEQTEST_NetEQClass::getSpeechTimeStamp() -{ - - WebRtc_UWord32 ts = 0; - int err; - - err = WebRtcNetEQ_GetSpeechTimeStamp(_inst, &ts); - - if (err) - { - printError(); - ts = 0; - } - - return (ts); - -} - -//NETEQTEST_NetEQVector::NETEQTEST_NetEQVector(int numChannels) -//: -//channels(numChannels, new NETEQTEST_NetEQClass()) -//{ -// //for (int i = 0; i < numChannels; i++) -// //{ -// // channels.push_back(new NETEQTEST_NetEQClass()); -// //} -//} -// -//NETEQTEST_NetEQVector::NETEQTEST_NetEQVector(int numChannels, enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, -// WebRtc_UWord16 fs, WebRtcNetEQNetworkType nwType) -// : -//channels(numChannels, new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, fs, nwType)) -//{ -// //for (int i = 0; i < numChannels; i++) -// //{ -// // channels.push_back(new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, fs, nwType)); -// //} -//} -// -//NETEQTEST_NetEQVector::~NETEQTEST_NetEQVector() -//{ -//} - diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.h b/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.h deleted file mode 100644 index cae332cd2..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_NETEQCLASS_H -#define NETEQTEST_NETEQCLASS_H - -#include -#include - -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" - -#include "NETEQTEST_RTPpacket.h" - -#ifdef WIN32 -#define WINDOWS_TIMING // complexity measurement only implemented for windows -//TODO(hlundin):Add complexity testing for Linux. -#include -#endif - -class NETEQTEST_NetEQClass -{ -public: - NETEQTEST_NetEQClass(); - NETEQTEST_NetEQClass(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, - WebRtc_UWord16 fs = 8000, WebRtcNetEQNetworkType nwType = kTCPLargeJitter); - ~NETEQTEST_NetEQClass(); - - int assign(); - int init(WebRtc_UWord16 fs = 8000); - int assignBuffer(enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, WebRtcNetEQNetworkType nwType = kTCPLargeJitter); - int loadCodec(WebRtcNetEQ_CodecDef & codecInst); - int recIn(NETEQTEST_RTPpacket & rtp); - WebRtc_Word16 recOut(WebRtc_Word16 *outData, void *msInfo = NULL, enum WebRtcNetEQOutputType *outputType = NULL); - WebRtc_UWord32 getSpeechTimeStamp(); - - void * instance() { return (_inst); }; - void usePreparseRTP( bool useIt = true ) { _preparseRTP = useIt; }; - bool usingPreparseRTP() { return (_preparseRTP); }; - void setMaster( bool isMaster = true ) { _isMaster = isMaster; }; - void setSlave() { _isMaster = false; }; - bool isMaster() { return (_isMaster); }; - bool isSlave() { return (!_isMaster); }; - -#ifdef WINDOWS_TIMING - double getRecInTime() { return (static_cast( _totTimeRecIn.QuadPart )); }; - double getRecOutTime() { return (static_cast( _totTimeRecOut.QuadPart )); }; -#else - double getRecInTime() { return (0.0); }; - double getRecOutTime() { return (0.0); }; - -#endif - - void printError(); - void printError(NETEQTEST_RTPpacket & rtp); - -private: - void * _inst; - WebRtc_Word8 * _instMem; - WebRtc_Word8 * _bufferMem; - bool _preparseRTP; - int _fsmult; - bool _isMaster; -#ifdef WINDOWS_TIMING - LARGE_INTEGER _totTimeRecIn; - LARGE_INTEGER _totTimeRecOut; -#endif -}; - - - -//class NETEQTEST_NetEQVector -//{ -//public: -// NETEQTEST_NetEQVector(int numChannels); -// NETEQTEST_NetEQVector(int numChannels, enum WebRtcNetEQDecoder *usedCodec, int noOfCodecs, -// WebRtc_UWord16 fs = 8000, WebRtcNetEQNetworkType nwType = kTCPLargeJitter); -// ~NETEQTEST_NetEQVector(); -// -//private: -// std::vector channels; -//}; - -#endif //NETEQTEST_NETEQCLASS_H diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.cc b/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.cc deleted file mode 100644 index 6d57f3f7c..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.cc +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "NETEQTEST_RTPpacket.h" - -#include - -#ifdef WIN32 -#include -#else -#include // for htons, htonl, etc -#endif - -#define HDR_SIZE 8 // rtpplay packet header size in bytes - - -NETEQTEST_RTPpacket::NETEQTEST_RTPpacket() -: -_datagram(NULL), -_payloadPtr(NULL), -_memSize(0), -_datagramLen(-1), -_payloadLen(0), -_rtpParsed(false), -_receiveTime(0), -_lost(false) -{ - memset(&_rtpInfo, 0, sizeof(_rtpInfo)); - _blockList.clear(); -} - -NETEQTEST_RTPpacket::NETEQTEST_RTPpacket(const NETEQTEST_RTPpacket& copyFromMe) -{ - - memcpy(this, ©FromMe, sizeof(NETEQTEST_RTPpacket)); - - _datagram = NULL; - _payloadPtr = NULL; - - if(copyFromMe._datagram) - { - _datagram = new WebRtc_UWord8[_memSize]; - - if(_datagram) - { - memcpy(_datagram, copyFromMe._datagram, _memSize); - } - } - - if(copyFromMe._payloadPtr) - { - _payloadPtr = _datagram + (copyFromMe._payloadPtr - copyFromMe._datagram); - } - - _blockList = copyFromMe._blockList; - -} - - -NETEQTEST_RTPpacket & NETEQTEST_RTPpacket::operator = (const NETEQTEST_RTPpacket & other) -{ - if (this != &other) // protect against invalid self-assignment - { - - // deallocate datagram memory if allocated - if(_datagram) - { - delete[] _datagram; - } - - // do shallow copy - memcpy(this, &other, sizeof(NETEQTEST_RTPpacket)); - - // reset pointers - _datagram = NULL; - _payloadPtr = NULL; - - if(other._datagram) - { - _datagram = new WebRtc_UWord8[other._memSize]; - _memSize = other._memSize; - - if(_datagram) - { - memcpy(_datagram, other._datagram, _memSize); - } - } - - if(other._payloadPtr) - { - _payloadPtr = _datagram + (other._payloadPtr - other._datagram); - } - - // copy the blocking list (map) - _blockList = other._blockList; - - } - - // by convention, always return *this - return *this; -} - - - -NETEQTEST_RTPpacket::~NETEQTEST_RTPpacket() -{ - if(_datagram) - { - delete _datagram; - } -} - - -void NETEQTEST_RTPpacket::reset() -{ - if(_datagram) { - delete _datagram; - } - _datagram = NULL; - _memSize = 0; - _datagramLen = -1; - _payloadLen = 0; - _payloadPtr = NULL; - _receiveTime = 0; - memset(&_rtpInfo, 0, sizeof(_rtpInfo)); - _rtpParsed = false; - -} - - -int NETEQTEST_RTPpacket::readFromFile(FILE *fp) -{ - if(!fp) - { - return(-1); - } - - WebRtc_UWord16 length, plen; - WebRtc_UWord32 offset; - - if (fread(&length,2,1,fp)==0) - { - reset(); - return(-2); - } - length = ntohs(length); - - if (fread(&plen,2,1,fp)==0) - { - reset(); - return(-1); - } - int packetLen = ntohs(plen); - - if (fread(&offset,4,1,fp)==0) - { - reset(); - return(-1); - } - WebRtc_UWord32 receiveTime = ntohl(offset); // store in local variable until we have passed the reset below - - // Use length here because a plen of 0 specifies rtcp - length = (WebRtc_UWord16) (length - HDR_SIZE); - - // check buffer size - if (_datagram && _memSize < length) - { - reset(); - } - - if (!_datagram) - { - _datagram = new WebRtc_UWord8[length]; - _memSize = length; - } - - if (fread((unsigned short *) _datagram,1,length,fp) != length) - { - reset(); - return(-1); - } - - _datagramLen = length; - _receiveTime = receiveTime; - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - // discard this payload - return(readFromFile(fp)); - } - - return(packetLen); - -} - - -int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, int length) -{ - if(!fp) - { - return(-1); - } - - // check buffer size - if (_datagram && _memSize < length) - { - reset(); - } - - if (!_datagram) - { - _datagram = new WebRtc_UWord8[length]; - _memSize = length; - } - - if (fread((unsigned short *) _datagram,1,length,fp) != length) - { - reset(); - return(-1); - } - - _datagramLen = length; - _receiveTime = 0; - - if (!_blockList.empty() && _blockList.count(payloadType()) > 0) - { - // discard this payload - return(readFromFile(fp)); - } - - return(length); - -} - - -int NETEQTEST_RTPpacket::writeToFile(FILE *fp) -{ - if(!fp) - { - return(-1); - } - - WebRtc_UWord16 length, plen; - WebRtc_UWord32 offset; - - // length including RTPplay header - length = htons(_datagramLen + HDR_SIZE); - if (fwrite(&length, 2, 1, fp) != 1) - { - return(-1); - } - - // payload length - plen = htons(_datagramLen); - if (fwrite(&plen, 2, 1, fp) != 1) - { - return(-1); - } - - // offset (=receive time) - offset = htonl(_receiveTime); - if (fwrite(&offset, 4, 1, fp) != 1) - { - return(-1); - } - - - // write packet data - if (fwrite((unsigned short *) _datagram, 1, _datagramLen, fp) != _datagramLen) - { - return(-1); - } - - return(_datagramLen + HDR_SIZE); // total number of bytes written - -} - - -void NETEQTEST_RTPpacket::blockPT(WebRtc_UWord8 pt) -{ - _blockList[pt] = true; -} - - -void NETEQTEST_RTPpacket::parseHeader() -{ - if (_rtpParsed) - { - // nothing to do - return; - } - - if (_datagramLen < 12) - { - // corrupt packet? - return; - } - - _payloadLen = parseRTPheader(_datagram, _datagramLen, &_rtpInfo, &_payloadPtr); - - _rtpParsed = true; - - return; - -} - -void NETEQTEST_RTPpacket::parseHeader(WebRtcNetEQ_RTPInfo & rtpInfo) -{ - if (!_rtpParsed) - { - // parse the header - parseHeader(); - } - - memcpy(&rtpInfo, &_rtpInfo, sizeof(WebRtcNetEQ_RTPInfo)); -} - -WebRtcNetEQ_RTPInfo const * NETEQTEST_RTPpacket::RTPinfo() const -{ - if (_rtpParsed) - { - return &_rtpInfo; - } - else - { - return NULL; - } -} - -WebRtc_UWord8 * NETEQTEST_RTPpacket::datagram() const -{ - if (_datagramLen > 0) - { - return _datagram; - } - else - { - return NULL; - } -} - -WebRtc_UWord8 * NETEQTEST_RTPpacket::payload() const -{ - if (_payloadLen > 0) - { - return _payloadPtr; - } - else - { - return NULL; - } -} - -WebRtc_Word16 NETEQTEST_RTPpacket::payloadLen() const -{ - return _payloadLen; -} - -WebRtc_Word16 NETEQTEST_RTPpacket::dataLen() const -{ - return _datagramLen; -} - -bool NETEQTEST_RTPpacket::isParsed() const -{ - return _rtpParsed; -} - -bool NETEQTEST_RTPpacket::isLost() const -{ - return _lost; -} - -WebRtc_UWord8 NETEQTEST_RTPpacket::payloadType() const -{ - WebRtcNetEQ_RTPInfo tempRTPinfo; - - if(_datagram) - { - parseRTPheader(_datagram, _datagramLen, &tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.payloadType; -} - -WebRtc_UWord16 NETEQTEST_RTPpacket::sequenceNumber() const -{ - WebRtcNetEQ_RTPInfo tempRTPinfo; - - if(_datagram) - { - parseRTPheader(_datagram, _datagramLen, &tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.sequenceNumber; -} - -WebRtc_UWord32 NETEQTEST_RTPpacket::timeStamp() const -{ - WebRtcNetEQ_RTPInfo tempRTPinfo; - - if(_datagram) - { - parseRTPheader(_datagram, _datagramLen, &tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.timeStamp; -} - -WebRtc_UWord32 NETEQTEST_RTPpacket::SSRC() const -{ - WebRtcNetEQ_RTPInfo tempRTPinfo; - - if(_datagram) - { - parseRTPheader(_datagram, _datagramLen, &tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.SSRC; -} - -WebRtc_UWord8 NETEQTEST_RTPpacket::markerBit() const -{ - WebRtcNetEQ_RTPInfo tempRTPinfo; - - if(_datagram) - { - parseRTPheader(_datagram, _datagramLen, &tempRTPinfo); - } - else - { - return 0; - } - - return tempRTPinfo.markerBit; -} - - - -int NETEQTEST_RTPpacket::setPayloadType(WebRtc_UWord8 pt) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.payloadType = pt; - } - - _datagram[1]=(unsigned char)(pt & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setSequenceNumber(WebRtc_UWord16 sn) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.sequenceNumber = sn; - } - - _datagram[2]=(unsigned char)((sn>>8)&0xFF); - _datagram[3]=(unsigned char)((sn)&0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setTimeStamp(WebRtc_UWord32 ts) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.timeStamp = ts; - } - - _datagram[4]=(unsigned char)((ts>>24)&0xFF); - _datagram[5]=(unsigned char)((ts>>16)&0xFF); - _datagram[6]=(unsigned char)((ts>>8)&0xFF); - _datagram[7]=(unsigned char)(ts & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setSSRC(WebRtc_UWord32 ssrc) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (!_rtpParsed) - { - _rtpInfo.SSRC = ssrc; - } - - _datagram[8]=(unsigned char)((ssrc>>24)&0xFF); - _datagram[9]=(unsigned char)((ssrc>>16)&0xFF); - _datagram[10]=(unsigned char)((ssrc>>8)&0xFF); - _datagram[11]=(unsigned char)(ssrc & 0xFF); - - return 0; - -} - -int NETEQTEST_RTPpacket::setMarkerBit(WebRtc_UWord8 mb) -{ - - if (_datagramLen < 12) - { - return -1; - } - - if (_rtpParsed) - { - _rtpInfo.markerBit = mb; - } - - if (mb) - { - _datagram[0] |= 0x01; - } - else - { - _datagram[0] &= 0xFE; - } - - return 0; - -} - -int NETEQTEST_RTPpacket::setRTPheader(const WebRtcNetEQ_RTPInfo *RTPinfo) -{ - if (_datagramLen < 12) - { - // this packet is not ok - return -1; - } - - makeRTPheader(_datagram, - RTPinfo->payloadType, - RTPinfo->sequenceNumber, - RTPinfo->timeStamp, - RTPinfo->SSRC, - RTPinfo->markerBit); - - return 0; -} - - -int NETEQTEST_RTPpacket::splitStereo(NETEQTEST_RTPpacket& slaveRtp, enum stereoModes mode) -{ - // if mono, do nothing - if (mode == stereoModeMono) - { - return 0; - } - - // check that the RTP header info is parsed - parseHeader(); - - // start by copying the main rtp packet - slaveRtp = *this; - - if(_payloadLen == 0) - { - // do no more - return 0; - } - - if(_payloadLen%2 != 0) - { - // length must be a factor of 2 - return -1; - } - - switch(mode) - { - case stereoModeSample1: - { - // sample based codec with 1-byte samples - splitStereoSample(slaveRtp, 1 /* 1 byte/sample */); - break; - } - case stereoModeSample2: - { - // sample based codec with 2-byte samples - splitStereoSample(slaveRtp, 2 /* 2 bytes/sample */); - break; - } - case stereoModeFrame: - { - // frame based codec - splitStereoFrame(slaveRtp); - break; - } - } - - return 0; -} - - -void NETEQTEST_RTPpacket::makeRTPheader(unsigned char* rtp_data, WebRtc_UWord8 payloadType, WebRtc_UWord16 seqNo, WebRtc_UWord32 timestamp, WebRtc_UWord32 ssrc, WebRtc_UWord8 markerBit) const -{ - rtp_data[0]=(unsigned char)0x80; - if (markerBit) - { - rtp_data[0] |= 0x01; - } - else - { - rtp_data[0] &= 0xFE; - } - rtp_data[1]=(unsigned char)(payloadType & 0xFF); - rtp_data[2]=(unsigned char)((seqNo>>8)&0xFF); - rtp_data[3]=(unsigned char)((seqNo)&0xFF); - rtp_data[4]=(unsigned char)((timestamp>>24)&0xFF); - rtp_data[5]=(unsigned char)((timestamp>>16)&0xFF); - - rtp_data[6]=(unsigned char)((timestamp>>8)&0xFF); - rtp_data[7]=(unsigned char)(timestamp & 0xFF); - - rtp_data[8]=(unsigned char)((ssrc>>24)&0xFF); - rtp_data[9]=(unsigned char)((ssrc>>16)&0xFF); - - rtp_data[10]=(unsigned char)((ssrc>>8)&0xFF); - rtp_data[11]=(unsigned char)(ssrc & 0xFF); -} - - -WebRtc_UWord16 NETEQTEST_RTPpacket::parseRTPheader(const WebRtc_UWord8 *datagram, int datagramLen, WebRtcNetEQ_RTPInfo *RTPinfo, WebRtc_UWord8 **payloadPtr) const -{ - WebRtc_Word16 *rtp_data = (WebRtc_Word16 *) datagram; - int i_P, i_X, i_CC, i_extlength=-1, i_padlength=0, i_startPosition; - - i_P=(((WebRtc_UWord16)(rtp_data[0] & 0x20))>>5); /* Extract the P bit */ - i_X=(((WebRtc_UWord16)(rtp_data[0] & 0x10))>>4); /* Extract the X bit */ - i_CC=(WebRtc_UWord16)(rtp_data[0] & 0xF); /* Get the CC number */ - RTPinfo->markerBit = (WebRtc_UWord8) ((rtp_data[0] >> 15) & 0x01); /* Get the marker bit */ - RTPinfo->payloadType = (WebRtc_UWord8) ((rtp_data[0] >> 8) & 0x7F); /* Get the coder type */ - RTPinfo->sequenceNumber = ((( ((WebRtc_UWord16)rtp_data[1]) >> 8) & 0xFF) | - ( ((WebRtc_UWord16)(rtp_data[1] & 0xFF)) << 8)); /* Get the packet number */ - RTPinfo->timeStamp = ((((WebRtc_UWord16)rtp_data[2]) & 0xFF) << 24) | - ((((WebRtc_UWord16)rtp_data[2]) & 0xFF00) << 8) | - ((((WebRtc_UWord16)rtp_data[3]) >> 8) & 0xFF) | - ((((WebRtc_UWord16)rtp_data[3]) & 0xFF) << 8); /* Get timestamp */ - RTPinfo->SSRC=((((WebRtc_UWord16)rtp_data[4]) & 0xFF) << 24) | - ((((WebRtc_UWord16)rtp_data[4]) & 0xFF00) << 8) | - ((((WebRtc_UWord16)rtp_data[5]) >> 8) & 0xFF) | - ((((WebRtc_UWord16)rtp_data[5]) & 0xFF) << 8); /* Get the SSRC */ - - if (i_X==1) { - /* Extention header exists. Find out how many WebRtc_Word32 it consists of */ - i_extlength=((( ((WebRtc_UWord16)rtp_data[7+2*i_CC]) >> 8) & 0xFF) | - ( ((WebRtc_UWord16)(rtp_data[7+2*i_CC]&0xFF)) << 8)); - } - if (i_P==1) { - /* Padding exists. Find out how many bytes the padding consists of */ - if (datagramLen & 0x1) { - /* odd number of bytes => last byte in higher byte */ - i_padlength=(rtp_data[datagramLen>>1] & 0xFF); - } else { - /* even number of bytes => last byte in lower byte */ - i_padlength=(((WebRtc_UWord16)rtp_data[(datagramLen>>1)-1]) >> 8); - } - } - - i_startPosition=12+4*(i_extlength+1)+4*i_CC; - - if (payloadPtr) { - *payloadPtr = (WebRtc_UWord8*) &rtp_data[i_startPosition>>1]; - } - - return (WebRtc_UWord16) (datagramLen-i_startPosition-i_padlength); -} - -//void NETEQTEST_RTPpacket::splitStereoSample(WebRtc_UWord8 *data, WebRtc_UWord16 *lenBytes, WebRtc_UWord8 *slaveData, WebRtc_UWord16 *slaveLenBytes, int stride) -void NETEQTEST_RTPpacket::splitStereoSample(NETEQTEST_RTPpacket& slaveRtp, int stride) -{ - if(!_payloadPtr || !slaveRtp._payloadPtr - || _payloadLen <= 0 || slaveRtp._memSize < _memSize) - { - return; - } - - WebRtc_UWord8 *readDataPtr = _payloadPtr; - WebRtc_UWord8 *writeDataPtr = _payloadPtr; - WebRtc_UWord8 *slaveData = slaveRtp._payloadPtr; - - while (readDataPtr - _payloadPtr < _payloadLen) - { - // master data - for (int ix = 0; ix < stride; ix++) { - *writeDataPtr = *readDataPtr; - writeDataPtr++; - readDataPtr++; - } - - // slave data - for (int ix = 0; ix < stride; ix++) { - *slaveData = *readDataPtr; - slaveData++; - readDataPtr++; - } - } - - _payloadLen /= 2; - slaveRtp._payloadLen = _payloadLen; -} - - -//void NETEQTEST_RTPpacket::splitStereoFrame(WebRtc_UWord8 *data, WebRtc_UWord16 *lenBytes, WebRtc_UWord8 *slaveData, WebRtc_UWord16 *slaveLenBytes) -void NETEQTEST_RTPpacket::splitStereoFrame(NETEQTEST_RTPpacket& slaveRtp) -{ - if(!_payloadPtr || !slaveRtp._payloadPtr - || _payloadLen <= 0 || slaveRtp._memSize < _memSize) - { - return; - } - - memmove(slaveRtp._payloadPtr, _payloadPtr + _payloadLen/2, _payloadLen/2); - - _payloadLen /= 2; - slaveRtp._payloadLen = _payloadLen; -} - diff --git a/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.h b/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.h deleted file mode 100644 index d5efb74f9..000000000 --- a/modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef NETEQTEST_RTPPACKET_H -#define NETEQTEST_RTPPACKET_H - -#include -#include -#include "typedefs.h" -#include "webrtc_neteq_internal.h" - -enum stereoModes { - stereoModeMono, - stereoModeSample1, - stereoModeSample2, - stereoModeFrame -}; - -class NETEQTEST_RTPpacket -{ -public: - NETEQTEST_RTPpacket(); - NETEQTEST_RTPpacket(const NETEQTEST_RTPpacket& copyFromMe); - NETEQTEST_RTPpacket & operator = (const NETEQTEST_RTPpacket & other); - bool operator !() const { return (dataLen() < 0); }; - ~NETEQTEST_RTPpacket(); - void reset(); - int readFromFile(FILE *fp); - int readFixedFromFile(FILE *fp, int len); - int writeToFile(FILE *fp); - void blockPT(WebRtc_UWord8 pt); - //WebRtc_Word16 payloadType(); - void parseHeader(); - void parseHeader(WebRtcNetEQ_RTPInfo & rtpInfo); - WebRtcNetEQ_RTPInfo const * RTPinfo() const; - WebRtc_UWord8 * datagram() const; - WebRtc_UWord8 * payload() const; - WebRtc_Word16 payloadLen() const; - WebRtc_Word16 dataLen() const; - bool isParsed() const; - bool isLost() const; - WebRtc_UWord32 time() const { return _receiveTime; }; - - WebRtc_UWord8 payloadType() const; - WebRtc_UWord16 sequenceNumber() const; - WebRtc_UWord32 timeStamp() const; - WebRtc_UWord32 SSRC() const; - WebRtc_UWord8 markerBit() const; - - int setPayloadType(WebRtc_UWord8 pt); - int setSequenceNumber(WebRtc_UWord16 sn); - int setTimeStamp(WebRtc_UWord32 ts); - int setSSRC(WebRtc_UWord32 ssrc); - int setMarkerBit(WebRtc_UWord8 mb); - void setTime(WebRtc_UWord32 receiveTime) { _receiveTime = receiveTime; }; - - int setRTPheader(const WebRtcNetEQ_RTPInfo *RTPinfo); - - int splitStereo(NETEQTEST_RTPpacket& slaveRtp, enum stereoModes mode); - - WebRtc_UWord8 * _datagram; - WebRtc_UWord8 * _payloadPtr; - int _memSize; - WebRtc_Word16 _datagramLen; - WebRtc_Word16 _payloadLen; - WebRtcNetEQ_RTPInfo _rtpInfo; - bool _rtpParsed; - WebRtc_UWord32 _receiveTime; - bool _lost; - std::map _blockList; - -private: - void makeRTPheader(unsigned char* rtp_data, WebRtc_UWord8 payloadType, WebRtc_UWord16 seqNo, WebRtc_UWord32 timestamp, WebRtc_UWord32 ssrc, WebRtc_UWord8 markerBit) const; - WebRtc_UWord16 parseRTPheader(const WebRtc_UWord8 *datagram, int datagramLen, WebRtcNetEQ_RTPInfo *RTPinfo, WebRtc_UWord8 **payloadPtr = NULL) const; - void splitStereoSample(NETEQTEST_RTPpacket& slaveRtp, int stride); - void splitStereoFrame(NETEQTEST_RTPpacket& slaveRtp); -}; - -#endif //NETEQTEST_RTPPACKET_H diff --git a/modules/audio_coding/NetEQ/main/test/NetEqRTPplay.cc b/modules/audio_coding/NetEQ/main/test/NetEqRTPplay.cc deleted file mode 100644 index 748380a02..000000000 --- a/modules/audio_coding/NetEQ/main/test/NetEqRTPplay.cc +++ /dev/null @@ -1,1756 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* header includes */ -#include "typedefs.h" -#include "stdio.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" -#include "webrtc_neteq_help_macros.h" -#include "neteq_error_codes.h" // for the API test - -#include "NETEQTEST_RTPpacket.h" -#include "NETEQTEST_NetEQClass.h" -#include "NETEQTEST_CodecClass.h" - -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#include -#endif - -#ifdef WEBRTC_LINUX -#include -#include -#include -#endif - -//#include "vld.h" - -//#define NETEQ_DELAY_LOGGING -//#define DUMMY_SLAVE_CHANNEL - -#ifdef NETEQ_DELAY_LOGGING -#include "delay_logging.h" -#define DUMMY_SLAVE_CHANNEL // do not use a slave channel, only generate zeros instead -#endif - - -/************************/ -/* Define payload types */ -/************************/ - -// Payload types are defined in the textfile ptypes.txt, and can be changed after compilation. - - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define TIME_STEP 1 -#define FIRSTLINELEN 40 -#define MAX_NETEQ_BUFFERSIZE 170000 //100000 -#define CHECK_ZERO(a) {int errCode = a; char tempErrName[WEBRTC_NETEQ_MAX_ERROR_NAME]; if((errCode)!=0){errCode = WebRtcNetEQ_GetErrorCode(inst); WebRtcNetEQ_GetErrorName(errCode, tempErrName, WEBRTC_NETEQ_MAX_ERROR_NAME); printf("\n %s \n line: %d \n error at %s\n Error Code = %d\n",__FILE__,__LINE__,#a, errCode); exit(0);}} -#define CHECK_NOT_NULL(a) if((a)==NULL){printf("\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} -//#define PLAY_CLEAN // ignore arrival times and let the packets arrive according to RTP timestamps -#define HDR_SIZE 8 // rtpplay packet header size in bytes -//#define JUNK_DATA // scramble the payloads to test error resilience -//#define ZERO_TS_START - -#ifdef JUNK_DATA - #define SEED_FILE "randseed.txt" -#endif - - -#ifdef WIN32 -#define MY_MAX_DRIVE _MAX_DRIVE -#define MY_MAX_PATH _MAX_PATH -#define MY_MAX_FNAME _MAX_FNAME -#define MY_MAX_EXT _MAX_EXT -#else -#if defined(WEBRTC_LINUX) -#include -//#define MY_MAX_DRIVE 17 // arbitary number -#define MY_MAX_PATH PATH_MAX -//#define MY_MAX_FNAME NAME_MAX -//#define MY_MAX_EXT 17 // arbitary number -#endif -#endif - -/************/ -/* Typedefs */ -/************/ - -typedef struct { - enum WebRtcNetEQDecoder codec; - enum stereoModes stereo; - NETEQTEST_Decoder * decoder[2]; - int fs; -} decoderStruct; - - -/*************************/ -/* Function declarations */ -/*************************/ - -void stereoInterleave(WebRtc_Word16 *data, WebRtc_Word16 totalLen); -int getNextRecoutTime(FILE *fp, WebRtc_UWord32 *nextTime); -void getNextExtraDelay(FILE *fp, WebRtc_UWord32 *t, int *d); -bool splitStereo(NETEQTEST_RTPpacket& rtp, NETEQTEST_RTPpacket& rtpSlave, - const WebRtc_Word16 *stereoPtype, const enum stereoModes *stereoMode, int noOfStereoCodecs, - const WebRtc_Word16 *cngPtype, int noOfCngCodecs, - bool *isStereo); -void parsePtypeFile(FILE *ptypeFile, std::map* decoders); -int populateUsedCodec(std::map* decoders, enum WebRtcNetEQDecoder *usedCodec); -void createAndInsertDecoders (NETEQTEST_NetEQClass *neteq, std::map* decoders, int channelNumber); -void free_coders(std::map & decoders); -int doAPItest(); -bool changeStereoMode(NETEQTEST_RTPpacket & rtp, std::map & decoders, enum stereoModes *stereoMode); - - - -/********************/ -/* Global variables */ -/********************/ - -WebRtc_Word16 NetEqPacketBuffer[MAX_NETEQ_BUFFERSIZE>>1]; -WebRtc_Word16 NetEqPacketBufferSlave[MAX_NETEQ_BUFFERSIZE>>1]; -FILE *in_file; -FILE *out_file; - -#ifdef NETEQ_DELAY_LOGGING -extern "C" { - FILE *delay_fid2; /* file pointer */ - WebRtc_UWord32 tot_received_packets=0; -} -#endif - -#ifdef DEF_BUILD_DATE -extern char BUILD_DATE; -#endif - -WebRtc_UWord32 writtenSamples = 0; -WebRtc_UWord32 simClock=0; - -int main(int argc, char* argv[]) -{ - std::vector NetEQvector; - NETEQTEST_RTPpacket rtp; - char version[20]; - char firstline[FIRSTLINELEN]; - - NETEQTEST_RTPpacket slaveRtp; - //bool switchMS = false; - //bool duplicatePayload = false; - enum WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd-1]; - int noOfCodecs; - int ok; - WebRtc_Word16 out_data[640*2]; - WebRtc_Word16 outLen, writeLen; - int fs = 8000; - WebRtcNetEQ_RTCPStat RTCPstat; -#ifdef WIN32 - char outdrive[MY_MAX_DRIVE]; - char outpath[MY_MAX_PATH]; - char outfile[MY_MAX_FNAME]; - char outext[MY_MAX_EXT]; -#endif - char outfilename[MY_MAX_PATH]; -#ifdef NETEQ_DELAY_LOGGING - float clock_float; - int temp_var; -#endif -#ifdef JUNK_DATA - FILE *seedfile; -#endif - FILE *recoutTimes = NULL; - FILE *extraDelays = NULL; - WebRtcNetEQPlayoutMode streamingMode = kPlayoutOn; - bool preParseRTP = false; - bool rtpOnly = false; - int packetLen = 0; - int packetCount = 0; - std::map decoders; - - /* get the version string */ - WebRtcNetEQ_GetVersion(version); - printf("\n\nNetEq version: %s\n", version); -#ifdef DEF_BUILD_DATE - printf("Build time: %s\n", __BUILD_DATE); -#endif - - /* check number of parameters */ - if ((argc < 3) -#ifdef WIN32 // implicit output file name possible for windows - && (argc < 2) -#endif - ) { - /* print help text and exit */ - printf("Test program for NetEQ.\n"); - printf("The program reads an RTP stream from file and inserts it into NetEQ.\n"); - printf("The format of the RTP stream file should be the same as for rtpplay,\n"); - printf("and can be obtained e.g., from Ethereal by using\n"); - printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> Save As\n\n"); - printf("Usage:\n\n"); -#ifdef WIN32 - printf("%s RTPfile [outfile] [-options]\n", argv[0]); -#else - printf("%s RTPfile outfile [-options]\n", argv[0]); -#endif - printf("where:\n"); - - printf("RTPfile : RTP stream input file\n\n"); - - printf("outfile : PCM speech output file\n"); - printf(" Output file name is derived from RTP file name if omitted\n\n"); - - printf("-options are optional switches:\n"); - printf("\t-recout datfile : supply recout times\n"); - printf("\t-extradelay datfile : supply extra delay settings and timing\n"); - printf("\t-streaming : engage streaming mode\n"); - printf("\t-fax : engage fax mode\n"); - printf("\t-preparsertp : use RecIn with pre-parsed RTP\n"); - printf("\t-rtponly packLenBytes : input file consists of constant size RTP packets without RTPplay headers\n"); - //printf("\t-switchms : switch from mono to stereo (copy channel) after 10 seconds\n"); - //printf("\t-duplicate : use two instances with identical input (2-channel mono)\n"); - - return(0); - } - - if (strcmp(argv[1], "-apitest")==0) { - // do API test and then return - ok=doAPItest(); - - if (ok==0) - printf("API test successful!\n"); - else - printf("API test failed!\n"); - - return(ok); - } - - in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - - int argIx = 2; // index of next argument from command line - - if ( argc >= 3 && argv[2][0] != '-' ) { // output name given on command line - strcpy(outfilename, argv[2]); - argIx++; - } else { // derive output name from input name -#ifdef WIN32 - _splitpath(argv[1],outdrive,outpath,outfile,outext); - _makepath(outfilename,outdrive,outpath,outfile,"pcm"); -#else - fprintf(stderr,"Output file name must be specified.\n"); - return(-1); -#endif - } - out_file=fopen(outfilename,"wb"); - if (out_file==NULL) { - fprintf(stderr,"Could not open file %s for writing\n", outfilename); - return(-1); - } - printf("Output file: %s\n\n",outfilename); - - // Parse for more arguments, all beginning with '-' - - while( argIx < argc ) { - if (argv[argIx][0] != '-') { - fprintf(stderr,"Unknown input argument %s\n", argv[argIx]); - return(-1); - } - - if( strcmp(argv[argIx], "-recout") == 0 ) { - argIx++; - recoutTimes = fopen(argv[argIx], "rb"); - CHECK_NOT_NULL(recoutTimes); - argIx++; - } - else if( strcmp(argv[argIx], "-extradelay") == 0 ) { - argIx++; - extraDelays = fopen(argv[argIx], "rb"); - CHECK_NOT_NULL(extraDelays); - argIx++; - } - else if( strcmp(argv[argIx], "-streaming") == 0 ) { - argIx++; - streamingMode = kPlayoutStreaming; - } - else if( strcmp(argv[argIx], "-fax") == 0 ) { - argIx++; - streamingMode = kPlayoutFax; - } - else if( strcmp(argv[argIx], "-preparsertp") == 0 ) { - argIx++; - preParseRTP = true; - } - else if( strcmp(argv[argIx], "-rtponly") == 0 ) { - argIx++; - rtpOnly = true; - packetLen = atoi(argv[argIx]); - argIx++; - if (packetLen <= 0) - { - printf("Wrong packet size used with argument -rtponly.\n"); - exit(1); - } - } - //else if( strcmp(argv[argIx], "-switchms") == 0 ) { - // argIx++; - // switchMS = true; - //} - //else if( strcmp(argv[argIx], "-duplicate") == 0 ) { - // argIx++; - // duplicatePayload = true; - //} - else { - fprintf(stderr,"Unknown input argument %s\n", argv[argIx]); - return(-1); - } - } - - - -#ifdef NETEQ_DELAY_LOGGING - char delayfile[MY_MAX_PATH]; -#ifdef WIN32 - _splitpath(outfilename,outdrive,outpath,outfile,outext); - _makepath(delayfile,outdrive,outpath,outfile,"d"); -#else - sprintf(delayfile, "%s.d", outfilename); -#endif - delay_fid2 = fopen(delayfile,"wb"); - fprintf(delay_fid2, "#!NetEQ_Delay_Logging%s\n", NETEQ_DELAY_LOGGING_VERSION_STRING); -#endif - - char ptypesfile[MY_MAX_PATH]; -#ifdef WIN32 - _splitpath(argv[0],outdrive,outpath,outfile,outext); - _makepath(ptypesfile,outdrive,outpath,"ptypes","txt"); -#else - // TODO(hlundin): Include path to ptypes, as for WIN32 above. - strcpy(ptypesfile, "ptypes.txt"); -#endif - FILE *ptypeFile = fopen(ptypesfile,"rt"); - CHECK_NOT_NULL(ptypeFile); - - - parsePtypeFile(ptypeFile, &decoders); - fclose(ptypeFile); - - noOfCodecs = populateUsedCodec(&decoders, usedCodec); - - - /* read RTP file header */ - if (!rtpOnly) - { - fgets(firstline, FIRSTLINELEN, in_file); - if(strncmp(firstline,"#!rtpplay",9) == 0) { - if(strncmp(firstline,"#!rtpplay1.0",12) != 0){ - printf("ERROR: wrong rtpplay version, must be 1.0\n"); - exit(0); - } - } - else if (strncmp(firstline,"#!RTPencode",11) == 0) { - if(strncmp(firstline,"#!RTPencode1.0",14) != 0){ - printf("ERROR: wrong RTPencode version, must be 1.0\n"); - exit(0); - } - } - else { - printf("ERROR: wrong file format of input file\n"); - exit(0); - } - - WebRtc_UWord32 start_sec; - WebRtc_UWord32 start_usec; - WebRtc_UWord32 source; - WebRtc_UWord16 port; - WebRtc_UWord16 padding; - - fread(&start_sec, 4, 1, in_file); - start_sec=ntohl(start_sec); - fread(&start_usec, 4, 1, in_file); - start_usec=ntohl(start_usec); - fread(&source, 4, 1, in_file); - source=ntohl(source); - fread(&port, 2, 1, in_file); - port=ntohs(port); - fread(&padding, 2, 1, in_file); - padding=ntohs(padding); - } - - /* check payload type for first speech packet */ - long tempFilePos = ftell(in_file); - enum stereoModes stereoMode = stereoModeMono; - - if (!rtpOnly) - { - while (rtp.readFromFile(in_file) >= 0) - { - if (decoders.count(rtp.payloadType()) > 0 - && decoders[rtp.payloadType()].codec != kDecoderRED - && decoders[rtp.payloadType()].codec != kDecoderAVT - && decoders[rtp.payloadType()].codec != kDecoderCNG ) - { - stereoMode = decoders[rtp.payloadType()].stereo; - fs = decoders[rtp.payloadType()].fs; - break; - } - } - } - else - { - while (rtp.readFixedFromFile(in_file, packetLen) >= 0) - { - if (decoders.count(rtp.payloadType()) > 0 - && decoders[rtp.payloadType()].codec != kDecoderRED - && decoders[rtp.payloadType()].codec != kDecoderAVT - && decoders[rtp.payloadType()].codec != kDecoderCNG ) - { - stereoMode = decoders[rtp.payloadType()].stereo; - fs = decoders[rtp.payloadType()].fs; - break; - } - } - } - - fseek(in_file, tempFilePos, SEEK_SET /* from beginning */); - - - /* block some payload types */ - //rtp.blockPT(72); - //rtp.blockPT(23); - - /* read first packet */ - if (!rtpOnly) - { - rtp.readFromFile(in_file); - } - else - { - rtp.readFixedFromFile(in_file, packetLen); - rtp.setTime((1000 * rtp.timeStamp()) / fs); - } - if (!rtp) - { - printf("\nWarning: RTP file is empty\n\n"); - } - - - /* Initialize NetEQ instances */ - int numInst = 1; - if (stereoMode > stereoModeMono) - { - numInst = 2; - } - - for (int i = 0; i < numInst; i++) - { - // create memory, allocate, initialize, and allocate packet buffer memory - NetEQvector.push_back (new NETEQTEST_NetEQClass(usedCodec, noOfCodecs, static_cast(fs), kTCPLargeJitter)); - - createAndInsertDecoders (NetEQvector[i], &decoders, i /* channel */); - - WebRtcNetEQ_SetAVTPlayout(NetEQvector[i]->instance(),1); // enable DTMF playout - - WebRtcNetEQ_SetPlayoutMode(NetEQvector[i]->instance(), streamingMode); - - NetEQvector[i]->usePreparseRTP(preParseRTP); - - if (numInst > 1) - { - // we are using master/slave mode - if (i == 0) - { - // first instance is master - NetEQvector[i]->isMaster(); - } - else - { - // all other are slaves - NetEQvector[i]->isSlave(); - } - } - } - - -#ifdef ZERO_TS_START - WebRtc_UWord32 firstTS = rtp.timeStamp(); - rtp.setTimeStamp(0); -#else - WebRtc_UWord32 firstTS = 0; -#endif - - // check stereo mode - if (stereoMode > stereoModeMono) - { - if(rtp.splitStereo(slaveRtp, stereoMode)) - { - printf("Error in splitStereo\n"); - } - } - -#ifdef PLAY_CLEAN - WebRtc_UWord32 prevTS = rtp.timeStamp(); - WebRtc_UWord32 currTS, prev_time; -#endif - -#ifdef JUNK_DATA - unsigned int random_seed = (unsigned int) /*1196764538; */time(NULL); - srand(random_seed); - - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "%u\n", random_seed); - fclose(seedfile); - } -#endif - - WebRtc_UWord32 nextRecoutTime; - int lastRecout = getNextRecoutTime(recoutTimes, &nextRecoutTime); // does nothing if recoutTimes == NULL - - if (recoutTimes) - simClock = (rtp.time() < nextRecoutTime ? rtp.time(): nextRecoutTime); - else - simClock = rtp.time(); // start immediately with first packet - - WebRtc_UWord32 start_clock = simClock; - - WebRtc_UWord32 nextExtraDelayTime; - int extraDelay = -1; - getNextExtraDelay(extraDelays, &nextExtraDelayTime, &extraDelay); - - void *msInfo; - msInfo = malloc(WebRtcNetEQ_GetMasterSlaveInfoSize()); - if(msInfo == NULL) - return(-1); - - while(rtp.dataLen() >= 0 || (recoutTimes && !lastRecout)) { -// printf("simClock = %Lu\n", simClock); - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_CLOCK; - clock_float = (float) simClock; - fwrite(&temp_var,sizeof(int),1,delay_fid2); - fwrite(&clock_float, sizeof(float),1,delay_fid2); -#endif - /* time to set extra delay */ - if (extraDelay > -1 && simClock >= nextExtraDelayTime) { - // set extra delay for all instances - for (int i = 0; i < numInst; i++) - { - WebRtcNetEQ_SetExtraDelay(NetEQvector[i]->instance(), extraDelay); - } - getNextExtraDelay(extraDelays, &nextExtraDelayTime, &extraDelay); - } - - /* check if time to receive */ - while (simClock >= rtp.time() && rtp.dataLen() >= 0) - { - if (rtp.dataLen() > 0) - { - - // insert main packet - NetEQvector[0]->recIn(rtp); - - if (stereoMode > stereoModeMono - && slaveRtp.dataLen() > 0) - { - // insert slave packet - NetEQvector[1]->recIn(slaveRtp); - } - - } - - /* get next packet */ -#ifdef PLAY_CLEAN - prev_time = rtp.time(); -#endif - if (!rtpOnly) - { - rtp.readFromFile(in_file); - } - else - { - rtp.readFixedFromFile(in_file, packetLen); - rtp.setTime((1000 * rtp.timeStamp()) / fs); - } - - if (rtp.dataLen() >= 0) - { - rtp.setTimeStamp(rtp.timeStamp() - firstTS); - } - - packetCount++; - - if (changeStereoMode(rtp, decoders, &stereoMode)) - { - printf("Warning: stereo mode changed\n"); - } - - if (stereoMode > stereoModeMono) - { - if(rtp.splitStereo(slaveRtp, stereoMode)) - { - printf("Error in splitStereo\n"); - } - } - -#ifdef PLAY_CLEAN - currTS = rtp.timeStamp(); - rtp.setTime(prev_time + (currTS-prevTS)/(fs/1000)); - prevTS = currTS; -#endif - } - - /* check if time to RecOut */ - if ( (!recoutTimes && (simClock%10)==0) // recout times not given from file - || ( recoutTimes && (simClock >= nextRecoutTime) ) ) // recout times given from file - { - if (stereoMode > stereoModeMono) - { - // stereo - WebRtc_Word16 tempLen; - tempLen = NetEQvector[0]->recOut( out_data, msInfo ); // master - outLen = NetEQvector[1]->recOut( &out_data[tempLen], msInfo ); // slave - - assert(tempLen == outLen); - - writeLen = outLen * 2; - stereoInterleave(out_data, writeLen); - } - else - { - // mono - outLen = NetEQvector[0]->recOut( out_data ); - writeLen = outLen; - } - - // write to file - fwrite(out_data,writeLen,2,out_file); - writtenSamples += writeLen; - - - lastRecout = getNextRecoutTime(recoutTimes, &nextRecoutTime); // does nothing if recoutTimes == NULL - - /* ask for statistics */ - WebRtcNetEQ_NetworkStatistics inCallStats; - WebRtcNetEQ_GetNetworkStatistics(NetEQvector[0]->instance(), &inCallStats); - - } - - /* increase time */ - simClock+=TIME_STEP; - } - - fclose(in_file); - fclose(out_file); - -#ifdef NETEQ_DELAY_LOGGING - temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EOF; - fwrite(&temp_var,sizeof(int),1,delay_fid2); - fwrite(&tot_received_packets,sizeof(WebRtc_UWord32),1,delay_fid2); - fprintf(delay_fid2,"End of file\n"); - fclose(delay_fid2); -#endif - - WebRtcNetEQ_GetRTCPStats(NetEQvector[0]->instance(), &RTCPstat); - printf("RTCP statistics:\n"); - printf(" cum_lost : %d\n", (int) RTCPstat.cum_lost); - printf(" ext_max : %d\n", (int) RTCPstat.ext_max); - printf(" fraction_lost : %d (%f%%)\n", RTCPstat.fraction_lost, (float)(100.0*RTCPstat.fraction_lost/256.0)); - printf(" jitter : %d\n", (int) RTCPstat.jitter); - - WebRtcNetEQ_JitterStatistics jitterStats; - WebRtcNetEQ_GetJitterStatistics(NetEQvector[0]->instance(), &jitterStats); - - printf("\nPost-call statistics:\n"); - printf(" Call duration ms : %lu\n", simClock-start_clock); - printf(" Expand (voice) ms : %lu \t(%.2f%%)\n", jitterStats.interpolatedVoiceMs, (float) 100.0 * jitterStats.interpolatedVoiceMs/(simClock-start_clock)); - printf(" Expand (silence) ms : %lu \t(%.2f%%)\n", jitterStats.interpolatedSilentMs, (float) 100.0 * jitterStats.interpolatedSilentMs/(simClock-start_clock)); - printf(" Accelerate ms : %lu \t(%.2f%%)\n", jitterStats.accelerateMs, (float) 100.0 * jitterStats.accelerateMs/(simClock-start_clock)); - printf(" Flushed ms : %lu \t(%.2f%%)\n", jitterStats.flushedMs, (float) 100.0 * jitterStats.flushedMs/(simClock-start_clock)); - printf(" JB avg size ms : %lu\n", jitterStats.jbAvgSize); - printf(" JB max size ms : %lu\n", jitterStats.jbMaxSize); - printf(" Max inter-arrival ms: %lu\n", jitterStats.longestIATms); - - printf("\nComplexity estimates (including sub-components):\n"); - printf(" RecIn complexity : %.2f MCPS\n", NetEQvector[0]->getRecInTime() / ((float) 1000*(simClock-start_clock))); - printf(" RecOut complexity : %.2f MCPS\n", NetEQvector[0]->getRecOutTime() / ((float) 1000*(simClock-start_clock))); - - free_coders(decoders); - //free_coders(0 /* first channel */); - // if (stereoMode > stereoModeMono) { - // free_coders(1 /* second channel */); - // } - free(msInfo); - - for (std::vector::iterator it = NetEQvector.begin(); - it < NetEQvector.end(); delete *it++); - - printf("\nSimulation done!\n"); - -#ifdef JUNK_DATA - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "ok\n\n"); - fclose(seedfile); - } -#endif - - - // Log complexity to file -/* FILE *statfile; - statfile = fopen("complexity.txt","at"); - fprintf(statfile,"%.4f, %.4f\n", (float) totTime_RecIn.QuadPart / ((float) 1000*(simClock-start_clock)), (float) totTime_RecOut.QuadPart / ((float) 1000*(simClock-start_clock))); - fclose(statfile);*/ - - return(0); - -} - - - - - -/****************/ -/* Subfunctions */ -/****************/ - -bool splitStereo(NETEQTEST_RTPpacket& rtp, NETEQTEST_RTPpacket& rtpSlave, - const WebRtc_Word16 *stereoPtype, const enum stereoModes *stereoMode, int noOfStereoCodecs, - const WebRtc_Word16 *cngPtype, int noOfCngCodecs, - bool *isStereo) -{ - - // init - //bool isStereo = false; - enum stereoModes tempStereoMode = stereoModeMono; - bool isCng = false; - - // check payload length - if (rtp.dataLen() <= 0) { - //*isStereo = false; // don't change - return(*isStereo); - } - - // check payload type - WebRtc_Word16 ptype = rtp.payloadType(); - - // is this a cng payload? - for (int k = 0; k < noOfCngCodecs; k++) { - if (ptype == cngPtype[k]) { - // do not change stereo state - isCng = true; - tempStereoMode = stereoModeFrame; - } - } - - if (!isCng) - { - *isStereo = false; - - // is this payload type a stereo codec? which type? - for (int k = 0; k < noOfStereoCodecs; k++) { - if (ptype == stereoPtype[k]) { - tempStereoMode = stereoMode[k]; - *isStereo = true; - break; // exit for loop - } - } - } - - if (*isStereo) - { - // split the payload if stereo - - if(rtp.splitStereo(rtpSlave, tempStereoMode)) - { - printf("Error in splitStereo\n"); - } - - } - - return(*isStereo); - -} - -void stereoInterleave(WebRtc_Word16 *data, WebRtc_Word16 totalLen) -{ - int k; - - for(k = totalLen/2; k < totalLen; k++) { - WebRtc_Word16 temp = data[k]; - memmove(&data[2*k - totalLen + 2], &data[2*k - totalLen + 1], (totalLen - k -1) * sizeof(WebRtc_Word16)); - data[2*k - totalLen + 1] = temp; - } -} - - -int getNextRecoutTime(FILE *fp, WebRtc_UWord32 *nextTime) { - - float tempTime; - - if (!fp) { - return -1; - } - - if (fread(&tempTime, sizeof(float), 1, fp) != 0) { - // not end of file - *nextTime = (WebRtc_UWord32) tempTime; - return 0; - } - - *nextTime = 0; - fclose(fp); - - return 1; -} - -void getNextExtraDelay(FILE *fp, WebRtc_UWord32 *t, int *d) { - - float temp[2]; - - if(!fp) { - *d = -1; - return; - } - - if (fread(&temp, sizeof(float), 2, fp) != 0) { - // not end of file - *t = (WebRtc_UWord32) temp[0]; - *d = (int) temp[1]; - return; - } - - *d = -1; - fclose(fp); - - return; -} - - -void parsePtypeFile(FILE *ptypeFile, std::map* decoders) -{ - int n, pt; - char codec[100]; - decoderStruct tempDecoder; - - // read first line - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - - while (n==2) - { - memset(&tempDecoder, 0, sizeof(decoderStruct)); - tempDecoder.stereo = stereoModeMono; - - if( pt >= 0 // < 0 disables this codec - && isalpha(codec[0]) ) // and is a letter - { - - /* check for stereo */ - int L = strlen(codec); - bool isStereo = false; - - if (codec[L-1] == '*') { - // stereo codec - isStereo = true; - - // remove '*' - codec[L-1] = '\0'; - } - -#ifdef CODEC_G711 - if(strcmp(codec, "pcmu") == 0) { - tempDecoder.codec = kDecoderPCMu; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "pcma") == 0) { - tempDecoder.codec = kDecoderPCMa; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_IPCMU - else if(strcmp(codec, "eg711u") == 0) { - tempDecoder.codec = kDecoderEG711u; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_IPCMA - else if(strcmp(codec, "eg711a") == 0) { - tempDecoder.codec = kDecoderEG711a; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_ILBC - else if(strcmp(codec, "ilbc") == 0) { - tempDecoder.codec = kDecoderILBC; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_ISAC - else if(strcmp(codec, "isac") == 0) { - tempDecoder.codec = kDecoderISAC; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_ISACLC - else if(strcmp(codec, "isaclc") == 0) { - tempDecoder.codec = NETEQ_CODEC_ISACLC; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_ISAC_SWB - else if(strcmp(codec, "isacswb") == 0) { - tempDecoder.codec = kDecoderISACswb; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_IPCMWB - else if(strcmp(codec, "ipcmwb") == 0) { - tempDecoder.codec = kDecoderIPCMwb; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722 - else if(strcmp(codec, "g722") == 0) { - tempDecoder.codec = kDecoderG722; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_16 - else if(strcmp(codec, "g722_1_16") == 0) { - tempDecoder.codec = kDecoderG722_1_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_24 - else if(strcmp(codec, "g722_1_24") == 0) { - tempDecoder.codec = kDecoderG722_1_24; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1_32 - else if(strcmp(codec, "g722_1_32") == 0) { - tempDecoder.codec = kDecoderG722_1_32; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_G722_1C_24 - else if(strcmp(codec, "g722_1c_24") == 0) { - tempDecoder.codec = kDecoderG722_1C_24; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G722_1C_32 - else if(strcmp(codec, "g722_1c_32") == 0) { - tempDecoder.codec = kDecoderG722_1C_32; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G722_1C_48 - else if(strcmp(codec, "g722_1c_48") == 0) { - tempDecoder.codec = kDecoderG722_1C_48; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_G723 - else if(strcmp(codec, "g723") == 0) { - tempDecoder.codec = NETEQ_CODEC_G723; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G726 - else if(strcmp(codec, "g726_16") == 0) { - tempDecoder.codec = kDecoderG726_16; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_24") == 0) { - tempDecoder.codec = kDecoderG726_24; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_32") == 0) { - tempDecoder.codec = kDecoderG726_32; - tempDecoder.fs = 8000; - } - else if(strcmp(codec, "g726_40") == 0) { - tempDecoder.codec = kDecoderG726_40; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729 - else if(strcmp(codec, "g729") == 0) { - tempDecoder.codec = kDecoderG729; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729D - else if(strcmp(codec, "g729d") == 0) { - tempDecoder.codec = NETEQ_CODEC_G729D; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_G729_1 - else if(strcmp(codec, "g729_1") == 0) { - tempDecoder.codec = kDecoderG729_1; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_GSMFR - else if(strcmp(codec, "gsmfr") == 0) { - tempDecoder.codec = kDecoderGSMFR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_GSMEFR - else if(strcmp(codec, "gsmefr") == 0) { - tempDecoder.codec = NETEQ_CODEC_GSMEFR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_AMR - else if(strcmp(codec, "amr") == 0) { - tempDecoder.codec = kDecoderAMR; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_AMRWB - else if(strcmp(codec, "amrwb") == 0) { - tempDecoder.codec = kDecoderAMRWB; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_DVI4 - else if(strcmp(codec, "dvi4") == 0) { - tempDecoder.codec = NETEQ_CODEC_DVI4; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SPEEX_8 - else if(strcmp(codec, "speex8") == 0) { - tempDecoder.codec = kDecoderSPEEX_8; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SPEEX_16 - else if(strcmp(codec, "speex16") == 0) { - tempDecoder.codec = kDecoderSPEEX_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_SILK_NB - else if(strcmp(codec, "silk8") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_8; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_SILK_WB - else if(strcmp(codec, "silk12") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_12; - tempDecoder.fs = 16000; - } - else if(strcmp(codec, "silk16") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_16; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_SILK_SWB - else if(strcmp(codec, "silk24") == 0) { - tempDecoder.codec = NETEQ_CODEC_SILK_24; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_MELPE - else if(strcmp(codec, "melpe") == 0) { - tempDecoder.codec = NETEQ_CODEC_MELPE; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_PCM16B - else if(strcmp(codec, "pcm16b") == 0) { - tempDecoder.codec = kDecoderPCM16B; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_PCM16B_WB - else if(strcmp(codec, "pcm16b_wb") == 0) { - tempDecoder.codec = kDecoderPCM16Bwb; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_PCM16B_32KHZ - else if(strcmp(codec, "pcm16b_swb32khz") == 0) { - tempDecoder.codec = kDecoderPCM16Bswb32kHz; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_PCM16B_48KHZ - else if(strcmp(codec, "pcm16b_swb48khz") == 0) { - tempDecoder.codec = kDecoderPCM16Bswb48kHz; - tempDecoder.fs = 48000; - } -#endif -#ifdef CODEC_CNGCODEC8 - else if(strcmp(codec, "cn") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_CNGCODEC16 - else if(strcmp(codec, "cn_wb") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 16000; - } -#endif -#ifdef CODEC_CNGCODEC32 - else if(strcmp(codec, "cn_swb32") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 32000; - } -#endif -#ifdef CODEC_CNGCODEC48 - else if(strcmp(codec, "cn_swb48") == 0) { - tempDecoder.codec = kDecoderCNG; - tempDecoder.fs = 48000; - } -#endif -#ifdef CODEC_ATEVENT_DECODE - else if(strcmp(codec, "avt") == 0) { - tempDecoder.codec = kDecoderAVT; - tempDecoder.fs = 8000; - } -#endif -#ifdef CODEC_RED - else if(strcmp(codec, "red") == 0) { - tempDecoder.codec = kDecoderRED; - tempDecoder.fs = 8000; - } -#endif - else if(isalpha(codec[0])) { - printf("Unsupported codec %s\n", codec); - // read next line and continue while loop - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - continue; - } - else { - // name is not recognized, and does not start with a letter - // hence, it is commented out - // read next line and continue while loop - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - continue; - } - - // handle stereo - if (tempDecoder.codec == kDecoderCNG) - { - // always set stereo mode for CNG, even if it is not marked at stereo - tempDecoder.stereo = stereoModeFrame; - } - else if(isStereo) - { - switch(tempDecoder.codec) { - // sample based codecs - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderG722: - { - // 1 octet per sample - tempDecoder.stereo = stereoModeSample1; - break; - } - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: - { - // 2 octets per sample - tempDecoder.stereo = stereoModeSample2; - break; - } - - // fixed-rate frame codecs -// case kDecoderG729: -// case NETEQ_CODEC_G729D: -// case NETEQ_CODEC_G729E: -// case kDecoderG722_1_16: -// case kDecoderG722_1_24: -// case kDecoderG722_1_32: -// case kDecoderG722_1C_24: -// case kDecoderG722_1C_32: -// case kDecoderG722_1C_48: -// case NETEQ_CODEC_MELPE: -// { -// tempDecoder.stereo = stereoModeFrame; -// break; -// } - default: - { - printf("Cannot use codec %s as stereo codec\n", codec); - exit(0); - } - } - } - - if (pt > 127) - { - printf("Payload type must be less than 128\n"); - exit(0); - } - - // insert into codecs map - (*decoders)[static_cast(pt)] = tempDecoder; - - } - - n = fscanf(ptypeFile, "%s %i\n", codec, &pt); - } // end while - -} - - -bool changeStereoMode(NETEQTEST_RTPpacket & rtp, std::map & decoders, enum stereoModes *stereoMode) -{ - if (decoders.count(rtp.payloadType()) > 0 - && decoders[rtp.payloadType()].codec != kDecoderRED - && decoders[rtp.payloadType()].codec != kDecoderAVT - && decoders[rtp.payloadType()].codec != kDecoderCNG ) - { - if (decoders[rtp.payloadType()].stereo != *stereoMode) - { - *stereoMode = decoders[rtp.payloadType()].stereo; - return true; // stereo mode did change - } - } - - return false; // stereo mode did not change -} - - -int populateUsedCodec(std::map* decoders, enum WebRtcNetEQDecoder *usedCodec) -{ - int numCodecs = 0; - - std::map::iterator it; - - it = decoders->begin(); - - for (int i = 0; i < static_cast(decoders->size()); i++, it++) - { - usedCodec[numCodecs] = (*it).second.codec; - numCodecs++; - } - - return numCodecs; -} - - -void createAndInsertDecoders (NETEQTEST_NetEQClass *neteq, std::map* decoders, int channelNumber) -{ - std::map::iterator it; - - for (it = decoders->begin(); it != decoders->end(); it++) - { - if (channelNumber == 0 || - ((*it).second.stereo > stereoModeMono )) - { - // create decoder instance - WebRtc_UWord8 pt = static_cast( (*it).first ); - NETEQTEST_Decoder **dec = &((*it).second.decoder[channelNumber]); - enum WebRtcNetEQDecoder type = (*it).second.codec; - - switch (type) - { -#ifdef CODEC_G711 - case kDecoderPCMu: - *dec = new decoder_PCMU( pt ); - break; - case kDecoderPCMa: - *dec = new decoder_PCMA( pt ); - break; -#endif -#ifdef CODEC_IPCMU - case kDecoderEG711u: - *dec = new decoder_IPCMU( pt ); - break; -#endif -#ifdef CODEC_IPCMA - case kDecoderEG711a: - *dec = new decoder_IPCMA( pt ); - break; -#endif -#ifdef CODEC_IPCMWB - case kDecoderIPCMwb: - *dec = new decoder_IPCMWB( pt ); - break; -#endif -#ifdef CODEC_ILBC - case kDecoderILBC: - *dec = new decoder_ILBC( pt ); - break; -#endif -#ifdef CODEC_ISAC - case kDecoderISAC: - *dec = new decoder_iSAC( pt ); - break; -#endif -#ifdef CODEC_ISAC_SWB - case kDecoderISACswb: - *dec = new decoder_iSACSWB( pt ); - break; -#endif -#ifdef CODEC_G729 - case kDecoderG729: - *dec = new decoder_G729( pt ); - break; - case NETEQ_CODEC_G729D: - printf("Error: G729D not supported\n"); - break; -#endif -#ifdef CODEC_G729E - case NETEQ_CODEC_G729E: - *dec = new decoder_G729E( pt ); - break; -#endif -#ifdef CODEC_G729_1 - case kDecoderG729_1: - *dec = new decoder_G729_1( pt ); - break; -#endif -#ifdef CODEC_G723 - case NETEQ_CODEC_G723: - *dec = new decoder_G723( pt ); - break; -#endif -#ifdef CODEC_PCM16B - case kDecoderPCM16B: - *dec = new decoder_PCM16B_NB( pt ); - break; -#endif -#ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb: - *dec = new decoder_PCM16B_WB( pt ); - break; -#endif -#ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz: - *dec = new decoder_PCM16B_SWB32( pt ); - break; -#endif -#ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz: - *dec = new decoder_PCM16B_SWB48( pt ); - break; -#endif -#ifdef CODEC_DVI4 - case NETEQ_CODEC_DVI4: - *dec = new decoder_DVI4( pt ); - break; -#endif -#ifdef CODEC_G722 - case kDecoderG722: - *dec = new decoder_G722( pt ); - break; -#endif -#ifdef CODEC_G722_1_16 - case kDecoderG722_1_16: - *dec = new decoder_G722_1_16( pt ); - break; -#endif -#ifdef CODEC_G722_1_24 - case kDecoderG722_1_24: - *dec = new decoder_G722_1_24( pt ); - break; -#endif -#ifdef CODEC_G722_1_32 - case kDecoderG722_1_32: - *dec = new decoder_G722_1_32( pt ); - break; -#endif -#ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24: - *dec = new decoder_G722_1C_24( pt ); - break; -#endif -#ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32: - *dec = new decoder_G722_1C_32( pt ); - break; -#endif -#ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48: - *dec = new decoder_G722_1C_48( pt ); - break; -#endif -#ifdef CODEC_AMR - case kDecoderAMR: - *dec = new decoder_AMR( pt ); - break; -#endif -#ifdef CODEC_AMRWB - case kDecoderAMRWB: - *dec = new decoder_AMRWB( pt ); - break; -#endif -#ifdef CODEC_GSMFR - case kDecoderGSMFR: - *dec = new decoder_GSMFR( pt ); - break; -#endif -#ifdef CODEC_GSMEFR - case NETEQ_CODEC_GSMEFR: - *dec = new decoder_GSMEFR( pt ); - break; -#endif -#ifdef CODEC_G726 - case kDecoderG726_16: - *dec = new decoder_G726_16( pt ); - break; - case kDecoderG726_24: - *dec = new decoder_G726_24( pt ); - break; - case kDecoderG726_32: - *dec = new decoder_G726_32( pt ); - break; - case kDecoderG726_40: - *dec = new decoder_G726_40( pt ); - break; -#endif -#ifdef CODEC_MELPE - case NETEQ_CODEC_MELPE: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_MELPE( pt ); -#endif - break; -#endif -#ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8: - *dec = new decoder_SPEEX( pt, 8000 ); - break; -#endif -#ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16: - *dec = new decoder_SPEEX( pt, 16000 ); - break; -#endif -#ifdef CODEC_RED - case kDecoderRED: - *dec = new decoder_RED( pt ); - break; -#endif -#ifdef CODEC_ATEVENT_DECODE - case kDecoderAVT: - *dec = new decoder_AVT( pt ); - break; -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - case kDecoderCNG: - *dec = new decoder_CNG( pt, static_cast((*it).second.fs) ); - break; -#endif -#ifdef CODEC_ISACLC - case NETEQ_CODEC_ISACLC: - *dec = new decoder_iSACLC( pt ); - break; -#endif -#ifdef CODEC_SILK_NB - case NETEQ_CODEC_SILK_8: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK8( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_WB - case NETEQ_CODEC_SILK_12: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK12( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_WB - case NETEQ_CODEC_SILK_16: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK16( pt ); -#endif - break; -#endif -#ifdef CODEC_SILK_SWB - case NETEQ_CODEC_SILK_24: -#if (_MSC_VER >= 1400) && !defined(_WIN64) // only for Visual 2005 or later, and not for x64 - *dec = new decoder_SILK24( pt ); -#endif - break; -#endif - - default: - printf("Unknown codec type encountered in createAndInsertDecoders\n"); - exit(0); - } - - // insert into codec DB - if (*dec) - { - (*dec)->loadToNetEQ(*neteq); - } - } - } - -} - - -void free_coders(std::map & decoders) -{ - std::map::iterator it; - - for (it = decoders.begin(); it != decoders.end(); it++) - { - if ((*it).second.decoder[0]) - { - delete (*it).second.decoder[0]; - } - - if ((*it).second.decoder[1]) - { - delete (*it).second.decoder[1]; - } - } -} - - - -#include "pcm16b.h" -#include "g711_interface.h" -#include "isac.h" - -int doAPItest() { - - char version[20]; - void *inst; - enum WebRtcNetEQDecoder usedCodec; - int NetEqBufferMaxPackets, BufferSizeInBytes; - WebRtcNetEQ_CodecDef codecInst; - WebRtcNetEQ_RTCPStat RTCPstat; - WebRtc_UWord32 timestamp; - int memorySize; - int ok; - - printf("API-test:\n"); - - /* get the version string */ - WebRtcNetEQ_GetVersion(version); - printf("NetEq version: %s\n\n", version); - - /* test that API functions return -1 if instance is NULL */ -#define CHECK_MINUS_ONE(x) {int errCode = x; if((errCode)!=-1){printf("\n API test failed at line %d: %s. Function did not return -1 as expected\n",__LINE__,#x); return(-1);}} -//#define RESET_ERROR(x) ((MainInst_t*) x)->ErrorCode = 0; - inst = NULL; - - CHECK_MINUS_ONE(WebRtcNetEQ_GetErrorCode(inst)) - CHECK_MINUS_ONE(WebRtcNetEQ_Assign(&inst, NULL)) -// printf("WARNING: Test of WebRtcNetEQ_Assign() is disabled due to a bug.\n"); - usedCodec=kDecoderPCMu; - CHECK_MINUS_ONE(WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter, &NetEqBufferMaxPackets, &BufferSizeInBytes)) - CHECK_MINUS_ONE(WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, BufferSizeInBytes)) - - CHECK_MINUS_ONE(WebRtcNetEQ_Init(inst, 8000)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetAVTPlayout(inst, 0)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetExtraDelay(inst, 17)) - CHECK_MINUS_ONE(WebRtcNetEQ_SetPlayoutMode(inst, kPlayoutOn)) - - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbReset(inst)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbRemove(inst, usedCodec)) - WebRtc_Word16 temp1, temp2; - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbGetSizeInfo(inst, &temp1, &temp2)) - CHECK_MINUS_ONE(WebRtcNetEQ_CodecDbGetCodecInfo(inst, 0, &usedCodec)) - - CHECK_MINUS_ONE(WebRtcNetEQ_RecIn(inst, &temp1, 17, 4711)) - CHECK_MINUS_ONE(WebRtcNetEQ_RecOut(inst, &temp1, &temp2)) - CHECK_MINUS_ONE(WebRtcNetEQ_GetRTCPStats(inst, &RTCPstat)); // error here!!! - CHECK_MINUS_ONE(WebRtcNetEQ_GetSpeechTimeStamp(inst, ×tamp)) - WebRtcNetEQOutputType temptype; - CHECK_MINUS_ONE(WebRtcNetEQ_GetSpeechOutputType(inst, &temptype)) - - WebRtc_UWord8 tempFlags; - WebRtc_UWord16 utemp1, utemp2; - CHECK_MINUS_ONE(WebRtcNetEQ_VQmonRecOutStatistics(inst, &utemp1, &utemp2, &tempFlags)) - CHECK_MINUS_ONE(WebRtcNetEQ_VQmonGetRxStatistics(inst, &utemp1, &utemp2)) - - WebRtcNetEQ_AssignSize(&memorySize); - CHECK_ZERO(WebRtcNetEQ_Assign(&inst, malloc(memorySize))) - - /* init with wrong sample frequency */ - CHECK_MINUS_ONE(WebRtcNetEQ_Init(inst, 17)) - - /* init with correct fs */ - CHECK_ZERO(WebRtcNetEQ_Init(inst, 8000)) - - /* GetRecommendedBufferSize with wrong codec */ - usedCodec=kDecoderReservedStart; - ok = WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter , &NetEqBufferMaxPackets, &BufferSizeInBytes); - if((ok!=-1) || ((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNKNOWN_CODEC))){ - printf("WebRtcNetEQ_GetRecommendedBufferSize() did not return proper error code for wrong codec.\n"); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst)); - } - //RESET_ERROR(inst) - - /* GetRecommendedBufferSize with wrong network type */ - usedCodec = kDecoderPCMu; - ok=WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, (enum WebRtcNetEQNetworkType) 4711 , &NetEqBufferMaxPackets, &BufferSizeInBytes); - if ((ok!=-1) || ((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_NETWORK_TYPE))) { - printf("WebRtcNetEQ_GetRecommendedBufferSize() did not return proper error code for wrong network type.\n"); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst)); - //RESET_ERROR(inst) - } - CHECK_ZERO(WebRtcNetEQ_GetRecommendedBufferSize(inst, &usedCodec, 1, kTCPLargeJitter , &NetEqBufferMaxPackets, &BufferSizeInBytes)) - - /* try to do RecIn before assigning the packet buffer */ -/* makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, 17,4711, 1235412312); - makeDTMFpayload(&rtp_data[12], 1, 1, 10, 100); - ok = WebRtcNetEQ_RecIn(inst, (short *) rtp_data, 12+4, 4711); - printf("return value = %d; error code = %d\n", ok, WebRtcNetEQ_GetErrorCode(inst));*/ - - /* check all limits of WebRtcNetEQ_AssignBuffer */ - ok=WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, 149<<1); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong sizeinbytes\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NULL, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for NULL memory pointer\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, 1, NetEqPacketBuffer, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong MaxNoOfPackets\n"); - } - ok=WebRtcNetEQ_AssignBuffer(inst, 601, NetEqPacketBuffer, BufferSizeInBytes); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-PBUFFER_INIT_ERROR))) { - printf("WebRtcNetEQ_AssignBuffer() did not return proper error code for wrong MaxNoOfPackets\n"); - } - - /* do correct assignbuffer */ - CHECK_ZERO(WebRtcNetEQ_AssignBuffer(inst, NetEqBufferMaxPackets, NetEqPacketBuffer, BufferSizeInBytes)) - - ok=WebRtcNetEQ_SetExtraDelay(inst, -1); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_DELAYVALUE))) { - printf("WebRtcNetEQ_SetExtraDelay() did not return proper error code for too small delay\n"); - } - ok=WebRtcNetEQ_SetExtraDelay(inst, 1001); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_DELAYVALUE))) { - printf("WebRtcNetEQ_SetExtraDelay() did not return proper error code for too large delay\n"); - } - - ok=WebRtcNetEQ_SetPlayoutMode(inst,(enum WebRtcNetEQPlayoutMode) 4711); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-FAULTY_PLAYOUTMODE))) { - printf("WebRtcNetEQ_SetPlayoutMode() did not return proper error code for wrong mode\n"); - } - - /* number of codecs should return zero before adding any codecs */ - WebRtcNetEQ_CodecDbGetSizeInfo(inst, &temp1, &temp2); - if(temp1!=0) - printf("WebRtcNetEQ_CodecDbGetSizeInfo() return non-zero number of codecs in DB before adding any codecs\n"); - - /* get info from empty database */ - ok=WebRtcNetEQ_CodecDbGetCodecInfo(inst, 17, &usedCodec); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_NOT_EXIST1))) { - printf("WebRtcNetEQ_CodecDbGetCodecInfo() did not return proper error code for out-of-range entry number\n"); - } - - /* remove codec from empty database */ - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderPCMa); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_NOT_EXIST4))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that has not been added\n"); - } - - /* add codec with unsupported fs */ -#ifdef CODEC_PCM16B -#ifndef NETEQ_48KHZ_WIDEBAND - SET_CODEC_PAR(codecInst,kDecoderPCM16Bswb48kHz,77,NULL,48000); - SET_PCM16B_SWB48_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_FS))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding codec with unsupported sample freq\n"); - } -#else - printf("Could not test adding codec with unsupported sample frequency since NetEQ is compiled with 48kHz support.\n"); -#endif -#else - printf("Could not test adding codec with unsupported sample frequency since NetEQ is compiled without PCM16B support.\n"); -#endif - - /* add two codecs with identical payload types */ - SET_CODEC_PAR(codecInst,kDecoderPCMa,17,NULL,8000); - SET_PCMA_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - - SET_CODEC_PAR(codecInst,kDecoderPCMu,17,NULL,8000); - SET_PCMU_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding two codecs with identical payload types\n"); - } - - /* try adding several payload types for CNG codecs */ - SET_CODEC_PAR(codecInst,kDecoderCNG,105,NULL,16000); - SET_CNG_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - SET_CODEC_PAR(codecInst,kDecoderCNG,13,NULL,8000); - SET_CNG_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) - - /* try adding a speech codec over a CNG codec */ - SET_CODEC_PAR(codecInst,kDecoderISAC,105,NULL,16000); /* same as WB CNG above */ - SET_ISAC_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding a speech codec over a CNG codec\n"); - } - - /* try adding a CNG codec over a speech codec */ - SET_CODEC_PAR(codecInst,kDecoderCNG,17,NULL,32000); /* same as PCMU above */ - SET_CNG_FUNCTIONS(codecInst); - ok=WebRtcNetEQ_CodecDbAdd(inst, &codecInst); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_PAYLOAD_TAKEN))) { - printf("WebRtcNetEQ_CodecDbAdd() did not return proper error code when adding a speech codec over a CNG codec\n"); - } - - - /* remove codec out of range */ - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderReservedStart); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_CODEC))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that is out of range\n"); - } - ok=WebRtcNetEQ_CodecDbRemove(inst,kDecoderReservedEnd); - if((ok!=-1)||((ok==-1)&&(WebRtcNetEQ_GetErrorCode(inst)!=-CODEC_DB_UNSUPPORTED_CODEC))) { - printf("WebRtcNetEQ_CodecDbRemove() did not return proper error code when removing codec that is out of range\n"); - } - - /*SET_CODEC_PAR(codecInst,kDecoderEG711a,NETEQ_CODEC_EG711A_PT,NetEqiPCMAState,8000); - SET_IPCMA_FUNCTIONS(codecInst); - CHECK_ZERO(WebRtcNetEQ_CodecDbAdd(inst, &codecInst)) -*/ - free(inst); - - return(0); - -} diff --git a/modules/audio_coding/NetEQ/main/test/PayloadTypes.h b/modules/audio_coding/NetEQ/main/test/PayloadTypes.h deleted file mode 100644 index b11a344ab..000000000 --- a/modules/audio_coding/NetEQ/main/test/PayloadTypes.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* PayloadTypes.h */ -/* Used by NetEqRTPplay application */ - -/* RTP defined codepoints */ -#define NETEQ_CODEC_PCMU_PT 0 -#define NETEQ_CODEC_GSMFR_PT 3 -#define NETEQ_CODEC_G723_PT 4 -#define NETEQ_CODEC_DVI4_PT 125 // 8 kHz version -//#define NETEQ_CODEC_DVI4_16_PT 6 // 16 kHz version -#define NETEQ_CODEC_PCMA_PT 8 -#define NETEQ_CODEC_G722_PT 9 -#define NETEQ_CODEC_CN_PT 13 -//#define NETEQ_CODEC_G728_PT 15 -//#define NETEQ_CODEC_DVI4_11_PT 16 // 11.025 kHz version -//#define NETEQ_CODEC_DVI4_22_PT 17 // 22.050 kHz version -#define NETEQ_CODEC_G729_PT 18 - -/* Dynamic RTP codepoints as defined in VoiceEngine (file VEAPI.cpp) */ -#define NETEQ_CODEC_IPCMWB_PT 97 -#define NETEQ_CODEC_SPEEX8_PT 98 -#define NETEQ_CODEC_SPEEX16_PT 99 -#define NETEQ_CODEC_EG711U_PT 100 -#define NETEQ_CODEC_EG711A_PT 101 -#define NETEQ_CODEC_ILBC_PT 102 -#define NETEQ_CODEC_ISAC_PT 103 -#define NETEQ_CODEC_ISACLC_PT 119 -#define NETEQ_CODEC_ISACSWB_PT 104 -#define NETEQ_CODEC_AVT_PT 106 -#define NETEQ_CODEC_G722_1_16_PT 108 -#define NETEQ_CODEC_G722_1_24_PT 109 -#define NETEQ_CODEC_G722_1_32_PT 110 -#define NETEQ_CODEC_SC3_PT 111 -#define NETEQ_CODEC_AMR_PT 112 -#define NETEQ_CODEC_GSMEFR_PT 113 -//#define NETEQ_CODEC_ILBCRCU_PT 114 -#define NETEQ_CODEC_G726_16_PT 115 -#define NETEQ_CODEC_G726_24_PT 116 -#define NETEQ_CODEC_G726_32_PT 121 -#define NETEQ_CODEC_RED_PT 117 -#define NETEQ_CODEC_G726_40_PT 118 -//#define NETEQ_CODEC_ENERGY_PT 120 -#define NETEQ_CODEC_CN_WB_PT 105 -#define NETEQ_CODEC_CN_SWB_PT 126 -#define NETEQ_CODEC_G729_1_PT 107 -#define NETEQ_CODEC_G729D_PT 123 -#define NETEQ_CODEC_MELPE_PT 124 - -/* Extra dynamic codepoints */ -#define NETEQ_CODEC_AMRWB_PT 120 -#define NETEQ_CODEC_PCM16B_PT 93 -#define NETEQ_CODEC_PCM16B_WB_PT 94 -#define NETEQ_CODEC_PCM16B_SWB32KHZ_PT 95 -#define NETEQ_CODEC_PCM16B_SWB48KHZ_PT 96 -#define NETEQ_CODEC_MPEG4AAC_PT 122 - - -/* Not default in VoiceEngine */ -#define NETEQ_CODEC_G722_1C_24_PT 84 -#define NETEQ_CODEC_G722_1C_32_PT 85 -#define NETEQ_CODEC_G722_1C_48_PT 86 - -#define NETEQ_CODEC_SILK_8_PT 80 -#define NETEQ_CODEC_SILK_12_PT 81 -#define NETEQ_CODEC_SILK_16_PT 82 -#define NETEQ_CODEC_SILK_24_PT 83 - diff --git a/modules/audio_coding/NetEQ/main/test/RTPanalyze.cc b/modules/audio_coding/NetEQ/main/test/RTPanalyze.cc deleted file mode 100644 index e8d843a96..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPanalyze.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "NETEQTEST_RTPpacket.h" - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - - -int main(int argc, char* argv[]) -{ - FILE *inFile=fopen(argv[1],"rb"); - if (!inFile) - { - printf("Cannot open input file %s\n", argv[1]); - return(-1); - } - printf("Input file: %s\n",argv[1]); - - FILE *outFile=fopen(argv[2],"wt"); - if (!outFile) - { - printf("Cannot open output file %s\n", argv[2]); - return(-1); - } - printf("Output file: %s\n\n",argv[2]); - - // print file header - fprintf(outFile, "SeqNo TimeStamp SendTime Size\n"); - - - // read file header - char firstline[FIRSTLINELEN]; - fgets(firstline, FIRSTLINELEN, inFile); - fread(firstline, 4+4+4+2+2, 1, inFile); // start_sec + start_usec + source + port + padding - - NETEQTEST_RTPpacket packet; - - while (packet.readFromFile(inFile) >= 0) - { - // write packet data to file - fprintf(outFile, "%5hu %10lu %10lu %5hi\n", - packet.sequenceNumber(), packet.timeStamp(), packet.time(), packet.dataLen()); - } - - fclose(inFile); - fclose(outFile); - - return 0; -} diff --git a/modules/audio_coding/NetEQ/main/test/RTPcat.cc b/modules/audio_coding/NetEQ/main/test/RTPcat.cc deleted file mode 100644 index fe4223e6d..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPcat.cc +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "NETEQTEST_RTPpacket.h" - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - -int main(int argc, char* argv[]) -{ - if (argc < 3) { - printf("Usage: RTPcat in1.rtp int2.rtp [...] out.rtp\n"); - exit(1); - } - - FILE *inFile = fopen(argv[1], "rb"); - if (!inFile) { - printf("Cannot open input file %s\n", argv[1]); - return (-1); - } - - FILE *outFile = fopen(argv[argc - 1], "wb"); // last parameter is output file - if (!outFile) { - printf("Cannot open output file %s\n", argv[argc - 1]); - return (-1); - } - printf("Output RTP file: %s\n\n", argv[argc - 1]); - - // read file header and write directly to output file - char firstline[FIRSTLINELEN]; - fgets(firstline, FIRSTLINELEN, inFile); - fputs(firstline, outFile); - fread(firstline, 4 + 4 + 4 + 2 + 2, 1, inFile); // start_sec + start_usec + source + port + padding - fwrite(firstline, 4 + 4 + 4 + 2 + 2, 1, outFile); - - // close input file and re-open it later (easier to write the loop below) - fclose(inFile); - - for (int i = 1; i < argc - 1; i++) { - - inFile = fopen(argv[i], "rb"); - if (!inFile) { - printf("Cannot open input file %s\n", argv[i]); - return (-1); - } - printf("Input RTP file: %s\n", argv[i]); - - // skip file header - fgets(firstline, FIRSTLINELEN, inFile); - fread(firstline, 4 + 4 + 4 + 2 + 2, 1, inFile); // start_sec + start_usec + source + port + padding - - NETEQTEST_RTPpacket packet; - int packLen = packet.readFromFile(inFile); - if (packLen < 0) { - exit(1); - } - - while (packLen >= 0) { - - packet.writeToFile(outFile); - - packLen = packet.readFromFile(inFile); - - } - - fclose(inFile); - - } - - fclose(outFile); - - return 0; -} diff --git a/modules/audio_coding/NetEQ/main/test/RTPchange.cc b/modules/audio_coding/NetEQ/main/test/RTPchange.cc deleted file mode 100644 index 5f693b8cb..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPchange.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "NETEQTEST_RTPpacket.h" - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - -bool pktCmp (NETEQTEST_RTPpacket *a, NETEQTEST_RTPpacket *b) -{ - return (a->time() < b->time()); -} - - -int main(int argc, char* argv[]) -{ - FILE *inFile=fopen(argv[1],"rb"); - if (!inFile) - { - printf("Cannot open input file %s\n", argv[1]); - return(-1); - } - printf("Input RTP file: %s\n",argv[1]); - - FILE *statFile=fopen(argv[2],"rt"); - if (!statFile) - { - printf("Cannot open timing file %s\n", argv[2]); - return(-1); - } - printf("Timing file: %s\n",argv[2]); - - FILE *outFile=fopen(argv[3],"wb"); - if (!outFile) - { - printf("Cannot open output file %s\n", argv[3]); - return(-1); - } - printf("Output RTP file: %s\n\n",argv[3]); - - // read all statistics and insert into map - // read first line - char tempStr[100]; - fgets(tempStr, 100, statFile); - - // define map - std::map, WebRtc_UWord32> - packetStats; - WebRtc_UWord16 seqNo; - WebRtc_UWord32 ts; - WebRtc_UWord32 sendTime; - - while(fscanf(statFile, "%u %u %u %*i\n", &seqNo, &ts, &sendTime) == 3) - { - std::pair tempPair = - std::pair(seqNo, ts); - - packetStats[tempPair] = sendTime; - } - - fclose(statFile); - - // read file header and write directly to output file - char firstline[FIRSTLINELEN]; - fgets(firstline, FIRSTLINELEN, inFile); - fputs(firstline, outFile); - fread(firstline, 4+4+4+2+2, 1, inFile); // start_sec + start_usec + source + port + padding - fwrite(firstline, 4+4+4+2+2, 1, outFile); - - std::vector packetVec; - int i = 0; - - while (1) - { - // insert in vector - NETEQTEST_RTPpacket *newPacket = new NETEQTEST_RTPpacket(); - if (newPacket->readFromFile(inFile) < 0) - { - // end of file - break; - } - - // look for new send time in statistics vector - std::pair tempPair = - std::pair(newPacket->sequenceNumber(), newPacket->timeStamp()); - - WebRtc_UWord32 newSendTime = packetStats[tempPair]; - if (newSendTime >= 0) - { - newPacket->setTime(newSendTime); // set new send time - packetVec.push_back(newPacket); // insert in vector - } - else - { - // negative value represents lost packet - // don't insert, but delete packet object - delete newPacket; - } - - } - - // sort the vector according to send times - std::sort(packetVec.begin(), packetVec.end(), pktCmp); - - std::vector::iterator it; - for (it = packetVec.begin(); it != packetVec.end(); it++) - { - // write to out file - if ((*it)->writeToFile(outFile) < 0) - { - printf("Error writing to file\n"); - return(-1); - } - - // delete packet - delete *it; - } - - fclose(inFile); - fclose(outFile); - - return 0; -} diff --git a/modules/audio_coding/NetEQ/main/test/RTPencode.cc b/modules/audio_coding/NetEQ/main/test/RTPencode.cc deleted file mode 100644 index b87fae275..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPencode.cc +++ /dev/null @@ -1,1985 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* header includes */ -#include "typedefs.h" -#include "stdio.h" -#include "webrtc_neteq.h" // needed for enum WebRtcNetEQDecoder -#include -#include -#include - -#ifdef WIN32 -#include -#endif -#ifdef WEBRTC_LINUX -#include -#endif - - -/************************/ -/* Define payload types */ -/************************/ - -#include "PayloadTypes.h" - - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define STOPSENDTIME 3000 -#define RESTARTSENDTIME 0 //162500 -#define FIRSTLINELEN 40 -#define CHECK_NOT_NULL(a) if((a)==0){printf("\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} - -//#define MULTIPLE_SAME_TIMESTAMP -#define REPEAT_PACKET_DISTANCE 17 -#define REPEAT_PACKET_COUNT 1 // number of extra packets to send - -//#define INSERT_OLD_PACKETS -#define OLD_PACKET 5 // how many seconds too old should the packet be? - -//#define TIMESTAMP_WRAPAROUND - -//#define RANDOM_DATA -//#define RANDOM_PAYLOAD_DATA -#define RANDOM_SEED 10 - -//#define INSERT_DTMF_PACKETS -//#define NO_DTMF_OVERDUB -#define DTMF_PACKET_INTERVAL 2000 -#define DTMF_DURATION 500 - -#define STEREO_MODE_FRAME 0 -#define STEREO_MODE_SAMPLE_1 1 //1 octet per sample -#define STEREO_MODE_SAMPLE_2 2 //2 octets per sample - -/*************************/ -/* Function declarations */ -/*************************/ - -void NetEQTest_GetCodec_and_PT(char * name, enum WebRtcNetEQDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed); -int NetEQTest_init_coders(enum WebRtcNetEQDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels); -void defineCodecs(enum WebRtcNetEQDecoder *usedCodec, int *noOfCodecs ); -int NetEQTest_free_coders(enum WebRtcNetEQDecoder coder, int numChannels); -int NetEQTest_encode(int coder, WebRtc_Word16 *indata, int frameLen, unsigned char * encoded,int sampleRate , int * vad, int useVAD, int bitrate, int numChannels); -void makeRTPheader(unsigned char* rtp_data, int payloadType, int seqNo, WebRtc_UWord32 timestamp, WebRtc_UWord32 ssrc); -int makeRedundantHeader(unsigned char* rtp_data, int *payloadType, int numPayloads, WebRtc_UWord32 *timestamp, WebRtc_UWord16 *blockLen, - int seqNo, WebRtc_UWord32 ssrc); -int makeDTMFpayload(unsigned char* payload_data, int Event, int End, int Volume, int Duration); -void stereoDeInterleave(WebRtc_Word16* audioSamples, int numSamples); -void stereoInterleave(unsigned char* data, int dataLen, int stride); - -/********************/ -/* Global variables */ -/********************/ - -FILE *in_file; -FILE *out_file; -FILE *dat_file; - - - - -/*********************/ -/* Codec definitions */ -/*********************/ - -#include "webrtc_vad.h" - -#if ((defined CODEC_PCM16B)||(defined NETEQ_ARBITRARY_CODEC)) - #include "pcm16b.h" -#endif -#ifdef CODEC_G711 - #include "g711_interface.h" -#endif -#ifdef CODEC_G729 - #include "G729Interface.h" -#endif -#ifdef CODEC_G729_1 - #include "G729_1Interface.h" -#endif -#ifdef CODEC_AMR - #include "AMRInterface.h" - #include "AMRCreation.h" -#endif -#ifdef CODEC_AMRWB - #include "AMRWBInterface.h" - #include "AMRWBCreation.h" -#endif -#ifdef CODEC_ILBC - #include "ilbc.h" -#endif -#if (defined CODEC_ISAC || defined CODEC_ISAC_SWB) - #include "isac.h" -#endif -#ifdef NETEQ_ISACFIX_CODEC - #include "isacfix.h" - #ifdef CODEC_ISAC - #error Cannot have both ISAC and ISACfix defined. Please de-select one in the beginning of RTPencode.cpp - #endif -#endif -#ifdef CODEC_G722 - #include "g722_interface.h" -#endif -#ifdef CODEC_G722_1_24 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1_32 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1_16 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_24 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_32 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G722_1C_48 - #include "G722_1Interface.h" -#endif -#ifdef CODEC_G726 - #include "G726Creation.h" - #include "G726Interface.h" -#endif -#ifdef CODEC_GSMFR - #include "GSMFRInterface.h" - #include "GSMFRCreation.h" -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - #include "webrtc_cng.h" -#endif -#if ((defined CODEC_SPEEX_8)||(defined CODEC_SPEEX_16)) - #include "SpeexInterface.h" -#endif - - -/***********************************/ -/* Global codec instance variables */ -/***********************************/ - -WebRtcVadInst *VAD_inst[2]; - -#ifdef CODEC_G722 - G722EncInst *g722EncState[2]; -#endif - -#ifdef CODEC_G722_1_24 - G722_1_24_encinst_t *G722_1_24enc_inst[2]; -#endif -#ifdef CODEC_G722_1_32 - G722_1_32_encinst_t *G722_1_32enc_inst[2]; -#endif -#ifdef CODEC_G722_1_16 - G722_1_16_encinst_t *G722_1_16enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_24 - G722_1C_24_encinst_t *G722_1C_24enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_32 - G722_1C_32_encinst_t *G722_1C_32enc_inst[2]; -#endif -#ifdef CODEC_G722_1C_48 - G722_1C_48_encinst_t *G722_1C_48enc_inst[2]; -#endif -#ifdef CODEC_G726 - G726_encinst_t *G726enc_inst[2]; -#endif -#ifdef CODEC_G729 - G729_encinst_t *G729enc_inst[2]; -#endif -#ifdef CODEC_G729_1 - G729_1_inst_t *G729_1_inst[2]; -#endif -#ifdef CODEC_AMR - AMR_encinst_t *AMRenc_inst[2]; - WebRtc_Word16 AMR_bitrate; -#endif -#ifdef CODEC_AMRWB - AMRWB_encinst_t *AMRWBenc_inst[2]; - WebRtc_Word16 AMRWB_bitrate; -#endif -#ifdef CODEC_ILBC - iLBC_encinst_t *iLBCenc_inst[2]; -#endif -#ifdef CODEC_ISAC - ISACStruct *ISAC_inst[2]; -#endif -#ifdef NETEQ_ISACFIX_CODEC - ISACFIX_MainStruct *ISAC_inst[2]; -#endif -#ifdef CODEC_ISAC_SWB - ISACStruct *ISACSWB_inst[2]; -#endif -#ifdef CODEC_GSMFR - GSMFR_encinst_t *GSMFRenc_inst[2]; -#endif -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - CNG_enc_inst *CNGenc_inst[2]; -#endif -#ifdef CODEC_SPEEX_8 - SPEEX_encinst_t *SPEEX8enc_inst[2]; -#endif -#ifdef CODEC_SPEEX_16 - SPEEX_encinst_t *SPEEX16enc_inst[2]; -#endif -#ifdef CODEC_G711 - void *G711state[2]={NULL, NULL}; -#endif - - -int main(int argc, char* argv[]) -{ - int packet_size, fs; - enum WebRtcNetEQDecoder usedCodec; - int payloadType; - int bitrate = 0; - int useVAD, vad; - int useRed=0; - int len, enc_len; - WebRtc_Word16 org_data[4000]; - unsigned char rtp_data[8000]; - WebRtc_Word16 seqNo=0xFFF; - WebRtc_UWord32 ssrc=1235412312; - WebRtc_UWord32 timestamp=0xAC1245; - WebRtc_UWord16 length, plen; - WebRtc_UWord32 offset; - double sendtime = 0; - int red_PT[2]; - WebRtc_UWord32 red_TS[2]; - WebRtc_UWord16 red_len[2]; - int RTPheaderLen=12; - unsigned char red_data[8000]; -#ifdef INSERT_OLD_PACKETS - WebRtc_UWord16 old_length, old_plen; - int old_enc_len; - int first_old_packet=1; - unsigned char old_rtp_data[8000]; - int packet_age=0; -#endif -#ifdef INSERT_DTMF_PACKETS - int NTone = 1; - int DTMFfirst = 1; - WebRtc_UWord32 DTMFtimestamp; - bool dtmfSent = false; -#endif - bool usingStereo = false; - int stereoMode; - int numChannels = 1; - - /* check number of parameters */ - if ((argc != 6) && (argc != 7)) { - /* print help text and exit */ - printf("Application to encode speech into an RTP stream.\n"); - printf("The program reads a PCM file and encodes is using the specified codec.\n"); - printf("The coded speech is packetized in RTP packest and written to the output file.\n"); - printf("The format of the RTP stream file is simlilar to that of rtpplay,\n"); - printf("but with the receive time euqal to 0 for all packets.\n"); - printf("Usage:\n\n"); - printf("%s PCMfile RTPfile frameLen codec useVAD bitrate\n", argv[0]); - printf("where:\n"); - - printf("PCMfile : PCM speech input file\n\n"); - - printf("RTPfile : RTP stream output file\n\n"); - - printf("frameLen : 80...960... Number of samples per packet (limit depends on codec)\n\n"); - - printf("codecName\n"); -#ifdef CODEC_PCM16B - printf(" : pcm16b 16 bit PCM (8kHz)\n"); -#endif -#ifdef CODEC_PCM16B_WB - printf(" : pcm16b_wb 16 bit PCM (16kHz)\n"); -#endif -#ifdef CODEC_PCM16B_32KHZ - printf(" : pcm16b_swb32 16 bit PCM (32kHz)\n"); -#endif -#ifdef CODEC_PCM16B_48KHZ - printf(" : pcm16b_swb48 16 bit PCM (48kHz)\n"); -#endif -#ifdef CODEC_G711 - printf(" : pcma g711 A-law (8kHz)\n"); -#endif -#ifdef CODEC_G711 - printf(" : pcmu g711 u-law (8kHz)\n"); -#endif -#ifdef CODEC_G729 - printf(" : g729 G729 (8kHz and 8kbps) CELP (One-Three frame(s)/packet)\n"); -#endif -#ifdef CODEC_G729_1 - printf(" : g729.1 G729.1 (16kHz) variable rate (8--32 kbps)\n"); -#endif -#ifdef CODEC_G722_1_16 - printf(" : g722.1_16 G722.1 coder (16kHz) (g722.1 with 16kbps)\n"); -#endif -#ifdef CODEC_G722_1_24 - printf(" : g722.1_24 G722.1 coder (16kHz) (the 24kbps version)\n"); -#endif -#ifdef CODEC_G722_1_32 - printf(" : g722.1_32 G722.1 coder (16kHz) (the 32kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_24 - printf(" : g722.1C_24 G722.1 C coder (32kHz) (the 24kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_32 - printf(" : g722.1C_32 G722.1 C coder (32kHz) (the 32kbps version)\n"); -#endif -#ifdef CODEC_G722_1C_48 - printf(" : g722.1C_48 G722.1 C coder (32kHz) (the 48kbps)\n"); -#endif - -#ifdef CODEC_G726 - printf(" : g726_16 G726 coder (8kHz) 16kbps\n"); - printf(" : g726_24 G726 coder (8kHz) 24kbps\n"); - printf(" : g726_32 G726 coder (8kHz) 32kbps\n"); - printf(" : g726_40 G726 coder (8kHz) 40kbps\n"); -#endif -#ifdef CODEC_AMR - printf(" : AMRXk Adaptive Multi Rate CELP codec (8kHz)\n"); - printf(" X = 4.75, 5.15, 5.9, 6.7, 7.4, 7.95, 10.2 or 12.2\n"); -#endif -#ifdef CODEC_AMRWB - printf(" : AMRwbXk Adaptive Multi Rate Wideband CELP codec (16kHz)\n"); - printf(" X = 7, 9, 12, 14, 16, 18, 20, 23 or 24\n"); -#endif -#ifdef CODEC_ILBC - printf(" : ilbc iLBC codec (8kHz and 13.8kbps)\n"); -#endif -#ifdef CODEC_ISAC - printf(" : isac iSAC (16kHz and 32.0 kbps). To set rate specify a rate parameter as last parameter\n"); -#endif -#ifdef CODEC_ISAC_SWB - printf(" : isacswb iSAC SWB (32kHz and 32.0-52.0 kbps). To set rate specify a rate parameter as last parameter\n"); -#endif -#ifdef CODEC_GSMFR - printf(" : gsmfr GSM FR codec (8kHz and 13kbps)\n"); -#endif -#ifdef CODEC_G722 - printf(" : g722 g722 coder (16kHz) (the 64kbps version)\n"); -#endif -#ifdef CODEC_SPEEX_8 - printf(" : speex8 speex coder (8 kHz)\n"); -#endif -#ifdef CODEC_SPEEX_16 - printf(" : speex16 speex coder (16 kHz)\n"); -#endif -#ifdef CODEC_RED -#ifdef CODEC_G711 - printf(" : red_pcm Redundancy RTP packet with 2*G711A frames\n"); -#endif -#ifdef CODEC_ISAC - printf(" : red_isac Redundancy RTP packet with 2*iSAC frames\n"); -#endif -#endif - printf("\n"); - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - printf("useVAD : 0 Voice Activity Detection is switched off\n"); - printf(" : 1 Voice Activity Detection is switched on\n\n"); -#else - printf("useVAD : 0 Voice Activity Detection switched off (on not supported)\n\n"); -#endif - printf("bitrate : Codec bitrate in bps (only applies to vbr codecs)\n\n"); - - return(0); - } - - in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - out_file=fopen(argv[2],"wb"); - CHECK_NOT_NULL(out_file); - printf("Output file: %s\n\n",argv[2]); - packet_size=atoi(argv[3]); - CHECK_NOT_NULL(packet_size); - printf("Packet size: %i\n",packet_size); - - // check for stereo - if(argv[4][strlen(argv[4])-1] == '*') { - // use stereo - usingStereo = true; - numChannels = 2; - argv[4][strlen(argv[4])-1] = '\0'; - } - - NetEQTest_GetCodec_and_PT(argv[4], &usedCodec, &payloadType, packet_size, &fs, &bitrate, &useRed); - - if(useRed) { - RTPheaderLen = 12 + 4 + 1; /* standard RTP = 12; 4 bytes per redundant payload, except last one which is 1 byte */ - } - - useVAD=atoi(argv[5]); -#if !(defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - if (useVAD!=0) { - printf("Error: this simulation does not support VAD/DTX/CNG\n"); - } -#endif - - // check stereo type - if(usingStereo) - { - switch(usedCodec) - { - // sample based codecs - case kDecoderPCMu: - case kDecoderPCMa: - case kDecoderG722: - { - // 1 octet per sample - stereoMode = STEREO_MODE_SAMPLE_1; - break; - } - case kDecoderPCM16B: - case kDecoderPCM16Bwb: - case kDecoderPCM16Bswb32kHz: - case kDecoderPCM16Bswb48kHz: - { - // 2 octets per sample - stereoMode = STEREO_MODE_SAMPLE_2; - break; - } - - // fixed-rate frame codecs (with internal VAD) - case kDecoderG729: - { - if(useVAD) { - printf("Cannot use codec-internal VAD and stereo\n"); - exit(0); - } - // break intentionally omitted - } - case kDecoderG722_1_16: - case kDecoderG722_1_24: - case kDecoderG722_1_32: - case kDecoderG722_1C_24: - case kDecoderG722_1C_32: - case kDecoderG722_1C_48: - { - stereoMode = STEREO_MODE_FRAME; - break; - } - default: - { - printf("Cannot use codec %s as stereo codec\n", argv[4]); - exit(0); - } - } - } - - if ((usedCodec == kDecoderISAC) || (usedCodec == kDecoderISACswb)) - { - if (argc != 7) - { - if (usedCodec == kDecoderISAC) - { - bitrate = 32000; - printf( - "Running iSAC at default bitrate of 32000 bps (to specify explicitly add the bps as last parameter)\n"); - } - else // (usedCodec==kDecoderISACswb) - { - bitrate = 56000; - printf( - "Running iSAC at default bitrate of 56000 bps (to specify explicitly add the bps as last parameter)\n"); - } - } - else - { - bitrate = atoi(argv[6]); - if (usedCodec == kDecoderISAC) - { - if ((bitrate < 10000) || (bitrate > 32000)) - { - printf( - "Error: iSAC bitrate must be between 10000 and 32000 bps (%i is invalid)\n", - bitrate); - exit(0); - } - printf("Running iSAC at bitrate of %i bps\n", bitrate); - } - else // (usedCodec==kDecoderISACswb) - { - if ((bitrate < 32000) || (bitrate > 56000)) - { - printf( - "Error: iSAC SWB bitrate must be between 32000 and 56000 bps (%i is invalid)\n", - bitrate); - exit(0); - } - } - } - } - else - { - if (argc == 7) - { - printf( - "Error: Bitrate parameter can only be specified for iSAC, G.723, and G.729.1\n"); - exit(0); - } - } - - if(useRed) { - printf("Redundancy engaged. "); - } - printf("Used codec: %i\n",usedCodec); - printf("Payload type: %i\n",payloadType); - - NetEQTest_init_coders(usedCodec, packet_size, bitrate, fs, useVAD, numChannels); - - /* write file header */ - //fprintf(out_file, "#!RTPencode%s\n", "1.0"); - fprintf(out_file, "#!rtpplay%s \n", "1.0"); // this is the string that rtpplay needs - WebRtc_UWord32 dummy_variable = 0; // should be converted to network endian format, but does not matter when 0 - fwrite(&dummy_variable, 4, 1, out_file); - fwrite(&dummy_variable, 4, 1, out_file); - fwrite(&dummy_variable, 4, 1, out_file); - fwrite(&dummy_variable, 2, 1, out_file); - fwrite(&dummy_variable, 2, 1, out_file); - -#ifdef TIMESTAMP_WRAPAROUND - timestamp = 0xFFFFFFFF - fs*10; /* should give wrap-around in 10 seconds */ -#endif -#if defined(RANDOM_DATA) | defined(RANDOM_PAYLOAD_DATA) - srand(RANDOM_SEED); -#endif - - /* if redundancy is used, the first redundant payload is zero length */ - red_len[0] = 0; - - /* read first frame */ - len=fread(org_data,2,packet_size * numChannels,in_file) / numChannels; - - /* de-interleave if stereo */ - if ( usingStereo ) - { - stereoDeInterleave(org_data, len * numChannels); - } - - while (len==packet_size) { - -#ifdef INSERT_DTMF_PACKETS - dtmfSent = false; - - if ( sendtime >= NTone * DTMF_PACKET_INTERVAL ) { - if ( sendtime < NTone * DTMF_PACKET_INTERVAL + DTMF_DURATION ) { - // tone has not ended - if (DTMFfirst==1) { - DTMFtimestamp = timestamp; // save this timestamp - DTMFfirst=0; - } - makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, seqNo,DTMFtimestamp, ssrc); - enc_len = makeDTMFpayload(&rtp_data[12], NTone % 12, 0, 4, (int) (sendtime - NTone * DTMF_PACKET_INTERVAL)*(fs/1000) + len); - } - else { - // tone has ended - makeRTPheader(rtp_data, NETEQ_CODEC_AVT_PT, seqNo,DTMFtimestamp, ssrc); - enc_len = makeDTMFpayload(&rtp_data[12], NTone % 12, 1, 4, DTMF_DURATION*(fs/1000)); - NTone++; - DTMFfirst=1; - } - - /* write RTP packet to file */ - length = htons(12 + enc_len + 8); - plen = htons(12 + enc_len); - offset = (WebRtc_UWord32) sendtime; //(timestamp/(fs/1000)); - offset = htonl(offset); - fwrite(&length, 2, 1, out_file); - fwrite(&plen, 2, 1, out_file); - fwrite(&offset, 4, 1, out_file); - fwrite(rtp_data, 12 + enc_len, 1, out_file); - - dtmfSent = true; - } -#endif - -#ifdef NO_DTMF_OVERDUB - /* If DTMF is sent, we should not send any speech packets during the same time */ - if (dtmfSent) { - enc_len = 0; - } - else { -#endif - /* encode frame */ - enc_len=NetEQTest_encode(usedCodec, org_data, packet_size, &rtp_data[12] ,fs,&vad, useVAD, bitrate, numChannels); - if (enc_len==-1) { - printf("Error encoding frame\n"); - exit(0); - } - - if ( usingStereo && - stereoMode != STEREO_MODE_FRAME && - vad == 1 ) - { - // interleave the encoded payload for sample-based codecs (not for CNG) - stereoInterleave(&rtp_data[12], enc_len, stereoMode); - } -#ifdef NO_DTMF_OVERDUB - } -#endif - - if (enc_len > 0 && (sendtime <= STOPSENDTIME || sendtime > RESTARTSENDTIME)) { - if(useRed) { - if(red_len[0] > 0) { - memmove(&rtp_data[RTPheaderLen+red_len[0]], &rtp_data[12], enc_len); - memcpy(&rtp_data[RTPheaderLen], red_data, red_len[0]); - - red_len[1] = enc_len; - red_TS[1] = timestamp; - if(vad) - red_PT[1] = payloadType; - else - red_PT[1] = NETEQ_CODEC_CN_PT; - - makeRedundantHeader(rtp_data, red_PT, 2, red_TS, red_len, seqNo++, ssrc); - - - enc_len += red_len[0] + RTPheaderLen - 12; - } - else { // do not use redundancy payload for this packet, i.e., only last payload - memmove(&rtp_data[RTPheaderLen-4], &rtp_data[12], enc_len); - //memcpy(&rtp_data[RTPheaderLen], red_data, red_len[0]); - - red_len[1] = enc_len; - red_TS[1] = timestamp; - if(vad) - red_PT[1] = payloadType; - else - red_PT[1] = NETEQ_CODEC_CN_PT; - - makeRedundantHeader(rtp_data, red_PT, 2, red_TS, red_len, seqNo++, ssrc); - - - enc_len += red_len[0] + RTPheaderLen - 4 - 12; // 4 is length of redundancy header (not used) - } - } - else { - - /* make RTP header */ - if (vad) // regular speech data - makeRTPheader(rtp_data, payloadType, seqNo++,timestamp, ssrc); - else // CNG data - makeRTPheader(rtp_data, NETEQ_CODEC_CN_PT, seqNo++,timestamp, ssrc); - - } -#ifdef MULTIPLE_SAME_TIMESTAMP - int mult_pack=0; - do { -#endif //MULTIPLE_SAME_TIMESTAMP - /* write RTP packet to file */ - length = htons(12 + enc_len + 8); - plen = htons(12 + enc_len); - offset = (WebRtc_UWord32) sendtime; //(timestamp/(fs/1000)); - offset = htonl(offset); - fwrite(&length, 2, 1, out_file); - fwrite(&plen, 2, 1, out_file); - fwrite(&offset, 4, 1, out_file); -#ifdef RANDOM_DATA - for (int k=0; k<12+enc_len; k++) { - rtp_data[k] = rand() + rand(); - } -#endif -#ifdef RANDOM_PAYLOAD_DATA - for (int k=12; k<12+enc_len; k++) { - rtp_data[k] = rand() + rand(); - } -#endif - fwrite(rtp_data, 12 + enc_len, 1, out_file); -#ifdef MULTIPLE_SAME_TIMESTAMP - } while ( (seqNo%REPEAT_PACKET_DISTANCE == 0) && (mult_pack++ < REPEAT_PACKET_COUNT) ); -#endif //MULTIPLE_SAME_TIMESTAMP - -#ifdef INSERT_OLD_PACKETS - if (packet_age >= OLD_PACKET*fs) { - if (!first_old_packet) { - // send the old packet - fwrite(&old_length, 2, 1, out_file); - fwrite(&old_plen, 2, 1, out_file); - fwrite(&offset, 4, 1, out_file); - fwrite(old_rtp_data, 12 + old_enc_len, 1, out_file); - } - // store current packet as old - old_length=length; - old_plen=plen; - memcpy(old_rtp_data,rtp_data,12+enc_len); - old_enc_len=enc_len; - first_old_packet=0; - packet_age=0; - - } - packet_age += packet_size; -#endif - - if(useRed) { - /* move data to redundancy store */ -#ifdef CODEC_ISAC - if(usedCodec==kDecoderISAC) - { - assert(!usingStereo); // Cannot handle stereo yet - red_len[0] = WebRtcIsac_GetRedPayload(ISAC_inst[0], (WebRtc_Word16*)red_data); - } - else - { -#endif - memcpy(red_data, &rtp_data[RTPheaderLen+red_len[0]], enc_len); - red_len[0]=red_len[1]; -#ifdef CODEC_ISAC - } -#endif - red_TS[0]=red_TS[1]; - red_PT[0]=red_PT[1]; - } - - } - - /* read next frame */ - len=fread(org_data,2,packet_size * numChannels,in_file) / numChannels; - /* de-interleave if stereo */ - if ( usingStereo ) - { - stereoDeInterleave(org_data, len * numChannels); - } - - if (payloadType==NETEQ_CODEC_G722_PT) - timestamp+=len>>1; - - sendtime += (double) len/(fs/1000); - } - - NetEQTest_free_coders(usedCodec, numChannels); - fclose(in_file); - fclose(out_file); - printf("Done!\n"); - - return(0); -} - - - - -/****************/ -/* Subfunctions */ -/****************/ - -void NetEQTest_GetCodec_and_PT(char * name, enum WebRtcNetEQDecoder *codec, int *PT, int frameLen, int *fs, int *bitrate, int *useRed) { - - *bitrate = 0; /* Default bitrate setting */ - *useRed = 0; /* Default no redundancy */ - - if(!strcmp(name,"pcmu")){ - *codec=kDecoderPCMu; - *PT=NETEQ_CODEC_PCMU_PT; - *fs=8000; - } - else if(!strcmp(name,"pcma")){ - *codec=kDecoderPCMa; - *PT=NETEQ_CODEC_PCMA_PT; - *fs=8000; - } - else if(!strcmp(name,"pcm16b")){ - *codec=kDecoderPCM16B; - *PT=NETEQ_CODEC_PCM16B_PT; - *fs=8000; - } - else if(!strcmp(name,"pcm16b_wb")){ - *codec=kDecoderPCM16Bwb; - *PT=NETEQ_CODEC_PCM16B_WB_PT; - *fs=16000; - } - else if(!strcmp(name,"pcm16b_swb32")){ - *codec=kDecoderPCM16Bswb32kHz; - *PT=NETEQ_CODEC_PCM16B_SWB32KHZ_PT; - *fs=32000; - } - else if(!strcmp(name,"pcm16b_swb48")){ - *codec=kDecoderPCM16Bswb48kHz; - *PT=NETEQ_CODEC_PCM16B_SWB48KHZ_PT; - *fs=48000; - } - else if(!strcmp(name,"g722")){ - *codec=kDecoderG722; - *PT=NETEQ_CODEC_G722_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1_16")){ - *codec=kDecoderG722_1_16; - *PT=NETEQ_CODEC_G722_1_16_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1_24")){ - *codec=kDecoderG722_1_24; - *PT=NETEQ_CODEC_G722_1_24_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1_32")){ - *codec=kDecoderG722_1_32; - *PT=NETEQ_CODEC_G722_1_32_PT; - *fs=16000; - } - else if(!strcmp(name,"g722.1C_24")){ - *codec=kDecoderG722_1C_24; - *PT=NETEQ_CODEC_G722_1C_24_PT; - *fs=32000; - } - else if(!strcmp(name,"g722.1C_32")){ - *codec=kDecoderG722_1C_32; - *PT=NETEQ_CODEC_G722_1C_32_PT; - *fs=32000; - } - else if(!strcmp(name,"g722.1C_48")){ - *codec=kDecoderG722_1C_48; - *PT=NETEQ_CODEC_G722_1C_48_PT; - *fs=32000; - } - else if(!strcmp(name,"g726_16")){ - *fs=8000; - *codec=kDecoderG726_16; - *PT=NETEQ_CODEC_G726_16_PT; - *bitrate=16; - } - else if(!strcmp(name,"g726_24")){ - *fs=8000; - *codec=kDecoderG726_24; - *PT=NETEQ_CODEC_G726_24_PT; - *bitrate=24; - } - else if(!strcmp(name,"g726_32")){ - *fs=8000; - *codec=kDecoderG726_32; - *PT=NETEQ_CODEC_G726_32_PT; - *bitrate=32; - } - else if(!strcmp(name,"g726_40")){ - *fs=8000; - *codec=kDecoderG726_40; - *PT=NETEQ_CODEC_G726_40_PT; - *bitrate=40; - } - else if((!strcmp(name,"amr4.75k"))||(!strcmp(name,"amr5.15k"))||(!strcmp(name,"amr5.9k"))|| - (!strcmp(name,"amr6.7k"))||(!strcmp(name,"amr7.4k"))||(!strcmp(name,"amr7.95k"))|| - (!strcmp(name,"amr10.2k"))||(!strcmp(name,"amr12.2k"))) { - *fs=8000; - if (!strcmp(name,"amr4.75k")) - *bitrate = 0; - if (!strcmp(name,"amr5.15k")) - *bitrate = 1; - if (!strcmp(name,"amr5.9k")) - *bitrate = 2; - if (!strcmp(name,"amr6.7k")) - *bitrate = 3; - if (!strcmp(name,"amr7.4k")) - *bitrate = 4; - if (!strcmp(name,"amr7.95k")) - *bitrate = 5; - if (!strcmp(name,"amr10.2k")) - *bitrate = 6; - if (!strcmp(name,"amr12.2k")) - *bitrate = 7; - *codec=kDecoderAMR; - *PT=NETEQ_CODEC_AMR_PT; - } - else if((!strcmp(name,"amrwb7k"))||(!strcmp(name,"amrwb9k"))||(!strcmp(name,"amrwb12k"))|| - (!strcmp(name,"amrwb14k"))||(!strcmp(name,"amrwb16k"))||(!strcmp(name,"amrwb18k"))|| - (!strcmp(name,"amrwb20k"))||(!strcmp(name,"amrwb23k"))||(!strcmp(name,"amrwb24k"))) { - *fs=16000; - if (!strcmp(name,"amrwb7k")) - *bitrate = 7000; - if (!strcmp(name,"amrwb9k")) - *bitrate = 9000; - if (!strcmp(name,"amrwb12k")) - *bitrate = 12000; - if (!strcmp(name,"amrwb14k")) - *bitrate = 14000; - if (!strcmp(name,"amrwb16k")) - *bitrate = 16000; - if (!strcmp(name,"amrwb18k")) - *bitrate = 18000; - if (!strcmp(name,"amrwb20k")) - *bitrate = 20000; - if (!strcmp(name,"amrwb23k")) - *bitrate = 23000; - if (!strcmp(name,"amrwb24k")) - *bitrate = 24000; - *codec=kDecoderAMRWB; - *PT=NETEQ_CODEC_AMRWB_PT; - } - else if((!strcmp(name,"ilbc"))&&((frameLen%240==0)||(frameLen%160==0))){ - *fs=8000; - *codec=kDecoderILBC; - *PT=NETEQ_CODEC_ILBC_PT; - } - else if(!strcmp(name,"isac")){ - *fs=16000; - *codec=kDecoderISAC; - *PT=NETEQ_CODEC_ISAC_PT; - } - else if(!strcmp(name,"isacswb")){ - *fs=32000; - *codec=kDecoderISACswb; - *PT=NETEQ_CODEC_ISACSWB_PT; - } - else if(!strcmp(name,"g729")){ - *fs=8000; - *codec=kDecoderG729; - *PT=NETEQ_CODEC_G729_PT; - } - else if(!strcmp(name,"g729.1")){ - *fs=16000; - *codec=kDecoderG729_1; - *PT=NETEQ_CODEC_G729_1_PT; - } - else if(!strcmp(name,"gsmfr")){ - *fs=8000; - *codec=kDecoderGSMFR; - *PT=NETEQ_CODEC_GSMFR_PT; - } - else if(!strcmp(name,"speex8")){ - *fs=8000; - *codec=kDecoderSPEEX_8; - *PT=NETEQ_CODEC_SPEEX8_PT; - } - else if(!strcmp(name,"speex16")){ - *fs=16000; - *codec=kDecoderSPEEX_16; - *PT=NETEQ_CODEC_SPEEX16_PT; - } - else if(!strcmp(name,"red_pcm")){ - *codec=kDecoderPCMa; - *PT=NETEQ_CODEC_PCMA_PT; /* this will be the PT for the sub-headers */ - *fs=8000; - *useRed = 1; - } else if(!strcmp(name,"red_isac")){ - *codec=kDecoderISAC; - *PT=NETEQ_CODEC_ISAC_PT; /* this will be the PT for the sub-headers */ - *fs=16000; - *useRed = 1; - } else { - printf("Error: Not a supported codec (%s)\n", name); - exit(0); - } - -} - - - - -int NetEQTest_init_coders(enum WebRtcNetEQDecoder coder, int enc_frameSize, int bitrate, int sampfreq , int vad, int numChannels){ - - int ok=0; - - for (int k = 0; k < numChannels; k++) - { - ok=WebRtcVad_Create(&VAD_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for VAD instance\n"); - exit(0); - } - ok=WebRtcVad_Init(VAD_inst[k]); - if (ok==-1) { - printf("Error: Initialization of VAD struct failed\n"); - exit(0); - } - - -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - ok=WebRtcCng_CreateEnc(&CNGenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for CNG encoding instance\n"); - exit(0); - } - if(sampfreq <= 16000) { - ok=WebRtcCng_InitEnc(CNGenc_inst[k],sampfreq, 200, 5); - if (ok==-1) { - printf("Error: Initialization of CNG struct failed. Error code %d\n", - WebRtcCng_GetErrorCodeEnc(CNGenc_inst[k])); - exit(0); - } - } -#endif - - switch (coder) { - case kDecoderReservedStart : // dummy codec -#ifdef CODEC_PCM16B - case kDecoderPCM16B : -#endif -#ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb : -#endif -#ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz : -#endif -#ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz : -#endif -#ifdef CODEC_G711 - case kDecoderPCMu : - case kDecoderPCMa : -#endif - // do nothing - break; -#ifdef CODEC_G729 - case kDecoderG729: - if (sampfreq==8000) { - if ((enc_frameSize==80)||(enc_frameSize==160)||(enc_frameSize==240)||(enc_frameSize==320)||(enc_frameSize==400)||(enc_frameSize==480)) { - ok=WebRtcG729_CreateEnc(&G729enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G729 encoding instance\n"); - exit(0); - } - } else { - printf("\nError: g729 only supports 10, 20, 30, 40, 50 or 60 ms!!\n\n"); - exit(0); - } - WebRtcG729_EncoderInit(G729enc_inst[k], vad); - if ((vad==1)&&(enc_frameSize!=80)) { - printf("\nError - This simulation only supports VAD for G729 at 10ms packets (not %dms)\n", (enc_frameSize>>3)); - } - } else { - printf("\nError - g729 is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G729_1 - case kDecoderG729_1: - if (sampfreq==16000) { - if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960) - ) { - ok=WebRtcG7291_Create(&G729_1_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.729.1 codec instance\n"); - exit(0); - } - } else { - printf("\nError: G.729.1 only supports 20, 40 or 60 ms!!\n\n"); - exit(0); - } - if (!(((bitrate >= 12000) && (bitrate <= 32000) && (bitrate%2000 == 0)) || (bitrate == 8000))) { - /* must be 8, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, or 32 kbps */ - printf("\nError: G.729.1 bitrate must be 8000 or 12000--32000 in steps of 2000 bps\n"); - exit(0); - } - WebRtcG7291_EncoderInit(G729_1_inst[k], bitrate, 0 /* flag8kHz*/, 0 /*flagG729mode*/); - } else { - printf("\nError - G.729.1 input is always 16 kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8 : - if (sampfreq==8000) { - if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - ok=WebRtcSpeex_CreateEnc(&SPEEX8enc_inst[k], sampfreq); - if (ok!=0) { - printf("Error: Couldn't allocate memory for Speex encoding instance\n"); - exit(0); - } - } else { - printf("\nError: Speex only supports 20, 40, and 60 ms!!\n\n"); - exit(0); - } - if ((vad==1)&&(enc_frameSize!=160)) { - printf("\nError - This simulation only supports VAD for Speex at 20ms packets (not %dms)\n", (enc_frameSize>>3)); - vad=0; - } - ok=WebRtcSpeex_EncoderInit(SPEEX8enc_inst[k], 0/*vbr*/, 3 /*complexity*/, vad); - if (ok!=0) exit(0); - } else { - printf("\nError - Speex8 called with sample frequency other than 8 kHz.\n\n"); - } - break; -#endif -#ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16 : - if (sampfreq==16000) { - if ((enc_frameSize==320)||(enc_frameSize==640)||(enc_frameSize==960)) { - ok=WebRtcSpeex_CreateEnc(&SPEEX16enc_inst[k], sampfreq); - if (ok!=0) { - printf("Error: Couldn't allocate memory for Speex encoding instance\n"); - exit(0); - } - } else { - printf("\nError: Speex only supports 20, 40, and 60 ms!!\n\n"); - exit(0); - } - if ((vad==1)&&(enc_frameSize!=320)) { - printf("\nError - This simulation only supports VAD for Speex at 20ms packets (not %dms)\n", (enc_frameSize>>4)); - vad=0; - } - ok=WebRtcSpeex_EncoderInit(SPEEX16enc_inst[k], 0/*vbr*/, 3 /*complexity*/, vad); - if (ok!=0) exit(0); - } else { - printf("\nError - Speex16 called with sample frequency other than 16 kHz.\n\n"); - } - break; -#endif - -#ifdef CODEC_G722_1_16 - case kDecoderG722_1_16 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc16(&G722_1_16enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit16((G722_1_16_encinst_t*)G722_1_16enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1_24 - case kDecoderG722_1_24 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc24(&G722_1_24enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit24((G722_1_24_encinst_t*)G722_1_24enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1_32 - case kDecoderG722_1_32 : - if (sampfreq==16000) { - ok=WebRtcG7221_CreateEnc32(&G722_1_32enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1 instance\n"); - exit(0); - } - if (enc_frameSize==320) { - } else { - printf("\nError: G722.1 only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221_EncoderInit32((G722_1_32_encinst_t*)G722_1_32enc_inst[k]); - } else { - printf("\nError - G722.1 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc24(&G722_1C_24enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit24((G722_1C_24_encinst_t*)G722_1C_24enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc32(&G722_1C_32enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit32((G722_1C_32_encinst_t*)G722_1C_32enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48 : - if (sampfreq==32000) { - ok=WebRtcG7221C_CreateEnc48(&G722_1C_48enc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for G.722.1C instance\n"); - exit(0); - } - if (enc_frameSize==640) { - } else { - printf("\nError: G722.1 C only supports 20 ms!!\n\n"); - exit(0); - } - WebRtcG7221C_EncoderInit48((G722_1C_48_encinst_t*)G722_1C_48enc_inst[k]); - } else { - printf("\nError - G722.1 C is only developed for 32kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_G722 - case kDecoderG722 : - if (sampfreq==16000) { - if (enc_frameSize%2==0) { - } else { - printf("\nError - g722 frames must have an even number of enc_frameSize\n"); - exit(0); - } - WebRtcG722_CreateEncoder(&g722EncState[k]); - WebRtcG722_EncoderInit(g722EncState[k]); - } else { - printf("\nError - g722 is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_AMR - case kDecoderAMR : - if (sampfreq==8000) { - ok=WebRtcAmr_CreateEnc(&AMRenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for AMR encoding instance\n"); - exit(0); - }if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - AMR must have a multiple of 160 enc_frameSize\n"); - exit(0); - } - WebRtcAmr_EncoderInit(AMRenc_inst[k], vad); - WebRtcAmr_EncodeBitmode(AMRenc_inst[k], AMRBandwidthEfficient); - AMR_bitrate = bitrate; - } else { - printf("\nError - AMR is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_AMRWB - case kDecoderAMRWB : - if (sampfreq==16000) { - ok=WebRtcAmrWb_CreateEnc(&AMRWBenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for AMRWB encoding instance\n"); - exit(0); - } - if (((enc_frameSize/320)<0)||((enc_frameSize/320)>3)||((enc_frameSize%320)!=0)) { - printf("\nError - AMRwb must have frameSize of 20, 40 or 60ms\n"); - exit(0); - } - WebRtcAmrWb_EncoderInit(AMRWBenc_inst[k], vad); - if (bitrate==7000) { - AMRWB_bitrate = AMRWB_MODE_7k; - } else if (bitrate==9000) { - AMRWB_bitrate = AMRWB_MODE_9k; - } else if (bitrate==12000) { - AMRWB_bitrate = AMRWB_MODE_12k; - } else if (bitrate==14000) { - AMRWB_bitrate = AMRWB_MODE_14k; - } else if (bitrate==16000) { - AMRWB_bitrate = AMRWB_MODE_16k; - } else if (bitrate==18000) { - AMRWB_bitrate = AMRWB_MODE_18k; - } else if (bitrate==20000) { - AMRWB_bitrate = AMRWB_MODE_20k; - } else if (bitrate==23000) { - AMRWB_bitrate = AMRWB_MODE_23k; - } else if (bitrate==24000) { - AMRWB_bitrate = AMRWB_MODE_24k; - } - WebRtcAmrWb_EncodeBitmode(AMRWBenc_inst[k], AMRBandwidthEfficient); - - } else { - printf("\nError - AMRwb is only developed for 16kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ILBC - case kDecoderILBC : - if (sampfreq==8000) { - ok=WebRtcIlbcfix_EncoderCreate(&iLBCenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iLBC encoding instance\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==240)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - iLBC only supports 160, 240, 320 and 480 enc_frameSize (20, 30, 40 and 60 ms)\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==320)) { - /* 20 ms version */ - WebRtcIlbcfix_EncoderInit(iLBCenc_inst[k], 20); - } else { - /* 30 ms version */ - WebRtcIlbcfix_EncoderInit(iLBCenc_inst[k], 30); - } - } else { - printf("\nError - iLBC is only developed for 8kHz \n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ISAC - case kDecoderISAC: - if (sampfreq==16000) { - ok=WebRtcIsac_Create(&ISAC_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC instance\n"); - exit(0); - }if ((enc_frameSize==480)||(enc_frameSize==960)) { - } else { - printf("\nError - iSAC only supports frameSize (30 and 60 ms)\n"); - exit(0); - } - WebRtcIsac_EncoderInit(ISAC_inst[k],1); - if ((bitrate<10000)||(bitrate>32000)) { - printf("\nError - iSAC bitrate has to be between 10000 and 32000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsac_Control(ISAC_inst[k], bitrate, enc_frameSize>>4); - } else { - printf("\nError - iSAC only supports 480 or 960 enc_frameSize (30 or 60 ms)\n"); - exit(0); - } - break; -#endif -#ifdef NETEQ_ISACFIX_CODEC - case kDecoderISAC: - if (sampfreq==16000) { - ok=WebRtcIsacfix_Create(&ISAC_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC instance\n"); - exit(0); - }if ((enc_frameSize==480)||(enc_frameSize==960)) { - } else { - printf("\nError - iSAC only supports frameSize (30 and 60 ms)\n"); - exit(0); - } - WebRtcIsacfix_EncoderInit(ISAC_inst[k],1); - if ((bitrate<10000)||(bitrate>32000)) { - printf("\nError - iSAC bitrate has to be between 10000 and 32000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsacfix_Control(ISAC_inst[k], bitrate, enc_frameSize>>4); - } else { - printf("\nError - iSAC only supports 480 or 960 enc_frameSize (30 or 60 ms)\n"); - exit(0); - } - break; -#endif -#ifdef CODEC_ISAC_SWB - case kDecoderISACswb: - if (sampfreq==32000) { - ok=WebRtcIsac_Create(&ISACSWB_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for iSAC SWB instance\n"); - exit(0); - }if ((enc_frameSize==960)) { - } else { - printf("\nError - iSAC SWB only supports frameSize 30 ms\n"); - exit(0); - } - ok = WebRtcIsac_SetEncSampRate(ISACSWB_inst[k], kIsacSuperWideband); - if (ok!=0) { - printf("Error: Couldn't set sample rate for iSAC SWB instance\n"); - exit(0); - } - WebRtcIsac_EncoderInit(ISACSWB_inst[k],1); - if ((bitrate<32000)||(bitrate>56000)) { - printf("\nError - iSAC SWB bitrate has to be between 32000 and 56000 bps (not %i)\n", bitrate); - exit(0); - } - WebRtcIsac_Control(ISACSWB_inst[k], bitrate, enc_frameSize>>5); - } else { - printf("\nError - iSAC SWB only supports 960 enc_frameSize (30 ms)\n"); - exit(0); - } - break; -#endif -#ifdef CODEC_GSMFR - case kDecoderGSMFR: - if (sampfreq==8000) { - ok=WebRtcGSMFR_CreateEnc(&GSMFRenc_inst[k]); - if (ok!=0) { - printf("Error: Couldn't allocate memory for GSM FR encoding instance\n"); - exit(0); - } - if ((enc_frameSize==160)||(enc_frameSize==320)||(enc_frameSize==480)) { - } else { - printf("\nError - GSM FR must have a multiple of 160 enc_frameSize\n"); - exit(0); - } - WebRtcGSMFR_EncoderInit(GSMFRenc_inst[k], 0); - } else { - printf("\nError - GSM FR is only developed for 8kHz \n"); - exit(0); - } - break; -#endif - default : - printf("Error: unknown codec in call to NetEQTest_init_coders.\n"); - exit(0); - break; - } - - if (ok != 0) { - return(ok); - } - } // end for - - return(0); -} - - - - -int NetEQTest_free_coders(enum WebRtcNetEQDecoder coder, int numChannels) { - - for (int k = 0; k < numChannels; k++) - { - WebRtcVad_Free(VAD_inst[k]); -#if (defined(CODEC_CNGCODEC8) || defined(CODEC_CNGCODEC16) || \ - defined(CODEC_CNGCODEC32) || defined(CODEC_CNGCODEC48)) - WebRtcCng_FreeEnc(CNGenc_inst[k]); -#endif - - switch (coder) - { - case kDecoderReservedStart : // dummy codec -#ifdef CODEC_PCM16B - case kDecoderPCM16B : -#endif -#ifdef CODEC_PCM16B_WB - case kDecoderPCM16Bwb : -#endif -#ifdef CODEC_PCM16B_32KHZ - case kDecoderPCM16Bswb32kHz : -#endif -#ifdef CODEC_PCM16B_48KHZ - case kDecoderPCM16Bswb48kHz : -#endif -#ifdef CODEC_G711 - case kDecoderPCMu : - case kDecoderPCMa : -#endif - // do nothing - break; -#ifdef CODEC_G729 - case kDecoderG729: - WebRtcG729_FreeEnc(G729enc_inst[k]); - break; -#endif -#ifdef CODEC_G729_1 - case kDecoderG729_1: - WebRtcG7291_Free(G729_1_inst[k]); - break; -#endif -#ifdef CODEC_SPEEX_8 - case kDecoderSPEEX_8 : - WebRtcSpeex_FreeEnc(SPEEX8enc_inst[k]); - break; -#endif -#ifdef CODEC_SPEEX_16 - case kDecoderSPEEX_16 : - WebRtcSpeex_FreeEnc(SPEEX16enc_inst[k]); - break; -#endif - -#ifdef CODEC_G722_1_16 - case kDecoderG722_1_16 : - WebRtcG7221_FreeEnc16(G722_1_16enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1_24 - case kDecoderG722_1_24 : - WebRtcG7221_FreeEnc24(G722_1_24enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1_32 - case kDecoderG722_1_32 : - WebRtcG7221_FreeEnc32(G722_1_32enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_24 - case kDecoderG722_1C_24 : - WebRtcG7221C_FreeEnc24(G722_1C_24enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_32 - case kDecoderG722_1C_32 : - WebRtcG7221C_FreeEnc32(G722_1C_32enc_inst[k]); - break; -#endif -#ifdef CODEC_G722_1C_48 - case kDecoderG722_1C_48 : - WebRtcG7221C_FreeEnc48(G722_1C_48enc_inst[k]); - break; -#endif -#ifdef CODEC_G722 - case kDecoderG722 : - WebRtcG722_FreeEncoder(g722EncState[k]); - break; -#endif -#ifdef CODEC_AMR - case kDecoderAMR : - WebRtcAmr_FreeEnc(AMRenc_inst[k]); - break; -#endif -#ifdef CODEC_AMRWB - case kDecoderAMRWB : - WebRtcAmrWb_FreeEnc(AMRWBenc_inst[k]); - break; -#endif -#ifdef CODEC_ILBC - case kDecoderILBC : - WebRtcIlbcfix_EncoderFree(iLBCenc_inst[k]); - break; -#endif -#ifdef CODEC_ISAC - case kDecoderISAC: - WebRtcIsac_Free(ISAC_inst[k]); - break; -#endif -#ifdef NETEQ_ISACFIX_CODEC - case kDecoderISAC: - WebRtcIsacfix_Free(ISAC_inst[k]); - break; -#endif -#ifdef CODEC_ISAC_SWB - case kDecoderISACswb: - WebRtcIsac_Free(ISACSWB_inst[k]); - break; -#endif -#ifdef CODEC_GSMFR - case kDecoderGSMFR: - WebRtcGSMFR_FreeEnc(GSMFRenc_inst[k]); - break; -#endif - default : - printf("Error: unknown codec in call to NetEQTest_init_coders.\n"); - exit(0); - break; - } - } - - return(0); -} - - - - - - -int NetEQTest_encode(int coder, WebRtc_Word16 *indata, int frameLen, unsigned char * encoded,int sampleRate , - int * vad, int useVAD, int bitrate, int numChannels){ - - short cdlen; - WebRtc_Word16 size_in_bytes, *tempdata; - static int first_cng=1; - WebRtc_Word16 tempLen; - - *vad =1; - - // check VAD first - if(useVAD&& - (coder!=kDecoderG729)&&(coder!=kDecoderAMR)&& - (coder!=kDecoderSPEEX_8)&&(coder!=kDecoderSPEEX_16)) - { - *vad = 0; - - for (int k = 0; k < numChannels; k++) - { - tempLen = frameLen; - tempdata = &indata[k*frameLen]; - int localVad=0; - /* Partition the signal and test each chunk for VAD. - All chunks must be VAD=0 to produce a total VAD=0. */ - while (tempLen >= 10*sampleRate/1000) { - if ((tempLen % 30*sampleRate/1000) == 0) { // tempLen is multiple of 30ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 30*sampleRate/1000); - tempdata += 30*sampleRate/1000; - tempLen -= 30*sampleRate/1000; - } - else if (tempLen >= 20*sampleRate/1000) { // tempLen >= 20ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 20*sampleRate/1000); - tempdata += 20*sampleRate/1000; - tempLen -= 20*sampleRate/1000; - } - else { // use 10ms - localVad |= WebRtcVad_Process(VAD_inst[k] ,sampleRate, tempdata, 10*sampleRate/1000); - tempdata += 10*sampleRate/1000; - tempLen -= 10*sampleRate/1000; - } - } - - // aggregate all VAD decisions over all channels - *vad |= localVad; - } - - if(!*vad){ - // all channels are silent - cdlen = 0; - for (int k = 0; k < numChannels; k++) - { - WebRtcCng_Encode(CNGenc_inst[k],&indata[k*frameLen], (frameLen <= 640 ? frameLen : 640) /* max 640 */, - encoded,&tempLen,first_cng); - encoded += tempLen; - cdlen += tempLen; - } - *vad=0; - first_cng=0; - return(cdlen); - } - } - - - // loop over all channels - int totalLen = 0; - - for (int k = 0; k < numChannels; k++) - { - /* Encode with the selected coder type */ - if ((coder==kDecoderPCMu)) { /*g711 u-law */ -#ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeU(G711state[k], indata, frameLen, (WebRtc_Word16*) encoded); -#endif - } - else if ((coder==kDecoderPCMa)) { /*g711 A-law */ -#ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeA(G711state[k], indata, frameLen, (WebRtc_Word16*) encoded); - } -#endif -#ifdef CODEC_PCM16B - else if ((coder==kDecoderPCM16B)||(coder==kDecoderPCM16Bwb)|| - (coder==kDecoderPCM16Bswb32kHz)||(coder==kDecoderPCM16Bswb48kHz)) { /*pcm16b (8kHz, 16kHz, 32kHz or 48kHz) */ - cdlen = WebRtcPcm16b_EncodeW16(indata, frameLen, (WebRtc_Word16*) encoded); - } -#endif -#ifdef CODEC_G722 - else if ((coder==kDecoderG722)) { /*g722 */ - cdlen=WebRtcG722_Encode(g722EncState[k], indata, frameLen, (WebRtc_Word16*)encoded); - cdlen=frameLen>>1; - } -#endif -#ifdef CODEC_G722_1_16 - else if (coder==kDecoderG722_1_16) { /* g722.1 16kbit/s mode */ - cdlen=WebRtcG7221_Encode16((G722_1_16_encinst_t*)G722_1_16enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G722_1_24 - else if (coder==kDecoderG722_1_24) { /* g722.1 24kbit/s mode*/ - cdlen=WebRtcG7221_Encode24((G722_1_24_encinst_t*)G722_1_24enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G722_1_32 - else if (coder==kDecoderG722_1_32) { /* g722.1 32kbit/s mode */ - cdlen=WebRtcG7221_Encode32((G722_1_32_encinst_t*)G722_1_32enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G722_1C_24 - else if (coder==kDecoderG722_1C_24) { /* g722.1 32 kHz 24kbit/s mode*/ - cdlen=WebRtcG7221C_Encode24((G722_1C_24_encinst_t*)G722_1C_24enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G722_1C_32 - else if (coder==kDecoderG722_1C_32) { /* g722.1 32 kHz 32kbit/s mode */ - cdlen=WebRtcG7221C_Encode32((G722_1C_32_encinst_t*)G722_1C_32enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G722_1C_48 - else if (coder==kDecoderG722_1C_48) { /* g722.1 32 kHz 48kbit/s mode */ - cdlen=WebRtcG7221C_Encode48((G722_1C_48_encinst_t*)G722_1C_48enc_inst[k], indata, frameLen, (WebRtc_Word16*)encoded); - } -#endif -#ifdef CODEC_G729 - else if ((coder==kDecoderG729)) { /*g729 */ - WebRtc_Word16 dataPos=0; - WebRtc_Word16 len=0; - cdlen = 0; - for (dataPos=0;dataPos>8)&0xFF); - rtp_data[3]=(unsigned char)((seqNo)&0xFF); - rtp_data[4]=(unsigned char)((timestamp>>24)&0xFF); - rtp_data[5]=(unsigned char)((timestamp>>16)&0xFF); - - rtp_data[6]=(unsigned char)((timestamp>>8)&0xFF); - rtp_data[7]=(unsigned char)(timestamp & 0xFF); - - rtp_data[8]=(unsigned char)((ssrc>>24)&0xFF); - rtp_data[9]=(unsigned char)((ssrc>>16)&0xFF); - - rtp_data[10]=(unsigned char)((ssrc>>8)&0xFF); - rtp_data[11]=(unsigned char)(ssrc & 0xFF); -} - - -int makeRedundantHeader(unsigned char* rtp_data, int *payloadType, int numPayloads, WebRtc_UWord32 *timestamp, WebRtc_UWord16 *blockLen, - int seqNo, WebRtc_UWord32 ssrc) -{ - - int i; - unsigned char *rtpPointer; - WebRtc_UWord16 offset; - - /* first create "standard" RTP header */ - makeRTPheader(rtp_data, NETEQ_CODEC_RED_PT, seqNo, timestamp[numPayloads-1], ssrc); - - rtpPointer = &rtp_data[12]; - - /* add one sub-header for each redundant payload (not the primary) */ - for(i=0; i 0) { - offset = (WebRtc_UWord16) (timestamp[numPayloads-1] - timestamp[i]); - - rtpPointer[0] = (unsigned char) ( 0x80 | (0x7F & payloadType[i]) ); /* |F| block PT | */ - rtpPointer[1] = (unsigned char) ((offset >> 6) & 0xFF); /* | timestamp- | */ - rtpPointer[2] = (unsigned char) ( ((offset & 0x3F)<<2) | - ( (blockLen[i]>>8) & 0x03 ) ); /* | -offset |bl-| */ - rtpPointer[3] = (unsigned char) ( blockLen[i] & 0xFF ); /* | -ock length | */ - - rtpPointer += 4; - } - } - - /* last sub-header */ - rtpPointer[0]= (unsigned char) (0x00 | (0x7F&payloadType[numPayloads-1]));/* |F| block PT | */ - rtpPointer += 1; - - return(rtpPointer - rtp_data); /* length of header in bytes */ -} - - - -int makeDTMFpayload(unsigned char* payload_data, int Event, int End, int Volume, int Duration) { - unsigned char E,R,V; - R=0; - V=(unsigned char)Volume; - if (End==0) { - E = 0x00; - } else { - E = 0x80; - } - payload_data[0]=(unsigned char)Event; - payload_data[1]=(unsigned char)(E|R|V); - //Duration equals 8 times time_ms, default is 8000 Hz. - payload_data[2]=(unsigned char)((Duration>>8)&0xFF); - payload_data[3]=(unsigned char)(Duration&0xFF); - return(4); -} - -void stereoDeInterleave(WebRtc_Word16* audioSamples, int numSamples) -{ - - WebRtc_Word16 *tempVec; - WebRtc_Word16 *readPtr, *writeL, *writeR; - - if (numSamples <= 0) - return; - - tempVec = (WebRtc_Word16 *) malloc(sizeof(WebRtc_Word16) * numSamples); - if (tempVec == NULL) { - printf("Error allocating memory\n"); - exit(0); - } - - memcpy(tempVec, audioSamples, numSamples*sizeof(WebRtc_Word16)); - - writeL = audioSamples; - writeR = &audioSamples[numSamples/2]; - readPtr = tempVec; - - for (int k = 0; k < numSamples; k += 2) - { - *writeL = *readPtr; - readPtr++; - *writeR = *readPtr; - readPtr++; - writeL++; - writeR++; - } - - free(tempVec); - -} - - -void stereoInterleave(unsigned char* data, int dataLen, int stride) -{ - - unsigned char *ptrL, *ptrR; - unsigned char temp[10]; - - if (stride > 10) - { - exit(0); - } - - if (dataLen%1 != 0) - { - // must be even number of samples - printf("Error: cannot interleave odd sample number\n"); - exit(0); - } - - ptrL = data + stride; - ptrR = &data[dataLen/2]; - - while (ptrL < ptrR) { - // copy from right pointer to temp - memcpy(temp, ptrR, stride); - - // shift data between pointers - memmove(ptrL + stride, ptrL, ptrR - ptrL); - - // copy from temp to left pointer - memcpy(ptrL, temp, stride); - - // advance pointers - ptrL += stride*2; - ptrR += stride; - } - -} diff --git a/modules/audio_coding/NetEQ/main/test/RTPjitter.cc b/modules/audio_coding/NetEQ/main/test/RTPjitter.cc deleted file mode 100644 index e29272bf0..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPjitter.cc +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* header includes */ -#include "typedefs.h" -#include -#include -#ifdef WIN32 -#include -#include -#endif -#ifdef WEBRTC_LINUX -#include -#endif -#include -#include - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 -#define CHECK_ZERO(a) {int errCode = a; if((errCode)!=0){fprintf(stderr,"\n %s \n line: %d \n error at %s\n Error Code = %d\n",__FILE__,__LINE__,#a, WebRtcNetEQ_GetErrorCode(inst)); exit(0);}} -#define CHECK_NOT_NULL(a) if((a)==NULL){fprintf(stderr,"\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a );return(-1);} - -/********************/ -/* Global variables */ -/********************/ - -FILE *in_file; -FILE *out_file; -FILE *dat_file; - - -struct arr_time { - float time; - WebRtc_UWord32 ix; -}; - -int filelen(FILE *fid) -{ - fpos_t cur_pos; - int len; - - if (!fid || fgetpos(fid, &cur_pos)) { - return(-1); - } - - fseek(fid, 0, SEEK_END); - len = ftell(fid); - - fsetpos(fid, &cur_pos); - - return (len); -} - -int compare_arr_time(const void *x, const void *y); - -int main(int argc, char* argv[]) -{ - unsigned int dat_len, rtp_len, Npack, k; - arr_time *time_vec; - char firstline[FIRSTLINELEN]; - unsigned char *rtp_vec, **packet_ptr, *temp_packet; - WebRtc_UWord16 len; - WebRtc_UWord32 *offset; - -/* check number of parameters */ - if (argc != 4) { - /* print help text and exit */ - printf("Apply jitter on RTP stream.\n"); - printf("The program reads an RTP stream and packet timing from two files.\n"); - printf("The RTP stream is modified to have the same jitter as described in the timing files.\n"); - printf("The format of the RTP stream file should be the same as for rtpplay,\n"); - printf("and can be obtained e.g., from Ethereal by using\n"); - printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> Save As\n\n"); - printf("Usage:\n\n"); - printf("%s RTP_infile dat_file RTP_outfile\n", argv[0]); - printf("where:\n"); - - printf("RTP_infile : RTP stream input file\n\n"); - - printf("dat_file : file with packet arrival times in ms\n\n"); - - printf("RTP_outfile : RTP stream output file\n\n"); - - return(0); - } - - in_file=fopen(argv[1],"rb"); - CHECK_NOT_NULL(in_file); - printf("Input file: %s\n",argv[1]); - dat_file=fopen(argv[2],"rb"); - CHECK_NOT_NULL(dat_file); - printf("Dat-file: %s\n",argv[2]); - out_file=fopen(argv[3],"wb"); - CHECK_NOT_NULL(out_file); - printf("Output file: %s\n\n",argv[3]); - - time_vec = (arr_time *) malloc(sizeof(arr_time)*(filelen(dat_file)/sizeof(float)) + 1000); // add 1000 bytes to avoid (rare) strange error - if (time_vec==NULL) { - fprintf(stderr, "Error: could not allocate memory for reading dat file\n"); - goto closing; - } - - dat_len=0; - while(fread(&(time_vec[dat_len].time),sizeof(float),1,dat_file)>0) { - time_vec[dat_len].ix=dat_len; - dat_len++; - } - - qsort(time_vec,dat_len,sizeof(arr_time),compare_arr_time); - - - rtp_vec = (unsigned char *) malloc(sizeof(unsigned char)*filelen(in_file)); - if (rtp_vec==NULL) { - fprintf(stderr,"Error: could not allocate memory for reading rtp file\n"); - goto closing; - } - - // read file header and write directly to output file - fgets(firstline, FIRSTLINELEN, in_file); - fputs(firstline, out_file); - fread(firstline, 4+4+4+2+2, 1, in_file); // start_sec + start_usec + source + port + padding - fwrite(firstline, 4+4+4+2+2, 1, out_file); - - // read all RTP packets into vector - rtp_len=0; - Npack=0; - len=(WebRtc_UWord16) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of first packet - while(len==2) { - len = ntohs(*((WebRtc_UWord16 *)(rtp_vec + rtp_len))); - rtp_len += 2; - if(fread(&rtp_vec[rtp_len], sizeof(unsigned char), len-2, in_file)!=(unsigned) (len-2)) { - fprintf(stderr,"Error: currupt packet length\n"); - goto closing; - } - rtp_len += len-2; - Npack++; - len=(WebRtc_UWord16) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file); // read length of next packet - } - - packet_ptr = (unsigned char **) malloc(Npack*sizeof(unsigned char*)); - - packet_ptr[0]=rtp_vec; - k=1; - while(k= 0 ) { - *offset = htonl((WebRtc_UWord32) time_vec[k].time); - } - else { - *offset = htonl((WebRtc_UWord32) 0); - fprintf(stderr, "Warning: negative receive time in dat file transformed to 0.\n"); - } - - // write packet to file - fwrite(temp_packet, sizeof(unsigned char), ntohs(*((WebRtc_UWord16*) temp_packet)), out_file); - } - } - - -closing: - free(time_vec); - free(rtp_vec); - fclose(in_file); - fclose(dat_file); - fclose(out_file); - - return(0); -} - - - -int compare_arr_time(const void *xp, const void *yp) { - - if(((arr_time *)xp)->time == ((arr_time *)yp)->time) - return(0); - else if(((arr_time *)xp)->time > ((arr_time *)yp)->time) - return(1); - - return(-1); -} diff --git a/modules/audio_coding/NetEQ/main/test/RTPtimeshift.cc b/modules/audio_coding/NetEQ/main/test/RTPtimeshift.cc deleted file mode 100644 index 97b6da0f8..000000000 --- a/modules/audio_coding/NetEQ/main/test/RTPtimeshift.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "NETEQTEST_RTPpacket.h" - - -/*********************/ -/* Misc. definitions */ -/*********************/ - -#define FIRSTLINELEN 40 - - -int main(int argc, char* argv[]) -{ - if(argc < 4 || argc > 6) - { - printf("Usage: RTPtimeshift in.rtp out.rtp newStartTS [newStartSN [newStartArrTime]]\n"); - exit(1); - } - - FILE *inFile=fopen(argv[1],"rb"); - if (!inFile) - { - printf("Cannot open input file %s\n", argv[1]); - return(-1); - } - printf("Input RTP file: %s\n",argv[1]); - - FILE *outFile=fopen(argv[2],"wb"); - if (!outFile) - { - printf("Cannot open output file %s\n", argv[2]); - return(-1); - } - printf("Output RTP file: %s\n\n",argv[2]); - - // read file header and write directly to output file - char firstline[FIRSTLINELEN]; - fgets(firstline, FIRSTLINELEN, inFile); - fputs(firstline, outFile); - fread(firstline, 4+4+4+2+2, 1, inFile); // start_sec + start_usec + source + port + padding - fwrite(firstline, 4+4+4+2+2, 1, outFile); - - NETEQTEST_RTPpacket packet; - int packLen = packet.readFromFile(inFile); - if (packLen < 0) - { - exit(1); - } - - // get new start TS and start SeqNo from arguments - WebRtc_UWord32 TSdiff = atoi(argv[3]) - packet.timeStamp(); - WebRtc_UWord16 SNdiff = 0; - WebRtc_UWord32 ATdiff = 0; - if (argc > 4) - { - if (argv[4] >= 0) - SNdiff = atoi(argv[4]) - packet.sequenceNumber(); - if (argc > 5) - { - if (argv[5] >= 0) - ATdiff = atoi(argv[5]) - packet.time(); - } - } - - while (packLen >= 0) - { - - packet.setTimeStamp(packet.timeStamp() + TSdiff); - packet.setSequenceNumber(packet.sequenceNumber() + SNdiff); - packet.setTime(packet.time() + ATdiff); - - packet.writeToFile(outFile); - - packLen = packet.readFromFile(inFile); - - } - - fclose(inFile); - fclose(outFile); - - return 0; -} diff --git a/modules/audio_coding/NetEQ/main/test/delay_tool/parse_delay_file.m b/modules/audio_coding/NetEQ/main/test/delay_tool/parse_delay_file.m deleted file mode 100644 index 77b394f41..000000000 --- a/modules/audio_coding/NetEQ/main/test/delay_tool/parse_delay_file.m +++ /dev/null @@ -1,191 +0,0 @@ -function outStruct = parse_delay_file(file) - -fid = fopen(file, 'rb'); -if fid == -1 - error('Cannot open file %s', file); -end - -textline = fgetl(fid); -if ~strncmp(textline, '#!NetEQ_Delay_Logging', 21) - error('Wrong file format'); -end - -ver = sscanf(textline, '#!NetEQ_Delay_Logging%d.%d'); -if ~all(ver == [2; 0]) - error('Wrong version of delay logging function') -end - - -start_pos = ftell(fid); -fseek(fid, -12, 'eof'); -textline = fgetl(fid); -if ~strncmp(textline, 'End of file', 21) - error('File ending is not correct. Seems like the simulation ended abnormally.'); -end - -fseek(fid,-12-4, 'eof'); -Npackets = fread(fid, 1, 'int32'); -fseek(fid, start_pos, 'bof'); - -rtpts = zeros(Npackets, 1); -seqno = zeros(Npackets, 1); -pt = zeros(Npackets, 1); -plen = zeros(Npackets, 1); -recin_t = nan*ones(Npackets, 1); -decode_t = nan*ones(Npackets, 1); -playout_delay = zeros(Npackets, 1); -optbuf = zeros(Npackets, 1); - -fs_ix = 1; -clock = 0; -ts_ix = 1; -ended = 0; -late_packets = 0; -fs_now = 8000; -last_decode_k = 0; -tot_expand = 0; -tot_accelerate = 0; -tot_preemptive = 0; - -while not(ended) - signal = fread(fid, 1, '*int32'); - - switch signal - case 3 % NETEQ_DELAY_LOGGING_SIGNAL_CLOCK - clock = fread(fid, 1, '*float32'); - - % keep on reading batches of M until the signal is no longer "3" - % read int32 + float32 in one go - % this is to save execution time - temp = [3; 0]; - M = 120; - while all(temp(1,:) == 3) - fp = ftell(fid); - temp = fread(fid, [2 M], '*int32'); - end - - % back up to last clock event - fseek(fid, fp - ftell(fid) + ... - (find(temp(1,:) ~= 3, 1 ) - 2) * 2 * 4 + 4, 'cof'); - % read the last clock value - clock = fread(fid, 1, '*float32'); - - case 1 % NETEQ_DELAY_LOGGING_SIGNAL_RECIN - temp_ts = fread(fid, 1, 'uint32'); - - if late_packets > 0 - temp_ix = ts_ix - 1; - while (temp_ix >= 1) && (rtpts(temp_ix) ~= temp_ts) - % TODO(hlundin): use matlab vector search instead? - temp_ix = temp_ix - 1; - end - - if temp_ix >= 1 - % the ts was found in the vector - late_packets = late_packets - 1; - else - temp_ix = ts_ix; - ts_ix = ts_ix + 1; - end - else - temp_ix = ts_ix; - ts_ix = ts_ix + 1; - end - - rtpts(temp_ix) = temp_ts; - seqno(temp_ix) = fread(fid, 1, 'uint16'); - pt(temp_ix) = fread(fid, 1, 'int32'); - plen(temp_ix) = fread(fid, 1, 'int16'); - recin_t(temp_ix) = clock; - - case 2 % NETEQ_DELAY_LOGGING_SIGNAL_FLUSH - % do nothing - - case 4 % NETEQ_DELAY_LOGGING_SIGNAL_EOF - ended = 1; - - case 5 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE - last_decode_ts = fread(fid, 1, 'uint32'); - temp_delay = fread(fid, 1, 'uint16'); - - k = find(rtpts(1:(ts_ix - 1))==last_decode_ts,1,'last'); - if ~isempty(k) - decode_t(k) = clock; - playout_delay(k) = temp_delay + ... - 5 * fs_now / 8000; % add overlap length - last_decode_k = k; - end - - case 6 % NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS - fsvec(fs_ix) = fread(fid, 1, 'uint16'); - fschange_ts(fs_ix) = last_decode_ts; - fs_now = fsvec(fs_ix); - fs_ix = fs_ix + 1; - - case 7 % NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO - playout_delay(last_decode_k) = playout_delay(last_decode_k) ... - + fread(fid, 1, 'int32'); - - case 8 % NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_expand = tot_expand + temp / (fs_now / 1000); - end - - case 9 % NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_accelerate = tot_accelerate + temp / (fs_now / 1000); - end - - case 10 % NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO - temp = fread(fid, 1, 'int32'); - if last_decode_k ~= 0 - tot_preemptive = tot_preemptive + temp / (fs_now / 1000); - end - - case 11 % NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF - optbuf(last_decode_k) = fread(fid, 1, 'int32'); - - case 12 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC - last_decode_ts = fread(fid, 1, 'uint32'); - k = ts_ix - 1; - - while (k >= 1) && (rtpts(k) ~= last_decode_ts) - % TODO(hlundin): use matlab vector search instead? - k = k - 1; - end - - if k < 1 - % packet not received yet - k = ts_ix; - rtpts(ts_ix) = last_decode_ts; - late_packets = late_packets + 1; - end - - decode_t(k) = clock; - playout_delay(k) = fread(fid, 1, 'uint16') + ... - 5 * fs_now / 8000; % add overlap length - last_decode_k = k; - - end - -end - - -fclose(fid); - -outStruct = struct(... - 'ts', rtpts, ... - 'sn', seqno, ... - 'pt', pt,... - 'plen', plen,... - 'arrival', recin_t,... - 'decode', decode_t,... - 'fs', fsvec(:),... - 'fschange_ts', fschange_ts(:),... - 'playout_delay', playout_delay,... - 'tot_expand', tot_expand,... - 'tot_accelerate', tot_accelerate,... - 'tot_preemptive', tot_preemptive,... - 'optbuf', optbuf); diff --git a/modules/audio_coding/NetEQ/main/test/delay_tool/plot_neteq_delay.m b/modules/audio_coding/NetEQ/main/test/delay_tool/plot_neteq_delay.m deleted file mode 100644 index bc1c85a20..000000000 --- a/modules/audio_coding/NetEQ/main/test/delay_tool/plot_neteq_delay.m +++ /dev/null @@ -1,187 +0,0 @@ -function [delay_struct, delayvalues] = plot_neteq_delay(delayfile, varargin) - -% InfoStruct = plot_neteq_delay(delayfile) -% InfoStruct = plot_neteq_delay(delayfile, 'skipdelay', skip_seconds) -% -% Henrik Lundin, 2006-11-17 -% Henrik Lundin, 2011-05-17 -% - -try - s = parse_delay_file(delayfile); -catch - error(lasterr); -end - -delayskip=0; -noplot=0; -arg_ptr=1; -delaypoints=[]; - -s.sn=unwrap_seqno(s.sn); - -while arg_ptr+1 <= nargin - switch lower(varargin{arg_ptr}) - case {'skipdelay', 'delayskip'} - % skip a number of seconds in the beginning when calculating delays - delayskip = varargin{arg_ptr+1}; - arg_ptr = arg_ptr + 2; - case 'noplot' - noplot=1; - arg_ptr = arg_ptr + 1; - case {'get_delay', 'getdelay'} - % return a vector of delay values for the points in the given vector - delaypoints = varargin{arg_ptr+1}; - arg_ptr = arg_ptr + 2; - otherwise - warning('Unknown switch %s\n', varargin{arg_ptr}); - arg_ptr = arg_ptr + 1; - end -end - -% find lost frames that were covered by one-descriptor decoding -one_desc_ix=find(isnan(s.arrival)); -for k=1:length(one_desc_ix) - ix=find(s.ts==max(s.ts(s.ts(one_desc_ix(k))>s.ts))); - s.sn(one_desc_ix(k))=s.sn(ix)+1; - s.pt(one_desc_ix(k))=s.pt(ix); - s.arrival(one_desc_ix(k))=s.arrival(ix)+s.decode(one_desc_ix(k))-s.decode(ix); -end - -% remove duplicate received frames that were never decoded (RED codec) -if length(unique(s.ts(isfinite(s.ts)))) < length(s.ts(isfinite(s.ts))) - ix=find(isfinite(s.decode)); - s.sn=s.sn(ix); - s.ts=s.ts(ix); - s.arrival=s.arrival(ix); - s.playout_delay=s.playout_delay(ix); - s.pt=s.pt(ix); - s.optbuf=s.optbuf(ix); - plen=plen(ix); - s.decode=s.decode(ix); -end - -% find non-unique sequence numbers -[~,un_ix]=unique(s.sn); -nonun_ix=setdiff(1:length(s.sn),un_ix); -if ~isempty(nonun_ix) - warning('RTP sequence numbers are in error'); -end - -% sort vectors -[s.sn,sort_ix]=sort(s.sn); -s.ts=s.ts(sort_ix); -s.arrival=s.arrival(sort_ix); -s.decode=s.decode(sort_ix); -s.playout_delay=s.playout_delay(sort_ix); -s.pt=s.pt(sort_ix); - -send_t=s.ts-s.ts(1); -if length(s.fs)<1 - warning('No info about sample rate found in file. Using default 8000.'); - s.fs(1)=8000; - s.fschange_ts(1)=min(s.ts); -elseif s.fschange_ts(1)>min(s.ts) - s.fschange_ts(1)=min(s.ts); -end - -end_ix=length(send_t); -for k=length(s.fs):-1:1 - start_ix=find(s.ts==s.fschange_ts(k)); - send_t(start_ix:end_ix)=send_t(start_ix:end_ix)/s.fs(k)*1000; - s.playout_delay(start_ix:end_ix)=s.playout_delay(start_ix:end_ix)/s.fs(k)*1000; - s.optbuf(start_ix:end_ix)=s.optbuf(start_ix:end_ix)/s.fs(k)*1000; - end_ix=start_ix-1; -end - -tot_time=max(send_t)-min(send_t); - -seq_ix=s.sn-min(s.sn)+1; -send_t=send_t+max(min(s.arrival-send_t),0); - -plot_send_t=nan*ones(max(seq_ix),1); -plot_send_t(seq_ix)=send_t; -plot_nw_delay=nan*ones(max(seq_ix),1); -plot_nw_delay(seq_ix)=s.arrival-send_t; - -cng_ix=find(s.pt~=13); % find those packets that are not CNG/SID - -if noplot==0 - h=plot(plot_send_t/1000,plot_nw_delay); - set(h,'color',0.75*[1 1 1]); - hold on - if any(s.optbuf~=0) - peak_ix=find(s.optbuf(cng_ix)<0); % peak mode is labeled with negative values - no_peak_ix=find(s.optbuf(cng_ix)>0); %setdiff(1:length(cng_ix),peak_ix); - h1=plot(send_t(cng_ix(peak_ix))/1000,... - s.arrival(cng_ix(peak_ix))+abs(s.optbuf(cng_ix(peak_ix)))-send_t(cng_ix(peak_ix)),... - 'r.'); - h2=plot(send_t(cng_ix(no_peak_ix))/1000,... - s.arrival(cng_ix(no_peak_ix))+abs(s.optbuf(cng_ix(no_peak_ix)))-send_t(cng_ix(no_peak_ix)),... - 'g.'); - set([h1, h2],'markersize',1) - end - %h=plot(send_t(seq_ix)/1000,s.decode+s.playout_delay-send_t(seq_ix)); - h=plot(send_t(cng_ix)/1000,s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix)); - set(h,'linew',1.5); - hold off - ax1=axis; - axis tight - ax2=axis; - axis([ax2(1:3) ax1(4)]) -end - - -% calculate delays and other parameters - -delayskip_ix = find(send_t-send_t(1)>=delayskip*1000, 1 ); - -use_ix = intersect(cng_ix,... % use those that are not CNG/SID frames... - intersect(find(isfinite(s.decode)),... % ... that did arrive ... - (delayskip_ix:length(s.decode))')); % ... and are sent after delayskip seconds - -mean_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-send_t(use_ix)); -neteq_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix)); - -Npack=max(s.sn(delayskip_ix:end))-min(s.sn(delayskip_ix:end))+1; -nw_lossrate=(Npack-length(s.sn(delayskip_ix:end)))/Npack; -neteq_lossrate=(length(s.sn(delayskip_ix:end))-length(use_ix))/Npack; - -delay_struct=struct('mean_delay',mean_delay,'neteq_delay',neteq_delay,... - 'nw_lossrate',nw_lossrate,'neteq_lossrate',neteq_lossrate,... - 'tot_expand',round(s.tot_expand),'tot_accelerate',round(s.tot_accelerate),... - 'tot_preemptive',round(s.tot_preemptive),'tot_time',tot_time,... - 'filename',delayfile,'units','ms','fs',unique(s.fs)); - -if not(isempty(delaypoints)) - delayvalues=interp1(send_t(cng_ix),... - s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix),... - delaypoints,'nearest',NaN); -else - delayvalues=[]; -end - - - -% SUBFUNCTIONS % - -function y=unwrap_seqno(x) - -jumps=find(abs((diff(x)-1))>65000); - -while ~isempty(jumps) - n=jumps(1); - if x(n+1)-x(n) < 0 - % negative jump - x(n+1:end)=x(n+1:end)+65536; - else - % positive jump - x(n+1:end)=x(n+1:end)-65536; - end - - jumps=find(abs((diff(x(n+1:end))-1))>65000); -end - -y=x; - -return; diff --git a/modules/audio_coding/NetEQ/main/test/ptypes.txt b/modules/audio_coding/NetEQ/main/test/ptypes.txt deleted file mode 100644 index 43bb78a08..000000000 --- a/modules/audio_coding/NetEQ/main/test/ptypes.txt +++ /dev/null @@ -1,47 +0,0 @@ -pcmu 0 -gsmfr 3 -g723 4 -dvi4 125 -pcma 8 -g722 9 -cn 13 -g729 18 -ipcmwb 97 -speex8 98 -speex16 99 -eg711u 100 -eg711a 101 -ilbc 102 -isac 103 -isaclc 119 -isacswb 104 -avt 106 -g722_1_16 108 -g722_1_24 109 -g722_1_32 110 -g722_1c_24 84 -g722_1c_32 85 -g722_1c_48 86 -//sc3 111 -amr 112 -gsmefr 113 -g726_16 115 -g726_24 116 -g726_32 121 -red 117 -g726_40 118 -cn_wb 105 -cn_swb32 126 -g729_1 107 -//g729d 123 -amrwb 120 -pcm16b 93 -pcm16b_wb 94 -pcm16b_swb32khz 95 -//pcm16b_swb48khz 96 -//mpeg4aac 122 -silk8 80 -silk12 81 -silk16 82 -silk24 83 -melpe 124 diff --git a/modules/audio_coding/codecs/CNG/main/interface/webrtc_cng.h b/modules/audio_coding/codecs/CNG/main/interface/webrtc_cng.h deleted file mode 100644 index d6e815788..000000000 --- a/modules/audio_coding/codecs/CNG/main/interface/webrtc_cng.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_INTERFACE_WEBRTC_CNG_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_INTERFACE_WEBRTC_CNG_H_ - -#include "typedefs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define WEBRTC_CNG_MAX_LPC_ORDER 12 -#define WEBRTC_CNG_MAX_OUTSIZE_ORDER 640 - -/* Define Error codes */ - -/* 6100 Encoder */ -#define CNG_ENCODER_MEMORY_ALLOCATION_FAILED 6110 -#define CNG_ENCODER_NOT_INITIATED 6120 -#define CNG_DISALLOWED_LPC_ORDER 6130 -#define CNG_DISALLOWED_FRAME_SIZE 6140 -#define CNG_DISALLOWED_SAMPLING_FREQUENCY 6150 -/* 6200 Decoder */ -#define CNG_DECODER_MEMORY_ALLOCATION_FAILED 6210 -#define CNG_DECODER_NOT_INITIATED 6220 - - -typedef struct WebRtcCngEncInst CNG_enc_inst; -typedef struct WebRtcCngDecInst CNG_dec_inst; - - -/**************************************************************************** - * WebRtcCng_Version(...) - * - * These functions returns the version name (string must be at least - * 500 characters long) - * - * Output: - * - version : Pointer to character string - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_Version(WebRtc_Word8 *version); - -/**************************************************************************** - * WebRtcCng_AssignSizeEnc/Dec(...) - * - * These functions get the size needed for storing the instance for encoder - * and decoder, respectively - * - * Input/Output: - * - sizeinbytes : Pointer to integer where the size is returned - * - * Return value : 0 - */ - -WebRtc_Word16 WebRtcCng_AssignSizeEnc(int *sizeinbytes); -WebRtc_Word16 WebRtcCng_AssignSizeDec(int *sizeinbytes); - - -/**************************************************************************** - * WebRtcCng_AssignEnc/Dec(...) - * - * These functions Assignes memory for the instances. - * - * Input: - * - CNG_inst_Addr : Adress to where to assign memory - * Output: - * - inst : Pointer to the instance that should be created - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_AssignEnc(CNG_enc_inst **inst, void *CNG_inst_Addr); -WebRtc_Word16 WebRtcCng_AssignDec(CNG_dec_inst **inst, void *CNG_inst_Addr); - - -/**************************************************************************** - * WebRtcCng_CreateEnc/Dec(...) - * - * These functions create an instance to the specified structure - * - * Input: - * - XXX_inst : Pointer to created instance that should be created - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_CreateEnc(CNG_enc_inst **cng_inst); -WebRtc_Word16 WebRtcCng_CreateDec(CNG_dec_inst **cng_inst); - - -/**************************************************************************** - * WebRtcCng_InitEnc/Dec(...) - * - * This function initializes a instance - * - * Input: - * - cng_inst : Instance that should be initialized - * - * - fs : 8000 for narrowband and 16000 for wideband - * - interval : generate SID data every interval ms - * - quality : Number of refl. coefs, maximum allowed is 12 - * - * Output: - * - cng_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_InitEnc(CNG_enc_inst *cng_inst, - WebRtc_Word16 fs, - WebRtc_Word16 interval, - WebRtc_Word16 quality); -WebRtc_Word16 WebRtcCng_InitDec(CNG_dec_inst *cng_dec_inst); - - -/**************************************************************************** - * WebRtcCng_FreeEnc/Dec(...) - * - * These functions frees the dynamic memory of a specified instance - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - * Return value : 0 - Ok - * -1 - Error - */ - - -WebRtc_Word16 WebRtcCng_FreeEnc(CNG_enc_inst *cng_inst); -WebRtc_Word16 WebRtcCng_FreeDec(CNG_dec_inst *cng_inst); - - - -/**************************************************************************** - * WebRtcCng_Encode(...) - * - * These functions analyzes background noise - * - * Input: - * - cng_inst : Pointer to created instance - * - speech : Signal to be analyzed - * - nrOfSamples : Size of speech vector - * - forceSID : not zero to force SID frame and reset - * - * Output: - * - bytesOut : Nr of bytes to transmit, might be 0 - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_Encode(CNG_enc_inst *cng_inst, - WebRtc_Word16 *speech, - WebRtc_Word16 nrOfSamples, - WebRtc_UWord8* SIDdata, - WebRtc_Word16 *bytesOut, - WebRtc_Word16 forceSID); - - -/**************************************************************************** - * WebRtcCng_UpdateSid(...) - * - * These functions updates the CN state, when a new SID packet arrives - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - SID : SID packet, all headers removed - * - length : Length in bytes of SID packet - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcCng_UpdateSid(CNG_dec_inst *cng_inst, - WebRtc_UWord8 *SID, - WebRtc_Word16 length); - - -/**************************************************************************** - * WebRtcCng_Generate(...) - * - * These functions generates CN data when needed - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - outData : pointer to area to write CN data - * - nrOfSamples : How much data to generate - * - new_period : >0 if a new period of CNG, will reset history - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcCng_Generate(CNG_dec_inst *cng_inst, - WebRtc_Word16 * outData, - WebRtc_Word16 nrOfSamples, - WebRtc_Word16 new_period); - - -/***************************************************************************** - * WebRtcCng_GetErrorCodeEnc/Dec(...) - * - * This functions can be used to check the error code of a CNG instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occurred in the - * specified instance. - * - * Input: - * - CNG_inst : CNG enc/dec instance - * - * Return value : Error code - */ - -WebRtc_Word16 WebRtcCng_GetErrorCodeEnc(CNG_enc_inst *cng_inst); -WebRtc_Word16 WebRtcCng_GetErrorCodeDec(CNG_dec_inst *cng_inst); - - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_INTERFACE_WEBRTC_CNG_H_ diff --git a/modules/audio_coding/codecs/CNG/main/source/Android.mk b/modules/audio_coding/codecs/CNG/main/source/Android.mk deleted file mode 100644 index fbd21c842..000000000 --- a/modules/audio_coding/codecs/CNG/main/source/Android.mk +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_cng -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := webrtc_cng.c \ - cng_helpfuns.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/CNG/main/source/cng.gyp b/modules/audio_coding/codecs/CNG/main/source/cng.gyp deleted file mode 100644 index 6d5ecafe3..000000000 --- a/modules/audio_coding/codecs/CNG/main/source/cng.gyp +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'CNG', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/webrtc_cng.h', - 'webrtc_cng.c', - 'cng_helpfuns.c', - 'cng_helpfuns.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.c b/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.c deleted file mode 100644 index 2e9029f0d..000000000 --- a/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#include "webrtc_cng.h" -#include "signal_processing_library.h" -#include "typedefs.h" -#include "cng_helpfuns.h" - - -#ifdef __cplusplus -extern "C" { -#endif - - -void WebRtcCng_K2a16( - WebRtc_Word16 *k, /* Q15. */ - int useOrder, - WebRtc_Word16 *a /* Q12. */ -) -{ - WebRtc_Word16 any[WEBRTC_SPL_MAX_LPC_ORDER+1]; - WebRtc_Word16 *aptr, *aptr2, *anyptr; - G_CONST WebRtc_Word16 *kptr; - int m, i; - - kptr = k; - *a = 4096; /* i.e., (Word16_MAX >> 3)+1 */ - *any = *a; - a[1] = (*k+4) >> 3; - for( m=1; m> 3; - for( i=0; i> 15); - } - - aptr = a; - anyptr = any; - for( i=0; i<(m+2); i++ ){ - *aptr++ = *anyptr++; - } - } -} - - -#ifdef __cplusplus -} -#endif - diff --git a/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.h b/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.h deleted file mode 100644 index fd8d6dcbc..000000000 --- a/modules/audio_coding/codecs/CNG/main/source/cng_helpfuns.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_SOURCE_CNG_HELPFUNS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_SOURCE_CNG_HELPFUNS_H_ - -extern WebRtc_Word32 lpc_lagwinTbl_fixw32[WEBRTC_CNG_MAX_LPC_ORDER + 1]; - -#ifdef __cplusplus -extern "C" { -#endif - - -void WebRtcCng_K2a16(WebRtc_Word16 *k, int useOrder, WebRtc_Word16 *a); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_SOURCE_CNG_HELPFUNS_H_ diff --git a/modules/audio_coding/codecs/CNG/main/source/webrtc_cng.c b/modules/audio_coding/codecs/CNG/main/source/webrtc_cng.c deleted file mode 100644 index 41893e6d3..000000000 --- a/modules/audio_coding/codecs/CNG/main/source/webrtc_cng.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#include -#include - -#include "webrtc_cng.h" -#include "signal_processing_library.h" -#include "cng_helpfuns.h" -#include "stdio.h" - - -typedef struct WebRtcCngDecInst_t_ { - - WebRtc_UWord32 dec_seed; - WebRtc_Word32 dec_target_energy; - WebRtc_Word32 dec_used_energy; - WebRtc_Word16 dec_target_reflCoefs[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_used_reflCoefs[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_filtstate[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_filtstateLow[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_Efiltstate[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_EfiltstateLow[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 dec_order; - WebRtc_Word16 dec_target_scale_factor; /*Q29*/ - WebRtc_Word16 dec_used_scale_factor; /*Q29*/ - WebRtc_Word16 target_scale_factor; /* Q13 */ - WebRtc_Word16 errorcode; - WebRtc_Word16 initflag; - -} WebRtcCngDecInst_t; - - -typedef struct WebRtcCngEncInst_t_ { - - WebRtc_Word16 enc_nrOfCoefs; - WebRtc_Word16 enc_sampfreq; - WebRtc_Word16 enc_interval; - WebRtc_Word16 enc_msSinceSID; - WebRtc_Word32 enc_Energy; - WebRtc_Word16 enc_reflCoefs[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word32 enc_corrVector[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 enc_filtstate[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 enc_filtstateLow[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_UWord32 enc_seed; - WebRtc_Word16 errorcode; - WebRtc_Word16 initflag; - -} WebRtcCngEncInst_t; - -const WebRtc_Word32 WebRtcCng_kDbov[94]={ - 1081109975, 858756178, 682134279, 541838517, 430397633, 341876992, - 271562548, 215709799, 171344384, 136103682, 108110997, 85875618, - 68213428, 54183852, 43039763, 34187699, 27156255, 21570980, - 17134438, 13610368, 10811100, 8587562, 6821343, 5418385, - 4303976, 3418770, 2715625, 2157098, 1713444, 1361037, - 1081110, 858756, 682134, 541839, 430398, 341877, - 271563, 215710, 171344, 136104, 108111, 85876, - 68213, 54184, 43040, 34188, 27156, 21571, - 17134, 13610, 10811, 8588, 6821, 5418, - 4304, 3419, 2716, 2157, 1713, 1361, - 1081, 859, 682, 542, 430, 342, - 272, 216, 171, 136, 108, 86, - 68, 54, 43, 34, 27, 22, - 17, 14, 11, 9, 7, 5, - 4, 3, 3, 2, 2, 1, - 1, 1, 1, 1 -}; -const WebRtc_Word16 WebRtcCng_kCorrWindow[WEBRTC_CNG_MAX_LPC_ORDER] = { - 32702, 32636, 32570, 32505, 32439, 32374, - 32309, 32244, 32179, 32114, 32049, 31985 -}; - -/**************************************************************************** - * WebRtcCng_Version(...) - * - * These functions returns the version name (string must be at least - * 500 characters long) - * - * Output: - * - version : Pointer to character string - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_Version(WebRtc_Word8 *version) -{ - strcpy((char*)version,(const char*)"1.2.0\n"); - return(0); -} - - -/**************************************************************************** - * WebRtcCng_AssignSizeEnc/Dec(...) - * - * These functions get the size needed for storing the instance for encoder - * and decoder, respectively - * - * Input/Output: - * - sizeinbytes : Pointer to integer where the size is returned - * - * Return value : 0 - */ - -WebRtc_Word16 WebRtcCng_AssignSizeEnc(int *sizeinbytes) -{ - *sizeinbytes=sizeof(WebRtcCngEncInst_t)*2/sizeof(WebRtc_Word16); - return(0); -} - -WebRtc_Word16 WebRtcCng_AssignSizeDec(int *sizeinbytes) -{ - *sizeinbytes=sizeof(WebRtcCngDecInst_t)*2/sizeof(WebRtc_Word16); - return(0); -} - - -/**************************************************************************** - * WebRtcCng_AssignEnc/Dec(...) - * - * These functions Assignes memory for the instances. - * - * Input: - * - CNG_inst_Addr : Adress to where to assign memory - * Output: - * - inst : Pointer to the instance that should be created - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_AssignEnc(CNG_enc_inst **inst, void *CNG_inst_Addr) -{ - if (CNG_inst_Addr!=NULL) { - *inst = (CNG_enc_inst*)CNG_inst_Addr; - (*(WebRtcCngEncInst_t**) inst)->errorcode = 0; - (*(WebRtcCngEncInst_t**) inst)->initflag = 0; - return(0); - } else { - /* The memory could not be allocated */ - return(-1); - } -} - -WebRtc_Word16 WebRtcCng_AssignDec(CNG_dec_inst **inst, void *CNG_inst_Addr) -{ - if (CNG_inst_Addr!=NULL) { - *inst = (CNG_dec_inst*)CNG_inst_Addr; - (*(WebRtcCngDecInst_t**) inst)->errorcode = 0; - (*(WebRtcCngDecInst_t**) inst)->initflag = 0; - return(0); - } else { - /* The memory could not be allocated */ - return(-1); - } -} - - -/**************************************************************************** - * WebRtcCng_CreateEnc/Dec(...) - * - * These functions create an instance to the specified structure - * - * Input: - * - XXX_inst : Pointer to created instance that should be created - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_CreateEnc(CNG_enc_inst **cng_inst) -{ - *cng_inst=(CNG_enc_inst*)malloc(sizeof(WebRtcCngEncInst_t)); - if(cng_inst!=NULL) { - (*(WebRtcCngEncInst_t**) cng_inst)->errorcode = 0; - (*(WebRtcCngEncInst_t**) cng_inst)->initflag = 0; - return(0); - } - else { - /* The memory could not be allocated */ - return(-1); - } -} - -WebRtc_Word16 WebRtcCng_CreateDec(CNG_dec_inst **cng_inst) -{ - *cng_inst=(CNG_dec_inst*)malloc(sizeof(WebRtcCngDecInst_t)); - if(cng_inst!=NULL) { - (*(WebRtcCngDecInst_t**) cng_inst)->errorcode = 0; - (*(WebRtcCngDecInst_t**) cng_inst)->initflag = 0; - return(0); - } - else { - /* The memory could not be allocated */ - return(-1); - } -} - - -/**************************************************************************** - * WebRtcCng_InitEnc/Dec(...) - * - * This function initializes a instance - * - * Input: - * - cng_inst : Instance that should be initialized - * - * - fs : 8000 for narrowband and 16000 for wideband - * - interval : generate SID data every interval ms - * - quality : TBD - * - * Output: - * - cng_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ - - -WebRtc_Word16 WebRtcCng_InitEnc(CNG_enc_inst *cng_inst, - WebRtc_Word16 fs, - WebRtc_Word16 interval, - WebRtc_Word16 quality) -{ - int i; - - WebRtcCngEncInst_t* inst=(WebRtcCngEncInst_t*)cng_inst; - - memset(inst, 0, sizeof(WebRtcCngEncInst_t)); - - /* Check LPC order */ - - if (quality>WEBRTC_CNG_MAX_LPC_ORDER) { - inst->errorcode = CNG_DISALLOWED_LPC_ORDER; - return (-1); - } - - if (fs<=0) { - inst->errorcode = CNG_DISALLOWED_SAMPLING_FREQUENCY; - return (-1); - } - - inst->enc_sampfreq=fs; - inst->enc_interval=interval; - inst->enc_nrOfCoefs=quality; - inst->enc_msSinceSID=0; - inst->enc_seed=7777; /*For debugging only*/ - inst->enc_Energy=0; - for(i=0;i<(WEBRTC_CNG_MAX_LPC_ORDER+1);i++){ - inst->enc_reflCoefs[i]=0; - inst->enc_corrVector[i]=0; - } - inst->initflag=1; - - return(0); -} - -WebRtc_Word16 WebRtcCng_InitDec(CNG_dec_inst *cng_inst) -{ - int i; - - WebRtcCngDecInst_t* inst=(WebRtcCngDecInst_t*)cng_inst; - - memset(inst, 0, sizeof(WebRtcCngDecInst_t)); - inst->dec_seed=7777; /*For debugging only*/ - inst->dec_order=5; - inst->dec_target_scale_factor=0; - inst->dec_used_scale_factor=0; - for(i=0;i<(WEBRTC_CNG_MAX_LPC_ORDER+1);i++){ - inst->dec_filtstate[i]=0; - inst->dec_target_reflCoefs[i]=0; - inst->dec_used_reflCoefs[i]=0; - } - inst->dec_target_reflCoefs[0]=0; - inst->dec_used_reflCoefs[0]=0; - inst ->dec_used_energy=0; - inst->initflag=1; - - return(0); -} - -/**************************************************************************** - * WebRtcCng_FreeEnc/Dec(...) - * - * These functions frees the dynamic memory of a specified instance - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - * Return value : 0 - Ok - * -1 - Error - */ - - -WebRtc_Word16 WebRtcCng_FreeEnc(CNG_enc_inst *cng_inst) -{ - free(cng_inst); - return(0); -} - -WebRtc_Word16 WebRtcCng_FreeDec(CNG_dec_inst *cng_inst) -{ - free(cng_inst); - return(0); -} - - - -/**************************************************************************** - * WebRtcCng_Encode(...) - * - * These functions analyzes background noise - * - * Input: - * - cng_inst : Pointer to created instance - * - speech : Signal (noise) to be analyzed - * - nrOfSamples : Size of speech vector - * - bytesOut : Nr of bytes to transmit, might be 0 - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcCng_Encode(CNG_enc_inst *cng_inst, - WebRtc_Word16 *speech, - WebRtc_Word16 nrOfSamples, - WebRtc_UWord8* SIDdata, - WebRtc_Word16* bytesOut, - WebRtc_Word16 forceSID) -{ - WebRtcCngEncInst_t* inst=(WebRtcCngEncInst_t*)cng_inst; - - WebRtc_Word16 arCoefs[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word32 corrVector[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 refCs[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 hanningW[WEBRTC_CNG_MAX_OUTSIZE_ORDER]; - WebRtc_Word16 ReflBeta=19661; /*0.6 in q15*/ - WebRtc_Word16 ReflBetaComp=13107; /*0.4 in q15*/ - WebRtc_Word32 outEnergy; - int outShifts; - int i, stab; - int acorrScale; - int index; - WebRtc_Word32 diff; - WebRtc_Word16 ind,factor; - WebRtc_Word32 *bptr, blo, bhi; - WebRtc_Word16 negate; - const WebRtc_Word16 *aptr; - - WebRtc_Word16 speechBuf[WEBRTC_CNG_MAX_OUTSIZE_ORDER]; - - - /* check if encoder initiated */ - if (inst->initflag != 1) { - inst->errorcode = CNG_ENCODER_NOT_INITIATED; - return (-1); - } - - - /* check framesize */ - if (nrOfSamples>WEBRTC_CNG_MAX_OUTSIZE_ORDER) { - inst->errorcode = CNG_DISALLOWED_FRAME_SIZE; - return (-1); - } - - - for(i=0;i0){ - if(outShifts>5){ /*We can only do 5 shifts without destroying accuracy in division factor*/ - outEnergy<<=(outShifts-5); - outShifts=5; - } - else{ - factor/=2; - outShifts--; - } - } - outEnergy=WebRtcSpl_DivW32W16(outEnergy,factor); - - if (outEnergy > 1){ - /* Create Hanning Window */ - WebRtcSpl_GetHanningWindow(hanningW, nrOfSamples/2); - for( i=0;i<(nrOfSamples/2);i++ ) - hanningW[nrOfSamples-i-1]=hanningW[i]; - - WebRtcSpl_ElementwiseVectorMult(speechBuf, hanningW, speechBuf, nrOfSamples, 14); - - WebRtcSpl_AutoCorrelation( speechBuf, nrOfSamples, inst->enc_nrOfCoefs, corrVector, &acorrScale ); - - if( *corrVector==0 ) - *corrVector = WEBRTC_SPL_WORD16_MAX; - - /* Adds the bandwidth expansion */ - aptr = WebRtcCng_kCorrWindow; - bptr = corrVector; - - // (zzz) lpc16_1 = 17+1+820+2+2 = 842 (ordo2=700) - for( ind=0; indenc_nrOfCoefs; ind++ ) - { - // The below code multiplies the 16 b corrWindow values (Q15) with - // the 32 b corrvector (Q0) and shifts the result down 15 steps. - - negate = *bptr<0; - if( negate ) - *bptr = -*bptr; - - blo = (WebRtc_Word32)*aptr * (*bptr & 0xffff); - bhi = ((blo >> 16) & 0xffff) + ((WebRtc_Word32)(*aptr++) * ((*bptr >> 16) & 0xffff)); - blo = (blo & 0xffff) | ((bhi & 0xffff) << 16); - - *bptr = (( (bhi>>16) & 0x7fff) << 17) | ((WebRtc_UWord32)blo >> 15); - if( negate ) - *bptr = -*bptr; - bptr++; - } - - // end of bandwidth expansion - - stab=WebRtcSpl_LevinsonDurbin(corrVector, arCoefs, refCs, inst->enc_nrOfCoefs); - - if(!stab){ - // disregard from this frame - *bytesOut=0; - return(0); - } - - } - else { - for(i=0;ienc_nrOfCoefs; i++) - refCs[i]=0; - } - - if(forceSID){ - /*Read instantaneous values instead of averaged*/ - for(i=0;ienc_nrOfCoefs;i++) - inst->enc_reflCoefs[i]=refCs[i]; - inst->enc_Energy=outEnergy; - } - else{ - /*Average history with new values*/ - for(i=0;i<(inst->enc_nrOfCoefs);i++){ - inst->enc_reflCoefs[i]=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->enc_reflCoefs[i],ReflBeta,15); - inst->enc_reflCoefs[i]+=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(refCs[i],ReflBetaComp,15); - } - inst->enc_Energy=(outEnergy>>2)+(inst->enc_Energy>>1)+(inst->enc_Energy>>2); - } - - - if(inst->enc_Energy<1){ - inst->enc_Energy=1; - } - - if((inst->enc_msSinceSID>(inst->enc_interval-1))||forceSID){ - - /* Search for best dbov value */ - /* Clumsy linear search that can be optimized since database is sorted */ - index=0; - diff=WEBRTC_SPL_ABS_W32(inst->enc_Energy-WebRtcCng_kDbov[index]); - for(i=1;i<93;i++){ - /* Always round downwards */ - if((inst->enc_Energy-WebRtcCng_kDbov[i])>0){ - index=i; - break; - } - } - if((i==93)&&(index==0)) - index=94; - SIDdata[0]=index; - - - /* Quantize coefs with tweak for WebRtc implementation of RFC3389 */ - if(inst->enc_nrOfCoefs==WEBRTC_CNG_MAX_LPC_ORDER){ - for(i=0;ienc_nrOfCoefs;i++){ - SIDdata[i+1]=((inst->enc_reflCoefs[i]+128)>>8); /* Q15 to Q7*/ /* +127 */ - } - }else{ - for(i=0;ienc_nrOfCoefs;i++){ - SIDdata[i+1]=(127+((inst->enc_reflCoefs[i]+128)>>8)); /* Q15 to Q7*/ /* +127 */ - } - } - - inst->enc_msSinceSID=0; - *bytesOut=inst->enc_nrOfCoefs+1; - - inst->enc_msSinceSID+=(1000*nrOfSamples)/inst->enc_sampfreq; - return(inst->enc_nrOfCoefs+1); - }else{ - inst->enc_msSinceSID+=(1000*nrOfSamples)/inst->enc_sampfreq; - *bytesOut=0; - return(0); - } -} - - -/**************************************************************************** - * WebRtcCng_UpdateSid(...) - * - * These functions updates the CN state, when a new SID packet arrives - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - SID : SID packet, all headers removed - * - length : Length in bytes of SID packet - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcCng_UpdateSid(CNG_dec_inst *cng_inst, - WebRtc_UWord8 *SID, - WebRtc_Word16 length) -{ - - WebRtcCngDecInst_t* inst=(WebRtcCngDecInst_t*)cng_inst; - WebRtc_Word16 refCs[WEBRTC_CNG_MAX_LPC_ORDER]; - WebRtc_Word32 targetEnergy; - int i; - - if (inst->initflag != 1) { - inst->errorcode = CNG_DECODER_NOT_INITIATED; - return (-1); - } - - /*Throw away reflection coefficients of higher order than we can handle*/ - if(length> (WEBRTC_CNG_MAX_LPC_ORDER+1)) - length=WEBRTC_CNG_MAX_LPC_ORDER+1; - - inst->dec_order=length-1; - - if(SID[0]>93) - SID[0]=93; - targetEnergy=WebRtcCng_kDbov[SID[0]]; - /* Take down target energy to 75% */ - targetEnergy=targetEnergy>>1; - targetEnergy+=targetEnergy>>2; - - inst->dec_target_energy=targetEnergy; - - /* Reconstruct coeffs with tweak for WebRtc implementation of RFC3389 */ - if(inst->dec_order==WEBRTC_CNG_MAX_LPC_ORDER){ - for(i=0;i<(inst->dec_order);i++){ - refCs[i]=SID[i+1]<<8; /* Q7 to Q15*/ - inst->dec_target_reflCoefs[i]=refCs[i]; - } - }else{ - for(i=0;i<(inst->dec_order);i++){ - refCs[i]=(SID[i+1]-127)<<8; /* Q7 to Q15*/ - inst->dec_target_reflCoefs[i]=refCs[i]; - } - } - - for(i=(inst->dec_order);idec_target_reflCoefs[i]=refCs[i]; - } - - return(0); -} - - -/**************************************************************************** - * WebRtcCng_Generate(...) - * - * These functions generates CN data when needed - * - * Input: - * - cng_inst : Pointer to created instance that should be freed - * - outData : pointer to area to write CN data - * - nrOfSamples : How much data to generate - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcCng_Generate(CNG_dec_inst *cng_inst, - WebRtc_Word16 *outData, - WebRtc_Word16 nrOfSamples, - WebRtc_Word16 new_period) -{ - WebRtcCngDecInst_t* inst=(WebRtcCngDecInst_t*)cng_inst; - - int i; - WebRtc_Word16 excitation[WEBRTC_CNG_MAX_OUTSIZE_ORDER]; - WebRtc_Word16 low[WEBRTC_CNG_MAX_OUTSIZE_ORDER]; - WebRtc_Word16 lpPoly[WEBRTC_CNG_MAX_LPC_ORDER+1]; - WebRtc_Word16 ReflBetaStd=26214; /*0.8 in q15*/ - WebRtc_Word16 ReflBetaCompStd=6553; /*0.2in q15*/ - WebRtc_Word16 ReflBetaNewP=19661; /*0.6 in q15*/ - WebRtc_Word16 ReflBetaCompNewP=13107; /*0.4 in q15*/ - WebRtc_Word16 Beta,BetaC, tmp1, tmp2, tmp3; - WebRtc_Word32 targetEnergy; - WebRtc_Word16 En; - WebRtc_Word16 temp16; - - if (nrOfSamples>WEBRTC_CNG_MAX_OUTSIZE_ORDER) { - inst->errorcode = CNG_DISALLOWED_FRAME_SIZE; - return (-1); - } - - - if (new_period) { - inst->dec_used_scale_factor=inst->dec_target_scale_factor; - Beta=ReflBetaNewP; - BetaC=ReflBetaCompNewP; - } else { - Beta=ReflBetaStd; - BetaC=ReflBetaCompStd; - } - - /*Here we use a 0.5 weighting, should possibly be modified to 0.6*/ - tmp1=inst->dec_used_scale_factor<<2; /* Q13->Q15 */ - tmp2=inst->dec_target_scale_factor<<2; /* Q13->Q15 */ - tmp3=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp1,Beta,15); - tmp3+=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp2,BetaC,15); - inst->dec_used_scale_factor=tmp3>>2; /* Q15->Q13 */ - - inst->dec_used_energy=inst->dec_used_energy>>1; - inst->dec_used_energy+=inst->dec_target_energy>>1; - - - /* Do the same for the reflection coeffs */ - for (i=0;idec_used_reflCoefs[i]=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->dec_used_reflCoefs[i],Beta,15); - inst->dec_used_reflCoefs[i]+=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->dec_target_reflCoefs[i],BetaC,15); - } - - /* Compute the polynomial coefficients */ - WebRtcCng_K2a16(inst->dec_used_reflCoefs, WEBRTC_CNG_MAX_LPC_ORDER, lpPoly); - - /***/ - - targetEnergy=inst->dec_used_energy; - - // Calculate scaling factor based on filter energy - En=8192; //1.0 in Q13 - for (i=0; i<(WEBRTC_CNG_MAX_LPC_ORDER); i++) { - - // Floating point value for reference - // E*=1.0-((float)inst->dec_used_reflCoefs[i]/32768.0)*((float)inst->dec_used_reflCoefs[i]/32768.0); - - // Same in fixed point - temp16=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->dec_used_reflCoefs[i],inst->dec_used_reflCoefs[i],15); // K(i).^2 in Q15 - temp16=0x7fff - temp16; // 1 - K(i).^2 in Q15 - En=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(En,temp16,15); - - } - - //float scaling= sqrt(E*inst->dec_target_energy/((1<<24))); - - //Calculate sqrt(En*target_energy/exctiation energy) - - targetEnergy=WebRtcSpl_Sqrt(inst->dec_used_energy); - - En=(WebRtc_Word16)WebRtcSpl_Sqrt(En)<<6; //We are missing a factor sqrt(2) here - En=(En*3)>>1; //1.5 estimates sqrt(2) - - inst->dec_used_scale_factor=(WebRtc_Word16)((En*targetEnergy)>>12); - - - /***/ - - /*Generate excitation*/ - /*Excitation energy per sample is 2.^24 - Q13 N(0,1) */ - for(i=0;idec_seed)>>1; - } - - /*Scale to correct energy*/ - WebRtcSpl_ScaleVector(excitation, excitation, inst->dec_used_scale_factor, nrOfSamples, 13); - - WebRtcSpl_FilterAR( - lpPoly, /* Coefficients in Q12 */ - WEBRTC_CNG_MAX_LPC_ORDER+1, - excitation, /* Speech samples */ - nrOfSamples, - inst->dec_filtstate, /* State preservation */ - WEBRTC_CNG_MAX_LPC_ORDER, - inst->dec_filtstateLow, /* State preservation */ - WEBRTC_CNG_MAX_LPC_ORDER, - outData, /* Filtered speech samples */ - low, - nrOfSamples - ); - - return(0); - -} - - - -/**************************************************************************** - * WebRtcCng_GetErrorCodeEnc/Dec(...) - * - * This functions can be used to check the error code of a CNG instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occured in the - * specified instance. - * - * Input: - * - CNG_inst : CNG enc/dec instance - * - * Return value : Error code - */ - -WebRtc_Word16 WebRtcCng_GetErrorCodeEnc(CNG_enc_inst *cng_inst) -{ - - /* typecast pointer to real structure */ - WebRtcCngEncInst_t* inst=(WebRtcCngEncInst_t*)cng_inst; - - return inst->errorcode; -} - -WebRtc_Word16 WebRtcCng_GetErrorCodeDec(CNG_dec_inst *cng_inst) -{ - - /* typecast pointer to real structure */ - WebRtcCngDecInst_t* inst=(WebRtcCngDecInst_t*)cng_inst; - - return inst->errorcode; -} diff --git a/modules/audio_coding/codecs/CNG/main/test/CNG.cpp b/modules/audio_coding/codecs/CNG/main/test/CNG.cpp deleted file mode 100644 index b24783191..000000000 --- a/modules/audio_coding/codecs/CNG/main/test/CNG.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * CNG.cpp : Defines the entry point for the console application. - */ - -#include -#include -#include "stdafx.h" -#include "webrtc_cng.h" -#include "webrtc_vad.h" - -CNG_enc_inst *e_inst; -CNG_dec_inst *d_inst; - -VadInst *vinst; -//#define ASSIGN - -short anaSpeech[WEBRTC_CNG_MAX_OUTSIZE_ORDER], genSpeech[WEBRTC_CNG_MAX_OUTSIZE_ORDER], state[WEBRTC_CNG_MAX_OUTSIZE_ORDER]; -unsigned char SIDpkt[114]; - -int main(int argc, char* argv[]) -{ - FILE * infile, *outfile, *statefile; - short res=0,errtype; - /*float time=0.0;*/ - - WebRtcVad_Create(&vinst); - WebRtcVad_Init(vinst); - - short size; - int samps=0; - - if (argc < 5){ - printf("Usage:\n CNG.exe infile outfile samplingfreq(Hz) interval(ms) order\n\n"); - return(0); - } - - infile=fopen(argv[1],"rb"); - if (infile==NULL){ - printf("file %s does not exist\n",argv[1]); - return(0); - } - outfile=fopen(argv[2],"wb"); - statefile=fopen("CNGVAD.d","wb"); - if (outfile==NULL){ - printf("file %s could not be created\n",argv[2]); - return(0); - } - - unsigned int fs=16000; - short frameLen=fs/50; - -#ifndef ASSIGN - res=WebRtcCng_CreateEnc(&e_inst); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeEnc(e_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - res=WebRtcCng_CreateDec(&d_inst); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeDec(d_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - -#else - - // Test the Assign-functions - int Esize, Dsize; - void *Eaddr, *Daddr; - - res=WebRtcCng_AssignSizeEnc(&Esize); - res=WebRtcCng_AssignSizeDec(&Dsize); - Eaddr=malloc(Esize); - Daddr=malloc(Dsize); - - res=WebRtcCng_AssignEnc(&e_inst, Eaddr); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeEnc(e_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - - res=WebRtcCng_AssignDec(&d_inst, Daddr); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeDec(d_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - -#endif - - res=WebRtcCng_InitEnc(e_inst,atoi(argv[3]),atoi(argv[4]),atoi(argv[5])); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeEnc(e_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - - res=WebRtcCng_InitDec(d_inst); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeDec(d_inst); - fprintf(stderr,"\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - - - static bool firstSilent=true; - - int numSamp=0; - int speech=0; - int silent=0; - long cnt=0; - - while(fread(anaSpeech,2,frameLen,infile)==frameLen){ - - cnt++; - if (cnt==60){ - cnt=60; - } - /* time+=(float)frameLen/fs; - numSamp+=frameLen; - float temp[640]; - for(unsigned int j=0;j0){ - res=WebRtcCng_UpdateSid(d_inst,SIDpkt, size); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeDec(d_inst); - fprintf(stderr,"\n\n Error in decoder: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - } - res=WebRtcCng_Generate(d_inst,genSpeech, frameLen,0); - if (res < 0) { - /* exit if returned with error */ - errtype=WebRtcCng_GetErrorCodeDec(d_inst); - fprintf(stderr,"\n\n Error in decoder: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - memcpy(state,anaSpeech,2*frameLen); - } - else{ - firstSilent=true; - memcpy(genSpeech,anaSpeech,2*frameLen); - - memset(anaSpeech,0,frameLen*2); - memset(state,0,frameLen*2); - - } - fwrite(genSpeech,2,frameLen,outfile); - fwrite(state,2,frameLen,statefile); - - } - - fclose(infile); - fclose(outfile); - fclose(statefile); - return 0; -} - - - diff --git a/modules/audio_coding/codecs/CNG/main/test/StdAfx.cpp b/modules/audio_coding/codecs/CNG/main/test/StdAfx.cpp deleted file mode 100644 index 995e51015..000000000 --- a/modules/audio_coding/codecs/CNG/main/test/StdAfx.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// stdafx.cpp : source file that includes just the standard includes -// CNG.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/modules/audio_coding/codecs/CNG/main/test/StdAfx.h b/modules/audio_coding/codecs/CNG/main/test/StdAfx.h deleted file mode 100644 index dd6c4450b..000000000 --- a/modules/audio_coding/codecs/CNG/main/test/StdAfx.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_TEST_STDAFX_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_CNG_MAIN_TEST_STDAFX_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#include - -// TODO: reference additional headers your program requires here - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__DE2097A7_569B_42A0_A615_41BF352D6FFB__INCLUDED_) diff --git a/modules/audio_coding/codecs/G711/main/interface/g711_interface.h b/modules/audio_coding/codecs/G711/main/interface/g711_interface.h deleted file mode 100644 index 25a990333..000000000 --- a/modules/audio_coding/codecs/G711/main/interface/g711_interface.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_CODING_CODECS_G711_MAIN_INTERFACE_G711_INTERFACE_H_ -#define MODULES_AUDIO_CODING_CODECS_G711_MAIN_INTERFACE_G711_INTERFACE_H_ - -#include "typedefs.h" - -// Comfort noise constants -#define G711_WEBRTC_SPEECH 1 -#define G711_WEBRTC_CNG 2 - -#ifdef __cplusplus -extern "C" { -#endif - -/**************************************************************************** - * WebRtcG711_EncodeA(...) - * - * This function encodes a G711 A-law frame and inserts it into a packet. - * Input speech length has be of any length. - * - * Input: - * - state : Dummy state to make this codec look more like - * other codecs - * - speechIn : Input speech vector - * - len : Samples in speechIn - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error - */ - -WebRtc_Word16 WebRtcG711_EncodeA(void *state, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded); - -/**************************************************************************** - * WebRtcG711_EncodeU(...) - * - * This function encodes a G711 U-law frame and inserts it into a packet. - * Input speech length has be of any length. - * - * Input: - * - state : Dummy state to make this codec look more like - * other codecs - * - speechIn : Input speech vector - * - len : Samples in speechIn - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error - */ - -WebRtc_Word16 WebRtcG711_EncodeU(void *state, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded); - -/**************************************************************************** - * WebRtcG711_DecodeA(...) - * - * This function decodes a packet G711 A-law frame. - * - * Input: - * - state : Dummy state to make this codec look more like - * other codecs - * - encoded : Encoded data - * - len : Bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - speechType : 1 normal, 2 CNG (for G711 it should - * always return 1 since G711 does not have a - * built-in DTX/CNG scheme) - * - * Return value : >0 - Samples in decoded vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcG711_DecodeA(void *state, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - -/**************************************************************************** - * WebRtcG711_DecodeU(...) - * - * This function decodes a packet G711 U-law frame. - * - * Input: - * - state : Dummy state to make this codec look more like - * other codecs - * - encoded : Encoded data - * - len : Bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - speechType : 1 normal, 2 CNG (for G711 it should - * always return 1 since G711 does not have a - * built-in DTX/CNG scheme) - * - * Return value : >0 - Samples in decoded vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcG711_DecodeU(void *state, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - -/********************************************************************** -* WebRtcG711_Version(...) -* -* This function gives the version string of the G.711 codec. -* -* Input: -* - lenBytes: the size of Allocated space (in Bytes) where -* the version number is written to (in string format). -* -* Output: -* - version: Pointer to a buffer where the version number is -* written to. -* -*/ - -WebRtc_Word16 WebRtcG711_Version(char* version, WebRtc_Word16 lenBytes); - -#ifdef __cplusplus -} -#endif - - -#endif /* MODULES_AUDIO_CODING_CODECS_G711_MAIN_INTERFACE_G711_INTERFACE_H_ */ diff --git a/modules/audio_coding/codecs/G711/main/source/Android.mk b/modules/audio_coding/codecs/G711/main/source/Android.mk deleted file mode 100644 index 85f7129a3..000000000 --- a/modules/audio_coding/codecs/G711/main/source/Android.mk +++ /dev/null @@ -1,47 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_g711 -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := g711_interface.c \ - g711.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := -# Duplicate the static libraries to fix circular references -LOCAL_STATIC_LIBRARIES += $(LOCAL_STATIC_LIBRARIES) - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/G711/main/source/g711.c b/modules/audio_coding/codecs/G711/main/source/g711.c deleted file mode 100644 index 954f37725..000000000 --- a/modules/audio_coding/codecs/G711/main/source/g711.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * g711.c - A-law and u-law transcoding routines - * - * Written by Steve Underwood - * - * Copyright (C) 2006 Steve Underwood - * - * Despite my general liking of the GPL, I place this code in the - * public domain for the benefit of all mankind - even the slimy - * ones who might try to proprietize my work and use it to my - * detriment. - * - * $Id: g711.c,v 1.1 2006/06/07 15:46:39 steveu Exp $ - * - * Modifications for WebRtc, 2011/04/28, by tlegrand: - * -Removed unused include files - * -Changed to use WebRtc types - * -Added option to run encoder bitexact with ITU-T reference implementation - */ - -/*! \file */ - -#include "g711.h" -#include "typedefs.h" - -/* Copied from the CCITT G.711 specification */ -static const WebRtc_UWord8 ulaw_to_alaw_table[256] = -{ - 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, - 58, 59, 56, 57, 62, 63, 60, 61, 50, 51, 48, 49, 54, 55, 52, 53, - 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 26, - 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, 106, - 104, 105, 110, 111, 108, 109, 98, 99, 96, 97, 102, 103, 100, 101, 122, 120, - 126, 127, 124, 125, 114, 115, 112, 113, 118, 119, 116, 117, 75, 73, 79, 77, - 66, 67, 64, 65, 70, 71, 68, 69, 90, 91, 88, 89, 94, 95, 92, 93, - 82, 82, 83, 83, 80, 80, 81, 81, 86, 86, 87, 87, 84, 84, 85, 85, - 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, - 186, 187, 184, 185, 190, 191, 188, 189, 178, 179, 176, 177, 182, 183, 180, 181, - 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 154, - 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, 234, - 232, 233, 238, 239, 236, 237, 226, 227, 224, 225, 230, 231, 228, 229, 250, 248, - 254, 255, 252, 253, 242, 243, 240, 241, 246, 247, 244, 245, 203, 201, 207, 205, - 194, 195, 192, 193, 198, 199, 196, 197, 218, 219, 216, 217, 222, 223, 220, 221, - 210, 210, 211, 211, 208, 208, 209, 209, 214, 214, 215, 215, 212, 212, 213, 213 -}; - -/* These transcoding tables are copied from the CCITT G.711 specification. To achieve - optimal results, do not change them. */ - -static const WebRtc_UWord8 alaw_to_ulaw_table[256] = -{ - 42, 43, 40, 41, 46, 47, 44, 45, 34, 35, 32, 33, 38, 39, 36, 37, - 57, 58, 55, 56, 61, 62, 59, 60, 49, 50, 47, 48, 53, 54, 51, 52, - 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, - 26, 27, 24, 25, 30, 31, 28, 29, 18, 19, 16, 17, 22, 23, 20, 21, - 98, 99, 96, 97, 102, 103, 100, 101, 93, 93, 92, 92, 95, 95, 94, 94, - 116, 118, 112, 114, 124, 126, 120, 122, 106, 107, 104, 105, 110, 111, 108, 109, - 72, 73, 70, 71, 76, 77, 74, 75, 64, 65, 63, 63, 68, 69, 66, 67, - 86, 87, 84, 85, 90, 91, 88, 89, 79, 79, 78, 78, 82, 83, 80, 81, - 170, 171, 168, 169, 174, 175, 172, 173, 162, 163, 160, 161, 166, 167, 164, 165, - 185, 186, 183, 184, 189, 190, 187, 188, 177, 178, 175, 176, 181, 182, 179, 180, - 138, 139, 136, 137, 142, 143, 140, 141, 130, 131, 128, 129, 134, 135, 132, 133, - 154, 155, 152, 153, 158, 159, 156, 157, 146, 147, 144, 145, 150, 151, 148, 149, - 226, 227, 224, 225, 230, 231, 228, 229, 221, 221, 220, 220, 223, 223, 222, 222, - 244, 246, 240, 242, 252, 254, 248, 250, 234, 235, 232, 233, 238, 239, 236, 237, - 200, 201, 198, 199, 204, 205, 202, 203, 192, 193, 191, 191, 196, 197, 194, 195, - 214, 215, 212, 213, 218, 219, 216, 217, 207, 207, 206, 206, 210, 211, 208, 209 -}; - -WebRtc_UWord8 alaw_to_ulaw(WebRtc_UWord8 alaw) -{ - return alaw_to_ulaw_table[alaw]; -} -/*- End of function --------------------------------------------------------*/ - -WebRtc_UWord8 ulaw_to_alaw(WebRtc_UWord8 ulaw) -{ - return ulaw_to_alaw_table[ulaw]; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/modules/audio_coding/codecs/G711/main/source/g711.gyp b/modules/audio_coding/codecs/G711/main/source/g711.gyp deleted file mode 100644 index 470d07ecd..000000000 --- a/modules/audio_coding/codecs/G711/main/source/g711.gyp +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'G711', - 'type': '<(library)', - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/g711_interface.h', - 'g711_interface.c', - 'g711.c', - 'g711.h', - ], - }, - - { - 'target_name': 'g711_test', - 'type': 'executable', - 'dependencies': [ - 'G711', - ], - 'sources': [ - '../testG711/testG711.cpp', - ], - # 'conditions': [ - # ['OS=="linux"', { - # 'cflags': [ - # '-fexceptions', # enable exceptions - # ], - # }], - # ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/G711/main/source/g711.h b/modules/audio_coding/codecs/G711/main/source/g711.h deleted file mode 100644 index cd5e3d798..000000000 --- a/modules/audio_coding/codecs/G711/main/source/g711.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * g711.h - In line A-law and u-law conversion routines - * - * Written by Steve Underwood - * - * Copyright (C) 2001 Steve Underwood - * - * Despite my general liking of the GPL, I place this code in the - * public domain for the benefit of all mankind - even the slimy - * ones who might try to proprietize my work and use it to my - * detriment. - * - * $Id: g711.h,v 1.1 2006/06/07 15:46:39 steveu Exp $ - * - * Modifications for WebRtc, 2011/04/28, by tlegrand: - * -Changed to use WebRtc types - * -Changed __inline__ to __inline - * -Two changes to make implementation bitexact with ITU-T reference implementation - */ - -/*! \file */ - -/*! \page g711_page A-law and mu-law handling -Lookup tables for A-law and u-law look attractive, until you consider the impact -on the CPU cache. If it causes a substantial area of your processor cache to get -hit too often, cache sloshing will severely slow things down. The main reason -these routines are slow in C, is the lack of direct access to the CPU's "find -the first 1" instruction. A little in-line assembler fixes that, and the -conversion routines can be faster than lookup tables, in most real world usage. -A "find the first 1" instruction is available on most modern CPUs, and is a -much underused feature. - -If an assembly language method of bit searching is not available, these routines -revert to a method that can be a little slow, so the cache thrashing might not -seem so bad :( - -Feel free to submit patches to add fast "find the first 1" support for your own -favourite processor. - -Look up tables are used for transcoding between A-law and u-law, since it is -difficult to achieve the precise transcoding procedure laid down in the G.711 -specification by other means. -*/ - -#if !defined(_G711_H_) -#define _G711_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "typedefs.h" - -#if defined(__i386__) -/*! \brief Find the bit position of the highest set bit in a word - \param bits The word to be searched - \return The bit number of the highest set bit, or -1 if the word is zero. */ -static __inline__ int top_bit(unsigned int bits) -{ - int res; - - __asm__ __volatile__(" movl $-1,%%edx;\n" - " bsrl %%eax,%%edx;\n" - : "=d" (res) - : "a" (bits)); - return res; -} -/*- End of function --------------------------------------------------------*/ - -/*! \brief Find the bit position of the lowest set bit in a word - \param bits The word to be searched - \return The bit number of the lowest set bit, or -1 if the word is zero. */ -static __inline__ int bottom_bit(unsigned int bits) -{ - int res; - - __asm__ __volatile__(" movl $-1,%%edx;\n" - " bsfl %%eax,%%edx;\n" - : "=d" (res) - : "a" (bits)); - return res; -} -/*- End of function --------------------------------------------------------*/ -#elif defined(__x86_64__) -static __inline__ int top_bit(unsigned int bits) -{ - int res; - - __asm__ __volatile__(" movq $-1,%%rdx;\n" - " bsrq %%rax,%%rdx;\n" - : "=d" (res) - : "a" (bits)); - return res; -} -/*- End of function --------------------------------------------------------*/ - -static __inline__ int bottom_bit(unsigned int bits) -{ - int res; - - __asm__ __volatile__(" movq $-1,%%rdx;\n" - " bsfq %%rax,%%rdx;\n" - : "=d" (res) - : "a" (bits)); - return res; -} -/*- End of function --------------------------------------------------------*/ -#else -static __inline int top_bit(unsigned int bits) -{ - int i; - - if (bits == 0) - return -1; - i = 0; - if (bits & 0xFFFF0000) - { - bits &= 0xFFFF0000; - i += 16; - } - if (bits & 0xFF00FF00) - { - bits &= 0xFF00FF00; - i += 8; - } - if (bits & 0xF0F0F0F0) - { - bits &= 0xF0F0F0F0; - i += 4; - } - if (bits & 0xCCCCCCCC) - { - bits &= 0xCCCCCCCC; - i += 2; - } - if (bits & 0xAAAAAAAA) - { - bits &= 0xAAAAAAAA; - i += 1; - } - return i; -} -/*- End of function --------------------------------------------------------*/ - -static __inline int bottom_bit(unsigned int bits) -{ - int i; - - if (bits == 0) - return -1; - i = 32; - if (bits & 0x0000FFFF) - { - bits &= 0x0000FFFF; - i -= 16; - } - if (bits & 0x00FF00FF) - { - bits &= 0x00FF00FF; - i -= 8; - } - if (bits & 0x0F0F0F0F) - { - bits &= 0x0F0F0F0F; - i -= 4; - } - if (bits & 0x33333333) - { - bits &= 0x33333333; - i -= 2; - } - if (bits & 0x55555555) - { - bits &= 0x55555555; - i -= 1; - } - return i; -} -/*- End of function --------------------------------------------------------*/ -#endif - -/* N.B. It is tempting to use look-up tables for A-law and u-law conversion. - * However, you should consider the cache footprint. - * - * A 64K byte table for linear to x-law and a 512 byte table for x-law to - * linear sound like peanuts these days, and shouldn't an array lookup be - * real fast? No! When the cache sloshes as badly as this one will, a tight - * calculation may be better. The messiest part is normally finding the - * segment, but a little inline assembly can fix that on an i386, x86_64 and - * many other modern processors. - */ - -/* - * Mu-law is basically as follows: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ - -//#define ULAW_ZEROTRAP /* turn on the trap as per the MIL-STD */ -#define ULAW_BIAS 0x84 /* Bias for linear code. */ - -/*! \brief Encode a linear sample to u-law - \param linear The sample to encode. - \return The u-law value. -*/ -static __inline WebRtc_UWord8 linear_to_ulaw(int linear) -{ - WebRtc_UWord8 u_val; - int mask; - int seg; - - /* Get the sign and the magnitude of the value. */ - if (linear < 0) - { - /* WebRtc, tlegrand: -1 added to get bitexact to reference implementation */ - linear = ULAW_BIAS - linear - 1; - mask = 0x7F; - } - else - { - linear = ULAW_BIAS + linear; - mask = 0xFF; - } - - seg = top_bit(linear | 0xFF) - 7; - - /* - * Combine the sign, segment, quantization bits, - * and complement the code word. - */ - if (seg >= 8) - u_val = (WebRtc_UWord8) (0x7F ^ mask); - else - u_val = (WebRtc_UWord8) (((seg << 4) | ((linear >> (seg + 3)) & 0xF)) ^ mask); -#ifdef ULAW_ZEROTRAP - /* Optional ITU trap */ - if (u_val == 0) - u_val = 0x02; -#endif - return u_val; -} -/*- End of function --------------------------------------------------------*/ - -/*! \brief Decode an u-law sample to a linear value. - \param ulaw The u-law sample to decode. - \return The linear value. -*/ -static __inline WebRtc_Word16 ulaw_to_linear(WebRtc_UWord8 ulaw) -{ - int t; - - /* Complement to obtain normal u-law value. */ - ulaw = ~ulaw; - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = (((ulaw & 0x0F) << 3) + ULAW_BIAS) << (((int) ulaw & 0x70) >> 4); - return (WebRtc_Word16) ((ulaw & 0x80) ? (ULAW_BIAS - t) : (t - ULAW_BIAS)); -} -/*- End of function --------------------------------------------------------*/ - -/* - * A-law is basically as follows: - * - * Linear Input Code Compressed Code - * ----------------- --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ - -#define ALAW_AMI_MASK 0x55 - -/*! \brief Encode a linear sample to A-law - \param linear The sample to encode. - \return The A-law value. -*/ -static __inline WebRtc_UWord8 linear_to_alaw(int linear) -{ - int mask; - int seg; - - if (linear >= 0) - { - /* Sign (bit 7) bit = 1 */ - mask = ALAW_AMI_MASK | 0x80; - } - else - { - /* Sign (bit 7) bit = 0 */ - mask = ALAW_AMI_MASK; - /* WebRtc, tlegrand: Changed from -8 to -1 to get bitexact to reference - * implementation */ - linear = -linear - 1; - } - - /* Convert the scaled magnitude to segment number. */ - seg = top_bit(linear | 0xFF) - 7; - if (seg >= 8) - { - if (linear >= 0) - { - /* Out of range. Return maximum value. */ - return (WebRtc_UWord8) (0x7F ^ mask); - } - /* We must be just a tiny step below zero */ - return (WebRtc_UWord8) (0x00 ^ mask); - } - /* Combine the sign, segment, and quantization bits. */ - return (WebRtc_UWord8) (((seg << 4) | ((linear >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask); -} -/*- End of function --------------------------------------------------------*/ - -/*! \brief Decode an A-law sample to a linear value. - \param alaw The A-law sample to decode. - \return The linear value. -*/ -static __inline WebRtc_Word16 alaw_to_linear(WebRtc_UWord8 alaw) -{ - int i; - int seg; - - alaw ^= ALAW_AMI_MASK; - i = ((alaw & 0x0F) << 4); - seg = (((int) alaw & 0x70) >> 4); - if (seg) - i = (i + 0x108) << (seg - 1); - else - i += 8; - return (WebRtc_Word16) ((alaw & 0x80) ? i : -i); -} -/*- End of function --------------------------------------------------------*/ - -/*! \brief Transcode from A-law to u-law, using the procedure defined in G.711. - \param alaw The A-law sample to transcode. - \return The best matching u-law value. -*/ -WebRtc_UWord8 alaw_to_ulaw(WebRtc_UWord8 alaw); - -/*! \brief Transcode from u-law to A-law, using the procedure defined in G.711. - \param alaw The u-law sample to transcode. - \return The best matching A-law value. -*/ -WebRtc_UWord8 ulaw_to_alaw(WebRtc_UWord8 ulaw); - -#ifdef __cplusplus -} -#endif - -#endif -/*- End of file ------------------------------------------------------------*/ diff --git a/modules/audio_coding/codecs/G711/main/source/g711_interface.c b/modules/audio_coding/codecs/G711/main/source/g711_interface.c deleted file mode 100644 index 5acd43164..000000000 --- a/modules/audio_coding/codecs/G711/main/source/g711_interface.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#include -#include "g711.h" -#include "g711_interface.h" -#include "typedefs.h" - -WebRtc_Word16 WebRtcG711_EncodeA(void *state, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded) -{ - int n; - WebRtc_UWord16 tempVal, tempVal2; - - // Set to avoid getting warnings - state = state; - - // Sanity check of input length - if (len < 0) { - return (-1); - } - - // Loop over all samples - for (n = 0; n < len; n++) { - tempVal = (WebRtc_UWord16)linear_to_alaw(speechIn[n]); - -#ifdef WEBRTC_BIG_ENDIAN - if ((n & 0x1) == 1) { - encoded[n>>1]|=((WebRtc_UWord16)tempVal); - } else { - encoded[n>>1]=((WebRtc_UWord16)tempVal)<<8; - } -#else - if ((n & 0x1) == 1) { - tempVal2 |= ((WebRtc_UWord16) tempVal) << 8; - encoded[n >> 1] |= ((WebRtc_UWord16) tempVal) << 8; - } else { - tempVal2 = ((WebRtc_UWord16) tempVal); - encoded[n >> 1] = ((WebRtc_UWord16) tempVal); - } -#endif - } - return (len); -} - -WebRtc_Word16 WebRtcG711_EncodeU(void *state, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded) -{ - int n; - WebRtc_UWord16 tempVal; - - // Set to avoid getting warnings - state = state; - - // Sanity check of input length - if (len < 0) { - return (-1); - } - - // Loop over all samples - for (n = 0; n < len; n++) { - tempVal = (WebRtc_UWord16)linear_to_ulaw(speechIn[n]); - - #ifdef WEBRTC_BIG_ENDIAN - if ((n & 0x1) == 1) { - encoded[n>>1]|=((WebRtc_UWord16)tempVal); - } else { - encoded[n>>1]=((WebRtc_UWord16)tempVal)<<8; - } - #else - if ((n & 0x1) == 1) { - encoded[n >> 1] |= ((WebRtc_UWord16) tempVal) << 8; - } else { - encoded[n >> 1] = ((WebRtc_UWord16) tempVal); - } - #endif - } - return (len); -} - -WebRtc_Word16 WebRtcG711_DecodeA(void *state, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - int n; - WebRtc_UWord16 tempVal; - - // Set to avoid getting warnings - state = state; - - // Sanity check of input length - if (len < 0) { - return (-1); - } - - for (n = 0; n < len; n++) { - #ifdef WEBRTC_BIG_ENDIAN - if ((n & 0x1) == 1) { - tempVal=((WebRtc_UWord16)encoded[n>>1] & 0xFF); - } else { - tempVal=((WebRtc_UWord16)encoded[n>>1] >> 8); - } - #else - if ((n & 0x1) == 1) { - tempVal = (encoded[n >> 1] >> 8); - } else { - tempVal = (encoded[n >> 1] & 0xFF); - } - #endif - decoded[n] = (WebRtc_Word16) alaw_to_linear(tempVal); - } - - *speechType = 1; - return (len); -} - -WebRtc_Word16 WebRtcG711_DecodeU(void *state, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - int n; - WebRtc_UWord16 tempVal; - - // Set to avoid getting warnings - state = state; - - // Sanity check of input length - if (len < 0) { - return (-1); - } - - for (n = 0; n < len; n++) { - #ifdef WEBRTC_BIG_ENDIAN - if ((n & 0x1) == 1) { - tempVal=((WebRtc_UWord16)encoded[n>>1] & 0xFF); - } else { - tempVal=((WebRtc_UWord16)encoded[n>>1] >> 8); - } - #else - if ((n & 0x1) == 1) { - tempVal = (encoded[n >> 1] >> 8); - } else { - tempVal = (encoded[n >> 1] & 0xFF); - } - #endif - decoded[n] = (WebRtc_Word16) ulaw_to_linear(tempVal); - } - - *speechType = 1; - return (len); -} - -WebRtc_Word16 WebRtcG711_Version(char* version, WebRtc_Word16 lenBytes) -{ - strncpy(version, "2.0.0", lenBytes); - return 0; -} diff --git a/modules/audio_coding/codecs/G711/main/testG711/testG711.cpp b/modules/audio_coding/codecs/G711/main/testG711/testG711.cpp deleted file mode 100644 index 09d79f60f..000000000 --- a/modules/audio_coding/codecs/G711/main/testG711/testG711.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * testG711.cpp : Defines the entry point for the console application. - */ - -#include -#include -#include - -/* include API */ -#include "g711_interface.h" - -/* Runtime statistics */ -#include -#define CLOCKS_PER_SEC_G711 1000 - - -/* function for reading audio data from PCM file */ -int readframe(WebRtc_Word16 *data, FILE *inp, int length) { - - short k, rlen, status = 0; - - rlen = (short)fread(data, sizeof(WebRtc_Word16), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - return status; -} - -int main(int argc, char* argv[]) -{ - char inname[80], outname[40], bitname[40]; - FILE *inp, *outp, *bitp; - int framecnt, endfile; - - WebRtc_Word16 framelength = 80; - - int err; - - /* Runtime statistics */ - double starttime; - double runtime; - double length_file; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 shortdata[480]; - WebRtc_Word16 decoded[480]; - WebRtc_Word16 decoded2[480]; - WebRtc_Word16 streamdata[500]; - WebRtc_Word16 speechType[1]; - char law[2]; - char versionNumber[40]; - - /* handling wrong input arguments in the command line */ - if ((argc!=5) && (argc!=6)) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - printf("\nG.711 test application\n\n"); - printf("Usage:\n\n"); - printf("./testG711.exe framelength law infile outfile \n\n"); - printf("framelength: Framelength in samples.\n"); - printf("law : Coding law, A och u.\n"); - printf("infile : Normal speech input file\n"); - printf("outfile : Speech output file\n\n"); - printf("outbits : Output bitstream file [optional]\n\n"); - exit(0); - - } - - /* Get version and print */ - WebRtcG711_Version(versionNumber, 40); - - printf("-----------------------------------\n"); - printf("G.711 version: %s\n\n", versionNumber); - /* Get frame length */ - framelength = atoi(argv[1]); - - /* Get compression law */ - strcpy(law, argv[2]); - - /* Get Input and Output files */ - sscanf(argv[3], "%s", inname); - sscanf(argv[4], "%s", outname); - if (argc==6) { - sscanf(argv[5], "%s", bitname); - if ((bitp = fopen(bitname,"wb")) == NULL) { - printf(" G.711: Cannot read file %s.\n", bitname); - exit(1); - } - } - - if ((inp = fopen(inname,"rb")) == NULL) { - printf(" G.711: Cannot read file %s.\n", inname); - exit(1); - } - if ((outp = fopen(outname,"wb")) == NULL) { - printf(" G.711: Cannot write file %s.\n", outname); - exit(1); - } - printf("\nInput: %s\nOutput: %s\n", inname, outname); - if (argc==6) { - printf("\nBitfile: %s\n", bitname); - } - - starttime = clock()/(double)CLOCKS_PER_SEC_G711; /* Runtime statistics */ - - /* Initialize encoder and decoder */ - framecnt= 0; - endfile = 0; - while (endfile == 0) { - framecnt++; - /* Read speech block */ - endfile = readframe(shortdata, inp, framelength); - - /* G.711 encoding */ - if (!strcmp(law,"A")) { - /* A-law encoding */ - stream_len = WebRtcG711_EncodeA(NULL, shortdata, framelength, streamdata); - if (argc==6){ - /* Write bits to file */ - fwrite(streamdata,sizeof(unsigned char),stream_len,bitp); - } - err = WebRtcG711_DecodeA(NULL, streamdata, stream_len, decoded, speechType); - } else if (!strcmp(law,"u")){ - /* u-law encoding */ - stream_len = WebRtcG711_EncodeU(NULL, shortdata, framelength, streamdata); - if (argc==6){ - /* Write bits to file */ - fwrite(streamdata,sizeof(unsigned char),stream_len,bitp); - } - err = WebRtcG711_DecodeU(NULL, streamdata, stream_len, decoded, speechType); - } else { - printf("Wrong law mode\n"); - exit (1); - } - if (stream_len < 0 || err < 0) { - /* exit if returned with error */ - printf("Error in encoder/decoder\n"); - } else { - /* Write coded speech to file */ - fwrite(decoded,sizeof(short),framelength,outp); - } - } - - - runtime = (double)(clock()/(double)CLOCKS_PER_SEC_G711-starttime); - length_file = ((double)framecnt*(double)framelength/8000); - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run G.711: %.2f s (%.2f %% of realtime)\n\n", runtime, (100*runtime/length_file)); - printf("---------------------END----------------------\n"); - - fclose(inp); - fclose(outp); - - - return 0; -} - diff --git a/modules/audio_coding/codecs/G722/main/interface/g722_interface.h b/modules/audio_coding/codecs/G722/main/interface/g722_interface.h deleted file mode 100644 index e50d66f56..000000000 --- a/modules/audio_coding/codecs/G722/main/interface/g722_interface.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MODULES_AUDIO_CODING_CODECS_G722_MAIN_INTERFACE_G722_INTERFACE_H_ -#define MODULES_AUDIO_CODING_CODECS_G722_MAIN_INTERFACE_G722_INTERFACE_H_ - -#include "typedefs.h" - -/* - * Solution to support multiple instances - */ - -typedef struct WebRtcG722EncInst G722EncInst; -typedef struct WebRtcG722DecInst G722DecInst; - -/* - * Comfort noise constants - */ - -#define G722_WEBRTC_SPEECH 1 -#define G722_WEBRTC_CNG 2 - -#ifdef __cplusplus -extern "C" { -#endif - - -/**************************************************************************** - * WebRtcG722_CreateEncoder(...) - * - * Create memory used for G722 encoder - * - * Input: - * - G722enc_inst : G722 instance for encoder - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcG722_CreateEncoder(G722EncInst **G722enc_inst); - - -/**************************************************************************** - * WebRtcG722_EncoderInit(...) - * - * This function initializes a G722 instance - * - * Input: - * - G722enc_inst : G722 instance, i.e. the user that should receive - * be initialized - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcG722_EncoderInit(G722EncInst *G722enc_inst); - - -/**************************************************************************** - * WebRtcG722_FreeEncoder(...) - * - * Free the memory used for G722 encoder - * - * Input: - * - G722enc_inst : G722 instance for encoder - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcG722_FreeEncoder(G722EncInst *G722enc_inst); - - - -/**************************************************************************** - * WebRtcG722_Encode(...) - * - * This function encodes G722 encoded data. - * - * Input: - * - G722enc_inst : G722 instance, i.e. the user that should encode - * a packet - * - speechIn : Input speech vector - * - len : Samples in speechIn - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error - */ - -WebRtc_Word16 WebRtcG722_Encode(G722EncInst *G722enc_inst, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded); - - -/**************************************************************************** - * WebRtcG722_CreateDecoder(...) - * - * Create memory used for G722 encoder - * - * Input: - * - G722dec_inst : G722 instance for decoder - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcG722_CreateDecoder(G722DecInst **G722dec_inst); - - -/**************************************************************************** - * WebRtcG722_DecoderInit(...) - * - * This function initializes a G729 instance - * - * Input: - * - G729_decinst_t : G729 instance, i.e. the user that should receive - * be initialized - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcG722_DecoderInit(G722DecInst *G722dec_inst); - - -/**************************************************************************** - * WebRtcG722_FreeDecoder(...) - * - * Free the memory used for G722 decoder - * - * Input: - * - G722dec_inst : G722 instance for decoder - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcG722_FreeDecoder(G722DecInst *G722dec_inst); - - -/**************************************************************************** - * WebRtcG722_Decode(...) - * - * This function decodes a packet with G729 frame(s). Output speech length - * will be a multiple of 80 samples (80*frames/packet). - * - * Input: - * - G722dec_inst : G722 instance, i.e. the user that should decode - * a packet - * - encoded : Encoded G722 frame(s) - * - len : Bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - speechType : 1 normal, 2 CNG (Since G722 does not have its own - * DTX/CNG scheme it should always return 1) - * - * Return value : >0 - Samples in decoded vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcG722_Decode(G722DecInst *G722dec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - -/**************************************************************************** - * WebRtcG722_Version(...) - * - * Get a string with the current version of the codec - */ - -WebRtc_Word16 WebRtcG722_Version(char *versionStr, short len); - - -#ifdef __cplusplus -} -#endif - - -#endif /* MODULES_AUDIO_CODING_CODECS_G722_MAIN_INTERFACE_G722_INTERFACE_H_ */ diff --git a/modules/audio_coding/codecs/G722/main/source/Android.mk b/modules/audio_coding/codecs/G722/main/source/Android.mk deleted file mode 100644 index c67c2bcab..000000000 --- a/modules/audio_coding/codecs/G722/main/source/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_g722 -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := g722_interface.c \ - g722_encode.c \ - g722_decode.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/G722/main/source/g722.gyp b/modules/audio_coding/codecs/G722/main/source/g722.gyp deleted file mode 100644 index fa6b95f00..000000000 --- a/modules/audio_coding/codecs/G722/main/source/g722.gyp +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'G722', - 'type': '<(library)', - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/g722_interface.h', - 'g722_interface.c', - 'g722_encode.c', - 'g722_decode.c', - 'g722_enc_dec.h', - ], - }, - { - 'target_name': 'G722Test', - 'type': 'executable', - 'dependencies': [ - 'G722', - ], - 'sources': [ - '../testG722/testG722.cpp', - ], - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }], - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/G722/main/source/g722_decode.c b/modules/audio_coding/codecs/G722/main/source/g722_decode.c deleted file mode 100644 index 898190350..000000000 --- a/modules/audio_coding/codecs/G722/main/source/g722_decode.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * g722_decode.c - The ITU G.722 codec, decode part. - * - * Written by Steve Underwood - * - * Copyright (C) 2005 Steve Underwood - * - * Despite my general liking of the GPL, I place my own contributions - * to this code in the public domain for the benefit of all mankind - - * even the slimy ones who might try to proprietize my work and use it - * to my detriment. - * - * Based in part on a single channel G.722 codec which is: - * - * Copyright (c) CMU 1993 - * Computer Science, Speech Group - * Chengxiang Lu and Alex Hauptmann - * - * $Id: g722_decode.c,v 1.15 2006/07/07 16:37:49 steveu Exp $ - * - * Modifications for WebRtc, 2011/04/28, by tlegrand: - * -Removed usage of inttypes.h and tgmath.h - * -Changed to use WebRtc types - * -Changed __inline__ to __inline - * -Added saturation check on output - */ - -/*! \file */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "typedefs.h" -#include "g722_enc_dec.h" - - -#if !defined(FALSE) -#define FALSE 0 -#endif -#if !defined(TRUE) -#define TRUE (!FALSE) -#endif - -static __inline WebRtc_Word16 saturate(WebRtc_Word32 amp) -{ - WebRtc_Word16 amp16; - - /* Hopefully this is optimised for the common case - not clipping */ - amp16 = (WebRtc_Word16) amp; - if (amp == amp16) - return amp16; - if (amp > WEBRTC_INT16_MAX) - return WEBRTC_INT16_MAX; - return WEBRTC_INT16_MIN; -} -/*- End of function --------------------------------------------------------*/ - -static void block4(g722_decode_state_t *s, int band, int d); - -static void block4(g722_decode_state_t *s, int band, int d) -{ - int wd1; - int wd2; - int wd3; - int i; - - /* Block 4, RECONS */ - s->band[band].d[0] = d; - s->band[band].r[0] = saturate(s->band[band].s + d); - - /* Block 4, PARREC */ - s->band[band].p[0] = saturate(s->band[band].sz + d); - - /* Block 4, UPPOL2 */ - for (i = 0; i < 3; i++) - s->band[band].sg[i] = s->band[band].p[i] >> 15; - wd1 = saturate(s->band[band].a[1] << 2); - - wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1; - if (wd2 > 32767) - wd2 = 32767; - wd3 = (s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128; - wd3 += (wd2 >> 7); - wd3 += (s->band[band].a[2]*32512) >> 15; - if (wd3 > 12288) - wd3 = 12288; - else if (wd3 < -12288) - wd3 = -12288; - s->band[band].ap[2] = wd3; - - /* Block 4, UPPOL1 */ - s->band[band].sg[0] = s->band[band].p[0] >> 15; - s->band[band].sg[1] = s->band[band].p[1] >> 15; - wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192; - wd2 = (s->band[band].a[1]*32640) >> 15; - - s->band[band].ap[1] = saturate(wd1 + wd2); - wd3 = saturate(15360 - s->band[band].ap[2]); - if (s->band[band].ap[1] > wd3) - s->band[band].ap[1] = wd3; - else if (s->band[band].ap[1] < -wd3) - s->band[band].ap[1] = -wd3; - - /* Block 4, UPZERO */ - wd1 = (d == 0) ? 0 : 128; - s->band[band].sg[0] = d >> 15; - for (i = 1; i < 7; i++) - { - s->band[band].sg[i] = s->band[band].d[i] >> 15; - wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1; - wd3 = (s->band[band].b[i]*32640) >> 15; - s->band[band].bp[i] = saturate(wd2 + wd3); - } - - /* Block 4, DELAYA */ - for (i = 6; i > 0; i--) - { - s->band[band].d[i] = s->band[band].d[i - 1]; - s->band[band].b[i] = s->band[band].bp[i]; - } - - for (i = 2; i > 0; i--) - { - s->band[band].r[i] = s->band[band].r[i - 1]; - s->band[band].p[i] = s->band[band].p[i - 1]; - s->band[band].a[i] = s->band[band].ap[i]; - } - - /* Block 4, FILTEP */ - wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]); - wd1 = (s->band[band].a[1]*wd1) >> 15; - wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]); - wd2 = (s->band[band].a[2]*wd2) >> 15; - s->band[band].sp = saturate(wd1 + wd2); - - /* Block 4, FILTEZ */ - s->band[band].sz = 0; - for (i = 6; i > 0; i--) - { - wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]); - s->band[band].sz += (s->band[band].b[i]*wd1) >> 15; - } - s->band[band].sz = saturate(s->band[band].sz); - - /* Block 4, PREDIC */ - s->band[band].s = saturate(s->band[band].sp + s->band[band].sz); -} -/*- End of function --------------------------------------------------------*/ - -g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options) -{ - if (s == NULL) - { - if ((s = (g722_decode_state_t *) malloc(sizeof(*s))) == NULL) - return NULL; - } - memset(s, 0, sizeof(*s)); - if (rate == 48000) - s->bits_per_sample = 6; - else if (rate == 56000) - s->bits_per_sample = 7; - else - s->bits_per_sample = 8; - if ((options & G722_SAMPLE_RATE_8000)) - s->eight_k = TRUE; - if ((options & G722_PACKED) && s->bits_per_sample != 8) - s->packed = TRUE; - else - s->packed = FALSE; - s->band[0].det = 32; - s->band[1].det = 8; - return s; -} -/*- End of function --------------------------------------------------------*/ - -int g722_decode_release(g722_decode_state_t *s) -{ - free(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ - -int g722_decode(g722_decode_state_t *s, WebRtc_Word16 amp[], - const WebRtc_UWord8 g722_data[], int len) -{ - static const int wl[8] = {-60, -30, 58, 172, 334, 538, 1198, 3042 }; - static const int rl42[16] = {0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 }; - static const int ilb[32] = - { - 2048, 2093, 2139, 2186, 2233, 2282, 2332, - 2383, 2435, 2489, 2543, 2599, 2656, 2714, - 2774, 2834, 2896, 2960, 3025, 3091, 3158, - 3228, 3298, 3371, 3444, 3520, 3597, 3676, - 3756, 3838, 3922, 4008 - }; - static const int wh[3] = {0, -214, 798}; - static const int rh2[4] = {2, 1, 2, 1}; - static const int qm2[4] = {-7408, -1616, 7408, 1616}; - static const int qm4[16] = - { - 0, -20456, -12896, -8968, - -6288, -4240, -2584, -1200, - 20456, 12896, 8968, 6288, - 4240, 2584, 1200, 0 - }; - static const int qm5[32] = - { - -280, -280, -23352, -17560, - -14120, -11664, -9752, -8184, - -6864, -5712, -4696, -3784, - -2960, -2208, -1520, -880, - 23352, 17560, 14120, 11664, - 9752, 8184, 6864, 5712, - 4696, 3784, 2960, 2208, - 1520, 880, 280, -280 - }; - static const int qm6[64] = - { - -136, -136, -136, -136, - -24808, -21904, -19008, -16704, - -14984, -13512, -12280, -11192, - -10232, -9360, -8576, -7856, - -7192, -6576, -6000, -5456, - -4944, -4464, -4008, -3576, - -3168, -2776, -2400, -2032, - -1688, -1360, -1040, -728, - 24808, 21904, 19008, 16704, - 14984, 13512, 12280, 11192, - 10232, 9360, 8576, 7856, - 7192, 6576, 6000, 5456, - 4944, 4464, 4008, 3576, - 3168, 2776, 2400, 2032, - 1688, 1360, 1040, 728, - 432, 136, -432, -136 - }; - static const int qmf_coeffs[12] = - { - 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, - }; - - int dlowt; - int rlow; - int ihigh; - int dhigh; - int rhigh; - int xout1; - int xout2; - int wd1; - int wd2; - int wd3; - int code; - int outlen; - int i; - int j; - - outlen = 0; - rhigh = 0; - for (j = 0; j < len; ) - { - if (s->packed) - { - /* Unpack the code bits */ - if (s->in_bits < s->bits_per_sample) - { - s->in_buffer |= (g722_data[j++] << s->in_bits); - s->in_bits += 8; - } - code = s->in_buffer & ((1 << s->bits_per_sample) - 1); - s->in_buffer >>= s->bits_per_sample; - s->in_bits -= s->bits_per_sample; - } - else - { - code = g722_data[j++]; - } - - switch (s->bits_per_sample) - { - default: - case 8: - wd1 = code & 0x3F; - ihigh = (code >> 6) & 0x03; - wd2 = qm6[wd1]; - wd1 >>= 2; - break; - case 7: - wd1 = code & 0x1F; - ihigh = (code >> 5) & 0x03; - wd2 = qm5[wd1]; - wd1 >>= 1; - break; - case 6: - wd1 = code & 0x0F; - ihigh = (code >> 4) & 0x03; - wd2 = qm4[wd1]; - break; - } - /* Block 5L, LOW BAND INVQBL */ - wd2 = (s->band[0].det*wd2) >> 15; - /* Block 5L, RECONS */ - rlow = s->band[0].s + wd2; - /* Block 6L, LIMIT */ - if (rlow > 16383) - rlow = 16383; - else if (rlow < -16384) - rlow = -16384; - - /* Block 2L, INVQAL */ - wd2 = qm4[wd1]; - dlowt = (s->band[0].det*wd2) >> 15; - - /* Block 3L, LOGSCL */ - wd2 = rl42[wd1]; - wd1 = (s->band[0].nb*127) >> 7; - wd1 += wl[wd2]; - if (wd1 < 0) - wd1 = 0; - else if (wd1 > 18432) - wd1 = 18432; - s->band[0].nb = wd1; - - /* Block 3L, SCALEL */ - wd1 = (s->band[0].nb >> 6) & 31; - wd2 = 8 - (s->band[0].nb >> 11); - wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); - s->band[0].det = wd3 << 2; - - block4(s, 0, dlowt); - - if (!s->eight_k) - { - /* Block 2H, INVQAH */ - wd2 = qm2[ihigh]; - dhigh = (s->band[1].det*wd2) >> 15; - /* Block 5H, RECONS */ - rhigh = dhigh + s->band[1].s; - /* Block 6H, LIMIT */ - if (rhigh > 16383) - rhigh = 16383; - else if (rhigh < -16384) - rhigh = -16384; - - /* Block 2H, INVQAH */ - wd2 = rh2[ihigh]; - wd1 = (s->band[1].nb*127) >> 7; - wd1 += wh[wd2]; - if (wd1 < 0) - wd1 = 0; - else if (wd1 > 22528) - wd1 = 22528; - s->band[1].nb = wd1; - - /* Block 3H, SCALEH */ - wd1 = (s->band[1].nb >> 6) & 31; - wd2 = 10 - (s->band[1].nb >> 11); - wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); - s->band[1].det = wd3 << 2; - - block4(s, 1, dhigh); - } - - if (s->itu_test_mode) - { - amp[outlen++] = (WebRtc_Word16) (rlow << 1); - amp[outlen++] = (WebRtc_Word16) (rhigh << 1); - } - else - { - if (s->eight_k) - { - amp[outlen++] = (WebRtc_Word16) (rlow << 1); - } - else - { - /* Apply the receive QMF */ - for (i = 0; i < 22; i++) - s->x[i] = s->x[i + 2]; - s->x[22] = rlow + rhigh; - s->x[23] = rlow - rhigh; - - xout1 = 0; - xout2 = 0; - for (i = 0; i < 12; i++) - { - xout2 += s->x[2*i]*qmf_coeffs[i]; - xout1 += s->x[2*i + 1]*qmf_coeffs[11 - i]; - } - /* We shift by 12 to allow for the QMF filters (DC gain = 4096), less 1 - to allow for the 15 bit input to the G.722 algorithm. */ - /* WebRtc, tlegrand: added saturation */ - amp[outlen++] = saturate(xout1 >> 11); - amp[outlen++] = saturate(xout2 >> 11); - } - } - } - return outlen; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/modules/audio_coding/codecs/G722/main/source/g722_enc_dec.h b/modules/audio_coding/codecs/G722/main/source/g722_enc_dec.h deleted file mode 100644 index 802126e24..000000000 --- a/modules/audio_coding/codecs/G722/main/source/g722_enc_dec.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * g722.h - The ITU G.722 codec. - * - * Written by Steve Underwood - * - * Copyright (C) 2005 Steve Underwood - * - * Despite my general liking of the GPL, I place my own contributions - * to this code in the public domain for the benefit of all mankind - - * even the slimy ones who might try to proprietize my work and use it - * to my detriment. - * - * Based on a single channel G.722 codec which is: - * - ***** Copyright (c) CMU 1993 ***** - * Computer Science, Speech Group - * Chengxiang Lu and Alex Hauptmann - * - * $Id: g722.h,v 1.10 2006/06/16 12:45:53 steveu Exp $ - * - * Modifications for WebRtc, 2011/04/28, by tlegrand: - * -Changed to use WebRtc types - * -Added new defines for minimum and maximum values of short int - */ - - -/*! \file */ - -#if !defined(_G722_ENC_DEC_H_) -#define _G722_ENC_DEC_H_ - -/*! \page g722_page G.722 encoding and decoding -\section g722_page_sec_1 What does it do? -The G.722 module is a bit exact implementation of the ITU G.722 specification for all three -specified bit rates - 64000bps, 56000bps and 48000bps. It passes the ITU tests. - -To allow fast and flexible interworking with narrow band telephony, the encoder and decoder -support an option for the linear audio to be an 8k samples/second stream. In this mode the -codec is considerably faster, and still fully compatible with wideband terminals using G.722. - -\section g722_page_sec_2 How does it work? -???. -*/ - -#define WEBRTC_INT16_MAX 32767 -#define WEBRTC_INT16_MIN -32768 - -enum -{ - G722_SAMPLE_RATE_8000 = 0x0001, - G722_PACKED = 0x0002 -}; - -typedef struct -{ - /*! TRUE if the operating in the special ITU test mode, with the band split filters - disabled. */ - int itu_test_mode; - /*! TRUE if the G.722 data is packed */ - int packed; - /*! TRUE if encode from 8k samples/second */ - int eight_k; - /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ - int bits_per_sample; - - /*! Signal history for the QMF */ - int x[24]; - - struct - { - int s; - int sp; - int sz; - int r[3]; - int a[3]; - int ap[3]; - int p[3]; - int d[7]; - int b[7]; - int bp[7]; - int sg[7]; - int nb; - int det; - } band[2]; - - unsigned int in_buffer; - int in_bits; - unsigned int out_buffer; - int out_bits; -} g722_encode_state_t; - -typedef struct -{ - /*! TRUE if the operating in the special ITU test mode, with the band split filters - disabled. */ - int itu_test_mode; - /*! TRUE if the G.722 data is packed */ - int packed; - /*! TRUE if decode to 8k samples/second */ - int eight_k; - /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */ - int bits_per_sample; - - /*! Signal history for the QMF */ - int x[24]; - - struct - { - int s; - int sp; - int sz; - int r[3]; - int a[3]; - int ap[3]; - int p[3]; - int d[7]; - int b[7]; - int bp[7]; - int sg[7]; - int nb; - int det; - } band[2]; - - unsigned int in_buffer; - int in_bits; - unsigned int out_buffer; - int out_bits; -} g722_decode_state_t; - -#ifdef __cplusplus -extern "C" { -#endif - -g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options); -int g722_encode_release(g722_encode_state_t *s); -int g722_encode(g722_encode_state_t *s, - WebRtc_UWord8 g722_data[], - const WebRtc_Word16 amp[], - int len); - -g722_decode_state_t *g722_decode_init(g722_decode_state_t *s, int rate, int options); -int g722_decode_release(g722_decode_state_t *s); -int g722_decode(g722_decode_state_t *s, - WebRtc_Word16 amp[], - const WebRtc_UWord8 g722_data[], - int len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/modules/audio_coding/codecs/G722/main/source/g722_encode.c b/modules/audio_coding/codecs/G722/main/source/g722_encode.c deleted file mode 100644 index 98fa2cd84..000000000 --- a/modules/audio_coding/codecs/G722/main/source/g722_encode.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * g722_encode.c - The ITU G.722 codec, encode part. - * - * Written by Steve Underwood - * - * Copyright (C) 2005 Steve Underwood - * - * All rights reserved. - * - * Despite my general liking of the GPL, I place my own contributions - * to this code in the public domain for the benefit of all mankind - - * even the slimy ones who might try to proprietize my work and use it - * to my detriment. - * - * Based on a single channel 64kbps only G.722 codec which is: - * - ***** Copyright (c) CMU 1993 ***** - * Computer Science, Speech Group - * Chengxiang Lu and Alex Hauptmann - * - * $Id: g722_encode.c,v 1.14 2006/07/07 16:37:49 steveu Exp $ - * - * Modifications for WebRtc, 2011/04/28, by tlegrand: - * -Removed usage of inttypes.h and tgmath.h - * -Changed to use WebRtc types - * -Added option to run encoder bitexact with ITU-T reference implementation - */ - -/*! \file */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "typedefs.h" -#include "g722_enc_dec.h" - -#if !defined(FALSE) -#define FALSE 0 -#endif -#if !defined(TRUE) -#define TRUE (!FALSE) -#endif - -static __inline WebRtc_Word16 saturate(WebRtc_Word32 amp) -{ - WebRtc_Word16 amp16; - - /* Hopefully this is optimised for the common case - not clipping */ - amp16 = (WebRtc_Word16) amp; - if (amp == amp16) - return amp16; - if (amp > WEBRTC_INT16_MAX) - return WEBRTC_INT16_MAX; - return WEBRTC_INT16_MIN; -} -/*- End of function --------------------------------------------------------*/ - -static void block4(g722_encode_state_t *s, int band, int d) -{ - int wd1; - int wd2; - int wd3; - int i; - - /* Block 4, RECONS */ - s->band[band].d[0] = d; - s->band[band].r[0] = saturate(s->band[band].s + d); - - /* Block 4, PARREC */ - s->band[band].p[0] = saturate(s->band[band].sz + d); - - /* Block 4, UPPOL2 */ - for (i = 0; i < 3; i++) - s->band[band].sg[i] = s->band[band].p[i] >> 15; - wd1 = saturate(s->band[band].a[1] << 2); - - wd2 = (s->band[band].sg[0] == s->band[band].sg[1]) ? -wd1 : wd1; - if (wd2 > 32767) - wd2 = 32767; - wd3 = (wd2 >> 7) + ((s->band[band].sg[0] == s->band[band].sg[2]) ? 128 : -128); - wd3 += (s->band[band].a[2]*32512) >> 15; - if (wd3 > 12288) - wd3 = 12288; - else if (wd3 < -12288) - wd3 = -12288; - s->band[band].ap[2] = wd3; - - /* Block 4, UPPOL1 */ - s->band[band].sg[0] = s->band[band].p[0] >> 15; - s->band[band].sg[1] = s->band[band].p[1] >> 15; - wd1 = (s->band[band].sg[0] == s->band[band].sg[1]) ? 192 : -192; - wd2 = (s->band[band].a[1]*32640) >> 15; - - s->band[band].ap[1] = saturate(wd1 + wd2); - wd3 = saturate(15360 - s->band[band].ap[2]); - if (s->band[band].ap[1] > wd3) - s->band[band].ap[1] = wd3; - else if (s->band[band].ap[1] < -wd3) - s->band[band].ap[1] = -wd3; - - /* Block 4, UPZERO */ - wd1 = (d == 0) ? 0 : 128; - s->band[band].sg[0] = d >> 15; - for (i = 1; i < 7; i++) - { - s->band[band].sg[i] = s->band[band].d[i] >> 15; - wd2 = (s->band[band].sg[i] == s->band[band].sg[0]) ? wd1 : -wd1; - wd3 = (s->band[band].b[i]*32640) >> 15; - s->band[band].bp[i] = saturate(wd2 + wd3); - } - - /* Block 4, DELAYA */ - for (i = 6; i > 0; i--) - { - s->band[band].d[i] = s->band[band].d[i - 1]; - s->band[band].b[i] = s->band[band].bp[i]; - } - - for (i = 2; i > 0; i--) - { - s->band[band].r[i] = s->band[band].r[i - 1]; - s->band[band].p[i] = s->band[band].p[i - 1]; - s->band[band].a[i] = s->band[band].ap[i]; - } - - /* Block 4, FILTEP */ - wd1 = saturate(s->band[band].r[1] + s->band[band].r[1]); - wd1 = (s->band[band].a[1]*wd1) >> 15; - wd2 = saturate(s->band[band].r[2] + s->band[band].r[2]); - wd2 = (s->band[band].a[2]*wd2) >> 15; - s->band[band].sp = saturate(wd1 + wd2); - - /* Block 4, FILTEZ */ - s->band[band].sz = 0; - for (i = 6; i > 0; i--) - { - wd1 = saturate(s->band[band].d[i] + s->band[band].d[i]); - s->band[band].sz += (s->band[band].b[i]*wd1) >> 15; - } - s->band[band].sz = saturate(s->band[band].sz); - - /* Block 4, PREDIC */ - s->band[band].s = saturate(s->band[band].sp + s->band[band].sz); -} -/*- End of function --------------------------------------------------------*/ - -g722_encode_state_t *g722_encode_init(g722_encode_state_t *s, int rate, int options) -{ - if (s == NULL) - { - if ((s = (g722_encode_state_t *) malloc(sizeof(*s))) == NULL) - return NULL; - } - memset(s, 0, sizeof(*s)); - if (rate == 48000) - s->bits_per_sample = 6; - else if (rate == 56000) - s->bits_per_sample = 7; - else - s->bits_per_sample = 8; - if ((options & G722_SAMPLE_RATE_8000)) - s->eight_k = TRUE; - if ((options & G722_PACKED) && s->bits_per_sample != 8) - s->packed = TRUE; - else - s->packed = FALSE; - s->band[0].det = 32; - s->band[1].det = 8; - return s; -} -/*- End of function --------------------------------------------------------*/ - -int g722_encode_release(g722_encode_state_t *s) -{ - free(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ - -/* WebRtc, tlegrand: - * Only define the following if bit-exactness with reference implementation - * is needed. Will only have any effect if input signal is saturated. - */ -//#define RUN_LIKE_REFERENCE_G722 -#ifdef RUN_LIKE_REFERENCE_G722 -WebRtc_Word16 limitValues (WebRtc_Word16 rl) -{ - - WebRtc_Word16 yl; - - yl = (rl > 16383) ? 16383 : ((rl < -16384) ? -16384 : rl); - - return (yl); -} -#endif - -int g722_encode(g722_encode_state_t *s, WebRtc_UWord8 g722_data[], - const WebRtc_Word16 amp[], int len) -{ - static const int q6[32] = - { - 0, 35, 72, 110, 150, 190, 233, 276, - 323, 370, 422, 473, 530, 587, 650, 714, - 786, 858, 940, 1023, 1121, 1219, 1339, 1458, - 1612, 1765, 1980, 2195, 2557, 2919, 0, 0 - }; - static const int iln[32] = - { - 0, 63, 62, 31, 30, 29, 28, 27, - 26, 25, 24, 23, 22, 21, 20, 19, - 18, 17, 16, 15, 14, 13, 12, 11, - 10, 9, 8, 7, 6, 5, 4, 0 - }; - static const int ilp[32] = - { - 0, 61, 60, 59, 58, 57, 56, 55, - 54, 53, 52, 51, 50, 49, 48, 47, - 46, 45, 44, 43, 42, 41, 40, 39, - 38, 37, 36, 35, 34, 33, 32, 0 - }; - static const int wl[8] = - { - -60, -30, 58, 172, 334, 538, 1198, 3042 - }; - static const int rl42[16] = - { - 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 - }; - static const int ilb[32] = - { - 2048, 2093, 2139, 2186, 2233, 2282, 2332, - 2383, 2435, 2489, 2543, 2599, 2656, 2714, - 2774, 2834, 2896, 2960, 3025, 3091, 3158, - 3228, 3298, 3371, 3444, 3520, 3597, 3676, - 3756, 3838, 3922, 4008 - }; - static const int qm4[16] = - { - 0, -20456, -12896, -8968, - -6288, -4240, -2584, -1200, - 20456, 12896, 8968, 6288, - 4240, 2584, 1200, 0 - }; - static const int qm2[4] = - { - -7408, -1616, 7408, 1616 - }; - static const int qmf_coeffs[12] = - { - 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, - }; - static const int ihn[3] = {0, 1, 0}; - static const int ihp[3] = {0, 3, 2}; - static const int wh[3] = {0, -214, 798}; - static const int rh2[4] = {2, 1, 2, 1}; - - int dlow; - int dhigh; - int el; - int wd; - int wd1; - int ril; - int wd2; - int il4; - int ih2; - int wd3; - int eh; - int mih; - int i; - int j; - /* Low and high band PCM from the QMF */ - int xlow; - int xhigh; - int g722_bytes; - /* Even and odd tap accumulators */ - int sumeven; - int sumodd; - int ihigh; - int ilow; - int code; - - g722_bytes = 0; - xhigh = 0; - for (j = 0; j < len; ) - { - if (s->itu_test_mode) - { - xlow = - xhigh = amp[j++] >> 1; - } - else - { - if (s->eight_k) - { - /* We shift by 1 to allow for the 15 bit input to the G.722 algorithm. */ - xlow = amp[j++] >> 1; - } - else - { - /* Apply the transmit QMF */ - /* Shuffle the buffer down */ - for (i = 0; i < 22; i++) - s->x[i] = s->x[i + 2]; - s->x[22] = amp[j++]; - s->x[23] = amp[j++]; - - /* Discard every other QMF output */ - sumeven = 0; - sumodd = 0; - for (i = 0; i < 12; i++) - { - sumodd += s->x[2*i]*qmf_coeffs[i]; - sumeven += s->x[2*i + 1]*qmf_coeffs[11 - i]; - } - /* We shift by 12 to allow for the QMF filters (DC gain = 4096), plus 1 - to allow for us summing two filters, plus 1 to allow for the 15 bit - input to the G.722 algorithm. */ - xlow = (sumeven + sumodd) >> 14; - xhigh = (sumeven - sumodd) >> 14; - -#ifdef RUN_LIKE_REFERENCE_G722 - /* The following lines are only used to verify bit-exactness - * with reference implementation of G.722. Higher precision - * is achieved without limiting the values. - */ - xlow = limitValues(xlow); - xhigh = limitValues(xhigh); -#endif - } - } - /* Block 1L, SUBTRA */ - el = saturate(xlow - s->band[0].s); - - /* Block 1L, QUANTL */ - wd = (el >= 0) ? el : -(el + 1); - - for (i = 1; i < 30; i++) - { - wd1 = (q6[i]*s->band[0].det) >> 12; - if (wd < wd1) - break; - } - ilow = (el < 0) ? iln[i] : ilp[i]; - - /* Block 2L, INVQAL */ - ril = ilow >> 2; - wd2 = qm4[ril]; - dlow = (s->band[0].det*wd2) >> 15; - - /* Block 3L, LOGSCL */ - il4 = rl42[ril]; - wd = (s->band[0].nb*127) >> 7; - s->band[0].nb = wd + wl[il4]; - if (s->band[0].nb < 0) - s->band[0].nb = 0; - else if (s->band[0].nb > 18432) - s->band[0].nb = 18432; - - /* Block 3L, SCALEL */ - wd1 = (s->band[0].nb >> 6) & 31; - wd2 = 8 - (s->band[0].nb >> 11); - wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); - s->band[0].det = wd3 << 2; - - block4(s, 0, dlow); - - if (s->eight_k) - { - /* Just leave the high bits as zero */ - code = (0xC0 | ilow) >> (8 - s->bits_per_sample); - } - else - { - /* Block 1H, SUBTRA */ - eh = saturate(xhigh - s->band[1].s); - - /* Block 1H, QUANTH */ - wd = (eh >= 0) ? eh : -(eh + 1); - wd1 = (564*s->band[1].det) >> 12; - mih = (wd >= wd1) ? 2 : 1; - ihigh = (eh < 0) ? ihn[mih] : ihp[mih]; - - /* Block 2H, INVQAH */ - wd2 = qm2[ihigh]; - dhigh = (s->band[1].det*wd2) >> 15; - - /* Block 3H, LOGSCH */ - ih2 = rh2[ihigh]; - wd = (s->band[1].nb*127) >> 7; - s->band[1].nb = wd + wh[ih2]; - if (s->band[1].nb < 0) - s->band[1].nb = 0; - else if (s->band[1].nb > 22528) - s->band[1].nb = 22528; - - /* Block 3H, SCALEH */ - wd1 = (s->band[1].nb >> 6) & 31; - wd2 = 10 - (s->band[1].nb >> 11); - wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2); - s->band[1].det = wd3 << 2; - - block4(s, 1, dhigh); - code = ((ihigh << 6) | ilow) >> (8 - s->bits_per_sample); - } - - if (s->packed) - { - /* Pack the code bits */ - s->out_buffer |= (code << s->out_bits); - s->out_bits += s->bits_per_sample; - if (s->out_bits >= 8) - { - g722_data[g722_bytes++] = (WebRtc_UWord8) (s->out_buffer & 0xFF); - s->out_bits -= 8; - s->out_buffer >>= 8; - } - } - else - { - g722_data[g722_bytes++] = (WebRtc_UWord8) code; - } - } - return g722_bytes; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/modules/audio_coding/codecs/G722/main/source/g722_interface.c b/modules/audio_coding/codecs/G722/main/source/g722_interface.c deleted file mode 100644 index 80bdf25a5..000000000 --- a/modules/audio_coding/codecs/G722/main/source/g722_interface.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - - -#include -#include -#include "g722_interface.h" -#include "g722_enc_dec.h" -#include "typedefs.h" - - -WebRtc_Word16 WebRtcG722_CreateEncoder(G722EncInst **G722enc_inst) -{ - *G722enc_inst=(G722EncInst*)malloc(sizeof(g722_encode_state_t)); - if (*G722enc_inst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcG722_EncoderInit(G722EncInst *G722enc_inst) -{ - // Create and/or reset the G.722 encoder - // Bitrate 64 kbps and wideband mode (2) - G722enc_inst = (G722EncInst *) g722_encode_init( - (g722_encode_state_t*) G722enc_inst, 64000, 2); - if (G722enc_inst == NULL) { - return -1; - } else { - return 0; - } -} - -WebRtc_Word16 WebRtcG722_FreeEncoder(G722EncInst *G722enc_inst) -{ - // Free encoder memory - return g722_encode_release((g722_encode_state_t*) G722enc_inst); -} - -WebRtc_Word16 WebRtcG722_Encode(G722EncInst *G722enc_inst, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded) -{ - unsigned char *codechar = (unsigned char*) encoded; - // Encode the input speech vector - return g722_encode((g722_encode_state_t*) G722enc_inst, - codechar, speechIn, len); -} - -WebRtc_Word16 WebRtcG722_CreateDecoder(G722DecInst **G722dec_inst) -{ - *G722dec_inst=(G722DecInst*)malloc(sizeof(g722_decode_state_t)); - if (*G722dec_inst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcG722_DecoderInit(G722DecInst *G722dec_inst) -{ - // Create and/or reset the G.722 decoder - // Bitrate 64 kbps and wideband mode (2) - G722dec_inst = (G722DecInst *) g722_decode_init( - (g722_decode_state_t*) G722dec_inst, 64000, 2); - if (G722dec_inst == NULL) { - return -1; - } else { - return 0; - } -} - -WebRtc_Word16 WebRtcG722_FreeDecoder(G722DecInst *G722dec_inst) -{ - // Free encoder memory - return g722_decode_release((g722_decode_state_t*) G722dec_inst); -} - -WebRtc_Word16 WebRtcG722_Decode(G722DecInst *G722dec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - // Decode the G.722 encoder stream - *speechType=G722_WEBRTC_SPEECH; - return g722_decode((g722_decode_state_t*) G722dec_inst, - decoded, (WebRtc_UWord8*) encoded, len); -} - -WebRtc_Word16 WebRtcG722_Version(char *versionStr, short len) -{ - // Get version string - char version[30] = "2.0.0\n"; - if (strlen(version) < (unsigned int)len) - { - strcpy(versionStr, version); - return 0; - } - else - { - return -1; - } -} - diff --git a/modules/audio_coding/codecs/G722/main/testG722/testG722.cpp b/modules/audio_coding/codecs/G722/main/testG722/testG722.cpp deleted file mode 100644 index 24d794f10..000000000 --- a/modules/audio_coding/codecs/G722/main/testG722/testG722.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * testG722.cpp : Defines the entry point for the console application. - */ - -#include -#include -#include -#include "typedefs.h" - -/* include API */ -#include "g722_interface.h" - -/* Runtime statistics */ -#include -#define CLOCKS_PER_SEC_G722 100000 - -// Forward declaration -typedef struct WebRtcG722EncInst G722EncInst; -typedef struct WebRtcG722DecInst G722DecInst; - -/* function for reading audio data from PCM file */ -int readframe(WebRtc_Word16 *data, FILE *inp, int length) -{ - short k, rlen, status = 0; - - rlen = (short)fread(data, sizeof(WebRtc_Word16), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - return status; -} - -int main(int argc, char* argv[]) -{ - char inname[60], outbit[40], outname[40]; - FILE *inp, *outbitp, *outp; - - int framecnt, endfile; - WebRtc_Word16 framelength = 160; - G722EncInst *G722enc_inst; - G722DecInst *G722dec_inst; - int err; - - /* Runtime statistics */ - double starttime; - double runtime; - double length_file; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 shortdata[960]; - WebRtc_Word16 decoded[960]; - WebRtc_Word16 streamdata[80*3]; - WebRtc_Word16 speechType[1]; - - /* handling wrong input arguments in the command line */ - if (argc!=5) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - printf("Usage:\n\n"); - printf("./testG722.exe framelength infile outbitfile outspeechfile \n\n"); - printf("with:\n"); - printf("framelength : Framelength in samples.\n\n"); - printf("infile : Normal speech input file\n\n"); - printf("outbitfile : Bitstream output file\n\n"); - printf("outspeechfile: Speech output file\n\n"); - exit(0); - - } - - /* Get frame length */ - framelength = atoi(argv[1]); - - /* Get Input and Output files */ - sscanf(argv[2], "%s", inname); - sscanf(argv[3], "%s", outbit); - sscanf(argv[4], "%s", outname); - - if ((inp = fopen(inname,"rb")) == NULL) { - printf(" G.722: Cannot read file %s.\n", inname); - exit(1); - } - if ((outbitp = fopen(outbit,"wb")) == NULL) { - printf(" G.722: Cannot write file %s.\n", outbit); - exit(1); - } - if ((outp = fopen(outname,"wb")) == NULL) { - printf(" G.722: Cannot write file %s.\n", outname); - exit(1); - } - printf("\nInput:%s\nOutput bitstream:%s\nOutput:%s\n", inname, outbit, outname); - - /* Create and init */ - WebRtcG722_CreateEncoder((G722EncInst **)&G722enc_inst); - WebRtcG722_CreateDecoder((G722DecInst **)&G722dec_inst); - WebRtcG722_EncoderInit((G722EncInst *)G722enc_inst); - WebRtcG722_DecoderInit((G722DecInst *)G722dec_inst); - - - /* Initialize encoder and decoder */ - framecnt = 0; - endfile = 0; - while (endfile == 0) { - framecnt++; - - /* Read speech block */ - endfile = readframe(shortdata, inp, framelength); - - /* Start clock before call to encoder and decoder */ - starttime = clock()/(double)CLOCKS_PER_SEC_G722; - - /* G.722 encoding + decoding */ - stream_len = WebRtcG722_Encode((G722EncInst *)G722enc_inst, shortdata, framelength, streamdata); - err = WebRtcG722_Decode((G722DecInst *)G722dec_inst, streamdata, stream_len, decoded, speechType); - - /* Stop clock after call to encoder and decoder */ - runtime += (double)((clock()/(double)CLOCKS_PER_SEC_G722)-starttime); - - if (stream_len < 0 || err < 0) { - /* exit if returned with error */ - printf("Error in encoder/decoder\n"); - } else { - /* Write coded bits to file */ - fwrite(streamdata,sizeof(short),stream_len/2,outbitp); - /* Write coded speech to file */ - fwrite(decoded,sizeof(short),framelength,outp); - } - } - - WebRtcG722_FreeEncoder((G722EncInst *)G722enc_inst); - WebRtcG722_FreeDecoder((G722DecInst *)G722dec_inst); - - length_file = ((double)framecnt*(double)framelength/16000); - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run G.722: %.2f s (%.2f %% of realtime)\n\n", runtime, (100*runtime/length_file)); - printf("---------------------END----------------------\n"); - - fclose(inp); - fclose(outbitp); - fclose(outp); - - return 0; -} - diff --git a/modules/audio_coding/codecs/OWNERS b/modules/audio_coding/codecs/OWNERS deleted file mode 100644 index a7220e7ea..000000000 --- a/modules/audio_coding/codecs/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -tlegrand@google.com -turajs@google.com -jks@google.com diff --git a/modules/audio_coding/codecs/PCM16B/main/interface/pcm16b.h b/modules/audio_coding/codecs/PCM16B/main/interface/pcm16b.h deleted file mode 100644 index e3cac4d20..000000000 --- a/modules/audio_coding/codecs/PCM16B/main/interface/pcm16b.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_PCM16B_MAIN_INTERFACE_PCM16B_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_PCM16B_MAIN_INTERFACE_PCM16B_H_ -/* - * Define the fixpoint numeric formats - */ - -#include "typedefs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/**************************************************************************** - * WebRtcPcm16b_EncodeW16(...) - * - * "Encode" a sample vector to 16 bit linear (Encoded standard is big endian) - * - * Input: - * - speechIn16b : Input speech vector - * - len : Number of samples in speech vector - * - * Output: - * - speechOut16b : Encoded data vector (big endian 16 bit) - * - * Returned value : Size in bytes of speechOut16b - */ - -WebRtc_Word16 WebRtcPcm16b_EncodeW16(WebRtc_Word16 *speechIn16b, - WebRtc_Word16 len, - WebRtc_Word16 *speechOut16b); - -/**************************************************************************** - * WebRtcPcm16b_Encode(...) - * - * "Encode" a sample vector to 16 bit linear (Encoded standard is big endian) - * - * Input: - * - speech16b : Input speech vector - * - len : Number of samples in speech vector - * - * Output: - * - speech8b : Encoded data vector (big endian 16 bit) - * - * Returned value : Size in bytes of speech8b - */ - -WebRtc_Word16 WebRtcPcm16b_Encode(WebRtc_Word16 *speech16b, - WebRtc_Word16 len, - unsigned char *speech8b); - -/**************************************************************************** - * WebRtcPcm16b_DecodeW16(...) - * - * "Decode" a vector to 16 bit linear (Encoded standard is big endian) - * - * Input: - * - speechIn16b : Encoded data vector (big endian 16 bit) - * - len : Number of bytes in speechIn16b - * - * Output: - * - speechOut16b : Decoded speech vector - * - * Returned value : Samples in speechOut16b - */ - -WebRtc_Word16 WebRtcPcm16b_DecodeW16(void *inst, - WebRtc_Word16 *speechIn16b, - WebRtc_Word16 len, - WebRtc_Word16 *speechOut16b, - WebRtc_Word16* speechType); - -/**************************************************************************** - * WebRtcPcm16b_Decode(...) - * - * "Decode" a vector to 16 bit linear (Encoded standard is big endian) - * - * Input: - * - speech8b : Encoded data vector (big endian 16 bit) - * - len : Number of bytes in speech8b - * - * Output: - * - speech16b : Decoded speech vector - * - * Returned value : Samples in speech16b - */ - - -WebRtc_Word16 WebRtcPcm16b_Decode(unsigned char *speech8b, - WebRtc_Word16 len, - WebRtc_Word16 *speech16b); - -#ifdef __cplusplus -} -#endif - -#endif /* PCM16B */ diff --git a/modules/audio_coding/codecs/PCM16B/main/source/Android.mk b/modules/audio_coding/codecs/PCM16B/main/source/Android.mk deleted file mode 100644 index 49a0b7eec..000000000 --- a/modules/audio_coding/codecs/PCM16B/main/source/Android.mk +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_pcm16b -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := pcm16b.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/PCM16B/main/source/pcm16b.c b/modules/audio_coding/codecs/PCM16B/main/source/pcm16b.c deleted file mode 100644 index 21ee7665d..000000000 --- a/modules/audio_coding/codecs/PCM16B/main/source/pcm16b.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#include "pcm16b.h" - -#include "typedefs.h" - -#ifdef WEBRTC_BIG_ENDIAN -#include "signal_processing_library.h" -#endif - -#define HIGHEND 0xFF00 -#define LOWEND 0xFF - - - -/* Encoder with WebRtc_Word16 Output */ -WebRtc_Word16 WebRtcPcm16b_EncodeW16(WebRtc_Word16 *speechIn16b, - WebRtc_Word16 len, - WebRtc_Word16 *speechOut16b) -{ -#ifdef WEBRTC_BIG_ENDIAN - WEBRTC_SPL_MEMCPY_W16(speechOut16b, speechIn16b, len); -#else - int i; - for (i=0;i>8)|((((WebRtc_UWord16)speechIn16b[i])<<8)&0xFF00); - } -#endif - return(len<<1); -} - - -/* Encoder with char Output (old version) */ -WebRtc_Word16 WebRtcPcm16b_Encode(WebRtc_Word16 *speech16b, - WebRtc_Word16 len, - unsigned char *speech8b) -{ - WebRtc_Word16 samples=len*2; - WebRtc_Word16 pos; - WebRtc_Word16 short1; - WebRtc_Word16 short2; - for (pos=0;pos>8; - speech8b[pos*2]=(unsigned char) short1; - speech8b[pos*2+1]=(unsigned char) short2; - } - return(samples); -} - - -/* Decoder with WebRtc_Word16 Input instead of char when the WebRtc_Word16 Encoder is used */ -WebRtc_Word16 WebRtcPcm16b_DecodeW16(void *inst, - WebRtc_Word16 *speechIn16b, - WebRtc_Word16 len, - WebRtc_Word16 *speechOut16b, - WebRtc_Word16* speechType) -{ -#ifdef WEBRTC_BIG_ENDIAN - WEBRTC_SPL_MEMCPY_W8(speechOut16b, speechIn16b, ((len*sizeof(WebRtc_Word16)+1)>>1)); -#else - int i; - int samples=len>>1; - - for (i=0;i>8)|(((WebRtc_UWord16)(speechIn16b[i]&0xFF))<<8); - } -#endif - - *speechType=1; - return(len>>1); -} - -/* "old" version of the decoder that uses char as input (not used in NetEq any more) */ -WebRtc_Word16 WebRtcPcm16b_Decode(unsigned char *speech8b, - WebRtc_Word16 len, - WebRtc_Word16 *speech16b) -{ - WebRtc_Word16 samples=len>>1; - WebRtc_Word16 pos; - WebRtc_Word16 shortval; - for (pos=0;pos | 1. Pre P | -> | 2. LPC | -> | 3. Ana | -> - +-----------+ +---------+ +---------+ - - +---------------+ +--------------+ - -> | 4. Start Sel | ->| 5. Scalar Qu | -> - +---------------+ +--------------+ - - +--------------+ +---------------+ - -> |6. CB Search | -> | 7. Packetize | -> payload - | +--------------+ | +---------------+ - ----<---------<------ - sub-frame 0..2/4 (20 ms/30 ms) - - Figure 3.1. Flow chart of the iLBC encoder - - 1. Pre-process speech with a HP filter, if needed (section 3.1). - - 2. Compute LPC parameters, quantize, and interpolate (section 3.2). - - 3. Use analysis filters on speech to compute residual (section 3.3). - - 4. Select position of 57/58-sample start state (section 3.5). - - 5. Quantize the 57/58-sample start state with scalar quantization - (section 3.5). - - 6. Search the codebook for each sub-frame. Start with 23/22 sample - block, then encode sub-blocks forward in time, and then encode - sub-blocks backward in time. For each block, the steps in Figure - 3.4 are performed (section 3.6). - - 7. Packetize the bits into the payload specified in Table 3.2. - - The input to the encoder SHOULD be 16-bit uniform PCM sampled at 8 - kHz. Also it SHOULD be partitioned into blocks of BLOCKL=160/240 - samples. Each block input to the encoder is divided into NSUB=4/6 - consecutive sub-blocks of SUBL=40 samples each. - - - - - - - - - - - - - -Andersen, et al. Experimental [Page 8] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - 0 39 79 119 159 - +---------------------------------------+ - | 1 | 2 | 3 | 4 | - +---------------------------------------+ - 20 ms frame - - 0 39 79 119 159 199 239 - +-----------------------------------------------------------+ - | 1 | 2 | 3 | 4 | 5 | 6 | - +-----------------------------------------------------------+ - 30 ms frame - Figure 3.2. One input block to the encoder for 20 ms (with four sub- - frames) and 30 ms (with six sub-frames). - -3.1. Pre-processing - - In some applications, the recorded speech signal contains DC level - and/or 50/60 Hz noise. If these components have not been removed - prior to the encoder call, they should be removed by a high-pass - filter. A reference implementation of this, using a filter with a - cutoff frequency of 90 Hz, can be found in Appendix A.28. - -3.2. LPC Analysis and Quantization - - The input to the LPC analysis module is a possibly high-pass filtered - speech buffer, speech_hp, that contains 240/300 (LPC_LOOKBACK + - BLOCKL = 80/60 + 160/240 = 240/300) speech samples, where samples 0 - through 79/59 are from the previous block and samples 80/60 through - 239/299 are from the current block. No look-ahead into the next - block is used. For the very first block processed, the look-back - samples are assumed to be zeros. - - For each input block, the LPC analysis calculates one/two set(s) of - LPC_FILTERORDER=10 LPC filter coefficients using the autocorrelation - method and the Levinson-Durbin recursion. These coefficients are - converted to the Line Spectrum Frequency representation. In the 20 - ms case, the single lsf set represents the spectral characteristics - as measured at the center of the third sub-block. For 30 ms frames, - the first set, lsf1, represents the spectral properties of the input - signal at the center of the second sub-block, and the other set, - lsf2, represents the spectral characteristics as measured at the - center of the fifth sub-block. The details of the computation for 30 - ms frames are described in sections 3.2.1 through 3.2.6. Section - 3.2.7 explains how the LPC Analysis and Quantization differs for 20 - ms frames. - - - - - - -Andersen, et al. Experimental [Page 9] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -3.2.1. Computation of Autocorrelation Coefficients - - The first step in the LPC analysis procedure is to calculate - autocorrelation coefficients by using windowed speech samples. This - windowing is the only difference in the LPC analysis procedure for - the two sets of coefficients. For the first set, a 240-sample-long - standard symmetric Hanning window is applied to samples 0 through 239 - of the input data. The first window, lpc_winTbl, is defined as - - lpc_winTbl[i]= 0.5 * (1.0 - cos((2*PI*(i+1))/(BLOCKL+1))); - i=0,...,119 - lpc_winTbl[i] = winTbl[BLOCKL - i - 1]; i=120,...,239 - - The windowed speech speech_hp_win1 is then obtained by multiplying - the first 240 samples of the input speech buffer with the window - coefficients: - - speech_hp_win1[i] = speech_hp[i] * lpc_winTbl[i]; - i=0,...,BLOCKL-1 - - From these 240 windowed speech samples, 11 (LPC_FILTERORDER + 1) - autocorrelation coefficients, acf1, are calculated: - - acf1[lag] += speech_hp_win1[n] * speech_hp_win1[n + lag]; - lag=0,...,LPC_FILTERORDER; n=0,...,BLOCKL-lag-1 - - In order to make the analysis more robust against numerical precision - problems, a spectral smoothing procedure is applied by windowing the - autocorrelation coefficients before the LPC coefficients are - computed. Also, a white noise floor is added to the autocorrelation - function by multiplying coefficient zero by 1.0001 (40dB below the - energy of the windowed speech signal). These two steps are - implemented by multiplying the autocorrelation coefficients with the - following window: - - lpc_lagwinTbl[0] = 1.0001; - lpc_lagwinTbl[i] = exp(-0.5 * ((2 * PI * 60.0 * i) /FS)^2); - i=1,...,LPC_FILTERORDER - where FS=8000 is the sampling frequency - - Then, the windowed acf function acf1_win is obtained by - - acf1_win[i] = acf1[i] * lpc_lagwinTbl[i]; - i=0,...,LPC_FILTERORDER - - The second set of autocorrelation coefficients, acf2_win, are - obtained in a similar manner. The window, lpc_asymwinTbl, is applied - to samples 60 through 299, i.e., the entire current block. The - - - -Andersen, et al. Experimental [Page 10] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - window consists of two segments, the first (samples 0 to 219) being - half a Hanning window with length 440 and the second a quarter of a - cycle of a cosine wave. By using this asymmetric window, an LPC - analysis centered in the fifth sub-block is obtained without the need - for any look-ahead, which would add delay. The asymmetric window is - defined as - - lpc_asymwinTbl[i] = (sin(PI * (i + 1) / 441))^2; i=0,...,219 - - lpc_asymwinTbl[i] = cos((i - 220) * PI / 40); i=220,...,239 - - and the windowed speech is computed by - - speech_hp_win2[i] = speech_hp[i + LPC_LOOKBACK] * - lpc_asymwinTbl[i]; i=0,....BLOCKL-1 - - The windowed autocorrelation coefficients are then obtained in - exactly the same way as for the first analysis instance. - - The generation of the windows lpc_winTbl, lpc_asymwinTbl, and - lpc_lagwinTbl are typically done in advance, and the arrays are - stored in ROM rather than repeating the calculation for every block. - -3.2.2. Computation of LPC Coefficients - - From the 2 x 11 smoothed autocorrelation coefficients, acf1_win and - acf2_win, the 2 x 11 LPC coefficients, lp1 and lp2, are calculated - in the same way for both analysis locations by using the well known - Levinson-Durbin recursion. The first LPC coefficient is always 1.0, - resulting in ten unique coefficients. - - After determining the LPC coefficients, a bandwidth expansion - procedure is applied to smooth the spectral peaks in the - short-term spectrum. The bandwidth addition is obtained by the - following modification of the LPC coefficients: - - lp1_bw[i] = lp1[i] * chirp^i; i=0,...,LPC_FILTERORDER - lp2_bw[i] = lp2[i] * chirp^i; i=0,...,LPC_FILTERORDER - - where "chirp" is a real number between 0 and 1. It is RECOMMENDED to - use a value of 0.9. - -3.2.3. Computation of LSF Coefficients from LPC Coefficients - - Thus far, two sets of LPC coefficients that represent the short-term - spectral characteristics of the speech signal for two different time - locations within the current block have been determined. These - coefficients SHOULD be quantized and interpolated. Before this is - - - -Andersen, et al. Experimental [Page 11] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - done, it is advantageous to convert the LPC parameters into another - type of representation called Line Spectral Frequencies (LSF). The - LSF parameters are used because they are better suited for - quantization and interpolation than the regular LPC coefficients. - Many computationally efficient methods for calculating the LSFs from - the LPC coefficients have been proposed in the literature. The - detailed implementation of one applicable method can be found in - Appendix A.26. The two arrays of LSF coefficients obtained, lsf1 and - lsf2, are of dimension 10 (LPC_FILTERORDER). - -3.2.4. Quantization of LSF Coefficients - - Because the LPC filters defined by the two sets of LSFs are also - needed in the decoder, the LSF parameters need to be quantized and - transmitted as side information. The total number of bits required - to represent the quantization of the two LSF representations for one - block of speech is 40, with 20 bits used for each of lsf1 and lsf2. - - For computational and storage reasons, the LSF vectors are quantized - using three-split vector quantization (VQ). That is, the LSF vectors - are split into three sub-vectors that are each quantized with a - regular VQ. The quantized versions of lsf1 and lsf2, qlsf1 and - qlsf2, are obtained by using the same memoryless split VQ. The - length of each of these two LSF vectors is 10, and they are split - into three sub-vectors containing 3, 3, and 4 values, respectively. - - For each of the sub-vectors, a separate codebook of quantized values - has been designed with a standard VQ training method for a large - database containing speech from a large number of speakers recorded - under various conditions. The size of each of the three codebooks - associated with the split definitions above is - - int size_lsfCbTbl[LSF_NSPLIT] = {64,128,128}; - - The actual values of the vector quantization codebook that must be - used can be found in the reference code of Appendix A. Both sets of - LSF coefficients, lsf1 and lsf2, are quantized with a standard - memoryless split vector quantization (VQ) structure using the squared - error criterion in the LSF domain. The split VQ quantization - consists of the following steps: - - 1) Quantize the first three LSF coefficients (1 - 3) with a VQ - codebook of size 64. - 2) Quantize the next three LSF coefficients 4 - 6 with VQ a codebook - of size 128. - 3) Quantize the last four LSF coefficients (7 - 10) with a VQ - codebook of size 128. - - - - -Andersen, et al. Experimental [Page 12] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - This procedure, repeated for lsf1 and lsf2, gives six quantization - indices and the quantized sets of LSF coefficients qlsf1 and qlsf2. - Each set of three indices is encoded with 6 + 7 + 7 = 20 bits. The - total number of bits used for LSF quantization in a block is thus 40 - bits. - -3.2.5. Stability Check of LSF Coefficients - - The LSF representation of the LPC filter has the convenient property - that the coefficients are ordered by increasing value, i.e., lsf(n-1) - < lsf(n), 0 < n < 10, if the corresponding synthesis filter is - stable. As we are employing a split VQ scheme, it is possible that - at the split boundaries the LSF coefficients are not ordered - correctly and hence that the corresponding LP filter is unstable. To - ensure that the filter used is stable, a stability check is performed - for the quantized LSF vectors. If it turns out that the coefficients - are not ordered appropriately (with a safety margin of 50 Hz to - ensure that formant peaks are not too narrow), they will be moved - apart. The detailed method for this can be found in Appendix A.40. - The same procedure is performed in the decoder. This ensures that - exactly the same LSF representations are used in both encoder and - decoder. - -3.2.6. Interpolation of LSF Coefficients - - From the two sets of LSF coefficients that are computed for each - block of speech, different LSFs are obtained for each sub-block by - means of interpolation. This procedure is performed for the original - LSFs (lsf1 and lsf2), as well as the quantized versions qlsf1 and - qlsf2, as both versions are used in the encoder. Here follows a - brief summary of the interpolation scheme; the details are found in - the c-code of Appendix A. In the first sub-block, the average of the - second LSF vector from the previous block and the first LSF vector in - the current block is used. For sub-blocks two through five, the LSFs - used are obtained by linear interpolation from lsf1 (and qlsf1) to - lsf2 (and qlsf2), with lsf1 used in sub-block two and lsf2 in sub- - block five. In the last sub-block, lsf2 is used. For the very first - block it is assumed that the last LSF vector of the previous block is - equal to a predefined vector, lsfmeanTbl, obtained by calculating the - mean LSF vector of the LSF design database. - - lsfmeanTbl[LPC_FILTERORDER] = {0.281738, 0.445801, 0.663330, - 0.962524, 1.251831, 1.533081, 1.850586, 2.137817, - 2.481445, 2.777344} - - - - - - - -Andersen, et al. Experimental [Page 13] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - The interpolation method is standard linear interpolation in the LSF - domain. The interpolated LSF values are converted to LPC - coefficients for each sub-block. The unquantized and quantized LPC - coefficients form two sets of filters respectively. The unquantized - analysis filter for sub-block k is defined as follows - - ___ - \ - Ak(z)= 1 + > ak(i)*z^(-i) - /__ - i=1...LPC_FILTERORDER - - The quantized analysis filter for sub-block k is defined as follows - ___ - \ - A~k(z)= 1 + > a~k(i)*z^(-i) - /__ - i=1...LPC_FILTERORDER - - A reference implementation of the lsf encoding is given in Appendix - A.38. A reference implementation of the corresponding decoding can - be found in Appendix A.36. - -3.2.7. LPC Analysis and Quantization for 20 ms Frames - - As previously stated, the codec only calculates one set of LPC - parameters for the 20 ms frame size as opposed to two sets for 30 ms - frames. A single set of autocorrelation coefficients is calculated - on the LPC_LOOKBACK + BLOCKL = 80 + 160 = 240 samples. These samples - are windowed with the asymmetric window lpc_asymwinTbl, centered over - the third sub-frame, to form speech_hp_win. Autocorrelation - coefficients, acf, are calculated on the 240 samples in speech_hp_win - and then windowed exactly as in section 3.2.1 (resulting in - acf_win). - - This single set of windowed autocorrelation coefficients is used to - calculate LPC coefficients, LSF coefficients, and quantized LSF - coefficients in exactly the same manner as in sections 3.2.3 through - 3.2.4. As for the 30 ms frame size, the ten LSF coefficients are - divided into three sub-vectors of size 3, 3, and 4 and quantized by - using the same scheme and codebook as in section 3.2.4 to finally get - 3 quantization indices. The quantized LSF coefficients are - stabilized with the algorithm described in section 3.2.5. - - From the set of LSF coefficients computed for this block and those - from the previous block, different LSFs are obtained for each sub- - block by means of interpolation. The interpolation is done linearly - in the LSF domain over the four sub-blocks, so that the n-th sub- - - - -Andersen, et al. Experimental [Page 14] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - frame uses the weight (4-n)/4 for the LSF from old frame and the - weight n/4 of the LSF from the current frame. For the very first - block the mean LSF, lsfmeanTbl, is used as the LSF from the previous - block. Similarly as seen in section 3.2.6, both unquantized, A(z), - and quantized, A~(z), analysis filters are calculated for each of the - four sub-blocks. - -3.3. Calculation of the Residual - - The block of speech samples is filtered by the quantized and - interpolated LPC analysis filters to yield the residual signal. In - particular, the corresponding LPC analysis filter for each 40 sample - sub-block is used to filter the speech samples for the same sub- - block. The filter memory at the end of each sub-block is carried - over to the LPC filter of the next sub-block. The signal at the - output of each LP analysis filter constitutes the residual signal for - the corresponding sub-block. - - A reference implementation of the LPC analysis filters is given in - Appendix A.10. - -3.4. Perceptual Weighting Filter - - In principle any good design of a perceptual weighting filter can be - applied in the encoder without compromising this codec definition. - However, it is RECOMMENDED to use the perceptual weighting filter Wk - for sub-block k specified below: - - Wk(z)=1/Ak(z/LPC_CHIRP_WEIGHTDENUM), where - LPC_CHIRP_WEIGHTDENUM = 0.4222 - - This is a simple design with low complexity that is applied in the - LPC residual domain. Here Ak(z) is the filter obtained for sub-block - k from unquantized but interpolated LSF coefficients. - -3.5. Start State Encoder - - The start state is quantized by using a common 6-bit scalar quantizer - for the block and a 3-bit scalar quantizer operating on scaled - samples in the weighted speech domain. In the following we describe - the state encoding in greater detail. - - - - - - - - - - -Andersen, et al. Experimental [Page 15] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -3.5.1. Start State Estimation - - The two sub-blocks containing the start state are determined by - finding the two consecutive sub-blocks in the block having the - highest power. Advantageously, down-weighting is used in the - beginning and end of the sub-frames, i.e., the following measure is - computed (NSUB=4/6 for 20/30 ms frame size): - - nsub=1,...,NSUB-1 - ssqn[nsub] = 0.0; - for (i=(nsub-1)*SUBL; i<(nsub-1)*SUBL+5; i++) - ssqn[nsub] += sampEn_win[i-(nsub-1)*SUBL]* - residual[i]*residual[i]; - for (i=(nsub-1)*SUBL+5; i<(nsub+1)*SUBL-5; i++) - ssqn[nsub] += residual[i]*residual[i]; - for (i=(nsub+1)*SUBL-5; i<(nsub+1)*SUBL; i++) - ssqn[nsub] += sampEn_win[(nsub+1)*SUBL-i-1]* - residual[i]*residual[i]; - - where sampEn_win[5]={1/6, 2/6, 3/6, 4/6, 5/6}; MAY be used. The - sub-frame number corresponding to the maximum value of - ssqEn_win[nsub-1]*ssqn[nsub] is selected as the start state - indicator. A weighting of ssqEn_win[]={0.8,0.9,1.0,0.9,0.8} for 30 - ms frames and ssqEn_win[]={0.9,1.0,0.9} for 20 ms frames; MAY - advantageously be used to bias the start state towards the middle of - the frame. - - For 20 ms frames there are three possible positions for the two-sub- - block length maximum power segment; the start state position is - encoded with 2 bits. The start state position, start, MUST be - encoded as - - start=1: start state in sub-frame 0 and 1 - start=2: start state in sub-frame 1 and 2 - start=3: start state in sub-frame 2 and 3 - - For 30 ms frames there are five possible positions of the two-sub- - block length maximum power segment, the start state position is - encoded with 3 bits. The start state position, start, MUST be - encoded as - - start=1: start state in sub-frame 0 and 1 - start=2: start state in sub-frame 1 and 2 - start=3: start state in sub-frame 2 and 3 - start=4: start state in sub-frame 3 and 4 - start=5: start state in sub-frame 4 and 5 - - - - - -Andersen, et al. Experimental [Page 16] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - Hence, in both cases, index 0 is not used. In order to shorten the - start state for bit rate efficiency, the start state is brought down - to STATE_SHORT_LEN=57 samples for 20 ms frames and STATE_SHORT_LEN=58 - samples for 30 ms frames. The power of the first 23/22 and last - 23/22 samples of the two sub-frame blocks identified above is - computed as the sum of the squared signal sample values, and the - 23/22-sample segment with the lowest power is excluded from the start - state. One bit is transmitted to indicate which of the two possible - 57/58 sample segments is used. The start state position within the - two sub-frames determined above, state_first, MUST be encoded as - - state_first=1: start state is first STATE_SHORT_LEN samples - state_first=0: start state is last STATE_SHORT_LEN samples - -3.5.2. All-Pass Filtering and Scale Quantization - - The block of residual samples in the start state is first filtered by - an all-pass filter with the quantized LPC coefficients as denominator - and reversed quantized LPC coefficients as numerator. The purpose of - this phase-dispersion filter is to get a more even distribution of - the sample values in the residual signal. The filtering is performed - by circular convolution, where the initial filter memory is set to - zero. - - res(0..(STATE_SHORT_LEN-1)) = uncoded start state residual - res((STATE_SHORT_LEN)..(2*STATE_SHORT_LEN-1)) = 0 - - Pk(z) = A~rk(z)/A~k(z), where - ___ - \ - A~rk(z)= z^(-LPC_FILTERORDER)+>a~k(i+1)*z^(i-(LPC_FILTERORDER-1)) - /__ - i=0...(LPC_FILTERORDER-1) - - and A~k(z) is taken from the block where the start state begins - - res -> Pk(z) -> filtered - - ccres(k) = filtered(k) + filtered(k+STATE_SHORT_LEN), - k=0..(STATE_SHORT_LEN-1) - - The all-pass filtered block is searched for its largest magnitude - sample. The 10-logarithm of this magnitude is quantized with a 6-bit - quantizer, state_frgqTbl, by finding the nearest representation. - - - - - - - -Andersen, et al. Experimental [Page 17] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - This results in an index, idxForMax, corresponding to a quantized - value, qmax. The all-pass filtered residual samples in the block are - then multiplied with a scaling factor scal=4.5/(10^qmax) to yield - normalized samples. - - state_frgqTbl[64] = {1.000085, 1.071695, 1.140395, 1.206868, - 1.277188, 1.351503, 1.429380, 1.500727, 1.569049, - 1.639599, 1.707071, 1.781531, 1.840799, 1.901550, - 1.956695, 2.006750, 2.055474, 2.102787, 2.142819, - 2.183592, 2.217962, 2.257177, 2.295739, 2.332967, - 2.369248, 2.402792, 2.435080, 2.468598, 2.503394, - 2.539284, 2.572944, 2.605036, 2.636331, 2.668939, - 2.698780, 2.729101, 2.759786, 2.789834, 2.818679, - 2.848074, 2.877470, 2.906899, 2.936655, 2.967804, - 3.000115, 3.033367, 3.066355, 3.104231, 3.141499, - 3.183012, 3.222952, 3.265433, 3.308441, 3.350823, - 3.395275, 3.442793, 3.490801, 3.542514, 3.604064, - 3.666050, 3.740994, 3.830749, 3.938770, 4.101764} - -3.5.3. Scalar Quantization - - The normalized samples are quantized in the perceptually weighted - speech domain by a sample-by-sample scalar DPCM quantization as - depicted in Figure 3.3. Each sample in the block is filtered by a - weighting filter Wk(z), specified in section 3.4, to form a weighted - speech sample x[n]. The target sample d[n] is formed by subtracting - a predicted sample y[n], where the prediction filter is given by - - Pk(z) = 1 - 1 / Wk(z). - - +-------+ x[n] + d[n] +-----------+ u[n] - residual -->| Wk(z) |-------->(+)---->| Quantizer |------> quantized - +-------+ - /|\ +-----------+ | residual - | \|/ - y[n] +--------------------->(+) - | | - | +------+ | - +--------| Pk(z)|<------+ - +------+ - - Figure 3.3. Quantization of start state samples by DPCM in weighted - speech domain. - - The coded state sample u[n] is obtained by quantizing d[n] with a 3- - bit quantizer with quantization table state_sq3Tbl. - - state_sq3Tbl[8] = {-3.719849, -2.177490, -1.130005, -0.309692, - 0.444214, 1.329712, 2.436279, 3.983887} - - - -Andersen, et al. Experimental [Page 18] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - The quantized samples are transformed back to the residual domain by - 1) scaling with 1/scal; 2) time-reversing the scaled samples; 3) - filtering the time-reversed samples by the same all-pass filter, as - in section 3.5.2, by using circular convolution; and 4) time- - reversing the filtered samples. (More detail is in section 4.2.) - - A reference implementation of the start-state encoding can be found - in Appendix A.46. - -3.6. Encoding the Remaining Samples - - A dynamic codebook is used to encode 1) the 23/22 remaining samples - in the two sub-blocks containing the start state; 2) the sub-blocks - after the start state in time; and 3) the sub-blocks before the start - state in time. Thus, the encoding target can be either the 23/22 - samples remaining of the 2 sub-blocks containing the start state, or - a 40-sample sub-block. This target can consist of samples that are - indexed forward in time or backward in time, depending on the - location of the start state. The length of the target is denoted by - lTarget. - - The coding is based on an adaptive codebook that is built from a - codebook memory that contains decoded LPC excitation samples from the - already encoded part of the block. These samples are indexed in the - same time direction as is the target vector and end at the sample - instant prior to the first sample instant represented in the target - vector. The codebook memory has length lMem, which is equal to - CB_MEML=147 for the two/four 40-sample sub-blocks and 85 for the - 23/22-sample sub-block. - - The following figure shows an overview of the encoding procedure. - - +------------+ +---------------+ +-------------+ - -> | 1. Decode | -> | 2. Mem setup | -> | 3. Perc. W. | -> - +------------+ +---------------+ +-------------+ - - +------------+ +-----------------+ - -> | 4. Search | -> | 5. Upd. Target | ------------------> - | +------------+ +------------------ | - ----<-------------<-----------<---------- - stage=0..2 - - +----------------+ - -> | 6. Recalc G[0] | ---------------> gains and CB indices - +----------------+ - - Figure 3.4. Flow chart of the codebook search in the iLBC encoder. - - - - -Andersen, et al. Experimental [Page 19] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - 1. Decode the part of the residual that has been encoded so far, - using the codebook without perceptual weighting. - - 2. Set up the memory by taking data from the decoded residual. This - memory is used to construct codebooks. For blocks preceding the - start state, both the decoded residual and the target are time - reversed (section 3.6.1). - 3. Filter the memory + target with the perceptual weighting filter - (section 3.6.2). - - 4. Search for the best match between the target and the codebook - vector. Compute the optimal gain for this match and quantize that - gain (section 3.6.4). - - 5. Update the perceptually weighted target by subtracting the - contribution from the selected codebook vector from the - perceptually weighted memory (quantized gain times selected - vector). Repeat 4 and 5 for the two additional stages. - - 6. Calculate the energy loss due to encoding of the residual. If - needed, compensate for this loss by an upscaling and - requantization of the gain for the first stage (section 3.7). - - The following sections provide an in-depth description of the - different blocks of Figure 3.4. - -3.6.1. Codebook Memory - - The codebook memory is based on the already encoded sub-blocks, so - the available data for encoding increases for each new sub-block that - has been encoded. Until enough sub-blocks have been encoded to fill - the codebook memory with data, it is padded with zeros. The - following figure shows an example of the order in which the sub- - blocks are encoded for the 30 ms frame size if the start state is - located in the last 58 samples of sub-block 2 and 3. - - +-----------------------------------------------------+ - | 5 | 1 |///|////////| 2 | 3 | 4 | - +-----------------------------------------------------+ - - Figure 3.5. The order from 1 to 5 in which the sub-blocks are - encoded. The slashed area is the start state. - - - - - - - - - -Andersen, et al. Experimental [Page 20] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - The first target sub-block to be encoded is number 1, and the - corresponding codebook memory is shown in the following figure. As - the target vector comes before the start state in time, the codebook - memory and target vector are time reversed; thus, after the block has - been time reversed the search algorithm can be reused. As only the - start state has been encoded so far, the last samples of the codebook - memory are padded with zeros. - - +------------------------- - |zeros|\\\\\\\\|\\\\| 1 | - +------------------------- - - Figure 3.6. The codebook memory, length lMem=85 samples, and the - target vector 1, length 22 samples. - - The next step is to encode sub-block 2 by using the memory that now - has increased since sub-block 1 has been encoded. The following - figure shows the codebook memory for encoding of sub-block 2. - - +----------------------------------- - | zeros | 1 |///|////////| 2 | - +----------------------------------- - - Figure 3.7. The codebook memory, length lMem=147 samples, and the - target vector 2, length 40 samples. - - The next step is to encode sub-block 3 by using the memory which has - been increased yet again since sub-blocks 1 and 2 have been encoded, - but the sub-block still has to be padded with a few zeros. The - following figure shows the codebook memory for encoding of sub-block - 3. - - +------------------------------------------ - |zeros| 1 |///|////////| 2 | 3 | - +------------------------------------------ - - Figure 3.8. The codebook memory, length lMem=147 samples, and the - target vector 3, length 40 samples. - - The next step is to encode sub-block 4 by using the memory which now - has increased yet again since sub-blocks 1, 2, and 3 have been - encoded. This time, the memory does not have to be padded with - zeros. The following figure shows the codebook memory for encoding - of sub-block 4. - - - - - - - -Andersen, et al. Experimental [Page 21] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - +------------------------------------------ - |1|///|////////| 2 | 3 | 4 | - +------------------------------------------ - - Figure 3.9. The codebook memory, length lMem=147 samples, and the - target vector 4, length 40 samples. - - The final target sub-block to be encoded is number 5, and the - following figure shows the corresponding codebook memory. As the - target vector comes before the start state in time, the codebook - memory and target vector are time reversed. - - +------------------------------------------- - | 3 | 2 |\\\\\\\\|\\\\| 1 | 5 | - +------------------------------------------- - - Figure 3.10. The codebook memory, length lMem=147 samples, and the - target vector 5, length 40 samples. - - For the case of 20 ms frames, the encoding procedure looks almost - exactly the same. The only difference is that the size of the start - state is 57 samples and that there are only three sub-blocks to be - encoded. The encoding order is the same as above, starting with the - 23-sample target and then encoding the two remaining 40-sample sub- - blocks, first going forward in time and then going backward in time - relative to the start state. - -3.6.2. Perceptual Weighting of Codebook Memory and Target - - To provide a perceptual weighting of the coding error, a - concatenation of the codebook memory and the target to be coded is - all-pole filtered with the perceptual weighting filter specified in - section 3.4. The filter state of the weighting filter is set to - zero. - - in(0..(lMem-1)) = unweighted codebook memory - in(lMem..(lMem+lTarget-1)) = unweighted target signal - - - in -> Wk(z) -> filtered, - where Wk(z) is taken from the sub-block of the target - - weighted codebook memory = filtered(0..(lMem-1)) - weighted target signal = filtered(lMem..(lMem+lTarget-1)) - - The codebook search is done with the weighted codebook memory and the - weighted target, whereas the decoding and the codebook memory update - uses the unweighted codebook memory. - - - -Andersen, et al. Experimental [Page 22] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -3.6.3. Codebook Creation - - The codebook for the search is created from the perceptually weighted - codebook memory. It consists of two sections, where the first is - referred to as the base codebook and the second as the expanded - codebook, as it is created by linear combinations of the first. Each - of these two sections also has a subsection referred to as the - augmented codebook. The augmented codebook is only created and used - for the coding of the 40-sample sub-blocks and not for the 23/22- - sample sub-block case. The codebook size used for the different - sub-blocks and different stages are summarized in the table below. - - Stage - 1 2 & 3 - -------------------------------------------- - 22 128 (64+0)*2 128 (64+0)*2 - Sub- 1:st 40 256 (108+20)*2 128 (44+20)*2 - Blocks 2:nd 40 256 (108+20)*2 256 (108+20)*2 - 3:rd 40 256 (108+20)*2 256 (108+20)*2 - 4:th 40 256 (108+20)*2 256 (108+20)*2 - - Table 3.1. Codebook sizes for the 30 ms mode. - - Table 3.1 shows the codebook size for the different sub-blocks and - stages for 30 ms frames. Inside the parentheses it shows how the - number of codebook vectors is distributed, within the two sections, - between the base/expanded codebook and the augmented base/expanded - codebook. It should be interpreted in the following way: - (base/expanded cb + augmented base/expanded cb). The total number of - codebook vectors for a specific sub-block and stage is given by the - following formula: - - Tot. cb vectors = base cb + aug. base cb + exp. cb + aug. exp. cb - - The corresponding values to Figure 3.1 for 20 ms frames are only - slightly modified. The short sub-block is 23 instead of 22 samples, - and the 3:rd and 4:th sub-frame are not present. - -3.6.3.1. Creation of a Base Codebook - - The base codebook is given by the perceptually weighted codebook - memory that is mentioned in section 3.5.3. The different codebook - vectors are given by sliding a window of length 23/22 or 40, given by - variable lTarget, over the lMem-long perceptually weighted codebook - memory. The indices are ordered so that the codebook vector - containing sample (lMem-lTarget-n) to (lMem-n-1) of the codebook - - - - - -Andersen, et al. Experimental [Page 23] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - memory vector has index n, where n=0..lMem-lTarget. Thus the total - number of base codebook vectors is lMem-lTarget+1, and the indices - are ordered from sample delay lTarget (23/22 or 40) to lMem+1 (86 or - 148). - -3.6.3.2. Codebook Expansion - - The base codebook is expanded by a factor of 2, creating an - additional section in the codebook. This new section is obtained by - filtering the base codebook, base_cb, with a FIR filter with filter - length CB_FILTERLEN=8. The construction of the expanded codebook - compensates for the delay of four samples introduced by the FIR - filter. - - cbfiltersTbl[CB_FILTERLEN]={-0.033691, 0.083740, -0.144043, - 0.713379, 0.806152, -0.184326, - 0.108887, -0.034180}; - - ___ - \ - exp_cb(k)= + > cbfiltersTbl(i)*x(k-i+4) - /__ - i=0...(LPC_FILTERORDER-1) - - where x(j) = base_cb(j) for j=0..lMem-1 and 0 otherwise - - The individual codebook vectors of the new filtered codebook, exp_cb, - and their indices are obtained in the same fashion as described above - for the base codebook. - -3.6.3.3. Codebook Augmentation - - For cases where encoding entire sub-blocks, i.e., cbveclen=40, the - base and expanded codebooks are augmented to increase codebook - richness. The codebooks are augmented by vectors produced by - interpolation of segments. The base and expanded codebook, - constructed above, consists of vectors corresponding to sample delays - in the range from cbveclen to lMem. The codebook augmentation - attempts to augment these codebooks with vectors corresponding to - sample delays from 20 to 39. However, not all of these samples are - present in the base codebook and expanded codebook, respectively. - Therefore, the augmentation vectors are constructed as linear - combinations between samples corresponding to sample delays in the - range 20 to 39. The general idea of this procedure is presented in - the following figures and text. The procedure is performed for both - the base codebook and the expanded codebook. - - - - - -Andersen, et al. Experimental [Page 24] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - - - ------------------------| - codebook memory | - - - ------------------------| - |-5-|---15---|-5-| - pi pp po - - | | Codebook vector - |---15---|-5-|-----20-----| <- corresponding to - i ii iii sample delay 20 - - Figure 3.11. Generation of the first augmented codebook. - - Figure 3.11 shows the codebook memory with pointers pi, pp, and po, - where pi points to sample 25, pp to sample 20, and po to sample 5. - Below the codebook memory, the augmented codebook vector - corresponding to sample delay 20 is drawn. Segment i consists of - fifteen samples from pointer pp and forward in time. Segment ii - consists of five interpolated samples from pi and forward and from po - and forward. The samples are linearly interpolated with weights - [0.0, 0.2, 0.4, 0.6, 0.8] for pi and weights [1.0, 0.8, 0.6, 0.4, - 0.2] for po. Segment iii consists of twenty samples from pp and - forward. The augmented codebook vector corresponding to sample delay - 21 is produced by moving pointers pp and pi one sample backward in - time. This gives us the following figure. - - - - ------------------------| - codebook memory | - - - ------------------------| - |-5-|---16---|-5-| - pi pp po - - | | Codebook vector - |---16---|-5-|-----19-----| <- corresponding to - i ii iii sample delay 21 - - Figure 3.12. Generation of the second augmented codebook. - - Figure 3.12 shows the codebook memory with pointers pi, pp and po - where pi points to sample 26, pp to sample 21, and po to sample 5. - Below the codebook memory, the augmented codebook vector - corresponding to sample delay 21 is drawn. Segment i now consists of - sixteen samples from pp and forward. Segment ii consists of five - interpolated samples from pi and forward and from po and forward, and - the interpolation weights are the same throughout the procedure. - Segment iii consists of nineteen samples from pp and forward. The - same procedure of moving the two pointers is continued until the last - augmented vector corresponding to sample delay 39 has been created. - This gives a total of twenty new codebook vectors to each of the two - - - -Andersen, et al. Experimental [Page 25] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - sections. Thus the total number of codebook vectors for each of the - two sections, when including the augmented codebook, becomes lMem- - SUBL+1+SUBL/2. This is provided that augmentation is evoked, i.e., - that lTarget=SUBL. - -3.6.4. Codebook Search - - The codebook search uses the codebooks described in the sections - above to find the best match of the perceptually weighted target, see - section 3.6.2. The search method is a multi-stage gain-shape - matching performed as follows. At each stage the best shape vector - is identified, then the gain is calculated and quantized, and finally - the target is updated in preparation for the next codebook search - stage. The number of stages is CB_NSTAGES=3. - - If the target is the 23/22-sample vector the codebooks are indexed so - that the base codebook is followed by the expanded codebook. If the - target is 40 samples the order is as follows: base codebook, - augmented base codebook, expanded codebook, and augmented expanded - codebook. The size of each codebook section and its corresponding - augmented section is given by Table 3.1 in section 3.6.3. - - For example, when the second 40-sample sub-block is coded, indices 0 - - 107 correspond to the base codebook, 108 - 127 correspond to the - augmented base codebook, 128 - 235 correspond to the expanded - codebook, and indices 236 - 255 correspond to the augmented expanded - codebook. The indices are divided in the same fashion for all stages - in the example. Only in the case of coding the first 40-sample sub- - block is there a difference between stages (see Table 3.1). - -3.6.4.1. Codebook Search at Each Stage - - The codebooks are searched to find the best match to the target at - each stage. When the best match is found, the target is updated and - the next-stage search is started. The three chosen codebook vectors - and their corresponding gains constitute the encoded sub-block. The - best match is decided by the following three criteria: - - 1. Compute the measure - - (target*cbvec)^2 / ||cbvec||^2 - - for all codebook vectors, cbvec, and choose the codebook vector - maximizing the measure. The expression (target*cbvec) is the dot - product between the target vector to be coded and the codebook vector - for which we compute the measure. The norm, ||x||, is defined as the - square root of (x*x). - - - - -Andersen, et al. Experimental [Page 26] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - 2. The absolute value of the gain, corresponding to the chosen - codebook vector, cbvec, must be smaller than a fixed limit, - CB_MAXGAIN=1.3: - - |gain| < CB_MAXGAIN - - where the gain is computed in the following way: - - gain = (target*cbvec) / ||cbvec||^2 - - 3. For the first stage, the dot product of the chosen codebook vector - and target must be positive: - - target*cbvec > 0 - - In practice the above criteria are used in a sequential search - through all codebook vectors. The best match is found by registering - a new max measure and index whenever the previously registered max - measure is surpassed and all other criteria are fulfilled. If none - of the codebook vectors fulfill (2) and (3), the first codebook - vector is selected. - -3.6.4.2. Gain Quantization at Each Stage - - The gain follows as a result of the computation - - gain = (target*cbvec) / ||cbvec||^2 - - for the optimal codebook vector found by the procedure in section - 3.6.4.1. - - The three stages quantize the gain, using 5, 4, and 3 bits, - respectively. In the first stage, the gain is limited to positive - values. This gain is quantized by finding the nearest value in the - quantization table gain_sq5Tbl. - - gain_sq5Tbl[32]={0.037476, 0.075012, 0.112488, 0.150024, 0.187500, - 0.224976, 0.262512, 0.299988, 0.337524, 0.375000, - 0.412476, 0.450012, 0.487488, 0.525024, 0.562500, - 0.599976, 0.637512, 0.674988, 0.712524, 0.750000, - 0.787476, 0.825012, 0.862488, 0.900024, 0.937500, - 0.974976, 1.012512, 1.049988, 1.087524, 1.125000, - 1.162476, 1.200012} - - The gains of the subsequent two stages can be either positive or - negative. The gains are quantized by using a quantization table - times a scale factor. The second stage uses the table gain_sq4Tbl, - and the third stage uses gain_sq3Tbl. The scale factor equates 0.1 - - - -Andersen, et al. Experimental [Page 27] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - or the absolute value of the quantized gain representation value - obtained in the previous stage, whichever is larger. Again, the - resulting gain index is the index to the nearest value of the - quantization table times the scale factor. - - gainQ = scaleFact * gain_sqXTbl[index] - - gain_sq4Tbl[16]={-1.049988, -0.900024, -0.750000, -0.599976, - -0.450012, -0.299988, -0.150024, 0.000000, 0.150024, - 0.299988, 0.450012, 0.599976, 0.750000, 0.900024, - 1.049988, 1.200012} - - gain_sq3Tbl[8]={-1.000000, -0.659973, -0.330017,0.000000, - 0.250000, 0.500000, 0.750000, 1.00000} - -3.6.4.3. Preparation of Target for Next Stage - - Before performing the search for the next stage, the perceptually - weighted target vector is updated by subtracting from it the selected - codebook vector (from the perceptually weighted codebook) times the - corresponding quantized gain. - - target[i] = target[i] - gainQ * selected_vec[i]; - - A reference implementation of the codebook encoding is found in - Appendix A.34. - -3.7. Gain Correction Encoding - - The start state is quantized in a relatively model independent manner - using 3 bits per sample. In contrast, the remaining parts of the - block are encoded by using an adaptive codebook. This codebook will - produce high matching accuracy whenever there is a high correlation - between the target and the best codebook vector. For unvoiced speech - segments and background noises, this is not necessarily so, which, - due to the nature of the squared error criterion, results in a coded - signal with less power than the target signal. As the coded start - state has good power matching to the target, the result is a power - fluctuation within the encoded frame. Perceptually, the main problem - with this is that the time envelope of the signal energy becomes - unsteady. To overcome this problem, the gains for the codebooks are - re-scaled after the codebook encoding by searching for a new gain - factor for the first stage codebook that provides better power - matching. - - First, the energy for the target signal, tene, is computed along with - the energy for the coded signal, cene, given by the addition of the - three gain scaled codebook vectors. Because the gains of the second - - - -Andersen, et al. Experimental [Page 28] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - and third stage scale with the gain of the first stage, when the - first stage gain is changed from gain[0] to gain_sq5Tbl[i] the energy - of the coded signal changes from cene to - - cene*(gain_sq5Tbl[i]*gain_sq5Tbl[i])/(gain[0]*gain[0]) - - where gain[0] is the gain for the first stage found in the original - codebook search. A refined search is performed by testing the gain - indices i=0 to 31, and as long as the new codebook energy as given - above is less than tene, the gain index for stage 1 is increased. A - restriction is applied so that the new gain value for stage 1 cannot - be more than two times higher than the original value found in the - codebook search. Note that by using this method we do not change the - shape of the encoded vector, only the gain or amplitude. - -3.8. Bitstream Definition - - The total number of bits used to describe one frame of 20 ms speech - is 304, which fits in 38 bytes and results in a bit rate of 15.20 - kbit/s. For the case of a frame length of 30 ms speech, the total - number of bits used is 400, which fits in 50 bytes and results in a - bit rate of 13.33 kbit/s. In the bitstream definition, the bits are - distributed into three classes according to their bit error or loss - sensitivity. The most sensitive bits (class 1) are placed first in - the bitstream for each frame. The less sensitive bits (class 2) are - placed after the class 1 bits. The least sensitive bits (class 3) - are placed at the end of the bitstream for each frame. - - In the 20/30 ms frame length cases for each class, the following hold - true: The class 1 bits occupy a total of 6/8 bytes (48/64 bits), the - class 2 bits occupy 8/12 bytes (64/96 bits), and the class 3 bits - occupy 24/30 bytes (191/239 bits). This distribution of the bits - enables the use of uneven level protection (ULP) as is exploited in - the payload format definition for iLBC [1]. The detailed bit - allocation is shown in the table below. When a quantization index is - distributed between more classes, the more significant bits belong to - the lowest class. - - - - - - - - - - - - - - -Andersen, et al. Experimental [Page 29] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - Bitstream structure: - - ------------------------------------------------------------------+ - Parameter | Bits Class <1,2,3> | - | 20 ms frame | 30 ms frame | - ----------------------------------+---------------+---------------+ - Split 1 | 6 <6,0,0> | 6 <6,0,0> | - LSF 1 Split 2 | 7 <7,0,0> | 7 <7,0,0> | - LSF Split 3 | 7 <7,0,0> | 7 <7,0,0> | - ------------------+---------------+---------------+ - Split 1 | NA (Not Appl.)| 6 <6,0,0> | - LSF 2 Split 2 | NA | 7 <7,0,0> | - Split 3 | NA | 7 <7,0,0> | - ------------------+---------------+---------------+ - Sum | 20 <20,0,0> | 40 <40,0,0> | - ----------------------------------+---------------+---------------+ - Block Class | 2 <2,0,0> | 3 <3,0,0> | - ----------------------------------+---------------+---------------+ - Position 22 sample segment | 1 <1,0,0> | 1 <1,0,0> | - ----------------------------------+---------------+---------------+ - Scale Factor State Coder | 6 <6,0,0> | 6 <6,0,0> | - ----------------------------------+---------------+---------------+ - Sample 0 | 3 <0,1,2> | 3 <0,1,2> | - Quantized Sample 1 | 3 <0,1,2> | 3 <0,1,2> | - Residual : | : : | : : | - State : | : : | : : | - Samples : | : : | : : | - Sample 56 | 3 <0,1,2> | 3 <0,1,2> | - Sample 57 | NA | 3 <0,1,2> | - ------------------+---------------+---------------+ - Sum | 171 <0,57,114>| 174 <0,58,116>| - ----------------------------------+---------------+---------------+ - Stage 1 | 7 <6,0,1> | 7 <4,2,1> | - CB for 22/23 Stage 2 | 7 <0,0,7> | 7 <0,0,7> | - sample block Stage 3 | 7 <0,0,7> | 7 <0,0,7> | - ------------------+---------------+---------------+ - Sum | 21 <6,0,15> | 21 <4,2,15> | - ----------------------------------+---------------+---------------+ - Stage 1 | 5 <2,0,3> | 5 <1,1,3> | - Gain for 22/23 Stage 2 | 4 <1,1,2> | 4 <1,1,2> | - sample block Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - ------------------+---------------+---------------+ - Sum | 12 <3,1,8> | 12 <2,2,8> | - ----------------------------------+---------------+---------------+ - Stage 1 | 8 <7,0,1> | 8 <6,1,1> | - sub-block 1 Stage 2 | 7 <0,0,7> | 7 <0,0,7> | - Stage 3 | 7 <0,0,7> | 7 <0,0,7> | - ------------------+---------------+---------------+ - - - -Andersen, et al. Experimental [Page 30] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - Stage 1 | 8 <0,0,8> | 8 <0,7,1> | - sub-block 2 Stage 2 | 8 <0,0,8> | 8 <0,0,8> | - Indices Stage 3 | 8 <0,0,8> | 8 <0,0,8> | - for CB ------------------+---------------+---------------+ - sub-blocks Stage 1 | NA | 8 <0,7,1> | - sub-block 3 Stage 2 | NA | 8 <0,0,8> | - Stage 3 | NA | 8 <0,0,8> | - ------------------+---------------+---------------+ - Stage 1 | NA | 8 <0,7,1> | - sub-block 4 Stage 2 | NA | 8 <0,0,8> | - Stage 3 | NA | 8 <0,0,8> | - ------------------+---------------+---------------+ - Sum | 46 <7,0,39> | 94 <6,22,66> | - ----------------------------------+---------------+---------------+ - Stage 1 | 5 <1,2,2> | 5 <1,2,2> | - sub-block 1 Stage 2 | 4 <1,1,2> | 4 <1,2,1> | - Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - ------------------+---------------+---------------+ - Stage 1 | 5 <1,1,3> | 5 <0,2,3> | - sub-block 2 Stage 2 | 4 <0,2,2> | 4 <0,2,2> | - Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - Gains for ------------------+---------------+---------------+ - sub-blocks Stage 1 | NA | 5 <0,1,4> | - sub-block 3 Stage 2 | NA | 4 <0,1,3> | - Stage 3 | NA | 3 <0,0,3> | - ------------------+---------------+---------------+ - Stage 1 | NA | 5 <0,1,4> | - sub-block 4 Stage 2 | NA | 4 <0,1,3> | - Stage 3 | NA | 3 <0,0,3> | - ------------------+---------------+---------------+ - Sum | 24 <3,6,15> | 48 <2,12,34> | - ----------------------------------+---------------+---------------+ - Empty frame indicator | 1 <0,0,1> | 1 <0,0,1> | - ------------------------------------------------------------------- - SUM 304 <48,64,192> 400 <64,96,240> - - Table 3.2. The bitstream definition for iLBC for both the 20 ms - frame size mode and the 30 ms frame size mode. - - When packetized into the payload, the bits MUST be sorted as follows: - All the class 1 bits in the order (from top to bottom) as specified - in the table, all the class 2 bits (from top to bottom), and all the - class 3 bits in the same sequential order. The last bit, the empty - frame indicator, SHOULD be set to zero by the encoder. If this bit - is set to 1 the decoder SHOULD treat the data as a lost frame. For - example, this bit can be set to 1 to indicate lost frame for file - storage format, as in [1]. - - - - -Andersen, et al. Experimental [Page 31] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4. Decoder Principles - - This section describes the principles of each component of the - decoder algorithm. - - +-------------+ +--------+ +---------------+ - payload -> | 1. Get para | -> | 2. LPC | -> | 3. Sc Dequant | -> - +-------------+ +--------+ +---------------+ - - +-------------+ +------------------+ - -> | 4. Mem setup| -> | 5. Construct res |-------> - | +-------------+ +------------------- | - ---------<-----------<-----------<------------ - Sub-frame 0...2/4 (20 ms/30 ms) - - +----------------+ +----------+ - -> | 6. Enhance res | -> | 7. Synth | ------------> - +----------------+ +----------+ - - +-----------------+ - -> | 8. Post Process | ----------------> decoded speech - +-----------------+ - - Figure 4.1. Flow chart of the iLBC decoder. If a frame was lost, - steps 1 to 5 SHOULD be replaced by a PLC algorithm. - - 1. Extract the parameters from the bitstream. - - 2. Decode the LPC and interpolate (section 4.1). - - 3. Construct the 57/58-sample start state (section 4.2). - - 4. Set up the memory by using data from the decoded residual. This - memory is used for codebook construction. For blocks preceding - the start state, both the decoded residual and the target are time - reversed. Sub-frames are decoded in the same order as they were - encoded. - - 5. Construct the residuals of this sub-frame (gain[0]*cbvec[0] + - gain[1]*cbvec[1] + gain[2]*cbvec[2]). Repeat 4 and 5 until the - residual of all sub-blocks has been constructed. - - 6. Enhance the residual with the post filter (section 4.6). - - 7. Synthesis of the residual (section 4.7). - - 8. Post process with HP filter, if desired (section 4.8). - - - - -Andersen, et al. Experimental [Page 32] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4.1. LPC Filter Reconstruction - - The decoding of the LP filter parameters is very straightforward. - For a set of three/six indices, the corresponding LSF vector(s) are - found by simple table lookup. For each of the LSF vectors, the three - split vectors are concatenated to obtain qlsf1 and qlsf2, - respectively (in the 20 ms mode only one LSF vector, qlsf, is - constructed). The next step is the stability check described in - section 3.2.5 followed by the interpolation scheme described in - section 3.2.6 (3.2.7 for 20 ms frames). The only difference is that - only the quantized LSFs are known at the decoder, and hence the - unquantized LSFs are not processed. - - A reference implementation of the LPC filter reconstruction is given - in Appendix A.36. - -4.2. Start State Reconstruction - - The scalar encoded STATE_SHORT_LEN=58 (STATE_SHORT_LEN=57 in the 20 - ms mode) state samples are reconstructed by 1) forming a set of - samples (by table lookup) from the index stream idxVec[n], 2) - multiplying the set with 1/scal=(10^qmax)/4.5, 3) time reversing the - 57/58 samples, 4) filtering the time reversed block with the - dispersion (all-pass) filter used in the encoder (as described in - section 3.5.2); this compensates for the phase distortion of the - earlier filter operation, and 5 reversing the 57/58 samples from the - previous step. - - in(0..(STATE_SHORT_LEN-1)) = time reversed samples from table - look-up, - idxVecDec((STATE_SHORT_LEN-1)..0) - - in(STATE_SHORT_LEN..(2*STATE_SHORT_LEN-1)) = 0 - - Pk(z) = A~rk(z)/A~k(z), where - ___ - \ - A~rk(z)= z^(-LPC_FILTERORDER) + > a~ki*z^(i-(LPC_FILTERORDER-1)) - /__ - i=0...(LPC_FILTERORDER-1) - - and A~k(z) is taken from the block where the start state begins - - in -> Pk(z) -> filtered - - out(k) = filtered(STATE_SHORT_LEN-1-k) + - filtered(2*STATE_SHORT_LEN-1-k), - k=0..(STATE_SHORT_LEN-1) - - - -Andersen, et al. Experimental [Page 33] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - The remaining 23/22 samples in the state are reconstructed by the - same adaptive codebook technique described in section 4.3. The - location bit determines whether these are the first or the last 23/22 - samples of the 80-sample state vector. If the remaining 23/22 - samples are the first samples, then the scalar encoded - STATE_SHORT_LEN state samples are time-reversed before initialization - of the adaptive codebook memory vector. - - A reference implementation of the start state reconstruction is given - in Appendix A.44. - -4.3. Excitation Decoding Loop - - The decoding of the LPC excitation vector proceeds in the same order - in which the residual was encoded at the encoder. That is, after the - decoding of the entire 80-sample state vector, the forward sub-blocks - (corresponding to samples occurring after the state vector samples) - are decoded, and then the backward sub-blocks (corresponding to - samples occurring before the state vector) are decoded, resulting in - a fully decoded block of excitation signal samples. - - In particular, each sub-block is decoded by using the multistage - adaptive codebook decoding module described in section 4.4. This - module relies upon an adaptive codebook memory constructed before - each run of the adaptive codebook decoding. The construction of the - adaptive codebook memory in the decoder is identical to the method - outlined in section 3.6.3, except that it is done on the codebook - memory without perceptual weighting. - - For the initial forward sub-block, the last STATE_LEN=80 samples of - the length CB_LMEM=147 adaptive codebook memory are filled with the - samples of the state vector. For subsequent forward sub-blocks, the - first SUBL=40 samples of the adaptive codebook memory are discarded, - the remaining samples are shifted by SUBL samples toward the - beginning of the vector, and the newly decoded SUBL=40 samples are - placed at the end of the adaptive codebook memory. For backward - sub-blocks, the construction is similar, except that every vector of - samples involved is first time reversed. - - A reference implementation of the excitation decoding loop is found - in Appendix A.5. - - - - - - - - - - -Andersen, et al. Experimental [Page 34] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4.4. Multistage Adaptive Codebook Decoding - - The Multistage Adaptive Codebook Decoding module is used at both the - sender (encoder) and the receiver (decoder) ends to produce a - synthetic signal in the residual domain that is eventually used to - produce synthetic speech. The module takes the index values used to - construct vectors that are scaled and summed together to produce a - synthetic signal that is the output of the module. - -4.4.1. Construction of the Decoded Excitation Signal - - The unpacked index values provided at the input to the module are - references to extended codebooks, which are constructed as described - in section 3.6.3, except that they are based on the codebook memory - without the perceptual weighting. The unpacked three indices are - used to look up three codebook vectors. The unpacked three gain - indices are used to decode the corresponding 3 gains. In this - decoding, the successive rescaling, as described in section 3.6.4.2, - is applied. - - A reference implementation of the adaptive codebook decoding is - listed in Appendix A.32. - -4.5. Packet Loss Concealment - - If packet loss occurs, the decoder receives a signal saying that - information regarding a block is lost. For such blocks it is - RECOMMENDED to use a Packet Loss Concealment (PLC) unit to create a - decoded signal that masks the effect of that packet loss. In the - following we will describe an example of a PLC unit that can be used - with the iLBC codec. As the PLC unit is used only at the decoder, - the PLC unit does not affect interoperability between - implementations. Other PLC implementations MAY therefore be used. - - The PLC described operates on the LP filters and the excitation - signals and is based on the following principles: - -4.5.1. Block Received Correctly and Previous Block Also Received - - If the block is received correctly, the PLC only records state - information of the current block that can be used in case the next - block is lost. The LP filter coefficients for each sub-block and the - entire decoded excitation signal are all saved in the decoder state - structure. All of this information will be needed if the following - block is lost. - - - - - - -Andersen, et al. Experimental [Page 35] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4.5.2. Block Not Received - - If the block is not received, the block substitution is based on a - pitch-synchronous repetition of the excitation signal, which is - filtered by the last LP filter of the previous block. The previous - block's information is stored in the decoder state structure. - - A correlation analysis is performed on the previous block's - excitation signal in order to detect the amount of pitch periodicity - and a pitch value. The correlation measure is also used to decide on - the voicing level (the degree to which the previous block's - excitation was a voiced or roughly periodic signal). The excitation - in the previous block is used to create an excitation for the block - to be substituted, such that the pitch of the previous block is - maintained. Therefore, the new excitation is constructed in a - pitch-synchronous manner. In order to avoid a buzzy-sounding - substituted block, a random excitation is mixed with the new pitch - periodic excitation, and the relative use of the two components is - computed from the correlation measure (voicing level). - - For the block to be substituted, the newly constructed excitation - signal is then passed through the LP filter to produce the speech - that will be substituted for the lost block. - - For several consecutive lost blocks, the packet loss concealment - continues in a similar manner. The correlation measure of the last - block received is still used along with the same pitch value. The LP - filters of the last block received are also used again. The energy - of the substituted excitation for consecutive lost blocks is - decreased, leading to a dampened excitation, and therefore to - dampened speech. - -4.5.3. Block Received Correctly When Previous Block Not Received - - For the case in which a block is received correctly when the previous - block was not, the correctly received block's directly decoded speech - (based solely on the received block) is not used as the actual - output. The reason for this is that the directly decoded speech does - not necessarily smoothly merge into the synthetic speech generated - for the previous lost block. If the two signals are not smoothly - merged, an audible discontinuity is accidentally produced. - Therefore, a correlation analysis between the two blocks of - excitation signal (the excitation of the previous concealed block and - that of the current received block) is performed to find the best - phase match. Then a simple overlap-add procedure is performed to - merge the previous excitation smoothly into the current block's - excitation. - - - - -Andersen, et al. Experimental [Page 36] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - The exact implementation of the packet loss concealment does not - influence interoperability of the codec. - - A reference implementation of the packet loss concealment is - suggested in Appendix A.14. Exact compliance with this suggested - algorithm is not needed for a reference implementation to be fully - compatible with the overall codec specification. - -4.6. Enhancement - - The decoder contains an enhancement unit that operates on the - reconstructed excitation signal. The enhancement unit increases the - perceptual quality of the reconstructed signal by reducing the - speech-correlated noise in the voiced speech segments. Compared to - traditional postfilters, the enhancer has an advantage in that it can - only modify the excitation signal slightly. This means that there is - no risk of over enhancement. The enhancer works very similarly for - both the 20 ms frame size mode and the 30 ms frame size mode. - - For the mode with 20 ms frame size, the enhancer uses a memory of six - 80-sample excitation blocks prior in time plus the two new 80-sample - excitation blocks. For each block of 160 new unenhanced excitation - samples, 160 enhanced excitation samples are produced. The enhanced - excitation is 40-sample delayed compared to the unenhanced - excitation, as the enhancer algorithm uses lookahead. - - For the mode with 30 ms frame size, the enhancer uses a memory of - five 80-sample excitation blocks prior in time plus the three new - 80-sample excitation blocks. For each block of 240 new unenhanced - excitation samples, 240 enhanced excitation samples are produced. - The enhanced excitation is 80-sample delayed compared to the - unenhanced excitation, as the enhancer algorithm uses lookahead. - - Outline of Enhancer - - The speech enhancement unit operates on sub-blocks of 80 samples, - which means that there are two/three 80 sample sub-blocks per frame. - Each of these two/three sub-blocks is enhanced separately, but in an - analogous manner. - - - - - - - - - - - - -Andersen, et al. Experimental [Page 37] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - unenhanced residual - | - | +---------------+ +--------------+ - +-> | 1. Pitch Est | -> | 2. Find PSSQ | --------> - +---------------+ | +--------------+ - +-----<-------<------<--+ - +------------+ enh block 0..1/2 | - -> | 3. Smooth | | - +------------+ | - \ | - /\ | - / \ Already | - / 4. \----------->----------->-----------+ | - \Crit/ Fulfilled | | - \? / v | - \/ | | - \ +-----------------+ +---------+ | | - Not +->| 5. Use Constr. | -> | 6. Mix | -----> - Fulfilled +-----------------+ +---------+ - - ---------------> enhanced residual - - Figure 4.2. Flow chart of the enhancer. - - 1. Pitch estimation of each of the two/three new 80-sample blocks. - - 2. Find the pitch-period-synchronous sequence n (for block k) by a - search around the estimated pitch value. Do this for n=1,2,3, - -1,-2,-3. - - 3. Calculate the smoothed residual generated by the six pitch- - period-synchronous sequences from prior step. - - 4. Check if the smoothed residual satisfies the criterion (section - 4.6.4). - - 5. Use constraint to calculate mixing factor (section 4.6.5). - - 6. Mix smoothed signal with unenhanced residual (pssq(n) n=0). - - The main idea of the enhancer is to find three 80 sample blocks - before and three 80-sample blocks after the analyzed unenhanced sub- - block and to use these to improve the quality of the excitation in - that sub-block. The six blocks are chosen so that they have the - highest possible correlation with the unenhanced sub-block that is - being enhanced. In other words, the six blocks are pitch-period- - synchronous sequences to the unenhanced sub-block. - - - - -Andersen, et al. Experimental [Page 38] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - A linear combination of the six pitch-period-synchronous sequences is - calculated that approximates the sub-block. If the squared error - between the approximation and the unenhanced sub-block is small - enough, the enhanced residual is set equal to this approximation. - For the cases when the squared error criterion is not fulfilled, a - linear combination of the approximation and the unenhanced residual - forms the enhanced residual. - -4.6.1. Estimating the Pitch - - Pitch estimates are needed to determine the locations of the pitch- - period-synchronous sequences in a complexity-efficient way. For each - of the new two/three sub-blocks, a pitch estimate is calculated by - finding the maximum correlation in the range from lag 20 to lag 120. - These pitch estimates are used to narrow down the search for the best - possible pitch-period-synchronous sequences. - -4.6.2. Determination of the Pitch-Synchronous Sequences - - Upon receiving the pitch estimates from the prior step, the enhancer - analyzes and enhances one 80-sample sub-block at a time. The pitch- - period-synchronous-sequences pssq(n) can be viewed as vectors of - length 80 samples each shifted n*lag samples from the current sub- - block. The six pitch-period-synchronous-sequences, pssq(-3) to - pssq(-1) and pssq(1) to pssq(3), are found one at a time by the steps - below: - - 1) Calculate the estimate of the position of the pssq(n). For - pssq(n) in front of pssq(0) (n > 0), the location of the pssq(n) - is estimated by moving one pitch estimate forward in time from the - exact location of pssq(n-1). Similarly, pssq(n) behind pssq(0) (n - < 0) is estimated by moving one pitch estimate backward in time - from the exact location of pssq(n+1). If the estimated pssq(n) - vector location is totally within the enhancer memory (Figure - 4.3), steps 2, 3, and 4 are performed, otherwise the pssq(n) is - set to zeros. - - 2) Compute the correlation between the unenhanced excitation and - vectors around the estimated location interval of pssq(n). The - correlation is calculated in the interval estimated location +/- 2 - samples. This results in five correlation values. - - 3) The five correlation values are upsampled by a factor of 4, by - using four simple upsampling filters (MA filters with coefficients - upsFilter1.. upsFilter4). Within these the maximum value is - found, which specifies the best pitch-period with a resolution of - a quarter of a sample. - - - - -Andersen, et al. Experimental [Page 39] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - upsFilter1[7]={0.000000 0.000000 0.000000 1.000000 - 0.000000 0.000000 0.000000} - upsFilter2[7]={0.015625 -0.076904 0.288330 0.862061 - -0.106445 0.018799 -0.015625} - upsFilter3[7]={0.023682 -0.124268 0.601563 0.601563 - -0.124268 0.023682 -0.023682} - upsFilter4[7]={0.018799 -0.106445 0.862061 0.288330 - -0.076904 0.015625 -0.018799} - - 4) Generate the pssq(n) vector by upsampling of the excitation memory - and extracting the sequence that corresponds to the lag delay that - was calculated in prior step. - - With the steps above, all the pssq(n) can be found in an iterative - manner, first moving backward in time from pssq(0) and then forward - in time from pssq(0). - - - 0 159 319 479 639 - +---------------------------------------------------------------+ - | -5 | -4 | -3 | -2 | -1 | 0 | 1 | 2 | - +---------------------------------------------------------------+ - |pssq 0 | - |pssq -1| |pssq 1 | - |pssq -2| |pssq 2 | - |pssq -3| |pssq 3 | - - Figure 4.3. Enhancement for 20 ms frame size. - - Figure 4.3 depicts pitch-period-synchronous sequences in the - enhancement of the first 80 sample block in the 20 ms frame size - mode. The unenhanced signal input is stored in the last two sub- - blocks (1 - 2), and the six other sub-blocks contain unenhanced - residual prior-in-time. We perform the enhancement algorithm on two - blocks of 80 samples, where the first of the two blocks consists of - the last 40 samples of sub-block 0 and the first 40 samples of sub- - block 1. The second 80-sample block consists of the last 40 samples - of sub-block 1 and the first 40 samples of sub-block 2. - - - - - - - - - - - - - -Andersen, et al. Experimental [Page 40] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - 0 159 319 479 639 - +---------------------------------------------------------------+ - | -4 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | - +---------------------------------------------------------------+ - |pssq 0 | - |pssq -1| |pssq 1 | - |pssq -2| |pssq 2 | - |pssq -3| |pssq 3 | - - Figure 4.4. Enhancement for 30 ms frame size. - - Figure 4.4 depicts pitch-period-synchronous sequences in the - enhancement of the first 80-sample block in the 30 ms frame size - mode. The unenhanced signal input is stored in the last three sub- - blocks (1 - 3). The five other sub-blocks contain unenhanced - residual prior-in-time. The enhancement algorithm is performed on - the three 80 sample sub-blocks 0, 1, and 2. - -4.6.3. Calculation of the Smoothed Excitation - - A linear combination of the six pssq(n) (n!=0) form a smoothed - approximation, z, of pssq(0). Most of the weight is put on the - sequences that are close to pssq(0), as these are likely to be most - similar to pssq(0). The smoothed vector is also rescaled so that the - energy of z is the same as the energy of pssq(0). - - ___ - \ - y = > pssq(i) * pssq_weight(i) - /__ - i=-3,-2,-1,1,2,3 - - pssq_weight(i) = 0.5*(1-cos(2*pi*(i+4)/(2*3+2))) - - z = C * y, where C = ||pssq(0)||/||y|| - -4.6.4. Enhancer Criterion - - The criterion of the enhancer is that the enhanced excitation is not - allowed to differ much from the unenhanced excitation. This - criterion is checked for each 80-sample sub-block. - - e < (b * ||pssq(0)||^2), where b=0.05 and (Constraint 1) - - e = (pssq(0)-z)*(pssq(0)-z), and "*" means the dot product - - - - - - -Andersen, et al. Experimental [Page 41] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4.6.5. Enhancing the excitation - - From the criterion in the previous section, it is clear that the - excitation is not allowed to change much. The purpose of this - constraint is to prevent the creation of an enhanced signal - significantly different from the original signal. This also means - that the constraint limits the numerical size of the errors that the - enhancement procedure can make. That is especially important in - unvoiced segments and background noise segments for which increased - periodicity could lead to lower perceived quality. - - When the constraint in the prior section is not met, the enhanced - residual is instead calculated through a constrained optimization by - using the Lagrange multiplier technique. The new constraint is that - - e = (b * ||pssq(0)||^2) (Constraint 2) - - We distinguish two solution regions for the optimization: 1) the - region where the first constraint is fulfilled and 2) the region - where the first constraint is not fulfilled and the second constraint - must be used. - - In the first case, where the second constraint is not needed, the - optimized re-estimated vector is simply z, the energy-scaled version - of y. - - In the second case, where the second constraint is activated and - becomes an equality constraint, we have - - z= A*y + B*pssq(0) - - where - - A = sqrt((b-b^2/4)*(w00*w00)/ (w11*w00 + w10*w10)) and - - w11 = pssq(0)*pssq(0) - w00 = y*y - w10 = y*pssq(0) (* symbolizes the dot product) - - and - - B = 1 - b/2 - A * w10/w00 - - Appendix A.16 contains a listing of a reference implementation for - the enhancement method. - - - - - - -Andersen, et al. Experimental [Page 42] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -4.7. Synthesis Filtering - - Upon decoding or PLC of the LP excitation block, the decoded speech - block is obtained by running the decoded LP synthesis filter, - 1/A~k(z), over the block. The synthesis filters have to be shifted - to compensate for the delay in the enhancer. For 20 ms frame size - mode, they SHOULD be shifted one 40-sample sub-block, and for 30 ms - frame size mode, they SHOULD be shifted two 40-sample sub-blocks. - The LP coefficients SHOULD be changed at the first sample of every - sub-block while keeping the filter state. For PLC blocks, one - solution is to apply the last LP coefficients of the last decoded - speech block for all sub-blocks. - - The reference implementation for the synthesis filtering can be found - in Appendix A.48. - -4.8. Post Filtering - - If desired, the decoded block can be filtered by a high-pass filter. - This removes the low frequencies of the decoded signal. A reference - implementation of this, with cutoff at 65 Hz, is shown in Appendix - A.30. - -5. Security Considerations - - This algorithm for the coding of speech signals is not subject to any - known security consideration; however, its RTP payload format [1] is - subject to several considerations, which are addressed there. - Confidentiality of the media streams is achieved by encryption; - therefore external mechanisms, such as SRTP [5], MAY be used for that - purpose. - -6. Evaluation of the iLBC Implementations - - It is possible and suggested to evaluate certain iLBC implementation - by utilizing methodology and tools available at - http://www.ilbcfreeware.org/evaluation.html - -7. References - -7.1. Normative References - - [1] Duric, A. and S. Andersen, "Real-time Transport Protocol (RTP) - Payload Format for internet Low Bit Rate Codec (iLBC) Speech", - RFC 3952, December 2004. - - [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement - Levels", BCP 14, RFC 2119, March 1997. - - - -Andersen, et al. Experimental [Page 43] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - [3] PacketCable(TM) Audio/Video Codecs Specification, Cable - Television Laboratories, Inc. - -7.2. Informative References - - [4] ITU-T Recommendation G.711, available online from the ITU - bookstore at http://www.itu.int. - - [5] Baugher, M., McGrew, D., Naslund, M., Carrara, E., and K. Norman, - "The Secure Real Time Transport Protocol (SRTP)", RFC 3711, March - 2004. - -8. Acknowledgements - - This extensive work, besides listed authors, has the following - authors, who could not have been listed among "official" authors (due - to IESG restrictions in the number of authors who can be listed): - - Manohar N. Murthi (Department of Electrical and Computer - Engineering, University of Miami), Fredrik Galschiodt, Julian - Spittka, and Jan Skoglund (Global IP Sound). - - The authors are deeply indebted to the following people and thank - them sincerely: - - Henry Sinnreich, Patrik Faltstrom, Alan Johnston, and Jean- - Francois Mule for great support of the iLBC initiative and for - valuable feedback and comments. - - Peter Vary, Frank Mertz, and Christoph Erdmann (RWTH Aachen); - Vladimir Cuperman (Niftybox LLC); Thomas Eriksson (Chalmers Univ - of Tech), and Gernot Kubin (TU Graz), for thorough review of the - iLBC document and their valuable feedback and remarks. - - - - - - - - - - - - - - - - - - -Andersen, et al. Experimental [Page 44] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -APPENDIX A. Reference Implementation - - This appendix contains the complete c-code for a reference - implementation of encoder and decoder for the specified codec. - - The c-code consists of the following files with highest-level - functions: - - iLBC_test.c: main function for evaluation purpose - iLBC_encode.h: encoder header - iLBC_encode.c: encoder function - iLBC_decode.h: decoder header - iLBC_decode.c: decoder function - - The following files contain global defines and constants: - - iLBC_define.h: global defines - constants.h: global constants header - constants.c: global constants memory allocations - - The following files contain subroutines: - - anaFilter.h: lpc analysis filter header - anaFilter.c: lpc analysis filter function - createCB.h: codebook construction header - createCB.c: codebook construction function - doCPLC.h: packet loss concealment header - doCPLC.c: packet loss concealment function - enhancer.h: signal enhancement header - enhancer.c: signal enhancement function - filter.h: general filter header - filter.c: general filter functions - FrameClassify.h: start state classification header - FrameClassify.c: start state classification function - gainquant.h: gain quantization header - gainquant.c: gain quantization function - getCBvec.h: codebook vector construction header - getCBvec.c: codebook vector construction function - helpfun.h: general purpose header - helpfun.c: general purpose functions - hpInput.h: input high pass filter header - hpInput.c: input high pass filter function - hpOutput.h: output high pass filter header - hpOutput.c: output high pass filter function - iCBConstruct.h: excitation decoding header - iCBConstruct.c: excitation decoding function - iCBSearch.h: excitation encoding header - iCBSearch.c: excitation encoding function - - - -Andersen, et al. Experimental [Page 45] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - LPCdecode.h: lpc decoding header - LPCdecode.c: lpc decoding function - LPCencode.h: lpc encoding header - LPCencode.c: lpc encoding function - lsf.h: line spectral frequencies header - lsf.c: line spectral frequencies functions - packing.h: bitstream packetization header - packing.c: bitstream packetization functions - StateConstructW.h: state decoding header - StateConstructW.c: state decoding functions - StateSearchW.h: state encoding header - StateSearchW.c: state encoding function - syntFilter.h: lpc synthesis filter header - syntFilter.c: lpc synthesis filter function - - The implementation is portable and should work on many different - platforms. However, it is not difficult to optimize the - implementation on particular platforms, an exercise left to the - reader. - -A.1. iLBC_test.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_test.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - #include - #include - #include "iLBC_define.h" - #include "iLBC_encode.h" - #include "iLBC_decode.h" - - /* Runtime statistics */ - #include - - #define ILBCNOOFWORDS_MAX (NO_OF_BYTES_30MS/2) - - /*----------------------------------------------------------------* - * Encoder interface function - - - -Andersen, et al. Experimental [Page 46] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - *---------------------------------------------------------------*/ - - short encode( /* (o) Number of bytes encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i/o) Encoder instance */ - short *encoded_data, /* (o) The encoded bytes */ - short *data /* (i) The signal block to encode*/ - ){ - float block[BLOCKL_MAX]; - int k; - - /* convert signal to float */ - - for (k=0; kblockl; k++) - block[k] = (float)data[k]; - - /* do the actual encoding */ - - iLBC_encode((unsigned char *)encoded_data, block, iLBCenc_inst); - - - return (iLBCenc_inst->no_of_bytes); - } - - /*----------------------------------------------------------------* - * Decoder interface function - *---------------------------------------------------------------*/ - - short decode( /* (o) Number of decoded samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - short *decoded_data, /* (o) Decoded signal block*/ - short *encoded_data, /* (i) Encoded bytes */ - short mode /* (i) 0=PL, 1=Normal */ - ){ - int k; - float decblock[BLOCKL_MAX], dtmp; - - /* check if mode is valid */ - - if (mode<0 || mode>1) { - printf("\nERROR - Wrong mode - 0, 1 allowed\n"); exit(3);} - - /* do actual decoding of block */ - - iLBC_decode(decblock, (unsigned char *)encoded_data, - iLBCdec_inst, mode); - - /* convert to short */ - - - -Andersen, et al. Experimental [Page 47] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - for (k=0; kblockl; k++){ - dtmp=decblock[k]; - - if (dtmpMAX_SAMPLE) - dtmp=MAX_SAMPLE; - decoded_data[k] = (short) dtmp; - } - - return (iLBCdec_inst->blockl); - } - - /*---------------------------------------------------------------* - * Main program to test iLBC encoding and decoding - * - * Usage: - * exefile_name.exe - * - * : Input file, speech for encoder (16-bit pcm file) - * : Bit stream output from the encoder - * : Output file, decoded speech (16-bit pcm file) - * : Bit error file, optional (16-bit) - * 1 - Packet received correctly - * 0 - Packet Lost - * - *--------------------------------------------------------------*/ - - int main(int argc, char* argv[]) - { - - /* Runtime statistics */ - - float starttime; - float runtime; - float outtime; - - FILE *ifileid,*efileid,*ofileid, *cfileid; - short data[BLOCKL_MAX]; - short encoded_data[ILBCNOOFWORDS_MAX], decoded_data[BLOCKL_MAX]; - int len; - short pli, mode; - int blockcount = 0; - int packetlosscount = 0; - - /* Create structs */ - iLBC_Enc_Inst_t Enc_Inst; - iLBC_Dec_Inst_t Dec_Inst; - - - -Andersen, et al. Experimental [Page 48] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* get arguments and open files */ - - if ((argc!=5) && (argc!=6)) { - fprintf(stderr, - "\n*-----------------------------------------------*\n"); - fprintf(stderr, - " %s <20,30> input encoded decoded (channel)\n\n", - argv[0]); - fprintf(stderr, - " mode : Frame size for the encoding/decoding\n"); - fprintf(stderr, - " 20 - 20 ms\n"); - fprintf(stderr, - " 30 - 30 ms\n"); - fprintf(stderr, - " input : Speech for encoder (16-bit pcm file)\n"); - fprintf(stderr, - " encoded : Encoded bit stream\n"); - fprintf(stderr, - " decoded : Decoded speech (16-bit pcm file)\n"); - fprintf(stderr, - " channel : Packet loss pattern, optional (16-bit)\n"); - fprintf(stderr, - " 1 - Packet received correctly\n"); - fprintf(stderr, - " 0 - Packet Lost\n"); - fprintf(stderr, - "*-----------------------------------------------*\n\n"); - exit(1); - } - mode=atoi(argv[1]); - if (mode != 20 && mode != 30) { - fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", - argv[1]); - exit(2); - } - if ( (ifileid=fopen(argv[2],"rb")) == NULL) { - fprintf(stderr,"Cannot open input file %s\n", argv[2]); - exit(2);} - if ( (efileid=fopen(argv[3],"wb")) == NULL) { - fprintf(stderr, "Cannot open encoded file %s\n", - argv[3]); exit(1);} - if ( (ofileid=fopen(argv[4],"wb")) == NULL) { - fprintf(stderr, "Cannot open decoded file %s\n", - argv[4]); exit(1);} - if (argc==6) { - if( (cfileid=fopen(argv[5],"rb")) == NULL) { - fprintf(stderr, "Cannot open channel file %s\n", - - - -Andersen, et al. Experimental [Page 49] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - argv[5]); - exit(1); - } - } else { - cfileid=NULL; - } - - /* print info */ - - fprintf(stderr, "\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* iLBC test program *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr,"\nMode : %2d ms\n", mode); - fprintf(stderr,"Input file : %s\n", argv[2]); - fprintf(stderr,"Encoded file : %s\n", argv[3]); - fprintf(stderr,"Output file : %s\n", argv[4]); - if (argc==6) { - fprintf(stderr,"Channel file : %s\n", argv[5]); - } - fprintf(stderr,"\n"); - - /* Initialization */ - - initEncode(&Enc_Inst, mode); - initDecode(&Dec_Inst, mode, 1); - - /* Runtime statistics */ - - starttime=clock()/(float)CLOCKS_PER_SEC; - - /* loop over input blocks */ - - while (fread(data,sizeof(short),Enc_Inst.blockl,ifileid)== - Enc_Inst.blockl) { - - blockcount++; - - /* encoding */ - - - -Andersen, et al. Experimental [Page 50] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - fprintf(stderr, "--- Encoding block %i --- ",blockcount); - len=encode(&Enc_Inst, encoded_data, data); - fprintf(stderr, "\r"); - - /* write byte file */ - - fwrite(encoded_data, sizeof(unsigned char), len, efileid); - - /* get channel data if provided */ - if (argc==6) { - if (fread(&pli, sizeof(short), 1, cfileid)) { - if ((pli!=0)&&(pli!=1)) { - fprintf(stderr, "Error in channel file\n"); - exit(0); - } - if (pli==0) { - /* Packet loss -> remove info from frame */ - memset(encoded_data, 0, - sizeof(short)*ILBCNOOFWORDS_MAX); - packetlosscount++; - } - } else { - fprintf(stderr, "Error. Channel file too short\n"); - exit(0); - } - } else { - pli=1; - } - - /* decoding */ - - fprintf(stderr, "--- Decoding block %i --- ",blockcount); - - len=decode(&Dec_Inst, decoded_data, encoded_data, pli); - fprintf(stderr, "\r"); - - /* write output file */ - - fwrite(decoded_data,sizeof(short),len,ofileid); - } - - /* Runtime statistics */ - - runtime = (float)(clock()/(float)CLOCKS_PER_SEC-starttime); - outtime = (float)((float)blockcount*(float)mode/1000.0); - printf("\n\nLength of speech file: %.1f s\n", outtime); - printf("Packet loss : %.1f%%\n", - 100.0*(float)packetlosscount/(float)blockcount); - - - -Andersen, et al. Experimental [Page 51] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - printf("Time to run iLBC :"); - printf(" %.1f s (%.1f %% of realtime)\n\n", runtime, - (100*runtime/outtime)); - - /* close files */ - - fclose(ifileid); fclose(efileid); fclose(ofileid); - if (argc==6) { - fclose(cfileid); - } - return(0); - } - -A.2. iLBC_encode.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_encode.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_ILBCENCODE_H - #define __iLBC_ILBCENCODE_H - - #include "iLBC_define.h" - - short initEncode( /* (o) Number of bytes - encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ - int mode /* (i) frame size mode */ - ); - - void iLBC_encode( - - unsigned char *bytes, /* (o) encoded data bits iLBC */ - float *block, /* (o) speech vector to - encode */ - iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder - state */ - ); - - #endif - - - - -Andersen, et al. Experimental [Page 52] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -A.3. iLBC_encode.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_encode.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - #include - - #include "iLBC_define.h" - #include "LPCencode.h" - #include "FrameClassify.h" - #include "StateSearchW.h" - #include "StateConstructW.h" - #include "helpfun.h" - #include "constants.h" - #include "packing.h" - #include "iCBSearch.h" - #include "iCBConstruct.h" - #include "hpInput.h" - #include "anaFilter.h" - #include "syntFilter.h" - - /*----------------------------------------------------------------* - * Initiation of encoder instance. - *---------------------------------------------------------------*/ - - short initEncode( /* (o) Number of bytes - encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ - int mode /* (i) frame size mode */ - ){ - iLBCenc_inst->mode = mode; - if (mode==30) { - iLBCenc_inst->blockl = BLOCKL_30MS; - iLBCenc_inst->nsub = NSUB_30MS; - iLBCenc_inst->nasub = NASUB_30MS; - iLBCenc_inst->lpc_n = LPC_N_30MS; - iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS; - iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS; - - - -Andersen, et al. Experimental [Page 53] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - iLBCenc_inst->state_short_len=STATE_SHORT_LEN_30MS; - /* ULP init */ - iLBCenc_inst->ULP_inst=&ULP_30msTbl; - } - else if (mode==20) { - iLBCenc_inst->blockl = BLOCKL_20MS; - iLBCenc_inst->nsub = NSUB_20MS; - iLBCenc_inst->nasub = NASUB_20MS; - iLBCenc_inst->lpc_n = LPC_N_20MS; - iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS; - iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS; - iLBCenc_inst->state_short_len=STATE_SHORT_LEN_20MS; - /* ULP init */ - iLBCenc_inst->ULP_inst=&ULP_20msTbl; - } - else { - exit(2); - } - - memset((*iLBCenc_inst).anaMem, 0, - LPC_FILTERORDER*sizeof(float)); - memcpy((*iLBCenc_inst).lsfold, lsfmeanTbl, - LPC_FILTERORDER*sizeof(float)); - memcpy((*iLBCenc_inst).lsfdeqold, lsfmeanTbl, - LPC_FILTERORDER*sizeof(float)); - memset((*iLBCenc_inst).lpc_buffer, 0, - (LPC_LOOKBACK+BLOCKL_MAX)*sizeof(float)); - memset((*iLBCenc_inst).hpimem, 0, 4*sizeof(float)); - - return (iLBCenc_inst->no_of_bytes); - } - - /*----------------------------------------------------------------* - * main encoder function - *---------------------------------------------------------------*/ - - void iLBC_encode( - unsigned char *bytes, /* (o) encoded data bits iLBC */ - float *block, /* (o) speech vector to - encode */ - iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder - state */ - ){ - - float data[BLOCKL_MAX]; - float residual[BLOCKL_MAX], reverseResidual[BLOCKL_MAX]; - - int start, idxForMax, idxVec[STATE_LEN]; - - - -Andersen, et al. Experimental [Page 54] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML]; - int n, k, meml_gotten, Nfor, Nback, i, pos; - int gain_index[CB_NSTAGES*NASUB_MAX], - extra_gain_index[CB_NSTAGES]; - int cb_index[CB_NSTAGES*NASUB_MAX],extra_cb_index[CB_NSTAGES]; - int lsf_i[LSF_NSPLIT*LPC_N_MAX]; - unsigned char *pbytes; - int diff, start_pos, state_first; - float en1, en2; - int index, ulp, firstpart; - int subcount, subframe; - float weightState[LPC_FILTERORDER]; - float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; - float weightdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; - float decresidual[BLOCKL_MAX]; - - /* high pass filtering of input signal if such is not done - prior to calling this function */ - - hpInput(block, iLBCenc_inst->blockl, - data, (*iLBCenc_inst).hpimem); - - /* otherwise simply copy */ - - /*memcpy(data,block,iLBCenc_inst->blockl*sizeof(float));*/ - - /* LPC of hp filtered input data */ - - LPCencode(syntdenum, weightdenum, lsf_i, data, iLBCenc_inst); - - - /* inverse filter to get residual */ - - for (n=0; nnsub; n++) { - anaFilter(&data[n*SUBL], &syntdenum[n*(LPC_FILTERORDER+1)], - SUBL, &residual[n*SUBL], iLBCenc_inst->anaMem); - } - - /* find state location */ - - start = FrameClassify(iLBCenc_inst, residual); - - /* check if state should be in first or last part of the - two subframes */ - - diff = STATE_LEN - iLBCenc_inst->state_short_len; - en1 = 0; - index = (start-1)*SUBL; - - - -Andersen, et al. Experimental [Page 55] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - for (i = 0; i < iLBCenc_inst->state_short_len; i++) { - en1 += residual[index+i]*residual[index+i]; - } - en2 = 0; - index = (start-1)*SUBL+diff; - for (i = 0; i < iLBCenc_inst->state_short_len; i++) { - en2 += residual[index+i]*residual[index+i]; - } - - - if (en1 > en2) { - state_first = 1; - start_pos = (start-1)*SUBL; - } else { - state_first = 0; - start_pos = (start-1)*SUBL + diff; - } - - /* scalar quantization of state */ - - StateSearchW(iLBCenc_inst, &residual[start_pos], - &syntdenum[(start-1)*(LPC_FILTERORDER+1)], - &weightdenum[(start-1)*(LPC_FILTERORDER+1)], &idxForMax, - idxVec, iLBCenc_inst->state_short_len, state_first); - - StateConstructW(idxForMax, idxVec, - &syntdenum[(start-1)*(LPC_FILTERORDER+1)], - &decresidual[start_pos], iLBCenc_inst->state_short_len); - - /* predictive quantization in state */ - - if (state_first) { /* put adaptive part in the end */ - - /* setup memory */ - - memset(mem, 0, - (CB_MEML-iLBCenc_inst->state_short_len)*sizeof(float)); - memcpy(mem+CB_MEML-iLBCenc_inst->state_short_len, - decresidual+start_pos, - iLBCenc_inst->state_short_len*sizeof(float)); - memset(weightState, 0, LPC_FILTERORDER*sizeof(float)); - - /* encode sub-frames */ - - iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index, - &residual[start_pos+iLBCenc_inst->state_short_len], - mem+CB_MEML-stMemLTbl, - stMemLTbl, diff, CB_NSTAGES, - - - -Andersen, et al. Experimental [Page 56] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - &weightdenum[start*(LPC_FILTERORDER+1)], - weightState, 0); - - /* construct decoded vector */ - - iCBConstruct( - &decresidual[start_pos+iLBCenc_inst->state_short_len], - extra_cb_index, extra_gain_index, - mem+CB_MEML-stMemLTbl, - stMemLTbl, diff, CB_NSTAGES); - - } - else { /* put adaptive part in the beginning */ - - /* create reversed vectors for prediction */ - - for (k=0; kstate_short_len)]; - } - - /* setup memory */ - - meml_gotten = iLBCenc_inst->state_short_len; - for (k=0; knsub-start-1; - - - if ( Nfor > 0 ) { - - /* setup memory */ - - memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float)); - memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL, - STATE_LEN*sizeof(float)); - memset(weightState, 0, LPC_FILTERORDER*sizeof(float)); - - /* loop over sub-frames to encode */ - - for (subframe=0; subframe 0 ) { - - /* create reverse order vectors */ - - for (n=0; nnsub+1-start); - - - if ( meml_gotten > CB_MEML ) { - meml_gotten=CB_MEML; - } - for (k=0; klpc_n; k++) { - packsplit(&lsf_i[k], &firstpart, &lsf_i[k], - iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], - iLBCenc_inst->ULP_inst->lsf_bits[k][ulp]+ - iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+1]+ - iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], &pos); - } - - /* Start block info */ - - packsplit(&start, &firstpart, &start, - iLBCenc_inst->ULP_inst->start_bits[ulp], - iLBCenc_inst->ULP_inst->start_bits[ulp]+ - iLBCenc_inst->ULP_inst->start_bits[ulp+1]+ - iLBCenc_inst->ULP_inst->start_bits[ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->start_bits[ulp], &pos); - - packsplit(&state_first, &firstpart, &state_first, - iLBCenc_inst->ULP_inst->startfirst_bits[ulp], - iLBCenc_inst->ULP_inst->startfirst_bits[ulp]+ - iLBCenc_inst->ULP_inst->startfirst_bits[ulp+1]+ - iLBCenc_inst->ULP_inst->startfirst_bits[ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->startfirst_bits[ulp], &pos); - - packsplit(&idxForMax, &firstpart, &idxForMax, - iLBCenc_inst->ULP_inst->scale_bits[ulp], - iLBCenc_inst->ULP_inst->scale_bits[ulp]+ - iLBCenc_inst->ULP_inst->scale_bits[ulp+1]+ - iLBCenc_inst->ULP_inst->scale_bits[ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->scale_bits[ulp], &pos); - - for (k=0; kstate_short_len; k++) { - packsplit(idxVec+k, &firstpart, idxVec+k, - iLBCenc_inst->ULP_inst->state_bits[ulp], - iLBCenc_inst->ULP_inst->state_bits[ulp]+ - iLBCenc_inst->ULP_inst->state_bits[ulp+1]+ - iLBCenc_inst->ULP_inst->state_bits[ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->state_bits[ulp], &pos); - } - - - - -Andersen, et al. Experimental [Page 61] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* 23/22 (20ms/30ms) sample block */ - - for (k=0;kULP_inst->extra_cb_index[k][ulp], - iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp]+ - iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+1]+ - iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp], - &pos); - } - - for (k=0;kULP_inst->extra_cb_gain[k][ulp], - iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp]+ - iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+1]+ - iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp], - &pos); - } - - /* The two/four (20ms/30ms) 40 sample sub-blocks */ - - for (i=0; inasub; i++) { - for (k=0; kULP_inst->cb_index[i][k][ulp], - iLBCenc_inst->ULP_inst->cb_index[i][k][ulp]+ - iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+1]+ - iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->cb_index[i][k][ulp], - &pos); - } - } - - for (i=0; inasub; i++) { - for (k=0; kULP_inst->cb_gain[i][k][ulp], - iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp]+ - - - -Andersen, et al. Experimental [Page 62] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+1]+ - iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+2]); - dopack( &pbytes, firstpart, - iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp], - &pos); - } - } - } - - /* set the last bit to zero (otherwise the decoder - will treat it as a lost frame) */ - dopack( &pbytes, 0, 1, &pos); - } - -A.4. iLBC_decode.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_decode.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_ILBCDECODE_H - #define __iLBC_ILBCDECODE_H - - #include "iLBC_define.h" - - short initDecode( /* (o) Number of decoded - samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - int mode, /* (i) frame size mode */ - int use_enhancer /* (i) 1 to use enhancer - 0 to run without - enhancer */ - ); - - void iLBC_decode( - float *decblock, /* (o) decoded signal block */ - unsigned char *bytes, /* (i) encoded signal bits */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state - structure */ - int mode /* (i) 0: bad packet, PLC, - 1: normal */ - - - -Andersen, et al. Experimental [Page 63] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - ); - - #endif - -A.5. iLBC_decode.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_decode.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - - #include "iLBC_define.h" - #include "StateConstructW.h" - #include "LPCdecode.h" - #include "iCBConstruct.h" - #include "doCPLC.h" - #include "helpfun.h" - #include "constants.h" - #include "packing.h" - #include "string.h" - #include "enhancer.h" - #include "hpOutput.h" - #include "syntFilter.h" - - /*----------------------------------------------------------------* - * Initiation of decoder instance. - *---------------------------------------------------------------*/ - - short initDecode( /* (o) Number of decoded - samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - int mode, /* (i) frame size mode */ - int use_enhancer /* (i) 1 to use enhancer - 0 to run without - enhancer */ - ){ - int i; - - iLBCdec_inst->mode = mode; - - - -Andersen, et al. Experimental [Page 64] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - if (mode==30) { - iLBCdec_inst->blockl = BLOCKL_30MS; - iLBCdec_inst->nsub = NSUB_30MS; - iLBCdec_inst->nasub = NASUB_30MS; - iLBCdec_inst->lpc_n = LPC_N_30MS; - iLBCdec_inst->no_of_bytes = NO_OF_BYTES_30MS; - iLBCdec_inst->no_of_words = NO_OF_WORDS_30MS; - iLBCdec_inst->state_short_len=STATE_SHORT_LEN_30MS; - /* ULP init */ - iLBCdec_inst->ULP_inst=&ULP_30msTbl; - } - else if (mode==20) { - iLBCdec_inst->blockl = BLOCKL_20MS; - iLBCdec_inst->nsub = NSUB_20MS; - iLBCdec_inst->nasub = NASUB_20MS; - iLBCdec_inst->lpc_n = LPC_N_20MS; - iLBCdec_inst->no_of_bytes = NO_OF_BYTES_20MS; - iLBCdec_inst->no_of_words = NO_OF_WORDS_20MS; - iLBCdec_inst->state_short_len=STATE_SHORT_LEN_20MS; - /* ULP init */ - iLBCdec_inst->ULP_inst=&ULP_20msTbl; - } - else { - exit(2); - } - - memset(iLBCdec_inst->syntMem, 0, - LPC_FILTERORDER*sizeof(float)); - memcpy((*iLBCdec_inst).lsfdeqold, lsfmeanTbl, - LPC_FILTERORDER*sizeof(float)); - - memset(iLBCdec_inst->old_syntdenum, 0, - ((LPC_FILTERORDER + 1)*NSUB_MAX)*sizeof(float)); - for (i=0; iold_syntdenum[i*(LPC_FILTERORDER+1)]=1.0; - - iLBCdec_inst->last_lag = 20; - - iLBCdec_inst->prevLag = 120; - iLBCdec_inst->per = 0.0; - iLBCdec_inst->consPLICount = 0; - iLBCdec_inst->prevPLI = 0; - iLBCdec_inst->prevLpc[0] = 1.0; - memset(iLBCdec_inst->prevLpc+1,0, - LPC_FILTERORDER*sizeof(float)); - memset(iLBCdec_inst->prevResidual, 0, BLOCKL_MAX*sizeof(float)); - iLBCdec_inst->seed=777; - - - - -Andersen, et al. Experimental [Page 65] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - memset(iLBCdec_inst->hpomem, 0, 4*sizeof(float)); - - iLBCdec_inst->use_enhancer = use_enhancer; - memset(iLBCdec_inst->enh_buf, 0, ENH_BUFL*sizeof(float)); - for (i=0;ienh_period[i]=(float)40.0; - - iLBCdec_inst->prev_enh_pl = 0; - - return (iLBCdec_inst->blockl); - } - - /*----------------------------------------------------------------* - * frame residual decoder function (subrutine to iLBC_decode) - *---------------------------------------------------------------*/ - - void Decode( - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state - structure */ - float *decresidual, /* (o) decoded residual frame */ - int start, /* (i) location of start - state */ - int idxForMax, /* (i) codebook index for the - maximum value */ - int *idxVec, /* (i) codebook indexes for the - samples in the start - state */ - float *syntdenum, /* (i) the decoded synthesis - filter coefficients */ - int *cb_index, /* (i) the indexes for the - adaptive codebook */ - int *gain_index, /* (i) the indexes for the - corresponding gains */ - int *extra_cb_index, /* (i) the indexes for the - adaptive codebook part - of start state */ - int *extra_gain_index, /* (i) the indexes for the - corresponding gains */ - int state_first /* (i) 1 if non adaptive part - of start state comes - first 0 if that part - comes last */ - ){ - float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML]; - int k, meml_gotten, Nfor, Nback, i; - int diff, start_pos; - int subcount, subframe; - - - - -Andersen, et al. Experimental [Page 66] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - diff = STATE_LEN - iLBCdec_inst->state_short_len; - - if (state_first == 1) { - start_pos = (start-1)*SUBL; - } else { - start_pos = (start-1)*SUBL + diff; - } - - /* decode scalar part of start state */ - - StateConstructW(idxForMax, idxVec, - &syntdenum[(start-1)*(LPC_FILTERORDER+1)], - &decresidual[start_pos], iLBCdec_inst->state_short_len); - - - if (state_first) { /* put adaptive part in the end */ - - /* setup memory */ - - memset(mem, 0, - (CB_MEML-iLBCdec_inst->state_short_len)*sizeof(float)); - memcpy(mem+CB_MEML-iLBCdec_inst->state_short_len, - decresidual+start_pos, - iLBCdec_inst->state_short_len*sizeof(float)); - - /* construct decoded vector */ - - iCBConstruct( - &decresidual[start_pos+iLBCdec_inst->state_short_len], - extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl, - stMemLTbl, diff, CB_NSTAGES); - - } - else {/* put adaptive part in the beginning */ - - /* create reversed vectors for prediction */ - - for (k=0; kstate_short_len)]; - } - - /* setup memory */ - - meml_gotten = iLBCdec_inst->state_short_len; - for (k=0; knsub-start-1; - - if ( Nfor > 0 ){ - - /* setup memory */ - - memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float)); - memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL, - STATE_LEN*sizeof(float)); - - /* loop over sub-frames to encode */ - - for (subframe=0; subframe 0 ) { - - /* setup memory */ - - meml_gotten = SUBL*(iLBCdec_inst->nsub+1-start); - - if ( meml_gotten > CB_MEML ) { - meml_gotten=CB_MEML; - } - for (k=0; k0) { /* the data are good */ - - /* decode data */ - - pbytes=bytes; - pos=0; - - - - -Andersen, et al. Experimental [Page 70] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* Set everything to zero before decoding */ - - for (k=0; kstate_short_len; k++) { - idxVec[k]=0; - } - for (k=0; knasub; i++) { - for (k=0; knasub; i++) { - for (k=0; klpc_n; k++){ - unpack( &pbytes, &lastpart, - iLBCdec_inst->ULP_inst->lsf_bits[k][ulp], &pos); - packcombine(&lsf_i[k], lastpart, - iLBCdec_inst->ULP_inst->lsf_bits[k][ulp]); - } - - /* Start block info */ - - unpack( &pbytes, &lastpart, - iLBCdec_inst->ULP_inst->start_bits[ulp], &pos); - packcombine(&start, lastpart, - iLBCdec_inst->ULP_inst->start_bits[ulp]); - - unpack( &pbytes, &lastpart, - - - -Andersen, et al. Experimental [Page 71] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - iLBCdec_inst->ULP_inst->startfirst_bits[ulp], &pos); - packcombine(&state_first, lastpart, - iLBCdec_inst->ULP_inst->startfirst_bits[ulp]); - - unpack( &pbytes, &lastpart, - iLBCdec_inst->ULP_inst->scale_bits[ulp], &pos); - packcombine(&idxForMax, lastpart, - iLBCdec_inst->ULP_inst->scale_bits[ulp]); - - for (k=0; kstate_short_len; k++) { - unpack( &pbytes, &lastpart, - iLBCdec_inst->ULP_inst->state_bits[ulp], &pos); - packcombine(idxVec+k, lastpart, - iLBCdec_inst->ULP_inst->state_bits[ulp]); - } - - /* 23/22 (20ms/30ms) sample block */ - - for (k=0; kULP_inst->extra_cb_index[k][ulp], - &pos); - packcombine(extra_cb_index+k, lastpart, - iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp]); - } - for (k=0; kULP_inst->extra_cb_gain[k][ulp], - &pos); - packcombine(extra_gain_index+k, lastpart, - iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp]); - } - - /* The two/four (20ms/30ms) 40 sample sub-blocks */ - - for (i=0; inasub; i++) { - for (k=0; kULP_inst->cb_index[i][k][ulp], - &pos); - packcombine(cb_index+i*CB_NSTAGES+k, lastpart, - iLBCdec_inst->ULP_inst->cb_index[i][k][ulp]); - } - } - - for (i=0; inasub; i++) { - for (k=0; kULP_inst->cb_gain[i][k][ulp], - &pos); - packcombine(gain_index+i*CB_NSTAGES+k, lastpart, - iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp]); - } - } - } - /* Extract last bit. If it is 1 this indicates an - empty/lost frame */ - unpack( &pbytes, &last_bit, 1, &pos); - - /* Check for bit errors or empty/lost frames */ - if (start<1) - mode = 0; - if (iLBCdec_inst->mode==20 && start>3) - mode = 0; - if (iLBCdec_inst->mode==30 && start>5) - mode = 0; - if (last_bit==1) - mode = 0; - - if (mode==1) { /* No bit errors was detected, - continue decoding */ - - /* adjust index */ - index_conv_dec(cb_index); - - /* decode the lsf */ - - SimplelsfDEQ(lsfdeq, lsf_i, iLBCdec_inst->lpc_n); - check=LSF_check(lsfdeq, LPC_FILTERORDER, - iLBCdec_inst->lpc_n); - DecoderInterpolateLSF(syntdenum, weightdenum, - lsfdeq, LPC_FILTERORDER, iLBCdec_inst); - - Decode(iLBCdec_inst, decresidual, start, idxForMax, - idxVec, syntdenum, cb_index, gain_index, - extra_cb_index, extra_gain_index, - state_first); - - /* preparing the plc for a future loss! */ - - doThePLC(PLCresidual, PLClpc, 0, decresidual, - syntdenum + - (LPC_FILTERORDER + 1)*(iLBCdec_inst->nsub - 1), - (*iLBCdec_inst).last_lag, iLBCdec_inst); - - - - - -Andersen, et al. Experimental [Page 73] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - memcpy(decresidual, PLCresidual, - iLBCdec_inst->blockl*sizeof(float)); - } - - } - - if (mode == 0) { - /* the data is bad (either a PLC call - * was made or a severe bit error was detected) - */ - - /* packet loss conceal */ - - memset(zeros, 0, BLOCKL_MAX*sizeof(float)); - - one[0] = 1; - memset(one+1, 0, LPC_FILTERORDER*sizeof(float)); - - start=0; - - doThePLC(PLCresidual, PLClpc, 1, zeros, one, - (*iLBCdec_inst).last_lag, iLBCdec_inst); - memcpy(decresidual, PLCresidual, - iLBCdec_inst->blockl*sizeof(float)); - - order_plus_one = LPC_FILTERORDER + 1; - for (i = 0; i < iLBCdec_inst->nsub; i++) { - memcpy(syntdenum+(i*order_plus_one), PLClpc, - order_plus_one*sizeof(float)); - } - } - - if (iLBCdec_inst->use_enhancer == 1) { - - /* post filtering */ - - iLBCdec_inst->last_lag = - enhancerInterface(data, decresidual, iLBCdec_inst); - - /* synthesis filtering */ - - if (iLBCdec_inst->mode==20) { - /* Enhancer has 40 samples delay */ - i=0; - syntFilter(data + i*SUBL, - iLBCdec_inst->old_syntdenum + - (i+iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1), - SUBL, iLBCdec_inst->syntMem); - - - -Andersen, et al. Experimental [Page 74] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - for (i=1; i < iLBCdec_inst->nsub; i++) { - syntFilter(data + i*SUBL, - syntdenum + (i-1)*(LPC_FILTERORDER+1), - SUBL, iLBCdec_inst->syntMem); - } - } else if (iLBCdec_inst->mode==30) { - /* Enhancer has 80 samples delay */ - for (i=0; i < 2; i++) { - syntFilter(data + i*SUBL, - iLBCdec_inst->old_syntdenum + - (i+iLBCdec_inst->nsub-2)*(LPC_FILTERORDER+1), - SUBL, iLBCdec_inst->syntMem); - } - for (i=2; i < iLBCdec_inst->nsub; i++) { - syntFilter(data + i*SUBL, - syntdenum + (i-2)*(LPC_FILTERORDER+1), SUBL, - iLBCdec_inst->syntMem); - } - } - - } else { - - /* Find last lag */ - lag = 20; - maxcc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL], - &decresidual[BLOCKL_MAX-ENH_BLOCKL-lag], ENH_BLOCKL); - - for (ilag=21; ilag<120; ilag++) { - cc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL], - &decresidual[BLOCKL_MAX-ENH_BLOCKL-ilag], - ENH_BLOCKL); - - if (cc > maxcc) { - maxcc = cc; - lag = ilag; - } - } - iLBCdec_inst->last_lag = lag; - - /* copy data and run synthesis filter */ - - memcpy(data, decresidual, - iLBCdec_inst->blockl*sizeof(float)); - for (i=0; i < iLBCdec_inst->nsub; i++) { - syntFilter(data + i*SUBL, - syntdenum + i*(LPC_FILTERORDER+1), SUBL, - iLBCdec_inst->syntMem); - } - - - -Andersen, et al. Experimental [Page 75] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - - /* high pass filtering on output if desired, otherwise - copy to out */ - - hpOutput(data, iLBCdec_inst->blockl, - decblock,iLBCdec_inst->hpomem); - - /* memcpy(decblock,data,iLBCdec_inst->blockl*sizeof(float));*/ - - memcpy(iLBCdec_inst->old_syntdenum, syntdenum, - - iLBCdec_inst->nsub*(LPC_FILTERORDER+1)*sizeof(float)); - - iLBCdec_inst->prev_enh_pl=0; - - if (mode==0) { /* PLC was used */ - iLBCdec_inst->prev_enh_pl=1; - } - } - -A.6. iLBC_define.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_define.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - #include - - #ifndef __iLBC_ILBCDEFINE_H - #define __iLBC_ILBCDEFINE_H - - /* general codec settings */ - - #define FS (float)8000.0 - #define BLOCKL_20MS 160 - #define BLOCKL_30MS 240 - #define BLOCKL_MAX 240 - #define NSUB_20MS 4 - #define NSUB_30MS 6 - #define NSUB_MAX 6 - #define NASUB_20MS 2 - - - -Andersen, et al. Experimental [Page 76] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #define NASUB_30MS 4 - #define NASUB_MAX 4 - #define SUBL 40 - #define STATE_LEN 80 - #define STATE_SHORT_LEN_30MS 58 - #define STATE_SHORT_LEN_20MS 57 - - /* LPC settings */ - - #define LPC_FILTERORDER 10 - #define LPC_CHIRP_SYNTDENUM (float)0.9025 - #define LPC_CHIRP_WEIGHTDENUM (float)0.4222 - #define LPC_LOOKBACK 60 - #define LPC_N_20MS 1 - #define LPC_N_30MS 2 - #define LPC_N_MAX 2 - #define LPC_ASYMDIFF 20 - #define LPC_BW (float)60.0 - #define LPC_WN (float)1.0001 - #define LSF_NSPLIT 3 - #define LSF_NUMBER_OF_STEPS 4 - #define LPC_HALFORDER (LPC_FILTERORDER/2) - - /* cb settings */ - - #define CB_NSTAGES 3 - #define CB_EXPAND 2 - #define CB_MEML 147 - #define CB_FILTERLEN 2*4 - #define CB_HALFFILTERLEN 4 - #define CB_RESRANGE 34 - #define CB_MAXGAIN (float)1.3 - - /* enhancer */ - - #define ENH_BLOCKL 80 /* block length */ - #define ENH_BLOCKL_HALF (ENH_BLOCKL/2) - #define ENH_HL 3 /* 2*ENH_HL+1 is number blocks - in said second sequence */ - #define ENH_SLOP 2 /* max difference estimated and - correct pitch period */ - #define ENH_PLOCSL 20 /* pitch-estimates and pitch- - locations buffer length */ - #define ENH_OVERHANG 2 - #define ENH_UPS0 4 /* upsampling rate */ - #define ENH_FL0 3 /* 2*FLO+1 is the length of - each filter */ - #define ENH_VECTL (ENH_BLOCKL+2*ENH_FL0) - - - -Andersen, et al. Experimental [Page 77] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #define ENH_CORRDIM (2*ENH_SLOP+1) - #define ENH_NBLOCKS (BLOCKL_MAX/ENH_BLOCKL) - #define ENH_NBLOCKS_EXTRA 5 - #define ENH_NBLOCKS_TOT 8 /* ENH_NBLOCKS + - ENH_NBLOCKS_EXTRA */ - #define ENH_BUFL (ENH_NBLOCKS_TOT)*ENH_BLOCKL - #define ENH_ALPHA0 (float)0.05 - - /* Down sampling */ - - #define FILTERORDER_DS 7 - #define DELAY_DS 3 - #define FACTOR_DS 2 - - /* bit stream defs */ - - #define NO_OF_BYTES_20MS 38 - #define NO_OF_BYTES_30MS 50 - #define NO_OF_WORDS_20MS 19 - #define NO_OF_WORDS_30MS 25 - #define STATE_BITS 3 - #define BYTE_LEN 8 - #define ULP_CLASSES 3 - - /* help parameters */ - - #define FLOAT_MAX (float)1.0e37 - #define EPS (float)2.220446049250313e-016 - #define PI (float)3.14159265358979323846 - #define MIN_SAMPLE -32768 - #define MAX_SAMPLE 32767 - #define TWO_PI (float)6.283185307 - #define PI2 (float)0.159154943 - - /* type definition encoder instance */ - typedef struct iLBC_ULP_Inst_t_ { - int lsf_bits[6][ULP_CLASSES+2]; - int start_bits[ULP_CLASSES+2]; - int startfirst_bits[ULP_CLASSES+2]; - int scale_bits[ULP_CLASSES+2]; - int state_bits[ULP_CLASSES+2]; - int extra_cb_index[CB_NSTAGES][ULP_CLASSES+2]; - int extra_cb_gain[CB_NSTAGES][ULP_CLASSES+2]; - int cb_index[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2]; - int cb_gain[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2]; - } iLBC_ULP_Inst_t; - - /* type definition encoder instance */ - - - -Andersen, et al. Experimental [Page 78] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - typedef struct iLBC_Enc_Inst_t_ { - - /* flag for frame size mode */ - int mode; - - /* basic parameters for different frame sizes */ - int blockl; - int nsub; - int nasub; - int no_of_bytes, no_of_words; - int lpc_n; - int state_short_len; - const iLBC_ULP_Inst_t *ULP_inst; - - /* analysis filter state */ - float anaMem[LPC_FILTERORDER]; - - /* old lsf parameters for interpolation */ - float lsfold[LPC_FILTERORDER]; - float lsfdeqold[LPC_FILTERORDER]; - - /* signal buffer for LP analysis */ - float lpc_buffer[LPC_LOOKBACK + BLOCKL_MAX]; - - /* state of input HP filter */ - float hpimem[4]; - - } iLBC_Enc_Inst_t; - - /* type definition decoder instance */ - typedef struct iLBC_Dec_Inst_t_ { - - /* flag for frame size mode */ - int mode; - - /* basic parameters for different frame sizes */ - int blockl; - int nsub; - int nasub; - int no_of_bytes, no_of_words; - int lpc_n; - int state_short_len; - const iLBC_ULP_Inst_t *ULP_inst; - - /* synthesis filter state */ - float syntMem[LPC_FILTERORDER]; - - /* old LSF for interpolation */ - - - -Andersen, et al. Experimental [Page 79] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float lsfdeqold[LPC_FILTERORDER]; - - /* pitch lag estimated in enhancer and used in PLC */ - int last_lag; - - /* PLC state information */ - int prevLag, consPLICount, prevPLI, prev_enh_pl; - float prevLpc[LPC_FILTERORDER+1]; - float prevResidual[NSUB_MAX*SUBL]; - float per; - unsigned long seed; - - /* previous synthesis filter parameters */ - float old_syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX]; - - /* state of output HP filter */ - float hpomem[4]; - - /* enhancer state information */ - int use_enhancer; - float enh_buf[ENH_BUFL]; - float enh_period[ENH_NBLOCKS_TOT]; - - } iLBC_Dec_Inst_t; - - #endif - -A.7. constants.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - constants.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_CONSTANTS_H - #define __iLBC_CONSTANTS_H - - #include "iLBC_define.h" - - - /* ULP bit allocation */ - - - - -Andersen, et al. Experimental [Page 80] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - extern const iLBC_ULP_Inst_t ULP_20msTbl; - extern const iLBC_ULP_Inst_t ULP_30msTbl; - - /* high pass filters */ - - extern float hpi_zero_coefsTbl[]; - extern float hpi_pole_coefsTbl[]; - extern float hpo_zero_coefsTbl[]; - extern float hpo_pole_coefsTbl[]; - - /* low pass filters */ - extern float lpFilt_coefsTbl[]; - - /* LPC analysis and quantization */ - - extern float lpc_winTbl[]; - extern float lpc_asymwinTbl[]; - extern float lpc_lagwinTbl[]; - extern float lsfCbTbl[]; - extern float lsfmeanTbl[]; - extern int dim_lsfCbTbl[]; - extern int size_lsfCbTbl[]; - extern float lsf_weightTbl_30ms[]; - extern float lsf_weightTbl_20ms[]; - - /* state quantization tables */ - - extern float state_sq3Tbl[]; - extern float state_frgqTbl[]; - - /* gain quantization tables */ - - extern float gain_sq3Tbl[]; - extern float gain_sq4Tbl[]; - extern float gain_sq5Tbl[]; - - /* adaptive codebook definitions */ - - extern int search_rangeTbl[5][CB_NSTAGES]; - extern int memLfTbl[]; - extern int stMemLTbl; - extern float cbfiltersTbl[CB_FILTERLEN]; - - /* enhancer definitions */ - - extern float polyphaserTbl[]; - extern float enh_plocsTbl[]; - - - - -Andersen, et al. Experimental [Page 81] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #endif - -A.8. constants.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - constants.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include "iLBC_define.h" - - /* ULP bit allocation */ - - /* 20 ms frame */ - - const iLBC_ULP_Inst_t ULP_20msTbl = { - /* LSF */ - { {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0}, - {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}, - /* Start state location, gain and samples */ - {2,0,0,0,0}, - {1,0,0,0,0}, - {6,0,0,0,0}, - {0,1,2,0,0}, - /* extra CB index and extra CB gain */ - {{6,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}}, - {{2,0,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}}, - /* CB index and CB gain */ - { {{7,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}}, - {{0,0,8,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}, - {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}, - {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}}, - { {{1,2,2,0,0}, {1,1,2,0,0}, {0,0,3,0,0}}, - {{1,1,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}}, - {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}, - {{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}} - }; - - /* 30 ms frame */ - - const iLBC_ULP_Inst_t ULP_30msTbl = { - /* LSF */ - - - -Andersen, et al. Experimental [Page 82] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - { {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0}, - {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0}}, - /* Start state location, gain and samples */ - {3,0,0,0,0}, - {1,0,0,0,0}, - {6,0,0,0,0}, - {0,1,2,0,0}, - /* extra CB index and extra CB gain */ - {{4,2,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}}, - {{1,1,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}}, - /* CB index and CB gain */ - { {{6,1,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}}, - {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}, - {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}, - {{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}}, - { {{1,2,2,0,0}, {1,2,1,0,0}, {0,0,3,0,0}}, - {{0,2,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}}, - {{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}}, - {{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}}} - }; - - /* HP Filters */ - - float hpi_zero_coefsTbl[3] = { - (float)0.92727436, (float)-1.8544941, (float)0.92727436 - }; - float hpi_pole_coefsTbl[3] = { - (float)1.0, (float)-1.9059465, (float)0.9114024 - }; - float hpo_zero_coefsTbl[3] = { - (float)0.93980581, (float)-1.8795834, (float)0.93980581 - }; - float hpo_pole_coefsTbl[3] = { - (float)1.0, (float)-1.9330735, (float)0.93589199 - }; - - /* LP Filter */ - - float lpFilt_coefsTbl[FILTERORDER_DS]={ - (float)-0.066650, (float)0.125000, (float)0.316650, - (float)0.414063, (float)0.316650, - (float)0.125000, (float)-0.066650 - }; - - /* State quantization tables */ - - float state_sq3Tbl[8] = { - (float)-3.719849, (float)-2.177490, (float)-1.130005, - - - -Andersen, et al. Experimental [Page 83] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)-0.309692, (float)0.444214, (float)1.329712, - (float)2.436279, (float)3.983887 - }; - - float state_frgqTbl[64] = { - (float)1.000085, (float)1.071695, (float)1.140395, - (float)1.206868, (float)1.277188, (float)1.351503, - (float)1.429380, (float)1.500727, (float)1.569049, - (float)1.639599, (float)1.707071, (float)1.781531, - (float)1.840799, (float)1.901550, (float)1.956695, - (float)2.006750, (float)2.055474, (float)2.102787, - (float)2.142819, (float)2.183592, (float)2.217962, - (float)2.257177, (float)2.295739, (float)2.332967, - (float)2.369248, (float)2.402792, (float)2.435080, - (float)2.468598, (float)2.503394, (float)2.539284, - (float)2.572944, (float)2.605036, (float)2.636331, - (float)2.668939, (float)2.698780, (float)2.729101, - (float)2.759786, (float)2.789834, (float)2.818679, - (float)2.848074, (float)2.877470, (float)2.906899, - (float)2.936655, (float)2.967804, (float)3.000115, - (float)3.033367, (float)3.066355, (float)3.104231, - (float)3.141499, (float)3.183012, (float)3.222952, - (float)3.265433, (float)3.308441, (float)3.350823, - (float)3.395275, (float)3.442793, (float)3.490801, - (float)3.542514, (float)3.604064, (float)3.666050, - (float)3.740994, (float)3.830749, (float)3.938770, - (float)4.101764 - }; - - /* CB tables */ - - int search_rangeTbl[5][CB_NSTAGES]={{58,58,58}, {108,44,44}, - {108,108,108}, {108,108,108}, {108,108,108}}; - int stMemLTbl=85; - int memLfTbl[NASUB_MAX]={147,147,147,147}; - - /* expansion filter(s) */ - - float cbfiltersTbl[CB_FILTERLEN]={ - (float)-0.034180, (float)0.108887, (float)-0.184326, - (float)0.806152, (float)0.713379, (float)-0.144043, - (float)0.083740, (float)-0.033691 - }; - - /* Gain Quantization */ - - float gain_sq3Tbl[8]={ - (float)-1.000000, (float)-0.659973, (float)-0.330017, - - - -Andersen, et al. Experimental [Page 84] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.000000, (float)0.250000, (float)0.500000, - (float)0.750000, (float)1.00000}; - - float gain_sq4Tbl[16]={ - (float)-1.049988, (float)-0.900024, (float)-0.750000, - (float)-0.599976, (float)-0.450012, (float)-0.299988, - (float)-0.150024, (float)0.000000, (float)0.150024, - (float)0.299988, (float)0.450012, (float)0.599976, - (float)0.750000, (float)0.900024, (float)1.049988, - (float)1.200012}; - - float gain_sq5Tbl[32]={ - (float)0.037476, (float)0.075012, (float)0.112488, - (float)0.150024, (float)0.187500, (float)0.224976, - (float)0.262512, (float)0.299988, (float)0.337524, - (float)0.375000, (float)0.412476, (float)0.450012, - (float)0.487488, (float)0.525024, (float)0.562500, - (float)0.599976, (float)0.637512, (float)0.674988, - (float)0.712524, (float)0.750000, (float)0.787476, - (float)0.825012, (float)0.862488, (float)0.900024, - (float)0.937500, (float)0.974976, (float)1.012512, - (float)1.049988, (float)1.087524, (float)1.125000, - (float)1.162476, (float)1.200012}; - - /* Enhancer - Upsamling a factor 4 (ENH_UPS0 = 4) */ - float polyphaserTbl[ENH_UPS0*(2*ENH_FL0+1)]={ - (float)0.000000, (float)0.000000, (float)0.000000, - (float)1.000000, - (float)0.000000, (float)0.000000, (float)0.000000, - (float)0.015625, (float)-0.076904, (float)0.288330, - (float)0.862061, - (float)-0.106445, (float)0.018799, (float)-0.015625, - (float)0.023682, (float)-0.124268, (float)0.601563, - (float)0.601563, - (float)-0.124268, (float)0.023682, (float)-0.023682, - (float)0.018799, (float)-0.106445, (float)0.862061, - (float)0.288330, - (float)-0.076904, (float)0.015625, (float)-0.018799}; - - float enh_plocsTbl[ENH_NBLOCKS_TOT] = {(float)40.0, (float)120.0, - (float)200.0, (float)280.0, (float)360.0, - (float)440.0, (float)520.0, (float)600.0}; - - /* LPC analysis and quantization */ - - int dim_lsfCbTbl[LSF_NSPLIT] = {3, 3, 4}; - int size_lsfCbTbl[LSF_NSPLIT] = {64,128,128}; - - - - -Andersen, et al. Experimental [Page 85] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float lsfmeanTbl[LPC_FILTERORDER] = { - (float)0.281738, (float)0.445801, (float)0.663330, - (float)0.962524, (float)1.251831, (float)1.533081, - (float)1.850586, (float)2.137817, (float)2.481445, - (float)2.777344}; - - float lsf_weightTbl_30ms[6] = {(float)(1.0/2.0), (float)1.0, - (float)(2.0/3.0), - (float)(1.0/3.0), (float)0.0, (float)0.0}; - - float lsf_weightTbl_20ms[4] = {(float)(3.0/4.0), (float)(2.0/4.0), - (float)(1.0/4.0), (float)(0.0)}; - - /* Hanning LPC window */ - float lpc_winTbl[BLOCKL_MAX]={ - (float)0.000183, (float)0.000671, (float)0.001526, - (float)0.002716, (float)0.004242, (float)0.006104, - (float)0.008301, (float)0.010834, (float)0.013702, - (float)0.016907, (float)0.020416, (float)0.024261, - (float)0.028442, (float)0.032928, (float)0.037750, - (float)0.042877, (float)0.048309, (float)0.054047, - (float)0.060089, (float)0.066437, (float)0.073090, - (float)0.080017, (float)0.087219, (float)0.094727, - (float)0.102509, (float)0.110535, (float)0.118835, - (float)0.127411, (float)0.136230, (float)0.145294, - (float)0.154602, (float)0.164154, (float)0.173920, - (float)0.183899, (float)0.194122, (float)0.204529, - (float)0.215149, (float)0.225952, (float)0.236938, - (float)0.248108, (float)0.259460, (float)0.270966, - (float)0.282654, (float)0.294464, (float)0.306396, - (float)0.318481, (float)0.330688, (float)0.343018, - (float)0.355438, (float)0.367981, (float)0.380585, - (float)0.393280, (float)0.406067, (float)0.418884, - (float)0.431763, (float)0.444702, (float)0.457672, - (float)0.470673, (float)0.483704, (float)0.496735, - (float)0.509766, (float)0.522797, (float)0.535828, - (float)0.548798, (float)0.561768, (float)0.574677, - (float)0.587524, (float)0.600342, (float)0.613068, - (float)0.625732, (float)0.638306, (float)0.650787, - (float)0.663147, (float)0.675415, (float)0.687561, - (float)0.699585, (float)0.711487, (float)0.723206, - (float)0.734802, (float)0.746216, (float)0.757477, - (float)0.768585, (float)0.779480, (float)0.790192, - (float)0.800720, (float)0.811005, (float)0.821106, - (float)0.830994, (float)0.840668, (float)0.850067, - (float)0.859253, (float)0.868225, (float)0.876892, - (float)0.885345, (float)0.893524, (float)0.901428, - (float)0.909058, (float)0.916412, (float)0.923492, - - - -Andersen, et al. Experimental [Page 86] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.930267, (float)0.936768, (float)0.942963, - (float)0.948853, (float)0.954437, (float)0.959717, - (float)0.964691, (float)0.969360, (float)0.973694, - (float)0.977692, (float)0.981384, (float)0.984741, - (float)0.987762, (float)0.990479, (float)0.992828, - (float)0.994873, (float)0.996552, (float)0.997925, - (float)0.998932, (float)0.999603, (float)0.999969, - (float)0.999969, (float)0.999603, (float)0.998932, - (float)0.997925, (float)0.996552, (float)0.994873, - (float)0.992828, (float)0.990479, (float)0.987762, - (float)0.984741, (float)0.981384, (float)0.977692, - (float)0.973694, (float)0.969360, (float)0.964691, - (float)0.959717, (float)0.954437, (float)0.948853, - (float)0.942963, (float)0.936768, (float)0.930267, - (float)0.923492, (float)0.916412, (float)0.909058, - (float)0.901428, (float)0.893524, (float)0.885345, - (float)0.876892, (float)0.868225, (float)0.859253, - (float)0.850067, (float)0.840668, (float)0.830994, - (float)0.821106, (float)0.811005, (float)0.800720, - (float)0.790192, (float)0.779480, (float)0.768585, - (float)0.757477, (float)0.746216, (float)0.734802, - (float)0.723206, (float)0.711487, (float)0.699585, - (float)0.687561, (float)0.675415, (float)0.663147, - (float)0.650787, (float)0.638306, (float)0.625732, - (float)0.613068, (float)0.600342, (float)0.587524, - (float)0.574677, (float)0.561768, (float)0.548798, - (float)0.535828, (float)0.522797, (float)0.509766, - (float)0.496735, (float)0.483704, (float)0.470673, - (float)0.457672, (float)0.444702, (float)0.431763, - (float)0.418884, (float)0.406067, (float)0.393280, - (float)0.380585, (float)0.367981, (float)0.355438, - (float)0.343018, (float)0.330688, (float)0.318481, - (float)0.306396, (float)0.294464, (float)0.282654, - (float)0.270966, (float)0.259460, (float)0.248108, - (float)0.236938, (float)0.225952, (float)0.215149, - (float)0.204529, (float)0.194122, (float)0.183899, - (float)0.173920, (float)0.164154, (float)0.154602, - (float)0.145294, (float)0.136230, (float)0.127411, - (float)0.118835, (float)0.110535, (float)0.102509, - (float)0.094727, (float)0.087219, (float)0.080017, - (float)0.073090, (float)0.066437, (float)0.060089, - (float)0.054047, (float)0.048309, (float)0.042877, - (float)0.037750, (float)0.032928, (float)0.028442, - (float)0.024261, (float)0.020416, (float)0.016907, - (float)0.013702, (float)0.010834, (float)0.008301, - (float)0.006104, (float)0.004242, (float)0.002716, - (float)0.001526, (float)0.000671, (float)0.000183 - }; - - - -Andersen, et al. Experimental [Page 87] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* Asymmetric LPC window */ - float lpc_asymwinTbl[BLOCKL_MAX]={ - (float)0.000061, (float)0.000214, (float)0.000458, - (float)0.000824, (float)0.001282, (float)0.001831, - (float)0.002472, (float)0.003235, (float)0.004120, - (float)0.005066, (float)0.006134, (float)0.007294, - (float)0.008545, (float)0.009918, (float)0.011383, - (float)0.012939, (float)0.014587, (float)0.016357, - (float)0.018219, (float)0.020172, (float)0.022217, - (float)0.024353, (float)0.026611, (float)0.028961, - (float)0.031372, (float)0.033905, (float)0.036530, - (float)0.039276, (float)0.042084, (float)0.044983, - (float)0.047974, (float)0.051086, (float)0.054260, - (float)0.057526, (float)0.060883, (float)0.064331, - (float)0.067871, (float)0.071503, (float)0.075226, - (float)0.079010, (float)0.082916, (float)0.086884, - (float)0.090942, (float)0.095062, (float)0.099304, - (float)0.103607, (float)0.107971, (float)0.112427, - (float)0.116974, (float)0.121582, (float)0.126282, - (float)0.131073, (float)0.135895, (float)0.140839, - (float)0.145813, (float)0.150879, (float)0.156006, - (float)0.161224, (float)0.166504, (float)0.171844, - (float)0.177246, (float)0.182709, (float)0.188263, - (float)0.193848, (float)0.199524, (float)0.205231, - (float)0.211029, (float)0.216858, (float)0.222778, - (float)0.228729, (float)0.234741, (float)0.240814, - (float)0.246918, (float)0.253082, (float)0.259308, - (float)0.265564, (float)0.271881, (float)0.278259, - (float)0.284668, (float)0.291107, (float)0.297607, - (float)0.304138, (float)0.310730, (float)0.317322, - (float)0.323975, (float)0.330658, (float)0.337372, - (float)0.344147, (float)0.350922, (float)0.357727, - (float)0.364594, (float)0.371460, (float)0.378357, - (float)0.385284, (float)0.392212, (float)0.399170, - (float)0.406158, (float)0.413177, (float)0.420197, - (float)0.427246, (float)0.434296, (float)0.441376, - (float)0.448456, (float)0.455536, (float)0.462646, - (float)0.469757, (float)0.476868, (float)0.483978, - (float)0.491089, (float)0.498230, (float)0.505341, - (float)0.512451, (float)0.519592, (float)0.526703, - (float)0.533813, (float)0.540924, (float)0.548004, - (float)0.555084, (float)0.562164, (float)0.569244, - (float)0.576294, (float)0.583313, (float)0.590332, - (float)0.597321, (float)0.604309, (float)0.611267, - (float)0.618195, (float)0.625092, (float)0.631989, - (float)0.638855, (float)0.645660, (float)0.652466, - (float)0.659241, (float)0.665985, (float)0.672668, - (float)0.679352, (float)0.685974, (float)0.692566, - - - -Andersen, et al. Experimental [Page 88] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.699127, (float)0.705658, (float)0.712128, - (float)0.718536, (float)0.724945, (float)0.731262, - (float)0.737549, (float)0.743805, (float)0.750000, - (float)0.756134, (float)0.762238, (float)0.768280, - (float)0.774261, (float)0.780182, (float)0.786072, - (float)0.791870, (float)0.797638, (float)0.803314, - (float)0.808960, (float)0.814514, (float)0.820038, - (float)0.825470, (float)0.830841, (float)0.836151, - (float)0.841400, (float)0.846558, (float)0.851654, - (float)0.856689, (float)0.861633, (float)0.866516, - (float)0.871338, (float)0.876068, (float)0.880737, - (float)0.885315, (float)0.889801, (float)0.894226, - (float)0.898560, (float)0.902832, (float)0.907013, - (float)0.911102, (float)0.915100, (float)0.919037, - (float)0.922882, (float)0.926636, (float)0.930328, - (float)0.933899, (float)0.937408, (float)0.940796, - (float)0.944122, (float)0.947357, (float)0.950470, - (float)0.953522, (float)0.956482, (float)0.959351, - (float)0.962097, (float)0.964783, (float)0.967377, - (float)0.969849, (float)0.972229, (float)0.974518, - (float)0.976715, (float)0.978821, (float)0.980835, - (float)0.982727, (float)0.984528, (float)0.986237, - (float)0.987854, (float)0.989380, (float)0.990784, - (float)0.992096, (float)0.993317, (float)0.994415, - (float)0.995422, (float)0.996338, (float)0.997162, - (float)0.997864, (float)0.998474, (float)0.998962, - (float)0.999390, (float)0.999695, (float)0.999878, - (float)0.999969, (float)0.999969, (float)0.996918, - (float)0.987701, (float)0.972382, (float)0.951050, - (float)0.923889, (float)0.891022, (float)0.852631, - (float)0.809021, (float)0.760406, (float)0.707092, - (float)0.649445, (float)0.587799, (float)0.522491, - (float)0.453979, (float)0.382690, (float)0.309021, - (float)0.233459, (float)0.156433, (float)0.078461 - }; - - /* Lag window for LPC */ - float lpc_lagwinTbl[LPC_FILTERORDER + 1]={ - (float)1.000100, (float)0.998890, (float)0.995569, - (float)0.990057, (float)0.982392, - (float)0.972623, (float)0.960816, (float)0.947047, - (float)0.931405, (float)0.913989, (float)0.894909}; - - /* LSF quantization*/ - float lsfCbTbl[64 * 3 + 128 * 3 + 128 * 4] = { - (float)0.155396, (float)0.273193, (float)0.451172, - (float)0.390503, (float)0.648071, (float)1.002075, - (float)0.440186, (float)0.692261, (float)0.955688, - - - -Andersen, et al. Experimental [Page 89] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.343628, (float)0.642334, (float)1.071533, - (float)0.318359, (float)0.491577, (float)0.670532, - (float)0.193115, (float)0.375488, (float)0.725708, - (float)0.364136, (float)0.510376, (float)0.658691, - (float)0.297485, (float)0.527588, (float)0.842529, - (float)0.227173, (float)0.365967, (float)0.563110, - (float)0.244995, (float)0.396729, (float)0.636475, - (float)0.169434, (float)0.300171, (float)0.520264, - (float)0.312866, (float)0.464478, (float)0.643188, - (float)0.248535, (float)0.429932, (float)0.626099, - (float)0.236206, (float)0.491333, (float)0.817139, - (float)0.334961, (float)0.625122, (float)0.895752, - (float)0.343018, (float)0.518555, (float)0.698608, - (float)0.372803, (float)0.659790, (float)0.945435, - (float)0.176880, (float)0.316528, (float)0.581421, - (float)0.416382, (float)0.625977, (float)0.805176, - (float)0.303223, (float)0.568726, (float)0.915039, - (float)0.203613, (float)0.351440, (float)0.588135, - (float)0.221191, (float)0.375000, (float)0.614746, - (float)0.199951, (float)0.323364, (float)0.476074, - (float)0.300781, (float)0.433350, (float)0.566895, - (float)0.226196, (float)0.354004, (float)0.507568, - (float)0.300049, (float)0.508179, (float)0.711670, - (float)0.312012, (float)0.492676, (float)0.763428, - (float)0.329956, (float)0.541016, (float)0.795776, - (float)0.373779, (float)0.604614, (float)0.928833, - (float)0.210571, (float)0.452026, (float)0.755249, - (float)0.271118, (float)0.473267, (float)0.662476, - (float)0.285522, (float)0.436890, (float)0.634399, - (float)0.246704, (float)0.565552, (float)0.859009, - (float)0.270508, (float)0.406250, (float)0.553589, - (float)0.361450, (float)0.578491, (float)0.813843, - (float)0.342651, (float)0.482788, (float)0.622437, - (float)0.340332, (float)0.549438, (float)0.743164, - (float)0.200439, (float)0.336304, (float)0.540894, - (float)0.407837, (float)0.644775, (float)0.895142, - (float)0.294678, (float)0.454834, (float)0.699097, - (float)0.193115, (float)0.344482, (float)0.643188, - (float)0.275757, (float)0.420776, (float)0.598755, - (float)0.380493, (float)0.608643, (float)0.861084, - (float)0.222778, (float)0.426147, (float)0.676514, - (float)0.407471, (float)0.700195, (float)1.053101, - (float)0.218384, (float)0.377197, (float)0.669922, - (float)0.313232, (float)0.454102, (float)0.600952, - (float)0.347412, (float)0.571533, (float)0.874146, - (float)0.238037, (float)0.405396, (float)0.729492, - (float)0.223877, (float)0.412964, (float)0.822021, - (float)0.395264, (float)0.582153, (float)0.743896, - - - -Andersen, et al. Experimental [Page 90] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.247925, (float)0.485596, (float)0.720581, - (float)0.229126, (float)0.496582, (float)0.907715, - (float)0.260132, (float)0.566895, (float)1.012695, - (float)0.337402, (float)0.611572, (float)0.978149, - (float)0.267822, (float)0.447632, (float)0.769287, - (float)0.250610, (float)0.381714, (float)0.530029, - (float)0.430054, (float)0.805054, (float)1.221924, - (float)0.382568, (float)0.544067, (float)0.701660, - (float)0.383545, (float)0.710327, (float)1.149170, - (float)0.271362, (float)0.529053, (float)0.775513, - (float)0.246826, (float)0.393555, (float)0.588623, - (float)0.266846, (float)0.422119, (float)0.676758, - (float)0.311523, (float)0.580688, (float)0.838623, - (float)1.331177, (float)1.576782, (float)1.779541, - (float)1.160034, (float)1.401978, (float)1.768188, - (float)1.161865, (float)1.525146, (float)1.715332, - (float)0.759521, (float)0.913940, (float)1.119873, - (float)0.947144, (float)1.121338, (float)1.282471, - (float)1.015015, (float)1.557007, (float)1.804932, - (float)1.172974, (float)1.402100, (float)1.692627, - (float)1.087524, (float)1.474243, (float)1.665405, - (float)0.899536, (float)1.105225, (float)1.406250, - (float)1.148438, (float)1.484741, (float)1.796265, - (float)0.785645, (float)1.209839, (float)1.567749, - (float)0.867798, (float)1.166504, (float)1.450684, - (float)0.922485, (float)1.229858, (float)1.420898, - (float)0.791260, (float)1.123291, (float)1.409546, - (float)0.788940, (float)0.966064, (float)1.340332, - (float)1.051147, (float)1.272827, (float)1.556641, - (float)0.866821, (float)1.181152, (float)1.538818, - (float)0.906738, (float)1.373535, (float)1.607910, - (float)1.244751, (float)1.581421, (float)1.933838, - (float)0.913940, (float)1.337280, (float)1.539673, - (float)0.680542, (float)0.959229, (float)1.662720, - (float)0.887207, (float)1.430542, (float)1.800781, - (float)0.912598, (float)1.433594, (float)1.683960, - (float)0.860474, (float)1.060303, (float)1.455322, - (float)1.005127, (float)1.381104, (float)1.706909, - (float)0.800781, (float)1.363892, (float)1.829102, - (float)0.781860, (float)1.124390, (float)1.505981, - (float)1.003662, (float)1.471436, (float)1.684692, - (float)0.981323, (float)1.309570, (float)1.618042, - (float)1.228760, (float)1.554321, (float)1.756470, - (float)0.734375, (float)0.895752, (float)1.225586, - (float)0.841797, (float)1.055664, (float)1.249268, - (float)0.920166, (float)1.119385, (float)1.486206, - (float)0.894409, (float)1.539063, (float)1.828979, - (float)1.283691, (float)1.543335, (float)1.858276, - - - -Andersen, et al. Experimental [Page 91] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.676025, (float)0.933105, (float)1.490845, - (float)0.821289, (float)1.491821, (float)1.739868, - (float)0.923218, (float)1.144653, (float)1.580566, - (float)1.057251, (float)1.345581, (float)1.635864, - (float)0.888672, (float)1.074951, (float)1.353149, - (float)0.942749, (float)1.195435, (float)1.505493, - (float)1.492310, (float)1.788086, (float)2.039673, - (float)1.070313, (float)1.634399, (float)1.860962, - (float)1.253296, (float)1.488892, (float)1.686035, - (float)0.647095, (float)0.864014, (float)1.401855, - (float)0.866699, (float)1.254883, (float)1.453369, - (float)1.063965, (float)1.532593, (float)1.731323, - (float)1.167847, (float)1.521484, (float)1.884033, - (float)0.956055, (float)1.502075, (float)1.745605, - (float)0.928711, (float)1.288574, (float)1.479614, - (float)1.088013, (float)1.380737, (float)1.570801, - (float)0.905029, (float)1.186768, (float)1.371948, - (float)1.057861, (float)1.421021, (float)1.617432, - (float)1.108276, (float)1.312500, (float)1.501465, - (float)0.979492, (float)1.416992, (float)1.624268, - (float)1.276001, (float)1.661011, (float)2.007935, - (float)0.993042, (float)1.168579, (float)1.331665, - (float)0.778198, (float)0.944946, (float)1.235962, - (float)1.223755, (float)1.491333, (float)1.815674, - (float)0.852661, (float)1.350464, (float)1.722290, - (float)1.134766, (float)1.593140, (float)1.787354, - (float)1.051392, (float)1.339722, (float)1.531006, - (float)0.803589, (float)1.271240, (float)1.652100, - (float)0.755737, (float)1.143555, (float)1.639404, - (float)0.700928, (float)0.837280, (float)1.130371, - (float)0.942749, (float)1.197876, (float)1.669800, - (float)0.993286, (float)1.378296, (float)1.566528, - (float)0.801025, (float)1.095337, (float)1.298950, - (float)0.739990, (float)1.032959, (float)1.383667, - (float)0.845703, (float)1.072266, (float)1.543823, - (float)0.915649, (float)1.072266, (float)1.224487, - (float)1.021973, (float)1.226196, (float)1.481323, - (float)0.999878, (float)1.204102, (float)1.555908, - (float)0.722290, (float)0.913940, (float)1.340210, - (float)0.673340, (float)0.835938, (float)1.259521, - (float)0.832397, (float)1.208374, (float)1.394165, - (float)0.962158, (float)1.576172, (float)1.912842, - (float)1.166748, (float)1.370850, (float)1.556763, - (float)0.946289, (float)1.138550, (float)1.400391, - (float)1.035034, (float)1.218262, (float)1.386475, - (float)1.393799, (float)1.717773, (float)2.000244, - (float)0.972656, (float)1.260986, (float)1.760620, - (float)1.028198, (float)1.288452, (float)1.484619, - - - -Andersen, et al. Experimental [Page 92] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)0.773560, (float)1.258057, (float)1.756714, - (float)1.080322, (float)1.328003, (float)1.742676, - (float)0.823975, (float)1.450806, (float)1.917725, - (float)0.859009, (float)1.016602, (float)1.191895, - (float)0.843994, (float)1.131104, (float)1.645020, - (float)1.189697, (float)1.702759, (float)1.894409, - (float)1.346680, (float)1.763184, (float)2.066040, - (float)0.980469, (float)1.253784, (float)1.441650, - (float)1.338135, (float)1.641968, (float)1.932739, - (float)1.223267, (float)1.424194, (float)1.626465, - (float)0.765747, (float)1.004150, (float)1.579102, - (float)1.042847, (float)1.269165, (float)1.647461, - (float)0.968750, (float)1.257568, (float)1.555786, - (float)0.826294, (float)0.993408, (float)1.275146, - (float)0.742310, (float)0.950439, (float)1.430542, - (float)1.054321, (float)1.439819, (float)1.828003, - (float)1.072998, (float)1.261719, (float)1.441895, - (float)0.859375, (float)1.036377, (float)1.314819, - (float)0.895752, (float)1.267212, (float)1.605591, - (float)0.805420, (float)0.962891, (float)1.142334, - (float)0.795654, (float)1.005493, (float)1.468506, - (float)1.105347, (float)1.313843, (float)1.584839, - (float)0.792236, (float)1.221802, (float)1.465698, - (float)1.170532, (float)1.467651, (float)1.664063, - (float)0.838257, (float)1.153198, (float)1.342163, - (float)0.968018, (float)1.198242, (float)1.391235, - (float)1.250122, (float)1.623535, (float)1.823608, - (float)0.711670, (float)1.058350, (float)1.512085, - (float)1.204834, (float)1.454468, (float)1.739136, - (float)1.137451, (float)1.421753, (float)1.620117, - (float)0.820435, (float)1.322754, (float)1.578247, - (float)0.798706, (float)1.005005, (float)1.213867, - (float)0.980713, (float)1.324951, (float)1.512939, - (float)1.112305, (float)1.438843, (float)1.735596, - (float)1.135498, (float)1.356689, (float)1.635742, - (float)1.101318, (float)1.387451, (float)1.686523, - (float)0.849854, (float)1.276978, (float)1.523438, - (float)1.377930, (float)1.627563, (float)1.858154, - (float)0.884888, (float)1.095459, (float)1.287476, - (float)1.289795, (float)1.505859, (float)1.756592, - (float)0.817505, (float)1.384155, (float)1.650513, - (float)1.446655, (float)1.702148, (float)1.931885, - (float)0.835815, (float)1.023071, (float)1.385376, - (float)0.916626, (float)1.139038, (float)1.335327, - (float)0.980103, (float)1.174072, (float)1.453735, - (float)1.705688, (float)2.153809, (float)2.398315, (float)2.743408, - (float)1.797119, (float)2.016846, (float)2.445679, (float)2.701904, - (float)1.990356, (float)2.219116, (float)2.576416, (float)2.813477, - - - -Andersen, et al. Experimental [Page 93] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)1.849365, (float)2.190918, (float)2.611572, (float)2.835083, - (float)1.657959, (float)1.854370, (float)2.159058, (float)2.726196, - (float)1.437744, (float)1.897705, (float)2.253174, (float)2.655396, - (float)2.028687, (float)2.247314, (float)2.542358, (float)2.875854, - (float)1.736938, (float)1.922119, (float)2.185913, (float)2.743408, - (float)1.521606, (float)1.870972, (float)2.526855, (float)2.786987, - (float)1.841431, (float)2.050659, (float)2.463623, (float)2.857666, - (float)1.590088, (float)2.067261, (float)2.427979, (float)2.794434, - (float)1.746826, (float)2.057373, (float)2.320190, (float)2.800781, - (float)1.734619, (float)1.940552, (float)2.306030, (float)2.826416, - (float)1.786255, (float)2.204468, (float)2.457520, (float)2.795288, - (float)1.861084, (float)2.170532, (float)2.414551, (float)2.763672, - (float)2.001465, (float)2.307617, (float)2.552734, (float)2.811890, - (float)1.784424, (float)2.124146, (float)2.381592, (float)2.645508, - (float)1.888794, (float)2.135864, (float)2.418579, (float)2.861206, - (float)2.301147, (float)2.531250, (float)2.724976, (float)2.913086, - (float)1.837769, (float)2.051270, (float)2.261963, (float)2.553223, - (float)2.012939, (float)2.221191, (float)2.440186, (float)2.678101, - (float)1.429565, (float)1.858276, (float)2.582275, (float)2.845703, - (float)1.622803, (float)1.897705, (float)2.367310, (float)2.621094, - (float)1.581543, (float)1.960449, (float)2.515869, (float)2.736450, - (float)1.419434, (float)1.933960, (float)2.394653, (float)2.746704, - (float)1.721924, (float)2.059570, (float)2.421753, (float)2.769653, - (float)1.911011, (float)2.220703, (float)2.461060, (float)2.740723, - (float)1.581177, (float)1.860840, (float)2.516968, (float)2.874634, - (float)1.870361, (float)2.098755, (float)2.432373, (float)2.656494, - (float)2.059692, (float)2.279785, (float)2.495605, (float)2.729370, - (float)1.815674, (float)2.181519, (float)2.451538, (float)2.680542, - (float)1.407959, (float)1.768311, (float)2.343018, (float)2.668091, - (float)2.168701, (float)2.394653, (float)2.604736, (float)2.829346, - (float)1.636230, (float)1.865723, (float)2.329102, (float)2.824219, - (float)1.878906, (float)2.139526, (float)2.376709, (float)2.679810, - (float)1.765381, (float)1.971802, (float)2.195435, (float)2.586914, - (float)2.164795, (float)2.410889, (float)2.673706, (float)2.903198, - (float)2.071899, (float)2.331055, (float)2.645874, (float)2.907104, - (float)2.026001, (float)2.311523, (float)2.594849, (float)2.863892, - (float)1.948975, (float)2.180786, (float)2.514893, (float)2.797852, - (float)1.881836, (float)2.130859, (float)2.478149, (float)2.804199, - (float)2.238159, (float)2.452759, (float)2.652832, (float)2.868286, - (float)1.897949, (float)2.101685, (float)2.524292, (float)2.880127, - (float)1.856445, (float)2.074585, (float)2.541016, (float)2.791748, - (float)1.695557, (float)2.199097, (float)2.506226, (float)2.742676, - (float)1.612671, (float)1.877075, (float)2.435425, (float)2.732910, - (float)1.568848, (float)1.786499, (float)2.194580, (float)2.768555, - (float)1.953369, (float)2.164551, (float)2.486938, (float)2.874023, - (float)1.388306, (float)1.725342, (float)2.384521, (float)2.771851, - (float)2.115356, (float)2.337769, (float)2.592896, (float)2.864014, - (float)1.905762, (float)2.111328, (float)2.363525, (float)2.789307, - - - -Andersen, et al. Experimental [Page 94] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)1.882568, (float)2.332031, (float)2.598267, (float)2.827637, - (float)1.683594, (float)2.088745, (float)2.361938, (float)2.608643, - (float)1.874023, (float)2.182129, (float)2.536133, (float)2.766968, - (float)1.861938, (float)2.070435, (float)2.309692, (float)2.700562, - (float)1.722168, (float)2.107422, (float)2.477295, (float)2.837646, - (float)1.926880, (float)2.184692, (float)2.442627, (float)2.663818, - (float)2.123901, (float)2.337280, (float)2.553101, (float)2.777466, - (float)1.588135, (float)1.911499, (float)2.212769, (float)2.543945, - (float)2.053955, (float)2.370850, (float)2.712158, (float)2.939941, - (float)2.210449, (float)2.519653, (float)2.770386, (float)2.958618, - (float)2.199463, (float)2.474731, (float)2.718262, (float)2.919922, - (float)1.960083, (float)2.175415, (float)2.608032, (float)2.888794, - (float)1.953735, (float)2.185181, (float)2.428223, (float)2.809570, - (float)1.615234, (float)2.036499, (float)2.576538, (float)2.834595, - (float)1.621094, (float)2.028198, (float)2.431030, (float)2.664673, - (float)1.824951, (float)2.267456, (float)2.514526, (float)2.747925, - (float)1.994263, (float)2.229126, (float)2.475220, (float)2.833984, - (float)1.746338, (float)2.011353, (float)2.588257, (float)2.826904, - (float)1.562866, (float)2.135986, (float)2.471680, (float)2.687256, - (float)1.748901, (float)2.083496, (float)2.460938, (float)2.686279, - (float)1.758057, (float)2.131470, (float)2.636597, (float)2.891602, - (float)2.071289, (float)2.299072, (float)2.550781, (float)2.814331, - (float)1.839600, (float)2.094360, (float)2.496460, (float)2.723999, - (float)1.882202, (float)2.088257, (float)2.636841, (float)2.923096, - (float)1.957886, (float)2.153198, (float)2.384399, (float)2.615234, - (float)1.992920, (float)2.351196, (float)2.654419, (float)2.889771, - (float)2.012817, (float)2.262451, (float)2.643799, (float)2.903076, - (float)2.025635, (float)2.254761, (float)2.508423, (float)2.784058, - (float)2.316040, (float)2.589355, (float)2.794189, (float)2.963623, - (float)1.741211, (float)2.279541, (float)2.578491, (float)2.816284, - (float)1.845337, (float)2.055786, (float)2.348511, (float)2.822021, - (float)1.679932, (float)1.926514, (float)2.499756, (float)2.835693, - (float)1.722534, (float)1.946899, (float)2.448486, (float)2.728760, - (float)1.829834, (float)2.043213, (float)2.580444, (float)2.867676, - (float)1.676636, (float)2.071655, (float)2.322510, (float)2.704834, - (float)1.791504, (float)2.113525, (float)2.469727, (float)2.784058, - (float)1.977051, (float)2.215088, (float)2.497437, (float)2.726929, - (float)1.800171, (float)2.106689, (float)2.357788, (float)2.738892, - (float)1.827759, (float)2.170166, (float)2.525879, (float)2.852417, - (float)1.918335, (float)2.132813, (float)2.488403, (float)2.728149, - (float)1.916748, (float)2.225098, (float)2.542603, (float)2.857666, - (float)1.761230, (float)1.976074, (float)2.507446, (float)2.884521, - (float)2.053711, (float)2.367432, (float)2.608032, (float)2.837646, - (float)1.595337, (float)2.000977, (float)2.307129, (float)2.578247, - (float)1.470581, (float)2.031250, (float)2.375854, (float)2.647583, - (float)1.801392, (float)2.128052, (float)2.399780, (float)2.822876, - (float)1.853638, (float)2.066650, (float)2.429199, (float)2.751465, - (float)1.956299, (float)2.163696, (float)2.394775, (float)2.734253, - - - -Andersen, et al. Experimental [Page 95] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - (float)1.963623, (float)2.275757, (float)2.585327, (float)2.865234, - (float)1.887451, (float)2.105469, (float)2.331787, (float)2.587402, - (float)2.120117, (float)2.443359, (float)2.733887, (float)2.941406, - (float)1.506348, (float)1.766968, (float)2.400513, (float)2.851807, - (float)1.664551, (float)1.981079, (float)2.375732, (float)2.774414, - (float)1.720703, (float)1.978882, (float)2.391479, (float)2.640991, - (float)1.483398, (float)1.814819, (float)2.434448, (float)2.722290, - (float)1.769043, (float)2.136597, (float)2.563721, (float)2.774414, - (float)1.810791, (float)2.049316, (float)2.373901, (float)2.613647, - (float)1.788330, (float)2.005981, (float)2.359131, (float)2.723145, - (float)1.785156, (float)1.993164, (float)2.399780, (float)2.832520, - (float)1.695313, (float)2.022949, (float)2.522583, (float)2.745117, - (float)1.584106, (float)1.965576, (float)2.299927, (float)2.715576, - (float)1.894897, (float)2.249878, (float)2.655884, (float)2.897705, - (float)1.720581, (float)1.995728, (float)2.299438, (float)2.557007, - (float)1.619385, (float)2.173950, (float)2.574219, (float)2.787964, - (float)1.883179, (float)2.220459, (float)2.474365, (float)2.825073, - (float)1.447632, (float)2.045044, (float)2.555542, (float)2.744873, - (float)1.502686, (float)2.156616, (float)2.653320, (float)2.846558, - (float)1.711548, (float)1.944092, (float)2.282959, (float)2.685791, - (float)1.499756, (float)1.867554, (float)2.341064, (float)2.578857, - (float)1.916870, (float)2.135132, (float)2.568237, (float)2.826050, - (float)1.498047, (float)1.711182, (float)2.223267, (float)2.755127, - (float)1.808716, (float)1.997559, (float)2.256470, (float)2.758545, - (float)2.088501, (float)2.402710, (float)2.667358, (float)2.890259, - (float)1.545044, (float)1.819214, (float)2.324097, (float)2.692993, - (float)1.796021, (float)2.012573, (float)2.505737, (float)2.784912, - (float)1.786499, (float)2.041748, (float)2.290405, (float)2.650757, - (float)1.938232, (float)2.264404, (float)2.529053, (float)2.796143 - }; - -A.9. anaFilter.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - anaFilter.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_ANAFILTER_H - #define __iLBC_ANAFILTER_H - - void anaFilter( - - - -Andersen, et al. Experimental [Page 96] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float *In, /* (i) Signal to be filtered */ - float *a, /* (i) LP parameters */ - int len,/* (i) Length of signal */ - float *Out, /* (o) Filtered signal */ - float *mem /* (i/o) Filter state */ - ); - - #endif - -A.10. anaFilter.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - anaFilter.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include "iLBC_define.h" - - /*----------------------------------------------------------------* - * LP analysis filter. - *---------------------------------------------------------------*/ - - void anaFilter( - float *In, /* (i) Signal to be filtered */ - float *a, /* (i) LP parameters */ - int len,/* (i) Length of signal */ - float *Out, /* (o) Filtered signal */ - float *mem /* (i/o) Filter state */ - ){ - int i, j; - float *po, *pi, *pm, *pa; - - po = Out; - - /* Filter first part using memory from past */ - - for (i=0; i - #include - - /*----------------------------------------------------------------* - * Construct an additional codebook vector by filtering the - * initial codebook buffer. This vector is then used to expand - * the codebook with an additional section. - *---------------------------------------------------------------*/ - - void filteredCBvecs( - float *cbvectors, /* (o) Codebook vectors for the - higher section */ - float *mem, /* (i) Buffer to create codebook - vector from */ - int lMem /* (i) Length of buffer */ - ){ - int j, k; - float *pp, *pp1; - float tempbuff2[CB_MEML+CB_FILTERLEN]; - float *pos; - - memset(tempbuff2, 0, (CB_HALFFILTERLEN-1)*sizeof(float)); - memcpy(&tempbuff2[CB_HALFFILTERLEN-1], mem, lMem*sizeof(float)); - memset(&tempbuff2[lMem+CB_HALFFILTERLEN-1], 0, - (CB_HALFFILTERLEN+1)*sizeof(float)); - - /* Create codebook vector for higher section by filtering */ - - /* do filtering */ - pos=cbvectors; - memset(pos, 0, lMem*sizeof(float)); - for (k=0; k0.0) { - invenergy[tmpIndex]=(float)1.0/(energy[tmpIndex]+EPS); - } else { - invenergy[tmpIndex] = (float) 0.0; - } - - if (stage==0) { - measure = (float)-10000000.0; - - if (crossDot > 0.0) { - measure = crossDot*crossDot*invenergy[tmpIndex]; - } - } - else { - measure = crossDot*crossDot*invenergy[tmpIndex]; - } - - /* check if measure is better */ - ftmp = crossDot*invenergy[tmpIndex]; - - if ((measure>*max_measure) && (fabs(ftmp) - #include - #include - - - -Andersen, et al. Experimental [Page 104] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #include "iLBC_define.h" - - /*----------------------------------------------------------------* - * Compute cross correlation and pitch gain for pitch prediction - * of last subframe at given lag. - *---------------------------------------------------------------*/ - - void compCorr( - float *cc, /* (o) cross correlation coefficient */ - float *gc, /* (o) gain */ - float *pm, - float *buffer, /* (i) signal buffer */ - int lag, /* (i) pitch lag */ - int bLen, /* (i) length of buffer */ - int sRange /* (i) correlation search length */ - ){ - int i; - float ftmp1, ftmp2, ftmp3; - - /* Guard against getting outside buffer */ - if ((bLen-sRange-lag)<0) { - sRange=bLen-lag; - } - - ftmp1 = 0.0; - ftmp2 = 0.0; - ftmp3 = 0.0; - for (i=0; i 0.0) { - *cc = ftmp1*ftmp1/ftmp2; - *gc = (float)fabs(ftmp1/ftmp2); - *pm=(float)fabs(ftmp1)/ - ((float)sqrt(ftmp2)*(float)sqrt(ftmp3)); - } - else { - *cc = 0.0; - *gc = 0.0; - *pm=0.0; - } - } - - - -Andersen, et al. Experimental [Page 105] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /*----------------------------------------------------------------* - * Packet loss concealment routine. Conceals a residual signal - * and LP parameters. If no packet loss, update state. - *---------------------------------------------------------------*/ - - void doThePLC( - float *PLCresidual, /* (o) concealed residual */ - float *PLClpc, /* (o) concealed LP parameters */ - int PLI, /* (i) packet loss indicator - 0 - no PL, 1 = PL */ - float *decresidual, /* (i) decoded residual */ - float *lpc, /* (i) decoded LPC (only used for no PL) */ - int inlag, /* (i) pitch lag */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i/o) decoder instance */ - ){ - int lag=20, randlag; - float gain, maxcc; - float use_gain; - float gain_comp, maxcc_comp, per, max_per; - int i, pick, use_lag; - float ftmp, randvec[BLOCKL_MAX], pitchfact, energy; - - /* Packet Loss */ - - if (PLI == 1) { - - iLBCdec_inst->consPLICount += 1; - - /* if previous frame not lost, - determine pitch pred. gain */ - - if (iLBCdec_inst->prevPLI != 1) { - - /* Search around the previous lag to find the - best pitch period */ - - lag=inlag-3; - compCorr(&maxcc, &gain, &max_per, - iLBCdec_inst->prevResidual, - lag, iLBCdec_inst->blockl, 60); - for (i=inlag-2;i<=inlag+3;i++) { - compCorr(&maxcc_comp, &gain_comp, &per, - iLBCdec_inst->prevResidual, - i, iLBCdec_inst->blockl, 60); - - if (maxcc_comp>maxcc) { - maxcc=maxcc_comp; - - - -Andersen, et al. Experimental [Page 106] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - gain=gain_comp; - lag=i; - max_per=per; - } - } - - } - - /* previous frame lost, use recorded lag and periodicity */ - - else { - lag=iLBCdec_inst->prevLag; - max_per=iLBCdec_inst->per; - } - - /* downscaling */ - - use_gain=1.0; - if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320) - use_gain=(float)0.9; - else if (iLBCdec_inst->consPLICount* - iLBCdec_inst->blockl>2*320) - use_gain=(float)0.7; - else if (iLBCdec_inst->consPLICount* - iLBCdec_inst->blockl>3*320) - use_gain=(float)0.5; - else if (iLBCdec_inst->consPLICount* - iLBCdec_inst->blockl>4*320) - use_gain=(float)0.0; - - /* mix noise and pitch repeatition */ - ftmp=(float)sqrt(max_per); - if (ftmp>(float)0.7) - pitchfact=(float)1.0; - else if (ftmp>(float)0.4) - pitchfact=(ftmp-(float)0.4)/((float)0.7-(float)0.4); - else - pitchfact=0.0; - - - /* avoid repetition of same pitch cycle */ - use_lag=lag; - if (lag<80) { - use_lag=2*lag; - } - - /* compute concealed residual */ - - - - -Andersen, et al. Experimental [Page 107] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - energy = 0.0; - for (i=0; iblockl; i++) { - - /* noise component */ - - iLBCdec_inst->seed=(iLBCdec_inst->seed*69069L+1) & - (0x80000000L-1); - randlag = 50 + ((signed long) iLBCdec_inst->seed)%70; - pick = i - randlag; - - if (pick < 0) { - randvec[i] = - iLBCdec_inst->prevResidual[ - iLBCdec_inst->blockl+pick]; - } else { - randvec[i] = randvec[pick]; - } - - /* pitch repeatition component */ - pick = i - use_lag; - - if (pick < 0) { - PLCresidual[i] = - iLBCdec_inst->prevResidual[ - iLBCdec_inst->blockl+pick]; - } else { - PLCresidual[i] = PLCresidual[pick]; - } - - /* mix random and periodicity component */ - - if (i<80) - PLCresidual[i] = use_gain*(pitchfact * - PLCresidual[i] + - ((float)1.0 - pitchfact) * randvec[i]); - else if (i<160) - PLCresidual[i] = (float)0.95*use_gain*(pitchfact * - PLCresidual[i] + - ((float)1.0 - pitchfact) * randvec[i]); - else - PLCresidual[i] = (float)0.9*use_gain*(pitchfact * - PLCresidual[i] + - ((float)1.0 - pitchfact) * randvec[i]); - - energy += PLCresidual[i] * PLCresidual[i]; - } - - /* less than 30 dB, use only noise */ - - - -Andersen, et al. Experimental [Page 108] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - - if (sqrt(energy/(float)iLBCdec_inst->blockl) < 30.0) { - gain=0.0; - for (i=0; iblockl; i++) { - PLCresidual[i] = randvec[i]; - } - } - - /* use old LPC */ - - memcpy(PLClpc,iLBCdec_inst->prevLpc, - (LPC_FILTERORDER+1)*sizeof(float)); - - } - - /* no packet loss, copy input */ - - else { - memcpy(PLCresidual, decresidual, - iLBCdec_inst->blockl*sizeof(float)); - memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float)); - iLBCdec_inst->consPLICount = 0; - } - - /* update state */ - - if (PLI) { - iLBCdec_inst->prevLag = lag; - iLBCdec_inst->per=max_per; - } - - iLBCdec_inst->prevPLI = PLI; - memcpy(iLBCdec_inst->prevLpc, PLClpc, - (LPC_FILTERORDER+1)*sizeof(float)); - memcpy(iLBCdec_inst->prevResidual, PLCresidual, - iLBCdec_inst->blockl*sizeof(float)); - } - -A.15. enhancer.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - enhancer.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - - -Andersen, et al. Experimental [Page 109] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - ******************************************************************/ - - #ifndef __ENHANCER_H - #define __ENHANCER_H - - #include "iLBC_define.h" - - float xCorrCoef( - float *target, /* (i) first array */ - float *regressor, /* (i) second array */ - int subl /* (i) dimension arrays */ - ); - - int enhancerInterface( - float *out, /* (o) the enhanced recidual signal */ - float *in, /* (i) the recidual signal to enhance */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i/o) the decoder state structure */ - ); - - #endif - -A.16. enhancer.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - enhancer.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - #include "iLBC_define.h" - #include "constants.h" - #include "filter.h" - - /*----------------------------------------------------------------* - * Find index in array such that the array element with said - * index is the element of said array closest to "value" - * according to the squared-error criterion - *---------------------------------------------------------------*/ - - void NearestNeighbor( - - - -Andersen, et al. Experimental [Page 110] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - int *index, /* (o) index of array element closest - to value */ - float *array, /* (i) data array */ - float value,/* (i) value */ - int arlength/* (i) dimension of data array */ - ){ - int i; - float bestcrit,crit; - - crit=array[0]-value; - bestcrit=crit*crit; - *index=0; - for (i=1; i dim1 ) { - hfl2=(int) (dim1/2); - for (j=0; j= idatal) { - searchSegEndPos=idatal-ENH_BLOCKL-1; - } - corrdim=searchSegEndPos-searchSegStartPos+1; - - /* compute upsampled correlation (corr33) and find - location of max */ - - mycorr1(corrVec,idata+searchSegStartPos, - corrdim+ENH_BLOCKL-1,idata+centerStartPos,ENH_BLOCKL); - enh_upsample(corrVecUps,corrVec,corrdim,ENH_FL0); - tloc=0; maxv=corrVecUps[0]; - for (i=1; imaxv) { - tloc=i; - maxv=corrVecUps[i]; - } - } - - /* make vector can be upsampled without ever running outside - bounds */ - - *updStartPos= (float)searchSegStartPos + - (float)tloc/(float)ENH_UPS0+(float)1.0; - tloc2=(int)(tloc/ENH_UPS0); - - if (tloc>tloc2*ENH_UPS0) { - tloc2++; - } - st=searchSegStartPos+tloc2-ENH_FL0; - - if (st<0) { - memset(vect,0,-st*sizeof(float)); - memcpy(&vect[-st],idata, (ENH_VECTL+st)*sizeof(float)); - } - else { - - - -Andersen, et al. Experimental [Page 114] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - en=st+ENH_VECTL; - - if (en>idatal) { - memcpy(vect, &idata[st], - (ENH_VECTL-(en-idatal))*sizeof(float)); - memset(&vect[ENH_VECTL-(en-idatal)], 0, - (en-idatal)*sizeof(float)); - } - else { - memcpy(vect, &idata[st], ENH_VECTL*sizeof(float)); - } - } - fraction=tloc2*ENH_UPS0-tloc; - - /* compute the segment (this is actually a convolution) */ - - mycorr1(seg,vect,ENH_VECTL,polyphaserTbl+(2*ENH_FL0+1)*fraction, - 2*ENH_FL0+1); - } - - /*----------------------------------------------------------------* - * find the smoothed output data - *---------------------------------------------------------------*/ - - void smath( - float *odata, /* (o) smoothed output */ - float *sseq,/* (i) said second sequence of waveforms */ - int hl, /* (i) 2*hl+1 is sseq dimension */ - float alpha0/* (i) max smoothing energy fraction */ - ){ - int i,k; - float w00,w10,w11,A,B,C,*psseq,err,errs; - float surround[BLOCKL_MAX]; /* shape contributed by other than - current */ - float wt[2*ENH_HL+1]; /* waveform weighting to get - surround shape */ - float denom; - - /* create shape of contribution from all waveforms except the - current one */ - - for (i=1; i<=2*hl+1; i++) { - wt[i-1] = (float)0.5*(1 - (float)cos(2*PI*i/(2*hl+2))); - } - wt[hl]=0.0; /* for clarity, not used */ - for (i=0; i alpha0 * w00) { - if ( w00 < 1) { - w00=1; - } - denom = (w11*w00-w10*w10)/(w00*w00); - - if (denom > 0.0001) { /* eliminates numerical problems - for if smooth */ - - - -Andersen, et al. Experimental [Page 116] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - A = (float)sqrt( (alpha0- alpha0*alpha0/4)/denom); - B = -alpha0/2 - A * w10/w00; - B = B+1; - } - else { /* essentially no difference between cycles; - smoothing not needed */ - A= 0.0; - B= 1.0; - } - - /* create smoothed sequence */ - - psseq=sseq+hl*ENH_BLOCKL; - for (i=0; i=0; q--) { - blockStartPos[q]=blockStartPos[q+1]-period[lagBlock[q+1]]; - NearestNeighbor(lagBlock+q,plocs, - blockStartPos[q]+ - ENH_BLOCKL_HALF-period[lagBlock[q+1]], periodl); - - - if (blockStartPos[q]-ENH_OVERHANG>=0) { - refiner(sseq+q*ENH_BLOCKL, blockStartPos+q, idata, - idatal, centerStartPos, blockStartPos[q], - period[lagBlock[q+1]]); - } else { - psseq=sseq+q*ENH_BLOCKL; - memset(psseq, 0, ENH_BLOCKL*sizeof(float)); - } - } - - /* future */ - - for (i=0; i 0.0) { - return (float)(ftmp1*ftmp1/ftmp2); - } - - - -Andersen, et al. Experimental [Page 119] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - else { - return (float)0.0; - } - } - - /*----------------------------------------------------------------* - * interface for enhancer - *---------------------------------------------------------------*/ - - int enhancerInterface( - float *out, /* (o) enhanced signal */ - float *in, /* (i) unenhanced signal */ - iLBC_Dec_Inst_t *iLBCdec_inst /* (i) buffers etc */ - ){ - float *enh_buf, *enh_period; - int iblock, isample; - int lag=0, ilag, i, ioffset; - float cc, maxcc; - float ftmp1, ftmp2; - float *inPtr, *enh_bufPtr1, *enh_bufPtr2; - float plc_pred[ENH_BLOCKL]; - - float lpState[6], downsampled[(ENH_NBLOCKS*ENH_BLOCKL+120)/2]; - int inLen=ENH_NBLOCKS*ENH_BLOCKL+120; - int start, plc_blockl, inlag; - - enh_buf=iLBCdec_inst->enh_buf; - enh_period=iLBCdec_inst->enh_period; - - memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl], - (ENH_BUFL-iLBCdec_inst->blockl)*sizeof(float)); - - memcpy(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in, - iLBCdec_inst->blockl*sizeof(float)); - - if (iLBCdec_inst->mode==30) - plc_blockl=ENH_BLOCKL; - else - plc_blockl=40; - - /* when 20 ms frame, move processing one block */ - ioffset=0; - if (iLBCdec_inst->mode==20) ioffset=1; - - i=3-ioffset; - memmove(enh_period, &enh_period[i], - (ENH_NBLOCKS_TOT-i)*sizeof(float)); - - - - -Andersen, et al. Experimental [Page 120] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* Set state information to the 6 samples right before - the samples to be downsampled. */ - - memcpy(lpState, - enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-126, - 6*sizeof(float)); - - /* Down sample a factor 2 to save computations */ - - DownSample(enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-120, - lpFilt_coefsTbl, inLen-ioffset*ENH_BLOCKL, - lpState, downsampled); - - /* Estimate the pitch in the down sampled domain. */ - for (iblock = 0; iblock maxcc) { - maxcc = cc; - lag = ilag; - } - } - - /* Store the estimated lag in the non-downsampled domain */ - enh_period[iblock+ENH_NBLOCKS_EXTRA+ioffset] = (float)lag*2; - - - } - - - /* PLC was performed on the previous packet */ - if (iLBCdec_inst->prev_enh_pl==1) { - - inlag=(int)enh_period[ENH_NBLOCKS_EXTRA+ioffset]; - - lag = inlag-1; - maxcc = xCorrCoef(in, in+lag, plc_blockl); - for (ilag=inlag; ilag<=inlag+1; ilag++) { - cc = xCorrCoef(in, in+ilag, plc_blockl); - - - - -Andersen, et al. Experimental [Page 121] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - if (cc > maxcc) { - maxcc = cc; - lag = ilag; - } - } - - enh_period[ENH_NBLOCKS_EXTRA+ioffset-1]=(float)lag; - - /* compute new concealed residual for the old lookahead, - mix the forward PLC with a backward PLC from - the new frame */ - - inPtr=&in[lag-1]; - - enh_bufPtr1=&plc_pred[plc_blockl-1]; - - if (lag>plc_blockl) { - start=plc_blockl; - } else { - start=lag; - } - - for (isample = start; isample>0; isample--) { - *enh_bufPtr1-- = *inPtr--; - } - - enh_bufPtr2=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl]; - for (isample = (plc_blockl-1-lag); isample>=0; isample--) { - *enh_bufPtr1-- = *enh_bufPtr2--; - } - - /* limit energy change */ - ftmp2=0.0; - ftmp1=0.0; - for (i=0;iblockl-i]* - enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i]; - ftmp1+=plc_pred[i]*plc_pred[i]; - } - ftmp1=(float)sqrt(ftmp1/(float)plc_blockl); - ftmp2=(float)sqrt(ftmp2/(float)plc_blockl); - if (ftmp1>(float)2.0*ftmp2 && ftmp1>0.0) { - for (i=0;iblockl]; - for (i=0; imode==20) { - /* Enhancer with 40 samples delay */ - for (iblock = 0; iblock<2; iblock++) { - enhancer(out+iblock*ENH_BLOCKL, enh_buf, - ENH_BUFL, (5+iblock)*ENH_BLOCKL+40, - ENH_ALPHA0, enh_period, enh_plocsTbl, - ENH_NBLOCKS_TOT); - } - } else if (iLBCdec_inst->mode==30) { - /* Enhancer with 80 samples delay */ - for (iblock = 0; iblock<3; iblock++) { - enhancer(out+iblock*ENH_BLOCKL, enh_buf, - ENH_BUFL, (4+iblock)*ENH_BLOCKL, - ENH_ALPHA0, enh_period, enh_plocsTbl, - ENH_NBLOCKS_TOT); - } - } - - return (lag*2); - } - -A.17. filter.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - filter.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - - - -Andersen, et al. Experimental [Page 123] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #ifndef __iLBC_FILTER_H - #define __iLBC_FILTER_H - - void AllPoleFilter( - float *InOut, /* (i/o) on entrance InOut[-orderCoef] to - InOut[-1] contain the state of the - filter (delayed samples). InOut[0] to - InOut[lengthInOut-1] contain the filter - input, on en exit InOut[-orderCoef] to - InOut[-1] is unchanged and InOut[0] to - InOut[lengthInOut-1] contain filtered - samples */ - float *Coef,/* (i) filter coefficients, Coef[0] is assumed - to be 1.0 */ - int lengthInOut,/* (i) number of input/output samples */ - int orderCoef /* (i) number of filter coefficients */ - ); - - void AllZeroFilter( - float *In, /* (i) In[0] to In[lengthInOut-1] contain - filter input samples */ - float *Coef,/* (i) filter coefficients (Coef[0] is assumed - to be 1.0) */ - int lengthInOut,/* (i) number of input/output samples */ - int orderCoef, /* (i) number of filter coefficients */ - float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1] - contain the filter state, on exit Out[0] - to Out[lengthInOut-1] contain filtered - samples */ - ); - - void ZeroPoleFilter( - float *In, /* (i) In[0] to In[lengthInOut-1] contain filter - input samples In[-orderCoef] to In[-1] - contain state of all-zero section */ - float *ZeroCoef,/* (i) filter coefficients for all-zero - section (ZeroCoef[0] is assumed to - be 1.0) */ - float *PoleCoef,/* (i) filter coefficients for all-pole section - (ZeroCoef[0] is assumed to be 1.0) */ - int lengthInOut,/* (i) number of input/output samples */ - int orderCoef, /* (i) number of filter coefficients */ - float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1] - contain state of all-pole section. On - exit Out[0] to Out[lengthInOut-1] - contain filtered samples */ - ); - - - - -Andersen, et al. Experimental [Page 124] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - void DownSample ( - float *In, /* (i) input samples */ - float *Coef, /* (i) filter coefficients */ - int lengthIn, /* (i) number of input samples */ - float *state, /* (i) filter state */ - float *Out /* (o) downsampled output */ - ); - - #endif - -A.18. filter.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - filter.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include "iLBC_define.h" - - /*----------------------------------------------------------------* - * all-pole filter - *---------------------------------------------------------------*/ - - void AllPoleFilter( - float *InOut, /* (i/o) on entrance InOut[-orderCoef] to - InOut[-1] contain the state of the - filter (delayed samples). InOut[0] to - InOut[lengthInOut-1] contain the filter - input, on en exit InOut[-orderCoef] to - InOut[-1] is unchanged and InOut[0] to - InOut[lengthInOut-1] contain filtered - samples */ - float *Coef,/* (i) filter coefficients, Coef[0] is assumed - to be 1.0 */ - int lengthInOut,/* (i) number of input/output samples */ - int orderCoef /* (i) number of filter coefficients */ - ){ - int n,k; - - for(n=0;nnsub-1; n++) { - pp=residual+n*SUBL; - for (l=0; l<5; l++) { - fssqEn[n] += sampEn_win[l] * (*pp) * (*pp); - bssqEn[n] += (*pp) * (*pp); - pp++; - } - for (l=5; lnsub-1; - pp=residual+n*SUBL; - for (l=0; lmode==20) l=1; - else l=0; - - max_ssqEn=(fssqEn[0]+bssqEn[1])*ssqEn_win[l]; - max_ssqEn_n=1; - for (n=2; nnsub; n++) { - - - - -Andersen, et al. Experimental [Page 130] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - l++; - if ((fssqEn[n-1]+bssqEn[n])*ssqEn_win[l] > max_ssqEn) { - max_ssqEn=(fssqEn[n-1]+bssqEn[n]) * - ssqEn_win[l]; - max_ssqEn_n=n; - } - } - - return max_ssqEn_n; - } - -A.21. gainquant.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - gainquant.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_GAINQUANT_H - #define __iLBC_GAINQUANT_H - - float gainquant(/* (o) quantized gain value */ - float in, /* (i) gain value */ - float maxIn,/* (i) maximum of gain value */ - int cblen, /* (i) number of quantization indices */ - int *index /* (o) quantization index */ - ); - - float gaindequant( /* (o) quantized gain value */ - int index, /* (i) quantization index */ - float maxIn,/* (i) maximum of unquantized gain */ - int cblen /* (i) number of quantization indices */ - ); - - #endif - -A.22. gainquant.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - - - -Andersen, et al. Experimental [Page 131] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - gainquant.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - #include "constants.h" - #include "filter.h" - - /*----------------------------------------------------------------* - * quantizer for the gain in the gain-shape coding of residual - *---------------------------------------------------------------*/ - - float gainquant(/* (o) quantized gain value */ - float in, /* (i) gain value */ - float maxIn,/* (i) maximum of gain value */ - int cblen, /* (i) number of quantization indices */ - int *index /* (o) quantization index */ - ){ - int i, tindex; - float minmeasure,measure, *cb, scale; - - /* ensure a lower bound on the scaling factor */ - - scale=maxIn; - - if (scale<0.1) { - scale=(float)0.1; - } - - /* select the quantization table */ - - if (cblen == 8) { - cb = gain_sq3Tbl; - } else if (cblen == 16) { - cb = gain_sq4Tbl; - } else { - cb = gain_sq5Tbl; - } - - /* select the best index in the quantization table */ - - minmeasure=10000000.0; - tindex=0; - for (i=0; i - - /*----------------------------------------------------------------* - * Construct codebook vector for given index. - *---------------------------------------------------------------*/ - - void getCBvec( - - - -Andersen, et al. Experimental [Page 134] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float *cbvec, /* (o) Constructed codebook vector */ - float *mem, /* (i) Codebook buffer */ - int index, /* (i) Codebook index */ - int lMem, /* (i) Length of codebook buffer */ - int cbveclen/* (i) Codebook vector length */ - ){ - int j, k, n, memInd, sFilt; - float tmpbuf[CB_MEML]; - int base_size; - int ilow, ihigh; - float alfa, alfa1; - - /* Determine size of codebook sections */ - - base_size=lMem-cbveclen+1; - - if (cbveclen==SUBL) { - base_size+=cbveclen/2; - } - - /* No filter -> First codebook section */ - - if (index - - #include "iLBC_define.h" - #include "constants.h" - - /*----------------------------------------------------------------* - * calculation of auto correlation - *---------------------------------------------------------------*/ - - void autocorr( - float *r, /* (o) autocorrelation vector */ - const float *x, /* (i) data vector */ - int N, /* (i) length of data vector */ - int order /* largest lag for calculated - autocorrelations */ - ){ - int lag, n; - float sum; - - for (lag = 0; lag <= order; lag++) { - sum = 0; - for (n = 0; n < N - lag; n++) { - sum += x[n] * x[n+lag]; - } - r[lag] = sum; - } - - - -Andersen, et al. Experimental [Page 140] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - - /*----------------------------------------------------------------* - * window multiplication - *---------------------------------------------------------------*/ - - void window( - float *z, /* (o) the windowed data */ - const float *x, /* (i) the original data vector */ - const float *y, /* (i) the window */ - int N /* (i) length of all vectors */ - ){ - int i; - - for (i = 0; i < N; i++) { - z[i] = x[i] * y[i]; - } - } - - /*----------------------------------------------------------------* - * levinson-durbin solution for lpc coefficients - *---------------------------------------------------------------*/ - - void levdurb( - float *a, /* (o) lpc coefficient vector starting - with 1.0 */ - float *k, /* (o) reflection coefficients */ - float *r, /* (i) autocorrelation vector */ - int order /* (i) order of lpc filter */ - ){ - float sum, alpha; - int m, m_h, i; - - a[0] = 1.0; - - if (r[0] < EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ - for (i = 0; i < order; i++) { - k[i] = 0; - a[i+1] = 0; - } - } else { - a[1] = k[0] = -r[1]/r[0]; - alpha = r[0] + r[1] * k[0]; - for (m = 1; m < order; m++){ - sum = r[m + 1]; - for (i = 0; i < m; i++){ - sum += a[i+1] * r[m - i]; - } - - - -Andersen, et al. Experimental [Page 141] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - k[m] = -sum / alpha; - alpha += k[m] * sum; - m_h = (m + 1) >> 1; - for (i = 0; i < m_h; i++){ - sum = a[i+1] + k[m] * a[m - i]; - a[m - i] += k[m] * a[i+1]; - a[i+1] = sum; - } - a[m+1] = k[m]; - } - } - } - - /*----------------------------------------------------------------* - * interpolation between vectors - *---------------------------------------------------------------*/ - - void interpolate( - float *out, /* (o) the interpolated vector */ - float *in1, /* (i) the first vector for the - interpolation */ - float *in2, /* (i) the second vector for the - interpolation */ - float coef, /* (i) interpolation weights */ - int length /* (i) length of all vectors */ - ){ - int i; - float invcoef; - - invcoef = (float)1.0 - coef; - for (i = 0; i < length; i++) { - out[i] = coef * in1[i] + invcoef * in2[i]; - } - } - - /*----------------------------------------------------------------* - * lpc bandwidth expansion - *---------------------------------------------------------------*/ - - void bwexpand( - float *out, /* (o) the bandwidth expanded lpc - coefficients */ - float *in, /* (i) the lpc coefficients before bandwidth - expansion */ - float coef, /* (i) the bandwidth expansion factor */ - int length /* (i) the length of lpc coefficient vectors */ - ){ - int i; - - - -Andersen, et al. Experimental [Page 142] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float chirp; - - chirp = coef; - - out[0] = in[0]; - for (i = 1; i < length; i++) { - out[i] = chirp * in[i]; - chirp *= coef; - } - } - - /*----------------------------------------------------------------* - * vector quantization - *---------------------------------------------------------------*/ - - void vq( - float *Xq, /* (o) the quantized vector */ - int *index, /* (o) the quantization index */ - const float *CB,/* (i) the vector quantization codebook */ - float *X, /* (i) the vector to quantize */ - int n_cb, /* (i) the number of vectors in the codebook */ - int dim /* (i) the dimension of all vectors */ - ){ - int i, j; - int pos, minindex; - float dist, tmp, mindist; - - pos = 0; - mindist = FLOAT_MAX; - minindex = 0; - for (j = 0; j < n_cb; j++) { - dist = X[0] - CB[pos]; - dist *= dist; - for (i = 1; i < dim; i++) { - tmp = X[i] - CB[pos + i]; - dist += tmp*tmp; - } - - if (dist < mindist) { - mindist = dist; - minindex = j; - } - pos += dim; - } - for (i = 0; i < dim; i++) { - Xq[i] = CB[minindex*dim + i]; - } - *index = minindex; - - - -Andersen, et al. Experimental [Page 143] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - - /*----------------------------------------------------------------* - * split vector quantization - *---------------------------------------------------------------*/ - - void SplitVQ( - float *qX, /* (o) the quantized vector */ - int *index, /* (o) a vector of indexes for all vector - codebooks in the split */ - float *X, /* (i) the vector to quantize */ - const float *CB,/* (i) the quantizer codebook */ - int nsplit, /* the number of vector splits */ - const int *dim, /* the dimension of X and qX */ - const int *cbsize /* the number of vectors in the codebook */ - ){ - int cb_pos, X_pos, i; - - cb_pos = 0; - X_pos= 0; - for (i = 0; i < nsplit; i++) { - vq(qX + X_pos, index + i, CB + cb_pos, X + X_pos, - cbsize[i], dim[i]); - X_pos += dim[i]; - cb_pos += dim[i] * cbsize[i]; - } - } - - /*----------------------------------------------------------------* - * scalar quantization - *---------------------------------------------------------------*/ - - void sort_sq( - float *xq, /* (o) the quantized value */ - int *index, /* (o) the quantization index */ - float x, /* (i) the value to quantize */ - const float *cb,/* (i) the quantization codebook */ - int cb_size /* (i) the size of the quantization codebook */ - ){ - int i; - - if (x <= cb[0]) { - *index = 0; - *xq = cb[0]; - } else { - i = 0; - while ((x > cb[i]) && i < cb_size - 1) { - i++; - - - -Andersen, et al. Experimental [Page 144] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - - if (x > ((cb[i] + cb[i - 1])/2)) { - *index = i; - *xq = cb[i]; - } else { - *index = i - 1; - *xq = cb[i - 1]; - } - } - } - - /*----------------------------------------------------------------* - * check for stability of lsf coefficients - *---------------------------------------------------------------*/ - - int LSF_check( /* (o) 1 for stable lsf vectors and 0 for - nonstable ones */ - float *lsf, /* (i) a table of lsf vectors */ - int dim, /* (i) the dimension of each lsf vector */ - int NoAn /* (i) the number of lsf vectors in the - table */ - ){ - int k,n,m, Nit=2, change=0,pos; - float tmp; - static float eps=(float)0.039; /* 50 Hz */ - static float eps2=(float)0.0195; - static float maxlsf=(float)3.14; /* 4000 Hz */ - static float minlsf=(float)0.01; /* 0 Hz */ - - /* LSF separation check*/ - - for (n=0; nmaxlsf) { - lsf[pos]=maxlsf; - change=1; - } - } - } - } - - return change; - } - -A.27. hpInput.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - hpInput.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_HPINPUT_H - #define __iLBC_HPINPUT_H - - void hpInput( - float *In, /* (i) vector to filter */ - int len, /* (i) length of vector to filter */ - float *Out, /* (o) the resulting filtered vector */ - float *mem /* (i/o) the filter state */ - ); - - #endif - -A.28. hpInput.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - - -Andersen, et al. Experimental [Page 146] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - hpInput.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include "constants.h" - - /*----------------------------------------------------------------* - * Input high-pass filter - *---------------------------------------------------------------*/ - - void hpInput( - float *In, /* (i) vector to filter */ - int len, /* (i) length of vector to filter */ - float *Out, /* (o) the resulting filtered vector */ - float *mem /* (i/o) the filter state */ - ){ - int i; - float *pi, *po; - - /* all-zero section*/ - - pi = &In[0]; - po = &Out[0]; - for (i=0; i - - #include "iLBC_define.h" - #include "gainquant.h" - #include "getCBvec.h" - - /*----------------------------------------------------------------* - * Convert the codebook indexes to make the search easier - *---------------------------------------------------------------*/ - - - - -Andersen, et al. Experimental [Page 150] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - void index_conv_enc( - int *index /* (i/o) Codebook indexes */ - ){ - int k; - - for (k=1; k=108)&&(index[k]<172)) { - index[k]-=64; - } else if (index[k]>=236) { - index[k]-=128; - } else { - /* ERROR */ - } - } - } - - void index_conv_dec( - int *index /* (i/o) Codebook indexes */ - ){ - int k; - - for (k=1; k=44)&&(index[k]<108)) { - index[k]+=64; - } else if ((index[k]>=108)&&(index[k]<128)) { - index[k]+=128; - } else { - /* ERROR */ - } - } - } - - /*----------------------------------------------------------------* - * Construct decoded vector from codebook and gains. - *---------------------------------------------------------------*/ - - void iCBConstruct( - float *decvector, /* (o) Decoded vector */ - int *index, /* (i) Codebook indices */ - int *gain_index,/* (i) Gain quantization indices */ - float *mem, /* (i) Buffer for codevector construction */ - int lMem, /* (i) Length of buffer */ - int veclen, /* (i) Length of vector */ - int nStages /* (i) Number of codebook stages */ - ){ - int j,k; - - - -Andersen, et al. Experimental [Page 151] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float gain[CB_NSTAGES]; - float cbvec[SUBL]; - - /* gain de-quantization */ - - gain[0] = gaindequant(gain_index[0], 1.0, 32); - if (nStages > 1) { - gain[1] = gaindequant(gain_index[1], - (float)fabs(gain[0]), 16); - } - if (nStages > 2) { - gain[2] = gaindequant(gain_index[2], - (float)fabs(gain[1]), 8); - } - - /* codebook vector construction and construction of - total vector */ - - getCBvec(cbvec, mem, index[0], lMem, veclen); - for (j=0;j 1) { - for (k=1; k - #include - - #include "iLBC_define.h" - #include "gainquant.h" - #include "createCB.h" - #include "filter.h" - #include "constants.h" - - /*----------------------------------------------------------------* - * Search routine for codebook encoding and gain quantization. - *---------------------------------------------------------------*/ - - void iCBSearch( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) the encoder state structure */ - int *index, /* (o) Codebook indices */ - int *gain_index,/* (o) Gain quantization indices */ - - - -Andersen, et al. Experimental [Page 153] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float *intarget,/* (i) Target vector for encoding */ - float *mem, /* (i) Buffer for codebook construction */ - int lMem, /* (i) Length of buffer */ - int lTarget, /* (i) Length of vector */ - int nStages, /* (i) Number of codebook stages */ - float *weightDenum, /* (i) weighting filter coefficients */ - float *weightState, /* (i) weighting filter state */ - int block /* (i) the sub-block number */ - ){ - int i, j, icount, stage, best_index, range, counter; - float max_measure, gain, measure, crossDot, ftmp; - float gains[CB_NSTAGES]; - float target[SUBL]; - int base_index, sInd, eInd, base_size; - int sIndAug=0, eIndAug=0; - float buf[CB_MEML+SUBL+2*LPC_FILTERORDER]; - float invenergy[CB_EXPAND*128], energy[CB_EXPAND*128]; - float *pp, *ppi=0, *ppo=0, *ppe=0; - float cbvectors[CB_MEML]; - float tene, cene, cvec[SUBL]; - float aug_vec[SUBL]; - - memset(cvec,0,SUBL*sizeof(float)); - - /* Determine size of codebook sections */ - - base_size=lMem-lTarget+1; - - if (lTarget==SUBL) { - base_size=lMem-lTarget+1+lTarget/2; - } - - /* setup buffer for weighting */ - - memcpy(buf,weightState,sizeof(float)*LPC_FILTERORDER); - memcpy(buf+LPC_FILTERORDER,mem,lMem*sizeof(float)); - memcpy(buf+LPC_FILTERORDER+lMem,intarget,lTarget*sizeof(float)); - - /* weighting */ - - AllPoleFilter(buf+LPC_FILTERORDER, weightDenum, - lMem+lTarget, LPC_FILTERORDER); - - /* Construct the codebook and target needed */ - - memcpy(target, buf+LPC_FILTERORDER+lMem, lTarget*sizeof(float)); - - tene=0.0; - - - -Andersen, et al. Experimental [Page 154] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - for (i=0; i0.0) { - invenergy[0] = (float) 1.0 / (*ppe + EPS); - } else { - invenergy[0] = (float) 0.0; - - - -Andersen, et al. Experimental [Page 155] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - ppe++; - - measure=(float)-10000000.0; - - if (crossDot > 0.0) { - measure = crossDot*crossDot*invenergy[0]; - } - } - else { - measure = crossDot*crossDot*invenergy[0]; - } - - /* check if measure is better */ - ftmp = crossDot*invenergy[0]; - - if ((measure>max_measure) && (fabs(ftmp)0.0) { - invenergy[icount] = - (float)1.0/(energy[icount]+EPS); - } else { - invenergy[icount] = (float) 0.0; - } - - - -Andersen, et al. Experimental [Page 156] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - measure=(float)-10000000.0; - - if (crossDot > 0.0) { - measure = crossDot*crossDot*invenergy[icount]; - } - } - else { - measure = crossDot*crossDot*invenergy[icount]; - } - - /* check if measure is better */ - ftmp = crossDot*invenergy[icount]; - - if ((measure>max_measure) && (fabs(ftmp) range) { - sInd -= (eInd-range); - eInd = range; - } - } else { /* base_index >= (base_size-20) */ - - if (sInd < (base_size-20)) { - sIndAug = 20; - sInd = 0; - eInd = 0; - eIndAug = 19 + CB_RESRANGE; - - if(eIndAug > 39) { - eInd = eIndAug-39; - eIndAug = 39; - } - } else { - sIndAug = 20 + sInd - (base_size-20); - eIndAug = 39; - sInd = 0; - eInd = CB_RESRANGE - (eIndAug-sIndAug+1); - } - } - - } else { /* lTarget = 22 or 23 */ - - if (sInd < 0) { - eInd -= sInd; - - - -Andersen, et al. Experimental [Page 158] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - sInd = 0; - } - - if(eInd > range) { - sInd -= (eInd - range); - eInd = range; - } - } - } - - /* search of higher codebook section */ - - /* index search range */ - counter = sInd; - sInd += base_size; - eInd += base_size; - - - if (stage==0) { - ppe = energy+base_size; - *ppe=0.0; - - pp=cbvectors+lMem-lTarget; - for (j=0; j0.0) { - invenergy[icount] =(float)1.0/(energy[icount]+EPS); - } else { - invenergy[icount] =(float)0.0; - } - - if (stage==0) { - - measure=(float)-10000000.0; - - if (crossDot > 0.0) { - measure = crossDot*crossDot* - invenergy[icount]; - } - } - else { - measure = crossDot*crossDot*invenergy[icount]; - } - - /* check if measure is better */ - ftmp = crossDot*invenergy[icount]; - - if ((measure>max_measure) && (fabs(ftmp)CB_MAXGAIN) { - gain = (float)CB_MAXGAIN; - } - gain = gainquant(gain, 1.0, 32, &gain_index[stage]); - } - else { - if (stage==1) { - gain = gainquant(gain, (float)fabs(gains[stage-1]), - 16, &gain_index[stage]); - } else { - gain = gainquant(gain, (float)fabs(gains[stage-1]), - 8, &gain_index[stage]); - } - } - - /* Extract the best (according to measure) - codebook vector */ - - if (lTarget==(STATE_LEN-iLBCenc_inst->state_short_len)) { - - if (index[stage] - #include - - #include "helpfun.h" - #include "lsf.h" - #include "iLBC_define.h" - #include "constants.h" - - /*---------------------------------------------------------------* - * interpolation of lsf coefficients for the decoder - *--------------------------------------------------------------*/ - - void LSFinterpolate2a_dec( - float *a, /* (o) lpc coefficients for a sub-frame */ - float *lsf1, /* (i) first lsf coefficient vector */ - float *lsf2, /* (i) second lsf coefficient vector */ - float coef, /* (i) interpolation weight */ - int length /* (i) length of lsf vectors */ - ){ - float lsftmp[LPC_FILTERORDER]; - - interpolate(lsftmp, lsf1, lsf2, coef, length); - lsf2a(a, lsftmp); - } - - /*---------------------------------------------------------------* - * obtain dequantized lsf coefficients from quantization index - *--------------------------------------------------------------*/ - - void SimplelsfDEQ( - float *lsfdeq, /* (o) dequantized lsf coefficients */ - int *index, /* (i) quantization index */ - int lpc_n /* (i) number of LPCs */ - ){ - int i, j, pos, cb_pos; - - - -Andersen, et al. Experimental [Page 164] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* decode first LSF */ - - pos = 0; - cb_pos = 0; - for (i = 0; i < LSF_NSPLIT; i++) { - for (j = 0; j < dim_lsfCbTbl[i]; j++) { - lsfdeq[pos + j] = lsfCbTbl[cb_pos + - (long)(index[i])*dim_lsfCbTbl[i] + j]; - } - pos += dim_lsfCbTbl[i]; - cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i]; - } - - if (lpc_n>1) { - - /* decode last LSF */ - - pos = 0; - cb_pos = 0; - for (i = 0; i < LSF_NSPLIT; i++) { - for (j = 0; j < dim_lsfCbTbl[i]; j++) { - lsfdeq[LPC_FILTERORDER + pos + j] = - lsfCbTbl[cb_pos + - (long)(index[LSF_NSPLIT + i])* - dim_lsfCbTbl[i] + j]; - } - pos += dim_lsfCbTbl[i]; - cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i]; - } - } - } - - /*----------------------------------------------------------------* - * obtain synthesis and weighting filters form lsf coefficients - *---------------------------------------------------------------*/ - - void DecoderInterpolateLSF( - float *syntdenum, /* (o) synthesis filter coefficients */ - float *weightdenum, /* (o) weighting denumerator - coefficients */ - float *lsfdeq, /* (i) dequantized lsf coefficients */ - int length, /* (i) length of lsf coefficient vector */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i) the decoder state structure */ - ){ - int i, pos, lp_length; - float lp[LPC_FILTERORDER + 1], *lsfdeq2; - - - - -Andersen, et al. Experimental [Page 165] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - lsfdeq2 = lsfdeq + length; - lp_length = length + 1; - - if (iLBCdec_inst->mode==30) { - /* sub-frame 1: Interpolation between old and first */ - - LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, lsfdeq, - lsf_weightTbl_30ms[0], length); - memcpy(syntdenum,lp,lp_length*sizeof(float)); - bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, - lp_length); - - /* sub-frames 2 to 6: interpolation between first - and last LSF */ - - pos = lp_length; - for (i = 1; i < 6; i++) { - LSFinterpolate2a_dec(lp, lsfdeq, lsfdeq2, - lsf_weightTbl_30ms[i], length); - memcpy(syntdenum + pos,lp,lp_length*sizeof(float)); - bwexpand(weightdenum + pos, lp, - LPC_CHIRP_WEIGHTDENUM, lp_length); - pos += lp_length; - } - } - else { - pos = 0; - for (i = 0; i < iLBCdec_inst->nsub; i++) { - LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, - lsfdeq, lsf_weightTbl_20ms[i], length); - memcpy(syntdenum+pos,lp,lp_length*sizeof(float)); - bwexpand(weightdenum+pos, lp, LPC_CHIRP_WEIGHTDENUM, - lp_length); - pos += lp_length; - } - } - - /* update memory */ - - if (iLBCdec_inst->mode==30) - memcpy(iLBCdec_inst->lsfdeqold, lsfdeq2, - length*sizeof(float)); - else - memcpy(iLBCdec_inst->lsfdeqold, lsfdeq, - length*sizeof(float)); - - } - - - - -Andersen, et al. Experimental [Page 166] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - -A.37. LPCencode.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - LPCencode.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_LPCENCOD_H - #define __iLBC_LPCENCOD_H - - void LPCencode( - float *syntdenum, /* (i/o) synthesis filter coefficients - before/after encoding */ - float *weightdenum, /* (i/o) weighting denumerator coefficients - before/after encoding */ - int *lsf_index, /* (o) lsf quantization index */ - float *data, /* (i) lsf coefficients to quantize */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ); - - #endif - -A.38. LPCencode.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - LPCencode.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - - #include "iLBC_define.h" - #include "helpfun.h" - #include "lsf.h" - #include "constants.h" - - - -Andersen, et al. Experimental [Page 167] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /*----------------------------------------------------------------* - * lpc analysis (subrutine to LPCencode) - *---------------------------------------------------------------*/ - - void SimpleAnalysis( - float *lsf, /* (o) lsf coefficients */ - float *data, /* (i) new data vector */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ){ - int k, is; - float temp[BLOCKL_MAX], lp[LPC_FILTERORDER + 1]; - float lp2[LPC_FILTERORDER + 1]; - float r[LPC_FILTERORDER + 1]; - - is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; - memcpy(iLBCenc_inst->lpc_buffer+is,data, - iLBCenc_inst->blockl*sizeof(float)); - - /* No lookahead, last window is asymmetric */ - - for (k = 0; k < iLBCenc_inst->lpc_n; k++) { - - is = LPC_LOOKBACK; - - if (k < (iLBCenc_inst->lpc_n - 1)) { - window(temp, lpc_winTbl, - iLBCenc_inst->lpc_buffer, BLOCKL_MAX); - } else { - window(temp, lpc_asymwinTbl, - iLBCenc_inst->lpc_buffer + is, BLOCKL_MAX); - } - - autocorr(r, temp, BLOCKL_MAX, LPC_FILTERORDER); - window(r, r, lpc_lagwinTbl, LPC_FILTERORDER + 1); - - levdurb(lp, temp, r, LPC_FILTERORDER); - bwexpand(lp2, lp, LPC_CHIRP_SYNTDENUM, LPC_FILTERORDER+1); - - a2lsf(lsf + k*LPC_FILTERORDER, lp2); - } - is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; - memmove(iLBCenc_inst->lpc_buffer, - iLBCenc_inst->lpc_buffer+LPC_LOOKBACK+BLOCKL_MAX-is, - is*sizeof(float)); - } - - /*----------------------------------------------------------------* - - - -Andersen, et al. Experimental [Page 168] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - * lsf interpolator and conversion from lsf to a coefficients - * (subrutine to SimpleInterpolateLSF) - *---------------------------------------------------------------*/ - - void LSFinterpolate2a_enc( - float *a, /* (o) lpc coefficients */ - float *lsf1,/* (i) first set of lsf coefficients */ - float *lsf2,/* (i) second set of lsf coefficients */ - float coef, /* (i) weighting coefficient to use between - lsf1 and lsf2 */ - long length /* (i) length of coefficient vectors */ - ){ - float lsftmp[LPC_FILTERORDER]; - - interpolate(lsftmp, lsf1, lsf2, coef, length); - lsf2a(a, lsftmp); - } - - /*----------------------------------------------------------------* - * lsf interpolator (subrutine to LPCencode) - *---------------------------------------------------------------*/ - - void SimpleInterpolateLSF( - float *syntdenum, /* (o) the synthesis filter denominator - resulting from the quantized - interpolated lsf */ - float *weightdenum, /* (o) the weighting filter denominator - resulting from the unquantized - interpolated lsf */ - float *lsf, /* (i) the unquantized lsf coefficients */ - float *lsfdeq, /* (i) the dequantized lsf coefficients */ - float *lsfold, /* (i) the unquantized lsf coefficients of - the previous signal frame */ - float *lsfdeqold, /* (i) the dequantized lsf coefficients of - the previous signal frame */ - int length, /* (i) should equate LPC_FILTERORDER */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ){ - int i, pos, lp_length; - float lp[LPC_FILTERORDER + 1], *lsf2, *lsfdeq2; - - lsf2 = lsf + length; - lsfdeq2 = lsfdeq + length; - lp_length = length + 1; - - if (iLBCenc_inst->mode==30) { - /* sub-frame 1: Interpolation between old and first - - - -Andersen, et al. Experimental [Page 169] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - set of lsf coefficients */ - - LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq, - lsf_weightTbl_30ms[0], length); - memcpy(syntdenum,lp,lp_length*sizeof(float)); - LSFinterpolate2a_enc(lp, lsfold, lsf, - lsf_weightTbl_30ms[0], length); - bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, lp_length); - - /* sub-frame 2 to 6: Interpolation between first - and second set of lsf coefficients */ - - pos = lp_length; - for (i = 1; i < iLBCenc_inst->nsub; i++) { - LSFinterpolate2a_enc(lp, lsfdeq, lsfdeq2, - lsf_weightTbl_30ms[i], length); - memcpy(syntdenum + pos,lp,lp_length*sizeof(float)); - - LSFinterpolate2a_enc(lp, lsf, lsf2, - lsf_weightTbl_30ms[i], length); - bwexpand(weightdenum + pos, lp, - LPC_CHIRP_WEIGHTDENUM, lp_length); - pos += lp_length; - } - } - else { - pos = 0; - for (i = 0; i < iLBCenc_inst->nsub; i++) { - LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq, - lsf_weightTbl_20ms[i], length); - memcpy(syntdenum+pos,lp,lp_length*sizeof(float)); - LSFinterpolate2a_enc(lp, lsfold, lsf, - lsf_weightTbl_20ms[i], length); - bwexpand(weightdenum+pos, lp, - LPC_CHIRP_WEIGHTDENUM, lp_length); - pos += lp_length; - } - } - - /* update memory */ - - if (iLBCenc_inst->mode==30) { - memcpy(lsfold, lsf2, length*sizeof(float)); - memcpy(lsfdeqold, lsfdeq2, length*sizeof(float)); - } - else { - memcpy(lsfold, lsf, length*sizeof(float)); - memcpy(lsfdeqold, lsfdeq, length*sizeof(float)); - - - -Andersen, et al. Experimental [Page 170] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - } - } - - /*----------------------------------------------------------------* - * lsf quantizer (subrutine to LPCencode) - *---------------------------------------------------------------*/ - - void SimplelsfQ( - float *lsfdeq, /* (o) dequantized lsf coefficients - (dimension FILTERORDER) */ - int *index, /* (o) quantization index */ - float *lsf, /* (i) the lsf coefficient vector to be - quantized (dimension FILTERORDER ) */ - int lpc_n /* (i) number of lsf sets to quantize */ - ){ - /* Quantize first LSF with memoryless split VQ */ - SplitVQ(lsfdeq, index, lsf, lsfCbTbl, LSF_NSPLIT, - dim_lsfCbTbl, size_lsfCbTbl); - - if (lpc_n==2) { - /* Quantize second LSF with memoryless split VQ */ - SplitVQ(lsfdeq + LPC_FILTERORDER, index + LSF_NSPLIT, - lsf + LPC_FILTERORDER, lsfCbTbl, LSF_NSPLIT, - dim_lsfCbTbl, size_lsfCbTbl); - } - } - - /*----------------------------------------------------------------* - * lpc encoder - *---------------------------------------------------------------*/ - - void LPCencode( - float *syntdenum, /* (i/o) synthesis filter coefficients - before/after encoding */ - float *weightdenum, /* (i/o) weighting denumerator - coefficients before/after - encoding */ - int *lsf_index, /* (o) lsf quantization index */ - float *data, /* (i) lsf coefficients to quantize */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ){ - float lsf[LPC_FILTERORDER * LPC_N_MAX]; - float lsfdeq[LPC_FILTERORDER * LPC_N_MAX]; - int change=0; - - SimpleAnalysis(lsf, data, iLBCenc_inst); - SimplelsfQ(lsfdeq, lsf_index, lsf, iLBCenc_inst->lpc_n); - - - -Andersen, et al. Experimental [Page 171] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - change=LSF_check(lsfdeq, LPC_FILTERORDER, iLBCenc_inst->lpc_n); - SimpleInterpolateLSF(syntdenum, weightdenum, - lsf, lsfdeq, iLBCenc_inst->lsfold, - iLBCenc_inst->lsfdeqold, LPC_FILTERORDER, iLBCenc_inst); - } - -A.39. lsf.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - lsf.h - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_LSF_H - #define __iLBC_LSF_H - - void a2lsf( - float *freq,/* (o) lsf coefficients */ - float *a /* (i) lpc coefficients */ - ); - - void lsf2a( - float *a_coef, /* (o) lpc coefficients */ - float *freq /* (i) lsf coefficients */ - ); - - #endif - -A.40. lsf.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - lsf.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - - - -Andersen, et al. Experimental [Page 172] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - #include - - #include "iLBC_define.h" - - /*----------------------------------------------------------------* - * conversion from lpc coefficients to lsf coefficients - *---------------------------------------------------------------*/ - - void a2lsf( - float *freq,/* (o) lsf coefficients */ - float *a /* (i) lpc coefficients */ - ){ - float steps[LSF_NUMBER_OF_STEPS] = - {(float)0.00635, (float)0.003175, (float)0.0015875, - (float)0.00079375}; - float step; - int step_idx; - int lsp_index; - float p[LPC_HALFORDER]; - float q[LPC_HALFORDER]; - float p_pre[LPC_HALFORDER]; - float q_pre[LPC_HALFORDER]; - float old_p, old_q, *old; - float *pq_coef; - float omega, old_omega; - int i; - float hlp, hlp1, hlp2, hlp3, hlp4, hlp5; - - for (i=0; i= 0.5)){ - - if (step_idx == (LSF_NUMBER_OF_STEPS - 1)){ - - if (fabs(hlp5) >= fabs(*old)) { - freq[lsp_index] = omega - step; - } else { - freq[lsp_index] = omega; - } - - - -Andersen, et al. Experimental [Page 174] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - - - if ((*old) >= 0.0){ - *old = (float)-1.0 * FLOAT_MAX; - } else { - *old = FLOAT_MAX; - } - - omega = old_omega; - step_idx = 0; - - step_idx = LSF_NUMBER_OF_STEPS; - } else { - - if (step_idx == 0) { - old_omega = omega; - } - - step_idx++; - omega -= steps[step_idx]; - - /* Go back one grid step */ - - step = steps[step_idx]; - } - } else { - - /* increment omega until they are of different sign, - and we know there is at least one root between omega - and old_omega */ - *old = hlp5; - omega += step; - } - } - } - - for (i = 0; i= 0.5)){ - - - if (freq[0] <= 0.0) { - freq[0] = (float)0.022; - } - - - if (freq[LPC_FILTERORDER - 1] >= 0.5) { - freq[LPC_FILTERORDER - 1] = (float)0.499; - } - - hlp = (freq[LPC_FILTERORDER - 1] - freq[0]) / - (float) (LPC_FILTERORDER - 1); - - for (i=1; i - #include - - #include "iLBC_define.h" - #include "constants.h" - #include "helpfun.h" - #include "string.h" - - /*----------------------------------------------------------------* - * splitting an integer into first most significant bits and - * remaining least significant bits - *---------------------------------------------------------------*/ - - void packsplit( - int *index, /* (i) the value to split */ - int *firstpart, /* (o) the value specified by most - significant bits */ - int *rest, /* (o) the value specified by least - significant bits */ - - - -Andersen, et al. Experimental [Page 179] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - int bitno_firstpart, /* (i) number of bits in most - significant part */ - int bitno_total /* (i) number of bits in full range - of value */ - ){ - int bitno_rest = bitno_total-bitno_firstpart; - - *firstpart = *index>>(bitno_rest); - *rest = *index-(*firstpart<<(bitno_rest)); - } - - /*----------------------------------------------------------------* - * combining a value corresponding to msb's with a value - * corresponding to lsb's - *---------------------------------------------------------------*/ - - void packcombine( - int *index, /* (i/o) the msb value in the - combined value out */ - int rest, /* (i) the lsb value */ - int bitno_rest /* (i) the number of bits in the - lsb part */ - ){ - *index = *index<0) { - - /* Jump to the next byte if end of this byte is reached*/ - - if (*pos==8) { - *pos=0; - (*bitstream)++; - **bitstream=0; - } - - posLeft=8-(*pos); - - /* Insert index into the bitstream */ - - if (bitno <= posLeft) { - **bitstream |= (unsigned char)(index<<(posLeft-bitno)); - *pos+=bitno; - bitno=0; - } else { - **bitstream |= (unsigned char)(index>>(bitno-posLeft)); - - *pos=8; - index-=((index>>(bitno-posLeft))<<(bitno-posLeft)); - - bitno-=posLeft; - } - } - } - - /*----------------------------------------------------------------* - * unpacking of bits from bitstream, i.e., vector of bytes - *---------------------------------------------------------------*/ - - void unpack( - unsigned char **bitstream, /* (i/o) on entrance pointer to - place in bitstream to - unpack new data from, on - exit pointer to place in - bitstream to unpack future - data from */ - int *index, /* (o) resulting value */ - int bitno, /* (i) number of bits used to - represent the value */ - int *pos /* (i/o) read position in the - current byte */ - - - -Andersen, et al. Experimental [Page 181] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - ){ - int BitsLeft; - - *index=0; - - while (bitno>0) { - - /* move forward in bitstream when the end of the - byte is reached */ - - if (*pos==8) { - *pos=0; - (*bitstream)++; - } - - BitsLeft=8-(*pos); - - /* Extract bits to index */ - - if (BitsLeft>=bitno) { - *index+=((((**bitstream)<<(*pos)) & 0xFF)>>(8-bitno)); - - *pos+=bitno; - bitno=0; - } else { - - if ((8-bitno)>0) { - *index+=((((**bitstream)<<(*pos)) & 0xFF)>> - (8-bitno)); - *pos=8; - } else { - *index+=(((int)(((**bitstream)<<(*pos)) & 0xFF))<< - (bitno-8)); - *pos=8; - } - bitno-=BitsLeft; - } - } - } - -A.43. StateConstructW.h - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - StateConstructW.h - - - - -Andersen, et al. Experimental [Page 182] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #ifndef __iLBC_STATECONSTRUCTW_H - #define __iLBC_STATECONSTRUCTW_H - - void StateConstructW( - int idxForMax, /* (i) 6-bit index for the quantization of - max amplitude */ - int *idxVec, /* (i) vector of quantization indexes */ - float *syntDenum, /* (i) synthesis filter denumerator */ - float *out, /* (o) the decoded state vector */ - int len /* (i) length of a state vector */ - ); - - #endif - -A.44. StateConstructW.c - - /****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - StateConstructW.c - - Copyright (C) The Internet Society (2004). - All Rights Reserved. - - ******************************************************************/ - - #include - #include - - #include "iLBC_define.h" - #include "constants.h" - #include "filter.h" - - /*----------------------------------------------------------------* - * decoding of the start state - *---------------------------------------------------------------*/ - - void StateConstructW( - int idxForMax, /* (i) 6-bit index for the quantization of - max amplitude */ - int *idxVec, /* (i) vector of quantization indexes */ - float *syntDenum, /* (i) synthesis filter denumerator */ - - - -Andersen, et al. Experimental [Page 183] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - float *out, /* (o) the decoded state vector */ - int len /* (i) length of a state vector */ - ){ - float maxVal, tmpbuf[LPC_FILTERORDER+2*STATE_LEN], *tmp, - numerator[LPC_FILTERORDER+1]; - float foutbuf[LPC_FILTERORDER+2*STATE_LEN], *fout; - int k,tmpi; - - /* decoding of the maximum value */ - - maxVal = state_frgqTbl[idxForMax]; - maxVal = (float)pow(10,maxVal)/(float)4.5; - - /* initialization of buffers and coefficients */ - - memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float)); - memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float)); - for (k=0; k - #include - - #include "iLBC_define.h" - #include "constants.h" - #include "filter.h" - #include "helpfun.h" - - /*----------------------------------------------------------------* - * predictive noise shaping encoding of scaled start state - * (subrutine for StateSearchW) - *---------------------------------------------------------------*/ - - void AbsQuantW( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) Encoder instance */ - float *in, /* (i) vector to encode */ - float *syntDenum, /* (i) denominator of synthesis filter */ - float *weightDenum, /* (i) denominator of weighting filter */ - int *out, /* (o) vector of quantizer indexes */ - int len, /* (i) length of vector to encode and - vector of quantizer indexes */ - int state_first /* (i) position of start state in the - 80 vec */ - ){ - float *syntOut; - float syntOutBuf[LPC_FILTERORDER+STATE_SHORT_LEN_30MS]; - float toQ, xq; - int n; - int index; - - /* initialization of buffer for filtering */ - - memset(syntOutBuf, 0, LPC_FILTERORDER*sizeof(float)); - - - - -Andersen, et al. Experimental [Page 186] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - /* initialization of pointer for filtering */ - - syntOut = &syntOutBuf[LPC_FILTERORDER]; - - /* synthesis and weighting filters on input */ - - if (state_first) { - AllPoleFilter (in, weightDenum, SUBL, LPC_FILTERORDER); - } else { - AllPoleFilter (in, weightDenum, - iLBCenc_inst->state_short_len-SUBL, - LPC_FILTERORDER); - } - - /* encoding loop */ - - for (n=0; nstate_short_len-SUBL))) { - syntDenum += (LPC_FILTERORDER+1); - weightDenum += (LPC_FILTERORDER+1); - - /* synthesis and weighting filters on input */ - AllPoleFilter (&in[n], weightDenum, len-n, - LPC_FILTERORDER); - - } - - /* prediction of synthesized and weighted input */ - - syntOut[n] = 0.0; - AllPoleFilter (&syntOut[n], weightDenum, 1, - LPC_FILTERORDER); - - /* quantization */ - - toQ = in[n]-syntOut[n]; - - - -Andersen, et al. Experimental [Page 187] - -RFC 3951 Internet Low Bit Rate Codec December 2004 - - - sort_sq(&xq, &index, toQ, state_sq3Tbl, 8); - out[n]=index; - syntOut[n] = state_sq3Tbl[out[n]]; - - /* update of the prediction filter */ - - AllPoleFilter(&syntOut[n], weightDenum, 1, - LPC_FILTERORDER); - } - } - - /*----------------------------------------------------------------* - * encoding of start state - *---------------------------------------------------------------*/ - - void StateSearchW( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) Encoder instance */ - float *residual,/* (i) target residual vector */ - float *syntDenum, /* (i) lpc synthesis filter */ - float *weightDenum, /* (i) weighting filter denuminator */ - int *idxForMax, /* (o) quantizer index for maximum - amplitude */ - int *idxVec, /* (o) vector of quantization indexes */ - int len, /* (i) length of all vectors */ - int state_first /* (i) position of start state in the - 80 vec */ - ){ - float dtmp, maxVal; - float tmpbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS]; - float *tmp, numerator[1+LPC_FILTERORDER]; - float foutbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS], *fout; - int k; - float qmax, scal; - - /* initialization of buffers and filter coefficients */ - - memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float)); - memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float)); - for (k=0; k maxVal*maxVal){ - maxVal = fout[k]; - } - } - maxVal=(float)fabs(maxVal); - - /* encoding of the maximum amplitude value */ - - if (maxVal < 10.0) { - maxVal = 10.0; - } - maxVal = (float)log10(maxVal); - sort_sq(&dtmp, idxForMax, maxVal, state_frgqTbl, 64); - - /* decoding of the maximum amplitude representation value, - and corresponding scaling of start state */ - - maxVal=state_frgqTbl[*idxForMax]; - qmax = (float)pow(10,maxVal); - scal = (float)(4.5)/qmax; - for (k=0; k | - | 20 ms frame | 30 ms frame | - ----------------------------------+---------------+---------------+ - Split 1 | 6 <6,0,0> | 6 <6,0,0> | - LSF 1 Split 2 | 7 <7,0,0> | 7 <7,0,0> | - LSF Split 3 | 7 <7,0,0> | 7 <7,0,0> | - ------------------+---------------+---------------+ - Split 1 | NA (Not Appl.)| 6 <6,0,0> | - LSF 2 Split 2 | NA | 7 <7,0,0> | - Split 3 | NA | 7 <7,0,0> | - ------------------+---------------+---------------+ - Sum | 20 <20,0,0> | 40 <40,0,0> | - ----------------------------------+---------------+---------------+ - Block Class. | 2 <2,0,0> | 3 <3,0,0> | - ----------------------------------+---------------+---------------+ - Position 22 sample segment | 1 <1,0,0> | 1 <1,0,0> | - ----------------------------------+---------------+---------------+ - Scale Factor State Coder | 6 <6,0,0> | 6 <6,0,0> | - ----------------------------------+---------------+---------------+ - Sample 0 | 3 <0,1,2> | 3 <0,1,2> | - Quantized Sample 1 | 3 <0,1,2> | 3 <0,1,2> | - Residual : | : : | : : | - State : | : : | : : | - Samples : | : : | : : | - Sample 56 | 3 <0,1,2> | 3 <0,1,2> | - Sample 57 | NA | 3 <0,1,2> | - ------------------+---------------+---------------+ - Sum | 171 <0,57,114>| 174 <0,58,116>| - ----------------------------------+---------------+---------------+ - Stage 1 | 7 <6,0,1> | 7 <4,2,1> | - CB for 22/23 Stage 2 | 7 <0,0,7> | 7 <0,0,7> | - sample block Stage 3 | 7 <0,0,7> | 7 <0,0,7> | - ------------------+---------------+---------------+ - Sum | 21 <6,0,15> | 21 <4,2,15> | - ----------------------------------+---------------+---------------+ - Stage 1 | 5 <2,0,3> | 5 <1,1,3> | - Gain for 22/23 Stage 2 | 4 <1,1,2> | 4 <1,1,2> | - sample block Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - ------------------+---------------+---------------+ - Sum | 12 <3,1,8> | 12 <2,2,8> | - ----------------------------------+---------------+---------------+ - Stage 1 | 8 <7,0,1> | 8 <6,1,1> | - sub-block 1 Stage 2 | 7 <0,0,7> | 7 <0,0,7> | - Stage 3 | 7 <0,0,7> | 7 <0,0,7> | - ------------------+---------------+---------------+ - - - -Duric & Andersen Experimental [Page 5] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - Stage 1 | 8 <0,0,8> | 8 <0,7,1> | - sub-block 2 Stage 2 | 8 <0,0,8> | 8 <0,0,8> | - Indices Stage 3 | 8 <0,0,8> | 8 <0,0,8> | - for CB ------------------+---------------+---------------+ - sub-blocks Stage 1 | NA | 8 <0,7,1> | - sub-block 3 Stage 2 | NA | 8 <0,0,8> | - Stage 3 | NA | 8 <0,0,8> | - ------------------+---------------+---------------+ - Stage 1 | NA | 8 <0,7,1> | - sub-block 4 Stage 2 | NA | 8 <0,0,8> | - Stage 3 | NA | 8 <0,0,8> | - ------------------+---------------+---------------+ - Sum | 46 <7,0,39> | 94 <6,22,66> | - ----------------------------------+---------------+---------------+ - Stage 1 | 5 <1,2,2> | 5 <1,2,2> | - sub-block 1 Stage 2 | 4 <1,1,2> | 4 <1,2,1> | - Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - ------------------+---------------+---------------+ - Stage 1 | 5 <1,1,3> | 5 <0,2,3> | - sub-block 2 Stage 2 | 4 <0,2,2> | 4 <0,2,2> | - Stage 3 | 3 <0,0,3> | 3 <0,0,3> | - Gains for ------------------+---------------+---------------+ - sub-blocks Stage 1 | NA | 5 <0,1,4> | - sub-block 3 Stage 2 | NA | 4 <0,1,3> | - Stage 3 | NA | 3 <0,0,3> | - ------------------+---------------+---------------+ - Stage 1 | NA | 5 <0,1,4> | - sub-block 4 Stage 2 | NA | 4 <0,1,3> | - Stage 3 | NA | 3 <0,0,3> | - ------------------+---------------+---------------+ - Sum | 24 <3,6,15> | 48 <2,12,34> | - ------------------------------------------------------------------- - Empty frame indicator | 1 <0,0,1> | 1 <0,0,1> | - ------------------------------------------------------------------- - SUM 304 <48,64,192> 400 <64,96,240> - - Table 3.1 The bitstream definition for iLBC. - - When packetized into the payload, all the class 1 bits MUST be sorted - in order (from top and down) as they were specified in the table. - Additionally, all the class 2 bits MUST be sorted (from top and down) - and all the class 3 bits MUST be sorted in the same sequential order. - -3.2. Multiple iLBC frames in a RTP packet - - More than one iLBC frame may be included in a single RTP packet by a - sender. - - - - -Duric & Andersen Experimental [Page 6] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - It is important to observe that senders have the following additional - restrictions: - - o SHOULD NOT include more iLBC frames in a single RTP packet than - will fit in the MTU of the RTP transport protocol. - - o Frames MUST NOT be split between RTP packets. - - o Frames of the different modes (20 ms and 30 ms) MUST NOT be - included within the same packet. - - It is RECOMMENDED that the number of frames contained within an RTP - packet are consistent with the application. For example, in - telephony and other real time applications where delay is important, - the delay is lower depending on the amount of frames per packet - (i.e., fewer frames per packet, the lower the delay). Whereas for - bandwidth constrained links or delay insensitive streaming messaging - application, one or more frames per packet would be acceptable. - - Information describing the number of frames contained in an RTP - packet is not transmitted as part of the RTP payload. The way to - determine the number of iLBC frames is to count the total number of - octets within the RTP packet, and divide the octet count by the - number of expected octets per frame (32/50 per frame). - -4. IANA Considerations - - One new MIME sub-type as described in this section has been - registered. - -4.1. Storage Mode - - The storage mode is used for storing speech frames (e.g., as a file - or email attachment). - - +------------------+ - | Header | - +------------------+ - | Speech frame 1 | - +------------------+ - : : - +------------------+ - | Speech frame n | - +------------------+ - - Figure 2, Storage format diagram - - - - - -Duric & Andersen Experimental [Page 7] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - The file begins with a header that includes only a magic number to - identify that it is an iLBC file. - - The magic number for iLBC file MUST correspond to the ASCII character - string: - - o for 30 ms frame size mode:"#!iLBC30\n", or "0x23 0x21 0x69 - 0x4C 0x42 0x43 0x33 0x30 0x0A" in hexadecimal form, - - o for 20 ms frame size mode:"#!iLBC20\n", or "0x23 0x21 0x69 - 0x4C 0x42 0x43 0x32 0x30 0x0A" in hexadecimal form. - - After the header, follow the speech frames in consecutive order. - - Speech frames lost in transmission MUST be stored as "empty frames", - as defined in [1]. - -4.2. MIME Registration of iLBC - - MIME media type name: audio - - MIME subtype: iLBC - - Optional parameters: - - All of the parameters does apply for RTP transfer only. - - maxptime:The maximum amount of media which can be encapsulated in - each packet, expressed as time in milliseconds. The time - SHALL be calculated as the sum of the time the media present - in the packet represents. The time SHOULD be a multiple of - the frame size. This attribute is probably only meaningful - for audio data, but may be used with other media types if it - makes sense. It is a media attribute, and is not dependent - on charset. Note that this attribute was introduced after - RFC 2327, and non updated implementations will ignore this - attribute. - - mode: The iLBC operating frame mode (20 or 30 ms) that will be - encapsulated in each packet. Values can be 0, 20 and 30 - (where 0 is reserved, 20 stands for preferred 20 ms frame - size and 30 stands for preferred 30 ms frame size). - - ptime: Defined as usual for RTP audio (see [5]). - - Encoding considerations: - This type is defined for transfer via both RTP (RFC 3550) - and stored-file methods as described in Section 4.1, of RFC - - - -Duric & Andersen Experimental [Page 8] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - 3952. Audio data is binary data, and must be encoded for - non-binary transport; the Base64 encoding is suitable for - email. - - Security considerations: - See Section 6 of RFC 3952. - - Public specification: - Please refer to RFC 3951 [1]. - - Additional information: - The following applies to stored-file transfer methods: - - Magic number: - ASCII character string for: - o 30 ms frame size mode "#!iLBC30\n" (or 0x23 0x21 - 0x69 0x4C 0x42 0x43 0x33 0x30 0x0A in hexadecimal) - o 20 ms frame size mode "#!iLBC20\n" (or 0x23 0x21 - 0x69 0x4C 0x42 0x43 0x32 0x30 0x0A in hexadecimal) - - File extensions: lbc, LBC - Macintosh file type code: none - Object identifier or OID: none - - Person & email address to contact for further information: - alan.duric@telio.no - - Intended usage: COMMON. - It is expected that many VoIP applications will use this - type. - - Author/Change controller: - alan.duric@telio.no - IETF Audio/Video transport working group - -5. Mapping To SDP Parameters - - The information carried in the MIME media type specification has a - specific mapping to fields in the Session Description Protocol (SDP) - [5], which is commonly used to describe RTP sessions. When SDP is - used to specify sessions employing the iLBC codec, the mapping is as - follows: - - o The MIME type ("audio") goes in SDP "m=" as the media name. - - o The MIME subtype (payload format name) goes in SDP "a=rtpmap" as - the encoding name. - - - - -Duric & Andersen Experimental [Page 9] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - o The parameters "ptime" and "maxptime" go in the SDP "a=ptime" and - "a=maxptime" attributes, respectively. - - o The parameter "mode" goes in the SDP "a=fmtp" attribute by copying - it directly from the MIME media type string as "mode=value". - - When conveying information by SDP, the encoding name SHALL be "iLBC" - (the same as the MIME subtype). - - An example of the media representation in SDP for describing iLBC - might be: - - m=audio 49120 RTP/AVP 97 - a=rtpmap:97 iLBC/8000 - - If 20 ms frame size mode is used, remote iLBC encoder SHALL receive - "mode" parameter in the SDP "a=fmtp" attribute by copying them - directly from the MIME media type string as a semicolon separated - with parameter=value, where parameter is "mode", and values can be 0 - and 20 (where 0 is reserved and 20 stands for preferred 20 ms frame - size). An example of the media representation in SDP for describing - iLBC when 20 ms frame size mode is used might be: - - m=audio 49120 RTP/AVP 97 - a=rtpmap:97 iLBC/8000 - a=fmtp:97 mode=20 - - It is important to emphasize the bi-directional character of the - "mode" parameter - both sides of a bi-directional session MUST use - the same "mode" value. - - The offer contains the preferred mode of the offerer. The answerer - may agree to that mode by including the same mode in the answer, or - may include a different mode. The resulting mode used by both - parties SHALL be the lower of the bandwidth modes in the offer and - answer. - - That is, an offer of "mode=20" receiving an answer of "mode=30" will - result in "mode=30" being used by both participants. Similarly, an - offer of "mode=30" and an answer of "mode=20" will result in - "mode=30" being used by both participants. - - This is important when one end point utilizes a bandwidth constrained - link (e.g., 28.8k modem link or slower), where only the lower frame - size will work. - - - - - - -Duric & Andersen Experimental [Page 10] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - Parameter ptime can not be used for the purpose of specifying iLBC - operating mode, due to fact that for the certain values it will be - impossible to distinguish which mode is about to be used (e.g., when - ptime=60, it would be impossible to distinguish if packet is carrying - 2 frames of 30 ms or 3 frames of 20 ms, etc.). - - Note that the payload format (encoding) names are commonly shown in - upper case. MIME subtypes are commonly shown in lower case. These - names are case-insensitive in both places. Similarly, parameter - names are case-insensitive both in MIME types and in the default - mapping to the SDP a=fmtp attribute - -6. Security Considerations - - RTP packets using the payload format defined in this specification - are subject to the general security considerations discussed in [3] - and any appropriate profile (e.g., [4]). - - As this format transports encoded speech, the main security issues - include confidentiality and authentication of the speech itself. The - payload format itself does not have any built-in security mechanisms. - Confidentiality of the media streams is achieved by encryption, - therefore external mechanisms, such as SRTP [6], MAY be used for that - purpose. The data compression used with this payload format is - applied end-to-end; hence encryption may be performed after - compression with no conflict between the two operations. - - A potential denial-of-service threat exists for data encoding using - compression techniques that have non-uniform receiver-end - computational load. The attacker can inject pathological datagrams - into the stream which are complex to decode and cause the receiver to - become overloaded. However, the encodings covered in this document - do not exhibit any significant non-uniformity. - -7. References - -7.1. Normative References - - [1] Andersen, S., Duric, A., Astrom, H., Hagen, R., Kleijn, W., and - J. Linden, "Internet Low Bit Rate Codec (iLBC)", RFC 3951, - December 2004. - - [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement - Levels", BCP 14, RFC 2119, March 1997. - - [3] Schulzrinne, H., Casner, S., Frederick, R., and V. Jacobson, - "RTP: A Transport Protocol for Real-Time Applications", STD 64, - RFC 3550, July 2003. - - - -Duric & Andersen Experimental [Page 11] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - - [4] Schulzrinne, H. and S. Casner, "RTP Profile for Audio and Video - Conferences with Minimal Control", STD 65, RFC 3551, July 2003. - - [5] Handley, M. and V. Jacobson, "SDP: Session Description - Protocol", RFC 2327, April 1998. - - [6] Baugher, M., McGrew, D., Naslund, M., Carrara, E., and K. - Norrman, "The Secure Real-time Transport Protocol", RFC 3711, - March 2004. - -7.2. Informative References - - [7] ITU-T Recommendation G.711, available online from the ITU - bookstore at http://www.itu.int. - -8. Acknowledgements - - Henry Sinnreich, Patrik Faltstrom, Alan Johnston and Jean-Francois - Mule for great support of the iLBC initiative and for valuable - feedback and comments. - -Authors' Addresses - - Alan Duric - Telio AS - Stoperigt. 2 - Oslo, N-0250 - Norway - - Phone: +47 21673505 - EMail: alan.duric@telio.no - - - Soren Vang Andersen - Department of Communication Technology - Aalborg University - Fredrik Bajers Vej 7A - 9200 Aalborg - Denmark - - Phone: ++45 9 6358627 - EMail: sva@kom.auc.dk - - - - - - - - - -Duric & Andersen Experimental [Page 12] - -RFC 3952 RTP Payload Format for iLBC Speech December 2004 - - -Full Copyright Statement - - Copyright (C) The Internet Society (2004). - - This document is subject to the rights, licenses and restrictions - contained in BCP 78, and except as set forth therein, the authors - retain all their rights. - - This document and the information contained herein are provided on an - "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS - OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET - ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, - INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE - INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - -Intellectual Property - - The IETF takes no position regarding the validity or scope of any - Intellectual Property Rights or other rights that might be claimed to - pertain to the implementation or use of the technology described in - this document or the extent to which any license under such rights - might or might not be available; nor does it represent that it has - made any independent effort to identify any such rights. Information - on the IETF's procedures with respect to rights in IETF Documents can - be found in BCP 78 and BCP 79. - - Copies of IPR disclosures made to the IETF Secretariat and any - assurances of licenses to be made available, or the result of an - attempt made to obtain a general license or permission for the use of - such proprietary rights by implementers or users of this - specification can be obtained from the IETF on-line IPR repository at - http://www.ietf.org/ipr. - - The IETF invites any interested party to bring to its attention any - copyrights, patents or patent applications, or other proprietary - rights that may cover technology that may be required to implement - this standard. Please address the information to the IETF at ietf- - ipr@ietf.org. - - -Acknowledgement - - Funding for the RFC Editor function is currently provided by the - Internet Society. - - - - - - -Duric & Andersen Experimental [Page 13] - diff --git a/modules/audio_coding/codecs/iLBC/main/interface/ilbc.h b/modules/audio_coding/codecs/iLBC/main/interface/ilbc.h deleted file mode 100644 index 919f722a2..000000000 --- a/modules/audio_coding/codecs/iLBC/main/interface/ilbc.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * ilbc.h - * - * This header file contains all of the API's for iLBC. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_INTERFACE_ILBC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_INTERFACE_ILBC_H_ - -/* - * Define the fixpoint numeric formats - */ - -#include "typedefs.h" - -/* - * Solution to support multiple instances - * Customer has to cast instance to proper type - */ - -typedef struct iLBC_encinst_t_ iLBC_encinst_t; - -typedef struct iLBC_decinst_t_ iLBC_decinst_t; - -/* - * Comfort noise constants - */ - -#define ILBC_SPEECH 1 -#define ILBC_CNG 2 - -#ifdef __cplusplus -extern "C" { -#endif - - /**************************************************************************** - * WebRtcIlbcfix_XxxAssign(...) - * - * These functions assigns the encoder/decoder instance to the specified - * memory location - * - * Input: - * - XXX_xxxinst : Pointer to created instance that should be - * assigned - * - ILBCXXX_inst_Addr : Pointer to the desired memory space - * - size : The size that this structure occupies (in Word16) - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_EncoderAssign(iLBC_encinst_t **iLBC_encinst, - WebRtc_Word16 *ILBCENC_inst_Addr, - WebRtc_Word16 *size); - WebRtc_Word16 WebRtcIlbcfix_DecoderAssign(iLBC_decinst_t **iLBC_decinst, - WebRtc_Word16 *ILBCDEC_inst_Addr, - WebRtc_Word16 *size); - - - /**************************************************************************** - * WebRtcIlbcfix_XxxAssign(...) - * - * These functions create a instance to the specified structure - * - * Input: - * - XXX_inst : Pointer to created instance that should be created - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_EncoderCreate(iLBC_encinst_t **iLBC_encinst); - WebRtc_Word16 WebRtcIlbcfix_DecoderCreate(iLBC_decinst_t **iLBC_decinst); - - /**************************************************************************** - * WebRtcIlbcfix_XxxFree(...) - * - * These functions frees the dynamic memory of a specified instance - * - * Input: - * - XXX_inst : Pointer to created instance that should be freed - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_EncoderFree(iLBC_encinst_t *iLBC_encinst); - WebRtc_Word16 WebRtcIlbcfix_DecoderFree(iLBC_decinst_t *iLBC_decinst); - - - /**************************************************************************** - * WebRtcIlbcfix_EncoderInit(...) - * - * This function initializes a iLBC instance - * - * Input: - * - iLBCenc_inst : iLBC instance, i.e. the user that should receive - * be initialized - * - frameLen : The frame length of the codec 20/30 (ms) - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_EncoderInit(iLBC_encinst_t *iLBCenc_inst, - WebRtc_Word16 frameLen); - - /**************************************************************************** - * WebRtcIlbcfix_Encode(...) - * - * This function encodes one iLBC frame. Input speech length has be a - * multiple of the frame length. - * - * Input: - * - iLBCenc_inst : iLBC instance, i.e. the user that should encode - * a package - * - speechIn : Input speech vector - * - len : Samples in speechIn (160, 240, 320 or 480) - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_Encode(iLBC_encinst_t *iLBCenc_inst, - WebRtc_Word16 *speechIn, - WebRtc_Word16 len, - WebRtc_Word16 *encoded); - - /**************************************************************************** - * WebRtcIlbcfix_DecoderInit(...) - * - * This function initializes a iLBC instance with either 20 or 30 ms frames - * Alternatively the WebRtcIlbcfix_DecoderInit_XXms can be used. Then it's - * not needed to specify the frame length with a variable. - * - * Input: - * - iLBC_decinst_t : iLBC instance, i.e. the user that should receive - * be initialized - * - frameLen : The frame length of the codec 20/30 (ms) - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_DecoderInit(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 frameLen); - WebRtc_Word16 WebRtcIlbcfix_DecoderInit20Ms(iLBC_decinst_t *iLBCdec_inst); - WebRtc_Word16 WebRtcIlbcfix_Decoderinit30Ms(iLBC_decinst_t *iLBCdec_inst); - - /**************************************************************************** - * WebRtcIlbcfix_Decode(...) - * - * This function decodes a packet with iLBC frame(s). Output speech length - * will be a multiple of 160 or 240 samples ((160 or 240)*frames/packet). - * - * Input: - * - iLBCdec_inst : iLBC instance, i.e. the user that should decode - * a packet - * - encoded : Encoded iLBC frame(s) - * - len : Bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - speechType : 1 normal, 2 CNG - * - * Return value : >0 - Samples in decoded vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_Decode(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16* encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - WebRtc_Word16 WebRtcIlbcfix_Decode20Ms(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - WebRtc_Word16 WebRtcIlbcfix_Decode30Ms(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - - /**************************************************************************** - * WebRtcIlbcfix_DecodePlc(...) - * - * This function conducts PLC for iLBC frame(s). Output speech length - * will be a multiple of 160 or 240 samples. - * - * Input: - * - iLBCdec_inst : iLBC instance, i.e. the user that should perform - * a PLC - * - noOfLostFrames : Number of PLC frames to produce - * - * Output: - * - decoded : The "decoded" vector - * - * Return value : >0 - Samples in decoded PLC vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_DecodePlc(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames); - - /**************************************************************************** - * WebRtcIlbcfix_NetEqPlc(...) - * - * This function updates the decoder when a packet loss has occured, but it - * does not produce any PLC data. Function can be used if another PLC method - * is used (i.e NetEq). - * - * Input: - * - iLBCdec_inst : iLBC instance that should be updated - * - noOfLostFrames : Number of lost frames - * - * Output: - * - decoded : The "decoded" vector (nothing in this case) - * - * Return value : >0 - Samples in decoded PLC vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIlbcfix_NetEqPlc(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames); - - /**************************************************************************** - * WebRtcIlbcfix_version(...) - * - * This function returns the version number of iLBC - * - * Output: - * - version : Version number of iLBC (maximum 20 char) - */ - - void WebRtcIlbcfix_version(WebRtc_Word8 *version); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/modules/audio_coding/codecs/iLBC/main/source/Android.mk b/modules/audio_coding/codecs/iLBC/main/source/Android.mk deleted file mode 100644 index e38a27dcb..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/Android.mk +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_ilbc -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := abs_quant.c \ - abs_quant_loop.c \ - augmented_cb_corr.c \ - bw_expand.c \ - cb_construct.c \ - cb_mem_energy.c \ - cb_mem_energy_augmentation.c \ - cb_mem_energy_calc.c \ - cb_search.c \ - cb_search_core.c \ - cb_update_best_index.c \ - chebyshev.c \ - comp_corr.c \ - constants.c \ - create_augmented_vec.c \ - decode.c \ - decode_residual.c \ - decoder_interpolate_lsf.c \ - do_plc.c \ - encode.c \ - energy_inverse.c \ - enh_upsample.c \ - enhancer.c \ - enhancer_interface.c \ - filtered_cb_vecs.c \ - frame_classify.c \ - gain_dequant.c \ - gain_quant.c \ - get_cd_vec.c \ - get_lsp_poly.c \ - get_sync_seq.c \ - hp_input.c \ - hp_output.c \ - ilbc.c \ - index_conv_dec.c \ - index_conv_enc.c \ - init_decode.c \ - init_encode.c \ - interpolate.c \ - interpolate_samples.c \ - lpc_encode.c \ - lsf_check.c \ - lsf_interpolate_to_poly_dec.c \ - lsf_interpolate_to_poly_enc.c \ - lsf_to_lsp.c \ - lsf_to_poly.c \ - lsp_to_lsf.c \ - my_corr.c \ - nearest_neighbor.c \ - pack_bits.c \ - poly_to_lsf.c \ - poly_to_lsp.c \ - refiner.c \ - simple_interpolate_lsf.c \ - simple_lpc_analysis.c \ - simple_lsf_dequant.c \ - simple_lsf_quant.c \ - smooth.c \ - smooth_out_data.c \ - sort_sq.c \ - split_vq.c \ - state_construct.c \ - state_search.c \ - swap_bytes.c \ - unpack_bits.c \ - vq3.c \ - vq4.c \ - window32_w32.c \ - xcorr_coef.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/iLBC/main/source/abs_quant.c b/modules/audio_coding/codecs/iLBC/main/source/abs_quant.c deleted file mode 100644 index 4a70c8b43..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/abs_quant.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AbsQuant.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "abs_quant_loop.h" - - -/*----------------------------------------------------------------* - * predictive noise shaping encoding of scaled start state - * (subrutine for WebRtcIlbcfix_StateSearch) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_AbsQuant( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) Encoder instance */ - iLBC_bits *iLBC_encbits, /* (i/o) Encoded bits (outputs idxForMax - and idxVec, uses state_first as - input) */ - WebRtc_Word16 *in, /* (i) vector to encode */ - WebRtc_Word16 *weightDenum /* (i) denominator of synthesis filter */ - ) { - WebRtc_Word16 *syntOut; - WebRtc_Word16 quantLen[2]; - - /* Stack based */ - WebRtc_Word16 syntOutBuf[LPC_FILTERORDER+STATE_SHORT_LEN_30MS]; - WebRtc_Word16 in_weightedVec[STATE_SHORT_LEN_30MS+LPC_FILTERORDER]; - WebRtc_Word16 *in_weighted = &in_weightedVec[LPC_FILTERORDER]; - - /* Initialize the buffers */ - WebRtcSpl_MemSetW16(syntOutBuf, 0, LPC_FILTERORDER+STATE_SHORT_LEN_30MS); - syntOut = &syntOutBuf[LPC_FILTERORDER]; - /* Start with zero state */ - WebRtcSpl_MemSetW16(in_weightedVec, 0, LPC_FILTERORDER); - - /* Perform the quantization loop in two sections of length quantLen[i], - where the perceptual weighting filter is updated at the subframe - border */ - - if (iLBC_encbits->state_first) { - quantLen[0]=SUBL; - quantLen[1]=iLBCenc_inst->state_short_len-SUBL; - } else { - quantLen[0]=iLBCenc_inst->state_short_len-SUBL; - quantLen[1]=SUBL; - } - - /* Calculate the weighted residual, switch perceptual weighting - filter at the subframe border */ - WebRtcSpl_FilterARFastQ12( - in, in_weighted, - weightDenum, LPC_FILTERORDER+1, quantLen[0]); - WebRtcSpl_FilterARFastQ12( - &in[quantLen[0]], &in_weighted[quantLen[0]], - &weightDenum[LPC_FILTERORDER+1], LPC_FILTERORDER+1, quantLen[1]); - - WebRtcIlbcfix_AbsQuantLoop( - syntOut, - in_weighted, - weightDenum, - quantLen, - iLBC_encbits->idxVec); - -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/abs_quant.h b/modules/audio_coding/codecs/iLBC/main/source/abs_quant.h deleted file mode 100644 index fa59593c0..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/abs_quant.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AbsQuant.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ABS_QUANT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ABS_QUANT_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * predictive noise shaping encoding of scaled start state - * (subrutine for WebRtcIlbcfix_StateSearch) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_AbsQuant( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) Encoder instance */ - iLBC_bits *iLBC_encbits, /* (i/o) Encoded bits (outputs idxForMax - and idxVec, uses state_first as - input) */ - WebRtc_Word16 *in, /* (i) vector to encode */ - WebRtc_Word16 *weightDenum /* (i) denominator of synthesis filter */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.c b/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.c deleted file mode 100644 index 4eebc3e23..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AbsQuantLoop.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "sort_sq.h" - -void WebRtcIlbcfix_AbsQuantLoop( - WebRtc_Word16 *syntOutIN, - WebRtc_Word16 *in_weightedIN, - WebRtc_Word16 *weightDenumIN, - WebRtc_Word16 *quantLenIN, - WebRtc_Word16 *idxVecIN - ) -{ - int n, k1, k2; - WebRtc_Word16 index; - WebRtc_Word32 toQW32; - WebRtc_Word32 toQ32; - WebRtc_Word16 tmp16a; - WebRtc_Word16 xq; - - WebRtc_Word16 *syntOut = syntOutIN; - WebRtc_Word16 *in_weighted = in_weightedIN; - WebRtc_Word16 *weightDenum = weightDenumIN; - WebRtc_Word16 *quantLen = quantLenIN; - WebRtc_Word16 *idxVec = idxVecIN; - - n=0; - - for(k1=0;k1<2;k1++) { - for(k2=0;k2 32767) { - toQ32 = (WebRtc_Word32) 32767; - } else if (toQ32 < -32768) { - toQ32 = (WebRtc_Word32) -32768; - } - - /* Quantize the state */ - if (toQW32<(-7577)) { - /* To prevent negative overflow */ - index=0; - } else if (toQW32>8151) { - /* To prevent positive overflow */ - index=7; - } else { - /* Find the best quantization index - (state_sq3Tbl is in Q13 and toQ is in Q11) - */ - WebRtcIlbcfix_SortSq(&xq, &index, - (WebRtc_Word16)toQ32, - WebRtcIlbcfix_kStateSq3, 8); - } - - /* Store selected index */ - (*idxVec++) = index; - - /* Compute decoded sample and update of the prediction filter */ - tmp16a = ((WebRtcIlbcfix_kStateSq3[index] + 2 ) >> 2); - - *syntOut = (WebRtc_Word16) (tmp16a + (WebRtc_Word32)(*in_weighted) - toQW32); - - n++; - syntOut++; in_weighted++; - } - /* Update perceptual weighting filter at subframe border */ - weightDenum += 11; - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.h b/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.h deleted file mode 100644 index f506e8e67..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/abs_quant_loop.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AbsQuantLoop.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ABS_QUANT_LOOP_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ABS_QUANT_LOOP_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * predictive noise shaping encoding of scaled start state - * (subrutine for WebRtcIlbcfix_StateSearch) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_AbsQuantLoop( - WebRtc_Word16 *syntOutIN, - WebRtc_Word16 *in_weightedIN, - WebRtc_Word16 *weightDenumIN, - WebRtc_Word16 *quantLenIN, - WebRtc_Word16 *idxVecIN - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.c b/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.c deleted file mode 100644 index 6011e921a..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AugmentedCbCorr.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "augmented_cb_corr.h" - -void WebRtcIlbcfix_AugmentedCbCorr( - WebRtc_Word16 *target, /* (i) Target vector */ - WebRtc_Word16 *buffer, /* (i) Memory buffer */ - WebRtc_Word16 *interpSamples, /* (i) buffer with - interpolated samples */ - WebRtc_Word32 *crossDot, /* (o) The cross correlation between - the target and the Augmented - vector */ - WebRtc_Word16 low, /* (i) Lag to start from (typically - 20) */ - WebRtc_Word16 high, /* (i) Lag to end at (typically 39) */ - WebRtc_Word16 scale) /* (i) Scale factor to use for - the crossDot */ -{ - int lagcount; - WebRtc_Word16 ilow; - WebRtc_Word16 *targetPtr; - WebRtc_Word32 *crossDotPtr; - WebRtc_Word16 *iSPtr=interpSamples; - - /* Calculate the correlation between the target and the - interpolated codebook. The correlation is calculated in - 3 sections with the interpolated part in the middle */ - crossDotPtr=crossDot; - for (lagcount=low; lagcount<=high; lagcount++) { - - ilow = (WebRtc_Word16) (lagcount-4); - - /* Compute dot product for the first (lagcount-4) samples */ - (*crossDotPtr) = WebRtcSpl_DotProductWithScale(target, buffer-lagcount, ilow, scale); - - /* Compute dot product on the interpolated samples */ - (*crossDotPtr) += WebRtcSpl_DotProductWithScale(target+ilow, iSPtr, 4, scale); - targetPtr = target + lagcount; - iSPtr += lagcount-ilow; - - /* Compute dot product for the remaining samples */ - (*crossDotPtr) += WebRtcSpl_DotProductWithScale(targetPtr, buffer-lagcount, SUBL-lagcount, scale); - crossDotPtr++; - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.h b/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.h deleted file mode 100644 index 8e097fe11..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/augmented_cb_corr.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_AugmentedCbCorr.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_AUGMENTED_CB_CORR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_AUGMENTED_CB_CORR_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Calculate correlation between target and Augmented codebooks - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_AugmentedCbCorr( - WebRtc_Word16 *target, /* (i) Target vector */ - WebRtc_Word16 *buffer, /* (i) Memory buffer */ - WebRtc_Word16 *interpSamples, /* (i) buffer with - interpolated samples */ - WebRtc_Word32 *crossDot, /* (o) The cross correlation between - the target and the Augmented - vector */ - WebRtc_Word16 low, /* (i) Lag to start from (typically - 20) */ - WebRtc_Word16 high, /* (i) Lag to end at (typically 39 */ - WebRtc_Word16 scale); /* (i) Scale factor to use for - the crossDot */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/bw_expand.c b/modules/audio_coding/codecs/iLBC/main/source/bw_expand.c deleted file mode 100644 index a2287aaac..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/bw_expand.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_BwExpand.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lpc bandwidth expansion - *---------------------------------------------------------------*/ - -/* The output is in the same domain as the input */ -void WebRtcIlbcfix_BwExpand( - WebRtc_Word16 *out, /* (o) the bandwidth expanded lpc coefficients */ - WebRtc_Word16 *in, /* (i) the lpc coefficients before bandwidth - expansion */ - WebRtc_Word16 *coef, /* (i) the bandwidth expansion factor Q15 */ - WebRtc_Word16 length /* (i) the length of lpc coefficient vectors */ - ) { - int i; - - out[0] = in[0]; - for (i = 1; i < length; i++) { - /* out[i] = coef[i] * in[i] with rounding. - in[] and out[] are in Q12 and coef[] is in Q15 - */ - out[i] = (WebRtc_Word16)((WEBRTC_SPL_MUL_16_16(coef[i], in[i])+16384)>>15); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/bw_expand.h b/modules/audio_coding/codecs/iLBC/main/source/bw_expand.h deleted file mode 100644 index c9f3fabee..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/bw_expand.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_BwExpand.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_BW_EXPAND_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_BW_EXPAND_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lpc bandwidth expansion - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_BwExpand( - WebRtc_Word16 *out, /* (o) the bandwidth expanded lpc coefficients */ - WebRtc_Word16 *in, /* (i) the lpc coefficients before bandwidth - expansion */ - WebRtc_Word16 *coef, /* (i) the bandwidth expansion factor Q15 */ - WebRtc_Word16 length /* (i) the length of lpc coefficient vectors */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/cb_construct.c b/modules/audio_coding/codecs/iLBC/main/source/cb_construct.c deleted file mode 100644 index 094a7e40e..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/cb_construct.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CbConstruct.c - -******************************************************************/ - -#include "defines.h" -#include "gain_dequant.h" -#include "get_cd_vec.h" - -/*----------------------------------------------------------------* - * Construct decoded vector from codebook and gains. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_CbConstruct( - WebRtc_Word16 *decvector, /* (o) Decoded vector */ - WebRtc_Word16 *index, /* (i) Codebook indices */ - WebRtc_Word16 *gain_index, /* (i) Gain quantization indices */ - WebRtc_Word16 *mem, /* (i) Buffer for codevector construction */ - WebRtc_Word16 lMem, /* (i) Length of buffer */ - WebRtc_Word16 veclen /* (i) Length of vector */ - ){ - int j; - WebRtc_Word16 gain[CB_NSTAGES]; - /* Stack based */ - WebRtc_Word16 cbvec0[SUBL]; - WebRtc_Word16 cbvec1[SUBL]; - WebRtc_Word16 cbvec2[SUBL]; - WebRtc_Word32 a32; - WebRtc_Word16 *gainPtr; - - /* gain de-quantization */ - - gain[0] = WebRtcIlbcfix_GainDequant(gain_index[0], 16384, 0); - gain[1] = WebRtcIlbcfix_GainDequant(gain_index[1], gain[0], 1); - gain[2] = WebRtcIlbcfix_GainDequant(gain_index[2], gain[1], 2); - - /* codebook vector construction and construction of total vector */ - - /* Stack based */ - WebRtcIlbcfix_GetCbVec(cbvec0, mem, index[0], lMem, veclen); - WebRtcIlbcfix_GetCbVec(cbvec1, mem, index[1], lMem, veclen); - WebRtcIlbcfix_GetCbVec(cbvec2, mem, index[2], lMem, veclen); - - gainPtr = &gain[0]; - for (j=0;j0)&&(temp2>0)) { - temp1 = WEBRTC_SPL_MAX(temp1, temp2); - scale = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_MUL_16_16(temp1, temp1)); - } else { - /* temp1 or temp2 is negative (maximum was -32768) */ - scale = 30; - } - - /* Scale to so that a mul-add 40 times does not overflow */ - scale = scale - 25; - scale = WEBRTC_SPL_MAX(0, scale); - - /* Compute energy of the original target */ - targetEner = WebRtcSpl_DotProductWithScale(target, target, lTarget, scale); - - /* Prepare search over one more codebook section. This section - is created by filtering the original buffer with a filter. */ - WebRtcIlbcfix_FilteredCbVecs(cbvectors, buf, lMem, WebRtcIlbcfix_kFilterRange[block]); - - range = WebRtcIlbcfix_kSearchRange[block][0]; - - if(lTarget == SUBL) { - /* Create the interpolated samples and store them for use in all stages */ - - /* First section, non-filtered half of the cb */ - WebRtcIlbcfix_InterpolateSamples(interpSamples, buf, lMem); - - /* Second section, filtered half of the cb */ - WebRtcIlbcfix_InterpolateSamples(interpSamplesFilt, cbvectors, lMem); - - /* Compute the CB vectors' energies for the first cb section (non-filtered) */ - WebRtcIlbcfix_CbMemEnergyAugmentation(interpSamples, buf, - scale, 20, energyW16, energyShifts); - - /* Compute the CB vectors' energies for the second cb section (filtered cb) */ - WebRtcIlbcfix_CbMemEnergyAugmentation(interpSamplesFilt, cbvectors, - scale, (WebRtc_Word16)(base_size+20), energyW16, energyShifts); - - /* Compute the CB vectors' energies and store them in the vector - * energyW16. Also the corresponding shift values are stored. The - * energy values are used in all three stages. */ - WebRtcIlbcfix_CbMemEnergy(range, buf, cbvectors, lMem, - lTarget, energyW16+20, energyShifts+20, scale, base_size); - - } else { - /* Compute the CB vectors' energies and store them in the vector - * energyW16. Also the corresponding shift values are stored. The - * energy values are used in all three stages. */ - WebRtcIlbcfix_CbMemEnergy(range, buf, cbvectors, lMem, - lTarget, energyW16, energyShifts, scale, base_size); - - /* Set the energy positions 58-63 and 122-127 to zero - (otherwise they are uninitialized) */ - WebRtcSpl_MemSetW16(energyW16+range, 0, (base_size-range)); - WebRtcSpl_MemSetW16(energyW16+range+base_size, 0, (base_size-range)); - } - - /* Calculate Inverse Energy (energyW16 is already normalized - and will contain the inverse energy in Q29 after this call */ - WebRtcIlbcfix_EnergyInverse(energyW16, base_size*CB_EXPAND); - - /* The gain value computed in the previous stage is used - * as an upper limit to what the next stage gain value - * is allowed to be. In stage 0, 16384 (1.0 in Q14) is used as - * the upper limit. */ - gains[0] = 16384; - - for (stage=0; stage>1); - eInd=sInd+CB_RESRANGE; - if (sInd<0) { - eInd-=sInd; - sInd=0; - } - if (eInd>=range) { - eInd=range-1; - sInd=eInd-CB_RESRANGE; - } - - range = WebRtcIlbcfix_kSearchRange[block][stage]; - - if (lTarget==SUBL) { - i=sInd; - if (sInd<20) { - WebRtcIlbcfix_AugmentedCbCorr(target, cbvectors+lMem, - interpSamplesFilt, cDot, - (WebRtc_Word16)(sInd+20), (WebRtc_Word16)(WEBRTC_SPL_MIN(39, (eInd+20))), scale); - i=20; - } - - cDotPtr=&cDot[WEBRTC_SPL_MAX(0,(20-sInd))]; - cb_vecPtr = cbvectors+lMem-20-i; - - /* Calculate the cross correlations (main part of the filtered CB) */ - WebRtcSpl_CrossCorrelation(cDotPtr, target, cb_vecPtr, lTarget, (WebRtc_Word16)(eInd-i+1), scale, -1); - - } else { - cDotPtr = cDot; - cb_vecPtr = cbvectors+lMem-lTarget-sInd; - - /* Calculate the cross correlations (main part of the filtered CB) */ - WebRtcSpl_CrossCorrelation(cDotPtr, target, cb_vecPtr, lTarget, (WebRtc_Word16)(eInd-sInd+1), scale, -1); - - } - - /* Adjust the search range for the augmented vectors */ - indexOffset=base_size+sInd; - - /* Search for best index in this part of the vector */ - WebRtcIlbcfix_CbSearchCore( - cDot, (WebRtc_Word16)(eInd-sInd+1), stage, inverseEnergy+indexOffset, - inverseEnergyShifts+indexOffset, Crit, - &indexNew, &CritNew, &CritNewSh); - - /* Update the global best index and the corresponding gain */ - WebRtcIlbcfix_CbUpdateBestIndex( - CritNew, CritNewSh, (WebRtc_Word16)(indexNew+indexOffset), cDot[indexNew], - inverseEnergy[indexNew+indexOffset], inverseEnergyShifts[indexNew+indexOffset], - &CritMax, &shTotMax, &bestIndex, &bestGain); - - index[stage] = bestIndex; - - - bestGain = WebRtcIlbcfix_GainQuant(bestGain, - (WebRtc_Word16)WEBRTC_SPL_ABS_W16(gains[stage]), stage, &gain_index[stage]); - - /* Extract the best (according to measure) codebook vector - Also adjust the index, so that the augmented vectors are last. - Above these vectors were first... - */ - - if(lTarget==(STATE_LEN-iLBCenc_inst->state_short_len)) { - - if(index[stage]=20) { - /* Adjust index and extract vector */ - index[stage]-=20; - pp=buf+lMem-lTarget-index[stage]; - } else { - /* Adjust index and extract vector */ - index[stage]+=(base_size-20); - - WebRtcIlbcfix_CreateAugmentedVec((WebRtc_Word16)(index[stage]-base_size+40), - buf+lMem, aug_vec); - pp = aug_vec; - - } - } else { - - if ((index[stage] - base_size) >= 20) { - /* Adjust index and extract vector */ - index[stage]-=20; - pp=cbvectors+lMem-lTarget- - index[stage]+base_size; - } else { - /* Adjust index and extract vector */ - index[stage]+=(base_size-20); - WebRtcIlbcfix_CreateAugmentedVec((WebRtc_Word16)(index[stage]-2*base_size+40), - cbvectors+lMem, aug_vec); - pp = aug_vec; - } - } - } - - /* Subtract the best codebook vector, according - to measure, from the target vector */ - - WebRtcSpl_AddAffineVectorToVector(target, pp, (WebRtc_Word16)(-bestGain), (WebRtc_Word32)8192, (WebRtc_Word16)14, (int)lTarget); - - /* record quantized gain */ - gains[stage+1] = bestGain; - - } /* end of Main Loop. for (stage=0;... */ - - /* Calculte the coded vector (original target - what's left) */ - for (i=0;i>16); - - /* Calculate the criteria (cDot*cDot/energy) */ - *critPtr=WEBRTC_SPL_MUL_16_16(cDotSqW16, (*inverseEnergyPtr)); - - /* Extract the maximum shift value under the constraint - that the criteria is not zero */ - if ((*critPtr)!=0) { - max = WEBRTC_SPL_MAX((*inverseEnergyShiftPtr), max); - } - - inverseEnergyPtr++; - inverseEnergyShiftPtr++; - critPtr++; - cDotPtr++; - } - - /* If no max shifts still at initialization value, set shift to zero */ - if (max==WEBRTC_SPL_WORD16_MIN) { - max = 0; - } - - /* Modify the criterias, so that all of them use the same Q domain */ - critPtr=Crit; - inverseEnergyShiftPtr=inverseEnergyShift; - for (i=0;i31) */ - tmp16 = WEBRTC_SPL_MIN(16, max-(*inverseEnergyShiftPtr)); - - (*critPtr)=WEBRTC_SPL_SHIFT_W32((*critPtr),-tmp16); - critPtr++; - inverseEnergyShiftPtr++; - } - - /* Find the index of the best value */ - *bestIndex = WebRtcSpl_MaxIndexW32(Crit, range); - *bestCrit = Crit[*bestIndex]; - - /* Calculate total shifts of this criteria */ - *bestCritSh = 32 - 2*sh + max; - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/cb_search_core.h b/modules/audio_coding/codecs/iLBC/main/source/cb_search_core.h deleted file mode 100644 index e074c526a..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/cb_search_core.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CbSearchCore.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CB_SEARCH_CORE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CB_SEARCH_CORE_H_ - -#include "defines.h" - -void WebRtcIlbcfix_CbSearchCore( - WebRtc_Word32 *cDot, /* (i) Cross Correlation */ - WebRtc_Word16 range, /* (i) Search range */ - WebRtc_Word16 stage, /* (i) Stage of this search */ - WebRtc_Word16 *inverseEnergy, /* (i) Inversed energy */ - WebRtc_Word16 *inverseEnergyShift, /* (i) Shifts of inversed energy - with the offset 2*16-29 */ - WebRtc_Word32 *Crit, /* (o) The criteria */ - WebRtc_Word16 *bestIndex, /* (o) Index that corresponds to - maximum criteria (in this - vector) */ - WebRtc_Word32 *bestCrit, /* (o) Value of critera for the - chosen index */ - WebRtc_Word16 *bestCritSh); /* (o) The domain of the chosen - criteria */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.c b/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.c deleted file mode 100644 index bf8540859..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CbUpdateBestIndex.c - -******************************************************************/ - -#include "defines.h" -#include "cb_update_best_index.h" -#include "constants.h" - -void WebRtcIlbcfix_CbUpdateBestIndex( - WebRtc_Word32 CritNew, /* (i) New Potentially best Criteria */ - WebRtc_Word16 CritNewSh, /* (i) Shift value of above Criteria */ - WebRtc_Word16 IndexNew, /* (i) Index of new Criteria */ - WebRtc_Word32 cDotNew, /* (i) Cross dot of new index */ - WebRtc_Word16 invEnergyNew, /* (i) Inversed energy new index */ - WebRtc_Word16 energyShiftNew, /* (i) Energy shifts of new index */ - WebRtc_Word32 *CritMax, /* (i/o) Maximum Criteria (so far) */ - WebRtc_Word16 *shTotMax, /* (i/o) Shifts of maximum criteria */ - WebRtc_Word16 *bestIndex, /* (i/o) Index that corresponds to - maximum criteria */ - WebRtc_Word16 *bestGain) /* (i/o) Gain in Q14 that corresponds - to maximum criteria */ -{ - WebRtc_Word16 shOld, shNew, tmp16; - WebRtc_Word16 scaleTmp; - WebRtc_Word32 gainW32; - - /* Normalize the new and old Criteria to the same domain */ - if (CritNewSh>(*shTotMax)) { - shOld=WEBRTC_SPL_MIN(31,CritNewSh-(*shTotMax)); - shNew=0; - } else { - shOld=0; - shNew=WEBRTC_SPL_MIN(31,(*shTotMax)-CritNewSh); - } - - /* Compare the two criterias. If the new one is better, - calculate the gain and store this index as the new best one - */ - - if (WEBRTC_SPL_RSHIFT_W32(CritNew, shNew)> - WEBRTC_SPL_RSHIFT_W32((*CritMax),shOld)) { - - tmp16 = (WebRtc_Word16)WebRtcSpl_NormW32(cDotNew); - tmp16 = 16 - tmp16; - - /* Calculate the gain in Q14 - Compensate for inverseEnergyshift in Q29 and that the energy - value was stored in a WebRtc_Word16 (shifted down 16 steps) - => 29-14+16 = 31 */ - - scaleTmp = -energyShiftNew-tmp16+31; - scaleTmp = WEBRTC_SPL_MIN(31, scaleTmp); - - gainW32 = WEBRTC_SPL_MUL_16_16_RSFT( - ((WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(cDotNew, -tmp16)), invEnergyNew, scaleTmp); - - /* Check if criteria satisfies Gain criteria (max 1.3) - if it is larger set the gain to 1.3 - (slightly different from FLP version) - */ - if (gainW32>21299) { - *bestGain=21299; - } else if (gainW32<-21299) { - *bestGain=-21299; - } else { - *bestGain=(WebRtc_Word16)gainW32; - } - - *CritMax=CritNew; - *shTotMax=CritNewSh; - *bestIndex = IndexNew; - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.h b/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.h deleted file mode 100644 index 901518769..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/cb_update_best_index.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CbUpdateBestIndex.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CB_UPDATE_BEST_INDEX_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CB_UPDATE_BEST_INDEX_H_ - -#include "defines.h" - -void WebRtcIlbcfix_CbUpdateBestIndex( - WebRtc_Word32 CritNew, /* (i) New Potentially best Criteria */ - WebRtc_Word16 CritNewSh, /* (i) Shift value of above Criteria */ - WebRtc_Word16 IndexNew, /* (i) Index of new Criteria */ - WebRtc_Word32 cDotNew, /* (i) Cross dot of new index */ - WebRtc_Word16 invEnergyNew, /* (i) Inversed energy new index */ - WebRtc_Word16 energyShiftNew, /* (i) Energy shifts of new index */ - WebRtc_Word32 *CritMax, /* (i/o) Maximum Criteria (so far) */ - WebRtc_Word16 *shTotMax, /* (i/o) Shifts of maximum criteria */ - WebRtc_Word16 *bestIndex, /* (i/o) Index that corresponds to - maximum criteria */ - WebRtc_Word16 *bestGain); /* (i/o) Gain in Q14 that corresponds - to maximum criteria */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/chebyshev.c b/modules/audio_coding/codecs/iLBC/main/source/chebyshev.c deleted file mode 100644 index 90108ffdb..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/chebyshev.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Chebyshev.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*------------------------------------------------------------------* - * Calculate the Chevyshev polynomial series - * F(w) = 2*exp(-j5w)*C(x) - * C(x) = (T_0(x) + f(1)T_1(x) + ... + f(4)T_1(x) + f(5)/2) - * T_i(x) is the i:th order Chebyshev polynomial - *------------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_Chebyshev( - /* (o) Result of C(x) */ - WebRtc_Word16 x, /* (i) Value to the Chevyshev polynomial */ - WebRtc_Word16 *f /* (i) The coefficients in the polynomial */ - ) { - WebRtc_Word16 b1_high, b1_low; /* Use the high, low format to increase the accuracy */ - WebRtc_Word32 b2; - WebRtc_Word32 tmp1W32; - WebRtc_Word32 tmp2W32; - int i; - - b2 = (WebRtc_Word32)0x1000000; /* b2 = 1.0 (Q23) */ - /* Calculate b1 = 2*x + f[1] */ - tmp1W32 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)x, 10); - tmp1W32 += WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)f[1], 14); - - for (i = 2; i < 5; i++) { - tmp2W32 = tmp1W32; - - /* Split b1 (in tmp1W32) into a high and low part */ - b1_high = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 16); - b1_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp1W32-WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)b1_high),16), 1); - - /* Calculate 2*x*b1-b2+f[i] */ - tmp1W32 = WEBRTC_SPL_LSHIFT_W32( (WEBRTC_SPL_MUL_16_16(b1_high, x) + - WEBRTC_SPL_MUL_16_16_RSFT(b1_low, x, 15)), 2); - - tmp1W32 -= b2; - tmp1W32 += WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)f[i], 14); - - /* Update b2 for next round */ - b2 = tmp2W32; - } - - /* Split b1 (in tmp1W32) into a high and low part */ - b1_high = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 16); - b1_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp1W32-WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)b1_high),16), 1); - - /* tmp1W32 = x*b1 - b2 + f[i]/2 */ - tmp1W32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(b1_high, x), 1) + - WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16_RSFT(b1_low, x, 15), 1); - - tmp1W32 -= b2; - tmp1W32 += WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)f[i], 13); - - /* Handle overflows and set to maximum or minimum WebRtc_Word16 instead */ - if (tmp1W32>((WebRtc_Word32)33553408)) { - return(WEBRTC_SPL_WORD16_MAX); - } else if (tmp1W32<((WebRtc_Word32)-33554432)) { - return(WEBRTC_SPL_WORD16_MIN); - } else { - return((WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp1W32, 10)); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/chebyshev.h b/modules/audio_coding/codecs/iLBC/main/source/chebyshev.h deleted file mode 100644 index 57aab99e3..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/chebyshev.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Chebyshev.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CHEBYSHEV_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CHEBYSHEV_H_ - -#include "defines.h" - -/*------------------------------------------------------------------* - * Calculate the Chevyshev polynomial series - * F(w) = 2*exp(-j5w)*C(x) - * C(x) = (T_0(x) + f(1)T_1(x) + ... + f(4)T_1(x) + f(5)/2) - * T_i(x) is the i:th order Chebyshev polynomial - *------------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_Chebyshev( - /* (o) Result of C(x) */ - WebRtc_Word16 x, /* (i) Value to the Chevyshev polynomial */ - WebRtc_Word16 *f /* (i) The coefficients in the polynomial */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/comp_corr.c b/modules/audio_coding/codecs/iLBC/main/source/comp_corr.c deleted file mode 100644 index 3d7f93eaa..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/comp_corr.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CompCorr.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Compute cross correlation and pitch gain for pitch prediction - * of last subframe at given lag. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_CompCorr( - WebRtc_Word32 *corr, /* (o) cross correlation */ - WebRtc_Word32 *ener, /* (o) energy */ - WebRtc_Word16 *buffer, /* (i) signal buffer */ - WebRtc_Word16 lag, /* (i) pitch lag */ - WebRtc_Word16 bLen, /* (i) length of buffer */ - WebRtc_Word16 sRange, /* (i) correlation search length */ - WebRtc_Word16 scale /* (i) number of rightshifts to use */ - ){ - WebRtc_Word16 *w16ptr; - - w16ptr=&buffer[bLen-sRange-lag]; - - /* Calculate correlation and energy */ - (*corr)=WebRtcSpl_DotProductWithScale(&buffer[bLen-sRange], w16ptr, sRange, scale); - (*ener)=WebRtcSpl_DotProductWithScale(w16ptr, w16ptr, sRange, scale); - - /* For zero energy set the energy to 0 in order to avoid potential - problems for coming divisions */ - if (*ener == 0) { - *corr = 0; - *ener = 1; - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/comp_corr.h b/modules/audio_coding/codecs/iLBC/main/source/comp_corr.h deleted file mode 100644 index cd4653221..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/comp_corr.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CompCorr.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_COMP_CORR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_COMP_CORR_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Compute cross correlation and pitch gain for pitch prediction - * of last subframe at given lag. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_CompCorr( - WebRtc_Word32 *corr, /* (o) cross correlation */ - WebRtc_Word32 *ener, /* (o) energy */ - WebRtc_Word16 *buffer, /* (i) signal buffer */ - WebRtc_Word16 lag, /* (i) pitch lag */ - WebRtc_Word16 bLen, /* (i) length of buffer */ - WebRtc_Word16 sRange, /* (i) correlation search length */ - WebRtc_Word16 scale /* (i) number of rightshifts to use */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/complexityMeasures.m b/modules/audio_coding/codecs/iLBC/main/source/complexityMeasures.m deleted file mode 100644 index f76819454..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/complexityMeasures.m +++ /dev/null @@ -1,49 +0,0 @@ -clear; -pack; -% -% Enter the path to YOUR executable and remember to define the perprocessor -% variable PRINT_MIPS te get the instructions printed to the screen. -% -command = '!iLBCtest.exe 30 speechAndBGnoise.pcm out1.bit out1.pcm tlm10_30ms.dat'; -cout=' > st.txt'; %saves to matlab variable 'st' -eval(strcat(command,cout)); -if(length(cout)>3) - load st.txt -else - disp('No cout file to load') -end - -% initialize vector to zero -index = find(st(1:end,1)==-1); -indexnonzero = find(st(1:end,1)>0); -frames = length(index)-indexnonzero(1)+1; -start = indexnonzero(1) - 1; -functionOrder=max(st(:,2)); -new=zeros(frames,functionOrder); - -for i = 1:frames, - for j = index(start-1+i)+1:(index(start+i)-1), - new(i,st(j,2)) = new(i,st(j,2)) + st(j,1); - end -end - -result=zeros(functionOrder,3); -for i=1:functionOrder - nonzeroelements = find(new(1:end,i)>0); - result(i,1)=i; - - % Compute each function's mean complexity - % result(i,2)=(sum(new(nonzeroelements,i))/(length(nonzeroelements)*0.03))/1000000; - - % Compute each function's maximum complexity in encoding - % and decoding respectively and then add it together: - % result(i,3)=(max(new(1:end,i))/0.03)/1000000; - result(i,3)=(max(new(1:size(new,1)/2,i))/0.03)/1000000 + (max(new(size(new,1)/2+1:end,i))/0.03)/1000000; -end - -result - -% Compute maximum complexity for a single frame (enc/dec separately and together) -maxEncComplexityInAFrame = (max(sum(new(1:size(new,1)/2,:),2))/0.03)/1000000 -maxDecComplexityInAFrame = (max(sum(new(size(new,1)/2+1:end,:),2))/0.03)/1000000 -totalComplexity = maxEncComplexityInAFrame + maxDecComplexityInAFrame \ No newline at end of file diff --git a/modules/audio_coding/codecs/iLBC/main/source/constants.c b/modules/audio_coding/codecs/iLBC/main/source/constants.c deleted file mode 100644 index 5ebe9be53..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/constants.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - constants.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/* HP Filters {b[0] b[1] b[2] -a[1] -a[2]} */ - -const WebRtc_Word16 WebRtcIlbcfix_kHpInCoefs[5] = {3798, -7596, 3798, 7807, -3733}; -const WebRtc_Word16 WebRtcIlbcfix_kHpOutCoefs[5] = {3849, -7699, 3849, 7918, -3833}; - -/* Window in Q11 to window the energies of the 5 choises (3 for 20ms) in the choise for - the 80 sample start state -*/ -const WebRtc_Word16 WebRtcIlbcfix_kStartSequenceEnrgWin[NSUB_MAX-1]= { - 1638, 1843, 2048, 1843, 1638 -}; - -/* LP Filter coeffs used for downsampling */ -const WebRtc_Word16 WebRtcIlbcfix_kLpFiltCoefs[FILTERORDER_DS_PLUS1]= { - -273, 512, 1297, 1696, 1297, 512, -273 -}; - -/* Constants used in the LPC calculations */ - -/* Hanning LPC window (in Q15) */ -const WebRtc_Word16 WebRtcIlbcfix_kLpcWin[BLOCKL_MAX] = { - 6, 22, 50, 89, 139, 200, 272, 355, 449, 554, 669, 795, - 932, 1079, 1237, 1405, 1583, 1771, 1969, 2177, 2395, 2622, 2858, 3104, - 3359, 3622, 3894, 4175, 4464, 4761, 5066, 5379, 5699, 6026, 6361, 6702, - 7050, 7404, 7764, 8130, 8502, 8879, 9262, 9649, 10040, 10436, 10836, 11240, - 11647, 12058, 12471, 12887, 13306, 13726, 14148, 14572, 14997, 15423, 15850, 16277, - 16704, 17131, 17558, 17983, 18408, 18831, 19252, 19672, 20089, 20504, 20916, 21325, - 21730, 22132, 22530, 22924, 23314, 23698, 24078, 24452, 24821, 25185, 25542, 25893, - 26238, 26575, 26906, 27230, 27547, 27855, 28156, 28450, 28734, 29011, 29279, 29538, - 29788, 30029, 30261, 30483, 30696, 30899, 31092, 31275, 31448, 31611, 31764, 31906, - 32037, 32158, 32268, 32367, 32456, 32533, 32600, 32655, 32700, 32733, 32755, 32767, - 32767, 32755, 32733, 32700, 32655, 32600, 32533, 32456, 32367, 32268, 32158, 32037, - 31906, 31764, 31611, 31448, 31275, 31092, 30899, 30696, 30483, 30261, 30029, 29788, - 29538, 29279, 29011, 28734, 28450, 28156, 27855, 27547, 27230, 26906, 26575, 26238, - 25893, 25542, 25185, 24821, 24452, 24078, 23698, 23314, 22924, 22530, 22132, 21730, - 21325, 20916, 20504, 20089, 19672, 19252, 18831, 18408, 17983, 17558, 17131, 16704, - 16277, 15850, 15423, 14997, 14572, 14148, 13726, 13306, 12887, 12471, 12058, 11647, - 11240, 10836, 10436, 10040, 9649, 9262, 8879, 8502, 8130, 7764, 7404, 7050, - 6702, 6361, 6026, 5699, 5379, 5066, 4761, 4464, 4175, 3894, 3622, 3359, - 3104, 2858, 2622, 2395, 2177, 1969, 1771, 1583, 1405, 1237, 1079, 932, - 795, 669, 554, 449, 355, 272, 200, 139, 89, 50, 22, 6 -}; - -/* Asymmetric LPC window (in Q15)*/ -const WebRtc_Word16 WebRtcIlbcfix_kLpcAsymWin[BLOCKL_MAX] = { - 2, 7, 15, 27, 42, 60, 81, 106, 135, 166, 201, 239, - 280, 325, 373, 424, 478, 536, 597, 661, 728, 798, 872, 949, - 1028, 1111, 1197, 1287, 1379, 1474, 1572, 1674, 1778, 1885, 1995, 2108, - 2224, 2343, 2465, 2589, 2717, 2847, 2980, 3115, 3254, 3395, 3538, 3684, - 3833, 3984, 4138, 4295, 4453, 4615, 4778, 4944, 5112, 5283, 5456, 5631, - 5808, 5987, 6169, 6352, 6538, 6725, 6915, 7106, 7300, 7495, 7692, 7891, - 8091, 8293, 8497, 8702, 8909, 9118, 9328, 9539, 9752, 9966, 10182, 10398, - 10616, 10835, 11055, 11277, 11499, 11722, 11947, 12172, 12398, 12625, 12852, 13080, - 13309, 13539, 13769, 14000, 14231, 14463, 14695, 14927, 15160, 15393, 15626, 15859, - 16092, 16326, 16559, 16792, 17026, 17259, 17492, 17725, 17957, 18189, 18421, 18653, - 18884, 19114, 19344, 19573, 19802, 20030, 20257, 20483, 20709, 20934, 21157, 21380, - 21602, 21823, 22042, 22261, 22478, 22694, 22909, 23123, 23335, 23545, 23755, 23962, - 24168, 24373, 24576, 24777, 24977, 25175, 25371, 25565, 25758, 25948, 26137, 26323, - 26508, 26690, 26871, 27049, 27225, 27399, 27571, 27740, 27907, 28072, 28234, 28394, - 28552, 28707, 28860, 29010, 29157, 29302, 29444, 29584, 29721, 29855, 29987, 30115, - 30241, 30364, 30485, 30602, 30717, 30828, 30937, 31043, 31145, 31245, 31342, 31436, - 31526, 31614, 31699, 31780, 31858, 31933, 32005, 32074, 32140, 32202, 32261, 32317, - 32370, 32420, 32466, 32509, 32549, 32585, 32618, 32648, 32675, 32698, 32718, 32734, - 32748, 32758, 32764, 32767, 32767, 32667, 32365, 31863, 31164, 30274, 29197, 27939, - 26510, 24917, 23170, 21281, 19261, 17121, 14876, 12540, 10126, 7650, 5126, 2571 -}; - -/* Lag window for LPC (Q31) */ -const WebRtc_Word32 WebRtcIlbcfix_kLpcLagWin[LPC_FILTERORDER + 1]={ - 2147483647, 2144885453, 2137754373, 2125918626, 2109459810, - 2088483140, 2063130336, 2033564590, 1999977009, 1962580174, - 1921610283}; - -/* WebRtcIlbcfix_kLpcChirpSyntDenum vector in Q15 corresponding - * floating point vector {1 0.9025 0.9025^2 0.9025^3 ...} - */ -const WebRtc_Word16 WebRtcIlbcfix_kLpcChirpSyntDenum[LPC_FILTERORDER + 1] = { - 32767, 29573, 26690, 24087, - 21739, 19619, 17707, 15980, - 14422, 13016, 11747}; - -/* WebRtcIlbcfix_kLpcChirpWeightDenum in Q15 corresponding to - * floating point vector {1 0.4222 0.4222^2... } - */ -const WebRtc_Word16 WebRtcIlbcfix_kLpcChirpWeightDenum[LPC_FILTERORDER + 1] = { - 32767, 13835, 5841, 2466, 1041, 440, - 186, 78, 33, 14, 6}; - -/* LSF quantization Q13 domain */ -const WebRtc_Word16 WebRtcIlbcfix_kLsfCb[64 * 3 + 128 * 3 + 128 * 4] = { - 1273, 2238, 3696, - 3199, 5309, 8209, - 3606, 5671, 7829, - 2815, 5262, 8778, - 2608, 4027, 5493, - 1582, 3076, 5945, - 2983, 4181, 5396, - 2437, 4322, 6902, - 1861, 2998, 4613, - 2007, 3250, 5214, - 1388, 2459, 4262, - 2563, 3805, 5269, - 2036, 3522, 5129, - 1935, 4025, 6694, - 2744, 5121, 7338, - 2810, 4248, 5723, - 3054, 5405, 7745, - 1449, 2593, 4763, - 3411, 5128, 6596, - 2484, 4659, 7496, - 1668, 2879, 4818, - 1812, 3072, 5036, - 1638, 2649, 3900, - 2464, 3550, 4644, - 1853, 2900, 4158, - 2458, 4163, 5830, - 2556, 4036, 6254, - 2703, 4432, 6519, - 3062, 4953, 7609, - 1725, 3703, 6187, - 2221, 3877, 5427, - 2339, 3579, 5197, - 2021, 4633, 7037, - 2216, 3328, 4535, - 2961, 4739, 6667, - 2807, 3955, 5099, - 2788, 4501, 6088, - 1642, 2755, 4431, - 3341, 5282, 7333, - 2414, 3726, 5727, - 1582, 2822, 5269, - 2259, 3447, 4905, - 3117, 4986, 7054, - 1825, 3491, 5542, - 3338, 5736, 8627, - 1789, 3090, 5488, - 2566, 3720, 4923, - 2846, 4682, 7161, - 1950, 3321, 5976, - 1834, 3383, 6734, - 3238, 4769, 6094, - 2031, 3978, 5903, - 1877, 4068, 7436, - 2131, 4644, 8296, - 2764, 5010, 8013, - 2194, 3667, 6302, - 2053, 3127, 4342, - 3523, 6595, 10010, - 3134, 4457, 5748, - 3142, 5819, 9414, - 2223, 4334, 6353, - 2022, 3224, 4822, - 2186, 3458, 5544, - 2552, 4757, 6870, - 10905, 12917, 14578, - 9503, 11485, 14485, - 9518, 12494, 14052, - 6222, 7487, 9174, - 7759, 9186, 10506, - 8315, 12755, 14786, - 9609, 11486, 13866, - 8909, 12077, 13643, - 7369, 9054, 11520, - 9408, 12163, 14715, - 6436, 9911, 12843, - 7109, 9556, 11884, - 7557, 10075, 11640, - 6482, 9202, 11547, - 6463, 7914, 10980, - 8611, 10427, 12752, - 7101, 9676, 12606, - 7428, 11252, 13172, - 10197, 12955, 15842, - 7487, 10955, 12613, - 5575, 7858, 13621, - 7268, 11719, 14752, - 7476, 11744, 13795, - 7049, 8686, 11922, - 8234, 11314, 13983, - 6560, 11173, 14984, - 6405, 9211, 12337, - 8222, 12054, 13801, - 8039, 10728, 13255, - 10066, 12733, 14389, - 6016, 7338, 10040, - 6896, 8648, 10234, - 7538, 9170, 12175, - 7327, 12608, 14983, - 10516, 12643, 15223, - 5538, 7644, 12213, - 6728, 12221, 14253, - 7563, 9377, 12948, - 8661, 11023, 13401, - 7280, 8806, 11085, - 7723, 9793, 12333, - 12225, 14648, 16709, - 8768, 13389, 15245, - 10267, 12197, 13812, - 5301, 7078, 11484, - 7100, 10280, 11906, - 8716, 12555, 14183, - 9567, 12464, 15434, - 7832, 12305, 14300, - 7608, 10556, 12121, - 8913, 11311, 12868, - 7414, 9722, 11239, - 8666, 11641, 13250, - 9079, 10752, 12300, - 8024, 11608, 13306, - 10453, 13607, 16449, - 8135, 9573, 10909, - 6375, 7741, 10125, - 10025, 12217, 14874, - 6985, 11063, 14109, - 9296, 13051, 14642, - 8613, 10975, 12542, - 6583, 10414, 13534, - 6191, 9368, 13430, - 5742, 6859, 9260, - 7723, 9813, 13679, - 8137, 11291, 12833, - 6562, 8973, 10641, - 6062, 8462, 11335, - 6928, 8784, 12647, - 7501, 8784, 10031, - 8372, 10045, 12135, - 8191, 9864, 12746, - 5917, 7487, 10979, - 5516, 6848, 10318, - 6819, 9899, 11421, - 7882, 12912, 15670, - 9558, 11230, 12753, - 7752, 9327, 11472, - 8479, 9980, 11358, - 11418, 14072, 16386, - 7968, 10330, 14423, - 8423, 10555, 12162, - 6337, 10306, 14391, - 8850, 10879, 14276, - 6750, 11885, 15710, - 7037, 8328, 9764, - 6914, 9266, 13476, - 9746, 13949, 15519, - 11032, 14444, 16925, - 8032, 10271, 11810, - 10962, 13451, 15833, - 10021, 11667, 13324, - 6273, 8226, 12936, - 8543, 10397, 13496, - 7936, 10302, 12745, - 6769, 8138, 10446, - 6081, 7786, 11719, - 8637, 11795, 14975, - 8790, 10336, 11812, - 7040, 8490, 10771, - 7338, 10381, 13153, - 6598, 7888, 9358, - 6518, 8237, 12030, - 9055, 10763, 12983, - 6490, 10009, 12007, - 9589, 12023, 13632, - 6867, 9447, 10995, - 7930, 9816, 11397, - 10241, 13300, 14939, - 5830, 8670, 12387, - 9870, 11915, 14247, - 9318, 11647, 13272, - 6721, 10836, 12929, - 6543, 8233, 9944, - 8034, 10854, 12394, - 9112, 11787, 14218, - 9302, 11114, 13400, - 9022, 11366, 13816, - 6962, 10461, 12480, - 11288, 13333, 15222, - 7249, 8974, 10547, - 10566, 12336, 14390, - 6697, 11339, 13521, - 11851, 13944, 15826, - 6847, 8381, 11349, - 7509, 9331, 10939, - 8029, 9618, 11909, - 13973, 17644, 19647, 22474, - 14722, 16522, 20035, 22134, - 16305, 18179, 21106, 23048, - 15150, 17948, 21394, 23225, - 13582, 15191, 17687, 22333, - 11778, 15546, 18458, 21753, - 16619, 18410, 20827, 23559, - 14229, 15746, 17907, 22474, - 12465, 15327, 20700, 22831, - 15085, 16799, 20182, 23410, - 13026, 16935, 19890, 22892, - 14310, 16854, 19007, 22944, - 14210, 15897, 18891, 23154, - 14633, 18059, 20132, 22899, - 15246, 17781, 19780, 22640, - 16396, 18904, 20912, 23035, - 14618, 17401, 19510, 21672, - 15473, 17497, 19813, 23439, - 18851, 20736, 22323, 23864, - 15055, 16804, 18530, 20916, - 16490, 18196, 19990, 21939, - 11711, 15223, 21154, 23312, - 13294, 15546, 19393, 21472, - 12956, 16060, 20610, 22417, - 11628, 15843, 19617, 22501, - 14106, 16872, 19839, 22689, - 15655, 18192, 20161, 22452, - 12953, 15244, 20619, 23549, - 15322, 17193, 19926, 21762, - 16873, 18676, 20444, 22359, - 14874, 17871, 20083, 21959, - 11534, 14486, 19194, 21857, - 17766, 19617, 21338, 23178, - 13404, 15284, 19080, 23136, - 15392, 17527, 19470, 21953, - 14462, 16153, 17985, 21192, - 17734, 19750, 21903, 23783, - 16973, 19096, 21675, 23815, - 16597, 18936, 21257, 23461, - 15966, 17865, 20602, 22920, - 15416, 17456, 20301, 22972, - 18335, 20093, 21732, 23497, - 15548, 17217, 20679, 23594, - 15208, 16995, 20816, 22870, - 13890, 18015, 20531, 22468, - 13211, 15377, 19951, 22388, - 12852, 14635, 17978, 22680, - 16002, 17732, 20373, 23544, - 11373, 14134, 19534, 22707, - 17329, 19151, 21241, 23462, - 15612, 17296, 19362, 22850, - 15422, 19104, 21285, 23164, - 13792, 17111, 19349, 21370, - 15352, 17876, 20776, 22667, - 15253, 16961, 18921, 22123, - 14108, 17264, 20294, 23246, - 15785, 17897, 20010, 21822, - 17399, 19147, 20915, 22753, - 13010, 15659, 18127, 20840, - 16826, 19422, 22218, 24084, - 18108, 20641, 22695, 24237, - 18018, 20273, 22268, 23920, - 16057, 17821, 21365, 23665, - 16005, 17901, 19892, 23016, - 13232, 16683, 21107, 23221, - 13280, 16615, 19915, 21829, - 14950, 18575, 20599, 22511, - 16337, 18261, 20277, 23216, - 14306, 16477, 21203, 23158, - 12803, 17498, 20248, 22014, - 14327, 17068, 20160, 22006, - 14402, 17461, 21599, 23688, - 16968, 18834, 20896, 23055, - 15070, 17157, 20451, 22315, - 15419, 17107, 21601, 23946, - 16039, 17639, 19533, 21424, - 16326, 19261, 21745, 23673, - 16489, 18534, 21658, 23782, - 16594, 18471, 20549, 22807, - 18973, 21212, 22890, 24278, - 14264, 18674, 21123, 23071, - 15117, 16841, 19239, 23118, - 13762, 15782, 20478, 23230, - 14111, 15949, 20058, 22354, - 14990, 16738, 21139, 23492, - 13735, 16971, 19026, 22158, - 14676, 17314, 20232, 22807, - 16196, 18146, 20459, 22339, - 14747, 17258, 19315, 22437, - 14973, 17778, 20692, 23367, - 15715, 17472, 20385, 22349, - 15702, 18228, 20829, 23410, - 14428, 16188, 20541, 23630, - 16824, 19394, 21365, 23246, - 13069, 16392, 18900, 21121, - 12047, 16640, 19463, 21689, - 14757, 17433, 19659, 23125, - 15185, 16930, 19900, 22540, - 16026, 17725, 19618, 22399, - 16086, 18643, 21179, 23472, - 15462, 17248, 19102, 21196, - 17368, 20016, 22396, 24096, - 12340, 14475, 19665, 23362, - 13636, 16229, 19462, 22728, - 14096, 16211, 19591, 21635, - 12152, 14867, 19943, 22301, - 14492, 17503, 21002, 22728, - 14834, 16788, 19447, 21411, - 14650, 16433, 19326, 22308, - 14624, 16328, 19659, 23204, - 13888, 16572, 20665, 22488, - 12977, 16102, 18841, 22246, - 15523, 18431, 21757, 23738, - 14095, 16349, 18837, 20947, - 13266, 17809, 21088, 22839, - 15427, 18190, 20270, 23143, - 11859, 16753, 20935, 22486, - 12310, 17667, 21736, 23319, - 14021, 15926, 18702, 22002, - 12286, 15299, 19178, 21126, - 15703, 17491, 21039, 23151, - 12272, 14018, 18213, 22570, - 14817, 16364, 18485, 22598, - 17109, 19683, 21851, 23677, - 12657, 14903, 19039, 22061, - 14713, 16487, 20527, 22814, - 14635, 16726, 18763, 21715, - 15878, 18550, 20718, 22906 -}; - -const WebRtc_Word16 WebRtcIlbcfix_kLsfDimCb[LSF_NSPLIT] = {3, 3, 4}; -const WebRtc_Word16 WebRtcIlbcfix_kLsfSizeCb[LSF_NSPLIT] = {64,128,128}; - -const WebRtc_Word16 WebRtcIlbcfix_kLsfMean[LPC_FILTERORDER] = { - 2308, 3652, 5434, 7885, - 10255, 12559, 15160, 17513, - 20328, 22752}; - -const WebRtc_Word16 WebRtcIlbcfix_kLspMean[LPC_FILTERORDER] = { - 31476, 29565, 25819, 18725, 10276, - 1236, -9049, -17600, -25884, -30618 -}; - -/* Q14 */ -const WebRtc_Word16 WebRtcIlbcfix_kLsfWeight20ms[4] = {12288, 8192, 4096, 0}; -const WebRtc_Word16 WebRtcIlbcfix_kLsfWeight30ms[6] = {8192, 16384, 10923, 5461, 0, 0}; - -/* - cos(x) in Q15 - WebRtcIlbcfix_kCos[i] = cos(pi*i/64.0) - used in WebRtcIlbcfix_Lsp2Lsf() -*/ - -const WebRtc_Word16 WebRtcIlbcfix_kCos[64] = { - 32767, 32729, 32610, 32413, 32138, 31786, 31357, 30853, - 30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279, - 23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010, - 12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608, - 0, -1608, -3212, -4808, -6393, -7962, -9512, -11039, - -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006, - -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622, - -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729 -}; - -/* - Derivative in Q19, used to interpolate between the - WebRtcIlbcfix_kCos[] values to get a more exact y = cos(x) -*/ -const WebRtc_Word16 WebRtcIlbcfix_kCosDerivative[64] = { - -632, -1893, -3150, -4399, -5638, -6863, -8072, -9261, - -10428, -11570, -12684, -13767, -14817, -15832, -16808, -17744, - -18637, -19486, -20287, -21039, -21741, -22390, -22986, -23526, - -24009, -24435, -24801, -25108, -25354, -25540, -25664, -25726, - -25726, -25664, -25540, -25354, -25108, -24801, -24435, -24009, - -23526, -22986, -22390, -21741, -21039, -20287, -19486, -18637, - -17744, -16808, -15832, -14817, -13767, -12684, -11570, -10428, - -9261, -8072, -6863, -5638, -4399, -3150, -1893, -632}; - -/* - Table in Q15, used for a2lsf conversion - WebRtcIlbcfix_kCosGrid[i] = cos((2*pi*i)/(float)(2*COS_GRID_POINTS)); -*/ - -const WebRtc_Word16 WebRtcIlbcfix_kCosGrid[COS_GRID_POINTS + 1] = { - 32760, 32723, 32588, 32364, 32051, 31651, 31164, 30591, - 29935, 29196, 28377, 27481, 26509, 25465, 24351, 23170, - 21926, 20621, 19260, 17846, 16384, 14876, 13327, 11743, - 10125, 8480, 6812, 5126, 3425, 1714, 0, -1714, -3425, - -5126, -6812, -8480, -10125, -11743, -13327, -14876, - -16384, -17846, -19260, -20621, -21926, -23170, -24351, - -25465, -26509, -27481, -28377, -29196, -29935, -30591, - -31164, -31651, -32051, -32364, -32588, -32723, -32760 -}; - -/* - Derivative of y = acos(x) in Q12 - used in WebRtcIlbcfix_Lsp2Lsf() -*/ - -const WebRtc_Word16 WebRtcIlbcfix_kAcosDerivative[64] = { - -26887, -8812, -5323, -3813, -2979, -2444, -2081, -1811, - -1608, -1450, -1322, -1219, -1132, -1059, -998, -946, - -901, -861, -827, -797, -772, -750, -730, -713, - -699, -687, -677, -668, -662, -657, -654, -652, - -652, -654, -657, -662, -668, -677, -687, -699, - -713, -730, -750, -772, -797, -827, -861, -901, - -946, -998, -1059, -1132, -1219, -1322, -1450, -1608, - -1811, -2081, -2444, -2979, -3813, -5323, -8812, -26887 -}; - - -/* Tables for quantization of start state */ - -/* State quantization tables */ -const WebRtc_Word16 WebRtcIlbcfix_kStateSq3[8] = { /* Values in Q13 */ - -30473, -17838, -9257, -2537, - 3639, 10893, 19958, 32636 -}; - -/* This table defines the limits for the selection of the freqg - less or equal than value 0 => index = 0 - less or equal than value k => index = k -*/ -const WebRtc_Word32 WebRtcIlbcfix_kChooseFrgQuant[64] = { - 118, 163, 222, 305, 425, 604, - 851, 1174, 1617, 2222, 3080, 4191, - 5525, 7215, 9193, 11540, 14397, 17604, - 21204, 25209, 29863, 35720, 42531, 50375, - 59162, 68845, 80108, 93754, 110326, 129488, - 150654, 174328, 201962, 233195, 267843, 308239, - 354503, 405988, 464251, 531550, 608652, 697516, - 802526, 928793, 1080145, 1258120, 1481106, 1760881, - 2111111, 2546619, 3078825, 3748642, 4563142, 5573115, - 6887601, 8582108, 10797296, 14014513, 18625760, 25529599, - 37302935, 58819185, 109782723, WEBRTC_SPL_WORD32_MAX -}; - -const WebRtc_Word16 WebRtcIlbcfix_kScale[64] = { - /* Values in Q16 */ - 29485, 25003, 21345, 18316, 15578, 13128, 10973, 9310, 7955, - 6762, 5789, 4877, 4255, 3699, 3258, 2904, 2595, 2328, - 2123, 1932, 1785, 1631, 1493, 1370, 1260, 1167, 1083, - /* Values in Q21 */ - 32081, 29611, 27262, 25229, 23432, 21803, 20226, 18883, 17609, - 16408, 15311, 14327, 13390, 12513, 11693, 10919, 10163, 9435, - 8739, 8100, 7424, 6813, 6192, 5648, 5122, 4639, 4207, 3798, - 3404, 3048, 2706, 2348, 2036, 1713, 1393, 1087, 747 -}; - -/*frgq in fixpoint, but already computed like this: - for(i=0; i<64; i++){ - a = (pow(10,frgq[i])/4.5); - WebRtcIlbcfix_kFrgQuantMod[i] = round(a); - } - - Value 0 :36 in Q8 - 37:58 in Q5 - 59:63 in Q3 -*/ -const WebRtc_Word16 WebRtcIlbcfix_kFrgQuantMod[64] = { - /* First 37 values in Q8 */ - 569, 671, 786, 916, 1077, 1278, - 1529, 1802, 2109, 2481, 2898, 3440, - 3943, 4535, 5149, 5778, 6464, 7208, - 7904, 8682, 9397, 10285, 11240, 12246, - 13313, 14382, 15492, 16735, 18131, 19693, - 21280, 22912, 24624, 26544, 28432, 30488, - 32720, - /* 22 values in Q5 */ - 4383, 4684, 5012, 5363, 5739, 6146, - 6603, 7113, 7679, 8285, 9040, 9850, - 10838, 11882, 13103, 14467, 15950, 17669, - 19712, 22016, 24800, 28576, - /* 5 values in Q3 */ - 8240, 9792, 12040, 15440, 22472 -}; - -/* Constants for codebook search and creation */ - -/* Expansion filter to get additional cb section. - * Q12 and reversed compared to flp - */ -const WebRtc_Word16 WebRtcIlbcfix_kCbFiltersRev[CB_FILTERLEN]={ - -140, 446, -755, 3302, 2922, -590, 343, -138}; - -/* Weighting coefficients for short lags. - * [0.2 0.4 0.6 0.8] in Q15 */ -const WebRtc_Word16 WebRtcIlbcfix_kAlpha[4]={ - 6554, 13107, 19661, 26214}; - -/* Ranges for search and filters at different subframes */ - -const WebRtc_Word16 WebRtcIlbcfix_kSearchRange[5][CB_NSTAGES]={ - {58,58,58}, {108,44,44}, {108,108,108}, {108,108,108}, {108,108,108}}; - -const WebRtc_Word16 WebRtcIlbcfix_kFilterRange[5]={63, 85, 125, 147, 147}; - -/* Gain Quantization for the codebook gains of the 3 stages */ - -/* Q14 (one extra value (max WebRtc_Word16) to simplify for the search) */ -const WebRtc_Word16 WebRtcIlbcfix_kGainSq3[9]={ - -16384, -10813, -5407, 0, 4096, 8192, - 12288, 16384, 32767}; - -/* Q14 (one extra value (max WebRtc_Word16) to simplify for the search) */ -const WebRtc_Word16 WebRtcIlbcfix_kGainSq4[17]={ - -17203, -14746, -12288, -9830, -7373, -4915, - -2458, 0, 2458, 4915, 7373, 9830, - 12288, 14746, 17203, 19661, 32767}; - -/* Q14 (one extra value (max WebRtc_Word16) to simplify for the search) */ -const WebRtc_Word16 WebRtcIlbcfix_kGainSq5[33]={ - 614, 1229, 1843, 2458, 3072, 3686, - 4301, 4915, 5530, 6144, 6758, 7373, - 7987, 8602, 9216, 9830, 10445, 11059, - 11674, 12288, 12902, 13517, 14131, 14746, - 15360, 15974, 16589, 17203, 17818, 18432, - 19046, 19661, 32767}; - -/* Q14 gain_sq5Tbl squared in Q14 */ -const WebRtc_Word16 WebRtcIlbcfix_kGainSq5Sq[32] = { - 23, 92, 207, 368, 576, 829, - 1129, 1474, 1866, 2304, 2787, 3317, - 3893, 4516, 5184, 5897, 6658, 7464, - 8318, 9216, 10160, 11151, 12187, 13271, - 14400, 15574, 16796, 18062, 19377, 20736, - 22140, 23593 -}; - -const WebRtc_Word16* const WebRtcIlbcfix_kGain[3] = -{WebRtcIlbcfix_kGainSq5, WebRtcIlbcfix_kGainSq4, WebRtcIlbcfix_kGainSq3}; - - -/* Tables for the Enhancer, using upsamling factor 4 (ENH_UPS0 = 4) */ - -const WebRtc_Word16 WebRtcIlbcfix_kEnhPolyPhaser[ENH_UPS0][ENH_FLO_MULT2_PLUS1]={ - {0, 0, 0, 4096, 0, 0, 0}, - {64, -315, 1181, 3531, -436, 77, -64}, - {97, -509, 2464, 2464, -509, 97, -97}, - {77, -436, 3531, 1181, -315, 64, -77} -}; - -const WebRtc_Word16 WebRtcIlbcfix_kEnhWt[3] = { - 4800, 16384, 27968 /* Q16 */ -}; - -const WebRtc_Word16 WebRtcIlbcfix_kEnhPlocs[ENH_NBLOCKS_TOT] = { - 160, 480, 800, 1120, 1440, 1760, 2080, 2400 /* Q(-2) */ -}; - -/* PLC table */ - -const WebRtc_Word16 WebRtcIlbcfix_kPlcPerSqr[6] = { /* Grid points for square of periodiciy in Q15 */ - 839, 1343, 2048, 2998, 4247, 5849 -}; - -const WebRtc_Word16 WebRtcIlbcfix_kPlcPitchFact[6] = { /* Value of y=(x^4-0.4)/(0.7-0.4) in grid points in Q15 */ - 0, 5462, 10922, 16384, 21846, 27306 -}; - -const WebRtc_Word16 WebRtcIlbcfix_kPlcPfSlope[6] = { /* Slope of y=(x^4-0.4)/(0.7-0.4) in Q11 */ - 26667, 18729, 13653, 10258, 7901, 6214 -}; diff --git a/modules/audio_coding/codecs/iLBC/main/source/constants.h b/modules/audio_coding/codecs/iLBC/main/source/constants.h deleted file mode 100644 index f787f743c..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/constants.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - constants.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CONSTANTS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CONSTANTS_H_ - -#include "defines.h" -#include "typedefs.h" - -/* high pass filters */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kHpInCoefs[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kHpOutCoefs[]; - -/* Window for start state decision */ -extern const WebRtc_Word16 WebRtcIlbcfix_kStartSequenceEnrgWin[]; - -/* low pass filter used for downsampling */ -extern const WebRtc_Word16 WebRtcIlbcfix_kLpFiltCoefs[]; - -/* LPC analysis and quantization */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kLpcWin[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLpcAsymWin[]; -extern const WebRtc_Word32 WebRtcIlbcfix_kLpcLagWin[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLpcChirpSyntDenum[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLpcChirpWeightDenum[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfDimCb[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfSizeCb[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfCb[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfWeight20ms[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfWeight30ms[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLsfMean[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kLspMean[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kCos[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kCosDerivative[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kCosGrid[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kAcosDerivative[]; - -/* state quantization tables */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kStateSq3[]; -extern const WebRtc_Word32 WebRtcIlbcfix_kChooseFrgQuant[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kScale[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kFrgQuantMod[]; - -/* Ranges for search and filters at different subframes */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kSearchRange[5][CB_NSTAGES]; -extern const WebRtc_Word16 WebRtcIlbcfix_kFilterRange[]; - -/* gain quantization tables */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kGainSq3[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kGainSq4[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kGainSq5[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kGainSq5Sq[]; -extern const WebRtc_Word16* const WebRtcIlbcfix_kGain[]; - -/* adaptive codebook definitions */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kCbFiltersRev[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kAlpha[]; - -/* enhancer definitions */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kEnhPolyPhaser[ENH_UPS0][ENH_FLO_MULT2_PLUS1]; -extern const WebRtc_Word16 WebRtcIlbcfix_kEnhWt[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kEnhPlocs[]; - -/* PLC tables */ - -extern const WebRtc_Word16 WebRtcIlbcfix_kPlcPerSqr[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kPlcPitchFact[]; -extern const WebRtc_Word16 WebRtcIlbcfix_kPlcPfSlope[]; - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.c b/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.c deleted file mode 100644 index f021c4d57..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CreateAugmentedVec.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * Recreate a specific codebook vector from the augmented part. - * - *----------------------------------------------------------------*/ - -void WebRtcIlbcfix_CreateAugmentedVec( - WebRtc_Word16 index, /* (i) Index for the augmented vector to be created */ - WebRtc_Word16 *buffer, /* (i) Pointer to the end of the codebook memory that - is used for creation of the augmented codebook */ - WebRtc_Word16 *cbVec /* (o) The construced codebook vector */ - ) { - WebRtc_Word16 ilow; - WebRtc_Word16 *ppo, *ppi; - WebRtc_Word16 cbVecTmp[4]; - - ilow = index-4; - - /* copy the first noninterpolated part */ - ppo = buffer-index; - WEBRTC_SPL_MEMCPY_W16(cbVec, ppo, index); - - /* interpolation */ - ppo = buffer - 4; - ppi = buffer - index - 4; - - /* perform cbVec[ilow+k] = ((ppi[k]*alphaTbl[k])>>15) + ((ppo[k]*alphaTbl[3-k])>>15); - for k = 0..3 - */ - WebRtcSpl_ElementwiseVectorMult(&cbVec[ilow], ppi, WebRtcIlbcfix_kAlpha, 4, 15); - WebRtcSpl_ReverseOrderMultArrayElements(cbVecTmp, ppo, &WebRtcIlbcfix_kAlpha[3], 4, 15); - WebRtcSpl_AddVectorsAndShift(&cbVec[ilow], &cbVec[ilow], cbVecTmp, 4, 0); - - /* copy the second noninterpolated part */ - ppo = buffer - index; - WEBRTC_SPL_MEMCPY_W16(cbVec+index,ppo,(SUBL-index)); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.h b/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.h deleted file mode 100644 index 970a9beff..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/create_augmented_vec.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_CreateAugmentedVec.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CREATE_AUGMENTED_VEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_CREATE_AUGMENTED_VEC_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Recreate a specific codebook vector from the augmented part. - * - *----------------------------------------------------------------*/ - -void WebRtcIlbcfix_CreateAugmentedVec( - WebRtc_Word16 index, /* (i) Index for the augmented vector to be created */ - WebRtc_Word16 *buffer, /* (i) Pointer to the end of the codebook memory that - is used for creation of the augmented codebook */ - WebRtc_Word16 *cbVec /* (o) The construced codebook vector */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/decode.c b/modules/audio_coding/codecs/iLBC/main/source/decode.c deleted file mode 100644 index 827532c56..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decode.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Decode.c - -******************************************************************/ - -#include "defines.h" -#include "simple_lsf_dequant.h" -#include "decoder_interpolate_lsf.h" -#include "index_conv_dec.h" -#include "do_plc.h" -#include "constants.h" -#include "enhancer_interface.h" -#include "xcorr_coef.h" -#include "lsf_check.h" -#include "decode_residual.h" -#include "unpack_bits.h" -#include "hp_output.h" -#ifndef WEBRTC_BIG_ENDIAN -#include "swap_bytes.h" -#endif - -/*----------------------------------------------------------------* - * main decoder function - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecodeImpl( - WebRtc_Word16 *decblock, /* (o) decoded signal block */ - WebRtc_UWord16 *bytes, /* (i) encoded signal bits */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state - structure */ - WebRtc_Word16 mode /* (i) 0: bad packet, PLC, - 1: normal */ - ) { - int i; - WebRtc_Word16 order_plus_one; - - WebRtc_Word16 last_bit; - WebRtc_Word16 *data; - /* Stack based */ - WebRtc_Word16 decresidual[BLOCKL_MAX]; - WebRtc_Word16 PLCresidual[BLOCKL_MAX + LPC_FILTERORDER]; - WebRtc_Word16 syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; - WebRtc_Word16 PLClpc[LPC_FILTERORDER + 1]; - iLBC_bits *iLBCbits_inst = (iLBC_bits*)PLCresidual; - - /* Reuse some buffers that are non overlapping in order to save stack memory */ - data = &PLCresidual[LPC_FILTERORDER]; - - if (mode>0) { /* the data are good */ - - /* decode data */ - -#ifndef WEBRTC_BIG_ENDIAN - WebRtcIlbcfix_SwapBytes((WebRtc_UWord16*)bytes, iLBCdec_inst->no_of_words); -#endif - - /* Unpacketize bits into parameters */ - - last_bit = WebRtcIlbcfix_UnpackBits(bytes, iLBCbits_inst, iLBCdec_inst->mode); - -#ifndef WEBRTC_BIG_ENDIAN - /* Swap back so that the the input vector "bytes" is unchanged */ - WebRtcIlbcfix_SwapBytes((WebRtc_UWord16*)bytes, iLBCdec_inst->no_of_words); -#endif - - /* Check for bit errors */ - if (iLBCbits_inst->startIdx<1) - mode = 0; - if ((iLBCdec_inst->mode==20) && (iLBCbits_inst->startIdx>3)) - mode = 0; - if ((iLBCdec_inst->mode==30) && (iLBCbits_inst->startIdx>5)) - mode = 0; - if (last_bit==1) - mode = 0; - - if (mode==1) { /* No bit errors was detected, continue decoding */ - /* Stack based */ - WebRtc_Word16 lsfdeq[LPC_FILTERORDER*LPC_N_MAX]; - WebRtc_Word16 weightdenum[(LPC_FILTERORDER + 1)*NSUB_MAX]; - - /* adjust index */ - WebRtcIlbcfix_IndexConvDec(iLBCbits_inst->cb_index); - - /* decode the lsf */ - WebRtcIlbcfix_SimpleLsfDeQ(lsfdeq, (WebRtc_Word16*)(iLBCbits_inst->lsf), iLBCdec_inst->lpc_n); - WebRtcIlbcfix_LsfCheck(lsfdeq, LPC_FILTERORDER, iLBCdec_inst->lpc_n); - WebRtcIlbcfix_DecoderInterpolateLsp(syntdenum, weightdenum, - lsfdeq, LPC_FILTERORDER, iLBCdec_inst); - - /* Decode the residual using the cb and gain indexes */ - WebRtcIlbcfix_DecodeResidual(iLBCdec_inst, iLBCbits_inst, decresidual, syntdenum); - - /* preparing the plc for a future loss! */ - WebRtcIlbcfix_DoThePlc( PLCresidual, PLClpc, 0, - decresidual, syntdenum + (LPC_FILTERORDER + 1)*(iLBCdec_inst->nsub - 1), - (WebRtc_Word16)(iLBCdec_inst->last_lag), iLBCdec_inst); - - /* Use the output from doThePLC */ - WEBRTC_SPL_MEMCPY_W16(decresidual, PLCresidual, iLBCdec_inst->blockl); - } - - } - - if (mode == 0) { - /* the data is bad (either a PLC call - * was made or a bit error was detected) - */ - - /* packet loss conceal */ - - WebRtcIlbcfix_DoThePlc( PLCresidual, PLClpc, 1, - decresidual, syntdenum, (WebRtc_Word16)(iLBCdec_inst->last_lag), iLBCdec_inst); - - WEBRTC_SPL_MEMCPY_W16(decresidual, PLCresidual, iLBCdec_inst->blockl); - - order_plus_one = LPC_FILTERORDER + 1; - - for (i = 0; i < iLBCdec_inst->nsub; i++) { - WEBRTC_SPL_MEMCPY_W16(syntdenum+(i*order_plus_one), - PLClpc, order_plus_one); - } - } - - if ((*iLBCdec_inst).use_enhancer == 1) { /* Enhancer activated */ - - /* Update the filter and filter coefficients if there was a packet loss */ - if (iLBCdec_inst->prev_enh_pl==2) { - for (i=0;insub;i++) { - WEBRTC_SPL_MEMCPY_W16(&(iLBCdec_inst->old_syntdenum[i*(LPC_FILTERORDER+1)]), - syntdenum, (LPC_FILTERORDER+1)); - } - } - - /* post filtering */ - (*iLBCdec_inst).last_lag = - WebRtcIlbcfix_EnhancerInterface(data, decresidual, iLBCdec_inst); - - /* synthesis filtering */ - - /* Set up the filter state */ - WEBRTC_SPL_MEMCPY_W16(&data[-LPC_FILTERORDER], iLBCdec_inst->syntMem, LPC_FILTERORDER); - - if (iLBCdec_inst->mode==20) { - /* Enhancer has 40 samples delay */ - i=0; - WebRtcSpl_FilterARFastQ12( - data, data, - iLBCdec_inst->old_syntdenum + (i+iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1), - LPC_FILTERORDER+1, SUBL); - - for (i=1; i < iLBCdec_inst->nsub; i++) { - WebRtcSpl_FilterARFastQ12( - data+i*SUBL, data+i*SUBL, - syntdenum+(i-1)*(LPC_FILTERORDER+1), - LPC_FILTERORDER+1, SUBL); - } - - } else if (iLBCdec_inst->mode==30) { - /* Enhancer has 80 samples delay */ - for (i=0; i < 2; i++) { - WebRtcSpl_FilterARFastQ12( - data+i*SUBL, data+i*SUBL, - iLBCdec_inst->old_syntdenum + (i+4)*(LPC_FILTERORDER+1), - LPC_FILTERORDER+1, SUBL); - } - for (i=2; i < iLBCdec_inst->nsub; i++) { - WebRtcSpl_FilterARFastQ12( - data+i*SUBL, data+i*SUBL, - syntdenum+(i-2)*(LPC_FILTERORDER+1), - LPC_FILTERORDER+1, SUBL); - } - } - - /* Save the filter state */ - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->syntMem, &data[iLBCdec_inst->blockl-LPC_FILTERORDER], LPC_FILTERORDER); - - } else { /* Enhancer not activated */ - WebRtc_Word16 lag; - - /* Find last lag (since the enhancer is not called to give this info) */ - lag = 20; - if (iLBCdec_inst->mode==20) { - lag = (WebRtc_Word16)WebRtcIlbcfix_XcorrCoef( - &decresidual[iLBCdec_inst->blockl-60], - &decresidual[iLBCdec_inst->blockl-60-lag], - 60, - 80, lag, -1); - } else { - lag = (WebRtc_Word16)WebRtcIlbcfix_XcorrCoef( - &decresidual[iLBCdec_inst->blockl-ENH_BLOCKL], - &decresidual[iLBCdec_inst->blockl-ENH_BLOCKL-lag], - ENH_BLOCKL, - 100, lag, -1); - } - - /* Store lag (it is needed if next packet is lost) */ - (*iLBCdec_inst).last_lag = (int)lag; - - /* copy data and run synthesis filter */ - WEBRTC_SPL_MEMCPY_W16(data, decresidual, iLBCdec_inst->blockl); - - /* Set up the filter state */ - WEBRTC_SPL_MEMCPY_W16(&data[-LPC_FILTERORDER], iLBCdec_inst->syntMem, LPC_FILTERORDER); - - for (i=0; i < iLBCdec_inst->nsub; i++) { - WebRtcSpl_FilterARFastQ12( - data+i*SUBL, data+i*SUBL, - syntdenum + i*(LPC_FILTERORDER+1), - LPC_FILTERORDER+1, SUBL); - } - - /* Save the filter state */ - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->syntMem, &data[iLBCdec_inst->blockl-LPC_FILTERORDER], LPC_FILTERORDER); - } - - WEBRTC_SPL_MEMCPY_W16(decblock,data,iLBCdec_inst->blockl); - - /* High pass filter the signal (with upscaling a factor 2 and saturation) */ - WebRtcIlbcfix_HpOutput(decblock, (WebRtc_Word16*)WebRtcIlbcfix_kHpOutCoefs, - iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx, - iLBCdec_inst->blockl); - - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->old_syntdenum, - syntdenum, iLBCdec_inst->nsub*(LPC_FILTERORDER+1)); - - iLBCdec_inst->prev_enh_pl=0; - - if (mode==0) { /* PLC was used */ - iLBCdec_inst->prev_enh_pl=1; - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/decode.h b/modules/audio_coding/codecs/iLBC/main/source/decode.h deleted file mode 100644 index 0252d9c4c..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decode.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Decode.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * main decoder function - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecodeImpl( - WebRtc_Word16 *decblock, /* (o) decoded signal block */ - WebRtc_UWord16 *bytes, /* (i) encoded signal bits */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state - structure */ - WebRtc_Word16 mode /* (i) 0: bad packet, PLC, - 1: normal */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/decode_residual.c b/modules/audio_coding/codecs/iLBC/main/source/decode_residual.c deleted file mode 100644 index 4bc1cd3cf..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decode_residual.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DecodeResidual.c - -******************************************************************/ - -#include "defines.h" -#include "state_construct.h" -#include "cb_construct.h" -#include "index_conv_dec.h" -#include "do_plc.h" -#include "constants.h" -#include "enhancer_interface.h" -#include "xcorr_coef.h" -#include "lsf_check.h" - - -/*----------------------------------------------------------------* - * frame residual decoder function (subrutine to iLBC_decode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecodeResidual( - iLBC_Dec_Inst_t *iLBCdec_inst, - /* (i/o) the decoder state structure */ - iLBC_bits *iLBC_encbits, /* (i/o) Encoded bits, which are used - for the decoding */ - WebRtc_Word16 *decresidual, /* (o) decoded residual frame */ - WebRtc_Word16 *syntdenum /* (i) the decoded synthesis filter - coefficients */ - ) { - WebRtc_Word16 meml_gotten, Nfor, Nback, diff, start_pos; - WebRtc_Word16 subcount, subframe; - WebRtc_Word16 *reverseDecresidual = iLBCdec_inst->enh_buf; /* Reversed decoded data, used for decoding backwards in time (reuse memory in state) */ - WebRtc_Word16 *memVec = iLBCdec_inst->prevResidual; /* Memory for codebook and filter state (reuse memory in state) */ - WebRtc_Word16 *mem = &memVec[CB_HALFFILTERLEN]; /* Memory for codebook */ - - diff = STATE_LEN - iLBCdec_inst->state_short_len; - - if (iLBC_encbits->state_first == 1) { - start_pos = (iLBC_encbits->startIdx-1)*SUBL; - } else { - start_pos = (iLBC_encbits->startIdx-1)*SUBL + diff; - } - - /* decode scalar part of start state */ - - WebRtcIlbcfix_StateConstruct(iLBC_encbits->idxForMax, - iLBC_encbits->idxVec, &syntdenum[(iLBC_encbits->startIdx-1)*(LPC_FILTERORDER+1)], - &decresidual[start_pos], iLBCdec_inst->state_short_len - ); - - if (iLBC_encbits->state_first) { /* put adaptive part in the end */ - - /* setup memory */ - - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-iLBCdec_inst->state_short_len)); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-iLBCdec_inst->state_short_len, decresidual+start_pos, - iLBCdec_inst->state_short_len); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct( - &decresidual[start_pos+iLBCdec_inst->state_short_len], - iLBC_encbits->cb_index, iLBC_encbits->gain_index, - mem+CB_MEML-ST_MEM_L_TBL, - ST_MEM_L_TBL, (WebRtc_Word16)diff - ); - - } - else {/* put adaptive part in the beginning */ - - /* create reversed vectors for prediction */ - - WebRtcSpl_MemCpyReversedOrder(reverseDecresidual+diff, - &decresidual[(iLBC_encbits->startIdx+1)*SUBL-1-STATE_LEN], diff); - - /* setup memory */ - - meml_gotten = iLBCdec_inst->state_short_len; - WebRtcSpl_MemCpyReversedOrder(mem+CB_MEML-1, - decresidual+start_pos, meml_gotten); - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-meml_gotten)); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct( - reverseDecresidual, - iLBC_encbits->cb_index, iLBC_encbits->gain_index, - mem+CB_MEML-ST_MEM_L_TBL, - ST_MEM_L_TBL, diff - ); - - /* get decoded residual from reversed vector */ - - WebRtcSpl_MemCpyReversedOrder(&decresidual[start_pos-1], - reverseDecresidual, diff); - } - - /* counter for predicted subframes */ - - subcount=1; - - /* forward prediction of subframes */ - - Nfor = iLBCdec_inst->nsub-iLBC_encbits->startIdx-1; - - if( Nfor > 0 ) { - - /* setup memory */ - WebRtcSpl_MemSetW16(mem, 0, CB_MEML-STATE_LEN); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-STATE_LEN, - decresidual+(iLBC_encbits->startIdx-1)*SUBL, STATE_LEN); - - /* loop over subframes to encode */ - - for (subframe=0; subframestartIdx+1+subframe)*SUBL], - iLBC_encbits->cb_index+subcount*CB_NSTAGES, - iLBC_encbits->gain_index+subcount*CB_NSTAGES, - mem, MEM_LF_TBL, SUBL - ); - - /* update memory */ - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, CB_MEML-SUBL); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, - &decresidual[(iLBC_encbits->startIdx+1+subframe)*SUBL], SUBL); - - subcount++; - } - - } - - /* backward prediction of subframes */ - - Nback = iLBC_encbits->startIdx-1; - - if( Nback > 0 ){ - - /* setup memory */ - - meml_gotten = SUBL*(iLBCdec_inst->nsub+1-iLBC_encbits->startIdx); - if( meml_gotten > CB_MEML ) { - meml_gotten=CB_MEML; - } - - WebRtcSpl_MemCpyReversedOrder(mem+CB_MEML-1, - decresidual+(iLBC_encbits->startIdx-1)*SUBL, meml_gotten); - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-meml_gotten)); - - /* loop over subframes to decode */ - - for (subframe=0; subframecb_index+subcount*CB_NSTAGES, - iLBC_encbits->gain_index+subcount*CB_NSTAGES, - mem, MEM_LF_TBL, SUBL - ); - - /* update memory */ - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, CB_MEML-SUBL); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, - &reverseDecresidual[subframe*SUBL], SUBL); - - subcount++; - } - - /* get decoded residual from reversed vector */ - WebRtcSpl_MemCpyReversedOrder(decresidual+SUBL*Nback-1, - reverseDecresidual, SUBL*Nback); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/decode_residual.h b/modules/audio_coding/codecs/iLBC/main/source/decode_residual.h deleted file mode 100644 index ea7208a15..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decode_residual.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DecodeResidual.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODE_RESIDUAL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODE_RESIDUAL_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * frame residual decoder function (subrutine to iLBC_decode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecodeResidual( - iLBC_Dec_Inst_t *iLBCdec_inst, - /* (i/o) the decoder state structure */ - iLBC_bits *iLBC_encbits, /* (i/o) Encoded bits, which are used - for the decoding */ - WebRtc_Word16 *decresidual, /* (o) decoded residual frame */ - WebRtc_Word16 *syntdenum /* (i) the decoded synthesis filter - coefficients */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.c b/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.c deleted file mode 100644 index eee3105c8..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DecoderInterpolateLsp.c - -******************************************************************/ - -#include "lsf_interpolate_to_poly_dec.h" -#include "bw_expand.h" -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * obtain synthesis and weighting filters form lsf coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecoderInterpolateLsp( - WebRtc_Word16 *syntdenum, /* (o) synthesis filter coefficients */ - WebRtc_Word16 *weightdenum, /* (o) weighting denumerator - coefficients */ - WebRtc_Word16 *lsfdeq, /* (i) dequantized lsf coefficients */ - WebRtc_Word16 length, /* (i) length of lsf coefficient vector */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i) the decoder state structure */ - ){ - int i, pos, lp_length; - WebRtc_Word16 lp[LPC_FILTERORDER + 1], *lsfdeq2; - - lsfdeq2 = lsfdeq + length; - lp_length = length + 1; - - if (iLBCdec_inst->mode==30) { - /* subframe 1: Interpolation between old and first LSF */ - - WebRtcIlbcfix_LspInterpolate2PolyDec(lp, (*iLBCdec_inst).lsfdeqold, lsfdeq, - WebRtcIlbcfix_kLsfWeight30ms[0], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum,lp,lp_length); - WebRtcIlbcfix_BwExpand(weightdenum, lp, (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpSyntDenum, (WebRtc_Word16)lp_length); - - /* subframes 2 to 6: interpolation between first and last LSF */ - - pos = lp_length; - for (i = 1; i < 6; i++) { - WebRtcIlbcfix_LspInterpolate2PolyDec(lp, lsfdeq, lsfdeq2, - WebRtcIlbcfix_kLsfWeight30ms[i], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum + pos,lp,lp_length); - WebRtcIlbcfix_BwExpand(weightdenum + pos, lp, - (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpSyntDenum, (WebRtc_Word16)lp_length); - pos += lp_length; - } - } else { /* iLBCdec_inst->mode=20 */ - /* subframes 1 to 4: interpolation between old and new LSF */ - pos = 0; - for (i = 0; i < iLBCdec_inst->nsub; i++) { - WebRtcIlbcfix_LspInterpolate2PolyDec(lp, iLBCdec_inst->lsfdeqold, lsfdeq, - WebRtcIlbcfix_kLsfWeight20ms[i], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum+pos,lp,lp_length); - WebRtcIlbcfix_BwExpand(weightdenum+pos, lp, - (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpSyntDenum, (WebRtc_Word16)lp_length); - pos += lp_length; - } - } - - /* update memory */ - - if (iLBCdec_inst->mode==30) { - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->lsfdeqold, lsfdeq2, length); - } else { - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->lsfdeqold, lsfdeq, length); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.h b/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.h deleted file mode 100644 index 3896ca979..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/decoder_interpolate_lsf.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DecoderInterpolateLsp.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODER_INTERPOLATE_LSF_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DECODER_INTERPOLATE_LSF_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * obtain synthesis and weighting filters form lsf coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DecoderInterpolateLsp( - WebRtc_Word16 *syntdenum, /* (o) synthesis filter coefficients */ - WebRtc_Word16 *weightdenum, /* (o) weighting denumerator - coefficients */ - WebRtc_Word16 *lsfdeq, /* (i) dequantized lsf coefficients */ - WebRtc_Word16 length, /* (i) length of lsf coefficient vector */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i) the decoder state structure */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/defines.h b/modules/audio_coding/codecs/iLBC/main/source/defines.h deleted file mode 100644 index bdeba0194..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/defines.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - define.h - -******************************************************************/ -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DEFINES_H_ - -#include "typedefs.h" -#include "signal_processing_library.h" -#include - -/* general codec settings */ - -#define FS 8000 -#define BLOCKL_20MS 160 -#define BLOCKL_30MS 240 -#define BLOCKL_MAX 240 -#define NSUB_20MS 4 -#define NSUB_30MS 6 -#define NSUB_MAX 6 -#define NASUB_20MS 2 -#define NASUB_30MS 4 -#define NASUB_MAX 4 -#define SUBL 40 -#define STATE_LEN 80 -#define STATE_SHORT_LEN_30MS 58 -#define STATE_SHORT_LEN_20MS 57 - -/* LPC settings */ - -#define LPC_FILTERORDER 10 -#define LPC_LOOKBACK 60 -#define LPC_N_20MS 1 -#define LPC_N_30MS 2 -#define LPC_N_MAX 2 -#define LPC_ASYMDIFF 20 -#define LSF_NSPLIT 3 -#define LSF_NUMBER_OF_STEPS 4 -#define LPC_HALFORDER 5 -#define COS_GRID_POINTS 60 - -/* cb settings */ - -#define CB_NSTAGES 3 -#define CB_EXPAND 2 -#define CB_MEML 147 -#define CB_FILTERLEN (2*4) -#define CB_HALFFILTERLEN 4 -#define CB_RESRANGE 34 -#define CB_MAXGAIN_FIXQ6 83 /* error = -0.24% */ -#define CB_MAXGAIN_FIXQ14 21299 - -/* enhancer */ - -#define ENH_BLOCKL 80 /* block length */ -#define ENH_BLOCKL_HALF (ENH_BLOCKL/2) -#define ENH_HL 3 /* 2*ENH_HL+1 is number blocks - in said second sequence */ -#define ENH_SLOP 2 /* max difference estimated and - correct pitch period */ -#define ENH_PLOCSL 8 /* pitch-estimates and - pitch-locations buffer length */ -#define ENH_OVERHANG 2 -#define ENH_UPS0 4 /* upsampling rate */ -#define ENH_FL0 3 /* 2*FLO+1 is the length of each filter */ -#define ENH_FLO_MULT2_PLUS1 7 -#define ENH_VECTL (ENH_BLOCKL+2*ENH_FL0) -#define ENH_CORRDIM (2*ENH_SLOP+1) -#define ENH_NBLOCKS (BLOCKL/ENH_BLOCKL) -#define ENH_NBLOCKS_EXTRA 5 -#define ENH_NBLOCKS_TOT 8 /* ENH_NBLOCKS+ENH_NBLOCKS_EXTRA */ -#define ENH_BUFL (ENH_NBLOCKS_TOT)*ENH_BLOCKL -#define ENH_BUFL_FILTEROVERHEAD 3 -#define ENH_A0 819 /* Q14 */ -#define ENH_A0_MINUS_A0A0DIV4 848256041 /* Q34 */ -#define ENH_A0DIV2 26843546 /* Q30 */ - -/* PLC */ - -/* Down sampling */ - -#define FILTERORDER_DS_PLUS1 7 -#define DELAY_DS 3 -#define FACTOR_DS 2 - -/* bit stream defs */ - -#define NO_OF_BYTES_20MS 38 -#define NO_OF_BYTES_30MS 50 -#define NO_OF_WORDS_20MS 19 -#define NO_OF_WORDS_30MS 25 -#define STATE_BITS 3 -#define BYTE_LEN 8 -#define ULP_CLASSES 3 - -/* help parameters */ - -#define TWO_PI_FIX 25736 /* Q12 */ - -/* Constants for codebook search and creation */ - -#define ST_MEM_L_TBL 85 -#define MEM_LF_TBL 147 - - -/* Struct for the bits */ -typedef struct iLBC_bits_t_ { - WebRtc_Word16 lsf[LSF_NSPLIT*LPC_N_MAX]; - WebRtc_Word16 cb_index[CB_NSTAGES*(NASUB_MAX+1)]; /* First CB_NSTAGES values contains extra CB index */ - WebRtc_Word16 gain_index[CB_NSTAGES*(NASUB_MAX+1)]; /* First CB_NSTAGES values contains extra CB gain */ - WebRtc_Word16 idxForMax; - WebRtc_Word16 state_first; - WebRtc_Word16 idxVec[STATE_SHORT_LEN_30MS]; - WebRtc_Word16 firstbits; - WebRtc_Word16 startIdx; -} iLBC_bits; - -/* type definition encoder instance */ -typedef struct iLBC_Enc_Inst_t_ { - - /* flag for frame size mode */ - WebRtc_Word16 mode; - - /* basic parameters for different frame sizes */ - WebRtc_Word16 blockl; - WebRtc_Word16 nsub; - WebRtc_Word16 nasub; - WebRtc_Word16 no_of_bytes, no_of_words; - WebRtc_Word16 lpc_n; - WebRtc_Word16 state_short_len; - - /* analysis filter state */ - WebRtc_Word16 anaMem[LPC_FILTERORDER]; - - /* Fix-point old lsf parameters for interpolation */ - WebRtc_Word16 lsfold[LPC_FILTERORDER]; - WebRtc_Word16 lsfdeqold[LPC_FILTERORDER]; - - /* signal buffer for LP analysis */ - WebRtc_Word16 lpc_buffer[LPC_LOOKBACK + BLOCKL_MAX]; - - /* state of input HP filter */ - WebRtc_Word16 hpimemx[2]; - WebRtc_Word16 hpimemy[4]; - -#ifdef SPLIT_10MS - WebRtc_Word16 weightdenumbuf[66]; - WebRtc_Word16 past_samples[160]; - WebRtc_UWord16 bytes[25]; - WebRtc_Word16 section; - WebRtc_Word16 Nfor_flag; - WebRtc_Word16 Nback_flag; - WebRtc_Word16 start_pos; - WebRtc_Word16 diff; -#endif - -} iLBC_Enc_Inst_t; - -/* type definition decoder instance */ -typedef struct iLBC_Dec_Inst_t_ { - - /* flag for frame size mode */ - WebRtc_Word16 mode; - - /* basic parameters for different frame sizes */ - WebRtc_Word16 blockl; - WebRtc_Word16 nsub; - WebRtc_Word16 nasub; - WebRtc_Word16 no_of_bytes, no_of_words; - WebRtc_Word16 lpc_n; - WebRtc_Word16 state_short_len; - - /* synthesis filter state */ - WebRtc_Word16 syntMem[LPC_FILTERORDER]; - - /* old LSF for interpolation */ - WebRtc_Word16 lsfdeqold[LPC_FILTERORDER]; - - /* pitch lag estimated in enhancer and used in PLC */ - int last_lag; - - /* PLC state information */ - int consPLICount, prev_enh_pl; - WebRtc_Word16 perSquare; - - WebRtc_Word16 prevScale, prevPLI; - WebRtc_Word16 prevLag, prevLpc[LPC_FILTERORDER+1]; - WebRtc_Word16 prevResidual[NSUB_MAX*SUBL]; - WebRtc_Word16 seed; - - /* previous synthesis filter parameters */ - - WebRtc_Word16 old_syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX]; - - /* state of output HP filter */ - WebRtc_Word16 hpimemx[2]; - WebRtc_Word16 hpimemy[4]; - - /* enhancer state information */ - int use_enhancer; - WebRtc_Word16 enh_buf[ENH_BUFL+ENH_BUFL_FILTEROVERHEAD]; - WebRtc_Word16 enh_period[ENH_NBLOCKS_TOT]; - -} iLBC_Dec_Inst_t; - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/do_plc.c b/modules/audio_coding/codecs/iLBC/main/source/do_plc.c deleted file mode 100644 index 0dfae2bde..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/do_plc.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DoThePlc.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "comp_corr.h" -#include "bw_expand.h" - -/*----------------------------------------------------------------* - * Packet loss concealment routine. Conceals a residual signal - * and LP parameters. If no packet loss, update state. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DoThePlc( - WebRtc_Word16 *PLCresidual, /* (o) concealed residual */ - WebRtc_Word16 *PLClpc, /* (o) concealed LP parameters */ - WebRtc_Word16 PLI, /* (i) packet loss indicator - 0 - no PL, 1 = PL */ - WebRtc_Word16 *decresidual, /* (i) decoded residual */ - WebRtc_Word16 *lpc, /* (i) decoded LPC (only used for no PL) */ - WebRtc_Word16 inlag, /* (i) pitch lag */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i/o) decoder instance */ - ){ - WebRtc_Word16 i, pick; - WebRtc_Word32 cross, ener, cross_comp, ener_comp = 0; - WebRtc_Word32 measure, maxMeasure, energy; - WebRtc_Word16 max, crossSquareMax, crossSquare; - WebRtc_Word16 j, lag, tmp1, tmp2, randlag; - WebRtc_Word16 shift1, shift2, shift3, shiftMax; - WebRtc_Word16 scale3; - WebRtc_Word16 corrLen; - WebRtc_Word32 tmpW32, tmp2W32; - WebRtc_Word16 use_gain; - WebRtc_Word16 tot_gain; - WebRtc_Word16 max_perSquare; - WebRtc_Word16 scale1, scale2; - WebRtc_Word16 totscale; - WebRtc_Word32 nom; - WebRtc_Word16 denom; - WebRtc_Word16 pitchfact; - WebRtc_Word16 use_lag; - int ind; - WebRtc_Word16 randvec[BLOCKL_MAX]; - - /* Packet Loss */ - if (PLI == 1) { - - (*iLBCdec_inst).consPLICount += 1; - - /* if previous frame not lost, - determine pitch pred. gain */ - - if (iLBCdec_inst->prevPLI != 1) { - - /* Maximum 60 samples are correlated, preserve as high accuracy - as possible without getting overflow */ - max = WebRtcSpl_MaxAbsValueW16((*iLBCdec_inst).prevResidual, (WebRtc_Word16)iLBCdec_inst->blockl); - scale3 = (WebRtcSpl_GetSizeInBits(max)<<1) - 25; - if (scale3 < 0) { - scale3 = 0; - } - - /* Store scale for use when interpolating between the - * concealment and the received packet */ - iLBCdec_inst->prevScale = scale3; - - /* Search around the previous lag +/-3 to find the - best pitch period */ - lag = inlag - 3; - - /* Guard against getting outside the frame */ - corrLen = WEBRTC_SPL_MIN(60, iLBCdec_inst->blockl-(inlag+3)); - - WebRtcIlbcfix_CompCorr( &cross, &ener, - iLBCdec_inst->prevResidual, lag, iLBCdec_inst->blockl, corrLen, scale3); - - /* Normalize and store cross^2 and the number of shifts */ - shiftMax = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_ABS_W32(cross))-15; - crossSquareMax = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(WEBRTC_SPL_SHIFT_W32(cross, -shiftMax), - WEBRTC_SPL_SHIFT_W32(cross, -shiftMax), 15); - - for (j=inlag-2;j<=inlag+3;j++) { - WebRtcIlbcfix_CompCorr( &cross_comp, &ener_comp, - iLBCdec_inst->prevResidual, j, iLBCdec_inst->blockl, corrLen, scale3); - - /* Use the criteria (corr*corr)/energy to compare if - this lag is better or not. To avoid the division, - do a cross multiplication */ - shift1 = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_ABS_W32(cross_comp))-15; - crossSquare = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(WEBRTC_SPL_SHIFT_W32(cross_comp, -shift1), - WEBRTC_SPL_SHIFT_W32(cross_comp, -shift1), 15); - - shift2 = WebRtcSpl_GetSizeInBits(ener)-15; - measure = WEBRTC_SPL_MUL_16_16(WEBRTC_SPL_SHIFT_W32(ener, -shift2), - crossSquare); - - shift3 = WebRtcSpl_GetSizeInBits(ener_comp)-15; - maxMeasure = WEBRTC_SPL_MUL_16_16(WEBRTC_SPL_SHIFT_W32(ener_comp, -shift3), - crossSquareMax); - - /* Calculate shift value, so that the two measures can - be put in the same Q domain */ - if(((shiftMax<<1)+shift3) > ((shift1<<1)+shift2)) { - tmp1 = WEBRTC_SPL_MIN(31, (shiftMax<<1)+shift3-(shift1<<1)-shift2); - tmp2 = 0; - } else { - tmp1 = 0; - tmp2 = WEBRTC_SPL_MIN(31, (shift1<<1)+shift2-(shiftMax<<1)-shift3); - } - - if ((measure>>tmp1) > (maxMeasure>>tmp2)) { - /* New lag is better => record lag, measure and domain */ - lag = j; - crossSquareMax = crossSquare; - cross = cross_comp; - shiftMax = shift1; - ener = ener_comp; - } - } - - /* Calculate the periodicity for the lag with the maximum correlation. - - Definition of the periodicity: - abs(corr(vec1, vec2))/(sqrt(energy(vec1))*sqrt(energy(vec2))) - - Work in the Square domain to simplify the calculations - max_perSquare is less than 1 (in Q15) - */ - tmp2W32=WebRtcSpl_DotProductWithScale(&iLBCdec_inst->prevResidual[iLBCdec_inst->blockl-corrLen], - &iLBCdec_inst->prevResidual[iLBCdec_inst->blockl-corrLen], - corrLen, scale3); - - if ((tmp2W32>0)&&(ener_comp>0)) { - /* norm energies to WebRtc_Word16, compute the product of the energies and - use the upper WebRtc_Word16 as the denominator */ - - scale1=(WebRtc_Word16)WebRtcSpl_NormW32(tmp2W32)-16; - tmp1=(WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(tmp2W32, scale1); - - scale2=(WebRtc_Word16)WebRtcSpl_NormW32(ener)-16; - tmp2=(WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(ener, scale2); - denom=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp1, tmp2, 16); /* denom in Q(scale1+scale2-16) */ - - /* Square the cross correlation and norm it such that max_perSquare - will be in Q15 after the division */ - - totscale = scale1+scale2-1; - tmp1 = (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(cross, (totscale>>1)); - tmp2 = (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(cross, totscale-(totscale>>1)); - - nom = WEBRTC_SPL_MUL_16_16(tmp1, tmp2); - max_perSquare = (WebRtc_Word16)WebRtcSpl_DivW32W16(nom, denom); - - } else { - max_perSquare = 0; - } - } - - /* previous frame lost, use recorded lag and gain */ - - else { - lag = iLBCdec_inst->prevLag; - max_perSquare = iLBCdec_inst->perSquare; - } - - /* Attenuate signal and scale down pitch pred gain if - several frames lost consecutively */ - - use_gain = 32767; /* 1.0 in Q15 */ - - if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320) { - use_gain = 29491; /* 0.9 in Q15 */ - } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>640) { - use_gain = 22938; /* 0.7 in Q15 */ - } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>960) { - use_gain = 16384; /* 0.5 in Q15 */ - } else if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>1280) { - use_gain = 0; /* 0.0 in Q15 */ - } - - /* Compute mixing factor of picth repeatition and noise: - for max_per>0.7 set periodicity to 1.0 - 0.47868) { /* periodicity > 0.7 (0.7^4=0.2401 in Q15) */ - pitchfact = 32767; - } else if (max_perSquare>839) { /* 0.4 < periodicity < 0.7 (0.4^4=0.0256 in Q15) */ - /* find best index and interpolate from that */ - ind = 5; - while ((max_perSquare0)) { - ind--; - } - /* pitch fact is approximated by first order */ - tmpW32 = (WebRtc_Word32)WebRtcIlbcfix_kPlcPitchFact[ind] + - WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIlbcfix_kPlcPfSlope[ind], (max_perSquare-WebRtcIlbcfix_kPlcPerSqr[ind]), 11); - - pitchfact = (WebRtc_Word16)WEBRTC_SPL_MIN(tmpW32, 32767); /* guard against overflow */ - - } else { /* periodicity < 0.4 */ - pitchfact = 0; - } - - /* avoid repetition of same pitch cycle (buzzyness) */ - use_lag = lag; - if (lag<80) { - use_lag = 2*lag; - } - - /* compute concealed residual */ - energy = 0; - - for (i=0; iblockl; i++) { - - /* noise component - 52 < randlagFIX < 117 */ - iLBCdec_inst->seed = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16(iLBCdec_inst->seed, 31821)+(WebRtc_Word32)13849); - randlag = 53 + (WebRtc_Word16)(iLBCdec_inst->seed & 63); - - pick = i - randlag; - - if (pick < 0) { - randvec[i] = iLBCdec_inst->prevResidual[iLBCdec_inst->blockl+pick]; - } else { - randvec[i] = iLBCdec_inst->prevResidual[pick]; - } - - /* pitch repeatition component */ - pick = i - use_lag; - - if (pick < 0) { - PLCresidual[i] = iLBCdec_inst->prevResidual[iLBCdec_inst->blockl+pick]; - } else { - PLCresidual[i] = PLCresidual[pick]; - } - - /* Attinuate total gain for each 10 ms */ - if (i<80) { - tot_gain=use_gain; - } else if (i<160) { - tot_gain=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(31130, use_gain, 15); /* 0.95*use_gain */ - } else { - tot_gain=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(29491, use_gain, 15); /* 0.9*use_gain */ - } - - - /* mix noise and pitch repeatition */ - - PLCresidual[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tot_gain, - (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32( (WEBRTC_SPL_MUL_16_16(pitchfact, PLCresidual[i]) + - WEBRTC_SPL_MUL_16_16((32767-pitchfact), randvec[i]) + 16384), - 15), - 15); - - /* Shifting down the result one step extra to ensure that no overflow - will occur */ - energy += WEBRTC_SPL_MUL_16_16_RSFT(PLCresidual[i], - PLCresidual[i], (iLBCdec_inst->prevScale+1)); - - } - - /* less than 30 dB, use only noise */ - if (energy < (WEBRTC_SPL_SHIFT_W32(((WebRtc_Word32)iLBCdec_inst->blockl*900),-(iLBCdec_inst->prevScale+1)))) { - energy = 0; - for (i=0; iblockl; i++) { - PLCresidual[i] = randvec[i]; - } - } - - /* use the old LPC */ - WEBRTC_SPL_MEMCPY_W16(PLClpc, (*iLBCdec_inst).prevLpc, LPC_FILTERORDER+1); - - /* Update state in case there are multiple frame losses */ - iLBCdec_inst->prevLag = lag; - iLBCdec_inst->perSquare = max_perSquare; - } - - /* no packet loss, copy input */ - - else { - WEBRTC_SPL_MEMCPY_W16(PLCresidual, decresidual, iLBCdec_inst->blockl); - WEBRTC_SPL_MEMCPY_W16(PLClpc, lpc, (LPC_FILTERORDER+1)); - iLBCdec_inst->consPLICount = 0; - } - - /* update state */ - iLBCdec_inst->prevPLI = PLI; - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->prevLpc, PLClpc, (LPC_FILTERORDER+1)); - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->prevResidual, PLCresidual, iLBCdec_inst->blockl); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/do_plc.h b/modules/audio_coding/codecs/iLBC/main/source/do_plc.h deleted file mode 100644 index c5bcc5228..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/do_plc.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_DoThePlc.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DO_PLC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_DO_PLC_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Packet loss concealment routine. Conceals a residual signal - * and LP parameters. If no packet loss, update state. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_DoThePlc( - WebRtc_Word16 *PLCresidual, /* (o) concealed residual */ - WebRtc_Word16 *PLClpc, /* (o) concealed LP parameters */ - WebRtc_Word16 PLI, /* (i) packet loss indicator - 0 - no PL, 1 = PL */ - WebRtc_Word16 *decresidual, /* (i) decoded residual */ - WebRtc_Word16 *lpc, /* (i) decoded LPC (only used for no PL) */ - WebRtc_Word16 inlag, /* (i) pitch lag */ - iLBC_Dec_Inst_t *iLBCdec_inst - /* (i/o) decoder instance */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/encode.c b/modules/audio_coding/codecs/iLBC/main/source/encode.c deleted file mode 100644 index 64e9eab3d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/encode.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Encode.c - -******************************************************************/ - -#include "defines.h" -#include "lpc_encode.h" -#include "frame_classify.h" -#include "state_search.h" -#include "state_construct.h" -#include "constants.h" -#include "cb_search.h" -#include "cb_construct.h" -#include "index_conv_enc.h" -#include "pack_bits.h" -#include "hp_input.h" - -#ifdef SPLIT_10MS -#include "unpack_bits.h" -#include "index_conv_dec.h" -#endif -#ifndef WEBRTC_BIG_ENDIAN -#include "swap_bytes.h" -#endif - -/*----------------------------------------------------------------* - * main encoder function - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_EncodeImpl( - WebRtc_UWord16 *bytes, /* (o) encoded data bits iLBC */ - WebRtc_Word16 *block, /* (i) speech vector to encode */ - iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder - state */ - ){ - int n, meml_gotten, Nfor, Nback; - WebRtc_Word16 diff, start_pos; - int index; - int subcount, subframe; - WebRtc_Word16 start_count, end_count; - WebRtc_Word16 *residual; - WebRtc_Word32 en1, en2; - WebRtc_Word16 scale, max; - WebRtc_Word16 *syntdenum; - WebRtc_Word16 *decresidual; - WebRtc_Word16 *reverseResidual; - WebRtc_Word16 *reverseDecresidual; - /* Stack based */ - WebRtc_Word16 weightdenum[(LPC_FILTERORDER + 1)*NSUB_MAX]; - WebRtc_Word16 dataVec[BLOCKL_MAX + LPC_FILTERORDER]; - WebRtc_Word16 memVec[CB_MEML+CB_FILTERLEN]; - WebRtc_Word16 bitsMemory[sizeof(iLBC_bits)/sizeof(WebRtc_Word16)]; - iLBC_bits *iLBCbits_inst = (iLBC_bits*)bitsMemory; - - -#ifdef SPLIT_10MS - WebRtc_Word16 *weightdenumbuf = iLBCenc_inst->weightdenumbuf; - WebRtc_Word16 last_bit; -#endif - - WebRtc_Word16 *data = &dataVec[LPC_FILTERORDER]; - WebRtc_Word16 *mem = &memVec[CB_HALFFILTERLEN]; - - /* Reuse som buffers to save stack memory */ - residual = &iLBCenc_inst->lpc_buffer[LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl]; - syntdenum = mem; /* syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX] and mem are used non overlapping in the code */ - decresidual = residual; /* Already encoded residual is overwritten by the decoded version */ - reverseResidual = data; /* data and reverseResidual are used non overlapping in the code */ - reverseDecresidual = reverseResidual; /* Already encoded residual is overwritten by the decoded version */ - -#ifdef SPLIT_10MS - - WebRtcSpl_MemSetW16 ( (WebRtc_Word16 *) iLBCbits_inst, 0, - (WebRtc_Word16) (sizeof(iLBC_bits) / sizeof(WebRtc_Word16)) ); - - start_pos = iLBCenc_inst->start_pos; - diff = iLBCenc_inst->diff; - - if (iLBCenc_inst->section != 0){ - WEBRTC_SPL_MEMCPY_W16 (weightdenum, weightdenumbuf, - SCRATCH_ENCODE_DATAVEC - SCRATCH_ENCODE_WEIGHTDENUM); - /* Un-Packetize the frame into parameters */ - last_bit = WebRtcIlbcfix_UnpackBits (iLBCenc_inst->bytes, iLBCbits_inst, iLBCenc_inst->mode); - if (last_bit) - return; - /* adjust index */ - WebRtcIlbcfix_IndexConvDec (iLBCbits_inst->cb_index); - - if (iLBCenc_inst->section == 1){ - /* Save first 80 samples of a 160/240 sample frame for 20/30msec */ - WEBRTC_SPL_MEMCPY_W16 (iLBCenc_inst->past_samples, block, 80); - } - else{ // iLBCenc_inst->section == 2 AND mode = 30ms - /* Save second 80 samples of a 240 sample frame for 30msec */ - WEBRTC_SPL_MEMCPY_W16 (iLBCenc_inst->past_samples + 80, block, 80); - } - } - else{ // iLBCenc_inst->section == 0 - /* form a complete frame of 160/240 for 20msec/30msec mode */ - WEBRTC_SPL_MEMCPY_W16 (data + (iLBCenc_inst->mode * 8) - 80, block, 80); - WEBRTC_SPL_MEMCPY_W16 (data, iLBCenc_inst->past_samples, - (iLBCenc_inst->mode * 8) - 80); - iLBCenc_inst->Nfor_flag = 0; - iLBCenc_inst->Nback_flag = 0; -#else - /* copy input block to data*/ - WEBRTC_SPL_MEMCPY_W16(data,block,iLBCenc_inst->blockl); -#endif - - /* high pass filtering of input signal and scale down the residual (*0.5) */ - WebRtcIlbcfix_HpInput(data, (WebRtc_Word16*)WebRtcIlbcfix_kHpInCoefs, - iLBCenc_inst->hpimemy, iLBCenc_inst->hpimemx, - iLBCenc_inst->blockl); - - /* LPC of hp filtered input data */ - WebRtcIlbcfix_LpcEncode(syntdenum, weightdenum, iLBCbits_inst->lsf, data, - iLBCenc_inst); - - /* Set up state */ - WEBRTC_SPL_MEMCPY_W16(dataVec, iLBCenc_inst->anaMem, LPC_FILTERORDER); - - /* inverse filter to get residual */ - for (n=0; nnsub; n++ ) { - WebRtcSpl_FilterMAFastQ12( - &data[n*SUBL], &residual[n*SUBL], - &syntdenum[n*(LPC_FILTERORDER+1)], - LPC_FILTERORDER+1, SUBL); - } - - /* Copy the state for next frame */ - WEBRTC_SPL_MEMCPY_W16(iLBCenc_inst->anaMem, &data[iLBCenc_inst->blockl-LPC_FILTERORDER], LPC_FILTERORDER); - - /* find state location */ - - iLBCbits_inst->startIdx = WebRtcIlbcfix_FrameClassify(iLBCenc_inst,residual); - - /* check if state should be in first or last part of the - two subframes */ - - index = (iLBCbits_inst->startIdx-1)*SUBL; - max=WebRtcSpl_MaxAbsValueW16(&residual[index], 2*SUBL); - scale=WebRtcSpl_GetSizeInBits(WEBRTC_SPL_MUL_16_16(max,max)); - - /* Scale to maximum 25 bits so that the MAC won't cause overflow */ - scale = scale - 25; - if(scale < 0) { - scale = 0; - } - - diff = STATE_LEN - iLBCenc_inst->state_short_len; - en1=WebRtcSpl_DotProductWithScale(&residual[index], &residual[index], - iLBCenc_inst->state_short_len, scale); - index += diff; - en2=WebRtcSpl_DotProductWithScale(&residual[index], &residual[index], - iLBCenc_inst->state_short_len, scale); - if (en1 > en2) { - iLBCbits_inst->state_first = 1; - start_pos = (iLBCbits_inst->startIdx-1)*SUBL; - } else { - iLBCbits_inst->state_first = 0; - start_pos = (iLBCbits_inst->startIdx-1)*SUBL + diff; - } - - /* scalar quantization of state */ - - WebRtcIlbcfix_StateSearch(iLBCenc_inst, iLBCbits_inst, &residual[start_pos], - &syntdenum[(iLBCbits_inst->startIdx-1)*(LPC_FILTERORDER+1)], - &weightdenum[(iLBCbits_inst->startIdx-1)*(LPC_FILTERORDER+1)]); - - WebRtcIlbcfix_StateConstruct(iLBCbits_inst->idxForMax, iLBCbits_inst->idxVec, - &syntdenum[(iLBCbits_inst->startIdx-1)*(LPC_FILTERORDER+1)], - &decresidual[start_pos], iLBCenc_inst->state_short_len - ); - - /* predictive quantization in state */ - - if (iLBCbits_inst->state_first) { /* put adaptive part in the end */ - - /* setup memory */ - - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-iLBCenc_inst->state_short_len)); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-iLBCenc_inst->state_short_len, - decresidual+start_pos, iLBCenc_inst->state_short_len); - - /* encode subframes */ - - WebRtcIlbcfix_CbSearch(iLBCenc_inst, iLBCbits_inst->cb_index, iLBCbits_inst->gain_index, - &residual[start_pos+iLBCenc_inst->state_short_len], - mem+CB_MEML-ST_MEM_L_TBL, ST_MEM_L_TBL, diff, - &weightdenum[iLBCbits_inst->startIdx*(LPC_FILTERORDER+1)], 0); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct(&decresidual[start_pos+iLBCenc_inst->state_short_len], - iLBCbits_inst->cb_index, iLBCbits_inst->gain_index, - mem+CB_MEML-ST_MEM_L_TBL, ST_MEM_L_TBL, - diff - ); - - } - else { /* put adaptive part in the beginning */ - - /* create reversed vectors for prediction */ - - WebRtcSpl_MemCpyReversedOrder(&reverseResidual[diff-1], - &residual[(iLBCbits_inst->startIdx+1)*SUBL-STATE_LEN], diff); - - /* setup memory */ - - meml_gotten = iLBCenc_inst->state_short_len; - WebRtcSpl_MemCpyReversedOrder(&mem[CB_MEML-1], &decresidual[start_pos], meml_gotten); - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-iLBCenc_inst->state_short_len)); - - /* encode subframes */ - WebRtcIlbcfix_CbSearch(iLBCenc_inst, iLBCbits_inst->cb_index, iLBCbits_inst->gain_index, - reverseResidual, mem+CB_MEML-ST_MEM_L_TBL, ST_MEM_L_TBL, diff, - &weightdenum[(iLBCbits_inst->startIdx-1)*(LPC_FILTERORDER+1)], - 0); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct(reverseDecresidual, - iLBCbits_inst->cb_index, iLBCbits_inst->gain_index, - mem+CB_MEML-ST_MEM_L_TBL, ST_MEM_L_TBL, - diff - ); - - /* get decoded residual from reversed vector */ - - WebRtcSpl_MemCpyReversedOrder(&decresidual[start_pos-1], reverseDecresidual, diff); - } - -#ifdef SPLIT_10MS - iLBCenc_inst->start_pos = start_pos; - iLBCenc_inst->diff = diff; - iLBCenc_inst->section++; - /* adjust index */ - WebRtcIlbcfix_IndexConvEnc (iLBCbits_inst->cb_index); - /* Packetize the parameters into the frame */ - WebRtcIlbcfix_PackBits (iLBCenc_inst->bytes, iLBCbits_inst, iLBCenc_inst->mode); - WEBRTC_SPL_MEMCPY_W16 (weightdenumbuf, weightdenum, - SCRATCH_ENCODE_DATAVEC - SCRATCH_ENCODE_WEIGHTDENUM); - return; - } -#endif - - /* forward prediction of subframes */ - - Nfor = iLBCenc_inst->nsub-iLBCbits_inst->startIdx-1; - - /* counter for predicted subframes */ -#ifdef SPLIT_10MS - if (iLBCenc_inst->mode == 20) - { - subcount = 1; - } - if (iLBCenc_inst->mode == 30) - { - if (iLBCenc_inst->section == 1) - { - subcount = 1; - } - if (iLBCenc_inst->section == 2) - { - subcount = 3; - } - } -#else - subcount=1; -#endif - - if( Nfor > 0 ){ - - /* setup memory */ - - WebRtcSpl_MemSetW16(mem, 0, CB_MEML-STATE_LEN); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-STATE_LEN, - decresidual+(iLBCbits_inst->startIdx-1)*SUBL, STATE_LEN); - -#ifdef SPLIT_10MS - if (iLBCenc_inst->Nfor_flag > 0) - { - for (subframe = 0; subframe < WEBRTC_SPL_MIN (Nfor, 2); subframe++) - { - /* update memory */ - WEBRTC_SPL_MEMCPY_W16 (mem, mem + SUBL, (CB_MEML - SUBL)); - WEBRTC_SPL_MEMCPY_W16 (mem + CB_MEML - SUBL, - &decresidual[(iLBCbits_inst->startIdx + 1 + - subframe) * SUBL], SUBL); - } - } - - iLBCenc_inst->Nfor_flag++; - - if (iLBCenc_inst->mode == 20) - { - start_count = 0; - end_count = Nfor; - } - if (iLBCenc_inst->mode == 30) - { - if (iLBCenc_inst->section == 1) - { - start_count = 0; - end_count = WEBRTC_SPL_MIN (Nfor, 2); - } - if (iLBCenc_inst->section == 2) - { - start_count = WEBRTC_SPL_MIN (Nfor, 2); - end_count = Nfor; - } - } -#else - start_count = 0; - end_count = (WebRtc_Word16)Nfor; -#endif - - /* loop over subframes to encode */ - - for (subframe = start_count; subframe < end_count; subframe++){ - - /* encode subframe */ - - WebRtcIlbcfix_CbSearch(iLBCenc_inst, iLBCbits_inst->cb_index+subcount*CB_NSTAGES, - iLBCbits_inst->gain_index+subcount*CB_NSTAGES, - &residual[(iLBCbits_inst->startIdx+1+subframe)*SUBL], - mem, MEM_LF_TBL, SUBL, - &weightdenum[(iLBCbits_inst->startIdx+1+subframe)*(LPC_FILTERORDER+1)], - (WebRtc_Word16)subcount); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct(&decresidual[(iLBCbits_inst->startIdx+1+subframe)*SUBL], - iLBCbits_inst->cb_index+subcount*CB_NSTAGES, - iLBCbits_inst->gain_index+subcount*CB_NSTAGES, - mem, MEM_LF_TBL, - SUBL - ); - - /* update memory */ - - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, (CB_MEML-SUBL)); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, - &decresidual[(iLBCbits_inst->startIdx+1+subframe)*SUBL], SUBL); - - subcount++; - } - } - -#ifdef SPLIT_10MS - if ((iLBCenc_inst->section == 1) && - (iLBCenc_inst->mode == 30) && (Nfor > 0) && (end_count == 2)) - { - iLBCenc_inst->section++; - /* adjust index */ - WebRtcIlbcfix_IndexConvEnc (iLBCbits_inst->cb_index); - /* Packetize the parameters into the frame */ - WebRtcIlbcfix_PackBits (iLBCenc_inst->bytes, iLBCbits_inst, iLBCenc_inst->mode); - WEBRTC_SPL_MEMCPY_W16 (weightdenumbuf, weightdenum, - SCRATCH_ENCODE_DATAVEC - SCRATCH_ENCODE_WEIGHTDENUM); - return; - } -#endif - - /* backward prediction of subframes */ - - Nback = iLBCbits_inst->startIdx-1; - - if( Nback > 0 ){ - - /* create reverse order vectors - (The decresidual does not need to be copied since it is - contained in the same vector as the residual) - */ - - WebRtcSpl_MemCpyReversedOrder(&reverseResidual[Nback*SUBL-1], residual, Nback*SUBL); - - /* setup memory */ - - meml_gotten = SUBL*(iLBCenc_inst->nsub+1-iLBCbits_inst->startIdx); - if( meml_gotten > CB_MEML ) { - meml_gotten=CB_MEML; - } - - WebRtcSpl_MemCpyReversedOrder(&mem[CB_MEML-1], &decresidual[Nback*SUBL], meml_gotten); - WebRtcSpl_MemSetW16(mem, 0, (WebRtc_Word16)(CB_MEML-meml_gotten)); - -#ifdef SPLIT_10MS - if (iLBCenc_inst->Nback_flag > 0) - { - for (subframe = 0; subframe < WEBRTC_SPL_MAX (2 - Nfor, 0); subframe++) - { - /* update memory */ - WEBRTC_SPL_MEMCPY_W16 (mem, mem + SUBL, (CB_MEML - SUBL)); - WEBRTC_SPL_MEMCPY_W16 (mem + CB_MEML - SUBL, - &reverseDecresidual[subframe * SUBL], SUBL); - } - } - - iLBCenc_inst->Nback_flag++; - - - if (iLBCenc_inst->mode == 20) - { - start_count = 0; - end_count = Nback; - } - if (iLBCenc_inst->mode == 30) - { - if (iLBCenc_inst->section == 1) - { - start_count = 0; - end_count = WEBRTC_SPL_MAX (2 - Nfor, 0); - } - if (iLBCenc_inst->section == 2) - { - start_count = WEBRTC_SPL_MAX (2 - Nfor, 0); - end_count = Nback; - } - } -#else - start_count = 0; - end_count = (WebRtc_Word16)Nback; -#endif - - /* loop over subframes to encode */ - - for (subframe = start_count; subframe < end_count; subframe++){ - - /* encode subframe */ - - WebRtcIlbcfix_CbSearch(iLBCenc_inst, iLBCbits_inst->cb_index+subcount*CB_NSTAGES, - iLBCbits_inst->gain_index+subcount*CB_NSTAGES, &reverseResidual[subframe*SUBL], - mem, MEM_LF_TBL, SUBL, - &weightdenum[(iLBCbits_inst->startIdx-2-subframe)*(LPC_FILTERORDER+1)], - (WebRtc_Word16)subcount); - - /* construct decoded vector */ - - WebRtcIlbcfix_CbConstruct(&reverseDecresidual[subframe*SUBL], - iLBCbits_inst->cb_index+subcount*CB_NSTAGES, - iLBCbits_inst->gain_index+subcount*CB_NSTAGES, - mem, MEM_LF_TBL, SUBL - ); - - /* update memory */ - - WEBRTC_SPL_MEMMOVE_W16(mem, mem+SUBL, (CB_MEML-SUBL)); - WEBRTC_SPL_MEMCPY_W16(mem+CB_MEML-SUBL, - &reverseDecresidual[subframe*SUBL], SUBL); - - subcount++; - - } - - /* get decoded residual from reversed vector */ - - WebRtcSpl_MemCpyReversedOrder(&decresidual[SUBL*Nback-1], reverseDecresidual, SUBL*Nback); - } - /* end encoding part */ - - /* adjust index */ - - WebRtcIlbcfix_IndexConvEnc(iLBCbits_inst->cb_index); - - /* Packetize the parameters into the frame */ - -#ifdef SPLIT_10MS - if( (iLBCenc_inst->mode==30) && (iLBCenc_inst->section==1) ){ - WebRtcIlbcfix_PackBits(iLBCenc_inst->bytes, iLBCbits_inst, iLBCenc_inst->mode); - } - else{ - WebRtcIlbcfix_PackBits(bytes, iLBCbits_inst, iLBCenc_inst->mode); - } -#else - WebRtcIlbcfix_PackBits(bytes, iLBCbits_inst, iLBCenc_inst->mode); -#endif - -#ifndef WEBRTC_BIG_ENDIAN - /* Swap bytes for LITTLE ENDIAN since the packbits() - function assumes BIG_ENDIAN machine */ -#ifdef SPLIT_10MS - if (( (iLBCenc_inst->section == 1) && (iLBCenc_inst->mode == 20) ) || - ( (iLBCenc_inst->section == 2) && (iLBCenc_inst->mode == 30) )){ - WebRtcIlbcfix_SwapBytes(bytes, iLBCenc_inst->no_of_words); - } -#else - WebRtcIlbcfix_SwapBytes(bytes, iLBCenc_inst->no_of_words); -#endif -#endif - -#ifdef SPLIT_10MS - if (subcount == (iLBCenc_inst->nsub - 1)) - { - iLBCenc_inst->section = 0; - } - else - { - iLBCenc_inst->section++; - WEBRTC_SPL_MEMCPY_W16 (weightdenumbuf, weightdenum, - SCRATCH_ENCODE_DATAVEC - SCRATCH_ENCODE_WEIGHTDENUM); - } -#endif - -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/encode.h b/modules/audio_coding/codecs/iLBC/main/source/encode.h deleted file mode 100644 index b553f0ce0..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/encode.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Encode.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ENCODE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_ENCODE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * main encoder function - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_EncodeImpl( - WebRtc_UWord16 *bytes, /* (o) encoded data bits iLBC */ - WebRtc_Word16 *block, /* (i) speech vector to encode */ - iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder - state */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/energy_inverse.c b/modules/audio_coding/codecs/iLBC/main/source/energy_inverse.c deleted file mode 100644 index d56069b02..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/energy_inverse.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_EnergyInverse.c - -******************************************************************/ - -/* Inverses the in vector in into Q29 domain */ - -#include "energy_inverse.h" - -void WebRtcIlbcfix_EnergyInverse( - WebRtc_Word16 *energy, /* (i/o) Energy and inverse - energy (in Q29) */ - int noOfEnergies) /* (i) The length of the energy - vector */ -{ - WebRtc_Word32 Nom=(WebRtc_Word32)0x1FFFFFFF; - WebRtc_Word16 *energyPtr; - int i; - - /* Set the minimum energy value to 16384 to avoid overflow */ - energyPtr=energy; - for (i=0; iblockl+120; - WebRtc_Word16 scale, scale1, plc_blockl; - WebRtc_Word16 *enh_buf, *enh_period; - WebRtc_Word32 tmp1, tmp2, max, new_blocks; - WebRtc_Word16 *enh_bufPtr1; - int i, k; - WebRtc_Word16 EnChange; - WebRtc_Word16 SqrtEnChange; - WebRtc_Word16 inc; - WebRtc_Word16 win; - WebRtc_Word16 *tmpW16ptr; - WebRtc_Word16 startPos; - WebRtc_Word16 *plc_pred; - WebRtc_Word16 *target, *regressor; - WebRtc_Word16 max16; - int shifts; - WebRtc_Word32 ener; - WebRtc_Word16 enerSh; - WebRtc_Word16 corrSh; - WebRtc_Word16 ind, sh; - WebRtc_Word16 start, stop; - /* Stack based */ - WebRtc_Word16 totsh[3]; - WebRtc_Word16 downsampled[(BLOCKL_MAX+120)>>1]; /* length 180 */ - WebRtc_Word32 corr32[50]; - WebRtc_Word32 corrmax[3]; - WebRtc_Word16 corr16[3]; - WebRtc_Word16 en16[3]; - WebRtc_Word16 lagmax[3]; - - plc_pred = downsampled; /* Reuse memory since plc_pred[ENH_BLOCKL] and downsampled are non overlapping */ - enh_buf=iLBCdec_inst->enh_buf; - enh_period=iLBCdec_inst->enh_period; - - /* Copy in the new data into the enhancer buffer */ - - WEBRTC_SPL_MEMMOVE_W16(enh_buf, &enh_buf[iLBCdec_inst->blockl], - ENH_BUFL-iLBCdec_inst->blockl); - - WEBRTC_SPL_MEMCPY_W16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in, - iLBCdec_inst->blockl); - - /* Set variables that are dependent on frame size */ - if (iLBCdec_inst->mode==30) { - plc_blockl=ENH_BLOCKL; - new_blocks=3; - startPos=320; /* Start position for enhancement (640-new_blocks*ENH_BLOCKL-80) */ - } else { - plc_blockl=40; - new_blocks=2; - startPos=440; /* Start position for enhancement (640-new_blocks*ENH_BLOCKL-40) */ - } - - /* Update the pitch prediction for each enhancer block, move the old ones */ - WEBRTC_SPL_MEMMOVE_W16(enh_period, &enh_period[new_blocks], (ENH_NBLOCKS_TOT-new_blocks)); - - k=WebRtcSpl_DownsampleFast( - enh_buf+ENH_BUFL-inLen, /* Input samples */ - (WebRtc_Word16)(inLen+ENH_BUFL_FILTEROVERHEAD), - downsampled, - (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(inLen, 1), - (WebRtc_Word16*)WebRtcIlbcfix_kLpFiltCoefs, /* Coefficients in Q12 */ - FILTERORDER_DS_PLUS1, /* Length of filter (order-1) */ - FACTOR_DS, - DELAY_DS); - - /* Estimate the pitch in the down sampled domain. */ - for(iblock = 0; iblock totsh[i]) { - sh = WEBRTC_SPL_MIN(31, totsh[ind]-totsh[i]); - if ( WEBRTC_SPL_MUL_16_16(corr16[ind], en16[i]) < WEBRTC_SPL_MUL_16_16_RSFT(corr16[i], en16[ind], sh)) { - ind = i; - } - } else { - sh = WEBRTC_SPL_MIN(31, totsh[i]-totsh[ind]); - if (WEBRTC_SPL_MUL_16_16_RSFT(corr16[ind], en16[i], sh) < WEBRTC_SPL_MUL_16_16(corr16[i], en16[ind])) { - ind = i; - } - } - } - - lag = lagmax[ind] + 10; - - /* Store the estimated lag in the non-downsampled domain */ - enh_period[ENH_NBLOCKS_TOT-new_blocks+iblock] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(lag, 8); - - /* Store the estimated lag for backward PLC */ - if (iLBCdec_inst->prev_enh_pl==1) { - if (!iblock) { - tlag = WEBRTC_SPL_MUL_16_16(lag, 2); - } - } else { - if (iblock==1) { - tlag = WEBRTC_SPL_MUL_16_16(lag, 2); - } - } - - lag = WEBRTC_SPL_MUL_16_16(lag, 2); - } - - if ((iLBCdec_inst->prev_enh_pl==1)||(iLBCdec_inst->prev_enh_pl==2)) { - - /* Calculate the best lag of the new frame - This is used to interpolate backwards and mix with the PLC'd data - */ - - /* references */ - target=in; - regressor=in+tlag-1; - - /* scaling */ - max16=WebRtcSpl_MaxAbsValueW16(regressor, (WebRtc_Word16)(plc_blockl+3-1)); - if (max16>5000) - shifts=2; - else - shifts=0; - - /* compute cross correlation */ - WebRtcSpl_CrossCorrelation(corr32, target, regressor, - plc_blockl, 3, (WebRtc_Word16)shifts, 1); - - /* find lag */ - lag=WebRtcSpl_MaxIndexW32(corr32, 3); - lag+=tlag-1; - - /* Copy the backward PLC to plc_pred */ - - if (iLBCdec_inst->prev_enh_pl==1) { - if (lag>plc_blockl) { - WEBRTC_SPL_MEMCPY_W16(plc_pred, &in[lag-plc_blockl], plc_blockl); - } else { - WEBRTC_SPL_MEMCPY_W16(&plc_pred[plc_blockl-lag], in, lag); - WEBRTC_SPL_MEMCPY_W16(plc_pred, &enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl+lag], (plc_blockl-lag)); - } - } else { - int pos; - - pos = plc_blockl; - - while (lagprev_enh_pl==1) { - /* limit energy change - if energy in backward PLC is more than 4 times higher than the forward PLC, - then reduce the energy in the backward PLC vector: - sample 1...len-16 set energy of the to 4 times forward PLC - sample len-15..len interpolate between 4 times fw PLC and bw PLC energy - - Note: Compared to floating point code there is a slight change, - the window is 16 samples long instead of 10 samples to simplify the calculations - */ - - max=WebRtcSpl_MaxAbsValueW16(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], plc_blockl); - max16=WebRtcSpl_MaxAbsValueW16(plc_pred, plc_blockl); - max = WEBRTC_SPL_MAX(max, max16); - scale=22-(WebRtc_Word16)WebRtcSpl_NormW32(max); - scale=WEBRTC_SPL_MAX(scale,0); - - tmp2 = WebRtcSpl_DotProductWithScale(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], &enh_buf[ENH_BUFL-iLBCdec_inst->blockl-plc_blockl], plc_blockl, scale); - tmp1 = WebRtcSpl_DotProductWithScale(plc_pred, plc_pred, plc_blockl, scale); - - /* Check the energy difference */ - if ((tmp1>0)&&((tmp1>>2)>tmp2)) { - /* EnChange is now guaranteed to be <0.5 - Calculate EnChange=tmp2/tmp1 in Q16 - */ - - scale1=(WebRtc_Word16)WebRtcSpl_NormW32(tmp1); - tmp1=WEBRTC_SPL_SHIFT_W32(tmp1, (scale1-16)); /* using 15 bits */ - - tmp2=WEBRTC_SPL_SHIFT_W32(tmp2, (scale1)); - EnChange = (WebRtc_Word16)WebRtcSpl_DivW32W16(tmp2, (WebRtc_Word16)tmp1); - - /* Calculate the Sqrt of the energy in Q15 ((14+16)/2) */ - SqrtEnChange = (WebRtc_Word16)WebRtcSpl_Sqrt(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)EnChange, 14)); - - /* Multiply first part of vector with 2*SqrtEnChange */ - WebRtcSpl_ScaleVector(plc_pred, plc_pred, SqrtEnChange, (WebRtc_Word16)(plc_blockl-16), 14); - - /* Calculate increase parameter for window part (16 last samples) */ - inc=(2048-WEBRTC_SPL_RSHIFT_W16(SqrtEnChange, 3)); /* (1-2*SqrtEnChange)/16 in Q15 */ - - win=0; - tmpW16ptr=&plc_pred[plc_blockl-16]; - - for (i=16;i>0;i--) { - (*tmpW16ptr)=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((*tmpW16ptr), - (SqrtEnChange+(win>>1)), 14); /* multiply by (2.0*SqrtEnChange+win) */ - - win += inc; - tmpW16ptr++; - } - } - - /* Make the linear interpolation between the forward PLC'd data - and the backward PLC'd data (from the new frame) - */ - - if (plc_blockl==40) { - inc=400; /* 1/41 in Q14 */ - } else { /* plc_blockl==80 */ - inc=202; /* 1/81 in Q14 */ - } - win=0; - enh_bufPtr1=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl]; - for (i=0; iblockl-plc_blockl]; - WEBRTC_SPL_MEMCPY_W16(enh_bufPtr1, plc_pred, plc_blockl); - - /* Clear fileter memory */ - WebRtcSpl_MemSetW16(iLBCdec_inst->syntMem, 0, LPC_FILTERORDER); - WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemy, 0, 4); - WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemx, 0, 2); - - /* Initialize filter memory by filtering through 2 lags */ - WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], iLBCdec_inst->syntMem, LPC_FILTERORDER); - WebRtcSpl_FilterARFastQ12( - enh_bufPtr1, synt, - &iLBCdec_inst->old_syntdenum[(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)], LPC_FILTERORDER+1, (WebRtc_Word16)lag); - - WEBRTC_SPL_MEMCPY_W16(&synt[-LPC_FILTERORDER], &synt[lag-LPC_FILTERORDER], LPC_FILTERORDER); - WebRtcIlbcfix_HpOutput(synt, (WebRtc_Word16*)WebRtcIlbcfix_kHpOutCoefs, - iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx, - (WebRtc_Word16)lag); - WebRtcSpl_FilterARFastQ12( - enh_bufPtr1, synt, - &iLBCdec_inst->old_syntdenum[(iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1)], LPC_FILTERORDER+1, (WebRtc_Word16)lag); - - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->syntMem, &synt[lag-LPC_FILTERORDER], LPC_FILTERORDER); - WebRtcIlbcfix_HpOutput(synt, (WebRtc_Word16*)WebRtcIlbcfix_kHpOutCoefs, - iLBCdec_inst->hpimemy, iLBCdec_inst->hpimemx, - (WebRtc_Word16)lag); - } - } - - - /* Perform enhancement block by block */ - - for (iblock = 0; iblockblockl); - scale=WebRtcSpl_GetSizeInBits(WEBRTC_SPL_MUL_16_16(max,max)); - - /* Scale to maximum 24 bits so that it won't overflow for 76 samples */ - scale = scale-24; - scale1 = WEBRTC_SPL_MAX(0, scale); - - /* Calculate energies */ - ssqPtr=residualFIX + 2; - seqEnPtr=ssqEn; - for (n=(iLBCenc_inst->nsub-1); n>0; n--) { - (*seqEnPtr) = WebRtcSpl_DotProductWithScale(ssqPtr, ssqPtr, 76, scale1); - ssqPtr += 40; - seqEnPtr++; - } - - /* Scale to maximum 20 bits in order to allow for the 11 bit window */ - maxW32 = WebRtcSpl_MaxValueW32(ssqEn, (WebRtc_Word16)(iLBCenc_inst->nsub-1)); - scale = WebRtcSpl_GetSizeInBits(maxW32) - 20; - scale1 = WEBRTC_SPL_MAX(0, scale); - - /* Window each 80 block with the ssqEn_winTbl window to give higher probability for - the blocks in the middle - */ - seqEnPtr=ssqEn; - if (iLBCenc_inst->mode==20) { - ssqPtr=(WebRtc_Word16*)WebRtcIlbcfix_kStartSequenceEnrgWin+1; - } else { - ssqPtr=(WebRtc_Word16*)WebRtcIlbcfix_kStartSequenceEnrgWin; - } - for (n=(iLBCenc_inst->nsub-1); n>0; n--) { - (*seqEnPtr)=WEBRTC_SPL_MUL(((*seqEnPtr)>>scale1), (*ssqPtr)); - seqEnPtr++; - ssqPtr++; - } - - /* Extract the best choise of start state */ - pos = WebRtcSpl_MaxIndexW32(ssqEn, (WebRtc_Word16)(iLBCenc_inst->nsub-1)) + 1; - - return(pos); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/frame_classify.h b/modules/audio_coding/codecs/iLBC/main/source/frame_classify.h deleted file mode 100644 index faf466667..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/frame_classify.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_FrameClassify.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_FRAME_CLASSIFY_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_FRAME_CLASSIFY_H_ - -WebRtc_Word16 WebRtcIlbcfix_FrameClassify( - /* (o) Index to the max-energy sub frame */ - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i/o) the encoder state structure */ - WebRtc_Word16 *residualFIX /* (i) lpc residual signal */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.c b/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.c deleted file mode 100644 index 9450a800b..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GainDequant.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * decoder for quantized gains in the gain-shape coding of - * residual - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_GainDequant( - /* (o) quantized gain value (Q14) */ - WebRtc_Word16 index, /* (i) quantization index */ - WebRtc_Word16 maxIn, /* (i) maximum of unquantized gain (Q14) */ - WebRtc_Word16 stage /* (i) The stage of the search */ - ){ - WebRtc_Word16 scale; - const WebRtc_Word16 *gain; - - /* obtain correct scale factor */ - - scale=WEBRTC_SPL_ABS_W16(maxIn); - scale = WEBRTC_SPL_MAX(1638, scale); /* if lower than 0.1, set it to 0.1 */ - - /* select the quantization table and return the decoded value */ - gain = WebRtcIlbcfix_kGain[stage]; - - return((WebRtc_Word16)((WEBRTC_SPL_MUL_16_16(scale, gain[index])+8192)>>14)); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.h b/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.h deleted file mode 100644 index 28f2ceb29..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/gain_dequant.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GainDequant.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GAIN_DEQUANT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GAIN_DEQUANT_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * decoder for quantized gains in the gain-shape coding of - * residual - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_GainDequant( - /* (o) quantized gain value (Q14) */ - WebRtc_Word16 index, /* (i) quantization index */ - WebRtc_Word16 maxIn, /* (i) maximum of unquantized gain (Q14) */ - WebRtc_Word16 stage /* (i) The stage of the search */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/gain_quant.c b/modules/audio_coding/codecs/iLBC/main/source/gain_quant.c deleted file mode 100644 index bdf88a5c9..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/gain_quant.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GainQuant.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * quantizer for the gain in the gain-shape coding of residual - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_GainQuant( /* (o) quantized gain value */ - WebRtc_Word16 gain, /* (i) gain value Q14 */ - WebRtc_Word16 maxIn, /* (i) maximum of gain value Q14 */ - WebRtc_Word16 stage, /* (i) The stage of the search */ - WebRtc_Word16 *index /* (o) quantization index */ - ) { - - WebRtc_Word16 scale, returnVal, cblen; - WebRtc_Word32 gainW32, measure1, measure2; - const WebRtc_Word16 *cbPtr, *cb; - int loc, noMoves, noChecks, i; - - /* ensure a lower bound (0.1) on the scaling factor */ - - scale = WEBRTC_SPL_MAX(1638, maxIn); - - /* select the quantization table and calculate - the length of the table and the number of - steps in the binary search that are needed */ - cb = WebRtcIlbcfix_kGain[stage]; - cblen = 32>>stage; - noChecks = 4-stage; - - /* Multiply the gain with 2^14 to make the comparison - easier and with higher precision */ - gainW32 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)gain, 14); - - /* Do a binary search, starting in the middle of the CB - loc - defines the current position in the table - noMoves - defines the number of steps to move in the CB in order - to get next CB location - */ - - loc = cblen>>1; - noMoves = loc; - cbPtr = cb + loc; /* Centre of CB */ - - for (i=noChecks;i>0;i--) { - noMoves>>=1; - measure1=WEBRTC_SPL_MUL_16_16(scale, (*cbPtr)); - - /* Move up if gain is larger, otherwise move down in table */ - measure1 = measure1 - gainW32; - - if (0>measure1) { - cbPtr+=noMoves; - loc+=noMoves; - } else { - cbPtr-=noMoves; - loc-=noMoves; - } - } - - /* Check which value is the closest one: loc-1, loc or loc+1 */ - - measure1=WEBRTC_SPL_MUL_16_16(scale, (*cbPtr)); - if (gainW32>measure1) { - /* Check against value above loc */ - measure2=WEBRTC_SPL_MUL_16_16(scale, (*(cbPtr+1))); - if ((measure2-gainW32)<(gainW32-measure1)) { - loc+=1; - } - } else { - /* Check against value below loc */ - measure2=WEBRTC_SPL_MUL_16_16(scale, (*(cbPtr-1))); - if ((gainW32-measure2)<=(measure1-gainW32)) { - loc-=1; - } - } - - /* Guard against getting outside the table. The calculation above can give a location - which is one above the maximum value (in very rare cases) */ - loc=WEBRTC_SPL_MIN(loc, (cblen-1)); - *index=loc; - - /* Calculate the quantized gain value (in Q14) */ - returnVal=(WebRtc_Word16)((WEBRTC_SPL_MUL_16_16(scale, cb[loc])+8192)>>14); - - /* return the quantized value */ - return(returnVal); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/gain_quant.h b/modules/audio_coding/codecs/iLBC/main/source/gain_quant.h deleted file mode 100644 index a2f059697..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/gain_quant.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GainQuant.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GAIN_QUANT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GAIN_QUANT_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * quantizer for the gain in the gain-shape coding of residual - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_GainQuant( /* (o) quantized gain value */ - WebRtc_Word16 gain, /* (i) gain value Q14 */ - WebRtc_Word16 maxIn, /* (i) maximum of gain value Q14 */ - WebRtc_Word16 stage, /* (i) The stage of the search */ - WebRtc_Word16 *index /* (o) quantization index */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/get_cd_vec.c b/modules/audio_coding/codecs/iLBC/main/source/get_cd_vec.c deleted file mode 100644 index aba3e31fa..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/get_cd_vec.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GetCbVec.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "create_augmented_vec.h" - -/*----------------------------------------------------------------* - * Construct codebook vector for given index. - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_GetCbVec( - WebRtc_Word16 *cbvec, /* (o) Constructed codebook vector */ - WebRtc_Word16 *mem, /* (i) Codebook buffer */ - WebRtc_Word16 index, /* (i) Codebook index */ - WebRtc_Word16 lMem, /* (i) Length of codebook buffer */ - WebRtc_Word16 cbveclen /* (i) Codebook vector length */ - ){ - WebRtc_Word16 k, base_size; - WebRtc_Word16 lag; - /* Stack based */ - WebRtc_Word16 tempbuff2[SUBL+5]; - - /* Determine size of codebook sections */ - - base_size=lMem-cbveclen+1; - - if (cbveclen==SUBL) { - base_size+=WEBRTC_SPL_RSHIFT_W16(cbveclen,1); - } - - /* No filter -> First codebook section */ - - if (index=2; j--) { - * f[j] = f[j] + tmp*f[j-1] + f[j-2]; - * } - * f[i] = f[i] + tmp; - * } - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_GetLspPoly( - WebRtc_Word16 *lsp, /* (i) LSP in Q15 */ - WebRtc_Word32 *f) /* (o) polonymial in Q24 */ -{ - WebRtc_Word32 tmpW32; - int i, j; - WebRtc_Word16 high, low; - WebRtc_Word16 *lspPtr; - WebRtc_Word32 *fPtr; - - lspPtr = lsp; - fPtr = f; - /* f[0] = 1.0 (Q24) */ - (*fPtr) = (WebRtc_Word32)16777216; - fPtr++; - - (*fPtr) = WEBRTC_SPL_MUL((*lspPtr), -1024); - fPtr++; - lspPtr+=2; - - for(i=2; i<=5; i++) - { - (*fPtr) = fPtr[-2]; - - for(j=i; j>1; j--) - { - /* Compute f[j] = f[j] + tmp*f[j-1] + f[j-2]; */ - high = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(fPtr[-1], 16); - low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(fPtr[-1]-WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)high),16), 1); - - tmpW32 = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(high, (*lspPtr)), 2) + - WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16_RSFT(low, (*lspPtr), 15), 2); - - (*fPtr) += fPtr[-2]; - (*fPtr) -= tmpW32; - fPtr--; - } - (*fPtr) -= (WebRtc_Word32)WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)(*lspPtr), 10); - - fPtr+=i; - lspPtr+=2; - } - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/get_lsp_poly.h b/modules/audio_coding/codecs/iLBC/main/source/get_lsp_poly.h deleted file mode 100644 index b0520b4cd..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/get_lsp_poly.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GetLspPoly.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GET_LSP_POLY_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_GET_LSP_POLY_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Construct the polynomials F1(z) and F2(z) from the LSP - * (Computations are done in Q24) - * - * The expansion is performed using the following recursion: - * - * f[0] = 1; - * tmp = -2.0 * lsp[0]; - * f[1] = tmp; - * for (i=2; i<=5; i++) { - * b = -2.0 * lsp[2*i-2]; - * f[i] = tmp*f[i-1] + 2.0*f[i-2]; - * for (j=i; j>=2; j--) { - * f[j] = f[j] + tmp*f[j-1] + f[j-2]; - * } - * f[i] = f[i] + tmp; - * } - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_GetLspPoly( - WebRtc_Word16 *lsp, /* (i) LSP in Q15 */ - WebRtc_Word32 *f); /* (o) polonymial in Q24 */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/get_sync_seq.c b/modules/audio_coding/codecs/iLBC/main/source/get_sync_seq.c deleted file mode 100644 index 4f13cf3d0..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/get_sync_seq.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_GetSyncSeq.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "refiner.h" -#include "nearest_neighbor.h" - -/*----------------------------------------------------------------* - * get the pitch-synchronous sample sequence - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_GetSyncSeq( - iLBC_Dec_Inst_t *iLBCdec_inst, - /* (i) Decoder state */ - WebRtc_Word16 *idata, /* (i) original data */ - WebRtc_Word16 idatal, /* (i) dimension of data */ - WebRtc_Word16 centerStartPos, /* (i) where current block starts */ - WebRtc_Word16 *period, /* (i) rough-pitch-period array (Q-2) */ - WebRtc_Word16 *plocs, /* (i) where periods of period array are taken (Q-2) */ - WebRtc_Word16 periodl, /* (i) dimension period array */ - WebRtc_Word16 hl, /* (i) 2*hl+1 is the number of sequences */ - WebRtc_Word16 *surround /* (i/o) The contribution from this sequence - summed with earlier contributions */ - ){ - WebRtc_Word16 i,centerEndPos,q; - /* Stack based */ - WebRtc_Word16 lagBlock[2*ENH_HL+1]; - WebRtc_Word16 blockStartPos[2*ENH_HL+1]; /* Defines the position to search around (Q2) */ - WebRtc_Word16 plocs2[ENH_PLOCSL]; - - centerEndPos=centerStartPos+ENH_BLOCKL-1; - - /* present (find predicted lag from this position) */ - - WebRtcIlbcfix_NearestNeighbor(iLBCdec_inst, lagBlock+hl,plocs, - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(2, (centerStartPos+centerEndPos)), - periodl); - - blockStartPos[hl]=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16(4, centerStartPos); - - /* past (find predicted position and perform a refined - search to find the best sequence) */ - - for(q=hl-1;q>=0;q--) { - blockStartPos[q]=blockStartPos[q+1]-period[lagBlock[q+1]]; - - WebRtcIlbcfix_NearestNeighbor(iLBCdec_inst, lagBlock+q, plocs, - (WebRtc_Word16)(blockStartPos[q] + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(4, ENH_BLOCKL_HALF)-period[lagBlock[q+1]]), - periodl); - - if((blockStartPos[q]-(WebRtc_Word16)WEBRTC_SPL_MUL_16_16(4, ENH_OVERHANG))>=0) { - - /* Find the best possible sequence in the 4 times upsampled - domain around blockStartPos+q */ - WebRtcIlbcfix_Refiner(iLBCdec_inst, blockStartPos+q,idata,idatal, - centerStartPos,blockStartPos[q],surround,WebRtcIlbcfix_kEnhWt[q]); - - } else { - /* Don't add anything since this sequence would - be outside the buffer */ - } - } - - /* future (find predicted position and perform a refined - search to find the best sequence) */ - - for(i=0;i>15); - tmpW32 += WEBRTC_SPL_MUL_16_16(y[0], ba[3]); /* (-a[1])*y[i-1] (high part) */ - tmpW32 += WEBRTC_SPL_MUL_16_16(y[2], ba[4]); /* (-a[2])*y[i-2] (high part) */ - tmpW32 = (tmpW32<<1); - - tmpW32 += WEBRTC_SPL_MUL_16_16(signal[i], ba[0]); /* b[0]*x[0] */ - tmpW32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]); /* b[1]*x[i-1] */ - tmpW32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]); /* b[2]*x[i-2] */ - - /* Update state (input part) */ - x[1] = x[0]; - x[0] = signal[i]; - - /* Rounding in Q(12+1), i.e. add 2^12 */ - tmpW32b = tmpW32 + 4096; - - /* Saturate (to 2^28) so that the HP filtered signal does not overflow */ - tmpW32b = WEBRTC_SPL_SAT((WebRtc_Word32)268435455, tmpW32b, (WebRtc_Word32)-268435456); - - /* Convert back to Q0 and multiply with 0.5 */ - signal[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmpW32b, 13); - - /* Update state (filtered part) */ - y[2] = y[0]; - y[3] = y[1]; - - /* upshift tmpW32 by 3 with saturation */ - if (tmpW32>268435455) { - tmpW32 = WEBRTC_SPL_WORD32_MAX; - } else if (tmpW32<-268435456) { - tmpW32 = WEBRTC_SPL_WORD32_MIN; - } else { - tmpW32 = WEBRTC_SPL_LSHIFT_W32(tmpW32, 3); - } - - y[0] = (WebRtc_Word16)(tmpW32 >> 16); - y[1] = (WebRtc_Word16)((tmpW32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)y[0], 16))>>1); - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/hp_input.h b/modules/audio_coding/codecs/iLBC/main/source/hp_input.h deleted file mode 100644 index f56c4f734..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/hp_input.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_HpInput.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_HP_INPUT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_HP_INPUT_H_ - -#include "defines.h" - -void WebRtcIlbcfix_HpInput( - WebRtc_Word16 *signal, /* (i/o) signal vector */ - WebRtc_Word16 *ba, /* (i) B- and A-coefficients (2:nd order) - {b[0] b[1] b[2] -a[1] -a[2]} a[0] - is assumed to be 1.0 */ - WebRtc_Word16 *y, /* (i/o) Filter state yhi[n-1] ylow[n-1] - yhi[n-2] ylow[n-2] */ - WebRtc_Word16 *x, /* (i/o) Filter state x[n-1] x[n-2] */ - WebRtc_Word16 len); /* (i) Number of samples to filter */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/hp_output.c b/modules/audio_coding/codecs/iLBC/main/source/hp_output.c deleted file mode 100644 index 8e1c9191a..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/hp_output.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_HpOutput.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * high-pass filter of output and *2 with saturation - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_HpOutput( - WebRtc_Word16 *signal, /* (i/o) signal vector */ - WebRtc_Word16 *ba, /* (i) B- and A-coefficients (2:nd order) - {b[0] b[1] b[2] -a[1] -a[2]} a[0] - is assumed to be 1.0 */ - WebRtc_Word16 *y, /* (i/o) Filter state yhi[n-1] ylow[n-1] - yhi[n-2] ylow[n-2] */ - WebRtc_Word16 *x, /* (i/o) Filter state x[n-1] x[n-2] */ - WebRtc_Word16 len) /* (i) Number of samples to filter */ -{ - int i; - WebRtc_Word32 tmpW32; - WebRtc_Word32 tmpW32b; - - for (i=0; i>15); - tmpW32 += WEBRTC_SPL_MUL_16_16(y[0], ba[3]); /* (-a[1])*y[i-1] (high part) */ - tmpW32 += WEBRTC_SPL_MUL_16_16(y[2], ba[4]); /* (-a[2])*y[i-2] (high part) */ - tmpW32 = (tmpW32<<1); - - tmpW32 += WEBRTC_SPL_MUL_16_16(signal[i], ba[0]); /* b[0]*x[0] */ - tmpW32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]); /* b[1]*x[i-1] */ - tmpW32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]); /* b[2]*x[i-2] */ - - /* Update state (input part) */ - x[1] = x[0]; - x[0] = signal[i]; - - /* Rounding in Q(12-1), i.e. add 2^10 */ - tmpW32b = tmpW32 + 1024; - - /* Saturate (to 2^26) so that the HP filtered signal does not overflow */ - tmpW32b = WEBRTC_SPL_SAT((WebRtc_Word32)67108863, tmpW32b, (WebRtc_Word32)-67108864); - - /* Convert back to Q0 and multiply with 2 */ - signal[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmpW32b, 11); - - /* Update state (filtered part) */ - y[2] = y[0]; - y[3] = y[1]; - - /* upshift tmpW32 by 3 with saturation */ - if (tmpW32>268435455) { - tmpW32 = WEBRTC_SPL_WORD32_MAX; - } else if (tmpW32<-268435456) { - tmpW32 = WEBRTC_SPL_WORD32_MIN; - } else { - tmpW32 = WEBRTC_SPL_LSHIFT_W32(tmpW32, 3); - } - - y[0] = (WebRtc_Word16)(tmpW32 >> 16); - y[1] = (WebRtc_Word16)((tmpW32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)y[0], 16))>>1); - - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/hp_output.h b/modules/audio_coding/codecs/iLBC/main/source/hp_output.h deleted file mode 100644 index c9a7426e6..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/hp_output.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_HpOutput.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_HP_OUTPUT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_HP_OUTPUT_H_ - -#include "defines.h" - -void WebRtcIlbcfix_HpOutput( - WebRtc_Word16 *signal, /* (i/o) signal vector */ - WebRtc_Word16 *ba, /* (i) B- and A-coefficients (2:nd order) - {b[0] b[1] b[2] -a[1] -a[2]} a[0] - is assumed to be 1.0 */ - WebRtc_Word16 *y, /* (i/o) Filter state yhi[n-1] ylow[n-1] - yhi[n-2] ylow[n-2] */ - WebRtc_Word16 *x, /* (i/o) Filter state x[n-1] x[n-2] */ - WebRtc_Word16 len); /* (i) Number of samples to filter */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/ilbc.c b/modules/audio_coding/codecs/iLBC/main/source/ilbc.c deleted file mode 100644 index e479d3e0b..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/ilbc.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBCInterface.c - -******************************************************************/ - -#include "ilbc.h" -#include "defines.h" -#include "init_encode.h" -#include "encode.h" -#include "init_decode.h" -#include "decode.h" -#include - - -WebRtc_Word16 WebRtcIlbcfix_EncoderAssign(iLBC_encinst_t **iLBC_encinst, WebRtc_Word16 *ILBCENC_inst_Addr, WebRtc_Word16 *size) { - *iLBC_encinst=(iLBC_encinst_t*)ILBCENC_inst_Addr; - *size=sizeof(iLBC_Enc_Inst_t)/sizeof(WebRtc_Word16); - if (*iLBC_encinst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcIlbcfix_DecoderAssign(iLBC_decinst_t **iLBC_decinst, WebRtc_Word16 *ILBCDEC_inst_Addr, WebRtc_Word16 *size) { - *iLBC_decinst=(iLBC_decinst_t*)ILBCDEC_inst_Addr; - *size=sizeof(iLBC_Dec_Inst_t)/sizeof(WebRtc_Word16); - if (*iLBC_decinst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcIlbcfix_EncoderCreate(iLBC_encinst_t **iLBC_encinst) { - *iLBC_encinst=(iLBC_encinst_t*)malloc(sizeof(iLBC_Enc_Inst_t)); - if (*iLBC_encinst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcIlbcfix_DecoderCreate(iLBC_decinst_t **iLBC_decinst) { - *iLBC_decinst=(iLBC_decinst_t*)malloc(sizeof(iLBC_Dec_Inst_t)); - if (*iLBC_decinst!=NULL) { - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcIlbcfix_EncoderFree(iLBC_encinst_t *iLBC_encinst) { - free(iLBC_encinst); - return(0); -} - -WebRtc_Word16 WebRtcIlbcfix_DecoderFree(iLBC_decinst_t *iLBC_decinst) { - free(iLBC_decinst); - return(0); -} - - -WebRtc_Word16 WebRtcIlbcfix_EncoderInit(iLBC_encinst_t *iLBCenc_inst, WebRtc_Word16 mode) -{ - if ((mode==20)||(mode==30)) { - WebRtcIlbcfix_InitEncode((iLBC_Enc_Inst_t*) iLBCenc_inst, mode); - return(0); - } else { - return(-1); - } -} - -WebRtc_Word16 WebRtcIlbcfix_Encode(iLBC_encinst_t *iLBCenc_inst, WebRtc_Word16 *speechIn, WebRtc_Word16 len, WebRtc_Word16 *encoded) { - - WebRtc_Word16 pos = 0; - WebRtc_Word16 encpos = 0; - - if ((len != ((iLBC_Enc_Inst_t*)iLBCenc_inst)->blockl) && -#ifdef SPLIT_10MS - (len != 80) && -#endif - (len != 2*((iLBC_Enc_Inst_t*)iLBCenc_inst)->blockl) && - (len != 3*((iLBC_Enc_Inst_t*)iLBCenc_inst)->blockl)) - { - /* A maximum of 3 frames/packet is allowed */ - return(-1); - } else { - - /* call encoder */ - while (possection == 0) -#else - pos += ((iLBC_Enc_Inst_t*)iLBCenc_inst)->blockl; -#endif - encpos += ((iLBC_Enc_Inst_t*)iLBCenc_inst)->no_of_words; - } - return (encpos*2); - } -} - -WebRtc_Word16 WebRtcIlbcfix_DecoderInit(iLBC_decinst_t *iLBCdec_inst, WebRtc_Word16 mode) { - if ((mode==20)||(mode==30)) { - WebRtcIlbcfix_InitDecode((iLBC_Dec_Inst_t*) iLBCdec_inst, mode, 1); - return(0); - } else { - return(-1); - } -} -WebRtc_Word16 WebRtcIlbcfix_DecoderInit20Ms(iLBC_decinst_t *iLBCdec_inst) { - WebRtcIlbcfix_InitDecode((iLBC_Dec_Inst_t*) iLBCdec_inst, 20, 1); - return(0); -} -WebRtc_Word16 WebRtcIlbcfix_Decoderinit30Ms(iLBC_decinst_t *iLBCdec_inst) { - WebRtcIlbcfix_InitDecode((iLBC_Dec_Inst_t*) iLBCdec_inst, 30, 1); - return(0); -} - - -WebRtc_Word16 WebRtcIlbcfix_Decode(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - int i=0; - /* Allow for automatic switching between the frame sizes - (although you do get some discontinuity) */ - if ((len==((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==2*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==3*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)) { - /* ok, do nothing */ - } else { - /* Test if the mode has changed */ - if (((iLBC_Dec_Inst_t*)iLBCdec_inst)->mode==20) { - if ((len==NO_OF_BYTES_30MS)|| - (len==2*NO_OF_BYTES_30MS)|| - (len==3*NO_OF_BYTES_30MS)) { - WebRtcIlbcfix_InitDecode(((iLBC_Dec_Inst_t*)iLBCdec_inst), 30, ((iLBC_Dec_Inst_t*)iLBCdec_inst)->use_enhancer); - } else { - /* Unsupported frame length */ - return(-1); - } - } else { - if ((len==NO_OF_BYTES_20MS)|| - (len==2*NO_OF_BYTES_20MS)|| - (len==3*NO_OF_BYTES_20MS)) { - WebRtcIlbcfix_InitDecode(((iLBC_Dec_Inst_t*)iLBCdec_inst), 20, ((iLBC_Dec_Inst_t*)iLBCdec_inst)->use_enhancer); - } else { - /* Unsupported frame length */ - return(-1); - } - } - } - - while ((i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)blockl], (WebRtc_UWord16*) &encoded[i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_words], (iLBC_Dec_Inst_t*) iLBCdec_inst, 1); - i++; - } - /* iLBC does not support VAD/CNG yet */ - *speechType=1; - return(i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->blockl); -} - -WebRtc_Word16 WebRtcIlbcfix_Decode20Ms(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - int i=0; - if ((len==((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==2*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==3*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)) { - /* ok, do nothing */ - } else { - return(-1); - } - - while ((i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)blockl], (WebRtc_UWord16*) &encoded[i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_words], (iLBC_Dec_Inst_t*) iLBCdec_inst, 1); - i++; - } - /* iLBC does not support VAD/CNG yet */ - *speechType=1; - return(i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->blockl); -} - -WebRtc_Word16 WebRtcIlbcfix_Decode30Ms(iLBC_decinst_t *iLBCdec_inst, - WebRtc_Word16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - int i=0; - if ((len==((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==2*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)|| - (len==3*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)) { - /* ok, do nothing */ - } else { - return(-1); - } - - while ((i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_bytes)blockl], (WebRtc_UWord16*) &encoded[i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->no_of_words], (iLBC_Dec_Inst_t*) iLBCdec_inst, 1); - i++; - } - /* iLBC does not support VAD/CNG yet */ - *speechType=1; - return(i*((iLBC_Dec_Inst_t*)iLBCdec_inst)->blockl); -} - -WebRtc_Word16 WebRtcIlbcfix_DecodePlc(iLBC_decinst_t *iLBCdec_inst, WebRtc_Word16 *decoded, WebRtc_Word16 noOfLostFrames) { - int i; - WebRtc_UWord16 dummy; - - for (i=0;iblockl], &dummy, (iLBC_Dec_Inst_t*) iLBCdec_inst, 0); - } - return (noOfLostFrames*((iLBC_Dec_Inst_t*)iLBCdec_inst)->blockl); -} - -WebRtc_Word16 WebRtcIlbcfix_NetEqPlc(iLBC_decinst_t *iLBCdec_inst, WebRtc_Word16 *decoded, WebRtc_Word16 noOfLostFrames) { - - /* Two input parameters not used, but needed for function pointers in NetEQ */ - decoded = decoded; - noOfLostFrames = noOfLostFrames; - - WebRtcSpl_MemSetW16(((iLBC_Dec_Inst_t*)iLBCdec_inst)->enh_buf, 0, ENH_BUFL); - ((iLBC_Dec_Inst_t*)iLBCdec_inst)->prev_enh_pl = 2; - - return (0); -} - -void WebRtcIlbcfix_version(WebRtc_Word8 *version) -{ - strcpy((char*)version, "1.1.0"); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/ilbc.gyp b/modules/audio_coding/codecs/iLBC/main/source/ilbc.gyp deleted file mode 100644 index bcd5f8170..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/ilbc.gyp +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'iLBC', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/ilbc.h', - 'abs_quant.c', - 'abs_quant_loop.c', - 'augmented_cb_corr.c', - 'bw_expand.c', - 'cb_construct.c', - 'cb_mem_energy.c', - 'cb_mem_energy_augmentation.c', - 'cb_mem_energy_calc.c', - 'cb_search.c', - 'cb_search_core.c', - 'cb_update_best_index.c', - 'chebyshev.c', - 'comp_corr.c', - 'constants.c', - 'create_augmented_vec.c', - 'decode.c', - 'decode_residual.c', - 'decoder_interpolate_lsf.c', - 'do_plc.c', - 'encode.c', - 'energy_inverse.c', - 'enh_upsample.c', - 'enhancer.c', - 'enhancer_interface.c', - 'filtered_cb_vecs.c', - 'frame_classify.c', - 'gain_dequant.c', - 'gain_quant.c', - 'get_cd_vec.c', - 'get_lsp_poly.c', - 'get_sync_seq.c', - 'hp_input.c', - 'hp_output.c', - 'ilbc.c', - 'index_conv_dec.c', - 'index_conv_enc.c', - 'init_decode.c', - 'init_encode.c', - 'interpolate.c', - 'interpolate_samples.c', - 'lpc_encode.c', - 'lsf_check.c', - 'lsf_interpolate_to_poly_dec.c', - 'lsf_interpolate_to_poly_enc.c', - 'lsf_to_lsp.c', - 'lsf_to_poly.c', - 'lsp_to_lsf.c', - 'my_corr.c', - 'nearest_neighbor.c', - 'pack_bits.c', - 'poly_to_lsf.c', - 'poly_to_lsp.c', - 'refiner.c', - 'simple_interpolate_lsf.c', - 'simple_lpc_analysis.c', - 'simple_lsf_dequant.c', - 'simple_lsf_quant.c', - 'smooth.c', - 'smooth_out_data.c', - 'sort_sq.c', - 'split_vq.c', - 'state_construct.c', - 'state_search.c', - 'swap_bytes.c', - 'unpack_bits.c', - 'vq3.c', - 'vq4.c', - 'window32_w32.c', - 'xcorr_coef.c', - 'abs_quant.h', - 'abs_quant_loop.h', - 'augmented_cb_corr.h', - 'bw_expand.h', - 'cb_construct.h', - 'cb_mem_energy.h', - 'cb_mem_energy_augmentation.h', - 'cb_mem_energy_calc.h', - 'cb_search.h', - 'cb_search_core.h', - 'cb_update_best_index.h', - 'chebyshev.h', - 'comp_corr.h', - 'constants.h', - 'create_augmented_vec.h', - 'decode.h', - 'decode_residual.h', - 'decoder_interpolate_lsf.h', - 'do_plc.h', - 'encode.h', - 'energy_inverse.h', - 'enh_upsample.h', - 'enhancer.h', - 'enhancer_interface.h', - 'filtered_cb_vecs.h', - 'frame_classify.h', - 'gain_dequant.h', - 'gain_quant.h', - 'get_cd_vec.h', - 'get_lsp_poly.h', - 'get_sync_seq.h', - 'hp_input.h', - 'hp_output.h', - 'defines.h', - 'index_conv_dec.h', - 'index_conv_enc.h', - 'init_decode.h', - 'init_encode.h', - 'interpolate.h', - 'interpolate_samples.h', - 'lpc_encode.h', - 'lsf_check.h', - 'lsf_interpolate_to_poly_dec.h', - 'lsf_interpolate_to_poly_enc.h', - 'lsf_to_lsp.h', - 'lsf_to_poly.h', - 'lsp_to_lsf.h', - 'my_corr.h', - 'nearest_neighbor.h', - 'pack_bits.h', - 'poly_to_lsf.h', - 'poly_to_lsp.h', - 'refiner.h', - 'simple_interpolate_lsf.h', - 'simple_lpc_analysis.h', - 'simple_lsf_dequant.h', - 'simple_lsf_quant.h', - 'smooth.h', - 'smooth_out_data.h', - 'sort_sq.h', - 'split_vq.h', - 'state_construct.h', - 'state_search.h', - 'swap_bytes.h', - 'unpack_bits.h', - 'vq3.h', - 'vq4.h', - 'window32_w32.h', - 'xcorr_coef.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.c b/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.c deleted file mode 100644 index 0d6346a98..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_IndexConvDec.c - -******************************************************************/ - -#include "defines.h" - -void WebRtcIlbcfix_IndexConvDec( - WebRtc_Word16 *index /* (i/o) Codebook indexes */ - ){ - int k; - - for (k=4;k<6;k++) { - /* Readjust the second and third codebook index for the first 40 sample - so that they look the same as the first (in terms of lag) - */ - if ((index[k]>=44)&&(index[k]<108)) { - index[k]+=64; - } else if ((index[k]>=108)&&(index[k]<128)) { - index[k]+=128; - } else { - /* ERROR */ - } - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.h b/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.h deleted file mode 100644 index f29ee230d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/index_conv_dec.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_IndexConvDec.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INDEX_CONV_DEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INDEX_CONV_DEC_H_ - -#include "defines.h" - -void WebRtcIlbcfix_IndexConvDec( - WebRtc_Word16 *index /* (i/o) Codebook indexes */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.c b/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.c deleted file mode 100644 index cbc04b62d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - IiLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_IndexConvEnc.c - -******************************************************************/ - -#include "defines.h" -/*----------------------------------------------------------------* - * Convert the codebook indexes to make the search easier - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_IndexConvEnc( - WebRtc_Word16 *index /* (i/o) Codebook indexes */ - ){ - int k; - - for (k=4;k<6;k++) { - /* Readjust the second and third codebook index so that it is - packetized into 7 bits (before it was put in lag-wise the same - way as for the first codebook which uses 8 bits) - */ - if ((index[k]>=108)&&(index[k]<172)) { - index[k]-=64; - } else if (index[k]>=236) { - index[k]-=128; - } else { - /* ERROR */ - } - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.h b/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.h deleted file mode 100644 index d28a6e2a3..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/index_conv_enc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_IndexConvEnc.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INDEX_CONV_ENC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INDEX_CONV_ENC_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Convert the codebook indexes to make the search easier - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_IndexConvEnc( - WebRtc_Word16 *index /* (i/o) Codebook indexes */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/init_decode.c b/modules/audio_coding/codecs/iLBC/main/source/init_decode.c deleted file mode 100644 index b654f1ec9..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/init_decode.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InitDecode.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * Initiation of decoder instance. - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_InitDecode( /* (o) Number of decoded samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - WebRtc_Word16 mode, /* (i) frame size mode */ - int use_enhancer /* (i) 1 to use enhancer - 0 to run without enhancer */ - ) { - int i; - - iLBCdec_inst->mode = mode; - - /* Set all the variables that are dependent on the frame size mode */ - if (mode==30) { - iLBCdec_inst->blockl = BLOCKL_30MS; - iLBCdec_inst->nsub = NSUB_30MS; - iLBCdec_inst->nasub = NASUB_30MS; - iLBCdec_inst->lpc_n = LPC_N_30MS; - iLBCdec_inst->no_of_bytes = NO_OF_BYTES_30MS; - iLBCdec_inst->no_of_words = NO_OF_WORDS_30MS; - iLBCdec_inst->state_short_len=STATE_SHORT_LEN_30MS; - } - else if (mode==20) { - iLBCdec_inst->blockl = BLOCKL_20MS; - iLBCdec_inst->nsub = NSUB_20MS; - iLBCdec_inst->nasub = NASUB_20MS; - iLBCdec_inst->lpc_n = LPC_N_20MS; - iLBCdec_inst->no_of_bytes = NO_OF_BYTES_20MS; - iLBCdec_inst->no_of_words = NO_OF_WORDS_20MS; - iLBCdec_inst->state_short_len=STATE_SHORT_LEN_20MS; - } - else { - return(-1); - } - - /* Reset all the previous LSF to mean LSF */ - WEBRTC_SPL_MEMCPY_W16(iLBCdec_inst->lsfdeqold, WebRtcIlbcfix_kLsfMean, LPC_FILTERORDER); - - /* Clear the synthesis filter memory */ - WebRtcSpl_MemSetW16(iLBCdec_inst->syntMem, 0, LPC_FILTERORDER); - - /* Set the old synthesis filter to {1.0 0.0 ... 0.0} */ - WebRtcSpl_MemSetW16(iLBCdec_inst->old_syntdenum, 0, ((LPC_FILTERORDER + 1)*NSUB_MAX)); - for (i=0; iold_syntdenum[i*(LPC_FILTERORDER+1)] = 4096; - } - - /* Clear the variables that are used for the PLC */ - iLBCdec_inst->last_lag = 20; - iLBCdec_inst->consPLICount = 0; - iLBCdec_inst->prevPLI = 0; - iLBCdec_inst->perSquare = 0; - iLBCdec_inst->prevLag = 120; - iLBCdec_inst->prevLpc[0] = 4096; - WebRtcSpl_MemSetW16(iLBCdec_inst->prevLpc+1, 0, LPC_FILTERORDER); - WebRtcSpl_MemSetW16(iLBCdec_inst->prevResidual, 0, BLOCKL_MAX); - - /* Initialize the seed for the random number generator */ - iLBCdec_inst->seed = 777; - - /* Set the filter state of the HP filter to 0 */ - WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemx, 0, 2); - WebRtcSpl_MemSetW16(iLBCdec_inst->hpimemy, 0, 4); - - /* Set the variables that are used in the ehnahcer */ - iLBCdec_inst->use_enhancer = use_enhancer; - WebRtcSpl_MemSetW16(iLBCdec_inst->enh_buf, 0, (ENH_BUFL+ENH_BUFL_FILTEROVERHEAD)); - for (i=0;ienh_period[i]=160; /* Q(-4) */ - } - - iLBCdec_inst->prev_enh_pl = 0; - - return (iLBCdec_inst->blockl); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/init_decode.h b/modules/audio_coding/codecs/iLBC/main/source/init_decode.h deleted file mode 100644 index 3452f34a2..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/init_decode.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InitDecode.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INIT_DECODE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INIT_DECODE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Initiation of decoder instance. - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_InitDecode( /* (o) Number of decoded samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - WebRtc_Word16 mode, /* (i) frame size mode */ - int use_enhancer /* (i) 1 to use enhancer - 0 to run without enhancer */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/init_encode.c b/modules/audio_coding/codecs/iLBC/main/source/init_encode.c deleted file mode 100644 index e034bb0dd..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/init_encode.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InitEncode.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * Initiation of encoder instance. - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_InitEncode( /* (o) Number of bytes encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ - WebRtc_Word16 mode /* (i) frame size mode */ - ){ - iLBCenc_inst->mode = mode; - - /* Set all the variables that are dependent on the frame size mode */ - if (mode==30) { - iLBCenc_inst->blockl = BLOCKL_30MS; - iLBCenc_inst->nsub = NSUB_30MS; - iLBCenc_inst->nasub = NASUB_30MS; - iLBCenc_inst->lpc_n = LPC_N_30MS; - iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS; - iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS; - iLBCenc_inst->state_short_len=STATE_SHORT_LEN_30MS; - } - else if (mode==20) { - iLBCenc_inst->blockl = BLOCKL_20MS; - iLBCenc_inst->nsub = NSUB_20MS; - iLBCenc_inst->nasub = NASUB_20MS; - iLBCenc_inst->lpc_n = LPC_N_20MS; - iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS; - iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS; - iLBCenc_inst->state_short_len=STATE_SHORT_LEN_20MS; - } - else { - return(-1); - } - - /* Clear the buffers and set the previous LSF and LSP to the mean value */ - WebRtcSpl_MemSetW16(iLBCenc_inst->anaMem, 0, LPC_FILTERORDER); - WEBRTC_SPL_MEMCPY_W16(iLBCenc_inst->lsfold, WebRtcIlbcfix_kLsfMean, LPC_FILTERORDER); - WEBRTC_SPL_MEMCPY_W16(iLBCenc_inst->lsfdeqold, WebRtcIlbcfix_kLsfMean, LPC_FILTERORDER); - WebRtcSpl_MemSetW16(iLBCenc_inst->lpc_buffer, 0, LPC_LOOKBACK + BLOCKL_MAX); - - /* Set the filter state of the HP filter to 0 */ - WebRtcSpl_MemSetW16(iLBCenc_inst->hpimemx, 0, 2); - WebRtcSpl_MemSetW16(iLBCenc_inst->hpimemy, 0, 4); - -#ifdef SPLIT_10MS - /*Zeroing the past samples for 10msec Split*/ - WebRtcSpl_MemSetW16(iLBCenc_inst->past_samples,0,160); - iLBCenc_inst->section = 0; -#endif - - return (iLBCenc_inst->no_of_bytes); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/init_encode.h b/modules/audio_coding/codecs/iLBC/main/source/init_encode.h deleted file mode 100644 index f1d185837..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/init_encode.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InitEncode.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INIT_ENCODE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INIT_ENCODE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Initiation of encoder instance. - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_InitEncode( /* (o) Number of bytes encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ - WebRtc_Word16 mode /* (i) frame size mode */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/interpolate.c b/modules/audio_coding/codecs/iLBC/main/source/interpolate.c deleted file mode 100644 index 11cb33c74..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/interpolate.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Interpolate.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * interpolation between vectors - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Interpolate( - WebRtc_Word16 *out, /* (o) output vector */ - WebRtc_Word16 *in1, /* (i) first input vector */ - WebRtc_Word16 *in2, /* (i) second input vector */ - WebRtc_Word16 coef, /* (i) weight coefficient in Q14 */ - WebRtc_Word16 length) /* (i) number of sample is vectors */ -{ - int i; - WebRtc_Word16 invcoef; - - /* - Performs the operation out[i] = in[i]*coef + (1-coef)*in2[i] (with rounding) - */ - - invcoef = 16384 - coef; /* 16384 = 1.0 (Q14)*/ - for (i = 0; i < length; i++) { - out[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(coef, in1[i]) + WEBRTC_SPL_MUL_16_16(invcoef, in2[i]))+8192, - 14); - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/interpolate.h b/modules/audio_coding/codecs/iLBC/main/source/interpolate.h deleted file mode 100644 index a12021c70..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/interpolate.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Interpolate.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INTERPOLATE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INTERPOLATE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * interpolation between vectors - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Interpolate( - WebRtc_Word16 *out, /* (o) output vector */ - WebRtc_Word16 *in1, /* (i) first input vector */ - WebRtc_Word16 *in2, /* (i) second input vector */ - WebRtc_Word16 coef, /* (i) weight coefficient in Q14 */ - WebRtc_Word16 length); /* (i) number of sample is vectors */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.c b/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.c deleted file mode 100644 index 31eb52e37..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InterpolateSamples.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -void WebRtcIlbcfix_InterpolateSamples( - WebRtc_Word16 *interpSamples, /* (o) The interpolated samples */ - WebRtc_Word16 *CBmem, /* (i) The CB memory */ - WebRtc_Word16 lMem /* (i) Length of the CB memory */ - ) { - WebRtc_Word16 *ppi, *ppo, i, j, temp1, temp2; - WebRtc_Word16 *tmpPtr; - - /* Calculate the 20 vectors of interpolated samples (4 samples each) - that are used in the codebooks for lag 20 to 39 */ - tmpPtr = interpSamples; - for (j=0; j<20; j++) { - temp1 = 0; - temp2 = 3; - ppo = CBmem+lMem-4; - ppi = CBmem+lMem-j-24; - for (i=0; i<4; i++) { - - *tmpPtr++ = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIlbcfix_kAlpha[temp2],*ppo, 15) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIlbcfix_kAlpha[temp1], *ppi, 15); - - ppo++; - ppi++; - temp1++; - temp2--; - } - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.h b/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.h deleted file mode 100644 index 5c98aafa3..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/interpolate_samples.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_InterpolateSamples.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INTERPOLATE_SAMPLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_INTERPOLATE_SAMPLES_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Construct the interpolated samples for the Augmented CB - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_InterpolateSamples( - WebRtc_Word16 *interpSamples, /* (o) The interpolated samples */ - WebRtc_Word16 *CBmem, /* (i) The CB memory */ - WebRtc_Word16 lMem /* (i) Length of the CB memory */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.c b/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.c deleted file mode 100644 index 73d67a04d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LpcEncode.c - -******************************************************************/ - -#include "defines.h" -#include "simple_lpc_analysis.h" -#include "simple_interpolate_lsf.h" -#include "simple_lsf_quant.h" -#include "lsf_check.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * lpc encoder - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LpcEncode( - WebRtc_Word16 *syntdenum, /* (i/o) synthesis filter coefficients - before/after encoding */ - WebRtc_Word16 *weightdenum, /* (i/o) weighting denumerator coefficients - before/after encoding */ - WebRtc_Word16 *lsf_index, /* (o) lsf quantization index */ - WebRtc_Word16 *data, /* (i) Speech to do LPC analysis on */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ) { - /* Stack based */ - WebRtc_Word16 lsf[LPC_FILTERORDER * LPC_N_MAX]; - WebRtc_Word16 lsfdeq[LPC_FILTERORDER * LPC_N_MAX]; - - /* Calculate LSF's from the input speech */ - WebRtcIlbcfix_SimpleLpcAnalysis(lsf, data, iLBCenc_inst); - - /* Quantize the LSF's */ - WebRtcIlbcfix_SimpleLsfQ(lsfdeq, lsf_index, lsf, iLBCenc_inst->lpc_n); - - /* Stableize the LSF's if needed */ - WebRtcIlbcfix_LsfCheck(lsfdeq, LPC_FILTERORDER, iLBCenc_inst->lpc_n); - - /* Calculate the synthesis and weighting filter coefficients from - the optimal LSF and the dequantized LSF */ - WebRtcIlbcfix_SimpleInterpolateLsf(syntdenum, weightdenum, - lsf, lsfdeq, iLBCenc_inst->lsfold, - iLBCenc_inst->lsfdeqold, LPC_FILTERORDER, iLBCenc_inst); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.h b/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.h deleted file mode 100644 index 36967a307..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lpc_encode.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LpcEncode.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LPC_ENCODE_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LPC_ENCODE_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lpc encoder - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LpcEncode( - WebRtc_Word16 *syntdenum, /* (i/o) synthesis filter coefficients - before/after encoding */ - WebRtc_Word16 *weightdenum, /* (i/o) weighting denumerator coefficients - before/after encoding */ - WebRtc_Word16 *lsf_index, /* (o) lsf quantization index */ - WebRtc_Word16 *data, /* (i) Speech to do LPC analysis on */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_check.c b/modules/audio_coding/codecs/iLBC/main/source/lsf_check.c deleted file mode 100644 index 7097d7488..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_check.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LsfCheck.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * check for stability of lsf coefficients - *---------------------------------------------------------------*/ - -int WebRtcIlbcfix_LsfCheck( - WebRtc_Word16 *lsf, /* LSF parameters */ - int dim, /* dimension of LSF */ - int NoAn) /* No of analysis per frame */ -{ - int k,n,m, Nit=2, change=0,pos; - const WebRtc_Word16 eps=319; /* 0.039 in Q13 (50 Hz)*/ - const WebRtc_Word16 eps2=160; /* eps/2.0 in Q13;*/ - const WebRtc_Word16 maxlsf=25723; /* 3.14; (4000 Hz)*/ - const WebRtc_Word16 minlsf=82; /* 0.01; (0 Hz)*/ - - /* LSF separation check*/ - for (n=0;nmaxlsf) { - lsf[pos]=maxlsf; - change=1; - } - } - } - } - - return change; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_check.h b/modules/audio_coding/codecs/iLBC/main/source/lsf_check.h deleted file mode 100644 index 830bbed6c..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_check.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LsfCheck.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_CHECK_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_CHECK_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * check for stability of lsf coefficients - *---------------------------------------------------------------*/ - -int WebRtcIlbcfix_LsfCheck( - WebRtc_Word16 *lsf, /* LSF parameters */ - int dim, /* dimension of LSF */ - int NoAn); /* No of analysis per frame */ - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.c b/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.c deleted file mode 100644 index 3bb23d008..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LspInterpolate2PolyDec.c - -******************************************************************/ - -#include "interpolate.h" -#include "lsf_to_poly.h" -#include "defines.h" - -/*----------------------------------------------------------------* - * interpolation of lsf coefficients for the decoder - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LspInterpolate2PolyDec( - WebRtc_Word16 *a, /* (o) lpc coefficients Q12 */ - WebRtc_Word16 *lsf1, /* (i) first set of lsf coefficients Q13 */ - WebRtc_Word16 *lsf2, /* (i) second set of lsf coefficients Q13 */ - WebRtc_Word16 coef, /* (i) weighting coefficient to use between - lsf1 and lsf2 Q14 */ - WebRtc_Word16 length /* (i) length of coefficient vectors */ - ){ - WebRtc_Word16 lsftmp[LPC_FILTERORDER]; - - /* interpolate LSF */ - WebRtcIlbcfix_Interpolate(lsftmp, lsf1, lsf2, coef, length); - - /* Compute the filter coefficients from the LSF */ - WebRtcIlbcfix_Lsf2Poly(a, lsftmp); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.h b/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.h deleted file mode 100644 index 23fe3a72e..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_dec.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LspInterpolate2PolyDec.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_INTERPOLATE_TO_POLY_DEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_INTERPOLATE_TO_POLY_DEC_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * interpolation of lsf coefficients for the decoder - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LspInterpolate2PolyDec( - WebRtc_Word16 *a, /* (o) lpc coefficients Q12 */ - WebRtc_Word16 *lsf1, /* (i) first set of lsf coefficients Q13 */ - WebRtc_Word16 *lsf2, /* (i) second set of lsf coefficients Q13 */ - WebRtc_Word16 coef, /* (i) weighting coefficient to use between - lsf1 and lsf2 Q14 */ - WebRtc_Word16 length /* (i) length of coefficient vectors */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.c b/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.c deleted file mode 100644 index dae5780ee..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LsfInterpolate2PloyEnc.c - -******************************************************************/ - -#include "defines.h" -#include "interpolate.h" -#include "lsf_to_poly.h" - -/*----------------------------------------------------------------* - * lsf interpolator and conversion from lsf to a coefficients - * (subrutine to SimpleInterpolateLSF) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LsfInterpolate2PloyEnc( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) the encoder state structure */ - WebRtc_Word16 *a, /* (o) lpc coefficients Q12 */ - WebRtc_Word16 *lsf1, /* (i) first set of lsf coefficients Q13 */ - WebRtc_Word16 *lsf2, /* (i) second set of lsf coefficients Q13 */ - WebRtc_Word16 coef, /* (i) weighting coefficient to use between - lsf1 and lsf2 Q14 */ - WebRtc_Word16 length /* (i) length of coefficient vectors */ - ) { - /* Stack based */ - WebRtc_Word16 lsftmp[LPC_FILTERORDER]; - - /* Input parameter not used if not using scratch memory */ - iLBCenc_inst = iLBCenc_inst; - /* interpolate LSF */ - WebRtcIlbcfix_Interpolate(lsftmp, lsf1, lsf2, coef, length); - - /* Compute the filter coefficients from the LSF */ - WebRtcIlbcfix_Lsf2Poly(a, lsftmp); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.h b/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.h deleted file mode 100644 index d83835d1d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_interpolate_to_poly_enc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_LsfInterpolate2PloyEnc.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_INTERPOLATE_TO_POLY_ENC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_INTERPOLATE_TO_POLY_ENC_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lsf interpolator and conversion from lsf to a coefficients - * (subrutine to SimpleInterpolateLSF) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_LsfInterpolate2PloyEnc( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) the encoder state structure */ - WebRtc_Word16 *a, /* (o) lpc coefficients Q12 */ - WebRtc_Word16 *lsf1, /* (i) first set of lsf coefficients Q13 */ - WebRtc_Word16 *lsf2, /* (i) second set of lsf coefficients Q13 */ - WebRtc_Word16 coef, /* (i) weighting coefficient to use between - lsf1 and lsf2 Q14 */ - WebRtc_Word16 length /* (i) length of coefficient vectors */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.c b/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.c deleted file mode 100644 index 84278a434..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsf2Lsp.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * conversion from lsf to lsp coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Lsf2Lsp( - WebRtc_Word16 *lsf, /* (i) lsf in Q13 values between 0 and pi */ - WebRtc_Word16 *lsp, /* (o) lsp in Q15 values between -1 and 1 */ - WebRtc_Word16 m /* (i) number of coefficients */ - ) { - WebRtc_Word16 i, k; - WebRtc_Word16 diff; /* difference, which is used for the - linear approximation (Q8) */ - WebRtc_Word16 freq; /* normalized frequency in Q15 (0..1) */ - WebRtc_Word32 tmpW32; - - for(i=0; i63) { - k = 63; - } - - /* Calculate linear approximation */ - tmpW32 = WEBRTC_SPL_MUL_16_16(WebRtcIlbcfix_kCosDerivative[k], diff); - lsp[i] = WebRtcIlbcfix_kCos[k]+(WebRtc_Word16)(WEBRTC_SPL_RSHIFT_W32(tmpW32, 12)); - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.h b/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.h deleted file mode 100644 index db6549bef..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_lsp.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsf2Lsp.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_TO_LSP_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_TO_LSP_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * conversion from lsf to lsp coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Lsf2Lsp( - WebRtc_Word16 *lsf, /* (i) lsf in Q13 values between 0 and pi */ - WebRtc_Word16 *lsp, /* (o) lsp in Q15 values between -1 and 1 */ - WebRtc_Word16 m /* (i) number of coefficients */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.c b/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.c deleted file mode 100644 index f1c4a9eec..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsf2Poly.c - -******************************************************************/ - -#include "defines.h" -#include "lsf_to_lsp.h" -#include "get_lsp_poly.h" -#include "constants.h" - -void WebRtcIlbcfix_Lsf2Poly( - WebRtc_Word16 *a, /* (o) predictor coefficients (order = 10) in Q12 */ - WebRtc_Word16 *lsf /* (i) line spectral frequencies in Q13 */ - ) { - WebRtc_Word32 f[2][6]; /* f[0][] and f[1][] corresponds to - F1(z) and F2(z) respectivly */ - WebRtc_Word32 *f1ptr, *f2ptr; - WebRtc_Word16 *a1ptr, *a2ptr; - WebRtc_Word32 tmpW32; - WebRtc_Word16 lsp[10]; - int i; - - /* Convert lsf to lsp */ - WebRtcIlbcfix_Lsf2Lsp(lsf, lsp, LPC_FILTERORDER); - - /* Get F1(z) and F2(z) from the lsp */ - f1ptr=f[0]; - f2ptr=f[1]; - WebRtcIlbcfix_GetLspPoly(&lsp[0],f1ptr); - WebRtcIlbcfix_GetLspPoly(&lsp[1],f2ptr); - - /* for i = 5 down to 1 - Compute f1[i] += f1[i-1]; - and f2[i] += f2[i-1]; - */ - f1ptr=&f[0][5]; - f2ptr=&f[1][5]; - for (i=5; i>0; i--) - { - (*f1ptr) += (*(f1ptr-1)); - (*f2ptr) -= (*(f2ptr-1)); - f1ptr--; - f2ptr--; - } - - /* Get the A(z) coefficients - a[0] = 1.0 - for i = 1 to 5 - a[i] = (f1[i] + f2[i] + round)>>13; - for i = 1 to 5 - a[11-i] = (f1[i] - f2[i] + round)>>13; - */ - a[0]=4096; - a1ptr=&a[1]; - a2ptr=&a[10]; - f1ptr=&f[0][1]; - f2ptr=&f[1][1]; - for (i=5; i>0; i--) - { - tmpW32 = (*f1ptr) + (*f2ptr); - (*a1ptr) = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((tmpW32+4096),13); - - tmpW32 = (*f1ptr) - (*f2ptr); - (*a2ptr) = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((tmpW32+4096),13); - - a1ptr++; - a2ptr--; - f1ptr++; - f2ptr++; - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.h b/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.h deleted file mode 100644 index a00693b59..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsf_to_poly.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsf2Poly.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_TO_POLY_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSF_TO_POLY_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Convert from LSF coefficients to A coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Lsf2Poly( - WebRtc_Word16 *a, /* (o) predictor coefficients (order = 10) in Q12 */ - WebRtc_Word16 *lsf /* (i) line spectral frequencies in Q13 */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.c b/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.c deleted file mode 100644 index 134afbb50..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsp2Lsf.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * conversion from LSP coefficients to LSF coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Lsp2Lsf( - WebRtc_Word16 *lsp, /* (i) lsp vector -1...+1 in Q15 */ - WebRtc_Word16 *lsf, /* (o) Lsf vector 0...Pi in Q13 - (ordered, so that lsf[i]=0; i--) - { - /* - locate value in the table, which is just above lsp[i], - basically an approximation to acos(x) - */ - while( (((WebRtc_Word32)(*cosTblPtr)-(*lspPtr)) < 0)&&(k>0) ) - { - k-=1; - cosTblPtr--; - } - - /* Calculate diff, which is used in the linear approximation of acos(x) */ - diff = (*lspPtr)-(*cosTblPtr); - - /* - The linear approximation of acos(lsp[i]) : - acos(lsp[i])= k*512 + (WebRtcIlbcfix_kAcosDerivative[ind]*offset >> 11) - */ - - /* tmp (linear offset) in Q16 */ - tmp = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(WebRtcIlbcfix_kAcosDerivative[k],diff, 11); - - /* freq in Q16 */ - freq = (WebRtc_Word16)WEBRTC_SPL_LSHIFT_W16(k,9)+tmp; - - /* lsf = freq*2*pi */ - (*lsfPtr) = (WebRtc_Word16)(((WebRtc_Word32)freq*25736)>>15); - - lsfPtr--; - lspPtr--; - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.h b/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.h deleted file mode 100644 index 97ba7e4b9..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/lsp_to_lsf.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Lsp2Lsf.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSP_TO_LSF_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_LSP_TO_LSF_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * conversion from LSP coefficients to LSF coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Lsp2Lsf( - WebRtc_Word16 *lsp, /* (i) lsp vector -1...+1 in Q15 */ - WebRtc_Word16 *lsf, /* (o) Lsf vector 0...Pi in Q13 - (ordered, so that lsf[i]lsf[0])<<10; /* Bit 0..5 */ - (*bitstreamPtr) |= (enc_bits->lsf[1])<<3; /* Bit 6..12 */ - (*bitstreamPtr) |= (enc_bits->lsf[2]&0x70)>>4; /* Bit 13..15 */ - bitstreamPtr++; - /* Second WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)enc_bits->lsf[2]&0xF)<<12; /* Bit 0..3 */ - - if (mode==20) { - (*bitstreamPtr) |= (enc_bits->startIdx)<<10; /* Bit 4..5 */ - (*bitstreamPtr) |= (enc_bits->state_first)<<9; /* Bit 6 */ - (*bitstreamPtr) |= (enc_bits->idxForMax)<<3; /* Bit 7..12 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[0])&0x70)>>4; /* Bit 13..15 */ - bitstreamPtr++; - /* Third WebRtc_Word16 */ - (*bitstreamPtr) = ((enc_bits->cb_index[0])&0xE)<<12; /* Bit 0..2 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[0])&0x18)<<8; /* Bit 3..4 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[1])&0x8)<<7; /* Bit 5 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[3])&0xFE)<<2; /* Bit 6..12 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[3])&0x10)>>2; /* Bit 13 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[4])&0x8)>>2; /* Bit 14 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[6])&0x10)>>4; /* Bit 15 */ - } else { /* mode==30 */ - (*bitstreamPtr) |= (enc_bits->lsf[3])<<6; /* Bit 4..9 */ - (*bitstreamPtr) |= (enc_bits->lsf[4]&0x7E)>>1; /* Bit 10..15 */ - bitstreamPtr++; - /* Third WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)enc_bits->lsf[4]&0x1)<<15; /* Bit 0 */ - (*bitstreamPtr) |= (enc_bits->lsf[5])<<8; /* Bit 1..7 */ - (*bitstreamPtr) |= (enc_bits->startIdx)<<5; /* Bit 8..10 */ - (*bitstreamPtr) |= (enc_bits->state_first)<<4; /* Bit 11 */ - (*bitstreamPtr) |= ((enc_bits->idxForMax)&0x3C)>>2; /* Bit 12..15 */ - bitstreamPtr++; - /* 4:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)enc_bits->idxForMax&0x3)<<14; /* Bit 0..1 */ - (*bitstreamPtr) |= (enc_bits->cb_index[0]&0x78)<<7; /* Bit 2..5 */ - (*bitstreamPtr) |= (enc_bits->gain_index[0]&0x10)<<5; /* Bit 6 */ - (*bitstreamPtr) |= (enc_bits->gain_index[1]&0x8)<<5; /* Bit 7 */ - (*bitstreamPtr) |= (enc_bits->cb_index[3]&0xFC); /* Bit 8..13 */ - (*bitstreamPtr) |= (enc_bits->gain_index[3]&0x10)>>3; /* Bit 14 */ - (*bitstreamPtr) |= (enc_bits->gain_index[4]&0x8)>>3; /* Bit 15 */ - } - /* Class 2 bits of ULP */ - /* 4:th to 6:th WebRtc_Word16 for 20 ms case - 5:th to 7:th WebRtc_Word16 for 30 ms case */ - bitstreamPtr++; - tmpPtr=enc_bits->idxVec; - for (k=0; k<3; k++) { - (*bitstreamPtr) = 0; - for (i=15; i>=0; i--) { - (*bitstreamPtr) |= ((WebRtc_UWord16)((*tmpPtr)&0x4)>>2)<6; i--) { - (*bitstreamPtr) |= ((WebRtc_UWord16)((*tmpPtr)&0x4)>>2)<gain_index[1]&0x4)<<4; /* Bit 9 */ - (*bitstreamPtr) |= (enc_bits->gain_index[3]&0xC)<<2; /* Bit 10..11 */ - (*bitstreamPtr) |= (enc_bits->gain_index[4]&0x4)<<1; /* Bit 12 */ - (*bitstreamPtr) |= (enc_bits->gain_index[6]&0x8)>>1; /* Bit 13 */ - (*bitstreamPtr) |= (enc_bits->gain_index[7]&0xC)>>2; /* Bit 14..15 */ - - } else { /* mode==30 */ - /* 8:th WebRtc_Word16 */ - (*bitstreamPtr) = 0; - for (i=15; i>5; i--) { - (*bitstreamPtr) |= ((WebRtc_UWord16)((*tmpPtr)&0x4)>>2)<cb_index[0]&0x6)<<3; /* Bit 10..11 */ - (*bitstreamPtr) |= (enc_bits->gain_index[0]&0x8); /* Bit 12 */ - (*bitstreamPtr) |= (enc_bits->gain_index[1]&0x4); /* Bit 13 */ - (*bitstreamPtr) |= (enc_bits->cb_index[3]&0x2); /* Bit 14 */ - (*bitstreamPtr) |= (enc_bits->cb_index[6]&0x80)>>7; /* Bit 15 */ - bitstreamPtr++; - /* 9:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)enc_bits->cb_index[6]&0x7E)<<9;/* Bit 0..5 */ - (*bitstreamPtr) |= (enc_bits->cb_index[9]&0xFE)<<2; /* Bit 6..12 */ - (*bitstreamPtr) |= (enc_bits->cb_index[12]&0xE0)>>5; /* Bit 13..15 */ - bitstreamPtr++; - /* 10:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)enc_bits->cb_index[12]&0x1E)<<11;/* Bit 0..3 */ - (*bitstreamPtr) |= (enc_bits->gain_index[3]&0xC)<<8; /* Bit 4..5 */ - (*bitstreamPtr) |= (enc_bits->gain_index[4]&0x6)<<7; /* Bit 6..7 */ - (*bitstreamPtr) |= (enc_bits->gain_index[6]&0x18)<<3; /* Bit 8..9 */ - (*bitstreamPtr) |= (enc_bits->gain_index[7]&0xC)<<2; /* Bit 10..11 */ - (*bitstreamPtr) |= (enc_bits->gain_index[9]&0x10)>>1; /* Bit 12 */ - (*bitstreamPtr) |= (enc_bits->gain_index[10]&0x8)>>1; /* Bit 13 */ - (*bitstreamPtr) |= (enc_bits->gain_index[12]&0x10)>>3; /* Bit 14 */ - (*bitstreamPtr) |= (enc_bits->gain_index[13]&0x8)>>3; /* Bit 15 */ - } - bitstreamPtr++; - /* Class 3 bits of ULP */ - /* 8:th to 14:th WebRtc_Word16 for 20 ms case - 11:th to 17:th WebRtc_Word16 for 30 ms case */ - tmpPtr=enc_bits->idxVec; - for (k=0; k<7; k++) { - (*bitstreamPtr) = 0; - for (i=14; i>=0; i-=2) { - (*bitstreamPtr) |= ((WebRtc_UWord16)((*tmpPtr)&0x3))<idxVec[56])&0x3))<<14;/* Bit 0..1 */ - (*bitstreamPtr) |= (((enc_bits->cb_index[0])&1))<<13; /* Bit 2 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[1]))<<6; /* Bit 3..9 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[2])&0x7E)>>1; /* Bit 10..15 */ - bitstreamPtr++; - /* 16:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)((enc_bits->cb_index[2])&0x1))<<15; - /* Bit 0 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[0])&0x7)<<12; /* Bit 1..3 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[1])&0x3)<<10; /* Bit 4..5 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[2]))<<7; /* Bit 6..8 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[3])&0x1)<<6; /* Bit 9 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[4])&0x7E)>>1; /* Bit 10..15 */ - bitstreamPtr++; - /* 17:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)((enc_bits->cb_index[4])&0x1))<<15; - /* Bit 0 */ - (*bitstreamPtr) |= (enc_bits->cb_index[5])<<8; /* Bit 1..7 */ - (*bitstreamPtr) |= (enc_bits->cb_index[6]); /* Bit 8..15 */ - bitstreamPtr++; - /* 18:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[7]))<<8; /* Bit 0..7 */ - (*bitstreamPtr) |= (enc_bits->cb_index[8]); /* Bit 8..15 */ - bitstreamPtr++; - /* 19:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)((enc_bits->gain_index[3])&0x3))<<14; - /* Bit 0..1 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[4])&0x3)<<12; /* Bit 2..3 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[5]))<<9; /* Bit 4..6 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[6])&0x7)<<6; /* Bit 7..9 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[7])&0x3)<<4; /* Bit 10..11 */ - (*bitstreamPtr) |= (enc_bits->gain_index[8])<<1; /* Bit 12..14 */ - } else { /* mode==30 */ - /* 18:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)((enc_bits->idxVec[56])&0x3))<<14;/* Bit 0..1 */ - (*bitstreamPtr) |= (((enc_bits->idxVec[57])&0x3))<<12; /* Bit 2..3 */ - (*bitstreamPtr) |= (((enc_bits->cb_index[0])&1))<<11; /* Bit 4 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[1]))<<4; /* Bit 5..11 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[2])&0x78)>>3; /* Bit 12..15 */ - bitstreamPtr++; - /* 19:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[2])&0x7)<<13; - /* Bit 0..2 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[0])&0x7)<<10; /* Bit 3..5 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[1])&0x3)<<8; /* Bit 6..7 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[2])&0x7)<<5; /* Bit 8..10 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[3])&0x1)<<4; /* Bit 11 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[4])&0x78)>>3; /* Bit 12..15 */ - bitstreamPtr++; - /* 20:th WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[4])&0x7)<<13; - /* Bit 0..2 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[5]))<<6; /* Bit 3..9 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[6])&0x1)<<5; /* Bit 10 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[7])&0xF8)>>3; /* Bit 11..15 */ - bitstreamPtr++; - /* 21:st WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[7])&0x7)<<13; - /* Bit 0..2 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[8]))<<5; /* Bit 3..10 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[9])&0x1)<<4; /* Bit 11 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[10])&0xF0)>>4; /* Bit 12..15 */ - bitstreamPtr++; - /* 22:nd WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[10])&0xF)<<12; - /* Bit 0..3 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[11]))<<4; /* Bit 4..11 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[12])&0x1)<<3; /* Bit 12 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[13])&0xE0)>>5; /* Bit 13..15 */ - bitstreamPtr++; - /* 23:rd WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->cb_index[13])&0x1F)<<11; - /* Bit 0..4 */ - (*bitstreamPtr) |= ((enc_bits->cb_index[14]))<<3; /* Bit 5..12 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[3])&0x3)<<1; /* Bit 13..14 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[4])&0x1); /* Bit 15 */ - bitstreamPtr++; - /* 24:rd WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->gain_index[5]))<<13; - /* Bit 0..2 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[6])&0x7)<<10; /* Bit 3..5 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[7])&0x3)<<8; /* Bit 6..7 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[8]))<<5; /* Bit 8..10 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[9])&0xF)<<1; /* Bit 11..14 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[10])&0x4)>>2; /* Bit 15 */ - bitstreamPtr++; - /* 25:rd WebRtc_Word16 */ - (*bitstreamPtr) = ((WebRtc_UWord16)(enc_bits->gain_index[10])&0x3)<<14; - /* Bit 0..1 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[11]))<<11; /* Bit 2..4 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[12])&0xF)<<7; /* Bit 5..8 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[13])&0x7)<<4; /* Bit 9..11 */ - (*bitstreamPtr) |= ((enc_bits->gain_index[14]))<<1; /* Bit 12..14 */ - } - /* Last bit is automatically zero */ - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/pack_bits.h b/modules/audio_coding/codecs/iLBC/main/source/pack_bits.h deleted file mode 100644 index ed3f22469..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/pack_bits.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_PackBits.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_PACK_BITS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_PACK_BITS_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * unpacking of bits from bitstream, i.e., vector of bytes - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_PackBits( - WebRtc_UWord16 *bitstream, /* (o) The packetized bitstream */ - iLBC_bits *enc_bits, /* (i) Encoded bits */ - WebRtc_Word16 mode /* (i) Codec mode (20 or 30) */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.c b/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.c deleted file mode 100644 index fe91851bd..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Poly2Lsf.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "poly_to_lsp.h" -#include "lsp_to_lsf.h" - -void WebRtcIlbcfix_Poly2Lsf( - WebRtc_Word16 *lsf, /* (o) lsf coefficients (Q13) */ - WebRtc_Word16 *a /* (i) A coefficients (Q12) */ - ) { - WebRtc_Word16 lsp[10]; - WebRtcIlbcfix_Poly2Lsp(a, lsp, (WebRtc_Word16*)WebRtcIlbcfix_kLspMean); - WebRtcIlbcfix_Lsp2Lsf(lsp, lsf, 10); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.h b/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.h deleted file mode 100644 index 0ea595ee8..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsf.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Poly2Lsf.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_POLY_TO_LSF_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_POLY_TO_LSF_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * conversion from lpc coefficients to lsf coefficients - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Poly2Lsf( - WebRtc_Word16 *lsf, /* (o) lsf coefficients (Q13) */ - WebRtc_Word16 *a /* (i) A coefficients (Q12) */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.c b/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.c deleted file mode 100644 index 29b4213c4..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Poly2Lsp.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "chebyshev.h" - -/*----------------------------------------------------------------* - * conversion from lpc coefficients to lsp coefficients - * function is only for 10:th order LPC - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Poly2Lsp( - WebRtc_Word16 *a, /* (o) A coefficients in Q12 */ - WebRtc_Word16 *lsp, /* (i) LSP coefficients in Q15 */ - WebRtc_Word16 *old_lsp /* (i) old LSP coefficients that are used if the new - coefficients turn out to be unstable */ - ) { - WebRtc_Word16 f[2][6]; /* f[0][] represents f1 and f[1][] represents f2 */ - WebRtc_Word16 *a_i_ptr, *a_10mi_ptr; - WebRtc_Word16 *f1ptr, *f2ptr; - WebRtc_Word32 tmpW32; - WebRtc_Word16 x, y, xlow, ylow, xmid, ymid, xhigh, yhigh, xint; - WebRtc_Word16 shifts, sign; - int i, j; - int foundFreqs; - int fi_select; - - /* - Calculate the two polynomials f1(z) and f2(z) - (the sum and the diff polynomial) - f1[0] = f2[0] = 1.0; - f1[i+1] = a[i+1] + a[10-i] - f1[i]; - f2[i+1] = a[i+1] - a[10-i] - f1[i]; - */ - - a_i_ptr = a + 1; - a_10mi_ptr = a + 10; - f1ptr = f[0]; - f2ptr = f[1]; - (*f1ptr) = 1024; /* 1.0 in Q10 */ - (*f2ptr) = 1024; /* 1.0 in Q10 */ - for (i = 0; i < 5; i++) { - (*(f1ptr+1)) = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_W32(((WebRtc_Word32)(*a_i_ptr)+(*a_10mi_ptr)), 2) - (*f1ptr)); - (*(f2ptr+1)) = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_W32(((WebRtc_Word32)(*a_i_ptr)-(*a_10mi_ptr)), 2) + (*f2ptr)); - a_i_ptr++; - a_10mi_ptr--; - f1ptr++; - f2ptr++; - } - - /* - find the LSPs using the Chebychev pol. evaluation - */ - - fi_select = 0; /* selector between f1 and f2, start with f1 */ - - foundFreqs = 0; - - xlow = WebRtcIlbcfix_kCosGrid[0]; - ylow = WebRtcIlbcfix_Chebyshev(xlow, f[fi_select]); - - /* - Iterate until all the 10 LSP's have been found or - all the grid points have been tried. If the 10 LSP's can - not be found, set the LSP vector to previous LSP - */ - - for (j = 1; j < COS_GRID_POINTS && foundFreqs < 10; j++) { - xhigh = xlow; - yhigh = ylow; - xlow = WebRtcIlbcfix_kCosGrid[j]; - ylow = WebRtcIlbcfix_Chebyshev(xlow, f[fi_select]); - - if (WEBRTC_SPL_MUL_16_16(ylow, yhigh) <= 0) { - /* Run 4 times to reduce the interval */ - for (i = 0; i < 4; i++) { - /* xmid =(xlow + xhigh)/2 */ - xmid = WEBRTC_SPL_RSHIFT_W16(xlow, 1) + WEBRTC_SPL_RSHIFT_W16(xhigh, 1); - ymid = WebRtcIlbcfix_Chebyshev(xmid, f[fi_select]); - - if (WEBRTC_SPL_MUL_16_16(ylow, ymid) <= 0) { - yhigh = ymid; - xhigh = xmid; - } else { - ylow = ymid; - xlow = xmid; - } - } - - /* - Calculater xint by linear interpolation: - xint = xlow - ylow*(xhigh-xlow)/(yhigh-ylow); - */ - - x = xhigh - xlow; - y = yhigh - ylow; - - if (y == 0) { - xint = xlow; - } else { - sign = y; - y = WEBRTC_SPL_ABS_W16(y); - shifts = (WebRtc_Word16)WebRtcSpl_NormW32(y)-16; - y = WEBRTC_SPL_LSHIFT_W16(y, shifts); - y = (WebRtc_Word16)WebRtcSpl_DivW32W16(536838144, y); /* 1/(yhigh-ylow) */ - - tmpW32 = WEBRTC_SPL_MUL_16_16_RSFT(x, y, (19-shifts)); - - /* y=(xhigh-xlow)/(yhigh-ylow) */ - y = (WebRtc_Word16)(tmpW32&0xFFFF); - - if (sign < 0) { - y = -y; - } - /* tmpW32 = ylow*(xhigh-xlow)/(yhigh-ylow) */ - tmpW32 = WEBRTC_SPL_MUL_16_16_RSFT(ylow, y, 10); - xint = xlow-(WebRtc_Word16)(tmpW32&0xFFFF); - } - - /* Store the calculated lsp */ - lsp[foundFreqs] = (WebRtc_Word16)xint; - foundFreqs++; - - /* if needed, set xlow and ylow for next recursion */ - if (foundFreqs<10) { - xlow = xint; - /* Swap between f1 and f2 (f[0][] and f[1][]) */ - fi_select = ((fi_select+1)&0x1); - - ylow = WebRtcIlbcfix_Chebyshev(xlow, f[fi_select]); - } - } - } - - /* Check if M roots found, if not then use the old LSP */ - if (foundFreqs < 10) { - WEBRTC_SPL_MEMCPY_W16(lsp, old_lsp, 10); - } - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.h b/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.h deleted file mode 100644 index 7eebb25d1..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/poly_to_lsp.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Poly2Lsp.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_POLY_TO_LSP_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_POLY_TO_LSP_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * conversion from lpc coefficients to lsp coefficients - * function is only for 10:th order LPC - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Poly2Lsp( - WebRtc_Word16 *a, /* (o) A coefficients in Q12 */ - WebRtc_Word16 *lsp, /* (i) LSP coefficients in Q15 */ - WebRtc_Word16 *old_lsp /* (i) old LSP coefficients that are used if the new - coefficients turn out to be unstable */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/refiner.c b/modules/audio_coding/codecs/iLBC/main/source/refiner.c deleted file mode 100644 index e57fa920d..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/refiner.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Refiner.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "enh_upsample.h" -#include "my_corr.h" - -/*----------------------------------------------------------------* - * find segment starting near idata+estSegPos that has highest - * correlation with idata+centerStartPos through - * idata+centerStartPos+ENH_BLOCKL-1 segment is found at a - * resolution of ENH_UPSO times the original of the original - * sampling rate - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Refiner( - iLBC_Dec_Inst_t *iLBCdec_inst, - /* (i) Decoder state */ - WebRtc_Word16 *updStartPos, /* (o) updated start point (Q-2) */ - WebRtc_Word16 *idata, /* (i) original data buffer */ - WebRtc_Word16 idatal, /* (i) dimension of idata */ - WebRtc_Word16 centerStartPos, /* (i) beginning center segment */ - WebRtc_Word16 estSegPos, /* (i) estimated beginning other segment (Q-2) */ - WebRtc_Word16 *surround, /* (i/o) The contribution from this sequence - summed with earlier contributions */ - WebRtc_Word16 gain /* (i) Gain to use for this sequence */ - ){ - WebRtc_Word16 estSegPosRounded,searchSegStartPos,searchSegEndPos,corrdim; - WebRtc_Word16 tloc,tloc2,i,st,en,fraction; - - WebRtc_Word32 maxtemp, scalefact; - WebRtc_Word16 *filtStatePtr, *polyPtr; - /* Stack based */ - WebRtc_Word16 filt[7]; - WebRtc_Word32 corrVecUps[ENH_CORRDIM*ENH_UPS0]; - WebRtc_Word32 corrVecTemp[ENH_CORRDIM]; - WebRtc_Word16 vect[ENH_VECTL]; - WebRtc_Word16 corrVec[ENH_CORRDIM]; - - /* The input parameter iLBCdec_inst is unused unless using scratch memory */ - iLBCdec_inst = iLBCdec_inst; - - /* defining array bounds */ - - estSegPosRounded=WEBRTC_SPL_RSHIFT_W16((estSegPos - 2),2); - - searchSegStartPos=estSegPosRounded-ENH_SLOP; - - if (searchSegStartPos<0) { - searchSegStartPos=0; - } - searchSegEndPos=estSegPosRounded+ENH_SLOP; - - if(searchSegEndPos+ENH_BLOCKL >= idatal) { - searchSegEndPos=idatal-ENH_BLOCKL-1; - } - corrdim=searchSegEndPos-searchSegStartPos+1; - - /* compute upsampled correlation and find - location of max */ - - WebRtcIlbcfix_MyCorr(corrVecTemp,idata+searchSegStartPos, - (WebRtc_Word16)(corrdim+ENH_BLOCKL-1),idata+centerStartPos,ENH_BLOCKL); - - /* Calculate the rescaling factor for the correlation in order to - put the correlation in a WebRtc_Word16 vector instead */ - maxtemp=WebRtcSpl_MaxAbsValueW32(corrVecTemp, (WebRtc_Word16)corrdim); - - scalefact=WebRtcSpl_GetSizeInBits(maxtemp)-15; - - if (scalefact>0) { - for (i=0;iidatal){ - WEBRTC_SPL_MEMCPY_W16(vect, &idata[st], - (ENH_VECTL-(en-idatal))); - WebRtcSpl_MemSetW16(&vect[ENH_VECTL-(en-idatal)], 0, - (WebRtc_Word16)(en-idatal)); - } - else { - WEBRTC_SPL_MEMCPY_W16(vect, &idata[st], ENH_VECTL); - } - } - /* Calculate which of the 4 fractions to use */ - fraction=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16(tloc2,ENH_UPS0)-tloc; - - /* compute the segment (this is actually a convolution) */ - - filtStatePtr = filt + 6; - polyPtr = (WebRtc_Word16*)WebRtcIlbcfix_kEnhPolyPhaser[fraction]; - for (i=0;i<7;i++) { - *filtStatePtr-- = *polyPtr++; - } - - WebRtcSpl_FilterMAFastQ12( - &vect[6], vect, filt, - ENH_FLO_MULT2_PLUS1, ENH_BLOCKL); - - /* Add the contribution from this vector (scaled with gain) to the total surround vector */ - WebRtcSpl_AddAffineVectorToVector( - surround, vect, gain, - (WebRtc_Word32)32768, 16, ENH_BLOCKL); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/refiner.h b/modules/audio_coding/codecs/iLBC/main/source/refiner.h deleted file mode 100644 index 02ad63211..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/refiner.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Refiner.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_REFINER_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_REFINER_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * find segment starting near idata+estSegPos that has highest - * correlation with idata+centerStartPos through - * idata+centerStartPos+ENH_BLOCKL-1 segment is found at a - * resolution of ENH_UPSO times the original of the original - * sampling rate - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Refiner( - iLBC_Dec_Inst_t *iLBCdec_inst, - /* (i) Decoder state */ - WebRtc_Word16 *updStartPos, /* (o) updated start point (Q-2) */ - WebRtc_Word16 *idata, /* (i) original data buffer */ - WebRtc_Word16 idatal, /* (i) dimension of idata */ - WebRtc_Word16 centerStartPos, /* (i) beginning center segment */ - WebRtc_Word16 estSegPos, /* (i) estimated beginning other segment (Q-2) */ - WebRtc_Word16 *surround, /* (i/o) The contribution from this sequence - summed with earlier contributions */ - WebRtc_Word16 gain /* (i) Gain to use for this sequence */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.c b/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.c deleted file mode 100644 index d094087e9..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleInterpolateLsf.c - -******************************************************************/ - -#include "defines.h" -#include "lsf_interpolate_to_poly_enc.h" -#include "bw_expand.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * lsf interpolator (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleInterpolateLsf( - WebRtc_Word16 *syntdenum, /* (o) the synthesis filter denominator - resulting from the quantized - interpolated lsf Q12 */ - WebRtc_Word16 *weightdenum, /* (o) the weighting filter denominator - resulting from the unquantized - interpolated lsf Q12 */ - WebRtc_Word16 *lsf, /* (i) the unquantized lsf coefficients Q13 */ - WebRtc_Word16 *lsfdeq, /* (i) the dequantized lsf coefficients Q13 */ - WebRtc_Word16 *lsfold, /* (i) the unquantized lsf coefficients of - the previous signal frame Q13 */ - WebRtc_Word16 *lsfdeqold, /* (i) the dequantized lsf coefficients of the - previous signal frame Q13 */ - WebRtc_Word16 length, /* (i) should equate FILTERORDER */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ) { - int i, pos, lp_length; - - WebRtc_Word16 *lsf2, *lsfdeq2; - /* Stack based */ - WebRtc_Word16 lp[LPC_FILTERORDER + 1]; - - lsf2 = lsf + length; - lsfdeq2 = lsfdeq + length; - lp_length = length + 1; - - if (iLBCenc_inst->mode==30) { - /* subframe 1: Interpolation between old and first set of - lsf coefficients */ - - /* Calculate Analysis/Syntehsis filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsfdeqold, lsfdeq, - WebRtcIlbcfix_kLsfWeight30ms[0], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum, lp, lp_length); - - /* Calculate Weighting filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsfold, lsf, WebRtcIlbcfix_kLsfWeight30ms[0], length); - WebRtcIlbcfix_BwExpand(weightdenum, lp, (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpWeightDenum, (WebRtc_Word16)lp_length); - - /* subframe 2 to 6: Interpolation between first and second - set of lsf coefficients */ - - pos = lp_length; - for (i = 1; i < iLBCenc_inst->nsub; i++) { - - /* Calculate Analysis/Syntehsis filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsfdeq, lsfdeq2, - WebRtcIlbcfix_kLsfWeight30ms[i], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum + pos, lp, lp_length); - - /* Calculate Weighting filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsf, lsf2, - WebRtcIlbcfix_kLsfWeight30ms[i], length); - WebRtcIlbcfix_BwExpand(weightdenum + pos, lp, - (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpWeightDenum, (WebRtc_Word16)lp_length); - - pos += lp_length; - } - - /* update memory */ - - WEBRTC_SPL_MEMCPY_W16(lsfold, lsf2, length); - WEBRTC_SPL_MEMCPY_W16(lsfdeqold, lsfdeq2, length); - - } else { /* iLBCenc_inst->mode==20 */ - pos = 0; - for (i = 0; i < iLBCenc_inst->nsub; i++) { - - /* Calculate Analysis/Syntehsis filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsfdeqold, lsfdeq, - WebRtcIlbcfix_kLsfWeight20ms[i], length); - WEBRTC_SPL_MEMCPY_W16(syntdenum + pos, lp, lp_length); - - /* Calculate Weighting filter from quantized LSF */ - WebRtcIlbcfix_LsfInterpolate2PloyEnc(iLBCenc_inst, lp, lsfold, lsf, - WebRtcIlbcfix_kLsfWeight20ms[i], length); - WebRtcIlbcfix_BwExpand(weightdenum+pos, lp, - (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpWeightDenum, (WebRtc_Word16)lp_length); - - pos += lp_length; - } - - /* update memory */ - - WEBRTC_SPL_MEMCPY_W16(lsfold, lsf, length); - WEBRTC_SPL_MEMCPY_W16(lsfdeqold, lsfdeq, length); - - } - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.h b/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.h deleted file mode 100644 index 8cdd7da53..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_interpolate_lsf.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleInterpolateLsf.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_INTERPOLATE_LSF_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_INTERPOLATE_LSF_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lsf interpolator (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleInterpolateLsf( - WebRtc_Word16 *syntdenum, /* (o) the synthesis filter denominator - resulting from the quantized - interpolated lsf Q12 */ - WebRtc_Word16 *weightdenum, /* (o) the weighting filter denominator - resulting from the unquantized - interpolated lsf Q12 */ - WebRtc_Word16 *lsf, /* (i) the unquantized lsf coefficients Q13 */ - WebRtc_Word16 *lsfdeq, /* (i) the dequantized lsf coefficients Q13 */ - WebRtc_Word16 *lsfold, /* (i) the unquantized lsf coefficients of - the previous signal frame Q13 */ - WebRtc_Word16 *lsfdeqold, /* (i) the dequantized lsf coefficients of the - previous signal frame Q13 */ - WebRtc_Word16 length, /* (i) should equate FILTERORDER */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.c b/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.c deleted file mode 100644 index 2d19eddf8..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLpcAnalysis.c - -******************************************************************/ - -#include "defines.h" -#include "window32_w32.h" -#include "bw_expand.h" -#include "poly_to_lsf.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * lpc analysis (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLpcAnalysis( - WebRtc_Word16 *lsf, /* (o) lsf coefficients */ - WebRtc_Word16 *data, /* (i) new block of speech */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ) { - int k; - int scale; - WebRtc_Word16 is; - WebRtc_Word16 stability; - /* Stack based */ - WebRtc_Word16 A[LPC_FILTERORDER + 1]; - WebRtc_Word32 R[LPC_FILTERORDER + 1]; - WebRtc_Word16 windowedData[BLOCKL_MAX]; - WebRtc_Word16 rc[LPC_FILTERORDER]; - - is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; - WEBRTC_SPL_MEMCPY_W16(iLBCenc_inst->lpc_buffer+is,data,iLBCenc_inst->blockl); - - /* No lookahead, last window is asymmetric */ - - for (k = 0; k < iLBCenc_inst->lpc_n; k++) { - - is = LPC_LOOKBACK; - - if (k < (iLBCenc_inst->lpc_n - 1)) { - - /* Hanning table WebRtcIlbcfix_kLpcWin[] is in Q15-domain so the output is right-shifted 15 */ - WebRtcSpl_ElementwiseVectorMult(windowedData, iLBCenc_inst->lpc_buffer, WebRtcIlbcfix_kLpcWin, BLOCKL_MAX, 15); - } else { - - /* Hanning table WebRtcIlbcfix_kLpcAsymWin[] is in Q15-domain so the output is right-shifted 15 */ - WebRtcSpl_ElementwiseVectorMult(windowedData, iLBCenc_inst->lpc_buffer+is, WebRtcIlbcfix_kLpcAsymWin, BLOCKL_MAX, 15); - } - - /* Compute autocorrelation */ - WebRtcSpl_AutoCorrelation(windowedData, BLOCKL_MAX, LPC_FILTERORDER, R, &scale); - - /* Window autocorrelation vector */ - WebRtcIlbcfix_Window32W32(R, R, WebRtcIlbcfix_kLpcLagWin, LPC_FILTERORDER + 1 ); - - /* Calculate the A coefficients from the Autocorrelation using Levinson Durbin algorithm */ - stability=WebRtcSpl_LevinsonDurbin(R, A, rc, LPC_FILTERORDER); - - /* - Set the filter to {1.0, 0.0, 0.0,...} if filter from Levinson Durbin algorithm is unstable - This should basically never happen... - */ - if (stability!=1) { - A[0]=4096; - WebRtcSpl_MemSetW16(&A[1], 0, LPC_FILTERORDER); - } - - /* Bandwidth expand the filter coefficients */ - WebRtcIlbcfix_BwExpand(A, A, (WebRtc_Word16*)WebRtcIlbcfix_kLpcChirpSyntDenum, LPC_FILTERORDER+1); - - /* Convert from A to LSF representation */ - WebRtcIlbcfix_Poly2Lsf(lsf + k*LPC_FILTERORDER, A); - } - - is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl; - WEBRTC_SPL_MEMCPY_W16(iLBCenc_inst->lpc_buffer, - iLBCenc_inst->lpc_buffer+LPC_LOOKBACK+BLOCKL_MAX-is, is); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.h b/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.h deleted file mode 100644 index 83c1e5b6f..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lpc_analysis.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLpcAnalysis.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LPC_ANALYSIS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LPC_ANALYSIS_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lpc analysis (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLpcAnalysis( - WebRtc_Word16 *lsf, /* (o) lsf coefficients */ - WebRtc_Word16 *data, /* (i) new block of speech */ - iLBC_Enc_Inst_t *iLBCenc_inst - /* (i/o) the encoder state structure */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.c b/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.c deleted file mode 100644 index 7b5efa075..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLsfDeQ.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * obtain dequantized lsf coefficients from quantization index - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLsfDeQ( - WebRtc_Word16 *lsfdeq, /* (o) dequantized lsf coefficients */ - WebRtc_Word16 *index, /* (i) quantization index */ - WebRtc_Word16 lpc_n /* (i) number of LPCs */ - ){ - int i, j, pos, cb_pos; - - /* decode first LSF */ - - pos = 0; - cb_pos = 0; - for (i = 0; i < LSF_NSPLIT; i++) { - for (j = 0; j < WebRtcIlbcfix_kLsfDimCb[i]; j++) { - lsfdeq[pos + j] = WebRtcIlbcfix_kLsfCb[cb_pos + - WEBRTC_SPL_MUL_16_16(index[i], WebRtcIlbcfix_kLsfDimCb[i]) + j]; - } - pos += WebRtcIlbcfix_kLsfDimCb[i]; - cb_pos += WEBRTC_SPL_MUL_16_16(WebRtcIlbcfix_kLsfSizeCb[i], WebRtcIlbcfix_kLsfDimCb[i]); - } - - if (lpc_n>1) { - /* decode last LSF */ - pos = 0; - cb_pos = 0; - for (i = 0; i < LSF_NSPLIT; i++) { - for (j = 0; j < WebRtcIlbcfix_kLsfDimCb[i]; j++) { - lsfdeq[LPC_FILTERORDER + pos + j] = WebRtcIlbcfix_kLsfCb[cb_pos + - WEBRTC_SPL_MUL_16_16(index[LSF_NSPLIT + i], WebRtcIlbcfix_kLsfDimCb[i]) + j]; - } - pos += WebRtcIlbcfix_kLsfDimCb[i]; - cb_pos += WEBRTC_SPL_MUL_16_16(WebRtcIlbcfix_kLsfSizeCb[i], WebRtcIlbcfix_kLsfDimCb[i]); - } - } - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.h b/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.h deleted file mode 100644 index efd31030f..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_dequant.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLsfDeQ.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LSF_DEQUANT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LSF_DEQUANT_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * obtain dequantized lsf coefficients from quantization index - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLsfDeQ( - WebRtc_Word16 *lsfdeq, /* (o) dequantized lsf coefficients */ - WebRtc_Word16 *index, /* (i) quantization index */ - WebRtc_Word16 lpc_n /* (i) number of LPCs */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.c b/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.c deleted file mode 100644 index aa27fb4eb..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLsfQ.c - -******************************************************************/ - -#include "defines.h" -#include "split_vq.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * lsf quantizer (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLsfQ( - WebRtc_Word16 *lsfdeq, /* (o) dequantized lsf coefficients - (dimension FILTERORDER) Q13 */ - WebRtc_Word16 *index, /* (o) quantization index */ - WebRtc_Word16 *lsf, /* (i) the lsf coefficient vector to be - quantized (dimension FILTERORDER) Q13 */ - WebRtc_Word16 lpc_n /* (i) number of lsf sets to quantize */ - ){ - - /* Quantize first LSF with memoryless split VQ */ - WebRtcIlbcfix_SplitVq( lsfdeq, index, lsf, - (WebRtc_Word16*)WebRtcIlbcfix_kLsfCb, (WebRtc_Word16*)WebRtcIlbcfix_kLsfDimCb, (WebRtc_Word16*)WebRtcIlbcfix_kLsfSizeCb); - - if (lpc_n==2) { - /* Quantize second LSF with memoryless split VQ */ - WebRtcIlbcfix_SplitVq( lsfdeq + LPC_FILTERORDER, index + LSF_NSPLIT, - lsf + LPC_FILTERORDER, (WebRtc_Word16*)WebRtcIlbcfix_kLsfCb, - (WebRtc_Word16*)WebRtcIlbcfix_kLsfDimCb, (WebRtc_Word16*)WebRtcIlbcfix_kLsfSizeCb); - } - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.h b/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.h deleted file mode 100644 index fd17b2eab..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/simple_lsf_quant.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SimpleLsfQ.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LSF_QUANT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SIMPLE_LSF_QUANT_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * lsf quantizer (subrutine to LPCencode) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SimpleLsfQ( - WebRtc_Word16 *lsfdeq, /* (o) dequantized lsf coefficients - (dimension FILTERORDER) Q13 */ - WebRtc_Word16 *index, /* (o) quantization index */ - WebRtc_Word16 *lsf, /* (i) the lsf coefficient vector to be - quantized (dimension FILTERORDER) Q13 */ - WebRtc_Word16 lpc_n /* (i) number of lsf sets to quantize */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/smooth.c b/modules/audio_coding/codecs/iLBC/main/source/smooth.c deleted file mode 100644 index 207de6d56..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/smooth.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Smooth.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "smooth_out_data.h" - -/*----------------------------------------------------------------* - * find the smoothed output data - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Smooth( - WebRtc_Word16 *odata, /* (o) smoothed output */ - WebRtc_Word16 *current, /* (i) the un enhanced residual for - this block */ - WebRtc_Word16 *surround /* (i) The approximation from the - surrounding sequences */ - ) { - WebRtc_Word16 maxtot, scale, scale1, scale2; - WebRtc_Word16 A, B, C, denomW16; - WebRtc_Word32 B_W32, denom, num; - WebRtc_Word32 errs; - WebRtc_Word32 w00,w10,w11, endiff, crit; - WebRtc_Word32 w00prim, w10prim, w11_div_w00; - WebRtc_Word16 w11prim; - WebRtc_Word16 bitsw00, bitsw10, bitsw11; - WebRtc_Word32 w11w00, w10w10, w00w00; - WebRtc_Word16 max1, max2; - - /* compute some inner products (ensure no overflow by first calculating proper scale factor) */ - - w00 = w10 = w11 = 0; - current=current; - - max1=WebRtcSpl_MaxAbsValueW16(current, ENH_BLOCKL); - max2=WebRtcSpl_MaxAbsValueW16(surround, ENH_BLOCKL); - maxtot=WEBRTC_SPL_MAX(max1, max2); - - scale=WebRtcSpl_GetSizeInBits(maxtot); - scale = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(2,scale)-26; - scale=WEBRTC_SPL_MAX(0, scale); - - w00=WebRtcSpl_DotProductWithScale(current,current,ENH_BLOCKL,scale); - w11=WebRtcSpl_DotProductWithScale(surround,surround,ENH_BLOCKL,scale); - w10=WebRtcSpl_DotProductWithScale(surround,current,ENH_BLOCKL,scale); - - if (w00<0) w00 = WEBRTC_SPL_WORD32_MAX; - if (w11<0) w11 = WEBRTC_SPL_WORD32_MAX; - - /* Rescale w00 and w11 to w00prim and w11prim, so that w00prim/w11prim - is in Q16 */ - - bitsw00 = WebRtcSpl_GetSizeInBits(w00); - bitsw11 = WebRtcSpl_GetSizeInBits(w11); - bitsw10 = WebRtcSpl_GetSizeInBits(WEBRTC_SPL_ABS_W32(w10)); - scale1 = 31 - bitsw00; - scale2 = 15 - bitsw11; - - if (scale2>(scale1-16)) { - scale2 = scale1 - 16; - } else { - scale1 = scale2 + 16; - } - - w00prim = WEBRTC_SPL_LSHIFT_W32(w00, scale1); - w11prim = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(w11, scale2); - - /* Perform C = sqrt(w11/w00) (C is in Q11 since (16+6)/2=11) */ - if (w11prim>64) { - endiff = WEBRTC_SPL_LSHIFT_W32( - (WebRtc_Word32)WebRtcSpl_DivW32W16(w00prim, w11prim), 6); - C = (WebRtc_Word16)WebRtcSpl_Sqrt(endiff); /* C is in Q11 */ - } else { - C = 1; - } - - /* first try enhancement without power-constraint */ - - errs = WebRtcIlbcfix_Smooth_odata(odata, current, surround, C); - - - - /* if constraint violated by first try, add constraint */ - - if ( (6-scale+scale1) > 31) { - crit=0; - } else { - /* crit = 0.05 * w00 (Result in Q-6) */ - crit = WEBRTC_SPL_SHIFT_W32( - WEBRTC_SPL_MUL(ENH_A0, WEBRTC_SPL_RSHIFT_W32(w00prim, 14)), - -(6-scale+scale1)); - } - - if (errs > crit) { - - if( w00 < 1) { - w00=1; - } - - /* Calculate w11*w00, w10*w10 and w00*w00 in the same Q domain */ - - scale1 = bitsw00-15; - scale2 = bitsw11-15; - - if (scale2>scale1) { - scale = scale2; - } else { - scale = scale1; - } - - w11w00 = WEBRTC_SPL_MUL_16_16( - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w11, -scale), - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w00, -scale)); - - w10w10 = WEBRTC_SPL_MUL_16_16( - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w10, -scale), - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w10, -scale)); - - w00w00 = WEBRTC_SPL_MUL_16_16( - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w00, -scale), - (WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(w00, -scale)); - - /* Calculate (w11*w00-w10*w10)/(w00*w00) in Q16 */ - if (w00w00>65536) { - endiff = (w11w00-w10w10); - endiff = WEBRTC_SPL_MAX(0, endiff); - /* denom is in Q16 */ - denom = WebRtcSpl_DivW32W16(endiff, (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(w00w00, 16)); - } else { - denom = 65536; - } - - if( denom > 7){ /* eliminates numerical problems - for if smooth */ - - scale=WebRtcSpl_GetSizeInBits(denom)-15; - - if (scale>0) { - /* denomW16 is in Q(16+scale) */ - denomW16=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(denom, scale); - - /* num in Q(34-scale) */ - num=WEBRTC_SPL_RSHIFT_W32(ENH_A0_MINUS_A0A0DIV4, scale); - } else { - /* denomW16 is in Q16 */ - denomW16=(WebRtc_Word16)denom; - - /* num in Q34 */ - num=ENH_A0_MINUS_A0A0DIV4; - } - - /* A sqrt( (ENH_A0-(ENH_A0^2)/4)*(w00*w00)/(w11*w00 + w10*w10) ) in Q9 */ - A = (WebRtc_Word16)WebRtcSpl_Sqrt(WebRtcSpl_DivW32W16(num, denomW16)); - - /* B_W32 is in Q30 ( B = 1 - ENH_A0/2 - A * w10/w00 ) */ - scale1 = 31-bitsw10; - scale2 = 21-scale1; - w10prim = WEBRTC_SPL_LSHIFT_W32(w10, scale1); - w00prim = WEBRTC_SPL_SHIFT_W32(w00, -scale2); - scale = bitsw00-scale2-15; - - if (scale>0) { - w10prim=WEBRTC_SPL_RSHIFT_W32(w10prim, scale); - w00prim=WEBRTC_SPL_RSHIFT_W32(w00prim, scale); - } - - if ((w00prim>0)&&(w10prim>0)) { - w11_div_w00=WebRtcSpl_DivW32W16(w10prim, (WebRtc_Word16)w00prim); - - if (WebRtcSpl_GetSizeInBits(w11_div_w00)+WebRtcSpl_GetSizeInBits(A)>31) { - B_W32 = 0; - } else { - B_W32 = (WebRtc_Word32)1073741824 - (WebRtc_Word32)ENH_A0DIV2 - - WEBRTC_SPL_MUL(A, w11_div_w00); - } - B = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(B_W32, 16); /* B in Q14 */ - } else { - /* No smoothing */ - A = 0; - B = 16384; /* 1 in Q14 */ - } - } - else{ /* essentially no difference between cycles; - smoothing not needed */ - - A = 0; - B = 16384; /* 1 in Q14 */ - } - - /* create smoothed sequence */ - - WebRtcSpl_ScaleAndAddVectors(surround, A, 9, - current, B, 14, - odata, ENH_BLOCKL); - } - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/smooth.h b/modules/audio_coding/codecs/iLBC/main/source/smooth.h deleted file mode 100644 index 88ce805d1..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/smooth.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Smooth.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SMOOTH_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SMOOTH_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * find the smoothed output data - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Smooth( - WebRtc_Word16 *odata, /* (o) smoothed output */ - WebRtc_Word16 *current, /* (i) the un enhanced residual for - this block */ - WebRtc_Word16 *surround /* (i) The approximation from the - surrounding sequences */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.c b/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.c deleted file mode 100644 index 9bacd85e8..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Smooth_odata.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -WebRtc_Word32 WebRtcIlbcfix_Smooth_odata( - WebRtc_Word16 *odata, - WebRtc_Word16 *psseq, - WebRtc_Word16 *surround, - WebRtc_Word16 C) -{ - int i; - - WebRtc_Word16 err; - WebRtc_Word32 errs; - - for(i=0;i<80;i++) { - odata[i]= (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(C, surround[i])+1024), 11); - } - - errs=0; - for(i=0;i<80;i++) { - err=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16((psseq[i]-odata[i]), 3); - errs+=WEBRTC_SPL_MUL_16_16(err, err); /* errs in Q-6 */ - } - - return errs; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.h b/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.h deleted file mode 100644 index 6fbe694f5..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/smooth_out_data.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Smooth_odata.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SMOOTH_OUT_DATA_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SMOOTH_OUT_DATA_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * help function to WebRtcIlbcfix_Smooth() - *---------------------------------------------------------------*/ - -WebRtc_Word32 WebRtcIlbcfix_Smooth_odata( - WebRtc_Word16 *odata, - WebRtc_Word16 *psseq, - WebRtc_Word16 *surround, - WebRtc_Word16 C); - - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/sort_sq.c b/modules/audio_coding/codecs/iLBC/main/source/sort_sq.c deleted file mode 100644 index 9276a7ba9..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/sort_sq.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SortSq.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * scalar quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SortSq( - WebRtc_Word16 *xq, /* (o) the quantized value */ - WebRtc_Word16 *index, /* (o) the quantization index */ - WebRtc_Word16 x, /* (i) the value to quantize */ - const WebRtc_Word16 *cb, /* (i) the quantization codebook */ - WebRtc_Word16 cb_size /* (i) the size of the quantization codebook */ - ){ - int i; - - if (x <= cb[0]) { - *index = 0; - *xq = cb[0]; - } else { - i = 0; - while ((x > cb[i]) && (i < (cb_size-1))) { - i++; - } - - if (x > WEBRTC_SPL_RSHIFT_W32(( (WebRtc_Word32)cb[i] + cb[i - 1] + 1),1)) { - *index = i; - *xq = cb[i]; - } else { - *index = i - 1; - *xq = cb[i - 1]; - } - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/sort_sq.h b/modules/audio_coding/codecs/iLBC/main/source/sort_sq.h deleted file mode 100644 index 2863dc525..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/sort_sq.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SortSq.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SORT_SQ_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SORT_SQ_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * scalar quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SortSq( - WebRtc_Word16 *xq, /* (o) the quantized value */ - WebRtc_Word16 *index, /* (o) the quantization index */ - WebRtc_Word16 x, /* (i) the value to quantize */ - const WebRtc_Word16 *cb, /* (i) the quantization codebook */ - WebRtc_Word16 cb_size /* (i) the size of the quantization codebook */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/split_vq.c b/modules/audio_coding/codecs/iLBC/main/source/split_vq.c deleted file mode 100644 index d908fa225..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/split_vq.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SplitVq.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" -#include "vq3.h" -#include "vq4.h" - -/*----------------------------------------------------------------* - * split vector quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SplitVq( - WebRtc_Word16 *qX, /* (o) the quantized vector in Q13 */ - WebRtc_Word16 *index, /* (o) a vector of indexes for all vector - codebooks in the split */ - WebRtc_Word16 *X, /* (i) the vector to quantize */ - WebRtc_Word16 *CB, /* (i) the quantizer codebook in Q13 */ - WebRtc_Word16 *dim, /* (i) the dimension of X and qX */ - WebRtc_Word16 *cbsize /* (i) the number of vectors in the codebook */ - ) { - - WebRtc_Word16 *qXPtr, *indexPtr, *CBPtr, *XPtr; - - /* Quantize X with the 3 vectror quantization tables */ - - qXPtr=qX; - indexPtr=index; - CBPtr=CB; - XPtr=X; - WebRtcIlbcfix_Vq3(qXPtr, indexPtr, CBPtr, XPtr, cbsize[0]); - - qXPtr+=3; - indexPtr+=1; - CBPtr+=(dim[0]*cbsize[0]); - XPtr+=3; - WebRtcIlbcfix_Vq3(qXPtr, indexPtr, CBPtr, XPtr, cbsize[1]); - - qXPtr+=3; - indexPtr+=1; - CBPtr+=(dim[1]*cbsize[1]); - XPtr+=3; - WebRtcIlbcfix_Vq4(qXPtr, indexPtr, CBPtr, XPtr, cbsize[2]); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/split_vq.h b/modules/audio_coding/codecs/iLBC/main/source/split_vq.h deleted file mode 100644 index 7264a212a..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/split_vq.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SplitVq.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SPLIT_VQ_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SPLIT_VQ_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * split vector quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SplitVq( - WebRtc_Word16 *qX, /* (o) the quantized vector in Q13 */ - WebRtc_Word16 *index, /* (o) a vector of indexes for all vector - codebooks in the split */ - WebRtc_Word16 *X, /* (i) the vector to quantize */ - WebRtc_Word16 *CB, /* (i) the quantizer codebook in Q13 */ - WebRtc_Word16 *dim, /* (i) the dimension of X and qX */ - WebRtc_Word16 *cbsize /* (i) the number of vectors in the codebook */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/state_construct.c b/modules/audio_coding/codecs/iLBC/main/source/state_construct.c deleted file mode 100644 index 9d03cc360..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/state_construct.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_StateConstruct.c - -******************************************************************/ - -#include "defines.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * decoding of the start state - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_StateConstruct( - WebRtc_Word16 idxForMax, /* (i) 6-bit index for the quantization of - max amplitude */ - WebRtc_Word16 *idxVec, /* (i) vector of quantization indexes */ - WebRtc_Word16 *syntDenum, /* (i) synthesis filter denumerator */ - WebRtc_Word16 *Out_fix, /* (o) the decoded state vector */ - WebRtc_Word16 len /* (i) length of a state vector */ - ) { - int k; - WebRtc_Word16 maxVal; - WebRtc_Word16 *tmp1, *tmp2, *tmp3; - /* Stack based */ - WebRtc_Word16 numerator[1+LPC_FILTERORDER]; - WebRtc_Word16 sampleValVec[2*STATE_SHORT_LEN_30MS+LPC_FILTERORDER]; - WebRtc_Word16 sampleMaVec[2*STATE_SHORT_LEN_30MS+LPC_FILTERORDER]; - WebRtc_Word16 *sampleVal = &sampleValVec[LPC_FILTERORDER]; - WebRtc_Word16 *sampleMa = &sampleMaVec[LPC_FILTERORDER]; - WebRtc_Word16 *sampleAr = &sampleValVec[LPC_FILTERORDER]; - - /* initialization of coefficients */ - - for (k=0; k> 22); - tmp1++; - tmp2--; - } - } else if (idxForMax<59) { - for(k=0; k> 19); - tmp1++; - tmp2--; - } - } else { - for(k=0; k> 17); - tmp1++; - tmp2--; - } - } - - /* Set the rest of the data to zero */ - WebRtcSpl_MemSetW16(&sampleVal[len], 0, len); - - /* circular convolution with all-pass filter */ - - /* Set the state to zero */ - WebRtcSpl_MemSetW16(sampleValVec, 0, (LPC_FILTERORDER)); - - /* Run MA filter + AR filter */ - WebRtcSpl_FilterMAFastQ12( - sampleVal, sampleMa, - numerator, LPC_FILTERORDER+1, (WebRtc_Word16)(len + LPC_FILTERORDER)); - WebRtcSpl_MemSetW16(&sampleMa[len + LPC_FILTERORDER], 0, (len - LPC_FILTERORDER)); - WebRtcSpl_FilterARFastQ12( - sampleMa, sampleAr, - syntDenum, LPC_FILTERORDER+1, (WebRtc_Word16)(2*len)); - - tmp1 = &sampleAr[len-1]; - tmp2 = &sampleAr[2*len-1]; - tmp3 = Out_fix; - for(k=0;kstate_short_len); - scaleRes = WebRtcSpl_GetSizeInBits(max)-12; - scaleRes = WEBRTC_SPL_MAX(0, scaleRes); - /* Set up the filter coefficients for the circular convolution */ - for (i=0; i>scaleRes); - } - - /* Copy the residual to a temporary buffer that we can filter - * and set the remaining samples to zero. - */ - WEBRTC_SPL_MEMCPY_W16(residualLong, residual, iLBCenc_inst->state_short_len); - WebRtcSpl_MemSetW16(residualLong + iLBCenc_inst->state_short_len, 0, iLBCenc_inst->state_short_len); - - /* Run the Zero-Pole filter (Ciurcular convolution) */ - WebRtcSpl_MemSetW16(residualLongVec, 0, LPC_FILTERORDER); - WebRtcSpl_FilterMAFastQ12( - residualLong, sampleMa, - numerator, LPC_FILTERORDER+1, (WebRtc_Word16)(iLBCenc_inst->state_short_len + LPC_FILTERORDER)); - WebRtcSpl_MemSetW16(&sampleMa[iLBCenc_inst->state_short_len + LPC_FILTERORDER], 0, iLBCenc_inst->state_short_len - LPC_FILTERORDER); - - WebRtcSpl_FilterARFastQ12( - sampleMa, sampleAr, - syntDenum, LPC_FILTERORDER+1, (WebRtc_Word16)(2*iLBCenc_inst->state_short_len)); - - for(k=0;kstate_short_len;k++){ - sampleAr[k] += sampleAr[k+iLBCenc_inst->state_short_len]; - } - - /* Find maximum absolute value in the vector */ - maxVal=WebRtcSpl_MaxAbsValueW16(sampleAr, iLBCenc_inst->state_short_len); - - /* Find the best index */ - - if ((((WebRtc_Word32)maxVal)<=WebRtcIlbcfix_kChooseFrgQuant[i]) { - index=i+1; - } else { - i=63; - } - } - iLBC_encbits->idxForMax=index; - - /* Rescale the vector before quantization */ - scale=WebRtcIlbcfix_kScale[index]; - - if (index<27) { /* scale table is in Q16, fout[] is in Q(-1) and we want the result to be in Q11 */ - shift=4; - } else { /* scale table is in Q21, fout[] is in Q(-1) and we want the result to be in Q11 */ - shift=9; - } - - /* Set up vectors for AbsQuant and rescale it with the scale factor */ - WebRtcSpl_ScaleVectorWithSat(sampleAr, sampleAr, scale, - iLBCenc_inst->state_short_len, (WebRtc_Word16)(shift-scaleRes)); - - /* Quantize the values in fout[] */ - WebRtcIlbcfix_AbsQuant(iLBCenc_inst, iLBC_encbits, sampleAr, weightDenum); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/state_search.h b/modules/audio_coding/codecs/iLBC/main/source/state_search.h deleted file mode 100644 index 8b7f29877..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/state_search.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_StateSearch.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_STATE_SEARCH_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_STATE_SEARCH_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * encoding of start state - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_StateSearch( - iLBC_Enc_Inst_t *iLBCenc_inst, - /* (i) Encoder instance */ - iLBC_bits *iLBC_encbits,/* (i/o) Encoded bits (output idxForMax - and idxVec, input state_first) */ - WebRtc_Word16 *residual, /* (i) target residual vector */ - WebRtc_Word16 *syntDenum, /* (i) lpc synthesis filter */ - WebRtc_Word16 *weightDenum /* (i) weighting filter denuminator */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.c b/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.c deleted file mode 100644 index 61b8b7bd2..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SwapBytes.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Swap bytes (to simplify operations on Little Endian machines) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SwapBytes( - WebRtc_UWord16 *sequence, /* (i/o) the sequence to swap */ - WebRtc_Word16 wordLength /* (i) number or WebRtc_UWord16 to swap */ - ) { - int k; - WebRtc_UWord16 temp=0; - for( k=wordLength; k>0; k-- ) { - temp = (*sequence >> 8)|(*sequence << 8); - *sequence++ = temp; - //*sequence++ = (*sequence >> 8) | (*sequence << 8); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.h b/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.h deleted file mode 100644 index 2f2b3eb1c..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/swap_bytes.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_SwapBytes.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SWAP_BYTES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_SWAP_BYTES_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * Swap bytes (to simplify operations on Little Endian machines) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_SwapBytes( - WebRtc_UWord16 *sequence, /* (i/o) the sequence to swap */ - WebRtc_Word16 wordLength /* (i) number or WebRtc_UWord16 to swap */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.c b/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.c deleted file mode 100644 index 6c883a7ba..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_UnpackBits.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * unpacking of bits from bitstream, i.e., vector of bytes - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_UnpackBits( /* (o) "Empty" frame indicator */ - WebRtc_UWord16 *bitstream, /* (i) The packatized bitstream */ - iLBC_bits *enc_bits, /* (o) Paramerers from bitstream */ - WebRtc_Word16 mode /* (i) Codec mode (20 or 30) */ - ) { - WebRtc_UWord16 *bitstreamPtr; - int i, k; - WebRtc_Word16 *tmpPtr; - - bitstreamPtr=bitstream; - - /* First WebRtc_Word16 */ - enc_bits->lsf[0] = (*bitstreamPtr)>>10; /* Bit 0..5 */ - enc_bits->lsf[1] = ((*bitstreamPtr)>>3)&0x7F; /* Bit 6..12 */ - enc_bits->lsf[2] = ((*bitstreamPtr)&0x7)<<4; /* Bit 13..15 */ - bitstreamPtr++; - /* Second WebRtc_Word16 */ - enc_bits->lsf[2] |= ((*bitstreamPtr)>>12)&0xF; /* Bit 0..3 */ - - if (mode==20) { - enc_bits->startIdx = ((*bitstreamPtr)>>10)&0x3; /* Bit 4..5 */ - enc_bits->state_first = ((*bitstreamPtr)>>9)&0x1; /* Bit 6 */ - enc_bits->idxForMax = ((*bitstreamPtr)>>3)&0x3F; /* Bit 7..12 */ - enc_bits->cb_index[0] = ((*bitstreamPtr)&0x7)<<4; /* Bit 13..15 */ - bitstreamPtr++; - /* Third WebRtc_Word16 */ - enc_bits->cb_index[0] |= ((*bitstreamPtr)>>12)&0xE; /* Bit 0..2 */ - enc_bits->gain_index[0] = ((*bitstreamPtr)>>8)&0x18; /* Bit 3..4 */ - enc_bits->gain_index[1] = ((*bitstreamPtr)>>7)&0x8; /* Bit 5 */ - enc_bits->cb_index[3] = ((*bitstreamPtr)>>2)&0xFE; /* Bit 6..12 */ - enc_bits->gain_index[3] = ((*bitstreamPtr)<<2)&0x10; /* Bit 13 */ - enc_bits->gain_index[4] = ((*bitstreamPtr)<<2)&0x8; /* Bit 14 */ - enc_bits->gain_index[6] = ((*bitstreamPtr)<<4)&0x10; /* Bit 15 */ - } else { /* mode==30 */ - enc_bits->lsf[3] = ((*bitstreamPtr)>>6)&0x3F; /* Bit 4..9 */ - enc_bits->lsf[4] = ((*bitstreamPtr)<<1)&0x7E; /* Bit 10..15 */ - bitstreamPtr++; - /* Third WebRtc_Word16 */ - enc_bits->lsf[4] |= ((*bitstreamPtr)>>15)&0x1; /* Bit 0 */ - enc_bits->lsf[5] = ((*bitstreamPtr)>>8)&0x7F; /* Bit 1..7 */ - enc_bits->startIdx = ((*bitstreamPtr)>>5)&0x7; /* Bit 8..10 */ - enc_bits->state_first = ((*bitstreamPtr)>>4)&0x1; /* Bit 11 */ - enc_bits->idxForMax = ((*bitstreamPtr)<<2)&0x3C; /* Bit 12..15 */ - bitstreamPtr++; - /* 4:th WebRtc_Word16 */ - enc_bits->idxForMax |= ((*bitstreamPtr)>>14)&0x3; /* Bit 0..1 */ - enc_bits->cb_index[0] = ((*bitstreamPtr)>>7)&0x78; /* Bit 2..5 */ - enc_bits->gain_index[0] = ((*bitstreamPtr)>>5)&0x10; /* Bit 6 */ - enc_bits->gain_index[1] = ((*bitstreamPtr)>>5)&0x8; /* Bit 7 */ - enc_bits->cb_index[3] = ((*bitstreamPtr))&0xFC; /* Bit 8..13 */ - enc_bits->gain_index[3] = ((*bitstreamPtr)<<3)&0x10; /* Bit 14 */ - enc_bits->gain_index[4] = ((*bitstreamPtr)<<3)&0x8; /* Bit 15 */ - } - /* Class 2 bits of ULP */ - /* 4:th to 6:th WebRtc_Word16 for 20 ms case - 5:th to 7:th WebRtc_Word16 for 30 ms case */ - bitstreamPtr++; - tmpPtr=enc_bits->idxVec; - for (k=0; k<3; k++) { - for (i=15; i>=0; i--) { - (*tmpPtr) = (((*bitstreamPtr)>>i)<<2)&0x4; - /* Bit 15-i */ - tmpPtr++; - } - bitstreamPtr++; - } - - if (mode==20) { - /* 7:th WebRtc_Word16 */ - for (i=15; i>6; i--) { - (*tmpPtr) = (((*bitstreamPtr)>>i)<<2)&0x4; - /* Bit 15-i */ - tmpPtr++; - } - enc_bits->gain_index[1] |= ((*bitstreamPtr)>>4)&0x4; /* Bit 9 */ - enc_bits->gain_index[3] |= ((*bitstreamPtr)>>2)&0xC; /* Bit 10..11 */ - enc_bits->gain_index[4] |= ((*bitstreamPtr)>>1)&0x4; /* Bit 12 */ - enc_bits->gain_index[6] |= ((*bitstreamPtr)<<1)&0x8; /* Bit 13 */ - enc_bits->gain_index[7] = ((*bitstreamPtr)<<2)&0xC; /* Bit 14..15 */ - - } else { /* mode==30 */ - /* 8:th WebRtc_Word16 */ - for (i=15; i>5; i--) { - (*tmpPtr) = (((*bitstreamPtr)>>i)<<2)&0x4; - /* Bit 15-i */ - tmpPtr++; - } - enc_bits->cb_index[0] |= ((*bitstreamPtr)>>3)&0x6; /* Bit 10..11 */ - enc_bits->gain_index[0] |= ((*bitstreamPtr))&0x8; /* Bit 12 */ - enc_bits->gain_index[1] |= ((*bitstreamPtr))&0x4; /* Bit 13 */ - enc_bits->cb_index[3] |= ((*bitstreamPtr))&0x2; /* Bit 14 */ - enc_bits->cb_index[6] = ((*bitstreamPtr)<<7)&0x80; /* Bit 15 */ - bitstreamPtr++; - /* 9:th WebRtc_Word16 */ - enc_bits->cb_index[6] |= ((*bitstreamPtr)>>9)&0x7E; /* Bit 0..5 */ - enc_bits->cb_index[9] = ((*bitstreamPtr)>>2)&0xFE; /* Bit 6..12 */ - enc_bits->cb_index[12] = ((*bitstreamPtr)<<5)&0xE0; /* Bit 13..15 */ - bitstreamPtr++; - /* 10:th WebRtc_Word16 */ - enc_bits->cb_index[12] |= ((*bitstreamPtr)>>11)&0x1E;/* Bit 0..3 */ - enc_bits->gain_index[3] |= ((*bitstreamPtr)>>8)&0xC; /* Bit 4..5 */ - enc_bits->gain_index[4] |= ((*bitstreamPtr)>>7)&0x6; /* Bit 6..7 */ - enc_bits->gain_index[6] = ((*bitstreamPtr)>>3)&0x18; /* Bit 8..9 */ - enc_bits->gain_index[7] = ((*bitstreamPtr)>>2)&0xC; /* Bit 10..11 */ - enc_bits->gain_index[9] = ((*bitstreamPtr)<<1)&0x10; /* Bit 12 */ - enc_bits->gain_index[10] = ((*bitstreamPtr)<<1)&0x8; /* Bit 13 */ - enc_bits->gain_index[12] = ((*bitstreamPtr)<<3)&0x10; /* Bit 14 */ - enc_bits->gain_index[13] = ((*bitstreamPtr)<<3)&0x8; /* Bit 15 */ - } - bitstreamPtr++; - /* Class 3 bits of ULP */ - /* 8:th to 14:th WebRtc_Word16 for 20 ms case - 11:th to 17:th WebRtc_Word16 for 30 ms case */ - tmpPtr=enc_bits->idxVec; - for (k=0; k<7; k++) { - for (i=14; i>=0; i-=2) { - (*tmpPtr) |= ((*bitstreamPtr)>>i)&0x3; /* Bit 15-i..14-i*/ - tmpPtr++; - } - bitstreamPtr++; - } - - if (mode==20) { - /* 15:th WebRtc_Word16 */ - enc_bits->idxVec[56] |= ((*bitstreamPtr)>>14)&0x3; /* Bit 0..1 */ - enc_bits->cb_index[0] |= ((*bitstreamPtr)>>13)&0x1; /* Bit 2 */ - enc_bits->cb_index[1] = ((*bitstreamPtr)>>6)&0x7F; /* Bit 3..9 */ - enc_bits->cb_index[2] = ((*bitstreamPtr)<<1)&0x7E; /* Bit 10..15 */ - bitstreamPtr++; - /* 16:th WebRtc_Word16 */ - enc_bits->cb_index[2] |= ((*bitstreamPtr)>>15)&0x1; /* Bit 0 */ - enc_bits->gain_index[0] |= ((*bitstreamPtr)>>12)&0x7; /* Bit 1..3 */ - enc_bits->gain_index[1] |= ((*bitstreamPtr)>>10)&0x3; /* Bit 4..5 */ - enc_bits->gain_index[2] = ((*bitstreamPtr)>>7)&0x7; /* Bit 6..8 */ - enc_bits->cb_index[3] |= ((*bitstreamPtr)>>6)&0x1; /* Bit 9 */ - enc_bits->cb_index[4] = ((*bitstreamPtr)<<1)&0x7E; /* Bit 10..15 */ - bitstreamPtr++; - /* 17:th WebRtc_Word16 */ - enc_bits->cb_index[4] |= ((*bitstreamPtr)>>15)&0x1; /* Bit 0 */ - enc_bits->cb_index[5] = ((*bitstreamPtr)>>8)&0x7F; /* Bit 1..7 */ - enc_bits->cb_index[6] = ((*bitstreamPtr))&0xFF; /* Bit 8..15 */ - bitstreamPtr++; - /* 18:th WebRtc_Word16 */ - enc_bits->cb_index[7] = (*bitstreamPtr)>>8; /* Bit 0..7 */ - enc_bits->cb_index[8] = (*bitstreamPtr)&0xFF; /* Bit 8..15 */ - bitstreamPtr++; - /* 19:th WebRtc_Word16 */ - enc_bits->gain_index[3] |= ((*bitstreamPtr)>>14)&0x3; /* Bit 0..1 */ - enc_bits->gain_index[4] |= ((*bitstreamPtr)>>12)&0x3; /* Bit 2..3 */ - enc_bits->gain_index[5] = ((*bitstreamPtr)>>9)&0x7; /* Bit 4..6 */ - enc_bits->gain_index[6] |= ((*bitstreamPtr)>>6)&0x7; /* Bit 7..9 */ - enc_bits->gain_index[7] |= ((*bitstreamPtr)>>4)&0x3; /* Bit 10..11 */ - enc_bits->gain_index[8] = ((*bitstreamPtr)>>1)&0x7; /* Bit 12..14 */ - } else { /* mode==30 */ - /* 18:th WebRtc_Word16 */ - enc_bits->idxVec[56] |= ((*bitstreamPtr)>>14)&0x3; /* Bit 0..1 */ - enc_bits->idxVec[57] |= ((*bitstreamPtr)>>12)&0x3; /* Bit 2..3 */ - enc_bits->cb_index[0] |= ((*bitstreamPtr)>>11)&1; /* Bit 4 */ - enc_bits->cb_index[1] = ((*bitstreamPtr)>>4)&0x7F; /* Bit 5..11 */ - enc_bits->cb_index[2] = ((*bitstreamPtr)<<3)&0x78; /* Bit 12..15 */ - bitstreamPtr++; - /* 19:th WebRtc_Word16 */ - enc_bits->cb_index[2] |= ((*bitstreamPtr)>>13)&0x7; /* Bit 0..2 */ - enc_bits->gain_index[0] |= ((*bitstreamPtr)>>10)&0x7; /* Bit 3..5 */ - enc_bits->gain_index[1] |= ((*bitstreamPtr)>>8)&0x3; /* Bit 6..7 */ - enc_bits->gain_index[2] = ((*bitstreamPtr)>>5)&0x7; /* Bit 8..10 */ - enc_bits->cb_index[3] |= ((*bitstreamPtr)>>4)&0x1; /* Bit 11 */ - enc_bits->cb_index[4] = ((*bitstreamPtr)<<3)&0x78; /* Bit 12..15 */ - bitstreamPtr++; - /* 20:th WebRtc_Word16 */ - enc_bits->cb_index[4] |= ((*bitstreamPtr)>>13)&0x7; /* Bit 0..2 */ - enc_bits->cb_index[5] = ((*bitstreamPtr)>>6)&0x7F; /* Bit 3..9 */ - enc_bits->cb_index[6] |= ((*bitstreamPtr)>>5)&0x1; /* Bit 10 */ - enc_bits->cb_index[7] = ((*bitstreamPtr)<<3)&0xF8; /* Bit 11..15 */ - bitstreamPtr++; - /* 21:st WebRtc_Word16 */ - enc_bits->cb_index[7] |= ((*bitstreamPtr)>>13)&0x7; /* Bit 0..2 */ - enc_bits->cb_index[8] = ((*bitstreamPtr)>>5)&0xFF; /* Bit 3..10 */ - enc_bits->cb_index[9] |= ((*bitstreamPtr)>>4)&0x1; /* Bit 11 */ - enc_bits->cb_index[10] = ((*bitstreamPtr)<<4)&0xF0; /* Bit 12..15 */ - bitstreamPtr++; - /* 22:nd WebRtc_Word16 */ - enc_bits->cb_index[10] |= ((*bitstreamPtr)>>12)&0xF; /* Bit 0..3 */ - enc_bits->cb_index[11] = ((*bitstreamPtr)>>4)&0xFF; /* Bit 4..11 */ - enc_bits->cb_index[12] |= ((*bitstreamPtr)>>3)&0x1; /* Bit 12 */ - enc_bits->cb_index[13] = ((*bitstreamPtr)<<5)&0xE0; /* Bit 13..15 */ - bitstreamPtr++; - /* 23:rd WebRtc_Word16 */ - enc_bits->cb_index[13] |= ((*bitstreamPtr)>>11)&0x1F;/* Bit 0..4 */ - enc_bits->cb_index[14] = ((*bitstreamPtr)>>3)&0xFF; /* Bit 5..12 */ - enc_bits->gain_index[3] |= ((*bitstreamPtr)>>1)&0x3; /* Bit 13..14 */ - enc_bits->gain_index[4] |= ((*bitstreamPtr)&0x1); /* Bit 15 */ - bitstreamPtr++; - /* 24:rd WebRtc_Word16 */ - enc_bits->gain_index[5] = ((*bitstreamPtr)>>13)&0x7; /* Bit 0..2 */ - enc_bits->gain_index[6] |= ((*bitstreamPtr)>>10)&0x7; /* Bit 3..5 */ - enc_bits->gain_index[7] |= ((*bitstreamPtr)>>8)&0x3; /* Bit 6..7 */ - enc_bits->gain_index[8] = ((*bitstreamPtr)>>5)&0x7; /* Bit 8..10 */ - enc_bits->gain_index[9] |= ((*bitstreamPtr)>>1)&0xF; /* Bit 11..14 */ - enc_bits->gain_index[10] |= ((*bitstreamPtr)<<2)&0x4; /* Bit 15 */ - bitstreamPtr++; - /* 25:rd WebRtc_Word16 */ - enc_bits->gain_index[10] |= ((*bitstreamPtr)>>14)&0x3; /* Bit 0..1 */ - enc_bits->gain_index[11] = ((*bitstreamPtr)>>11)&0x7; /* Bit 2..4 */ - enc_bits->gain_index[12] |= ((*bitstreamPtr)>>7)&0xF; /* Bit 5..8 */ - enc_bits->gain_index[13] |= ((*bitstreamPtr)>>4)&0x7; /* Bit 9..11 */ - enc_bits->gain_index[14] = ((*bitstreamPtr)>>1)&0x7; /* Bit 12..14 */ - } - /* Last bit should be zero, otherwise it's an "empty" frame */ - if (((*bitstreamPtr)&0x1) == 1) { - return(1); - } else { - return(0); - } -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.h b/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.h deleted file mode 100644 index 864865f09..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/unpack_bits.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_UnpackBits.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_UNPACK_BITS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_UNPACK_BITS_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * unpacking of bits from bitstream, i.e., vector of bytes - *---------------------------------------------------------------*/ - -WebRtc_Word16 WebRtcIlbcfix_UnpackBits( /* (o) "Empty" frame indicator */ - WebRtc_UWord16 *bitstream, /* (i) The packatized bitstream */ - iLBC_bits *enc_bits, /* (o) Paramerers from bitstream */ - WebRtc_Word16 mode /* (i) Codec mode (20 or 30) */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/vq3.c b/modules/audio_coding/codecs/iLBC/main/source/vq3.c deleted file mode 100644 index 81d1bfa5c..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/vq3.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Vq3.c - -******************************************************************/ - -#include "vq3.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * vector quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Vq3( - WebRtc_Word16 *Xq, /* quantized vector (Q13) */ - WebRtc_Word16 *index, - WebRtc_Word16 *CB, /* codebook in Q13 */ - WebRtc_Word16 *X, /* vector to quantize (Q13) */ - WebRtc_Word16 n_cb - ){ - WebRtc_Word16 i, j; - WebRtc_Word16 pos, minindex=0; - WebRtc_Word16 tmp; - WebRtc_Word32 dist, mindist; - - pos = 0; - mindist = WEBRTC_SPL_WORD32_MAX; /* start value */ - - /* Find the codebook with the lowest square distance */ - for (j = 0; j < n_cb; j++) { - tmp = X[0] - CB[pos]; - dist = WEBRTC_SPL_MUL_16_16(tmp, tmp); - for (i = 1; i < 3; i++) { - tmp = X[i] - CB[pos + i]; - dist += WEBRTC_SPL_MUL_16_16(tmp, tmp); - } - - if (dist < mindist) { - mindist = dist; - minindex = j; - } - pos += 3; - } - - /* Store the quantized codebook and the index */ - for (i = 0; i < 3; i++) { - Xq[i] = CB[minindex*3 + i]; - } - *index = minindex; - -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/vq3.h b/modules/audio_coding/codecs/iLBC/main/source/vq3.h deleted file mode 100644 index f2628e0db..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/vq3.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Vq3.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ3_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ3_H_ - -#include "typedefs.h" - -/*----------------------------------------------------------------* - * Vector quantization of order 3 (based on MSE) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Vq3( - WebRtc_Word16 *Xq, /* (o) the quantized vector (Q13) */ - WebRtc_Word16 *index, /* (o) the quantization index */ - WebRtc_Word16 *CB, /* (i) the vector quantization codebook (Q13) */ - WebRtc_Word16 *X, /* (i) the vector to quantize (Q13) */ - WebRtc_Word16 n_cb /* (i) the number of vectors in the codebook */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/vq4.c b/modules/audio_coding/codecs/iLBC/main/source/vq4.c deleted file mode 100644 index 3d4c26d6e..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/vq4.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Vq4.c - -******************************************************************/ - -#include "vq4.h" -#include "constants.h" - -/*----------------------------------------------------------------* - * vector quantization - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Vq4( - WebRtc_Word16 *Xq, /* quantized vector (Q13) */ - WebRtc_Word16 *index, - WebRtc_Word16 *CB, /* codebook in Q13 */ - WebRtc_Word16 *X, /* vector to quantize (Q13) */ - WebRtc_Word16 n_cb - ){ - WebRtc_Word16 i, j; - WebRtc_Word16 pos, minindex=0; - WebRtc_Word16 tmp; - WebRtc_Word32 dist, mindist; - - pos = 0; - mindist = WEBRTC_SPL_WORD32_MAX; /* start value */ - - /* Find the codebook with the lowest square distance */ - for (j = 0; j < n_cb; j++) { - tmp = X[0] - CB[pos]; - dist = WEBRTC_SPL_MUL_16_16(tmp, tmp); - for (i = 1; i < 4; i++) { - tmp = X[i] - CB[pos + i]; - dist += WEBRTC_SPL_MUL_16_16(tmp, tmp); - } - - if (dist < mindist) { - mindist = dist; - minindex = j; - } - pos += 4; - } - - /* Store the quantized codebook and the index */ - for (i = 0; i < 4; i++) { - Xq[i] = CB[minindex*4 + i]; - } - *index = minindex; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/vq4.h b/modules/audio_coding/codecs/iLBC/main/source/vq4.h deleted file mode 100644 index 1b8cff254..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/vq4.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Vq4.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ4_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_VQ4_H_ - -#include "typedefs.h" - -/*----------------------------------------------------------------* - * Vector quantization of order 4 (based on MSE) - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Vq4( - WebRtc_Word16 *Xq, /* (o) the quantized vector (Q13) */ - WebRtc_Word16 *index, /* (o) the quantization index */ - WebRtc_Word16 *CB, /* (i) the vector quantization codebook (Q13) */ - WebRtc_Word16 *X, /* (i) the vector to quantize (Q13) */ - WebRtc_Word16 n_cb /* (i) the number of vectors in the codebook */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/window32_w32.c b/modules/audio_coding/codecs/iLBC/main/source/window32_w32.c deleted file mode 100644 index b0e840677..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/window32_w32.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Window32W32.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * window multiplication - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Window32W32( - WebRtc_Word32 *z, /* Output */ - WebRtc_Word32 *x, /* Input (same domain as Output)*/ - const WebRtc_Word32 *y, /* Q31 Window */ - WebRtc_Word16 N /* length to process */ - ) { - WebRtc_Word16 i; - WebRtc_Word16 x_low, x_hi, y_low, y_hi; - WebRtc_Word16 left_shifts; - WebRtc_Word32 temp; - - left_shifts = (WebRtc_Word16)WebRtcSpl_NormW32(x[0]); - WebRtcSpl_VectorBitShiftW32(x, N, x, (WebRtc_Word16)(-left_shifts)); - - - /* The double precision numbers use a special representation: - * w32 = hi<<16 + lo<<1 - */ - for (i = 0; i < N; i++) { - /* Extract higher bytes */ - x_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(x[i], 16); - y_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(y[i], 16); - - /* Extract lower bytes, defined as (w32 - hi<<16)>>1 */ - temp = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)x_hi, 16); - x_low = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32((x[i] - temp), 1); - - temp = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)y_hi, 16); - y_low = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32((y[i] - temp), 1); - - /* Calculate z by a 32 bit multiplication using both low and high from x and y */ - temp = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(x_hi, y_hi), 1); - temp = (temp + (WEBRTC_SPL_MUL_16_16_RSFT(x_hi, y_low, 14))); - - z[i] = (temp + (WEBRTC_SPL_MUL_16_16_RSFT(x_low, y_hi, 14))); - } - - WebRtcSpl_VectorBitShiftW32(z, N, z, left_shifts); - - return; -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/window32_w32.h b/modules/audio_coding/codecs/iLBC/main/source/window32_w32.h deleted file mode 100644 index 121188ab8..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/window32_w32.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_Window32W32.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_WINDOW32_W32_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_WINDOW32_W32_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * window multiplication - *---------------------------------------------------------------*/ - -void WebRtcIlbcfix_Window32W32( - WebRtc_Word32 *z, /* Output */ - WebRtc_Word32 *x, /* Input (same domain as Output)*/ - const WebRtc_Word32 *y, /* Q31 Window */ - WebRtc_Word16 N /* length to process */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.c b/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.c deleted file mode 100644 index 04170adbd..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_XcorrCoef.c - -******************************************************************/ - -#include "defines.h" - -/*----------------------------------------------------------------* - * cross correlation which finds the optimal lag for the - * crossCorr*crossCorr/(energy) criteria - *---------------------------------------------------------------*/ - -int WebRtcIlbcfix_XcorrCoef( - WebRtc_Word16 *target, /* (i) first array */ - WebRtc_Word16 *regressor, /* (i) second array */ - WebRtc_Word16 subl, /* (i) dimension arrays */ - WebRtc_Word16 searchLen, /* (i) the search lenght */ - WebRtc_Word16 offset, /* (i) samples offset between arrays */ - WebRtc_Word16 step /* (i) +1 or -1 */ - ){ - int k; - WebRtc_Word16 maxlag; - WebRtc_Word16 pos; - WebRtc_Word16 max; - WebRtc_Word16 crossCorrScale, Energyscale; - WebRtc_Word16 crossCorrSqMod, crossCorrSqMod_Max; - WebRtc_Word32 crossCorr, Energy; - WebRtc_Word16 crossCorrmod, EnergyMod, EnergyMod_Max; - WebRtc_Word16 *tp, *rp; - WebRtc_Word16 *rp_beg, *rp_end; - WebRtc_Word16 totscale, totscale_max; - WebRtc_Word16 scalediff; - WebRtc_Word32 newCrit, maxCrit; - int shifts; - - /* Initializations, to make sure that the first one is selected */ - crossCorrSqMod_Max=0; - EnergyMod_Max=WEBRTC_SPL_WORD16_MAX; - totscale_max=-500; - maxlag=0; - pos=0; - - /* Find scale value and start position */ - if (step==1) { - max=WebRtcSpl_MaxAbsValueW16(regressor, (WebRtc_Word16)(subl+searchLen-1)); - rp_beg = regressor; - rp_end = ®ressor[subl]; - } else { /* step==-1 */ - max=WebRtcSpl_MaxAbsValueW16(®ressor[-searchLen], (WebRtc_Word16)(subl+searchLen-1)); - rp_beg = ®ressor[-1]; - rp_end = ®ressor[subl-1]; - } - - /* Introduce a scale factor on the Energy in WebRtc_Word32 in - order to make sure that the calculation does not - overflow */ - - if (max>5000) { - shifts=2; - } else { - shifts=0; - } - - /* Calculate the first energy, then do a +/- to get the other energies */ - Energy=WebRtcSpl_DotProductWithScale(regressor, regressor, subl, shifts); - - for (k=0;k0)&&(crossCorr>0)) { - - /* Put cross correlation and energy on 16 bit word */ - crossCorrScale=(WebRtc_Word16)WebRtcSpl_NormW32(crossCorr)-16; - crossCorrmod=(WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(crossCorr, crossCorrScale); - Energyscale=(WebRtc_Word16)WebRtcSpl_NormW32(Energy)-16; - EnergyMod=(WebRtc_Word16)WEBRTC_SPL_SHIFT_W32(Energy, Energyscale); - - /* Square cross correlation and store upper WebRtc_Word16 */ - crossCorrSqMod=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(crossCorrmod, crossCorrmod, 16); - - /* Calculate the total number of (dynamic) right shifts that have - been performed on (crossCorr*crossCorr)/energy - */ - totscale=Energyscale-(crossCorrScale<<1); - - /* Calculate the shift difference in order to be able to compare the two - (crossCorr*crossCorr)/energy in the same domain - */ - scalediff=totscale-totscale_max; - scalediff=WEBRTC_SPL_MIN(scalediff,31); - scalediff=WEBRTC_SPL_MAX(scalediff,-31); - - /* Compute the cross multiplication between the old best criteria - and the new one to be able to compare them without using a - division */ - - if (scalediff<0) { - newCrit = ((WebRtc_Word32)crossCorrSqMod*EnergyMod_Max)>>(-scalediff); - maxCrit = ((WebRtc_Word32)crossCorrSqMod_Max*EnergyMod); - } else { - newCrit = ((WebRtc_Word32)crossCorrSqMod*EnergyMod_Max); - maxCrit = ((WebRtc_Word32)crossCorrSqMod_Max*EnergyMod)>>scalediff; - } - - /* Store the new lag value if the new criteria is larger - than previous largest criteria */ - - if (newCrit > maxCrit) { - crossCorrSqMod_Max = crossCorrSqMod; - EnergyMod_Max = EnergyMod; - totscale_max = totscale; - maxlag = k; - } - } - pos+=step; - - /* Do a +/- to get the next energy */ - Energy += step*(WEBRTC_SPL_RSHIFT_W32( - ((WebRtc_Word32)(*rp_end)*(*rp_end)) - ((WebRtc_Word32)(*rp_beg)*(*rp_beg)), - shifts)); - rp_beg+=step; - rp_end+=step; - } - - return(maxlag+offset); -} diff --git a/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.h b/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.h deleted file mode 100644 index ac885c435..000000000 --- a/modules/audio_coding/codecs/iLBC/main/source/xcorr_coef.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - WebRtcIlbcfix_XcorrCoef.h - -******************************************************************/ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_XCORR_COEF_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ILBC_MAIN_SOURCE_XCORR_COEF_H_ - -#include "defines.h" - -/*----------------------------------------------------------------* - * cross correlation which finds the optimal lag for the - * crossCorr*crossCorr/(energy) criteria - *---------------------------------------------------------------*/ - -int WebRtcIlbcfix_XcorrCoef( - WebRtc_Word16 *target, /* (i) first array */ - WebRtc_Word16 *regressor, /* (i) second array */ - WebRtc_Word16 subl, /* (i) dimension arrays */ - WebRtc_Word16 searchLen, /* (i) the search lenght */ - WebRtc_Word16 offset, /* (i) samples offset between arrays */ - WebRtc_Word16 step /* (i) +1 or -1 */ - ); - -#endif diff --git a/modules/audio_coding/codecs/iLBC/main/test/iLBC_test.c b/modules/audio_coding/codecs/iLBC/main/test/iLBC_test.c deleted file mode 100644 index 7fa61e005..000000000 --- a/modules/audio_coding/codecs/iLBC/main/test/iLBC_test.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_test.c - -******************************************************************/ - -#include -#include -#include -#include "ilbc.h" - -/*---------------------------------------------------------------* - * Main program to test iLBC encoding and decoding - * - * Usage: - * exefile_name.exe - * - * : Input file, speech for encoder (16-bit pcm file) - * : Bit stream output from the encoder - * : Output file, decoded speech (16-bit pcm file) - * : Bit error file, optional (16-bit) - * 1 - Packet received correctly - * 0 - Packet Lost - * - *--------------------------------------------------------------*/ - -#define BLOCKL_MAX 240 -#define ILBCNOOFWORDS_MAX 25 -#define ILBCSCRATCHMEMSIZE 2156 - -#ifdef __ILBC_WITH_SCRATCHMEM -WebRtc_Word16 iLBC_ScratchMem[ILBCSCRATCHMEMSIZE]; -WebRtc_Word16 size; -#endif - - -int main(int argc, char* argv[]) -{ - - FILE *ifileid,*efileid,*ofileid, *cfileid; - WebRtc_Word16 data[BLOCKL_MAX]; - WebRtc_Word16 encoded_data[ILBCNOOFWORDS_MAX], decoded_data[BLOCKL_MAX]; - int len; - short pli, mode; - int blockcount = 0; - int packetlosscount = 0; - int frameLen; - WebRtc_Word16 speechType; - iLBC_encinst_t *Enc_Inst; - iLBC_decinst_t *Dec_Inst; - -#ifdef __ILBC_WITH_40BITACC - /* Doublecheck that long long exists */ - if (sizeof(long)>=sizeof(long long)) { - fprintf(stderr, "40-bit simulation is not be supported on this platform\n"); - exit(0); - } -#endif - - /* get arguments and open files */ - - if ((argc!=5) && (argc!=6)) { - fprintf(stderr, - "\n*-----------------------------------------------*\n"); - fprintf(stderr, - " %s <20,30> input encoded decoded (channel)\n\n", - argv[0]); - fprintf(stderr, - " mode : Frame size for the encoding/decoding\n"); - fprintf(stderr, - " 20 - 20 ms\n"); - fprintf(stderr, - " 30 - 30 ms\n"); - fprintf(stderr, - " input : Speech for encoder (16-bit pcm file)\n"); - fprintf(stderr, - " encoded : Encoded bit stream\n"); - fprintf(stderr, - " decoded : Decoded speech (16-bit pcm file)\n"); - fprintf(stderr, - " channel : Packet loss pattern, optional (16-bit)\n"); - fprintf(stderr, - " 1 - Packet received correctly\n"); - fprintf(stderr, - " 0 - Packet Lost\n"); - fprintf(stderr, - "*-----------------------------------------------*\n\n"); - exit(1); - } - mode=atoi(argv[1]); - if (mode != 20 && mode != 30) { - fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", - argv[1]); - exit(2); - } - if ( (ifileid=fopen(argv[2],"rb")) == NULL) { - fprintf(stderr,"Cannot open input file %s\n", argv[2]); - exit(2);} - if ( (efileid=fopen(argv[3],"wb")) == NULL) { - fprintf(stderr, "Cannot open encoded file file %s\n", - argv[3]); exit(1);} - if ( (ofileid=fopen(argv[4],"wb")) == NULL) { - fprintf(stderr, "Cannot open decoded file %s\n", - argv[4]); exit(1);} - if (argc==6) { - if( (cfileid=fopen(argv[5],"rb")) == NULL) { - fprintf(stderr, "Cannot open channel file %s\n", - argv[5]); - exit(1); - } - } else { - cfileid=NULL; - } - - /* print info */ - - fprintf(stderr, "\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* iLBC test program *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr,"\nMode : %2d ms\n", mode); - fprintf(stderr,"Input file : %s\n", argv[2]); - fprintf(stderr,"Encoded file : %s\n", argv[3]); - fprintf(stderr,"Output file : %s\n", argv[4]); - if (argc==6) { - fprintf(stderr,"Channel file : %s\n", argv[5]); - } - fprintf(stderr,"\n"); - - /* Create structs */ - WebRtcIlbcfix_EncoderCreate(&Enc_Inst); - WebRtcIlbcfix_DecoderCreate(&Dec_Inst); - -#ifdef __ILBC_WITH_SCRATCHMEM - /* Assign scratch memory. Note that Enc and Dec can use the same area */ - WebRtcIlbcfix_EncoderAssignScratchMem(Enc_Inst, iLBC_ScratchMem, &size); - if (size>ILBCSCRATCHMEMSIZE) { fprintf(stderr,"Error: Scratch not big enough"); exit(0); } - WebRtcIlbcfix_DecoderAssignScratchMem(Dec_Inst, iLBC_ScratchMem, &size); - if (size>ILBCSCRATCHMEMSIZE) { fprintf(stderr,"Error: Scratch not big enough"); exit(0); } -#endif - - /* Initialization */ - - WebRtcIlbcfix_EncoderInit(Enc_Inst, mode); - WebRtcIlbcfix_DecoderInit(Dec_Inst, mode); - frameLen = mode*8; - - /* loop over input blocks */ - - while (((WebRtc_Word16)fread(data,sizeof(WebRtc_Word16),frameLen,ifileid))== - frameLen) { - - blockcount++; - - /* encoding */ - - fprintf(stderr, "--- Encoding block %i --- ",blockcount); - len=WebRtcIlbcfix_Encode(Enc_Inst, data, (WebRtc_Word16)frameLen, encoded_data); - fprintf(stderr, "\r"); - - /* write byte file */ - - fwrite(encoded_data, sizeof(WebRtc_Word16), ((len+1)/sizeof(WebRtc_Word16)), efileid); - - /* get channel data if provided */ - if (argc==6) { - if (fread(&pli, sizeof(WebRtc_Word16), 1, cfileid)) { - if ((pli!=0)&&(pli!=1)) { - fprintf(stderr, "Error in channel file\n"); - exit(0); - } - if (pli==0) { - /* Packet loss -> remove info from frame */ - memset(encoded_data, 0, - sizeof(WebRtc_Word16)*ILBCNOOFWORDS_MAX); - packetlosscount++; - } - } else { - fprintf(stderr, "Error. Channel file too short\n"); - exit(0); - } - } else { - pli=1; - } - - /* decoding */ - - fprintf(stderr, "--- Decoding block %i --- ",blockcount); - if (pli==1) { - len=WebRtcIlbcfix_Decode(Dec_Inst, encoded_data, - (WebRtc_Word16)len, decoded_data,&speechType); - } else { - len=WebRtcIlbcfix_DecodePlc(Dec_Inst, decoded_data, 1); - } - fprintf(stderr, "\r"); - - /* write output file */ - - fwrite(decoded_data,sizeof(WebRtc_Word16),len,ofileid); - } - - /* close files */ - - fclose(ifileid); fclose(efileid); fclose(ofileid); - if (argc==6) { - fclose(cfileid); - } - - /* Free structs */ - WebRtcIlbcfix_EncoderFree(Enc_Inst); - WebRtcIlbcfix_DecoderFree(Dec_Inst); - - - printf("\nDone with simulation\n\n"); - - return(0); -} - diff --git a/modules/audio_coding/codecs/iLBC/main/test/iLBC_testLib.c b/modules/audio_coding/codecs/iLBC/main/test/iLBC_testLib.c deleted file mode 100644 index 5bbf26302..000000000 --- a/modules/audio_coding/codecs/iLBC/main/test/iLBC_testLib.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - -iLBC Speech Coder ANSI-C Source Code - -iLBC_test.c - -******************************************************************/ - -#include -#include -#include -#include -#include -#include "ilbc.h" - -//#define JUNK_DATA -#ifdef JUNK_DATA - #define SEED_FILE "randseed.txt" -#endif - - -/*----------------------------------------------------------------* -* Main program to test iLBC encoding and decoding -* -* Usage: -* exefile_name.exe -* -*---------------------------------------------------------------*/ - -int main(int argc, char* argv[]) -{ - FILE *ifileid,*efileid,*ofileid, *chfileid; - short encoded_data[55], data[240], speechType; - short len, mode, pli; - int blockcount = 0; - - iLBC_encinst_t *Enc_Inst; - iLBC_decinst_t *Dec_Inst; - -#ifdef JUNK_DATA - int i; - FILE *seedfile; - unsigned int random_seed = (unsigned int) time(NULL);//1196764538 -#endif - - /* Create structs */ - WebRtcIlbcfix_EncoderCreate(&Enc_Inst); - WebRtcIlbcfix_DecoderCreate(&Dec_Inst); - - /* get arguments and open files */ - - if (argc != 6 ) { - fprintf(stderr, "%s mode inputfile bytefile outputfile channelfile\n", - argv[0]); - fprintf(stderr, "Example:\n"); - fprintf(stderr, "%s <30,20> in.pcm byte.dat out.pcm T30.0.dat\n", argv[0]); - exit(1); - } - mode=atoi(argv[1]); - if (mode != 20 && mode != 30) { - fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", argv[1]); - exit(2); - } - if ( (ifileid=fopen(argv[2],"rb")) == NULL) { - fprintf(stderr,"Cannot open input file %s\n", argv[2]); - exit(2);} - if ( (efileid=fopen(argv[3],"wb")) == NULL) { - fprintf(stderr, "Cannot open channelfile file %s\n", - argv[3]); exit(3);} - if( (ofileid=fopen(argv[4],"wb")) == NULL) { - fprintf(stderr, "Cannot open output file %s\n", - argv[4]); exit(3);} - if ( (chfileid=fopen(argv[5],"rb")) == NULL) { - fprintf(stderr,"Cannot open channel file file %s\n", argv[5]); - exit(2);} - - /* print info */ - - fprintf(stderr, "\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* iLBCtest *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); -#ifdef SPLIT_10MS - fprintf(stderr,"\n10ms split with raw mode: %2d ms\n", mode); -#else - fprintf(stderr,"\nMode : %2d ms\n", mode); -#endif - fprintf(stderr,"\nInput file : %s\n", argv[2]); - fprintf(stderr,"Coded file : %s\n", argv[3]); - fprintf(stderr,"Output file : %s\n\n", argv[4]); - fprintf(stderr,"Channel file : %s\n\n", argv[5]); - - -#ifdef JUNK_DATA - srand(random_seed); - - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "%u\n", random_seed); - fclose(seedfile); - } -#endif - - /* Initialization */ - - WebRtcIlbcfix_EncoderInit(Enc_Inst, mode); - WebRtcIlbcfix_DecoderInit(Dec_Inst, mode); - - /* loop over input blocks */ -#ifdef SPLIT_10MS - while(fread(data, sizeof(short), 80, ifileid) == 80){ -#else - while( (short)fread(data,sizeof(short),(mode<<3),ifileid)==(mode<<3)){ -#endif - blockcount++; - - /* encoding */ - - fprintf(stderr, "--- Encoding block %i --- ",blockcount); -#ifdef SPLIT_10MS - len=WebRtcIlbcfix_Encode(Enc_Inst, data, 80, encoded_data); -#else - len=WebRtcIlbcfix_Encode(Enc_Inst, data, (short)(mode<<3), encoded_data); -#endif - fprintf(stderr, "\r"); - -#ifdef JUNK_DATA - for ( i = 0; i < len; i++) { - encoded_data[i] = (short) (encoded_data[i] + (short) rand()); - } -#endif - /* write byte file */ - if(len != 0){ //len may be 0 in 10ms split case - fwrite(encoded_data,1,len,efileid); - } - - if(len != 0){ //len may be 0 in 10ms split case - /* get channel data if provided */ - if (argc==6) { - if (fread(&pli, sizeof(WebRtc_Word16), 1, chfileid)) { - if ((pli!=0)&&(pli!=1)) { - fprintf(stderr, "Error in channel file\n"); - exit(0); - } - if (pli==0) { - /* Packet loss -> remove info from frame */ - memset(encoded_data, 0, sizeof(WebRtc_Word16)*25); - } - } else { - fprintf(stderr, "Error. Channel file too short\n"); - exit(0); - } - } else { - pli=1; - } - - /* decoding */ - - fprintf(stderr, "--- Decoding block %i --- ",blockcount); - if (pli==1) { - len=WebRtcIlbcfix_Decode(Dec_Inst, encoded_data, len, data, &speechType); - } else { - len=WebRtcIlbcfix_DecodePlc(Dec_Inst, data, 1); - } - fprintf(stderr, "\r"); - - /* write output file */ - - fwrite(data,sizeof(short),len,ofileid); - } - } - -#ifdef JUNK_DATA - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "ok\n\n"); - fclose(seedfile); - } -#endif - - /* free structs */ - WebRtcIlbcfix_EncoderFree(Enc_Inst); - WebRtcIlbcfix_DecoderFree(Dec_Inst); - - /* close files */ - - fclose(ifileid); fclose(efileid); fclose(ofileid); -} diff --git a/modules/audio_coding/codecs/iLBC/main/test/iLBC_testprogram.c b/modules/audio_coding/codecs/iLBC/main/test/iLBC_testprogram.c deleted file mode 100644 index 2a00829ea..000000000 --- a/modules/audio_coding/codecs/iLBC/main/test/iLBC_testprogram.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - - iLBC Speech Coder ANSI-C Source Code - - iLBC_test.c - -******************************************************************/ - -#include -#include -#include -#include - -#include "defines.h" -#include "nit_encode.h" -#include "encode.h" -#include "init_decode.h" -#include "decode.h" -#include "constants.h" -#include "ilbc.h" - -#define ILBCNOOFWORDS_MAX (NO_OF_BYTES_30MS)/2 - -/* Runtime statistics */ -#include -/* #define CLOCKS_PER_SEC 1000 */ - -/*----------------------------------------------------------------* - * Encoder interface function - *---------------------------------------------------------------*/ - -short encode( /* (o) Number of bytes encoded */ - iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */ - WebRtc_Word16 *encoded_data, /* (o) The encoded bytes */ - WebRtc_Word16 *data /* (i) The signal block to encode */ -){ - - /* do the actual encoding */ - WebRtcIlbcfix_Encode((WebRtc_UWord16 *)encoded_data, data, iLBCenc_inst); - - return (iLBCenc_inst->no_of_bytes); -} - -/*----------------------------------------------------------------* - * Decoder interface function - *---------------------------------------------------------------*/ - -short decode( /* (o) Number of decoded samples */ - iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */ - short *decoded_data, /* (o) Decoded signal block */ - short *encoded_data, /* (i) Encoded bytes */ - short mode /* (i) 0=PL, 1=Normal */ -){ - - /* check if mode is valid */ - - if (mode<0 || mode>1) { - printf("\nERROR - Wrong mode - 0, 1 allowed\n"); exit(3);} - - /* do actual decoding of block */ - - WebRtcIlbcfix_Decode(decoded_data, (WebRtc_UWord16 *)encoded_data, - iLBCdec_inst, mode); - - return (iLBCdec_inst->blockl); -} - -/*----------------------------------------------------------------* - * Main program to test iLBC encoding and decoding - * - * Usage: - * exefile_name.exe - * - *---------------------------------------------------------------*/ - -#define MAXFRAMES 10000 -#define MAXFILELEN (BLOCKL_MAX*MAXFRAMES) - -int main(int argc, char* argv[]) -{ - - /* Runtime statistics */ - - float starttime1, starttime2; - float runtime1, runtime2; - float outtime; - - FILE *ifileid,*efileid,*ofileid, *chfileid; - short *inputdata, *encodeddata, *decodeddata; - short *channeldata; - int blockcount = 0, noOfBlocks=0, i, noOfLostBlocks=0; - short mode; - iLBC_Enc_Inst_t Enc_Inst; - iLBC_Dec_Inst_t Dec_Inst; - - short frameLen; - short count; -#ifdef SPLIT_10MS - short size; -#endif - - inputdata=(short*) malloc(MAXFILELEN*sizeof(short)); - if (inputdata==NULL) { - fprintf(stderr,"Could not allocate memory for vector\n"); - exit(0); - } - encodeddata=(short*) malloc(ILBCNOOFWORDS_MAX*MAXFRAMES*sizeof(short)); - if (encodeddata==NULL) { - fprintf(stderr,"Could not allocate memory for vector\n"); - free(inputdata); - exit(0); - } - decodeddata=(short*) malloc(MAXFILELEN*sizeof(short)); - if (decodeddata==NULL) { - fprintf(stderr,"Could not allocate memory for vector\n"); - free(inputdata); - free(encodeddata); - exit(0); - } - channeldata=(short*) malloc(MAXFRAMES*sizeof(short)); - if (channeldata==NULL) { - fprintf(stderr,"Could not allocate memory for vector\n"); - free(inputdata); - free(encodeddata); - free(decodeddata); - exit(0); - } - - /* get arguments and open files */ - - if (argc != 6 ) { - fprintf(stderr, "%s mode inputfile bytefile outputfile channelfile\n", - argv[0]); - fprintf(stderr, "Example:\n"); - fprintf(stderr, "%s <30,20> in.pcm byte.dat out.pcm T30.0.dat\n", argv[0]); - exit(1); - } - mode=atoi(argv[1]); - if (mode != 20 && mode != 30) { - fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", argv[1]); - exit(2); - } - if ( (ifileid=fopen(argv[2],"rb")) == NULL) { - fprintf(stderr,"Cannot open input file %s\n", argv[2]); - exit(2);} - if ( (efileid=fopen(argv[3],"wb")) == NULL) { - fprintf(stderr, "Cannot open channelfile file %s\n", - argv[3]); exit(3);} - if( (ofileid=fopen(argv[4],"wb")) == NULL) { - fprintf(stderr, "Cannot open output file %s\n", - argv[4]); exit(3);} - if ( (chfileid=fopen(argv[5],"rb")) == NULL) { - fprintf(stderr,"Cannot open channel file file %s\n", argv[5]); - exit(2);} - - - /* print info */ -#ifndef PRINT_MIPS - fprintf(stderr, "\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* iLBCtest *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "* *\n"); - fprintf(stderr, - "*---------------------------------------------------*\n"); -#ifdef SPLIT_10MS - fprintf(stderr,"\n10ms split with raw mode: %2d ms\n", mode); -#else - fprintf(stderr,"\nMode : %2d ms\n", mode); -#endif - fprintf(stderr,"\nInput file : %s\n", argv[2]); - fprintf(stderr,"Coded file : %s\n", argv[3]); - fprintf(stderr,"Output file : %s\n\n", argv[4]); - fprintf(stderr,"Channel file : %s\n\n", argv[5]); -#endif - - /* Initialization */ - - WebRtcIlbcfix_EncoderInit(&Enc_Inst, mode); - WebRtcIlbcfix_DecoderInit(&Dec_Inst, mode, 1); - -#if __ILBC_WITH_SCRATCHMEM - // we can use the same scratch space for both encoder and decoder. - WebRtcIlbcfix_EncoderAssignScratchMem((iLBC_encinst_t*)&Enc_Inst, scratchBlock, &size); - WebRtcIlbcfix_DecoderAssignScratchMem((iLBC_decinst_t*)&Dec_Inst, scratchBlock, &size); -#endif - - /* extract the input file and channel file */ - -#ifdef SPLIT_10MS - frameLen = (mode==20)? 80:160; - fread(Enc_Inst.past_samples, sizeof(short), frameLen, ifileid); - Enc_Inst.section = 0; - - while( fread(&inputdata[noOfBlocks*80], sizeof(short), - 80, ifileid) == 80 ) { - noOfBlocks++; - } - - noOfBlocks += frameLen/80; - frameLen = 80; -#else - frameLen = Enc_Inst.blockl; - - while( fread(&inputdata[noOfBlocks*Enc_Inst.blockl],sizeof(short), - Enc_Inst.blockl,ifileid)==(WebRtc_UWord16)Enc_Inst.blockl){ - noOfBlocks++; - } -#endif - - - while ( (fread(&channeldata[blockcount],sizeof(short), 1,chfileid)==1) - && ( blockcount < noOfBlocks/(Enc_Inst.blockl/frameLen) ) ) { - blockcount++; - } - - if ( blockcount < noOfBlocks/(Enc_Inst.blockl/frameLen) ) { - fprintf(stderr,"Channel file %s is too short\n", argv[4]); - free(inputdata); - free(encodeddata); - free(decodeddata); - free(channeldata); - exit(0); - } - - count=0; - - /* Runtime statistics */ - - starttime1 = clock()/(float)CLOCKS_PER_SEC; - - /* Encoding loop */ - #ifdef PRINT_MIPS - printf("-1 -1\n"); - #endif - -#ifdef SPLIT_10MS - /* "Enc_Inst.section != 0" is to make sure we run through full - lengths of all vectors for 10ms split mode. - */ -// while( (count < noOfBlocks) || (Enc_Inst.section != 0) ) { - while( count < blockcount * (Enc_Inst.blockl/frameLen) ) { - - encode(&Enc_Inst, &encodeddata[Enc_Inst.no_of_words * - (count/(Enc_Inst.nsub/2))], &inputdata[frameLen * count] ); -#else - while (count < noOfBlocks) { - encode( &Enc_Inst, &encodeddata[Enc_Inst.no_of_words * count], - &inputdata[frameLen * count] ); -#endif - - #ifdef PRINT_MIPS - printf("-1 -1\n"); - #endif - - count++; - } - - count=0; - - /* Runtime statistics */ - - starttime2=clock()/(float)CLOCKS_PER_SEC; - runtime1 = (float)(starttime2-starttime1); - - /* Decoding loop */ - - while (count < blockcount) { - if (channeldata[count]==1) { - /* Normal decoding */ - decode(&Dec_Inst, &decodeddata[count * Dec_Inst.blockl], - &encodeddata[Dec_Inst.no_of_words * count], 1); - } else if (channeldata[count]==0) { - /* PLC */ - short emptydata[ILBCNOOFWORDS_MAX]; - memset(emptydata, 0, Dec_Inst.no_of_words*sizeof(short)); - decode(&Dec_Inst, &decodeddata[count*Dec_Inst.blockl], - emptydata, 0); - noOfLostBlocks++; - } else { - printf("Error in channel file (values have to be either 1 or 0)\n"); - exit(0); - } - #ifdef PRINT_MIPS - printf("-1 -1\n"); - #endif - - count++; - } - - /* Runtime statistics */ - - runtime2 = (float)(clock()/(float)CLOCKS_PER_SEC-starttime2); - - outtime = (float)((float)blockcount* - (float)mode/1000.0); - -#ifndef PRINT_MIPS - printf("\nLength of speech file: %.1f s\n", outtime); - printf("Lost frames : %.1f%%\n\n", 100*(float)noOfLostBlocks/(float)blockcount); - - printf("Time to run iLBC_encode+iLBC_decode:"); - printf(" %.1f s (%.1f%% of realtime)\n", runtime1+runtime2, - (100*(runtime1+runtime2)/outtime)); - - printf("Time in iLBC_encode :"); - printf(" %.1f s (%.1f%% of total runtime)\n", - runtime1, 100.0*runtime1/(runtime1+runtime2)); - - printf("Time in iLBC_decode :"); - printf(" %.1f s (%.1f%% of total runtime)\n\n", - runtime2, 100.0*runtime2/(runtime1+runtime2)); -#endif - - /* Write data to files */ - for (i=0; i/dev/null && set -o igncr; # force bash to ignore \r character - -# -# This script can be used to verify the bit exatness of iLBC fixpoint version 1.0.6 -# - - -./iLBCtest 20 ./inFiles/F00.INP ./GeneratedFiles/F00.BIT20 ./GeneratedFiles/F00.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F01.INP ./GeneratedFiles/F01.BIT20 ./GeneratedFiles/F01.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F02.INP ./GeneratedFiles/F02.BIT20 ./GeneratedFiles/F02.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F03.INP ./GeneratedFiles/F03.BIT20 ./GeneratedFiles/F03.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F04.INP ./GeneratedFiles/F04.BIT20 ./GeneratedFiles/F04.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F05.INP ./GeneratedFiles/F05.BIT20 ./GeneratedFiles/F05.OUT20 ./inFiles/clean.chn -./iLBCtest 20 ./inFiles/F06.INP ./GeneratedFiles/F06.BIT20 ./GeneratedFiles/F06.OUT20 ./inFiles/clean.chn - -./iLBCtest 30 ./inFiles/F00.INP ./GeneratedFiles/F00.BIT30 ./GeneratedFiles/F00.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F01.INP ./GeneratedFiles/F01.BIT30 ./GeneratedFiles/F01.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F02.INP ./GeneratedFiles/F02.BIT30 ./GeneratedFiles/F02.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F03.INP ./GeneratedFiles/F03.BIT30 ./GeneratedFiles/F03.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F04.INP ./GeneratedFiles/F04.BIT30 ./GeneratedFiles/F04.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F05.INP ./GeneratedFiles/F05.BIT30 ./GeneratedFiles/F05.OUT30 ./inFiles/clean.chn -./iLBCtest 30 ./inFiles/F06.INP ./GeneratedFiles/F06.BIT30 ./GeneratedFiles/F06.OUT30 ./inFiles/clean.chn - -./iLBCtest 20 ./inFiles/F00.INP ./GeneratedFiles/F00.BIT20 ./GeneratedFiles/F00_tlm10.OUT20 ./inFiles/tlm10.chn -./iLBCtest 20 ./inFiles/F01.INP ./GeneratedFiles/F01.BIT20 ./GeneratedFiles/F01_tlm10.OUT20 ./inFiles/tlm10.chn -./iLBCtest 20 ./inFiles/F02.INP ./GeneratedFiles/F02.BIT20 ./GeneratedFiles/F02_tlm10.OUT20 ./inFiles/tlm10.chn -./iLBCtest 30 ./inFiles/F00.INP ./GeneratedFiles/F00.BIT30 ./GeneratedFiles/F00_tlm10.OUT30 ./inFiles/tlm10.chn -./iLBCtest 30 ./inFiles/F01.INP ./GeneratedFiles/F01.BIT30 ./GeneratedFiles/F01_tlm10.OUT30 ./inFiles/tlm10.chn -./iLBCtest 30 ./inFiles/F02.INP ./GeneratedFiles/F02.BIT30 ./GeneratedFiles/F02_tlm10.OUT30 ./inFiles/tlm10.chn - - -diff ./GeneratedFiles/F00.BIT20 ./ReferenceVectors/F00.BIT20 -diff ./GeneratedFiles/F01.BIT20 ./ReferenceVectors/F01.BIT20 -diff ./GeneratedFiles/F02.BIT20 ./ReferenceVectors/F02.BIT20 -diff ./GeneratedFiles/F03.BIT20 ./ReferenceVectors/F03.BIT20 -diff ./GeneratedFiles/F04.BIT20 ./ReferenceVectors/F04.BIT20 -diff ./GeneratedFiles/F05.BIT20 ./ReferenceVectors/F05.BIT20 -diff ./GeneratedFiles/F06.BIT20 ./ReferenceVectors/F06.BIT20 -diff ./GeneratedFiles/F00.OUT20 ./ReferenceVectors/F00.OUT20 -diff ./GeneratedFiles/F01.OUT20 ./ReferenceVectors/F01.OUT20 -diff ./GeneratedFiles/F02.OUT20 ./ReferenceVectors/F02.OUT20 -diff ./GeneratedFiles/F03.OUT20 ./ReferenceVectors/F03.OUT20 -diff ./GeneratedFiles/F04.OUT20 ./ReferenceVectors/F04.OUT20 -diff ./GeneratedFiles/F05.OUT20 ./ReferenceVectors/F05.OUT20 -diff ./GeneratedFiles/F06.OUT20 ./ReferenceVectors/F06.OUT20 - -diff ./GeneratedFiles/F00.BIT30 ./ReferenceVectors/F00.BIT30 -diff ./GeneratedFiles/F01.BIT30 ./ReferenceVectors/F01.BIT30 -diff ./GeneratedFiles/F02.BIT30 ./ReferenceVectors/F02.BIT30 -diff ./GeneratedFiles/F03.BIT30 ./ReferenceVectors/F03.BIT30 -diff ./GeneratedFiles/F04.BIT30 ./ReferenceVectors/F04.BIT30 -diff ./GeneratedFiles/F05.BIT30 ./ReferenceVectors/F05.BIT30 -diff ./GeneratedFiles/F06.BIT30 ./ReferenceVectors/F06.BIT30 -diff ./GeneratedFiles/F00.OUT30 ./ReferenceVectors/F00.OUT30 -diff ./GeneratedFiles/F01.OUT30 ./ReferenceVectors/F01.OUT30 -diff ./GeneratedFiles/F02.OUT30 ./ReferenceVectors/F02.OUT30 -diff ./GeneratedFiles/F03.OUT30 ./ReferenceVectors/F03.OUT30 -diff ./GeneratedFiles/F04.OUT30 ./ReferenceVectors/F04.OUT30 -diff ./GeneratedFiles/F05.OUT30 ./ReferenceVectors/F05.OUT30 -diff ./GeneratedFiles/F06.OUT30 ./ReferenceVectors/F06.OUT30 - -diff ./GeneratedFiles/F00_tlm10.OUT20 ./ReferenceVectors/F00_tlm10.OUT20 -diff ./GeneratedFiles/F01_tlm10.OUT20 ./ReferenceVectors/F01_tlm10.OUT20 -diff ./GeneratedFiles/F02_tlm10.OUT20 ./ReferenceVectors/F02_tlm10.OUT20 -diff ./GeneratedFiles/F00_tlm10.OUT30 ./ReferenceVectors/F00_tlm10.OUT30 -diff ./GeneratedFiles/F01_tlm10.OUT30 ./ReferenceVectors/F01_tlm10.OUT30 -diff ./GeneratedFiles/F02_tlm10.OUT30 ./ReferenceVectors/F02_tlm10.OUT30 - diff --git a/modules/audio_coding/codecs/iSAC/fix/interface/isacfix.h b/modules/audio_coding/codecs/iSAC/fix/interface/isacfix.h deleted file mode 100644 index 4df005547..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/interface/isacfix.h +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_INTERFACE_ISACFIX_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_INTERFACE_ISACFIX_H_ - -/* - * Define the fixpoint numeric formats - */ -#include "typedefs.h" - - -typedef struct { - void *dummy; -} ISACFIX_MainStruct; - - -#if defined(__cplusplus) -extern "C" { -#endif - - - /************************************************************************** - * WebRtcIsacfix_AssignSize(...) - * - * Functions used when malloc is not allowed - * Output the number of bytes needed to allocate for iSAC struct. - * - */ - - WebRtc_Word16 WebRtcIsacfix_AssignSize(int *sizeinbytes); - - /************************************************************************** - * WebRtcIsacfix_Assign(...) - * - * Functions used when malloc is not allowed, it - * places a struct at the given address. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - ISACFIX_inst_Addr : address of the memory where a space is - * for iSAC structure. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Assign(ISACFIX_MainStruct **inst, - void *ISACFIX_inst_Addr); - - /**************************************************************************** - * WebRtcIsacfix_Create(...) - * - * This function creates an ISAC instance, which will contain the state - * information for one coding/decoding channel. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Create(ISACFIX_MainStruct **ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_Free(...) - * - * This function frees the ISAC instance created at the beginning. - * - * Input: - * - ISAC_main_inst : a ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Free(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_EncoderInit(...) - * - * This function initializes an ISAC instance prior to the encoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - CodingMode : 0 - Bit rate and frame length are automatically - * adjusted to available bandwidth on - * transmission channel. - * 1 - User sets a frame length and a target bit - * rate which is taken as the maximum short-term - * average bit rate. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_EncoderInit(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 CodingMode); - - - /**************************************************************************** - * WebRtcIsacfix_Encode(...) - * - * This function encodes 10ms frame(s) and inserts it into a package. - * Input speech length has to be 160 samples (10ms). The encoder buffers those - * 10ms frames until it reaches the chosen Framesize (480 or 960 samples - * corresponding to 30 or 60 ms frames), and then proceeds to the encoding. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * 0 - The buffer didn't reach the chosen framesize - * so it keeps buffering speech samples. - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_Word16 *speechIn, - WebRtc_Word16 *encoded); - - - - /**************************************************************************** - * WebRtcIsacfix_EncodeNb(...) - * - * This function encodes 10ms narrow band (8 kHz sampling) frame(s) and inserts - * it into a package. Input speech length has to be 80 samples (10ms). The encoder - * interpolates into wide-band (16 kHz sampling) buffers those - * 10ms frames until it reaches the chosen Framesize (480 or 960 wide-band samples - * corresponding to 30 or 60 ms frames), and then proceeds to the encoding. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * 0 - The buffer didn't reach the chosen framesize - * so it keeps buffering speech samples. - * -1 - Error - */ - - - WebRtc_Word16 WebRtcIsacfix_EncodeNb(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_Word16 *speechIn, - WebRtc_Word16 *encoded); - - - - /**************************************************************************** - * WebRtcIsacfix_DecoderInit(...) - * - * This function initializes an ISAC instance prior to the decoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - * Return value - * : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_DecoderInit(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_UpdateBwEstimate1(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 arr_ts); - - /**************************************************************************** - * WebRtcIsacfix_UpdateBwEstimate(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - send_ts : the send time of the packet from RTP header, - * in samples. - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_UpdateBwEstimate(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts); - - /**************************************************************************** - * WebRtcIsacfix_Decode(...) - * - * This function decodes an ISAC frame. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Decode(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - - - /**************************************************************************** - * WebRtcIsacfix_DecodeNb(...) - * - * This function decodes a ISAC frame in narrow-band (8 kHz sampling). - * Output speech length will be a multiple of 240 samples: 240 or 480 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_DecodeNb(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType); - - - /**************************************************************************** - * WebRtcIsacfix_DecodePlcNb(...) - * - * This function conducts PLC for ISAC frame(s) in narrow-band (8kHz sampling). - * Output speech length will be "240*noOfLostFrames" samples - * that equevalent of "30*noOfLostFrames" millisecond. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames (240 sample=30ms) to produce - * NOTE! Maximum number is 2 (480 samples = 60ms) - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded PLC vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_DecodePlcNb(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames ); - - - - - /**************************************************************************** - * WebRtcIsacfix_DecodePlc(...) - * - * This function conducts PLC for ISAC frame(s) in wide-band (16kHz sampling). - * Output speech length will be "480*noOfLostFrames" samples - * that is equevalent of "30*noOfLostFrames" millisecond. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames (480sample = 30ms) - * to produce - * NOTE! Maximum number is 2 (960 samples = 60ms) - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded PLC vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_DecodePlc(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames ); - - - /**************************************************************************** - * WebRtcIsacfix_ReadFrameLen(...) - * - * This function returns the length of the frame represented in the packet. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - */ - - WebRtc_Word16 WebRtcIsacfix_ReadFrameLen(const WebRtc_Word16* encoded, - WebRtc_Word16* frameLength); - - /**************************************************************************** - * WebRtcIsacfix_Control(...) - * - * This function sets the limit on the short-term average bit rate and the - * frame length. Should be used only in Instantaneous mode. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rate : limit on the short-term average bit rate, - * in bits/second (between 10000 and 32000) - * - framesize : number of milliseconds per frame (30 or 60) - * - * Return value : 0 - ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_Control(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 rate, - WebRtc_Word16 framesize); - - - - /**************************************************************************** - * WebRtcIsacfix_ControlBwe(...) - * - * This function sets the initial values of bottleneck and frame-size if - * iSAC is used in channel-adaptive mode. Through this API, users can - * enforce a frame-size for all values of bottleneck. Then iSAC will not - * automatically change the frame-size. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rateBPS : initial value of bottleneck in bits/second - * 10000 <= rateBPS <= 32000 is accepted - * - frameSizeMs : number of milliseconds per frame (30 or 60) - * - enforceFrameSize : 1 to enforce the given frame-size through out - * the adaptation process, 0 to let iSAC change - * the frame-size if required. - * - * Return value : 0 - ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_ControlBwe(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 rateBPS, - WebRtc_Word16 frameSizeMs, - WebRtc_Word16 enforceFrameSize); - - - - /**************************************************************************** - * WebRtcIsacfix_version(...) - * - * This function returns the version number. - * - * Output: - * - version : Pointer to character string - * - */ - - void WebRtcIsacfix_version(char *version); - - - /**************************************************************************** - * WebRtcIsacfix_GetErrorCode(...) - * - * This function can be used to check the error code of an iSAC instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occured in the - * specified instance. - * - * Input: - * - ISAC_main_inst : ISAC instance - * - * Return value : Error code - */ - - WebRtc_Word16 WebRtcIsacfix_GetErrorCode(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_GetUplinkBw(...) - * - * This function return iSAC send bitrate - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : <0 Error code - * else bitrate - */ - - WebRtc_Word32 WebRtcIsacfix_GetUplinkBw(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_SetMaxPayloadSize(...) - * - * This function sets a limit for the maximum payload size of iSAC. The same - * value is used both for 30 and 60 msec packets. - * The absolute max will be valid until next time the function is called. - * NOTE! This function may override the function WebRtcIsacfix_SetMaxRate() - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxPayloadBytes : maximum size of the payload in bytes - * valid values are between 100 and 400 bytes - * - * - * Return value : 0 if sucessful - * -1 if error happens - */ - - WebRtc_Word16 WebRtcIsacfix_SetMaxPayloadSize(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 maxPayloadBytes); - - - /**************************************************************************** - * WebRtcIsacfix_SetMaxRate(...) - * - * This function sets the maximum rate which the codec may not exceed for a - * singel packet. The maximum rate is set in bits per second. - * The codec has an absolute maximum rate of 53400 bits per second (200 bytes - * per 30 msec). - * It is possible to set a maximum rate between 32000 and 53400 bits per second. - * - * The rate limit is valid until next time the function is called. - * - * NOTE! Packet size will never go above the value set if calling - * WebRtcIsacfix_SetMaxPayloadSize() (default max packet size is 400 bytes). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxRateInBytes : maximum rate in bits per second, - * valid values are 32000 to 53400 bits - * - * Return value : 0 if sucessful - * -1 if error happens - */ - - WebRtc_Word16 WebRtcIsacfix_SetMaxRate(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word32 maxRate); - - /**************************************************************************** - * WebRtcIsacfix_CreateInternal(...) - * - * This function creates the memory that is used to store data in the encoder - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_CreateInternal(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_FreeInternal(...) - * - * This function frees the internal memory for storing encoder data. - * - * Input: - * - ISAC_main_inst : an ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_FreeInternal(ISACFIX_MainStruct *ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsacfix_GetNewBitStream(...) - * - * This function returns encoded data, with the recieved bwe-index in the - * stream. It should always return a complete packet, i.e. only called once - * even for 60 msec frames - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - bweIndex : index of bandwidth estimate to put in new bitstream - * - scale : factor for rate change (0.4 ~=> half the rate, 1 no change). - * - * Output: - * - encoded : the encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsacfix_GetNewBitStream(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 bweIndex, - float scale, - WebRtc_Word16 *encoded); - - - /**************************************************************************** - * WebRtcIsacfix_GetDownLinkBwIndex(...) - * - * This function returns index representing the Bandwidth estimate from - * other side to this side. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - rateIndex : Bandwidth estimate to transmit to other side. - * - */ - - WebRtc_Word16 WebRtcIsacfix_GetDownLinkBwIndex(ISACFIX_MainStruct* ISAC_main_inst, - WebRtc_Word16* rateIndex); - - - /**************************************************************************** - * WebRtcIsacfix_UpdateUplinkBw(...) - * - * This function takes an index representing the Bandwidth estimate from - * this side to other side and updates BWE. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - rateIndex : Bandwidth estimate from other side. - * - */ - - WebRtc_Word16 WebRtcIsacfix_UpdateUplinkBw(ISACFIX_MainStruct* ISAC_main_inst, - WebRtc_Word16 rateIndex); - - - /**************************************************************************** - * WebRtcIsacfix_ReadBwIndex(...) - * - * This function returns the index of the Bandwidth estimate from the bitstream. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - rateIndex : Bandwidth estimate in bitstream - * - */ - - WebRtc_Word16 WebRtcIsacfix_ReadBwIndex(const WebRtc_Word16* encoded, - WebRtc_Word16* rateIndex); - - - /**************************************************************************** - * WebRtcIsacfix_GetNewFrameLen(...) - * - * This function return the next frame length (in samples) of iSAC. - * - * Input: - * -ISAC_main_inst : iSAC instance - * - * Return value : frame lenght in samples - */ - - WebRtc_Word16 WebRtcIsacfix_GetNewFrameLen(ISACFIX_MainStruct *ISAC_main_inst); - - -#if defined(__cplusplus) -} -#endif - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_INTERFACE_ISACFIX_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/Android.mk b/modules/audio_coding/codecs/iSAC/fix/source/Android.mk deleted file mode 100644 index 4243cb00a..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/Android.mk +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_isacfix -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := arith_routines.c \ - arith_routines_hist.c \ - arith_routines_logist.c \ - bandwidth_estimator.c \ - decode.c \ - decode_bwe.c \ - decode_plc.c \ - encode.c \ - entropy_coding.c \ - fft.c \ - filterbank_tables.c \ - filterbanks.c \ - filters.c \ - initialize.c \ - isacfix.c \ - lattice.c \ - lpc_masking_model.c \ - lpc_tables.c \ - pitch_estimator.c \ - pitch_filter.c \ - pitch_gain_tables.c \ - pitch_lag_tables.c \ - spectrum_ar_model_tables.c \ - transform.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines.c b/modules/audio_coding/codecs/iSAC/fix/source/arith_routines.c deleted file mode 100644 index ee62bad5f..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routins.c - * - * This C file contains a function for finalizing the bitstream - * after arithmetic coding. - * - */ - -#include "arith_routins.h" - - -/**************************************************************************** - * WebRtcIsacfix_EncTerminate(...) - * - * Final call to the arithmetic coder for an encoder call. This function - * terminates and return byte stream. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - * Return value : number of bytes in the stream - */ -WebRtc_Word16 WebRtcIsacfix_EncTerminate(Bitstr_enc *streamData) -{ - WebRtc_UWord16 *streamPtr; - WebRtc_UWord16 negCarry; - - /* point to the right place in the stream buffer */ - streamPtr = streamData->stream + streamData->stream_index; - - /* find minimum length (determined by current interval width) */ - if ( streamData->W_upper > 0x01FFFFFF ) - { - streamData->streamval += 0x01000000; - - /* if result is less than the added value we must take care of the carry */ - if (streamData->streamval < 0x01000000) - { - /* propagate carry */ - if (streamData->full == 0) { - /* Add value to current value */ - negCarry = *streamPtr; - negCarry += 0x0100; - *streamPtr = negCarry; - - /* if value is too big, propagate carry to next byte, and so on */ - while (!(negCarry)) - { - negCarry = *--streamPtr; - negCarry++; - *streamPtr = negCarry; - } - } else { - /* propagate carry by adding one to the previous byte in the - * stream if that byte is 0xFFFF we need to propagate the carry - * furhter back in the stream */ - while ( !(++(*--streamPtr)) ); - } - - /* put pointer back to the old value */ - streamPtr = streamData->stream + streamData->stream_index; - } - /* write remaining data to bitstream, if "full == 0" first byte has data */ - if (streamData->full == 0) { - *streamPtr++ += (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); - streamData->full = 1; - } else { - *streamPtr = (WebRtc_UWord16) WEBRTC_SPL_LSHIFT_W32( - WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24), 8); - streamData->full = 0; - } - } - else - { - streamData->streamval += 0x00010000; - - /* if result is less than the added value we must take care of the carry */ - if (streamData->streamval < 0x00010000) - { - /* propagate carry */ - if (streamData->full == 0) { - /* Add value to current value */ - negCarry = *streamPtr; - negCarry += 0x0100; - *streamPtr = negCarry; - - /* if value to big, propagate carry to next byte, and so on */ - while (!(negCarry)) - { - negCarry = *--streamPtr; - negCarry++; - *streamPtr = negCarry; - } - } else { - /* Add carry to previous byte */ - while ( !(++(*--streamPtr)) ); - } - - /* put pointer back to the old value */ - streamPtr = streamData->stream + streamData->stream_index; - } - /* write remaining data (2 bytes) to bitstream */ - if (streamData->full) { - *streamPtr++ = (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 16); - } else { - *streamPtr++ |= (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); - *streamPtr = (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 8) - & 0xFF00; - } - } - - /* calculate stream length in bytes */ - return (((streamPtr - streamData->stream)<<1) + !(streamData->full)); -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_hist.c b/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_hist.c deleted file mode 100644 index 14f1addab..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_hist.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routinshist.c - * - * This C file contains arithmetic encoding and decoding. - * - */ - -#include "arith_routins.h" - - -/**************************************************************************** - * WebRtcIsacfix_EncHistMulti(...) - * - * Encode the histogram interval - * - * Input: - * - streamData : in-/output struct containing bitstream - * - data : data vector - * - cdf : array of cdf arrays - * - lenData : data vector length - * - * Return value : 0 if ok - * <0 if error detected - */ -int WebRtcIsacfix_EncHistMulti(Bitstr_enc *streamData, - const WebRtc_Word16 *data, - const WebRtc_UWord16 **cdf, - const WebRtc_Word16 lenData) -{ - WebRtc_UWord32 W_lower; - WebRtc_UWord32 W_upper; - WebRtc_UWord32 W_upper_LSB; - WebRtc_UWord32 W_upper_MSB; - WebRtc_UWord16 *streamPtr; - WebRtc_UWord16 negCarry; - WebRtc_UWord16 *maxStreamPtr; - WebRtc_UWord16 *streamPtrCarry; - WebRtc_UWord32 cdfLo; - WebRtc_UWord32 cdfHi; - int k; - - - /* point to beginning of stream buffer - * and set maximum streamPtr value */ - streamPtr = streamData->stream + streamData->stream_index; - maxStreamPtr = streamData->stream + STREAM_MAXW16_60MS - 1; - - W_upper = streamData->W_upper; - - for (k = lenData; k > 0; k--) - { - /* fetch cdf_lower and cdf_upper from cdf tables */ - cdfLo = (WebRtc_UWord32) *(*cdf + (WebRtc_UWord32)*data); - cdfHi = (WebRtc_UWord32) *(*cdf++ + (WebRtc_UWord32)*data++ + 1); - - /* update interval */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = WEBRTC_SPL_RSHIFT_W32(W_upper, 16); - W_lower = WEBRTC_SPL_UMUL(W_upper_MSB, cdfLo); - W_lower += WEBRTC_SPL_UMUL_RSFT16(W_upper_LSB, cdfLo); - W_upper = WEBRTC_SPL_UMUL(W_upper_MSB, cdfHi); - W_upper += WEBRTC_SPL_UMUL_RSFT16(W_upper_LSB, cdfHi); - - /* shift interval such that it begins at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamData->streamval += W_lower; - - /* handle carry */ - if (streamData->streamval < W_lower) - { - /* propagate carry */ - streamPtrCarry = streamPtr; - if (streamData->full == 0) { - negCarry = *streamPtrCarry; - negCarry += 0x0100; - *streamPtrCarry = negCarry; - while (!(negCarry)) - { - negCarry = *--streamPtrCarry; - negCarry++; - *streamPtrCarry = negCarry; - } - } else { - while ( !(++(*--streamPtrCarry)) ); - } - } - - /* renormalize interval, store most significant byte of streamval and update streamval - * W_upper < 2^24 */ - while ( !(W_upper & 0xFF000000) ) - { - W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); - if (streamData->full == 0) { - *streamPtr++ += (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24); - streamData->full = 1; - } else { - *streamPtr = (WebRtc_UWord16) WEBRTC_SPL_LSHIFT_W32( - WEBRTC_SPL_RSHIFT_W32(streamData->streamval, 24), 8); - streamData->full = 0; - } - - if( streamPtr > maxStreamPtr ) { - return -ISAC_DISALLOWED_BITSTREAM_LENGTH; - } - streamData->streamval = WEBRTC_SPL_LSHIFT_W32(streamData->streamval, 8); - } - } - - /* calculate new stream_index */ - streamData->stream_index = streamPtr - streamData->stream; - streamData->W_upper = W_upper; - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_DecHistBisectMulti(...) - * - * Function to decode more symbols from the arithmetic bytestream, using - * method of bisection cdf tables should be of size 2^k-1 (which corresponds - * to an alphabet size of 2^k-2) - * - * Input: - * - streamData : in-/output struct containing bitstream - * - cdf : array of cdf arrays - * - cdfSize : array of cdf table sizes+1 (power of two: 2^k) - * - lenData : data vector length - * - * Output: - * - data : data vector - * - * Return value : number of bytes in the stream - * <0 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecHistBisectMulti(WebRtc_Word16 *data, - Bitstr_dec *streamData, - const WebRtc_UWord16 **cdf, - const WebRtc_UWord16 *cdfSize, - const WebRtc_Word16 lenData) -{ - WebRtc_UWord32 W_lower = 0; - WebRtc_UWord32 W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord32 W_upper_LSB; - WebRtc_UWord32 W_upper_MSB; - WebRtc_UWord32 streamval; - const WebRtc_UWord16 *streamPtr; - const WebRtc_UWord16 *cdfPtr; - WebRtc_Word16 sizeTmp; - int k; - - - streamPtr = streamData->stream + streamData->stream_index; - W_upper = streamData->W_upper; - - /* Error check: should not be possible in normal operation */ - if (W_upper == 0) { - return -2; - } - - /* first time decoder is called for this stream */ - if (streamData->stream_index == 0) - { - /* read first word from bytestream */ - streamval = WEBRTC_SPL_LSHIFT_W32((WebRtc_UWord32)*streamPtr++, 16); - streamval |= *streamPtr++; - } else { - streamval = streamData->streamval; - } - - for (k = lenData; k > 0; k--) - { - /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = WEBRTC_SPL_RSHIFT_W32(W_upper, 16); - - /* start halfway the cdf range */ - sizeTmp = WEBRTC_SPL_RSHIFT_W16(*cdfSize++, 1); - cdfPtr = *cdf + (sizeTmp - 1); - - /* method of bisection */ - for ( ;; ) - { - W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); - sizeTmp = WEBRTC_SPL_RSHIFT_W16(sizeTmp, 1); - if (sizeTmp == 0) { - break; - } - - if (streamval > W_tmp) - { - W_lower = W_tmp; - cdfPtr += sizeTmp; - } else { - W_upper = W_tmp; - cdfPtr -= sizeTmp; - } - } - if (streamval > W_tmp) - { - W_lower = W_tmp; - *data++ = cdfPtr - *cdf++; - } else { - W_upper = W_tmp; - *data++ = cdfPtr - *cdf++ - 1; - } - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamval -= W_lower; - - /* renormalize interval and update streamval */ - /* W_upper < 2^24 */ - while ( !(W_upper & 0xFF000000) ) - { - /* read next byte from stream */ - if (streamData->full == 0) { - streamval = WEBRTC_SPL_LSHIFT_W32(streamval, 8) | - (*streamPtr++ & 0x00FF); - streamData->full = 1; - } else { - streamval = WEBRTC_SPL_LSHIFT_W32(streamval, 8) | - WEBRTC_SPL_RSHIFT_W16(*streamPtr, 8); - streamData->full = 0; - } - W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); - } - - - /* Error check: should not be possible in normal operation */ - if (W_upper == 0) { - return -2; - } - - } - - streamData->stream_index = streamPtr - streamData->stream; - streamData->W_upper = W_upper; - streamData->streamval = streamval; - - if ( W_upper > 0x01FFFFFF ) { - return (streamData->stream_index*2 - 3 + !streamData->full); - } else { - return (streamData->stream_index*2 - 2 + !streamData->full); - } -} - - -/**************************************************************************** - * WebRtcIsacfix_DecHistOneStepMulti(...) - * - * Function to decode more symbols from the arithmetic bytestream, taking - * single step up or down at a time. - * cdf tables can be of arbitrary size, but large tables may take a lot of - * iterations. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - cdf : array of cdf arrays - * - initIndex : vector of initial cdf table search entries - * - lenData : data vector length - * - * Output: - * - data : data vector - * - * Return value : number of bytes in original stream - * <0 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecHistOneStepMulti(WebRtc_Word16 *data, - Bitstr_dec *streamData, - const WebRtc_UWord16 **cdf, - const WebRtc_UWord16 *initIndex, - const WebRtc_Word16 lenData) -{ - WebRtc_UWord32 W_lower; - WebRtc_UWord32 W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord32 W_upper_LSB; - WebRtc_UWord32 W_upper_MSB; - WebRtc_UWord32 streamval; - const WebRtc_UWord16 *streamPtr; - const WebRtc_UWord16 *cdfPtr; - int k; - - - streamPtr = streamData->stream + streamData->stream_index; - W_upper = streamData->W_upper; - /* Error check: Should not be possible in normal operation */ - if (W_upper == 0) { - return -2; - } - - /* Check if it is the first time decoder is called for this stream */ - if (streamData->stream_index == 0) - { - /* read first word from bytestream */ - streamval = WEBRTC_SPL_LSHIFT_U32(*streamPtr++, 16); - streamval |= *streamPtr++; - } else { - streamval = streamData->streamval; - } - - for (k = lenData; k > 0; k--) - { - /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = WEBRTC_SPL_RSHIFT_U32(W_upper, 16); - - /* start at the specified table entry */ - cdfPtr = *cdf + (*initIndex++); - W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); - - if (streamval > W_tmp) - { - for ( ;; ) - { - W_lower = W_tmp; - - /* range check */ - if (cdfPtr[0] == 65535) { - return -3; - } - - W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *++cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); - - if (streamval <= W_tmp) { - break; - } - } - W_upper = W_tmp; - *data++ = cdfPtr - *cdf++ - 1; - } else { - for ( ;; ) - { - W_upper = W_tmp; - --cdfPtr; - - /* range check */ - if (cdfPtr < *cdf) { - return -3; - } - - W_tmp = WEBRTC_SPL_UMUL_32_16(W_upper_MSB, *cdfPtr); - W_tmp += WEBRTC_SPL_UMUL_32_16_RSFT16(W_upper_LSB, *cdfPtr); - - if (streamval > W_tmp) { - break; - } - } - W_lower = W_tmp; - *data++ = cdfPtr - *cdf++; - } - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamval -= W_lower; - - /* renormalize interval and update streamval */ - /* W_upper < 2^24 */ - while ( !(W_upper & 0xFF000000) ) - { - /* read next byte from stream */ - if (streamData->full == 0) { - streamval = WEBRTC_SPL_LSHIFT_W32(streamval, 8) | (*streamPtr++ & 0x00FF); - streamData->full = 1; - } else { - streamval = WEBRTC_SPL_LSHIFT_W32(streamval, 8) | (*streamPtr >> 8); - streamData->full = 0; - } - W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); - } - } - - streamData->stream_index = streamPtr - streamData->stream; - streamData->W_upper = W_upper; - streamData->streamval = streamval; - - /* find number of bytes in original stream (determined by current interval width) */ - if ( W_upper > 0x01FFFFFF ) { - return (streamData->stream_index*2 - 3 + !streamData->full); - } else { - return (streamData->stream_index*2 - 2 + !streamData->full); - } -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_logist.c b/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_logist.c deleted file mode 100644 index 39c437e51..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/arith_routines_logist.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routinslogist.c - * - * This C file contains arithmetic encode and decode logistic - * - */ - -#include "arith_routins.h" - - -/* Tables for piecewise linear cdf functions: y = k*x */ - -/* x Points for function piecewise() in Q15 */ -static const WebRtc_Word32 kHistEdges[51] = { - -327680, -314573, -301466, -288359, -275252, -262144, -249037, -235930, -222823, -209716, - -196608, -183501, -170394, -157287, -144180, -131072, -117965, -104858, -91751, -78644, - -65536, -52429, -39322, -26215, -13108, 0, 13107, 26214, 39321, 52428, - 65536, 78643, 91750, 104857, 117964, 131072, 144179, 157286, 170393, 183500, - 196608, 209715, 222822, 235929, 249036, 262144, 275251, 288358, 301465, 314572, - 327680 -}; - - -/* k Points for function piecewise() in Q0 */ -static const WebRtc_UWord16 kCdfSlope[51] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 13, 23, 47, 87, 154, 315, 700, 1088, - 2471, 6064, 14221, 21463, 36634, 36924, 19750, 13270, 5806, 2312, - 1095, 660, 316, 145, 86, 41, 32, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 0 -}; - -/* y Points for function piecewise() in Q0 */ -static const WebRtc_UWord16 kCdfLogistic[51] = { - 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, - 20, 22, 24, 29, 38, 57, 92, 153, 279, 559, - 994, 1983, 4408, 10097, 18682, 33336, 48105, 56005, 61313, 63636, - 64560, 64998, 65262, 65389, 65447, 65481, 65497, 65510, 65512, 65514, - 65516, 65518, 65520, 65522, 65524, 65526, 65528, 65530, 65532, 65534, - 65535 -}; - - -/**************************************************************************** - * WebRtcIsacfix_Piecewise(...) - * - * Piecewise linear function - * - * Input: - * - xinQ15 : input value x in Q15 - * - * Return value : korresponding y-value in Q0 - */ - - -static __inline WebRtc_UWord16 WebRtcIsacfix_Piecewise(WebRtc_Word32 xinQ15) { - WebRtc_Word32 ind; - WebRtc_Word32 qtmp1; - WebRtc_UWord16 qtmp2; - - /* Find index for x-value */ - qtmp1 = WEBRTC_SPL_SAT(kHistEdges[50],xinQ15,kHistEdges[0]); - ind = WEBRTC_SPL_MUL(5, qtmp1 - kHistEdges[0]); - ind = WEBRTC_SPL_RSHIFT_W32(ind, 16); - - /* Calculate corresponding y-value ans return*/ - qtmp1 = qtmp1 - kHistEdges[ind]; - qtmp2 = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_U32( - WEBRTC_SPL_UMUL_32_16(qtmp1,kCdfSlope[ind]), 15); - return (kCdfLogistic[ind] + qtmp2); -} - -/**************************************************************************** - * WebRtcIsacfix_EncLogisticMulti2(...) - * - * Arithmetic coding of spectrum. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - dataQ7 : data vector in Q7 - * - envQ8 : side info vector defining the width of the pdf - * in Q8 - * - lenData : data vector length - * - * Return value : 0 if ok, - * <0 otherwise. - */ -int WebRtcIsacfix_EncLogisticMulti2(Bitstr_enc *streamData, - WebRtc_Word16 *dataQ7, - const WebRtc_UWord16 *envQ8, - const WebRtc_Word16 lenData) -{ - WebRtc_UWord32 W_lower; - WebRtc_UWord32 W_upper; - WebRtc_UWord16 W_upper_LSB; - WebRtc_UWord16 W_upper_MSB; - WebRtc_UWord16 *streamPtr; - WebRtc_UWord16 *maxStreamPtr; - WebRtc_UWord16 *streamPtrCarry; - WebRtc_UWord16 negcarry; - WebRtc_UWord32 cdfLo; - WebRtc_UWord32 cdfHi; - int k; - - /* point to beginning of stream buffer - * and set maximum streamPtr value */ - streamPtr = streamData->stream + streamData->stream_index; - maxStreamPtr = streamData->stream + STREAM_MAXW16_60MS - 1; - W_upper = streamData->W_upper; - - for (k = 0; k < lenData; k++) - { - /* compute cdf_lower and cdf_upper by evaluating the - * WebRtcIsacfix_Piecewise linear cdf */ - cdfLo = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(*dataQ7 - 64, *envQ8)); - cdfHi = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(*dataQ7 + 64, *envQ8)); - - /* test and clip if probability gets too small */ - while ((cdfLo + 1) >= cdfHi) { - /* clip */ - if (*dataQ7 > 0) { - *dataQ7 -= 128; - cdfHi = cdfLo; - cdfLo = WebRtcIsacfix_Piecewise( - WEBRTC_SPL_MUL_16_U16(*dataQ7 - 64, *envQ8)); - } else { - *dataQ7 += 128; - cdfLo = cdfHi; - cdfHi = WebRtcIsacfix_Piecewise( - WEBRTC_SPL_MUL_16_U16(*dataQ7 + 64, *envQ8)); - } - } - - dataQ7++; - /* increment only once per 4 iterations */ - envQ8 += (k & 1) & (k >> 1); - - - /* update interval */ - W_upper_LSB = (WebRtc_UWord16)W_upper; - W_upper_MSB = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_U32(W_upper, 16); - W_lower = WEBRTC_SPL_UMUL_32_16(cdfLo, W_upper_MSB); - W_lower += WEBRTC_SPL_UMUL_32_16_RSFT16(cdfLo, W_upper_LSB); - W_upper = WEBRTC_SPL_UMUL_32_16(cdfHi, W_upper_MSB); - W_upper += WEBRTC_SPL_UMUL_32_16_RSFT16(cdfHi, W_upper_LSB); - - /* shift interval such that it begins at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamData->streamval += W_lower; - - /* handle carry */ - if (streamData->streamval < W_lower) - { - /* propagate carry */ - streamPtrCarry = streamPtr; - if (streamData->full == 0) { - negcarry = *streamPtrCarry; - negcarry += 0x0100; - *streamPtrCarry = negcarry; - while (!(negcarry)) - { - negcarry = *--streamPtrCarry; - negcarry++; - *streamPtrCarry = negcarry; - } - } else { - while (!(++(*--streamPtrCarry))); - } - } - - /* renormalize interval, store most significant byte of streamval and update streamval - * W_upper < 2^24 */ - while ( !(W_upper & 0xFF000000) ) - { - W_upper = WEBRTC_SPL_LSHIFT_U32(W_upper, 8); - if (streamData->full == 0) { - *streamPtr++ += (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_U32( - streamData->streamval, 24); - streamData->full = 1; - } else { - *streamPtr = (WebRtc_UWord16) WEBRTC_SPL_LSHIFT_U32( - WEBRTC_SPL_RSHIFT_U32(streamData->streamval, 24), 8); - streamData->full = 0; - } - - if( streamPtr > maxStreamPtr ) - return -ISAC_DISALLOWED_BITSTREAM_LENGTH; - - streamData->streamval = WEBRTC_SPL_LSHIFT_U32(streamData->streamval, 8); - } - } - - /* calculate new stream_index */ - streamData->stream_index = streamPtr - streamData->stream; - streamData->W_upper = W_upper; - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_DecLogisticMulti2(...) - * - * Arithmetic decoding of spectrum. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - envQ8 : side info vector defining the width of the pdf - * in Q8 - * - lenData : data vector length - * - * Input/Output: - * - dataQ7 : input: dither vector, output: data vector - * - * Return value : number of bytes in the stream so far - * -1 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecLogisticMulti2(WebRtc_Word16 *dataQ7, - Bitstr_dec *streamData, - const WebRtc_Word32 *envQ8, - const WebRtc_Word16 lenData) -{ - WebRtc_UWord32 W_lower; - WebRtc_UWord32 W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord16 W_upper_LSB; - WebRtc_UWord16 W_upper_MSB; - WebRtc_UWord32 streamVal; - WebRtc_UWord16 cdfTmp; - WebRtc_Word32 res; - WebRtc_Word32 inSqrt; - WebRtc_Word32 newRes; - const WebRtc_UWord16 *streamPtr; - WebRtc_Word16 candQ7; - WebRtc_Word16 envCount; - WebRtc_UWord16 tmpARSpecQ8 = 0; - int k, i; - - - /* point to beginning of stream buffer */ - streamPtr = streamData->stream + streamData->stream_index; - W_upper = streamData->W_upper; - - /* Check if it is first time decoder is called for this stream */ - if (streamData->stream_index == 0) - { - /* read first word from bytestream */ - streamVal = WEBRTC_SPL_LSHIFT_U32(*streamPtr++, 16); - streamVal |= *streamPtr++; - - } else { - streamVal = streamData->streamval; - } - - - res = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, - WEBRTC_SPL_RSHIFT_W16(WebRtcSpl_GetSizeInBits(envQ8[0]), 1)); - envCount = 0; - - /* code assumes lenData%4 == 0 */ - for (k = 0; k < lenData; k += 4) - { - int k4; - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - inSqrt = envQ8[envCount]; - i = 10; - - /* For safty reasons */ - if (inSqrt < 0) - inSqrt=-inSqrt; - - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(inSqrt, res) + res, 1); - do - { - res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(inSqrt, res) + res, 1); - } while (newRes != res && i-- > 0); - - tmpARSpecQ8 = (WebRtc_UWord16)newRes; - - for(k4 = 0; k4 < 4; k4++) - { - /* find the integer *data for which streamVal lies in [W_lower+1, W_upper] */ - W_upper_LSB = (WebRtc_UWord16) (W_upper & 0x0000FFFF); - W_upper_MSB = (WebRtc_UWord16) WEBRTC_SPL_RSHIFT_U32(W_upper, 16); - - /* find first candidate by inverting the logistic cdf - * Input dither value collected from io-stream */ - candQ7 = - *dataQ7 + 64; - cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); - - if (streamVal > W_tmp) - { - W_lower = W_tmp; - candQ7 += 128; - cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); - - while (streamVal > W_tmp) - { - W_lower = W_tmp; - candQ7 += 128; - cdfTmp = WebRtcIsacfix_Piecewise( - WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); - - /* error check */ - if (W_lower == W_tmp) { - return -1; - } - } - W_upper = W_tmp; - - /* Output value put in dataQ7: another sample decoded */ - *dataQ7 = candQ7 - 64; - } - else - { - W_upper = W_tmp; - candQ7 -= 128; - cdfTmp = WebRtcIsacfix_Piecewise(WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); - - while ( !(streamVal > W_tmp) ) - { - W_upper = W_tmp; - candQ7 -= 128; - cdfTmp = WebRtcIsacfix_Piecewise( - WEBRTC_SPL_MUL_16_U16(candQ7, tmpARSpecQ8)); - - W_tmp = WEBRTC_SPL_UMUL_16_16(cdfTmp, W_upper_MSB); - W_tmp += WEBRTC_SPL_UMUL_16_16_RSFT16(cdfTmp, W_upper_LSB); - - /* error check */ - if (W_upper == W_tmp){ - return -1; - } - } - W_lower = W_tmp; - - /* Output value put in dataQ7: another sample decoded */ - *dataQ7 = candQ7 + 64; - } - - dataQ7++; - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamVal -= W_lower; - - /* renormalize interval and update streamVal - * W_upper < 2^24 */ - while ( !(W_upper & 0xFF000000) ) - { - /* read next byte from stream */ - if (streamData->full == 0) { - streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | (*streamPtr++ & 0x00FF); - streamData->full = 1; - } else { - streamVal = WEBRTC_SPL_LSHIFT_W32(streamVal, 8) | - WEBRTC_SPL_RSHIFT_U16(*streamPtr, 8); - streamData->full = 0; - } - W_upper = WEBRTC_SPL_LSHIFT_W32(W_upper, 8); - } - } - envCount++; - } - - streamData->stream_index = streamPtr - streamData->stream; - streamData->W_upper = W_upper; - streamData->streamval = streamVal; - - /* find number of bytes in original stream (determined by current interval width) */ - if ( W_upper > 0x01FFFFFF ) - return (streamData->stream_index*2 - 3 + !streamData->full); - else - return (streamData->stream_index*2 - 2 + !streamData->full); -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/arith_routins.h b/modules/audio_coding/codecs/iSAC/fix/source/arith_routins.h deleted file mode 100644 index 9aa49dac7..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/arith_routins.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routins.h - * - * Functions for arithmetic coding. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ARITH_ROUTINS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ARITH_ROUTINS_H_ - -#include "structs.h" - - -/**************************************************************************** - * WebRtcIsacfix_EncLogisticMulti2(...) - * - * Arithmetic coding of spectrum. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - dataQ7 : data vector in Q7 - * - envQ8 : side info vector defining the width of the pdf - * in Q8 - * - lenData : data vector length - * - * Return value : 0 if ok, - * <0 otherwise. - */ -int WebRtcIsacfix_EncLogisticMulti2( - Bitstr_enc *streamData, - WebRtc_Word16 *dataQ7, - const WebRtc_UWord16 *env, - const WebRtc_Word16 lenData); - - -/**************************************************************************** - * WebRtcIsacfix_EncTerminate(...) - * - * Final call to the arithmetic coder for an encoder call. This function - * terminates and return byte stream. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - * Return value : number of bytes in the stream - */ -WebRtc_Word16 WebRtcIsacfix_EncTerminate(Bitstr_enc *streamData); - - -/**************************************************************************** - * WebRtcIsacfix_DecLogisticMulti2(...) - * - * Arithmetic decoding of spectrum. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - envQ8 : side info vector defining the width of the pdf - * in Q8 - * - lenData : data vector length - * - * Input/Output: - * - dataQ7 : input: dither vector, output: data vector, in Q7 - * - * Return value : number of bytes in the stream so far - * <0 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecLogisticMulti2( - WebRtc_Word16 *data, - Bitstr_dec *streamData, - const WebRtc_Word32 *env, - const WebRtc_Word16 lenData); - - -/**************************************************************************** - * WebRtcIsacfix_EncHistMulti(...) - * - * Encode the histogram interval - * - * Input: - * - streamData : in-/output struct containing bitstream - * - data : data vector - * - cdf : array of cdf arrays - * - lenData : data vector length - * - * Return value : 0 if ok - * <0 if error detected - */ -int WebRtcIsacfix_EncHistMulti( - Bitstr_enc *streamData, - const WebRtc_Word16 *data, - const WebRtc_UWord16 **cdf, - const WebRtc_Word16 lenData); - - -/**************************************************************************** - * WebRtcIsacfix_DecHistBisectMulti(...) - * - * Function to decode more symbols from the arithmetic bytestream, using - * method of bisection. - * C df tables should be of size 2^k-1 (which corresponds to an - * alphabet size of 2^k-2) - * - * Input: - * - streamData : in-/output struct containing bitstream - * - cdf : array of cdf arrays - * - cdfSize : array of cdf table sizes+1 (power of two: 2^k) - * - lenData : data vector length - * - * Output: - * - data : data vector - * - * Return value : number of bytes in the stream - * <0 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecHistBisectMulti( - WebRtc_Word16 *data, - Bitstr_dec *streamData, - const WebRtc_UWord16 **cdf, - const WebRtc_UWord16 *cdfSize, - const WebRtc_Word16 lenData); - - -/**************************************************************************** - * WebRtcIsacfix_DecHistOneStepMulti(...) - * - * Function to decode more symbols from the arithmetic bytestream, taking - * single step up or down at a time. - * cdf tables can be of arbitrary size, but large tables may take a lot of - * iterations. - * - * Input: - * - streamData : in-/output struct containing bitstream - * - cdf : array of cdf arrays - * - initIndex : vector of initial cdf table search entries - * - lenData : data vector length - * - * Output: - * - data : data vector - * - * Return value : number of bytes in original stream - * <0 if error detected - */ -WebRtc_Word16 WebRtcIsacfix_DecHistOneStepMulti( - WebRtc_Word16 *data, - Bitstr_dec *streamData, - const WebRtc_UWord16 **cdf, - const WebRtc_UWord16 *initIndex, - const WebRtc_Word16 lenData); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ARITH_ROUTINS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.c b/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.c deleted file mode 100644 index a274b6671..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.c +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * bandwidth_estimator.c - * - * This file contains the code for the Bandwidth Estimator designed - * for iSAC. - * - * NOTE! Castings needed for C55, do not remove! - * - */ - -#include "bandwidth_estimator.h" -#include "settings.h" - - -/* array of quantization levels for bottle neck info; Matlab code: */ -/* sprintf('%4.1ff, ', logspace(log10(5000), log10(40000), 12)) */ -static const WebRtc_Word16 kQRateTable[12] = { - 10000, 11115, 12355, 13733, 15265, 16967, - 18860, 20963, 23301, 25900, 28789, 32000 -}; - -/* 0.1 times the values in the table kQRateTable */ -/* values are in Q16 */ -static const WebRtc_Word32 KQRate01[12] = { - 65536000, 72843264, 80969728, 90000589, 100040704, 111194931, - 123600896, 137383117, 152705434, 169738240, 188671590, 209715200 -}; - -/* Bits per Bytes Seconds - * 8 bits/byte * 1000 msec/sec * 1/framelength (in msec)->bits/byte*sec - * frame length will either be 30 or 60 msec. 8738 is 1/60 in Q19 and 1/30 in Q18 - * The following number is either in Q15 or Q14 depending on the current frame length */ -static const WebRtc_Word32 kBitsByteSec = 4369000; - -/* Received header rate. First value is for 30 ms packets and second for 60 ms */ -static const WebRtc_Word16 kRecHeaderRate[2] = { - 9333, 4666 -}; - -/* Inverted minimum and maximum bandwidth in Q30. - minBwInv 30 ms, maxBwInv 30 ms, - minBwInv 60 ms, maxBwInv 69 ms -*/ -static const WebRtc_Word32 kInvBandwidth[4] = { - 55539, 25978, - 73213, 29284 -}; - -/* Number of samples in 25 msec */ -static const WebRtc_Word32 kSamplesIn25msec = 400; - - -/**************************************************************************** - * WebRtcIsacfix_InitBandwidthEstimator(...) - * - * This function initializes the struct for the bandwidth estimator - * - * Input/Output: - * - bweStr : Struct containing bandwidth information. - * - * Return value : 0 - */ -WebRtc_Word32 WebRtcIsacfix_InitBandwidthEstimator(BwEstimatorstr *bweStr) -{ - bweStr->prevFrameSizeMs = INIT_FRAME_LEN; - bweStr->prevRtpNumber = 0; - bweStr->prevSendTime = 0; - bweStr->prevArrivalTime = 0; - bweStr->prevRtpRate = 1; - bweStr->lastUpdate = 0; - bweStr->lastReduction = 0; - bweStr->countUpdates = -9; - - /* INIT_BN_EST = 20000 - * INIT_BN_EST_Q7 = 2560000 - * INIT_HDR_RATE = 4666 - * INIT_REC_BN_EST_Q5 = 789312 - * - * recBwInv = 1/(INIT_BN_EST + INIT_HDR_RATE) in Q30 - * recBwAvg = INIT_BN_EST + INIT_HDR_RATE in Q5 - */ - bweStr->recBwInv = 43531; - bweStr->recBw = INIT_BN_EST; - bweStr->recBwAvgQ = INIT_BN_EST_Q7; - bweStr->recBwAvg = INIT_REC_BN_EST_Q5; - bweStr->recJitter = (WebRtc_Word32) 327680; /* 10 in Q15 */ - bweStr->recJitterShortTerm = 0; - bweStr->recJitterShortTermAbs = (WebRtc_Word32) 40960; /* 5 in Q13 */ - bweStr->recMaxDelay = (WebRtc_Word32) 10; - bweStr->recMaxDelayAvgQ = (WebRtc_Word32) 5120; /* 10 in Q9 */ - bweStr->recHeaderRate = INIT_HDR_RATE; - bweStr->countRecPkts = 0; - bweStr->sendBwAvg = INIT_BN_EST_Q7; - bweStr->sendMaxDelayAvg = (WebRtc_Word32) 5120; /* 10 in Q9 */ - - bweStr->countHighSpeedRec = 0; - bweStr->highSpeedRec = 0; - bweStr->countHighSpeedSent = 0; - bweStr->highSpeedSend = 0; - bweStr->inWaitPeriod = 0; - - /* Find the inverse of the max bw and min bw in Q30 - * (1 / (MAX_ISAC_BW + INIT_HDR_RATE) in Q30 - * (1 / (MIN_ISAC_BW + INIT_HDR_RATE) in Q30 - */ - bweStr->maxBwInv = kInvBandwidth[3]; - bweStr->minBwInv = kInvBandwidth[2]; - - return 0; -} - -/**************************************************************************** - * WebRtcIsacfix_UpdateUplinkBwImpl(...) - * - * This function updates bottle neck rate received from other side in payload - * and calculates a new bottle neck to send to the other side. - * - * Input/Output: - * - bweStr : struct containing bandwidth information. - * - rtpNumber : value from RTP packet, from NetEq - * - frameSize : length of signal frame in ms, from iSAC decoder - * - sendTime : value in RTP header giving send time in samples - * - arrivalTime : value given by timeGetTime() time of arrival in - * samples of packet from NetEq - * - pksize : size of packet in bytes, from NetEq - * - Index : integer (range 0...23) indicating bottle neck & - * jitter as estimated by other side - * - * Return value : 0 if everything went fine, - * -1 otherwise - */ -WebRtc_Word32 WebRtcIsacfix_UpdateUplinkBwImpl(BwEstimatorstr *bweStr, - const WebRtc_UWord16 rtpNumber, - const WebRtc_Word16 frameSize, - const WebRtc_UWord32 sendTime, - const WebRtc_UWord32 arrivalTime, - const WebRtc_Word16 pksize, - const WebRtc_UWord16 Index) -{ - WebRtc_UWord16 weight = 0; - WebRtc_UWord32 currBwInv = 0; - WebRtc_UWord16 recRtpRate; - WebRtc_UWord32 arrTimeProj; - WebRtc_Word32 arrTimeDiff; - WebRtc_Word32 arrTimeNoise; - WebRtc_Word32 arrTimeNoiseAbs; - WebRtc_Word32 sendTimeDiff; - - WebRtc_Word32 delayCorrFactor = DELAY_CORRECTION_MED; - WebRtc_Word32 lateDiff = 0; - WebRtc_Word16 immediateSet = 0; - WebRtc_Word32 frameSizeSampl; - - WebRtc_Word32 temp; - WebRtc_Word32 msec; - WebRtc_UWord32 exponent; - WebRtc_UWord32 reductionFactor; - WebRtc_UWord32 numBytesInv; - WebRtc_Word32 sign; - - WebRtc_UWord32 byteSecondsPerBit; - WebRtc_UWord32 tempLower; - WebRtc_UWord32 tempUpper; - WebRtc_Word32 recBwAvgInv; - WebRtc_Word32 numPktsExpected; - - WebRtc_Word16 errCode; - - /* UPDATE ESTIMATES FROM OTHER SIDE */ - - /* The function also checks if Index has a valid value */ - errCode = WebRtcIsacfix_UpdateUplinkBwRec(bweStr, Index); - if (errCode <0) { - return(errCode); - } - - - /* UPDATE ESTIMATES ON THIS SIDE */ - - /* Bits per second per byte * 1/30 or 1/60 */ - if (frameSize == 60) { - /* If frameSize changed since last call, from 30 to 60, recalculate some values */ - if ( (frameSize != bweStr->prevFrameSizeMs) && (bweStr->countUpdates > 0)) { - bweStr->countUpdates = 10; - bweStr->recHeaderRate = kRecHeaderRate[1]; - - bweStr->maxBwInv = kInvBandwidth[3]; - bweStr->minBwInv = kInvBandwidth[2]; - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, (bweStr->recBw + bweStr->recHeaderRate)); - } - - /* kBitsByteSec is in Q15 */ - recRtpRate = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(kBitsByteSec, - (WebRtc_Word32)pksize), 15) + bweStr->recHeaderRate; - - } else { - /* If frameSize changed since last call, from 60 to 30, recalculate some values */ - if ( (frameSize != bweStr->prevFrameSizeMs) && (bweStr->countUpdates > 0)) { - bweStr->countUpdates = 10; - bweStr->recHeaderRate = kRecHeaderRate[0]; - - bweStr->maxBwInv = kInvBandwidth[1]; - bweStr->minBwInv = kInvBandwidth[0]; - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, (bweStr->recBw + bweStr->recHeaderRate)); - } - - /* kBitsByteSec is in Q14 */ - recRtpRate = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(kBitsByteSec, - (WebRtc_Word32)pksize), 14) + bweStr->recHeaderRate; - } - - - /* Check for timer wrap-around */ - if (arrivalTime < bweStr->prevArrivalTime) { - bweStr->prevArrivalTime = arrivalTime; - bweStr->lastUpdate = arrivalTime; - bweStr->lastReduction = arrivalTime + FS3; - - bweStr->countRecPkts = 0; - - /* store frame size */ - bweStr->prevFrameSizeMs = frameSize; - - /* store far-side transmission rate */ - bweStr->prevRtpRate = recRtpRate; - - /* store far-side RTP time stamp */ - bweStr->prevRtpNumber = rtpNumber; - - return 0; - } - - bweStr->countRecPkts++; - - /* Calculate framesize in msec */ - frameSizeSampl = WEBRTC_SPL_MUL_16_16((WebRtc_Word16)SAMPLES_PER_MSEC, frameSize); - - /* Check that it's not one of the first 9 packets */ - if ( bweStr->countUpdates > 0 ) { - - /* Stay in Wait Period for 1.5 seconds (no updates in wait period) */ - if(bweStr->inWaitPeriod) { - if ((arrivalTime - bweStr->startWaitPeriod)> FS_1_HALF) { - bweStr->inWaitPeriod = 0; - } - } - - /* If not been updated for a long time, reduce the BN estimate */ - - /* Check send time difference between this packet and previous received */ - sendTimeDiff = sendTime - bweStr->prevSendTime; - if (sendTimeDiff <= WEBRTC_SPL_LSHIFT_W32(frameSizeSampl, 1)) { - - /* Only update if 3 seconds has past since last update */ - if ((arrivalTime - bweStr->lastUpdate) > FS3) { - - /* Calculate expected number of received packets since last update */ - numPktsExpected = WEBRTC_SPL_UDIV(arrivalTime - bweStr->lastUpdate, frameSizeSampl); - - /* If received number of packets is more than 90% of expected (922 = 0.9 in Q10): */ - /* do the update, else not */ - if(WEBRTC_SPL_LSHIFT_W32(bweStr->countRecPkts, 10) > WEBRTC_SPL_MUL_16_16(922, numPktsExpected)) { - /* Q4 chosen to approx dividing by 16 */ - msec = (arrivalTime - bweStr->lastReduction); - - /* the number below represents 13 seconds, highly unlikely - but to insure no overflow when reduction factor is multiplied by recBw inverse */ - if (msec > 208000) { - msec = 208000; - } - - /* Q20 2^(negative number: - 76/1048576) = .99995 - product is Q24 */ - exponent = WEBRTC_SPL_UMUL(0x0000004C, msec); - - /* do the approx with positive exponent so that value is actually rf^-1 - and multiply by bw inverse */ - reductionFactor = WEBRTC_SPL_RSHIFT_U32(0x01000000 | (exponent & 0x00FFFFFF), - WEBRTC_SPL_RSHIFT_U32(exponent, 24)); - - /* reductionFactor in Q13 */ - reductionFactor = WEBRTC_SPL_RSHIFT_U32(reductionFactor, 11); - - if ( reductionFactor != 0 ) { - bweStr->recBwInv = WEBRTC_SPL_MUL((WebRtc_Word32)bweStr->recBwInv, (WebRtc_Word32)reductionFactor); - bweStr->recBwInv = WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)bweStr->recBwInv, 13); - - } else { - /* recBwInv = 1 / (INIT_BN_EST + INIT_HDR_RATE) in Q26 (Q30??)*/ - bweStr->recBwInv = WEBRTC_SPL_DIV((1073741824 + - WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)INIT_BN_EST + INIT_HDR_RATE), 1)), INIT_BN_EST + INIT_HDR_RATE); - } - - /* reset time-since-update counter */ - bweStr->lastReduction = arrivalTime; - } else { - /* Delay last reduction with 3 seconds */ - bweStr->lastReduction = arrivalTime + FS3; - bweStr->lastUpdate = arrivalTime; - bweStr->countRecPkts = 0; - } - } - } else { - bweStr->lastReduction = arrivalTime + FS3; - bweStr->lastUpdate = arrivalTime; - bweStr->countRecPkts = 0; - } - - - /* update only if previous packet was not lost */ - if ( rtpNumber == bweStr->prevRtpNumber + 1 ) { - arrTimeDiff = arrivalTime - bweStr->prevArrivalTime; - - if (!(bweStr->highSpeedSend && bweStr->highSpeedRec)) { - if (arrTimeDiff > frameSizeSampl) { - if (sendTimeDiff > 0) { - lateDiff = arrTimeDiff - sendTimeDiff - - WEBRTC_SPL_LSHIFT_W32(frameSizeSampl, 1); - } else { - lateDiff = arrTimeDiff - frameSizeSampl; - } - - /* 8000 is 1/2 second (in samples at FS) */ - if (lateDiff > 8000) { - delayCorrFactor = (WebRtc_Word32) DELAY_CORRECTION_MAX; - bweStr->inWaitPeriod = 1; - bweStr->startWaitPeriod = arrivalTime; - immediateSet = 1; - } else if (lateDiff > 5120) { - delayCorrFactor = (WebRtc_Word32) DELAY_CORRECTION_MED; - immediateSet = 1; - bweStr->inWaitPeriod = 1; - bweStr->startWaitPeriod = arrivalTime; - } - } - } - - if ((bweStr->prevRtpRate > WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32) bweStr->recBwAvg, 5)) && - (recRtpRate > WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)bweStr->recBwAvg, 5)) && - !bweStr->inWaitPeriod) { - - /* test if still in initiation period and increment counter */ - if (bweStr->countUpdates++ > 99) { - /* constant weight after initiation part, 0.01 in Q13 */ - weight = (WebRtc_UWord16) 82; - } else { - /* weight decreases with number of updates, 1/countUpdates in Q13 */ - weight = (WebRtc_UWord16) WebRtcSpl_DivW32W16( - (WebRtc_Word32)(8192 + WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32) bweStr->countUpdates, 1)), - (WebRtc_Word16)bweStr->countUpdates); - } - - /* Bottle Neck Estimation */ - - /* limit outliers, if more than 25 ms too much */ - if (arrTimeDiff > frameSizeSampl + kSamplesIn25msec) { - arrTimeDiff = frameSizeSampl + kSamplesIn25msec; - } - - /* don't allow it to be less than frame rate - 10 ms */ - if (arrTimeDiff < frameSizeSampl - FRAMESAMPLES_10ms) { - arrTimeDiff = frameSizeSampl - FRAMESAMPLES_10ms; - } - - /* compute inverse receiving rate for last packet, in Q19 */ - numBytesInv = (WebRtc_UWord16) WebRtcSpl_DivW32W16( - (WebRtc_Word32)(524288 + WEBRTC_SPL_RSHIFT_W32(((WebRtc_Word32)pksize + HEADER_SIZE), 1)), - (WebRtc_Word16)(pksize + HEADER_SIZE)); - - /* 8389 is ~ 1/128000 in Q30 */ - byteSecondsPerBit = WEBRTC_SPL_MUL_16_16(arrTimeDiff, 8389); - - /* get upper N bits */ - tempUpper = WEBRTC_SPL_RSHIFT_U32(byteSecondsPerBit, 15); - - /* get lower 15 bits */ - tempLower = byteSecondsPerBit & 0x00007FFF; - - tempUpper = WEBRTC_SPL_MUL(tempUpper, numBytesInv); - tempLower = WEBRTC_SPL_MUL(tempLower, numBytesInv); - tempLower = WEBRTC_SPL_RSHIFT_U32(tempLower, 15); - - currBwInv = tempUpper + tempLower; - currBwInv = WEBRTC_SPL_RSHIFT_U32(currBwInv, 4); - - /* Limit inv rate. Note that minBwInv > maxBwInv! */ - if(currBwInv < bweStr->maxBwInv) { - currBwInv = bweStr->maxBwInv; - } else if(currBwInv > bweStr->minBwInv) { - currBwInv = bweStr->minBwInv; - } - - /* update bottle neck rate estimate */ - bweStr->recBwInv = WEBRTC_SPL_UMUL(weight, currBwInv) + - WEBRTC_SPL_UMUL((WebRtc_UWord32) 8192 - weight, bweStr->recBwInv); - - /* Shift back to Q30 from Q40 (actual used bits shouldn't be more than 27 based on minBwInv) - up to 30 bits used with Q13 weight */ - bweStr->recBwInv = WEBRTC_SPL_RSHIFT_U32(bweStr->recBwInv, 13); - - /* reset time-since-update counter */ - bweStr->lastUpdate = arrivalTime; - bweStr->lastReduction = arrivalTime + FS3; - bweStr->countRecPkts = 0; - - /* to save resolution compute the inverse of recBwAvg in Q26 by left shifting numerator to 2^31 - and NOT right shifting recBwAvg 5 bits to an integer - At max 13 bits are used - shift to Q5 */ - recBwAvgInv = WEBRTC_SPL_UDIV((WebRtc_UWord32)(0x80000000 + WEBRTC_SPL_RSHIFT_U32(bweStr->recBwAvg, 1)), - bweStr->recBwAvg); - - /* Calculate Projected arrival time difference */ - - /* The numerator of the quotient can be 22 bits so right shift inv by 4 to avoid overflow - result in Q22 */ - arrTimeProj = WEBRTC_SPL_MUL((WebRtc_Word32)8000, recBwAvgInv); - /* shift to Q22 */ - arrTimeProj = WEBRTC_SPL_RSHIFT_U32(arrTimeProj, 4); - /* complete calulation */ - arrTimeProj = WEBRTC_SPL_MUL(((WebRtc_Word32)pksize + HEADER_SIZE), arrTimeProj); - /* shift to Q10 */ - arrTimeProj = WEBRTC_SPL_RSHIFT_U32(arrTimeProj, 12); - - /* difference between projected and actual arrival time differences */ - /* Q9 (only shift arrTimeDiff by 5 to simulate divide by 16 (need to revisit if change sampling rate) DH */ - if (WEBRTC_SPL_LSHIFT_W32(arrTimeDiff, 6) > (WebRtc_Word32)arrTimeProj) { - arrTimeNoise = WEBRTC_SPL_LSHIFT_W32(arrTimeDiff, 6) - arrTimeProj; - sign = 1; - } else { - arrTimeNoise = arrTimeProj - WEBRTC_SPL_LSHIFT_W32(arrTimeDiff, 6); - sign = -1; - } - - /* Q9 */ - arrTimeNoiseAbs = arrTimeNoise; - - /* long term averaged absolute jitter, Q15 */ - weight = WEBRTC_SPL_RSHIFT_W32(weight, 3); - bweStr->recJitter = WEBRTC_SPL_MUL(weight, WEBRTC_SPL_LSHIFT_W32(arrTimeNoiseAbs, 5)) - + WEBRTC_SPL_MUL(1024 - weight, bweStr->recJitter); - - /* remove the fractional portion */ - bweStr->recJitter = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitter, 10); - - /* Maximum jitter is 10 msec in Q15 */ - if (bweStr->recJitter > (WebRtc_Word32)327680) { - bweStr->recJitter = (WebRtc_Word32)327680; - } - - /* short term averaged absolute jitter */ - /* Calculation in Q13 products in Q23 */ - bweStr->recJitterShortTermAbs = WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32(arrTimeNoiseAbs, 3)) + - WEBRTC_SPL_MUL(973, bweStr->recJitterShortTermAbs); - bweStr->recJitterShortTermAbs = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTermAbs , 10); - - /* short term averaged jitter */ - /* Calculation in Q13 products in Q23 */ - bweStr->recJitterShortTerm = WEBRTC_SPL_MUL(205, WEBRTC_SPL_LSHIFT_W32(arrTimeNoise, 3)) * sign + - WEBRTC_SPL_MUL(3891, bweStr->recJitterShortTerm); - - if (bweStr->recJitterShortTerm < 0) { - temp = -bweStr->recJitterShortTerm; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 12); - bweStr->recJitterShortTerm = -temp; - } else { - bweStr->recJitterShortTerm = WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTerm, 12); - } - } - } - } else { - /* reset time-since-update counter when receiving the first 9 packets */ - bweStr->lastUpdate = arrivalTime; - bweStr->lastReduction = arrivalTime + FS3; - bweStr->countRecPkts = 0; - bweStr->countUpdates++; - } - - /* Limit to minimum or maximum bottle neck rate (in Q30) */ - if (bweStr->recBwInv > bweStr->minBwInv) { - bweStr->recBwInv = bweStr->minBwInv; - } else if (bweStr->recBwInv < bweStr->maxBwInv) { - bweStr->recBwInv = bweStr->maxBwInv; - } - - - /* store frame length */ - bweStr->prevFrameSizeMs = frameSize; - - /* store far-side transmission rate */ - bweStr->prevRtpRate = recRtpRate; - - /* store far-side RTP time stamp */ - bweStr->prevRtpNumber = rtpNumber; - - /* Replace bweStr->recMaxDelay by the new value (atomic operation) */ - if (bweStr->prevArrivalTime != 0xffffffff) { - bweStr->recMaxDelay = WEBRTC_SPL_MUL(3, bweStr->recJitter); - } - - /* store arrival time stamp */ - bweStr->prevArrivalTime = arrivalTime; - bweStr->prevSendTime = sendTime; - - /* Replace bweStr->recBw by the new value */ - bweStr->recBw = WEBRTC_SPL_UDIV(1073741824, bweStr->recBwInv) - bweStr->recHeaderRate; - - if (immediateSet) { - /* delay correction factor is in Q10 */ - bweStr->recBw = WEBRTC_SPL_UMUL(delayCorrFactor, bweStr->recBw); - bweStr->recBw = WEBRTC_SPL_RSHIFT_U32(bweStr->recBw, 10); - - if (bweStr->recBw < (WebRtc_Word32) MIN_ISAC_BW) { - bweStr->recBw = (WebRtc_Word32) MIN_ISAC_BW; - } - - bweStr->recBwAvg = WEBRTC_SPL_LSHIFT_U32(bweStr->recBw + bweStr->recHeaderRate, 5); - - bweStr->recBwAvgQ = WEBRTC_SPL_LSHIFT_U32(bweStr->recBw, 7); - - bweStr->recJitterShortTerm = 0; - - bweStr->recBwInv = WEBRTC_SPL_UDIV(1073741824, bweStr->recBw + bweStr->recHeaderRate); - - immediateSet = 0; - } - - - return 0; -} - -/* This function updates the send bottle neck rate */ -/* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */ -/* returns 0 if everything went fine, -1 otherwise */ -WebRtc_Word16 WebRtcIsacfix_UpdateUplinkBwRec(BwEstimatorstr *bweStr, - const WebRtc_Word16 Index) -{ - WebRtc_UWord16 RateInd; - - if ( (Index < 0) || (Index > 23) ) { - return -ISAC_RANGE_ERROR_BW_ESTIMATOR; - } - - /* UPDATE ESTIMATES FROM OTHER SIDE */ - - if ( Index > 11 ) { - RateInd = Index - 12; - /* compute the jitter estimate as decoded on the other side in Q9 */ - /* sendMaxDelayAvg = 0.9 * sendMaxDelayAvg + 0.1 * MAX_ISAC_MD */ - bweStr->sendMaxDelayAvg = WEBRTC_SPL_MUL(461, bweStr->sendMaxDelayAvg) + - WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)MAX_ISAC_MD, 9)); - bweStr->sendMaxDelayAvg = WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); - - } else { - RateInd = Index; - /* compute the jitter estimate as decoded on the other side in Q9 */ - /* sendMaxDelayAvg = 0.9 * sendMaxDelayAvg + 0.1 * MIN_ISAC_MD */ - bweStr->sendMaxDelayAvg = WEBRTC_SPL_MUL(461, bweStr->sendMaxDelayAvg) + - WEBRTC_SPL_MUL(51, WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)MIN_ISAC_MD,9)); - bweStr->sendMaxDelayAvg = WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); - - } - - - /* compute the BN estimate as decoded on the other side */ - /* sendBwAvg = 0.9 * sendBwAvg + 0.1 * kQRateTable[RateInd]; */ - bweStr->sendBwAvg = WEBRTC_SPL_UMUL(461, bweStr->sendBwAvg) + - WEBRTC_SPL_UMUL(51, WEBRTC_SPL_LSHIFT_U32(kQRateTable[RateInd], 7)); - bweStr->sendBwAvg = WEBRTC_SPL_RSHIFT_U32(bweStr->sendBwAvg, 9); - - - if (WEBRTC_SPL_RSHIFT_U32(bweStr->sendBwAvg, 7) > 28000 && !bweStr->highSpeedSend) { - bweStr->countHighSpeedSent++; - - /* approx 2 seconds with 30ms frames */ - if (bweStr->countHighSpeedSent >= 66) { - bweStr->highSpeedSend = 1; - } - } else if (!bweStr->highSpeedSend) { - bweStr->countHighSpeedSent = 0; - } - - return 0; -} - -/**************************************************************************** - * WebRtcIsacfix_GetDownlinkBwIndexImpl(...) - * - * This function calculates and returns the bandwidth/jitter estimation code - * (integer 0...23) to put in the sending iSAC payload. - * - * Input: - * - bweStr : BWE struct - * - * Return: - * bandwith and jitter index (0..23) - */ -WebRtc_UWord16 WebRtcIsacfix_GetDownlinkBwIndexImpl(BwEstimatorstr *bweStr) -{ - WebRtc_Word32 rate; - WebRtc_Word32 maxDelay; - WebRtc_UWord16 rateInd; - WebRtc_UWord16 maxDelayBit; - WebRtc_Word32 tempTerm1; - WebRtc_Word32 tempTerm2; - WebRtc_Word32 tempTermX; - WebRtc_Word32 tempTermY; - WebRtc_Word32 tempMin; - WebRtc_Word32 tempMax; - - /* Get Rate Index */ - - /* Get unquantized rate. Always returns 10000 <= rate <= 32000 */ - rate = WebRtcIsacfix_GetDownlinkBandwidth(bweStr); - - /* Compute the averaged BN estimate on this side */ - - /* recBwAvg = 0.9 * recBwAvg + 0.1 * (rate + bweStr->recHeaderRate), 0.9 and 0.1 in Q9 */ - bweStr->recBwAvg = WEBRTC_SPL_UMUL(922, bweStr->recBwAvg) + - WEBRTC_SPL_UMUL(102, WEBRTC_SPL_LSHIFT_U32((WebRtc_UWord32)rate + bweStr->recHeaderRate, 5)); - bweStr->recBwAvg = WEBRTC_SPL_RSHIFT_U32(bweStr->recBwAvg, 10); - - /* find quantization index that gives the closest rate after averaging */ - for (rateInd = 1; rateInd < 12; rateInd++) { - if (rate <= kQRateTable[rateInd]){ - break; - } - } - - /* find closest quantization index, and update quantized average by taking: */ - /* 0.9*recBwAvgQ + 0.1*kQRateTable[rateInd] */ - - /* 0.9 times recBwAvgQ in Q16 */ - /* 461/512 - 25/65536 =0.900009 */ - tempTerm1 = WEBRTC_SPL_MUL(bweStr->recBwAvgQ, 25); - tempTerm1 = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 7); - tempTermX = WEBRTC_SPL_UMUL(461, bweStr->recBwAvgQ) - tempTerm1; - - /* rate in Q16 */ - tempTermY = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)rate, 16); - - /* 0.1 * kQRateTable[rateInd] = KQRate01[rateInd] */ - tempTerm1 = tempTermX + KQRate01[rateInd] - tempTermY; - tempTerm2 = tempTermY - tempTermX - KQRate01[rateInd-1]; - - /* Compare (0.9 * recBwAvgQ + 0.1 * kQRateTable[rateInd] - rate) > - (rate - 0.9 * recBwAvgQ - 0.1 * kQRateTable[rateInd-1]) */ - if (tempTerm1 > tempTerm2) { - rateInd--; - } - - /* Update quantized average by taking: */ - /* 0.9*recBwAvgQ + 0.1*kQRateTable[rateInd] */ - - /* Add 0.1 times kQRateTable[rateInd], in Q16 */ - tempTermX += KQRate01[rateInd]; - - /* Shift back to Q7 */ - bweStr->recBwAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTermX, 9); - - /* Count consecutive received bandwidth above 28000 kbps (28000 in Q7 = 3584000) */ - /* If 66 high estimates in a row, set highSpeedRec to one */ - /* 66 corresponds to ~2 seconds in 30 msec mode */ - if ((bweStr->recBwAvgQ > 3584000) && !bweStr->highSpeedRec) { - bweStr->countHighSpeedRec++; - if (bweStr->countHighSpeedRec >= 66) { - bweStr->highSpeedRec = 1; - } - } else if (!bweStr->highSpeedRec) { - bweStr->countHighSpeedRec = 0; - } - - /* Get Max Delay Bit */ - - /* get unquantized max delay */ - maxDelay = WebRtcIsacfix_GetDownlinkMaxDelay(bweStr); - - /* Update quantized max delay average */ - tempMax = 652800; /* MAX_ISAC_MD * 0.1 in Q18 */ - tempMin = 130560; /* MIN_ISAC_MD * 0.1 in Q18 */ - tempTermX = WEBRTC_SPL_MUL((WebRtc_Word32)bweStr->recMaxDelayAvgQ, (WebRtc_Word32)461); - tempTermY = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)maxDelay, 18); - - tempTerm1 = tempTermX + tempMax - tempTermY; - tempTerm2 = tempTermY - tempTermX - tempMin; - - if ( tempTerm1 > tempTerm2) { - maxDelayBit = 0; - tempTerm1 = tempTermX + tempMin; - - /* update quantized average, shift back to Q9 */ - bweStr->recMaxDelayAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 9); - } else { - maxDelayBit = 12; - tempTerm1 = tempTermX + tempMax; - - /* update quantized average, shift back to Q9 */ - bweStr->recMaxDelayAvgQ = WEBRTC_SPL_RSHIFT_W32(tempTerm1, 9); - } - - /* Return bandwitdh and jitter index (0..23) */ - return (WebRtc_UWord16)(rateInd + maxDelayBit); -} - -/* get the bottle neck rate from far side to here, as estimated on this side */ -WebRtc_UWord16 WebRtcIsacfix_GetDownlinkBandwidth(const BwEstimatorstr *bweStr) -{ - WebRtc_UWord32 recBw; - WebRtc_Word32 jitter_sign; /* Q8 */ - WebRtc_Word32 bw_adjust; /* Q16 */ - WebRtc_Word32 rec_jitter_short_term_abs_inv; /* Q18 */ - WebRtc_Word32 temp; - - /* Q18 rec jitter short term abs is in Q13, multiply it by 2^13 to save precision - 2^18 then needs to be shifted 13 bits to 2^31 */ - rec_jitter_short_term_abs_inv = WEBRTC_SPL_UDIV(0x80000000, bweStr->recJitterShortTermAbs); - - /* Q27 = 9 + 18 */ - jitter_sign = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(bweStr->recJitterShortTerm, 4), (WebRtc_Word32)rec_jitter_short_term_abs_inv); - - if (jitter_sign < 0) { - temp = -jitter_sign; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 19); - jitter_sign = -temp; - } else { - jitter_sign = WEBRTC_SPL_RSHIFT_W32(jitter_sign, 19); - } - - /* adjust bw proportionally to negative average jitter sign */ - //bw_adjust = 1.0f - jitter_sign * (0.15f + 0.15f * jitter_sign * jitter_sign); - //Q8 -> Q16 .15 +.15 * jitter^2 first term is .15 in Q16 latter term is Q8*Q8*Q8 - //38 in Q8 ~.15 9830 in Q16 ~.15 - temp = 9830 + WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL(38, WEBRTC_SPL_MUL(jitter_sign, jitter_sign))), 8); - - if (jitter_sign < 0) { - temp = WEBRTC_SPL_MUL(jitter_sign, temp); - temp = -temp; - temp = WEBRTC_SPL_RSHIFT_W32(temp, 8); - bw_adjust = (WebRtc_UWord32)65536 + temp; /* (1 << 16) + temp; */ - } else { - bw_adjust = (WebRtc_UWord32)65536 - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(jitter_sign, temp), 8);/* (1 << 16) - ((jitter_sign * temp) >> 8); */ - } - - //make sure following multiplication won't overflow - //bw adjust now Q14 - bw_adjust = WEBRTC_SPL_RSHIFT_W32(bw_adjust, 2);//see if good resolution is maintained - - /* adjust Rate if jitter sign is mostly constant */ - recBw = WEBRTC_SPL_UMUL(bweStr->recBw, bw_adjust); - - recBw = WEBRTC_SPL_RSHIFT_W32(recBw, 14); - - /* limit range of bottle neck rate */ - if (recBw < MIN_ISAC_BW) { - recBw = MIN_ISAC_BW; - } else if (recBw > MAX_ISAC_BW) { - recBw = MAX_ISAC_BW; - } - - return (WebRtc_UWord16) recBw; -} - -/* Returns the mmax delay (in ms) */ -WebRtc_Word16 WebRtcIsacfix_GetDownlinkMaxDelay(const BwEstimatorstr *bweStr) -{ - WebRtc_Word16 recMaxDelay; - - recMaxDelay = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(bweStr->recMaxDelay, 15); - - /* limit range of jitter estimate */ - if (recMaxDelay < MIN_ISAC_MD) { - recMaxDelay = MIN_ISAC_MD; - } else if (recMaxDelay > MAX_ISAC_MD) { - recMaxDelay = MAX_ISAC_MD; - } - - return recMaxDelay; -} - -/* get the bottle neck rate from here to far side, as estimated by far side */ -WebRtc_Word16 WebRtcIsacfix_GetUplinkBandwidth(const BwEstimatorstr *bweStr) -{ - WebRtc_Word16 send_bw; - - send_bw = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_U32(bweStr->sendBwAvg, 7); - - /* limit range of bottle neck rate */ - if (send_bw < MIN_ISAC_BW) { - send_bw = MIN_ISAC_BW; - } else if (send_bw > MAX_ISAC_BW) { - send_bw = MAX_ISAC_BW; - } - - return send_bw; -} - - - -/* Returns the max delay value from the other side in ms */ -WebRtc_Word16 WebRtcIsacfix_GetUplinkMaxDelay(const BwEstimatorstr *bweStr) -{ - WebRtc_Word16 send_max_delay; - - send_max_delay = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(bweStr->sendMaxDelayAvg, 9); - - /* limit range of jitter estimate */ - if (send_max_delay < MIN_ISAC_MD) { - send_max_delay = MIN_ISAC_MD; - } else if (send_max_delay > MAX_ISAC_MD) { - send_max_delay = MAX_ISAC_MD; - } - - return send_max_delay; -} - - - - -/* - * update long-term average bitrate and amount of data in buffer - * returns minimum payload size (bytes) - */ -WebRtc_UWord16 WebRtcIsacfix_GetMinBytes(RateModel *State, - WebRtc_Word16 StreamSize, /* bytes in bitstream */ - const WebRtc_Word16 FrameSamples, /* samples per frame */ - const WebRtc_Word16 BottleNeck, /* bottle neck rate; excl headers (bps) */ - const WebRtc_Word16 DelayBuildUp) /* max delay from bottle neck buffering (ms) */ -{ - WebRtc_Word32 MinRate = 0; - WebRtc_UWord16 MinBytes; - WebRtc_Word16 TransmissionTime; - WebRtc_Word32 inv_Q12; - WebRtc_Word32 den; - - - /* first 10 packets @ low rate, then INIT_BURST_LEN packets @ fixed rate of INIT_RATE bps */ - if (State->InitCounter > 0) { - if (State->InitCounter-- <= INIT_BURST_LEN) { - MinRate = INIT_RATE; - } else { - MinRate = 0; - } - } else { - /* handle burst */ - if (State->BurstCounter) { - if (State->StillBuffered < WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL((512 - WEBRTC_SPL_DIV(512, BURST_LEN)), DelayBuildUp), 9)) { - /* max bps derived from BottleNeck and DelayBuildUp values */ - inv_Q12 = WEBRTC_SPL_DIV(4096, WEBRTC_SPL_MUL(BURST_LEN, FrameSamples)); - MinRate = WEBRTC_SPL_MUL(512 + WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(DelayBuildUp, inv_Q12), 3)), BottleNeck); - } else { - /* max bps derived from StillBuffered and DelayBuildUp values */ - inv_Q12 = WEBRTC_SPL_DIV(4096, FrameSamples); - if (DelayBuildUp > State->StillBuffered) { - MinRate = WEBRTC_SPL_MUL(512 + WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(DelayBuildUp - State->StillBuffered, inv_Q12), 3)), BottleNeck); - } else if ((den = WEBRTC_SPL_MUL(SAMPLES_PER_MSEC, (State->StillBuffered - DelayBuildUp))) >= FrameSamples) { - /* MinRate will be negative here */ - MinRate = 0; - } else { - MinRate = WEBRTC_SPL_MUL((512 - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(den, inv_Q12), 3)), BottleNeck); - } - //if (MinRate < 1.04 * BottleNeck) - // MinRate = 1.04 * BottleNeck; - //Q9 - if (MinRate < WEBRTC_SPL_MUL(532, BottleNeck)) { - MinRate += WEBRTC_SPL_MUL(22, BottleNeck); - } - } - - State->BurstCounter--; - } - } - - - /* convert rate from bits/second to bytes/packet */ - //round and shift before conversion - MinRate += 256; - MinRate = WEBRTC_SPL_RSHIFT_W32(MinRate, 9); - MinBytes = (WebRtc_UWord16)WEBRTC_SPL_UDIV(WEBRTC_SPL_MUL(MinRate, FrameSamples), FS8); - - /* StreamSize will be adjusted if less than MinBytes */ - if (StreamSize < MinBytes) { - StreamSize = MinBytes; - } - - /* keep track of when bottle neck was last exceeded by at least 1% */ - //517/512 ~ 1.01 - if (WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(StreamSize, FS8), FrameSamples) > (WEBRTC_SPL_MUL(517, BottleNeck) >> 9)) { - if (State->PrevExceed) { - /* bottle_neck exceded twice in a row, decrease ExceedAgo */ - State->ExceedAgo -= WEBRTC_SPL_DIV(BURST_INTERVAL, BURST_LEN - 1); - if (State->ExceedAgo < 0) { - State->ExceedAgo = 0; - } - } else { - State->ExceedAgo += (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ - State->PrevExceed = 1; - } - } else { - State->PrevExceed = 0; - State->ExceedAgo += (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ - } - - /* set burst flag if bottle neck not exceeded for long time */ - if ((State->ExceedAgo > BURST_INTERVAL) && (State->BurstCounter == 0)) { - if (State->PrevExceed) { - State->BurstCounter = BURST_LEN - 1; - } else { - State->BurstCounter = BURST_LEN; - } - } - - - /* Update buffer delay */ - TransmissionTime = (WebRtc_Word16)WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(StreamSize, 8000), BottleNeck); /* ms */ - State->StillBuffered += TransmissionTime; - State->StillBuffered -= (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); //>>4 = SAMPLES_PER_MSEC /* ms */ - if (State->StillBuffered < 0) { - State->StillBuffered = 0; - } - - if (State->StillBuffered > 2000) { - State->StillBuffered = 2000; - } - - return MinBytes; -} - - -/* - * update long-term average bitrate and amount of data in buffer - */ -void WebRtcIsacfix_UpdateRateModel(RateModel *State, - WebRtc_Word16 StreamSize, /* bytes in bitstream */ - const WebRtc_Word16 FrameSamples, /* samples per frame */ - const WebRtc_Word16 BottleNeck) /* bottle neck rate; excl headers (bps) */ -{ - WebRtc_Word16 TransmissionTime; - - /* avoid the initial "high-rate" burst */ - State->InitCounter = 0; - - /* Update buffer delay */ - TransmissionTime = (WebRtc_Word16)WEBRTC_SPL_DIV(WEBRTC_SPL_MUL(WEBRTC_SPL_MUL(StreamSize, 8), 1000), BottleNeck); /* ms */ - State->StillBuffered += TransmissionTime; - State->StillBuffered -= (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(FrameSamples, 4); /* ms */ - if (State->StillBuffered < 0) { - State->StillBuffered = 0; - } - -} - - -void WebRtcIsacfix_InitRateModel(RateModel *State) -{ - State->PrevExceed = 0; /* boolean */ - State->ExceedAgo = 0; /* ms */ - State->BurstCounter = 0; /* packets */ - State->InitCounter = INIT_BURST_LEN + 10; /* packets */ - State->StillBuffered = 1; /* ms */ -} - - - - - -WebRtc_Word16 WebRtcIsacfix_GetNewFrameLength(WebRtc_Word16 bottle_neck, WebRtc_Word16 current_framesamples) -{ - WebRtc_Word16 new_framesamples; - - new_framesamples = current_framesamples; - - /* find new framelength */ - switch(current_framesamples) { - case 480: - if (bottle_neck < Thld_30_60) { - new_framesamples = 960; - } - break; - case 960: - if (bottle_neck >= Thld_60_30) { - new_framesamples = 480; - } - break; - default: - new_framesamples = -1; /* Error */ - } - - return new_framesamples; -} - -WebRtc_Word16 WebRtcIsacfix_GetSnr(WebRtc_Word16 bottle_neck, WebRtc_Word16 framesamples) -{ - WebRtc_Word16 s2nr = 0; - - /* find new SNR value */ - //consider BottleNeck to be in Q10 ( * 1 in Q10) - switch(framesamples) { - case 480: - /*s2nr = -1*(a_30 << 10) + ((b_30 * bottle_neck) >> 10);*/ - s2nr = -22500 + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(500, bottle_neck, 10); //* 0.001; //+ c_30 * bottle_neck * bottle_neck * 0.000001; - break; - case 960: - /*s2nr = -1*(a_60 << 10) + ((b_60 * bottle_neck) >> 10);*/ - s2nr = -22500 + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(500, bottle_neck, 10); //* 0.001; //+ c_30 * bottle_neck * bottle_neck * 0.000001; - break; - default: - s2nr = -1; /* Error */ - } - - return s2nr; //return in Q10 - -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.h b/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.h deleted file mode 100644 index 76a50f823..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/bandwidth_estimator.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * bandwidth_estimator.h - * - * This header file contains the API for the Bandwidth Estimator - * designed for iSAC. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_BANDWIDTH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_BANDWIDTH_ESTIMATOR_H_ - -#include "structs.h" - - -/**************************************************************************** - * WebRtcIsacfix_InitBandwidthEstimator(...) - * - * This function initializes the struct for the bandwidth estimator - * - * Input/Output: - * - bwest_str : Struct containing bandwidth information. - * - * Return value : 0 - */ - -WebRtc_Word32 WebRtcIsacfix_InitBandwidthEstimator(BwEstimatorstr *bwest_str); - - -/**************************************************************************** - * WebRtcIsacfix_UpdateUplinkBwImpl(...) - * - * This function updates bottle neck rate received from other side in payload - * and calculates a new bottle neck to send to the other side. - * - * Input/Output: - * - bweStr : struct containing bandwidth information. - * - rtpNumber : value from RTP packet, from NetEq - * - frameSize : length of signal frame in ms, from iSAC decoder - * - sendTime : value in RTP header giving send time in samples - * - arrivalTime : value given by timeGetTime() time of arrival in - * samples of packet from NetEq - * - pksize : size of packet in bytes, from NetEq - * - Index : integer (range 0...23) indicating bottle neck & - * jitter as estimated by other side - * - * Return value : 0 if everything went fine, - * -1 otherwise - */ - -WebRtc_Word32 WebRtcIsacfix_UpdateUplinkBwImpl(BwEstimatorstr *bwest_str, - const WebRtc_UWord16 rtp_number, - const WebRtc_Word16 frameSize, - const WebRtc_UWord32 send_ts, - const WebRtc_UWord32 arr_ts, - const WebRtc_Word16 pksize, - const WebRtc_UWord16 Index); - -/* Update receiving estimates. Used when we only receive BWE index, no iSAC data packet. */ -WebRtc_Word16 WebRtcIsacfix_UpdateUplinkBwRec(BwEstimatorstr *bwest_str, - const WebRtc_Word16 Index); - -/**************************************************************************** - * WebRtcIsacfix_GetDownlinkBwIndexImpl(...) - * - * This function calculates and returns the bandwidth/jitter estimation code - * (integer 0...23) to put in the sending iSAC payload. - * - * Input: - * - bweStr : BWE struct - * - * Return: - * bandwith and jitter index (0..23) - */ -WebRtc_UWord16 WebRtcIsacfix_GetDownlinkBwIndexImpl(BwEstimatorstr *bwest_str); - -/* Returns the bandwidth estimation (in bps) */ -WebRtc_UWord16 WebRtcIsacfix_GetDownlinkBandwidth(const BwEstimatorstr *bwest_str); - -/* Returns the bandwidth that iSAC should send with in bps */ -WebRtc_Word16 WebRtcIsacfix_GetUplinkBandwidth(const BwEstimatorstr *bwest_str); - -/* Returns the max delay (in ms) */ -WebRtc_Word16 WebRtcIsacfix_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str); - -/* Returns the max delay value from the other side in ms */ -WebRtc_Word16 WebRtcIsacfix_GetUplinkMaxDelay(const BwEstimatorstr *bwest_str); - -/* - * update amount of data in bottle neck buffer and burst handling - * returns minimum payload size (bytes) - */ -WebRtc_UWord16 WebRtcIsacfix_GetMinBytes(RateModel *State, - WebRtc_Word16 StreamSize, /* bytes in bitstream */ - const WebRtc_Word16 FrameLen, /* ms per frame */ - const WebRtc_Word16 BottleNeck, /* bottle neck rate; excl headers (bps) */ - const WebRtc_Word16 DelayBuildUp); /* max delay from bottle neck buffering (ms) */ - -/* - * update long-term average bitrate and amount of data in buffer - */ -void WebRtcIsacfix_UpdateRateModel(RateModel *State, - WebRtc_Word16 StreamSize, /* bytes in bitstream */ - const WebRtc_Word16 FrameSamples, /* samples per frame */ - const WebRtc_Word16 BottleNeck); /* bottle neck rate; excl headers (bps) */ - - -void WebRtcIsacfix_InitRateModel(RateModel *State); - -/* Returns the new framelength value (input argument: bottle_neck) */ -WebRtc_Word16 WebRtcIsacfix_GetNewFrameLength(WebRtc_Word16 bottle_neck, WebRtc_Word16 current_framelength); - -/* Returns the new SNR value (input argument: bottle_neck) */ -//returns snr in Q10 -WebRtc_Word16 WebRtcIsacfix_GetSnr(WebRtc_Word16 bottle_neck, WebRtc_Word16 framesamples); - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_BANDWIDTH_ESTIMATOR_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/codec.h b/modules/audio_coding/codecs/iSAC/fix/source/codec.h deleted file mode 100644 index f852d9d33..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/codec.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * codec.h - * - * This header file contains the calls to the internal encoder - * and decoder functions. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_CODEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_CODEC_H_ - -#include "structs.h" - - -int WebRtcIsacfix_EstimateBandwidth(BwEstimatorstr *bwest_str, - Bitstr_dec *streamdata, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts); - -WebRtc_Word16 WebRtcIsacfix_DecodeImpl(WebRtc_Word16 *signal_out16, - ISACFIX_DecInst_t *ISACdec_obj, - WebRtc_Word16 *current_framesamples); - -WebRtc_Word16 WebRtcIsacfix_DecodePlcImpl(WebRtc_Word16 *decoded, - ISACFIX_DecInst_t *ISACdec_obj, - WebRtc_Word16 *current_framesample ); - -int WebRtcIsacfix_EncodeImpl(WebRtc_Word16 *in, - ISACFIX_EncInst_t *ISACenc_obj, - BwEstimatorstr *bw_estimatordata, - WebRtc_Word16 CodingMode); - -int WebRtcIsacfix_EncodeStoredData(ISACFIX_EncInst_t *ISACenc_obj, - int BWnumber, - float scale, - WebRtc_Word16 CodingMode); - -/************************** initialization functions *************************/ - -void WebRtcIsacfix_InitMaskingEnc(MaskFiltstr_enc *maskdata); -void WebRtcIsacfix_InitMaskingDec(MaskFiltstr_dec *maskdata); - -void WebRtcIsacfix_InitPreFilterbank(PreFiltBankstr *prefiltdata); - -void WebRtcIsacfix_InitPostFilterbank(PostFiltBankstr *postfiltdata); - -void WebRtcIsacfix_InitPitchFilter(PitchFiltstr *pitchfiltdata); - -void WebRtcIsacfix_InitPitchAnalysis(PitchAnalysisStruct *State); - -void WebRtcIsacfix_InitPlc( PLCstr *State ); - - -/**************************** transform functions ****************************/ - -void WebRtcIsacfix_InitTransform(); - - -void WebRtcIsacfix_Time2Spec(WebRtc_Word16 *inre1Q9, - WebRtc_Word16 *inre2Q9, - WebRtc_Word16 *outre, - WebRtc_Word16 *outim); - - - -void WebRtcIsacfix_Spec2Time(WebRtc_Word16 *inreQ7, - WebRtc_Word16 *inimQ7, - WebRtc_Word32 *outre1Q16, - WebRtc_Word32 *outre2Q16); - - - - -/***************************** filterbank functions **************************/ - - - -void WebRtcIsacfix_SplitAndFilter1(WebRtc_Word16 *in, - WebRtc_Word16 *LP16, - WebRtc_Word16 *HP16, - PreFiltBankstr *prefiltdata); - -void WebRtcIsacfix_FilterAndCombine1(WebRtc_Word16 *tempin_ch1, - WebRtc_Word16 *tempin_ch2, - WebRtc_Word16 *out16, - PostFiltBankstr *postfiltdata); - -#ifdef NB_CALLS - -void WebRtcIsacfix_SplitAndFilter2(WebRtc_Word16 *in, - WebRtc_Word16 *LP16, - WebRtc_Word16 *HP16, - PreFiltBankstr *prefiltdata); - -void WebRtcIsacfix_FilterAndCombine2(WebRtc_Word16 *tempin_ch1, - WebRtc_Word16 *tempin_ch2, - WebRtc_Word16 *out16, - PostFiltBankstr *postfiltdata, - WebRtc_Word16 len); - -#endif - -/************************* normalized lattice filters ************************/ - - -void WebRtcIsacfix_NormLatticeFilterMa(WebRtc_Word16 orderCoef, - WebRtc_Word32 *stateGQ15, - WebRtc_Word16 *lat_inQ0, - WebRtc_Word16 *filt_coefQ15, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 lo_hi, - WebRtc_Word16 *lat_outQ9); - - -void WebRtcIsacfix_NormLatticeFilterAr(WebRtc_Word16 orderCoef, - WebRtc_Word16 *stateGQ0, - WebRtc_Word32 *lat_inQ25, - WebRtc_Word16 *filt_coefQ15, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 lo_hi, - WebRtc_Word16 *lat_outQ0); - -int WebRtcIsacfix_AutocorrFix(WebRtc_Word32 *r, - const WebRtc_Word16 *x, - WebRtc_Word16 N, - WebRtc_Word16 order, - WebRtc_Word16 *scale); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_CODEC_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/decode.c b/modules/audio_coding/codecs/iSAC/fix/source/decode.c deleted file mode 100644 index 89d46ce37..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/decode.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * decode.c - * - * This C file contains the internal decoding function. - * - */ - -#include - -#include "bandwidth_estimator.h" -#include "codec.h" -#include "entropy_coding.h" -#include "pitch_estimator.h" -#include "settings.h" -#include "structs.h" - - - - -WebRtc_Word16 WebRtcIsacfix_DecodeImpl(WebRtc_Word16 *signal_out16, - ISACFIX_DecInst_t *ISACdec_obj, - WebRtc_Word16 *current_framesamples) -{ - int k; - int err; - WebRtc_Word16 BWno; - WebRtc_Word16 len = 0; - - WebRtc_Word16 model; - - - WebRtc_Word16 Vector_Word16_1[FRAMESAMPLES/2]; - WebRtc_Word16 Vector_Word16_2[FRAMESAMPLES/2]; - - WebRtc_Word32 Vector_Word32_1[FRAMESAMPLES/2]; - WebRtc_Word32 Vector_Word32_2[FRAMESAMPLES/2]; - - WebRtc_Word16 lofilt_coefQ15[ORDERLO*SUBFRAMES]; //refl. coeffs - WebRtc_Word16 hifilt_coefQ15[ORDERHI*SUBFRAMES]; //refl. coeffs - WebRtc_Word32 gain_lo_hiQ17[2*SUBFRAMES]; - - WebRtc_Word16 PitchLags_Q7[PITCH_SUBFRAMES]; - WebRtc_Word16 PitchGains_Q12[PITCH_SUBFRAMES]; - WebRtc_Word16 AvgPitchGain_Q12; - - WebRtc_Word16 tmp_1, tmp_2; - WebRtc_Word32 tmp32a, tmp32b; - WebRtc_Word16 gainQ13; - - - WebRtc_Word16 frame_nb; /* counter */ - WebRtc_Word16 frame_mode; /* 0 for 20ms and 30ms, 1 for 60ms */ - WebRtc_Word16 processed_samples; - - /* PLC */ - WebRtc_Word16 overlapWin[ 240 ]; - - (ISACdec_obj->bitstr_obj).W_upper = 0xFFFFFFFF; - (ISACdec_obj->bitstr_obj).streamval = 0; - (ISACdec_obj->bitstr_obj).stream_index = 0; - (ISACdec_obj->bitstr_obj).full = 1; - - - /* decode framelength and BW estimation - not used, only for stream pointer*/ - err = WebRtcIsacfix_DecodeFrameLen(&ISACdec_obj->bitstr_obj, current_framesamples); - if (err<0) // error check - return err; - - frame_mode = (WebRtc_Word16)WEBRTC_SPL_DIV(*current_framesamples, MAX_FRAMESAMPLES); /* 0, or 1 */ - processed_samples = (WebRtc_Word16)WEBRTC_SPL_DIV(*current_framesamples, frame_mode+1); /* either 320 (20ms) or 480 (30, 60 ms) */ - - err = WebRtcIsacfix_DecodeSendBandwidth(&ISACdec_obj->bitstr_obj, &BWno); - if (err<0) // error check - return err; - - /* one loop if it's one frame (20 or 30ms), 2 loops if 2 frames bundled together (60ms) */ - for (frame_nb = 0; frame_nb <= frame_mode; frame_nb++) { - - /* decode & dequantize pitch parameters */ - err = WebRtcIsacfix_DecodePitchGain(&(ISACdec_obj->bitstr_obj), PitchGains_Q12); - if (err<0) // error check - return err; - - err = WebRtcIsacfix_DecodePitchLag(&ISACdec_obj->bitstr_obj, PitchGains_Q12, PitchLags_Q7); - if (err<0) // error check - return err; - - AvgPitchGain_Q12 = (WebRtc_Word16)(((WebRtc_Word32)PitchGains_Q12[0] + PitchGains_Q12[1] + PitchGains_Q12[2] + PitchGains_Q12[3])>>2); - - /* decode & dequantize FiltCoef */ - err = WebRtcIsacfix_DecodeLpc(gain_lo_hiQ17, lofilt_coefQ15, hifilt_coefQ15, - &ISACdec_obj->bitstr_obj, &model); - - if (err<0) // error check - return err; - - /* decode & dequantize spectrum */ - len = WebRtcIsacfix_DecodeSpec(&ISACdec_obj->bitstr_obj, Vector_Word16_1, Vector_Word16_2, AvgPitchGain_Q12); - if (len < 0) // error check - return len; - - // Why does this need Q16 in and out? /JS - WebRtcIsacfix_Spec2Time(Vector_Word16_1, Vector_Word16_2, Vector_Word32_1, Vector_Word32_2); - - for (k=0; k Q9 - } - - /* ---- If this is recovery frame ---- */ - if( (ISACdec_obj->plcstr_obj).used == PLC_WAS_USED ) - { - (ISACdec_obj->plcstr_obj).used = PLC_NOT_USED; - if( (ISACdec_obj->plcstr_obj).B < 1000 ) - { - (ISACdec_obj->plcstr_obj).decayCoeffPriodic = 4000; - } - - ISACdec_obj->plcstr_obj.decayCoeffPriodic = WEBRTC_SPL_WORD16_MAX; /* DECAY_RATE is in Q15 */ - ISACdec_obj->plcstr_obj.decayCoeffNoise = WEBRTC_SPL_WORD16_MAX; /* DECAY_RATE is in Q15 */ - ISACdec_obj->plcstr_obj.pitchCycles = 0; - - PitchGains_Q12[0] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(PitchGains_Q12[0], 700, 10 ); - - /* ---- Add-overlap ---- */ - WebRtcSpl_GetHanningWindow( overlapWin, RECOVERY_OVERLAP ); - for( k = 0; k < RECOVERY_OVERLAP; k++ ) - Vector_Word16_1[k] = WEBRTC_SPL_ADD_SAT_W16( - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( (ISACdec_obj->plcstr_obj).overlapLP[k], overlapWin[RECOVERY_OVERLAP - k - 1], 14), - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( Vector_Word16_1[k], overlapWin[k], 14) ); - - - - } - - /* --- Store side info --- */ - if( frame_nb == frame_mode ) - { - /* --- LPC info */ - WEBRTC_SPL_MEMCPY_W16( (ISACdec_obj->plcstr_obj).lofilt_coefQ15, &lofilt_coefQ15[(SUBFRAMES-1)*ORDERLO], ORDERLO ); - WEBRTC_SPL_MEMCPY_W16( (ISACdec_obj->plcstr_obj).hifilt_coefQ15, &hifilt_coefQ15[(SUBFRAMES-1)*ORDERHI], ORDERHI ); - (ISACdec_obj->plcstr_obj).gain_lo_hiQ17[0] = gain_lo_hiQ17[(SUBFRAMES-1) * 2]; - (ISACdec_obj->plcstr_obj).gain_lo_hiQ17[1] = gain_lo_hiQ17[(SUBFRAMES-1) * 2 + 1]; - - /* --- LTP info */ - (ISACdec_obj->plcstr_obj).AvgPitchGain_Q12 = PitchGains_Q12[3]; - (ISACdec_obj->plcstr_obj).lastPitchGain_Q12 = PitchGains_Q12[3]; - (ISACdec_obj->plcstr_obj).lastPitchLag_Q7 = PitchLags_Q7[3]; - - if( PitchLags_Q7[3] < 3000 ) - (ISACdec_obj->plcstr_obj).lastPitchLag_Q7 += PitchLags_Q7[3]; - - WEBRTC_SPL_MEMCPY_W16( (ISACdec_obj->plcstr_obj).prevPitchInvIn, Vector_Word16_1, FRAMESAMPLES/2 ); - - } - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - - /* inverse pitch filter */ - WebRtcIsacfix_PitchFilter(Vector_Word16_1, Vector_Word16_2, &ISACdec_obj->pitchfiltstr_obj, PitchLags_Q7, PitchGains_Q12, 4); - - if( frame_nb == frame_mode ) - { - WEBRTC_SPL_MEMCPY_W16( (ISACdec_obj->plcstr_obj).prevPitchInvOut, &(Vector_Word16_2[FRAMESAMPLES/2 - (PITCH_MAX_LAG + 10)]), PITCH_MAX_LAG ); - } - - - /* reduce gain to compensate for pitch enhancer */ - /* gain = 1.0f - 0.45f * AvgPitchGain; */ - tmp32a = WEBRTC_SPL_MUL_16_16_RSFT(AvgPitchGain_Q12, 29, 0); // Q18 - tmp32b = 262144 - tmp32a; // Q18 - gainQ13 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp32b, 5); // Q13 - - for (k = 0; k < FRAMESAMPLES/2; k++) - { - Vector_Word32_1[k] = (WebRtc_Word32) WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(Vector_Word16_2[k], gainQ13), 3); // Q25 - } - - - /* perceptual post-filtering (using normalized lattice filter) */ - WebRtcIsacfix_NormLatticeFilterAr(ORDERLO, (ISACdec_obj->maskfiltstr_obj).PostStateLoGQ0, - Vector_Word32_1, lofilt_coefQ15, gain_lo_hiQ17, 0, Vector_Word16_1); - - /* --- Store Highpass Residual --- */ - for (k = 0; k < FRAMESAMPLES/2; k++) - Vector_Word32_1[k] = WEBRTC_SPL_LSHIFT_W32(Vector_Word32_2[k], 9); // Q16 -> Q25 - - for( k = 0; k < PITCH_MAX_LAG + 10; k++ ) - (ISACdec_obj->plcstr_obj).prevHP[k] = Vector_Word32_1[FRAMESAMPLES/2 - (PITCH_MAX_LAG + 10) + k]; - - - WebRtcIsacfix_NormLatticeFilterAr(ORDERHI, (ISACdec_obj->maskfiltstr_obj).PostStateHiGQ0, - Vector_Word32_1, hifilt_coefQ15, gain_lo_hiQ17, 1, Vector_Word16_2); - - /* recombine the 2 bands */ - - /* Form the polyphase signals, and compensate for DC offset */ - for (k=0;kpostfiltbankstr_obj); - - } - return len; -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/decode_bwe.c b/modules/audio_coding/codecs/iSAC/fix/source/decode_bwe.c deleted file mode 100644 index 68c600306..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/decode_bwe.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * decode_bwe.c - * - * This C file contains the internal decode bandwidth estimate function. - * - */ - - -#include "bandwidth_estimator.h" -#include "codec.h" -#include "entropy_coding.h" -#include "structs.h" - - - - -int WebRtcIsacfix_EstimateBandwidth(BwEstimatorstr *bwest_str, - Bitstr_dec *streamdata, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts) -{ - WebRtc_Word16 index; - WebRtc_Word16 frame_samples; - int err; - - /* decode framelength */ - err = WebRtcIsacfix_DecodeFrameLen(streamdata, &frame_samples); - /* error check */ - if (err<0) { - return err; - } - - /* decode BW estimation */ - err = WebRtcIsacfix_DecodeSendBandwidth(streamdata, &index); - /* error check */ - if (err<0) { - return err; - } - - /* Update BWE with received data */ - err = WebRtcIsacfix_UpdateUplinkBwImpl( - bwest_str, - rtp_seq_number, - (WebRtc_UWord16)WEBRTC_SPL_UDIV(WEBRTC_SPL_UMUL(frame_samples,1000), FS), - send_ts, - arr_ts, - (WebRtc_Word16) packet_size, /* in bytes */ - index); - - /* error check */ - if (err<0) { - return err; - } - - /* Succesful */ - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/decode_plc.c b/modules/audio_coding/codecs/iSAC/fix/source/decode_plc.c deleted file mode 100644 index 8a96b3597..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/decode_plc.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * decode_plc.c - * - * Packet Loss Concealment. - * - */ - -#include - -#include "settings.h" -#include "entropy_coding.h" -#include "pitch_estimator.h" -#include "bandwidth_estimator.h" -#include "structs.h" -#include "codec.h" - - -#define NO_OF_PRIMES 8 -#define NOISE_FILTER_LEN 30 - -/* - * function to decode the bitstream - * returns the total number of bytes in the stream - */ - -WebRtc_Word16 plc_filterma_Fast( - WebRtc_Word16 *In, /* (i) Vector to be filtered. InOut[-orderCoef+1] - to InOut[-1] contains state */ - WebRtc_Word16 *Out, /* (o) Filtered vector */ - WebRtc_Word16 *B, /* (i) The filter coefficients (in Q0) */ - WebRtc_Word16 Blen, /* (i) Number of B coefficients */ - WebRtc_Word16 len, /* (i) Number of samples to be filtered */ - WebRtc_Word16 reduceDecay, - WebRtc_Word16 decay, - WebRtc_Word16 rshift ) -{ - int i, j; - WebRtc_Word32 o; - WebRtc_Word32 lim; - - lim = WEBRTC_SPL_LSHIFT_W32( (WebRtc_Word32)1, 15 + rshift )-1; - - for (i = 0; i < len; i++) - { - G_CONST WebRtc_Word16 *b_ptr = &B[0]; - G_CONST WebRtc_Word16 *x_ptr = &In[i]; - - o = (WebRtc_Word32)0; - - for (j = 0;j < Blen; j++) - { - o = WEBRTC_SPL_ADD_SAT_W32( o, WEBRTC_SPL_MUL_16_16( *b_ptr, *x_ptr) ); - b_ptr++; - x_ptr--; - } - - /* to round off correctly */ - o = WEBRTC_SPL_ADD_SAT_W32( o, WEBRTC_SPL_LSHIFT_W32( 1, (rshift-1) ) ); - - /* saturate according to the domain of the filter coefficients */ - o = WEBRTC_SPL_SAT((WebRtc_Word32)lim, o, (WebRtc_Word32)-lim); - - /* o should be in the range of WebRtc_Word16 */ - o = WEBRTC_SPL_RSHIFT_W32( o, rshift ); - - /* decay the output signal; this is specific to plc */ - *Out++ = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16)o, decay, 15); // ((o + (WebRtc_Word32)2048) >> 12); - - /* change the decay */ - decay -= reduceDecay; - if( decay < 0 ) - decay = 0; - } - return( decay ); -} - - - - - - - - -static __inline WebRtc_Word32 log2_Q8_T( WebRtc_UWord32 x ) { - - WebRtc_Word32 zeros, lg2; - WebRtc_Word16 frac; - - zeros=WebRtcSpl_NormU32(x); - frac=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(((WebRtc_UWord32)WEBRTC_SPL_LSHIFT_W32(x, zeros)&0x7FFFFFFF), 23); - /* log2(magn(i)) */ - - lg2= (WEBRTC_SPL_LSHIFT_W16((31-zeros), 8)+frac); - return lg2; - -} - -static __inline WebRtc_Word16 exp2_Q10_T(WebRtc_Word16 x) { // Both in and out in Q10 - - WebRtc_Word16 tmp16_1, tmp16_2; - - tmp16_2=(WebRtc_Word16)(0x0400|(x&0x03FF)); - tmp16_1=-(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(x,10); - if(tmp16_1>0) - return (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); - else - return (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); - -} - - -/* - This is a fixed-point version of the above code with limLow = 700 and limHigh = 5000, - hard-coded. The values 700 and 5000 were experimentally obtained. - - The function implements membership values for two sets. The mebership functions are - of second orders corresponding to half-bell-shapped pulses. -*/ -static void MemshipValQ15( WebRtc_Word16 in, WebRtc_Word16 *A, WebRtc_Word16 *B ) -{ - WebRtc_Word16 x; - - in -= 700; /* translate the lowLim to 0, limHigh = 5000 - 700, M = 2150 */ - - if( in <= 2150 ) - { - if( in > 0 ) - { - /* b = in^2 / (2 * M^2), a = 1 - b in Q0. - We have to compute in Q15 */ - - /* x = in / 2150 {in Q15} = x * 15.2409 {in Q15} = - x*15 + (x*983)/(2^12); note that 983/2^12 = 0.23999 */ - - /* we are sure that x is in the range of WebRtc_Word16 */ - x = (WebRtc_Word16)( WEBRTC_SPL_MUL_16_16( in, 15 ) + - WEBRTC_SPL_MUL_16_16_RSFT( in, 983, 12) ); - /* b = x^2 / 2 {in Q15} so a shift of 16 is required to - be in correct domain and one more for the division by 2 */ - *B = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32( WEBRTC_SPL_MUL_16_16( x, x ) + 0x00010000, 17 ); - *A = WEBRTC_SPL_WORD16_MAX - *B; - } - else - { - *B = 0; - *A = WEBRTC_SPL_WORD16_MAX; - } - } - else - { - if( in < 4300 ) - { - /* This is a mirror case of the above */ - in = 4300 - in; - x = (WebRtc_Word16)( WEBRTC_SPL_MUL_16_16( in, 15 ) + - WEBRTC_SPL_MUL_16_16_RSFT( in, 983, 12) ); - /* b = x^2 / 2 {in Q15} so a shift of 16 is required to - be in correct domain and one more for the division by 2 */ - *A = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32( WEBRTC_SPL_MUL_16_16( x, x ) + 0x00010000, 17 ); - *B = WEBRTC_SPL_WORD16_MAX - *A; - - } - else - { - *A = 0; - *B = WEBRTC_SPL_WORD16_MAX; - } - } -} - - - - -static void LinearResampler( WebRtc_Word16 *in, WebRtc_Word16 *out, WebRtc_Word16 lenIn, WebRtc_Word16 lenOut ) -{ - WebRtc_Word32 n; - WebRtc_Word16 resOut, i, j, relativePos, diff; /* */ - WebRtc_UWord16 udiff; - - if( lenIn == lenOut ) - { - WEBRTC_SPL_MEMCPY_W16( out, in, lenIn ); - return; - } - - n = WEBRTC_SPL_MUL_16_16( (WebRtc_Word16)(lenIn-1), RESAMP_RES ); - resOut = WebRtcSpl_DivW32W16ResW16( n, (WebRtc_Word16)(lenOut-1) ); - - out[0] = in[0]; - for( i = 1, j = 0, relativePos = 0; i < lenOut; i++ ) - { - - relativePos += resOut; - while( relativePos > RESAMP_RES ) - { - j++; - relativePos -= RESAMP_RES; - } - - - /* an overflow may happen and the differce in sample values may - * require more than 16 bits. We like to avoid 32 bit arithmatic - * as much as possible */ - - if( (in[ j ] > 0) && (in[j + 1] < 0) ) - { - udiff = (WebRtc_UWord16)(in[ j ] - in[j + 1]); - out[ i ] = in[ j ] - (WebRtc_UWord16)( ((WebRtc_Word32)( udiff * relativePos )) >> RESAMP_RES_BIT); - } - else - { - if( (in[j] < 0) && (in[j+1] > 0) ) - { - udiff = (WebRtc_UWord16)( in[j + 1] - in[ j ] ); - out[ i ] = in[ j ] + (WebRtc_UWord16)( ((WebRtc_Word32)( udiff * relativePos )) >> RESAMP_RES_BIT); - } - else - { - diff = in[ j + 1 ] - in[ j ]; - out[ i ] = in[ j ] + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( diff, relativePos, RESAMP_RES_BIT ); - } - } - } -} - - - - - -WebRtc_Word16 WebRtcIsacfix_DecodePlcImpl(WebRtc_Word16 *signal_out16, - ISACFIX_DecInst_t *ISACdec_obj, - WebRtc_Word16 *current_framesamples ) -{ - int subframecnt; - WebRtc_Word16 len = 0; - - WebRtc_Word16* Vector_Word16_1; - WebRtc_Word16 Vector_Word16_Extended_1[FRAMESAMPLES_HALF + NOISE_FILTER_LEN]; - WebRtc_Word16* Vector_Word16_2; - WebRtc_Word16 Vector_Word16_Extended_2[FRAMESAMPLES_HALF + NOISE_FILTER_LEN]; - - WebRtc_Word32 Vector_Word32_1[FRAMESAMPLES_HALF]; - WebRtc_Word32 Vector_Word32_2[FRAMESAMPLES_HALF]; - - WebRtc_Word16 lofilt_coefQ15[ORDERLO*SUBFRAMES]; //refl. coeffs - WebRtc_Word16 hifilt_coefQ15[ORDERHI*SUBFRAMES]; //refl. coeffs - - WebRtc_Word16 pitchLags_Q7[PITCH_SUBFRAMES]; - WebRtc_Word16 pitchGains_Q12[PITCH_SUBFRAMES]; - - WebRtc_Word16 tmp_1, tmp_2; - WebRtc_Word32 tmp32a, tmp32b; - WebRtc_Word16 gainQ13; - - WebRtc_Word16 myDecayRate; - - /* ---------- PLC variables ------------ */ - WebRtc_Word16 lag0, i, k, noiseIndex; - WebRtc_Word16 stretchPitchLP[PITCH_MAX_LAG + 10], stretchPitchLP1[PITCH_MAX_LAG + 10]; - - WebRtc_Word32 gain_lo_hiQ17[2*SUBFRAMES]; - - WebRtc_Word16 nLP, pLP, wNoisyLP, wPriodicLP, tmp16, minIdx; - WebRtc_Word32 nHP, pHP, wNoisyHP, wPriodicHP, corr, minCorr, maxCoeff; - WebRtc_Word16 noise1, rshift; - - - WebRtc_Word16 ltpGain, pitchGain, myVoiceIndicator, myAbs, maxAbs; - WebRtc_Word32 varIn, varOut, logVarIn, logVarOut, Q, logMaxAbs; - int rightShiftIn, rightShiftOut; - - - /* ------------------------------------- */ - - - myDecayRate = (DECAY_RATE); - Vector_Word16_1 = &Vector_Word16_Extended_1[NOISE_FILTER_LEN]; - Vector_Word16_2 = &Vector_Word16_Extended_2[NOISE_FILTER_LEN]; - - - /* ----- Simply Copy Previous LPC parameters ------ */ - for( subframecnt = 0; subframecnt < SUBFRAMES; subframecnt++ ) - { - /* lower Band */ - WEBRTC_SPL_MEMCPY_W16(&lofilt_coefQ15[ subframecnt * ORDERLO ], - (ISACdec_obj->plcstr_obj).lofilt_coefQ15, ORDERLO); - gain_lo_hiQ17[2*subframecnt] = (ISACdec_obj->plcstr_obj).gain_lo_hiQ17[0]; - - /* Upper Band */ - WEBRTC_SPL_MEMCPY_W16(&hifilt_coefQ15[ subframecnt * ORDERHI ], - (ISACdec_obj->plcstr_obj).hifilt_coefQ15, ORDERHI); - gain_lo_hiQ17[2*subframecnt + 1] = (ISACdec_obj->plcstr_obj).gain_lo_hiQ17[1]; - } - - - - - lag0 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).lastPitchLag_Q7 + 64, 7 ) + 1; - - - if( (ISACdec_obj->plcstr_obj).used != PLC_WAS_USED ) - { - (ISACdec_obj->plcstr_obj).pitchCycles = 0; - - (ISACdec_obj->plcstr_obj).lastPitchLP = - &((ISACdec_obj->plcstr_obj).prevPitchInvIn[FRAMESAMPLES_HALF - lag0]); - minCorr = WEBRTC_SPL_WORD32_MAX; - - if ( (FRAMESAMPLES_HALF - 2*lag0 - 10) > 0 ) - { - minIdx = 11; - for( i = 0; i < 21; i++ ) - { - corr = 0; - for( k = 0; k < lag0; k++ ) - { - corr = WEBRTC_SPL_ADD_SAT_W32( corr, WEBRTC_SPL_ABS_W32( - WEBRTC_SPL_SUB_SAT_W16( - (ISACdec_obj->plcstr_obj).lastPitchLP[k], - (ISACdec_obj->plcstr_obj).prevPitchInvIn[ - FRAMESAMPLES_HALF - 2*lag0 - 10 + i + k ] ) ) ); - } - if( corr < minCorr ) - { - minCorr = corr; - minIdx = i; - } - } - (ISACdec_obj->plcstr_obj).prevPitchLP = - &( (ISACdec_obj->plcstr_obj).prevPitchInvIn[ - FRAMESAMPLES_HALF - lag0*2 - 10 + minIdx] ); - } - else - { - (ISACdec_obj->plcstr_obj).prevPitchLP = - (ISACdec_obj->plcstr_obj).lastPitchLP; - } - pitchGain = (ISACdec_obj->plcstr_obj).lastPitchGain_Q12; - - WebRtcSpl_AutoCorrelation( - &(ISACdec_obj->plcstr_obj).prevPitchInvIn[FRAMESAMPLES_HALF - lag0], - lag0, 0, &varIn, &rightShiftIn); - WebRtcSpl_AutoCorrelation( - &(ISACdec_obj->plcstr_obj).prevPitchInvOut[PITCH_MAX_LAG + 10 - lag0], - lag0, 0, &varOut, &rightShiftOut); - - maxAbs = 0; - for( i = 0; i< lag0; i++) - { - myAbs = WEBRTC_SPL_ABS_W16( - (ISACdec_obj->plcstr_obj).prevPitchInvOut[ - PITCH_MAX_LAG + 10 - lag0 + i] ); - maxAbs = (myAbs > maxAbs)? myAbs:maxAbs; - } - logVarIn = log2_Q8_T( (WebRtc_UWord32)( varIn ) ) + - (WebRtc_Word32)(rightShiftIn << 8); - logVarOut = log2_Q8_T( (WebRtc_UWord32)( varOut ) ) + - (WebRtc_Word32)(rightShiftOut << 8); - logMaxAbs = log2_Q8_T( (WebRtc_UWord32)( maxAbs ) ); - - ltpGain = (WebRtc_Word16)(logVarOut - logVarIn); - Q = 2 * logMaxAbs - ( logVarOut - 1512 ); - - /* - * --- - * We are computing sqrt( (VarIn/lag0) / var( noise ) ) - * var( noise ) is almost 256. we have already computed log2( VarIn ) in Q8 - * so we actually compute 2^( 0.5*(log2( VarIn ) - log2( lag0 ) - log2( var(noise ) ) ). - * Note that put log function is in Q8 but the exponential function is in Q10. - * -- - */ - - logVarIn -= log2_Q8_T( (WebRtc_UWord32)( lag0 ) ); - tmp16 = (WebRtc_Word16)((logVarIn<<1) - (4<<10) ); - rightShiftIn = 0; - if( tmp16 > 4096 ) - { - tmp16 -= 4096; - tmp16 = exp2_Q10_T( tmp16 ); - tmp16 >>= 6; - } - else - tmp16 = exp2_Q10_T( tmp16 )>>10; - - (ISACdec_obj->plcstr_obj).std = tmp16 - 4; - - if( (ltpGain < 110) || (ltpGain > 230) ) - { - if( ltpGain < 100 && (pitchGain < 1800) ) - { - (ISACdec_obj->plcstr_obj).A = WEBRTC_SPL_WORD16_MAX; - } - else - { - (ISACdec_obj->plcstr_obj).A = ((ltpGain < 110) && (Q < 800) - )? WEBRTC_SPL_WORD16_MAX:0; - } - (ISACdec_obj->plcstr_obj).B = WEBRTC_SPL_WORD16_MAX - - (ISACdec_obj->plcstr_obj).A; - } - else - { - if( (pitchGain < 450) || (pitchGain > 1600) ) - { - (ISACdec_obj->plcstr_obj).A = ((pitchGain < 450) - )? WEBRTC_SPL_WORD16_MAX:0; - (ISACdec_obj->plcstr_obj).B = WEBRTC_SPL_WORD16_MAX - - (ISACdec_obj->plcstr_obj).A; - } - else - { - myVoiceIndicator = ltpGain * 2 + pitchGain; - MemshipValQ15( myVoiceIndicator, - &(ISACdec_obj->plcstr_obj).A, &(ISACdec_obj->plcstr_obj).B ); - } - } - - - - myVoiceIndicator = ltpGain * 16 + pitchGain * 2 + (pitchGain >> 8); - MemshipValQ15( myVoiceIndicator, - &(ISACdec_obj->plcstr_obj).A, &(ISACdec_obj->plcstr_obj).B ); - - - - (ISACdec_obj->plcstr_obj).stretchLag = lag0; - (ISACdec_obj->plcstr_obj).pitchIndex = 0; - - } - else - { - myDecayRate = (DECAY_RATE<<2); - } - - if( (ISACdec_obj->plcstr_obj).B < 1000 ) - { - myDecayRate += (DECAY_RATE<<3); - } - - /* ------------ reconstructing the residual signal ------------------ */ - - LinearResampler( (ISACdec_obj->plcstr_obj).lastPitchLP, - stretchPitchLP, lag0, (ISACdec_obj->plcstr_obj).stretchLag ); - /* inverse pitch filter */ - - pitchLags_Q7[0] = pitchLags_Q7[1] = pitchLags_Q7[2] = pitchLags_Q7[3] = - ((ISACdec_obj->plcstr_obj).stretchLag<<7); - pitchGains_Q12[3] = ( (ISACdec_obj->plcstr_obj).lastPitchGain_Q12); - pitchGains_Q12[2] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - pitchGains_Q12[3], 1010, 10 ); - pitchGains_Q12[1] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - pitchGains_Q12[2], 1010, 10 ); - pitchGains_Q12[0] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - pitchGains_Q12[1], 1010, 10 ); - - - /* most of the time either B or A are zero so seperating */ - if( (ISACdec_obj->plcstr_obj).B == 0 ) - { - for( i = 0; i < FRAMESAMPLES_HALF; i++ ) - { - /* --- Low Pass */ - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_1[i] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; - - /* --- Highpass */ - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_2[i] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; - - } - for( i = 1; i < NOISE_FILTER_LEN; i++ ) - { - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_Extended_1[ i ] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; - - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - Vector_Word16_Extended_2[ i ] = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; - } - plc_filterma_Fast(Vector_Word16_1, Vector_Word16_Extended_1, - &(ISACdec_obj->plcstr_obj).prevPitchInvIn[FRAMESAMPLES_HALF - - NOISE_FILTER_LEN], (WebRtc_Word16) NOISE_FILTER_LEN, - (WebRtc_Word16) FRAMESAMPLES_HALF, (WebRtc_Word16)(5), - (ISACdec_obj->plcstr_obj).decayCoeffNoise, (WebRtc_Word16)(6)); - - maxCoeff = WebRtcSpl_MaxAbsValueW32( - &(ISACdec_obj->plcstr_obj).prevHP[ - PITCH_MAX_LAG + 10 - NOISE_FILTER_LEN], NOISE_FILTER_LEN ); - - rshift = 0; - while( maxCoeff > WEBRTC_SPL_WORD16_MAX ) - { - maxCoeff = WEBRTC_SPL_RSHIFT_W32(maxCoeff, 1); - rshift++; - } - for( i = 0; i < NOISE_FILTER_LEN; i++ ) { - Vector_Word16_1[ FRAMESAMPLES_HALF - NOISE_FILTER_LEN + i] = - (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32( - (ISACdec_obj->plcstr_obj).prevHP[ - PITCH_MAX_LAG + 10 - NOISE_FILTER_LEN + i], rshift); - } - (ISACdec_obj->plcstr_obj).decayCoeffNoise = plc_filterma_Fast( - Vector_Word16_2, - Vector_Word16_Extended_2, - &Vector_Word16_1[FRAMESAMPLES_HALF - NOISE_FILTER_LEN], - (WebRtc_Word16) NOISE_FILTER_LEN, - (WebRtc_Word16) FRAMESAMPLES_HALF, - (WebRtc_Word16) (5), - (ISACdec_obj->plcstr_obj).decayCoeffNoise, - (WebRtc_Word16) (7) ); - - for( i = 0; i < FRAMESAMPLES_HALF; i++ ) - Vector_Word32_2[i] = WEBRTC_SPL_LSHIFT_W32( - (WebRtc_Word32)Vector_Word16_Extended_2[i], rshift ); - - Vector_Word16_1 = Vector_Word16_Extended_1; - } - else - { - if( (ISACdec_obj->plcstr_obj).A == 0 ) - { - /* ------ Periodic Vector --- */ - for( i = 0, noiseIndex = 0; i < FRAMESAMPLES_HALF; i++, noiseIndex++ ) - { - /* --- Lowpass */ - pLP = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - stretchPitchLP[(ISACdec_obj->plcstr_obj).pitchIndex], - (ISACdec_obj->plcstr_obj).decayCoeffPriodic, 15 ); - - /* --- Highpass */ - pHP = (WebRtc_Word32)WEBRTC_SPL_MUL_16_32_RSFT15( - (ISACdec_obj->plcstr_obj).decayCoeffPriodic, - (ISACdec_obj->plcstr_obj).prevHP[PITCH_MAX_LAG + 10 - - (ISACdec_obj->plcstr_obj).stretchLag + - (ISACdec_obj->plcstr_obj).pitchIndex] ); - - /* --- lower the muliplier (more decay at next sample) --- */ - (ISACdec_obj->plcstr_obj).decayCoeffPriodic -= (myDecayRate); - if( (ISACdec_obj->plcstr_obj).decayCoeffPriodic < 0 ) - (ISACdec_obj->plcstr_obj).decayCoeffPriodic = 0; - - (ISACdec_obj->plcstr_obj).pitchIndex++; - - if( (ISACdec_obj->plcstr_obj).pitchIndex == - (ISACdec_obj->plcstr_obj).stretchLag ) - { - (ISACdec_obj->plcstr_obj).pitchIndex = 0; - (ISACdec_obj->plcstr_obj).pitchCycles++; - - if( (ISACdec_obj->plcstr_obj).stretchLag != (lag0 + 1) ) - { - (ISACdec_obj->plcstr_obj).stretchLag = lag0 + 1; - } - else - { - (ISACdec_obj->plcstr_obj).stretchLag = lag0; - } - - (ISACdec_obj->plcstr_obj).stretchLag = ( - (ISACdec_obj->plcstr_obj).stretchLag > PITCH_MAX_LAG - )? (PITCH_MAX_LAG):(ISACdec_obj->plcstr_obj).stretchLag; - - LinearResampler( (ISACdec_obj->plcstr_obj).lastPitchLP, - stretchPitchLP, lag0, (ISACdec_obj->plcstr_obj).stretchLag ); - - LinearResampler( (ISACdec_obj->plcstr_obj).prevPitchLP, - stretchPitchLP1, lag0, (ISACdec_obj->plcstr_obj).stretchLag ); - - switch( (ISACdec_obj->plcstr_obj).pitchCycles ) - { - case 1: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)(( - (WebRtc_Word32)stretchPitchLP[k]* 3 + - (WebRtc_Word32)stretchPitchLP1[k])>>2); - } - break; - } - case 2: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)(( - (WebRtc_Word32)stretchPitchLP[k] + - (WebRtc_Word32)stretchPitchLP1[k] )>>1); - } - break; - } - case 3: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)((stretchPitchLP[k] + - (WebRtc_Word32)stretchPitchLP1[k]*3 )>>2); - } - break; - } - } - - if( (ISACdec_obj->plcstr_obj).pitchCycles == 3 ) - { - myDecayRate += 35; //(myDecayRate>>1); - (ISACdec_obj->plcstr_obj).pitchCycles = 0; - } - - } - - /* ------ Sum the noisy and periodic signals ------ */ - Vector_Word16_1[i] = pLP; - Vector_Word32_2[i] = pHP; - } - } - else - { - for( i = 0, noiseIndex = 0; i < FRAMESAMPLES_HALF; i++, noiseIndex++ ) - { - - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - - noise1 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 10 ) - 16; - - nLP = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - (WebRtc_Word16)((noise1)*(ISACdec_obj->plcstr_obj).std), - (ISACdec_obj->plcstr_obj).decayCoeffNoise, 15 ); - - /* --- Highpass */ - (ISACdec_obj->plcstr_obj).seed = WEBRTC_SPL_RAND( - (ISACdec_obj->plcstr_obj).seed ); - noise1 = WEBRTC_SPL_RSHIFT_W16( - (ISACdec_obj->plcstr_obj).seed, 11 ) - 8; - - nHP = (WebRtc_Word32)WEBRTC_SPL_MUL_16_32_RSFT15( - (ISACdec_obj->plcstr_obj).decayCoeffNoise, - (WebRtc_Word32)(noise1*(ISACdec_obj->plcstr_obj).std) ); - - /* --- lower the muliplier (more decay at next sample) --- */ - (ISACdec_obj->plcstr_obj).decayCoeffNoise -= (myDecayRate); - if( (ISACdec_obj->plcstr_obj).decayCoeffNoise < 0 ) - (ISACdec_obj->plcstr_obj).decayCoeffNoise = 0; - - /* ------ Periodic Vector --- */ - /* --- Lowpass */ - pLP = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - stretchPitchLP[(ISACdec_obj->plcstr_obj).pitchIndex], - (ISACdec_obj->plcstr_obj).decayCoeffPriodic, 15 ); - - /* --- Highpass */ - pHP = (WebRtc_Word32)WEBRTC_SPL_MUL_16_32_RSFT15( - (ISACdec_obj->plcstr_obj).decayCoeffPriodic, - (ISACdec_obj->plcstr_obj).prevHP[PITCH_MAX_LAG + 10 - - (ISACdec_obj->plcstr_obj).stretchLag + - (ISACdec_obj->plcstr_obj).pitchIndex] ); - - /* --- lower the muliplier (more decay at next sample) --- */ - (ISACdec_obj->plcstr_obj).decayCoeffPriodic -= (myDecayRate); - if( (ISACdec_obj->plcstr_obj).decayCoeffPriodic < 0 ) - { - (ISACdec_obj->plcstr_obj).decayCoeffPriodic = 0; - } - - /* ------ Weighting the noisy and periodic vectors ------- */ - wNoisyLP = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16_RSFT( - (ISACdec_obj->plcstr_obj).A, nLP, 15 ) ); - wNoisyHP = (WebRtc_Word32)(WEBRTC_SPL_MUL_16_32_RSFT15( - (ISACdec_obj->plcstr_obj).A, (nHP) ) ); - - wPriodicLP = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16_RSFT( - (ISACdec_obj->plcstr_obj).B, pLP, 15)); - wPriodicHP = (WebRtc_Word32)(WEBRTC_SPL_MUL_16_32_RSFT15( - (ISACdec_obj->plcstr_obj).B, pHP)); - - (ISACdec_obj->plcstr_obj).pitchIndex++; - - if((ISACdec_obj->plcstr_obj).pitchIndex == - (ISACdec_obj->plcstr_obj).stretchLag) - { - (ISACdec_obj->plcstr_obj).pitchIndex = 0; - (ISACdec_obj->plcstr_obj).pitchCycles++; - - if( (ISACdec_obj->plcstr_obj).stretchLag != (lag0 + 1) ) - (ISACdec_obj->plcstr_obj).stretchLag = lag0 + 1; - else - (ISACdec_obj->plcstr_obj).stretchLag = lag0; - - (ISACdec_obj->plcstr_obj).stretchLag = ( - (ISACdec_obj->plcstr_obj).stretchLag > PITCH_MAX_LAG - )? (PITCH_MAX_LAG):(ISACdec_obj->plcstr_obj).stretchLag; - LinearResampler( - (ISACdec_obj->plcstr_obj).lastPitchLP, - stretchPitchLP, lag0, (ISACdec_obj->plcstr_obj).stretchLag ); - - LinearResampler((ISACdec_obj->plcstr_obj).prevPitchLP, - stretchPitchLP1, lag0, (ISACdec_obj->plcstr_obj).stretchLag ); - - switch((ISACdec_obj->plcstr_obj).pitchCycles) - { - case 1: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)(( - (WebRtc_Word32)stretchPitchLP[k]* 3 + - (WebRtc_Word32)stretchPitchLP1[k] )>>2); - } - break; - } - case 2: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)(( - (WebRtc_Word32)stretchPitchLP[k] + - (WebRtc_Word32)stretchPitchLP1[k])>>1); - } - break; - } - case 3: - { - for( k=0; k<(ISACdec_obj->plcstr_obj).stretchLag; k++ ) - { - stretchPitchLP[k] = (WebRtc_Word16)( - (stretchPitchLP[k] + - (WebRtc_Word32)stretchPitchLP1[k]*3 )>>2); - } - break; - } - } - - if( (ISACdec_obj->plcstr_obj).pitchCycles == 3 ) - { - myDecayRate += 55; //(myDecayRate>>1); - (ISACdec_obj->plcstr_obj).pitchCycles = 0; - } - } - - /* ------ Sum the noisy and periodic signals ------ */ - Vector_Word16_1[i] = (WebRtc_Word16)WEBRTC_SPL_ADD_SAT_W16( - wNoisyLP, wPriodicLP ); - Vector_Word32_2[i] = (WebRtc_Word32)WEBRTC_SPL_ADD_SAT_W32( - wNoisyHP, wPriodicHP ); - } - } - } - /* ----------------- residual signal is reconstructed ------------------ */ - - k = (ISACdec_obj->plcstr_obj).pitchIndex; - /* --- Write one pitch cycle for recovery block --- */ - - for( i = 0; i < RECOVERY_OVERLAP; i++ ) - { - (ISACdec_obj->plcstr_obj).overlapLP[i] = (WebRtc_Word16)( - WEBRTC_SPL_MUL_16_16_RSFT(stretchPitchLP[k], - (ISACdec_obj->plcstr_obj).decayCoeffPriodic, 15) ); - k = ( k < ((ISACdec_obj->plcstr_obj).stretchLag - 1) )? (k+1):0; - } - - (ISACdec_obj->plcstr_obj).lastPitchLag_Q7 = (ISACdec_obj->plcstr_obj).stretchLag << 7; - - - /* --- Inverse Pitch Filter --- */ - WebRtcIsacfix_PitchFilter(Vector_Word16_1, Vector_Word16_2, - &ISACdec_obj->pitchfiltstr_obj, pitchLags_Q7, pitchGains_Q12, 4); - - /* reduce gain to compensate for pitch enhancer */ - /* gain = 1.0f - 0.45f * AvgPitchGain; */ - tmp32a = WEBRTC_SPL_MUL_16_16_RSFT((ISACdec_obj->plcstr_obj).AvgPitchGain_Q12, - 29, 0); // Q18 - tmp32b = 262144 - tmp32a; // Q18 - gainQ13 = (WebRtc_Word16) (tmp32b >> 5); // Q13 - - /* perceptual post-filtering (using normalized lattice filter) */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) - Vector_Word32_1[k] = (WebRtc_Word32) WEBRTC_SPL_MUL_16_16( - Vector_Word16_2[k], gainQ13) << 3; // Q25 - - - WebRtcIsacfix_NormLatticeFilterAr(ORDERLO, - (ISACdec_obj->maskfiltstr_obj).PostStateLoGQ0, - Vector_Word32_1, lofilt_coefQ15, gain_lo_hiQ17, 0, Vector_Word16_1); - - WebRtcIsacfix_NormLatticeFilterAr(ORDERHI, - (ISACdec_obj->maskfiltstr_obj).PostStateHiGQ0, - Vector_Word32_2, hifilt_coefQ15, gain_lo_hiQ17, 1, Vector_Word16_2); - - /* recombine the 2 bands */ - - /* Form the polyphase signals, and compensate for DC offset */ - for (k=0;kpostfiltbankstr_obj); - - (ISACdec_obj->plcstr_obj).used = PLC_WAS_USED; - *current_framesamples = 480; - - return len; -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/encode.c b/modules/audio_coding/codecs/iSAC/fix/source/encode.c deleted file mode 100644 index c188ba1b3..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/encode.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * encode.c - * - * Encoding function for the iSAC coder. - * - */ - -#include "arith_routins.h" -#include "bandwidth_estimator.h" -#include "codec.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "entropy_coding.h" -#include "lpc_tables.h" -#include "lpc_masking_model.h" -#include "pitch_estimator.h" -#include "structs.h" -#include - - -int WebRtcIsacfix_EncodeImpl(WebRtc_Word16 *in, - ISACFIX_EncInst_t *ISACenc_obj, - BwEstimatorstr *bw_estimatordata, - WebRtc_Word16 CodingMode) -{ - WebRtc_Word16 stream_length = 0; - WebRtc_Word16 usefulstr_len = 0; - int k; - WebRtc_Word16 BWno; - - WebRtc_Word16 lofilt_coefQ15[(ORDERLO)*SUBFRAMES]; - WebRtc_Word16 hifilt_coefQ15[(ORDERHI)*SUBFRAMES]; - WebRtc_Word32 gain_lo_hiQ17[2*SUBFRAMES]; - - WebRtc_Word16 LPandHP[FRAMESAMPLES/2 + QLOOKAHEAD]; - WebRtc_Word16 LP16a[FRAMESAMPLES/2 + QLOOKAHEAD]; - WebRtc_Word16 HP16a[FRAMESAMPLES/2 + QLOOKAHEAD]; - - WebRtc_Word16 PitchLags_Q7[PITCH_SUBFRAMES]; - WebRtc_Word16 PitchGains_Q12[PITCH_SUBFRAMES]; - WebRtc_Word16 AvgPitchGain_Q12; - - WebRtc_Word16 frame_mode; /* 0 for 30ms, 1 for 60ms */ - WebRtc_Word16 processed_samples; - int status; - - WebRtc_Word32 bits_gainsQ11; - WebRtc_Word16 MinBytes; - WebRtc_Word16 bmodel; - - transcode_obj transcodingParam; - WebRtc_Word16 payloadLimitBytes; - WebRtc_Word16 arithLenBeforeEncodingDFT; - WebRtc_Word16 iterCntr; - - /* copy new frame length and bottle neck rate only for the first 10 ms data */ - if (ISACenc_obj->buffer_index == 0) { - /* set the framelength for the next packet */ - ISACenc_obj->current_framesamples = ISACenc_obj->new_framelength; - } - - frame_mode = ISACenc_obj->current_framesamples/MAX_FRAMESAMPLES; /* 0 (30 ms) or 1 (60 ms) */ - processed_samples = ISACenc_obj->current_framesamples/(frame_mode+1); /* 480 (30, 60 ms) */ - - /* buffer speech samples (by 10ms packet) until the framelength is reached (30 or 60 ms) */ - /**************************************************************************************/ - /* fill the buffer with 10ms input data */ - for(k=0; kdata_buffer_fix[k + ISACenc_obj->buffer_index] = in[k]; - } - /* if buffersize is not equal to current framesize, and end of file is not reached yet, */ - /* increase index and go back to main to get more speech samples */ - if (ISACenc_obj->buffer_index + FRAMESAMPLES_10ms != processed_samples) { - ISACenc_obj->buffer_index = ISACenc_obj->buffer_index + FRAMESAMPLES_10ms; - return 0; - } - /* if buffer reached the right size, reset index and continue with encoding the frame */ - ISACenc_obj->buffer_index = 0; - - /* end of buffer function */ - /**************************/ - - /* encoding */ - /************/ - - if (frame_mode == 0 || ISACenc_obj->frame_nb == 0 ) - { - /* reset bitstream */ - ISACenc_obj->bitstr_obj.W_upper = 0xFFFFFFFF; - ISACenc_obj->bitstr_obj.streamval = 0; - ISACenc_obj->bitstr_obj.stream_index = 0; - ISACenc_obj->bitstr_obj.full = 1; - - if (CodingMode == 0) { - ISACenc_obj->BottleNeck = WebRtcIsacfix_GetUplinkBandwidth(bw_estimatordata); - ISACenc_obj->MaxDelay = WebRtcIsacfix_GetUplinkMaxDelay(bw_estimatordata); - } - if (CodingMode == 0 && frame_mode == 0 && (ISACenc_obj->enforceFrameSize == 0)) { - ISACenc_obj->new_framelength = WebRtcIsacfix_GetNewFrameLength(ISACenc_obj->BottleNeck, - ISACenc_obj->current_framesamples); - } - - // multiply the bottleneck by 0.88 before computing SNR, 0.88 is tuned by experimenting on TIMIT - // 901/1024 is 0.87988281250000 - ISACenc_obj->s2nr = WebRtcIsacfix_GetSnr((WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ISACenc_obj->BottleNeck, 901, 10), - ISACenc_obj->current_framesamples); - - /* encode frame length */ - status = WebRtcIsacfix_EncodeFrameLen(ISACenc_obj->current_framesamples, &ISACenc_obj->bitstr_obj); - if (status < 0) - { - /* Wrong frame size */ - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - - /* Save framelength for multiple packets memory */ - if (ISACenc_obj->SaveEnc_ptr != NULL) { - (ISACenc_obj->SaveEnc_ptr)->framelength=ISACenc_obj->current_framesamples; - } - - /* bandwidth estimation and coding */ - BWno = WebRtcIsacfix_GetDownlinkBwIndexImpl(bw_estimatordata); - status = WebRtcIsacfix_EncodeReceiveBandwidth(&BWno, &ISACenc_obj->bitstr_obj); - if (status < 0) - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - } - - /* split signal in two bands */ - WebRtcIsacfix_SplitAndFilter1(ISACenc_obj->data_buffer_fix, LP16a, HP16a, &ISACenc_obj->prefiltbankstr_obj ); - - /* estimate pitch parameters and pitch-filter lookahead signal */ - WebRtcIsacfix_PitchAnalysis(LP16a+QLOOKAHEAD, LPandHP, - &ISACenc_obj->pitchanalysisstr_obj, PitchLags_Q7, PitchGains_Q12); /* LPandHP = LP_lookahead_pfQ0, */ - - /* Set where to store data in multiple packets memory */ - if (ISACenc_obj->SaveEnc_ptr != NULL) { - if (frame_mode == 0 || ISACenc_obj->frame_nb == 0) - { - (ISACenc_obj->SaveEnc_ptr)->startIdx = 0; - } - else - { - (ISACenc_obj->SaveEnc_ptr)->startIdx = 1; - } - } - - /* quantize & encode pitch parameters */ - status = WebRtcIsacfix_EncodePitchGain(PitchGains_Q12, &ISACenc_obj->bitstr_obj, ISACenc_obj->SaveEnc_ptr); - if (status < 0) - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - status = WebRtcIsacfix_EncodePitchLag(PitchLags_Q7 , PitchGains_Q12, &ISACenc_obj->bitstr_obj, ISACenc_obj->SaveEnc_ptr); - if (status < 0) - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - AvgPitchGain_Q12 = WEBRTC_SPL_RSHIFT_W32(PitchGains_Q12[0] + PitchGains_Q12[1] + PitchGains_Q12[2] + PitchGains_Q12[3], 2); - - /* find coefficients for perceptual pre-filters */ - WebRtcIsacfix_GetLpcCoef(LPandHP, HP16a+QLOOKAHEAD, &ISACenc_obj->maskfiltstr_obj, - ISACenc_obj->s2nr, PitchGains_Q12, - gain_lo_hiQ17, lofilt_coefQ15, hifilt_coefQ15); /*LPandHP = LP_lookahead_pfQ0*/ - - // record LPC Gains for possible bit-rate reduction - for(k = 0; k < KLT_ORDER_GAIN; k++) - { - transcodingParam.lpcGains[k] = gain_lo_hiQ17[k]; - } - - /* code LPC model and shape - gains not quantized yet */ - status = WebRtcIsacfix_EncodeLpc(gain_lo_hiQ17, lofilt_coefQ15, hifilt_coefQ15, - &bmodel, &bits_gainsQ11, &ISACenc_obj->bitstr_obj, ISACenc_obj->SaveEnc_ptr, &transcodingParam); - if (status < 0) - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - arithLenBeforeEncodingDFT = (ISACenc_obj->bitstr_obj.stream_index << 1) + (1-ISACenc_obj->bitstr_obj.full); - - /* low-band filtering */ - WebRtcIsacfix_NormLatticeFilterMa(ORDERLO, ISACenc_obj->maskfiltstr_obj.PreStateLoGQ15, - LP16a, lofilt_coefQ15, gain_lo_hiQ17, 0, LPandHP);/* LPandHP = LP16b */ - - /* pitch filter */ - WebRtcIsacfix_PitchFilter(LPandHP, LP16a, &ISACenc_obj->pitchfiltstr_obj, PitchLags_Q7, PitchGains_Q12, 1);/* LPandHP = LP16b */ - - /* high-band filtering */ - WebRtcIsacfix_NormLatticeFilterMa(ORDERHI, ISACenc_obj->maskfiltstr_obj.PreStateHiGQ15, - HP16a, hifilt_coefQ15, gain_lo_hiQ17, 1, LPandHP);/*LPandHP = HP16b*/ - - /* transform */ - WebRtcIsacfix_Time2Spec(LP16a, LPandHP, LP16a, LPandHP); /*LPandHP = HP16b*/ - - /* Save data for multiple packets memory */ - if (ISACenc_obj->SaveEnc_ptr != NULL) { - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - (ISACenc_obj->SaveEnc_ptr)->fre[k + (ISACenc_obj->SaveEnc_ptr)->startIdx*FRAMESAMPLES_HALF] = LP16a[k]; - (ISACenc_obj->SaveEnc_ptr)->fim[k + (ISACenc_obj->SaveEnc_ptr)->startIdx*FRAMESAMPLES_HALF] = LPandHP[k]; - } - (ISACenc_obj->SaveEnc_ptr)->AvgPitchGain[(ISACenc_obj->SaveEnc_ptr)->startIdx] = AvgPitchGain_Q12; - } - - /* quantization and lossless coding */ - status = WebRtcIsacfix_EncodeSpec(LP16a, LPandHP, &ISACenc_obj->bitstr_obj, AvgPitchGain_Q12); - if((status <= -1) && (status != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) /*LPandHP = HP16b*/ - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - - if((frame_mode == 1) && (ISACenc_obj->frame_nb == 0)) - { - // it is a 60ms and we are in the first 30ms - // then the limit at this point should be half of the assigned value - payloadLimitBytes = ISACenc_obj->payloadLimitBytes60 >> 1; - } - else if ((frame_mode == 0)) - { - // it is a 30ms frame - payloadLimitBytes = (ISACenc_obj->payloadLimitBytes30) - 3; - } - else - { - // this is the second half of a 60ms frame. - payloadLimitBytes = ISACenc_obj->payloadLimitBytes60 - 3; // subract 3 because termination process may add 3 bytes - } - - iterCntr = 0; - while((((ISACenc_obj->bitstr_obj.stream_index) << 1) > payloadLimitBytes) || - (status == -ISAC_DISALLOWED_BITSTREAM_LENGTH)) - { - WebRtc_Word16 arithLenDFTByte; - WebRtc_Word16 bytesLeftQ5; - WebRtc_Word16 ratioQ5[8] = {0, 6, 9, 12, 16, 19, 22, 25}; - - // According to experiments on TIMIT the following is proper for audio, but it is not agressive enough for tonal inputs - // such as DTMF, sweep-sine, ... - // - // (0.55 - (0.8 - ratio[i]/32) * 5 / 6) * 2^14 - // WebRtc_Word16 scaleQ14[8] = {0, 648, 1928, 3208, 4915, 6195, 7475, 8755}; - - - // This is a supper-agressive scaling passed the tests (tonal inputs) tone with one iteration for payload limit - // of 120 (32kbps bottleneck), number of frames needed a rate-reduction was 58403 - // - WebRtc_Word16 scaleQ14[8] = {0, 348, 828, 1408, 2015, 3195, 3500, 3500}; - WebRtc_Word16 idx; - - if(iterCntr >= MAX_PAYLOAD_LIMIT_ITERATION) - { - // We were not able to limit the payload size - - if((frame_mode == 1) && (ISACenc_obj->frame_nb == 0)) - { - // This was the first 30ms of a 60ms frame. Although the payload is larger than it - // should be but we let the second 30ms be encoded. Maybe togetehr we won't exceed - // the limit. - ISACenc_obj->frame_nb = 1; - return 0; - } - else if((frame_mode == 1) && (ISACenc_obj->frame_nb == 1)) - { - ISACenc_obj->frame_nb = 0; - } - - if(status != -ISAC_DISALLOWED_BITSTREAM_LENGTH) - { - return -ISAC_PAYLOAD_LARGER_THAN_LIMIT; - } - else - { - return status; - } - } - if(status != -ISAC_DISALLOWED_BITSTREAM_LENGTH) - { - arithLenDFTByte = (ISACenc_obj->bitstr_obj.stream_index << 1) + (1-ISACenc_obj->bitstr_obj.full) - arithLenBeforeEncodingDFT; - bytesLeftQ5 = (payloadLimitBytes - arithLenBeforeEncodingDFT) << 5; - - // bytesLeft / arithLenDFTBytes indicates how much scaling is required a rough estimate (agressive) - // scale = 0.55 - (0.8 - bytesLeft / arithLenDFTBytes) * 5 / 6 - // bytesLeft / arithLenDFTBytes below 0.2 will have a scale of zero and above 0.8 are treated as 0.8 - // to avoid division we do more simplification. - // - // values of (bytesLeft / arithLenDFTBytes)*32 between ratioQ5[i] and ratioQ5[i+1] are rounded to ratioQ5[i] - // and the corresponding scale is chosen - - // we compare bytesLeftQ5 with ratioQ5[]*arithLenDFTByte; - idx = 4; - idx += (bytesLeftQ5 >= WEBRTC_SPL_MUL_16_16(ratioQ5[idx], arithLenDFTByte))? 2:-2; - idx += (bytesLeftQ5 >= WEBRTC_SPL_MUL_16_16(ratioQ5[idx], arithLenDFTByte))? 1:-1; - idx += (bytesLeftQ5 >= WEBRTC_SPL_MUL_16_16(ratioQ5[idx], arithLenDFTByte))? 0:-1; - } - else - { - // we are here because the bit-stream did not fit into the buffer, in this case, the stream_index is not - // trustable, especially if the is the first 30ms of a packet. Thereforem, we will go for the most agressive - // case. - idx = 0; - } - // scale FFT coefficients to reduce the bit-rate - for(k = 0; k < FRAMESAMPLES_HALF; k++) - { - LP16a[k] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(LP16a[k], scaleQ14[idx], 14); - LPandHP[k] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(LPandHP[k], scaleQ14[idx], 14); - } - - // Save data for multiple packets memory - if (ISACenc_obj->SaveEnc_ptr != NULL) - { - for(k = 0; k < FRAMESAMPLES_HALF; k++) - { - (ISACenc_obj->SaveEnc_ptr)->fre[k + (ISACenc_obj->SaveEnc_ptr)->startIdx*FRAMESAMPLES_HALF] = LP16a[k]; - (ISACenc_obj->SaveEnc_ptr)->fim[k + (ISACenc_obj->SaveEnc_ptr)->startIdx*FRAMESAMPLES_HALF] = LPandHP[k]; - } - } - - // scale the unquantized LPC gains and save the scaled version for the future use - for(k = 0; k < KLT_ORDER_GAIN; k++) - { - gain_lo_hiQ17[k] = WEBRTC_SPL_MUL_16_32_RSFT14(scaleQ14[idx], transcodingParam.lpcGains[k]);//transcodingParam.lpcGains[k]; // - transcodingParam.lpcGains[k] = gain_lo_hiQ17[k]; - } - - // reset the bit-stream object to the state which it had before encoding LPC Gains - ISACenc_obj->bitstr_obj.full = transcodingParam.full; - ISACenc_obj->bitstr_obj.stream_index = transcodingParam.stream_index; - ISACenc_obj->bitstr_obj.streamval = transcodingParam.streamval; - ISACenc_obj->bitstr_obj.W_upper = transcodingParam.W_upper; - ISACenc_obj->bitstr_obj.stream[transcodingParam.stream_index-1] = transcodingParam.beforeLastWord; - ISACenc_obj->bitstr_obj.stream[transcodingParam.stream_index] = transcodingParam.lastWord; - - - // quantize and encode LPC gain - WebRtcIsacfix_EstCodeLpcGain(gain_lo_hiQ17, &ISACenc_obj->bitstr_obj, ISACenc_obj->SaveEnc_ptr); - arithLenBeforeEncodingDFT = (ISACenc_obj->bitstr_obj.stream_index << 1) + (1-ISACenc_obj->bitstr_obj.full); - status = WebRtcIsacfix_EncodeSpec(LP16a, LPandHP, &ISACenc_obj->bitstr_obj, AvgPitchGain_Q12); - if((status <= -1) && (status != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) /*LPandHP = HP16b*/ - { - if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - // If this is the second 30ms of a 60ms frame reset this such that in the next call - // encoder starts fresh. - ISACenc_obj->frame_nb = 0; - } - return status; - } - iterCntr++; - } - - if (frame_mode == 1 && ISACenc_obj->frame_nb == 0) - /* i.e. 60 ms framesize and just processed the first 30ms, */ - /* go back to main function to buffer the other 30ms speech frame */ - { - ISACenc_obj->frame_nb = 1; - return 0; - } - else if (frame_mode == 1 && ISACenc_obj->frame_nb == 1) - { - ISACenc_obj->frame_nb = 0; - /* also update the framelength for next packet, in Adaptive mode only */ - if (CodingMode == 0 && (ISACenc_obj->enforceFrameSize == 0)) { - ISACenc_obj->new_framelength = WebRtcIsacfix_GetNewFrameLength(ISACenc_obj->BottleNeck, - ISACenc_obj->current_framesamples); - } - } - - - /* complete arithmetic coding */ - stream_length = WebRtcIsacfix_EncTerminate(&ISACenc_obj->bitstr_obj); - /* can this be negative? */ - - if(CodingMode == 0) - { - - /* update rate model and get minimum number of bytes in this packet */ - MinBytes = WebRtcIsacfix_GetMinBytes(&ISACenc_obj->rate_data_obj, (WebRtc_Word16) stream_length, - ISACenc_obj->current_framesamples, ISACenc_obj->BottleNeck, ISACenc_obj->MaxDelay); - - /* if bitstream is too short, add garbage at the end */ - - /* Store length of coded data */ - usefulstr_len = stream_length; - - /* Make sure MinBytes does not exceed packet size limit */ - if ((ISACenc_obj->frame_nb == 0) && (MinBytes > ISACenc_obj->payloadLimitBytes30)) { - MinBytes = ISACenc_obj->payloadLimitBytes30; - } else if ((ISACenc_obj->frame_nb == 1) && (MinBytes > ISACenc_obj->payloadLimitBytes60)) { - MinBytes = ISACenc_obj->payloadLimitBytes60; - } - - /* Make sure we don't allow more than 255 bytes of garbage data. - We store the length of the garbage data in 8 bits in the bitstream, - 255 is the max garbage lenght we can signal using 8 bits. */ - if( MinBytes > usefulstr_len + 255 ) { - MinBytes = usefulstr_len + 255; - } - - /* Save data for creation of multiple bitstreams */ - if (ISACenc_obj->SaveEnc_ptr != NULL) { - (ISACenc_obj->SaveEnc_ptr)->minBytes = MinBytes; - } - - while (stream_length < MinBytes) - { - if (stream_length & 0x0001){ - ISACenc_obj->bitstr_seed = WEBRTC_SPL_RAND( ISACenc_obj->bitstr_seed ); - ISACenc_obj->bitstr_obj.stream[ WEBRTC_SPL_RSHIFT_W16(stream_length, 1) ] |= (WebRtc_UWord16)(ISACenc_obj->bitstr_seed & 0xFF); - } else { - ISACenc_obj->bitstr_seed = WEBRTC_SPL_RAND( ISACenc_obj->bitstr_seed ); - ISACenc_obj->bitstr_obj.stream[ WEBRTC_SPL_RSHIFT_W16(stream_length, 1) ] = WEBRTC_SPL_LSHIFT_U16(ISACenc_obj->bitstr_seed, 8); - } - stream_length++; - } - - /* to get the real stream_length, without garbage */ - if (usefulstr_len & 0x0001) { - ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] &= 0xFF00; - ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] += (MinBytes - usefulstr_len) & 0x00FF; - } - else { - ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] &= 0x00FF; - ISACenc_obj->bitstr_obj.stream[usefulstr_len>>1] += WEBRTC_SPL_LSHIFT_U16((MinBytes - usefulstr_len) & 0x00FF, 8); - } - } - else - { - /* update rate model */ - WebRtcIsacfix_UpdateRateModel(&ISACenc_obj->rate_data_obj, (WebRtc_Word16) stream_length, - ISACenc_obj->current_framesamples, ISACenc_obj->BottleNeck); - } - return stream_length; -} - -/* This function is used to create a new bitstream with new BWE. - The same data as previously encoded with the fucntion WebRtcIsacfix_EncodeImpl() - is used. The data needed is taken from the struct, where it was stored - when calling the encoder. */ -int WebRtcIsacfix_EncodeStoredData(ISACFIX_EncInst_t *ISACenc_obj, - int BWnumber, - float scale, - WebRtc_Word16 CodingMode) -{ - int ii; - int status; - WebRtc_Word16 BWno = BWnumber; - int stream_length = 0; - int usefulstr_len; - - WebRtc_Word16 model; - const WebRtc_UWord16 *Q_PitchGain_cdf_ptr[1]; - const WebRtc_UWord16 **cdf; - const ISAC_SaveEncData_t *SaveEnc_str; - WebRtc_Word32 tmpLPCcoeffs_g[KLT_ORDER_GAIN<<1]; - WebRtc_Word16 tmpLPCindex_g[KLT_ORDER_GAIN<<1]; - WebRtc_Word16 tmp_fre[FRAMESAMPLES]; - WebRtc_Word16 tmp_fim[FRAMESAMPLES]; - - SaveEnc_str = ISACenc_obj->SaveEnc_ptr; - - /* Check if SaveEnc memory exists */ - if (SaveEnc_str == NULL) { - return (-1); - } - - /* Sanity Check - possible values for BWnumber is 0 - 23 */ - if ((BWnumber < 0) || (BWnumber > 23)) { - return -ISAC_RANGE_ERROR_BW_ESTIMATOR; - } - - /* reset bitstream */ - ISACenc_obj->bitstr_obj.W_upper = 0xFFFFFFFF; - ISACenc_obj->bitstr_obj.streamval = 0; - ISACenc_obj->bitstr_obj.stream_index = 0; - ISACenc_obj->bitstr_obj.full = 1; - - /* encode frame length */ - status = WebRtcIsacfix_EncodeFrameLen(SaveEnc_str->framelength, &ISACenc_obj->bitstr_obj); - if (status < 0) { - /* Wrong frame size */ - return status; - } - - /* encode bandwidth estimate */ - status = WebRtcIsacfix_EncodeReceiveBandwidth(&BWno, &ISACenc_obj->bitstr_obj); - if (status < 0) { - return status; - } - - /* Transcoding */ - /* If scale < 1, rescale data to produce lower bitrate signal */ - if ((0.0 < scale) && (scale < 1.0)) { - /* Compensate LPC gain */ - for (ii = 0; ii < (KLT_ORDER_GAIN*(1+SaveEnc_str->startIdx)); ii++) { - tmpLPCcoeffs_g[ii] = (WebRtc_Word32) ((scale) * (float) SaveEnc_str->LPCcoeffs_g[ii]); - } - - /* Scale DFT */ - for (ii = 0; ii < (FRAMESAMPLES_HALF*(1+SaveEnc_str->startIdx)); ii++) { - tmp_fre[ii] = (WebRtc_Word16) ((scale) * (float) SaveEnc_str->fre[ii]) ; - tmp_fim[ii] = (WebRtc_Word16) ((scale) * (float) SaveEnc_str->fim[ii]) ; - } - } else { - for (ii = 0; ii < (KLT_ORDER_GAIN*(1+SaveEnc_str->startIdx)); ii++) { - tmpLPCindex_g[ii] = SaveEnc_str->LPCindex_g[ii]; - } - - for (ii = 0; ii < (FRAMESAMPLES_HALF*(1+SaveEnc_str->startIdx)); ii++) { - tmp_fre[ii] = SaveEnc_str->fre[ii]; - tmp_fim[ii] = SaveEnc_str->fim[ii]; - } - } - - /* Loop over number of 30 msec */ - for (ii = 0; ii <= SaveEnc_str->startIdx; ii++) - { - - /* encode pitch gains */ - *Q_PitchGain_cdf_ptr = WebRtcIsacfix_kPitchGainCdf; - status = WebRtcIsacfix_EncHistMulti(&ISACenc_obj->bitstr_obj, &SaveEnc_str->pitchGain_index[ii], - Q_PitchGain_cdf_ptr, 1); - if (status < 0) { - return status; - } - - /* entropy coding of quantization pitch lags */ - /* voicing classificiation */ - if (SaveEnc_str->meanGain[ii] <= 819) { - cdf = WebRtcIsacfix_kPitchLagPtrLo; - } else if (SaveEnc_str->meanGain[ii] <= 1638) { - cdf = WebRtcIsacfix_kPitchLagPtrMid; - } else { - cdf = WebRtcIsacfix_kPitchLagPtrHi; - } - status = WebRtcIsacfix_EncHistMulti(&ISACenc_obj->bitstr_obj, - &SaveEnc_str->pitchIndex[PITCH_SUBFRAMES*ii], cdf, PITCH_SUBFRAMES); - if (status < 0) { - return status; - } - - /* LPC */ - /* entropy coding of model number */ - model = 0; - status = WebRtcIsacfix_EncHistMulti(&ISACenc_obj->bitstr_obj, &model, - WebRtcIsacfix_kModelCdfPtr, 1); - if (status < 0) { - return status; - } - - /* entropy coding of quantization indices - LPC shape only */ - status = WebRtcIsacfix_EncHistMulti(&ISACenc_obj->bitstr_obj, &SaveEnc_str->LPCindex_s[KLT_ORDER_SHAPE*ii], - WebRtcIsacfix_kCdfShapePtr[0], KLT_ORDER_SHAPE); - if (status < 0) { - return status; - } - - /* If transcoding, get new LPC gain indices */ - if (scale < 1.0) { - WebRtcIsacfix_TranscodeLpcCoef(&tmpLPCcoeffs_g[KLT_ORDER_GAIN*ii], &tmpLPCindex_g[KLT_ORDER_GAIN*ii]); - } - - /* entropy coding of quantization indices - LPC gain */ - status = WebRtcIsacfix_EncHistMulti(&ISACenc_obj->bitstr_obj, &tmpLPCindex_g[KLT_ORDER_GAIN*ii], - WebRtcIsacfix_kCdfGainPtr[0], KLT_ORDER_GAIN); - if (status < 0) { - return status; - } - - /* quantization and lossless coding */ - status = WebRtcIsacfix_EncodeSpec(&tmp_fre[ii*FRAMESAMPLES_HALF], &tmp_fim[ii*FRAMESAMPLES_HALF], - &ISACenc_obj->bitstr_obj, SaveEnc_str->AvgPitchGain[ii]); - if (status < 0) { - return status; - } - } - - /* complete arithmetic coding */ - stream_length = WebRtcIsacfix_EncTerminate(&ISACenc_obj->bitstr_obj); - - return stream_length; -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.c b/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.c deleted file mode 100644 index 0b64d8358..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.c +++ /dev/null @@ -1,2072 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * entropy_coding.c - * - * This file contains all functions used to arithmetically - * encode the iSAC bistream. - * - */ - -#include - -#include "arith_routins.h" -#include "spectrum_ar_model_tables.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "entropy_coding.h" -#include "lpc_tables.h" -#include "settings.h" -#include "signal_processing_library.h" - - -/* - This function implements the fix-point correspondant function to lrint. - - FLP: (WebRtc_Word32)floor(flt+.499999999999) - FIP: (fixVal+roundVal)>>qDomain - - where roundVal = 2^(qDomain-1) = 1<<(qDomain-1) - -*/ -static __inline WebRtc_Word32 CalcLrIntQ(WebRtc_Word32 fixVal, WebRtc_Word16 qDomain) { - WebRtc_Word32 intgr; - WebRtc_Word32 roundVal; - - roundVal = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, qDomain-1); - intgr = WEBRTC_SPL_RSHIFT_W32(fixVal+roundVal, qDomain); - - return intgr; -} - -/* - __inline WebRtc_UWord32 stepwise(WebRtc_Word32 dinQ10) { - - WebRtc_Word32 ind, diQ10, dtQ10; - - diQ10 = dinQ10; - if (diQ10 < DPMIN_Q10) - diQ10 = DPMIN_Q10; - if (diQ10 >= DPMAX_Q10) - diQ10 = DPMAX_Q10 - 1; - - dtQ10 = diQ10 - DPMIN_Q10;*/ /* Q10 + Q10 = Q10 */ -/* ind = (dtQ10 * 5) >> 10; */ /* 2^10 / 5 = 0.2 in Q10 */ -/* Q10 -> Q0 */ - -/* return rpointsFIX_Q10[ind]; - - } -*/ - -/* logN(x) = logN(2)*log2(x) = 0.6931*log2(x). Output in Q8. */ -/* The input argument X to logN(X) is 2^17 times higher than the - input floating point argument Y to log(Y), since the X value - is a Q17 value. This can be compensated for after the call, by - subraction a value Z for each Q-step. One Q-step means that - X gets 2 thimes higher, i.e. Z = logN(2)*256 = 0.693147180559*256 = - 177.445678 should be subtracted (since logN() returns a Q8 value). - For a X value in Q17, the value 177.445678*17 = 3017 should be - subtracted */ -static WebRtc_Word16 CalcLogN(WebRtc_Word32 arg) { - WebRtc_Word16 zeros, log2, frac, logN; - - zeros=WebRtcSpl_NormU32(arg); - frac=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_LSHIFT_W32(arg, zeros)&0x7FFFFFFF, 23); - log2=(WebRtc_Word16)(WEBRTC_SPL_LSHIFT_W32(31-zeros, 8)+frac); // log2(x) in Q8 - logN=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(log2,22713,15); //Q8*Q15 log(2) = 0.693147 = 22713 in Q15 - logN=logN+11; //Scalar compensation which minimizes the (log(x)-logN(x))^2 error over all x. - - return logN; -} - - -/* - expN(x) = 2^(a*x), where a = log2(e) ~= 1.442695 - - Input: Q8 (WebRtc_Word16) - Output: Q17 (WebRtc_Word32) - - a = log2(e) = log2(exp(1)) ~= 1.442695 ==> a = 23637 in Q14 (1.442688) - To this value, 700 is added or subtracted in order to get an average error - nearer zero, instead of always same-sign. -*/ - -static WebRtc_Word32 CalcExpN(WebRtc_Word16 x) { - WebRtc_Word16 ax, axINT, axFRAC; - WebRtc_Word16 exp16; - WebRtc_Word32 exp; - - if (x>=0) { - // ax=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637-700, 14); //Q8 - ax=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8 - axINT = WEBRTC_SPL_RSHIFT_W16(ax, 8); //Q0 - axFRAC = ax&0x00FF; - exp16 = WEBRTC_SPL_LSHIFT_W32(1, axINT); //Q0 - axFRAC = axFRAC+256; //Q8 - exp = WEBRTC_SPL_MUL_16_16(exp16, axFRAC); // Q0*Q8 = Q8 - exp = WEBRTC_SPL_LSHIFT_W32(exp, 9); //Q17 - } else { - // ax=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637+700, 14); //Q8 - ax=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(x, 23637, 14); //Q8 - ax = -ax; - axINT = 1 + WEBRTC_SPL_RSHIFT_W16(ax, 8); //Q0 - axFRAC = 0x00FF - (ax&0x00FF); - exp16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(32768, axINT); //Q15 - axFRAC = axFRAC+256; //Q8 - exp = WEBRTC_SPL_MUL_16_16(exp16, axFRAC); // Q15*Q8 = Q23 - exp = WEBRTC_SPL_RSHIFT_W32(exp, 6); //Q17 - } - - return exp; -} - - -/* compute correlation from power spectrum */ -static void CalcCorrelation(WebRtc_Word32 *PSpecQ12, WebRtc_Word32 *CorrQ7) -{ - WebRtc_Word32 summ[FRAMESAMPLES/8]; - WebRtc_Word32 diff[FRAMESAMPLES/8]; - WebRtc_Word32 sum; - int k, n; - - for (k = 0; k < FRAMESAMPLES/8; k++) { - summ[k] = WEBRTC_SPL_RSHIFT_W32(PSpecQ12[k] + PSpecQ12[FRAMESAMPLES/4-1 - k] + 16, 5); - diff[k] = WEBRTC_SPL_RSHIFT_W32(PSpecQ12[k] - PSpecQ12[FRAMESAMPLES/4-1 - k] + 16, 5); - } - - sum = 2; - for (n = 0; n < FRAMESAMPLES/8; n++) - sum += summ[n]; - CorrQ7[0] = sum; - - for (k = 0; k < AR_ORDER; k += 2) { - sum = 0; - for (n = 0; n < FRAMESAMPLES/8; n++) - sum += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WebRtcIsacfix_kCos[k][n], diff[n]) + 256, 9); - CorrQ7[k+1] = sum; - } - - for (k=1; k400000){ - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); - round = 32; - shftVal = 6; - } else { - tmpGain = gainQ10; - round = 256; - shftVal = 9; - } - - for (k = 1; k < AR_ORDER+1; k++) { - sum = 16384; - for (n = k; n < AR_ORDER+1; n++) - sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, shftVal); - } - sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); - for (n = 0; n < FRAMESAMPLES/8; n++) - CurveQ16[n] = sum; - - for (k = 1; k < AR_ORDER; k += 2) { - for (n = 0; n < FRAMESAMPLES/8; n++) - CurveQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WebRtcIsacfix_kCos[k][n], CorrQ11[k+1]) + 2, 2); - } - - CS_ptrQ9 = WebRtcIsacfix_kCos[0]; - - /* If CorrQ11[1] too large we avoid getting overflow in the calculation by shifting */ - sh=WebRtcSpl_NormW32(CorrQ11[1]); - if (CorrQ11[1]==0) /* Use next correlation */ - sh=WebRtcSpl_NormW32(CorrQ11[2]); - - if (sh<9) - shftVal = 9 - sh; - else - shftVal = 0; - - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); - for (k = 2; k < AR_ORDER; k += 2) { - CS_ptrQ9 = WebRtcIsacfix_kCos[k]; - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k+1], shftVal)) + 2, 2); - } - - for (k=0; k400000){ - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); - round = 32; - shftVal = 6; - } else { - tmpGain = gainQ10; - round = 256; - shftVal = 9; - } - - for (k = 1; k < AR_ORDER+1; k++) { - sum = 16384; - for (n = k; n < AR_ORDER+1; n++) - sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, shftVal); - } - sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); - for (n = 0; n < FRAMESAMPLES/8; n++) - summQ16[n] = sum; - - for (k = 1; k < (AR_ORDER); k += 2) { - for (n = 0; n < FRAMESAMPLES/8; n++) - summQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(CorrQ11[k+1],WebRtcIsacfix_kCos[k][n]) + 2, 2); - } - - CS_ptrQ9 = WebRtcIsacfix_kCos[0]; - - /* If CorrQ11[1] too large we avoid getting overflow in the calculation by shifting */ - sh=WebRtcSpl_NormW32(CorrQ11[1]); - if (CorrQ11[1]==0) /* Use next correlation */ - sh=WebRtcSpl_NormW32(CorrQ11[2]); - - if (sh<9) - shftVal = 9 - sh; - else - shftVal = 0; - - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); - for (k = 2; k < AR_ORDER; k += 2) { - CS_ptrQ9 = WebRtcIsacfix_kCos[k]; - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k+1], shftVal)) + 2, 2); - } - - in_sqrt = summQ16[0] + WEBRTC_SPL_LSHIFT_W32(diffQ16[0], shftVal); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = WEBRTC_SPL_LSHIFT_W32(1, WEBRTC_SPL_RSHIFT_W16(WebRtcSpl_GetSizeInBits(in_sqrt), 1)); - - for (k = 0; k < FRAMESAMPLES/8; k++) - { - in_sqrt = summQ16[k] + WEBRTC_SPL_LSHIFT_W32(diffQ16[k], shftVal); - i = 10; - - /* make in_sqrt positive to prohibit sqrt of negative values */ - if(in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); - do - { - res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); - } while (newRes != res && i-- > 0); - - CurveQ8[k] = (WebRtc_Word16)newRes; - } - for (k = FRAMESAMPLES/8; k < FRAMESAMPLES/4; k++) { - - in_sqrt = summQ16[FRAMESAMPLES/4-1 - k] - WEBRTC_SPL_LSHIFT_W32(diffQ16[FRAMESAMPLES/4-1 - k], shftVal); - i = 10; - - /* make in_sqrt positive to prohibit sqrt of negative values */ - if(in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); - do - { - res = newRes; - newRes = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_DIV(in_sqrt, res) + res, 1); - } while (newRes != res && i-- > 0); - - CurveQ8[k] = (WebRtc_Word16)newRes; - } - -} - - - -/* generate array of dither samples in Q7 */ -static void GenerateDitherQ7(WebRtc_Word16 *bufQ7, - WebRtc_UWord32 seed, - WebRtc_Word16 length, - WebRtc_Word16 AvgPitchGain_Q12) -{ - int k; - WebRtc_Word16 dither1_Q7, dither2_Q7, dither_gain_Q14, shft; - - if (AvgPitchGain_Q12 < 614) /* this threshold should be equal to that in decode_spec() */ - { - for (k = 0; k < length-2; k += 3) - { - /* new random unsigned WebRtc_Word32 */ - seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 (Q7) */ - dither1_Q7 = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)seed + 16777216, 25); // * 128/4294967295 - - /* new random unsigned WebRtc_Word32 */ - seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 */ - dither2_Q7 = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(seed + 16777216, 25); - - shft = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 15); - if (shft < 5) - { - bufQ7[k] = dither1_Q7; - bufQ7[k+1] = dither2_Q7; - bufQ7[k+2] = 0; - } - else if (shft < 10) - { - bufQ7[k] = dither1_Q7; - bufQ7[k+1] = 0; - bufQ7[k+2] = dither2_Q7; - } - else - { - bufQ7[k] = 0; - bufQ7[k+1] = dither1_Q7; - bufQ7[k+2] = dither2_Q7; - } - } - } - else - { - dither_gain_Q14 = (WebRtc_Word16)(22528 - WEBRTC_SPL_MUL(10, AvgPitchGain_Q12)); - - /* dither on half of the coefficients */ - for (k = 0; k < length-1; k += 2) - { - /* new random unsigned WebRtc_Word32 */ - seed = WEBRTC_SPL_UMUL(seed, 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 */ - dither1_Q7 = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)seed + 16777216, 25); - - /* dither sample is placed in either even or odd index */ - shft = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_U32(seed, 25) & 1); /* either 0 or 1 */ - - bufQ7[k + shft] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(dither_gain_Q14, dither1_Q7) + 8192, 14); - bufQ7[k + 1 - shft] = 0; - } - } -} - - - - -/* - * function to decode the complex spectrum from the bitstream - * returns the total number of bytes in the stream - */ -WebRtc_Word16 WebRtcIsacfix_DecodeSpec(Bitstr_dec *streamdata, - WebRtc_Word16 *frQ7, - WebRtc_Word16 *fiQ7, - WebRtc_Word16 AvgPitchGain_Q12) -{ - WebRtc_Word16 data[FRAMESAMPLES]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES/4]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 gainQ10; - WebRtc_Word32 gain2_Q10; - WebRtc_Word16 len; - int k; - - /* create dither signal */ - GenerateDitherQ7(data, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12); /* Dither is output in vector 'Data' */ - - /* decode model parameters */ - if (WebRtcIsacfix_DecodeRcCoef(streamdata, RCQ15) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - if (WebRtcIsacfix_DecodeGain2(streamdata, &gain2_Q10) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* compute inverse AR power spectrum */ - CalcInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* arithmetic decoding of spectrum */ - /* 'data' input and output. Input = Dither */ - len = WebRtcIsacfix_DecLogisticMulti2(data, streamdata, invARSpec2_Q16, (WebRtc_Word16)FRAMESAMPLES); - - if (len<1) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* subtract dither and scale down spectral samples with low SNR */ - if (AvgPitchGain_Q12 <= 614) - { - for (k = 0; k < FRAMESAMPLES; k += 4) - { - gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)30, 10), - (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (WebRtc_UWord32)2195456, 16)); - *frQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[ k ], gainQ10) + 512, 10); - *fiQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+1], gainQ10) + 512, 10); - *frQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+2], gainQ10) + 512, 10); - *fiQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+3], gainQ10) + 512, 10); - } - } - else - { - for (k = 0; k < FRAMESAMPLES; k += 4) - { - gainQ10 = WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)36, 10), - (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(invARSpec2_Q16[k>>2] + (WebRtc_UWord32)2654208, 16)); - *frQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[ k ], gainQ10) + 512, 10); - *fiQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+1], gainQ10) + 512, 10); - *frQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+2], gainQ10) + 512, 10); - *fiQ7++ = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(data[k+3], gainQ10) + 512, 10); - } - } - - return len; -} - - -int WebRtcIsacfix_EncodeSpec(const WebRtc_Word16 *fr, - const WebRtc_Word16 *fi, - Bitstr_enc *streamdata, - WebRtc_Word16 AvgPitchGain_Q12) -{ - WebRtc_Word16 dataQ7[FRAMESAMPLES]; - WebRtc_Word32 PSpec[FRAMESAMPLES/4]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES/4]; - WebRtc_Word32 CorrQ7[AR_ORDER+1]; - WebRtc_Word32 CorrQ7_norm[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word32 gain2_Q10; - WebRtc_Word16 val; - WebRtc_Word32 nrg; - WebRtc_UWord32 sum; - WebRtc_Word16 lft_shft; - WebRtc_Word16 status; - int k, n, j; - - - /* create dither_float signal */ - GenerateDitherQ7(dataQ7, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12); - - /* add dither and quantize, and compute power spectrum */ - /* Vector dataQ7 contains Dither in Q7 */ - for (k = 0; k < FRAMESAMPLES; k += 4) - { - val = ((*fr++ + dataQ7[k] + 64) & 0xFF80) - dataQ7[k]; /* Data = Dither */ - dataQ7[k] = val; /* New value in Data */ - sum = WEBRTC_SPL_UMUL(val, val); - - val = ((*fi++ + dataQ7[k+1] + 64) & 0xFF80) - dataQ7[k+1]; /* Data = Dither */ - dataQ7[k+1] = val; /* New value in Data */ - sum += WEBRTC_SPL_UMUL(val, val); - - val = ((*fr++ + dataQ7[k+2] + 64) & 0xFF80) - dataQ7[k+2]; /* Data = Dither */ - dataQ7[k+2] = val; /* New value in Data */ - sum += WEBRTC_SPL_UMUL(val, val); - - val = ((*fi++ + dataQ7[k+3] + 64) & 0xFF80) - dataQ7[k+3]; /* Data = Dither */ - dataQ7[k+3] = val; /* New value in Data */ - sum += WEBRTC_SPL_UMUL(val, val); - - PSpec[k>>2] = WEBRTC_SPL_RSHIFT_U32(sum, 2); - } - - /* compute correlation from power spectrum */ - CalcCorrelation(PSpec, CorrQ7); - - - /* find AR coefficients */ - /* number of bit shifts to 14-bit normalize CorrQ7[0] (leaving room for sign) */ - lft_shft = WebRtcSpl_NormW32(CorrQ7[0]) - 18; - - if (lft_shft > 0) { - for (k=0; k AR coefficients */ - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - /* compute ARCoef' * Corr * ARCoef in Q19 */ - nrg = 0; - for (j = 0; j <= AR_ORDER; j++) { - for (n = 0; n <= j; n++) - nrg += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(ARCoefQ12[j], WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CorrQ7_norm[j-n], ARCoefQ12[n]) + 256, 9)) + 4, 3); - for (n = j+1; n <= AR_ORDER; n++) - nrg += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(ARCoefQ12[j], WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CorrQ7_norm[n-j], ARCoefQ12[n]) + 256, 9)) + 4, 3); - } - - if (lft_shft > 0) - nrg = WEBRTC_SPL_RSHIFT_W32(nrg, lft_shft); - else - nrg = WEBRTC_SPL_LSHIFT_W32(nrg, -lft_shft); - - if(nrg>131072) - gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES >> 2, nrg); /* also shifts 31 bits to the left! */ - else - gain2_Q10 = WEBRTC_SPL_RSHIFT_W32(FRAMESAMPLES, 2); - - /* quantize & code gain2_Q10 */ - if (WebRtcIsacfix_EncodeGain2(&gain2_Q10, streamdata)) - return -1; - - /* compute inverse AR magnitude spectrum */ - CalcRootInvArSpec(ARCoefQ12, gain2_Q10, invARSpecQ8); - - - /* arithmetic coding of spectrum */ - status = WebRtcIsacfix_EncLogisticMulti2(streamdata, dataQ7, invARSpecQ8, (WebRtc_Word16)FRAMESAMPLES); - if ( status ) - return( status ); - - return 0; -} - - -/* Matlab's LAR definition */ -static void Rc2LarFix(const WebRtc_Word16 *rcQ15, WebRtc_Word32 *larQ17, WebRtc_Word16 order) { - - /* - - This is a piece-wise implemenetation of a rc2lar-function (all values in the comment - are Q15 values and are based on [0 24956/32768 30000/32768 32500/32768], i.e. - [0.76159667968750 0.91552734375000 0.99182128906250] - - x0 x1 a k x0(again) b - ================================================================================== - 0.00 0.76: 0 2.625997508581 0 0 - 0.76 0.91: 2.000012018559 7.284502668663 0.761596679688 -3.547841027073 - 0.91 0.99: 3.121320351712 31.115835041229 0.915527343750 -25.366077452148 - 0.99 1.00: 5.495270168700 686.663805654056 0.991821289063 -675.552510708011 - - The implementation is y(x)= a + (x-x0)*k, but this can be simplified to - - y(x) = a-x0*k + x*k = b + x*k, where b = a-x0*k - - akx=[0 2.625997508581 0 - 2.000012018559 7.284502668663 0.761596679688 - 3.121320351712 31.115835041229 0.915527343750 - 5.495270168700 686.663805654056 0.991821289063]; - - b = akx(:,1) - akx(:,3).*akx(:,2) - - [ 0.0 - -3.547841027073 - -25.366077452148 - -675.552510708011] - - */ - - int k; - WebRtc_Word16 rc; - WebRtc_Word32 larAbsQ17; - - for (k = 0; k < order; k++) { - - rc = WEBRTC_SPL_ABS_W16(rcQ15[k]); //Q15 - - /* Calculate larAbsQ17 in Q17 from rc in Q15 */ - - if (rc<24956) { //0.7615966 in Q15 - // (Q15*Q13)>>11 = Q17 - larAbsQ17 = WEBRTC_SPL_MUL_16_16_RSFT(rc, 21512, 11); - } else if (rc<30000) { //0.91552734375 in Q15 - // Q17 + (Q15*Q12)>>10 = Q17 - larAbsQ17 = -465024 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 29837, 10); - } else if (rc<32500) { //0.99182128906250 in Q15 - // Q17 + (Q15*Q10)>>8 = Q17 - larAbsQ17 = -3324784 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 31863, 8); - } else { - // Q17 + (Q15*Q5)>>3 = Q17 - larAbsQ17 = -88546020 + WEBRTC_SPL_MUL_16_16_RSFT(rc, 21973, 3); - } - - if (rcQ15[k]>0) { - larQ17[k] = larAbsQ17; - } else { - larQ17[k] = -larAbsQ17; - } - } -} - - -static void Lar2RcFix(const WebRtc_Word32 *larQ17, WebRtc_Word16 *rcQ15, WebRtc_Word16 order) { - - /* - This is a piece-wise implemenetation of a lar2rc-function - See comment in Rc2LarFix() about details. - */ - - int k; - WebRtc_Word16 larAbsQ11; - WebRtc_Word32 rc; - - for (k = 0; k < order; k++) { - - larAbsQ11 = (WebRtc_Word16) WEBRTC_SPL_ABS_W32(WEBRTC_SPL_RSHIFT_W32(larQ17[k]+32,6)); //Q11 - - if (larAbsQ11<4097) { //2.000012018559 in Q11 - // Q11*Q16>>12 = Q15 - rc = WEBRTC_SPL_MUL_16_16_RSFT(larAbsQ11, 24957, 12); - } else if (larAbsQ11<6393) { //3.121320351712 in Q11 - // (Q11*Q17 + Q13)>>13 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(larAbsQ11, 17993) + 130738688), 13); - } else if (larAbsQ11<11255) { //5.495270168700 in Q11 - // (Q11*Q19 + Q30)>>15 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32((WEBRTC_SPL_MUL_16_16(larAbsQ11, 16850) + 875329820), 15); - } else { - // (Q11*Q24>>16 + Q19)>>4 = Q15 - rc = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16_RSFT(larAbsQ11, 24433, 16)) + 515804), 4); - } - - if (larQ17[k]<=0) { - rc = -rc; - } - - rcQ15[k] = (WebRtc_Word16) rc; // Q15 - } -} - -static void Poly2LarFix(WebRtc_Word16 *lowbandQ15, - WebRtc_Word16 orderLo, - WebRtc_Word16 *hibandQ15, - WebRtc_Word16 orderHi, - WebRtc_Word16 Nsub, - WebRtc_Word32 *larsQ17) { - - int k, n; - WebRtc_Word32 *outpQ17; - WebRtc_Word16 orderTot; - WebRtc_Word32 larQ17[MAX_ORDER]; // Size 7+6 is enough - - orderTot = (orderLo + orderHi); - outpQ17 = larsQ17; - for (k = 0; k < Nsub; k++) { - - Rc2LarFix(lowbandQ15, larQ17, orderLo); - - for (n = 0; n < orderLo; n++) - outpQ17[n] = larQ17[n]; //Q17 - - Rc2LarFix(hibandQ15, larQ17, orderHi); - - for (n = 0; n < orderHi; n++) - outpQ17[n + orderLo] = larQ17[n]; //Q17; - - outpQ17 += orderTot; - lowbandQ15 += orderLo; - hibandQ15 += orderHi; - } -} - - -static void Lar2polyFix(WebRtc_Word32 *larsQ17, - WebRtc_Word16 *lowbandQ15, - WebRtc_Word16 orderLo, - WebRtc_Word16 *hibandQ15, - WebRtc_Word16 orderHi, - WebRtc_Word16 Nsub) { - - int k, n; - WebRtc_Word16 orderTot; - WebRtc_Word16 *outplQ15, *outphQ15; - WebRtc_Word32 *inpQ17; - WebRtc_Word16 rcQ15[7+6]; - - orderTot = (orderLo + orderHi); - outplQ15 = lowbandQ15; - outphQ15 = hibandQ15; - inpQ17 = larsQ17; - for (k = 0; k < Nsub; k++) { - - /* gains not handled here as in the FLP version */ - - /* Low band */ - Lar2RcFix(&inpQ17[0], rcQ15, orderLo); - for (n = 0; n < orderLo; n++) - outplQ15[n] = rcQ15[n]; // Refl. coeffs - - /* High band */ - Lar2RcFix(&inpQ17[orderLo], rcQ15, orderHi); - for (n = 0; n < orderHi; n++) - outphQ15[n] = rcQ15[n]; // Refl. coeffs - - inpQ17 += orderTot; - outplQ15 += orderLo; - outphQ15 += orderHi; - } -} - -int WebRtcIsacfix_DecodeLpc(WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *LPCCoef_loQ15, - WebRtc_Word16 *LPCCoef_hiQ15, - Bitstr_dec *streamdata, - WebRtc_Word16 *outmodel) { - - WebRtc_Word32 larsQ17[KLT_ORDER_SHAPE]; // KLT_ORDER_GAIN+KLT_ORDER_SHAPE == (ORDERLO+ORDERHI)*SUBFRAMES - int err; - - err = WebRtcIsacfix_DecodeLpcCoef(streamdata, larsQ17, gain_lo_hiQ17, outmodel); - if (err<0) // error check - return -ISAC_RANGE_ERROR_DECODE_LPC; - - Lar2polyFix(larsQ17, LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES); - - return 0; -} - -/* decode & dequantize LPC Coef */ -int WebRtcIsacfix_DecodeLpcCoef(Bitstr_dec *streamdata, - WebRtc_Word32 *LPCCoefQ17, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *outmodel) -{ - int j, k, n; - int err; - WebRtc_Word16 pos, pos2, posg, poss, offsg, offss, offs2; - WebRtc_Word16 gainpos; - WebRtc_Word16 model; - WebRtc_Word16 index_QQ[KLT_ORDER_SHAPE]; - WebRtc_Word32 tmpcoeffs_gQ17[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs2_gQ21[KLT_ORDER_GAIN]; - WebRtc_Word16 tmpcoeffs_sQ10[KLT_ORDER_SHAPE]; - WebRtc_Word32 tmpcoeffs_sQ17[KLT_ORDER_SHAPE]; - WebRtc_Word32 tmpcoeffs2_sQ18[KLT_ORDER_SHAPE]; - WebRtc_Word32 sumQQ; - WebRtc_Word16 sumQQ16; - WebRtc_Word32 tmp32; - - - - /* entropy decoding of model number */ - err = WebRtcIsacfix_DecHistOneStepMulti(&model, streamdata, WebRtcIsacfix_kModelCdfPtr, WebRtcIsacfix_kModelInitIndex, 1); - if (err<0) // error check - return err; - - /* entropy decoding of quantization indices */ - err = WebRtcIsacfix_DecHistOneStepMulti(index_QQ, streamdata, WebRtcIsacfix_kCdfShapePtr[model], WebRtcIsacfix_kInitIndexShape[model], KLT_ORDER_SHAPE); - if (err<0) // error check - return err; - /* find quantization levels for coefficients */ - for (k=0; k>(16-5) = Q21 - pos++; - pos2++; - } - tmpcoeffs2_gQ21[posg] = sumQQ; //Q21 - posg++; - offs2 += 2; - } - offs2 = 0; - - for (k=0; k>7 = Q18 - pos++; - pos2++; - } - tmpcoeffs2_sQ18[poss] = sumQQ; //Q18 - poss++; - offs2 += LPC_SHAPE_ORDER; - } - offsg += 2; - offss += LPC_SHAPE_ORDER; - } - - /* right transform */ // Transpose matrix - offsg = 0; - offss = 0; - posg = 0; - poss = 0; - for (j=0; j>(16-1) = Q21 - pos += 2; - pos2 += SUBFRAMES; - - } - tmpcoeffs_gQ17[posg] = WEBRTC_SPL_RSHIFT_W32(sumQQ, 4); - posg++; - } - poss = offss; - for (k=0; k>16 = Q17 - pos += LPC_SHAPE_ORDER; - pos2 += SUBFRAMES; - } - tmpcoeffs_sQ17[poss] = sumQQ; - poss++; - } - offsg += 2; - offss += LPC_SHAPE_ORDER; - } - - /* scaling, mean addition, and gain restoration */ - gainpos = 0; - posg = 0;poss = 0;pos=0; - for (k=0; k>16 = Q17, with 1/2.1 = 0.47619047619 ~= 31208 in Q16 - tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[model][poss]; // Q17+Q17 = Q17 - LPCCoefQ17[pos] = tmp32; - } - - /* hi band LAR coeffs */ - for (n=0; n>16)<<3 = Q17, with 1/0.45 = 2.222222222222 ~= 18204 in Q13 - tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[model][poss]; // Q17+Q17 = Q17 - LPCCoefQ17[pos] = tmp32; - } - } - - - *outmodel=model; - - return 0; -} - -/* estimate codel length of LPC Coef */ -static int EstCodeLpcCoef(WebRtc_Word32 *LPCCoefQ17, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *model, - WebRtc_Word32 *sizeQ11, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData, - transcode_obj *transcodingParam) { - int j, k, n; - WebRtc_Word16 posQQ, pos2QQ, gainpos; - WebRtc_Word16 pos, pos2, poss, posg, offsg, offss, offs2; - WebRtc_Word16 index_gQQ[KLT_ORDER_GAIN], index_sQQ[KLT_ORDER_SHAPE]; - WebRtc_Word16 index_ovr_gQQ[KLT_ORDER_GAIN], index_ovr_sQQ[KLT_ORDER_SHAPE]; - WebRtc_Word32 BitsQQ; - - WebRtc_Word16 tmpcoeffs_gQ6[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs_gQ17[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs_sQ17[KLT_ORDER_SHAPE]; - WebRtc_Word32 tmpcoeffs2_gQ21[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs2_sQ17[KLT_ORDER_SHAPE]; - WebRtc_Word32 sumQQ; - WebRtc_Word32 tmp32; - WebRtc_Word16 sumQQ16; - int status = 0; - - /* write LAR coefficients to statistics file */ - /* Save data for creation of multiple bitstreams (and transcoding) */ - if (encData != NULL) { - for (k=0; kLPCcoeffs_g[KLT_ORDER_GAIN*encData->startIdx + k] = gain_lo_hiQ17[k]; - } - } - - /* log gains, mean removal and scaling */ - posg = 0;poss = 0;pos=0; gainpos=0; - - for (k=0; k>(16-1) = Q17 - pos++; - pos2 += LPC_SHAPE_ORDER; - } - tmpcoeffs2_sQ17[poss] = sumQQ; //Q17 - poss++; - } - offsg += 2; - offss += LPC_SHAPE_ORDER; - } - - /* right transform */ - offsg = 0; - offss = 0; - offs2 = 0; - for (j=0; j>(16-1) = Q21 - pos += 2; - pos2++; - } - tmpcoeffs_gQ17[posg] = WEBRTC_SPL_RSHIFT_W32(sumQQ, 4); - posg++; - } - poss = offss; - for (k=0; k>(16-1) = Q17 - pos += LPC_SHAPE_ORDER; - pos2++; - } - tmpcoeffs_sQ17[poss] = sumQQ; - poss++; - } - offs2 += SUBFRAMES; - offsg += 2; - offss += LPC_SHAPE_ORDER; - } - - /* quantize coefficients */ - - BitsQQ = 0; - for (k=0; k WebRtcIsacfix_kMaxIndGain[k]) { - index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k]; - } - index_ovr_gQQ[k] = WebRtcIsacfix_kOffsetGain[0][k]+index_gQQ[k]; - posQQ = WebRtcIsacfix_kOfLevelsGain[0] + index_ovr_gQQ[k]; - - /* Save data for creation of multiple bitstreams */ - if (encData != NULL) { - encData->LPCindex_g[KLT_ORDER_GAIN*encData->startIdx + k] = index_gQQ[k]; - } - - /* determine number of bits */ - sumQQ = WebRtcIsacfix_kCodeLenGainQ11[posQQ]; //Q11 - BitsQQ += sumQQ; - } - - for (k=0; k WebRtcIsacfix_kMaxIndShape[k]) - index_sQQ[k] = WebRtcIsacfix_kMaxIndShape[k]; - index_ovr_sQQ[k] = WebRtcIsacfix_kOffsetShape[0][k]+index_sQQ[k]; - - posQQ = WebRtcIsacfix_kOfLevelsShape[0] + index_ovr_sQQ[k]; - sumQQ = WebRtcIsacfix_kCodeLenShapeQ11[posQQ]; //Q11 - BitsQQ += sumQQ; - } - - - - *model = 0; - *sizeQ11=BitsQQ; - - /* entropy coding of model number */ - status = WebRtcIsacfix_EncHistMulti(streamdata, model, WebRtcIsacfix_kModelCdfPtr, 1); - if (status < 0) { - return status; - } - - /* entropy coding of quantization indices - shape only */ - status = WebRtcIsacfix_EncHistMulti(streamdata, index_sQQ, WebRtcIsacfix_kCdfShapePtr[0], KLT_ORDER_SHAPE); - if (status < 0) { - return status; - } - - /* Save data for creation of multiple bitstreams */ - if (encData != NULL) { - for (k=0; kLPCindex_s[KLT_ORDER_SHAPE*encData->startIdx + k] = index_sQQ[k]; - } - } - /* save the state of the bitstream object 'streamdata' for the possible bit-rate reduction */ - transcodingParam->full = streamdata->full; - transcodingParam->stream_index = streamdata->stream_index; - transcodingParam->streamval = streamdata->streamval; - transcodingParam->W_upper = streamdata->W_upper; - transcodingParam->beforeLastWord = streamdata->stream[streamdata->stream_index-1]; - transcodingParam->lastWord = streamdata->stream[streamdata->stream_index]; - - /* entropy coding of index */ - status = WebRtcIsacfix_EncHistMulti(streamdata, index_gQQ, WebRtcIsacfix_kCdfGainPtr[0], KLT_ORDER_GAIN); - if (status < 0) { - return status; - } - - /* find quantization levels for shape coefficients */ - for (k=0; k>(16-1) = Q17 - pos++; - pos2++; - } - tmpcoeffs2_sQ17[poss] = sumQQ; - - poss++; - offs2 += LPC_SHAPE_ORDER; - } - offss += LPC_SHAPE_ORDER; - } - - - /* right transform */ // Transpose matrix - offss = 0; - poss = 0; - for (j=0; j>(16-1) = Q17 - pos += LPC_SHAPE_ORDER; - pos2 += SUBFRAMES; - } - tmpcoeffs_sQ17[poss] = sumQQ; - poss++; - } - offss += LPC_SHAPE_ORDER; - } - - /* scaling, mean addition, and gain restoration */ - poss = 0;pos=0; - for (k=0; k>16 = Q17, with 1/2.1 = 0.47619047619 ~= 31208 in Q16 - tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[0][poss]; // Q17+Q17 = Q17 - LPCCoefQ17[pos] = tmp32; - } - - /* hi band LAR coeffs */ - for (n=0; n>16)<<3 = Q17, with 1/0.45 = 2.222222222222 ~= 18204 in Q13 - tmp32 = tmp32 + WebRtcIsacfix_kMeansShapeQ17[0][poss]; // Q17+Q17 = Q17 - LPCCoefQ17[pos] = tmp32; - } - - } - - //to update tmpcoeffs_gQ17 to the proper state - for (k=0; k>(16-1) = Q17 - pos++; - pos2++; - } - tmpcoeffs2_gQ21[posg] = WEBRTC_SPL_LSHIFT_W32(sumQQ, 4); //Q17<<4 = Q21 - posg++; - offs2 += 2; - } - offsg += 2; - } - - /* right transform */ // Transpose matrix - offsg = 0; - posg = 0; - for (j=0; j>(16-1) = Q21 - pos += 2; - pos2 += SUBFRAMES; - } - tmpcoeffs_gQ17[posg] = WEBRTC_SPL_RSHIFT_W32(sumQQ, 4); - posg++; - } - offsg += 2; - } - - /* scaling, mean addition, and gain restoration */ - posg = 0; - gainpos = 0; - for (k=0; k<2*SUBFRAMES; k++) { - - sumQQ16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmpcoeffs_gQ17[posg], 2+9); //Divide by 4 and get Q17 to Q8, i.e. shift 2+9 - sumQQ16 += WebRtcIsacfix_kMeansGainQ8[0][posg]; - sumQQ = CalcExpN(sumQQ16); // Q8 in and Q17 out - gain_lo_hiQ17[gainpos] = sumQQ; //Q17 - - gainpos++; - pos++;posg++; - } - - return 0; -} - -int WebRtcIsacfix_EstCodeLpcGain(WebRtc_Word32 *gain_lo_hiQ17, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData) { - int j, k, n; - WebRtc_Word16 posQQ, pos2QQ, gainpos; - WebRtc_Word16 pos, pos2, posg, offsg, offs2; - WebRtc_Word16 index_gQQ[KLT_ORDER_GAIN]; - - WebRtc_Word16 tmpcoeffs_gQ6[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs_gQ17[KLT_ORDER_GAIN]; - WebRtc_Word32 tmpcoeffs2_gQ21[KLT_ORDER_GAIN]; - WebRtc_Word32 sumQQ; - int status = 0; - - /* write LAR coefficients to statistics file */ - /* Save data for creation of multiple bitstreams (and transcoding) */ - if (encData != NULL) { - for (k=0; kLPCcoeffs_g[KLT_ORDER_GAIN*encData->startIdx + k] = gain_lo_hiQ17[k]; - } - } - - /* log gains, mean removal and scaling */ - posg = 0; pos = 0; gainpos = 0; - - for (k=0; k>(16-1) = Q21 - pos += 2; - pos2++; - } - tmpcoeffs_gQ17[posg] = WEBRTC_SPL_RSHIFT_W32(sumQQ, 4); - posg++; - } - offsg += 2; - offs2 += SUBFRAMES; - } - - /* quantize coefficients */ - - for (k=0; k WebRtcIsacfix_kMaxIndGain[k]) { - index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k]; - } - - /* Save data for creation of multiple bitstreams */ - if (encData != NULL) { - encData->LPCindex_g[KLT_ORDER_GAIN*encData->startIdx + k] = index_gQQ[k]; - } - } - - /* entropy coding of index */ - status = WebRtcIsacfix_EncHistMulti(streamdata, index_gQQ, WebRtcIsacfix_kCdfGainPtr[0], KLT_ORDER_GAIN); - if (status < 0) { - return status; - } - - return 0; -} - - -int WebRtcIsacfix_EncodeLpc(WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *LPCCoef_loQ15, - WebRtc_Word16 *LPCCoef_hiQ15, - WebRtc_Word16 *model, - WebRtc_Word32 *sizeQ11, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData, - transcode_obj *transcodeParam) -{ - int status = 0; - WebRtc_Word32 larsQ17[KLT_ORDER_SHAPE]; // KLT_ORDER_SHAPE == (ORDERLO+ORDERHI)*SUBFRAMES - // = (6+12)*6 == 108 - - Poly2LarFix(LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES, larsQ17); - - status = EstCodeLpcCoef(larsQ17, gain_lo_hiQ17, model, sizeQ11, streamdata, encData, transcodeParam); - if (status < 0) { - return (status); - } - - Lar2polyFix(larsQ17, LPCCoef_loQ15, ORDERLO, LPCCoef_hiQ15, ORDERHI, SUBFRAMES); - - return 0; -} - - -/* decode & dequantize RC */ -int WebRtcIsacfix_DecodeRcCoef(Bitstr_dec *streamdata, WebRtc_Word16 *RCQ15) -{ - int k, err; - WebRtc_Word16 index[AR_ORDER]; - - /* entropy decoding of quantization indices */ - err = WebRtcIsacfix_DecHistOneStepMulti(index, streamdata, WebRtcIsacfix_kRcCdfPtr, WebRtcIsacfix_kRcInitInd, AR_ORDER); - if (err<0) // error check - return err; - - /* find quantization levels for reflection coefficients */ - for (k=0; k WebRtcIsacfix_kRcBound[index[k]]) - { - while (RCQ15[k] > WebRtcIsacfix_kRcBound[index[k] + 1]) - index[k]++; - } - else - { - while (RCQ15[k] < WebRtcIsacfix_kRcBound[--index[k]]) ; - } - - RCQ15[k] = *(WebRtcIsacfix_kRcLevPtr[k] + index[k]); - } - - - /* entropy coding of quantization indices */ - status = WebRtcIsacfix_EncHistMulti(streamdata, index, WebRtcIsacfix_kRcCdfPtr, AR_ORDER); - - /* If error in WebRtcIsacfix_EncHistMulti(), status will be negative, otherwise 0 */ - return status; -} - - -/* decode & dequantize squared Gain */ -int WebRtcIsacfix_DecodeGain2(Bitstr_dec *streamdata, WebRtc_Word32 *gainQ10) -{ - int err; - WebRtc_Word16 index; - - /* entropy decoding of quantization index */ - err = WebRtcIsacfix_DecHistOneStepMulti( - &index, - streamdata, - WebRtcIsacfix_kGainPtr, - WebRtcIsacfix_kGainInitInd, - 1); - /* error check */ - if (err<0) { - return err; - } - - /* find quantization level */ - *gainQ10 = WebRtcIsacfix_kGain2Lev[index]; - - return 0; -} - - - -/* quantize & code squared Gain */ -int WebRtcIsacfix_EncodeGain2(WebRtc_Word32 *gainQ10, Bitstr_enc *streamdata) -{ - WebRtc_Word16 index; - int status = 0; - - /* find quantization index */ - index = WebRtcIsacfix_kGainInitInd[0]; - if (*gainQ10 > WebRtcIsacfix_kGain2Bound[index]) - { - while (*gainQ10 > WebRtcIsacfix_kGain2Bound[index + 1]) - index++; - } - else - { - while (*gainQ10 < WebRtcIsacfix_kGain2Bound[--index]) ; - } - - /* dequantize */ - *gainQ10 = WebRtcIsacfix_kGain2Lev[index]; - - /* entropy coding of quantization index */ - status = WebRtcIsacfix_EncHistMulti(streamdata, &index, WebRtcIsacfix_kGainPtr, 1); - - /* If error in WebRtcIsacfix_EncHistMulti(), status will be negative, otherwise 0 */ - return status; -} - - -/* code and decode Pitch Gains and Lags functions */ - -/* decode & dequantize Pitch Gains */ -int WebRtcIsacfix_DecodePitchGain(Bitstr_dec *streamdata, WebRtc_Word16 *PitchGains_Q12) -{ - int err; - WebRtc_Word16 index_comb; - const WebRtc_UWord16 *pitch_gain_cdf_ptr[1]; - - /* entropy decoding of quantization indices */ - *pitch_gain_cdf_ptr = WebRtcIsacfix_kPitchGainCdf; - err = WebRtcIsacfix_DecHistBisectMulti(&index_comb, streamdata, pitch_gain_cdf_ptr, WebRtcIsacfix_kCdfTableSizeGain, 1); - /* error check, Q_mean_Gain.. tables are of size 144 */ - if ((err<0) || (index_comb<0) || (index_comb>144)) - return -ISAC_RANGE_ERROR_DECODE_PITCH_GAIN; - - /* unquantize back to pitch gains by table look-up */ - PitchGains_Q12[0] = WebRtcIsacfix_kPitchGain1[index_comb]; - PitchGains_Q12[1] = WebRtcIsacfix_kPitchGain2[index_comb]; - PitchGains_Q12[2] = WebRtcIsacfix_kPitchGain3[index_comb]; - PitchGains_Q12[3] = WebRtcIsacfix_kPitchGain4[index_comb]; - - return 0; -} - - -/* quantize & code Pitch Gains */ -int WebRtcIsacfix_EncodePitchGain(WebRtc_Word16 *PitchGains_Q12, Bitstr_enc *streamdata, ISAC_SaveEncData_t* encData) -{ - int k,j; - WebRtc_Word16 SQ15[PITCH_SUBFRAMES]; - WebRtc_Word16 index[3]; - WebRtc_Word16 index_comb; - const WebRtc_UWord16 *pitch_gain_cdf_ptr[1]; - WebRtc_Word32 CQ17; - int status = 0; - - - /* get the approximate arcsine (almost linear)*/ - for (k=0; k>14); // Rounding and scaling with stepsize (=1/0.125=8) - - /* check that the index is not outside the boundaries of the table */ - if (index[k] < WebRtcIsacfix_kLowerlimiGain[k]) index[k] = WebRtcIsacfix_kLowerlimiGain[k]; - else if (index[k] > WebRtcIsacfix_kUpperlimitGain[k]) index[k] = WebRtcIsacfix_kUpperlimitGain[k]; - index[k] -= WebRtcIsacfix_kLowerlimiGain[k]; - } - - /* calculate unique overall index */ - index_comb = (WebRtc_Word16)(WEBRTC_SPL_MUL(WebRtcIsacfix_kMultsGain[0], index[0]) + - WEBRTC_SPL_MUL(WebRtcIsacfix_kMultsGain[1], index[1]) + index[2]); - - /* unquantize back to pitch gains by table look-up */ - // (Y) - PitchGains_Q12[0] = WebRtcIsacfix_kPitchGain1[index_comb]; - PitchGains_Q12[1] = WebRtcIsacfix_kPitchGain2[index_comb]; - PitchGains_Q12[2] = WebRtcIsacfix_kPitchGain3[index_comb]; - PitchGains_Q12[3] = WebRtcIsacfix_kPitchGain4[index_comb]; - - - /* entropy coding of quantization pitch gains */ - *pitch_gain_cdf_ptr = WebRtcIsacfix_kPitchGainCdf; - status = WebRtcIsacfix_EncHistMulti(streamdata, &index_comb, pitch_gain_cdf_ptr, 1); - if (status < 0) { - return status; - } - - /* Save data for creation of multiple bitstreams */ - if (encData != NULL) { - encData->pitchGain_index[encData->startIdx] = index_comb; - } - - return 0; -} - - - -/* Pitch LAG */ - - -/* decode & dequantize Pitch Lags */ -int WebRtcIsacfix_DecodePitchLag(Bitstr_dec *streamdata, - WebRtc_Word16 *PitchGain_Q12, - WebRtc_Word16 *PitchLags_Q7) -{ - int k, err; - WebRtc_Word16 index[PITCH_SUBFRAMES]; - const WebRtc_Word16 *mean_val2Q10, *mean_val4Q10; - - const WebRtc_Word16 *lower_limit; - const WebRtc_UWord16 *init_index; - const WebRtc_UWord16 *cdf_size; - const WebRtc_UWord16 **cdf; - - WebRtc_Word32 meangainQ12; - WebRtc_Word32 CQ11, CQ10,tmp32a,tmp32b; - WebRtc_Word16 shft,tmp16a,tmp16c; - - meangainQ12=0; - for (k = 0; k < 4; k++) - meangainQ12 += PitchGain_Q12[k]; - - meangainQ12 = WEBRTC_SPL_RSHIFT_W32(meangainQ12, 2); // Get average - - /* voicing classificiation */ - if (meangainQ12 <= 819) { // mean_gain < 0.2 - shft = -1; // StepSize=2.0; - cdf = WebRtcIsacfix_kPitchLagPtrLo; - cdf_size = WebRtcIsacfix_kPitchLagSizeLo; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Lo; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Lo; - lower_limit = WebRtcIsacfix_kLowerLimitLo; - init_index = WebRtcIsacfix_kInitIndLo; - } else if (meangainQ12 <= 1638) { // mean_gain < 0.4 - shft = 0; // StepSize=1.0; - cdf = WebRtcIsacfix_kPitchLagPtrMid; - cdf_size = WebRtcIsacfix_kPitchLagSizeMid; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Mid; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Mid; - lower_limit = WebRtcIsacfix_kLowerLimitMid; - init_index = WebRtcIsacfix_kInitIndMid; - } else { - shft = 1; // StepSize=0.5; - cdf = WebRtcIsacfix_kPitchLagPtrHi; - cdf_size = WebRtcIsacfix_kPitchLagSizeHi; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Hi; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Hi; - lower_limit = WebRtcIsacfix_kLowerLimitHi; - init_index = WebRtcIsacfix_kInitIndHi; - } - - /* entropy decoding of quantization indices */ - err = WebRtcIsacfix_DecHistBisectMulti(index, streamdata, cdf, cdf_size, 1); - if ((err<0) || (index[0]<0)) // error check - return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG; - - err = WebRtcIsacfix_DecHistOneStepMulti(index+1, streamdata, cdf+1, init_index, 3); - if (err<0) // error check - return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG; - - - /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */ - CQ11 = ((WebRtc_Word32)index[0] + lower_limit[0]); // Q0 - CQ11 = WEBRTC_SPL_SHIFT_W32(CQ11,11-shft); // Scale with StepSize, Q11 - for (k=0; kmeanGain[encData->startIdx] = meangainQ12; - } - - /* voicing classificiation */ - if (meangainQ12 <= 819) { // mean_gain < 0.2 - shft = -1; // StepSize=2.0; - cdf = WebRtcIsacfix_kPitchLagPtrLo; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Lo; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Lo; - lower_limit = WebRtcIsacfix_kLowerLimitLo; - upper_limit = WebRtcIsacfix_kUpperLimitLo; - } else if (meangainQ12 <= 1638) { // mean_gain < 0.4 - shft = 0; // StepSize=1.0; - cdf = WebRtcIsacfix_kPitchLagPtrMid; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Mid; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Mid; - lower_limit = WebRtcIsacfix_kLowerLimitMid; - upper_limit = WebRtcIsacfix_kUpperLimitMid; - } else { - shft = 1; // StepSize=0.5; - cdf = WebRtcIsacfix_kPitchLagPtrHi; - mean_val2Q10 = WebRtcIsacfix_kMeanLag2Hi; - mean_val4Q10 = WebRtcIsacfix_kMeanLag4Hi; - lower_limit = WebRtcIsacfix_kLowerLimitHi; - upper_limit = WebRtcIsacfix_kUpperLimitHi; - } - - /* find quantization index */ - for (k=0; k<4; k++) - { - /* transform */ - CQ17=0; - for (j=0; j upper_limit[k]) index[k] = upper_limit[k]; - index[k] -= lower_limit[k]; - - /* Save data for creation of multiple bitstreams */ - if(encData != NULL) { - encData->pitchIndex[PITCH_SUBFRAMES*encData->startIdx + k] = index[k]; - } - } - - /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */ - CQ11 = (index[0] + lower_limit[0]); // Q0 - CQ11 = WEBRTC_SPL_SHIFT_W32(CQ11,11-shft); // Scale with StepSize, Q11 - - for (k=0; k>(16-1) = Q21 - pos += 2; - pos2++; - } - tmpcoeffs_gQ17[posg] = WEBRTC_SPL_RSHIFT_W32(sumQQ, 4); - posg++; - } - offsg += 2; - offs2 += SUBFRAMES; - } - - /* quantize coefficients */ - for (k=0; k WebRtcIsacfix_kMaxIndGain[k]) { - index_gQQ[k] = WebRtcIsacfix_kMaxIndGain[k]; - } - } -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.h b/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.h deleted file mode 100644 index 298ea223a..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/entropy_coding.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * entropy_coding.h - * - * This header file contains all of the functions used to arithmetically - * encode the iSAC bistream - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ENTROPY_CODING_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ENTROPY_CODING_H_ - -#include "structs.h" - -/* decode complex spectrum (return number of bytes in stream) */ -WebRtc_Word16 WebRtcIsacfix_DecodeSpec(Bitstr_dec *streamdata, - WebRtc_Word16 *frQ7, - WebRtc_Word16 *fiQ7, - WebRtc_Word16 AvgPitchGain_Q12); - -/* encode complex spectrum */ -int WebRtcIsacfix_EncodeSpec(const WebRtc_Word16 *fr, - const WebRtc_Word16 *fi, - Bitstr_enc *streamdata, - WebRtc_Word16 AvgPitchGain_Q12); - - -/* decode & dequantize LPC Coef */ -int WebRtcIsacfix_DecodeLpcCoef(Bitstr_dec *streamdata, - WebRtc_Word32 *LPCCoefQ17, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *outmodel); - -int WebRtcIsacfix_DecodeLpc(WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *LPCCoef_loQ15, - WebRtc_Word16 *LPCCoef_hiQ15, - Bitstr_dec *streamdata, - WebRtc_Word16 *outmodel); - -/* quantize & code LPC Coef */ -int WebRtcIsacfix_EncodeLpc(WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *LPCCoef_loQ15, - WebRtc_Word16 *LPCCoef_hiQ15, - WebRtc_Word16 *model, - WebRtc_Word32 *sizeQ11, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData, - transcode_obj *transcodeParam); - -int WebRtcIsacfix_EstCodeLpcGain(WebRtc_Word32 *gain_lo_hiQ17, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData); -/* decode & dequantize RC */ -int WebRtcIsacfix_DecodeRcCoef(Bitstr_dec *streamdata, - WebRtc_Word16 *RCQ15); - -/* quantize & code RC */ -int WebRtcIsacfix_EncodeRcCoef(WebRtc_Word16 *RCQ15, - Bitstr_enc *streamdata); - -/* decode & dequantize squared Gain */ -int WebRtcIsacfix_DecodeGain2(Bitstr_dec *streamdata, - WebRtc_Word32 *Gain2); - -/* quantize & code squared Gain (input is squared gain) */ -int WebRtcIsacfix_EncodeGain2(WebRtc_Word32 *gain2, - Bitstr_enc *streamdata); - -int WebRtcIsacfix_EncodePitchGain(WebRtc_Word16 *PitchGains_Q12, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData); - -int WebRtcIsacfix_EncodePitchLag(WebRtc_Word16 *PitchLagQ7, - WebRtc_Word16 *PitchGain_Q12, - Bitstr_enc *streamdata, - ISAC_SaveEncData_t* encData); - -int WebRtcIsacfix_DecodePitchGain(Bitstr_dec *streamdata, - WebRtc_Word16 *PitchGain_Q12); - -int WebRtcIsacfix_DecodePitchLag(Bitstr_dec *streamdata, - WebRtc_Word16 *PitchGain_Q12, - WebRtc_Word16 *PitchLagQ7); - -int WebRtcIsacfix_DecodeFrameLen(Bitstr_dec *streamdata, - WebRtc_Word16 *framelength); - - -int WebRtcIsacfix_EncodeFrameLen(WebRtc_Word16 framelength, - Bitstr_enc *streamdata); - -int WebRtcIsacfix_DecodeSendBandwidth(Bitstr_dec *streamdata, - WebRtc_Word16 *BWno); - - -int WebRtcIsacfix_EncodeReceiveBandwidth(WebRtc_Word16 *BWno, - Bitstr_enc *streamdata); - -void WebRtcIsacfix_TranscodeLpcCoef(WebRtc_Word32 *tmpcoeffs_gQ6, - WebRtc_Word16 *index_gQQ); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_ENTROPY_CODING_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/fft.c b/modules/audio_coding/codecs/iSAC/fix/source/fft.c deleted file mode 100644 index fff35c4ff..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/fft.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * fft.c - * - * Fast Fourier Transform - * - */ - - -#include "fft.h" - -const WebRtc_Word16 kSortTabFft[240] = { - 0, 60, 120, 180, 20, 80, 140, 200, 40, 100, 160, 220, - 4, 64, 124, 184, 24, 84, 144, 204, 44, 104, 164, 224, - 8, 68, 128, 188, 28, 88, 148, 208, 48, 108, 168, 228, - 12, 72, 132, 192, 32, 92, 152, 212, 52, 112, 172, 232, - 16, 76, 136, 196, 36, 96, 156, 216, 56, 116, 176, 236, - 1, 61, 121, 181, 21, 81, 141, 201, 41, 101, 161, 221, - 5, 65, 125, 185, 25, 85, 145, 205, 45, 105, 165, 225, - 9, 69, 129, 189, 29, 89, 149, 209, 49, 109, 169, 229, - 13, 73, 133, 193, 33, 93, 153, 213, 53, 113, 173, 233, - 17, 77, 137, 197, 37, 97, 157, 217, 57, 117, 177, 237, - 2, 62, 122, 182, 22, 82, 142, 202, 42, 102, 162, 222, - 6, 66, 126, 186, 26, 86, 146, 206, 46, 106, 166, 226, - 10, 70, 130, 190, 30, 90, 150, 210, 50, 110, 170, 230, - 14, 74, 134, 194, 34, 94, 154, 214, 54, 114, 174, 234, - 18, 78, 138, 198, 38, 98, 158, 218, 58, 118, 178, 238, - 3, 63, 123, 183, 23, 83, 143, 203, 43, 103, 163, 223, - 7, 67, 127, 187, 27, 87, 147, 207, 47, 107, 167, 227, - 11, 71, 131, 191, 31, 91, 151, 211, 51, 111, 171, 231, - 15, 75, 135, 195, 35, 95, 155, 215, 55, 115, 175, 235, - 19, 79, 139, 199, 39, 99, 159, 219, 59, 119, 179, 239 -}; - -/* Cosine table in Q14 */ -const WebRtc_Word16 kCosTabFfftQ14[240] = { - 16384, 16378, 16362, 16333, 16294, 16244, 16182, 16110, 16026, 15931, 15826, 15709, - 15582, 15444, 15296, 15137, 14968, 14788, 14598, 14399, 14189, 13970, 13741, 13502, - 13255, 12998, 12733, 12458, 12176, 11885, 11585, 11278, 10963, 10641, 10311, 9974, - 9630, 9280, 8923, 8561, 8192, 7818, 7438, 7053, 6664, 6270, 5872, 5469, - 5063, 4653, 4240, 3825, 3406, 2986, 2563, 2139, 1713, 1285, 857, 429, - 0, -429, -857, -1285, -1713, -2139, -2563, -2986, -3406, -3825, -4240, -4653, - -5063, -5469, -5872, -6270, -6664, -7053, -7438, -7818, -8192, -8561, -8923, -9280, - -9630, -9974, -10311, -10641, -10963, -11278, -11585, -11885, -12176, -12458, -12733, -12998, - -13255, -13502, -13741, -13970, -14189, -14399, -14598, -14788, -14968, -15137, -15296, -15444, - -15582, -15709, -15826, -15931, -16026, -16110, -16182, -16244, -16294, -16333, -16362, -16378, - -16384, -16378, -16362, -16333, -16294, -16244, -16182, -16110, -16026, -15931, -15826, -15709, - -15582, -15444, -15296, -15137, -14968, -14788, -14598, -14399, -14189, -13970, -13741, -13502, - -13255, -12998, -12733, -12458, -12176, -11885, -11585, -11278, -10963, -10641, -10311, -9974, - -9630, -9280, -8923, -8561, -8192, -7818, -7438, -7053, -6664, -6270, -5872, -5469, - -5063, -4653, -4240, -3825, -3406, -2986, -2563, -2139, -1713, -1285, -857, -429, - 0, 429, 857, 1285, 1713, 2139, 2563, 2986, 3406, 3825, 4240, 4653, - 5063, 5469, 5872, 6270, 6664, 7053, 7438, 7818, 8192, 8561, 8923, 9280, - 9630, 9974, 10311, 10641, 10963, 11278, 11585, 11885, 12176, 12458, 12733, 12998, - 13255, 13502, 13741, 13970, 14189, 14399, 14598, 14788, 14968, 15137, 15296, 15444, - 15582, 15709, 15826, 15931, 16026, 16110, 16182, 16244, 16294, 16333, 16362, 16378 -}; - - - -/* Uses 16x16 mul, without rounding, which is faster. Uses WEBRTC_SPL_MUL_16_16_RSFT */ -WebRtc_Word16 WebRtcIsacfix_FftRadix16Fastest(WebRtc_Word16 RexQx[], WebRtc_Word16 ImxQx[], WebRtc_Word16 iSign) { - - WebRtc_Word16 dd, ee, ff, gg, hh, ii; - WebRtc_Word16 k0, k1, k2, k3, k4, kk; - WebRtc_Word16 tmp116, tmp216; - - WebRtc_Word16 ccc1Q14, ccc2Q14, ccc3Q14, sss1Q14, sss2Q14, sss3Q14; - WebRtc_Word16 sss60Q14, ccc72Q14, sss72Q14; - WebRtc_Word16 aaQx, ajQx, akQx, ajmQx, ajpQx, akmQx, akpQx; - WebRtc_Word16 bbQx, bjQx, bkQx, bjmQx, bjpQx, bkmQx, bkpQx; - - WebRtc_Word16 ReDATAQx[240], ImDATAQx[240]; - - sss60Q14 = kCosTabFfftQ14[20]; - ccc72Q14 = kCosTabFfftQ14[48]; - sss72Q14 = kCosTabFfftQ14[12]; - - if (iSign < 0) { - sss72Q14 = -sss72Q14; - sss60Q14 = -sss60Q14; - } - /* Complexity is: 10 cycles */ - - /* compute fourier transform */ - - // transform for factor of 4 - for (kk=0; kk<60; kk++) { - k0 = kk; - k1 = k0 + 60; - k2 = k1 + 60; - k3 = k2 + 60; - - akpQx = RexQx[k0] + RexQx[k2]; - akmQx = RexQx[k0] - RexQx[k2]; - ajpQx = RexQx[k1] + RexQx[k3]; - ajmQx = RexQx[k1] - RexQx[k3]; - bkpQx = ImxQx[k0] + ImxQx[k2]; - bkmQx = ImxQx[k0] - ImxQx[k2]; - bjpQx = ImxQx[k1] + ImxQx[k3]; - bjmQx = ImxQx[k1] - ImxQx[k3]; - - RexQx[k0] = akpQx + ajpQx; - ImxQx[k0] = bkpQx + bjpQx; - ajpQx = akpQx - ajpQx; - bjpQx = bkpQx - bjpQx; - if (iSign < 0) { - akpQx = akmQx + bjmQx; - bkpQx = bkmQx - ajmQx; - akmQx -= bjmQx; - bkmQx += ajmQx; - } else { - akpQx = akmQx - bjmQx; - bkpQx = bkmQx + ajmQx; - akmQx += bjmQx; - bkmQx -= ajmQx; - } - - ccc1Q14 = kCosTabFfftQ14[kk]; - ccc2Q14 = kCosTabFfftQ14[WEBRTC_SPL_MUL_16_16(2, kk)]; - ccc3Q14 = kCosTabFfftQ14[WEBRTC_SPL_MUL_16_16(3, kk)]; - sss1Q14 = kCosTabFfftQ14[kk+60]; - sss2Q14 = kCosTabFfftQ14[WEBRTC_SPL_MUL_16_16(2, kk)+60]; - sss3Q14 = kCosTabFfftQ14[WEBRTC_SPL_MUL_16_16(3, kk)+60]; - if (iSign==1) { - sss1Q14 = -sss1Q14; - sss2Q14 = -sss2Q14; - sss3Q14 = -sss3Q14; - } - - //Do several multiplications like Q14*Q16>>14 = Q16 - // RexQ16[k1] = akpQ16 * ccc1Q14 - bkpQ16 * sss1Q14; - // RexQ16[k2] = ajpQ16 * ccc2Q14 - bjpQ16 * sss2Q14; - // RexQ16[k3] = akmQ16 * ccc3Q14 - bkmQ16 * sss3Q14; - // ImxQ16[k1] = akpQ16 * sss1Q14 + bkpQ16 * ccc1Q14; - // ImxQ16[k2] = ajpQ16 * sss2Q14 + bjpQ16 * ccc2Q14; - // ImxQ16[k3] = akmQ16 * sss3Q14 + bkmQ16 * ccc3Q14; - - RexQx[k1] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc1Q14, akpQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss1Q14, bkpQx, 14); // 6 non-mul + 2 mul cycles, i.e. 8 cycles (6+2*7=20 cycles if 16x32mul) - RexQx[k2] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, ajpQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, bjpQx, 14); - RexQx[k3] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc3Q14, akmQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss3Q14, bkmQx, 14); - ImxQx[k1] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss1Q14, akpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc1Q14, bkpQx, 14); - ImxQx[k2] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, ajpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, bjpQx, 14); - ImxQx[k3] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss3Q14, akmQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc3Q14, bkmQx, 14); - //This mul segment needs 6*8 = 48 cycles for 16x16 muls, but 6*20 = 120 cycles for 16x32 muls - - - } - /* Complexity is: 51+48 = 99 cycles for 16x16 muls, but 51+120 = 171 cycles for 16x32 muls*/ - - // transform for factor of 3 - kk=0; - k1=20; - k2=40; - - for (hh=0; hh<4; hh++) { - for (ii=0; ii<20; ii++) { - akQx = RexQx[kk]; - bkQx = ImxQx[kk]; - ajQx = RexQx[k1] + RexQx[k2]; - bjQx = ImxQx[k1] + ImxQx[k2]; - RexQx[kk] = akQx + ajQx; - ImxQx[kk] = bkQx + bjQx; - tmp116 = WEBRTC_SPL_RSHIFT_W16(ajQx, 1); - tmp216 = WEBRTC_SPL_RSHIFT_W16(bjQx, 1); - akQx = akQx - tmp116; - bkQx = bkQx - tmp216; - tmp116 = RexQx[k1] - RexQx[k2]; - tmp216 = ImxQx[k1] - ImxQx[k2]; - - ajQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss60Q14, tmp116, 14); // Q14*Qx>>14 = Qx - bjQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss60Q14, tmp216, 14); // Q14*Qx>>14 = Qx - RexQx[k1] = akQx - bjQx; - RexQx[k2] = akQx + bjQx; - ImxQx[k1] = bkQx + ajQx; - ImxQx[k2] = bkQx - ajQx; - - kk++; - k1++; - k2++; - } - /* Complexity : (31+6)*20 = 740 cycles for 16x16 muls, but (31+18)*20 = 980 cycles for 16x32 muls*/ - kk=kk+40; - k1=k1+40; - k2=k2+40; - } - /* Complexity : 4*(740+3) = 2972 cycles for 16x16 muls, but 4*(980+3) = 3932 cycles for 16x32 muls*/ - - /* multiply by rotation factor for odd factor 3 or 5 (not for 4) - Same code (duplicated) for both ii=2 and ii=3 */ - kk = 1; - ee = 0; - ff = 0; - - for (gg=0; gg<19; gg++) { - kk += 20; - ff = ff+4; - for (hh=0; hh<2; hh++) { - ee = ff + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(hh, ff); - dd = ee + 60; - ccc2Q14 = kCosTabFfftQ14[ee]; - sss2Q14 = kCosTabFfftQ14[dd]; - if (iSign==1) { - sss2Q14 = -sss2Q14; - } - for (ii=0; ii<4; ii++) { - akQx = RexQx[kk]; - bkQx = ImxQx[kk]; - RexQx[kk] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, akQx, 14) - // Q14*Qx>>14 = Qx - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, bkQx, 14); - ImxQx[kk] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, akQx, 14) + // Q14*Qx>>14 = Qx - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, bkQx, 14); - - - kk += 60; - } - kk = kk - 220; - } - // Complexity: 2*(13+5+4*13+2) = 144 for 16x16 muls, but 2*(13+5+4*33+2) = 304 cycles for 16x32 muls - kk = kk - 59; - } - // Complexity: 19*144 = 2736 for 16x16 muls, but 19*304 = 5776 cycles for 16x32 muls - - // transform for factor of 5 - kk = 0; - ccc2Q14 = kCosTabFfftQ14[96]; - sss2Q14 = kCosTabFfftQ14[84]; - if (iSign==1) { - sss2Q14 = -sss2Q14; - } - - for (hh=0; hh<4; hh++) { - for (ii=0; ii<12; ii++) { - k1 = kk + 4; - k2 = k1 + 4; - k3 = k2 + 4; - k4 = k3 + 4; - - akpQx = RexQx[k1] + RexQx[k4]; - akmQx = RexQx[k1] - RexQx[k4]; - bkpQx = ImxQx[k1] + ImxQx[k4]; - bkmQx = ImxQx[k1] - ImxQx[k4]; - ajpQx = RexQx[k2] + RexQx[k3]; - ajmQx = RexQx[k2] - RexQx[k3]; - bjpQx = ImxQx[k2] + ImxQx[k3]; - bjmQx = ImxQx[k2] - ImxQx[k3]; - aaQx = RexQx[kk]; - bbQx = ImxQx[kk]; - RexQx[kk] = aaQx + akpQx + ajpQx; - ImxQx[kk] = bbQx + bkpQx + bjpQx; - - akQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc72Q14, akpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, ajpQx, 14) + aaQx; - bkQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc72Q14, bkpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, bjpQx, 14) + bbQx; - ajQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss72Q14, akmQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, ajmQx, 14); - bjQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss72Q14, bkmQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, bjmQx, 14); - // 32+4*8=64 or 32+4*20=112 - - RexQx[k1] = akQx - bjQx; - RexQx[k4] = akQx + bjQx; - ImxQx[k1] = bkQx + ajQx; - ImxQx[k4] = bkQx - ajQx; - - akQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, akpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc72Q14, ajpQx, 14) + aaQx; - bkQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, bkpQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc72Q14, bjpQx, 14) + bbQx; - ajQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, akmQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss72Q14, ajmQx, 14); - bjQx = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, bkmQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss72Q14, bjmQx, 14); - // 8+4*8=40 or 8+4*20=88 - - RexQx[k2] = akQx - bjQx; - RexQx[k3] = akQx + bjQx; - ImxQx[k2] = bkQx + ajQx; - ImxQx[k3] = bkQx - ajQx; - - kk = k4 + 4; - } - // Complexity: 12*(64+40+10) = 1368 for 16x16 muls, but 12*(112+88+10) = 2520 cycles for 16x32 muls - kk -= 239; - } - // Complexity: 4*1368 = 5472 for 16x16 muls, but 4*2520 = 10080 cycles for 16x32 muls - - /* multiply by rotation factor for odd factor 3 or 5 (not for 4) - Same code (duplicated) for both ii=2 and ii=3 */ - kk = 1; - ee=0; - - for (gg=0; gg<3; gg++) { - kk += 4; - dd = 12 + (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(12, gg); - ff = 0; - for (hh=0; hh<4; hh++) { - ff = ff+dd; - ee = ff+60; - for (ii=0; ii<12; ii++) { - akQx = RexQx[kk]; - bkQx = ImxQx[kk]; - - ccc2Q14 = kCosTabFfftQ14[ff]; - sss2Q14 = kCosTabFfftQ14[ee]; - - if (iSign==1) { - sss2Q14 = -sss2Q14; - } - - RexQx[kk] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, akQx, 14) - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, bkQx, 14); - ImxQx[kk] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(sss2Q14, akQx, 14) + - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(ccc2Q14, bkQx, 14); - - kk += 20; - } - kk = kk - 236; - // Complexity: 12*(12+12) = 288 for 16x16 muls, but 12*(12+32) = 528 cycles for 16x32 muls - } - kk = kk - 19; - // Complexity: 4*288+6 for 16x16 muls, but 4*528+6 cycles for 16x32 muls - } - // Complexity: 3*4*288+6 = 3462 for 16x16 muls, but 3*4*528+6 = 6342 cycles for 16x32 muls - - - // last transform for factor of 4 */ - for (kk=0; kk<240; kk=kk+4) { - k1 = kk + 1; - k2 = k1 + 1; - k3 = k2 + 1; - - akpQx = RexQx[kk] + RexQx[k2]; - akmQx = RexQx[kk] - RexQx[k2]; - ajpQx = RexQx[k1] + RexQx[k3]; - ajmQx = RexQx[k1] - RexQx[k3]; - bkpQx = ImxQx[kk] + ImxQx[k2]; - bkmQx = ImxQx[kk] - ImxQx[k2]; - bjpQx = ImxQx[k1] + ImxQx[k3]; - bjmQx = ImxQx[k1] - ImxQx[k3]; - RexQx[kk] = akpQx + ajpQx; - ImxQx[kk] = bkpQx + bjpQx; - ajpQx = akpQx - ajpQx; - bjpQx = bkpQx - bjpQx; - if (iSign < 0) { - akpQx = akmQx + bjmQx; - bkpQx = bkmQx - ajmQx; - akmQx -= bjmQx; - bkmQx += ajmQx; - } else { - akpQx = akmQx - bjmQx; - bkpQx = bkmQx + ajmQx; - akmQx += bjmQx; - bkmQx -= ajmQx; - } - RexQx[k1] = akpQx; - RexQx[k2] = ajpQx; - RexQx[k3] = akmQx; - ImxQx[k1] = bkpQx; - ImxQx[k2] = bjpQx; - ImxQx[k3] = bkmQx; - } - // Complexity: 60*45 = 2700 for 16x16 muls, but 60*45 = 2700 cycles for 16x32 muls - - /* permute the results to normal order */ - for (ii=0; ii<240; ii++) { - ReDATAQx[ii]=RexQx[ii]; - ImDATAQx[ii]=ImxQx[ii]; - } - // Complexity: 240*2=480 cycles - - for (ii=0; ii<240; ii++) { - RexQx[ii]=ReDATAQx[kSortTabFft[ii]]; - ImxQx[ii]=ImDATAQx[kSortTabFft[ii]]; - } - // Complexity: 240*2*2=960 cycles - - // Total complexity: - // 16x16 16x32 - // Complexity: 10 10 - // Complexity: 99 171 - // Complexity: 2972 3932 - // Complexity: 2736 5776 - // Complexity: 5472 10080 - // Complexity: 3462 6342 - // Complexity: 2700 2700 - // Complexity: 480 480 - // Complexity: 960 960 - // ======================= - // 18891 30451 - // - // If this FFT is called 2 time each frame, i.e. 67 times per second, it will correspond to - // a C54 complexity of 67*18891/1000000 = 1.27 MIPS with 16x16-muls, and 67*30451/1000000 = - // = 2.04 MIPS with 16x32-muls. Note that this routine somtimes is called 6 times during the - // encoding of a frame, i.e. the max complexity would be 7/2*1.27 = 4.4 MIPS for the 16x16 mul case. - - - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/fft.h b/modules/audio_coding/codecs/iSAC/fix/source/fft.h deleted file mode 100644 index efa116e70..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/fft.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/*--------------------------------*-C-*---------------------------------* - * File: - * fft.h - * ---------------------------------------------------------------------* - * Re[]: real value array - * Im[]: imaginary value array - * nTotal: total number of complex values - * nPass: number of elements involved in this pass of transform - * nSpan: nspan/nPass = number of bytes to increment pointer - * in Re[] and Im[] - * isign: exponent: +1 = forward -1 = reverse - * scaling: normalizing constant by which the final result is *divided* - * scaling == -1, normalize by total dimension of the transform - * scaling < -1, normalize by the square-root of the total dimension - * - * ---------------------------------------------------------------------- - * See the comments in the code for correct usage! - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FFT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FFT_H_ - - -#include "structs.h" - - -WebRtc_Word16 WebRtcIsacfix_FftRadix16Fastest(WebRtc_Word16 RexQx[], WebRtc_Word16 ImxQx[], WebRtc_Word16 iSign); - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FFT_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.c b/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.c deleted file mode 100644 index 87c62aab3..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * filterbank_tables.c - * - * This file contains variables that are used in - * filterbanks.c - * - */ - -#include "filterbank_tables.h" -#include "settings.h" - - -/* HPstcoeff_in_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; - * In float, they are: - * {-1.94895953203325f, 0.94984516000000f, -0.05101826139794f, 0.05015484000000f}; - */ -const WebRtc_Word16 WebRtcIsacfix_kHpStCoeffInQ30[8] = { - -31932, 16189, /* Q30 hi/lo pair */ - 15562, 17243, /* Q30 hi/lo pair */ - -26748, -17186, /* Q35 hi/lo pair */ - 26296, -27476 /* Q35 hi/lo pair */ -}; - -/* HPstcoeff_out_1_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; - * In float, they are: - * {-1.99701049409000f, 0.99714204490000f, 0.01701049409000f, -0.01704204490000f}; - */ -const WebRtc_Word16 WebRtcIsacfix_kHPStCoeffOut1Q30[8] = { - -32719, -1306, /* Q30 hi/lo pair */ - 16337, 11486, /* Q30 hi/lo pair */ - 8918, 26078, /* Q35 hi/lo pair */ - -8935, 3956 /* Q35 hi/lo pair */ -}; - -/* HPstcoeff_out_2_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; - * In float, they are: - * {-1.98645294509837f, 0.98672435560000f, 0.00645294509837f, -0.00662435560000f}; - */ -const WebRtc_Word16 WebRtcIsacfix_kHPStCoeffOut2Q30[8] = { - -32546, -2953, /* Q30 hi/lo pair */ - 16166, 32233, /* Q30 hi/lo pair */ - 3383, 13217, /* Q35 hi/lo pair */ - -3473, -4597 /* Q35 hi/lo pair */ -}; - -/* The upper channel all-pass filter factors */ -const WebRtc_Word16 WebRtcIsacfix_kUpperApFactorsQ15[2] = { - 1137, 12537 -}; - -/* The lower channel all-pass filter factors */ -const WebRtc_Word16 WebRtcIsacfix_kLowerApFactorsQ15[2] = { - 5059, 24379 -}; diff --git a/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.h b/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.h deleted file mode 100644 index b6be4f091..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/filterbank_tables.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * filterbank_tables.h - * - * Header file for variables that are defined in - * filterbank_tables.c. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_TABLES_H_ - -#include "typedefs.h" - -/********************* Coefficient Tables ************************/ - -/* HPstcoeff_in_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; */ -extern const WebRtc_Word16 WebRtcIsacfix_kHpStCoeffInQ30[8]; /* [Q30hi Q30lo Q30hi Q30lo Q35hi Q35lo Q35hi Q35lo] */ - -/* HPstcoeff_out_1_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; */ -extern const WebRtc_Word16 WebRtcIsacfix_kHPStCoeffOut1Q30[8]; /* [Q30hi Q30lo Q30hi Q30lo Q35hi Q35lo Q35hi Q35lo] */ - -/* HPstcoeff_out_2_Q14 = {a1, a2, b1 - b0 * a1, b2 - b0 * a2}; */ -extern const WebRtc_Word16 WebRtcIsacfix_kHPStCoeffOut2Q30[8]; /* [Q30hi Q30lo Q30hi Q30lo Q35hi Q35lo Q35hi Q35lo] */ - -/* The upper channel all-pass filter factors */ -extern const WebRtc_Word16 WebRtcIsacfix_kUpperApFactorsQ15[2]; - -/* The lower channel all-pass filter factors */ -extern const WebRtc_Word16 WebRtcIsacfix_kLowerApFactorsQ15[2]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_FILTERBANK_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/filterbanks.c b/modules/audio_coding/codecs/iSAC/fix/source/filterbanks.c deleted file mode 100644 index ac412b460..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/filterbanks.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * filterbanks.c - * - * This file contains function - * WebRtcIsacfix_SplitAndFilter, and WebRtcIsacfix_FilterAndCombine - * which implement filterbanks that produce decimated lowpass and - * highpass versions of a signal, and performs reconstruction. - * - */ - -#include "codec.h" -#include "filterbank_tables.h" -#include "settings.h" - - -static void AllpassFilter2FixDec16(WebRtc_Word16 *InOut16, //Q0 - const WebRtc_Word16 *APSectionFactors, //Q15 - WebRtc_Word16 lengthInOut, - WebRtc_Word16 NumberOfSections, - WebRtc_Word32 *FilterState) //Q16 -{ - int n, j; - WebRtc_Word32 a, b; - - for (j=0; j Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, FilterState[j]); //Q16+Q16=Q16 - a = WEBRTC_SPL_MUL_16_16_RSFT(-APSectionFactors[j], (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(b, 16), 0); //Q15*Q0=Q15 - FilterState[j] = WEBRTC_SPL_ADD_SAT_W32(WEBRTC_SPL_LSHIFT_W32(a,1), WEBRTC_SPL_LSHIFT_W32((WebRtc_UWord32)InOut16[n],16)); // Q15<<1 + Q0<<16 = Q16 + Q16 = Q16 - InOut16[n] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(b, 16); //Save as Q0 - - } - } - -} - - -static void HighpassFilterFixDec32( - WebRtc_Word16 *io, /* Q0:input Q0: Output */ - WebRtc_Word16 len, /* length of input, Input */ - const WebRtc_Word16 *coeff, /* Coeff: [Q30hi Q30lo Q30hi Q30lo Q35hi Q35lo Q35hi Q35lo] */ - WebRtc_Word32 *state) /* Q4:filter state Input/Output */ -{ - int k; - WebRtc_Word32 a, b, c, in; - - - - for (k=0; k Q7 */ - a = WEBRTC_SPL_MUL_32_32_RSFT32(coeff[2*2], coeff[2*2+1], state[0]); - b = WEBRTC_SPL_MUL_32_32_RSFT32(coeff[2*3], coeff[2*3+1], state[1]); - - c = ((WebRtc_Word32)in) + WEBRTC_SPL_RSHIFT_W32(a+b, 7); // Q0 - //c = WEBRTC_SPL_RSHIFT_W32(c, 1); // Q-1 - io[k] = (WebRtc_Word16)WEBRTC_SPL_SAT((WebRtc_Word32)32767, c, (WebRtc_Word32)-32768); // Write output as Q0 - - /* Q30 * Q4 = Q34 ; shift 32 bit => Q2 */ - a = WEBRTC_SPL_MUL_32_32_RSFT32(coeff[2*0], coeff[2*0+1], state[0]); - b = WEBRTC_SPL_MUL_32_32_RSFT32(coeff[2*1], coeff[2*1+1], state[1]); - - c = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)in, 2) - a - b; // New state in Q2 - c= (WebRtc_Word32)WEBRTC_SPL_SAT((WebRtc_Word32)536870911, c, (WebRtc_Word32)-536870912); // Check for wrap-around - - state[1] = state[0]; - state[0] = WEBRTC_SPL_LSHIFT_W32(c, 2); // Write state as Q4 - - } -} - - -void WebRtcIsacfix_SplitAndFilter1(WebRtc_Word16 *pin, - WebRtc_Word16 *LP16, - WebRtc_Word16 *HP16, - PreFiltBankstr *prefiltdata) -{ - /* Function WebRtcIsacfix_SplitAndFilter */ - /* This function creates low-pass and high-pass decimated versions of part of - the input signal, and part of the signal in the input 'lookahead buffer'. */ - - int k; - - WebRtc_Word16 tempin_ch1[FRAMESAMPLES/2 + QLOOKAHEAD]; - WebRtc_Word16 tempin_ch2[FRAMESAMPLES/2 + QLOOKAHEAD]; - WebRtc_Word32 tmpState[WEBRTC_SPL_MUL_16_16(2,(QORDER-1))]; /* 4 */ - - - /* High pass filter */ - HighpassFilterFixDec32(pin, FRAMESAMPLES, WebRtcIsacfix_kHpStCoeffInQ30, prefiltdata->HPstates_fix); - - - /* First Channel */ - for (k=0;kINLABUF1_fix[k]; - prefiltdata->INLABUF1_fix[k]=pin[FRAMESAMPLES+1-WEBRTC_SPL_MUL_16_16(2, QLOOKAHEAD)+WEBRTC_SPL_MUL_16_16(2, k)]; - } - - /* Second Channel. This is exactly like the first channel, except that the - even samples are now filtered instead (lower channel). */ - for (k=0;kINLABUF2_fix[k]; - prefiltdata->INLABUF2_fix[k]=pin[FRAMESAMPLES-WEBRTC_SPL_MUL_16_16(2, QLOOKAHEAD)+WEBRTC_SPL_MUL_16_16(2, k)]; - } - - - /*obtain polyphase components by forward all-pass filtering through each channel */ - /* The all pass filtering automatically updates the filter states which are exported in the - prefiltdata structure */ - AllpassFilter2FixDec16(tempin_ch1,WebRtcIsacfix_kUpperApFactorsQ15, FRAMESAMPLES/2 , NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT1_fix); - AllpassFilter2FixDec16(tempin_ch2,WebRtcIsacfix_kLowerApFactorsQ15, FRAMESAMPLES/2 , NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT2_fix); - - for (k=0;kINSTAT1_fix[k]; - AllpassFilter2FixDec16(tempin_ch1 + FRAMESAMPLES/2,WebRtcIsacfix_kUpperApFactorsQ15, QLOOKAHEAD , NUMBEROFCHANNELAPSECTIONS, tmpState); - for (k=0;kINSTAT2_fix[k]; - AllpassFilter2FixDec16(tempin_ch2 + FRAMESAMPLES/2,WebRtcIsacfix_kLowerApFactorsQ15, QLOOKAHEAD , NUMBEROFCHANNELAPSECTIONS, tmpState); - - - /* Now Construct low-pass and high-pass signals as combinations of polyphase components */ - for (k=0; k Q0 - tmp2 = (WebRtc_Word32)tempin_ch2[k]; // Q0 -> Q0 - tmp3 = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_W32((tmp1 + tmp2), 1);/* low pass signal*/ - LP16[k] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp3, -32768); /*low pass */ - tmp3 = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_W32((tmp1 - tmp2), 1);/* high pass signal*/ - HP16[k] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp3, -32768); /*high pass */ - } - -}/*end of WebRtcIsacfix_SplitAndFilter */ - - -#ifdef NB_CALLS - -/* Without lookahead */ -void WebRtcIsacfix_SplitAndFilter2(WebRtc_Word16 *pin, - WebRtc_Word16 *LP16, - WebRtc_Word16 *HP16, - PreFiltBankstr *prefiltdata) -{ - /* Function WebRtcIsacfix_SplitAndFilter2 */ - /* This function creates low-pass and high-pass decimated versions of part of - the input signal. */ - - int k; - - WebRtc_Word16 tempin_ch1[FRAMESAMPLES/2]; - WebRtc_Word16 tempin_ch2[FRAMESAMPLES/2]; - - - /* High pass filter */ - HighpassFilterFixDec32(pin, FRAMESAMPLES, WebRtcIsacfix_kHpStCoeffInQ30, prefiltdata->HPstates_fix); - - - /* First Channel */ - for (k=0;kINSTAT1_fix); - AllpassFilter2FixDec16(tempin_ch2,WebRtcIsacfix_kLowerApFactorsQ15, FRAMESAMPLES/2 , NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT2_fix); - - - /* Now Construct low-pass and high-pass signals as combinations of polyphase components */ - for (k=0; k Q0 - tmp2 = (WebRtc_Word32)tempin_ch2[k]; // Q0 -> Q0 - tmp3 = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_W32((tmp1 + tmp2), 1);/* low pass signal*/ - LP16[k] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp3, -32768); /*low pass */ - tmp3 = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_W32((tmp1 - tmp2), 1);/* high pass signal*/ - HP16[k] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp3, -32768); /*high pass */ - } - -}/*end of WebRtcIsacfix_SplitAndFilter */ - -#endif - - - -////////////////////////////////////////////////////////// -////////// Combining -/* Function WebRtcIsacfix_FilterAndCombine */ -/* This is a decoder function that takes the decimated - length FRAMESAMPLES/2 input low-pass and - high-pass signals and creates a reconstructed fullband - output signal of length FRAMESAMPLES. WebRtcIsacfix_FilterAndCombine - is the sibling function of WebRtcIsacfix_SplitAndFilter */ -/* INPUTS: - inLP: a length FRAMESAMPLES/2 array of input low-pass - samples. - inHP: a length FRAMESAMPLES/2 array of input high-pass - samples. - postfiltdata: input data structure containing the filterbank - states from the previous decoding iteration. - OUTPUTS: - Out: a length FRAMESAMPLES array of output reconstructed - samples (fullband) based on the input low-pass and - high-pass signals. - postfiltdata: the input data structure containing the filterbank - states is updated for the next decoding iteration */ -void WebRtcIsacfix_FilterAndCombine1(WebRtc_Word16 *tempin_ch1, - WebRtc_Word16 *tempin_ch2, - WebRtc_Word16 *out16, - PostFiltBankstr *postfiltdata) -{ - int k; - WebRtc_Word16 in[FRAMESAMPLES]; - - /* all-pass filter the new upper channel signal. HOWEVER, use the all-pass filter factors - that were used as a lower channel at the encoding side. So at the decoder, the - corresponding all-pass filter factors for each channel are swapped.*/ - - AllpassFilter2FixDec16(tempin_ch1, WebRtcIsacfix_kLowerApFactorsQ15, FRAMESAMPLES/2, NUMBEROFCHANNELAPSECTIONS,postfiltdata->STATE_0_UPPER_fix); - - /* Now, all-pass filter the new lower channel signal. But since all-pass filter factors - at the decoder are swapped from the ones at the encoder, the 'upper' channel - all-pass filter factors (kUpperApFactors) are used to filter this new lower channel signal */ - - AllpassFilter2FixDec16(tempin_ch2, WebRtcIsacfix_kUpperApFactorsQ15, FRAMESAMPLES/2, NUMBEROFCHANNELAPSECTIONS,postfiltdata->STATE_0_LOWER_fix); - - /* Merge outputs to form the full length output signal.*/ - for (k=0;kHPstates1_fix); - HighpassFilterFixDec32(in, FRAMESAMPLES, WebRtcIsacfix_kHPStCoeffOut2Q30, postfiltdata->HPstates2_fix); - - for (k=0;kSTATE_0_UPPER_fix); - - /* Now, all-pass filter the new lower channel signal. But since all-pass filter factors - at the decoder are swapped from the ones at the encoder, the 'upper' channel - all-pass filter factors (kUpperApFactors) are used to filter this new lower channel signal */ - - AllpassFilter2FixDec16(tempin_ch2, WebRtcIsacfix_kUpperApFactorsQ15, (WebRtc_Word16) (len/2), NUMBEROFCHANNELAPSECTIONS,postfiltdata->STATE_0_LOWER_fix); - - /* Merge outputs to form the full length output signal.*/ - for (k=0;kHPstates1_fix); - HighpassFilterFixDec32(in, len, WebRtcIsacfix_kHPStCoeffOut2Q30, postfiltdata->HPstates2_fix); - - for (k=0;k - -#include "pitch_estimator.h" -#include "lpc_masking_model.h" -#include "codec.h" - - -/* Autocorrelation function in fixed point. NOTE! Different from SPLIB-version in how it scales the signal. */ -int WebRtcIsacfix_AutocorrFix( - WebRtc_Word32 *r, - const WebRtc_Word16 *x, - WebRtc_Word16 N, - WebRtc_Word16 order, - WebRtc_Word16 *scale) -{ - int j, i; - WebRtc_Word16 scaling; - WebRtc_Word32 sum, prod, newsum; - G_CONST WebRtc_Word16 *xptr1; - G_CONST WebRtc_Word16 *xptr2; - - sum=0; - scaling=0; - /* Calculate r[0] and how much scaling is needed */ - for (i=0; i < N; i++) { - prod = WEBRTC_SPL_MUL_16_16_RSFT(x[i],x[i],scaling); - newsum = sum+prod; - /* If sum gets less than 0 we have overflow and need to scale the signal */ - if(newsum<0) { - scaling++; - sum=WEBRTC_SPL_RSHIFT_W32(sum, 1); - prod=WEBRTC_SPL_RSHIFT_W32(prod, 1); - } - sum += prod; - } - r[0]=sum; - - /* Perform the actual correlation calculation */ - for (i = 1; i < order + 1; i++) - { - int loops=(N-i); - sum = 0; - xptr1=(G_CONST WebRtc_Word16 *)x; - xptr2=(G_CONST WebRtc_Word16 *)&x[i]; - - for (j = loops;j > 0; j--) - { - sum += WEBRTC_SPL_MUL_16_16_RSFT(*xptr1++,*xptr2++,scaling); - } - - r[i] = sum; - } - - *scale = scaling; - - return(order + 1); -} - -static const WebRtc_Word32 kApUpperQ15[ALLPASSSECTIONS] = { 1137, 12537 }; -static const WebRtc_Word32 kApLowerQ15[ALLPASSSECTIONS] = { 5059, 24379 }; - - -static void AllpassFilterForDec32(WebRtc_Word16 *InOut16, //Q0 - const WebRtc_Word32 *APSectionFactors, //Q15 - WebRtc_Word16 lengthInOut, - WebRtc_Word32 *FilterState) //Q16 -{ - int n, j; - WebRtc_Word32 a, b; - - for (j=0; j Q16 - b = WEBRTC_SPL_ADD_SAT_W32(a, FilterState[j]); //Q16+Q16=Q16 - a = WEBRTC_SPL_MUL_16_32_RSFT16( - (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(b, 16), - -APSectionFactors[j]); //Q0*Q31=Q31 shifted 16 gives Q15 - FilterState[j] = WEBRTC_SPL_ADD_SAT_W32( - WEBRTC_SPL_LSHIFT_W32(a,1), - WEBRTC_SPL_LSHIFT_W32((WebRtc_UWord32)InOut16[n], 16)); // Q15<<1 + Q0<<16 = Q16 + Q16 = Q16 - InOut16[n] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(b, 16); //Save as Q0 - } - } -} - - - - -void WebRtcIsacfix_DecimateAllpass32(const WebRtc_Word16 *in, - WebRtc_Word32 *state_in, /* array of size: 2*ALLPASSSECTIONS+1 */ - WebRtc_Word16 N, /* number of input samples */ - WebRtc_Word16 *out) /* array of size N/2 */ -{ - int n; - WebRtc_Word16 data_vec[PITCH_FRAME_LEN]; - - /* copy input */ - memcpy(data_vec+1, in, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), (N-1))); - - - data_vec[0] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(state_in[WEBRTC_SPL_MUL_16_16(2, ALLPASSSECTIONS)],16); //the z^(-1) state - state_in[WEBRTC_SPL_MUL_16_16(2, ALLPASSSECTIONS)] = WEBRTC_SPL_LSHIFT_W32((WebRtc_UWord32)in[N-1],16); - - - - AllpassFilterForDec32(data_vec+1, kApUpperQ15, N, state_in); - AllpassFilterForDec32(data_vec, kApLowerQ15, N, state_in+ALLPASSSECTIONS); - - for (n=0;nDataBufferLoQ0[k] = (WebRtc_Word16) 0; - maskdata->DataBufferHiQ0[k] = (WebRtc_Word16) 0; - } - for (k = 0; k < ORDERLO+1; k++) { - maskdata->CorrBufLoQQ[k] = (WebRtc_Word32) 0; - maskdata->CorrBufLoQdom[k] = 0; - - maskdata->PreStateLoGQ15[k] = 0; - - } - for (k = 0; k < ORDERHI+1; k++) { - maskdata->CorrBufHiQQ[k] = (WebRtc_Word32) 0; - maskdata->CorrBufHiQdom[k] = 0; - maskdata->PreStateHiGQ15[k] = 0; - } - - maskdata->OldEnergy = 10; - - return; -} - -void WebRtcIsacfix_InitMaskingDec(MaskFiltstr_dec *maskdata) { - - int k; - - for (k = 0; k < ORDERLO+1; k++) - { - maskdata->PostStateLoGQ0[k] = 0; - } - for (k = 0; k < ORDERHI+1; k++) - { - maskdata->PostStateHiGQ0[k] = 0; - } - - maskdata->OldEnergy = 10; - - return; -} - - - - - - - -void WebRtcIsacfix_InitPreFilterbank(PreFiltBankstr *prefiltdata) -{ - int k; - - for (k = 0; k < QLOOKAHEAD; k++) { - prefiltdata->INLABUF1_fix[k] = 0; - prefiltdata->INLABUF2_fix[k] = 0; - } - for (k = 0; k < WEBRTC_SPL_MUL_16_16(2,(QORDER-1)); k++) { - - prefiltdata->INSTAT1_fix[k] = 0; - prefiltdata->INSTAT2_fix[k] = 0; - } - - /* High pass filter states */ - prefiltdata->HPstates_fix[0] = 0; - prefiltdata->HPstates_fix[1] = 0; - - return; -} - -void WebRtcIsacfix_InitPostFilterbank(PostFiltBankstr *postfiltdata) -{ - int k; - - for (k = 0; k < WEBRTC_SPL_MUL_16_16(2, POSTQORDER); k++) { - - postfiltdata->STATE_0_LOWER_fix[k] = 0; - postfiltdata->STATE_0_UPPER_fix[k] = 0; - } - - /* High pass filter states */ - - postfiltdata->HPstates1_fix[0] = 0; - postfiltdata->HPstates1_fix[1] = 0; - - postfiltdata->HPstates2_fix[0] = 0; - postfiltdata->HPstates2_fix[1] = 0; - - return; -} - - -void WebRtcIsacfix_InitPitchFilter(PitchFiltstr *pitchfiltdata) -{ - int k; - - for (k = 0; k < PITCH_BUFFSIZE; k++) - pitchfiltdata->ubufQQ[k] = 0; - for (k = 0; k < (PITCH_DAMPORDER); k++) - pitchfiltdata->ystateQQ[k] = 0; - - pitchfiltdata->oldlagQ7 = 6400; /* 50.0 in Q7 */ - pitchfiltdata->oldgainQ12 = 0; -} - -void WebRtcIsacfix_InitPitchAnalysis(PitchAnalysisStruct *State) -{ - int k; - - for (k = 0; k < PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2; k++) { - State->dec_buffer16[k] = 0; - } - for (k = 0; k < WEBRTC_SPL_MUL_16_16(2, ALLPASSSECTIONS)+1; k++) { - State->decimator_state32[k] = 0; - } - - for (k = 0; k < QLOOKAHEAD; k++) - State->inbuf[k] = 0; - - WebRtcIsacfix_InitPitchFilter(&(State->PFstr_wght)); - - WebRtcIsacfix_InitPitchFilter(&(State->PFstr)); -} - - -void WebRtcIsacfix_InitPlc( PLCstr *State ) -{ - State->decayCoeffPriodic = WEBRTC_SPL_WORD16_MAX; - State->decayCoeffNoise = WEBRTC_SPL_WORD16_MAX; - - State->used = PLC_WAS_USED; - - WebRtcSpl_ZerosArrayW16(State->overlapLP, RECOVERY_OVERLAP); - WebRtcSpl_ZerosArrayW16(State->lofilt_coefQ15, ORDERLO); - WebRtcSpl_ZerosArrayW16(State->hifilt_coefQ15, ORDERHI ); - - State->AvgPitchGain_Q12 = 0; - State->lastPitchGain_Q12 = 0; - State->lastPitchLag_Q7 = 0; - State->gain_lo_hiQ17[0]=State->gain_lo_hiQ17[1] = 0; - WebRtcSpl_ZerosArrayW16(State->prevPitchInvIn, FRAMESAMPLES/2); - WebRtcSpl_ZerosArrayW16(State->prevPitchInvOut, PITCH_MAX_LAG + 10 ); - WebRtcSpl_ZerosArrayW32(State->prevHP, PITCH_MAX_LAG + 10 ); - State->pitchCycles = 0; - State->A = 0; - State->B = 0; - State->pitchIndex = 0; - State->stretchLag = 240; - State->seed = 4447; - - -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/isacfix.c b/modules/audio_coding/codecs/iSAC/fix/source/isacfix.c deleted file mode 100644 index bab7b5185..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/isacfix.c +++ /dev/null @@ -1,1534 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * isacfix.c - * - * This C file contains the functions for the ISAC API - * - */ - -#include -#include - -#include "isacfix.h" -#include "bandwidth_estimator.h" -#include "codec.h" -#include "entropy_coding.h" -#include "structs.h" - - -/************************************************************************** - * WebRtcIsacfix_AssignSize(...) - * - * Functions used when malloc is not allowed - * Returns number of bytes needed to allocate for iSAC struct. - * - */ - -WebRtc_Word16 WebRtcIsacfix_AssignSize(int *sizeinbytes) { - *sizeinbytes=sizeof(ISACFIX_SubStruct)*2/sizeof(WebRtc_Word16); - return(0); -} - -/*************************************************************************** - * WebRtcIsacfix_Assign(...) - * - * Functions used when malloc is not allowed - * Place struct at given address - * - * If successful, Return 0, else Return -1 - */ - -WebRtc_Word16 WebRtcIsacfix_Assign(ISACFIX_MainStruct **inst, void *ISACFIX_inst_Addr) { - if (ISACFIX_inst_Addr!=NULL) { - *inst = (ISACFIX_MainStruct*)ISACFIX_inst_Addr; - (*(ISACFIX_SubStruct**)inst)->errorcode = 0; - (*(ISACFIX_SubStruct**)inst)->initflag = 0; - (*(ISACFIX_SubStruct**)inst)->ISACenc_obj.SaveEnc_ptr = NULL; - return(0); - } else { - return(-1); - } -} - - -#ifndef ISACFIX_NO_DYNAMIC_MEM - -/**************************************************************************** - * WebRtcIsacfix_Create(...) - * - * This function creates a ISAC instance, which will contain the state - * information for one coding/decoding channel. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_Create(ISACFIX_MainStruct **ISAC_main_inst) -{ - ISACFIX_SubStruct *tempo; - tempo = malloc(1 * sizeof(ISACFIX_SubStruct)); - *ISAC_main_inst = (ISACFIX_MainStruct *)tempo; - if (*ISAC_main_inst!=NULL) { - (*(ISACFIX_SubStruct**)ISAC_main_inst)->errorcode = 0; - (*(ISACFIX_SubStruct**)ISAC_main_inst)->initflag = 0; - (*(ISACFIX_SubStruct**)ISAC_main_inst)->ISACenc_obj.SaveEnc_ptr = NULL; - return(0); - } else { - return(-1); - } -} - - -/**************************************************************************** - * WebRtcIsacfix_CreateInternal(...) - * - * This function creates the memory that is used to store data in the encoder - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_CreateInternal(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Allocate memory for storing encoder data */ - ISAC_inst->ISACenc_obj.SaveEnc_ptr = malloc(1 * sizeof(ISAC_SaveEncData_t)); - - if (ISAC_inst->ISACenc_obj.SaveEnc_ptr!=NULL) { - return(0); - } else { - return(-1); - } -} - - -#endif - - - -/**************************************************************************** - * WebRtcIsacfix_Free(...) - * - * This function frees the ISAC instance created at the beginning. - * - * Input: - * - ISAC_main_inst : a ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_Free(ISACFIX_MainStruct *ISAC_main_inst) -{ - free(ISAC_main_inst); - return(0); -} - -/**************************************************************************** - * WebRtcIsacfix_FreeInternal(...) - * - * This function frees the internal memory for storing encoder data. - * - * Input: - * - ISAC_main_inst : a ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_FreeInternal(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Release memory */ - free(ISAC_inst->ISACenc_obj.SaveEnc_ptr); - - return(0); -} - -/**************************************************************************** - * WebRtcIsacfix_EncoderInit(...) - * - * This function initializes a ISAC instance prior to the encoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - CodingMode : 0 -> Bit rate and frame length are automatically - * adjusted to available bandwidth on - * transmission channel. - * 1 -> User sets a frame length and a target bit - * rate which is taken as the maximum short-term - * average bit rate. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_EncoderInit(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 CodingMode) -{ - int k; - WebRtc_Word16 statusInit; - ISACFIX_SubStruct *ISAC_inst; - - statusInit = 0; - /* typecast pointer to rela structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* flag encoder init */ - ISAC_inst->initflag |= 2; - - if (CodingMode == 0) - /* Adaptive mode */ - ISAC_inst->ISACenc_obj.new_framelength = INITIAL_FRAMESAMPLES; - else if (CodingMode == 1) - /* Instantaneous mode */ - ISAC_inst->ISACenc_obj.new_framelength = 480; /* default for I-mode */ - else { - ISAC_inst->errorcode = ISAC_DISALLOWED_CODING_MODE; - statusInit = -1; - } - - ISAC_inst->CodingMode = CodingMode; - - WebRtcIsacfix_InitMaskingEnc(&ISAC_inst->ISACenc_obj.maskfiltstr_obj); - WebRtcIsacfix_InitPreFilterbank(&ISAC_inst->ISACenc_obj.prefiltbankstr_obj); - WebRtcIsacfix_InitPitchFilter(&ISAC_inst->ISACenc_obj.pitchfiltstr_obj); - WebRtcIsacfix_InitPitchAnalysis(&ISAC_inst->ISACenc_obj.pitchanalysisstr_obj); - - - WebRtcIsacfix_InitBandwidthEstimator(&ISAC_inst->bwestimator_obj); - WebRtcIsacfix_InitRateModel(&ISAC_inst->ISACenc_obj.rate_data_obj); - - - ISAC_inst->ISACenc_obj.buffer_index = 0; - ISAC_inst->ISACenc_obj.frame_nb = 0; - ISAC_inst->ISACenc_obj.BottleNeck = 32000; /* default for I-mode */ - ISAC_inst->ISACenc_obj.MaxDelay = 10; /* default for I-mode */ - ISAC_inst->ISACenc_obj.current_framesamples = 0; - ISAC_inst->ISACenc_obj.s2nr = 0; - ISAC_inst->ISACenc_obj.MaxBits = 0; - ISAC_inst->ISACenc_obj.bitstr_seed = 4447; - ISAC_inst->ISACenc_obj.payloadLimitBytes30 = STREAM_MAXW16_30MS << 1; - ISAC_inst->ISACenc_obj.payloadLimitBytes60 = STREAM_MAXW16_60MS << 1; - ISAC_inst->ISACenc_obj.maxPayloadBytes = STREAM_MAXW16_60MS << 1; - ISAC_inst->ISACenc_obj.maxRateInBytes = STREAM_MAXW16_30MS << 1; - ISAC_inst->ISACenc_obj.enforceFrameSize = 0; - - /* Init the bistream data area to zero */ - for (k=0; kISACenc_obj.bitstr_obj.stream[k] = 0; - } - -#ifdef NB_CALLS - WebRtcIsacfix_InitPostFilterbank(&ISAC_inst->ISACenc_obj.interpolatorstr_obj); -#endif - - - return statusInit; -} - - -/**************************************************************************** - * WebRtcIsacfix_Encode(...) - * - * This function encodes 10ms frame(s) and inserts it into a package. - * Input speech length has to be 160 samples (10ms). The encoder buffers those - * 10ms frames until it reaches the chosen Framesize (480 or 960 samples - * corresponding to 30 or 60 ms frames), and then proceeds to the encoding. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : 0 - The buffer didn't reach the chosen framesize - * so it keeps buffering speech samples. - * : -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_Word16 *speechIn, - WebRtc_Word16 *encoded) -{ - ISACFIX_SubStruct *ISAC_inst; - WebRtc_Word16 stream_len; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - - /* typecast pointer to rela structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - - /* check if encoder initiated */ - if ((ISAC_inst->initflag & 2) != 2) { - ISAC_inst->errorcode = ISAC_ENCODER_NOT_INITIATED; - return (-1); - } - - stream_len = WebRtcIsacfix_EncodeImpl((WebRtc_Word16*)speechIn, - &ISAC_inst->ISACenc_obj, - &ISAC_inst->bwestimator_obj, - ISAC_inst->CodingMode); - if (stream_len<0) { - ISAC_inst->errorcode = - stream_len; - return -1; - } - - - /* convert from bytes to WebRtc_Word16 */ -#ifndef WEBRTC_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (WebRtc_Word16)( ( (WebRtc_UWord16)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8 ) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - - - - return stream_len; - -} - - - - -/**************************************************************************** - * WebRtcIsacfix_EncodeNb(...) - * - * This function encodes 10ms narrow band (8 kHz sampling) frame(s) and inserts - * it into a package. Input speech length has to be 80 samples (10ms). The encoder - * interpolates into wide-band (16 kHz sampling) buffers those - * 10ms frames until it reaches the chosen Framesize (480 or 960 wide-band samples - * corresponding to 30 or 60 ms frames), and then proceeds to the encoding. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : 0 - The buffer didn't reach the chosen framesize - * so it keeps buffering speech samples. - * : -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_EncodeNb(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_Word16 *speechIn, - WebRtc_Word16 *encoded) -{ -#ifdef NB_CALLS - - ISACFIX_SubStruct *ISAC_inst; - WebRtc_Word16 stream_len; - WebRtc_Word16 speechInWB[FRAMESAMPLES_10ms]; - WebRtc_Word16 Vector_Word16_1[FRAMESAMPLES_10ms/2]; - WebRtc_Word16 Vector_Word16_2[FRAMESAMPLES_10ms/2]; - - int k; - - - /* typecast pointer to rela structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - - /* check if encoder initiated */ - if ((ISAC_inst->initflag & 2) != 2) { - ISAC_inst->errorcode = ISAC_ENCODER_NOT_INITIATED; - return (-1); - } - - - /* Oversample to WB */ - - /* Form polyphase signals, and compensate for DC offset */ - for (k=0;kISACenc_obj.interpolatorstr_obj, FRAMESAMPLES_10ms); - - - /* Encode WB signal */ - stream_len = WebRtcIsacfix_EncodeImpl((WebRtc_Word16*)speechInWB, - &ISAC_inst->ISACenc_obj, - &ISAC_inst->bwestimator_obj, - ISAC_inst->CodingMode); - if (stream_len<0) { - ISAC_inst->errorcode = - stream_len; - return -1; - } - - - /* convert from bytes to WebRtc_Word16 */ -#ifndef WEBRTC_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (WebRtc_Word16)(((WebRtc_UWord16)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - - - - return stream_len; - -#else /* NB_CALLS not defined */ - return -1; -#endif /* NB_CALLS */ -} - - -/**************************************************************************** - * WebRtcIsacfix_GetNewBitStream(...) - * - * This function returns encoded data, with the recieved bwe-index in the - * stream. It should always return a complete packet, i.e. only called once - * even for 60 msec frames - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - bweIndex : index of bandwidth estimate to put in new bitstream - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_GetNewBitStream(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 bweIndex, - float scale, - WebRtc_Word16 *encoded) -{ - ISACFIX_SubStruct *ISAC_inst; - WebRtc_Word16 stream_len; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - - /* typecast pointer to rela structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - - /* check if encoder initiated */ - if ((ISAC_inst->initflag & 2) != 2) { - ISAC_inst->errorcode = ISAC_ENCODER_NOT_INITIATED; - return (-1); - } - - stream_len = WebRtcIsacfix_EncodeStoredData(&ISAC_inst->ISACenc_obj, - bweIndex, - scale, - ISAC_inst->CodingMode); - if (stream_len<0) { - ISAC_inst->errorcode = - stream_len; - return -1; - } - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0;k<(stream_len+1)>>1;k++) { - encoded[k] = (WebRtc_Word16)( ( (WebRtc_UWord16)(ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] >> 8 ) - | (((ISAC_inst->ISACenc_obj.bitstr_obj).stream[k] & 0x00FF) << 8)); - } - -#else - WEBRTC_SPL_MEMCPY_W16(encoded, (ISAC_inst->ISACenc_obj.bitstr_obj).stream, (stream_len + 1)>>1); -#endif - - return stream_len; - -} - - - -/**************************************************************************** - * WebRtcIsacfix_DecoderInit(...) - * - * This function initializes a ISAC instance prior to the decoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - * Return value - * : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_DecoderInit(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* flag decoder init */ - ISAC_inst->initflag |= 1; - - - WebRtcIsacfix_InitMaskingDec(&ISAC_inst->ISACdec_obj.maskfiltstr_obj); - WebRtcIsacfix_InitPostFilterbank(&ISAC_inst->ISACdec_obj.postfiltbankstr_obj); - WebRtcIsacfix_InitPitchFilter(&ISAC_inst->ISACdec_obj.pitchfiltstr_obj); - - /* TS */ - WebRtcIsacfix_InitPlc( &ISAC_inst->ISACdec_obj.plcstr_obj ); - - -#ifdef NB_CALLS - WebRtcIsacfix_InitPreFilterbank(&ISAC_inst->ISACdec_obj.decimatorstr_obj); -#endif - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_UpdateBwEstimate1(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 arr_ts) -{ - ISACFIX_SubStruct *ISAC_inst; - Bitstr_dec streamdata; - WebRtc_UWord16 partOfStream[5]; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (WebRtc_UWord16 *)partOfStream; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Sanity check of packet length */ - if (packet_size <= 0) { - /* return error code if the packet length is null or less */ - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } else if (packet_size > (STREAM_MAXW16<<1)) { - /* return error code if length of stream is too long */ - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - return -1; - } - - /* check if decoder initiated */ - if ((ISAC_inst->initflag & 1) != 1) { - ISAC_inst->errorcode = ISAC_DECODER_NOT_INITIATED; - return (-1); - } - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (WebRtc_UWord16) (((WebRtc_UWord16)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif - - if (packet_size == 0) - { - /* return error code if the packet length is null */ - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } - - err = WebRtcIsacfix_EstimateBandwidth(&ISAC_inst->bwestimator_obj, - &streamdata, - packet_size, - rtp_seq_number, - 0, - arr_ts); - - - if (err < 0) - { - /* return error code if something went wrong */ - ISAC_inst->errorcode = -err; - return -1; - } - - - return 0; -} - -/**************************************************************************** - * WebRtcIsacfix_UpdateBwEstimate(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - send_ts : Send Time Stamp from RTP header - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_UpdateBwEstimate(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts) -{ - ISACFIX_SubStruct *ISAC_inst; - Bitstr_dec streamdata; - WebRtc_UWord16 partOfStream[5]; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (WebRtc_UWord16 *)partOfStream; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Sanity check of packet length */ - if (packet_size <= 0) { - /* return error code if the packet length is null or less */ - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } else if (packet_size > (STREAM_MAXW16<<1)) { - /* return error code if length of stream is too long */ - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - return -1; - } - - /* check if decoder initiated */ - if ((ISAC_inst->initflag & 1) != 1) { - ISAC_inst->errorcode = ISAC_DECODER_NOT_INITIATED; - return (-1); - } - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (WebRtc_UWord16) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif - - if (packet_size == 0) - { - /* return error code if the packet length is null */ - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } - - err = WebRtcIsacfix_EstimateBandwidth(&ISAC_inst->bwestimator_obj, - &streamdata, - packet_size, - rtp_seq_number, - send_ts, - arr_ts); - - if (err < 0) - { - /* return error code if something went wrong */ - ISAC_inst->errorcode = -err; - return -1; - } - - - return 0; -} - -/**************************************************************************** - * WebRtcIsacfix_Decode(...) - * - * This function decodes a ISAC frame. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - - -WebRtc_Word16 WebRtcIsacfix_Decode(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ - ISACFIX_SubStruct *ISAC_inst; - /* number of samples (480 or 960), output from decoder */ - /* that were actually used in the encoder/decoder (determined on the fly) */ - WebRtc_Word16 number_of_samples; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 declen = 0; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* check if decoder initiated */ - if ((ISAC_inst->initflag & 1) != 1) { - ISAC_inst->errorcode = ISAC_DECODER_NOT_INITIATED; - return (-1); - } - - /* Sanity check of packet length */ - if (len <= 0) { - /* return error code if the packet length is null or less */ - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } else if (len > (STREAM_MAXW16<<1)) { - /* return error code if length of stream is too long */ - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - return -1; - } - - (ISAC_inst->ISACdec_obj.bitstr_obj).stream = (WebRtc_UWord16 *)encoded; - - /* convert bitstream from WebRtc_Word16 to bytes */ -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<(len>>1); k++) { - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (WebRtc_UWord16) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } - if (len & 0x0001) - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (WebRtc_UWord16) ((encoded[k] & 0xFF)<<8); -#endif - - /* added for NetEq purposes (VAD/DTX related) */ - *speechType=1; - - declen = WebRtcIsacfix_DecodeImpl(decoded,&ISAC_inst->ISACdec_obj, &number_of_samples); - - if (declen < 0) { - /* Some error inside the decoder */ - ISAC_inst->errorcode = -declen; - memset(decoded, 0, sizeof(WebRtc_Word16) * MAX_FRAMESAMPLES); - return -1; - } - - /* error check */ - - if (declen & 0x0001) { - if (len != declen && len != declen + (((ISAC_inst->ISACdec_obj.bitstr_obj).stream[declen>>1]) & 0x00FF) ) { - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - memset(decoded, 0, sizeof(WebRtc_Word16) * number_of_samples); - return -1; - } - } else { - if (len != declen && len != declen + (((ISAC_inst->ISACdec_obj.bitstr_obj).stream[declen>>1]) >> 8) ) { - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - memset(decoded, 0, sizeof(WebRtc_Word16) * number_of_samples); - return -1; - } - } - - return number_of_samples; -} - - - - - -/**************************************************************************** - * WebRtcIsacfix_DecodeNb(...) - * - * This function decodes a ISAC frame in narrow-band (8 kHz sampling). - * Output speech length will be a multiple of 240 samples: 240 or 480 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - - -WebRtc_Word16 WebRtcIsacfix_DecodeNb(ISACFIX_MainStruct *ISAC_main_inst, - const WebRtc_UWord16 *encoded, - WebRtc_Word16 len, - WebRtc_Word16 *decoded, - WebRtc_Word16 *speechType) -{ -#ifdef NB_CALLS - ISACFIX_SubStruct *ISAC_inst; - /* twice the number of samples (480 or 960), output from decoder */ - /* that were actually used in the encoder/decoder (determined on the fly) */ - WebRtc_Word16 number_of_samples; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 declen = 0; - WebRtc_Word16 dummy[FRAMESAMPLES/2]; - - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* check if decoder initiated */ - if ((ISAC_inst->initflag & 1) != 1) { - ISAC_inst->errorcode = ISAC_DECODER_NOT_INITIATED; - return (-1); - } - - if (len == 0) - { /* return error code if the packet length is null */ - - ISAC_inst->errorcode = ISAC_EMPTY_PACKET; - return -1; - } - - (ISAC_inst->ISACdec_obj.bitstr_obj).stream = (WebRtc_UWord16 *)encoded; - - /* convert bitstream from WebRtc_Word16 to bytes */ -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<(len>>1); k++) { - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (WebRtc_UWord16) ((encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } - if (len & 0x0001) - (ISAC_inst->ISACdec_obj.bitstr_obj).stream[k] = (WebRtc_UWord16) ((encoded[k] & 0xFF)<<8); -#endif - - /* added for NetEq purposes (VAD/DTX related) */ - *speechType=1; - - declen = WebRtcIsacfix_DecodeImpl(decoded,&ISAC_inst->ISACdec_obj, &number_of_samples); - - if (declen < 0) { - /* Some error inside the decoder */ - ISAC_inst->errorcode = -declen; - memset(decoded, 0, sizeof(WebRtc_Word16) * FRAMESAMPLES); - return -1; - } - - /* error check */ - - if (declen & 0x0001) { - if (len != declen && len != declen + (((ISAC_inst->ISACdec_obj.bitstr_obj).stream[declen>>1]) & 0x00FF) ) { - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - memset(decoded, 0, sizeof(WebRtc_Word16) * number_of_samples); - return -1; - } - } else { - if (len != declen && len != declen + (((ISAC_inst->ISACdec_obj.bitstr_obj).stream[declen>>1]) >> 8) ) { - ISAC_inst->errorcode = ISAC_LENGTH_MISMATCH; - memset(decoded, 0, sizeof(WebRtc_Word16) * number_of_samples); - return -1; - } - } - - WebRtcIsacfix_SplitAndFilter2(decoded, decoded, dummy, &ISAC_inst->ISACdec_obj.decimatorstr_obj); - - if (number_of_samples>FRAMESAMPLES) { - WebRtcIsacfix_SplitAndFilter2(decoded + FRAMESAMPLES, decoded + FRAMESAMPLES/2, - dummy, &ISAC_inst->ISACdec_obj.decimatorstr_obj); - } - - return number_of_samples/2; - -#else /* NB_CALLS not defined */ - return -1; -#endif /* NB_CALLS */ -} - - -/**************************************************************************** - * WebRtcIsacfix_DecodePlcNb(...) - * - * This function conducts PLC for ISAC frame(s) in narrow-band (8kHz sampling). - * Output speech length will be "240*noOfLostFrames" samples - * that is equevalent of "30*noOfLostFrames" millisecond. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames (240 sample=30ms) to produce - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded PLC vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_DecodePlcNb(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames ) -{ - -#ifdef NB_CALLS - - WebRtc_Word16 no_of_samples, declen, k, ok; - WebRtc_Word16 outframeNB[FRAMESAMPLES]; - WebRtc_Word16 outframeWB[FRAMESAMPLES]; - WebRtc_Word16 dummy[FRAMESAMPLES/2]; - - - ISACFIX_SubStruct *ISAC_inst; - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Limit number of frames to two = 60 msec. Otherwise we exceed data vectors */ - if (noOfLostFrames > 2){ - noOfLostFrames = 2; - } - - k = 0; - declen = 0; - while( noOfLostFrames > 0 ) - { - ok = WebRtcIsacfix_DecodePlcImpl( outframeWB, &ISAC_inst->ISACdec_obj, &no_of_samples ); - if(ok) - return -1; - - WebRtcIsacfix_SplitAndFilter2(outframeWB, &(outframeNB[k*240]), dummy, &ISAC_inst->ISACdec_obj.decimatorstr_obj); - - declen += no_of_samples; - noOfLostFrames--; - k++; - } - - declen>>=1; - - for (k=0;k0 - number of samples in decoded PLC vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_DecodePlc(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 *decoded, - WebRtc_Word16 noOfLostFrames) -{ - - WebRtc_Word16 no_of_samples, declen, k, ok; - WebRtc_Word16 outframe16[MAX_FRAMESAMPLES]; - - ISACFIX_SubStruct *ISAC_inst; - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Limit number of frames to two = 60 msec. Otherwise we exceed data vectors */ - if (noOfLostFrames > 2) { - noOfLostFrames = 2; - } - k = 0; - declen = 0; - while( noOfLostFrames > 0 ) - { - ok = WebRtcIsacfix_DecodePlcImpl( &(outframe16[k*480]), &ISAC_inst->ISACdec_obj, &no_of_samples ); - if(ok) - return -1; - declen += no_of_samples; - noOfLostFrames--; - k++; - } - - for (k=0;kCodingMode == 0) - { - /* in adaptive mode */ - ISAC_inst->errorcode = ISAC_MODE_MISMATCH; - return -1; - } - - - if (rate >= 10000 && rate <= 32000) - ISAC_inst->ISACenc_obj.BottleNeck = rate; - else { - ISAC_inst->errorcode = ISAC_DISALLOWED_BOTTLENECK; - return -1; - } - - - - if (framesize == 30 || framesize == 60) - ISAC_inst->ISACenc_obj.new_framelength = (FS/1000) * framesize; - else { - ISAC_inst->errorcode = ISAC_DISALLOWED_FRAME_LENGTH; - return -1; - } - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_ControlBwe(...) - * - * This function sets the initial values of bottleneck and frame-size if - * iSAC is used in channel-adaptive mode. Through this API, users can - * enforce a frame-size for all values of bottleneck. Then iSAC will not - * automatically change the frame-size. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rateBPS : initial value of bottleneck in bits/second - * 10000 <= rateBPS <= 32000 is accepted - * For default bottleneck set rateBPS = 0 - * - frameSizeMs : number of milliseconds per frame (30 or 60) - * - enforceFrameSize : 1 to enforce the given frame-size through out - * the adaptation process, 0 to let iSAC change - * the frame-size if required. - * - * Return value : 0 - ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsacfix_ControlBwe(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 rateBPS, - WebRtc_Word16 frameSizeMs, - WebRtc_Word16 enforceFrameSize) -{ - ISACFIX_SubStruct *ISAC_inst; - /* Typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* check if encoder initiated */ - if ((ISAC_inst->initflag & 2) != 2) { - ISAC_inst->errorcode = ISAC_ENCODER_NOT_INITIATED; - return (-1); - } - - /* Check that we are in channel-adaptive mode, otherwise, return -1 */ - if (ISAC_inst->CodingMode != 0) { - ISAC_inst->errorcode = ISAC_MODE_MISMATCH; - return (-1); - } - - /* Set struct variable if enforceFrameSize is set. ISAC will then keep the */ - /* chosen frame size. */ - ISAC_inst->ISACenc_obj.enforceFrameSize = (enforceFrameSize != 0)? 1:0; - - /* Set initial rate, if value between 10000 and 32000, */ - /* if rateBPS is 0, keep the default initial bottleneck value (15000) */ - if ((rateBPS >= 10000) && (rateBPS <= 32000)) { - ISAC_inst->bwestimator_obj.sendBwAvg = (((WebRtc_UWord32)rateBPS) << 7); - } else if (rateBPS != 0) { - ISAC_inst->errorcode = ISAC_DISALLOWED_BOTTLENECK; - return -1; - } - - /* Set initial framesize. If enforceFrameSize is set the frame size will not change */ - if ((frameSizeMs == 30) || (frameSizeMs == 60)) { - ISAC_inst->ISACenc_obj.new_framelength = (FS/1000) * frameSizeMs; - } else { - ISAC_inst->errorcode = ISAC_DISALLOWED_FRAME_LENGTH; - return -1; - } - - return 0; -} - - - - - -/**************************************************************************** - * WebRtcIsacfix_GetDownLinkBwIndex(...) - * - * This function returns index representing the Bandwidth estimate from - * other side to this side. - * - * Input: - * - ISAC_main_inst: iSAC struct - * - * Output: - * - rateIndex : Bandwidth estimate to transmit to other side. - * - */ - -WebRtc_Word16 WebRtcIsacfix_GetDownLinkBwIndex(ISACFIX_MainStruct* ISAC_main_inst, - WebRtc_Word16* rateIndex) -{ - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Call function to get Bandwidth Estimate */ - *rateIndex = WebRtcIsacfix_GetDownlinkBwIndexImpl(&ISAC_inst->bwestimator_obj); - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_UpdateUplinkBw(...) - * - * This function takes an index representing the Bandwidth estimate from - * this side to other side and updates BWE. - * - * Input: - * - ISAC_main_inst: iSAC struct - * - rateIndex : Bandwidth estimate from other side. - * - */ - -WebRtc_Word16 WebRtcIsacfix_UpdateUplinkBw(ISACFIX_MainStruct* ISAC_main_inst, - WebRtc_Word16 rateIndex) -{ - WebRtc_Word16 err = 0; - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - /* Call function to update BWE with received Bandwidth Estimate */ - err = WebRtcIsacfix_UpdateUplinkBwRec(&ISAC_inst->bwestimator_obj, rateIndex); - if (err < 0) { - ISAC_inst->errorcode = -err; - return (-1); - } - - return 0; -} - -/**************************************************************************** - * WebRtcIsacfix_ReadFrameLen(...) - * - * This function returns the length of the frame represented in the packet. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - */ - -WebRtc_Word16 WebRtcIsacfix_ReadFrameLen(const WebRtc_Word16* encoded, - WebRtc_Word16* frameLength) -{ - Bitstr_dec streamdata; - WebRtc_UWord16 partOfStream[5]; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (WebRtc_UWord16 *)partOfStream; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (WebRtc_UWord16) (((WebRtc_UWord16)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif - - /* decode frame length */ - err = WebRtcIsacfix_DecodeFrameLen(&streamdata, frameLength); - if (err<0) // error check - return err; - - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_ReadBwIndex(...) - * - * This function returns the index of the Bandwidth estimate from the bitstream. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - rateIndex : Bandwidth estimate in bitstream - * - */ - -WebRtc_Word16 WebRtcIsacfix_ReadBwIndex(const WebRtc_Word16* encoded, - WebRtc_Word16* rateIndex) -{ - Bitstr_dec streamdata; - WebRtc_UWord16 partOfStream[5]; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - /* Set stream pointer to point at partOfStream */ - streamdata.stream = (WebRtc_UWord16 *)partOfStream; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - streamdata.full = 1; - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<5; k++) { - streamdata.stream[k] = (WebRtc_UWord16) (((WebRtc_UWord16)encoded[k] >> 8)|((encoded[k] & 0xFF)<<8)); - } -#else - memcpy(streamdata.stream, encoded, 5); -#endif - - /* decode frame length, needed to get to the rateIndex in the bitstream */ - err = WebRtcIsacfix_DecodeFrameLen(&streamdata, rateIndex); - if (err<0) // error check - return err; - - /* decode BW estimation */ - err = WebRtcIsacfix_DecodeSendBandwidth(&streamdata, rateIndex); - if (err<0) // error check - return err; - - return 0; -} - - - - -/**************************************************************************** - * WebRtcIsacfix_GetErrorCode(...) - * - * This function can be used to check the error code of an iSAC instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occured in the - * specified instance. - * - * Input: - * - ISAC_main_inst : ISAC instance - * - * Return value : Error code - */ - -WebRtc_Word16 WebRtcIsacfix_GetErrorCode(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst; - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - return ISAC_inst->errorcode; -} - - - -/**************************************************************************** - * WebRtcIsacfix_GetUplinkBw(...) - * - * This function returns the inst quantized iSAC send bitrate - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : bitrate - */ - -WebRtc_Word32 WebRtcIsacfix_GetUplinkBw(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - BwEstimatorstr * bw = (BwEstimatorstr*)&(ISAC_inst->bwestimator_obj); - - return (WebRtc_Word32) WebRtcIsacfix_GetUplinkBandwidth(bw); -} - -/**************************************************************************** - * WebRtcIsacfix_GetNewFrameLen(...) - * - * This function return the next frame length (in samples) of iSAC. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : frame lenght in samples - */ - -WebRtc_Word16 WebRtcIsacfix_GetNewFrameLen(ISACFIX_MainStruct *ISAC_main_inst) -{ - ISACFIX_SubStruct *ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - return ISAC_inst->ISACenc_obj.new_framelength; -} - - -/**************************************************************************** - * WebRtcIsacfix_SetMaxPayloadSize(...) - * - * This function sets a limit for the maximum payload size of iSAC. The same - * value is used both for 30 and 60 msec packets. - * The absolute max will be valid until next time the function is called. - * NOTE! This function may override the function WebRtcIsacfix_SetMaxRate() - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxPayloadBytes : maximum size of the payload in bytes - * valid values are between 100 and 400 bytes - * - * - * Return value : 0 if sucessful - * -1 if error happens - */ - -WebRtc_Word16 WebRtcIsacfix_SetMaxPayloadSize(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word16 maxPayloadBytes) -{ - ISACFIX_SubStruct *ISAC_inst; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - if((maxPayloadBytes < 100) || (maxPayloadBytes > 400)) - { - /* maxPayloadBytes is out of valid range */ - return -1; - } - else - { - /* Set new absolute max, which will not change unless this function - is called again with a new value */ - ISAC_inst->ISACenc_obj.maxPayloadBytes = maxPayloadBytes; - - /* Set new maximum values for 30 and 60 msec packets */ - if (maxPayloadBytes < ISAC_inst->ISACenc_obj.maxRateInBytes) { - ISAC_inst->ISACenc_obj.payloadLimitBytes30 = maxPayloadBytes; - } else { - ISAC_inst->ISACenc_obj.payloadLimitBytes30 = ISAC_inst->ISACenc_obj.maxRateInBytes; - } - - if ( maxPayloadBytes < (ISAC_inst->ISACenc_obj.maxRateInBytes << 1)) { - ISAC_inst->ISACenc_obj.payloadLimitBytes60 = maxPayloadBytes; - } else { - ISAC_inst->ISACenc_obj.payloadLimitBytes60 = (ISAC_inst->ISACenc_obj.maxRateInBytes << 1); - } - } - return 0; -} - - -/**************************************************************************** - * WebRtcIsacfix_SetMaxRate(...) - * - * This function sets the maximum rate which the codec may not exceed for a - * singel packet. The maximum rate is set in bits per second. - * The codec has an absolute maximum rate of 53400 bits per second (200 bytes - * per 30 msec). - * It is possible to set a maximum rate between 32000 and 53400 bits per second. - * - * The rate limit is valid until next time the function is called. - * - * NOTE! Packet size will never go above the value set if calling - * WebRtcIsacfix_SetMaxPayloadSize() (default max packet size is 400 bytes). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxRateInBytes : maximum rate in bits per second, - * valid values are 32000 to 53400 bits - * - * Return value : 0 if sucessful - * -1 if error happens - */ - -WebRtc_Word16 WebRtcIsacfix_SetMaxRate(ISACFIX_MainStruct *ISAC_main_inst, - WebRtc_Word32 maxRate) -{ - ISACFIX_SubStruct *ISAC_inst; - WebRtc_Word16 maxRateInBytes; - - /* typecast pointer to real structure */ - ISAC_inst = (ISACFIX_SubStruct *)ISAC_main_inst; - - if((maxRate < 32000) || (maxRate > 53400)) - { - /* maxRate is out of valid range */ - return -1; - } - else - { - /* Calculate maximum number of bytes per 30 msec packets for the given - maximum rate. Multiply with 30/1000 to get number of bits per 30 msec, - divide by 8 to get number of bytes per 30 msec: - maxRateInBytes = floor((maxRate * 30/1000) / 8); */ - maxRateInBytes = (WebRtc_Word16)( WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_MUL(maxRate, 3), 800) ); - - /* Store the value for usage in the WebRtcIsacfix_SetMaxPayloadSize-function */ - ISAC_inst->ISACenc_obj.maxRateInBytes = maxRateInBytes; - - /* For 30 msec packets: if the new limit is below the maximum - payload size, set a new limit */ - if (maxRateInBytes < ISAC_inst->ISACenc_obj.maxPayloadBytes) { - ISAC_inst->ISACenc_obj.payloadLimitBytes30 = maxRateInBytes; - } else { - ISAC_inst->ISACenc_obj.payloadLimitBytes30 = ISAC_inst->ISACenc_obj.maxPayloadBytes; - } - - /* For 60 msec packets: if the new limit (times 2) is below the - maximum payload size, set a new limit */ - if ( (maxRateInBytes << 1) < ISAC_inst->ISACenc_obj.maxPayloadBytes) { - ISAC_inst->ISACenc_obj.payloadLimitBytes60 = (maxRateInBytes << 1); - } else { - ISAC_inst->ISACenc_obj.payloadLimitBytes60 = ISAC_inst->ISACenc_obj.maxPayloadBytes; - } - } - - return 0; -} - - - -/**************************************************************************** - * WebRtcIsacfix_version(...) - * - * This function returns the version number. - * - * Output: - * - version : Pointer to character string - * - */ - -void WebRtcIsacfix_version(char *version) -{ - strcpy(version, "3.6.0"); -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/isacfix.gyp b/modules/audio_coding/codecs/iSAC/fix/source/isacfix.gyp deleted file mode 100644 index d9651fa26..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/isacfix.gyp +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'iSACFix', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/isacfix.h', - 'arith_routines.c', - 'arith_routines_hist.c', - 'arith_routines_logist.c', - 'bandwidth_estimator.c', - 'decode.c', - 'decode_bwe.c', - 'decode_plc.c', - 'encode.c', - 'entropy_coding.c', - 'fft.c', - 'filterbank_tables.c', - 'filterbanks.c', - 'filters.c', - 'initialize.c', - 'isacfix.c', - 'lattice.c', - 'lpc_masking_model.c', - 'lpc_tables.c', - 'pitch_estimator.c', - 'pitch_filter.c', - 'pitch_gain_tables.c', - 'pitch_lag_tables.c', - 'spectrum_ar_model_tables.c', - 'transform.c', - 'arith_routins.h', - 'bandwidth_estimator.h', - 'codec.h', - 'entropy_coding.h', - 'fft.h', - 'filterbank_tables.h', - 'lpc_masking_model.h', - 'lpc_tables.h', - 'pitch_estimator.h', - 'pitch_gain_tables.h', - 'pitch_lag_tables.h', - 'settings.h', - 'spectrum_ar_model_tables.h', - 'structs.h', - ], - 'conditions': [ - ['OS!="win"', { - 'defines': [ - 'WEBRTC_LINUX', - ], - }], - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/iSAC/fix/source/lattice.c b/modules/audio_coding/codecs/iSAC/fix/source/lattice.c deleted file mode 100644 index 78e454ee5..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/lattice.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lattice.c - * - * Contains the normalized lattice filter routines (MA and AR) for iSAC codec - * - */ - -#include "codec.h" -#include "settings.h" - -/* filter the signal using normalized lattice filter */ -/* MA filter */ -void WebRtcIsacfix_NormLatticeFilterMa(WebRtc_Word16 orderCoef, - WebRtc_Word32 *stateGQ15, - WebRtc_Word16 *lat_inQ0, - WebRtc_Word16 *filt_coefQ15, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 lo_hi, - WebRtc_Word16 *lat_outQ9) -{ - WebRtc_Word16 sthQ15[MAX_AR_MODEL_ORDER]; - WebRtc_Word16 cthQ15[MAX_AR_MODEL_ORDER]; - - int u, i, ii, k, n; - WebRtc_Word16 temp2,temp3; - WebRtc_Word16 ord_1 = orderCoef+1; - WebRtc_Word32 inv_cthQ16[MAX_AR_MODEL_ORDER]; - - WebRtc_Word32 gain32, fQtmp; - WebRtc_Word16 gain16; - WebRtc_Word16 gain_sh; - - WebRtc_Word32 tmp32, tmp32b; - WebRtc_Word32 fQ15vec[HALF_SUBFRAMELEN]; - WebRtc_Word32 gQ15[MAX_AR_MODEL_ORDER+1][HALF_SUBFRAMELEN]; - WebRtc_Word16 sh; - WebRtc_Word16 t16a; - WebRtc_Word16 t16b; - -#define LATTICE_MUL_32_32_RSFT16(a32a, a32b, b32) \ - ((WebRtc_Word32)(WEBRTC_SPL_MUL(a32a, b32) + (WEBRTC_SPL_MUL_16_32_RSFT16(a32b, b32)))) - /* This macro is FORBIDDEN to use elsewhere than in two places in this file - since it might give unpredictable results, since a general WebRtc_Word32*WebRtc_Word32 - multiplication results in a 64 bit value. The result is then shifted just - 16 steps to the right, giving need for 48 bits, i.e. in the generel case, - it will NOT fit in a WebRtc_Word32. In the cases used in here, the WebRtc_Word32 will be - enough, since (FOR SOME REASON!!!) the involved multiplicands aren't big - enough to overflow a WebRtc_Word32 after shifting right 16 bits. I have compared - the result of a multiplication between t32 and tmp32, done in two ways: - - 1) Using (WebRtc_Word32) (((float)(tmp32))*((float)(tmp32b))/65536.0); - - 2) Using LATTICE_MUL_32_32_RSFT16(t16a, t16b, tmp32b); - - By running 25 files, I haven't found any bigger diff than 64 - this was in the - case when method 1) gave 650235648 and 2) gave 650235712. - - It might be good to investigate this further, in order to PROVE why it seems to - work without any problems. This might be done, by using the properties of - all reflection coefficients etc. - - */ - - for (u=0;u>15 = Q(17+gain_sh) - inv_cthQ16[k] = WebRtcSpl_DivW32W16((WebRtc_Word32)2147483647, cthQ15[k]); // 1/cth[k] in Q31/Q15 = Q16 - } - gain16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(gain32, 16); //Q(1+gain_sh) - - /* normalized lattice filter */ - /*****************************/ - - /* initial conditions */ - for (i=0;i>15 = Q15 - tmp32b= fQtmp + tmp32; //Q15+Q15=Q15 - tmp32 = inv_cthQ16[i-1]; //Q16 - t16a = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp32, 16); - t16b = (WebRtc_Word16) (tmp32-WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)t16a), 16)); - if (t16b<0) t16a++; - tmp32 = LATTICE_MUL_32_32_RSFT16(t16a, t16b, tmp32b); - fQtmp = tmp32; // Q15 - - // Calculate g[i][0] = cth[i-1]*stateG[i-1] + sth[i-1]* f[i][0]; - tmp32 = WEBRTC_SPL_MUL_16_32_RSFT15(cthQ15[i-1], stateGQ15[i-1]); //Q15*Q15>>15 = Q15 - tmp32b = WEBRTC_SPL_MUL_16_32_RSFT15(sthQ15[i-1], fQtmp); //Q15*Q15>>15 = Q15 - tmp32 = tmp32 + tmp32b;//Q15+Q15 = Q15 - gQ15[i][0] = tmp32; // Q15 - } - - /* filtering */ - /* save the states */ - for(k=0;k>15 = Q15 - tmp32b= fQ15vec[n+1] + tmp32; //Q15+Q15=Q15 - tmp32 = inv_cthQ16[k]; //Q16 - t16a = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp32, 16); - t16b = (WebRtc_Word16) (tmp32-WEBRTC_SPL_LSHIFT_W32(((WebRtc_Word32)t16a), 16)); - if (t16b<0) t16a++; - tmp32 = LATTICE_MUL_32_32_RSFT16(t16a, t16b, tmp32b); - fQ15vec[n+1] = tmp32; // Q15 - - // Calculate g[k+1][n+1] = cth[k]*g[k][n] + sth[k]* f[k+1][n+1]; - tmp32 = WEBRTC_SPL_MUL_16_32_RSFT15(cthQ15[k], gQ15[k][n]); //Q15*Q15>>15 = Q15 - tmp32b = WEBRTC_SPL_MUL_16_32_RSFT15(sthQ15[k], fQ15vec[n+1]); //Q15*Q15>>15 = Q15 - tmp32 = tmp32 + tmp32b;//Q15+Q15 = Q15 - gQ15[k+1][n+1] = tmp32; // Q15 - } - } - - fQ15vec[0] = fQtmp; - - for(n=0;n Q17 - tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(gain16, fQ15vec[n]); //Q(1+gain_sh)*Q15>>16 = Q(gain_sh) - sh = 9-gain_sh; //number of needed shifts to reach Q9 - t16a = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(tmp32, sh); - lat_outQ9[n + WEBRTC_SPL_MUL_16_16(u, HALF_SUBFRAMELEN)] = t16a; - } - - /* save the states */ - for (i=0;i>15 = Q27 - } - - sh = WebRtcSpl_NormW32(tmp32); // tmp32 is the gain - den16 = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(tmp32, sh-16); //Q(27+sh-16) = Q(sh+11) (all 16 bits are value bits) - inv_gain32 = WebRtcSpl_DivW32W16((WebRtc_Word32)2147483647, den16); // 1/gain in Q31/Q(sh+11) = Q(20-sh) - - //initial conditions - inv_gain16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(inv_gain32, 2); // 1/gain in Q(20-sh-2) = Q(18-sh) - - for (i=0;iQ26 - tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(inv_gain16, tmp32); //lat_in[]*inv_gain in (Q(18-sh)*Q26)>>16 = Q(28-sh) - tmp32 = WEBRTC_SPL_SHIFT_W32(tmp32, -(28-sh)); // lat_in[]*inv_gain in Q0 - - ARfQ0vec[i] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp32, -32768); // Q0 - } - - for (i=orderCoef-1;i>=0;i--) //get the state of f&g for the first input, for all orders - { - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(cthQ15[i],ARfQ0vec[0])) - (WEBRTC_SPL_MUL_16_16(sthQ15[i],stateGQ0[i])) + 16384), 15); - tmpAR = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp32, -32768); // Q0 - - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(sthQ15[i],ARfQ0vec[0])) + (WEBRTC_SPL_MUL_16_16(cthQ15[i], stateGQ0[i])) + 16384), 15); - ARgQ0vec[i+1] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp32, -32768); // Q0 - ARfQ0vec[0] = tmpAR; - } - ARgQ0vec[0] = ARfQ0vec[0]; - - for(n=0;n=0;k--) - { - tmp32 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(cthQ15[k], tmpAR)) - (WEBRTC_SPL_MUL_16_16(sthQ15[k], ARgQ0vec[k])) + 16384), 15); - tmp32_2 = WEBRTC_SPL_RSHIFT_W32(((WEBRTC_SPL_MUL_16_16(sthQ15[k], tmpAR)) + (WEBRTC_SPL_MUL_16_16(cthQ15[k], ARgQ0vec[k])) + 16384), 15); - tmpAR = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp32, -32768); // Q0 - - ARgQ0vec[k+1] = (WebRtc_Word16) WEBRTC_SPL_SAT(32767, tmp32_2, -32768); // Q0 - - } - ARfQ0vec[n+1] = tmpAR; - ARgQ0vec[0] = tmpAR; - } - - for(n=0;n Q15 - - for (m=useOrder-1; m>0; m--) { - tmp_inv_denum32 = ((WebRtc_Word32) 1073741823) - WEBRTC_SPL_MUL_16_16(k16[m], k16[m]); // (1 - k^2) in Q30 - tmp_inv_denum16 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(tmp_inv_denum32, 15); // (1 - k^2) in Q15 - - for (k=1; k<=m; k++) { - tmp32b = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)a16[k], 16) - - WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_16(k16[m], a16[m-k+1]), 1); - - tmp32[k] = WebRtcSpl_DivW32W16(tmp32b, tmp_inv_denum16); //Q27/Q15 = Q12 - } - - for (k=1; k>1 => Q11 - } - - tmp32[m] = WEBRTC_SPL_SAT(4092, tmp32[m], -4092); - k16[m-1] = (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W32(tmp32[m], 3); //Q12<<3 => Q15 - } - - return; -} - - - - - -WebRtc_Word16 WebRtcSpl_LevinsonW32_JSK( - WebRtc_Word32 *R, /* (i) Autocorrelation of length >= order+1 */ - WebRtc_Word16 *A, /* (o) A[0..order] LPC coefficients (Q11) */ - WebRtc_Word16 *K, /* (o) K[0...order-1] Reflection coefficients (Q15) */ - WebRtc_Word16 order /* (i) filter order */ - ) { - WebRtc_Word16 i, j; - WebRtc_Word16 R_hi[LEVINSON_MAX_ORDER+1], R_low[LEVINSON_MAX_ORDER+1]; - /* Aurocorr coefficients in high precision */ - WebRtc_Word16 A_hi[LEVINSON_MAX_ORDER+1], A_low[LEVINSON_MAX_ORDER+1]; - /* LPC coefficients in high precicion */ - WebRtc_Word16 A_upd_hi[LEVINSON_MAX_ORDER+1], A_upd_low[LEVINSON_MAX_ORDER+1]; - /* LPC coefficients for next iteration */ - WebRtc_Word16 K_hi, K_low; /* reflection coefficient in high precision */ - WebRtc_Word16 Alpha_hi, Alpha_low, Alpha_exp; /* Prediction gain Alpha in high precision - and with scale factor */ - WebRtc_Word16 tmp_hi, tmp_low; - WebRtc_Word32 temp1W32, temp2W32, temp3W32; - WebRtc_Word16 norm; - - /* Normalize the autocorrelation R[0]...R[order+1] */ - - norm = WebRtcSpl_NormW32(R[0]); - - for (i=order;i>=0;i--) { - temp1W32 = WEBRTC_SPL_LSHIFT_W32(R[i], norm); - /* Put R in hi and low format */ - R_hi[i] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - R_low[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)R_hi[i], 16)), 1); - } - - /* K = A[1] = -R[1] / R[0] */ - - temp2W32 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)R_hi[1],16) + - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)R_low[1],1); /* R[1] in Q31 */ - temp3W32 = WEBRTC_SPL_ABS_W32(temp2W32); /* abs R[1] */ - temp1W32 = WebRtcSpl_DivW32HiLow(temp3W32, R_hi[0], R_low[0]); /* abs(R[1])/R[0] in Q31 */ - /* Put back the sign on R[1] */ - if (temp2W32 > 0) { - temp1W32 = -temp1W32; - } - - /* Put K in hi and low format */ - K_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - K_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)K_hi, 16)), 1); - - /* Store first reflection coefficient */ - K[0] = K_hi; - - temp1W32 = WEBRTC_SPL_RSHIFT_W32(temp1W32, 4); /* A[1] in Q27 */ - - /* Put A[1] in hi and low format */ - A_hi[1] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - A_low[1] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)A_hi[1], 16)), 1); - - /* Alpha = R[0] * (1-K^2) */ - - temp1W32 = WEBRTC_SPL_LSHIFT_W32((WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(K_hi, K_low), 14) + - WEBRTC_SPL_MUL_16_16(K_hi, K_hi)), 1); /* temp1W32 = k^2 in Q31 */ - - temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* Guard against <0 */ - temp1W32 = (WebRtc_Word32)0x7fffffffL - temp1W32; /* temp1W32 = (1 - K[0]*K[0]) in Q31 */ - - /* Store temp1W32 = 1 - K[0]*K[0] on hi and low format */ - tmp_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - tmp_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)tmp_hi, 16)), 1); - - /* Calculate Alpha in Q31 */ - temp1W32 = WEBRTC_SPL_LSHIFT_W32((WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_hi) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(R_hi[0], tmp_low), 15) + - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(R_low[0], tmp_hi), 15) ), 1); - - /* Normalize Alpha and put it in hi and low format */ - - Alpha_exp = WebRtcSpl_NormW32(temp1W32); - temp1W32 = WEBRTC_SPL_LSHIFT_W32(temp1W32, Alpha_exp); - Alpha_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp1W32, 16); - Alpha_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp1W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)Alpha_hi, 16)), 1); - - /* Perform the iterative calculations in the - Levinson Durbin algorithm */ - - for (i=2; i<=order; i++) - { - - /* ---- - \ - temp1W32 = R[i] + > R[j]*A[i-j] - / - ---- - j=1..i-1 - */ - - temp1W32 = 0; - - for(j=1; j 0) { - temp3W32 = -temp3W32; - } - - /* Use the Alpha shifts from earlier to denormalize */ - norm = WebRtcSpl_NormW32(temp3W32); - if ((Alpha_exp <= norm)||(temp3W32==0)) { - temp3W32 = WEBRTC_SPL_LSHIFT_W32(temp3W32, Alpha_exp); - } else { - if (temp3W32 > 0) - { - temp3W32 = (WebRtc_Word32)0x7fffffffL; - } else - { - temp3W32 = (WebRtc_Word32)0x80000000L; - } - } - - /* Put K on hi and low format */ - K_hi = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(temp3W32, 16); - K_low = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((temp3W32 - WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)K_hi, 16)), 1); - - /* Store Reflection coefficient in Q15 */ - K[i-1] = K_hi; - - /* Test for unstable filter. If unstable return 0 and let the - user decide what to do in that case - */ - - if ((WebRtc_Word32)WEBRTC_SPL_ABS_W16(K_hi) > (WebRtc_Word32)32740) { - return(-i); /* Unstable filter */ - } - - /* - Compute updated LPC coefficient: Anew[i] - Anew[j]= A[j] + K*A[i-j] for j=1..i-1 - Anew[i]= K - */ - - for(j=1; j>11 => Q17 */ - pg3 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(pgQ, pg3,13); /* Q17*Q14>>13 =>Q18 */ - pg3 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(pg3, kMulPitchGain ,5); /* Q10 kMulPitchGain = -25 = -200 in Q-3. */ - - tmp16=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,pg3,13);/* Q13*Q10>>13 => Q10*/ - if (tmp16<0) { - tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); - tmp16_1 = (WEBRTC_SPL_RSHIFT_W16((WebRtc_UWord16)(tmp16 ^ 0xFFFF), 10)-3); /* Gives result in Q14 */ - if (tmp16_1<0) - expPg=(WebRtc_Word16) -WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); - else - expPg=(WebRtc_Word16) -WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); - } else - expPg = (WebRtc_Word16) -16384; /* 1 in Q14, since 2^0=1 */ - - expPg32 = (WebRtc_Word32)WEBRTC_SPL_LSHIFT_W16((WebRtc_Word32)expPg, 8); /* Q22 */ - divVal = WebRtcSpl_DivW32W16ResW16(expPg32, chngQ); /* Q22/Q12=Q10 */ - - tmp16=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,divVal,13);/* Q13*Q10>>13 => Q10*/ - if (tmp16<0) { - tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); - tmp16_1 = (WEBRTC_SPL_RSHIFT_W16((WebRtc_UWord16)(tmp16 ^ 0xFFFF), 10)-3); /* Gives result in Q14 */ - if (tmp16_1<0) - expPg=(WebRtc_Word16) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); - else - expPg=(WebRtc_Word16) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); - } else - expPg = (WebRtc_Word16) 16384; /* 1 in Q14, since 2^0=1 */ - - *varscale = expPg-1; - *oldEnergy = nrgQ[3]; -} - - - -static __inline WebRtc_Word16 exp2_Q10_T(WebRtc_Word16 x) { // Both in and out in Q10 - - WebRtc_Word16 tmp16_1, tmp16_2; - - tmp16_2=(WebRtc_Word16)(0x0400|(x&0x03FF)); - tmp16_1=-(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(x,10); - if(tmp16_1>0) - return (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); - else - return (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); - -} - - - - -void WebRtcIsacfix_GetLpcCoef(WebRtc_Word16 *inLoQ0, - WebRtc_Word16 *inHiQ0, - MaskFiltstr_enc *maskdata, - WebRtc_Word16 snrQ10, - const WebRtc_Word16 *pitchGains_Q12, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *lo_coeffQ15, - WebRtc_Word16 *hi_coeffQ15) -{ - int k, n, j, ii; - WebRtc_Word16 pos1, pos2; - WebRtc_Word16 sh_lo, sh_hi, sh, ssh, shMem; - WebRtc_Word16 varscaleQ14; - - WebRtc_Word16 tmpQQlo, tmpQQhi; - WebRtc_Word32 tmp32; - WebRtc_Word16 tmp16,tmp16b; - - WebRtc_Word16 polyHI[ORDERHI+1]; - WebRtc_Word16 rcQ15_lo[ORDERLO], rcQ15_hi[ORDERHI]; - - - WebRtc_Word16 DataLoQ6[WINLEN], DataHiQ6[WINLEN]; - WebRtc_Word32 corrloQQ[ORDERLO+2]; - WebRtc_Word32 corrhiQQ[ORDERHI+1]; - WebRtc_Word32 corrlo2QQ[ORDERLO+1]; - WebRtc_Word16 scale; - WebRtc_Word16 QdomLO, QdomHI, newQdomHI, newQdomLO; - - WebRtc_Word32 round; - WebRtc_Word32 res_nrgQQ; - WebRtc_Word32 sqrt_nrg; - - WebRtc_Word32 aSQR32; - - /* less-noise-at-low-frequencies factor */ - WebRtc_Word16 aaQ14; - - /* Multiplication with 1/sqrt(12) ~= 0.28901734104046 can be done by convertion to - Q15, i.e. round(0.28901734104046*32768) = 9471, and use 9471/32768.0 ~= 0.289032 - */ - WebRtc_Word16 snrq, shft; - - WebRtc_Word16 tmp16a; - WebRtc_Word32 tmp32a, tmp32b, tmp32c; - - WebRtc_Word16 a_LOQ11[ORDERLO+1]; - WebRtc_Word16 k_vecloQ15[ORDERLO]; - WebRtc_Word16 a_HIQ12[ORDERHI+1]; - WebRtc_Word16 k_vechiQ15[ORDERHI]; - - WebRtc_Word16 stab; - - snrq=snrQ10; - - /* SNR= C * 2 ^ (D * snrq) ; C=0.289, D=0.05*log2(10)=0.166 (~=172 in Q10)*/ - tmp16 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(snrq, 172, 10); // Q10 - tmp16b = exp2_Q10_T(tmp16); // Q10 - snrq = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(tmp16b, 285, 10); // Q10 - - /* change quallevel depending on pitch gains and level fluctuations */ - WebRtcIsacfix_GetVars(inLoQ0, pitchGains_Q12, &(maskdata->OldEnergy), &varscaleQ14); - - /* less-noise-at-low-frequencies factor */ - /* Calculation of 0.35 * (0.5 + 0.5 * varscale) in fixpoint: - With 0.35 in Q16 (0.35 ~= 22938/65536.0 = 0.3500061) and varscaleQ14 in Q14, - we get Q16*Q14>>16 = Q14 - */ - aaQ14 = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32( - (WEBRTC_SPL_MUL_16_16(22938, (8192 + WEBRTC_SPL_RSHIFT_W32(varscaleQ14, 1))) - + ((WebRtc_Word32)32768)), 16); - - /* Calculate tmp = (1.0 + aa*aa); in Q12 */ - tmp16 = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(aaQ14, aaQ14, 15); //Q14*Q14>>15 = Q13 - tmpQQlo = 4096 + WEBRTC_SPL_RSHIFT_W16(tmp16, 1); // Q12 + Q13>>1 = Q12 - - /* Calculate tmp = (1.0+aa) * (1.0+aa); */ - tmp16 = 8192 + WEBRTC_SPL_RSHIFT_W16(aaQ14, 1); // 1+a in Q13 - tmpQQhi = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT(tmp16, tmp16, 14); //Q13*Q13>>14 = Q12 - - /* replace data in buffer by new look-ahead data */ - for (pos1 = 0; pos1 < QLOOKAHEAD; pos1++) { - maskdata->DataBufferLoQ0[pos1 + WINLEN - QLOOKAHEAD] = inLoQ0[pos1]; - } - - for (k = 0; k < SUBFRAMES; k++) { - - /* Update input buffer and multiply signal with window */ - for (pos1 = 0; pos1 < WINLEN - UPDATE/2; pos1++) { - maskdata->DataBufferLoQ0[pos1] = maskdata->DataBufferLoQ0[pos1 + UPDATE/2]; - maskdata->DataBufferHiQ0[pos1] = maskdata->DataBufferHiQ0[pos1 + UPDATE/2]; - DataLoQ6[pos1] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT( - maskdata->DataBufferLoQ0[pos1], kWindowAutocorr[pos1], 15); // Q0*Q21>>15 = Q6 - DataHiQ6[pos1] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT( - maskdata->DataBufferHiQ0[pos1], kWindowAutocorr[pos1], 15); // Q0*Q21>>15 = Q6 - } - pos2 = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16(k, UPDATE)/2); - for (n = 0; n < UPDATE/2; n++, pos1++) { - maskdata->DataBufferLoQ0[pos1] = inLoQ0[QLOOKAHEAD + pos2]; - maskdata->DataBufferHiQ0[pos1] = inHiQ0[pos2++]; - DataLoQ6[pos1] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT( - maskdata->DataBufferLoQ0[pos1], kWindowAutocorr[pos1], 15); // Q0*Q21>>15 = Q6 - DataHiQ6[pos1] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT( - maskdata->DataBufferHiQ0[pos1], kWindowAutocorr[pos1], 15); // Q0*Q21>>15 = Q6 - } - - /* Get correlation coefficients */ - /* The highest absolute value measured inside DataLo in the test set - For DataHi, corresponding value was 160. - - This means that it should be possible to represent the input values - to WebRtcSpl_AutoCorrelation() as Q6 values (since 307*2^6 = - 19648). Of course, Q0 will also work, but due to the low energy in - DataLo and DataHi, the outputted autocorrelation will be more accurate - and mimic the floating point code better, by being in an high as possible - Q-domain. - */ - - WebRtcIsacfix_AutocorrFix(corrloQQ,DataLoQ6,WINLEN, ORDERLO+1, &scale); - QdomLO = 12-scale; // QdomLO is the Q-domain of corrloQQ - sh_lo = WebRtcSpl_NormW32(corrloQQ[0]); - QdomLO += sh_lo; - for (ii=0; ii>1 = Q(QdomLO-5) - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, corrloQQ[1]), 2); // 2*Q(14+QdomLO-16)>>3 = Q(QdomLO-2)>>2 = Q(QdomLO-5) - - /* Calculate corrlo2[n] = tmpQQlo * corrlo[n] - tmpQQlo * (corrlo[n-1] + corrlo[n+1]);*/ - for (n = 1; n <= ORDERLO; n++) { - - tmp32 = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n-1], 1) + WEBRTC_SPL_RSHIFT_W32(corrloQQ[n+1], 1); // Q(QdomLO-1) - corrlo2QQ[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[n]), 1)- // Q(12+QdomLO-16)>>1 = Q(QdomLO-5) - WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, tmp32), 2); // Q(14+QdomLO-1-16)>>2 = Q(QdomLO-3)>>2 = Q(QdomLO-5) - - } - QdomLO -= 5; - - /* Calculate corrhi[n] = tmpQQhi * corrhi[n]; */ - for (n = 0; n <= ORDERHI; n++) { - corrhiQQ[n] = WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQhi, corrhiQQ[n]); // Q(12+QdomHI-16) = Q(QdomHI-4) - } - QdomHI -= 4; - - /* add white noise floor */ - /* corrlo2QQ is in Q(QdomLO) and corrhiQQ is in Q(QdomHI) */ - /* Calculate corrlo2[0] += 9.5367431640625e-7; and - corrhi[0] += 9.5367431640625e-7, where the constant is 1/2^20 */ - - tmp32 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32) 1, QdomLO-20); - corrlo2QQ[0] += tmp32; - tmp32 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32) 1, QdomHI-20); - corrhiQQ[0] += tmp32; - - /* corrlo2QQ is in Q(QdomLO) and corrhiQQ is in Q(QdomHI) before the following - code segment, where we want to make sure we get a 1-bit margin */ - for (n = 0; n <= ORDERLO; n++) { - corrlo2QQ[n] = WEBRTC_SPL_RSHIFT_W32(corrlo2QQ[n], 1); // Make sure we have a 1-bit margin - } - QdomLO -= 1; // Now, corrlo2QQ is in Q(QdomLO), with a 1-bit margin - - for (n = 0; n <= ORDERHI; n++) { - corrhiQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], 1); // Make sure we have a 1-bit margin - } - QdomHI -= 1; // Now, corrhiQQ is in Q(QdomHI), with a 1-bit margin - - - newQdomLO = QdomLO; - - for (n = 0; n <= ORDERLO; n++) { - WebRtc_Word32 tmp, tmpB, tmpCorr; - WebRtc_Word16 alpha=328; //0.01 in Q15 - WebRtc_Word16 beta=324; //(1-0.01)*0.01=0.0099 in Q15 - WebRtc_Word16 gamma=32440; //(1-0.01)=0.99 in Q15 - - if (maskdata->CorrBufLoQQ[n] != 0) { - shMem=WebRtcSpl_NormW32(maskdata->CorrBufLoQQ[n]); - sh = QdomLO - maskdata->CorrBufLoQdom[n]; - if (sh<=shMem) { - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], sh); // Get CorrBufLoQQ to same domain as corrlo2 - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha, tmp); - } else if ((sh-shMem)<7){ - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufLoQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, (sh-shMem)), tmp); // Shift alpha the number of times required to get tmp in QdomLO - } else { - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, 6), tmp); // Shift alpha as much as possible without overflow the number of times required to get tmp in QdomHI - tmpCorr = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n], sh-shMem-6); - tmp = tmp + tmpCorr; - maskdata->CorrBufLoQQ[n] = tmp; - newQdomLO = QdomLO-(sh-shMem-6); - maskdata->CorrBufLoQdom[n] = newQdomLO; - } - } else - tmp = 0; - - tmp = tmp + corrlo2QQ[n]; - - maskdata->CorrBufLoQQ[n] = tmp; - maskdata->CorrBufLoQdom[n] = QdomLO; - - tmp=WEBRTC_SPL_MUL_16_32_RSFT15(beta, tmp); - tmpB=WEBRTC_SPL_MUL_16_32_RSFT15(gamma, corrlo2QQ[n]); - corrlo2QQ[n] = tmp + tmpB; - } - if( newQdomLO!=QdomLO) { - for (n = 0; n <= ORDERLO; n++) { - if (maskdata->CorrBufLoQdom[n] != newQdomLO) - corrloQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrloQQ[n], maskdata->CorrBufLoQdom[n]-newQdomLO); - } - QdomLO = newQdomLO; - } - - - newQdomHI = QdomHI; - - for (n = 0; n <= ORDERHI; n++) { - WebRtc_Word32 tmp, tmpB, tmpCorr; - WebRtc_Word16 alpha=328; //0.01 in Q15 - WebRtc_Word16 beta=324; //(1-0.01)*0.01=0.0099 in Q15 - WebRtc_Word16 gamma=32440; //(1-0.01)=0.99 in Q1 - if (maskdata->CorrBufHiQQ[n] != 0) { - shMem=WebRtcSpl_NormW32(maskdata->CorrBufHiQQ[n]); - sh = QdomHI - maskdata->CorrBufHiQdom[n]; - if (sh<=shMem) { - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], sh); // Get CorrBufHiQQ to same domain as corrhi - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha, tmp); - tmpCorr = corrhiQQ[n]; - tmp = tmp + tmpCorr; - maskdata->CorrBufHiQQ[n] = tmp; - maskdata->CorrBufHiQdom[n] = QdomHI; - } else if ((sh-shMem)<7) { - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, (sh-shMem)), tmp); // Shift alpha the number of times required to get tmp in QdomHI - tmpCorr = corrhiQQ[n]; - tmp = tmp + tmpCorr; - maskdata->CorrBufHiQQ[n] = tmp; - maskdata->CorrBufHiQdom[n] = QdomHI; - } else { - tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible - tmp = WEBRTC_SPL_MUL_16_32_RSFT15(WEBRTC_SPL_LSHIFT_W16(alpha, 6), tmp); // Shift alpha as much as possible without overflow the number of times required to get tmp in QdomHI - tmpCorr = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], sh-shMem-6); - tmp = tmp + tmpCorr; - maskdata->CorrBufHiQQ[n] = tmp; - newQdomHI = QdomHI-(sh-shMem-6); - maskdata->CorrBufHiQdom[n] = newQdomHI; - } - } else { - tmp = corrhiQQ[n]; - tmpCorr = tmp; - maskdata->CorrBufHiQQ[n] = tmp; - maskdata->CorrBufHiQdom[n] = QdomHI; - } - - tmp=WEBRTC_SPL_MUL_16_32_RSFT15(beta, tmp); - tmpB=WEBRTC_SPL_MUL_16_32_RSFT15(gamma, tmpCorr); - corrhiQQ[n] = tmp + tmpB; - } - - if( newQdomHI!=QdomHI) { - for (n = 0; n <= ORDERHI; n++) { - if (maskdata->CorrBufHiQdom[n] != newQdomHI) - corrhiQQ[n] = WEBRTC_SPL_RSHIFT_W32(corrhiQQ[n], maskdata->CorrBufHiQdom[n]-newQdomHI); - } - QdomHI = newQdomHI; - } - - stab=WebRtcSpl_LevinsonW32_JSK(corrlo2QQ, a_LOQ11, k_vecloQ15, ORDERLO); - - if (stab<0) { // If unstable use lower order - a_LOQ11[0]=2048; - for (n = 1; n <= ORDERLO; n++) { - a_LOQ11[n]=0; - } - - stab=WebRtcSpl_LevinsonW32_JSK(corrlo2QQ, a_LOQ11, k_vecloQ15, 8); - } - - - WebRtcSpl_LevinsonDurbin(corrhiQQ, a_HIQ12, k_vechiQ15, ORDERHI); - - /* bandwidth expansion */ - for (n = 1; n <= ORDERLO; n++) { - a_LOQ11[n] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(kPolyVecLo[n-1], a_LOQ11[n]); - } - - - polyHI[0] = a_HIQ12[0]; - for (n = 1; n <= ORDERHI; n++) { - a_HIQ12[n] = (WebRtc_Word16) WEBRTC_SPL_MUL_16_16_RSFT_WITH_FIXROUND(kPolyVecHi[n-1], a_HIQ12[n]); - polyHI[n] = a_HIQ12[n]; - } - - /* Normalize the corrlo2 vector */ - sh = WebRtcSpl_NormW32(corrlo2QQ[0]); - for (n = 0; n <= ORDERLO; n++) { - corrlo2QQ[n] = WEBRTC_SPL_LSHIFT_W32(corrlo2QQ[n], sh); - } - QdomLO += sh; /* Now, corrlo2QQ is still in Q(QdomLO) */ - - - /* residual energy */ - - sh_lo = 31; - res_nrgQQ = 0; - for (j = 0; j <= ORDERLO; j++) - { - for (n = 0; n < j; n++) - { - WebRtc_Word16 index, diff, sh_corr; - - index = j - n; //WEBRTC_SPL_ABS_W16(j-n); - - /* Calculation of res_nrg += a_LO[j] * corrlo2[j-n] * a_LO[n]; */ - /* corrlo2QQ is in Q(QdomLO) */ - tmp32 = ((WebRtc_Word32) WEBRTC_SPL_MUL_16_16(a_LOQ11[j], a_LOQ11[n])); // Q11*Q11 = Q22 - // multiply by 2 as loop only on half of the matrix. a_LOQ11 gone through bandwidth - // expation so the following shift is safe. - tmp32 = WEBRTC_SPL_LSHIFT_W32(tmp32, 1); - sh = WebRtcSpl_NormW32(tmp32); - aSQR32 = WEBRTC_SPL_LSHIFT_W32(tmp32, sh); // Q(22+sh) - sh_corr = WebRtcSpl_NormW32(corrlo2QQ[index]); - tmp32 = WEBRTC_SPL_LSHIFT_W32(corrlo2QQ[index], sh_corr); - tmp32 = (WebRtc_Word32) WEBRTC_SPL_MUL_32_32_RSFT32BI(aSQR32, tmp32); // Q(22+sh)*Q(QdomLO+sh_corr)>>32 = Q(22+sh+QdomLO+sh_corr-32) = Q(sh+QdomLO+sh_corr-10) - sh = sh+QdomLO+sh_corr-10; - - diff = sh_lo-sh; - - round = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, (WEBRTC_SPL_ABS_W32(diff)-1)); - if (diff==0) - round = 0; - if (diff>=31) { - res_nrgQQ = tmp32; - sh_lo = sh; - } else if (diff>0) { - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32((res_nrgQQ+round), (diff+1)) + WEBRTC_SPL_RSHIFT_W32(tmp32, 1); - sh_lo = sh-1; - } else if (diff>-31){ - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1) + WEBRTC_SPL_SHIFT_W32((tmp32+round), -(-diff+1)); - sh_lo = sh_lo-1; - } - sh = WebRtcSpl_NormW32(res_nrgQQ); - res_nrgQQ = WEBRTC_SPL_LSHIFT_W32(res_nrgQQ, sh); - sh_lo += sh; - } - n = j; - { - WebRtc_Word16 index, diff, sh_corr; - - index = 0; //WEBRTC_SPL_ABS_W16(j-n); - - /* Calculation of res_nrg += a_LO[j] * corrlo2[j-n] * a_LO[n]; */ - /* corrlo2QQ is in Q(QdomLO) */ - tmp32 = (WebRtc_Word32) WEBRTC_SPL_MUL_16_16(a_LOQ11[j], a_LOQ11[n]); // Q11*Q11 = Q22 - sh = WebRtcSpl_NormW32(tmp32); - aSQR32 = WEBRTC_SPL_LSHIFT_W32(tmp32, sh); // Q(22+sh) - sh_corr = WebRtcSpl_NormW32(corrlo2QQ[index]); - tmp32 = WEBRTC_SPL_LSHIFT_W32(corrlo2QQ[index], sh_corr); - tmp32 = (WebRtc_Word32) WEBRTC_SPL_MUL_32_32_RSFT32BI(aSQR32, tmp32); // Q(22+sh)*Q(QdomLO+sh_corr)>>32 = Q(22+sh+QdomLO+sh_corr-32) = Q(sh+QdomLO+sh_corr-10) - sh = sh+QdomLO+sh_corr-10; - diff = sh_lo-sh; - - round = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, (WEBRTC_SPL_ABS_W32(diff)-1)); - if (diff==0) - round = 0; - if (diff>=31) { - res_nrgQQ = tmp32; - sh_lo = sh; - } else if (diff>0) { - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32((res_nrgQQ+round), (diff+1)) + WEBRTC_SPL_RSHIFT_W32(tmp32, 1); - sh_lo = sh-1; - } else if (diff>-31){ - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1) + WEBRTC_SPL_SHIFT_W32((tmp32+round), -(-diff+1)); - sh_lo = sh_lo-1; - } - sh = WebRtcSpl_NormW32(res_nrgQQ); - res_nrgQQ = WEBRTC_SPL_LSHIFT_W32(res_nrgQQ, sh); - sh_lo += sh; - } - } - /* Convert to reflection coefficients */ - - - WebRtcSpl_AToK_JSK(a_LOQ11, ORDERLO, rcQ15_lo); - - if (sh_lo & 0x0001) { - res_nrgQQ=WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1); - sh_lo-=1; - } - - - if( res_nrgQQ > 0 ) - { - sqrt_nrg=WebRtcSpl_Sqrt(res_nrgQQ); - - /* add hearing threshold and compute the gain */ - /* lo_coeff = varscale * S_N_R / (sqrt_nrg + varscale * H_T_H); */ - - - //tmp32a=WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, H_T_HQ19, 17); // Q14 - tmp32a=WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32) varscaleQ14,1); // H_T_HQ19=65536 (16-17=-1) ssh= WEBRTC_SPL_RSHIFT_W16(sh_lo, 1); // sqrt_nrg is in Qssh - ssh= WEBRTC_SPL_RSHIFT_W16(sh_lo, 1); // sqrt_nrg is in Qssh - sh = ssh - 14; - tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh - tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) - tmp32a = WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, snrq, 0); //Q24 (numerator) - - sh = WebRtcSpl_NormW32(tmp32c); - shft = 16 - sh; - tmp16a = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(tmp32c, -shft); // Q(ssh-shft) (denominator) - - tmp32b = WebRtcSpl_DivW32W16(tmp32a, tmp16a); // Q(24-ssh+shft) - sh = ssh-shft-7; - *gain_lo_hiQ17 = WEBRTC_SPL_SHIFT_W32(tmp32b, sh); // Gains in Q17 - } - else - { - *gain_lo_hiQ17 = 100; //(WebRtc_Word32)WEBRTC_SPL_LSHIFT_W32( (WebRtc_Word32)1, 17); // Gains in Q17 - } - gain_lo_hiQ17++; - - /* copy coefficients to output array */ - for (n = 0; n < ORDERLO; n++) { - *lo_coeffQ15 = (WebRtc_Word16) (rcQ15_lo[n]); - lo_coeffQ15++; - } - /* residual energy */ - res_nrgQQ = 0; - sh_hi = 31; - - - for (j = 0; j <= ORDERHI; j++) - { - for (n = 0; n < j; n++) - { - WebRtc_Word16 index, diff, sh_corr; - - index = j-n; //WEBRTC_SPL_ABS_W16(j-n); - - /* Calculation of res_nrg += a_HI[j] * corrhi[j-n] * a_HI[n] * 2; for j != n */ - /* corrhiQQ is in Q(QdomHI) */ - tmp32 = ((WebRtc_Word32) WEBRTC_SPL_MUL_16_16(a_HIQ12[j], a_HIQ12[n])); // Q12*Q12 = Q24 - tmp32 = WEBRTC_SPL_LSHIFT_W32(tmp32, 1); - sh = WebRtcSpl_NormW32(tmp32); - aSQR32 = WEBRTC_SPL_LSHIFT_W32(tmp32, sh); // Q(24+sh) - sh_corr = WebRtcSpl_NormW32(corrhiQQ[index]); - tmp32 = WEBRTC_SPL_LSHIFT_W32(corrhiQQ[index],sh_corr); - tmp32 = (WebRtc_Word32) WEBRTC_SPL_MUL_32_32_RSFT32BI(aSQR32, tmp32); // Q(24+sh)*Q(QdomHI+sh_corr)>>32 = Q(24+sh+QdomHI+sh_corr-32) = Q(sh+QdomHI+sh_corr-8) - sh = sh+QdomHI+sh_corr-8; - diff = sh_hi-sh; - - round = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, (WEBRTC_SPL_ABS_W32(diff)-1)); - if (diff==0) - round = 0; - if (diff>=31) { - res_nrgQQ = tmp32; - sh_hi = sh; - } else if (diff>0) { - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32((res_nrgQQ+round), (diff+1)) + WEBRTC_SPL_RSHIFT_W32(tmp32, 1); - sh_hi = sh-1; - } else if (diff>-31){ - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1) + WEBRTC_SPL_SHIFT_W32((tmp32+round), -(-diff+1)); - sh_hi = sh_hi-1; - } - - sh = WebRtcSpl_NormW32(res_nrgQQ); - res_nrgQQ = WEBRTC_SPL_LSHIFT_W32(res_nrgQQ, sh); - sh_hi += sh; - } - - n = j; - { - WebRtc_Word16 index, diff, sh_corr; - - index = 0; //n-j; //WEBRTC_SPL_ABS_W16(j-n); - - /* Calculation of res_nrg += a_HI[j] * corrhi[j-n] * a_HI[n];*/ - /* corrhiQQ is in Q(QdomHI) */ - tmp32 = ((WebRtc_Word32) WEBRTC_SPL_MUL_16_16(a_HIQ12[j], a_HIQ12[n])); // Q12*Q12 = Q24 - sh = WebRtcSpl_NormW32(tmp32); - aSQR32 = WEBRTC_SPL_LSHIFT_W32(tmp32, sh); // Q(24+sh) - sh_corr = WebRtcSpl_NormW32(corrhiQQ[index]); - tmp32 = WEBRTC_SPL_LSHIFT_W32(corrhiQQ[index],sh_corr); - tmp32 = (WebRtc_Word32) WEBRTC_SPL_MUL_32_32_RSFT32BI(aSQR32, tmp32); // Q(24+sh)*Q(QdomHI+sh_corr)>>32 = Q(24+sh+QdomHI+sh_corr-32) = Q(sh+QdomHI+sh_corr-8) - sh = sh+QdomHI+sh_corr-8; - diff = sh_hi-sh; - - round = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, (WEBRTC_SPL_ABS_W32(diff)-1)); - if (diff==0) - round = 0; - if (diff>=31) { - res_nrgQQ = tmp32; - sh_hi = sh; - } else if (diff>0) { - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32((res_nrgQQ+round), (diff+1)) + WEBRTC_SPL_RSHIFT_W32(tmp32, 1); - sh_hi = sh-1; - } else if (diff>-31){ - res_nrgQQ = WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1) + WEBRTC_SPL_SHIFT_W32((tmp32+round), -(-diff+1)); - sh_hi = sh_hi-1; - } - - sh = WebRtcSpl_NormW32(res_nrgQQ); - res_nrgQQ = WEBRTC_SPL_LSHIFT_W32(res_nrgQQ, sh); - sh_hi += sh; - } - } - - /* Convert to reflection coefficients */ - WebRtcSpl_LpcToReflCoef(polyHI, ORDERHI, rcQ15_hi); - - if (sh_hi & 0x0001) { - res_nrgQQ=WEBRTC_SPL_RSHIFT_W32(res_nrgQQ, 1); - sh_hi-=1; - } - - - if( res_nrgQQ > 0 ) - { - sqrt_nrg=WebRtcSpl_Sqrt(res_nrgQQ); - - - /* add hearing threshold and compute the gain */ - /* hi_coeff = varscale * S_N_R / (sqrt_nrg + varscale * H_T_H); */ - - //tmp32a=WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, H_T_HQ19, 17); // Q14 - tmp32a=WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32) varscaleQ14,1); // H_T_HQ19=65536 (16-17=-1) - - ssh= WEBRTC_SPL_RSHIFT_W32(sh_hi, 1); // sqrt_nrg is in Qssh - sh = ssh - 14; - tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh - tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) - tmp32a = WEBRTC_SPL_MUL_16_16_RSFT(varscaleQ14, snrq, 0); //Q24 (numerator) - - sh = WebRtcSpl_NormW32(tmp32c); - shft = 16 - sh; - tmp16a = (WebRtc_Word16) WEBRTC_SPL_SHIFT_W32(tmp32c, -shft); // Q(ssh-shft) (denominator) - - tmp32b = WebRtcSpl_DivW32W16(tmp32a, tmp16a); // Q(24-ssh+shft) - sh = ssh-shft-7; - *gain_lo_hiQ17 = WEBRTC_SPL_SHIFT_W32(tmp32b, sh); // Gains in Q17 - } - else - { - *gain_lo_hiQ17 = 100; //(WebRtc_Word32)WEBRTC_SPL_LSHIFT_W32( (WebRtc_Word32)1, 17); // Gains in Q17 - } - gain_lo_hiQ17++; - - - /* copy coefficients to output array */ - for (n = 0; n < ORDERHI; n++) { - *hi_coeffQ15 = rcQ15_hi[n]; - hi_coeffQ15++; - } - } -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/lpc_masking_model.h b/modules/audio_coding/codecs/iSAC/fix/source/lpc_masking_model.h deleted file mode 100644 index 9a64844b0..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/lpc_masking_model.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_masking_model.h - * - * LPC functions - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_MASKING_MODEL_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_MASKING_MODEL_H_ - -#include "structs.h" - -void WebRtcIsacfix_GetVars(const WebRtc_Word16 *input, - const WebRtc_Word16 *pitchGains_Q12, - WebRtc_UWord32 *oldEnergy, - WebRtc_Word16 *varscale); - -void WebRtcIsacfix_GetLpcCoef(WebRtc_Word16 *inLoQ0, - WebRtc_Word16 *inHiQ0, - MaskFiltstr_enc *maskdata, - WebRtc_Word16 snrQ10, - const WebRtc_Word16 *pitchGains_Q12, - WebRtc_Word32 *gain_lo_hiQ17, - WebRtc_Word16 *lo_coeffQ15, - WebRtc_Word16 *hi_coeffQ15); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_MASKING_MODEL_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.c b/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.c deleted file mode 100644 index 90cc9af47..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_tables.c - * - * Coding tables for the KLT coefficients - * - */ - - -#include "settings.h" -#include "lpc_tables.h" - -/* indices of KLT coefficients used */ -const WebRtc_UWord16 WebRtcIsacfix_kSelIndGain[12] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11}; - -const WebRtc_UWord16 WebRtcIsacfix_kSelIndShape[108] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 107 -}; - -/* cdf array for model indicator */ -const WebRtc_UWord16 WebRtcIsacfix_kModelCdf[4] = { - 0, 15434, 37548, 65535 -}; - -/* pointer to cdf array for model indicator */ -const WebRtc_UWord16 *WebRtcIsacfix_kModelCdfPtr[1] = { - WebRtcIsacfix_kModelCdf -}; - -/* initial cdf index for decoder of model indicator */ -const WebRtc_UWord16 WebRtcIsacfix_kModelInitIndex[1] = { - 1 -}; - -/* offset to go from rounded value to quantization index */ -const WebRtc_Word16 WebRtcIsacfix_kQuantMinGain[12] ={ - 3, 6, 4, 6, 6, 9, 5, 16, 11, 34, 32, 47 -}; - -const WebRtc_Word16 WebRtcIsacfix_kQuantMinShape[108] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 2, 2, 3, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 4, 3, 5, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 2, 1, 2, 2, 3, 4, - 4, 7, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 2, 3, 2, 3, 4, 4, 5, 7, 13, - 0, 1, 1, 2, 3, 2, 2, 2, 4, 4, - 5, 6, 7, 11, 9, 13, 12, 26 -}; - -/* maximum quantization index */ -const WebRtc_UWord16 WebRtcIsacfix_kMaxIndGain[12] = { - 6, 12, 8, 14, 10, 19, 12, 31, 22, 56, 52, 138 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kMaxIndShape[108] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 2, 2, 2, 2, 4, 4, 5, 6, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 2, 2, - 2, 2, 3, 4, 5, 7, 0, 0, 0, 0, - 2, 0, 2, 2, 2, 2, 3, 2, 2, 4, - 4, 6, 6, 9, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 3, 2, 4, 4, 7, 7, - 9, 13, 0, 0, 2, 2, 2, 2, 2, 2, - 3, 4, 5, 4, 6, 8, 8, 10, 16, 25, - 0, 2, 2, 4, 5, 4, 4, 4, 7, 8, - 9, 10, 13, 19, 17, 23, 25, 49 -}; - -/* index offset */ -const WebRtc_UWord16 WebRtcIsacfix_kOffsetGain[3][12] = { - { 0, 7, 20, 29, 44, 55, 75, 88, 120, 143, 200, 253}, - { 0, 7, 19, 27, 42, 53, 73, 86, 117, 140, 197, 249}, - { 0, 7, 20, 28, 44, 55, 75, 89, 121, 145, 202, 257} -}; - -const WebRtc_UWord16 WebRtcIsacfix_kOffsetShape[3][108] = { - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 11, 14, 17, 20, 23, 28, 33, 39, 46, 47, - 48, 49, 50, 52, 53, 54, 55, 56, 58, 61, - 64, 67, 70, 74, 79, 85, 93, 94, 95, 96, - 97, 100, 101, 104, 107, 110, 113, 117, 120, 123, - 128, 133, 140, 147, 157, 158, 159, 160, 161, 164, - 167, 170, 173, 176, 179, 183, 186, 191, 196, 204, - 212, 222, 236, 237, 238, 241, 244, 247, 250, 253, - 256, 260, 265, 271, 276, 283, 292, 301, 312, 329, - 355, 356, 359, 362, 367, 373, 378, 383, 388, 396, - 405, 415, 426, 440, 460, 478, 502, 528 - }, - { - 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, - 13, 16, 19, 22, 26, 29, 34, 39, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 55, 57, 60, - 63, 66, 70, 73, 78, 84, 91, 92, 93, 94, - 95, 96, 97, 99, 102, 105, 108, 111, 114, 118, - 123, 128, 134, 141, 151, 152, 153, 154, 156, 159, - 162, 165, 168, 171, 174, 177, 181, 186, 194, 200, - 208, 218, 233, 234, 235, 236, 239, 242, 245, 248, - 251, 254, 258, 263, 270, 277, 288, 297, 308, 324, - 349, 351, 354, 357, 361, 366, 372, 378, 383, 390, - 398, 407, 420, 431, 450, 472, 496, 524 - }, - { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, - 14, 17, 20, 23, 26, 29, 34, 40, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 58, 61, 64, - 67, 70, 73, 77, 82, 88, 96, 97, 98, 99, - 101, 102, 104, 107, 110, 113, 116, 119, 122, 125, - 129, 134, 141, 150, 160, 161, 162, 163, 166, 168, - 171, 174, 177, 180, 183, 186, 190, 195, 201, 208, - 216, 226, 243, 244, 245, 248, 251, 254, 257, 260, - 263, 268, 273, 278, 284, 291, 299, 310, 323, 340, - 366, 368, 371, 374, 379, 383, 389, 394, 399, 406, - 414, 422, 433, 445, 461, 480, 505, 533 - } -}; - -/* initial cdf index for KLT coefficients */ -const WebRtc_UWord16 WebRtcIsacfix_kInitIndexGain[3][12] = { - { 3, 6, 4, 7, 5, 10, 6, 16, 11, 28, 26, 69}, - { 3, 6, 4, 7, 5, 10, 6, 15, 11, 28, 26, 69}, - { 3, 6, 4, 8, 5, 10, 7, 16, 12, 28, 27, 70} -}; - -const WebRtc_UWord16 WebRtcIsacfix_kInitIndexShape[3][108] = { - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 2, 2, 3, 3, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 2, 2, 3, 4, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 1, 2, 1, 1, 2, - 2, 3, 3, 5, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 2, 1, 2, 2, 4, 4, - 5, 7, 0, 0, 1, 1, 1, 1, 1, 1, - 2, 2, 3, 2, 3, 4, 4, 5, 8, 13, - 0, 1, 1, 2, 3, 2, 2, 2, 4, 4, - 5, 5, 7, 10, 9, 12, 13, 25 - }, - { - 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 1, 1, 1, 2, 1, 2, 2, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 2, 1, 2, 3, 3, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, - 2, 3, 3, 5, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 2, 4, 3, 4, - 5, 7, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 2, 2, 3, 3, 5, 4, 5, 8, 12, - 1, 1, 1, 2, 2, 3, 3, 2, 3, 4, - 4, 6, 5, 9, 11, 12, 14, 25 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 2, 3, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 2, 2, 3, 4, 0, 0, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 3, 4, 5, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, - 5, 8, 0, 0, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 3, 3, 4, 5, 6, 8, 13, - 1, 1, 1, 2, 2, 3, 2, 2, 3, 4, - 4, 5, 6, 8, 9, 12, 14, 25 - } -}; - -/* offsets for quantizer representation levels*/ -const WebRtc_UWord16 WebRtcIsacfix_kOfLevelsGain[3] = { - 0, 392, 779 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kOfLevelsShape[3] = { - 0, 578, 1152 -}; - -/* quantizer representation levels */ - - - -const WebRtc_Word32 WebRtcIsacfix_kLevelsGainQ17[1176] = { - -364547,-231664,-102123,-573,104807,238257,368823,-758583,-640135,-510291 - ,-377662,-252785,-113177,2627,112906,248601,389461,522691,644517,763974 - ,-538963,-368179,-245823,-106095,-890,104299,241111,350730,493190,-800763 - ,-646230,-510239,-382115,-248623,-111829,-2983,113852,251105,388114,519757 - ,644048,774712,896334,1057931,-770009,-635310,-503690,-375087,-248106,-108525 - ,-105,108259,243472,377948,519271,-1160885,-1032044,-914636,-777593,-647891 - ,-518408,-388028,-254321,-115293,-598,117849,251296,385367,515510,652727 - ,777432,920363,1038625,1153637,1316836,-632287,-505430,-379430,-248458,-118013 - ,-888,118762,250266,381650,513327,652169,766471,932113,-2107480,-1971030 - ,-1827020,-1698263,-1558670,-1436314,-1305377,-1172252,-1047355,-914202,-779651,-651001 - ,-520999,-390394,-255761,-123490,-1893,126839,256703,385493,518607,651760 - ,782750,908693,1044131,1163865,1311066,1424177,1582628,1709823,1831740,1955391 - ,-1423044,-1288917,-1181281,-1043222,-911770,-780354,-646799,-522664,-386721,-258266 - ,-128060,-1101,128233,259996,390336,519590,649290,778701,908010,1040796 - ,1161235,1306889,1441882,-4446002,-4301031,-4194304,-4080591,-3947740,-3808975,-3686530 - ,-3567839,-3383251,-3287089,-3136577,-3017405,-2869860,-2751321,-2619984,-2482932,-2354790 - ,-2223147,-2090669,-1964135,-1831208,-1706697,-1570817,-1446008,-1305386,-1175773,-1046066 - ,-915356,-785120,-653614,-524331,-393767,-260442,-130187,-799,128841,261466 - ,393616,520542,652117,784613,914159,1045399,1181072,1308971,1442502,1570346 - ,1693912,1843986,1966014,2090474,2224869,2364593,2475934,2628403,2752512,2856640 - ,-4192441,-4063232,-3917821,-3799195,-3666233,-3519199,-3411021,-3269192,-3135684,-3008826 - ,-2880875,-2747342,-2620981,-2494872,-2354979,-2229718,-2098939,-1964971,-1835399,-1703452 - ,-1572806,-1440482,-1311794,-1179338,-1046521,-919823,-785914,-655335,-523416,-395507 - ,-264833,-132184,-2546,131698,256217,391372,522688,651248,789964,909618 - ,1035305,1179145,1313824,1436934,1552353,1693722,1815508,1972826,2096328,2228224 - ,2359296,2490368,2598848,-6160384,-6029312,-5881382,-5767168,-5636096,-5505024,-5373952 - ,-5228418,-5110384,-4954923,-4880576,-4710990,-4587364,-4471340,-4333905,-4211513,-4051293 - ,-3907927,-3800105,-3675961,-3538640,-3413663,-3271148,-3152105,-3019103,-2869647,-2744015 - ,-2620639,-2479385,-2364211,-2227611,-2095427,-1974497,-1834168,-1703561,-1568961,-1439826 - ,-1309192,-1174050,-1050191,-917836,-786015,-656943,-518934,-394831,-257708,-128041 - ,1610,128991,264442,393977,521383,653849,788164,918641,1049122,1181971 - ,1308934,1439505,1571808,1706305,1836318,1966235,2097269,2228990,2357005,2490292 - ,2617400,2749680,2881234,3014880,3145637,3276467,3409099,3536637,3671493,3802918 - ,3929740,4065036,4194143,4325999,4456126,4586857,4717194,4843923,4978676,5110913 - ,5245281,5371394,5499780,5633779,5762611,5897682,6028688,6167546,6296465,6421682 - ,6548882,6682074,6809432,6941956,7078143,7204509,7334296,7475137,7609896,7732044 - ,7861604,8002039,8131670,8259222,8390299,8522399,8650037,8782348,8908402,9037815 - ,9164594,9300338,9434679,9574500,9699702,9833934,9948152,10083972,10244937,10332822 - ,10485760,10600122,10760754,10892964,11010048,11111004,11272192,11403264,11525091,11624984 - ,11796480,11915146,-393216,-262144,-101702,-740,100568,262144,393216,-786432 - ,-655360,-524288,-383907,-243301,-94956,-156,95547,269629,416691,524288 - ,655360,-393216,-262144,-88448,-37,87318,262144,393216,524288,-917504 - ,-786432,-655360,-495894,-373308,-267503,-93211,4119,91308,250895,393216 - ,526138,655360,786432,917504,-786432,-655360,-524288,-393216,-262144,-83497 - ,222,86893,240922,393216,524288,-1048576,-917504,-790472,-655360,-508639 - ,-383609,-262016,-95550,-3775,96692,256797,364847,534906,655360,786432 - ,889679,1048576,1179648,1310720,1441792,-655360,-524288,-377684,-248408,-93690 - ,1261,95441,227519,393216,524288,655360,786432,917504,-2097152,-1966080 - ,-1809470,-1703936,-1572864,-1441792,-1314289,-1195149,-1056205,-917504,-809951,-657769 - ,-521072,-383788,-248747,-106350,-2944,105550,243408,388548,521064,628732 - ,786432,885456,1064548,1179648,1310720,1441792,1572864,1703936,1835008,-1441792 - ,-1310720,-1179648,-1037570,-888492,-767774,-646634,-519935,-373458,-248029,-111915 - ,760,111232,247735,379432,507672,672699,786432,917504,1048576,1179648 - ,1310720,1441792,-4456448,-4325376,-4194304,-4063232,-3932160,-3801088,-3670016,-3538944 - ,-3407872,-3276800,-3145728,-3014656,-2883584,-2752512,-2647002,-2490368,-2359296,-2228224 - ,-2097152,-1951753,-1835008,-1703936,-1594177,-1462001,-1289150,-1160774,-1025917,-924928 - ,-782509,-641294,-516191,-386630,-251910,-118886,5210,121226,253949,386008 - ,517973,649374,780064,917783,1052462,1183856,1290593,1419389,1556641,1699884 - ,1835008,1988314,2090470,2228224,2359296,2490368,2621440,2752512,2883584,-3801088 - ,-3643514,-3539937,-3409931,-3263294,-3145658,-3012952,-2879230,-2752359,-2622556,-2483471 - ,-2357556,-2226500,-2093112,-1965892,-1833664,-1701035,-1567767,-1440320,-1310556,-1178339 - ,-1049625,-916812,-786477,-655277,-525050,-393773,-264828,-130696,-480,132126 - ,260116,394197,527846,652294,785563,917183,1049511,1175958,1308161,1438759 - ,1572253,1698835,1828535,1967072,2089391,2212798,2348901,2461547,2621440,2752512 - ,2883584,-7309870,-7203780,-7062699,-6939106,-6790819,-6672036,-6553600,-6422317,-6288422 - ,-6164694,-6026456,-5901410,-5754168,-5621459,-5502710,-5369686,-5240454,-5120712,-4976140 - ,-4847970,-4723070,-4589083,-4450923,-4324680,-4189892,-4065551,-3931803,-3800209,-3668539 - ,-3539395,-3404801,-3277470,-3141389,-3016710,-2885724,-2752612,-2618541,-2486762,-2354153 - ,-2225059,-2094984,-1968194,-1830895,-1699508,-1575743,-1444516,-1308683,-1179714,-1053088 - ,-917981,-783707,-653900,-524980,-395409,-260309,-131948,-3452,132113,263241 - ,392185,522597,654134,788288,919810,1045795,1179210,1314201,1444235,1574447 - ,1705193,1834009,1967332,2098102,2229019,2359147,2489859,2619878,2754966,2879671 - ,3014438,3146143,3276733,3405958,3542196,3667493,3798815,3932961,4062458,4187125 - ,4322346,4454875,4587752,4716809,4848274,4975027,5111957,5242215,5373085,5501158 - ,5640140,5762918,5895358,6024008,6157906,6290628,6422713,6546339,6675888,6815606 - ,6955288,7077501,7211630,7337893,7473635,7607175,7728310,7866475,7999658,8127888 - ,8241758,8386483,8522550,8641582,8771915,8922139,9038632,9179385,9313426,9437184 - ,9568256,9699328,9830400,9952933,10120004,10223616,10354688,10474645,10616832,-393216 - ,-262144,-85425,-121,82533,262144,393216,-786432,-655360,-524288,-379928 - ,-222821,-95200,287,95541,227093,393216,493567,655360,786432,-393216 - ,-262144,-86805,510,86722,262144,393216,524288,-1048576,-917504,-786432 - ,-624456,-529951,-395071,-241627,-101168,81,99975,241605,393216,524288 - ,655360,786432,917504,-786432,-655360,-524288,-393216,-230359,-95619,-137 - ,94425,226222,393216,524288,-1179648,-1048576,-917504,-773841,-655360,-492258 - ,-379715,-244707,-103621,-434,104523,242680,381575,523659,650565,786432 - ,917504,1048576,1179648,1310720,-786432,-629344,-524288,-376757,-242858,-101932 - ,-2715,107155,239212,366480,514943,655360,786432,917504,-2228224,-2097152 - ,-1966080,-1835008,-1703936,-1572864,-1441792,-1284584,-1179648,-1048819,-934658,-777181 - ,-626371,-515660,-377493,-248975,-113036,436,113584,248354,379718,512475 - ,653932,796494,917504,1048576,1179648,1310720,1441792,1572864,1703936,1835008 - ,-1572864,-1441792,-1297608,-1161159,-1032316,-917092,-779770,-647384,-515529,-384269 - ,-250003,-119252,1053,118111,249512,380545,512039,648101,770656,907003 - ,1021725,1178082,1310720,1441792,-4587520,-4456448,-4325376,-4194304,-4063232,-3932160 - ,-3801088,-3670016,-3538944,-3407872,-3276800,-3145728,-2999335,-2883584,-2752512,-2621440 - ,-2490368,-2359296,-2228224,-2112691,-1966080,-1848781,-1709830,-1566109,-1438427,-1303530 - ,-1176124,-1040936,-913876,-784585,-652025,-518361,-385267,-256342,-127297,-2733 - ,125422,257792,389363,519911,651106,783805,909407,1044143,1174156,1309267 - ,1436173,1553771,1708958,1814083,1967036,2095386,2255169,2359296,2478303,2621440 - ,2752512,-4456448,-4325376,-4194304,-4063232,-3932160,-3797524,-3670016,-3560250,-3413217 - ,-3257719,-3166416,-2986626,-2878000,-2781144,-2625383,-2495465,-2346792,-2230930,-2077063 - ,-1949225,-1819274,-1697261,-1568664,-1443074,-1304302,-1175289,-1043794,-913423,-785561 - ,-652104,-522835,-392667,-260517,-130088,-2,129509,260990,391931,522470 - ,655770,784902,917093,1046445,1176951,1303121,1441362,1565401,1702022,1822856 - ,1952852,2090384,2214607,2338436,2457483,2621440,-8781824,-8650752,-8519680,-8388608 - ,-8260828,-8126464,-8003337,-7859030,-7750057,-7602176,-7471104,-7340032,-7193045,-7090588 - ,-6946816,-6843344,-6676635,-6557575,-6447804,-6277614,-6159736,-6035729,-5884723,-5739567 - ,-5634818,-5489867,-5372864,-5243300,-5098939,-4988639,-4856258,-4728494,-4591717,-4447428 - ,-4322409,-4192918,-4062638,-3934141,-3797545,-3673373,-3531587,-3407391,-3277404,-3147797 - ,-3013578,-2886548,-2749811,-2616428,-2490949,-2361301,-2228482,-2096883,-1964343,-1831754 - ,-1702201,-1572495,-1442012,-1309242,-1182451,-1048996,-916905,-786510,-657079,-524730 - ,-393672,-261313,-128743,166,130678,261334,393287,524155,655570,786839 - ,917353,1052167,1179013,1309360,1442634,1571153,1703961,1832027,1965014,2097912 - ,2224861,2355341,2490455,2623051,2753484,2877015,3015783,3144157,3273705,3405255 - ,3542006,3669580,3802417,3935413,4065088,4190896,4333521,4456355,4579781,4713832 - ,4845707,4978625,5113278,5243817,5382318,5500592,5638135,5761179,5900822,6029270 - ,6186398,6297816,6436435,6559163,6666389,6806548,6950461,7086078,7195777,7350973 - ,7480132,7614852,7743514,7847288,8014762,8126464,8257536,8388608,8519680,8650752 - ,8781824,8912896,9043968,9175040,9306112,9437184 -}; - - - -const WebRtc_Word16 WebRtcIsacfix_kLevelsShapeQ10[1735] = { - 0, 0, -1, 0, 0, 1, 0, 1, 0, -821 - , 1, -763, -1, 656, -620, 0, 633, -636, 4, 615 - , -630, 1, 649, -1773, -670, 5, 678, 1810, -1876, -676 - , 0, 691, 1843, -1806, -743, -1, 749, 1795, 2920, -2872 - , -1761, -772, -3, 790, 1763, 2942, 0, 0, 0, 0 - , -792, 2, 0, 0, 1, 0, -854, 0, -702, -1 - , 662, -624, -5, 638, -611, -6, 638, -647, 0, 651 - , -685, -4, 679, 2123, -1814, -693, 0, 664, 1791, -1735 - , -737, 0, 771, 1854, 2873, -2867, -1842, -793, -1, 821 - , 1826, 2805, 3922, 0, 0, 0, -1, -779, 1, 786 - , 1, -708, 0, 789, -799, 1, 797, -663, 2, 646 - , -600, 3, 609, -600, 1, 658, 1807, -627, -3, 612 - , -625, 3, 632, -1732, -674, 1, 672, 2048, -1768, -715 - , 0, 724, 1784, -3881, -3072, -1774, -719, -1, 730, 1811 - , -2963, -1829, -806, -1, 816, 1795, 3050, -5389, -3784, -2942 - , -1893, -865, -12, 867, 1885, 2945, 3928, -2, 1, 4 - , 0, -694, 2, 665, -598, 5, 587, -599, -1, 661 - , -656, -7, 611, -607, 5, 603, -618, -4, 620, -1794 - , -645, -2, 654, -655, -1, 658, -1801, -700, 5, 707 - , 1927, -1752, -745, -8, 752, 1843, -2838, -1781, -801, 11 - , 796, 1811, 2942, 3866, -3849, -3026, -1848, -819, 2, 827 - , 1825, 2963, -3873, -2904, -1869, -910, -6, 903, 1902, 2885 - , 3978, 5286, -7168, -6081, -4989, -3968, -2963, -1970, -943, -2 - , 953, 1951, 2968, 3974, 5009, 6032, -2, 3, -1024, 2 - , 1024, -637, 1, 669, -613, -7, 630, -603, 4, 612 - , -612, 0, 590, -645, -11, 627, -657, -2, 671, 1849 - , -1853, -694, 2, 702, 1838, -3304, -1780, -736, -8, 732 - , 1772, -1709, -755, -6, 760, 1780, -2994, -1780, -800, 8 - , 819, 1830, 2816, -4096, -2822, -1881, -851, -4, 855, 1872 - , 2840, 3899, -3908, -2904, -1878, -887, 6, 897, 1872, 2942 - , 4008, -4992, -3881, -2933, -1915, -928, 1, 937, 1919, 2900 - , 4009, 4881, -6848, -6157, -5065, -3981, -2983, -1972, -978, -1 - , 968, 1979, 2988, 4008, 5007, 6108, 7003, 8051, 9027,-13272 - ,-12012,-11228,-10213, -9261, -8084, -7133, -6075, -5052, -4050, -3036 - , -2014, -996, -4, 1007, 2031, 3038, 4049, 5074, 6134, 7069 - , 8094, 9069, 10212, 11049, 12104, 51, -1024, -13, 1024, -609 - , -107, 613, -2048, -687, -95, 667, 2048, -3072, -1724, -785 - , -34, 732, 1819, -2048, -703, -26, 681, 2048, -2048, -686 - , -9, 665, 2048, -2048, -702, 37, 748, 1723, -4096, -2786 - , -1844, -837, 37, 811, 1742, 3072, -4096, -2783, -1848, -881 - , 39, 898, 1843, 2792, 3764, -5120, -4096, -2923, -1833, -852 - , -14, 862, 1824, 2834, 4096, -6144, -5120, -3914, -2842, -1870 - , -886, -27, 888, 1929, 2931, 4051, -7168, -6144, -5120, -3866 - , -2933, -1915, -927, 64, 933, 1902, 2929, 3912, 5063, 6144 - ,-11264,-10240, -9216, -8192, -7086, -6144, -5039, -3972, -2943, -1929 - , -941, 3, 938, 1942, 2959, 3933, 4905, 6088, 6983, 8192 - , -9216, -8192, -7202, -6088, -4983, -4019, -2955, -1975, -966, 17 - , 997, 1981, 2967, 3990, 4948, 6022, 6967, 8192,-13312,-12288 - ,-11264,-10240, -9216, -8049, -6997, -6040, -5026, -4043, -3029, -2034 - , -1015, -23, 984, 1997, 3010, 4038, 5002, 6015, 6946, 8061 - , 9216, 10240,-12381,-11264,-10240, -9060, -8058, -7153, -6085, -5075 - , -4051, -3042, -2037, -1017, -5, 1007, 2028, 3035, 4050, 5088 - , 6111, 7160, 8156, 9215, 10095, 11229, 12202, 13016,-26624,-25600 - ,-24582,-23671,-22674,-21400,-20355,-19508,-18315,-17269,-16361,-15299 - ,-14363,-13294,-12262,-11237,-10203, -9227, -8165, -7156, -6116, -5122 - , -4076, -3056, -2043, -1020, -8, 1027, 2047, 3065, 4110, 5130 - , 6125, 7168, 8195, 9206, 10230, 11227, 12256, 13304, 14281, 15316 - , 16374, 17382, 18428, 19388, 20361, 21468, 22448, 23781, 0, 0 - , -1, 0, -2, 1024, 0, 0, 0, -1, 1024, -1024 - , 1, -1024, 4, 1024, -1024, 2, 1024, -1024, 2, 1024 - , -2048, -1024, -4, 1024, -1024, 2, 1024, -2048, -1024, -3 - , 1024, 2048, -2048, -1024, 4, 1024, 2048, -3072, -2048, -1024 - , -1, 662, 2048, 0, 1, 0, 0, 1, -2, -2 - , 0, 2, 1024, -1, 1024, -1024, 4, 1024, -1024, 1 - , 1024, -1024, 1, 1024, -2048, -781, -4, 844, -807, -5 - , 866, -2048, -726, -13, 777, 2048, -2048, -643, -4, 617 - , 2048, 3072, -3072, -2048, -629, 1, 630, 2048, 3072, 0 - , -1, 1, -2, 2, 1, -1024, 5, -1024, 6, 1024 - , -1024, 4, 1024, -1024, 1, 1024, -1024, -9, 1024, -673 - , -7, 655, -2048, -665, -15, 716, -2048, -647, 4, 640 - , 2048, -2048, -615, -1, 635, 2048, -2048, -613, 10, 637 - , 2048, 3072, -3072, -2048, -647, -3, 641, 2048, 3072, -5120 - , -4096, -3072, -2048, -681, 6, 685, 2048, 3072, 4096, 1 - , 1, 0, -1, 1024, -1024, -3, 1024, -1024, 6, 1024 - , -1024, -1, 769, -733, 0, 1024, -876, -2, 653, -1024 - , -4, 786, -596, -13, 595, -634, -2, 638, 2048, -2048 - , -620, -5, 620, 2048, -4096, -3072, -2048, -639, 11, 655 - , 2048, 3072, -3072, -2048, -659, 5, 663, 2048, -3072, -1823 - , -687, 22, 695, 2048, 3072, 4096, -4096, -3072, -1848, -715 - , -3, 727, 1816, 3072, 4096, 5120, -8192, -7168, -6144, -5120 - , -4096, -2884, -1771, -756, -14, 775, 1844, 3072, 4096, 5120 - , 6144, -1, 1, 0, -1024, 2, 815, -768, 2, 708 - , -1024, -3, 693, -661, -7, 607, -643, -5, 609, -624 - , 3, 631, -682, -3, 691, 2048, -2048, -640, 5, 650 - , 2048, -3072, -2048, -701, 9, 704, 2048, 3072, -3072, -2048 - , -670, 10, 674, 2048, 3072, -5120, -4096, -3072, -1749, -738 - , 0, 733, 1811, 3072, 4096, 5120, -4096, -3072, -1873, -753 - , 0, 756, 1874, 3072, 4096, -5120, -4096, -2900, -1838, -793 - , -6, 793, 1868, 2837, 4096, 5120, -7168, -6144, -5120, -4096 - , -2832, -1891, -828, 1, 828, 1901, 2823, 3912, 5120, 6144 - , 7168, 8192,-13312,-12288,-11264,-10240, -9216, -8192, -7168, -6144 - , -5120, -3976, -3004, -1911, -869, 7, 869, 1932, 3024, 3992 - , 5009, 6144, 7168, 8192, 9216, 10240, 11264, -4, 1024, -629 - , -22, 609, -623, 9, 640, -2048, -768, 1, 682, -2048 - , -741, 49, 722, 2048, -3072, -1706, -808, -20, 768, 1750 - , -1684, -727, -29, 788, 1840, 3033, -1758, -784, 0, 801 - , 1702, -3072, -1813, -814, 38, 820, 1884, 2927, -4096, -3241 - , -1839, -922, 25, 882, 1886, 2812, -4096, -2982, -1923, -894 - , 84, 912, 1869, 2778, 4096, -4928, -3965, -2902, -1920, -883 - , 3, 917, 1953, 2921, 3957, 4922, 6144, 7168, -5120, -3916 - , -2897, -1949, -930, 31, 959, 1934, 2901, 3851, 5120, -9216 - , -8192, -7046, -6029, -5030, -4034, -2980, -1969, -1013, -76, 963 - , 1963, 2901, 3929, 4893, 6270, 7168, 8192, 9216,-12288,-11264 - ,-10240, -9216, -8192, -6846, -6123, -5108, -4008, -3000, -1963, -954 - , -6, 958, 1992, 3009, 4020, 5085, 6097, 7168, 8192, 9216 - ,-11264,-10139, -9194, -8127, -7156, -6102, -5053, -4049, -3036, -2025 - , -1009, -34, 974, 1984, 3034, 4028, 5138, 6000, 7057, 8166 - , 9070, 10033, 11360, 12288,-13312,-12288,-10932,-10190, -9120, -8123 - , -7128, -6103, -5074, -4081, -3053, -2029, -989, -4, 1010, 2028 - , 3051, 4073, 5071, 6099, 7132, 8147, 9295, 10159, 11023, 12263 - , 13312, 14336,-25600,-24576,-23552,-22529,-21504,-20480,-19456,-18637 - ,-17425,-16165,-15316,-14327,-13606,-12135,-11182,-10107, -9153, -8144 - , -7146, -6160, -5129, -4095, -3064, -2038, -1025, 1, 1031, 2072 - , 3074, 4088, 5123, 6149, 7157, 8173, 9198, 10244, 11250, 12268 - , 13263, 14289, 15351, 16370, 17402, 18413, 19474, 20337, 21386, 22521 - , 23367, 24350, 0, 0, 0, 0, 0, 0, 0, 0 - , -1024, 0, 1024, -1024, 0, 1024, -1024, 0, 1024, -1024 - , 0, 1024, -1024, 0, 1024, -773, 0, 1024, -674, 0 - , 645, -2048, -745, 0, 628, 2048, -2048, -712, 0, 681 - , 2048, 3072, -3072, -2048, -673, 0, 682, 1964, 3257, 0 - , 0, 0, 0, 0, 0, 0, 0, -1024, 0, 1024 - , -1024, 0, 1024, -1024, 0, 1024, -705, 0, 623, -771 - , 0, 1024, -786, 0, 688, -631, 0, 652, 2048, -2048 - , -627, -1, 666, 2048, -3072, -1756, -694, 0, 674, 2048 - , -3098, -1879, -720, 5, 694, 1886, 2958, 4096, 0, 0 - , 0, 0, 1024, 0, 0, 1024, -769, 0, 1024, -1024 - , 0, 1024, -1024, 0, 1024, -817, 0, 734, -786, 0 - , 651, -638, 0, 637, -623, 0, 671, -652, 0, 619 - , 2048, -2048, -670, -1, 663, 2048, -1908, -680, 1, 686 - , 2048, 3072, 4096, -4096, -3072, -1833, -711, 0, 727, 1747 - , 3072, 4096, -4096, -2971, -1826, -762, 2, 766, 1832, 2852 - , 3928, 5079, 0, 0, 0, -1024, 0, 1024, -1024, 0 - , -656, 0, 1024, -599, 0, 620, -1024, 0, 1024, -603 - , 0, 622, -643, 0, 660, -599, 0, 611, -641, -1 - , 651, 2048, -2048, -648, -2, 647, 1798, -3072, -2048, -672 - , 2, 670, 2048, -3072, -1780, -694, -1, 706, 1751, 3072 - , -3072, -1862, -757, 7, 739, 1798, 3072, 4096, -5120, -4096 - , -3253, -1811, -787, 3, 782, 1887, 3123, 4096, -7252, -6144 - , -5354, -4060, -2864, -1863, -820, -11, 847, 1903, 2970, 3851 - , 4921, 5957, 7168, 8192, 9306, 0, 0, -1024, 0, 1024 - , -726, 0, 706, -692, 0, 593, -598, 0, 616, -624 - , 0, 616, -605, 0, 613, -2048, -652, 1, 635, 2048 - , -2048, -647, -1, 660, 2048, -1811, -668, -2, 685, 2048 - , -1796, -731, -2, 730, 1702, 3072, -3072, -1766, -747, -4 - , 756, 1770, 3072, -4096, -3024, -1762, -783, 4, 771, 1781 - , 3072, -5120, -4057, -2807, -1832, -822, 0, 816, 1804, 2851 - , 3949, 5120, -6144, -4899, -3927, -2920, -1893, -874, -2, 868 - , 1881, 2905, 3960, 4912, 6144, -9216, -8192, -7168, -6225, -4963 - , -3943, -2956, -1890, -902, 0, 897, 1914, 2916, 3984, 4990 - , 6050, 7168,-11264,-10217, -9114, -8132, -7035, -5988, -4984, -4000 - , -2980, -1962, -927, 7, 931, 1956, 2981, 4031, 4972, 6213 - , 7227, 8192, 9216, 10240, 11170, 12288, 13312, 14336, 0, 1024 - , -557, 1, 571, -606, -4, 612, -1676, -707, 10, 673 - , 2048, -2048, -727, 5, 686, -3072, -1772, -755, 12, 716 - , 1877, -1856, -786, 2, 786, 1712, -1685, -818, -16, 863 - , 1729, -3072, -1762, -857, 3, 866, 1838, 2841, -3862, -2816 - , -1864, -925, -2, 923, 1897, 2779, -2782, -1838, -920, -28 - , 931, 1951, 2835, 3804, -4815, -4001, -2940, -1934, -959, -22 - , 975, 1957, 2904, 3971, 4835, -5148, -3892, -2944, -1953, -986 - , -11, 989, 1968, 2939, 3949, 4947, 5902, -9216, -8192, -6915 - , -6004, -4965, -4013, -3009, -1977, -987, -1, 982, 1972, 3000 - , 3960, 4939, 5814, -8976, -7888, -7084, -5955, -5043, -4009, -2991 - , -2002, -1000, -8, 993, 2011, 3023, 4026, 5028, 6023, 7052 - , 8014, 9216,-11240,-10036, -9125, -8118, -7105, -6062, -5048, -4047 - , -3044, -2025, -1009, -1, 1011, 2023, 3042, 4074, 5085, 6108 - , 7119, 8142, 9152, 10114, 11141, 12250, 13307,-15360,-14099,-13284 - ,-12291,-11223,-10221, -9152, -8147, -7128, -6104, -5077, -4072, -3062 - , -2033, -1020, 7, 1018, 2038, 3059, 4081, 5084, 6109, 7102 - , 8128, 9134, 10125, 11239, 12080,-23552,-22528,-21504,-20480,-19456 - ,-18159,-17240,-16291,-15364,-14285,-13305,-12271,-11233,-10217, -9198 - , -8175, -7157, -6134, -5122, -4089, -3071, -2047, -1018, 3, 1026 - , 2041, 3077, 4090, 5108, 6131, 7150, 8172, 9175, 10196, 11272 - , 12303, 13273, 14328, 15332, 16334, 17381, 18409, 19423, 20423, 21451 - , 22679, 23391, 24568, 25600, 26589 -}; - -/* cdf tables for quantizer indices */ -const WebRtc_UWord16 WebRtcIsacfix_kCdfGain[1212] = { - 0, 13, 301, 3730, 61784, 65167, 65489, 65535, 0, 17, - 142, 314, 929, 2466, 7678, 56450, 63463, 64740, 65204, 65426, - 65527, 65535, 0, 8, 100, 724, 6301, 60105, 65125, 65510, - 65531, 65535, 0, 13, 117, 368, 1068, 3010, 11928, 53603, - 61177, 63404, 64505, 65108, 65422, 65502, 65531, 65535, 0, 4, - 17, 96, 410, 1859, 12125, 54361, 64103, 65305, 65497, 65535, - 0, 4, 88, 230, 469, 950, 1746, 3228, 6092, 16592, - 44756, 56848, 61256, 63308, 64325, 64920, 65309, 65460, 65502, 65522, - 65535, 0, 88, 352, 1675, 6339, 20749, 46686, 59284, 63525, - 64949, 65359, 65502, 65527, 65535, 0, 13, 38, 63, 117, - 234, 381, 641, 929, 1407, 2043, 2809, 4032, 5753, 8792, - 14407, 24308, 38941, 48947, 55403, 59293, 61411, 62688, 63630, 64329, - 64840, 65188, 65376, 65472, 65506, 65527, 65531, 65535, 0, 8, - 29, 75, 222, 615, 1327, 2801, 5623, 9931, 16094, 24966, - 34419, 43458, 50676, 56186, 60055, 62500, 63936, 64765, 65225, 65435, - 65514, 65535, 0, 8, 13, 15, 17, 21, 33, 59, - 71, 92, 151, 243, 360, 456, 674, 934, 1223, 1583, - 1989, 2504, 3031, 3617, 4354, 5154, 6163, 7411, 8780, 10747, - 12874, 15591, 18974, 23027, 27436, 32020, 36948, 41830, 46205, 49797, - 53042, 56094, 58418, 60360, 61763, 62818, 63559, 64103, 64509, 64798, - 65045, 65162, 65288, 65363, 65447, 65506, 65522, 65531, 65533, 65535, - 0, 4, 6, 25, 38, 71, 138, 264, 519, 808, - 1227, 1825, 2516, 3408, 4279, 5560, 7092, 9197, 11420, 14108, - 16947, 20300, 23926, 27459, 31164, 34827, 38575, 42178, 45540, 48747, - 51444, 54090, 56426, 58460, 60080, 61595, 62734, 63668, 64275, 64673, - 64936, 65112, 65217, 65334, 65426, 65464, 65477, 65489, 65518, 65527, - 65529, 65531, 65533, 65535, 0, 2, 4, 8, 10, 12, - 14, 16, 21, 33, 50, 71, 84, 92, 105, 138, - 180, 255, 318, 377, 435, 473, 511, 590, 682, 758, - 913, 1097, 1256, 1449, 1671, 1884, 2169, 2445, 2772, 3157, - 3563, 3944, 4375, 4848, 5334, 5820, 6448, 7101, 7716, 8378, - 9102, 9956, 10752, 11648, 12707, 13670, 14758, 15910, 17187, 18472, - 19627, 20649, 21951, 23169, 24283, 25552, 26862, 28227, 29391, 30764, - 31882, 33213, 34432, 35600, 36910, 38116, 39464, 40729, 41872, 43144, - 44371, 45514, 46762, 47813, 48968, 50069, 51032, 51974, 52908, 53737, - 54603, 55445, 56282, 56990, 57572, 58191, 58840, 59410, 59887, 60264, - 60607, 60946, 61269, 61516, 61771, 61960, 62198, 62408, 62558, 62776, - 62985, 63207, 63408, 63546, 63739, 63906, 64070, 64237, 64371, 64551, - 64677, 64836, 64999, 65095, 65213, 65284, 65338, 65380, 65426, 65447, - 65472, 65485, 65487, 65489, 65502, 65510, 65512, 65514, 65516, 65518, - 65522, 65531, 65533, 65535, 0, 2, 4, 6, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 10, 222, 65321, - 65513, 65528, 65531, 65533, 65535, 0, 2, 4, 50, 65476, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, 12, - 38, 544, 64936, 65509, 65523, 65525, 65529, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 10, 1055, 64508, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 10, 12, 123, - 3956, 62999, 65372, 65495, 65515, 65521, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 12, 53, 4707, 59445, - 65467, 65525, 65527, 65529, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 38, 40, 50, 67, - 96, 234, 929, 14345, 55750, 64866, 65389, 65462, 65514, 65517, - 65519, 65521, 65523, 65525, 65527, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 15, 35, 91, 377, 1946, - 13618, 52565, 63714, 65184, 65465, 65520, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, 12, - 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, - 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, - 54, 82, 149, 362, 751, 1701, 4239, 12893, 38627, 55072, - 60875, 63071, 64158, 64702, 65096, 65283, 65412, 65473, 65494, 65505, - 65508, 65517, 65519, 65521, 65523, 65525, 65527, 65529, 65531, 65533, - 65535, 0, 2, 15, 23, 53, 143, 260, 418, 698, - 988, 1353, 1812, 2411, 3144, 4015, 5143, 6401, 7611, 8999, - 10653, 12512, 14636, 16865, 19404, 22154, 24798, 27521, 30326, 33102, - 35790, 38603, 41415, 43968, 46771, 49435, 52152, 54715, 57143, 59481, - 61178, 62507, 63603, 64489, 64997, 65257, 65427, 65473, 65503, 65520, - 65529, 65531, 65533, 65535, 0, 3, 6, 9, 26, 32, - 44, 46, 64, 94, 111, 164, 205, 254, 327, 409, - 506, 608, 733, 885, 1093, 1292, 1482, 1742, 1993, 2329, - 2615, 3029, 3374, 3798, 4257, 4870, 5405, 5992, 6618, 7225, - 7816, 8418, 9051, 9761, 10532, 11380, 12113, 13010, 13788, 14594, - 15455, 16361, 17182, 18088, 18997, 20046, 20951, 21968, 22947, 24124, - 25296, 26547, 27712, 28775, 29807, 30835, 31709, 32469, 33201, 34014, - 34876, 35773, 36696, 37620, 38558, 39547, 40406, 41277, 42367, 43290, - 44445, 45443, 46510, 47684, 48973, 50157, 51187, 52242, 53209, 54083, - 55006, 55871, 56618, 57293, 57965, 58556, 59222, 59722, 60180, 60554, - 60902, 61250, 61554, 61837, 62100, 62372, 62631, 62856, 63078, 63324, - 63557, 63768, 63961, 64089, 64235, 64352, 64501, 64633, 64770, 64887, - 65001, 65059, 65121, 65188, 65246, 65302, 65346, 65390, 65428, 65463, - 65477, 65506, 65515, 65517, 65519, 65521, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 109, 65332, 65531, 65533, - 65535, 0, 2, 4, 6, 8, 25, 1817, 63874, 65511, - 65527, 65529, 65531, 65533, 65535, 0, 2, 4, 907, 65014, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, - 12, 132, 2743, 62708, 65430, 65525, 65527, 65529, 65531, 65533, - 65535, 0, 2, 4, 6, 8, 35, 3743, 61666, 65485, - 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, 23, - 109, 683, 6905, 58417, 64911, 65398, 65497, 65518, 65525, 65527, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 53, 510, - 10209, 55212, 64573, 65441, 65522, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 22, 32, 90, 266, 1037, 3349, 14468, 50488, 62394, 64685, - 65341, 65480, 65514, 65519, 65521, 65523, 65525, 65527, 65529, 65531, - 65533, 65535, 0, 2, 4, 6, 9, 16, 37, 106, - 296, 748, 1868, 5733, 18897, 45553, 60165, 63949, 64926, 65314, - 65441, 65508, 65524, 65529, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, - 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, - 46, 48, 50, 83, 175, 344, 667, 1293, 2337, 4357, - 8033, 14988, 28600, 43244, 52011, 57042, 59980, 61779, 63065, 63869, - 64390, 64753, 64988, 65164, 65326, 65422, 65462, 65492, 65506, 65522, - 65524, 65526, 65531, 65533, 65535, 0, 2, 4, 6, 8, - 10, 12, 14, 16, 25, 39, 48, 55, 62, 65, - 85, 106, 139, 169, 194, 252, 323, 485, 688, 1074, - 1600, 2544, 3863, 5733, 8303, 11397, 15529, 20273, 25734, 31455, - 36853, 41891, 46410, 50306, 53702, 56503, 58673, 60479, 61880, 62989, - 63748, 64404, 64852, 65124, 65309, 65424, 65480, 65524, 65528, 65533, - 65535, 0, 2, 4, 6, 8, 10, 12, 14, 21, - 23, 25, 27, 29, 31, 39, 41, 43, 48, 60, - 72, 79, 106, 136, 166, 187, 224, 252, 323, 381, - 427, 478, 568, 660, 783, 912, 1046, 1175, 1365, 1567, - 1768, 2024, 2347, 2659, 3049, 3529, 4033, 4623, 5281, 5925, - 6726, 7526, 8417, 9468, 10783, 12141, 13571, 15222, 16916, 18659, - 20350, 22020, 23725, 25497, 27201, 29026, 30867, 32632, 34323, 36062, - 37829, 39466, 41144, 42654, 43981, 45343, 46579, 47759, 49013, 50171, - 51249, 52283, 53245, 54148, 54938, 55669, 56421, 57109, 57791, 58464, - 59092, 59674, 60105, 60653, 61083, 61407, 61757, 62095, 62388, 62649, - 62873, 63157, 63358, 63540, 63725, 63884, 64046, 64155, 64278, 64426, - 64548, 64654, 64806, 64906, 64994, 65077, 65137, 65215, 65277, 65324, - 65354, 65409, 65437, 65455, 65462, 65490, 65495, 65499, 65508, 65511, - 65513, 65515, 65517, 65519, 65521, 65523, 65525, 65527, 65529, 65531, - 65533, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kCdfShape[2059] = { - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 4, - 65535, 0, 8, 65514, 65535, 0, 29, 65481, 65535, 0, - 121, 65439, 65535, 0, 239, 65284, 65535, 0, 8, 779, - 64999, 65527, 65535, 0, 8, 888, 64693, 65522, 65535, 0, - 29, 2604, 62843, 65497, 65531, 65535, 0, 25, 176, 4576, - 61164, 65275, 65527, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 4, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 4, 65535, 0, 33, 65502, 65535, - 0, 54, 65481, 65535, 0, 251, 65309, 65535, 0, 611, - 65074, 65535, 0, 1273, 64292, 65527, 65535, 0, 4, 1809, - 63940, 65518, 65535, 0, 88, 4392, 60603, 65426, 65531, 65535, - 0, 25, 419, 7046, 57756, 64961, 65514, 65531, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 4, 65531, - 65535, 0, 65535, 0, 8, 65531, 65535, 0, 4, 65527, - 65535, 0, 17, 65510, 65535, 0, 42, 65481, 65535, 0, - 197, 65342, 65531, 65535, 0, 385, 65154, 65535, 0, 1005, - 64522, 65535, 0, 8, 1985, 63469, 65533, 65535, 0, 38, - 3119, 61884, 65514, 65535, 0, 4, 6, 67, 4961, 60804, - 65472, 65535, 0, 17, 565, 9182, 56538, 65087, 65514, 65535, - 0, 8, 63, 327, 2118, 14490, 52774, 63839, 65376, 65522, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 17, 65522, 65535, 0, 59, 65489, 65535, 0, 50, 65522, - 65535, 0, 54, 65489, 65535, 0, 310, 65179, 65535, 0, - 615, 64836, 65535, 0, 4, 1503, 63965, 65535, 0, 2780, - 63383, 65535, 0, 21, 3919, 61051, 65527, 65535, 0, 84, - 6674, 59929, 65435, 65535, 0, 4, 255, 7976, 55784, 65150, - 65518, 65531, 65535, 0, 4, 8, 582, 10726, 53465, 64949, - 65518, 65535, 0, 29, 339, 3006, 17555, 49517, 62956, 65200, - 65497, 65531, 65535, 0, 2, 33, 138, 565, 2324, 7670, - 22089, 45966, 58949, 63479, 64966, 65380, 65518, 65535, 0, 65535, - 0, 65535, 0, 2, 65533, 65535, 0, 46, 65514, 65535, - 0, 414, 65091, 65535, 0, 540, 64911, 65535, 0, 419, - 65162, 65535, 0, 976, 64790, 65535, 0, 2977, 62495, 65531, - 65535, 0, 4, 3852, 61034, 65527, 65535, 0, 4, 29, - 6021, 60243, 65468, 65535, 0, 84, 6711, 58066, 65418, 65535, - 0, 13, 281, 9550, 54917, 65125, 65506, 65535, 0, 2, - 63, 984, 12108, 52644, 64342, 65435, 65527, 65535, 0, 29, - 251, 2014, 14871, 47553, 62881, 65229, 65518, 65535, 0, 13, - 142, 749, 4220, 18497, 45200, 60913, 64823, 65426, 65527, 65535, - 0, 13, 71, 264, 1176, 3789, 10500, 24480, 43488, 56324, - 62315, 64493, 65242, 65464, 65514, 65522, 65531, 65535, 0, 4, - 13, 38, 109, 205, 448, 850, 1708, 3429, 6276, 11371, - 19221, 29734, 40955, 49391, 55411, 59460, 62102, 63793, 64656, 65150, - 65401, 65485, 65522, 65531, 65535, 0, 65535, 0, 2, 65533, - 65535, 0, 1160, 65476, 65535, 0, 2, 6640, 64763, 65533, - 65535, 0, 2, 38, 9923, 61009, 65527, 65535, 0, 2, - 4949, 63092, 65533, 65535, 0, 2, 3090, 63398, 65533, 65535, - 0, 2, 2520, 58744, 65510, 65535, 0, 2, 13, 544, - 8784, 51403, 65148, 65533, 65535, 0, 2, 25, 1017, 10412, - 43550, 63651, 65489, 65527, 65535, 0, 2, 4, 29, 783, - 13377, 52462, 64524, 65495, 65533, 65535, 0, 2, 4, 6, - 100, 1817, 18451, 52590, 63559, 65376, 65531, 65535, 0, 2, - 4, 6, 46, 385, 2562, 11225, 37416, 60488, 65026, 65487, - 65529, 65533, 65535, 0, 2, 4, 6, 8, 10, 12, - 42, 222, 971, 5221, 19811, 45048, 60312, 64486, 65294, 65474, - 65525, 65529, 65533, 65535, 0, 2, 4, 8, 71, 167, - 666, 2533, 7875, 19622, 38082, 54359, 62108, 64633, 65290, 65495, - 65529, 65533, 65535, 0, 2, 4, 6, 8, 10, 13, - 109, 586, 1930, 4949, 11600, 22641, 36125, 48312, 56899, 61495, - 63927, 64932, 65389, 65489, 65518, 65531, 65533, 65535, 0, 4, - 6, 8, 67, 209, 712, 1838, 4195, 8432, 14432, 22834, - 31723, 40523, 48139, 53929, 57865, 60657, 62403, 63584, 64363, 64907, - 65167, 65372, 65472, 65514, 65535, 0, 2, 4, 13, 25, - 42, 46, 50, 75, 113, 147, 281, 448, 657, 909, - 1185, 1591, 1976, 2600, 3676, 5317, 7398, 9914, 12941, 16169, - 19477, 22885, 26464, 29851, 33360, 37228, 41139, 44802, 48654, 52058, - 55181, 57676, 59581, 61022, 62190, 63107, 63676, 64199, 64547, 64924, - 65158, 65313, 65430, 65481, 65518, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65533, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65533, 65535, 0, 2, 65535, 0, - 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, - 65535, 0, 2, 4, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 4, 65531, 65533, 65535, 0, 2, 4, 65531, - 65533, 65535, 0, 2, 4, 6, 65524, 65533, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65533, 65535, 0, 65533, - 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, - 2, 65533, 65535, 0, 2, 4, 65532, 65535, 0, 6, - 65523, 65535, 0, 2, 15, 65530, 65533, 65535, 0, 2, - 35, 65493, 65531, 65533, 65535, 0, 2, 4, 158, 65382, - 65531, 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 2, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 65533, 65535, 0, 9, 65512, 65535, 0, 2, - 12, 65529, 65535, 0, 2, 73, 65434, 65533, 65535, 0, - 2, 240, 65343, 65533, 65535, 0, 2, 476, 65017, 65531, - 65533, 65535, 0, 2, 4, 1046, 64686, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 1870, 63898, 65529, 65531, 65533, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65533, 65535, - 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, - 65532, 65535, 0, 6, 65533, 65535, 0, 6, 65523, 65535, - 0, 2, 65532, 65535, 0, 137, 65439, 65535, 0, 576, - 64899, 65533, 65535, 0, 2, 289, 65299, 65533, 65535, 0, - 2, 4, 6, 880, 64134, 65531, 65533, 65535, 0, 2, - 4, 1853, 63347, 65533, 65535, 0, 2, 6, 2516, 61762, - 65529, 65531, 65533, 65535, 0, 2, 4, 9, 3980, 61380, - 65503, 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, - 10, 12, 61, 6393, 59859, 65466, 65527, 65529, 65531, 65533, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 2, 65532, - 65535, 0, 3, 65529, 65535, 0, 2, 65529, 65535, 0, - 61, 65453, 65535, 0, 234, 65313, 65535, 0, 503, 65138, - 65535, 0, 155, 65402, 65533, 65535, 0, 2, 1058, 64554, - 65533, 65535, 0, 2, 4, 3138, 62109, 65531, 65533, 65535, - 0, 2, 4, 2031, 63339, 65531, 65533, 65535, 0, 2, - 4, 6, 9, 4155, 60778, 65523, 65529, 65531, 65533, 65535, - 0, 2, 4, 41, 6189, 59269, 65490, 65531, 65533, 65535, - 0, 2, 4, 6, 210, 8789, 57043, 65400, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 26, 453, 10086, - 55499, 64948, 65483, 65524, 65527, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 114, 1014, 11202, 52670, 64226, 65356, 65503, 65514, 65523, 65525, - 65527, 65529, 65531, 65533, 65535, 0, 65533, 65535, 0, 15, - 65301, 65535, 0, 152, 64807, 65535, 0, 2, 3328, 63308, - 65535, 0, 2, 4050, 59730, 65533, 65535, 0, 2, 164, - 10564, 61894, 65529, 65535, 0, 15, 6712, 59831, 65076, 65532, - 65535, 0, 32, 7712, 57449, 65459, 65535, 0, 2, 210, - 7849, 53110, 65021, 65523, 65535, 0, 2, 12, 1081, 13883, - 48262, 62870, 65477, 65535, 0, 2, 88, 847, 6145, 37852, - 62012, 65454, 65533, 65535, 0, 9, 47, 207, 1823, 14522, - 45521, 61069, 64891, 65481, 65528, 65531, 65533, 65535, 0, 2, - 9, 488, 2881, 12758, 38703, 58412, 64420, 65410, 65533, 65535, - 0, 2, 4, 6, 61, 333, 1891, 6486, 19720, 43188, - 57547, 62472, 64796, 65421, 65497, 65523, 65529, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 10, 12, 29, 117, 447, - 1528, 6138, 21242, 43133, 56495, 62432, 64746, 65362, 65500, 65529, - 65531, 65533, 65535, 0, 2, 18, 105, 301, 760, 1490, - 3472, 7568, 15002, 26424, 40330, 53029, 60048, 62964, 64274, 64890, - 65337, 65445, 65489, 65513, 65527, 65530, 65533, 65535, 0, 2, - 4, 6, 41, 102, 409, 853, 2031, 4316, 7302, 11328, - 16869, 24825, 34926, 43481, 50877, 56126, 59874, 62103, 63281, 63857, - 64166, 64675, 65382, 65522, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 18, 29, 38, 53, - 58, 96, 181, 503, 1183, 2849, 5590, 8600, 11379, 13942, - 16478, 19453, 22638, 26039, 29411, 32921, 37596, 41433, 44998, 48560, - 51979, 55106, 57666, 59892, 61485, 62616, 63484, 64018, 64375, 64685, - 64924, 65076, 65278, 65395, 65471, 65509, 65529, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 2, 65533, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, 7, - 65519, 65535, 0, 2, 14, 65491, 65533, 65535, 0, 2, - 81, 65427, 65531, 65533, 65535, 0, 2, 4, 312, 65293, - 65528, 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, - 65535, 0, 5, 65523, 65535, 0, 2, 65533, 65535, 0, - 7, 65526, 65535, 0, 46, 65464, 65533, 65535, 0, 2, - 120, 65309, 65533, 65535, 0, 2, 5, 362, 65097, 65533, - 65535, 0, 2, 18, 1164, 64785, 65528, 65531, 65533, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65533, 65535, 0, - 65535, 0, 65533, 65535, 0, 2, 65533, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65530, 65535, - 0, 2, 65523, 65535, 0, 69, 65477, 65535, 0, 141, - 65459, 65535, 0, 194, 65325, 65533, 65535, 0, 2, 543, - 64912, 65533, 65535, 0, 5, 1270, 64301, 65529, 65531, 65533, - 65535, 0, 2, 4, 12, 2055, 63538, 65508, 65531, 65533, - 65535, 0, 2, 7, 102, 3775, 61970, 65429, 65526, 65528, - 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 2, - 65533, 65535, 0, 2, 65535, 0, 9, 65533, 65535, 0, - 25, 65512, 65535, 0, 2, 65533, 65535, 0, 44, 65480, - 65535, 0, 48, 65475, 65535, 0, 162, 65373, 65535, 0, - 637, 64806, 65533, 65535, 0, 2, 935, 64445, 65533, 65535, - 0, 2, 4, 1662, 64083, 65533, 65535, 0, 2, 12, - 3036, 62469, 65521, 65533, 65535, 0, 2, 120, 5405, 60468, - 65469, 65531, 65533, 65535, 0, 2, 4, 18, 254, 6663, - 58999, 65272, 65528, 65533, 65535, 0, 2, 4, 9, 12, - 67, 591, 8981, 56781, 64564, 65365, 65508, 65524, 65526, 65529, - 65531, 65533, 65535, 0, 65535, 0, 65535, 0, 2, 65533, - 65535, 0, 9, 65526, 65535, 0, 14, 65503, 65535, 0, - 127, 65390, 65535, 0, 517, 64990, 65535, 0, 178, 65330, - 65535, 0, 2, 1055, 64533, 65533, 65535, 0, 2, 1558, - 63942, 65533, 65535, 0, 2, 2205, 63173, 65533, 65535, 0, - 25, 4493, 60862, 65505, 65533, 65535, 0, 2, 48, 5890, - 59442, 65482, 65533, 65535, 0, 2, 4, 127, 7532, 58191, - 65394, 65533, 65535, 0, 2, 5, 32, 550, 10388, 54924, - 65046, 65510, 65531, 65533, 65535, 0, 2, 4, 30, 150, - 1685, 14340, 51375, 63619, 65288, 65503, 65528, 65533, 65535, 0, - 2, 4, 6, 8, 28, 97, 473, 2692, 15407, 50020, - 62880, 65064, 65445, 65508, 65531, 65533, 65535, 0, 2, 4, - 12, 32, 79, 150, 372, 907, 2184, 5868, 18207, 45431, - 59856, 64031, 65096, 65401, 65481, 65507, 65521, 65523, 65525, 65527, - 65529, 65531, 65533, 65535, 0, 65533, 65535, 0, 182, 65491, - 65535, 0, 877, 64286, 65535, 0, 9, 2708, 63612, 65533, - 65535, 0, 2, 6038, 59532, 65535, 0, 2, 92, 5500, - 60539, 65533, 65535, 0, 268, 8908, 56512, 65385, 65535, 0, - 129, 13110, 52742, 65036, 65535, 0, 2, 806, 14003, 51929, - 64732, 65523, 65535, 0, 7, 92, 2667, 18159, 47678, 62610, - 65355, 65535, 0, 32, 1836, 19676, 48237, 61677, 64960, 65526, - 65535, 0, 21, 159, 967, 5668, 22782, 44709, 58317, 64020, - 65406, 65528, 65535, 0, 7, 162, 1838, 8328, 23929, 43014, - 56394, 63374, 65216, 65484, 65521, 65535, 0, 2, 4, 6, - 28, 268, 1120, 3613, 10688, 24185, 40989, 54917, 61684, 64510, - 65403, 65530, 65535, 0, 2, 16, 44, 139, 492, 1739, - 5313, 13558, 26766, 41566, 52446, 58937, 62815, 64480, 65201, 65454, - 65524, 65533, 65535, 0, 7, 25, 76, 263, 612, 1466, - 3325, 6832, 12366, 20152, 29466, 39255, 47360, 53506, 57740, 60726, - 62845, 64131, 64882, 65260, 65459, 65521, 65528, 65530, 65535, 0, - 2, 4, 14, 48, 136, 312, 653, 1240, 2369, 4327, - 7028, 10759, 15449, 21235, 28027, 35386, 42938, 49562, 54990, 59119, - 62086, 63916, 64863, 65249, 65445, 65493, 65523, 65535, 0, 2, - 4, 6, 8, 10, 12, 21, 83, 208, 409, 723, - 1152, 1868, 2951, 4463, 6460, 8979, 11831, 15195, 18863, 22657, - 26762, 30881, 34963, 39098, 43054, 47069, 50620, 53871, 56821, 59386, - 61340, 62670, 63512, 64023, 64429, 64750, 64944, 65126, 65279, 65366, - 65413, 65445, 65473, 65505, 65510, 65521, 65528, 65530, 65535 -}; - -/* pointers to cdf tables for quantizer indices */ -const WebRtc_UWord16 *WebRtcIsacfix_kCdfGainPtr[3][12] = { - { WebRtcIsacfix_kCdfGain +0 +0, WebRtcIsacfix_kCdfGain +0 +8, WebRtcIsacfix_kCdfGain +0 +22, - WebRtcIsacfix_kCdfGain +0 +32, WebRtcIsacfix_kCdfGain +0 +48, WebRtcIsacfix_kCdfGain +0 +60, - WebRtcIsacfix_kCdfGain +0 +81, WebRtcIsacfix_kCdfGain +0 +95, WebRtcIsacfix_kCdfGain +0 +128, - WebRtcIsacfix_kCdfGain +0 +152, WebRtcIsacfix_kCdfGain +0 +210, WebRtcIsacfix_kCdfGain +0 +264 - }, - { WebRtcIsacfix_kCdfGain +404 +0, WebRtcIsacfix_kCdfGain +404 +8, WebRtcIsacfix_kCdfGain +404 +21, - WebRtcIsacfix_kCdfGain +404 +30, WebRtcIsacfix_kCdfGain +404 +46, WebRtcIsacfix_kCdfGain +404 +58, - WebRtcIsacfix_kCdfGain +404 +79, WebRtcIsacfix_kCdfGain +404 +93, WebRtcIsacfix_kCdfGain +404 +125, - WebRtcIsacfix_kCdfGain +404 +149, WebRtcIsacfix_kCdfGain +404 +207, WebRtcIsacfix_kCdfGain +404 +260 - }, - { WebRtcIsacfix_kCdfGain +803 +0, WebRtcIsacfix_kCdfGain +803 +8, WebRtcIsacfix_kCdfGain +803 +22, - WebRtcIsacfix_kCdfGain +803 +31, WebRtcIsacfix_kCdfGain +803 +48, WebRtcIsacfix_kCdfGain +803 +60, - WebRtcIsacfix_kCdfGain +803 +81, WebRtcIsacfix_kCdfGain +803 +96, WebRtcIsacfix_kCdfGain +803 +129, - WebRtcIsacfix_kCdfGain +803 +154, WebRtcIsacfix_kCdfGain +803 +212, WebRtcIsacfix_kCdfGain +803 +268 - } -}; - -const WebRtc_UWord16 *WebRtcIsacfix_kCdfShapePtr[3][108] = { - { WebRtcIsacfix_kCdfShape +0 +0, WebRtcIsacfix_kCdfShape +0 +2, WebRtcIsacfix_kCdfShape +0 +4, - WebRtcIsacfix_kCdfShape +0 +6, WebRtcIsacfix_kCdfShape +0 +8, WebRtcIsacfix_kCdfShape +0 +10, - WebRtcIsacfix_kCdfShape +0 +12, WebRtcIsacfix_kCdfShape +0 +14, WebRtcIsacfix_kCdfShape +0 +16, - WebRtcIsacfix_kCdfShape +0 +18, WebRtcIsacfix_kCdfShape +0 +21, WebRtcIsacfix_kCdfShape +0 +25, - WebRtcIsacfix_kCdfShape +0 +29, WebRtcIsacfix_kCdfShape +0 +33, WebRtcIsacfix_kCdfShape +0 +37, - WebRtcIsacfix_kCdfShape +0 +43, WebRtcIsacfix_kCdfShape +0 +49, WebRtcIsacfix_kCdfShape +0 +56, - WebRtcIsacfix_kCdfShape +0 +64, WebRtcIsacfix_kCdfShape +0 +66, WebRtcIsacfix_kCdfShape +0 +68, - WebRtcIsacfix_kCdfShape +0 +70, WebRtcIsacfix_kCdfShape +0 +72, WebRtcIsacfix_kCdfShape +0 +75, - WebRtcIsacfix_kCdfShape +0 +77, WebRtcIsacfix_kCdfShape +0 +79, WebRtcIsacfix_kCdfShape +0 +81, - WebRtcIsacfix_kCdfShape +0 +83, WebRtcIsacfix_kCdfShape +0 +86, WebRtcIsacfix_kCdfShape +0 +90, - WebRtcIsacfix_kCdfShape +0 +94, WebRtcIsacfix_kCdfShape +0 +98, WebRtcIsacfix_kCdfShape +0 +102, - WebRtcIsacfix_kCdfShape +0 +107, WebRtcIsacfix_kCdfShape +0 +113, WebRtcIsacfix_kCdfShape +0 +120, - WebRtcIsacfix_kCdfShape +0 +129, WebRtcIsacfix_kCdfShape +0 +131, WebRtcIsacfix_kCdfShape +0 +133, - WebRtcIsacfix_kCdfShape +0 +135, WebRtcIsacfix_kCdfShape +0 +137, WebRtcIsacfix_kCdfShape +0 +141, - WebRtcIsacfix_kCdfShape +0 +143, WebRtcIsacfix_kCdfShape +0 +147, WebRtcIsacfix_kCdfShape +0 +151, - WebRtcIsacfix_kCdfShape +0 +155, WebRtcIsacfix_kCdfShape +0 +159, WebRtcIsacfix_kCdfShape +0 +164, - WebRtcIsacfix_kCdfShape +0 +168, WebRtcIsacfix_kCdfShape +0 +172, WebRtcIsacfix_kCdfShape +0 +178, - WebRtcIsacfix_kCdfShape +0 +184, WebRtcIsacfix_kCdfShape +0 +192, WebRtcIsacfix_kCdfShape +0 +200, - WebRtcIsacfix_kCdfShape +0 +211, WebRtcIsacfix_kCdfShape +0 +213, WebRtcIsacfix_kCdfShape +0 +215, - WebRtcIsacfix_kCdfShape +0 +217, WebRtcIsacfix_kCdfShape +0 +219, WebRtcIsacfix_kCdfShape +0 +223, - WebRtcIsacfix_kCdfShape +0 +227, WebRtcIsacfix_kCdfShape +0 +231, WebRtcIsacfix_kCdfShape +0 +235, - WebRtcIsacfix_kCdfShape +0 +239, WebRtcIsacfix_kCdfShape +0 +243, WebRtcIsacfix_kCdfShape +0 +248, - WebRtcIsacfix_kCdfShape +0 +252, WebRtcIsacfix_kCdfShape +0 +258, WebRtcIsacfix_kCdfShape +0 +264, - WebRtcIsacfix_kCdfShape +0 +273, WebRtcIsacfix_kCdfShape +0 +282, WebRtcIsacfix_kCdfShape +0 +293, - WebRtcIsacfix_kCdfShape +0 +308, WebRtcIsacfix_kCdfShape +0 +310, WebRtcIsacfix_kCdfShape +0 +312, - WebRtcIsacfix_kCdfShape +0 +316, WebRtcIsacfix_kCdfShape +0 +320, WebRtcIsacfix_kCdfShape +0 +324, - WebRtcIsacfix_kCdfShape +0 +328, WebRtcIsacfix_kCdfShape +0 +332, WebRtcIsacfix_kCdfShape +0 +336, - WebRtcIsacfix_kCdfShape +0 +341, WebRtcIsacfix_kCdfShape +0 +347, WebRtcIsacfix_kCdfShape +0 +354, - WebRtcIsacfix_kCdfShape +0 +360, WebRtcIsacfix_kCdfShape +0 +368, WebRtcIsacfix_kCdfShape +0 +378, - WebRtcIsacfix_kCdfShape +0 +388, WebRtcIsacfix_kCdfShape +0 +400, WebRtcIsacfix_kCdfShape +0 +418, - WebRtcIsacfix_kCdfShape +0 +445, WebRtcIsacfix_kCdfShape +0 +447, WebRtcIsacfix_kCdfShape +0 +451, - WebRtcIsacfix_kCdfShape +0 +455, WebRtcIsacfix_kCdfShape +0 +461, WebRtcIsacfix_kCdfShape +0 +468, - WebRtcIsacfix_kCdfShape +0 +474, WebRtcIsacfix_kCdfShape +0 +480, WebRtcIsacfix_kCdfShape +0 +486, - WebRtcIsacfix_kCdfShape +0 +495, WebRtcIsacfix_kCdfShape +0 +505, WebRtcIsacfix_kCdfShape +0 +516, - WebRtcIsacfix_kCdfShape +0 +528, WebRtcIsacfix_kCdfShape +0 +543, WebRtcIsacfix_kCdfShape +0 +564, - WebRtcIsacfix_kCdfShape +0 +583, WebRtcIsacfix_kCdfShape +0 +608, WebRtcIsacfix_kCdfShape +0 +635 - }, - { WebRtcIsacfix_kCdfShape +686 +0, WebRtcIsacfix_kCdfShape +686 +2, WebRtcIsacfix_kCdfShape +686 +4, - WebRtcIsacfix_kCdfShape +686 +6, WebRtcIsacfix_kCdfShape +686 +8, WebRtcIsacfix_kCdfShape +686 +11, - WebRtcIsacfix_kCdfShape +686 +13, WebRtcIsacfix_kCdfShape +686 +15, WebRtcIsacfix_kCdfShape +686 +17, - WebRtcIsacfix_kCdfShape +686 +20, WebRtcIsacfix_kCdfShape +686 +23, WebRtcIsacfix_kCdfShape +686 +27, - WebRtcIsacfix_kCdfShape +686 +31, WebRtcIsacfix_kCdfShape +686 +35, WebRtcIsacfix_kCdfShape +686 +40, - WebRtcIsacfix_kCdfShape +686 +44, WebRtcIsacfix_kCdfShape +686 +50, WebRtcIsacfix_kCdfShape +686 +56, - WebRtcIsacfix_kCdfShape +686 +63, WebRtcIsacfix_kCdfShape +686 +65, WebRtcIsacfix_kCdfShape +686 +67, - WebRtcIsacfix_kCdfShape +686 +69, WebRtcIsacfix_kCdfShape +686 +71, WebRtcIsacfix_kCdfShape +686 +73, - WebRtcIsacfix_kCdfShape +686 +75, WebRtcIsacfix_kCdfShape +686 +77, WebRtcIsacfix_kCdfShape +686 +79, - WebRtcIsacfix_kCdfShape +686 +82, WebRtcIsacfix_kCdfShape +686 +85, WebRtcIsacfix_kCdfShape +686 +89, - WebRtcIsacfix_kCdfShape +686 +93, WebRtcIsacfix_kCdfShape +686 +97, WebRtcIsacfix_kCdfShape +686 +102, - WebRtcIsacfix_kCdfShape +686 +106, WebRtcIsacfix_kCdfShape +686 +112, WebRtcIsacfix_kCdfShape +686 +119, - WebRtcIsacfix_kCdfShape +686 +127, WebRtcIsacfix_kCdfShape +686 +129, WebRtcIsacfix_kCdfShape +686 +131, - WebRtcIsacfix_kCdfShape +686 +133, WebRtcIsacfix_kCdfShape +686 +135, WebRtcIsacfix_kCdfShape +686 +137, - WebRtcIsacfix_kCdfShape +686 +139, WebRtcIsacfix_kCdfShape +686 +142, WebRtcIsacfix_kCdfShape +686 +146, - WebRtcIsacfix_kCdfShape +686 +150, WebRtcIsacfix_kCdfShape +686 +154, WebRtcIsacfix_kCdfShape +686 +158, - WebRtcIsacfix_kCdfShape +686 +162, WebRtcIsacfix_kCdfShape +686 +167, WebRtcIsacfix_kCdfShape +686 +173, - WebRtcIsacfix_kCdfShape +686 +179, WebRtcIsacfix_kCdfShape +686 +186, WebRtcIsacfix_kCdfShape +686 +194, - WebRtcIsacfix_kCdfShape +686 +205, WebRtcIsacfix_kCdfShape +686 +207, WebRtcIsacfix_kCdfShape +686 +209, - WebRtcIsacfix_kCdfShape +686 +211, WebRtcIsacfix_kCdfShape +686 +214, WebRtcIsacfix_kCdfShape +686 +218, - WebRtcIsacfix_kCdfShape +686 +222, WebRtcIsacfix_kCdfShape +686 +226, WebRtcIsacfix_kCdfShape +686 +230, - WebRtcIsacfix_kCdfShape +686 +234, WebRtcIsacfix_kCdfShape +686 +238, WebRtcIsacfix_kCdfShape +686 +242, - WebRtcIsacfix_kCdfShape +686 +247, WebRtcIsacfix_kCdfShape +686 +253, WebRtcIsacfix_kCdfShape +686 +262, - WebRtcIsacfix_kCdfShape +686 +269, WebRtcIsacfix_kCdfShape +686 +278, WebRtcIsacfix_kCdfShape +686 +289, - WebRtcIsacfix_kCdfShape +686 +305, WebRtcIsacfix_kCdfShape +686 +307, WebRtcIsacfix_kCdfShape +686 +309, - WebRtcIsacfix_kCdfShape +686 +311, WebRtcIsacfix_kCdfShape +686 +315, WebRtcIsacfix_kCdfShape +686 +319, - WebRtcIsacfix_kCdfShape +686 +323, WebRtcIsacfix_kCdfShape +686 +327, WebRtcIsacfix_kCdfShape +686 +331, - WebRtcIsacfix_kCdfShape +686 +335, WebRtcIsacfix_kCdfShape +686 +340, WebRtcIsacfix_kCdfShape +686 +346, - WebRtcIsacfix_kCdfShape +686 +354, WebRtcIsacfix_kCdfShape +686 +362, WebRtcIsacfix_kCdfShape +686 +374, - WebRtcIsacfix_kCdfShape +686 +384, WebRtcIsacfix_kCdfShape +686 +396, WebRtcIsacfix_kCdfShape +686 +413, - WebRtcIsacfix_kCdfShape +686 +439, WebRtcIsacfix_kCdfShape +686 +442, WebRtcIsacfix_kCdfShape +686 +446, - WebRtcIsacfix_kCdfShape +686 +450, WebRtcIsacfix_kCdfShape +686 +455, WebRtcIsacfix_kCdfShape +686 +461, - WebRtcIsacfix_kCdfShape +686 +468, WebRtcIsacfix_kCdfShape +686 +475, WebRtcIsacfix_kCdfShape +686 +481, - WebRtcIsacfix_kCdfShape +686 +489, WebRtcIsacfix_kCdfShape +686 +498, WebRtcIsacfix_kCdfShape +686 +508, - WebRtcIsacfix_kCdfShape +686 +522, WebRtcIsacfix_kCdfShape +686 +534, WebRtcIsacfix_kCdfShape +686 +554, - WebRtcIsacfix_kCdfShape +686 +577, WebRtcIsacfix_kCdfShape +686 +602, WebRtcIsacfix_kCdfShape +686 +631 - }, - { WebRtcIsacfix_kCdfShape +1368 +0, WebRtcIsacfix_kCdfShape +1368 +2, WebRtcIsacfix_kCdfShape +1368 +4, - WebRtcIsacfix_kCdfShape +1368 +6, WebRtcIsacfix_kCdfShape +1368 +8, WebRtcIsacfix_kCdfShape +1368 +10, - WebRtcIsacfix_kCdfShape +1368 +12, WebRtcIsacfix_kCdfShape +1368 +14, WebRtcIsacfix_kCdfShape +1368 +16, - WebRtcIsacfix_kCdfShape +1368 +20, WebRtcIsacfix_kCdfShape +1368 +24, WebRtcIsacfix_kCdfShape +1368 +28, - WebRtcIsacfix_kCdfShape +1368 +32, WebRtcIsacfix_kCdfShape +1368 +36, WebRtcIsacfix_kCdfShape +1368 +40, - WebRtcIsacfix_kCdfShape +1368 +44, WebRtcIsacfix_kCdfShape +1368 +50, WebRtcIsacfix_kCdfShape +1368 +57, - WebRtcIsacfix_kCdfShape +1368 +65, WebRtcIsacfix_kCdfShape +1368 +67, WebRtcIsacfix_kCdfShape +1368 +69, - WebRtcIsacfix_kCdfShape +1368 +71, WebRtcIsacfix_kCdfShape +1368 +73, WebRtcIsacfix_kCdfShape +1368 +75, - WebRtcIsacfix_kCdfShape +1368 +77, WebRtcIsacfix_kCdfShape +1368 +79, WebRtcIsacfix_kCdfShape +1368 +81, - WebRtcIsacfix_kCdfShape +1368 +85, WebRtcIsacfix_kCdfShape +1368 +89, WebRtcIsacfix_kCdfShape +1368 +93, - WebRtcIsacfix_kCdfShape +1368 +97, WebRtcIsacfix_kCdfShape +1368 +101, WebRtcIsacfix_kCdfShape +1368 +105, - WebRtcIsacfix_kCdfShape +1368 +110, WebRtcIsacfix_kCdfShape +1368 +116, WebRtcIsacfix_kCdfShape +1368 +123, - WebRtcIsacfix_kCdfShape +1368 +132, WebRtcIsacfix_kCdfShape +1368 +134, WebRtcIsacfix_kCdfShape +1368 +136, - WebRtcIsacfix_kCdfShape +1368 +138, WebRtcIsacfix_kCdfShape +1368 +141, WebRtcIsacfix_kCdfShape +1368 +143, - WebRtcIsacfix_kCdfShape +1368 +146, WebRtcIsacfix_kCdfShape +1368 +150, WebRtcIsacfix_kCdfShape +1368 +154, - WebRtcIsacfix_kCdfShape +1368 +158, WebRtcIsacfix_kCdfShape +1368 +162, WebRtcIsacfix_kCdfShape +1368 +166, - WebRtcIsacfix_kCdfShape +1368 +170, WebRtcIsacfix_kCdfShape +1368 +174, WebRtcIsacfix_kCdfShape +1368 +179, - WebRtcIsacfix_kCdfShape +1368 +185, WebRtcIsacfix_kCdfShape +1368 +193, WebRtcIsacfix_kCdfShape +1368 +203, - WebRtcIsacfix_kCdfShape +1368 +214, WebRtcIsacfix_kCdfShape +1368 +216, WebRtcIsacfix_kCdfShape +1368 +218, - WebRtcIsacfix_kCdfShape +1368 +220, WebRtcIsacfix_kCdfShape +1368 +224, WebRtcIsacfix_kCdfShape +1368 +227, - WebRtcIsacfix_kCdfShape +1368 +231, WebRtcIsacfix_kCdfShape +1368 +235, WebRtcIsacfix_kCdfShape +1368 +239, - WebRtcIsacfix_kCdfShape +1368 +243, WebRtcIsacfix_kCdfShape +1368 +247, WebRtcIsacfix_kCdfShape +1368 +251, - WebRtcIsacfix_kCdfShape +1368 +256, WebRtcIsacfix_kCdfShape +1368 +262, WebRtcIsacfix_kCdfShape +1368 +269, - WebRtcIsacfix_kCdfShape +1368 +277, WebRtcIsacfix_kCdfShape +1368 +286, WebRtcIsacfix_kCdfShape +1368 +297, - WebRtcIsacfix_kCdfShape +1368 +315, WebRtcIsacfix_kCdfShape +1368 +317, WebRtcIsacfix_kCdfShape +1368 +319, - WebRtcIsacfix_kCdfShape +1368 +323, WebRtcIsacfix_kCdfShape +1368 +327, WebRtcIsacfix_kCdfShape +1368 +331, - WebRtcIsacfix_kCdfShape +1368 +335, WebRtcIsacfix_kCdfShape +1368 +339, WebRtcIsacfix_kCdfShape +1368 +343, - WebRtcIsacfix_kCdfShape +1368 +349, WebRtcIsacfix_kCdfShape +1368 +355, WebRtcIsacfix_kCdfShape +1368 +361, - WebRtcIsacfix_kCdfShape +1368 +368, WebRtcIsacfix_kCdfShape +1368 +376, WebRtcIsacfix_kCdfShape +1368 +385, - WebRtcIsacfix_kCdfShape +1368 +397, WebRtcIsacfix_kCdfShape +1368 +411, WebRtcIsacfix_kCdfShape +1368 +429, - WebRtcIsacfix_kCdfShape +1368 +456, WebRtcIsacfix_kCdfShape +1368 +459, WebRtcIsacfix_kCdfShape +1368 +463, - WebRtcIsacfix_kCdfShape +1368 +467, WebRtcIsacfix_kCdfShape +1368 +473, WebRtcIsacfix_kCdfShape +1368 +478, - WebRtcIsacfix_kCdfShape +1368 +485, WebRtcIsacfix_kCdfShape +1368 +491, WebRtcIsacfix_kCdfShape +1368 +497, - WebRtcIsacfix_kCdfShape +1368 +505, WebRtcIsacfix_kCdfShape +1368 +514, WebRtcIsacfix_kCdfShape +1368 +523, - WebRtcIsacfix_kCdfShape +1368 +535, WebRtcIsacfix_kCdfShape +1368 +548, WebRtcIsacfix_kCdfShape +1368 +565, - WebRtcIsacfix_kCdfShape +1368 +585, WebRtcIsacfix_kCdfShape +1368 +611, WebRtcIsacfix_kCdfShape +1368 +640 - } -}; - -/* code length for all coefficients using different models */ - -const WebRtc_Word16 WebRtcIsacfix_kCodeLenGainQ11[392] = { - 25189, 16036, 8717, 358, 8757, 15706, 21456, 24397, 18502, 17559 - , 13794, 11088, 7480, 873, 6603, 11636, 14627, 16805, 19132, 26624 - , 26624, 19408, 13751, 7280, 583, 7591, 15178, 23773, 28672, 25189 - , 19045, 16442, 13412, 10397, 5893, 1338, 6376, 9992, 12074, 13853 - , 15781, 19821, 22819, 28672, 28672, 25189, 19858, 15781, 11262, 5477 - , 1298, 5632, 11814, 17234, 22020, 28672, 19677, 18125, 16587, 14521 - , 13032, 11196, 9249, 5411, 2495, 4994, 7975, 10234, 12308, 13892 - , 15148, 17944, 21725, 23917, 25189, 19539, 16293, 11531, 7808, 4475 - , 2739, 4872, 8089, 11314, 14992, 18105, 23257, 26624, 25189, 23257 - , 23257, 20982, 18697, 18023, 16338, 16036, 14539, 13695, 13146, 11763 - , 10754, 9074, 7260, 5584, 4430, 5553, 6848, 8344, 10141, 11636 - , 12535, 13416, 14342, 15477, 17296, 19282, 22349, 23773, 28672, 28672 - , 26624, 23773, 21456, 18023, 15118, 13362, 11212, 9293, 8043, 6985 - , 5908, 5721, 5853, 6518, 7316, 8360, 9716, 11289, 12912, 14652 - , 16969, 19858, 23773, 26624, 28013, 30720, 30720, 28672, 25426, 23141 - , 25426, 23773, 20720, 19408, 18697, 19282, 16859, 16338, 16026, 15377 - , 15021, 14319, 14251, 13937, 13260, 13017, 12332, 11703, 11430, 10359 - , 10128, 9405, 8757, 8223, 7974, 7859, 7646, 7673, 7997, 8580 - , 8880, 9061, 9866, 10397, 11358, 12200, 13244, 14157, 15021, 16026 - , 16490, 18697, 18479, 20011, 19677, 20720, 24576, 26276, 30720, 30720 - , 28672, 30720, 24068, 25189, 22437, 20345, 18479, 16396, 16026, 14928 - , 13877, 13450, 12696, 12766, 11626, 11098, 10159, 9998, 9437, 9275 - , 8783, 8552, 8629, 8488, 8522, 8454, 8571, 8775, 8915, 9427 - , 9483, 9851, 10260, 10933, 11131, 11974, 12560, 13833, 15080, 16304 - , 17491, 19017, 18697, 19408, 22020, 25189, 25426, 22819, 26276, 30720 - , 30720, 30720, 30720, 30720, 30720, 28672, 30720, 30720, 30720, 30720 - , 28013, 25426, 24397, 23773, 25189, 26624, 25189, 22437, 21725, 20011 - , 20527, 20720, 20771, 22020, 22020, 19858, 19408, 19972, 17866, 17360 - , 17791, 17219, 16805, 16927, 16067, 16162, 15661, 15178, 15021, 15209 - , 14845, 14570, 14490, 14490, 13733, 13617, 13794, 13577, 13312, 12824 - , 13032, 12683, 12189, 12469, 12109, 11940, 11636, 11617, 11932, 12294 - , 11578, 11775, 12039, 11654, 11560, 11439, 11909, 11421, 12029, 11513 - , 11773, 11899, 11560, 11805, 11476, 11664, 11963, 11647, 11754, 11963 - , 11703, 12211, 11932, 12074, 12469, 12535, 12560, 12912, 12783, 12866 - , 12884, 13378, 13957, 13775, 13635, 14019, 14545, 15240, 15520, 15554 - , 15697, 16490, 16396, 17281, 16599, 16969, 17963, 16859, 16983, 16805 - , 17099, 18210, 17219, 17646, 17700, 17646, 18297, 17425, 18479, 17791 - , 17718, 19282, 18672, 20173, 20982, 21725, 21456, 23773, 23257, 25189 - , 30720, 30720, 25189, 26624, 30720, 30720, 30720, 30720, 28672, 26276 - , 30720, 30720 -}; - -const WebRtc_Word16 WebRtcIsacfix_kCodeLenShapeQ11[577] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 28672 - , 0, 26624, 1, 23773, 22819, 4, 20982, 18598, 10, 19282 - , 16587, 22, 16442, 26624, 13126, 60, 14245, 26624, 26624, 12736 - , 79, 12912, 25189, 22819, 9563, 249, 9474, 22349, 28672, 23257 - , 17944, 7980, 434, 8181, 16431, 26624, 0, 0, 0, 0 - , 28672, 0, 0, 0, 0, 0, 28672, 0, 22437, 3 - , 22437, 20982, 5, 20982, 16442, 22, 16752, 13814, 49, 14646 - , 11645, 116, 11734, 26624, 28672, 10613, 158, 11010, 24397, 19539 - , 8046, 453, 7709, 19017, 28672, 23257, 15110, 6770, 758, 6523 - , 14108, 24397, 28672, 0, 0, 0, 0, 28672, 0, 28672 - , 0, 26624, 1, 28672, 28672, 1, 26624, 24397, 2, 23257 - , 21725, 4, 20982, 17158, 18, 17281, 28672, 15178, 35, 15209 - , 12343, 92, 12320, 26624, 10344, 189, 10217, 30720, 22020, 9033 - , 322, 8549, 23773, 28672, 30720, 20622, 7666, 473, 7806, 20527 - , 24397, 14135, 5995, 960, 6018, 14872, 23773, 26624, 20928, 16293 - , 10636, 4926, 1588, 5256, 11088, 18043, 25189, 0, 0, 0 - , 0, 24397, 1, 25189, 20720, 5, 21456, 21209, 3, 25189 - , 20982, 5, 21456, 15818, 30, 15410, 13794, 60, 13416, 28672 - , 11162, 142, 11025, 9337, 231, 10094, 23773, 8338, 405, 7930 - , 26624, 19677, 6787, 613, 7318, 19161, 28672, 16442, 6319, 932 - , 5748, 15312, 25189, 28672, 28672, 28672, 13998, 5513, 1263, 5146 - , 14024, 24397, 22819, 15818, 9460, 4447, 2122, 4681, 9970, 15945 - , 22349, 28672, 30720, 22622, 19017, 14872, 10689, 7405, 4473, 2983 - , 4783, 7894, 11186, 14964, 18210, 24397, 0, 0, 30720, 0 - , 30720, 21456, 3, 23773, 14964, 39, 14757, 14179, 53, 13751 - , 14928, 36, 15272, 12430, 79, 13228, 9135, 285, 9077, 28672 - , 28672, 8377, 403, 7919, 26624, 28672, 23257, 7068, 560, 7473 - , 20345, 19677, 6770, 720, 6464, 18697, 25189, 16249, 5779, 1087 - , 5494, 15209, 22819, 30720, 20622, 12601, 5240, 1419, 5091, 12095 - , 19408, 26624, 22819, 16805, 10683, 4812, 2056, 4293, 9836, 16026 - , 24397, 25189, 18409, 13833, 8681, 4503, 2653, 4220, 8329, 13853 - , 19132, 26624, 25189, 20771, 17219, 12630, 9520, 6733, 4565, 3657 - , 4817, 7069, 10058, 13212, 16805, 21209, 26624, 26276, 28672, 28672 - , 26276, 23257, 20173, 19282, 16538, 15051, 12811, 10754, 9267, 7547 - , 6270, 5407, 5214, 6057, 7054, 8226, 9488, 10806, 12793, 14442 - , 16442, 19677, 22099, 26276, 28672, 0, 30720, 0, 30720, 11920 - , 56, 20720, 30720, 6766, 355, 13130, 30720, 30720, 22180, 5589 - , 736, 7902, 26624, 30720, 7634, 354, 9721, 30720, 30720, 9027 - , 246, 10117, 30720, 30720, 9630, 453, 6709, 23257, 30720, 25683 - , 14228, 6127, 1271, 4615, 15178, 30720, 30720, 23504, 12382, 5739 - , 2015, 3492, 10560, 22020, 26624, 30720, 30720, 23257, 13192, 4873 - , 1527, 5001, 12445, 22020, 30720, 30720, 30720, 30720, 19344, 10761 - , 4051, 1927, 5281, 10594, 17866, 28672, 30720, 30720, 30720, 21869 - , 15554, 10060, 5979, 2710, 3085, 7889, 14646, 21725, 28672, 30720 - , 30720, 30720, 30720, 30720, 30720, 30720, 22719, 17425, 13212, 8083 - , 4439, 2820, 4305, 8136, 12988, 17425, 21151, 28672, 28672, 30720 - , 30720, 30720, 28672, 20527, 19282, 14412, 10513, 7407, 5079, 3744 - , 4115, 6308, 9621, 13599, 17040, 22349, 28672, 30720, 30720, 30720 - , 30720, 30720, 30720, 29522, 19282, 14545, 11485, 9093, 6760, 5262 - , 4672, 4970, 6005, 7852, 9732, 12343, 14672, 19161, 22819, 25189 - , 30720, 30720, 28672, 30720, 30720, 20720, 18125, 14388, 12007, 9825 - , 8092, 7064, 6069, 5903, 5932, 6359, 7169, 8310, 9324, 10711 - , 11867, 13096, 14157, 16338, 17040, 19161, 21725, 23773, 30720, 30720 - , 26276, 25426, 24397, 28672, 28672, 23257, 22020, 22349, 18297, 17646 - , 16983, 16431, 16162, 15021, 15178, 13751, 12142, 10895, 10193, 9632 - , 9086, 8896, 8823, 8735, 8591, 8754, 8649, 8361, 8329, 8522 - , 8373, 8739, 8993, 9657, 10454, 11279, 11899, 12614, 14024, 14273 - , 15477, 15240, 16649, 17866, 18697, 21151, 22099 -}; - -/* left KLT transforms */ -const WebRtc_Word16 WebRtcIsacfix_kT1GainQ15[3][4] = { - { -26130, 19773, 19773, 26130 }, - { -26664, 19046, 19046, 26664 }, - { -23538, 22797, 22797, 23538 } -}; - - - -const WebRtc_Word16 WebRtcIsacfix_kT1ShapeQ15[3][324] = { - { 52,16,168,7,439,-138,-89,306,671,882, - 157,1301,291,1598,-3571,-1943,-1119,32404,96,-12, - 379,-64,-307,345,-836,539,1045,2541,-2865,-992, - 1683,-4717,5808,7427,30599,2319,183,-73,451,481, - 933,-198,781,-397,1244,-777,3690,-2414,149,-1356, - -2593,-31140,8289,-1737,-202,-14,-214,360,501,450, - -245,-7,797,3638,-2804,3042,-337,22137,-22103,2264, - 6838,-3381,305,172,263,-195,-355,351,179,513, - 2234,3343,5509,7531,19075,-17740,-16836,2244,-629,-1505, - -153,108,124,-324,2694,-124,1492,-850,5347,4285, - 7439,-10229,-22822,-12467,-12891,3645,822,-232,131,13, - 374,565,536,4681,1294,-1935,1926,-5734,-10643,26462, - -12480,-5589,-1038,-2468,964,-704,-247,-106,186,-558, - -4050,3760,2972,2141,-7393,6294,26740,11991,-3251,5461, - 5341,1574,2208,-51,-552,-297,-753,-154,2068,-5371, - 3578,4106,28043,-10533,8041,2353,2389,4609,3410,1906, - 351,-249,18,-15,1117,539,2870,9084,17585,-24528, - -366,-6490,2009,-3170,2942,1116,-232,1672,1065,606, - -399,-388,-518,38,3728,28948,-11936,4543,4104,-4441, - 1545,-4044,1485,622,-68,186,-473,135,-280,125, - -546,-1813,6989,6606,23711,19376,-2636,2870,-4553,-1687, - 878,-375,205,-208,-409,-108,-200,-45,-1670,-337, - 8213,-5524,-2334,5240,-12939,-26205,5937,-1582,-592,-959, - -5374,2449,3400,559,349,-492,668,12379,-27684,3419, - 5117,4415,-297,-8270,-1252,-3490,-1272,-1199,-3159,191, - 630,488,-797,-3071,12912,-27783,-10249,1047,647,619, - 111,-3722,-915,-1055,-502,5,-1384,-306,221,68, - 5219,13173,-26474,-11663,-5626,927,806,-1127,236,-589, - -522,-230,-312,-315,-428,-573,426,192,-11830,-26883, - -14121,-2785,-1429,-109,410,-832,-302,539,-459,104, - 1,-530,-202,-289,153,116,30082,-12944,-671,20, - 649,98,103,215,234,0,280,-51,-169,298, - 31,230,-73,-51 - }, - { -154,-7,-192,61,-739,-389,-947,-162,-60,94, - 511,-716,1520,-1428,4168,-2214,1816,32270,-123,-77, - -199,-99,-42,-588,203,-240,-930,-35,1580,234, - 3206,-5507,-1495,-10946,30000,-2667,-136,-176,-240,-175, - -204,-661,-1796,-1039,-1271,498,3143,734,2663,2699, - -8127,29333,10495,2356,-72,113,-91,118,-2840,-723, - -1733,-1158,-389,-2116,-3054,-3,-5179,8071,29546,6308, - 5657,-3178,-186,-294,-473,-635,1213,-983,-1437,-1715, - -1094,1280,-92,-9573,948,29576,-7060,-5921,2954,1349, - -337,-108,-1099,962,418,-413,-1149,-334,1241,3975, - -6825,26725,-14377,7051,-4772,-1707,2335,2008,-150,570, - 1371,42,-1649,-619,2039,3369,-1225,1583,-2755,-15207, - -27504,-4855,-4304,1495,2733,1324,15,-448,403,353, - 3016,-1242,2338,2673,2064,-7496,-30447,-3686,5833,-1301, - -2455,2122,1519,608,43,-653,773,-3072,912,-1537, - 4505,10284,30237,1549,3200,-691,205,1702,658,1014, - 1499,148,79,-322,-1162,-4639,-813,7536,3204,29109, - -10747,-26,1611,2286,2114,2561,1022,372,348,207, - 1062,-1088,-443,-9849,2381,5671,29097,-7612,-2927,3853, - 194,1155,275,1438,1438,1312,581,888,-784,906, - 112,-11103,25104,14438,-9311,-3068,1210,368,370,-940, - -2434,-1148,1925,392,657,258,-526,1475,-2281,-4265, - -1880,1534,2185,-1472,959,-30934,6306,3114,-4109,1768, - -2612,-703,45,644,2185,2033,5670,7211,19114,-22427, - 6432,5150,-4090,-2694,3860,1245,-596,293,1829,369, - -319,229,-3256,2170,-6374,-26216,-4570,-16053,-5766,-262, - -2006,2873,-1477,147,378,-1544,-344,-544,-985,-481, - 4210,4542,30757,-7291,-4863,1529,-2079,-628,-603,-783, - -408,1646,697,808,-620,-292,181,158,-13313,-29173, - 5984,-1262,859,-1776,-558,-24,-883,-1421,739,210, - -531,-285,131,-160,-246,-56,29345,-13706,-2859,-2966, - -300,-970,-2382,-268,-103,-636,-12,-62,-691,-253, - -147,-127,27,66 - }, - { 55,-212,-198,489,-274,81,682,399,328,-934, - -389,-37,1357,-3632,5276,6581,-9493,-29921,29,-45, - 2,190,172,-15,311,-130,-1085,-25,324,-684, - 3223,-6580,4485,-5280,-29521,9933,82,-320,-530,229, - -705,-533,-414,848,-1842,-4473,1390,-857,6717,-6692, - 4648,29397,576,8339,-68,-85,238,-330,264,-1012, - -381,-203,-3384,-3329,3906,6810,3790,-6250,28312,-8078, - 8089,1565,160,-569,-612,-613,-1063,-1928,-1125,3421, - -7481,-7484,4942,-6984,4330,-25591,-10574,-6982,5682,-1781, - -308,89,178,-1715,-420,-3530,-5776,1219,-8617,-7137, - 7015,4981,24875,12657,-5408,-3356,-785,-1972,326,-858, - -506,-3382,-986,-6258,-2259,4015,-8374,-10482,3127,23826, - -14126,-514,-5417,2178,-2912,-17,-587,80,67,-5881, - -1702,-5351,-4481,398,-10156,-225,20727,-15460,-11603,7752, - 3660,1714,-2001,-359,499,-527,-1225,-7820,-1297,-6326, - -8526,7900,-18328,13311,-17488,-2926,-196,-17,2281,873, - 480,-160,-624,471,780,-8729,1707,-14262,-20647,1721, - 18590,-2206,-1214,-1066,312,-2602,783,-412,-113,49, - -119,1305,-2371,-15132,-1833,-18252,20295,-8316,2227,341, - -2074,-702,3082,-262,-465,-198,430,30,-70,-788, - 2342,-25132,-4863,19783,-484,2137,2811,-1906,799,1586, - 962,-734,-191,-30,-129,-93,-1126,1729,5860,-2030, - 8953,603,-3338,-10869,-1144,22070,12130,10513,3191,-6881, - -3514,2090,711,-666,1843,-5997,-5681,2921,-17641,-2801, - 4969,18590,7169,12214,8587,4405,3008,-1074,-371,-77, - 253,331,-5611,5014,13152,-1985,18483,-1696,8043,20463, - 2381,-393,1688,-1205,618,1220,457,248,-83,176, - 7920,-13676,-22139,-3038,17402,2036,844,3258,994,719, - 2087,-44,426,494,12,-91,46,5,-14204,22912, - -18156,-361,442,2298,-829,2229,386,1433,1335,1323, - 55,-592,-139,49,-12,-57,27783,17134,350,-282, - 552,158,142,2488,465,329,1087,118,143,10, - 56,65,-15,-31 - } -}; - -/* right KLT transforms */ -const WebRtc_Word16 WebRtcIsacfix_kT2GainQ15[3][36] = { - { 4775, -14892, 20313, -17104, 10533, -3613, -6782, 16044, -8889, - -11019, 21330, -10720, 13193, -15678, -11101, 14461, 12250, -13096, - -16951, 2167, 16066, 15569, -702, -16754, -19195, -12823, -4321, - 5128, 13348, 17825, 13232, 13404, 13494, 13490, 13383, 13261 - }, - { -3725, 11408, -18493, 20031, -13097, 3865, 9344, -19294, 10740, - 8856, -18432, 8982, 13975, -14444, -11930, 11774, 14285, -13594, - -16323, -4, 16340, 15609, 359, -17220, -18401, -13471, -4643, - 5225, 13375, 18053, 13124, 13463, 13621, 13583, 13393, 13072 - }, - { -3513, 11402, -17883, 19504, -14399, 4885, 8702, -19513, 12046, - 8533, -18110, 8447, 12778, -14838, -12444, 13177, 14107, -12759, - -17268, 914, 15822, 15661, 838, -16686, -18907, -12936, -4820, - 4175, 12398, 18830, 12913, 13215, 13433, 13572, 13601, 13518 - } -}; - -const WebRtc_Word16 WebRtcIsacfix_kT2ShapeQ15[3][36] = { - { 4400, -11512, 17205, -19470, 14770, -5345, 9784, -19222, 11228, - 6842, -18371, 9909, 14191, -13496, -11563, 14015, 11827, -14839, - -15439, 948, 17802, 14827, -2053, -17132, 18723, 14516, 4135, - -6822, -13869, -16016, 12975, 13341, 13563, 13603, 13478, 13296 - }, - { 5420, -14215, 19060, -18073, 11709, -3911, 9645, -18335, 7717, - 10842, -19283, 9777, 14898, -12555, -13661, 11668, 13520, -13733, - -15936, -1358, 15671, 16728, 328, -17100, 17527, 13973, 5587, - -5194, -14165, -17677, 12970, 13446, 13693, 13660, 13462, 13015 - }, - { 4386, -12426, 18019, -18895, 13894, -5034, 9713, -19270, 10283, - 8692, -18439, 9317, 13992, -13454, -13241, 12850, 13366, -13336, - -16334, -498, 15976, 16213, -114, -16987, 18191, 13659, 4958, - -5116, -13444, -18021, 12911, 13424, 13718, 13674, 13464, 13054 - } -}; - -/* means of log gains and LAR coefficients*/ -const WebRtc_Word16 WebRtcIsacfix_kMeansGainQ8[3][12] = { - { -1758, -1370, -1758, -1373, -1757, -1375, - -1758, -1374, -1758, -1373, -1755, -1370 - }, - { -1569, -1224, -1569, -1225, -1569, -1227, - -1569, -1226, -1567, -1225, -1565, -1224 - }, - { -1452, -957, -1447, -951, -1438, -944, - -1431, -938, -1419, -931, -1406, -926 - } -}; - - -const WebRtc_Word32 WebRtcIsacfix_kMeansShapeQ17[3][108] = { - { -119581, 34418, -44193, 11112, -4428, 18906, 9222, 8068, 1953, 5425, - 1871, 1689, 109933, 33751, 10471, -2566, 1090, 2320, -119219, 33728, - -43759, 11450, -4870, 19117, 9174, 8037, 1972, 5331, 1872, 1843, - 109899, 34301, 10629, -2316, 1272, 2562, -118608, 32318, -44012, 11591, - -4914, 18932, 9456, 8088, 1900, 5419, 1723, 1853, 109963, 35059, - 10745, -2335, 1161, 2520, -119174, 32107, -44462, 11635, -4694, 18611, - 9757, 8108, 1969, 5486, 1673, 1777, 109636, 34907, 10643, -2406, - 1034, 2420, -118597, 32320, -44590, 10854, -4569, 18821, 9701, 7866, - 2003, 5577, 1732, 1626, 109913, 34448, 10714, -2752, 990, 2228, - -118138, 32996, -44352, 10334, -3772, 18488, 9464, 7865, 2208, 5540, - 1745, 1664, 109880, 33381, 10640, -2779, 980, 2054 - }, - { -146328, 46370, 1047, 26431, 10035, 13933, 6415, 14359, -2368, 6661, - 2269, 1764, 96623, 7802, 4163, 10742, 1643, 2954, -146871, 46561, 1127, - 26225, 10113, 14096, 6771, 14323, -2037, 6788, 2297, 1761, 96324, 8382, - 4309, 10450, 1695, 3016, -146502, 46475, 1580, 26118, 10487, 14179, 6622, - 14439, -2034, 6757, 2342, 1761, 95869, 8966, 4347, 10358, 1999, 2855, - -146958, 47717, 826, 25952, 10263, 14061, 5266, 13681, -2417, 6582, 2047, - 1608, 96257, 9107, 4452, 10301, 1792, 2676, -146992, 47123, 446, 25822, - 10405, 14292, 5140, 13804, -2403, 6496, 1834, 1735, 97489, 9253, 4414, - 10684, 1549, 2721, -145811, 46182, 901, 26482, 10241, 14524, 6075, 14514, - -2147, 6691, 2196, 1899, 97011, 8178, 4102, 10758, 1638, 2869 - }, - { -166617, 46969, -43908, 17726, 6330, 25615, 6913, 5450, -2301, 1984, - 507, 2883, 149998, 28709, 19333, 16703, 11093, 8965, -168254, 46604, - -44315, 17862, 6474, 25746, 7018, 5373, -2343, 1930, 513, 2819, 150391, - 28627, 19194, 16678, 10998, 8929, -169093, 46084, -44767, 17427, 6401, - 25674, 7147, 5472, -2336, 1820, 491, 2802, 149860, 28430, 19064, 16524, - 10898, 8875, -170205, 46189, -44877, 17403, 6190, 25209, 7035, 5673, -2173, - 1894, 574, 2756, 148830, 28230, 18819, 16418, 10789, 8811, -171263, 45045, - -44834, 16858, 6103, 24726, 7014, 5713, -2103, 1877, 518, 2729, 147073, - 27744, 18629, 16277, 10690, 8703, -171720, 44153, -45062, 15951, 5872, - 24429, 7044, 5585, -2082, 1807, 519, 2769, 144791, 27402, 18490, 16126, - 10548, 8635 - } -}; diff --git a/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.h b/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.h deleted file mode 100644 index 4f2e0e7f1..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/lpc_tables.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_tables.h - * - * header file for coding tables for the LPC coefficients - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_TABLES_H_ - -#include "typedefs.h" - - -/* indices of KLT coefficients used */ -extern const WebRtc_UWord16 WebRtcIsacfix_kSelIndGain[12]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kSelIndShape[108]; - -/* cdf array for model indicator */ -extern const WebRtc_UWord16 WebRtcIsacfix_kModelCdf[KLT_NUM_MODELS+1]; - -/* pointer to cdf array for model indicator */ -extern const WebRtc_UWord16 *WebRtcIsacfix_kModelCdfPtr[1]; - -/* initial cdf index for decoder of model indicator */ -extern const WebRtc_UWord16 WebRtcIsacfix_kModelInitIndex[1]; - -/* offset to go from rounded value to quantization index */ -extern const WebRtc_Word16 WebRtcIsacfix_kQuantMinGain[12]; - -extern const WebRtc_Word16 WebRtcIsacfix_kQuantMinShape[108]; - -/* maximum quantization index */ -extern const WebRtc_UWord16 WebRtcIsacfix_kMaxIndGain[12]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kMaxIndShape[108]; - -/* index offset */ -extern const WebRtc_UWord16 WebRtcIsacfix_kOffsetGain[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kOffsetShape[KLT_NUM_MODELS][108]; - -/* initial cdf index for KLT coefficients */ -extern const WebRtc_UWord16 WebRtcIsacfix_kInitIndexGain[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kInitIndexShape[KLT_NUM_MODELS][108]; - -/* offsets for quantizer representation levels */ -extern const WebRtc_UWord16 WebRtcIsacfix_kOfLevelsGain[3]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kOfLevelsShape[3]; - -/* quantizer representation levels */ -extern const WebRtc_Word32 WebRtcIsacfix_kLevelsGainQ17[1176]; - -extern const WebRtc_Word16 WebRtcIsacfix_kLevelsShapeQ10[1735]; - -/* cdf tables for quantizer indices */ -extern const WebRtc_UWord16 WebRtcIsacfix_kCdfGain[1212]; - -extern const WebRtc_UWord16 WebRtcIsacfix_kCdfShape[2059]; - -/* pointers to cdf tables for quantizer indices */ -extern const WebRtc_UWord16 *WebRtcIsacfix_kCdfGainPtr[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 *WebRtcIsacfix_kCdfShapePtr[KLT_NUM_MODELS][108]; - -/* code length for all coefficients using different models */ -extern const WebRtc_Word16 WebRtcIsacfix_kCodeLenGainQ11[392]; - -extern const WebRtc_Word16 WebRtcIsacfix_kCodeLenShapeQ11[577]; - -/* left KLT transforms */ -extern const WebRtc_Word16 WebRtcIsacfix_kT1GainQ15[KLT_NUM_MODELS][4]; - -extern const WebRtc_Word16 WebRtcIsacfix_kT1ShapeQ15[KLT_NUM_MODELS][324]; - -/* right KLT transforms */ -extern const WebRtc_Word16 WebRtcIsacfix_kT2GainQ15[KLT_NUM_MODELS][36]; - -extern const WebRtc_Word16 WebRtcIsacfix_kT2ShapeQ15[KLT_NUM_MODELS][36]; - -/* means of log gains and LAR coefficients */ -extern const WebRtc_Word16 WebRtcIsacfix_kMeansGainQ8[KLT_NUM_MODELS][12]; - -extern const WebRtc_Word32 WebRtcIsacfix_kMeansShapeQ17[3][108]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_LPC_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.c b/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.c deleted file mode 100644 index 1702098ed..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_estimator.c - * - * Pitch filter functions - * - */ - -#include - -#include "signal_processing_library.h" -#include "pitch_estimator.h" - -/* log2[0.2, 0.5, 0.98] in Q8 */ -static const WebRtc_Word16 kLogLagWinQ8[3] = { - -594, -256, -7 -}; - -/* [1 -0.75 0.25] in Q12 */ -static const WebRtc_Word16 kACoefQ12[3] = { - 4096, -3072, 1024 -}; - - - -static __inline WebRtc_Word32 Log2Q8( WebRtc_UWord32 x ) { - - WebRtc_Word32 zeros, lg2; - WebRtc_Word16 frac; - - zeros=WebRtcSpl_NormU32(x); - frac=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(((WebRtc_UWord32)(WEBRTC_SPL_LSHIFT_W32(x, zeros))&0x7FFFFFFF), 23); - /* log2(magn(i)) */ - - lg2= (WEBRTC_SPL_LSHIFT_W32((31-zeros), 8)+frac); - return lg2; - -} - -static __inline WebRtc_Word16 Exp2Q10(WebRtc_Word16 x) { // Both in and out in Q10 - - WebRtc_Word16 tmp16_1, tmp16_2; - - tmp16_2=(WebRtc_Word16)(0x0400|(x&0x03FF)); - tmp16_1=-(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W16(x,10); - if(tmp16_1>0) - return (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W16(tmp16_2, tmp16_1); - else - return (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W16(tmp16_2, -tmp16_1); - -} - - - -/* 1D parabolic interpolation . All input and output values are in Q8 */ -static __inline void Intrp1DQ8(WebRtc_Word32 *x, WebRtc_Word32 *fx, WebRtc_Word32 *y, WebRtc_Word32 *fy) { - - WebRtc_Word16 sign1=1, sign2=1; - WebRtc_Word32 r32, q32, t32, nom32, den32; - WebRtc_Word16 t16, tmp16, tmp16_1; - - if ((fx[0]>0) && (fx[2]>0)) { - r32=fx[1]-fx[2]; - q32=fx[0]-fx[1]; - nom32=q32+r32; - den32=WEBRTC_SPL_MUL_32_16((q32-r32), 2); - if (nom32<0) - sign1=-1; - if (den32<0) - sign2=-1; - - /* t = (q32+r32)/(2*(q32-r32)) = (fx[0]-fx[1] + fx[1]-fx[2])/(2 * fx[0]-fx[1] - (fx[1]-fx[2]))*/ - /* (Signs are removed because WebRtcSpl_DivResultInQ31 can't handle negative numbers) */ - t32=WebRtcSpl_DivResultInQ31(WEBRTC_SPL_MUL_32_16(nom32, sign1),WEBRTC_SPL_MUL_32_16(den32, sign2)); /* t in Q31, without signs */ - - t16=(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(t32, 23); /* Q8 */ - t16=t16*sign1*sign2; /* t in Q8 with signs */ - - *y = x[0]+t16; /* Q8 */ - // *y = x[1]+t16; /* Q8 */ - - /* The following code calculates fy in three steps */ - /* fy = 0.5 * t * (t-1) * fx[0] + (1-t*t) * fx[1] + 0.5 * t * (t+1) * fx[2]; */ - - /* Part I: 0.5 * t * (t-1) * fx[0] */ - tmp16_1=(WebRtc_Word16)WEBRTC_SPL_MUL_16_16(t16,t16); /* Q8*Q8=Q16 */ - tmp16_1 = WEBRTC_SPL_RSHIFT_W16(tmp16_1,2); /* Q16>>2 = Q14 */ - t16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(t16, 64); /* Q8<<6 = Q14 */ - tmp16 = tmp16_1-t16; - *fy = WEBRTC_SPL_MUL_16_32_RSFT15(tmp16, fx[0]); /* (Q14 * Q8 >>15)/2 = Q8 */ - - /* Part II: (1-t*t) * fx[1] */ - tmp16 = 16384-tmp16_1; /* 1 in Q14 - Q14 */ - *fy += WEBRTC_SPL_MUL_16_32_RSFT14(tmp16, fx[1]);/* Q14 * Q8 >> 14 = Q8 */ - - /* Part III: 0.5 * t * (t+1) * fx[2] */ - tmp16 = tmp16_1+t16; - *fy += WEBRTC_SPL_MUL_16_32_RSFT15(tmp16, fx[2]);/* (Q14 * Q8 >>15)/2 = Q8 */ - } else { - *y = x[0]; - *fy= fx[1]; - } -} - - -static void FindFour32(WebRtc_Word32 *in, WebRtc_Word16 length, WebRtc_Word16 *bestind) -{ - WebRtc_Word32 best[4]= {-100, -100, -100, -100}; - WebRtc_Word16 k; - - for (k=0; k best[3]) { - if (in[k] > best[2]) { - if (in[k] > best[1]) { - if (in[k] > best[0]) { // The Best - best[3] = best[2]; - bestind[3] = bestind[2]; - best[2] = best[1]; - bestind[2] = bestind[1]; - best[1] = best[0]; - bestind[1] = bestind[0]; - best[0] = in[k]; - bestind[0] = k; - } else { // 2nd best - best[3] = best[2]; - bestind[3] = bestind[2]; - best[2] = best[1]; - bestind[2] = bestind[1]; - best[1] = in[k]; - bestind[1] = k; - } - } else { // 3rd best - best[3] = best[2]; - bestind[3] = bestind[2]; - best[2] = in[k]; - bestind[2] = k; - } - } else { // 4th best - best[3] = in[k]; - bestind[3] = k; - } - } - } -} - - - - - -static void PCorr2Q32(const WebRtc_Word16 *in, WebRtc_Word32 *logcorQ8) -{ - WebRtc_Word16 scaling,n,k; - WebRtc_Word32 ysum32,csum32, lys, lcs; - WebRtc_Word32 prod32, oneQ8; - - - const WebRtc_Word16 *x, *inptr; - - oneQ8 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, 8); // 1.00 in Q8 - - x = in + PITCH_MAX_LAG/2 + 2; - scaling = WebRtcSpl_GetScalingSquare ((WebRtc_Word16 *) in, PITCH_CORR_LEN2, PITCH_CORR_LEN2); - ysum32 = 1; - csum32 = 0; - x = in + PITCH_MAX_LAG/2 + 2; - for (n = 0; n < PITCH_CORR_LEN2; n++) { - ysum32 += WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16) in[n],(WebRtc_Word16) in[n], scaling); // Q0 - csum32 += WEBRTC_SPL_MUL_16_16_RSFT((WebRtc_Word16) x[n],(WebRtc_Word16) in[n], scaling); // Q0 - } - - logcorQ8 += PITCH_LAG_SPAN2 - 1; - - lys=Log2Q8((WebRtc_UWord32) ysum32); // Q8 - lys=WEBRTC_SPL_RSHIFT_W32(lys, 1); //sqrt(ysum); - - if (csum32>0) { - - lcs=Log2Q8((WebRtc_UWord32) csum32); // 2log(csum) in Q8 - - if (lcs>(lys + oneQ8) ){ // csum/sqrt(ysum) > 2 in Q8 - *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) - } else { - *logcorQ8 = oneQ8; // 1.00 - } - - } else { - *logcorQ8 = 0; - } - - - for (k = 1; k < PITCH_LAG_SPAN2; k++) { - inptr = &in[k]; - ysum32 -= WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16) in[k-1],(WebRtc_Word16) in[k-1], scaling); - ysum32 += WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16) in[PITCH_CORR_LEN2 + k - 1],(WebRtc_Word16) in[PITCH_CORR_LEN2 + k - 1], scaling); - csum32 = 0; - prod32 = WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16) x[0],(WebRtc_Word16) inptr[0], scaling); - - for (n = 1; n < PITCH_CORR_LEN2; n++) { - csum32 += prod32; - prod32 = WEBRTC_SPL_MUL_16_16_RSFT( (WebRtc_Word16) x[n],(WebRtc_Word16) inptr[n], scaling); - } - - csum32 += prod32; - logcorQ8--; - - lys=Log2Q8((WebRtc_UWord32)ysum32); // Q8 - lys=WEBRTC_SPL_RSHIFT_W32(lys, 1); //sqrt(ysum); - - if (csum32>0) { - - lcs=Log2Q8((WebRtc_UWord32) csum32); // 2log(csum) in Q8 - - if (lcs>(lys + oneQ8) ){ // csum/sqrt(ysum) > 2 - *logcorQ8 = lcs - lys; // log2(csum/sqrt(ysum)) - } else { - *logcorQ8 = oneQ8; // 1.00 - } - - } else { - *logcorQ8 = 0; - } - } -} - - - -void WebRtcIsacfix_InitialPitch(const WebRtc_Word16 *in, /* Q0 */ - PitchAnalysisStruct *State, - WebRtc_Word16 *lagsQ7 /* Q7 */ - ) -{ - WebRtc_Word16 buf_dec16[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2+2]; - WebRtc_Word32 *crrvecQ8_1,*crrvecQ8_2; - WebRtc_Word32 cv1q[PITCH_LAG_SPAN2+2],cv2q[PITCH_LAG_SPAN2+2], peakvq[PITCH_LAG_SPAN2+2]; - int k; - WebRtc_Word16 peaks_indq; - WebRtc_Word16 peakiq[PITCH_LAG_SPAN2]; - WebRtc_Word32 corr; - WebRtc_Word32 corr32, corr_max32, corr_max_o32; - WebRtc_Word16 npkq; - WebRtc_Word16 best4q[4]={0,0,0,0}; - WebRtc_Word32 xq[3],yq[1],fyq[1]; - WebRtc_Word32 *fxq; - WebRtc_Word32 best_lag1q, best_lag2q; - WebRtc_Word32 tmp32a,tmp32b,lag32,ratq; - WebRtc_Word16 start; - WebRtc_Word16 oldgQ12, tmp16a, tmp16b, gain_bias16,tmp16c, tmp16d, bias16; - WebRtc_Word32 tmp32c,tmp32d, tmp32e; - WebRtc_Word16 old_lagQ; - WebRtc_Word32 old_lagQ8; - WebRtc_Word32 lagsQ8[4]; - - old_lagQ = State->PFstr_wght.oldlagQ7; // Q7 - old_lagQ8= WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)old_lagQ,1); //Q8 - - oldgQ12= State->PFstr_wght.oldgainQ12; - - crrvecQ8_1=&cv1q[1]; - crrvecQ8_2=&cv2q[1]; - - - /* copy old values from state buffer */ - memcpy(buf_dec16, State->dec_buffer16, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), (PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2))); - - /* decimation; put result after the old values */ - WebRtcIsacfix_DecimateAllpass32(in, State->decimator_state32, PITCH_FRAME_LEN, - &buf_dec16[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2]); - - /* low-pass filtering */ - start= PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2; - WebRtcSpl_FilterARFastQ12(&buf_dec16[start],&buf_dec16[start],(WebRtc_Word16*)kACoefQ12,3, PITCH_FRAME_LEN/2); - - /* copy end part back into state buffer */ - for (k = 0; k < (PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2); k++) - State->dec_buffer16[k] = buf_dec16[k+PITCH_FRAME_LEN/2]; - - - /* compute correlation for first and second half of the frame */ - PCorr2Q32(buf_dec16, crrvecQ8_1); - PCorr2Q32(buf_dec16 + PITCH_CORR_STEP2, crrvecQ8_2); - - - /* bias towards pitch lag of previous frame */ - tmp32a = Log2Q8((WebRtc_UWord32) old_lagQ8) - 2304; // log2(0.5*oldlag) in Q8 - tmp32b = WEBRTC_SPL_MUL_16_16_RSFT(oldgQ12,oldgQ12, 10); //Q12 & * 4.0; - gain_bias16 = (WebRtc_Word16) tmp32b; //Q12 - if (gain_bias16 > 3276) gain_bias16 = 3276; // 0.8 in Q12 - - - for (k = 0; k < PITCH_LAG_SPAN2; k++) - { - if (crrvecQ8_1[k]>0) { - tmp32b = Log2Q8((WebRtc_UWord32) (k + (PITCH_MIN_LAG/2-2))); - tmp16a = (WebRtc_Word16) (tmp32b - tmp32a); // Q8 & fabs(ratio)<4 - tmp32c = WEBRTC_SPL_MUL_16_16_RSFT(tmp16a,tmp16a, 6); //Q10 - tmp16b = (WebRtc_Word16) tmp32c; // Q10 & <8 - tmp32d = WEBRTC_SPL_MUL_16_16_RSFT(tmp16b, 177 , 8); // mult with ln2 in Q8 - tmp16c = (WebRtc_Word16) tmp32d; // Q10 & <4 - tmp16d = Exp2Q10((WebRtc_Word16) -tmp16c); //Q10 - tmp32c = WEBRTC_SPL_MUL_16_16_RSFT(gain_bias16,tmp16d,13); // Q10 & * 0.5 - bias16 = (WebRtc_Word16) (1024 + tmp32c); // Q10 - tmp32b = Log2Q8((WebRtc_UWord32) bias16) - 2560; // Q10 in -> Q8 out with 10*2^8 offset - crrvecQ8_1[k] += tmp32b ; // -10*2^8 offset - } - } - - /* taper correlation functions */ - for (k = 0; k < 3; k++) { - crrvecQ8_1[k] += kLogLagWinQ8[k]; - crrvecQ8_2[k] += kLogLagWinQ8[k]; - - crrvecQ8_1[PITCH_LAG_SPAN2-1-k] += kLogLagWinQ8[k]; - crrvecQ8_2[PITCH_LAG_SPAN2-1-k] += kLogLagWinQ8[k]; - } - - - /* Make zeropadded corr vectors */ - cv1q[0]=0; - cv2q[0]=0; - cv1q[PITCH_LAG_SPAN2+1]=0; - cv2q[PITCH_LAG_SPAN2+1]=0; - corr_max32 = 0; - - for (k = 1; k <= PITCH_LAG_SPAN2; k++) - { - - - corr32=crrvecQ8_1[k-1]; - if (corr32 > corr_max32) - corr_max32 = corr32; - - corr32=crrvecQ8_2[k-1]; - corr32 += -4; // Compensate for later (log2(0.99)) - - if (corr32 > corr_max32) - corr_max32 = corr32; - - } - - /* threshold value to qualify as a peak */ - // corr_max32 += -726; // log(0.14)/log(2.0) in Q8 - corr_max32 += -1000; // log(0.14)/log(2.0) in Q8 - corr_max_o32 = corr_max32; - - - /* find peaks in corr1 */ - peaks_indq = 0; - for (k = 1; k <= PITCH_LAG_SPAN2; k++) - { - corr32=cv1q[k]; - if (corr32>corr_max32) { // Disregard small peaks - if ((corr32>=cv1q[k-1]) && (corr32>cv1q[k+1])) { // Peak? - peakvq[peaks_indq] = corr32; - peakiq[peaks_indq++] = k; - } - } - } - - - /* find highest interpolated peak */ - corr_max32=0; - best_lag1q =0; - if (peaks_indq > 0) { - FindFour32(peakvq, (WebRtc_Word16) peaks_indq, best4q); - npkq = WEBRTC_SPL_MIN(peaks_indq, 4); - - for (k=0;k corr_max32) { - corr_max32 = *fyq; - best_lag1q = *yq; - } - } - tmp32a = best_lag1q - OFFSET_Q8; - tmp32b = WEBRTC_SPL_LSHIFT_W32(tmp32a, 1); - lagsQ8[0] = tmp32b + PITCH_MIN_LAG_Q8; - lagsQ8[1] = lagsQ8[0]; - } else { - lagsQ8[0] = old_lagQ8; - lagsQ8[1] = lagsQ8[0]; - } - - /* Bias towards constant pitch */ - tmp32a = lagsQ8[0] - PITCH_MIN_LAG_Q8; - ratq = WEBRTC_SPL_RSHIFT_W32(tmp32a, 1) + OFFSET_Q8; - - for (k = 1; k <= PITCH_LAG_SPAN2; k++) - { - tmp32a = WEBRTC_SPL_LSHIFT_W32(k, 7); // 0.5*k Q8 - tmp32b = (WebRtc_Word32) (WEBRTC_SPL_LSHIFT_W32(tmp32a, 1)) - ratq; // Q8 - tmp32c = WEBRTC_SPL_MUL_16_16_RSFT((WebRtc_Word16) tmp32b, (WebRtc_Word16) tmp32b, 8); // Q8 - - tmp32b = (WebRtc_Word32) tmp32c + (WebRtc_Word32) WEBRTC_SPL_RSHIFT_W32(ratq, 1); // (k-r)^2 + 0.5 * r Q8 - tmp32c = Log2Q8((WebRtc_UWord32) tmp32a) - 2048; // offset 8*2^8 , log2(0.5*k) Q8 - tmp32d = Log2Q8((WebRtc_UWord32) tmp32b) - 2048; // offset 8*2^8 , log2(0.5*k) Q8 - tmp32e = tmp32c -tmp32d; - - cv2q[k] += WEBRTC_SPL_RSHIFT_W32(tmp32e, 1); - - } - - /* find peaks in corr2 */ - corr_max32 = corr_max_o32; - peaks_indq = 0; - - for (k = 1; k <= PITCH_LAG_SPAN2; k++) - { - corr=cv2q[k]; - if (corr>corr_max32) { // Disregard small peaks - if ((corr>=cv2q[k-1]) && (corr>cv2q[k+1])) { // Peak? - peakvq[peaks_indq] = corr; - peakiq[peaks_indq++] = k; - } - } - } - - - - /* find highest interpolated peak */ - corr_max32 = 0; - best_lag2q =0; - if (peaks_indq > 0) { - - FindFour32(peakvq, (WebRtc_Word16) peaks_indq, best4q); - npkq = WEBRTC_SPL_MIN(peaks_indq, 4); - for (k=0;k corr_max32) { - corr_max32 = *fyq; - best_lag2q = *yq; - } - } - - tmp32a = best_lag2q - OFFSET_Q8; - tmp32b = WEBRTC_SPL_LSHIFT_W32(tmp32a, 1); - lagsQ8[2] = tmp32b + PITCH_MIN_LAG_Q8; - lagsQ8[3] = lagsQ8[2]; - } else { - lagsQ8[2] = lagsQ8[0]; - lagsQ8[3] = lagsQ8[0]; - } - - lagsQ7[0]=(WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(lagsQ8[0], 1); - lagsQ7[1]=(WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(lagsQ8[1], 1); - lagsQ7[2]=(WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(lagsQ8[2], 1); - lagsQ7[3]=(WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(lagsQ8[3], 1); - - -} - - - -void WebRtcIsacfix_PitchAnalysis(const WebRtc_Word16 *inn, /* PITCH_FRAME_LEN samples */ - WebRtc_Word16 *outQ0, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ - PitchAnalysisStruct *State, - WebRtc_Word16 *PitchLags_Q7, - WebRtc_Word16 *PitchGains_Q12) -{ - WebRtc_Word16 inbufQ0[PITCH_FRAME_LEN + QLOOKAHEAD]; - WebRtc_Word16 k; - - /* inital pitch estimate */ - WebRtcIsacfix_InitialPitch(inn, State, PitchLags_Q7); - - - /* Calculate gain */ - WebRtcIsacfix_PitchFilterGains(inn, &(State->PFstr_wght), PitchLags_Q7, PitchGains_Q12); - - /* concatenate previous input's end and current input */ - for (k = 0; k < QLOOKAHEAD; k++) { - inbufQ0[k] = State->inbuf[k]; - } - for (k = 0; k < PITCH_FRAME_LEN; k++) { - inbufQ0[k+QLOOKAHEAD] = (WebRtc_Word16) inn[k]; - } - - /* lookahead pitch filtering for masking analysis */ - WebRtcIsacfix_PitchFilter(inbufQ0, outQ0, &(State->PFstr), PitchLags_Q7,PitchGains_Q12, 2); - - - /* store last part of input */ - for (k = 0; k < QLOOKAHEAD; k++) { - State->inbuf[k] = inbufQ0[k + PITCH_FRAME_LEN]; - } -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.h b/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.h deleted file mode 100644 index afdc9785d..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_estimator.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_estimator.h - * - * Pitch functions - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_ESTIMATOR_H_ - -#include "structs.h" - - - -void WebRtcIsacfix_PitchAnalysis(const WebRtc_Word16 *in, /* PITCH_FRAME_LEN samples */ - WebRtc_Word16 *outQ0, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ - PitchAnalysisStruct *State, - WebRtc_Word16 *lagsQ7, - WebRtc_Word16 *PitchGains_Q12); - - -void WebRtcIsacfix_InitialPitch(const WebRtc_Word16 *in, - PitchAnalysisStruct *State, - WebRtc_Word16 *qlags); - -void WebRtcIsacfix_PitchFilter(WebRtc_Word16 *indatFix, - WebRtc_Word16 *outdatQQ, - PitchFiltstr *pfp, - WebRtc_Word16 *lagsQ7, - WebRtc_Word16 *gainsQ12, - WebRtc_Word16 type); - -void WebRtcIsacfix_PitchFilterGains(const WebRtc_Word16 *indatQ0, - PitchFiltstr *pfp, - WebRtc_Word16 *lagsQ7, - WebRtc_Word16 *gainsQ12); - - - -void WebRtcIsacfix_DecimateAllpass32(const WebRtc_Word16 *in, - WebRtc_Word32 *state_in, /* array of size: 2*ALLPASSSECTIONS+1 */ - WebRtc_Word16 N, /* number of input samples */ - WebRtc_Word16 *out); /* array of size N/2 */ - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_ESTIMATOR_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_filter.c b/modules/audio_coding/codecs/iSAC/fix/source/pitch_filter.c deleted file mode 100644 index cf11084bb..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_filter.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_estimatorfilter.c - * - * Pitch filter functions - * - */ - -#include - -#include "pitch_estimator.h" - - -/* Filter coefficicients in Q15 */ -static const WebRtc_Word16 kDampFilter[PITCH_DAMPORDER] = { - -2294, 8192, 20972, 8192, -2294 -}; - -/* Interpolation coefficients; generated by design_pitch_filter.m. - * Coefficients are stored in Q14. - */ -static const WebRtc_Word16 kIntrpCoef[PITCH_FRACS][PITCH_FRACORDER] = { - {-367, 1090, -2706, 9945, 10596, -3318, 1626, -781, 287}, - {-325, 953, -2292, 7301, 12963, -3320, 1570, -743, 271}, - {-240, 693, -1622, 4634, 14809, -2782, 1262, -587, 212}, - {-125, 358, -817, 2144, 15982, -1668, 721, -329, 118}, - { 0, 0, -1, 1, 16380, 1, -1, 0, 0}, - { 118, -329, 721, -1668, 15982, 2144, -817, 358, -125}, - { 212, -587, 1262, -2782, 14809, 4634, -1622, 693, -240}, - { 271, -743, 1570, -3320, 12963, 7301, -2292, 953, -325} -}; - - - - -static __inline WebRtc_Word32 CalcLrIntQ(WebRtc_Word32 fixVal, WebRtc_Word16 qDomain) { - WebRtc_Word32 intgr; - WebRtc_Word32 roundVal; - - roundVal = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, qDomain-1); - intgr = WEBRTC_SPL_RSHIFT_W32(fixVal+roundVal, qDomain); - - return intgr; -} - -void WebRtcIsacfix_PitchFilter(WebRtc_Word16 *indatQQ, /* Q10 if type is 1 or 4, Q0 if type is 2 */ - WebRtc_Word16 *outdatQQ, - PitchFiltstr *pfp, - WebRtc_Word16 *lagsQ7, - WebRtc_Word16 *gainsQ12, - WebRtc_Word16 type) -{ - int k, n, m, ind; - WebRtc_Word16 sign = 1; - WebRtc_Word16 inystateQQ[PITCH_DAMPORDER]; - WebRtc_Word16 ubufQQ[PITCH_INTBUFFSIZE+QLOOKAHEAD]; - WebRtc_Word16 Gain = 21299; /* 1.3 in Q14 */ - WebRtc_Word16 DivFactor = 6553; /* 0.2 in Q15 */ - WebRtc_Word16 oldLagQ7, oldGainQ12, - lagdeltaQ7, curLagQ7, - gaindeltaQ12, curGainQ12; - WebRtc_Word16 tmpW16, indW16=0, frcQQ, cnt=0, pos, pos2; - const WebRtc_Word16 *fracoeffQQ=NULL; - WebRtc_Word32 tmpW32; - - if (type==4) - sign = -1; - - /* Set up buffer and states */ - memcpy(ubufQQ, pfp->ubufQQ, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_BUFFSIZE)); - memcpy(inystateQQ, pfp->ystateQQ, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_DAMPORDER)); - - /* Get old lag and gain value from memory */ - oldLagQ7 = pfp->oldlagQ7; - oldGainQ12 = pfp->oldgainQ12; - - if (type==4) { - /* make output more periodic */ - /* Fixed 1.3 = 21299 in Q14 */ - for (k=0;k WEBRTC_SPL_RSHIFT_W16(WEBRTC_SPL_MUL_16_16(oldLagQ7, 3), 1))) { - oldLagQ7 = lagsQ7[0]; - oldGainQ12 = gainsQ12[0]; - } - - ind=0; - for (k=0;k0;m--) - inystateQQ[m] = inystateQQ[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - (indW16 + 2); - - tmpW32=0; - for (m=0;mubufQQ, ubufQQ+PITCH_FRAME_LEN, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_BUFFSIZE)); - memcpy(pfp->ystateQQ, inystateQQ, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_DAMPORDER)); - - pfp->oldlagQ7 = oldLagQ7; - pfp->oldgainQ12 = oldGainQ12; - - if (type==2) { - /* Filter look-ahead segment */ - for (n=0;n0;m--) - inystateQQ[m] = inystateQQ[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2= pos - (indW16 + 2); - - tmpW32=0; - for (m=0;mubufQQ, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_BUFFSIZE)); - oldLagQ7 = pfp->oldlagQ7; - - /* No interpolation if pitch lag step is big */ - if ((WEBRTC_SPL_RSHIFT_W16(WEBRTC_SPL_MUL_16_16(lagsQ7[0], 3), 1) < oldLagQ7) || - (lagsQ7[0] > WEBRTC_SPL_RSHIFT_W16(WEBRTC_SPL_MUL_16_16(oldLagQ7, 3), 1))) { - oldLagQ7 = lagsQ7[0]; - } - - ind=0; - scale=0; - for (k=0;k1073700000) || (csum1QQ>1073700000) || (tmpW32>1073700000) || (esumxQQ>1073700000)) {//2^30 - scale++; - csum1QQ = WEBRTC_SPL_RSHIFT_W32(csum1QQ,1); - esumxQQ = WEBRTC_SPL_RSHIFT_W32(esumxQQ,1); - } - tmp2W32 = WEBRTC_SPL_RSHIFT_W32(tmp2W32,scale); - csum1QQ += tmp2W32; - tmpW32 = WEBRTC_SPL_RSHIFT_W32(tmpW32,scale); - esumxQQ += tmpW32; - - ind++; - cnt--; - } - - if (csum1QQubufQQ, ubufQQ+PITCH_FRAME_LEN, WEBRTC_SPL_MUL_16_16(sizeof(WebRtc_Word16), PITCH_BUFFSIZE)); - pfp->oldlagQ7 = lagsQ7[PITCH_SUBFRAMES-1]; - pfp->oldgainQ12 = gainsQ12[PITCH_SUBFRAMES-1]; - -} diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.c b/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.c deleted file mode 100644 index 50ea65836..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_gain_tables.c - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#include "pitch_gain_tables.h" - - -/********************* Pitch Filter Gain Coefficient Tables ************************/ - -/* cdf for quantized pitch filter gains */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchGainCdf[255] = { - 0, 2, 4, 6, 64, 901, 903, 905, 16954, 16956, - 16961, 17360, 17362, 17364, 17366, 17368, 17370, 17372, 17374, 17411, - 17514, 17516, 17583, 18790, 18796, 18802, 20760, 20777, 20782, 21722, - 21724, 21728, 21738, 21740, 21742, 21744, 21746, 21748, 22224, 22227, - 22230, 23214, 23229, 23239, 25086, 25108, 25120, 26088, 26094, 26098, - 26175, 26177, 26179, 26181, 26183, 26185, 26484, 26507, 26522, 27705, - 27731, 27750, 29767, 29799, 29817, 30866, 30883, 30885, 31025, 31029, - 31031, 31033, 31035, 31037, 31114, 31126, 31134, 32687, 32722, 32767, - 35718, 35742, 35757, 36943, 36952, 36954, 37115, 37128, 37130, 37132, - 37134, 37136, 37143, 37145, 37152, 38843, 38863, 38897, 47458, 47467, - 47474, 49040, 49061, 49063, 49145, 49157, 49159, 49161, 49163, 49165, - 49167, 49169, 49171, 49757, 49770, 49782, 61333, 61344, 61346, 62860, - 62883, 62885, 62887, 62889, 62891, 62893, 62895, 62897, 62899, 62901, - 62903, 62905, 62907, 62909, 65496, 65498, 65500, 65521, 65523, 65525, - 65527, 65529, 65531, 65533, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535 -}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsacfix_kLowerlimiGain[3] = { - -7, -2, -1 -}; - -const WebRtc_Word16 WebRtcIsacfix_kUpperlimitGain[3] = { - 0, 3, 1 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kMultsGain[2] = { - 18, 3 -}; - -/* size of cdf table */ -const WebRtc_UWord16 WebRtcIsacfix_kCdfTableSizeGain[1] = { - 256 -}; - -/* mean values of pitch filter gains in FIXED point Q12 */ -const WebRtc_Word16 WebRtcIsacfix_kPitchGain1[144] = { - 843, 1092, 1336, 1222, 1405, 1656, 1500, 1815, 1843, 1838, 1839, - 1843, 1843, 1843, 1843, 1843, 1843, 1843, 814, 846, 1092, 1013, - 1174, 1383, 1391, 1511, 1584, 1734, 1753, 1843, 1843, 1843, 1843, - 1843, 1843, 1843, 524, 689, 777, 845, 947, 1069, 1090, 1263, - 1380, 1447, 1559, 1676, 1645, 1749, 1843, 1843, 1843, 1843, 81, - 477, 563, 611, 706, 806, 849, 1012, 1192, 1128, 1330, 1489, - 1425, 1576, 1826, 1741, 1843, 1843, 0, 290, 305, 356, 488, - 575, 602, 741, 890, 835, 1079, 1196, 1182, 1376, 1519, 1506, - 1680, 1843, 0, 47, 97, 69, 289, 381, 385, 474, 617, - 664, 803, 1079, 935, 1160, 1269, 1265, 1506, 1741, 0, 0, - 0, 0, 112, 120, 190, 283, 442, 343, 526, 809, 684, - 935, 1134, 1020, 1265, 1506, 0, 0, 0, 0, 0, 0, - 0, 111, 256, 87, 373, 597, 430, 684, 935, 770, 1020, - 1265 -}; - -const WebRtc_Word16 WebRtcIsacfix_kPitchGain2[144] = { - 1760, 1525, 1285, 1747, 1671, 1393, 1843, 1826, 1555, 1843, 1784, - 1606, 1843, 1843, 1711, 1843, 1843, 1814, 1389, 1275, 1040, 1564, - 1414, 1252, 1610, 1495, 1343, 1753, 1592, 1405, 1804, 1720, 1475, - 1843, 1814, 1581, 1208, 1061, 856, 1349, 1148, 994, 1390, 1253, - 1111, 1495, 1343, 1178, 1770, 1465, 1234, 1814, 1581, 1342, 1040, - 793, 713, 1053, 895, 737, 1128, 1003, 861, 1277, 1094, 981, - 1475, 1192, 1019, 1581, 1342, 1098, 855, 570, 483, 833, 648, - 540, 948, 744, 572, 1009, 844, 636, 1234, 934, 685, 1342, - 1217, 984, 537, 318, 124, 603, 423, 350, 687, 479, 322, - 791, 581, 430, 987, 671, 488, 1098, 849, 597, 283, 27, - 0, 397, 222, 38, 513, 271, 124, 624, 325, 157, 737, - 484, 233, 849, 597, 343, 27, 0, 0, 141, 0, 0, - 256, 69, 0, 370, 87, 0, 484, 229, 0, 597, 343, - 87 -}; - -const WebRtc_Word16 WebRtcIsacfix_kPitchGain3[144] = { - 1843, 1843, 1711, 1843, 1818, 1606, 1843, 1827, 1511, 1814, 1639, - 1393, 1760, 1525, 1285, 1656, 1419, 1176, 1835, 1718, 1475, 1841, - 1650, 1387, 1648, 1498, 1287, 1600, 1411, 1176, 1522, 1299, 1040, - 1419, 1176, 928, 1773, 1461, 1128, 1532, 1355, 1202, 1429, 1260, - 1115, 1398, 1151, 1025, 1172, 1080, 790, 1176, 928, 677, 1475, - 1147, 1019, 1276, 1096, 922, 1214, 1010, 901, 1057, 893, 800, - 1040, 796, 734, 928, 677, 424, 1137, 897, 753, 1120, 830, - 710, 875, 751, 601, 795, 642, 583, 790, 544, 475, 677, - 474, 140, 987, 750, 482, 697, 573, 450, 691, 487, 303, - 661, 394, 332, 537, 303, 220, 424, 168, 0, 737, 484, - 229, 624, 348, 153, 441, 261, 136, 397, 166, 51, 283, - 27, 0, 168, 0, 0, 484, 229, 0, 370, 57, 0, - 256, 43, 0, 141, 0, 0, 27, 0, 0, 0, 0, - 0 -}; - - -const WebRtc_Word16 WebRtcIsacfix_kPitchGain4[144] = { - 1843, 1843, 1843, 1843, 1841, 1843, 1500, 1821, 1843, 1222, 1434, - 1656, 843, 1092, 1336, 504, 757, 1007, 1843, 1843, 1843, 1838, - 1791, 1843, 1265, 1505, 1599, 965, 1219, 1425, 730, 821, 1092, - 249, 504, 757, 1783, 1819, 1843, 1351, 1567, 1727, 1096, 1268, - 1409, 805, 961, 1131, 444, 670, 843, 0, 249, 504, 1425, - 1655, 1743, 1096, 1324, 1448, 822, 1019, 1199, 490, 704, 867, - 81, 450, 555, 0, 0, 249, 1247, 1428, 1530, 881, 1073, - 1283, 610, 759, 939, 278, 464, 645, 0, 200, 270, 0, - 0, 0, 935, 1163, 1410, 528, 790, 1068, 377, 499, 717, - 173, 240, 274, 0, 43, 62, 0, 0, 0, 684, 935, - 1182, 343, 551, 735, 161, 262, 423, 0, 55, 27, 0, - 0, 0, 0, 0, 0, 430, 684, 935, 87, 377, 597, - 0, 46, 256, 0, 0, 0, 0, 0, 0, 0, 0, - 0 -}; - - - -/* transform matrix in Q12*/ -const WebRtc_Word16 WebRtcIsacfix_kTransform[4][4] = { - { -2048, -2048, -2048, -2048 }, - { 2748, 916, -916, -2748 }, - { 2048, -2048, -2048, 2048 }, - { 916, -2748, 2748, -916 } -}; diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.h b/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.h deleted file mode 100644 index 788e5536b..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_gain_tables.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_gain_tables.h - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_GAIN_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_GAIN_TABLES_H_ - -#include "typedefs.h" - - -/********************* Pitch Filter Gain Coefficient Tables ************************/ -/* cdf for quantized pitch filter gains */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchGainCdf[255]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsacfix_kLowerlimiGain[3]; -extern const WebRtc_Word16 WebRtcIsacfix_kUpperlimitGain[3]; -extern const WebRtc_UWord16 WebRtcIsacfix_kMultsGain[2]; - -/* mean values of pitch filter gains in Q12*/ -extern const WebRtc_Word16 WebRtcIsacfix_kPitchGain1[144]; -extern const WebRtc_Word16 WebRtcIsacfix_kPitchGain2[144]; -extern const WebRtc_Word16 WebRtcIsacfix_kPitchGain3[144]; -extern const WebRtc_Word16 WebRtcIsacfix_kPitchGain4[144]; - -/* size of cdf table */ -extern const WebRtc_UWord16 WebRtcIsacfix_kCdfTableSizeGain[1]; - -/* transform matrix */ -extern const WebRtc_Word16 WebRtcIsacfix_kTransform[4][4]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_GAIN_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.c b/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.c deleted file mode 100644 index 81700e474..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_lag_tables.c - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#include "settings.h" -#include "pitch_lag_tables.h" - - -/********************* Pitch Filter Gain Coefficient Tables ************************/ - -/* tables for use with small pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Lo[127] = { - 0, 134, 336, 549, 778, 998, 1264, 1512, 1777, 2070, - 2423, 2794, 3051, 3361, 3708, 3979, 4315, 4610, 4933, 5269, - 5575, 5896, 6155, 6480, 6816, 7129, 7477, 7764, 8061, 8358, - 8718, 9020, 9390, 9783, 10177, 10543, 10885, 11342, 11795, 12213, - 12680, 13096, 13524, 13919, 14436, 14903, 15349, 15795, 16267, 16734, - 17266, 17697, 18130, 18632, 19080, 19447, 19884, 20315, 20735, 21288, - 21764, 22264, 22723, 23193, 23680, 24111, 24557, 25022, 25537, 26082, - 26543, 27090, 27620, 28139, 28652, 29149, 29634, 30175, 30692, 31273, - 31866, 32506, 33059, 33650, 34296, 34955, 35629, 36295, 36967, 37726, - 38559, 39458, 40364, 41293, 42256, 43215, 44231, 45253, 46274, 47359, - 48482, 49678, 50810, 51853, 53016, 54148, 55235, 56263, 57282, 58363, - 59288, 60179, 61076, 61806, 62474, 63129, 63656, 64160, 64533, 64856, - 65152, 65535, 65535, 65535, 65535, 65535, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Lo[20] = { - 0, 429, 3558, 5861, 8558, 11639, 15210, 19502, 24773, 31983, - 42602, 48567, 52601, 55676, 58160, 60172, 61889, 63235, 65383, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Lo[2] = { - 0, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Lo[10] = { - 0, 2966, 6368, 11182, 19431, 37793, 48532, 55353, 60626, 65535 -}; - -const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrLo[4] = { - WebRtcIsacfix_kPitchLagCdf1Lo, - WebRtcIsacfix_kPitchLagCdf2Lo, - WebRtcIsacfix_kPitchLagCdf3Lo, - WebRtcIsacfix_kPitchLagCdf4Lo -}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeLo[1] = { - 128 -}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsacfix_kLowerLimitLo[4] = { - -140, -9, 0, -4 -}; - -const WebRtc_Word16 WebRtcIsacfix_kUpperLimitLo[4] = { - -20, 9, 0, 4 -}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsacfix_kInitIndLo[3] = { - 10, 1, 5 -}; - -/* mean values of pitch filter lags in Q10 */ - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Lo[19] = { - -17627, -16207, -14409, -12319, -10253, -8200, -6054, -3986, -1948, -19, - 1937, 3974, 6064, 8155, 10229, 12270, 14296, 16127, 17520 -}; - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Lo[9] = { - -7949, -6063, -4036, -1941, 38, 1977, 4060, 6059 -}; - - - -/* tables for use with medium pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Mid[255] = { - 0, 28, 61, 88, 121, 149, 233, 331, 475, 559, - 624, 661, 689, 712, 745, 791, 815, 843, 866, 922, - 959, 1024, 1061, 1117, 1178, 1238, 1280, 1350, 1453, 1513, - 1564, 1625, 1671, 1741, 1788, 1904, 2072, 2421, 2626, 2770, - 2840, 2900, 2942, 3012, 3068, 3115, 3147, 3194, 3254, 3319, - 3366, 3520, 3678, 3780, 3850, 3911, 3957, 4032, 4106, 4185, - 4292, 4474, 4683, 4842, 5019, 5191, 5321, 5428, 5540, 5675, - 5763, 5847, 5959, 6127, 6304, 6564, 6839, 7090, 7263, 7421, - 7556, 7728, 7872, 7984, 8142, 8361, 8580, 8743, 8938, 9227, - 9409, 9539, 9674, 9795, 9930, 10060, 10177, 10382, 10614, 10861, - 11038, 11271, 11415, 11629, 11792, 12044, 12193, 12416, 12574, 12821, - 13007, 13235, 13445, 13654, 13901, 14134, 14488, 15000, 15703, 16285, - 16504, 16797, 17086, 17328, 17579, 17807, 17998, 18268, 18538, 18836, - 19087, 19274, 19474, 19716, 19935, 20270, 20833, 21303, 21532, 21741, - 21978, 22207, 22523, 22770, 23054, 23613, 23943, 24204, 24399, 24651, - 24832, 25074, 25270, 25549, 25759, 26015, 26150, 26424, 26713, 27048, - 27342, 27504, 27681, 27854, 28021, 28207, 28412, 28664, 28859, 29064, - 29278, 29548, 29748, 30107, 30377, 30656, 30856, 31164, 31452, 31755, - 32011, 32328, 32626, 32919, 33319, 33789, 34329, 34925, 35396, 35973, - 36443, 36964, 37551, 38156, 38724, 39357, 40023, 40908, 41587, 42602, - 43924, 45037, 45810, 46597, 47421, 48291, 49092, 50051, 51448, 52719, - 53440, 54241, 54944, 55977, 56676, 57299, 57872, 58389, 59059, 59688, - 60237, 60782, 61094, 61573, 61890, 62290, 62658, 63030, 63217, 63454, - 63622, 63882, 64003, 64273, 64427, 64529, 64581, 64697, 64758, 64902, - 65414, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Mid[36] = { - 0, 71, 335, 581, 836, 1039, 1323, 1795, 2258, 2608, - 3005, 3591, 4243, 5344, 7163, 10583, 16848, 28078, 49448, 57007, - 60357, 61850, 62837, 63437, 63872, 64188, 64377, 64614, 64774, 64949, - 65039, 65115, 65223, 65360, 65474, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Mid[2] = { - 0, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Mid[20] = { - 0, 28, 246, 459, 667, 1045, 1523, 2337, 4337, 11347, - 44231, 56709, 60781, 62243, 63161, 63969, 64608, 65062, 65502, 65535 -}; - -const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrMid[4] = { - WebRtcIsacfix_kPitchLagCdf1Mid, - WebRtcIsacfix_kPitchLagCdf2Mid, - WebRtcIsacfix_kPitchLagCdf3Mid, - WebRtcIsacfix_kPitchLagCdf4Mid -}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeMid[1] = { - 256 -}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsacfix_kLowerLimitMid[4] = { - -280, -17, 0, -9 -}; - -const WebRtc_Word16 WebRtcIsacfix_kUpperLimitMid[4] = { - -40, 17, 0, 9 -}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsacfix_kInitIndMid[3] = { - 18, 1, 10 -}; - -/* mean values of pitch filter lags in Q10 */ - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Mid[35] = { - -17297, -16250, -15416, -14343, -13341, -12363, -11270, - -10355, -9122, -8217, -7172, -6083, -5102, -4004, -3060, - -1982, -952, -18, 935, 1976, 3040, 4032, - 5082, 6065, 7257, 8202, 9264, 10225, 11242, - 12234, 13337, 14336, 15374, 16187, 17347 -}; - - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Mid[19] = { - -8811, -8081, -7203, -6003, -5057, -4025, -2983, -1964, - -891, 29, 921, 1920, 2988, 4064, 5187, 6079, 7173, 8074, 8849 -}; - - -/* tables for use with large pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Hi[511] = { - 0, 7, 18, 33, 69, 105, 156, 228, 315, 612, - 680, 691, 709, 724, 735, 738, 742, 746, 749, 753, - 756, 760, 764, 774, 782, 785, 789, 796, 800, 803, - 807, 814, 818, 822, 829, 832, 847, 854, 858, 869, - 876, 883, 898, 908, 934, 977, 1010, 1050, 1060, 1064, - 1075, 1078, 1086, 1089, 1093, 1104, 1111, 1122, 1133, 1136, - 1151, 1162, 1183, 1209, 1252, 1281, 1339, 1364, 1386, 1401, - 1411, 1415, 1426, 1430, 1433, 1440, 1448, 1455, 1462, 1477, - 1487, 1495, 1502, 1506, 1509, 1516, 1524, 1531, 1535, 1542, - 1553, 1556, 1578, 1589, 1611, 1625, 1639, 1643, 1654, 1665, - 1672, 1687, 1694, 1705, 1708, 1719, 1730, 1744, 1752, 1759, - 1791, 1795, 1820, 1867, 1886, 1915, 1936, 1943, 1965, 1987, - 2041, 2099, 2161, 2175, 2200, 2211, 2226, 2233, 2244, 2251, - 2266, 2280, 2287, 2298, 2309, 2316, 2331, 2342, 2356, 2378, - 2403, 2418, 2447, 2497, 2544, 2602, 2863, 2895, 2903, 2935, - 2950, 2971, 3004, 3011, 3018, 3029, 3040, 3062, 3087, 3127, - 3152, 3170, 3199, 3243, 3293, 3322, 3340, 3377, 3402, 3427, - 3474, 3518, 3543, 3579, 3601, 3637, 3659, 3706, 3731, 3760, - 3818, 3847, 3869, 3901, 3920, 3952, 4068, 4169, 4220, 4271, - 4524, 4571, 4604, 4632, 4672, 4730, 4777, 4806, 4857, 4904, - 4951, 5002, 5031, 5060, 5107, 5150, 5212, 5266, 5331, 5382, - 5432, 5490, 5544, 5610, 5700, 5762, 5812, 5874, 5972, 6022, - 6091, 6163, 6232, 6305, 6402, 6540, 6685, 6880, 7090, 7271, - 7379, 7452, 7542, 7625, 7687, 7770, 7843, 7911, 7966, 8024, - 8096, 8190, 8252, 8320, 8411, 8501, 8585, 8639, 8751, 8842, - 8918, 8986, 9066, 9127, 9203, 9269, 9345, 9406, 9464, 9536, - 9612, 9667, 9735, 9844, 9931, 10036, 10119, 10199, 10260, 10358, - 10441, 10514, 10666, 10734, 10872, 10951, 11053, 11125, 11223, 11324, - 11516, 11664, 11737, 11816, 11892, 12008, 12120, 12200, 12280, 12392, - 12490, 12576, 12685, 12812, 12917, 13003, 13108, 13210, 13300, 13384, - 13470, 13579, 13673, 13771, 13879, 13999, 14136, 14201, 14368, 14614, - 14759, 14867, 14958, 15030, 15121, 15189, 15280, 15385, 15461, 15555, - 15653, 15768, 15884, 15971, 16069, 16145, 16210, 16279, 16380, 16463, - 16539, 16615, 16688, 16818, 16919, 17017, 18041, 18338, 18523, 18649, - 18790, 18917, 19047, 19167, 19315, 19460, 19601, 19731, 19858, 20068, - 20173, 20318, 20466, 20625, 20741, 20911, 21045, 21201, 21396, 21588, - 21816, 22022, 22305, 22547, 22786, 23072, 23322, 23600, 23879, 24168, - 24433, 24769, 25120, 25511, 25895, 26289, 26792, 27219, 27683, 28077, - 28566, 29094, 29546, 29977, 30491, 30991, 31573, 32105, 32594, 33173, - 33788, 34497, 35181, 35833, 36488, 37255, 37921, 38645, 39275, 39894, - 40505, 41167, 41790, 42431, 43096, 43723, 44385, 45134, 45858, 46607, - 47349, 48091, 48768, 49405, 49955, 50555, 51167, 51985, 52611, 53078, - 53494, 53965, 54435, 54996, 55601, 56125, 56563, 56838, 57244, 57566, - 57967, 58297, 58771, 59093, 59419, 59647, 59886, 60143, 60461, 60693, - 60917, 61170, 61416, 61634, 61891, 62122, 62310, 62455, 62632, 62839, - 63103, 63436, 63639, 63805, 63906, 64015, 64192, 64355, 64475, 64558, - 64663, 64742, 64811, 64865, 64916, 64956, 64981, 65025, 65068, 65115, - 65195, 65314, 65419, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Hi[68] = { - 0, 7, 11, 22, 37, 52, 56, 59, 81, 85, - 89, 96, 115, 130, 137, 152, 170, 181, 193, 200, - 207, 233, 237, 259, 289, 318, 363, 433, 592, 992, - 1607, 3062, 6149, 12206, 25522, 48368, 58223, 61918, 63640, 64584, - 64943, 65098, 65206, 65268, 65294, 65335, 65350, 65372, 65387, 65402, - 65413, 65420, 65428, 65435, 65439, 65450, 65454, 65468, 65472, 65476, - 65483, 65491, 65498, 65505, 65516, 65520, 65528, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Hi[2] = { - 0, 65535 -}; - -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Hi[35] = { - 0, 7, 19, 30, 41, 48, 63, 74, 82, 96, - 122, 152, 215, 330, 701, 2611, 10931, 48106, 61177, 64341, - 65112, 65238, 65309, 65338, 65364, 65379, 65401, 65427, 65453, - 65465, 65476, 65490, 65509, 65528, 65535 -}; - -const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrHi[4] = { - WebRtcIsacfix_kPitchLagCdf1Hi, - WebRtcIsacfix_kPitchLagCdf2Hi, - WebRtcIsacfix_kPitchLagCdf3Hi, - WebRtcIsacfix_kPitchLagCdf4Hi -}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeHi[1] = { - 512 -}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsacfix_kLowerLimitHi[4] = { - -552, -34, 0, -16 -}; - -const WebRtc_Word16 WebRtcIsacfix_kUpperLimitHi[4] = { - -80, 32, 0, 17 -}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsacfix_kInitIndHi[3] = { - 34, 1, 18 -}; - -/* mean values of pitch filter lags */ - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Hi[67] = { - -17482, -16896, -16220, -15929, -15329, -14848, -14336, -13807, -13312, -12800, -12218, -11720, - -11307, -10649, -10396, -9742, -9148, -8668, -8297, -7718, -7155, -6656, -6231, -5600, -5129, - -4610, -4110, -3521, -3040, -2525, -2016, -1506, -995, -477, -5, 469, 991, 1510, 2025, 2526, 3079, - 3555, 4124, 4601, 5131, 5613, 6194, 6671, 7140, 7645, 8207, 8601, 9132, 9728, 10359, 10752, 11302, - 11776, 12288, 12687, 13204, 13759, 14295, 14810, 15360, 15764, 16350 -}; - - -const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Hi[34] = { - -8175, -7659, -7205, -6684, -6215, -5651, -5180, -4566, -4087, -3536, -3096, - -2532, -1990, -1482, -959, -440, 11, 451, 954, 1492, 2020, 2562, 3059, - 3577, 4113, 4618, 5134, 5724, 6060, 6758, 7015, 7716, 8066, 8741 -}; diff --git a/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.h b/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.h deleted file mode 100644 index 9517c29b3..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/pitch_lag_tables.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_lag_tables.h - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_LAG_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_LAG_TABLES_H_ - - -#include "typedefs.h" - - -/********************* Pitch Filter Lag Coefficient Tables ************************/ - -/* tables for use with small pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Lo[127]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Lo[20]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Lo[2]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Lo[10]; - -extern const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrLo[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeLo[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsacfix_kLowerLimitLo[4]; -extern const WebRtc_Word16 WebRtcIsacfix_kUpperLimitLo[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsacfix_kInitIndLo[3]; - -/* mean values of pitch filter lags */ -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Lo[19]; -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Lo[9]; - - - -/* tables for use with medium pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Mid[255]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Mid[36]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Mid[2]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Mid[20]; - -extern const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrMid[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeMid[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsacfix_kLowerLimitMid[4]; -extern const WebRtc_Word16 WebRtcIsacfix_kUpperLimitMid[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsacfix_kInitIndMid[3]; - -/* mean values of pitch filter lags */ -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Mid[35]; -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Mid[19]; - - -/* tables for use with large pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf1Hi[511]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf2Hi[68]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf3Hi[2]; -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagCdf4Hi[35]; - -extern const WebRtc_UWord16 *WebRtcIsacfix_kPitchLagPtrHi[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsacfix_kPitchLagSizeHi[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsacfix_kLowerLimitHi[4]; -extern const WebRtc_Word16 WebRtcIsacfix_kUpperLimitHi[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsacfix_kInitIndHi[3]; - -/* mean values of pitch filter lags */ -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag2Hi[67]; -extern const WebRtc_Word16 WebRtcIsacfix_kMeanLag4Hi[34]; - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_PITCH_LAG_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/settings.h b/modules/audio_coding/codecs/iSAC/fix/source/settings.h deleted file mode 100644 index da88ba25f..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/settings.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * settings.h - * - * Declaration of #defines used in the iSAC codec - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SETTINGS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SETTINGS_H_ - - -/* sampling frequency (Hz) */ -#define FS 16000 -/* 1.5 times Sampling frequency */ -#define FS_1_HALF (WebRtc_UWord32) 24000 -/* Three times Sampling frequency */ -#define FS3 (WebRtc_UWord32) 48000 -/* Eight times Sampling frequency */ -#define FS8 (WebRtc_UWord32) 128000 - -/* number of samples per frame (either 480 (30ms) or 960 (60ms)) */ -#define INITIAL_FRAMESAMPLES 960 - -/* miliseconds */ -#define FRAMESIZE 30 -/* number of samples per frame processed in the encoder (30ms) */ -#define FRAMESAMPLES 480 /* ((FRAMESIZE*FS)/1000) */ -#define FRAMESAMPLES_HALF 240 -/* max number of samples per frame (= 60 ms frame) */ -#define MAX_FRAMESAMPLES 960 -/* number of samples per 10ms frame */ -#define FRAMESAMPLES_10ms 160 /* ((10*FS)/1000) */ -/* Number of samples per 1 ms */ -#define SAMPLES_PER_MSEC 16 -/* number of subframes */ -#define SUBFRAMES 6 -/* length of a subframe */ -#define UPDATE 80 -/* length of half a subframe (low/high band) */ -#define HALF_SUBFRAMELEN 40 /* (UPDATE/2) */ -/* samples of look ahead (in a half-band, so actually half the samples of look ahead @ FS) */ -#define QLOOKAHEAD 24 /* 3 ms */ - -/* order of AR model in spectral entropy coder */ -#define AR_ORDER 6 -#define MAX_ORDER 13 -#define LEVINSON_MAX_ORDER 12 - -/* window length (masking analysis) */ -#define WINLEN 256 -/* order of low-band pole filter used to approximate masking curve */ -#define ORDERLO 12 -/* order of hi-band pole filter used to approximate masking curve */ -#define ORDERHI 6 - -#define KLT_NUM_AVG_GAIN 0 -#define KLT_NUM_AVG_SHAPE 0 -#define KLT_NUM_MODELS 3 -#define LPC_SHAPE_ORDER 18 /* (ORDERLO + ORDERHI) */ - -#define KLT_ORDER_GAIN 12 /* (2 * SUBFRAMES) */ -#define KLT_ORDER_SHAPE 108 /* (LPC_SHAPE_ORDER * SUBFRAMES) */ - - - -/* order for post_filter_bank */ -#define POSTQORDER 3 -/* order for pre-filterbank */ -#define QORDER 3 -/* for decimator */ -#define ALLPASSSECTIONS 2 -/* The number of composite all-pass filter factors */ -#define NUMBEROFCOMPOSITEAPSECTIONS 4 - -/* The number of all-pass filter factors in an upper or lower channel*/ -#define NUMBEROFCHANNELAPSECTIONS 2 - - - -#define DPMIN_Q10 -10240 /* -10.00 in Q10 */ -#define DPMAX_Q10 10240 /* 10.00 in Q10 */ -#define MINBITS_Q10 10240 /* 10.0 in Q10 */ - - -/* array size for byte stream in number of Word16. */ -#define STREAM_MAXW16 300 /* The old maximum size still needed for the decoding */ -#define STREAM_MAXW16_30MS 100 /* 100 Word16 = 200 bytes = 53.4 kbit/s @ 30 ms.framelength */ -#define STREAM_MAXW16_60MS 200 /* 200 Word16 = 400 bytes = 53.4 kbit/s @ 60 ms.framelength */ - - -/* storage size for bit counts */ -//#define BIT_COUNTER_SIZE 30 -/* maximum order of any AR model or filter */ -#define MAX_AR_MODEL_ORDER 12 - -/* Maximum number of iterations allowed to limit payload size */ -#define MAX_PAYLOAD_LIMIT_ITERATION 1 - -/* Bandwidth estimator */ - -#define MIN_ISAC_BW 10000 /* Minimum bandwidth in bits per sec */ -#define MAX_ISAC_BW 32000 /* Maxmum bandwidth in bits per sec */ -#define MIN_ISAC_MD 5 /* Minimum Max Delay in ?? */ -#define MAX_ISAC_MD 25 /* Maxmum Max Delay in ?? */ -#define DELAY_CORRECTION_MAX 717 -#define DELAY_CORRECTION_MED 819 -#define Thld_30_60 18000 -#define Thld_60_30 27000 - -/* assumed header size; we don't know the exact number (header compression may be used) */ -#define HEADER_SIZE 35 /* bytes */ -#define INIT_FRAME_LEN 60 -#define INIT_BN_EST 20000 -#define INIT_BN_EST_Q7 2560000 /* 20 kbps in Q7 */ -#define INIT_REC_BN_EST_Q5 789312 /* INIT_BN_EST + INIT_HDR_RATE in Q5 */ - -/* 8738 in Q18 is ~ 1/30 */ -/* #define INIT_HDR_RATE (((HEADER_SIZE * 8 * 1000) * 8738) >> NUM_BITS_TO_SHIFT (INIT_FRAME_LEN)) */ -#define INIT_HDR_RATE 4666 -/* number of packets in a row for a high rate burst */ -#define BURST_LEN 3 -/* ms, max time between two full bursts */ -#define BURST_INTERVAL 800 -/* number of packets in a row for initial high rate burst */ -#define INIT_BURST_LEN 5 -/* bits/s, rate for the first BURST_LEN packets */ -#define INIT_RATE 10240000 /* INIT_BN_EST in Q9 */ - - -/* For pitch analysis */ -#define PITCH_FRAME_LEN 240 /* (FRAMESAMPLES/2) 30 ms */ -#define PITCH_MAX_LAG 140 /* 57 Hz */ -#define PITCH_MIN_LAG 20 /* 400 Hz */ -#define PITCH_MIN_LAG_Q8 5120 /* 256 * PITCH_MIN_LAG */ -#define OFFSET_Q8 768 /* 256 * 3 */ - -#define PITCH_MAX_GAIN_Q12 1843 /* 0.45 */ -#define PITCH_LAG_SPAN2 65 /* (PITCH_MAX_LAG/2-PITCH_MIN_LAG/2+5) */ -#define PITCH_CORR_LEN2 60 /* 15 ms */ -#define PITCH_CORR_STEP2 60 /* (PITCH_FRAME_LEN/4) */ -#define PITCH_SUBFRAMES 4 -#define PITCH_SUBFRAME_LEN 60 /* (PITCH_FRAME_LEN/PITCH_SUBFRAMES) */ - -/* For pitch filter */ -#define PITCH_BUFFSIZE 190 /* (PITCH_MAX_LAG + 50) Extra 50 for fraction and LP filters */ -#define PITCH_INTBUFFSIZE 430 /* (PITCH_FRAME_LEN+PITCH_BUFFSIZE) */ -#define PITCH_FRACS 8 -#define PITCH_FRACORDER 9 -#define PITCH_DAMPORDER 5 - - -/* Order of high pass filter */ -#define HPORDER 2 - - -/* PLC */ -#define DECAY_RATE 10 /* Q15, 20% of decay every lost frame apllied linearly sample by sample*/ -#define PLC_WAS_USED 1 -#define PLC_NOT_USED 3 -#define RECOVERY_OVERLAP 80 -#define RESAMP_RES 256 -#define RESAMP_RES_BIT 8 - - - -/* Define Error codes */ -/* 6000 General */ -#define ISAC_MEMORY_ALLOCATION_FAILED 6010 -#define ISAC_MODE_MISMATCH 6020 -#define ISAC_DISALLOWED_BOTTLENECK 6030 -#define ISAC_DISALLOWED_FRAME_LENGTH 6040 -/* 6200 Bandwidth estimator */ -#define ISAC_RANGE_ERROR_BW_ESTIMATOR 6240 -/* 6400 Encoder */ -#define ISAC_ENCODER_NOT_INITIATED 6410 -#define ISAC_DISALLOWED_CODING_MODE 6420 -#define ISAC_DISALLOWED_FRAME_MODE_ENCODER 6430 -#define ISAC_DISALLOWED_BITSTREAM_LENGTH 6440 -#define ISAC_PAYLOAD_LARGER_THAN_LIMIT 6450 -/* 6600 Decoder */ -#define ISAC_DECODER_NOT_INITIATED 6610 -#define ISAC_EMPTY_PACKET 6620 -#define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 -#define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 -#define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 -#define ISAC_RANGE_ERROR_DECODE_PITCH_GAIN 6660 -#define ISAC_RANGE_ERROR_DECODE_PITCH_LAG 6670 -#define ISAC_RANGE_ERROR_DECODE_LPC 6680 -#define ISAC_RANGE_ERROR_DECODE_SPECTRUM 6690 -#define ISAC_LENGTH_MISMATCH 6730 -/* 6800 Call setup formats */ -#define ISAC_INCOMPATIBLE_FORMATS 6810 - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SETTINGS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.c b/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.c deleted file mode 100644 index 81b932fd8..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * spectrum_ar_model_tables.c - * - * This file contains tables with AR coefficients, Gain coefficients - * and cosine tables. - * - */ - -#include "spectrum_ar_model_tables.h" -#include "settings.h" - -/********************* AR Coefficient Tables ************************/ - -/* cdf for quantized reflection coefficient 1 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc1Cdf[12] = { - 0, 2, 4, 129, 7707, 57485, 65495, 65527, 65529, 65531, - 65533, 65535 -}; - -/* cdf for quantized reflection coefficient 2 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc2Cdf[12] = { - 0, 2, 4, 7, 531, 25298, 64525, 65526, 65529, 65531, - 65533, 65535 -}; - -/* cdf for quantized reflection coefficient 3 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc3Cdf[12] = { - 0, 2, 4, 6, 620, 22898, 64843, 65527, 65529, 65531, - 65533, 65535 -}; - -/* cdf for quantized reflection coefficient 4 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc4Cdf[12] = { - 0, 2, 4, 6, 35, 10034, 60733, 65506, 65529, 65531, - 65533, 65535 -}; - -/* cdf for quantized reflection coefficient 5 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc5Cdf[12] = { - 0, 2, 4, 6, 36, 7567, 56727, 65385, 65529, 65531, - 65533, 65535 -}; - -/* cdf for quantized reflection coefficient 6 */ -const WebRtc_UWord16 WebRtcIsacfix_kRc6Cdf[12] = { - 0, 2, 4, 6, 14, 6579, 57360, 65409, 65529, 65531, - 65533, 65535 -}; - -/* representation levels for quantized reflection coefficient 1 */ -const WebRtc_Word16 WebRtcIsacfix_kRc1Levels[11] = { - -32104, -29007, -23202, -15496, -9279, -2577, 5934, 17535, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 2 */ -const WebRtc_Word16 WebRtcIsacfix_kRc2Levels[11] = { - -32104, -29503, -23494, -15261, -7309, -1399, 6158, 16381, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 3 */ -const WebRtc_Word16 WebRtcIsacfix_kRc3Levels[11] = { - -32104, -29503, -23157, -15186, -7347, -1359, 5829, 17535, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 4 */ -const WebRtc_Word16 WebRtcIsacfix_kRc4Levels[11] = { - -32104, -29503, -24512, -15362, -6665, -342, 6596, 14585, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 5 */ -const WebRtc_Word16 WebRtcIsacfix_kRc5Levels[11] = { - -32104, -29503, -24512, -15005, -6564, -106, 7123, 14920, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 6 */ -const WebRtc_Word16 WebRtcIsacfix_kRc6Levels[11] = { - -32104, -29503, -24512, -15096, -6656, -37, 7036, 14847, 24512, 29503, 32104 -}; - -/* quantization boundary levels for reflection coefficients */ -const WebRtc_Word16 WebRtcIsacfix_kRcBound[12] = { - -32768, -31441, -27566, -21458, -13612, -4663, - 4663, 13612, 21458, 27566, 31441, 32767 -}; - -/* initial index for AR reflection coefficient quantizer and cdf table search */ -const WebRtc_UWord16 WebRtcIsacfix_kRcInitInd[6] = { - 5, 5, 5, 5, 5, 5 -}; - -/* pointers to AR cdf tables */ -const WebRtc_UWord16 *WebRtcIsacfix_kRcCdfPtr[AR_ORDER] = { - WebRtcIsacfix_kRc1Cdf, - WebRtcIsacfix_kRc2Cdf, - WebRtcIsacfix_kRc3Cdf, - WebRtcIsacfix_kRc4Cdf, - WebRtcIsacfix_kRc5Cdf, - WebRtcIsacfix_kRc6Cdf -}; - -/* pointers to AR representation levels tables */ -const WebRtc_Word16 *WebRtcIsacfix_kRcLevPtr[AR_ORDER] = { - WebRtcIsacfix_kRc1Levels, - WebRtcIsacfix_kRc2Levels, - WebRtcIsacfix_kRc3Levels, - WebRtcIsacfix_kRc4Levels, - WebRtcIsacfix_kRc5Levels, - WebRtcIsacfix_kRc6Levels -}; - - -/******************** GAIN Coefficient Tables ***********************/ - -/* cdf for Gain coefficient */ -const WebRtc_UWord16 WebRtcIsacfix_kGainCdf[19] = { - 0, 2, 4, 6, 8, 10, 12, 14, 16, 1172, - 11119, 29411, 51699, 64445, 65527, 65529, 65531, 65533, 65535 -}; - -/* representation levels for quantized squared Gain coefficient */ -const WebRtc_Word32 WebRtcIsacfix_kGain2Lev[18] = { - 128, 128, 128, 128, 128, 215, 364, 709, 1268, - 1960, 3405, 6078, 11286, 17827, 51918, 134498, 487432, 2048000 -}; - -/* quantization boundary levels for squared Gain coefficient */ -const WebRtc_Word32 WebRtcIsacfix_kGain2Bound[19] = { - 0, 21, 35, 59, 99, 166, 280, 475, 815, 1414, - 2495, 4505, 8397, 16405, 34431, 81359, 240497, 921600, 0x7FFFFFFF -}; - -/* pointers to Gain cdf table */ -const WebRtc_UWord16 *WebRtcIsacfix_kGainPtr[1] = { - WebRtcIsacfix_kGainCdf -}; - -/* gain initial index for gain quantizer and cdf table search */ -const WebRtc_UWord16 WebRtcIsacfix_kGainInitInd[1] = { - 11 -}; - - -/************************* Cosine Tables ****************************/ - -/* cosine table */ -const WebRtc_Word16 WebRtcIsacfix_kCos[6][60] = { - { 512, 512, 511, 510, 508, 507, 505, 502, 499, 496, - 493, 489, 485, 480, 476, 470, 465, 459, 453, 447, - 440, 433, 426, 418, 410, 402, 394, 385, 376, 367, - 357, 348, 338, 327, 317, 306, 295, 284, 273, 262, - 250, 238, 226, 214, 202, 190, 177, 165, 152, 139, - 126, 113, 100, 87, 73, 60, 47, 33, 20, 7 }, - { 512, 510, 508, 503, 498, 491, 483, 473, 462, 450, - 437, 422, 406, 389, 371, 352, 333, 312, 290, 268, - 244, 220, 196, 171, 145, 120, 93, 67, 40, 13, - -13, -40, -67, -93, -120, -145, -171, -196, -220, -244, - -268, -290, -312, -333, -352, -371, -389, -406, -422, -437, - -450, -462, -473, -483, -491, -498, -503, -508, -510, -512 }, - { 512, 508, 502, 493, 480, 465, 447, 426, 402, 376, - 348, 317, 284, 250, 214, 177, 139, 100, 60, 20, - -20, -60, -100, -139, -177, -214, -250, -284, -317, -348, - -376, -402, -426, -447, -465, -480, -493, -502, -508, -512, - -512, -508, -502, -493, -480, -465, -447, -426, -402, -376, - -348, -317, -284, -250, -214, -177, -139, -100, -60, -20 }, - { 511, 506, 495, 478, 456, 429, 398, 362, 322, 279, - 232, 183, 133, 80, 27, -27, -80, -133, -183, -232, - -279, -322, -362, -398, -429, -456, -478, -495, -506, -511, - -511, -506, -495, -478, -456, -429, -398, -362, -322, -279, - -232, -183, -133, -80, -27, 27, 80, 133, 183, 232, - 279, 322, 362, 398, 429, 456, 478, 495, 506, 511 }, - { 511, 502, 485, 459, 426, 385, 338, 284, 226, 165, - 100, 33, -33, -100, -165, -226, -284, -338, -385, -426, - -459, -485, -502, -511, -511, -502, -485, -459, -426, -385, - -338, -284, -226, -165, -100, -33, 33, 100, 165, 226, - 284, 338, 385, 426, 459, 485, 502, 511, 511, 502, - 485, 459, 426, 385, 338, 284, 226, 165, 100, 33 }, - { 510, 498, 473, 437, 389, 333, 268, 196, 120, 40, - -40, -120, -196, -268, -333, -389, -437, -473, -498, -510, - -510, -498, -473, -437, -389, -333, -268, -196, -120, -40, - 40, 120, 196, 268, 333, 389, 437, 473, 498, 510, - 510, 498, 473, 437, 389, 333, 268, 196, 120, 40, - -40, -120, -196, -268, -333, -389, -437, -473, -498, -510 } -}; diff --git a/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.h b/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.h deleted file mode 100644 index b506d0e43..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/spectrum_ar_model_tables.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * spectrum_ar_model_tables.h - * - * This file contains definitions of tables with AR coefficients, - * Gain coefficients and cosine tables. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ - -#include "typedefs.h" -#include "settings.h" - - -/********************* AR Coefficient Tables ************************/ -/* cdf for quantized reflection coefficient 1 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc1Cdf[12]; - -/* cdf for quantized reflection coefficient 2 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc2Cdf[12]; - -/* cdf for quantized reflection coefficient 3 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc3Cdf[12]; - -/* cdf for quantized reflection coefficient 4 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc4Cdf[12]; - -/* cdf for quantized reflection coefficient 5 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc5Cdf[12]; - -/* cdf for quantized reflection coefficient 6 */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRc6Cdf[12]; - -/* representation levels for quantized reflection coefficient 1 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc1Levels[11]; - -/* representation levels for quantized reflection coefficient 2 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc2Levels[11]; - -/* representation levels for quantized reflection coefficient 3 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc3Levels[11]; - -/* representation levels for quantized reflection coefficient 4 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc4Levels[11]; - -/* representation levels for quantized reflection coefficient 5 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc5Levels[11]; - -/* representation levels for quantized reflection coefficient 6 */ -extern const WebRtc_Word16 WebRtcIsacfix_kRc6Levels[11]; - -/* quantization boundary levels for reflection coefficients */ -extern const WebRtc_Word16 WebRtcIsacfix_kRcBound[12]; - -/* initial indices for AR reflection coefficient quantizer and cdf table search */ -extern const WebRtc_UWord16 WebRtcIsacfix_kRcInitInd[AR_ORDER]; - -/* pointers to AR cdf tables */ -extern const WebRtc_UWord16 *WebRtcIsacfix_kRcCdfPtr[AR_ORDER]; - -/* pointers to AR representation levels tables */ -extern const WebRtc_Word16 *WebRtcIsacfix_kRcLevPtr[AR_ORDER]; - - -/******************** GAIN Coefficient Tables ***********************/ -/* cdf for Gain coefficient */ -extern const WebRtc_UWord16 WebRtcIsacfix_kGainCdf[19]; - -/* representation levels for quantized Gain coefficient */ -extern const WebRtc_Word32 WebRtcIsacfix_kGain2Lev[18]; - -/* squared quantization boundary levels for Gain coefficient */ -extern const WebRtc_Word32 WebRtcIsacfix_kGain2Bound[19]; - -/* pointer to Gain cdf table */ -extern const WebRtc_UWord16 *WebRtcIsacfix_kGainPtr[1]; - -/* Gain initial index for gain quantizer and cdf table search */ -extern const WebRtc_UWord16 WebRtcIsacfix_kGainInitInd[1]; - -/************************* Cosine Tables ****************************/ -/* Cosine table */ -extern const WebRtc_Word16 WebRtcIsacfix_kCos[6][60]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/structs.h b/modules/audio_coding/codecs/iSAC/fix/source/structs.h deleted file mode 100644 index b500379cb..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/structs.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * structs.h - * - * This header file contains all the structs used in the ISAC codec - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_STRUCTS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_STRUCTS_H_ - - -#include "typedefs.h" -#include "signal_processing_library.h" -#include "settings.h" - -/* Bitstream struct for decoder */ -typedef struct Bitstreamstruct_dec { - - WebRtc_UWord16 *stream; /* Pointer to bytestream to decode */ - WebRtc_UWord32 W_upper; /* Upper boundary of interval W */ - WebRtc_UWord32 streamval; - WebRtc_UWord16 stream_index; /* Index to the current position in bytestream */ - WebRtc_Word16 full; /* 0 - first byte in memory filled, second empty*/ - /* 1 - both bytes are empty (we just filled the previous memory */ - -} Bitstr_dec; - -/* Bitstream struct for encoder */ -typedef struct Bitstreamstruct_enc { - - WebRtc_UWord16 stream[STREAM_MAXW16_60MS]; /* Vector for adding encoded bytestream */ - WebRtc_UWord32 W_upper; /* Upper boundary of interval W */ - WebRtc_UWord32 streamval; - WebRtc_UWord16 stream_index; /* Index to the current position in bytestream */ - WebRtc_Word16 full; /* 0 - first byte in memory filled, second empty*/ - /* 1 - both bytes are empty (we just filled the previous memory */ - -} Bitstr_enc; - - -typedef struct { - - WebRtc_Word16 DataBufferLoQ0[WINLEN]; - WebRtc_Word16 DataBufferHiQ0[WINLEN]; - - WebRtc_Word32 CorrBufLoQQ[ORDERLO+1]; - WebRtc_Word32 CorrBufHiQQ[ORDERHI+1]; - - WebRtc_Word16 CorrBufLoQdom[ORDERLO+1]; - WebRtc_Word16 CorrBufHiQdom[ORDERHI+1]; - - WebRtc_Word32 PreStateLoGQ15[ORDERLO+1]; - WebRtc_Word32 PreStateHiGQ15[ORDERHI+1]; - - WebRtc_UWord32 OldEnergy; - -} MaskFiltstr_enc; - - - -typedef struct { - - WebRtc_Word16 PostStateLoGQ0[ORDERLO+1]; - WebRtc_Word16 PostStateHiGQ0[ORDERHI+1]; - - WebRtc_UWord32 OldEnergy; - -} MaskFiltstr_dec; - - - - - - - - -typedef struct { - - //state vectors for each of the two analysis filters - - WebRtc_Word32 INSTAT1_fix[2*(QORDER-1)]; - WebRtc_Word32 INSTAT2_fix[2*(QORDER-1)]; - WebRtc_Word16 INLABUF1_fix[QLOOKAHEAD]; - WebRtc_Word16 INLABUF2_fix[QLOOKAHEAD]; - - /* High pass filter */ - WebRtc_Word32 HPstates_fix[HPORDER]; - -} PreFiltBankstr; - - -typedef struct { - - //state vectors for each of the two analysis filters - WebRtc_Word32 STATE_0_LOWER_fix[2*POSTQORDER]; - WebRtc_Word32 STATE_0_UPPER_fix[2*POSTQORDER]; - - /* High pass filter */ - - WebRtc_Word32 HPstates1_fix[HPORDER]; - WebRtc_Word32 HPstates2_fix[HPORDER]; - -} PostFiltBankstr; - -typedef struct { - - - /* data buffer for pitch filter */ - WebRtc_Word16 ubufQQ[PITCH_BUFFSIZE]; - - /* low pass state vector */ - WebRtc_Word16 ystateQQ[PITCH_DAMPORDER]; - - /* old lag and gain */ - WebRtc_Word16 oldlagQ7; - WebRtc_Word16 oldgainQ12; - -} PitchFiltstr; - - - -typedef struct { - - //for inital estimator - WebRtc_Word16 dec_buffer16[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2]; - WebRtc_Word32 decimator_state32[2*ALLPASSSECTIONS+1]; - WebRtc_Word16 inbuf[QLOOKAHEAD]; - - PitchFiltstr PFstr_wght; - PitchFiltstr PFstr; - - -} PitchAnalysisStruct; - - -typedef struct { - /* Parameters used in PLC to avoid re-computation */ - - /* --- residual signals --- */ - WebRtc_Word16 prevPitchInvIn[FRAMESAMPLES/2]; - WebRtc_Word16 prevPitchInvOut[PITCH_MAX_LAG + 10]; // [FRAMESAMPLES/2]; save 90 - WebRtc_Word32 prevHP[PITCH_MAX_LAG + 10]; // [FRAMESAMPLES/2]; save 90 - - - WebRtc_Word16 decayCoeffPriodic; /* how much to supress a sample */ - WebRtc_Word16 decayCoeffNoise; - WebRtc_Word16 used; /* if PLC is used */ - - - WebRtc_Word16 *lastPitchLP; // [FRAMESAMPLES/2]; saved 240; - - - /* --- LPC side info --- */ - WebRtc_Word16 lofilt_coefQ15[ ORDERLO ]; - WebRtc_Word16 hifilt_coefQ15[ ORDERHI ]; - WebRtc_Word32 gain_lo_hiQ17[2]; - - /* --- LTP side info --- */ - WebRtc_Word16 AvgPitchGain_Q12; - WebRtc_Word16 lastPitchGain_Q12; - WebRtc_Word16 lastPitchLag_Q7; - - /* --- Add-overlap in recovery packet --- */ - WebRtc_Word16 overlapLP[ RECOVERY_OVERLAP ]; // [FRAMESAMPLES/2]; saved 160 - - WebRtc_Word16 pitchCycles; - WebRtc_Word16 A; - WebRtc_Word16 B; - WebRtc_Word16 pitchIndex; - WebRtc_Word16 stretchLag; - WebRtc_Word16 *prevPitchLP; // [ FRAMESAMPLES/2 ]; saved 240 - WebRtc_Word16 seed; - - WebRtc_Word16 std; -} PLCstr; - - - -/* Have instance of struct together with other iSAC structs */ -typedef struct { - - WebRtc_Word16 prevFrameSizeMs; /* Previous frame size (in ms) */ - WebRtc_UWord16 prevRtpNumber; /* Previous RTP timestamp from received packet */ - /* (in samples relative beginning) */ - WebRtc_UWord32 prevSendTime; /* Send time for previous packet, from RTP header */ - WebRtc_UWord32 prevArrivalTime; /* Arrival time for previous packet (in ms using timeGetTime()) */ - WebRtc_UWord16 prevRtpRate; /* rate of previous packet, derived from RTP timestamps (in bits/s) */ - WebRtc_UWord32 lastUpdate; /* Time since the last update of the Bottle Neck estimate (in samples) */ - WebRtc_UWord32 lastReduction; /* Time sinse the last reduction (in samples) */ - WebRtc_Word32 countUpdates; /* How many times the estimate was update in the beginning */ - - /* The estimated bottle neck rate from there to here (in bits/s) */ - WebRtc_UWord32 recBw; - WebRtc_UWord32 recBwInv; - WebRtc_UWord32 recBwAvg; - WebRtc_UWord32 recBwAvgQ; - - WebRtc_UWord32 minBwInv; - WebRtc_UWord32 maxBwInv; - - /* The estimated mean absolute jitter value, as seen on this side (in ms) */ - WebRtc_Word32 recJitter; - WebRtc_Word32 recJitterShortTerm; - WebRtc_Word32 recJitterShortTermAbs; - WebRtc_Word32 recMaxDelay; - WebRtc_Word32 recMaxDelayAvgQ; - - - WebRtc_Word16 recHeaderRate; /* (assumed) bitrate for headers (bps) */ - - WebRtc_UWord32 sendBwAvg; /* The estimated bottle neck rate from here to there (in bits/s) */ - WebRtc_Word32 sendMaxDelayAvg; /* The estimated mean absolute jitter value, as seen on the other siee (in ms) */ - - - WebRtc_Word16 countRecPkts; /* number of packets received since last update */ - WebRtc_Word16 highSpeedRec; /* flag for marking that a high speed network has been detected downstream */ - - /* number of consecutive pkts sent during which the bwe estimate has - remained at a value greater than the downstream threshold for determining highspeed network */ - WebRtc_Word16 countHighSpeedRec; - - /* flag indicating bwe should not adjust down immediately for very late pckts */ - WebRtc_Word16 inWaitPeriod; - - /* variable holding the time of the start of a window of time when - bwe should not adjust down immediately for very late pckts */ - WebRtc_UWord32 startWaitPeriod; - - /* number of consecutive pkts sent during which the bwe estimate has - remained at a value greater than the upstream threshold for determining highspeed network */ - WebRtc_Word16 countHighSpeedSent; - - /* flag indicated the desired number of packets over threshold rate have been sent and - bwe will assume the connection is over broadband network */ - WebRtc_Word16 highSpeedSend; - - - - -} BwEstimatorstr; - - -typedef struct { - - /* boolean, flags if previous packet exceeded B.N. */ - WebRtc_Word16 PrevExceed; - /* ms */ - WebRtc_Word16 ExceedAgo; - /* packets left to send in current burst */ - WebRtc_Word16 BurstCounter; - /* packets */ - WebRtc_Word16 InitCounter; - /* ms remaining in buffer when next packet will be sent */ - WebRtc_Word16 StillBuffered; - -} RateModel; - -/* The following strutc is used to store data from encoding, to make it - fast and easy to construct a new bitstream with a different Bandwidth - estimate. All values (except framelength and minBytes) is double size to - handle 60 ms of data. -*/ -typedef struct { - - /* Used to keep track of if it is first or second part of 60 msec packet */ - int startIdx; - - /* Frame length in samples */ - WebRtc_Word16 framelength; - - /* Pitch Gain */ - WebRtc_Word16 pitchGain_index[2]; - - /* Pitch Lag */ - WebRtc_Word32 meanGain[2]; - WebRtc_Word16 pitchIndex[PITCH_SUBFRAMES*2]; - - /* LPC */ - WebRtc_Word32 LPCcoeffs_g[12*2]; /* KLT_ORDER_GAIN = 12 */ - WebRtc_Word16 LPCindex_s[108*2]; /* KLT_ORDER_SHAPE = 108 */ - WebRtc_Word16 LPCindex_g[12*2]; /* KLT_ORDER_GAIN = 12 */ - - /* Encode Spec */ - WebRtc_Word16 fre[FRAMESAMPLES]; - WebRtc_Word16 fim[FRAMESAMPLES]; - WebRtc_Word16 AvgPitchGain[2]; - - /* Used in adaptive mode only */ - int minBytes; - -} ISAC_SaveEncData_t; - -typedef struct { - - Bitstr_enc bitstr_obj; - MaskFiltstr_enc maskfiltstr_obj; - PreFiltBankstr prefiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; - PitchAnalysisStruct pitchanalysisstr_obj; - RateModel rate_data_obj; - - WebRtc_Word16 buffer_index; - WebRtc_Word16 current_framesamples; - - WebRtc_Word16 data_buffer_fix[FRAMESAMPLES]; // the size was MAX_FRAMESAMPLES - - WebRtc_Word16 frame_nb; - WebRtc_Word16 BottleNeck; - WebRtc_Word16 MaxDelay; - WebRtc_Word16 new_framelength; - WebRtc_Word16 s2nr; - WebRtc_UWord16 MaxBits; - - WebRtc_Word16 bitstr_seed; -#ifdef NB_CALLS - PostFiltBankstr interpolatorstr_obj; -#endif - - ISAC_SaveEncData_t *SaveEnc_ptr; - WebRtc_Word16 payloadLimitBytes30; /* Maximum allowed number of bits for a 30 msec packet */ - WebRtc_Word16 payloadLimitBytes60; /* Maximum allowed number of bits for a 30 msec packet */ - WebRtc_Word16 maxPayloadBytes; /* Maximum allowed number of bits for both 30 and 60 msec packet */ - WebRtc_Word16 maxRateInBytes; /* Maximum allowed rate in bytes per 30 msec packet */ - WebRtc_Word16 enforceFrameSize; /* If set iSAC will never change packet size */ - -} ISACFIX_EncInst_t; - - -typedef struct { - - Bitstr_dec bitstr_obj; - MaskFiltstr_dec maskfiltstr_obj; - PostFiltBankstr postfiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; - PLCstr plcstr_obj; /* TS; for packet loss concealment */ - -#ifdef NB_CALLS - PreFiltBankstr decimatorstr_obj; -#endif - -} ISACFIX_DecInst_t; - - - -typedef struct { - - ISACFIX_EncInst_t ISACenc_obj; - ISACFIX_DecInst_t ISACdec_obj; - BwEstimatorstr bwestimator_obj; - WebRtc_Word16 CodingMode; /* 0 = adaptive; 1 = instantaneous */ - WebRtc_Word16 errorcode; - WebRtc_Word16 initflag; /* 0 = nothing initiated; 1 = encoder or decoder */ - /* not initiated; 2 = all initiated */ -} ISACFIX_SubStruct; - - -typedef struct { - WebRtc_Word32 lpcGains[12]; /* 6 lower-band & 6 upper-band we may need to double it for 60*/ - /* */ - WebRtc_UWord32 W_upper; /* Upper boundary of interval W */ - WebRtc_UWord32 streamval; - WebRtc_UWord16 stream_index; /* Index to the current position in bytestream */ - WebRtc_Word16 full; /* 0 - first byte in memory filled, second empty*/ - /* 1 - both bytes are empty (we just filled the previous memory */ - WebRtc_UWord16 beforeLastWord; - WebRtc_UWord16 lastWord; -} transcode_obj; - - -//Bitstr_enc myBitStr; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_FIX_SOURCE_STRUCTS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/fix/source/transform.c b/modules/audio_coding/codecs/iSAC/fix/source/transform.c deleted file mode 100644 index 56ef9f2fe..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/source/transform.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * WebRtcIsacfix_kTransform.c - * - * Transform functions - * - */ - -#include "fft.h" -#include "codec.h" -#include "settings.h" - - -/* Cosine table 1 in Q14 */ -static const WebRtc_Word16 kCosTab1[FRAMESAMPLES/2] = { - 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, - 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, - 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, - 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, - 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, - 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, - 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, - 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, - 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, - 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, - 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, - 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214, - 0, -214, -429, -643, -857, -1072, -1285, -1499, -1713, -1926, - -2139, -2351, -2563, -2775, -2986, -3196, -3406, -3616, -3825, -4033, - -4240, -4447, -4653, -4859, -5063, -5266, -5469, -5671, -5872, -6071, - -6270, -6467, -6664, -6859, -7053, -7246, -7438, -7629, -7818, -8006, - -8192, -8377, -8561, -8743, -8923, -9102, -9280, -9456, -9630, -9803, - -9974, -10143, -10311, -10477, -10641, -10803, -10963, -11121, -11278, -11433, - -11585, -11736, -11885, -12031, -12176, -12318, -12458, -12597, -12733, -12867, - -12998, -13128, -13255, -13380, -13502, -13623, -13741, -13856, -13970, -14081, - -14189, -14295, -14399, -14500, -14598, -14694, -14788, -14879, -14968, -15053, - -15137, -15218, -15296, -15371, -15444, -15515, -15582, -15647, -15709, -15769, - -15826, -15880, -15931, -15980, -16026, -16069, -16110, -16147, -16182, -16214, - -16244, -16270, -16294, -16315, -16333, -16349, -16362, -16371, -16378, -16383 -}; - - -/* Sine table 1 in Q14 */ -static const WebRtc_Word16 kSinTab1[FRAMESAMPLES/2] = { - 0, 214, 429, 643, 857, 1072, 1285, 1499, 1713, 1926, - 2139, 2351, 2563, 2775, 2986, 3196, 3406, 3616, 3825, 4033, - 4240, 4447, 4653, 4859, 5063, 5266, 5469, 5671, 5872, 6071, - 6270, 6467, 6664, 6859, 7053, 7246, 7438, 7629, 7818, 8006, - 8192, 8377, 8561, 8743, 8923, 9102, 9280, 9456, 9630, 9803, - 9974, 10143, 10311, 10477, 10641, 10803, 10963, 11121, 11278, 11433, - 11585, 11736, 11885, 12031, 12176, 12318, 12458, 12597, 12733, 12867, - 12998, 13128, 13255, 13380, 13502, 13623, 13741, 13856, 13970, 14081, - 14189, 14295, 14399, 14500, 14598, 14694, 14788, 14879, 14968, 15053, - 15137, 15218, 15296, 15371, 15444, 15515, 15582, 15647, 15709, 15769, - 15826, 15880, 15931, 15980, 16026, 16069, 16110, 16147, 16182, 16214, - 16244, 16270, 16294, 16315, 16333, 16349, 16362, 16371, 16378, 16383, - 16384, 16383, 16378, 16371, 16362, 16349, 16333, 16315, 16294, 16270, - 16244, 16214, 16182, 16147, 16110, 16069, 16026, 15980, 15931, 15880, - 15826, 15769, 15709, 15647, 15582, 15515, 15444, 15371, 15296, 15218, - 15137, 15053, 14968, 14879, 14788, 14694, 14598, 14500, 14399, 14295, - 14189, 14081, 13970, 13856, 13741, 13623, 13502, 13380, 13255, 13128, - 12998, 12867, 12733, 12597, 12458, 12318, 12176, 12031, 11885, 11736, - 11585, 11433, 11278, 11121, 10963, 10803, 10641, 10477, 10311, 10143, - 9974, 9803, 9630, 9456, 9280, 9102, 8923, 8743, 8561, 8377, - 8192, 8006, 7818, 7629, 7438, 7246, 7053, 6859, 6664, 6467, - 6270, 6071, 5872, 5671, 5469, 5266, 5063, 4859, 4653, 4447, - 4240, 4033, 3825, 3616, 3406, 3196, 2986, 2775, 2563, 2351, - 2139, 1926, 1713, 1499, 1285, 1072, 857, 643, 429, 214 -}; - - -/* Cosine table 2 in Q14 */ -static const WebRtc_Word16 kCosTab2[FRAMESAMPLES/4] = { - 107, -322, 536, -750, 965, -1179, 1392, -1606, 1819, -2032, - 2245, -2457, 2669, -2880, 3091, -3301, 3511, -3720, 3929, -4137, - 4344, -4550, 4756, -4961, 5165, -5368, 5570, -5771, 5971, -6171, - 6369, -6566, 6762, -6957, 7150, -7342, 7534, -7723, 7912, -8099, - 8285, -8469, 8652, -8833, 9013, -9191, 9368, -9543, 9717, -9889, - 10059, -10227, 10394, -10559, 10722, -10883, 11042, -11200, 11356, -11509, - 11661, -11810, 11958, -12104, 12247, -12389, 12528, -12665, 12800, -12933, - 13063, -13192, 13318, -13441, 13563, -13682, 13799, -13913, 14025, -14135, - 14242, -14347, 14449, -14549, 14647, -14741, 14834, -14924, 15011, -15095, - 15178, -15257, 15334, -15408, 15480, -15549, 15615, -15679, 15739, -15798, - 15853, -15906, 15956, -16003, 16048, -16090, 16129, -16165, 16199, -16229, - 16257, -16283, 16305, -16325, 16342, -16356, 16367, -16375, 16381, -16384 -}; - - -/* Sine table 2 in Q14 */ -static const WebRtc_Word16 kSinTab2[FRAMESAMPLES/4] = { - 16384, -16381, 16375, -16367, 16356, -16342, 16325, -16305, 16283, -16257, - 16229, -16199, 16165, -16129, 16090, -16048, 16003, -15956, 15906, -15853, - 15798, -15739, 15679, -15615, 15549, -15480, 15408, -15334, 15257, -15178, - 15095, -15011, 14924, -14834, 14741, -14647, 14549, -14449, 14347, -14242, - 14135, -14025, 13913, -13799, 13682, -13563, 13441, -13318, 13192, -13063, - 12933, -12800, 12665, -12528, 12389, -12247, 12104, -11958, 11810, -11661, - 11509, -11356, 11200, -11042, 10883, -10722, 10559, -10394, 10227, -10059, - 9889, -9717, 9543, -9368, 9191, -9013, 8833, -8652, 8469, -8285, - 8099, -7912, 7723, -7534, 7342, -7150, 6957, -6762, 6566, -6369, - 6171, -5971, 5771, -5570, 5368, -5165, 4961, -4756, 4550, -4344, - 4137, -3929, 3720, -3511, 3301, -3091, 2880, -2669, 2457, -2245, - 2032, -1819, 1606, -1392, 1179, -965, 750, -536, 322, -107 -}; - - - -void WebRtcIsacfix_Time2Spec(WebRtc_Word16 *inre1Q9, - WebRtc_Word16 *inre2Q9, - WebRtc_Word16 *outreQ7, - WebRtc_Word16 *outimQ7) -{ - - int k; - WebRtc_Word32 tmpreQ16[FRAMESAMPLES/2], tmpimQ16[FRAMESAMPLES/2]; - WebRtc_Word16 tmp1rQ14, tmp1iQ14; - WebRtc_Word32 xrQ16, xiQ16, yrQ16, yiQ16; - WebRtc_Word32 v1Q16, v2Q16; - WebRtc_Word16 factQ19, sh; - - /* Multiply with complex exponentials and combine into one complex vector */ - factQ19 = 16921; // 0.5/sqrt(240) in Q19 is round(.5/sqrt(240)*(2^19)) = 16921 - for (k = 0; k < FRAMESAMPLES/2; k++) { - tmp1rQ14 = kCosTab1[k]; - tmp1iQ14 = kSinTab1[k]; - xrQ16 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(tmp1rQ14, inre1Q9[k]) + WEBRTC_SPL_MUL_16_16(tmp1iQ14, inre2Q9[k]), 7); - xiQ16 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(tmp1rQ14, inre2Q9[k]) - WEBRTC_SPL_MUL_16_16(tmp1iQ14, inre1Q9[k]), 7); - tmpreQ16[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xrQ16)+4, 3); // (Q16*Q19>>16)>>3 = Q16 - tmpimQ16[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_32_RSFT16(factQ19, xiQ16)+4, 3); // (Q16*Q19>>16)>>3 = Q16 - } - - - xrQ16 = WebRtcSpl_MaxAbsValueW32(tmpreQ16, FRAMESAMPLES/2); - yrQ16 = WebRtcSpl_MaxAbsValueW32(tmpimQ16, FRAMESAMPLES/2); - if (yrQ16>xrQ16) { - xrQ16 = yrQ16; - } - - sh = WebRtcSpl_NormW32(xrQ16); - sh = sh-24; //if sh becomes >=0, then we should shift sh steps to the left, and the domain will become Q(16+sh) - //if sh becomes <0, then we should shift -sh steps to the right, and the domain will become Q(16+sh) - - //"Fastest" vectors - if (sh>=0) { - for (k=0; k=0) { - for (k=0; k Q16 - tmpimQ16[k] = WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)inre2Q9[k], sh); //Q(16+sh) -> Q16 - } - } else { - for (k=0; k Q16 - tmpimQ16[k] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)inre2Q9[k], -sh); //Q(16+sh) -> Q16 - } - } - - - /* Use symmetry to separate into two complex vectors and center frames in time around zero */ - for (k = 0; k < FRAMESAMPLES/4; k++) { - xrQ16 = tmpreQ16[k] + tmpreQ16[FRAMESAMPLES/2 - 1 - k]; - yiQ16 = -tmpreQ16[k] + tmpreQ16[FRAMESAMPLES/2 - 1 - k]; - xiQ16 = tmpimQ16[k] - tmpimQ16[FRAMESAMPLES/2 - 1 - k]; - yrQ16 = tmpimQ16[k] + tmpimQ16[FRAMESAMPLES/2 - 1 - k]; - tmp1rQ14 = kCosTab2[k]; - tmp1iQ14 = kSinTab2[k]; - v1Q16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, xrQ16) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, xiQ16); - v2Q16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, xrQ16) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, xiQ16); - outreQ7[k] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(v1Q16, 9); - outimQ7[k] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(v2Q16, 9); - v1Q16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, yrQ16) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, yiQ16); - v2Q16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, yrQ16) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, yiQ16); - outreQ7[FRAMESAMPLES/2 - 1 - k] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(v1Q16, 9); //CalcLrIntQ(v1Q16, 9); - outimQ7[FRAMESAMPLES/2 - 1 - k] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(v2Q16, 9); //CalcLrIntQ(v2Q16, 9); - - } -} - - -void WebRtcIsacfix_Spec2Time(WebRtc_Word16 *inreQ7, WebRtc_Word16 *inimQ7, WebRtc_Word32 *outre1Q16, WebRtc_Word32 *outre2Q16) -{ - - int k; - WebRtc_Word16 tmp1rQ14, tmp1iQ14; - WebRtc_Word32 xrQ16, xiQ16, yrQ16, yiQ16; - WebRtc_Word32 tmpInRe, tmpInIm, tmpInRe2, tmpInIm2; - WebRtc_Word16 factQ11; - WebRtc_Word16 sh; - - for (k = 0; k < FRAMESAMPLES/4; k++) { - /* Move zero in time to beginning of frames */ - tmp1rQ14 = kCosTab2[k]; - tmp1iQ14 = kSinTab2[k]; - - tmpInRe = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) inreQ7[k], 9); // Q7 -> Q16 - tmpInIm = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) inimQ7[k], 9); // Q7 -> Q16 - tmpInRe2 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) inreQ7[FRAMESAMPLES/2 - 1 - k], 9); // Q7 -> Q16 - tmpInIm2 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32) inimQ7[FRAMESAMPLES/2 - 1 - k], 9); // Q7 -> Q16 - - xrQ16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, tmpInRe) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, tmpInIm); - xiQ16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, tmpInIm) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, tmpInRe); - yrQ16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, tmpInIm2) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, tmpInRe2); - yiQ16 = -WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, tmpInRe2) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, tmpInIm2); - - /* Combine into one vector, z = x + j * y */ - outre1Q16[k] = xrQ16 - yiQ16; - outre1Q16[FRAMESAMPLES/2 - 1 - k] = xrQ16 + yiQ16; - outre2Q16[k] = xiQ16 + yrQ16; - outre2Q16[FRAMESAMPLES/2 - 1 - k] = -xiQ16 + yrQ16; - } - - /* Get IDFT */ - tmpInRe = WebRtcSpl_MaxAbsValueW32(outre1Q16, 240); - tmpInIm = WebRtcSpl_MaxAbsValueW32(outre2Q16, 240); - if (tmpInIm>tmpInRe) { - tmpInRe = tmpInIm; - } - - sh = WebRtcSpl_NormW32(tmpInRe); - sh = sh-24; //if sh becomes >=0, then we should shift sh steps to the left, and the domain will become Q(16+sh) - //if sh becomes <0, then we should shift -sh steps to the right, and the domain will become Q(16+sh) - - //"Fastest" vectors - if (sh>=0) { - for (k=0; k<240; k++) { - inreQ7[k] = (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W32(outre1Q16[k], sh); //Q(16+sh) - inimQ7[k] = (WebRtc_Word16) WEBRTC_SPL_LSHIFT_W32(outre2Q16[k], sh); //Q(16+sh) - } - } else { - WebRtc_Word32 round = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)1, -sh-1); - for (k=0; k<240; k++) { - inreQ7[k] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(outre1Q16[k]+round, -sh); //Q(16+sh) - inimQ7[k] = (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(outre2Q16[k]+round, -sh); //Q(16+sh) - } - } - - WebRtcIsacfix_FftRadix16Fastest(inreQ7, inimQ7, 1); // real call - - //"Fastest" vectors - if (sh>=0) { - for (k=0; k<240; k++) { - outre1Q16[k] = WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)inreQ7[k], sh); //Q(16+sh) -> Q16 - outre2Q16[k] = WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)inimQ7[k], sh); //Q(16+sh) -> Q16 - } - } else { - for (k=0; k<240; k++) { - outre1Q16[k] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)inreQ7[k], -sh); //Q(16+sh) -> Q16 - outre2Q16[k] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)inimQ7[k], -sh); //Q(16+sh) -> Q16 - } - } - - /* Divide through by the normalizing constant: */ - /* scale all values with 1/240, i.e. with 273 in Q16 */ - /* 273/65536 ~= 0.0041656 */ - /* 1/240 ~= 0.0041666 */ - for (k=0; k<240; k++) { - outre1Q16[k] = WEBRTC_SPL_MUL_16_32_RSFT16(273, outre1Q16[k]); - outre2Q16[k] = WEBRTC_SPL_MUL_16_32_RSFT16(273, outre2Q16[k]); - } - - /* Demodulate and separate */ - factQ11 = 31727; // sqrt(240) in Q11 is round(15.49193338482967*2048) = 31727 - for (k = 0; k < FRAMESAMPLES/2; k++) { - tmp1rQ14 = kCosTab1[k]; - tmp1iQ14 = kSinTab1[k]; - xrQ16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, outre1Q16[k]) - WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, outre2Q16[k]); - xiQ16 = WEBRTC_SPL_MUL_16_32_RSFT14(tmp1rQ14, outre2Q16[k]) + WEBRTC_SPL_MUL_16_32_RSFT14(tmp1iQ14, outre1Q16[k]); - xrQ16 = WEBRTC_SPL_MUL_16_32_RSFT11(factQ11, xrQ16); - xiQ16 = WEBRTC_SPL_MUL_16_32_RSFT11(factQ11, xiQ16); - outre2Q16[k] = xiQ16; - outre1Q16[k] = xrQ16; - } -} diff --git a/modules/audio_coding/codecs/iSAC/fix/test/ISACHist.cpp b/modules/audio_coding/codecs/iSAC/fix/test/ISACHist.cpp deleted file mode 100644 index 753acd7e6..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/ISACHist.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include - -//#include "isac_codec.h" -//#include "isac_structs.h" -#include "isacfix.h" - - -#define NUM_CODECS 1 - -int main(int argc, char* argv[]) -{ - FILE *inFileList; - FILE *audioFile; - FILE *outFile; - char audioFileName[501]; - short audioBuff[960]; - short encoded[600]; - short startAudio; - short encodedLen; - ISACFIX_MainStruct *isac_struct; - unsigned long int hist[601]; - - // reset the histogram - for(short n=0; n < 601; n++) - { - hist[n] = 0; - } - - - inFileList = fopen(argv[1], "r"); - if(inFileList == NULL) - { - printf("Could not open the input file.\n"); - getchar(); - exit(-1); - } - outFile = fopen(argv[2], "w"); - if(outFile == NULL) - { - printf("Could not open the histogram file.\n"); - getchar(); - exit(-1); - } - - short frameSizeMsec = 30; - if(argc > 3) - { - frameSizeMsec = atoi(argv[3]); - } - - short audioOffset = 0; - if(argc > 4) - { - audioOffset = atoi(argv[4]); - } - int ok; - ok = WebRtcIsacfix_Create(&isac_struct); - // instantaneous mode - ok |= WebRtcIsacfix_EncoderInit(isac_struct, 1); - // is not used but initialize - ok |= WebRtcIsacfix_DecoderInit(isac_struct); - ok |= WebRtcIsacfix_Control(isac_struct, 32000, frameSizeMsec); - - if(ok != 0) - { - printf("\nProblem in seting up iSAC\n"); - exit(-1); - } - - while( fgets(audioFileName, 500, inFileList) != NULL ) - { - // remove trailing white-spaces and any Cntrl character - if(strlen(audioFileName) == 0) - { - continue; - } - short n = strlen(audioFileName) - 1; - while(isspace(audioFileName[n]) || iscntrl(audioFileName[n])) - { - audioFileName[n] = '\0'; - n--; - if(n < 0) - { - break; - } - } - - // remove leading spaces - if(strlen(audioFileName) == 0) - { - continue; - } - n = 0; - while((isspace(audioFileName[n]) || iscntrl(audioFileName[n])) && - (audioFileName[n] != '\0')) - { - n++; - } - memmove(audioFileName, &audioFileName[n], 500 - n); - if(strlen(audioFileName) == 0) - { - continue; - } - audioFile = fopen(audioFileName, "rb"); - if(audioFile == NULL) - { - printf("\nCannot open %s!!!!!\n", audioFileName); - exit(0); - } - - if(audioOffset > 0) - { - fseek(audioFile, (audioOffset<<1), SEEK_SET); - } - - while(fread(audioBuff, sizeof(short), (480*frameSizeMsec/30), audioFile) >= (480*frameSizeMsec/30)) - { - startAudio = 0; - do - { - encodedLen = WebRtcIsacfix_Encode(isac_struct, - &audioBuff[startAudio], encoded); - startAudio += 160; - } while(encodedLen == 0); - - if(encodedLen < 0) - { - printf("\nEncoding Error!!!\n"); - exit(0); - } - hist[encodedLen]++; - } - fclose(audioFile); - } - fclose(inFileList); - unsigned long totalFrames = 0; - for(short n=0; n < 601; n++) - { - totalFrames += hist[n]; - fprintf(outFile, "%10lu\n", hist[n]); - } - fclose(outFile); - - short topTenCntr = 0; - printf("\nTotal number of Frames %lu\n\n", totalFrames); - printf("Payload Len # occurences\n"); - printf("=========== ============\n"); - - for(short n = 600; (n >= 0) && (topTenCntr < 10); n--) - { - if(hist[n] > 0) - { - topTenCntr++; - printf(" %3d %3d\n", n, hist[n]); - } - } - WebRtcIsacfix_Free(isac_struct); - return 0; -} - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/Isac_test.cpp b/modules/audio_coding/codecs/iSAC/fix/test/Isac_test.cpp deleted file mode 100644 index 2791db451..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/Isac_test.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/****************************************************************** - Stand Alone test application for ISACFIX and ISAC LC - -******************************************************************/ - -#include -#include -#include -#include "typedefs.h" - -#include "isacfix.h" -ISACFIX_MainStruct *ISACfix_inst; - -#define FS 16000 - - -typedef struct { - WebRtc_UWord32 arrival_time; /* samples */ - WebRtc_UWord32 sample_count; /* samples */ - WebRtc_UWord16 rtp_number; -} BottleNeckModel; - -void get_arrival_time(int current_framesamples, /* samples */ - int packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel *BN_data) -{ - const int HeaderSize = 35; - int HeaderRate; - - HeaderRate = HeaderSize * 8 * FS / current_framesamples; /* bits/s */ - - /* everything in samples */ - BN_data->sample_count = BN_data->sample_count + current_framesamples; - - BN_data->arrival_time += ((packet_size + HeaderSize) * 8 * FS) / (bottleneck + HeaderRate); - - if (BN_data->arrival_time < BN_data->sample_count) - BN_data->arrival_time = BN_data->sample_count; - - BN_data->rtp_number++; -} - -/* -#ifdef __cplusplus -extern "C" { -#endif -*/ -int main(int argc, char* argv[]){ - - /* Parameters */ - FILE *pInFile, *pOutFile, *pChcFile; - WebRtc_Word8 inFile[40]; - WebRtc_Word8 outFile[40]; - WebRtc_Word8 chcFile[40]; - WebRtc_Word8 codec[10]; - WebRtc_Word16 bitrt, spType, size; - WebRtc_UWord16 frameLen; - WebRtc_Word16 sigOut[1000], sigIn[1000]; - WebRtc_UWord16 bitStream[500]; /* double to 32 kbps for 60 ms */ - - WebRtc_Word16 chc, ok; - int noOfCalls, cdlen; - WebRtc_Word16 noOfLostFrames; - int err, errtype; - - BottleNeckModel BN_data; - - int totalbits =0; - int totalsmpls =0; - - /*End Parameters*/ - - - if ((argc==6)||(argc==7) ){ - - strcpy(codec,argv[5]); - - if(argc==7){ - if (!_stricmp("isac",codec)){ - bitrt = atoi(argv[6]); - if ( (bitrt<10000)&&(bitrt>32000)){ - printf("Error: Supported bit rate in the range 10000-32000 bps!\n"); - exit(-1); - } - - }else{ - printf("Error: Codec not recognized. Check spelling!\n"); - exit(-1); - } - - } else { - printf("Error: Codec not recognized. Check spelling!\n"); - exit(-1); - } - } else { - printf("Error: Wrong number of input parameter!\n\n"); - exit(-1); - } - - frameLen = atoi(argv[4]); - strcpy(chcFile,argv[3]); - strcpy(outFile,argv[2]); - strcpy(inFile,argv[1]); - - /* Open file streams */ - if( (pInFile = fopen(inFile,"rb")) == NULL ) { - printf( "Error: Did not find input file!\n" ); - exit(-1); - } - strcat(outFile,"_"); - strcat(outFile, argv[4]); - strcat(outFile,"_"); - strcat(outFile, codec); - - if (argc==7){ - strcat(outFile,"_"); - strcat(outFile, argv[6]); - } - if (_stricmp("none", chcFile)){ - strcat(outFile,"_"); - strcat(outFile, "plc"); - } - - strcat(outFile, ".otp"); - - if (_stricmp("none", chcFile)){ - if( (pChcFile = fopen(chcFile,"rb")) == NULL ) { - printf( "Error: Did not find channel file!\n" ); - exit(-1); - } - } - /******************************************************************/ - if (!_stricmp("isac", codec)){ /* ISAC */ - if ((frameLen!=480)&&(frameLen!=960)) { - printf("Error: ISAC only supports 480 and 960 samples per frame (not %d)\n", frameLen); - exit(-1); - } - if( (pOutFile = fopen(outFile,"wb")) == NULL ) { - printf( "Could not open output file!\n" ); - exit(-1); - } - ok=WebRtcIsacfix_Create(&ISACfix_inst); - if (ok!=0) { - printf("Couldn't allocate memory for iSAC fix instance\n"); - exit(-1); - } - - BN_data.arrival_time = 0; - BN_data.sample_count = 0; - BN_data.rtp_number = 0; - - WebRtcIsacfix_EncoderInit(ISACfix_inst,1); - WebRtcIsacfix_DecoderInit(ISACfix_inst); - err = WebRtcIsacfix_Control(ISACfix_inst, bitrt, (frameLen>>4)); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACfix_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - /* loop over frame */ - while (fread(sigIn,sizeof(WebRtc_Word16),frameLen,pInFile) == frameLen) { - - noOfCalls=0; - cdlen=0; - while (cdlen<=0) { - cdlen=WebRtcIsacfix_Encode(ISACfix_inst,&sigIn[noOfCalls*160],(WebRtc_Word16*)bitStream); - if(cdlen==-1){ - errtype=WebRtcIsacfix_GetErrorCode(ISACfix_inst); - printf("\n\nError in encoder: %d.\n\n", errtype); - exit(-1); - } - noOfCalls++; - } - - - if(_stricmp("none", chcFile)){ - if (fread(&chc,sizeof(WebRtc_Word16),1,pChcFile)!=1) /* packet may be lost */ - break; - } else { - chc = 1; /* packets never lost */ - } - - /* simulate packet handling through NetEq and the modem */ - get_arrival_time(frameLen, cdlen, bitrt, &BN_data); - - if (chc){ /* decode */ - - err = WebRtcIsacfix_UpdateBwEstimate1(ISACfix_inst, - bitStream, - cdlen, - BN_data.rtp_number, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACfix_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - size = WebRtcIsacfix_Decode(ISACfix_inst, bitStream, cdlen, sigOut, &spType); - if(size<=0){ - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACfix_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - exit(-1); - } - } else { /* PLC */ - if (frameLen == 480){ - noOfLostFrames = 1; - } else{ - noOfLostFrames = 2; - } - size = WebRtcIsacfix_DecodePlc(ISACfix_inst, sigOut, noOfLostFrames ); - if(size<=0){ - errtype=WebRtcIsacfix_GetErrorCode(ISACfix_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - exit(-1); - } - } - - /* Write decoded speech to file */ - fwrite(sigOut,sizeof(short),size,pOutFile); - - totalbits += 8 * cdlen; - totalsmpls += size; - - } - /******************************************************************/ - } - -// printf("\n\ntotal bits = %d bits", totalbits); - printf("\nmeasured average bitrate = %0.3f kbits/s", (double)totalbits * 16 / totalsmpls); - printf("\n"); - - - fclose(pInFile); - fclose(pOutFile); - if (_stricmp("none", chcFile)){ - fclose(pChcFile); - } - - if (!_stricmp("isac", codec)){ - WebRtcIsacfix_Free(ISACfix_inst); - } - - return 0; - -} diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/ChannelFiles.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/ChannelFiles.txt deleted file mode 100644 index 05f741014..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/ChannelFiles.txt +++ /dev/null @@ -1,3 +0,0 @@ -bottlenecks.txt -lowrates.txt -tworates.txt diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFiles.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFiles.txt deleted file mode 100644 index f26b7afb6..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFiles.txt +++ /dev/null @@ -1,31 +0,0 @@ -DTMF_16kHz_long.pcm -DTMF_16kHz_short.pcm -F00.INP -F01.INP -F02.INP -F03.INP -F04.INP -F05.INP -F06.INP -longtest.pcm -ltest_speech_clean.pcm -ltest_music.pcm -ltest_speech_noisy.pcm -misc2.pcm -purenb.pcm -sawsweep_380_60.pcm -sinesweep.pcm -sinesweep_half.pcm -speechmusic.pcm -speechmusic_nb.pcm -speechoffice0dB.pcm -speech_and_misc_NB.pcm -speech_and_misc_WB.pcm -testM4.pcm -testM4D_rev.pcm -testM4D.pcm -testfile.pcm -tone_cisco.pcm -tone_cisco_long.pcm -wb_contspeech.pcm -wb_speech_office25db.pcm \ No newline at end of file diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFilesFew.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFilesFew.txt deleted file mode 100644 index 08bbde30d..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/InputFilesFew.txt +++ /dev/null @@ -1,6 +0,0 @@ -DTMF_16kHz_short.pcm -ltest_speech_noisy.pcm -misc2.pcm -sinesweep.pcm -speechmusic.pcm -tone_cisco.pcm diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/ListOfTestCases.xls b/modules/audio_coding/codecs/iSAC/fix/test/QA/ListOfTestCases.xls deleted file mode 100644 index f0889ef4eddb53b5295e4248059e3e345223f25b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152064 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 diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSAC.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSAC.txt deleted file mode 100644 index 96b87c066..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSAC.txt +++ /dev/null @@ -1,481 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -diff ../dataqa350/i30_1DTMF_16kHz_long.pcm ../dataqa351/i30_1DTMF_16kHz_long.pcm -diff ../dataqa350/i60_1DTMF_16kHz_long.pcm ../dataqa351/i60_1DTMF_16kHz_long.pcm -diff ../dataqa350/i30_2DTMF_16kHz_long.pcm ../dataqa351/i30_2DTMF_16kHz_long.pcm -diff ../dataqa350/i60_2DTMF_16kHz_long.pcm ../dataqa351/i60_2DTMF_16kHz_long.pcm -diff ../dataqa350/i30_3DTMF_16kHz_long.pcm ../dataqa351/i30_3DTMF_16kHz_long.pcm -diff ../dataqa350/i60_3DTMF_16kHz_long.pcm ../dataqa351/i60_3DTMF_16kHz_long.pcm -diff ../dataqa350/i30_4DTMF_16kHz_long.pcm ../dataqa351/i30_4DTMF_16kHz_long.pcm -diff ../dataqa350/i60_4DTMF_16kHz_long.pcm ../dataqa351/i60_4DTMF_16kHz_long.pcm -diff ../dataqa350/i30_5DTMF_16kHz_long.pcm ../dataqa351/i30_5DTMF_16kHz_long.pcm -diff ../dataqa350/i60_5DTMF_16kHz_long.pcm ../dataqa351/i60_5DTMF_16kHz_long.pcm -diff ../dataqa350/i30_6DTMF_16kHz_long.pcm ../dataqa351/i30_6DTMF_16kHz_long.pcm -diff ../dataqa350/i60_6DTMF_16kHz_long.pcm ../dataqa351/i60_6DTMF_16kHz_long.pcm -diff ../dataqa350/a1DTMF_16kHz_long.pcm ../dataqa351/a1DTMF_16kHz_long.pcm -diff ../dataqa350/a2DTMF_16kHz_long.pcm ../dataqa351/a2DTMF_16kHz_long.pcm -diff ../dataqa350/a3DTMF_16kHz_long.pcm ../dataqa351/a3DTMF_16kHz_long.pcm -diff ../dataqa350/i30_7DTMF_16kHz_short.pcm ../dataqa351/i30_7DTMF_16kHz_short.pcm -diff ../dataqa350/i60_7DTMF_16kHz_short.pcm ../dataqa351/i60_7DTMF_16kHz_short.pcm -diff ../dataqa350/i30_8DTMF_16kHz_short.pcm ../dataqa351/i30_8DTMF_16kHz_short.pcm -diff ../dataqa350/i60_8DTMF_16kHz_short.pcm ../dataqa351/i60_8DTMF_16kHz_short.pcm -diff ../dataqa350/i30_9DTMF_16kHz_short.pcm ../dataqa351/i30_9DTMF_16kHz_short.pcm -diff ../dataqa350/i60_9DTMF_16kHz_short.pcm ../dataqa351/i60_9DTMF_16kHz_short.pcm -diff ../dataqa350/i30_10DTMF_16kHz_short.pcm ../dataqa351/i30_10DTMF_16kHz_short.pcm -diff ../dataqa350/i60_10DTMF_16kHz_short.pcm ../dataqa351/i60_10DTMF_16kHz_short.pcm -diff ../dataqa350/i30_11DTMF_16kHz_short.pcm ../dataqa351/i30_11DTMF_16kHz_short.pcm -diff ../dataqa350/i60_11DTMF_16kHz_short.pcm ../dataqa351/i60_11DTMF_16kHz_short.pcm -diff ../dataqa350/i30_12DTMF_16kHz_short.pcm ../dataqa351/i30_12DTMF_16kHz_short.pcm -diff ../dataqa350/i60_12DTMF_16kHz_short.pcm ../dataqa351/i60_12DTMF_16kHz_short.pcm -diff ../dataqa350/a4DTMF_16kHz_short.pcm ../dataqa351/a4DTMF_16kHz_short.pcm -diff ../dataqa350/a5DTMF_16kHz_short.pcm ../dataqa351/a5DTMF_16kHz_short.pcm -diff ../dataqa350/a6DTMF_16kHz_short.pcm ../dataqa351/a6DTMF_16kHz_short.pcm -diff ../dataqa350/i30_13F00.INP ../dataqa350/i30_13F00.INP -diff ../dataqa350/i60_13F00.INP ../dataqa350/i60_13F00.INP -diff ../dataqa350/i30_14F00.INP ../dataqa350/i30_14F00.INP -diff ../dataqa350/i60_14F00.INP ../dataqa350/i60_14F00.INP -diff ../dataqa350/i30_15F00.INP ../dataqa350/i30_15F00.INP -diff ../dataqa350/i60_15F00.INP ../dataqa350/i60_15F00.INP -diff ../dataqa350/i30_16F00.INP ../dataqa350/i30_16F00.INP -diff ../dataqa350/i60_16F00.INP ../dataqa350/i60_16F00.INP -diff ../dataqa350/i30_17F00.INP ../dataqa350/i30_17F00.INP -diff ../dataqa350/i60_17F00.INP ../dataqa350/i60_17F00.INP -diff ../dataqa350/i30_18F00.INP ../dataqa350/i30_18F00.INP -diff ../dataqa350/i60_18F00.INP ../dataqa350/i60_18F00.INP -diff ../dataqa350/a7F00.INP ../dataqa350/a7F00.INP -diff ../dataqa350/a8F00.INP ../dataqa350/a8F00.INP -diff ../dataqa350/a9F00.INP ../dataqa350/a9F00.INP -diff ../dataqa350/i30_19F01.INP ../dataqa350/i30_19F01.INP -diff ../dataqa350/i60_19F01.INP ../dataqa350/i60_19F01.INP -diff ../dataqa350/i30_20F01.INP ../dataqa350/i30_20F01.INP -diff ../dataqa350/i60_20F01.INP ../dataqa350/i60_20F01.INP -diff ../dataqa350/i30_21F01.INP ../dataqa350/i30_21F01.INP -diff ../dataqa350/i60_21F01.INP ../dataqa350/i60_21F01.INP -diff ../dataqa350/i30_22F01.INP ../dataqa350/i30_22F01.INP -diff ../dataqa350/i60_22F01.INP ../dataqa350/i60_22F01.INP -diff ../dataqa350/i30_23F01.INP ../dataqa350/i30_23F01.INP -diff ../dataqa350/i60_23F01.INP ../dataqa350/i60_23F01.INP -diff ../dataqa350/i30_24F01.INP ../dataqa350/i30_24F01.INP -diff ../dataqa350/i60_24F01.INP ../dataqa350/i60_24F01.INP -diff ../dataqa350/a10F01.INP ../dataqa350/a10F01.INP -diff ../dataqa350/a11F01.INP ../dataqa350/a11F01.INP -diff ../dataqa350/a12F01.INP ../dataqa350/a12F01.INP -diff ../dataqa350/i30_25F02.INP ../dataqa350/i30_25F02.INP -diff ../dataqa350/i60_25F02.INP ../dataqa350/i60_25F02.INP -diff ../dataqa350/i30_26F02.INP ../dataqa350/i30_26F02.INP -diff ../dataqa350/i60_26F02.INP ../dataqa350/i60_26F02.INP -diff ../dataqa350/i30_27F02.INP ../dataqa350/i30_27F02.INP -diff ../dataqa350/i60_27F02.INP ../dataqa350/i60_27F02.INP -diff ../dataqa350/i30_28F02.INP ../dataqa350/i30_28F02.INP -diff ../dataqa350/i60_28F02.INP ../dataqa350/i60_28F02.INP -diff ../dataqa350/i30_29F02.INP ../dataqa350/i30_29F02.INP -diff ../dataqa350/i60_29F02.INP ../dataqa350/i60_29F02.INP -diff ../dataqa350/i30_30F02.INP ../dataqa350/i30_30F02.INP -diff ../dataqa350/i60_30F02.INP ../dataqa350/i60_30F02.INP -diff ../dataqa350/a13F02.INP ../dataqa350/a13F02.INP -diff ../dataqa350/a14F02.INP ../dataqa350/a14F02.INP -diff ../dataqa350/a15F02.INP ../dataqa350/a15F02.INP -diff ../dataqa350/i30_31F03.INP ../dataqa350/i30_31F03.INP -diff ../dataqa350/i60_31F03.INP ../dataqa350/i60_31F03.INP -diff ../dataqa350/i30_32F03.INP ../dataqa350/i30_32F03.INP -diff ../dataqa350/i60_32F03.INP ../dataqa350/i60_32F03.INP -diff ../dataqa350/i30_33F03.INP ../dataqa350/i30_33F03.INP -diff ../dataqa350/i60_33F03.INP ../dataqa350/i60_33F03.INP -diff ../dataqa350/i30_34F03.INP ../dataqa350/i30_34F03.INP -diff ../dataqa350/i60_34F03.INP ../dataqa350/i60_34F03.INP -diff ../dataqa350/i30_35F03.INP ../dataqa350/i30_35F03.INP -diff ../dataqa350/i60_35F03.INP ../dataqa350/i60_35F03.INP -diff ../dataqa350/i30_36F03.INP ../dataqa350/i30_36F03.INP -diff ../dataqa350/i60_36F03.INP ../dataqa350/i60_36F03.INP -diff ../dataqa350/a16F03.INP ../dataqa350/a16F03.INP -diff ../dataqa350/a17F03.INP ../dataqa350/a17F03.INP -diff ../dataqa350/a18F03.INP ../dataqa350/a18F03.INP -diff ../dataqa350/i30_37F04.INP ../dataqa350/i30_37F04.INP -diff ../dataqa350/i60_37F04.INP ../dataqa350/i60_37F04.INP -diff ../dataqa350/i30_38F04.INP ../dataqa350/i30_38F04.INP -diff ../dataqa350/i60_38F04.INP ../dataqa350/i60_38F04.INP -diff ../dataqa350/i30_39F04.INP ../dataqa350/i30_39F04.INP -diff ../dataqa350/i60_39F04.INP ../dataqa350/i60_39F04.INP -diff ../dataqa350/i30_40F04.INP ../dataqa350/i30_40F04.INP -diff ../dataqa350/i60_40F04.INP ../dataqa350/i60_40F04.INP -diff ../dataqa350/i30_41F04.INP ../dataqa350/i30_41F04.INP -diff ../dataqa350/i60_41F04.INP ../dataqa350/i60_41F04.INP -diff ../dataqa350/i30_42F04.INP ../dataqa350/i30_42F04.INP -diff ../dataqa350/i60_42F04.INP ../dataqa350/i60_42F04.INP -diff ../dataqa350/a19F04.INP ../dataqa350/a19F04.INP -diff ../dataqa350/a20F04.INP ../dataqa350/a20F04.INP -diff ../dataqa350/a21F04.INP ../dataqa350/a21F04.INP -diff ../dataqa350/i30_43F05.INP ../dataqa350/i30_43F05.INP -diff ../dataqa350/i60_43F05.INP ../dataqa350/i60_43F05.INP -diff ../dataqa350/i30_44F05.INP ../dataqa350/i30_44F05.INP -diff ../dataqa350/i60_44F05.INP ../dataqa350/i60_44F05.INP -diff ../dataqa350/i30_45F05.INP ../dataqa350/i30_45F05.INP -diff ../dataqa350/i60_45F05.INP ../dataqa350/i60_45F05.INP -diff ../dataqa350/i30_46F05.INP ../dataqa350/i30_46F05.INP -diff ../dataqa350/i60_46F05.INP ../dataqa350/i60_46F05.INP -diff ../dataqa350/i30_47F05.INP ../dataqa350/i30_47F05.INP -diff ../dataqa350/i60_47F05.INP ../dataqa350/i60_47F05.INP -diff ../dataqa350/i30_48F05.INP ../dataqa350/i30_48F05.INP -diff ../dataqa350/i60_48F05.INP ../dataqa350/i60_48F05.INP -diff ../dataqa350/a22F05.INP ../dataqa350/a22F05.INP -diff ../dataqa350/a23F05.INP ../dataqa350/a23F05.INP -diff ../dataqa350/a24F05.INP ../dataqa350/a24F05.INP -diff ../dataqa350/i30_49F06.INP ../dataqa350/i30_49F06.INP -diff ../dataqa350/i60_49F06.INP ../dataqa350/i60_49F06.INP -diff ../dataqa350/i30_50F06.INP ../dataqa350/i30_50F06.INP -diff ../dataqa350/i60_50F06.INP ../dataqa350/i60_50F06.INP -diff ../dataqa350/i30_51F06.INP ../dataqa350/i30_51F06.INP -diff ../dataqa350/i60_51F06.INP ../dataqa350/i60_51F06.INP -diff ../dataqa350/i30_52F06.INP ../dataqa350/i30_52F06.INP -diff ../dataqa350/i60_52F06.INP ../dataqa350/i60_52F06.INP -diff ../dataqa350/i30_53F06.INP ../dataqa350/i30_53F06.INP -diff ../dataqa350/i60_53F06.INP ../dataqa350/i60_53F06.INP -diff ../dataqa350/i30_54F06.INP ../dataqa350/i30_54F06.INP -diff ../dataqa350/i60_54F06.INP ../dataqa350/i60_54F06.INP -diff ../dataqa350/a25F06.INP ../dataqa350/a25F06.INP -diff ../dataqa350/a26F06.INP ../dataqa350/a26F06.INP -diff ../dataqa350/a27F06.INP ../dataqa350/a27F06.INP -diff ../dataqa350/i30_55longtest.pcm ../dataqa351/i30_55longtest.pcm -diff ../dataqa350/i60_55longtest.pcm ../dataqa351/i60_55longtest.pcm -diff ../dataqa350/i30_56longtest.pcm ../dataqa351/i30_56longtest.pcm -diff ../dataqa350/i60_56longtest.pcm ../dataqa351/i60_56longtest.pcm -diff ../dataqa350/i30_57longtest.pcm ../dataqa351/i30_57longtest.pcm -diff ../dataqa350/i60_57longtest.pcm ../dataqa351/i60_57longtest.pcm -diff ../dataqa350/i30_58longtest.pcm ../dataqa351/i30_58longtest.pcm -diff ../dataqa350/i60_58longtest.pcm ../dataqa351/i60_58longtest.pcm -diff ../dataqa350/i30_59longtest.pcm ../dataqa351/i30_59longtest.pcm -diff ../dataqa350/i60_59longtest.pcm ../dataqa351/i60_59longtest.pcm -diff ../dataqa350/i30_60longtest.pcm ../dataqa351/i30_60longtest.pcm -diff ../dataqa350/i60_60longtest.pcm ../dataqa351/i60_60longtest.pcm -diff ../dataqa350/a28longtest.pcm ../dataqa351/a28longtest.pcm -diff ../dataqa350/a29longtest.pcm ../dataqa351/a29longtest.pcm -diff ../dataqa350/a30longtest.pcm ../dataqa351/a30longtest.pcm -diff ../dataqa350/i30_61ltest_speech_clean.pcm ../dataqa351/i30_61ltest_speech_clean.pcm -diff ../dataqa350/i60_61ltest_speech_clean.pcm ../dataqa351/i60_61ltest_speech_clean.pcm -diff ../dataqa350/i30_62ltest_speech_clean.pcm ../dataqa351/i30_62ltest_speech_clean.pcm -diff ../dataqa350/i60_62ltest_speech_clean.pcm ../dataqa351/i60_62ltest_speech_clean.pcm -diff ../dataqa350/i30_63ltest_speech_clean.pcm ../dataqa351/i30_63ltest_speech_clean.pcm -diff ../dataqa350/i60_63ltest_speech_clean.pcm ../dataqa351/i60_63ltest_speech_clean.pcm -diff ../dataqa350/i30_64ltest_speech_clean.pcm ../dataqa351/i30_64ltest_speech_clean.pcm -diff ../dataqa350/i60_64ltest_speech_clean.pcm ../dataqa351/i60_64ltest_speech_clean.pcm -diff ../dataqa350/i30_65ltest_speech_clean.pcm ../dataqa351/i30_65ltest_speech_clean.pcm -diff ../dataqa350/i60_65ltest_speech_clean.pcm ../dataqa351/i60_65ltest_speech_clean.pcm -diff ../dataqa350/i30_66ltest_speech_clean.pcm ../dataqa351/i30_66ltest_speech_clean.pcm -diff ../dataqa350/i60_66ltest_speech_clean.pcm ../dataqa351/i60_66ltest_speech_clean.pcm -diff ../dataqa350/a31ltest_speech_clean.pcm ../dataqa351/a31ltest_speech_clean.pcm -diff ../dataqa350/a32ltest_speech_clean.pcm ../dataqa351/a32ltest_speech_clean.pcm -diff ../dataqa350/a33ltest_speech_clean.pcm ../dataqa351/a33ltest_speech_clean.pcm -diff ../dataqa350/i30_67ltest_music.pcm ../dataqa351/i30_67ltest_music.pcm -diff ../dataqa350/i60_67ltest_music.pcm ../dataqa351/i60_67ltest_music.pcm -diff ../dataqa350/i30_68ltest_music.pcm ../dataqa351/i30_68ltest_music.pcm -diff ../dataqa350/i60_68ltest_music.pcm ../dataqa351/i60_68ltest_music.pcm -diff ../dataqa350/i30_69ltest_music.pcm ../dataqa351/i30_69ltest_music.pcm -diff ../dataqa350/i60_69ltest_music.pcm ../dataqa351/i60_69ltest_music.pcm -diff ../dataqa350/i30_70ltest_music.pcm ../dataqa351/i30_70ltest_music.pcm -diff ../dataqa350/i60_70ltest_music.pcm ../dataqa351/i60_70ltest_music.pcm -diff ../dataqa350/i30_71ltest_music.pcm ../dataqa351/i30_71ltest_music.pcm -diff ../dataqa350/i60_71ltest_music.pcm ../dataqa351/i60_71ltest_music.pcm -diff ../dataqa350/i30_72ltest_music.pcm ../dataqa351/i30_72ltest_music.pcm -diff ../dataqa350/i60_72ltest_music.pcm ../dataqa351/i60_72ltest_music.pcm -diff ../dataqa350/a34ltest_music.pcm ../dataqa351/a34ltest_music.pcm -diff ../dataqa350/a35ltest_music.pcm ../dataqa351/a35ltest_music.pcm -diff ../dataqa350/a36ltest_music.pcm ../dataqa351/a36ltest_music.pcm -diff ../dataqa350/i30_73ltest_speech_noisy.pcm ../dataqa351/i30_73ltest_speech_noisy.pcm -diff ../dataqa350/i60_73ltest_speech_noisy.pcm ../dataqa351/i60_73ltest_speech_noisy.pcm -diff ../dataqa350/i30_74ltest_speech_noisy.pcm ../dataqa351/i30_74ltest_speech_noisy.pcm -diff ../dataqa350/i60_74ltest_speech_noisy.pcm ../dataqa351/i60_74ltest_speech_noisy.pcm -diff ../dataqa350/i30_75ltest_speech_noisy.pcm ../dataqa351/i30_75ltest_speech_noisy.pcm -diff ../dataqa350/i60_75ltest_speech_noisy.pcm ../dataqa351/i60_75ltest_speech_noisy.pcm -diff ../dataqa350/i30_76ltest_speech_noisy.pcm ../dataqa351/i30_76ltest_speech_noisy.pcm -diff ../dataqa350/i60_76ltest_speech_noisy.pcm ../dataqa351/i60_76ltest_speech_noisy.pcm -diff ../dataqa350/i30_77ltest_speech_noisy.pcm ../dataqa351/i30_77ltest_speech_noisy.pcm -diff ../dataqa350/i60_77ltest_speech_noisy.pcm ../dataqa351/i60_77ltest_speech_noisy.pcm -diff ../dataqa350/i30_78ltest_speech_noisy.pcm ../dataqa351/i30_78ltest_speech_noisy.pcm -diff ../dataqa350/i60_78ltest_speech_noisy.pcm ../dataqa351/i60_78ltest_speech_noisy.pcm -diff ../dataqa350/a37ltest_speech_noisy.pcm ../dataqa351/a37ltest_speech_noisy.pcm -diff ../dataqa350/a38ltest_speech_noisy.pcm ../dataqa351/a38ltest_speech_noisy.pcm -diff ../dataqa350/a39ltest_speech_noisy.pcm ../dataqa351/a39ltest_speech_noisy.pcm -diff ../dataqa350/i30_79misc2.pcm ../dataqa351/i30_79misc2.pcm -diff ../dataqa350/i60_79misc2.pcm ../dataqa351/i60_79misc2.pcm -diff ../dataqa350/i30_80misc2.pcm ../dataqa351/i30_80misc2.pcm -diff ../dataqa350/i60_80misc2.pcm ../dataqa351/i60_80misc2.pcm -diff ../dataqa350/i30_81misc2.pcm ../dataqa351/i30_81misc2.pcm -diff ../dataqa350/i60_81misc2.pcm ../dataqa351/i60_81misc2.pcm -diff ../dataqa350/i30_82misc2.pcm ../dataqa351/i30_82misc2.pcm -diff ../dataqa350/i60_82misc2.pcm ../dataqa351/i60_82misc2.pcm -diff ../dataqa350/i30_83misc2.pcm ../dataqa351/i30_83misc2.pcm -diff ../dataqa350/i60_83misc2.pcm ../dataqa351/i60_83misc2.pcm -diff ../dataqa350/i30_84misc2.pcm ../dataqa351/i30_84misc2.pcm -diff ../dataqa350/i60_84misc2.pcm ../dataqa351/i60_84misc2.pcm -diff ../dataqa350/a40misc2.pcm ../dataqa351/a40misc2.pcm -diff ../dataqa350/a41misc2.pcm ../dataqa351/a41misc2.pcm -diff ../dataqa350/a42misc2.pcm ../dataqa351/a42misc2.pcm -diff ../dataqa350/i30_85purenb.pcm ../dataqa351/i30_85purenb.pcm -diff ../dataqa350/i60_85purenb.pcm ../dataqa351/i60_85purenb.pcm -diff ../dataqa350/i30_86purenb.pcm ../dataqa351/i30_86purenb.pcm -diff ../dataqa350/i60_86purenb.pcm ../dataqa351/i60_86purenb.pcm -diff ../dataqa350/i30_87purenb.pcm ../dataqa351/i30_87purenb.pcm -diff ../dataqa350/i60_87purenb.pcm ../dataqa351/i60_87purenb.pcm -diff ../dataqa350/i30_88purenb.pcm ../dataqa351/i30_88purenb.pcm -diff ../dataqa350/i60_88purenb.pcm ../dataqa351/i60_88purenb.pcm -diff ../dataqa350/i30_89purenb.pcm ../dataqa351/i30_89purenb.pcm -diff ../dataqa350/i60_89purenb.pcm ../dataqa351/i60_89purenb.pcm -diff ../dataqa350/i30_90purenb.pcm ../dataqa351/i30_90purenb.pcm -diff ../dataqa350/i60_90purenb.pcm ../dataqa351/i60_90purenb.pcm -diff ../dataqa350/a43purenb.pcm ../dataqa351/a43purenb.pcm -diff ../dataqa350/a44purenb.pcm ../dataqa351/a44purenb.pcm -diff ../dataqa350/a45purenb.pcm ../dataqa351/a45purenb.pcm -diff ../dataqa350/i30_91sawsweep_380_60.pcm ../dataqa351/i30_91sawsweep_380_60.pcm -diff ../dataqa350/i60_91sawsweep_380_60.pcm ../dataqa351/i60_91sawsweep_380_60.pcm -diff ../dataqa350/i30_92sawsweep_380_60.pcm ../dataqa351/i30_92sawsweep_380_60.pcm -diff ../dataqa350/i60_92sawsweep_380_60.pcm ../dataqa351/i60_92sawsweep_380_60.pcm -diff ../dataqa350/i30_93sawsweep_380_60.pcm ../dataqa351/i30_93sawsweep_380_60.pcm -diff ../dataqa350/i60_93sawsweep_380_60.pcm ../dataqa351/i60_93sawsweep_380_60.pcm -diff ../dataqa350/i30_94sawsweep_380_60.pcm ../dataqa351/i30_94sawsweep_380_60.pcm -diff ../dataqa350/i60_94sawsweep_380_60.pcm ../dataqa351/i60_94sawsweep_380_60.pcm -diff ../dataqa350/i30_95sawsweep_380_60.pcm ../dataqa351/i30_95sawsweep_380_60.pcm -diff ../dataqa350/i60_95sawsweep_380_60.pcm ../dataqa351/i60_95sawsweep_380_60.pcm -diff ../dataqa350/i30_96sawsweep_380_60.pcm ../dataqa351/i30_96sawsweep_380_60.pcm -diff ../dataqa350/i60_96sawsweep_380_60.pcm ../dataqa351/i60_96sawsweep_380_60.pcm -diff ../dataqa350/a46sawsweep_380_60.pcm ../dataqa351/a46sawsweep_380_60.pcm -diff ../dataqa350/a47sawsweep_380_60.pcm ../dataqa351/a47sawsweep_380_60.pcm -diff ../dataqa350/a48sawsweep_380_60.pcm ../dataqa351/a48sawsweep_380_60.pcm -diff ../dataqa350/i30_97sinesweep.pcm ../dataqa351/i30_97sinesweep.pcm -diff ../dataqa350/i60_97sinesweep.pcm ../dataqa351/i60_97sinesweep.pcm -diff ../dataqa350/i30_98sinesweep.pcm ../dataqa351/i30_98sinesweep.pcm -diff ../dataqa350/i60_98sinesweep.pcm ../dataqa351/i60_98sinesweep.pcm -diff ../dataqa350/i30_99sinesweep.pcm ../dataqa351/i30_99sinesweep.pcm -diff ../dataqa350/i60_99sinesweep.pcm ../dataqa351/i60_99sinesweep.pcm -diff ../dataqa350/i30_100sinesweep.pcm ../dataqa351/i30_100sinesweep.pcm -diff ../dataqa350/i60_100sinesweep.pcm ../dataqa351/i60_100sinesweep.pcm -diff ../dataqa350/i30_101sinesweep.pcm ../dataqa351/i30_101sinesweep.pcm -diff ../dataqa350/i60_101sinesweep.pcm ../dataqa351/i60_101sinesweep.pcm -diff ../dataqa350/i30_102sinesweep.pcm ../dataqa351/i30_102sinesweep.pcm -diff ../dataqa350/i60_102sinesweep.pcm ../dataqa351/i60_102sinesweep.pcm -diff ../dataqa350/a49sinesweep.pcm ../dataqa351/a49sinesweep.pcm -diff ../dataqa350/a50sinesweep.pcm ../dataqa351/a50sinesweep.pcm -diff ../dataqa350/a51sinesweep.pcm ../dataqa351/a51sinesweep.pcm -diff ../dataqa350/i30_103sinesweep_half.pcm ../dataqa351/i30_103sinesweep_half.pcm -diff ../dataqa350/i60_103sinesweep_half.pcm ../dataqa351/i60_103sinesweep_half.pcm -diff ../dataqa350/i30_104sinesweep_half.pcm ../dataqa351/i30_104sinesweep_half.pcm -diff ../dataqa350/i60_104sinesweep_half.pcm ../dataqa351/i60_104sinesweep_half.pcm -diff ../dataqa350/i30_105sinesweep_half.pcm ../dataqa351/i30_105sinesweep_half.pcm -diff ../dataqa350/i60_105sinesweep_half.pcm ../dataqa351/i60_105sinesweep_half.pcm -diff ../dataqa350/i30_106sinesweep_half.pcm ../dataqa351/i30_106sinesweep_half.pcm -diff ../dataqa350/i60_106sinesweep_half.pcm ../dataqa351/i60_106sinesweep_half.pcm -diff ../dataqa350/i30_107sinesweep_half.pcm ../dataqa351/i30_107sinesweep_half.pcm -diff ../dataqa350/i60_107sinesweep_half.pcm ../dataqa351/i60_107sinesweep_half.pcm -diff ../dataqa350/i30_108sinesweep_half.pcm ../dataqa351/i30_108sinesweep_half.pcm -diff ../dataqa350/i60_108sinesweep_half.pcm ../dataqa351/i60_108sinesweep_half.pcm -diff ../dataqa350/a52sinesweep_half.pcm ../dataqa351/a52sinesweep_half.pcm -diff ../dataqa350/a53sinesweep_half.pcm ../dataqa351/a53sinesweep_half.pcm -diff ../dataqa350/a54sinesweep_half.pcm ../dataqa351/a54sinesweep_half.pcm -diff ../dataqa350/i30_109speechmusic.pcm ../dataqa351/i30_109speechmusic.pcm -diff ../dataqa350/i60_109speechmusic.pcm ../dataqa351/i60_109speechmusic.pcm -diff ../dataqa350/i30_110speechmusic.pcm ../dataqa351/i30_110speechmusic.pcm -diff ../dataqa350/i60_110speechmusic.pcm ../dataqa351/i60_110speechmusic.pcm -diff ../dataqa350/i30_111speechmusic.pcm ../dataqa351/i30_111speechmusic.pcm -diff ../dataqa350/i60_111speechmusic.pcm ../dataqa351/i60_111speechmusic.pcm -diff ../dataqa350/i30_112speechmusic.pcm ../dataqa351/i30_112speechmusic.pcm -diff ../dataqa350/i60_112speechmusic.pcm ../dataqa351/i60_112speechmusic.pcm -diff ../dataqa350/i30_113speechmusic.pcm ../dataqa351/i30_113speechmusic.pcm -diff ../dataqa350/i60_113speechmusic.pcm ../dataqa351/i60_113speechmusic.pcm -diff ../dataqa350/i30_114speechmusic.pcm ../dataqa351/i30_114speechmusic.pcm -diff ../dataqa350/i60_114speechmusic.pcm ../dataqa351/i60_114speechmusic.pcm -diff ../dataqa350/a55speechmusic.pcm ../dataqa351/a55speechmusic.pcm -diff ../dataqa350/a56speechmusic.pcm ../dataqa351/a56speechmusic.pcm -diff ../dataqa350/a57speechmusic.pcm ../dataqa351/a57speechmusic.pcm -diff ../dataqa350/i30_115speechmusic_nb.pcm ../dataqa351/i30_115speechmusic_nb.pcm -diff ../dataqa350/i60_115speechmusic_nb.pcm ../dataqa351/i60_115speechmusic_nb.pcm -diff ../dataqa350/i30_116speechmusic_nb.pcm ../dataqa351/i30_116speechmusic_nb.pcm -diff ../dataqa350/i60_116speechmusic_nb.pcm ../dataqa351/i60_116speechmusic_nb.pcm -diff ../dataqa350/i30_117speechmusic_nb.pcm ../dataqa351/i30_117speechmusic_nb.pcm -diff ../dataqa350/i60_117speechmusic_nb.pcm ../dataqa351/i60_117speechmusic_nb.pcm -diff ../dataqa350/i30_118speechmusic_nb.pcm ../dataqa351/i30_118speechmusic_nb.pcm -diff ../dataqa350/i60_118speechmusic_nb.pcm ../dataqa351/i60_118speechmusic_nb.pcm -diff ../dataqa350/i30_119speechmusic_nb.pcm ../dataqa351/i30_119speechmusic_nb.pcm -diff ../dataqa350/i60_119speechmusic_nb.pcm ../dataqa351/i60_119speechmusic_nb.pcm -diff ../dataqa350/i30_120speechmusic_nb.pcm ../dataqa351/i30_120speechmusic_nb.pcm -diff ../dataqa350/i60_120speechmusic_nb.pcm ../dataqa351/i60_120speechmusic_nb.pcm -diff ../dataqa350/a58speechmusic_nb.pcm ../dataqa351/a58speechmusic_nb.pcm -diff ../dataqa350/a59speechmusic_nb.pcm ../dataqa351/a59speechmusic_nb.pcm -diff ../dataqa350/a60speechmusic_nb.pcm ../dataqa351/a60speechmusic_nb.pcm -diff ../dataqa350/i30_121speechoffice0dB.pcm ../dataqa351/i30_121speechoffice0dB.pcm -diff ../dataqa350/i60_121speechoffice0dB.pcm ../dataqa351/i60_121speechoffice0dB.pcm -diff ../dataqa350/i30_122speechoffice0dB.pcm ../dataqa351/i30_122speechoffice0dB.pcm -diff ../dataqa350/i60_122speechoffice0dB.pcm ../dataqa351/i60_122speechoffice0dB.pcm -diff ../dataqa350/i30_123speechoffice0dB.pcm ../dataqa351/i30_123speechoffice0dB.pcm -diff ../dataqa350/i60_123speechoffice0dB.pcm ../dataqa351/i60_123speechoffice0dB.pcm -diff ../dataqa350/i30_124speechoffice0dB.pcm ../dataqa351/i30_124speechoffice0dB.pcm -diff ../dataqa350/i60_124speechoffice0dB.pcm ../dataqa351/i60_124speechoffice0dB.pcm -diff ../dataqa350/i30_125speechoffice0dB.pcm ../dataqa351/i30_125speechoffice0dB.pcm -diff ../dataqa350/i60_125speechoffice0dB.pcm ../dataqa351/i60_125speechoffice0dB.pcm -diff ../dataqa350/i30_126speechoffice0dB.pcm ../dataqa351/i30_126speechoffice0dB.pcm -diff ../dataqa350/i60_126speechoffice0dB.pcm ../dataqa351/i60_126speechoffice0dB.pcm -diff ../dataqa350/a61speechoffice0dB.pcm ../dataqa351/a61speechoffice0dB.pcm -diff ../dataqa350/a62speechoffice0dB.pcm ../dataqa351/a62speechoffice0dB.pcm -diff ../dataqa350/a63speechoffice0dB.pcm ../dataqa351/a63speechoffice0dB.pcm -diff ../dataqa350/i30_127speech_and_misc_NB.pcm ../dataqa351/i30_127speech_and_misc_NB.pcm -diff ../dataqa350/i60_127speech_and_misc_NB.pcm ../dataqa351/i60_127speech_and_misc_NB.pcm -diff ../dataqa350/i30_128speech_and_misc_NB.pcm ../dataqa351/i30_128speech_and_misc_NB.pcm -diff ../dataqa350/i60_128speech_and_misc_NB.pcm ../dataqa351/i60_128speech_and_misc_NB.pcm -diff ../dataqa350/i30_129speech_and_misc_NB.pcm ../dataqa351/i30_129speech_and_misc_NB.pcm -diff ../dataqa350/i60_129speech_and_misc_NB.pcm ../dataqa351/i60_129speech_and_misc_NB.pcm -diff ../dataqa350/i30_130speech_and_misc_NB.pcm ../dataqa351/i30_130speech_and_misc_NB.pcm -diff ../dataqa350/i60_130speech_and_misc_NB.pcm ../dataqa351/i60_130speech_and_misc_NB.pcm -diff ../dataqa350/i30_131speech_and_misc_NB.pcm ../dataqa351/i30_131speech_and_misc_NB.pcm -diff ../dataqa350/i60_131speech_and_misc_NB.pcm ../dataqa351/i60_131speech_and_misc_NB.pcm -diff ../dataqa350/i30_132speech_and_misc_NB.pcm ../dataqa351/i30_132speech_and_misc_NB.pcm -diff ../dataqa350/i60_132speech_and_misc_NB.pcm ../dataqa351/i60_132speech_and_misc_NB.pcm -diff ../dataqa350/a64speech_and_misc_NB.pcm ../dataqa351/a64speech_and_misc_NB.pcm -diff ../dataqa350/a65speech_and_misc_NB.pcm ../dataqa351/a65speech_and_misc_NB.pcm -diff ../dataqa350/a66speech_and_misc_NB.pcm ../dataqa351/a66speech_and_misc_NB.pcm -diff ../dataqa350/i30_133speech_and_misc_WB.pcm ../dataqa351/i30_133speech_and_misc_WB.pcm -diff ../dataqa350/i60_133speech_and_misc_WB.pcm ../dataqa351/i60_133speech_and_misc_WB.pcm -diff ../dataqa350/i30_134speech_and_misc_WB.pcm ../dataqa351/i30_134speech_and_misc_WB.pcm -diff ../dataqa350/i60_134speech_and_misc_WB.pcm ../dataqa351/i60_134speech_and_misc_WB.pcm -diff ../dataqa350/i30_135speech_and_misc_WB.pcm ../dataqa351/i30_135speech_and_misc_WB.pcm -diff ../dataqa350/i60_135speech_and_misc_WB.pcm ../dataqa351/i60_135speech_and_misc_WB.pcm -diff ../dataqa350/i30_136speech_and_misc_WB.pcm ../dataqa351/i30_136speech_and_misc_WB.pcm -diff ../dataqa350/i60_136speech_and_misc_WB.pcm ../dataqa351/i60_136speech_and_misc_WB.pcm -diff ../dataqa350/i30_137speech_and_misc_WB.pcm ../dataqa351/i30_137speech_and_misc_WB.pcm -diff ../dataqa350/i60_137speech_and_misc_WB.pcm ../dataqa351/i60_137speech_and_misc_WB.pcm -diff ../dataqa350/i30_138speech_and_misc_WB.pcm ../dataqa351/i30_138speech_and_misc_WB.pcm -diff ../dataqa350/i60_138speech_and_misc_WB.pcm ../dataqa351/i60_138speech_and_misc_WB.pcm -diff ../dataqa350/a67speech_and_misc_WB.pcm ../dataqa351/a67speech_and_misc_WB.pcm -diff ../dataqa350/a68speech_and_misc_WB.pcm ../dataqa351/a68speech_and_misc_WB.pcm -diff ../dataqa350/a69speech_and_misc_WB.pcm ../dataqa351/a69speech_and_misc_WB.pcm -diff ../dataqa350/i30_139testM4.pcm ../dataqa351/i30_139testM4.pcm -diff ../dataqa350/i60_139testM4.pcm ../dataqa351/i60_139testM4.pcm -diff ../dataqa350/i30_140testM4.pcm ../dataqa351/i30_140testM4.pcm -diff ../dataqa350/i60_140testM4.pcm ../dataqa351/i60_140testM4.pcm -diff ../dataqa350/i30_141testM4.pcm ../dataqa351/i30_141testM4.pcm -diff ../dataqa350/i60_141testM4.pcm ../dataqa351/i60_141testM4.pcm -diff ../dataqa350/i30_142testM4.pcm ../dataqa351/i30_142testM4.pcm -diff ../dataqa350/i60_142testM4.pcm ../dataqa351/i60_142testM4.pcm -diff ../dataqa350/i30_143testM4.pcm ../dataqa351/i30_143testM4.pcm -diff ../dataqa350/i60_143testM4.pcm ../dataqa351/i60_143testM4.pcm -diff ../dataqa350/i30_144testM4.pcm ../dataqa351/i30_144testM4.pcm -diff ../dataqa350/i60_144testM4.pcm ../dataqa351/i60_144testM4.pcm -diff ../dataqa350/a70testM4.pcm ../dataqa351/a70testM4.pcm -diff ../dataqa350/a71testM4.pcm ../dataqa351/a71testM4.pcm -diff ../dataqa350/a72testM4.pcm ../dataqa351/a72testM4.pcm -diff ../dataqa350/i30_145testM4D_rev.pcm ../dataqa351/i30_145testM4D_rev.pcm -diff ../dataqa350/i60_145testM4D_rev.pcm ../dataqa351/i60_145testM4D_rev.pcm -diff ../dataqa350/i30_146testM4D_rev.pcm ../dataqa351/i30_146testM4D_rev.pcm -diff ../dataqa350/i60_146testM4D_rev.pcm ../dataqa351/i60_146testM4D_rev.pcm -diff ../dataqa350/i30_147testM4D_rev.pcm ../dataqa351/i30_147testM4D_rev.pcm -diff ../dataqa350/i60_147testM4D_rev.pcm ../dataqa351/i60_147testM4D_rev.pcm -diff ../dataqa350/i30_148testM4D_rev.pcm ../dataqa351/i30_148testM4D_rev.pcm -diff ../dataqa350/i60_148testM4D_rev.pcm ../dataqa351/i60_148testM4D_rev.pcm -diff ../dataqa350/i30_149testM4D_rev.pcm ../dataqa351/i30_149testM4D_rev.pcm -diff ../dataqa350/i60_149testM4D_rev.pcm ../dataqa351/i60_149testM4D_rev.pcm -diff ../dataqa350/i30_150testM4D_rev.pcm ../dataqa351/i30_150testM4D_rev.pcm -diff ../dataqa350/i60_150testM4D_rev.pcm ../dataqa351/i60_150testM4D_rev.pcm -diff ../dataqa350/a73testM4D_rev.pcm ../dataqa351/a73testM4D_rev.pcm -diff ../dataqa350/a74testM4D_rev.pcm ../dataqa351/a74testM4D_rev.pcm -diff ../dataqa350/a75testM4D_rev.pcm ../dataqa351/a75testM4D_rev.pcm -diff ../dataqa350/i30_151testM4D.pcm ../dataqa351/i30_151testM4D.pcm -diff ../dataqa350/i60_151testM4D.pcm ../dataqa351/i60_151testM4D.pcm -diff ../dataqa350/i30_152testM4D.pcm ../dataqa351/i30_152testM4D.pcm -diff ../dataqa350/i60_152testM4D.pcm ../dataqa351/i60_152testM4D.pcm -diff ../dataqa350/i30_153testM4D.pcm ../dataqa351/i30_153testM4D.pcm -diff ../dataqa350/i60_153testM4D.pcm ../dataqa351/i60_153testM4D.pcm -diff ../dataqa350/i30_154testM4D.pcm ../dataqa351/i30_154testM4D.pcm -diff ../dataqa350/i60_154testM4D.pcm ../dataqa351/i60_154testM4D.pcm -diff ../dataqa350/i30_155testM4D.pcm ../dataqa351/i30_155testM4D.pcm -diff ../dataqa350/i60_155testM4D.pcm ../dataqa351/i60_155testM4D.pcm -diff ../dataqa350/i30_156testM4D.pcm ../dataqa351/i30_156testM4D.pcm -diff ../dataqa350/i60_156testM4D.pcm ../dataqa351/i60_156testM4D.pcm -diff ../dataqa350/a76testM4D.pcm ../dataqa351/a76testM4D.pcm -diff ../dataqa350/a77testM4D.pcm ../dataqa351/a77testM4D.pcm -diff ../dataqa350/a78testM4D.pcm ../dataqa351/a78testM4D.pcm -diff ../dataqa350/i30_157testfile.pcm ../dataqa351/i30_157testfile.pcm -diff ../dataqa350/i60_157testfile.pcm ../dataqa351/i60_157testfile.pcm -diff ../dataqa350/i30_158testfile.pcm ../dataqa351/i30_158testfile.pcm -diff ../dataqa350/i60_158testfile.pcm ../dataqa351/i60_158testfile.pcm -diff ../dataqa350/i30_159testfile.pcm ../dataqa351/i30_159testfile.pcm -diff ../dataqa350/i60_159testfile.pcm ../dataqa351/i60_159testfile.pcm -diff ../dataqa350/i30_160testfile.pcm ../dataqa351/i30_160testfile.pcm -diff ../dataqa350/i60_160testfile.pcm ../dataqa351/i60_160testfile.pcm -diff ../dataqa350/i30_161testfile.pcm ../dataqa351/i30_161testfile.pcm -diff ../dataqa350/i60_161testfile.pcm ../dataqa351/i60_161testfile.pcm -diff ../dataqa350/i30_162testfile.pcm ../dataqa351/i30_162testfile.pcm -diff ../dataqa350/i60_162testfile.pcm ../dataqa351/i60_162testfile.pcm -diff ../dataqa350/a79testfile.pcm ../dataqa351/a79testfile.pcm -diff ../dataqa350/a80testfile.pcm ../dataqa351/a80testfile.pcm -diff ../dataqa350/a81testfile.pcm ../dataqa351/a81testfile.pcm -diff ../dataqa350/i30_163tone_cisco.pcm ../dataqa351/i30_163tone_cisco.pcm -diff ../dataqa350/i60_163tone_cisco.pcm ../dataqa351/i60_163tone_cisco.pcm -diff ../dataqa350/i30_164tone_cisco.pcm ../dataqa351/i30_164tone_cisco.pcm -diff ../dataqa350/i60_164tone_cisco.pcm ../dataqa351/i60_164tone_cisco.pcm -diff ../dataqa350/i30_165tone_cisco.pcm ../dataqa351/i30_165tone_cisco.pcm -diff ../dataqa350/i60_165tone_cisco.pcm ../dataqa351/i60_165tone_cisco.pcm -diff ../dataqa350/i30_166tone_cisco.pcm ../dataqa351/i30_166tone_cisco.pcm -diff ../dataqa350/i60_166tone_cisco.pcm ../dataqa351/i60_166tone_cisco.pcm -diff ../dataqa350/i30_167tone_cisco.pcm ../dataqa351/i30_167tone_cisco.pcm -diff ../dataqa350/i60_167tone_cisco.pcm ../dataqa351/i60_167tone_cisco.pcm -diff ../dataqa350/i30_168tone_cisco.pcm ../dataqa351/i30_168tone_cisco.pcm -diff ../dataqa350/i60_168tone_cisco.pcm ../dataqa351/i60_168tone_cisco.pcm -diff ../dataqa350/a82tone_cisco.pcm ../dataqa351/a82tone_cisco.pcm -diff ../dataqa350/a83tone_cisco.pcm ../dataqa351/a83tone_cisco.pcm -diff ../dataqa350/a84tone_cisco.pcm ../dataqa351/a84tone_cisco.pcm -diff ../dataqa350/i30_169tone_cisco_long.pcm ../dataqa351/i30_169tone_cisco_long.pcm -diff ../dataqa350/i60_169tone_cisco_long.pcm ../dataqa351/i60_169tone_cisco_long.pcm -diff ../dataqa350/i30_170tone_cisco_long.pcm ../dataqa351/i30_170tone_cisco_long.pcm -diff ../dataqa350/i60_170tone_cisco_long.pcm ../dataqa351/i60_170tone_cisco_long.pcm -diff ../dataqa350/i30_171tone_cisco_long.pcm ../dataqa351/i30_171tone_cisco_long.pcm -diff ../dataqa350/i60_171tone_cisco_long.pcm ../dataqa351/i60_171tone_cisco_long.pcm -diff ../dataqa350/i30_172tone_cisco_long.pcm ../dataqa351/i30_172tone_cisco_long.pcm -diff ../dataqa350/i60_172tone_cisco_long.pcm ../dataqa351/i60_172tone_cisco_long.pcm -diff ../dataqa350/i30_173tone_cisco_long.pcm ../dataqa351/i30_173tone_cisco_long.pcm -diff ../dataqa350/i60_173tone_cisco_long.pcm ../dataqa351/i60_173tone_cisco_long.pcm -diff ../dataqa350/i30_174tone_cisco_long.pcm ../dataqa351/i30_174tone_cisco_long.pcm -diff ../dataqa350/i60_174tone_cisco_long.pcm ../dataqa351/i60_174tone_cisco_long.pcm -diff ../dataqa350/a85tone_cisco_long.pcm ../dataqa351/a85tone_cisco_long.pcm -diff ../dataqa350/a86tone_cisco_long.pcm ../dataqa351/a86tone_cisco_long.pcm -diff ../dataqa350/a87tone_cisco_long.pcm ../dataqa351/a87tone_cisco_long.pcm -diff ../dataqa350/i30_175wb_contspeech.pcm ../dataqa351/i30_175wb_contspeech.pcm -diff ../dataqa350/i60_175wb_contspeech.pcm ../dataqa351/i60_175wb_contspeech.pcm -diff ../dataqa350/i30_176wb_contspeech.pcm ../dataqa351/i30_176wb_contspeech.pcm -diff ../dataqa350/i60_176wb_contspeech.pcm ../dataqa351/i60_176wb_contspeech.pcm -diff ../dataqa350/i30_177wb_contspeech.pcm ../dataqa351/i30_177wb_contspeech.pcm -diff ../dataqa350/i60_177wb_contspeech.pcm ../dataqa351/i60_177wb_contspeech.pcm -diff ../dataqa350/i30_178wb_contspeech.pcm ../dataqa351/i30_178wb_contspeech.pcm -diff ../dataqa350/i60_178wb_contspeech.pcm ../dataqa351/i60_178wb_contspeech.pcm -diff ../dataqa350/i30_179wb_contspeech.pcm ../dataqa351/i30_179wb_contspeech.pcm -diff ../dataqa350/i60_179wb_contspeech.pcm ../dataqa351/i60_179wb_contspeech.pcm -diff ../dataqa350/i30_180wb_contspeech.pcm ../dataqa351/i30_180wb_contspeech.pcm -diff ../dataqa350/i60_180wb_contspeech.pcm ../dataqa351/i60_180wb_contspeech.pcm -diff ../dataqa350/a88wb_contspeech.pcm ../dataqa351/a88wb_contspeech.pcm -diff ../dataqa350/a89wb_contspeech.pcm ../dataqa351/a89wb_contspeech.pcm -diff ../dataqa350/a90wb_contspeech.pcm ../dataqa351/a90wb_contspeech.pcm -diff ../dataqa350/i30_181wb_speech_office25db.pcm ../dataqa351/i30_181wb_speech_office25db.pcm -diff ../dataqa350/i60_181wb_speech_office25db.pcm ../dataqa351/i60_181wb_speech_office25db.pcm -diff ../dataqa350/i30_182wb_speech_office25db.pcm ../dataqa351/i30_182wb_speech_office25db.pcm -diff ../dataqa350/i60_182wb_speech_office25db.pcm ../dataqa351/i60_182wb_speech_office25db.pcm -diff ../dataqa350/i30_183wb_speech_office25db.pcm ../dataqa351/i30_183wb_speech_office25db.pcm -diff ../dataqa350/i60_183wb_speech_office25db.pcm ../dataqa351/i60_183wb_speech_office25db.pcm -diff ../dataqa350/i30_184wb_speech_office25db.pcm ../dataqa351/i30_184wb_speech_office25db.pcm -diff ../dataqa350/i60_184wb_speech_office25db.pcm ../dataqa351/i60_184wb_speech_office25db.pcm -diff ../dataqa350/i30_185wb_speech_office25db.pcm ../dataqa351/i30_185wb_speech_office25db.pcm -diff ../dataqa350/i60_185wb_speech_office25db.pcm ../dataqa351/i60_185wb_speech_office25db.pcm -diff ../dataqa350/i30_186wb_speech_office25db.pcm ../dataqa351/i30_186wb_speech_office25db.pcm -diff ../dataqa350/i60_186wb_speech_office25db.pcm ../dataqa351/i60_186wb_speech_office25db.pcm -diff ../dataqa350/a91wb_speech_office25db.pcm ../dataqa351/a91wb_speech_office25db.pcm -diff ../dataqa350/a92wb_speech_office25db.pcm ../dataqa351/a92wb_speech_office25db.pcm -diff ../dataqa350/a93wb_speech_office25db.pcm ../dataqa351/a93wb_speech_office25db.pcm -diff ../dataqa350/a30_1DTMF_16kHz_short.pcm ../dataqa351/a30_1DTMF_16kHz_short.pcm -diff ../dataqa350/a60_1DTMF_16kHz_short.pcm ../dataqa351/a60_1DTMF_16kHz_short.pcm -diff ../dataqa350/a30_2ltest_speech_noisy.pcm ../dataqa351/a30_2ltest_speech_noisy.pcm -diff ../dataqa350/a60_2ltest_speech_noisy.pcm ../dataqa351/a60_2ltest_speech_noisy.pcm -diff ../dataqa350/a30_3misc2.pcm ../dataqa351/a30_3misc2.pcm -diff ../dataqa350/a60_3misc2.pcm ../dataqa351/a60_3misc2.pcm -diff ../dataqa350/a30_4sinesweep.pcm ../dataqa351/a30_4sinesweep.pcm -diff ../dataqa350/a60_4sinesweep.pcm ../dataqa351/a60_4sinesweep.pcm -diff ../dataqa350/a30_5speechmusic.pcm ../dataqa351/a30_5speechmusic.pcm -diff ../dataqa350/a60_5speechmusic.pcm ../dataqa351/a60_5speechmusic.pcm -diff ../dataqa350/a30_6tone_cisco.pcm ../dataqa351/a30_6tone_cisco.pcm -diff ../dataqa350/a60_6tone_cisco.pcm ../dataqa351/a60_6tone_cisco.pcm -diff ../dataqa350/a60_7tone_cisco.pcm ../dataqa351/a60_7tone_cisco.pcm diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSACPLC.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSACPLC.txt deleted file mode 100644 index 9e3629b2c..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/diffiSACPLC.txt +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logplc.txt -echo "START PLC TEST" > $LOGFILE - -OUTDIR1=../dataqaplc_0 -OUTDIR2=../dataqaplc_1 - -diff $OUTDIR1/outplc1.pcm $OUTDIR2/outplc1.pcm -diff $OUTDIR1/outplc2.pcm $OUTDIR2/outplc2.pcm -diff $OUTDIR1/outplc3.pcm $OUTDIR2/outplc3.pcm -diff $OUTDIR1/outplc4.pcm $OUTDIR2/outplc4.pcm -diff $OUTDIR1/outplc5.pcm $OUTDIR2/outplc5.pcm -diff $OUTDIR1/outplc6.pcm $OUTDIR2/outplc6.pcm - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACLongtest.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACLongtest.txt deleted file mode 100644 index eeffc0c95..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACLongtest.txt +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logNormal.txt -echo "START ISAC TEST" > $LOGFILE -echo >> $LOGFILE - -ISAC=../Release/kenny.exe -ISACFIXFLOAT=../Release/testFixFloat.exe - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -OUTDIR=../dataqa -mkdir -p $OUTDIR - -TARGETRATE=(10000 15000 20000 25000 30000 32000) -#echo ${CHANNELFILES[1]} - -index1=0 -index2=0 - -for file in $INFILES # loop over all input files - do - - for rate in ${TARGETRATE[*]} - do - let "index1=index1+1" - $ISAC -I $rate -FL 30 $INDIR/"$file" $OUTDIR/i30_$index1"$file" >> $LOGFILE - $ISAC -I $rate -FL 60 $INDIR/"$file" $OUTDIR/i60_$index1"$file" >> $LOGFILE - done - for channel in $CHANNELFILES - do - let "index2=index2+1" - $ISAC $INDIR/$channel $INDIR/"$file" $OUTDIR/a$index2"$file" >> $LOGFILE - done - -done - -index1=0 - -for file in $SUBSET # loop over the subset of input files - do - let "index1=index1+1" - $ISAC $INDIR/${CHANNELLIST[0]} -FL 30 -FIXED_FL $INDIR/"$file" $OUTDIR/a30_$index1"$file" >> $LOGFILE - $ISAC $INDIR/${CHANNELLIST[0]} -FL 60 -FIXED_FL $INDIR/"$file" $OUTDIR/a60_$index1"$file" >> $LOGFILE -done - -let "index1=index1+1" - $ISAC $INDIR/${CHANNELLIST[0]} -INITRATE 25000 -FL 30 $INDIR/"$file" $OUTDIR/a60_$index1"$file" >> $LOGFILE - -# Run fault test - -#./runiSACfault.txt - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACNB.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACNB.txt deleted file mode 100644 index 605595cc0..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACNB.txt +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logNB.txt -echo "START NARROWBAND TEST" > $LOGFILE -echo >> $LOGFILE - -ISAC=../Release/kenny.exe -ISACFIXFLOAT=../Release/testFixFloat.exe - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -OUTDIR=../dataqaNB -mkdir -p $OUTDIR - -TARGETRATE=(10000 15000 20000 25000 30000 32000) -#echo ${CHANNELFILES[1]} - -index1=0 -index2=0 - -# Narrowband Interfaces - -for file in $SUBSET # loop over all input files - do - for rate in ${TARGETRATE[*]} - do - let "index1=index1+1" - $ISAC $rate -FL 30 -NB 1 $INDIR/"$file" $OUTDIR/nb130_$index1"$file" >> $LOGFILE - $ISAC $rate -FL 60 -NB 1 $INDIR/"$file" $OUTDIR/nb160_$index1"$file" >> $LOGFILE - $ISAC $rate -FL 30 -NB 2 $INDIR/"$file" $OUTDIR/nb230_$index1"$file" >> $LOGFILE - $ISAC $rate -FL 60 -NB 2 $INDIR/"$file" $OUTDIR/nb260_$index1"$file" >> $LOGFILE - $ISAC $rate -FL 30 -NB 2 -PL 10 $INDIR/"$file" $OUTDIR/nb2plc30_$index1"$file" >> $LOGFILE - $ISAC $rate -FL 60 -NB 2 -PL 10 $INDIR/"$file" $OUTDIR/nb2plc60_$index1"$file" >> $LOGFILE - done - -done - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACPLC.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACPLC.txt deleted file mode 100644 index 6bee6f7c3..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACPLC.txt +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logplc.txt -echo "START PLC TEST" > $LOGFILE - -ISAC=../Release/kenny.exe - -INDIR=../data/orig -OUTDIR=../dataqaplc_0 -mkdir -p $OUTDIR - -$ISAC 12000 -PL 15 $INDIR/speechmusic.pcm $OUTDIR/outplc1.pcm -$ISAC 20000 -PL 15 $INDIR/speechmusic.pcm $OUTDIR/outplc2.pcm -$ISAC 32000 -PL 15 $INDIR/speechmusic.pcm $OUTDIR/outplc3.pcm -$ISAC 12000 -PL 15 $INDIR/tone_cisco.pcm $OUTDIR/outplc4.pcm -$ISAC 20000 -PL 15 $INDIR/tone_cisco.pcm $OUTDIR/outplc5.pcm -$ISAC 32000 -PL 15 $INDIR/tone_cisco.pcm $OUTDIR/outplc6.pcm - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACRate.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACRate.txt deleted file mode 100644 index d8403e099..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACRate.txt +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGG=loggRate.txt -OUTDIR=../dataqaRate -mkdir -p $OUTDIR - -../Release/kenny.exe 13000 -FIXED_FL -FL 30 -MAX 100 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_1.pcm > $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 30 -MAXRATE 32000 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_2.pcm >> $LOGG -../Release/kenny.exe 13000 -FIXED_FL -FL 30 -MAX 100 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_3.pcm >> $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 30 -MAXRATE 32000 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_4.pcm >> $LOGG -../Release/kenny.exe 13000 -FIXED_FL -FL 60 -MAX 100 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_5.pcm >> $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 60 -MAXRATE 32000 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_6.pcm >> $LOGG -../Release/kenny.exe 13000 -INIT_RATE 32000 -FIXED_FL -FL 60 -MAX 100 ../data/orig/sawsweep_380_60.pcm $OUTDIR/out_napi_7.pcm >> $LOGG - -../Release/kenny.exe 13000 -FIXED_FL -FL 30 -MAX 100 ../data/orig/longspeech.pcm $OUTDIR/out_napi_11.pcm >> $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 30 -MAXRATE 32000 ../data/orig/longspeech.pcm $OUTDIR/out_napi_12.pcm >> $LOGG -../Release/kenny.exe 13000 -FIXED_FL -FL 30 -MAX 100 ../data/orig/longspeech.pcm $OUTDIR/out_napi_13.pcm >> $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 30 -MAXRATE 32000 ../data/orig/longspeech.pcm $OUTDIR/out_napi_14.pcm >> $LOGG -../Release/kenny.exe 13000 -FIXED_FL -FL 60 -MAX 100 ../data/orig/longspeech.pcm $OUTDIR/out_napi_15.pcm >> $LOGG -../Release/kenny.exe ../data/orig/bottlenecks.txt -FIXED_FL -FL 60 -MAXRATE 32000 ../data/orig/longspeech.pcm $OUTDIR/out_napi_16.pcm >> $LOGG -../Release/kenny.exe 13000 -INIT_RATE 32000 -FIXED_FL -FL 60 -MAX 100 ../data/orig/longspeech.pcm $OUTDIR/out_napi_17.pcm >> $LOGG - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfault.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfault.txt deleted file mode 100644 index f4d9478fd..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfault.txt +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logfault.txt -echo "START FAULT TEST" > $LOGFILE - -ISAC=../Release/kenny.exe -ISACFIXFLOAT=../Release/testFixFloat.exe - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -OUTDIR=../dataqaft -mkdir -p $OUTDIR - -TARGETRATE=(10000 15000 20000 25000 30000 32000) -FAULTTEST=(1 2 3 4 5 6 7 9) - -index1=0 - -file=wb_contspeech.pcm - -# Fault test -for testnr in ${FAULTTEST[*]} - do - $ISAC 32000 -F $testnr $INDIR/"$file" $OUTDIR/ft$testnr"$file" >> $LOGFILE -done - -# Fault test number 10, error in bitstream - $ISAC 32000 -F 10 $INDIR/"$file" $OUTDIR/ft10_"$file" >> $LOGFILE - $ISAC 32000 -F 10 -PL 10 $INDIR/"$file" $OUTDIR/ft10plc_"$file" >> $LOGFILE - $ISAC 32000 -F 10 -NB 1 $INDIR/"$file" $OUTDIR/ft10nb1_"$file" >> $LOGFILE - $ISAC 32000 -F 10 -NB 2 -PL 10 $INDIR/"$file" $OUTDIR/ft10nb2_"$file" >> $LOGFILE - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfixfloat.txt b/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfixfloat.txt deleted file mode 100644 index c9e02df2e..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/QA/runiSACfixfloat.txt +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logfxfl.txt -echo "START FIX-FLOAT TEST" > $LOGFILE - - -ISACFIXFLOAT=../testFixFloat.exe - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -OUTDIR=../dataqafxfl -mkdir -p $OUTDIR - -index1=0 - -for file in $INFILES # loop over all input files - do - - for channel in $CHANNELFILES - do - let "index1=index1+1" - - $ISACFIXFLOAT $INDIR/$channel -m 1 -PLC $INDIR/"$file" $OUTDIR/flfx$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -PLC $INDIR/"$file" $OUTDIR/fxfl$index1"$file" >> $LOGFILE - done - -done - -index1=0 - -for file in $SUBSET # loop over the subset of input files - do - let "index1=index1+1" - $ISACFIXFLOAT $INDIR/$channel -m 1 -NB 1 $INDIR/"$file" $OUTDIR/flfxnb1_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -NB 1 $INDIR/"$file" $OUTDIR/fxflnb1_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 1 -NB 2 -PLC $INDIR/"$file" $OUTDIR/flfxnb2_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -NB 2 -PLC $INDIR/"$file" $OUTDIR/fxflnb2_$index1"$file" >> $LOGFILE -done - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/fix/test/kenny.c b/modules/audio_coding/codecs/iSAC/fix/test/kenny.c deleted file mode 100644 index 6bfe33bcc..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/kenny.c +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* kenny.c - Main function for the iSAC coder */ - -#include -#include -#include -#include -#include - -#include "isacfix.h" - - -/* Defines */ -#define SEED_FILE "randseed.txt" /* Used when running decoder on garbage data */ -#define MAX_FRAMESAMPLES 960 /* max number of samples per frame (= 60 ms frame) */ -#define FRAMESAMPLES_10ms 160 /* number of samples per 10ms frame */ -#define FS 16000 /* sampling frequency (Hz) */ - -/* Function for reading audio data from PCM file */ -int readframe(WebRtc_Word16 *data, FILE *inp, int length) { - - short k, rlen, status = 0; - - rlen = fread(data, sizeof(WebRtc_Word16), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - return status; -} - -/* Struct for bottleneck model */ -typedef struct { - WebRtc_UWord32 send_time; /* samples */ - WebRtc_UWord32 arrival_time; /* samples */ - WebRtc_UWord32 sample_count; /* samples */ - WebRtc_UWord16 rtp_number; -} BottleNeckModel; - -void get_arrival_time(int current_framesamples, /* samples */ - int packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel *BN_data) -{ - const int HeaderSize = 35; - int HeaderRate; - - HeaderRate = HeaderSize * 8 * FS / current_framesamples; /* bits/s */ - - /* everything in samples */ - BN_data->sample_count = BN_data->sample_count + current_framesamples; - - BN_data->arrival_time += ((packet_size + HeaderSize) * 8 * FS) / (bottleneck + HeaderRate); - BN_data->send_time += current_framesamples; - - if (BN_data->arrival_time < BN_data->sample_count) - BN_data->arrival_time = BN_data->sample_count; - - BN_data->rtp_number++; -} - -void get_arrival_time2(int current_framesamples, - int current_delay, - BottleNeckModel *BN_data) -{ - if (current_delay == -1) - //dropped packet - { - BN_data->arrival_time += current_framesamples; - } - else if (current_delay != -2) - { - // - BN_data->arrival_time += (current_framesamples + ((FS/1000) * current_delay)); - } - //else - //current packet has same timestamp as previous packet - - BN_data->rtp_number++; -} - -int main(int argc, char* argv[]) -{ - - char inname[100], outname[100], outbitsname[100], bottleneck_file[100]; - FILE *inp, *outp, *f_bn, *outbits; - int endfile; - - int i, errtype, h = 0, k, packetLossPercent = 0; - WebRtc_Word16 CodingMode; - WebRtc_Word16 bottleneck; - WebRtc_Word16 framesize = 30; /* ms */ - int cur_framesmpls, err, lostPackets = 0; - - /* Runtime statistics */ - double starttime, runtime, length_file; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 trans_len = 0; - WebRtc_Word16 framecnt, declen; - WebRtc_Word16 shortdata[FRAMESAMPLES_10ms]; - WebRtc_Word16 decoded[MAX_FRAMESAMPLES]; - WebRtc_UWord16 streamdata[500]; - WebRtc_Word16 speechType[1]; - WebRtc_Word16 prevFrameSize = 1; - WebRtc_Word16 rateBPS = 0; - WebRtc_Word16 fixedFL = 0; - WebRtc_Word16 payloadSize = 0; - WebRtc_Word32 payloadRate = 0; - int setControlBWE = 0; - int readLoss; - FILE *plFile; - - char version_number[20]; - char tmpBit[5] = ".bit"; - - double kbps; - int totalbits =0; - int totalsmpls =0; -#ifdef _DEBUG - FILE *fy; -#endif - WebRtc_Word16 testNum, testCE; - - FILE *fp_gns = NULL; - int gns = 0; - int cur_delay = 0; - char gns_file[100]; - - WebRtc_UWord16 rtp_num_init = 0; - - int nbTest = 0; - WebRtc_Word16 lostFrame; - float scale = (float)0.7; - /* only one structure used for ISAC encoder */ - ISACFIX_MainStruct *ISAC_main_inst; - - /* For fault test 10, garbage data */ - FILE *seedfile; - unsigned int random_seed = (unsigned int) time(NULL);//1196764538 - - BottleNeckModel BN_data; - f_bn = NULL; - -#ifdef _DEBUG - fy = fopen("bit_rate.dat", "w"); - fclose(fy); - fy = fopen("bytes_frames.dat", "w"); - fclose(fy); -#endif - - readLoss = 0; - packetLossPercent = 0; - - /* Handling wrong input arguments in the command line */ - if ((argc<3) || (argc>21)) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - WebRtcIsacfix_version(version_number); - printf("iSAC version %s \n\n", version_number); - - printf("Usage:\n\n"); - printf("./kenny.exe [-F num][-I] bottleneck_value infile outfile \n\n"); - printf("with:\n"); - printf("[-I] : if -I option is specified, the coder will use\n"); - printf(" an instantaneous Bottleneck value. If not, it\n"); - printf(" will be an adaptive Bottleneck value.\n\n"); - printf("bottleneck_value : the value of the bottleneck provided either\n"); - printf(" as a fixed value (e.g. 25000) or\n"); - printf(" read from a file (e.g. bottleneck.txt)\n\n"); - printf("[-INITRATE num] : Set a new value for initial rate. Note! Only used in adaptive mode.\n\n"); - printf("[-FL num] : Set (initial) frame length in msec. Valid length are 30 and 60 msec.\n\n"); - printf("[-FIXED_FL] : Frame length will be fixed to initial value.\n\n"); - printf("[-MAX num] : Set the limit for the payload size of iSAC in bytes. \n"); - printf(" Minimum 100, maximum 400.\n\n"); - printf("[-MAXRATE num] : Set the maxrate for iSAC in bits per second. \n"); - printf(" Minimum 32000, maximum 53400.\n\n"); - printf("[-F num] : if -F option is specified, the test function\n"); - printf(" will run the iSAC API fault scenario specified by the\n"); - printf(" supplied number.\n"); - printf(" F 1 - Call encoder prior to init encoder call\n"); - printf(" F 2 - Call decoder prior to init decoder call\n"); - printf(" F 3 - Call decoder prior to encoder call\n"); - printf(" F 4 - Call decoder with a too short coded sequence\n"); - printf(" F 5 - Call decoder with a too long coded sequence\n"); - printf(" F 6 - Call decoder with random bit stream\n"); - printf(" F 7 - Call init encoder/decoder at random during a call\n"); - printf(" F 8 - Call encoder/decoder without having allocated memory for \n"); - printf(" encoder/decoder instance\n"); - printf(" F 9 - Call decodeB without calling decodeA\n"); - printf(" F 10 - Call decodeB with garbage data\n"); - printf("[-PL num] : if -PL option is specified 0 encode with narrowband encoder (infile is narrowband)\n"); - printf(" num=2 => decode with narrowband decoder (outfile is narrowband)\n\n"); - printf("[-CE num] : Test of APIs used by Conference Engine.\n"); - printf(" CE 1 - createInternal, freeInternal, getNewBitstream \n"); - printf(" CE 2 - transcode, getBWE \n"); - printf(" CE 3 - getSendBWE, setSendBWE. \n\n"); - printf("[-RTP_INIT num] : if -RTP_INIT option is specified num will be the initial\n"); - printf(" value of the rtp sequence number.\n\n"); - printf("infile : Normal speech input file\n\n"); - printf("outfile : Speech output file\n\n"); - printf("Example usage : \n\n"); - printf("./kenny.exe -I bottleneck.txt speechIn.pcm speechOut.pcm\n\n"); - exit(0); - - } - - /* Print version number */ - WebRtcIsacfix_version(version_number); - printf("iSAC version %s \n\n", version_number); - - /* Loop over all command line arguments */ - CodingMode = 0; - testNum = 0; - testCE = 0; - for (i = 1; i < argc-2;i++) { - /* Instantaneous mode */ - if (!strcmp ("-I", argv[i])) { - printf("\nInstantaneous BottleNeck\n"); - CodingMode = 1; - i++; - } - - /* Set (initial) bottleneck value */ - if (!strcmp ("-INITRATE", argv[i])) { - rateBPS = atoi(argv[i + 1]); - setControlBWE = 1; - if ((rateBPS < 10000) || (rateBPS > 32000)) { - printf("\n%d is not a initial rate. Valid values are in the range 10000 to 32000.\n", rateBPS); - exit(0); - } - printf("\nNew initial rate: %d\n", rateBPS); - i++; - } - - /* Set (initial) framelength */ - if (!strcmp ("-FL", argv[i])) { - framesize = atoi(argv[i + 1]); - if ((framesize != 30) && (framesize != 60)) { - printf("\n%d is not a valid frame length. Valid length are 30 and 60 msec.\n", framesize); - exit(0); - } - printf("\nFrame Length: %d\n", framesize); - i++; - } - - /* Fixed frame length */ - if (!strcmp ("-FIXED_FL", argv[i])) { - fixedFL = 1; - setControlBWE = 1; - } - - /* Set maximum allowed payload size in bytes */ - if (!strcmp ("-MAX", argv[i])) { - payloadSize = atoi(argv[i + 1]); - printf("Maximum Payload Size: %d\n", payloadSize); - i++; - } - - /* Set maximum rate in bytes */ - if (!strcmp ("-MAXRATE", argv[i])) { - payloadRate = atoi(argv[i + 1]); - printf("Maximum Rate in kbps: %d\n", payloadRate); - i++; - } - - /* Test of fault scenarious */ - if (!strcmp ("-F", argv[i])) { - testNum = atoi(argv[i + 1]); - printf("\nFault test: %d\n", testNum); - if (testNum < 1 || testNum > 10) { - printf("\n%d is not a valid Fault Scenario number. Valid Fault Scenarios are numbered 1-10.\n", testNum); - exit(0); - } - i++; - } - - /* Packet loss test */ - if (!strcmp ("-PL", argv[i])) { - if( isdigit( *argv[i+1] ) ) { - packetLossPercent = atoi( argv[i+1] ); - if( (packetLossPercent < 0) | (packetLossPercent > 100) ) { - printf( "\nInvalid packet loss perentage \n" ); - exit( 0 ); - } - if( packetLossPercent > 0 ) { - printf( "\nSimulating %d %% of independent packet loss\n", packetLossPercent ); - } else { - printf( "\nNo Packet Loss Is Simulated \n" ); - } - readLoss = 0; - } else { - readLoss = 1; - plFile = fopen( argv[i+1], "rb" ); - if( plFile == NULL ) { - printf( "\n couldn't open the frameloss file: %s\n", argv[i+1] ); - exit( 0 ); - } - printf( "\nSimulating packet loss through the given channel file: %s\n", argv[i+1] ); - } - i++; - } - - /* Random packetlosses */ - if (!strcmp ("-rnd", argv[i])) { - srand(time(NULL) ); - printf( "\n Random pattern in lossed packets \n" ); - } - - /* Use gns file */ - if (!strcmp ("-G", argv[i])) { - sscanf(argv[i + 1], "%s", gns_file); - fp_gns = fopen(gns_file, "rb"); - if (fp_gns == NULL) { - printf("Cannot read file %s.\n", gns_file); - exit(0); - } - gns = 1; - i++; - } - - /* Run Narrowband interfaces (either encoder or decoder) */ - if (!strcmp ("-NB", argv[i])) { - nbTest = atoi(argv[i + 1]); - i++; - } - - /* Run Conference Engine APIs */ - if (!strcmp ("-CE", argv[i])) { - testCE = atoi(argv[i + 1]); - if (testCE==1 || testCE==2) { - i++; - scale = (float)atof( argv[i+1] ); - } else if (testCE < 1 || testCE > 3) { - printf("\n%d is not a valid CE-test number, valid Fault Scenarios are numbered 1-3\n", testCE); - exit(0); - } - i++; - } - - /* Set initial RTP number */ - if (!strcmp ("-RTP_INIT", argv[i])) { - rtp_num_init = atoi(argv[i + 1]); - i++; - } - } - - /* Get Bottleneck value */ - /* Gns files and bottleneck should not and can not be used simultaneously */ - bottleneck = atoi(argv[CodingMode+1]); - if (bottleneck == 0 && gns == 0) { - sscanf(argv[CodingMode+1], "%s", bottleneck_file); - f_bn = fopen(bottleneck_file, "rb"); - if (f_bn == NULL) { - printf("No value provided for BottleNeck and cannot read file %s\n", bottleneck_file); - exit(0); - } else { - printf("reading bottleneck rates from file %s\n\n",bottleneck_file); - if (fscanf(f_bn, "%d", &bottleneck) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - - /* Bottleneck is a cosine function - * Matlab code for writing the bottleneck file: - * BottleNeck_10ms = 20e3 + 10e3 * cos((0:5999)/5999*2*pi); - * fid = fopen('bottleneck.txt', 'wb'); - * fprintf(fid, '%d\n', BottleNeck_10ms); fclose(fid); - */ - } - } else { - f_bn = NULL; - printf("\nfixed bottleneck rate of %d bits/s\n\n", bottleneck); - } - - if (CodingMode == 0) { - printf("\nAdaptive BottleNeck\n"); - } - - /* Get Input and Output files */ - sscanf(argv[argc-2], "%s", inname); - sscanf(argv[argc-1], "%s", outname); - - /* Add '.bit' to output bitstream file */ - while ((int)outname[h] != 0) { - outbitsname[h] = outname[h]; - h++; - } - for (k=0; k<5; k++) { - outbitsname[h] = tmpBit[k]; - h++; - } - if ((inp = fopen(inname,"rb")) == NULL) { - printf(" iSAC: Cannot read file %s\n", inname); - exit(1); - } - if ((outp = fopen(outname,"wb")) == NULL) { - printf(" iSAC: Cannot write file %s\n", outname); - exit(1); - } - - if ((outbits = fopen(outbitsname,"wb")) == NULL) { - printf(" iSAC: Cannot write file %s\n", outbitsname); - exit(1); - } - printf("\nInput:%s\nOutput:%s\n\n", inname, outname); - - /* Error test number 10, garbage data */ - if (testNum == 10) { - /* Test to run decoder with garbage data */ - srand(random_seed); - - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - printf("Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "%u\n", random_seed); - fclose(seedfile); - } - } - - /* Runtime statistics */ - starttime = clock()/(double)CLOCKS_PER_SEC; - - /* Initialize the ISAC and BN structs */ - if (testNum != 8) - { - if(1){ - err =WebRtcIsacfix_Create(&ISAC_main_inst); - }else{ - /* Test the Assign functions */ - int sss; - void *ppp; - err =WebRtcIsacfix_AssignSize(&sss); - ppp=malloc(sss); - err =WebRtcIsacfix_Assign(&ISAC_main_inst,ppp); - } - /* Error check */ - if (err < 0) { - printf("\n\n Error in create.\n\n"); - } - if (testCE == 1) { - err = WebRtcIsacfix_CreateInternal(ISAC_main_inst); - /* Error check */ - if (err < 0) { - printf("\n\n Error in createInternal.\n\n"); - } - } - } - - /* Init of bandwidth data */ - BN_data.send_time = 0; - BN_data.arrival_time = 0; - BN_data.sample_count = 0; - BN_data.rtp_number = 0; - - /* Initialize encoder and decoder */ - framecnt= 0; - endfile = 0; - if (testNum != 1) { - WebRtcIsacfix_EncoderInit(ISAC_main_inst, CodingMode); - } - if (testNum != 2) { - WebRtcIsacfix_DecoderInit(ISAC_main_inst); - } - - if (CodingMode == 1) { - err = WebRtcIsacfix_Control(ISAC_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in control: %d.\n\n", errtype); - } - } else if(setControlBWE == 1) { - err = WebRtcIsacfix_ControlBwe(ISAC_main_inst, rateBPS, framesize, fixedFL); - } - - if (payloadSize != 0) { - err = WebRtcIsacfix_SetMaxPayloadSize(ISAC_main_inst, payloadSize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in SetMaxPayloadSize: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - } - if (payloadRate != 0) { - err = WebRtcIsacfix_SetMaxRate(ISAC_main_inst, payloadRate); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in SetMaxRateInBytes: %d.\n\n", errtype); - exit(EXIT_FAILURE); - } - } - - *speechType = 1; - - - while (endfile == 0) { - - if(testNum == 7 && (rand()%2 == 0)) { - err = WebRtcIsacfix_EncoderInit(ISAC_main_inst, CodingMode); - /* Error check */ - if (err < 0) { - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in encoderinit: %d.\n\n", errtype); - } - - err = WebRtcIsacfix_DecoderInit(ISAC_main_inst); - /* Error check */ - if (err < 0) { - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in decoderinit: %d.\n\n", errtype); - } - } - - - cur_framesmpls = 0; - while (1) { - /* Read 10 ms speech block */ - if (nbTest != 1) { - endfile = readframe(shortdata, inp, FRAMESAMPLES_10ms); - } else { - endfile = readframe(shortdata, inp, (FRAMESAMPLES_10ms/2)); - } - - if (testNum == 7) { - srand(time(NULL)); - } - - /* iSAC encoding */ - if (!(testNum == 3 && framecnt == 0)) { - if (nbTest != 1) { - short bwe; - - /* Encode */ - stream_len = WebRtcIsacfix_Encode(ISAC_main_inst, - shortdata, - streamdata); - - /* If packet is ready, and CE testing, call the different API functions - from the internal API. */ - if (stream_len>0) { - if (testCE == 1) { - err = WebRtcIsacfix_ReadBwIndex(streamdata, &bwe); - stream_len = WebRtcIsacfix_GetNewBitStream(ISAC_main_inst, bwe, scale, streamdata); - } else if (testCE == 2) { - /* transcode function not supported */ - } else if (testCE == 3) { - /* Only for Function testing. The functions should normally - not be used in this way */ - - err = WebRtcIsacfix_GetDownLinkBwIndex(ISAC_main_inst, &bwe); - /* Error Check */ - if (err < 0) { - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in getSendBWE: %d.\n", errtype); - } - - err = WebRtcIsacfix_UpdateUplinkBw(ISAC_main_inst, bwe); - /* Error Check */ - if (err < 0) { - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in setBWE: %d.\n", errtype); - } - - } - } - } else { - stream_len = WebRtcIsacfix_EncodeNb(ISAC_main_inst, - shortdata, - streamdata); - } - } - else - { - break; - } - - if (stream_len < 0 || err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in encoder: %d.\n", errtype); - } else { - fwrite(streamdata, sizeof(char), stream_len, outbits); - } - - cur_framesmpls += FRAMESAMPLES_10ms; - - /* read next bottleneck rate */ - if (f_bn != NULL) { - if (fscanf(f_bn, "%d", &bottleneck) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - if (CodingMode == 1) { - WebRtcIsacfix_Control(ISAC_main_inst, bottleneck, framesize); - } - } - - /* exit encoder loop if the encoder returned a bitstream */ - if (stream_len != 0) break; - } - - /* make coded sequence to short be inreasing */ - /* the length the decoder expects */ - if (testNum == 4) { - stream_len += 10; - } - - /* make coded sequence to long be decreasing */ - /* the length the decoder expects */ - if (testNum == 5) { - stream_len -= 10; - } - - if (testNum == 6) { - srand(time(NULL)); - for (i = 0; i < stream_len; i++ ) { - streamdata[i] = rand(); - } - } - - /* set pointer to beginning of file */ - if (fp_gns != NULL) { - if (fscanf(fp_gns, "%d", &cur_delay) == EOF) { - fseek(fp_gns, 0L, SEEK_SET); - fscanf(fp_gns, "%d", &cur_delay); - } - } - - - /* simulate packet handling through NetEq and the modem */ - if (!(testNum == 3 && framecnt == 0)) { - if (gns == 0) { - get_arrival_time(cur_framesmpls, stream_len, bottleneck, - &BN_data); - } else { - get_arrival_time2(cur_framesmpls, cur_delay, &BN_data); - } - } - - /* packet not dropped */ - if (cur_delay != -1) { - - /* Error test number 10, garbage data */ - if (testNum == 10) { - for ( i = 0; i < stream_len; i++) { - streamdata[i] = (short) (streamdata[i] + (short) rand()); - } - } - - if (testNum != 9) { - err = WebRtcIsacfix_UpdateBwEstimate(ISAC_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.send_time, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in decoder: %d.\n", errtype); - } - } -#ifdef _DEBUG - fprintf(stderr," \rframe = %7d", framecnt); -#endif - - if( readLoss == 1 ) { - if( fread( &lostFrame, sizeof(WebRtc_Word16), 1, plFile ) != 1 ) { - rewind( plFile ); - } - lostFrame = !lostFrame; - } else { - lostFrame = (rand()%100 < packetLossPercent); - } - - - - /* iSAC decoding */ - if( lostFrame && framecnt > 0) { - if (nbTest !=2) { - declen = WebRtcIsacfix_DecodePlc( ISAC_main_inst, decoded, prevFrameSize ); - } else { - declen = WebRtcIsacfix_DecodePlcNb( ISAC_main_inst, decoded, prevFrameSize ); - } - lostPackets++; - } else { - if (nbTest !=2 ) { - short FL; - /* Call getFramelen, only used here for function test */ - err = WebRtcIsacfix_ReadFrameLen(streamdata, &FL); - declen = WebRtcIsacfix_Decode( ISAC_main_inst, streamdata, stream_len, decoded, speechType ); - /* Error check */ - if (err<0 || declen<0 || FL!=declen) { - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in decode_B/or getFrameLen: %d.\n", errtype); - } - prevFrameSize = declen/480; - - } else { - declen = WebRtcIsacfix_DecodeNb( ISAC_main_inst, streamdata, stream_len, decoded, speechType ); - prevFrameSize = declen/240; - } - } - - if (declen <= 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISAC_main_inst); - printf("\nError in decoder: %d.\n", errtype); - } - - /* Write decoded speech frame to file */ - fwrite(decoded, sizeof(WebRtc_Word16), declen, outp); - // fprintf( ratefile, "%f \n", stream_len / ( ((double)declen)/ ((double)FS) ) * 8 ); - - } else { - lostPackets++; - } - framecnt++; - - totalsmpls += declen; - totalbits += 8 * stream_len; - kbps = ((double) FS) / ((double) cur_framesmpls) * 8.0 * stream_len / 1000.0;// kbits/s - - /* Error test number 10, garbage data */ - if (testNum == 10) { - if ( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) { - printf( "Error: Could not open file %s\n", SEED_FILE); - } - else { - fprintf(seedfile, "ok\n\n"); - fclose(seedfile); - } - } - -#ifdef _DEBUG - - fy = fopen("bit_rate.dat", "a"); - fprintf(fy, "Frame %i = %0.14f\n", framecnt, kbps); - fclose(fy); - -#endif /* _DEBUG */ - - } - printf("\nLost Frames %d ~ %4.1f%%\n", lostPackets, (double)lostPackets/(double)framecnt*100.0 ); - printf("\n\ntotal bits = %d bits", totalbits); - printf("\nmeasured average bitrate = %0.3f kbits/s", (double)totalbits *(FS/1000) / totalsmpls); - printf("\n"); - -#ifdef _DEBUG - /* fprintf(stderr,"\n\ntotal bits = %d bits", totalbits); - fprintf(stderr,"\nmeasured average bitrate = %0.3f kbits/s", (double)totalbits *(FS/1000) / totalsmpls); - fprintf(stderr,"\n"); - */ -#endif /* _DEBUG */ - - /* Runtime statistics */ - - - runtime = (double)(((double)clock()/(double)CLOCKS_PER_SEC)-starttime); - length_file = ((double)framecnt*(double)declen/FS); - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run iSAC: %.2f s (%.2f %% of realtime)\n\n", runtime, (100*runtime/length_file)); - printf("\n\n_______________________________________________\n"); - - fclose(inp); - fclose(outp); - fclose(outbits); - - if ( testCE == 1) { - WebRtcIsacfix_FreeInternal(ISAC_main_inst); - } - WebRtcIsacfix_Free(ISAC_main_inst); - - // exit(0); -} diff --git a/modules/audio_coding/codecs/iSAC/fix/test/test_iSACfixfloat.c b/modules/audio_coding/codecs/iSAC/fix/test/test_iSACfixfloat.c deleted file mode 100644 index 57c30cad9..000000000 --- a/modules/audio_coding/codecs/iSAC/fix/test/test_iSACfixfloat.c +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * test_iSACfixfloat.c - * - * Test compatibility and quality between floating- and fixed-point code - * */ - -#include -#include -#include - -/* include API */ -#include "isac.h" -#include "isacfix.h" - - -/* max number of samples per frame (= 60 ms frame) */ -#define MAX_FRAMESAMPLES 960 -/* number of samples per 10ms frame */ -#define FRAMESAMPLES_10ms 160 -/* sampling frequency (Hz) */ -#define FS 16000 - - - -/* Runtime statistics */ -#include -#define CLOCKS_PER_SEC 1000 - - - -// FILE *histfile, *ratefile; - - -/* function for reading audio data from PCM file */ -int readframe(WebRtc_Word16 *data, FILE *inp, int length) { - - short k, rlen, status = 0; - - rlen = fread(data, sizeof(WebRtc_Word16), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - return status; -} - -typedef struct { - WebRtc_UWord32 send_time; /* samples */ - WebRtc_UWord32 arrival_time; /* samples */ - WebRtc_UWord32 sample_count; /* samples */ - WebRtc_UWord16 rtp_number; -} BottleNeckModel; - -void get_arrival_time(int current_framesamples, /* samples */ - int packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel *BN_data) -{ - const int HeaderSize = 35; - int HeaderRate; - - HeaderRate = HeaderSize * 8 * FS / current_framesamples; /* bits/s */ - - /* everything in samples */ - BN_data->sample_count = BN_data->sample_count + current_framesamples; - - BN_data->arrival_time += ((packet_size + HeaderSize) * 8 * FS) / (bottleneck + HeaderRate); - BN_data->send_time += current_framesamples; - - if (BN_data->arrival_time < BN_data->sample_count) - BN_data->arrival_time = BN_data->sample_count; - - BN_data->rtp_number++; -} - - - -int main(int argc, char* argv[]) -{ - - char inname[50], outname[50], bottleneck_file[50], bitfilename[60], bitending[10]="_bits.pcm"; - FILE *inp, *outp, *f_bn, *bitsp; - int framecnt, endfile; - - - int i,j,errtype, plc=0; - WebRtc_Word16 CodingMode; - WebRtc_Word16 bottleneck; - - WebRtc_Word16 framesize = 30; /* ms */ - //WebRtc_Word16 framesize = 60; /* To invoke cisco complexity case at frame 2252 */ - - int cur_framesmpls, err; - - /* Runtime statistics */ - double starttime; - double runtime; - double length_file; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 declen; - - WebRtc_Word16 shortdata[FRAMESAMPLES_10ms]; - WebRtc_Word16 decoded[MAX_FRAMESAMPLES]; - WebRtc_UWord16 streamdata[600]; - WebRtc_Word16 speechType[1]; - -// WebRtc_Word16 *iSACstruct; - - char version_number[20]; - int mode=-1, tmp, nbTest=0; /*,sss;*/ - -#ifdef _DEBUG - FILE *fy; - double kbps; - int totalbits =0; - int totalsmpls =0; -#endif /* _DEBUG */ - - - - - /* only one structure used for ISAC encoder */ - ISAC_MainStruct *ISAC_main_inst; - ISACFIX_MainStruct *ISACFIX_main_inst; - - BottleNeckModel BN_data; - f_bn = NULL; - -#ifdef _DEBUG - fy = fopen("bit_rate.dat", "w"); - fclose(fy); - fy = fopen("bytes_frames.dat", "w"); - fclose(fy); -#endif /* _DEBUG */ - - -//histfile = fopen("histo.dat", "ab"); -//ratefile = fopen("rates.dat", "ab"); - - /* handling wrong input arguments in the command line */ - if ((argc<6) || (argc>10)) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - WebRtcIsacfix_version(version_number); - printf("iSAC version %s \n\n", version_number); - - printf("Usage:\n\n"); - printf("./kenny.exe [-I] bottleneck_value infile outfile \n\n"); - printf("with:\n"); - - printf("[-I] : if -I option is specified, the coder will use\n"); - printf(" an instantaneous Bottleneck value. If not, it\n"); - printf(" will be an adaptive Bottleneck value.\n\n"); - printf("bottleneck_value : the value of the bottleneck provided either\n"); - printf(" as a fixed value (e.g. 25000) or\n"); - printf(" read from a file (e.g. bottleneck.txt)\n\n"); - printf("[-m] mode : Mode (encoder - decoder):\n"); - printf(" : 0 - float - float \n"); - printf(" : 1 - float - fix \n"); - printf(" : 2 - fix - float \n"); - printf(" : 3 - fix - fix \n"); - printf("[-PLC] : Test PLC packetlosses\n"); - printf("[-NB] num : Test NB interfaces, num=1 encNB, num=2 decNB\n"); - printf("infile : Normal speech input file\n\n"); - printf("outfile : Speech output file\n\n"); - printf("Example usage:\n\n"); - printf("./kenny.exe -I bottleneck.txt -m 1 speechIn.pcm speechOut.pcm\n\n"); - exit(0); - - } - - - printf("--------------------START---------------------\n\n"); - WebRtcIsac_version(version_number); - printf("iSAC FLOAT version %s \n", version_number); - WebRtcIsacfix_version(version_number); - printf("iSAC FIX version %s \n\n", version_number); - - CodingMode = 0; - tmp=1; - for (i = 1; i < argc;i++) - { - if (!strcmp ("-I", argv[i])) - { - printf("\nInstantaneous BottleNeck\n"); - CodingMode = 1; - i++; - tmp=0; - } - - if (!strcmp ("-m", argv[i])) { - mode=atoi(argv[i+1]); - i++; - } - - if (!strcmp ("-PLC", argv[i])) - { - plc=1; - } - - if (!strcmp ("-NB", argv[i])) - { - nbTest = atoi(argv[i + 1]); - i++; - } - - } - - if(mode<0) { - printf("\nError! Mode must be set: -m 0 \n"); - exit(0); - } - - if (CodingMode == 0) - { - printf("\nAdaptive BottleNeck\n"); - } - - - - /* Get Bottleneck value */ - bottleneck = atoi(argv[2-tmp]); - if (bottleneck == 0) - { - sscanf(argv[2-tmp], "%s", bottleneck_file); - f_bn = fopen(bottleneck_file, "rb"); - if (f_bn == NULL) - { - printf("No value provided for BottleNeck and cannot read file %s.\n", bottleneck_file); - exit(0); - } - else { - printf("reading bottleneck rates from file %s\n\n",bottleneck_file); - if (fscanf(f_bn, "%d", &bottleneck) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - - /* Bottleneck is a cosine function - * Matlab code for writing the bottleneck file: - * BottleNeck_10ms = 20e3 + 10e3 * cos((0:5999)/5999*2*pi); - * fid = fopen('bottleneck.txt', 'wb'); - * fprintf(fid, '%d\n', BottleNeck_10ms); fclose(fid); - */ - } - } - else - { - printf("\nfixed bottleneck rate of %d bits/s\n\n", bottleneck); - } - - - - /* Get Input and Output files */ - sscanf(argv[argc-2], "%s", inname); - sscanf(argv[argc-1], "%s", outname); - - if ((inp = fopen(inname,"rb")) == NULL) { - printf(" iSAC: Cannot read file %s.\n", inname); - exit(1); - } - if ((outp = fopen(outname,"wb")) == NULL) { - printf(" iSAC: Cannot write file %s.\n", outname); - exit(1); - } - printf("\nInput:%s\nOutput:%s\n", inname, outname); - - i=0; - while (outname[i]!='\0') { - bitfilename[i]=outname[i]; - i++; - } - i-=4; - for (j=0;j<9;j++, i++) - bitfilename[i]=bitending[j]; - bitfilename[i]='\0'; - if ((bitsp = fopen(bitfilename,"wb")) == NULL) { - printf(" iSAC: Cannot read file %s.\n", bitfilename); - exit(1); - } - printf("Bitstream:%s\n\n", bitfilename); - - - - starttime = clock()/(double)CLOCKS_PER_SEC; /* Runtime statistics */ - - - /* Initialize the ISAC and BN structs */ - WebRtcIsac_create(&ISAC_main_inst); -/* WebRtcIsacfix_AssignSize(&sss); - iSACstruct=malloc(sss); - WebRtcIsacfix_Assign(&ISACFIX_main_inst,iSACstruct);*/ - WebRtcIsacfix_Create(&ISACFIX_main_inst); - - BN_data.send_time = 0; - BN_data.arrival_time = 0; - BN_data.sample_count = 0; - BN_data.rtp_number = 0; - - /* Initialize encoder and decoder */ - framecnt= 0; - endfile = 0; - - if (mode==0) { /* Encode using FLOAT, decode using FLOAT */ - - printf("Coding mode: Encode using FLOAT, decode using FLOAT \n\n"); - - /* Init iSAC FLOAT */ - WebRtcIsac_EncoderInit(ISAC_main_inst, CodingMode); - WebRtcIsac_DecoderInit(ISAC_main_inst); - if (CodingMode == 1) { - err = WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - // exit(EXIT_FAILURE); - } - } - - } else if (mode==1) { /* Encode using FLOAT, decode using FIX */ - - printf("Coding mode: Encode using FLOAT, decode using FIX \n\n"); - - /* Init iSAC FLOAT */ - WebRtcIsac_EncoderInit(ISAC_main_inst, CodingMode); - WebRtcIsac_DecoderInit(ISAC_main_inst); - if (CodingMode == 1) { - err = WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - // exit(EXIT_FAILURE); - } - } - - /* Init iSAC FIX */ - WebRtcIsacfix_EncoderInit(ISACFIX_main_inst, CodingMode); - WebRtcIsacfix_DecoderInit(ISACFIX_main_inst); - if (CodingMode == 1) { - err = WebRtcIsacfix_Control(ISACFIX_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } - } else if (mode==2) { /* Encode using FIX, decode using FLOAT */ - - printf("Coding mode: Encode using FIX, decode using FLOAT \n\n"); - - /* Init iSAC FLOAT */ - WebRtcIsac_EncoderInit(ISAC_main_inst, CodingMode); - WebRtcIsac_DecoderInit(ISAC_main_inst); - if (CodingMode == 1) { - err = WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } - - /* Init iSAC FIX */ - WebRtcIsacfix_EncoderInit(ISACFIX_main_inst, CodingMode); - WebRtcIsacfix_DecoderInit(ISACFIX_main_inst); - if (CodingMode == 1) { - err = WebRtcIsacfix_Control(ISACFIX_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } - } else if (mode==3) { - - printf("Coding mode: Encode using FIX, decode using FIX \n\n"); - - WebRtcIsacfix_EncoderInit(ISACFIX_main_inst, CodingMode); - WebRtcIsacfix_DecoderInit(ISACFIX_main_inst); - if (CodingMode == 1) { - err = WebRtcIsacfix_Control(ISACFIX_main_inst, bottleneck, framesize); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\n Error in initialization: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } - - } else - printf("Mode must be value between 0 and 3\n"); - *speechType = 1; - -//#define BI_TEST 1 -#ifdef BI_TEST - err = WebRtcIsacfix_SetMaxPayloadSize(ISACFIX_main_inst, 300); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\n Error in setMaxPayloadSize: %d.\n\n", errtype); - fclose(inp); - fclose(outp); - fclose(bitsp); - return(EXIT_FAILURE); - } -#endif - - - while (endfile == 0) { - - cur_framesmpls = 0; - while (1) { - /* Read 10 ms speech block */ - if (nbTest != 1) - endfile = readframe(shortdata, inp, FRAMESAMPLES_10ms); - else - endfile = readframe(shortdata, inp, (FRAMESAMPLES_10ms/2)); - - /* iSAC encoding */ - - if (mode==0 || mode ==1) { - stream_len = WebRtcIsac_Encode(ISAC_main_inst, shortdata, streamdata); - if (stream_len < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in encoder: %d.\n\n", errtype); - // exit(EXIT_FAILURE); - } - } else if (mode==2 || mode==3) { - /* iSAC encoding */ - if (nbTest != 1) - stream_len = WebRtcIsacfix_Encode(ISACFIX_main_inst, shortdata, streamdata); - else - stream_len = WebRtcIsacfix_EncodeNb(ISACFIX_main_inst, shortdata, streamdata); - - if (stream_len < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\nError in encoder: %d.\n\n", errtype); - // exit(EXIT_FAILURE); - } - } - - cur_framesmpls += FRAMESAMPLES_10ms; - - /* read next bottleneck rate */ - if (f_bn != NULL) { - if (fscanf(f_bn, "%d", &bottleneck) == EOF) { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - if (CodingMode == 1) { - if (mode==0 || mode==1) - WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - else if (mode==2 || mode==3) - WebRtcIsacfix_Control(ISACFIX_main_inst, bottleneck, framesize); - } - } - - /* exit encoder loop if the encoder returned a bitstream */ - if (stream_len != 0) break; - } - - fwrite(streamdata, 1, stream_len, bitsp); /* NOTE! Writes bytes to file */ - - /* simulate packet handling through NetEq and the modem */ - get_arrival_time(cur_framesmpls, stream_len, bottleneck, - &BN_data); -//***************************** - if (1){ - if (mode==0) { - err = WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - /* iSAC decoding */ - declen = WebRtcIsac_Decode(ISAC_main_inst, - streamdata, - stream_len, - decoded, - speechType); - if (declen <= 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } else if (mode==1) { - - err = WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.arrival_time); - err = WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.arrival_time); - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - - declen = WebRtcIsac_Decode(ISAC_main_inst, - streamdata, - stream_len, - decoded, - speechType); - - /* iSAC decoding */ - if (plc && (framecnt+1)%10 == 0) { - if (nbTest !=2 ) - declen = WebRtcIsacfix_DecodePlc( ISACFIX_main_inst, decoded, 1 ); - else - declen = WebRtcIsacfix_DecodePlcNb( ISACFIX_main_inst, decoded, 1 ); - } else { - if (nbTest !=2 ) - declen = WebRtcIsacfix_Decode(ISACFIX_main_inst, - streamdata, - stream_len, - decoded, - speechType); - else - declen = WebRtcIsacfix_DecodeNb(ISACFIX_main_inst, - streamdata, - stream_len, - decoded, - speechType); - } - - if (declen <= 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } else if (mode==2) { - err = WebRtcIsacfix_UpdateBwEstimate1(ISACFIX_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.arrival_time); - - err = WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - /* iSAC decoding */ - declen = WebRtcIsac_Decode(ISAC_main_inst, - streamdata, - stream_len, - decoded, - speechType); - if (declen <= 0) { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } else if (mode==3) { - err = WebRtcIsacfix_UpdateBwEstimate(ISACFIX_main_inst, - streamdata, - stream_len, - BN_data.rtp_number, - BN_data.send_time, - BN_data.arrival_time); - - if (err < 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - /* iSAC decoding */ - - if (plc && (framecnt+1)%10 == 0) { - if (nbTest !=2 ) - declen = WebRtcIsacfix_DecodePlc( ISACFIX_main_inst, decoded, 1 ); - else - declen = WebRtcIsacfix_DecodePlcNb( ISACFIX_main_inst, decoded, 1 ); - } else { - if (nbTest !=2 ) - declen = WebRtcIsacfix_Decode(ISACFIX_main_inst, - streamdata, - stream_len, - decoded, - speechType); - else - declen = WebRtcIsacfix_DecodeNb(ISACFIX_main_inst, - streamdata, - stream_len, - decoded, - speechType); - } - if (declen <= 0) { - /* exit if returned with error */ - errtype=WebRtcIsacfix_GetErrorCode(ISACFIX_main_inst); - printf("\n\nError in decoder: %d.\n\n", errtype); - //exit(EXIT_FAILURE); - } - } - - /* Write decoded speech frame to file */ - fwrite(decoded, sizeof(WebRtc_Word16), declen, outp); - } - - fprintf(stderr," \rframe = %d", framecnt); - framecnt++; - - - -#ifdef _DEBUG - - totalsmpls += declen; - totalbits += 8 * stream_len; - kbps = ((double) FS) / ((double) cur_framesmpls) * 8.0 * stream_len / 1000.0;// kbits/s - fy = fopen("bit_rate.dat", "a"); - fprintf(fy, "Frame %i = %0.14f\n", framecnt, kbps); - fclose(fy); - -#endif /* _DEBUG */ - - } - -#ifdef _DEBUG - printf("\n\ntotal bits = %d bits", totalbits); - printf("\nmeasured average bitrate = %0.3f kbits/s", (double)totalbits *(FS/1000) / totalsmpls); - printf("\n"); -#endif /* _DEBUG */ - - /* Runtime statistics */ - runtime = (double)(clock()/(double)CLOCKS_PER_SEC-starttime); - length_file = ((double)framecnt*(double)declen/FS); - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run iSAC: %.2f s (%.2f %% of realtime)\n\n", runtime, (100*runtime/length_file)); - printf("---------------------END----------------------\n"); - - fclose(inp); - fclose(outp); - - WebRtcIsac_Free(ISAC_main_inst); - WebRtcIsacfix_Free(ISACFIX_main_inst); - - - -// fclose(histfile); -// fclose(ratefile); - - return 0; - -} - - diff --git a/modules/audio_coding/codecs/iSAC/isac_test.gyp b/modules/audio_coding/codecs/iSAC/isac_test.gyp deleted file mode 100644 index 726238c18..000000000 --- a/modules/audio_coding/codecs/iSAC/isac_test.gyp +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - # simple kenny - { - 'target_name': 'iSACtest', - 'type': 'executable', - 'dependencies': [ - './main/source/isac.gyp:iSAC', - ], - 'include_dirs': [ - './main/test', - './main/interface', - './main/util', - ], - 'sources': [ - './main/test/simpleKenny.c', - './main/util/utility.c', - ], - }, - # ReleaseTest-API - { - 'target_name': 'iSACAPITest', - 'type': 'executable', - 'dependencies': [ - './main/source/isac.gyp:iSAC', - ], - 'include_dirs': [ - './main/test', - './main/interface', - './main/util', - ], - 'sources': [ - './main/test/ReleaseTest-API/ReleaseTest-API.cc', - './main/util/utility.c', - ], - }, - # SwitchingSampRate - { - 'target_name': 'iSACSwitchSampRateTest', - 'type': 'executable', - 'dependencies': [ - './main/source/isac.gyp:iSAC', - ], - 'include_dirs': [ - './main/test', - './main/interface', - '../../../../common_audio/signal_processing_library/main/interface', - './main/util', - ], - 'sources': [ - './main/test/SwitchingSampRate/SwitchingSampRate.cc', - './main/util/utility.c', - ], - }, - - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/iSAC/isacfix_test.gyp b/modules/audio_coding/codecs/iSAC/isacfix_test.gyp deleted file mode 100644 index dd9ccd9d5..000000000 --- a/modules/audio_coding/codecs/iSAC/isacfix_test.gyp +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - # kenny - { - 'target_name': 'iSACFixtest', - 'type': 'executable', - 'dependencies': [ - './fix/source/isacfix.gyp:iSACFix', - ], - 'include_dirs': [ - './fix/test', - './fix/interface', - ], - 'sources': [ - './fix/test/kenny.c', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/iSAC/main/interface/isac.h b/modules/audio_coding/codecs/iSAC/main/interface/isac.h deleted file mode 100644 index 03c260bb8..000000000 --- a/modules/audio_coding/codecs/iSAC/main/interface/isac.h +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INTERFACE_ISAC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INTERFACE_ISAC_H_ - -/* - * Define the fixed-point numeric formats - */ -#include "typedefs.h" - -typedef struct WebRtcISACStruct ISACStruct; - -enum IsacSamplingRate {kIsacWideband = 16, kIsacSuperWideband = 32}; - - -#if defined(__cplusplus) -extern "C" { -#endif - - /****************************************************************************** - * WebRtcIsac_AssignSize(...) - * - * This function returns the size of the ISAC instance, so that the instance - * can be created outside iSAC. - * - * Input: - * - samplingRate : sampling rate of the input/output audio. - * - * Output: - * - sizeinbytes : number of bytes needed to allocate for the - * instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_AssignSize( - int* sizeinbytes); - - - /****************************************************************************** - * WebRtcIsac_Assign(...) - * - * This function assignes the memory already created to the ISAC instance. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - samplingRate : sampling rate of the input/output audio. - * - ISAC_inst_Addr : the already allocated memory, where we put the - * iSAC structure. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_Assign( - ISACStruct** ISAC_main_inst, - void* ISAC_inst_Addr); - - - /****************************************************************************** - * WebRtcIsac_Create(...) - * - * This function creates an ISAC instance, which will contain the state - * information for one coding/decoding channel. - * - * Input: - * - *ISAC_main_inst : a pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_Create( - ISACStruct** ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_Free(...) - * - * This function frees the ISAC instance created at the beginning. - * - * Input: - * - ISAC_main_inst : an ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_Free( - ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_EncoderInit(...) - * - * This function initializes an ISAC instance prior to the encoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - CodingMode : 0 -> Bit rate and frame length are - * automatically adjusted to available bandwidth - * on transmission channel, just valid if codec - * is created to work in wideband mode. - * 1 -> User sets a frame length and a target bit - * rate which is taken as the maximum - * short-term average bit rate. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_EncoderInit( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 CodingMode); - - - /****************************************************************************** - * WebRtcIsac_Encode(...) - * - * This function encodes 10ms audio blocks and inserts it into a package. - * Input speech length has 160 samples if operating at 16 kHz sampling - * rate, or 320 if operating at 32 kHz sampling rate. The encoder buffers the - * input audio until the whole frame is buffered then proceeds with encoding. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : 0 - The buffer didn't reach the chosen - * frame-size so it keeps buffering speech - * samples. - * : -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_Encode( - ISACStruct* ISAC_main_inst, - const WebRtc_Word16* speechIn, - WebRtc_Word16* encoded); - - - /****************************************************************************** - * WebRtcIsac_DecoderInit(...) - * - * This function initializes an ISAC instance prior to the decoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - * Return value - * : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_DecoderInit( - ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_UpdateBwEstimate(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - send_ts : the RTP send timestamp, given in samples - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_UpdateBwEstimate( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts); - - - /****************************************************************************** - * WebRtcIsac_Decode(...) - * - * This function decodes an ISAC frame. At 16 kHz sampling rate, the length - * of the output audio could be either 480 or 960 samples, equivalent to - * 30 or 60 ms respectively. At 32 kHz sampling rate, the length of the - * output audio is 960 samples, which is 30 ms. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - len : bytes in encoded vector. - * - * Output: - * - decoded : The decoded vector. - * - * Return value : >0 - number of samples in decoded vector. - * -1 - Error. - */ - - WebRtc_Word16 WebRtcIsac_Decode( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word16 len, - WebRtc_Word16* decoded, - WebRtc_Word16* speechType); - - - /****************************************************************************** - * WebRtcIsac_DecodePlc(...) - * - * This function conducts PLC for ISAC frame(s). Output speech length - * will be a multiple of frames, i.e. multiples of 30 ms audio. Therefore, - * the output is multiple of 480 samples if operating at 16 kHz and multiple - * of 960 if operating at 32 kHz. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames to produce. - * - * Output: - * - decoded : The decoded vector. - * - * Return value : >0 - number of samples in decoded PLC vector - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_DecodePlc( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* decoded, - WebRtc_Word16 noOfLostFrames); - - - /****************************************************************************** - * WebRtcIsac_Control(...) - * - * This function sets the limit on the short-term average bit-rate and the - * frame length. Should be used only in Instantaneous mode. At 16 kHz sampling - * rate, an average bit-rate between 10000 to 32000 bps is valid and a - * frame-size of 30 or 60 ms is acceptable. At 32 kHz, an average bit-rate - * between 10000 to 56000 is acceptable, and the valid frame-size is 30 ms. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rate : limit on the short-term average bit rate, - * in bits/second. - * - framesize : frame-size in millisecond. - * - * Return value : 0 - ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_Control( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 rate, - WebRtc_Word16 framesize); - - - /****************************************************************************** - * WebRtcIsac_ControlBwe(...) - * - * This function sets the initial values of bottleneck and frame-size if - * iSAC is used in channel-adaptive mode. Therefore, this API is not - * applicable if the codec is created to operate in super-wideband mode. - * - * Through this API, users can enforce a frame-size for all values of - * bottleneck. Then iSAC will not automatically change the frame-size. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rateBPS : initial value of bottleneck in bits/second - * 10000 <= rateBPS <= 56000 is accepted - * For default bottleneck set rateBPS = 0 - * - frameSizeMs : number of milliseconds per frame (30 or 60) - * - enforceFrameSize : 1 to enforce the given frame-size through - * out the adaptation process, 0 to let iSAC - * change the frame-size if required. - * - * Return value : 0 - ok - * -1 - Error - */ - - WebRtc_Word16 WebRtcIsac_ControlBwe( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 rateBPS, - WebRtc_Word16 frameSizeMs, - WebRtc_Word16 enforceFrameSize); - - - /****************************************************************************** - * WebRtcIsac_ReadFrameLen(...) - * - * This function returns the length of the frame represented in the packet. - * - * Input: - * - encoded : Encoded bit-stream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - */ - - WebRtc_Word16 WebRtcIsac_ReadFrameLen( - ISACStruct* ISAC_main_inst, - const WebRtc_Word16* encoded, - WebRtc_Word16* frameLength); - - - /****************************************************************************** - * WebRtcIsac_version(...) - * - * This function returns the version number. - * - * Output: - * - version : Pointer to character string - * - */ - - void WebRtcIsac_version( - char *version); - - - /****************************************************************************** - * WebRtcIsac_GetErrorCode(...) - * - * This function can be used to check the error code of an iSAC instance. When - * a function returns -1 a error code will be set for that instance. The - * function below extract the code of the last error that occurred in the - * specified instance. - * - * Input: - * - ISAC_main_inst : ISAC instance - * - * Return value : Error code - */ - - WebRtc_Word16 WebRtcIsac_GetErrorCode( - ISACStruct* ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsac_GetUplinkBw(...) - * - * This function outputs the target bottleneck of the codec. In - * channel-adaptive mode, the target bottleneck is specified through in-band - * signalling retreived by bandwidth estimator. - * In channel-independent, also called instantaneous mode, the target - * bottleneck is provided to the encoder by calling xxx_control(...). If - * xxx_control is never called the default values is returned. The default - * value for bottleneck at 16 kHz encoder sampling rate is 32000 bits/sec, - * and it is 56000 bits/sec for 32 kHz sampling rate. - * Note that the output is the iSAC internal operating bottleneck which might - * differ slightly from the one provided through xxx_control(). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Output: - * - *bottleneck : bottleneck in bits/sec - * - * Return value : -1 if error happens - * 0 bit-rates computed correctly. - */ - - WebRtc_Word16 WebRtcIsac_GetUplinkBw( - ISACStruct* ISAC_main_inst, - WebRtc_Word32* bottleneck); - - - /****************************************************************************** - * WebRtcIsac_SetMaxPayloadSize(...) - * - * This function sets a limit for the maximum payload size of iSAC. The same - * value is used both for 30 and 60 ms packets. If the encoder sampling rate - * is 16 kHz the maximum payload size is between 120 and 400 bytes. If the - * encoder sampling rate is 32 kHz the maximum payload size is between 120 - * and 600 bytes. - * - * If an out of range limit is used, the function returns -1, but the closest - * valid value will be applied. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, i.e. min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxPayloadBytes : maximum size of the payload in bytes - * valid values are between 120 and 400 bytes - * if encoder sampling rate is 16 kHz. For - * 32 kHz encoder sampling rate valid values - * are between 120 and 600 bytes. - * - * Return value : 0 if successful - * -1 if error happens - */ - - WebRtc_Word16 WebRtcIsac_SetMaxPayloadSize( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 maxPayloadBytes); - - - /****************************************************************************** - * WebRtcIsac_SetMaxRate(...) - * - * This function sets the maximum rate which the codec may not exceed for - * any signal packet. The maximum rate is defined and payload-size per - * frame-size in bits per second. - * - * The codec has a maximum rate of 53400 bits per second (200 bytes per 30 - * ms) if the encoder sampling rate is 16kHz, and 160 kbps (600 bytes/30 ms) - * if the encoder sampling rate is 32 kHz. - * - * It is possible to set a maximum rate between 32000 and 53400 bits/sec - * in wideband mode, and 32000 to 160000 bits/sec in super-wideband mode. - * - * If an out of range limit is used, the function returns -1, but the closest - * valid value will be applied. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxRate : maximum rate in bits per second, - * valid values are 32000 to 53400 bits/sec in - * wideband mode, and 32000 to 160000 bits/sec in - * super-wideband mode. - * - * Return value : 0 if successful - * -1 if error happens - */ - - WebRtc_Word16 WebRtcIsac_SetMaxRate( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 maxRate); - - - /****************************************************************************** - * WebRtcIsac_DecSampRate() - * Return the sampling rate of the decoded audio. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : enumerator representing sampling frequency - * associated with the decoder, i.e. the - * sampling rate of the decoded audio. - * - */ - - enum IsacSamplingRate WebRtcIsac_DecSampRate( - ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_EncSampRate() - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : enumerator representing sampling frequency - * associated with the encoder, the input audio - * is expected to be sampled at this rate. - * - */ - - enum IsacSamplingRate WebRtcIsac_EncSampRate( - ISACStruct* ISAC_main_inst); - - - /****************************************************************************** - * WebRtcIsac_SetDecSampRate() - * Set the sampling rate of the decoder. Initialization of the decoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : enumerator specifying the sampling rate. - * - * Return value : 0 if successful - * -1 if failed. - */ - - WebRtc_Word16 WebRtcIsac_SetDecSampRate( - ISACStruct* ISAC_main_inst, - enum IsacSamplingRate sampRate); - - - /****************************************************************************** - * WebRtcIsac_SetEncSampRate() - * Set the sampling rate of the encoder. Initialization of the encoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. The encoding-mode and the - * bottleneck remain unchanged by this call, however, the maximum rate and - * maximum payload-size will reset to their default value. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : enumerator specifying the sampling rate. - * - * Return value : 0 if successful - * -1 if failed. - */ - - WebRtc_Word16 WebRtcIsac_SetEncSampRate( - ISACStruct* ISAC_main_inst, - enum IsacSamplingRate sampRate); - - - - /****************************************************************************** - * WebRtcIsac_GetNewBitStream(...) - * - * This function returns encoded data, with the recieved bwe-index in the - * stream. If the rate is set to a value less than bottleneck of codec - * the new bistream will be re-encoded with the given target rate. - * It should always return a complete packet, i.e. only called once - * even for 60 msec frames. - * - * NOTE 1! This function does not write in the ISACStruct, it is not allowed. - * NOTE 2! Currently not implemented for SWB mode. - * NOTE 3! Rates larger than the bottleneck of the codec will be limited - * to the current bottleneck. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - bweIndex : Index of bandwidth estimate to put in new - * bitstream - * - rate : target rate of the transcoder is bits/sec. - * Valid values are the accepted rate in iSAC, - * i.e. 10000 to 56000. - * - isRCU : if the new bit-stream is an RCU stream. - * Note that the rate parameter always indicates - * the target rate of the main paylaod, regardless - * of 'isRCU' value. - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error or called in SWB mode - * NOTE! No error code is written to - * the struct since it is only allowed to read - * the struct. - */ - WebRtc_Word16 WebRtcIsac_GetNewBitStream( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 bweIndex, - WebRtc_Word16 jitterInfo, - WebRtc_Word32 rate, - WebRtc_Word16* encoded, - WebRtc_Word16 isRCU); - - - - /**************************************************************************** - * WebRtcIsac_GetDownLinkBwIndex(...) - * - * This function returns index representing the Bandwidth estimate from - * other side to this side. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - bweIndex : Bandwidth estimate to transmit to other side. - * - */ - - WebRtc_Word16 WebRtcIsac_GetDownLinkBwIndex( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* bweIndex, - WebRtc_Word16* jitterInfo); - - - /**************************************************************************** - * WebRtcIsac_UpdateUplinkBw(...) - * - * This function takes an index representing the Bandwidth estimate from - * this side to other side and updates BWE. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - bweIndex : Bandwidth estimate from other side. - * - */ - - WebRtc_Word16 WebRtcIsac_UpdateUplinkBw( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 bweIndex); - - - /**************************************************************************** - * WebRtcIsac_ReadBwIndex(...) - * - * This function returns the index of the Bandwidth estimate from the bitstream. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - bweIndex : Bandwidth estimate in bitstream - * - */ - - WebRtc_Word16 WebRtcIsac_ReadBwIndex( - const WebRtc_Word16* encoded, - WebRtc_Word16* bweIndex); - - - - /******************************************************************************* - * WebRtcIsac_GetNewFrameLen(...) - * - * returns the frame lenght (in samples) of the next packet. In the case of channel-adaptive - * mode, iSAC decides on its frame lenght based on the estimated bottleneck - * this allows a user to prepare for the next packet (at the encoder) - * - * The primary usage is in CE to make the iSAC works in channel-adaptive mode - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Return Value : frame lenght in samples - * - */ - - WebRtc_Word16 WebRtcIsac_GetNewFrameLen( - ISACStruct* ISAC_main_inst); - - - /**************************************************************************** - * WebRtcIsac_GetRedPayload(...) - * - * Populates "encoded" with the redundant payload of the recently encoded - * frame. This function has to be called once that WebRtcIsac_Encode(...) - * returns a positive value. Regardless of the frame-size this function will - * be called only once after encoding is completed. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - encoded : the encoded data vector - * - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : -1 - Error - * - * - */ - WebRtc_Word16 WebRtcIsac_GetRedPayload( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* encoded); - - - /**************************************************************************** - * WebRtcIsac_DecodeRcu(...) - * - * This function decodes a redundant (RCU) iSAC frame. Function is called in - * NetEq with a stored RCU payload i case of packet loss. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC RCU frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - WebRtc_Word16 WebRtcIsac_DecodeRcu( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word16 len, - WebRtc_Word16* decoded, - WebRtc_Word16* speechType); - - -#if defined(__cplusplus) -} -#endif - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INTERFACE_ISAC_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/Android.mk b/modules/audio_coding/codecs/iSAC/main/source/Android.mk deleted file mode 100644 index aee5e50a9..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/Android.mk +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_isac -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := arith_routines.c \ - arith_routines_hist.c \ - arith_routines_logist.c \ - bandwidth_estimator.c \ - crc.c \ - decode.c \ - decode_bwe.c \ - encode.c \ - encode_lpc_swb.c \ - entropy_coding.c \ - fft.c \ - filter_functions.c \ - filterbank_tables.c \ - intialize.c \ - isac.c \ - filterbanks.c \ - pitch_lag_tables.c \ - lattice.c \ - lpc_gain_swb_tables.c \ - lpc_analysis.c \ - lpc_shape_swb12_tables.c \ - lpc_shape_swb16_tables.c \ - lpc_tables.c \ - pitch_estimator.c \ - pitch_filter.c \ - pitch_gain_tables.c \ - spectrum_ar_model_tables.c \ - transform.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/iSAC/main/source/arith_routines.c b/modules/audio_coding/codecs/iSAC/main/source/arith_routines.c deleted file mode 100644 index 31c441a7a..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/arith_routines.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "arith_routines.h" -#include "settings.h" - - -/* - * terminate and return byte stream; - * returns the number of bytes in the stream - */ -int WebRtcIsac_EncTerminate(Bitstr *streamdata) /* in-/output struct containing bitstream */ -{ - WebRtc_UWord8 *stream_ptr; - - - /* point to the right place in the stream buffer */ - stream_ptr = streamdata->stream + streamdata->stream_index; - - /* find minimum length (determined by current interval width) */ - if ( streamdata->W_upper > 0x01FFFFFF ) - { - streamdata->streamval += 0x01000000; - /* add carry to buffer */ - if (streamdata->streamval < 0x01000000) - { - /* propagate carry */ - while ( !(++(*--stream_ptr)) ); - /* put pointer back to the old value */ - stream_ptr = streamdata->stream + streamdata->stream_index; - } - /* write remaining data to bitstream */ - *stream_ptr++ = (WebRtc_UWord8) (streamdata->streamval >> 24); - } - else - { - streamdata->streamval += 0x00010000; - /* add carry to buffer */ - if (streamdata->streamval < 0x00010000) - { - /* propagate carry */ - while ( !(++(*--stream_ptr)) ); - /* put pointer back to the old value */ - stream_ptr = streamdata->stream + streamdata->stream_index; - } - /* write remaining data to bitstream */ - *stream_ptr++ = (WebRtc_UWord8) (streamdata->streamval >> 24); - *stream_ptr++ = (WebRtc_UWord8) ((streamdata->streamval >> 16) & 0x00FF); - } - - /* calculate stream length */ - return (int)(stream_ptr - streamdata->stream); -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/arith_routines.h b/modules/audio_coding/codecs/iSAC/main/source/arith_routines.h deleted file mode 100644 index 8e5f496f9..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/arith_routines.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routines.h - * - * Functions for arithmetic coding. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ - -#include "structs.h" - - -int WebRtcIsac_EncLogisticMulti2( - Bitstr *streamdata, /* in-/output struct containing bitstream */ - WebRtc_Word16 *dataQ7, /* input: data vector */ - const WebRtc_UWord16 *env, /* input: side info vector defining the width of the pdf */ - const int N, /* input: data vector length */ - const WebRtc_Word16 isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ - -/* returns the number of bytes in the stream */ -int WebRtcIsac_EncTerminate(Bitstr *streamdata); /* in-/output struct containing bitstream */ - -/* returns the number of bytes in the stream so far */ -int WebRtcIsac_DecLogisticMulti2( - WebRtc_Word16 *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 *env, /* input: side info vector defining the width of the pdf */ - const WebRtc_Word16 *dither, /* input: dither vector */ - const int N, /* input: data vector length */ - const WebRtc_Word16 isSWB12kHz); /* if the codec is working in 12kHz bandwidth */ - -void WebRtcIsac_EncHistMulti( - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const int *data, /* input: data vector */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const int N); /* input: data vector length */ - -int WebRtcIsac_DecHistBisectMulti( - int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const WebRtc_UWord16 *cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ - const int N); /* input: data vector length */ - -int WebRtcIsac_DecHistOneStepMulti( - int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const WebRtc_UWord16 *init_index,/* input: vector of initial cdf table search entries */ - const int N); /* input: data vector length */ - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ARITH_ROUTINES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/arith_routines_hist.c b/modules/audio_coding/codecs/iSAC/main/source/arith_routines_hist.c deleted file mode 100644 index f4a13d607..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/arith_routines_hist.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "settings.h" -#include "arith_routines.h" - - -/* - * code symbols into arithmetic bytestream - */ -void WebRtcIsac_EncHistMulti(Bitstr *streamdata, /* in-/output struct containing bitstream */ - const int *data, /* input: data vector */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const int N) /* input: data vector length */ -{ - WebRtc_UWord32 W_lower, W_upper; - WebRtc_UWord32 W_upper_LSB, W_upper_MSB; - WebRtc_UWord8 *stream_ptr; - WebRtc_UWord8 *stream_ptr_carry; - WebRtc_UWord32 cdf_lo, cdf_hi; - int k; - - - /* point to beginning of stream buffer */ - stream_ptr = streamdata->stream + streamdata->stream_index; - W_upper = streamdata->W_upper; - - for (k=N; k>0; k--) - { - /* fetch cdf_lower and cdf_upper from cdf tables */ - cdf_lo = (WebRtc_UWord32) *(*cdf + *data); - cdf_hi = (WebRtc_UWord32) *(*cdf++ + *data++ + 1); - - /* update interval */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = W_upper >> 16; - W_lower = W_upper_MSB * cdf_lo; - W_lower += (W_upper_LSB * cdf_lo) >> 16; - W_upper = W_upper_MSB * cdf_hi; - W_upper += (W_upper_LSB * cdf_hi) >> 16; - - /* shift interval such that it begins at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamdata->streamval += W_lower; - - /* handle carry */ - if (streamdata->streamval < W_lower) - { - /* propagate carry */ - stream_ptr_carry = stream_ptr; - while (!(++(*--stream_ptr_carry))); - } - - /* renormalize interval, store most significant byte of streamval and update streamval */ - while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ - { - W_upper <<= 8; - *stream_ptr++ = (WebRtc_UWord8) (streamdata->streamval >> 24); - streamdata->streamval <<= 8; - } - } - - /* calculate new stream_index */ - streamdata->stream_index = (int)(stream_ptr - streamdata->stream); - streamdata->W_upper = W_upper; - - return; -} - - - -/* - * function to decode more symbols from the arithmetic bytestream, using method of bisection - * cdf tables should be of size 2^k-1 (which corresponds to an alphabet size of 2^k-2) - */ -int WebRtcIsac_DecHistBisectMulti(int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const WebRtc_UWord16 *cdf_size, /* input: array of cdf table sizes+1 (power of two: 2^k) */ - const int N) /* input: data vector length */ -{ - WebRtc_UWord32 W_lower, W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord32 W_upper_LSB, W_upper_MSB; - WebRtc_UWord32 streamval; - const WebRtc_UWord8 *stream_ptr; - const WebRtc_UWord16 *cdf_ptr; - int size_tmp; - int k; - - W_lower = 0; //to remove warning -DH - stream_ptr = streamdata->stream + streamdata->stream_index; - W_upper = streamdata->W_upper; - if (W_upper == 0) - /* Should not be possible in normal operation */ - return -2; - - if (streamdata->stream_index == 0) /* first time decoder is called for this stream */ - { - /* read first word from bytestream */ - streamval = *stream_ptr << 24; - streamval |= *++stream_ptr << 16; - streamval |= *++stream_ptr << 8; - streamval |= *++stream_ptr; - } else { - streamval = streamdata->streamval; - } - - for (k=N; k>0; k--) - { - /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = W_upper >> 16; - - /* start halfway the cdf range */ - size_tmp = *cdf_size++ >> 1; - cdf_ptr = *cdf + (size_tmp - 1); - - /* method of bisection */ - for ( ;; ) - { - W_tmp = W_upper_MSB * *cdf_ptr; - W_tmp += (W_upper_LSB * *cdf_ptr) >> 16; - size_tmp >>= 1; - if (size_tmp == 0) break; - if (streamval > W_tmp) - { - W_lower = W_tmp; - cdf_ptr += size_tmp; - } else { - W_upper = W_tmp; - cdf_ptr -= size_tmp; - } - } - if (streamval > W_tmp) - { - W_lower = W_tmp; - *data++ = (int)(cdf_ptr - *cdf++); - } else { - W_upper = W_tmp; - *data++ = (int)(cdf_ptr - *cdf++ - 1); - } - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamval -= W_lower; - - /* renormalize interval and update streamval */ - while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ - { - /* read next byte from stream */ - streamval = (streamval << 8) | *++stream_ptr; - W_upper <<= 8; - } - - if (W_upper == 0) - /* Should not be possible in normal operation */ - return -2; - - - } - - streamdata->stream_index = (int)(stream_ptr - streamdata->stream); - streamdata->W_upper = W_upper; - streamdata->streamval = streamval; - - - /* find number of bytes in original stream (determined by current interval width) */ - if ( W_upper > 0x01FFFFFF ) - return streamdata->stream_index - 2; - else - return streamdata->stream_index - 1; -} - - - -/* - * function to decode more symbols from the arithmetic bytestream, taking single step up or - * down at a time - * cdf tables can be of arbitrary size, but large tables may take a lot of iterations - */ -int WebRtcIsac_DecHistOneStepMulti(int *data, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 **cdf, /* input: array of cdf arrays */ - const WebRtc_UWord16 *init_index, /* input: vector of initial cdf table search entries */ - const int N) /* input: data vector length */ -{ - WebRtc_UWord32 W_lower, W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord32 W_upper_LSB, W_upper_MSB; - WebRtc_UWord32 streamval; - const WebRtc_UWord8 *stream_ptr; - const WebRtc_UWord16 *cdf_ptr; - int k; - - - stream_ptr = streamdata->stream + streamdata->stream_index; - W_upper = streamdata->W_upper; - if (W_upper == 0) - /* Should not be possible in normal operation */ - return -2; - - if (streamdata->stream_index == 0) /* first time decoder is called for this stream */ - { - /* read first word from bytestream */ - streamval = *stream_ptr << 24; - streamval |= *++stream_ptr << 16; - streamval |= *++stream_ptr << 8; - streamval |= *++stream_ptr; - } else { - streamval = streamdata->streamval; - } - - - for (k=N; k>0; k--) - { - /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = W_upper >> 16; - - /* start at the specified table entry */ - cdf_ptr = *cdf + (*init_index++); - W_tmp = W_upper_MSB * *cdf_ptr; - W_tmp += (W_upper_LSB * *cdf_ptr) >> 16; - if (streamval > W_tmp) - { - for ( ;; ) - { - W_lower = W_tmp; - if (cdf_ptr[0]==65535) - /* range check */ - return -3; - W_tmp = W_upper_MSB * *++cdf_ptr; - W_tmp += (W_upper_LSB * *cdf_ptr) >> 16; - if (streamval <= W_tmp) break; - } - W_upper = W_tmp; - *data++ = (int)(cdf_ptr - *cdf++ - 1); - } else { - for ( ;; ) - { - W_upper = W_tmp; - --cdf_ptr; - if (cdf_ptr<*cdf) { - /* range check */ - return -3; - } - W_tmp = W_upper_MSB * *cdf_ptr; - W_tmp += (W_upper_LSB * *cdf_ptr) >> 16; - if (streamval > W_tmp) break; - } - W_lower = W_tmp; - *data++ = (int)(cdf_ptr - *cdf++); - } - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - /* add integer to bitstream */ - streamval -= W_lower; - - /* renormalize interval and update streamval */ - while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ - { - /* read next byte from stream */ - streamval = (streamval << 8) | *++stream_ptr; - W_upper <<= 8; - } - } - - streamdata->stream_index = (int)(stream_ptr - streamdata->stream); - streamdata->W_upper = W_upper; - streamdata->streamval = streamval; - - - /* find number of bytes in original stream (determined by current interval width) */ - if ( W_upper > 0x01FFFFFF ) - return streamdata->stream_index - 2; - else - return streamdata->stream_index - 1; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/arith_routines_logist.c b/modules/audio_coding/codecs/iSAC/main/source/arith_routines_logist.c deleted file mode 100644 index 422855a4c..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/arith_routines_logist.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * arith_routines.h - * - * This file contains functions for arithmatically encoding and - * decoding DFT coefficients. - * - */ - - -#include "arith_routines.h" - - - -static const WebRtc_Word32 kHistEdgesQ15[51] = { - -327680, -314573, -301466, -288359, -275252, -262144, -249037, -235930, -222823, -209716, - -196608, -183501, -170394, -157287, -144180, -131072, -117965, -104858, -91751, -78644, - -65536, -52429, -39322, -26215, -13108, 0, 13107, 26214, 39321, 52428, - 65536, 78643, 91750, 104857, 117964, 131072, 144179, 157286, 170393, 183500, - 196608, 209715, 222822, 235929, 249036, 262144, 275251, 288358, 301465, 314572, - 327680}; - - -static const int kCdfSlopeQ0[51] = { /* Q0 */ - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 13, 23, 47, 87, 154, 315, 700, 1088, - 2471, 6064, 14221, 21463, 36634, 36924, 19750, 13270, 5806, 2312, - 1095, 660, 316, 145, 86, 41, 32, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 0}; - - -static const int kCdfQ16[51] = { /* Q16 */ - 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, - 20, 22, 24, 29, 38, 57, 92, 153, 279, 559, - 994, 1983, 4408, 10097, 18682, 33336, 48105, 56005, 61313, 63636, - 64560, 64998, 65262, 65389, 65447, 65481, 65497, 65510, 65512, 65514, - 65516, 65518, 65520, 65522, 65524, 65526, 65528, 65530, 65532, 65534, - 65535}; - - - -/* function to be converted to fixed point */ -static __inline WebRtc_UWord32 piecewise(WebRtc_Word32 xinQ15) { - - WebRtc_Word32 ind, qtmp1, qtmp2, qtmp3; - WebRtc_UWord32 tmpUW32; - - - qtmp2 = xinQ15; - - if (qtmp2 < kHistEdgesQ15[0]) { - qtmp2 = kHistEdgesQ15[0]; - } - if (qtmp2 > kHistEdgesQ15[50]) { - qtmp2 = kHistEdgesQ15[50]; - } - - qtmp1 = qtmp2 - kHistEdgesQ15[0]; /* Q15 - Q15 = Q15 */ - ind = (qtmp1 * 5) >> 16; /* 2^16 / 5 = 0.4 in Q15 */ - /* Q15 -> Q0 */ - qtmp1 = qtmp2 - kHistEdgesQ15[ind]; /* Q15 - Q15 = Q15 */ - qtmp2 = kCdfSlopeQ0[ind] * qtmp1; /* Q0 * Q15 = Q15 */ - qtmp3 = qtmp2>>15; /* Q15 -> Q0 */ - - tmpUW32 = kCdfQ16[ind] + qtmp3; /* Q0 + Q0 = Q0 */ - return tmpUW32; -} - - - -int WebRtcIsac_EncLogisticMulti2( - Bitstr *streamdata, /* in-/output struct containing bitstream */ - WebRtc_Word16 *dataQ7, /* input: data vector */ - const WebRtc_UWord16 *envQ8, /* input: side info vector defining the width of the pdf */ - const int N, /* input: data vector length / 2 */ - const WebRtc_Word16 isSWB12kHz) -{ - WebRtc_UWord32 W_lower, W_upper; - WebRtc_UWord32 W_upper_LSB, W_upper_MSB; - WebRtc_UWord8 *stream_ptr; - WebRtc_UWord8 *maxStreamPtr; - WebRtc_UWord8 *stream_ptr_carry; - WebRtc_UWord32 cdf_lo, cdf_hi; - int k; - - /* point to beginning of stream buffer */ - stream_ptr = streamdata->stream + streamdata->stream_index; - W_upper = streamdata->W_upper; - - maxStreamPtr = streamdata->stream + STREAM_SIZE_MAX_60 - 1; - for (k = 0; k < N; k++) - { - /* compute cdf_lower and cdf_upper by evaluating the piecewise linear cdf */ - cdf_lo = piecewise((*dataQ7 - 64) * *envQ8); - cdf_hi = piecewise((*dataQ7 + 64) * *envQ8); - - /* test and clip if probability gets too small */ - while (cdf_lo+1 >= cdf_hi) { - /* clip */ - if (*dataQ7 > 0) { - *dataQ7 -= 128; - cdf_hi = cdf_lo; - cdf_lo = piecewise((*dataQ7 - 64) * *envQ8); - } else { - *dataQ7 += 128; - cdf_lo = cdf_hi; - cdf_hi = piecewise((*dataQ7 + 64) * *envQ8); - } - } - - dataQ7++; - // increment only once per 4 iterations for SWB-16kHz or WB - // increment only once per 2 iterations for SWB-12kHz - envQ8 += (isSWB12kHz)? (k & 1):((k & 1) & (k >> 1)); - - - /* update interval */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = W_upper >> 16; - W_lower = W_upper_MSB * cdf_lo; - W_lower += (W_upper_LSB * cdf_lo) >> 16; - W_upper = W_upper_MSB * cdf_hi; - W_upper += (W_upper_LSB * cdf_hi) >> 16; - - /* shift interval such that it begins at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamdata->streamval += W_lower; - - /* handle carry */ - if (streamdata->streamval < W_lower) - { - /* propagate carry */ - stream_ptr_carry = stream_ptr; - while (!(++(*--stream_ptr_carry))); - } - - /* renormalize interval, store most significant byte of streamval and update streamval */ - while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ - { - W_upper <<= 8; - *stream_ptr++ = (WebRtc_UWord8) (streamdata->streamval >> 24); - - if(stream_ptr > maxStreamPtr) - { - return -ISAC_DISALLOWED_BITSTREAM_LENGTH; - } - streamdata->streamval <<= 8; - } - } - - /* calculate new stream_index */ - streamdata->stream_index = (int)(stream_ptr - streamdata->stream); - streamdata->W_upper = W_upper; - - return 0; -} - - - -int WebRtcIsac_DecLogisticMulti2( - WebRtc_Word16 *dataQ7, /* output: data vector */ - Bitstr *streamdata, /* in-/output struct containing bitstream */ - const WebRtc_UWord16 *envQ8, /* input: side info vector defining the width of the pdf */ - const WebRtc_Word16 *ditherQ7,/* input: dither vector */ - const int N, /* input: data vector length */ - const WebRtc_Word16 isSWB12kHz) -{ - WebRtc_UWord32 W_lower, W_upper; - WebRtc_UWord32 W_tmp; - WebRtc_UWord32 W_upper_LSB, W_upper_MSB; - WebRtc_UWord32 streamval; - const WebRtc_UWord8 *stream_ptr; - WebRtc_UWord32 cdf_tmp; - WebRtc_Word16 candQ7; - int k; - - stream_ptr = streamdata->stream + streamdata->stream_index; - W_upper = streamdata->W_upper; - if (streamdata->stream_index == 0) /* first time decoder is called for this stream */ - { - /* read first word from bytestream */ - streamval = *stream_ptr << 24; - streamval |= *++stream_ptr << 16; - streamval |= *++stream_ptr << 8; - streamval |= *++stream_ptr; - } else { - streamval = streamdata->streamval; - } - - - for (k = 0; k < N; k++) - { - /* find the integer *data for which streamval lies in [W_lower+1, W_upper] */ - W_upper_LSB = W_upper & 0x0000FFFF; - W_upper_MSB = W_upper >> 16; - - /* find first candidate by inverting the logistic cdf */ - candQ7 = - *ditherQ7 + 64; - cdf_tmp = piecewise(candQ7 * *envQ8); - - W_tmp = W_upper_MSB * cdf_tmp; - W_tmp += (W_upper_LSB * cdf_tmp) >> 16; - if (streamval > W_tmp) - { - W_lower = W_tmp; - candQ7 += 128; - cdf_tmp = piecewise(candQ7 * *envQ8); - - W_tmp = W_upper_MSB * cdf_tmp; - W_tmp += (W_upper_LSB * cdf_tmp) >> 16; - while (streamval > W_tmp) - { - W_lower = W_tmp; - candQ7 += 128; - cdf_tmp = piecewise(candQ7 * *envQ8); - - W_tmp = W_upper_MSB * cdf_tmp; - W_tmp += (W_upper_LSB * cdf_tmp) >> 16; - - /* error check */ - if (W_lower == W_tmp) return -1; - } - W_upper = W_tmp; - - /* another sample decoded */ - *dataQ7 = candQ7 - 64; - } - else - { - W_upper = W_tmp; - candQ7 -= 128; - cdf_tmp = piecewise(candQ7 * *envQ8); - - W_tmp = W_upper_MSB * cdf_tmp; - W_tmp += (W_upper_LSB * cdf_tmp) >> 16; - while ( !(streamval > W_tmp) ) - { - W_upper = W_tmp; - candQ7 -= 128; - cdf_tmp = piecewise(candQ7 * *envQ8); - - W_tmp = W_upper_MSB * cdf_tmp; - W_tmp += (W_upper_LSB * cdf_tmp) >> 16; - - /* error check */ - if (W_upper == W_tmp) return -1; - } - W_lower = W_tmp; - - /* another sample decoded */ - *dataQ7 = candQ7 + 64; - } - ditherQ7++; - dataQ7++; - // increment only once per 4 iterations for SWB-16kHz or WB - // increment only once per 2 iterations for SWB-12kHz - envQ8 += (isSWB12kHz)? (k & 1):((k & 1) & (k >> 1)); - - /* shift interval to start at zero */ - W_upper -= ++W_lower; - - /* add integer to bitstream */ - streamval -= W_lower; - - /* renormalize interval and update streamval */ - while ( !(W_upper & 0xFF000000) ) /* W_upper < 2^24 */ - { - /* read next byte from stream */ - streamval = (streamval << 8) | *++stream_ptr; - W_upper <<= 8; - } - } - - streamdata->stream_index = (int)(stream_ptr - streamdata->stream); - streamdata->W_upper = W_upper; - streamdata->streamval = streamval; - - /* find number of bytes in original stream (determined by current interval width) */ - if ( W_upper > 0x01FFFFFF ) - return streamdata->stream_index - 2; - else - return streamdata->stream_index - 1; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.c b/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.c deleted file mode 100644 index 6225eb12a..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.c +++ /dev/null @@ -1,1028 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * BwEstimator.c - * - * This file contains the code for the Bandwidth Estimator designed - * for iSAC. - * - */ - -#include "bandwidth_estimator.h" -#include "settings.h" -#include "isac.h" - -#include - -/* array of quantization levels for bottle neck info; Matlab code: */ -/* sprintf('%4.1ff, ', logspace(log10(5000), log10(40000), 12)) */ -const float WebRtcIsac_kQRateTableWb[12] = -{ - 10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f, - 18859.8f, 20963.3f, 23301.4f, 25900.3f, 28789.0f, 32000.0f}; - - -const float WebRtcIsac_kQRateTableSwb[24] = -{ - 10000.0f, 11115.3f, 12355.1f, 13733.1f, 15264.8f, 16967.3f, - 18859.8f, 20963.3f, 23153.1f, 25342.9f, 27532.7f, 29722.5f, - 31912.3f, 34102.1f, 36291.9f, 38481.7f, 40671.4f, 42861.2f, - 45051.0f, 47240.8f, 49430.6f, 51620.4f, 53810.2f, 56000.0f, -}; - - - - -WebRtc_Word32 WebRtcIsac_InitBandwidthEstimator( - BwEstimatorstr* bwest_str, - enum IsacSamplingRate encoderSampRate, - enum IsacSamplingRate decoderSampRate) -{ - switch(encoderSampRate) - { - case kIsacWideband: - { - bwest_str->send_bw_avg = INIT_BN_EST_WB; - break; - } - case kIsacSuperWideband: - { - bwest_str->send_bw_avg = INIT_BN_EST_SWB; - break; - } - default: - return -1; - } - - switch(decoderSampRate) - { - case kIsacWideband: - { - bwest_str->prev_frame_length = INIT_FRAME_LEN_WB; - bwest_str->rec_bw_inv = 1.0f / - (INIT_BN_EST_WB + INIT_HDR_RATE_WB); - bwest_str->rec_bw = (WebRtc_Word32)INIT_BN_EST_WB; - bwest_str->rec_bw_avg_Q = INIT_BN_EST_WB; - bwest_str->rec_bw_avg = INIT_BN_EST_WB + INIT_HDR_RATE_WB; - bwest_str->rec_header_rate = INIT_HDR_RATE_WB; - break; - } - case kIsacSuperWideband: - { - bwest_str->prev_frame_length = INIT_FRAME_LEN_SWB; - bwest_str->rec_bw_inv = 1.0f / - (INIT_BN_EST_SWB + INIT_HDR_RATE_SWB); - bwest_str->rec_bw = (WebRtc_Word32)INIT_BN_EST_SWB; - bwest_str->rec_bw_avg_Q = INIT_BN_EST_SWB; - bwest_str->rec_bw_avg = INIT_BN_EST_SWB + INIT_HDR_RATE_SWB; - bwest_str->rec_header_rate = INIT_HDR_RATE_SWB; - break; - } - default: - return -1; - } - - bwest_str->prev_rec_rtp_number = 0; - bwest_str->prev_rec_arr_ts = 0; - bwest_str->prev_rec_send_ts = 0; - bwest_str->prev_rec_rtp_rate = 1.0f; - bwest_str->last_update_ts = 0; - bwest_str->last_reduction_ts = 0; - bwest_str->count_tot_updates_rec = -9; - bwest_str->rec_jitter = 10.0f; - bwest_str->rec_jitter_short_term = 0.0f; - bwest_str->rec_jitter_short_term_abs = 5.0f; - bwest_str->rec_max_delay = 10.0f; - bwest_str->rec_max_delay_avg_Q = 10.0f; - bwest_str->num_pkts_rec = 0; - - bwest_str->send_max_delay_avg = 10.0f; - - bwest_str->hsn_detect_rec = 0; - - bwest_str->num_consec_rec_pkts_over_30k = 0; - - bwest_str->hsn_detect_snd = 0; - - bwest_str->num_consec_snt_pkts_over_30k = 0; - - bwest_str->in_wait_period = 0; - - bwest_str->change_to_WB = 0; - - bwest_str->numConsecLatePkts = 0; - bwest_str->consecLatency = 0; - bwest_str->inWaitLatePkts = 0; - bwest_str->senderTimestamp = 0; - bwest_str->receiverTimestamp = 0; - return 0; -} - -/* This function updates both bottle neck rates */ -/* Parameters: */ -/* rtp_number - value from RTP packet, from NetEq */ -/* frame length - length of signal frame in ms, from iSAC decoder */ -/* send_ts - value in RTP header giving send time in samples */ -/* arr_ts - value given by timeGetTime() time of arrival in samples of packet from NetEq */ -/* pksize - size of packet in bytes, from NetEq */ -/* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */ -/* returns 0 if everything went fine, -1 otherwise */ -WebRtc_Word16 WebRtcIsac_UpdateBandwidthEstimator( - BwEstimatorstr *bwest_str, - const WebRtc_UWord16 rtp_number, - const WebRtc_Word32 frame_length, - const WebRtc_UWord32 send_ts, - const WebRtc_UWord32 arr_ts, - const WebRtc_Word32 pksize - /*, const WebRtc_UWord16 Index*/) -{ - float weight = 0.0f; - float curr_bw_inv = 0.0f; - float rec_rtp_rate; - float t_diff_proj; - float arr_ts_diff; - float send_ts_diff; - float arr_time_noise; - float arr_time_noise_abs; - - WebRtc_Word32 asdf; - - float delay_correction_factor = 1; - float late_diff = 0.0f; - int immediate_set = 0; - int num_pkts_expected; - - - // We have to adjust the header-rate if the first packet has a - // frame-size different than the initialized value. - if ( frame_length != bwest_str->prev_frame_length ) - { - bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f * - 1000.0f / (float)frame_length; /* bits/s */ - } - - /* UPDATE ESTIMATES ON THIS SIDE */ - /* compute far-side transmission rate */ - rec_rtp_rate = ((float)pksize * 8.0f * 1000.0f / (float)frame_length) + - bwest_str->rec_header_rate; - // rec_rtp_rate packet bits/s + header bits/s - - /* check for timer wrap-around */ - if (arr_ts < bwest_str->prev_rec_arr_ts) - { - bwest_str->prev_rec_arr_ts = arr_ts; - bwest_str->last_update_ts = arr_ts; - bwest_str->last_reduction_ts = arr_ts + 3*FS; - bwest_str->num_pkts_rec = 0; - - /* store frame length */ - bwest_str->prev_frame_length = frame_length; - - /* store far-side transmission rate */ - bwest_str->prev_rec_rtp_rate = rec_rtp_rate; - - /* store far-side RTP time stamp */ - bwest_str->prev_rec_rtp_number = rtp_number; - - return 0; - } - - bwest_str->num_pkts_rec++; - - /* check that it's not one of the first 9 packets */ - if ( bwest_str->count_tot_updates_rec > 0 ) - { - if(bwest_str->in_wait_period > 0 ) - { - bwest_str->in_wait_period--; - } - - bwest_str->inWaitLatePkts -= ((bwest_str->inWaitLatePkts > 0)? 1:0); - send_ts_diff = (float)(send_ts - bwest_str->prev_rec_send_ts); - - if (send_ts_diff <= (16 * frame_length)*2) - //doesn't allow for a dropped packet, not sure necessary to be - // that strict -DH - { - /* if not been updated for a long time, reduce the BN estimate */ - if((WebRtc_UWord32)(arr_ts - bwest_str->last_update_ts) * - 1000.0f / FS > 3000) - { - //how many frames should have been received since the last - // update if too many have been dropped or there have been - // big delays won't allow this reduction may no longer need - // the send_ts_diff here - num_pkts_expected = (int)(((float)(arr_ts - - bwest_str->last_update_ts) * 1000.0f /(float) FS) / - (float)frame_length); - - if(((float)bwest_str->num_pkts_rec/(float)num_pkts_expected) > - 0.9) - { - float inv_bitrate = (float) pow( 0.99995, - (double)((WebRtc_UWord32)(arr_ts - - bwest_str->last_reduction_ts)*1000.0f/FS) ); - - if ( inv_bitrate ) - { - bwest_str->rec_bw_inv /= inv_bitrate; - - //precautionary, likely never necessary - if (bwest_str->hsn_detect_snd && - bwest_str->hsn_detect_rec) - { - if (bwest_str->rec_bw_inv > 0.000066f) - { - bwest_str->rec_bw_inv = 0.000066f; - } - } - } - else - { - bwest_str->rec_bw_inv = 1.0f / - (INIT_BN_EST_WB + INIT_HDR_RATE_WB); - } - /* reset time-since-update counter */ - bwest_str->last_reduction_ts = arr_ts; - } - else - //reset here? - { - bwest_str->last_reduction_ts = arr_ts + 3*FS; - bwest_str->last_update_ts = arr_ts; - bwest_str->num_pkts_rec = 0; - } - } - } - else - { - bwest_str->last_reduction_ts = arr_ts + 3*FS; - bwest_str->last_update_ts = arr_ts; - bwest_str->num_pkts_rec = 0; - } - - - /* temporarily speed up adaptation if frame length has changed */ - if ( frame_length != bwest_str->prev_frame_length ) - { - bwest_str->count_tot_updates_rec = 10; - bwest_str->rec_header_rate = (float)HEADER_SIZE * 8.0f * - 1000.0f / (float)frame_length; /* bits/s */ - - bwest_str->rec_bw_inv = 1.0f /((float)bwest_str->rec_bw + - bwest_str->rec_header_rate); - } - - //////////////////////// - arr_ts_diff = (float)(arr_ts - bwest_str->prev_rec_arr_ts); - - if (send_ts_diff > 0 ) - { - late_diff = arr_ts_diff - send_ts_diff; - } - else - { - late_diff = arr_ts_diff - (float)(16 * frame_length); - } - - if((late_diff > 0) && !bwest_str->inWaitLatePkts) - { - bwest_str->numConsecLatePkts++; - bwest_str->consecLatency += late_diff; - } - else - { - bwest_str->numConsecLatePkts = 0; - bwest_str->consecLatency = 0; - } - if(bwest_str->numConsecLatePkts > 50) - { - float latencyMs = bwest_str->consecLatency/(FS/1000); - float averageLatencyMs = latencyMs / bwest_str->numConsecLatePkts; - delay_correction_factor = frame_length / (frame_length + averageLatencyMs); - immediate_set = 1; - bwest_str->inWaitLatePkts = (WebRtc_Word16)((bwest_str->consecLatency/(FS/1000)) / 30);// + 150; - bwest_str->start_wait_period = arr_ts; - } - /////////////////////////////////////////////// - - - - /* update only if previous packet was not lost */ - if ( rtp_number == bwest_str->prev_rec_rtp_number + 1 ) - { - - - if (!(bwest_str->hsn_detect_snd && bwest_str->hsn_detect_rec)) - { - if ((arr_ts_diff > (float)(16 * frame_length))) - { - //1/2 second - if ((late_diff > 8000.0f) && !bwest_str->in_wait_period) - { - delay_correction_factor = 0.7f; - bwest_str->in_wait_period = 55; - bwest_str->start_wait_period = arr_ts; - immediate_set = 1; - } - //320 ms - else if (late_diff > 5120.0f && !bwest_str->in_wait_period) - { - delay_correction_factor = 0.8f; - immediate_set = 1; - bwest_str->in_wait_period = 44; - bwest_str->start_wait_period = arr_ts; - } - } - } - - - if ((bwest_str->prev_rec_rtp_rate > bwest_str->rec_bw_avg) && - (rec_rtp_rate > bwest_str->rec_bw_avg) && - !bwest_str->in_wait_period) - { - /* test if still in initiation period and increment counter */ - if (bwest_str->count_tot_updates_rec++ > 99) - { - /* constant weight after initiation part */ - weight = 0.01f; - } - else - { - /* weight decreases with number of updates */ - weight = 1.0f / (float) bwest_str->count_tot_updates_rec; - } - /* Bottle Neck Estimation */ - - /* limit outliers */ - /* if more than 25 ms too much */ - if (arr_ts_diff > frame_length * FS/1000 + 400.0f) - { - // in samples, why 25ms?? - arr_ts_diff = frame_length * FS/1000 + 400.0f; - } - if(arr_ts_diff < (frame_length * FS/1000) - 160.0f) - { - /* don't allow it to be less than frame rate - 10 ms */ - arr_ts_diff = (float)frame_length * FS/1000 - 160.0f; - } - - /* compute inverse receiving rate for last packet */ - curr_bw_inv = arr_ts_diff / ((float)(pksize + HEADER_SIZE) * - 8.0f * FS); // (180+35)*8*16000 = 27.5 Mbit.... - - - if(curr_bw_inv < - (1.0f / (MAX_ISAC_BW + bwest_str->rec_header_rate))) - { - // don't allow inv rate to be larger than MAX - curr_bw_inv = (1.0f / - (MAX_ISAC_BW + bwest_str->rec_header_rate)); - } - - /* update bottle neck rate estimate */ - bwest_str->rec_bw_inv = weight * curr_bw_inv + - (1.0f - weight) * bwest_str->rec_bw_inv; - - /* reset time-since-update counter */ - bwest_str->last_update_ts = arr_ts; - bwest_str->last_reduction_ts = arr_ts + 3 * FS; - bwest_str->num_pkts_rec = 0; - - /* Jitter Estimation */ - /* projected difference between arrival times */ - t_diff_proj = ((float)(pksize + HEADER_SIZE) * 8.0f * - 1000.0f) / bwest_str->rec_bw_avg; - - - // difference between projected and actual - // arrival time differences - arr_time_noise = (float)(arr_ts_diff*1000.0f/FS) - - t_diff_proj; - arr_time_noise_abs = (float) fabs( arr_time_noise ); - - /* long term averaged absolute jitter */ - bwest_str->rec_jitter = weight * arr_time_noise_abs + - (1.0f - weight) * bwest_str->rec_jitter; - if (bwest_str->rec_jitter > 10.0f) - { - bwest_str->rec_jitter = 10.0f; - } - /* short term averaged absolute jitter */ - bwest_str->rec_jitter_short_term_abs = 0.05f * - arr_time_noise_abs + 0.95f * - bwest_str->rec_jitter_short_term_abs; - - /* short term averaged jitter */ - bwest_str->rec_jitter_short_term = 0.05f * arr_time_noise + - 0.95f * bwest_str->rec_jitter_short_term; - } - } - } - else - { - // reset time-since-update counter when - // receiving the first 9 packets - bwest_str->last_update_ts = arr_ts; - bwest_str->last_reduction_ts = arr_ts + 3*FS; - bwest_str->num_pkts_rec = 0; - - bwest_str->count_tot_updates_rec++; - } - - /* limit minimum bottle neck rate */ - if (bwest_str->rec_bw_inv > 1.0f / ((float)MIN_ISAC_BW + - bwest_str->rec_header_rate)) - { - bwest_str->rec_bw_inv = 1.0f / ((float)MIN_ISAC_BW + - bwest_str->rec_header_rate); - } - - // limit maximum bitrate - if (bwest_str->rec_bw_inv < 1.0f / ((float)MAX_ISAC_BW + - bwest_str->rec_header_rate)) - { - bwest_str->rec_bw_inv = 1.0f / ((float)MAX_ISAC_BW + - bwest_str->rec_header_rate); - } - - /* store frame length */ - bwest_str->prev_frame_length = frame_length; - - /* store far-side transmission rate */ - bwest_str->prev_rec_rtp_rate = rec_rtp_rate; - - /* store far-side RTP time stamp */ - bwest_str->prev_rec_rtp_number = rtp_number; - - // Replace bwest_str->rec_max_delay by the new - // value (atomic operation) - bwest_str->rec_max_delay = 3.0f * bwest_str->rec_jitter; - - /* store send and arrival time stamp */ - bwest_str->prev_rec_arr_ts = arr_ts ; - bwest_str->prev_rec_send_ts = send_ts; - - asdf = bwest_str->rec_bw; - - /* Replace bwest_str->rec_bw by the new value (atomic operation) */ - bwest_str->rec_bw = (WebRtc_Word32)(1.0f / bwest_str->rec_bw_inv - - bwest_str->rec_header_rate); - - if (immediate_set) - { - bwest_str->rec_bw = (WebRtc_Word32) (delay_correction_factor * - (float) bwest_str->rec_bw); - - if (bwest_str->rec_bw < (WebRtc_Word32) MIN_ISAC_BW) - { - bwest_str->rec_bw = (WebRtc_Word32) MIN_ISAC_BW; - } - - bwest_str->rec_bw_avg = bwest_str->rec_bw + - bwest_str->rec_header_rate; - - bwest_str->rec_bw_avg_Q = (float) bwest_str->rec_bw; - - bwest_str->rec_jitter_short_term = 0.0f; - - bwest_str->rec_bw_inv = 1.0f / (bwest_str->rec_bw + - bwest_str->rec_header_rate); - - bwest_str->count_tot_updates_rec = 1; - - immediate_set = 0; - bwest_str->consecLatency = 0; - bwest_str->numConsecLatePkts = 0; - } - - return 0; -} - - -/* This function updates the send bottle neck rate */ -/* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */ -/* returns 0 if everything went fine, -1 otherwise */ -WebRtc_Word16 WebRtcIsac_UpdateUplinkBwImpl( - BwEstimatorstr* bwest_str, - WebRtc_Word16 index, - enum IsacSamplingRate encoderSamplingFreq) -{ - if((index < 0) || (index > 23)) - { - return -ISAC_RANGE_ERROR_BW_ESTIMATOR; - } - - /* UPDATE ESTIMATES FROM OTHER SIDE */ - if(encoderSamplingFreq == kIsacWideband) - { - if(index > 11) - { - index -= 12; - /* compute the jitter estimate as decoded on the other side */ - bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg + - 0.1f * (float)MAX_ISAC_MD; - } - else - { - /* compute the jitter estimate as decoded on the other side */ - bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg + - 0.1f * (float)MIN_ISAC_MD; - } - - /* compute the BN estimate as decoded on the other side */ - bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg + - 0.1f * WebRtcIsac_kQRateTableWb[index]; - } - else - { - /* compute the BN estimate as decoded on the other side */ - bwest_str->send_bw_avg = 0.9f * bwest_str->send_bw_avg + - 0.1f * WebRtcIsac_kQRateTableSwb[index]; - } - - if (bwest_str->send_bw_avg > (float) 28000 && !bwest_str->hsn_detect_snd) - { - bwest_str->num_consec_snt_pkts_over_30k++; - - if (bwest_str->num_consec_snt_pkts_over_30k >= 66) - { - //approx 2 seconds with 30ms frames - bwest_str->hsn_detect_snd = 1; - } - } - else if (!bwest_str->hsn_detect_snd) - { - bwest_str->num_consec_snt_pkts_over_30k = 0; - } - return 0; -} - -// called when there is upper-band bit-stream to update jitter -// statistics. -WebRtc_Word16 WebRtcIsac_UpdateUplinkJitter( - BwEstimatorstr* bwest_str, - WebRtc_Word32 index) -{ - if((index < 0) || (index > 23)) - { - return -ISAC_RANGE_ERROR_BW_ESTIMATOR; - } - - if(index > 0) - { - /* compute the jitter estimate as decoded on the other side */ - bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg + - 0.1f * (float)MAX_ISAC_MD; - } - else - { - /* compute the jitter estimate as decoded on the other side */ - bwest_str->send_max_delay_avg = 0.9f * bwest_str->send_max_delay_avg + - 0.1f * (float)MIN_ISAC_MD; - } - - return 0; -} - - - -// Returns the bandwidth/jitter estimation code (integer 0...23) -// to put in the sending iSAC payload -WebRtc_UWord16 -WebRtcIsac_GetDownlinkBwJitIndexImpl( - BwEstimatorstr* bwest_str, - WebRtc_Word16* bottleneckIndex, - WebRtc_Word16* jitterInfo, - enum IsacSamplingRate decoderSamplingFreq) -{ - float MaxDelay; - //WebRtc_UWord16 MaxDelayBit; - - float rate; - float r; - float e1, e2; - const float weight = 0.1f; - const float* ptrQuantizationTable; - WebRtc_Word16 addJitterInfo; - WebRtc_Word16 minInd; - WebRtc_Word16 maxInd; - WebRtc_Word16 midInd; - - /* Get Max Delay Bit */ - /* get unquantized max delay */ - MaxDelay = (float)WebRtcIsac_GetDownlinkMaxDelay(bwest_str); - - if ( ((1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight * - MAX_ISAC_MD - MaxDelay) > (MaxDelay - (1.f-weight) * - bwest_str->rec_max_delay_avg_Q - weight * MIN_ISAC_MD) ) - { - jitterInfo[0] = 0; - /* update quantized average */ - bwest_str->rec_max_delay_avg_Q = - (1.f - weight) * bwest_str->rec_max_delay_avg_Q + weight * - (float)MIN_ISAC_MD; - } - else - { - jitterInfo[0] = 1; - /* update quantized average */ - bwest_str->rec_max_delay_avg_Q = - (1.f-weight) * bwest_str->rec_max_delay_avg_Q + weight * - (float)MAX_ISAC_MD; - } - - // Get unquantized rate. - rate = (float)WebRtcIsac_GetDownlinkBandwidth(bwest_str); - - /* Get Rate Index */ - if(decoderSamplingFreq == kIsacWideband) - { - ptrQuantizationTable = WebRtcIsac_kQRateTableWb; - addJitterInfo = 1; - maxInd = 11; - } - else - { - ptrQuantizationTable = WebRtcIsac_kQRateTableSwb; - addJitterInfo = 0; - maxInd = 23; - } - - minInd = 0; - while(maxInd > minInd + 1) - { - midInd = (maxInd + minInd) >> 1; - if(rate > ptrQuantizationTable[midInd]) - { - minInd = midInd; - } - else - { - maxInd = midInd; - } - } - // Chose the index which gives results an average which is closest - // to rate - r = (1 - weight) * bwest_str->rec_bw_avg_Q - rate; - e1 = weight * ptrQuantizationTable[minInd] + r; - e2 = weight * ptrQuantizationTable[maxInd] + r; - e1 = (e1 > 0)? e1:-e1; - e2 = (e2 > 0)? e2:-e2; - if(e1 < e2) - { - bottleneckIndex[0] = minInd; - } - else - { - bottleneckIndex[0] = maxInd; - } - - bwest_str->rec_bw_avg_Q = (1 - weight) * bwest_str->rec_bw_avg_Q + - weight * ptrQuantizationTable[bottleneckIndex[0]]; - bottleneckIndex[0] += jitterInfo[0] * 12 * addJitterInfo; - - bwest_str->rec_bw_avg = (1 - weight) * bwest_str->rec_bw_avg + weight * - (rate + bwest_str->rec_header_rate); - - return 0; -} - - - -/* get the bottle neck rate from far side to here, as estimated on this side */ -WebRtc_Word32 WebRtcIsac_GetDownlinkBandwidth( const BwEstimatorstr *bwest_str) -{ - WebRtc_Word32 rec_bw; - float jitter_sign; - float bw_adjust; - - /* create a value between -1.0 and 1.0 indicating "average sign" of jitter */ - jitter_sign = bwest_str->rec_jitter_short_term / - bwest_str->rec_jitter_short_term_abs; - - /* adjust bw proportionally to negative average jitter sign */ - bw_adjust = 1.0f - jitter_sign * (0.15f + 0.15f * jitter_sign * jitter_sign); - - /* adjust Rate if jitter sign is mostly constant */ - rec_bw = (WebRtc_Word32)(bwest_str->rec_bw * bw_adjust); - - /* limit range of bottle neck rate */ - if (rec_bw < MIN_ISAC_BW) - { - rec_bw = MIN_ISAC_BW; - } - else if (rec_bw > MAX_ISAC_BW) - { - rec_bw = MAX_ISAC_BW; - } - return rec_bw; -} - -/* Returns the max delay (in ms) */ -WebRtc_Word32 -WebRtcIsac_GetDownlinkMaxDelay(const BwEstimatorstr *bwest_str) -{ - WebRtc_Word32 rec_max_delay; - - rec_max_delay = (WebRtc_Word32)(bwest_str->rec_max_delay); - - /* limit range of jitter estimate */ - if (rec_max_delay < MIN_ISAC_MD) - { - rec_max_delay = MIN_ISAC_MD; - } - else if (rec_max_delay > MAX_ISAC_MD) - { - rec_max_delay = MAX_ISAC_MD; - } - return rec_max_delay; -} - -/* get the bottle neck rate from here to far side, as estimated by far side */ -void -WebRtcIsac_GetUplinkBandwidth( - const BwEstimatorstr* bwest_str, - WebRtc_Word32* bitRate) -{ - /* limit range of bottle neck rate */ - if (bwest_str->send_bw_avg < MIN_ISAC_BW) - { - *bitRate = MIN_ISAC_BW; - } - else if (bwest_str->send_bw_avg > MAX_ISAC_BW) - { - *bitRate = MAX_ISAC_BW; - } - else - { - *bitRate = (WebRtc_Word32)(bwest_str->send_bw_avg); - } - return; -} - -/* Returns the max delay value from the other side in ms */ -WebRtc_Word32 -WebRtcIsac_GetUplinkMaxDelay(const BwEstimatorstr *bwest_str) -{ - WebRtc_Word32 send_max_delay; - - send_max_delay = (WebRtc_Word32)(bwest_str->send_max_delay_avg); - - /* limit range of jitter estimate */ - if (send_max_delay < MIN_ISAC_MD) - { - send_max_delay = MIN_ISAC_MD; - } - else if (send_max_delay > MAX_ISAC_MD) - { - send_max_delay = MAX_ISAC_MD; - } - return send_max_delay; -} - - -/* - * update long-term average bitrate and amount of data in buffer - * returns minimum payload size (bytes) - */ -int WebRtcIsac_GetMinBytes( - RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameSamples, /* samples per frame */ - const double BottleNeck, /* bottle neck rate; excl headers (bps) */ - const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ - enum ISACBandwidth bandwidth - /*,WebRtc_Word16 frequentLargePackets*/) -{ - double MinRate = 0.0; - int MinBytes; - double TransmissionTime; - int burstInterval = BURST_INTERVAL; - - // first 10 packets @ low rate, then INIT_BURST_LEN packets @ - // fixed rate of INIT_RATE bps - if (State->InitCounter > 0) - { - if (State->InitCounter-- <= INIT_BURST_LEN) - { - if(bandwidth == isac8kHz) - { - MinRate = INIT_RATE_WB; - } - else - { - MinRate = INIT_RATE_SWB; - } - } - else - { - MinRate = 0; - } - } - else - { - /* handle burst */ - if (State->BurstCounter) - { - if (State->StillBuffered < (1.0 - 1.0/BURST_LEN) * DelayBuildUp) - { - /* max bps derived from BottleNeck and DelayBuildUp values */ - MinRate = (1.0 + (FS/1000) * DelayBuildUp / - (double)(BURST_LEN * FrameSamples)) * BottleNeck; - } - else - { - // max bps derived from StillBuffered and DelayBuildUp - // values - MinRate = (1.0 + (FS/1000) * (DelayBuildUp - - State->StillBuffered) / (double)FrameSamples) * BottleNeck; - if (MinRate < 1.04 * BottleNeck) - { - MinRate = 1.04 * BottleNeck; - } - } - State->BurstCounter--; - } - } - - - /* convert rate from bits/second to bytes/packet */ - MinBytes = (int) (MinRate * FrameSamples / (8.0 * FS)); - - /* StreamSize will be adjusted if less than MinBytes */ - if (StreamSize < MinBytes) - { - StreamSize = MinBytes; - } - - /* keep track of when bottle neck was last exceeded by at least 1% */ - if (StreamSize * 8.0 * FS / FrameSamples > 1.01 * BottleNeck) { - if (State->PrevExceed) { - /* bottle_neck exceded twice in a row, decrease ExceedAgo */ - State->ExceedAgo -= /*BURST_INTERVAL*/ burstInterval / (BURST_LEN - 1); - if (State->ExceedAgo < 0) - State->ExceedAgo = 0; - } - else - { - State->ExceedAgo += (FrameSamples * 1000) / FS; /* ms */ - State->PrevExceed = 1; - } - } - else - { - State->PrevExceed = 0; - State->ExceedAgo += (FrameSamples * 1000) / FS; /* ms */ - } - - /* set burst flag if bottle neck not exceeded for long time */ - if ((State->ExceedAgo > burstInterval) && - (State->BurstCounter == 0)) - { - if (State->PrevExceed) - { - State->BurstCounter = BURST_LEN - 1; - } - else - { - State->BurstCounter = BURST_LEN; - } - } - - - /* Update buffer delay */ - TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck; /* ms */ - State->StillBuffered += TransmissionTime; - State->StillBuffered -= (FrameSamples * 1000) / FS; /* ms */ - if (State->StillBuffered < 0.0) - { - State->StillBuffered = 0.0; - } - - return MinBytes; -} - - -/* - * update long-term average bitrate and amount of data in buffer - */ -void WebRtcIsac_UpdateRateModel( - RateModel *State, - int StreamSize, /* bytes in bitstream */ - const int FrameSamples, /* samples per frame */ - const double BottleNeck) /* bottle neck rate; excl headers (bps) */ -{ - double TransmissionTime; - - /* avoid the initial "high-rate" burst */ - State->InitCounter = 0; - - /* Update buffer delay */ - TransmissionTime = StreamSize * 8.0 * 1000.0 / BottleNeck; /* ms */ - State->StillBuffered += TransmissionTime; - State->StillBuffered -= (FrameSamples * 1000) / FS; /* ms */ - if (State->StillBuffered < 0.0) - State->StillBuffered = 0.0; - -} - - -void WebRtcIsac_InitRateModel( - RateModel *State) -{ - State->PrevExceed = 0; /* boolean */ - State->ExceedAgo = 0; /* ms */ - State->BurstCounter = 0; /* packets */ - State->InitCounter = INIT_BURST_LEN + 10; /* packets */ - State->StillBuffered = 1.0; /* ms */ -} - -int WebRtcIsac_GetNewFrameLength( - double bottle_neck, - int current_framesamples) -{ - int new_framesamples; - - const int Thld_20_30 = 20000; - - //const int Thld_30_20 = 30000; - const int Thld_30_20 = 1000000; // disable 20 ms frames - - const int Thld_30_60 = 18000; - //const int Thld_30_60 = 0; // disable 60 ms frames - - const int Thld_60_30 = 27000; - - - new_framesamples = current_framesamples; - - /* find new framelength */ - switch(current_framesamples) { - case 320: - if (bottle_neck < Thld_20_30) - new_framesamples = 480; - break; - case 480: - if (bottle_neck < Thld_30_60) - new_framesamples = 960; - else if (bottle_neck > Thld_30_20) - new_framesamples = 320; - break; - case 960: - if (bottle_neck >= Thld_60_30) - new_framesamples = 480; - break; - } - - return new_framesamples; -} - -double WebRtcIsac_GetSnr( - double bottle_neck, - int framesamples) -{ - double s2nr; - - const double a_20 = -30.0; - const double b_20 = 0.8; - const double c_20 = 0.0; - - const double a_30 = -23.0; - const double b_30 = 0.48; - const double c_30 = 0.0; - - const double a_60 = -23.0; - const double b_60 = 0.53; - const double c_60 = 0.0; - - - /* find new SNR value */ - switch(framesamples) { - case 320: - s2nr = a_20 + b_20 * bottle_neck * 0.001 + c_20 * bottle_neck * - bottle_neck * 0.000001; - break; - case 480: - s2nr = a_30 + b_30 * bottle_neck * 0.001 + c_30 * bottle_neck * - bottle_neck * 0.000001; - break; - case 960: - s2nr = a_60 + b_60 * bottle_neck * 0.001 + c_60 * bottle_neck * - bottle_neck * 0.000001; - break; - default: - s2nr = 0; - } - - return s2nr; - -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.h b/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.h deleted file mode 100644 index 353546446..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/bandwidth_estimator.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * bandwidth_estimator.h - * - * This header file contains the API for the Bandwidth Estimator - * designed for iSAC. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ - -#include "structs.h" -#include "settings.h" - - -#define MIN_ISAC_BW 10000 -#define MIN_ISAC_BW_LB 10000 -#define MIN_ISAC_BW_UB 25000 - -#define MAX_ISAC_BW 56000 -#define MAX_ISAC_BW_UB 32000 -#define MAX_ISAC_BW_LB 32000 - -#define MIN_ISAC_MD 5 -#define MAX_ISAC_MD 25 - -// assumed header size, in bytes; we don't know the exact number -// (header compression may be used) -#define HEADER_SIZE 35 - -// Initial Frame-Size, in ms, for Wideband & Super-Wideband Mode -#define INIT_FRAME_LEN_WB 60 -#define INIT_FRAME_LEN_SWB 30 - -// Initial Bottleneck Estimate, in bits/sec, for -// Wideband & Super-wideband mode -#define INIT_BN_EST_WB 20e3f -#define INIT_BN_EST_SWB 56e3f - -// Initial Header rate (header rate depends on frame-size), -// in bits/sec, for Wideband & Super-Wideband mode. -#define INIT_HDR_RATE_WB \ - ((float)HEADER_SIZE * 8.0f * 1000.0f / (float)INIT_FRAME_LEN_WB) -#define INIT_HDR_RATE_SWB \ - ((float)HEADER_SIZE * 8.0f * 1000.0f / (float)INIT_FRAME_LEN_SWB) - -// number of packets in a row for a high rate burst -#define BURST_LEN 3 - -// ms, max time between two full bursts -#define BURST_INTERVAL 500 - -// number of packets in a row for initial high rate burst -#define INIT_BURST_LEN 5 - -// bits/s, rate for the first BURST_LEN packets -#define INIT_RATE_WB INIT_BN_EST_WB -#define INIT_RATE_SWB INIT_BN_EST_SWB - - -#if defined(__cplusplus) -extern "C" { -#endif - - /* This function initializes the struct */ - /* to be called before using the struct for anything else */ - /* returns 0 if everything went fine, -1 otherwise */ - WebRtc_Word32 WebRtcIsac_InitBandwidthEstimator( - BwEstimatorstr* bwest_str, - enum IsacSamplingRate encoderSampRate, - enum IsacSamplingRate decoderSampRate); - - /* This function updates the receiving estimate */ - /* Parameters: */ - /* rtp_number - value from RTP packet, from NetEq */ - /* frame length - length of signal frame in ms, from iSAC decoder */ - /* send_ts - value in RTP header giving send time in samples */ - /* arr_ts - value given by timeGetTime() time of arrival in samples of packet from NetEq */ - /* pksize - size of packet in bytes, from NetEq */ - /* Index - integer (range 0...23) indicating bottle neck & jitter as estimated by other side */ - /* returns 0 if everything went fine, -1 otherwise */ - WebRtc_Word16 WebRtcIsac_UpdateBandwidthEstimator( - BwEstimatorstr* bwest_str, - const WebRtc_UWord16 rtp_number, - const WebRtc_Word32 frame_length, - const WebRtc_UWord32 send_ts, - const WebRtc_UWord32 arr_ts, - const WebRtc_Word32 pksize); - - /* Update receiving estimates. Used when we only receive BWE index, no iSAC data packet. */ - WebRtc_Word16 WebRtcIsac_UpdateUplinkBwImpl( - BwEstimatorstr* bwest_str, - WebRtc_Word16 Index, - enum IsacSamplingRate encoderSamplingFreq); - - /* Returns the bandwidth/jitter estimation code (integer 0...23) to put in the sending iSAC payload */ - WebRtc_UWord16 WebRtcIsac_GetDownlinkBwJitIndexImpl( - BwEstimatorstr* bwest_str, - WebRtc_Word16* bottleneckIndex, - WebRtc_Word16* jitterInfo, - enum IsacSamplingRate decoderSamplingFreq); - - /* Returns the bandwidth estimation (in bps) */ - WebRtc_Word32 WebRtcIsac_GetDownlinkBandwidth( - const BwEstimatorstr *bwest_str); - - /* Returns the max delay (in ms) */ - WebRtc_Word32 WebRtcIsac_GetDownlinkMaxDelay( - const BwEstimatorstr *bwest_str); - - /* Returns the bandwidth that iSAC should send with in bps */ - void WebRtcIsac_GetUplinkBandwidth( - const BwEstimatorstr* bwest_str, - WebRtc_Word32* bitRate); - - /* Returns the max delay value from the other side in ms */ - WebRtc_Word32 WebRtcIsac_GetUplinkMaxDelay( - const BwEstimatorstr *bwest_str); - - - /* - * update amount of data in bottle neck buffer and burst handling - * returns minimum payload size (bytes) - */ - int WebRtcIsac_GetMinBytes( - RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameLen, /* ms per frame */ - const double BottleNeck, /* bottle neck rate; excl headers (bps) */ - const double DelayBuildUp, /* max delay from bottleneck buffering (ms) */ - enum ISACBandwidth bandwidth - /*,WebRtc_Word16 frequentLargePackets*/); - - /* - * update long-term average bitrate and amount of data in buffer - */ - void WebRtcIsac_UpdateRateModel( - RateModel* State, - int StreamSize, /* bytes in bitstream */ - const int FrameSamples, /* samples per frame */ - const double BottleNeck); /* bottle neck rate; excl headers (bps) */ - - - void WebRtcIsac_InitRateModel( - RateModel *State); - - /* Returns the new framelength value (input argument: bottle_neck) */ - int WebRtcIsac_GetNewFrameLength( - double bottle_neck, - int current_framelength); - - /* Returns the new SNR value (input argument: bottle_neck) */ - double WebRtcIsac_GetSnr( - double bottle_neck, - int new_framelength); - - - WebRtc_Word16 WebRtcIsac_UpdateUplinkJitter( - BwEstimatorstr* bwest_str, - WebRtc_Word32 index); - - extern const float WebRtcIsac_kQRateTableWb[12]; - extern const float WebRtcIsac_kQRateTableSwb[24]; - -#if defined(__cplusplus) -} -#endif - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_BANDWIDTH_ESTIMATOR_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/codec.h b/modules/audio_coding/codecs/iSAC/main/source/codec.h deleted file mode 100644 index 6af27ea9e..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/codec.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * codec.h - * - * This header file contains the calls to the internal encoder - * and decoder functions. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ - -#include "structs.h" - -int WebRtcIsac_EstimateBandwidth( - BwEstimatorstr* bwest_str, - Bitstr* streamdata, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts, - enum IsacSamplingRate encoderSampRate, - enum IsacSamplingRate decoderSampRate); - -int WebRtcIsac_DecodeLb( - float* signal_out, - ISACLBDecStruct* ISACdec_obj, - WebRtc_Word16* current_framesamples, - WebRtc_Word16 isRCUPayload); - -int WebRtcIsac_DecodeRcuLb( - float* signal_out, - ISACLBDecStruct* ISACdec_obj, - WebRtc_Word16* current_framesamples); - -int WebRtcIsac_EncodeLb( - float* in, - ISACLBEncStruct* ISACencLB_obj, - WebRtc_Word16 codingMode, - WebRtc_Word16 bottleneckIndex); - -int WebRtcIsac_EncodeStoredDataLb( - const ISAC_SaveEncData_t* ISACSavedEnc_obj, - Bitstr* ISACBitStr_obj, - int BWnumber, - float scale); - - -int WebRtcIsac_EncodeStoredDataUb12( - const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, - Bitstr* bitStream, - WebRtc_Word32 jitterInfo, - float scale); - -int WebRtcIsac_EncodeStoredDataUb16( - const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, - Bitstr* bitStream, - WebRtc_Word32 jitterInfo, - float scale); - - -WebRtc_Word16 WebRtcIsac_GetRedPayloadUb( - const ISACUBSaveEncDataStruct* ISACSavedEncObj, - Bitstr* bitStreamObj, - enum ISACBandwidth bandwidth); -/****************************************************************************** - * WebRtcIsac_RateAllocation() - * Internal function to perform a rate-allocation for upper and lower-band, - * given a total rate. - * - * Input: - * - inRateBitPerSec : a total bit-rate in bits/sec. - * - * Output: - * - rateLBBitPerSec : a bit-rate allocated to the lower-band - * in bits/sec. - * - rateUBBitPerSec : a bit-rate allocated to the upper-band - * in bits/sec. - * - * Return value : 0 if rate allocation has been successful. - * -1 if failed to allocate rates. - */ - -WebRtc_Word16 -WebRtcIsac_RateAllocation( - WebRtc_Word32 inRateBitPerSec, - double* rateLBBitPerSec, - double* rateUBBitPerSec, - enum ISACBandwidth* bandwidthKHz); - - -/****************************************************************************** - * WebRtcIsac_DecodeUb16() - * - * Decode the upper-band if the codec is in 0-16 kHz mode. - * - * Input/Output: - * -ISACdec_obj : pointer to the upper-band decoder object. The - * bit-stream is stored inside the decoder object. - * - * Output: - * -signal_out : decoded audio, 480 samples 30 ms. - * - * Return value : >0 number of decoded bytes. - * <0 if an error occurred. - */ -int WebRtcIsac_DecodeUb16( - float* signal_out, - ISACUBDecStruct* ISACdec_obj, - WebRtc_Word16 isRCUPayload); - - -/****************************************************************************** - * WebRtcIsac_DecodeUb12() - * - * Decode the upper-band if the codec is in 0-12 kHz mode. - * - * Input/Output: - * -ISACdec_obj : pointer to the upper-band decoder object. The - * bit-stream is stored inside the decoder object. - * - * Output: - * -signal_out : decoded audio, 480 samples 30 ms. - * - * Return value : >0 number of decoded bytes. - * <0 if an error occurred. - */ -int WebRtcIsac_DecodeUb12( - float* signal_out, - ISACUBDecStruct* ISACdec_obj, - WebRtc_Word16 isRCUPayload); - - -/****************************************************************************** - * WebRtcIsac_EncodeUb16() - * - * Encode the upper-band if the codec is in 0-16 kHz mode. - * - * Input: - * -in : upper-band audio, 160 samples (10 ms). - * - * Input/Output: - * -ISACdec_obj : pointer to the upper-band encoder object. The - * bit-stream is stored inside the encoder object. - * - * Return value : >0 number of encoded bytes. - * <0 if an error occurred. - */ -int WebRtcIsac_EncodeUb16( - float* in, - ISACUBEncStruct* ISACenc_obj, - WebRtc_Word32 jitterInfo); - - -/****************************************************************************** - * WebRtcIsac_EncodeUb12() - * - * Encode the upper-band if the codec is in 0-12 kHz mode. - * - * Input: - * -in : upper-band audio, 160 samples (10 ms). - * - * Input/Output: - * -ISACdec_obj : pointer to the upper-band encoder object. The - * bit-stream is stored inside the encoder object. - * - * Return value : >0 number of encoded bytes. - * <0 if an error occurred. - */ -int WebRtcIsac_EncodeUb12( - float* in, - ISACUBEncStruct* ISACenc_obj, - WebRtc_Word32 jitterInfo); - -/************************** initialization functions *************************/ - -void WebRtcIsac_InitMasking(MaskFiltstr *maskdata); - -void WebRtcIsac_InitPreFilterbank(PreFiltBankstr *prefiltdata); - -void WebRtcIsac_InitPostFilterbank(PostFiltBankstr *postfiltdata); - -void WebRtcIsac_InitPitchFilter(PitchFiltstr *pitchfiltdata); - -void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct *State); - - -/**************************** transform functions ****************************/ - -void WebRtcIsac_InitTransform(); - -void WebRtcIsac_Time2Spec(double *inre1, - double *inre2, - WebRtc_Word16 *outre, - WebRtc_Word16 *outim, - FFTstr *fftstr_obj); - -void WebRtcIsac_Spec2time(double *inre, - double *inim, - double *outre1, - double *outre2, - FFTstr *fftstr_obj); - - -/******************************* filter functions ****************************/ - -void WebRtcIsac_AllPoleFilter(double *InOut, - double *Coef, - int lengthInOut, - int orderCoef); - -void WebRtcIsac_AllZeroFilter(double *In, - double *Coef, - int lengthInOut, - int orderCoef, - double *Out); - -void WebRtcIsac_ZeroPoleFilter(double *In, - double *ZeroCoef, - double *PoleCoef, - int lengthInOut, - int orderCoef, - double *Out); - - -/***************************** filterbank functions **************************/ - -void WebRtcIsac_SplitAndFilter(double *in, - double *LP, - double *HP, - double *LP_la, - double *HP_la, - PreFiltBankstr *prefiltdata); - - -void WebRtcIsac_FilterAndCombine(double *InLP, - double *InHP, - double *Out, - PostFiltBankstr *postfiltdata); - - - -void WebRtcIsac_SplitAndFilterFloat(float *in, - float *LP, - float *HP, - double *LP_la, - double *HP_la, - PreFiltBankstr *prefiltdata); - - -void WebRtcIsac_FilterAndCombineFloat(float *InLP, - float *InHP, - float *Out, - PostFiltBankstr *postfiltdata); - - -/************************* normalized lattice filters ************************/ - -void WebRtcIsac_NormLatticeFilterMa(int orderCoef, - float *stateF, - float *stateG, - float *lat_in, - double *filtcoeflo, - double *lat_out); - -void WebRtcIsac_NormLatticeFilterAr(int orderCoef, - float *stateF, - float *stateG, - double *lat_in, - double *lo_filt_coef, - float *lat_out); - -void WebRtcIsac_Dir2Lat(double *a, - int orderCoef, - float *sth, - float *cth); - -void WebRtcIsac_AutoCorr(double *r, - const double *x, - int N, - int order); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CODEC_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/crc.c b/modules/audio_coding/codecs/iSAC/main/source/crc.c deleted file mode 100644 index 91a8712f0..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/crc.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "crc.h" -#include -#include "signal_processing_library.h" - -#define POLYNOMIAL 0x04c11db7L - - -static WebRtc_UWord32 kCrcTable[256] = { - 0, 0x4c11db7, 0x9823b6e, 0xd4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x18aeb13, 0x54bf6a4, - 0x808d07d, 0xcc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x315d626, 0x7d4cb91, 0xa97ed48, 0xe56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x29f3d35, 0x65e2082, 0xb1d065b, 0xfdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 -}; - - - - -/**************************************************************************** - * WebRtcIsac_GetCrc(...) - * - * This function returns a 32 bit CRC checksum of a bit stream - * - * Input: - * - bitstream : payload bitstream - * - len_bitstream_in_bytes : number of 8-bit words in the bit stream - * - * Output: - * - crc : checksum - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsac_GetCrc(const WebRtc_Word16* bitstream, - WebRtc_Word16 len_bitstream_in_bytes, - WebRtc_UWord32* crc) -{ - WebRtc_UWord8* bitstream_ptr_uw8; - WebRtc_UWord32 crc_state; - int byte_cntr; - int crc_tbl_indx; - - /* Sanity Check. */ - if (bitstream == NULL) { - return -1; - } - /* cast to UWord8 pointer */ - bitstream_ptr_uw8 = (WebRtc_UWord8 *)bitstream; - - /* initialize */ - crc_state = 0xFFFFFFFF; - - for (byte_cntr = 0; byte_cntr < len_bitstream_in_bytes; byte_cntr++) { - crc_tbl_indx = (WEBRTC_SPL_RSHIFT_U32(crc_state, 24) ^ - bitstream_ptr_uw8[byte_cntr]) & 0xFF; - crc_state = WEBRTC_SPL_LSHIFT_U32(crc_state, 8) ^ kCrcTable[crc_tbl_indx]; - } - - *crc = ~crc_state; - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/crc.h b/modules/audio_coding/codecs/iSAC/main/source/crc.h deleted file mode 100644 index 015127894..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/crc.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * crc.h - * - * Checksum functions - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ - -#include "typedefs.h" - -/**************************************************************************** - * WebRtcIsac_GetCrc(...) - * - * This function returns a 32 bit CRC checksum of a bit stream - * - * Input: - * - encoded : payload bit stream - * - no_of_word8s : number of 8-bit words in the bit stream - * - * Output: - * - crc : checksum - * - * Return value : 0 - Ok - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsac_GetCrc( - const WebRtc_Word16* encoded, - WebRtc_Word16 no_of_word8s, - WebRtc_UWord32* crc); - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_CRC_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/decode.c b/modules/audio_coding/codecs/iSAC/main/source/decode.c deleted file mode 100644 index 25634b0a8..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/decode.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * decode_B.c - * - * This file contains definition of funtions for decoding. - * Decoding of lower-band, including normal-decoding and RCU decoding. - * Decoding of upper-band, including 8-12 kHz, when the bandwidth is - * 0-12 kHz, and 8-16 kHz, when the bandwidth is 0-16 kHz. - * - */ - - -#include "codec.h" -#include "entropy_coding.h" -#include "pitch_estimator.h" -#include "bandwidth_estimator.h" -#include "structs.h" -#include "settings.h" - -#include -#include -#include - - -/* - * function to decode the bitstream - * returns the total number of bytes in the stream - */ -int -WebRtcIsac_DecodeLb( - float* signal_out, - ISACLBDecStruct* ISACdecLB_obj, - WebRtc_Word16* current_framesamples, - WebRtc_Word16 isRCUPayload) -{ - int k, model; - int len, err; - WebRtc_Word16 bandwidthInd; - - float LP_dec_float[FRAMESAMPLES_HALF]; - float HP_dec_float[FRAMESAMPLES_HALF]; - - double LPw[FRAMESAMPLES_HALF]; - double HPw[FRAMESAMPLES_HALF]; - double LPw_pf[FRAMESAMPLES_HALF]; - - double lo_filt_coef[(ORDERLO+1)*SUBFRAMES]; - double hi_filt_coef[(ORDERHI+1)*SUBFRAMES]; - - double real_f[FRAMESAMPLES_HALF]; - double imag_f[FRAMESAMPLES_HALF]; - - double PitchLags[4]; - double PitchGains[4]; - double AvgPitchGain; - WebRtc_Word16 PitchGains_Q12[4]; - WebRtc_Word16 AvgPitchGain_Q12; - - float gain; - - int frame_nb; /* counter */ - int frame_mode; /* 0 for 20ms and 30ms, 1 for 60ms */ - int processed_samples; - - (ISACdecLB_obj->bitstr_obj).W_upper = 0xFFFFFFFF; - (ISACdecLB_obj->bitstr_obj).streamval = 0; - (ISACdecLB_obj->bitstr_obj).stream_index = 0; - - len = 0; - - /* decode framelength and BW estimation - not used, - only for stream pointer*/ - err = WebRtcIsac_DecodeFrameLen(&ISACdecLB_obj->bitstr_obj, - current_framesamples); - if (err < 0) { // error check - return err; - } - - /* frame_mode: 0, or 1 */ - frame_mode = *current_framesamples/MAX_FRAMESAMPLES; - /* processed_samples: either 320 (20ms) or 480 (30, 60 ms) */ - processed_samples = *current_framesamples/(frame_mode+1); - - err = WebRtcIsac_DecodeSendBW(&ISACdecLB_obj->bitstr_obj, &bandwidthInd); - if (err < 0) { // error check - return err; - } - - /* one loop if it's one frame (20 or 30ms), 2 loops if 2 frames - bundled together (60ms) */ - for (frame_nb = 0; frame_nb <= frame_mode; frame_nb++) { - /* decode & dequantize pitch parameters */ - err = WebRtcIsac_DecodePitchGain(&(ISACdecLB_obj->bitstr_obj), - PitchGains_Q12); - if (err < 0) { // error check - return err; - } - - err = WebRtcIsac_DecodePitchLag(&ISACdecLB_obj->bitstr_obj, - PitchGains_Q12, PitchLags); - if (err < 0) { // error check - return err; - } - - AvgPitchGain_Q12 = (PitchGains_Q12[0] + PitchGains_Q12[1] + - PitchGains_Q12[2] + PitchGains_Q12[3])>>2; - - /* decode & dequantize FiltCoef */ - err = WebRtcIsac_DecodeLpc(&ISACdecLB_obj->bitstr_obj, - lo_filt_coef,hi_filt_coef, &model); - if (err < 0) { // error check - return err; - } - /* decode & dequantize spectrum */ - len = WebRtcIsac_DecodeSpecLb(&ISACdecLB_obj->bitstr_obj, - real_f, imag_f, AvgPitchGain_Q12); - if (len < 0) { // error check - return len; - } - - /* inverse transform */ - WebRtcIsac_Spec2time(real_f, imag_f, LPw, HPw, - &ISACdecLB_obj->fftstr_obj); - - /* convert PitchGains back to FLOAT for pitchfilter_post */ - for (k = 0; k < 4; k++) { - PitchGains[k] = ((float)PitchGains_Q12[k])/4096; - } - - if(isRCUPayload) - { - for (k = 0; k < 240; k++) { - LPw[k] *= RCU_TRANSCODING_SCALE_INVERSE; - HPw[k] *= RCU_TRANSCODING_SCALE_INVERSE; - } - } - - /* inverse pitch filter */ - WebRtcIsac_PitchfilterPost(LPw, LPw_pf, - &ISACdecLB_obj->pitchfiltstr_obj, PitchLags, PitchGains); - /* convert AvgPitchGain back to FLOAT for computation of gain */ - AvgPitchGain = ((float)AvgPitchGain_Q12)/4096; - gain = 1.0f - 0.45f * (float)AvgPitchGain; - - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - /* reduce gain to compensate for pitch enhancer */ - LPw_pf[ k ] *= gain; - } - - if(isRCUPayload) - { - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - /* compensation for transcoding gain changes*/ - LPw_pf[k] *= RCU_TRANSCODING_SCALE; - HPw[k] *= RCU_TRANSCODING_SCALE; - } - } - - /* perceptual post-filtering (using normalized lattice filter) */ - WebRtcIsac_NormLatticeFilterAr(ORDERLO, - ISACdecLB_obj->maskfiltstr_obj.PostStateLoF, - (ISACdecLB_obj->maskfiltstr_obj).PostStateLoG, - LPw_pf, lo_filt_coef, LP_dec_float); - WebRtcIsac_NormLatticeFilterAr(ORDERHI, - ISACdecLB_obj->maskfiltstr_obj.PostStateHiF, - (ISACdecLB_obj->maskfiltstr_obj).PostStateHiG, - HPw, hi_filt_coef, HP_dec_float); - - /* recombine the 2 bands */ - WebRtcIsac_FilterAndCombineFloat( LP_dec_float, HP_dec_float, - signal_out + frame_nb * processed_samples, - &ISACdecLB_obj->postfiltbankstr_obj); - } - - return len; -} - - -/* - * This decode function is called when the codec is operating in 16 kHz - * bandwidth to decode the upperband, i.e. 8-16 kHz. - * - * Contrary to lower-band, the upper-band (8-16 kHz) is not split in - * frequency, but split to 12 sub-frames, i.e. twice as lower-band. - */ -int -WebRtcIsac_DecodeUb16( - float* signal_out, - ISACUBDecStruct* ISACdecUB_obj, - WebRtc_Word16 isRCUPayload) -{ - int len, err; - - double halfFrameFirst[FRAMESAMPLES_HALF]; - double halfFrameSecond[FRAMESAMPLES_HALF]; - - double percepFilterParam[(UB_LPC_ORDER+1) * (SUBFRAMES<<1) + - (UB_LPC_ORDER+1)]; - - double real_f[FRAMESAMPLES_HALF]; - double imag_f[FRAMESAMPLES_HALF]; - - len = 0; - - /* decode & dequantize FiltCoef */ - memset(percepFilterParam, 0, sizeof(percepFilterParam)); - err = WebRtcIsac_DecodeInterpolLpcUb(&ISACdecUB_obj->bitstr_obj, - percepFilterParam, isac16kHz); - if (err < 0) { // error check - return err; - } - - /* decode & dequantize spectrum */ - len = WebRtcIsac_DecodeSpecUB16(&ISACdecUB_obj->bitstr_obj, real_f, - imag_f); - if (len < 0) { // error check - return len; - } - if(isRCUPayload) - { - int n; - for(n = 0; n < 240; n++) - { - real_f[n] *= RCU_TRANSCODING_SCALE_UB_INVERSE; - imag_f[n] *= RCU_TRANSCODING_SCALE_UB_INVERSE; - } - } - - /* inverse transform */ - WebRtcIsac_Spec2time(real_f, imag_f, halfFrameFirst, halfFrameSecond, - &ISACdecUB_obj->fftstr_obj); - - /* perceptual post-filtering (using normalized lattice filter) */ - WebRtcIsac_NormLatticeFilterAr(UB_LPC_ORDER, - ISACdecUB_obj->maskfiltstr_obj.PostStateLoF, - (ISACdecUB_obj->maskfiltstr_obj).PostStateLoG, halfFrameFirst, - &percepFilterParam[(UB_LPC_ORDER+1)], signal_out); - - WebRtcIsac_NormLatticeFilterAr(UB_LPC_ORDER, - ISACdecUB_obj->maskfiltstr_obj.PostStateLoF, - (ISACdecUB_obj->maskfiltstr_obj).PostStateLoG, halfFrameSecond, - &percepFilterParam[(UB_LPC_ORDER + 1) * SUBFRAMES + (UB_LPC_ORDER+1)], - &signal_out[FRAMESAMPLES_HALF]); - - return len; -} - -/* - * This decode function is called when the codec operates at 0-12 kHz - * bandwidth to decode the upperband, i.e. 8-12 kHz. - * - * At the encoder the upper-band is split into two band, 8-12 kHz & 12-16 - * kHz, and only 8-12 kHz is encoded. At the decoder, 8-12 kHz band is - * reconstructed and 12-16 kHz replaced with zeros. Then two bands - * are combined, to reconstruct the upperband 8-16 kHz. - */ -int -WebRtcIsac_DecodeUb12( - float* signal_out, - ISACUBDecStruct* ISACdecUB_obj, - WebRtc_Word16 isRCUPayload) -{ - int len, err; - - float LP_dec_float[FRAMESAMPLES_HALF]; - float HP_dec_float[FRAMESAMPLES_HALF]; - - double LPw[FRAMESAMPLES_HALF]; - double HPw[FRAMESAMPLES_HALF]; - - double percepFilterParam[(UB_LPC_ORDER+1)*SUBFRAMES]; - - double real_f[FRAMESAMPLES_HALF]; - double imag_f[FRAMESAMPLES_HALF]; - - len = 0; - - /* decode & dequantize FiltCoef */ - err = WebRtcIsac_DecodeInterpolLpcUb(&ISACdecUB_obj->bitstr_obj, - percepFilterParam, isac12kHz); - if(err < 0) { // error check - return err; - } - - /* decode & dequantize spectrum */ - len = WebRtcIsac_DecodeSpecUB12(&ISACdecUB_obj->bitstr_obj, - real_f, imag_f); - if(len < 0) { // error check - return len; - } - - if(isRCUPayload) - { - int n; - for(n = 0; n < 240; n++) - { - real_f[n] *= RCU_TRANSCODING_SCALE_UB_INVERSE; - imag_f[n] *= RCU_TRANSCODING_SCALE_UB_INVERSE; - } - } - - /* inverse transform */ - WebRtcIsac_Spec2time(real_f, imag_f, LPw, HPw, &ISACdecUB_obj->fftstr_obj); - - /* perceptual post-filtering (using normalized lattice filter) */ - WebRtcIsac_NormLatticeFilterAr(UB_LPC_ORDER, - ISACdecUB_obj->maskfiltstr_obj.PostStateLoF, - (ISACdecUB_obj->maskfiltstr_obj).PostStateLoG, LPw, - percepFilterParam, LP_dec_float); - - /* Zerro for upper-band */ - memset(HP_dec_float, 0, sizeof(float) * (FRAMESAMPLES_HALF)); - - /* recombine the 2 bands */ - WebRtcIsac_FilterAndCombineFloat(HP_dec_float, LP_dec_float, signal_out, - &ISACdecUB_obj->postfiltbankstr_obj); - - - - return len; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/decode_bwe.c b/modules/audio_coding/codecs/iSAC/main/source/decode_bwe.c deleted file mode 100644 index cdac7fa8b..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/decode_bwe.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "structs.h" -#include "bandwidth_estimator.h" -#include "entropy_coding.h" -#include "codec.h" - - -int -WebRtcIsac_EstimateBandwidth( - BwEstimatorstr* bwest_str, - Bitstr* streamdata, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts, - enum IsacSamplingRate encoderSampRate, - enum IsacSamplingRate decoderSampRate) -{ - WebRtc_Word16 index; - WebRtc_Word16 frame_samples; - WebRtc_UWord32 sendTimestampIn16kHz; - WebRtc_UWord32 arrivalTimestampIn16kHz; - WebRtc_UWord32 diffSendTime; - WebRtc_UWord32 diffArrivalTime; - int err; - - /* decode framelength and BW estimation */ - err = WebRtcIsac_DecodeFrameLen(streamdata, &frame_samples); - if(err < 0) // error check - { - return err; - } - err = WebRtcIsac_DecodeSendBW(streamdata, &index); - if(err < 0) // error check - { - return err; - } - - /* UPDATE ESTIMATES FROM OTHER SIDE */ - err = WebRtcIsac_UpdateUplinkBwImpl(bwest_str, index, encoderSampRate); - if(err < 0) - { - return err; - } - - // We like BWE to work at 16 kHz sampling rate, - // therefore, we have to change the timestamps accordingly. - // translate the send timestamp if required - diffSendTime = (WebRtc_UWord32)((WebRtc_UWord32)send_ts - - (WebRtc_UWord32)bwest_str->senderTimestamp); - bwest_str->senderTimestamp = send_ts; - - diffArrivalTime = (WebRtc_UWord32)((WebRtc_UWord32)arr_ts - - (WebRtc_UWord32)bwest_str->receiverTimestamp); - bwest_str->receiverTimestamp = arr_ts; - - if(decoderSampRate == kIsacSuperWideband) - { - diffArrivalTime = (WebRtc_UWord32)diffArrivalTime >> 1; - diffSendTime = (WebRtc_UWord32)diffSendTime >> 1; - } - // arrival timestamp in 16 kHz - arrivalTimestampIn16kHz = (WebRtc_UWord32)((WebRtc_UWord32) - bwest_str->prev_rec_arr_ts + (WebRtc_UWord32)diffArrivalTime); - // send timestamp in 16 kHz - sendTimestampIn16kHz = (WebRtc_UWord32)((WebRtc_UWord32) - bwest_str->prev_rec_send_ts + (WebRtc_UWord32)diffSendTime); - - err = WebRtcIsac_UpdateBandwidthEstimator(bwest_str, rtp_seq_number, - (frame_samples * 1000) / FS, sendTimestampIn16kHz, - arrivalTimestampIn16kHz, packet_size); - // error check - if(err < 0) - { - return err; - } - - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/encode.c b/modules/audio_coding/codecs/iSAC/main/source/encode.c deleted file mode 100644 index 1a86f7c12..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/encode.c +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * encode.c - * - * This file contains definition of funtions for encoding. - * Decoding of upper-band, including 8-12 kHz, when the bandwidth is - * 0-12 kHz, and 8-16 kHz, when the bandwidth is 0-16 kHz. - * - */ - -#include -#include - -#include "structs.h" -#include "codec.h" -#include "pitch_estimator.h" -#include "entropy_coding.h" -#include "arith_routines.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "spectrum_ar_model_tables.h" -#include "lpc_tables.h" -#include "lpc_analysis.h" -#include "bandwidth_estimator.h" -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "lpc_gain_swb_tables.h" - - -#define UB_LOOKAHEAD 24 - -/* - Rate allocation tables of lower and upper-band bottleneck for - 12kHz & 16kHz bandwidth. - - 12 kHz bandwidth - ----------------- - The overall bottleneck of the coder is between 38 kbps and 45 kbps. We have - considered 7 enteries, uniformly distributed in this interval, i.e. 38, - 39.17, 40.33, 41.5, 42.67, 43.83 and 45. For every entery, the lower-band - and the upper-band bottlenecks are specified in - 'WebRtcIsac_kLowerBandBitRate12' and 'WebRtcIsac_kUpperBandBitRate12' - tables, respectively. E.g. the overall rate of 41.5 kbps corresponts to a - bottleneck of 31 kbps for lower-band and 27 kbps for upper-band. Given an - overall bottleneck of the codec, we use linear interpolation to get - lower-band and upper-band bottlenecks. - - 16 kHz bandwidth - ----------------- - The overall bottleneck of the coder is between 38 kbps and 45 kbps. We have - considered 7 enteries, uniformly distributed in this interval, i.e. 50, 51.2, - 52.4, 53.6, 54.8 and 56. For every entery, the lower-band and the upper-band - bottlenecks are specified in 'WebRtcIsac_kLowerBandBitRate12' and - 'WebRtcIsac_kUpperBandBitRate12' tables, respectively. E.g. the overall rate - of 53.6 kbps corresponts to a bottleneck of 32 kbps for lower-band and 30 - kbps for upper-band. Given an overall bottleneck of the codec, we use linear - interpolation to get lower-band and upper-band bottlenecks. - -*/ - -// 38 39.17 40.33 41.5 42.67 43.83 45 -static const WebRtc_Word16 WebRtcIsac_kLowerBandBitRate12[7] = { - 29000, 30000, 30000, 31000, 31000, 32000, 32000}; -static const WebRtc_Word16 WebRtcIsac_kUpperBandBitRate12[7] = { - 25000, 25000, 27000, 27000, 29000, 29000, 32000}; - -// 50 51.2 52.4 53.6 54.8 56 -static const WebRtc_Word16 WebRtcIsac_kLowerBandBitRate16[6] = { - 31000, 31000, 32000, 32000, 32000, 32000}; -static const WebRtc_Word16 WebRtcIsac_kUpperBandBitRate16[6] = { - 28000, 29000, 29000, 30000, 31000, 32000}; - -/****************************************************************************** - * WebRtcIsac_RateAllocation() - * Internal function to perform a rate-allocation for upper and lower-band, - * given a total rate. - * - * Input: - * - inRateBitPerSec : a total bottleneck in bits/sec. - * - * Output: - * - rateLBBitPerSec : a bottleneck allocated to the lower-band - * in bits/sec. - * - rateUBBitPerSec : a bottleneck allocated to the upper-band - * in bits/sec. - * - * Return value : 0 if rate allocation has been successful. - * -1 if failed to allocate rates. - */ - -WebRtc_Word16 -WebRtcIsac_RateAllocation( - WebRtc_Word32 inRateBitPerSec, - double* rateLBBitPerSec, - double* rateUBBitPerSec, - enum ISACBandwidth* bandwidthKHz) -{ - WebRtc_Word16 idx; - double idxD; - double idxErr; - if(inRateBitPerSec < 38000) - { - // If the given overall bottleneck is less than 38000 then - // then codec has to operate in wideband mode, i.e. 8 kHz - // bandwidth. - *rateLBBitPerSec = (WebRtc_Word16)((inRateBitPerSec > 32000)? - 32000:inRateBitPerSec); - *rateUBBitPerSec = 0; - *bandwidthKHz = isac8kHz; - } - else if((inRateBitPerSec >= 38000) && (inRateBitPerSec < 50000)) - { - // At a bottleneck between 38 and 50 kbps the codec is operating - // at 12 kHz bandwidth. Using xxxBandBitRate12[] to calculates - // upper/lower bottleneck - - // find the bottlenecks by linear interpolation - // step is (45000 - 38000)/6.0 we use the inverse of it. - const double stepSizeInv = 8.5714286e-4; - idxD = (inRateBitPerSec - 38000) * stepSizeInv; - idx = (idxD >= 6)? 6:((WebRtc_Word16)idxD); - idxErr = idxD - idx; - *rateLBBitPerSec = WebRtcIsac_kLowerBandBitRate12[idx]; - *rateUBBitPerSec = WebRtcIsac_kUpperBandBitRate12[idx]; - - if(idx < 6) - { - *rateLBBitPerSec += (WebRtc_Word16)(idxErr * - (WebRtcIsac_kLowerBandBitRate12[idx + 1] - - WebRtcIsac_kLowerBandBitRate12[idx])); - *rateUBBitPerSec += (WebRtc_Word16)(idxErr * - (WebRtcIsac_kUpperBandBitRate12[idx + 1] - - WebRtcIsac_kUpperBandBitRate12[idx])); - } - - *bandwidthKHz = isac12kHz; - } - else if((inRateBitPerSec >= 50000) && (inRateBitPerSec <= 56000)) - { - // A bottleneck between 50 and 56 kbps corresponds to bandwidth - // of 16 kHz. Using xxxBandBitRate16[] to calculates - // upper/lower bottleneck - - // find the bottlenecks by linear interpolation - // step is (56000 - 50000)/5 we use the inverse of it - const double stepSizeInv = 8.3333333e-4; - idxD = (inRateBitPerSec - 50000) * stepSizeInv; - idx = (idxD >= 5)? 5:((WebRtc_Word16)idxD); - idxErr = idxD - idx; - *rateLBBitPerSec = WebRtcIsac_kLowerBandBitRate16[idx]; - *rateUBBitPerSec = WebRtcIsac_kUpperBandBitRate16[idx]; - - if(idx < 5) - { - *rateLBBitPerSec += (WebRtc_Word16)(idxErr * - (WebRtcIsac_kLowerBandBitRate16[idx + 1] - - WebRtcIsac_kLowerBandBitRate16[idx])); - - *rateUBBitPerSec += (WebRtc_Word16)(idxErr * - (WebRtcIsac_kUpperBandBitRate16[idx + 1] - - WebRtcIsac_kUpperBandBitRate16[idx])); - } - - *bandwidthKHz = isac16kHz; - } - else - { - // Out-of-range botlteneck value. - return -1; - } - - // limit the values. - *rateLBBitPerSec = (*rateLBBitPerSec > 32000)? 32000:*rateLBBitPerSec; - *rateUBBitPerSec = (*rateUBBitPerSec > 32000)? 32000:*rateUBBitPerSec; - - return 0; -} - - - -int -WebRtcIsac_EncodeLb( - float* in, - ISACLBEncStruct* ISACencLB_obj, - WebRtc_Word16 codingMode, - WebRtc_Word16 bottleneckIndex) -{ - int stream_length = 0; - int err; - int k; - int iterCntr; - - double lofilt_coef[(ORDERLO+1)*SUBFRAMES]; - double hifilt_coef[(ORDERHI+1)*SUBFRAMES]; - float LP[FRAMESAMPLES_HALF]; - float HP[FRAMESAMPLES_HALF]; - - double LP_lookahead[FRAMESAMPLES_HALF]; - double HP_lookahead[FRAMESAMPLES_HALF]; - double LP_lookahead_pf[FRAMESAMPLES_HALF + QLOOKAHEAD]; - double LPw[FRAMESAMPLES_HALF]; - - double HPw[FRAMESAMPLES_HALF]; - double LPw_pf[FRAMESAMPLES_HALF]; - WebRtc_Word16 fre[FRAMESAMPLES_HALF]; /* Q7 */ - WebRtc_Word16 fim[FRAMESAMPLES_HALF]; /* Q7 */ - - double PitchLags[4]; - double PitchGains[4]; - WebRtc_Word16 PitchGains_Q12[4]; - WebRtc_Word16 AvgPitchGain_Q12; - - int frame_mode; /* 0 for 30ms, 1 for 60ms */ - int processed_samples, status = 0; - - double bits_gains; - int bmodel; - - transcode_obj transcodingParam; - double bytesLeftSpecCoding; - WebRtc_UWord16 payloadLimitBytes; - - /* copy new frame length and bottle neck rate only for the first - 10 ms data */ - if (ISACencLB_obj->buffer_index == 0) { - /* set the framelength for the next packet */ - ISACencLB_obj->current_framesamples = ISACencLB_obj->new_framelength; - } - /* frame_mode is 0 (30 ms) or 1 (60 ms) */ - frame_mode = ISACencLB_obj->current_framesamples/MAX_FRAMESAMPLES; - /* processed_samples: 480 (30, 60 ms) */ - processed_samples = ISACencLB_obj->current_framesamples/(frame_mode+1); - - /* buffer speech samples (by 10ms packet) until the framelength */ - /* is reached (30 or 60 ms) */ - /****************************************************************/ - - /* fill the buffer with 10ms input data */ - for (k = 0; k < FRAMESAMPLES_10ms; k++) { - ISACencLB_obj->data_buffer_float[k + ISACencLB_obj->buffer_index] = - in[k]; - } - - /* if buffersize is not equal to current framesize then increase index - and return. We do no encoding untill we have enough audio. */ - if (ISACencLB_obj->buffer_index + FRAMESAMPLES_10ms != processed_samples) { - ISACencLB_obj->buffer_index += FRAMESAMPLES_10ms; - return 0; - } - /* if buffer reached the right size, reset index and continue with - encoding the frame */ - ISACencLB_obj->buffer_index = 0; - - /* end of buffer function */ - /**************************/ - - /* encoding */ - /************/ - - if (frame_mode == 0 || ISACencLB_obj->frame_nb == 0 ) { - // This is to avoid Linux warnings until we change 'int' to 'Word32' - // at all places. - int intVar; - /* reset bitstream */ - ISACencLB_obj->bitstr_obj.W_upper = 0xFFFFFFFF; - ISACencLB_obj->bitstr_obj.streamval = 0; - ISACencLB_obj->bitstr_obj.stream_index = 0; - - if((codingMode == 0) && (frame_mode == 0) && - (ISACencLB_obj->enforceFrameSize == 0)) { - ISACencLB_obj->new_framelength = - WebRtcIsac_GetNewFrameLength(ISACencLB_obj->bottleneck, - ISACencLB_obj->current_framesamples); - } - - ISACencLB_obj->s2nr = WebRtcIsac_GetSnr( - ISACencLB_obj->bottleneck, ISACencLB_obj->current_framesamples); - - /* encode frame length */ - status = WebRtcIsac_EncodeFrameLen( - ISACencLB_obj->current_framesamples, &ISACencLB_obj->bitstr_obj); - if (status < 0) { - /* Wrong frame size */ - return status; - } - /* Save framelength for multiple packets memory */ - ISACencLB_obj->SaveEnc_obj.framelength = - ISACencLB_obj->current_framesamples; - - /* To be used for Redundant Coding */ - ISACencLB_obj->lastBWIdx = bottleneckIndex; - intVar = (int)bottleneckIndex; - WebRtcIsac_EncodeReceiveBw(&intVar, &ISACencLB_obj->bitstr_obj); - } - - /* split signal in two bands */ - WebRtcIsac_SplitAndFilterFloat(ISACencLB_obj->data_buffer_float, LP, HP, - LP_lookahead, HP_lookahead, &ISACencLB_obj->prefiltbankstr_obj ); - - /* estimate pitch parameters and pitch-filter lookahead signal */ - WebRtcIsac_PitchAnalysis(LP_lookahead, LP_lookahead_pf, - &ISACencLB_obj->pitchanalysisstr_obj, PitchLags, PitchGains); - - /* encode in FIX Q12 */ - - /* convert PitchGain to Fixed point */ - for (k=0;kframe_nb == 0) - { - ISACencLB_obj->SaveEnc_obj.startIdx = 0; - } else { - ISACencLB_obj->SaveEnc_obj.startIdx = 1; - } - - /* quantize & encode pitch parameters */ - WebRtcIsac_EncodePitchGain(PitchGains_Q12, &ISACencLB_obj->bitstr_obj, - &ISACencLB_obj->SaveEnc_obj); - WebRtcIsac_EncodePitchLag(PitchLags, PitchGains_Q12, - &ISACencLB_obj->bitstr_obj, &ISACencLB_obj->SaveEnc_obj); - - AvgPitchGain_Q12 = (PitchGains_Q12[0] + PitchGains_Q12[1] + - PitchGains_Q12[2] + PitchGains_Q12[3])>>2; - - /* find coefficients for perceptual pre-filters */ - WebRtcIsac_GetLpcCoefLb(LP_lookahead_pf, HP_lookahead, - &ISACencLB_obj->maskfiltstr_obj, ISACencLB_obj->s2nr, - PitchGains_Q12, lofilt_coef, hifilt_coef); - - /* code LPC model and shape - gains not quantized yet */ - WebRtcIsac_EncodeLpcLb(lofilt_coef, hifilt_coef, &bmodel, &bits_gains, - &ISACencLB_obj->bitstr_obj, &ISACencLB_obj->SaveEnc_obj); - - /* convert PitchGains back to FLOAT for pitchfilter_pre */ - for (k = 0; k < 4; k++) { - PitchGains[k] = ((float)PitchGains_Q12[k])/4096; - } - - /* Store the state of arithmetic coder before coding LPC gains */ - transcodingParam.W_upper = ISACencLB_obj->bitstr_obj.W_upper; - transcodingParam.stream_index = ISACencLB_obj->bitstr_obj.stream_index; - transcodingParam.streamval = ISACencLB_obj->bitstr_obj.streamval; - transcodingParam.stream[0] = ISACencLB_obj->bitstr_obj.stream[ - ISACencLB_obj->bitstr_obj.stream_index - 2]; - transcodingParam.stream[1] = ISACencLB_obj->bitstr_obj.stream[ - ISACencLB_obj->bitstr_obj.stream_index - 1]; - transcodingParam.stream[2] = ISACencLB_obj->bitstr_obj.stream[ - ISACencLB_obj->bitstr_obj.stream_index]; - - /* Store LPC Gains before encoding them */ - for(k = 0; k < SUBFRAMES; k++) { - transcodingParam.loFiltGain[k] = lofilt_coef[(LPC_LOBAND_ORDER+1)*k]; - transcodingParam.hiFiltGain[k] = hifilt_coef[(LPC_HIBAND_ORDER+1)*k]; - } - - /* Code gains */ - WebRtcIsac_EncodeLpcGainLb(lofilt_coef, hifilt_coef, bmodel, - &ISACencLB_obj->bitstr_obj, &ISACencLB_obj->SaveEnc_obj); - - /* Get the correct value for the payload limit and calculate the - number of bytes left for coding the spectrum.*/ - if((frame_mode == 1) && (ISACencLB_obj->frame_nb == 0)) { - /* It is a 60ms and we are in the first 30ms then the limit at - this point should be half of the assigned value */ - payloadLimitBytes = ISACencLB_obj->payloadLimitBytes60 >> 1; - } - else if (frame_mode == 0) { - /* It is a 30ms frame */ - /* Subract 3 because termination process may add 3 bytes */ - payloadLimitBytes = ISACencLB_obj->payloadLimitBytes30 - 3; - } else { - /* This is the second half of a 60ms frame. */ - /* Subract 3 because termination process may add 3 bytes */ - payloadLimitBytes = ISACencLB_obj->payloadLimitBytes60 - 3; - } - bytesLeftSpecCoding = payloadLimitBytes - transcodingParam.stream_index; - - /* perceptual pre-filtering (using normalized lattice filter) */ - /* low-band filtering */ - WebRtcIsac_NormLatticeFilterMa(ORDERLO, - ISACencLB_obj->maskfiltstr_obj.PreStateLoF, - ISACencLB_obj->maskfiltstr_obj.PreStateLoG, LP, lofilt_coef, LPw); - /* high-band filtering */ - WebRtcIsac_NormLatticeFilterMa(ORDERHI, - ISACencLB_obj->maskfiltstr_obj.PreStateHiF, - ISACencLB_obj->maskfiltstr_obj.PreStateHiG, HP, hifilt_coef, HPw); - - - /* pitch filter */ - WebRtcIsac_PitchfilterPre(LPw, LPw_pf, &ISACencLB_obj->pitchfiltstr_obj, - PitchLags, PitchGains); - - /* transform */ - WebRtcIsac_Time2Spec(LPw_pf, HPw, fre, fim, &ISACencLB_obj->fftstr_obj); - - - /* Save data for multiple packets memory */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - ISACencLB_obj->SaveEnc_obj.fre[k + - ISACencLB_obj->SaveEnc_obj.startIdx*FRAMESAMPLES_HALF] = fre[k]; - ISACencLB_obj->SaveEnc_obj.fim[k + - ISACencLB_obj->SaveEnc_obj.startIdx*FRAMESAMPLES_HALF] = fim[k]; - } - ISACencLB_obj->SaveEnc_obj.AvgPitchGain[ - ISACencLB_obj->SaveEnc_obj.startIdx] = AvgPitchGain_Q12; - - /* quantization and lossless coding */ - err = WebRtcIsac_EncodeSpecLb(fre, fim, &ISACencLB_obj->bitstr_obj, - AvgPitchGain_Q12); - if ((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - /* There has been an error but it was not too large payload - (we can cure too large payload) */ - if (frame_mode == 1 && ISACencLB_obj->frame_nb == 1) { - /* If this is the second 30ms of a 60ms frame reset - this such that in the next call encoder starts fresh. */ - ISACencLB_obj->frame_nb = 0; - } - return err; - } - iterCntr = 0; - while((ISACencLB_obj->bitstr_obj.stream_index > payloadLimitBytes) || - (err == -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - double bytesSpecCoderUsed; - double transcodeScale; - - if(iterCntr >= MAX_PAYLOAD_LIMIT_ITERATION) { - /* We were not able to limit the payload size */ - if((frame_mode == 1) && (ISACencLB_obj->frame_nb == 0)) { - /* This was the first 30ms of a 60ms frame. Although - the payload is larger than it should be but we let - the second 30ms be encoded. Maybe together we - won't exceed the limit. */ - ISACencLB_obj->frame_nb = 1; - return 0; - } else if((frame_mode == 1) && (ISACencLB_obj->frame_nb == 1)) { - ISACencLB_obj->frame_nb = 0; - } - - if(err != -ISAC_DISALLOWED_BITSTREAM_LENGTH) { - return -ISAC_PAYLOAD_LARGER_THAN_LIMIT; - } else { - return status; - } - } - - if(err == -ISAC_DISALLOWED_BITSTREAM_LENGTH) { - bytesSpecCoderUsed = STREAM_SIZE_MAX; - // being coservative - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed * 0.5; - } else { - bytesSpecCoderUsed = ISACencLB_obj->bitstr_obj.stream_index - - transcodingParam.stream_index; - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed; - } - - /* To be safe, we reduce the scale depending on - the number of iterations. */ - transcodeScale *= (1.0 - (0.9 * (double)iterCntr / - (double)MAX_PAYLOAD_LIMIT_ITERATION)); - - /* Scale the LPC Gains */ - for (k = 0; k < SUBFRAMES; k++) { - lofilt_coef[(LPC_LOBAND_ORDER+1) * k] = - transcodingParam.loFiltGain[k] * transcodeScale; - hifilt_coef[(LPC_HIBAND_ORDER+1) * k] = - transcodingParam.hiFiltGain[k] * transcodeScale; - transcodingParam.loFiltGain[k] = - lofilt_coef[(LPC_LOBAND_ORDER+1) * k]; - transcodingParam.hiFiltGain[k] = - hifilt_coef[(LPC_HIBAND_ORDER+1) * k]; - } - - /* Scale DFT coefficients */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - fre[k] = (WebRtc_Word16)(fre[k] * transcodeScale); - fim[k] = (WebRtc_Word16)(fim[k] * transcodeScale); - } - - /* Save data for multiple packets memory */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - ISACencLB_obj->SaveEnc_obj.fre[k + - ISACencLB_obj->SaveEnc_obj.startIdx * FRAMESAMPLES_HALF] = - fre[k]; - ISACencLB_obj->SaveEnc_obj.fim[k + - ISACencLB_obj->SaveEnc_obj.startIdx * FRAMESAMPLES_HALF] = - fim[k]; - } - - /* Re-store the state of arithmetic coder before coding LPC gains */ - ISACencLB_obj->bitstr_obj.W_upper = transcodingParam.W_upper; - ISACencLB_obj->bitstr_obj.stream_index = transcodingParam.stream_index; - ISACencLB_obj->bitstr_obj.streamval = transcodingParam.streamval; - ISACencLB_obj->bitstr_obj.stream[transcodingParam.stream_index - 2] = - transcodingParam.stream[0]; - ISACencLB_obj->bitstr_obj.stream[transcodingParam.stream_index - 1] = - transcodingParam.stream[1]; - ISACencLB_obj->bitstr_obj.stream[transcodingParam.stream_index] = - transcodingParam.stream[2]; - - /* Code gains */ - WebRtcIsac_EncodeLpcGainLb(lofilt_coef, hifilt_coef, bmodel, - &ISACencLB_obj->bitstr_obj, &ISACencLB_obj->SaveEnc_obj); - - /* Update the number of bytes left for encoding the spectrum */ - bytesLeftSpecCoding = payloadLimitBytes - - transcodingParam.stream_index; - - /* Encode the spectrum */ - err = WebRtcIsac_EncodeSpecLb(fre, fim, &ISACencLB_obj->bitstr_obj, - AvgPitchGain_Q12); - if((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - /* There has been an error but it was not too large - payload (we can cure too large payload) */ - if (frame_mode == 1 && ISACencLB_obj->frame_nb == 1) { - /* If this is the second 30ms of a 60ms frame reset - this such that in the next call encoder starts fresh. */ - ISACencLB_obj->frame_nb = 0; - } - return err; - } - iterCntr++; - } - - /* i.e. 60 ms framesize and just processed the first 30ms, */ - /* go back to main function to buffer the other 30ms speech frame */ - if (frame_mode == 1) - { - if(ISACencLB_obj->frame_nb == 0) - { - ISACencLB_obj->frame_nb = 1; - return 0; - } - else if(ISACencLB_obj->frame_nb == 1) - { - ISACencLB_obj->frame_nb = 0; - /* also update the framelength for next packet, - in Adaptive mode only */ - if (codingMode == 0 && (ISACencLB_obj->enforceFrameSize == 0)) - { - ISACencLB_obj->new_framelength = - WebRtcIsac_GetNewFrameLength(ISACencLB_obj->bottleneck, - ISACencLB_obj->current_framesamples); - } - } - } - else - { - ISACencLB_obj->frame_nb = 0; - } - - /* complete arithmetic coding */ - stream_length = WebRtcIsac_EncTerminate(&ISACencLB_obj->bitstr_obj); - - return stream_length; -} - - -int -WebRtcIsac_EncodeUb16( - float* in, - ISACUBEncStruct* ISACencUB_obj, - WebRtc_Word32 jitterInfo) -{ - int err; - int k; - - double lpcVecs[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - double percepFilterParams[(1 + UB_LPC_ORDER) * (SUBFRAMES<<1) + - (1 + UB_LPC_ORDER)]; - - double LP_lookahead[FRAMESAMPLES]; - WebRtc_Word16 fre[FRAMESAMPLES_HALF]; /* Q7 */ - WebRtc_Word16 fim[FRAMESAMPLES_HALF]; /* Q7 */ - - int status = 0; - - double varscale[2]; - double corr[SUBFRAMES<<1][UB_LPC_ORDER + 1]; - double lpcGains[SUBFRAMES<<1]; - transcode_obj transcodingParam; - double bytesLeftSpecCoding; - WebRtc_UWord16 payloadLimitBytes; - WebRtc_UWord16 iterCntr; - double s2nr; - - - /* buffer speech samples (by 10ms packet) until the framelength is */ - /* reached (30 or 60 ms) */ - /*********************************************************************/ - - /* fill the buffer with 10ms input data */ - for (k = 0; k < FRAMESAMPLES_10ms; k++) { - ISACencUB_obj->data_buffer_float[k + ISACencUB_obj->buffer_index] = - in[k]; - } - - /* if buffersize is not equal to current framesize, and end of file is - not reached yet, we don't do encoding unless we have the whole frame */ - if (ISACencUB_obj->buffer_index + FRAMESAMPLES_10ms < FRAMESAMPLES) { - ISACencUB_obj->buffer_index += FRAMESAMPLES_10ms; - return 0; - } - - /* end of buffer function */ - /**************************/ - - /* encoding */ - /************/ - - /* reset bitstream */ - ISACencUB_obj->bitstr_obj.W_upper = 0xFFFFFFFF; - ISACencUB_obj->bitstr_obj.streamval = 0; - ISACencUB_obj->bitstr_obj.stream_index = 0; - - /* bandwidth estimation and coding */ - /* To be used for Redundant Coding */ - WebRtcIsac_EncodeJitterInfo(jitterInfo, &ISACencUB_obj->bitstr_obj); - - status = WebRtcIsac_EncodeBandwidth(isac16kHz, - &ISACencUB_obj->bitstr_obj); - if (status < 0) { - return status; - } - - s2nr = WebRtcIsac_GetSnr(ISACencUB_obj->bottleneck, - FRAMESAMPLES); - - memcpy(lpcVecs, ISACencUB_obj->lastLPCVec, UB_LPC_ORDER * sizeof(double)); - - for (k = 0; k < FRAMESAMPLES; k++) { - LP_lookahead[k] = ISACencUB_obj->data_buffer_float[UB_LOOKAHEAD + k]; - } - - /* find coefficients for perceptual pre-filters */ - WebRtcIsac_GetLpcCoefUb(LP_lookahead, &ISACencUB_obj->maskfiltstr_obj, - &lpcVecs[UB_LPC_ORDER], corr, varscale, isac16kHz); - - memcpy(ISACencUB_obj->lastLPCVec, - &lpcVecs[(UB16_LPC_VEC_PER_FRAME - 1) * (UB_LPC_ORDER)], - sizeof(double) * UB_LPC_ORDER); - - /* code LPC model and shape - gains not quantized yet */ - WebRtcIsac_EncodeLpcUB(lpcVecs, &ISACencUB_obj->bitstr_obj, - percepFilterParams, isac16kHz, &ISACencUB_obj->SaveEnc_obj); - - - // the first set of lpc parameters are from the last sub-frame of - // the previous frame. so we don't care about them - WebRtcIsac_GetLpcGain(s2nr, &percepFilterParams[UB_LPC_ORDER + 1], - (SUBFRAMES<<1), lpcGains, corr, varscale); - - /* Store the state of arithmetic coder before coding LPC gains */ - transcodingParam.stream_index = ISACencUB_obj->bitstr_obj.stream_index; - transcodingParam.W_upper = ISACencUB_obj->bitstr_obj.W_upper; - transcodingParam.streamval = ISACencUB_obj->bitstr_obj.streamval; - transcodingParam.stream[0] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index - 2]; - transcodingParam.stream[1] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index - 1]; - transcodingParam.stream[2] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index]; - - /* Store LPC Gains before encoding them */ - for(k = 0; k < SUBFRAMES; k++) { - transcodingParam.loFiltGain[k] = lpcGains[k]; - transcodingParam.hiFiltGain[k] = lpcGains[SUBFRAMES + k]; - } - - // Store the gains for multiple encoding - memcpy(ISACencUB_obj->SaveEnc_obj.lpcGain, lpcGains, (SUBFRAMES << 1) * sizeof(double)); - - WebRtcIsac_EncodeLpcGainUb(lpcGains, &ISACencUB_obj->bitstr_obj, - ISACencUB_obj->SaveEnc_obj.lpcGainIndex); - WebRtcIsac_EncodeLpcGainUb(&lpcGains[SUBFRAMES], &ISACencUB_obj->bitstr_obj, - &ISACencUB_obj->SaveEnc_obj.lpcGainIndex[SUBFRAMES]); - - /* Get the correct value for the payload limit and calculate the number of - bytes left for coding the spectrum. It is a 30ms frame - Subract 3 because termination process may add 3 bytes */ - payloadLimitBytes = ISACencUB_obj->maxPayloadSizeBytes - - ISACencUB_obj->numBytesUsed - 3; - bytesLeftSpecCoding = payloadLimitBytes - - ISACencUB_obj->bitstr_obj.stream_index; - - for (k = 0; k < (SUBFRAMES<<1); k++) { - percepFilterParams[k*(UB_LPC_ORDER + 1) + (UB_LPC_ORDER + 1)] = - lpcGains[k]; - } - - /* perceptual pre-filtering (using normalized lattice filter) */ - /* first half-frame filtering */ - WebRtcIsac_NormLatticeFilterMa(UB_LPC_ORDER, - ISACencUB_obj->maskfiltstr_obj.PreStateLoF, - ISACencUB_obj->maskfiltstr_obj.PreStateLoG, - &ISACencUB_obj->data_buffer_float[0], - &percepFilterParams[UB_LPC_ORDER + 1], - &LP_lookahead[0]); - - /* Second half-frame filtering */ - WebRtcIsac_NormLatticeFilterMa(UB_LPC_ORDER, - ISACencUB_obj->maskfiltstr_obj.PreStateLoF, - ISACencUB_obj->maskfiltstr_obj.PreStateLoG, - &ISACencUB_obj->data_buffer_float[FRAMESAMPLES_HALF], - &percepFilterParams[(UB_LPC_ORDER + 1) + SUBFRAMES * - (UB_LPC_ORDER + 1)], &LP_lookahead[FRAMESAMPLES_HALF]); - - WebRtcIsac_Time2Spec(&LP_lookahead[0], &LP_lookahead[FRAMESAMPLES_HALF], - fre, fim, &ISACencUB_obj->fftstr_obj); - - //Store FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.realFFT, fre, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - memcpy(&ISACencUB_obj->SaveEnc_obj.imagFFT, fim, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - // Prepare the audio buffer for the next packet - // move the last 3 ms to the beginning of the buffer - memcpy(ISACencUB_obj->data_buffer_float, - &ISACencUB_obj->data_buffer_float[FRAMESAMPLES], - LB_TOTAL_DELAY_SAMPLES * sizeof(float)); - // start writing with 3 ms delay to compensate for the delay - // of the lower-band. - ISACencUB_obj->buffer_index = LB_TOTAL_DELAY_SAMPLES; - - // Save the bit-stream object at this point for FEC. - memcpy(&ISACencUB_obj->SaveEnc_obj.bitStreamObj, - &ISACencUB_obj->bitstr_obj, sizeof(Bitstr)); - - /* quantization and lossless coding */ - err = WebRtcIsac_EncodeSpecUB16(fre, fim, &ISACencUB_obj->bitstr_obj); - if ((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - return err; - } - - iterCntr = 0; - while((ISACencUB_obj->bitstr_obj.stream_index > payloadLimitBytes) || - (err == -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - double bytesSpecCoderUsed; - double transcodeScale; - - if (iterCntr >= MAX_PAYLOAD_LIMIT_ITERATION) { - /* We were not able to limit the payload size */ - return -ISAC_PAYLOAD_LARGER_THAN_LIMIT; - } - - if (err == -ISAC_DISALLOWED_BITSTREAM_LENGTH) { - bytesSpecCoderUsed = STREAM_SIZE_MAX; - // being conservative - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed * 0.5; - } else { - bytesSpecCoderUsed = ISACencUB_obj->bitstr_obj.stream_index - - transcodingParam.stream_index; - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed; - } - - /* To be safe, we reduce the scale depending on the - number of iterations. */ - transcodeScale *= (1.0 - (0.9 * (double)iterCntr/ - (double)MAX_PAYLOAD_LIMIT_ITERATION)); - - /* Scale the LPC Gains */ - for (k = 0; k < SUBFRAMES; k++) { - transcodingParam.loFiltGain[k] *= transcodeScale; - transcodingParam.hiFiltGain[k] *= transcodeScale; - } - - /* Scale DFT coefficients */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - fre[k] = (WebRtc_Word16)(fre[k] * transcodeScale + 0.5); - fim[k] = (WebRtc_Word16)(fim[k] * transcodeScale + 0.5); - } - - //Store FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.realFFT, fre, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - memcpy(&ISACencUB_obj->SaveEnc_obj.imagFFT, fim, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - - /* Store the state of arithmetic coder before coding LPC gains */ - ISACencUB_obj->bitstr_obj.W_upper = transcodingParam.W_upper; - - ISACencUB_obj->bitstr_obj.stream_index = transcodingParam.stream_index; - - ISACencUB_obj->bitstr_obj.streamval = transcodingParam.streamval; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index - 2] = - transcodingParam.stream[0]; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index - 1] = - transcodingParam.stream[1]; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index] = - transcodingParam.stream[2]; - - // Store the gains for multiple encoding - memcpy(ISACencUB_obj->SaveEnc_obj.lpcGain, lpcGains, - (SUBFRAMES << 1) * sizeof(double)); - - WebRtcIsac_EncodeLpcGainUb(transcodingParam.loFiltGain, - &ISACencUB_obj->bitstr_obj, - ISACencUB_obj->SaveEnc_obj.lpcGainIndex); - WebRtcIsac_EncodeLpcGainUb(transcodingParam.hiFiltGain, - &ISACencUB_obj->bitstr_obj, - &ISACencUB_obj->SaveEnc_obj.lpcGainIndex[SUBFRAMES]); - - /* Update the number of bytes left for encoding the spectrum */ - bytesLeftSpecCoding = payloadLimitBytes - - ISACencUB_obj->bitstr_obj.stream_index; - - // Save the bit-stream object at this point for FEC. - memcpy(&ISACencUB_obj->SaveEnc_obj.bitStreamObj, - &ISACencUB_obj->bitstr_obj, sizeof(Bitstr)); - - /* Encode the spectrum */ - err = WebRtcIsac_EncodeSpecUB16(fre, fim, &ISACencUB_obj->bitstr_obj); - if ((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - /* There has been an error but it was not too large payload - (we can cure too large payload) */ - return err; - } - iterCntr++; - } - - /* complete arithmetic coding */ - return WebRtcIsac_EncTerminate(&ISACencUB_obj->bitstr_obj); -} - - -int -WebRtcIsac_EncodeUb12( - float* in, - ISACUBEncStruct* ISACencUB_obj, - WebRtc_Word32 jitterInfo) -{ - int err; - int k; - int iterCntr; - - double lpcVecs[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; - - double percepFilterParams[(1 + UB_LPC_ORDER) * SUBFRAMES]; - float LP[FRAMESAMPLES_HALF]; - float HP[FRAMESAMPLES_HALF]; - - double LP_lookahead[FRAMESAMPLES_HALF]; - double HP_lookahead[FRAMESAMPLES_HALF]; - double LPw[FRAMESAMPLES_HALF]; - - double HPw[FRAMESAMPLES_HALF]; - WebRtc_Word16 fre[FRAMESAMPLES_HALF]; /* Q7 */ - WebRtc_Word16 fim[FRAMESAMPLES_HALF]; /* Q7 */ - - WebRtc_Word16 AvgPitchGain_Q12; - - int status = 0; - - double varscale[1]; - - double corr[UB_LPC_GAIN_DIM][UB_LPC_ORDER + 1]; - double lpcGains[SUBFRAMES]; - transcode_obj transcodingParam; - double bytesLeftSpecCoding; - WebRtc_UWord16 payloadLimitBytes; - double s2nr; - - /* buffer speech samples (by 10ms packet) until the framelength is */ - /* reached (30 or 60 ms) */ - /********************************************************************/ - - /* fill the buffer with 10ms input data */ - for (k=0; kdata_buffer_float[k + ISACencUB_obj->buffer_index] = - in[k]; - } - - /* if buffer-size is not equal to current frame-size then increase the - index and return. We do the encoding when we have enough audio. */ - if (ISACencUB_obj->buffer_index + FRAMESAMPLES_10ms < FRAMESAMPLES) { - ISACencUB_obj->buffer_index += FRAMESAMPLES_10ms; - return 0; - } - /* if buffer reached the right size, reset index and continue - with encoding the frame */ - ISACencUB_obj->buffer_index = 0; - - /* end of buffer function */ - /**************************/ - - /* encoding */ - /************/ - - /* reset bitstream */ - ISACencUB_obj->bitstr_obj.W_upper = 0xFFFFFFFF; - ISACencUB_obj->bitstr_obj.streamval = 0; - ISACencUB_obj->bitstr_obj.stream_index = 0; - - /* bandwidth estimation and coding */ - /* To be used for Redundant Coding */ - WebRtcIsac_EncodeJitterInfo(jitterInfo, &ISACencUB_obj->bitstr_obj); - - status = WebRtcIsac_EncodeBandwidth(isac12kHz, - &ISACencUB_obj->bitstr_obj); - if (status < 0) { - return status; - } - - - s2nr = WebRtcIsac_GetSnr(ISACencUB_obj->bottleneck, - FRAMESAMPLES); - - /* split signal in two bands */ - WebRtcIsac_SplitAndFilterFloat(ISACencUB_obj->data_buffer_float, HP, LP, - HP_lookahead, LP_lookahead, &ISACencUB_obj->prefiltbankstr_obj); - - AvgPitchGain_Q12 = 0; - - /* find coefficients for perceptual pre-filters */ - WebRtcIsac_GetLpcCoefUb(LP_lookahead, &ISACencUB_obj->maskfiltstr_obj, - lpcVecs, corr, varscale, isac12kHz); - - /* code LPC model and shape - gains not quantized yet */ - WebRtcIsac_EncodeLpcUB(lpcVecs, &ISACencUB_obj->bitstr_obj, - percepFilterParams, isac12kHz, &ISACencUB_obj->SaveEnc_obj); - - WebRtcIsac_GetLpcGain(s2nr, percepFilterParams, SUBFRAMES, lpcGains, - corr, varscale); - - /* Store the state of arithmetic coder before coding LPC gains */ - transcodingParam.W_upper = ISACencUB_obj->bitstr_obj.W_upper; - - transcodingParam.stream_index = ISACencUB_obj->bitstr_obj.stream_index; - - transcodingParam.streamval = ISACencUB_obj->bitstr_obj.streamval; - - transcodingParam.stream[0] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index - 2]; - - transcodingParam.stream[1] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index - 1]; - - transcodingParam.stream[2] = ISACencUB_obj->bitstr_obj.stream[ - ISACencUB_obj->bitstr_obj.stream_index]; - - /* Store LPC Gains before encoding them */ - for(k = 0; k < SUBFRAMES; k++) { - transcodingParam.loFiltGain[k] = lpcGains[k]; - } - - // Store the gains for multiple encoding - memcpy(ISACencUB_obj->SaveEnc_obj.lpcGain, lpcGains, SUBFRAMES * - sizeof(double)); - - WebRtcIsac_EncodeLpcGainUb(lpcGains, &ISACencUB_obj->bitstr_obj, - ISACencUB_obj->SaveEnc_obj.lpcGainIndex); - - for(k = 0; k < SUBFRAMES; k++) { - percepFilterParams[k*(UB_LPC_ORDER + 1)] = lpcGains[k]; - } - - /* perceptual pre-filtering (using normalized lattice filter) */ - /* low-band filtering */ - WebRtcIsac_NormLatticeFilterMa(UB_LPC_ORDER, - ISACencUB_obj->maskfiltstr_obj.PreStateLoF, - ISACencUB_obj->maskfiltstr_obj.PreStateLoG, LP, percepFilterParams, - LPw); - - /* Get the correct value for the payload limit and calculate the number - of bytes left for coding the spectrum. It is a 30ms frame Subract 3 - because termination process may add 3 bytes */ - payloadLimitBytes = ISACencUB_obj->maxPayloadSizeBytes - - ISACencUB_obj->numBytesUsed - 3; - bytesLeftSpecCoding = payloadLimitBytes - - ISACencUB_obj->bitstr_obj.stream_index; - - memset(HPw, 0, sizeof(double) * FRAMESAMPLES_HALF); - - /* transform */ - WebRtcIsac_Time2Spec(LPw, HPw, fre, fim, &ISACencUB_obj->fftstr_obj); - - //Store real FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.realFFT, fre, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - //Store imaginary FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.imagFFT, fim, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - // Save the bit-stream object at this point for FEC. - memcpy(&ISACencUB_obj->SaveEnc_obj.bitStreamObj, - &ISACencUB_obj->bitstr_obj, sizeof(Bitstr)); - - /* quantization and lossless coding */ - err = WebRtcIsac_EncodeSpecUB12(fre, fim, &ISACencUB_obj->bitstr_obj); - if ((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - /* There has been an error but it was not too large - payload (we can cure too large payload) */ - return err; - } - iterCntr = 0; - while((ISACencUB_obj->bitstr_obj.stream_index > payloadLimitBytes) || - (err == -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - double bytesSpecCoderUsed; - double transcodeScale; - - if (iterCntr >= MAX_PAYLOAD_LIMIT_ITERATION) { - /* We were not able to limit the payload size */ - return -ISAC_PAYLOAD_LARGER_THAN_LIMIT; - } - - if (err == -ISAC_DISALLOWED_BITSTREAM_LENGTH) { - bytesSpecCoderUsed = STREAM_SIZE_MAX; - // being coservative - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed * 0.5; - } else { - bytesSpecCoderUsed = ISACencUB_obj->bitstr_obj.stream_index - - transcodingParam.stream_index; - transcodeScale = bytesLeftSpecCoding / bytesSpecCoderUsed; - } - - /* To be safe, we reduce the scale depending on the - number of iterations. */ - transcodeScale *= (1.0 - (0.9 * (double)iterCntr/ - (double)MAX_PAYLOAD_LIMIT_ITERATION)); - - /* Scale the LPC Gains */ - for (k = 0; k < SUBFRAMES; k++) { - transcodingParam.loFiltGain[k] *= transcodeScale; - } - - /* Scale DFT coefficients */ - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - fre[k] = (WebRtc_Word16)(fre[k] * transcodeScale + 0.5); - fim[k] = (WebRtc_Word16)(fim[k] * transcodeScale + 0.5); - } - - //Store real FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.realFFT, fre, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - //Store imaginary FFT coefficients for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.imagFFT, fim, - FRAMESAMPLES_HALF * sizeof(WebRtc_Word16)); - - - /* Re-store the state of arithmetic coder before coding LPC gains */ - ISACencUB_obj->bitstr_obj.W_upper = transcodingParam.W_upper; - - ISACencUB_obj->bitstr_obj.stream_index = transcodingParam.stream_index; - - ISACencUB_obj->bitstr_obj.streamval = transcodingParam.streamval; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index - 2] = - transcodingParam.stream[0]; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index - 1] = - transcodingParam.stream[1]; - - ISACencUB_obj->bitstr_obj.stream[transcodingParam.stream_index] = - transcodingParam.stream[2]; - - // Store the gains for multiple encoding - memcpy(&ISACencUB_obj->SaveEnc_obj.lpcGain, lpcGains, SUBFRAMES * - sizeof(double)); - - // encode LPC gain and store quantization indices. HAving quantization - // indices reduces transcoding complexity if 'scale factor' is 1. - WebRtcIsac_EncodeLpcGainUb(transcodingParam.loFiltGain, - &ISACencUB_obj->bitstr_obj, - ISACencUB_obj->SaveEnc_obj.lpcGainIndex); - - // Save the bit-stream object at this point for FEC. - memcpy(&ISACencUB_obj->SaveEnc_obj.bitStreamObj, - &ISACencUB_obj->bitstr_obj, sizeof(Bitstr)); - - /* Update the number of bytes left for encoding the spectrum */ - bytesLeftSpecCoding = payloadLimitBytes - - ISACencUB_obj->bitstr_obj.stream_index; - - /* Encode the spectrum */ - err = WebRtcIsac_EncodeSpecUB12(fre, fim, - &ISACencUB_obj->bitstr_obj); - if ((err < 0) && (err != -ISAC_DISALLOWED_BITSTREAM_LENGTH)) { - /* There has been an error but it was not too large payload - (we can cure too large payload) */ - return err; - } - iterCntr++; - } - - /* complete arithmetic coding */ - return WebRtcIsac_EncTerminate(&ISACencUB_obj->bitstr_obj); -} - - - - - - -/* This function is used to create a new bitstream with new BWE. - The same data as previously encoded with the function WebRtcIsac_Encoder(). - The data needed is taken from the struct, where it was stored - when calling the encoder. */ - -int WebRtcIsac_EncodeStoredDataLb( - const ISAC_SaveEncData_t* ISACSavedEnc_obj, - Bitstr* ISACBitStr_obj, - int BWnumber, - float scale) -{ - int ii; - int status; - int BWno = BWnumber; - - const WebRtc_UWord16 *WebRtcIsac_kQPitchGainCdf_ptr[1]; - const WebRtc_UWord16 **cdf; - - double tmpLPCcoeffs_lo[(ORDERLO+1)*SUBFRAMES*2]; - double tmpLPCcoeffs_hi[(ORDERHI+1)*SUBFRAMES*2]; - int tmpLPCindex_g[12*2]; - WebRtc_Word16 tmp_fre[FRAMESAMPLES], tmp_fim[FRAMESAMPLES]; - - /* Sanity Check - possible values for BWnumber is 0 - 23 */ - if ((BWnumber < 0) || (BWnumber > 23)) { - return -ISAC_RANGE_ERROR_BW_ESTIMATOR; - } - - /* reset bitstream */ - ISACBitStr_obj->W_upper = 0xFFFFFFFF; - ISACBitStr_obj->streamval = 0; - ISACBitStr_obj->stream_index = 0; - - /* encode frame length */ - status = WebRtcIsac_EncodeFrameLen(ISACSavedEnc_obj->framelength, - ISACBitStr_obj); - if (status < 0) { - /* Wrong frame size */ - return status; - } - - /* Transcoding */ - if ((scale > 0.0) && (scale < 1.0)) { - /* Compensate LPC gain */ - for (ii = 0; - ii < ((ORDERLO + 1)* SUBFRAMES * (1 + ISACSavedEnc_obj->startIdx)); - ii++) { - tmpLPCcoeffs_lo[ii] = scale * ISACSavedEnc_obj->LPCcoeffs_lo[ii]; - } - for (ii = 0; - ii < ((ORDERHI + 1) * SUBFRAMES *(1 + ISACSavedEnc_obj->startIdx)); - ii++) { - tmpLPCcoeffs_hi[ii] = scale * ISACSavedEnc_obj->LPCcoeffs_hi[ii]; - } - /* Scale DFT */ - for (ii = 0; - ii < (FRAMESAMPLES_HALF * (1 + ISACSavedEnc_obj->startIdx)); - ii++) { - tmp_fre[ii] = (WebRtc_Word16)((scale) * - (float)ISACSavedEnc_obj->fre[ii]) ; - tmp_fim[ii] = (WebRtc_Word16)((scale) * - (float)ISACSavedEnc_obj->fim[ii]) ; - } - } else { - for (ii = 0; - ii < (KLT_ORDER_GAIN * (1 + ISACSavedEnc_obj->startIdx)); - ii++) { - tmpLPCindex_g[ii] = ISACSavedEnc_obj->LPCindex_g[ii]; - } - for (ii = 0; - ii < (FRAMESAMPLES_HALF * (1 + ISACSavedEnc_obj->startIdx)); - ii++) { - tmp_fre[ii] = ISACSavedEnc_obj->fre[ii]; - tmp_fim[ii] = ISACSavedEnc_obj->fim[ii]; - } - } - - /* encode bandwidth estimate */ - WebRtcIsac_EncodeReceiveBw(&BWno, ISACBitStr_obj); - - /* Loop over number of 30 msec */ - for (ii = 0; ii <= ISACSavedEnc_obj->startIdx; ii++) { - /* encode pitch gains */ - *WebRtcIsac_kQPitchGainCdf_ptr = WebRtcIsac_kQPitchGainCdf; - WebRtcIsac_EncHistMulti(ISACBitStr_obj, - &ISACSavedEnc_obj->pitchGain_index[ii], WebRtcIsac_kQPitchGainCdf_ptr, 1); - - /* entropy coding of quantization pitch lags */ - /* voicing classificiation */ - if (ISACSavedEnc_obj->meanGain[ii] < 0.2) { - cdf = WebRtcIsac_kQPitchLagCdfPtrLo; - } else if (ISACSavedEnc_obj->meanGain[ii] < 0.4) { - cdf = WebRtcIsac_kQPitchLagCdfPtrMid; - } else { - cdf = WebRtcIsac_kQPitchLagCdfPtrHi; - } - WebRtcIsac_EncHistMulti(ISACBitStr_obj, - &ISACSavedEnc_obj->pitchIndex[PITCH_SUBFRAMES*ii], cdf, - PITCH_SUBFRAMES); - - /* LPC */ - /* entropy coding of model number */ - WebRtcIsac_EncHistMulti(ISACBitStr_obj, - &ISACSavedEnc_obj->LPCmodel[ii], WebRtcIsac_kQKltModelCdfPtr, 1); - - /* entropy coding of quantization indices - LPC shape only */ - WebRtcIsac_EncHistMulti(ISACBitStr_obj, - &ISACSavedEnc_obj->LPCindex_s[KLT_ORDER_SHAPE*ii], - WebRtcIsac_kQKltCdfPtrShape[ISACSavedEnc_obj->LPCmodel[ii]], - KLT_ORDER_SHAPE); - - /* If transcoding, get new LPC gain indices */ - if (scale < 1.0) { - WebRtcIsac_TranscodeLPCCoef(&tmpLPCcoeffs_lo[(ORDERLO+1) * - SUBFRAMES*ii], &tmpLPCcoeffs_hi[(ORDERHI+1)*SUBFRAMES*ii], - ISACSavedEnc_obj->LPCmodel[ii], - &tmpLPCindex_g[KLT_ORDER_GAIN * ii]); - } - - /* entropy coding of quantization indices - LPC gain */ - WebRtcIsac_EncHistMulti(ISACBitStr_obj, - &tmpLPCindex_g[KLT_ORDER_GAIN*ii], WebRtcIsac_kQKltCdfPtrGain[ - ISACSavedEnc_obj->LPCmodel[ii]], KLT_ORDER_GAIN); - - /* quantization and lossless coding */ - status = WebRtcIsac_EncodeSpecLb(&tmp_fre[ii*FRAMESAMPLES_HALF], - &tmp_fim[ii*FRAMESAMPLES_HALF], ISACBitStr_obj, - ISACSavedEnc_obj->AvgPitchGain[ii]); - if (status < 0) { - return status; - } - } - - /* complete arithmetic coding */ - return WebRtcIsac_EncTerminate(ISACBitStr_obj); -} - - - - -int WebRtcIsac_EncodeStoredDataUb12( - const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, - Bitstr* bitStream, - WebRtc_Word32 jitterInfo, - float scale) -{ - int n; - int err; - double lpcGain[SUBFRAMES]; - WebRtc_Word16 realFFT[FRAMESAMPLES]; - WebRtc_Word16 imagFFT[FRAMESAMPLES]; - - /* reset bitstream */ - bitStream->W_upper = 0xFFFFFFFF; - bitStream->streamval = 0; - bitStream->stream_index = 0; - - // Encode jitter index - WebRtcIsac_EncodeJitterInfo(jitterInfo, bitStream); - - err = WebRtcIsac_EncodeBandwidth(isac12kHz, bitStream); - if(err < 0) - { - return err; - } - - // Encode LPC-shape - WebRtcIsac_EncHistMulti(bitStream, ISACSavedEnc_obj->indexLPCShape, - WebRtcIsac_kLpcShapeCdfMatUb12, UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME); - - - // we only consider scales between zero and one. - if((scale <= 0.0) || (scale > 1.0)) - { - scale = 1.0f; - } - - if(scale == 1.0f) - { - //memcpy(lpcGain, ISACSavedEnc_obj->lpcGain, SUBFRAMES * sizeof(double)); - WebRtcIsac_EncHistMulti(bitStream, ISACSavedEnc_obj->lpcGainIndex, - WebRtcIsac_kLpcGainCdfMat, UB_LPC_GAIN_DIM); - // store FFT coefficients - err = WebRtcIsac_EncodeSpecUB12(ISACSavedEnc_obj->realFFT, - ISACSavedEnc_obj->imagFFT, bitStream); - } - else - { - /* scale lpc gain and FFT coefficients */ - for(n = 0; n < SUBFRAMES; n++) - { - lpcGain[n] = scale * ISACSavedEnc_obj->lpcGain[n]; - } - // store lpc gain - WebRtcIsac_StoreLpcGainUb(lpcGain, bitStream); - for(n = 0; n < FRAMESAMPLES; n++) - { - realFFT[n] = (WebRtc_Word16)(scale * (float)ISACSavedEnc_obj->realFFT[n] + 0.5f); - imagFFT[n] = (WebRtc_Word16)(scale * (float)ISACSavedEnc_obj->imagFFT[n] + 0.5f); - } - // store FFT coefficients - err = WebRtcIsac_EncodeSpecUB12(realFFT, imagFFT, bitStream); - } - if(err < 0) - { - // error happened while encoding FFT coefficients. - return err; - } - - /* complete arithmetic coding */ - return WebRtcIsac_EncTerminate(bitStream); -} - - -int -WebRtcIsac_EncodeStoredDataUb16( - const ISACUBSaveEncDataStruct* ISACSavedEnc_obj, - Bitstr* bitStream, - WebRtc_Word32 jitterInfo, - float scale) -{ - int n; - int err; - double lpcGain[SUBFRAMES << 1]; - WebRtc_Word16 realFFT[FRAMESAMPLES]; - WebRtc_Word16 imagFFT[FRAMESAMPLES]; - - /* reset bitstream */ - bitStream->W_upper = 0xFFFFFFFF; - bitStream->streamval = 0; - bitStream->stream_index = 0; - - // Encode jitter index - WebRtcIsac_EncodeJitterInfo(jitterInfo, bitStream); - - err = WebRtcIsac_EncodeBandwidth(isac16kHz, bitStream); - if(err < 0) - { - return err; - } - - WebRtcIsac_EncHistMulti(bitStream, ISACSavedEnc_obj->indexLPCShape, - WebRtcIsac_kLpcShapeCdfMatUb16, UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME); - - // we only consider scales between zero and one. - if((scale <= 0.0) || (scale > 1.0)) - { - scale = 1.0f; - } - - if(scale == 1.0f) - { - // store gains - WebRtcIsac_EncHistMulti(bitStream, ISACSavedEnc_obj->lpcGainIndex, - WebRtcIsac_kLpcGainCdfMat, UB_LPC_GAIN_DIM); - WebRtcIsac_EncHistMulti(bitStream, &ISACSavedEnc_obj->lpcGainIndex[SUBFRAMES], - WebRtcIsac_kLpcGainCdfMat, UB_LPC_GAIN_DIM); - // store FFT coefficients - err = WebRtcIsac_EncodeSpecUB16(ISACSavedEnc_obj->realFFT, - ISACSavedEnc_obj->imagFFT, bitStream); - - } - else - { - /* Scale Gain */ - for(n = 0; n < SUBFRAMES; n++) - { - lpcGain[n] = scale * ISACSavedEnc_obj->lpcGain[n]; - lpcGain[n + SUBFRAMES] = scale * ISACSavedEnc_obj->lpcGain[n + SUBFRAMES]; - } - // store lpc gain - WebRtcIsac_StoreLpcGainUb(lpcGain, bitStream); - WebRtcIsac_StoreLpcGainUb(&lpcGain[SUBFRAMES], bitStream); - /* scale FFT coefficients */ - for(n = 0; n < FRAMESAMPLES; n++) - { - realFFT[n] = (WebRtc_Word16)(scale * (float)ISACSavedEnc_obj->realFFT[n] + 0.5f); - imagFFT[n] = (WebRtc_Word16)(scale * (float)ISACSavedEnc_obj->imagFFT[n] + 0.5f); - } - // store FFT coefficients - err = WebRtcIsac_EncodeSpecUB16(realFFT, imagFFT, bitStream); - } - - if(err < 0) - { - // error happened while encoding FFT coefficients. - return err; - } - - /* complete arithmetic coding */ - return WebRtcIsac_EncTerminate(bitStream); -} - - -WebRtc_Word16 -WebRtcIsac_GetRedPayloadUb( - const ISACUBSaveEncDataStruct* ISACSavedEncObj, - Bitstr* bitStreamObj, - enum ISACBandwidth bandwidth) -{ - int n; - WebRtc_Word16 status; - WebRtc_Word16 realFFT[FRAMESAMPLES_HALF]; - WebRtc_Word16 imagFFT[FRAMESAMPLES_HALF]; - - // store bit-stream object. - memcpy(bitStreamObj, &ISACSavedEncObj->bitStreamObj, sizeof(Bitstr)); - - // Scale FFT coefficients. - for(n = 0; n < FRAMESAMPLES_HALF; n++) - { - realFFT[n] = (WebRtc_Word16)((float)ISACSavedEncObj->realFFT[n] * - RCU_TRANSCODING_SCALE_UB + 0.5); - imagFFT[n] = (WebRtc_Word16)((float)ISACSavedEncObj->imagFFT[n] * - RCU_TRANSCODING_SCALE_UB + 0.5); - } - - switch(bandwidth) - { - case isac12kHz: - { - status = WebRtcIsac_EncodeSpecUB12(realFFT, imagFFT, bitStreamObj); - break; - } - case isac16kHz: - { - status = WebRtcIsac_EncodeSpecUB16(realFFT, imagFFT, bitStreamObj); - break; - } - default: - return -1; - } - - if(status < 0) - { - // error happened - return status; - } - else - { - // terminate entropy coding - return WebRtcIsac_EncTerminate(bitStreamObj); - } -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.c b/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.c deleted file mode 100644 index 1881db21e..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.c +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * code_LPC_UB.c - * - * This file contains definition of functions used to - * encode LPC parameters (Shape & gain) of the upper band. - * - */ - -#include "encode_lpc_swb.h" -#include "typedefs.h" -#include "settings.h" - -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" -#include "lpc_gain_swb_tables.h" - -#include -#include -#include - -/****************************************************************************** - * WebRtcIsac_RemoveLarMean() - * - * Remove the means from LAR coefficients. - * - * Input: - * -lar : pointer to lar vectors. LAR vectors are - * concatenated. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -lar : pointer to mean-removed LAR:s. - * - * - */ -WebRtc_Word16 -WebRtcIsac_RemoveLarMean( - double* lar, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 coeffCntr; - WebRtc_Word16 vecCntr; - WebRtc_Word16 numVec; - const double* meanLAR; - switch(bandwidth) - { - case isac12kHz: - { - numVec = UB_LPC_VEC_PER_FRAME; - meanLAR = WebRtcIsac_kMeanLarUb12; - break; - } - case isac16kHz: - { - numVec = UB16_LPC_VEC_PER_FRAME; - meanLAR = meanLARUB16; - break; - } - default: - return -1; - } - - for(vecCntr = 0; vecCntr < numVec; vecCntr++) - { - for(coeffCntr = 0; coeffCntr < UB_LPC_ORDER; coeffCntr++) - { - // REMOVE MEAN - *lar++ -= meanLAR[coeffCntr]; - } - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_DecorrelateIntraVec() - * - * Remove the correlation amonge the components of LAR vectors. If LAR vectors - * of one frame are put in a matrix where each column is a LAR vector of a - * sub-frame, then this is equivalent to multiplying the LAR matrix with - * a decorrelting mtrix from left. - * - * Input: - * -inLar : pointer to mean-removed LAR vecrtors. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : decorrelated LAR vectors. - */ -WebRtc_Word16 -WebRtcIsac_DecorrelateIntraVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth) -{ - const double* ptrData; - const double* ptrRow; - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - WebRtc_Word16 larVecCntr; - WebRtc_Word16 numVec; - const double* decorrMat; - switch(bandwidth) - { - case isac12kHz: - { - decorrMat = &WebRtcIsac_kIntraVecDecorrMatUb12[0][0]; - numVec = UB_LPC_VEC_PER_FRAME; - break; - } - case isac16kHz: - { - decorrMat = &WebRtcIsac_kIintraVecDecorrMatUb16[0][0]; - numVec = UB16_LPC_VEC_PER_FRAME; - break; - } - default: - return -1; - } - - // - // decorrMat * data - // - // data is assumed to contain 'numVec' of LAR - // vectors (mean removed) each of dimension 'UB_LPC_ORDER' - // concatenated one after the other. - // - - ptrData = data; - for(larVecCntr = 0; larVecCntr < numVec; larVecCntr++) - { - for(rowCntr = 0; rowCntr < UB_LPC_ORDER; rowCntr++) - { - ptrRow = &decorrMat[rowCntr * UB_LPC_ORDER]; - *out = 0; - for(colCntr = 0; colCntr < UB_LPC_ORDER; colCntr++) - { - *out += ptrData[colCntr] * ptrRow[colCntr]; - } - out++; - } - ptrData += UB_LPC_ORDER; - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_DecorrelateInterVec() - * - * Remover the correlation among mean-removed LAR vectors. If LAR vectors - * of one frame are put in a matrix where each column is a LAR vector of a - * sub-frame, then this is equivalent to multiplying the LAR matrix with - * a decorrelting mtrix from right. - * - * Input: - * -data : pointer to matrix of LAR vectors. The matrix - * is stored column-wise. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : decorrelated LAR vectors. - */ -WebRtc_Word16 -WebRtcIsac_DecorrelateInterVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 coeffCntr; - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - const double* decorrMat; - WebRtc_Word16 interVecDim; - - switch(bandwidth) - { - case isac12kHz: - { - decorrMat = &WebRtcIsac_kInterVecDecorrMatUb12[0][0]; - interVecDim = UB_LPC_VEC_PER_FRAME; - break; - } - case isac16kHz: - { - decorrMat = &WebRtcIsac_kInterVecDecorrMatUb16[0][0]; - interVecDim = UB16_LPC_VEC_PER_FRAME; - break; - } - default: - return -1; - } - - // - // data * decorrMat - // - // data is of size 'interVecDim' * 'UB_LPC_ORDER' - // That is 'interVecDim' of LAR vectors (mean removed) - // in columns each of dimension 'UB_LPC_ORDER'. - // matrix is stored column-wise. - // - - for(coeffCntr = 0; coeffCntr < UB_LPC_ORDER; coeffCntr++) - { - for(colCntr = 0; colCntr < interVecDim; colCntr++) - { - out[coeffCntr + colCntr * UB_LPC_ORDER] = 0; - for(rowCntr = 0; rowCntr < interVecDim; rowCntr++) - { - out[coeffCntr + colCntr * UB_LPC_ORDER] += - data[coeffCntr + rowCntr * UB_LPC_ORDER] * - decorrMat[rowCntr * interVecDim + colCntr]; - } - } - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_QuantizeUncorrLar() - * - * Quantize the uncorrelated parameters. - * - * Input: - * -data : uncorrelated LAR vectors. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -data : quantized version of the input. - * -idx : pointer to quantization indices. - */ -double -WebRtcIsac_QuantizeUncorrLar( - double* data, - int* recIdx, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 cntr; - WebRtc_Word32 idx; - WebRtc_Word16 interVecDim; - const double* leftRecPoint; - double quantizationStepSize; - const WebRtc_Word16* numQuantCell; - switch(bandwidth) - { - case isac12kHz: - { - leftRecPoint = WebRtcIsac_kLpcShapeLeftRecPointUb12; - quantizationStepSize = WebRtcIsac_kLpcShapeQStepSizeUb12; - numQuantCell = WebRtcIsac_kLpcShapeNumRecPointUb12; - interVecDim = UB_LPC_VEC_PER_FRAME; - break; - } - case isac16kHz: - { - leftRecPoint = WebRtcIsac_kLpcShapeLeftRecPointUb16; - quantizationStepSize = WebRtcIsac_kLpcShapeQStepSizeUb16; - numQuantCell = WebRtcIsac_kLpcShapeNumRecPointUb16; - interVecDim = UB16_LPC_VEC_PER_FRAME; - break; - } - default: - return -1; - } - - // - // Quantize the parametrs. - // - for(cntr = 0; cntr < UB_LPC_ORDER * interVecDim; cntr++) - { - idx = (WebRtc_Word32)floor((*data - leftRecPoint[cntr]) / - quantizationStepSize + 0.5); - if(idx < 0) - { - idx = 0; - } - else if(idx >= numQuantCell[cntr]) - { - idx = numQuantCell[cntr] - 1; - } - - *data++ = leftRecPoint[cntr] + idx * quantizationStepSize; - *recIdx++ = idx; - } - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_DequantizeLpcParam() - * - * Get the quantized value of uncorrelated LARs given the quantization indices. - * - * Input: - * -idx : pointer to quantiztion indices. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : pointer to quantized values. - */ -WebRtc_Word16 -WebRtcIsac_DequantizeLpcParam( - const int* idx, - double* out, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 cntr; - WebRtc_Word16 interVecDim; - const double* leftRecPoint; - double quantizationStepSize; - - switch(bandwidth) - { - case isac12kHz: - { - leftRecPoint = WebRtcIsac_kLpcShapeLeftRecPointUb12; - quantizationStepSize = WebRtcIsac_kLpcShapeQStepSizeUb12; - interVecDim = UB_LPC_VEC_PER_FRAME; - break; - } - case isac16kHz: - { - leftRecPoint = WebRtcIsac_kLpcShapeLeftRecPointUb16; - quantizationStepSize = WebRtcIsac_kLpcShapeQStepSizeUb16; - interVecDim = UB16_LPC_VEC_PER_FRAME; - break; - } - default: - return -1; - } - - // - // Dequantize given the quantization indices - // - - for(cntr = 0; cntr < UB_LPC_ORDER * interVecDim; cntr++) - { - *out++ = leftRecPoint[cntr] + *idx++ * quantizationStepSize; - } - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_CorrelateIntraVec() - * - * This is the inverse of WebRtcIsac_DecorrelateIntraVec(). - * - * Input: - * -data : uncorrelated parameters. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : correlated parametrs. - */ -WebRtc_Word16 -WebRtcIsac_CorrelateIntraVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 vecCntr; - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - WebRtc_Word16 numVec; - const double* ptrData; - const double* intraVecDecorrMat; - - switch(bandwidth) - { - case isac12kHz: - { - numVec = UB_LPC_VEC_PER_FRAME; - intraVecDecorrMat = &WebRtcIsac_kIntraVecDecorrMatUb12[0][0]; - break; - } - case isac16kHz: - { - numVec = UB16_LPC_VEC_PER_FRAME; - intraVecDecorrMat = &WebRtcIsac_kIintraVecDecorrMatUb16[0][0]; - break; - } - default: - return -1; - } - - - ptrData = data; - for(vecCntr = 0; vecCntr < numVec; vecCntr++) - { - for(colCntr = 0; colCntr < UB_LPC_ORDER; colCntr++) - { - *out = 0; - for(rowCntr = 0; rowCntr < UB_LPC_ORDER; rowCntr++) - { - *out += ptrData[rowCntr] * - intraVecDecorrMat[rowCntr * UB_LPC_ORDER + colCntr]; - } - out++; - } - ptrData += UB_LPC_ORDER; - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_CorrelateInterVec() - * - * This is the inverse of WebRtcIsac_DecorrelateInterVec(). - * - * Input: - * -data - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : correlated parametrs. - */ -WebRtc_Word16 -WebRtcIsac_CorrelateInterVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 coeffCntr; - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - WebRtc_Word16 interVecDim; - double myVec[UB16_LPC_VEC_PER_FRAME]; - const double* interVecDecorrMat; - - switch(bandwidth) - { - case isac12kHz: - { - interVecDim = UB_LPC_VEC_PER_FRAME; - interVecDecorrMat = &WebRtcIsac_kInterVecDecorrMatUb12[0][0]; - break; - } - case isac16kHz: - { - interVecDim = UB16_LPC_VEC_PER_FRAME; - interVecDecorrMat = &WebRtcIsac_kInterVecDecorrMatUb16[0][0]; - break; - } - default: - return -1; - } - - for(coeffCntr = 0; coeffCntr < UB_LPC_ORDER; coeffCntr++) - { - for(rowCntr = 0; rowCntr < interVecDim; rowCntr++) - { - myVec[rowCntr] = 0; - for(colCntr = 0; colCntr < interVecDim; colCntr++) - { - myVec[rowCntr] += data[coeffCntr + colCntr * UB_LPC_ORDER] * //*ptrData * - interVecDecorrMat[rowCntr * interVecDim + colCntr]; - //ptrData += UB_LPC_ORDER; - } - } - - for(rowCntr = 0; rowCntr < interVecDim; rowCntr++) - { - out[coeffCntr + rowCntr * UB_LPC_ORDER] = myVec[rowCntr]; - } - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_AddLarMean() - * - * This is the inverse of WebRtcIsac_RemoveLarMean() - * - * Input: - * -data : pointer to mean-removed LAR:s. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -data : pointer to LARs. - */ -WebRtc_Word16 -WebRtcIsac_AddLarMean( - double* data, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 coeffCntr; - WebRtc_Word16 vecCntr; - WebRtc_Word16 numVec; - const double* meanLAR; - - switch(bandwidth) - { - case isac12kHz: - { - numVec = UB_LPC_VEC_PER_FRAME; - meanLAR = WebRtcIsac_kMeanLarUb12; - break; - } - case isac16kHz: - { - numVec = UB16_LPC_VEC_PER_FRAME; - meanLAR = meanLARUB16; - break; - } - default: - return -1; - } - - for(vecCntr = 0; vecCntr < numVec; vecCntr++) - { - for(coeffCntr = 0; coeffCntr < UB_LPC_ORDER; coeffCntr++) - { - *data++ += meanLAR[coeffCntr]; - } - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_ToLogDomainRemoveMean() - * - * Transform the LPC gain to log domain then remove the mean value. - * - * Input: - * -lpcGain : pointer to LPC Gain, expecting 6 LPC gains - * - * Output: - * -lpcGain : mean-removed in log domain. - */ -WebRtc_Word16 -WebRtcIsac_ToLogDomainRemoveMean( - double* data) -{ - WebRtc_Word16 coeffCntr; - for(coeffCntr = 0; coeffCntr < UB_LPC_GAIN_DIM; coeffCntr++) - { - data[coeffCntr] = log(data[coeffCntr]) - WebRtcIsac_kMeanLpcGain; - } - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_DecorrelateLPGain() - * - * Decorrelate LPC gains. There are 6 LPC Gains per frame. This is like - * multiplying gain vector with decorrelating matrix. - * - * Input: - * -data : LPC gain in log-domain with mean removed. - * - * Output: - * -out : decorrelated parameters. - */ -WebRtc_Word16 WebRtcIsac_DecorrelateLPGain( - const double* data, - double* out) -{ - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - - for(colCntr = 0; colCntr < UB_LPC_GAIN_DIM; colCntr++) - { - *out = 0; - for(rowCntr = 0; rowCntr < UB_LPC_GAIN_DIM; rowCntr++) - { - *out += data[rowCntr] * WebRtcIsac_kLpcGainDecorrMat[rowCntr][colCntr]; - } - out++; - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_QuantizeLpcGain() - * - * Quantize the decorrelated log-domain gains. - * - * Input: - * -lpcGain : uncorrelated LPC gains. - * - * Output: - * -idx : quantization indices - * -lpcGain : quantized value of the inpt. - */ -double WebRtcIsac_QuantizeLpcGain( - double* data, - int* idx) -{ - WebRtc_Word16 coeffCntr; - for(coeffCntr = 0; coeffCntr < UB_LPC_GAIN_DIM; coeffCntr++) - { - *idx = (int)floor((*data - WebRtcIsac_kLeftRecPointLpcGain[coeffCntr]) / - WebRtcIsac_kQSizeLpcGain + 0.5); - - if(*idx < 0) - { - *idx = 0; - } - else if(*idx >= WebRtcIsac_kNumQCellLpcGain[coeffCntr]) - { - *idx = WebRtcIsac_kNumQCellLpcGain[coeffCntr] - 1; - } - *data = WebRtcIsac_kLeftRecPointLpcGain[coeffCntr] + *idx * - WebRtcIsac_kQSizeLpcGain; - - data++; - idx++; - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_DequantizeLpcGain() - * - * Get the quantized values given the quantization indices. - * - * Input: - * -idx : pointer to quantization indices. - * - * Output: - * -lpcGains : quantized values of the given parametes. - */ -WebRtc_Word16 WebRtcIsac_DequantizeLpcGain( - const int* idx, - double* out) -{ - WebRtc_Word16 coeffCntr; - for(coeffCntr = 0; coeffCntr < UB_LPC_GAIN_DIM; coeffCntr++) - { - *out = WebRtcIsac_kLeftRecPointLpcGain[coeffCntr] + *idx * - WebRtcIsac_kQSizeLpcGain; - out++; - idx++; - } - return 0; -} - -/****************************************************************************** - * WebRtcIsac_CorrelateLpcGain() - * - * This is the inverse of WebRtcIsac_DecorrelateLPGain(). - * - * Input: - * -data : decorrelated parameters. - * - * Output: - * -out : correlated parameters. - */ -WebRtc_Word16 WebRtcIsac_CorrelateLpcGain( - const double* data, - double* out) -{ - WebRtc_Word16 rowCntr; - WebRtc_Word16 colCntr; - - for(rowCntr = 0; rowCntr < UB_LPC_GAIN_DIM; rowCntr++) - { - *out = 0; - for(colCntr = 0; colCntr < UB_LPC_GAIN_DIM; colCntr++) - { - *out += WebRtcIsac_kLpcGainDecorrMat[rowCntr][colCntr] * data[colCntr]; - } - out++; - } - - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_AddMeanToLinearDomain() - * - * This is the inverse of WebRtcIsac_ToLogDomainRemoveMean(). - * - * Input: - * -lpcGain : LPC gain in log-domain & mean removed - * - * Output: - * -lpcGain : LPC gain in normal domain. - */ -WebRtc_Word16 WebRtcIsac_AddMeanToLinearDomain( - double* lpcGains) -{ - WebRtc_Word16 coeffCntr; - for(coeffCntr = 0; coeffCntr < UB_LPC_GAIN_DIM; coeffCntr++) - { - lpcGains[coeffCntr] = exp(lpcGains[coeffCntr] + WebRtcIsac_kMeanLpcGain); - } - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.h b/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.h deleted file mode 100644 index e7f1a76af..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/encode_lpc_swb.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * encode_lpc_swb.h - * - * This file contains declaration of functions used to - * encode LPC parameters (Shape & gain) of the upper band. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ - -#include "typedefs.h" -#include "settings.h" -#include "structs.h" - - -/****************************************************************************** - * WebRtcIsac_RemoveLarMean() - * - * Remove the means from LAR coefficients. - * - * Input: - * -lar : pointer to lar vectors. LAR vectors are - * concatenated. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -lar : pointer to mean-removed LAR:s. - * - * - */ -WebRtc_Word16 WebRtcIsac_RemoveLarMean( - double* lar, - WebRtc_Word16 bandwidth); - -/****************************************************************************** - * WebRtcIsac_DecorrelateIntraVec() - * - * Remove the correlation amonge the components of LAR vectors. If LAR vectors - * of one frame are put in a matrix where each column is a LAR vector of a - * sub-frame, then this is equivalent to multiplying the LAR matrix with - * a decorrelting mtrix from left. - * - * Input: - * -inLar : pointer to mean-removed LAR vecrtors. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : decorrelated LAR vectors. - */ -WebRtc_Word16 WebRtcIsac_DecorrelateIntraVec( - const double* inLAR, - double* out, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_DecorrelateInterVec() - * - * Remover the correlation among mean-removed LAR vectors. If LAR vectors - * of one frame are put in a matrix where each column is a LAR vector of a - * sub-frame, then this is equivalent to multiplying the LAR matrix with - * a decorrelting mtrix from right. - * - * Input: - * -data : pointer to matrix of LAR vectors. The matrix - * is stored column-wise. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : decorrelated LAR vectors. - */ -WebRtc_Word16 WebRtcIsac_DecorrelateInterVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_QuantizeUncorrLar() - * - * Quantize the uncorrelated parameters. - * - * Input: - * -data : uncorrelated LAR vectors. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -data : quantized version of the input. - * -idx : pointer to quantization indices. - */ -double WebRtcIsac_QuantizeUncorrLar( - double* data, - int* idx, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_CorrelateIntraVec() - * - * This is the inverse of WebRtcIsac_DecorrelateIntraVec(). - * - * Input: - * -data : uncorrelated parameters. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : correlated parametrs. - */ -WebRtc_Word16 WebRtcIsac_CorrelateIntraVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_CorrelateInterVec() - * - * This is the inverse of WebRtcIsac_DecorrelateInterVec(). - * - * Input: - * -data - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : correlated parametrs. - */ -WebRtc_Word16 WebRtcIsac_CorrelateInterVec( - const double* data, - double* out, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_AddLarMean() - * - * This is the inverse of WebRtcIsac_RemoveLarMean() - * - * Input: - * -data : pointer to mean-removed LAR:s. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -data : pointer to LARs. - */ -WebRtc_Word16 WebRtcIsac_AddLarMean( - double* data, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_DequantizeLpcParam() - * - * Get the quantized value of uncorrelated LARs given the quantization indices. - * - * Input: - * -idx : pointer to quantiztion indices. - * -bandwidth : indicates if the given LAR vectors belong - * to SWB-12kHz or SWB-16kHz. - * - * Output: - * -out : pointer to quantized values. - */ -WebRtc_Word16 WebRtcIsac_DequantizeLpcParam( - const int* idx, - double* out, - WebRtc_Word16 bandwidth); - - -/****************************************************************************** - * WebRtcIsac_ToLogDomainRemoveMean() - * - * Transform the LPC gain to log domain then remove the mean value. - * - * Input: - * -lpcGain : pointer to LPC Gain, expecting 6 LPC gains - * - * Output: - * -lpcGain : mean-removed in log domain. - */ -WebRtc_Word16 WebRtcIsac_ToLogDomainRemoveMean( - double* lpGains); - - -/****************************************************************************** - * WebRtcIsac_DecorrelateLPGain() - * - * Decorrelate LPC gains. There are 6 LPC Gains per frame. This is like - * multiplying gain vector with decorrelating matrix. - * - * Input: - * -data : LPC gain in log-domain with mean removed. - * - * Output: - * -out : decorrelated parameters. - */ -WebRtc_Word16 WebRtcIsac_DecorrelateLPGain( - const double* data, - double* out); - - -/****************************************************************************** - * WebRtcIsac_QuantizeLpcGain() - * - * Quantize the decorrelated log-domain gains. - * - * Input: - * -lpcGain : uncorrelated LPC gains. - * - * Output: - * -idx : quantization indices - * -lpcGain : quantized value of the inpt. - */ -double WebRtcIsac_QuantizeLpcGain( - double* lpGains, - int* idx); - - -/****************************************************************************** - * WebRtcIsac_DequantizeLpcGain() - * - * Get the quantized values given the quantization indices. - * - * Input: - * -idx : pointer to quantization indices. - * - * Output: - * -lpcGains : quantized values of the given parametes. - */ -WebRtc_Word16 WebRtcIsac_DequantizeLpcGain( - const int* idx, - double* lpGains); - - -/****************************************************************************** - * WebRtcIsac_CorrelateLpcGain() - * - * This is the inverse of WebRtcIsac_DecorrelateLPGain(). - * - * Input: - * -data : decorrelated parameters. - * - * Output: - * -out : correlated parameters. - */ -WebRtc_Word16 WebRtcIsac_CorrelateLpcGain( - const double* data, - double* out); - - -/****************************************************************************** - * WebRtcIsac_AddMeanToLinearDomain() - * - * This is the inverse of WebRtcIsac_ToLogDomainRemoveMean(). - * - * Input: - * -lpcGain : LPC gain in log-domain & mean removed - * - * Output: - * -lpcGain : LPC gain in normal domain. - */ -WebRtc_Word16 WebRtcIsac_AddMeanToLinearDomain( - double* lpcGains); - - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENCODE_LPC_SWB_H_ diff --git a/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.c b/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.c deleted file mode 100644 index 59219911c..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.c +++ /dev/null @@ -1,2762 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * entropy_coding.c - * - * This header file defines all of the functions used to arithmetically - * encode the iSAC bistream - * - */ - - -#include "entropy_coding.h" -#include "settings.h" -#include "arith_routines.h" -#include "signal_processing_library.h" -#include "spl_inl.h" -#include "spectrum_ar_model_tables.h" -#include "lpc_tables.h" -#include "pitch_gain_tables.h" -#include "pitch_lag_tables.h" -#include "encode_lpc_swb.h" -#include "lpc_shape_swb12_tables.h" -#include "lpc_shape_swb16_tables.h" - -#include "lpc_gain_swb_tables.h" - -#include -#include - -static const WebRtc_UWord16 WebRtcIsac_kLpcVecPerSegmentUb12 = 5; -static const WebRtc_UWord16 WebRtcIsac_kLpcVecPerSegmentUb16 = 4; - -/* coefficients for the stepwise rate estimation */ -const WebRtc_Word32 WebRtcIsac_kRPointsQ10[100] = { - 14495, 14295, 14112, 13944, 13788, 13643, 13459, 13276, 13195, 13239, - 13243, 13191, 13133, 13216, 13263, 13330, 13316, 13242, 13191, 13106, - 12942, 12669, 12291, 11840, 11361, 10795, 10192, 9561, 8934, 8335, - 7750, 7161, 6589, 6062, 5570, 5048, 4548, 4069, 3587, 3143, - 2717, 2305, 1915, 1557, 1235, 963, 720, 541, 423, 366, - 369, 435, 561, 750, 1001, 1304, 1626, 1989, 2381, 2793, - 3219, 3656, 4134, 4612, 5106, 5629, 6122, 6644, 7216, 7801, - 8386, 8987, 9630, 10255, 10897, 11490, 11950, 12397, 12752, 12999, - 13175, 13258, 13323, 13290, 13296, 13335, 13113, 13255, 13347, 13355, - 13298, 13247, 13313, 13155, 13267, 13313, 13374, 13446, 13525, 13609}; - - -/* cdf array for encoder bandwidth (12 vs 16 kHz) indicator */ -static const WebRtc_UWord16 WebRtcIsac_kOneBitEqualProbCdf[3] = { - 0, 32768, 65535}; - -/* pointer to cdf array for encoder bandwidth (12 vs 16 kHz) indicator */ -static const WebRtc_UWord16 *WebRtcIsac_kOneBitEqualProbCdf_ptr[1] = {WebRtcIsac_kOneBitEqualProbCdf}; - -/* initial cdf index for decoder of encoded bandwidth (12 vs 16 kHz) indicator */ -static const WebRtc_UWord16 WebRtcIsac_kOneBitEqualProbInitIndex[1] = {1}; - - -/* coefficients for the stepwise rate estimation */ - - -const WebRtc_Word32 acnQ10 = 426; -const WebRtc_Word32 bcnQ10 = -581224; -const WebRtc_Word32 ccnQ10 = 722631; -const WebRtc_Word32 lbcnQ10 = -402874; -#define DPMIN_Q10 -10240 // -10.00 in Q10 -#define DPMAX_Q10 10240 // 10.00 in Q10 -#define MINBITS_Q10 10240 /* 10.0 in Q10 */ -#define IS_SWB_12KHZ 1 - -#if defined(WEBRTC_LINUX) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(WEBRTC_MAC) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(X64) -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} -#elif defined(WEBRTC_TARGET_PC) - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - - WebRtc_Word32 intgr; - - _asm - { - fld flt - fistp intgr - } ; - - return intgr ; -} -#else // Do a slow but correct implementation of lrint - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} - -#endif - -__inline WebRtc_UWord32 stepwise(WebRtc_Word32 dinQ10) { - - WebRtc_Word32 ind, diQ10, dtQ10; - - diQ10 = dinQ10; - if (diQ10 < DPMIN_Q10) - diQ10 = DPMIN_Q10; - if (diQ10 >= DPMAX_Q10) - diQ10 = DPMAX_Q10 - 1; - - dtQ10 = diQ10 - DPMIN_Q10; /* Q10 + Q10 = Q10 */ - ind = (dtQ10 * 5) >> 10; /* 2^10 / 5 = 0.2 in Q10 */ - /* Q10 -> Q0 */ - - return WebRtcIsac_kRPointsQ10[ind]; - -} - - -__inline short log2_Q10_B( int x ) -{ - int zeros; - short frac; - - zeros = WebRtcSpl_NormU32( x ); - frac = ((unsigned int)(x << zeros) & 0x7FFFFFFF) >> 21; - return (short) (((31 - zeros) << 10) + frac); -} - - - -/* compute correlation from power spectrum */ -static void WebRtcIsac_FindCorrelation(WebRtc_Word32 *PSpecQ12, WebRtc_Word32 *CorrQ7) -{ - WebRtc_Word32 summ[FRAMESAMPLES/8]; - WebRtc_Word32 diff[FRAMESAMPLES/8]; - const WebRtc_Word16 *CS_ptrQ9; - WebRtc_Word32 sum; - int k, n; - - for (k = 0; k < FRAMESAMPLES/8; k++) { - summ[k] = (PSpecQ12[k] + PSpecQ12[FRAMESAMPLES_QUARTER-1 - k] + 16) >> 5; - diff[k] = (PSpecQ12[k] - PSpecQ12[FRAMESAMPLES_QUARTER-1 - k] + 16) >> 5; - } - - sum = 2; - for (n = 0; n < FRAMESAMPLES/8; n++) - sum += summ[n]; - CorrQ7[0] = sum; - - for (k = 0; k < AR_ORDER; k += 2) { - sum = 0; - CS_ptrQ9 = WebRtcIsac_kCos[k]; - for (n = 0; n < FRAMESAMPLES/8; n++) - sum += (CS_ptrQ9[n] * diff[n] + 256) >> 9; - CorrQ7[k+1] = sum; - } - - for (k=1; k> 9; - CorrQ7[k+1] = sum; - } -} - -/* compute inverse AR power spectrum */ -/* Changed to the function used in iSAC FIX for compatibility reasons */ -static void WebRtcIsac_FindInvArSpec(const WebRtc_Word16 *ARCoefQ12, - const WebRtc_Word32 gainQ10, - WebRtc_Word32 *CurveQ16) -{ - WebRtc_Word32 CorrQ11[AR_ORDER+1]; - WebRtc_Word32 sum, tmpGain; - WebRtc_Word32 diffQ16[FRAMESAMPLES/8]; - const WebRtc_Word16 *CS_ptrQ9; - int k, n; - WebRtc_Word16 round, shftVal = 0, sh; - - sum = 0; - for (n = 0; n < AR_ORDER+1; n++) - sum += WEBRTC_SPL_MUL(ARCoefQ12[n], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(sum, 6), 65) + 32768, 16); /* result in Q8 */ - CorrQ11[0] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, gainQ10) + 256, 9); - - /* To avoid overflow, we shift down gainQ10 if it is large. We will not lose any precision */ - if(gainQ10>400000){ - tmpGain = WEBRTC_SPL_RSHIFT_W32(gainQ10, 3); - round = 32; - shftVal = 6; - } else { - tmpGain = gainQ10; - round = 256; - shftVal = 9; - } - - for (k = 1; k < AR_ORDER+1; k++) { - sum = 16384; - for (n = k; n < AR_ORDER+1; n++) - sum += WEBRTC_SPL_MUL(ARCoefQ12[n-k], ARCoefQ12[n]); /* Q24 */ - sum = WEBRTC_SPL_RSHIFT_W32(sum, 15); - CorrQ11[k] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(sum, tmpGain) + round, shftVal); - } - sum = WEBRTC_SPL_LSHIFT_W32(CorrQ11[0], 7); - for (n = 0; n < FRAMESAMPLES/8; n++) - CurveQ16[n] = sum; - - for (k = 1; k < AR_ORDER; k += 2) { - //CS_ptrQ9 = WebRtcIsac_kCos[k]; - for (n = 0; n < FRAMESAMPLES/8; n++) - CurveQ16[n] += WEBRTC_SPL_RSHIFT_W32( - WEBRTC_SPL_MUL(WebRtcIsac_kCos[k][n], CorrQ11[k+1]) + 2, 2); - } - - CS_ptrQ9 = WebRtcIsac_kCos[0]; - - /* If CorrQ11[1] too large we avoid getting overflow in the calculation by shifting */ - sh=WebRtcSpl_NormW32(CorrQ11[1]); - if (CorrQ11[1]==0) /* Use next correlation */ - sh=WebRtcSpl_NormW32(CorrQ11[2]); - - if (sh<9) - shftVal = 9 - sh; - else - shftVal = 0; - - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[1], shftVal)) + 2, 2); - for (k = 2; k < AR_ORDER; k += 2) { - CS_ptrQ9 = WebRtcIsac_kCos[k]; - for (n = 0; n < FRAMESAMPLES/8; n++) - diffQ16[n] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(CS_ptrQ9[n], WEBRTC_SPL_RSHIFT_W32(CorrQ11[k+1], shftVal)) + 2, 2); - } - - for (k=0; k>25); // * 128/4294967295 - - /* new random unsigned int */ - seed = (seed * 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 */ - dither2_Q7 = (WebRtc_Word16)(((int)seed + 16777216)>>25); - - shft = (seed >> 25) & 15; - if (shft < 5) - { - bufQ7[k] = dither1_Q7; - bufQ7[k+1] = dither2_Q7; - bufQ7[k+2] = 0; - } - else if (shft < 10) - { - bufQ7[k] = dither1_Q7; - bufQ7[k+1] = 0; - bufQ7[k+2] = dither2_Q7; - } - else - { - bufQ7[k] = 0; - bufQ7[k+1] = dither1_Q7; - bufQ7[k+2] = dither2_Q7; - } - } - } - else - { - dither_gain_Q14 = (WebRtc_Word16)(22528 - 10 * AvgPitchGain_Q12); - - /* dither on half of the coefficients */ - for (k = 0; k < length-1; k += 2) - { - /* new random unsigned int */ - seed = (seed * 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 */ - dither1_Q7 = (WebRtc_Word16)(((int)seed + 16777216)>>25); - - /* dither sample is placed in either even or odd index */ - shft = (seed >> 25) & 1; /* either 0 or 1 */ - - bufQ7[k + shft] = (((dither_gain_Q14 * dither1_Q7) + 8192)>>14); - bufQ7[k + 1 - shft] = 0; - } - } -} - - - -/****************************************************************************** - * GenerateDitherQ7LbUB() - * - * generate array of dither samples in Q7 There are less zeros in dither - * vector compared to GenerateDitherQ7Lb. - * - * A uniform random number generator with the range of [-64 64] is employed - * but the generated dithers are scaled by 0.35, a heuristic scaling. - * - * Input: - * -seed : the initial seed for the random number generator. - * -length : the number of dither values to be generated. - * - * Output: - * -bufQ7 : pointer to a buffer where dithers are written to. - */ -static void GenerateDitherQ7LbUB( - WebRtc_Word16 *bufQ7, - WebRtc_UWord32 seed, - int length) -{ - int k; - for (k = 0; k < length; k++) { - /* new random unsigned int */ - seed = (seed * 196314165) + 907633515; - - /* fixed-point dither sample between -64 and 64 (Q7) */ - // * 128/4294967295 - bufQ7[k] = (WebRtc_Word16)(((int)seed + 16777216)>>25); - - // scale by 0.35 - bufQ7[k] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(bufQ7[k], - 2048, 13); - } -} - - - -/* - * function to decode the complex spectrum from the bit stream - * returns the total number of bytes in the stream - */ -int WebRtcIsac_DecodeSpecLb(Bitstr *streamdata, - double *fr, - double *fi, - WebRtc_Word16 AvgPitchGain_Q12) -{ - WebRtc_Word16 DitherQ7[FRAMESAMPLES]; - WebRtc_Word16 data[FRAMESAMPLES]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 gainQ10; - WebRtc_Word32 gain2_Q10, res; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - int k, len, i; - - /* create dither signal */ - GenerateDitherQ7Lb(DitherQ7, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12); - - /* decode model parameters */ - if (WebRtcIsac_DecodeRc(streamdata, RCQ15) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - if (WebRtcIsac_DecodeGain2(streamdata, &gain2_Q10) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic decoding of spectrum */ - if ((len = WebRtcIsac_DecLogisticMulti2(data, streamdata, invARSpecQ8, DitherQ7, - FRAMESAMPLES, !IS_SWB_12KHZ)) <1) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* subtract dither and scale down spectral samples with low SNR */ - if (AvgPitchGain_Q12 <= 614) - { - for (k = 0; k < FRAMESAMPLES; k += 4) - { - gainQ10 = WebRtcSpl_DivW32W16ResW16(30 << 10, - (WebRtc_Word16)((invARSpec2_Q16[k>>2] + (32768 + (33 << 16))) >> 16)); - *fr++ = (double)((data[ k ] * gainQ10 + 512) >> 10) / 128.0; - *fi++ = (double)((data[k+1] * gainQ10 + 512) >> 10) / 128.0; - *fr++ = (double)((data[k+2] * gainQ10 + 512) >> 10) / 128.0; - *fi++ = (double)((data[k+3] * gainQ10 + 512) >> 10) / 128.0; - } - } - else - { - for (k = 0; k < FRAMESAMPLES; k += 4) - { - gainQ10 = WebRtcSpl_DivW32W16ResW16(36 << 10, - (WebRtc_Word16)((invARSpec2_Q16[k>>2] + (32768 + (40 << 16))) >> 16)); - *fr++ = (double)((data[ k ] * gainQ10 + 512) >> 10) / 128.0; - *fi++ = (double)((data[k+1] * gainQ10 + 512) >> 10) / 128.0; - *fr++ = (double)((data[k+2] * gainQ10 + 512) >> 10) / 128.0; - *fi++ = (double)((data[k+3] * gainQ10 + 512) >> 10) / 128.0; - } - } - - return len; -} - -/****************************************************************************** - * WebRtcIsac_DecodeSpecUB16() - * Decode real and imaginary part of the DFT coefficients, given a bit-stream. - * This function is called when the codec is in 0-16 kHz bandwidth. - * The decoded DFT coefficient can be transformed to time domain by - * WebRtcIsac_Time2Spec(). - * - * Input: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are written to. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are written to. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_DecodeSpecUB16( - Bitstr* streamdata, - double* fr, - double* fi) -{ - WebRtc_Word16 DitherQ7[FRAMESAMPLES]; - WebRtc_Word16 data[FRAMESAMPLES]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word32 gain2_Q10, res; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - int k, len, i, j; - - /* create dither signal */ - GenerateDitherQ7LbUB(DitherQ7, streamdata->W_upper, FRAMESAMPLES); - - /* decode model parameters */ - if (WebRtcIsac_DecodeRc(streamdata, RCQ15) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - if (WebRtcIsac_DecodeGain2(streamdata, &gain2_Q10) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic decoding of spectrum */ - if ((len = WebRtcIsac_DecLogisticMulti2(data, streamdata, invARSpecQ8, - DitherQ7, FRAMESAMPLES, !IS_SWB_12KHZ)) <1) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* re-arrange DFT coefficients and scale down */ - for (j = 0, k = 0; k < FRAMESAMPLES; k += 4, j++) - { - fr[j] = (double)data[ k ] / 128.0; - fi[j] = (double)data[k+1] / 128.0; - fr[(FRAMESAMPLES_HALF) - 1 - j] = (double)data[k+2] / 128.0; - fi[(FRAMESAMPLES_HALF) - 1 - j] = (double)data[k+3] / 128.0; - - } - return len; -} - - - - -/****************************************************************************** - * WebRtcIsac_DecodeSpecUB12() - * Decode real and imaginary part of the DFT coefficients, given a bit-stream. - * This function is called when the codec is in 0-12 kHz bandwidth. - * The decoded DFT coefficient can be transformed to time domain by - * WebRtcIsac_Time2Spec(). - * - * Input: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are written to. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are written to. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_DecodeSpecUB12( - Bitstr *streamdata, - double *fr, - double *fi) -{ - WebRtc_Word16 DitherQ7[FRAMESAMPLES]; - WebRtc_Word16 data[FRAMESAMPLES]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word32 gain2_Q10; - WebRtc_Word32 res; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - int k, len, i; - - /* create dither signal */ - GenerateDitherQ7LbUB(DitherQ7, streamdata->W_upper, FRAMESAMPLES); - - /* decode model parameters */ - if (WebRtcIsac_DecodeRc(streamdata, RCQ15) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - if (WebRtcIsac_DecodeGain2(streamdata, &gain2_Q10) < 0) - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic decoding of spectrum */ - if ((len = WebRtcIsac_DecLogisticMulti2(data, streamdata, - invARSpecQ8, DitherQ7, (FRAMESAMPLES_HALF), IS_SWB_12KHZ)) < 1) - { - return -ISAC_RANGE_ERROR_DECODE_SPECTRUM; - } - - for (k = 0, i = 0; k < FRAMESAMPLES_HALF; k += 4) - { - fr[i] = (double)data[ k ] / 128.0; - fi[i] = (double)data[k+1] / 128.0; - i++; - fr[i] = (double)data[k+2] / 128.0; - fi[i] = (double)data[k+3] / 128.0; - i++; - } - - // The second half of real and imaginary coefficients is zero. This is - // due to using the old FFT module which requires two signals as input - // while in 0-12 kHz mode we only have 8-12 kHz band, and the second signal - // is set to zero - memset(&fr[FRAMESAMPLES_QUARTER], 0, FRAMESAMPLES_QUARTER * sizeof(double)); - memset(&fi[FRAMESAMPLES_QUARTER], 0, FRAMESAMPLES_QUARTER * sizeof(double)); - - return len; -} - - - - - -int WebRtcIsac_EncodeSpecLb(const WebRtc_Word16 *fr, - const WebRtc_Word16 *fi, - Bitstr *streamdata, - WebRtc_Word16 AvgPitchGain_Q12) -{ - WebRtc_Word16 ditherQ7[FRAMESAMPLES]; - WebRtc_Word16 dataQ7[FRAMESAMPLES]; - WebRtc_Word32 PSpec[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 CorrQ7[AR_ORDER+1]; - WebRtc_Word32 CorrQ7_norm[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word32 gain2_Q10; - WebRtc_Word16 val; - WebRtc_Word32 nrg, res; - WebRtc_UWord32 sum; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - WebRtc_Word16 err; - int lft_shft; - int k, n, j, i; - - - /* create dither_float signal */ - GenerateDitherQ7Lb(ditherQ7, streamdata->W_upper, FRAMESAMPLES, AvgPitchGain_Q12); - - /* add dither and quantize, and compute power spectrum */ - for (k = 0; k < FRAMESAMPLES; k += 4) - { - val = ((*fr++ + ditherQ7[k] + 64) & 0xFF80) - ditherQ7[k]; - dataQ7[k] = val; - sum = val * val; - - val = ((*fi++ + ditherQ7[k+1] + 64) & 0xFF80) - ditherQ7[k+1]; - dataQ7[k+1] = val; - sum += val * val; - - val = ((*fr++ + ditherQ7[k+2] + 64) & 0xFF80) - ditherQ7[k+2]; - dataQ7[k+2] = val; - sum += val * val; - - val = ((*fi++ + ditherQ7[k+3] + 64) & 0xFF80) - ditherQ7[k+3]; - dataQ7[k+3] = val; - sum += val * val; - - PSpec[k>>2] = sum >> 2; - } - - /* compute correlation from power spectrum */ - WebRtcIsac_FindCorrelation(PSpec, CorrQ7); - - - /* find AR coefficients */ - /* number of bit shifts to 14-bit normalize CorrQ7[0] (leaving room for sign) */ - lft_shft = WebRtcSpl_NormW32(CorrQ7[0]) - 18; - - if (lft_shft > 0) { - for (k=0; k> (-lft_shft); - } - } - - /* find RC coefficients */ - WebRtcSpl_AutoCorrToReflCoef(CorrQ7_norm, AR_ORDER, RCQ15); - - /* quantize & code RC Coefficient */ - WebRtcIsac_EncodeRc(RCQ15, streamdata); - - /* RC -> AR coefficients */ - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - /* compute ARCoef' * Corr * ARCoef in Q19 */ - nrg = 0; - for (j = 0; j <= AR_ORDER; j++) { - for (n = 0; n <= j; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[j-n] * ARCoefQ12[n] + 256) >> 9) + 4 ) >> 3; - } - for (n = j+1; n <= AR_ORDER; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[n-j] * ARCoefQ12[n] + 256) >> 9) + 4 ) >> 3; - } - } - if (lft_shft > 0) { - nrg >>= lft_shft; - } else { - nrg <<= -lft_shft; - } - - gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES_QUARTER, nrg); /* also shifts 31 bits to the left! */ - - /* quantize & code gain2_Q10 */ - if (WebRtcIsac_EncodeGain2(&gain2_Q10, streamdata)) { - return -1; - } - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic coding of spectrum */ - err = WebRtcIsac_EncLogisticMulti2(streamdata, dataQ7, invARSpecQ8, - FRAMESAMPLES, !IS_SWB_12KHZ); - if (err < 0) - { - return (err); - } - - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_EncodeSpecUB16() - * Quantize and encode real and imaginary part of the DFT coefficients. - * This function is called when the codec is in 0-16 kHz bandwidth. - * The real and imaginary part are computed by calling WebRtcIsac_Time2Spec(). - * - * - * Input: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are stored. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are stored. - * - * Output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_EncodeSpecUB16( - const WebRtc_Word16* fr, - const WebRtc_Word16* fi, - Bitstr* streamdata) -{ - WebRtc_Word16 ditherQ7[FRAMESAMPLES]; - WebRtc_Word16 dataQ7[FRAMESAMPLES]; - WebRtc_Word32 PSpec[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 CorrQ7[AR_ORDER+1]; - WebRtc_Word32 CorrQ7_norm[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word32 gain2_Q10; - WebRtc_Word16 val; - WebRtc_Word32 nrg, res; - WebRtc_UWord32 sum; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - WebRtc_Word16 err; - int lft_shft; - int k, n, j, i; - - /* create dither_float signal */ - GenerateDitherQ7LbUB(ditherQ7, streamdata->W_upper, FRAMESAMPLES); - - /* add dither and quantize, and compute power spectrum */ - for (j = 0, k = 0; k < FRAMESAMPLES; k += 4, j++) - { - val = ((fr[j] + ditherQ7[k] + 64) & 0xFF80) - ditherQ7[k]; - dataQ7[k] = val; - sum = val * val; - - val = ((fi[j] + ditherQ7[k+1] + 64) & 0xFF80) - ditherQ7[k+1]; - dataQ7[k+1] = val; - sum += val * val; - - val = ((fr[(FRAMESAMPLES_HALF) - 1 - j] + ditherQ7[k+2] + 64) & - 0xFF80) - ditherQ7[k+2]; - dataQ7[k+2] = val; - sum += val * val; - - val = ((fi[(FRAMESAMPLES_HALF) - 1 - j] + ditherQ7[k+3] + 64) & - 0xFF80) - ditherQ7[k+3]; - dataQ7[k+3] = val; - sum += val * val; - - PSpec[k>>2] = sum >> 2; - } - - /* compute correlation from power spectrum */ - WebRtcIsac_FindCorrelation(PSpec, CorrQ7); - - - /* find AR coefficients - number of bit shifts to 14-bit normalize CorrQ7[0] - (leaving room for sign) */ - lft_shft = WebRtcSpl_NormW32(CorrQ7[0]) - 18; - - if (lft_shft > 0) { - for (k=0; k> (-lft_shft); - } - } - - /* find RC coefficients */ - WebRtcSpl_AutoCorrToReflCoef(CorrQ7_norm, AR_ORDER, RCQ15); - - /* quantize & code RC Coef */ - WebRtcIsac_EncodeRc(RCQ15, streamdata); - - /* RC -> AR coefficients */ - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - /* compute ARCoef' * Corr * ARCoef in Q19 */ - nrg = 0; - for (j = 0; j <= AR_ORDER; j++) { - for (n = 0; n <= j; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[j-n] * ARCoefQ12[n] + - 256) >> 9) + 4 ) >> 3; - } - for (n = j+1; n <= AR_ORDER; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[n-j] * ARCoefQ12[n] + - 256) >> 9) + 4 ) >> 3; - } - } - if (lft_shft > 0) { - nrg >>= lft_shft; - } else { - nrg <<= -lft_shft; - } - - gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES_QUARTER, nrg); /* also shifts 31 bits to the left! */ - - /* quantize & code gain2_Q10 */ - if (WebRtcIsac_EncodeGain2(&gain2_Q10, streamdata)) { - return -1; - } - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic coding of spectrum */ - err = WebRtcIsac_EncLogisticMulti2(streamdata, dataQ7, invARSpecQ8, - FRAMESAMPLES, !IS_SWB_12KHZ); - if (err < 0) - { - return (err); - } - - return 0; -} - - - - -int WebRtcIsac_EncodeSpecUB12(const WebRtc_Word16 *fr, - const WebRtc_Word16 *fi, - Bitstr *streamdata) -{ - WebRtc_Word16 ditherQ7[FRAMESAMPLES]; - WebRtc_Word16 dataQ7[FRAMESAMPLES]; - WebRtc_Word32 PSpec[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 invARSpec2_Q16[FRAMESAMPLES_QUARTER]; - WebRtc_UWord16 invARSpecQ8[FRAMESAMPLES_QUARTER]; - WebRtc_Word32 CorrQ7[AR_ORDER+1]; - WebRtc_Word32 CorrQ7_norm[AR_ORDER+1]; - WebRtc_Word16 RCQ15[AR_ORDER]; - WebRtc_Word16 ARCoefQ12[AR_ORDER+1]; - WebRtc_Word32 gain2_Q10; - WebRtc_Word16 val; - WebRtc_Word32 nrg, res; - WebRtc_UWord32 sum; - WebRtc_Word32 in_sqrt; - WebRtc_Word32 newRes; - WebRtc_Word16 err; - int lft_shft; - int k, n, j, i; - - /* create dither_float signal */ - GenerateDitherQ7LbUB(ditherQ7, streamdata->W_upper, FRAMESAMPLES); - - /* add dither and quantize, and compute power spectrum */ - for (k = 0, j = 0; k < (FRAMESAMPLES_HALF); k += 4) - { - val = ((*fr++ + ditherQ7[k] + 64) & 0xFF80) - ditherQ7[k]; - dataQ7[k] = val; - sum = (val) * (val); - - val = ((*fi++ + ditherQ7[k+1] + 64) & 0xFF80) - ditherQ7[k+1]; - dataQ7[k+1] = val; - sum += (val) * (val); - - if(j < FRAMESAMPLES_QUARTER) - { - PSpec[j] = sum >> 1; - j++; - } - - val = ((*fr++ + ditherQ7[k+2] + 64) & 0xFF80) - ditherQ7[k+2]; - dataQ7[k+2] = val; - sum = (val) * (val); - - val = ((*fi++ + ditherQ7[k+3] + 64) & 0xFF80) - ditherQ7[k+3]; - dataQ7[k+3] = val; - sum += (val) * (val); - - if(j < FRAMESAMPLES_QUARTER) - { - PSpec[j] = sum >> 1; - j++; - } - } - /* compute correlation from power spectrum */ - WebRtcIsac_FindCorrelation(PSpec, CorrQ7); - - - /* find AR coefficients */ - /* number of bit shifts to 14-bit normalize CorrQ7[0] (leaving room for sign) */ - lft_shft = WebRtcSpl_NormW32(CorrQ7[0]) - 18; - - if (lft_shft > 0) { - for (k=0; k> (-lft_shft); - } - } - - /* find RC coefficients */ - WebRtcSpl_AutoCorrToReflCoef(CorrQ7_norm, AR_ORDER, RCQ15); - - /* quantize & code RC Coef */ - WebRtcIsac_EncodeRc(RCQ15, streamdata); - - /* RC -> AR coefficients */ - WebRtcSpl_ReflCoefToLpc(RCQ15, AR_ORDER, ARCoefQ12); - - /* compute ARCoef' * Corr * ARCoef in Q19 */ - nrg = 0; - for (j = 0; j <= AR_ORDER; j++) { - for (n = 0; n <= j; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[j-n] * ARCoefQ12[n] + 256) >> 9) + 4 ) >> 3; - } - for (n = j+1; n <= AR_ORDER; n++) { - nrg += ( ARCoefQ12[j] * ((CorrQ7_norm[n-j] * ARCoefQ12[n] + 256) >> 9) + 4 ) >> 3; - } - } - if (lft_shft > 0) { - nrg >>= lft_shft; - } else { - nrg <<= -lft_shft; - } - - gain2_Q10 = WebRtcSpl_DivResultInQ31(FRAMESAMPLES_QUARTER, nrg); /* also shifts 31 bits to the left! */ - - /* quantize & code gain2_Q10 */ - if (WebRtcIsac_EncodeGain2(&gain2_Q10, streamdata)) { - return -1; - } - - /* compute inverse AR power spectrum */ - WebRtcIsac_FindInvArSpec(ARCoefQ12, gain2_Q10, invARSpec2_Q16); - - /* convert to magnitude spectrum, by doing square-roots (modified from SPLIB) */ - res = 1 << (WebRtcSpl_GetSizeInBits(invARSpec2_Q16[0]) >> 1); - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) - { - in_sqrt = invARSpec2_Q16[k]; - i = 10; - - /* Negative values make no sense for a real sqrt-function. */ - if (in_sqrt<0) - in_sqrt=-in_sqrt; - - newRes = (in_sqrt / res + res) >> 1; - do - { - res = newRes; - newRes = (in_sqrt / res + res) >> 1; - } while (newRes != res && i-- > 0); - - invARSpecQ8[k] = (WebRtc_Word16)newRes; - } - - /* arithmetic coding of spectrum */ - err = WebRtcIsac_EncLogisticMulti2(streamdata, dataQ7, invARSpecQ8, - (FRAMESAMPLES_HALF), IS_SWB_12KHZ); - if (err < 0) - { - return (err); - } - - return 0; -} - - - -/* step-up */ -void WebRtcIsac_Rc2Poly(double *RC, int N, double *a) -{ - int m, k; - double tmp[MAX_AR_MODEL_ORDER]; - - a[0] = 1.0; - tmp[0] = 1.0; - for (m=1; m<=N; m++) { - /* copy */ - for (k=1; k0; m--) { - tmp_inv = 1.0 / (1.0 - RC[m]*RC[m]); - for (k=1; k<=m; k++) - tmp[k] = (a[k] - RC[m] * a[m-k+1]) * tmp_inv; - - for (k=1; k WebRtcIsac_kQKltMaxIndGain[k]) - index_g[k] = WebRtcIsac_kQKltMaxIndGain[k]; - index_ovr_g[k] = WebRtcIsac_kQKltOffsetGain[bmodel][k]+index_g[k]; - pos = WebRtcIsac_kQKltOfLevelsGain[bmodel] + index_ovr_g[k]; - - /* determine number of bits */ - sum = WebRtcIsac_kQKltCodeLenGain[pos]; - Bits += sum; - } - - for (k=0; k WebRtcIsac_kQKltMaxIndShape[k]) - index_s[k] = WebRtcIsac_kQKltMaxIndShape[k]; - index_ovr_s[k] = WebRtcIsac_kQKltOffsetShape[bmodel][k]+index_s[k]; - pos = WebRtcIsac_kQKltOfLevelsShape[bmodel] + index_ovr_s[k]; - sum = WebRtcIsac_kQKltCodeLenShape[pos]; - Bits += sum; - } - - - /* Only one model remains in this version of the code, model = 0 */ - *model=bmodel; - *size=Bits; - - /* entropy coding of model number */ - WebRtcIsac_EncHistMulti(streamdata, model, WebRtcIsac_kQKltModelCdfPtr, 1); - - /* entropy coding of quantization indices - shape only */ - WebRtcIsac_EncHistMulti(streamdata, index_s, WebRtcIsac_kQKltCdfPtrShape[bmodel], KLT_ORDER_SHAPE); - - /* Save data for creation of multiple bit streams */ - encData->LPCmodel[encData->startIdx] = 0; - for (k=0; kLPCindex_s[KLT_ORDER_SHAPE*encData->startIdx + k] = index_s[k]; - } - - /* find quantization levels for shape coefficients */ - for (k=0; kLPCcoeffs_lo[(ORDERLO+1)*SUBFRAMES*encData->startIdx + k] = LPCCoef_lo[k]; - } - for (k=0; k<(ORDERHI+1)*SUBFRAMES; k++) { - encData->LPCcoeffs_hi[(ORDERHI+1)*SUBFRAMES*encData->startIdx + k] = LPCCoef_hi[k]; - } -} - - -WebRtc_Word16 -WebRtcIsac_EncodeLpcUB( - double* lpcVecs, - Bitstr* streamdata, - double* interpolLPCCoeff, - WebRtc_Word16 bandwidth, - ISACUBSaveEncDataStruct* encData) -{ - - double U[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - int idx[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - int interpolCntr; - - WebRtcIsac_Poly2LarUB(lpcVecs, bandwidth); - WebRtcIsac_RemoveLarMean(lpcVecs, bandwidth); - WebRtcIsac_DecorrelateIntraVec(lpcVecs, U, bandwidth); - WebRtcIsac_DecorrelateInterVec(U, lpcVecs, bandwidth); - WebRtcIsac_QuantizeUncorrLar(lpcVecs, idx, bandwidth); - - WebRtcIsac_CorrelateInterVec(lpcVecs, U, bandwidth); - WebRtcIsac_CorrelateIntraVec(U, lpcVecs, bandwidth); - WebRtcIsac_AddLarMean(lpcVecs, bandwidth); - - switch(bandwidth) - { - case isac12kHz: - { - // Stor the indices to be used for multiple encoding. - memcpy(encData->indexLPCShape, idx, UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME * - sizeof(int)); - WebRtcIsac_EncHistMulti(streamdata, idx, WebRtcIsac_kLpcShapeCdfMatUb12, - UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME); - for(interpolCntr = 0; interpolCntr < UB_INTERPOL_SEGMENTS; interpolCntr++) - { - WebRtcIsac_Lar2PolyInterpolUB(lpcVecs, - interpolLPCCoeff, WebRtcIsac_kLpcVecPerSegmentUb12 + 1); - lpcVecs += UB_LPC_ORDER; - interpolLPCCoeff += (WebRtcIsac_kLpcVecPerSegmentUb12 * (UB_LPC_ORDER + 1)); - } - break; - } - case isac16kHz: - { - // Stor the indices to be used for multiple encoding. - memcpy(encData->indexLPCShape, idx, UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME * - sizeof(int)); - WebRtcIsac_EncHistMulti(streamdata, idx, WebRtcIsac_kLpcShapeCdfMatUb16, - UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME); - for(interpolCntr = 0; interpolCntr < UB16_INTERPOL_SEGMENTS; interpolCntr++) - { - WebRtcIsac_Lar2PolyInterpolUB(lpcVecs, - interpolLPCCoeff, WebRtcIsac_kLpcVecPerSegmentUb16 + 1); - lpcVecs += UB_LPC_ORDER; - interpolLPCCoeff += (WebRtcIsac_kLpcVecPerSegmentUb16 * (UB_LPC_ORDER + 1)); - } - break; - } - default: - return -1; - } - return 0; -} - -void WebRtcIsac_EncodeLpcGainLb(double *LPCCoef_lo, double *LPCCoef_hi, int model, Bitstr *streamdata, ISAC_SaveEncData_t* encData) { - - int j, k, n, pos, pos2, posg, offsg, offs2; - int index_g[KLT_ORDER_GAIN]; - int index_ovr_g[KLT_ORDER_GAIN]; - double Bits; - double tmpcoeffs_g[KLT_ORDER_GAIN]; - double tmpcoeffs2_g[KLT_ORDER_GAIN]; - double sum; - - /* log gains, mean removal and scaling */ - posg = 0; - for (k=0; k WebRtcIsac_kQKltMaxIndGain[k]) { - index_g[k] = WebRtcIsac_kQKltMaxIndGain[k]; - } - index_ovr_g[k] = WebRtcIsac_kQKltOffsetGain[model][k]+index_g[k]; - - /* find quantization levels for coefficients */ - tmpcoeffs_g[WebRtcIsac_kQKltSelIndGain[k]] = WebRtcIsac_kQKltLevelsGain[WebRtcIsac_kQKltOfLevelsGain[model]+index_ovr_g[k]]; - - /* Save data for creation of multiple bit streams */ - encData->LPCindex_g[KLT_ORDER_GAIN*encData->startIdx + k] = index_g[k]; - } - - - /* entropy coding of quantization indices - gain */ - WebRtcIsac_EncHistMulti(streamdata, index_g, WebRtcIsac_kQKltCdfPtrGain[model], KLT_ORDER_GAIN); - - /* find quantization levels for coefficients */ - - /* left transform */ - offsg = 0; - posg = 0; - for (j=0; j WebRtcIsac_kQArBoundaryLevels[index[k]]) - { - while (RCQ15[k] > WebRtcIsac_kQArBoundaryLevels[index[k] + 1]) - index[k]++; - } - else - { - while (RCQ15[k] < WebRtcIsac_kQArBoundaryLevels[--index[k]]) ; - } - - RCQ15[k] = *(WebRtcIsac_kQArRcLevelsPtr[k] + index[k]); - } - - - /* entropy coding of quantization indices */ - WebRtcIsac_EncHistMulti(streamdata, index, WebRtcIsac_kQArRcCdfPtr, AR_ORDER); -} - - -/* decode & dequantize squared Gain */ -int WebRtcIsac_DecodeGain2(Bitstr *streamdata, WebRtc_Word32 *gainQ10) -{ - int index, err; - - /* entropy decoding of quantization index */ - err = WebRtcIsac_DecHistOneStepMulti(&index, streamdata, WebRtcIsac_kQGainCdf_ptr, - WebRtcIsac_kQGainInitIndex, 1); - if (err<0) // error check - return err; - - /* find quantization level */ - *gainQ10 = WebRtcIsac_kQGain2Levels[index]; - - return 0; -} - - - -/* quantize & code squared Gain */ -int WebRtcIsac_EncodeGain2(WebRtc_Word32 *gainQ10, Bitstr *streamdata) -{ - int index; - - - /* find quantization index */ - index = WebRtcIsac_kQGainInitIndex[0]; - if (*gainQ10 > WebRtcIsac_kQGain2BoundaryLevels[index]) - { - while (*gainQ10 > WebRtcIsac_kQGain2BoundaryLevels[index + 1]) - index++; - } - else - { - while (*gainQ10 < WebRtcIsac_kQGain2BoundaryLevels[--index]) ; - } - - /* dequantize */ - *gainQ10 = WebRtcIsac_kQGain2Levels[index]; - - - /* entropy coding of quantization index */ - WebRtcIsac_EncHistMulti(streamdata, &index, WebRtcIsac_kQGainCdf_ptr, 1); - - return 0; -} - - -/* code and decode Pitch Gains and Lags functions */ - -/* decode & dequantize Pitch Gains */ -int WebRtcIsac_DecodePitchGain(Bitstr *streamdata, WebRtc_Word16 *PitchGains_Q12) -{ - int index_comb, err; - const WebRtc_UWord16 *WebRtcIsac_kQPitchGainCdf_ptr[1]; - - /* entropy decoding of quantization indices */ - *WebRtcIsac_kQPitchGainCdf_ptr = WebRtcIsac_kQPitchGainCdf; - err = WebRtcIsac_DecHistBisectMulti(&index_comb, streamdata, WebRtcIsac_kQPitchGainCdf_ptr, WebRtcIsac_kQCdfTableSizeGain, 1); - /* error check, Q_mean_Gain.. tables are of size 144 */ - if ((err<0) || (index_comb<0) || (index_comb>144)) - return -ISAC_RANGE_ERROR_DECODE_PITCH_GAIN; - - /* unquantize back to pitch gains by table look-up */ - PitchGains_Q12[0] = WebRtcIsac_kQMeanGain1Q12[index_comb]; - PitchGains_Q12[1] = WebRtcIsac_kQMeanGain2Q12[index_comb]; - PitchGains_Q12[2] = WebRtcIsac_kQMeanGain3Q12[index_comb]; - PitchGains_Q12[3] = WebRtcIsac_kQMeanGain4Q12[index_comb]; - - return 0; -} - - -/* quantize & code Pitch Gains */ -void WebRtcIsac_EncodePitchGain(WebRtc_Word16 *PitchGains_Q12, Bitstr *streamdata, ISAC_SaveEncData_t* encData) -{ - int k,j; - double C; - double S[PITCH_SUBFRAMES]; - int index[3]; - int index_comb; - const WebRtc_UWord16 *WebRtcIsac_kQPitchGainCdf_ptr[1]; - double PitchGains[PITCH_SUBFRAMES] = {0,0,0,0}; - - /* take the asin */ - for (k=0; k WebRtcIsac_kIndexUpperLimitGain[k]) index[k] = WebRtcIsac_kIndexUpperLimitGain[k]; - index[k] -= WebRtcIsac_kIndexLowerLimitGain[k]; - } - - /* calculate unique overall index */ - index_comb = WebRtcIsac_kIndexMultsGain[0] * index[0] + WebRtcIsac_kIndexMultsGain[1] * index[1] + index[2]; - - /* unquantize back to pitch gains by table look-up */ - PitchGains_Q12[0] = WebRtcIsac_kQMeanGain1Q12[index_comb]; - PitchGains_Q12[1] = WebRtcIsac_kQMeanGain2Q12[index_comb]; - PitchGains_Q12[2] = WebRtcIsac_kQMeanGain3Q12[index_comb]; - PitchGains_Q12[3] = WebRtcIsac_kQMeanGain4Q12[index_comb]; - - /* entropy coding of quantization pitch gains */ - *WebRtcIsac_kQPitchGainCdf_ptr = WebRtcIsac_kQPitchGainCdf; - WebRtcIsac_EncHistMulti(streamdata, &index_comb, WebRtcIsac_kQPitchGainCdf_ptr, 1); - encData->pitchGain_index[encData->startIdx] = index_comb; - -} - - - -/* Pitch LAG */ - - -/* decode & dequantize Pitch Lags */ -int WebRtcIsac_DecodePitchLag(Bitstr *streamdata, WebRtc_Word16 *PitchGain_Q12, double *PitchLags) -{ - int k, err; - double StepSize; - double C; - int index[PITCH_SUBFRAMES]; - double mean_gain; - const double *mean_val2, *mean_val3, *mean_val4; - const WebRtc_Word16 *lower_limit; - const WebRtc_UWord16 *init_index; - const WebRtc_UWord16 *cdf_size; - const WebRtc_UWord16 **cdf; - - //(Y) - double PitchGain[4]={0,0,0,0}; - // - - /* compute mean pitch gain */ - mean_gain = 0.0; - for (k = 0; k < 4; k++) - { - //(Y) - PitchGain[k] = ((float)PitchGain_Q12[k])/4096; - //(Y) - mean_gain += PitchGain[k]; - } - mean_gain /= 4.0; - - /* voicing classificiation */ - if (mean_gain < 0.2) { - StepSize = WebRtcIsac_kQPitchLagStepsizeLo; - cdf = WebRtcIsac_kQPitchLagCdfPtrLo; - cdf_size = WebRtcIsac_kQPitchLagCdfSizeLo; - mean_val2 = WebRtcIsac_kQMeanLag2Lo; - mean_val3 = WebRtcIsac_kQMeanLag3Lo; - mean_val4 = WebRtcIsac_kQMeanLag4Lo; - lower_limit = WebRtcIsac_kQIndexLowerLimitLagLo; - init_index = WebRtcIsac_kQInitIndexLagLo; - } else if (mean_gain < 0.4) { - StepSize = WebRtcIsac_kQPitchLagStepsizeMid; - cdf = WebRtcIsac_kQPitchLagCdfPtrMid; - cdf_size = WebRtcIsac_kQPitchLagCdfSizeMid; - mean_val2 = WebRtcIsac_kQMeanLag2Mid; - mean_val3 = WebRtcIsac_kQMeanLag3Mid; - mean_val4 = WebRtcIsac_kQMeanLag4Mid; - lower_limit = WebRtcIsac_kQIndexLowerLimitLagMid; - init_index = WebRtcIsac_kQInitIndexLagMid; - } else { - StepSize = WebRtcIsac_kQPitchLagStepsizeHi; - cdf = WebRtcIsac_kQPitchLagCdfPtrHi; - cdf_size = WebRtcIsac_kQPitchLagCdfSizeHi; - mean_val2 = WebRtcIsac_kQMeanLag2Hi; - mean_val3 = WebRtcIsac_kQMeanLag3Hi; - mean_val4 = WebRtcIsac_kQMeanLag4Hi; - lower_limit = WebRtcIsac_kQindexLowerLimitLagHi; - init_index = WebRtcIsac_kQInitIndexLagHi; - } - - /* entropy decoding of quantization indices */ - err = WebRtcIsac_DecHistBisectMulti(index, streamdata, cdf, cdf_size, 1); - if ((err<0) || (index[0]<0)) // error check - return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG; - - err = WebRtcIsac_DecHistOneStepMulti(index+1, streamdata, cdf+1, init_index, 3); - if (err<0) // error check - return -ISAC_RANGE_ERROR_DECODE_PITCH_LAG; - - - /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */ - C = (index[0] + lower_limit[0]) * StepSize; - for (k=0; kmeanGain[encData->startIdx] = mean_gain; - - /* voicing classification */ - if (mean_gain < 0.2) { - StepSize = WebRtcIsac_kQPitchLagStepsizeLo; - cdf = WebRtcIsac_kQPitchLagCdfPtrLo; - mean_val2 = WebRtcIsac_kQMeanLag2Lo; - mean_val3 = WebRtcIsac_kQMeanLag3Lo; - mean_val4 = WebRtcIsac_kQMeanLag4Lo; - lower_limit = WebRtcIsac_kQIndexLowerLimitLagLo; - upper_limit = WebRtcIsac_kQIndexUpperLimitLagLo; - } else if (mean_gain < 0.4) { - StepSize = WebRtcIsac_kQPitchLagStepsizeMid; - cdf = WebRtcIsac_kQPitchLagCdfPtrMid; - mean_val2 = WebRtcIsac_kQMeanLag2Mid; - mean_val3 = WebRtcIsac_kQMeanLag3Mid; - mean_val4 = WebRtcIsac_kQMeanLag4Mid; - lower_limit = WebRtcIsac_kQIndexLowerLimitLagMid; - upper_limit = WebRtcIsac_kQIndexUpperLimitLagMid; - } else { - StepSize = WebRtcIsac_kQPitchLagStepsizeHi; - cdf = WebRtcIsac_kQPitchLagCdfPtrHi; - mean_val2 = WebRtcIsac_kQMeanLag2Hi; - mean_val3 = WebRtcIsac_kQMeanLag3Hi; - mean_val4 = WebRtcIsac_kQMeanLag4Hi; - lower_limit = WebRtcIsac_kQindexLowerLimitLagHi; - upper_limit = WebRtcIsac_kQindexUpperLimitLagHi; - } - - - /* find quantization index */ - for (k=0; k<4; k++) - { - /* transform */ - C = 0.0; - for (j=0; j upper_limit[k]) index[k] = upper_limit[k]; - index[k] -= lower_limit[k]; - - /* Save data for creation of multiple bit streams */ - encData->pitchIndex[PITCH_SUBFRAMES*encData->startIdx + k] = index[k]; - } - - /* unquantize back to transform coefficients and do the inverse transform: S = T'*C */ - C = (index[0] + lower_limit[0]) * StepSize; - for (k=0; k WebRtcIsac_kQKltMaxIndGain[k]) { - index_g[k] = WebRtcIsac_kQKltMaxIndGain[k]; - } - index_ovr_g[k] = WebRtcIsac_kQKltOffsetGain[model][k]+index_g[k]; - - /* find quantization levels for coefficients */ - tmpcoeffs_g[WebRtcIsac_kQKltSelIndGain[k]] = WebRtcIsac_kQKltLevelsGain[WebRtcIsac_kQKltOfLevelsGain[model]+index_ovr_g[k]]; - } -} - - -/* decode & dequantize LPC Coef */ -int -WebRtcIsac_DecodeLpcCoefUB( - Bitstr* streamdata, - double* lpcVecs, - double* percepFilterGains, - WebRtc_Word16 bandwidth) -{ - int index_s[KLT_ORDER_SHAPE]; - - double U[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - int err; - - /* entropy decoding of quantization indices */ - switch(bandwidth) - { - case isac12kHz: - { - err = WebRtcIsac_DecHistOneStepMulti(index_s, streamdata, - WebRtcIsac_kLpcShapeCdfMatUb12, WebRtcIsac_kLpcShapeEntropySearchUb12, - UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME); - break; - } - case isac16kHz: - { - err = WebRtcIsac_DecHistOneStepMulti(index_s, streamdata, - WebRtcIsac_kLpcShapeCdfMatUb16, WebRtcIsac_kLpcShapeEntropySearchUb16, - UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME); - break; - } - default: - return -1; - } - - if (err<0) // error check - { - return err; - } - - WebRtcIsac_DequantizeLpcParam(index_s, lpcVecs, bandwidth); - WebRtcIsac_CorrelateInterVec(lpcVecs, U, bandwidth); - WebRtcIsac_CorrelateIntraVec(U, lpcVecs, bandwidth); - WebRtcIsac_AddLarMean(lpcVecs, bandwidth); - - - WebRtcIsac_DecodeLpcGainUb(percepFilterGains, streamdata); - - if(bandwidth == isac16kHz) - { - // decode another set of Gains - WebRtcIsac_DecodeLpcGainUb(&percepFilterGains[SUBFRAMES], streamdata); - } - - return 0; -} - -WebRtc_Word16 -WebRtcIsac_EncodeBandwidth( - enum ISACBandwidth bandwidth, - Bitstr* streamData) -{ - int bandwidthMode; - switch(bandwidth) - { - case isac12kHz: - { - bandwidthMode = 0; - break; - } - case isac16kHz: - { - bandwidthMode = 1; - break; - } - default: - return -ISAC_DISALLOWED_ENCODER_BANDWIDTH; - } - - WebRtcIsac_EncHistMulti(streamData, &bandwidthMode, - WebRtcIsac_kOneBitEqualProbCdf_ptr, 1); - return 0; -} - -WebRtc_Word16 -WebRtcIsac_DecodeBandwidth( - Bitstr* streamData, - enum ISACBandwidth* bandwidth) -{ - int bandwidthMode; - - if(WebRtcIsac_DecHistOneStepMulti(&bandwidthMode, streamData, - WebRtcIsac_kOneBitEqualProbCdf_ptr, - WebRtcIsac_kOneBitEqualProbInitIndex, 1) < 0) - { - // error check - return -ISAC_RANGE_ERROR_DECODE_BANDWITH; - } - - switch(bandwidthMode) - { - case 0: - { - *bandwidth = isac12kHz; - break; - } - case 1: - { - *bandwidth = isac16kHz; - break; - } - default: - return -ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER; - } - return 0; -} - -WebRtc_Word16 -WebRtcIsac_EncodeJitterInfo( - WebRtc_Word32 jitterIndex, - Bitstr* streamData) -{ - // This is to avoid LINUX warning until we change 'int' to - // 'Word32' - int intVar; - - if((jitterIndex < 0) || (jitterIndex > 1)) - { - return -1; - } - intVar = (int)(jitterIndex); - // Use the same CDF table as for bandwidth - // both take two values with equal probability - WebRtcIsac_EncHistMulti(streamData, &intVar, - WebRtcIsac_kOneBitEqualProbCdf_ptr, 1); - return 0; - -} - -WebRtc_Word16 -WebRtcIsac_DecodeJitterInfo( - Bitstr* streamData, - WebRtc_Word32* jitterInfo) -{ - int intVar; - - // Use the same CDF table as for bandwidth - // both take two values with equal probability - if(WebRtcIsac_DecHistOneStepMulti(&intVar, streamData, - WebRtcIsac_kOneBitEqualProbCdf_ptr, - WebRtcIsac_kOneBitEqualProbInitIndex, 1) < 0) - { - // error check - return -ISAC_RANGE_ERROR_DECODE_BANDWITH; - } - *jitterInfo = (WebRtc_Word16)(intVar); - return 0; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.h b/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.h deleted file mode 100644 index 8446bcf3e..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/entropy_coding.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * entropy_coding.h - * - * This header file declares all of the functions used to arithmetically - * encode the iSAC bistream - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ - -#include "structs.h" - -/* decode complex spectrum (return number of bytes in stream) */ -int WebRtcIsac_DecodeSpecLb(Bitstr *streamdata, - double *fr, - double *fi, - WebRtc_Word16 AvgPitchGain_Q12); - -/****************************************************************************** - * WebRtcIsac_DecodeSpecUB16() - * Decode real and imaginary part of the DFT coefficients, given a bit-stream. - * This function is called when the codec is in 0-16 kHz bandwidth. - * The decoded DFT coefficient can be transformed to time domain by - * WebRtcIsac_Time2Spec(). - * - * Input: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are written to. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are written to. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_DecodeSpecUB16( - Bitstr* streamdata, - double* fr, - double* fi); - - -/****************************************************************************** - * WebRtcIsac_DecodeSpecUB12() - * Decode real and imaginary part of the DFT coefficients, given a bit-stream. - * This function is called when the codec is in 0-12 kHz bandwidth. - * The decoded DFT coefficient can be transformed to time domain by - * WebRtcIsac_Time2Spec(). - * - * Input: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are written to. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are written to. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_DecodeSpecUB12( - Bitstr* streamdata, - double* fr, - double* fi); - - -/* encode complex spectrum */ -int WebRtcIsac_EncodeSpecLb(const WebRtc_Word16* fr, - const WebRtc_Word16* fi, - Bitstr* streamdata, - WebRtc_Word16 AvgPitchGain_Q12); - - -/****************************************************************************** - * WebRtcIsac_EncodeSpecUB16() - * Quantize and encode real and imaginary part of the DFT coefficients. - * This function is called when the codec is in 0-16 kHz bandwidth. - * The real and imaginary part are computed by calling WebRtcIsac_Time2Spec(). - * - * - * Input: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are stored. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are stored. - * - * Output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_EncodeSpecUB16( - const WebRtc_Word16* fr, - const WebRtc_Word16* fi, - Bitstr* streamdata); - - -/****************************************************************************** - * WebRtcIsac_EncodeSpecUB12() - * Quantize and encode real and imaginary part of the DFT coefficients. - * This function is called when the codec is in 0-12 kHz bandwidth. - * The real and imaginary part are computed by calling WebRtcIsac_Time2Spec(). - * - * - * Input: - * -*fr : pointer to a buffer where the real part of DFT - * coefficients are stored. - * -*fi : pointer to a buffer where the imaginary part - * of DFT coefficients are stored. - * - * Output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Return value : < 0 if an error occures - * 0 if succeeded. - */ -int WebRtcIsac_EncodeSpecUB12( - const WebRtc_Word16* fr, - const WebRtc_Word16* fi, - Bitstr* streamdata); - - -/* decode & dequantize LPC Coef */ -int WebRtcIsac_DecodeLpcCoef(Bitstr *streamdata, double *LPCCoef, int *outmodel); -int WebRtcIsac_DecodeLpcCoefUB( - Bitstr* streamdata, - double* lpcVecs, - double* percepFilterGains, - WebRtc_Word16 bandwidth); - -int WebRtcIsac_DecodeLpc(Bitstr *streamdata, double *LPCCoef_lo, double *LPCCoef_hi, int *outmodel); - -/* quantize & code LPC Coef */ -void WebRtcIsac_EncodeLpcLb(double *LPCCoef_lo, double *LPCCoef_hi, int *model, double *size, Bitstr *streamdata, ISAC_SaveEncData_t* encData); -void WebRtcIsac_EncodeLpcGainLb(double *LPCCoef_lo, double *LPCCoef_hi, int model, Bitstr *streamdata, ISAC_SaveEncData_t* encData); - -/****************************************************************************** - * WebRtcIsac_EncodeLpcUB() - * Encode LPC parameters, given as A-polynomial, of upper-band. The encoding - * is performed in LAR domain. - * For the upper-band, we compute and encode LPC of some sub-frames, LPC of - * other sub-frames are computed by linear interpolation, in LAR domain. This - * function performs the interpolation and returns the LPC of all sub-frames. - * - * Inputs: - * - lpcCoef : a buffer containing A-polynomials of sub-frames - * (excluding first coefficient that is 1). - * - bandwidth : specifies if the codec is operating at 0-12 kHz - * or 0-16 kHz mode. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - interpolLPCCoeff : Decoded and interpolated LPC (A-polynomial) - * of all sub-frames. - * If LP analysis is of order K, and there are N - * sub-frames then this is a buffer of size - * (k + 1) * N, each vector starts with the LPC gain - * of the corresponding sub-frame. The LPC gains - * are encoded and inserted after this function is - * called. The first A-coefficient which is 1 is not - * included. - * - * Return value : 0 if encoding is successful, - * <0 if failed to encode. - */ -WebRtc_Word16 WebRtcIsac_EncodeLpcUB( - double* lpcCoeff, - Bitstr* streamdata, - double* interpolLPCCoeff, - WebRtc_Word16 bandwidth, - ISACUBSaveEncDataStruct* encData); - -/****************************************************************************** - * WebRtcIsac_DecodeInterpolLpcUb() - * Decode LPC coefficients and interpolate to get the coefficients fo all - * sub-frmaes. - * - * Inputs: - * - bandwidth : spepecifies if the codec is in 0-12 kHz or - * 0-16 kHz mode. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - percepFilterParam : Decoded and interpolated LPC (A-polynomial) of - * all sub-frames. - * If LP analysis is of order K, and there are N - * sub-frames then this is a buffer of size - * (k + 1) * N, each vector starts with the LPC gain - * of the corresponding sub-frame. The LPC gains - * are encoded and inserted after this function is - * called. The first A-coefficient which is 1 is not - * included. - * - * Return value : 0 if encoding is successful, - * <0 if failed to encode. - */ -WebRtc_Word16 WebRtcIsac_DecodeInterpolLpcUb( - Bitstr* streamdata, - double* percepFilterParam, - WebRtc_Word16 bandwidth); - -/* decode & dequantize RC */ -int WebRtcIsac_DecodeRc(Bitstr *streamdata, WebRtc_Word16 *RCQ15); - -/* quantize & code RC */ -void WebRtcIsac_EncodeRc(WebRtc_Word16 *RCQ15, Bitstr *streamdata); - -/* decode & dequantize squared Gain */ -int WebRtcIsac_DecodeGain2(Bitstr *streamdata, WebRtc_Word32 *Gain2); - -/* quantize & code squared Gain (input is squared gain) */ -int WebRtcIsac_EncodeGain2(WebRtc_Word32 *gain2, Bitstr *streamdata); - -void WebRtcIsac_EncodePitchGain(WebRtc_Word16* PitchGains_Q12, Bitstr* streamdata, ISAC_SaveEncData_t* encData); - -void WebRtcIsac_EncodePitchLag(double* PitchLags, WebRtc_Word16* PitchGain_Q12, Bitstr* streamdata, ISAC_SaveEncData_t* encData); - -int WebRtcIsac_DecodePitchGain(Bitstr *streamdata, WebRtc_Word16 *PitchGain_Q12); -int WebRtcIsac_DecodePitchLag(Bitstr *streamdata, WebRtc_Word16 *PitchGain_Q12, double *PitchLag); - -int WebRtcIsac_DecodeFrameLen(Bitstr *streamdata, WebRtc_Word16 *framelength); -int WebRtcIsac_EncodeFrameLen(WebRtc_Word16 framelength, Bitstr *streamdata); -int WebRtcIsac_DecodeSendBW(Bitstr *streamdata, WebRtc_Word16 *BWno); -void WebRtcIsac_EncodeReceiveBw(int *BWno, Bitstr *streamdata); - -/* step-down */ -void WebRtcIsac_Poly2Rc(double *a, int N, double *RC); - -/* step-up */ -void WebRtcIsac_Rc2Poly(double *RC, int N, double *a); - -void WebRtcIsac_TranscodeLPCCoef(double *LPCCoef_lo, double *LPCCoef_hi, int model, - int *index_g); - - -/****************************************************************************** - * WebRtcIsac_EncodeLpcGainUb() - * Encode LPC gains of sub-Frames. - * - * Input/outputs: - * - lpGains : a buffer which contains 'SUBFRAME' number of - * LP gains to be encoded. The input values are - * overwritten by the quantized values. - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - lpcGainIndex : quantization indices for lpc gains, these will - * be stored to be used for FEC. - */ -void WebRtcIsac_EncodeLpcGainUb( - double* lpGains, - Bitstr* streamdata, - int* lpcGainIndex); - - -/****************************************************************************** - * WebRtcIsac_EncodeLpcGainUb() - * Store LPC gains of sub-Frames in 'streamdata'. - * - * Input: - * - lpGains : a buffer which contains 'SUBFRAME' number of - * LP gains to be encoded. - * Input/outputs: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - */ -void WebRtcIsac_StoreLpcGainUb( - double* lpGains, - Bitstr* streamdata); - - -/****************************************************************************** - * WebRtcIsac_DecodeLpcGainUb() - * Decode the LPC gain of sub-frames. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - lpGains : a buffer where decoded LPC gians will be stored. - * - * Return value : 0 if succeeded. - * <0 if failed. - */ -WebRtc_Word16 WebRtcIsac_DecodeLpcGainUb( - double* lpGains, - Bitstr* streamdata); - - -/****************************************************************************** - * WebRtcIsac_EncodeBandwidth() - * Encode if the bandwidth of encoded audio is 0-12 kHz or 0-16 kHz. - * - * Input: - * - bandwidth : an enumerator specifying if the codec in is - * 0-12 kHz or 0-16 kHz mode. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Return value : 0 if succeeded. - * <0 if failed. - */ -WebRtc_Word16 WebRtcIsac_EncodeBandwidth( - enum ISACBandwidth bandwidth, - Bitstr* streamData); - - -/****************************************************************************** - * WebRtcIsac_DecodeBandwidth() - * Decode the bandwidth of the encoded audio, i.e. if the bandwidth is 0-12 kHz - * or 0-16 kHz. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - bandwidth : an enumerator specifying if the codec is in - * 0-12 kHz or 0-16 kHz mode. - * - * Return value : 0 if succeeded. - * <0 if failed. - */ -WebRtc_Word16 WebRtcIsac_DecodeBandwidth( - Bitstr* streamData, - enum ISACBandwidth* bandwidth); - - -/****************************************************************************** - * WebRtcIsac_EncodeJitterInfo() - * Decode the jitter information. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Input: - * - jitterInfo : one bit of info specifying if the channel is - * in high/low jitter. Zero indicates low jitter - * and one indicates high jitter. - * - * Return value : 0 if succeeded. - * <0 if failed. - */ -WebRtc_Word16 WebRtcIsac_EncodeJitterInfo( - WebRtc_Word32 jitterIndex, - Bitstr* streamData); - - -/****************************************************************************** - * WebRtcIsac_DecodeJitterInfo() - * Decode the jitter information. - * - * Input/output: - * - streamdata : pointer to a stucture containg the encoded - * data and theparameters needed for entropy - * coding. - * - * Output: - * - jitterInfo : one bit of info specifying if the channel is - * in high/low jitter. Zero indicates low jitter - * and one indicates high jitter. - * - * Return value : 0 if succeeded. - * <0 if failed. - */ -WebRtc_Word16 WebRtcIsac_DecodeJitterInfo( - Bitstr* streamData, - WebRtc_Word32* jitterInfo); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_ENTROPY_CODING_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/fft.c b/modules/audio_coding/codecs/iSAC/main/source/fft.c deleted file mode 100644 index c8247983a..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/fft.c +++ /dev/null @@ -1,947 +0,0 @@ -/* - * Copyright(c)1995,97 Mark Olesen - * Queen's Univ at Kingston (Canada) - * - * Permission to use, copy, modify, and distribute this software for - * any purpose without fee is hereby granted, provided that this - * entire notice is included in all copies of any software which is - * or includes a copy or modification of this software and in all - * copies of the supporting documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR QUEEN'S - * UNIVERSITY AT KINGSTON MAKES ANY REPRESENTATION OR WARRANTY OF ANY - * KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * All of which is to say that you can do what you like with this - * source code provided you don't try to sell it as your own and you - * include an unaltered copy of this message (including the - * copyright). - * - * It is also implicitly understood that bug fixes and improvements - * should make their way back to the general Internet community so - * that everyone benefits. - * - * Changes: - * Trivial type modifications by the WebRTC authors. - */ - - -/* - * File: - * WebRtcIsac_Fftn.c - * - * Public: - * WebRtcIsac_Fftn / fftnf (); - * - * Private: - * WebRtcIsac_Fftradix / fftradixf (); - * - * Descript: - * multivariate complex Fourier transform, computed in place - * using mixed-radix Fast Fourier Transform algorithm. - * - * Fortran code by: - * RC Singleton, Stanford Research Institute, Sept. 1968 - * - * translated by f2c (version 19950721). - * - * int WebRtcIsac_Fftn (int ndim, const int dims[], REAL Re[], REAL Im[], - * int iSign, double scaling); - * - * NDIM = the total number dimensions - * DIMS = a vector of array sizes - * if NDIM is zero then DIMS must be zero-terminated - * - * RE and IM hold the real and imaginary components of the data, and return - * the resulting real and imaginary Fourier coefficients. Multidimensional - * data *must* be allocated contiguously. There is no limit on the number - * of dimensions. - * - * ISIGN = the sign of the complex exponential (ie, forward or inverse FFT) - * the magnitude of ISIGN (normally 1) is used to determine the - * correct indexing increment (see below). - * - * SCALING = normalizing constant by which the final result is *divided* - * if SCALING == -1, normalize by total dimension of the transform - * if SCALING < -1, normalize by the square-root of the total dimension - * - * example: - * tri-variate transform with Re[n1][n2][n3], Im[n1][n2][n3] - * - * int dims[3] = {n1,n2,n3} - * WebRtcIsac_Fftn (3, dims, Re, Im, 1, scaling); - * - *-----------------------------------------------------------------------* - * int WebRtcIsac_Fftradix (REAL Re[], REAL Im[], size_t nTotal, size_t nPass, - * size_t nSpan, int iSign, size_t max_factors, - * size_t max_perm); - * - * RE, IM - see above documentation - * - * Although there is no limit on the number of dimensions, WebRtcIsac_Fftradix() must - * be called once for each dimension, but the calls may be in any order. - * - * NTOTAL = the total number of complex data values - * NPASS = the dimension of the current variable - * NSPAN/NPASS = the spacing of consecutive data values while indexing the - * current variable - * ISIGN - see above documentation - * - * example: - * tri-variate transform with Re[n1][n2][n3], Im[n1][n2][n3] - * - * WebRtcIsac_Fftradix (Re, Im, n1*n2*n3, n1, n1, 1, maxf, maxp); - * WebRtcIsac_Fftradix (Re, Im, n1*n2*n3, n2, n1*n2, 1, maxf, maxp); - * WebRtcIsac_Fftradix (Re, Im, n1*n2*n3, n3, n1*n2*n3, 1, maxf, maxp); - * - * single-variate transform, - * NTOTAL = N = NSPAN = (number of complex data values), - * - * WebRtcIsac_Fftradix (Re, Im, n, n, n, 1, maxf, maxp); - * - * The data can also be stored in a single array with alternating real and - * imaginary parts, the magnitude of ISIGN is changed to 2 to give correct - * indexing increment, and data [0] and data [1] used to pass the initial - * addresses for the sequences of real and imaginary values, - * - * example: - * REAL data [2*NTOTAL]; - * WebRtcIsac_Fftradix ( &data[0], &data[1], NTOTAL, nPass, nSpan, 2, maxf, maxp); - * - * for temporary allocation: - * - * MAX_FACTORS >= the maximum prime factor of NPASS - * MAX_PERM >= the number of prime factors of NPASS. In addition, - * if the square-free portion K of NPASS has two or more prime - * factors, then MAX_PERM >= (K-1) - * - * storage in FACTOR for a maximum of 15 prime factors of NPASS. if NPASS - * has more than one square-free factor, the product of the square-free - * factors must be <= 210 array storage for maximum prime factor of 23 the - * following two constants should agree with the array dimensions. - * - *----------------------------------------------------------------------*/ -#include "fft.h" - -#include -#include - - - -/* double precision routine */ -static int -WebRtcIsac_Fftradix (double Re[], double Im[], - size_t nTotal, size_t nPass, size_t nSpan, int isign, - int max_factors, unsigned int max_perm, - FFTstr *fftstate); - - - -#ifndef M_PI -# define M_PI 3.14159265358979323846264338327950288 -#endif - -#ifndef SIN60 -# define SIN60 0.86602540378443865 /* sin(60 deg) */ -# define COS72 0.30901699437494742 /* cos(72 deg) */ -# define SIN72 0.95105651629515357 /* sin(72 deg) */ -#endif - -# define REAL double -# define FFTN WebRtcIsac_Fftn -# define FFTNS "fftn" -# define FFTRADIX WebRtcIsac_Fftradix -# define FFTRADIXS "fftradix" - - -int WebRtcIsac_Fftns(unsigned int ndim, const int dims[], - double Re[], - double Im[], - int iSign, - double scaling, - FFTstr *fftstate) -{ - - size_t nSpan, nPass, nTotal; - unsigned int i; - int ret, max_factors, max_perm; - - /* - * tally the number of elements in the data array - * and determine the number of dimensions - */ - nTotal = 1; - if (ndim && dims [0]) - { - for (i = 0; i < ndim; i++) - { - if (dims [i] <= 0) - { - return -1; - } - nTotal *= dims [i]; - } - } - else - { - ndim = 0; - for (i = 0; dims [i]; i++) - { - if (dims [i] <= 0) - { - return -1; - } - nTotal *= dims [i]; - ndim++; - } - } - - /* determine maximum number of factors and permuations */ -#if 1 - /* - * follow John Beale's example, just use the largest dimension and don't - * worry about excess allocation. May be someone else will do it? - */ - max_factors = max_perm = 1; - for (i = 0; i < ndim; i++) - { - nSpan = dims [i]; - if ((int)nSpan > max_factors) - { - max_factors = (int)nSpan; - } - if ((int)nSpan > max_perm) - { - max_perm = (int)nSpan; - } - } -#else - /* use the constants used in the original Fortran code */ - max_factors = 23; - max_perm = 209; -#endif - /* loop over the dimensions: */ - nPass = 1; - for (i = 0; i < ndim; i++) - { - nSpan = dims [i]; - nPass *= nSpan; - ret = FFTRADIX (Re, Im, nTotal, nSpan, nPass, iSign, - max_factors, max_perm, fftstate); - /* exit, clean-up already done */ - if (ret) - return ret; - } - - /* Divide through by the normalizing constant: */ - if (scaling && scaling != 1.0) - { - if (iSign < 0) iSign = -iSign; - if (scaling < 0.0) - { - scaling = (double)nTotal; - if (scaling < -1.0) - scaling = sqrt (scaling); - } - scaling = 1.0 / scaling; /* multiply is often faster */ - for (i = 0; i < nTotal; i += iSign) - { - Re [i] *= scaling; - Im [i] *= scaling; - } - } - return 0; -} - -/* - * singleton's mixed radix routine - * - * could move allocation out to WebRtcIsac_Fftn(), but leave it here so that it's - * possible to make this a standalone function - */ - -static int FFTRADIX (REAL Re[], - REAL Im[], - size_t nTotal, - size_t nPass, - size_t nSpan, - int iSign, - int max_factors, - unsigned int max_perm, - FFTstr *fftstate) -{ - int ii, mfactor, kspan, ispan, inc; - int j, jc, jf, jj, k, k1, k2, k3, k4, kk, kt, nn, ns, nt; - - - REAL radf; - REAL c1, c2, c3, cd, aa, aj, ak, ajm, ajp, akm, akp; - REAL s1, s2, s3, sd, bb, bj, bk, bjm, bjp, bkm, bkp; - - REAL *Rtmp = NULL; /* temp space for real part*/ - REAL *Itmp = NULL; /* temp space for imaginary part */ - REAL *Cos = NULL; /* Cosine values */ - REAL *Sin = NULL; /* Sine values */ - - REAL s60 = SIN60; /* sin(60 deg) */ - REAL c72 = COS72; /* cos(72 deg) */ - REAL s72 = SIN72; /* sin(72 deg) */ - REAL pi2 = M_PI; /* use PI first, 2 PI later */ - - - fftstate->SpaceAlloced = 0; - fftstate->MaxPermAlloced = 0; - - - // initialize to avoid warnings - k3 = c2 = c3 = s2 = s3 = 0.0; - - if (nPass < 2) - return 0; - - /* allocate storage */ - if (fftstate->SpaceAlloced < max_factors * sizeof (REAL)) - { -#ifdef SUN_BROKEN_REALLOC - if (!fftstate->SpaceAlloced) /* first time */ - { - fftstate->SpaceAlloced = max_factors * sizeof (REAL); - } - else - { -#endif - fftstate->SpaceAlloced = max_factors * sizeof (REAL); -#ifdef SUN_BROKEN_REALLOC - } -#endif - } - else - { - /* allow full use of alloc'd space */ - max_factors = fftstate->SpaceAlloced / sizeof (REAL); - } - if (fftstate->MaxPermAlloced < max_perm) - { -#ifdef SUN_BROKEN_REALLOC - if (!fftstate->MaxPermAlloced) /* first time */ - else -#endif - fftstate->MaxPermAlloced = max_perm; - } - else - { - /* allow full use of alloc'd space */ - max_perm = fftstate->MaxPermAlloced; - } - if (fftstate->Tmp0 == NULL || fftstate->Tmp1 == NULL || fftstate->Tmp2 == NULL || fftstate->Tmp3 == NULL - || fftstate->Perm == NULL) { - return -1; - } - - /* assign pointers */ - Rtmp = (REAL *) fftstate->Tmp0; - Itmp = (REAL *) fftstate->Tmp1; - Cos = (REAL *) fftstate->Tmp2; - Sin = (REAL *) fftstate->Tmp3; - - /* - * Function Body - */ - inc = iSign; - if (iSign < 0) { - s72 = -s72; - s60 = -s60; - pi2 = -pi2; - inc = -inc; /* absolute value */ - } - - /* adjust for strange increments */ - nt = inc * (int)nTotal; - ns = inc * (int)nSpan; - kspan = ns; - - nn = nt - inc; - jc = ns / (int)nPass; - radf = pi2 * (double) jc; - pi2 *= 2.0; /* use 2 PI from here on */ - - ii = 0; - jf = 0; - /* determine the factors of n */ - mfactor = 0; - k = (int)nPass; - while (k % 16 == 0) { - mfactor++; - fftstate->factor [mfactor - 1] = 4; - k /= 16; - } - j = 3; - jj = 9; - do { - while (k % jj == 0) { - mfactor++; - fftstate->factor [mfactor - 1] = j; - k /= jj; - } - j += 2; - jj = j * j; - } while (jj <= k); - if (k <= 4) { - kt = mfactor; - fftstate->factor [mfactor] = k; - if (k != 1) - mfactor++; - } else { - if (k - (k / 4 << 2) == 0) { - mfactor++; - fftstate->factor [mfactor - 1] = 2; - k /= 4; - } - kt = mfactor; - j = 2; - do { - if (k % j == 0) { - mfactor++; - fftstate->factor [mfactor - 1] = j; - k /= j; - } - j = ((j + 1) / 2 << 1) + 1; - } while (j <= k); - } - if (kt) { - j = kt; - do { - mfactor++; - fftstate->factor [mfactor - 1] = fftstate->factor [j - 1]; - j--; - } while (j); - } - - /* test that mfactors is in range */ - if (mfactor > NFACTOR) - { - return -1; - } - - /* compute fourier transform */ - for (;;) { - sd = radf / (double) kspan; - cd = sin(sd); - cd = 2.0 * cd * cd; - sd = sin(sd + sd); - kk = 0; - ii++; - - switch (fftstate->factor [ii - 1]) { - case 2: - /* transform for factor of 2 (including rotation factor) */ - kspan /= 2; - k1 = kspan + 2; - do { - do { - k2 = kk + kspan; - ak = Re [k2]; - bk = Im [k2]; - Re [k2] = Re [kk] - ak; - Im [k2] = Im [kk] - bk; - Re [kk] += ak; - Im [kk] += bk; - kk = k2 + kspan; - } while (kk < nn); - kk -= nn; - } while (kk < jc); - if (kk >= kspan) - goto Permute_Results_Label; /* exit infinite loop */ - do { - c1 = 1.0 - cd; - s1 = sd; - do { - do { - do { - k2 = kk + kspan; - ak = Re [kk] - Re [k2]; - bk = Im [kk] - Im [k2]; - Re [kk] += Re [k2]; - Im [kk] += Im [k2]; - Re [k2] = c1 * ak - s1 * bk; - Im [k2] = s1 * ak + c1 * bk; - kk = k2 + kspan; - } while (kk < (nt-1)); - k2 = kk - nt; - c1 = -c1; - kk = k1 - k2; - } while (kk > k2); - ak = c1 - (cd * c1 + sd * s1); - s1 = sd * c1 - cd * s1 + s1; - c1 = 2.0 - (ak * ak + s1 * s1); - s1 *= c1; - c1 *= ak; - kk += jc; - } while (kk < k2); - k1 += inc + inc; - kk = (k1 - kspan + 1) / 2 + jc - 1; - } while (kk < (jc + jc)); - break; - - case 4: /* transform for factor of 4 */ - ispan = kspan; - kspan /= 4; - - do { - c1 = 1.0; - s1 = 0.0; - do { - do { - k1 = kk + kspan; - k2 = k1 + kspan; - k3 = k2 + kspan; - akp = Re [kk] + Re [k2]; - akm = Re [kk] - Re [k2]; - ajp = Re [k1] + Re [k3]; - ajm = Re [k1] - Re [k3]; - bkp = Im [kk] + Im [k2]; - bkm = Im [kk] - Im [k2]; - bjp = Im [k1] + Im [k3]; - bjm = Im [k1] - Im [k3]; - Re [kk] = akp + ajp; - Im [kk] = bkp + bjp; - ajp = akp - ajp; - bjp = bkp - bjp; - if (iSign < 0) { - akp = akm + bjm; - bkp = bkm - ajm; - akm -= bjm; - bkm += ajm; - } else { - akp = akm - bjm; - bkp = bkm + ajm; - akm += bjm; - bkm -= ajm; - } - /* avoid useless multiplies */ - if (s1 == 0.0) { - Re [k1] = akp; - Re [k2] = ajp; - Re [k3] = akm; - Im [k1] = bkp; - Im [k2] = bjp; - Im [k3] = bkm; - } else { - Re [k1] = akp * c1 - bkp * s1; - Re [k2] = ajp * c2 - bjp * s2; - Re [k3] = akm * c3 - bkm * s3; - Im [k1] = akp * s1 + bkp * c1; - Im [k2] = ajp * s2 + bjp * c2; - Im [k3] = akm * s3 + bkm * c3; - } - kk = k3 + kspan; - } while (kk < nt); - - c2 = c1 - (cd * c1 + sd * s1); - s1 = sd * c1 - cd * s1 + s1; - c1 = 2.0 - (c2 * c2 + s1 * s1); - s1 *= c1; - c1 *= c2; - /* values of c2, c3, s2, s3 that will get used next time */ - c2 = c1 * c1 - s1 * s1; - s2 = 2.0 * c1 * s1; - c3 = c2 * c1 - s2 * s1; - s3 = c2 * s1 + s2 * c1; - kk = kk - nt + jc; - } while (kk < kspan); - kk = kk - kspan + inc; - } while (kk < jc); - if (kspan == jc) - goto Permute_Results_Label; /* exit infinite loop */ - break; - - default: - /* transform for odd factors */ -#ifdef FFT_RADIX4 - return -1; - break; -#else /* FFT_RADIX4 */ - k = fftstate->factor [ii - 1]; - ispan = kspan; - kspan /= k; - - switch (k) { - case 3: /* transform for factor of 3 (optional code) */ - do { - do { - k1 = kk + kspan; - k2 = k1 + kspan; - ak = Re [kk]; - bk = Im [kk]; - aj = Re [k1] + Re [k2]; - bj = Im [k1] + Im [k2]; - Re [kk] = ak + aj; - Im [kk] = bk + bj; - ak -= 0.5 * aj; - bk -= 0.5 * bj; - aj = (Re [k1] - Re [k2]) * s60; - bj = (Im [k1] - Im [k2]) * s60; - Re [k1] = ak - bj; - Re [k2] = ak + bj; - Im [k1] = bk + aj; - Im [k2] = bk - aj; - kk = k2 + kspan; - } while (kk < (nn - 1)); - kk -= nn; - } while (kk < kspan); - break; - - case 5: /* transform for factor of 5 (optional code) */ - c2 = c72 * c72 - s72 * s72; - s2 = 2.0 * c72 * s72; - do { - do { - k1 = kk + kspan; - k2 = k1 + kspan; - k3 = k2 + kspan; - k4 = k3 + kspan; - akp = Re [k1] + Re [k4]; - akm = Re [k1] - Re [k4]; - bkp = Im [k1] + Im [k4]; - bkm = Im [k1] - Im [k4]; - ajp = Re [k2] + Re [k3]; - ajm = Re [k2] - Re [k3]; - bjp = Im [k2] + Im [k3]; - bjm = Im [k2] - Im [k3]; - aa = Re [kk]; - bb = Im [kk]; - Re [kk] = aa + akp + ajp; - Im [kk] = bb + bkp + bjp; - ak = akp * c72 + ajp * c2 + aa; - bk = bkp * c72 + bjp * c2 + bb; - aj = akm * s72 + ajm * s2; - bj = bkm * s72 + bjm * s2; - Re [k1] = ak - bj; - Re [k4] = ak + bj; - Im [k1] = bk + aj; - Im [k4] = bk - aj; - ak = akp * c2 + ajp * c72 + aa; - bk = bkp * c2 + bjp * c72 + bb; - aj = akm * s2 - ajm * s72; - bj = bkm * s2 - bjm * s72; - Re [k2] = ak - bj; - Re [k3] = ak + bj; - Im [k2] = bk + aj; - Im [k3] = bk - aj; - kk = k4 + kspan; - } while (kk < (nn-1)); - kk -= nn; - } while (kk < kspan); - break; - - default: - if (k != jf) { - jf = k; - s1 = pi2 / (double) k; - c1 = cos(s1); - s1 = sin(s1); - if (jf > max_factors){ - return -1; - } - Cos [jf - 1] = 1.0; - Sin [jf - 1] = 0.0; - j = 1; - do { - Cos [j - 1] = Cos [k - 1] * c1 + Sin [k - 1] * s1; - Sin [j - 1] = Cos [k - 1] * s1 - Sin [k - 1] * c1; - k--; - Cos [k - 1] = Cos [j - 1]; - Sin [k - 1] = -Sin [j - 1]; - j++; - } while (j < k); - } - do { - do { - k1 = kk; - k2 = kk + ispan; - ak = aa = Re [kk]; - bk = bb = Im [kk]; - j = 1; - k1 += kspan; - do { - k2 -= kspan; - j++; - Rtmp [j - 1] = Re [k1] + Re [k2]; - ak += Rtmp [j - 1]; - Itmp [j - 1] = Im [k1] + Im [k2]; - bk += Itmp [j - 1]; - j++; - Rtmp [j - 1] = Re [k1] - Re [k2]; - Itmp [j - 1] = Im [k1] - Im [k2]; - k1 += kspan; - } while (k1 < k2); - Re [kk] = ak; - Im [kk] = bk; - k1 = kk; - k2 = kk + ispan; - j = 1; - do { - k1 += kspan; - k2 -= kspan; - jj = j; - ak = aa; - bk = bb; - aj = 0.0; - bj = 0.0; - k = 1; - do { - k++; - ak += Rtmp [k - 1] * Cos [jj - 1]; - bk += Itmp [k - 1] * Cos [jj - 1]; - k++; - aj += Rtmp [k - 1] * Sin [jj - 1]; - bj += Itmp [k - 1] * Sin [jj - 1]; - jj += j; - if (jj > jf) { - jj -= jf; - } - } while (k < jf); - k = jf - j; - Re [k1] = ak - bj; - Im [k1] = bk + aj; - Re [k2] = ak + bj; - Im [k2] = bk - aj; - j++; - } while (j < k); - kk += ispan; - } while (kk < nn); - kk -= nn; - } while (kk < kspan); - break; - } - - /* multiply by rotation factor (except for factors of 2 and 4) */ - if (ii == mfactor) - goto Permute_Results_Label; /* exit infinite loop */ - kk = jc; - do { - c2 = 1.0 - cd; - s1 = sd; - do { - c1 = c2; - s2 = s1; - kk += kspan; - do { - do { - ak = Re [kk]; - Re [kk] = c2 * ak - s2 * Im [kk]; - Im [kk] = s2 * ak + c2 * Im [kk]; - kk += ispan; - } while (kk < nt); - ak = s1 * s2; - s2 = s1 * c2 + c1 * s2; - c2 = c1 * c2 - ak; - kk = kk - nt + kspan; - } while (kk < ispan); - c2 = c1 - (cd * c1 + sd * s1); - s1 += sd * c1 - cd * s1; - c1 = 2.0 - (c2 * c2 + s1 * s1); - s1 *= c1; - c2 *= c1; - kk = kk - ispan + jc; - } while (kk < kspan); - kk = kk - kspan + jc + inc; - } while (kk < (jc + jc)); - break; -#endif /* FFT_RADIX4 */ - } - } - - /* permute the results to normal order---done in two stages */ - /* permutation for square factors of n */ -Permute_Results_Label: - fftstate->Perm [0] = ns; - if (kt) { - k = kt + kt + 1; - if (mfactor < k) - k--; - j = 1; - fftstate->Perm [k] = jc; - do { - fftstate->Perm [j] = fftstate->Perm [j - 1] / fftstate->factor [j - 1]; - fftstate->Perm [k - 1] = fftstate->Perm [k] * fftstate->factor [j - 1]; - j++; - k--; - } while (j < k); - k3 = fftstate->Perm [k]; - kspan = fftstate->Perm [1]; - kk = jc; - k2 = kspan; - j = 1; - if (nPass != nTotal) { - /* permutation for multivariate transform */ - Permute_Multi_Label: - do { - do { - k = kk + jc; - do { - /* swap Re [kk] <> Re [k2], Im [kk] <> Im [k2] */ - ak = Re [kk]; Re [kk] = Re [k2]; Re [k2] = ak; - bk = Im [kk]; Im [kk] = Im [k2]; Im [k2] = bk; - kk += inc; - k2 += inc; - } while (kk < (k-1)); - kk += ns - jc; - k2 += ns - jc; - } while (kk < (nt-1)); - k2 = k2 - nt + kspan; - kk = kk - nt + jc; - } while (k2 < (ns-1)); - do { - do { - k2 -= fftstate->Perm [j - 1]; - j++; - k2 = fftstate->Perm [j] + k2; - } while (k2 > fftstate->Perm [j - 1]); - j = 1; - do { - if (kk < (k2-1)) - goto Permute_Multi_Label; - kk += jc; - k2 += kspan; - } while (k2 < (ns-1)); - } while (kk < (ns-1)); - } else { - /* permutation for single-variate transform (optional code) */ - Permute_Single_Label: - do { - /* swap Re [kk] <> Re [k2], Im [kk] <> Im [k2] */ - ak = Re [kk]; Re [kk] = Re [k2]; Re [k2] = ak; - bk = Im [kk]; Im [kk] = Im [k2]; Im [k2] = bk; - kk += inc; - k2 += kspan; - } while (k2 < (ns-1)); - do { - do { - k2 -= fftstate->Perm [j - 1]; - j++; - k2 = fftstate->Perm [j] + k2; - } while (k2 >= fftstate->Perm [j - 1]); - j = 1; - do { - if (kk < k2) - goto Permute_Single_Label; - kk += inc; - k2 += kspan; - } while (k2 < (ns-1)); - } while (kk < (ns-1)); - } - jc = k3; - } - - if ((kt << 1) + 1 >= mfactor) - return 0; - ispan = fftstate->Perm [kt]; - /* permutation for square-free factors of n */ - j = mfactor - kt; - fftstate->factor [j] = 1; - do { - fftstate->factor [j - 1] *= fftstate->factor [j]; - j--; - } while (j != kt); - kt++; - nn = fftstate->factor [kt - 1] - 1; - if (nn > (int) max_perm) { - return -1; - } - j = jj = 0; - for (;;) { - k = kt + 1; - k2 = fftstate->factor [kt - 1]; - kk = fftstate->factor [k - 1]; - j++; - if (j > nn) - break; /* exit infinite loop */ - jj += kk; - while (jj >= k2) { - jj -= k2; - k2 = kk; - k++; - kk = fftstate->factor [k - 1]; - jj += kk; - } - fftstate->Perm [j - 1] = jj; - } - /* determine the permutation cycles of length greater than 1 */ - j = 0; - for (;;) { - do { - j++; - kk = fftstate->Perm [j - 1]; - } while (kk < 0); - if (kk != j) { - do { - k = kk; - kk = fftstate->Perm [k - 1]; - fftstate->Perm [k - 1] = -kk; - } while (kk != j); - k3 = kk; - } else { - fftstate->Perm [j - 1] = -j; - if (j == nn) - break; /* exit infinite loop */ - } - } - max_factors *= inc; - /* reorder a and b, following the permutation cycles */ - for (;;) { - j = k3 + 1; - nt -= ispan; - ii = nt - inc + 1; - if (nt < 0) - break; /* exit infinite loop */ - do { - do { - j--; - } while (fftstate->Perm [j - 1] < 0); - jj = jc; - do { - kspan = jj; - if (jj > max_factors) { - kspan = max_factors; - } - jj -= kspan; - k = fftstate->Perm [j - 1]; - kk = jc * k + ii + jj; - k1 = kk + kspan - 1; - k2 = 0; - do { - k2++; - Rtmp [k2 - 1] = Re [k1]; - Itmp [k2 - 1] = Im [k1]; - k1 -= inc; - } while (k1 != (kk-1)); - do { - k1 = kk + kspan - 1; - k2 = k1 - jc * (k + fftstate->Perm [k - 1]); - k = -fftstate->Perm [k - 1]; - do { - Re [k1] = Re [k2]; - Im [k1] = Im [k2]; - k1 -= inc; - k2 -= inc; - } while (k1 != (kk-1)); - kk = k2 + 1; - } while (k != j); - k1 = kk + kspan - 1; - k2 = 0; - do { - k2++; - Re [k1] = Rtmp [k2 - 1]; - Im [k1] = Itmp [k2 - 1]; - k1 -= inc; - } while (k1 != (kk-1)); - } while (jj); - } while (j != 1); - } - return 0; /* exit point here */ -} -/* ---------------------- end-of-file (c source) ---------------------- */ - diff --git a/modules/audio_coding/codecs/iSAC/main/source/fft.h b/modules/audio_coding/codecs/iSAC/main/source/fft.h deleted file mode 100644 index a42f57bcb..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/fft.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/*--------------------------------*-C-*---------------------------------* - * File: - * fftn.h - * ---------------------------------------------------------------------* - * Re[]: real value array - * Im[]: imaginary value array - * nTotal: total number of complex values - * nPass: number of elements involved in this pass of transform - * nSpan: nspan/nPass = number of bytes to increment pointer - * in Re[] and Im[] - * isign: exponent: +1 = forward -1 = reverse - * scaling: normalizing constant by which the final result is *divided* - * scaling == -1, normalize by total dimension of the transform - * scaling < -1, normalize by the square-root of the total dimension - * - * ---------------------------------------------------------------------- - * See the comments in the code for correct usage! - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ - - -#include "structs.h" - - -/* double precision routine */ - - -int WebRtcIsac_Fftns (unsigned int ndim, const int dims[], double Re[], double Im[], - int isign, double scaling, FFTstr *fftstate); - - - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_FFT_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/filter_functions.c b/modules/audio_coding/codecs/iSAC/main/source/filter_functions.c deleted file mode 100644 index 31cd657ab..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/filter_functions.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#ifdef ANDROID -#include -#endif -#include "pitch_estimator.h" -#include "lpc_analysis.h" -#include "codec.h" - - - -void WebRtcIsac_AllPoleFilter(double *InOut, double *Coef, int lengthInOut, int orderCoef){ - - /* the state of filter is assumed to be in InOut[-1] to InOut[-orderCoef] */ - double scal; - double sum; - int n,k; - - //if (fabs(Coef[0]-1.0)<0.001) { - if ( (Coef[0] > 0.9999) && (Coef[0] < 1.0001) ) - { - for(n = 0; n < lengthInOut; n++) - { - sum = Coef[1] * InOut[-1]; - for(k = 2; k <= orderCoef; k++){ - sum += Coef[k] * InOut[-k]; - } - *InOut++ -= sum; - } - } - else - { - scal = 1.0 / Coef[0]; - for(n=0;nbuffer, sizeof(double) * PITCH_WLPCBUFLEN); - memcpy(tmpbuffer+PITCH_WLPCBUFLEN, in, sizeof(double) * PITCH_FRAME_LEN); - memcpy(wfdata->buffer, tmpbuffer+PITCH_FRAME_LEN, sizeof(double) * PITCH_WLPCBUFLEN); - - dp=weoutbuf; - dp2=whoutbuf; - for (k=0;kweostate[k]; - *dp2++ = wfdata->whostate[k]; - opol[k]=0.0; - } - opol[0]=1.0; - opol[PITCH_WLPCORDER]=0.0; - weo=dp; - who=dp2; - - endpos=PITCH_WLPCBUFLEN + PITCH_SUBFRAME_LEN; - inp=tmpbuffer + PITCH_WLPCBUFLEN; - - for (n=0; nwindow[k]*tmpbuffer[start+k]; - } - - /* Get LPC polynomial */ - WebRtcIsac_AutoCorr(corr, ext, PITCH_WLPCWINLEN, PITCH_WLPCORDER); - corr[0]=1.01*corr[0]+1.0; /* White noise correction */ - WebRtcIsac_LevDurb(apol, rc, corr, PITCH_WLPCORDER); - WebRtcIsac_BwExpand(apolr, apol, rho, PITCH_WLPCORDER+1); - - /* Filtering */ - WebRtcIsac_ZeroPoleFilter(inp, apol, apolr, PITCH_SUBFRAME_LEN, PITCH_WLPCORDER, weo); - WebRtcIsac_ZeroPoleFilter(inp, apolr, opol, PITCH_SUBFRAME_LEN, PITCH_WLPCORDER, who); - - inp+=PITCH_SUBFRAME_LEN; - endpos+=PITCH_SUBFRAME_LEN; - weo+=PITCH_SUBFRAME_LEN; - who+=PITCH_SUBFRAME_LEN; - } - - /* Export filter states */ - for (k=0;kweostate[k]=weoutbuf[PITCH_FRAME_LEN+k]; - wfdata->whostate[k]=whoutbuf[PITCH_FRAME_LEN+k]; - } - - /* Export output data */ - memcpy(weiout, weoutbuf+PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); - memcpy(whiout, whoutbuf+PITCH_WLPCORDER, sizeof(double) * PITCH_FRAME_LEN); -} - - -static const double APupper[ALLPASSSECTIONS] = {0.0347, 0.3826}; -static const double APlower[ALLPASSSECTIONS] = {0.1544, 0.744}; - - - -void WebRtcIsac_AllpassFilterForDec(double *InOut, - const double *APSectionFactors, - int lengthInOut, - double *FilterState) -{ - //This performs all-pass filtering--a series of first order all-pass sections are used - //to filter the input in a cascade manner. - int n,j; - double temp; - for (j=0; jINLABUFx arrays - each of length QLOOKAHEAD. - The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based - on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input - array in[]. - HP: a FRAMESAMPLES_HALF array of high-pass filtered samples that - have been phase equalized. The first QLOOKAHEAD samples are - based on the samples in the two prefiltdata->INLABUFx arrays - each of length QLOOKAHEAD. - The remaining FRAMESAMPLES_HALF-QLOOKAHEAD samples are based - on the first FRAMESAMPLES_HALF-QLOOKAHEAD samples of the input - array in[]. - - LP_la: a FRAMESAMPLES_HALF array of low-pass filtered samples. - These samples are not phase equalized. They are computed - from the samples in the in[] array. - HP_la: a FRAMESAMPLES_HALF array of high-pass filtered samples - that are not phase equalized. They are computed from - the in[] vector. - prefiltdata: this input data structure's filterbank state and - lookahead sample buffers are updated for the next - encoding iteration. -*/ -void WebRtcIsac_SplitAndFilterFloat(float *pin, float *LP, float *HP, - double *LP_la, double *HP_la, - PreFiltBankstr *prefiltdata) -{ - int k,n; - float CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; - float ForTransform_CompositeAPFilterState[NUMBEROFCOMPOSITEAPSECTIONS]; - float ForTransform_CompositeAPFilterState2[NUMBEROFCOMPOSITEAPSECTIONS]; - float tempinoutvec[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float tempin_ch1[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float tempin_ch2[FRAMESAMPLES+MAX_AR_MODEL_ORDER]; - float in[FRAMESAMPLES]; - float ftmp; - - - /* High pass filter */ - - for (k=0;kHPstates_float[0] + - kHpStCoefInFloat[3] * prefiltdata->HPstates_float[1]; - ftmp = pin[k] - kHpStCoefInFloat[0] * prefiltdata->HPstates_float[0] - - kHpStCoefInFloat[1] * prefiltdata->HPstates_float[1]; - prefiltdata->HPstates_float[1] = prefiltdata->HPstates_float[0]; - prefiltdata->HPstates_float[0] = ftmp; - } - - /* - % backwards all-pass filtering to obtain zero-phase - [tmp1(N2+LA:-1:LA+1, 1), state1] = filter(Q.coef, Q.coef(end:-1:1), in(N:-2:2)); - tmp1(LA:-1:1) = filter(Q.coef, Q.coef(end:-1:1), Q.LookAheadBuf1, state1); - Q.LookAheadBuf1 = in(N:-2:N-2*LA+2); - */ - /*Backwards all-pass filter the odd samples of the input (upper channel) - to eventually obtain zero phase. The composite all-pass filter (comprised of both - the upper and lower channel all-pass filsters in series) is used for the - filtering. */ - - /* First Channel */ - - /*initial state of composite filter is zero */ - for (k=0;kINLABUF1_float, - WebRtcIsac_kCompositeApFactorsFloat, QLOOKAHEAD, - NUMBEROFCOMPOSITEAPSECTIONS, CompositeAPFilterState); - - /* save the output, but write it in forward order */ - /* write the lookahead samples for the next encoding iteration. Every other - sample at the end of the input frame is written in reverse order for the - lookahead length. Exported in the prefiltdata structure. */ - for (k=0;kINLABUF1_float[k]; - prefiltdata->INLABUF1_float[k]=in[FRAMESAMPLES-1-2*k]; - } - - /* Second Channel. This is exactly like the first channel, except that the - even samples are now filtered instead (lower channel). */ - for (k=0;kINLABUF2_float, - WebRtcIsac_kCompositeApFactorsFloat, QLOOKAHEAD,NUMBEROFCOMPOSITEAPSECTIONS, - CompositeAPFilterState); - - for (k=0;kINLABUF2_float[k]; - prefiltdata->INLABUF2_float[k]=in[FRAMESAMPLES-2-2*k]; - } - - /* Transform filter states from backward to forward */ - /*At this point, each of the states of the backwards composite filters for the - two channels are transformed into forward filtering states for the corresponding - forward channel filters. Each channel's forward filtering state from the previous - encoding iteration is added to the transformed state to get a proper forward state */ - - /* So the existing NUMBEROFCOMPOSITEAPSECTIONS x 1 (4x1) state vector is multiplied by a - NUMBEROFCHANNELAPSECTIONSxNUMBEROFCOMPOSITEAPSECTIONS (2x4) transform matrix to get the - new state that is added to the previous 2x1 input state */ - - for (k=0;kINSTAT1_float[k] += ForTransform_CompositeAPFilterState[n]* - WebRtcIsac_kTransform1Float[k*NUMBEROFCHANNELAPSECTIONS+n]; - prefiltdata->INSTAT2_float[k] += ForTransform_CompositeAPFilterState2[n]* - WebRtcIsac_kTransform2Float[k*NUMBEROFCHANNELAPSECTIONS+n]; - } - } - - /*obtain polyphase components by forward all-pass filtering through each channel */ - /* the backward filtered samples are now forward filtered with the corresponding channel filters */ - /* The all pass filtering automatically updates the filter states which are exported in the - prefiltdata structure */ - WebRtcIsac_AllPassFilter2Float(tempin_ch1,WebRtcIsac_kUpperApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT1_float); - WebRtcIsac_AllPassFilter2Float(tempin_ch2,WebRtcIsac_kLowerApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTAT2_float); - - /* Now Construct low-pass and high-pass signals as combinations of polyphase components */ - for (k=0; kINSTATLA1_float); - WebRtcIsac_AllPassFilter2Float(tempin_ch2,WebRtcIsac_kLowerApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS, prefiltdata->INSTATLA2_float); - - for (k=0; kSTATE_0_UPPER_float); - - /* Now, all-pass filter the new lower channel signal. But since all-pass filter factors - at the decoder are swapped from the ones at the encoder, the 'upper' channel - all-pass filter factors (WebRtcIsac_kUpperApFactorsFloat) are used to filter this new - lower channel signal */ - WebRtcIsac_AllPassFilter2Float(tempin_ch2, WebRtcIsac_kUpperApFactorsFloat, - FRAMESAMPLES_HALF, NUMBEROFCHANNELAPSECTIONS,postfiltdata->STATE_0_LOWER_float); - - - /* Merge outputs to form the full length output signal.*/ - for (k=0;kHPstates1_float[0] + - kHpStCoefOut1Float[3] * postfiltdata->HPstates1_float[1]; - ftmp = Out[k] - kHpStCoefOut1Float[0] * postfiltdata->HPstates1_float[0] - - kHpStCoefOut1Float[1] * postfiltdata->HPstates1_float[1]; - postfiltdata->HPstates1_float[1] = postfiltdata->HPstates1_float[0]; - postfiltdata->HPstates1_float[0] = ftmp; - Out[k] = ftmp2; - } - - for (k=0;kHPstates2_float[0] + - kHpStCoefOut2Float[3] * postfiltdata->HPstates2_float[1]; - ftmp = Out[k] - kHpStCoefOut2Float[0] * postfiltdata->HPstates2_float[0] - - kHpStCoefOut2Float[1] * postfiltdata->HPstates2_float[1]; - postfiltdata->HPstates2_float[1] = postfiltdata->HPstates2_float[0]; - postfiltdata->HPstates2_float[0] = ftmp; - Out[k] = ftmp2; - } -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/intialize.c b/modules/audio_coding/codecs/iSAC/main/source/intialize.c deleted file mode 100644 index 6df034d1c..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/intialize.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* encode.c - Encoding function for the iSAC coder */ - -#include "structs.h" -#include "codec.h" -#include "pitch_estimator.h" - -#include - -void WebRtcIsac_InitMasking(MaskFiltstr *maskdata) { - - int k; - - for (k = 0; k < WINLEN; k++) { - maskdata->DataBufferLo[k] = 0.0; - maskdata->DataBufferHi[k] = 0.0; - } - for (k = 0; k < ORDERLO+1; k++) { - maskdata->CorrBufLo[k] = 0.0; - maskdata->PreStateLoF[k] = 0.0; - maskdata->PreStateLoG[k] = 0.0; - maskdata->PostStateLoF[k] = 0.0; - maskdata->PostStateLoG[k] = 0.0; - } - for (k = 0; k < ORDERHI+1; k++) { - maskdata->CorrBufHi[k] = 0.0; - maskdata->PreStateHiF[k] = 0.0; - maskdata->PreStateHiG[k] = 0.0; - maskdata->PostStateHiF[k] = 0.0; - maskdata->PostStateHiG[k] = 0.0; - } - - maskdata->OldEnergy = 10.0; - - /* fill tables for transforms */ - WebRtcIsac_InitTransform(); - - return; -} - -void WebRtcIsac_InitPreFilterbank(PreFiltBankstr *prefiltdata) -{ - int k; - - for (k = 0; k < QLOOKAHEAD; k++) { - prefiltdata->INLABUF1[k] = 0; - prefiltdata->INLABUF2[k] = 0; - - prefiltdata->INLABUF1_float[k] = 0; - prefiltdata->INLABUF2_float[k] = 0; - } - for (k = 0; k < 2*(QORDER-1); k++) { - prefiltdata->INSTAT1[k] = 0; - prefiltdata->INSTAT2[k] = 0; - prefiltdata->INSTATLA1[k] = 0; - prefiltdata->INSTATLA2[k] = 0; - - prefiltdata->INSTAT1_float[k] = 0; - prefiltdata->INSTAT2_float[k] = 0; - prefiltdata->INSTATLA1_float[k] = 0; - prefiltdata->INSTATLA2_float[k] = 0; - } - - /* High pass filter states */ - prefiltdata->HPstates[0] = 0.0; - prefiltdata->HPstates[1] = 0.0; - - prefiltdata->HPstates_float[0] = 0.0f; - prefiltdata->HPstates_float[1] = 0.0f; - - return; -} - -void WebRtcIsac_InitPostFilterbank(PostFiltBankstr *postfiltdata) -{ - int k; - - for (k = 0; k < 2*POSTQORDER; k++) { - postfiltdata->STATE_0_LOWER[k] = 0; - postfiltdata->STATE_0_UPPER[k] = 0; - - postfiltdata->STATE_0_LOWER_float[k] = 0; - postfiltdata->STATE_0_UPPER_float[k] = 0; - } - - /* High pass filter states */ - postfiltdata->HPstates1[0] = 0.0; - postfiltdata->HPstates1[1] = 0.0; - - postfiltdata->HPstates2[0] = 0.0; - postfiltdata->HPstates2[1] = 0.0; - - postfiltdata->HPstates1_float[0] = 0.0f; - postfiltdata->HPstates1_float[1] = 0.0f; - - postfiltdata->HPstates2_float[0] = 0.0f; - postfiltdata->HPstates2_float[1] = 0.0f; - - return; -} - - -void WebRtcIsac_InitPitchFilter(PitchFiltstr *pitchfiltdata) -{ - int k; - - for (k = 0; k < PITCH_BUFFSIZE; k++) { - pitchfiltdata->ubuf[k] = 0.0; - } - pitchfiltdata->ystate[0] = 0.0; - for (k = 1; k < (PITCH_DAMPORDER); k++) { - pitchfiltdata->ystate[k] = 0.0; - } - pitchfiltdata->oldlagp[0] = 50.0; - pitchfiltdata->oldgainp[0] = 0.0; -} - -void WebRtcIsac_InitWeightingFilter(WeightFiltstr *wfdata) -{ - int k; - double t, dtmp, dtmp2, denum, denum2; - - for (k=0;kbuffer[k]=0.0; - - for (k=0;kistate[k]=0.0; - wfdata->weostate[k]=0.0; - wfdata->whostate[k]=0.0; - } - - /* next part should be in Matlab, writing to a global table */ - t = 0.5; - denum = 1.0 / ((double) PITCH_WLPCWINLEN); - denum2 = denum * denum; - for (k=0;kwindow[k] = dtmp2 * dtmp2; - t++; - } -} - -/* clear all buffers */ -void WebRtcIsac_InitPitchAnalysis(PitchAnalysisStruct *State) -{ - int k; - - for (k = 0; k < PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2; k++) - State->dec_buffer[k] = 0.0; - for (k = 0; k < 2*ALLPASSSECTIONS+1; k++) - State->decimator_state[k] = 0.0; - for (k = 0; k < 2; k++) - State->hp_state[k] = 0.0; - for (k = 0; k < QLOOKAHEAD; k++) - State->whitened_buf[k] = 0.0; - for (k = 0; k < QLOOKAHEAD; k++) - State->inbuf[k] = 0.0; - - WebRtcIsac_InitPitchFilter(&(State->PFstr_wght)); - - WebRtcIsac_InitPitchFilter(&(State->PFstr)); - - WebRtcIsac_InitWeightingFilter(&(State->Wghtstr)); -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/isac.c b/modules/audio_coding/codecs/iSAC/main/source/isac.c deleted file mode 100644 index 3e364bcf5..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/isac.c +++ /dev/null @@ -1,2814 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * isac.c - * - * This C file contains the functions for the ISAC API - * - */ - -#include "isac.h" -#include "bandwidth_estimator.h" -#include "crc.h" -#include "entropy_coding.h" -#include "codec.h" -#include "structs.h" -#include "signal_processing_library.h" -#include "lpc_shape_swb16_tables.h" - -#include -#include -#include -#include - -#define BIT_MASK_DEC_INIT 0x0001 -#define BIT_MASK_ENC_INIT 0x0002 - -#define LEN_CHECK_SUM_WORD8 4 -#define MAX_NUM_LAYERS 10 - - -/**************************************************************************** - * UpdatePayloadSizeLimit() - * - * Call this function to update the limit on the payload size. The limit on - * payload size might change i) if a user ''directly changes the limit by - * calling xxx_setMaxPayloadSize() or xxx_setMaxRate(), or ii) indirectly - * when bandwidth is changing. The latter might be the result of bandwidth - * adaptation, or direct change of the bottleneck in instantaneous mode. - * - * This function takes the current overall limit on payload, and translate it - * to the limits on lower and upper-band. If the codec is in wideband mode - * then the overall limit and the limit on the lower-band is the same. - * Otherwise, a fraction of the limit should be allocated to lower-band - * leaving some room for the upper-band bit-stream. That is why an update - * of limit is required every time that the bandwidth is changing. - * - */ -static void UpdatePayloadSizeLimit( - ISACMainStruct *instISAC) -{ - WebRtc_Word16 lim30MsPayloadBytes; - WebRtc_Word16 lim60MsPayloadBytes; - - lim30MsPayloadBytes = WEBRTC_SPL_MIN( - (instISAC->maxPayloadSizeBytes), - (instISAC->maxRateBytesPer30Ms)); - - lim60MsPayloadBytes = WEBRTC_SPL_MIN( - (instISAC->maxPayloadSizeBytes), - (instISAC->maxRateBytesPer30Ms << 1)); - - // The only time that iSAC will have 60 ms - // frame-size is when operating in wideband so - // there is no upper-band bit-stream - - if(instISAC->bandwidthKHz == isac8kHz) - { - // at 8 kHz there is no upper-band bit-stream - // therefore the lower-band limit is as the overall - // limit. - instISAC->instLB.ISACencLB_obj.payloadLimitBytes60 = - lim60MsPayloadBytes; - instISAC->instLB.ISACencLB_obj.payloadLimitBytes30 = - lim30MsPayloadBytes; - } - else - { - // when in super-wideband, we only have 30 ms frames - // Do a rate allocation for the given limit. - if(lim30MsPayloadBytes > 250) - { - // 4/5 to lower-band the rest for upper-band - instISAC->instLB.ISACencLB_obj.payloadLimitBytes30 = - (lim30MsPayloadBytes << 2) / 5; - } - else if(lim30MsPayloadBytes > 200) - { - // for the interval of 200 to 250 the share of - // upper-band linearly grows from 20 to 50; - instISAC->instLB.ISACencLB_obj.payloadLimitBytes30 = - (lim30MsPayloadBytes << 1) / 5 + 100; - } - else - { - // allocate only 20 for upper-band - instISAC->instLB.ISACencLB_obj.payloadLimitBytes30 = - lim30MsPayloadBytes - 20; - } - instISAC->instUB.ISACencUB_obj.maxPayloadSizeBytes = - lim30MsPayloadBytes; - } -} - - -/**************************************************************************** - * UpdateBottleneck() - * - * This function updates the bottleneck only if the codec is operating in - * channel-adaptive mode. Furthermore, as the update of bottleneck might - * result in an update of bandwidth, therefore, the bottlenech should be - * updated just right before the first 10ms of a frame is pushed into encoder. - * - */ -static void UpdateBottleneck( - ISACMainStruct *instISAC) -{ - // read the bottleneck from bandwidth estimator for the - // first 10 ms audio. This way, if there is a change - // in bandwidth upper and lower-band will be in sync. - if((instISAC->codingMode == 0) && - (instISAC->instLB.ISACencLB_obj.buffer_index == 0) && - (instISAC->instLB.ISACencLB_obj.frame_nb == 0)) - { - WebRtc_Word32 bottleneck; - WebRtcIsac_GetUplinkBandwidth(&(instISAC->bwestimator_obj), - &bottleneck); - - // Adding hysteresis when increasing signal bandwidth - if((instISAC->bandwidthKHz == isac8kHz) - && (bottleneck > 37000) - && (bottleneck < 41000)) - { - bottleneck = 37000; - } - - // switching from 12 kHz to 16 kHz is not allowed at this revision - // If we let this happen, we have to take care of buffer_index and - // the last LPC vector. - if((instISAC->bandwidthKHz != isac16kHz) && - (bottleneck > 46000)) - { - bottleneck = 46000; - } - - // we might need a rate allocation. - if(instISAC->encoderSamplingRateKHz == kIsacWideband) - { - // wideband is the only choise we have here. - instISAC->instLB.ISACencLB_obj.bottleneck = - (bottleneck > 32000)? 32000:bottleneck; - instISAC->bandwidthKHz = isac8kHz; - } - else - { - // do the rate-allosation and get the new bandwidth. - enum ISACBandwidth bandwidth; - WebRtcIsac_RateAllocation(bottleneck, - &(instISAC->instLB.ISACencLB_obj.bottleneck), - &(instISAC->instUB.ISACencUB_obj.bottleneck), - &bandwidth); - if(bandwidth != isac8kHz) - { - instISAC->instLB.ISACencLB_obj.new_framelength = 480; - } - if(bandwidth != instISAC->bandwidthKHz) - { - // bandwidth is changing. - instISAC->bandwidthKHz = bandwidth; - UpdatePayloadSizeLimit(instISAC); - if(bandwidth == isac12kHz) - { - instISAC->instLB.ISACencLB_obj.buffer_index = 0; - } - // currently we don't let the bandwidth to switch to 16 kHz - // if in adaptive mode. If we let this happen, we have to take - // car of buffer_index and the last LPC vector. - } - } - } -} - - -/**************************************************************************** - * GetSendBandwidthInfo() - * - * This is called to get the bandwidth info. This info is the bandwidth and - * and the jitter of 'there-to-here' channel, estimated 'here.' These info - * is signaled in an in-band fashion to the otherside. - * - * The call to the bandwidth estimator trigers a recursive averaging which - * has to be synchronized between encoder & decoder, therefore. The call to - * BWE should be once per packet. As the BWE info is inserted into bit-stream - * we need a valid info right before the encodeLB function is going to - * generating a bit-stream. That is when lower-band buffer has already 20ms - * of audio, and the 3rd block of 10ms is going to be injected into encoder. - * - * Inputs: - * - instISAC : iSAC instance. - * - * Outputs: - * - bandwidthIndex : an index which has to be encoded in - * lower-band bit-stream, indicating the - * bandwidth of there-to-here channel. - * - jitterInfo : this indicates if the jitter is high - * or low and it is encoded in upper-band - * bit-stream. - * - */ -static void GetSendBandwidthInfo( - ISACMainStruct* instISAC, - WebRtc_Word16* bandwidthIndex, - WebRtc_Word16* jitterInfo) -{ - if((instISAC->instLB.ISACencLB_obj.buffer_index == - (FRAMESAMPLES_10ms << 1)) && - (instISAC->instLB.ISACencLB_obj.frame_nb == 0)) - { - /* bandwidth estimation and coding */ - WebRtcIsac_GetDownlinkBwJitIndexImpl(&(instISAC->bwestimator_obj), - bandwidthIndex, jitterInfo, instISAC->decoderSamplingRateKHz); - } -} - - -/**************************************************************************** - * WebRtcIsac_AssignSize(...) - * - * This function returns the size of the ISAC instance, so that the instance - * can be created out side iSAC. - * - * Output: - * - sizeinbytes : number of bytes needed to allocate for the - * instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_AssignSize( - int *sizeInBytes) -{ - *sizeInBytes = sizeof(ISACMainStruct) * 2 / sizeof(WebRtc_Word16); - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_Assign(...) - * - * This function assignes the memory already created to the ISAC instance. - * - * Input: - * - ISAC_main_inst : address of the pointer to the coder instance. - * - instISAC_Addr : the already allocaded memeory, where we put the - * iSAC struct - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_Assign( - ISACStruct** ISAC_main_inst, - void* instISAC_Addr) -{ - if(instISAC_Addr != NULL) - { - ISACMainStruct* instISAC = (ISACMainStruct*)instISAC_Addr; - instISAC->errorCode = 0; - instISAC->initFlag = 0; - - // Assign the address - *ISAC_main_inst = (ISACStruct*)instISAC_Addr; - - // Default is wideband. - instISAC->encoderSamplingRateKHz = kIsacWideband; - instISAC->decoderSamplingRateKHz = kIsacWideband; - instISAC->bandwidthKHz = isac8kHz; - return 0; - } - else - { - return -1; - } -} - - -/**************************************************************************** - * WebRtcIsac_Create(...) - * - * This function creates an ISAC instance, which will contain the state - * information for one coding/decoding channel. - * - * Input: - * - ISAC_main_inst : address of the pointer to the coder instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_Create( - ISACStruct** ISAC_main_inst) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)WEBRTC_SPL_VNEW(ISACMainStruct, 1); - *ISAC_main_inst = (ISACStruct*)instISAC; - if(*ISAC_main_inst != NULL) - { - instISAC->errorCode = 0; - instISAC->initFlag = 0; - // Default is wideband - instISAC->bandwidthKHz = isac8kHz; - instISAC->encoderSamplingRateKHz = kIsacWideband; - instISAC->decoderSamplingRateKHz = kIsacWideband; - return 0; - } - else - { - return -1; - } -} - - -/**************************************************************************** - * WebRtcIsac_Free(...) - * - * This function frees the ISAC instance created at the beginning. - * - * Input: - * - ISAC_main_inst : a ISAC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_Free( - ISACStruct* ISAC_main_inst) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - WEBRTC_SPL_FREE(instISAC); - return 0; -} - - -/**************************************************************************** - * EncoderInitLb(...) - internal function for initialization of - * Lower Band - * EncoderInitUb(...) - internal function for initialization of - * Upper Band - * WebRtcIsac_EncoderInit(...) - API function - * - * This function initializes a ISAC instance prior to the encoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - CodingMode : 0 -> Bit rate and frame length are automatically - * adjusted to available bandwidth on - * transmission channel, applicable just to - * wideband mode. - * 1 -> User sets a frame length and a target bit - * rate which is taken as the maximum - * short-term average bit rate. - * - * Return value : 0 - Ok - * -1 - Error - */ -static WebRtc_Word16 EncoderInitLb( - ISACLBStruct* instLB, - WebRtc_Word16 codingMode, - enum IsacSamplingRate sampRate) -{ - WebRtc_Word16 statusInit = 0; - int k; - - /* Init stream vector to zero */ - for(k=0; k < STREAM_SIZE_MAX_60; k++) - { - instLB->ISACencLB_obj.bitstr_obj.stream[k] = 0; - } - - if((codingMode == 1) || (sampRate == kIsacSuperWideband)) - { - // 30 ms frame-size if either in super-wideband or - // instanteneous mode (I-mode) - instLB->ISACencLB_obj.new_framelength = 480; - } - else - { - instLB->ISACencLB_obj.new_framelength = INITIAL_FRAMESAMPLES; - } - - WebRtcIsac_InitMasking(&instLB->ISACencLB_obj.maskfiltstr_obj); - WebRtcIsac_InitPreFilterbank(&instLB->ISACencLB_obj.prefiltbankstr_obj); - WebRtcIsac_InitPitchFilter(&instLB->ISACencLB_obj.pitchfiltstr_obj); - WebRtcIsac_InitPitchAnalysis( - &instLB->ISACencLB_obj.pitchanalysisstr_obj); - - - instLB->ISACencLB_obj.buffer_index = 0; - instLB->ISACencLB_obj.frame_nb = 0; - /* default for I-mode */ - instLB->ISACencLB_obj.bottleneck = 32000; - instLB->ISACencLB_obj.current_framesamples = 0; - instLB->ISACencLB_obj.s2nr = 0; - instLB->ISACencLB_obj.payloadLimitBytes30 = STREAM_SIZE_MAX_30; - instLB->ISACencLB_obj.payloadLimitBytes60 = STREAM_SIZE_MAX_60; - instLB->ISACencLB_obj.maxPayloadBytes = STREAM_SIZE_MAX_60; - instLB->ISACencLB_obj.maxRateInBytes = STREAM_SIZE_MAX_30; - instLB->ISACencLB_obj.enforceFrameSize = 0; - /* invalid value prevents getRedPayload to - run before encoder is called */ - instLB->ISACencLB_obj.lastBWIdx = -1; - return statusInit; -} - -static WebRtc_Word16 EncoderInitUb( - ISACUBStruct* instUB, - WebRtc_Word16 bandwidth) -{ - WebRtc_Word16 statusInit = 0; - int k; - - /* Init stream vector to zero */ - for(k = 0; k < STREAM_SIZE_MAX_60; k++) - { - instUB->ISACencUB_obj.bitstr_obj.stream[k] = 0; - } - - WebRtcIsac_InitMasking(&instUB->ISACencUB_obj.maskfiltstr_obj); - WebRtcIsac_InitPreFilterbank(&instUB->ISACencUB_obj.prefiltbankstr_obj); - - if(bandwidth == isac16kHz) - { - instUB->ISACencUB_obj.buffer_index = LB_TOTAL_DELAY_SAMPLES; - } - else - { - instUB->ISACencUB_obj.buffer_index = 0; - } - /* default for I-mode */ - instUB->ISACencUB_obj.bottleneck = 32000; - // These store the limits for the wideband + super-wideband bit-stream. - instUB->ISACencUB_obj.maxPayloadSizeBytes = STREAM_SIZE_MAX_30 << 1; - // This has to be updated after each lower-band encoding to guarantee - // a correct payload-limitation. - instUB->ISACencUB_obj.numBytesUsed = 0; - memset(instUB->ISACencUB_obj.data_buffer_float, 0, - (MAX_FRAMESAMPLES + LB_TOTAL_DELAY_SAMPLES) * sizeof(float)); - - memcpy(&(instUB->ISACencUB_obj.lastLPCVec), - meanLARUB16, sizeof(double) * UB_LPC_ORDER); - - return statusInit; -} - - -WebRtc_Word16 WebRtcIsac_EncoderInit( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 codingMode) -{ - ISACMainStruct *instISAC; - WebRtc_Word16 status; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if((codingMode != 0) && (codingMode != 1)) - { - instISAC->errorCode = ISAC_DISALLOWED_CODING_MODE; - return -1; - } - // default bottleneck - instISAC->bottleneck = MAX_ISAC_BW; - - if(instISAC->encoderSamplingRateKHz == kIsacWideband) - { - instISAC->maxPayloadSizeBytes = STREAM_SIZE_MAX_60; - instISAC->maxRateBytesPer30Ms = STREAM_SIZE_MAX_30; - } - else - { - instISAC->maxPayloadSizeBytes = STREAM_SIZE_MAX; - instISAC->maxRateBytesPer30Ms = STREAM_SIZE_MAX; - } - - // Channel-adaptive = 0; Instantaneous (Channel-independent) = 1; - instISAC->codingMode = codingMode; - - WebRtcIsac_InitBandwidthEstimator(&instISAC->bwestimator_obj, - instISAC->encoderSamplingRateKHz, - instISAC->decoderSamplingRateKHz); - - WebRtcIsac_InitRateModel(&instISAC->rate_data_obj); - /* default for I-mode */ - instISAC->MaxDelay = 10.0; - - status = EncoderInitLb(&instISAC->instLB, codingMode, - instISAC->encoderSamplingRateKHz); - if(status < 0) - { - instISAC->errorCode = -status; - return -1; - } - - if(instISAC->encoderSamplingRateKHz == kIsacSuperWideband) - { - // Initialize encoder filter-bank. - memset(instISAC->analysisFBState1, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - memset(instISAC->analysisFBState2, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - - status = EncoderInitUb(&(instISAC->instUB), - instISAC->bandwidthKHz); - if(status < 0) - { - instISAC->errorCode = -status; - return -1; - } - } - // Initializtion is successful, set the flag - instISAC->initFlag |= BIT_MASK_ENC_INIT; - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_Encode(...) - * - * This function encodes 10ms frame(s) and inserts it into a package. - * Input speech length has to be 160 samples (10ms). The encoder buffers those - * 10ms frames until it reaches the chosen Framesize (480 or 960 samples - * corresponding to 30 or 60 ms frames), and then proceeds to the encoding. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - speechIn : input speech vector. - * - * Output: - * - encoded : the encoded data vector - * - * Return value: - * : >0 - Length (in bytes) of coded data - * : 0 - The buffer didn't reach the chosen - * frameSize so it keeps buffering speech - * samples. - * : -1 - Error - */ -WebRtc_Word16 WebRtcIsac_Encode( - ISACStruct* ISAC_main_inst, - const WebRtc_Word16* speechIn, - WebRtc_Word16* encoded) -{ - ISACMainStruct* instISAC; - ISACLBStruct* instLB; - ISACUBStruct* instUB; - - float inFrame[FRAMESAMPLES_10ms]; - WebRtc_Word16 speechInLB[FRAMESAMPLES_10ms]; - WebRtc_Word16 speechInUB[FRAMESAMPLES_10ms]; - WebRtc_Word16 streamLenLB; - WebRtc_Word16 streamLenUB; - WebRtc_Word16 streamLen; - WebRtc_Word16 k; - WebRtc_UWord8* ptrEncodedUW8 = (WebRtc_UWord8*)encoded; - int garbageLen; - WebRtc_Word32 bottleneck; - WebRtc_Word16 bottleneckIdx = 0; - WebRtc_Word16 jitterInfo = 0; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - instLB = &(instISAC->instLB); - instUB = &(instISAC->instUB); - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - if(instISAC->encoderSamplingRateKHz == kIsacSuperWideband) - { - WebRtcSpl_AnalysisQMF(speechIn, speechInLB, speechInUB, - instISAC->analysisFBState1, instISAC->analysisFBState2); - - /* convert from fixed to floating point */ - for(k = 0; k < FRAMESAMPLES_10ms; k++) - { - inFrame[k] = (float)speechInLB[k]; - } - } - else - { - for(k = 0; k < FRAMESAMPLES_10ms; k++) - { - inFrame[k] = (float) speechIn[k]; - } - } - - /* add some noise to avoid denormal numbers */ - inFrame[0] += (float)1.23455334e-3; - inFrame[1] -= (float)2.04324239e-3; - inFrame[2] += (float)1.90854954e-3; - inFrame[9] += (float)1.84854878e-3; - - - // This function will update the bottleneck if required - UpdateBottleneck(instISAC); - - // Get the bandwith information which has to be sent to the other side - GetSendBandwidthInfo(instISAC, &bottleneckIdx, &jitterInfo); - - // - // ENCODE LOWER-BAND - // - streamLenLB = WebRtcIsac_EncodeLb(inFrame, &instLB->ISACencLB_obj, - instISAC->codingMode, bottleneckIdx); - - if(streamLenLB < 0) - { - return -1; - } - - if(instISAC->encoderSamplingRateKHz == kIsacSuperWideband) - { - instUB = &(instISAC->instUB); - - // convert to float - for(k = 0; k < FRAMESAMPLES_10ms; k++) - { - inFrame[k] = (float) speechInUB[k]; - } - - /* add some noise to avoid denormal numbers */ - inFrame[0] += (float)1.23455334e-3; - inFrame[1] -= (float)2.04324239e-3; - inFrame[2] += (float)1.90854954e-3; - inFrame[9] += (float)1.84854878e-3; - - // Tell to upper-band the number of bytes used so far. - // This is for payload limitation. - instUB->ISACencUB_obj.numBytesUsed = streamLenLB + 1 + - LEN_CHECK_SUM_WORD8; - - // - // ENCODE UPPER-BAND - // - switch(instISAC->bandwidthKHz) - { - case isac12kHz: - { - streamLenUB = WebRtcIsac_EncodeUb12(inFrame, - &instUB->ISACencUB_obj, - jitterInfo); - break; - } - case isac16kHz: - { - streamLenUB = WebRtcIsac_EncodeUb16(inFrame, - &instUB->ISACencUB_obj, - jitterInfo); - break; - } - case isac8kHz: - { - streamLenUB = 0; - break; - } - default: - return -1; - } - - if((streamLenUB < 0) && - (streamLenUB != -ISAC_PAYLOAD_LARGER_THAN_LIMIT)) - { - // an error has happened but this is not the error due to a - // bit-stream larger than the limit - return -1; - } - - if(streamLenLB == 0) - { - return 0; - } - - // One bite is allocated for the length. According to older decoders - // so the length bit-stream plus one byte for size and - // LEN_CHECK_SUM_WORD8 for the checksum should be less than or equal - // to 255. - if((streamLenUB > (255 - (LEN_CHECK_SUM_WORD8 + 1))) || - (streamLenUB == -ISAC_PAYLOAD_LARGER_THAN_LIMIT)) - { - // we have got a too long bit-stream we skip the upper-band - // bit-stream for this frame. - streamLenUB = 0; - } - - memcpy(ptrEncodedUW8, instLB->ISACencLB_obj.bitstr_obj.stream, - streamLenLB); - streamLen = streamLenLB; - if(streamLenUB > 0) - { - ptrEncodedUW8[streamLenLB] = (WebRtc_UWord8)(streamLenUB + 1 + - LEN_CHECK_SUM_WORD8); - memcpy(&ptrEncodedUW8[streamLenLB + 1], - instUB->ISACencUB_obj.bitstr_obj.stream, streamLenUB); - streamLen += ptrEncodedUW8[streamLenLB]; - } - else - { - ptrEncodedUW8[streamLenLB] = 0; - } - } - else - { - if(streamLenLB == 0) - { - return 0; - } - memcpy(ptrEncodedUW8, instLB->ISACencLB_obj.bitstr_obj.stream, - streamLenLB); - streamLenUB = 0; - streamLen = streamLenLB; - } - - // Add Garbage if required. - WebRtcIsac_GetUplinkBandwidth(&instISAC->bwestimator_obj, &bottleneck); - if(instISAC->codingMode == 0) - { - int minBytes; - int limit; - WebRtc_UWord8* ptrGarbage; - - instISAC->MaxDelay = (double)WebRtcIsac_GetUplinkMaxDelay( - &instISAC->bwestimator_obj); - - /* update rate model and get minimum number of bytes in this packet */ - minBytes = WebRtcIsac_GetMinBytes(&(instISAC->rate_data_obj), - streamLen, instISAC->instLB.ISACencLB_obj.current_framesamples, - bottleneck, instISAC->MaxDelay, instISAC->bandwidthKHz); - - /* Make sure MinBytes does not exceed packet size limit */ - if(instISAC->bandwidthKHz == isac8kHz) - { - if(instLB->ISACencLB_obj.current_framesamples == FRAMESAMPLES) - { - limit = instLB->ISACencLB_obj.payloadLimitBytes30; - } - else - { - limit = instLB->ISACencLB_obj.payloadLimitBytes60; - } - } - else - { - limit = instUB->ISACencUB_obj.maxPayloadSizeBytes; - } - minBytes = (minBytes > limit)? limit:minBytes; - - /* Make sure we don't allow more than 255 bytes of garbage data. - We store the length of the garbage data in 8 bits in the bitstream, - 255 is the max garbage length we can signal using 8 bits. */ - if((instISAC->bandwidthKHz == isac8kHz) || - (streamLenUB == 0)) - { - ptrGarbage = &ptrEncodedUW8[streamLenLB]; - limit = streamLen + 255; - } - else - { - ptrGarbage = &ptrEncodedUW8[streamLenLB + 1 + streamLenUB]; - limit = streamLen + (255 - ptrEncodedUW8[streamLenLB]); - } - minBytes = (minBytes > limit)? limit:minBytes; - - garbageLen = (minBytes > streamLen)? (minBytes - streamLen):0; - - /* Save data for creation of multiple bitstreams */ - //ISACencLB_obj->SaveEnc_obj.minBytes = MinBytes; - - /* if bitstream is too short, add garbage at the end */ - if(garbageLen > 0) - { - for(k = 0; k < garbageLen; k++) - { - ptrGarbage[k] = (WebRtc_UWord8)(rand() & 0xFF); - } - - // for a correct length of the upper-band bit-stream together - // with the garbage. Garbage is embeded in upper-band bit-stream. - // That is the only way to preserve backward compatibility. - if((instISAC->bandwidthKHz == isac8kHz) || - (streamLenUB == 0)) - { - ptrEncodedUW8[streamLenLB] = (WebRtc_UWord8)garbageLen; - } - else - { - ptrEncodedUW8[streamLenLB] += (WebRtc_UWord8)garbageLen; - // write the length of the garbage at the end of the upper-band - // bit-stream, if exists. This helps for sanity check. - ptrEncodedUW8[streamLenLB + 1 + streamLenUB] = (WebRtc_UWord8)garbageLen; - - } - - streamLen += garbageLen; - } - } - else - { - /* update rate model */ - WebRtcIsac_UpdateRateModel(&instISAC->rate_data_obj, streamLen, - instISAC->instLB.ISACencLB_obj.current_framesamples, bottleneck); - garbageLen = 0; - } - - // Generate CRC if required. - if((instISAC->bandwidthKHz != isac8kHz) && - (streamLenUB > 0)) - { - WebRtc_UWord32 crc; - - WebRtcIsac_GetCrc((WebRtc_Word16*)(&(ptrEncodedUW8[streamLenLB + 1])), - streamLenUB + garbageLen, &crc); -#ifndef WEBRTC_BIG_ENDIAN - for(k = 0; k < LEN_CHECK_SUM_WORD8; k++) - { - ptrEncodedUW8[streamLen - LEN_CHECK_SUM_WORD8 + k] = - (WebRtc_UWord8)((crc >> (24 - k * 8)) & 0xFF); - } -#else - memcpy(&ptrEncodedUW8[streamLenLB + streamLenUB + 1], &crc, - LEN_CHECK_SUM_WORD8); -#endif - } - - return streamLen; -} - - -/****************************************************************************** - * WebRtcIsac_GetNewBitStream(...) - * - * This function returns encoded data, with the recieved bwe-index in the - * stream. If the rate is set to a value less than bottleneck of codec - * the new bistream will be re-encoded with the given target rate. - * It should always return a complete packet, i.e. only called once - * even for 60 msec frames. - * - * NOTE 1! This function does not write in the ISACStruct, it is not allowed. - * NOTE 3! Rates larger than the bottleneck of the codec will be limited - * to the current bottleneck. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - bweIndex : Index of bandwidth estimate to put in new - * bitstream - * - rate : target rate of the transcoder is bits/sec. - * Valid values are the accepted rate in iSAC, - * i.e. 10000 to 56000. - * - * Output: - * - encoded : The encoded data vector - * - * Return value : >0 - Length (in bytes) of coded data - * -1 - Error or called in SWB mode - * NOTE! No error code is written to - * the struct since it is only allowed to read - * the struct. - */ -WebRtc_Word16 WebRtcIsac_GetNewBitStream( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 bweIndex, - WebRtc_Word16 jitterInfo, - WebRtc_Word32 rate, - WebRtc_Word16* encoded, - WebRtc_Word16 isRCU) -{ - Bitstr iSACBitStreamInst; /* Local struct for bitstream handling */ - WebRtc_Word16 streamLenLB; - WebRtc_Word16 streamLenUB; - WebRtc_Word16 totalStreamLen; - double gain2; - double gain1; - float scale; - enum ISACBandwidth bandwidthKHz; - double rateLB; - double rateUB; - WebRtc_Word32 currentBN; - ISACMainStruct* instISAC; - WebRtc_UWord8* encodedPtrUW8 = (WebRtc_UWord8*)encoded; - WebRtc_UWord32 crc; -#ifndef WEBRTC_BIG_ENDIAN - WebRtc_Word16 k; -#endif - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - return -1; - } - - // Get the bottleneck of this iSAC and limit the - // given rate to the current bottleneck. - WebRtcIsac_GetUplinkBw(ISAC_main_inst, ¤tBN); - if(rate > currentBN) - { - rate = currentBN; - } - - if(WebRtcIsac_RateAllocation(rate, &rateLB, &rateUB, &bandwidthKHz) < 0) - { - return -1; - } - - // Cannot transcode from 16 kHz to 12 kHz - if((bandwidthKHz == isac12kHz) && - (instISAC->bandwidthKHz == isac16kHz)) - { - return -1; - } - - // These gains are in dB - // gain for the given rate. - gain1 = WebRtcIsac_GetSnr(rateLB, - instISAC->instLB.ISACencLB_obj.current_framesamples); - // gain of this iSAC - gain2 = WebRtcIsac_GetSnr( - instISAC->instLB.ISACencLB_obj.bottleneck, - instISAC->instLB.ISACencLB_obj.current_framesamples); - - // scale is the ratio of two gains in normal domain. - scale = (float)pow(10, (gain1 - gain2) / 20.0); - // change the scale if this is a RCU bit-stream. - scale = (isRCU)? (scale * RCU_TRANSCODING_SCALE):scale; - - streamLenLB = WebRtcIsac_EncodeStoredDataLb( - &instISAC->instLB.ISACencLB_obj.SaveEnc_obj, &iSACBitStreamInst, - bweIndex, scale); - - if(streamLenLB < 0) - { - return -1; - } - - /* convert from bytes to WebRtc_Word16 */ - memcpy(encoded, iSACBitStreamInst.stream, streamLenLB); - - if(bandwidthKHz == isac8kHz) - { - return streamLenLB; - } - - totalStreamLen = streamLenLB; - // super-wideband is always at 30ms. - // These gains are in dB - // gain for the given rate. - gain1 = WebRtcIsac_GetSnr(rateUB, FRAMESAMPLES); - // gain of this iSAC - gain2 = WebRtcIsac_GetSnr( - instISAC->instUB.ISACencUB_obj.bottleneck, FRAMESAMPLES); - - // scale is the ratio of two gains in normal domain. - scale = (float)pow(10, (gain1 - gain2) / 20.0); - - // change the scale if this is a RCU bit-stream. - scale = (isRCU)? (scale * RCU_TRANSCODING_SCALE_UB):scale; - - switch(instISAC->bandwidthKHz) - { - case isac12kHz: - { - streamLenUB = WebRtcIsac_EncodeStoredDataUb12( - &(instISAC->instUB.ISACencUB_obj.SaveEnc_obj), - &iSACBitStreamInst, jitterInfo, scale); - break; - } - case isac16kHz: - { - streamLenUB = WebRtcIsac_EncodeStoredDataUb16( - &(instISAC->instUB.ISACencUB_obj.SaveEnc_obj), - &iSACBitStreamInst, jitterInfo, scale); - break; - } - default: - return -1; - } - - if(streamLenUB < 0) - { - return -1; - } - - if(streamLenUB + 1 + LEN_CHECK_SUM_WORD8 > 255) - { - return streamLenLB; - } - - totalStreamLen = streamLenLB + streamLenUB + 1 + LEN_CHECK_SUM_WORD8; - encodedPtrUW8[streamLenLB] = streamLenUB + 1 + LEN_CHECK_SUM_WORD8; - - memcpy(&encodedPtrUW8[streamLenLB+1], iSACBitStreamInst.stream, - streamLenUB); - - WebRtcIsac_GetCrc((WebRtc_Word16*)(&(encodedPtrUW8[streamLenLB + 1])), - streamLenUB, &crc); -#ifndef WEBRTC_BIG_ENDIAN - for(k = 0; k < LEN_CHECK_SUM_WORD8; k++) - { - encodedPtrUW8[totalStreamLen - LEN_CHECK_SUM_WORD8 + k] = - (WebRtc_UWord8)((crc >> (24 - k * 8)) & 0xFF); - } -#else - memcpy(&encodedPtrUW8[streamLenLB + streamLenUB + 1], &crc, - LEN_CHECK_SUM_WORD8); -#endif - - - return totalStreamLen; -} - - -/**************************************************************************** - * DecoderInitLb(...) - internal function for initialization of - * Lower Band - * DecoderInitUb(...) - internal function for initialization of - * Upper Band - * WebRtcIsac_DecoderInit(...) - API function - * - * This function initializes a ISAC instance prior to the decoder calls. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - * Return value - * : 0 - Ok - * -1 - Error - */ -static WebRtc_Word16 DecoderInitLb( - ISACLBStruct* instISAC) -{ - int i; - /* Init stream vector to zero */ - for (i=0; iISACdecLB_obj.bitstr_obj.stream[i] = 0; - } - - WebRtcIsac_InitMasking(&instISAC->ISACdecLB_obj.maskfiltstr_obj); - WebRtcIsac_InitPostFilterbank( - &instISAC->ISACdecLB_obj.postfiltbankstr_obj); - WebRtcIsac_InitPitchFilter(&instISAC->ISACdecLB_obj.pitchfiltstr_obj); - - return (0); -} - -static WebRtc_Word16 DecoderInitUb( - ISACUBStruct* instISAC) -{ - int i; - /* Init stream vector to zero */ - for (i = 0; i < STREAM_SIZE_MAX_60; i++) - { - instISAC->ISACdecUB_obj.bitstr_obj.stream[i] = 0; - } - - WebRtcIsac_InitMasking(&instISAC->ISACdecUB_obj.maskfiltstr_obj); - WebRtcIsac_InitPostFilterbank( - &instISAC->ISACdecUB_obj.postfiltbankstr_obj); - return (0); -} - -WebRtc_Word16 WebRtcIsac_DecoderInit( - ISACStruct *ISAC_main_inst) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if(DecoderInitLb(&instISAC->instLB) < 0) - { - return -1; - } - - if(instISAC->decoderSamplingRateKHz == kIsacSuperWideband) - { - memset(instISAC->synthesisFBState1, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - memset(instISAC->synthesisFBState2, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - - if(DecoderInitUb(&(instISAC->instUB)) < 0) - { - return -1; - } - } - - if((instISAC->initFlag && BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - WebRtcIsac_InitBandwidthEstimator(&instISAC->bwestimator_obj, - instISAC->encoderSamplingRateKHz, - instISAC->decoderSamplingRateKHz); - } - - instISAC->initFlag |= BIT_MASK_DEC_INIT; - - instISAC->resetFlag_8kHz = 0; - - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_UpdateBwEstimate(...) - * - * This function updates the estimate of the bandwidth. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s). - * - packet_size : size of the packet. - * - rtp_seq_number : the RTP number of the packet. - * - arr_ts : the arrival time of the packet (from NetEq) - * in samples. - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_UpdateBwEstimate( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word32 packet_size, - WebRtc_UWord16 rtp_seq_number, - WebRtc_UWord32 send_ts, - WebRtc_UWord32 arr_ts) -{ - ISACMainStruct *instISAC; - Bitstr streamdata; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* check if decoder initiated */ - if((instISAC->initFlag & BIT_MASK_DEC_INIT) != - BIT_MASK_DEC_INIT) - { - instISAC->errorCode = ISAC_DECODER_NOT_INITIATED; - return -1; - } - - if(packet_size <= 0) - { - /* return error code if the packet length is null */ - instISAC->errorCode = ISAC_EMPTY_PACKET; - return -1; - } - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - -#ifndef WEBRTC_BIG_ENDIAN - for(k = 0; k < 10; k++) - { - streamdata.stream[k] = (WebRtc_UWord8) ((encoded[k>>1] >> - ((k&1) << 3)) & 0xFF); - } -#else - memcpy(streamdata.stream, encoded, 10); -#endif - - err = WebRtcIsac_EstimateBandwidth(&instISAC->bwestimator_obj, &streamdata, - packet_size, rtp_seq_number, send_ts, arr_ts, - instISAC->encoderSamplingRateKHz, - instISAC->decoderSamplingRateKHz); - - if(err < 0) - { - /* return error code if something went wrong */ - instISAC->errorCode = -err; - return -1; - } - - return 0; -} - - -#if defined(WEBRTC_LINUX) -#define WebRtcIsac_W16RIntF(x) ((WebRtc_Word16)(x)) -#elif defined(WEBRTC_MAC) -#define WebRtcIsac_W16RIntF(x) ((WebRtc_Word16)(x)) -#elif defined(X64) -#define WebRtcIsac_W16RIntF(x) ((WebRtc_Word16)(x)) -#elif defined(WEBRTC_TARGET_PC) - -static __inline WebRtc_Word16 WebRtcIsac_W16RIntF(float flt) { - - short intgr; - - _asm - { - fld flt - fistp intgr - } ; - - return intgr ; -} -#else - -#define WebRtcIsac_W16RIntF(x) ((WebRtc_Word16)(x)) -#endif - - - - -static WebRtc_Word16 Decode( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word16 lenEncodedBytes, - WebRtc_Word16* decoded, - WebRtc_Word16* speechType, - WebRtc_Word16 isRCUPayload) -{ - /* number of samples (480 or 960), output from decoder - that were actually used in the encoder/decoder - (determined on the fly) */ - ISACMainStruct* instISAC; - ISACUBDecStruct* decInstUB; - ISACLBDecStruct* decInstLB; - - WebRtc_Word16 numSamplesLB; - WebRtc_Word16 numSamplesUB; - WebRtc_Word16 speechIdx; - float outFrame[MAX_FRAMESAMPLES]; - WebRtc_Word16 outFrameLB[MAX_FRAMESAMPLES]; - WebRtc_Word16 outFrameUB[MAX_FRAMESAMPLES]; - WebRtc_Word16 numDecodedBytesLB; - WebRtc_Word16 numDecodedBytesUB; - WebRtc_Word16 lenEncodedLBBytes; - WebRtc_Word16 validChecksum = 1; - WebRtc_Word16 k; - WebRtc_UWord8* ptrEncodedUW8 = (WebRtc_UWord8*)encoded; - WebRtc_UWord16 numLayer; - WebRtc_Word16 totSizeBytes; - WebRtc_Word16 err; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - decInstUB = &(instISAC->instUB.ISACdecUB_obj); - decInstLB = &(instISAC->instLB.ISACdecLB_obj); - - /* check if decoder initiated */ - if((instISAC->initFlag & BIT_MASK_DEC_INIT) != - BIT_MASK_DEC_INIT) - { - instISAC->errorCode = ISAC_DECODER_NOT_INITIATED; - return -1; - } - - if(lenEncodedBytes <= 0) - { - /* return error code if the packet length is null */ - instISAC->errorCode = ISAC_EMPTY_PACKET; - return -1; - } - - // the size of the rncoded lower-band is bounded by - // STREAM_SIZE_MAX, - // If a payload with the size larger than STREAM_SIZE_MAX - // is received, it is not considered erroneous. - lenEncodedLBBytes = (lenEncodedBytes > STREAM_SIZE_MAX) - ? STREAM_SIZE_MAX:lenEncodedBytes; - - // Copy to lower-band bit-stream structure - memcpy(instISAC->instLB.ISACdecLB_obj.bitstr_obj.stream, ptrEncodedUW8, - lenEncodedLBBytes); - - // Regardless of that the current codec is setup to work in - // wideband or super-wideband, the decoding of the lower-band - // has to be performed. - numDecodedBytesLB = WebRtcIsac_DecodeLb(outFrame, decInstLB, - &numSamplesLB, isRCUPayload); - - // Check for error - if((numDecodedBytesLB < 0) || - (numDecodedBytesLB > lenEncodedLBBytes) || - (numSamplesLB > MAX_FRAMESAMPLES)) - { - instISAC->errorCode = ISAC_LENGTH_MISMATCH; - return -1; - } - - // Error Check, we accept multi-layer bit-stream - // This will limit number of iterations of the - // while loop. Even withouut this the number of iterations - // is limited. - numLayer = 1; - totSizeBytes = numDecodedBytesLB; - while(totSizeBytes != lenEncodedBytes) - { - if((totSizeBytes > lenEncodedBytes) || - (ptrEncodedUW8[totSizeBytes] == 0) || - (numLayer > MAX_NUM_LAYERS)) - { - instISAC->errorCode = ISAC_LENGTH_MISMATCH; - return -1; - } - totSizeBytes += ptrEncodedUW8[totSizeBytes]; - numLayer++; - } - - if(instISAC->decoderSamplingRateKHz == kIsacWideband) - { - for(k = 0; k < numSamplesLB; k++) - { - if(outFrame[k] > 32767) - { - decoded[k] = 32767; - } - else if(outFrame[k] < -32768) - { - decoded[k] = -32768; - } - else - { - decoded[k] = (WebRtc_Word16)WebRtcIsac_W16RIntF(outFrame[k]); - } - } - numSamplesUB = 0; - } - else - { - WebRtc_UWord32 crc; - // We don't accept larger than 30ms (480 samples at lower-band) - // frame-size. - for(k = 0; k < numSamplesLB; k++) - { - if(outFrame[k] > 32767) - { - outFrameLB[k] = 32767; - } - else if(outFrame[k] < -32768) - { - outFrameLB[k] = -32768; - } - else - { - outFrameLB[k] = (WebRtc_Word16)WebRtcIsac_W16RIntF(outFrame[k]); - } - } - - //numSamplesUB = numSamplesLB; - - // Check for possible error, and if upper-band stream exist. - if(numDecodedBytesLB == lenEncodedBytes) - { - // Decoding was successful. No super-wideband bitstream - // exists. - numSamplesUB = numSamplesLB; - memset(outFrameUB, 0, sizeof(WebRtc_Word16) * numSamplesUB); - - // Prepare for the potential increase of signal bandwidth - instISAC->resetFlag_8kHz = 2; - } - else - { - // this includes the check sum and the bytes that stores the - // length - WebRtc_Word16 lenNextStream = ptrEncodedUW8[numDecodedBytesLB]; - - // Is this garbage or valid super-wideband bit-stream? - // Check if checksum is valid - if(lenNextStream <= (LEN_CHECK_SUM_WORD8 + 1)) - { - // such a small second layer cannot be super-wideband layer. - // It must be a short garbage. - validChecksum = 0; - } - else - { - // Run CRC to see if the checksum match. - WebRtcIsac_GetCrc((WebRtc_Word16*)( - &ptrEncodedUW8[numDecodedBytesLB + 1]), - lenNextStream - LEN_CHECK_SUM_WORD8 - 1, &crc); - - validChecksum = 1; - for(k = 0; k < LEN_CHECK_SUM_WORD8; k++) - { - validChecksum &= (((crc >> (24 - k * 8)) & 0xFF) == - ptrEncodedUW8[numDecodedBytesLB + lenNextStream - - LEN_CHECK_SUM_WORD8 + k]); - } - } - - if(!validChecksum) - { - // this is a garbage, we have received a wideband - // bit-stream with garbage - numSamplesUB = numSamplesLB; - memset(outFrameUB, 0, sizeof(WebRtc_Word16) * numSamplesUB); - } - else - { - // A valid super-wideband biststream exists. - enum ISACBandwidth bandwidthKHz; - WebRtc_Word32 maxDelayBit; - - //instISAC->bwestimator_obj.incomingStreamSampFreq = - // kIsacSuperWideband; - // If we have super-wideband bit-stream, we cannot - // have 60 ms frame-size. - if(numSamplesLB > FRAMESAMPLES) - { - instISAC->errorCode = ISAC_LENGTH_MISMATCH; - return -1; - } - - // the rest of the bit-stream contains the upper-band - // bit-stream curently this is the only thing there, - // however, we might add more layers. - - // Have to exclude one byte where the length is stored - // and last 'LEN_CHECK_SUM_WORD8' bytes where the - // checksum is stored. - lenNextStream -= (LEN_CHECK_SUM_WORD8 + 1); - - memcpy(decInstUB->bitstr_obj.stream, - &ptrEncodedUW8[numDecodedBytesLB + 1], lenNextStream); - - // THIS IS THE FIRST DECODING - decInstUB->bitstr_obj.W_upper = 0xFFFFFFFF; - decInstUB->bitstr_obj.streamval = 0; - decInstUB->bitstr_obj.stream_index = 0; - - // Decode jitter infotmation - err = WebRtcIsac_DecodeJitterInfo(&decInstUB->bitstr_obj, - &maxDelayBit); - // error check - if(err < 0) - { - instISAC->errorCode = -err; - return -1; - } - - // Update jitter info which is in the upper-band bit-stream - // only if the encoder is in super-wideband. Otherwise, - // the jitter info is already embeded in bandwidth index - // and has been updated. - if(instISAC->encoderSamplingRateKHz == kIsacSuperWideband) - { - err = WebRtcIsac_UpdateUplinkJitter( - &(instISAC->bwestimator_obj), maxDelayBit); - if(err < 0) - { - instISAC->errorCode = -err; - return -1; - } - } - - // decode bandwidth information - err = WebRtcIsac_DecodeBandwidth(&decInstUB->bitstr_obj, - &bandwidthKHz); - if(err < 0) - { - instISAC->errorCode = -err; - return -1; - } - - switch(bandwidthKHz) - { - case isac12kHz: - { - numDecodedBytesUB = WebRtcIsac_DecodeUb12(outFrame, - decInstUB, isRCUPayload); - - // Hang-over for transient alleviation - - // wait two frames to add the upper band going up from 8 kHz - if (instISAC->resetFlag_8kHz > 0) - { - if (instISAC->resetFlag_8kHz == 2) - { - // Silence first and a half frame - memset(outFrame, 0, MAX_FRAMESAMPLES * - sizeof(float)); - } - else - { - const float rampStep = 2.0f / MAX_FRAMESAMPLES; - float rampVal = 0; - memset(outFrame, 0, (MAX_FRAMESAMPLES>>1) * - sizeof(float)); - - // Ramp up second half of second frame - for(k = MAX_FRAMESAMPLES/2; k < MAX_FRAMESAMPLES; k++) - { - outFrame[k] *= rampVal; - rampVal += rampStep; - } - } - instISAC->resetFlag_8kHz -= 1; - } - - break; - } - case isac16kHz: - { - numDecodedBytesUB = WebRtcIsac_DecodeUb16(outFrame, - decInstUB, isRCUPayload); - break; - } - default: - return -1; - } - - // it might be less due to garbage. - if((numDecodedBytesUB != lenNextStream) && - (numDecodedBytesUB != (lenNextStream - ptrEncodedUW8[ - numDecodedBytesLB + 1 + numDecodedBytesUB]))) - { - instISAC->errorCode = ISAC_LENGTH_MISMATCH; - return -1; - } - - // If there is no error Upper-band always decodes - // 30 ms (480 samples) - numSamplesUB = FRAMESAMPLES; - - // Convert to W16 - for(k = 0; k < numSamplesUB; k++) - { - if(outFrame[k] > 32767) - { - outFrameUB[k] = 32767; - } - else if(outFrame[k] < -32768) - { - outFrameUB[k] = -32768; - } - else - { - outFrameUB[k] = (WebRtc_Word16)WebRtcIsac_W16RIntF( - outFrame[k]); - } - } - } - } - - speechIdx = 0; - while(speechIdx < numSamplesLB) - { - WebRtcSpl_SynthesisQMF(&outFrameLB[speechIdx], - &outFrameUB[speechIdx], &decoded[(speechIdx<<1)], - instISAC->synthesisFBState1, instISAC->synthesisFBState2); - - speechIdx += FRAMESAMPLES_10ms; - } - } - *speechType = 0; - return (numSamplesLB + numSamplesUB); -} - - - - - - - -/**************************************************************************** - * WebRtcIsac_Decode(...) - * - * This function decodes a ISAC frame. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the frameSize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - -WebRtc_Word16 WebRtcIsac_Decode( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word16 lenEncodedBytes, - WebRtc_Word16* decoded, - WebRtc_Word16* speechType) -{ - WebRtc_Word16 isRCUPayload = 0; - return Decode(ISAC_main_inst, encoded, lenEncodedBytes, decoded, - speechType, isRCUPayload); -} - -/**************************************************************************** - * WebRtcIsac_DecodeRcu(...) - * - * This function decodes a redundant (RCU) iSAC frame. Function is called in - * NetEq with a stored RCU payload i case of packet loss. Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the framesize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - encoded : encoded ISAC RCU frame(s) - * - len : bytes in encoded vector - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded vector - * -1 - Error - */ - - - -WebRtc_Word16 WebRtcIsac_DecodeRcu( - ISACStruct* ISAC_main_inst, - const WebRtc_UWord16* encoded, - WebRtc_Word16 lenEncodedBytes, - WebRtc_Word16* decoded, - WebRtc_Word16* speechType) -{ - WebRtc_Word16 isRCUPayload = 1; - return Decode(ISAC_main_inst, encoded, lenEncodedBytes, decoded, - speechType, isRCUPayload); -} - - -/**************************************************************************** - * WebRtcIsac_DecodePlc(...) - * - * This function conducts PLC for ISAC frame(s). Output speech length - * will be a multiple of 480 samples: 480 or 960 samples, - * depending on the frameSize (30 or 60 ms). - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - noOfLostFrames : Number of PLC frames to produce - * - * Output: - * - decoded : The decoded vector - * - * Return value : >0 - number of samples in decoded PLC vector - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_DecodePlc( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* decoded, - WebRtc_Word16 noOfLostFrames) -{ - WebRtc_Word16 numSamples; - ISACMainStruct* instISAC; - - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct*)ISAC_main_inst; - - /* Limit number of frames to two = 60 msec. Otherwise we exceed data vectors */ - if(noOfLostFrames > 2) - { - noOfLostFrames = 2; - } - - /* Get the number of samples per frame */ - switch(instISAC->decoderSamplingRateKHz) - { - case kIsacWideband: - { - numSamples = 480 * noOfLostFrames; - break; - } - case kIsacSuperWideband: - { - numSamples = 960 * noOfLostFrames; - break; - } - default: - return -1; - } - - /* Set output samples to zero */ - memset(decoded, 0, numSamples * sizeof(WebRtc_Word16)); - return numSamples; -} - - -/**************************************************************************** - * ControlLb(...) - Internal function for controling Lower Band - * ControlUb(...) - Internal function for controling Upper Band - * WebRtcIsac_Control(...) - API function - * - * This function sets the limit on the short-term average bit rate and the - * frame length. Should be used only in Instantaneous mode. - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rate : limit on the short-term average bit rate, - * in bits/second (between 10000 and 32000) - * - frameSize : number of milliseconds per frame (30 or 60) - * - * Return value : 0 - ok - * -1 - Error - */ -static WebRtc_Word16 ControlLb( - ISACLBStruct* instISAC, - double rate, - WebRtc_Word16 frameSize) -{ - if((rate >= 10000) && (rate <= 32000)) - { - instISAC->ISACencLB_obj.bottleneck = rate; - } - else - { - return -ISAC_DISALLOWED_BOTTLENECK; - } - - if((frameSize == 30) || (frameSize == 60)) - { - instISAC->ISACencLB_obj.new_framelength = (FS/1000) * frameSize; - } - else - { - return -ISAC_DISALLOWED_FRAME_LENGTH; - } - - return 0; -} - -static WebRtc_Word16 ControlUb( - ISACUBStruct* instISAC, - double rate) -{ - if((rate >= 10000) && (rate <= 32000)) - { - instISAC->ISACencUB_obj.bottleneck = rate; - } - else - { - return -ISAC_DISALLOWED_BOTTLENECK; - } - return 0; -} - -WebRtc_Word16 WebRtcIsac_Control( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 bottleneckBPS, - WebRtc_Word16 frameSize) -{ - ISACMainStruct *instISAC; - WebRtc_Word16 status; - double rateLB; - double rateUB; - enum ISACBandwidth bandwidthKHz; - - - /* Typecast pointer to real structure */ - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if(instISAC->codingMode == 0) - { - /* in adaptive mode */ - instISAC->errorCode = ISAC_MODE_MISMATCH; - return -1; - } - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - if(instISAC->encoderSamplingRateKHz == kIsacWideband) - { - // if the sampling rate is 16kHz then bandwith should be 8kHz, - // regardless of bottleneck. - bandwidthKHz = isac8kHz; - rateLB = (bottleneckBPS > 32000)? 32000:bottleneckBPS; - rateUB = 0; - } - else - { - if(WebRtcIsac_RateAllocation(bottleneckBPS, &rateLB, &rateUB, - &bandwidthKHz) < 0) - { - return -1; - } - } - - if((instISAC->encoderSamplingRateKHz == kIsacSuperWideband) && - (frameSize != 30) && - (bandwidthKHz != isac8kHz)) - { - // Cannot have 60 ms in super-wideband - instISAC->errorCode = ISAC_DISALLOWED_FRAME_LENGTH; - return -1; - } - - status = ControlLb(&instISAC->instLB, rateLB, frameSize); - if(status < 0) - { - instISAC->errorCode = -status; - return -1; - } - if(bandwidthKHz != isac8kHz) - { - status = ControlUb(&(instISAC->instUB), rateUB); - if(status < 0) - { - instISAC->errorCode = -status; - return -1; - } - } - - // - // Check if bandwidth is changing from wideband to super-wideband - // then we have to synch data buffer of lower & upper-band. also - // clean up the upper-band data buffer. - // - if((instISAC->bandwidthKHz == isac8kHz) && - (bandwidthKHz != isac8kHz)) - { - memset(instISAC->instUB.ISACencUB_obj.data_buffer_float, 0, - sizeof(float) * (MAX_FRAMESAMPLES + LB_TOTAL_DELAY_SAMPLES)); - - if(bandwidthKHz == isac12kHz) - { - instISAC->instUB.ISACencUB_obj.buffer_index = - instISAC->instLB.ISACencLB_obj.buffer_index; - } - else - { - instISAC->instUB.ISACencUB_obj.buffer_index = LB_TOTAL_DELAY_SAMPLES + - instISAC->instLB.ISACencLB_obj.buffer_index; - - memcpy(&(instISAC->instUB.ISACencUB_obj.lastLPCVec), - meanLARUB16, sizeof(double) * UB_LPC_ORDER); - } - } - - // update the payload limit it the bandwidth is changing. - if(instISAC->bandwidthKHz != bandwidthKHz) - { - instISAC->bandwidthKHz = bandwidthKHz; - UpdatePayloadSizeLimit(instISAC); - } - instISAC->bottleneck = bottleneckBPS; - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_ControlBwe(...) - * - * This function sets the initial values of bottleneck and frame-size if - * iSAC is used in channel-adaptive mode. Through this API, users can - * enforce a frame-size for all values of bottleneck. Then iSAC will not - * automatically change the frame-size. - * - * - * Input: - * - ISAC_main_inst : ISAC instance. - * - rateBPS : initial value of bottleneck in bits/second - * 10000 <= rateBPS <= 32000 is accepted - * For default bottleneck set rateBPS = 0 - * - frameSizeMs : number of milliseconds per frame (30 or 60) - * - enforceFrameSize : 1 to enforce the given frame-size through out - * the adaptation process, 0 to let iSAC change - * the frame-size if required. - * - * Return value : 0 - ok - * -1 - Error - */ -WebRtc_Word16 WebRtcIsac_ControlBwe( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 bottleneckBPS, - WebRtc_Word16 frameSizeMs, - WebRtc_Word16 enforceFrameSize) -{ - ISACMainStruct *instISAC; - enum ISACBandwidth bandwidth; - - /* Typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - /* Check that we are in channel-adaptive mode, otherwise, return (-1) */ - if(instISAC->codingMode != 0) - { - instISAC->errorCode = ISAC_MODE_MISMATCH; - return -1; - } - if((frameSizeMs != 30) && - (instISAC->encoderSamplingRateKHz == kIsacSuperWideband)) - { - return -1; - } - - /* Set struct variable if enforceFrameSize is set. ISAC will then */ - /* keep the chosen frame size. */ - if((enforceFrameSize != 0) /*|| - (instISAC->samplingRateKHz == kIsacSuperWideband)*/) - { - instISAC->instLB.ISACencLB_obj.enforceFrameSize = 1; - } - else - { - instISAC->instLB.ISACencLB_obj.enforceFrameSize = 0; - } - - /* Set initial rate, if value between 10000 and 32000, */ - /* if rateBPS is 0, keep the default initial bottleneck value (15000) */ - if(bottleneckBPS != 0) - { - double rateLB; - double rateUB; - if(WebRtcIsac_RateAllocation(bottleneckBPS, &rateLB, &rateUB, &bandwidth) < 0) - { - return -1; - } - instISAC->bwestimator_obj.send_bw_avg = (float)bottleneckBPS; - instISAC->bandwidthKHz = bandwidth; - } - - /* Set initial frameSize. If enforceFrameSize is set the frame size will - not change */ - if(frameSizeMs != 0) - { - if((frameSizeMs == 30) || (frameSizeMs == 60)) - { - instISAC->instLB.ISACencLB_obj.new_framelength = (FS/1000) * - frameSizeMs; - //instISAC->bwestimator_obj.rec_header_rate = ((float)HEADER_SIZE * - // 8.0f * 1000.0f / (float)frameSizeMs); - } - else - { - instISAC->errorCode = ISAC_DISALLOWED_FRAME_LENGTH; - return -1; - } - } - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_GetDownLinkBwIndex(...) - * - * This function returns index representing the Bandwidth estimate from - * other side to this side. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - bweIndex : Bandwidth estimate to transmit to other side. - * - */ -WebRtc_Word16 WebRtcIsac_GetDownLinkBwIndex( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* bweIndex, - WebRtc_Word16* jitterInfo) -{ - ISACMainStruct *instISAC; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct*)ISAC_main_inst; - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_DEC_INIT) != - BIT_MASK_DEC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - /* Call function to get Bandwidth Estimate */ - WebRtcIsac_GetDownlinkBwJitIndexImpl(&(instISAC->bwestimator_obj), - bweIndex, jitterInfo, instISAC->decoderSamplingRateKHz); - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_UpdateUplinkBw(...) - * - * This function takes an index representing the Bandwidth estimate from - * this side to other side and updates BWE. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - rateIndex : Bandwidth estimate from other side. - * - * Return value : 0 - ok - * -1 - index out of range - */ -WebRtc_Word16 WebRtcIsac_UpdateUplinkBw( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 bweIndex) -{ - ISACMainStruct *instISAC; - WebRtc_Word16 returnVal; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - /* Call function to get Bandwidth Estimate */ - returnVal = WebRtcIsac_UpdateUplinkBwImpl( - &(instISAC->bwestimator_obj), bweIndex, - instISAC->encoderSamplingRateKHz); - - if(returnVal < 0) - { - instISAC->errorCode = -returnVal; - return -1; - } - else - { - return 0; - } -} - - -/**************************************************************************** - * WebRtcIsac_ReadBwIndex(...) - * - * This function returns the index of the Bandwidth estimate from the - * bitstream. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - bweIndex : Bandwidth estimate in bitstream - * - */ -WebRtc_Word16 WebRtcIsac_ReadBwIndex( - const WebRtc_Word16* encoded, - WebRtc_Word16* bweIndex) -{ - Bitstr streamdata; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - -#ifndef WEBRTC_BIG_ENDIAN - for(k = 0; k < 10; k++) - { - streamdata.stream[k] = (WebRtc_UWord8) ((encoded[k>>1] >> - ((k&1) << 3)) & 0xFF); - } -#else - memcpy(streamdata.stream, encoded, 10); -#endif - - /* decode frame length */ - err = WebRtcIsac_DecodeFrameLen(&streamdata, bweIndex); - if(err < 0) - { - return err; - } - - /* decode BW estimation */ - err = WebRtcIsac_DecodeSendBW(&streamdata, bweIndex); - if(err < 0) - { - return err; - } - - return 0; -} - - -/**************************************************************************** - * WebRtcIsac_ReadFrameLen(...) - * - * This function returns the length of the frame represented in the packet. - * - * Input: - * - encoded : Encoded bitstream - * - * Output: - * - frameLength : Length of frame in packet (in samples) - * - */ -WebRtc_Word16 WebRtcIsac_ReadFrameLen( - ISACStruct* ISAC_main_inst, - const WebRtc_Word16* encoded, - WebRtc_Word16* frameLength) -{ - Bitstr streamdata; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - WebRtc_Word16 err; - ISACMainStruct* instISAC; - - streamdata.W_upper = 0xFFFFFFFF; - streamdata.streamval = 0; - streamdata.stream_index = 0; - -#ifndef WEBRTC_BIG_ENDIAN - for (k=0; k<10; k++) { - streamdata.stream[k] = (WebRtc_UWord8) ((encoded[k>>1] >> - ((k&1) << 3)) & 0xFF); - } -#else - memcpy(streamdata.stream, encoded, 10); -#endif - - /* decode frame length */ - err = WebRtcIsac_DecodeFrameLen(&streamdata, frameLength); - if(err < 0) { - return -1; - } - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if(instISAC->decoderSamplingRateKHz == kIsacSuperWideband) - { - // the decoded frame length indicates the number of samples in - // lower-band in this case, multiply by 2 to get the total number - // of samples. - *frameLength <<= 1; - } - - return 0; -} - - -/******************************************************************************* - * WebRtcIsac_GetNewFrameLen(...) - * - * returns the frame lenght (in samples) of the next packet. In the case of - * channel-adaptive mode, iSAC decides on its frame lenght based on the - * estimated bottleneck this allows a user to prepare for the next packet - * (at the encoder). - * - * The primary usage is in CE to make the iSAC works in channel-adaptive mode - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Return Value : frame lenght in samples - * - */ -WebRtc_Word16 WebRtcIsac_GetNewFrameLen( - ISACStruct *ISAC_main_inst) -{ - ISACMainStruct *instISAC; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* Return new frame length */ - if(instISAC->encoderSamplingRateKHz == kIsacWideband) - { - return (instISAC->instLB.ISACencLB_obj.new_framelength); - } - else - { - return ((instISAC->instLB.ISACencLB_obj.new_framelength) << 1); - } -} - - -/**************************************************************************** - * WebRtcIsac_GetErrorCode(...) - * - * This function can be used to check the error code of an iSAC instance. - * When a function returns -1 a error code will be set for that instance. - * The function below extract the code of the last error that occured in - * the specified instance. - * - * Input: - * - ISAC_main_inst : ISAC instance - * - * Return value : Error code - */ -WebRtc_Word16 WebRtcIsac_GetErrorCode( - ISACStruct *ISAC_main_inst) -{ - ISACMainStruct *instISAC; - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - return (instISAC->errorCode); -} - - -/**************************************************************************** - * WebRtcIsac_GetUplinkBw(...) - * - * This function outputs the target bottleneck of the codec. In - * channel-adaptive mode, the target bottleneck is specified through in-band - * signalling retreived by bandwidth estimator. - * In channel-independent, also called instantaneous mode, the target - * bottleneck is provided to the encoder by calling xxx_control(...) (if - * xxx_control is never called the default values is). - * Note that the output is the iSAC internal operating bottleneck whch might - * differ slightly from the one provided through xxx_control(). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Output: - * - *bottleneck : bottleneck in bits/sec - * - * Return value : -1 if error happens - * 0 bit-rates computed correctly. - */ -WebRtc_Word16 WebRtcIsac_GetUplinkBw( - ISACStruct* ISAC_main_inst, - WebRtc_Word32* bottleneck) -{ - ISACMainStruct* instISAC = (ISACMainStruct *)ISAC_main_inst; - - if(instISAC->codingMode == 0) - { - // we are in adaptive mode then get the bottleneck from BWE - *bottleneck = (WebRtc_Word32)instISAC->bwestimator_obj.send_bw_avg; - } - else - { - *bottleneck = instISAC->bottleneck; - } - - if((*bottleneck > 32000) && (*bottleneck < 38000)) - { - *bottleneck = 32000; - } - else if((*bottleneck > 45000) && (*bottleneck < 50000)) - { - *bottleneck = 45000; - } - else if(*bottleneck > 56000) - { - *bottleneck = 56000; - } - - return 0; -} - - -/****************************************************************************** - * WebRtcIsac_SetMaxPayloadSize(...) - * - * This function sets a limit for the maximum payload size of iSAC. The same - * value is used both for 30 and 60 ms packets. If the encoder sampling rate - * is 16 kHz the maximum payload size is between 120 and 400 bytes. If the - * encoder sampling rate is 32 kHz the maximum payload size is between 120 - * and 600 bytes. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, i.e. min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxPayloadBytes : maximum size of the payload in bytes - * valid values are between 100 and 400 bytes - * if encoder sampling rate is 16 kHz. For - * 32 kHz encoder sampling rate valid values - * are between 100 and 600 bytes. - * - * Return value : 0 if successful - * -1 if error happens - */ -WebRtc_Word16 WebRtcIsac_SetMaxPayloadSize( - ISACStruct* ISAC_main_inst, - WebRtc_Word16 maxPayloadBytes) -{ - ISACMainStruct *instISAC; - WebRtc_Word16 status = 0; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - - if(instISAC->encoderSamplingRateKHz == kIsacSuperWideband) - { - // sanity check - if(maxPayloadBytes < 120) - { - // maxRate is out of valid range - // set to the acceptable value and return -1. - maxPayloadBytes = 120; - status = -1; - } - - /* sanity check */ - if(maxPayloadBytes > STREAM_SIZE_MAX) - { - // maxRate is out of valid range - // set to the acceptable value and return -1. - maxPayloadBytes = STREAM_SIZE_MAX; - status = -1; - } - } - else - { - if(maxPayloadBytes < 120) - { - // max payload-size is out of valid range - // set to the acceptable value and return -1. - maxPayloadBytes = 120; - status = -1; - } - if(maxPayloadBytes > STREAM_SIZE_MAX_60) - { - // max payload-size is out of valid range - // set to the acceptable value and return -1. - maxPayloadBytes = STREAM_SIZE_MAX_60; - status = -1; - } - } - instISAC->maxPayloadSizeBytes = maxPayloadBytes; - UpdatePayloadSizeLimit(instISAC); - return status; -} - - -/****************************************************************************** - * WebRtcIsac_SetMaxRate(...) - * - * This function sets the maximum rate which the codec may not exceed for - * any signal packet. The maximum rate is defined and payload-size per - * frame-size in bits per second. - * - * The codec has a maximum rate of 53400 bits per second (200 bytes per 30 - * ms) if the encoder sampling rate is 16kHz, and 160 kbps (600 bytes/30 ms) - * if the encoder sampling rate is 32 kHz. - * - * It is possible to set a maximum rate between 32000 and 53400 bits/sec - * in wideband mode, and 32000 to 160000 bits/sec in super-wideband mode. - * - * --------------- - * IMPORTANT NOTES - * --------------- - * The size of a packet is limited to the minimum of 'max-payload-size' and - * 'max-rate.' For instance, let's assume the max-payload-size is set to - * 170 bytes, and max-rate is set to 40 kbps. Note that a limit of 40 kbps - * translates to 150 bytes for 30ms frame-size & 300 bytes for 60ms - * frame-size. Then a packet with a frame-size of 30 ms is limited to 150, - * i.e. min(170, 150), and a packet with 60 ms frame-size is limited to - * 170 bytes, min(170, 300). - * - * Input: - * - ISAC_main_inst : iSAC instance - * - maxRate : maximum rate in bits per second, - * valid values are 32000 to 53400 bits/sec in - * wideband mode, and 32000 to 160000 bits/sec in - * super-wideband mode. - * - * Return value : 0 if successful - * -1 if error happens - */ -WebRtc_Word16 WebRtcIsac_SetMaxRate( - ISACStruct* ISAC_main_inst, - WebRtc_Word32 maxRate) -{ - ISACMainStruct *instISAC; - WebRtc_Word16 maxRateInBytesPer30Ms; - WebRtc_Word16 status = 0; - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct *)ISAC_main_inst; - - /* check if encoder initiated */ - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - return -1; - } - /* - Calculate maximum number of bytes per 30 msec packets for the - given maximum rate. Multiply with 30/1000 to get number of - bits per 30 ms, divide by 8 to get number of bytes per 30 ms: - maxRateInBytes = floor((maxRate * 30/1000) / 8); - */ - maxRateInBytesPer30Ms = (WebRtc_Word16)(maxRate*3/800); - - if(instISAC->encoderSamplingRateKHz == kIsacWideband) - { - if(maxRate < 32000) - { - // max rate is out of valid range - // set to the acceptable value and return -1. - maxRateInBytesPer30Ms = 120; - status = -1; - } - - if(maxRate > 53400) - { - // max rate is out of valid range - // set to the acceptable value and return -1. - maxRateInBytesPer30Ms = 200; - status = -1; - } - } - else - { - if(maxRateInBytesPer30Ms < 120) - { - // maxRate is out of valid range - // set to the acceptable value and return -1. - maxRateInBytesPer30Ms = 120; - status = -1; - } - - if(maxRateInBytesPer30Ms > STREAM_SIZE_MAX) - { - // maxRate is out of valid range - // set to the acceptable value and return -1. - maxRateInBytesPer30Ms = STREAM_SIZE_MAX; - status = -1; - } - } - instISAC->maxRateBytesPer30Ms = maxRateInBytesPer30Ms; - UpdatePayloadSizeLimit(instISAC); - return status; -} - - -/**************************************************************************** - * WebRtcIsac_GetRedPayload(...) - * - * Populates "encoded" with the redundant payload of the recently encoded - * frame. This function has to be called once that WebRtcIsac_Encode(...) - * returns a positive value. Regardless of the frame-size this function will - * be called only once after encoding is completed. The bit-stream is - * targeted for 16000 bit/sec. - * - * Input: - * - ISAC_main_inst : iSAC struct - * - * Output: - * - encoded : the encoded data vector - * - * - * Return value : >0 - Length (in bytes) of coded data - * : -1 - Error - * - * - */ -WebRtc_Word16 WebRtcIsac_GetRedPayload( - ISACStruct* ISAC_main_inst, - WebRtc_Word16* encoded) -{ - ISACMainStruct* instISAC; - Bitstr iSACBitStreamInst; - WebRtc_Word16 streamLenLB; - WebRtc_Word16 streamLenUB; - WebRtc_Word16 streamLen; - WebRtc_Word16 totalLenUB; - WebRtc_UWord8* ptrEncodedUW8 = (WebRtc_UWord8*)encoded; -#ifndef WEBRTC_BIG_ENDIAN - int k; -#endif - - /* typecast pointer to real structure */ - instISAC = (ISACMainStruct*)ISAC_main_inst; - - - if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->errorCode = ISAC_ENCODER_NOT_INITIATED; - } - - - iSACBitStreamInst.W_upper = 0xFFFFFFFF; - iSACBitStreamInst.streamval = 0; - iSACBitStreamInst.stream_index = 0; - - - streamLenLB = WebRtcIsac_EncodeStoredDataLb( - &instISAC->instLB.ISACencLB_obj.SaveEnc_obj, - &iSACBitStreamInst, - instISAC->instLB.ISACencLB_obj.lastBWIdx, - RCU_TRANSCODING_SCALE); - - if(streamLenLB < 0) - { - return -1; - } - - /* convert from bytes to WebRtc_Word16 */ - memcpy(ptrEncodedUW8, iSACBitStreamInst.stream, streamLenLB); - - streamLen = streamLenLB; - - if(instISAC->bandwidthKHz == isac8kHz) - { - return streamLenLB; - } - - streamLenUB = WebRtcIsac_GetRedPayloadUb( - &instISAC->instUB.ISACencUB_obj.SaveEnc_obj, - &iSACBitStreamInst, instISAC->bandwidthKHz); - - if(streamLenUB < 0) - { - // an error has happened but this is not the error due to a - // bit-stream larger than the limit - return -1; - } - - // We have one byte to write the total length of the upper band - // the length include the bitstream length, check-sum and the - // single byte where the length is written to. This is according to - // iSAC wideband and how the "garbage" is dealt. - totalLenUB = streamLenUB + 1 + LEN_CHECK_SUM_WORD8; - if(totalLenUB > 255) - { - streamLenUB = 0; - } - - // Generate CRC if required. - if((instISAC->bandwidthKHz != isac8kHz) && - (streamLenUB > 0)) - { - WebRtc_UWord32 crc; - streamLen += totalLenUB; - ptrEncodedUW8[streamLenLB] = (WebRtc_UWord8)totalLenUB; - memcpy(&ptrEncodedUW8[streamLenLB+1], iSACBitStreamInst.stream, streamLenUB); - - WebRtcIsac_GetCrc((WebRtc_Word16*)(&(ptrEncodedUW8[streamLenLB + 1])), - streamLenUB, &crc); -#ifndef WEBRTC_BIG_ENDIAN - for(k = 0; k < LEN_CHECK_SUM_WORD8; k++) - { - ptrEncodedUW8[streamLen - LEN_CHECK_SUM_WORD8 + k] = - (WebRtc_UWord8)((crc >> (24 - k * 8)) & 0xFF); - } -#else - memcpy(&ptrEncodedUW8[streamLenLB + streamLenUB + 1], &crc, - LEN_CHECK_SUM_WORD8); -#endif - } - - - return streamLen; -} - - -/**************************************************************************** - * WebRtcIsac_version(...) - * - * This function returns the version number. - * - * Output: - * - version : Pointer to character string - * - */ -void WebRtcIsac_version(char *version) -{ - strcpy(version, "4.3.0"); -} - - -/****************************************************************************** - * WebRtcIsac_SetEncSampRate() - * Set the sampling rate of the encoder. Initialization of the encoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. The encoding-mode and the - * bottleneck remain unchanged by this call, however, the maximum rate and - * maximum payload-size will reset to their default value. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : enumerator specifying the sampling rate. - * - * Return value : 0 if successful - * -1 if failed. - */ -WebRtc_Word16 WebRtcIsac_SetEncSampRate( - ISACStruct* ISAC_main_inst, - enum IsacSamplingRate sampRate) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if((sampRate != kIsacWideband) && - (sampRate != kIsacSuperWideband)) - { - // Sampling Frequency is not supported - instISAC->errorCode = ISAC_UNSUPPORTED_SAMPLING_FREQUENCY; - return -1; - } - else if((instISAC->initFlag & BIT_MASK_ENC_INIT) != - BIT_MASK_ENC_INIT) - { - instISAC->encoderSamplingRateKHz = sampRate; - return 0; - } - else - { - ISACUBStruct* instUB = &(instISAC->instUB); - ISACLBStruct* instLB = &(instISAC->instLB); - double bottleneckLB; - double bottleneckUB; - WebRtc_Word32 bottleneck = instISAC->bottleneck; - WebRtc_Word16 codingMode = instISAC->codingMode; - WebRtc_Word16 frameSizeMs = instLB->ISACencLB_obj.new_framelength / (FS / 1000); - - if((sampRate == kIsacWideband) && - (instISAC->encoderSamplingRateKHz == kIsacSuperWideband)) - { - // changing from super-wideband to wideband. - // we don't need to re-initialize the encoder of the - // lower-band. - instISAC->bandwidthKHz = isac8kHz; - if(codingMode == 1) - { - ControlLb(instLB, - (bottleneck > 32000)? 32000:bottleneck, FRAMESIZE); - } - instISAC->maxPayloadSizeBytes = STREAM_SIZE_MAX_60; - instISAC->maxRateBytesPer30Ms = STREAM_SIZE_MAX_30; - } - else if((sampRate == kIsacSuperWideband) && - (instISAC->encoderSamplingRateKHz == kIsacWideband)) - { - if(codingMode == 1) - { - WebRtcIsac_RateAllocation(bottleneck, &bottleneckLB, &bottleneckUB, - &(instISAC->bandwidthKHz)); - } - - instISAC->maxPayloadSizeBytes = STREAM_SIZE_MAX; - instISAC->maxRateBytesPer30Ms = STREAM_SIZE_MAX; - - EncoderInitLb(instLB, codingMode, sampRate); - EncoderInitUb(instUB, instISAC->bandwidthKHz); - - memset(instISAC->analysisFBState1, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - memset(instISAC->analysisFBState2, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - - if(codingMode == 1) - { - instISAC->bottleneck = bottleneck; - ControlLb(instLB, bottleneckLB, - (instISAC->bandwidthKHz == isac8kHz)? frameSizeMs:FRAMESIZE); - if(instISAC->bandwidthKHz > isac8kHz) - { - ControlUb(instUB, bottleneckUB); - } - } - else - { - instLB->ISACencLB_obj.enforceFrameSize = 0; - instLB->ISACencLB_obj.new_framelength = FRAMESAMPLES; - } - } - instISAC->encoderSamplingRateKHz = sampRate; - return 0; - } -} - - -/****************************************************************************** - * WebRtcIsac_SetDecSampRate() - * Set the sampling rate of the decoder. Initialization of the decoder WILL - * NOT overwrite the sampling rate of the encoder. The default value is 16 kHz - * which is set when the instance is created. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - sampRate : enumerator specifying the sampling rate. - * - * Return value : 0 if successful - * -1 if failed. - */ -WebRtc_Word16 WebRtcIsac_SetDecSampRate( - ISACStruct* ISAC_main_inst, - enum IsacSamplingRate sampRate) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - if((sampRate != kIsacWideband) && - (sampRate != kIsacSuperWideband)) - { - // Sampling Frequency is not supported - instISAC->errorCode = ISAC_UNSUPPORTED_SAMPLING_FREQUENCY; - return -1; - } - else - { - if((instISAC->decoderSamplingRateKHz == kIsacWideband) && - (sampRate == kIsacSuperWideband)) - { - // switching from wideband to super-wideband at the decoder - // we need to reset the filter-bank and initialize - // upper-band decoder. - memset(instISAC->synthesisFBState1, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - memset(instISAC->synthesisFBState2, 0, - FB_STATE_SIZE_WORD32 * sizeof(WebRtc_Word32)); - - if(DecoderInitUb(&(instISAC->instUB)) < 0) - { - return -1; - } - } - instISAC->decoderSamplingRateKHz = sampRate; - return 0; - } -} - - -/****************************************************************************** - * WebRtcIsac_EncSampRate() - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : enumerator representing sampling frequency - * associated with the encoder, the input audio - * is expected to be sampled at this rate. - * - */ -enum IsacSamplingRate WebRtcIsac_EncSampRate( - ISACStruct* ISAC_main_inst) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - return instISAC->encoderSamplingRateKHz; -} - - -/****************************************************************************** - * WebRtcIsac_DecSampRate() - * Return the sampling rate of the decoded audio. - * - * Input: - * - ISAC_main_inst : iSAC instance - * - * Return value : enumerator representing sampling frequency - * associated with the decoder, i.e. the - * sampling rate of the decoded audio. - * - */ -enum IsacSamplingRate WebRtcIsac_DecSampRate( - ISACStruct* ISAC_main_inst) -{ - ISACMainStruct* instISAC; - - instISAC = (ISACMainStruct*)ISAC_main_inst; - - return instISAC->decoderSamplingRateKHz; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/isac.gyp b/modules/audio_coding/codecs/iSAC/main/source/isac.gyp deleted file mode 100644 index cdfbe2741..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/isac.gyp +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'iSAC', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/isac.h', - 'arith_routines.c', - 'arith_routines_hist.c', - 'arith_routines_logist.c', - 'bandwidth_estimator.c', - 'crc.c', - 'decode.c', - 'decode_bwe.c', - 'encode.c', - 'encode_lpc_swb.c', - 'entropy_coding.c', - 'fft.c', - 'filter_functions.c', - 'filterbank_tables.c', - 'intialize.c', - 'isac.c', - 'filterbanks.c', - 'pitch_lag_tables.c', - 'lattice.c', - 'lpc_gain_swb_tables.c', - 'lpc_analysis.c', - 'lpc_shape_swb12_tables.c', - 'lpc_shape_swb16_tables.c', - 'lpc_tables.c', - 'pitch_estimator.c', - 'pitch_filter.c', - 'pitch_gain_tables.c', - 'spectrum_ar_model_tables.c', - 'transform.c', - 'arith_routines.h', - 'bandwidth_estimator.h', - 'codec.h', - 'crc.h', - 'encode_lpc_swb.h', - 'entropy_coding.h', - 'fft.h', - 'filterbank_tables.h', - 'lpc_gain_swb_tables.h', - 'lpc_analysis.h', - 'lpc_shape_swb12_tables.h', - 'lpc_shape_swb16_tables.h', - 'lpc_tables.h', - 'pitch_estimator.h', - 'pitch_gain_tables.h', - 'pitch_lag_tables.h', - 'settings.h', - 'spectrum_ar_model_tables.h', - 'structs.h', - ], - 'conditions': [ - ['OS!="win"', { - 'defines': [ - 'WEBRTC_LINUX', - ], - }], - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/codecs/iSAC/main/source/lattice.c b/modules/audio_coding/codecs/iSAC/main/source/lattice.c deleted file mode 100644 index 197f001bb..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lattice.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lattice.c - * - * contains the normalized lattice filter routines (MA and AR) for iSAC codec - * - */ -#include "settings.h" -#include "codec.h" - -#include -#include -#ifdef ANDROID -#include -#endif - -/* filter the signal using normalized lattice filter */ -/* MA filter */ -void WebRtcIsac_NormLatticeFilterMa(int orderCoef, - float *stateF, - float *stateG, - float *lat_in, - double *filtcoeflo, - double *lat_out) -{ - int n,k,i,u,temp1; - int ord_1 = orderCoef+1; - float sth[MAX_AR_MODEL_ORDER]; - float cth[MAX_AR_MODEL_ORDER]; - float inv_cth[MAX_AR_MODEL_ORDER]; - double a[MAX_AR_MODEL_ORDER+1]; - float f[MAX_AR_MODEL_ORDER+1][HALF_SUBFRAMELEN], g[MAX_AR_MODEL_ORDER+1][HALF_SUBFRAMELEN]; - float gain1; - - for (u=0;u=0;i--) //get the state of f&g for the first input, for all orders - { - ARf[i][0] = cth[i]*ARf[i+1][0] - sth[i]*stateG[i]; - ARg[i+1][0] = sth[i]*ARf[i+1][0] + cth[i]* stateG[i]; - } - ARg[0][0] = ARf[0][0]; - - for(n=0;n<(HALF_SUBFRAMELEN-1);n++) - { - for(k=orderCoef-1;k>=0;k--) - { - ARf[k][n+1] = cth[k]*ARf[k+1][n+1] - sth[k]*ARg[k][n]; - ARg[k+1][n+1] = sth[k]*ARf[k+1][n+1] + cth[k]* ARg[k][n]; - } - ARg[0][n+1] = ARf[0][n+1]; - } - - memcpy(lat_out+u * HALF_SUBFRAMELEN, &(ARf[0][0]), sizeof(float) * HALF_SUBFRAMELEN); - - /* cannot use memcpy in the following */ - for (i=0;i0; m--) - { - tmp_inv = 1.0f / cth2; - for (k=1; k<=m; k++) - { - tmp[k] = ((float)a[k] - sth[m] * (float)a[m-k+1]) * tmp_inv; - } - - for (k=1; k -#include - -#define LEVINSON_EPS 1.0e-10 - - -/* window */ -/* Matlab generation code: - * t = (1:256)/257; r = 1-(1-t).^.45; w = sin(r*pi).^3; w = w/sum(w); plot((1:256)/8, w); grid; - * for k=1:16, fprintf(1, '%.8f, ', w(k*16 + (-15:0))); fprintf(1, '\n'); end - */ -const double WebRtcIsac_kLpcCorrWindow[WINLEN] = { - 0.00000000, 0.00000001, 0.00000004, 0.00000010, 0.00000020, 0.00000035, 0.00000055, 0.00000083, 0.00000118, 0.00000163, 0.00000218, 0.00000283, 0.00000361, 0.00000453, 0.00000558, 0.00000679, - 0.00000817, 0.00000973, 0.00001147, 0.00001342, 0.00001558, 0.00001796, 0.00002058, 0.00002344, 0.00002657, 0.00002997, 0.00003365, 0.00003762, 0.00004190, 0.00004651, 0.00005144, 0.00005673, - 0.00006236, 0.00006837, 0.00007476, 0.00008155, 0.00008875, 0.00009636, 0.00010441, 0.00011290, 0.00012186, 0.00013128, 0.00014119, 0.00015160, 0.00016252, 0.00017396, 0.00018594, 0.00019846, - 0.00021155, 0.00022521, 0.00023946, 0.00025432, 0.00026978, 0.00028587, 0.00030260, 0.00031998, 0.00033802, 0.00035674, 0.00037615, 0.00039626, 0.00041708, 0.00043863, 0.00046092, 0.00048396, - 0.00050775, 0.00053233, 0.00055768, 0.00058384, 0.00061080, 0.00063858, 0.00066720, 0.00069665, 0.00072696, 0.00075813, 0.00079017, 0.00082310, 0.00085692, 0.00089164, 0.00092728, 0.00096384, - 0.00100133, 0.00103976, 0.00107914, 0.00111947, 0.00116077, 0.00120304, 0.00124630, 0.00129053, 0.00133577, 0.00138200, 0.00142924, 0.00147749, 0.00152676, 0.00157705, 0.00162836, 0.00168070, - 0.00173408, 0.00178850, 0.00184395, 0.00190045, 0.00195799, 0.00201658, 0.00207621, 0.00213688, 0.00219860, 0.00226137, 0.00232518, 0.00239003, 0.00245591, 0.00252284, 0.00259079, 0.00265977, - 0.00272977, 0.00280078, 0.00287280, 0.00294582, 0.00301984, 0.00309484, 0.00317081, 0.00324774, 0.00332563, 0.00340446, 0.00348421, 0.00356488, 0.00364644, 0.00372889, 0.00381220, 0.00389636, - 0.00398135, 0.00406715, 0.00415374, 0.00424109, 0.00432920, 0.00441802, 0.00450754, 0.00459773, 0.00468857, 0.00478001, 0.00487205, 0.00496464, 0.00505775, 0.00515136, 0.00524542, 0.00533990, - 0.00543476, 0.00552997, 0.00562548, 0.00572125, 0.00581725, 0.00591342, 0.00600973, 0.00610612, 0.00620254, 0.00629895, 0.00639530, 0.00649153, 0.00658758, 0.00668341, 0.00677894, 0.00687413, - 0.00696891, 0.00706322, 0.00715699, 0.00725016, 0.00734266, 0.00743441, 0.00752535, 0.00761540, 0.00770449, 0.00779254, 0.00787947, 0.00796519, 0.00804963, 0.00813270, 0.00821431, 0.00829437, - 0.00837280, 0.00844949, 0.00852436, 0.00859730, 0.00866822, 0.00873701, 0.00880358, 0.00886781, 0.00892960, 0.00898884, 0.00904542, 0.00909923, 0.00915014, 0.00919805, 0.00924283, 0.00928436, - 0.00932252, 0.00935718, 0.00938821, 0.00941550, 0.00943890, 0.00945828, 0.00947351, 0.00948446, 0.00949098, 0.00949294, 0.00949020, 0.00948262, 0.00947005, 0.00945235, 0.00942938, 0.00940099, - 0.00936704, 0.00932738, 0.00928186, 0.00923034, 0.00917268, 0.00910872, 0.00903832, 0.00896134, 0.00887763, 0.00878706, 0.00868949, 0.00858478, 0.00847280, 0.00835343, 0.00822653, 0.00809199, - 0.00794970, 0.00779956, 0.00764145, 0.00747530, 0.00730103, 0.00711857, 0.00692787, 0.00672888, 0.00652158, 0.00630597, 0.00608208, 0.00584994, 0.00560962, 0.00536124, 0.00510493, 0.00484089, - 0.00456935, 0.00429062, 0.00400505, 0.00371310, 0.00341532, 0.00311238, 0.00280511, 0.00249452, 0.00218184, 0.00186864, 0.00155690, 0.00124918, 0.00094895, 0.00066112, 0.00039320, 0.00015881 -}; - -double WebRtcIsac_LevDurb(double *a, double *k, double *r, int order) -{ - - double sum, alpha; - int m, m_h, i; - alpha = 0; //warning -DH - a[0] = 1.0; - if (r[0] < LEVINSON_EPS) { /* if r[0] <= 0, set LPC coeff. to zero */ - for (i = 0; i < order; i++) { - k[i] = 0; - a[i+1] = 0; - } - } else { - a[1] = k[0] = -r[1]/r[0]; - alpha = r[0] + r[1] * k[0]; - for (m = 1; m < order; m++){ - sum = r[m + 1]; - for (i = 0; i < m; i++){ - sum += a[i+1] * r[m - i]; - } - k[m] = -sum / alpha; - alpha += k[m] * sum; - m_h = (m + 1) >> 1; - for (i = 0; i < m_h; i++){ - sum = a[i+1] + k[m] * a[m - i]; - a[m - i] += k[m] * a[i+1]; - a[i+1] = sum; - } - a[m+1] = k[m]; - } - } - return alpha; -} - - -//was static before, but didn't work with MEX file -void WebRtcIsac_GetVars(const double *input, const WebRtc_Word16 *pitchGains_Q12, - double *oldEnergy, double *varscale) -{ - double nrg[4], chng, pg; - int k; - - double pitchGains[4]={0,0,0,0};; - - /* Calculate energies of first and second frame halfs */ - nrg[0] = 0.0001; - for (k = QLOOKAHEAD/2; k < (FRAMESAMPLES_QUARTER + QLOOKAHEAD) / 2; k++) { - nrg[0] += input[k]*input[k]; - } - nrg[1] = 0.0001; - for ( ; k < (FRAMESAMPLES_HALF + QLOOKAHEAD) / 2; k++) { - nrg[1] += input[k]*input[k]; - } - nrg[2] = 0.0001; - for ( ; k < (FRAMESAMPLES*3/4 + QLOOKAHEAD) / 2; k++) { - nrg[2] += input[k]*input[k]; - } - nrg[3] = 0.0001; - for ( ; k < (FRAMESAMPLES + QLOOKAHEAD) / 2; k++) { - nrg[3] += input[k]*input[k]; - } - - /* Calculate average level change */ - chng = 0.25 * (fabs(10.0 * log10(nrg[3] / nrg[2])) + - fabs(10.0 * log10(nrg[2] / nrg[1])) + - fabs(10.0 * log10(nrg[1] / nrg[0])) + - fabs(10.0 * log10(nrg[0] / *oldEnergy))); - - - /* Find average pitch gain */ - pg = 0.0; - for (k=0; k<4; k++) - { - pitchGains[k] = ((float)pitchGains_Q12[k])/4096; - pg += pitchGains[k]; - } - pg *= 0.25; - - /* If pitch gain is low and energy constant - increase noise level*/ - /* Matlab code: - pg = 0:.01:.45; plot(pg, 0.0 + 1.0 * exp( -1.0 * exp(-200.0 * pg.*pg.*pg) / (1.0 + 0.4 * 0) )) - */ - *varscale = 0.0 + 1.0 * exp( -1.4 * exp(-200.0 * pg*pg*pg) / (1.0 + 0.4 * chng) ); - - *oldEnergy = nrg[3]; -} - -void -WebRtcIsac_GetVarsUB( - const double* input, - double* oldEnergy, - double* varscale) -{ - double nrg[4], chng, pg; - int k; - - /* Calculate energies of first and second frame halfs */ - nrg[0] = 0.0001; - for (k = 0; k < (FRAMESAMPLES_QUARTER) / 2; k++) { - nrg[0] += input[k]*input[k]; - } - nrg[1] = 0.0001; - for ( ; k < (FRAMESAMPLES_HALF) / 2; k++) { - nrg[1] += input[k]*input[k]; - } - nrg[2] = 0.0001; - for ( ; k < (FRAMESAMPLES*3/4) / 2; k++) { - nrg[2] += input[k]*input[k]; - } - nrg[3] = 0.0001; - for ( ; k < (FRAMESAMPLES) / 2; k++) { - nrg[3] += input[k]*input[k]; - } - - /* Calculate average level change */ - chng = 0.25 * (fabs(10.0 * log10(nrg[3] / nrg[2])) + - fabs(10.0 * log10(nrg[2] / nrg[1])) + - fabs(10.0 * log10(nrg[1] / nrg[0])) + - fabs(10.0 * log10(nrg[0] / *oldEnergy))); - - - /* Find average pitch gain */ - pg = 0.0; - - /* If pitch gain is low and energy constant - increase noise level*/ - /* Matlab code: - pg = 0:.01:.45; plot(pg, 0.0 + 1.0 * exp( -1.0 * exp(-200.0 * pg.*pg.*pg) / (1.0 + 0.4 * 0) )) - */ - *varscale = exp( -1.4 / (1.0 + 0.4 * chng) ); - - *oldEnergy = nrg[3]; -} - -void WebRtcIsac_GetLpcCoefLb(double *inLo, double *inHi, MaskFiltstr *maskdata, - double signal_noise_ratio, const WebRtc_Word16 *pitchGains_Q12, - double *lo_coeff, double *hi_coeff) -{ - int k, n, j, pos1, pos2; - double varscale; - - double DataLo[WINLEN], DataHi[WINLEN]; - double corrlo[ORDERLO+2], corrlo2[ORDERLO+1]; - double corrhi[ORDERHI+1]; - double k_veclo[ORDERLO], k_vechi[ORDERHI]; - - double a_LO[ORDERLO+1], a_HI[ORDERHI+1]; - double tmp, res_nrg; - - double FwdA, FwdB; - - /* hearing threshold level in dB; higher value gives more noise */ - const double HearThresOffset = -28.0; - - /* bandwdith expansion factors for low- and high band */ - const double gammaLo = 0.9; - const double gammaHi = 0.8; - - /* less-noise-at-low-frequencies factor */ - double aa; - - - /* convert from dB to signal level */ - const double H_T_H = pow(10.0, 0.05 * HearThresOffset); - double S_N_R = pow(10.0, 0.05 * signal_noise_ratio) / 3.46; /* divide by sqrt(12) */ - - /* change quallevel depending on pitch gains and level fluctuations */ - WebRtcIsac_GetVars(inLo, pitchGains_Q12, &(maskdata->OldEnergy), &varscale); - - /* less-noise-at-low-frequencies factor */ - aa = 0.35 * (0.5 + 0.5 * varscale); - - /* replace data in buffer by new look-ahead data */ - for (pos1 = 0; pos1 < QLOOKAHEAD; pos1++) - maskdata->DataBufferLo[pos1 + WINLEN - QLOOKAHEAD] = inLo[pos1]; - - for (k = 0; k < SUBFRAMES; k++) { - - /* Update input buffer and multiply signal with window */ - for (pos1 = 0; pos1 < WINLEN - UPDATE/2; pos1++) { - maskdata->DataBufferLo[pos1] = maskdata->DataBufferLo[pos1 + UPDATE/2]; - maskdata->DataBufferHi[pos1] = maskdata->DataBufferHi[pos1 + UPDATE/2]; - DataLo[pos1] = maskdata->DataBufferLo[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - DataHi[pos1] = maskdata->DataBufferHi[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - } - pos2 = k * UPDATE/2; - for (n = 0; n < UPDATE/2; n++, pos1++) { - maskdata->DataBufferLo[pos1] = inLo[QLOOKAHEAD + pos2]; - maskdata->DataBufferHi[pos1] = inHi[pos2++]; - DataLo[pos1] = maskdata->DataBufferLo[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - DataHi[pos1] = maskdata->DataBufferHi[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - } - - /* Get correlation coefficients */ - WebRtcIsac_AutoCorr(corrlo, DataLo, WINLEN, ORDERLO+1); /* computing autocorrelation */ - WebRtcIsac_AutoCorr(corrhi, DataHi, WINLEN, ORDERHI); - - - /* less noise for lower frequencies, by filtering/scaling autocorrelation sequences */ - corrlo2[0] = (1.0+aa*aa) * corrlo[0] - 2.0*aa * corrlo[1]; - tmp = (1.0 + aa*aa); - for (n = 1; n <= ORDERLO; n++) { - corrlo2[n] = tmp * corrlo[n] - aa * (corrlo[n-1] + corrlo[n+1]); - } - tmp = (1.0+aa) * (1.0+aa); - for (n = 0; n <= ORDERHI; n++) { - corrhi[n] = tmp * corrhi[n]; - } - - /* add white noise floor */ - corrlo2[0] += 1e-6; - corrhi[0] += 1e-6; - - - FwdA = 0.01; - FwdB = 0.01; - - /* recursive filtering of correlation over subframes */ - for (n = 0; n <= ORDERLO; n++) { - maskdata->CorrBufLo[n] = FwdA * maskdata->CorrBufLo[n] + corrlo2[n]; - corrlo2[n] = ((1.0-FwdA)*FwdB) * maskdata->CorrBufLo[n] + (1.0-FwdB) * corrlo2[n]; - } - for (n = 0; n <= ORDERHI; n++) { - maskdata->CorrBufHi[n] = FwdA * maskdata->CorrBufHi[n] + corrhi[n]; - corrhi[n] = ((1.0-FwdA)*FwdB) * maskdata->CorrBufHi[n] + (1.0-FwdB) * corrhi[n]; - } - - /* compute prediction coefficients */ - WebRtcIsac_LevDurb(a_LO, k_veclo, corrlo2, ORDERLO); - WebRtcIsac_LevDurb(a_HI, k_vechi, corrhi, ORDERHI); - - /* bandwidth expansion */ - tmp = gammaLo; - for (n = 1; n <= ORDERLO; n++) { - a_LO[n] *= tmp; - tmp *= gammaLo; - } - - /* residual energy */ - res_nrg = 0.0; - for (j = 0; j <= ORDERLO; j++) { - for (n = 0; n <= j; n++) { - res_nrg += a_LO[j] * corrlo2[j-n] * a_LO[n]; - } - for (n = j+1; n <= ORDERLO; n++) { - res_nrg += a_LO[j] * corrlo2[n-j] * a_LO[n]; - } - } - - /* add hearing threshold and compute the gain */ - *lo_coeff++ = S_N_R / (sqrt(res_nrg) / varscale + H_T_H); - - /* copy coefficients to output array */ - for (n = 1; n <= ORDERLO; n++) { - *lo_coeff++ = a_LO[n]; - } - - - /* bandwidth expansion */ - tmp = gammaHi; - for (n = 1; n <= ORDERHI; n++) { - a_HI[n] *= tmp; - tmp *= gammaHi; - } - - /* residual energy */ - res_nrg = 0.0; - for (j = 0; j <= ORDERHI; j++) { - for (n = 0; n <= j; n++) { - res_nrg += a_HI[j] * corrhi[j-n] * a_HI[n]; - } - for (n = j+1; n <= ORDERHI; n++) { - res_nrg += a_HI[j] * corrhi[n-j] * a_HI[n]; - } - } - - /* add hearing threshold and compute of the gain */ - *hi_coeff++ = S_N_R / (sqrt(res_nrg) / varscale + H_T_H); - - /* copy coefficients to output array */ - for (n = 1; n <= ORDERHI; n++) { - *hi_coeff++ = a_HI[n]; - } - } -} - - - -/****************************************************************************** - * WebRtcIsac_GetLpcCoefUb() - * - * Compute LP coefficients and correlation coefficients. At 12 kHz LP - * coefficients of the first and the last sub-frame is computed. At 16 kHz - * LP coefficients of 4th, 8th and 12th sub-frames are computed. We always - * compute correlation coefficients of all sub-frames. - * - * Inputs: - * -inSignal : Input signal - * -maskdata : a structure keeping signal from previous frame. - * -bandwidth : specifies if the codec is in 0-16 kHz mode or - * 0-12 kHz mode. - * - * Outputs: - * -lpCoeff : pointer to a buffer where A-polynomials are - * written to (first coeff is 1 and it is not - * written) - * -corrMat : a matrix where correlation coefficients of each - * sub-frame are written to one row. - * -varscale : a scale used to compute LPC gains. - */ -void -WebRtcIsac_GetLpcCoefUb( - double* inSignal, - MaskFiltstr* maskdata, - double* lpCoeff, - double corrMat[][UB_LPC_ORDER + 1], - double* varscale, - WebRtc_Word16 bandwidth) -{ - int frameCntr, activeFrameCntr, n, pos1, pos2; - WebRtc_Word16 criterion1; - WebRtc_Word16 criterion2; - WebRtc_Word16 numSubFrames = SUBFRAMES * (1 + (bandwidth == isac16kHz)); - double data[WINLEN]; - double corrSubFrame[UB_LPC_ORDER+2]; - double reflecCoeff[UB_LPC_ORDER]; - - double aPolynom[UB_LPC_ORDER+1]; - double tmp; - - /* bandwdith expansion factors */ - const double gamma = 0.9; - - /* change quallevel depending on pitch gains and level fluctuations */ - WebRtcIsac_GetVarsUB(inSignal, &(maskdata->OldEnergy), varscale); - - /* replace data in buffer by new look-ahead data */ - for(frameCntr = 0, activeFrameCntr = 0; frameCntr < numSubFrames; - frameCntr++) - { - if(frameCntr == SUBFRAMES) - { - // we are in 16 kHz - varscale++; - WebRtcIsac_GetVarsUB(&inSignal[FRAMESAMPLES_HALF], - &(maskdata->OldEnergy), varscale); - } - /* Update input buffer and multiply signal with window */ - for(pos1 = 0; pos1 < WINLEN - UPDATE/2; pos1++) - { - maskdata->DataBufferLo[pos1] = maskdata->DataBufferLo[pos1 + - UPDATE/2]; - data[pos1] = maskdata->DataBufferLo[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - } - pos2 = frameCntr * UPDATE/2; - for(n = 0; n < UPDATE/2; n++, pos1++, pos2++) - { - maskdata->DataBufferLo[pos1] = inSignal[pos2]; - data[pos1] = maskdata->DataBufferLo[pos1] * WebRtcIsac_kLpcCorrWindow[pos1]; - } - - /* Get correlation coefficients */ - /* computing autocorrelation */ - WebRtcIsac_AutoCorr(corrSubFrame, data, WINLEN, UB_LPC_ORDER+1); - memcpy(corrMat[frameCntr], corrSubFrame, - (UB_LPC_ORDER+1)*sizeof(double)); - - criterion1 = ((frameCntr == 0) || (frameCntr == (SUBFRAMES - 1))) && - (bandwidth == isac12kHz); - criterion2 = (((frameCntr+1) % 4) == 0) && - (bandwidth == isac16kHz); - if(criterion1 || criterion2) - { - /* add noise */ - corrSubFrame[0] += 1e-6; - /* compute prediction coefficients */ - WebRtcIsac_LevDurb(aPolynom, reflecCoeff, corrSubFrame, - UB_LPC_ORDER); - - /* bandwidth expansion */ - tmp = gamma; - for (n = 1; n <= UB_LPC_ORDER; n++) - { - *lpCoeff++ = aPolynom[n] * tmp; - tmp *= gamma; - } - activeFrameCntr++; - } - } -} - - - -/****************************************************************************** - * WebRtcIsac_GetLpcGain() - * - * Compute the LPC gains for each sub-frame, given the LPC of each sub-frame - * and the corresponding correlation coefficients. - * - * Inputs: - * -signal_noise_ratio : the desired SNR in dB. - * -numVecs : number of sub-frames - * -corrMat : a matrix of correlation coefficients where - * each row is a set of correlation coefficients of - * one sub-frame. - * -varscale : a scale computed when WebRtcIsac_GetLpcCoefUb() - * is called. - * - * Outputs: - * -gain : pointer to a buffer where LP gains are written. - * - */ -void -WebRtcIsac_GetLpcGain( - double signal_noise_ratio, - const double* filtCoeffVecs, - int numVecs, - double* gain, - double corrMat[][UB_LPC_ORDER + 1], - const double* varscale) -{ - WebRtc_Word16 j, n; - WebRtc_Word16 varscaleIdx; - WebRtc_Word16 subFrameCntr; - double aPolynom[ORDERLO + 1]; - double res_nrg; - - const double HearThresOffset = -28.0; - const double H_T_H = pow(10.0, 0.05 * HearThresOffset); - /* divide by sqrt(12) = 3.46 */ - const double S_N_R = pow(10.0, 0.05 * signal_noise_ratio) / 3.46; - - aPolynom[0] = 1; - varscaleIdx = 0; - for(subFrameCntr = 0; subFrameCntr < numVecs; subFrameCntr++) - { - if(subFrameCntr == SUBFRAMES) - { - // we are in second half of a SWB frame. use new varscale - varscale++; - } - memcpy(&aPolynom[1], &filtCoeffVecs[(subFrameCntr * (UB_LPC_ORDER + 1)) + - 1], sizeof(double) * UB_LPC_ORDER); - - /* residual energy */ - res_nrg = 0.0; - for(j = 0; j <= UB_LPC_ORDER; j++) - { - for(n = 0; n <= j; n++) - { - res_nrg += aPolynom[j] * corrMat[subFrameCntr][j-n] * - aPolynom[n]; - } - for(n = j+1; n <= UB_LPC_ORDER; n++) - { - res_nrg += aPolynom[j] * corrMat[subFrameCntr][n-j] * - aPolynom[n]; - } - } - - /* add hearing threshold and compute the gain */ - gain[subFrameCntr] = S_N_R / (sqrt(res_nrg) / *varscale + H_T_H); - } -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_analysis.h b/modules/audio_coding/codecs/iSAC/main/source/lpc_analysis.h deleted file mode 100644 index 9d083f83e..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_analysis.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_analysis.h - * - * LPC functions - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYSIS_H_ - -#include "settings.h" -#include "structs.h" - -/* window */ -extern const double WebRtcIsac_kLpcCorrWindow[256]; - -double WebRtcIsac_LevDurb(double *a, double *k, double *r, int order); - -void WebRtcIsac_GetVars(const double *input, const WebRtc_Word16 *pitchGains_Q12, - double *oldEnergy, double *varscale); - -void WebRtcIsac_GetLpcCoefLb(double *inLo, double *inHi, MaskFiltstr *maskdata, - double signal_noise_ratio, const WebRtc_Word16 *pitchGains_Q12, - double *lo_coeff, double *hi_coeff); - - -void WebRtcIsac_GetLpcGain( - double signal_noise_ratio, - const double* filtCoeffVecs, - int numVecs, - double* gain, - double corrLo[][UB_LPC_ORDER + 1], - const double* varscale); - -void WebRtcIsac_GetLpcCoefUb( - double* inSignal, - MaskFiltstr* maskdata, - double* lpCoeff, - double corr[][UB_LPC_ORDER + 1], - double* varscale, - WebRtc_Word16 bandwidth); - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_ANALYIS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.c b/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.c deleted file mode 100644 index 25c69cbfe..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * SWB_KLT_Tables_LPCGain.c - * - * This file defines tables used for entropy coding of LPC Gain - * of upper-band. - * - */ - -#include "lpc_gain_swb_tables.h" -#include "settings.h" -#include "typedefs.h" - -const double WebRtcIsac_kQSizeLpcGain = 0.100000; - -const double WebRtcIsac_kMeanLpcGain = -3.3822; - -/* -* The smallest reconstruction points for quantiztion of -* LPC gains. -*/ -const double WebRtcIsac_kLeftRecPointLpcGain[SUBFRAMES] = -{ - -0.800000, -1.000000, -1.200000, -2.200000, -3.000000, -12.700000 -}; - -/* -* Number of reconstruction points of quantizers for LPC Gains. -*/ -const WebRtc_Word16 WebRtcIsac_kNumQCellLpcGain[SUBFRAMES] = -{ - 17, 20, 25, 45, 77, 170 -}; -/* -* Starting index for entropy decoder to search for the right interval, -* one entry per LAR coefficient -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcGainEntropySearch[SUBFRAMES] = -{ - 8, 10, 12, 22, 38, 85 -}; - -/* -* The following 6 vectors define CDF of 6 decorrelated LPC -* gains. -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec0[18] = -{ - 0, 10, 27, 83, 234, 568, 1601, 4683, 16830, 57534, 63437, - 64767, 65229, 65408, 65483, 65514, 65527, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec1[21] = -{ - 0, 15, 33, 84, 185, 385, 807, 1619, 3529, 7850, 19488, - 51365, 62437, 64548, 65088, 65304, 65409, 65484, 65507, 65522, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec2[26] = -{ - 0, 15, 29, 54, 89, 145, 228, 380, 652, 1493, 4260, - 12359, 34133, 50749, 57224, 60814, 62927, 64078, 64742, 65103, 65311, 65418, - 65473, 65509, 65521, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec3[46] = -{ - 0, 8, 12, 16, 26, 42, 56, 76, 111, 164, 247, - 366, 508, 693, 1000, 1442, 2155, 3188, 4854, 7387, 11249, 17617, - 30079, 46711, 56291, 60127, 62140, 63258, 63954, 64384, 64690, 64891, 65031, - 65139, 65227, 65293, 65351, 65399, 65438, 65467, 65492, 65504, 65510, 65518, - 65523, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec4[78] = -{ - 0, 17, 29, 39, 51, 70, 104, 154, 234, 324, 443, - 590, 760, 971, 1202, 1494, 1845, 2274, 2797, 3366, 4088, 4905, - 5899, 7142, 8683, 10625, 12983, 16095, 20637, 28216, 38859, 47237, 51537, - 54150, 56066, 57583, 58756, 59685, 60458, 61103, 61659, 62144, 62550, 62886, - 63186, 63480, 63743, 63954, 64148, 64320, 64467, 64600, 64719, 64837, 64939, - 65014, 65098, 65160, 65211, 65250, 65290, 65325, 65344, 65366, 65391, 65410, - 65430, 65447, 65460, 65474, 65487, 65494, 65501, 65509, 65513, 65518, 65520, - 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec5[171] = -{ - 0, 10, 12, 14, 16, 18, 23, 29, 35, 42, 51, - 58, 65, 72, 78, 87, 96, 103, 111, 122, 134, 150, - 167, 184, 202, 223, 244, 265, 289, 315, 346, 379, 414, - 450, 491, 532, 572, 613, 656, 700, 751, 802, 853, 905, - 957, 1021, 1098, 1174, 1250, 1331, 1413, 1490, 1565, 1647, 1730, - 1821, 1913, 2004, 2100, 2207, 2314, 2420, 2532, 2652, 2783, 2921, - 3056, 3189, 3327, 3468, 3640, 3817, 3993, 4171, 4362, 4554, 4751, - 4948, 5142, 5346, 5566, 5799, 6044, 6301, 6565, 6852, 7150, 7470, - 7797, 8143, 8492, 8835, 9181, 9547, 9919, 10315, 10718, 11136, 11566, - 12015, 12482, 12967, 13458, 13953, 14432, 14903, 15416, 15936, 16452, 16967, - 17492, 18024, 18600, 19173, 19736, 20311, 20911, 21490, 22041, 22597, 23157, - 23768, 24405, 25034, 25660, 26280, 26899, 27614, 28331, 29015, 29702, 30403, - 31107, 31817, 32566, 33381, 34224, 35099, 36112, 37222, 38375, 39549, 40801, - 42074, 43350, 44626, 45982, 47354, 48860, 50361, 51845, 53312, 54739, 56026, - 57116, 58104, 58996, 59842, 60658, 61488, 62324, 63057, 63769, 64285, 64779, - 65076, 65344, 65430, 65500, 65517, 65535 -}; - -/* -* An array of pointers to CDFs of decorrelated LPC Gains -*/ -const WebRtc_UWord16* WebRtcIsac_kLpcGainCdfMat[SUBFRAMES] = -{ - WebRtcIsac_kLpcGainCdfVec0, WebRtcIsac_kLpcGainCdfVec1, - WebRtcIsac_kLpcGainCdfVec2, WebRtcIsac_kLpcGainCdfVec3, - WebRtcIsac_kLpcGainCdfVec4, WebRtcIsac_kLpcGainCdfVec5 -}; - -/* -* A matrix to decorrellate LPC gains of subframes. -*/ -const double WebRtcIsac_kLpcGainDecorrMat[SUBFRAMES][SUBFRAMES] = -{ - {-0.150860, 0.327872, 0.367220, 0.504613, 0.559270, 0.409234}, - { 0.457128, -0.613591, -0.289283, -0.029734, 0.393760, 0.418240}, - {-0.626043, 0.136489, -0.439118, -0.448323, 0.135987, 0.420869}, - { 0.526617, 0.480187, 0.242552, -0.488754, -0.158713, 0.411331}, - {-0.302587, -0.494953, 0.588112, -0.063035, -0.404290, 0.387510}, - { 0.086378, 0.147714, -0.428875, 0.548300, -0.570121, 0.401391} -}; diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.h b/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.h deleted file mode 100644 index 1eba97c8b..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_gain_swb_tables.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * SWB_KLT_Tables_LPCGain.h - * - * This file declares tables used for entropy coding of LPC Gain - * of upper-band. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ - -#include "settings.h" -#include "typedefs.h" - -extern const double WebRtcIsac_kQSizeLpcGain; - -extern const double WebRtcIsac_kLeftRecPointLpcGain[SUBFRAMES]; - -extern const WebRtc_Word16 WebRtcIsac_kNumQCellLpcGain[SUBFRAMES]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainEntropySearch[SUBFRAMES]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec0[18]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec1[21]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec2[26]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec3[46]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec4[78]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcGainCdfVec5[171]; - -extern const WebRtc_UWord16* WebRtcIsac_kLpcGainCdfMat[SUBFRAMES]; - -extern const double WebRtcIsac_kLpcGainDecorrMat[SUBFRAMES][SUBFRAMES]; - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_GAIN_SWB_TABLES_H_ diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.c b/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.c deleted file mode 100644 index 695d58327..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * SWB_KLT_Tables.c - * - * This file defines tables used for entropy coding of LPC shape of - * upper-band signal if the bandwidth is 12 kHz. - * - */ - -#include "lpc_shape_swb12_tables.h" -#include "settings.h" -#include "typedefs.h" - -/* -* Mean value of LAR -*/ -const double WebRtcIsac_kMeanLarUb12[UB_LPC_ORDER] = -{ - 0.03748928306641, 0.09453441192543, -0.01112522344398, 0.03800237516842 -}; - -/* -* A rotation matrix to decorrelate intra-vector correlation, -* i.e. correlation among components of LAR vector. -*/ -const double WebRtcIsac_kIntraVecDecorrMatUb12[UB_LPC_ORDER][UB_LPC_ORDER] = -{ - {-0.00075365493856, -0.05809964887743, -0.23397966154116, 0.97050367376411}, - { 0.00625021257734, -0.17299965610679, 0.95977735920651, 0.22104179375008}, - { 0.20543384258374, -0.96202143495696, -0.15301870801552, -0.09432375099565}, - {-0.97865075648479, -0.20300322280841, -0.02581111653779, -0.01913568980258} -}; - -/* -* A rotation matrix to remove correlation among LAR coefficients -* of different LAR vectors. One might guess that decorrelation matrix -* for the first component should differ from the second component -* but we haven't observed a significant benefit of having different -* decorrelation matrices for different components. -*/ -const double WebRtcIsac_kInterVecDecorrMatUb12 -[UB_LPC_VEC_PER_FRAME][UB_LPC_VEC_PER_FRAME] = -{ - { 0.70650597970460, -0.70770707262373}, - {-0.70770707262373, -0.70650597970460} -}; - -/* -* LAR quantization step-size. -*/ -const double WebRtcIsac_kLpcShapeQStepSizeUb12 = 0.150000; - -/* -* The smallest reconstruction points for quantiztion of LAR coefficients. -*/ -const double WebRtcIsac_kLpcShapeLeftRecPointUb12 -[UB_LPC_ORDER*UB_LPC_VEC_PER_FRAME] = -{ - -0.900000, -1.050000, -1.350000, -1.800000, -1.350000, -1.650000, - -2.250000, -3.450000 -}; - -/* -* Number of reconstruction points of quantizers for LAR coefficients. -*/ -const WebRtc_Word16 WebRtcIsac_kLpcShapeNumRecPointUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME] = -{ - 13, 15, 19, 27, 19, 24, 32, 48 -}; - -/* -* Starting index for entropy decoder to search for the right interval, -* one entry per LAR coefficient -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcShapeEntropySearchUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME] = -{ - 6, 7, 9, 13, 9, 12, 16, 24 -}; - -/* -* The following 8 vectors define CDF of 8 decorrelated LAR -* coefficients. -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec0Ub12[14] = -{ - 0, 13, 95, 418, 1687, 6498, 21317, 44200, 59029, 63849, 65147, - 65449, 65525, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec1Ub12[16] = -{ - 0, 10, 59, 255, 858, 2667, 8200, 22609, 42988, 57202, 62947, - 64743, 65308, 65476, 65522, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec2Ub12[20] = -{ - 0, 18, 40, 118, 332, 857, 2017, 4822, 11321, 24330, 41279, - 54342, 60637, 63394, 64659, 65184, 65398, 65482, 65518, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec3Ub12[28] = -{ - 0, 21, 38, 90, 196, 398, 770, 1400, 2589, 4650, 8211, - 14933, 26044, 39592, 50814, 57452, 60971, 62884, 63995, 64621, 65019, 65273, - 65410, 65480, 65514, 65522, 65531, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec4Ub12[20] = -{ - 0, 7, 46, 141, 403, 969, 2132, 4649, 10633, 24902, 43254, - 54665, 59928, 62674, 64173, 64938, 65293, 65464, 65523, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec5Ub12[25] = -{ - 0, 7, 22, 72, 174, 411, 854, 1737, 3545, 6774, 13165, - 25221, 40980, 52821, 58714, 61706, 63472, 64437, 64989, 65287, 65430, 65503, - 65525, 65529, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec6Ub12[33] = -{ - 0, 11, 21, 36, 65, 128, 228, 401, 707, 1241, 2126, - 3589, 6060, 10517, 18853, 31114, 42477, 49770, 54271, 57467, 59838, 61569, - 62831, 63772, 64433, 64833, 65123, 65306, 65419, 65466, 65499, 65519, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec7Ub12[49] = -{ - 0, 14, 34, 67, 107, 167, 245, 326, 449, 645, 861, - 1155, 1508, 2003, 2669, 3544, 4592, 5961, 7583, 9887, 13256, 18765, - 26519, 34077, 40034, 44349, 47795, 50663, 53262, 55473, 57458, 59122, 60592, - 61742, 62690, 63391, 63997, 64463, 64794, 65045, 65207, 65309, 65394, 65443, - 65478, 65504, 65514, 65523, 65535 -}; - -/* -* An array of pointers to CDFs of decorrelated LARs -*/ -const WebRtc_UWord16* WebRtcIsac_kLpcShapeCdfMatUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME] = -{ - WebRtcIsac_kLpcShapeCdfVec0Ub12, WebRtcIsac_kLpcShapeCdfVec1Ub12, - WebRtcIsac_kLpcShapeCdfVec2Ub12, WebRtcIsac_kLpcShapeCdfVec3Ub12, - WebRtcIsac_kLpcShapeCdfVec4Ub12, WebRtcIsac_kLpcShapeCdfVec5Ub12, - WebRtcIsac_kLpcShapeCdfVec6Ub12, WebRtcIsac_kLpcShapeCdfVec7Ub12 -}; diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.h b/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.h deleted file mode 100644 index 1e93847fa..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb12_tables.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_shape_swb12_tables.h - * - * This file declares tables used for entropy coding of LPC shape of - * upper-band signal if the bandwidth is 12 kHz. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ - -#include "settings.h" -#include "typedefs.h" - -extern const double WebRtcIsac_kMeanLarUb12[UB_LPC_ORDER]; - -extern const double WebRtcIsac_kMeanLpcGain; - -extern const double WebRtcIsac_kIntraVecDecorrMatUb12[UB_LPC_ORDER][UB_LPC_ORDER]; - -extern const double WebRtcIsac_kInterVecDecorrMatUb12 -[UB_LPC_VEC_PER_FRAME][UB_LPC_VEC_PER_FRAME]; - -extern const double WebRtcIsac_kLpcShapeQStepSizeUb12; - -extern const double WebRtcIsac_kLpcShapeLeftRecPointUb12 -[UB_LPC_ORDER*UB_LPC_VEC_PER_FRAME]; - - -extern const WebRtc_Word16 WebRtcIsac_kLpcShapeNumRecPointUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeEntropySearchUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec0Ub12[14]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec1Ub12[16]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec2Ub12[20]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec3Ub12[28]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec4Ub12[20]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec5Ub12[25]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec6Ub12[33]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec7Ub12[49]; - -extern const WebRtc_UWord16* WebRtcIsac_kLpcShapeCdfMatUb12 -[UB_LPC_ORDER * UB_LPC_VEC_PER_FRAME]; - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB12_TABLES_H_ diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.c b/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.c deleted file mode 100644 index 1480fe90b..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * SWB16_KLT_Tables.c - * - * This file defines tables used for entropy coding of LPC shape of - * upper-band signal if the bandwidth is 16 kHz. - * - */ - -#include "lpc_shape_swb16_tables.h" -#include "settings.h" -#include "typedefs.h" - -/* -* Mean value of LAR -*/ -const double meanLARUB16[UB_LPC_ORDER] = -{ -0.454978, 0.364747, 0.102999, 0.104523 -}; - -/* -* A rotation matrix to decorrelate intra-vector correlation, -* i.e. correlation among components of LAR vector. -*/ -const double WebRtcIsac_kIintraVecDecorrMatUb16[UB_LPC_ORDER][UB_LPC_ORDER] = -{ - {-0.020528, -0.085858, -0.002431, 0.996093}, - {-0.033155, 0.036102, 0.998786, 0.004866}, - { 0.202627, 0.974853, -0.028940, 0.088132}, - {-0.978479, 0.202454, -0.039785, -0.002811} -}; - -/* -* A rotation matrix to remove correlation among LAR coefficients -* of different LAR vectors. One might guess that decorrelation matrix -* for the first component should differ from the second component -* but we haven't observed a significant benefit of having different -* decorrelation matrices for different components. -*/ -const double WebRtcIsac_kInterVecDecorrMatUb16 -[UB16_LPC_VEC_PER_FRAME][UB16_LPC_VEC_PER_FRAME] = -{ - { 0.291675, -0.515786, 0.644927, 0.482658}, - {-0.647220, 0.479712, 0.289556, 0.516856}, - { 0.643084, 0.485489, -0.289307, 0.516763}, - {-0.287185, -0.517823, -0.645389, 0.482553} -}; - -/* -* The following 16 vectors define CDF of 16 decorrelated LAR -* coefficients. -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub16[14] = -{ - 0, 2, 20, 159, 1034, 5688, 20892, 44653, - 59849, 64485, 65383, 65518, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec1Ub16[16] = -{ - 0, 1, 7, 43, 276, 1496, 6681, 21653, - 43891, 58859, 64022, 65248, 65489, 65529, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec2Ub16[18] = -{ - 0, 1, 9, 54, 238, 933, 3192, 9461, - 23226, 42146, 56138, 62413, 64623, 65300, 65473, 65521, - 65533, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec3Ub16[30] = -{ - 0, 2, 4, 8, 17, 36, 75, 155, - 329, 683, 1376, 2662, 5047, 9508, 17526, 29027, - 40363, 48997, 55096, 59180, 61789, 63407, 64400, 64967, - 65273, 65429, 65497, 65526, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec4Ub16[16] = -{ - 0, 1, 10, 63, 361, 1785, 7407, 22242, - 43337, 58125, 63729, 65181, 65472, 65527, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec5Ub16[17] = -{ - 0, 1, 7, 29, 134, 599, 2443, 8590, - 22962, 42635, 56911, 63060, 64940, 65408, 65513, 65531, - 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec6Ub16[21] = -{ - 0, 1, 5, 16, 57, 191, 611, 1808, - 4847, 11755, 24612, 40910, 53789, 60698, 63729, 64924, - 65346, 65486, 65523, 65532, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec7Ub16[36] = -{ - 0, 1, 4, 12, 25, 55, 104, 184, - 314, 539, 926, 1550, 2479, 3861, 5892, 8845, - 13281, 20018, 29019, 38029, 45581, 51557, 56057, 59284, - 61517, 63047, 64030, 64648, 65031, 65261, 65402, 65480, - 65518, 65530, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec8Ub16[21] = -{ - 0, 1, 2, 7, 26, 103, 351, 1149, - 3583, 10204, 23846, 41711, 55361, 61917, 64382, 65186, - 65433, 65506, 65528, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub160[21] = -{ - 0, 6, 19, 63, 205, 638, 1799, 4784, - 11721, 24494, 40803, 53805, 60886, 63822, 64931, 65333, - 65472, 65517, 65530, 65533, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub161[28] = -{ - 0, 1, 3, 11, 31, 86, 221, 506, - 1101, 2296, 4486, 8477, 15356, 26079, 38941, 49952, - 57165, 61257, 63426, 64549, 65097, 65351, 65463, 65510, - 65526, 65532, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub162[55] = -{ - 0, 3, 12, 23, 42, 65, 89, 115, - 150, 195, 248, 327, 430, 580, 784, 1099, - 1586, 2358, 3651, 5899, 9568, 14312, 19158, 23776, - 28267, 32663, 36991, 41153, 45098, 48680, 51870, 54729, - 57141, 59158, 60772, 62029, 63000, 63761, 64322, 64728, - 65000, 65192, 65321, 65411, 65463, 65496, 65514, 65523, - 65527, 65529, 65531, 65532, 65533, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub163[26] = -{ - 0, 2, 4, 10, 21, 48, 114, 280, - 701, 1765, 4555, 11270, 24267, 41213, 54285, 61003, - 63767, 64840, 65254, 65421, 65489, 65514, 65526, 65532, - 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub164[28] = -{ - 0, 1, 3, 6, 15, 36, 82, 196, - 453, 1087, 2557, 5923, 13016, 25366, 40449, 52582, - 59539, 62896, 64389, 65033, 65316, 65442, 65494, 65519, - 65529, 65533, 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub165[34] = -{ - 0, 2, 4, 8, 18, 35, 73, 146, - 279, 524, 980, 1789, 3235, 5784, 10040, 16998, - 27070, 38543, 48499, 55421, 59712, 62257, 63748, 64591, - 65041, 65278, 65410, 65474, 65508, 65522, 65530, 65533, - 65534, 65535 -}; - -const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub166[71] = -{ - 0, 1, 2, 6, 13, 26, 55, 92, - 141, 191, 242, 296, 355, 429, 522, 636, - 777, 947, 1162, 1428, 1753, 2137, 2605, 3140, - 3743, 4409, 5164, 6016, 6982, 8118, 9451, 10993, - 12754, 14810, 17130, 19780, 22864, 26424, 30547, 35222, - 40140, 44716, 48698, 52056, 54850, 57162, 59068, 60643, - 61877, 62827, 63561, 64113, 64519, 64807, 65019, 65167, - 65272, 65343, 65399, 65440, 65471, 65487, 65500, 65509, - 65518, 65524, 65527, 65531, 65533, 65534, 65535 -}; - -/* -* An array of pointers to CDFs of decorrelated LARs -*/ -const WebRtc_UWord16* WebRtcIsac_kLpcShapeCdfMatUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME] = { - WebRtcIsac_kLpcShapeCdfVec01Ub16, - WebRtcIsac_kLpcShapeCdfVec1Ub16, - WebRtcIsac_kLpcShapeCdfVec2Ub16, - WebRtcIsac_kLpcShapeCdfVec3Ub16, - WebRtcIsac_kLpcShapeCdfVec4Ub16, - WebRtcIsac_kLpcShapeCdfVec5Ub16, - WebRtcIsac_kLpcShapeCdfVec6Ub16, - WebRtcIsac_kLpcShapeCdfVec7Ub16, - WebRtcIsac_kLpcShapeCdfVec8Ub16, - WebRtcIsac_kLpcShapeCdfVec01Ub160, - WebRtcIsac_kLpcShapeCdfVec01Ub161, - WebRtcIsac_kLpcShapeCdfVec01Ub162, - WebRtcIsac_kLpcShapeCdfVec01Ub163, - WebRtcIsac_kLpcShapeCdfVec01Ub164, - WebRtcIsac_kLpcShapeCdfVec01Ub165, - WebRtcIsac_kLpcShapeCdfVec01Ub166 -}; - -/* -* The smallest reconstruction points for quantiztion of LAR coefficients. -*/ -const double WebRtcIsac_kLpcShapeLeftRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME] = -{ - -0.8250, -0.9750, -1.1250, -2.1750, -0.9750, -1.1250, -1.4250, - -2.6250, -1.4250, -1.2750, -1.8750, -3.6750, -1.7250, -1.8750, - -2.3250, -5.4750 -}; - -/* -* Number of reconstruction points of quantizers for LAR coefficients. -*/ -const WebRtc_Word16 WebRtcIsac_kLpcShapeNumRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME] = -{ - 13, 15, 17, 29, 15, 16, 20, 35, 20, - 20, 27, 54, 25, 27, 33, 70 -}; - -/* -* Starting index for entropy decoder to search for the right interval, -* one entry per LAR coefficient -*/ -const WebRtc_UWord16 WebRtcIsac_kLpcShapeEntropySearchUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME] = -{ - 6, 7, 8, 14, 7, 8, 10, 17, 10, - 10, 13, 27, 12, 13, 16, 35 -}; - -/* -* LAR quantization step-size. -*/ -const double WebRtcIsac_kLpcShapeQStepSizeUb16 = 0.150000; diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.h b/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.h deleted file mode 100644 index 082f4a8a9..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_shape_swb16_tables.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_shape_swb16_tables.h - * - * This file declares tables used for entropy coding of LPC shape of - * upper-band signal if the bandwidth is 16 kHz. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ - -#include "settings.h" -#include "typedefs.h" - - -extern const double meanLARUB16[UB_LPC_ORDER]; - -extern const double WebRtcIsac_kIintraVecDecorrMatUb16[UB_LPC_ORDER][UB_LPC_ORDER]; - -extern const double WebRtcIsac_kInterVecDecorrMatUb16 -[UB16_LPC_VEC_PER_FRAME][UB16_LPC_VEC_PER_FRAME]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub16[14]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec1Ub16[16]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec2Ub16[18]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec3Ub16[30]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec4Ub16[16]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec5Ub16[17]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec6Ub16[21]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec7Ub16[36]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec8Ub16[21]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub160[21]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub161[28]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub162[55]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub163[26]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub164[28]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub165[34]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeCdfVec01Ub166[71]; - -extern const WebRtc_UWord16* WebRtcIsac_kLpcShapeCdfMatUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - -extern const double WebRtcIsac_kLpcShapeLeftRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - -extern const WebRtc_Word16 WebRtcIsac_kLpcShapeNumRecPointUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - -extern const WebRtc_UWord16 WebRtcIsac_kLpcShapeEntropySearchUb16 -[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - -extern const double WebRtcIsac_kLpcShapeQStepSizeUb16; - -#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_SHAPE_SWB16_TABLES_H_ diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.c b/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.c deleted file mode 100644 index 7df612146..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.c +++ /dev/null @@ -1,1129 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* coding tables for the KLT coefficients */ - -#include "lpc_tables.h" -#include "settings.h" - -/* indices of KLT coefficients used */ -const WebRtc_UWord16 WebRtcIsac_kQKltSelIndGain[12] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11}; - -const WebRtc_UWord16 WebRtcIsac_kQKltSelIndShape[108] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 101, 102, 103, 104, 105, 106, 107}; - -/* cdf array for model indicator */ -const WebRtc_UWord16 WebRtcIsac_kQKltModelCdf[4] = { - 0, 15434, 37548, 65535}; - -/* pointer to cdf array for model indicator */ -const WebRtc_UWord16 *WebRtcIsac_kQKltModelCdfPtr[1] = {WebRtcIsac_kQKltModelCdf}; - -/* initial cdf index for decoder of model indicator */ -const WebRtc_UWord16 WebRtcIsac_kQKltModelInitIndex[1] = {1}; - -/* offset to go from rounded value to quantization index */ -const short WebRtcIsac_kQKltQuantMinGain[12] = { - 3, 6, 4, 6, 6, 9, 5, 16, 11, 34, 32, 47}; - - -const short WebRtcIsac_kQKltQuantMinShape[108] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 2, 2, 3, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 4, 3, 5, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 2, 1, 2, 2, 3, 4, - 4, 7, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 2, 3, 2, 3, 4, 4, 5, 7, 13, - 0, 1, 1, 2, 3, 2, 2, 2, 4, 4, - 5, 6, 7, 11, 9, 13, 12, 26}; - -/* maximum quantization index */ -const WebRtc_UWord16 WebRtcIsac_kQKltMaxIndGain[12] = { - 6, 12, 8, 14, 10, 19, 12, 31, 22, 56, 52, 138}; - -const WebRtc_UWord16 WebRtcIsac_kQKltMaxIndShape[108] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 2, 2, 2, 2, 4, 4, 5, 6, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 2, 2, - 2, 2, 3, 4, 5, 7, 0, 0, 0, 0, - 2, 0, 2, 2, 2, 2, 3, 2, 2, 4, - 4, 6, 6, 9, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 3, 2, 4, 4, 7, 7, - 9, 13, 0, 0, 2, 2, 2, 2, 2, 2, - 3, 4, 5, 4, 6, 8, 8, 10, 16, 25, - 0, 2, 2, 4, 5, 4, 4, 4, 7, 8, - 9, 10, 13, 19, 17, 23, 25, 49}; - -/* index offset */ -const WebRtc_UWord16 WebRtcIsac_kQKltOffsetGain[3][12] = { -{ 0, 7, 20, 29, 44, 55, 75, 88, 120, 143, - 200, 253}, -{ 0, 7, 19, 27, 42, 53, 73, 86, 117, 140, - 197, 249}, -{ 0, 7, 20, 28, 44, 55, 75, 89, 121, 145, - 202, 257}}; - -const WebRtc_UWord16 WebRtcIsac_kQKltOffsetShape[3][108] = { -{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 11, 14, 17, 20, 23, 28, 33, 39, 46, 47, - 48, 49, 50, 52, 53, 54, 55, 56, 58, 61, - 64, 67, 70, 74, 79, 85, 93, 94, 95, 96, - 97, 100, 101, 104, 107, 110, 113, 117, 120, 123, - 128, 133, 140, 147, 157, 158, 159, 160, 161, 164, - 167, 170, 173, 176, 179, 183, 186, 191, 196, 204, - 212, 222, 236, 237, 238, 241, 244, 247, 250, 253, - 256, 260, 265, 271, 276, 283, 292, 301, 312, 329, - 355, 356, 359, 362, 367, 373, 378, 383, 388, 396, - 405, 415, 426, 440, 460, 478, 502, 528}, -{ 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, - 13, 16, 19, 22, 26, 29, 34, 39, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 55, 57, 60, - 63, 66, 70, 73, 78, 84, 91, 92, 93, 94, - 95, 96, 97, 99, 102, 105, 108, 111, 114, 118, - 123, 128, 134, 141, 151, 152, 153, 154, 156, 159, - 162, 165, 168, 171, 174, 177, 181, 186, 194, 200, - 208, 218, 233, 234, 235, 236, 239, 242, 245, 248, - 251, 254, 258, 263, 270, 277, 288, 297, 308, 324, - 349, 351, 354, 357, 361, 366, 372, 378, 383, 390, - 398, 407, 420, 431, 450, 472, 496, 524}, -{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, - 14, 17, 20, 23, 26, 29, 34, 40, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 58, 61, 64, - 67, 70, 73, 77, 82, 88, 96, 97, 98, 99, - 101, 102, 104, 107, 110, 113, 116, 119, 122, 125, - 129, 134, 141, 150, 160, 161, 162, 163, 166, 168, - 171, 174, 177, 180, 183, 186, 190, 195, 201, 208, - 216, 226, 243, 244, 245, 248, 251, 254, 257, 260, - 263, 268, 273, 278, 284, 291, 299, 310, 323, 340, - 366, 368, 371, 374, 379, 383, 389, 394, 399, 406, - 414, 422, 433, 445, 461, 480, 505, 533}}; - -/* initial cdf index for KLT coefficients */ -const WebRtc_UWord16 WebRtcIsac_kQKltInitIndexGain[3][12] = { -{ 3, 6, 4, 7, 5, 10, 6, 16, 11, 28, - 26, 69}, -{ 3, 6, 4, 7, 5, 10, 6, 15, 11, 28, - 26, 69}, -{ 3, 6, 4, 8, 5, 10, 7, 16, 12, 28, - 27, 70}}; - -const WebRtc_UWord16 WebRtcIsac_kQKltInitIndexShape[3][108] = { -{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 2, 2, 3, 3, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 2, 2, 3, 4, 0, 0, 0, 0, - 1, 0, 1, 1, 1, 1, 2, 1, 1, 2, - 2, 3, 3, 5, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 2, 1, 2, 2, 4, 4, - 5, 7, 0, 0, 1, 1, 1, 1, 1, 1, - 2, 2, 3, 2, 3, 4, 4, 5, 8, 13, - 0, 1, 1, 2, 3, 2, 2, 2, 4, 4, - 5, 5, 7, 10, 9, 12, 13, 25}, -{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 1, 1, 1, 2, 1, 2, 2, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 2, 1, 2, 3, 3, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, - 2, 3, 3, 5, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 2, 4, 3, 4, - 5, 7, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 2, 2, 3, 3, 5, 4, 5, 8, 12, - 1, 1, 1, 2, 2, 3, 3, 2, 3, 4, - 4, 6, 5, 9, 11, 12, 14, 25}, -{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 2, 3, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 2, 2, 3, 4, 0, 0, 0, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 3, 4, 5, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, - 5, 8, 0, 0, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 3, 3, 4, 5, 6, 8, 13, - 1, 1, 1, 2, 2, 3, 2, 2, 3, 4, - 4, 5, 6, 8, 9, 12, 14, 25}}; - -/* offsets for quantizer representation levels*/ -const WebRtc_UWord16 WebRtcIsac_kQKltOfLevelsGain[3] = { - 0, 392, 779}; - -const WebRtc_UWord16 WebRtcIsac_kQKltOfLevelsShape[3] = { - 0, 578, 1152}; - -/* quantizer representation levels */ -const double WebRtcIsac_kQKltLevelsGain[1176] = { --2.78127126, -1.76745590, -0.77913790, -0.00437329, 0.79961206, 1.81775776, 2.81389782, -5.78753143, -4.88384084, -3.89320940, --2.88133610, -1.92859977, -0.86347396, 0.02003888, 0.86140400, 1.89667156, 2.97134967, 3.98781964, 4.91727277, 5.82865898, --4.11195874, -2.80898424, -1.87547977, -0.80943825, -0.00679084, 0.79573851, 1.83953397, 2.67586037, 3.76274082, -6.10933968, --4.93034581, -3.89281296, -2.91530625, -1.89684163, -0.85319130, -0.02275767, 0.86862017, 1.91578276, 2.96107339, 3.96543056, - 4.91369908, 5.91058154, 6.83848343, 8.07136925, -5.87470395, -4.84703049, -3.84284597, -2.86168446, -1.89290192, -0.82798145, --0.00080013, 0.82594974, 1.85754329, 2.88351798, 3.96172628, -8.85684885, -7.87387461, -6.97811862, -5.93256270, -4.94301439, --3.95513701, -2.96041544, -1.94031192, -0.87961478, -0.00456201, 0.89911505, 1.91723376, 2.94011511, 3.93302540, 4.97990967, - 5.93133404, 7.02181199, 7.92407762, 8.80155440, 10.04665814, -4.82396678, -3.85612158, -2.89482244, -1.89558408, -0.90036978, --0.00677823, 0.90607989, 1.90937981, 2.91175777, 3.91637730, 4.97565723, 5.84771228, 7.11145863, -16.07879840, -15.03776309, --13.93905670, -12.95671800, -11.89171202, -10.95820934, -9.95923714, -8.94357334, -7.99068299, -6.97481009, -5.94826231, -4.96673988, --3.97490466, -2.97846970, -1.95130435, -0.94215262, -0.01444043, 0.96770704, 1.95848598, 2.94107862, 3.95666119, 4.97253085, - 5.97191122, 6.93277360, 7.96608727, 8.87958779, 10.00264269, 10.86560820, 12.07449071, 13.04491775, 13.97507061, 14.91845261, --10.85696295, -9.83365357, -9.01245635, -7.95915145, -6.95625003, -5.95362618, -4.93468444, -3.98760978, -2.95044407, -1.97041277, --0.97701799, -0.00840234, 0.97834289, 1.98361415, 2.97802439, 3.96415871, 4.95369042, 5.94101770, 6.92756798, 7.94063998, - 8.85951828, 9.97077022, 11.00068503, -33.92030406, -32.81426422, -32.00000000, -31.13243639, -30.11886909, -29.06017570, -28.12598824, --27.22045482, -25.81215858, -25.07849962, -23.93018013, -23.02097643, -21.89529725, -20.99091085, -19.98889048, -18.94327044, -17.96562071, --16.96126218, -15.95054062, -14.98516200, -13.97101012, -13.02106500, -11.98438006, -11.03216748, -9.95930286, -8.97043946, -7.98085082, --6.98360995, -5.98998802, -4.98668173, -4.00032906, -3.00420619, -1.98701132, -0.99324682, -0.00609324, 0.98297834, 1.99483076, - 3.00305044, 3.97142097, 4.97525759, 5.98612258, 6.97448236, 7.97575900, 9.01086211, 9.98665542, 11.00541438, 11.98078628, - 12.92352471, 14.06849675, 14.99949430, 15.94904834, 16.97440321, 18.04040916, 18.88987609, 20.05312391, 21.00000000, 21.79443341, --31.98578825, -31.00000000, -29.89060567, -28.98555686, -27.97114102, -26.84935410, -26.02402230, -24.94195278, -23.92336849, -22.95552382, --21.97932836, -20.96055470, -19.99649553, -19.03436122, -17.96706525, -17.01139515, -16.01363516, -14.99154248, -14.00298333, -12.99630613, --11.99955519, -10.99000421, -10.00819092, -8.99763648, -7.98431793, -7.01769025, -5.99604690, -4.99980697, -3.99334671, -3.01748192, --2.02051217, -1.00848371, -0.01942358, 1.00477757, 1.95477872, 2.98593031, 3.98779079, 4.96862849, 6.02694771, 6.93983733, - 7.89874717, 8.99615862, 10.02367921, 10.96293452, 11.84351528, 12.92207187, 13.85122329, 15.05146877, 15.99371264, 17.00000000, - 18.00000000, 19.00000000, 19.82763573, -47.00000000, -46.00000000, -44.87138498, -44.00000000, -43.00000000, -42.00000000, -41.00000000, --39.88966612, -38.98913239, -37.80306486, -37.23584325, -35.94200288, -34.99881301, -34.11361858, -33.06507360, -32.13129135, -30.90891364, --29.81511907, -28.99250380, -28.04535391, -26.99767800, -26.04418164, -24.95687851, -24.04865595, -23.03392645, -21.89366707, -20.93517364, --19.99388660, -18.91620943, -18.03749683, -16.99532379, -15.98683813, -15.06421479, -13.99359211, -12.99714098, -11.97022520, -10.98500279, --9.98834422, -8.95729330, -8.01232284, -7.00253661, -5.99681626, -5.01207817, -3.95914904, -3.01232178, -1.96615919, -0.97687670, - 0.01228030, 0.98412288, 2.01753544, 3.00580570, 3.97783510, 4.98846894, 6.01321400, 7.00867732, 8.00416375, 9.01771966, - 9.98637729, 10.98255180, 11.99194163, 13.01807333, 14.00999545, 15.00118556, 16.00089224, 17.00584148, 17.98251763, 18.99942091, - 19.96917690, 20.97839265, 21.98207297, 23.00171271, 23.99930737, 24.99746061, 26.00936304, 26.98240132, 28.01126868, 29.01395915, - 29.98153507, 31.01376711, 31.99876818, 33.00475317, 33.99753994, 34.99493913, 35.98933585, 36.95620160, 37.98428461, 38.99317544, - 40.01832073, 40.98048133, 41.95999283, 42.98232091, 43.96523612, 44.99574268, 45.99524194, 47.05464025, 48.03821548, 48.99354366, - 49.96400411, 50.98017973, 51.95184408, 52.96291806, 54.00194392, 54.96603783, 55.95623778, 57.03076595, 58.05889901, 58.99081551, - 59.97928121, 61.05071612, 62.03971580, 63.01286038, 64.01290338, 65.02074503, 65.99454594, 67.00399425, 67.96571257, 68.95305727, - 69.92030664, 70.95594862, 71.98088567, 73.04764124, 74.00285480, 75.02696330, 75.89837673, 76.93459997, 78.16266309, 78.83317543, - 80.00000000, 80.87251574, 82.09803524, 83.10671664, 84.00000000, 84.77023523, 86.00000000, 87.00000000, 87.92946897, 88.69159118, - 90.00000000, 90.90535270, -3.00000000, -2.00000000, -0.77592424, -0.00564307, 0.76727305, 2.00000000, 3.00000000, -6.00000000, --5.00000000, -4.00000000, -2.92897924, -1.85623684, -0.72445303, -0.00119184, 0.72896652, 2.05710416, 3.17909894, 4.00000000, - 5.00000000, -3.00000000, -2.00000000, -0.67480586, -0.00028016, 0.66618169, 2.00000000, 3.00000000, 4.00000000, -7.00000000, --6.00000000, -5.00000000, -3.78336783, -2.84811556, -2.04088844, -0.71114371, 0.03142493, 0.69662772, 1.91417930, 3.00000000, - 4.01411062, 5.00000000, 6.00000000, 7.00000000, -6.00000000, -5.00000000, -4.00000000, -3.00000000, -2.00000000, -0.63703469, - 0.00169604, 0.66294191, 1.83808563, 3.00000000, 4.00000000, -8.00000000, -7.00000000, -6.03082300, -5.00000000, -3.88061019, --2.92670084, -1.99902336, -0.72898996, -0.02880170, 0.73769927, 1.95920233, 2.78356263, 4.08100921, 5.00000000, 6.00000000, - 6.78771437, 8.00000000, 9.00000000, 10.00000000, 11.00000000, -5.00000000, -4.00000000, -2.88150384, -1.89520024, -0.71479482, - 0.00962397, 0.72816030, 1.73583550, 3.00000000, 4.00000000, 5.00000000, 6.00000000, 7.00000000, -16.00000000, -15.00000000, --13.80516401, -13.00000000, -12.00000000, -11.00000000, -10.02723144, -9.11825995, -8.05820112, -7.00000000, -6.17943541, -5.01837980, --3.97546169, -2.92806857, -1.89778775, -0.81138893, -0.02246016, 0.80528415, 1.85705214, 2.96438524, 3.97540151, 4.79684246, - 6.00000000, 6.75549513, 8.12185828, 9.00000000, 10.00000000, 11.00000000, 12.00000000, 13.00000000, 14.00000000, -11.00000000, --10.00000000, -9.00000000, -7.91603344, -6.77865892, -5.85765006, -4.93342332, -3.96679157, -2.84925552, -1.89230732, -0.85384229, - 0.00579591, 0.84863246, 1.89006713, 2.89483818, 3.87322971, 5.13228411, 6.00000000, 7.00000000, 8.00000000, 9.00000000, - 10.00000000, 11.00000000, -34.00000000, -33.00000000, -32.00000000, -31.00000000, -30.00000000, -29.00000000, -28.00000000, -27.00000000, --26.00000000, -25.00000000, -24.00000000, -23.00000000, -22.00000000, -21.00000000, -20.19501953, -19.00000000, -18.00000000, -17.00000000, --16.00000000, -14.89069633, -14.00000000, -13.00000000, -12.16260304, -11.15418282, -9.83543570, -8.85600407, -7.82712677, -7.05664308, --5.97007352, -4.89268438, -3.93822771, -2.94975269, -1.92192127, -0.90702480, 0.03974847, 0.92488359, 1.93747579, 2.94500522, - 3.95181797, 4.95433087, 5.95141808, 7.00212920, 8.02964757, 9.03210585, 9.84644504, 10.82907720, 11.87622530, 12.96908371, - 14.00000000, 15.16963413, 15.94902025, 17.00000000, 18.00000000, 19.00000000, 20.00000000, 21.00000000, 22.00000000, -29.00000000, --27.79780781, -27.00757888, -26.01571026, -24.89695568, -23.99946491, -22.98699614, -21.96678139, -20.99883532, -20.00851529, -18.94738054, --17.98672566, -16.98684787, -15.96917397, -14.99856852, -13.98974852, -12.97786927, -11.96110939, -10.98877093, -9.99875257, -8.99001359, --8.00799989, -6.99471760, -6.00034670, -4.99936372, -4.00581479, -3.00424577, -2.02047620, -0.99713266, -0.00366397, 1.00803955, - 1.98452687, 3.00748501, 4.02714611, 4.97661026, 5.99337271, 6.99754716, 8.00713602, 8.97184974, 9.98047901, 10.97685939, - 11.99533975, 12.96107876, 13.95061478, 15.00756776, 15.94078690, 16.88231059, 17.92069248, 18.78011047, 20.00000000, 21.00000000, - 22.00000000, -55.76988333, -54.96048193, -53.88411581, -52.94117980, -51.80983449, -50.90359699, -50.00000000, -48.99838741, -47.97685542, --47.03288597, -45.97820919, -45.02418374, -43.90081897, -42.88832512, -41.98234549, -40.96745512, -39.98148729, -39.06792854, -37.96493755, --36.98707870, -36.03416079, -35.01192444, -33.95785029, -32.99469087, -31.96633807, -31.01769053, -29.99727691, -28.99329690, -27.98873019, --27.00344273, -25.97657141, -25.00511074, -23.96689479, -23.01566842, -22.01632643, -21.00076343, -19.97788007, -18.97248680, -17.96076284, --16.97585453, -15.98345587, -15.01612745, -13.96862118, -12.96622055, -12.02196641, -11.02078103, -9.98445656, -9.00050060, -8.03442387, --7.00363761, -5.97921358, -4.98886269, -4.00528221, -3.01672947, -1.98599795, -1.00668518, -0.02633490, 1.00794139, 2.00837138, - 2.99213287, 3.98710216, 4.99064334, 6.01416391, 7.01759708, 7.97878151, 8.99665730, 10.02656114, 11.01863887, 12.01207901, - 13.00958725, 13.99237829, 15.00954971, 16.00724653, 17.00606559, 17.99886292, 18.99611967, 19.98808171, 21.01871930, 21.97014763, - 22.99833843, 24.00316842, 24.99949142, 25.98539601, 27.02480733, 27.98075377, 28.98266019, 30.00611445, 30.99409128, 31.94523141, - 32.97688339, 33.98800206, 35.00177074, 35.98639997, 36.98939428, 37.95644255, 39.00114054, 39.99492439, 40.99338254, 41.97050844, - 43.03085663, 43.96757668, 44.97800970, 45.95953358, 46.98109551, 47.99368477, 49.00141209, 49.94459923, 50.93298108, 51.99894661, - 53.06463883, 53.99704669, 55.02037199, 55.98368047, 57.01930954, 58.03813852, 58.96232502, 60.01644186, 61.03254711, 62.01086576, - 62.87962247, 63.98378413, 65.02189831, 65.93003954, 66.92439900, 68.07051633, 68.95928756, 70.03315022, 71.05579859, 72.00000000, - 73.00000000, 74.00000000, 75.00000000, 75.93485291, 77.20950456, 78.00000000, 79.00000000, 79.91519960, 81.00000000, -3.00000000, --2.00000000, -0.65174074, -0.00092112, 0.62967387, 2.00000000, 3.00000000, -6.00000000, -5.00000000, -4.00000000, -2.89861729, --1.69999061, -0.72632201, 0.00219241, 0.72891750, 1.73257865, 3.00000000, 3.76561508, 5.00000000, 6.00000000, -3.00000000, --2.00000000, -0.66227013, 0.00389373, 0.66163500, 2.00000000, 3.00000000, 4.00000000, -8.00000000, -7.00000000, -6.00000000, --4.76421796, -4.04320264, -3.01415201, -1.84346485, -0.77185048, 0.00061977, 0.76274524, 1.84330156, 3.00000000, 4.00000000, - 5.00000000, 6.00000000, 7.00000000, -6.00000000, -5.00000000, -4.00000000, -3.00000000, -1.75749611, -0.72951347, -0.00104394, - 0.72040315, 1.72594036, 3.00000000, 4.00000000, -9.00000000, -8.00000000, -7.00000000, -5.90394062, -5.00000000, -3.75562807, --2.89699407, -1.86696610, -0.79056636, -0.00330943, 0.79744554, 1.85149941, 2.91118681, 3.99520311, 4.96341987, 6.00000000, - 7.00000000, 8.00000000, 9.00000000, 10.00000000, -6.00000000, -4.80151529, -4.00000000, -2.87442856, -1.85285815, -0.77767592, --0.02071301, 0.81752572, 1.82503940, 2.79602150, 3.92870203, 5.00000000, 6.00000000, 7.00000000, -17.00000000, -16.00000000, --15.00000000, -14.00000000, -13.00000000, -12.00000000, -11.00000000, -9.80059874, -9.00000000, -8.00185204, -7.13087808, -5.92942149, --4.77883243, -3.93417708, -2.88004618, -1.89952522, -0.86239337, 0.00332274, 0.86657548, 1.89479279, 2.89701813, 3.90987417, - 4.98910145, 6.07676766, 7.00000000, 8.00000000, 9.00000000, 10.00000000, 11.00000000, 12.00000000, 13.00000000, 14.00000000, --12.00000000, -11.00000000, -9.89996262, -8.85894205, -7.87594823, -6.99685317, -5.94917589, -4.93914916, -3.93317670, -2.93174244, --1.90737478, -0.90982242, 0.00803316, 0.90111563, 1.90362879, 2.90332432, 3.90654662, 4.94461954, 5.87963665, 6.91988113, - 7.79514004, 8.98805413, 10.00000000, 11.00000000, -35.00000000, -34.00000000, -33.00000000, -32.00000000, -31.00000000, -30.00000000, --29.00000000, -28.00000000, -27.00000000, -26.00000000, -25.00000000, -24.00000000, -22.88310970, -22.00000000, -21.00000000, -20.00000000, --19.00000000, -18.00000000, -17.00000000, -16.11854974, -15.00000000, -14.10507667, -13.04497040, -11.94846700, -10.97432494, -9.94514368, --8.97311414, -7.94171496, -6.97232122, -5.98590548, -4.97455572, -3.95477903, -2.93935454, -1.95573532, -0.97120273, -0.02084826, - 0.95689153, 1.96679781, 2.97060165, 3.96660892, 4.96754331, 5.97996089, 6.93822411, 7.96618014, 8.95809791, 9.98891474, - 10.95713402, 11.85433084, 13.03831696, 13.84035295, 15.00729606, 15.98652872, 17.20557599, 18.00000000, 18.90794805, 20.00000000, - 21.00000000, -34.00000000, -33.00000000, -32.00000000, -31.00000000, -30.00000000, -28.97280602, -28.00000000, -27.16255057, -26.04078092, --24.85442050, -24.15783484, -22.78614956, -21.95739865, -21.21844626, -20.03008104, -19.03888543, -17.90460490, -17.02064693, -15.84673652, --14.87140709, -13.87996048, -12.94907251, -11.96795995, -11.00977925, -9.95103238, -8.96674655, -7.96351667, -6.96886200, -5.99335494, --4.97515534, -3.98891694, -2.99581150, -1.98758360, -0.99249128, -0.00001403, 0.98807868, 1.99119869, 2.99019366, 3.98612953, - 5.00312941, 5.98833080, 6.99686651, 7.98373889, 8.97942222, 9.94202752, 10.99671622, 11.94306164, 12.98539825, 13.90728690, - 14.89907642, 15.94836675, 16.89611342, 17.84084949, 18.74910958, 20.00000000, -67.00000000, -66.00000000, -65.00000000, -64.00000000, --63.02511977, -62.00000000, -61.06061493, -59.95964043, -59.12824439, -58.00000000, -57.00000000, -56.00000000, -54.87857996, -54.09689334, --53.00000000, -52.21057366, -50.93867921, -50.03032952, -49.19283867, -47.89439051, -46.99505692, -46.04895543, -44.89687413, -43.78942208, --42.99025156, -41.88436155, -40.99169704, -40.00320429, -38.90181498, -38.06029271, -37.05030818, -36.07554573, -35.03202233, -33.93117946, --32.97736655, -31.98942819, -30.99546798, -30.01511004, -28.97296525, -28.02561164, -26.94386985, -25.99632704, -25.00461143, -24.01578192, --22.99177609, -22.02261094, -20.97939001, -19.96176066, -19.00442980, -18.01529434, -17.00196902, -15.99794828, -14.98675055, -13.97517657, --12.98676283, -11.99718760, -11.00167809, -9.98872268, -9.02138474, -8.00320338, -6.99542797, -6.00059136, -5.01311763, -4.00336943, --3.00348281, -1.99365875, -0.98223019, 0.00126343, 0.99699237, 1.99381968, 3.00054436, 3.99898305, 5.00160508, 6.00310399, - 6.99885096, 8.02740039, 8.99515550, 9.98962151, 11.00642302, 11.98694516, 13.00018933, 13.97726018, 14.99186645, 16.00580131, - 16.97434224, 17.96982658, 19.00066438, 20.01228749, 21.00741822, 21.94988312, 23.00860212, 23.98801542, 24.97638417, 25.98003521, - 27.02336188, 27.99667029, 29.01014125, 30.02481912, 31.01415797, 31.97399854, 33.06214485, 33.99929330, 34.94095386, 35.96368372, - 36.96980925, 37.98389244, 39.01121235, 40.00715026, 41.06382894, 41.96618280, 43.01555590, 43.95430436, 45.01970038, 45.99967821, - 47.19847394, 48.04852502, 49.10609965, 50.04244122, 50.86051406, 51.92983796, 53.02781107, 54.06248545, 54.89942009, 56.08347165, - 57.06887956, 58.09671115, 59.07832400, 59.87005277, 61.14778499, 62.00000000, 63.00000000, 64.00000000, 65.00000000, 66.00000000, - 67.00000000, 68.00000000, 69.00000000, 70.00000000, 71.00000000, 72.00000000}; - -const double WebRtcIsac_kQKltLevelsShape[1735] = { - 0.00032397, 0.00008053, -0.00061202, -0.00012620, 0.00030437, 0.00054764, -0.00027902, 0.00069360, 0.00029449, -0.80219239, - 0.00091089, -0.74514927, -0.00094283, 0.64030631, -0.60509119, 0.00035575, 0.61851665, -0.62129957, 0.00375219, 0.60054900, --0.61554359, 0.00054977, 0.63362016, -1.73118727, -0.65422341, 0.00524568, 0.66165298, 1.76785515, -1.83182018, -0.65997434, --0.00011887, 0.67524299, 1.79933938, -1.76344480, -0.72547708, -0.00133017, 0.73104704, 1.75305377, 2.85164534, -2.80423916, --1.71959639, -0.75419722, -0.00329945, 0.77196760, 1.72211069, 2.87339653, 0.00031089, -0.00015311, 0.00018201, -0.00035035, --0.77357251, 0.00154647, -0.00047625, -0.00045299, 0.00086590, 0.00044762, -0.83383829, 0.00024787, -0.68526258, -0.00122472, - 0.64643255, -0.60904942, -0.00448987, 0.62309184, -0.59626442, -0.00574132, 0.62296546, -0.63222115, 0.00013441, 0.63609545, --0.66911055, -0.00369971, 0.66346095, 2.07281301, -1.77184694, -0.67640425, -0.00010145, 0.64818392, 1.74948973, -1.69420224, --0.71943894, -0.00004680, 0.75303493, 1.81075983, 2.80610041, -2.80005755, -1.79866753, -0.77409777, -0.00084220, 0.80141293, - 1.78291081, 2.73954236, 3.82994169, 0.00015140, -0.00012766, -0.00034241, -0.00119125, -0.76113497, 0.00069246, 0.76722027, - 0.00132862, -0.69107530, 0.00010656, 0.77061578, -0.78012970, 0.00095947, 0.77828502, -0.64787758, 0.00217168, 0.63050167, --0.58601125, 0.00306596, 0.59466308, -0.58603410, 0.00059779, 0.64257970, 1.76512766, -0.61193600, -0.00259517, 0.59767574, --0.61026273, 0.00315811, 0.61725479, -1.69169719, -0.65816029, 0.00067575, 0.65576890, 2.00000000, -1.72689193, -0.69780808, --0.00040990, 0.70668487, 1.74198458, -3.79028154, -3.00000000, -1.73194459, -0.70179341, -0.00106695, 0.71302629, 1.76849782, --2.89332364, -1.78585007, -0.78731491, -0.00132610, 0.79692976, 1.75247009, 2.97828682, -5.26238694, -3.69559829, -2.87286122, --1.84908818, -0.84434577, -0.01167975, 0.84641753, 1.84087672, 2.87628156, 3.83556679, -0.00190204, 0.00092642, 0.00354385, --0.00012982, -0.67742785, 0.00229509, 0.64935672, -0.58444751, 0.00470733, 0.57299534, -0.58456202, -0.00097715, 0.64593607, --0.64060330, -0.00638534, 0.59680157, -0.59287537, 0.00490772, 0.58919707, -0.60306173, -0.00417464, 0.60562100, -1.75218757, --0.63018569, -0.00225922, 0.63863300, -0.63949939, -0.00126421, 0.64268914, -1.75851182, -0.68318060, 0.00510418, 0.69049211, - 1.88178506, -1.71136148, -0.72710534, -0.00815559, 0.73412917, 1.79996711, -2.77111145, -1.73940498, -0.78212945, 0.01074476, - 0.77688916, 1.76873972, 2.87281379, 3.77554698, -3.75832725, -2.95463235, -1.80451491, -0.80017226, 0.00149902, 0.80729206, - 1.78265046, 2.89391793, -3.78236148, -2.83640598, -1.82532067, -0.88844327, -0.00620952, 0.88208030, 1.85757631, 2.81712391, - 3.88430176, 5.16179367, -7.00000000, -5.93805408, -4.87172597, -3.87524433, -2.89399744, -1.92359563, -0.92136341, -0.00172725, - 0.93087018, 1.90528280, 2.89809686, 3.88085708, 4.89147740, 5.89078692, -0.00239502, 0.00312564, -1.00000000, 0.00178325, - 1.00000000, -0.62198029, 0.00143254, 0.65344051, -0.59851220, -0.00676987, 0.61510140, -0.58894151, 0.00385055, 0.59794203, --0.59808568, -0.00038214, 0.57625703, -0.63009713, -0.01107985, 0.61278758, -0.64206758, -0.00154369, 0.65480598, 1.80604162, --1.80909286, -0.67810514, 0.00205762, 0.68571097, 1.79453891, -3.22682422, -1.73808453, -0.71870305, -0.00738594, 0.71486172, - 1.73005326, -1.66891897, -0.73689615, -0.00616203, 0.74262409, 1.73807899, -2.92417482, -1.73866741, -0.78133871, 0.00764425, - 0.80027264, 1.78668732, 2.74992588, -4.00000000, -2.75578740, -1.83697516, -0.83117035, -0.00355191, 0.83527172, 1.82814700, - 2.77377675, 3.80718693, -3.81667698, -2.83575471, -1.83372350, -0.86579471, 0.00547578, 0.87582281, 1.82858793, 2.87265007, - 3.91405377, -4.87521600, -3.78999094, -2.86437014, -1.86964365, -0.90618018, 0.00128243, 0.91497811, 1.87374952, 2.83199819, - 3.91519130, 4.76632822, -6.68713448, -6.01252467, -4.94587936, -3.88795368, -2.91299088, -1.92592211, -0.95504570, -0.00089980, - 0.94565200, 1.93239633, 2.91832808, 3.91363475, 4.88920034, 5.96471415, 6.83905252, 7.86195009, 8.81571018, -12.96141759, --11.73039516, -10.96459719, -9.97382433, -9.04414433, -7.89460619, -6.96628608, -5.93236595, -4.93337924, -3.95479990, -2.96451499, --1.96635876, -0.97271229, -0.00402238, 0.98343930, 1.98348291, 2.96641164, 3.95456471, 4.95517089, 5.98975714, 6.90322073, - 7.90468849, 8.85639467, 9.97255498, 10.79006309, 11.81988596, 0.04950500, -1.00000000, -0.01226628, 1.00000000, -0.59479469, --0.10438305, 0.59822144, -2.00000000, -0.67109149, -0.09256692, 0.65171621, 2.00000000, -3.00000000, -1.68391999, -0.76681039, --0.03354151, 0.71509146, 1.77615472, -2.00000000, -0.68661511, -0.02497881, 0.66478398, 2.00000000, -2.00000000, -0.67032784, --0.00920582, 0.64892756, 2.00000000, -2.00000000, -0.68561894, 0.03641869, 0.73021611, 1.68293863, -4.00000000, -2.72024184, --1.80096059, -0.81696185, 0.03604685, 0.79232033, 1.70070730, 3.00000000, -4.00000000, -2.71795670, -1.80482986, -0.86001162, - 0.03764903, 0.87723968, 1.79970771, 2.72685932, 3.67589143, -5.00000000, -4.00000000, -2.85492548, -1.78996365, -0.83250358, --0.01376828, 0.84195506, 1.78161105, 2.76754458, 4.00000000, -6.00000000, -5.00000000, -3.82268811, -2.77563624, -1.82608163, --0.86486114, -0.02671886, 0.86693165, 1.88422879, 2.86248347, 3.95632216, -7.00000000, -6.00000000, -5.00000000, -3.77533988, --2.86391432, -1.87052039, -0.90513658, 0.06271236, 0.91083620, 1.85734756, 2.86031688, 3.82019418, 4.94420394, 6.00000000, --11.00000000, -10.00000000, -9.00000000, -8.00000000, -6.91952415, -6.00000000, -4.92044374, -3.87845165, -2.87392362, -1.88413020, --0.91915740, 0.00318517, 0.91602800, 1.89664838, 2.88925058, 3.84123856, 4.78988651, 5.94526812, 6.81953917, 8.00000000, --9.00000000, -8.00000000, -7.03319143, -5.94530963, -4.86669720, -3.92438007, -2.88620396, -1.92848070, -0.94365985, 0.01671855, - 0.97349410, 1.93419878, 2.89740109, 3.89662823, 4.83235583, 5.88106535, 6.80328232, 8.00000000, -13.00000000, -12.00000000, --11.00000000, -10.00000000, -9.00000000, -7.86033489, -6.83344055, -5.89844215, -4.90811454, -3.94841298, -2.95820490, -1.98627966, --0.99161468, -0.02286136, 0.96055651, 1.95052433, 2.93969396, 3.94304346, 4.88522624, 5.87434241, 6.78309433, 7.87244101, - 9.00000000, 10.00000000, -12.09117356, -11.00000000, -10.00000000, -8.84766108, -7.86934236, -6.98544896, -5.94233429, -4.95583292, --3.95575986, -2.97085529, -1.98955811, -0.99359873, -0.00485413, 0.98298870, 1.98093258, 2.96430203, 3.95540216, 4.96915010, - 5.96775124, 6.99236918, 7.96503302, 8.99864542, 9.85857723, 10.96541926, 11.91647197, 12.71060069, -26.00000000, -25.00000000, --24.00585596, -23.11642573, -22.14271284, -20.89800711, -19.87815799, -19.05036354, -17.88555651, -16.86471209, -15.97711073, -14.94012359, --14.02661226, -12.98243228, -11.97489256, -10.97402777, -9.96425624, -9.01085220, -7.97372506, -6.98795002, -5.97271328, -5.00191694, --3.98055849, -2.98458048, -1.99470442, -0.99656768, -0.00825666, 1.00272004, 1.99922218, 2.99357669, 4.01407905, 5.01003897, - 5.98115528, 7.00018958, 8.00338125, 8.98981046, 9.98990318, 10.96341479, 11.96866930, 12.99175139, 13.94580443, 14.95745083, - 15.98992869, 16.97484646, 17.99630043, 18.93396897, 19.88347741, 20.96532482, 21.92191032, 23.22314702, 0.00006846, 0.00014352, --0.00056203, 0.00027588, -0.00147678, 1.00000000, 0.00003823, 0.00001975, -0.00033710, -0.00096712, 1.00000000, -1.00000000, - 0.00067511, -1.00000000, 0.00342065, 1.00000000, -1.00000000, 0.00196254, 1.00000000, -1.00000000, 0.00201173, 1.00000000, --2.00000000, -1.00000000, -0.00381686, 1.00000000, -1.00000000, 0.00178037, 1.00000000, -2.00000000, -1.00000000, -0.00320274, - 1.00000000, 2.00000000, -2.00000000, -1.00000000, 0.00426519, 1.00000000, 2.00000000, -3.00000000, -2.00000000, -1.00000000, --0.00074072, 0.64654602, 2.00000000, 0.00031217, 0.00063348, 0.00020247, 0.00047891, 0.00122893, -0.00150669, -0.00148276, - 0.00016848, 0.00147085, 1.00000000, -0.00088160, 1.00000000, -1.00000000, 0.00381641, 1.00000000, -1.00000000, 0.00129816, - 1.00000000, -1.00000000, 0.00074903, 1.00000000, -2.00000000, -0.76230566, -0.00370764, 0.82467977, -0.78769346, -0.00492670, - 0.84532630, -2.00000000, -0.70943195, -0.01257613, 0.75905385, 2.00000000, -2.00000000, -0.62780445, -0.00408633, 0.60272506, - 2.00000000, 3.00000000, -3.00000000, -2.00000000, -0.61412985, 0.00102833, 0.61527589, 2.00000000, 3.00000000, 0.00012115, --0.00080909, 0.00071061, -0.00227957, 0.00179794, 0.00103827, -1.00000000, 0.00444757, -1.00000000, 0.00604068, 1.00000000, --1.00000000, 0.00427327, 1.00000000, -1.00000000, 0.00086662, 1.00000000, -1.00000000, -0.00837492, 1.00000000, -0.65715934, --0.00645342, 0.64004630, -2.00000000, -0.64987682, -0.01449567, 0.69893373, -2.00000000, -0.63221961, 0.00421765, 0.62452105, - 2.00000000, -2.00000000, -0.60027006, -0.00110630, 0.62033821, 2.00000000, -2.00000000, -0.59823932, 0.00928313, 0.62188520, - 2.00000000, 3.00000000, -3.00000000, -2.00000000, -0.63230286, -0.00248555, 0.62632575, 2.00000000, 3.00000000, -5.00000000, --4.00000000, -3.00000000, -2.00000000, -0.66521143, 0.00544305, 0.66930486, 2.00000000, 3.00000000, 4.00000000, 0.00077008, - 0.00061140, -0.00009317, -0.00049643, 1.00000000, -1.00000000, -0.00285084, 1.00000000, -1.00000000, 0.00601784, 1.00000000, --1.00000000, -0.00091887, 0.75122772, -0.71579859, -0.00043545, 1.00000000, -0.85571363, -0.00227654, 0.63816873, -1.00000000, --0.00393484, 0.76748004, -0.58223659, -0.01229777, 0.58080322, -0.61945902, -0.00232238, 0.62277938, 2.00000000, -2.00000000, --0.60595489, -0.00535702, 0.60547736, 2.00000000, -4.00000000, -3.00000000, -2.00000000, -0.62368122, 0.01112097, 0.63997294, - 2.00000000, 3.00000000, -3.00000000, -2.00000000, -0.64318217, 0.00515139, 0.64781184, 2.00000000, -3.00000000, -1.78031579, --0.67122588, 0.02153711, 0.67899877, 2.00000000, 3.00000000, 4.00000000, -4.00000000, -3.00000000, -1.80503233, -0.69835727, --0.00270770, 0.70999554, 1.77332849, 3.00000000, 4.00000000, 5.00000000, -8.00000000, -7.00000000, -6.00000000, -5.00000000, --4.00000000, -2.81600693, -1.72970368, -0.73779413, -0.01384841, 0.75694606, 1.80042618, 3.00000000, 4.00000000, 5.00000000, - 6.00000000, -0.00051787, 0.00059593, -0.00023319, -1.00000000, 0.00191861, 0.79547197, -0.75020995, 0.00217840, 0.69165833, --1.00000000, -0.00304964, 0.67698951, -0.64516943, -0.00657667, 0.59260129, -0.62819301, -0.00456626, 0.59426260, -0.60909519, - 0.00256476, 0.61660408, -0.66560131, -0.00293463, 0.67477566, 2.00000000, -2.00000000, -0.62484067, 0.00505116, 0.63491494, - 2.00000000, -3.00000000, -2.00000000, -0.68427246, 0.00924353, 0.68755774, 2.00000000, 3.00000000, -3.00000000, -2.00000000, --0.65390928, 0.01008025, 0.65849449, 2.00000000, 3.00000000, -5.00000000, -4.00000000, -3.00000000, -1.70848232, -0.72079538, --0.00007674, 0.71556176, 1.76815351, 3.00000000, 4.00000000, 5.00000000, -4.00000000, -3.00000000, -1.82887466, -0.73529886, - 0.00033458, 0.73847588, 1.83009515, 3.00000000, 4.00000000, -5.00000000, -4.00000000, -2.83203553, -1.79500085, -0.77452749, --0.00614320, 0.77416943, 1.82469471, 2.77034612, 4.00000000, 5.00000000, -7.00000000, -6.00000000, -5.00000000, -4.00000000, --2.76574798, -1.84700836, -0.80822297, 0.00054165, 0.80901445, 1.85687331, 2.75680191, 3.81986695, 5.00000000, 6.00000000, - 7.00000000, 8.00000000, -13.00000000, -12.00000000, -11.00000000, -10.00000000, -9.00000000, -8.00000000, -7.00000000, -6.00000000, --5.00000000, -3.88304817, -2.93396067, -1.86645989, -0.84825410, 0.00666207, 0.84853252, 1.88634684, 2.95282618, 3.89813287, - 4.89189079, 6.00000000, 7.00000000, 8.00000000, 9.00000000, 10.00000000, 11.00000000, -0.00344877, 1.00000000, -0.61413659, --0.02115630, 0.59438887, -0.60873054, 0.00844993, 0.62510557, -2.00000000, -0.75002947, 0.00120913, 0.66616051, -2.00000000, --0.72324691, 0.04760499, 0.70532533, 2.00000000, -3.00000000, -1.66577589, -0.78941380, -0.01909714, 0.74993685, 1.70945570, --1.64422308, -0.70992006, -0.02795108, 0.76990363, 1.79682243, 2.96233315, -1.71686461, -0.76572785, -0.00041846, 0.78174132, - 1.66217596, -3.00000000, -1.77033369, -0.79475091, 0.03709740, 0.80097076, 1.83947400, 2.85879773, -4.00000000, -3.16528651, --1.79564411, -0.90078981, 0.02403102, 0.86138856, 1.84207433, 2.74584048, -4.00000000, -2.91249347, -1.87804769, -0.87323549, - 0.08164382, 0.89037056, 1.82505263, 2.71336163, 4.00000000, -4.81262228, -3.87173565, -2.83424209, -1.87517938, -0.86199960, - 0.00268598, 0.89547657, 1.90713511, 2.85219071, 3.86417171, 4.80711781, 6.00000000, 7.00000000, -5.00000000, -3.82388480, --2.82875808, -1.90350457, -0.90795818, 0.03047007, 0.93676836, 1.88844957, 2.83269711, 3.76109686, 5.00000000, -9.00000000, --8.00000000, -6.88037957, -5.88776398, -4.91209139, -3.93902541, -2.90989221, -1.92281230, -0.98960535, -0.07440511, 0.94023957, - 1.91666262, 2.83340828, 3.83651295, 4.77839424, 6.12284019, 7.00000000, 8.00000000, 9.00000000, -12.00000000, -11.00000000, --10.00000000, -9.00000000, -8.00000000, -6.68554513, -5.97994708, -4.98789075, -3.91383581, -2.92952795, -1.91727195, -0.93148075, --0.00568870, 0.93515148, 1.94580068, 2.93838956, 3.92567644, 4.96573603, 5.95402763, 7.00000000, 8.00000000, 9.00000000, --11.00000000, -9.90096030, -8.97868124, -7.93663988, -6.98806055, -5.95937864, -4.93473664, -3.95454756, -2.96518446, -1.97711766, --0.98552111, -0.03317271, 0.95115775, 1.93785086, 2.96310779, 3.93322450, 5.01716212, 5.85909823, 6.89163669, 7.97492693, - 8.85698897, 9.79802946, 11.09373957, 12.00000000, -13.00000000, -12.00000000, -10.67579109, -9.95079100, -8.90576592, -7.93254656, --6.96112672, -5.96015798, -4.95493809, -3.98556269, -2.98182856, -1.98150255, -0.96551153, -0.00399791, 0.98644875, 1.98043830, - 2.97969033, 3.97728257, 4.95173541, 5.95649050, 6.96447378, 7.95591513, 9.07680954, 9.92093070, 10.76496555, 11.97525735, - 13.00000000, 14.00000000, -25.00000000, -24.00000000, -23.00000000, -22.00072357, -21.00000000, -20.00000000, -19.00000000, -18.20003462, --17.01648407, -15.78651996, -14.95660266, -13.99167850, -13.28722978, -11.85013840, -10.92025302, -9.87055810, -8.93841040, -7.95329867, --6.97819441, -6.01593394, -5.00905213, -3.99905285, -2.99171810, -1.99062796, -1.00112466, 0.00140492, 1.00701091, 2.02327185, - 3.00194633, 3.99188294, 5.00313145, 6.00448038, 6.98904951, 7.98158293, 8.98212774, 10.00363404, 10.98641678, 11.98034311, - 12.95176779, 13.95383703, 14.99084578, 15.98600642, 16.99406826, 17.98134623, 19.01793961, 19.86072639, 20.88465474, 21.99287082, - 22.81916620, 23.77946383, 0.00000234, 0.00000298, 0.00000048, 0.00002408, -0.00000165, -0.00001831, -0.00005703, -0.00000184, --1.00000000, 0.00001977, 1.00000000, -1.00000000, 0.00000010, 1.00000000, -1.00000000, -0.00001152, 1.00000000, -1.00000000, - 0.00000840, 1.00000000, -1.00000000, 0.00002353, 1.00000000, -0.75455603, -0.00001433, 1.00000000, -0.65859705, -0.00000703, - 0.62995860, -2.00000000, -0.72724652, -0.00033969, 0.61359174, 2.00000000, -2.00000000, -0.69510998, -0.00031410, 0.66467605, - 2.00000000, 3.00000000, -3.00000000, -2.00000000, -0.65738683, 0.00039019, 0.66554720, 1.91774106, 3.18089124, 0.00000070, - 0.00001152, -0.00000795, -0.00000058, -0.00003502, -0.00001508, -0.00004225, -0.00002165, -1.00000000, 0.00004391, 1.00000000, --1.00000000, 0.00001784, 1.00000000, -1.00000000, -0.00003678, 1.00000000, -0.68878314, -0.00013166, 0.60880149, -0.75291978, - 0.00006493, 1.00000000, -0.76757316, 0.00003057, 0.67140524, -0.61602267, -0.00014495, 0.63625803, 2.00000000, -2.00000000, --0.61253314, -0.00116483, 0.65071851, 2.00000000, -3.00000000, -1.71451667, -0.67799909, -0.00048294, 0.65846019, 2.00000000, --3.02497593, -1.83515395, -0.70317981, 0.00519701, 0.67780009, 1.84218153, 2.88846262, 4.00000000, 0.00001124, 0.00000588, --0.00000172, 0.00002835, 1.00000000, 0.00001012, -0.00008644, 1.00000000, -0.75115901, 0.00004347, 1.00000000, -1.00000000, - 0.00002800, 1.00000000, -1.00000000, -0.00006039, 1.00000000, -0.79763258, -0.00011907, 0.71713616, -0.76791870, -0.00007113, - 0.63583609, -0.62337806, 0.00012891, 0.62242094, -0.60837055, 0.00043216, 0.65515705, -0.63637782, -0.00019749, 0.60423967, - 2.00000000, -2.00000000, -0.65404827, -0.00089304, 0.64706660, 2.00000000, -1.86334076, -0.66410366, 0.00063219, 0.66968004, - 2.00000000, 3.00000000, 4.00000000, -4.00000000, -3.00000000, -1.79048834, -0.69451890, 0.00030677, 0.71009333, 1.70591343, - 3.00000000, 4.00000000, -4.00000000, -2.90176499, -1.78368781, -0.74425178, 0.00234068, 0.74847325, 1.78886822, 2.78478854, - 3.83608985, 4.95996151, 0.00002170, 0.00001281, 0.00002162, -1.00000000, -0.00007266, 1.00000000, -1.00000000, -0.00003250, --0.64088804, 0.00015239, 1.00000000, -0.58450370, -0.00008410, 0.60567186, -1.00000000, -0.00010752, 1.00000000, -0.58922508, --0.00017378, 0.60755779, -0.62797206, -0.00001016, 0.64432847, -0.58497934, -0.00001851, 0.59716791, -0.62642499, -0.00097386, - 0.63568558, 2.00000000, -2.00000000, -0.63236390, -0.00173361, 0.63142762, 1.75629192, -3.00000000, -2.00000000, -0.65596684, - 0.00209364, 0.65419742, 2.00000000, -3.00000000, -1.73856625, -0.67767521, -0.00119512, 0.68973603, 1.70985573, 3.00000000, --3.00000000, -1.81820220, -0.73974134, 0.00695869, 0.72216179, 1.75624461, 3.00000000, 4.00000000, -5.00000000, -4.00000000, --3.17718593, -1.76857567, -0.76822322, 0.00267400, 0.76414602, 1.84309221, 3.04940652, 4.00000000, -7.08189123, -6.00000000, --5.22882249, -3.96477958, -2.79653492, -1.81923435, -0.80050253, -0.01086663, 0.82708565, 1.85804900, 2.89996354, 3.76028554, - 4.80518081, 5.81738096, 7.00000000, 8.00000000, 9.08816091, -0.00002979, -0.00000333, -1.00000000, -0.00011532, 1.00000000, --0.70921122, -0.00005325, 0.68933188, -0.67581263, -0.00023107, 0.57868212, -0.58388312, -0.00020850, 0.60149012, -0.60912457, - 0.00001567, 0.60180554, -0.59130091, -0.00038863, 0.59908653, -2.00000000, -0.63697707, 0.00083913, 0.62040514, 2.00000000, --2.00000000, -0.63216238, -0.00081100, 0.64411071, 2.00000000, -1.76856259, -0.65266989, -0.00243486, 0.66888899, 2.00000000, --1.75427214, -0.71415385, -0.00226376, 0.71296778, 1.66182947, 3.00000000, -3.00000000, -1.72505821, -0.72920134, -0.00360424, - 0.73800767, 1.72848281, 3.00000000, -4.00000000, -2.95284408, -1.72025758, -0.76503859, 0.00418761, 0.75297139, 1.73959808, - 3.00000000, -5.00000000, -3.96232791, -2.74080544, -1.78897123, -0.80233505, -0.00002050, 0.79693417, 1.76182598, 2.78434458, - 3.85693287, 5.00000000, -6.00000000, -4.78439284, -3.83501790, -2.85203629, -1.84909573, -0.85382658, -0.00181019, 0.84735145, - 1.83676575, 2.83656843, 3.86722376, 4.79702431, 6.00000000, -9.00000000, -8.00000000, -7.00000000, -6.07957292, -4.84677515, --3.85093972, -2.88683139, -1.84596391, -0.88058034, -0.00008692, 0.87554746, 1.86933183, 2.84729990, 3.89029797, 4.87311773, - 5.90844023, 7.00000000, -11.00000000, -9.97745420, -8.90015761, -7.94187517, -6.86987726, -5.84795335, -4.86693435, -3.90601819, --2.91031804, -1.91620096, -0.90497055, 0.00659199, 0.90926869, 1.90980821, 2.91070850, 3.93685967, 4.85581177, 6.06727337, - 7.05801043, 8.00000000, 9.00000000, 10.00000000, 10.90825787, 12.00000000, 13.00000000, 14.00000000, -0.00008918, 1.00000000, --0.54405938, 0.00120348, 0.55781920, -0.59227786, -0.00349602, 0.59777231, -1.63717598, -0.69048065, 0.00999281, 0.65770558, - 2.00000000, -2.00000000, -0.71013571, 0.00454518, 0.66991065, -3.00000000, -1.73004867, -0.73743921, 0.01162454, 0.69964842, - 1.83319587, -1.81225491, -0.76806000, 0.00164742, 0.76780397, 1.67168896, -1.64564794, -0.79903361, -0.01522880, 0.84277926, - 1.68873752, -3.00000000, -1.72063244, -0.83687428, 0.00246724, 0.84618697, 1.79464483, 2.77447025, -3.77118426, -2.75025539, --1.82050448, -0.90373722, -0.00187780, 0.90102245, 1.85249394, 2.71364180, -2.71720889, -1.79466125, -0.89860801, -0.02725825, - 0.90877329, 1.90542096, 2.76847902, 3.71496428, -4.70257302, -3.90746659, -2.87078421, -1.88858709, -0.93608993, -0.02157425, - 0.95181182, 1.91155682, 2.83614575, 3.87820801, 4.72172277, -5.02764544, -3.80066801, -2.87484378, -1.90707477, -0.96326017, --0.01060091, 0.96558851, 1.92191548, 2.86970759, 3.85655474, 4.83135970, 5.76387469, -9.00000000, -8.00000000, -6.75261776, --5.86333393, -4.84846871, -3.91871758, -2.93827286, -1.93050320, -0.96359634, -0.00141931, 0.95926312, 1.92541870, 2.93009411, - 3.86699087, 4.82315929, 5.67815206, -8.76594345, -7.70350451, -6.91784020, -5.81539490, -4.92526872, -3.91513203, -2.92134949, --1.95465646, -0.97638102, -0.00742564, 0.96948714, 1.96401112, 2.95256722, 3.93146353, 4.90991357, 5.88139022, 6.88640588, - 7.82610489, 9.00000000, -10.97611369, -9.80036760, -8.91109518, -7.92809404, -6.93865353, -5.91965899, -4.92957669, -3.95206224, --2.97308718, -1.97778214, -0.98552568, -0.00063212, 0.98686014, 1.97511867, 2.97114218, 3.97854244, 4.96578513, 5.96457765, - 6.95180187, 7.95163483, 8.93760897, 9.87666900, 10.88024562, 11.96270158, 12.99519291, -15.00000000, -13.76826291, -12.97229116, --12.00334834, -10.95980884, -9.98190891, -8.93798503, -7.95621309, -6.96109479, -5.96056649, -4.95843419, -3.97688640, -2.98989576, --1.98533395, -0.99580972, 0.00694370, 0.99421120, 1.99033132, 2.98751217, 3.98549580, 4.96482394, 5.96623233, 6.93564626, - 7.93772467, 8.92015276, 9.88785129, 10.97606096, 11.79686057, -23.00000000, -22.00000000, -21.00000000, -20.00000000, -19.00000000, --17.73310977, -16.83574096, -15.90889480, -15.00437366, -13.95007272, -12.99296117, -11.98334751, -10.96970820, -9.97775151, -8.98193840, --7.98378966, -6.98887770, -5.99059477, -5.00228769, -3.99355850, -2.99947486, -1.99897483, -0.99375857, 0.00324880, 1.00215912, - 1.99277083, 3.00503747, 3.99390482, 4.98854283, 5.98753219, 6.98245347, 7.98089893, 8.95960522, 9.95663648, 11.00810285, - 12.01421617, 12.96208687, 13.99227766, 14.97230040, 15.95114804, 16.97347393, 17.97794884, 18.96777118, 19.94446034, 20.94799029, - 22.14740083, 22.84288347, 23.99212109, 25.00000000, 25.96562658}; - -/* cdf tables for quantizer indices */ -const WebRtc_UWord16 WebRtcIsac_kQKltCdfGain[1212] = { - 0, 13, 301, 3730, 61784, 65167, 65489, 65535, 0, 17, - 142, 314, 929, 2466, 7678, 56450, 63463, 64740, 65204, 65426, - 65527, 65535, 0, 8, 100, 724, 6301, 60105, 65125, 65510, - 65531, 65535, 0, 13, 117, 368, 1068, 3010, 11928, 53603, - 61177, 63404, 64505, 65108, 65422, 65502, 65531, 65535, 0, 4, - 17, 96, 410, 1859, 12125, 54361, 64103, 65305, 65497, 65535, - 0, 4, 88, 230, 469, 950, 1746, 3228, 6092, 16592, - 44756, 56848, 61256, 63308, 64325, 64920, 65309, 65460, 65502, 65522, - 65535, 0, 88, 352, 1675, 6339, 20749, 46686, 59284, 63525, - 64949, 65359, 65502, 65527, 65535, 0, 13, 38, 63, 117, - 234, 381, 641, 929, 1407, 2043, 2809, 4032, 5753, 8792, - 14407, 24308, 38941, 48947, 55403, 59293, 61411, 62688, 63630, 64329, - 64840, 65188, 65376, 65472, 65506, 65527, 65531, 65535, 0, 8, - 29, 75, 222, 615, 1327, 2801, 5623, 9931, 16094, 24966, - 34419, 43458, 50676, 56186, 60055, 62500, 63936, 64765, 65225, 65435, - 65514, 65535, 0, 8, 13, 15, 17, 21, 33, 59, - 71, 92, 151, 243, 360, 456, 674, 934, 1223, 1583, - 1989, 2504, 3031, 3617, 4354, 5154, 6163, 7411, 8780, 10747, - 12874, 15591, 18974, 23027, 27436, 32020, 36948, 41830, 46205, 49797, - 53042, 56094, 58418, 60360, 61763, 62818, 63559, 64103, 64509, 64798, - 65045, 65162, 65288, 65363, 65447, 65506, 65522, 65531, 65533, 65535, - 0, 4, 6, 25, 38, 71, 138, 264, 519, 808, - 1227, 1825, 2516, 3408, 4279, 5560, 7092, 9197, 11420, 14108, - 16947, 20300, 23926, 27459, 31164, 34827, 38575, 42178, 45540, 48747, - 51444, 54090, 56426, 58460, 60080, 61595, 62734, 63668, 64275, 64673, - 64936, 65112, 65217, 65334, 65426, 65464, 65477, 65489, 65518, 65527, - 65529, 65531, 65533, 65535, 0, 2, 4, 8, 10, 12, - 14, 16, 21, 33, 50, 71, 84, 92, 105, 138, - 180, 255, 318, 377, 435, 473, 511, 590, 682, 758, - 913, 1097, 1256, 1449, 1671, 1884, 2169, 2445, 2772, 3157, - 3563, 3944, 4375, 4848, 5334, 5820, 6448, 7101, 7716, 8378, - 9102, 9956, 10752, 11648, 12707, 13670, 14758, 15910, 17187, 18472, - 19627, 20649, 21951, 23169, 24283, 25552, 26862, 28227, 29391, 30764, - 31882, 33213, 34432, 35600, 36910, 38116, 39464, 40729, 41872, 43144, - 44371, 45514, 46762, 47813, 48968, 50069, 51032, 51974, 52908, 53737, - 54603, 55445, 56282, 56990, 57572, 58191, 58840, 59410, 59887, 60264, - 60607, 60946, 61269, 61516, 61771, 61960, 62198, 62408, 62558, 62776, - 62985, 63207, 63408, 63546, 63739, 63906, 64070, 64237, 64371, 64551, - 64677, 64836, 64999, 65095, 65213, 65284, 65338, 65380, 65426, 65447, - 65472, 65485, 65487, 65489, 65502, 65510, 65512, 65514, 65516, 65518, - 65522, 65531, 65533, 65535, 0, 2, 4, 6, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 10, 222, 65321, - 65513, 65528, 65531, 65533, 65535, 0, 2, 4, 50, 65476, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, 12, - 38, 544, 64936, 65509, 65523, 65525, 65529, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 10, 1055, 64508, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 10, 12, 123, - 3956, 62999, 65372, 65495, 65515, 65521, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 12, 53, 4707, 59445, - 65467, 65525, 65527, 65529, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 38, 40, 50, 67, - 96, 234, 929, 14345, 55750, 64866, 65389, 65462, 65514, 65517, - 65519, 65521, 65523, 65525, 65527, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 15, 35, 91, 377, 1946, - 13618, 52565, 63714, 65184, 65465, 65520, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, 12, - 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, - 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, - 54, 82, 149, 362, 751, 1701, 4239, 12893, 38627, 55072, - 60875, 63071, 64158, 64702, 65096, 65283, 65412, 65473, 65494, 65505, - 65508, 65517, 65519, 65521, 65523, 65525, 65527, 65529, 65531, 65533, - 65535, 0, 2, 15, 23, 53, 143, 260, 418, 698, - 988, 1353, 1812, 2411, 3144, 4015, 5143, 6401, 7611, 8999, - 10653, 12512, 14636, 16865, 19404, 22154, 24798, 27521, 30326, 33102, - 35790, 38603, 41415, 43968, 46771, 49435, 52152, 54715, 57143, 59481, - 61178, 62507, 63603, 64489, 64997, 65257, 65427, 65473, 65503, 65520, - 65529, 65531, 65533, 65535, 0, 3, 6, 9, 26, 32, - 44, 46, 64, 94, 111, 164, 205, 254, 327, 409, - 506, 608, 733, 885, 1093, 1292, 1482, 1742, 1993, 2329, - 2615, 3029, 3374, 3798, 4257, 4870, 5405, 5992, 6618, 7225, - 7816, 8418, 9051, 9761, 10532, 11380, 12113, 13010, 13788, 14594, - 15455, 16361, 17182, 18088, 18997, 20046, 20951, 21968, 22947, 24124, - 25296, 26547, 27712, 28775, 29807, 30835, 31709, 32469, 33201, 34014, - 34876, 35773, 36696, 37620, 38558, 39547, 40406, 41277, 42367, 43290, - 44445, 45443, 46510, 47684, 48973, 50157, 51187, 52242, 53209, 54083, - 55006, 55871, 56618, 57293, 57965, 58556, 59222, 59722, 60180, 60554, - 60902, 61250, 61554, 61837, 62100, 62372, 62631, 62856, 63078, 63324, - 63557, 63768, 63961, 64089, 64235, 64352, 64501, 64633, 64770, 64887, - 65001, 65059, 65121, 65188, 65246, 65302, 65346, 65390, 65428, 65463, - 65477, 65506, 65515, 65517, 65519, 65521, 65523, 65525, 65527, 65529, - 65531, 65533, 65535, 0, 2, 4, 109, 65332, 65531, 65533, - 65535, 0, 2, 4, 6, 8, 25, 1817, 63874, 65511, - 65527, 65529, 65531, 65533, 65535, 0, 2, 4, 907, 65014, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, - 12, 132, 2743, 62708, 65430, 65525, 65527, 65529, 65531, 65533, - 65535, 0, 2, 4, 6, 8, 35, 3743, 61666, 65485, - 65531, 65533, 65535, 0, 2, 4, 6, 8, 10, 23, - 109, 683, 6905, 58417, 64911, 65398, 65497, 65518, 65525, 65527, - 65529, 65531, 65533, 65535, 0, 2, 4, 6, 53, 510, - 10209, 55212, 64573, 65441, 65522, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 22, 32, 90, 266, 1037, 3349, 14468, 50488, 62394, 64685, - 65341, 65480, 65514, 65519, 65521, 65523, 65525, 65527, 65529, 65531, - 65533, 65535, 0, 2, 4, 6, 9, 16, 37, 106, - 296, 748, 1868, 5733, 18897, 45553, 60165, 63949, 64926, 65314, - 65441, 65508, 65524, 65529, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, - 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, - 46, 48, 50, 83, 175, 344, 667, 1293, 2337, 4357, - 8033, 14988, 28600, 43244, 52011, 57042, 59980, 61779, 63065, 63869, - 64390, 64753, 64988, 65164, 65326, 65422, 65462, 65492, 65506, 65522, - 65524, 65526, 65531, 65533, 65535, 0, 2, 4, 6, 8, - 10, 12, 14, 16, 25, 39, 48, 55, 62, 65, - 85, 106, 139, 169, 194, 252, 323, 485, 688, 1074, - 1600, 2544, 3863, 5733, 8303, 11397, 15529, 20273, 25734, 31455, - 36853, 41891, 46410, 50306, 53702, 56503, 58673, 60479, 61880, 62989, - 63748, 64404, 64852, 65124, 65309, 65424, 65480, 65524, 65528, 65533, - 65535, 0, 2, 4, 6, 8, 10, 12, 14, 21, - 23, 25, 27, 29, 31, 39, 41, 43, 48, 60, - 72, 79, 106, 136, 166, 187, 224, 252, 323, 381, - 427, 478, 568, 660, 783, 912, 1046, 1175, 1365, 1567, - 1768, 2024, 2347, 2659, 3049, 3529, 4033, 4623, 5281, 5925, - 6726, 7526, 8417, 9468, 10783, 12141, 13571, 15222, 16916, 18659, - 20350, 22020, 23725, 25497, 27201, 29026, 30867, 32632, 34323, 36062, - 37829, 39466, 41144, 42654, 43981, 45343, 46579, 47759, 49013, 50171, - 51249, 52283, 53245, 54148, 54938, 55669, 56421, 57109, 57791, 58464, - 59092, 59674, 60105, 60653, 61083, 61407, 61757, 62095, 62388, 62649, - 62873, 63157, 63358, 63540, 63725, 63884, 64046, 64155, 64278, 64426, - 64548, 64654, 64806, 64906, 64994, 65077, 65137, 65215, 65277, 65324, - 65354, 65409, 65437, 65455, 65462, 65490, 65495, 65499, 65508, 65511, - 65513, 65515, 65517, 65519, 65521, 65523, 65525, 65527, 65529, 65531, - 65533, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQKltCdfShape[2059] = { - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 4, - 65535, 0, 8, 65514, 65535, 0, 29, 65481, 65535, 0, - 121, 65439, 65535, 0, 239, 65284, 65535, 0, 8, 779, - 64999, 65527, 65535, 0, 8, 888, 64693, 65522, 65535, 0, - 29, 2604, 62843, 65497, 65531, 65535, 0, 25, 176, 4576, - 61164, 65275, 65527, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 4, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 4, 65535, 0, 33, 65502, 65535, - 0, 54, 65481, 65535, 0, 251, 65309, 65535, 0, 611, - 65074, 65535, 0, 1273, 64292, 65527, 65535, 0, 4, 1809, - 63940, 65518, 65535, 0, 88, 4392, 60603, 65426, 65531, 65535, - 0, 25, 419, 7046, 57756, 64961, 65514, 65531, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 4, 65531, - 65535, 0, 65535, 0, 8, 65531, 65535, 0, 4, 65527, - 65535, 0, 17, 65510, 65535, 0, 42, 65481, 65535, 0, - 197, 65342, 65531, 65535, 0, 385, 65154, 65535, 0, 1005, - 64522, 65535, 0, 8, 1985, 63469, 65533, 65535, 0, 38, - 3119, 61884, 65514, 65535, 0, 4, 6, 67, 4961, 60804, - 65472, 65535, 0, 17, 565, 9182, 56538, 65087, 65514, 65535, - 0, 8, 63, 327, 2118, 14490, 52774, 63839, 65376, 65522, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 17, 65522, 65535, 0, 59, 65489, 65535, 0, 50, 65522, - 65535, 0, 54, 65489, 65535, 0, 310, 65179, 65535, 0, - 615, 64836, 65535, 0, 4, 1503, 63965, 65535, 0, 2780, - 63383, 65535, 0, 21, 3919, 61051, 65527, 65535, 0, 84, - 6674, 59929, 65435, 65535, 0, 4, 255, 7976, 55784, 65150, - 65518, 65531, 65535, 0, 4, 8, 582, 10726, 53465, 64949, - 65518, 65535, 0, 29, 339, 3006, 17555, 49517, 62956, 65200, - 65497, 65531, 65535, 0, 2, 33, 138, 565, 2324, 7670, - 22089, 45966, 58949, 63479, 64966, 65380, 65518, 65535, 0, 65535, - 0, 65535, 0, 2, 65533, 65535, 0, 46, 65514, 65535, - 0, 414, 65091, 65535, 0, 540, 64911, 65535, 0, 419, - 65162, 65535, 0, 976, 64790, 65535, 0, 2977, 62495, 65531, - 65535, 0, 4, 3852, 61034, 65527, 65535, 0, 4, 29, - 6021, 60243, 65468, 65535, 0, 84, 6711, 58066, 65418, 65535, - 0, 13, 281, 9550, 54917, 65125, 65506, 65535, 0, 2, - 63, 984, 12108, 52644, 64342, 65435, 65527, 65535, 0, 29, - 251, 2014, 14871, 47553, 62881, 65229, 65518, 65535, 0, 13, - 142, 749, 4220, 18497, 45200, 60913, 64823, 65426, 65527, 65535, - 0, 13, 71, 264, 1176, 3789, 10500, 24480, 43488, 56324, - 62315, 64493, 65242, 65464, 65514, 65522, 65531, 65535, 0, 4, - 13, 38, 109, 205, 448, 850, 1708, 3429, 6276, 11371, - 19221, 29734, 40955, 49391, 55411, 59460, 62102, 63793, 64656, 65150, - 65401, 65485, 65522, 65531, 65535, 0, 65535, 0, 2, 65533, - 65535, 0, 1160, 65476, 65535, 0, 2, 6640, 64763, 65533, - 65535, 0, 2, 38, 9923, 61009, 65527, 65535, 0, 2, - 4949, 63092, 65533, 65535, 0, 2, 3090, 63398, 65533, 65535, - 0, 2, 2520, 58744, 65510, 65535, 0, 2, 13, 544, - 8784, 51403, 65148, 65533, 65535, 0, 2, 25, 1017, 10412, - 43550, 63651, 65489, 65527, 65535, 0, 2, 4, 29, 783, - 13377, 52462, 64524, 65495, 65533, 65535, 0, 2, 4, 6, - 100, 1817, 18451, 52590, 63559, 65376, 65531, 65535, 0, 2, - 4, 6, 46, 385, 2562, 11225, 37416, 60488, 65026, 65487, - 65529, 65533, 65535, 0, 2, 4, 6, 8, 10, 12, - 42, 222, 971, 5221, 19811, 45048, 60312, 64486, 65294, 65474, - 65525, 65529, 65533, 65535, 0, 2, 4, 8, 71, 167, - 666, 2533, 7875, 19622, 38082, 54359, 62108, 64633, 65290, 65495, - 65529, 65533, 65535, 0, 2, 4, 6, 8, 10, 13, - 109, 586, 1930, 4949, 11600, 22641, 36125, 48312, 56899, 61495, - 63927, 64932, 65389, 65489, 65518, 65531, 65533, 65535, 0, 4, - 6, 8, 67, 209, 712, 1838, 4195, 8432, 14432, 22834, - 31723, 40523, 48139, 53929, 57865, 60657, 62403, 63584, 64363, 64907, - 65167, 65372, 65472, 65514, 65535, 0, 2, 4, 13, 25, - 42, 46, 50, 75, 113, 147, 281, 448, 657, 909, - 1185, 1591, 1976, 2600, 3676, 5317, 7398, 9914, 12941, 16169, - 19477, 22885, 26464, 29851, 33360, 37228, 41139, 44802, 48654, 52058, - 55181, 57676, 59581, 61022, 62190, 63107, 63676, 64199, 64547, 64924, - 65158, 65313, 65430, 65481, 65518, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65533, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65533, 65535, 0, 2, 65535, 0, - 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, - 65535, 0, 2, 4, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 4, 65531, 65533, 65535, 0, 2, 4, 65531, - 65533, 65535, 0, 2, 4, 6, 65524, 65533, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65533, 65535, 0, 65533, - 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, - 2, 65533, 65535, 0, 2, 4, 65532, 65535, 0, 6, - 65523, 65535, 0, 2, 15, 65530, 65533, 65535, 0, 2, - 35, 65493, 65531, 65533, 65535, 0, 2, 4, 158, 65382, - 65531, 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 2, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 65533, 65535, 0, 9, 65512, 65535, 0, 2, - 12, 65529, 65535, 0, 2, 73, 65434, 65533, 65535, 0, - 2, 240, 65343, 65533, 65535, 0, 2, 476, 65017, 65531, - 65533, 65535, 0, 2, 4, 1046, 64686, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 1870, 63898, 65529, 65531, 65533, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65533, 65535, - 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, - 65532, 65535, 0, 6, 65533, 65535, 0, 6, 65523, 65535, - 0, 2, 65532, 65535, 0, 137, 65439, 65535, 0, 576, - 64899, 65533, 65535, 0, 2, 289, 65299, 65533, 65535, 0, - 2, 4, 6, 880, 64134, 65531, 65533, 65535, 0, 2, - 4, 1853, 63347, 65533, 65535, 0, 2, 6, 2516, 61762, - 65529, 65531, 65533, 65535, 0, 2, 4, 9, 3980, 61380, - 65503, 65529, 65531, 65533, 65535, 0, 2, 4, 6, 8, - 10, 12, 61, 6393, 59859, 65466, 65527, 65529, 65531, 65533, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 2, 65532, - 65535, 0, 3, 65529, 65535, 0, 2, 65529, 65535, 0, - 61, 65453, 65535, 0, 234, 65313, 65535, 0, 503, 65138, - 65535, 0, 155, 65402, 65533, 65535, 0, 2, 1058, 64554, - 65533, 65535, 0, 2, 4, 3138, 62109, 65531, 65533, 65535, - 0, 2, 4, 2031, 63339, 65531, 65533, 65535, 0, 2, - 4, 6, 9, 4155, 60778, 65523, 65529, 65531, 65533, 65535, - 0, 2, 4, 41, 6189, 59269, 65490, 65531, 65533, 65535, - 0, 2, 4, 6, 210, 8789, 57043, 65400, 65528, 65531, - 65533, 65535, 0, 2, 4, 6, 8, 26, 453, 10086, - 55499, 64948, 65483, 65524, 65527, 65529, 65531, 65533, 65535, 0, - 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, - 114, 1014, 11202, 52670, 64226, 65356, 65503, 65514, 65523, 65525, - 65527, 65529, 65531, 65533, 65535, 0, 65533, 65535, 0, 15, - 65301, 65535, 0, 152, 64807, 65535, 0, 2, 3328, 63308, - 65535, 0, 2, 4050, 59730, 65533, 65535, 0, 2, 164, - 10564, 61894, 65529, 65535, 0, 15, 6712, 59831, 65076, 65532, - 65535, 0, 32, 7712, 57449, 65459, 65535, 0, 2, 210, - 7849, 53110, 65021, 65523, 65535, 0, 2, 12, 1081, 13883, - 48262, 62870, 65477, 65535, 0, 2, 88, 847, 6145, 37852, - 62012, 65454, 65533, 65535, 0, 9, 47, 207, 1823, 14522, - 45521, 61069, 64891, 65481, 65528, 65531, 65533, 65535, 0, 2, - 9, 488, 2881, 12758, 38703, 58412, 64420, 65410, 65533, 65535, - 0, 2, 4, 6, 61, 333, 1891, 6486, 19720, 43188, - 57547, 62472, 64796, 65421, 65497, 65523, 65529, 65531, 65533, 65535, - 0, 2, 4, 6, 8, 10, 12, 29, 117, 447, - 1528, 6138, 21242, 43133, 56495, 62432, 64746, 65362, 65500, 65529, - 65531, 65533, 65535, 0, 2, 18, 105, 301, 760, 1490, - 3472, 7568, 15002, 26424, 40330, 53029, 60048, 62964, 64274, 64890, - 65337, 65445, 65489, 65513, 65527, 65530, 65533, 65535, 0, 2, - 4, 6, 41, 102, 409, 853, 2031, 4316, 7302, 11328, - 16869, 24825, 34926, 43481, 50877, 56126, 59874, 62103, 63281, 63857, - 64166, 64675, 65382, 65522, 65531, 65533, 65535, 0, 2, 4, - 6, 8, 10, 12, 14, 16, 18, 29, 38, 53, - 58, 96, 181, 503, 1183, 2849, 5590, 8600, 11379, 13942, - 16478, 19453, 22638, 26039, 29411, 32921, 37596, 41433, 44998, 48560, - 51979, 55106, 57666, 59892, 61485, 62616, 63484, 64018, 64375, 64685, - 64924, 65076, 65278, 65395, 65471, 65509, 65529, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, - 0, 65535, 0, 65535, 0, 2, 65533, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, 65535, - 0, 2, 65533, 65535, 0, 2, 65533, 65535, 0, 7, - 65519, 65535, 0, 2, 14, 65491, 65533, 65535, 0, 2, - 81, 65427, 65531, 65533, 65535, 0, 2, 4, 312, 65293, - 65528, 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, - 2, 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65533, - 65535, 0, 5, 65523, 65535, 0, 2, 65533, 65535, 0, - 7, 65526, 65535, 0, 46, 65464, 65533, 65535, 0, 2, - 120, 65309, 65533, 65535, 0, 2, 5, 362, 65097, 65533, - 65535, 0, 2, 18, 1164, 64785, 65528, 65531, 65533, 65535, - 0, 65535, 0, 65535, 0, 65535, 0, 65533, 65535, 0, - 65535, 0, 65533, 65535, 0, 2, 65533, 65535, 0, 2, - 65533, 65535, 0, 2, 65533, 65535, 0, 2, 65530, 65535, - 0, 2, 65523, 65535, 0, 69, 65477, 65535, 0, 141, - 65459, 65535, 0, 194, 65325, 65533, 65535, 0, 2, 543, - 64912, 65533, 65535, 0, 5, 1270, 64301, 65529, 65531, 65533, - 65535, 0, 2, 4, 12, 2055, 63538, 65508, 65531, 65533, - 65535, 0, 2, 7, 102, 3775, 61970, 65429, 65526, 65528, - 65533, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 2, - 65533, 65535, 0, 2, 65535, 0, 9, 65533, 65535, 0, - 25, 65512, 65535, 0, 2, 65533, 65535, 0, 44, 65480, - 65535, 0, 48, 65475, 65535, 0, 162, 65373, 65535, 0, - 637, 64806, 65533, 65535, 0, 2, 935, 64445, 65533, 65535, - 0, 2, 4, 1662, 64083, 65533, 65535, 0, 2, 12, - 3036, 62469, 65521, 65533, 65535, 0, 2, 120, 5405, 60468, - 65469, 65531, 65533, 65535, 0, 2, 4, 18, 254, 6663, - 58999, 65272, 65528, 65533, 65535, 0, 2, 4, 9, 12, - 67, 591, 8981, 56781, 64564, 65365, 65508, 65524, 65526, 65529, - 65531, 65533, 65535, 0, 65535, 0, 65535, 0, 2, 65533, - 65535, 0, 9, 65526, 65535, 0, 14, 65503, 65535, 0, - 127, 65390, 65535, 0, 517, 64990, 65535, 0, 178, 65330, - 65535, 0, 2, 1055, 64533, 65533, 65535, 0, 2, 1558, - 63942, 65533, 65535, 0, 2, 2205, 63173, 65533, 65535, 0, - 25, 4493, 60862, 65505, 65533, 65535, 0, 2, 48, 5890, - 59442, 65482, 65533, 65535, 0, 2, 4, 127, 7532, 58191, - 65394, 65533, 65535, 0, 2, 5, 32, 550, 10388, 54924, - 65046, 65510, 65531, 65533, 65535, 0, 2, 4, 30, 150, - 1685, 14340, 51375, 63619, 65288, 65503, 65528, 65533, 65535, 0, - 2, 4, 6, 8, 28, 97, 473, 2692, 15407, 50020, - 62880, 65064, 65445, 65508, 65531, 65533, 65535, 0, 2, 4, - 12, 32, 79, 150, 372, 907, 2184, 5868, 18207, 45431, - 59856, 64031, 65096, 65401, 65481, 65507, 65521, 65523, 65525, 65527, - 65529, 65531, 65533, 65535, 0, 65533, 65535, 0, 182, 65491, - 65535, 0, 877, 64286, 65535, 0, 9, 2708, 63612, 65533, - 65535, 0, 2, 6038, 59532, 65535, 0, 2, 92, 5500, - 60539, 65533, 65535, 0, 268, 8908, 56512, 65385, 65535, 0, - 129, 13110, 52742, 65036, 65535, 0, 2, 806, 14003, 51929, - 64732, 65523, 65535, 0, 7, 92, 2667, 18159, 47678, 62610, - 65355, 65535, 0, 32, 1836, 19676, 48237, 61677, 64960, 65526, - 65535, 0, 21, 159, 967, 5668, 22782, 44709, 58317, 64020, - 65406, 65528, 65535, 0, 7, 162, 1838, 8328, 23929, 43014, - 56394, 63374, 65216, 65484, 65521, 65535, 0, 2, 4, 6, - 28, 268, 1120, 3613, 10688, 24185, 40989, 54917, 61684, 64510, - 65403, 65530, 65535, 0, 2, 16, 44, 139, 492, 1739, - 5313, 13558, 26766, 41566, 52446, 58937, 62815, 64480, 65201, 65454, - 65524, 65533, 65535, 0, 7, 25, 76, 263, 612, 1466, - 3325, 6832, 12366, 20152, 29466, 39255, 47360, 53506, 57740, 60726, - 62845, 64131, 64882, 65260, 65459, 65521, 65528, 65530, 65535, 0, - 2, 4, 14, 48, 136, 312, 653, 1240, 2369, 4327, - 7028, 10759, 15449, 21235, 28027, 35386, 42938, 49562, 54990, 59119, - 62086, 63916, 64863, 65249, 65445, 65493, 65523, 65535, 0, 2, - 4, 6, 8, 10, 12, 21, 83, 208, 409, 723, - 1152, 1868, 2951, 4463, 6460, 8979, 11831, 15195, 18863, 22657, - 26762, 30881, 34963, 39098, 43054, 47069, 50620, 53871, 56821, 59386, - 61340, 62670, 63512, 64023, 64429, 64750, 64944, 65126, 65279, 65366, - 65413, 65445, 65473, 65505, 65510, 65521, 65528, 65530, 65535}; - -/* pointers to cdf tables for quantizer indices */ -const WebRtc_UWord16 *WebRtcIsac_kQKltCdfPtrGain[3][12] = { -{WebRtcIsac_kQKltCdfGain +0 +0, WebRtcIsac_kQKltCdfGain +0 +8, WebRtcIsac_kQKltCdfGain +0 +22, WebRtcIsac_kQKltCdfGain +0 +32, WebRtcIsac_kQKltCdfGain +0 +48, WebRtcIsac_kQKltCdfGain +0 +60, WebRtcIsac_kQKltCdfGain +0 +81, WebRtcIsac_kQKltCdfGain +0 +95, WebRtcIsac_kQKltCdfGain +0 +128, WebRtcIsac_kQKltCdfGain +0 +152, -WebRtcIsac_kQKltCdfGain +0 +210, WebRtcIsac_kQKltCdfGain +0 +264}, -{WebRtcIsac_kQKltCdfGain +404 +0, WebRtcIsac_kQKltCdfGain +404 +8, WebRtcIsac_kQKltCdfGain +404 +21, WebRtcIsac_kQKltCdfGain +404 +30, WebRtcIsac_kQKltCdfGain +404 +46, WebRtcIsac_kQKltCdfGain +404 +58, WebRtcIsac_kQKltCdfGain +404 +79, WebRtcIsac_kQKltCdfGain +404 +93, WebRtcIsac_kQKltCdfGain +404 +125, WebRtcIsac_kQKltCdfGain +404 +149, -WebRtcIsac_kQKltCdfGain +404 +207, WebRtcIsac_kQKltCdfGain +404 +260}, -{WebRtcIsac_kQKltCdfGain +803 +0, WebRtcIsac_kQKltCdfGain +803 +8, WebRtcIsac_kQKltCdfGain +803 +22, WebRtcIsac_kQKltCdfGain +803 +31, WebRtcIsac_kQKltCdfGain +803 +48, WebRtcIsac_kQKltCdfGain +803 +60, WebRtcIsac_kQKltCdfGain +803 +81, WebRtcIsac_kQKltCdfGain +803 +96, WebRtcIsac_kQKltCdfGain +803 +129, WebRtcIsac_kQKltCdfGain +803 +154, -WebRtcIsac_kQKltCdfGain +803 +212, WebRtcIsac_kQKltCdfGain +803 +268}}; - -const WebRtc_UWord16 *WebRtcIsac_kQKltCdfPtrShape[3][108] = { -{WebRtcIsac_kQKltCdfShape +0 +0, WebRtcIsac_kQKltCdfShape +0 +2, WebRtcIsac_kQKltCdfShape +0 +4, WebRtcIsac_kQKltCdfShape +0 +6, WebRtcIsac_kQKltCdfShape +0 +8, WebRtcIsac_kQKltCdfShape +0 +10, WebRtcIsac_kQKltCdfShape +0 +12, WebRtcIsac_kQKltCdfShape +0 +14, WebRtcIsac_kQKltCdfShape +0 +16, WebRtcIsac_kQKltCdfShape +0 +18, -WebRtcIsac_kQKltCdfShape +0 +21, WebRtcIsac_kQKltCdfShape +0 +25, WebRtcIsac_kQKltCdfShape +0 +29, WebRtcIsac_kQKltCdfShape +0 +33, WebRtcIsac_kQKltCdfShape +0 +37, WebRtcIsac_kQKltCdfShape +0 +43, WebRtcIsac_kQKltCdfShape +0 +49, WebRtcIsac_kQKltCdfShape +0 +56, WebRtcIsac_kQKltCdfShape +0 +64, WebRtcIsac_kQKltCdfShape +0 +66, -WebRtcIsac_kQKltCdfShape +0 +68, WebRtcIsac_kQKltCdfShape +0 +70, WebRtcIsac_kQKltCdfShape +0 +72, WebRtcIsac_kQKltCdfShape +0 +75, WebRtcIsac_kQKltCdfShape +0 +77, WebRtcIsac_kQKltCdfShape +0 +79, WebRtcIsac_kQKltCdfShape +0 +81, WebRtcIsac_kQKltCdfShape +0 +83, WebRtcIsac_kQKltCdfShape +0 +86, WebRtcIsac_kQKltCdfShape +0 +90, -WebRtcIsac_kQKltCdfShape +0 +94, WebRtcIsac_kQKltCdfShape +0 +98, WebRtcIsac_kQKltCdfShape +0 +102, WebRtcIsac_kQKltCdfShape +0 +107, WebRtcIsac_kQKltCdfShape +0 +113, WebRtcIsac_kQKltCdfShape +0 +120, WebRtcIsac_kQKltCdfShape +0 +129, WebRtcIsac_kQKltCdfShape +0 +131, WebRtcIsac_kQKltCdfShape +0 +133, WebRtcIsac_kQKltCdfShape +0 +135, -WebRtcIsac_kQKltCdfShape +0 +137, WebRtcIsac_kQKltCdfShape +0 +141, WebRtcIsac_kQKltCdfShape +0 +143, WebRtcIsac_kQKltCdfShape +0 +147, WebRtcIsac_kQKltCdfShape +0 +151, WebRtcIsac_kQKltCdfShape +0 +155, WebRtcIsac_kQKltCdfShape +0 +159, WebRtcIsac_kQKltCdfShape +0 +164, WebRtcIsac_kQKltCdfShape +0 +168, WebRtcIsac_kQKltCdfShape +0 +172, -WebRtcIsac_kQKltCdfShape +0 +178, WebRtcIsac_kQKltCdfShape +0 +184, WebRtcIsac_kQKltCdfShape +0 +192, WebRtcIsac_kQKltCdfShape +0 +200, WebRtcIsac_kQKltCdfShape +0 +211, WebRtcIsac_kQKltCdfShape +0 +213, WebRtcIsac_kQKltCdfShape +0 +215, WebRtcIsac_kQKltCdfShape +0 +217, WebRtcIsac_kQKltCdfShape +0 +219, WebRtcIsac_kQKltCdfShape +0 +223, -WebRtcIsac_kQKltCdfShape +0 +227, WebRtcIsac_kQKltCdfShape +0 +231, WebRtcIsac_kQKltCdfShape +0 +235, WebRtcIsac_kQKltCdfShape +0 +239, WebRtcIsac_kQKltCdfShape +0 +243, WebRtcIsac_kQKltCdfShape +0 +248, WebRtcIsac_kQKltCdfShape +0 +252, WebRtcIsac_kQKltCdfShape +0 +258, WebRtcIsac_kQKltCdfShape +0 +264, WebRtcIsac_kQKltCdfShape +0 +273, -WebRtcIsac_kQKltCdfShape +0 +282, WebRtcIsac_kQKltCdfShape +0 +293, WebRtcIsac_kQKltCdfShape +0 +308, WebRtcIsac_kQKltCdfShape +0 +310, WebRtcIsac_kQKltCdfShape +0 +312, WebRtcIsac_kQKltCdfShape +0 +316, WebRtcIsac_kQKltCdfShape +0 +320, WebRtcIsac_kQKltCdfShape +0 +324, WebRtcIsac_kQKltCdfShape +0 +328, WebRtcIsac_kQKltCdfShape +0 +332, -WebRtcIsac_kQKltCdfShape +0 +336, WebRtcIsac_kQKltCdfShape +0 +341, WebRtcIsac_kQKltCdfShape +0 +347, WebRtcIsac_kQKltCdfShape +0 +354, WebRtcIsac_kQKltCdfShape +0 +360, WebRtcIsac_kQKltCdfShape +0 +368, WebRtcIsac_kQKltCdfShape +0 +378, WebRtcIsac_kQKltCdfShape +0 +388, WebRtcIsac_kQKltCdfShape +0 +400, WebRtcIsac_kQKltCdfShape +0 +418, -WebRtcIsac_kQKltCdfShape +0 +445, WebRtcIsac_kQKltCdfShape +0 +447, WebRtcIsac_kQKltCdfShape +0 +451, WebRtcIsac_kQKltCdfShape +0 +455, WebRtcIsac_kQKltCdfShape +0 +461, WebRtcIsac_kQKltCdfShape +0 +468, WebRtcIsac_kQKltCdfShape +0 +474, WebRtcIsac_kQKltCdfShape +0 +480, WebRtcIsac_kQKltCdfShape +0 +486, WebRtcIsac_kQKltCdfShape +0 +495, -WebRtcIsac_kQKltCdfShape +0 +505, WebRtcIsac_kQKltCdfShape +0 +516, WebRtcIsac_kQKltCdfShape +0 +528, WebRtcIsac_kQKltCdfShape +0 +543, WebRtcIsac_kQKltCdfShape +0 +564, WebRtcIsac_kQKltCdfShape +0 +583, WebRtcIsac_kQKltCdfShape +0 +608, WebRtcIsac_kQKltCdfShape +0 +635}, -{WebRtcIsac_kQKltCdfShape +686 +0, WebRtcIsac_kQKltCdfShape +686 +2, WebRtcIsac_kQKltCdfShape +686 +4, WebRtcIsac_kQKltCdfShape +686 +6, WebRtcIsac_kQKltCdfShape +686 +8, WebRtcIsac_kQKltCdfShape +686 +11, WebRtcIsac_kQKltCdfShape +686 +13, WebRtcIsac_kQKltCdfShape +686 +15, WebRtcIsac_kQKltCdfShape +686 +17, WebRtcIsac_kQKltCdfShape +686 +20, -WebRtcIsac_kQKltCdfShape +686 +23, WebRtcIsac_kQKltCdfShape +686 +27, WebRtcIsac_kQKltCdfShape +686 +31, WebRtcIsac_kQKltCdfShape +686 +35, WebRtcIsac_kQKltCdfShape +686 +40, WebRtcIsac_kQKltCdfShape +686 +44, WebRtcIsac_kQKltCdfShape +686 +50, WebRtcIsac_kQKltCdfShape +686 +56, WebRtcIsac_kQKltCdfShape +686 +63, WebRtcIsac_kQKltCdfShape +686 +65, -WebRtcIsac_kQKltCdfShape +686 +67, WebRtcIsac_kQKltCdfShape +686 +69, WebRtcIsac_kQKltCdfShape +686 +71, WebRtcIsac_kQKltCdfShape +686 +73, WebRtcIsac_kQKltCdfShape +686 +75, WebRtcIsac_kQKltCdfShape +686 +77, WebRtcIsac_kQKltCdfShape +686 +79, WebRtcIsac_kQKltCdfShape +686 +82, WebRtcIsac_kQKltCdfShape +686 +85, WebRtcIsac_kQKltCdfShape +686 +89, -WebRtcIsac_kQKltCdfShape +686 +93, WebRtcIsac_kQKltCdfShape +686 +97, WebRtcIsac_kQKltCdfShape +686 +102, WebRtcIsac_kQKltCdfShape +686 +106, WebRtcIsac_kQKltCdfShape +686 +112, WebRtcIsac_kQKltCdfShape +686 +119, WebRtcIsac_kQKltCdfShape +686 +127, WebRtcIsac_kQKltCdfShape +686 +129, WebRtcIsac_kQKltCdfShape +686 +131, WebRtcIsac_kQKltCdfShape +686 +133, -WebRtcIsac_kQKltCdfShape +686 +135, WebRtcIsac_kQKltCdfShape +686 +137, WebRtcIsac_kQKltCdfShape +686 +139, WebRtcIsac_kQKltCdfShape +686 +142, WebRtcIsac_kQKltCdfShape +686 +146, WebRtcIsac_kQKltCdfShape +686 +150, WebRtcIsac_kQKltCdfShape +686 +154, WebRtcIsac_kQKltCdfShape +686 +158, WebRtcIsac_kQKltCdfShape +686 +162, WebRtcIsac_kQKltCdfShape +686 +167, -WebRtcIsac_kQKltCdfShape +686 +173, WebRtcIsac_kQKltCdfShape +686 +179, WebRtcIsac_kQKltCdfShape +686 +186, WebRtcIsac_kQKltCdfShape +686 +194, WebRtcIsac_kQKltCdfShape +686 +205, WebRtcIsac_kQKltCdfShape +686 +207, WebRtcIsac_kQKltCdfShape +686 +209, WebRtcIsac_kQKltCdfShape +686 +211, WebRtcIsac_kQKltCdfShape +686 +214, WebRtcIsac_kQKltCdfShape +686 +218, -WebRtcIsac_kQKltCdfShape +686 +222, WebRtcIsac_kQKltCdfShape +686 +226, WebRtcIsac_kQKltCdfShape +686 +230, WebRtcIsac_kQKltCdfShape +686 +234, WebRtcIsac_kQKltCdfShape +686 +238, WebRtcIsac_kQKltCdfShape +686 +242, WebRtcIsac_kQKltCdfShape +686 +247, WebRtcIsac_kQKltCdfShape +686 +253, WebRtcIsac_kQKltCdfShape +686 +262, WebRtcIsac_kQKltCdfShape +686 +269, -WebRtcIsac_kQKltCdfShape +686 +278, WebRtcIsac_kQKltCdfShape +686 +289, WebRtcIsac_kQKltCdfShape +686 +305, WebRtcIsac_kQKltCdfShape +686 +307, WebRtcIsac_kQKltCdfShape +686 +309, WebRtcIsac_kQKltCdfShape +686 +311, WebRtcIsac_kQKltCdfShape +686 +315, WebRtcIsac_kQKltCdfShape +686 +319, WebRtcIsac_kQKltCdfShape +686 +323, WebRtcIsac_kQKltCdfShape +686 +327, -WebRtcIsac_kQKltCdfShape +686 +331, WebRtcIsac_kQKltCdfShape +686 +335, WebRtcIsac_kQKltCdfShape +686 +340, WebRtcIsac_kQKltCdfShape +686 +346, WebRtcIsac_kQKltCdfShape +686 +354, WebRtcIsac_kQKltCdfShape +686 +362, WebRtcIsac_kQKltCdfShape +686 +374, WebRtcIsac_kQKltCdfShape +686 +384, WebRtcIsac_kQKltCdfShape +686 +396, WebRtcIsac_kQKltCdfShape +686 +413, -WebRtcIsac_kQKltCdfShape +686 +439, WebRtcIsac_kQKltCdfShape +686 +442, WebRtcIsac_kQKltCdfShape +686 +446, WebRtcIsac_kQKltCdfShape +686 +450, WebRtcIsac_kQKltCdfShape +686 +455, WebRtcIsac_kQKltCdfShape +686 +461, WebRtcIsac_kQKltCdfShape +686 +468, WebRtcIsac_kQKltCdfShape +686 +475, WebRtcIsac_kQKltCdfShape +686 +481, WebRtcIsac_kQKltCdfShape +686 +489, -WebRtcIsac_kQKltCdfShape +686 +498, WebRtcIsac_kQKltCdfShape +686 +508, WebRtcIsac_kQKltCdfShape +686 +522, WebRtcIsac_kQKltCdfShape +686 +534, WebRtcIsac_kQKltCdfShape +686 +554, WebRtcIsac_kQKltCdfShape +686 +577, WebRtcIsac_kQKltCdfShape +686 +602, WebRtcIsac_kQKltCdfShape +686 +631}, -{WebRtcIsac_kQKltCdfShape +1368 +0, WebRtcIsac_kQKltCdfShape +1368 +2, WebRtcIsac_kQKltCdfShape +1368 +4, WebRtcIsac_kQKltCdfShape +1368 +6, WebRtcIsac_kQKltCdfShape +1368 +8, WebRtcIsac_kQKltCdfShape +1368 +10, WebRtcIsac_kQKltCdfShape +1368 +12, WebRtcIsac_kQKltCdfShape +1368 +14, WebRtcIsac_kQKltCdfShape +1368 +16, WebRtcIsac_kQKltCdfShape +1368 +20, -WebRtcIsac_kQKltCdfShape +1368 +24, WebRtcIsac_kQKltCdfShape +1368 +28, WebRtcIsac_kQKltCdfShape +1368 +32, WebRtcIsac_kQKltCdfShape +1368 +36, WebRtcIsac_kQKltCdfShape +1368 +40, WebRtcIsac_kQKltCdfShape +1368 +44, WebRtcIsac_kQKltCdfShape +1368 +50, WebRtcIsac_kQKltCdfShape +1368 +57, WebRtcIsac_kQKltCdfShape +1368 +65, WebRtcIsac_kQKltCdfShape +1368 +67, -WebRtcIsac_kQKltCdfShape +1368 +69, WebRtcIsac_kQKltCdfShape +1368 +71, WebRtcIsac_kQKltCdfShape +1368 +73, WebRtcIsac_kQKltCdfShape +1368 +75, WebRtcIsac_kQKltCdfShape +1368 +77, WebRtcIsac_kQKltCdfShape +1368 +79, WebRtcIsac_kQKltCdfShape +1368 +81, WebRtcIsac_kQKltCdfShape +1368 +85, WebRtcIsac_kQKltCdfShape +1368 +89, WebRtcIsac_kQKltCdfShape +1368 +93, -WebRtcIsac_kQKltCdfShape +1368 +97, WebRtcIsac_kQKltCdfShape +1368 +101, WebRtcIsac_kQKltCdfShape +1368 +105, WebRtcIsac_kQKltCdfShape +1368 +110, WebRtcIsac_kQKltCdfShape +1368 +116, WebRtcIsac_kQKltCdfShape +1368 +123, WebRtcIsac_kQKltCdfShape +1368 +132, WebRtcIsac_kQKltCdfShape +1368 +134, WebRtcIsac_kQKltCdfShape +1368 +136, WebRtcIsac_kQKltCdfShape +1368 +138, -WebRtcIsac_kQKltCdfShape +1368 +141, WebRtcIsac_kQKltCdfShape +1368 +143, WebRtcIsac_kQKltCdfShape +1368 +146, WebRtcIsac_kQKltCdfShape +1368 +150, WebRtcIsac_kQKltCdfShape +1368 +154, WebRtcIsac_kQKltCdfShape +1368 +158, WebRtcIsac_kQKltCdfShape +1368 +162, WebRtcIsac_kQKltCdfShape +1368 +166, WebRtcIsac_kQKltCdfShape +1368 +170, WebRtcIsac_kQKltCdfShape +1368 +174, -WebRtcIsac_kQKltCdfShape +1368 +179, WebRtcIsac_kQKltCdfShape +1368 +185, WebRtcIsac_kQKltCdfShape +1368 +193, WebRtcIsac_kQKltCdfShape +1368 +203, WebRtcIsac_kQKltCdfShape +1368 +214, WebRtcIsac_kQKltCdfShape +1368 +216, WebRtcIsac_kQKltCdfShape +1368 +218, WebRtcIsac_kQKltCdfShape +1368 +220, WebRtcIsac_kQKltCdfShape +1368 +224, WebRtcIsac_kQKltCdfShape +1368 +227, -WebRtcIsac_kQKltCdfShape +1368 +231, WebRtcIsac_kQKltCdfShape +1368 +235, WebRtcIsac_kQKltCdfShape +1368 +239, WebRtcIsac_kQKltCdfShape +1368 +243, WebRtcIsac_kQKltCdfShape +1368 +247, WebRtcIsac_kQKltCdfShape +1368 +251, WebRtcIsac_kQKltCdfShape +1368 +256, WebRtcIsac_kQKltCdfShape +1368 +262, WebRtcIsac_kQKltCdfShape +1368 +269, WebRtcIsac_kQKltCdfShape +1368 +277, -WebRtcIsac_kQKltCdfShape +1368 +286, WebRtcIsac_kQKltCdfShape +1368 +297, WebRtcIsac_kQKltCdfShape +1368 +315, WebRtcIsac_kQKltCdfShape +1368 +317, WebRtcIsac_kQKltCdfShape +1368 +319, WebRtcIsac_kQKltCdfShape +1368 +323, WebRtcIsac_kQKltCdfShape +1368 +327, WebRtcIsac_kQKltCdfShape +1368 +331, WebRtcIsac_kQKltCdfShape +1368 +335, WebRtcIsac_kQKltCdfShape +1368 +339, -WebRtcIsac_kQKltCdfShape +1368 +343, WebRtcIsac_kQKltCdfShape +1368 +349, WebRtcIsac_kQKltCdfShape +1368 +355, WebRtcIsac_kQKltCdfShape +1368 +361, WebRtcIsac_kQKltCdfShape +1368 +368, WebRtcIsac_kQKltCdfShape +1368 +376, WebRtcIsac_kQKltCdfShape +1368 +385, WebRtcIsac_kQKltCdfShape +1368 +397, WebRtcIsac_kQKltCdfShape +1368 +411, WebRtcIsac_kQKltCdfShape +1368 +429, -WebRtcIsac_kQKltCdfShape +1368 +456, WebRtcIsac_kQKltCdfShape +1368 +459, WebRtcIsac_kQKltCdfShape +1368 +463, WebRtcIsac_kQKltCdfShape +1368 +467, WebRtcIsac_kQKltCdfShape +1368 +473, WebRtcIsac_kQKltCdfShape +1368 +478, WebRtcIsac_kQKltCdfShape +1368 +485, WebRtcIsac_kQKltCdfShape +1368 +491, WebRtcIsac_kQKltCdfShape +1368 +497, WebRtcIsac_kQKltCdfShape +1368 +505, -WebRtcIsac_kQKltCdfShape +1368 +514, WebRtcIsac_kQKltCdfShape +1368 +523, WebRtcIsac_kQKltCdfShape +1368 +535, WebRtcIsac_kQKltCdfShape +1368 +548, WebRtcIsac_kQKltCdfShape +1368 +565, WebRtcIsac_kQKltCdfShape +1368 +585, WebRtcIsac_kQKltCdfShape +1368 +611, WebRtcIsac_kQKltCdfShape +1368 +640}}; - -/* code length for all coefficients using different models */ -const double WebRtcIsac_kQKltCodeLenGain[392] = { - 12.29956028, 7.83007500, 4.25642781, 0.17489215, 4.27591254, 7.66908312, 10.47643804, 11.91253716, 9.03421572, 8.57373525, - 6.73555740, 5.41409855, 3.65237863, 0.42623449, 3.22418399, 5.68145719, 7.14201900, 8.20558413, 9.34178852, 13.00000000, - 13.00000000, 9.47643804, 6.71459778, 3.55472644, 0.28457419, 3.70652835, 7.41128536, 11.60768258, 14.00000000, 12.29956028, - 9.29956028, 8.02845645, 6.54878889, 5.07667251, 2.87749552, 0.65310542, 3.11316029, 4.87911416, 5.89540125, 6.76398581, - 7.70537925, 9.67807191, 11.14201900, 14.00000000, 14.00000000, 12.29956028, 9.69621925, 7.70537925, 5.49915812, 2.67441345, - 0.63381441, 2.74999773, 5.76877882, 8.41503750, 10.75207249, 14.00000000, 9.60768258, 8.85025288, 8.09913319, 7.09010692, - 6.36337538, 5.46667027, 4.51618422, 2.64189829, 1.21843537, 2.43823474, 3.89409149, 4.99718498, 6.00989604, 6.78325414, - 7.39637366, 8.76159526, 10.60768258, 11.67807191, 12.29956028, 9.54056838, 7.95560588, 5.63040265, 3.81264793, 2.18521728, - 1.33727600, 2.37909290, 3.94981123, 5.52426657, 7.32051990, 8.84012866, 11.35614381, 13.00000000, 12.29956028, 11.35614381, - 11.35614381, 10.24511250, 9.12963528, 8.80032766, 7.97763219, 7.83007500, 7.09913319, 6.68711704, 6.41879942, 5.74379131, - 5.25096862, 4.43061904, 3.54492969, 2.72664147, 2.16306204, 2.71142226, 3.34357514, 4.07444556, 4.95151313, 5.68145719, - 6.12041675, 6.55085135, 7.00282052, 7.55705650, 8.44541115, 9.41503750, 10.91253716, 11.60768258, 14.00000000, 14.00000000, - 13.00000000, 11.60768258, 10.47643804, 8.80032766, 7.38161450, 6.52426657, 5.47447919, 4.53749773, 3.92719747, 3.41058292, - 2.88495635, 2.79344346, 2.85805254, 3.18261657, 3.57216340, 4.08225499, 4.74438125, 5.51215997, 6.30477171, 7.15450995, - 8.28575448, 9.69621925, 11.60768258, 13.00000000, 13.67807191, 15.00000000, 15.00000000, 14.00000000, 12.41503750, 11.29956028, - 12.41503750, 11.60768258, 10.11735695, 9.47643804, 9.12963528, 9.41503750, 8.23181568, 7.97763219, 7.82507432, 7.50814690, - 7.33466408, 6.99157138, 6.95834085, 6.80524315, 6.47447919, 6.35614381, 6.02128954, 5.71459778, 5.58109327, 5.05821876, - 4.94539568, 4.59220115, 4.27591254, 4.01522554, 3.89376424, 3.83760867, 3.73321346, 3.74674342, 3.90493270, 4.18942837, - 4.33599724, 4.42446075, 4.81760565, 5.07667251, 5.54570071, 5.95697272, 6.46667027, 6.91253716, 7.33466408, 7.82507432, - 8.05163277, 9.12963528, 9.02272008, 9.77118131, 9.60768258, 10.11735695, 12.00000000, 12.83007500, 15.00000000, 15.00000000, - 14.00000000, 15.00000000, 11.75207249, 12.29956028, 10.95560588, 9.93391081, 9.02272008, 8.00564656, 7.82507432, 7.28919357, - 6.77599833, 6.56745810, 6.19910010, 6.23347109, 5.67694524, 5.41879942, 4.96039548, 4.88170777, 4.60768258, 4.52883287, - 4.28876323, 4.17583679, 4.21332197, 4.14474217, 4.16119001, 4.12809476, 4.18501706, 4.28489599, 4.35299136, 4.60286019, - 4.63040265, 4.81017544, 5.00989604, 5.33822190, 5.43489792, 5.84644797, 6.13272126, 6.75444729, 7.36337538, 7.96108101, - 8.54056838, 9.28575448, 9.12963528, 9.47643804, 10.75207249, 12.29956028, 12.41503750, 11.14201900, 12.83007500, 15.00000000, - 15.00000000, 15.00000000, 15.00000000, 15.00000000, 15.00000000, 14.00000000, 15.00000000, 15.00000000, 15.00000000, 15.00000000, - 13.67807191, 12.41503750, 11.91253716, 11.60768258, 12.29956028, 13.00000000, 12.29956028, 10.95560588, 10.60768258, 9.77118131, - 10.02272008, 10.11735695, 10.14201900, 10.75207249, 10.75207249, 9.69621925, 9.47643804, 9.75207249, 8.72387559, 8.47643804, - 8.68711704, 8.40754296, 8.20558413, 8.26529038, 7.84518189, 7.89147554, 7.64685317, 7.41128536, 7.33466408, 7.42635281, - 7.24845594, 7.11430363, 7.07518750, 7.07518750, 6.70537925, 6.64906082, 6.73555740, 6.62931259, 6.50015411, 6.26190774, - 6.36337538, 6.19264508, 5.95151313, 6.08860801, 5.91253716, 5.83007500, 5.68145719, 5.67244736, 5.82632286, 6.00282052, - 5.65348627, 5.74970158, 5.87846648, 5.69052365, 5.64464890, 5.58531476, 5.81512466, 5.57688409, 5.87329553, 5.62170514, - 5.74851759, 5.81017544, 5.64464890, 5.76398581, 5.60339522, 5.69507833, 5.84139031, 5.68711704, 5.73908047, 5.84139031, - 5.71459778, 5.96245305, 5.82632286, 5.89540125, 6.08860801, 6.12041675, 6.13272126, 6.30477171, 6.24177679, 6.28232358, - 6.29091619, 6.53239445, 6.81512466, 6.72620440, 6.65792533, 6.84518189, 7.10215454, 7.44157929, 7.57793523, 7.59485854, - 7.66460965, 8.05163277, 8.00564656, 8.43775758, 8.10518224, 8.28575448, 8.77118131, 8.23181568, 8.29264087, 8.20558413, - 8.34894831, 8.89147554, 8.40754296, 8.61629571, 8.64244800, 8.61629571, 8.93391081, 8.50814690, 9.02272008, 8.68711704, - 8.65127185, 9.41503750, 9.11735695, 9.85025288, 10.24511250, 10.60768258, 10.47643804, 11.60768258, 11.35614381, 12.29956028, - 15.00000000, 15.00000000, 12.29956028, 13.00000000, 15.00000000, 15.00000000, 15.00000000, 15.00000000, 14.00000000, 12.83007500, - 15.00000000, 15.00000000}; - -const double WebRtcIsac_kQKltCodeLenShape[578] = { - 0.00002201, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 14.00000000, - 0.00011007, 13.00000000, 0.00066056, 11.60768258, 11.14201900, 0.00185034, 10.24511250, 9.08113676, 0.00480700, 9.41503750, - 8.09913319, 0.01084946, 8.02845645, 13.00000000, 6.40941295, 0.02926496, 6.95560588, 13.00000000, 13.00000000, 6.21864029, - 0.03861814, 6.30477171, 12.29956028, 11.14201900, 4.66964328, 0.12158980, 4.62604734, 10.91253716, 14.00000000, 11.35614381, - 8.76159526, 3.89671219, 0.21179147, 3.99472634, 8.02272008, 13.00000000, 0.00002201, 0.00002201, 0.00002201, 0.00002201, - 14.00000000, 0.00011007, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 14.00000000, 0.00011007, 10.95560588, 0.00147568, - 10.95560588, 10.24511250, 0.00240150, 10.24511250, 8.02845645, 0.01056115, 8.17982104, 6.74497143, 0.02381629, 7.15137706, - 5.68598330, 0.05650076, 5.72970467, 13.00000000, 14.00000000, 5.18221688, 0.07697435, 5.37611851, 11.91253716, 9.54056838, - 3.92853764, 0.22143514, 3.76428491, 9.28575448, 14.00000000, 11.35614381, 7.37794818, 3.30585980, 0.37001735, 3.18521728, - 6.88886433, 11.91253716, 14.00000000, 0.00002201, 0.00002201, 0.00002201, 0.00002201, 14.00000000, 0.00019814, 14.00000000, - 0.00002201, 13.00000000, 0.00028621, 14.00000000, 14.00000000, 0.00028621, 13.00000000, 11.91253716, 0.00094690, 11.35614381, - 10.60768258, 0.00213692, 10.24511250, 8.37794818, 0.00863317, 8.43775758, 14.00000000, 7.41128536, 0.01698415, 7.42635281, - 6.02702021, 0.04514485, 6.01558154, 13.00000000, 5.05090284, 0.09207659, 4.98877274, 15.00000000, 10.75207249, 4.41081703, - 0.15733047, 4.17424617, 11.60768258, 14.00000000, 15.00000000, 10.06926266, 3.74320161, 0.23091117, 3.81141115, 10.02272008, - 11.91253716, 6.90196792, 2.92703003, 0.46874039, 2.93846004, 7.26190774, 11.60768258, 13.00000000, 10.21864029, 7.95560588, - 5.19345038, 2.40520888, 0.77554605, 2.56628417, 5.41409855, 8.81017544, 12.29956028, 0.00002201, 0.00002201, 0.00002201, - 0.00002201, 11.91253716, 0.00068259, 12.29956028, 10.11735695, 0.00233535, 10.47643804, 10.35614381, 0.00140957, 12.29956028, - 10.24511250, 0.00222511, 10.47643804, 7.72387559, 0.01475842, 7.52426657, 6.73555740, 0.02924249, 6.55085135, 14.00000000, - 5.45021533, 0.06930886, 5.38345116, 4.55913083, 0.11289841, 4.92853764, 11.60768258, 4.07148162, 0.19798859, 3.87200568, - 13.00000000, 9.60768258, 3.31393725, 0.29937064, 3.57321111, 9.35614381, 14.00000000, 8.02845645, 3.08542800, 0.45503557, - 2.80678268, 7.47643804, 12.29956028, 14.00000000, 14.00000000, 14.00000000, 6.83509307, 2.69166097, 0.61673447, 2.51266238, - 6.84771516, 11.91253716, 11.14201900, 7.72387559, 4.61899789, 2.17136763, 1.03592993, 2.28586183, 4.86814304, 7.78568088, - 10.91253716, 14.00000000, 15.00000000, 11.04580369, 9.28575448, 7.26190774, 5.21946023, 3.61575588, 2.18431651, 1.45666604, - 2.33566383, 3.85470467, 5.46181107, 7.30651304, 8.89147554, 11.91253716, 0.00002201, 0.00002201, 15.00000000, 0.00011007, - 15.00000000, 10.47643804, 0.00149771, 11.60768258, 7.30651304, 0.01903486, 7.20558413, 6.92318440, 0.02587674, 6.71459778, - 7.28919357, 0.01756340, 7.45696818, 6.06926266, 0.03841465, 6.45890338, 4.46035649, 0.13896157, 4.43204392, 14.00000000, - 14.00000000, 4.09010692, 0.19672654, 3.86653665, 13.00000000, 14.00000000, 11.35614381, 3.45117809, 0.27340929, 3.64878468, - 9.93391081, 9.60768258, 3.30585980, 0.35178287, 3.15607895, 9.12963528, 12.29956028, 7.93391081, 2.82180202, 0.53064436, - 2.68258739, 7.42635281, 11.14201900, 15.00000000, 10.06926266, 6.15294265, 2.55861197, 0.69308389, 2.48602573, 5.90592231, - 9.47643804, 13.00000000, 11.14201900, 8.20558413, 5.21618324, 2.34973357, 1.00379135, 2.09611815, 4.80278331, 7.82507432, - 11.91253716, 12.29956028, 8.98877274, 6.75444729, 4.23886435, 2.19859476, 1.29528579, 2.06032897, 4.06704711, 6.76398581, - 9.34178852, 13.00000000, 12.29956028, 10.14201900, 8.40754296, 6.16710999, 4.64850859, 3.28768796, 2.22892326, 1.78568088, - 2.35209193, 3.45141888, 4.91121176, 6.45117809, 8.20558413, 10.35614381, 13.00000000, 12.83007500, 14.00000000, 14.00000000, - 12.83007500, 11.35614381, 9.85025288, 9.41503750, 8.07518750, 7.34894831, 6.25516616, 5.25096862, 4.52477322, 3.68513357, - 3.06152306, 2.64011320, 2.54608637, 2.95765662, 3.44445223, 4.01665007, 4.63258525, 5.27633906, 6.24678325, 7.05163277, - 8.02845645, 9.60768258, 10.79054663, 12.83007500, 14.00000000, 0.00002201, 15.00000000, 0.00011007, 15.00000000, 5.82009091, - 0.02710994, 10.11735695, 15.00000000, 3.30346709, 0.17317845, 6.41128536, 15.00000000, 15.00000000, 10.83007500, 2.72897475, - 0.35935964, 3.85853144, 13.00000000, 15.00000000, 3.72766182, 0.17268211, 4.74674342, 15.00000000, 15.00000000, 4.40754296, - 0.11993823, 4.93997965, 15.00000000, 15.00000000, 4.70193743, 0.22110152, 3.27591254, 11.35614381, 15.00000000, 12.54056838, - 6.94743195, 2.99157138, 0.62079088, 2.25338071, 7.41128536, 15.00000000, 15.00000000, 11.47643804, 6.04580369, 2.80232255, - 0.98380109, 1.70502034, 5.15607895, 10.75207249, 13.00000000, 15.00000000, 15.00000000, 11.35614381, 6.44157929, 2.37955105, - 0.74567258, 2.44181848, 6.07667251, 10.75207249, 15.00000000, 15.00000000, 15.00000000, 15.00000000, 9.44541115, 5.25432568, - 1.97815248, 0.94086682, 2.57885561, 5.17265730, 8.72387559, 14.00000000, 15.00000000, 15.00000000, 15.00000000, 10.67807191, - 7.59485854, 4.91187431, 2.91934900, 1.32321648, 1.50614455, 3.85215911, 7.15137706, 10.60768258, 14.00000000, 15.00000000, - 15.00000000, 15.00000000, 15.00000000, 15.00000000, 15.00000000, 15.00000000, 11.09310940, 8.50814690, 6.45117809, 3.94675287, - 2.16730774, 1.37674720, 2.10215454, 3.97278511, 6.34178852, 8.50814690, 10.32757466, 14.00000000, 14.00000000, 15.00000000, - 15.00000000, 15.00000000, 14.00000000, 10.02272008, 9.41503750, 7.03710399, 5.13349379, 3.61683574, 2.47999526, 1.82788507, - 2.00945280, 3.08020557, 4.69793233, 6.64025044, 8.32051990, 10.91253716, 14.00000000, 15.00000000, 15.00000000, 15.00000000, - 15.00000000, 15.00000000, 15.00000000, 14.41503750, 9.41503750, 7.10215454, 5.60768258, 4.44014496, 3.30064444, 2.56941678, - 2.28103909, 2.42694459, 2.93206152, 3.83383692, 4.75207249, 6.02702021, 7.16394964, 9.35614381, 11.14201900, 12.29956028, - 15.00000000, 15.00000000, 14.00000000, 15.00000000, 15.00000000, 10.11735695, 8.85025288, 7.02558541, 5.86300889, 4.79726396, - 3.95117259, 3.44925321, 2.96348293, 2.88219459, 2.89671219, 3.10518224, 3.50065237, 4.05748549, 4.55291677, 5.23016216, - 5.79420675, 6.39452048, 6.91253716, 7.97763219, 8.32051990, 9.35614381, 10.60768258, 11.60768258, 15.00000000, 15.00000000, - 12.83007500, 12.41503750, 11.91253716, 14.00000000, 14.00000000, 11.35614381, 10.75207249, 10.91253716, 8.93391081, 8.61629571, - 8.29264087, 8.02272008, 7.89147554, 7.33466408, 7.41128536, 6.71459778, 5.92853764, 5.31964048, 4.97693875, 4.70308379, - 4.43632704, 4.34357514, 4.30825648, 4.26529038, 4.19465917, 4.27420773, 4.22315577, 4.08262792, 4.06667818, 4.16119001, - 4.08860801, 4.26698468, 4.39128315, 4.71517590, 5.10442472, 5.50714538, 5.81017544, 6.15922208}; - -/* left KLT transforms */ -const double WebRtcIsac_kKltT1Gain[3][4] = { -{-0.79742827, 0.60341375, 0.60341375, 0.79742827}, -{-0.81372390, 0.58125159, 0.58125159, 0.81372390}, -{-0.71832547, 0.69570721, 0.69570721, 0.71832547}}; - -const double WebRtcIsac_kKltT1Shape[3][324] = { -{ 0.00159597, 0.00049320, 0.00513821, 0.00021066, 0.01338581, -0.00422367, -0.00272072, 0.00935107, 0.02047622, 0.02691189, - 0.00478236, 0.03969702, 0.00886698, 0.04877604, -0.10898362, -0.05930891, -0.03415047, 0.98889721, 0.00293558, -0.00035282, - 0.01156321, -0.00195341, -0.00937631, 0.01052213, -0.02551163, 0.01644059, 0.03189927, 0.07754773, -0.08742313, -0.03026338, - 0.05136248, -0.14395974, 0.17725040, 0.22664856, 0.93380230, 0.07076411, 0.00557890, -0.00222834, 0.01377569, 0.01466808, - 0.02847361, -0.00603178, 0.02382480, -0.01210452, 0.03797267, -0.02371480, 0.11260335, -0.07366682, 0.00453436, -0.04136941, --0.07912843, -0.95031418, 0.25295337, -0.05302216, -0.00617554, -0.00044040, -0.00653778, 0.01097838, 0.01529174, 0.01374431, --0.00748512, -0.00020034, 0.02432713, 0.11101570, -0.08556891, 0.09282249, -0.01029446, 0.67556443, -0.67454300, 0.06910063, - 0.20866865, -0.10318050, 0.00932175, 0.00524058, 0.00803610, -0.00594676, -0.01082578, 0.01069906, 0.00546768, 0.01565291, - 0.06816200, 0.10201227, 0.16812734, 0.22984074, 0.58213170, -0.54138651, -0.51379962, 0.06847390, -0.01920037, -0.04592324, --0.00467394, 0.00328858, 0.00377424, -0.00987448, 0.08222096, -0.00377301, 0.04551941, -0.02592517, 0.16317082, 0.13077530, - 0.22702921, -0.31215289, -0.69645962, -0.38047101, -0.39339411, 0.11124777, 0.02508035, -0.00708074, 0.00400344, 0.00040331, - 0.01142402, 0.01725406, 0.01635170, 0.14285366, 0.03949233, -0.05905676, 0.05877154, -0.17497577, -0.32479440, 0.80754464, --0.38085603, -0.17055430, -0.03168622, -0.07531451, 0.02942002, -0.02148095, -0.00754114, -0.00322372, 0.00567812, -0.01701521, --0.12358320, 0.11473564, 0.09070136, 0.06533068, -0.22560802, 0.19209022, 0.81605094, 0.36592275, -0.09919829, 0.16667122, - 0.16300725, 0.04803807, 0.06739263, -0.00156752, -0.01685302, -0.00905240, -0.02297836, -0.00469939, 0.06310613, -0.16391930, - 0.10919511, 0.12529293, 0.85581322, -0.32145522, 0.24539076, 0.07181839, 0.07289591, 0.14066759, 0.10406711, 0.05815518, - 0.01072680, -0.00759339, 0.00053486, -0.00044865, 0.03407361, 0.01645348, 0.08758579, 0.27722240, 0.53665485, -0.74853376, --0.01118192, -0.19805430, 0.06130619, -0.09675299, 0.08978480, 0.03405255, -0.00706867, 0.05102045, 0.03250746, 0.01849966, --0.01216314, -0.01184187, -0.01579288, 0.00114807, 0.11376166, 0.88342114, -0.36425379, 0.13863190, 0.12524180, -0.13553892, - 0.04715856, -0.12341103, 0.04531568, 0.01899360, -0.00206897, 0.00567768, -0.01444163, 0.00411946, -0.00855896, 0.00381663, --0.01664861, -0.05534280, 0.21328278, 0.20161162, 0.72360394, 0.59130708, -0.08043791, 0.08757349, -0.13893918, -0.05147377, - 0.02680690, -0.01144070, 0.00625162, -0.00634215, -0.01248947, -0.00329455, -0.00609625, -0.00136305, -0.05097048, -0.01029851, - 0.25065384, -0.16856837, -0.07123372, 0.15992623, -0.39487617, -0.79972301, 0.18118185, -0.04826639, -0.01805578, -0.02927253, --0.16400618, 0.07472763, 0.10376449, 0.01705406, 0.01065801, -0.01500498, 0.02039914, 0.37776349, -0.84484186, 0.10434286, - 0.15616990, 0.13474456, -0.00906238, -0.25238368, -0.03820885, -0.10650905, -0.03880833, -0.03660028, -0.09640894, 0.00583314, - 0.01922097, 0.01489911, -0.02431117, -0.09372217, 0.39404721, -0.84786223, -0.31277121, 0.03193850, 0.01974060, 0.01887901, - 0.00337911, -0.11359599, -0.02792521, -0.03220184, -0.01533311, 0.00015962, -0.04225043, -0.00933965, 0.00675311, 0.00206060, - 0.15926771, 0.40199829, -0.80792558, -0.35591604, -0.17169764, 0.02830436, 0.02459982, -0.03438589, 0.00718705, -0.01798329, --0.01594508, -0.00702430, -0.00952419, -0.00962701, -0.01307212, -0.01749740, 0.01299602, 0.00587270, -0.36103108, -0.82039266, --0.43092844, -0.08500097, -0.04361674, -0.00333482, 0.01250434, -0.02538295, -0.00921797, 0.01645071, -0.01400872, 0.00317607, - 0.00003277, -0.01617646, -0.00616863, -0.00882661, 0.00466157, 0.00353237, 0.91803104, -0.39503305, -0.02048964, 0.00060125, - 0.01980634, 0.00300109, 0.00313880, 0.00657337, 0.00715163, 0.00000261, 0.00854276, -0.00154825, -0.00516128, 0.00909527, - 0.00095609, 0.00701196, -0.00221867, -0.00156741}, -{-0.00469582, -0.00020403, -0.00587134, 0.00185153, -0.02256479, -0.01185761, -0.02891481, -0.00493792, -0.00182344, 0.00285962, - 0.01558059, -0.02185140, 0.04639438, -0.04357142, 0.12718613, -0.06756136, 0.05542227, 0.98480184, -0.00374376, -0.00236433, --0.00607169, -0.00303290, -0.00127243, -0.01794845, 0.00620033, -0.00732704, -0.02837749, -0.00107164, 0.04820548, 0.00713300, - 0.09784244, -0.16806261, -0.04563341, -0.33406041, 0.91554083, -0.08139655, -0.00415851, -0.00538193, -0.00731198, -0.00534534, --0.00623075, -0.02016943, -0.05480133, -0.03172290, -0.03879603, 0.01518441, 0.09591688, 0.02238470, 0.08126640, 0.08236821, --0.24802119, 0.89516402, 0.32029647, 0.07188887, -0.00220366, 0.00344025, -0.00277284, 0.00358963, -0.08668007, -0.02205910, --0.05289669, -0.03535201, -0.01188017, -0.06456872, -0.09321006, -0.00009617, -0.15804070, 0.24632041, 0.90166119, 0.19250690, - 0.17264619, -0.09699155, -0.00567329, -0.00897700, -0.01442565, -0.01939390, 0.03702127, -0.02999862, -0.04385696, -0.05232394, --0.03339177, 0.03905964, -0.00281424, -0.29213275, 0.02892968, 0.90257613, -0.21546058, -0.18070946, 0.09014567, 0.04117230, --0.01029696, -0.00329116, -0.03354346, 0.02937079, 0.01274208, -0.01260649, -0.03505571, -0.01020645, 0.03787209, 0.12132165, --0.20826840, 0.81556933, -0.43874351, 0.21518682, -0.14564290, -0.05210031, 0.07124563, 0.06127983, -0.00457321, 0.01740496, - 0.04185176, 0.00128036, -0.05033693, -0.01890046, 0.06221734, 0.10280078, -0.03738531, 0.04830209, -0.08408293, -0.46409009, --0.83936263, -0.14817619, -0.13135927, 0.04563506, 0.08340661, 0.04040200, 0.00044396, -0.01365972, 0.01228951, 0.01078273, - 0.09205406, -0.03791500, 0.07135889, 0.08158339, 0.06298278, -0.22875755, -0.92917558, -0.11248260, 0.17801883, -0.03971674, --0.07491915, 0.06477287, 0.04635713, 0.01856159, 0.00130895, -0.01991604, 0.02358176, -0.09376056, 0.02782280, -0.04691559, - 0.13749249, 0.31383132, 0.92274602, 0.04727419, 0.09765196, -0.02108945, 0.00626005, 0.05193322, 0.02009133, 0.03094066, - 0.04573470, 0.00451733, 0.00240169, -0.00982355, -0.03546208, -0.14156875, -0.02480689, 0.22997442, 0.09778317, 0.88834235, --0.32797611, -0.00079977, 0.04917079, 0.06977359, 0.06451185, 0.07816204, 0.03119314, 0.01136506, 0.01062006, 0.00632783, - 0.03241828, -0.03318847, -0.01350502, -0.30055361, 0.07265375, 0.17308022, 0.88795796, -0.23231020, -0.08932700, 0.11759604, - 0.00590705, 0.03525351, 0.00840466, 0.04389942, 0.04387629, 0.04003275, 0.01772966, 0.02709780, -0.02393282, 0.02766178, - 0.00342983, -0.33882220, 0.76612668, 0.44061716, -0.28414784, -0.09364014, 0.03694060, 0.01124120, 0.01130268, -0.02869682, --0.07428963, -0.03504754, 0.05874942, 0.01196795, 0.02003875, 0.00787152, -0.01605561, 0.04501257, -0.06959958, -0.13015784, --0.05738065, 0.04681625, 0.06668700, -0.04492094, 0.02927765, -0.94404277, 0.19243952, 0.09504337, -0.12540826, 0.05394317, --0.07972638, -0.02145188, 0.00136427, 0.01964678, 0.06667373, 0.06204535, 0.17302394, 0.22005905, 0.58329964, -0.68440447, - 0.19628796, 0.15718011, -0.12481840, -0.08222507, 0.11780870, 0.03798206, -0.01818866, 0.00892766, 0.05582263, 0.01126832, --0.00973589, 0.00697442, -0.09937902, 0.06621185, -0.19452202, -0.80004569, -0.13946094, -0.48990700, -0.17595191, -0.00798873, --0.06121856, 0.08768040, -0.04507631, 0.00448896, 0.01153941, -0.04711652, -0.01050749, -0.01660047, -0.03007159, -0.01468906, - 0.12848053, 0.13859838, 0.93863771, -0.22250065, -0.14841278, 0.04666032, -0.06344813, -0.01915105, -0.01840150, -0.02389410, --0.01245496, 0.05023402, 0.02125840, 0.02467318, -0.01893022, -0.00889647, 0.00551817, 0.00481915, -0.40626968, -0.89028236, - 0.18261687, -0.03852330, 0.02621926, -0.05420122, -0.01704117, -0.00072893, -0.02694170, -0.04335124, 0.02256467, 0.00642301, --0.01619484, -0.00871160, 0.00400065, -0.00488820, -0.00752173, -0.00170603, 0.89554989, -0.41825934, -0.08725803, -0.09051404, --0.00916236, -0.02959065, -0.07268075, -0.00816626, -0.00314215, -0.01941078, -0.00036782, -0.00188655, -0.02107724, -0.00771657, --0.00448194, -0.00387517, 0.00082998, 0.00202471}, -{ 0.00167296, -0.00647772, -0.00604394, 0.01490810, -0.00837664, 0.00246438, 0.02082153, 0.01216715, 0.01001396, -0.02850860, --0.01187868, -0.00113289, 0.04140237, -0.11084998, 0.16102260, 0.20084170, -0.28969446, -0.91312256, 0.00087788, -0.00136895, - 0.00004622, 0.00578894, 0.00524119, -0.00044550, 0.00948906, -0.00396910, -0.03312197, -0.00075487, 0.00987494, -0.02088734, - 0.09835550, -0.20080342, 0.13687782, -0.16111863, -0.90089988, 0.30312999, 0.00248784, -0.00975419, -0.01617200, 0.00699371, --0.02151635, -0.01625774, -0.01262800, 0.02588781, -0.05620764, -0.13651454, 0.04242442, -0.02615307, 0.20497288, -0.20422909, - 0.14184406, 0.89712919, 0.01758042, 0.25447787, -0.00207668, -0.00260329, 0.00724812, -0.01007749, 0.00806242, -0.03089729, --0.01161934, -0.00618676, -0.10327342, -0.10160272, 0.11919283, 0.20781533, 0.11564869, -0.19072476, 0.86402008, -0.24650846, - 0.24684161, 0.04775750, 0.00486888, -0.01735569, -0.01868000, -0.01870386, -0.03243262, -0.05883701, -0.03433371, 0.10441236, --0.22831067, -0.22837988, 0.15082544, -0.21313767, 0.13215611, -0.78096079, -0.32270595, -0.21307018, 0.17339271, -0.05435742, --0.00940813, 0.00272520, 0.00542917, -0.05232991, -0.01280809, -0.10773627, -0.17626479, 0.03719285, -0.26297104, -0.21780618, - 0.21406665, 0.15202177, 0.75911044, 0.38627481, -0.16504189, -0.10242997, -0.02394939, -0.06018959, 0.00994733, -0.02617197, --0.01543723, -0.10320051, -0.03010481, -0.19098072, -0.06893233, 0.12253174, -0.25556092, -0.31989059, 0.09542655, 0.72712041, --0.43108921, -0.01568072, -0.16532685, 0.06646835, -0.08885408, -0.00050364, -0.01791050, 0.00245405, 0.00204794, -0.17948691, --0.05193881, -0.16329387, -0.13676259, 0.01214133, -0.30994612, -0.00687734, 0.63254090, -0.47180795, -0.35409214, 0.23658315, - 0.11170294, 0.05229887, -0.06107035, -0.01094212, 0.01523854, -0.01608284, -0.03739206, -0.23864328, -0.03958494, -0.19305719, --0.26019058, 0.24108257, -0.55933566, 0.40623396, -0.53367968, -0.08930957, -0.00599383, -0.00050845, 0.06960811, 0.02664961, - 0.01464197, -0.00486781, -0.01905736, 0.01437578, 0.02379930, -0.26639588, 0.05208876, -0.43525002, -0.63009424, 0.05251889, - 0.56732782, -0.06731164, -0.03705909, -0.03253946, 0.00950673, -0.07941760, 0.02388267, -0.01258409, -0.00343524, 0.00148711, --0.00362107, 0.03981813, -0.07235214, -0.46180041, -0.05595288, -0.55699317, 0.61935853, -0.25379716, 0.06796783, 0.01039267, --0.06329171, -0.02143024, 0.09406929, -0.00799203, -0.01419805, -0.00603024, 0.01313145, 0.00091161, -0.00212107, -0.02405340, - 0.07146405, -0.76695326, -0.14841817, 0.60372663, -0.01478424, 0.06522462, 0.08580016, -0.05817981, 0.02438942, 0.04840904, - 0.02934363, -0.02239678, -0.00582247, -0.00091312, -0.00394148, -0.00285276, -0.03435745, 0.05277435, 0.17882781, -0.06194164, - 0.27321118, 0.01840179, -0.10188148, -0.33168524, -0.03491221, 0.67351789, 0.37017376, 0.32083717, 0.09737800, -0.20998084, --0.10725041, 0.06379186, 0.02169903, -0.02031584, 0.05623799, -0.18300962, -0.17337803, 0.08915172, -0.53835537, -0.08547263, - 0.15163321, 0.56732906, 0.21878115, 0.37274266, 0.26206918, 0.13443927, 0.09178695, -0.03276324, -0.01131664, -0.00236369, - 0.00772568, 0.01008805, -0.17122615, 0.15301569, 0.40135484, -0.06058913, 0.56405128, -0.05176853, 0.24544337, 0.62448073, - 0.07265009, -0.01198695, 0.05151774, -0.03678498, 0.01886154, 0.03724094, 0.01393667, 0.00758055, -0.00254297, 0.00537118, - 0.24169707, -0.41735970, -0.67564355, -0.09270478, 0.53106033, 0.06214579, 0.02574404, 0.09943837, 0.03032542, 0.02194476, - 0.06369772, -0.00133741, 0.01301113, 0.01508494, 0.00036111, -0.00278870, 0.00139205, 0.00015792, -0.43347887, 0.69923146, --0.55406563, -0.01102231, 0.01347767, 0.07012139, -0.02530164, 0.06803192, 0.01177196, 0.04374491, 0.04073027, 0.04037438, - 0.00167330, -0.01807065, -0.00425562, 0.00149653, -0.00035119, -0.00172888, 0.84785495, 0.52289580, 0.01067734, -0.00859194, - 0.01685964, 0.00481442, 0.00434738, 0.07592695, 0.01419942, 0.01005336, 0.03316937, 0.00360465, 0.00435039, 0.00029122, - 0.00171268, 0.00198919, -0.00046889, -0.00094176}}; - -/* right KLT transforms */ -const double WebRtcIsac_kKltT2Gain[3][36] = { -{ 0.14572837, -0.45446306, 0.61990621, -0.52197033, 0.32145074, -0.11026900, -0.20698282, 0.48962182, -0.27127933, -0.33627476, - 0.65094037, -0.32715751, 0.40262573, -0.47844405, -0.33876075, 0.44130653, 0.37383966, -0.39964662, -0.51730480, 0.06611973, - 0.49030187, 0.47512886, -0.02141226, -0.51129451, -0.58578569, -0.39132064, -0.13187771, 0.15649421, 0.40735596, 0.54396897, - 0.40381276, 0.40904942, 0.41179766, 0.41167576, 0.40840251, 0.40468132}, -{-0.11368135, 0.34815515, -0.56434996, 0.61130763, -0.39970336, 0.11795708, 0.28514257, -0.58879243, 0.32775812, 0.27024886, --0.56251299, 0.27411037, 0.42649186, -0.44080232, -0.36408215, 0.35932457, 0.43592895, -0.41484213, -0.49813030, -0.00012592, - 0.49865688, 0.47634953, 0.01094246, -0.52552726, -0.56154082, -0.41110686, -0.14170764, 0.15946614, 0.40818082, 0.55094554, - 0.40051601, 0.41084781, 0.41567800, 0.41450700, 0.40871872, 0.39891823}, -{-0.10719481, 0.34796287, -0.54573957, 0.59521001, -0.43943367, 0.14907223, 0.26554957, -0.59549939, 0.36760692, 0.26040652, --0.55268701, 0.25778784, 0.38994096, -0.45282773, -0.37975656, 0.40213055, 0.43052647, -0.38937904, -0.52698359, 0.02788094, - 0.48284286, 0.47792474, 0.02557759, -0.50922240, -0.57699826, -0.39476779, -0.14708238, 0.12742149, 0.37835245, 0.57464021, - 0.39408127, 0.40327462, 0.40993655, 0.41419345, 0.41506301, 0.41253853}}; - -const double WebRtcIsac_kKltT2Shape[3][36] = { -{ 0.13427386, -0.35132558, 0.52506528, -0.59419077, 0.45075085, -0.16312057, 0.29857439, -0.58660147, 0.34265431, 0.20879510, --0.56063262, 0.30238345, 0.43308283, -0.41186999, -0.35288681, 0.42768996, 0.36094634, -0.45284910, -0.47116680, 0.02893449, - 0.54326135, 0.45249040, -0.06264420, -0.52283830, 0.57137758, 0.44298139, 0.12617554, -0.20819946, -0.42324603, -0.48876443, - 0.39597050, 0.40713935, 0.41389880, 0.41512486, 0.41130400, 0.40575001}, -{ 0.16540737, -0.43379435, 0.58165221, -0.55154773, 0.35734028, -0.11935912, 0.29434254, -0.55954817, 0.23549804, 0.33087258, --0.58848503, 0.29835834, 0.45464789, -0.38316155, -0.41689708, 0.35607296, 0.41260747, -0.41910198, -0.48633899, -0.04144955, - 0.47824583, 0.51050942, 0.01000345, -0.52184032, 0.53488229, 0.42641051, 0.17049774, -0.15849613, -0.43229355, -0.53945045, - 0.39582002, 0.41033103, 0.41788713, 0.41688080, 0.41081697, 0.39719658}, -{ 0.13386268, -0.37919915, 0.54989123, -0.57663572, 0.42402636, -0.15362720, 0.29641577, -0.58806770, 0.31381040, 0.26524954, --0.56271012, 0.28431868, 0.42699898, -0.41058922, -0.40408270, 0.39215865, 0.40788513, -0.40699735, -0.49846482, -0.01521208, - 0.48756040, 0.49479418, -0.00347672, -0.51841384, 0.55513106, 0.41683793, 0.15131217, -0.15613621, -0.41029341, -0.54996461, - 0.39402116, 0.40965305, 0.41862791, 0.41730770, 0.41089648, 0.39837262}}; - -/* means of log gains and LAR coefficients*/ -const double WebRtcIsac_kLpcMeansGain[3][12] = { -{-6.86881911, -5.35075273, -6.86792680, -5.36200897, -6.86401538, -5.36921533, -6.86802969, -5.36893966, -6.86538097, -5.36315063, --6.85535304, -5.35155315}, -{-6.12914600, -4.78070092, -6.12971780, -4.78382183, -6.12858525, -4.79362198, -6.12926491, -4.79017481, -6.12102401, -4.78346122, --6.11441152, -4.78019228}, -{-5.67273484, -3.73876311, -5.65246094, -3.71407895, -5.61716443, -3.68814580, -5.58804560, -3.66334094, -5.54189577, -3.63845640, --5.49293185, -3.61760203}}; - -const double WebRtcIsac_kLpcMeansShape[3][108] = { -{-0.91232981, 0.26258634, -0.33716701, 0.08477430, -0.03378426, 0.14423909, 0.07036185, 0.06155019, 0.01490385, 0.04138740, - 0.01427317, 0.01288970, 0.83872106, 0.25750199, 0.07988929, -0.01957923, 0.00831390, 0.01770300, -0.90957164, 0.25732216, --0.33385344, 0.08735740, -0.03715332, 0.14584917, 0.06998990, 0.06131968, 0.01504379, 0.04067339, 0.01428039, 0.01406460, - 0.83846243, 0.26169862, 0.08109025, -0.01767055, 0.00970539, 0.01954310, -0.90490803, 0.24656405, -0.33578607, 0.08843286, --0.03749139, 0.14443959, 0.07214669, 0.06170993, 0.01449947, 0.04134309, 0.01314762, 0.01413471, 0.83895203, 0.26748062, - 0.08197507, -0.01781298, 0.00885967, 0.01922394, -0.90922472, 0.24495889, -0.33921540, 0.08877169, -0.03581332, 0.14199172, - 0.07444032, 0.06185940, 0.01502054, 0.04185113, 0.01276579, 0.01355457, 0.83645358, 0.26631720, 0.08119697, -0.01835449, - 0.00788512, 0.01846446, -0.90482253, 0.24658310, -0.34019734, 0.08281090, -0.03486038, 0.14359248, 0.07401336, 0.06001471, - 0.01528421, 0.04254560, 0.01321472, 0.01240799, 0.83857127, 0.26281654, 0.08174380, -0.02099842, 0.00755176, 0.01699448, --0.90132307, 0.25174308, -0.33838268, 0.07883863, -0.02877906, 0.14105407, 0.07220290, 0.06000352, 0.01684879, 0.04226844, - 0.01331331, 0.01269244, 0.83832138, 0.25467485, 0.08118028, -0.02120528, 0.00747832, 0.01567212}, -{-1.11639718, 0.35377266, 0.00798929, 0.20165280, 0.07656104, 0.10629964, 0.04894160, 0.10955305, -0.01806405, 0.05082282, - 0.01730794, 0.01345957, 0.73717782, 0.05952284, 0.03176204, 0.08195122, 0.01253148, 0.02253385, -1.12053537, 0.35523538, - 0.00859646, 0.20007706, 0.07715852, 0.10754596, 0.05165976, 0.10927703, -0.01554395, 0.05178866, 0.01752534, 0.01343468, - 0.73489046, 0.06395167, 0.03287798, 0.07972374, 0.01293550, 0.02300929, -1.11772179, 0.35457623, 0.01205524, 0.19926481, - 0.08000866, 0.10817921, 0.05052481, 0.11016167, -0.01552091, 0.05155510, 0.01787163, 0.01343778, 0.73142568, 0.06840830, - 0.03316828, 0.07902608, 0.01525042, 0.02178127, -1.12120164, 0.36405233, 0.00630305, 0.19799738, 0.07829690, 0.10727588, - 0.04017317, 0.10437949, -0.01844109, 0.05021700, 0.01561726, 0.01226571, 0.73438044, 0.06947982, 0.03396317, 0.07858683, - 0.01367105, 0.02041955, -1.12146187, 0.35952226, 0.00340090, 0.19700813, 0.07938222, 0.10904137, 0.03921216, 0.10531403, --0.01833415, 0.04956231, 0.01399539, 0.01323582, 0.74378099, 0.07059589, 0.03367692, 0.08151462, 0.01182040, 0.02075577, --1.11245254, 0.35234230, 0.00687490, 0.20204252, 0.07813186, 0.11081259, 0.04634665, 0.11073238, -0.01637954, 0.05104577, - 0.01675122, 0.01448696, 0.74013627, 0.06239059, 0.03129412, 0.08207461, 0.01249475, 0.02189238}, -{-1.27118948, 0.35834331, -0.33499347, 0.13524073, 0.04829079, 0.19542773, 0.05273835, 0.04157974, -0.01755227, 0.01513442, - 0.00386630, 0.02199463, 1.14439142, 0.21903073, 0.14750213, 0.12743356, 0.08463334, 0.06839691, -1.28367777, 0.35556287, --0.33809405, 0.13627881, 0.04939309, 0.19642571, 0.05354373, 0.04099247, -0.01787481, 0.01472425, 0.00391474, 0.02150716, - 1.14739079, 0.21840872, 0.14643624, 0.12724347, 0.08390642, 0.06811938, -1.29007667, 0.35159558, -0.34154267, 0.13295849, - 0.04883602, 0.19587595, 0.05452759, 0.04174703, -0.01782110, 0.01388270, 0.00374754, 0.02138105, 1.14333767, 0.21690116, - 0.14544599, 0.12606728, 0.08314168, 0.06771389, -1.29856471, 0.35239315, -0.34238732, 0.13277553, 0.04722712, 0.19233156, - 0.05366901, 0.04328110, -0.01657749, 0.01444736, 0.00438108, 0.02102563, 1.13548397, 0.21537812, 0.14357377, 0.12525845, - 0.08230994, 0.06722511, -1.30663540, 0.34366563, -0.34205544, 0.12861679, 0.04655851, 0.18864359, 0.05351285, 0.04358693, --0.01604498, 0.01431907, 0.00395326, 0.02082299, 1.12207794, 0.21167325, 0.14212491, 0.12418671, 0.08155467, 0.06639789, --1.31011673, 0.33686271, -0.34379843, 0.12169569, 0.04480323, 0.18637557, 0.05374078, 0.04260827, -0.01588226, 0.01378294, - 0.00396009, 0.02112406, 1.10466984, 0.20905894, 0.14107033, 0.12303074, 0.08047136, 0.06588031}}; - diff --git a/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.h b/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.h deleted file mode 100644 index 604d963c3..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/lpc_tables.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * lpc_tables.h - * - * header file for coding tables for the LPC coefficients - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ - -#include "structs.h" - -#include "settings.h" - -#define KLT_STEPSIZE 1.00000000 -#define KLT_NUM_AVG_GAIN 0 -#define KLT_NUM_AVG_SHAPE 0 -#define KLT_NUM_MODELS 3 -#define LPC_GAIN_SCALE 4.000f -#define LPC_LOBAND_SCALE 2.100f -#define LPC_LOBAND_ORDER ORDERLO -#define LPC_HIBAND_SCALE 0.450f -#define LPC_HIBAND_ORDER ORDERHI -#define LPC_GAIN_ORDER 2 - -#define LPC_SHAPE_ORDER (LPC_LOBAND_ORDER + LPC_HIBAND_ORDER) - -#define KLT_ORDER_GAIN (LPC_GAIN_ORDER * SUBFRAMES) -#define KLT_ORDER_SHAPE (LPC_SHAPE_ORDER * SUBFRAMES) -/* indices of KLT coefficients used */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltSelIndGain[12]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltSelIndShape[108]; - -/* cdf array for model indicator */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltModelCdf[KLT_NUM_MODELS+1]; - -/* pointer to cdf array for model indicator */ -extern const WebRtc_UWord16 *WebRtcIsac_kQKltModelCdfPtr[1]; - -/* initial cdf index for decoder of model indicator */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltModelInitIndex[1]; - -/* offset to go from rounded value to quantization index */ -extern const short WebRtcIsac_kQKltQuantMinGain[12]; - -extern const short WebRtcIsac_kQKltQuantMinShape[108]; - -/* maximum quantization index */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltMaxIndGain[12]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltMaxIndShape[108]; - -/* index offset */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltOffsetGain[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltOffsetShape[KLT_NUM_MODELS][108]; - -/* initial cdf index for KLT coefficients */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltInitIndexGain[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltInitIndexShape[KLT_NUM_MODELS][108]; - -/* offsets for quantizer representation levels */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltOfLevelsGain[3]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltOfLevelsShape[3]; - -/* quantizer representation levels */ -extern const double WebRtcIsac_kQKltLevelsGain[1176]; - -extern const double WebRtcIsac_kQKltLevelsShape[1735]; - -/* cdf tables for quantizer indices */ -extern const WebRtc_UWord16 WebRtcIsac_kQKltCdfGain[1212]; - -extern const WebRtc_UWord16 WebRtcIsac_kQKltCdfShape[2059]; - -/* pointers to cdf tables for quantizer indices */ -extern const WebRtc_UWord16 *WebRtcIsac_kQKltCdfPtrGain[KLT_NUM_MODELS][12]; - -extern const WebRtc_UWord16 *WebRtcIsac_kQKltCdfPtrShape[KLT_NUM_MODELS][108]; - -/* code length for all coefficients using different models */ -extern const double WebRtcIsac_kQKltCodeLenGain[392]; - -extern const double WebRtcIsac_kQKltCodeLenShape[578]; - -/* left KLT transforms */ -extern const double WebRtcIsac_kKltT1Gain[KLT_NUM_MODELS][4]; - -extern const double WebRtcIsac_kKltT1Shape[KLT_NUM_MODELS][324]; - -/* right KLT transforms */ -extern const double WebRtcIsac_kKltT2Gain[KLT_NUM_MODELS][36]; - -extern const double WebRtcIsac_kKltT2Shape[KLT_NUM_MODELS][36]; - -/* means of log gains and LAR coefficients */ -extern const double WebRtcIsac_kLpcMeansGain[KLT_NUM_MODELS][12]; - -extern const double WebRtcIsac_kLpcMeansShape[KLT_NUM_MODELS][108]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_LPC_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.c b/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.c deleted file mode 100644 index 6132aa6ca..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pitch_estimator.h" - -#include -#include -#ifdef ANDROID -#include -#endif - -static const double kInterpolWin[8] = {-0.00067556028640, 0.02184247643159, -0.12203175715679, 0.60086484101160, - 0.60086484101160, -0.12203175715679, 0.02184247643159, -0.00067556028640}; - -/* interpolation filter */ -__inline static void IntrepolFilter(double *data_ptr, double *intrp) -{ - *intrp = kInterpolWin[0] * data_ptr[-3]; - *intrp += kInterpolWin[1] * data_ptr[-2]; - *intrp += kInterpolWin[2] * data_ptr[-1]; - *intrp += kInterpolWin[3] * data_ptr[0]; - *intrp += kInterpolWin[4] * data_ptr[1]; - *intrp += kInterpolWin[5] * data_ptr[2]; - *intrp += kInterpolWin[6] * data_ptr[3]; - *intrp += kInterpolWin[7] * data_ptr[4]; -} - - -/* 2D parabolic interpolation */ -/* probably some 0.5 factors can be eliminated, and the square-roots can be removed from the Cholesky fact. */ -__inline static void Intrpol2D(double T[3][3], double *x, double *y, double *peak_val) -{ - double c, b[2], A[2][2]; - double t1, t2, d; - double delta1, delta2; - - - // double T[3][3] = {{-1.25, -.25,-.25}, {-.25, .75, .75}, {-.25, .75, .75}}; - // should result in: delta1 = 0.5; delta2 = 0.0; peak_val = 1.0 - - c = T[1][1]; - b[0] = 0.5 * (T[1][2] + T[2][1] - T[0][1] - T[1][0]); - b[1] = 0.5 * (T[1][0] + T[2][1] - T[0][1] - T[1][2]); - A[0][1] = -0.5 * (T[0][1] + T[2][1] - T[1][0] - T[1][2]); - t1 = 0.5 * (T[0][0] + T[2][2]) - c; - t2 = 0.5 * (T[2][0] + T[0][2]) - c; - d = (T[0][1] + T[1][2] + T[1][0] + T[2][1]) - 4.0 * c - t1 - t2; - A[0][0] = -t1 - 0.5 * d; - A[1][1] = -t2 - 0.5 * d; - - /* deal with singularities or ill-conditioned cases */ - if ( (A[0][0] < 1e-7) || ((A[0][0] * A[1][1] - A[0][1] * A[0][1]) < 1e-7) ) { - *peak_val = T[1][1]; - return; - } - - /* Cholesky decomposition: replace A by upper-triangular factor */ - A[0][0] = sqrt(A[0][0]); - A[0][1] = A[0][1] / A[0][0]; - A[1][1] = sqrt(A[1][1] - A[0][1] * A[0][1]); - - /* compute [x; y] = -0.5 * inv(A) * b */ - t1 = b[0] / A[0][0]; - t2 = (b[1] - t1 * A[0][1]) / A[1][1]; - delta2 = t2 / A[1][1]; - delta1 = 0.5 * (t1 - delta2 * A[0][1]) / A[0][0]; - delta2 *= 0.5; - - /* limit norm */ - t1 = delta1 * delta1 + delta2 * delta2; - if (t1 > 1.0) { - delta1 /= t1; - delta2 /= t1; - } - - *peak_val = 0.5 * (b[0] * delta1 + b[1] * delta2) + c; - - *x += delta1; - *y += delta2; -} - - -static void PCorr(const double *in, double *outcorr) -{ - double sum, ysum, prod; - const double *x, *inptr; - int k, n; - - //ysum = 1e-6; /* use this with float (i.s.o. double)! */ - ysum = 1e-13; - sum = 0.0; - x = in + PITCH_MAX_LAG/2 + 2; - for (n = 0; n < PITCH_CORR_LEN2; n++) { - ysum += in[n] * in[n]; - sum += x[n] * in[n]; - } - - outcorr += PITCH_LAG_SPAN2 - 1; /* index of last element in array */ - *outcorr = sum / sqrt(ysum); - - for (k = 1; k < PITCH_LAG_SPAN2; k++) { - ysum -= in[k-1] * in[k-1]; - ysum += in[PITCH_CORR_LEN2 + k - 1] * in[PITCH_CORR_LEN2 + k - 1]; - sum = 0.0; - inptr = &in[k]; - prod = x[0] * inptr[0]; - for (n = 1; n < PITCH_CORR_LEN2; n++) { - sum += prod; - prod = x[n] * inptr[n]; - } - sum += prod; - outcorr--; - *outcorr = sum / sqrt(ysum); - } -} - - -void WebRtcIsac_InitializePitch(const double *in, - const double old_lag, - const double old_gain, - PitchAnalysisStruct *State, - double *lags) -{ - double buf_dec[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2+2]; - double ratio, log_lag, gain_bias; - double bias; - double corrvec1[PITCH_LAG_SPAN2]; - double corrvec2[PITCH_LAG_SPAN2]; - int m, k; - // Allocating 10 extra entries at the begining of the CorrSurf - double corrSurfBuff[10 + (2*PITCH_BW+3)*(PITCH_LAG_SPAN2+4)]; - double* CorrSurf[2*PITCH_BW+3]; - double *CorrSurfPtr1, *CorrSurfPtr2; - double LagWin[3] = {0.2, 0.5, 0.98}; - int ind1, ind2, peaks_ind, peak, max_ind; - int peaks[PITCH_MAX_NUM_PEAKS]; - double adj, gain_tmp; - double corr, corr_max; - double intrp_a, intrp_b, intrp_c, intrp_d; - double peak_vals[PITCH_MAX_NUM_PEAKS]; - double lags1[PITCH_MAX_NUM_PEAKS]; - double lags2[PITCH_MAX_NUM_PEAKS]; - double T[3][3]; - int row; - - for(k = 0; k < 2*PITCH_BW+3; k++) - { - CorrSurf[k] = &corrSurfBuff[10 + k * (PITCH_LAG_SPAN2+4)]; - } - /* reset CorrSurf matrix */ - memset(corrSurfBuff, 0, sizeof(double) * (10 + (2*PITCH_BW+3) * (PITCH_LAG_SPAN2+4))); - - //warnings -DH - max_ind = 0; - peak = 0; - - /* copy old values from state buffer */ - memcpy(buf_dec, State->dec_buffer, sizeof(double) * (PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2)); - - /* decimation; put result after the old values */ - WebRtcIsac_DecimateAllpass(in, State->decimator_state, PITCH_FRAME_LEN, - &buf_dec[PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2]); - - /* low-pass filtering */ - for (k = PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2; k < PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2+2; k++) - buf_dec[k] += 0.75 * buf_dec[k-1] - 0.25 * buf_dec[k-2]; - - /* copy end part back into state buffer */ - memcpy(State->dec_buffer, buf_dec+PITCH_FRAME_LEN/2, sizeof(double) * (PITCH_CORR_LEN2+PITCH_CORR_STEP2+PITCH_MAX_LAG/2-PITCH_FRAME_LEN/2+2)); - - /* compute correlation for first and second half of the frame */ - PCorr(buf_dec, corrvec1); - PCorr(buf_dec + PITCH_CORR_STEP2, corrvec2); - - /* bias towards pitch lag of previous frame */ - log_lag = log(0.5 * old_lag); - gain_bias = 4.0 * old_gain * old_gain; - if (gain_bias > 0.8) gain_bias = 0.8; - for (k = 0; k < PITCH_LAG_SPAN2; k++) - { - ratio = log((double) (k + (PITCH_MIN_LAG/2-2))) - log_lag; - bias = 1.0 + gain_bias * exp(-5.0 * ratio * ratio); - corrvec1[k] *= bias; - } - - /* taper correlation functions */ - for (k = 0; k < 3; k++) { - gain_tmp = LagWin[k]; - corrvec1[k] *= gain_tmp; - corrvec2[k] *= gain_tmp; - corrvec1[PITCH_LAG_SPAN2-1-k] *= gain_tmp; - corrvec2[PITCH_LAG_SPAN2-1-k] *= gain_tmp; - } - - corr_max = 0.0; - /* fill middle row of correlation surface */ - ind1 = 0; - ind2 = 0; - CorrSurfPtr1 = &CorrSurf[PITCH_BW][2]; - for (k = 0; k < PITCH_LAG_SPAN2; k++) { - corr = corrvec1[ind1++] + corrvec2[ind2++]; - CorrSurfPtr1[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - } - } - /* fill first and last rows of correlation surface */ - ind1 = 0; - ind2 = PITCH_BW; - CorrSurfPtr1 = &CorrSurf[0][2]; - CorrSurfPtr2 = &CorrSurf[2*PITCH_BW][PITCH_BW+2]; - for (k = 0; k < PITCH_LAG_SPAN2-PITCH_BW; k++) { - ratio = ((double) (ind1 + 12)) / ((double) (ind2 + 12)); - adj = 0.2 * ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as a function of ratio */ - corr = adj * (corrvec1[ind1] + corrvec2[ind2]); - CorrSurfPtr1[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - } - corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); - CorrSurfPtr2[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); - } - } - /* fill second and next to last rows of correlation surface */ - ind1 = 0; - ind2 = PITCH_BW-1; - CorrSurfPtr1 = &CorrSurf[1][2]; - CorrSurfPtr2 = &CorrSurf[2*PITCH_BW-1][PITCH_BW+1]; - for (k = 0; k < PITCH_LAG_SPAN2-PITCH_BW+1; k++) { - ratio = ((double) (ind1 + 12)) / ((double) (ind2 + 12)); - adj = 0.9 * ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as a function of ratio */ - corr = adj * (corrvec1[ind1] + corrvec2[ind2]); - CorrSurfPtr1[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - } - corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); - CorrSurfPtr2[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); - } - } - /* fill remainder of correlation surface */ - for (m = 2; m < PITCH_BW; m++) { - ind1 = 0; - ind2 = PITCH_BW - m; /* always larger than ind1 */ - CorrSurfPtr1 = &CorrSurf[m][2]; - CorrSurfPtr2 = &CorrSurf[2*PITCH_BW-m][PITCH_BW+2-m]; - for (k = 0; k < PITCH_LAG_SPAN2-PITCH_BW+m; k++) { - ratio = ((double) (ind1 + 12)) / ((double) (ind2 + 12)); - adj = ratio * (2.0 - ratio); /* adjustment factor; inverse parabola as a function of ratio */ - corr = adj * (corrvec1[ind1] + corrvec2[ind2]); - CorrSurfPtr1[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - } - corr = adj * (corrvec1[ind2++] + corrvec2[ind1++]); - CorrSurfPtr2[k] = corr; - if (corr > corr_max) { - corr_max = corr; /* update maximum */ - max_ind = (int)(&CorrSurfPtr2[k] - &CorrSurf[0][0]); - } - } - } - - /* threshold value to qualify as a peak */ - corr_max *= 0.6; - - peaks_ind = 0; - /* find peaks */ - for (m = 1; m < PITCH_BW+1; m++) { - if (peaks_ind == PITCH_MAX_NUM_PEAKS) break; - CorrSurfPtr1 = &CorrSurf[m][2]; - for (k = 2; k < PITCH_LAG_SPAN2-PITCH_BW-2+m; k++) { - corr = CorrSurfPtr1[k]; - if (corr > corr_max) { - if ( (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2+5)]) && (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2+4)]) ) { - if ( (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2+4)]) && (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2+5)]) ) { - /* found a peak; store index into matrix */ - peaks[peaks_ind++] = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - if (peaks_ind == PITCH_MAX_NUM_PEAKS) break; - } - } - } - } - } - for (m = PITCH_BW+1; m < 2*PITCH_BW; m++) { - if (peaks_ind == PITCH_MAX_NUM_PEAKS) break; - CorrSurfPtr1 = &CorrSurf[m][2]; - for (k = 2+m-PITCH_BW; k < PITCH_LAG_SPAN2-2; k++) { - corr = CorrSurfPtr1[k]; - if (corr > corr_max) { - if ( (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2+5)]) && (corr > CorrSurfPtr1[k - (PITCH_LAG_SPAN2+4)]) ) { - if ( (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2+4)]) && (corr > CorrSurfPtr1[k + (PITCH_LAG_SPAN2+5)]) ) { - /* found a peak; store index into matrix */ - peaks[peaks_ind++] = (int)(&CorrSurfPtr1[k] - &CorrSurf[0][0]); - if (peaks_ind == PITCH_MAX_NUM_PEAKS) break; - } - } - } - } - } - - if (peaks_ind > 0) { - /* examine each peak */ - CorrSurfPtr1 = &CorrSurf[0][0]; - for (k = 0; k < peaks_ind; k++) { - peak = peaks[k]; - - /* compute four interpolated values around current peak */ - IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+5)], &intrp_a); - IntrepolFilter(&CorrSurfPtr1[peak - 1 ], &intrp_b); - IntrepolFilter(&CorrSurfPtr1[peak ], &intrp_c); - IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+4)], &intrp_d); - - /* determine maximum of the interpolated values */ - corr = CorrSurfPtr1[peak]; - corr_max = intrp_a; - if (intrp_b > corr_max) corr_max = intrp_b; - if (intrp_c > corr_max) corr_max = intrp_c; - if (intrp_d > corr_max) corr_max = intrp_d; - - /* determine where the peak sits and fill a 3x3 matrix around it */ - row = peak / (PITCH_LAG_SPAN2+4); - lags1[k] = (double) ((peak - row * (PITCH_LAG_SPAN2+4)) + PITCH_MIN_LAG/2 - 4); - lags2[k] = (double) (lags1[k] + PITCH_BW - row); - if ( corr > corr_max ) { - T[0][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+5)]; - T[2][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+4)]; - T[1][1] = corr; - T[0][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+4)]; - T[2][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+5)]; - T[1][0] = intrp_a; - T[0][1] = intrp_b; - T[2][1] = intrp_c; - T[1][2] = intrp_d; - } else { - if (intrp_a == corr_max) { - lags1[k] -= 0.5; - lags2[k] += 0.5; - IntrepolFilter(&CorrSurfPtr1[peak - 2*(PITCH_LAG_SPAN2+5)], &T[0][0]); - IntrepolFilter(&CorrSurfPtr1[peak - (2*PITCH_LAG_SPAN2+9)], &T[2][0]); - T[1][1] = intrp_a; - T[0][2] = intrp_b; - T[2][2] = intrp_c; - T[1][0] = CorrSurfPtr1[peak - (2*PITCH_LAG_SPAN2+9)]; - T[0][1] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+5)]; - T[2][1] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+4)]; - T[1][2] = corr; - } else if (intrp_b == corr_max) { - lags1[k] -= 0.5; - lags2[k] -= 0.5; - IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+6)], &T[0][0]); - T[2][0] = intrp_a; - T[1][1] = intrp_b; - IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+3)], &T[0][2]); - T[2][2] = intrp_d; - T[1][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+5)]; - T[0][1] = CorrSurfPtr1[peak - 1]; - T[2][1] = corr; - T[1][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+4)]; - } else if (intrp_c == corr_max) { - lags1[k] += 0.5; - lags2[k] += 0.5; - T[0][0] = intrp_a; - IntrepolFilter(&CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+4)], &T[2][0]); - T[1][1] = intrp_c; - T[0][2] = intrp_d; - IntrepolFilter(&CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+5)], &T[2][2]); - T[1][0] = CorrSurfPtr1[peak - (PITCH_LAG_SPAN2+4)]; - T[0][1] = corr; - T[2][1] = CorrSurfPtr1[peak + 1]; - T[1][2] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+5)]; - } else { - lags1[k] += 0.5; - lags2[k] -= 0.5; - T[0][0] = intrp_b; - T[2][0] = intrp_c; - T[1][1] = intrp_d; - IntrepolFilter(&CorrSurfPtr1[peak + 2*(PITCH_LAG_SPAN2+4)], &T[0][2]); - IntrepolFilter(&CorrSurfPtr1[peak + (2*PITCH_LAG_SPAN2+9)], &T[2][2]); - T[1][0] = corr; - T[0][1] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+4)]; - T[2][1] = CorrSurfPtr1[peak + (PITCH_LAG_SPAN2+5)]; - T[1][2] = CorrSurfPtr1[peak + (2*PITCH_LAG_SPAN2+9)]; - } - } - - /* 2D parabolic interpolation gives more accurate lags and peak value */ - Intrpol2D(T, &lags1[k], &lags2[k], &peak_vals[k]); - } - - /* determine the highest peak, after applying a bias towards short lags */ - corr_max = 0.0; - for (k = 0; k < peaks_ind; k++) { - corr = peak_vals[k] * pow(PITCH_PEAK_DECAY, log(lags1[k] + lags2[k])); - if (corr > corr_max) { - corr_max = corr; - peak = k; - } - } - - lags1[peak] *= 2.0; - lags2[peak] *= 2.0; - - if (lags1[peak] < (double) PITCH_MIN_LAG) lags1[peak] = (double) PITCH_MIN_LAG; - if (lags2[peak] < (double) PITCH_MIN_LAG) lags2[peak] = (double) PITCH_MIN_LAG; - if (lags1[peak] > (double) PITCH_MAX_LAG) lags1[peak] = (double) PITCH_MAX_LAG; - if (lags2[peak] > (double) PITCH_MAX_LAG) lags2[peak] = (double) PITCH_MAX_LAG; - - /* store lags of highest peak in output array */ - lags[0] = lags1[peak]; - lags[1] = lags1[peak]; - lags[2] = lags2[peak]; - lags[3] = lags2[peak]; - } - else - { - row = max_ind / (PITCH_LAG_SPAN2+4); - lags1[0] = (double) ((max_ind - row * (PITCH_LAG_SPAN2+4)) + PITCH_MIN_LAG/2 - 4); - lags2[0] = (double) (lags1[0] + PITCH_BW - row); - - if (lags1[0] < (double) PITCH_MIN_LAG) lags1[0] = (double) PITCH_MIN_LAG; - if (lags2[0] < (double) PITCH_MIN_LAG) lags2[0] = (double) PITCH_MIN_LAG; - if (lags1[0] > (double) PITCH_MAX_LAG) lags1[0] = (double) PITCH_MAX_LAG; - if (lags2[0] > (double) PITCH_MAX_LAG) lags2[0] = (double) PITCH_MAX_LAG; - - /* store lags of highest peak in output array */ - lags[0] = lags1[0]; - lags[1] = lags1[0]; - lags[2] = lags2[0]; - lags[3] = lags2[0]; - } -} - - - -/* create weighting matrix by orthogonalizing a basis of polynomials of increasing order - * t = (0:4)'; - * A = [t.^0, t.^1, t.^2, t.^3, t.^4]; - * [Q, dummy] = qr(A); - * P.Weight = Q * diag([0, .1, .5, 1, 1]) * Q'; */ -static const double kWeight[5][5] = { - { 0.29714285714286, -0.30857142857143, -0.05714285714286, 0.05142857142857, 0.01714285714286}, - {-0.30857142857143, 0.67428571428571, -0.27142857142857, -0.14571428571429, 0.05142857142857}, - {-0.05714285714286, -0.27142857142857, 0.65714285714286, -0.27142857142857, -0.05714285714286}, - { 0.05142857142857, -0.14571428571429, -0.27142857142857, 0.67428571428571, -0.30857142857143}, - { 0.01714285714286, 0.05142857142857, -0.05714285714286, -0.30857142857143, 0.29714285714286} -}; - - -void WebRtcIsac_PitchAnalysis(const double *in, /* PITCH_FRAME_LEN samples */ - double *out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ - PitchAnalysisStruct *State, - double *lags, - double *gains) -{ - double HPin[PITCH_FRAME_LEN]; - double Weighted[PITCH_FRAME_LEN]; - double Whitened[PITCH_FRAME_LEN + QLOOKAHEAD]; - double inbuf[PITCH_FRAME_LEN + QLOOKAHEAD]; - double out_G[PITCH_FRAME_LEN + QLOOKAHEAD]; // could be removed by using out instead - double out_dG[4][PITCH_FRAME_LEN + QLOOKAHEAD]; - double old_lag, old_gain; - double nrg_wht, tmp; - double Wnrg, Wfluct, Wgain; - double H[4][4]; - double grad[4]; - double dG[4]; - int k, m, n, iter; - - /* high pass filtering using second order pole-zero filter */ - WebRtcIsac_Highpass(in, HPin, State->hp_state, PITCH_FRAME_LEN); - - /* copy from state into buffer */ - memcpy(Whitened, State->whitened_buf, sizeof(double) * QLOOKAHEAD); - - /* compute weighted and whitened signals */ - WebRtcIsac_WeightingFilter(HPin, &Weighted[0], &Whitened[QLOOKAHEAD], &(State->Wghtstr)); - - /* copy from buffer into state */ - memcpy(State->whitened_buf, Whitened+PITCH_FRAME_LEN, sizeof(double) * QLOOKAHEAD); - - old_lag = State->PFstr_wght.oldlagp[0]; - old_gain = State->PFstr_wght.oldgainp[0]; - - /* inital pitch estimate */ - WebRtcIsac_InitializePitch(Weighted, old_lag, old_gain, State, lags); - - - /* Iterative optimization of lags - to be done */ - - /* compute energy of whitened signal */ - nrg_wht = 0.0; - for (k = 0; k < PITCH_FRAME_LEN + QLOOKAHEAD; k++) - nrg_wht += Whitened[k] * Whitened[k]; - - - /* Iterative optimization of gains */ - - /* set weights for energy, gain fluctiation, and spectral gain penalty functions */ - Wnrg = 1.0 / nrg_wht; - Wgain = 0.005; - Wfluct = 3.0; - - /* set initial gains */ - for (k = 0; k < 4; k++) - gains[k] = PITCH_MAX_GAIN_06; - - /* two iterations should be enough */ - for (iter = 0; iter < 2; iter++) { - /* compute Jacobian of pre-filter output towards gains */ - WebRtcIsac_PitchfilterPre_gains(Whitened, out_G, out_dG, &(State->PFstr_wght), lags, gains); - - /* gradient and approximate Hessian (lower triangle) for minimizing the filter's output power */ - for (k = 0; k < 4; k++) { - tmp = 0.0; - for (n = 0; n < PITCH_FRAME_LEN + QLOOKAHEAD; n++) - tmp += out_G[n] * out_dG[k][n]; - grad[k] = tmp * Wnrg; - } - for (k = 0; k < 4; k++) { - for (m = 0; m <= k; m++) { - tmp = 0.0; - for (n = 0; n < PITCH_FRAME_LEN + QLOOKAHEAD; n++) - tmp += out_dG[m][n] * out_dG[k][n]; - H[k][m] = tmp * Wnrg; - } - } - - /* add gradient and Hessian (lower triangle) for dampening fast gain changes */ - for (k = 0; k < 4; k++) { - tmp = kWeight[k+1][0] * old_gain; - for (m = 0; m < 4; m++) - tmp += kWeight[k+1][m+1] * gains[m]; - grad[k] += tmp * Wfluct; - } - for (k = 0; k < 4; k++) { - for (m = 0; m <= k; m++) { - H[k][m] += kWeight[k+1][m+1] * Wfluct; - } - } - - /* add gradient and Hessian for dampening gain */ - for (k = 0; k < 3; k++) { - tmp = 1.0 / (1 - gains[k]); - grad[k] += tmp * tmp * Wgain; - H[k][k] += 2.0 * tmp * (tmp * tmp * Wgain); - } - tmp = 1.0 / (1 - gains[3]); - grad[3] += 1.33 * (tmp * tmp * Wgain); - H[3][3] += 2.66 * tmp * (tmp * tmp * Wgain); - - - /* compute Cholesky factorization of Hessian - * by overwritting the upper triangle; scale factors on diagonal - * (for non pc-platforms store the inverse of the diagonals seperately to minimize divisions) */ - H[0][1] = H[1][0] / H[0][0]; - H[0][2] = H[2][0] / H[0][0]; - H[0][3] = H[3][0] / H[0][0]; - H[1][1] -= H[0][0] * H[0][1] * H[0][1]; - H[1][2] = (H[2][1] - H[0][1] * H[2][0]) / H[1][1]; - H[1][3] = (H[3][1] - H[0][1] * H[3][0]) / H[1][1]; - H[2][2] -= H[0][0] * H[0][2] * H[0][2] + H[1][1] * H[1][2] * H[1][2]; - H[2][3] = (H[3][2] - H[0][2] * H[3][0] - H[1][2] * H[1][1] * H[1][3]) / H[2][2]; - H[3][3] -= H[0][0] * H[0][3] * H[0][3] + H[1][1] * H[1][3] * H[1][3] + H[2][2] * H[2][3] * H[2][3]; - - /* Compute update as delta_gains = -inv(H) * grad */ - /* copy and negate */ - for (k = 0; k < 4; k++) - dG[k] = -grad[k]; - /* back substitution */ - dG[1] -= dG[0] * H[0][1]; - dG[2] -= dG[0] * H[0][2] + dG[1] * H[1][2]; - dG[3] -= dG[0] * H[0][3] + dG[1] * H[1][3] + dG[2] * H[2][3]; - /* scale */ - for (k = 0; k < 4; k++) - dG[k] /= H[k][k]; - /* back substitution */ - dG[2] -= dG[3] * H[2][3]; - dG[1] -= dG[3] * H[1][3] + dG[2] * H[1][2]; - dG[0] -= dG[3] * H[0][3] + dG[2] * H[0][2] + dG[1] * H[0][1]; - - /* update gains and check range */ - for (k = 0; k < 4; k++) { - gains[k] += dG[k]; - if (gains[k] > PITCH_MAX_GAIN) - gains[k] = PITCH_MAX_GAIN; - else if (gains[k] < 0.0) - gains[k] = 0.0; - } - } - - /* update state for next frame */ - WebRtcIsac_PitchfilterPre(Whitened, out, &(State->PFstr_wght), lags, gains); - - /* concatenate previous input's end and current input */ - memcpy(inbuf, State->inbuf, sizeof(double) * QLOOKAHEAD); - memcpy(inbuf+QLOOKAHEAD, in, sizeof(double) * PITCH_FRAME_LEN); - - /* lookahead pitch filtering for masking analysis */ - WebRtcIsac_PitchfilterPre_la(inbuf, out, &(State->PFstr), lags, gains); - - /* store last part of input */ - for (k = 0; k < QLOOKAHEAD; k++) - State->inbuf[k] = inbuf[k + PITCH_FRAME_LEN]; -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.h b/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.h deleted file mode 100644 index f5d93564b..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_estimator.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_estimator.h - * - * Pitch functions - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ - -#include "structs.h" - - - -void WebRtcIsac_PitchAnalysis(const double *in, /* PITCH_FRAME_LEN samples */ - double *out, /* PITCH_FRAME_LEN+QLOOKAHEAD samples */ - PitchAnalysisStruct *State, - double *lags, - double *gains); - -void WebRtcIsac_InitializePitch(const double *in, - const double old_lag, - const double old_gain, - PitchAnalysisStruct *State, - double *lags); - -void WebRtcIsac_PitchfilterPre(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPost(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPre_la(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_PitchfilterPre_gains(double *indat, - double *outdat, - double out_dG[][PITCH_FRAME_LEN + QLOOKAHEAD], - PitchFiltstr *pfp, - double *lags, - double *gains); - -void WebRtcIsac_WeightingFilter(const double *in, double *weiout, double *whiout, WeightFiltstr *wfdata); - -void WebRtcIsac_Highpass(const double *in, double *out, double *state, int N); - -void WebRtcIsac_DecimateAllpass(const double *in, - double *state_in, /* array of size: 2*ALLPASSSECTIONS+1 */ - int N, /* number of input samples */ - double *out); /* array of size N/2 */ - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_ESTIMATOR_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_filter.c b/modules/audio_coding/codecs/iSAC/main/source/pitch_filter.c deleted file mode 100644 index 0d88e3eb8..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_filter.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pitch_estimator.h" -#include -#include -#include - -static double kDampFilter[PITCH_DAMPORDER] = {-0.07, 0.25, 0.64, 0.25, -0.07}; - -/* interpolation coefficients; generated by design_pitch_filter.m */ -static double kIntrpCoef[PITCH_FRACS][PITCH_FRACORDER] = { - {-0.02239172458614, 0.06653315052934, -0.16515880017569, 0.60701333734125, 0.64671399919202, -0.20249000396417, 0.09926548334755, -0.04765933793109, 0.01754159521746}, - {-0.01985640750434, 0.05816126837866, -0.13991265473714, 0.44560418147643, 0.79117042386876, -0.20266133815188, 0.09585268418555, -0.04533310458084, 0.01654127246314}, - {-0.01463300534216, 0.04229888475060, -0.09897034715253, 0.28284326017787, 0.90385267956632, -0.16976950138649, 0.07704272393639, -0.03584218578311, 0.01295781500709}, - {-0.00764851320885, 0.02184035544377, -0.04985561057281, 0.13083306574393, 0.97545011664662, -0.10177807997561, 0.04400901776474, -0.02010737175166, 0.00719783432422}, - {-0.00000000000000, 0.00000000000000, -0.00000000000001, 0.00000000000001, 0.99999999999999, 0.00000000000001, -0.00000000000001, 0.00000000000000, -0.00000000000000}, - { 0.00719783432422, -0.02010737175166, 0.04400901776474, -0.10177807997562, 0.97545011664663, 0.13083306574393, -0.04985561057280, 0.02184035544377, -0.00764851320885}, - { 0.01295781500710, -0.03584218578312, 0.07704272393640, -0.16976950138650, 0.90385267956634, 0.28284326017785, -0.09897034715252, 0.04229888475059, -0.01463300534216}, - { 0.01654127246315, -0.04533310458085, 0.09585268418557, -0.20266133815190, 0.79117042386878, 0.44560418147640, -0.13991265473712, 0.05816126837865, -0.01985640750433} -}; - -#if defined(WEBRTC_LINUX) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(WEBRTC_MAC) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(X64) -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} -#elif defined(WEBRTC_TARGET_PC) - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - - WebRtc_Word32 intgr; - - _asm - { - fld flt - fistp intgr - } ; - - return intgr ; -} - -#else// Do a slow but correct implementation of lrint - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} -#endif - -void WebRtcIsac_PitchfilterPre(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains) -{ - - double ubuf[PITCH_INTBUFFSIZE]; - double *fracoeff = NULL; - double curgain, curlag, gaindelta, lagdelta; - double sum, inystate[PITCH_DAMPORDER]; - double ftmp, oldlag, oldgain; - int k, n, m, pos, ind, pos2, Li, frc; - - Li = 0; - /* Set up buffer and states */ - memcpy(ubuf, pfp->ubuf, sizeof(double) * PITCH_BUFFSIZE); - memcpy(inystate, pfp->ystate, sizeof(double) * PITCH_DAMPORDER); - - oldlag = *pfp->oldlagp; - oldgain = *pfp->oldgainp; - - /* No interpolation if pitch lag step is big */ - if ((lags[0] > (PITCH_UPSTEP * oldlag)) || (lags[0] < (PITCH_DOWNSTEP * oldlag))) { - oldlag = lags[0]; - oldgain = gains[0]; - } - - ind=0; - for (k=0;k0;m--) - inystate[m] = inystate[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - sum=0; - for (m=0;mubuf, ubuf+PITCH_FRAME_LEN, sizeof(double) * PITCH_BUFFSIZE); - memcpy(pfp->ystate, inystate, sizeof(double) * PITCH_DAMPORDER); - - *pfp->oldlagp = oldlag; - *pfp->oldgainp = oldgain; - -} - - -void WebRtcIsac_PitchfilterPre_la(double *indat, - double *outdat, - PitchFiltstr *pfp, - double *lags, - double *gains) -{ - double ubuf[PITCH_INTBUFFSIZE+QLOOKAHEAD]; - double *fracoeff = NULL; - double curgain, curlag, gaindelta, lagdelta; - double sum, inystate[PITCH_DAMPORDER]; - double ftmp; - double oldlag, oldgain; - int k, n, m, pos, ind, pos2, Li, frc; - - Li = 0; - /* Set up buffer and states */ - memcpy(ubuf, pfp->ubuf, sizeof(double) * PITCH_BUFFSIZE); - memcpy(inystate, pfp->ystate, sizeof(double) * PITCH_DAMPORDER); - - oldlag = *pfp->oldlagp; - oldgain = *pfp->oldgainp; - - /* No interpolation if pitch lag step is big */ - if ((lags[0] > (PITCH_UPSTEP * oldlag)) || (lags[0] < (PITCH_DOWNSTEP * oldlag))) { - oldlag = lags[0]; - oldgain = gains[0]; - } - - - ind=0; - for (k=0;k0;m--) - inystate[m] = inystate[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - sum=0.0; - for (m=0;mubuf, ubuf+PITCH_FRAME_LEN, sizeof(double) * PITCH_BUFFSIZE); - memcpy(pfp->ystate, inystate, sizeof(double) * PITCH_DAMPORDER); - - *pfp->oldlagp = oldlag; - *pfp->oldgainp = oldgain; - - - /* Filter look-ahead segment */ - for (n=0;n0;m--) - inystate[m] = inystate[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - sum=0.0; - for (m=0;mubuf, sizeof(double) * PITCH_BUFFSIZE); - memcpy(inystate, pfp->ystate, sizeof(double) * PITCH_DAMPORDER); - - /* clear some buffers */ - for (k = 0; k < 4; k++) { - gain_mult[k] = 0.0; - for (n = 0; n < PITCH_DAMPORDER; n++) - inystate_dG[k][n] = 0.0; - } - - oldlag = *pfp->oldlagp; - oldgain = *pfp->oldgainp; - - /* No interpolation if pitch lag step is big */ - if ((lags[0] > (PITCH_UPSTEP * oldlag)) || (lags[0] < (PITCH_DOWNSTEP * oldlag))) { - oldlag = lags[0]; - oldgain = gains[0]; - gain_mult[0] = 1.0; - } - - - ind=0; - for (k=0;k 1.0) gain_mult[k] = 1.0; - if (k > 0) gain_mult[k-1] -= 0.2; - } - - /* shift low pass filter states */ - for (m=PITCH_DAMPORDER-1;m>0;m--) { - inystate[m] = inystate[m-1]; - for (j = 0; j < 4; j++) - inystate_dG[j][m] = inystate_dG[j][m-1]; - } - - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - - /* Filter to get fractional pitch */ - sum=0.0; - for (m=0;m 0) ? Li-ind : 0; - for (j = 0; j < k+1; j++) { - /* filter */ - sum2 = 0.0; - for (m = PITCH_FRACORDER-1; m >= m_tmp; m--) - sum2 += out_dG[j][ind-Li + m] * fracoeff[m]; - inystate_dG[j][0] = gain_mult[j] * sum + curgain * sum2; - } - - /* Low pass filter */ - sum=0.0; - for (m=0;m0;m--) { - inystate[m] = inystate[m-1]; - for (j = 0; j < 4; j++) - inystate_dG[j][m] = inystate_dG[j][m-1]; - } - - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - - /* Filter to get fractional pitch */ - sum=0.0; - for (m=0;m 0) ? Li-ind : 0; - for (j = 0; (j= m_tmp; m--) - sum2 += out_dG[j][ind-Li + m] * fracoeff[m]; - inystate_dG[j][0] = gain_mult[j] * sum + curgain * sum2; - } - - /* Low pass filter */ - sum=0.0; - for (m=0;mubuf, sizeof(double) * PITCH_BUFFSIZE); - memcpy(inystate, pfp->ystate, sizeof(double) * PITCH_DAMPORDER); - - oldlag = *pfp->oldlagp; - oldgain = *pfp->oldgainp; - - /* make output more periodic */ - for (k=0;k (PITCH_UPSTEP * oldlag)) || (lags[0] < (PITCH_DOWNSTEP * oldlag))) { - oldlag = lags[0]; - oldgain = gains[0]; - } - - - ind=0; - for (k=0;k0;m--) - inystate[m] = inystate[m-1]; - - /* Filter to get fractional pitch */ - pos = ind + PITCH_BUFFSIZE; - pos2 = pos - Li; - sum=0.0; - for (m=0;mubuf, ubuf+PITCH_FRAME_LEN, sizeof(double) * PITCH_BUFFSIZE); - memcpy(pfp->ystate, inystate, sizeof(double) * PITCH_DAMPORDER); - - *pfp->oldlagp = oldlag; - *pfp->oldgainp = oldgain; - -} diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.c b/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.c deleted file mode 100644 index 5d998a292..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pitch_gain_tables.h" - -#include "settings.h" - -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Gain Coefficient Tables ************************/ -/* cdf for quantized pitch filter gains */ -const WebRtc_UWord16 WebRtcIsac_kQPitchGainCdf[255] = { - 0, 2, 4, 6, 64, 901, 903, 905, 16954, 16956, - 16961, 17360, 17362, 17364, 17366, 17368, 17370, 17372, 17374, 17411, - 17514, 17516, 17583, 18790, 18796, 18802, 20760, 20777, 20782, 21722, - 21724, 21728, 21738, 21740, 21742, 21744, 21746, 21748, 22224, 22227, - 22230, 23214, 23229, 23239, 25086, 25108, 25120, 26088, 26094, 26098, - 26175, 26177, 26179, 26181, 26183, 26185, 26484, 26507, 26522, 27705, - 27731, 27750, 29767, 29799, 29817, 30866, 30883, 30885, 31025, 31029, - 31031, 31033, 31035, 31037, 31114, 31126, 31134, 32687, 32722, 32767, - 35718, 35742, 35757, 36943, 36952, 36954, 37115, 37128, 37130, 37132, - 37134, 37136, 37143, 37145, 37152, 38843, 38863, 38897, 47458, 47467, - 47474, 49040, 49061, 49063, 49145, 49157, 49159, 49161, 49163, 49165, - 49167, 49169, 49171, 49757, 49770, 49782, 61333, 61344, 61346, 62860, - 62883, 62885, 62887, 62889, 62891, 62893, 62895, 62897, 62899, 62901, - 62903, 62905, 62907, 62909, 65496, 65498, 65500, 65521, 65523, 65525, - 65527, 65529, 65531, 65533, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsac_kIndexLowerLimitGain[3] = { - -7, -2, -1}; - -const WebRtc_Word16 WebRtcIsac_kIndexUpperLimitGain[3] = { - 0, 3, 1}; - -const WebRtc_UWord16 WebRtcIsac_kIndexMultsGain[2] = { - 18, 3}; - -/* size of cdf table */ -const WebRtc_UWord16 WebRtcIsac_kQCdfTableSizeGain[1] = { - 256}; - -///////////////////////////FIXED POINT -/* mean values of pitch filter gains in FIXED point */ -const WebRtc_Word16 WebRtcIsac_kQMeanGain1Q12[144] = { - 843, 1092, 1336, 1222, 1405, 1656, 1500, 1815, 1843, 1838, 1839, 1843, 1843, 1843, 1843, 1843, - 1843, 1843, 814, 846, 1092, 1013, 1174, 1383, 1391, 1511, 1584, 1734, 1753, 1843, 1843, 1843, - 1843, 1843, 1843, 1843, 524, 689, 777, 845, 947, 1069, 1090, 1263, 1380, 1447, 1559, 1676, - 1645, 1749, 1843, 1843, 1843, 1843, 81, 477, 563, 611, 706, 806, 849, 1012, 1192, 1128, - 1330, 1489, 1425, 1576, 1826, 1741, 1843, 1843, 0, 290, 305, 356, 488, 575, 602, 741, - 890, 835, 1079, 1196, 1182, 1376, 1519, 1506, 1680, 1843, 0, 47, 97, 69, 289, 381, - 385, 474, 617, 664, 803, 1079, 935, 1160, 1269, 1265, 1506, 1741, 0, 0, 0, 0, - 112, 120, 190, 283, 442, 343, 526, 809, 684, 935, 1134, 1020, 1265, 1506, 0, 0, - 0, 0, 0, 0, 0, 111, 256, 87, 373, 597, 430, 684, 935, 770, 1020, 1265}; - -const WebRtc_Word16 WebRtcIsac_kQMeanGain2Q12[144] = { - 1760, 1525, 1285, 1747, 1671, 1393, 1843, 1826, 1555, 1843, 1784, 1606, 1843, 1843, 1711, 1843, - 1843, 1814, 1389, 1275, 1040, 1564, 1414, 1252, 1610, 1495, 1343, 1753, 1592, 1405, 1804, 1720, - 1475, 1843, 1814, 1581, 1208, 1061, 856, 1349, 1148, 994, 1390, 1253, 1111, 1495, 1343, 1178, - 1770, 1465, 1234, 1814, 1581, 1342, 1040, 793, 713, 1053, 895, 737, 1128, 1003, 861, 1277, - 1094, 981, 1475, 1192, 1019, 1581, 1342, 1098, 855, 570, 483, 833, 648, 540, 948, 744, - 572, 1009, 844, 636, 1234, 934, 685, 1342, 1217, 984, 537, 318, 124, 603, 423, 350, - 687, 479, 322, 791, 581, 430, 987, 671, 488, 1098, 849, 597, 283, 27, 0, 397, - 222, 38, 513, 271, 124, 624, 325, 157, 737, 484, 233, 849, 597, 343, 27, 0, - 0, 141, 0, 0, 256, 69, 0, 370, 87, 0, 484, 229, 0, 597, 343, 87}; - -const WebRtc_Word16 WebRtcIsac_kQMeanGain3Q12[144] = { - 1843, 1843, 1711, 1843, 1818, 1606, 1843, 1827, 1511, 1814, 1639, 1393, 1760, 1525, 1285, 1656, - 1419, 1176, 1835, 1718, 1475, 1841, 1650, 1387, 1648, 1498, 1287, 1600, 1411, 1176, 1522, 1299, - 1040, 1419, 1176, 928, 1773, 1461, 1128, 1532, 1355, 1202, 1429, 1260, 1115, 1398, 1151, 1025, - 1172, 1080, 790, 1176, 928, 677, 1475, 1147, 1019, 1276, 1096, 922, 1214, 1010, 901, 1057, - 893, 800, 1040, 796, 734, 928, 677, 424, 1137, 897, 753, 1120, 830, 710, 875, 751, - 601, 795, 642, 583, 790, 544, 475, 677, 474, 140, 987, 750, 482, 697, 573, 450, - 691, 487, 303, 661, 394, 332, 537, 303, 220, 424, 168, 0, 737, 484, 229, 624, - 348, 153, 441, 261, 136, 397, 166, 51, 283, 27, 0, 168, 0, 0, 484, 229, - 0, 370, 57, 0, 256, 43, 0, 141, 0, 0, 27, 0, 0, 0, 0, 0}; - - -const WebRtc_Word16 WebRtcIsac_kQMeanGain4Q12[144] = { - 1843, 1843, 1843, 1843, 1841, 1843, 1500, 1821, 1843, 1222, 1434, 1656, 843, 1092, 1336, 504, - 757, 1007, 1843, 1843, 1843, 1838, 1791, 1843, 1265, 1505, 1599, 965, 1219, 1425, 730, 821, - 1092, 249, 504, 757, 1783, 1819, 1843, 1351, 1567, 1727, 1096, 1268, 1409, 805, 961, 1131, - 444, 670, 843, 0, 249, 504, 1425, 1655, 1743, 1096, 1324, 1448, 822, 1019, 1199, 490, - 704, 867, 81, 450, 555, 0, 0, 249, 1247, 1428, 1530, 881, 1073, 1283, 610, 759, - 939, 278, 464, 645, 0, 200, 270, 0, 0, 0, 935, 1163, 1410, 528, 790, 1068, - 377, 499, 717, 173, 240, 274, 0, 43, 62, 0, 0, 0, 684, 935, 1182, 343, - 551, 735, 161, 262, 423, 0, 55, 27, 0, 0, 0, 0, 0, 0, 430, 684, - 935, 87, 377, 597, 0, 46, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0}; diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.h b/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.h deleted file mode 100644 index f958f5df4..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_gain_tables.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_gain_tables.h - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ - -#include "typedefs.h" - -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Gain Coefficient Tables ************************/ -/* cdf for quantized pitch filter gains */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchGainCdf[255]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsac_kIndexLowerLimitGain[3]; - -extern const WebRtc_Word16 WebRtcIsac_kIndexUpperLimitGain[3]; -extern const WebRtc_UWord16 WebRtcIsac_kIndexMultsGain[2]; - -/* mean values of pitch filter gains */ -//(Y) -extern const WebRtc_Word16 WebRtcIsac_kQMeanGain1Q12[144]; -extern const WebRtc_Word16 WebRtcIsac_kQMeanGain2Q12[144]; -extern const WebRtc_Word16 WebRtcIsac_kQMeanGain3Q12[144]; -extern const WebRtc_Word16 WebRtcIsac_kQMeanGain4Q12[144]; -//(Y) - -/* size of cdf table */ -extern const WebRtc_UWord16 WebRtcIsac_kQCdfTableSizeGain[1]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_GAIN_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.c b/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.c deleted file mode 100644 index 72a031e22..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "pitch_lag_tables.h" -#include "settings.h" - -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Gain Coefficient Tables ************************/ - -/* tables for use with small pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Lo[127] = { - 0, 134, 336, 549, 778, 998, 1264, 1512, 1777, 2070, - 2423, 2794, 3051, 3361, 3708, 3979, 4315, 4610, 4933, 5269, - 5575, 5896, 6155, 6480, 6816, 7129, 7477, 7764, 8061, 8358, - 8718, 9020, 9390, 9783, 10177, 10543, 10885, 11342, 11795, 12213, - 12680, 13096, 13524, 13919, 14436, 14903, 15349, 15795, 16267, 16734, - 17266, 17697, 18130, 18632, 19080, 19447, 19884, 20315, 20735, 21288, - 21764, 22264, 22723, 23193, 23680, 24111, 24557, 25022, 25537, 26082, - 26543, 27090, 27620, 28139, 28652, 29149, 29634, 30175, 30692, 31273, - 31866, 32506, 33059, 33650, 34296, 34955, 35629, 36295, 36967, 37726, - 38559, 39458, 40364, 41293, 42256, 43215, 44231, 45253, 46274, 47359, - 48482, 49678, 50810, 51853, 53016, 54148, 55235, 56263, 57282, 58363, - 59288, 60179, 61076, 61806, 62474, 63129, 63656, 64160, 64533, 64856, - 65152, 65535, 65535, 65535, 65535, 65535, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Lo[20] = { - 0, 429, 3558, 5861, 8558, 11639, 15210, 19502, 24773, 31983, - 42602, 48567, 52601, 55676, 58160, 60172, 61889, 63235, 65383, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Lo[2] = { - 0, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Lo[10] = { - 0, 2966, 6368, 11182, 19431, 37793, 48532, 55353, 60626, 65535}; - -const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrLo[4] = {WebRtcIsac_kQPitchLagCdf1Lo, WebRtcIsac_kQPitchLagCdf2Lo, WebRtcIsac_kQPitchLagCdf3Lo, WebRtcIsac_kQPitchLagCdf4Lo}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeLo[1] = {128}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsac_kQIndexLowerLimitLagLo[4] = { --140, -9, 0, -4}; - -const WebRtc_Word16 WebRtcIsac_kQIndexUpperLimitLagLo[4] = { --20, 9, 0, 4}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagLo[3] = { - 10, 1, 5}; - -/* mean values of pitch filter lags */ -const double WebRtcIsac_kQMeanLag2Lo[19] = { --17.21385070, -15.82678944, -14.07123081, -12.03003877, -10.01311864, -8.00794627, -5.91162987, -3.89231876, -1.90220980, -0.01879275, - 1.89144232, 3.88123171, 5.92146992, 7.96435361, 9.98923648, 11.98266347, 13.96101002, 15.74855713, 17.10976611}; - -const double WebRtcIsac_kQMeanLag3Lo[1] = { - 0.00000000}; - -const double WebRtcIsac_kQMeanLag4Lo[9] = { --7.76246496, -5.92083980, -3.94095226, -1.89502305, 0.03724681, 1.93054221, 3.96443467, 5.91726366, 7.78434291}; - -const double WebRtcIsac_kQPitchLagStepsizeLo = 2.000000; - - -/* tables for use with medium pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Mid[255] = { - 0, 28, 61, 88, 121, 149, 233, 331, 475, 559, - 624, 661, 689, 712, 745, 791, 815, 843, 866, 922, - 959, 1024, 1061, 1117, 1178, 1238, 1280, 1350, 1453, 1513, - 1564, 1625, 1671, 1741, 1788, 1904, 2072, 2421, 2626, 2770, - 2840, 2900, 2942, 3012, 3068, 3115, 3147, 3194, 3254, 3319, - 3366, 3520, 3678, 3780, 3850, 3911, 3957, 4032, 4106, 4185, - 4292, 4474, 4683, 4842, 5019, 5191, 5321, 5428, 5540, 5675, - 5763, 5847, 5959, 6127, 6304, 6564, 6839, 7090, 7263, 7421, - 7556, 7728, 7872, 7984, 8142, 8361, 8580, 8743, 8938, 9227, - 9409, 9539, 9674, 9795, 9930, 10060, 10177, 10382, 10614, 10861, - 11038, 11271, 11415, 11629, 11792, 12044, 12193, 12416, 12574, 12821, - 13007, 13235, 13445, 13654, 13901, 14134, 14488, 15000, 15703, 16285, - 16504, 16797, 17086, 17328, 17579, 17807, 17998, 18268, 18538, 18836, - 19087, 19274, 19474, 19716, 19935, 20270, 20833, 21303, 21532, 21741, - 21978, 22207, 22523, 22770, 23054, 23613, 23943, 24204, 24399, 24651, - 24832, 25074, 25270, 25549, 25759, 26015, 26150, 26424, 26713, 27048, - 27342, 27504, 27681, 27854, 28021, 28207, 28412, 28664, 28859, 29064, - 29278, 29548, 29748, 30107, 30377, 30656, 30856, 31164, 31452, 31755, - 32011, 32328, 32626, 32919, 33319, 33789, 34329, 34925, 35396, 35973, - 36443, 36964, 37551, 38156, 38724, 39357, 40023, 40908, 41587, 42602, - 43924, 45037, 45810, 46597, 47421, 48291, 49092, 50051, 51448, 52719, - 53440, 54241, 54944, 55977, 56676, 57299, 57872, 58389, 59059, 59688, - 60237, 60782, 61094, 61573, 61890, 62290, 62658, 63030, 63217, 63454, - 63622, 63882, 64003, 64273, 64427, 64529, 64581, 64697, 64758, 64902, - 65414, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Mid[36] = { - 0, 71, 335, 581, 836, 1039, 1323, 1795, 2258, 2608, - 3005, 3591, 4243, 5344, 7163, 10583, 16848, 28078, 49448, 57007, - 60357, 61850, 62837, 63437, 63872, 64188, 64377, 64614, 64774, 64949, - 65039, 65115, 65223, 65360, 65474, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Mid[2] = { - 0, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Mid[20] = { - 0, 28, 246, 459, 667, 1045, 1523, 2337, 4337, 11347, - 44231, 56709, 60781, 62243, 63161, 63969, 64608, 65062, 65502, 65535}; - -const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrMid[4] = {WebRtcIsac_kQPitchLagCdf1Mid, WebRtcIsac_kQPitchLagCdf2Mid, WebRtcIsac_kQPitchLagCdf3Mid, WebRtcIsac_kQPitchLagCdf4Mid}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeMid[1] = {256}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsac_kQIndexLowerLimitLagMid[4] = { --280, -17, 0, -9}; - -const WebRtc_Word16 WebRtcIsac_kQIndexUpperLimitLagMid[4] = { --40, 17, 0, 9}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagMid[3] = { - 18, 1, 10}; - -/* mean values of pitch filter lags */ -const double WebRtcIsac_kQMeanLag2Mid[35] = { --16.89183900, -15.86949778, -15.05476653, -14.00664348, -13.02793036, -12.07324237, -11.00542532, -10.11250602, -8.90792971, -8.02474753, --7.00426767, -5.94055287, -4.98251338, -3.91053158, -2.98820425, -1.93524245, -0.92978085, -0.01722509, 0.91317387, 1.92973955, - 2.96908851, 3.93728974, 4.96308471, 5.92244151, 7.08673497, 8.00993708, 9.04656316, 9.98538742, 10.97851694, 11.94772884, - 13.02426166, 14.00039951, 15.01347042, 15.80758023, 16.94086895}; - -const double WebRtcIsac_kQMeanLag3Mid[1] = { - 0.00000000}; - -const double WebRtcIsac_kQMeanLag4Mid[19] = { --8.60409403, -7.89198395, -7.03450280, -5.86260421, -4.93822322, -3.93078706, -2.91302322, -1.91824007, -0.87003282, 0.02822649, - 0.89951758, 1.87495484, 2.91802604, 3.96874074, 5.06571703, 5.93618227, 7.00520185, 7.88497726, 8.64160364}; - -const double WebRtcIsac_kQPitchLagStepsizeMid = 1.000000; - - -/* tables for use with large pitch gain */ - -/* cdf for quantized pitch filter lags */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Hi[511] = { - 0, 7, 18, 33, 69, 105, 156, 228, 315, 612, - 680, 691, 709, 724, 735, 738, 742, 746, 749, 753, - 756, 760, 764, 774, 782, 785, 789, 796, 800, 803, - 807, 814, 818, 822, 829, 832, 847, 854, 858, 869, - 876, 883, 898, 908, 934, 977, 1010, 1050, 1060, 1064, - 1075, 1078, 1086, 1089, 1093, 1104, 1111, 1122, 1133, 1136, - 1151, 1162, 1183, 1209, 1252, 1281, 1339, 1364, 1386, 1401, - 1411, 1415, 1426, 1430, 1433, 1440, 1448, 1455, 1462, 1477, - 1487, 1495, 1502, 1506, 1509, 1516, 1524, 1531, 1535, 1542, - 1553, 1556, 1578, 1589, 1611, 1625, 1639, 1643, 1654, 1665, - 1672, 1687, 1694, 1705, 1708, 1719, 1730, 1744, 1752, 1759, - 1791, 1795, 1820, 1867, 1886, 1915, 1936, 1943, 1965, 1987, - 2041, 2099, 2161, 2175, 2200, 2211, 2226, 2233, 2244, 2251, - 2266, 2280, 2287, 2298, 2309, 2316, 2331, 2342, 2356, 2378, - 2403, 2418, 2447, 2497, 2544, 2602, 2863, 2895, 2903, 2935, - 2950, 2971, 3004, 3011, 3018, 3029, 3040, 3062, 3087, 3127, - 3152, 3170, 3199, 3243, 3293, 3322, 3340, 3377, 3402, 3427, - 3474, 3518, 3543, 3579, 3601, 3637, 3659, 3706, 3731, 3760, - 3818, 3847, 3869, 3901, 3920, 3952, 4068, 4169, 4220, 4271, - 4524, 4571, 4604, 4632, 4672, 4730, 4777, 4806, 4857, 4904, - 4951, 5002, 5031, 5060, 5107, 5150, 5212, 5266, 5331, 5382, - 5432, 5490, 5544, 5610, 5700, 5762, 5812, 5874, 5972, 6022, - 6091, 6163, 6232, 6305, 6402, 6540, 6685, 6880, 7090, 7271, - 7379, 7452, 7542, 7625, 7687, 7770, 7843, 7911, 7966, 8024, - 8096, 8190, 8252, 8320, 8411, 8501, 8585, 8639, 8751, 8842, - 8918, 8986, 9066, 9127, 9203, 9269, 9345, 9406, 9464, 9536, - 9612, 9667, 9735, 9844, 9931, 10036, 10119, 10199, 10260, 10358, - 10441, 10514, 10666, 10734, 10872, 10951, 11053, 11125, 11223, 11324, - 11516, 11664, 11737, 11816, 11892, 12008, 12120, 12200, 12280, 12392, - 12490, 12576, 12685, 12812, 12917, 13003, 13108, 13210, 13300, 13384, - 13470, 13579, 13673, 13771, 13879, 13999, 14136, 14201, 14368, 14614, - 14759, 14867, 14958, 15030, 15121, 15189, 15280, 15385, 15461, 15555, - 15653, 15768, 15884, 15971, 16069, 16145, 16210, 16279, 16380, 16463, - 16539, 16615, 16688, 16818, 16919, 17017, 18041, 18338, 18523, 18649, - 18790, 18917, 19047, 19167, 19315, 19460, 19601, 19731, 19858, 20068, - 20173, 20318, 20466, 20625, 20741, 20911, 21045, 21201, 21396, 21588, - 21816, 22022, 22305, 22547, 22786, 23072, 23322, 23600, 23879, 24168, - 24433, 24769, 25120, 25511, 25895, 26289, 26792, 27219, 27683, 28077, - 28566, 29094, 29546, 29977, 30491, 30991, 31573, 32105, 32594, 33173, - 33788, 34497, 35181, 35833, 36488, 37255, 37921, 38645, 39275, 39894, - 40505, 41167, 41790, 42431, 43096, 43723, 44385, 45134, 45858, 46607, - 47349, 48091, 48768, 49405, 49955, 50555, 51167, 51985, 52611, 53078, - 53494, 53965, 54435, 54996, 55601, 56125, 56563, 56838, 57244, 57566, - 57967, 58297, 58771, 59093, 59419, 59647, 59886, 60143, 60461, 60693, - 60917, 61170, 61416, 61634, 61891, 62122, 62310, 62455, 62632, 62839, - 63103, 63436, 63639, 63805, 63906, 64015, 64192, 64355, 64475, 64558, - 64663, 64742, 64811, 64865, 64916, 64956, 64981, 65025, 65068, 65115, - 65195, 65314, 65419, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, - 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Hi[68] = { - 0, 7, 11, 22, 37, 52, 56, 59, 81, 85, - 89, 96, 115, 130, 137, 152, 170, 181, 193, 200, - 207, 233, 237, 259, 289, 318, 363, 433, 592, 992, - 1607, 3062, 6149, 12206, 25522, 48368, 58223, 61918, 63640, 64584, - 64943, 65098, 65206, 65268, 65294, 65335, 65350, 65372, 65387, 65402, - 65413, 65420, 65428, 65435, 65439, 65450, 65454, 65468, 65472, 65476, - 65483, 65491, 65498, 65505, 65516, 65520, 65528, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Hi[2] = { - 0, 65535}; - -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Hi[35] = { - 0, 7, 19, 30, 41, 48, 63, 74, 82, 96, - 122, 152, 215, 330, 701, 2611, 10931, 48106, 61177, 64341, - 65112, 65238, 65309, 65338, 65364, 65379, 65401, 65427, 65453, 65465, - 65476, 65490, 65509, 65528, 65535}; - -const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrHi[4] = {WebRtcIsac_kQPitchLagCdf1Hi, WebRtcIsac_kQPitchLagCdf2Hi, WebRtcIsac_kQPitchLagCdf3Hi, WebRtcIsac_kQPitchLagCdf4Hi}; - -/* size of first cdf table */ -const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeHi[1] = {512}; - -/* index limits and ranges */ -const WebRtc_Word16 WebRtcIsac_kQindexLowerLimitLagHi[4] = { --552, -34, 0, -16}; - -const WebRtc_Word16 WebRtcIsac_kQindexUpperLimitLagHi[4] = { --80, 32, 0, 17}; - -/* initial index for arithmetic decoder */ -const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagHi[3] = { - 34, 1, 18}; - -/* mean values of pitch filter lags */ -const double WebRtcIsac_kQMeanLag2Hi[67] = { --17.07263295, -16.50000000, -15.83966081, -15.55613708, -14.96948007, -14.50000000, -14.00000000, -13.48377986, -13.00000000, -12.50000000, --11.93199636, -11.44530414, -11.04197641, -10.39910301, -10.15202337, -9.51322461, -8.93357741, -8.46456632, -8.10270672, -7.53751847, --6.98686404, -6.50000000, -6.08463150, -5.46872991, -5.00864717, -4.50163760, -4.01382410, -3.43856708, -2.96898001, -2.46554810, --1.96861004, -1.47106701, -0.97197237, -0.46561654, -0.00531409, 0.45767857, 0.96777907, 1.47507903, 1.97740425, 2.46695420, - 3.00695774, 3.47167185, 4.02712538, 4.49280007, 5.01087640, 5.48191963, 6.04916550, 6.51511058, 6.97297819, 7.46565499, - 8.01489405, 8.39912001, 8.91819757, 9.50000000, 10.11654065, 10.50000000, 11.03712583, 11.50000000, 12.00000000, 12.38964346, - 12.89466127, 13.43657881, 13.96013840, 14.46279912, 15.00000000, 15.39412269, 15.96662441}; - -const double WebRtcIsac_kQMeanLag3Hi[1] = { - 0.00000000}; - -const double WebRtcIsac_kQMeanLag4Hi[34] = { --7.98331221, -7.47988769, -7.03626557, -6.52708003, -6.06982173, -5.51856292, -5.05827033, -4.45909878, -3.99125864, -3.45308135, --3.02328139, -2.47297273, -1.94341995, -1.44699056, -0.93612243, -0.43012406, 0.01120357, 0.44054812, 0.93199883, 1.45669587, - 1.97218322, 2.50187419, 2.98748690, 3.49343202, 4.01660147, 4.50984306, 5.01402683, 5.58936797, 5.91787793, 6.59998900, - 6.85034315, 7.53503316, 7.87711194, 8.53631648}; - -const double WebRtcIsac_kQPitchLagStepsizeHi = 0.500000; - -/* transform matrix */ -const double WebRtcIsac_kTransform[4][4] = { -{-0.50000000, -0.50000000, -0.50000000, -0.50000000}, -{ 0.67082039, 0.22360680, -0.22360680, -0.67082039}, -{ 0.50000000, -0.50000000, -0.50000000, 0.50000000}, -{ 0.22360680, -0.67082039, 0.67082039, -0.22360680}}; - -/* transpose transform matrix */ -const double WebRtcIsac_kTransformTranspose[4][4] = { -{-0.50000000, 0.67082039, 0.50000000, 0.22360680}, -{-0.50000000, 0.22360680, -0.50000000, -0.67082039}, -{-0.50000000, -0.22360680, -0.50000000, 0.67082039}, -{-0.50000000, -0.67082039, 0.50000000, -0.22360680}}; - diff --git a/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.h b/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.h deleted file mode 100644 index 67b02e5e4..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/pitch_lag_tables.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * pitch_lag_tables.h - * - * This file contains tables for the pitch filter side-info in the entropy coder. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ - -#include "typedefs.h" -/* header file for coding tables for the pitch filter side-info in the entropy coder */ -/********************* Pitch Filter Lag Coefficient Tables ************************/ - -/* tables for use with small pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Lo[127]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Lo[20]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Lo[2]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Lo[10]; - -extern const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrLo[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeLo[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsac_kQIndexLowerLimitLagLo[4]; -extern const WebRtc_Word16 WebRtcIsac_kQIndexUpperLimitLagLo[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagLo[3]; - -/* mean values of pitch filter lags */ -extern const double WebRtcIsac_kQMeanLag2Lo[19]; -extern const double WebRtcIsac_kQMeanLag3Lo[1]; -extern const double WebRtcIsac_kQMeanLag4Lo[9]; - -extern const double WebRtcIsac_kQPitchLagStepsizeLo; - - -/* tables for use with medium pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Mid[255]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Mid[36]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Mid[2]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Mid[20]; - -extern const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrMid[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeMid[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsac_kQIndexLowerLimitLagMid[4]; -extern const WebRtc_Word16 WebRtcIsac_kQIndexUpperLimitLagMid[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagMid[3]; - -/* mean values of pitch filter lags */ -extern const double WebRtcIsac_kQMeanLag2Mid[35]; -extern const double WebRtcIsac_kQMeanLag3Mid[1]; -extern const double WebRtcIsac_kQMeanLag4Mid[19]; - -extern const double WebRtcIsac_kQPitchLagStepsizeMid; - - -/* tables for use with large pitch gain */ - -/* cdfs for quantized pitch lags */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf1Hi[511]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf2Hi[68]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf3Hi[2]; -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdf4Hi[35]; - -extern const WebRtc_UWord16 *WebRtcIsac_kQPitchLagCdfPtrHi[4]; - -/* size of first cdf table */ -extern const WebRtc_UWord16 WebRtcIsac_kQPitchLagCdfSizeHi[1]; - -/* index limits and ranges */ -extern const WebRtc_Word16 WebRtcIsac_kQindexLowerLimitLagHi[4]; -extern const WebRtc_Word16 WebRtcIsac_kQindexUpperLimitLagHi[4]; - -/* initial index for arithmetic decoder */ -extern const WebRtc_UWord16 WebRtcIsac_kQInitIndexLagHi[3]; - -/* mean values of pitch filter lags */ -extern const double WebRtcIsac_kQMeanLag2Hi[67]; -extern const double WebRtcIsac_kQMeanLag3Hi[1]; -extern const double WebRtcIsac_kQMeanLag4Hi[34]; - -extern const double WebRtcIsac_kQPitchLagStepsizeHi; - -/* transform matrix */ -extern const double WebRtcIsac_kTransform[4][4]; - -/* transpose transform matrix */ -extern const double WebRtcIsac_kTransformTranspose[4][4]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_PITCH_LAG_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/settings.h b/modules/audio_coding/codecs/iSAC/main/source/settings.h deleted file mode 100644 index b7aed7706..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/settings.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * settings.h - * - * Declaration of #defines used in the iSAC codec - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ - -/* sampling frequency (Hz) */ -#define FS 16000 - -/* number of samples per frame (either 320 (20ms), 480 (30ms) or 960 (60ms)) */ -#define INITIAL_FRAMESAMPLES 960 - - -#define MAXFFTSIZE 2048 -#define NFACTOR 11 - - - -/* do not modify the following; this will have to be modified if we have a 20ms framesize option */ -/*************************************************************************************************/ -/* miliseconds */ -#define FRAMESIZE 30 -/* number of samples per frame processed in the encoder, 480 */ -#define FRAMESAMPLES 480 /* ((FRAMESIZE*FS)/1000) */ -#define FRAMESAMPLES_HALF 240 -#define FRAMESAMPLES_QUARTER 120 -/*************************************************************************************************/ - - - -/* max number of samples per frame (= 60 ms frame) */ -#define MAX_FRAMESAMPLES 960 -#define MAX_SWBFRAMESAMPLES (MAX_FRAMESAMPLES * 2) -/* number of samples per 10ms frame */ -#define FRAMESAMPLES_10ms ((10*FS)/1000) -#define SWBFRAMESAMPLES_10ms (FRAMESAMPLES_10ms * 2) -/* number of samples in 30 ms frame */ -#define FRAMESAMPLES_30ms 480 -/* number of subframes */ -#define SUBFRAMES 6 -/* length of a subframe */ -#define UPDATE 80 -/* length of half a subframe (low/high band) */ -#define HALF_SUBFRAMELEN (UPDATE/2) -/* samples of look ahead (in a half-band, so actually half the samples of look ahead @ FS) */ -#define QLOOKAHEAD 24 /* 3 ms */ -/* order of AR model in spectral entropy coder */ -#define AR_ORDER 6 -/* order of LP model in spectral entropy coder */ -#define LP_ORDER 0 - -/* window length (masking analysis) */ -#define WINLEN 256 -/* order of low-band pole filter used to approximate masking curve */ -#define ORDERLO 12 -/* order of hi-band pole filter used to approximate masking curve */ -#define ORDERHI 6 - -#define UB_LPC_ORDER 4 -#define UB_LPC_VEC_PER_FRAME 2 -#define UB16_LPC_VEC_PER_FRAME 4 -#define UB_ACTIVE_SUBFRAMES 2 -#define UB_MAX_LPC_ORDER 6 -#define UB_INTERPOL_SEGMENTS 1 -#define UB16_INTERPOL_SEGMENTS 3 -#define LB_TOTAL_DELAY_SAMPLES 48 -enum ISACBandwidth {isac8kHz = 8, isac12kHz = 12, isac16kHz = 16}; -enum ISACBand{isacLowerBand = 0, isacUpperBand = 1}; -#define UB_LPC_GAIN_DIM SUBFRAMES -#define FB_STATE_SIZE_WORD32 6 - - -/* order for post_filter_bank */ -#define POSTQORDER 3 -/* order for pre-filterbank */ -#define QORDER 3 -/* another order */ -#define QORDER_ALL (POSTQORDER+QORDER-1) -/* for decimator */ -#define ALLPASSSECTIONS 2 - - -/* array size for byte stream in number of bytes. */ -#define STREAM_SIZE_MAX 600 /* The old maximum size still needed for the decoding */ -#define STREAM_SIZE_MAX_30 200 /* 200 bytes = 53.4 kbit/s @ 30 ms.framelength */ -#define STREAM_SIZE_MAX_60 400 /* 400 bytes = 53.4 kbit/s @ 60 ms.framelength */ - -/* storage size for bit counts */ -#define BIT_COUNTER_SIZE 30 -/* maximum order of any AR model or filter */ -#define MAX_AR_MODEL_ORDER 12//50 - - -/* For pitch analysis */ -#define PITCH_FRAME_LEN (FRAMESAMPLES_HALF) /* 30 ms */ -#define PITCH_MAX_LAG 140 /* 57 Hz */ -#define PITCH_MIN_LAG 20 /* 400 Hz */ -#define PITCH_MAX_GAIN 0.45 -#define PITCH_MAX_GAIN_06 0.27 /* PITCH_MAX_GAIN*0.6 */ -#define PITCH_MAX_GAIN_Q12 1843 -#define PITCH_LAG_SPAN2 (PITCH_MAX_LAG/2-PITCH_MIN_LAG/2+5) -#define PITCH_CORR_LEN2 60 /* 15 ms */ -#define PITCH_CORR_STEP2 (PITCH_FRAME_LEN/4) -#define PITCH_BW 11 /* half the band width of correlation surface */ -#define PITCH_SUBFRAMES 4 -#define PITCH_GRAN_PER_SUBFRAME 5 -#define PITCH_SUBFRAME_LEN (PITCH_FRAME_LEN/PITCH_SUBFRAMES) -#define PITCH_UPDATE (PITCH_SUBFRAME_LEN/PITCH_GRAN_PER_SUBFRAME) -/* maximum number of peaks to be examined in correlation surface */ -#define PITCH_MAX_NUM_PEAKS 10 -#define PITCH_PEAK_DECAY 0.85 -/* For weighting filter */ -#define PITCH_WLPCORDER 6 -#define PITCH_WLPCWINLEN PITCH_FRAME_LEN -#define PITCH_WLPCASYM 0.3 /* asymmetry parameter */ -#define PITCH_WLPCBUFLEN PITCH_WLPCWINLEN -/* For pitch filter */ -#define PITCH_BUFFSIZE (PITCH_MAX_LAG + 50) /* Extra 50 for fraction and LP filters */ -#define PITCH_INTBUFFSIZE (PITCH_FRAME_LEN+PITCH_BUFFSIZE) -/* Max rel. step for interpolation */ -#define PITCH_UPSTEP 1.5 -/* Max rel. step for interpolation */ -#define PITCH_DOWNSTEP 0.67 -#define PITCH_FRACS 8 -#define PITCH_FRACORDER 9 -#define PITCH_DAMPORDER 5 -#define PITCH_FILTDELAY 1.5f -/* stepsize for quantization of the pitch Gain */ -#define PITCH_GAIN_STEPSIZE 0.125 - - - -/* Order of high pass filter */ -#define HPORDER 2 - -/* some mathematical constants */ -#define LOG2EXP 1.44269504088896 /* log2(exp) */ -#define PI 3.14159265358979 - -/* Maximum number of iterations allowed to limit payload size */ -#define MAX_PAYLOAD_LIMIT_ITERATION 5 - -/* Redundant Coding */ -#define RCU_BOTTLENECK_BPS 16000 -#define RCU_TRANSCODING_SCALE 0.40f -#define RCU_TRANSCODING_SCALE_INVERSE 2.5f - -#define RCU_TRANSCODING_SCALE_UB 0.50f -#define RCU_TRANSCODING_SCALE_UB_INVERSE 2.0f - - -/* Define Error codes */ -/* 6000 General */ -#define ISAC_MEMORY_ALLOCATION_FAILED 6010 -#define ISAC_MODE_MISMATCH 6020 -#define ISAC_DISALLOWED_BOTTLENECK 6030 -#define ISAC_DISALLOWED_FRAME_LENGTH 6040 -#define ISAC_UNSUPPORTED_SAMPLING_FREQUENCY 6050 - -/* 6200 Bandwidth estimator */ -#define ISAC_RANGE_ERROR_BW_ESTIMATOR 6240 -/* 6400 Encoder */ -#define ISAC_ENCODER_NOT_INITIATED 6410 -#define ISAC_DISALLOWED_CODING_MODE 6420 -#define ISAC_DISALLOWED_FRAME_MODE_ENCODER 6430 -#define ISAC_DISALLOWED_BITSTREAM_LENGTH 6440 -#define ISAC_PAYLOAD_LARGER_THAN_LIMIT 6450 -#define ISAC_DISALLOWED_ENCODER_BANDWIDTH 6460 -/* 6600 Decoder */ -#define ISAC_DECODER_NOT_INITIATED 6610 -#define ISAC_EMPTY_PACKET 6620 -#define ISAC_DISALLOWED_FRAME_MODE_DECODER 6630 -#define ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH 6640 -#define ISAC_RANGE_ERROR_DECODE_BANDWIDTH 6650 -#define ISAC_RANGE_ERROR_DECODE_PITCH_GAIN 6660 -#define ISAC_RANGE_ERROR_DECODE_PITCH_LAG 6670 -#define ISAC_RANGE_ERROR_DECODE_LPC 6680 -#define ISAC_RANGE_ERROR_DECODE_SPECTRUM 6690 -#define ISAC_LENGTH_MISMATCH 6730 -#define ISAC_RANGE_ERROR_DECODE_BANDWITH 6740 -#define ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER 6750 -/* 6800 Call setup formats */ -#define ISAC_INCOMPATIBLE_FORMATS 6810 - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SETTINGS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.c b/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.c deleted file mode 100644 index 92b9c4d62..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "spectrum_ar_model_tables.h" -#include "settings.h" - -/********************* AR Coefficient Tables ************************/ -/* cdf for quantized reflection coefficient 1 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc1Cdf[12] = { - 0, 2, 4, 129, 7707, 57485, 65495, 65527, 65529, 65531, - 65533, 65535}; - -/* cdf for quantized reflection coefficient 2 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc2Cdf[12] = { - 0, 2, 4, 7, 531, 25298, 64525, 65526, 65529, 65531, - 65533, 65535}; - -/* cdf for quantized reflection coefficient 3 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc3Cdf[12] = { - 0, 2, 4, 6, 620, 22898, 64843, 65527, 65529, 65531, - 65533, 65535}; - -/* cdf for quantized reflection coefficient 4 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc4Cdf[12] = { - 0, 2, 4, 6, 35, 10034, 60733, 65506, 65529, 65531, - 65533, 65535}; - -/* cdf for quantized reflection coefficient 5 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc5Cdf[12] = { - 0, 2, 4, 6, 36, 7567, 56727, 65385, 65529, 65531, - 65533, 65535}; - -/* cdf for quantized reflection coefficient 6 */ -const WebRtc_UWord16 WebRtcIsac_kQArRc6Cdf[12] = { - 0, 2, 4, 6, 14, 6579, 57360, 65409, 65529, 65531, - 65533, 65535}; - -/* representation levels for quantized reflection coefficient 1 */ -const WebRtc_Word16 WebRtcIsac_kQArRc1Levels[11] = { - -32104, -29007, -23202, -15496, -9279, -2577, 5934, 17535, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 2 */ -const WebRtc_Word16 WebRtcIsac_kQArRc2Levels[11] = { - -32104, -29503, -23494, -15261, -7309, -1399, 6158, 16381, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 3 */ -const WebRtc_Word16 WebRtcIsac_kQArRc3Levels[11] = { --32104, -29503, -23157, -15186, -7347, -1359, 5829, 17535, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 4 */ -const WebRtc_Word16 WebRtcIsac_kQArRc4Levels[11] = { --32104, -29503, -24512, -15362, -6665, -342, 6596, 14585, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 5 */ -const WebRtc_Word16 WebRtcIsac_kQArRc5Levels[11] = { --32104, -29503, -24512, -15005, -6564, -106, 7123, 14920, 24512, 29503, 32104 -}; - -/* representation levels for quantized reflection coefficient 6 */ -const WebRtc_Word16 WebRtcIsac_kQArRc6Levels[11] = { --32104, -29503, -24512, -15096, -6656, -37, 7036, 14847, 24512, 29503, 32104 -}; - -/* quantization boundary levels for reflection coefficients */ -const WebRtc_Word16 WebRtcIsac_kQArBoundaryLevels[12] = { --32768, -31441, -27566, -21458, -13612, -4663, 4663, 13612, 21458, 27566, 31441, 32767 -}; - -/* initial index for AR reflection coefficient quantizer and cdf table search */ -const WebRtc_UWord16 WebRtcIsac_kQArRcInitIndex[6] = { - 5, 5, 5, 5, 5, 5}; - -/* pointers to AR cdf tables */ -const WebRtc_UWord16 *WebRtcIsac_kQArRcCdfPtr[AR_ORDER] = { - WebRtcIsac_kQArRc1Cdf, WebRtcIsac_kQArRc2Cdf, WebRtcIsac_kQArRc3Cdf, - WebRtcIsac_kQArRc4Cdf, WebRtcIsac_kQArRc5Cdf, WebRtcIsac_kQArRc6Cdf -}; - -/* pointers to AR representation levels tables */ -const WebRtc_Word16 *WebRtcIsac_kQArRcLevelsPtr[AR_ORDER] = { - WebRtcIsac_kQArRc1Levels, WebRtcIsac_kQArRc2Levels, WebRtcIsac_kQArRc3Levels, - WebRtcIsac_kQArRc4Levels, WebRtcIsac_kQArRc5Levels, WebRtcIsac_kQArRc6Levels -}; - - -/******************** GAIN Coefficient Tables ***********************/ -/* cdf for Gain coefficient */ -const WebRtc_UWord16 WebRtcIsac_kQGainCdf[19] = { - 0, 2, 4, 6, 8, 10, 12, 14, 16, 1172, - 11119, 29411, 51699, 64445, 65527, 65529, 65531, 65533, 65535}; - -/* representation levels for quantized squared Gain coefficient */ -const WebRtc_Word32 WebRtcIsac_kQGain2Levels[18] = { -// 17, 28, 46, 76, 128, 215, 364, 709, 1268, 1960, 3405, 6078, 11286, 17827, 51918, 134498, 487432, 2048000}; - 128, 128, 128, 128, 128, 215, 364, 709, 1268, 1960, 3405, 6078, 11286, 17827, 51918, 134498, 487432, 2048000}; -/* quantization boundary levels for squared Gain coefficient */ -const WebRtc_Word32 WebRtcIsac_kQGain2BoundaryLevels[19] = { -0, 21, 35, 59, 99, 166, 280, 475, 815, 1414, 2495, 4505, 8397, 16405, 34431, 81359, 240497, 921600, 0x7FFFFFFF}; - -/* pointers to Gain cdf table */ -const WebRtc_UWord16 *WebRtcIsac_kQGainCdf_ptr[1] = {WebRtcIsac_kQGainCdf}; - -/* Gain initial index for gain quantizer and cdf table search */ -const WebRtc_UWord16 WebRtcIsac_kQGainInitIndex[1] = {11}; - -/************************* Cosine Tables ****************************/ -/* Cosine table */ -const WebRtc_Word16 WebRtcIsac_kCos[6][60] = { -{512, 512, 511, 510, 508, 507, 505, 502, 499, 496, 493, 489, 485, 480, 476, 470, 465, 459, 453, 447, -440, 433, 426, 418, 410, 402, 394, 385, 376, 367, 357, 348, 338, 327, 317, 306, 295, 284, 273, 262, -250, 238, 226, 214, 202, 190, 177, 165, 152, 139, 126, 113, 100, 87, 73, 60, 47, 33, 20, 7}, -{512, 510, 508, 503, 498, 491, 483, 473, 462, 450, 437, 422, 406, 389, 371, 352, 333, 312, 290, 268, -244, 220, 196, 171, 145, 120, 93, 67, 40, 13, -13, -40, -67, -93, -120, -145, -171, -196, -220, -244, --268, -290, -312, -333, -352, -371, -389, -406, -422, -437, -450, -462, -473, -483, -491, -498, -503, -508, -510, -512}, -{512, 508, 502, 493, 480, 465, 447, 426, 402, 376, 348, 317, 284, 250, 214, 177, 139, 100, 60, 20, --20, -60, -100, -139, -177, -214, -250, -284, -317, -348, -376, -402, -426, -447, -465, -480, -493, -502, -508, -512, --512, -508, -502, -493, -480, -465, -447, -426, -402, -376, -348, -317, -284, -250, -214, -177, -139, -100, -60, -20}, -{511, 506, 495, 478, 456, 429, 398, 362, 322, 279, 232, 183, 133, 80, 27, -27, -80, -133, -183, -232, --279, -322, -362, -398, -429, -456, -478, -495, -506, -511, -511, -506, -495, -478, -456, -429, -398, -362, -322, -279, --232, -183, -133, -80, -27, 27, 80, 133, 183, 232, 279, 322, 362, 398, 429, 456, 478, 495, 506, 511}, -{511, 502, 485, 459, 426, 385, 338, 284, 226, 165, 100, 33, -33, -100, -165, -226, -284, -338, -385, -426, --459, -485, -502, -511, -511, -502, -485, -459, -426, -385, -338, -284, -226, -165, -100, -33, 33, 100, 165, 226, -284, 338, 385, 426, 459, 485, 502, 511, 511, 502, 485, 459, 426, 385, 338, 284, 226, 165, 100, 33}, -{510, 498, 473, 437, 389, 333, 268, 196, 120, 40, -40, -120, -196, -268, -333, -389, -437, -473, -498, -510, --510, -498, -473, -437, -389, -333, -268, -196, -120, -40, 40, 120, 196, 268, 333, 389, 437, 473, 498, 510, -510, 498, 473, 437, 389, 333, 268, 196, 120, 40, -40, -120, -196, -268, -333, -389, -437, -473, -498, -510} -}; diff --git a/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.h b/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.h deleted file mode 100644 index 159245bd9..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/spectrum_ar_model_tables.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * spectrum_ar_model_tables.h - * - * This file contains definitions of tables with AR coefficients, - * Gain coefficients and cosine tables. - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ - -#include "structs.h" - -/********************* AR Coefficient Tables ************************/ -/* cdf for quantized reflection coefficient 1 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc1Cdf[12]; - -/* cdf for quantized reflection coefficient 2 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc2Cdf[12]; - -/* cdf for quantized reflection coefficient 3 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc3Cdf[12]; - -/* cdf for quantized reflection coefficient 4 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc4Cdf[12]; - -/* cdf for quantized reflection coefficient 5 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc5Cdf[12]; - -/* cdf for quantized reflection coefficient 6 */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRc6Cdf[12]; - -/* quantization boundary levels for reflection coefficients */ -extern const WebRtc_Word16 WebRtcIsac_kQArBoundaryLevels[12]; - -/* initial indices for AR reflection coefficient quantizer and cdf table search */ -extern const WebRtc_UWord16 WebRtcIsac_kQArRcInitIndex[AR_ORDER]; - -/* pointers to AR cdf tables */ -extern const WebRtc_UWord16 *WebRtcIsac_kQArRcCdfPtr[AR_ORDER]; - -/* pointers to AR representation levels tables */ -extern const WebRtc_Word16 *WebRtcIsac_kQArRcLevelsPtr[AR_ORDER]; - - -/******************** GAIN Coefficient Tables ***********************/ -/* cdf for Gain coefficient */ -extern const WebRtc_UWord16 WebRtcIsac_kQGainCdf[19]; - -/* representation levels for quantized Gain coefficient */ -extern const WebRtc_Word32 WebRtcIsac_kQGain2Levels[18]; - -/* squared quantization boundary levels for Gain coefficient */ -extern const WebRtc_Word32 WebRtcIsac_kQGain2BoundaryLevels[19]; - -/* pointer to Gain cdf table */ -extern const WebRtc_UWord16 *WebRtcIsac_kQGainCdf_ptr[1]; - -/* Gain initial index for gain quantizer and cdf table search */ -extern const WebRtc_UWord16 WebRtcIsac_kQGainInitIndex[1]; - -/************************* Cosine Tables ****************************/ -/* Cosine table */ -extern const WebRtc_Word16 WebRtcIsac_kCos[6][60]; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_SPECTRUM_AR_MODEL_TABLES_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/structs.h b/modules/audio_coding/codecs/iSAC/main/source/structs.h deleted file mode 100644 index 7523ad6e4..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/structs.h +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * structs.h - * - * This header file contains all the structs used in the ISAC codec - * - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ - - -#include "typedefs.h" -#include "settings.h" -#include "isac.h" - -typedef struct Bitstreamstruct { - - WebRtc_UWord8 stream[STREAM_SIZE_MAX]; - WebRtc_UWord32 W_upper; - WebRtc_UWord32 streamval; - WebRtc_UWord32 stream_index; - -} Bitstr; - -typedef struct { - - double DataBufferLo[WINLEN]; - double DataBufferHi[WINLEN]; - - double CorrBufLo[ORDERLO+1]; - double CorrBufHi[ORDERHI+1]; - - float PreStateLoF[ORDERLO+1]; - float PreStateLoG[ORDERLO+1]; - float PreStateHiF[ORDERHI+1]; - float PreStateHiG[ORDERHI+1]; - float PostStateLoF[ORDERLO+1]; - float PostStateLoG[ORDERLO+1]; - float PostStateHiF[ORDERHI+1]; - float PostStateHiG[ORDERHI+1]; - - double OldEnergy; - -} MaskFiltstr; - - -typedef struct { - - //state vectors for each of the two analysis filters - double INSTAT1[2*(QORDER-1)]; - double INSTAT2[2*(QORDER-1)]; - double INSTATLA1[2*(QORDER-1)]; - double INSTATLA2[2*(QORDER-1)]; - double INLABUF1[QLOOKAHEAD]; - double INLABUF2[QLOOKAHEAD]; - - float INSTAT1_float[2*(QORDER-1)]; - float INSTAT2_float[2*(QORDER-1)]; - float INSTATLA1_float[2*(QORDER-1)]; - float INSTATLA2_float[2*(QORDER-1)]; - float INLABUF1_float[QLOOKAHEAD]; - float INLABUF2_float[QLOOKAHEAD]; - - /* High pass filter */ - double HPstates[HPORDER]; - float HPstates_float[HPORDER]; - -} PreFiltBankstr; - - -typedef struct { - - //state vectors for each of the two analysis filters - double STATE_0_LOWER[2*POSTQORDER]; - double STATE_0_UPPER[2*POSTQORDER]; - - /* High pass filter */ - double HPstates1[HPORDER]; - double HPstates2[HPORDER]; - - float STATE_0_LOWER_float[2*POSTQORDER]; - float STATE_0_UPPER_float[2*POSTQORDER]; - - float HPstates1_float[HPORDER]; - float HPstates2_float[HPORDER]; - -} PostFiltBankstr; - -typedef struct { - - //data buffer for pitch filter - double ubuf[PITCH_BUFFSIZE]; - - //low pass state vector - double ystate[PITCH_DAMPORDER]; - - //old lag and gain - double oldlagp[1]; - double oldgainp[1]; - -} PitchFiltstr; - -typedef struct { - - //data buffer - double buffer[PITCH_WLPCBUFLEN]; - - //state vectors - double istate[PITCH_WLPCORDER]; - double weostate[PITCH_WLPCORDER]; - double whostate[PITCH_WLPCORDER]; - - //LPC window -> should be a global array because constant - double window[PITCH_WLPCWINLEN]; - -} WeightFiltstr; - -typedef struct { - - //for inital estimator - double dec_buffer[PITCH_CORR_LEN2 + PITCH_CORR_STEP2 + - PITCH_MAX_LAG/2 - PITCH_FRAME_LEN/2+2]; - double decimator_state[2*ALLPASSSECTIONS+1]; - double hp_state[2]; - - double whitened_buf[QLOOKAHEAD]; - - double inbuf[QLOOKAHEAD]; - - PitchFiltstr PFstr_wght; - PitchFiltstr PFstr; - WeightFiltstr Wghtstr; - -} PitchAnalysisStruct; - - - -/* Have instance of struct together with other iSAC structs */ -typedef struct { - - /* Previous frame length (in ms) */ - WebRtc_Word32 prev_frame_length; - - /* Previous RTP timestamp from received - packet (in samples relative beginning) */ - WebRtc_Word32 prev_rec_rtp_number; - - /* Send timestamp for previous packet (in ms using timeGetTime()) */ - WebRtc_UWord32 prev_rec_send_ts; - - /* Arrival time for previous packet (in ms using timeGetTime()) */ - WebRtc_UWord32 prev_rec_arr_ts; - - /* rate of previous packet, derived from RTP timestamps (in bits/s) */ - float prev_rec_rtp_rate; - - /* Time sinse the last update of the BN estimate (in ms) */ - WebRtc_UWord32 last_update_ts; - - /* Time sinse the last reduction (in ms) */ - WebRtc_UWord32 last_reduction_ts; - - /* How many times the estimate was update in the beginning */ - WebRtc_Word32 count_tot_updates_rec; - - /* The estimated bottle neck rate from there to here (in bits/s) */ - WebRtc_Word32 rec_bw; - float rec_bw_inv; - float rec_bw_avg; - float rec_bw_avg_Q; - - /* The estimated mean absolute jitter value, - as seen on this side (in ms) */ - float rec_jitter; - float rec_jitter_short_term; - float rec_jitter_short_term_abs; - float rec_max_delay; - float rec_max_delay_avg_Q; - - /* (assumed) bitrate for headers (bps) */ - float rec_header_rate; - - /* The estimated bottle neck rate from here to there (in bits/s) */ - float send_bw_avg; - - /* The estimated mean absolute jitter value, as seen on - the other siee (in ms) */ - float send_max_delay_avg; - - // number of packets received since last update - int num_pkts_rec; - - int num_consec_rec_pkts_over_30k; - - // flag for marking that a high speed network has been - // detected downstream - int hsn_detect_rec; - - int num_consec_snt_pkts_over_30k; - - // flag for marking that a high speed network has - // been detected upstream - int hsn_detect_snd; - - WebRtc_UWord32 start_wait_period; - - int in_wait_period; - - int change_to_WB; - - WebRtc_UWord32 senderTimestamp; - WebRtc_UWord32 receiverTimestamp; - //enum IsacSamplingRate incomingStreamSampFreq; - WebRtc_UWord16 numConsecLatePkts; - float consecLatency; - WebRtc_Word16 inWaitLatePkts; -} BwEstimatorstr; - - -typedef struct { - - /* boolean, flags if previous packet exceeded B.N. */ - int PrevExceed; - /* ms */ - int ExceedAgo; - /* packets left to send in current burst */ - int BurstCounter; - /* packets */ - int InitCounter; - /* ms remaining in buffer when next packet will be sent */ - double StillBuffered; - -} RateModel; - - -typedef struct { - - unsigned int SpaceAlloced; - unsigned int MaxPermAlloced; - double Tmp0[MAXFFTSIZE]; - double Tmp1[MAXFFTSIZE]; - double Tmp2[MAXFFTSIZE]; - double Tmp3[MAXFFTSIZE]; - int Perm[MAXFFTSIZE]; - int factor [NFACTOR]; - -} FFTstr; - - -/* The following strutc is used to store data from encoding, to make it - fast and easy to construct a new bitstream with a different Bandwidth - estimate. All values (except framelength and minBytes) is double size to - handle 60 ms of data. -*/ -typedef struct { - - /* Used to keep track of if it is first or second part of 60 msec packet */ - int startIdx; - - /* Frame length in samples */ - WebRtc_Word16 framelength; - - /* Pitch Gain */ - int pitchGain_index[2]; - - /* Pitch Lag */ - double meanGain[2]; - int pitchIndex[PITCH_SUBFRAMES*2]; - - /* LPC */ - int LPCmodel[2]; - int LPCindex_s[108*2]; /* KLT_ORDER_SHAPE = 108 */ - int LPCindex_g[12*2]; /* KLT_ORDER_GAIN = 12 */ - double LPCcoeffs_lo[(ORDERLO+1)*SUBFRAMES*2]; - double LPCcoeffs_hi[(ORDERHI+1)*SUBFRAMES*2]; - - /* Encode Spec */ - WebRtc_Word16 fre[FRAMESAMPLES]; - WebRtc_Word16 fim[FRAMESAMPLES]; - WebRtc_Word16 AvgPitchGain[2]; - - /* Used in adaptive mode only */ - int minBytes; - -} ISAC_SaveEncData_t; - - -typedef struct { - - int indexLPCShape[UB_LPC_ORDER * UB16_LPC_VEC_PER_FRAME]; - double lpcGain[SUBFRAMES<<1]; - int lpcGainIndex[SUBFRAMES<<1]; - - Bitstr bitStreamObj; - - WebRtc_Word16 realFFT[FRAMESAMPLES_HALF]; - WebRtc_Word16 imagFFT[FRAMESAMPLES_HALF]; -} ISACUBSaveEncDataStruct; - - - -typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PreFiltBankstr prefiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; - PitchAnalysisStruct pitchanalysisstr_obj; - FFTstr fftstr_obj; - ISAC_SaveEncData_t SaveEnc_obj; - - int buffer_index; - WebRtc_Word16 current_framesamples; - - float data_buffer_float[FRAMESAMPLES_30ms]; - - int frame_nb; - double bottleneck; - WebRtc_Word16 new_framelength; - double s2nr; - - /* Maximum allowed number of bits for a 30 msec packet */ - WebRtc_Word16 payloadLimitBytes30; - /* Maximum allowed number of bits for a 30 msec packet */ - WebRtc_Word16 payloadLimitBytes60; - /* Maximum allowed number of bits for both 30 and 60 msec packet */ - WebRtc_Word16 maxPayloadBytes; - /* Maximum allowed rate in bytes per 30 msec packet */ - WebRtc_Word16 maxRateInBytes; - - /*--- - If set to 1 iSAC will not addapt the frame-size, if used in - channel-adaptive mode. The initial value will be used for all rates. - ---*/ - WebRtc_Word16 enforceFrameSize; - - /*----- - This records the BWE index the encoder injected into the bit-stream. - It will be used in RCU. The same BWE index of main paylaod will be in - the redundant payload. We can not retrive it from BWE because it is - a recursive procedure (WebRtcIsac_GetDownlinkBwJitIndexImpl) and has to be - called only once per each encode. - -----*/ - WebRtc_Word16 lastBWIdx; -} ISACLBEncStruct; - -typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PreFiltBankstr prefiltbankstr_obj; - FFTstr fftstr_obj; - ISACUBSaveEncDataStruct SaveEnc_obj; - - int buffer_index; - float data_buffer_float[MAX_FRAMESAMPLES + - LB_TOTAL_DELAY_SAMPLES]; - double bottleneck; - /* Maximum allowed number of bits for a 30 msec packet */ - //WebRtc_Word16 payloadLimitBytes30; - /* Maximum allowed number of bits for both 30 and 60 msec packet */ - //WebRtc_Word16 maxPayloadBytes; - WebRtc_Word16 maxPayloadSizeBytes; - - double lastLPCVec[UB_LPC_ORDER]; - WebRtc_Word16 numBytesUsed; - WebRtc_Word16 lastJitterInfo; -} ISACUBEncStruct; - - - -typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PostFiltBankstr postfiltbankstr_obj; - PitchFiltstr pitchfiltstr_obj; - FFTstr fftstr_obj; - -} ISACLBDecStruct; - -typedef struct { - - Bitstr bitstr_obj; - MaskFiltstr maskfiltstr_obj; - PostFiltBankstr postfiltbankstr_obj; - FFTstr fftstr_obj; - -} ISACUBDecStruct; - - - -typedef struct { - - ISACLBEncStruct ISACencLB_obj; - ISACLBDecStruct ISACdecLB_obj; -} ISACLBStruct; - - -typedef struct { - - ISACUBEncStruct ISACencUB_obj; - ISACUBDecStruct ISACdecUB_obj; -} ISACUBStruct; - -/* - This struct is used to take a snapshot of the entropy coder and LPC gains - right before encoding LPC gains. This allows us to go back to that state - if we like to limit the payload size. -*/ -typedef struct { - /* 6 lower-band & 6 upper-band */ - double loFiltGain[SUBFRAMES]; - double hiFiltGain[SUBFRAMES]; - /* Upper boundary of interval W */ - WebRtc_UWord32 W_upper; - WebRtc_UWord32 streamval; - /* Index to the current position in bytestream */ - WebRtc_UWord32 stream_index; - WebRtc_UWord8 stream[3]; -} transcode_obj; - - -typedef struct { - // lower-band codec instance - ISACLBStruct instLB; - // upper-band codec instance - ISACUBStruct instUB; - - // Bandwidth Estimator and model for the rate. - BwEstimatorstr bwestimator_obj; - RateModel rate_data_obj; - double MaxDelay; - - /* 0 = adaptive; 1 = instantaneous */ - WebRtc_Word16 codingMode; - - // overall bottleneck of the codec - WebRtc_Word32 bottleneck; - - // QMF Filter state - WebRtc_Word32 analysisFBState1[FB_STATE_SIZE_WORD32]; - WebRtc_Word32 analysisFBState2[FB_STATE_SIZE_WORD32]; - WebRtc_Word32 synthesisFBState1[FB_STATE_SIZE_WORD32]; - WebRtc_Word32 synthesisFBState2[FB_STATE_SIZE_WORD32]; - - // Error Code - WebRtc_Word16 errorCode; - - // bandwidth of the encoded audio 8, 12 or 16 kHz - enum ISACBandwidth bandwidthKHz; - // Sampling rate of audio, encoder and decode, 8 or 16 kHz - enum IsacSamplingRate encoderSamplingRateKHz; - enum IsacSamplingRate decoderSamplingRateKHz; - // Flag to keep track of initializations, lower & upper-band - // encoder and decoder. - WebRtc_Word16 initFlag; - - // Flag to to indicate signal bandwidth switch - WebRtc_Word16 resetFlag_8kHz; - - // Maximum allowed rate, measured in Bytes per 30 ms. - WebRtc_Word16 maxRateBytesPer30Ms; - // Maximum allowed payload-size, measured in Bytes. - WebRtc_Word16 maxPayloadSizeBytes; -} ISACMainStruct; - -#endif /* WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_SOURCE_STRUCTS_H_ */ diff --git a/modules/audio_coding/codecs/iSAC/main/source/transform.c b/modules/audio_coding/codecs/iSAC/main/source/transform.c deleted file mode 100644 index 3cce9f460..000000000 --- a/modules/audio_coding/codecs/iSAC/main/source/transform.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "settings.h" -#include "fft.h" -#include "codec.h" -#include - -static double costab1[FRAMESAMPLES_HALF]; -static double sintab1[FRAMESAMPLES_HALF]; -static double costab2[FRAMESAMPLES_QUARTER]; -static double sintab2[FRAMESAMPLES_QUARTER]; - -#if defined(WEBRTC_LINUX) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(WEBRTC_MAC) -extern long int lrint(double x); /* Note! This declaration is missing in math.h, that is why it is declared as extern*/ -#define WebRtcIsac_lrint lrint -#elif defined(X64) -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} -#elif defined(WEBRTC_TARGET_PC) - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - - WebRtc_Word32 intgr; - - _asm - { - fld flt - fistp intgr - } ; - - return intgr ; -} -#else// Do a slow but correct implementation of lrint - -static __inline WebRtc_Word32 WebRtcIsac_lrint(double flt) { - WebRtc_Word32 intgr; - intgr = (WebRtc_Word32)floor(flt+.499999999999); - return intgr ; -} -#endif - -void WebRtcIsac_InitTransform() -{ - int k; - double fact, phase; - - fact = PI / (FRAMESAMPLES_HALF); - phase = 0.0; - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - costab1[k] = cos(phase); - sintab1[k] = sin(phase); - phase += fact; - } - - fact = PI * ((double) (FRAMESAMPLES_HALF - 1)) / ((double) FRAMESAMPLES_HALF); - phase = 0.5 * fact; - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) { - costab2[k] = cos(phase); - sintab2[k] = sin(phase); - phase += fact; - } -} - - -void WebRtcIsac_Time2Spec(double *inre1, - double *inre2, - WebRtc_Word16 *outreQ7, - WebRtc_Word16 *outimQ7, - FFTstr *fftstr_obj) -{ - - int k; - int dims[1]; - double tmp1r, tmp1i, xr, xi, yr, yi, fact; - double tmpre[FRAMESAMPLES_HALF], tmpim[FRAMESAMPLES_HALF]; - - - dims[0] = FRAMESAMPLES_HALF; - - - /* Multiply with complex exponentials and combine into one complex vector */ - fact = 0.5 / sqrt(FRAMESAMPLES_HALF); - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - tmp1r = costab1[k]; - tmp1i = sintab1[k]; - tmpre[k] = (inre1[k] * tmp1r + inre2[k] * tmp1i) * fact; - tmpim[k] = (inre2[k] * tmp1r - inre1[k] * tmp1i) * fact; - } - - - /* Get DFT */ - WebRtcIsac_Fftns(1, dims, tmpre, tmpim, -1, 1.0, fftstr_obj); - - /* Use symmetry to separate into two complex vectors and center frames in time around zero */ - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) { - xr = tmpre[k] + tmpre[FRAMESAMPLES_HALF - 1 - k]; - yi = -tmpre[k] + tmpre[FRAMESAMPLES_HALF - 1 - k]; - xi = tmpim[k] - tmpim[FRAMESAMPLES_HALF - 1 - k]; - yr = tmpim[k] + tmpim[FRAMESAMPLES_HALF - 1 - k]; - - tmp1r = costab2[k]; - tmp1i = sintab2[k]; - outreQ7[k] = (WebRtc_Word16)WebRtcIsac_lrint((xr * tmp1r - xi * tmp1i) * 128.0); - outimQ7[k] = (WebRtc_Word16)WebRtcIsac_lrint((xr * tmp1i + xi * tmp1r) * 128.0); - outreQ7[FRAMESAMPLES_HALF - 1 - k] = (WebRtc_Word16)WebRtcIsac_lrint((-yr * tmp1i - yi * tmp1r) * 128.0); - outimQ7[FRAMESAMPLES_HALF - 1 - k] = (WebRtc_Word16)WebRtcIsac_lrint((-yr * tmp1r + yi * tmp1i) * 128.0); - } -} - - -void WebRtcIsac_Spec2time(double *inre, double *inim, double *outre1, double *outre2, FFTstr *fftstr_obj) -{ - - int k; - double tmp1r, tmp1i, xr, xi, yr, yi, fact; - - int dims; - - dims = FRAMESAMPLES_HALF; - - for (k = 0; k < FRAMESAMPLES_QUARTER; k++) { - /* Move zero in time to beginning of frames */ - tmp1r = costab2[k]; - tmp1i = sintab2[k]; - xr = inre[k] * tmp1r + inim[k] * tmp1i; - xi = inim[k] * tmp1r - inre[k] * tmp1i; - yr = -inim[FRAMESAMPLES_HALF - 1 - k] * tmp1r - inre[FRAMESAMPLES_HALF - 1 - k] * tmp1i; - yi = -inre[FRAMESAMPLES_HALF - 1 - k] * tmp1r + inim[FRAMESAMPLES_HALF - 1 - k] * tmp1i; - - /* Combine into one vector, z = x + j * y */ - outre1[k] = xr - yi; - outre1[FRAMESAMPLES_HALF - 1 - k] = xr + yi; - outre2[k] = xi + yr; - outre2[FRAMESAMPLES_HALF - 1 - k] = -xi + yr; - } - - - /* Get IDFT */ - WebRtcIsac_Fftns(1, &dims, outre1, outre2, 1, FRAMESAMPLES_HALF, fftstr_obj); - - - /* Demodulate and separate */ - fact = sqrt(FRAMESAMPLES_HALF); - for (k = 0; k < FRAMESAMPLES_HALF; k++) { - tmp1r = costab1[k]; - tmp1i = sintab1[k]; - xr = (outre1[k] * tmp1r - outre2[k] * tmp1i) * fact; - outre2[k] = (outre2[k] * tmp1r + outre1[k] * tmp1i) * fact; - outre1[k] = xr; - } -} diff --git a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACLongtest.txt b/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACLongtest.txt deleted file mode 100644 index 3f05224a0..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACLongtest.txt +++ /dev/null @@ -1,433 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - - - -if [ "$1" = "x64" ] || [ "$2" = "x64" ] || [ "$#" -eq 0 ] - then - PLATFORM=_X64 - ISAC=../x64/Release/ReleaseTest-API_2005.exe -elif [ "$1" = "LINUX" ] || [ "$2" = "LINUX" ] - then - PLATFORM=_linux - ISAC=../ReleaseTest-API/isacswtest -else - PLATFORM=_2005 - ISAC=../win32/Release/ReleaseTest-API_2005.exe -fi - -if [ "$#" -eq 0 ] || [ "$1" = "all" ] || [ "$1" = "wb" ] - then - LOGFILE=logNormal"$PLATFORM".txt - echo "START ISAC WB TEST" > $LOGFILE - echo >> $LOGFILE - - INFILES=$(cat InputFiles.txt) - SUBSET=$(cat InputFilesFew.txt) - CHANNELFILES=$(cat ChannelFiles.txt) - CHANNELLIST=($(cat ChannelFiles.txt)) - INDIR=../data/orig - OUTDIR=../dataqa"$PLATFORM" - mkdir -p $OUTDIR - rm -f $OUTDIR/* - - idx=0 - RATE=10000 - FRAMESIZE=30 - - - for file in $INFILES # loop over all input files - do - - echo "Input file: " $file - echo "-----------------------------------" - echo "Instantaneous with RATE " $RATE ", and Frame-size " $FRAMESIZE - $ISAC -I -B $RATE -FL $FRAMESIZE -FS 16 $INDIR/"$file" $OUTDIR/i_"$FRAMESIZE"_"$RATE"_"$file" >> $LOGFILE - echo - - name="${CHANNELLIST[$idx]}" - echo "Adaptive with channel file: " $name - - $ISAC -B $INDIR/${CHANNELLIST[$idx]} -FS 16 $INDIR/"$file" $OUTDIR/a_${name%.*}_"$file" >> $LOGFILE - - echo - echo - -# alternate between 30 & 60 ms. - if [ $FRAMESIZE -eq 30 ] - then - FRAMESIZE=60 - else - FRAMESIZE=30 - fi - -# rate between 10000 to 32000 bits/sec - if [ $RATE -le 30000 ] - then - let "RATE=RATE+2000" - else - let "RATE=10000" - fi - -# there are only three channel file - if [ $idx -ge 2 ]; then - idx=0 - else - let "idx=idx+1" - fi - - done - - idx=0 - -# loop over the subset of input files - for file in $SUBSET - do - - if [ $idx -eq 0 ]; then - $ISAC -B $INDIR/${CHANNELLIST[0]} -FL 30 -FIXED_FL -FS 16 $INDIR/"$file" $OUTDIR/a30_"$file" >> $LOGFILE - idx=1 - else - $ISAC -B $INDIR/${CHANNELLIST[0]} -FL 60 -FIXED_FL -FS 16 $INDIR/"$file" $OUTDIR/a60_"$file" >> $LOGFILE - idx=0 - fi - done - - $ISAC -B $INDIR/${CHANNELLIST[0]} -INITRATE 25000 -FL 30 -FS 16 $INDIR/"$file" $OUTDIR/a60_Init25kbps_"$file" >> $LOGFILE - - echo - echo WIDEBAND DONE! - echo - echo -fi - -if [ "$#" -eq 0 ] || [ "$1" = "all" ] || [ "$1" = "swb" ] - then - - LOGFILE=logNormal_SWB"$PLATFORM".txt - echo "START ISAC SWB TEST" > $LOGFILE - echo >> $LOGFILE - - echo STARTING TO TEST SUPER-WIDEBAND - - INFILES=$(cat InputFilesSWB.txt) - INDIR=../data/origswb - OUTDIR=../dataqaswb"$PLATFORM" - mkdir -p $OUTDIR - rm -f $OUTDIR/* - - for file in $INFILES - do - echo - echo "Input file: " $file - echo "--------------------------------" - for RATE in 12000 20000 32000 38000 45000 50000 56000 - do - - echo "Rate " $RATE - $ISAC -I -B $RATE -FL 30 -FS 32 $INDIR/"$file" $OUTDIR/swb_"$RATE"_"$file" >> $LOGFILE - echo - - done - - done -fi - -if [ "$#" -eq 0 ] || [ "$1" = "all" ] || [ "$1" = "API" ] - then - - LOGFILE_API=logNormal_API"$PLATFORM".txt - echo - echo - echo "START ISAC API TEST" > $LOGFILE_API - echo >> $LOGFILE_API - idx=1 - echo " Test Enforcement of frame-size" - echo "========================================================================================" - mkdir -p ../FrameSizeLim"$PLATFORM" - rm -f ../FrameSizeLim"$PLATFORM"/* - echo - echo "-- No enforcement; BN 10000" - echo - $ISAC -B 10000 -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../FrameSizeLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Now Enforce 30 ms frame size with the same bottleneck" - echo "There should not be any 60 ms frame" - echo - $ISAC -B 10000 -FL 30 -FIXED_FL -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../FrameSizeLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- No enforcement; BN 32000" - echo - $ISAC -B 32000 -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../FrameSizeLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Now Enforce 60 ms frame size with the same bottleneck" - echo "There should not be any 30 ms frame" - echo - $ISAC -B 32000 -FL 60 -FIXED_FL -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../FrameSizeLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo - echo - echo - - echo " Test Limiting of Payload Size and Rate" - echo "========================================================================================" - mkdir -p ../PayloadLim"$PLATFORM" - rm -f ../PayloadLim"$PLATFORM"/* - echo - echo - echo "-- No Limit, frame-size 60 ms, WIDEBAND" - echo - $ISAC -I -B 32000 -FL 60 -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Payload-size limit of 250, frame-size 60 ms, WIDEBAND" - echo - $ISAC -I -B 32000 -FL 60 -FS 16 -MAX 250 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Rate limit of 33 kbps for 60 ms frame-size" - echo - $ISAC -I -B 32000 -FL 60 -FS 16 -MAXRATE 33000 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - - echo "-- No Limit, frame-size 30 ms, WIDEBAND" - echo - $ISAC -I -B 32000 -FL 30 -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Payload-size limit of 130, frame-size 30 ms, WIDEBAND" - echo - $ISAC -I -B 32000 -FL 30 -FS 16 -MAX 130 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Rate limit of 33 kbps for 30 ms frame-size, wideband" - echo - $ISAC -I -B 32000 -FL 30 -FS 16 -MAXRATE 33000 ../data/orig/speech_and_misc_WB.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - - echo "-- No limit for 32 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 32000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Payload limit of 130 bytes for 32 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 32000 -FL 30 -FS 32 -MAX 130 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - - echo "-- No limit, Rate 45 kbps, 30 ms, SUPER-WIDEBAND, 12 kHz" - echo - $ISAC -I -B 45000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Rate limit of 46 kbps for 42 kbps, 30 ms, SUPER-WIDEBAND, 12 kHz" - echo - $ISAC -I -B 45000 -FL 30 -FS 32 -MAXRATE 46000 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Payload limit of 170 bytes for 45 kbps, 30 ms, SUPER-WIDEBAND, 12 kHz" - echo - $ISAC -I -B 45000 -FL 30 -FS 32 -MAX 170 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - - echo "-- No limit for 56 kbps, 30 ms, SUPER-WIDEBAND, 16 kHz" - echo - $ISAC -I -B 56000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Payload limit of 200 bytes for 56 kbps 30 ms, SUPER-WIDEBAND, 16 kHz" - echo - $ISAC -I -B 56000 -FL 30 -FS 32 -MAX 200 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - echo "-- Rate limit of 57 kbps for 56 kbps 30 ms, SUPER-WIDEBAND, 16 kHz" - echo - $ISAC -I -B 56000 -FL 30 -FS 32 -MAXRATE 57000 ../data/origswb/jstest_32.pcm \ - ../PayloadLim"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - echo - echo - echo - - echo " Test Trans-Coding" - echo "========================================================================================" - mkdir -p ../Transcoding"$PLATFORM" - rm -f ../Transcoding"$PLATFORM"/* - echo - echo - echo "-- 20 kbps, 30 ms, WIDEBAND" - echo - $ISAC -I -B 20000 -FL 30 -FS 16 ../data/orig/speech_and_misc_WB.pcm \ - ../Transcoding"$PLATFORM"/APITest_refTrans20WB.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 32 kbps trans-coding to 20 kbps, 30 ms, WIDEBAND" - echo - $ISAC -I -B 32000 -FL 30 -FS 16 -T 20000 ../Transcoding"$PLATFORM"/APITest_32T20.pcm \ - ../data/orig/speech_and_misc_WB.pcm ../Transcoding"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - - echo - echo - echo "-- 38 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 38000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../Transcoding"$PLATFORM"/APITest_refTrans38.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 45 kbps trans-coding to 38 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 45000 -FL 30 -FS 32 -T 38000 ../Transcoding"$PLATFORM"/APITest_45T38.pcm \ - ../data/origswb/jstest_32.pcm ../Transcoding"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - - echo - echo - echo "-- 20 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 20000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../Transcoding"$PLATFORM"/APITest_refTrans20SWB.pcm >> $LOGFILE_API - let "idx=idx+1" - - echo - echo - - echo "-- 45 kbps trans-coding to 20 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 45000 -FL 30 -FS 32 -T 20000 ../Transcoding"$PLATFORM"/APITest_45T20.pcm \ - ../data/origswb/jstest_32.pcm ../Transcoding"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - echo "-- 50 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 50000 -FL 30 -FS 32 ../data/origswb/jstest_32.pcm \ - ../Transcoding"$PLATFORM"/APITest_refTrans50.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 56 kbps trans-coding to 50 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 56000 -FL 30 -FS 32 -T 50000 ../Transcoding"$PLATFORM"/APITest_56T50.pcm \ - ../data/origswb/jstest_32.pcm ../Transcoding"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 56 kbps trans-coding to 20 kbps, 30 ms, SUPER-WIDEBAND" - echo - $ISAC -I -B 56000 -FL 30 -FS 32 -T 20000 ../Transcoding"$PLATFORM"/APITest_56T20.pcm \ - ../data/origswb/jstest_32.pcm ../Transcoding"$PLATFORM"/APITest_"$idx".pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo "________________________________________________________" - echo - echo - echo - echo - echo - - echo " Test FEC" - echo "========================================================================================" - mkdir -p ../FEC"$PLATFORM" - rm -f ../FEC"$PLATFORM"/* - echo - echo - echo "-- 32 kbps with transcoding to 20kbps, 30 ms, WIDEBAND, 10% packet loss" - $ISAC -I -B 32000 -FL 30 -FS 16 -PL 10 -T 20000 ../FEC"$PLATFORM"/APITest_PL10_WB30_T20.pcm \ - ../data/orig/speech_and_misc_WB.pcm ../FEC"$PLATFORM"/APITest_PL10_WB30.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 32 kbps, 60 ms, WIDEBAND, 10% packet loss" - $ISAC -I -B 32000 -FL 60 -FS 16 -PL 10 ../data/orig/speech_and_misc_WB.pcm \ - ../FEC"$PLATFORM"/APITest_PL10_WB60.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 32 kbps with transcoding to 20 kbps, 30 ms, SUPER-WIDEBAND, 10% packet loss" - $ISAC -I -B 32000 -FL 30 -FS 32 -PL 10 -T 20000 ../FEC"$PLATFORM"/APITest_PL10_SWB_8kHz_T20.pcm \ - ../data/origswb/jstest_32.pcm ../FEC"$PLATFORM"/APITest_PL10_SWB_8kHz.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 45 kbps with Trascoding to 38 kbps, 30 ms, SUPER-WIDEBAND, 10% packet loss" - $ISAC -I -B 45000 -FL 30 -FS 32 -PL 10 -T 38000 ../FEC"$PLATFORM"/APITest_PL10_SWB_12kHz_T38.pcm \ - ../data/origswb/jstest_32.pcm ../FEC"$PLATFORM"/APITest_PL10_SWB_12kHz.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo - - echo "-- 56 kbps with transcoding to 50 kbps, 30 ms, SUPER-WIDEBAND, 10% packet loss" - $ISAC -I -B 56000 -FL 30 -FS 32 -PL 10 -T 50000 ../FEC"$PLATFORM"/APITest_PL10_SWB_16kHz_T50.pcm \ - ../data/origswb/jstest_32.pcm ../FEC"$PLATFORM"/APITest_PL10_SWB_16kHz.pcm >> $LOGFILE_API - let "idx=idx+1" - echo - echo -fi diff --git a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfault.txt b/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfault.txt deleted file mode 100644 index 63829a4b9..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfault.txt +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character????? ?????? -if [ "$1" = "x64" ] || [ "$#" -eq 0 ] - then - PLATFORM=_X64 - ISAC=../x64/Release/ReleaseTest-API_2005.exe -elif [ "$1" = "2005" ] - then - PLATFORM=_2005 - ISAC=../win32/Release/ReleaseTest-API_2005.exe -elif [ "$1" == "LINUX" ] - then - PLATFORM=_linux - ISAC=../ReleaseTest-API/isacswtest -else - echo Unknown Platform - exit 2 -fi - -LOGFILE=logfault$PLATFORM.txt -echo "START FAULT TEST" > $LOGFILE - - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -INDIRSWB=../data/origswb -OUTDIR=../dataqaft$PLATFORM -mkdir -p $OUTDIR - -#maximum Target rate for different bandwidth -TARGETRATE=( 32000 32000 44000 56000 ) -SAMPFREQ=( 16 32 32 32 ) -FAULTTEST=(1 2 3 4 5 6 7 9) - -index1=0 - -file_wb=../data/orig/16kHz.pcm -file_swb=../data/origswb/32kHz.pcm - -for idx in 0 1 2 3 - do -# Fault test - echo - echo "Sampling Frequency " ${SAMPFREQ[idx]} "kHz, Rate " ${TARGETRATE[idx]} "bps." - echo "---------------------------------------------------" - if [ ${SAMPFREQ[idx]} -eq 16 ]; then - file=$file_wb - else - file=$file_swb - fi - - for testnr in ${FAULTTEST[*]} - do - echo "Running Fault Test " $testnr - $ISAC -I -B "${TARGETRATE[idx]}" -F $testnr -FS "${SAMPFREQ[idx]}" "$file" \ - $OUTDIR/ft"$testnr"_"${TARGETRATE[idx]}"_"${SAMPFREQ[idx]}".pcm >> LOGFILE - echo - - done - -# Fault test number 10, error in bitstream - echo "Running Fault Test 10" - $ISAC -I -B "${TARGETRATE[idx]}" -F 10 -FS "${SAMPFREQ[idx]}" "$file" \ - $OUTDIR/ft10_"${TARGETRATE[idx]}"_"${SAMPFREQ[idx]}".pcm >> LOGFILE - echo - echo "Running Fault Test 10 with packetloss" - $ISAC -I -B "${TARGETRATE[idx]}" -F 10 -PL 10 -FS "${SAMPFREQ[idx]}" "$file" \ - $OUTDIR/ft10plc_"${TARGETRATE[idx]}"_"${SAMPFREQ[idx]}".pcm >> LOGFILE - echo -done - -echo -echo -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfixfloat.txt b/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfixfloat.txt deleted file mode 100644 index 4cda78e4f..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/QA/runiSACfixfloat.txt +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -(set -o igncr) 2>/dev/null && set -o igncr; # force bash to ignore \r character - -LOGFILE=logfxfl.txt -echo "START FIX-FLOAT TEST" > $LOGFILE - - -ISACFIXFLOAT=../../../fix/test/testFixFloat.exe - -INFILES=$(cat InputFiles.txt) -SUBSET=$(cat InputFilesFew.txt) -CHANNELFILES=$(cat ChannelFiles.txt) -CHANNELLIST=($(cat ChannelFiles.txt)) -INDIR=../data/orig -OUTDIR=../dataqafxfl -mkdir -p $OUTDIR - -index1=0 - -for file in $INFILES # loop over all input files - do - - for channel in $CHANNELFILES - do - let "index1=index1+1" - - $ISACFIXFLOAT $INDIR/$channel -m 1 -PLC $INDIR/"$file" $OUTDIR/flfx$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -PLC $INDIR/"$file" $OUTDIR/fxfl$index1"$file" >> $LOGFILE - done - -done - -index1=0 - -for file in $SUBSET # loop over the subset of input files - do - let "index1=index1+1" - $ISACFIXFLOAT $INDIR/$channel -m 1 -NB 1 $INDIR/"$file" $OUTDIR/flfxnb1_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -NB 1 $INDIR/"$file" $OUTDIR/fxflnb1_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 1 -NB 2 -PLC $INDIR/"$file" $OUTDIR/flfxnb2_$index1"$file" >> $LOGFILE - $ISACFIXFLOAT $INDIR/$channel -m 2 -NB 2 -PLC $INDIR/"$file" $OUTDIR/fxflnb2_$index1"$file" >> $LOGFILE -done - -echo DONE! - - - diff --git a/modules/audio_coding/codecs/iSAC/main/test/ReleaseTest-API/ReleaseTest-API.cc b/modules/audio_coding/codecs/iSAC/main/test/ReleaseTest-API/ReleaseTest-API.cc deleted file mode 100644 index f8ec170cf..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/ReleaseTest-API/ReleaseTest-API.cc +++ /dev/null @@ -1,1063 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// ReleaseTest-API.cpp : Defines the entry point for the console application. -// - -#include -#include -#include -#include -#include -#include - -/* include API */ -#include "isac.h" -#include "utility.h" - -/* Defines */ -#define SEED_FILE "randseed.txt" /* Used when running decoder on garbage data */ -#define MAX_FRAMESAMPLES 960 /* max number of samples per frame - (= 60 ms frame & 16 kHz) or - (= 30 ms frame & 32 kHz) */ -#define FRAMESAMPLES_10ms 160 /* number of samples per 10ms frame */ -#define SWBFRAMESAMPLES_10ms 320 -//#define FS 16000 /* sampling frequency (Hz) */ - -#ifdef WIN32 -#define CLOCKS_PER_SEC 1000 /* Runtime statistics */ -#endif - - - - -using namespace std; - -int main(int argc, char* argv[]) -{ - - char inname[100], outname[100], bottleneck_file[100], vadfile[100]; - FILE *inp, *outp, *f_bn=NULL, *vadp, *bandwidthp; - int framecnt, endfile; - - int i, errtype, VADusage = 0, packetLossPercent = 0; - WebRtc_Word16 CodingMode; - WebRtc_Word32 bottleneck; - WebRtc_Word16 framesize = 30; /* ms */ - int cur_framesmpls, err; - int testCE=0; - - /* Runtime statistics */ - double starttime, runtime, length_file; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 declen, lostFrame = 0, declenTC; - - WebRtc_Word16 shortdata[SWBFRAMESAMPLES_10ms]; - WebRtc_Word16 vaddata[SWBFRAMESAMPLES_10ms*3]; - WebRtc_Word16 decoded[MAX_FRAMESAMPLES << 1]; - WebRtc_Word16 decodedTC[MAX_FRAMESAMPLES << 1]; - WebRtc_UWord16 streamdata[500]; - WebRtc_Word16 speechType[1]; - WebRtc_Word16 prevFrameSize = 1; - WebRtc_Word16 rateBPS = 0; - WebRtc_Word16 fixedFL = 0; - WebRtc_Word16 payloadSize = 0; - WebRtc_Word32 payloadRate = 0; - int setControlBWE = 0; - short FL, testNum; - char version_number[20]; - int readLoss; - FILE *plFile; - WebRtc_Word32 sendBN; - -#ifdef _DEBUG - FILE *fy; - double kbps; -#endif /* _DEBUG */ - int totalbits =0; - int totalsmpls =0; - - /* For fault test 10, garbage data */ - //FILE *seedfile; - unsigned int random_seed = (unsigned int) time(NULL);//1196764538 - - /* If use GNS file */ - FILE *fp_gns = NULL; - int gns = 0; - int cur_delay = 0; - char gns_file[100]; - short maxStreamLen30 = 0; - short maxStreamLen60 = 0; - short sampFreqKHz = 32; - short samplesIn10Ms; - short useAssign = 0; - //FILE logFile; - bool doTransCoding = false; - WebRtc_Word32 rateTransCoding = 0; - WebRtc_UWord16 streamDataTransCoding[600]; - WebRtc_Word16 streamLenTransCoding; - FILE* transCodingFile; - FILE* transcodingBitstream; - WebRtc_UWord32 numTransCodingBytes=0; - WebRtc_UWord32 numREDTransCodingBytes=0; - - /* only one structure used for ISAC encoder */ - ISACStruct* ISAC_main_inst; - ISACStruct* decoderTransCoding; - - BottleNeckModel BN_data; - -#ifdef _DEBUG - fy = fopen("bit_rate.dat", "w"); - fclose(fy); - fy = fopen("bytes_frames.dat", "w"); - fclose(fy); -#endif /* _DEBUG */ - - /* Handling wrong input arguments in the command line */ - if((argc<3) || (argc>17)) { - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("\n"); - WebRtcIsac_version(version_number); - printf("iSAC-swb version %s \n\n", version_number); - - printf("Usage:\n\n"); - printf("./kenny.exe [-I] bottleneck_value infile outfile \n\n"); - printf("with:\n"); - printf("[-FS num] : sampling frequency in kHz, valid values are 16 & 32,\n"); - printf(" with 16 as default.\n"); - printf("[-I] : if -I option is specified, the coder will use\n"); - printf(" an instantaneous Bottleneck value. If not, it\n"); - printf(" will be an adaptive Bottleneck value.\n\n"); - printf("[-assign] : Use Assign API.\n"); - printf("[-B num] : the value of the bottleneck provided either\n"); - printf(" as a fixed value in bits/sec (e.g. 25000) or\n"); - printf(" read from a file (e.g. bottleneck.txt)\n\n"); - printf("[-INITRATE num] : Set a new value for initial rate. Note! Only used in \n"); - printf(" adaptive mode.\n\n"); - printf("[-FL num] : Set (initial) frame length in msec. Valid length are \n"); - printf(" 30 and 60 msec.\n\n"); - printf("[-FIXED_FL] : Frame length will be fixed to initial value.\n\n"); - printf("[-MAX num] : Set the limit for the payload size of iSAC in bytes. \n"); - printf(" Minimum 100 maximum 400.\n\n"); - printf("[-MAXRATE num] : Set the maxrate for iSAC in bits per second. \n"); - printf(" Minimum 32000, maximum 53400.\n\n"); - printf("[-F num] : if -F option is specified, the test function\n"); - printf(" will run the iSAC API fault scenario specified by the\n"); - printf(" supplied number.\n"); - printf(" F 1 - Call encoder prior to init encoder call\n"); - printf(" F 2 - Call decoder prior to init decoder call\n"); - printf(" F 3 - Call decoder prior to encoder call\n"); - printf(" F 4 - Call decoder with a too short coded sequence\n"); - printf(" F 5 - Call decoder with a too long coded sequence\n"); - printf(" F 6 - Call decoder with random bit stream\n"); - printf(" F 7 - Call init encoder/decoder at random during a call\n"); - printf(" F 8 - Call encoder/decoder without having allocated memory \n"); - printf(" for encoder/decoder instance\n"); - printf(" F 9 - Call decodeB without calling decodeA\n"); - printf(" F 10 - Call decodeB with garbage data\n"); - printf("[-PL num] : if -PL option is specified \n"); - printf("[-T rate file] : test trans-coding with target bottleneck 'rate' bits/sec\n"); - printf(" the output file is written to 'file'\n"); - printf("[-LOOP num] : number of times to repeat coding the input file for stress testing\n"); - //printf("[-CE num] : Test of APIs used by Conference Engine.\n"); - //printf(" CE 1 - getNewBitstream, getBWE \n"); - //printf(" (CE 2 - RESERVED for transcoding)\n"); - //printf(" CE 3 - getSendBWE, setSendBWE. \n\n"); - //printf("-L filename : write the logging info into file (appending)\n"); - printf("infile : Normal speech input file\n\n"); - printf("outfile : Speech output file\n\n"); - exit(0); - } - - /* Print version number */ - printf("-------------------------------------------------\n"); - WebRtcIsac_version(version_number); - printf("iSAC version %s \n\n", version_number); - - /* Loop over all command line arguments */ - CodingMode = 0; - testNum = 0; - testCE = 0; - useAssign = 0; - //logFile = NULL; - char transCodingFileName[500]; - WebRtc_Word16 totFileLoop = 0; - WebRtc_Word16 numFileLoop = 0; - for (i = 1; i < argc-2;i++) - { - if(!strcmp("-LOOP", argv[i])) - { - i++; - totFileLoop = (WebRtc_Word16)atol(argv[i]); - if(totFileLoop <= 0) - { - fprintf(stderr, "Invalid number of runs for the given input file, %d.", totFileLoop); - exit(0); - } - } - - if(!strcmp("-T", argv[i])) - { - doTransCoding = true; - i++; - rateTransCoding = atoi(argv[i]); - i++; - strcpy(transCodingFileName, argv[i]); - } - - /*Should we use assign API*/ - if(!strcmp("-assign", argv[i])) - { - useAssign = 1; - } - - /* Set Sampling Rate */ - if(!strcmp("-FS", argv[i])) - { - i++; - sampFreqKHz = atoi(argv[i]); - } - - /* Instantaneous mode */ - if(!strcmp ("-I", argv[i])) - { - printf("Instantaneous BottleNeck\n"); - CodingMode = 1; - } - - /* Set (initial) bottleneck value */ - if(!strcmp ("-INITRATE", argv[i])) { - rateBPS = atoi(argv[i + 1]); - setControlBWE = 1; - if((rateBPS < 10000) || (rateBPS > 32000)) - { - printf("\n%d is not a initial rate. Valid values are in the range 10000 to 32000.\n", rateBPS); - exit(0); - } - printf("New initial rate: %d\n", rateBPS); - i++; - } - - /* Set (initial) framelength */ - if(!strcmp ("-FL", argv[i])) { - framesize = atoi(argv[i + 1]); - if((framesize != 30) && (framesize != 60)) - { - printf("\n%d is not a valid frame length. Valid length are 30 and 60 msec.\n", framesize); - exit(0); - } - setControlBWE = 1; - printf("Frame Length: %d\n", framesize); - i++; - } - - /* Fixed frame length */ - if(!strcmp ("-FIXED_FL", argv[i])) - { - fixedFL = 1; - setControlBWE = 1; - printf("Fixed Frame Length\n"); - } - - /* Set maximum allowed payload size in bytes */ - if(!strcmp ("-MAX", argv[i])) { - payloadSize = atoi(argv[i + 1]); - printf("Maximum Payload Size: %d\n", payloadSize); - i++; - } - - /* Set maximum rate in bytes */ - if(!strcmp ("-MAXRATE", argv[i])) { - payloadRate = atoi(argv[i + 1]); - printf("Maximum Rate in kbps: %d\n", payloadRate); - i++; - } - - /* Test of fault scenarious */ - if(!strcmp ("-F", argv[i])) - { - testNum = atoi(argv[i + 1]); - printf("Fault test: %d\n", testNum); - if(testNum < 1 || testNum > 10) - { - printf("\n%d is not a valid Fault Scenario number. Valid Fault Scenarios are numbered 1-10.\n", testNum); - exit(0); - } - i++; - } - - /* Packet loss test */ - if(!strcmp ("-PL", argv[i])) - { - if( isdigit( *argv[i+1] ) ) - { - packetLossPercent = atoi( argv[i+1] ); - if( (packetLossPercent < 0) | (packetLossPercent > 100) ) - { - printf( "\nInvalid packet loss perentage \n" ); - exit( 0 ); - } - if( packetLossPercent > 0 ) - { - printf( "Simulating %d %% of independent packet loss\n", packetLossPercent ); - } - else - { - printf( "\nNo Packet Loss Is Simulated \n" ); - } - readLoss = 0; - } - else - { - readLoss = 1; - plFile = fopen( argv[i+1], "rb" ); - if( plFile == NULL ) - { - printf( "\n couldn't open the frameloss file: %s\n", argv[i+1] ); - exit( 0 ); - } - printf( "Simulating packet loss through the given channel file: %s\n", argv[i+1] ); - } - i++; - } - - /* Random packetlosses */ - if(!strcmp ("-rnd", argv[i])) - { - srand((unsigned int)time(NULL) ); - printf( "Random pattern in lossed packets \n" ); - } - - /* Use gns file */ - if(!strcmp ("-G", argv[i])) - { - sscanf(argv[i + 1], "%s", gns_file); - fp_gns = fopen(gns_file, "rb"); - if(fp_gns == NULL) - { - printf("Cannot read file %s.\n", gns_file); - exit(0); - } - gns = 1; - i++; - } - - - // make it with '-B' - /* Get Bottleneck value */ - if(!strcmp("-B", argv[i])) - { - i++; - bottleneck = atoi(argv[i]); - if(bottleneck == 0) - { - sscanf(argv[i], "%s", bottleneck_file); - f_bn = fopen(bottleneck_file, "rb"); - if(f_bn == NULL) - { - printf("Error No value provided for BottleNeck and cannot read file %s.\n", bottleneck_file); - exit(0); - } - else - { - printf("reading bottleneck rates from file %s\n\n",bottleneck_file); - if(fscanf(f_bn, "%d", &bottleneck) == EOF) - { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - - /* Bottleneck is a cosine function - * Matlab code for writing the bottleneck file: - * BottleNeck_10ms = 20e3 + 10e3 * cos((0:5999)/5999*2*pi); - * fid = fopen('bottleneck.txt', 'wb'); - * fprintf(fid, '%d\n', BottleNeck_10ms); fclose(fid); - */ - } - } - else - { - printf("\nfixed bottleneck rate of %d bits/s\n\n", bottleneck); - } - } - /* Run Conference Engine APIs */ - // Do not test it in the first release - // - // if(!strcmp ("-CE", argv[i])) - // { - // testCE = atoi(argv[i + 1]); - // if(testCE==1) - // { - // i++; - // scale = (float)atof( argv[i+1] ); - // } - // else if(testCE == 2) - // { - // printf("\nCE-test 2 (transcoding) not implemented.\n"); - // exit(0); - // } - // else if(testCE < 1 || testCE > 3) - // { - // printf("\n%d is not a valid CE-test number. Valid CE tests are 1-3.\n", testCE); - // exit(0); - // } - // printf("CE-test number: %d\n", testCE); - // i++; - // } - } - - if(CodingMode == 0) - { - printf("\nAdaptive BottleNeck\n"); - } - - switch(sampFreqKHz) - { - case 16: - { - printf("iSAC Wideband.\n"); - samplesIn10Ms = FRAMESAMPLES_10ms; - break; - } - case 32: - { - printf("iSAC Supper-Wideband.\n"); - samplesIn10Ms = SWBFRAMESAMPLES_10ms; - break; - } - default: - printf("Unsupported sampling frequency %d kHz", sampFreqKHz); - exit(0); - } - - - - - /* Get Input and Output files */ - sscanf(argv[argc-2], "%s", inname); - sscanf(argv[argc-1], "%s", outname); - printf("\nInput file: %s\n", inname); - printf("Output file: %s\n\n", outname); - if((inp = fopen(inname,"rb")) == NULL) - { - printf(" Error iSAC Cannot read file %s.\n", inname); - cout << flush; - exit(1); - } - - if((outp = fopen(outname,"wb")) == NULL) - { - printf(" Error iSAC Cannot write file %s.\n", outname); - cout << flush; - getchar(); - exit(1); - } - if(VADusage) - { - if((vadp = fopen(vadfile,"rb")) == NULL) - { - printf(" Error iSAC Cannot read file %s.\n", vadfile); - cout << flush; - exit(1); - } - } - - if((bandwidthp = fopen("bwe.pcm","wb")) == NULL) - { - printf(" Error iSAC Cannot read file %s.\n", "bwe.pcm"); - cout << flush; - exit(1); - } - - - starttime = clock()/(double)CLOCKS_PER_SEC; /* Runtime statistics */ - - /* Initialize the ISAC and BN structs */ - if(testNum != 8) - { - if(!useAssign) - { - err =WebRtcIsac_Create(&ISAC_main_inst); - WebRtcIsac_SetEncSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - WebRtcIsac_SetDecSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - } - else - { - /* Test the Assign functions */ - int sss; - void *ppp; - err = WebRtcIsac_AssignSize(&sss); - ppp = malloc(sss); - err = WebRtcIsac_Assign(&ISAC_main_inst, ppp); - WebRtcIsac_SetEncSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - WebRtcIsac_SetDecSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - } - /* Error check */ - if(err < 0) - { - printf("\n\n Error in create.\n\n"); - cout << flush; - exit(EXIT_FAILURE); - } - } - BN_data.arrival_time = 0; - BN_data.sample_count = 0; - BN_data.rtp_number = 0; - - /* Initialize encoder and decoder */ - framecnt= 0; - endfile = 0; - - if(doTransCoding) - { - WebRtcIsac_Create(&decoderTransCoding); - WebRtcIsac_SetEncSampRate(decoderTransCoding, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - WebRtcIsac_SetDecSampRate(decoderTransCoding, (sampFreqKHz == 16)? kIsacWideband:kIsacSuperWideband); - WebRtcIsac_DecoderInit(decoderTransCoding); - transCodingFile = fopen(transCodingFileName, "wb"); - if(transCodingFile == NULL) - { - printf("Could not open %s to output trans-coding.\n", transCodingFileName); - exit(0); - } - strcat(transCodingFileName, ".bit"); - transcodingBitstream = fopen(transCodingFileName, "wb"); - if(transcodingBitstream == NULL) - { - printf("Could not open %s to write the bit-stream of transcoder.\n", transCodingFileName); - exit(0); - } - } - - if(testNum != 1) - { - if(WebRtcIsac_EncoderInit(ISAC_main_inst, CodingMode) < 0) - { - printf("Error could not initialize the encoder \n"); - cout << flush; - return 0; - } - } - if(testNum != 2) - { - if(WebRtcIsac_DecoderInit(ISAC_main_inst) < 0) - { - printf("Error could not initialize the decoder \n"); - cout << flush; - return 0; - } - } - if(CodingMode == 1) - { - err = WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in initialization (control): %d.\n\n", errtype); - cout << flush; - if(testNum == 0) - { - exit(EXIT_FAILURE); - } - } - } - - if((setControlBWE) && (CodingMode == 0)) - { - err = WebRtcIsac_ControlBwe(ISAC_main_inst, rateBPS, framesize, fixedFL); - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - - printf("\n\n Error in Control BWE: %d.\n\n", errtype); - cout << flush; - exit(EXIT_FAILURE); - } - } - - if(payloadSize != 0) - { - err = WebRtcIsac_SetMaxPayloadSize(ISAC_main_inst, payloadSize); - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in SetMaxPayloadSize: %d.\n\n", errtype); - cout << flush; - exit(EXIT_FAILURE); - } - } - if(payloadRate != 0) - { - err = WebRtcIsac_SetMaxRate(ISAC_main_inst, payloadRate); - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in SetMaxRateInBytes: %d.\n\n", errtype); - cout << flush; - exit(EXIT_FAILURE); - } - } - - *speechType = 1; - - cout << "\n" << flush; - - length_file = 0; - WebRtc_Word16 bnIdxTC; - WebRtc_Word16 jitterInfoTC; - while (endfile == 0) - { - /* Call init functions at random, fault test number 7 */ - if(testNum == 7 && (rand()%2 == 0)) - { - err = WebRtcIsac_EncoderInit(ISAC_main_inst, CodingMode); - /* Error check */ - if(err < 0) - { - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in encoderinit: %d.\n\n", errtype); - cout << flush; - } - - err = WebRtcIsac_DecoderInit(ISAC_main_inst); - /* Error check */ - if(err < 0) - { - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\n Error in decoderinit: %d.\n\n", errtype); - cout << flush; - } - } - - cur_framesmpls = 0; - while (1) - { - int kkk; - - /* Read 10 ms speech block */ - endfile = readframe(shortdata, inp, samplesIn10Ms); - - if(endfile) - { - numFileLoop++; - if(numFileLoop < totFileLoop) - { - rewind(inp); - framecnt = 0; - fprintf(stderr, "\n"); - endfile = readframe(shortdata, inp, samplesIn10Ms); - } - } - - if(testNum == 7) - { - srand((unsigned int)time(NULL)); - } - - /* iSAC encoding */ - if(!(testNum == 3 && framecnt == 0)) - { - stream_len = WebRtcIsac_Encode(ISAC_main_inst, - shortdata, - (WebRtc_Word16*)streamdata); - if((payloadSize != 0) && (stream_len > payloadSize)) - { - if(testNum == 0) - { - printf("\n\n"); - } - - printf("\nError: Streamsize out of range %d\n", stream_len - payloadSize); - cout << flush; - } - - kkk = WebRtcIsac_GetUplinkBw(ISAC_main_inst, &sendBN); - - if(stream_len>0) - { - if(doTransCoding) - { - WebRtc_Word16 indexStream; - WebRtc_UWord8 auxUW8; - - /************************* Main Transcoding stream *******************************/ - WebRtcIsac_GetDownLinkBwIndex(ISAC_main_inst, &bnIdxTC, &jitterInfoTC); - streamLenTransCoding = WebRtcIsac_GetNewBitStream( - ISAC_main_inst, bnIdxTC, jitterInfoTC, rateTransCoding, - (WebRtc_Word16*)streamDataTransCoding, false); - if(streamLenTransCoding < 0) - { - fprintf(stderr, "Error in trans-coding\n"); - exit(0); - } - auxUW8 = (WebRtc_UWord8)(((streamLenTransCoding & 0xFF00) >> 8) & 0x00FF); - fwrite(&auxUW8, sizeof(WebRtc_UWord8), - 1, transcodingBitstream); - - auxUW8 = (WebRtc_UWord8)(streamLenTransCoding & 0x00FF); - fwrite(&auxUW8, sizeof(WebRtc_UWord8), - 1, transcodingBitstream); - - fwrite((WebRtc_UWord8*)streamDataTransCoding, sizeof(WebRtc_UWord8), - streamLenTransCoding, transcodingBitstream); - - WebRtcIsac_ReadBwIndex((WebRtc_Word16*)streamDataTransCoding, &indexStream); - if(indexStream != bnIdxTC) - { - fprintf(stderr, "Error in inserting Bandwidth index into transcoding stream.\n"); - exit(0); - } - numTransCodingBytes += streamLenTransCoding; - } - } - } - else - { - kkk = 0; - break; - } - - if(stream_len < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - printf("\n\nError in encoder: %d.\n\n", errtype); - cout << flush; - } - cur_framesmpls += samplesIn10Ms; - /* exit encoder loop if the encoder returned a bitstream */ - if(stream_len != 0) break; - } - - /* read next bottleneck rate */ - if(f_bn != NULL) - { - if(fscanf(f_bn, "%d", &bottleneck) == EOF) - { - /* Set pointer to beginning of file */ - fseek(f_bn, 0L, SEEK_SET); - fscanf(f_bn, "%d", &bottleneck); - } - if(CodingMode == 1) - { - WebRtcIsac_Control(ISAC_main_inst, bottleneck, framesize); - } - } - - length_file += cur_framesmpls; - if(cur_framesmpls == (3 * samplesIn10Ms)) - { - maxStreamLen30 = (stream_len > maxStreamLen30)? stream_len:maxStreamLen30; - } - else - { - maxStreamLen60 = (stream_len > maxStreamLen60)? stream_len:maxStreamLen60; - } - - if(!lostFrame) - { - lostFrame = ((rand()%100) < packetLossPercent); - } - else - { - lostFrame = 0; - } - - // RED. - if(lostFrame) - { - stream_len = WebRtcIsac_GetRedPayload(ISAC_main_inst, - (WebRtc_Word16*)streamdata); - - if(doTransCoding) - { - streamLenTransCoding = WebRtcIsac_GetNewBitStream( - ISAC_main_inst, bnIdxTC, jitterInfoTC, rateTransCoding, - (WebRtc_Word16*)streamDataTransCoding, true); - if(streamLenTransCoding < 0) - { - fprintf(stderr, "Error in RED trans-coding\n"); - exit(0); - } - } - } - - /* make coded sequence to short be inreasing */ - /* the length the decoder expects */ - if(testNum == 4) - { - stream_len += 10; - } - - /* make coded sequence to long be decreasing */ - /* the length the decoder expects */ - if(testNum == 5) - { - stream_len -= 10; - } - - if(testNum == 6) - { - srand((unsigned int)time(NULL)); - for(i = 0; i < stream_len; i++) - { - streamdata[i] = rand(); - } - } - - if(VADusage){ - readframe(vaddata, vadp, samplesIn10Ms*3); - } - - /* simulate packet handling through NetEq and the modem */ - if(!(testNum == 3 && framecnt == 0)) - { - get_arrival_time(cur_framesmpls, stream_len, bottleneck, &BN_data, - sampFreqKHz*1000, sampFreqKHz*1000); - } - - if(VADusage && (framecnt>10 && vaddata[0]==0)) - { - BN_data.rtp_number--; - } - else - { - /* Error test number 10, garbage data */ - if(testNum == 10) - { - /* Test to run decoder with garbage data */ - for(i = 0; i < stream_len; i++) - { - streamdata[i] = (short) (streamdata[i]) + (short) rand(); - } - } - - if(testNum != 9) - { - err = WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, streamdata, - stream_len, BN_data.rtp_number, BN_data.sample_count, - BN_data.arrival_time); - - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - if(testNum == 0) - { - printf("\n\n"); - } - - printf("Error: in decoder: %d.", errtype); - cout << flush; - if(testNum == 0) - { - printf("\n\n"); - } - - } - } - - /* Call getFramelen, only used here for function test */ - err = WebRtcIsac_ReadFrameLen(ISAC_main_inst, - (WebRtc_Word16*)streamdata, &FL); - if(err < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - if(testNum == 0) - { - printf("\n\n"); - } - printf(" Error: in getFrameLen %d.", errtype); - cout << flush; - if(testNum == 0) - { - printf("\n\n"); - } - } - - // iSAC decoding - - if(lostFrame) - { - declen = WebRtcIsac_DecodeRcu(ISAC_main_inst, streamdata, - stream_len, decoded, speechType); - - if(doTransCoding) - { - declenTC = WebRtcIsac_DecodeRcu(decoderTransCoding, - streamDataTransCoding, streamLenTransCoding, - decodedTC, speechType); - } - } - else - { - declen = WebRtcIsac_Decode(ISAC_main_inst, streamdata, - stream_len, decoded, speechType); - - if(doTransCoding) - { - declenTC = WebRtcIsac_Decode(decoderTransCoding, - streamDataTransCoding, streamLenTransCoding, - decodedTC, speechType); - } - } - - if(declen < 0) - { - /* exit if returned with error */ - errtype=WebRtcIsac_GetErrorCode(ISAC_main_inst); - if(testNum == 0) - { - printf("\n\n"); - } - printf(" Error: in decoder %d.", errtype); - cout << flush; - if(testNum == 0) - { - printf("\n\n"); - } - } - - if(declenTC < 0) - { - if(testNum == 0) - { - printf("\n\n"); - } - printf(" Error: in decoding the transcoded stream"); - cout << fflush; - if(testNum == 0) - { - printf("\n\n"); - } - - } - } - /* Write decoded speech frame to file */ - if((declen > 0) && (numFileLoop == 0)) - { - fwrite(decoded, sizeof(WebRtc_Word16), declen, outp); - } - - if((declenTC > 0) && (numFileLoop == 0)) - { - fwrite(decodedTC, sizeof(WebRtc_Word16), declen, transCodingFile); - } - - - fprintf(stderr, "\rframe = %5d ", framecnt); - fflush(stderr); - framecnt++; - - /* Error test number 10, garbage data */ - //if(testNum == 10) - //{ - // /* Test to run decoder with garbage data */ - // if( (seedfile = fopen(SEED_FILE, "a+t") ) == NULL ) - // { - // fprintf(stderr, "Error: Could not open file %s\n", SEED_FILE); - // } - // else - // { - // fprintf(seedfile, "ok\n\n"); - // fclose(seedfile); - // } - //} - /* Error test number 10, garbage data */ - //if(testNum == 10) - //{ - // /* Test to run decoder with garbage data */ - // for ( i = 0; i < stream_len; i++) - // { - // streamdata[i] = (short) (streamdata[i] + (short) rand()); - // } - //} - - - totalsmpls += declen; - totalbits += 8 * stream_len; -#ifdef _DEBUG - kbps = ((double) sampFreqKHz * 1000.) / ((double) cur_framesmpls) * 8.0 * stream_len / 1000.0;// kbits/s - fy = fopen("bit_rate.dat", "a"); - fprintf(fy, "Frame %i = %0.14f\n", framecnt, kbps); - fclose(fy); - -#endif /* _DEBUG */ - - } - printf("\n"); - printf("total bits = %d bits\n", totalbits); - printf("measured average bitrate = %0.3f kbits/s\n", - (double)totalbits *(sampFreqKHz) / totalsmpls); - if(doTransCoding) - { - printf("Transcoding average bit-rate = %0.3f kbps\n", - (double)numTransCodingBytes * 8.0 *(sampFreqKHz) / totalsmpls); - fclose(transCodingFile); - } - printf("\n"); - - /* Runtime statistics */ - runtime = (double)(clock()/(double)CLOCKS_PER_SEC-starttime); - length_file = length_file /(sampFreqKHz * 1000.); - - printf("\n\nLength of speech file: %.1f s\n", length_file); - printf("Time to run iSAC: %.2f s (%.2f %% of realtime)\n\n", runtime, (100*runtime/length_file)); - - if(maxStreamLen30 != 0) - { - printf("Maximum payload size 30ms Frames %d bytes (%0.3f kbps)\n", - maxStreamLen30, - maxStreamLen30 * 8 / 30.); - } - if(maxStreamLen60 != 0) - { - printf("Maximum payload size 60ms Frames %d bytes (%0.3f kbps)\n", - maxStreamLen60, - maxStreamLen60 * 8 / 60.); - } - //fprintf(stderr, "\n"); - - fprintf(stderr, " %.1f s", length_file); - fprintf(stderr, " %0.1f kbps", (double)totalbits *(sampFreqKHz) / totalsmpls); - if(maxStreamLen30 != 0) - { - fprintf(stderr, " plmax-30ms %d bytes (%0.0f kbps)", - maxStreamLen30, - maxStreamLen30 * 8 / 30.); - } - if(maxStreamLen60 != 0) - { - fprintf(stderr, " plmax-60ms %d bytes (%0.0f kbps)", - maxStreamLen60, - maxStreamLen60 * 8 / 60.); - } - if(doTransCoding) - { - fprintf(stderr, " transcoding rate %.0f kbps", - (double)numTransCodingBytes * 8.0 *(sampFreqKHz) / totalsmpls); - } - - fclose(inp); - fclose(outp); - WebRtcIsac_Free(ISAC_main_inst); - - - exit(0); -} diff --git a/modules/audio_coding/codecs/iSAC/main/test/SwitchingSampRate/SwitchingSampRate.cc b/modules/audio_coding/codecs/iSAC/main/test/SwitchingSampRate/SwitchingSampRate.cc deleted file mode 100644 index 25320f956..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/SwitchingSampRate/SwitchingSampRate.cc +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// SwitchingSampRate.cpp : Defines the entry point for the console -// application. -// - -#include -#include "isac.h" -#include "utility.h" -#include "signal_processing_library.h" - -#define MAX_FILE_NAME 500 -#define MAX_NUM_CLIENTS 2 - - -#define NUM_CLIENTS 2 - -using namespace std; - -int main(int argc, char* argv[]) -{ - char fileNameWB[MAX_FILE_NAME]; - char fileNameSWB[MAX_FILE_NAME]; - - char outFileName[MAX_NUM_CLIENTS][MAX_FILE_NAME]; - - FILE* inFile[MAX_NUM_CLIENTS]; - FILE* outFile[MAX_NUM_CLIENTS]; - - ISACStruct* codecInstance[MAX_NUM_CLIENTS]; - WebRtc_Word32 resamplerState[MAX_NUM_CLIENTS][8]; - - enum IsacSamplingRate encoderSampRate[MAX_NUM_CLIENTS]; - - int minBn = 16000; - int maxBn = 56000; - - int bnWB = 32000; - int bnSWB = 56000; - - strcpy(outFileName[0], "switchSampRate_out1.pcm"); - strcpy(outFileName[1], "switchSampRate_out2.pcm"); - - short clientCntr; - - unsigned int lenEncodedInBytes[MAX_NUM_CLIENTS]; - unsigned int lenAudioIn10ms[MAX_NUM_CLIENTS]; - unsigned int lenEncodedInBytesTmp[MAX_NUM_CLIENTS]; - unsigned int lenAudioIn10msTmp[MAX_NUM_CLIENTS]; - BottleNeckModel* packetData[MAX_NUM_CLIENTS]; - - char versionNumber[100]; - short bottleneckChange[MAX_NUM_CLIENTS]; - short samplesIn10ms[MAX_NUM_CLIENTS]; - int bottleneck[MAX_NUM_CLIENTS]; - - printf("\n\n"); - printf("____________________________________________\n\n"); - WebRtcIsac_version(versionNumber); - printf(" iSAC-swb version %s\n", versionNumber); - printf("____________________________________________\n"); - - - fileNameWB[0] = '\0'; - fileNameSWB[0] = '\0'; - - // READ THE WIDEBAND AND SUPER-WIDEBAND FILE NAMES - if(readParamString(argc, argv, "-wb", fileNameWB, MAX_FILE_NAME) <= 0) - { - printf("No wideband file is specified"); - } - if(readParamString(argc, argv, "-swb", fileNameSWB, MAX_FILE_NAME) <= 0) - { - printf("No super-wideband file is specified"); - } - - // THE FIRST CLIENT STARTS IN WIDEBAND - encoderSampRate[0] = kIsacWideband; - OPEN_FILE_RB(inFile[0], fileNameWB); - - // THE SECOND CLIENT STARTS IN SUPER-WIDEBAND - encoderSampRate[1] = kIsacSuperWideband; - OPEN_FILE_RB(inFile[1], fileNameSWB); - - short codingMode = readSwitch(argc, argv, "-I"); - - for(clientCntr = 0; clientCntr < NUM_CLIENTS; clientCntr++) - { - codecInstance[clientCntr] = NULL; - - printf("\n"); - printf("Client %d\n", clientCntr + 1); - printf("---------\n"); - printf("Starting %s", - (encoderSampRate[clientCntr] == kIsacWideband) - ? "wideband":"super-wideband"); - - bottleneckChange[clientCntr] = 2 * clientCntr - 1; - // Open output File Name - OPEN_FILE_WB(outFile[clientCntr], outFileName[clientCntr]); - printf("Output File...................... %s\n", outFileName[clientCntr]); - - samplesIn10ms[clientCntr] = encoderSampRate[clientCntr] * 10; - - if(codingMode == 1) - { - bottleneck[clientCntr] = (clientCntr)? bnSWB:bnWB; - } - else - { - bottleneck[clientCntr] = (clientCntr)? minBn:maxBn; - } - - printf("Bottleneck....................... %0.3f kbits/sec \n", - bottleneck[clientCntr] / 1000.0); - - // coding-mode - printf("Encoding Mode.................... %s\n", - (codingMode == 1)? "Channel-Independent (Instantaneous)":"Adaptive"); - - lenEncodedInBytes[clientCntr] = 0; - lenAudioIn10ms[clientCntr] = 0; - lenEncodedInBytesTmp[clientCntr] = 0; - lenAudioIn10msTmp[clientCntr] = 0; - - packetData[clientCntr] = (BottleNeckModel*)new(BottleNeckModel); - if(packetData[clientCntr] == NULL) - { - printf("Could not allocate memory for packetData \n"); - return -1; - } - memset(packetData[clientCntr], 0, sizeof(BottleNeckModel)); - memset(resamplerState[clientCntr], 0, sizeof(WebRtc_Word32) * 8); - } - - for(clientCntr = 0; clientCntr < NUM_CLIENTS; clientCntr++) - { - // Create - if(WebRtcIsac_Create(&codecInstance[clientCntr])) - { - printf("Could not creat client %d\n", clientCntr + 1); - return -1; - } - - WebRtcIsac_SetEncSampRate(codecInstance[clientCntr], encoderSampRate[clientCntr]); - - WebRtcIsac_SetDecSampRate(codecInstance[clientCntr], - encoderSampRate[clientCntr + (1 - ((clientCntr & 1)<<1))]); - - // Initialize Encoder - if(WebRtcIsac_EncoderInit(codecInstance[clientCntr], - codingMode) < 0) - { - printf("Could not initialize client, %d\n", clientCntr + 1); - return -1; - } - - // Initialize Decoder - if(WebRtcIsac_DecoderInit(codecInstance[clientCntr]) < 0) - { - printf("Could not initialize decoder of client %d\n", - clientCntr + 1); - return -1; - } - - // setup Rate if in Instantaneous mode - if(codingMode != 0) - { - // ONLY Clients who are not in Adaptive mode - if(WebRtcIsac_Control(codecInstance[clientCntr], - bottleneck[clientCntr], 30) < 0) - { - printf("Could not setup bottleneck and frame-size for client %d\n", - clientCntr + 1); - return -1; - } - } - } - - - short streamLen; - short numSamplesRead; - short lenDecodedAudio; - short senderIdx; - short receiverIdx; - - printf("\n"); - char rot[4] = {'\\', '|', '/', '-'}; - unsigned int rotCntr = 0; - short num10ms[MAX_NUM_CLIENTS]; - memset(num10ms, 0, sizeof(short)*MAX_NUM_CLIENTS); - FILE* arrivalTimeFile1 = fopen("arrivalTime1.dat", "wb"); - FILE* arrivalTimeFile2 = fopen("arrivalTime2.dat", "wb"); - short numPrint[MAX_NUM_CLIENTS]; - memset(numPrint, 0, sizeof(short) * MAX_NUM_CLIENTS); - - // Audio Buffers - short silence10ms[10 * 32]; - memset(silence10ms, 0, 320 * sizeof(short)); - short audioBuff10ms[10 * 32]; - short audioBuff60ms[60 * 32]; - short resampledAudio60ms[60 * 32]; - - unsigned short bitStream[600+600]; - short speechType[1]; - - short numSampFreqChanged = 0; - while(numSampFreqChanged < 10) - { - for(clientCntr = 0; clientCntr < NUM_CLIENTS; clientCntr++) - { - // Encoding/decoding for this pair of clients, if there is - // audio for any of them - //if(audioLeft[clientCntr] || audioLeft[clientCntr + 1]) - //{ - //for(pairCntr = 0; pairCntr < 2; pairCntr++) - //{ - senderIdx = clientCntr; // + pairCntr; - receiverIdx = 1 - clientCntr;// + (1 - pairCntr); - - //if(num10ms[senderIdx] > 6) - //{ - // printf("Too many frames read for client %d", - // senderIdx + 1); - // return -1; - //} - - numSamplesRead = (short)fread(audioBuff10ms, sizeof(short), - samplesIn10ms[senderIdx], inFile[senderIdx]); - if(numSamplesRead != samplesIn10ms[senderIdx]) - { - // file finished switch encoder sampling frequency. - printf("Changing Encoder Sampling frequency in client %d to ", senderIdx+1); - fclose(inFile[senderIdx]); - numSampFreqChanged++; - if(encoderSampRate[senderIdx] == kIsacWideband) - { - printf("super-wideband.\n"); - OPEN_FILE_RB(inFile[senderIdx], fileNameSWB); - encoderSampRate[senderIdx] = kIsacSuperWideband; - } - else - { - printf("wideband.\n"); - OPEN_FILE_RB(inFile[senderIdx], fileNameWB); - encoderSampRate[senderIdx] = kIsacWideband; - } - WebRtcIsac_SetEncSampRate(codecInstance[senderIdx], encoderSampRate[senderIdx]); - WebRtcIsac_SetDecSampRate(codecInstance[receiverIdx], encoderSampRate[senderIdx]); - - samplesIn10ms[clientCntr] = encoderSampRate[clientCntr] * 10; - - numSamplesRead = (short)fread(audioBuff10ms, sizeof(short), - samplesIn10ms[senderIdx], inFile[senderIdx]); - if(numSamplesRead != samplesIn10ms[senderIdx]) - { - printf(" File %s for client %d has not enough audio\n", - (encoderSampRate[senderIdx]==kIsacWideband)? "wideband":"super-wideband", - senderIdx + 1); - return -1; - } - } - num10ms[senderIdx]++; - - // sanity check - //if(num10ms[senderIdx] > 6) - //{ - // printf("Client %d has got more than 60 ms audio and encoded no packet.\n", - // senderIdx); - // return -1; - //} - - // Encode - - - streamLen = WebRtcIsac_Encode(codecInstance[senderIdx], - audioBuff10ms, (short*)bitStream); - WebRtc_Word16 ggg; - if (streamLen > 0) { - if(( WebRtcIsac_ReadFrameLen(codecInstance[receiverIdx], - (short *) bitStream, &ggg))<0) - printf("ERROR\n"); - } - - // Sanity check - if(streamLen < 0) - { - printf(" Encoder error in client %d \n", senderIdx + 1); - return -1; - } - - - if(streamLen > 0) - { - // Packet generated; model sending through a channel, do bandwidth - // estimation at the receiver and decode. - lenEncodedInBytes[senderIdx] += streamLen; - lenAudioIn10ms[senderIdx] += (unsigned int)num10ms[senderIdx]; - lenEncodedInBytesTmp[senderIdx] += streamLen; - lenAudioIn10msTmp[senderIdx] += (unsigned int)num10ms[senderIdx]; - - // Print after ~5 sec. - if(lenAudioIn10msTmp[senderIdx] >= 100) - { - numPrint[senderIdx]++; - printf(" %d, %6.3f => %6.3f ", senderIdx+1, - bottleneck[senderIdx] / 1000.0, - lenEncodedInBytesTmp[senderIdx] * 0.8 / - lenAudioIn10msTmp[senderIdx]); - - if(codingMode == 0) - { - WebRtc_Word32 bn; - WebRtcIsac_GetUplinkBw(codecInstance[senderIdx], &bn); - printf("[%d] ", bn); - } - //WebRtc_Word16 rateIndexLB; - //WebRtc_Word16 rateIndexUB; - //WebRtcIsac_GetDownLinkBwIndex(codecInstance[receiverIdx], - // &rateIndexLB, &rateIndexUB); - //printf(" (%2d, %2d) ", rateIndexLB, rateIndexUB); - - cout << flush; - lenEncodedInBytesTmp[senderIdx] = 0; - lenAudioIn10msTmp[senderIdx] = 0; - //if(senderIdx == (NUM_CLIENTS - 1)) - //{ - printf(" %0.1f \n", lenAudioIn10ms[senderIdx] * 10. /1000); - //} - - // After ~20 sec change the bottleneck. - // if((numPrint[senderIdx] == 4) && (codingMode == 0)) - // { - // numPrint[senderIdx] = 0; - // if(codingMode == 0) - // { - // int newBottleneck = bottleneck[senderIdx] + - // (bottleneckChange[senderIdx] * 1000); - - // if(bottleneckChange[senderIdx] > 0) - // { - // if(newBottleneck >maxBn) - // { - // bottleneckChange[senderIdx] = -1; - // newBottleneck = bottleneck[senderIdx] + - // (bottleneckChange[senderIdx] * 1000); - // if(newBottleneck > minBn) - // { - // bottleneck[senderIdx] = newBottleneck; - // } - // } - // else - // { - // bottleneck[senderIdx] = newBottleneck; - // } - // } - // else - // { - // if(newBottleneck < minBn) - // { - // bottleneckChange[senderIdx] = 1; - // newBottleneck = bottleneck[senderIdx] + - // (bottleneckChange[senderIdx] * 1000); - // if(newBottleneck < maxBn) - // { - // bottleneck[senderIdx] = newBottleneck; - // } - // } - // else - // { - // bottleneck[senderIdx] = newBottleneck; - // } - // } - // } - // } - } - - // model a channel of given bottleneck, to get the receive timestamp - get_arrival_time(num10ms[senderIdx] * samplesIn10ms[senderIdx], - streamLen, bottleneck[senderIdx], packetData[senderIdx], - encoderSampRate[senderIdx]*1000, encoderSampRate[senderIdx]*1000); - - // Write the arrival time. - if(senderIdx == 0) - { - fwrite(&(packetData[senderIdx]->arrival_time), sizeof(unsigned int), 1, - arrivalTimeFile1); - } - else - { - fwrite(&(packetData[senderIdx]->arrival_time), sizeof(unsigned int), 1, - arrivalTimeFile2); - } - - // BWE - if(WebRtcIsac_UpdateBwEstimate(codecInstance[receiverIdx], - bitStream, streamLen, packetData[senderIdx]->rtp_number, - packetData[senderIdx]->sample_count, - packetData[senderIdx]->arrival_time) < 0) - { - printf(" BWE Error at client %d \n", receiverIdx + 1); - return -1; - } - /**/ - // Decode - lenDecodedAudio = WebRtcIsac_Decode( - codecInstance[receiverIdx], bitStream, streamLen, - audioBuff60ms, speechType); - if(lenDecodedAudio < 0) - { - printf(" Decoder error in client %d \n", receiverIdx + 1); - return -1; - } - - - if(encoderSampRate[senderIdx] == kIsacWideband) - { - WebRtcSpl_UpsampleBy2(audioBuff60ms, lenDecodedAudio, resampledAudio60ms, - resamplerState[receiverIdx]); - fwrite(resampledAudio60ms, sizeof(short), lenDecodedAudio << 1, - outFile[receiverIdx]); - } - else - { - fwrite(audioBuff60ms, sizeof(short), lenDecodedAudio, - outFile[receiverIdx]); - } - num10ms[senderIdx] = 0; - } - //} - //} - } - } -} diff --git a/modules/audio_coding/codecs/iSAC/main/test/debugUtility.h b/modules/audio_coding/codecs/iSAC/main/test/debugUtility.h deleted file mode 100644 index d708ad1fe..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/debugUtility.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_TEST_DEBUGUTILITY_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_TEST_DEBUGUTILITY_H_ - -#include -#include -#include "utility.h" - -typedef struct -{ - FILE* res0to4FilePtr; - FILE* res4to8FilePtr; - FILE* res8to12FilePtr; - FILE* res8to16FilePtr; - - FILE* res0to4DecFilePtr; - FILE* res4to8DecFilePtr; - FILE* res8to12DecFilePtr; - FILE* res8to16DecFilePtr; - - FILE* in0to4FilePtr; - FILE* in4to8FilePtr; - FILE* in8to12FilePtr; - FILE* in8to16FilePtr; - - FILE* out0to4FilePtr; - FILE* out4to8FilePtr; - FILE* out8to12FilePtr; - FILE* out8to16FilePtr; - - FILE* fftFilePtr; - FILE* fftDecFilePtr; - - FILE* arrivalTime; - - float lastArrivalTime; - - int prevPacketLost; - int currPacketLost; - int nextPacketLost; - - //double residualSignal4kHZ[240]; - int packetLossPercent; - - int maxPayloadLB; - int maxPayloadUB; - int lbBytes; - int ubBytes; - - -}debugStruct; - - -#define PRINT_ENTROPY_INFO(obj) \ - do \ - { \ - printf("%10u, %u; ", \ - obj->bitstr_obj.streamval, obj->bitstr_obj.stream_index); \ - } while(0) - -int setupDebugStruct(debugStruct* str); - -#endif \ No newline at end of file diff --git a/modules/audio_coding/codecs/iSAC/main/test/simpleKenny.c b/modules/audio_coding/codecs/iSAC/main/test/simpleKenny.c deleted file mode 100644 index de818f434..000000000 --- a/modules/audio_coding/codecs/iSAC/main/test/simpleKenny.c +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* kenny.c - Main function for the iSAC coder */ - -#include -#include -#include -#include - -#ifdef WIN32 -#include "windows.h" -#define CLOCKS_PER_SEC 1000 -#endif - -#include -#include - -/* include API */ -#include "isac.h" -#include "utility.h" -//#include "commonDefs.h" - -/* max number of samples per frame (= 60 ms frame) */ -#define MAX_FRAMESAMPLES_SWB 1920 -/* number of samples per 10ms frame */ -#define FRAMESAMPLES_SWB_10ms 320 -#define FRAMESAMPLES_WB_10ms 160 - -/* sampling frequency (Hz) */ -#define FS_SWB 32000 -#define FS_WB 16000 - - -//#define CHANGE_OUTPUT_NAME - -#ifdef HAVE_DEBUG_INFO - #include "debugUtility.h" - debugStruct debugInfo; -#endif - -unsigned long framecnt = 0; - - - -int main(int argc, char* argv[]) -{ - //--- File IO ---- - FILE* inp; - FILE* outp; - char inname[500]; - char outname[500]; - char usageFileName[500] = "usage.txt"; - - /* Runtime statistics */ - double starttime; - double runtime; - double length_file; - double rate; - double rateRCU; - double rateLB; - double rateUB; - unsigned long totalbits = 0; - unsigned long totalBitsRCU = 0; - unsigned long totalsmpls =0; - - WebRtc_Word32 bottleneck = 39; - WebRtc_Word16 frameSize = 30; /* ms */ - WebRtc_Word16 codingMode = 1; - WebRtc_Word16 shortdata[FRAMESAMPLES_SWB_10ms]; - WebRtc_Word16 decoded[MAX_FRAMESAMPLES_SWB]; - //WebRtc_UWord16 streamdata[1000]; - WebRtc_Word16 speechType[1]; - WebRtc_Word16 payloadLimit; - WebRtc_Word32 rateLimit; - ISACStruct* ISAC_main_inst; - - WebRtc_Word16 stream_len = 0; - WebRtc_Word16 declen; - WebRtc_Word16 err; - WebRtc_Word16 cur_framesmpls; - int endfile; - char outDrive[10]; - char outPath[500]; - char outPrefix[500]; - char outSuffix[500]; - char bitrateFileName[500]; - FILE* bitrateFile; - FILE* histFile; - FILE* averageFile; - int sampFreqKHz; - int samplesIn10Ms; - WebRtc_Word16 maxStreamLen = 0; - char histFileName[500]; - char averageFileName[500]; - unsigned int hist[600]; - unsigned int tmpSumStreamLen = 0; - unsigned int packetCntr = 0; - unsigned int lostPacketCntr = 0; - WebRtc_UWord16 payload[600]; - WebRtc_UWord16 payloadRCU[600]; - WebRtc_UWord16 packetLossPercent = 0; - WebRtc_Word16 rcuStreamLen; - int onlyEncode; - int onlyDecode; - - - BottleNeckModel packetData; - packetData.arrival_time = 0; - packetData.sample_count = 0; - packetData.rtp_number = 0; - memset(hist, 0, sizeof(hist)); - - /* handling wrong input arguments in the command line */ - if(argc < 5) - { - int size; - WebRtcIsac_AssignSize(&size); - - printf("\n\nWrong number of arguments or flag values.\n\n"); - - printf("Usage:\n\n"); - printf("%s infile outfile -bn bottelneck [options] \n\n", argv[0]); - printf("with:\n"); - printf("-I................... indicates encoding in instantaneous mode.\n"); - printf("-bn bottleneck....... the value of the bottleneck in bit/sec, e.g. 39742,\n"); - printf(" in instantaneous (channel-independent) mode.\n\n"); - printf("infile............... Normal speech input file\n\n"); - printf("outfile.............. Speech output file\n\n"); - printf("OPTIONS\n"); - printf("-------\n"); - printf("-fs sampFreq......... sampling frequency of codec 16 or 32 (default) kHz.\n"); - printf("-plim payloadLim..... payload limit in bytes,\n"); - printf(" default is the maximum possible.\n"); - printf("-rlim rateLim........ rate limit in bits/sec, \n"); - printf(" default is the maimum possible.\n"); - printf("-h file.............. record histogram and *append* to 'file'.\n"); - printf("-ave file............ record average rate of 3 sec intervales and *append* to 'file'.\n"); - printf("-ploss............... packet-loss percentage.\n"); - printf("-enc................. do only encoding and store the bit-stream\n"); - printf("-dec................. the input file is a bit-stream, decode it.\n"); - - printf("\n"); - printf("Example usage:\n\n"); - printf("%s speechIn.pcm speechOut.pcm -B 40000 -fs 32 \n\n", argv[0]); - - printf("structure size %d bytes\n", size); - - exit(0); - } - - - - /* Get Bottleneck value */ - bottleneck = readParamInt(argc, argv, "-bn", 50000); - fprintf(stderr,"\nfixed bottleneck rate of %d bits/s\n\n", bottleneck); - - /* Get Input and Output files */ - sscanf(argv[1], "%s", inname); - sscanf(argv[2], "%s", outname); - codingMode = readSwitch(argc, argv, "-I"); - sampFreqKHz = (WebRtc_Word16)readParamInt(argc, argv, "-fs", 32); - if(readParamString(argc, argv, "-h", histFileName, 500) > 0) - { - histFile = fopen(histFileName, "a"); - if(histFile == NULL) - { - printf("cannot open hist file %s", histFileName); - exit(0); - } - } - else - { - // NO recording of hitstogram - histFile = NULL; - } - - - packetLossPercent = readParamInt(argc, argv, "-ploss", 0); - - if(readParamString(argc, argv, "-ave", averageFileName, 500) > 0) - { - averageFile = fopen(averageFileName, "a"); - if(averageFile == NULL) - { - printf("cannot open file to write rate %s", averageFileName); - exit(0); - } - } - else - { - averageFile = NULL; - } - - onlyEncode = readSwitch(argc, argv, "-enc"); - onlyDecode = readSwitch(argc, argv, "-dec"); - - - switch(sampFreqKHz) - { - case 16: - { - samplesIn10Ms = 160; - break; - } - case 32: - { - samplesIn10Ms = 320; - break; - } - default: - printf("A sampling frequency of %d kHz is not supported,\ -valid values are 8 and 16.\n", sampFreqKHz); - exit(-1); - } - payloadLimit = (WebRtc_Word16)readParamInt(argc, argv, "-plim", 400); - rateLimit = readParamInt(argc, argv, "-rlim", 106800); - - if ((inp = fopen(inname,"rb")) == NULL) { - printf(" iSAC: Cannot read file %s.\n", inname); - exit(1); - } - if ((outp = fopen(outname,"wb")) == NULL) { - printf(" iSAC: Cannot write file %s.\n", outname); - exit(1); - } - -#ifdef WIN32 - _splitpath(outname, outDrive, outPath, outPrefix, outSuffix); - _makepath(bitrateFileName, outDrive, outPath, "bitrate", ".txt"); - - bitrateFile = fopen(bitrateFileName, "a"); - fprintf(bitrateFile, "% %%s \n", inname); -#endif - - printf("\n"); - printf("Input.................... %s\n", inname); - printf("Output................... %s\n", outname); - printf("Encoding Mode............ %s\n", - (codingMode == 1)? "Channel-Independent":"Channel-Adaptive"); - printf("Bottleneck............... %d bits/sec\n", bottleneck); - printf("Packet-loss Percentage... %d\n", packetLossPercent); - printf("\n"); - - starttime = clock()/(double)CLOCKS_PER_SEC; /* Runtime statistics */ - - /* Initialize the ISAC and BN structs */ - err = WebRtcIsac_Create(&ISAC_main_inst); - - WebRtcIsac_SetEncSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband: kIsacSuperWideband); - WebRtcIsac_SetDecSampRate(ISAC_main_inst, (sampFreqKHz == 16)? kIsacWideband: kIsacSuperWideband); - /* Error check */ - if (err < 0) { - fprintf(stderr,"\n\n Error in create.\n\n"); - exit(EXIT_FAILURE); - } - - framecnt = 0; - endfile = 0; - - /* Initialize encoder and decoder */ - if(WebRtcIsac_EncoderInit(ISAC_main_inst, codingMode) < 0) - { - printf("cannot initialize encoder\n"); - return -1; - } - if(WebRtcIsac_DecoderInit(ISAC_main_inst) < 0) - { - printf("cannot initialize decoder\n"); - return -1; - } - - //{ - // WebRtc_Word32 b1, b2; - // FILE* fileID = fopen("GetBNTest.txt", "w"); - // b2 = 32100; - // while(b2 <= 52000) - // { - // WebRtcIsac_Control(ISAC_main_inst, b2, frameSize); - // WebRtcIsac_GetUplinkBw(ISAC_main_inst, &b1); - // fprintf(fileID, "%5d %5d\n", b2, b1); - // b2 += 10; - // } - //} - - if(codingMode == 1) - { - if(WebRtcIsac_Control(ISAC_main_inst, bottleneck, frameSize) < 0) - { - printf("cannot set bottleneck\n"); - return -1; - } - } - else - { - if(WebRtcIsac_ControlBwe(ISAC_main_inst, 15000, 30, 1) < 0) - { - printf("cannot configure BWE\n"); - return -1; - } - } - - if(WebRtcIsac_SetMaxPayloadSize(ISAC_main_inst, payloadLimit) < 0) - { - printf("cannot set maximum payload size %d.\n", payloadLimit); - return -1; - } - - if (rateLimit < 106800) { - if(WebRtcIsac_SetMaxRate(ISAC_main_inst, rateLimit) < 0) - { - printf("cannot set the maximum rate %d.\n", rateLimit); - return -1; - } - } - - //===================================== -//#ifdef HAVE_DEBUG_INFO -// if(setupDebugStruct(&debugInfo) < 0) -// { -// exit(1); -// } -//#endif - - while (endfile == 0) - { - fprintf(stderr," \rframe = %7li", framecnt); - - //============== Readind from the file and encoding ================= - cur_framesmpls = 0; - stream_len = 0; - - - if(onlyDecode) - { - WebRtc_UWord8 auxUW8; - if(fread(&auxUW8, sizeof(WebRtc_UWord8), 1, inp) < 1) - { - break; - } - stream_len = ((WebRtc_UWord8)auxUW8) << 8; - if(fread(&auxUW8, sizeof(WebRtc_UWord8), 1, inp) < 1) - { - break; - } - stream_len |= (WebRtc_UWord16)auxUW8; - if(fread(payload, 1, stream_len, inp) < stream_len) - { - printf("last payload is corrupted\n"); - break; - } - } - else - { - while(stream_len == 0) - { - // Read 10 ms speech block - endfile = readframe(shortdata, inp, samplesIn10Ms); - if(endfile) - { - break; - } - cur_framesmpls += samplesIn10Ms; - - //-------- iSAC encoding --------- - if(framecnt == 11) - { - framecnt = framecnt; - } - stream_len = WebRtcIsac_Encode(ISAC_main_inst, shortdata, - (WebRtc_Word16*)payload); - - if(stream_len < 0) - { - // exit if returned with error - //errType=WebRtcIsac_GetErrorCode(ISAC_main_inst); - fprintf(stderr,"\nError in encoder\n"); - getchar(); - exit(EXIT_FAILURE); - } - - - } - //=================================================================== - if(endfile) - { - break; - } - - rcuStreamLen = WebRtcIsac_GetRedPayload(ISAC_main_inst, (WebRtc_Word16*)payloadRCU); - - get_arrival_time(cur_framesmpls, stream_len, bottleneck, &packetData, - sampFreqKHz * 1000, sampFreqKHz * 1000); - if(WebRtcIsac_UpdateBwEstimate(ISAC_main_inst, - payload, stream_len, packetData.rtp_number, - packetData.sample_count, - packetData.arrival_time) < 0) - { - printf(" BWE Error at client\n"); - return -1; - } - } - - if(endfile) - { - break; - } - - maxStreamLen = (stream_len > maxStreamLen)? stream_len:maxStreamLen; - packetCntr++; - - hist[stream_len]++; - if(averageFile != NULL) - { - tmpSumStreamLen += stream_len; - if(packetCntr == 100) - { - // kbps - fprintf(averageFile, "%8.3f ", (double)tmpSumStreamLen * 8.0 / (30.0 * packetCntr)); - packetCntr = 0; - tmpSumStreamLen = 0; - } - } - - if(onlyEncode) - { - WebRtc_UWord8 auxUW8; - auxUW8 = (WebRtc_UWord8)(((stream_len & 0x7F00) >> 8) & 0xFF); - fwrite(&auxUW8, sizeof(WebRtc_UWord8), 1, outp); - - auxUW8 = (WebRtc_UWord8)(stream_len & 0xFF); - fwrite(&auxUW8, sizeof(WebRtc_UWord8), 1, outp); - fwrite(payload, 1, stream_len, outp); - } - else - { - - //======================= iSAC decoding =========================== - - if((rand() % 100) < packetLossPercent) - { - declen = WebRtcIsac_DecodeRcu(ISAC_main_inst, payloadRCU, - rcuStreamLen, decoded, speechType); - lostPacketCntr++; - } - else - { - declen = WebRtcIsac_Decode(ISAC_main_inst, payload, - stream_len, decoded, speechType); - } - if(declen <= 0) - { - //errType=WebRtcIsac_GetErrorCode(ISAC_main_inst); - fprintf(stderr,"\nError in decoder.\n"); - getchar(); - exit(1); - } - - // Write decoded speech frame to file - fwrite(decoded, sizeof(WebRtc_Word16), declen, outp); - cur_framesmpls = declen; - } - // Update Statistics - framecnt++; - totalsmpls += cur_framesmpls; - if(stream_len > 0) - { - totalbits += 8 * stream_len; - } - if(rcuStreamLen > 0) - { - totalBitsRCU += 8 * rcuStreamLen; - } - } - - rate = ((double)totalbits * (sampFreqKHz)) / (double)totalsmpls; - rateRCU = ((double)totalBitsRCU * (sampFreqKHz)) / (double)totalsmpls; - rateLB = 0; - rateUB = 0; - - printf("\n\n"); - printf("Sampling Rate......................... %d kHz\n", sampFreqKHz); - printf("Payload Limit......................... %d bytes \n", payloadLimit); - printf("Rate Limit............................ %d bits/sec \n", rateLimit); -#ifdef HAVE_DEBUG_INFO - rateLB = ((double)debugInfo.lbBytes * 8. * (sampFreqKHz)) / (double)totalsmpls; - rateUB = ((double)debugInfo.ubBytes * 8. * (sampFreqKHz)) / (double)totalsmpls; -#endif - -#ifdef WIN32 - fprintf(bitrateFile, "%d %10u %d %6.3f %6.3f %6.3f\n", - sampFreqKHz, - framecnt, - bottleneck, - rateLB, - rateUB, - rate); - fclose(bitrateFile); -#endif - - printf("\n"); - printf("Measured bit-rate..................... %0.3f kbps\n", rate); - printf("Measured RCU bit-ratre................ %0.3f kbps\n", rateRCU); - printf("Maximum bit-rate/payloadsize.......... %0.3f / %d\n", - maxStreamLen * 8 / 0.03, maxStreamLen); - printf("Measured packet-loss.................. %0.1f%% \n", - 100.0f * (float)lostPacketCntr / (float)packetCntr); - -//#ifdef HAVE_DEBUG_INFO -// printf("Measured lower-band bit-rate.......... %0.3f kbps (%.0f%%)\n", -// rateLB, (double)(rateLB) * 100. /(double)(rate)); -// printf("Measured upper-band bit-rate.......... %0.3f kbps (%.0f%%)\n", -// rateUB, (double)(rateUB) * 100. /(double)(rate)); -// -// printf("Maximum payload lower-band............ %d bytes (%0.3f kbps)\n", -// debugInfo.maxPayloadLB, debugInfo.maxPayloadLB * 8.0 / 0.03); -// printf("Maximum payload upper-band............ %d bytes (%0.3f kbps)\n", -// debugInfo.maxPayloadUB, debugInfo.maxPayloadUB * 8.0 / 0.03); -//#endif - - printf("\n"); - - /* Runtime statistics */ -#ifdef WIN32 - runtime = (double)(clock()/(double)CLOCKS_PER_SEC-starttime); - length_file = ((double)framecnt*(double)declen/(sampFreqKHz*1000)); - printf("Length of speech file................ %.1f s\n", length_file); - printf("Time to run iSAC..................... %.2f s (%.2f %% of realtime)\n\n", - runtime, (100*runtime/length_file)); -#endif - printf("\n\n_______________________________________________\n"); - - if(histFile != NULL) - { - int n; - for(n = 0; n < 600; n++) - { - fprintf(histFile, "%6d ", hist[n]); - } - fprintf(histFile, "\n"); - fclose(histFile); - } - if(averageFile != NULL) - { - if(packetCntr > 0) - { - fprintf(averageFile, "%8.3f ", (double)tmpSumStreamLen * 8.0 / (30.0 * packetCntr)); - } - fprintf(averageFile, "\n"); - fclose(averageFile); - } - - fclose(inp); - fclose(outp); - - WebRtcIsac_Free(ISAC_main_inst); - - -#ifdef CHANGE_OUTPUT_NAME - { - char* p; - char myExt[50]; - char bitRateStr[10]; - char newOutName[500]; - strcpy(newOutName, outname); - - myExt[0] = '\0'; - p = strchr(newOutName, '.'); - if(p != NULL) - { - strcpy(myExt, p); - *p = '_'; - p++; - *p = '\0'; - } - else - { - strcat(newOutName, "_"); - } - sprintf(bitRateStr, "%0.0fkbps", rate); - strcat(newOutName, bitRateStr); - strcat(newOutName, myExt); - rename(outname, newOutName); - } -#endif - exit(0); -} - - -#ifdef HAVE_DEBUG_INFO -int setupDebugStruct(debugStruct* str) -{ - str->prevPacketLost = 0; - str->currPacketLost = 0; - - OPEN_FILE_WB(str->res0to4FilePtr, "Res0to4.dat"); - OPEN_FILE_WB(str->res4to8FilePtr, "Res4to8.dat"); - OPEN_FILE_WB(str->res8to12FilePtr, "Res8to12.dat"); - OPEN_FILE_WB(str->res8to16FilePtr, "Res8to16.dat"); - - OPEN_FILE_WB(str->res0to4DecFilePtr, "Res0to4Dec.dat"); - OPEN_FILE_WB(str->res4to8DecFilePtr, "Res4to8Dec.dat"); - OPEN_FILE_WB(str->res8to12DecFilePtr, "Res8to12Dec.dat"); - OPEN_FILE_WB(str->res8to16DecFilePtr, "Res8to16Dec.dat"); - - OPEN_FILE_WB(str->in0to4FilePtr, "in0to4.dat"); - OPEN_FILE_WB(str->in4to8FilePtr, "in4to8.dat"); - OPEN_FILE_WB(str->in8to12FilePtr, "in8to12.dat"); - OPEN_FILE_WB(str->in8to16FilePtr, "in8to16.dat"); - - OPEN_FILE_WB(str->out0to4FilePtr, "out0to4.dat"); - OPEN_FILE_WB(str->out4to8FilePtr, "out4to8.dat"); - OPEN_FILE_WB(str->out8to12FilePtr, "out8to12.dat"); - OPEN_FILE_WB(str->out8to16FilePtr, "out8to16.dat"); - OPEN_FILE_WB(str->fftFilePtr, "riFFT.dat"); - OPEN_FILE_WB(str->fftDecFilePtr, "riFFTDec.dat"); - - OPEN_FILE_WB(str->arrivalTime, NULL/*"ArivalTime.dat"*/); - str->lastArrivalTime = 0; - - str->maxPayloadLB = 0; - str->maxPayloadUB = 0; - str->lbBytes = 0; - str->ubBytes = 0; - - return 0; -}; -#endif diff --git a/modules/audio_coding/codecs/iSAC/main/util/utility.c b/modules/audio_coding/codecs/iSAC/main/util/utility.c deleted file mode 100644 index 85734827c..000000000 --- a/modules/audio_coding/codecs/iSAC/main/util/utility.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include -#include "utility.h" - -/* function for reading audio data from PCM file */ -int -readframe( - short* data, - FILE* inp, - int length) -{ - short k, rlen, status = 0; - unsigned char* ptrUChar; - ptrUChar = (unsigned char*)data; - - rlen = (short)fread(data, sizeof(short), length, inp); - if (rlen < length) { - for (k = rlen; k < length; k++) - data[k] = 0; - status = 1; - } - - // Assuming that our PCM files are written in Intel machines - for(k = 0; k < length; k++) - { - data[k] = (short)ptrUChar[k<<1] | ((((short)ptrUChar[(k<<1) + 1]) << 8) & 0xFF00); - } - - return status; -} - -short -readSwitch( - int argc, - char* argv[], - char* strID) -{ - short n; - for(n = 0; n < argc; n++) - { - if(strcmp(argv[n], strID) == 0) - { - return 1; - } - } - return 0; -} - -double -readParamDouble( - int argc, - char* argv[], - char* strID, - double defaultVal) -{ - double returnVal = defaultVal; - short n; - for(n = 0; n < argc; n++) - { - if(strcmp(argv[n], strID) == 0) - { - n++; - if(n < argc) - { - returnVal = atof(argv[n]); - } - break; - } - } - return returnVal; -} - -int -readParamInt( - int argc, - char* argv[], - char* strID, - int defaultVal) -{ - int returnVal = defaultVal; - short n; - for(n = 0; n < argc; n++) - { - if(strcmp(argv[n], strID) == 0) - { - n++; - if(n < argc) - { - returnVal = atoi(argv[n]); - } - break; - } - } - return returnVal; -} - -int -readParamString( - int argc, - char* argv[], - char* strID, - char* stringParam, - int maxSize) -{ - int paramLenght = 0; - short n; - for(n = 0; n < argc; n++) - { - if(strcmp(argv[n], strID) == 0) - { - n++; - if(n < argc) - { - strncpy(stringParam, argv[n], maxSize); - paramLenght = (int)strlen(argv[n]); - } - break; - } - } - return paramLenght; -} - -void -get_arrival_time( - int current_framesamples, /* samples */ - int packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel* BN_data, - short senderSampFreqHz, - short receiverSampFreqHz) -{ - unsigned int travelTimeMs; - const int headerSizeByte = 35; - unsigned int dummy; - - int headerRate; - - BN_data->whenPackGeneratedMs += (current_framesamples / (senderSampFreqHz / 1000)); - - headerRate = headerSizeByte * 8 * senderSampFreqHz / current_framesamples; /* bits/s */ - - /* everything in samples */ - BN_data->sample_count = BN_data->sample_count + current_framesamples; - - //travelTimeMs = ((packet_size + HeaderSize) * 8 * sampFreqHz) / - // (bottleneck + HeaderRate) - travelTimeMs = (unsigned int)floor((double)((packet_size + headerSizeByte) * 8 * 1000) - / (double)(bottleneck + headerRate) + 0.5); - - if(BN_data->whenPrevPackLeftMs > BN_data->whenPackGeneratedMs) - { - BN_data->whenPrevPackLeftMs += travelTimeMs; - } - else - { - BN_data->whenPrevPackLeftMs = BN_data->whenPackGeneratedMs + - travelTimeMs; - } - - - dummy = (BN_data->whenPrevPackLeftMs * - (receiverSampFreqHz / 1000)) - BN_data->arrival_time; - BN_data->arrival_time = (BN_data->whenPrevPackLeftMs * - (receiverSampFreqHz / 1000)); - - - -// if (BN_data->arrival_time < BN_data->sample_count) -// BN_data->arrival_time = BN_data->sample_count; - - BN_data->rtp_number++; -} diff --git a/modules/audio_coding/codecs/iSAC/main/util/utility.h b/modules/audio_coding/codecs/iSAC/main/util/utility.h deleted file mode 100644 index f9fba9431..000000000 --- a/modules/audio_coding/codecs/iSAC/main/util/utility.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_UTIL_UTILITY_H_ -#define WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_UTIL_UTILITY_H_ - -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -#define OPEN_FILE_WB(filePtr, fullPath) \ - do \ - { \ - if(fullPath != NULL) \ - { \ - filePtr = fopen(fullPath, "wb"); \ - if(filePtr == NULL) \ - { \ - printf("could not open %s to write to.", fullPath); \ - return -1; \ - } \ - } \ - else \ - { \ - filePtr = NULL; \ - } \ - }while(0) - -#define OPEN_FILE_AB(filePtr, fullPath) \ - do \ - { \ - if(fullPath != NULL) \ - { \ - filePtr = fopen(fullPath, "ab"); \ - if(filePtr == NULL) \ - { \ - printf("could not open %s to write to.", fullPath); \ - return -1; \ - } \ - } \ - else \ - { \ - filePtr = NULL; \ - } \ - }while(0) - -#define OPEN_FILE_RB(filePtr, fullPath) \ - do \ - { \ - if(fullPath != NULL) \ - { \ - filePtr = fopen(fullPath, "rb"); \ - if(filePtr == NULL) \ - { \ - printf("could not open %s to read from.", fullPath); \ - return -1; \ - } \ - } \ - else \ - { \ - filePtr = NULL; \ - } \ - }while(0) - -#define WRITE_FILE_D(bufferPtr, len, filePtr) \ - do \ - { \ - if(filePtr != NULL) \ - { \ - double dummy[1000]; \ - int cntr; \ - for(cntr = 0; cntr < (len); cntr++) \ - { \ - dummy[cntr] = (double)bufferPtr[cntr]; \ - } \ - fwrite(dummy, sizeof(double), len, filePtr); \ - fflush(filePtr); \ - } \ - } while(0) - - typedef struct { - unsigned int whenPackGeneratedMs; - unsigned int whenPrevPackLeftMs; - unsigned int sendTimeMs ; /* milisecond */ - unsigned int arrival_time; /* samples */ - unsigned int sample_count; /* samples, also used as "send time stamp" */ - unsigned int rtp_number; - } BottleNeckModel; - - void get_arrival_time( - int current_framesamples, /* samples */ - int packet_size, /* bytes */ - int bottleneck, /* excluding headers; bits/s */ - BottleNeckModel* BN_data, - short senderSampFreqHz, - short receiverSampFreqHz); - - /* function for reading audio data from PCM file */ - int readframe( - short* data, - FILE* inp, - int length); - - short readSwitch( - int argc, - char* argv[], - char* strID); - - double readParamDouble( - int argc, - char* argv[], - char* strID, - double defaultVal); - - int readParamInt( - int argc, - char* argv[], - char* strID, - int defaultVal); - - int readParamString( - int argc, - char* argv[], - char* strID, - char* stringParam, - int maxSize); - -#if defined(__cplusplus) -} -#endif - - - -#endif diff --git a/modules/audio_coding/main/OWNERS b/modules/audio_coding/main/OWNERS deleted file mode 100644 index a7220e7ea..000000000 --- a/modules/audio_coding/main/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -tlegrand@google.com -turajs@google.com -jks@google.com diff --git a/modules/audio_coding/main/interface/audio_coding_module.h b/modules/audio_coding/main/interface/audio_coding_module.h deleted file mode 100644 index 7164e2999..000000000 --- a/modules/audio_coding/main/interface/audio_coding_module.h +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef AUDIO_CODING_MODULE_H -#define AUDIO_CODING_MODULE_H - -#include "module.h" -#include "audio_coding_module_typedefs.h" -#include "module_common_types.h" - - -namespace webrtc -{ - -// forward declarations -struct CodecInst; - - -#define WEBRTC_10MS_PCM_AUDIO 960 // 16 bits super wideband 48 Khz - - -// Callback class used for sending data ready to be packetized -class AudioPacketizationCallback -{ -public: - virtual ~AudioPacketizationCallback() {} - - virtual WebRtc_Word32 SendData( - FrameType frameType, - WebRtc_UWord8 payloadType, - WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation) = 0; -}; - -// Callback class used for inband Dtmf detection -class AudioCodingFeedback -{ -public: - virtual ~AudioCodingFeedback() {} - - virtual WebRtc_Word32 IncomingDtmf( - const WebRtc_UWord8 digitDtmf, - const bool end) = 0; -}; - -// Callback class used for reporting VAD decision -class ACMVADCallback -{ -public: - virtual ~ACMVADCallback() {} - virtual WebRtc_Word32 InFrameType( - WebRtc_Word16 frameType) = 0; -}; - -// Callback class used for reporting receiver statistics -class ACMVQMonCallback -{ -public: - virtual ~ACMVQMonCallback() {} - - virtual WebRtc_Word32 NetEqStatistics( - const WebRtc_Word32 id, // current ACM id - const WebRtc_UWord16 MIUsValid, // valid voice duration in ms - const WebRtc_UWord16 MIUsReplaced, // concealed voice duration in ms - const WebRtc_UWord8 eventFlags, // concealed voice flags - const WebRtc_UWord16 delayMS) = 0; // average delay in ms -}; - -class AudioCodingModule: public Module -{ -protected: - AudioCodingModule(){} - virtual ~AudioCodingModule(){} - -public: - /////////////////////////////////////////////////////////////////////////// - // Creation and destruction of a ACM - // - static AudioCodingModule* Create( - const WebRtc_Word32 id); - - static void Destroy( - AudioCodingModule* module); - - /////////////////////////////////////////////////////////////////////////// - // Utility functions - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 GetVersion() - // Returns version of the module and its components. - // - // Outputs: - // -version : a buffer that the version string is stored. - // -remainBuffBytes : remaining size of the buffer "version" in - // bytes, excluding terminating-null. - // -position : the first character of the ACM version will - // written to version[position] and so on. - // - // Return value: - // -1 if failed to write the whole version string, - // 0 if succeeded. - // - static WebRtc_Word32 GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainBuffBytes, - WebRtc_UWord32& position); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_UWord8 NumberOfCodecs() - // Returns number of supported codecs. - // - // Return value: - // number of supported codecs. - /// - static WebRtc_UWord8 NumberOfCodecs(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Codec() - // Get supported codec with list number. - // - // Input: - // -listId : list number. - // - // Output: - // -codec : a structure where the parameters of the codec, - // given by list number is written to. - // - // Return value: - // -1 if the list number (listId) is invalid. - // 0 if succeeded. - // - static WebRtc_Word32 Codec( - const WebRtc_UWord8 listId, - CodecInst& codec); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Codec() - // Get supported codec with the given codec name and sampling frequency. - // If the sampling frequency is -1 then the search will be only based on - // codec name. - // - // Input: - // -payloadName : name of the codec. - // -samplingFreqHz : samling frequency of the codec. - // - // Output: - // -codec : a structure where the parameters of the codec, - // given by name is written to. - // - // Return value: - // -1 if the list number (listId) is invalid. - // 0 if succeeded. - // - static WebRtc_Word32 Codec( - const WebRtc_Word8* payloadName, - CodecInst& codec, - const WebRtc_Word32 samplingFreqHz = -1); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Codec() - // - // Returns the list number of the given codec name and sampling frequency. - // If the sampling frequency is -1 then the search will be only based on - // codec name. - // - // Input: - // -payloadName : name of the codec. - // -samplingFreqHz : samling frequency of the codec. - // - // Return value: - // if the codec is found, the index of the codec in the list, - // -1 if the codec is not found. - // - static WebRtc_Word32 Codec( - const WebRtc_Word8* payloadName, - const WebRtc_Word32 samplingFreqHz = -1); - - - /////////////////////////////////////////////////////////////////////////// - // bool IsCodecValid() - // Checks the validity of the parameters of the given codec. - // - // Input: - // -codec : the structur which keeps the parameters of the - // codec. - // - // Reurn value: - // true if the parameters are valid, - // false if any parameter is not valid. - // - static bool IsCodecValid(const CodecInst& codec); - - /////////////////////////////////////////////////////////////////////////// - // Sender - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 InitializeSender() - // Any encoder-related state of ACM will be initialized to the - // same state when ACM is created. This will not interrupt or - // effect decoding functionality of ACM. ACM will lose all the - // encoding-related settings by calling this function. - // For instance, a send codec has to be registered again. - // - // Return value: - // -1 if failed to initialize, - // 0 if succeeded. - // - virtual WebRtc_Word32 InitializeSender() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ResetEncoder() - // This API resets the states of encoder. All the encoder settings, such as - // send-codec or VAD/DTX, will be preserved. - // - // Return value: - // -1 if failed to initialize, - // 0 if succeeded. - // - virtual WebRtc_Word32 ResetEncoder() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterSendCodec() - // Registers a codec, specified by "sendCodec," as sending codec. - // This API can be called multiple of times to register Codec. The last codec - // registered overwrites the previous ones. - // The API can also be used to change payload type for CNG and RED, which are - // registered by default to default payload types. - // Note that registering CNG and RED won't overwrite speech codecs. - // This API can be called to set/change the send payload-type, frame-size - // or encoding rate (if applicable for the codec). - // - // Input: - // -sendCodec : Parameters of the codec to be registered, c.f. - // common_types.h for the definition of - // CodecInst. - // - // Return value: - // -1 if failed to initialize, - // 0 if succeeded. - // - virtual WebRtc_Word32 RegisterSendCodec( - const CodecInst& sendCodec) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SendCodec() - // Get parameters for the codec currently registered as send codec. - // - // Output: - // -currentSendCodec : parameters of the send codec. - // - // Return value: - // -1 if failed to get send codec, - // 0 if succeeded. - // - virtual WebRtc_Word32 SendCodec( - CodecInst& currentSendCodec) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SendFrequency() - // Get the sampling frequency of the current encoder in Hertz. - // - // Return value: - // positive; sampling frequency [Hz] of the current encoder. - // -1 if an error has happened. - // - virtual WebRtc_Word32 SendFrequency() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Bitrate() - // Get encoding bit-rate in bits per second. - // - // Return value: - // positive; encoding rate in bits/sec, - // -1 if an error is happened. - // - virtual WebRtc_Word32 SendBitrate() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetReceivedEstimatedBandwidth() - // Set available bandwidth [bits/sec] of the up-link channel. - // This information is used for traffic shaping, and is currently only - // supported if iSAC is the send codec. - // - // Input: - // -bw : bandwidth in bits/sec estimated for - // up-link. - // Return value - // -1 if error occurred in setting the bandwidth, - // 0 bandwidth is set successfully. - // - virtual WebRtc_Word32 SetReceivedEstimatedBandwidth( - const WebRtc_Word32 bw) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterTransportCallback() - // Register a transport callback which will be called to deliver - // the encoded buffers whenever Process() is called and a - // bit-stream is ready. - // - // Input: - // -transport : pointer to the callback class - // transport->SendData() is called whenever - // Process() is called and bit-stream is ready - // to deliver. - // - // Return value: - // -1 if the transport callback could not be registered - // 0 if registration is successful. - // - virtual WebRtc_Word32 RegisterTransportCallback( - AudioPacketizationCallback* transport) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Add10MsData() - // Add 10MS of raw (PCM) audio data to the encoder. If the sampling - // frequency of the audio does not match the sampling frequency of the - // current encoder ACM will resample the audio. - // - // Input: - // -audioFrame : the input audio frame, containing raw audio - // sampling frequency etc., - // c.f. module_common_types.h for definition of - // AudioFrame. - // - // Return value: - // 0 successfully added the frame. - // -1 some error occurred and data is not added. - // < -1 to add the frame to the buffer n samples had to be - // overwritten, -n is the return value in this case. - // - virtual WebRtc_Word32 Add10MsData( - const AudioFrame& audioFrame) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // (FEC) Forward Error Correction - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetFECStatus(const bool enable) - // configure FEC status i.e. on/off. - // - // RFC 2198 describes a solution which has a single payload type which - // signifies a packet with redundancy. That packet then becomes a container, - // encapsulating multiple payloads into a single RTP packet. - // Such a scheme is flexible, since any amount of redundancy may be - // encapsulated within a single packet. There is, however, a small overhead - // since each encapsulated payload must be preceded by a header indicating - // the type of data enclosed. - // - // This means that FEC is actually a RED scheme. - // - // Input: - // -enableFEC : if true FEC is enabled, otherwise FEC is - // disabled. - // - // Return value: - // -1 if failed to set FEC status, - // 0 if succeeded. - // - virtual WebRtc_Word32 SetFECStatus( - const bool enableFEC) = 0; - - /////////////////////////////////////////////////////////////////////////// - // bool FECStatus() - // Get FEC status - // - // Return value - // true if FEC is enabled, - // false if FEC is disabled. - // - virtual bool FECStatus() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // (VAD) Voice Activity Detection - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetVAD() - // If DTX is enabled & the codec does not have internal DTX/VAD - // WebRtc VAD will be automatically enabled and 'enableVAD' is ignored. - // - // If DTX is disabled but VAD is enabled no DTX packets are send, - // regardless of whether the codec has internal DTX/VAD or not. In this - // case, WebRtc VAD is running to label frames as active/in-active. - // - // Inputs: - // -enableDTX : if true DTX is enabled, - // otherwise DTX is disabled. - // -enableVAD : if true VAD is enabled, - // otherwise VAD is disabled. - // -vadMode : determines the aggressiveness of VAD. A more - // aggressive mode results in more frames labeled - // as in-active, c.f. definition of - // ACMVADMode in audio_coding_module_typedefs.h - // for valid values. - // - // Return value: - // -1 if failed to set up VAD/DTX, - // 0 if succeeded. - // - virtual WebRtc_Word32 SetVAD( - const bool enableDTX = true, - const bool enableVAD = false, - const ACMVADMode vadMode = VADNormal) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 VAD() - // Get VAD status. - // - // Outputs: - // -dtxEnabled : is set to true if DTX is enabled, otherwise - // is set to false. - // -vadEnabled : is set to true if VAD is enabled, otherwise - // is set to false. - // -vadMode : is set to the current aggressiveness of VAD. - // - // Return value: - // -1 if fails to retrieve the setting of DTX/VAD, - // 0 if succeeeded. - // - virtual WebRtc_Word32 VAD( - bool& dtxEnabled, - bool& vadEnabled, - ACMVADMode& vadMode) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ReplaceInternalDTXWithWebRtc() - // Used to replace codec internal DTX scheme with WebRtc. This is only - // supported for G729, where this call replaces AnnexB with WebRtc DTX. - // - // Input: - // -useWebRtcDTX : if false (default) the codec built-in DTX/VAD - // scheme is used, otherwise the internal DTX is - // replaced with WebRtc DTX/VAD. - // - // Return value: - // -1 if failed to replace codec internal DTX with WebRtc, - // 0 if succeeded. - // - virtual WebRtc_Word32 ReplaceInternalDTXWithWebRtc( - const bool useWebRtcDTX = false) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 IsInternalDTXReplacedWithWebRtc() - // Get status if the codec internal DTX (when such exists) is replaced with - // WebRtc DTX. This is only supported for G729. - // - // Output: - // -usesWebRtcDTX : is set to true if the codec internal DTX is - // replaced with WebRtc DTX/VAD, otherwise it is set - // to false. - // - // Return value: - // -1 if failed to determine if codec internal DTX is replaced with WebRtc, - // 0 if succeeded. - // - virtual WebRtc_Word32 IsInternalDTXReplacedWithWebRtc( - bool& usesWebRtcDTX) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterVADCallback() - // Call this method to register a callback function which is called - // any time that ACM encounters an empty frame. That is a frame which is - // recognized inactive. Depending on the codec WebRtc VAD or internal codec - // VAD is employed to identify a frame as active/inactive. - // - // Input: - // -vadCallback : pointer to a callback function. - // - // Return value: - // -1 if failed to register the callback function. - // 0 if the callback function is registered successfully. - // - virtual WebRtc_Word32 RegisterVADCallback( - ACMVADCallback* vadCallback) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // Receiver - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 InitializeReceiver() - // Any decoder-related state of ACM will be initialized to the - // same state when ACM is created. This will not interrupt or - // effect encoding functionality of ACM. ACM would lose all the - // decoding-related settings by calling this function. - // For instance, all registered codecs are deleted and have to be - // registered again. - // - // Return value: - // -1 if failed to initialize, - // 0 if succeeded. - // - virtual WebRtc_Word32 InitializeReceiver() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ResetDecoder() - // This API resets the states of decoders. ACM will not lose any - // decoder-related settings, such as registered codecs. - // - // Return value: - // -1 if failed to initialize, - // 0 if succeeded. - // - virtual WebRtc_Word32 ResetDecoder() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ReceiveFrequency() - // Get sampling frequency of the last received payload. - // - // Return value: - // non-negative the sampling frequency in Hertz. - // -1 if an error has occurred. - // - virtual WebRtc_Word32 ReceiveFrequency() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 PlayoutFrequency() - // Get sampling frequency of audio played out. - // - // Return value: - // the sampling frequency in Hertz. - // - virtual WebRtc_Word32 PlayoutFrequency() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterReceiveCodec() - // Register possible decoders, can be called multiple times for - // codecs, CNG-NB, CNG-WB, CNG-SWB, AVT and RED. - // - // Input: - // -receiveCodec : parameters of the codec to be registered, c.f. - // common_types.h for the definition of - // CodecInst. - // - // Return value: - // -1 if failed to register the codec - // 0 if the codec registered successfully. - // - virtual WebRtc_Word32 RegisterReceiveCodec( - const CodecInst& receiveCodec) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 UnregisterReceiveCodec() - // Unregister the codec currently registered with a specific payload type - // from the list of possible receive codecs. - // - // Input: - // -payloadType : The number representing the payload type to - // unregister. - // - // Output: - // -1 if the unregistration fails. - // 0 if the given codec is successfully unregistered. - // - virtual WebRtc_Word32 UnregisterReceiveCodec( - const WebRtc_Word16 receiveCodec) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ReceiveCodec() - // Get the codec associated with last received payload. - // - // Output: - // -currRcvCodec : parameters of the codec associated with the last - // received payload, c.f. common_types.h for - // the definition of CodecInst. - // - // Return value: - // -1 if failed to retrieve the codec, - // 0 if the codec is successfully retrieved. - // - virtual WebRtc_Word32 ReceiveCodec( - CodecInst& currRcvCodec) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 IncomingPacket() - // Call this function to insert a parsed RTP packet into ACM. - // - // Inputs: - // -incomingPayload : received payload. - // -payloadLengthByte : the length of payload in bytes. - // -rtpInfo : the relevant information retrieved from RTP - // header. - // - // Return value: - // -1 if failed to push in the payload - // 0 if payload is successfully pushed in. - // - virtual WebRtc_Word32 IncomingPacket( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLengthByte, - const WebRtcRTPHeader& rtpInfo) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 IncomingPayload() - // Call this API to push incoming payloads when there is no rtp-info. - // The rtp-info will be created in ACM. One usage for this API is when - // pre-encoded files are pushed in ACM - // - // Inputs: - // -incomingPayload : received payload. - // -payloadLenghtByte : the length, in bytes, of the received payload. - // -payloadType : the payload-type. This specifies which codec has - // to be used to decode the payload. - // -timestamp : send timestamp of the payload. ACM starts with - // a random value and increment it by the - // packet-size, which is given when the codec in - // question is registered by RegisterReceiveCodec(). - // Therefore, it is essential to have the timestamp - // if the frame-size differ from the registered - // value or if the incoming payload contains DTX - // packets. - // - // Return value: - // -1 if failed to push in the payload - // 0 if payload is successfully pushed in. - // - virtual WebRtc_Word32 IncomingPayload( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLengthByte, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timestamp = 0) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetMinimumPlayoutDelay() - // Set Minimum playout delay, used for lip-sync. - // - // Input: - // -timeMs : minimum delay in milliseconds. - // - // Return value: - // -1 if failed to set the delay, - // 0 if the minimum delay is set. - // - virtual WebRtc_Word32 SetMinimumPlayoutDelay( - const WebRtc_Word32 timeMs) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Delay() - // Get the current playout delay. - // - // Output: - // - delayInMs : delay in millisecond - // - // Return value: - // -1 if failed to get the delay, - // 0 if succeeded to get the delay. - // - virtual WebRtc_Word32 Delay(WebRtc_UWord16& delayMs) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterIncomingMessagesCallback() - // Used by the module to deliver messages to the codec module/application - // when a Dtmf tone is detected, as well as when it stopped. - // - // Inputs: - // -inMsgCallback : pointer to callback function which will be called - // if Dtmf is detected. - // -cpt : enables CPT (Call Progress Tone) detection for the - // specified country. c.f. definition of ACMCountries - // in audio_coding_module_typedefs.h for valid - // entries. The default value disables CPT - // detection. - // - // Return value: - // -1 if the message callback could not be registered - // 0 if registration is successful. - // - virtual WebRtc_Word32 RegisterIncomingMessagesCallback( - AudioCodingFeedback* inMsgCallback, - const ACMCountries cpt = ACMDisableCountryDetection) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetDtmfPlayoutStatus() - // Configure Dtmf playout, i.e. whether out-of-band - // Dtmf tones are played or not. - // - // Input: - // -enable : if true to enable playout out-of-band Dtmf tones, - // false to disable. - // - // Return value: - // -1 if the method fails, e.g. Dtmf playout is not supported. - // 0 if the status is set successfully. - // - virtual WebRtc_Word32 SetDtmfPlayoutStatus(const bool enable) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // bool DtmfPlayoutStatus() - // Get Dtmf playout status. - // - // Return value: - // true if out-of-band Dtmf tones are played, - // false if playout of Dtmf tones is disabled. - // - virtual bool DtmfPlayoutStatus() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetBackgroundNoiseMode() - // Sets the mode of the background noise playout in an event of long - // packetloss burst. For the valid modes see the declaration of - // ACMBackgroundNoiseMode in audio_coding_module_typedefs.h. - // - // Input: - // -mode : the mode for the background noise playout. - // - // Return value: - // -1 if failed to set the mode. - // 0 if succeeded in setting the mode. - // - virtual WebRtc_Word32 SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 BackgroundNoiseMode() - // Call this method to get the mode of the background noise playout. - // Playout of background noise is a result of a long packetloss burst. - // See ACMBackgroundNoiseMode in audio_coding_module_typedefs.h for - // possible modes. - // - // Output: - // -mode : a reference to ACMBackgroundNoiseMode enumerator. - // - // Return value: - // 0 if the output is a valid mode. - // -1 if ACM failed to output a valid mode. - // - virtual WebRtc_Word32 BackgroundNoiseMode( - ACMBackgroundNoiseMode& mode) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 PlayoutTimestamp() - // The send timestamp of an RTP packet is associated with the decoded - // audio of the packet in question. This function returns the timestamp of - // the latest audio obtained by calling PlayoutData10ms(). - // - // Input: - // -timestamp : a reference to a WebRtc_UWord32 to receive the - // timestamp. - // Return value: - // 0 if the output is a correct timestamp. - // -1 if failed to output the correct timestamp. - // - // - virtual WebRtc_Word32 PlayoutTimestamp( - WebRtc_UWord32& timestamp) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 DecoderEstimatedBandwidth() - // Get the estimate of the Bandwidth, in bits/second, based on the incoming - // stream. This API is useful in one-way communication scenarios, where - // the bandwidth information is sent in an out-of-band fashion. - // Currently only supported if iSAC is registered as a reciever. - // - // Return value: - // >0 bandwidth in bits/second. - // -1 if failed to get a bandwidth estimate. - // - virtual WebRtc_Word32 DecoderEstimatedBandwidth() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetPlayoutMode() - // Call this API to set the playout mode. Playout mode could be optimized - // for i) voice, ii) FAX or iii) streaming. In Voice mode, NetEQ is - // optimized to deliver highest audio quality while maintaining a minimum - // delay. In FAX mode, NetEQ is optimized to have few delay changes as - // possible and maintain a constant delay, perhaps large relative to voice - // mode, to avoid PLC. In streaming mode, we tolerate a little more delay - // to acheive better jitter robustness. - // - // Input: - // -mode : playout mode. Possible inputs are: - // "voice", - // "fax" and - // "streaming". - // - // Return value: - // -1 if failed to set the mode, - // 0 if succeeding. - // - virtual WebRtc_Word32 SetPlayoutMode( - const AudioPlayoutMode mode) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // AudioPlayoutMode PlayoutMode() - // Get playout mode, i.e. whether it is speech, FAX or streaming. See - // audio_coding_module_typedefs.h for definition of AudioPlayoutMode. - // - // Return value: - // voice: is for voice output, - // fax: a mode that is optimized for receiving FAX signals. - // In this mode NetEq tries to maintain a constant high - // delay to avoid PLC if possible. - // streaming: a mode that is suitable for streaminq. In this mode we - // accept longer delay to improve jitter robustness. - // - virtual AudioPlayoutMode PlayoutMode() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 PlayoutData10Ms( - // Get 10 milliseconds of raw audio data for playout, at the given sampling - // frequency. ACM will perform a resampling if required. - // - // Input: - // -desiredFreqHz : the desired sampling frequency, in Hertz, of the - // output audio. If set to -1, the function returns the - // audio at the current sampling frequency. - // - // Output: - // -audioFrame : output audio frame which contains raw audio data - // and other relevant parameters, c.f. - // module_common_types.h for the definition of - // AudioFrame. - // - // Return value: - // -1 if the function fails, - // 0 if the function succeeds. - // - virtual WebRtc_Word32 PlayoutData10Ms( - const WebRtc_Word32 desiredFreqHz, - AudioFrame &audioFrame) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // (CNG) Comfort Noise Generation - // Generate comfort noise when receiving DTX packets - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SetReceiveVADStatus() - // configure VAD status i.e. on/off on the incoming stream - // Running VAD on decoded audio is desired in some applications, e.g. - // conferencing. - // - // Input: - // -enable : true to enable VAD on incoming stream, and false - // to disable. - // - // Return value: - // -1 if failed to enable/disable VAD, - // 0 if succeded to enable/disable VAD. - // - virtual WebRtc_Word16 SetReceiveVADStatus( - const bool enable) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // bool ReceiveVADStatus() - // Call this API to get whether VAD is enabled on incoming stream or not. - // Running VAD on decoded audio is desired in some applications, e.g. - // conferencing. - // - // Return value: - // true if VAD is enabled on the incoming stream, - // false if VAD is disabled on the incoming stream. - // - virtual bool ReceiveVADStatus() const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SetReceiveVADMode() - // Configure VAD aggressiveness on the incoming stream. - // - // Input: - // -mode : aggressiveness of the VAD on incoming stream. - // See audio_coding_module_typedefs.h for the - // definition of ACMVADMode, and possible - // values for aggressiveness. - // - // Return value: - // -1 if fails to set the mode, - // 0 if the mode is set successfully. - // - virtual WebRtc_Word16 SetReceiveVADMode( - const ACMVADMode mode) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // ACMVADMode ReceiveVADMode() - // Get VAD aggressiveness on the incoming stream. - // - // Return value: - // aggressiveness of VAD, running on the incoming stream. A more - // aggressive mode means more audio frames will be labeled as in-active. - // See audio_coding_module_typedefs.h for the definition of - // ACMVADMode. - // - virtual ACMVADMode ReceiveVADMode() const = 0; - - /////////////////////////////////////////////////////////////////////////// - // Codec specific - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetISACMaxRate() - // Set the maximum instantaneous rate of iSAC. For a payload of B bits - // with a frame-size of T sec the instantaneous rate is B/T bist per - // second. Therefore, (B/T < maxRateBitPerSec) and - // (B < maxPayloadLenBytes * 8) are always satisfied for iSAC payloads, - // c.f SetISACMaxPayloadSize(). - // - // Input: - // -maxRateBitPerSec : maximum instantaneous bit-rate given in bits/sec. - // - // Return value: - // -1 if failed to set the maximum rate. - // 0 if the maximum rate is set successfully. - // - virtual WebRtc_Word32 SetISACMaxRate( - const WebRtc_UWord32 maxRateBitPerSec) = 0; - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetISACMaxPayloadSize() - // Set the maximum payload size of iSAC packets. No iSAC payload, - // regardless of its frame-size, may exceed the given limit. For - // an iSAC payload of size B bits and frame-size T sec we have; - // (B < maxPayloadLenBytes * 8) and (B/T < maxRateBitPerSec), c.f. - // SetISACMaxRate(). - // - // Input: - // -maxPayloadLenBytes : maximum payload size in bytes. - // - // Return value: - // -1 if failed to set the maximm payload-size. - // 0 if the given linit is seet successfully. - // - virtual WebRtc_Word32 SetISACMaxPayloadSize( - const WebRtc_UWord16 maxPayloadLenBytes) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ConfigISACBandwidthEstimator() - // Call this function to configure the bandwidth estimator of ISAC. - // During the adaptation of bit-rate, iSAC atomatically adjusts the - // frame-size (either 30 or 60 ms) to save on RTP header. The initial - // frame-size can be specified by the first argument. The configuration also - // regards the initial estimate of bandwidths. The estimator starts from - // this point and converges to the actual bottleneck. This is given by the - // second parameter. Furthermore, it is also possible to control the - // adaptation of frame-size. This is specified by the last parameter. - // - // Input: - // -initFrameSizeMsec : initial frame-size in milisecods. For iSAC-wb - // 30 ms and 60 ms (default) are acceptable values, - // and for iSAC-swb 30 ms is the only acceptable - // value. Zero indiates default value. - // -initRateBitPerSec : initial estimate of the bandwidth. Values - // between 10000 and 58000 are acceptable. - // -enforceFrameSize : if true, the frame-size will not be adapted. - // - // Return value: - // -1 if failed to configure the bandwidth estimator, - // 0 if the configuration was successfully applied. - // - virtual WebRtc_Word32 ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize = false) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // statistics - // - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 NetworkStatistics() - // Get network statistics. - // - // Input: - // -networkStatistics : a structure that contains network statistics. - // - // Return value: - // -1 if failed to set the network statistics, - // 0 if statistics are set successfully. - // - virtual WebRtc_Word32 NetworkStatistics( - ACMNetworkStatistics& networkStatistics) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 JitterStatistics() - // Get the jitter statistics. - // - // Input: - // -jitterStatistics : the given jitter statistics. - // - // Return value: - // -1 if failed to set the jitter statistics, - // 0 if jitter statistics are set successfully. - // - virtual WebRtc_Word32 JitterStatistics( - ACMJitterStatistics& jitterStatistics) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 PreferredBufferSize() - // Get the optimal buffer size calculated for the current network - // conditions. - // - // Output: - // -prefBuffSize : the optimal size of the jitter buffer in - // milliseconds. - // - // Return value: - // -1 if the preferred buffer size could not be computed, - // 0 if a valid buffer is computed successfully. - // - virtual WebRtc_Word32 PreferredBufferSize( - WebRtc_UWord16& prefBufSize) const = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ResetJitterStatistics() - // Reset jitter statistics. - // - // Return value: - // -1 if failed to reset the statistics, - // 0 if succeeded. - // - virtual WebRtc_Word32 ResetJitterStatistics() const = 0; -}; - -} // namespace webrtc - -#endif - diff --git a/modules/audio_coding/main/interface/audio_coding_module_typedefs.h b/modules/audio_coding/main/interface/audio_coding_module_typedefs.h deleted file mode 100644 index 28b7a4e30..000000000 --- a/modules/audio_coding/main/interface/audio_coding_module_typedefs.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef AUDIO_CODING_MODULE_TYPEDEFS_H -#define AUDIO_CODING_MODULE_TYPEDEFS_H - -#include "typedefs.h" - -namespace webrtc -{ - -/////////////////////////////////////////////////////////////////////////// -// enum AudioPlayoutMode -// An enumerator for different playout modes. -// -// -voice : This is the standard mode for VoIP calls. The trade-off -// between low delay and jitter robustness is optimized -// for high-quality two-way communication. -// NetEQs packet loss concealment and signal processing -// capabilities are fully employed. -// -fax : The fax mode is optimized for decodability of fax signals -// rather than for perceived audio quality. When this mode -// is selected, NetEQ will do as few delay changes as possible, -// trying to maintain a high and constant delay. Meanwhile, -// the packet loss concealment efforts are reduced. -// -// -streaming : In the case of one-way communication such as passive -// conference participant, a webinar, or a streaming application, -// this mode can be used to improve the jitter robustness at -// the cost of increased delay. -// -enum AudioPlayoutMode -{ - voice = 0, - fax = 1, - streaming = 2 -}; - - -/////////////////////////////////////////////////////////////////////////// -// enum ACMSpeechType -// An enumerator for possible labels of a decoded frame. -// -// -normal : a normal speech frame. If VAD is enabled on the -// incoming stream this label indicate that the -// frame is active. -// -PLC : a PLC frame. The corresponding packet was lost -// and this frame generated by PLC techniques. -// -CNG : the frame is comfort noise. This happens if VAD -// is enabled at the sender and we have received -// SID. -// -PLCCNG : PLC will fade to comfort noise if the duration -// of PLC is long. This labels such a case. -// -VADPassive : the VAD at the receiver recognizes this frame as -// passive. -// -enum ACMSpeechType -{ - normal = 0, - PLC = 1, - CNG = 2, - PLCCNG = 3, - VADPassive = 4 -}; - - -/////////////////////////////////////////////////////////////////////////// -// enum ACMVADMode -// An enumerator for aggressiveness of VAD -// -VADNormal : least aggressive mode. -// -VADLowBitrate : more aggressive than "VADNormal" to save on -// bit-rate. -// -VADAggr : an aggressive mode. -// -VADVeryAggr : the most agressive mode. -// -enum ACMVADMode -{ - VADNormal = 0, - VADLowBitrate = 1, - VADAggr = 2, - VADVeryAggr = 3 -}; - - -/////////////////////////////////////////////////////////////////////////// -// enum ACMCountries -// An enumerator for countries, used when enabling CPT for a specific country. -// -enum ACMCountries -{ - ACMDisableCountryDetection = -1, // disable CPT detection - ACMUSA = 0, - ACMJapan, - ACMCanada, - ACMFrance, - ACMGermany, - ACMAustria, - ACMBelgium, - ACMUK, - ACMCzech, - ACMDenmark, - ACMFinland, - ACMGreece, - ACMHungary, - ACMIceland , - ACMIreland, - ACMItaly, - ACMLuxembourg, - ACMMexico, - ACMNorway, - ACMPoland, - ACMPortugal, - ACMSpain, - ACMSweden, - ACMTurkey, - ACMChina, - ACMHongkong, - ACMTaiwan, - ACMKorea, - ACMSingapore, - ACMNonStandard1 // non-standard countries -}; - -/////////////////////////////////////////////////////////////////////////// -// enum ACMAMRPackingFormat -// An enumerator for different bit-packing format of AMR codec according to -// RFC 3267. -// -// -AMRUndefined : undefined. -// -AMRBandwidthEfficient : bandwidth-efficient mode. -// -AMROctetAlligned : Octet-alligned mode. -// -AMRFileStorage : file-storage mode. -// -enum ACMAMRPackingFormat -{ - AMRUndefined = -1, - AMRBandwidthEfficient = 0, - AMROctetAlligned = 1, - AMRFileStorage = 2 -}; - - -/////////////////////////////////////////////////////////////////////////// -// -// Struct containing network statistics -// -// -currentBufferSize : current jitter buffer size in ms -// -preferredBufferSize : preferred (optimal) buffer size in ms -// -currentPacketLossRate : loss rate (network + late) (in Q14) -// -currentDiscardRate : late loss rate (in Q14) -// -currentExpandRate : fraction (of original stream) of synthesized speech -// inserted through expansion (in Q14) -// -currentPreemptiveRate : fraction of synthesized speech inserted through -// pre-emptive expansion (in Q14) -// -currentAccelerateRate : fraction of data removed through acceleration (in Q14) -typedef struct -{ - WebRtc_UWord16 currentBufferSize; - WebRtc_UWord16 preferredBufferSize; - WebRtc_UWord16 currentPacketLossRate; - WebRtc_UWord16 currentDiscardRate; - WebRtc_UWord16 currentExpandRate; - WebRtc_UWord16 currentPreemptiveRate; - WebRtc_UWord16 currentAccelerateRate; -} ACMNetworkStatistics; - -/////////////////////////////////////////////////////////////////////////// -// -// Struct containing jitter statistics -// -// -jbMinSize : smallest Jitter Buffer size during call in ms -// -jbMaxSize : largest Jitter Buffer size during call in ms -// -jbAvgSize : the average JB size, measured over time - ms -// -jbChangeCount : number of times the Jitter Buffer changed (using Accelerate or Pre-emptive Expand) -// -lateLossMs : amount (in ms) of audio data received late -// -accelerateMs : milliseconds removed to reduce jitter buffer size -// -flushedMs : milliseconds discarded through buffer flushing -// -generatedSilentMs : milliseconds of generated silence -// -interpolatedVoiceMs : milliseconds of synthetic audio data (non-background noise) -// -interpolatedSilentMs : milliseconds of synthetic audio data (background noise level) -// -numExpandTiny : count of tiny expansions in output audio less than 250 ms*/ -// -numExpandSmall : count of small expansions in output audio 250 to 500 ms*/ -// -numExpandMedium : count of medium expansions in output audio 500 to 2000 ms*/ -// -numExpandLong : count of long expansions in output audio longer than 2000 -// -longestExpandDurationMs : duration of longest audio drop-out -// -countIAT500ms : count of times we got small network outage (inter-arrival time in [500, 1000) ms) -// -countIAT1000ms : count of times we got medium network outage (inter-arrival time in [1000, 2000) ms) -// -countIAT2000ms : count of times we got large network outage (inter-arrival time >= 2000 ms) -// -longestIATms : longest packet inter-arrival time in ms -// -minPacketDelayMs : min time incoming Packet "waited" to be played -// -maxPacketDelayMs : max time incoming Packet "waited" to be played -// -avgPacketDelayMs : avg time incoming Packet "waited" to be played -// -typedef struct -{ - WebRtc_UWord32 jbMinSize; - WebRtc_UWord32 jbMaxSize; - WebRtc_UWord32 jbAvgSize; - WebRtc_UWord32 jbChangeCount; - WebRtc_UWord32 lateLossMs; - WebRtc_UWord32 accelerateMs; - WebRtc_UWord32 flushedMs; - WebRtc_UWord32 generatedSilentMs; - WebRtc_UWord32 interpolatedVoiceMs; - WebRtc_UWord32 interpolatedSilentMs; - WebRtc_UWord32 numExpandTiny; - WebRtc_UWord32 numExpandSmall; - WebRtc_UWord32 numExpandMedium; - WebRtc_UWord32 numExpandLong; - WebRtc_UWord32 longestExpandDurationMs; - WebRtc_UWord32 countIAT500ms; - WebRtc_UWord32 countIAT1000ms; - WebRtc_UWord32 countIAT2000ms; - WebRtc_UWord32 longestIATms; - WebRtc_UWord32 minPacketDelayMs; - WebRtc_UWord32 maxPacketDelayMs; - WebRtc_UWord32 avgPacketDelayMs; -} ACMJitterStatistics; - -/////////////////////////////////////////////////////////////////////////// -// -// Enumeration of background noise mode a mapping from NetEQ interface. -// -// -On : default "normal" behavior with eternal noise -// -Fade : noise fades to zero after some time -// -Off : background noise is always zero -// -enum ACMBackgroundNoiseMode -{ - On, - Fade, - Off -}; - - -} // namespace webrtc - -#endif diff --git a/modules/audio_coding/main/source/Android.mk b/modules/audio_coding/main/source/Android.mk deleted file mode 100644 index 5074335a2..000000000 --- a/modules/audio_coding/main/source/Android.mk +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_coding -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := acm_amr.cc \ - acm_amrwb.cc \ - acm_cng.cc \ - acm_codec_database.cc \ - acm_dtmf_detection.cc \ - acm_dtmf_playout.cc \ - acm_g722.cc \ - acm_g7221.cc \ - acm_g7221c.cc \ - acm_g729.cc \ - acm_g7291.cc \ - acm_generic_codec.cc \ - acm_gsmfr.cc \ - acm_ilbc.cc \ - acm_isac.cc \ - acm_neteq.cc \ - acm_opus.cc \ - acm_speex.cc \ - acm_pcm16b.cc \ - acm_pcma.cc \ - acm_pcmu.cc \ - acm_red.cc \ - acm_resampler.cc \ - audio_coding_module.cc \ - audio_coding_module_impl.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../codecs/CNG/main/interface \ - $(LOCAL_PATH)/../../codecs/G711/main/interface \ - $(LOCAL_PATH)/../../codecs/G722/main/interface \ - $(LOCAL_PATH)/../../codecs/iLBC/main/interface \ - $(LOCAL_PATH)/../../codecs/iSAC/main/interface \ - $(LOCAL_PATH)/../../codecs/iSAC/fix/interface \ - $(LOCAL_PATH)/../../codecs/PCM16B/main/interface \ - $(LOCAL_PATH)/../../NetEQ/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/resampler/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing_library/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/vad/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -ifneq ($(MY_WEBRTC_NDK_BUILD),true) -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) -endif \ No newline at end of file diff --git a/modules/audio_coding/main/source/acm_amr.cc b/modules/audio_coding/main/source/acm_amr.cc deleted file mode 100644 index 895dee60f..000000000 --- a/modules/audio_coding/main/source/acm_amr.cc +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_amr.h" -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "audio_coding_module_typedefs.h" -#include "rw_lock_wrapper.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_AMR - // NOTE! GSM AMR is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used AMR API file. - // #include "amr_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_AMR -ACMAMR::ACMAMR(WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMAMR::~ACMAMR() -{ - return; -} - - -WebRtc_Word16 -ACMAMR::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMAMR::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMAMR::EnableDTX() -{ - return -1; -} - - -WebRtc_Word16 -ACMAMR::DisableDTX() -{ - return -1; -} - - -WebRtc_Word16 -ACMAMR::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMAMR::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - -WebRtc_Word32 -ACMAMR::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - -ACMGenericCodec* -ACMAMR::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMAMR::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMAMR::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMAMR::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMAMR::DestructDecoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMAMR::SetBitRateSafe( - const WebRtc_Word32 /* rate */) -{ - return -1; -} - - -void -ACMAMR::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMAMR::SetAMREncoderPackingFormat( - ACMAMRPackingFormat /* packingFormat */) -{ - return -1; -} - - -ACMAMRPackingFormat -ACMAMR::AMREncoderPackingFormat() const -{ - return AMRUndefined; -} - -WebRtc_Word16 -ACMAMR::SetAMRDecoderPackingFormat( - ACMAMRPackingFormat /* packingFormat */) -{ - return -1; -} - -ACMAMRPackingFormat -ACMAMR::AMRDecoderPackingFormat() const -{ - return AMRUndefined; -} - -WebRtc_Word16 -ACMAMR::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - - -#define WEBRTC_AMR_MR475 0 -#define WEBRTC_AMR_MR515 1 -#define WEBRTC_AMR_MR59 2 -#define WEBRTC_AMR_MR67 3 -#define WEBRTC_AMR_MR74 4 -#define WEBRTC_AMR_MR795 5 -#define WEBRTC_AMR_MR102 6 -#define WEBRTC_AMR_MR122 7 - -// Remove when integrating a real GSM AMR wrapper -extern WebRtc_Word16 WebRtcAmr_CreateEnc(AMR_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcAmr_CreateDec(AMR_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcAmr_FreeEnc(AMR_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcAmr_FreeDec(AMR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmr_Encode(AMR_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16*output, - WebRtc_Word16 mode); -extern WebRtc_Word16 WebRtcAmr_EncoderInit(AMR_encinst_t_* encInst, - WebRtc_Word16 dtxMode); -extern WebRtc_Word16 WebRtcAmr_EncodeBitmode(AMR_encinst_t_* encInst, - ACMAMRPackingFormat format); -extern WebRtc_Word16 WebRtcAmr_Decode(AMR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmr_DecodePlc(AMR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmr_DecoderInit(AMR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmr_DecodeBitmode(AMR_decinst_t_* decInst, - ACMAMRPackingFormat format); - -ACMAMR::ACMAMR(WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL), -_encodingMode(-1), // invalid value -_encodingRate(0) // invalid value -{ - _codecID = codecID; - _hasInternalDTX = true; - _encoderPackingFormat = AMRBandwidthEfficient; - _decoderPackingFormat = AMRBandwidthEfficient; - return; -} - - -ACMAMR::~ACMAMR() -{ - if(_encoderInstPtr != NULL) - { - WebRtcAmr_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcAmr_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMAMR::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 vadDecision = 1; - // sanity check, if the rate is set correctly. we might skip this - // sanity check. if rate is not set correctly, initialization flag - // should be false and should not be here. - if((_encodingMode < WEBRTC_AMR_MR475) || (_encodingMode > WEBRTC_AMR_MR122)) - { - *bitStreamLenByte = 0; - return -1; - } - *bitStreamLenByte = WebRtcAmr_Encode(_encoderInstPtr, - &_inAudio[_inAudioIxRead], _frameLenSmpl, (WebRtc_Word16*)bitStream, - _encodingMode); - - // Update VAD, if internal DTX is used - if(_hasInternalDTX && _dtxEnabled) - { - if(*bitStreamLenByte <= (7*_frameLenSmpl/160)) - { - vadDecision = 0; - } - for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) - { - _vadLabel[n] = vadDecision; - } - } - // increment the read index - _inAudioIxRead += _frameLenSmpl; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMAMR::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMAMR::EnableDTX() -{ - if(_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // enable DTX - if(WebRtcAmr_EncoderInit(_encoderInstPtr, 1) < 0) - { - return -1; - } - _dtxEnabled = true; - return 0; - } - else - { - return -1; - } -} - - -WebRtc_Word16 -ACMAMR::DisableDTX() -{ - if(!_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // disable DTX - if(WebRtcAmr_EncoderInit(_encoderInstPtr, 0) < 0) - { - return -1; - } - _dtxEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - - -WebRtc_Word16 -ACMAMR::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - WebRtc_Word16 status = SetBitRateSafe((codecParams->codecInstant).rate); - status += (WebRtcAmr_EncoderInit(_encoderInstPtr, - ((codecParams->enableDTX)? 1:0)) < 0)? -1:0; - status += (WebRtcAmr_EncodeBitmode(_encoderInstPtr, - _encoderPackingFormat ) < 0)? -1:0; - return (status < 0)? -1:0; -} - - -WebRtc_Word16 -ACMAMR::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - WebRtc_Word16 status = ( - (WebRtcAmr_DecoderInit(_decoderInstPtr) < 0)? -1:0); - status += - WebRtcAmr_DecodeBitmode(_decoderInstPtr, _decoderPackingFormat); - return (status < 0)? -1:0; -} - - -WebRtc_Word32 -ACMAMR::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // Todo: - // log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AMR_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderAMR, codecInst.pltype, - _decoderInstPtr, 8000); - SET_AMR_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMAMR::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMAMR::InternalCreateEncoder() -{ - return WebRtcAmr_CreateEnc(&_encoderInstPtr); -} - - -void -ACMAMR::DestructEncoderSafe() -{ - if(_encoderInstPtr != NULL) - { - WebRtcAmr_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - // there is no encoder set the following - _encoderExist = false; - _encoderInitialized = false; - _encodingMode = -1; // invalid value - _encodingRate = 0; // invalid value -} - - -WebRtc_Word16 -ACMAMR::InternalCreateDecoder() -{ - return WebRtcAmr_CreateDec(&_decoderInstPtr); -} - - -void -ACMAMR::DestructDecoderSafe() -{ - if(_decoderInstPtr != NULL) - { - WebRtcAmr_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - // there is no encoder instance set the followings - _decoderExist = false; - _decoderInitialized = false; -} - - -WebRtc_Word16 -ACMAMR::SetBitRateSafe(const WebRtc_Word32 rate) -{ - switch(rate) - { - case 4750: - _encodingMode = WEBRTC_AMR_MR475; - _encodingRate = 4750; - break; - case 5150: - _encodingMode = WEBRTC_AMR_MR515; - _encodingRate = 5150; - break; - case 5900: - _encodingMode = WEBRTC_AMR_MR59; - _encodingRate = 5900; - break; - case 6700: - _encodingMode = WEBRTC_AMR_MR67; - _encodingRate = 6700; - break; - case 7400: - _encodingMode = WEBRTC_AMR_MR74; - _encodingRate = 7400; - break; - case 7950: - _encodingMode = WEBRTC_AMR_MR795; - _encodingRate = 7950; - break; - case 10200: - _encodingMode = WEBRTC_AMR_MR102; - _encodingRate = 10200; - break; - case 12200: - _encodingMode = WEBRTC_AMR_MR122; - _encodingRate = 12200; - break; - default: - return -1; - break; - } - return 0; -} - - -void -ACMAMR::InternalDestructEncoderInst( - void* ptrInst) -{ - // Free the memory where ptrInst is pointing to - if(ptrInst != NULL) - { - WebRtcAmr_FreeEnc((AMR_encinst_t_*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMAMR::SetAMREncoderPackingFormat( - ACMAMRPackingFormat packingFormat) -{ - if((packingFormat != AMRBandwidthEfficient) && - (packingFormat != AMROctetAlligned) && - (packingFormat != AMRFileStorage)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Invalid AMR Encoder packing-format."); - return -1; - } - else - { - if(WebRtcAmr_EncodeBitmode(_encoderInstPtr, - packingFormat) < 0) - { - return -1; - } - else - { - _encoderPackingFormat = packingFormat; - return 0; - } - - } -} - - -ACMAMRPackingFormat -ACMAMR::AMREncoderPackingFormat() const -{ - return _encoderPackingFormat; -} - -WebRtc_Word16 -ACMAMR::SetAMRDecoderPackingFormat( - ACMAMRPackingFormat packingFormat) -{ - if((packingFormat != AMRBandwidthEfficient) && - (packingFormat != AMROctetAlligned) && - (packingFormat != AMRFileStorage)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Invalid AMR decoder packing-format."); - return -1; - } - else - { - if(WebRtcAmr_DecodeBitmode(_decoderInstPtr, - packingFormat) < 0) - { - return -1; - } - else - { - _decoderPackingFormat = packingFormat; - return 0; - } - - } -} - -ACMAMRPackingFormat -ACMAMR::AMRDecoderPackingFormat() const -{ - return _decoderPackingFormat; -} - -WebRtc_Word16 -ACMAMR::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderAMR); -} - -#endif - -} - diff --git a/modules/audio_coding/main/source/acm_amr.h b/modules/audio_coding/main/source/acm_amr.h deleted file mode 100644 index d206827af..000000000 --- a/modules/audio_coding/main/source/acm_amr.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_AMR_H -#define ACM_AMR_H - -#include "acm_generic_codec.h" - -// forward declaration -struct AMR_encinst_t_; -struct AMR_decinst_t_; - -namespace webrtc -{ - -enum ACMAMRPackingFormat; - -class ACMAMR : public ACMGenericCodec -{ -public: - ACMAMR(WebRtc_Word16 codecID); - ~ACMAMR(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 SetAMREncoderPackingFormat( - const ACMAMRPackingFormat packingFormat); - - ACMAMRPackingFormat AMREncoderPackingFormat() const; - - WebRtc_Word16 SetAMRDecoderPackingFormat( - const ACMAMRPackingFormat packingFormat); - - ACMAMRPackingFormat AMRDecoderPackingFormat() const; - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - WebRtc_Word16 EnableDTX(); - - WebRtc_Word16 DisableDTX(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - AMR_encinst_t_* _encoderInstPtr; - AMR_decinst_t_* _decoderInstPtr; - WebRtc_Word16 _encodingMode; - WebRtc_Word16 _encodingRate; - ACMAMRPackingFormat _encoderPackingFormat; - ACMAMRPackingFormat _decoderPackingFormat; -}; - -} // namespace webrtc - -#endif // ACM_AMR_H diff --git a/modules/audio_coding/main/source/acm_amrwb.cc b/modules/audio_coding/main/source/acm_amrwb.cc deleted file mode 100644 index 4958abc56..000000000 --- a/modules/audio_coding/main/source/acm_amrwb.cc +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_amrwb.h" -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "audio_coding_module_typedefs.h" -#include "rw_lock_wrapper.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_AMRWB - // NOTE! GSM AMR-wb is not included in the open-source package. Modify this file or your - // codec API to match the function call and name of used AMR-wb API file. - // #include "amrwb_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_AMRWB -ACMAMRwb::ACMAMRwb(WebRtc_Word16 /* codecID*/) -{ - return; -} - -ACMAMRwb::~ACMAMRwb() -{ - return; -} - -WebRtc_Word16 -ACMAMRwb::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - -WebRtc_Word16 -ACMAMRwb::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - -WebRtc_Word16 -ACMAMRwb::EnableDTX() -{ - return -1; -} - -WebRtc_Word16 -ACMAMRwb::DisableDTX() -{ - return -1; -} - -WebRtc_Word16 -ACMAMRwb::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - -WebRtc_Word16 -ACMAMRwb::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - -WebRtc_Word32 -ACMAMRwb::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - -ACMGenericCodec* -ACMAMRwb::CreateInstance(void) -{ - return NULL; -} - - - -WebRtc_Word16 -ACMAMRwb::InternalCreateEncoder() -{ - return -1; -} - -void -ACMAMRwb::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMAMRwb::InternalCreateDecoder() -{ - return -1; -} - -void -ACMAMRwb::DestructDecoderSafe() -{ - return; -} - -WebRtc_Word16 -ACMAMRwb::SetBitRateSafe( - const WebRtc_Word32 /* rate */) -{ - return -1; -} - -void -ACMAMRwb::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - -WebRtc_Word16 -ACMAMRwb::SetAMRwbEncoderPackingFormat( - ACMAMRPackingFormat /* packingFormat */) -{ - return -1; -} - -ACMAMRPackingFormat -ACMAMRwb::AMRwbEncoderPackingFormat() const -{ - return AMRUndefined; -} - -WebRtc_Word16 ACMAMRwb::SetAMRwbDecoderPackingFormat( - ACMAMRPackingFormat /* packingFormat */) -{ - return -1; -} - -ACMAMRPackingFormat -ACMAMRwb::AMRwbDecoderPackingFormat() const -{ - return AMRUndefined; -} - -WebRtc_Word16 -ACMAMRwb::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -#else //===================== Actual Implementation ======================= - -#define AMRWB_MODE_7k 0 -#define AMRWB_MODE_9k 1 -#define AMRWB_MODE_12k 2 -#define AMRWB_MODE_14k 3 -#define AMRWB_MODE_16k 4 -#define AMRWB_MODE_18k 5 -#define AMRWB_MODE_20k 6 -#define AMRWB_MODE_23k 7 -#define AMRWB_MODE_24k 8 - -// Remove when integrating a real GSM AMR wrapper -extern WebRtc_Word16 WebRtcAmrWb_CreateEnc(AMRWB_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcAmrWb_CreateDec(AMRWB_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcAmrWb_FreeEnc(AMRWB_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcAmrWb_FreeDec(AMRWB_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmrWb_Encode(AMRWB_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16*output, - WebRtc_Word16 mode); -extern WebRtc_Word16 WebRtcAmrWb_EncoderInit(AMRWB_encinst_t_* encInst, - WebRtc_Word16 dtxMode); -extern WebRtc_Word16 WebRtcAmrWb_EncodeBitmode(AMRWB_encinst_t_* encInst, - ACMAMRPackingFormat format); -extern WebRtc_Word16 WebRtcAmrWb_Decode(AMRWB_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmrWb_DecodePlc(AMRWB_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmrWb_DecoderInit(AMRWB_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcAmrWb_DecodeBitmode(AMRWB_decinst_t_* decInst, - ACMAMRPackingFormat format); - - -ACMAMRwb::ACMAMRwb(WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL), -_encodingMode(-1), // invalid value -_encodingRate(0) // invalid value -{ - _codecID = codecID; - _hasInternalDTX = true; - _encoderPackingFormat = AMRBandwidthEfficient; - _decoderPackingFormat = AMRBandwidthEfficient; - return; -} - -ACMAMRwb::~ACMAMRwb() -{ - if(_encoderInstPtr != NULL) - { - WebRtcAmrWb_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcAmrWb_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - -WebRtc_Word16 -ACMAMRwb::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 vadDecision = 1; - // sanity check, if the rate is set correctly. we might skip this - // sanity check. if rate is not set correctly, initialization flag - // should be false and should not be here. - if((_encodingMode < AMRWB_MODE_7k) || (_encodingMode > AMRWB_MODE_24k)) - { - *bitStreamLenByte = 0; - return -1; - } - *bitStreamLenByte = - WebRtcAmrWb_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], - _frameLenSmpl, (WebRtc_Word16*)bitStream, _encodingMode); - - // Update VAD, if internal DTX is used - if(_hasInternalDTX && _dtxEnabled) - { - if (*bitStreamLenByte <= (7*_frameLenSmpl/160)) - { - vadDecision = 0; - } - for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) - { - _vadLabel[n] = vadDecision; - } - } - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl; - return *bitStreamLenByte; -} - -WebRtc_Word16 -ACMAMRwb::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - -WebRtc_Word16 -ACMAMRwb::EnableDTX() -{ - if(_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // enable DTX - if(WebRtcAmrWb_EncoderInit(_encoderInstPtr, 1) < 0) - { - return -1; - } - _dtxEnabled = true; - return 0; - } - else - { - return -1; - } -} - -WebRtc_Word16 -ACMAMRwb::DisableDTX() -{ - if(!_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // disable DTX - if(WebRtcAmrWb_EncoderInit(_encoderInstPtr, 0) < 0) - { - return -1; - } - _dtxEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -WebRtc_Word16 -ACMAMRwb::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - // sanity check - if (_encoderInstPtr == NULL) - { - return -1; - } - - WebRtc_Word16 status = SetBitRateSafe((codecParams->codecInstant).rate); - status += (WebRtcAmrWb_EncoderInit( - _encoderInstPtr, ((codecParams->enableDTX)? 1:0)) < 0)? -1:0; - status += (WebRtcAmrWb_EncodeBitmode( - _encoderInstPtr, _encoderPackingFormat ) < 0)? -1:0; - return (status < 0)? -1:0; -} - -WebRtc_Word16 -ACMAMRwb::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - WebRtc_Word16 status = WebRtcAmrWb_DecodeBitmode( - _decoderInstPtr, _decoderPackingFormat); - status += ((WebRtcAmrWb_DecoderInit(_decoderInstPtr) < 0)? -1:0); - return (status < 0)? -1:0; -} - -WebRtc_Word32 -ACMAMRwb::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // Todo: - // log error - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AMRWB_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderAMRWB, codecInst.pltype, - _decoderInstPtr, 16000); - SET_AMRWB_FUNCTIONS((codecDef)); - return 0; -} - -ACMGenericCodec* -ACMAMRwb::CreateInstance(void) -{ - return NULL; -} - -WebRtc_Word16 -ACMAMRwb::InternalCreateEncoder() -{ - return WebRtcAmrWb_CreateEnc(&_encoderInstPtr); -} - -void -ACMAMRwb::DestructEncoderSafe() -{ - if(_encoderInstPtr != NULL) - { - WebRtcAmrWb_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - // there is no encoder set the following - _encoderExist = false; - _encoderInitialized = false; - _encodingMode = -1; // invalid value - _encodingRate = 0; -} - - -WebRtc_Word16 -ACMAMRwb::InternalCreateDecoder() -{ - return WebRtcAmrWb_CreateDec(&_decoderInstPtr); -} - -void -ACMAMRwb::DestructDecoderSafe() -{ - if(_decoderInstPtr != NULL) - { - WebRtcAmrWb_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - // there is no encoder instance set the followings - _decoderExist = false; - _decoderInitialized = false; -} - -WebRtc_Word16 -ACMAMRwb::SetBitRateSafe( - const WebRtc_Word32 rate) -{ - switch(rate) - { - case 7000: - { - _encodingMode = AMRWB_MODE_7k; - _encodingRate = 7000; - break; - } - case 9000: - { - _encodingMode = AMRWB_MODE_9k; - _encodingRate = 9000; - break; - } - case 12000: - { - _encodingMode = AMRWB_MODE_12k; - _encodingRate = 12000; - break; - } - case 14000: - { - _encodingMode = AMRWB_MODE_14k; - _encodingRate = 14000; - break; - } - case 16000: - { - _encodingMode = AMRWB_MODE_16k; - _encodingRate = 16000; - break; - } - case 18000: - { - _encodingMode = AMRWB_MODE_18k; - _encodingRate = 18000; - break; - } - case 20000: - { - _encodingMode = AMRWB_MODE_20k; - _encodingRate = 20000; - break; - } - case 23000: - { - _encodingMode = AMRWB_MODE_23k; - _encodingRate = 23000; - break; - } - case 24000: - { - _encodingMode = AMRWB_MODE_24k; - _encodingRate = 24000; - break; - } - default: - { - return -1; - break; - } - } - return 0; -} - -void -ACMAMRwb::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcAmrWb_FreeEnc((AMRWB_encinst_t_*)ptrInst); - } - return; -} - -WebRtc_Word16 -ACMAMRwb::SetAMRwbEncoderPackingFormat( - ACMAMRPackingFormat packingFormat) -{ - if((packingFormat != AMRBandwidthEfficient) && - (packingFormat != AMROctetAlligned) && - (packingFormat != AMRFileStorage)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Invalid AMRwb encoder packing-format."); - return -1; - } - else - { - - if(WebRtcAmrWb_EncodeBitmode(_encoderInstPtr, - packingFormat) < 0) - { - return -1; - } - else - { - _encoderPackingFormat = packingFormat; - return 0; - } - } -} - -ACMAMRPackingFormat -ACMAMRwb::AMRwbEncoderPackingFormat() const -{ - return _encoderPackingFormat; -} - -WebRtc_Word16 ACMAMRwb::SetAMRwbDecoderPackingFormat( - ACMAMRPackingFormat packingFormat) -{ - if((packingFormat != AMRBandwidthEfficient) && - (packingFormat != AMROctetAlligned) && - (packingFormat != AMRFileStorage)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Invalid AMRwb decoder packing-format."); - return -1; - } - else - { - if(WebRtcAmrWb_DecodeBitmode(_decoderInstPtr, - packingFormat) < 0) - { - return -1; - } - else - { - _decoderPackingFormat = packingFormat; - return 0; - } - } -} - -ACMAMRPackingFormat -ACMAMRwb::AMRwbDecoderPackingFormat() const -{ - return _decoderPackingFormat; -} - -WebRtc_Word16 -ACMAMRwb::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderAMRWB); -} -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_amrwb.h b/modules/audio_coding/main/source/acm_amrwb.h deleted file mode 100644 index 2aa149155..000000000 --- a/modules/audio_coding/main/source/acm_amrwb.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_AMRWB_H -#define ACM_AMRWB_H - -#include "acm_generic_codec.h" - -// forward declaration -struct AMRWB_encinst_t_; -struct AMRWB_decinst_t_; - -namespace webrtc -{ - -enum ACMAMRPackingFormat; - -class ACMAMRwb : public ACMGenericCodec -{ -public: - ACMAMRwb(WebRtc_Word16 codecID); - ~ACMAMRwb(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 SetAMRwbEncoderPackingFormat( - const ACMAMRPackingFormat packingFormat); - - ACMAMRPackingFormat AMRwbEncoderPackingFormat() const; - - WebRtc_Word16 SetAMRwbDecoderPackingFormat( - const ACMAMRPackingFormat packingFormat); - - ACMAMRPackingFormat AMRwbDecoderPackingFormat() const; - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - WebRtc_Word16 EnableDTX(); - - WebRtc_Word16 DisableDTX(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - AMRWB_encinst_t_* _encoderInstPtr; - AMRWB_decinst_t_* _decoderInstPtr; - - WebRtc_Word16 _encodingMode; - WebRtc_Word16 _encodingRate; - ACMAMRPackingFormat _encoderPackingFormat; - ACMAMRPackingFormat _decoderPackingFormat; -}; - -} // namespace webrtc - -#endif // ACM_AMRWB_H diff --git a/modules/audio_coding/main/source/acm_cng.cc b/modules/audio_coding/main/source/acm_cng.cc deleted file mode 100644 index 20bbdf459..000000000 --- a/modules/audio_coding/main/source/acm_cng.cc +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_cng.h" -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_cng.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -namespace webrtc -{ - -ACMCNG::ACMCNG(WebRtc_Word16 codecID) -{ - _encoderInstPtr = NULL; - _decoderInstPtr = NULL; - _codecID = codecID; - if(_codecID == ACMCodecDB::cnNB) - { - _sampFreqHz = 8000; - } - else if(_codecID == ACMCodecDB::cnWB) - { - _sampFreqHz = 16000; - } - else if(_codecID == ACMCodecDB::cnSWB) - { - _sampFreqHz = 32000; - } - else - { - _sampFreqHz = -1; - } - return; -} - - -ACMCNG::~ACMCNG() -{ - if(_encoderInstPtr != NULL) - { - WebRtcCng_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcCng_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -// CNG is not like a regular encoder, this function -// should not be called normally -// instead the following function is called from inside -// ACMGenericCodec::ProcessFrameVADDTX -WebRtc_Word16 -ACMCNG::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - -WebRtc_Word16 -ACMCNG::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -// CNG is not like a regular encoder, -// this function should not be called normally -// instead the following function is called from inside -// ACMGenericCodec::ProcessFrameVADDTX -WebRtc_Word16 -ACMCNG::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMCNG::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return WebRtcCng_InitDec(_decoderInstPtr); -} - - -WebRtc_Word32 -ACMCNG::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // TODO (tlegrand): log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_CNG_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - - if (_sampFreqHz == 8000 || _sampFreqHz == 16000 || _sampFreqHz == 32000) - { - SET_CODEC_PAR((codecDef), kDecoderCNG, codecInst.pltype, - _decoderInstPtr, _sampFreqHz); - SET_CNG_FUNCTIONS((codecDef)); - return 0; - } - else - { - return -1; - } -} - - -ACMGenericCodec* ACMCNG::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMCNG::InternalCreateEncoder() -{ - if(WebRtcCng_CreateEnc(&_encoderInstPtr) < 0) - { - _encoderInstPtr = NULL; - return -1; - } - else - { - return 0; - } -} - - -void -ACMCNG::DestructEncoderSafe() -{ - if(_encoderInstPtr != NULL) - { - WebRtcCng_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - _encoderExist = false; - _encoderInitialized = false; -} - - -WebRtc_Word16 -ACMCNG::InternalCreateDecoder() -{ - if(WebRtcCng_CreateDec(&_decoderInstPtr) < 0) - { - _decoderInstPtr = NULL; - return -1; - } - else - { - return 0; - } -} - - -void -ACMCNG::DestructDecoderSafe() -{ - if(_decoderInstPtr != NULL) - { - WebRtcCng_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - _decoderExist = false; - _decoderInitialized = false; -} - - -void -ACMCNG::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcCng_FreeEnc((CNG_enc_inst*)ptrInst); - } - return; -} - -WebRtc_Word16 -ACMCNG::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderCNG); -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_cng.h b/modules/audio_coding/main/source/acm_cng.h deleted file mode 100644 index 2ceb59f6c..000000000 --- a/modules/audio_coding/main/source/acm_cng.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_CNG_H -#define ACM_CNG_H - -#include "acm_generic_codec.h" - - -// forward declaration -struct WebRtcCngEncInst; -struct WebRtcCngDecInst; - -namespace webrtc -{ - -class ACMCNG : public ACMGenericCodec -{ -public: - ACMCNG(WebRtc_Word16 codecID); - ~ACMCNG(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 EnableDTX() - { - return -1; - } - - WebRtc_Word16 DisableDTX() - { - return -1; - } - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - WebRtcCngEncInst* _encoderInstPtr; - WebRtcCngDecInst* _decoderInstPtr; - WebRtc_Word16 _sampFreqHz; -}; - -} // namespace webrtc - -#endif // ACM_CNG_H - diff --git a/modules/audio_coding/main/source/acm_codec_database.cc b/modules/audio_coding/main/source/acm_codec_database.cc deleted file mode 100644 index f38331bff..000000000 --- a/modules/audio_coding/main/source/acm_codec_database.cc +++ /dev/null @@ -1,953 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// 'conversion' conversion from 'type1' to 'type2', possible loss of data -#pragma warning(disable: 4267) - -#include - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "trace.h" - -// Includes needed to get version info -// and to create the codecs -#include "acm_pcma.h" -#include "acm_pcmu.h" -#include "g711_interface.h" -#include "webrtc_neteq.h" -#include "webrtc_cng.h" -#include "acm_cng.h" -#ifdef WEBRTC_CODEC_AVT - #include "acm_dtmf_playout.h" -#endif -#ifdef WEBRTC_CODEC_RED - #include "acm_red.h" -#endif -#ifdef WEBRTC_CODEC_ILBC - #include "acm_ilbc.h" - #include "ilbc.h" -#endif -#ifdef WEBRTC_CODEC_ISAC - #include "acm_isac.h" - #include "acm_isac_macros.h" - #include "isac.h" -#endif -#ifdef WEBRTC_CODEC_ISACFX - #include "acm_isac.h" - #include "acm_isac_macros.h" - #include "isacfix.h" -#endif -#ifdef WEBRTC_CODEC_PCM16 - #include "pcm16b.h" - #include "acm_pcm16b.h" -#endif -#ifdef WEBRTC_CODEC_G722 - #include "acm_g722.h" - #include "g722_interface.h" -#endif - -namespace webrtc -{ - -#define TEMPORARY_BUFFER_SIZE 500 - -bool ACMCodecDB::_isInitiated = false; -WebRtc_Word16 ACMCodecDB::_noOfCodecs = 0; -WebRtc_Word16 ACMCodecDB::_noNetEqDecoders = 0; -WebRtc_Word16 ACMCodecDB::_noPayloads = 0; - -WebRtc_Word16 ACMCodecDB::isac = -1; -WebRtc_Word16 ACMCodecDB::isacswb = -1; -WebRtc_Word16 ACMCodecDB::pcm16b = -1; -WebRtc_Word16 ACMCodecDB::pcm16bwb = -1; -WebRtc_Word16 ACMCodecDB::pcm16bswb32 = -1; -WebRtc_Word16 ACMCodecDB::pcm16bswb48 = -1; -WebRtc_Word16 ACMCodecDB::pcmu = -1; -WebRtc_Word16 ACMCodecDB::pcma = -1; -WebRtc_Word16 ACMCodecDB::ilbc = -1; -WebRtc_Word16 ACMCodecDB::gsmAMR = -1; -WebRtc_Word16 ACMCodecDB::gsmAMRWB = -1; -WebRtc_Word16 ACMCodecDB::g722 = -1; -WebRtc_Word16 ACMCodecDB::g722_1_32 = -1; -WebRtc_Word16 ACMCodecDB::g722_1_24 = -1; -WebRtc_Word16 ACMCodecDB::g722_1_16 = -1; -WebRtc_Word16 ACMCodecDB::g722_1C_48 = -1; -WebRtc_Word16 ACMCodecDB::g722_1C_32 = -1; -WebRtc_Word16 ACMCodecDB::g722_1C_24 = -1; -WebRtc_Word16 ACMCodecDB::g729 = -1; -WebRtc_Word16 ACMCodecDB::gsmfr = -1; -WebRtc_Word16 ACMCodecDB::speex8 = -1; -WebRtc_Word16 ACMCodecDB::speex16 = -1; -WebRtc_Word16 ACMCodecDB::cnNB = -1; -WebRtc_Word16 ACMCodecDB::cnWB = -1; -WebRtc_Word16 ACMCodecDB::cnSWB = -1; -WebRtc_Word16 ACMCodecDB::avt = -1; -WebRtc_Word16 ACMCodecDB::red = -1; - -WebRtc_UWord8 ACMCodecDB::_nrOfAllowedPacSizes[MAX_NR_OF_CODECS]; -WebRtc_UWord16 ACMCodecDB::_allowedPacSizesSmpl[MAX_NR_OF_CODECS][MAX_NR_OF_PACSIZES]; -CodecInst ACMCodecDB::_mycodecs[MAX_NR_OF_CODECS]; -enum WebRtcNetEQDecoder ACMCodecDB::_netEqDecoders[MAX_NR_OF_CODECS]; -WebRtc_Word8 ACMCodecDB::_versions[VERSION_SIZE]; -WebRtc_UWord16 ACMCodecDB::_basicCodingBlockSmpl[MAX_NR_OF_CODECS]; -WebRtc_UWord16 ACMCodecDB::_channelSupport[MAX_NR_OF_CODECS]; -WebRtc_UWord32 ACMCodecDB::_versionStringSize = 0; - -// We dynamically allocate some of the dynamic payload types to the defined codecs -// Note! There are a limited number of payload types. If more codecs are defined -// they will receive reserved fixed payload types (values 65-95). -static int kDynamicPayloadtypes[MAX_NR_OF_CODECS] = { - 105, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 95, 94, 93, 92, 91, 90, 89, - 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, - 74, 73, 72, 71, 70, 69, 68, 67, 66, 65 -}; - -WebRtc_Word16 -ACMCodecDB::Codec( - WebRtc_Word16 listnr, - CodecInst* codec_inst) -{ - // Error check to se that listnr is between 0 and (_noOfCodecs - 1) - if ((listnr < 0) || (listnr >= _noOfCodecs)) - { - return -1; - } - - memcpy(codec_inst,&_mycodecs[listnr],sizeof(CodecInst)); - return 0; -} - - -WebRtc_Word16 -ACMCodecDB::CodecNumber( - const CodecInst* codecInst, - WebRtc_Word16& mirrorID, - WebRtc_Word8* errMessage, - WebRtc_Word16 maxErrMsgLenByte) -{ - WebRtc_Word16 codecID = ACMCodecDB::CodecNumber(codecInst, mirrorID); - if((codecID < 0) && (errMessage != NULL)) - { - WebRtc_Word8 myErrMsg[1000]; - if (codecID == -10) - { - // Codec not supported - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, plname=%s is \ -not a valid codec", codecInst->plname); - } - else if (codecID == -20) - { - // Sampling frequency doesn't match codec - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, plfreq=%d is \ -not a valid frequency for the codec %s", codecInst->plfreq, codecInst->plname); - } - else if (codecID == -30) - { - // Wrong payload type for Comfort Noise - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, payload \ -number %d is out of range for %s", codecInst->pltype, codecInst->plname); - } - else if (codecID == -40) - { - // Wrong payload type for RED - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, payload \ -number %d is out of range for %s", codecInst->pltype, codecInst->plname); - } - else if (codecID == -50) - { - // Packet size is out of range for the codec - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, Packet \ -size is out of range for %s", codecInst->plname); - } - else if (codecID == -60) - { - // Not a valid rate for the codec - sprintf(myErrMsg, - "Call to ACMCodecDB::CodecNumber failed, rate=%d \ -is not a valid rate for %s", codecInst->rate, codecInst->plname); - } - else - { - // Other error - sprintf(myErrMsg, - "invalid codec parameters to be registered, \ -ACMCodecDB::CodecNumber failed"); - } - strncpy(errMessage, myErrMsg, maxErrMsgLenByte - 1); - // make sure that the massage is null-terminated. - errMessage[maxErrMsgLenByte - 1] = '\0'; - } - return codecID; -} - - -WebRtc_Word16 -ACMCodecDB::CodecNumber( - const CodecInst* codec_inst, - WebRtc_Word16& mirrorID) -{ - WebRtc_Word16 codecNumber = -1; - WebRtc_Word16 nameMatch = 0; - - // Find a matching payload name and frequency in the codec list - // Need to check both since some codecs have several codec entries with - // Different frequencies (like iSAC) - for(WebRtc_Word16 i = 0; i < _noOfCodecs; i++) - { - if(STR_CASE_CMP(_mycodecs[i].plname, codec_inst->plname) == 0) - { - //We have found a matching codec name in the list - nameMatch = 1; - - // Check if frequncy match - if (codec_inst->plfreq == _mycodecs[i].plfreq) - { - codecNumber = i; - break; - } - } - } - - if(codecNumber == -1) - { - if (!nameMatch) { - // Codec name doesn't match any codec in the list - return -10; - } else { - // Error in payload frequency, doesn't match codec - return -20; - } - } - - // Check the validity of payload type - if(ValidPayloadType(codec_inst->pltype) < 0) - { - // Payload type out of range - return -40; - } - - // Comfort Noise is special case, packet-size & rate is not checked - if(STR_CASE_CMP(_mycodecs[codecNumber].plname, "CN") == 0) - { - mirrorID = codecNumber; - return codecNumber; - } - - // RED is special case, packet-size & rate is not checked - if(STR_CASE_CMP(_mycodecs[codecNumber].plname, "red") == 0) - { - mirrorID = codecNumber; - return codecNumber; - } - - // Check the validity of packet size - if(_nrOfAllowedPacSizes[codecNumber] > 0) - { - // Check that the new packet size is in the valid range. - bool pacSizeOK = false; - for(WebRtc_Word32 i=0;i< _nrOfAllowedPacSizes[codecNumber];i++) - { - if(codec_inst->pacsize == _allowedPacSizesSmpl[codecNumber][i]) - { - pacSizeOK = true; - break; - } - } - if(!pacSizeOK) - { - // Packet size is out of range - return -50; - } - } - if( codec_inst->pacsize < 1) - { - // Packet size is out of range - return -50; - } - - mirrorID = codecNumber; - - // Check the validity of rate - if(STR_CASE_CMP("isac", codec_inst->plname) == 0) - { - if(IsISACRateValid(codec_inst->rate)) - { - // Set mirrorID to iSAC WB which is only created - // once to be used both for iSAC WB and SWB, because - // they need to share struct - mirrorID = ACMCodecDB::isac; - return codecNumber; - } - else - { - // Not a valid rate - return -60; - } - } - else if(STR_CASE_CMP("ilbc", codec_inst->plname) == 0) - { - return IsILBCRateValid(codec_inst->rate, codec_inst->pacsize) ? codecNumber : -60; - } - - return IsRateValid(codecNumber, codec_inst->rate) ? codecNumber : -60; -} - - -WebRtc_Word16 -ACMCodecDB::ReceiverCodecNumber( - const CodecInst& codecInst, - WebRtc_Word16& mirrorID) -{ - WebRtc_Word16 codecNumber = -1; - WebRtc_Word16 nameMatch = 0; - - // Find a matching payload name and frequency in the codec list - // Need to check both since some codecs have several codec entries with - // Different frequencies (like iSAC) - for(WebRtc_Word16 i = 0; i < _noOfCodecs; i++) - { - if(STR_CASE_CMP(_mycodecs[i].plname, codecInst.plname) == 0) - { - //We have found a matching codec name in the list - nameMatch = 1; - - // Check if frequency match - if (codecInst.plfreq == _mycodecs[i].plfreq) - { - codecNumber = i; - mirrorID = codecNumber; - - // Check if codec is iSAC, set mirrorID to iSAC WB - // which is only created once to be used both for - // iSAC WB and SWB, because they need to share struct - if(STR_CASE_CMP(codecInst.plname, "ISAC") == 0) - { - mirrorID = ACMCodecDB::isac; - } - break; - } - } - } - - if(codecNumber == -1) - { - return codecNumber; - } - - return codecNumber; -} - - -// Return number of codecs in the database -WebRtc_Word16 -ACMCodecDB::NoOfCodecs() -{ - return _noOfCodecs; -} - - -// Return number of NetEQ decoders in the database. -// Note that the number is huigher than _moOfCodecs because some payload names -// are treated as different decoders in NetEQ, like iSAC wb and swb. -WebRtc_Word16 -ACMCodecDB::NoNetEqDecoders() -{ - return _noNetEqDecoders; -} - - -// Return the codec sampling frequency for code number "listnr" in database -WebRtc_Word32 -ACMCodecDB::CodecFreq(WebRtc_Word16 listnr) -{ - // Error check to se that listnr is between 0 and (_noOfCodecs - 1) - if ( listnr < 0 || listnr >= _noOfCodecs) - return -1; - - return _mycodecs[listnr].plfreq; -} - -// Return the codecs basic coding block size in samples -WebRtc_Word16 -ACMCodecDB::BasicCodingBlock( - WebRtc_Word16 listNr) -{ - // Error check to se that listnr is between 0 and (_noOfCodecs - 1) - if ( listNr < 0 || listNr >= _noOfCodecs) - return -1; - - return _basicCodingBlockSmpl[listNr]; -} - -// Return the NetEQ decoder database -enum WebRtcNetEQDecoder* -ACMCodecDB::NetEqDecoders() -{ - return _netEqDecoders; -} - -// All version numbers for the codecs in the data base are listed in text. -WebRtc_Word16 -ACMCodecDB::CodecsVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - WebRtc_UWord32 len = position; - strncpy(&version[len], _versions, remainingBufferInBytes); - position = (WebRtc_UWord32)strlen(version); - remainingBufferInBytes -= (position - len); - if(remainingBufferInBytes < _versionStringSize) - { - return -1; - } - return 0; -} - -// Get mirror id. The Id is used for codecs sharing struct for settings that -// need different payload types. -WebRtc_Word16 -ACMCodecDB::MirrorID( - const WebRtc_Word16 codecID) -{ - if(STR_CASE_CMP(_mycodecs[codecID].plname, "isac") == 0) - { - return ACMCodecDB::isac; - } - else - { - return codecID; - } -} - - - -ACMGenericCodec* -ACMCodecDB::CreateCodecInstance( - const CodecInst* codecInst) -{ - // All we have support for right now - if(!STR_CASE_CMP(codecInst->plname, "ISAC")) - { -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - return new ACMISAC(isac); -#endif - } - else if(!STR_CASE_CMP(codecInst->plname, "PCMU")) - { - return new ACMPCMU(pcmu); - } - else if(!STR_CASE_CMP(codecInst->plname, "PCMA")) - { - return new ACMPCMA(pcma); - } - else if(!STR_CASE_CMP(codecInst->plname, "ILBC")) - { -#ifdef WEBRTC_CODEC_ILBC - return new ACMILBC(ilbc); -#endif - } - else if(!STR_CASE_CMP(codecInst->plname, "G722")) - { -#ifdef WEBRTC_CODEC_G722 - return new ACMG722(g722); -#endif - } - else if(!STR_CASE_CMP(codecInst->plname, "CN")) - { - // We need to check sampling frequency - // know what codec to create. - WebRtc_Word16 codecID; - switch(codecInst->plfreq) - { - case 8000: - { - codecID = ACMCodecDB::cnNB; - break; - } - case 16000: - { - codecID = ACMCodecDB::cnWB; - break; - } - case 32000: - { - codecID = ACMCodecDB::cnSWB; - break; - } - default: - return NULL; - } - return new ACMCNG(codecID); - } - else if(!STR_CASE_CMP(codecInst->plname, "L16")) - { -#ifdef WEBRTC_CODEC_PCM16 - // For this codec we need to check sampling frequency - // to know what codec to create. - WebRtc_Word16 codecID; - switch(codecInst->plfreq) - { - case 8000: - { - codecID = ACMCodecDB::pcm16b; - break; - } - case 16000: - { - codecID = ACMCodecDB::pcm16bwb; - break; - } - case 32000: - { - codecID = ACMCodecDB::pcm16bswb32; - break; - } - default: - return NULL; - } - return new ACMPCM16B(codecID); -#endif - } - else if(!STR_CASE_CMP(codecInst->plname, "telephone-event")) - { -#ifdef WEBRTC_CODEC_AVT - return new ACMDTMFPlayout(avt); -#endif - } - else if(!STR_CASE_CMP(codecInst->plname, "red")) - { -#ifdef WEBRTC_CODEC_RED - return new ACMRED(red); -#endif - } - return NULL; -} - - -//Here we build the complete database "_mycodecs" of our codecs -void -ACMCodecDB::initACMCodecDB() -{ - if(_isInitiated) - { - return; - } - else - { - _isInitiated = true; - } - WebRtc_Word8 versionNrBuff[TEMPORARY_BUFFER_SIZE]; - WebRtc_Word32 remainingSize = VERSION_SIZE; - - _versions[0] = '\0'; - - // Init the stereo settings vector - for (int i=0; i= 10000))) - { - return true; - } - else - { - return false; - } -#endif -} - -// Check if the bitrate is valid for iLBC -bool -ACMCodecDB::IsILBCRateValid( -#ifndef WEBRTC_CODEC_ILBC - const WebRtc_Word32 /* rate */, - const WebRtc_Word16 /* frameSizeSamples */) -{ - return false; -#else - const WebRtc_Word32 rate, - const WebRtc_Word16 frameSizeSamples) -{ - if(((frameSizeSamples == 240) || (frameSizeSamples == 480)) && - (rate == 13300)) - { - return true; - } - else if(((frameSizeSamples == 160) || (frameSizeSamples == 320)) && - (rate == 15200)) - { - return true; - } - else - { - return false; - } -#endif -} - -// Check if the payload type is valid -WebRtc_Word16 -ACMCodecDB::ValidPayloadType( - const int payloadType) -{ - if((payloadType < 0) || (payloadType > 127)) - { - return -1; - } - return 0; -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_codec_database.h b/modules/audio_coding/main/source/acm_codec_database.h deleted file mode 100644 index c83e84b16..000000000 --- a/modules/audio_coding/main/source/acm_codec_database.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_CODEC_DATABASE_H -#define ACM_CODEC_DATABASE_H - -#include "acm_generic_codec.h" -#include "common_types.h" -#include "typedefs.h" -#include "webrtc_neteq.h" - -namespace webrtc -{ - -// These might need to be increased if adding a new codec to -// the database -#define MAX_NR_OF_CODECS 52 -#define MAX_NR_OF_PACSIZES 6 -#define VERSION_SIZE 1000 - -class ACMCodecDB -{ -public: - static WebRtc_Word16 Codec( - WebRtc_Word16 listnr, - CodecInst* codec_inst); - - - static WebRtc_Word16 CodecNumber( - const CodecInst* codec_inst, - WebRtc_Word16& mirrorID, - WebRtc_Word8* errMessage, - WebRtc_Word16 maxErrMsgLenByte); - - static WebRtc_Word16 CodecNumber( - const CodecInst* codec_inst, - WebRtc_Word16& mirrorID); - - static WebRtc_Word16 ReceiverCodecNumber( - const CodecInst& codecInst, - WebRtc_Word16& mirrorID); - - static WebRtc_Word16 NoOfCodecs(); - - static WebRtc_Word16 NoNetEqDecoders(); - - static WebRtc_Word32 CodecFreq( - WebRtc_Word16 listnr); - - static WebRtc_Word16 BasicCodingBlock( - WebRtc_Word16 listnr); - - static enum WebRtcNetEQDecoder* NetEqDecoders(); - - static WebRtc_Word16 CodecsVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - static WebRtc_Word16 MirrorID( - const WebRtc_Word16 codecID); - - static ACMGenericCodec* CreateCodecInstance( - const CodecInst* codecInst); - - static void initACMCodecDB(); - - static bool IsRateValid( - const WebRtc_Word16 listNr, - const WebRtc_Word32 rate); - - static bool IsISACRateValid( - const WebRtc_Word32 rate); - - static bool IsILBCRateValid( - const WebRtc_Word32 rate, - const WebRtc_Word16 frameSizeSamples); - - static WebRtc_Word16 ValidPayloadType( - const int payloadType); - - static WebRtc_Word16 - pcm16b, - pcm16bwb, - pcm16bswb32, - pcm16bswb48, - pcmu, - pcma, - ilbc, - gsmAMR, - gsmAMRWB, - g722, - g722_1_32, - g722_1_24, - g722_1_16, - g722_1C_48, - g722_1C_32, - g722_1C_24, - g729, - isac, - isacswb, - gsmfr, - speex8, - speex16, - cnNB, - cnWB, - cnSWB, - avt, - red; - - static WebRtc_Word16 _noOfCodecs; - static WebRtc_Word16 _noNetEqDecoders; - static WebRtc_Word16 _noPayloads; - - // Information about the supported codecs - static CodecInst _mycodecs[MAX_NR_OF_CODECS]; - static enum WebRtcNetEQDecoder _netEqDecoders[MAX_NR_OF_CODECS]; - static WebRtc_UWord16 _allowedPacSizesSmpl[MAX_NR_OF_CODECS][MAX_NR_OF_PACSIZES]; - static WebRtc_UWord8 _nrOfAllowedPacSizes[MAX_NR_OF_CODECS]; - static WebRtc_UWord16 _basicCodingBlockSmpl[MAX_NR_OF_CODECS]; - static WebRtc_UWord16 _channelSupport[MAX_NR_OF_CODECS]; - -private: - static bool _isInitiated; - static WebRtc_Word8 _versions[VERSION_SIZE]; - static WebRtc_UWord32 _versionStringSize; -}; - -} // namespace webrtc - -#endif //ACM_CODEC_DATABASE_H diff --git a/modules/audio_coding/main/source/acm_common_defs.h b/modules/audio_coding/main/source/acm_common_defs.h deleted file mode 100644 index 7258fc7c0..000000000 --- a/modules/audio_coding/main/source/acm_common_defs.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_ACM_COMMON_DEFS_H -#define WEBRTC_ACM_COMMON_DEFS_H - -#include - -#include "audio_coding_module_typedefs.h" -#include "common_types.h" -#include "engine_configurations.h" -#include "typedefs.h" - - -// Checks for enabled codecs, we prevent enabling codecs which are not -// compatible. -#if ((defined WEBRTC_CODEC_ISAC) && (defined WEBRTC_CODEC_ISACFX)) - # error iSAC and iSACFX codecs cannot be enabled at the same time -#endif - -#ifdef WIN32 - // OS-dependent case-insensitive string comparison - #define STR_CASE_CMP(x,y) ::_stricmp(x,y) -#else - // OS-dependent case-insensitive string comparison - #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) -#endif - -namespace webrtc -{ - -// 60 ms is the maximum block size we support. An extra 20 ms is considered -// for safety if process() method is not called when it should be, i.e. we -// accept 20 ms of jitter. 80 ms @ 32 kHz (super wide-band) is 2560 samples. -#define AUDIO_BUFFER_SIZE_W16 2560 - -// There is one timestamp per each 10 ms of audio -// the audio buffer, at max, may contain 32 blocks of 10ms -// audio if the sampling frequency is 8000 Hz (80 samples per block). -// Therefore, The size of the buffer where we keep timestamps -// is defined as follows -#define TIMESTAMP_BUFFER_SIZE_W32 AUDIO_BUFFER_SIZE_W16/80 - -// The maximum size of a payload, that is 60 ms of PCM-16 @ 32 kHz stereo -#define MAX_PAYLOAD_SIZE_BYTE 7680 - -// General codec specific defines -#define ISACWB_DEFAULT_RATE 32000 -#define ISACSWB_DEFAULT_RATE 56000 -#define ISACWB_PAC_SIZE 480 -#define ISACSWB_PAC_SIZE 960 - -// An encoded bit-stream is labeled by one of the following enumerators. -// -// kNoEncoding : There has been no encoding. -// kActiveNormalEncoded : Active audio frame coded by the codec. -// kPassiveNormalEncoded : Passive audio frame coded by the codec. -// kPassiveDTXNB : Passive audio frame coded by narrow-band CN. -// kPassiveDTXWB : Passive audio frame coded by wide-band CN. -// kPassiveDTXSWB : Passive audio frame coded by super-wide-band CN. -// -enum WebRtcACMEncodingType -{ - kNoEncoding, - kActiveNormalEncoded, - kPassiveNormalEncoded, - kPassiveDTXNB, - kPassiveDTXWB, - kPassiveDTXSWB -}; - -// A structure which contains codec parameters. For instance, used when -// initializing encoder and decoder. -// -// codecInstant : c.f. common_types.h -// enableDTX : set true to enable DTX. If codec does not have -// internal DTX, this will enable VAD. -// enableVAD : set true to enable VAD. -// vadMode : VAD mode, c.f. audio_coding_module_typedefs.h -// for possible values. -struct WebRtcACMCodecParams -{ - CodecInst codecInstant; - bool enableDTX; - bool enableVAD; - ACMVADMode vadMode; -}; - -// A structure that encapsulates audio buffer and related parameters -// used for synchronization of audio of two ACMs. -// -// inAudio : same as ACMGenericCodec::_inAudio -// inAudioIxRead : same as ACMGenericCodec::_inAudioIxRead -// inAudioIxWrite : same as ACMGenericCodec::_inAudioIxWrite -// inTimestamp : same as ACMGenericCodec::_inTimestamp -// inTimestampIxWrite : same as ACMGenericCodec::_inTImestampIxWrite -// lastTimestamp : same as ACMGenericCodec::_lastTimestamp -// lastInTimestamp : same as AudioCodingModuleImpl::_lastInTimestamp -// -struct WebRtcACMAudioBuff -{ - WebRtc_Word16 inAudio[AUDIO_BUFFER_SIZE_W16]; - WebRtc_Word16 inAudioIxRead; - WebRtc_Word16 inAudioIxWrite; - WebRtc_UWord32 inTimestamp[TIMESTAMP_BUFFER_SIZE_W32]; - WebRtc_Word16 inTimestampIxWrite; - WebRtc_UWord32 lastTimestamp; - WebRtc_UWord32 lastInTimestamp; -}; - -} // namespace webrtc - -#endif diff --git a/modules/audio_coding/main/source/acm_dtmf_detection.cc b/modules/audio_coding/main/source/acm_dtmf_detection.cc deleted file mode 100644 index cab9ea57e..000000000 --- a/modules/audio_coding/main/source/acm_dtmf_detection.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#include "acm_dtmf_detection.h" -#include "audio_coding_module_typedefs.h" - -namespace webrtc -{ - -ACMDTMFDetection::ACMDTMFDetection() -{ -} - - -ACMDTMFDetection::~ACMDTMFDetection() -{ -} - - -WebRtc_Word16 -ACMDTMFDetection::Enable( - ACMCountries /* cpt */) -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFDetection::Disable() -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFDetection::Detect( - const WebRtc_Word16* /* inAudioBuff */, - const WebRtc_UWord16 /* inBuffLenWord16 */, - const WebRtc_Word32 /* inFreqHz */, - bool& /* toneDetected */, - WebRtc_Word16& /* tone */) -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFDetection::GetVersion( - WebRtc_Word8* /* version */, - WebRtc_UWord32& /* remainingBufferInBytes */, - WebRtc_UWord32& /* position */) -{ - return -1; -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_dtmf_detection.h b/modules/audio_coding/main/source/acm_dtmf_detection.h deleted file mode 100644 index 0142fc904..000000000 --- a/modules/audio_coding/main/source/acm_dtmf_detection.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_DTMF_DETECTION_H -#define ACM_DTMF_DETECTION_H - -#include "acm_resampler.h" -#include "audio_coding_module_typedefs.h" -#include "typedefs.h" - -namespace webrtc -{ - -class ACMDTMFDetection -{ -public: - ACMDTMFDetection(); - ~ACMDTMFDetection(); - WebRtc_Word16 Enable(ACMCountries cpt = ACMDisableCountryDetection); - WebRtc_Word16 Disable(); - WebRtc_Word16 Detect( - const WebRtc_Word16* inAudioBuff, - const WebRtc_UWord16 inBuffLenWord16, - const WebRtc_Word32 inFreqHz, - bool& toneDetected, - WebRtc_Word16& tone); - - static WebRtc_Word16 GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - -private: - ACMResampler _resampler; - bool _init; -}; - -} // namespace webrtc - -#endif // ACM_DTMF_DETECTION_H diff --git a/modules/audio_coding/main/source/acm_dtmf_playout.cc b/modules/audio_coding/main/source/acm_dtmf_playout.cc deleted file mode 100644 index 8286cb540..000000000 --- a/modules/audio_coding/main/source/acm_dtmf_playout.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_dtmf_playout.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_AVT - -ACMDTMFPlayout::ACMDTMFPlayout( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMDTMFPlayout::~ACMDTMFPlayout() -{ - return; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFPlayout::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMDTMFPlayout::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMDTMFPlayout::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalCreateEncoder() -{ - return -1; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMDTMFPlayout::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - -void -ACMDTMFPlayout::DestructEncoderSafe() -{ - return; -} - -void -ACMDTMFPlayout::DestructDecoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMDTMFPlayout::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -#else //===================== Actual Implementation ======================= - -ACMDTMFPlayout::ACMDTMFPlayout( - WebRtc_Word16 codecID) -{ - _codecID = codecID; -} - - -ACMDTMFPlayout::~ACMDTMFPlayout() -{ - return; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return 0; -} - - -WebRtc_Word16 -ACMDTMFPlayout::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // DTMFPlayout has no instance - return 0; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // DTMFPlayout has no instance - return 0; -} - - -WebRtc_Word32 -ACMDTMFPlayout::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_AVT_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderAVT, codecInst.pltype, NULL, 8000); - SET_AVT_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMDTMFPlayout::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalCreateEncoder() -{ - // DTMFPlayout has no instance - return 0; -} - - -WebRtc_Word16 -ACMDTMFPlayout::InternalCreateDecoder() -{ - // DTMFPlayout has no instance - return 0; -} - - -void -ACMDTMFPlayout::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - // DTMFPlayout has no instance - return; -} - - -void -ACMDTMFPlayout::DestructEncoderSafe() -{ - // DTMFPlayout has no instance - return; -} - -void -ACMDTMFPlayout::DestructDecoderSafe() -{ - // DTMFPlayout has no instance - return; -} - - -WebRtc_Word16 -ACMDTMFPlayout::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderAVT); -} -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_dtmf_playout.h b/modules/audio_coding/main/source/acm_dtmf_playout.h deleted file mode 100644 index 9f38baa33..000000000 --- a/modules/audio_coding/main/source/acm_dtmf_playout.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_DTMF_PLAYOUT_H -#define ACM_DTMF_PLAYOUT_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -class ACMDTMFPlayout : public ACMGenericCodec -{ -public: - ACMDTMFPlayout(WebRtc_Word16 codecID); - ~ACMDTMFPlayout(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void InternalDestructEncoderInst( - void* ptrInst); -}; - -} // namespace webrtc - -#endif // ACM_DTMF_PLAYOUT_H - diff --git a/modules/audio_coding/main/source/acm_g722.cc b/modules/audio_coding/main/source/acm_g722.cc deleted file mode 100644 index 948910f47..000000000 --- a/modules/audio_coding/main/source/acm_g722.cc +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_g722.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" -#include "g722_interface.h" -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_G722 - -ACMG722::ACMG722( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMG722::~ACMG722() -{ - return; -} - - -WebRtc_Word32 -ACMG722::Add10MsDataSafe( - const WebRtc_UWord32 /* timestamp */, - const WebRtc_Word16* /* data */, - const WebRtc_UWord16 /* lengthSmpl */, - const WebRtc_UWord8 /* audioChannel */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMG722::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMG722::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMG722::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMG722::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMG722::DestructDecoderSafe() -{ - return; -} - - -void -ACMG722::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMG722::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - - -// Encoder and decoder memory -struct ACMG722EncStr { - G722EncInst* inst; // instance for left channel in case of stereo - G722EncInst* instRight; // instance for right channel in case of stereo -}; -struct ACMG722DecStr { - G722DecInst* inst; // instance for left channel in case of stereo - G722DecInst* instRight; // instance for right channel in case of stereo -}; - -ACMG722::ACMG722( - WebRtc_Word16 codecID) -{ - // Encoder - _ptrEncStr = new ACMG722EncStr; - if(_ptrEncStr != NULL) - { - _ptrEncStr->inst = NULL; - _ptrEncStr->instRight = NULL; - } - // Decoder - _ptrDecStr = new ACMG722DecStr; - if(_ptrDecStr != NULL) - { - _ptrDecStr->inst = NULL; - _ptrDecStr->instRight = NULL; // Not used - } - _codecID = codecID; - return; -} - - -ACMG722::~ACMG722() -{ - if(_ptrEncStr != NULL) - { - if(_ptrEncStr->inst != NULL) - { - WebRtcG722_FreeEncoder(_ptrEncStr->inst); - _ptrEncStr->inst = NULL; - } - - if(_ptrEncStr->instRight != NULL) - { - WebRtcG722_FreeEncoder(_ptrEncStr->instRight); - _ptrEncStr->instRight = NULL; - } - - delete _ptrEncStr; - _ptrEncStr = NULL; - } - if(_ptrDecStr != NULL) - { - if(_ptrDecStr->inst != NULL) - { - WebRtcG722_FreeDecoder(_ptrDecStr->inst); - _ptrDecStr->inst = NULL; - } - if(_ptrDecStr->instRight != NULL) - { - WebRtcG722_FreeDecoder(_ptrDecStr->instRight); - _ptrDecStr->instRight = NULL; - } - - delete _ptrDecStr; - _ptrDecStr = NULL; - } - return; -} - - -WebRtc_Word32 -ACMG722::Add10MsDataSafe( - const WebRtc_UWord32 timestamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 lengthSmpl, - const WebRtc_UWord8 audioChannel) -{ - return ACMGenericCodec::Add10MsDataSafe((timestamp>>1), data, lengthSmpl, - audioChannel); -} - -WebRtc_Word16 -ACMG722::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - // If stereo, split input signal in left and right channel before encoding - if(_noChannels == 2) { - WebRtc_Word16 leftChannel[960]; - WebRtc_Word16 rightChannel[960]; - WebRtc_UWord8 outLeft[480]; - WebRtc_UWord8 outRight[480]; - WebRtc_Word16 lenInBytes; - for (int i=0, j=0; i<_frameLenSmpl*2; i+=2, j++) { - leftChannel[j] = _inAudio[_inAudioIxRead+i]; - rightChannel[j] = _inAudio[_inAudioIxRead+i+1]; - } - lenInBytes = WebRtcG722_Encode(_encoderInstPtr, - leftChannel, _frameLenSmpl, (WebRtc_Word16*)outLeft); - lenInBytes += WebRtcG722_Encode(_encoderInstPtrRight, - rightChannel, _frameLenSmpl, (WebRtc_Word16*)outRight); - *bitStreamLenByte = lenInBytes; - - // Interleave the 4 bits per sample from left and right channel - for (int i=0, j=0; i> 4); - bitStream[i+1] = ((outRight[j] & 0x0F) << 4) + (outLeft[j] & 0x0F); - } - } else { - *bitStreamLenByte = WebRtcG722_Encode(_encoderInstPtr, - &_inAudio[_inAudioIxRead], _frameLenSmpl, (WebRtc_Word16*)bitStream); - } - - // increment the read index this tell the caller how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl*_noChannels; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMG722::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMG722::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - if(codecParams->codecInstant.channels == 2) - { - // Create codec struct for right channel - if (_ptrEncStr->instRight == NULL) - { - WebRtcG722_CreateEncoder(&_ptrEncStr->instRight); - if(_ptrEncStr->instRight == NULL) - { - return -1; - } - } - _encoderInstPtrRight = (G722EncInst*)_ptrEncStr->instRight; - if (WebRtcG722_EncoderInit(_encoderInstPtrRight) < 0) - { - return -1; - } - } - - return WebRtcG722_EncoderInit(_encoderInstPtr); -} - - -WebRtc_Word16 -ACMG722::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return WebRtcG722_DecoderInit(_decoderInstPtr); -} - - -WebRtc_Word32 -ACMG722::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // TODO: log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G722_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderG722, codecInst.pltype, - _decoderInstPtr, 16000); - SET_G722_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMG722::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722::InternalCreateEncoder() -{ - if(_ptrEncStr == NULL) - { - // this structure must be created at the costructor - // if it is still NULL then there is a probelm and - // we dont continue - return -1; - } - WebRtcG722_CreateEncoder(&_ptrEncStr->inst); - if(_ptrEncStr->inst == NULL) - { - return -1; - } - _encoderInstPtr = (G722EncInst*)_ptrEncStr->inst; - return 0; -} - - -void -ACMG722::DestructEncoderSafe() -{ - if(_ptrEncStr != NULL) - { - if(_ptrEncStr->inst != NULL) - { - WebRtcG722_FreeEncoder(_ptrEncStr->inst); - _ptrEncStr->inst = NULL; - } - } - _encoderExist = false; - _encoderInitialized = false; - -} - -WebRtc_Word16 -ACMG722::InternalCreateDecoder() -{ - if(_ptrDecStr == NULL) - { - // this structure must be created at the costructor - // if it is still NULL then there is a probelm and - // we dont continue - return -1; - } - - WebRtcG722_CreateDecoder(&_ptrDecStr->inst); - if(_ptrDecStr->inst == NULL) - { - return -1; - } - _decoderInstPtr = (G722DecInst*)_ptrDecStr->inst; - return 0; -} - -void -ACMG722::DestructDecoderSafe() -{ - _decoderExist = false; - _decoderInitialized = false; - if(_ptrDecStr != NULL) - { - if(_ptrDecStr->inst != NULL) - { - WebRtcG722_FreeDecoder(_ptrDecStr->inst); - _ptrDecStr->inst = NULL; - } - } -} - - -void -ACMG722::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcG722_FreeEncoder((G722EncInst*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMG722::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderG722); -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_g722.h b/modules/audio_coding/main/source/acm_g722.h deleted file mode 100644 index a2035cfea..000000000 --- a/modules/audio_coding/main/source/acm_g722.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_G722_H -#define ACM_G722_H - -#include "acm_generic_codec.h" - -typedef struct WebRtcG722EncInst G722EncInst; -typedef struct WebRtcG722DecInst G722DecInst; - -namespace webrtc -{ - -// forward declaration -struct ACMG722EncStr; -struct ACMG722DecStr; - -class ACMG722 : public ACMGenericCodec -{ -public: - ACMG722(WebRtc_Word16 codecID); - ~ACMG722(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - WebRtc_Word32 Add10MsDataSafe( - const WebRtc_UWord32 timestamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 lengthSmpl, - const WebRtc_UWord8 audioChannel); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - ACMG722EncStr* _ptrEncStr; - ACMG722DecStr* _ptrDecStr; - - G722EncInst* _encoderInstPtr; - G722EncInst* _encoderInstPtrRight; // Prepared for stereo - G722DecInst* _decoderInstPtr; -}; - -} // namespace webrtc - -#endif // ACM_G722_H diff --git a/modules/audio_coding/main/source/acm_g7221.cc b/modules/audio_coding/main/source/acm_g7221.cc deleted file mode 100644 index 76cc3ad39..000000000 --- a/modules/audio_coding/main/source/acm_g7221.cc +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_g7221.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_G722_1 - // NOTE! G.722.1 is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used G.722.1 API file. - // #include "g7221_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_G722_1 - -ACMG722_1::ACMG722_1( - WebRtc_Word16 /* codecID */) -{ - return; -} - -ACMG722_1::~ACMG722_1() -{ - return; -} - - -WebRtc_Word16 -ACMG722_1::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMG722_1::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - -ACMGenericCodec* -ACMG722_1::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722_1::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMG722_1::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMG722_1::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMG722_1::DestructDecoderSafe() -{ - return; -} - - -void -ACMG722_1::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMG722_1::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real G.722.1 wrapper -struct G722_1_Inst_t_; - -extern WebRtc_Word16 WebRtcG7221_CreateEnc16(G722_1_16_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_CreateEnc24(G722_1_24_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_CreateEnc32(G722_1_32_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_CreateDec16(G722_1_16_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221_CreateDec24(G722_1_24_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221_CreateDec32(G722_1_32_decinst_t_** decInst); - -extern WebRtc_Word16 WebRtcG7221_FreeEnc16(G722_1_16_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_FreeEnc24(G722_1_24_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_FreeEnc32(G722_1_32_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221_FreeDec16(G722_1_16_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221_FreeDec24(G722_1_24_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221_FreeDec32(G722_1_32_decinst_t_** decInst); - -extern WebRtc_Word16 WebRtcG7221_EncoderInit16(G722_1_16_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221_EncoderInit24(G722_1_24_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221_EncoderInit32(G722_1_32_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221_DecoderInit16(G722_1_16_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG7221_DecoderInit24(G722_1_24_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG7221_DecoderInit32(G722_1_32_decinst_t_* decInst); - -extern WebRtc_Word16 WebRtcG7221_Encode16(G722_1_16_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221_Encode24(G722_1_24_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221_Encode32(G722_1_32_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); - -extern WebRtc_Word16 WebRtcG7221_Decode16(G722_1_16_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221_Decode24(G722_1_24_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221_Decode32(G722_1_32_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); - -extern WebRtc_Word16 WebRtcG7221_DecodePlc16(G722_1_16_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); -extern WebRtc_Word16 WebRtcG7221_DecodePlc24(G722_1_24_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); -extern WebRtc_Word16 WebRtcG7221_DecodePlc32(G722_1_32_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); - - -ACMG722_1::ACMG722_1( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_encoderInstPtrRight(NULL), -_decoderInstPtr(NULL), -_encoderInst16Ptr(NULL), -_encoderInst16PtrR(NULL), -_encoderInst24Ptr(NULL), -_encoderInst24PtrR(NULL), -_encoderInst32Ptr(NULL), -_encoderInst32PtrR(NULL), -_decoderInst16Ptr(NULL), -_decoderInst24Ptr(NULL), -_decoderInst32Ptr(NULL) -{ - _codecID = codecID; - if(_codecID == ACMCodecDB::g722_1_16) - { - _operationalRate = 16000; - } - else if(_codecID == ACMCodecDB::g722_1_24) - { - _operationalRate = 24000; - } - else if(_codecID == ACMCodecDB::g722_1_32) - { - _operationalRate = 32000; - } - else - { - _operationalRate = -1; - } - return; -} - -ACMG722_1::~ACMG722_1() -{ - if(_encoderInstPtr != NULL) - { - delete _encoderInstPtr; - _encoderInstPtr = NULL; - } - if(_encoderInstPtrRight != NULL) - { - delete _encoderInstPtrRight; - _encoderInstPtrRight = NULL; - } - if(_decoderInstPtr != NULL) - { - delete _decoderInstPtr; - _decoderInstPtr = NULL; - } - - switch(_operationalRate) - { - case 16000: - { - _encoderInst16Ptr = NULL; - _encoderInst16PtrR = NULL; - _decoderInst16Ptr = NULL; - break; - } - case 24000: - { - _encoderInst24Ptr = NULL; - _encoderInst24PtrR = NULL; - _decoderInst24Ptr = NULL; - break; - } - case 32000: - { - _encoderInst32Ptr = NULL; - _encoderInst32PtrR = NULL; - _decoderInst32Ptr = NULL; - break; - } - default: - { - break; - } - } - return; -} - - -WebRtc_Word16 -ACMG722_1::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 leftChannel[320]; - WebRtc_Word16 rightChannel[320]; - WebRtc_Word16 lenInBytes; - WebRtc_Word16 outB[160]; - - // If stereo, split input signal in left and right channel before encoding - if(_noChannels == 2) - { - for (int i=0, j=0; i<_frameLenSmpl*2; i+=2, j++) { - leftChannel[j] = _inAudio[_inAudioIxRead+i]; - rightChannel[j] = _inAudio[_inAudioIxRead+i+1]; - } - } else { - memcpy(leftChannel, &_inAudio[_inAudioIxRead], 320); - } - - switch(_operationalRate) - { - case 16000: - { - lenInBytes = WebRtcG7221_Encode16(_encoderInst16Ptr, - leftChannel, 320, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221_Encode16(_encoderInst16PtrR, - rightChannel, 320, &outB[lenInBytes/2]); - } - break; - } - case 24000: - { - lenInBytes = WebRtcG7221_Encode24(_encoderInst24Ptr, - leftChannel, 320, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221_Encode24(_encoderInst24PtrR, - rightChannel, 320, &outB[lenInBytes/2]); - } - break; - } - case 32000: - { - lenInBytes = WebRtcG7221_Encode32(_encoderInst32Ptr, - leftChannel, 320, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221_Encode32(_encoderInst32PtrR, - rightChannel, 320, &outB[lenInBytes/2]); - } - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitEncode: Wrong rate for G722_1."); - return -1; - break; - } - } - memcpy(bitStream, outB, lenInBytes); - *bitStreamLenByte = lenInBytes; - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += 320*_noChannels; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMG722_1::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMG722_1::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - WebRtc_Word16 ret; - - switch(_operationalRate) - { - case 16000: - { - ret = WebRtcG7221_EncoderInit16(_encoderInst16PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit16(_encoderInst16Ptr); - break; - } - case 24000: - { - ret = WebRtcG7221_EncoderInit24(_encoderInst24PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit24(_encoderInst24Ptr); - break; - } - case 32000: - { - ret = WebRtcG7221_EncoderInit32(_encoderInst32PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221_EncoderInit32(_encoderInst32Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitEncoder: Wrong rate for G722_1."); - return -1; - break; - } - } -} - - -WebRtc_Word16 -ACMG722_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - switch(_operationalRate) - { - case 16000: - { - return WebRtcG7221_DecoderInit16(_decoderInst16Ptr); - break; - } - case 24000: - { - return WebRtcG7221_DecoderInit24(_decoderInst24Ptr); - break; - } - case 32000: - { - return WebRtcG7221_DecoderInit32(_decoderInst32Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: Wrong rate for G722_1."); - return -1; - break; - } - } -} - - -WebRtc_Word32 -ACMG722_1::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // Todo: - // log error - return -1; - } - // NetEq has an array of pointers to WebRtcNetEQ_CodecDef. - // get an entry of that array (neteq wrapper will allocate memory) - // by calling "netEq->CodecDef", where "NETEQ_CODEC_G722_1_XX" would - // be the index of the entry. - // Fill up the given structure by calling - // "SET_CODEC_PAR" & "SET_G722_1_XX_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - switch(_operationalRate) - { - case 16000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1_16, codecInst.pltype, - _decoderInst16Ptr, 16000); - SET_G722_1_16_FUNCTIONS((codecDef)); - break; - } - case 24000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1_24, codecInst.pltype, - _decoderInst24Ptr, 16000); - SET_G722_1_24_FUNCTIONS((codecDef)); - break; - } - case 32000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1_32, codecInst.pltype, - _decoderInst32Ptr, 16000); - SET_G722_1_32_FUNCTIONS((codecDef)); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodecDef: Wrong rate for G722_1."); - return -1; - break; - } - } - return 0; -} - -ACMGenericCodec* -ACMG722_1::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722_1::InternalCreateEncoder() -{ - if((_encoderInstPtr == NULL) || (_encoderInstPtrRight == NULL)) - { - return -1; - } - switch(_operationalRate) - { - case 16000: - { - WebRtcG7221_CreateEnc16(&_encoderInst16Ptr); - WebRtcG7221_CreateEnc16(&_encoderInst16PtrR); - break; - } - case 24000: - { - WebRtcG7221_CreateEnc24(&_encoderInst24Ptr); - WebRtcG7221_CreateEnc24(&_encoderInst24PtrR); - break; - } - case 32000: - { - WebRtcG7221_CreateEnc32(&_encoderInst32Ptr); - WebRtcG7221_CreateEnc32(&_encoderInst32PtrR); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: Wrong rate for G722_1."); - return -1; - break; - } - } - return 0; -} - - -void -ACMG722_1::DestructEncoderSafe() -{ - _encoderExist = false; - _encoderInitialized = false; - if(_encoderInstPtr != NULL) - { - delete _encoderInstPtr; - _encoderInstPtr = NULL; - } - if(_encoderInstPtrRight != NULL) - { - delete _encoderInstPtrRight; - _encoderInstPtrRight = NULL; - } - _encoderInst16Ptr = NULL; - _encoderInst24Ptr = NULL; - _encoderInst32Ptr = NULL; -} - - -WebRtc_Word16 -ACMG722_1::InternalCreateDecoder() -{ - if(_decoderInstPtr == NULL) - { - return -1; - } - switch(_operationalRate) - { - case 16000: - { - WebRtcG7221_CreateDec16(&_decoderInst16Ptr); - break; - } - case 24000: - { - WebRtcG7221_CreateDec24(&_decoderInst24Ptr); - break; - } - case 32000: - { - WebRtcG7221_CreateDec32(&_decoderInst32Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateDecoder: Wrong rate for G722_1."); - return -1; - break; - } - } - return 0; -} - - -void -ACMG722_1::DestructDecoderSafe() -{ - _decoderExist = false; - _decoderInitialized = false; - if(_decoderInstPtr != NULL) - { - delete _decoderInstPtr; - _decoderInstPtr = NULL; - } - _decoderInst16Ptr = NULL; - _decoderInst24Ptr = NULL; - _decoderInst32Ptr = NULL; -} - - -void -ACMG722_1::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - delete ptrInst; - } - return; -} - - -WebRtc_Word16 -ACMG722_1::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - switch(_operationalRate) - { - case 16000: - { - return netEq->RemoveCodec(kDecoderG722_1_16); - } - case 24000: - { - return netEq->RemoveCodec(kDecoderG722_1_24); - } - case 32000: - { - return netEq->RemoveCodec(kDecoderG722_1_32); - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "UnregisterFromNetEqSafe: Wrong rate for G722_1."); - return -1; - } - } -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_g7221.h b/modules/audio_coding/main/source/acm_g7221.h deleted file mode 100644 index 829fbf09c..000000000 --- a/modules/audio_coding/main/source/acm_g7221.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_G722_1_H -#define ACM_G722_1_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -// forward declaration -struct G722_1_16_encinst_t_; -struct G722_1_16_decinst_t_; - -struct G722_1_24_encinst_t_; -struct G722_1_24_decinst_t_; - -struct G722_1_32_encinst_t_; -struct G722_1_32_decinst_t_; - -struct G722_1_Inst_t_; - - -class ACMG722_1 : public ACMGenericCodec -{ -public: - ACMG722_1(WebRtc_Word16 codecID); - ~ACMG722_1(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - WebRtc_Word32 _operationalRate; - - G722_1_Inst_t_* _encoderInstPtr; - G722_1_Inst_t_* _encoderInstPtrRight; //Used in stereo mode - G722_1_Inst_t_* _decoderInstPtr; - - // Only one set of these pointer is valid at any instance - G722_1_16_encinst_t_* _encoderInst16Ptr; - G722_1_16_encinst_t_* _encoderInst16PtrR; - G722_1_24_encinst_t_* _encoderInst24Ptr; - G722_1_24_encinst_t_* _encoderInst24PtrR; - G722_1_32_encinst_t_* _encoderInst32Ptr; - G722_1_32_encinst_t_* _encoderInst32PtrR; - - // Only one of these pointer is valid at any instance - G722_1_16_decinst_t_* _decoderInst16Ptr; - G722_1_24_decinst_t_* _decoderInst24Ptr; - G722_1_32_decinst_t_* _decoderInst32Ptr; -}; - -} // namespace webrtc - -#endif // ACM_G722_1_H diff --git a/modules/audio_coding/main/source/acm_g7221c.cc b/modules/audio_coding/main/source/acm_g7221c.cc deleted file mode 100644 index 9aede6edf..000000000 --- a/modules/audio_coding/main/source/acm_g7221c.cc +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_g7221c.h" -#include "acm_neteq.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" -#include "trace.h" - -#ifdef WEBRTC_CODEC_G722_1C - // NOTE! G.722.1C is not included in the open-source package. Modify this file or your - // codec API to match the function call and name of used G.722.1C API file. - // #include "g7221C_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_G722_1C - -ACMG722_1C::ACMG722_1C( - WebRtc_Word16 /* codecID */) -{ - return; -} - -ACMG722_1C::~ACMG722_1C() -{ - return; -} - - -WebRtc_Word16 -ACMG722_1C::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1C::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1C::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG722_1C::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMG722_1C::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - -ACMGenericCodec* -ACMG722_1C::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722_1C::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMG722_1C::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMG722_1C::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMG722_1C::DestructDecoderSafe() -{ - return; -} - - -void -ACMG722_1C::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMG722_1C::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= -// Remove when integrating a real G,722.1 wrapper -struct G722_1_Inst_t_; - -extern WebRtc_Word16 WebRtcG7221C_CreateEnc24(G722_1C_24_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_CreateEnc32(G722_1C_32_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_CreateEnc48(G722_1C_48_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_CreateDec24(G722_1C_24_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221C_CreateDec32(G722_1C_32_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221C_CreateDec48(G722_1C_48_decinst_t_** decInst); - -extern WebRtc_Word16 WebRtcG7221C_FreeEnc24(G722_1C_24_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_FreeEnc32(G722_1C_32_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_FreeEnc48(G722_1C_48_encinst_t_** encInst); -extern WebRtc_Word16 WebRtcG7221C_FreeDec24(G722_1C_24_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221C_FreeDec32(G722_1C_32_decinst_t_** decInst); -extern WebRtc_Word16 WebRtcG7221C_FreeDec48(G722_1C_48_decinst_t_** decInst); - - -extern WebRtc_Word16 WebRtcG7221C_EncoderInit24(G722_1C_24_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221C_EncoderInit32(G722_1C_32_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221C_EncoderInit48(G722_1C_48_encinst_t_* encInst); -extern WebRtc_Word16 WebRtcG7221C_DecoderInit24(G722_1C_24_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG7221C_DecoderInit32(G722_1C_32_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG7221C_DecoderInit48(G722_1C_48_decinst_t_* decInst); - -extern WebRtc_Word16 WebRtcG7221C_Encode24(G722_1C_24_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221C_Encode32(G722_1C_32_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221C_Encode48(G722_1C_48_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 len, - WebRtc_Word16* output); - -extern WebRtc_Word16 WebRtcG7221C_Decode24(G722_1C_24_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221C_Decode32(G722_1C_32_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG7221C_Decode48(G722_1C_48_decinst_t_* decInst, - WebRtc_Word16* bitstream, - WebRtc_Word16 len, - WebRtc_Word16* output); - -extern WebRtc_Word16 WebRtcG7221C_DecodePlc24(G722_1C_24_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); -extern WebRtc_Word16 WebRtcG7221C_DecodePlc32(G722_1C_32_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); -extern WebRtc_Word16 WebRtcG7221C_DecodePlc48(G722_1C_48_decinst_t_* decInst, - WebRtc_Word16* output, - WebRtc_Word16 nrLostFrames); - - -ACMG722_1C::ACMG722_1C( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_encoderInstPtrRight(NULL), -_decoderInstPtr(NULL), -_encoderInst24Ptr(NULL), -_encoderInst24PtrR(NULL), -_encoderInst32Ptr(NULL), -_encoderInst32PtrR(NULL), -_encoderInst48Ptr(NULL), -_encoderInst48PtrR(NULL), -_decoderInst24Ptr(NULL), -_decoderInst32Ptr(NULL), -_decoderInst48Ptr(NULL) -{ - _codecID = codecID; - if(_codecID == ACMCodecDB::g722_1C_24) - { - _operationalRate = 24000; - } - else if(_codecID == ACMCodecDB::g722_1C_32) - { - _operationalRate = 32000; - } - else if(_codecID == ACMCodecDB::g722_1C_48) - { - _operationalRate = 48000; - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Wrong codec id for G722_1c."); - - _operationalRate = -1; - } - return; -} - -ACMG722_1C::~ACMG722_1C() -{ - if(_encoderInstPtr != NULL) - { - delete _encoderInstPtr; - _encoderInstPtr = NULL; - } - if(_encoderInstPtrRight != NULL) - { - delete _encoderInstPtrRight; - _encoderInstPtrRight = NULL; - } - if(_decoderInstPtr != NULL) - { - delete _decoderInstPtr; - _decoderInstPtr = NULL; - } - - switch(_operationalRate) - { - case 24000: - { - _encoderInst24Ptr = NULL; - _encoderInst24PtrR = NULL; - _decoderInst24Ptr = NULL; - break; - } - case 32000: - { - _encoderInst32Ptr = NULL; - _encoderInst32PtrR = NULL; - _decoderInst32Ptr = NULL; - break; - } - case 48000: - { - _encoderInst48Ptr = NULL; - _encoderInst48PtrR = NULL; - _decoderInst48Ptr = NULL; - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Wrong rate for G722_1c."); - break; - } - } - return; -} - - -WebRtc_Word16 -ACMG722_1C::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 leftChannel[640]; - WebRtc_Word16 rightChannel[640]; - WebRtc_Word16 lenInBytes; - WebRtc_Word16 outB[240]; - - // If stereo, split input signal in left and right channel before encoding - if(_noChannels == 2) - { - for (int i=0, j=0; i<_frameLenSmpl*2; i+=2, j++) { - leftChannel[j] = _inAudio[_inAudioIxRead+i]; - rightChannel[j] = _inAudio[_inAudioIxRead+i+1]; - } - } else { - memcpy(leftChannel, &_inAudio[_inAudioIxRead], 640); - } - - switch(_operationalRate) - { - case 24000: - { - lenInBytes = WebRtcG7221C_Encode24(_encoderInst24Ptr, - leftChannel, 640, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221C_Encode24(_encoderInst24PtrR, - rightChannel, 640, &outB[lenInBytes/2]); - } - break; - } - case 32000: - { - lenInBytes = WebRtcG7221C_Encode32(_encoderInst32Ptr, - leftChannel, 640, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221C_Encode32(_encoderInst32PtrR, - rightChannel, 640, &outB[lenInBytes/2]); - } - break; - } - case 48000: - { - lenInBytes = WebRtcG7221C_Encode48(_encoderInst48Ptr, - leftChannel, 640, &outB[0]); - if (_noChannels == 2) - { - lenInBytes += WebRtcG7221C_Encode48(_encoderInst48PtrR, - rightChannel, 640, &outB[lenInBytes/2]); - } - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalEncode: Wrong rate for G722_1c."); - return -1; - break; - } - } - - memcpy(bitStream, outB, lenInBytes); - *bitStreamLenByte = lenInBytes; - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += 640*_noChannels; - - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMG722_1C::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - -WebRtc_Word16 -ACMG722_1C::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - WebRtc_Word16 ret; - - switch(_operationalRate) - { - case 24000: - { - ret = WebRtcG7221C_EncoderInit24(_encoderInst24PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit24(_encoderInst24Ptr); - break; - } - case 32000: - { - ret = WebRtcG7221C_EncoderInit32(_encoderInst32PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit32(_encoderInst32Ptr); - break; - } - case 48000: - { - ret = WebRtcG7221C_EncoderInit48(_encoderInst48PtrR); - if (ret < 0) { - return ret; - } - return WebRtcG7221C_EncoderInit48(_encoderInst48Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitEncode: Wrong rate for G722_1c."); - return -1; - break; - } - } -} - - -WebRtc_Word16 -ACMG722_1C::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - switch(_operationalRate) - { - case 24000: - { - return WebRtcG7221C_DecoderInit24(_decoderInst24Ptr); - break; - } - case 32000: - { - return WebRtcG7221C_DecoderInit32(_decoderInst32Ptr); - break; - } - case 48000: - { - return WebRtcG7221C_DecoderInit48(_decoderInst48Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: Wrong rate for G722_1c."); - return -1; - break; - } - } -} - - -WebRtc_Word32 -ACMG722_1C::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodeDef: decoder not initialized for G722_1c"); - return -1; - } - // NetEq has an array of pointers to WebRtcNetEQ_CodecDef. - // get an entry of that array (neteq wrapper will allocate memory) - // by calling "netEq->CodecDef", where "NETEQ_CODEC_G722_1_XX" would - // be the index of the entry. - // Fill up the given structure by calling - // "SET_CODEC_PAR" & "SET_G722_1_XX_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - switch(_operationalRate) - { - case 24000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1C_24, codecInst.pltype, - _decoderInst24Ptr, 32000); - SET_G722_1C_24_FUNCTIONS((codecDef)); - break; - } - case 32000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1C_32, codecInst.pltype, - _decoderInst32Ptr, 32000); - SET_G722_1C_32_FUNCTIONS((codecDef)); - break; - } - case 48000: - { - SET_CODEC_PAR((codecDef), kDecoderG722_1C_32, codecInst.pltype, - _decoderInst48Ptr, 32000); - SET_G722_1C_48_FUNCTIONS((codecDef)); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodeDef: Wrong rate for G722_1c."); - return -1; - break; - } - } - return 0; -} - -ACMGenericCodec* -ACMG722_1C::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG722_1C::InternalCreateEncoder() -{ - if((_encoderInstPtr == NULL) || (_encoderInstPtrRight == NULL)) - { - return -1; - } - switch(_operationalRate) - { - case 24000: - { - WebRtcG7221C_CreateEnc24(&_encoderInst24Ptr); - WebRtcG7221C_CreateEnc24(&_encoderInst24PtrR); - break; - } - case 32000: - { - WebRtcG7221C_CreateEnc32(&_encoderInst32Ptr); - WebRtcG7221C_CreateEnc32(&_encoderInst32PtrR); - break; - } - case 48000: - { - WebRtcG7221C_CreateEnc48(&_encoderInst48Ptr); - WebRtcG7221C_CreateEnc48(&_encoderInst48PtrR); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: Wrong rate for G722_1c."); - return -1; - break; - } - } - return 0; -} - - -void -ACMG722_1C::DestructEncoderSafe() -{ - _encoderExist = false; - _encoderInitialized = false; - if(_encoderInstPtr != NULL) - { - delete _encoderInstPtr; - _encoderInstPtr = NULL; - } - if(_encoderInstPtrRight != NULL) - { - delete _encoderInstPtrRight; - _encoderInstPtrRight = NULL; - } - _encoderInst24Ptr = NULL; - _encoderInst32Ptr = NULL; - _encoderInst48Ptr = NULL; -} - - -WebRtc_Word16 -ACMG722_1C::InternalCreateDecoder() -{ - if(_decoderInstPtr == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: cannot create decoder"); - return -1; - } - switch(_operationalRate) - { - case 24000: - { - WebRtcG7221C_CreateDec24(&_decoderInst24Ptr); - break; - } - case 32000: - { - WebRtcG7221C_CreateDec32(&_decoderInst32Ptr); - break; - } - case 48000: - { - WebRtcG7221C_CreateDec48(&_decoderInst48Ptr); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: Wrong rate for G722_1c."); - return -1; - break; - } - } - return 0; -} - - -void -ACMG722_1C::DestructDecoderSafe() -{ - _decoderExist = false; - _decoderInitialized = false; - if(_decoderInstPtr != NULL) - { - delete _decoderInstPtr; - _decoderInstPtr = NULL; - } - _decoderInst24Ptr = NULL; - _decoderInst32Ptr = NULL; - _decoderInst48Ptr = NULL; -} - - -void -ACMG722_1C::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - delete ptrInst; - } - return; -} - - -WebRtc_Word16 -ACMG722_1C::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - switch(_operationalRate) - { - case 24000: - { - return netEq->RemoveCodec(kDecoderG722_1C_24); - } - case 32000: - { - return netEq->RemoveCodec(kDecoderG722_1C_32); - } - case 48000: - { - return netEq->RemoveCodec(kDecoderG722_1C_48); - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Could not remove codec from NetEQ for G722_1c. \ -Sampling frequency doesn't match"); - return -1; - } - } -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_g7221c.h b/modules/audio_coding/main/source/acm_g7221c.h deleted file mode 100644 index 9fb55cb03..000000000 --- a/modules/audio_coding/main/source/acm_g7221c.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_G722_1C_H -#define ACM_G722_1C_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -// forward declaration -struct G722_1C_24_encinst_t_; -struct G722_1C_24_decinst_t_; - -struct G722_1C_32_encinst_t_; -struct G722_1C_32_decinst_t_; - -struct G722_1C_48_encinst_t_; -struct G722_1C_48_decinst_t_; - -struct G722_1_Inst_t_; - - -class ACMG722_1C : public ACMGenericCodec -{ -public: - ACMG722_1C(WebRtc_Word16 codecID); - ~ACMG722_1C(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - WebRtc_Word32 _operationalRate; - - G722_1_Inst_t_* _encoderInstPtr; - G722_1_Inst_t_* _encoderInstPtrRight; //Used in stereo mode - G722_1_Inst_t_* _decoderInstPtr; - - // Only one set of these pointer is valid at any instance - G722_1C_24_encinst_t_* _encoderInst24Ptr; - G722_1C_24_encinst_t_* _encoderInst24PtrR; - G722_1C_32_encinst_t_* _encoderInst32Ptr; - G722_1C_32_encinst_t_* _encoderInst32PtrR; - G722_1C_48_encinst_t_* _encoderInst48Ptr; - G722_1C_48_encinst_t_* _encoderInst48PtrR; - - // Only one of these pointer is valid at any instance - G722_1C_24_decinst_t_* _decoderInst24Ptr; - G722_1C_32_decinst_t_* _decoderInst32Ptr; - G722_1C_48_decinst_t_* _decoderInst48Ptr; -}; - -} // namespace webrtc; - -#endif // ACM_G722_1C_H diff --git a/modules/audio_coding/main/source/acm_g729.cc b/modules/audio_coding/main/source/acm_g729.cc deleted file mode 100644 index 6bfd9686b..000000000 --- a/modules/audio_coding/main/source/acm_g729.cc +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_g729.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_G729 - // NOTE! G.729 is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used G.729 API file. - // #include "g729_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_G729 - -ACMG729::ACMG729( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMG729::~ACMG729() -{ - return; -} - - -WebRtc_Word16 -ACMG729::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729::EnableDTX() -{ - return -1; -} - - -WebRtc_Word16 -ACMG729::DisableDTX() -{ - return -1; -} - -WebRtc_Word32 -ACMG729::ReplaceInternalDTXSafe( - const bool /*replaceInternalDTX*/) -{ - return -1; -} - -WebRtc_Word32 -ACMG729::IsInternalDTXReplacedSafe( - bool* /* internalDTXReplaced */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMG729::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMG729::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG729::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMG729::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMG729::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMG729::DestructDecoderSafe() -{ - return; -} - - -void -ACMG729::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMG729::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real GSM FR wrapper -extern WebRtc_Word16 WebRtcG729_CreateEnc(G729_encinst_t_** inst); -extern WebRtc_Word16 WebRtcG729_CreateDec(G729_decinst_t_** inst); -extern WebRtc_Word16 WebRtcG729_FreeEnc(G729_encinst_t_* inst); -extern WebRtc_Word16 WebRtcG729_FreeDec(G729_decinst_t_* inst); -extern WebRtc_Word16 WebRtcG729_Encode(G729_encinst_t_* encInst, WebRtc_Word16* input, - WebRtc_Word16 len, WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcG729_EncoderInit(G729_encinst_t_* encInst, WebRtc_Word16 mode); -extern WebRtc_Word16 WebRtcG729_Decode(G729_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG729_DecodeBwe(G729_decinst_t_* decInst, WebRtc_Word16* input); -extern WebRtc_Word16 WebRtcG729_DecodePlc(G729_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcG729_DecoderInit(G729_decinst_t_* decInst); - - -ACMG729::ACMG729( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL) -{ - _codecID = codecID; - _hasInternalDTX = true; - return; -} - - -ACMG729::~ACMG729() -{ - if(_encoderInstPtr != NULL) - { - // Delete encoder memory - WebRtcG729_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - // Delete decoder memory - WebRtcG729_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMG729::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - // Initialize before entering the loop - WebRtc_Word16 noEncodedSamples = 0; - WebRtc_Word16 tmpLenByte = 0; - WebRtc_Word16 vadDecision = 0; - *bitStreamLenByte = 0; - while(noEncodedSamples < _frameLenSmpl) - { - // Call G.729 encoder with pointer to encoder memory, input - // audio, number of samples and bitsream - tmpLenByte = WebRtcG729_Encode(_encoderInstPtr, - &_inAudio[_inAudioIxRead], 80, - (WebRtc_Word16*)(&(bitStream[*bitStreamLenByte]))); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += 80; - - // sanity check - if(tmpLenByte < 0) - { - // error has happened - *bitStreamLenByte = 0; - return -1; - } - - // increment number of written bytes - *bitStreamLenByte += tmpLenByte; - switch(tmpLenByte) - { - case 0: - { - if(0 == noEncodedSamples) - { - // this is the first 10 ms in this packet and there is - // no data generated, perhaps DTX is enabled and the - // codec is not generating any bit-stream for this 10 ms. - // we do not continue encoding this frame. - return 0; - } - break; - } - case 2: - { - // check if G.729 internal DTX is enabled - if(_hasInternalDTX && _dtxEnabled) - { - vadDecision = 0; - for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) - { - _vadLabel[n] = vadDecision; - } - } - // we got a SID and have to send out this packet no matter - // how much audio we have encoded - return *bitStreamLenByte; - } - case 10: - { - vadDecision = 1; - // this is a valid length just continue encoding - break; - } - default: - { - return -1; - } - } - - // update number of encoded samples - noEncodedSamples += 80; - } - - // update VAD decision vector - if(_hasInternalDTX && !vadDecision && _dtxEnabled) - { - for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) - { - _vadLabel[n] = vadDecision; - } - } - - // done encoding, return number of encoded bytes - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMG729::EnableDTX() -{ - if(_dtxEnabled) - { - // DTX already enabled, do nothing - return 0; - } - else if(_encoderExist) - { - // Re-init the G.729 encoder to turn on DTX - if(WebRtcG729_EncoderInit(_encoderInstPtr, 1) < 0) - { - return -1; - } - _dtxEnabled = true; - return 0; - } - else - { - return -1; - } -} - - -WebRtc_Word16 -ACMG729::DisableDTX() -{ - if(!_dtxEnabled) - { - // DTX already dissabled, do nothing - return 0; - } - else if(_encoderExist) - { - // Re-init the G.729 decoder to turn off DTX - if(WebRtcG729_EncoderInit(_encoderInstPtr, 0) < 0) - { - return -1; - } - _dtxEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - - -WebRtc_Word32 -ACMG729::ReplaceInternalDTXSafe( - const bool replaceInternalDTX) -{ - // This function is used to dissable the G.729 built in DTX and use an - // external instead. - - if(replaceInternalDTX == _hasInternalDTX) - { - // Make sure we keep the DTX/VAD setting if possible - bool oldEnableDTX = _dtxEnabled; - bool oldEnableVAD = _vadEnabled; - ACMVADMode oldMode = _vadMode; - if (replaceInternalDTX) - { - // Disable internal DTX before enabling external DTX - DisableDTX(); - } - else - { - // Disable external DTX before enabling internal - ACMGenericCodec::DisableDTX(); - } - _hasInternalDTX = !replaceInternalDTX; - WebRtc_Word16 status = SetVADSafe(oldEnableDTX, oldEnableVAD, oldMode); - // Check if VAD status has changed from inactive to active, or if error was - // reported - if (status == 1) { - _vadEnabled = true; - return status; - } else if (status < 0) { - _hasInternalDTX = replaceInternalDTX; - return -1; - } - } - return 0; -} - - -WebRtc_Word32 -ACMG729::IsInternalDTXReplacedSafe( - bool* internalDTXReplaced) -{ - // Get status of wether DTX is replaced or not - *internalDTXReplaced = !_hasInternalDTX; - return 0; -} - - -WebRtc_Word16 -ACMG729::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - // This function is not used. G.729 decoder is called from inside NetEQ - return 0; -} - - -WebRtc_Word16 -ACMG729::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - // Init G.729 encoder - return WebRtcG729_EncoderInit(_encoderInstPtr, - ((codecParams->enableDTX)? 1:0)); -} - - -WebRtc_Word16 -ACMG729::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // Init G.729 decoder - return WebRtcG729_DecoderInit(_decoderInstPtr); -} - - -WebRtc_Word32 -ACMG729::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // Todo: - // log error - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G729_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderG729, codecInst.pltype, - _decoderInstPtr, 8000); - SET_G729_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMG729::CreateInstance(void) -{ - // Function not used - return NULL; -} - - -WebRtc_Word16 -ACMG729::InternalCreateEncoder() -{ - // Create encoder memory - return WebRtcG729_CreateEnc(&_encoderInstPtr); -} - - -void -ACMG729::DestructEncoderSafe() -{ - // Free encoder memory - _encoderExist = false; - _encoderInitialized = false; - if(_encoderInstPtr != NULL) - { - WebRtcG729_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } -} - - -WebRtc_Word16 -ACMG729::InternalCreateDecoder() -{ - // Create decoder memory - return WebRtcG729_CreateDec(&_decoderInstPtr); -} - - -void -ACMG729::DestructDecoderSafe() -{ - // Free decoder memory - _decoderExist = false; - _decoderInitialized = false; - if(_decoderInstPtr != NULL) - { - WebRtcG729_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } -} - - -void -ACMG729::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcG729_FreeEnc((G729_encinst_t_*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMG729::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - // Remove codec from the NetEQ database - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderG729); -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_g729.h b/modules/audio_coding/main/source/acm_g729.h deleted file mode 100644 index 3c00f63cc..000000000 --- a/modules/audio_coding/main/source/acm_g729.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_G729_H -#define ACM_G729_H - -#include "acm_generic_codec.h" - -// forward declaration -struct G729_encinst_t_; -struct G729_decinst_t_; - -namespace webrtc -{ - -class ACMG729 : public ACMGenericCodec -{ -public: - ACMG729(WebRtc_Word16 codecID); - ~ACMG729(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 EnableDTX(); - - WebRtc_Word16 DisableDTX(); - - WebRtc_Word32 ReplaceInternalDTXSafe( - const bool replaceInternalDTX); - - WebRtc_Word32 IsInternalDTXReplacedSafe( - bool* internalDTXReplaced); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - G729_encinst_t_* _encoderInstPtr; - G729_decinst_t_* _decoderInstPtr; - -}; - -} // namespace webrtc - -#endif //ACM_G729_H - diff --git a/modules/audio_coding/main/source/acm_g7291.cc b/modules/audio_coding/main/source/acm_g7291.cc deleted file mode 100644 index 3c3d5195b..000000000 --- a/modules/audio_coding/main/source/acm_g7291.cc +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_g7291.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_G729_1 - // NOTE! G.729.1 is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used G.729.1 API file. - // #include "g7291_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_G729_1 - -ACMG729_1::ACMG729_1( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMG729_1::~ACMG729_1() -{ - return; -} - - -WebRtc_Word16 -ACMG729_1::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729_1::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729_1::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMG729_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMG729_1::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMG729_1::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG729_1::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMG729_1::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMG729_1::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMG729_1::DestructDecoderSafe() -{ - return; -} - - -void -ACMG729_1::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMG729_1::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -WebRtc_Word16 -ACMG729_1::SetBitRateSafe( - const WebRtc_Word32 /*rate*/ ) -{ - return -1; -} - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real GSM AMR wrapper - -struct G729_1_inst_t_; -extern WebRtc_Word16 WebRtcG7291_Create(G729_1_inst_t_** inst); -extern WebRtc_Word16 WebRtcG7291_Free(G729_1_inst_t_* inst); -extern WebRtc_Word16 WebRtcG7291_Encode(G729_1_inst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16* output, - WebRtc_Word16 myRate, - WebRtc_Word16 nrFrames); -extern WebRtc_Word16 WebRtcG7291_EncoderInit(G729_1_inst_t_* encInst, - WebRtc_Word16 myRate, - WebRtc_Word16 flag8kHz, - WebRtc_Word16 flagG729mode); -extern WebRtc_Word16 WebRtcG7291_Decode(G729_1_inst_t_* decInst); -extern WebRtc_Word16 WebRtcG7291_DecodeBwe(G729_1_inst_t_* decInst, WebRtc_Word16* input); -extern WebRtc_Word16 WebRtcG7291_DecodePlc(G729_1_inst_t_* decInst); -extern WebRtc_Word16 WebRtcG7291_DecoderInit(G729_1_inst_t_* decInst); - -ACMG729_1::ACMG729_1( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL) -{ - _codecID = codecID; - // Our current G729.1 does not support Annex C - // which is DTX. - _hasInternalDTX = false; - - // Default rate - _myRate = 32000; - - _flag8kHz = 0; - _flagG729mode = 0; - return; -} - -ACMG729_1::~ACMG729_1() -{ - if(_encoderInstPtr != NULL) - { - WebRtcG7291_Free(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcG7291_Free(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMG729_1::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - - // Initialize before entering the loop - WebRtc_Word16 noEncodedSamples = 0; - WebRtc_Word16 tmpLenByte = 0; - *bitStreamLenByte = 0; - - WebRtc_Word16 byteLengthFrame = 0; - - // Derive number of 20ms frames per encoded packet. - // [1,2,3] <=> [20,40,60]ms <=> [320,640,960] samples - WebRtc_Word16 n20msFrames = (_frameLenSmpl / 320); - // Byte length for the frame. +1 is for rate information. - byteLengthFrame = _myRate/(8*50) * n20msFrames + (1 - _flagG729mode); - - // The following might be revised if we have G729.1 Annex C (support for DTX); - do - { - *bitStreamLenByte = WebRtcG7291_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], - (WebRtc_Word16*)bitStream, _myRate, n20msFrames); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += 160; - - // sanity check - if(*bitStreamLenByte < 0) - { - // error has happened - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalEncode: Encode error for G729_1"); - *bitStreamLenByte = 0; - return -1; - } - - noEncodedSamples += 160; - } while(*bitStreamLenByte == 0); - - - // This criteria will change if we have Annex C. - if(*bitStreamLenByte != byteLengthFrame) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalEncode: Encode error for G729_1"); - *bitStreamLenByte = 0; - return -1; - } - - - if(noEncodedSamples != _frameLenSmpl) - { - *bitStreamLenByte = 0; - return -1; - } - - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMG729_1::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMG729_1::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - //set the bit rate and initialize - _myRate = codecParams->codecInstant.rate; - return SetBitRateSafe( (WebRtc_UWord32)_myRate); -} - - -WebRtc_Word16 -ACMG729_1::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - if (WebRtcG7291_DecoderInit(_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: init decoder failed for G729_1"); - return -1; - } - return 0; -} - - -WebRtc_Word32 -ACMG729_1::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodeDef: Decoder uninitialized for G729_1"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G729_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderG729_1, codecInst.pltype, - _decoderInstPtr, 16000); - SET_G729_1_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMG729_1::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMG729_1::InternalCreateEncoder() -{ - if (WebRtcG7291_Create(&_encoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: create encoder failed for G729_1"); - return -1; - } - return 0; -} - - -void -ACMG729_1::DestructEncoderSafe() -{ - _encoderExist = false; - _encoderInitialized = false; - if(_encoderInstPtr != NULL) - { - WebRtcG7291_Free(_encoderInstPtr); - _encoderInstPtr = NULL; - } -} - - -WebRtc_Word16 -ACMG729_1::InternalCreateDecoder() -{ - if (WebRtcG7291_Create(&_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateDecoder: create decoder failed for G729_1"); - return -1; - } - return 0; -} - - -void -ACMG729_1::DestructDecoderSafe() -{ - _decoderExist = false; - _decoderInitialized = false; - if(_decoderInstPtr != NULL) - { - WebRtcG7291_Free(_decoderInstPtr); - _decoderInstPtr = NULL; - } -} - - -void -ACMG729_1::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - //WebRtcG7291_Free((G729_1_inst_t*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMG729_1::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec: given payload-type does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderG729_1); -} - -WebRtc_Word16 -ACMG729_1::SetBitRateSafe( - const WebRtc_Word32 rate) -{ - //allowed rates: { 8000, 12000, 14000, 16000, 18000, 20000, - // 22000, 24000, 26000, 28000, 30000, 32000}; - switch(rate) - { - case 8000: - { - _myRate = 8000; - break; - } - case 12000: - { - _myRate = 12000; - break; - } - case 14000: - { - _myRate = 14000; - break; - } - case 16000: - { - _myRate = 16000; - break; - } - case 18000: - { - _myRate = 18000; - break; - } - case 20000: - { - _myRate = 20000; - break; - } - case 22000: - { - _myRate = 22000; - break; - } - case 24000: - { - _myRate = 24000; - break; - } - case 26000: - { - _myRate = 26000; - break; - } - case 28000: - { - _myRate = 28000; - break; - } - case 30000: - { - _myRate = 30000; - break; - } - case 32000: - { - _myRate = 32000; - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetBitRateSafe: Invalid rate G729_1"); - return -1; - break; - } - } - - // Re-init with new rate - if (WebRtcG7291_EncoderInit(_encoderInstPtr, _myRate, _flag8kHz, _flagG729mode) >= 0) - { - _encoderParams.codecInstant.rate = _myRate; - return 0; - } - else - { - return -1; - } -} - - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_g7291.h b/modules/audio_coding/main/source/acm_g7291.h deleted file mode 100644 index 0454b01c2..000000000 --- a/modules/audio_coding/main/source/acm_g7291.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_G729_1_H -#define ACM_G729_1_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -// forward declaration -struct G729_1_inst_t_; -struct G729_1_inst_t_; - -class ACMG729_1: public ACMGenericCodec -{ -public: - ACMG729_1(WebRtc_Word16 codecID); - ~ACMG729_1(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - //WebRtc_Word16 EnableDTX(); - // - //WebRtc_Word16 DisableDTX(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - G729_1_inst_t_* _encoderInstPtr; - G729_1_inst_t_* _decoderInstPtr; - - WebRtc_UWord16 _myRate; - WebRtc_Word16 _flag8kHz; - WebRtc_Word16 _flagG729mode; - -}; - -} // namespace webrtc - -#endif // ACM_G729_1_H - diff --git a/modules/audio_coding/main/source/acm_generic_codec.cc b/modules/audio_coding/main/source/acm_generic_codec.cc deleted file mode 100644 index ce0c93c4c..000000000 --- a/modules/audio_coding/main/source/acm_generic_codec.cc +++ /dev/null @@ -1,1551 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_generic_codec.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_vad.h" -#include "webrtc_cng.h" - -namespace webrtc -{ - -// Enum for CNG -enum -{ - kMaxPLCParamsCNG = WEBRTC_CNG_MAX_LPC_ORDER, - kNewCNGNumPLCParams = 8 -}; - -#define ACM_SID_INTERVAL_MSEC 100 - -// We set some of the variables to invalid values as a check point -// if a proper initialization has happened. Another approach is -// to initialize to a default codec that we are sure is always included. -ACMGenericCodec::ACMGenericCodec(): -_inAudioIxWrite(0), -_inAudioIxRead(0), -_inTimestampIxWrite(0), -_inAudio(NULL), -_inTimestamp(NULL), -_frameLenSmpl(-1), // invalid value -_noChannels(1), -_codecID(-1), // invalid value -_noMissedSamples(0), -_encoderExist(false), -_decoderExist(false), -_encoderInitialized(false), -_decoderInitialized(false), -_registeredInNetEq(false), -_hasInternalDTX(false), -_ptrVADInst(NULL), -_vadEnabled(false), -_vadMode(VADNormal), -_dtxEnabled(false), -_ptrDTXInst(NULL), -_numLPCParams(kNewCNGNumPLCParams), -_sentCNPrevious(false), -_isMaster(true), -_netEqDecodeLock(NULL), -_codecWrapperLock(*RWLockWrapper::CreateRWLock()), -_lastEncodedTimestamp(0), -_lastTimestamp(0), -_isAudioBuffFresh(true), -_uniqueID(0) -{ - _lastTimestamp = 0xD87F3F9F; - //NullifyCodecInstance(); -} -ACMGenericCodec::~ACMGenericCodec() -{ - // Check all the members which are pointers and - // if they are not NULL delete/free them. - - if(_ptrVADInst != NULL) - { - WebRtcVad_Free(_ptrVADInst); - _ptrVADInst = NULL; - } - - if (_inAudio != NULL) - { - delete [] _inAudio; - _inAudio = NULL; - } - - if (_inTimestamp != NULL) - { - delete [] _inTimestamp; - _inTimestamp = NULL; - } - if(_ptrDTXInst != NULL) - { - WebRtcCng_FreeEnc(_ptrDTXInst); - _ptrDTXInst = NULL; - } - delete &_codecWrapperLock; -} - -WebRtc_Word32 -ACMGenericCodec::Add10MsData( - const WebRtc_UWord32 timestamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 lengthSmpl, - const WebRtc_UWord8 audioChannel) -{ - WriteLockScoped wl(_codecWrapperLock); - return Add10MsDataSafe(timestamp, data, lengthSmpl, audioChannel); -} - -WebRtc_Word32 -ACMGenericCodec::Add10MsDataSafe( - const WebRtc_UWord32 timestamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 lengthSmpl, - const WebRtc_UWord8 audioChannel) -{ - // The codec expects to get data in correct sampling rate. - // get the sampling frequency of the codec - WebRtc_UWord16 plFreqHz; - - if(EncoderSampFreq(plFreqHz) < 0) - { - // _codecID is not correct, perhaps the codec is not initialized yet. - return -1; - } - - // Sanity check, if the length of the input corresponds to 10 ms. - if((plFreqHz / 100) != lengthSmpl) - { - // This is not 10 ms of audio, given the sampling frequency of the - // codec - return -1; - } - if(_lastTimestamp == timestamp) - { - // Same timestamp as the last time, overwrite. - if((_inAudioIxWrite >= lengthSmpl) && (_inTimestampIxWrite > 0)) - { - _inAudioIxWrite -= lengthSmpl; - _inTimestampIxWrite--; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, - "Adding 10ms with previous timestamp, \ -overwriting the previous 10ms"); - } - else - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, - "Adding 10ms with previous timestamp, this will sound bad"); - } - } - - _lastTimestamp = timestamp; - - if ((_inAudioIxWrite + lengthSmpl*audioChannel) > AUDIO_BUFFER_SIZE_W16) - { - // Get the number of samples to be overwritten - WebRtc_Word16 missedSamples = _inAudioIxWrite + lengthSmpl*audioChannel - - AUDIO_BUFFER_SIZE_W16; - - // Move the data (overwite the old data) - memmove(_inAudio, _inAudio + missedSamples, - (AUDIO_BUFFER_SIZE_W16 - lengthSmpl*audioChannel)*sizeof(WebRtc_Word16)); - // Copy the new data - memcpy(_inAudio + (AUDIO_BUFFER_SIZE_W16 - lengthSmpl*audioChannel), data, - lengthSmpl*audioChannel * sizeof(WebRtc_Word16)); - - // Get the number of 10 ms blocks which are overwritten - WebRtc_Word16 missed10MsecBlocks = - (WebRtc_Word16)((missedSamples/audioChannel * 100) / plFreqHz); - - // Move the timestamps - memmove(_inTimestamp, _inTimestamp + missed10MsecBlocks, - (_inTimestampIxWrite - missed10MsecBlocks) * sizeof(WebRtc_UWord32)); - _inTimestampIxWrite -= missed10MsecBlocks; - _inTimestamp[_inTimestampIxWrite] = timestamp; - _inTimestampIxWrite++; - - // Buffer is full - _inAudioIxWrite = AUDIO_BUFFER_SIZE_W16; - IncreaseNoMissedSamples(missedSamples); - _isAudioBuffFresh = false; - return -missedSamples; - } - memcpy(_inAudio + _inAudioIxWrite, data, lengthSmpl*audioChannel * sizeof(WebRtc_Word16)); - _inAudioIxWrite += lengthSmpl*audioChannel; - - assert(_inTimestampIxWrite < TIMESTAMP_BUFFER_SIZE_W32); - assert(_inTimestampIxWrite >= 0); - - _inTimestamp[_inTimestampIxWrite] = timestamp; - _inTimestampIxWrite++; - _isAudioBuffFresh = false; - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::Encode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_UWord32* timeStamp, - WebRtcACMEncodingType* encodingType) -{ - WriteLockScoped lockCodec(_codecWrapperLock); - ReadLockScoped lockNetEq(*_netEqDecodeLock); - return EncodeSafe(bitStream, bitStreamLenByte, - timeStamp, encodingType); -} - - -WebRtc_Word16 -ACMGenericCodec::EncodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_UWord32* timeStamp, - WebRtcACMEncodingType* encodingType) -{ - // Do we have enough data to encode? - // we wait until we have a full frame to encode. - if(_inAudioIxWrite < _frameLenSmpl*_noChannels) - { - // There is not enough audio - *timeStamp = 0; - *bitStreamLenByte = 0; - // Doesn't really matter what this parameter set to - *encodingType = kNoEncoding; - return 0; - } - - // Not all codecs accept the whole frame to be pushed into - // encoder at once. "myBasicCodingBlockSmpl" is - const WebRtc_Word16 myBasicCodingBlockSmpl = - ACMCodecDB::_basicCodingBlockSmpl[_codecID]; - if((myBasicCodingBlockSmpl < 0) || - (!_encoderInitialized) || - (!_encoderExist)) - { - // This should not happen - *timeStamp = 0; - *bitStreamLenByte = 0; - *encodingType = kNoEncoding; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EncodeSafe: error, basic coding sample block is negative"); - return -1; - } - - // This makes the internal encoder read from the begining of the buffer - _inAudioIxRead = 0; - *timeStamp = _inTimestamp[0]; - - // Process the audio through VAD the function doesn't set _vadLabels. - // If VAD is disabled all labels are set to ONE (active) - WebRtc_Word16 status = 0; - WebRtc_Word16 dtxProcessedSamples = 0; - - status = ProcessFrameVADDTX(bitStream, bitStreamLenByte, - &dtxProcessedSamples); - - if(status < 0) - { - *timeStamp = 0; - *bitStreamLenByte = 0; - *encodingType = kNoEncoding; - } - else - { - if(dtxProcessedSamples > 0) - { - // Dtx have processed some samples may or may not a bit-stream - // is generated we should not do any encoding (normally there - // will be not enough data) - - // Setting the following makes that the move of audio data - // and timestamps happen correctly - _inAudioIxRead = dtxProcessedSamples; - // This will let the owner of ACMGenericCodec to know that the - // generated bit-stream is DTX to use correct payload type - WebRtc_UWord16 sampFreqHz; - EncoderSampFreq(sampFreqHz); - if (sampFreqHz == 8000) { - *encodingType = kPassiveDTXNB; - } else if (sampFreqHz == 16000) { - *encodingType = kPassiveDTXWB; - } else if (sampFreqHz == 32000) { - *encodingType = kPassiveDTXSWB; - } else { - status = -1; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EncodeSafe: Wrong sampling frequency for DTX."); - } - - // Transport empty frame if we have an empty bitstream - if ((*bitStreamLenByte == 0) - && (_sentCNPrevious || ((_inAudioIxWrite - _inAudioIxRead) <= 0)) - ) - { - // Makes sure we transmit an empty frame - *bitStreamLenByte = 1; - *encodingType = kNoEncoding; - } - _sentCNPrevious = true; - } - else - { - _sentCNPrevious = false; - // This will let the caller of the method to know if the frame is - // Active or non-Active The caller of the method knows that the - // stream is encoded by codec and can use the info for callbacks, - // if any registered. - if(myBasicCodingBlockSmpl == 0) - { - // This codec can handle all allowed frame sizes as basic - // coding block - status = InternalEncode(bitStream, bitStreamLenByte); - - if(status < 0) - { - // TODO: - // Maybe reseting the encoder to be fresh for the next - // frame - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EncodeSafe: error in internalEncode"); - *bitStreamLenByte = 0; - *encodingType = kNoEncoding; - } - } - else - { - // A basic-coding-block for this codec is defined so we loop - // over the audio with the steps of the basic-coding-block. - // It is not necessary that in each itteration - WebRtc_Word16 tmpBitStreamLenByte; - - // Reset the variables which will be increamented in the loop - *bitStreamLenByte = 0; - bool done = false; - while(!done) - { - status = InternalEncode(&bitStream[*bitStreamLenByte], - &tmpBitStreamLenByte); - *bitStreamLenByte += tmpBitStreamLenByte; - - // Guard Against errors and too large payloads - if((status < 0) || - (*bitStreamLenByte > MAX_PAYLOAD_SIZE_BYTE)) - { - // Error has happened if we are in the middle of a full - // frame we have to exit. Before exiting, whatever bits - // are in the buffer are probably corruptred. Anyways - // we ignore them. - *bitStreamLenByte = 0; - *encodingType = kNoEncoding; - // We might have come here because of the second - // condition. - status = -1; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, - _uniqueID, "EncodeSafe: error in InternalEncode"); - // break from the loop - break; - } - - done = _inAudioIxRead >= _frameLenSmpl; - } - } - if(status >= 0) - { - *encodingType = (_vadLabel[0] == 1)? - kActiveNormalEncoded:kPassiveNormalEncoded; - // Transport empty frame if we have an empty bitsteram - if ((*bitStreamLenByte == 0) && ((_inAudioIxWrite - _inAudioIxRead) <= 0)) - { - // Makes sure we transmit an empty frame - *bitStreamLenByte = 1; - *encodingType = kNoEncoding; - } - } - } - } - - // Move the timestampe buffer according to the number of 10 ms blocks - // which are read. - WebRtc_UWord16 sampFreqHz; - EncoderSampFreq(sampFreqHz); - - WebRtc_Word16 num10MsecBlocks = - (WebRtc_Word16)((_inAudioIxRead/_noChannels * 100) / sampFreqHz); - if(_inTimestampIxWrite > num10MsecBlocks) - { - memmove(_inTimestamp, _inTimestamp + num10MsecBlocks, - (_inTimestampIxWrite - num10MsecBlocks) * sizeof(WebRtc_Word32)); - } - _inTimestampIxWrite -= num10MsecBlocks; - - // We have to move the audio that is not encoded to the beginning - // of the buffer and accordingly adjust the read and write indices. - if(_inAudioIxRead < _inAudioIxWrite) - { - memmove(_inAudio, &_inAudio[_inAudioIxRead], - (_inAudioIxWrite - _inAudioIxRead)*sizeof(WebRtc_Word16)); - } - - _inAudioIxWrite -= _inAudioIxRead; - - _inAudioIxRead = 0; - _lastEncodedTimestamp = *timeStamp; - return (status < 0) ? (-1):(*bitStreamLenByte); -} - -WebRtc_Word16 -ACMGenericCodec::Decode( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType) -{ - WriteLockScoped wl(_codecWrapperLock); - return DecodeSafe(bitStream, bitStreamLenByte, audio, - audioSamples, speechType); -} - -bool -ACMGenericCodec::EncoderInitialized() -{ - ReadLockScoped rl(_codecWrapperLock); - return _encoderInitialized; -} - -bool -ACMGenericCodec::DecoderInitialized() -{ - ReadLockScoped rl(_codecWrapperLock); - return _decoderInitialized; -} - - -WebRtc_Word32 -ACMGenericCodec::RegisterInNetEq( - ACMNetEQ* netEq, - const CodecInst& codecInst) -{ - WebRtcNetEQ_CodecDef codecDef; - WriteLockScoped wl(_codecWrapperLock); - - if(CodecDef(codecDef, codecInst) < 0) - { - // Failed to register - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "RegisterInNetEq: error, failed to register"); - _registeredInNetEq = false; - return -1; - } - else - { - if(netEq->AddCodec(&codecDef, _isMaster) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "RegisterInNetEq: error, failed to add codec"); - _registeredInNetEq = false; - return -1; - } - // Registered - _registeredInNetEq = true; - return 0; - } -} - -WebRtc_Word16 -ACMGenericCodec::EncoderParams( - WebRtcACMCodecParams* encParams) -{ - ReadLockScoped rl(_codecWrapperLock); - return EncoderParamsSafe(encParams); -} - -WebRtc_Word16 -ACMGenericCodec::EncoderParamsSafe( - WebRtcACMCodecParams* encParams) -{ - // Codec parameters are valid only if the encoder is initialized - if(_encoderInitialized) - { - WebRtc_Word32 currentRate; - memcpy(encParams, &_encoderParams, sizeof(WebRtcACMCodecParams)); - currentRate = encParams->codecInstant.rate; - CurrentRate(currentRate); - encParams->codecInstant.rate = currentRate; - return 0; - } - else - { - encParams->codecInstant.plname[0] = '\0'; - encParams->codecInstant.pltype = -1; - encParams->codecInstant.pacsize = 0; - encParams->codecInstant.rate = 0; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EncoderParamsSafe: error, encoder not initialized"); - return -1; - } -} - -bool -ACMGenericCodec::DecoderParams( - WebRtcACMCodecParams* decParams, - const WebRtc_UWord8 payloadType) -{ - ReadLockScoped rl(_codecWrapperLock); - return DecoderParamsSafe(decParams, payloadType); -} - -bool -ACMGenericCodec::DecoderParamsSafe( - WebRtcACMCodecParams* decParams, - const WebRtc_UWord8 payloadType) -{ - // Decoder parameters are valid only if decoder is initialized - if(_decoderInitialized) - { - if(payloadType == _decoderParams.codecInstant.pltype) - { - memcpy(decParams, &_decoderParams, sizeof(WebRtcACMCodecParams)); - return true; - } - } - - decParams->codecInstant.plname[0] = '\0'; - decParams->codecInstant.pltype = -1; - decParams->codecInstant.pacsize = 0; - decParams->codecInstant.rate = 0; - return false; -} - -WebRtc_Word16 -ACMGenericCodec::ResetEncoder() -{ - WriteLockScoped lockCodec(_codecWrapperLock); - ReadLockScoped lockNetEq(*_netEqDecodeLock); - return ResetEncoderSafe(); -} - -WebRtc_Word16 -ACMGenericCodec::ResetEncoderSafe() -{ - if(!_encoderExist || !_encoderInitialized) - { - // We don't reset if doesn't exists or not initialized yet - return 0; - } - - _inAudioIxWrite = 0; - _inAudioIxRead = 0; - _inTimestampIxWrite = 0; - _noMissedSamples = 0; - _isAudioBuffFresh = true; - memset(_inAudio, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); - memset(_inTimestamp, 0, TIMESTAMP_BUFFER_SIZE_W32 * sizeof(WebRtc_Word32)); - - // Store DTX/VAD params - bool enableVAD = _vadEnabled; - bool enableDTX = _dtxEnabled; - ACMVADMode mode = _vadMode; - - // Reset the encoder - if(InternalResetEncoder() < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "ResetEncoderSafe: error in reset encoder"); - return -1; - } - - // Disable DTX & VAD this deletes the states - // we like to have fresh start - DisableDTX(); - DisableVAD(); - - // Set DTX/VAD - return SetVADSafe(enableDTX, enableVAD, mode); -} - -WebRtc_Word16 -ACMGenericCodec::InternalResetEncoder() -{ - // For most of the codecs it is sufficient to - // call their internal initialization. - // There are some exceptions. - // ---- - // For iSAC we don't want to lose BWE history, - // so for iSAC we have to over-write this function. - // ---- - return InternalInitEncoder(&_encoderParams); -} - -WebRtc_Word16 -ACMGenericCodec::InitEncoder( - WebRtcACMCodecParams* codecParams, - bool forceInitialization) -{ - WriteLockScoped lockCodec(_codecWrapperLock); - ReadLockScoped lockNetEq(*_netEqDecodeLock); - return InitEncoderSafe(codecParams, forceInitialization); -} - -WebRtc_Word16 -ACMGenericCodec::InitEncoderSafe( - WebRtcACMCodecParams* codecParams, - bool forceInitialization) -{ - // Check if we got a valid set of parameters - WebRtc_Word16 mirrorID; - WebRtc_Word16 codecNumber = - ACMCodecDB::CodecNumber(&(codecParams->codecInstant), mirrorID); - - if(codecNumber < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitEncoderSafe: error, codec number negative"); - return -1; - } - // Check if the parameters are for this codec - if((_codecID >= 0) && (_codecID != codecNumber) && (_codecID != mirrorID)) - { - // The current codec is not the same as the one given by codecParams - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitEncoderSafe: current codec is not the same as the one given by codecParams"); - return -1; - } - - if(!CanChangeEncodingParam(codecParams->codecInstant)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitEncoderSafe: cannot change encoding parameters"); - return -1; - } - - if(_encoderInitialized && !forceInitialization) - { - // The encoder is already initialized - return 0; - } - WebRtc_Word16 status; - if(!_encoderExist) - { - _encoderInitialized = false; - status = CreateEncoder(); - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitEncoderSafe: cannot create encoder"); - return -1; - } - else - { - _encoderExist = true; - } - } - _frameLenSmpl = (codecParams->codecInstant).pacsize; - status = InternalInitEncoder(codecParams); - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitEncoderSafe: error in init encoder"); - _encoderInitialized = false; - return -1; - } - else - { - memcpy(&_encoderParams, codecParams, sizeof(WebRtcACMCodecParams)); - _encoderInitialized = true; - if(_inAudio == NULL) - { - _inAudio = new WebRtc_Word16[AUDIO_BUFFER_SIZE_W16]; - if(_inAudio == NULL) - { - return -1; - } - memset(_inAudio, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); - } - if(_inTimestamp == NULL) - { - _inTimestamp = new WebRtc_UWord32[TIMESTAMP_BUFFER_SIZE_W32]; - if(_inTimestamp == NULL) - { - return -1; - } - memset(_inTimestamp, 0, sizeof(WebRtc_UWord32) * - TIMESTAMP_BUFFER_SIZE_W32); - } - _isAudioBuffFresh = true; - } - status = SetVADSafe(codecParams->enableDTX, codecParams->enableVAD, - codecParams->vadMode); - - _noChannels = codecParams->codecInstant.channels; - - return status; -} - -bool -ACMGenericCodec::CanChangeEncodingParam( - CodecInst& /*codecInst*/) -{ - return true; -} - -WebRtc_Word16 -ACMGenericCodec::InitDecoder( - WebRtcACMCodecParams* codecParams, - bool forceInitialization) -{ - WriteLockScoped lockCodc(_codecWrapperLock); - WriteLockScoped lockNetEq(*_netEqDecodeLock); - return InitDecoderSafe(codecParams, forceInitialization); -} - -WebRtc_Word16 -ACMGenericCodec::InitDecoderSafe( - WebRtcACMCodecParams* codecParams, - bool forceInitialization) -{ - WebRtc_Word16 mirrorID; - // Check if we got a valid set of parameters - WebRtc_Word16 codecNumber = - ACMCodecDB::ReceiverCodecNumber(codecParams->codecInstant, mirrorID); - - if(codecNumber < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitDecoderSafe: error, invalid codec number"); - return -1; - } - // Check if the parameters are for this codec - if((_codecID >= 0) && (_codecID != codecNumber) && (_codecID != mirrorID)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitDecoderSafe: current codec is not the same as the one given " - "by codecParams"); - // The current codec is not the same as the one given by codecParams - return -1; - } - - - if(_decoderInitialized && !forceInitialization) - { - // The encoder is already initialized - return 0; - } - - WebRtc_Word16 status; - if(!_decoderExist) - { - _decoderInitialized = false; - status = CreateDecoder(); - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitDecoderSafe: cannot create decoder"); - return -1; - } - else - { - _decoderExist = true; - } - } - - status = InternalInitDecoder(codecParams); - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InitDecoderSafe: cannot init decoder"); - _decoderInitialized = false; - return -1; - } - else - { - // Store the parameters - SaveDecoderParamSafe(codecParams); - _decoderInitialized = true; - } - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::ResetDecoder(WebRtc_Word16 payloadType) -{ - WriteLockScoped lockCodec(_codecWrapperLock); - WriteLockScoped lockNetEq(*_netEqDecodeLock); - return ResetDecoderSafe(payloadType); -} - -WebRtc_Word16 -ACMGenericCodec::ResetDecoderSafe(WebRtc_Word16 payloadType) -{ - WebRtcACMCodecParams decoderParams; - if(!_decoderExist || !_decoderInitialized) - { - return 0; - } - // Initialization of the decoder should work for all - // the codec. If there is a codec that has to keep - // some states then we need to define a virtual and - // overwrite in that codec - DecoderParamsSafe(&decoderParams, (WebRtc_UWord8) payloadType); - return InternalInitDecoder(&decoderParams); -} - -void -ACMGenericCodec::ResetNoMissedSamples() -{ - WriteLockScoped cs(_codecWrapperLock); - _noMissedSamples = 0; -} - -void -ACMGenericCodec::IncreaseNoMissedSamples( - const WebRtc_Word16 noSamples) -{ - _noMissedSamples += noSamples; -} - -// Get the number of missed samples, this can be public -WebRtc_UWord32 -ACMGenericCodec::NoMissedSamples() const -{ - ReadLockScoped cs(_codecWrapperLock); - return _noMissedSamples; -} -void -ACMGenericCodec::DestructEncoder() -{ - WriteLockScoped wl(_codecWrapperLock); - - // Disable VAD and delete the instance - if(_ptrVADInst != NULL) - { - WebRtcVad_Free(_ptrVADInst); - _ptrVADInst = NULL; - } - _vadEnabled = false; - _vadMode = VADNormal; - - //Disable DTX and delete the instance - _dtxEnabled = false; - if(_ptrDTXInst != NULL) - { - WebRtcCng_FreeEnc(_ptrDTXInst); - _ptrDTXInst = NULL; - } - _numLPCParams = kNewCNGNumPLCParams; - - DestructEncoderSafe(); -} - -void -ACMGenericCodec::DestructDecoder() -{ - WriteLockScoped wl(_codecWrapperLock); - _decoderParams.codecInstant.pltype = -1; - DestructDecoderSafe(); -} - -WebRtc_Word16 -ACMGenericCodec::SetBitRate( - const WebRtc_Word32 bitRateBPS) -{ - WriteLockScoped wl(_codecWrapperLock); - return SetBitRateSafe(bitRateBPS); -} - -WebRtc_Word16 -ACMGenericCodec::SetBitRateSafe( - const WebRtc_Word32 bitRateBPS) -{ - // If the codec can change the bit-rate this function - // should be overwritten, otherewise the only acceptable - // value is the one that is in database. - CodecInst codecParams; - if(ACMCodecDB::Codec(_codecID, &codecParams) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetBitRateSafe: error in ACMCodecDB::Codec"); - return -1; - } - if(codecParams.rate != bitRateBPS) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetBitRateSafe: rate value is not acceptable"); - return -1; - } - else - { - return 0; - } -} - -WebRtc_Word32 -ACMGenericCodec::GetEstimatedBandwidth() -{ - WriteLockScoped wl(_codecWrapperLock); - return GetEstimatedBandwidthSafe(); -} - -WebRtc_Word32 -ACMGenericCodec::GetEstimatedBandwidthSafe() -{ - // All codecs but iSAC will return -1 - return -1; -} - -WebRtc_Word32 -ACMGenericCodec::SetEstimatedBandwidth( - WebRtc_Word32 estimatedBandwidth) -{ - WriteLockScoped wl(_codecWrapperLock); - return SetEstimatedBandwidthSafe(estimatedBandwidth); -} - -WebRtc_Word32 -ACMGenericCodec::SetEstimatedBandwidthSafe( - WebRtc_Word32 /*estimatedBandwidth*/) -{ - // All codecs but iSAC will return -1 - return -1; -} - -WebRtc_Word32 -ACMGenericCodec::GetRedPayload( - WebRtc_UWord8* redPayload, - WebRtc_Word16* payloadBytes) -{ - WriteLockScoped wl(_codecWrapperLock); - return GetRedPayloadSafe(redPayload, payloadBytes); -} - -WebRtc_Word32 -ACMGenericCodec::GetRedPayloadSafe( - WebRtc_UWord8* /* redPayload */, - WebRtc_Word16* /* payloadBytes */) -{ - return -1; // Do nothing by default -} - -WebRtc_Word16 -ACMGenericCodec::CreateEncoder() -{ - WebRtc_Word16 status = 0; - if(!_encoderExist) - { - status = InternalCreateEncoder(); - // We just created the codec and obviously it is not initialized - _encoderInitialized = false; - } - - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CreateEncoder: error in internal create encoder"); - _encoderExist = false; - } - else - { - _encoderExist = true; - } - return status; -} - -WebRtc_Word16 -ACMGenericCodec::CreateDecoder() -{ - WebRtc_Word16 status = 0; - if(!_decoderExist) - { - status = InternalCreateDecoder(); - // Decoder just created and obviously it is not initialized - _decoderInitialized = false; - } - - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CreateDecoder: error in internal create decoder"); - _decoderExist = false; - } - else - { - _decoderExist = true; - } - return status; -} - - -void ACMGenericCodec::DestructEncoderInst(void* ptrInst) -{ - if(ptrInst != NULL) - { - WriteLockScoped lockCodec(_codecWrapperLock); - ReadLockScoped lockNetEq(*_netEqDecodeLock); - InternalDestructEncoderInst(ptrInst); - } -} - - -WebRtc_Word16 -ACMGenericCodec::AudioBuffer( - WebRtcACMAudioBuff& audioBuff) -{ - ReadLockScoped cs(_codecWrapperLock); - memcpy(audioBuff.inAudio, _inAudio, - AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); - audioBuff.inAudioIxRead = _inAudioIxRead; - audioBuff.inAudioIxWrite = _inAudioIxWrite; - memcpy(audioBuff.inTimestamp, _inTimestamp, - TIMESTAMP_BUFFER_SIZE_W32*sizeof(WebRtc_UWord32)); - audioBuff.inTimestampIxWrite = _inTimestampIxWrite; - audioBuff.lastTimestamp = _lastTimestamp; - return 0; -} - - -WebRtc_Word16 -ACMGenericCodec::SetAudioBuffer( - WebRtcACMAudioBuff& audioBuff) -{ - WriteLockScoped cs(_codecWrapperLock); - memcpy(_inAudio, audioBuff.inAudio, - AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); - _inAudioIxRead = audioBuff.inAudioIxRead; - _inAudioIxWrite = audioBuff.inAudioIxWrite; - memcpy(_inTimestamp, audioBuff.inTimestamp, - TIMESTAMP_BUFFER_SIZE_W32*sizeof(WebRtc_UWord32)); - _inTimestampIxWrite = audioBuff.inTimestampIxWrite; - _lastTimestamp = audioBuff.lastTimestamp; - _isAudioBuffFresh = false; - return 0; -} - - -WebRtc_UWord32 -ACMGenericCodec::LastEncodedTimestamp() const -{ - ReadLockScoped cs(_codecWrapperLock); - return _lastEncodedTimestamp; -} - - -WebRtc_UWord32 -ACMGenericCodec::EarliestTimestamp() const -{ - ReadLockScoped cs(_codecWrapperLock); - return _inTimestamp[0]; -} - - -WebRtc_Word16 -ACMGenericCodec::SetVAD( - const bool enableDTX, - const bool enableVAD, - const ACMVADMode mode) -{ - WriteLockScoped cs(_codecWrapperLock); - return SetVADSafe(enableDTX, enableVAD, mode); -} - - -WebRtc_Word16 -ACMGenericCodec::SetVADSafe( - const bool enableDTX, - const bool enableVAD, - const ACMVADMode mode) -{ - if(enableDTX) - { - // Make G729 AnnexB a special case - if (!STR_CASE_CMP(_encoderParams.codecInstant.plname, "G729") && !_hasInternalDTX) - { - if (ACMGenericCodec::EnableDTX() < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetVADSafe: error in enable DTX"); - return -1; - } - } - else - { - if(EnableDTX() < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetVADSafe: error in enable DTX"); - return -1; - } - } - - if(_hasInternalDTX) - { - // Codec has internal DTX, practically we don't need WebRtc VAD, - // however, we let the user to turn it on if they need call-backs - // on silence. Store VAD mode for future even if VAD is off. - _vadMode = mode; - return (enableVAD)? EnableVAD(mode):DisableVAD(); - } - else - { - // Codec does not have internal DTX so enabling DTX requires an - // active VAD. 'enableDTX == true' overwrites VAD status. - if(EnableVAD(mode) < 0) - { - // If we cannot create VAD we have to disable DTX - if(!_vadEnabled) - { - DisableDTX(); - } - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetVADSafe: error in enable VAD"); - return -1; - } - - // Return '1', to let the caller know VAD was turned on, even if the - // function was called with VAD='false' - if (enableVAD == false) { - return 1; - } else { - return 0; - } - } - } - else - { - // Make G729 AnnexB a special case - if (!STR_CASE_CMP(_encoderParams.codecInstant.plname, "G729") && !_hasInternalDTX) - { - ACMGenericCodec::DisableDTX(); - } - else - { - DisableDTX(); - } - return (enableVAD)? EnableVAD(mode):DisableVAD(); - } -} - -WebRtc_Word16 -ACMGenericCodec::EnableDTX() -{ - if(_hasInternalDTX) - { - // We should not be here if we have internal DTX - // this function should be overwritten by the derived - // class in this case - return -1; - } - if(!_dtxEnabled) - { - if(WebRtcCng_CreateEnc(&_ptrDTXInst) < 0) - { - _ptrDTXInst = false; - return -1; - } - WebRtc_UWord16 freqHz; - EncoderSampFreq(freqHz); - if(WebRtcCng_InitEnc(_ptrDTXInst, (WebRtc_Word16)freqHz, - ACM_SID_INTERVAL_MSEC, _numLPCParams) < 0) - { - // Couldn't initialize, has to return -1, and free the memory - WebRtcCng_FreeEnc(_ptrDTXInst); - _ptrDTXInst = NULL; - return -1; - } - _dtxEnabled = true; - } - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::DisableDTX() -{ - if(_hasInternalDTX) - { - // We should not be here if we have internal DTX - // this function should be overwritten by the derived - // class in this case - return -1; - } - if(_ptrDTXInst != NULL) - { - WebRtcCng_FreeEnc(_ptrDTXInst); - _ptrDTXInst = NULL; - } - _dtxEnabled = false; - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::EnableVAD( - ACMVADMode mode) -{ - if((mode < VADNormal) || (mode > VADVeryAggr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EnableVAD: error in VAD mode range"); - return -1; - } - - if(!_vadEnabled) - { - if(WebRtcVad_Create(&_ptrVADInst) < 0) - { - _ptrVADInst = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EnableVAD: error in create VAD"); - return -1; - } - if(WebRtcVad_Init(_ptrVADInst) < 0) - { - WebRtcVad_Free(_ptrVADInst); - _ptrVADInst = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EnableVAD: error in init VAD"); - return -1; - } - } - - // Set the vad mode to the given value - if(WebRtcVad_set_mode(_ptrVADInst, mode) < 0) - { - // We failed to set the mode and we have to return -1. If - // we already have a working VAD (_vadEnabled == true) then - // we leave it to work. otherwise, the following will be - // executed. - if(!_vadEnabled) - { - // We just created the instance but cannot set the mode - // we have to free the memomry. - WebRtcVad_Free(_ptrVADInst); - _ptrVADInst = NULL; - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, - "EnableVAD: failed to set the VAD mode"); - return -1; - } - _vadMode = mode; - _vadEnabled = true; - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::DisableVAD() -{ - if(_ptrVADInst != NULL) - { - WebRtcVad_Free(_ptrVADInst); - _ptrVADInst = NULL; - } - _vadEnabled = false; - return 0; -} - -WebRtc_Word32 -ACMGenericCodec::ReplaceInternalDTX( - const bool replaceInternalDTX) -{ - WriteLockScoped cs(_codecWrapperLock); - return ReplaceInternalDTXSafe(replaceInternalDTX); -} - -WebRtc_Word32 -ACMGenericCodec::ReplaceInternalDTXSafe( - const bool /* replaceInternalDTX */) -{ - return -1; -} - -WebRtc_Word32 -ACMGenericCodec::IsInternalDTXReplaced( - bool* internalDTXReplaced) -{ - WriteLockScoped cs(_codecWrapperLock); - return IsInternalDTXReplacedSafe(internalDTXReplaced); -} - -WebRtc_Word32 -ACMGenericCodec::IsInternalDTXReplacedSafe( - bool* internalDTXReplaced) -{ - *internalDTXReplaced = false; - return 0; -} - -WebRtc_Word16 -ACMGenericCodec::ProcessFrameVADDTX( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_Word16* samplesProcessed) -{ - if(!_vadEnabled) - { - // VAD not enabled, set all vadLable[] to 1 (speech detected) - for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) - { - _vadLabel[n] = 1; - } - *samplesProcessed = 0; - return 0; - } - WebRtc_UWord16 freqHz; - EncoderSampFreq(freqHz); - - // Calculate number of samples in 10 ms blocks, and number ms in one frame - WebRtc_Word16 samplesIn10Msec = (WebRtc_Word16)(freqHz / 100); - WebRtc_Word32 frameLenMsec = (((WebRtc_Word32)_frameLenSmpl * 1000) / freqHz); - WebRtc_Word16 status; - WebRtc_Word16 vadFlag = 0; - - // Vector for storing maximum 30 ms of mono audio at 32 kHz - WebRtc_Word16 audio[960]; - - // Calculate number of VAD-blocks to process, and number of samples in each block. - int noSamplesToProcess[2]; - if (frameLenMsec == 40) - { - // 20 ms in each VAD block - noSamplesToProcess[0] = noSamplesToProcess[1] = 2*samplesIn10Msec; - } - else - { - // For 10-30 ms framesizes, second VAD block will be size zero ms, - // for 50 and 60 ms first VAD block will be 30 ms. - noSamplesToProcess[0] = (frameLenMsec > 30)? 3*samplesIn10Msec : _frameLenSmpl; - noSamplesToProcess[1] = _frameLenSmpl-noSamplesToProcess[0]; - } - - int offSet = 0; - int loops = (noSamplesToProcess[1]>0) ? 2 : 1; - for (int i=0; i 0 once per 100 ms - *bitStreamLenByte += bitStreamLen; - } - - - // Check if all samples got processed by the DTX - if(*samplesProcessed != noSamplesToProcess[i]*_noChannels) { - // Set to zero since something went wrong. Shouldn't happen. - *samplesProcessed = 0; - } - } - - if(*samplesProcessed > 0) - { - // The block contains inactive speech, and is processed by DTX. - // Discontinue running VAD. - break; - } - } - - return status; -} - -WebRtc_Word16 -ACMGenericCodec::SamplesLeftToEncode() -{ - ReadLockScoped rl(_codecWrapperLock); - return (_frameLenSmpl <= _inAudioIxWrite)? - 0:(_frameLenSmpl - _inAudioIxWrite); -} - -WebRtc_Word32 -ACMGenericCodec::UnregisterFromNetEq( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - WriteLockScoped wl(_codecWrapperLock); - if(!_registeredInNetEq) - { - return 0; - } - if(UnregisterFromNetEqSafe(netEq, payloadType) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "UnregisterFromNetEq: error, cannot unregister from NetEq"); - _registeredInNetEq = true; - return -1; - } - else - { - _registeredInNetEq = false; - return 0; - } -} - -void -ACMGenericCodec::SetUniqueID( - const WebRtc_UWord32 id) -{ - _uniqueID = id; -} - -bool -ACMGenericCodec::IsAudioBufferFresh() const -{ - ReadLockScoped rl(_codecWrapperLock); - return _isAudioBuffFresh; -} - -// This function is replaced by codec specific functions for some codecs -WebRtc_Word16 -ACMGenericCodec::EncoderSampFreq(WebRtc_UWord16& sampFreqHz) -{ - WebRtc_Word32 f; - f = ACMCodecDB::CodecFreq(_codecID); - if(f < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EncoderSampFreq: codec frequency is negative"); - return -1; - } - else - { - sampFreqHz = (WebRtc_UWord16)f; - return 0; - } -} - - -WebRtc_Word32 -ACMGenericCodec::ConfigISACBandwidthEstimator( - const WebRtc_UWord8 /* initFrameSizeMsec */, - const WebRtc_UWord16 /* initRateBitPerSec */, - const bool /* enforceFrameSize */) -{ - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, - "The send-codec is not iSAC, failed to config iSAC bandwidth estimator."); - return -1; -} - -WebRtc_Word32 -ACMGenericCodec::SetISACMaxRate( - const WebRtc_UWord32 /* maxRateBitPerSec */) -{ - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, - "The send-codec is not iSAC, failed to set iSAC max rate."); - return -1; -} - -WebRtc_Word32 -ACMGenericCodec::SetISACMaxPayloadSize( - const WebRtc_UWord16 /* maxPayloadLenBytes */) -{ - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, - "The send-codec is not iSAC, failed to set iSAC max payload-size."); - return -1; -} - - -void -ACMGenericCodec::SaveDecoderParam( - const WebRtcACMCodecParams* codecParams) -{ - WriteLockScoped wl(_codecWrapperLock); - SaveDecoderParamSafe(codecParams); -} - - -void -ACMGenericCodec::SaveDecoderParamSafe( - const WebRtcACMCodecParams* codecParams) -{ - memcpy(&_decoderParams, codecParams, sizeof(WebRtcACMCodecParams)); -} - -WebRtc_Word16 -ACMGenericCodec::UpdateEncoderSampFreq( - WebRtc_UWord16 /* encoderSampFreqHz */) -{ - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "It is asked for a change in smapling frequency while the \ -current send-codec supports only one sampling rate."); - return -1; -} - - -void -ACMGenericCodec::SetIsMaster( - bool isMaster) -{ - WriteLockScoped wl(_codecWrapperLock); - _isMaster = isMaster; -} - - - -WebRtc_Word16 -ACMGenericCodec::REDPayloadISAC( - const WebRtc_Word32 /* isacRate */, - const WebRtc_Word16 /* isacBwEstimate */, - WebRtc_UWord8* /* payload */, - WebRtc_Word16* /* payloadLenBytes */) -{ - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error: REDPayloadISAC is an iSAC specific function"); - return -1; -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_generic_codec.h b/modules/audio_coding/main/source/acm_generic_codec.h deleted file mode 100644 index 23e16a6a2..000000000 --- a/modules/audio_coding/main/source/acm_generic_codec.h +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_GENERIC_CODEC_H -#define ACM_GENERIC_CODEC_H - -#include "acm_common_defs.h" -#include "audio_coding_module_typedefs.h" -#include "rw_lock_wrapper.h" -#include "trace.h" -#include "webrtc_neteq.h" - -#define MAX_FRAME_SIZE_10MSEC 6 - -// forward declaration -struct WebRtcVadInst; -struct WebRtcCngEncInst; - -namespace webrtc -{ - -// forward declaration -struct CodecInst; -class ACMNetEQ; - -class ACMGenericCodec -{ -public: - /////////////////////////////////////////////////////////////////////////// - // Constructor of the class - // - ACMGenericCodec(); - - - /////////////////////////////////////////////////////////////////////////// - // Destructor of the class. - // - virtual ~ACMGenericCodec(); - - - /////////////////////////////////////////////////////////////////////////// - // ACMGenericCodec* CreateInstance(); - // The function will be used for FEC. It is not implemented yet. - // - virtual ACMGenericCodec* CreateInstance() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 Encode() - // The function is called to perform an encoding of the audio stored in - // audio buffer. An encoding is performed only if enough audio, i.e. equal - // to the frame-size of the codec, exist. The audio frame will be processed - // by VAD and CN/DTX if required. There are few different cases. - // - // A) Neither VAD nor DTX is active; the frame is encoded by the encoder. - // - // B) VAD is enabled but not DTX; in this case the audio is processed by VAD - // and encoded by the encoder. The "*encodingType" will be either - // "activeNormalEncode" or "passiveNormalEncode" if frame is active or - // passive, respectively. - // - // C) DTX is enabled; if the codec has internal VAD/DTX we just encode the - // frame by the encoder. Otherwise, the frame is passed through VAD and - // if identified as passive, then it will be processed by CN/DTX. If the - // frame is active it will be encoded by the encoder. - // - // This function acquires the appropriate locks and calls EncodeSafe() for - // the actual processing. - // - // Outputs: - // -bitStream : a buffer where bit-stream will be written to. - // -bitStreamLenByte : contains the length of the bit-stream in - // bytes. - // -timeStamp : contains the RTP timestamp, this is the - // sampling time of the first sample encoded - // (measured in number of samples). - // -encodingType : contains the type of encoding applied on the - // audio samples. The alternatives are - // (c.f. acm_common_types.h) - // -kNoEncoding: - // there was not enough data to encode. or - // some error has happened that we could - // not do encoding. - // -kActiveNormalEncoded: - // the audio frame is active and encoded by - // the given codec. - // -kPassiveNormalEncoded: - // the audio frame is passive but coded with - // the given codec (NO DTX). - // -kPassiveDTXWB: - // The audio frame is passive and used - // wide-band CN to encode. - // -kPassiveDTXNB: - // The audio frame is passive and used - // narrow-band CN to encode. - // - // Return value: - // -1 if error is occurred, otherwise the length of the bit-stream in - // bytes. - // - WebRtc_Word16 Encode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_UWord32* timeStamp, - WebRtcACMEncodingType* encodingType); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 Decode() - // This function is used to decode a given bit-stream, without engaging - // NetEQ. - // - // This function acquires the appropriate locks and calls DecodeSafe() for - // the actual processing. Please note that this is not functional yet. - // - // Inputs: - // -bitStream : a buffer where bit-stream will be read. - // -bitStreamLenByte : the length of the bit-stream in bytes. - // - // Outputs: - // -audio : pointer to a buffer where the audio will written. - // -audioSamples : number of audio samples out of decoding the given - // bit-stream. - // -speechType : speech type (for future use). - // - // Return value: - // -1 if failed to decode, - // 0 if succeeded. - // - WebRtc_Word16 Decode( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - - /////////////////////////////////////////////////////////////////////////// - // bool EncoderInitialized(); - // - // Return value: - // True if the encoder is successfully initialized, - // false otherwise. - // - bool EncoderInitialized(); - - - /////////////////////////////////////////////////////////////////////////// - // bool DecoderInitialized(); - // - // Return value: - // True if the decoder is successfully initialized, - // false otherwise. - // - bool DecoderInitialized(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 EncoderParams() - // It is called to get encoder parameters. It will call - // EncoderParamsSafe() in turn. - // - // Output: - // -encParams : a buffer where the encoder parameters is - // written to. If the encoder is not - // initialized this buffer is filled with - // invalid values - // Return value: - // -1 if the encoder is not initialized, - // 0 otherwise. - // - // - WebRtc_Word16 EncoderParams( - WebRtcACMCodecParams *encParams); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 DecoderParams(...) - // It is called to get decoder parameters. It will call DecoderParamsSafe() - // in turn. - // - // Output: - // -decParams : a buffer where the decoder parameters is - // written to. If the decoder is not initialized - // this buffer is filled with invalid values - // - // Return value: - // -1 if the decoder is not initialized, - // 0 otherwise. - // - // - bool DecoderParams( - WebRtcACMCodecParams *decParams, - const WebRtc_UWord8 payloadType); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InitEncoder(...) - // This function is called to initialize the encoder with the given - // parameters. - // - // Input: - // -codecParams : parameters of encoder. - // -forceInitialization: if false the initialization is invoked only if - // the encoder is not initialized. If true the - // encoder is forced to (re)initialize. - // - // Return value: - // 0 if could initialize successfully, - // -1 if failed to initialize. - // - // - WebRtc_Word16 InitEncoder( - WebRtcACMCodecParams* codecParams, - bool forceInitialization); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InitDecoder() - // This function is called to initialize the decoder with the given - // parameters. (c.f. acm_common_defs.h & common_types.h for the - // definition of the structure) - // - // Input: - // -codecParams : parameters of decoder. - // -forceInitialization: if false the initialization is invoked only - // if the decoder is not initialized. If true - // the encoder is forced to(re)initialize. - // - // Return value: - // 0 if could initialize successfully, - // -1 if failed to initialize. - // - // - WebRtc_Word16 InitDecoder( - WebRtcACMCodecParams* codecParams, - bool forceInitialization); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 RegisterInNetEq(...) - // This function is called to register the decoder in NetEq, with the given - // payload-type. - // - // Inputs: - // -netEq : pointer to NetEq Instance - // -codecInst : instance with of the codec settings of the codec - // - // Return values - // -1 if failed to register, - // 0 if successfully initialized. - // - WebRtc_Word32 RegisterInNetEq( - ACMNetEQ* netEq, - const CodecInst& codecInst); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 Add10MsData(...) - // This function is called to add 10 ms of audio to the audio buffer of - // the codec. - // - // Inputs: - // -timeStamp : the timestamp of the 10 ms audio. the timestamp - // is the sampling time of the - // first sample measured in number of samples. - // -data : a buffer that contains the audio. The codec - // expects to get the audio in correct sampling - // frequency - // -length : the length of the audio buffer - // -audioChannel : 0 for mono, 1 for stereo (not supported yet) - // - // Return values: - // -1 if failed - // 0 otherwise. - // - WebRtc_Word32 Add10MsData( - const WebRtc_UWord32 timeStamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 length, - const WebRtc_UWord8 audioChannel); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_UWord32 NoMissedSamples() - // This function returns the number of samples which are overwritten in - // the audio buffer. The audio samples are overwritten if the input audio - // buffer is full, but Add10MsData() is called. (We might remove this - // function if it is not used) - // - // Return Value: - // Number of samples which are overwritten. - // - WebRtc_UWord32 NoMissedSamples() const; - - - /////////////////////////////////////////////////////////////////////////// - // void ResetNoMissedSamples() - // This function resets the number of overwritten samples to zero. - // (We might remove this function if we remove NoMissedSamples()) - // - void ResetNoMissedSamples(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SetBitRate() - // The function is called to set the encoding rate. - // - // Input: - // -bitRateBPS : encoding rate in bits per second - // - // Return value: - // -1 if failed to set the rate, due to invalid input or given - // codec is not rate-adjustable. - // 0 if the rate is adjusted successfully - // - WebRtc_Word16 SetBitRate(const WebRtc_Word32 bitRateBPS); - - - /////////////////////////////////////////////////////////////////////////// - // DestructEncoderInst() - // This API is used in conferencing. It will free the memory that is pointed - // by "ptrInst". "ptrInst" is a pointer to encoder instance, created and - // filled up by calling EncoderInst(...). - // - // Inputs: - // -ptrInst : pointer to an encoder instance to be deleted. - // - // - void DestructEncoderInst( - void* ptrInst); - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 AudioBuffer() - // This is used when synchronization of codecs is required. There are cases - // that the audio buffers of two codecs have to be synched. By calling this - // function on can get the audio buffer and other related parameters, such - // as timestamps... - // - // Output: - // -audioBuff : a pointer to WebRtcACMAudioBuff where the audio - // buffer of this codec will be written to. - // - // Return value: - // -1 if fails to copy the audio buffer, - // 0 if succeeded. - // - WebRtc_Word16 AudioBuffer( - WebRtcACMAudioBuff& audioBuff); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_UWord32 EarliestTimestamp() - // Returns the timestamp of the first 10 ms in audio buffer. This is used - // to identify if a synchronization of two encoders is required. - // - // Return value: - // timestamp of the first 10 ms audio in the audio buffer. - // - WebRtc_UWord32 EarliestTimestamp() const; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SetAudioBuffer() - // This function is called to set the audio buffer and the associated - // parameters to a given value. - // - // Return value: - // -1 if fails to copy the audio buffer, - // 0 if succeeded. - // - WebRtc_Word16 SetAudioBuffer(WebRtcACMAudioBuff& audioBuff); - - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SetVAD() - // This is called to set VAD & DTX. If the codec has internal DTX that will - // be used. If DTX is enabled and the codec does not have internal DTX, - // WebRtc-VAD will be used to decide if the frame is active. If DTX is - // disabled but VAD is enabled. The audio is passed through VAD to label it - // as active or passive, but the frame is encoded normally. However the - // bit-stream is labeled properly so that ACM::Process() can use this - // information. In case of failure, the previous states of the VAD & DTX - // are kept. - // - // Inputs: - // -enableDTX : if true DTX will be enabled otherwise the DTX is - // disabled. If codec has internal DTX that will be - // used, otherwise WebRtc-CNG is used. In the latter - // case VAD is automatically activated. - // -enableVAD : if true WebRtc-VAD is enabled, otherwise VAD is - // disabled, except for the case that DTX is enabled - // but codec doesn't have internal DTX. In this case - // VAD is enabled regardless of the value of - // "enableVAD." - // -mode : this specifies the aggressiveness of VAD. - // - // Return value - // -1 if failed to set DTX & VAD as specified, - // 0 if succeeded. - // - WebRtc_Word16 SetVAD( - const bool enableDTX = true, - const bool enableVAD = false, - const ACMVADMode mode = VADNormal); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ReplaceInternalDTX() - // This is called to replace the codec internal DTX with WebRtc DTX. - // This is only valid for G729 where the user has possibility to replace - // AnnexB with WebRtc DTX. For other codecs this function has no effect. - // - // Input: - // -replaceInternalDTX : if true the internal DTX is replaced with WebRtc. - // - // Return value - // -1 if failed to replace internal DTX, - // 0 if succeeded. - // - WebRtc_Word32 ReplaceInternalDTX(const bool replaceInternalDTX); - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 IsInternalDTXReplaced() - // This is called to check if the codec internal DTX is replaced by WebRtc DTX. - // This is only valid for G729 where the user has possibility to replace - // AnnexB with WebRtc DTX. For other codecs this function has no effect. - // - // Output: - // -internalDTXReplaced : if true the internal DTX is replaced with WebRtc. - // - // Return value - // -1 if failed to check if replace internal DTX or replacement not feasible, - // 0 if succeeded. - // - WebRtc_Word32 IsInternalDTXReplaced(bool* internalDTXReplaced); - - /////////////////////////////////////////////////////////////////////////// - // void SetNetEqDecodeLock() - // Passes the NetEq lock to the codec. - // - // Input: - // -netEqDecodeLock : pointer to the lock associated with NetEQ of ACM. - // - void SetNetEqDecodeLock( - RWLockWrapper* netEqDecodeLock) - { - _netEqDecodeLock = netEqDecodeLock; - } - - - /////////////////////////////////////////////////////////////////////////// - // bool HasInternalDTX() - // Used to check if the codec has internal DTX. - // - // Return value: - // true if the codec has an internal DTX, e.g. G729, - // false otherwise. - // - bool HasInternalDTX() const - { - return _hasInternalDTX; - } - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 GetEstimatedBandwidth() - // Used to get decoder estimated bandwidth. Only iSAC will provide a value. - // - // - // Return value: - // -1 if fails to get decoder estimated bandwidth, - // >0 estimated bandwidth in bits/sec. - // - WebRtc_Word32 GetEstimatedBandwidth(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 SetEstimatedBandwidth() - // Used to set estiamted bandwidth sent out of band from other side. Only - // iSAC will have use for the value. - // - // Input: - // -estimatedBandwidth: estimated bandwidth in bits/sec - // - // Return value: - // -1 if fails to set estimated bandwidth, - // 0 on success. - // - WebRtc_Word32 SetEstimatedBandwidth(WebRtc_Word32 estimatedBandwidth); - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 GetRedPayload() - // Used to get codec specific RED payload (if such is implemented). - // Currently only done in iSAC. - // - // Outputs: - // -redPayload : a pointer to the data for RED payload. - // -payloadBytes : number of bytes in RED payload. - // - // Return value: - // -1 if fails to get codec specific RED, - // 0 if succeeded. - // - WebRtc_Word32 GetRedPayload( - WebRtc_UWord8* redPayload, - WebRtc_Word16* payloadBytes); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 ResetEncoder() - // By calling this function you would re-initialize the encoder with the - // current parameters. All the settings, e.g. VAD/DTX, frame-size... should - // remain unchanged. (In case of iSAC we don't want to lose BWE history.) - // - // Return value - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 ResetEncoder(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 ResetEncoder() - // By calling this function you would re-initialize the decoder with the - // current parameters. - // - // Return value - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 ResetDecoder( - WebRtc_Word16 payloadType); - - - /////////////////////////////////////////////////////////////////////////// - // void DestructEncoder() - // This function is called to delete the encoder instance, if possible, to - // have a fresh start. For codecs where encoder and decoder share the same - // instance we cannot delete the encoder and instead we will initialize the - // encoder. We also delete VAD and DTX if they have been created. - // - void DestructEncoder(); - - - /////////////////////////////////////////////////////////////////////////// - // void DestructDecoder() - // This function is called to delete the decoder instance, if possible, to - // have a fresh start. For codecs where encoder and decoder share the same - // instance we cannot delete the encoder and instead we will initialize the - // decoder. Before deleting decoder instance it has to be removed from the - // NetEq list. - // - void DestructDecoder(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 SamplesLeftToEncode() - // Returns the number of samples required to be able to do encoding. - // - // Return value: - // Number of samples. - // - WebRtc_Word16 SamplesLeftToEncode(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_UWord32 LastEncodedTimestamp() - // Returns the timestamp of the last frame it encoded. - // - // Return value: - // Timestamp. - // - WebRtc_UWord32 LastEncodedTimestamp() const; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 UnregisterFromNetEq() - // To remove the codec from NetEQ. If the codec (or the decoder instance) - // is going to be deleted, first the codec has to be removed from NetEq - // by calling this function. - // - // Input: - // -netEq : pointer to a NetEq instance that the codec - // has to be unregistered from. - // - // Output: - // -1 if failed to unregister the codec, - // 0 if the codec is successfully unregistered. - // - WebRtc_Word32 UnregisterFromNetEq( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - - /////////////////////////////////////////////////////////////////////////// - // SetUniqueID() - // Set a unique ID for the codec to be used for tracing and debuging - // - // Input - // -id : A number to identify the codec. - // - void SetUniqueID( - const WebRtc_UWord32 id); - - - /////////////////////////////////////////////////////////////////////////// - // IsAudioBufferFresh() - // Specifies if ever audio is injected to this codec. - // - // Return value - // -true; no audio is feed into this codec - // -false; audio has already been fed to the codec. - // - bool IsAudioBufferFresh() const; - - - /////////////////////////////////////////////////////////////////////////// - // UpdateDecoderSampFreq() - // For most of the codecs this function does nothing. It must be - // implemented for those codecs that one codec instance serves as the - // decoder for different flavers of the codec. One example is iSAC. there, - // iSAC 16 kHz and iSAC 32 kHz are treated as two different codecs with - // different payload types, however, there is only one iSAC instance to - // decode. The reason for that is we would like to decode and encode with - // the same codec instance for bandwidth estimator to work. - // - // Each time that we receive a new payload type, we call this funtion to - // prepare the decoder associated with the new payload. Normally, decoders - // doesn't have to do anything. For iSAC the decoder has to change it's - // sampling rate. The input parameter specifies the current flaver of the - // codec in codec database. For instance, if we just got a SWB payload then - // the input parameter is ACMCodecDB::isacswb. - // - // Input: - // -codecId : the ID of the codec associated with the - // payload type that we just received. - // - // Return value: - // 0 if succeeded in updating the decoder. - // -1 if failed to update. - // - virtual WebRtc_Word16 UpdateDecoderSampFreq( - WebRtc_Word16 /* codecId */) - { - return 0; - } - - - /////////////////////////////////////////////////////////////////////////// - // UpdateEncoderSampFreq() - // Call this function to update the encoder sampling frequency. This - // is for codecs where one payload-name supports several encoder sampling - // frequencies. Otherwise, to change the sampling frequency we need to - // register new codec. ACM will consider that as registration of a new - // codec, not a change in parameter. For iSAC, switching from WB to SWB - // is treated as a change in parameter. Therefore, we need this function. - // - // Input: - // -encoderSampFreqHz : encoder sampling frequency. - // - // Return value: - // -1 if failed, or if this is meaningless for the given codec. - // 0 if succeeded. - // - virtual WebRtc_Word16 UpdateEncoderSampFreq( - WebRtc_UWord16 encoderSampFreqHz); - - - /////////////////////////////////////////////////////////////////////////// - // EncoderSampFreq() - // Get the sampling frequency that the encoder (WebRtc wrapper) expects. - // - // Output: - // -sampFreqHz : sampling frequency, in Hertz, which the encoder - // should be fed with. - // - // Return value: - // -1 if failed to output sampling rate. - // 0 if the sample rate is returned successfully. - // - virtual WebRtc_Word16 EncoderSampFreq( - WebRtc_UWord16& sampFreqHz); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word32 ConfigISACBandwidthEstimator() - // Call this function to configure the bandwidth estimator of ISAC. - // During the adaptation of bit-rate, iSAC atomatically adjusts the - // frame-size (either 30 or 60 ms) to save on RTP header. The initial - // frame-size can be specified by the first argument. The configuration also - // regards the initial estimate of bandwidths. The estimator starts from - // this point and converges to the actual bottleneck. This is given by the - // second parameter. Furthermore, it is also possible to control the - // adaptation of frame-size. This is specified by the last parameter. - // - // Input: - // -initFrameSizeMsec : initial frame-size in milisecods. For iSAC-wb - // 30 ms and 60 ms (default) are acceptable values, - // and for iSAC-swb 30 ms is the only acceptable - // value. Zero indiates default value. - // -initRateBitPerSec : initial estimate of the bandwidth. Values - // between 10000 and 58000 are acceptable. - // -enforceFrameSize : if true, the frame-size will not be adapted. - // - // Return value: - // -1 if failed to configure the bandwidth estimator, - // 0 if the configuration was successfully applied. - // - virtual WebRtc_Word32 ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize); - - - /////////////////////////////////////////////////////////////////////////// - // SetISACMaxPayloadSize() - // Set the maximum payload size of iSAC packets. No iSAC payload, - // regardless of its frame-size, may exceed the given limit. For - // an iSAC payload of size B bits and frame-size T sec we have; - // (B < maxPayloadLenBytes * 8) and (B/T < maxRateBitPerSec), c.f. - // SetISACMaxRate(). - // - // Input: - // -maxPayloadLenBytes : maximum payload size in bytes. - // - // Return value: - // -1 if failed to set the maximm payload-size. - // 0 if the given linit is seet successfully. - // - virtual WebRtc_Word32 SetISACMaxPayloadSize( - const WebRtc_UWord16 maxPayloadLenBytes); - - - /////////////////////////////////////////////////////////////////////////// - // SetISACMaxRate() - // Set the maximum instantaneous rate of iSAC. For a payload of B bits - // with a frame-size of T sec the instantaneous rate is B/T bist per - // second. Therefore, (B/T < maxRateBitPerSec) and - // (B < maxPayloadLenBytes * 8) are always satisfied for iSAC payloads, - // c.f SetISACMaxPayloadSize(). - // - // Input: - // -maxRateBitPerSec : maximum instantaneous bit-rate given in bits/sec. - // - // Return value: - // -1 if failed to set the maximum rate. - // 0 if the maximum rate is set successfully. - // - virtual WebRtc_Word32 SetISACMaxRate( - const WebRtc_UWord32 maxRateBitPerSec); - - - /////////////////////////////////////////////////////////////////////////// - // SaveDecoderParamS() - // Save the parameters of decoder. - // - // Input: - // -codecParams : pointer to a struct where the parameters of - // decoder is stored in. - // - void SaveDecoderParam( - const WebRtcACMCodecParams* codecParams); - - - WebRtc_Word32 FrameSize() - { - return _frameLenSmpl; - } - - void SetIsMaster(bool isMaster); - - - - - /////////////////////////////////////////////////////////////////////////// - // REDPayloadISAC() - // This is an iSAC-specific function. The function is called to get RED - // paylaod from a default-encoder. - // - // Inputs: - // -isacRate : the target rate of the main payload. A RED - // paylaod is generated according to the rate of - // main paylaod. Note that we are not specifying the - // rate of RED payload, but the main payload. - // -isacBwEstimate : bandwidth information should be inserted in - // RED payload. - // - // Output: - // -payload : pointer to a buffer where the RED paylaod will - // written to. - // -paylaodLenBytes : a place-holder to write the length of the RED - // payload in Bytes. - // - // Return value: - // -1 if an error occures, otherwise the length of the payload (in Bytes) - // is returned. - // - // - virtual WebRtc_Word16 REDPayloadISAC( - const WebRtc_Word32 isacRate, - const WebRtc_Word16 isacBwEstimate, - WebRtc_UWord8* payload, - WebRtc_Word16* payloadLenBytes); - -protected: - /////////////////////////////////////////////////////////////////////////// - // All the functions with FunctionNameSafe(...) contain the actual - // implementation of FunctionName(...). FunctionName() acquires an - // appropriate lock and calls FunctionNameSafe() to do the actual work. - // Therefore, for the description of functionality, input/output arguments - // and return value we refer to FunctionName() - // - - /////////////////////////////////////////////////////////////////////////// - // See Encode() for the description of function, input(s)/output(s) and - // return value. - // - WebRtc_Word16 EncodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_UWord32* timeStamp, - WebRtcACMEncodingType* encodingType); - - /////////////////////////////////////////////////////////////////////////// - // See Decode() for the description of function, input(s)/output(s) and - // return value. - // - virtual WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType) = 0; - - /////////////////////////////////////////////////////////////////////////// - // See Add10MsSafe() for the description of function, input(s)/output(s) - // and return value. - // - virtual WebRtc_Word32 Add10MsDataSafe( - const WebRtc_UWord32 timeStamp, - const WebRtc_Word16* data, - const WebRtc_UWord16 length, - const WebRtc_UWord8 audioChannel); - - /////////////////////////////////////////////////////////////////////////// - // See RegisterInNetEq() for the description of function, - // input(s)/output(s) and return value. - // - virtual WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) = 0; - - /////////////////////////////////////////////////////////////////////////// - // See EncoderParam() for the description of function, input(s)/output(s) - // and return value. - // - WebRtc_Word16 EncoderParamsSafe( - WebRtcACMCodecParams *encParams); - - /////////////////////////////////////////////////////////////////////////// - // See DecoderParam for the description of function, input(s)/output(s) - // and return value. - // - // Note: - // Any Class where a single instance handle several flavers of the - // same codec, therefore, several payload types are associated with - // the same instance have to implement this function. - // - // Currently only iSAC is implementing it. A single iSAC instance is - // used for decoding both WB & SWB stream. At one moment both WB & SWB - // can be registered as receive codec. Hence two payloads are associated - // with a single codec instance. - // - virtual bool DecoderParamsSafe( - WebRtcACMCodecParams *decParams, - const WebRtc_UWord8 payloadType); - - /////////////////////////////////////////////////////////////////////////// - // See ResetEncoder() for the description of function, input(s)/output(s) - // and return value. - // - WebRtc_Word16 ResetEncoderSafe(); - - /////////////////////////////////////////////////////////////////////////// - // See InitEncoder() for the description of function, input(s)/output(s) - // and return value. - // - WebRtc_Word16 InitEncoderSafe( - WebRtcACMCodecParams *codecParams, - bool forceInitialization); - - /////////////////////////////////////////////////////////////////////////// - // See InitDecoder() for the description of function, input(s)/output(s) - // and return value. - // - WebRtc_Word16 InitDecoderSafe( - WebRtcACMCodecParams *codecParams, - bool forceInitialization); - - /////////////////////////////////////////////////////////////////////////// - // See ResetDecoder() for the description of function, input(s)/output(s) - // and return value. - // - WebRtc_Word16 ResetDecoderSafe( - WebRtc_Word16 payloadType); - - /////////////////////////////////////////////////////////////////////////// - // See DestructEncoder() for the description of function, - // input(s)/output(s) and return value. - // - virtual void DestructEncoderSafe() = 0; - - /////////////////////////////////////////////////////////////////////////// - // See DestructDecoder() for the description of function, - // input(s)/output(s) and return value. - // - virtual void DestructDecoderSafe() = 0; - - /////////////////////////////////////////////////////////////////////////// - // See SetBitRate() for the description of function, input(s)/output(s) - // and return value. - // - // Any codec that can change the bit-rate has to implement this. - // - virtual WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 bitRateBPS); - - /////////////////////////////////////////////////////////////////////////// - // See GetEstimatedBandwidth() for the description of function, input(s)/output(s) - // and return value. - // - virtual WebRtc_Word32 GetEstimatedBandwidthSafe(); - - /////////////////////////////////////////////////////////////////////////// - // See SetEstimatedBandwidth() for the description of function, input(s)/output(s) - // and return value. - // - virtual WebRtc_Word32 SetEstimatedBandwidthSafe(WebRtc_Word32 estimatedBandwidth); - - /////////////////////////////////////////////////////////////////////////// - // See GetRedPayload() for the description of function, input(s)/output(s) - // and return value. - // - virtual WebRtc_Word32 GetRedPayloadSafe( - WebRtc_UWord8* redPayload, - WebRtc_Word16* payloadBytes); - - /////////////////////////////////////////////////////////////////////////// - // See SetVAD() for the description of function, input(s)/output(s) and - // return value. - // - WebRtc_Word16 SetVADSafe( - const bool enableDTX = true, - const bool enableVAD = false, - const ACMVADMode mode = VADNormal); - - /////////////////////////////////////////////////////////////////////////// - // See ReplaceInternalDTX() for the description of function, input and - // return value. - // - virtual WebRtc_Word32 ReplaceInternalDTXSafe( - const bool replaceInternalDTX); - - /////////////////////////////////////////////////////////////////////////// - // See IsInternalDTXReplaced() for the description of function, input and - // return value. - // - virtual WebRtc_Word32 IsInternalDTXReplacedSafe( - bool* internalDTXReplaced); - - /////////////////////////////////////////////////////////////////////////// - // See UnregisterFromNetEq() for the description of function, - // input(s)/output(s) and return value. - // - virtual WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 CreateEncoder() - // Creates the encoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 CreateEncoder(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 CreateDecoder() - // Creates the decoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 CreateDecoder(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 EnableVAD(); - // Enables VAD with the given mode. The VAD instance will be created if - // it does not exists. - // - // Input: - // -mode : VAD mode c.f. audio_coding_module_typedefs.h for - // the options. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 EnableVAD(ACMVADMode mode); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 DisableVAD() - // Disables VAD. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 DisableVAD(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 EnableDTX() - // Enables DTX. This method should be overwritten for codecs which have - // internal DTX. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 EnableDTX(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 DisableDTX() - // Disables usage of DTX. This method should be overwritten for codecs which - // have internal DTX. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 DisableDTX(); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalEncode() - // This is a codec-specific function called in EncodeSafe() to actually - // encode a frame of audio. - // - // Outputs: - // -bitStream : pointer to a buffer where the bit-stream is - // written to. - // -bitStreamLenByte : the length of the bit-stream in byte, a negative - // value indicates error. - // - // Return value: - // -1 if failed, - // otherwise the length of the bit-stream is returned. - // - virtual WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalInitEncoder() - // This is a codec-specific function called in InitEncoderSafe(), it has to - // do all codec-specific operation to initialize the encoder given the - // encoder parameters. - // - // Input: - // -codecParams : pointer to a structure that contains parameters to - // initialize encoder. - // Set codecParam->CodecInst.rate to -1 for - // iSAC to operate in adaptive mode. - // (to do: if frame-length is -1 frame-length will be - // automatically adjusted, otherwise, given - // frame-length is forced) - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalInitDecoder() - // This is a codec-specific function called in InitDecoderSafe(), it has to - // do all codec-specific operation to initialize the decoder given the - // decoder parameters. - // - // Input: - // -codecParams : pointer to a structure that contains parameters to - // initialize encoder. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // void IncreaseNoMissedSamples() - // This method is called to increase the number of samples that are - // overwritten in the audio buffer. - // - // Input: - // -noSamples : the number of overwritten samples is incremented - // by this value. - // - void IncreaseNoMissedSamples( - const WebRtc_Word16 noSamples); - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalCreateEncoder() - // This is a codec-specific method called in CreateEncoderSafe() it is - // supposed to perform all codec-specific operations to create encoder - // instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 InternalCreateEncoder() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalCreateDecoder() - // This is a codec-specific method called in CreateDecoderSafe() it is - // supposed to perform all codec-specific operations to create decoder - // instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 InternalCreateDecoder() = 0; - - - /////////////////////////////////////////////////////////////////////////// - // void InternalDestructEncoderInst() - // This is a codec-specific method, used in conferencing, called from - // DestructEncoderInst(). The input argument is pointer to encoder instance - // (codec instance for codecs that encoder and decoder share the same - // instance). This method is called to free the memory that "ptrInst" is - // pointing to. - // - // Input: - // -ptrInst : pointer to encoder instance. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual void InternalDestructEncoderInst( - void* ptrInst) = 0; - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 InternalResetEncoder() - // This method is called to reset the states of encoder. However, the - // current parameters, e.g. frame-length, should remain as they are. For - // most of the codecs a re-initialization of the encoder is what needs to - // be down. But for iSAC we like to keep the BWE history so we cannot - // re-initialize. As soon as such an API is implemented in iSAC this method - // has to be overwritten in ACMISAC class. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - virtual WebRtc_Word16 InternalResetEncoder(); - - - - /////////////////////////////////////////////////////////////////////////// - // WebRtc_Word16 ProcessFrameVADDTX() - // This function is called when a full frame of audio is available. It will - // break the audio frame into blocks such that each block could be processed - // by VAD & CN/DTX. If a frame is divided into two blocks then there are two - // cases. First, the first block is active, the second block will not be - // processed by CN/DTX but only by VAD and return to caller with - // '*samplesProcessed' set to zero. There, the audio frame will be encoded - // by the encoder. Second, the first block is inactive and is processed by - // CN/DTX, then we stop processing the next block and return to the caller - // which is EncodeSafe(), with "*samplesProcessed" equal to the number of - // samples in first block. - // - // Output: - // -bitStream : pointer to a buffer where DTX frame, if - // generated, will be written to. - // -bitStreamLenByte : contains the length of bit-stream in bytes, if - // generated. Zero if no bit-stream is generated. - // -noSamplesProcessed : contains no of samples that actually CN has - // processed. Those samples processed by CN will not - // be encoded by the encoder, obviously. If - // contains zero, it means that the frame has been - // identified as active by VAD. Note that - // "*noSamplesProcessed" might be non-zero but - // "*bitStreamLenByte" be zero. - // - // Return value: - // -1 if failed, - // 0 if succeeded. - // - WebRtc_Word16 ProcessFrameVADDTX( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_Word16* samplesProcessed); - - - /////////////////////////////////////////////////////////////////////////// - // CanChangeEncodingParam() - // Check if the codec parameters can be changed. In conferencing normally - // codec parametrs cannot be changed. The exception is bit-rate of isac. - // - // return value: - // -true if codec parameters are allowed to change. - // -flase otherwise. - // - virtual bool CanChangeEncodingParam(CodecInst& codecInst); - - - /////////////////////////////////////////////////////////////////////////// - // CurrentRate() - // Call to get the current encoding rate of the encoder. This function - // should be overwritten for codecs whic automatically change their - // target rate. One example is iSAC. The output of the function is the - // current target rate. - // - // Output: - // -rateBitPerSec : the current target rate of the codec. - // - virtual void CurrentRate( - WebRtc_Word32& /* rateBitPerSec */) - { - - return; - } - - virtual void SaveDecoderParamSafe( - const WebRtcACMCodecParams* codecParams); - - - // &_inAudio[_inAudioIxWrite] always point to where new audio can be - // written to - WebRtc_Word16 _inAudioIxWrite; - - // &_inAudio[_inAudioIxRead] points to where audio has to be read from - WebRtc_Word16 _inAudioIxRead; - - WebRtc_Word16 _inTimestampIxWrite; - - // Where the audio is stored before encoding, - // To save memory the following buffer can be allocated - // dynamically for 80ms depending on the sampling frequency - // of the codec. - WebRtc_Word16* _inAudio; - WebRtc_UWord32* _inTimestamp; - - WebRtc_Word16 _frameLenSmpl; - WebRtc_UWord16 _noChannels; - - // This will point to a static database of the supported codecs - WebRtc_Word16 _codecID; - - // This will account for the No of samples were not encoded - // the case is rare, either samples are missed due to overwite - // at input buffer or due to encoding error - WebRtc_UWord32 _noMissedSamples; - - // True if the encoder instance created - bool _encoderExist; - bool _decoderExist; - // True if the ecncoder instance initialized - bool _encoderInitialized; - bool _decoderInitialized; - - bool _registeredInNetEq; - - // VAD/DTX - bool _hasInternalDTX; - WebRtcVadInst* _ptrVADInst; - bool _vadEnabled; - ACMVADMode _vadMode; - WebRtc_Word16 _vadLabel[MAX_FRAME_SIZE_10MSEC]; - bool _dtxEnabled; - WebRtcCngEncInst* _ptrDTXInst; - WebRtc_UWord8 _numLPCParams; - bool _sentCNPrevious; - bool _isMaster; - - WebRtcACMCodecParams _encoderParams; - WebRtcACMCodecParams _decoderParams; - - // Used as a global lock for all avaiable decoders - // so that no decoder is used when NetEQ decodes. - RWLockWrapper* _netEqDecodeLock; - // Used to lock wrapper internal data - // such as buffers and state variables. - RWLockWrapper& _codecWrapperLock; - - WebRtc_UWord32 _lastEncodedTimestamp; - WebRtc_UWord32 _lastTimestamp; - bool _isAudioBuffFresh; - WebRtc_UWord32 _uniqueID; -}; - -} // namespace webrt - -#endif // ACM_GENERIC_CODEC_H diff --git a/modules/audio_coding/main/source/acm_gsmfr.cc b/modules/audio_coding/main/source/acm_gsmfr.cc deleted file mode 100644 index 073040895..000000000 --- a/modules/audio_coding/main/source/acm_gsmfr.cc +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_gsmfr.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_GSMFR - // NOTE! GSM-FR is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used GSM-FR API file. - // #include "gsmfr_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_GSMFR - -ACMGSMFR::ACMGSMFR( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMGSMFR::~ACMGSMFR() -{ - return; -} - - -WebRtc_Word16 -ACMGSMFR::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMGSMFR::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMGSMFR::EnableDTX() -{ - return -1; -} - - -WebRtc_Word16 -ACMGSMFR::DisableDTX() -{ - return -1; -} - - -WebRtc_Word16 -ACMGSMFR::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMGSMFR::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMGSMFR::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMGSMFR::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMGSMFR::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMGSMFR::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMGSMFR::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMGSMFR::DestructDecoderSafe() -{ - return; -} - - -void -ACMGSMFR::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMGSMFR::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real GSM FR wrapper -extern WebRtc_Word16 WebRtcGSMFR_CreateEnc(GSMFR_encinst_t_** inst); -extern WebRtc_Word16 WebRtcGSMFR_CreateDec(GSMFR_decinst_t_** inst); -extern WebRtc_Word16 WebRtcGSMFR_FreeEnc(GSMFR_encinst_t_* inst); -extern WebRtc_Word16 WebRtcGSMFR_FreeDec(GSMFR_decinst_t_* inst); -extern WebRtc_Word16 WebRtcGSMFR_Encode(GSMFR_encinst_t_* encInst, WebRtc_Word16* input, - WebRtc_Word16 len, WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcGSMFR_EncoderInit(GSMFR_encinst_t_* encInst, WebRtc_Word16 mode); -extern WebRtc_Word16 WebRtcGSMFR_Decode(GSMFR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcGSMFR_DecodeBwe(GSMFR_decinst_t_* decInst, WebRtc_Word16* input); -extern WebRtc_Word16 WebRtcGSMFR_DecodePlc(GSMFR_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcGSMFR_DecoderInit(GSMFR_decinst_t_* decInst); - -ACMGSMFR::ACMGSMFR( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL) -{ - _codecID = codecID; - _hasInternalDTX = true; - return; -} - - -ACMGSMFR::~ACMGSMFR() -{ - if(_encoderInstPtr != NULL) - { - WebRtcGSMFR_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcGSMFR_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMGSMFR::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - *bitStreamLenByte = WebRtcGSMFR_Encode(_encoderInstPtr, - &_inAudio[_inAudioIxRead], _frameLenSmpl, (WebRtc_Word16*)bitStream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMGSMFR::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMGSMFR::EnableDTX() -{ - if(_dtxEnabled) - { - return 0; - } - else if(_encoderExist) - { - if(WebRtcGSMFR_EncoderInit(_encoderInstPtr, 1) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "EnableDTX: cannot init encoder for GSMFR"); - return -1; - } - _dtxEnabled = true; - return 0; - } - else - { - return -1; - } -} - - -WebRtc_Word16 -ACMGSMFR::DisableDTX() -{ - if(!_dtxEnabled) - { - return 0; - } - else if(_encoderExist) - { - if(WebRtcGSMFR_EncoderInit(_encoderInstPtr, 0) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "DisableDTX: cannot init encoder for GSMFR"); - return -1; - } - _dtxEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - - -WebRtc_Word16 -ACMGSMFR::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - if (WebRtcGSMFR_EncoderInit(_encoderInstPtr, ((codecParams->enableDTX)? 1:0)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitEncoder: cannot init encoder for GSMFR"); - } - return 0; -} - - -WebRtc_Word16 -ACMGSMFR::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - if (WebRtcGSMFR_DecoderInit(_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: cannot init decoder for GSMFR"); - return -1; - } - return 0; -} - - -WebRtc_Word32 -ACMGSMFR::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodecDef: decoder is not initialized for GSMFR"); - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_GSMFR_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderGSMFR, codecInst.pltype, - _decoderInstPtr, 8000); - SET_GSMFR_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMGSMFR::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMGSMFR::InternalCreateEncoder() -{ - if (WebRtcGSMFR_CreateEnc(&_encoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: cannot create instance for GSMFR encoder"); - return -1; - } - return 0; -} - - -void -ACMGSMFR::DestructEncoderSafe() -{ - if(_encoderInstPtr != NULL) - { - WebRtcGSMFR_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - _encoderExist = false; - _encoderInitialized = false; -} - - -WebRtc_Word16 -ACMGSMFR::InternalCreateDecoder() -{ - if (WebRtcGSMFR_CreateDec(&_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateDecoder: cannot create instance for GSMFR decoder"); - return -1; - } - return 0; -} - - -void -ACMGSMFR::DestructDecoderSafe() -{ - if(_decoderInstPtr != NULL) - { - WebRtcGSMFR_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - _decoderExist = false; - _decoderInitialized = false; -} - - -void -ACMGSMFR::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcGSMFR_FreeEnc((GSMFR_encinst_t_*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMGSMFR::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec: payload-type does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderGSMFR); -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_gsmfr.h b/modules/audio_coding/main/source/acm_gsmfr.h deleted file mode 100644 index ead5ea764..000000000 --- a/modules/audio_coding/main/source/acm_gsmfr.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_GSMFR_H -#define ACM_GSMFR_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -// forward declaration -struct GSMFR_encinst_t_; -struct GSMFR_decinst_t_; - -class ACMGSMFR : public ACMGenericCodec -{ -public: - ACMGSMFR(WebRtc_Word16 codecID); - ~ACMGSMFR(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 EnableDTX(); - - WebRtc_Word16 DisableDTX(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - GSMFR_encinst_t_* _encoderInstPtr; - GSMFR_decinst_t_* _decoderInstPtr; -}; - -} // namespace webrtc - -#endif // ACM_GSMFR_H - diff --git a/modules/audio_coding/main/source/acm_ilbc.cc b/modules/audio_coding/main/source/acm_ilbc.cc deleted file mode 100644 index 28e69e792..000000000 --- a/modules/audio_coding/main/source/acm_ilbc.cc +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_ilbc.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_ILBC - #include "ilbc.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_ILBC - -ACMILBC::ACMILBC( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMILBC::~ACMILBC() -{ - return; -} - - -WebRtc_Word16 -ACMILBC::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMILBC::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMILBC::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMILBC::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMILBC::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMILBC::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMILBC::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMILBC::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMILBC::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMILBC::DestructDecoderSafe() -{ - return; -} - - -void -ACMILBC::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - -WebRtc_Word16 -ACMILBC::SetBitRateSafe(const WebRtc_Word32 /* rate */) -{ - return -1; -} - - -WebRtc_Word16 -ACMILBC::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - - -ACMILBC::ACMILBC( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL) -{ - _codecID = codecID; - return; -} - - -ACMILBC::~ACMILBC() -{ - if(_encoderInstPtr != NULL) - { - WebRtcIlbcfix_EncoderFree(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcIlbcfix_DecoderFree(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMILBC::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - *bitStreamLenByte = WebRtcIlbcfix_Encode(_encoderInstPtr, - &_inAudio[_inAudioIxRead], _frameLenSmpl, (WebRtc_Word16*)bitStream); - if (*bitStreamLenByte < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalEncode: error in encode for ILBC"); - return -1; - } - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMILBC::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMILBC::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - // initialize with a correct processing block length - if((160 == (codecParams->codecInstant).pacsize) || - (320 == (codecParams->codecInstant).pacsize)) - { - // processing block of 20ms - return WebRtcIlbcfix_EncoderInit(_encoderInstPtr, 20); - } - else if((240 == (codecParams->codecInstant).pacsize) || - (480 == (codecParams->codecInstant).pacsize)) - { - // processing block of 30ms - return WebRtcIlbcfix_EncoderInit(_encoderInstPtr, 30); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitEncoder: invalid processing block"); - return -1; - } -} - - -WebRtc_Word16 -ACMILBC::InternalInitDecoder( - WebRtcACMCodecParams* codecParams) -{ - // initialize with a correct processing block length - if((160 == (codecParams->codecInstant).pacsize) || - (320 == (codecParams->codecInstant).pacsize)) - { - // processing block of 20ms - return WebRtcIlbcfix_DecoderInit(_decoderInstPtr, 20); - } - else if((240 == (codecParams->codecInstant).pacsize) || - (480 == (codecParams->codecInstant).pacsize)) - { - // processing block of 30ms - return WebRtcIlbcfix_DecoderInit(_decoderInstPtr, 30); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: invalid processing block"); - return -1; - } -} - - -WebRtc_Word32 -ACMILBC::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodeDef: decoder not initialized for ILBC"); - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_ILBC_FUNCTION." - // Then return the structure back to NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderILBC, codecInst.pltype, - _decoderInstPtr, 8000); - SET_ILBC_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMILBC::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMILBC::InternalCreateEncoder() -{ - if (WebRtcIlbcfix_EncoderCreate(&_encoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: cannot create instance for ILBC encoder"); - return -1; - } - return 0; -} - - -void -ACMILBC::DestructEncoderSafe() -{ - _encoderInitialized = false; - _encoderExist = false; - if(_encoderInstPtr != NULL) - { - WebRtcIlbcfix_EncoderFree(_encoderInstPtr); - _encoderInstPtr = NULL; - } -} - - -WebRtc_Word16 -ACMILBC::InternalCreateDecoder() -{ - if (WebRtcIlbcfix_DecoderCreate(&_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateDecoder: cannot create instance for ILBC decoder"); - return -1; - } - return 0; -} - - -void -ACMILBC::DestructDecoderSafe() -{ - _decoderInitialized = false; - _decoderExist = false; - if(_decoderInstPtr != NULL) - { - WebRtcIlbcfix_DecoderFree(_decoderInstPtr); - _decoderInstPtr = NULL; - } -} - - -void -ACMILBC::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcIlbcfix_EncoderFree((iLBC_encinst_t_*)ptrInst); - } - return; -} - -WebRtc_Word16 -ACMILBC::SetBitRateSafe(const WebRtc_Word32 rate) -{ - // Check that rate is valid. No need to store the value - if (rate == 13300) - { - WebRtcIlbcfix_EncoderInit(_encoderInstPtr, 30); - } - else if (rate == 15200) - { - WebRtcIlbcfix_EncoderInit(_encoderInstPtr, 20); - } - else - { - return -1; - } - _encoderParams.codecInstant.rate = rate; - - return 0; -} - -WebRtc_Word16 -ACMILBC::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec: given payload-type does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderILBC); -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_ilbc.h b/modules/audio_coding/main/source/acm_ilbc.h deleted file mode 100644 index c35c2cec7..000000000 --- a/modules/audio_coding/main/source/acm_ilbc.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_ILBC_H -#define ACM_ILBC_H - -#include "acm_generic_codec.h" - -// forward declaration -struct iLBC_encinst_t_; -struct iLBC_decinst_t_; - -namespace webrtc -{ - -class ACMILBC : public ACMGenericCodec -{ -public: - ACMILBC(WebRtc_Word16 codecID); - ~ACMILBC(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - iLBC_encinst_t_* _encoderInstPtr; - iLBC_decinst_t_* _decoderInstPtr; -}; - -} // namespace webrtc - -#endif //ACM_ILBC_H - diff --git a/modules/audio_coding/main/source/acm_isac.cc b/modules/audio_coding/main/source/acm_isac.cc deleted file mode 100644 index d5ec8914c..000000000 --- a/modules/audio_coding/main/source/acm_isac.cc +++ /dev/null @@ -1,1233 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_isac.h" -#include "acm_neteq.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - - -#ifdef WEBRTC_CODEC_ISAC - #include "acm_isac_macros.h" - #include "isac.h" -#endif - -#ifdef WEBRTC_CODEC_ISACFX - #include "acm_isac_macros.h" - #include "isacfix.h" -#endif - -namespace webrtc -{ - -// we need this otherwise we cannot use forward declaration -// in the header file -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) -struct ACMISACInst -{ - ACM_ISAC_STRUCT *inst; -}; -#endif - -#define ISAC_MIN_RATE 10000 -#define ISAC_MAX_RATE 56000 - - -// How the scaling is computed. iSAC computes a gain based on the -// bottleneck. It follows the following expression for that -// -// G(BN_kbps) = pow(10, (a + b * BN_kbps + c * BN_kbps * BN_kbps) / 20.0) -// / 3.4641; -// -// Where for 30 ms framelength we have, -// -// a = -23; b = 0.48; c = 0; -// -// As the default encoder is operating at 32kbps we have the scale as -// -// S(BN_kbps) = G(BN_kbps) / G(32); - -#define ISAC_NUM_SUPPORTED_RATES 9 -const WebRtc_UWord16 isacSuportedRates[ISAC_NUM_SUPPORTED_RATES] = { - 32000, 30000, 26000, 23000, 21000, - 19000, 17000, 15000, 12000}; - -const float isacScale[ISAC_NUM_SUPPORTED_RATES] = { - 1.0f, 0.8954f, 0.7178f, 0.6081f, 0.5445f, - 0.4875f, 0.4365f, 0.3908f, 0.3311f}; - -// Tables for bandwidth estimates -#define NR_ISAC_BANDWIDTHS 24 -const WebRtc_Word32 isacRatesWB[NR_ISAC_BANDWIDTHS] = -{ - 10000, 11100, 12300, 13700, 15200, 16900, - 18800, 20900, 23300, 25900, 28700, 31900, - 10100, 11200, 12400, 13800, 15300, 17000, - 18900, 21000, 23400, 26000, 28800, 32000}; - - -const WebRtc_Word32 isacRatesSWB[NR_ISAC_BANDWIDTHS] = -{ - 10000, 11000, 12400, 13800, 15300, 17000, - 18900, 21000, 23200, 25400, 27600, 29800, - 32000, 34100, 36300, 38500, 40700, 42900, - 45100, 47300, 49500, 51700, 53900, 56000, -}; - -#if (!defined(WEBRTC_CODEC_ISAC) && !defined(WEBRTC_CODEC_ISACFX)) - -ACMISAC::ACMISAC( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMISAC::~ACMISAC() -{ - return; -} - - -ACMGenericCodec* -ACMISAC::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMISAC::InternalEncode( - WebRtc_UWord8* /* bitstream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMISAC::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMISAC::DestructDecoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMISAC::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMISAC::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word32 -ACMISAC::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -void -ACMISAC::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - -WebRtc_Word16 -ACMISAC::DeliverCachedIsacData( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */, - WebRtc_UWord32* /* timestamp */, - WebRtcACMEncodingType* /* encodingType */, - const WebRtc_UWord16 /* isacRate */, - const WebRtc_UWord8 /* isacBWestimate */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::Transcode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */, - WebRtc_Word16 /* qBWE */, - WebRtc_Word32 /* scale */, - bool /* isRED */) -{ - return -1; -} - -WebRtc_Word16 -ACMISAC::SetBitRateSafe( - WebRtc_Word32 /* bitRate */) -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::GetEstimatedBandwidthSafe() -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::SetEstimatedBandwidthSafe( - WebRtc_Word32 /* estimatedBandwidth */) -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::GetRedPayloadSafe( - WebRtc_UWord8* /* redPayload */, - WebRtc_Word16* /* payloadBytes */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -bool -ACMISAC::IsValidDefaultEncoder() -{ - return false; -} - -WebRtc_Word16 -ACMISAC::UpdateDecoderSampFreq( - WebRtc_Word16 /* codecId */) -{ - return -1; -} - - -WebRtc_Word16 -ACMISAC::UpdateEncoderSampFreq( - WebRtc_UWord16 /* encoderSampFreqHz */) -{ - return -1; -} - -WebRtc_Word16 -ACMISAC::EncoderSampFreq( - WebRtc_UWord16& /* sampFreqHz */) -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::ConfigISACBandwidthEstimator( - const WebRtc_UWord8 /* initFrameSizeMsec */, - const WebRtc_UWord16 /* initRateBitPerSec */, - const bool /* enforceFrameSize */) -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::SetISACMaxPayloadSize( - const WebRtc_UWord16 /* maxPayloadLenBytes */) -{ - return -1; -} - -WebRtc_Word32 -ACMISAC::SetISACMaxRate( - const WebRtc_UWord32 /* maxRateBitPerSec */) -{ - return -1; -} - - -void -ACMISAC::UpdateFrameLen() -{ - return; -} - -void -ACMISAC::CurrentRate( - WebRtc_Word32& /*rateBitPerSec */) -{ - return; -} - -bool -ACMISAC::DecoderParamsSafe( - WebRtcACMCodecParams* /* decParams */, - const WebRtc_UWord8 /* payloadType */) -{ - return false; -} - -void -ACMISAC::SaveDecoderParamSafe( - const WebRtcACMCodecParams* /* codecParams */) -{ - return; -} - -WebRtc_Word16 -ACMISAC::REDPayloadISAC( - const WebRtc_Word32 /* isacRate */, - const WebRtc_Word16 /* isacBwEstimate */, - WebRtc_UWord8* /* payload */, - WebRtc_Word16* /* payloadLenBytes */) -{ - return -1; -} - - -#else //===================== Actual Implementation ======================= - - - -#ifdef WEBRTC_CODEC_ISACFX - -enum IsacSamplingRate -{ - kIsacWideband = 16, - kIsacSuperWideband = 32 -}; - -static float -ACMISACFixTranscodingScale( - WebRtc_UWord16 rate) -{ - // find the scale for transcoding, the scale is rounded - // downward - float scale = -1; - for(WebRtc_Word16 n=0; n < ISAC_NUM_SUPPORTED_RATES; n++) - { - if(rate >= isacSuportedRates[n]) - { - scale = isacScale[n]; - break; - } - } - return scale; -} - -static void -ACMISACFixGetSendBitrate( - ACM_ISAC_STRUCT* inst, - WebRtc_Word32* bottleNeck) -{ - *bottleNeck = WebRtcIsacfix_GetUplinkBw(inst); -} - -static WebRtc_Word16 -ACMISACFixGetNewBitstream( - ACM_ISAC_STRUCT* inst, - WebRtc_Word16 BWEIndex, - WebRtc_Word16 jitterIndex, - WebRtc_Word32 rate, - WebRtc_Word16* bitStream, - bool isRED) -{ - if (isRED) - { - // RED not supported with iSACFIX - return -1; - } - float scale = ACMISACFixTranscodingScale((WebRtc_UWord16)rate); - return WebRtcIsacfix_GetNewBitStream(inst, BWEIndex, scale, bitStream); -} - - -static WebRtc_Word16 -ACMISACFixGetSendBWE( - ACM_ISAC_STRUCT* inst, - WebRtc_Word16* rateIndex, - WebRtc_Word16* /* dummy */) -{ - WebRtc_Word16 localRateIndex; - WebRtc_Word16 status = WebRtcIsacfix_GetDownLinkBwIndex(inst, &localRateIndex); - if(status < 0) - { - return -1; - } - else - { - *rateIndex = localRateIndex; - return 0; - } -} - -static WebRtc_Word16 -ACMISACFixControlBWE( - ACM_ISAC_STRUCT* inst, - WebRtc_Word32 rateBPS, - WebRtc_Word16 frameSizeMs, - WebRtc_Word16 enforceFrameSize) -{ - return WebRtcIsacfix_ControlBwe(inst, (WebRtc_Word16)rateBPS, - frameSizeMs, enforceFrameSize); -} - -static WebRtc_Word16 -ACMISACFixControl( - ACM_ISAC_STRUCT* inst, - WebRtc_Word32 rateBPS, - WebRtc_Word16 frameSizeMs) -{ - return WebRtcIsacfix_Control(inst, (WebRtc_Word16)rateBPS, - frameSizeMs); -} - -static IsacSamplingRate -ACMISACFixGetEncSampRate( - ACM_ISAC_STRUCT* /* inst */) -{ - return kIsacWideband; -} - - -static IsacSamplingRate -ACMISACFixGetDecSampRate( - ACM_ISAC_STRUCT* /* inst */) -{ - return kIsacWideband; -} - -#endif - - - - - - -ACMISAC::ACMISAC( - WebRtc_Word16 codecID): -_codecInstPtr(NULL) -{ - _codecInstPtr = new ACMISACInst; - if (_codecInstPtr == NULL) - { - return; - } - _codecInstPtr->inst = NULL; - _codecID = codecID; - _enforceFrameSize = false; - // by default a 16 kHz iSAC is created. - _samplesIn10MsAudio = 160; - - // Initialize values that can be used uninitialized otherwise - _decoderParams.codecInstant.pltype = -1; - _decoderParams32kHz.codecInstant.pltype = -1; -} - - -ACMISAC::~ACMISAC() -{ - if (_codecInstPtr != NULL) - { - if(_codecInstPtr->inst != NULL) - { - ACM_ISAC_FREE(_codecInstPtr->inst); - _codecInstPtr->inst = NULL; - } - delete _codecInstPtr; - _codecInstPtr = NULL; - } - return; -} - - -ACMGenericCodec* -ACMISAC::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMISAC::InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte) -{ - // ISAC takes 10ms audio everytime we call encoder, therefor, - // it should be treated like codecs with 'basic coding block' - // non-zero, and the following 'while-loop' should not be necessary. - // However, due to a mistake in the codec the frame-size might change - // at the first 10ms pushed in to iSAC if the bit-rate is low, this is - // sort of a bug in iSAC. to address this we treat iSAC as the - // following. - - if (_codecInstPtr == NULL) - { - return -1; - } - *bitStreamLenByte = 0; - while((*bitStreamLenByte == 0) && (_inAudioIxRead < _frameLenSmpl)) - { - if(_inAudioIxRead > _inAudioIxWrite) - { - // something is wrong. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "The actual fram-size of iSAC appears to be larger that expected. All audio \ -pushed in but no bit-stream is generated."); - return -1; - } - *bitStreamLenByte = ACM_ISAC_ENCODE(_codecInstPtr->inst, - &_inAudio[_inAudioIxRead], (WebRtc_Word16*)bitstream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _samplesIn10MsAudio; - } - if(*bitStreamLenByte == 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, - "ISAC Has encoded the whole frame but no bit-stream is generated."); - } - - // a packet is generated iSAC, is set in adaptive mode may change - // the frame length and we like to update the bottleneck value as - // well, although updating bottleneck is not crucial - if((*bitStreamLenByte > 0) && (_isacCodingMode == ADAPTIVE)) - { - //_frameLenSmpl = ACM_ISAC_GETNEWFRAMELEN(_codecInstPtr->inst); - ACM_ISAC_GETSENDBITRATE(_codecInstPtr->inst, &_isacCurrentBN); - } - UpdateFrameLen(); - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMISAC::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMISAC::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - // if rate is set to -1 then iSAC has to be in adaptive mode - if(codecParams->codecInstant.rate == -1) - { - _isacCodingMode = ADAPTIVE; - } - - // sanity check that rate is in acceptable range - else if((codecParams->codecInstant.rate >= ISAC_MIN_RATE) && - (codecParams->codecInstant.rate <= ISAC_MAX_RATE)) - { - _isacCodingMode = CHANNEL_INDEPENDENT; - _isacCurrentBN = codecParams->codecInstant.rate; - } - else - { - return -1; - } - - // we need to set the encoder sampling frequency. - if(UpdateEncoderSampFreq((WebRtc_UWord16)codecParams->codecInstant.plfreq) < 0) - { - return -1; - } - if(ACM_ISAC_ENCODERINIT(_codecInstPtr->inst, _isacCodingMode) < 0) - { - return -1; - } - - // apply the frame-size and rate if operating in - // channel-independent mode - if(_isacCodingMode == CHANNEL_INDEPENDENT) - { - if(ACM_ISAC_CONTROL(_codecInstPtr->inst, - codecParams->codecInstant.rate, - codecParams->codecInstant.pacsize / - (codecParams->codecInstant.plfreq / 1000)) < 0) - { - return -1; - } - } - else - { - // We need this for adaptive case and has to be called - // after initialization - ACM_ISAC_GETSENDBITRATE( - _codecInstPtr->inst, &_isacCurrentBN); - } - _frameLenSmpl = ACM_ISAC_GETNEWFRAMELEN(_codecInstPtr->inst); - return 0; -} - -WebRtc_Word16 -ACMISAC::InternalInitDecoder( - WebRtcACMCodecParams* codecParams) -{ - if (_codecInstPtr == NULL) - { - return -1; - } - - // set decoder sampling frequency. - if(codecParams->codecInstant.plfreq == 32000) - { - UpdateDecoderSampFreq(ACMCodecDB::isacswb); - } - else - { - UpdateDecoderSampFreq(ACMCodecDB::isac); - } - - // in a one-way communication we may never register send-codec. - // However we like that the BWE to work properly so it has to - // be initialized. The BWE is initialized when iSAC encoder is initialized. - // Therefore, we need this. - if(!_encoderInitialized) - { - // Since we don't require a valid rate or a valid packet size when initializing - // the decoder, we set valid values before initializing encoder - codecParams->codecInstant.rate = ISACWB_DEFAULT_RATE; - codecParams->codecInstant.pacsize = ISACSWB_PAC_SIZE; - if(InternalInitEncoder(codecParams) < 0) - { - return -1; - } - _encoderInitialized = true; - } - - return ACM_ISAC_DECODERINIT(_codecInstPtr->inst); -} - - -WebRtc_Word16 -ACMISAC::InternalCreateDecoder() -{ - if (_codecInstPtr == NULL) - { - return -1; - } - WebRtc_Word16 status = ACM_ISAC_CREATE (&(_codecInstPtr->inst)); - - // specific to codecs with one instance for encoding and decoding - _encoderInitialized = false; - if(status < 0) - { - _encoderExist = false; - } - else - { - _encoderExist = true; - } - return status; -} - - -void -ACMISAC::DestructDecoderSafe() -{ - // codec with shared instance cannot delete. - _decoderInitialized = false; - return; -} - - -WebRtc_Word16 -ACMISAC::InternalCreateEncoder() -{ - if (_codecInstPtr == NULL) - { - return -1; - } - WebRtc_Word16 status = ACM_ISAC_CREATE(&(_codecInstPtr->inst)); - - // specific to codecs with one instance for encoding and decoding - _decoderInitialized = false; - if(status < 0) - { - _decoderExist = false; - } - else - { - _decoderExist = true; - } - return status; -} - - -void -ACMISAC::DestructEncoderSafe() -{ - // codec with shared instance cannot delete. - _encoderInitialized = false; - return; -} - - -WebRtc_Word32 -ACMISAC::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - // Sanity checks - if (_codecInstPtr == NULL) - { - return -1; - } - if (!_decoderInitialized || !_decoderExist) - { - // Todo: - // log error - return -1; - } - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_ISAC_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - if(codecInst.plfreq == 16000) - { - SET_CODEC_PAR((codecDef), kDecoderISAC, codecInst.pltype, - _codecInstPtr->inst, 16000); -#ifdef WEBRTC_CODEC_ISAC - SET_ISAC_FUNCTIONS((codecDef)); -#else - SET_ISACfix_FUNCTIONS((codecDef)); -#endif - } - else - { -#ifdef WEBRTC_CODEC_ISAC - SET_CODEC_PAR((codecDef), kDecoderISACswb, codecInst.pltype, - _codecInstPtr->inst, 32000); - SET_ISACSWB_FUNCTIONS((codecDef)); -#else - return -1; -#endif - } - - return 0; -} - - -void -ACMISAC::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - ACM_ISAC_FREE((ACM_ISAC_STRUCT *)ptrInst); - } - return; -} - -WebRtc_Word16 -ACMISAC::Transcode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_Word16 qBWE, - WebRtc_Word32 rate, - bool isRED) -{ - WebRtc_Word16 jitterInfo = 0; - // transcode from a higher rate to lower rate - // sanity check - if (_codecInstPtr == NULL) - { - return -1; - } - - *bitStreamLenByte = ACM_ISAC_GETNEWBITSTREAM(_codecInstPtr->inst, - qBWE, jitterInfo, rate, (WebRtc_Word16*)bitStream, (isRED)? 1:0); - - if(*bitStreamLenByte < 0) - { - // error happened - *bitStreamLenByte = 0; - return -1; - } - else - { - return *bitStreamLenByte; - } -} - -WebRtc_Word16 -ACMISAC::SetBitRateSafe( - WebRtc_Word32 bitRate) -{ - if (_codecInstPtr == NULL) - { - return -1; - } - WebRtc_UWord16 encoderSampFreq; - EncoderSampFreq(encoderSampFreq); - bool reinit = false; - // change the BN of iSAC - if(bitRate == -1) - { - // ADAPTIVE MODE - // Check if it was already in adaptive mode - if(_isacCodingMode != ADAPTIVE) - { - // was not in adaptive, then set the mode to adaptive - // and flag for re-initialization - _isacCodingMode = ADAPTIVE; - reinit = true; - } - } - // Sanity check if the rate valid - else if((bitRate >= ISAC_MIN_RATE) && - (bitRate <= ISAC_MAX_RATE)) - { - //check if it was in channel-independent mode before - if(_isacCodingMode != CHANNEL_INDEPENDENT) - { - // was not in channel independent, set the mode to - // channel-independent and flag for re-initialization - _isacCodingMode = CHANNEL_INDEPENDENT; - reinit = true; - } - // store the bottleneck - _isacCurrentBN = (WebRtc_UWord16)bitRate; - } - else - { - // invlaid rate - return -1; - } - - WebRtc_Word16 status = 0; - if(reinit) - { - // initialize and check if it is successful - if(ACM_ISAC_ENCODERINIT(_codecInstPtr->inst, _isacCodingMode) < 0) - { - // failed initialization - return -1; - } - } - if(_isacCodingMode == CHANNEL_INDEPENDENT) - { - - status = ACM_ISAC_CONTROL(_codecInstPtr->inst, _isacCurrentBN, - (encoderSampFreq == 32000)? 30:(_frameLenSmpl / 16)); - if(status < 0) - { - status = -1; - } - } - - // Update encoder parameters - _encoderParams.codecInstant.rate = bitRate; - - UpdateFrameLen(); - return status; -} - - -WebRtc_Word32 -ACMISAC::GetEstimatedBandwidthSafe() -{ - WebRtc_Word16 bandwidthIndex; - WebRtc_Word16 delayIndex; - IsacSamplingRate sampRate; - - // Get bandwidth information - ACM_ISAC_GETSENDBWE(_codecInstPtr->inst, &bandwidthIndex, &delayIndex); - - // Validy check of index - if ((bandwidthIndex < 0) || (bandwidthIndex > NR_ISAC_BANDWIDTHS)) - { - return -1; - } - - // Check sample frequency - sampRate = ACM_ISAC_GETDECSAMPRATE(_codecInstPtr->inst); - if(sampRate == kIsacWideband) - { - return isacRatesWB[bandwidthIndex]; - } - else - { - return isacRatesSWB[bandwidthIndex]; - } -} - -WebRtc_Word32 -ACMISAC::SetEstimatedBandwidthSafe( - WebRtc_Word32 estimatedBandwidth) -{ - IsacSamplingRate sampRate; - WebRtc_Word16 bandwidthIndex; - - // Check sample frequency and choose appropriate table - sampRate = ACM_ISAC_GETENCSAMPRATE(_codecInstPtr->inst); - - if(sampRate == kIsacWideband) - { - // Search through the WB rate table to find the index - - bandwidthIndex = NR_ISAC_BANDWIDTHS/2 - 1; - for (int i=0; i<(NR_ISAC_BANDWIDTHS/2); i++) - { - if (estimatedBandwidth == isacRatesWB[i]) - { - bandwidthIndex = i; - break; - } else if (estimatedBandwidth == isacRatesWB[i+NR_ISAC_BANDWIDTHS/2]) - { - bandwidthIndex = i + NR_ISAC_BANDWIDTHS/2; - break; - } else if (estimatedBandwidth < isacRatesWB[i]) - { - bandwidthIndex = i; - break; - } - } - } - else - { - // Search through the SWB rate table to find the index - bandwidthIndex = NR_ISAC_BANDWIDTHS - 1; - for (int i=0; iinst, bandwidthIndex); - - return 0; -} - -WebRtc_Word32 -ACMISAC::GetRedPayloadSafe( -#if (!defined(WEBRTC_CODEC_ISAC)) - WebRtc_UWord8* /* redPayload */, - WebRtc_Word16* /* payloadBytes */) -{ - return -1; -#else - WebRtc_UWord8* redPayload, - WebRtc_Word16* payloadBytes) -{ - - WebRtc_Word16 bytes = WebRtcIsac_GetRedPayload(_codecInstPtr->inst, (WebRtc_Word16*)redPayload); - if (bytes < 0) - { - return -1; - } - *payloadBytes = bytes; - return 0; -#endif -} - - -WebRtc_Word16 -ACMISAC::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType == _decoderParams.codecInstant.pltype) - { - return netEq->RemoveCodec(kDecoderISAC); - } - else if(payloadType == _decoderParams32kHz.codecInstant.pltype) - { - return netEq->RemoveCodec(kDecoderISACswb); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type %d or %d", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype, - _decoderParams32kHz.codecInstant.pltype); - - return -1; - } -} - - -WebRtc_Word16 -ACMISAC::UpdateDecoderSampFreq( -#ifdef WEBRTC_CODEC_ISAC - WebRtc_Word16 codecId) -{ - if(ACMCodecDB::isac == codecId) - { - return WebRtcIsac_SetDecSampRate(_codecInstPtr->inst, kIsacWideband); - } - else if(ACMCodecDB::isacswb == codecId) - { - return WebRtcIsac_SetDecSampRate(_codecInstPtr->inst, kIsacSuperWideband); - } - else - { - return -1; - } - -#else - WebRtc_Word16 /* codecId */) -{ - return 0; -#endif -} - - -WebRtc_Word16 -ACMISAC::UpdateEncoderSampFreq( -#ifdef WEBRTC_CODEC_ISAC - WebRtc_UWord16 encoderSampFreqHz) -{ - WebRtc_UWord16 currentSampRateHz; - EncoderSampFreq(currentSampRateHz); - - if(currentSampRateHz != encoderSampFreqHz) - { - if((encoderSampFreqHz != 16000) && (encoderSampFreqHz != 32000)) - { - return -1; - } - else - { - _inAudioIxRead = 0; - _inAudioIxWrite = 0; - _inTimestampIxWrite = 0; - if(encoderSampFreqHz == 16000) - { - if(WebRtcIsac_SetEncSampRate(_codecInstPtr->inst, kIsacWideband) < 0) - { - return -1; - } - _samplesIn10MsAudio = 160; - } - else - { - - if(WebRtcIsac_SetEncSampRate(_codecInstPtr->inst, kIsacSuperWideband) < 0) - { - return -1; - } - _samplesIn10MsAudio = 320; - } - _frameLenSmpl = ACM_ISAC_GETNEWFRAMELEN(_codecInstPtr->inst); - _encoderParams.codecInstant.pacsize = _frameLenSmpl; - _encoderParams.codecInstant.plfreq = encoderSampFreqHz; - return 0; - } - } -#else - WebRtc_UWord16 /* codecId */) -{ -#endif - return 0; -} - -WebRtc_Word16 -ACMISAC::EncoderSampFreq( - WebRtc_UWord16& sampFreqHz) -{ - IsacSamplingRate sampRate; - sampRate = ACM_ISAC_GETENCSAMPRATE(_codecInstPtr->inst); - if(sampRate == kIsacSuperWideband) - { - sampFreqHz = 32000; - } - else - { - sampFreqHz = 16000; - } - return 0; -} - -WebRtc_Word32 -ACMISAC::ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize) -{ - WebRtc_Word16 status; - { - WebRtc_UWord16 sampFreqHz; - EncoderSampFreq(sampFreqHz); - // @TODO: at 32kHz we hardcode calling with 30ms and enforce - // the frame-size otherwise we might get error. Revise if - // control-bwe is changed. - if(sampFreqHz == 32000) - { - status = ACM_ISAC_CONTROL_BWE(_codecInstPtr->inst, - initRateBitPerSec, 30, 1); - } - else - { - status = ACM_ISAC_CONTROL_BWE(_codecInstPtr->inst, - initRateBitPerSec, initFrameSizeMsec, enforceFrameSize? 1:0); - } - } - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Coutn't config iSAC BWE."); - return -1; - } - UpdateFrameLen(); - ACM_ISAC_GETSENDBITRATE(_codecInstPtr->inst, &_isacCurrentBN); - return 0; -} - -WebRtc_Word32 -ACMISAC::SetISACMaxPayloadSize( - const WebRtc_UWord16 maxPayloadLenBytes) -{ - return ACM_ISAC_SETMAXPAYLOADSIZE(_codecInstPtr->inst, maxPayloadLenBytes); -} - -WebRtc_Word32 -ACMISAC::SetISACMaxRate( - const WebRtc_UWord32 maxRateBitPerSec) -{ - return ACM_ISAC_SETMAXRATE(_codecInstPtr->inst, maxRateBitPerSec); -} - - -void -ACMISAC::UpdateFrameLen() -{ - _frameLenSmpl = ACM_ISAC_GETNEWFRAMELEN(_codecInstPtr->inst); - _encoderParams.codecInstant.pacsize = _frameLenSmpl; -} - -void -ACMISAC::CurrentRate(WebRtc_Word32& rateBitPerSec) -{ - if(_isacCodingMode == ADAPTIVE) - { - ACM_ISAC_GETSENDBITRATE(_codecInstPtr->inst, &rateBitPerSec); - } -} - - -bool -ACMISAC::DecoderParamsSafe( - WebRtcACMCodecParams* decParams, - const WebRtc_UWord8 payloadType) -{ - if(_decoderInitialized) - { - if(payloadType == _decoderParams.codecInstant.pltype) - { - memcpy(decParams, &_decoderParams, sizeof(WebRtcACMCodecParams)); - return true; - } - if(payloadType == _decoderParams32kHz.codecInstant.pltype) - { - memcpy(decParams, &_decoderParams32kHz, - sizeof(WebRtcACMCodecParams)); - return true; - } - } - return false; -} - -void -ACMISAC::SaveDecoderParamSafe( - const WebRtcACMCodecParams* codecParams) -{ - // set decoder sampling frequency. - if(codecParams->codecInstant.plfreq == 32000) - { - memcpy(&_decoderParams32kHz, codecParams, sizeof(WebRtcACMCodecParams)); - } - else - { - memcpy(&_decoderParams, codecParams, sizeof(WebRtcACMCodecParams)); - } -} - - -WebRtc_Word16 -ACMISAC::REDPayloadISAC( - const WebRtc_Word32 isacRate, - const WebRtc_Word16 isacBwEstimate, - WebRtc_UWord8* payload, - WebRtc_Word16* payloadLenBytes) -{ - WebRtc_Word16 status; - ReadLockScoped rl(_codecWrapperLock); - status = Transcode(payload, payloadLenBytes, isacBwEstimate, isacRate, true); - return status; -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_isac.h b/modules/audio_coding/main/source/acm_isac.h deleted file mode 100644 index 681c1a4f6..000000000 --- a/modules/audio_coding/main/source/acm_isac.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_ISAC_H -#define ACM_ISAC_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -struct ACMISACInst; - -enum iSACCodingMode {ADAPTIVE, CHANNEL_INDEPENDENT}; - - -class ACMISAC : public ACMGenericCodec -{ -public: - ACMISAC(WebRtc_Word16 codecID); - ~ACMISAC(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 DeliverCachedIsacData( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_UWord32* timestamp, - WebRtcACMEncodingType* encodingType, - const WebRtc_UWord16 isacRate, - const WebRtc_UWord8 isacBWestimate); - - WebRtc_Word16 DeliverCachedData( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */, - WebRtc_UWord32* /* timestamp */, - WebRtcACMEncodingType* /* encodingType */) - { - return -1; - } - - WebRtc_Word16 UpdateDecoderSampFreq( - WebRtc_Word16 codecId); - - WebRtc_Word16 UpdateEncoderSampFreq( - WebRtc_UWord16 sampFreqHz); - - WebRtc_Word16 EncoderSampFreq( - WebRtc_UWord16& sampFreqHz); - - WebRtc_Word32 ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize); - - WebRtc_Word32 SetISACMaxPayloadSize( - const WebRtc_UWord16 maxPayloadLenBytes); - - WebRtc_Word32 SetISACMaxRate( - const WebRtc_UWord32 maxRateBitPerSec); - - WebRtc_Word16 REDPayloadISAC( - const WebRtc_Word32 isacRate, - const WebRtc_Word16 isacBwEstimate, - WebRtc_UWord8* payload, - WebRtc_Word16* payloadLenBytes); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 bitRate); - - WebRtc_Word32 GetEstimatedBandwidthSafe(); - - WebRtc_Word32 SetEstimatedBandwidthSafe(WebRtc_Word32 estimatedBandwidth); - - WebRtc_Word32 GetRedPayloadSafe( - WebRtc_UWord8* redPayload, - WebRtc_Word16* payloadBytes); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 Transcode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte, - WebRtc_Word16 qBWE, - WebRtc_Word32 rate, - bool isRED); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void CurrentRate(WebRtc_Word32& rateBitPerSec); - - void UpdateFrameLen(); - - bool DecoderParamsSafe( - WebRtcACMCodecParams *decParams, - const WebRtc_UWord8 payloadType); - - void SaveDecoderParamSafe( - const WebRtcACMCodecParams* codecParams); - - ACMISACInst* _codecInstPtr; - - bool _isEncInitialized; - iSACCodingMode _isacCodingMode; - bool _enforceFrameSize; - WebRtc_Word32 _isacCurrentBN; - WebRtc_UWord16 _samplesIn10MsAudio; - WebRtcACMCodecParams _decoderParams32kHz; -}; - -} //namespace - -#endif // ACM_ISAC_H diff --git a/modules/audio_coding/main/source/acm_isac_macros.h b/modules/audio_coding/main/source/acm_isac_macros.h deleted file mode 100644 index 030164c8b..000000000 --- a/modules/audio_coding/main/source/acm_isac_macros.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_ISAC_MACROS_H -#define ACM_ISAC_MACROS_H - -#include "engine_configurations.h" - -namespace webrtc -{ - -#ifdef WEBRTC_CODEC_ISAC -# define ACM_ISAC_CREATE WebRtcIsac_Create -# define ACM_ISAC_FREE WebRtcIsac_Free -# define ACM_ISAC_ENCODERINIT WebRtcIsac_EncoderInit -# define ACM_ISAC_ENCODE WebRtcIsac_Encode -# define ACM_ISAC_DECODERINIT WebRtcIsac_DecoderInit -# define ACM_ISAC_DECODE_BWE WebRtcIsac_UpdateBwEstimate -# define ACM_ISAC_DECODE_B WebRtcIsac_Decode -# define ACM_ISAC_DECODEPLC WebRtcIsac_DecodePlc -# define ACM_ISAC_CONTROL WebRtcIsac_Control -# define ACM_ISAC_CONTROL_BWE WebRtcIsac_ControlBwe -# define ACM_ISAC_GETFRAMELEN WebRtcIsac_ReadFrameLen -# define ACM_ISAC_VERSION WebRtcIsac_version -# define ACM_ISAC_GETERRORCODE WebRtcIsac_GetErrorCode -# define ACM_ISAC_GETSENDBITRATE WebRtcIsac_GetUplinkBw -# define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsac_SetMaxPayloadSize -# define ACM_ISAC_SETMAXRATE WebRtcIsac_SetMaxRate -# define ACM_ISAC_GETNEWBITSTREAM WebRtcIsac_GetNewBitStream -# define ACM_ISAC_GETSENDBWE WebRtcIsac_GetDownLinkBwIndex -# define ACM_ISAC_SETBWE WebRtcIsac_UpdateUplinkBw -# define ACM_ISAC_GETBWE WebRtcIsac_ReadBwIndex -# define ACM_ISAC_GETNEWFRAMELEN WebRtcIsac_GetNewFrameLen -# define ACM_ISAC_STRUCT ISACStruct -# define ACM_ISAC_GETENCSAMPRATE WebRtcIsac_EncSampRate -# define ACM_ISAC_GETDECSAMPRATE WebRtcIsac_DecSampRate -#endif - -#ifdef WEBRTC_CODEC_ISACFX -# define ACM_ISAC_CREATE WebRtcIsacfix_Create -# define ACM_ISAC_FREE WebRtcIsacfix_Free -# define ACM_ISAC_ENCODERINIT WebRtcIsacfix_EncoderInit -# define ACM_ISAC_ENCODE WebRtcIsacfix_Encode -# define ACM_ISAC_DECODERINIT WebRtcIsacfix_DecoderInit -# define ACM_ISAC_DECODE_BWE WebRtcIsacfix_UpdateBwEstimate -# define ACM_ISAC_DECODE_B WebRtcIsacfix_Decode -# define ACM_ISAC_DECODEPLC WebRtcIsacfix_DecodePlc -# define ACM_ISAC_CONTROL ACMISACFixControl // local Impl -# define ACM_ISAC_CONTROL_BWE ACMISACFixControlBWE // local Impl -# define ACM_ISAC_GETFRAMELEN WebRtcIsacfix_ReadFrameLen -# define ACM_ISAC_VERSION WebRtcIsacfix_version -# define ACM_ISAC_GETERRORCODE WebRtcIsacfix_GetErrorCode -# define ACM_ISAC_GETSENDBITRATE ACMISACFixGetSendBitrate // local Impl -# define ACM_ISAC_SETMAXPAYLOADSIZE WebRtcIsacfix_SetMaxPayloadSize -# define ACM_ISAC_SETMAXRATE WebRtcIsacfix_SetMaxRate -# define ACM_ISAC_GETNEWBITSTREAM ACMISACFixGetNewBitstream // local Impl -# define ACM_ISAC_GETSENDBWE ACMISACFixGetSendBWE // local Impl -# define ACM_ISAC_SETBWE WebRtcIsacfix_UpdateUplinkBw -# define ACM_ISAC_GETBWE WebRtcIsacfix_ReadBwIndex -# define ACM_ISAC_GETNEWFRAMELEN WebRtcIsacfix_GetNewFrameLen -# define ACM_ISAC_STRUCT ISACFIX_MainStruct -# define ACM_ISAC_GETENCSAMPRATE ACMISACFixGetEncSampRate // local Impl -# define ACM_ISAC_GETDECSAMPRATE ACMISACFixGetDecSampRate // local Impl -#endif - -} //namespace - -#endif // ACM_ISAC_MACROS_H - diff --git a/modules/audio_coding/main/source/acm_neteq.cc b/modules/audio_coding/main/source/acm_neteq.cc deleted file mode 100644 index d829db19b..000000000 --- a/modules/audio_coding/main/source/acm_neteq.cc +++ /dev/null @@ -1,1370 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// 'conversion' conversion from 'type1' to 'type2', possible loss of data -#pragma warning(disable: 4267) - -#include // malloc - -#include "acm_neteq.h" -#include "common_types.h" -#include "critical_section_wrapper.h" -#include "rw_lock_wrapper.h" -#include "signal_processing_library.h" -#include "tick_util.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_internal.h" - -namespace webrtc -{ - -#define RTP_HEADER_SIZE 12 -#define NETEQ_INIT_FREQ 8000 -#define NETEQ_INIT_FREQ_KHZ (NETEQ_INIT_FREQ/1000) -#define NETEQ_ERR_MSG_LEN_BYTE (WEBRTC_NETEQ_MAX_ERROR_NAME + 1) - - -ACMNetEQ::ACMNetEQ() -: -_id(0), -_currentSampFreqKHz(NETEQ_INIT_FREQ_KHZ), -_avtPlayout(false), -_playoutMode(voice), -_netEqCritSect(CriticalSectionWrapper::CreateCriticalSection()), -_vadStatus(false), -_vadMode(VADNormal), -_decodeLock(RWLockWrapper::CreateRWLock()), -_numSlaves(0), -_receivedStereo(false), -_masterSlaveInfo(NULL), -_previousAudioActivity(AudioFrame::kVadUnknown), -_callbackCritSect(CriticalSectionWrapper::CreateCriticalSection()) -{ - for(int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) - { - _isInitialized[n] = false; - _ptrVADInst[n] = NULL; - _inst[n] = NULL; - _instMem[n] = NULL; - _netEqPacketBuffer[n] = NULL; - } -} - -ACMNetEQ::~ACMNetEQ() -{ - { - CriticalSectionScoped lock(*_netEqCritSect); - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if (_instMem[idx] != NULL) - { - free(_instMem[idx]); - _instMem[idx] = NULL; - } - if (_netEqPacketBuffer[idx] != NULL) - { - free(_netEqPacketBuffer[idx]); - _netEqPacketBuffer[idx] = NULL; - } - if(_ptrVADInst[idx] != NULL) - { - WebRtcVad_Free(_ptrVADInst[idx]); - _ptrVADInst[idx] = NULL; - } - } - if(_masterSlaveInfo != NULL) - { - free(_masterSlaveInfo); - _masterSlaveInfo = NULL; - } - } - if(_netEqCritSect != NULL) - { - delete _netEqCritSect; - } - - if(_decodeLock != NULL) - { - delete _decodeLock; - } - - if(_callbackCritSect != NULL) - { - delete _callbackCritSect; - } -} - -WebRtc_Word32 -ACMNetEQ::Init() -{ - CriticalSectionScoped lock(*_netEqCritSect); - - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(InitByIdxSafe(idx) < 0) - { - return -1; - } - // delete VAD instance and start fresh if required. - if(_ptrVADInst[idx] != NULL) - { - WebRtcVad_Free(_ptrVADInst[idx]); - _ptrVADInst[idx] = NULL; - } - if(_vadStatus) - { - // Has to enable VAD - if(EnableVADByIdxSafe(idx) < 0) - { - // Failed to enable VAD. - // Delete VAD instance, if it is created - if(_ptrVADInst[idx] != NULL) - { - WebRtcVad_Free(_ptrVADInst[idx]); - _ptrVADInst[idx] = NULL; - } - // We are at initialization of NetEq, if failed to - // enable VAD, we delete the NetEq instance. - if (_instMem[idx] != NULL) { - free(_instMem[idx]); - _instMem[idx] = NULL; - _inst[idx] = NULL; - } - _isInitialized[idx] = false; - return -1; - } - } - _isInitialized[idx] = true; - } - return 0; -} - -WebRtc_Word16 -ACMNetEQ::InitByIdxSafe( - const WebRtc_Word16 idx) -{ - int memorySizeBytes; - if (WebRtcNetEQ_AssignSize(&memorySizeBytes) != 0) - { - LogError("AssignSize", idx); - return -1; - } - - if(_instMem[idx] != NULL) - { - free(_instMem[idx]); - _instMem[idx] = NULL; - } - _instMem[idx] = malloc(memorySizeBytes); - if (_instMem[idx] == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "InitByIdxSafe: NetEq Initialization error: could not allocate memory for NetEq"); - _isInitialized[idx] = false; - return -1; - } - if (WebRtcNetEQ_Assign(&_inst[idx], _instMem[idx]) != 0) - { - if (_instMem[idx] != NULL) { - free(_instMem[idx]); - _instMem[idx] = NULL; - } - LogError("Assign", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "InitByIdxSafe: NetEq Initialization error: could not Assign"); - _isInitialized[idx] = false; - return -1; - } - if (WebRtcNetEQ_Init(_inst[idx], NETEQ_INIT_FREQ) != 0) - { - if (_instMem[idx] != NULL) { - free(_instMem[idx]); - _instMem[idx] = NULL; - } - LogError("Init", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "InitByIdxSafe: NetEq Initialization error: could not initialize NetEq"); - _isInitialized[idx] = false; - return -1; - } - _isInitialized[idx] = true; - return 0; -} - -WebRtc_Word16 -ACMNetEQ::EnableVADByIdxSafe( - const WebRtc_Word16 idx) -{ - if(_ptrVADInst[idx] == NULL) - { - if(WebRtcVad_Create(&_ptrVADInst[idx]) < 0) - { - _ptrVADInst[idx] = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "EnableVADByIdxSafe: NetEq Initialization error: could not create VAD"); - return -1; - } - } - - if(WebRtcNetEQ_SetVADInstance(_inst[idx], _ptrVADInst[idx], - (WebRtcNetEQ_VADInitFunction) WebRtcVad_Init, - (WebRtcNetEQ_VADSetmodeFunction) WebRtcVad_set_mode, - (WebRtcNetEQ_VADFunction) WebRtcVad_Process) < 0) - { - LogError("setVADinstance", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "EnableVADByIdxSafe: NetEq Initialization error: could not set VAD instance"); - return -1; - } - - if(WebRtcNetEQ_SetVADMode(_inst[idx], _vadMode) < 0) - { - LogError("setVADmode", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "EnableVADByIdxSafe: NetEq Initialization error: could not set VAD mode"); - return -1; - } - return 0; -} - - - - -WebRtc_Word32 -ACMNetEQ::AllocatePacketBuffer( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs) -{ - // Due to WebRtcNetEQ_GetRecommendedBufferSize - // the following has to be int otherwise we will have compiler error - // if not casted - - CriticalSectionScoped lock(*_netEqCritSect); - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(AllocatePacketBufferByIdxSafe(usedCodecs, noOfCodecs, idx) < 0) - { - return -1; - } - } - return 0; -} - -WebRtc_Word16 -ACMNetEQ::AllocatePacketBufferByIdxSafe( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs, - const WebRtc_Word16 idx) -{ - int maxNoPackets; - int bufferSizeInBytes; - - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AllocatePacketBufferByIdxSafe: NetEq is not initialized."); - return -1; - } - if (WebRtcNetEQ_GetRecommendedBufferSize(_inst[idx], usedCodecs, noOfCodecs, - kTCPLargeJitter , &maxNoPackets, &bufferSizeInBytes) - != 0) - { - LogError("GetRecommendedBufferSize", idx); - return -1; - } - if(_netEqPacketBuffer[idx] != NULL) - { - free(_netEqPacketBuffer[idx]); - _netEqPacketBuffer[idx] = NULL; - } - - _netEqPacketBuffer[idx] = (WebRtc_Word16 *)malloc(bufferSizeInBytes); - if (_netEqPacketBuffer[idx] == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AllocatePacketBufferByIdxSafe: NetEq Initialization error: could not allocate " - "memory for NetEq Packet Buffer"); - return -1; - - } - if (WebRtcNetEQ_AssignBuffer(_inst[idx], maxNoPackets, _netEqPacketBuffer[idx], - bufferSizeInBytes) != 0) - { - if (_netEqPacketBuffer[idx] != NULL) { - free(_netEqPacketBuffer[idx]); - _netEqPacketBuffer[idx] = NULL; - } - LogError("AssignBuffer", idx); - return -1; - } - return 0; -} - - - - -WebRtc_Word32 -ACMNetEQ::SetExtraDelay( - const WebRtc_Word32 delayInMS) -{ - CriticalSectionScoped lock(*_netEqCritSect); - - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetExtraDelay: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_SetExtraDelay(_inst[idx], delayInMS) < 0) - { - LogError("SetExtraDelay", idx); - return -1; - } - } - return 0; -} - - -WebRtc_Word32 -ACMNetEQ::SetAVTPlayout( - const bool enable) -{ - CriticalSectionScoped lock(*_netEqCritSect); - if (_avtPlayout != enable) - { - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetAVTPlayout: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_SetAVTPlayout(_inst[idx], (enable) ? 1 : 0) < 0) - { - LogError("SetAVTPlayout", idx); - return -1; - } - } - } - _avtPlayout = enable; - return 0; -} - - -bool -ACMNetEQ::AVTPlayout() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - return _avtPlayout; -} - -WebRtc_Word32 -ACMNetEQ::CurrentSampFreqHz() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "CurrentSampFreqHz: NetEq is not initialized."); - return -1; - } - return (WebRtc_Word32)(1000*_currentSampFreqKHz); -} - - -WebRtc_Word32 -ACMNetEQ::SetPlayoutMode( - const AudioPlayoutMode mode) -{ - CriticalSectionScoped lock(*_netEqCritSect); - if(_playoutMode != mode) - { - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetPlayoutMode: NetEq is not initialized."); - return -1; - } - - enum WebRtcNetEQPlayoutMode playoutMode; - switch(mode) - { - case voice: - playoutMode = kPlayoutOn; - break; - case fax: - playoutMode = kPlayoutFax; - break; - case streaming: - playoutMode = kPlayoutStreaming; - break; - default: - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetPlayoutMode: NetEq Error playout mode not recognized"); - return -1; - break; - } - if(WebRtcNetEQ_SetPlayoutMode(_inst[idx], playoutMode) < 0) - { - LogError("SetPlayoutMode", idx); - return -1; - } - } - _playoutMode = mode; - } - - return 0; -} - -AudioPlayoutMode -ACMNetEQ::PlayoutMode() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - return _playoutMode; -} - - -WebRtc_Word32 -ACMNetEQ::NetworkStatistics( - ACMNetworkStatistics* statistics) const -{ - WebRtcNetEQ_NetworkStatistics stats; - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "NetworkStatistics: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_GetNetworkStatistics(_inst[0], &stats) == 0) - { - statistics->currentAccelerateRate = stats.currentAccelerateRate; - statistics->currentBufferSize = stats.currentBufferSize; - statistics->currentDiscardRate = stats.currentDiscardRate; - statistics->currentExpandRate = stats.currentExpandRate; - statistics->currentPacketLossRate = stats.currentPacketLossRate; - statistics->currentPreemptiveRate = stats.currentPreemptiveRate; - statistics->preferredBufferSize = stats.preferredBufferSize; - return 0; - } - else - { - LogError("getNetworkStatistics", 0); - return -1; - } -} - - -WebRtc_Word32 -ACMNetEQ::JitterStatistics( - ACMJitterStatistics* jitterStatistics) const -{ - WebRtcNetEQ_JitterStatistics stats; - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "JitterStatistics: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_GetJitterStatistics(_inst[0], &stats) == 0) - { - jitterStatistics->accelerateMs = stats.accelerateMs; - jitterStatistics->avgPacketDelayMs = stats.avgPacketDelayMs; - jitterStatistics->numExpandTiny = stats.countExpandMoreThan120ms; - jitterStatistics->numExpandLong = stats.countExpandMoreThan2000ms; - jitterStatistics->numExpandSmall = stats.countExpandMoreThan250ms; - jitterStatistics->numExpandMedium = stats.countExpandMoreThan500ms; - jitterStatistics->countIAT1000ms = stats.countIAT1000ms; - jitterStatistics->countIAT2000ms = stats.countIAT2000ms; - jitterStatistics->countIAT500ms = stats.countIAT500ms; - jitterStatistics->flushedMs = stats.flushedMs; - jitterStatistics->generatedSilentMs = stats.generatedSilentMs; - jitterStatistics->interpolatedSilentMs = stats.interpolatedSilentMs; - jitterStatistics->interpolatedVoiceMs = stats.interpolatedVoiceMs; - jitterStatistics->jbAvgSize = stats.jbAvgSize; - jitterStatistics->jbChangeCount = stats.jbChangeCount; - jitterStatistics->jbMaxSize = stats.jbMaxSize; - jitterStatistics->jbMinSize = stats.jbMinSize; - jitterStatistics->lateLossMs = stats.lateLossMs; - jitterStatistics->longestExpandDurationMs = stats.longestExpandDurationMs; - jitterStatistics->longestIATms = stats.longestIATms; - jitterStatistics->maxPacketDelayMs = stats.maxPacketDelayMs; - jitterStatistics->minPacketDelayMs = stats.minPacketDelayMs; - return 0; - } - else - { - LogError("getJitterStatistics", 0); - return -1; - } -} - -WebRtc_Word32 -ACMNetEQ::PreferredBufferSize( - WebRtc_UWord16* prefBufSize) const -{ - CriticalSectionScoped lock(*_netEqCritSect); - WebRtc_Word32 ok = WebRtcNetEQ_GetPreferredBufferSize(_inst[0], prefBufSize); - if((*prefBufSize == 0) || (*prefBufSize == 0xFFFF)) - { - ok = -1; - LogError("getPreferredBufferSize", 0); - } - return ok; -} - -WebRtc_Word32 -ACMNetEQ::ResetJitterStatistics() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - if(WebRtcNetEQ_ResetJitterStatistics(_inst[0]) < 0) - { - LogError("resetJitterStatistics", 0); - return -1; - } - else - { - return 0; - } -} - -WebRtc_Word32 -ACMNetEQ::RecIn( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtcRTPHeader& rtpInfo) -{ - // translate to NetEq struct - WebRtcNetEQ_RTPInfo netEqRTPInfo; - netEqRTPInfo.payloadType = rtpInfo.header.payloadType; - netEqRTPInfo.sequenceNumber = rtpInfo.header.sequenceNumber; - netEqRTPInfo.timeStamp = rtpInfo.header.timestamp; - netEqRTPInfo.SSRC = rtpInfo.header.ssrc; - netEqRTPInfo.markerBit = rtpInfo.header.markerBit; - - CriticalSectionScoped lock(*_netEqCritSect); - // Down-cast the time to (32-6)-bit since we only care about - // the least significant bits. (32-6) bits cover 2^(32-6) = 67108864 ms. - // we masked 6 most significant bits of 32-bit so we don't loose resolution - // when do the following multiplication. - const WebRtc_UWord32 nowInMs = static_cast( - TickTime::MillisecondTimestamp() & 0x03ffffff); - WebRtc_UWord32 recvTimestamp = static_cast - (_currentSampFreqKHz * nowInMs); - - int status; - - if(rtpInfo.type.Audio.channel == 1) - { - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecIn: NetEq is not initialized."); - return -1; - } - // PUSH into Master - status = WebRtcNetEQ_RecInRTPStruct(_inst[0], &netEqRTPInfo, - (WebRtc_UWord8 *)incomingPayload, (WebRtc_Word16)payloadLength, - recvTimestamp); - if(status < 0) - { - LogError("RecInRTPStruct", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecIn: NetEq, error in pushing in Master"); - return -1; - } - } - else if(rtpInfo.type.Audio.channel == 2) - { - if(!_isInitialized[1]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecIn: NetEq is not initialized."); - return -1; - } - // PUSH into Slave - status = WebRtcNetEQ_RecInRTPStruct(_inst[1], &netEqRTPInfo, - (WebRtc_UWord8 *)incomingPayload, (WebRtc_Word16)payloadLength, - recvTimestamp); - if(status < 0) - { - LogError("RecInRTPStruct", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecIn: NetEq, error in pushing in Slave"); - return -1; - } - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecIn: NetEq, error invalid numbe of channels %d \ -(1, for Master stream, and 2, for slave stream, are valid values)", - rtpInfo.type.Audio.channel); - return -1; - } - - return 0; -} - -WebRtc_Word32 -ACMNetEQ::RecOut( - AudioFrame& audioFrame) -{ - enum WebRtcNetEQOutputType type; - WebRtc_Word16 payloadLenSample; - enum WebRtcNetEQOutputType typeMaster; - enum WebRtcNetEQOutputType typeSlave; - - WebRtc_Word16 payloadLenSampleSlave; - - CriticalSectionScoped lockNetEq(*_netEqCritSect); - - if(!_receivedStereo) - { - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq is not initialized."); - return -1; - } - { - WriteLockScoped lockCodec(*_decodeLock); - if(WebRtcNetEQ_RecOut(_inst[0], &(audioFrame._payloadData[0]), - &payloadLenSample) != 0) - { - LogError("RecOut", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq, error in pulling out for mono case"); - - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int errorCode = WebRtcNetEQ_GetErrorCode(_inst[0]); - if(errorCode != 2003) - { - // Cannot recover; return an error - return -1; - } - } - } - WebRtcNetEQ_GetSpeechOutputType(_inst[0], &type); - audioFrame._audioChannel = 1; - } - else - { - if(!_isInitialized[0] || !_isInitialized[1]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq is not initialized."); - return -1; - } - WebRtc_Word16 payloadMaster[480]; - WebRtc_Word16 payloadSlave[480]; - { - WriteLockScoped lockCodec(*_decodeLock); - if(WebRtcNetEQ_RecOutMasterSlave(_inst[0], payloadMaster, - &payloadLenSample, _masterSlaveInfo, 1) != 0) - { - LogError("RecOutMasterSlave", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq, error in pulling out for master"); - - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int errorCode = WebRtcNetEQ_GetErrorCode(_inst[0]); - if(errorCode != 2003) - { - // Cannot recover; return an error - return -1; - } - } - if(WebRtcNetEQ_RecOutMasterSlave(_inst[1], payloadSlave, - &payloadLenSampleSlave, _masterSlaveInfo, 0) != 0) - { - LogError("RecOutMasterSlave", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq, error in pulling out for slave"); - - // Check for errors that can be recovered from: - // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 - int errorCode = WebRtcNetEQ_GetErrorCode(_inst[0]); - if(errorCode != 2003) - { - // Cannot recover; return an error - return -1; - } - } - } - if(payloadLenSample != payloadLenSampleSlave) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, - "RecOut: mismatch between the lenght of the decoded \ -audio by Master (%d samples) and Slave (%d samples).", - payloadLenSample, payloadLenSampleSlave); - if(payloadLenSample > payloadLenSampleSlave) - { - memset(&payloadSlave[payloadLenSampleSlave], 0, - (payloadLenSample - payloadLenSampleSlave) * sizeof(WebRtc_Word16)); - } - } - - for(WebRtc_Word16 n = 0; n < payloadLenSample; n++) - { - audioFrame._payloadData[n<<1] = payloadMaster[n]; - audioFrame._payloadData[(n<<1)+1] = payloadSlave[n]; - } - audioFrame._audioChannel = 2; - - WebRtcNetEQ_GetSpeechOutputType(_inst[0], &typeMaster); - WebRtcNetEQ_GetSpeechOutputType(_inst[1], &typeSlave); - if((typeMaster == kOutputNormal) || - (typeSlave == kOutputNormal)) - { - type = kOutputNormal; - } - else - { - type = typeMaster; - } - } - - audioFrame._payloadDataLengthInSamples = static_cast(payloadLenSample); - // NetEq always returns 10 ms of audio. - _currentSampFreqKHz = static_cast(audioFrame._payloadDataLengthInSamples) / 10.0f; - audioFrame._frequencyInHz = audioFrame._payloadDataLengthInSamples * 100; - if(_vadStatus) - { - if(type == kOutputVADPassive) - { - audioFrame._vadActivity = AudioFrame::kVadPassive; - audioFrame._speechType = AudioFrame::kNormalSpeech; - } - else if(type == kOutputNormal) - { - audioFrame._vadActivity = AudioFrame::kVadActive; - audioFrame._speechType = AudioFrame::kNormalSpeech; - } - else if(type == kOutputPLC) - { - audioFrame._vadActivity = _previousAudioActivity; - audioFrame._speechType = AudioFrame::kPLC; - } - else if(type == kOutputCNG) - { - audioFrame._vadActivity = AudioFrame::kVadPassive; - audioFrame._speechType = AudioFrame::kCNG; - } - else - { - audioFrame._vadActivity = AudioFrame::kVadPassive; - audioFrame._speechType = AudioFrame::kPLCCNG; - } - } - else - { - // Always return kVadUnknown when receive VAD is inactive - audioFrame._vadActivity = AudioFrame::kVadUnknown; - - if(type == kOutputNormal) - { - audioFrame._speechType = AudioFrame::kNormalSpeech; - } - else if(type == kOutputPLC) - { - audioFrame._speechType = AudioFrame::kPLC; - } - else if(type == kOutputPLCtoCNG) - { - audioFrame._speechType = AudioFrame::kPLCCNG; - } - else if(type == kOutputCNG) - { - audioFrame._speechType = AudioFrame::kCNG; - } - else - { - // type is kOutputVADPassive which - // we don't expect to get if _vadStatus is false - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, - "RecOut: NetEq returned kVadPassive while _vadStatus is false."); - audioFrame._vadActivity = AudioFrame::kVadUnknown; - audioFrame._speechType = AudioFrame::kNormalSpeech; - } - } - _previousAudioActivity = audioFrame._vadActivity; - - return 0; -} - -// When ACMGenericCodec has set the codec specific parameters in codecDef -// it calls AddCodec() to add the new codec to the NetEQ database. -WebRtc_Word32 -ACMNetEQ::AddCodec( - WebRtcNetEQ_CodecDef* codecDef, - bool toMaster) -{ - if (codecDef == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "ACMNetEQ::AddCodec: error, codecDef is NULL"); - return -1; - } - CriticalSectionScoped lock(*_netEqCritSect); - - WebRtc_Word16 idx; - if(toMaster) - { - idx = 0; - } - else - { - idx = 1; - } - - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "ACMNetEQ::AddCodec: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_CodecDbAdd(_inst[idx], codecDef) < 0) - { - LogError("CodecDB_Add", idx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "ACMNetEQ::AddCodec: NetEq, error in adding codec"); - return -1; - } - else - { - return 0; - } -} - -// Creates a Word16 RTP packet out of a Word8 payload and an rtp info struct. -// Must be byte order safe. -void -ACMNetEQ::RTPPack( - WebRtc_Word16* rtpPacket, - const WebRtc_Word8* payload, - const WebRtc_Word32 payloadLengthW8, - const WebRtcRTPHeader& rtpInfo) -{ - WebRtc_Word32 idx = 0; - WEBRTC_SPL_SET_BYTE(rtpPacket, (WebRtc_Word8)0x80, idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, rtpInfo.header.payloadType, idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.sequenceNumber), 1), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.sequenceNumber), 0), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.timestamp), 3), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.timestamp), 2), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.timestamp), 1), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.timestamp), 0), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.ssrc), 3), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.ssrc), 2), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.ssrc), 1), idx); - idx++; - - WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE( - &(rtpInfo.header.ssrc), 0), idx); - idx++; - - for (WebRtc_Word16 i=0; i < payloadLengthW8; i++) - { - WEBRTC_SPL_SET_BYTE(rtpPacket, payload[i], idx); - idx++; - } - if (payloadLengthW8 & 1) - { - // Our 16 bits buffer is one byte too large, set that - // last byte to zero. - WEBRTC_SPL_SET_BYTE(rtpPacket, 0x0, idx); - } -} - - -bool -ACMNetEQ::VADStatus() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - return _vadStatus; -} - - -WebRtc_Word16 -ACMNetEQ::SetVADStatus( - const bool status) -{ - CriticalSectionScoped lock(*_netEqCritSect); - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetVADStatus: NetEq is not initialized."); - return -1; - } - if(_vadStatus && !status) - { - // We have been using VAD but we want to stop using it calling the - // following function with NULL as VAD instance switches off the - // post-decode VAD - if(WebRtcNetEQ_SetVADInstance(_inst[idx], NULL, - (WebRtcNetEQ_VADInitFunction) WebRtcVad_Init, - (WebRtcNetEQ_VADSetmodeFunction) WebRtcVad_set_mode, - (WebRtcNetEQ_VADFunction) WebRtcVad_Process) < 0) - { - LogError("setVADinstance", idx); - return -1; - } - // Free VAD Memory - if(_ptrVADInst[idx] != NULL) - { - WebRtcVad_Free(_ptrVADInst[idx]); - _ptrVADInst[idx] = NULL; - } - - // Set previous VAD status to UNKNOWN - _previousAudioActivity = AudioFrame::kVadUnknown; - } - else if(!_vadStatus && status) - { - // VAD was off and we have to turn it on - if(EnableVADByIdxSafe(idx) < 0) - { - return -1; - } - - // Set previous VAD status to PASSIVE - _previousAudioActivity = AudioFrame::kVadPassive; - } - } - _vadStatus = status; - return 0; -} - - -ACMVADMode -ACMNetEQ::VADMode() const -{ - CriticalSectionScoped lock(*_netEqCritSect); - return _vadMode; -} - - -WebRtc_Word16 -ACMNetEQ::SetVADMode( - const ACMVADMode mode) -{ - CriticalSectionScoped lock(*_netEqCritSect); - if((mode < VADNormal) || (mode > VADVeryAggr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetVADMode: NetEq error: could not set VAD mode, mode is not supported"); - return -1; - } - else - { - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetVADMode: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_SetVADMode(_inst[idx], mode) < 0) - { - LogError("SetVADmode", idx); - return -1; - } - } - _vadMode = mode; - return 0; - } -} - - -WebRtc_Word32 -ACMNetEQ::FlushBuffers() -{ - CriticalSectionScoped lock(*_netEqCritSect); - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "FlushBuffers: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_FlushBuffers(_inst[idx]) < 0) - { - LogError("FlushBuffers", idx); - return -1; - } - } - return 0; -} - - -WebRtc_Word32 -ACMNetEQ::GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - WebRtc_UWord32 len = position; - strncpy(&version[position], "NetEq\t\t", remainingBufferInBytes); - position = (WebRtc_UWord32)strlen(version); - remainingBufferInBytes -= (position - len); - len = position; - - WebRtc_Word8 myVersion[100]; - if(WebRtcNetEQ_GetVersion(myVersion) < 0) - { - return -1; - } - - strncpy(&version[position], myVersion, remainingBufferInBytes); - position = (WebRtc_UWord32)strlen(version); - remainingBufferInBytes -= (position - len); - len = position; - - strncpy(&version[position], "\n", remainingBufferInBytes); - position = (WebRtc_UWord32)strlen(version); - remainingBufferInBytes -= (position - len); - len = position; - - return 0; -} - -WebRtc_Word16 -ACMNetEQ::RemoveCodec( - WebRtcNetEQDecoder codecIdx, - bool isStereo) -{ - // sanity check - if((codecIdx <= kDecoderReservedStart) || - (codecIdx >= kDecoderReservedEnd)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RemoveCodec: NetEq error: could not Remove Codec, codec index out of range"); - return -1; - } - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RemoveCodec: NetEq is not initialized."); - return -1; - } - - if(WebRtcNetEQ_CodecDbRemove(_inst[0], codecIdx) < 0) - { - LogError("CodecDB_Remove", 0); - return -1; - } - - if(isStereo) - { - if(WebRtcNetEQ_CodecDbRemove(_inst[1], codecIdx) < 0) - { - LogError("CodecDB_Remove", 1); - return -1; - } - } - - return 0; -} - -WebRtc_Word16 -ACMNetEQ::Delay( - WebRtc_UWord16& currentDelayInMs) const -{ - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Delay: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_GetCurrentDelay(_inst[0], ¤tDelayInMs) < 0) - { - LogError("GetCurrentDelay", 0); - return -1; - } - else - { - return 0; - } -} - - -WebRtc_Word16 -ACMNetEQ::SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode) -{ - CriticalSectionScoped lock(*_netEqCritSect); - for(WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) - { - if(!_isInitialized[idx]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetBackgroundNoiseMode: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_SetBGNMode(_inst[idx], (WebRtcNetEQBGNMode)mode) < 0) - { - LogError("SetBGNMode", idx); - return -1; - } - } - return 0; -} - -WebRtc_Word16 -ACMNetEQ::BackgroundNoiseMode( - ACMBackgroundNoiseMode& mode) -{ - WebRtcNetEQBGNMode myMode; - CriticalSectionScoped lock(*_netEqCritSect); - if(!_isInitialized[0]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "BackgroundNoiseMode: NetEq is not initialized."); - return -1; - } - if(WebRtcNetEQ_GetBGNMode(_inst[0], &myMode) < 0) - { - LogError("WebRtcNetEQ_GetBGNMode", 0); - return -1; - } - else - { - mode = (ACMBackgroundNoiseMode)myMode; - } - return 0; -} - -void -ACMNetEQ::SetUniqueId( - WebRtc_Word32 id) -{ - CriticalSectionScoped lock(*_netEqCritSect); - _id = id; -} - - -void -ACMNetEQ::LogError( - const WebRtc_Word8* neteqFuncName, - const WebRtc_Word16 idx) const -{ - WebRtc_Word8 errorName[NETEQ_ERR_MSG_LEN_BYTE]; - WebRtc_Word8 myFuncName[50]; - int neteqErrorCode = WebRtcNetEQ_GetErrorCode(_inst[idx]); - WebRtcNetEQ_GetErrorName(neteqErrorCode, errorName, NETEQ_ERR_MSG_LEN_BYTE - 1); - strncpy(myFuncName, neteqFuncName, 49); - errorName[NETEQ_ERR_MSG_LEN_BYTE - 1] = '\0'; - myFuncName[49] = '\0'; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "NetEq-%d Error in function %s, error-code: %d, error-string: %s", - idx, - myFuncName, - neteqErrorCode, - errorName); -} - - -WebRtc_Word32 -ACMNetEQ::PlayoutTimestamp( - WebRtc_UWord32& timestamp) -{ - CriticalSectionScoped lock(*_netEqCritSect); - if(WebRtcNetEQ_GetSpeechTimeStamp(_inst[0], ×tamp) < 0) - { - LogError("GetSpeechTimeStamp", 0); - return -1; - } - else - { - return 0; - } -} - -WebRtc_Word16 -ACMNetEQ::AddSlave( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs) -{ - CriticalSectionScoped lock(*_netEqCritSect); - const WebRtc_Word16 slaveIdx = 1; - if(_numSlaves < 1) - { - // initialize the receiver, this also sets up VAD. - if(InitByIdxSafe(slaveIdx) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not Initialize"); - return -1; - } - - // Allocate buffer. - if(AllocatePacketBufferByIdxSafe(usedCodecs, noOfCodecs, slaveIdx) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not Allocate Packet Buffer"); - return -1; - } - - if(_masterSlaveInfo != NULL) - { - free(_masterSlaveInfo); - _masterSlaveInfo = NULL; - } - int msInfoSize = WebRtcNetEQ_GetMasterSlaveInfoSize(); - _masterSlaveInfo = malloc(msInfoSize); - - if(_masterSlaveInfo == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not Allocate memory for Master-Slave Info"); - return -1; - } - - // We accept this as initialized NetEQ, the rest is to synchronize - // Slave with Master. - _numSlaves = 1; - _isInitialized[slaveIdx] = true; - - // Set Slave delay as Master delay - WebRtc_UWord16 currentDelayMs; - if(WebRtcNetEQ_GetCurrentDelay(_inst[0], ¤tDelayMs) < 0) - { - LogError("GetCurrentDelay", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not Get Current Delay from Master."); - return -1; - } - if(WebRtcNetEQ_SetExtraDelay(_inst[slaveIdx], currentDelayMs) < 0) - { - LogError("SetExtraDelay", slaveIdx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not set delay"); - return -1; - } - - // Set AVT - if(WebRtcNetEQ_SetAVTPlayout(_inst[slaveIdx], (_avtPlayout) ? 1 : 0) < 0) - { - LogError("SetAVTPlayout", slaveIdx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not set AVT playout."); - return -1; - } - - // Set Background Noise - WebRtcNetEQBGNMode currentMode; - if(WebRtcNetEQ_GetBGNMode(_inst[0], ¤tMode) < 0) - { - LogError("GetBGNMode", 0); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AAddSlave: AddSlave Failed, Could not Get BGN form Master."); - return -1; - } - - if(WebRtcNetEQ_SetBGNMode(_inst[slaveIdx], (WebRtcNetEQBGNMode)currentMode) < 0) - { - LogError("SetBGNMode", slaveIdx); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not set BGN mode."); - return -1; - } - - enum WebRtcNetEQPlayoutMode playoutMode; - switch(_playoutMode) - { - case voice: - playoutMode = kPlayoutOn; - break; - case fax: - playoutMode = kPlayoutFax; - break; - case streaming: - playoutMode = kPlayoutStreaming; - break; - default: - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: NetEq Error, playout mode not recognized"); - return -1; - break; - } - if(WebRtcNetEQ_SetPlayoutMode(_inst[slaveIdx], playoutMode) < 0) - { - LogError("SetPlayoutMode", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "AddSlave: AddSlave Failed, Could not Set Playout Mode."); - return -1; - } - } - - return 0; -} - -void -ACMNetEQ::SetReceivedStereo( - bool receivedStereo) -{ - CriticalSectionScoped lock(*_netEqCritSect); - _receivedStereo = receivedStereo; -} - -WebRtc_UWord8 -ACMNetEQ::NumSlaves() -{ - CriticalSectionScoped lock(*_netEqCritSect); - return _numSlaves; -} - -} // namespace webrtc - diff --git a/modules/audio_coding/main/source/acm_neteq.h b/modules/audio_coding/main/source/acm_neteq.h deleted file mode 100644 index 0b983f8c9..000000000 --- a/modules/audio_coding/main/source/acm_neteq.h +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_NETEQ_H -#define ACM_NETEQ_H - -#include "audio_coding_module.h" -#include "audio_coding_module_typedefs.h" -#include "engine_configurations.h" -#include "module_common_types.h" -#include "typedefs.h" -#include "webrtc_neteq.h" -#include "webrtc_vad.h" - -namespace webrtc { - -class CriticalSectionWrapper; -class RWLockWrapper; -struct CodecInst; -enum AudioPlayoutMode; -enum ACMSpeechType; - -#define MAX_NUM_SLAVE_NETEQ 1 - -class ACMNetEQ -{ -public: - // Constructor of the class - ACMNetEQ(); - - // Destructor of the class. - ~ACMNetEQ(); - - // - // GetVersion() - // Fills the version array with the NetEQ version and updates the - // remainingBufferInBytes and position variables accordingly. - // - // Output: - // - version : An array to be filled with the version - // data. - // - // Input/Output: - // - remainingBuffInBytes : The number of free bytes at the end of - // the version array. - // - position : Position where the free space starts. - // - // Return value : 0 if ok. - // -1 if NetEQ returned an error. - // - static WebRtc_Word32 GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBuffInBytes, - WebRtc_UWord32& position); - - // - // Init() - // Allocates memory for NetEQ and VAD and initializes them. - // - // Return value : 0 if ok. - // -1 if NetEQ or VAD returned an error or - // if out of memory. - // - WebRtc_Word32 Init(); - - // - // RecIn() - // Gives the payload to NetEQ. - // - // Input: - // - incomingPayload : Incoming audio payload. - // - payloadLength : Length of incoming audio payload. - // - rtpInfo : RTP header for the incoming payload containing - // information about payload type, sequence number, - // timestamp, ssrc and marker bit. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 RecIn( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtcRTPHeader& rtpInfo); - - // - // RecOut() - // Asks NetEQ for 10 ms of decoded audio. - // - // Input: - // -audioFrame : an audio frame were output data and - // associated parameters are written to. - // - // Return value : 0 if ok. - // -1 if NetEQ returned an error. - // - WebRtc_Word32 RecOut( - AudioFrame& audioFrame); - - // - // AddCodec() - // Adds a new codec to the NetEQ codec database. - // - // Input: - // - codecDef : The codec to be added. - // - toMaster : true if the codec has to be added to Master - // NetEq, otherwise will be added to the Slave - // NetEQ. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 AddCodec( - WebRtcNetEQ_CodecDef *codecDef, - bool toMaster = true); - - // - // AllocatePacketBuffer() - // Allocates the NetEQ packet buffer. - // - // Input: - // - usedCodecs : An array of the codecs to be used by NetEQ. - // - noOfCodecs : Number of codecs in usedCodecs. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 AllocatePacketBuffer( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs); - - // - // SetExtraDelay() - // Sets an delayInMS milliseconds extra delay in NetEQ. - // - // Input: - // - delayInMS : Extra delay in milliseconds. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 SetExtraDelay( - const WebRtc_Word32 delayInMS); - - // - // SetAVTPlayout() - // Enable/disable playout of AVT payloads. - // - // Input: - // - enable : Enable if true, disable if false. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 SetAVTPlayout( - const bool enable); - - // - // AVTPlayout() - // Get the current AVT playout state. - // - // Return value : True if AVT playout is enabled. - // False if AVT playout is disabled. - // - bool AVTPlayout() const; - - // - // CurrentSampFreqHz() - // Get the current sampling frequency in Hz. - // - // Return value : Sampling frequency in Hz. - // - WebRtc_Word32 CurrentSampFreqHz() const; - - // - // SetPlayoutMode() - // Sets the playout mode to voice or fax. - // - // Input: - // - mode : The playout mode to be used, voice, - // fax, or streaming. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 SetPlayoutMode( - const AudioPlayoutMode mode); - - // - // PlayoutMode() - // Get the current playout mode. - // - // Return value : The current playout mode. - // - AudioPlayoutMode PlayoutMode() const; - - // - // NetworkStatistics() - // Get the current network statistics from NetEQ. - // - // Output: - // - statistics : The current network statistics. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 NetworkStatistics( - ACMNetworkStatistics* statistics) const; - - // - // JitterStatistics() - // Get the current jitter statistics from NetEQ. - // - // Output: - // - jitterStatistics : The current jitter statistics. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 JitterStatistics( - ACMJitterStatistics* jitterStatistics) const; - - // - // PreferredBufferSize() - // Get the currently preferred buffer size from NetEQ. - // - // Output: - // - prefBufSize : The optimal buffer size for the current network - // conditions. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 PreferredBufferSize( - WebRtc_UWord16* prefBufSize) const; - - // - // ResetJitterStatistics() - // Resets the NetEQ jitter statistics. - // - // Return value : 0 if ok. - // <0 if NetEQ returned an error. - // - WebRtc_Word32 ResetJitterStatistics() const; - - // - // VADStatus() - // Get the current VAD status. - // - // Return value : True if VAD is enabled. - // False if VAD is disabled. - // - bool VADStatus() const; - - // - // SetVADStatus() - // Enable/disable VAD. - // - // Input: - // - enable : Enable if true, disable if false. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - WebRtc_Word16 SetVADStatus( - const bool status); - - // - // VADMode() - // Get the current VAD Mode. - // - // Return value : The current VAD mode. - // - ACMVADMode VADMode() const; - - // - // SetVADMode() - // Set the VAD mode. - // - // Input: - // - mode : The new VAD mode. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - WebRtc_Word16 SetVADMode( - const ACMVADMode mode); - - // - // DecodeLock() - // Get the decode lock used to protect decoder instances while decoding. - // - // Return value : Pointer to the decode lock. - // - RWLockWrapper* DecodeLock() const - { - return _decodeLock; - } - - // - // FlushBuffers() - // Flushes the NetEQ packet and speech buffers. - // - // Return value : 0 if ok. - // -1 if NetEQ returned an error. - // - WebRtc_Word32 FlushBuffers(); - - // - // RemoveCodec() - // Removes a codec from the NetEQ codec database. - // - // Input: - // - codecIdx : Codec to be removed. - // - // Return value : 0 if ok. - // -1 if an error occurred. - // - WebRtc_Word16 RemoveCodec( - WebRtcNetEQDecoder codecIdx, - bool isStereo = false); - - - // - // Delay() - // Get the length of the current audio buffer in milliseconds. That is - // approximately the playout delay, which can be used for lip-synch. - // - // Output: - // - currentDelayInMs : delay in audio buffer given in milliseconds - // - // return value : 0 if ok - // -1 if an error occurred. - // - WebRtc_Word16 Delay( - WebRtc_UWord16& currentDelayInMs) const; - - // - // SetBackgroundNoiseMode() - // Set the mode of the background noise. - // - // Input: - // - mode : an enumerator specifying the mode of the - // background noise. - // - // Return value : 0 if succeeded, - // -1 if failed to set the mode. - // - WebRtc_Word16 SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode); - - // - // BackgroundNoiseMode() - // return the mode of the background noise. - // - // Return value : The mode of background noise. - // - WebRtc_Word16 BackgroundNoiseMode( - ACMBackgroundNoiseMode& mode); - - void SetUniqueId( - WebRtc_Word32 id); - - WebRtc_Word32 PlayoutTimestamp( - WebRtc_UWord32& timestamp); - - void SetReceivedStereo( - bool receivedStereo); - - WebRtc_UWord8 NumSlaves(); - - enum JB {masterJB = 0, slaveJB = 1}; - - WebRtc_Word16 AddSlave( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs); - -private: - // - // RTPPack() - // Creates a Word16 RTP packet out of the payload data in Word16 and - // a WebRtcRTPHeader. - // - // Input: - // - payload : Payload to be packetized. - // - payloadLengthW8 : Length of the payload in bytes. - // - rtpInfo : RTP header struct. - // - // Output: - // - rtpPacket : The RTP packet. - // - static void RTPPack( - WebRtc_Word16* rtpPacket, - const WebRtc_Word8* payload, - const WebRtc_Word32 payloadLengthW8, - const WebRtcRTPHeader& rtpInfo); - - void LogError( - const WebRtc_Word8* neteqFuncName, - const WebRtc_Word16 idx) const; - - WebRtc_Word16 InitByIdxSafe( - const WebRtc_Word16 idx); - - WebRtc_Word16 EnableVADByIdxSafe( - const WebRtc_Word16 idx); - - WebRtc_Word16 AllocatePacketBufferByIdxSafe( - WebRtcNetEQDecoder* usedCodecs, - WebRtc_Word16 noOfCodecs, - const WebRtc_Word16 idx); - - void* _inst[MAX_NUM_SLAVE_NETEQ + 1]; - void* _instMem[MAX_NUM_SLAVE_NETEQ + 1]; - - WebRtc_Word16* _netEqPacketBuffer[MAX_NUM_SLAVE_NETEQ + 1]; - - WebRtc_Word32 _id; - float _currentSampFreqKHz; - bool _avtPlayout; - AudioPlayoutMode _playoutMode; - CriticalSectionWrapper* _netEqCritSect; - - WebRtcVadInst* _ptrVADInst[MAX_NUM_SLAVE_NETEQ + 1]; - - bool _vadStatus; - ACMVADMode _vadMode; - RWLockWrapper* _decodeLock; - bool _isInitialized[MAX_NUM_SLAVE_NETEQ + 1]; - WebRtc_UWord8 _numSlaves; - bool _receivedStereo; - void* _masterSlaveInfo; - AudioFrame::VADActivity _previousAudioActivity; - - CriticalSectionWrapper* _callbackCritSect; -}; - -} //namespace webrtc - -#endif //ACM_NETEQ_H diff --git a/modules/audio_coding/main/source/acm_opus.cc b/modules/audio_coding/main/source/acm_opus.cc deleted file mode 100644 index 069175b96..000000000 --- a/modules/audio_coding/main/source/acm_opus.cc +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "acm_opus.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_OPUS - // NOTE! Opus is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used Opus API file. - // #include "opus_interface.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_OPUS - -ACMOPUS::ACMOPUS( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMOPUS::~ACMOPUS() -{ - return; -} - - -WebRtc_Word16 -ACMOPUS::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMOPUS::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMOPUS::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMOPUS::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMOPUS::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMOPUS::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMOPUS::InternalCreateEncoder() -{ - return -1; -} - - -void -ACMOPUS::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMOPUS::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMOPUS::DestructDecoderSafe() -{ - return; -} - - -void -ACMOPUS::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -WebRtc_Word16 -ACMOPUS::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -WebRtc_Word16 -ACMOPUS::SetBitRateSafe( - const WebRtc_Word32 /*rate*/ ) -{ - return -1; -} - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real Opus wrapper -extern WebRtc_Word16 WebRtcOpus_CreateEnc(OPUS_inst_t_** inst, WebRtc_Word16 samplFreq); -extern WebRtc_Word16 WebRtcOpus_CreateDec(OPUS_inst_t_** inst, WebRtc_Word16 samplFreq); -extern WebRtc_Word16 WebRtcOpus_FreeEnc(OPUS_inst_t_* inst); -extern WebRtc_Word16 WebRtcOpus_FreeDec(OPUS_inst_t_* inst); -extern WebRtc_Word16 WebRtcOpus_Encode(OPUS_inst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16* output, - WebRtc_Word16 len, - WebRtc_Word16 byteLen); -extern WebRtc_Word16 WebRtcOpus_EncoderInit(OPUS_inst_t_* encInst, - WebRtc_Word16 samplFreq, - WebRtc_Word16 mode, - WebRtc_Word16 vbrFlag); -extern WebRtc_Word16 WebRtcOpus_Decode(OPUS_inst_t_* decInst); -extern WebRtc_Word16 WebRtcOpus_DecodeBwe(OPUS_inst_t_* decInst, WebRtc_Word16* input); -extern WebRtc_Word16 WebRtcOpus_DecodePlc(OPUS_inst_t_* decInst); -extern WebRtc_Word16 WebRtcOpus_DecoderInit(OPUS_inst_t_* decInst); - -ACMOPUS::ACMOPUS( - WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL), -_opusMode(1), // default mode is the hybrid mode -_flagVBR(0) // default VBR off -{ - _codecID = codecID; - - // Current implementation doesn't have DTX. That might change. - _hasInternalDTX = false; - - // Default sampling frequency - _mySampFreq = 48000; - - // default rate - _myRate = 50000; - - return; -} - -ACMOPUS::~ACMOPUS() -{ - if(_encoderInstPtr != NULL) - { - WebRtcOpus_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcOpus_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - - -WebRtc_Word16 -ACMOPUS::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 noEncodedSamples = 0; - WebRtc_Word16 tmpLenByte = 0; - *bitStreamLenByte = 0; - - WebRtc_Word16 byteLengthFrame = 0; - - // Derive what byte-length is requested - byteLengthFrame = _myRate*_frameLenSmpl/(8*_mySampFreq); - - // Call Encoder - *bitStreamLenByte = WebRtcOpus_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], - (WebRtc_Word16*)bitStream, _frameLenSmpl, byteLengthFrame); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl; - - // sanity check - if(*bitStreamLenByte < 0) - { - // error has happened - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalEncode: Encode error for Opus"); - *bitStreamLenByte = 0; - return -1; - } - - return *bitStreamLenByte; -} - - - -WebRtc_Word16 -ACMOPUS::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMOPUS::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - //set the bit rate and initialize - _myRate = codecParams->codecInstant.rate; - return SetBitRateSafe( (WebRtc_UWord32)_myRate); -} - - -WebRtc_Word16 -ACMOPUS::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - if (WebRtcOpus_DecoderInit(_decoderInstPtr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalInitDecoder: init decoder failed for Opus"); - return -1; - } - return 0; -} - - -WebRtc_Word32 -ACMOPUS::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "CodeDef: Decoder uninitialized for Opus"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_G729_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderOpus, codecInst.pltype, - _decoderInstPtr, 16000); - SET_OPUS_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMOPUS::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMOPUS::InternalCreateEncoder() -{ - if (WebRtcOpus_CreateEnc(&_encoderInstPtr, _mySampFreq) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateEncoder: create encoder failed for Opus"); - return -1; - } - return 0; -} - - -void -ACMOPUS::DestructEncoderSafe() -{ - _encoderExist = false; - _encoderInitialized = false; - if(_encoderInstPtr != NULL) - { - WebRtcOpus_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } -} - - -WebRtc_Word16 -ACMOPUS::InternalCreateDecoder() -{ - if (WebRtcOpus_CreateDec(&_decoderInstPtr, _mySampFreq) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "InternalCreateDecoder: create decoder failed for Opus"); - return -1; - } - return 0; -} - - -void -ACMOPUS::DestructDecoderSafe() -{ - _decoderExist = false; - _decoderInitialized = false; - if(_decoderInstPtr != NULL) - { - WebRtcOpus_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } -} - - -void -ACMOPUS::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcOpus_FreeEnc((OPUS_inst_t*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMOPUS::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec: given payload-type does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - return netEq->RemoveCodec(kDecoderOpus); -} - -WebRtc_Word16 -ACMOPUS::SetBitRateSafe( - const WebRtc_Word32 rate) -{ - //allowed rates: {8000, 12000, 14000, 16000, 18000, 20000, - // 22000, 24000, 26000, 28000, 30000, 32000}; - switch(rate) - { - case 8000: - { - _myRate = 8000; - break; - } - case 12000: - { - _myRate = 12000; - break; - } - case 14000: - { - _myRate = 14000; - break; - } - case 16000: - { - _myRate = 16000; - break; - } - case 18000: - { - _myRate = 18000; - break; - } - case 20000: - { - _myRate = 20000; - break; - } - case 22000: - { - _myRate = 22000; - break; - } - case 24000: - { - _myRate = 24000; - break; - } - case 26000: - { - _myRate = 26000; - break; - } - case 28000: - { - _myRate = 28000; - break; - } - case 30000: - { - _myRate = 30000; - break; - } - case 32000: - { - _myRate = 32000; - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "SetBitRateSafe: Invalid rate Opus"); - return -1; - break; - } - } - - // Re-init with new rate - if (WebRtcOpus_EncoderInit(_encoderInstPtr, _mySampFreq, _opusMode, _flagVBR) >= 0) - { - _encoderParams.codecInstant.rate = _myRate; - return 0; - } - else - { - return -1; - } -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_opus.h b/modules/audio_coding/main/source/acm_opus.h deleted file mode 100644 index 47a8a1392..000000000 --- a/modules/audio_coding/main/source/acm_opus.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_OPUS_H -#define ACM_OPUS_H - -#include "acm_generic_codec.h" - -// forward declaration -struct OPUS_inst_t_; -struct OPUS_inst_t_; - -namespace webrtc -{ - -class ACMOPUS: public ACMGenericCodec -{ -public: - ACMOPUS(WebRtc_Word16 codecID); - ~ACMOPUS(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - OPUS_inst_t_* _encoderInstPtr; - OPUS_inst_t_* _decoderInstPtr; - - WebRtc_UWord16 _mySampFreq; - WebRtc_UWord16 _myRate; - WebRtc_Word16 _opusMode; - WebRtc_Word16 _flagVBR; - -}; - -} // namespace webrtc - -#endif // ACM_OPUS_H - diff --git a/modules/audio_coding/main/source/acm_pcm16b.cc b/modules/audio_coding/main/source/acm_pcm16b.cc deleted file mode 100644 index 1887f5bef..000000000 --- a/modules/audio_coding/main/source/acm_pcm16b.cc +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "acm_pcm16b.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_PCM16 - #include "pcm16b.h" -#endif - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_PCM16 - -ACMPCM16B::ACMPCM16B( - WebRtc_Word16 /* codecID */) -{ - return; -} - - -ACMPCM16B::~ACMPCM16B() -{ - return; -} - - -WebRtc_Word16 -ACMPCM16B::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - - -WebRtc_Word16 -ACMPCM16B::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - - -WebRtc_Word16 -ACMPCM16B::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word16 -ACMPCM16B::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - - -WebRtc_Word32 -ACMPCM16B::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - - -ACMGenericCodec* -ACMPCM16B::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMPCM16B::InternalCreateEncoder() -{ - return -1; -} - - -WebRtc_Word16 -ACMPCM16B::InternalCreateDecoder() -{ - return -1; -} - - -void -ACMPCM16B::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - - -void -ACMPCM16B::DestructEncoderSafe() -{ - return; -} - -void -ACMPCM16B::DestructDecoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMPCM16B::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - - - -#else //===================== Actual Implementation ======================= - - -ACMPCM16B::ACMPCM16B( - WebRtc_Word16 codecID) -{ - _codecID = codecID; - _samplingFreqHz = ACMCodecDB::CodecFreq(_codecID); -} - - -ACMPCM16B::~ACMPCM16B() -{ - return; -} - - -WebRtc_Word16 -ACMPCM16B::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - *bitStreamLenByte = WebRtcPcm16b_Encode(&_inAudio[_inAudioIxRead], - _frameLenSmpl*_noChannels, - bitStream); - // increment the read index to tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl*_noChannels; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMPCM16B::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMPCM16B::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCM16B::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word32 -ACMPCM16B::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMU_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - switch(_samplingFreqHz) - { - case 8000: - { - SET_CODEC_PAR((codecDef), kDecoderPCM16B, codecInst.pltype, - NULL, 8000); - SET_PCM16B_FUNCTIONS((codecDef)); - break; - } - case 16000: - { - SET_CODEC_PAR((codecDef), kDecoderPCM16Bwb, codecInst.pltype, - NULL, 16000); - SET_PCM16B_WB_FUNCTIONS((codecDef)); - break; - } - case 32000: - { - SET_CODEC_PAR((codecDef), kDecoderPCM16Bswb32kHz, - codecInst.pltype, NULL, 32000); - SET_PCM16B_SWB32_FUNCTIONS((codecDef)); - break; - } - default: - { - return -1; - } - } - return 0; -} - - -ACMGenericCodec* -ACMPCM16B::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMPCM16B::InternalCreateEncoder() -{ - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCM16B::InternalCreateDecoder() -{ - // PCM has no instance - return 0; -} - - -void -ACMPCM16B::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - // PCM has no instance - return; -} - - -void -ACMPCM16B::DestructEncoderSafe() -{ - // PCM has no instance - _encoderExist = false; - _encoderInitialized = false; - return; -} - -void -ACMPCM16B::DestructDecoderSafe() -{ - // PCM has no instance - _decoderExist = false; - _decoderInitialized = false; - return; -} - - -WebRtc_Word16 -ACMPCM16B::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - - switch(_samplingFreqHz) - { - case 8000: - { - return netEq->RemoveCodec(kDecoderPCM16B); - break; - } - case 16000: - { - return netEq->RemoveCodec(kDecoderPCM16Bwb); - break; - } - case 32000: - { - return netEq->RemoveCodec(kDecoderPCM16Bswb32kHz); - break; - } - default: - { - return -1; - } - } -} - -#endif - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_pcm16b.h b/modules/audio_coding/main/source/acm_pcm16b.h deleted file mode 100644 index 7f4b6916f..000000000 --- a/modules/audio_coding/main/source/acm_pcm16b.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_PCM16B_H -#define ACM_PCM16B_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -class ACMPCM16B : public ACMGenericCodec -{ -public: - ACMPCM16B(WebRtc_Word16 codecID); - ~ACMPCM16B(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word32 _samplingFreqHz; -}; - -} // namespace webrtc - -#endif //ACM_PCM16B_H - diff --git a/modules/audio_coding/main/source/acm_pcma.cc b/modules/audio_coding/main/source/acm_pcma.cc deleted file mode 100644 index c86bd1c66..000000000 --- a/modules/audio_coding/main/source/acm_pcma.cc +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "acm_pcma.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -// Codec interface -#include "g711_interface.h" - -namespace webrtc -{ - -ACMPCMA::ACMPCMA(WebRtc_Word16 codecID) -{ - _codecID = codecID; -} - - -ACMPCMA::~ACMPCMA() -{ - return; -} - - -WebRtc_Word16 -ACMPCMA::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - *bitStreamLenByte = WebRtcG711_EncodeA(NULL, &_inAudio[_inAudioIxRead], - _frameLenSmpl*_noChannels, (WebRtc_Word16*)bitStream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl*_noChannels; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMPCMA::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMPCMA::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCMA::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word32 ACMPCMA::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMA_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderPCMa, codecInst.pltype, NULL, 8000); - SET_PCMA_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMPCMA::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMPCMA::InternalCreateEncoder() -{ - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCMA::InternalCreateDecoder() -{ - // PCM has no instance - return 0; -} - - -void -ACMPCMA::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - // PCM has no instance - return; -} - - -void -ACMPCMA::DestructEncoderSafe() -{ - // PCM has no instance - return; -} - - -void -ACMPCMA::DestructDecoderSafe() -{ - // PCM has no instance - _decoderInitialized = false; - _decoderExist = false; - return; -} - - -WebRtc_Word16 -ACMPCMA::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - - return netEq->RemoveCodec(kDecoderPCMa); -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_pcma.h b/modules/audio_coding/main/source/acm_pcma.h deleted file mode 100644 index 874f471b2..000000000 --- a/modules/audio_coding/main/source/acm_pcma.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_PCMA_H -#define ACM_PCMA_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -class ACMPCMA : public ACMGenericCodec -{ -public: - ACMPCMA(WebRtc_Word16 codecID); - ~ACMPCMA(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void InternalDestructEncoderInst( - void* ptrInst); -}; - -} // namespace webrtc - -#endif //ACM_PCMA_H - diff --git a/modules/audio_coding/main/source/acm_pcmu.cc b/modules/audio_coding/main/source/acm_pcmu.cc deleted file mode 100644 index 945eaa022..000000000 --- a/modules/audio_coding/main/source/acm_pcmu.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "acm_pcmu.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -// Codec interface -#include "g711_interface.h" - -namespace webrtc -{ - -ACMPCMU::ACMPCMU(WebRtc_Word16 codecID) -{ - _codecID = codecID; -} - - -ACMPCMU::~ACMPCMU() -{ - return; -} - - -WebRtc_Word16 -ACMPCMU::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - *bitStreamLenByte = WebRtcG711_EncodeU(NULL, &_inAudio[_inAudioIxRead], - _frameLenSmpl*_noChannels, (WebRtc_Word16*)bitStream); - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _frameLenSmpl*_noChannels; - return *bitStreamLenByte; -} - - -WebRtc_Word16 -ACMPCMU::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMPCMU::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCMU::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // PCM has no instance - return 0; -} - - -WebRtc_Word32 -ACMPCMU::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMU_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderPCMu, codecInst.pltype, NULL, 8000); - SET_PCMU_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMPCMU::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMPCMU::InternalCreateEncoder() -{ - // PCM has no instance - return 0; -} - - -WebRtc_Word16 -ACMPCMU::InternalCreateDecoder() -{ - // PCM has no instance - return 0; -} - - -void -ACMPCMU::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - // PCM has no instance - return; -} - - -void -ACMPCMU::DestructEncoderSafe() -{ - // PCM has no instance - _encoderExist = false; - _encoderInitialized = false; - return; -} - -void ACMPCMU::DestructDecoderSafe() -{ - // PCM has no instance - _decoderInitialized = false; - _decoderExist = false; - return; -} - - -WebRtc_Word16 -ACMPCMU::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - - return netEq->RemoveCodec(kDecoderPCMu); -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_pcmu.h b/modules/audio_coding/main/source/acm_pcmu.h deleted file mode 100644 index a4fbbadaa..000000000 --- a/modules/audio_coding/main/source/acm_pcmu.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_PCMU_H -#define ACM_PCMU_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -class ACMPCMU : public ACMGenericCodec -{ -public: - ACMPCMU(WebRtc_Word16 codecID); - ~ACMPCMU(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void InternalDestructEncoderInst( - void* ptrInst); -}; - -} // namespace webrtc - -#endif //ACM_PCMU_H - diff --git a/modules/audio_coding/main/source/acm_red.cc b/modules/audio_coding/main/source/acm_red.cc deleted file mode 100644 index edc8c7649..000000000 --- a/modules/audio_coding/main/source/acm_red.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_red.h" -#include "acm_neteq.h" -#include "acm_common_defs.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -namespace webrtc -{ - -ACMRED::ACMRED(WebRtc_Word16 codecID) -{ - _codecID = codecID; -} - - -ACMRED::~ACMRED() -{ - return; -} - - -WebRtc_Word16 -ACMRED::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - // RED is never used as an encoder - // RED has no instance - return 0; -} - - -WebRtc_Word16 -ACMRED::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - - -WebRtc_Word16 -ACMRED::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // RED has no instance - return 0; -} - - -WebRtc_Word16 -ACMRED::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - // This codec does not need initialization, - // RED has no instance - return 0; -} - - -WebRtc_Word32 -ACMRED::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - // Todo: - // log error - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_PCMU_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - SET_CODEC_PAR((codecDef), kDecoderRED, codecInst.pltype, NULL, 8000); - SET_RED_FUNCTIONS((codecDef)); - return 0; -} - - -ACMGenericCodec* -ACMRED::CreateInstance(void) -{ - return NULL; -} - - -WebRtc_Word16 -ACMRED::InternalCreateEncoder() -{ - // RED has no instance - return 0; -} - - -WebRtc_Word16 -ACMRED::InternalCreateDecoder() -{ - // RED has no instance - return 0; -} - - -void -ACMRED::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - // RED has no instance - return; -} - - -void -ACMRED::DestructEncoderSafe() -{ - // RED has no instance - return; -} - -void ACMRED::DestructDecoderSafe() -{ - // RED has no instance - return; -} - - -WebRtc_Word16 -ACMRED::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - - return netEq->RemoveCodec(kDecoderRED); -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_red.h b/modules/audio_coding/main/source/acm_red.h deleted file mode 100644 index 5900ec684..000000000 --- a/modules/audio_coding/main/source/acm_red.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_RED_H -#define ACM_RED_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -class ACMRED : public ACMGenericCodec -{ -public: - ACMRED(WebRtc_Word16 codecID); - ~ACMRED(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - void InternalDestructEncoderInst( - void* ptrInst); -}; - -} // namespace webrtc - -#endif //ACM_RED_H - diff --git a/modules/audio_coding/main/source/acm_resampler.cc b/modules/audio_coding/main/source/acm_resampler.cc deleted file mode 100644 index 7389c1424..000000000 --- a/modules/audio_coding/main/source/acm_resampler.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "acm_resampler.h" -#include "critical_section_wrapper.h" -#include "resampler.h" -#include "signal_processing_library.h" -#include "trace.h" - -namespace webrtc -{ - -ACMResampler::ACMResampler(): - -_resamplerCritSect(*CriticalSectionWrapper::CreateCriticalSection()) -{ -} - -ACMResampler::~ACMResampler() -{ - - delete &_resamplerCritSect; -} - - -WebRtc_Word16 -ACMResampler::Resample10Msec( - const WebRtc_Word16* inAudio, - WebRtc_Word32 inFreqHz, - WebRtc_Word16* outAudio, - WebRtc_Word32 outFreqHz, - WebRtc_UWord8 numAudioChannels) -{ - - CriticalSectionScoped cs(_resamplerCritSect); - - if(inFreqHz == outFreqHz) - { - memcpy(outAudio, inAudio, (inFreqHz*numAudioChannels / 100) * sizeof(WebRtc_Word16)); - return (WebRtc_Word16)(inFreqHz / 100); - } - - int maxLen = 480 * numAudioChannels; //max number of samples for 10ms at 48kHz - int lengthIn = (WebRtc_Word16)(inFreqHz / 100) * numAudioChannels; - int outLen; - - WebRtc_Word32 ret; - ResamplerType type; - type = (numAudioChannels == 1)? kResamplerSynchronous:kResamplerSynchronousStereo; - - ret = _resampler.ResetIfNeeded(inFreqHz,outFreqHz,type); - if (ret < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Error in reset of resampler"); - return -1; - } - - ret = _resampler.Push(inAudio, lengthIn, outAudio, maxLen, outLen); - if (ret < 0 ) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Error in resampler: resampler.Push"); - return -1; - } - - WebRtc_Word16 outAudioLenSmpl = (WebRtc_Word16) outLen / numAudioChannels; - - return outAudioLenSmpl; - -} - -void -ACMResampler::SetUniqueId( - WebRtc_Word32 id) -{ - CriticalSectionScoped lock(_resamplerCritSect); - _id = id; -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/acm_resampler.h b/modules/audio_coding/main/source/acm_resampler.h deleted file mode 100644 index f4d5c538b..000000000 --- a/modules/audio_coding/main/source/acm_resampler.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_RESAMPLER_H -#define ACM_RESAMPLER_H - -#include "resampler.h" -#include "typedefs.h" - -namespace webrtc { - -class CriticalSectionWrapper; - -class ACMResampler -{ -public: - ACMResampler(); - ~ACMResampler(); - - WebRtc_Word16 Resample10Msec( - const WebRtc_Word16* inAudio, - const WebRtc_Word32 inFreqHz, - WebRtc_Word16* outAudio, - const WebRtc_Word32 outFreqHz, - WebRtc_UWord8 numAudioChannels); - - void SetUniqueId( - WebRtc_Word32 id); - -private: - - //Use the Resampler class - Resampler _resampler; - WebRtc_Word32 _id; - CriticalSectionWrapper& _resamplerCritSect; -}; - -} // namespace webrtc - -#endif //ACM_RESAMPLER_H diff --git a/modules/audio_coding/main/source/acm_speex.cc b/modules/audio_coding/main/source/acm_speex.cc deleted file mode 100644 index f1973167e..000000000 --- a/modules/audio_coding/main/source/acm_speex.cc +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_neteq.h" -#include "acm_speex.h" -#include "trace.h" -#include "webrtc_neteq.h" -#include "webrtc_neteq_help_macros.h" - -#ifdef WEBRTC_CODEC_SPEEX - // NOTE! Speex is not included in the open-source package. Modify this file or your codec - // API to match the function call and name of used Speex API file. - // #include "speex_interface.h" -#endif - - -namespace webrtc -{ - -#ifndef WEBRTC_CODEC_SPEEX -ACMSPEEX::ACMSPEEX(WebRtc_Word16 /* codecID*/) -{ - return; -} - -ACMSPEEX::~ACMSPEEX() -{ - return; -} - -WebRtc_Word16 -ACMSPEEX::InternalEncode( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16* /* bitStreamLenByte */) -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::EnableDTX() -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::DisableDTX() -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::InternalInitEncoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - return -1; -} - -WebRtc_Word32 -ACMSPEEX::CodecDef( - WebRtcNetEQ_CodecDef& /* codecDef */, - const CodecInst& /* codecInst */) -{ - return -1; -} - -ACMGenericCodec* -ACMSPEEX::CreateInstance(void) -{ - return NULL; -} - -WebRtc_Word16 -ACMSPEEX::InternalCreateEncoder() -{ - return -1; -} - -void -ACMSPEEX::DestructEncoderSafe() -{ - return; -} - - -WebRtc_Word16 -ACMSPEEX::InternalCreateDecoder() -{ - return -1; -} - -void -ACMSPEEX::DestructDecoderSafe() -{ - return; -} - -WebRtc_Word16 -ACMSPEEX::SetBitRateSafe( - const WebRtc_Word32 /* rate */) -{ - return -1; -} - -void -ACMSPEEX::InternalDestructEncoderInst( - void* /* ptrInst */) -{ - return; -} - -WebRtc_Word16 -ACMSPEEX::UnregisterFromNetEqSafe( - ACMNetEQ* /* netEq */, - WebRtc_Word16 /* payloadType */) -{ - return -1; -} - -#ifdef UNUSEDSPEEX -WebRtc_Word16 -ACMSPEEX::EnableVBR() -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::DisableVBR() -{ - return -1; -} - -WebRtc_Word16 -ACMSPEEX::SetComplMode( - WebRtc_Word16 mode) -{ - return -1; -} -#endif - -#else //===================== Actual Implementation ======================= - -// Remove when integrating a real Speex wrapper -extern WebRtc_Word16 WebRtcSpeex_CreateEnc(SPEEX_encinst_t_** inst, - WebRtc_Word16 samplFreq); -extern WebRtc_Word16 WebRtcSpeex_CreateDec(SPEEX_decinst_t_** inst, - WebRtc_Word16 samplFreq, - WebRtc_Word16 mode); -extern WebRtc_Word16 WebRtcSpeex_FreeEnc(SPEEX_encinst_t_* inst); -extern WebRtc_Word16 WebRtcSpeex_FreeDec(SPEEX_decinst_t_* inst); -extern WebRtc_Word16 WebRtcSpeex_Encode(SPEEX_encinst_t_* encInst, - WebRtc_Word16* input, - WebRtc_Word16 rate); -extern WebRtc_Word16 WebRtcSpeex_EncoderInit(SPEEX_encinst_t_* encInst, - WebRtc_Word16 samplFreq, - WebRtc_Word16 mode, - WebRtc_Word16 vbrFlag); -extern WebRtc_Word16 WebRtcSpeex_GetBitstream(SPEEX_encinst_t_* encInst, - WebRtc_Word16* output); -extern WebRtc_Word16 WebRtcSpeex_Decode(SPEEX_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcSpeex_DecodePlc(SPEEX_decinst_t_* decInst); -extern WebRtc_Word16 WebRtcSpeex_DecoderInit(SPEEX_decinst_t_* decInst); - -ACMSPEEX::ACMSPEEX(WebRtc_Word16 codecID): -_encoderInstPtr(NULL), -_decoderInstPtr(NULL) -{ - _codecID = codecID; - - // Set sampling frequency, frame size and rate Speex - if(_codecID == ACMCodecDB::speex8) - { - _samplingFrequency = 8000; - _samplesIn20MsAudio = 160; - _encodingRate = 11000; - } - else if(_codecID == ACMCodecDB::speex16) - { - _samplingFrequency = 16000; - _samplesIn20MsAudio = 320; - _encodingRate = 22000; - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Wrong codec id for Speex."); - - _samplingFrequency = -1; - _samplesIn20MsAudio = -1; - _encodingRate = -1; - } - - _hasInternalDTX = true; - _dtxEnabled = false; - _vbrEnabled = false; - _complMode = 3; // default complexity value - - return; -} - -ACMSPEEX::~ACMSPEEX() -{ - if(_encoderInstPtr != NULL) - { - WebRtcSpeex_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - if(_decoderInstPtr != NULL) - { - WebRtcSpeex_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - return; -} - -WebRtc_Word16 -ACMSPEEX::InternalEncode( - WebRtc_UWord8* bitStream, - WebRtc_Word16* bitStreamLenByte) -{ - WebRtc_Word16 status; - WebRtc_Word16 numEncodedSamples = 0; - WebRtc_Word16 n = 0; - - while( numEncodedSamples < _frameLenSmpl) - { - status = WebRtcSpeex_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], - _encodingRate); - - // increment the read index this tell the caller that how far - // we have gone forward in reading the audio buffer - _inAudioIxRead += _samplesIn20MsAudio; - numEncodedSamples += _samplesIn20MsAudio; - - if(status < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error in Speex encoder"); - return status; - } - - // Update VAD, if internal DTX is used - if(_hasInternalDTX && _dtxEnabled) - { - _vadLabel[n++] = status; - _vadLabel[n++] = status; - } - - if(status == 0) - { - // This frame is detected as inactive. We need send whatever - // encoded so far. - *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, - (WebRtc_Word16*)bitStream); - - return *bitStreamLenByte; - } - } - - *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, - (WebRtc_Word16*)bitStream); - return *bitStreamLenByte; -} - -WebRtc_Word16 -ACMSPEEX::DecodeSafe( - WebRtc_UWord8* /* bitStream */, - WebRtc_Word16 /* bitStreamLenByte */, - WebRtc_Word16* /* audio */, - WebRtc_Word16* /* audioSamples */, - WebRtc_Word8* /* speechType */) -{ - return 0; -} - -WebRtc_Word16 -ACMSPEEX::EnableDTX() -{ - if(_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // enable DTX - if(WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1:0), _complMode, 1) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot enable DTX for Speex"); - return -1; - } - _dtxEnabled = true; - return 0; - } - else - { - return -1; - } - - return 0; -} - -WebRtc_Word16 -ACMSPEEX::DisableDTX() -{ - if(!_dtxEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // disable DTX - if(WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1:0), _complMode, 0) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot disable DTX for Speex"); - return -1; - } - _dtxEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } - - return 0; -} - -WebRtc_Word16 -ACMSPEEX::InternalInitEncoder( - WebRtcACMCodecParams* codecParams) -{ - // sanity check - if (_encoderInstPtr == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot initialize Speex encoder, instance does not exist"); - return -1; - } - - WebRtc_Word16 status = SetBitRateSafe((codecParams->codecInstant).rate); - status += (WebRtcSpeex_EncoderInit(_encoderInstPtr, _vbrEnabled, _complMode, ((codecParams->enableDTX)? 1:0)) < 0)? -1:0; - - if (status >= 0) { - return 0; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error in initialization of Speex encoder"); - return -1; - } -} - -WebRtc_Word16 -ACMSPEEX::InternalInitDecoder( - WebRtcACMCodecParams* /* codecParams */) -{ - WebRtc_Word16 status; - - // sanity check - if (_decoderInstPtr == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot initialize Speex decoder, instance does not exist"); - return -1; - } - status = ((WebRtcSpeex_DecoderInit(_decoderInstPtr) < 0)? -1:0); - - if (status >= 0) { - return 0; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error in initialization of Speex decoder"); - return -1; - } -} - -WebRtc_Word32 -ACMSPEEX::CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst) -{ - if (!_decoderInitialized) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error, Speex decoder is not initialized"); - return -1; - } - - // Fill up the structure by calling - // "SET_CODEC_PAR" & "SET_SPEEX_FUNCTION." - // Then call NetEQ to add the codec to it's - // database. - - switch(_samplingFrequency) - { - case 8000: - { - SET_CODEC_PAR((codecDef), kDecoderSPEEX_8, codecInst.pltype, - _decoderInstPtr, 8000); - break; - } - case 16000: - { - SET_CODEC_PAR((codecDef), kDecoderSPEEX_16, codecInst.pltype, - _decoderInstPtr, 16000); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Unsupported sampling frequency for Speex"); - - return -1; - break; - } - } - - SET_SPEEX_FUNCTIONS((codecDef)); - return 0; -} - -ACMGenericCodec* -ACMSPEEX::CreateInstance(void) -{ - return NULL; -} - -WebRtc_Word16 -ACMSPEEX::InternalCreateEncoder() -{ - return WebRtcSpeex_CreateEnc(&_encoderInstPtr, _samplingFrequency); -} - -void -ACMSPEEX::DestructEncoderSafe() -{ - if(_encoderInstPtr != NULL) - { - WebRtcSpeex_FreeEnc(_encoderInstPtr); - _encoderInstPtr = NULL; - } - // there is no encoder set the following - _encoderExist = false; - _encoderInitialized = false; - _encodingRate = 0; -} - - -WebRtc_Word16 -ACMSPEEX::InternalCreateDecoder() -{ - return WebRtcSpeex_CreateDec(&_decoderInstPtr, _samplingFrequency, 1); -} - -void -ACMSPEEX::DestructDecoderSafe() -{ - if(_decoderInstPtr != NULL) - { - WebRtcSpeex_FreeDec(_decoderInstPtr); - _decoderInstPtr = NULL; - } - // there is no encoder instance set the followings - _decoderExist = false; - _decoderInitialized = false; -} - -WebRtc_Word16 -ACMSPEEX::SetBitRateSafe( - const WebRtc_Word32 rate) -{ - // Check if changed rate - if (rate == _encodingRate) { - return 0; - } else if (rate > 2000) { - _encodingRate = rate; - _encoderParams.codecInstant.rate = rate; - } else { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Unsupported encoding rate for Speex"); - - return -1; - } - - return 0; -} - - -void -ACMSPEEX::InternalDestructEncoderInst( - void* ptrInst) -{ - if(ptrInst != NULL) - { - WebRtcSpeex_FreeEnc((SPEEX_encinst_t_*)ptrInst); - } - return; -} - - -WebRtc_Word16 -ACMSPEEX::UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType) -{ - if(payloadType != _decoderParams.codecInstant.pltype) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot unregister codec %s given payload-type %d does not match \ -the stored payload type", - _decoderParams.codecInstant.plname, - payloadType, - _decoderParams.codecInstant.pltype); - return -1; - } - - - switch(_samplingFrequency) - { - case 8000: - { - return netEq->RemoveCodec(kDecoderSPEEX_8); - } - case 16000: - { - return netEq->RemoveCodec(kDecoderSPEEX_16); - } - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Could not unregister Speex from NetEQ. Sampling frequency doesn't match"); - return -1; - } - } -} - - -#ifdef UNUSEDSPEEX - -// This API is currently not in use. If requested to be able to enable/disable VBR -// an ACM API need to be added. -WebRtc_Word16 -ACMSPEEX::EnableVBR() -{ - if(_vbrEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // enable Variable Bit Rate (VBR) - if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 1, _complMode, (_dtxEnabled? 1:0)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot enable VBR mode for Speex"); - - return -1; - } - _vbrEnabled = true; - return 0; - } - else - { - return -1; - } -} - - -// This API is currently not in use. If requested to be able to enable/disable VBR -// an ACM API need to be added. -WebRtc_Word16 -ACMSPEEX::DisableVBR() -{ - if(!_vbrEnabled) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // disable DTX - if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, _complMode, (_dtxEnabled? 1:0)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Cannot disable DTX for Speex"); - - return -1; - } - _vbrEnabled = false; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -// This API is currently not in use. If requested to be able to set complexity -// an ACM API need to be added. -WebRtc_Word16 -ACMSPEEX::SetComplMode( - WebRtc_Word16 mode) -{ - // Check if new mode - if(mode == _complMode) - { - return 0; - } - else if(_encoderExist) // check if encoder exist - { - // Set new mode - if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, mode, (_dtxEnabled? 1:0)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, - "Error in complexity mode for Speex"); - return -1; - } - _complMode = mode; - return 0; - } - else - { - // encoder doesn't exists, therefore disabling is harmless - return 0; - } -} - -#endif - -#endif - -} // namespace webrtc - diff --git a/modules/audio_coding/main/source/acm_speex.h b/modules/audio_coding/main/source/acm_speex.h deleted file mode 100644 index e03c2cd9b..000000000 --- a/modules/audio_coding/main/source/acm_speex.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_SPEEX_H -#define ACM_SPEEX_H - -#include "acm_generic_codec.h" - -namespace webrtc -{ - -// forward declaration -struct SPEEX_encinst_t_; -struct SPEEX_decinst_t_; - -class ACMSPEEX : public ACMGenericCodec -{ -public: - ACMSPEEX(WebRtc_Word16 codecID); - ~ACMSPEEX(); - // for FEC - ACMGenericCodec* CreateInstance(void); - - WebRtc_Word16 InternalEncode( - WebRtc_UWord8* bitstream, - WebRtc_Word16* bitStreamLenByte); - - WebRtc_Word16 InternalInitEncoder( - WebRtcACMCodecParams *codecParams); - - WebRtc_Word16 InternalInitDecoder( - WebRtcACMCodecParams *codecParams); - -protected: - WebRtc_Word16 DecodeSafe( - WebRtc_UWord8* bitStream, - WebRtc_Word16 bitStreamLenByte, - WebRtc_Word16* audio, - WebRtc_Word16* audioSamples, - WebRtc_Word8* speechType); - - WebRtc_Word32 CodecDef( - WebRtcNetEQ_CodecDef& codecDef, - const CodecInst& codecInst); - - void DestructEncoderSafe(); - - void DestructDecoderSafe(); - - WebRtc_Word16 InternalCreateEncoder(); - - WebRtc_Word16 InternalCreateDecoder(); - - void InternalDestructEncoderInst( - void* ptrInst); - - WebRtc_Word16 SetBitRateSafe( - const WebRtc_Word32 rate); - - WebRtc_Word16 EnableDTX(); - - WebRtc_Word16 DisableDTX(); - -#ifdef UNUSEDSPEEX - WebRtc_Word16 EnableVBR(); - - WebRtc_Word16 DisableVBR(); - - WebRtc_Word16 SetComplMode( - WebRtc_Word16 mode); -#endif - - WebRtc_Word16 UnregisterFromNetEqSafe( - ACMNetEQ* netEq, - WebRtc_Word16 payloadType); - - SPEEX_encinst_t_* _encoderInstPtr; - SPEEX_decinst_t_* _decoderInstPtr; - WebRtc_Word16 _complMode; - bool _vbrEnabled; - WebRtc_Word32 _encodingRate; - WebRtc_Word16 _samplingFrequency; - WebRtc_UWord16 _samplesIn20MsAudio; -}; - -} // namespace webrtc - -#endif // ACM_SPEEX_H diff --git a/modules/audio_coding/main/source/audio_coding_module.cc b/modules/audio_coding/main/source/audio_coding_module.cc deleted file mode 100644 index 0ecad8daa..000000000 --- a/modules/audio_coding/main/source/audio_coding_module.cc +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// 'conversion' conversion from 'type1' to 'type2', possible loss of data -#pragma warning(disable: 4267) - -#include "acm_dtmf_detection.h" -#include "audio_coding_module.h" -#include "audio_coding_module_impl.h" -#include "trace.h" - -namespace webrtc -{ - -// Create module -AudioCodingModule* -AudioCodingModule::Create( - const WebRtc_Word32 id) -{ - return new AudioCodingModuleImpl(id); -} - -// Destroy module -void -AudioCodingModule::Destroy( - AudioCodingModule* module) -{ - delete static_cast (module); -} - -// Returns version of the module and its components. -WebRtc_Word32 -AudioCodingModule::GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - WebRtc_Word32 len = position; - strncpy(&version[position], "AudioCodingModule 1.3.0\n", remainingBufferInBytes); - position = (WebRtc_UWord32)strlen(version); - remainingBufferInBytes -= (position - len); - - if(ACMNetEQ::GetVersion(version, - remainingBufferInBytes, position) < 0) - { - return -1; - } - - ACMCodecDB::initACMCodecDB(); - if(ACMCodecDB::CodecsVersion(version, - remainingBufferInBytes, position) < 0) - { - return -1; - } - return 0; -} - -// Get number of supported codecs -WebRtc_UWord8 AudioCodingModule::NumberOfCodecs() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, -1, - "NumberOfCodecs()"); - ACMCodecDB::initACMCodecDB(); - return (WebRtc_UWord8)ACMCodecDB::NoOfCodecs(); -} - -// Get supported codec param with id -WebRtc_Word32 -AudioCodingModule::Codec( - const WebRtc_UWord8 listId, - CodecInst& codec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, -1, - "Codec(const WebRtc_UWord8 listId, CodecInst& codec)"); - ACMCodecDB::initACMCodecDB(); - - // Get the codec settings for the codec with the given list ID - return ACMCodecDB::Codec(listId, &codec); -} - -// Get supported codec Param with name -WebRtc_Word32 -AudioCodingModule::Codec( - const WebRtc_Word8* payloadName, - CodecInst& codec, - const WebRtc_Word32 samplingFreqHz) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, -1, - "Codec(const WebRtc_Word8* payloadName, CodecInst& codec)"); - ACMCodecDB::initACMCodecDB(); - - // Search through codec list for a matching name - for(WebRtc_Word16 codecCntr = 0; codecCntr < ACMCodecDB::NoOfCodecs(); - codecCntr++) - { - // Store codec settings for codec number "codeCntr" in the output struct - ACMCodecDB::Codec(codecCntr, &codec); - - if(!STR_CASE_CMP(codec.plname, payloadName)) - { - // If samplingFreqHz is set (!= -1), check if frequency matches - if((samplingFreqHz == codec.plfreq) || (samplingFreqHz == -1)) - { - // We found a match, return OK - return 0; - } - } - } - - // if we are here we couldn't find anything - // set the params to unacceptable values - codec.plname[0] = '\0'; - codec.pltype = -1; - codec.pacsize = 0; - codec.rate = 0; - codec.plfreq = 0; - return -1; -} - -// Get supported codec Index with name, and frequency if needed -WebRtc_Word32 -AudioCodingModule::Codec( - const WebRtc_Word8* payloadName, - const WebRtc_Word32 samplingFreqHz) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, -1, - "Codec(const WebRtc_Word8* payloadName)"); - ACMCodecDB::initACMCodecDB(); - CodecInst codec; - - // Search through codec list for a matching name - for(WebRtc_Word16 codecCntr = 0; codecCntr < ACMCodecDB::NoOfCodecs(); - codecCntr++) - { - // Temporally store codec settings for codec number "codeCntr" in "codec" - ACMCodecDB::Codec(codecCntr, &codec); - - if(!STR_CASE_CMP(codec.plname, payloadName)) - { - // If samplingFreqHz is set (!= -1), check if frequency matches - if((samplingFreqHz == codec.plfreq) || (samplingFreqHz == -1)) - { - // We found a match, return codec list number (index) - return codecCntr; - } - } - } - - // We did not find a matching codec in the list - return -1; -} - -// Checks the validity of the parameters of the given codec -bool -AudioCodingModule::IsCodecValid( - const CodecInst& codec) -{ - WebRtc_Word16 mirrorID; - WebRtc_Word8 errMsg[500]; - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, -1, - "IsCodecValid(const CodecInst& codec)"); - ACMCodecDB::initACMCodecDB(); - WebRtc_Word16 codecNumber = ACMCodecDB::CodecNumber(&codec, mirrorID, errMsg, 500); - - if(codecNumber < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, -1, errMsg); - return false; - } - else - { - return true; - } -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/audio_coding_module.gyp b/modules/audio_coding/main/source/audio_coding_module.gyp deleted file mode 100644 index b5753bf9f..000000000 --- a/modules/audio_coding/main/source/audio_coding_module.gyp +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'audio_coding_module', - 'type': '<(library)', - 'dependencies': [ - '../../codecs/CNG/main/source/cng.gyp:CNG', - '../../codecs/G711/main/source/g711.gyp:G711', - '../../codecs/G722/main/source/g722.gyp:G722', - '../../codecs/iLBC/main/source/ilbc.gyp:iLBC', - '../../codecs/iSAC/main/source/isac.gyp:iSAC', - '../../codecs/iSAC/fix/source/isacfix.gyp:iSACFix', - '../../codecs/PCM16B/main/source/pcm16b.gyp:PCM16B', - '../../NetEQ/main/source/neteq.gyp:NetEq', - '../../../../common_audio/resampler/main/source/resampler.gyp:resampler', - '../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../../common_audio/vad/main/source/vad.gyp:vad', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../interface', - ], - }, - 'sources': [ - # TODO: Remove files from here and P4 when ACM is slimmed down. - '../interface/audio_coding_module.h', - '../interface/audio_coding_module_typedefs.h', - 'acm_amr.cc', - 'acm_amr.h', - 'acm_amrwb.cc', - 'acm_amrwb.h', - 'acm_cng.cc', - 'acm_cng.h', - 'acm_codec_database.cc', - 'acm_codec_database.h', - 'acm_dtmf_detection.cc', - 'acm_dtmf_detection.h', - 'acm_dtmf_playout.cc', - 'acm_dtmf_playout.h', - 'acm_g722.cc', - 'acm_g722.h', - 'acm_g7221.cc', - 'acm_g7221.h', - 'acm_g7221c.cc', - 'acm_g7221c.h', - 'acm_g729.cc', - 'acm_g729.h', - 'acm_g7291.cc', - 'acm_g7291.h', - 'acm_generic_codec.cc', - 'acm_generic_codec.h', - 'acm_gsmfr.cc', - 'acm_gsmfr.h', - 'acm_ilbc.cc', - 'acm_ilbc.h', - 'acm_isac.cc', - 'acm_isac.h', - 'acm_isac_macros.h', - 'acm_neteq.cc', - 'acm_neteq.h', - 'acm_opus.cc', - 'acm_opus.h', - 'acm_speex.cc', - 'acm_speex.h', - 'acm_pcm16b.cc', - 'acm_pcm16b.h', - 'acm_pcma.cc', - 'acm_pcma.h', - 'acm_pcmu.cc', - 'acm_pcmu.h', - 'acm_red.cc', - 'acm_red.h', - 'acm_resampler.cc', - 'acm_resampler.h', - 'audio_coding_module.cc', - 'audio_coding_module_impl.cc', - 'audio_coding_module_impl.h', - ], - }, - { - 'target_name': 'audio_coding_module_test', - 'type': 'executable', - 'dependencies': [ - 'audio_coding_module', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'sources': [ - '../test/ACMTest.cpp', - '../test/APITest.cpp', - '../test/Channel.cpp', - '../test/EncodeDecodeTest.cpp', - '../test/EncodeToFileTest.cpp', - '../test/iSACTest.cpp', - '../test/PCMFile.cpp', - '../test/RTPFile.cpp', - '../test/SpatialAudio.cpp', - '../test/TestAllCodecs.cpp', - '../test/Tester.cpp', - '../test/TestFEC.cpp', - '../test/TestStereo.cpp', - '../test/TestVADDTX.cpp', - '../test/TimedTrace.cpp', - '../test/TwoWayCommunication.cpp', - '../test/utility.cpp', - ], - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }], - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.cc b/modules/audio_coding/main/source/audio_coding_module_impl.cc deleted file mode 100644 index 75adb8998..000000000 --- a/modules/audio_coding/main/source/audio_coding_module_impl.cc +++ /dev/null @@ -1,2713 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "acm_codec_database.h" -#include "acm_common_defs.h" -#include "acm_dtmf_detection.h" -#include "acm_generic_codec.h" -#include "acm_resampler.h" -#include "audio_coding_module_impl.h" -#include "critical_section_wrapper.h" -#include "engine_configurations.h" -#include "rw_lock_wrapper.h" -#include "trace.h" - -#include -#include - -#ifdef ACM_QA_TEST -# include -#endif - -#ifdef TIMED_LOGGING - char message[500]; - #include "../test/timedtrace.h" - #include - #define LOGWITHTIME(logString) \ - sprintf(message, logString, _id); \ - _trace.TimedLogg(message); -#else - #define LOGWITHTIME(logString) -#endif - -namespace webrtc -{ - -enum { - kACMToneEnd = 999 -}; - -AudioCodingModuleImpl::AudioCodingModuleImpl( - const WebRtc_Word32 id): - _packetizationCallback(NULL), - _id(id), - _lastTimestamp(0), - _lastInTimestamp(0), - _vadEnabled(false), - _dtxEnabled(false), - _vadMode(VADNormal), - _stereoSend(false), - _currentSendCodecIdx(-1), // invalid value - _sendCodecRegistered(false), - _acmCritSect(CriticalSectionWrapper::CreateCriticalSection()), - _vadCallback(NULL), - _lastRecvAudioCodecPlType(255), - _isFirstRED(true), - _fecEnabled(false), - _fragmentation(NULL), - _lastFECTimestamp(0), - _receiveREDPayloadType(255), // invalid value - _previousPayloadType(255), - _dummyRTPHeader(NULL), - _receiverInitialized(false), - _dtmfDetector(NULL), - _dtmfCallback(NULL), - _lastDetectedTone(kACMToneEnd), - _callbackCritSect(CriticalSectionWrapper::CreateCriticalSection()) -{ - _lastTimestamp = 0xD87F3F9F; - _lastInTimestamp = 0xD87F3F9F; - // nullify the codec name - strncpy(_sendCodecInst.plname, "noCodecRegistered", 31); - ACMCodecDB::initACMCodecDB(); - for (WebRtc_Word16 i = 0; i < MAX_NR_OF_CODECS; i++) - { - _codecs[i] = NULL; - _registeredPlTypes[i] = -1; - _stereoReceive[i] = false; - _slaveCodecs[i] = NULL; - _mirrorCodecIdx[i] = -1; - } - - _netEq.SetUniqueId(_id); - - // Allocate memory for RED - _redBuffer = new WebRtc_UWord8[MAX_PAYLOAD_SIZE_BYTE]; - _fragmentation = new RTPFragmentationHeader; - _fragmentation->fragmentationVectorSize = 2; - _fragmentation->fragmentationOffset = new WebRtc_UWord32[2]; - _fragmentation->fragmentationLength = new WebRtc_UWord32[2]; - _fragmentation->fragmentationTimeDiff = new WebRtc_UWord16[2]; - _fragmentation->fragmentationPlType = new WebRtc_UWord8[2]; - - // Register the default payload type for RED and for - // CNG for the three frequencies 8, 16 and 32 kHz - for (int i = (ACMCodecDB::_noOfCodecs - 1); i>=0; i--) - { - if((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "red") == 0)) - { - _redPayloadType = ACMCodecDB::_mycodecs[i].pltype; - } - else if ((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "CN") == 0)) - { - if (ACMCodecDB::_mycodecs[i].plfreq == 8000) - { - memcpy(&_cngNB, &ACMCodecDB::_mycodecs[i], sizeof(_cngNB)); - } - else if (ACMCodecDB::_mycodecs[i].plfreq == 16000) - { - memcpy(&_cngWB, &ACMCodecDB::_mycodecs[i], sizeof(_cngWB)); - } else if (ACMCodecDB::_mycodecs[i].plfreq == 32000) - { - memcpy(&_cngSWB, &ACMCodecDB::_mycodecs[i], sizeof(_cngSWB)); - } - } - } - - if(InitializeReceiverSafe() < 0 ) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot initialize reciever"); - } -#ifdef TIMED_LOGGING - _trace.SetUp("TimedLogg.txt"); -#endif - -#ifdef ACM_QA_TEST - char fileName[500]; - sprintf(fileName, "ACM_QA_incomingPL_%03d_%d%d%d%d%d%d.dat", - _id, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10); - - _incomingPL = fopen(fileName, "wb"); - - sprintf(fileName, "ACM_QA_outgoingPL_%03d_%d%d%d%d%d%d.dat", - _id, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10); - _outgoingPL = fopen(fileName, "wb"); -#endif - - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); -} - -AudioCodingModuleImpl::~AudioCodingModuleImpl() -{ - { - CriticalSectionScoped lock(*_acmCritSect); - _currentSendCodecIdx = -1; - - for (WebRtc_Word16 i=0; i < MAX_NR_OF_CODECS; i++) - { - if (_codecs[i] != NULL) - { - assert(_mirrorCodecIdx[i] > -1); - if(_codecs[_mirrorCodecIdx[i]] != NULL) - { - delete _codecs[_mirrorCodecIdx[i]]; - _codecs[_mirrorCodecIdx[i]] = NULL; - } - _codecs[i] = NULL; - } - - if(_slaveCodecs[i] != NULL) - { - assert(_mirrorCodecIdx[i] > -1); - if(_slaveCodecs[_mirrorCodecIdx[i]] != NULL) - { - delete _slaveCodecs[_mirrorCodecIdx[i]]; - _slaveCodecs[_mirrorCodecIdx[i]] = NULL; - } - _slaveCodecs[i] = NULL; - } - } - - if(_dtmfDetector != NULL) - { - delete _dtmfDetector; - _dtmfDetector = NULL; - } - if(_dummyRTPHeader != NULL) - { - delete _dummyRTPHeader; - _dummyRTPHeader = NULL; - } - if(_redBuffer != NULL) - { - delete [] _redBuffer; - _redBuffer = NULL; - } - if(_fragmentation != NULL) - { - // Only need to delete fragmentation header, it will clean - // up it's own memory - delete _fragmentation; - _fragmentation = NULL; - } - } - - -#ifdef ACM_QA_TEST - if(_incomingPL != NULL) - { - fclose(_incomingPL); - } - - if(_outgoingPL != NULL) - { - fclose(_outgoingPL); - } -#endif - - delete _callbackCritSect; - _callbackCritSect = NULL; - - delete _acmCritSect; - _acmCritSect = NULL; - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, _id, "Destroyed"); -} - -WebRtc_Word32 -AudioCodingModuleImpl::ChangeUniqueId( - const WebRtc_Word32 id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ChangeUniqueId(new id:%d)", id); - { - CriticalSectionScoped lock(*_acmCritSect); - _id = id; -#ifdef ACM_QA_TEST - if(_incomingPL != NULL) - { - fclose(_incomingPL); - } - - if(_outgoingPL != NULL) - { - fclose(_outgoingPL); - } - - char fileName[500]; - sprintf(fileName, "ACM_QA_incomingPL_%03d_%d%d%d%d%d%d.dat", - _id, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10); - - _incomingPL = fopen(fileName, "wb"); - - sprintf(fileName, "ACM_QA_outgoingPL_%03d_%d%d%d%d%d%d.dat", - _id, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10, - rand() % 10); - _outgoingPL = fopen(fileName, "wb"); -#endif - - for (WebRtc_Word16 i = 0; i < MAX_NR_OF_CODECS; i++) - { - if(_codecs[i] != NULL) - { - _codecs[i]->SetUniqueID(id); - } - } - } - - _netEq.SetUniqueId(_id); - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::Version( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "Version()"); - if(version == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Input buffer is NULL"); - return -1; - } - return GetVersion(version, remainingBufferInBytes, position); -} - -// returns the number of milliseconds until the module want a -// worker thread to call Process -WebRtc_Word32 -AudioCodingModuleImpl::TimeUntilNextProcess() -{ - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("TimeUntilNextProcess")) - { - return -1; - } - return _codecs[_currentSendCodecIdx]->SamplesLeftToEncode() / - (_sendCodecInst.plfreq / 1000); -} - -// Process any pending tasks such as timeouts -WebRtc_Word32 -AudioCodingModuleImpl::Process() -{ - WebRtc_UWord8 bitStream[2 * MAX_PAYLOAD_SIZE_BYTE]; // Make room for 1 RED payload - WebRtc_Word16 lengthBytes = 2 * MAX_PAYLOAD_SIZE_BYTE; - WebRtc_UWord32 rtpTimestamp; - WebRtc_Word16 status; - WebRtcACMEncodingType encodingType; - FrameType frameType = kAudioFrameSpeech; - WebRtc_Word16 redLengthBytes; - WebRtc_UWord8 currentPayloadType; - bool hasDataToSend = false; - bool fecActive = false; - WebRtc_UWord32 dummyFragLength; - - // keep the scope of the ACM critical section limited - { - CriticalSectionScoped lock(*_acmCritSect); - if(!HaveValidEncoder("Process")) - { - return -1; - } - - status = _codecs[_currentSendCodecIdx]->Encode(bitStream, &lengthBytes, - &rtpTimestamp, &encodingType); - if (status < 0) // Encode failed - { - // logging error - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Process(): Encoding Failed"); - lengthBytes = 0; - return -1; - } - else if(status == 0) - { - // Not enough data - return 0; - } - else - { - switch(encodingType) - { - case kNoEncoding: - { - currentPayloadType = _previousPayloadType; - frameType = kFrameEmpty; - lengthBytes = 0; - break; - } - case kActiveNormalEncoded: - case kPassiveNormalEncoded: - { - currentPayloadType = (WebRtc_UWord8)_sendCodecInst.pltype; - frameType = kAudioFrameSpeech; - break; - } - case kPassiveDTXNB: - { - currentPayloadType = (WebRtc_UWord8)_cngNB.pltype; - frameType = kAudioFrameCN; - _isFirstRED = true; - break; - } - case kPassiveDTXWB: - { - currentPayloadType = (WebRtc_UWord8)_cngWB.pltype; - frameType = kAudioFrameCN; - _isFirstRED = true; - break; - } - case kPassiveDTXSWB: - { - currentPayloadType = (WebRtc_UWord8)_cngSWB.pltype; - frameType = kAudioFrameCN; - _isFirstRED = true; - break; - } - - default: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Process(): Wrong Encoding-Type"); - return -1; - } - } - hasDataToSend = true; - _previousPayloadType = currentPayloadType; - - // Redundancy encode is done here, - // the two bitstreams packetized into - // one RTP packet and the fragmentation points - // are set. - // Only apply RED on speech data. - - if((_fecEnabled) && - ((encodingType == kActiveNormalEncoded) || - (encodingType == kPassiveNormalEncoded))) - { - // FEC is enabled within this scope. - // - // Note that, a special solution exists for iSAC since it is the only codec for - // which getRedPayload has a non-empty implementation. - // - // Summary of the FEC scheme below (use iSAC as example): - // - // 1st (_firstRED is true) encoded iSAC frame (primary #1) => - // - call getRedPayload() and store redundancy for packet #1 in second - // fragment of RED buffer (old data) - // - drop the primary iSAC frame - // - don't call SendData - // 2nd (_firstRED is false) encoded iSAC frame (primary #2) => - // - store primary #2 in 1st fragment of RED buffer and send the combined - // packet - // - the transmitted packet contains primary #2 (new) and reduncancy for - // packet #1 (old) - // - call getRedPayload() and store redundancy for packet #2 in second - // fragment of RED buffer - // - // ... - // - // Nth encoded iSAC frame (primary #N) => - // - store primary #N in 1st fragment of RED buffer and send the combined - // packet - // - the transmitted packet contains primary #N (new) and reduncancy for - // packet #(N-1) (old) - // - call getRedPayload() and store redundancy for packet #N in second - // fragment of RED buffer - // - // For all other codecs, getRedPayload does nothing and returns -1 => - // redundant data is only a copy. - // - // First combined packet contains : #2 (new) and #1 (old) - // Second combined packet contains: #3 (new) and #2 (old) - // Third combined packet contains : #4 (new) and #3 (old) - // - // Hence, even if every second packet is dropped, perfect reconstruction is - // possible. - fecActive = true; - - hasDataToSend = false; - if(!_isFirstRED) // skip this part for the first packet in a RED session - { - // Rearrange bitStream such that FEC packets are included. - // Replace bitStream now that we have stored current bitStream. - memcpy(bitStream + _fragmentation->fragmentationOffset[1], _redBuffer, - _fragmentation->fragmentationLength[1]); - // Update the fragmentation time difference vector - WebRtc_UWord16 timeSinceLastTimestamp = - WebRtc_UWord16(rtpTimestamp - _lastFECTimestamp); - - // Update fragmentation vectors - _fragmentation->fragmentationPlType[1] = - _fragmentation->fragmentationPlType[0]; - _fragmentation->fragmentationTimeDiff[1] = timeSinceLastTimestamp; - hasDataToSend = true; - } - - // Insert new packet length. - _fragmentation->fragmentationLength[0] = lengthBytes; - - // Insert new packet payload type. - _fragmentation->fragmentationPlType[0] = currentPayloadType; - _lastFECTimestamp = rtpTimestamp; - - // can be modified by the getRedPayload() call if iSAC is utilized - redLengthBytes = lengthBytes; - // A fragmentation header is provided => packetization according to RFC 2198 - // (RTP Payload for Redundant Audio Data) will be used. - // First fragment is the current data (new). - // Second fragment is the previous data (old). - lengthBytes = - static_cast (_fragmentation->fragmentationLength[0] + - _fragmentation->fragmentationLength[1]); - - // Get, and store, redundant data from the encoder based on the recently - // encoded frame. - // NOTE - only iSAC contains an implementation; all other codecs does nothing - // and returns -1. - if (_codecs[_currentSendCodecIdx]->GetRedPayload(_redBuffer, - &redLengthBytes) == -1) - { - // The codec was not iSAC => use current encoder output as redundant data - // instead (trivial FEC scheme) - memcpy(_redBuffer, bitStream, redLengthBytes); - } - - // Temporary storing RED length - dummyFragLength = redLengthBytes; - - _isFirstRED = false; - // Update payload type with RED payload type - currentPayloadType = _redPayloadType; - } - } - } - - if(hasDataToSend) - { - CriticalSectionScoped lock(*_callbackCritSect); -#ifdef ACM_QA_TEST - if(_outgoingPL != NULL) - { - fwrite(&rtpTimestamp, sizeof(WebRtc_UWord32), 1, _outgoingPL); - fwrite(¤tPayloadType, sizeof(WebRtc_UWord8), 1, _outgoingPL); - fwrite(&lengthBytes, sizeof(WebRtc_Word16), 1, _outgoingPL); - } -#endif - - if(_packetizationCallback != NULL) - { - if (fecActive) { - _packetizationCallback->SendData(frameType, currentPayloadType, - rtpTimestamp, bitStream, lengthBytes, _fragmentation); - } else { - _packetizationCallback->SendData(frameType, currentPayloadType, - rtpTimestamp, bitStream, lengthBytes, NULL); - } - } - - // This is for test - if(_vadCallback != NULL) - { - _vadCallback->InFrameType(((WebRtc_Word16)encodingType)); - } - } - if (fecActive) { - _fragmentation->fragmentationLength[1] = dummyFragLength; - } - return lengthBytes; -} - - - - -///////////////////////////////////////// -// Sender -// - -// Initialize send codec -WebRtc_Word32 -AudioCodingModuleImpl::InitializeSender() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "InitializeSender()"); - - CriticalSectionScoped lock(*_acmCritSect); - - _sendCodecRegistered = false; - _currentSendCodecIdx = -1; // invalid value - - _sendCodecInst.plname[0] = '\0'; - - for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) - { - if(_codecs[codecCntr] != NULL) - { - _codecs[codecCntr]->DestructEncoder(); - } - } - // Initialize FEC/RED - _isFirstRED = true; - if(_fecEnabled) - { - if(_redBuffer != NULL) - { - memset(_redBuffer, 0, MAX_PAYLOAD_SIZE_BYTE); - } - if(_fragmentation != NULL) - { - _fragmentation->fragmentationVectorSize = 2; - _fragmentation->fragmentationOffset[0] = 0; - _fragmentation->fragmentationOffset[0] = MAX_PAYLOAD_SIZE_BYTE; - memset(_fragmentation->fragmentationLength, 0, sizeof(WebRtc_UWord32) * 2); - memset(_fragmentation->fragmentationTimeDiff, 0, sizeof(WebRtc_UWord16) * 2); - memset(_fragmentation->fragmentationPlType, 0, sizeof(WebRtc_UWord8) * 2); - } - } - - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::ResetEncoder() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ResetEncoder()"); - - CriticalSectionScoped lock(*_acmCritSect); - if(!HaveValidEncoder("ResetEncoder")) - { - return -1; - } - return _codecs[_currentSendCodecIdx]->ResetEncoder(); -} - -void -AudioCodingModuleImpl::UnregisterSendCodec() -{ - CriticalSectionScoped lock(*_acmCritSect); - _sendCodecRegistered = false; - _currentSendCodecIdx = -1; // invalid value - - return; -} - -ACMGenericCodec* -AudioCodingModuleImpl::CreateCodec( - const CodecInst& codec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "CreateCodec()"); - - ACMGenericCodec* myCodec = NULL; - - myCodec = ACMCodecDB::CreateCodecInstance(&codec); - if(myCodec == NULL) - { - // Error, could not create the codec - - // logging error - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "ACMCodecDB::CreateCodecInstance() failed in \ -CreateCodec()"); - return myCodec; - } - myCodec->SetUniqueID(_id); - myCodec->SetNetEqDecodeLock(_netEq.DecodeLock()); - - return myCodec; -} - -// can be called multiple times for Codec, CNG, RED -WebRtc_Word32 -AudioCodingModuleImpl::RegisterSendCodec( - const CodecInst& sendCodec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "Registering Send Codec"); - - if((sendCodec.channels != 1) && (sendCodec.channels != 2)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Registering Send codec failed due to wrong number of channels, %d. Only\ -mono codecs are supported, i.e. channels=1.", sendCodec.channels); - return -1; - } - - WebRtc_Word8 errMsg[500]; - WebRtc_Word16 mirrorId; - WebRtc_Word16 codecID = ACMCodecDB::CodecNumber(&sendCodec, mirrorId, errMsg, 500); - CriticalSectionScoped lock(*_acmCritSect); - - // Check for reported errors from function CodecNumber() - if(codecID < 0) - { - if(!_sendCodecRegistered) - { - // This values has to be NULL if there is no codec registered - _currentSendCodecIdx = -1; // invalid value - } - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, errMsg); - // Failed to register Send Codec - return -1; - } - - // telephone-event cannot be a send codec - if(!STR_CASE_CMP(sendCodec.plname, "telephone-event")) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "telephone-event cannot be registered as send codec"); - return -1; - } - - // RED can be registered with other payload type. If not registered a default - // payload type is used. - if(!STR_CASE_CMP(sendCodec.plname, "red")) - { - // Check if the payload-type is valid - if(ACMCodecDB::ValidPayloadType(sendCodec.pltype) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Invalid payload-type %d for %s.", - sendCodec.pltype, sendCodec.plname); - return -1; - } - // Set RED payload type - _redPayloadType = (WebRtc_UWord8)sendCodec.pltype; - return 0; - } - - // CNG can be registered with other payload type. If not registered the - // default payload types will be used: CNNB=13 (fixed), CNWB=97, CNSWB=98 - if(!STR_CASE_CMP(sendCodec.plname, "CN")) - { - // CNG is registered - - switch(sendCodec.plfreq) - { - case 8000: - { - memcpy(&_cngNB, &sendCodec, sizeof(_cngNB)); - break; - } - case 16000: - { - memcpy(&_cngWB, &sendCodec, sizeof(_cngWB)); - break; - } - case 32000: - { - memcpy(&_cngSWB, &sendCodec, sizeof(_cngSWB)); - break; - } - default : - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RegisterSendCodec() failed, invalid frequency for CNG registeration"); - return -1; - } - } - - return 0; - } - - // Check if the payload-type is valid - if(ACMCodecDB::ValidPayloadType(sendCodec.pltype) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Invalid payload-type %d for %s.", - sendCodec.pltype, sendCodec.plname); - return -1; - } - - // Check if codec supports the number of channels - if(ACMCodecDB::_channelSupport[codecID] < sendCodec.channels) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "%d number of channels not supportedn for %s.", - sendCodec.channels, sendCodec.plname); - return -1; - } - - // Set Stereo - if (sendCodec.channels == 2) - { - _stereoSend = true; - } - - // check if the codec is already registered as send codec - bool oldCodecFamily; - if(_sendCodecRegistered) - { - WebRtc_Word16 sendCodecMirrorID; - WebRtc_Word16 sendCodecID = - ACMCodecDB::CodecNumber(&_sendCodecInst, sendCodecMirrorID); - assert(sendCodecID >= 0); - oldCodecFamily = (sendCodecID == codecID) || (mirrorId == sendCodecMirrorID); - } - else - { - oldCodecFamily = false; - } - - // If new codec, register - if (!oldCodecFamily) - { - if(_codecs[mirrorId] == NULL) - { - - _codecs[mirrorId] = CreateCodec(sendCodec); - if(_codecs[mirrorId] == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Create the codec"); - return -1; - } - _mirrorCodecIdx[mirrorId] = mirrorId; - } - - if(mirrorId != codecID) - { - _codecs[codecID] = _codecs[mirrorId]; - _mirrorCodecIdx[codecID] = mirrorId; - } - - ACMGenericCodec* tmpCodecPtr = _codecs[codecID]; - WebRtc_Word16 status; - WebRtcACMCodecParams codecParams; - - memcpy(&(codecParams.codecInstant), &sendCodec, - sizeof(CodecInst)); - codecParams.enableVAD = _vadEnabled; - codecParams.enableDTX = _dtxEnabled; - codecParams.vadMode = _vadMode; - // force initialization - status = tmpCodecPtr->InitEncoder(&codecParams, true); - - // Check if VAD was turned on, or if error is reported - if (status == 1) { - _vadEnabled = true; - } else if (status < 0) - { - // could not initialize the encoder - - // Check if already have a registered codec - // Depending on that differet messages are logged - if(!_sendCodecRegistered) - { - _currentSendCodecIdx = -1; // invalid value - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Initialize the encoder No Encoder is registered"); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Initialize the encoder, continue encoding \ -with the previously registered codec"); - } - return -1; - } - - // Everything is fine so we can replace the previous codec - // with this one - if(_sendCodecRegistered) - { - // If we change codec we start fresh with FEC. - // This is not strictly required by the standard. - _isFirstRED = true; - - if(tmpCodecPtr->SetVAD(_dtxEnabled, _vadEnabled, _vadMode) < 0){ - // SetVAD failed - _vadEnabled = false; - _dtxEnabled = false; - } - - } - - _currentSendCodecIdx = codecID; - _sendCodecRegistered = true; - memcpy(&_sendCodecInst, &sendCodec, sizeof(CodecInst)); - _previousPayloadType = _sendCodecInst.pltype; - return 0; - } - else - { - // If codec is the same as already registers check if any parameters - // has changed compared to the current values. - // If any parameter is valid then apply it and record. - bool forceInit = false; - - if(mirrorId != codecID) - { - _codecs[codecID] = _codecs[mirrorId]; - _mirrorCodecIdx[codecID] = mirrorId; - } - - // check the payload-type - if(sendCodec.pltype != _sendCodecInst.pltype) - { - // At this point check if the given payload type is valid. - // Record it later when the sampling frequency is changed - // successfully. - if(ACMCodecDB::ValidPayloadType(sendCodec.pltype) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Out of range payload type"); - return -1; - } - - } - - // If there is a codec that ONE instance of codec supports multiple - // sampling frequencies, then we need to take care of it here. - // one such a codec is iSAC. Both WB and SWB are encoded and decoded - // with one iSAC instance. Therefore, we need to update the encoder - // frequency if required. - if(_sendCodecInst.plfreq != sendCodec.plfreq) - { - forceInit = true; - - // if sampling frequency is changed we have to start fresh with RED. - _isFirstRED = true; - } - - if(_sendCodecInst.pacsize != sendCodec.pacsize) - { - forceInit = true; - } - - if(forceInit) - { - WebRtcACMCodecParams codecParams; - - memcpy(&(codecParams.codecInstant), &sendCodec, - sizeof(CodecInst)); - codecParams.enableVAD = _vadEnabled; - codecParams.enableDTX = _dtxEnabled; - codecParams.vadMode = _vadMode; - - // force initialization - if(_codecs[_currentSendCodecIdx]->InitEncoder(&codecParams, true) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Could not change the codec packet-size."); - return -1; - } - - _sendCodecInst.plfreq = sendCodec.plfreq; - _sendCodecInst.pacsize = sendCodec.pacsize; - } - - // If the change of sampling frequency has been successful then - // we store the payload-type. - _sendCodecInst.pltype = sendCodec.pltype; - - // check if a change in Rate is required - if(sendCodec.rate != _sendCodecInst.rate) - { - if(_codecs[codecID]->SetBitRate(sendCodec.rate) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Could not change the codec rate."); - return -1; - } - _sendCodecInst.rate = sendCodec.rate; - } - _previousPayloadType = _sendCodecInst.pltype; - - return 0; - } -} - -// get current send codec -WebRtc_Word32 -AudioCodingModuleImpl::SendCodec( - CodecInst& currentSendCodec) const -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "SendCodec()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!_sendCodecRegistered) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "SendCodec Failed, no codec is registered"); - - return -1; - } - WebRtcACMCodecParams encoderParam; - _codecs[_currentSendCodecIdx]->EncoderParams(&encoderParam); - encoderParam.codecInstant.pltype = _sendCodecInst.pltype; - memcpy(¤tSendCodec, &(encoderParam.codecInstant), - sizeof(CodecInst)); - - return 0; -} - -// get current send freq -WebRtc_Word32 -AudioCodingModuleImpl::SendFrequency() const -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "SendFrequency()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!_sendCodecRegistered) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "SendFrequency Failed, no codec is registered"); - - return -1; - } - - return _sendCodecInst.plfreq; -} - -// Get encode bitrate -// Adaptive rate codecs return their current encode target rate, while other codecs -// return there longterm avarage or their fixed rate. -WebRtc_Word32 -AudioCodingModuleImpl::SendBitrate() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SendBitrate()"); - - CriticalSectionScoped lock(*_acmCritSect); - - if(!_sendCodecRegistered) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "SendBitrate Failed, no codec is registered"); - - return -1; - } - - WebRtcACMCodecParams encoderParam; - _codecs[_currentSendCodecIdx]->EncoderParams(&encoderParam); - - return encoderParam.codecInstant.rate; -} - -// set available bandwidth, inform the encoder about the estimated bandwidth -// received from the remote party -WebRtc_Word32 -AudioCodingModuleImpl::SetReceivedEstimatedBandwidth( - const WebRtc_Word32 bw ) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetReceivedEstimatedBandwidth()"); - return _codecs[_currentSendCodecIdx]->SetEstimatedBandwidth(bw); -} - -// register a transport callback wich will be called to deliver -// the encoded buffers -WebRtc_Word32 -AudioCodingModuleImpl::RegisterTransportCallback( - AudioPacketizationCallback* transport) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "RegisterTransportCallback()"); - CriticalSectionScoped lock(*_callbackCritSect); - _packetizationCallback = transport; - return 0; -} - -// Used by the module to deliver messages to the codec module/appliation -// AVT(DTMF) -WebRtc_Word32 -AudioCodingModuleImpl::RegisterIncomingMessagesCallback( -#ifndef WEBRTC_DTMF_DETECTION - AudioCodingFeedback* /* incomingMessagesCallback */, - const ACMCountries /* cpt */) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "RegisterIncomingMessagesCallback()"); - return -1; -#else - AudioCodingFeedback* incomingMessagesCallback, - const ACMCountries cpt) -{ - WebRtc_Word16 status = 0; - - // Enter the critical section for callback - { - CriticalSectionScoped lock(*_callbackCritSect); - _dtmfCallback = incomingMessagesCallback; - } - // enter the ACM critical section to set up the DTMF class. - { - CriticalSectionScoped lock(*_acmCritSect); - // Check if the call is to disable or enable the callback - if(incomingMessagesCallback == NULL) - { - // callback is disabled, delete DTMF-detector class - if(_dtmfDetector != NULL) - { - delete _dtmfDetector; - _dtmfDetector = NULL; - } - status = 0; - } - else - { - status = 0; - if(_dtmfDetector == NULL) - { - _dtmfDetector = new(ACMDTMFDetection); - if(_dtmfDetector == NULL) - { - status = -1; - } - } - if(status >= 0) - { - status = _dtmfDetector->Enable(cpt); - if(status < 0) - { - // failed to initialize if DTMF-detection was not enabled before, - // delete the class, and set the callback to NULL and return -1. - delete _dtmfDetector; - _dtmfDetector = NULL; - } - } - } - } - // check if we failed in setting up the DTMF-detector class - if((status < 0)) - { - // we failed, we cannot have the callback - CriticalSectionScoped lock(*_callbackCritSect); - _dtmfCallback = NULL; - } - - return status; -#endif -} - - -// Add 10MS of raw (PCM) audio data to the encoder -WebRtc_Word32 -AudioCodingModuleImpl::Add10MsData( - const AudioFrame& audioFrame) -{ - // Do we have a codec registered? - CriticalSectionScoped lock(*_acmCritSect); - if(!HaveValidEncoder("Add10MsData")) - { - return -1; - } - - if(audioFrame._payloadDataLengthInSamples == 0) - { - assert(false); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Add 10 ms audio, payload length is zero"); - return -1; - } - // Allow for 8, 16, 32 and 48kHz input audio - if((audioFrame._frequencyInHz != 8000) && - (audioFrame._frequencyInHz != 16000) && - (audioFrame._frequencyInHz != 32000) && - (audioFrame._frequencyInHz != 48000)) - { - assert(false); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Add 10 ms audio, input frequency not valid"); - return -1; - } - - - // If the length and frequency matches. We currently just support raw PCM - if((audioFrame._frequencyInHz/ 100) != - audioFrame._payloadDataLengthInSamples) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Add 10 ms audio, input frequency and length doesn't \ -match"); - return -1; - } - - // Calculate the timestamp that should be pushed to codec. - // This might be different from the timestamp of the frame - // due to re-sampling - bool resamplingRequired = - ((WebRtc_Word32)audioFrame._frequencyInHz != _sendCodecInst.plfreq); - - WebRtc_UWord32 currentTimestamp; - WebRtc_Word32 status; - // if it is required, we have to do a resampling - if(resamplingRequired) - { - WebRtc_Word16 resampledAudio[WEBRTC_10MS_PCM_AUDIO]; - WebRtc_Word32 sendPlFreq = _sendCodecInst.plfreq; - WebRtc_UWord32 diffInputTimestamp; - WebRtc_Word16 newLengthSmpl; - - // calculate the timestamp of this frame - if(_lastInTimestamp > audioFrame._timeStamp) - { - // a wrap around has happened - diffInputTimestamp = ((WebRtc_UWord32)0xFFFFFFFF - _lastInTimestamp) - + audioFrame._timeStamp; - } - else - { - diffInputTimestamp = audioFrame._timeStamp - _lastInTimestamp; - } - currentTimestamp = _lastTimestamp + (WebRtc_UWord32)(diffInputTimestamp * - ((double)_sendCodecInst.plfreq / (double)audioFrame._frequencyInHz)); - - newLengthSmpl = _inputResampler.Resample10Msec( - audioFrame._payloadData, audioFrame._frequencyInHz, - resampledAudio, sendPlFreq, _sendCodecInst.channels); - - if(newLengthSmpl < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot add 10 ms audio, resmapling failed"); - return -1; - } - status = _codecs[_currentSendCodecIdx]->Add10MsData(currentTimestamp, - resampledAudio, newLengthSmpl, audioFrame._audioChannel); - } - else - { - currentTimestamp = audioFrame._timeStamp; - - status = _codecs[_currentSendCodecIdx]->Add10MsData(currentTimestamp, - audioFrame._payloadData, audioFrame._payloadDataLengthInSamples, - audioFrame._audioChannel); - } - _lastInTimestamp = audioFrame._timeStamp; - _lastTimestamp = currentTimestamp; - return status; -} - -///////////////////////////////////////// -// (FEC) Forward Error Correction -// - -bool -AudioCodingModuleImpl::FECStatus() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "FECStatus()"); - CriticalSectionScoped lock(*_acmCritSect); - return _fecEnabled; -} - -// configure FEC status i.e on/off -WebRtc_Word32 -AudioCodingModuleImpl::SetFECStatus( -#ifdef WEBRTC_CODEC_RED - const bool enableFEC) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetFECStatus()"); - CriticalSectionScoped lock(*_acmCritSect); - - if (_fecEnabled != enableFEC) - { - // Reset the RED buffer - memset(_redBuffer, 0, MAX_PAYLOAD_SIZE_BYTE); - - // Reset fragmentation buffers - _fragmentation->fragmentationVectorSize = 2; - _fragmentation->fragmentationOffset[0] = 0; - _fragmentation->fragmentationOffset[1] = MAX_PAYLOAD_SIZE_BYTE; - memset(_fragmentation->fragmentationLength, 0, sizeof(WebRtc_UWord32) * 2); - memset(_fragmentation->fragmentationTimeDiff, 0, sizeof(WebRtc_UWord16) * 2); - memset(_fragmentation->fragmentationPlType, 0, sizeof(WebRtc_UWord8) * 2); - - // set _fecEnabled - _fecEnabled = enableFEC; - } - _isFirstRED = true; // Make sure we restart FEC - return 0; -#else - const bool /* enableFEC */) -{ - _fecEnabled = false; - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, - " WEBRTC_CODEC_RED is undefined => _fecEnabled = %d", _fecEnabled); - return -1; -#endif -} - - -///////////////////////////////////////// -// (VAD) Voice Activity Detection -// - -WebRtc_Word32 -AudioCodingModuleImpl::SetVAD( - const bool enableDTX, - const bool enableVAD, - const ACMVADMode vadMode) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetVAD()"); - CriticalSectionScoped lock(*_acmCritSect); - - // sanity check of the mode - if((vadMode != VADNormal) && - (vadMode != VADLowBitrate) && - (vadMode != VADAggr) && - (vadMode != VADVeryAggr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Invalid VAD Mode %d, no change is made to VAD/DTX status", - (int)vadMode); - return -1; - } - - // If a send codec is registered, set VAD/DTX for the codec - if(HaveValidEncoder("SetVAD")) { - WebRtc_Word16 status = - _codecs[_currentSendCodecIdx]->SetVAD(enableDTX, enableVAD, vadMode); - if(status == 1) { - // Vad was enabled; - _vadEnabled = true; - _dtxEnabled = enableDTX; - _vadMode = vadMode; - - return 0; - } else if (status < 0) { - // SetVAD failed - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "SetVAD failed"); - - _vadEnabled = false; - _dtxEnabled = false; - - return -1; - } - } - - _vadEnabled = enableVAD; - _dtxEnabled = enableDTX; - _vadMode = vadMode; - - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::VAD( - bool& dtxEnabled, - bool& vadEnabled, - ACMVADMode& vadMode) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "VAD()"); - CriticalSectionScoped lock(*_acmCritSect); - - dtxEnabled = _dtxEnabled; - vadEnabled = _vadEnabled; - vadMode = _vadMode; - - return 0; -} - -///////////////////////////////////////// -// Receiver -// - -WebRtc_Word32 -AudioCodingModuleImpl::InitializeReceiver() -{ - CriticalSectionScoped lock(*_acmCritSect); - return InitializeReceiverSafe(); -} - -// Initialize receiver, resets codec database etc -WebRtc_Word32 -AudioCodingModuleImpl::InitializeReceiverSafe() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "InitializeReceiver()"); - WebRtc_Word16 noCodecs = ACMCodecDB::NoOfCodecs(); - int i = 0; - - // If the receiver is already initialized then we - // also like to destruct decoders if any exist. After a call - // to this function, we should have a clean start-up. - if(_receiverInitialized) - { - for(WebRtc_Word16 codecCntr = 0; codecCntr < noCodecs; codecCntr++) - { - if(UnregisterReceiveCodecSafe(codecCntr) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "InitializeReceiver() failed, Could not unregister codec"); - return -1; - } - } - } - if (_netEq.Init() != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "InitializeReceiver() failed, Could not initialize NetEq"); - return -1; - } - _netEq.SetUniqueId(_id); - if (_netEq.AllocatePacketBuffer(ACMCodecDB::NetEqDecoders(), - ACMCodecDB::NoNetEqDecoders()) != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "NetEq cannot allocatePacket Buffer"); - return -1; - } - - // Register RED and CN - int regInNeteq = 0; - for (i = (ACMCodecDB::_noOfCodecs - 1); i>-1; i--) { - if((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "red") == 0)) { - regInNeteq = 1; - } else if ((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "CN") == 0)) { - regInNeteq = 1; - } - - if (regInNeteq == 1) { - if(RegisterRecCodecMSSafe(ACMCodecDB::_mycodecs[i], i, i, - ACMNetEQ::masterJB) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot register master codec."); - return -1; - } - _registeredPlTypes[i] = ACMCodecDB::_mycodecs[i].pltype; - regInNeteq = 0; - } - } - - _receiverInitialized = true; - return 0; -} - -// Reset the decoder state -WebRtc_Word32 -AudioCodingModuleImpl::ResetDecoder() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ResetDecoder()"); - CriticalSectionScoped lock(*_acmCritSect); - - for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) - { - if((_codecs[codecCntr] != NULL) && (_registeredPlTypes[codecCntr] != -1)) - { - if(_codecs[codecCntr]->ResetDecoder(_registeredPlTypes[codecCntr]) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "ResetDecoder failed:"); - return -1; - } - } - } - return _netEq.FlushBuffers(); -} - -// get current receive freq -WebRtc_Word32 -AudioCodingModuleImpl::ReceiveFrequency() const -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "ReceiveFrequency()"); - WebRtcACMCodecParams codecParams; - - CriticalSectionScoped lock(*_acmCritSect); - if(DecoderParamByPlType(_lastRecvAudioCodecPlType, codecParams) < 0) - { - return _netEq.CurrentSampFreqHz(); - } - else - { - return codecParams.codecInstant.plfreq; - } -} - -// get current playout freq -WebRtc_Word32 -AudioCodingModuleImpl::PlayoutFrequency() const -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "PlayoutFrequency()"); - - CriticalSectionScoped lock(*_acmCritSect); - - return _netEq.CurrentSampFreqHz(); -} - - -// register possible reveive codecs, can be called multiple times, -// for codecs, CNG (NB, WB and SWB), DTMF, RED -WebRtc_Word32 -AudioCodingModuleImpl::RegisterReceiveCodec( - const CodecInst& receiveCodec) -{ - CriticalSectionScoped lock(*_acmCritSect); - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "RegisterReceiveCodec()"); - - if(receiveCodec.channels > 2) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "More than 2 audio channel is not supported."); - return -1; - } - - WebRtc_Word16 mirrorId; - WebRtc_Word16 codecId = ACMCodecDB::ReceiverCodecNumber(receiveCodec, mirrorId); - - if(codecId < 0 || codecId >= ACMCodecDB::NoOfCodecs()) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Wrong codec params to be registered as receive codec"); - return -1; - } - // Check if the payload-type is valid. - if(ACMCodecDB::ValidPayloadType(receiveCodec.pltype) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Invalid payload-type %d for %s.", - receiveCodec.pltype, receiveCodec.plname); - return -1; - } - - if(!_receiverInitialized) - { - if(InitializeReceiverSafe() < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot initialize reciver, so failed registering a codec."); - return -1; - } - } - - // If codec already registered, start with unregistering - if(_registeredPlTypes[codecId] != -1) - { - if(UnregisterReceiveCodecSafe(codecId) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot register master codec."); - return -1; - } - } - - if(RegisterRecCodecMSSafe(receiveCodec, codecId, mirrorId, - ACMNetEQ::masterJB) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot register master codec."); - return -1; - } - - - // If receive stereo, make sure we have two instances of NetEQ, one for each channel - if(receiveCodec.channels == 2) - { - if(_netEq.NumSlaves() < 1) - { - if(_netEq.AddSlave(ACMCodecDB::NetEqDecoders(), - ACMCodecDB::NoNetEqDecoders()) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot Add Slave jitter buffer to NetEq."); - return -1; - } - } - - if(RegisterRecCodecMSSafe(receiveCodec, codecId, mirrorId, - ACMNetEQ::slaveJB) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot register slave codec."); - return -1; - } - - if((_stereoReceive[codecId] == false) && - (_lastRecvAudioCodecPlType == receiveCodec.pltype)) - { - _lastRecvAudioCodecPlType = -1; - } - _stereoReceive[codecId] = true; - } - else - { - _stereoReceive[codecId] = false; - } - - _registeredPlTypes[codecId] = receiveCodec.pltype; - - if(!STR_CASE_CMP(receiveCodec.plname, "RED")) - { - _receiveREDPayloadType = receiveCodec.pltype; - } - return 0; -} - - - -WebRtc_Word32 -AudioCodingModuleImpl::RegisterRecCodecMSSafe( - const CodecInst& receiveCodec, - WebRtc_Word16 codecId, - WebRtc_Word16 mirrorId, - ACMNetEQ::JB jitterBuffer) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "RegisterReceiveCodecMSSafe()"); - - ACMGenericCodec** codecArray; - if(jitterBuffer == ACMNetEQ::masterJB) - { - codecArray = &_codecs[0]; - } - else if(jitterBuffer == ACMNetEQ::slaveJB) - { - codecArray = &_slaveCodecs[0]; - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "RegisterReceiveCodecMSSafe failed, jitterBuffer is neither master or slave "); - return -1; - } - - if (codecArray[mirrorId] == NULL) - { - codecArray[mirrorId] = CreateCodec(receiveCodec); - if(codecArray[mirrorId] == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot create codec to register as receive codec"); - return -1; - } - _mirrorCodecIdx[mirrorId] = mirrorId; - } - if(mirrorId != codecId) - { - codecArray[codecId] = codecArray[mirrorId]; - _mirrorCodecIdx[codecId] = mirrorId; - } - - codecArray[codecId]->SetIsMaster(jitterBuffer == ACMNetEQ::masterJB); - - WebRtc_Word16 status = 0; - bool registerInNetEq = true; - WebRtcACMCodecParams codecParams; - memcpy(&(codecParams.codecInstant), &receiveCodec, - sizeof(CodecInst)); - codecParams.enableVAD = false; - codecParams.enableDTX = false; - codecParams.vadMode = VADNormal; - if (!codecArray[codecId]->DecoderInitialized()) - { - // force initialization - status = codecArray[codecId]->InitDecoder(&codecParams, true); - if(status < 0) - { - // could not initialize the decoder we don't want to - // continue if we could not initialize properly. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "could not initialize the receive codec, codec not registered"); - - return -1; - } - } - else if(mirrorId != codecId) - { - // Currently this only happens for iSAC. - // we have to store the decoder parameters - - codecArray[codecId]->SaveDecoderParam(&codecParams); - } - if (registerInNetEq) - { - if(codecArray[codecId]->RegisterInNetEq(&_netEq, receiveCodec) - != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Receive codec could not be registered in NetEq"); - - return -1; - } - // Guaranty that the same payload-type that is - // registered in NetEq is stored in the codec. - codecArray[codecId]->SaveDecoderParam(&codecParams); - } - - return status; -} - - - -// Get current received codec -WebRtc_Word32 -AudioCodingModuleImpl::ReceiveCodec( - CodecInst& currentReceiveCodec) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ReceiveCodec()"); - WebRtc_Word16 decCntr; - WebRtcACMCodecParams decoderParam; - CriticalSectionScoped lock(*_acmCritSect); - - for(decCntr = 0; decCntr < MAX_NR_OF_CODECS; decCntr++) - { - if(_codecs[decCntr] != NULL) - { - if(_codecs[decCntr]->DecoderInitialized()) - { - if(_codecs[decCntr]->DecoderParams(&decoderParam, - _lastRecvAudioCodecPlType)) - { - memcpy(¤tReceiveCodec, &decoderParam.codecInstant, - sizeof(CodecInst)); - return 0; - } - } - } - } - - // if we are here then we haven't found any codec - // set codec pltype to -1 to indicate that the structure - // is invalid and return -1. - currentReceiveCodec.pltype = -1; - return -1; -} - -// Incoming packet from network parsed and ready for decode -WebRtc_Word32 -AudioCodingModuleImpl::IncomingPacket( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtcRTPHeader& rtpInfo) -{ - - if (payloadLength < 0) - { - // Log error - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "IncomingPacket() Error, payload-length cannot be negative"); - return -1; - } - { - // store the payload Type. this will be used to retrieve "received codec" - // and "received frequency." - CriticalSectionScoped lock(*_acmCritSect); -#ifdef ACM_QA_TEST - if(_incomingPL != NULL) - { - fwrite(&rtpInfo.header.timestamp, sizeof(WebRtc_UWord32), 1, _incomingPL); - fwrite(&rtpInfo.header.payloadType, sizeof(WebRtc_UWord8), 1, _incomingPL); - fwrite(&payloadLength, sizeof(WebRtc_Word16), 1, _incomingPL); - } -#endif - - WebRtc_UWord8 myPayloadType; - - // Check if this is an RED payload - if(rtpInfo.header.payloadType == _receiveREDPayloadType) - { - // get the primary payload-type. - myPayloadType = (WebRtc_UWord8)(incomingPayload[0] & 0x7F); - } - else - { - myPayloadType = rtpInfo.header.payloadType; - } - - // If payload is audio, check if received payload is different from previous - if((!rtpInfo.type.Audio.isCNG) && - (myPayloadType != _cngNB.pltype) && - (myPayloadType != _cngWB.pltype) && - (myPayloadType != _cngSWB.pltype)) - { - // This is Audio not CNG - - if(myPayloadType != _lastRecvAudioCodecPlType) - { - // We detect a change in payload type. It is necessary for iSAC - // we are going to use ONE iSAC instance for decoding both WB and - // SWB payloads. If payload is changed there might be a need to reset - // sampling rate of decoder. depending what we have received "now". - // TODO (tlegrand): Change or remove the following comment - // I cannot use the function that BV has written, i.e. - // "DecoderParamByPlType()" as for iSAC there is one instance and - // multiple payloads. - int i; - for(i = 0; i < MAX_NR_OF_CODECS; i++) - { - if(_registeredPlTypes[i] == myPayloadType) - { - if(_codecs[i] == NULL) - { - // we found a payload type but the corresponding - // codec is NULL this should not happen - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "IncomingPacket() Error, payload type found but corresponding " - "codec is NULL"); - return -1; - } - _codecs[i]->UpdateDecoderSampFreq(i); - _netEq.SetReceivedStereo(_stereoReceive[i]); - break; - } - } - } - _lastRecvAudioCodecPlType = myPayloadType; - } - } - - return _netEq.RecIn(incomingPayload, payloadLength, rtpInfo); -} - -// Minimum playout delay (Used for lip-sync) -WebRtc_Word32 -AudioCodingModuleImpl::SetMinimumPlayoutDelay( - const WebRtc_Word32 timeMs) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetMinimumPlayoutDelay()"); - if((timeMs < 0) || (timeMs > 1000)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Delay must be in the range of 0-1000 milliseconds."); - return -1; - } - return _netEq.SetExtraDelay(timeMs); -} - -// current play out delay -WebRtc_Word32 -AudioCodingModuleImpl::Delay( - WebRtc_UWord16& delayMs) const -{ - // NetEQ will get an API for this later. - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "Delay()"); - return _netEq.Delay(delayMs); -} - -// Get Dtmf playout status -bool -AudioCodingModuleImpl::DtmfPlayoutStatus() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "DtmfPlayoutStatus()"); -#ifndef WEBRTC_CODEC_AVT - return false; -#else - return _netEq.AVTPlayout(); -#endif -} - -// configure Dtmf playout status i.e on/off -// playout the incoming outband Dtmf tone -WebRtc_Word32 -AudioCodingModuleImpl::SetDtmfPlayoutStatus( -#ifndef WEBRTC_CODEC_AVT - const bool /* enable */) -{ - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, - "SetDtmfPlayoutStatus() failed: AVT is not supported."); - return -1; -#else - const bool enable) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetDtmfPlayoutStatus()"); - return _netEq.SetAVTPlayout(enable); -#endif -} - -// Estimate the Bandwidth based on the incoming stream -// This is also done in the RTP module -// need this for one way audio where the RTCP send the BW estimate -WebRtc_Word32 -AudioCodingModuleImpl::DecoderEstimatedBandwidth() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "DecoderEstimatedBandwidth()"); - - CodecInst codecInst; - WebRtc_Word16 codecID = -1; - int plTypWB; - int plTypSWB; - - // Get iSAC settings - for(WebRtc_Word16 codecCntr = 0; codecCntr < ACMCodecDB::NoOfCodecs(); - codecCntr++) - { - // Store codec settings for codec number "codeCntr" in the output struct - ACMCodecDB::Codec(codecCntr, &codecInst); - - if(!STR_CASE_CMP(codecInst.plname, "isac")) - { - codecID = 1; - plTypWB = codecInst.pltype; - - ACMCodecDB::Codec(codecCntr+1, &codecInst); - plTypSWB = codecInst.pltype; - - break; - } - } - - if(codecID < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "DecoderEstimatedBandwidth failed"); - return -1; - } - - if ((_lastRecvAudioCodecPlType == plTypWB) || (_lastRecvAudioCodecPlType == plTypSWB)) - { - return _codecs[codecID]->GetEstimatedBandwidth(); - } else { - return -1; - } -} - -// Set playout mode for: voice, fax, or streaming -WebRtc_Word32 -AudioCodingModuleImpl::SetPlayoutMode( - const AudioPlayoutMode mode) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetPlayoutMode()"); - if((mode != voice) && - (mode != fax) && - (mode != streaming)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Invalid playout mode."); - return -1; - } - return _netEq.SetPlayoutMode(mode); -} - -// Get playout mode voice, fax -AudioPlayoutMode -AudioCodingModuleImpl::PlayoutMode() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "PlayoutMode()"); - return _netEq.PlayoutMode(); -} - - -// Get 10 milliseconds of raw audio data to play out -// automatic resample to the requested frequency -WebRtc_Word32 -AudioCodingModuleImpl::PlayoutData10Ms( - const WebRtc_Word32 desiredFreqHz, - AudioFrame& audioFrame) -{ - bool stereoMode; - AudioFrame audioFrameTmp; - - // recOut always returns 10 ms - if (_netEq.RecOut(audioFrameTmp) != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "PlayoutData failed, RecOut Failed"); - return -1; - } - - audioFrame._audioChannel = audioFrameTmp._audioChannel; - audioFrame._vadActivity = audioFrameTmp._vadActivity; - audioFrame._speechType = audioFrameTmp._speechType; - - stereoMode = (audioFrameTmp._audioChannel > 1); - //For stereo playout: - // Master and Slave samples are interleaved starting with Master - - const WebRtc_UWord16 recvFreq = static_cast(audioFrameTmp._frequencyInHz); - bool toneDetected = false; - WebRtc_Word16 lastDetectedTone; - WebRtc_Word16 tone; - - // limit the scope of ACM Critical section - // perhaps we don't need to have output resampler in - // critical section, it is supposed to be called in this - // function and no where else. However, it won't degrade complexity - { - CriticalSectionScoped lock(*_acmCritSect); - - if ((recvFreq != desiredFreqHz) && (desiredFreqHz != -1)) - { - // resample payloadData - WebRtc_Word16 tmpLen = _outputResampler.Resample10Msec( - audioFrameTmp._payloadData, recvFreq, audioFrame._payloadData, desiredFreqHz, - audioFrameTmp._audioChannel); - - if(tmpLen < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "PlayoutData failed, resampler failed"); - return -1; - } - - //Set the payload data length from the resampler - audioFrame._payloadDataLengthInSamples = (WebRtc_UWord16)tmpLen; - // set the ssampling frequency - audioFrame._frequencyInHz = desiredFreqHz; - } - else - { - memcpy(audioFrame._payloadData, audioFrameTmp._payloadData, - audioFrameTmp._payloadDataLengthInSamples * audioFrame._audioChannel - * sizeof(WebRtc_Word16)); - // set the payload length - audioFrame._payloadDataLengthInSamples = audioFrameTmp._payloadDataLengthInSamples; - // set the sampling frequency - audioFrame._frequencyInHz = recvFreq; - } - - //Tone detection done for master channel - if(_dtmfDetector != NULL) - { - // Dtmf Detection - if(audioFrame._frequencyInHz == 8000) - { - // use audioFrame._payloadData then Dtmf detector doesn't - // need resampling - if(!stereoMode) - { - _dtmfDetector->Detect(audioFrame._payloadData, - audioFrame._payloadDataLengthInSamples, - audioFrame._frequencyInHz, toneDetected, tone); - } - else - { - // we are in 8 kHz so the master channel needs only 80 samples - WebRtc_Word16 masterChannel[80]; - for(WebRtc_Word16 n = 0; n < 80; n++) - { - masterChannel[n] = audioFrame._payloadData[n<<1]; - } - _dtmfDetector->Detect(audioFrame._payloadData, - audioFrame._payloadDataLengthInSamples, - audioFrame._frequencyInHz, toneDetected, tone); - } - } - else - { - // Do the detection on the audio that we got from NetEQ (audioFrameTmp). - if(!stereoMode) - { - _dtmfDetector->Detect(audioFrameTmp._payloadData, - audioFrameTmp._payloadDataLengthInSamples, recvFreq, - toneDetected, tone); - } - else - { - WebRtc_Word16 masterChannel[WEBRTC_10MS_PCM_AUDIO]; - WebRtc_Word16 n; - for(n = 0; n < audioFrameTmp._payloadDataLengthInSamples; n++) - { - masterChannel[n] = audioFrameTmp._payloadData[n<<1]; - } - _dtmfDetector->Detect(audioFrameTmp._payloadData, - audioFrameTmp._payloadDataLengthInSamples, recvFreq, - toneDetected, tone); - } - } - } - - // we want to do this while we are in _acmCritSect - // doesn't really need to initialize the following - // variable but Linux complains if we don't - lastDetectedTone = kACMToneEnd; - if(toneDetected) - { - lastDetectedTone = _lastDetectedTone; - _lastDetectedTone = tone; - } - } - - if(toneDetected) - { - // we will deal with callback here, so enter callback critical - // section - CriticalSectionScoped lock(*_callbackCritSect); - - if(_dtmfCallback != NULL) - { - if(tone != kACMToneEnd) - { - // just a tone - _dtmfCallback->IncomingDtmf((WebRtc_UWord8)tone, false); - } - else if((tone == kACMToneEnd) && - (lastDetectedTone != kACMToneEnd)) - { - // The tone is "END" and the previously detected tone is - // not "END," so call fir an end. - _dtmfCallback->IncomingDtmf((WebRtc_UWord8)lastDetectedTone, - true); - } - } - } - - audioFrame._id = _id; - audioFrame._volume = -1; - audioFrame._energy = -1; - audioFrame._timeStamp = 0; - - return 0; -} - - - -///////////////////////////////////////// -// (CNG) Comfort Noise Generation -// Generate comfort noise when receiving DTX packets -// - -// Get VAD status on the incoming stream -bool -AudioCodingModuleImpl::ReceiveVADStatus() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ReceiveVADStatus()"); - return _netEq.VADStatus(); -} - - -// configure VAD status i.e on/off on the incoming stream -WebRtc_Word16 -AudioCodingModuleImpl::SetReceiveVADStatus( - const bool enable) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetReceiveVADStatus()"); - return _netEq.SetVADStatus(enable); -} - -// Get VAD aggressiveness on the incoming stream -ACMVADMode -AudioCodingModuleImpl::ReceiveVADMode() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ReceiveVADMode()"); - return _netEq.VADMode(); -} - -// Configure VAD aggressiveness on the incoming stream -WebRtc_Word16 -AudioCodingModuleImpl::SetReceiveVADMode( - const ACMVADMode mode) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetReceiveVADMode()"); - return _netEq.SetVADMode(mode); -} - -///////////////////////////////////////// -// statistics -// - -WebRtc_Word32 -AudioCodingModuleImpl::NetworkStatistics( - ACMNetworkStatistics& statistics) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "NetworkStatistics()"); - WebRtc_Word32 status; - status = _netEq.NetworkStatistics(&statistics); - return status; -} - -WebRtc_Word32 -AudioCodingModuleImpl::JitterStatistics( - ACMJitterStatistics& jitterStatistics) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "JitterStatistics()"); - return _netEq.JitterStatistics(&jitterStatistics); -} - -WebRtc_Word32 -AudioCodingModuleImpl::PreferredBufferSize( - WebRtc_UWord16& prefbufsize) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "PreferedBufferSize()"); - return _netEq.PreferredBufferSize(&prefbufsize); -} - -WebRtc_Word32 -AudioCodingModuleImpl::ResetJitterStatistics() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ResetJitterStatistics()"); - return _netEq.ResetJitterStatistics(); -} - -void -AudioCodingModuleImpl::DestructEncoderInst( - void* ptrInst) -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, - "DestructEncoderInst()"); - if(!HaveValidEncoder("DestructEncoderInst")) - { - return; - } - - _codecs[_currentSendCodecIdx]->DestructEncoderInst(ptrInst); -} - -WebRtc_Word16 -AudioCodingModuleImpl::AudioBuffer( - WebRtcACMAudioBuff& audioBuff) -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, - "AudioBuffer()"); - if(!HaveValidEncoder("AudioBuffer")) - { - return -1; - } - - audioBuff.lastInTimestamp = _lastInTimestamp; - return _codecs[_currentSendCodecIdx]->AudioBuffer(audioBuff); -} - -WebRtc_Word16 -AudioCodingModuleImpl::SetAudioBuffer( - WebRtcACMAudioBuff& audioBuff) -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, - "SetAudioBuffer()"); - if(!HaveValidEncoder("SetAudioBuffer")) - { - return -1; - } - - return _codecs[_currentSendCodecIdx]->SetAudioBuffer(audioBuff); -} - - -WebRtc_UWord32 -AudioCodingModuleImpl::EarliestTimestamp() const -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, - "EarliestTimestamp()"); - if(!HaveValidEncoder("EarliestTimestamp")) - { - return -1; - } - - return _codecs[_currentSendCodecIdx]->EarliestTimestamp(); -} - -WebRtc_Word32 -AudioCodingModuleImpl::RegisterVADCallback( - ACMVADCallback* vadCallback) -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, - "RegisterVADCallback()"); - CriticalSectionScoped lock(*_callbackCritSect); - _vadCallback = vadCallback; - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::IncomingPayload( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timestamp) -{ - if (payloadLength < 0) - { - // Log error in trace file. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "IncomingPacket() Error, payload-length cannot be negative"); - return -1; - } - - if(_dummyRTPHeader == NULL) - { - // This is the first time that we are using _dummyRTPHeader - // so we have to create it. - WebRtcACMCodecParams codecParams; - _dummyRTPHeader = new WebRtcRTPHeader; - if (_dummyRTPHeader == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "IncomingPacket() Error, out of memory"); - return -1; - } - _dummyRTPHeader->header.payloadType = payloadType; - // Don't matter in this case - _dummyRTPHeader->header.ssrc = 0; - _dummyRTPHeader->header.markerBit = false; - // start with random numbers - _dummyRTPHeader->header.sequenceNumber = rand(); - _dummyRTPHeader->header.timestamp = (((WebRtc_UWord32)rand()) << 16) + - (WebRtc_UWord32)rand(); - _dummyRTPHeader->type.Audio.channel = 1; - - if(DecoderParamByPlType(payloadType, codecParams) < 0) - { - // we didn't find a codec with the given payload. - // something is wrong we exit, but we delete _dummyRTPHeader - // and set it to NULL to start clean next time - delete _dummyRTPHeader; - _dummyRTPHeader = NULL; - return -1; - } - _recvPlFrameSizeSmpls = codecParams.codecInstant.pacsize; - } - - if(payloadType != _dummyRTPHeader->header.payloadType) - { - // payload type has changed since the last time we might need to - // update the frame-size - WebRtcACMCodecParams codecParams; - if(DecoderParamByPlType(payloadType, codecParams) < 0) - { - // we didn't find a codec with the given payload. - // something is wrong we exit - return -1; - } - _recvPlFrameSizeSmpls = codecParams.codecInstant.pacsize; - _dummyRTPHeader->header.payloadType = payloadType; - } - - if(timestamp > 0) - { - _dummyRTPHeader->header.timestamp = timestamp; - } - - // store the payload Type. this will be used to retrieve "received codec" - // and "received frequency." - _lastRecvAudioCodecPlType = payloadType; - - // Insert in NetEQ - if(_netEq.RecIn(incomingPayload, payloadLength, (*_dummyRTPHeader)) < 0) - { - return -1; - } - - // get ready for the next payload - _dummyRTPHeader->header.sequenceNumber++; - _dummyRTPHeader->header.timestamp += _recvPlFrameSizeSmpls; - return 0; -} - -WebRtc_Word16 -AudioCodingModuleImpl::DecoderParamByPlType( - const WebRtc_UWord8 payloadType, - WebRtcACMCodecParams& codecParams) const -{ - CriticalSectionScoped lock(*_acmCritSect); - for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) - { - if(_codecs[codecCntr] != NULL) - { - if(_codecs[codecCntr]->DecoderInitialized()) - { - if(_codecs[codecCntr]->DecoderParams(&codecParams, - payloadType)) - { - return 0; - } - } - } - } - // if we are here it means that we could not find a - // codec with that payload type. reset the values to - // not acceptable values and return -1; - codecParams.codecInstant.plname[0] = '\0'; - codecParams.codecInstant.pacsize = 0; - codecParams.codecInstant.rate = 0; - codecParams.codecInstant.pltype = -1; - return -1; -} - - - -WebRtc_Word16 -AudioCodingModuleImpl::DecoderListIDByPlName( - const WebRtc_Word8* payloadName, - const WebRtc_UWord16 sampFreqHz) const -{ - WebRtcACMCodecParams codecParams; - CriticalSectionScoped lock(*_acmCritSect); - for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) - { - if((_codecs[codecCntr] != NULL)) - { - if(_codecs[codecCntr]->DecoderInitialized()) - { - assert(_registeredPlTypes[codecCntr] >= 0); - assert(_registeredPlTypes[codecCntr] <= 255); - _codecs[codecCntr]->DecoderParams(&codecParams, - (WebRtc_UWord8)_registeredPlTypes[codecCntr]); - if(!STR_CASE_CMP(codecParams.codecInstant.plname, payloadName)) - { - // Check if the given sampling frequency matches. - // A zero sampling frequency means we matching the names - // is sufficient and we don't need to check for the - // frequencies. - // Currently it is only iSAC which has one name but two - // sampling frequencies. - if((sampFreqHz == 0) || - (codecParams.codecInstant.plfreq == sampFreqHz)) - { - return codecCntr; - } - } - } - } - } - // if we are here it means that we could not find a - // codec with that payload type. return -1; - return -1; -} - -WebRtc_Word32 -AudioCodingModuleImpl::LastEncodedTimestamp(WebRtc_UWord32& timestamp) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "LastEncodedTimestamp()"); - CriticalSectionScoped lock(*_acmCritSect); - if(!HaveValidEncoder("LastEncodedTimestamp")) - { - return -1; - } - timestamp = _codecs[_currentSendCodecIdx]->LastEncodedTimestamp(); - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::ReplaceInternalDTXWithWebRtc(bool useWebRtcDTX) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ReplaceInternalDTXWithWebRtc()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("ReplaceInternalDTXWithWebRtc")) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Cannot replace codec internal DTX when no send codec is registered."); - return -1; - } - - WebRtc_Word32 res = _codecs[_currentSendCodecIdx]->ReplaceInternalDTX(useWebRtcDTX); - // Check if VAD is turned on, or if there is any error - if(res == 1) - { - _vadEnabled = true; - } else if(res < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Failed to set ReplaceInternalDTXWithWebRtc(%d)", useWebRtcDTX); - return res; - } - - return 0; -} - -WebRtc_Word32 -AudioCodingModuleImpl::IsInternalDTXReplacedWithWebRtc(bool& usesWebRtcDTX) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "IsInternalDTXReplacedWithWebRtc()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("IsInternalDTXReplacedWithWebRtc")) - { - return -1; - } - if(_codecs[_currentSendCodecIdx]->IsInternalDTXReplaced(&usesWebRtcDTX) < 0) - { - return -1; - } - return 0; -} - - -WebRtc_Word32 -AudioCodingModuleImpl::SetISACMaxRate( - const WebRtc_UWord32 maxRateBitPerSec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetISACMaxRate()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("SetISACMaxRate")) - { - return -1; - } - - return _codecs[_currentSendCodecIdx]->SetISACMaxRate(maxRateBitPerSec); -} - - -WebRtc_Word32 -AudioCodingModuleImpl::SetISACMaxPayloadSize( - const WebRtc_UWord16 maxPayloadLenBytes) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetISACPayloadSize()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("SetISACMaxPayloadSize")) - { - return -1; - } - - return _codecs[_currentSendCodecIdx]->SetISACMaxPayloadSize(maxPayloadLenBytes); -} - -WebRtc_Word32 -AudioCodingModuleImpl::ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "ConfigISACBandwidthEstimator()"); - CriticalSectionScoped lock(*_acmCritSect); - - if(!HaveValidEncoder("ConfigISACBandwidthEstimator")) - { - return -1; - } - - return _codecs[_currentSendCodecIdx]->ConfigISACBandwidthEstimator( - initFrameSizeMsec, initRateBitPerSec, enforceFrameSize); -} - -WebRtc_Word32 -AudioCodingModuleImpl::SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "SetBackgroundNoiseMode()"); - if((mode < On) || - (mode > Off)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "The specified background noise is out of range.\n"); - return -1; - } - return _netEq.SetBackgroundNoiseMode(mode); -} - -WebRtc_Word32 -AudioCodingModuleImpl::BackgroundNoiseMode( - ACMBackgroundNoiseMode& mode) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "BackgroundNoiseMode()"); - return _netEq.BackgroundNoiseMode(mode); -} - -WebRtc_Word32 -AudioCodingModuleImpl::PlayoutTimestamp( - WebRtc_UWord32& timestamp) -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, - "PlayoutTimestamp()"); - return _netEq.PlayoutTimestamp(timestamp); -} - - - - - -bool -AudioCodingModuleImpl::HaveValidEncoder( - const WebRtc_Word8* callerName) const -{ - WebRtc_Word16 numCodecs = ACMCodecDB::NoOfCodecs(); - if((!_sendCodecRegistered) || - (_currentSendCodecIdx < 0) || - (_currentSendCodecIdx >= numCodecs)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "%s failed: No send codec is registered.", callerName); - return false; - } - if((_currentSendCodecIdx < 0) || - (_currentSendCodecIdx >= numCodecs)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "%s failed: Send codec index out of range.", callerName); - return false; - } - if(_codecs[_currentSendCodecIdx] == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "%s failed: Send codec is NULL pointer.", callerName); - return false; - } - return true; -} - -WebRtc_Word32 -AudioCodingModuleImpl::UnregisterReceiveCodec( - const WebRtc_Word16 payloadType) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, - "UnregisterReceiveCodec()"); - CriticalSectionScoped lock(*_acmCritSect); - WebRtc_Word16 codecID; - - // Search through the list of registered payload types - for (codecID = 0; codecID < MAX_NR_OF_CODECS; codecID++) - { - if (_registeredPlTypes[codecID] == payloadType) - { - // we have found the codecID registered with the payload type - break; - } - } - - if(codecID >= ACMCodecDB::NoOfCodecs()) - { - // payload type was not registered. No need to unregister - return 0; - } - else if(codecID < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "UnregisterReceiveCodec() failed: the given codec, %s, is not supported", - payloadType); - return -1; - } - - // Unregister the codec with the given payload type - return UnregisterReceiveCodecSafe(codecID); -} - -WebRtc_Word32 -AudioCodingModuleImpl::UnregisterReceiveCodecSafe( - const WebRtc_Word16 codecID) -{ - WebRtcNetEQDecoder *neteqDecoder = ACMCodecDB::NetEqDecoders(); - WebRtc_Word16 mirrorID = ACMCodecDB::MirrorID(codecID); - if(_codecs[codecID] != NULL) - { - if(_registeredPlTypes[codecID] != -1) - { - // before deleting the decoder instance unregister - // from NetEq. - if(_netEq.RemoveCodec(neteqDecoder[codecID], _stereoReceive[codecID]) < 0) - { - CodecInst codecInst; - ACMCodecDB::Codec(codecID, &codecInst); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, - "Unregistering %s-%d from NetEq failed.", - codecInst.plname, codecInst.plfreq); - return -1; - } - - // CN is a special case for NetEQ, all three sampling frequencies are - // deletad if one is deleted - if(STR_CASE_CMP(ACMCodecDB::_mycodecs[codecID].plname, "CN") == 0) - { - // Search codecs nearby in the database to unregister all CN. - for (int i=-2; i<3; i++) - { - if (STR_CASE_CMP(ACMCodecDB::_mycodecs[codecID+i].plname, "CN") == 0) - { - _codecs[codecID+i]->DestructDecoder(); - if(_stereoReceive[codecID+i]) - { - _slaveCodecs[codecID+i]->DestructDecoder(); - } - _registeredPlTypes[codecID+i] = -1; - } - } - } else - { - if(codecID == mirrorID) - { - _codecs[codecID]->DestructDecoder(); - if(_stereoReceive[codecID]) - { - _slaveCodecs[codecID]->DestructDecoder(); - } - } - } - } - } - - if(_registeredPlTypes[codecID] == _receiveREDPayloadType) - { - // RED is going to be unregistered. - // set the following to an invalid value. - _receiveREDPayloadType = 255; - } - _registeredPlTypes[codecID] = -1; - - return 0; -} - - -WebRtc_Word32 -AudioCodingModuleImpl::REDPayloadISAC( - const WebRtc_Word32 isacRate, - const WebRtc_Word16 isacBwEstimate, - WebRtc_UWord8* payload, - WebRtc_Word16* payloadLenByte) -{ - - if(!HaveValidEncoder("EncodeData")) - { - return -1; - } - WebRtc_Word16 status; - - status = _codecs[_currentSendCodecIdx]->REDPayloadISAC(isacRate, isacBwEstimate, - payload, payloadLenByte); - - return status; -} - -} // namespace webrtc diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.h b/modules/audio_coding/main/source/audio_coding_module_impl.h deleted file mode 100644 index e39c1157a..000000000 --- a/modules/audio_coding/main/source/audio_coding_module_impl.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef AUDIO_CODING_MODULE_IMPL_H -#define AUDIO_CODING_MODULE_IMPL_H - -#include "acm_codec_database.h" -#include "acm_neteq.h" -#include "acm_resampler.h" -#include "common_types.h" -#include "engine_configurations.h" - -namespace webrtc { - -class ACMDTMFDetection; -class ACMGenericCodec; -class CriticalSectionWrapper; -class RWLockWrapper; - -//#define TIMED_LOGGING - -#ifdef TIMED_LOGGING - #include "../test/timedtrace.h" -#endif - -#ifdef ACM_QA_TEST -# include -#endif - -class AudioCodingModuleImpl : public AudioCodingModule -{ -public: - // constructor - AudioCodingModuleImpl( - const WebRtc_Word32 id); - - // destructor - ~AudioCodingModuleImpl(); - - // get version information for ACM and all components - WebRtc_Word32 Version( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - // change the unique identifier of this object - virtual WebRtc_Word32 ChangeUniqueId( - const WebRtc_Word32 id); - - // returns the number of milliseconds until the module want - // a worker thread to call Process - WebRtc_Word32 TimeUntilNextProcess(); - - // Process any pending tasks such as timeouts - WebRtc_Word32 Process(); - - // used in conference to go to and from active encoding, hence - // in and out of mix - WebRtc_Word32 SetMode( - const bool passive); - - - - ///////////////////////////////////////// - // Sender - // - - // initialize send codec - WebRtc_Word32 InitializeSender(); - - // reset send codec - WebRtc_Word32 ResetEncoder(); - - // can be called multiple times for Codec, CNG, RED - WebRtc_Word32 RegisterSendCodec( - const CodecInst& sendCodec); - - // get current send codec - WebRtc_Word32 SendCodec( - CodecInst& currentSendCodec) const; - - // get current send freq - WebRtc_Word32 SendFrequency() const; - - // Get encode bitrate - // Adaptive rate codecs return their current encode target rate, while other codecs - // return there longterm avarage or their fixed rate. - WebRtc_Word32 SendBitrate() const; - - // set available bandwidth, inform the encoder about the - // estimated bandwidth received from the remote party - virtual WebRtc_Word32 SetReceivedEstimatedBandwidth( - const WebRtc_Word32 bw); - - // register a transport callback which will be - // called to deliver the encoded buffers - WebRtc_Word32 RegisterTransportCallback( - AudioPacketizationCallback* transport); - - // Used by the module to deliver messages to the codec module/application - // AVT(DTMF) - WebRtc_Word32 RegisterIncomingMessagesCallback( - AudioCodingFeedback* incomingMessagesCallback, - const ACMCountries cpt); - - // Add 10MS of raw (PCM) audio data to the encoder - WebRtc_Word32 Add10MsData( - const AudioFrame& audioFrame); - - // set background noise mode for NetEQ, on, off or fade - WebRtc_Word32 SetBackgroundNoiseMode( - const ACMBackgroundNoiseMode mode); - - // get current background noise mode - WebRtc_Word32 BackgroundNoiseMode( - ACMBackgroundNoiseMode& mode); - - ///////////////////////////////////////// - // (FEC) Forward Error Correction - // - - // configure FEC status i.e on/off - WebRtc_Word32 SetFECStatus( - const bool enable); - - // Get FEC status - bool FECStatus() const; - - ///////////////////////////////////////// - // (VAD) Voice Activity Detection - // and - // (CNG) Comfort Noise Generation - // - - WebRtc_Word32 SetVAD( - const bool enableDTX = true, - const bool enableVAD = false, - const ACMVADMode vadMode = VADNormal); - - WebRtc_Word32 VAD( - bool& dtxEnabled, - bool& vadEnabled, - ACMVADMode& vadMode) const; - - WebRtc_Word32 RegisterVADCallback( - ACMVADCallback* vadCallback); - - // Get VAD status on the incoming stream - bool ReceiveVADStatus() const; - - // configure VAD status i.e on/off on the incoming stream - WebRtc_Word16 SetReceiveVADStatus( - const bool enable); - - // Get VAD aggressiveness on the incoming stream - ACMVADMode ReceiveVADMode() const; - - // Configure VAD aggressiveness on the incoming stream - WebRtc_Word16 SetReceiveVADMode( - const ACMVADMode mode); - - - ///////////////////////////////////////// - // Receiver - // - - // initialize receiver, resets codec database etc - WebRtc_Word32 InitializeReceiver(); - - // reset the decoder state - WebRtc_Word32 ResetDecoder(); - - // get current receive freq - WebRtc_Word32 ReceiveFrequency() const; - - // get current playout freq - WebRtc_Word32 PlayoutFrequency() const; - - // register possible reveive codecs, can be called multiple times, - // for codecs, CNG, DTMF, RED - WebRtc_Word32 RegisterReceiveCodec( - const CodecInst& receiveCodec); - - // get current received codec - WebRtc_Word32 ReceiveCodec( - CodecInst& currentReceiveCodec) const; - - // incoming packet from network parsed and ready for decode - WebRtc_Word32 IncomingPacket( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtcRTPHeader& rtpInfo); - - // Incoming payloads, without rtp-info, the rtp-info will be created in ACM. - // One usage for this API is when pre-encoded files are pushed in ACM. - WebRtc_Word32 IncomingPayload( - const WebRtc_Word8* incomingPayload, - const WebRtc_Word32 payloadLength, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timestamp = 0); - - // Minimum playout dealy (Used for lip-sync) - WebRtc_Word32 SetMinimumPlayoutDelay( - const WebRtc_Word32 timeMs); - - // current play out delay - WebRtc_Word32 Delay(WebRtc_UWord16& delayMs) const; - - // configure Dtmf playout status i.e on/off playout the incoming outband Dtmf tone - WebRtc_Word32 SetDtmfPlayoutStatus( - const bool enable); - - // Get Dtmf playout status - bool DtmfPlayoutStatus() const; - - // Estimate the Bandwidth based on the incoming stream - // This is also done in the RTP module - // need this for one way audio where the RTCP send the BW estimate - WebRtc_Word32 DecoderEstimatedBandwidth() const; - - // Set playout mode voice, fax - WebRtc_Word32 SetPlayoutMode( - const AudioPlayoutMode mode); - - // Get playout mode voice, fax - AudioPlayoutMode PlayoutMode() const; - - // Get playout timestamp - WebRtc_Word32 PlayoutTimestamp( - WebRtc_UWord32& timestamp); - - // Get 10 milliseconds of raw audio data to play out - // automatic resample to the requested frequency if > 0 - WebRtc_Word32 PlayoutData10Ms( - const WebRtc_Word32 desiredFreqHz, - AudioFrame &audioFrame); - - - ///////////////////////////////////////// - // Statistics - // - - WebRtc_Word32 NetworkStatistics( - ACMNetworkStatistics& statistics) const; - - WebRtc_Word32 JitterStatistics( - ACMJitterStatistics& jitterStatistics) const; - - WebRtc_Word32 PreferredBufferSize( - WebRtc_UWord16& prefbufsize) const; - - WebRtc_Word32 ResetJitterStatistics() const; - - void DestructEncoderInst(void* ptrInst); - - WebRtc_Word16 AudioBuffer(WebRtcACMAudioBuff& audioBuff); - - // GET RED payload for iSAC. The method id called - // when 'this' ACM is default ACM. - WebRtc_Word32 REDPayloadISAC( - const WebRtc_Word32 isacRate, - const WebRtc_Word16 isacBwEstimate, - WebRtc_UWord8* payload, - WebRtc_Word16* payloadLenByte); - - WebRtc_Word16 SetAudioBuffer(WebRtcACMAudioBuff& audioBuff); - - WebRtc_UWord32 EarliestTimestamp() const; - - WebRtc_Word32 LastEncodedTimestamp(WebRtc_UWord32& timestamp) const; - - WebRtc_Word32 ReplaceInternalDTXWithWebRtc( - const bool useWebRtcDTX); - - WebRtc_Word32 IsInternalDTXReplacedWithWebRtc( - bool& usesWebRtcDTX); - - WebRtc_Word32 SetISACMaxRate( - const WebRtc_UWord32 rateBitPerSec); - - WebRtc_Word32 SetISACMaxPayloadSize( - const WebRtc_UWord16 payloadLenBytes); - - WebRtc_Word32 ConfigISACBandwidthEstimator( - const WebRtc_UWord8 initFrameSizeMsec, - const WebRtc_UWord16 initRateBitPerSec, - const bool enforceFrameSize = false); - - WebRtc_Word32 UnregisterReceiveCodec( - const WebRtc_Word16 payloadType); - -protected: - void UnregisterSendCodec(); - - WebRtc_Word32 UnregisterReceiveCodecSafe( - const WebRtc_Word16 codecID); - - ACMGenericCodec* CreateCodec( - const CodecInst& codec); - - WebRtc_Word16 DecoderParamByPlType( - const WebRtc_UWord8 payloadType, - WebRtcACMCodecParams& codecParams) const; - - WebRtc_Word16 DecoderListIDByPlName( - const WebRtc_Word8* payloadName, - const WebRtc_UWord16 sampFreqHz = 0) const; - - WebRtc_Word32 InitializeReceiverSafe(); - - bool HaveValidEncoder(const WebRtc_Word8* callerName) const; - - WebRtc_Word32 RegisterRecCodecMSSafe( - const CodecInst& receiveCodec, - WebRtc_Word16 codecId, - WebRtc_Word16 mirrorId, - ACMNetEQ::JB jitterBuffer); - -private: - AudioPacketizationCallback* _packetizationCallback; - WebRtc_Word32 _id; - WebRtc_UWord32 _lastTimestamp; - WebRtc_UWord32 _lastInTimestamp; - CodecInst _sendCodecInst; - CodecInst _cngNB; - CodecInst _cngWB; - CodecInst _cngSWB; - CodecInst _RED; - bool _REDRegistered; - CodecInst _DTMF; - bool _DTMFRegistered; - bool _vadEnabled; - bool _dtxEnabled; - ACMVADMode _vadMode; - ACMGenericCodec* _codecs[MAX_NR_OF_CODECS]; - ACMGenericCodec* _slaveCodecs[MAX_NR_OF_CODECS]; - WebRtc_Word16 _mirrorCodecIdx[MAX_NR_OF_CODECS]; - bool _stereoReceive[MAX_NR_OF_CODECS]; - bool _stereoSend; - WebRtc_Word32 _currentSendCodecIdx; - bool _sendCodecRegistered; - ACMResampler _inputResampler; - ACMResampler _outputResampler; - ACMNetEQ _netEq; - CriticalSectionWrapper* _acmCritSect; - ACMVADCallback* _vadCallback; - WebRtc_UWord8 _lastRecvAudioCodecPlType; - - // RED/FEC - bool _isFirstRED; - bool _fecEnabled; - WebRtc_UWord8* _redBuffer; - RTPFragmentationHeader* _fragmentation; - WebRtc_UWord32 _lastFECTimestamp; - WebRtc_UWord8 _redPayloadType; - // if no RED is registered as receive codec this - // will have an invalid value. - WebRtc_UWord8 _receiveREDPayloadType; - - // This is to keep track of CN instances where we can send DTMFs - WebRtc_UWord8 _previousPayloadType; - - // This keeps track of payload types associated with _codecs[]. - // We define it as signed variable and initialize with -1 to indicate - // unused elements. - WebRtc_Word16 _registeredPlTypes[MAX_NR_OF_CODECS]; - - // Used when payloads are pushed into ACM without any RTP info - // One example is when pre-encoded bit-stream is pushed from - // a file. - WebRtcRTPHeader* _dummyRTPHeader; - WebRtc_UWord16 _recvPlFrameSizeSmpls; - - bool _receiverInitialized; - ACMDTMFDetection* _dtmfDetector; - - AudioCodingFeedback* _dtmfCallback; - WebRtc_Word16 _lastDetectedTone; - CriticalSectionWrapper* _callbackCritSect; -#ifdef TIMED_LOGGING - TimedTrace _trace; -#endif - -#ifdef ACM_QA_TEST - FILE* _outgoingPL; - FILE* _incomingPL; -#endif - -}; - -} // namespace webrtc - -#endif diff --git a/modules/audio_coding/main/test/ACMTest.cpp b/modules/audio_coding/main/test/ACMTest.cpp deleted file mode 100644 index 1bbac0e35..000000000 --- a/modules/audio_coding/main/test/ACMTest.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ACMTest.h" - -ACMTest::~ACMTest() -{ -} - diff --git a/modules/audio_coding/main/test/ACMTest.h b/modules/audio_coding/main/test/ACMTest.h deleted file mode 100644 index e96567143..000000000 --- a/modules/audio_coding/main/test/ACMTest.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACMTEST_H -#define ACMTEST_H - -class ACMTest -{ -public: - virtual ~ACMTest() =0; - virtual void Perform() =0; -}; - -#endif diff --git a/modules/audio_coding/main/test/APITest.cpp b/modules/audio_coding/main/test/APITest.cpp deleted file mode 100644 index 98c39052b..000000000 --- a/modules/audio_coding/main/test/APITest.cpp +++ /dev/null @@ -1,1602 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include -#include -#include - -#include "APITest.h" -#include "thread_wrapper.h" -#include "event_wrapper.h" -#include "tick_util.h" -#include "trace.h" -#include "utility.h" -#include "common_types.h" -#include "engine_configurations.h" - -#define TEST_DURATION_SEC 600 - -#define NUMBER_OF_SENDER_TESTS 6 - -#define MAX_FILE_NAME_LENGTH_BYTE 500 -#define CHECK_THREAD_NULLITY(myThread, S) if(myThread != NULL){unsigned int i; (myThread)->Start(i);}else{throw S; exit(1);} - -using namespace webrtc; - -void -APITest::Wait(WebRtc_UWord32 waitLengthMs) -{ - if(_randomTest) - { - return; - } - else - { - EventWrapper* myEvent = EventWrapper::Create(); - myEvent->Wait(waitLengthMs); - delete myEvent; - return; - } -} - - - -APITest::APITest(): -_acmA(NULL), -_acmB(NULL), -_channel_A2B(NULL), -_channel_B2A(NULL), -_writeToFile(true), -_pullEventA(NULL), -_pushEventA(NULL), -_processEventA(NULL), -_apiEventA(NULL), -_pullEventB(NULL), -_pushEventB(NULL), -_processEventB(NULL), -_apiEventB(NULL), -_codecCntrA(0), -_codecCntrB(0), -_testCntrA(1), -_testCntrB(1), -_thereIsEncoderA(false), -_thereIsEncoderB(false), -_thereIsDecoderA(false), -_thereIsDecoderB(false), -_sendVADA(false), -_sendDTXA(false), -_sendVADModeA(VADNormal), -_sendVADB(false), -_sendDTXB(false), -_sendVADModeB(VADNormal), -_minDelayA(0), -_minDelayB(0), -_dotPositionA(0), -_dotMoveDirectionA(1), -_dotPositionB(39), -_dotMoveDirectionB(-1), -_dtmfCallback(NULL), -_vadCallbackA(NULL), -_vadCallbackB(NULL), -_apiTestRWLock(*RWLockWrapper::CreateRWLock()), -_randomTest(false), -_testNumA(0), -_testNumB(1) -{ - int n; - for( n = 0; n < 32; n++) - { - _payloadUsed[n] = false; - } - - for(n = 0; n < 3; n++) - { - _receiveVADActivityA[n] = 0; - _receiveVADActivityB[n] = 0; - } - - _movingDot[40] = '\0'; - - for(int n = 0; n <40; n++) - { - _movingDot[n] = ' '; - } -} - -APITest::~APITest() -{ - DESTROY_ACM(_acmA); - DESTROY_ACM(_acmB); - - DELETE_POINTER(_channel_A2B); - DELETE_POINTER(_channel_B2A); - - DELETE_POINTER(_pushEventA); - DELETE_POINTER(_pullEventA); - DELETE_POINTER(_processEventA); - DELETE_POINTER(_apiEventA); - - DELETE_POINTER(_pushEventB); - DELETE_POINTER(_pullEventB); - DELETE_POINTER(_processEventB); - DELETE_POINTER(_apiEventB); - - _inFileA.Close(); - _outFileA.Close(); - - _inFileB.Close(); - _outFileB.Close(); - - DELETE_POINTER(_dtmfCallback); - DELETE_POINTER(_vadCallbackA); - DELETE_POINTER(_vadCallbackB); - - delete &_apiTestRWLock; -} - - - -//WebRtc_Word16 -//APITest::SetInFile(char* fileName, WebRtc_UWord16 frequencyHz) -//{ -// return _inFile.Open(fileName, frequencyHz, "rb"); -//} -// -//WebRtc_Word16 -//APITest::SetOutFile(char* fileName, WebRtc_UWord16 frequencyHz) -//{ -// return _outFile.Open(fileName, frequencyHz, "wb"); -//} - -WebRtc_Word16 -APITest::SetUp() -{ - _acmA = AudioCodingModule::Create(1); - _acmB = AudioCodingModule::Create(2); - - CodecInst dummyCodec; - int lastPayloadType = 0; - - WebRtc_Word16 numCodecs = _acmA->NumberOfCodecs(); - for(WebRtc_UWord8 n = 0; n < numCodecs; n++) - { - AudioCodingModule::Codec(n, dummyCodec); - if((STR_CASE_CMP(dummyCodec.plname, "CN") == 0) && - (dummyCodec.plfreq == 32000)) - { - continue; - } - - printf("Register Receive Codec %s ", dummyCodec.plname); - - if((n != 0) && !FixedPayloadTypeCodec(dummyCodec.plname)) - { - // Check registration with an already occupied payload type - int currentPayloadType = dummyCodec.pltype; - dummyCodec.pltype = 97; //lastPayloadType; - CHECK_ERROR(_acmB->RegisterReceiveCodec(dummyCodec)); - dummyCodec.pltype = currentPayloadType; - } - - if((n < numCodecs - 1) && !FixedPayloadTypeCodec(dummyCodec.plname)) - { - // test if re-registration works; - CodecInst nextCodec; - int currentPayloadType = dummyCodec.pltype; - AudioCodingModule::Codec(n + 1, nextCodec); - dummyCodec.pltype = nextCodec.pltype; - if(!FixedPayloadTypeCodec(nextCodec.plname)) - { - _acmB->RegisterReceiveCodec(dummyCodec); - } - dummyCodec.pltype = currentPayloadType; - } - - if((n < numCodecs - 1) && !FixedPayloadTypeCodec(dummyCodec.plname)) - { - // test if un-registration works; - CodecInst nextCodec; - int currentPayloadType = dummyCodec.pltype; - AudioCodingModule::Codec(n + 1, nextCodec); - nextCodec.pltype = dummyCodec.pltype; - if(!FixedPayloadTypeCodec(nextCodec.plname)) - { - CHECK_ERROR_MT(_acmA->RegisterReceiveCodec(nextCodec)); - CHECK_ERROR_MT(_acmA->UnregisterReceiveCodec(nextCodec.pltype)); - } - } - - - CHECK_ERROR_MT(_acmA->RegisterReceiveCodec(dummyCodec)); - printf(" side A done!"); - CHECK_ERROR_MT(_acmB->RegisterReceiveCodec(dummyCodec)); - printf(" side B done!\n"); - - if(!strcmp(dummyCodec.plname, "CN")) - { - CHECK_ERROR_MT(_acmA->RegisterSendCodec(dummyCodec)); - CHECK_ERROR_MT(_acmB->RegisterSendCodec(dummyCodec)); - } - lastPayloadType = dummyCodec.pltype; - if((lastPayloadType >= 96) && (lastPayloadType <= 127)) - { - _payloadUsed[lastPayloadType - 96] = true; - } - } - _thereIsDecoderA = true; - _thereIsDecoderB = true; - - // Register Send Codec - AudioCodingModule::Codec((WebRtc_UWord8)_codecCntrA, dummyCodec); - CHECK_ERROR_MT(_acmA->RegisterSendCodec(dummyCodec)); - _thereIsEncoderA = true; - // - AudioCodingModule::Codec((WebRtc_UWord8)_codecCntrB, dummyCodec); - CHECK_ERROR_MT(_acmB->RegisterSendCodec(dummyCodec)); - _thereIsEncoderB = true; - - char fileName[500]; - WebRtc_UWord16 frequencyHz; - - printf("\n\nAPI Test\n"); - printf("========\n"); - printf("Hit enter to accept the default values indicated in []\n\n"); - - //--- Input A - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - frequencyHz = 32000; - printf("Enter input file at side A [%s]: ", fileName); - PCMFile::ChooseFile(fileName, 499, &frequencyHz); - _inFileA.Open(fileName, frequencyHz, "rb", true); - - //--- Output A - strcpy(fileName, "./modules/audio_coding/main/test/outA.pcm"); - printf("Enter output file at side A [%s]: ", fileName); - PCMFile::ChooseFile(fileName, 499, &frequencyHz); - _outFileA.Open(fileName, frequencyHz, "wb"); - - //--- Input B - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - printf("\n\nEnter input file at side B [%s]: ", fileName); - PCMFile::ChooseFile(fileName, 499, &frequencyHz); - _inFileB.Open(fileName, frequencyHz, "rb", true); - - //--- Output B - strcpy(fileName, "./modules/audio_coding/main/test/outB.pcm"); - printf("Enter output file at side B [%s]: ", fileName); - PCMFile::ChooseFile(fileName, 499, &frequencyHz); - _outFileB.Open(fileName, frequencyHz, "wb"); - - //--- Set A-to-B channel - _channel_A2B = new Channel(2); - CHECK_ERROR_MT(_acmA->RegisterTransportCallback(_channel_A2B)); - _channel_A2B->RegisterReceiverACM(_acmB); - - //--- Set B-to-A channel - _channel_B2A = new Channel(1); - CHECK_ERROR_MT(_acmB->RegisterTransportCallback(_channel_B2A)); - _channel_B2A->RegisterReceiverACM(_acmA); - - //--- EVENT TIMERS - // A - _pullEventA = EventWrapper::Create(); - _pushEventA = EventWrapper::Create(); - _processEventA = EventWrapper::Create(); - _apiEventA = EventWrapper::Create(); - // B - _pullEventB = EventWrapper::Create(); - _pushEventB = EventWrapper::Create(); - _processEventB = EventWrapper::Create(); - _apiEventB = EventWrapper::Create(); - - //--- I/O params - // A - _outFreqHzA = _outFileA.SamplingFrequency(); - // B - _outFreqHzB = _outFileB.SamplingFrequency(); - - - //Trace::SetEncryptedTraceFile("ACMAPITestEncrypted.txt"); - - char print[11]; - - printf("\nRandom Test (y/n)?"); - fgets(print, 10, stdin); - print[10] = '\0'; - if(strstr(print, "y") != NULL) - { - _randomTest = true; - _verbose = false; - _writeToFile = false; - Trace::CreateTrace(); - Trace::SetTraceFile("ACMAPITest.txt"); - //freopen("APITest_log.txt", "w", stdout); - } - else - { - Trace::CreateTrace(); - Trace::SetTraceFile("ACMAPITest.txt", true); - _randomTest = false; - printf("\nPrint Tests (y/n)? "); - fgets(print, 10, stdin); - print[10] = '\0'; - if(strstr(print, "y") == NULL) - { - freopen("APITest_log.txt", "w", stdout); - _verbose = false; - } - } - -#ifdef WEBRTC_DTMF_DETECTION - _dtmfCallback = new DTMFDetector; -#endif - _vadCallbackA = new VADCallback; - _vadCallbackB = new VADCallback; - - return 0; -} - -bool -APITest::PushAudioThreadA(void* obj) -{ - return static_cast(obj)->PushAudioRunA(); -} - -bool -APITest::PushAudioThreadB(void* obj) -{ - return static_cast(obj)->PushAudioRunB(); -} - -bool -APITest::PullAudioThreadA(void* obj) -{ - return static_cast(obj)->PullAudioRunA(); -} - -bool -APITest::PullAudioThreadB(void* obj) -{ - return static_cast(obj)->PullAudioRunB(); -} - -bool -APITest::ProcessThreadA(void* obj) -{ - return static_cast(obj)->ProcessRunA(); -} - -bool -APITest::ProcessThreadB(void* obj) -{ - return static_cast(obj)->ProcessRunB(); -} - -bool -APITest::APIThreadA(void* obj) -{ - return static_cast(obj)->APIRunA(); -} - -bool -APITest::APIThreadB(void* obj) -{ - return static_cast(obj)->APIRunB(); -} - -bool -APITest::PullAudioRunA() -{ - _pullEventA->Wait(100); - AudioFrame audioFrame; - if(_acmA->PlayoutData10Ms(_outFreqHzA, audioFrame) < 0) - { - bool thereIsDecoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsDecoder = _thereIsDecoderA; - } - if(thereIsDecoder) - { - fprintf(stderr, "\n>>>>>> cannot pull audio A <<<<<<<< \n"); - } - } - else - { - if(_writeToFile) - { - _outFileA.Write10MsData(audioFrame); - } - _receiveVADActivityA[(int)audioFrame._vadActivity]++; - } - return true; -} - -bool -APITest::PullAudioRunB() -{ - _pullEventB->Wait(100); - AudioFrame audioFrame; - if(_acmB->PlayoutData10Ms(_outFreqHzB, audioFrame) < 0) - { - bool thereIsDecoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsDecoder = _thereIsDecoderB; - } - if(thereIsDecoder) - { - fprintf(stderr, "\n>>>>>> cannot pull audio B <<<<<<<< \n"); - fprintf(stderr, "%d %d\n", _testNumA, _testNumB); - } - } - else - { - if(_writeToFile) - { - _outFileB.Write10MsData(audioFrame); - } - _receiveVADActivityB[(int)audioFrame._vadActivity]++; - } - return true; -} - -bool -APITest::PushAudioRunA() -{ - _pushEventA->Wait(100); - AudioFrame audioFrame; - _inFileA.Read10MsData(audioFrame); - if(_acmA->Add10MsData(audioFrame) < 0) - { - bool thereIsEncoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsEncoder = _thereIsEncoderA; - } - if(thereIsEncoder) - { - fprintf(stderr, "\n>>>> add10MsData at A failed <<<<\n"); - } - } - return true; -} - -bool -APITest::PushAudioRunB() -{ - _pushEventB->Wait(100); - AudioFrame audioFrame; - _inFileB.Read10MsData(audioFrame); - if(_acmB->Add10MsData(audioFrame) < 0) - { - bool thereIsEncoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsEncoder = _thereIsEncoderB; - } - - if(thereIsEncoder) - { - fprintf(stderr, "\n>>>> cannot add audio to B <<<<"); - } - } - - return true; -} - -bool -APITest::ProcessRunA() -{ - _processEventA->Wait(100); - if(_acmA->Process() < 0) - { - // do not print error message if there is no encoder - bool thereIsEncoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsEncoder = _thereIsEncoderA; - } - - if(thereIsEncoder) - { - fprintf(stderr, "\n>>>>> Process Failed at A <<<<<\n"); - } - } - return true; -} - -bool -APITest::ProcessRunB() -{ - _processEventB->Wait(100); - if(_acmB->Process() < 0) - { - bool thereIsEncoder; - { - ReadLockScoped rl(_apiTestRWLock); - thereIsEncoder = _thereIsEncoderB; - } - if(thereIsEncoder) - { - fprintf(stderr, "\n>>>>> Process Failed at B <<<<<\n"); - } - } - return true; -} - -/*/ - * - * In side A we test the APIs which are related to sender Side. - * -/*/ - - -void -APITest::RunTest(char thread) -{ - int testNum; - { - WriteLockScoped cs(_apiTestRWLock); - if(thread == 'A') - { - _testNumA = (_testNumB + 1 + (rand() % 6)) % 7; - testNum = _testNumA; - - _movingDot[_dotPositionA] = ' '; - if(_dotPositionA == 0) - { - _dotMoveDirectionA = 1; - } - if(_dotPositionA == 19) - { - _dotMoveDirectionA = -1; - } - _dotPositionA += _dotMoveDirectionA; - _movingDot[_dotPositionA] = (_dotMoveDirectionA > 0)? '>':'<'; - } - else - { - _testNumB = (_testNumA + 1 + (rand() % 6)) % 7; - testNum = _testNumB; - - _movingDot[_dotPositionB] = ' '; - if(_dotPositionB == 20) - { - _dotMoveDirectionB = 1; - } - if(_dotPositionB == 39) - { - _dotMoveDirectionB = -1; - } - _dotPositionB += _dotMoveDirectionB; - _movingDot[_dotPositionB] = (_dotMoveDirectionB > 0)? '>':'<'; - } - //fprintf(stderr, "%c: %d \n", thread, testNum); - //fflush(stderr); - } - switch(testNum) - { - case 0: - CurrentCodec('A'); - ChangeCodec('A'); - break; - case 1: - TestPlayout('B'); - break; - case 2: - if(!_randomTest) - { - fprintf(stdout, "\nTesting Delay ...\n"); - } - TestDelay('A'); - break; - case 3: - TestSendVAD('A'); - break; - case 4: - TestRegisteration('A'); - break; - case 5: - TestReceiverVAD('A'); - break; - case 6: -#ifdef WEBRTC_DTMF_DETECTION - LookForDTMF('A'); -#endif - break; - default: - fprintf(stderr, "Wrong Test Number\n"); - getchar(); - exit(1); - } -} - - - -bool -APITest::APIRunA() -{ - _apiEventA->Wait(50); - - bool randomTest; - { - ReadLockScoped rl(_apiTestRWLock); - randomTest = _randomTest; - } - if(randomTest) - { - RunTest('A'); - } - else - { - CurrentCodec('A'); - ChangeCodec('A'); - TestPlayout('B'); - if(_codecCntrA == 0) - { - fprintf(stdout, "\nTesting Delay ...\n"); - TestDelay('A'); - } - // VAD TEST - TestSendVAD('A'); - TestRegisteration('A'); - TestReceiverVAD('A'); -#ifdef WEBRTC_DTMF_DETECTION - LookForDTMF('A'); -#endif - } - return true; -} - -bool -APITest::APIRunB() -{ - _apiEventB->Wait(50); - bool randomTest; - { - ReadLockScoped rl(_apiTestRWLock); - randomTest = _randomTest; - } - //_apiEventB->Wait(2000); - if(randomTest) - { - RunTest('B'); - } - - return true; -} - -void -APITest::Perform() -{ - SetUp(); - - //--- THREADS - // A - // PUSH - ThreadWrapper* myPushAudioThreadA = ThreadWrapper::CreateThread(PushAudioThreadA, - this, kNormalPriority, "PushAudioThreadA"); - CHECK_THREAD_NULLITY(myPushAudioThreadA, "Unable to start A::PUSH thread"); - // PULL - ThreadWrapper* myPullAudioThreadA = ThreadWrapper::CreateThread(PullAudioThreadA, - this, kNormalPriority, "PullAudioThreadA"); - CHECK_THREAD_NULLITY(myPullAudioThreadA, "Unable to start A::PULL thread"); - // Process - ThreadWrapper* myProcessThreadA = ThreadWrapper::CreateThread(ProcessThreadA, - this, kNormalPriority, "ProcessThreadA"); - CHECK_THREAD_NULLITY(myProcessThreadA, "Unable to start A::Process thread"); - // API - ThreadWrapper* myAPIThreadA = ThreadWrapper::CreateThread(APIThreadA, - this, kNormalPriority, "APIThreadA"); - CHECK_THREAD_NULLITY(myAPIThreadA, "Unable to start A::API thread"); - // B - // PUSH - ThreadWrapper* myPushAudioThreadB = ThreadWrapper::CreateThread(PushAudioThreadB, - this, kNormalPriority, "PushAudioThreadB"); - CHECK_THREAD_NULLITY(myPushAudioThreadB, "Unable to start B::PUSH thread"); - // PULL - ThreadWrapper* myPullAudioThreadB = ThreadWrapper::CreateThread(PullAudioThreadB, - this, kNormalPriority, "PullAudioThreadB"); - CHECK_THREAD_NULLITY(myPullAudioThreadB, "Unable to start B::PULL thread"); - // Process - ThreadWrapper* myProcessThreadB = ThreadWrapper::CreateThread(ProcessThreadB, - this, kNormalPriority, "ProcessThreadB"); - CHECK_THREAD_NULLITY(myProcessThreadB, "Unable to start B::Process thread"); - // API - ThreadWrapper* myAPIThreadB = ThreadWrapper::CreateThread(APIThreadB, - this, kNormalPriority, "APIThreadB"); - CHECK_THREAD_NULLITY(myAPIThreadB, "Unable to start B::API thread"); - - - //_apiEventA->StartTimer(true, 5000); - //_apiEventB->StartTimer(true, 5000); - - _processEventA->StartTimer(true, 10); - _processEventB->StartTimer(true, 10); - - _pullEventA->StartTimer(true, 10); - _pullEventB->StartTimer(true, 10); - - _pushEventA->StartTimer(true, 10); - _pushEventB->StartTimer(true, 10); - - // Keep main thread waiting for sender/receiver - // threads to complete - EventWrapper* completeEvent = EventWrapper::Create(); - WebRtc_UWord64 startTime = TickTime::MillisecondTimestamp(); - WebRtc_UWord64 currentTime; - do - { - { - //ReadLockScoped rl(_apiTestRWLock); - //fprintf(stderr, "\r%s", _movingDot); - } - //fflush(stderr); - completeEvent->Wait(50); - currentTime = TickTime::MillisecondTimestamp(); - } while((currentTime - startTime) < 120000); // Run test in 2 minutes (120000 ms) - - //completeEvent->Wait(0xFFFFFFFF);//(unsigned long)((unsigned long)TEST_DURATION_SEC * (unsigned long)1000)); - delete completeEvent; - - myPushAudioThreadA->Stop(); - myPullAudioThreadA->Stop(); - myProcessThreadA->Stop(); - myAPIThreadA->Stop(); - - delete myPushAudioThreadA; - delete myPullAudioThreadA; - delete myProcessThreadA; - delete myAPIThreadA; - - - myPushAudioThreadB->Stop(); - myPullAudioThreadB->Stop(); - myProcessThreadB->Stop(); - myAPIThreadB->Stop(); - - delete myPushAudioThreadB; - delete myPullAudioThreadB; - delete myProcessThreadB; - delete myAPIThreadB; -} - - -void -APITest::CheckVADStatus(char side) -{ - - bool dtxEnabled; - bool vadEnabled; - ACMVADMode vadMode; - EventWrapper* myEvent = EventWrapper::Create(); - if(side == 'A') - { - _acmA->VAD(dtxEnabled, vadEnabled, vadMode); - _acmA->RegisterVADCallback(NULL); - _vadCallbackA->Reset(); - _acmA->RegisterVADCallback(_vadCallbackA); - - if(!_randomTest) - { - if(_verbose) - { - fprintf(stdout, "DTX %3s, VAD %3s, Mode %d", - dtxEnabled? "ON":"OFF", - vadEnabled? "ON":"OFF", - (int)vadMode); - Wait(5000); - fprintf(stdout, " => bit-rate %3.0f kbps\n", - _channel_A2B->BitRate()); - } - else - { - Wait(5000); - fprintf(stdout, "DTX %3s, VAD %3s, Mode %d => bit-rate %3.0f kbps\n", - dtxEnabled? "ON":"OFF", - vadEnabled? "ON":"OFF", - (int)vadMode, - _channel_A2B->BitRate()); - } - _vadCallbackA->PrintFrameTypes(); - } - - if(dtxEnabled != _sendDTXA) - { - fprintf(stderr, ">>> Error Enabling DTX <<<\n"); - } - if((vadEnabled != _sendVADA) && (!dtxEnabled)) - { - fprintf(stderr, ">>> Error Enabling VAD <<<\n"); - } - if((vadMode != _sendVADModeA) && vadEnabled) - { - fprintf(stderr, ">>> Error setting VAD-mode <<<\n"); - } - } - else - { - _acmB->VAD(dtxEnabled, vadEnabled, vadMode); - - _acmB->RegisterVADCallback(NULL); - _vadCallbackB->Reset(); - _acmB->RegisterVADCallback(_vadCallbackB); - - if(!_randomTest) - { - if(_verbose) - { - fprintf(stdout, "DTX %3s, VAD %3s, Mode %d", - dtxEnabled? "ON":"OFF", - vadEnabled? "ON":"OFF", - (int)vadMode); - Wait(5000); - fprintf(stdout, " => bit-rate %3.0f kbps\n", - _channel_B2A->BitRate()); - } - else - { - Wait(5000); - fprintf(stdout, "DTX %3s, VAD %3s, Mode %d => bit-rate %3.0f kbps\n", - dtxEnabled? "ON":"OFF", - vadEnabled? "ON":"OFF", - (int)vadMode, - _channel_B2A->BitRate()); - } - _vadCallbackB->PrintFrameTypes(); - } - - if(dtxEnabled != _sendDTXB) - { - fprintf(stderr, ">>> Error Enabling DTX <<<\n"); - } - if((vadEnabled != _sendVADB) && (!dtxEnabled)) - { - fprintf(stderr, ">>> Error Enabling VAD <<<\n"); - } - if((vadMode != _sendVADModeB) && vadEnabled) - { - fprintf(stderr, ">>> Error setting VAD-mode <<<\n"); - } - } -} - -// Set Min delay, get delay, playout timestamp -void -APITest::TestDelay(char side) -{ - AudioCodingModule* myACM; - Channel* myChannel; - WebRtc_Word32* myMinDelay; - EventWrapper* myEvent = EventWrapper::Create(); - - WebRtc_UWord32 inTimestamp = 0; - WebRtc_UWord32 outTimestamp = 0; - double estimDelay = 0; - WebRtc_UWord16 delay = 0; - - double averageEstimDelay = 0; - double averageDelay = 0; - - CircularBuffer estimDelayCB(100); - CircularBuffer delayCB(100); - estimDelayCB.SetArithMean(true); - delayCB.SetArithMean(true); - - - if(side == 'A') - { - myACM = _acmA; - myChannel = _channel_B2A; - myMinDelay = &_minDelayA; - } - else - { - myACM = _acmB; - myChannel = _channel_A2B; - myMinDelay = &_minDelayB; - } - - - CHECK_ERROR_MT(myACM->SetMinimumPlayoutDelay(*myMinDelay)); - - - inTimestamp = myChannel->LastInTimestamp(); - CHECK_ERROR_MT(myACM->PlayoutTimestamp(outTimestamp)); - CHECK_ERROR_MT(myACM->Delay(delay)); - - if(!_randomTest) - { - myEvent->StartTimer(true, 30); - int n = 0; - int settlePoint = 5000; - while(n < settlePoint + 400) - { - myEvent->Wait(1000); - - inTimestamp = myChannel->LastInTimestamp(); - CHECK_ERROR_MT(myACM->PlayoutTimestamp(outTimestamp)); - - //std::cout << outTimestamp << std::endl << std::flush; - estimDelay = (double)((WebRtc_UWord32)(inTimestamp - outTimestamp)) / - ((double)myACM->ReceiveFrequency() / 1000.0); - - estimDelayCB.Update(estimDelay); - - estimDelayCB.ArithMean(averageEstimDelay); - //printf("\n %6.1f \n", estimDelay); - //std::cout << " " << std::flush; - - CHECK_ERROR_MT(myACM->Delay(delay)); - delayCB.Update(delay); - delayCB.ArithMean(averageDelay); - - if(_verbose) - { - fprintf(stdout, "\rExpected: %4d, retreived: %6.1f, measured: %6.1f", - *myMinDelay, averageDelay, averageEstimDelay); - std::cout << " " << std::flush; - } - if((averageDelay > *myMinDelay) && (n < settlePoint)) - { - settlePoint = n; - } - n++; - } - myEvent->StopTimer(); - } - - if((!_verbose) && (!_randomTest)) - { - fprintf(stdout, "\nExpected: %4d, retreived: %6.1f, measured: %6.1f", - *myMinDelay, averageDelay, averageEstimDelay); - } - - *myMinDelay = (rand() % 1000) + 1; - - ACMJitterStatistics jitterStat; - ACMNetworkStatistics networkStat; - CHECK_ERROR_MT(myACM->JitterStatistics(jitterStat)); - CHECK_ERROR_MT(myACM->NetworkStatistics(networkStat)); - - if(!_randomTest) - { - fprintf(stdout, "\n\nJitter Statistics at Side %c\n", side); - fprintf(stdout, "--------------------------------------\n"); - fprintf(stdout, "buffer-size............. %d\n", networkStat.currentBufferSize); - fprintf(stdout, "Preferred buffer-size... %d\n", networkStat.preferredBufferSize); - fprintf(stdout, "packet-size rate........ %d\n", networkStat.currentPacketLossRate); - fprintf(stdout, "discard rate............ %d\n", networkStat.currentDiscardRate); - fprintf(stdout, "expand rate............. %d\n", networkStat.currentExpandRate); - fprintf(stdout, "Preemptive rate......... %d\n", networkStat.currentPreemptiveRate); - fprintf(stdout, "Accelerate rate......... %d\n", networkStat.currentAccelerateRate); - - fprintf(stdout, "\n\nJitter Statistics at side %c\n", side); - fprintf(stdout, "--------------------------------------\n"); - fprintf(stdout, "Jitter buffer min size....... %d\n", jitterStat.jbMinSize); - fprintf(stdout, "Jitter buffer Max size....... %d\n", jitterStat.jbMaxSize); - fprintf(stdout, "Jitter buffer Average size... %d\n", jitterStat.jbAvgSize); - fprintf(stdout, "Change Count................. %d ms\n", jitterStat.jbChangeCount); - fprintf(stdout, "Late Loss.................... %d ms\n", jitterStat.lateLossMs); - fprintf(stdout, "Accelerate................... %d ms\n", jitterStat.accelerateMs); - fprintf(stdout, "Flushed...................... %d ms\n", jitterStat.flushedMs); - fprintf(stdout, "Generated Silence............ %d ms\n", jitterStat.generatedSilentMs); - fprintf(stdout, "Interpolated Voice........... %d ms\n", jitterStat.interpolatedVoiceMs); - fprintf(stdout, "Interpolated Silence......... %d ms\n", jitterStat.interpolatedSilentMs); - fprintf(stdout, "No tiny expand............... %d\n", jitterStat.numExpandTiny); - fprintf(stdout, "No small expand.............. %d\n", jitterStat.numExpandSmall); - fprintf(stdout, "No Medium expand............. %d\n", jitterStat.numExpandMedium); - fprintf(stdout, "No long expand............... %d\n", jitterStat.numExpandLong); - fprintf(stdout, "longest expand............... %d ms\n", jitterStat.longestExpandDurationMs); - fprintf(stdout, "No IAT 500................... %d ms\n", jitterStat.countIAT500ms); - fprintf(stdout, "No IAT 1000.................. %d ms\n", jitterStat.countIAT1000ms); - fprintf(stdout, "No IAT 2000.................. %d ms\n", jitterStat.countIAT2000ms); - fprintf(stdout, "longest IAT.................. %d ms\n", jitterStat.longestIATms); - fprintf(stdout, "Min packet delay............. %d ms\n", jitterStat.minPacketDelayMs); - fprintf(stdout, "Max packet delay............. %d ms\n", jitterStat.maxPacketDelayMs); - fprintf(stdout, "Average packet delay......... %d ms\n", jitterStat.avgPacketDelayMs); - } - - CHECK_ERROR_MT(myACM->SetMinimumPlayoutDelay(*myMinDelay)); - - if(!_randomTest) - { - myEvent->Wait(500); - fprintf(stdout, "\n"); - fprintf(stdout, "\n"); - } - delete myEvent; -} - -// Unregister a codec & register again. -void -APITest::TestRegisteration(char sendSide) -{ - AudioCodingModule* sendACM; - AudioCodingModule* receiveACM; - bool* thereIsDecoder; - EventWrapper* myEvent = EventWrapper::Create(); - - if(!_randomTest) - { - fprintf(stdout, "\n\n"); - fprintf(stdout, "---------------------------------------------------------\n"); - fprintf(stdout, " Unregister/register Receive Codec\n"); - fprintf(stdout, "---------------------------------------------------------\n"); - } - - switch(sendSide) - { - case 'A': - { - sendACM = _acmA; - receiveACM = _acmB; - thereIsDecoder = &_thereIsDecoderB; - break; - } - case 'B': - { - sendACM = _acmB; - receiveACM = _acmA; - thereIsDecoder = &_thereIsDecoderA; - break; - } - default: - fprintf(stderr, "Invalid sender-side in TestRegistration(%c)\n", sendSide); - exit(-1); - } - - CodecInst myCodec; - if(sendACM->SendCodec(myCodec) < 0) - { - AudioCodingModule::Codec(_codecCntrA, myCodec); - } - - if(!_randomTest) - { - fprintf(stdout, "Unregistering reveive codec, NO AUDIO.\n"); - fflush(stdout); - } - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsDecoder = false; - } - //myEvent->Wait(20); - CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); - Wait(1000); - - int currentPayload = myCodec.pltype; - - if(!FixedPayloadTypeCodec(myCodec.plname)) - { - WebRtc_Word32 i; - for(i = 0; i < 32; i++) - { - if(!_payloadUsed[i]) - { - if(!_randomTest) - { - fprintf(stdout, "Register receive codec with new Payload, AUDIO BACK.\n"); - } - //myCodec.pltype = i + 96; - //CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); - //CHECK_ERROR_MT(sendACM->RegisterSendCodec(myCodec)); - //myEvent->Wait(20); - //{ - // WriteLockScoped wl(_apiTestRWLock); - // *thereIsDecoder = true; - //} - Wait(1000); - - if(!_randomTest) - { - fprintf(stdout, "Unregistering reveive codec, NO AUDIO.\n"); - } - //{ - // WriteLockScoped wl(_apiTestRWLock); - // *thereIsDecoder = false; - //} - //myEvent->Wait(20); - //CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); - Wait(1000); - - myCodec.pltype = currentPayload; - if(!_randomTest) - { - fprintf(stdout, "Register receive codec with default Payload, AUDIO BACK.\n"); - fflush(stdout); - } - CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); - //CHECK_ERROR_MT(sendACM->RegisterSendCodec(myCodec)); - myEvent->Wait(20); - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsDecoder = true; - } - Wait(1000); - - break; - } - } - if(i == 32) - { - CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsDecoder = true; - } - } - } - else - { - if(!_randomTest) - { - fprintf(stdout, "Register receive codec with fixed Payload, AUDIO BACK.\n"); - fflush(stdout); - } - CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); - //CHECK_ERROR_MT(receiveACM->UnregisterReceiveCodec(myCodec.pltype)); - //CHECK_ERROR_MT(receiveACM->RegisterReceiveCodec(myCodec)); - myEvent->Wait(20); - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsDecoder = true; - } - } - delete myEvent; - if(!_randomTest) - { - fprintf(stdout, "---------------------------------------------------------\n"); - } -} - -// Playout Mode, background noise mode. -// Receiver Frequency, playout frequency. -void -APITest::TestPlayout(char receiveSide) -{ - AudioCodingModule* receiveACM; - AudioPlayoutMode* playoutMode; - ACMBackgroundNoiseMode* bgnMode; - switch(receiveSide) - { - case 'A': - { - receiveACM = _acmA; - playoutMode = &_playoutModeA; - bgnMode = &_bgnModeA; - break; - } - case 'B': - { - receiveACM = _acmB; - playoutMode = &_playoutModeB; - bgnMode = &_bgnModeB; - break; - } - default: - receiveACM = _acmA; - } - - WebRtc_Word32 receiveFreqHz = receiveACM->ReceiveFrequency(); - WebRtc_Word32 playoutFreqHz = receiveACM->PlayoutFrequency(); - - CHECK_ERROR_MT(receiveFreqHz); - CHECK_ERROR_MT(playoutFreqHz); - - char bgnString[25]; - switch(*bgnMode) - { - case On: - { - *bgnMode = Fade; - strncpy(bgnString, "Fade", 25); - break; - } - case Fade: - { - *bgnMode = Off; - strncpy(bgnString, "OFF", 25); - break; - } - case Off: - { - *bgnMode = On; - strncpy(bgnString, "ON", 25); - break; - } - default: - *bgnMode = On; - strncpy(bgnString, "ON", 25); - } - CHECK_ERROR_MT(receiveACM->SetBackgroundNoiseMode(*bgnMode)); - bgnString[24] = '\0'; - - char playoutString[25]; - switch(*playoutMode) - { - case voice: - { - *playoutMode = fax; - strncpy(playoutString, "FAX", 25); - break; - } - case fax: - { - *playoutMode = streaming; - strncpy(playoutString, "Streaming", 25); - break; - } - case streaming: - { - *playoutMode = voice; - strncpy(playoutString, "Voice", 25); - break; - } - default: - *playoutMode = voice; - strncpy(playoutString, "Voice", 25); - } - CHECK_ERROR_MT(receiveACM->SetPlayoutMode(*playoutMode)); - playoutString[24] = '\0'; - - if(!_randomTest) - { - fprintf(stdout, "\n"); - fprintf(stdout, "In Side %c\n", receiveSide); - fprintf(stdout, "---------------------------------\n"); - fprintf(stdout, "Receive Frequency....... %d Hz\n", receiveFreqHz); - fprintf(stdout, "Playout Frequency....... %d Hz\n", playoutFreqHz); - fprintf(stdout, "Audio Playout Mode...... %s\n", playoutString); - fprintf(stdout, "Background Noise Mode... %s\n", bgnString); - } -} - -// set/get receiver VAD status & mode. -void -APITest::TestReceiverVAD(char side) -{ - AudioCodingModule* myACM; - EventWrapper* myEvent = EventWrapper::Create(); - WebRtc_UWord64* myReceiveVADActivity; - - if(side == 'A') - { - myACM = _acmA; - myReceiveVADActivity = _receiveVADActivityA; - } - else - { - myACM = _acmB; - myReceiveVADActivity = _receiveVADActivityB; - } - - bool vadStatus = myACM->ReceiveVADStatus(); - ACMVADMode mode = myACM->ReceiveVADMode(); - - CHECK_ERROR_MT(mode); - - if(!_randomTest) - { - fprintf(stdout, "\n\nCurrent Receive VAD at side %c\n", side); - fprintf(stdout, "----------------------------------\n"); - fprintf(stdout, "Status........ %s\n", vadStatus? "ON":"OFF"); - fprintf(stdout, "mode.......... %d\n", (int)mode); - fprintf(stdout, "VAD Active.... %llu\n", myReceiveVADActivity[0]); - fprintf(stdout, "VAD Passive... %llu\n", myReceiveVADActivity[1]); - fprintf(stdout, "VAD Unknown... %llu\n", myReceiveVADActivity[2]); - } - - if(vadStatus) - { - if(!_randomTest) - { - fprintf(stdout, "\nChange Receive VAD at side %c\n\n", side); - } - - switch(mode) - { - case VADNormal: - mode = VADAggr; - break; - case VADLowBitrate: - mode = VADVeryAggr; - break; - case VADAggr: - mode = VADLowBitrate; - break; - case VADVeryAggr: - vadStatus = false; - mode = VADNormal; - break; - default: - mode = VADNormal; - } - - CHECK_ERROR_MT(myACM->SetReceiveVADMode(mode)); - CHECK_ERROR_MT(myACM->SetReceiveVADStatus(vadStatus)); - } - else - { - if(!_randomTest) - { - fprintf(stdout, "\nTurn on Receive VAD at side %c\n\n", side); - } - CHECK_ERROR_MT(myACM->SetReceiveVADStatus(true)); - CHECK_ERROR_MT(myACM->SetReceiveVADMode(VADNormal)); - } - for(int n = 0; n < 3; n++) - { - myReceiveVADActivity[n] = 0; - } -} - - -void -APITest::TestSendVAD(char side) -{ - if(_randomTest) - { - return; - } - - bool* vad; - bool* dtx; - ACMVADMode* mode; - Channel* myChannel; - AudioCodingModule* myACM; - - CodecInst myCodec; - if(!_randomTest) - { - fprintf(stdout, "\n\n"); - fprintf(stdout, "-----------------------------------------------\n"); - fprintf(stdout, " Test VAD API\n"); - fprintf(stdout, "-----------------------------------------------\n"); - } - - if(side == 'A') - { - AudioCodingModule::Codec(_codecCntrA, myCodec); - vad = &_sendVADA; - dtx = &_sendDTXA; - mode = &_sendVADModeA; - myChannel = _channel_A2B; - myACM = _acmA; - } - else - { - AudioCodingModule::Codec(_codecCntrB, myCodec); - vad = &_sendVADB; - dtx = &_sendDTXB; - mode = &_sendVADModeB; - myChannel = _channel_B2A; - myACM = _acmB; - } - - CheckVADStatus(side); - if(!_randomTest) - { - fprintf(stdout, "\n\n"); - } - - switch(*mode) - { - case VADNormal: - *vad = true; - *dtx = true; - *mode = VADAggr; - break; - case VADLowBitrate: - *vad = true; - *dtx = true; - *mode = VADVeryAggr; - break; - case VADAggr: - *vad = true; - *dtx = true; - *mode = VADLowBitrate; - break; - case VADVeryAggr: - *vad = false; - *dtx = false; - *mode = VADNormal; - break; - default: - *mode = VADNormal; - } - - *dtx = (myCodec.plfreq == 32000)? false:*dtx; - - CHECK_ERROR_MT(myACM->SetVAD(*dtx, *vad, *mode)); - myChannel->ResetStats(); - - CheckVADStatus(side); - if(!_randomTest) - { - fprintf(stdout, "\n"); - fprintf(stdout, "-----------------------------------------------\n"); - } - - // Fault Test - CHECK_PROTECTED_MT(myACM->SetVAD(false, true, (ACMVADMode)-1)); - CHECK_PROTECTED_MT(myACM->SetVAD(false, true, (ACMVADMode)4)); - - - -} - - -void -APITest::CurrentCodec(char side) -{ - CodecInst myCodec; - EventWrapper* myEvent = EventWrapper::Create(); - if(side == 'A') - { - _acmA->SendCodec(myCodec); - } - else - { - _acmB->SendCodec(myCodec); - } - - if(!_randomTest) - { - fprintf(stdout, "\n\n"); - fprintf(stdout, "Send codec in Side A\n"); - fprintf(stdout, "----------------------------\n"); - fprintf(stdout, "Name................. %s\n", myCodec.plname); - fprintf(stdout, "Sampling Frequency... %d\n", myCodec.plfreq); - fprintf(stdout, "Rate................. %d\n", myCodec.rate); - fprintf(stdout, "Payload-type......... %d\n", myCodec.pltype); - fprintf(stdout, "Packet-size.......... %d\n", myCodec.pacsize); - } - - Wait(100); -} - -void -APITest::ChangeCodec(char side) -{ - CodecInst myCodec; - AudioCodingModule* myACM; - WebRtc_UWord8* codecCntr; - bool* thereIsEncoder; - bool* vad; - bool* dtx; - ACMVADMode* mode; - Channel* myChannel; - EventWrapper* myEvent = EventWrapper::Create(); - // Reset and Wait - if(!_randomTest) - { - fprintf(stdout, "Reset Encoder Side A \n"); - } - if(side == 'A') - { - myACM = _acmA; - codecCntr = &_codecCntrA; - { - WriteLockScoped wl(_apiTestRWLock); - thereIsEncoder = &_thereIsEncoderA; - } - vad = &_sendVADA; - dtx = &_sendDTXA; - mode = &_sendVADModeA; - myChannel = _channel_A2B; - } - else - { - myACM = _acmB; - codecCntr = &_codecCntrB; - { - WriteLockScoped wl(_apiTestRWLock); - thereIsEncoder = &_thereIsEncoderB; - } - vad = &_sendVADB; - dtx = &_sendDTXB; - mode = &_sendVADModeB; - myChannel = _channel_B2A; - } - - myACM->ResetEncoder(); - Wait(100); - - // Register the next codec - do - { - *codecCntr = (*codecCntr < AudioCodingModule::NumberOfCodecs() - 1)? - (*codecCntr + 1):0; - - if(*codecCntr == 0) - { - //printf("Initialize Sender Side A \n"); - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsEncoder = false; - } - CHECK_ERROR_MT(myACM->InitializeSender()); - Wait(1000); - - // After Initialization CN is lost, re-register them - if(AudioCodingModule::Codec("CN", myCodec, 8000) >= 0) - { - CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); - } - if(AudioCodingModule::Codec("CN", myCodec, 16000) >= 0) - { - CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); - } - // VAD & DTX are disabled after initialization - *vad = false; - *dtx = false; - _writeToFile = false; - } - - AudioCodingModule::Codec(*codecCntr, myCodec); - } while(!STR_CASE_CMP(myCodec.plname, "CN") || - !STR_CASE_CMP(myCodec.plname, "telephone-event") || - !STR_CASE_CMP(myCodec.plname, "RED")); - - if(!_randomTest) - { - fprintf(stdout, "\n====================================================================\n"); - fprintf(stdout, " Registering New Codec %s, %d kHz, %d kbps\n", - myCodec.plname, myCodec.plfreq / 1000, myCodec.rate / 1000); - } - //std::cout<< std::flush; - - // NO DTX for supe-wideband codec at this point - if(myCodec.plfreq == 32000) - { - *dtx = false; - CHECK_ERROR_MT(myACM->SetVAD(*dtx, *vad, *mode)); - - } - - CHECK_ERROR_MT(myACM->RegisterSendCodec(myCodec)); - myChannel->ResetStats(); - { - WriteLockScoped wl(_apiTestRWLock); - *thereIsEncoder = true; - } - Wait(500); -} - - -void -APITest::LookForDTMF(char side) -{ - if(!_randomTest) - { - fprintf(stdout, "\n\nLooking for DTMF Signal in Side %c\n", side); - fprintf(stdout, "----------------------------------------\n"); - } - - if(side == 'A') - { - _acmB->RegisterIncomingMessagesCallback(NULL); - _acmA->RegisterIncomingMessagesCallback(_dtmfCallback); - Wait(1000); - _acmA->RegisterIncomingMessagesCallback(NULL); - } - else - { - _acmA->RegisterIncomingMessagesCallback(NULL); - _acmB->RegisterIncomingMessagesCallback(_dtmfCallback); - Wait(1000); - _acmB->RegisterIncomingMessagesCallback(NULL); - } -} diff --git a/modules/audio_coding/main/test/APITest.h b/modules/audio_coding/main/test/APITest.h deleted file mode 100644 index 52ecb2764..000000000 --- a/modules/audio_coding/main/test/APITest.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_TEST_H -#define API_TEST_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" -#include "event_wrapper.h" -#include "utility.h" - -enum APITESTAction {TEST_CHANGE_CODEC_ONLY = 0, DTX_TEST = 1}; - -class APITest : public ACMTest -{ -public: - APITest(); - ~APITest(); - - void Perform(); -private: - WebRtc_Word16 SetUp(); - - static bool PushAudioThreadA(void* obj); - static bool PullAudioThreadA(void* obj); - static bool ProcessThreadA(void* obj); - static bool APIThreadA(void* obj); - - static bool PushAudioThreadB(void* obj); - static bool PullAudioThreadB(void* obj); - static bool ProcessThreadB(void* obj); - static bool APIThreadB(void* obj); - - void CheckVADStatus(char side); - - // Set Min delay, get delay, playout timestamp - void TestDelay(char side); - - // Unregister a codec & register again. - void TestRegisteration(char side); - - // Playout Mode, background noise mode. - // Receiver Frequency, playout frequency. - void TestPlayout(char receiveSide); - - // set/get receiver VAD status & mode. - void TestReceiverVAD(char side); - - // - void TestSendVAD(char side); - - void CurrentCodec(char side); - - void ChangeCodec(char side); - - void Wait(WebRtc_UWord32 waitLengthMs); - - void LookForDTMF(char side); - - void RunTest(char thread); - - bool PushAudioRunA(); - bool PullAudioRunA(); - bool ProcessRunA(); - bool APIRunA(); - - bool PullAudioRunB(); - bool PushAudioRunB(); - bool ProcessRunB(); - bool APIRunB(); - - - - //--- ACMs - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - //--- Channels - Channel* _channel_A2B; - Channel* _channel_B2A; - - //--- I/O files - // A - PCMFile _inFileA; - PCMFile _outFileA; - // B - PCMFile _outFileB; - PCMFile _inFileB; - - //--- I/O params - // A - WebRtc_Word32 _outFreqHzA; - // B - WebRtc_Word32 _outFreqHzB; - - // Should we write to file. - // we might skip writing to file if we - // run the test for a long time. - bool _writeToFile; - //--- Events - // A - EventWrapper* _pullEventA; // pulling data from ACM - EventWrapper* _pushEventA; // pushing data to ACM - EventWrapper* _processEventA; // process - EventWrapper* _apiEventA; // API calls - // B - EventWrapper* _pullEventB; // pulling data from ACM - EventWrapper* _pushEventB; // pushing data to ACM - EventWrapper* _processEventB; // process - EventWrapper* _apiEventB; // API calls - - // keep track of the codec in either side. - WebRtc_UWord8 _codecCntrA; - WebRtc_UWord8 _codecCntrB; - - // keep track of tests - WebRtc_UWord8 _testCntrA; - WebRtc_UWord8 _testCntrB; - - // Is set to true if there is no encoder in either side - bool _thereIsEncoderA; - bool _thereIsEncoderB; - bool _thereIsDecoderA; - bool _thereIsDecoderB; - - bool _sendVADA; - bool _sendDTXA; - ACMVADMode _sendVADModeA; - - bool _sendVADB; - bool _sendDTXB; - ACMVADMode _sendVADModeB; - - WebRtc_Word32 _minDelayA; - WebRtc_Word32 _minDelayB; - bool _payloadUsed[32]; - - AudioPlayoutMode _playoutModeA; - AudioPlayoutMode _playoutModeB; - - ACMBackgroundNoiseMode _bgnModeA; - ACMBackgroundNoiseMode _bgnModeB; - - - WebRtc_UWord64 _receiveVADActivityA[3]; - WebRtc_UWord64 _receiveVADActivityB[3]; - bool _verbose; - - int _dotPositionA; - int _dotMoveDirectionA; - int _dotPositionB; - int _dotMoveDirectionB; - - char _movingDot[41]; - - DTMFDetector* _dtmfCallback; - VADCallback* _vadCallbackA; - VADCallback* _vadCallbackB; - RWLockWrapper& _apiTestRWLock; - bool _randomTest; - int _testNumA; - int _testNumB; -}; - - -#endif diff --git a/modules/audio_coding/main/test/Channel.cpp b/modules/audio_coding/main/test/Channel.cpp deleted file mode 100644 index bf440eaa0..000000000 --- a/modules/audio_coding/main/test/Channel.cpp +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "audio_coding_module.h" -#include "Channel.h" -#include "tick_util.h" -#include "typedefs.h" -#include "common_types.h" - -using namespace webrtc; - -WebRtc_Word32 -Channel::SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation) -{ - WebRtcRTPHeader rtpInfo; - WebRtc_Word32 status; - WebRtc_UWord16 payloadDataSize = payloadSize; - - rtpInfo.header.markerBit = false; - rtpInfo.header.ssrc = 0; - rtpInfo.header.sequenceNumber = _seqNo++; - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.timestamp = timeStamp; - if(frameType == kAudioFrameCN) - { - rtpInfo.type.Audio.isCNG = true; - } - else - { - rtpInfo.type.Audio.isCNG = false; - } - if(frameType == kFrameEmpty) - { - // Skip this frame - return 0; - } - - rtpInfo.type.Audio.channel = 1; - // Treat fragmentation separately - if(fragmentation != NULL) - { - if((fragmentation->fragmentationTimeDiff[1] <= 0x3fff) && // silence for too long send only new data - (fragmentation->fragmentationVectorSize == 2)) - { - // only 0x80 if we have multiple blocks - _payloadData[0] = 0x80 + fragmentation->fragmentationPlType[1]; - WebRtc_UWord32 REDheader = (((WebRtc_UWord32)fragmentation->fragmentationTimeDiff[1]) << 10) + fragmentation->fragmentationLength[1]; - _payloadData[1] = WebRtc_UWord8((REDheader >> 16) & 0x000000FF); - _payloadData[2] = WebRtc_UWord8((REDheader >> 8) & 0x000000FF); - _payloadData[3] = WebRtc_UWord8(REDheader & 0x000000FF); - - _payloadData[4] = fragmentation->fragmentationPlType[0]; - // copy the RED data - memcpy(_payloadData + 5, - payloadData + fragmentation->fragmentationOffset[1], - fragmentation->fragmentationLength[1]); - // copy the normal data - memcpy(_payloadData + 5 + fragmentation->fragmentationLength[1], - payloadData + fragmentation->fragmentationOffset[0], - fragmentation->fragmentationLength[0]); - payloadDataSize += 5; - } else - { - // single block (newest one) - memcpy(_payloadData, - payloadData + fragmentation->fragmentationOffset[0], - fragmentation->fragmentationLength[0]); - payloadDataSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]); - rtpInfo.header.payloadType = fragmentation->fragmentationPlType[0]; - } - } - else - { - memcpy(_payloadData, payloadData, payloadDataSize); - if(_isStereo) - { - if(_leftChannel) - { - memcpy(&_rtpInfo, &rtpInfo, sizeof(WebRtcRTPHeader)); - _leftChannel = false; - rtpInfo.type.Audio.channel = 1; - } - else - { - memcpy(&rtpInfo, &_rtpInfo, sizeof(WebRtcRTPHeader)); - _leftChannel = true; - rtpInfo.type.Audio.channel = 2; - } - } - } - - _channelCritSect->Enter(); - if(_saveBitStream) - { - //fwrite(payloadData, sizeof(WebRtc_UWord8), payloadSize, _bitStreamFile); - } - - if(!_isStereo) - { - CalcStatistics(rtpInfo, payloadSize); - } - _lastInTimestamp = timeStamp; - _totalBytes += payloadDataSize; - _channelCritSect->Leave(); - - if(_useFECTestWithPacketLoss) - { - _packetLoss += 1; - if(_packetLoss == 3) - { - _packetLoss = 0; - return 0; - } - } - - - //status = _receiverACM->IncomingPayload((WebRtc_Word8*)_payloadData, payloadSize, payloadType, timeStamp); - status = _receiverACM->IncomingPacket((WebRtc_Word8*)_payloadData, payloadDataSize, rtpInfo); - - //delete [] payloadData; - - - - return status; -} - -void -Channel::CalcStatistics( - WebRtcRTPHeader& rtpInfo, - WebRtc_UWord16 payloadSize) -{ - int n; - if((rtpInfo.header.payloadType != _lastPayloadType) && - (_lastPayloadType != -1)) - { - // payload-type is changed. - // we have to terminate the calculations on the previous payload type - // we ignore the last packet in that payload type just to make things - // easier. - for(n = 0; n < MAX_NUM_PAYLOADS; n++) - { - if(_lastPayloadType == _payloadStats[n].payloadType) - { - _payloadStats[n].newPacket = true; - break; - } - } - } - _lastPayloadType = rtpInfo.header.payloadType; - - bool newPayload = true; - ACMTestPayloadStats* currentPayloadStr; - for(n = 0; n < MAX_NUM_PAYLOADS; n++) - { - if(rtpInfo.header.payloadType == _payloadStats[n].payloadType) - { - newPayload = false; - currentPayloadStr = &_payloadStats[n]; - break; - } - } - - if(!newPayload) - { - if(!currentPayloadStr->newPacket) - { - WebRtc_UWord32 lastFrameSizeSample = (WebRtc_UWord32)((WebRtc_UWord32)rtpInfo.header.timestamp - - (WebRtc_UWord32)currentPayloadStr->lastTimestamp); - assert(lastFrameSizeSample > 0); - int k = 0; - while((currentPayloadStr->frameSizeStats[k].frameSizeSample != - lastFrameSizeSample) && - (currentPayloadStr->frameSizeStats[k].frameSizeSample != 0)) - { - k++; - } - ACMTestFrameSizeStats* currentFrameSizeStats = - &(currentPayloadStr->frameSizeStats[k]); - currentFrameSizeStats->frameSizeSample = (WebRtc_Word16)lastFrameSizeSample; - - // increment the number of encoded samples. - currentFrameSizeStats->totalEncodedSamples += - lastFrameSizeSample; - // increment the number of recveived packets - currentFrameSizeStats->numPackets++; - // increment the total number of bytes (this is based on - // the previous payload we don't know the frame-size of - // the current payload. - currentFrameSizeStats->totalPayloadLenByte += - currentPayloadStr->lastPayloadLenByte; - // store the maximum payload-size (this is based on - // the previous payload we don't know the frame-size of - // the current payload. - if(currentFrameSizeStats->maxPayloadLen < - currentPayloadStr->lastPayloadLenByte) - { - currentFrameSizeStats->maxPayloadLen = - currentPayloadStr->lastPayloadLenByte; - } - // store the current values for the next time - currentPayloadStr->lastTimestamp = rtpInfo.header.timestamp; - currentPayloadStr->lastPayloadLenByte = payloadSize; - } - else - { - currentPayloadStr->newPacket = false; - currentPayloadStr->lastPayloadLenByte = payloadSize; - currentPayloadStr->lastTimestamp = rtpInfo.header.timestamp; - currentPayloadStr->payloadType = rtpInfo.header.payloadType; - } - } - else - { - n = 0; - while(_payloadStats[n].payloadType != -1) - { - n++; - } - // first packet - _payloadStats[n].newPacket = false; - _payloadStats[n].lastPayloadLenByte = payloadSize; - _payloadStats[n].lastTimestamp = rtpInfo.header.timestamp; - _payloadStats[n].payloadType = rtpInfo.header.payloadType; - } -} - -Channel::Channel(WebRtc_Word16 chID) : -_receiverACM(NULL), -_seqNo(0), -_channelCritSect(CriticalSectionWrapper::CreateCriticalSection()), -_bitStreamFile(NULL), -_saveBitStream(false), -_lastPayloadType(-1), -_isStereo(false), -_leftChannel(true), -_useFECTestWithPacketLoss(false), -_packetLoss(0), -_lastInTimestamp(0), -_chID(chID), -_beginTime(TickTime::MillisecondTimestamp()), -_totalBytes(0) -{ - int n; - int k; - for(n = 0; n < MAX_NUM_PAYLOADS; n++) - { - _payloadStats[n].payloadType = -1; - _payloadStats[n].newPacket = true; - for(k = 0; k < MAX_NUM_FRAMESIZES; k++) - { - _payloadStats[n].frameSizeStats[k].frameSizeSample = 0; - _payloadStats[n].frameSizeStats[k].maxPayloadLen = 0; - _payloadStats[n].frameSizeStats[k].numPackets = 0; - _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0; - _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0; - } - } - if(chID >= 0) - { - _saveBitStream = true; - char bitStreamFileName[500]; - sprintf(bitStreamFileName, "bitStream_%d.dat", chID); - _bitStreamFile = fopen(bitStreamFileName, "wb"); - } - else - { - _saveBitStream = false; - } -} - -Channel::~Channel() -{ - delete _channelCritSect; -} - -void -Channel::RegisterReceiverACM(AudioCodingModule* acm) -{ - _receiverACM = acm; - return; -} - -void -Channel::ResetStats() -{ - int n; - int k; - _channelCritSect->Enter(); - _lastPayloadType = -1; - for(n = 0; n < MAX_NUM_PAYLOADS; n++) - { - _payloadStats[n].payloadType = -1; - _payloadStats[n].newPacket = true; - for(k = 0; k < MAX_NUM_FRAMESIZES; k++) - { - _payloadStats[n].frameSizeStats[k].frameSizeSample = 0; - _payloadStats[n].frameSizeStats[k].maxPayloadLen = 0; - _payloadStats[n].frameSizeStats[k].numPackets = 0; - _payloadStats[n].frameSizeStats[k].totalPayloadLenByte = 0; - _payloadStats[n].frameSizeStats[k].totalEncodedSamples = 0; - } - } - _beginTime = TickTime::MillisecondTimestamp(); - _totalBytes = 0; - _channelCritSect->Leave(); -} - -WebRtc_Word16 -Channel::Stats(CodecInst& codecInst, ACMTestPayloadStats& payloadStats) -{ - _channelCritSect->Enter(); - int n; - payloadStats.payloadType = -1; - for(n = 0; n < MAX_NUM_PAYLOADS; n++) - { - if(_payloadStats[n].payloadType == codecInst.pltype) - { - memcpy(&payloadStats, &_payloadStats[n], sizeof(ACMTestPayloadStats)); - break; - } - } - if(payloadStats.payloadType == -1) - { - _channelCritSect->Leave(); - return -1; - } - for(n = 0; n < MAX_NUM_FRAMESIZES; n++) - { - if(payloadStats.frameSizeStats[n].frameSizeSample == 0) - { - _channelCritSect->Leave(); - return 0; - } - payloadStats.frameSizeStats[n].usageLenSec = - (double)payloadStats.frameSizeStats[n].totalEncodedSamples - / (double)codecInst.plfreq; - - payloadStats.frameSizeStats[n].rateBitPerSec = - payloadStats.frameSizeStats[n].totalPayloadLenByte * 8 / - payloadStats.frameSizeStats[n].usageLenSec; - - } - _channelCritSect->Leave(); - return 0; -} - -void -Channel::Stats(WebRtc_UWord32* numPackets) -{ - _channelCritSect->Enter(); - int k; - int n; - memset(numPackets, 0, MAX_NUM_PAYLOADS * sizeof(WebRtc_UWord32)); - for(k = 0; k < MAX_NUM_PAYLOADS; k++) - { - if(_payloadStats[k].payloadType == -1) - { - break; - } - numPackets[k] = 0; - for(n = 0; n < MAX_NUM_FRAMESIZES; n++) - { - if(_payloadStats[k].frameSizeStats[n].frameSizeSample == 0) - { - break; - } - numPackets[k] += - _payloadStats[k].frameSizeStats[n].numPackets; - } - } - _channelCritSect->Leave(); -} - -void -Channel::Stats(WebRtc_UWord8* payloadType, WebRtc_UWord32* payloadLenByte) -{ - _channelCritSect->Enter(); - - int k; - int n; - memset(payloadLenByte, 0, MAX_NUM_PAYLOADS * sizeof(WebRtc_UWord32)); - for(k = 0; k < MAX_NUM_PAYLOADS; k++) - { - if(_payloadStats[k].payloadType == -1) - { - break; - } - payloadType[k] = (WebRtc_UWord8)_payloadStats[k].payloadType; - payloadLenByte[k] = 0; - for(n = 0; n < MAX_NUM_FRAMESIZES; n++) - { - if(_payloadStats[k].frameSizeStats[n].frameSizeSample == 0) - { - break; - } - payloadLenByte[k] += (WebRtc_UWord16) - _payloadStats[k].frameSizeStats[n].totalPayloadLenByte; - } - } - - _channelCritSect->Leave(); -} - - -void -Channel::PrintStats(CodecInst& codecInst) -{ - ACMTestPayloadStats payloadStats; - Stats(codecInst, payloadStats); - printf("%s %d kHz\n", - codecInst.plname, - codecInst.plfreq / 1000); - printf("=====================================================\n"); - if(payloadStats.payloadType == -1) - { - printf("No Packets are sent with payload-type %d (%s)\n\n", - codecInst.pltype, - codecInst.plname); - return; - } - for(int k = 0; k < MAX_NUM_FRAMESIZES; k++) - { - if(payloadStats.frameSizeStats[k].frameSizeSample == 0) - { - break; - } - printf("Frame-size.................... %d samples\n", - payloadStats.frameSizeStats[k].frameSizeSample); - printf("Average Rate.................. %.0f bits/sec\n", - payloadStats.frameSizeStats[k].rateBitPerSec); - printf("Maximum Payload-Size.......... %d Bytes\n", - payloadStats.frameSizeStats[k].maxPayloadLen); - printf("Maximum Instantaneous Rate.... %.0f bits/sec\n", - ((double)payloadStats.frameSizeStats[k].maxPayloadLen * 8.0 * - (double)codecInst.plfreq) / - (double)payloadStats.frameSizeStats[k].frameSizeSample); - printf("Number of Packets............. %u\n", - (unsigned int)payloadStats.frameSizeStats[k].numPackets); - printf("Duration...................... %0.3f sec\n\n", - payloadStats.frameSizeStats[k].usageLenSec); - - } - -} - -WebRtc_UWord32 -Channel::LastInTimestamp() -{ - WebRtc_UWord32 timestamp; - _channelCritSect->Enter(); - timestamp = _lastInTimestamp; - _channelCritSect->Leave(); - return timestamp; -} - -double -Channel::BitRate() -{ - double rate; - WebRtc_UWord64 currTime = TickTime::MillisecondTimestamp(); - _channelCritSect->Enter(); - rate = ((double)_totalBytes * 8.0)/ (double)(currTime - _beginTime); - _channelCritSect->Leave(); - return rate; -} diff --git a/modules/audio_coding/main/test/Channel.h b/modules/audio_coding/main/test/Channel.h deleted file mode 100644 index 396fadcae..000000000 --- a/modules/audio_coding/main/test/Channel.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef CHANNEL_H -#define CHANNEL_H - -#include - -#include "audio_coding_module.h" -#include "critical_section_wrapper.h" -#include "rw_lock_wrapper.h" - - -#define MAX_NUM_PAYLOADS 50 -#define MAX_NUM_FRAMESIZES 6 - - -struct ACMTestFrameSizeStats -{ - WebRtc_UWord16 frameSizeSample; - WebRtc_Word16 maxPayloadLen; - WebRtc_UWord32 numPackets; - WebRtc_UWord64 totalPayloadLenByte; - WebRtc_UWord64 totalEncodedSamples; - double rateBitPerSec; - double usageLenSec; - -}; - -struct ACMTestPayloadStats -{ - bool newPacket; - WebRtc_Word16 payloadType; - WebRtc_Word16 lastPayloadLenByte; - WebRtc_UWord32 lastTimestamp; - ACMTestFrameSizeStats frameSizeStats[MAX_NUM_FRAMESIZES]; -}; - -using namespace webrtc; - -class Channel: public AudioPacketizationCallback -{ -public: - - Channel( - WebRtc_Word16 chID = -1); - ~Channel(); - - WebRtc_Word32 SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - - void RegisterReceiverACM( - AudioCodingModule *acm); - - void ResetStats(); - - WebRtc_Word16 Stats( - CodecInst& codecInst, - ACMTestPayloadStats& payloadStats); - - void Stats( - WebRtc_UWord32* numPackets); - - void Stats( - WebRtc_UWord8* payloadLenByte, - WebRtc_UWord32* payloadType); - - void PrintStats( - CodecInst& codecInst); - - void SetIsStereo(bool isStereo) - { - _isStereo = isStereo; - } - - WebRtc_UWord32 LastInTimestamp(); - - void SetFECTestWithPacketLoss(bool usePacketLoss) - { - _useFECTestWithPacketLoss = usePacketLoss; - } - - double BitRate(); - -private: - void CalcStatistics( - WebRtcRTPHeader& rtpInfo, - WebRtc_UWord16 payloadSize); - - AudioCodingModule* _receiverACM; - WebRtc_UWord16 _seqNo; - // 60 msec * 32 sample (max) / msec * 2 description (maybe) * 2 bytes / sample - WebRtc_UWord8 _payloadData[60 * 32 * 2 * 2]; - - CriticalSectionWrapper* _channelCritSect; - FILE* _bitStreamFile; - bool _saveBitStream; - WebRtc_Word16 _lastPayloadType; - ACMTestPayloadStats _payloadStats[MAX_NUM_PAYLOADS]; - bool _isStereo; - WebRtcRTPHeader _rtpInfo; - bool _leftChannel; - WebRtc_UWord32 _lastInTimestamp; - // FEC Test variables - WebRtc_Word16 _packetLoss; - bool _useFECTestWithPacketLoss; - WebRtc_Word16 _chID; - WebRtc_UWord64 _beginTime; - WebRtc_UWord64 _totalBytes; -}; - - -#endif diff --git a/modules/audio_coding/main/test/EncodeDecodeTest.cpp b/modules/audio_coding/main/test/EncodeDecodeTest.cpp deleted file mode 100644 index 08555da4e..000000000 --- a/modules/audio_coding/main/test/EncodeDecodeTest.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "EncodeDecodeTest.h" -#include "common_types.h" - -#include -#include -#include "trace.h" -#include "utility.h" - -Receiver::Receiver() -: -_playoutLengthSmpls(WEBRTC_10MS_PCM_AUDIO), -_payloadSizeBytes(MAX_INCOMING_PAYLOAD) -{ -} - -void Receiver::Setup(AudioCodingModule *acm, RTPStream *rtpStream) -{ - struct CodecInst recvCodec; - int noOfCodecs; - acm->InitializeReceiver(); - - noOfCodecs = acm->NumberOfCodecs(); - for (int i=0; i < noOfCodecs; i++) - { - acm->Codec((WebRtc_UWord8)i, recvCodec); - if (acm->RegisterReceiveCodec(recvCodec) != 0) - { - printf("Unable to register codec: for run: codecId: %d\n", codeId); - exit(1); - } - } - - char filename[128]; - _rtpStream = rtpStream; - int playSampFreq; - - if (testMode == 1) - { - playSampFreq=recvCodec.plfreq; - //output file for current run - sprintf(filename,"./modules/audio_coding/main/test/res_tests/out%dFile.pcm",codeId); - _pcmFile.Open(filename, recvCodec.plfreq, "wb+"); - } - else if (testMode == 0) - { - playSampFreq=32000; - //output file for current run - sprintf(filename,"./modules/audio_coding/main/test/res_autotests/encodeDecode_out%d.pcm",codeId); - _pcmFile.Open(filename, 32000/*recvCodec.plfreq*/, "wb+"); - } - else - { - printf("\nValid output frequencies:\n"); - printf("8000\n16000\n32000\n-1, which means output freq equal to received signal freq"); - printf("\n\nChoose output sampling frequency: "); - scanf("%d", &playSampFreq); - char fileName[] = "./modules/audio_coding/main/test/outFile.pcm"; - _pcmFile.Open(fileName, 32000, "wb+"); - } - - _realPayloadSizeBytes = 0; - _playoutBuffer = new WebRtc_Word16[WEBRTC_10MS_PCM_AUDIO]; - _frequency = playSampFreq; - _acm = acm; - _firstTime = true; -} - -void Receiver::Teardown() -{ - delete [] _playoutBuffer; - _pcmFile.Close(); - if (testMode > 1) Trace::ReturnTrace(); -} - -bool Receiver::IncomingPacket() -{ - if (!_rtpStream->EndOfFile()) - { - if (_firstTime) - { - _firstTime = false; - _realPayloadSizeBytes = _rtpStream->Read(&_rtpInfo, _incomingPayload, _payloadSizeBytes, &_nextTime); - if (_realPayloadSizeBytes == 0 && _rtpStream->EndOfFile()) - { - _firstTime = true; - return true; - } - } - - WebRtc_Word32 ok = _acm->IncomingPacket(_incomingPayload, _realPayloadSizeBytes, _rtpInfo); - if (ok != 0) - { - printf("Error when inserting packet to ACM, for run: codecId: %d\n", codeId); - exit(1); - } - _realPayloadSizeBytes = _rtpStream->Read(&_rtpInfo, _incomingPayload, _payloadSizeBytes, &_nextTime); - if (_realPayloadSizeBytes == 0 && _rtpStream->EndOfFile()) - { - _firstTime = true; - } - } - return true; -} - -bool Receiver::PlayoutData() -{ - AudioFrame audioFrame; - - if (_acm->PlayoutData10Ms(_frequency, audioFrame) != 0) - { - printf("Error when calling PlayoutData10Ms, for run: codecId: %d\n", codeId); - exit(1); - } - if (_playoutLengthSmpls == 0) - { - return false; - } - _pcmFile.Write10MsData(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples); - return true; -} - -void Receiver::Run() -{ - WebRtc_UWord8 counter500Ms = 50; - - WebRtc_UWord32 clock = 0; - - while (counter500Ms > 0) - { - if (clock == 0 || clock >= _nextTime) - { - IncomingPacket(); - if (clock == 0) - { - clock = _nextTime; - } - } - if ((clock % 10) == 0) - { - if (!PlayoutData()) - { - clock++; - continue; - } - } - if (_rtpStream->EndOfFile()) - { - counter500Ms--; - } - clock++; - } -} - -EncodeDecodeTest::EncodeDecodeTest() -{ - _testMode = 2; - Trace::CreateTrace(); - Trace::SetTraceFile("acm_encdec_test.txt"); -} - -EncodeDecodeTest::EncodeDecodeTest(int testMode) -{ - //testMode == 0 for autotest - //testMode == 1 for testing all codecs/parameters - //testMode > 1 for specific user-input test (as it was used before) - _testMode = testMode; - if(_testMode != 0) - { - Trace::CreateTrace(); - Trace::SetTraceFile("acm_encdec_test.txt"); - } -} -void EncodeDecodeTest::Perform() -{ - - if(_testMode == 0) - { - printf("Running Encode/Decode Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- EncodeDecodeTest ----------"); - } - - int numCodecs = 1; - int codePars[3]; //freq, pacsize, rate - int playoutFreq[3]; //8, 16, 32k - - int numPars[52]; //number of codec parameters sets (rate,freq,pacsize)to test, for a given codec - - codePars[0]=0; - codePars[1]=0; - codePars[2]=0; - - if (_testMode == 1) - { - AudioCodingModule *acmTmp = AudioCodingModule::Create(0); - struct CodecInst sendCodecTmp; - numCodecs = acmTmp->NumberOfCodecs(); - printf("List of supported codec.\n"); - for(int n = 0; n < numCodecs; n++) - { - acmTmp->Codec(n, sendCodecTmp); - if (STR_CASE_CMP(sendCodecTmp.plname, "telephone-event") == 0) { - numPars[n] = 0; - } else if (STR_CASE_CMP(sendCodecTmp.plname, "cn") == 0) { - numPars[n] = 0; - } else if (STR_CASE_CMP(sendCodecTmp.plname, "red") == 0) { - numPars[n] = 0; - } else { - numPars[n] = 1; - printf("%d %s\n", n, sendCodecTmp.plname); - } - } - AudioCodingModule::Destroy(acmTmp); - playoutFreq[1]=16000; - } - else if (_testMode == 0) - { - AudioCodingModule *acmTmp = AudioCodingModule::Create(0); - numCodecs = acmTmp->NumberOfCodecs(); - AudioCodingModule::Destroy(acmTmp); - struct CodecInst dummyCodec; - - //chose range of testing for codecs/parameters - for(int i = 0 ; i < numCodecs ; i++) - { - numPars[i] = 1; - acmTmp->Codec(i, dummyCodec); - if (STR_CASE_CMP(dummyCodec.plname, "telephone-event") == 0) - { - numPars[i] = 0; - } else if (STR_CASE_CMP(dummyCodec.plname, "cn") == 0) { - numPars[i] = 0; - } else if (STR_CASE_CMP(dummyCodec.plname, "red") == 0) { - numPars[i] = 0; - } - } - playoutFreq[1] = 16000; - } - else - { - numCodecs = 1; - numPars[0] = 1; - playoutFreq[1]=16000; - } - - _receiver.testMode = _testMode; - - //loop over all codecs: - for(int codeId=0;codeId -#else -# include -#endif - - -#include -#include -#include - -TestPacketization::TestPacketization(RTPStream *rtpStream, WebRtc_UWord16 frequency) -: -_frequency(frequency), -_seqNo(0) -{ - _rtpStream = rtpStream; -} - -TestPacketization::~TestPacketization() -{ -} - -WebRtc_Word32 TestPacketization::SendData( - const FrameType /* frameType */, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* /* fragmentation */) -{ - _rtpStream->Write(payloadType, timeStamp, _seqNo++, payloadData, payloadSize, _frequency); - //delete [] payloadData; - return 1; -} - -Sender::Sender() -: -_acm(NULL), -//_payloadData(NULL), -_payloadSize(0), -_timeStamp(0) -{ -} - -void Sender::Setup(AudioCodingModule *acm, RTPStream *rtpStream) -{ - acm->InitializeSender(); - struct CodecInst sendCodec; - int noOfCodecs = acm->NumberOfCodecs(); - int codecNo; - - if (testMode == 1) - { - //set the codec, input file, and parameters for the current test - codecNo = codeId; - //use same input file for now - char fileName[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _pcmFile.Open(fileName, 32000, "rb"); - } - else if (testMode == 0) - { - //set the codec, input file, and parameters for the current test - codecNo = codeId; - acm->Codec(codecNo, sendCodec); - //use same input file for now - char fileName[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _pcmFile.Open(fileName, 32000, "rb"); - } - else - { - printf("List of supported codec.\n"); - for(int n = 0; n < noOfCodecs; n++) - { - acm->Codec(n, sendCodec); - printf("%d %s\n", n, sendCodec.plname); - } - printf("Choose your codec:"); - - scanf("%d", &codecNo); - char fileName[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _pcmFile.Open(fileName, 32000, "rb"); - } - - acm->Codec(codecNo, sendCodec); - acm->RegisterSendCodec(sendCodec); - _packetization = new TestPacketization(rtpStream, sendCodec.plfreq); - if(acm->RegisterTransportCallback(_packetization) < 0) - { - printf("Registering Transport Callback failed, for run: codecId: %d: --\n", - codeId); - } - - _acm = acm; -} - -void Sender::Teardown() -{ - _pcmFile.Close(); - delete _packetization; -} - -bool Sender::Add10MsData() -{ - if (!_pcmFile.EndOfFile()) - { - _pcmFile.Read10MsData(_audioFrame); - WebRtc_Word32 ok = _acm->Add10MsData(_audioFrame); - if (ok != 0) - { - printf("Error calling Add10MsData: for run: codecId: %d\n", - codeId); - exit(1); - } - //_audioFrame._timeStamp += _pcmFile.PayloadLength10Ms(); - return true; - } - return false; -} - -bool Sender::Process() -{ - WebRtc_Word32 ok = _acm->Process(); - if (ok < 0) - { - printf("Error calling Add10MsData: for run: codecId: %d\n", - codeId); - exit(1); - } - return true; -} - -void Sender::Run() -{ - while (true) - { - if (!Add10MsData()) - { - break; - } - if (!Process()) // This could be done in a processing thread - { - break; - } - } -} - -EncodeToFileTest::EncodeToFileTest() -{ -} - - -void EncodeToFileTest::Perform(int fileType, int codeId, int* codePars, int testMode) -{ - AudioCodingModule *acm = AudioCodingModule::Create(0); - RTPFile rtpFile; - char fileName[] = "outFile.rtp"; - rtpFile.Open(fileName, "wb+"); - rtpFile.WriteHeader(); - - //for auto_test and logging - _sender.testMode = testMode; - _sender.codeId = codeId; - - _sender.Setup(acm, &rtpFile); - struct CodecInst sendCodecInst; - if(acm->SendCodec(sendCodecInst) >= 0) - { - _sender.Run(); - } - _sender.Teardown(); - rtpFile.Close(); - AudioCodingModule::Destroy(acm); -} diff --git a/modules/audio_coding/main/test/EncodeToFileTest.h b/modules/audio_coding/main/test/EncodeToFileTest.h deleted file mode 100644 index fdd380439..000000000 --- a/modules/audio_coding/main/test/EncodeToFileTest.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ENCODETOFILETEST_H -#define ENCODETOFILETEST_H - -#include "ACMTest.h" -#include "audio_coding_module.h" -#include "typedefs.h" -#include "RTPFile.h" -#include "PCMFile.h" -#include - -using namespace webrtc; - -// TestPacketization callback which writes the encoded payloads to file -class TestPacketization : public AudioPacketizationCallback -{ -public: - TestPacketization(RTPStream *rtpStream, WebRtc_UWord16 frequency); - ~TestPacketization(); - virtual WebRtc_Word32 SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - -private: - static void MakeRTPheader(WebRtc_UWord8* rtpHeader, - WebRtc_UWord8 payloadType, WebRtc_Word16 seqNo, - WebRtc_UWord32 timeStamp, WebRtc_UWord32 ssrc); - RTPStream* _rtpStream; - WebRtc_Word32 _frequency; - WebRtc_Word16 _seqNo; -}; - -class Sender -{ -public: - Sender(); - void Setup(AudioCodingModule *acm, RTPStream *rtpStream); - void Teardown(); - void Run(); - bool Add10MsData(); - bool Process(); - - //for auto_test and logging - WebRtc_UWord8 testMode; - WebRtc_UWord8 codeId; - -private: - AudioCodingModule* _acm; - PCMFile _pcmFile; - //WebRtc_Word16* _payloadData; - AudioFrame _audioFrame; - WebRtc_UWord16 _payloadSize; - WebRtc_UWord32 _timeStamp; - TestPacketization* _packetization; -}; - -// Test class -class EncodeToFileTest : public ACMTest -{ -public: - EncodeToFileTest(); - virtual void Perform(int fileType, int codeId, int* codePars, int testMode); -protected: - Sender _sender; -}; - -#endif diff --git a/modules/audio_coding/main/test/PCMFile.cpp b/modules/audio_coding/main/test/PCMFile.cpp deleted file mode 100644 index 7418a3e34..000000000 --- a/modules/audio_coding/main/test/PCMFile.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - - -#include "PCMFile.h" -#include "module_common_types.h" - -#define MAX_FILE_NAME_LENGTH_BYTE 500 - - - -PCMFile::PCMFile(): -_pcmFile(NULL), -_nSamples10Ms(160), -_frequency(16000), -_endOfFile(false), -_autoRewind(false), -_rewinded(false), -_timestamp(0), -_readStereo(false), -_saveStereo(false) -{ - _timestamp = (((WebRtc_UWord32)rand() & 0x0000FFFF) << 16) | - ((WebRtc_UWord32)rand() & 0x0000FFFF); -} - -/* -PCMFile::~PCMFile() -{ - if(_pcmFile != NULL) - { - fclose(_pcmFile); - _pcmFile = NULL; - } -} -*/ - -WebRtc_Word16 -PCMFile::ChooseFile( - char* fileName, - WebRtc_Word16 maxLen) -{ - WebRtc_Word8 tmpName[MAX_FILE_NAME_LENGTH_BYTE]; - //strcpy(_fileName, "in.pcm"); - //printf("\n\nPlease enter the input file: "); - fgets(tmpName, MAX_FILE_NAME_LENGTH_BYTE, stdin); - tmpName[MAX_FILE_NAME_LENGTH_BYTE-1] = '\0'; - WebRtc_Word16 n = 0; - - // removing leading spaces - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (tmpName[n] != 0) && - (n < MAX_FILE_NAME_LENGTH_BYTE)) - { - n++; - } - if(n > 0) - { - memmove(tmpName, &tmpName[n], MAX_FILE_NAME_LENGTH_BYTE - n); - } - - //removing trailing spaces - n = (WebRtc_Word16)(strlen(tmpName) - 1); - if(n >= 0) - { - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (n >= 0)) - { - n--; - } - } - if(n >= 0) - { - tmpName[n + 1] = '\0'; - } - - WebRtc_Word16 len = (WebRtc_Word16)strlen(tmpName); - if(len > maxLen) - { - return -1; - } - if(len > 0) - { - strncpy(fileName, tmpName, len+1); - } - return 0; -} - -WebRtc_Word16 -PCMFile::ChooseFile( - char* fileName, - WebRtc_Word16 maxLen, - WebRtc_UWord16* frequencyHz) -{ - WebRtc_Word8 tmpName[MAX_FILE_NAME_LENGTH_BYTE]; - //strcpy(_fileName, "in.pcm"); - //printf("\n\nPlease enter the input file: "); - fgets(tmpName, MAX_FILE_NAME_LENGTH_BYTE, stdin); - tmpName[MAX_FILE_NAME_LENGTH_BYTE-1] = '\0'; - WebRtc_Word16 n = 0; - - // removing leading spaces - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (tmpName[n] != 0) && - (n < MAX_FILE_NAME_LENGTH_BYTE)) - { - n++; - } - if(n > 0) - { - memmove(tmpName, &tmpName[n], MAX_FILE_NAME_LENGTH_BYTE - n); - } - - //removing trailing spaces - n = (WebRtc_Word16)(strlen(tmpName) - 1); - if(n >= 0) - { - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (n >= 0)) - { - n--; - } - } - if(n >= 0) - { - tmpName[n + 1] = '\0'; - } - - WebRtc_Word16 len = (WebRtc_Word16)strlen(tmpName); - if(len > maxLen) - { - return -1; - } - if(len > 0) - { - strncpy(fileName, tmpName, len+1); - } - printf("Enter the sampling frequency (in Hz) of the above file [%u]: ", *frequencyHz); - fgets(tmpName, 10, stdin); - WebRtc_UWord16 tmpFreq = (WebRtc_UWord16)atoi(tmpName); - if(tmpFreq > 0) - { - *frequencyHz = tmpFreq; - } - return 0; -} - -void -PCMFile::Open( - char* filename, - WebRtc_UWord16 frequency, - const char* mode, - bool autoRewind) -{ - if ((_pcmFile = fopen(filename, mode)) == NULL) - { - printf("Cannot open file %s.\n", filename); - throw "Unable to read file"; - exit(1); - } - _frequency = frequency; - _nSamples10Ms = (WebRtc_UWord16)(_frequency / 100); - _autoRewind = autoRewind; - _endOfFile = false; - _rewinded = false; -} - -WebRtc_Word32 -PCMFile::SamplingFrequency() const -{ - return _frequency; -} - -WebRtc_UWord16 -PCMFile::PayloadLength10Ms() const -{ - return _nSamples10Ms; -} - -WebRtc_Word32 -PCMFile::Read10MsData( - AudioFrame& audioFrame) -{ - WebRtc_UWord16 noChannels = 1; - if (_readStereo) - { - noChannels = 2; - } - - WebRtc_Word32 payloadSize = (WebRtc_Word32)fread(audioFrame._payloadData, sizeof(WebRtc_UWord16), _nSamples10Ms*noChannels, _pcmFile); - if (payloadSize < _nSamples10Ms*noChannels) { - for (int k = payloadSize; k < _nSamples10Ms*noChannels; k++) - { - audioFrame._payloadData[k] = 0; - } - if(_autoRewind) - { - rewind(_pcmFile); - _rewinded = true; - } - else - { - _endOfFile = true; - } - } - audioFrame._payloadDataLengthInSamples = _nSamples10Ms; - audioFrame._frequencyInHz = _frequency; - audioFrame._audioChannel = noChannels; - audioFrame._timeStamp = _timestamp; - _timestamp += _nSamples10Ms; - return _nSamples10Ms; -} - -void -PCMFile::Write10MsData( - AudioFrame& audioFrame) -{ - if(audioFrame._audioChannel == 1) - { - if(!_saveStereo) - { - fwrite(audioFrame._payloadData, sizeof(WebRtc_UWord16), - audioFrame._payloadDataLengthInSamples, _pcmFile); - } - else - { - WebRtc_Word16* stereoAudio = new WebRtc_Word16[2 * - audioFrame._payloadDataLengthInSamples]; - int k; - for(k = 0; k < audioFrame._payloadDataLengthInSamples; k++) - { - stereoAudio[k<<1] = audioFrame._payloadData[k]; - stereoAudio[(k<<1) + 1] = audioFrame._payloadData[k]; - } - fwrite(stereoAudio, sizeof(WebRtc_Word16), 2*audioFrame._payloadDataLengthInSamples, - _pcmFile); - delete [] stereoAudio; - } - } - else - { - fwrite(audioFrame._payloadData, sizeof(WebRtc_Word16), - audioFrame._audioChannel * audioFrame._payloadDataLengthInSamples, _pcmFile); - } -} - - -void -PCMFile::Write10MsData( - WebRtc_Word16* playoutBuffer, - WebRtc_UWord16 playoutLengthSmpls) -{ - fwrite(playoutBuffer, sizeof(WebRtc_UWord16), playoutLengthSmpls, _pcmFile); -} - - -void -PCMFile::Close() -{ - fclose(_pcmFile); - _pcmFile = NULL; -} - -void -PCMFile::Rewind() -{ - rewind(_pcmFile); - _endOfFile = false; -} - -bool -PCMFile::Rewinded() -{ - return _rewinded; -} - -void -PCMFile::SaveStereo( - bool saveStereo) -{ - _saveStereo = saveStereo; -} - -void -PCMFile::ReadStereo( - bool readStereo) -{ - _readStereo = readStereo; -} diff --git a/modules/audio_coding/main/test/PCMFile.h b/modules/audio_coding/main/test/PCMFile.h deleted file mode 100644 index dd8d06f43..000000000 --- a/modules/audio_coding/main/test/PCMFile.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef PCMFILE_H -#define PCMFILE_H - -#include "typedefs.h" -#include "module_common_types.h" -#include -#include - -// class PCMStream -// { -// protected: -// PCMStream(){} -// ~PCMStream(){} -// public: -// virtual WebRtc_Word32 Read10MsData(AudioFrame& audioFrame) = 0; -// virtual void Write10MsData(WebRtc_Word16 *playoutBuffer, WebRtc_UWord16 playoutLengthSmpls) = 0; -// virtual WebRtc_UWord16 PayloadLength10Ms() const = 0; -// virtual WebRtc_Word32 SamplingFrequency() const = 0; -// }; - - -using namespace webrtc; - -class PCMFile /*: public PCMStream*/ -{ -public: - PCMFile(); - ~PCMFile() - { - if(_pcmFile != NULL) - { - fclose(_pcmFile); - } - } - void Open(char *filename, WebRtc_UWord16 frequency, const char *mode, bool autoRewind = false); - - WebRtc_Word32 Read10MsData(AudioFrame& audioFrame); - - void Write10MsData(WebRtc_Word16 *playoutBuffer, WebRtc_UWord16 playoutLengthSmpls); - void Write10MsData(AudioFrame& audioFrame); - - WebRtc_UWord16 PayloadLength10Ms() const; - WebRtc_Word32 SamplingFrequency() const; - void Close(); - bool EndOfFile() const { return _endOfFile; } - void Rewind(); - static WebRtc_Word16 ChooseFile(char* fileName, WebRtc_Word16 maxLen, WebRtc_UWord16* frequencyHz); - static WebRtc_Word16 ChooseFile(char* fileName, WebRtc_Word16 maxLen); - bool Rewinded(); - void SaveStereo( - bool saveStereo = true); - void ReadStereo( - bool readStereo = true); -private: - FILE* _pcmFile; - WebRtc_UWord16 _nSamples10Ms; - WebRtc_Word32 _frequency; - bool _endOfFile; - bool _autoRewind; - bool _rewinded; - WebRtc_UWord32 _timestamp; - bool _saveStereo; - bool _readStereo; -}; - -#endif diff --git a/modules/audio_coding/main/test/RTPFile.cpp b/modules/audio_coding/main/test/RTPFile.cpp deleted file mode 100644 index 23d78d761..000000000 --- a/modules/audio_coding/main/test/RTPFile.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "RTPFile.h" -#include "rw_lock_wrapper.h" -#include "engine_configurations.h" -#include - -#ifdef WIN32 -# include -#else -# include -#endif - -#include "audio_coding_module.h" - -void RTPStream::ParseRTPHeader(WebRtcRTPHeader* rtpInfo, const WebRtc_UWord8* rtpHeader) -{ - rtpInfo->header.payloadType = rtpHeader[1]; - rtpInfo->header.sequenceNumber = (static_cast(rtpHeader[2])<<8) | rtpHeader[3]; - rtpInfo->header.timestamp = (static_cast(rtpHeader[4])<<24) | - (static_cast(rtpHeader[5])<<16) | - (static_cast(rtpHeader[6])<<8) | - rtpHeader[7]; - rtpInfo->header.ssrc = (static_cast(rtpHeader[8])<<24) | - (static_cast(rtpHeader[9])<<16) | - (static_cast(rtpHeader[10])<<8) | - rtpHeader[11]; -} - -void RTPStream::MakeRTPheader(WebRtc_UWord8* rtpHeader, - WebRtc_UWord8 payloadType, WebRtc_Word16 seqNo, - WebRtc_UWord32 timeStamp, WebRtc_UWord32 ssrc) -{ - rtpHeader[0]=(unsigned char)0x80; - rtpHeader[1]=(unsigned char)(payloadType & 0xFF); - rtpHeader[2]=(unsigned char)((seqNo>>8)&0xFF); - rtpHeader[3]=(unsigned char)((seqNo)&0xFF); - rtpHeader[4]=(unsigned char)((timeStamp>>24)&0xFF); - rtpHeader[5]=(unsigned char)((timeStamp>>16)&0xFF); - - rtpHeader[6]=(unsigned char)((timeStamp>>8)&0xFF); - rtpHeader[7]=(unsigned char)(timeStamp & 0xFF); - - rtpHeader[8]=(unsigned char)((ssrc>>24)&0xFF); - rtpHeader[9]=(unsigned char)((ssrc>>16)&0xFF); - - rtpHeader[10]=(unsigned char)((ssrc>>8)&0xFF); - rtpHeader[11]=(unsigned char)(ssrc & 0xFF); -} - - -RTPPacket::RTPPacket(WebRtc_UWord8 payloadType, WebRtc_UWord32 timeStamp, - WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency) - : -payloadType(payloadType), -timeStamp(timeStamp), -seqNo(seqNo), -payloadSize(payloadSize), -frequency(frequency) -{ - if (payloadSize > 0) - { - this->payloadData = new WebRtc_UWord8[payloadSize]; - memcpy(this->payloadData, payloadData, payloadSize); - } -} - -RTPPacket::~RTPPacket() -{ - delete [] payloadData; -} - -RTPBuffer::RTPBuffer() -{ - _queueRWLock = RWLockWrapper::CreateRWLock(); -} - -RTPBuffer::~RTPBuffer() -{ - delete _queueRWLock; -} - -void -RTPBuffer::Write(const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency) -{ - RTPPacket *packet = new RTPPacket(payloadType, timeStamp, seqNo, payloadData, payloadSize, frequency); - _queueRWLock->AcquireLockExclusive(); - _rtpQueue.push(packet); - _queueRWLock->ReleaseLockExclusive(); -} - -WebRtc_UWord16 -RTPBuffer::Read(WebRtcRTPHeader* rtpInfo, - WebRtc_Word8* payloadData, - WebRtc_UWord16 payloadSize, - WebRtc_UWord32* offset) -{ - _queueRWLock->AcquireLockShared(); - RTPPacket *packet = _rtpQueue.front(); - _rtpQueue.pop(); - _queueRWLock->ReleaseLockShared(); - rtpInfo->header.markerBit = 1; - rtpInfo->header.payloadType = packet->payloadType; - rtpInfo->header.sequenceNumber = packet->seqNo; - rtpInfo->header.ssrc = 0; - rtpInfo->header.timestamp = packet->timeStamp; - if (packet->payloadSize > 0 && payloadSize >= packet->payloadSize) - { - memcpy(payloadData, packet->payloadData, packet->payloadSize); - } - else - { - throw "Payload buffer too small"; - exit(1); - } -/*#ifdef WEBRTC_CODEC_G722 - if(ACMCodecDB::_mycodecs[ACMCodecDB::g722].pltype == packet->payloadType) - { - *offset = (packet->timeStamp/(packet->frequency/1000))<<1; - } - else - { -#endif*/ - *offset = (packet->timeStamp/(packet->frequency/1000)); -/*#ifdef WEBRTC_CODEC_G722 - } -#endif*/ - return packet->payloadSize; -} - -bool -RTPBuffer::EndOfFile() const -{ - _queueRWLock->AcquireLockShared(); - bool eof = _rtpQueue.empty(); - _queueRWLock->ReleaseLockShared(); - return eof; -} - -void RTPFile::Open(char *filename, const char *mode) -{ - if ((_rtpFile = fopen(filename, mode)) == NULL) - { - printf("Cannot write file %s.\n", filename); - throw "Unable to write file"; - exit(1); - } -} - -void RTPFile::Close() -{ - if (_rtpFile != NULL) - { - fclose(_rtpFile); - _rtpFile = NULL; - } -} - - -void RTPFile::WriteHeader() -{ - // Write data in a format that NetEQ and RTP Play can parse - fprintf(_rtpFile, "#!RTPencode%s\n", "1.0"); - WebRtc_UWord32 dummy_variable = 0; // should be converted to network endian format, but does not matter when 0 - fwrite(&dummy_variable, 4, 1, _rtpFile); - fwrite(&dummy_variable, 4, 1, _rtpFile); - fwrite(&dummy_variable, 4, 1, _rtpFile); - fwrite(&dummy_variable, 2, 1, _rtpFile); - fwrite(&dummy_variable, 2, 1, _rtpFile); - fflush(_rtpFile); -} - -void RTPFile::ReadHeader() -{ - WebRtc_UWord32 start_sec, start_usec, source; - WebRtc_UWord16 port, padding; - char fileHeader[40]; - fgets(fileHeader, 40, _rtpFile); - fread(&start_sec, 4, 1, _rtpFile); - start_sec=ntohl(start_sec); - fread(&start_usec, 4, 1, _rtpFile); - start_usec=ntohl(start_usec); - fread(&source, 4, 1, _rtpFile); - source=ntohl(source); - fread(&port, 2, 1, _rtpFile); - port=ntohs(port); - fread(&padding, 2, 1, _rtpFile); - padding=ntohs(padding); -} - -void RTPFile::Write(const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency) -{ - /* write RTP packet to file */ - WebRtc_UWord8 rtpHeader[12]; - MakeRTPheader(rtpHeader, payloadType, seqNo, timeStamp, 0); - WebRtc_UWord16 lengthBytes = htons(12 + payloadSize + 8); - WebRtc_UWord16 plen = htons(12 + payloadSize); - WebRtc_UWord32 offsetMs; -/*#ifdef WEBRTC_CODEC_G722 - if(ACMCodecDB::_mycodecs[ACMCodecDB::g722].pltype == payloadType) - { - offsetMs = (timeStamp/(frequency/1000))<<1; - } - else - { -#endif*/ - offsetMs = (timeStamp/(frequency/1000)); -/*#ifdef WEBRTC_CODEC_G722 - } -#endif*/ - offsetMs = htonl(offsetMs); - fwrite(&lengthBytes, 2, 1, _rtpFile); - fwrite(&plen, 2, 1, _rtpFile); - fwrite(&offsetMs, 4, 1, _rtpFile); - fwrite(rtpHeader, 12, 1, _rtpFile); - fwrite(payloadData, 1, payloadSize, _rtpFile); -} - -WebRtc_UWord16 RTPFile::Read(WebRtcRTPHeader* rtpInfo, - WebRtc_Word8* payloadData, - WebRtc_UWord16 payloadSize, - WebRtc_UWord32* offset) -{ - WebRtc_UWord16 lengthBytes; - WebRtc_UWord16 plen; - WebRtc_UWord8 rtpHeader[12]; - fread(&lengthBytes, 2, 1, _rtpFile); - if (feof(_rtpFile)) - { - _rtpEOF = true; - return 0; - } - fread(&plen, 2, 1, _rtpFile); - if (feof(_rtpFile)) - { - _rtpEOF = true; - return 0; - } - fread(offset, 4, 1, _rtpFile); - if (feof(_rtpFile)) - { - _rtpEOF = true; - return 0; - } - lengthBytes = ntohs(lengthBytes); - plen = ntohs(plen); - *offset = ntohl(*offset); - if (plen < 12) - { - throw "Unable to read RTP file"; - exit(1); - } - fread(rtpHeader, 12, 1, _rtpFile); - if (feof(_rtpFile)) - { - _rtpEOF = true; - return 0; - } - ParseRTPHeader(rtpInfo, rtpHeader); - rtpInfo->type.Audio.isCNG = false; - rtpInfo->type.Audio.channel = 1; - if (lengthBytes != plen + 8) - { - throw "Length parameters in RTP file doesn't match"; - exit(1); - } - if (plen == 0) - { - return 0; - } - else if (lengthBytes - 20 > payloadSize) - { - throw "Payload buffer too small"; - exit(1); - } - lengthBytes -= 20; - fread(payloadData, 1, lengthBytes, _rtpFile); - if (feof(_rtpFile)) - { - _rtpEOF = true; - } - return lengthBytes; -} - diff --git a/modules/audio_coding/main/test/RTPFile.h b/modules/audio_coding/main/test/RTPFile.h deleted file mode 100644 index 23a43d08e..000000000 --- a/modules/audio_coding/main/test/RTPFile.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RTPFILE_H -#define RTPFILE_H - -#include "audio_coding_module.h" -#include "module_common_types.h" -#include "typedefs.h" -#include "rw_lock_wrapper.h" -#include -#include - -using namespace webrtc; - -class RTPStream -{ -public: - virtual ~RTPStream(){} - - virtual void Write(const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency) = 0; - virtual WebRtc_UWord16 Read(WebRtcRTPHeader* rtpInfo, - WebRtc_Word8* payloadData, - WebRtc_UWord16 payloadSize, - WebRtc_UWord32* offset) = 0; - virtual bool EndOfFile() const = 0; - -protected: - void MakeRTPheader(WebRtc_UWord8* rtpHeader, - WebRtc_UWord8 payloadType, WebRtc_Word16 seqNo, - WebRtc_UWord32 timeStamp, WebRtc_UWord32 ssrc); - void ParseRTPHeader(WebRtcRTPHeader* rtpInfo, const WebRtc_UWord8* rtpHeader); -}; - -class RTPPacket -{ -public: - RTPPacket(WebRtc_UWord8 payloadType, WebRtc_UWord32 timeStamp, - WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency); - ~RTPPacket(); - WebRtc_UWord8 payloadType; - WebRtc_UWord32 timeStamp; - WebRtc_Word16 seqNo; - WebRtc_UWord8* payloadData; - WebRtc_UWord16 payloadSize; - WebRtc_UWord32 frequency; -}; - -class RTPBuffer : public RTPStream -{ -public: - RTPBuffer(); - ~RTPBuffer(); - void Write(const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency); - WebRtc_UWord16 Read(WebRtcRTPHeader* rtpInfo, - WebRtc_Word8* payloadData, - WebRtc_UWord16 payloadSize, - WebRtc_UWord32* offset); - virtual bool EndOfFile() const; -private: - RWLockWrapper* _queueRWLock; - std::queue _rtpQueue; -}; - -class RTPFile : public RTPStream -{ -public: - ~RTPFile(){} - RTPFile() : _rtpFile(NULL),_rtpEOF(false) {} - void Open(char *outFilename, const char *mode); - void Close(); - void WriteHeader(); - void ReadHeader(); - void Write(const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_Word16 seqNo, const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, WebRtc_UWord32 frequency); - WebRtc_UWord16 Read(WebRtcRTPHeader* rtpInfo, - WebRtc_Word8* payloadData, - WebRtc_UWord16 payloadSize, - WebRtc_UWord32* offset); - bool EndOfFile() const { return _rtpEOF; } -private: - FILE* _rtpFile; - bool _rtpEOF; -}; - -#endif diff --git a/modules/audio_coding/main/test/SpatialAudio.cpp b/modules/audio_coding/main/test/SpatialAudio.cpp deleted file mode 100644 index 016ec0adb..000000000 --- a/modules/audio_coding/main/test/SpatialAudio.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include - -#include "SpatialAudio.h" -#include "utility.h" -#include "trace.h" -#include "common_types.h" - -using namespace webrtc; - -#define NUM_PANN_COEFFS 10 - -SpatialAudio::SpatialAudio(int testMode) -{ - _testMode = testMode; -} - -SpatialAudio::~SpatialAudio() -{ - AudioCodingModule::Destroy(_acmLeft); - AudioCodingModule::Destroy(_acmRight); - AudioCodingModule::Destroy(_acmReceiver); - delete _channel; - _inFile.Close(); - _outFile.Close(); -} - -WebRtc_Word16 -SpatialAudio::Setup() -{ - // Create ACMs and the Channel; - _acmLeft = AudioCodingModule::Create(1); - _acmRight = AudioCodingModule::Create(2); - _acmReceiver = AudioCodingModule::Create(3); - _channel = new Channel; - - // Register callback for the sender side. - CHECK_ERROR(_acmLeft->RegisterTransportCallback(_channel)); - CHECK_ERROR(_acmRight->RegisterTransportCallback(_channel)); - // Register the receiver ACM in channel - _channel->RegisterReceiverACM(_acmReceiver); - - WebRtc_Word8 audioFileName[MAX_FILE_NAME_LENGTH_BYTE]; - WebRtc_UWord16 sampFreqHz = 32000; - - strncpy(audioFileName, "./modules/audio_coding/main/test/testfile32kHz.pcm", - MAX_FILE_NAME_LENGTH_BYTE - 1); - if(_testMode == 1) - { - printf("Enter the input file [%s]: ", audioFileName); - PCMFile::ChooseFile(audioFileName, MAX_FILE_NAME_LENGTH_BYTE, &sampFreqHz); - } - _inFile.Open(audioFileName, sampFreqHz, "rb", false); - - if(_testMode == 0) - { - strncpy(audioFileName, "./modules/audio_coding/main/test/res_autotests/out_spatial_autotest.pcm", - MAX_FILE_NAME_LENGTH_BYTE - 1); - } - else if(_testMode == 1) - { - printf("\n"); - strncpy(audioFileName, "./modules/audio_coding/main/test/res_tests/testspatial_out.pcm", - MAX_FILE_NAME_LENGTH_BYTE - 1); - printf("Enter the output file [%s]: ", audioFileName); - PCMFile::ChooseFile(audioFileName, MAX_FILE_NAME_LENGTH_BYTE, &sampFreqHz); - } - else - { - strncpy(audioFileName, "./modules/audio_coding/main/test/res_tests/testspatial_out.pcm", - MAX_FILE_NAME_LENGTH_BYTE - 1); - } - _outFile.Open(audioFileName, sampFreqHz, "wb", false); - _outFile.SaveStereo(true); - - - // Register couple of codecs as receive codec - CodecInst codecInst; - - _acmLeft->Codec((WebRtc_UWord8)0, codecInst); - codecInst.channels = 2; - CHECK_ERROR(_acmReceiver->RegisterReceiveCodec(codecInst)); - - _acmLeft->Codec((WebRtc_UWord8)3, codecInst); - codecInst.channels = 2; - CHECK_ERROR(_acmReceiver->RegisterReceiveCodec(codecInst)); - - _acmLeft->Codec((WebRtc_UWord8)1, codecInst); - CHECK_ERROR(_acmReceiver->RegisterReceiveCodec(codecInst)); - - _acmLeft->Codec((WebRtc_UWord8)4, codecInst); - CHECK_ERROR(_acmReceiver->RegisterReceiveCodec(codecInst)); - - return 0; -} - -void -SpatialAudio::Perform() -{ - if(_testMode == 0) - { - printf("Running SpatialAudio Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- SpatialAudio ----------"); - } - - Setup(); - - CodecInst codecInst; - _acmLeft->Codec((WebRtc_UWord8)1, codecInst); - CHECK_ERROR(_acmLeft->RegisterSendCodec(codecInst)); - EncodeDecode(); - - WebRtc_Word16 pannCntr = 0; - - double leftPanning[NUM_PANN_COEFFS] = - {1.00, 0.95, 0.90, 0.85, 0.80, 0.75, 0.70, 0.60, 0.55, 0.50}; - double rightPanning[NUM_PANN_COEFFS] = - {0.50, 0.55, 0.60, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00}; - - while((pannCntr + 1) < NUM_PANN_COEFFS) - { - _acmLeft->Codec((WebRtc_UWord8)0, codecInst); - codecInst.pacsize = 480; - CHECK_ERROR(_acmLeft->RegisterSendCodec(codecInst)); - CHECK_ERROR(_acmRight->RegisterSendCodec(codecInst)); - - EncodeDecode(leftPanning[pannCntr], rightPanning[pannCntr]); - pannCntr++; - - // Change codec - _acmLeft->Codec((WebRtc_UWord8)3, codecInst); - codecInst.pacsize = 320; - CHECK_ERROR(_acmLeft->RegisterSendCodec(codecInst)); - CHECK_ERROR(_acmRight->RegisterSendCodec(codecInst)); - - EncodeDecode(leftPanning[pannCntr], rightPanning[pannCntr]); - pannCntr++; - if(_testMode == 0) - { - printf("."); - } - } - - _acmLeft->Codec((WebRtc_UWord8)4, codecInst); - CHECK_ERROR(_acmLeft->RegisterSendCodec(codecInst)); - EncodeDecode(); - - _acmLeft->Codec((WebRtc_UWord8)0, codecInst); - codecInst.pacsize = 480; - CHECK_ERROR(_acmLeft->RegisterSendCodec(codecInst)); - CHECK_ERROR(_acmRight->RegisterSendCodec(codecInst)); - pannCntr = NUM_PANN_COEFFS -1; - while(pannCntr >= 0) - { - EncodeDecode(leftPanning[pannCntr], rightPanning[pannCntr]); - pannCntr--; - if(_testMode == 0) - { - printf("."); - } - } - if(_testMode == 0) - { - printf("Done!\n"); - } -} - -void -SpatialAudio::EncodeDecode( - const double leftPanning, - const double rightPanning) -{ - AudioFrame audioFrame; - WebRtc_Word32 outFileSampFreq = _outFile.SamplingFrequency(); - - const double rightToLeftRatio = rightPanning / leftPanning; - - _channel->SetIsStereo(true); - - while(!_inFile.EndOfFile()) - { - _inFile.Read10MsData(audioFrame); - for(int n = 0; n < audioFrame._payloadDataLengthInSamples; n++) - { - audioFrame._payloadData[n] = (WebRtc_Word16)floor( - audioFrame._payloadData[n] * leftPanning + 0.5); - } - CHECK_ERROR(_acmLeft->Add10MsData(audioFrame)); - - for(int n = 0; n < audioFrame._payloadDataLengthInSamples; n++) - { - audioFrame._payloadData[n] = (WebRtc_Word16)floor( - audioFrame._payloadData[n] * rightToLeftRatio + 0.5); - } - CHECK_ERROR(_acmRight->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmLeft->Process()); - CHECK_ERROR(_acmRight->Process()); - - CHECK_ERROR(_acmReceiver->PlayoutData10Ms(outFileSampFreq, audioFrame)); - _outFile.Write10MsData(audioFrame); - } - _inFile.Rewind(); -} - -void -SpatialAudio::EncodeDecode() -{ - AudioFrame audioFrame; - WebRtc_Word32 outFileSampFreq = _outFile.SamplingFrequency(); - - _channel->SetIsStereo(false); - - while(!_inFile.EndOfFile()) - { - _inFile.Read10MsData(audioFrame); - CHECK_ERROR(_acmLeft->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmLeft->Process()); - - CHECK_ERROR(_acmReceiver->PlayoutData10Ms(outFileSampFreq, audioFrame)); - _outFile.Write10MsData(audioFrame); - } - _inFile.Rewind(); -} - - diff --git a/modules/audio_coding/main/test/SpatialAudio.h b/modules/audio_coding/main/test/SpatialAudio.h deleted file mode 100644 index 6a137d44e..000000000 --- a/modules/audio_coding/main/test/SpatialAudio.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_TEST_SPATIAL_AUDIO_H -#define ACM_TEST_SPATIAL_AUDIO_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" -#include "audio_coding_module.h" -#include "utility.h" - -#define MAX_FILE_NAME_LENGTH_BYTE 500 - - -class SpatialAudio : public ACMTest -{ -public: - SpatialAudio(int testMode); - ~SpatialAudio(); - - void Perform(); -private: - WebRtc_Word16 Setup(); - void EncodeDecode(double leftPanning, double rightPanning); - void EncodeDecode(); - - AudioCodingModule* _acmLeft; - AudioCodingModule* _acmRight; - AudioCodingModule* _acmReceiver; - Channel* _channel; - PCMFile _inFile; - PCMFile _outFile; - int _testMode; -}; -#endif diff --git a/modules/audio_coding/main/test/TestAllCodecs.cpp b/modules/audio_coding/main/test/TestAllCodecs.cpp deleted file mode 100644 index 35e5b0a86..000000000 --- a/modules/audio_coding/main/test/TestAllCodecs.cpp +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "TestAllCodecs.h" - -#include "audio_coding_module_typedefs.h" -#include "common_types.h" -#include "engine_configurations.h" -#include -#include -#include "trace.h" -#include "utility.h" - -// Class for simulating packet handling -TestPack::TestPack(): -_receiverACM(NULL), -_seqNo(0), -_timeStampDiff(0), -_lastInTimestamp(0), -_totalBytes(0), -_payloadSize(0) -{ -} -TestPack::~TestPack() -{ -} - -void -TestPack::RegisterReceiverACM(AudioCodingModule* acm) -{ - _receiverACM = acm; - return; -} -WebRtc_Word32 -TestPack::SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation) -{ - WebRtcRTPHeader rtpInfo; - WebRtc_Word32 status; - WebRtc_UWord16 payloadDataSize = payloadSize; - - rtpInfo.header.markerBit = false; - rtpInfo.header.ssrc = 0; - rtpInfo.header.sequenceNumber = _seqNo++; - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.timestamp = timeStamp; - if(frameType == kAudioFrameCN) - { - rtpInfo.type.Audio.isCNG = true; - } - else - { - rtpInfo.type.Audio.isCNG = false; - } - if(frameType == kFrameEmpty) - { - // Skip this frame - return 0; - } - - rtpInfo.type.Audio.channel = 1; - memcpy(_payloadData, payloadData, payloadDataSize); - - status = _receiverACM->IncomingPacket((WebRtc_Word8*)_payloadData, payloadDataSize, rtpInfo); - - _payloadSize = payloadDataSize; - _timeStampDiff = timeStamp - _lastInTimestamp; - _lastInTimestamp = timeStamp; - _totalBytes += payloadDataSize; - return status; -} - -WebRtc_UWord16 -TestPack::GetPayloadSize() -{ - return _payloadSize; -} - - -WebRtc_UWord32 -TestPack::GetTimeStampDiff() -{ - return _timeStampDiff; -} - -void -TestPack::ResetPayloadSize() -{ - _payloadSize = 0; -} - -TestAllCodecs::TestAllCodecs(int testMode): -_acmA(NULL), -_acmB(NULL), -_channelA2B(NULL), -_testCntr(0), -_packSizeSamp(0), -_packSizeBytes(0), -_counter(0) -{ - // testMode = 0 for silent test (auto test) - _testMode = testMode; -} - -using namespace std; -TestAllCodecs::~TestAllCodecs() -{ - if(_acmA != NULL) - { - AudioCodingModule::Destroy(_acmA); - _acmA = NULL; - } - if(_acmB != NULL) - { - AudioCodingModule::Destroy(_acmB); - _acmB = NULL; - } - if(_channelA2B != NULL) - { - delete _channelA2B; - _channelA2B = NULL; - } -} - -void TestAllCodecs::Perform() -{ - - char file[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _inFileA.Open(file, 32000, "rb"); - - if(_testMode == 0) - { - printf("Running All Codecs Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, - "---------- TestAllCodecs ----------"); - } - - _acmA = AudioCodingModule::Create(0); - _acmB = AudioCodingModule::Create(1); - - _acmA->InitializeReceiver(); - _acmB->InitializeReceiver(); - - WebRtc_UWord8 numEncoders = _acmA->NumberOfCodecs(); - CodecInst myCodecParam; - - for(WebRtc_UWord8 n = 0; n < numEncoders; n++) - { - _acmB->Codec(n, myCodecParam); - _acmB->RegisterReceiveCodec(myCodecParam); - } - - // Create and connect the channel - _channelA2B = new TestPack; - _acmA->RegisterTransportCallback(_channelA2B); - _channelA2B->RegisterReceiverACM(_acmB); - - // All codecs are tested for all allowed sampling frequencies, rates and packet sizes -#ifdef WEBRTC_CODEC_GSMAMR - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecAMR[] = "AMR"; - RegisterSendCodec('A', codecAMR, 8000, 4750, 160, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 4750, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 4750, 480, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5150, 160, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5150, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5150, 480, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5900, 160, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5900, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 5900, 480, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 6700, 160, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 6700, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 6700, 480, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7400, 160, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7400, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7400, 480, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7950, 160, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7950, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 7950, 480, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 10200, 160, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 10200, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 10200, 480, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 12200, 160, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 12200, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMR, 8000, 12200, 480, 3); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - char codecAMRWB[] = "AMR-WB"; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecAMRWB, 16000, 7000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 7000, 640, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 7000, 960, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 9000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 9000, 640, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 9000, 960, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 12000, 320, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 12000, 640, 6); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 12000, 960, 8); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 14000, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 14000, 640, 4); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 14000, 960, 5); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 16000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 16000, 640, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 16000, 960, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 18000, 320, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 18000, 640, 4); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 18000, 960, 5); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 20000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 20000, 640, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 20000, 960, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 23000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 23000, 640, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 23000, 960, 3); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 24000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 24000, 640, 2); - Run(_channelA2B); - RegisterSendCodec('A', codecAMRWB, 16000, 24000, 960, 2); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_G722 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecG722[] = "G722"; - RegisterSendCodec('A', codecG722, 16000, 64000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 480, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 640, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 800, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 960, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_G722_1 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecG7221_1[] = "G7221"; - RegisterSendCodec('A', codecG7221_1, 16000, 32000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG7221_1, 16000, 24000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG7221_1, 16000, 16000, 320, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_G722_1C - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecG7221_2[] = "G7221"; - RegisterSendCodec('A', codecG7221_2, 32000, 48000, 640, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG7221_2, 32000, 32000, 640, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG7221_2, 32000, 24000, 640, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_G729 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecG729[] = "G729"; - RegisterSendCodec('A', codecG729, 8000, 8000, 80, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG729, 8000, 8000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG729, 8000, 8000, 240, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG729, 8000, 8000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG729, 8000, 8000, 400, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecG729, 8000, 8000, 480, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_G729_1 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecG7291[] = "G7291"; - RegisterSendCodec('A', codecG7291, 16000, 8000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 8000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 8000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 12000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 12000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 12000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 14000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 14000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 14000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 16000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 16000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 16000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 18000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 18000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 18000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 20000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 20000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 20000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 22000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 22000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 22000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 24000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 24000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 24000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 26000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 26000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 26000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 28000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 28000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 28000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 30000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 30000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 30000, 960, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 32000, 320, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 32000, 640, 1); - Run(_channelA2B); - RegisterSendCodec('A', codecG7291, 16000, 32000, 960, 1); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_GSMFR - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecGSM[] = "GSM"; - RegisterSendCodec('A', codecGSM, 8000, 13200, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecGSM, 8000, 13200, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecGSM, 8000, 13200, 480, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_ILBC - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecILBC[] = "ILBC"; - RegisterSendCodec('A', codecILBC, 8000, 13300, 240, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecILBC, 8000, 13300, 480, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecILBC, 8000, 15200, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecILBC, 8000, 15200, 320, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif -#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecISAC[] = "ISAC"; - RegisterSendCodec('A', codecISAC, 16000, -1, 480, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 16000, -1, 960, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 16000, 15000, 480, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 16000, 32000, 960, -1); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_ISAC - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecISAC, 32000, -1, 960, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 32000, 56000, 960, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 32000, 37000, 960, -1); - Run(_channelA2B); - RegisterSendCodec('A', codecISAC, 32000, 32000, 960, -1); - Run(_channelA2B); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_PCM16 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecL16[] = "L16"; - RegisterSendCodec('A', codecL16, 8000, 128000, 80, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 240, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 320, 0); - Run(_channelA2B); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecL16, 16000, 256000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 480, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 640, 0); - Run(_channelA2B); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecL16, 32000, 512000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 32000, 512000, 640, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecPCMA[] = "PCMA"; - RegisterSendCodec('A', codecPCMA, 8000, 64000, 80, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 240, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 400, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 480, 0); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - Run(_channelA2B); - char codecPCMU[] = "PCMU"; - RegisterSendCodec('A', codecPCMU, 8000, 64000, 80, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 240, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 400, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 480, 0); - Run(_channelA2B); - _outFileB.Close(); -#ifdef WEBRTC_CODEC_SPEEX - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - char codecSPEEX[] = "SPEEX"; - RegisterSendCodec('A', codecSPEEX, 8000, 2400, 160, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecSPEEX, 8000, 8000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecSPEEX, 8000, 18200, 480, 0); - Run(_channelA2B); - _outFileB.Close(); - - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecSPEEX, 16000, 4000, 320, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecSPEEX, 16000, 12800, 640, 0); - Run(_channelA2B); - RegisterSendCodec('A', codecSPEEX, 16000, 34200, 960, 0); - Run(_channelA2B); - _outFileB.Close(); -#endif - - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("Done!\n"); - } - - /* Print out all codecs that were not tested in the run */ - - - if(_testMode != 0) { - printf("The following codecs was not included in the test:\n"); -#ifndef WEBRTC_CODEC_GSMAMR - printf(" GSMAMR\n"); -#endif -#ifndef WEBRTC_CODEC_GSMAMRWB - printf(" GSMAMR-wb\n"); -#endif -#ifndef WEBRTC_CODEC_G722 - printf(" G.722\n"); -#endif -#ifndef WEBRTC_CODEC_G722_1 - printf(" G.722.1\n"); -#endif -#ifndef WEBRTC_CODEC_G722_1C - printf(" G.722.1C\n"); -#endif -#ifndef WEBRTC_CODEC_G729 - printf(" G.729\n"); -#endif -#ifndef WEBRTC_CODEC_G729_1 - printf(" G.729.1\n"); -#endif -#ifndef WEBRTC_CODEC_GSMFR - printf(" GSMFR\n"); -#endif -#ifndef WEBRTC_CODEC_ILBC - printf(" iLBC\n"); -#endif -#ifndef WEBRTC_CODEC_ISAC - printf(" ISAC float\n"); -#endif -#ifndef WEBRTC_CODEC_ISACFX - printf(" ISAC fix\n"); -#endif -#ifndef WEBRTC_CODEC_PCM16 - printf(" PCM16\n"); -#endif -#ifndef WEBRTC_CODEC_SPEEX - printf(" Speex\n"); -#endif - - printf("\nTo complete the test, listen to the %d number of output files.\n", _testCntr); - } -} - -// Register Codec to use in the test -// -// Input: side - which ACM to use, 'A' or 'B' -// codecName - name to use when register the codec -// samplingFreqHz - sampling frequency in Herz -// rate - bitrate in bytes -// packSize - packet size in samples -// extraByte - if extra bytes needed compared to the bitrate -// used when registering, can be an internal header -// set to -1 if the codec is a variable rate codec -WebRtc_Word16 TestAllCodecs::RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 samplingFreqHz, - int rate, - int packSize, - int extraByte) -{ - if(_testMode != 0) { - // Print out codec and settings - printf("codec: %s Freq: %d Rate: %d PackSize: %d", codecName, samplingFreqHz, rate, packSize); - } - - // Store packetsize in samples, used to validate the recieved packet - _packSizeSamp = packSize; - - // Store the expected packet size in bytes, used to validate the recieved packet - // If variable rate codec (extraByte == -1), set to -1 (65535) - if (extraByte != -1) - { - // Add 0.875 to always round up to a whole byte - _packSizeBytes = (WebRtc_UWord16)((float)(packSize*rate)/(float)(samplingFreqHz*8)+0.875)+extraByte; - } - else - { - // Packets will have a variable size - _packSizeBytes = -1; - } - - // Set pointer to the ACM where to register the codec - AudioCodingModule* myACM; - switch(side) - { - case 'A': - { - myACM = _acmA; - break; - } - case 'B': - { - myACM = _acmB; - break; - } - default: - return -1; - } - - if(myACM == NULL) - { - assert(false); - return -1; - } - CodecInst myCodecParam; - - // Get all codec paramters before registering - CHECK_ERROR(AudioCodingModule::Codec(codecName, myCodecParam, samplingFreqHz)); - myCodecParam.rate = rate; - myCodecParam.pacsize = packSize; - CHECK_ERROR(myACM->RegisterSendCodec(myCodecParam)); - - // initialization was succesful - return 0; -} - -void TestAllCodecs::Run(TestPack* channel) -{ - AudioFrame audioFrame; - - WebRtc_UWord16 SamplesIn10MsecA = _inFileA.PayloadLength10Ms(); - WebRtc_UWord32 timestampA = 1; - WebRtc_Word32 outFreqHzB = _outFileB.SamplingFrequency(); - WebRtc_UWord16 recSize; - WebRtc_UWord32 timeStampDiff; - channel->ResetPayloadSize(); - int errorCount = 0; - - // Only run 1 second for each test case - while((_counter<1000)&& (!_inFileA.EndOfFile())) - { - // Add 10 msec to ACM - _inFileA.Read10MsData(audioFrame); - CHECK_ERROR(_acmA->Add10MsData(audioFrame)); - - // Run sender side of ACM - CHECK_ERROR(_acmA->Process()); - - // Verify that the received packet size matches the settings - recSize = channel->GetPayloadSize(); - if (recSize) { - if ((recSize != _packSizeBytes) && (_packSizeBytes < 65535)) { - errorCount++; - } - - // Verify that the timestamp is updated with expected length - timeStampDiff = channel->GetTimeStampDiff(); - if ((_counter > 10) && (timeStampDiff != _packSizeSamp)) - errorCount++; - } - - - // Run received side of ACM - CHECK_ERROR(_acmB->PlayoutData10Ms(outFreqHzB, audioFrame)); - - // Write output speech to file - _outFileB.Write10MsData(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples); - } - - if (errorCount) - { - printf(" - test FAILED\n"); - } - else if(_testMode != 0) - { - printf(" - test PASSED\n"); - } - - // Reset _counter - if (_counter == 1000) { - _counter = 0; - } - if (_inFileA.EndOfFile()) { - _inFileA.Rewind(); - } -} - -void TestAllCodecs::OpenOutFile(WebRtc_Word16 testNumber) -{ - char fileName[500] = "testallcodecs_out_"; - char cntrStr[10]; - - sprintf(cntrStr, "%02d.pcm", testNumber); - strcat(fileName, cntrStr); - _outFileB.Open(fileName, 32000, "wb"); -} - -void TestAllCodecs::DisplaySendReceiveCodec() -{ - CodecInst myCodecParam; - _acmA->SendCodec(myCodecParam); - printf("%s -> ", myCodecParam.plname); - _acmB->ReceiveCodec(myCodecParam); - printf("%s\n", myCodecParam.plname); -} - diff --git a/modules/audio_coding/main/test/TestAllCodecs.h b/modules/audio_coding/main/test/TestAllCodecs.h deleted file mode 100644 index 958cefd71..000000000 --- a/modules/audio_coding/main/test/TestAllCodecs.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TEST_ALL_CODECS_H -#define TEST_ALL_CODECS_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" - -class TestPack : public AudioPacketizationCallback -{ -public: - TestPack(); - ~TestPack(); - - void RegisterReceiverACM(AudioCodingModule* acm); - - virtual WebRtc_Word32 SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - - WebRtc_UWord16 GetPayloadSize(); - WebRtc_UWord32 GetTimeStampDiff(); - void ResetPayloadSize(); - -private: - AudioCodingModule* _receiverACM; - WebRtc_Word16 _seqNo; - WebRtc_UWord8 _payloadData[60 * 32 * 2 * 2]; - WebRtc_UWord32 _timeStampDiff; - WebRtc_UWord32 _lastInTimestamp; - WebRtc_UWord64 _totalBytes; - WebRtc_UWord16 _payloadSize; -}; - -class TestAllCodecs : public ACMTest -{ -public: - TestAllCodecs(int testMode); - ~TestAllCodecs(); - - void Perform(); -private: - // The default value of '-1' indicates that the registration is based only on codec name - // and a sampling frequncy matching is not required. This is useful for codecs which support - // several sampling frequency. - WebRtc_Word16 RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 sampFreqHz, - int rate, - int packSize, - int extraByte); - - void Run(TestPack* channel); - void OpenOutFile(WebRtc_Word16 testNumber); - void DisplaySendReceiveCodec(); - - WebRtc_Word32 SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - - int _testMode; - - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - TestPack* _channelA2B; - - PCMFile _inFileA; - PCMFile _outFileB; - WebRtc_Word16 _testCntr; - WebRtc_UWord16 _packSizeSamp; - WebRtc_UWord16 _packSizeBytes; - int _counter; -}; - - -#endif // TEST_ALL_CODECS_H - diff --git a/modules/audio_coding/main/test/TestFEC.cpp b/modules/audio_coding/main/test/TestFEC.cpp deleted file mode 100644 index 829e1de14..000000000 --- a/modules/audio_coding/main/test/TestFEC.cpp +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "TestFEC.h" - -#include "audio_coding_module_typedefs.h" -#include "common_types.h" -#include "engine_configurations.h" - -#include -#include -#include "trace.h" -#include "utility.h" - -TestFEC::TestFEC(int testMode): -_acmA(NULL), -_acmB(NULL), -_channelA2B(NULL), -_testCntr(0) -{ - _testMode = testMode; -} - -using namespace std; - -TestFEC::~TestFEC() -{ - if(_acmA != NULL) - { - AudioCodingModule::Destroy(_acmA); - _acmA = NULL; - } - if(_acmB != NULL) - { - AudioCodingModule::Destroy(_acmB); - _acmB = NULL; - } - if(_channelA2B != NULL) - { - delete _channelA2B; - _channelA2B = NULL; - } -} - -void TestFEC::Perform() -{ - - if(_testMode == 0) - { - printf("Running FEC Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, - "---------- TestFEC ----------"); - } - char fileName[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _inFileA.Open(fileName, 32000, "rb"); - - - bool fecEnabled; - - _acmA = AudioCodingModule::Create(0); - _acmB = AudioCodingModule::Create(1); - - _acmA->InitializeReceiver(); - _acmB->InitializeReceiver(); - - WebRtc_UWord8 numEncoders = _acmA->NumberOfCodecs(); - CodecInst myCodecParam; - if(_testMode != 0) - { - printf("Registering codecs at receiver... \n"); - } - for(WebRtc_UWord8 n = 0; n < numEncoders; n++) - { - _acmB->Codec(n, myCodecParam); - if(_testMode != 0) - { - printf("%s\n", myCodecParam.plname); - } - _acmB->RegisterReceiveCodec(myCodecParam); - } - - // Create and connect the channel - _channelA2B = new Channel; - _acmA->RegisterTransportCallback(_channelA2B); - _channelA2B->RegisterReceiverACM(_acmB); - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } -#ifndef WEBRTC_CODEC_G722 - printf("G722 needs to be activated to run this test\n"); - exit(-1); -#endif - char nameG722[] = "G722"; - RegisterSendCodec('A', nameG722, 16000); - char nameCN[] = "CN"; - RegisterSendCodec('A', nameCN, 16000); - char nameRED[] = "RED"; - RegisterSendCodec('A', nameRED); - OpenOutFile(_testCntr); - SetVAD(true, true, VADAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - char nameISAC[] = "iSAC"; - RegisterSendCodec('A',nameISAC, 16000); - OpenOutFile(_testCntr); - SetVAD(true, true, VADVeryAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - SetVAD(true, true, VADVeryAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - SetVAD(false, false, VADNormal); - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - - RegisterSendCodec('A', nameISAC, 16000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - RegisterSendCodec('A', nameISAC, 32000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - - - - - _channelA2B->SetFECTestWithPacketLoss(true); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - - RegisterSendCodec('A',nameG722); - RegisterSendCodec('A', nameCN, 16000); - OpenOutFile(_testCntr); - SetVAD(true, true, VADAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - RegisterSendCodec('A', nameISAC, 16000); - OpenOutFile(_testCntr); - SetVAD(true, true, VADVeryAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - SetVAD(true, true, VADVeryAggr); - _acmA->SetFECStatus(false); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - OpenOutFile(_testCntr); - Run(); - _outFileB.Close(); - - - - - - - if(_testMode != 0) - { - printf("=======================================================================\n"); - printf("%d ",_testCntr++); - } - else - { - printf("."); - } - RegisterSendCodec('A', nameISAC, 32000); - OpenOutFile(_testCntr); - SetVAD(false, false, VADNormal); - _acmA->SetFECStatus(true); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - - RegisterSendCodec('A', nameISAC, 16000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - RegisterSendCodec('A', nameISAC, 32000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - - RegisterSendCodec('A', nameISAC, 16000); - fecEnabled = _acmA->FECStatus(); - if(_testMode != 0) - { - printf("FEC currently %s\n",(fecEnabled?"ON":"OFF")); - DisplaySendReceiveCodec(); - } - Run(); - _outFileB.Close(); - - - - if(_testMode == 0) - { - printf("Done!\n"); - } -} - -WebRtc_Word32 TestFEC::SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode) -{ - if(_testMode != 0) - { - printf("DTX %s; VAD %s; VAD-Mode %d\n", - enableDTX? "ON":"OFF", - enableVAD? "ON":"OFF", - (WebRtc_Word16)vadMode); - } - return _acmA->SetVAD(enableDTX, enableVAD, vadMode); -} - -WebRtc_Word16 TestFEC::RegisterSendCodec(char side, char* codecName, WebRtc_Word32 samplingFreqHz) -{ - if(_testMode != 0) - { - if(samplingFreqHz > 0) - { - printf("Registering %s-%d for side %c\n", codecName, samplingFreqHz, side); - } - else - { - printf("Registering %s for side %c\n", codecName, side); - } - } - cout << flush; - AudioCodingModule* myACM; - switch(side) - { - case 'A': - { - myACM = _acmA; - break; - } - case 'B': - { - myACM = _acmB; - break; - } - default: - return -1; - } - - if(myACM == NULL) - { - assert(false); - return -1; - } - CodecInst myCodecParam; - - CHECK_ERROR(AudioCodingModule::Codec(codecName, myCodecParam, samplingFreqHz)); - - CHECK_ERROR(myACM->RegisterSendCodec(myCodecParam)); - - // initialization was succesful - return 0; -} - -void TestFEC::Run() -{ - AudioFrame audioFrame; - - WebRtc_UWord16 msecPassed = 0; - WebRtc_UWord32 secPassed = 0; - WebRtc_UWord16 SamplesIn10MsecA = _inFileA.PayloadLength10Ms(); - WebRtc_UWord32 timestampA = 1; - WebRtc_Word32 outFreqHzB = _outFileB.SamplingFrequency(); - - while(!_inFileA.EndOfFile()) - { - _inFileA.Read10MsData(audioFrame); - //audioFrame._timeStamp = timestampA; - //timestampA += SamplesIn10MsecA; - CHECK_ERROR(_acmA->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmA->Process()); - - CHECK_ERROR(_acmB->PlayoutData10Ms(outFreqHzB, audioFrame)); - _outFileB.Write10MsData(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples); - msecPassed += 10; - if(msecPassed >= 1000) - { - msecPassed = 0; - secPassed++; - } - if(((secPassed%5) == 4) && (msecPassed == 0) && (_testCntr > 14)) - { - printf("%3u:%3u ", secPassed, msecPassed); - _acmA->SetFECStatus(false); - printf("FEC currently %s\n",(_acmA->FECStatus()?"ON":"OFF")); - } - if(((secPassed%5) == 4) && (msecPassed >= 990) && (_testCntr > 14)) - { - printf("%3u:%3u ", secPassed, msecPassed); - _acmA->SetFECStatus(true); - printf("FEC currently %s\n",(_acmA->FECStatus()?"ON":"OFF")); - } - } - _inFileA.Rewind(); -} - -void TestFEC::OpenOutFile(WebRtc_Word16 testNumber) -{ - char fileName[500] = "./modules/audio_coding/main/test/res_tests/TestFEC_outFile_"; - char cntrStr[10]; - - if(_testMode == 0) - { - sprintf(fileName, "./modules/audio_coding/main/test/res_autotests/TestFEC_outFile_"); - } - sprintf(cntrStr, "%02d.pcm", testNumber); - strcat(fileName, cntrStr); - _outFileB.Open(fileName, 32000, "wb"); -} - -void TestFEC::DisplaySendReceiveCodec() -{ - CodecInst myCodecParam; - _acmA->SendCodec(myCodecParam); - printf("%s -> ", myCodecParam.plname); - _acmB->ReceiveCodec(myCodecParam); - printf("%s\n", myCodecParam.plname); -} diff --git a/modules/audio_coding/main/test/TestFEC.h b/modules/audio_coding/main/test/TestFEC.h deleted file mode 100644 index 09d100961..000000000 --- a/modules/audio_coding/main/test/TestFEC.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TEST_FEC_H -#define TEST_FEC_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" - -class TestFEC : public ACMTest -{ -public: - TestFEC(int testMode); - ~TestFEC(); - - void Perform(); -private: - // The default value of '-1' indicates that the registration is based only on codec name - // and a sampling frequncy matching is not required. This is useful for codecs which support - // several sampling frequency. - WebRtc_Word16 RegisterSendCodec(char side, char* codecName, WebRtc_Word32 sampFreqHz = -1); - void Run(); - void OpenOutFile(WebRtc_Word16 testNumber); - void DisplaySendReceiveCodec(); - WebRtc_Word32 SetVAD(bool enableDTX, bool enableVAD, ACMVADMode vadMode); - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - Channel* _channelA2B; - - PCMFile _inFileA; - PCMFile _outFileB; - WebRtc_Word16 _testCntr; - int _testMode; -}; - - -#endif - diff --git a/modules/audio_coding/main/test/TestStereo.cpp b/modules/audio_coding/main/test/TestStereo.cpp deleted file mode 100644 index bb4c40bb4..000000000 --- a/modules/audio_coding/main/test/TestStereo.cpp +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "TestStereo.h" - -#include "common_types.h" -#include "audio_coding_module_typedefs.h" -#include "engine_configurations.h" -#include -#include "utility.h" -#include -#include "trace.h" - - -// Class for simulating packet handling -TestPackStereo::TestPackStereo(): -_receiverACM(NULL), -_seqNo(0), -_timeStampDiff(0), -_lastInTimestamp(0), -_totalBytes(0), -_payloadSize(0), -_noChannels(1), -_codecType(0) -{ -} -TestPackStereo::~TestPackStereo() -{ -} - -void -TestPackStereo::RegisterReceiverACM(AudioCodingModule* acm) -{ - _receiverACM = acm; - return; -} - - -WebRtc_Word32 -TestPackStereo::SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation) -{ - WebRtcRTPHeader rtpInfo; - WebRtc_Word32 status; - WebRtc_UWord16 payloadDataSize = payloadSize; - WebRtc_UWord8 payloadDataMaster[60 * 32 * 2 * 2]; - WebRtc_UWord8 payloadDataSlave[60 * 32 * 2 * 2]; - bool twoBytePerSample = false; - bool oneBytePerSample = true; - bool frameBased = false; - - rtpInfo.header.markerBit = false; - rtpInfo.header.ssrc = 0; - rtpInfo.header.sequenceNumber = _seqNo++; - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.timestamp = timeStamp; - if(frameType == kFrameEmpty) - { - // Skip this frame - return 0; - } - if(frameType != kAudioFrameCN) - { - rtpInfo.type.Audio.isCNG = false; - - // For stereo we need to call ACM with two incoming packets, one for each channel. - // Different packet-splitting depending on codec. - if (_codecType == 0) { - // one byte per sample - for (int i=0, j=0; i> 4); - payloadDataSlave[j] = ((payloadData[i] & 0x0F) << 4) + (payloadData[i+1] & 0x0F); - } - } - } - else - { - // If CNG packet, send the same packet to both master and slave. - rtpInfo.type.Audio.isCNG = true; - memcpy(payloadDataMaster, payloadData, payloadSize); - memcpy(payloadDataSlave, payloadData, payloadSize); - payloadDataSize = payloadSize*2; - } - - // Call ACM with two packets, one for each channel - rtpInfo.type.Audio.channel = 1; - status = _receiverACM->IncomingPacket((WebRtc_Word8*)payloadDataMaster, payloadDataSize/2, rtpInfo); - rtpInfo.type.Audio.channel = 2; - status = _receiverACM->IncomingPacket((WebRtc_Word8*)payloadDataSlave, payloadDataSize/2, rtpInfo); - - if (frameType != kAudioFrameCN) { - _payloadSize = payloadDataSize; - } else { - _payloadSize = -1; - } - _timeStampDiff = timeStamp - _lastInTimestamp; - _lastInTimestamp = timeStamp; - _totalBytes += payloadDataSize; - return status; -} - -WebRtc_UWord16 -TestPackStereo::GetPayloadSize() -{ - return _payloadSize; -} - - -WebRtc_UWord32 -TestPackStereo::GetTimeStampDiff() -{ - return _timeStampDiff; -} - -void -TestPackStereo::ResetPayloadSize() -{ - _payloadSize = 0; -} - -void -TestPackStereo::SetCodecType(int codecType) -{ - _codecType = codecType; -} - -TestStereo::TestStereo(int testMode): -_acmA(NULL), -_acmB(NULL), -_channelA2B(NULL), -_testCntr(0), -_packSizeSamp(0), -_packSizeBytes(0), -_counter(0) -{ - // testMode = 0 for silent test (auto test) - _testMode = testMode; -} - -using namespace std; -TestStereo::~TestStereo() -{ - if(_acmA != NULL) - { - AudioCodingModule::Destroy(_acmA); - _acmA = NULL; - } - if(_acmB != NULL) - { - AudioCodingModule::Destroy(_acmB); - _acmB = NULL; - } - if(_channelA2B != NULL) - { - delete _channelA2B; - _channelA2B = NULL; - } -} - -void TestStereo::Perform() -{ - char fileName[500]; - WebRtc_UWord16 frequencyHz; - - if(_testMode == 0) - { - printf("Running Stereo Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, - "---------- TestStereo ----------"); - } - - strcpy(fileName, "./modules/audio_coding/main/test/teststereo32kHz.pcm"); - frequencyHz = 32000; - - _inFileA.Open(fileName, frequencyHz, "rb"); - _inFileA.ReadStereo(true); - - _acmA = AudioCodingModule::Create(0); - _acmB = AudioCodingModule::Create(1); - - _acmA->InitializeReceiver(); - _acmB->InitializeReceiver(); - - WebRtc_UWord8 numEncoders = _acmA->NumberOfCodecs(); - CodecInst myCodecParam; - - for(WebRtc_UWord8 n = 0; n < numEncoders; n++) - { - _acmB->Codec(n, myCodecParam); - if(!strcmp(myCodecParam.plname, "L16") || - !strcmp(myCodecParam.plname, "PCMA")|| - !strcmp(myCodecParam.plname, "PCMU")|| - !strcmp(myCodecParam.plname, "G722")) - { - myCodecParam.channels=2; - _acmB->RegisterReceiveCodec(myCodecParam); - } - } - - // Create and connect the channel - _channelA2B = new TestPackStereo; - _acmA->RegisterTransportCallback(_channelA2B); - _channelA2B->RegisterReceiverACM(_acmB); - - // All codecs are tested for all allowed sampling frequencies, rates and packet sizes -#ifdef WEBRTC_CODEC_G722 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _channelA2B->SetCodecType(3); - _testCntr++; - OpenOutFile(_testCntr); - char codecG722[] = "G722"; - RegisterSendCodec('A', codecG722, 16000, 64000, 160); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 320); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 480); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 640); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 800); - Run(_channelA2B); - RegisterSendCodec('A', codecG722, 16000, 64000, 960); - Run(_channelA2B); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecG722, 16000, 64000, 320); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_PCM16 - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _channelA2B->SetCodecType(1); - _testCntr++; - OpenOutFile(_testCntr); - char codecL16[] = "L16"; - RegisterSendCodec('A', codecL16, 8000, 128000, 80); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 160); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 240); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 8000, 128000, 320); - Run(_channelA2B); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecL16, 8000, 128000, 80); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecL16, 16000, 256000, 160); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 320); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 480); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 16000, 256000, 640); - Run(_channelA2B); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecL16, 16000, 256000, 160); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - RegisterSendCodec('A', codecL16, 32000, 512000, 320); - Run(_channelA2B); - RegisterSendCodec('A', codecL16, 32000, 512000, 640); - Run(_channelA2B); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecL16, 32000, 512000, 320); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - _outFileB.Close(); -#endif -#define PCMA_AND_PCMU -#ifdef PCMA_AND_PCMU - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _channelA2B->SetCodecType(0); - _testCntr++; - OpenOutFile(_testCntr); - char codecPCMA[] = "PCMA"; - RegisterSendCodec('A', codecPCMA, 8000, 64000, 80); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 160); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 240); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 320); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 400); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 480); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecPCMA, 8000, 64000, 80); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } - _testCntr++; - OpenOutFile(_testCntr); - Run(_channelA2B); - char codecPCMU[] = "PCMU"; - RegisterSendCodec('A', codecPCMU, 8000, 64000, 80); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 160); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 240); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 320); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 400); - Run(_channelA2B); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 480); - _acmA->SetVAD(true, true, VADNormal); - RegisterSendCodec('A', codecPCMU, 8000, 64000, 80); - Run(_channelA2B); - _acmA->SetVAD(false, false, VADNormal); - Run(_channelA2B); - _outFileB.Close(); - if(_testMode != 0) { - printf("=======================================================================\n"); - } else { - printf("."); - } -#endif - - /* Print out which codecs were tested, and which were not, in the run */ - - if(_testMode != 0) { - printf("The following codecs was INCLUDED in the test:\n"); -#ifdef WEBRTC_CODEC_G722 - printf(" G.722\n"); -#endif -#ifdef WEBRTC_CODEC_PCM16 - printf(" PCM16\n"); -#endif - printf(" G.711\n"); - - printf("\nTo complete the test, listen to the %d number of output files.\n", _testCntr); - } else { - printf("Done!\n"); - } -} - -// Register Codec to use in the test -// -// Input: side - which ACM to use, 'A' or 'B' -// codecName - name to use when register the codec -// samplingFreqHz - sampling frequency in Herz -// rate - bitrate in bytes -// packSize - packet size in samples -// extraByte - if extra bytes needed compared to the bitrate -// used when registering, can be an internal header -// set to -1 if the codec is a variable rate codec -WebRtc_Word16 TestStereo::RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 samplingFreqHz, - int rate, - int packSize) -{ - if(_testMode != 0) { - // Print out codec and settings - printf("codec: %s Freq: %d Rate: %d PackSize: %d", codecName, samplingFreqHz, rate, packSize); - } - - // Store packetsize in samples, used to validate the recieved packet - _packSizeSamp = packSize; - - // Store the expected packet size in bytes, used to validate the recieved packet - // Add 0.875 to always round up to a whole byte - _packSizeBytes = (WebRtc_UWord16)((float)(packSize*rate)/(float)(samplingFreqHz*8)+0.875); - - // Set pointer to the ACM where to register the codec - AudioCodingModule* myACM; - switch(side) - { - case 'A': - { - myACM = _acmA; - break; - } - case 'B': - { - myACM = _acmB; - break; - } - default: - return -1; - } - - if(myACM == NULL) - { - assert(false); - return -1; - } - CodecInst myCodecParam; - - // Get all codec paramters before registering - CHECK_ERROR(AudioCodingModule::Codec(codecName, myCodecParam, samplingFreqHz)); - myCodecParam.rate = rate; - myCodecParam.pacsize = packSize; - myCodecParam.channels = 2; - CHECK_ERROR(myACM->RegisterSendCodec(myCodecParam)); - - // initialization was succesful - return 0; -} - -void TestStereo::Run(TestPackStereo* channel) -{ - AudioFrame audioFrame; - - WebRtc_UWord16 SamplesIn10MsecA = _inFileA.PayloadLength10Ms(); - WebRtc_UWord32 timestampA = 1; - WebRtc_Word32 outFreqHzB = _outFileB.SamplingFrequency(); - WebRtc_UWord16 recSize; - WebRtc_UWord32 timeStampDiff; - channel->ResetPayloadSize(); - int errorCount = 0; - - // Only run 1 second for each test case - while((_counter<1000)&& (!_inFileA.EndOfFile())) - { - // Add 10 msec to ACM - _inFileA.Read10MsData(audioFrame); - CHECK_ERROR(_acmA->Add10MsData(audioFrame)); - - // Run sender side of ACM - CHECK_ERROR(_acmA->Process()); - - // Verify that the received packet size matches the settings - recSize = channel->GetPayloadSize(); - if ((0GetTimeStampDiff(); - if ((_counter > 10) && (timeStampDiff != _packSizeSamp)) { - errorCount++; - } - } - - // Run received side of ACM - CHECK_ERROR(_acmB->PlayoutData10Ms(outFreqHzB, audioFrame)); - - // Write output speech to file - _outFileB.Write10MsData(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples*audioFrame._audioChannel); - } - - if (errorCount) - { - printf(" - test FAILED\n"); - } - else if(_testMode != 0) - { - printf(" - test PASSED\n"); - } - - // Reset _counter - if (_counter == 1000) { - _counter = 0; - } - if (_inFileA.EndOfFile()) { - _inFileA.Rewind(); - } -} - -void TestStereo::OpenOutFile(WebRtc_Word16 testNumber) -{ - char fileName[500] = "./modules/audio_coding/main/test/res_tests/teststereo_out_"; - char cntrStr[10]; - - sprintf(cntrStr, "%02d.pcm", testNumber); - strcat(fileName, cntrStr); - - _outFileB.Open(fileName, 32000, "wb"); -} - -void TestStereo::DisplaySendReceiveCodec() -{ - CodecInst myCodecParam; - _acmA->SendCodec(myCodecParam); - if(_testMode != 0) { - printf("%s -> ", myCodecParam.plname); - } - _acmB->ReceiveCodec(myCodecParam); - if(_testMode != 0) { - printf("%s\n", myCodecParam.plname); - } -} - diff --git a/modules/audio_coding/main/test/TestStereo.h b/modules/audio_coding/main/test/TestStereo.h deleted file mode 100644 index 07c32de8f..000000000 --- a/modules/audio_coding/main/test/TestStereo.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TEST_STEREO_H -#define TEST_STEREO_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" - -class TestPackStereo : public AudioPacketizationCallback -{ -public: - TestPackStereo(); - ~TestPackStereo(); - - void RegisterReceiverACM(AudioCodingModule* acm); - - virtual WebRtc_Word32 SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - - WebRtc_UWord16 GetPayloadSize(); - WebRtc_UWord32 GetTimeStampDiff(); - void ResetPayloadSize(); - void SetCodecType(int codecType); - - -private: - AudioCodingModule* _receiverACM; - WebRtc_Word16 _seqNo; - WebRtc_UWord8 _payloadData[60 * 32 * 2 * 2]; - WebRtc_UWord32 _timeStampDiff; - WebRtc_UWord32 _lastInTimestamp; - WebRtc_UWord64 _totalBytes; - WebRtc_UWord16 _payloadSize; - WebRtc_UWord16 _noChannels; - int _codecType; -}; - -class TestStereo : public ACMTest -{ -public: - TestStereo(int testMode); - ~TestStereo(); - - void Perform(); -private: - // The default value of '-1' indicates that the registration is based only on codec name - // and a sampling frequncy matching is not required. This is useful for codecs which support - // several sampling frequency. - WebRtc_Word16 RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 sampFreqHz, - int rate, - int packSize); - - void Run(TestPackStereo* channel); - void OpenOutFile(WebRtc_Word16 testNumber); - void DisplaySendReceiveCodec(); - - WebRtc_Word32 SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - - int _testMode; - - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - TestPackStereo* _channelA2B; - - PCMFile _inFileA; - PCMFile _outFileB; - PCMFile _inFileStereo; - WebRtc_Word16 _testCntr; - WebRtc_UWord16 _packSizeSamp; - WebRtc_UWord16 _packSizeBytes; - int _counter; - int _codecType; -}; - - -#endif - diff --git a/modules/audio_coding/main/test/TestVADDTX.cpp b/modules/audio_coding/main/test/TestVADDTX.cpp deleted file mode 100644 index 8186f620b..000000000 --- a/modules/audio_coding/main/test/TestVADDTX.cpp +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "TestVADDTX.h" - -#include "common_types.h" -#include "audio_coding_module_typedefs.h" -#include "utility.h" -#include "engine_configurations.h" -#include -#include "trace.h" - - -TestVADDTX::TestVADDTX(int testMode): -_acmA(NULL), -_acmB(NULL), -_channelA2B(NULL), -_testResults(0) -{ - //testMode == 1 for more extensive testing - //testMode == 0 for quick test (autotest) - _testMode = testMode; -} - -using namespace std; -TestVADDTX::~TestVADDTX() -{ - if(_acmA != NULL) - { - AudioCodingModule::Destroy(_acmA); - _acmA = NULL; - } - if(_acmB != NULL) - { - AudioCodingModule::Destroy(_acmB); - _acmB = NULL; - } - if(_channelA2B != NULL) - { - delete _channelA2B; - _channelA2B = NULL; - } -} - -void TestVADDTX::Perform() -{ - - if(_testMode == 0) - { - printf("Running VAD/DTX Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, - "---------- TestVADDTX ----------"); - } - char fileName[] = "./modules/audio_coding/main/test/testfile32kHz.pcm"; - _inFileA.Open(fileName, 32000, "rb"); - - _acmA = AudioCodingModule::Create(0); - _acmB = AudioCodingModule::Create(1); - - _acmA->InitializeReceiver(); - _acmB->InitializeReceiver(); - - WebRtc_UWord8 numEncoders = _acmA->NumberOfCodecs(); - CodecInst myCodecParam; - if(_testMode != 0) - { - printf("Registering codecs at receiver... \n"); - } - for(WebRtc_UWord8 n = 0; n < numEncoders; n++) - { - _acmB->Codec(n, myCodecParam); - if(_testMode != 0) - { - printf("%s\n", myCodecParam.plname); - } - _acmB->RegisterReceiveCodec(myCodecParam); - } - - // Create and connect the channel - _channelA2B = new Channel; - _acmA->RegisterTransportCallback(_channelA2B); - _channelA2B->RegisterReceiverACM(_acmB); - - _acmA->RegisterVADCallback(&_monitor); - - - WebRtc_Word16 testCntr = 1; - VADDTXstruct setDTX, getDTX, expectedDTX; - bool dtxReplaced; - WebRtc_Word16 testResults = 0; - -#ifdef WEBRTC_CODEC_ISAC - // Open outputfile - OpenOutFile(testCntr++); - - // Register iSAC WB as send codec - char nameISAC[] = "ISAC"; - RegisterSendCodec('A', nameISAC, 16000); - - // Run the five test cased - runTestCases(); - - // Close file - _outFileB.Close(); - - // Open outputfile - OpenOutFile(testCntr++); - - // Register iSAC SWB as send codec - RegisterSendCodec('A', nameISAC, 32000); - - // Run the five test cased - runTestCases(); - - // Close file - _outFileB.Close(); -#endif -#ifdef WEBRTC_CODEC_ILBC - // Open outputfile - OpenOutFile(testCntr++); - - // Register iLBC as send codec - char nameILBC[] = "ilbc"; - RegisterSendCodec('A', nameILBC); - - // Run the five test cased - runTestCases(); - - // Close file - _outFileB.Close(); - -#endif - if(_testMode) { - printf("Done!\n"); - } - - printf("VAD/DTX test completed with %d subtests failed\n", testResults); - if (testResults > 0) - { - printf("Press return\n\n", testResults); - getchar(); - } -} - -void TestVADDTX::runTestCases() -{ - if(_testMode != 0) - { - CodecInst myCodecParam; - _acmA->SendCodec(myCodecParam); - printf("%s\n", myCodecParam.plname); - } - else - { - printf("."); - } - // #1 DTX = OFF, VAD = ON, VADNormal - if(_testMode != 0) - printf("Test #1 "); - SetVAD(false, true, VADNormal); - Run(); - _testResults += VerifyTest(); - - // #2 DTX = OFF, VAD = ON, VADAggr - if(_testMode != 0) - printf("Test #2 "); - SetVAD(false, true, VADAggr); - Run(); - _testResults += VerifyTest(); - - // #3 DTX = ON, VAD = ON, VADLowBitrate - if(_testMode != 0) - printf("Test #3 "); - SetVAD(true, true, VADLowBitrate); - Run(); - _testResults += VerifyTest(); - - // #4 DTX = ON, VAD = ON, VADVeryAggr - if(_testMode != 0) - printf("Test #4 "); - SetVAD(true, true, VADVeryAggr); - Run(); - _testResults += VerifyTest(); - - // #5 DTX = ON, VAD = OFF, VADNormal - if(_testMode != 0) - printf("Test #5 "); - SetVAD(true, false, VADNormal); - Run(); - _testResults += VerifyTest(); - -} -void TestVADDTX::runTestInternalDTX() -{ - // #6 DTX = ON, VAD = ON, VADNormal - if(_testMode != 0) - printf("Test #6 "); - - SetVAD(true, true, VADNormal); - if(_acmA->ReplaceInternalDTXWithWebRtc(true) < 0) { - printf("Was not able to replace DTX since CN was not registered\n"); - } - Run(); - _testResults += VerifyTest(); -} - -void TestVADDTX::SetVAD(bool statusDTX, bool statusVAD, WebRtc_Word16 vadMode) -{ - WebRtc_Word32 status; - bool dtxEnabled, vadEnabled; - ACMVADMode vadModeSet; - - status = _acmA->SetVAD(statusDTX, statusVAD, (ACMVADMode) vadMode); - status = _acmA->VAD(dtxEnabled, vadEnabled, vadModeSet); - - if(_testMode != 0) - { - if(statusDTX != dtxEnabled) - { - printf("DTX: %s not the same as requested: %s\n", - dtxEnabled? "ON":"OFF", dtxEnabled? "OFF":"ON"); - } - if((statusVAD == true) && (vadEnabled == false) || - (statusVAD == false) && (vadEnabled == false) && (statusDTX == true)) - { - printf("VAD: %s not the same as requested: %s\n", - vadEnabled? "ON":"OFF", vadEnabled? "OFF":"ON"); - } - if(vadModeSet != vadMode) - { - printf("VAD mode: %d not the same as requested: %d\n", - (WebRtc_Word16)vadModeSet, (WebRtc_Word16)vadMode); - } - } - - // Requested VAD/DTX settings - _setStruct.statusDTX = statusDTX; - _setStruct.statusVAD = statusVAD; - _setStruct.vadMode = (ACMVADMode) vadMode; - - // VAD settings after setting VAD in ACM - _getStruct.statusDTX = dtxEnabled; - _getStruct.statusVAD = vadEnabled; - _getStruct.vadMode = vadModeSet; - -} - -VADDTXstruct TestVADDTX::GetVAD() -{ - VADDTXstruct retStruct; - WebRtc_Word32 status; - bool dtxEnabled, vadEnabled; - ACMVADMode vadModeSet; - - status = _acmA->VAD(dtxEnabled, vadEnabled, vadModeSet); - - retStruct.statusDTX = dtxEnabled; - retStruct.statusVAD = vadEnabled; - retStruct.vadMode = vadModeSet; - return retStruct; -} - -WebRtc_Word16 TestVADDTX::RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 samplingFreqHz, - WebRtc_Word32 rateKbps) -{ - if(_testMode != 0) - { - printf("Registering %s for side %c\n", codecName, side); - } - cout << flush; - AudioCodingModule* myACM; - switch(side) - { - case 'A': - { - myACM = _acmA; - break; - } - case 'B': - { - myACM = _acmB; - break; - } - default: - return -1; - } - - if(myACM == NULL) - { - return -1; - } - - CodecInst myCodecParam; - for(WebRtc_Word16 codecCntr = 0; codecCntr < myACM->NumberOfCodecs(); - codecCntr++) - { - CHECK_ERROR(myACM->Codec((WebRtc_UWord8)codecCntr, myCodecParam)); - if(!STR_CASE_CMP(myCodecParam.plname, codecName)) - { - if((samplingFreqHz == -1) || (myCodecParam.plfreq == samplingFreqHz)) - { - if((rateKbps == -1) || (myCodecParam.rate == rateKbps)) - { - break; - } - } - } - } - - CHECK_ERROR(myACM->RegisterSendCodec(myCodecParam)); - - // initialization was succesful - return 0; -} - -void TestVADDTX::Run() -{ - AudioFrame audioFrame; - - WebRtc_UWord16 SamplesIn10MsecA = _inFileA.PayloadLength10Ms(); - WebRtc_UWord32 timestampA = 1; - WebRtc_Word32 outFreqHzB = _outFileB.SamplingFrequency(); - - while(!_inFileA.EndOfFile()) - { - _inFileA.Read10MsData(audioFrame); - audioFrame._timeStamp = timestampA; - timestampA += SamplesIn10MsecA; - CHECK_ERROR(_acmA->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmA->Process()); - - CHECK_ERROR(_acmB->PlayoutData10Ms(outFreqHzB, audioFrame)); - _outFileB.Write10MsData(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples); - } -#ifdef PRINT_STAT - _monitor.PrintStatistics(_testMode); -#endif - _inFileA.Rewind(); - _monitor.GetStatistics(_statCounter); - _monitor.ResetStatistics(); -} - -void TestVADDTX::OpenOutFile(WebRtc_Word16 testNumber) -{ - char fileName[500] = "./modules/audio_coding/main/test/res_tests/testVADDTX_outFile_"; - char cntrStr[10]; - - if(_testMode == 0) - { - sprintf(fileName, "./modules/audio_coding/main/test/res_autotests/testVADDTX_outFile_"); - } - sprintf(cntrStr, "%02d.pcm", testNumber); - strcat(fileName, cntrStr); - _outFileB.Open(fileName, 16000, "wb"); -} - - -WebRtc_Word16 TestVADDTX::VerifyTest() -{ - // Verify empty frame result - WebRtc_UWord8 statusEF = 0; - WebRtc_UWord8 vadPattern = 0; - WebRtc_UWord8 emptyFramePattern[6]; - CodecInst myCodecParam; - _acmA->SendCodec(myCodecParam); - bool dtxInUse = true; - bool isReplaced = false; - if ((STR_CASE_CMP(myCodecParam.plname,"G729") == 0) || - (STR_CASE_CMP(myCodecParam.plname,"G723") == 0) || - (STR_CASE_CMP(myCodecParam.plname,"AMR") == 0) || - (STR_CASE_CMP(myCodecParam.plname,"AMR-wb") == 0) || - (STR_CASE_CMP(myCodecParam.plname,"speex") == 0)) - { - _acmA->IsInternalDTXReplacedWithWebRtc(isReplaced); - if (!isReplaced) - { - dtxInUse = false; - } - } - - // Check for error in VAD/DTX settings - if (_getStruct.statusDTX != _setStruct.statusDTX){ - // DTX status doesn't match expected - vadPattern |= 4; - } - if (_getStruct.statusDTX){ - if ((!_getStruct.statusVAD && dtxInUse) || (!dtxInUse && (_getStruct.statusVAD !=_setStruct.statusVAD))) - { - // Missmatch in VAD setting - vadPattern |= 2; - } - } else { - if (_getStruct.statusVAD != _setStruct.statusVAD){ - // VAD status doesn't match expected - vadPattern |= 2; - } - } - if (_getStruct.vadMode != _setStruct.vadMode){ - // VAD Mode doesn't match expected - vadPattern |= 1; - } - - // Set expected empty frame pattern - int ii; - for (ii = 0; ii < 6; ii++) { - emptyFramePattern[ii] = 0; - } - emptyFramePattern[0] = 1; // "kNoEncoding", not important to check. Codecs with packetsize != 80 samples will get this output. - emptyFramePattern[1] = 1; // Expect to always receive some frames labeled "kActiveNormalEncoded" - emptyFramePattern[2] = (((!_getStruct.statusDTX && _getStruct.statusVAD) || (!dtxInUse && _getStruct.statusDTX))); // "kPassiveNormalEncoded" - emptyFramePattern[3] = ((_getStruct.statusDTX && dtxInUse && (_acmA->SendFrequency() == 8000))); // "kPassiveDTXNB" - emptyFramePattern[4] = ((_getStruct.statusDTX && dtxInUse && (_acmA->SendFrequency() == 16000))); // "kPassiveDTXWB" - emptyFramePattern[5] = ((_getStruct.statusDTX && dtxInUse && (_acmA->SendFrequency() == 32000))); // "kPassiveDTXSWB" - - // Check pattern 1-5 (skip 0) - for (int ii = 1; ii < 6; ii++) - { - if (emptyFramePattern[ii]) - { - statusEF |= (_statCounter[ii] == 0); - } - else - { - statusEF |= (_statCounter[ii] > 0); - } - } - if ((statusEF == 0) && (vadPattern == 0)) - { - if(_testMode != 0) - { - printf(" Test OK!\n"); - } - return 0; - } - else - { - if (statusEF) - { - printf("\t\t\tUnexpected empty frame result!\n"); - } - if (vadPattern) - { - printf("\t\t\tUnexpected SetVAD() result!\tDTX: %d\tVAD: %d\tMode: %d\n", (vadPattern >> 2) & 1, (vadPattern >> 1) & 1, vadPattern & 1); - } - return 1; - } -} - -ActivityMonitor::ActivityMonitor() -{ - _counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] = _counter[5] = 0; -} - -ActivityMonitor::~ActivityMonitor() -{ -} - -WebRtc_Word32 ActivityMonitor::InFrameType(WebRtc_Word16 frameType) -{ - _counter[frameType]++; - return 0; -} - -void ActivityMonitor::PrintStatistics(int testMode) -{ - if(testMode != 0) - { - printf("\n"); - printf("kActiveNormalEncoded kPassiveNormalEncoded kPassiveDTXWB kPassiveDTXNB kPassiveDTXSWB kFrameEmpty\n"); - - printf("%19u", _counter[1]); - printf("%22u", _counter[2]); - printf("%14u", _counter[3]); - printf("%14u", _counter[4]); - printf("%14u", _counter[5]); - printf("%11u", _counter[0]); - - printf("\n\n"); - } -} - -void ActivityMonitor::ResetStatistics() -{ - _counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] = _counter[5] = 0; -} - -void ActivityMonitor::GetStatistics(WebRtc_UWord32* getCounter) -{ - for (int ii = 0; ii < 6; ii++) - { - getCounter[ii] = _counter[ii]; - } -} diff --git a/modules/audio_coding/main/test/TestVADDTX.h b/modules/audio_coding/main/test/TestVADDTX.h deleted file mode 100644 index cf9088b7e..000000000 --- a/modules/audio_coding/main/test/TestVADDTX.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TEST_VAD_DTX_H -#define TEST_VAD_DTX_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" - -typedef struct -{ - bool statusDTX; - bool statusVAD; - ACMVADMode vadMode; -} VADDTXstruct; - -class ActivityMonitor : public ACMVADCallback -{ -public: - ActivityMonitor(); - ~ActivityMonitor(); - WebRtc_Word32 InFrameType(WebRtc_Word16 frameType); - void PrintStatistics(int testMode); - void ResetStatistics(); - void GetStatistics(WebRtc_UWord32* getCounter); -private: - // counting according to - /*enum WebRtcACMEncodingType - { - kNoEncoding, - kActiveNormalEncoded, - kPassiveNormalEncoded, - kPassiveDTXNB, - kPassiveDTXWB, - kPassiveDTXSWB - };*/ - WebRtc_UWord32 _counter[6]; -}; - -class TestVADDTX : public ACMTest -{ -public: - TestVADDTX(int testMode); - ~TestVADDTX(); - - void Perform(); -private: - // Registration can be based on codec name only, codec name and sampling frequency, or - // codec name, sampling frequency and rate. - WebRtc_Word16 RegisterSendCodec(char side, - char* codecName, - WebRtc_Word32 samplingFreqHz = -1, - WebRtc_Word32 rateKhz = -1); - void Run(); - void OpenOutFile(WebRtc_Word16 testNumber); - void runTestCases(); - void runTestInternalDTX(); - void SetVAD(bool statusDTX, bool statusVAD, WebRtc_Word16 vadMode); - VADDTXstruct GetVAD(); - WebRtc_Word16 VerifyTest();//VADDTXstruct setDTX, VADDTXstruct getDTX); - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - Channel* _channelA2B; - - PCMFile _inFileA; - PCMFile _outFileB; - - ActivityMonitor _monitor; - WebRtc_UWord32 _statCounter[6]; - - int _testMode; - int _testResults; - VADDTXstruct _setStruct; - VADDTXstruct _getStruct; -}; - - -#endif diff --git a/modules/audio_coding/main/test/Tester.cpp b/modules/audio_coding/main/test/Tester.cpp deleted file mode 100644 index 1049cad53..000000000 --- a/modules/audio_coding/main/test/Tester.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "audio_coding_module.h" -#include "trace.h" - -#include "APITest.h" -#include "EncodeDecodeTest.h" -#include "EncodeToFileTest.h" -#include "iSACTest.h" -#include "SpatialAudio.h" -#include "TestAllCodecs.h" -#include "TestFEC.h" -#include "TestStereo.h" -#include "TestVADDTX.h" -#include "TwoWayCommunication.h" - -// Be sure to create the following directories before running the tests: -// ./modules/audio_coding/main/test/res_tests -// ./modules/audio_coding/main/test/res_autotests - -// Choose what tests to run by defining one or more of the following: -#define ACM_AUTO_TEST // Most common codecs and settings will be tested -//#define ACM_TEST_ENC_DEC // You decide what to test in run time. - // Used for debugging and for testing while implementing. -//#define ACM_TEST_TWO_WAY // Debugging -//#define ACM_TEST_ALL_ENC_DEC // Loop through all defined codecs and settings -//#define ACM_TEST_STEREO // Run stereo and spatial audio tests -//#define ACM_TEST_VAD_DTX // Run all VAD/DTX tests -//#define ACM_TEST_FEC // Test FEC (also called RED) -//#define ACM_TEST_CODEC_SPEC_API // Only iSAC has codec specfic APIs in this version -//#define ACM_TEST_FULL_API // Test all APIs with threads (long test) - - -void PopulateTests(std::vector* tests) -{ - - Trace::CreateTrace(); - Trace::SetTraceFile("./modules/audio_coding/main/test/res_tests/test_trace.txt"); - - printf("The following tests will be executed:\n"); -#ifdef ACM_AUTO_TEST - printf(" ACM auto test\n"); - tests->push_back(new EncodeDecodeTest(0)); - tests->push_back(new TwoWayCommunication(0)); - tests->push_back(new TestAllCodecs(0)); - tests->push_back(new TestStereo(0)); - tests->push_back(new SpatialAudio(0)); - tests->push_back(new TestVADDTX(0)); - tests->push_back(new TestFEC(0)); - tests->push_back(new ISACTest(0)); -#endif -#ifdef ACM_TEST_ENC_DEC - printf(" ACM encode-decode test\n"); - tests->push_back(new EncodeDecodeTest(2)); -#endif -#ifdef ACM_TEST_TWO_WAY - printf(" ACM two-way communication test\n"); - tests->push_back(new TwoWayCommunication(1)); -#endif -#ifdef ACM_TEST_ALL_ENC_DEC - printf(" ACM all codecs test\n"); - tests->push_back(new TestAllCodecs(1)); -#endif -#ifdef ACM_TEST_STEREO - printf(" ACM stereo test\n"); - tests->push_back(new TestStereo(1)); - tests->push_back(new SpatialAudio(2)); -#endif -#ifdef ACM_TEST_VAD_DTX - printf(" ACM VAD-DTX test\n"); - tests->push_back(new TestVADDTX(1)); -#endif -#ifdef ACM_TEST_FEC - printf(" ACM FEC test\n"); - tests->push_back(new TestFEC(1)); -#endif -#ifdef ACM_TEST_CODEC_SPEC_API - printf(" ACM codec API test\n"); - tests->push_back(new ISACTest(1)); -#endif -#ifdef ACM_TEST_FULL_API - printf(" ACM full API test\n"); - tests->push_back(new APITest()); -#endif - printf("\n"); -} - -int main() -{ - std::vector tests; - PopulateTests(&tests); - std::vector::iterator it; - WebRtc_Word8 version[5000]; - version[0] = '\0'; - - WebRtc_UWord32 remainingBufferInByte = 4999; - WebRtc_UWord32 position = 0; - AudioCodingModule::GetVersion(version, remainingBufferInByte, position); - - printf("%s\n", version); - for (it=tests.begin() ; it < tests.end(); it++) - { - try { - - (*it)->Perform(); - } - catch (char *except) - { - printf("Test failed with message: %s", except); - getchar(); - return -1; - } - delete (*it); - } - - Trace::ReturnTrace(); - printf("ACM test completed\n"); - - return 0; -} diff --git a/modules/audio_coding/main/test/TimedTrace.cpp b/modules/audio_coding/main/test/TimedTrace.cpp deleted file mode 100644 index 6bf301f52..000000000 --- a/modules/audio_coding/main/test/TimedTrace.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "TimedTrace.h" -#include - -double TimedTrace::_timeEllapsedSec = 0; -FILE* TimedTrace::_timedTraceFile = NULL; - -TimedTrace::TimedTrace() -{ - -} - -TimedTrace::~TimedTrace() -{ - if(_timedTraceFile != NULL) - { - fclose(_timedTraceFile); - } - _timedTraceFile = NULL; -} - -WebRtc_Word16 -TimedTrace::SetUp(char* fileName) -{ - if(_timedTraceFile == NULL) - { - _timedTraceFile = fopen(fileName, "w"); - } - if(_timedTraceFile == NULL) - { - return -1; - } - return 0; -} - -void -TimedTrace::SetTimeEllapsed(double timeEllapsedSec) -{ - _timeEllapsedSec = timeEllapsedSec; -} - -double -TimedTrace::TimeEllapsed() -{ - return _timeEllapsedSec; -} - -void -TimedTrace::Tick10Msec() -{ - _timeEllapsedSec += 0.010; -} - -void -TimedTrace::TimedLogg(char* message) -{ - unsigned int minutes = (WebRtc_UWord32)floor(_timeEllapsedSec / 60.0); - double seconds = _timeEllapsedSec - minutes * 60; - //char myFormat[100] = "%8.2f, %3u:%05.2f: %s\n"; - if(_timedTraceFile != NULL) - { - fprintf(_timedTraceFile, "%8.2f, %3u:%05.2f: %s\n", - _timeEllapsedSec, - minutes, - seconds, - message); - } -} diff --git a/modules/audio_coding/main/test/TimedTrace.h b/modules/audio_coding/main/test/TimedTrace.h deleted file mode 100644 index d37d287b9..000000000 --- a/modules/audio_coding/main/test/TimedTrace.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TIMED_TRACE_H -#define TIMED_TRACE_H - -#include "typedefs.h" - -#include -#include - - -class TimedTrace -{ -public: - TimedTrace(); - ~TimedTrace(); - - void SetTimeEllapsed(double myTime); - double TimeEllapsed(); - void Tick10Msec(); - WebRtc_Word16 SetUp(char* fileName); - void TimedLogg(char* message); - -private: - static double _timeEllapsedSec; - static FILE* _timedTraceFile; - -}; - -#endif diff --git a/modules/audio_coding/main/test/TwoWayCommunication.cpp b/modules/audio_coding/main/test/TwoWayCommunication.cpp deleted file mode 100644 index 21d40121a..000000000 --- a/modules/audio_coding/main/test/TwoWayCommunication.cpp +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#ifdef WIN32 -#include -#endif - -#include "TwoWayCommunication.h" -#include "engine_configurations.h" -#include "PCMFile.h" -#include "utility.h" -#include "trace.h" -#include "common_types.h" - -using namespace webrtc; - -#define MAX_FILE_NAME_LENGTH_BYTE 500 - -TwoWayCommunication::TwoWayCommunication(int testMode) -{ - _testMode = testMode; -} - -TwoWayCommunication::~TwoWayCommunication() -{ - AudioCodingModule::Destroy(_acmA); - AudioCodingModule::Destroy(_acmB); - - AudioCodingModule::Destroy(_acmRefA); - AudioCodingModule::Destroy(_acmRefB); - - delete _channel_A2B; - delete _channel_B2A; - - delete _channelRef_A2B; - delete _channelRef_B2A; -#ifdef WEBRTC_DTMF_DETECTION - if(_dtmfDetectorA != NULL) - { - delete _dtmfDetectorA; - } - if(_dtmfDetectorB != NULL) - { - delete _dtmfDetectorB; - } -#endif - _inFileA.Close(); - _inFileB.Close(); - _outFileA.Close(); - _outFileB.Close(); - _outFileRefA.Close(); - _outFileRefB.Close(); -} - - -WebRtc_UWord8 -TwoWayCommunication::ChooseCodec(WebRtc_UWord8* codecID_A, WebRtc_UWord8* codecID_B) -{ - AudioCodingModule* tmpACM = AudioCodingModule::Create(0); - WebRtc_UWord8 noCodec = tmpACM->NumberOfCodecs(); - CodecInst codecInst; - printf("List of Supported Codecs\n"); - printf("========================\n"); - for(WebRtc_UWord8 codecCntr = 0; codecCntr < noCodec; codecCntr++) - { - tmpACM->Codec(codecCntr, codecInst); - printf("%d- %s\n", codecCntr, codecInst.plname); - } - printf("\nChoose a send codec for side A [0]: "); - char myStr[15] = ""; - fgets(myStr, 10, stdin); - *codecID_A = (WebRtc_UWord8)atoi(myStr); - - printf("\nChoose a send codec for side B [0]: "); - fgets(myStr, 10, stdin); - *codecID_B = (WebRtc_UWord8)atoi(myStr); - - AudioCodingModule::Destroy(tmpACM); - printf("\n"); - return 0; -} - -WebRtc_Word16 -TwoWayCommunication::ChooseFile(char* fileName, WebRtc_Word16 maxLen, WebRtc_UWord16* frequencyHz) -{ - WebRtc_Word8 tmpName[MAX_FILE_NAME_LENGTH_BYTE]; - //strcpy(_fileName, "in.pcm"); - //printf("\n\nPlease enter the input file: "); - fgets(tmpName, MAX_FILE_NAME_LENGTH_BYTE, stdin); - tmpName[MAX_FILE_NAME_LENGTH_BYTE-1] = '\0'; - WebRtc_Word16 n = 0; - - // removing leading spaces - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (tmpName[n] != 0) && - (n < MAX_FILE_NAME_LENGTH_BYTE)) - { - n++; - } - if(n > 0) - { - memmove(tmpName, &tmpName[n], MAX_FILE_NAME_LENGTH_BYTE - n); - } - - //removing trailing spaces - n = (WebRtc_Word16)(strlen(tmpName) - 1); - if(n >= 0) - { - while((isspace(tmpName[n]) || iscntrl(tmpName[n])) && - (n >= 0)) - { - n--; - } - } - if(n >= 0) - { - tmpName[n + 1] = '\0'; - } - - WebRtc_Word16 len = (WebRtc_Word16)strlen(tmpName); - if(len > maxLen) - { - return -1; - } - if(len > 0) - { - strncpy(fileName, tmpName, len+1); - } - printf("Enter the sampling frequency (in Hz) of the above file [%u]: ", *frequencyHz); - fgets(tmpName, 6, stdin); - WebRtc_UWord16 tmpFreq = (WebRtc_UWord16)atoi(tmpName); - if(tmpFreq > 0) - { - *frequencyHz = tmpFreq; - } - return 0; -} - -WebRtc_Word16 TwoWayCommunication::SetUp() -{ - _acmA = AudioCodingModule::Create(1); - _acmB = AudioCodingModule::Create(2); - - _acmRefA = AudioCodingModule::Create(3); - _acmRefB = AudioCodingModule::Create(4); - - WebRtc_UWord8 codecID_A; - WebRtc_UWord8 codecID_B; - - ChooseCodec(&codecID_A, &codecID_B); - CodecInst codecInst_A; - CodecInst codecInst_B; - CodecInst dummyCodec; - _acmA->Codec(codecID_A, codecInst_A); - _acmB->Codec(codecID_B, codecInst_B); - - _acmA->Codec(6, dummyCodec); - - //--- Set A codecs - CHECK_ERROR(_acmA->RegisterSendCodec(codecInst_A)); - CHECK_ERROR(_acmA->RegisterReceiveCodec(codecInst_B)); -#ifdef WEBRTC_DTMF_DETECTION - _dtmfDetectorA = new(DTMFDetector); - CHECK_ERROR(_acmA->RegisterIncomingMessagesCallback(_dtmfDetectorA, ACMUSA)); -#endif - //--- Set ref-A codecs - CHECK_ERROR(_acmRefA->RegisterSendCodec(codecInst_A)); - CHECK_ERROR(_acmRefA->RegisterReceiveCodec(codecInst_B)); - - //--- Set B codecs - CHECK_ERROR(_acmB->RegisterSendCodec(codecInst_B)); - CHECK_ERROR(_acmB->RegisterReceiveCodec(codecInst_A)); -#ifdef WEBRTC_DTMF_DETECTION - _dtmfDetectorB = new(DTMFDetector); - CHECK_ERROR(_acmB->RegisterIncomingMessagesCallback(_dtmfDetectorB, ACMUSA)); -#endif - - //--- Set ref-B codecs - CHECK_ERROR(_acmRefB->RegisterSendCodec(codecInst_B)); - CHECK_ERROR(_acmRefB->RegisterReceiveCodec(codecInst_A)); - - char fileName[500]; - char refFileName[500]; - WebRtc_UWord16 frequencyHz; - - //--- Input A - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - frequencyHz = 32000; - printf("Enter input file at side A [%s]: ", fileName); - ChooseFile(fileName, 499, &frequencyHz); - - - _inFileA.Open(fileName, frequencyHz, "rb"); - - //--- Output A - strcpy(fileName, "outA.pcm"); - frequencyHz = 16000; - printf("Enter output file at side A [%s]: ", fileName); - ChooseFile(fileName, 499, &frequencyHz); - _outFileA.Open(fileName, frequencyHz, "wb"); - strcpy(refFileName, "ref_"); - strcat(refFileName, fileName); - _outFileRefA.Open(refFileName, frequencyHz, "wb"); - - //--- Input B - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - frequencyHz = 32000; - printf("\n\nEnter input file at side B [%s]: ", fileName); - ChooseFile(fileName, 499, &frequencyHz); - _inFileB.Open(fileName, frequencyHz, "rb"); - - //--- Output B - strcpy(fileName, "outB.pcm"); - frequencyHz = 16000; - printf("Enter output file at side B [%s]: ", fileName); - ChooseFile(fileName, 499, &frequencyHz); - _outFileB.Open(fileName, frequencyHz, "wb"); - strcpy(refFileName, "ref_"); - strcat(refFileName, fileName); - _outFileRefB.Open(refFileName, frequencyHz, "wb"); - - //--- Set A-to-B channel - _channel_A2B = new Channel; - _acmA->RegisterTransportCallback(_channel_A2B); - _channel_A2B->RegisterReceiverACM(_acmB); - //--- Do the same for the reference - _channelRef_A2B = new Channel; - _acmRefA->RegisterTransportCallback(_channelRef_A2B); - _channelRef_A2B->RegisterReceiverACM(_acmRefB); - - //--- Set B-to-A channel - _channel_B2A = new Channel; - _acmB->RegisterTransportCallback(_channel_B2A); - _channel_B2A->RegisterReceiverACM(_acmA); - //--- Do the same for reference - _channelRef_B2A = new Channel; - _acmRefB->RegisterTransportCallback(_channelRef_B2A); - _channelRef_B2A->RegisterReceiverACM(_acmRefA); - - // The clicks will be more obvious when we - // are in FAX mode. - _acmB->SetPlayoutMode(fax); - _acmRefB->SetPlayoutMode(fax); - - return 0; -} - -WebRtc_Word16 TwoWayCommunication::SetUpAutotest() -{ - _acmA = AudioCodingModule::Create(1); - _acmB = AudioCodingModule::Create(2); - - _acmRefA = AudioCodingModule::Create(3); - _acmRefB = AudioCodingModule::Create(4); - - CodecInst codecInst_A; - CodecInst codecInst_B; - CodecInst dummyCodec; - - _acmA->Codec("ISAC", codecInst_A, 16000); - _acmB->Codec("L16", codecInst_B, 8000); - _acmA->Codec(6, dummyCodec); - - //--- Set A codecs - CHECK_ERROR(_acmA->RegisterSendCodec(codecInst_A)); - CHECK_ERROR(_acmA->RegisterReceiveCodec(codecInst_B)); -#ifdef WEBRTC_DTMF_DETECTION - _dtmfDetectorA = new(DTMFDetector); - CHECK_ERROR(_acmA->RegisterIncomingMessagesCallback(_dtmfDetectorA, ACMUSA)); -#endif - - //--- Set ref-A codecs - CHECK_ERROR(_acmRefA->RegisterSendCodec(codecInst_A)); - CHECK_ERROR(_acmRefA->RegisterReceiveCodec(codecInst_B)); - - //--- Set B codecs - CHECK_ERROR(_acmB->RegisterSendCodec(codecInst_B)); - CHECK_ERROR(_acmB->RegisterReceiveCodec(codecInst_A)); -#ifdef WEBRTC_DTMF_DETECTION - _dtmfDetectorB = new(DTMFDetector); - CHECK_ERROR(_acmB->RegisterIncomingMessagesCallback(_dtmfDetectorB, ACMUSA)); -#endif - - //--- Set ref-B codecs - CHECK_ERROR(_acmRefB->RegisterSendCodec(codecInst_B)); - CHECK_ERROR(_acmRefB->RegisterReceiveCodec(codecInst_A)); - - char fileName[500]; - char refFileName[500]; - WebRtc_UWord16 frequencyHz; - - - //--- Input A - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - frequencyHz = 16000; - _inFileA.Open(fileName, frequencyHz, "rb"); - - //--- Output A - strcpy(fileName, "./modules/audio_coding/main/test/res_autotests/outAutotestA.pcm"); - frequencyHz = 16000; - _outFileA.Open(fileName, frequencyHz, "wb"); - strcpy(refFileName, "./modules/audio_coding/main/test/res_autotests/ref_outAutotestA.pcm"); - _outFileRefA.Open(refFileName, frequencyHz, "wb"); - - //--- Input B - strcpy(fileName, "./modules/audio_coding/main/test/testfile32kHz.pcm"); - frequencyHz = 16000; - _inFileB.Open(fileName, frequencyHz, "rb"); - - //--- Output B - strcpy(fileName, "./modules/audio_coding/main/test/res_autotests/outAutotestB.pcm"); - frequencyHz = 16000; - _outFileB.Open(fileName, frequencyHz, "wb"); - strcpy(refFileName, "./modules/audio_coding/main/test/res_autotests/ref_outAutotestB.pcm"); - _outFileRefB.Open(refFileName, frequencyHz, "wb"); - - //--- Set A-to-B channel - _channel_A2B = new Channel; - _acmA->RegisterTransportCallback(_channel_A2B); - _channel_A2B->RegisterReceiverACM(_acmB); - //--- Do the same for the reference - _channelRef_A2B = new Channel; - _acmRefA->RegisterTransportCallback(_channelRef_A2B); - _channelRef_A2B->RegisterReceiverACM(_acmRefB); - - //--- Set B-to-A channel - _channel_B2A = new Channel; - _acmB->RegisterTransportCallback(_channel_B2A); - _channel_B2A->RegisterReceiverACM(_acmA); - //--- Do the same for reference - _channelRef_B2A = new Channel; - _acmRefB->RegisterTransportCallback(_channelRef_B2A); - _channelRef_B2A->RegisterReceiverACM(_acmRefA); - - // The clicks will be more obvious when we - // are in FAX mode. - _acmB->SetPlayoutMode(fax); - _acmRefB->SetPlayoutMode(fax); - - return 0; -} - -void -TwoWayCommunication::Perform() -{ - if(_testMode == 0) - { - printf("Running TwoWayCommunication Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- TwoWayCommunication ----------"); - SetUpAutotest(); - } - else - { - SetUp(); - } - unsigned int msecPassed = 0; - unsigned int secPassed = 0; - - WebRtc_Word32 outFreqHzA = _outFileA.SamplingFrequency(); - WebRtc_Word32 outFreqHzB = _outFileB.SamplingFrequency(); - - AudioFrame audioFrame; - - CodecInst codecInst_B; - CodecInst dummy; - - _acmB->SendCodec(codecInst_B); - - if(_testMode != 0) - { - printf("\n"); - printf("sec:msec A B\n"); - printf("-------- ----- -----\n"); - } - - while(!_inFileA.EndOfFile() && !_inFileB.EndOfFile()) - { - _inFileA.Read10MsData(audioFrame); - _acmA->Add10MsData(audioFrame); - _acmRefA->Add10MsData(audioFrame); - - _inFileB.Read10MsData(audioFrame); - _acmB->Add10MsData(audioFrame); - _acmRefB->Add10MsData(audioFrame); - - - _acmA->Process(); - _acmB->Process(); - _acmRefA->Process(); - _acmRefB->Process(); - - _acmA->PlayoutData10Ms(outFreqHzA, audioFrame); - _outFileA.Write10MsData(audioFrame); - - _acmRefA->PlayoutData10Ms(outFreqHzA, audioFrame); - _outFileRefA.Write10MsData(audioFrame); - - _acmB->PlayoutData10Ms(outFreqHzB, audioFrame); - _outFileB.Write10MsData(audioFrame); - - _acmRefB->PlayoutData10Ms(outFreqHzB, audioFrame); - _outFileRefB.Write10MsData(audioFrame); - - msecPassed += 10; - if(msecPassed >= 1000) - { - msecPassed = 0; - secPassed++; - } - if(((secPassed%5) == 4) && (msecPassed == 0)) - { - if(_testMode != 0) - { - printf("%3u:%3u ", secPassed, msecPassed); - } - _acmA->ResetEncoder(); - if(_testMode == 0) - { - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- Errors epected"); - printf("."); - } - else - { - printf("Reset Encoder (click in side B) "); - printf("Initialize Sender (no audio in side A)\n"); - } - CHECK_ERROR(_acmB->InitializeSender()); - } - if(((secPassed%5) == 4) && (msecPassed >= 990)) - { - if(_testMode == 0) - { - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "----- END: Errors epected"); - printf("."); - } - else - { - printf("%3u:%3u ", secPassed, msecPassed); - printf(" "); - printf("Register Send Codec (audio back in side A)\n"); - } - CHECK_ERROR(_acmB->RegisterSendCodec(codecInst_B)); - CHECK_ERROR(_acmB->SendCodec(dummy)); - } - if(((secPassed%7) == 6) && (msecPassed == 0)) - { - CHECK_ERROR(_acmB->ResetDecoder()); - if(_testMode == 0) - { - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- Errors epected"); - printf("."); - } - else - { - printf("%3u:%3u ", secPassed, msecPassed); - printf("Initialize Receiver (no audio in side A) "); - printf("Reset Decoder\n"); - } - CHECK_ERROR(_acmA->InitializeReceiver()); - } - if(((secPassed%7) == 6) && (msecPassed >= 990)) - { - if(_testMode == 0) - { - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "----- END: Errors epected"); - printf("."); - } - else - { - printf("%3u:%3u ", secPassed, msecPassed); - printf("Register Receive Coded (audio back in side A)\n"); - } - CHECK_ERROR(_acmA->RegisterReceiveCodec(codecInst_B)); - } - //Sleep(9); - } - if(_testMode == 0) - { - printf("Done!\n"); - } - -#ifdef WEBRTC_DTMF_DETECTION - printf("\nDTMF at Side A\n"); - _dtmfDetectorA->PrintDetectedDigits(); - - printf("\nDTMF at Side B\n"); - _dtmfDetectorB->PrintDetectedDigits(); -#endif - - -} - diff --git a/modules/audio_coding/main/test/TwoWayCommunication.h b/modules/audio_coding/main/test/TwoWayCommunication.h deleted file mode 100644 index 66ede04d7..000000000 --- a/modules/audio_coding/main/test/TwoWayCommunication.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TWO_WAY_COMMUNICATION_H -#define TWO_WAY_COMMUNICATION_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" -#include "audio_coding_module.h" -#include "utility.h" - - -class TwoWayCommunication : public ACMTest -{ -public: - TwoWayCommunication(int testMode = 1); - ~TwoWayCommunication(); - - void Perform(); -private: - WebRtc_UWord8 ChooseCodec(WebRtc_UWord8* codecID_A, WebRtc_UWord8* codecID_B); - WebRtc_Word16 ChooseFile(char* fileName, WebRtc_Word16 maxLen, WebRtc_UWord16* frequencyHz); - WebRtc_Word16 SetUp(); - WebRtc_Word16 SetUpAutotest(); - - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - AudioCodingModule* _acmRefA; - AudioCodingModule* _acmRefB; - - Channel* _channel_A2B; - Channel* _channel_B2A; - - Channel* _channelRef_A2B; - Channel* _channelRef_B2A; - - PCMFile _inFileA; - PCMFile _inFileB; - - PCMFile _outFileA; - PCMFile _outFileB; - - PCMFile _outFileRefA; - PCMFile _outFileRefB; - - DTMFDetector* _dtmfDetectorA; - DTMFDetector* _dtmfDetectorB; - - int _testMode; -}; - - -#endif diff --git a/modules/audio_coding/main/test/iSACTest.cpp b/modules/audio_coding/main/test/iSACTest.cpp deleted file mode 100644 index b1c011996..000000000 --- a/modules/audio_coding/main/test/iSACTest.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#if _WIN32 -#include -#elif WEBRTC_LINUX -#include -#else -#include -#include -#endif - -#include "event_wrapper.h" -#include "iSACTest.h" -#include "utility.h" -#include "trace.h" - -#include "tick_util.h" - - -void SetISACConfigDefault( - ACMTestISACConfig& isacConfig) -{ - isacConfig.currentRateBitPerSec = 0; - isacConfig.currentFrameSizeMsec = 0; - isacConfig.maxRateBitPerSec = 0; - isacConfig.maxPayloadSizeByte = 0; - isacConfig.encodingMode = -1; - isacConfig.initRateBitPerSec = 0; - isacConfig.initFrameSizeInMsec = 0; - isacConfig.enforceFrameSize = false; - return; -} - - -WebRtc_Word16 SetISAConfig( - ACMTestISACConfig& isacConfig, - AudioCodingModule* acm, - int testMode) -{ - - if((isacConfig.currentRateBitPerSec != 0) || - (isacConfig.currentFrameSizeMsec != 0)) - { - CodecInst sendCodec; - acm->SendCodec(sendCodec); - if(isacConfig.currentRateBitPerSec < 0) - { - sendCodec.rate = -1; - CHECK_ERROR(acm->RegisterSendCodec(sendCodec)); - if(testMode != 0) - { - printf("ISAC-%s Registered in adaptive (channel-dependent) mode.\n", - (sendCodec.plfreq == 32000)? "swb":"wb"); - } - } - else - { - - if(isacConfig.currentRateBitPerSec != 0) - { - sendCodec.rate = isacConfig.currentRateBitPerSec; - } - if(isacConfig.currentFrameSizeMsec != 0) - { - sendCodec.pacsize = isacConfig.currentFrameSizeMsec * - (sendCodec.plfreq / 1000); - } - CHECK_ERROR(acm->RegisterSendCodec(sendCodec)); - if(testMode != 0) - { - printf("Target rate is set to %d bit/sec with frame-size %d ms \n", - (int)isacConfig.currentRateBitPerSec, - (int)sendCodec.pacsize / (sendCodec.plfreq / 1000)); - } - } - } - - if(isacConfig.maxRateBitPerSec > 0) - { - CHECK_ERROR(acm->SetISACMaxRate(isacConfig.maxRateBitPerSec)); - if(testMode != 0) - { - printf("Max rate is set to %u bit/sec\n", - isacConfig.maxRateBitPerSec); - } - } - if(isacConfig.maxPayloadSizeByte > 0) - { - CHECK_ERROR(acm->SetISACMaxPayloadSize(isacConfig.maxPayloadSizeByte)); - if(testMode != 0) - { - printf("Max payload-size is set to %u bit/sec\n", - isacConfig.maxPayloadSizeByte); - } - } - if((isacConfig.initFrameSizeInMsec != 0) || - (isacConfig.initRateBitPerSec != 0)) - { - CHECK_ERROR(acm->ConfigISACBandwidthEstimator( - (WebRtc_UWord8)isacConfig.initFrameSizeInMsec, - (WebRtc_UWord16)isacConfig.initRateBitPerSec, - isacConfig.enforceFrameSize)); - if((isacConfig.initFrameSizeInMsec != 0) && (testMode != 0)) - { - printf("Initialize BWE to %d msec frame-size\n", - isacConfig.initFrameSizeInMsec); - } - if((isacConfig.initRateBitPerSec != 0) && (testMode != 0)) - { - printf("Initialize BWE to %u bit/sec send-bandwidth\n", - isacConfig.initRateBitPerSec); - } - } - - return 0; -} - - -ISACTest::ISACTest(int testMode) -{ - _testMode = testMode; -} - -ISACTest::~ISACTest() -{ - AudioCodingModule::Destroy(_acmA); - AudioCodingModule::Destroy(_acmB); - - delete _channel_A2B; - delete _channel_B2A; -} - - -WebRtc_Word16 -ISACTest::Setup() -{ - int codecCntr; - CodecInst codecParam; - - _acmA = AudioCodingModule::Create(1); - _acmB = AudioCodingModule::Create(2); - - for(codecCntr = 0; codecCntr < AudioCodingModule::NumberOfCodecs(); codecCntr++) - { - AudioCodingModule::Codec(codecCntr, codecParam); - if(!STR_CASE_CMP(codecParam.plname, "ISAC") && codecParam.plfreq == 16000) - { - memcpy(&_paramISAC16kHz, &codecParam, sizeof(CodecInst)); - _idISAC16kHz = codecCntr; - } - if(!STR_CASE_CMP(codecParam.plname, "ISAC") && codecParam.plfreq == 32000) - { - memcpy(&_paramISAC32kHz, &codecParam, sizeof(CodecInst)); - _idISAC32kHz = codecCntr; - } - } - - // register both iSAC-wb & iSAC-swb in both sides as receiver codecs - CHECK_ERROR(_acmA->RegisterReceiveCodec(_paramISAC16kHz)); - CHECK_ERROR(_acmA->RegisterReceiveCodec(_paramISAC32kHz)); - CHECK_ERROR(_acmB->RegisterReceiveCodec(_paramISAC16kHz)); - CHECK_ERROR(_acmB->RegisterReceiveCodec(_paramISAC32kHz)); - - //--- Set A-to-B channel - _channel_A2B = new Channel; - CHECK_ERROR(_acmA->RegisterTransportCallback(_channel_A2B)); - _channel_A2B->RegisterReceiverACM(_acmB); - - //--- Set B-to-A channel - _channel_B2A = new Channel; - CHECK_ERROR(_acmB->RegisterTransportCallback(_channel_B2A)); - _channel_B2A->RegisterReceiverACM(_acmA); - - strncpy(_fileNameSWB, "./modules/audio_coding/main/test/testfile32kHz.pcm", - MAX_FILE_NAME_LENGTH_BYTE); - - _acmB->RegisterSendCodec(_paramISAC16kHz); - _acmA->RegisterSendCodec(_paramISAC32kHz); - - if(_testMode != 0) - { - printf("Side A Send Codec\n"); - printf("%s %d\n", _paramISAC32kHz.plname, _paramISAC32kHz.plfreq); - - printf("Side B Send Codec\n"); - printf("%s %d\n", _paramISAC16kHz.plname, _paramISAC16kHz.plfreq); - } - - _inFileA.Open(_fileNameSWB, 32000, "rb"); - if(_testMode == 0) - { - char fileNameA[] = "./modules/audio_coding/main/test/res_autotests/testisac_a.pcm"; - char fileNameB[] = "./modules/audio_coding/main/test/res_autotests/testisac_b.pcm"; - _outFileA.Open(fileNameA, 32000, "wb"); - _outFileB.Open(fileNameB, 32000, "wb"); - } - else - { - char fileNameA[] = "./modules/audio_coding/main/test/res_tests/testisac_a.pcm"; - char fileNameB[] = "./modules/audio_coding/main/test/res_tests/testisac_b.pcm"; - _outFileA.Open(fileNameA, 32000, "wb"); - _outFileB.Open(fileNameB, 32000, "wb"); - } - - while(!_inFileA.EndOfFile()) - { - Run10ms(); - } - CodecInst receiveCodec; - CHECK_ERROR(_acmA->ReceiveCodec(receiveCodec)); - if(_testMode != 0) - { - printf("Side A Receive Codec\n"); - printf("%s %d\n", receiveCodec.plname, receiveCodec.plfreq); - } - - CHECK_ERROR(_acmB->ReceiveCodec(receiveCodec)); - if(_testMode != 0) - { - printf("Side B Receive Codec\n"); - printf("%s %d\n", receiveCodec.plname, receiveCodec.plfreq); - } - - _inFileA.Close(); - _outFileA.Close(); - _outFileB.Close(); - - return 0; -} - - -void -ISACTest::Perform() -{ - if(_testMode == 0) - { - printf("Running iSAC Test"); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceAudioCoding, -1, "---------- iSACTest ----------"); - } - - Setup(); - - WebRtc_Word16 testNr = 0; - ACMTestISACConfig wbISACConfig; - ACMTestISACConfig swbISACConfig; - - SetISACConfigDefault(wbISACConfig); - SetISACConfigDefault(swbISACConfig); - - wbISACConfig.currentRateBitPerSec = -1; - swbISACConfig.currentRateBitPerSec = -1; - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - if (_testMode != 0) - { - SetISACConfigDefault(wbISACConfig); - SetISACConfigDefault(swbISACConfig); - - wbISACConfig.currentRateBitPerSec = -1; - swbISACConfig.currentRateBitPerSec = -1; - wbISACConfig.initRateBitPerSec = 13000; - wbISACConfig.initFrameSizeInMsec = 60; - swbISACConfig.initRateBitPerSec = 20000; - swbISACConfig.initFrameSizeInMsec = 30; - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - SetISACConfigDefault(wbISACConfig); - SetISACConfigDefault(swbISACConfig); - - wbISACConfig.currentRateBitPerSec = 20000; - swbISACConfig.currentRateBitPerSec = 48000; - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - wbISACConfig.currentRateBitPerSec = 16000; - swbISACConfig.currentRateBitPerSec = 30000; - wbISACConfig.currentFrameSizeMsec = 60; - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - } - - SetISACConfigDefault(wbISACConfig); - SetISACConfigDefault(swbISACConfig); - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - int dummy; - if((_testMode == 0) || (_testMode == 1)) - { - swbISACConfig.maxPayloadSizeByte = (WebRtc_UWord16)200; - wbISACConfig.maxPayloadSizeByte = (WebRtc_UWord16)200; - } - else - { - printf("Enter the max payload-size for side A: "); - scanf("%d", &dummy); - swbISACConfig.maxPayloadSizeByte = (WebRtc_UWord16)dummy; - printf("Enter the max payload-size for side B: "); - scanf("%d", &dummy); - wbISACConfig.maxPayloadSizeByte = (WebRtc_UWord16)dummy; - } - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - _acmA->ResetEncoder(); - _acmB->ResetEncoder(); - SetISACConfigDefault(wbISACConfig); - SetISACConfigDefault(swbISACConfig); - - if((_testMode == 0) || (_testMode == 1)) - { - swbISACConfig.maxRateBitPerSec = (WebRtc_UWord32)48000; - wbISACConfig.maxRateBitPerSec = (WebRtc_UWord32)48000; - } - else - { - printf("Enter the max rate for side A: "); - scanf("%d", &dummy); - swbISACConfig.maxRateBitPerSec = (WebRtc_UWord32)dummy; - printf("Enter the max rate for side B: "); - scanf("%d", &dummy); - wbISACConfig.maxRateBitPerSec = (WebRtc_UWord32)dummy; - } - - testNr++; - EncodeDecode(testNr, wbISACConfig, swbISACConfig); - - - testNr++; - if(_testMode == 0) - { - SwitchingSamplingRate(testNr, 4); - printf("Done!\n"); - } - else - { - SwitchingSamplingRate(testNr, 80); - } -} - - -void -ISACTest::Run10ms() -{ - AudioFrame audioFrame; - - _inFileA.Read10MsData(audioFrame); - CHECK_ERROR(_acmA->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmB->Add10MsData(audioFrame)); - - CHECK_ERROR(_acmA->Process()); - CHECK_ERROR(_acmB->Process()); - - CHECK_ERROR(_acmA->PlayoutData10Ms(32000, audioFrame)); - _outFileA.Write10MsData(audioFrame); - - CHECK_ERROR(_acmB->PlayoutData10Ms(32000, audioFrame)); - _outFileB.Write10MsData(audioFrame); -} - -void -ISACTest::EncodeDecode( - int testNr, - ACMTestISACConfig& wbISACConfig, - ACMTestISACConfig& swbISACConfig) -{ - if(_testMode == 0) - { - printf("."); - } - else - { - printf("\nTest %d:\n\n", testNr); - } - char fileNameOut[MAX_FILE_NAME_LENGTH_BYTE]; - - // Files in Side A - _inFileA.Open(_fileNameSWB, 32000, "rb", true); - if(_testMode == 0) - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_autotests/out_iSACTest_%s_%02d.pcm", - "A", - testNr); - } - else - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_tests/out%s_%02d.pcm", - "A", - testNr); - } - _outFileA.Open(fileNameOut, 32000, "wb"); - - // Files in Side B - _inFileB.Open(_fileNameSWB, 32000, "rb", true); - if(_testMode == 0) - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_autotests/out_iSACTest_%s_%02d.pcm", - "B", - testNr); - } - else - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_tests/out%s_%02d.pcm", - "B", - testNr); - } - _outFileB.Open(fileNameOut, 32000, "wb"); - - CHECK_ERROR(_acmA->RegisterSendCodec(_paramISAC16kHz)); - CHECK_ERROR(_acmA->RegisterSendCodec(_paramISAC32kHz)); - - CHECK_ERROR(_acmB->RegisterSendCodec(_paramISAC32kHz)); - CHECK_ERROR(_acmB->RegisterSendCodec(_paramISAC16kHz)); - if(_testMode != 0) - { - printf("Side A Sending Super-Wideband \n"); - printf("Side B Sending Wideband\n\n"); - } - - SetISAConfig(swbISACConfig, _acmA, _testMode); - SetISAConfig(wbISACConfig, _acmB, _testMode); - - bool adaptiveMode = false; - if((swbISACConfig.currentRateBitPerSec == -1) || - (wbISACConfig.currentRateBitPerSec == -1)) - { - adaptiveMode = true; - } - _myTimer.Reset(); - _channel_A2B->ResetStats(); - _channel_B2A->ResetStats(); - - char currentTime[500]; - if(_testMode == 2) printf("\n"); - CodecInst sendCodec; - EventWrapper* myEvent = EventWrapper::Create(); - myEvent->StartTimer(true, 10); - while(!(_inFileA.EndOfFile() || _inFileA.Rewinded())) - { - Run10ms(); - _myTimer.Tick10ms(); - _myTimer.CurrentTimeHMS(currentTime); - if(_testMode == 2) printf("\r%s ", currentTime); - - if((adaptiveMode) && (_testMode != 0)) - { - myEvent->Wait(5000); - - _acmA->SendCodec(sendCodec); - if(_testMode == 2) printf("[%d] ", sendCodec.rate); - _acmB->SendCodec(sendCodec); - if(_testMode == 2) printf("[%d] ", sendCodec.rate); - } - } - - if(_testMode != 0) - { - printf("\n\nSide A statistics\n\n"); - _channel_A2B->PrintStats(_paramISAC32kHz); - - printf("\n\nSide B statistics\n\n"); - _channel_B2A->PrintStats(_paramISAC16kHz); - } - - _channel_A2B->ResetStats(); - _channel_B2A->ResetStats(); - - if(_testMode != 0) printf("\n"); - _outFileA.Close(); - _outFileB.Close(); - _inFileA.Close(); - _inFileB.Close(); -} - -void -ISACTest::SwitchingSamplingRate( - int testNr, - int maxSampRateChange) -{ - char fileNameOut[MAX_FILE_NAME_LENGTH_BYTE]; - - // Files in Side A - _inFileA.Open(_fileNameSWB, 32000, "rb"); - if(_testMode == 0) - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_autotests/out_iSACTest_%s_%02d.pcm", - "A", - testNr); - } - else - { - printf("\nTest %d", testNr); - printf(" Alternate between WB and SWB at the sender Side\n\n"); - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_tests/out%s_%02d.pcm", - "A", - testNr); - } - _outFileA.Open(fileNameOut, 32000, "wb", true); - - // Files in Side B - _inFileB.Open(_fileNameSWB, 32000, "rb"); - if(_testMode == 0) - { - sprintf(fileNameOut, - "./modules/audio_coding/main/test/res_autotests/out_iSACTest_%s_%02d.pcm", - "B", - testNr); - } - else - { - sprintf(fileNameOut, "./modules/audio_coding/main/test/res_tests/out%s_%02d.pcm", - "B", - testNr); - } - _outFileB.Open(fileNameOut, 32000, "wb", true); - - CHECK_ERROR(_acmA->RegisterSendCodec(_paramISAC32kHz)); - CHECK_ERROR(_acmB->RegisterSendCodec(_paramISAC16kHz)); - if(_testMode != 0) - { - printf("Side A Sending Super-Wideband \n"); - printf("Side B Sending Wideband\n"); - } - - int numSendCodecChanged = 0; - _myTimer.Reset(); - char currentTime[50]; - while(numSendCodecChanged < (maxSampRateChange<<1)) - { - Run10ms(); - _myTimer.Tick10ms(); - _myTimer.CurrentTimeHMS(currentTime); - if(_testMode == 2) printf("\r%s", currentTime); - if(_inFileA.EndOfFile()) - { - if(_inFileA.SamplingFrequency() == 16000) - { - if(_testMode != 0) printf("\nSide A switched to Send Super-Wideband\n"); - _inFileA.Close(); - _inFileA.Open(_fileNameSWB, 32000, "rb"); - CHECK_ERROR(_acmA->RegisterSendCodec(_paramISAC32kHz)); - } - else - { - if(_testMode != 0) printf("\nSide A switched to Send Wideband\n"); - _inFileA.Close(); - _inFileA.Open(_fileNameSWB, 32000, "rb"); - CHECK_ERROR(_acmA->RegisterSendCodec(_paramISAC16kHz)); - } - numSendCodecChanged++; - } - - if(_inFileB.EndOfFile()) - { - if(_inFileB.SamplingFrequency() == 16000) - { - if(_testMode != 0) printf("\nSide B switched to Send Super-Wideband\n"); - _inFileB.Close(); - _inFileB.Open(_fileNameSWB, 32000, "rb"); - CHECK_ERROR(_acmB->RegisterSendCodec(_paramISAC32kHz)); - } - else - { - if(_testMode != 0) printf("\nSide B switched to Send Wideband\n"); - _inFileB.Close(); - _inFileB.Open(_fileNameSWB, 32000, "rb"); - CHECK_ERROR(_acmB->RegisterSendCodec(_paramISAC16kHz)); - } - numSendCodecChanged++; - } - } - _outFileA.Close(); - _outFileB.Close(); - _inFileA.Close(); - _inFileB.Close(); -} diff --git a/modules/audio_coding/main/test/iSACTest.h b/modules/audio_coding/main/test/iSACTest.h deleted file mode 100644 index c6d4b9cb8..000000000 --- a/modules/audio_coding/main/test/iSACTest.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_ISAC_TEST_H -#define ACM_ISAC_TEST_H - -#include "ACMTest.h" -#include "Channel.h" -#include "PCMFile.h" -#include "audio_coding_module.h" -#include "utility.h" -#include "common_types.h" - -#define MAX_FILE_NAME_LENGTH_BYTE 500 -#define NO_OF_CLIENTS 15 - -struct ACMTestISACConfig -{ - WebRtc_Word32 currentRateBitPerSec; - WebRtc_Word16 currentFrameSizeMsec; - WebRtc_UWord32 maxRateBitPerSec; - WebRtc_Word16 maxPayloadSizeByte; - WebRtc_Word16 encodingMode; - WebRtc_UWord32 initRateBitPerSec; - WebRtc_Word16 initFrameSizeInMsec; - bool enforceFrameSize; -}; - - - -class ISACTest : public ACMTest -{ -public: - ISACTest(int testMode); - ~ISACTest(); - - void Perform(); -private: - WebRtc_Word16 Setup(); - WebRtc_Word16 SetupConference(); - WebRtc_Word16 RunConference(); - - - void Run10ms(); - - void EncodeDecode( - int testNr, - ACMTestISACConfig& wbISACConfig, - ACMTestISACConfig& swbISACConfig); - - void TestBWE( - int testNr); - - void SwitchingSamplingRate( - int testNr, - int maxSampRateChange); - - AudioCodingModule* _acmA; - AudioCodingModule* _acmB; - - Channel* _channel_A2B; - Channel* _channel_B2A; - - PCMFile _inFileA; - PCMFile _inFileB; - - PCMFile _outFileA; - PCMFile _outFileB; - - WebRtc_UWord8 _idISAC16kHz; - WebRtc_UWord8 _idISAC32kHz; - CodecInst _paramISAC16kHz; - CodecInst _paramISAC32kHz; - - char _fileNameWB[MAX_FILE_NAME_LENGTH_BYTE]; - char _fileNameSWB[MAX_FILE_NAME_LENGTH_BYTE]; - - ACMTestTimer _myTimer; - int _testMode; - - AudioCodingModule* _defaultACM32; - AudioCodingModule* _defaultACM16; - - AudioCodingModule* _confACM[NO_OF_CLIENTS]; - AudioCodingModule* _clientACM[NO_OF_CLIENTS]; - Channel* _conf2Client[NO_OF_CLIENTS]; - Channel* _client2Conf[NO_OF_CLIENTS]; - - PCMFile _clientOutFile[NO_OF_CLIENTS]; -}; - - -#endif diff --git a/modules/audio_coding/main/test/utility.cpp b/modules/audio_coding/main/test/utility.cpp deleted file mode 100644 index c65401921..000000000 --- a/modules/audio_coding/main/test/utility.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "utility.h" -#include -#include -#include - - -#include "audio_coding_module.h" -#include "common_types.h" - - -#define NUM_CODECS_WITH_FIXED_PAYLOAD_TYPE 13 - - -ACMTestTimer::ACMTestTimer() : -_msec(0), -_sec(0), -_min(0), -_hour(0) -{ - return; -} - -ACMTestTimer::~ACMTestTimer() -{ - return; -} - -void ACMTestTimer::Reset() -{ - _msec = 0; - _sec = 0; - _min = 0; - _hour = 0; - return; -} -void ACMTestTimer::Tick10ms() -{ - _msec += 10; - Adjust(); - return; -} - -void ACMTestTimer::Tick1ms() -{ - _msec++; - Adjust(); - return; -} - -void ACMTestTimer::Tick100ms() -{ - _msec += 100; - Adjust(); - return; -} - -void ACMTestTimer::Tick1sec() -{ - _sec++; - Adjust(); - return; -} - -void ACMTestTimer::CurrentTimeHMS(char* currTime) -{ - sprintf(currTime, "%4lu:%02u:%06.3f", _hour, _min, (double)_sec + (double)_msec / 1000.); - return; -} - -void ACMTestTimer::CurrentTime( - unsigned long& h, - unsigned char& m, - unsigned char& s, - unsigned short& ms) -{ - h = _hour; - m = _min; - s = _sec; - ms = _msec; - return; -} - -void ACMTestTimer::Adjust() -{ - unsigned int n; - if(_msec >= 1000) - { - n = _msec / 1000; - _msec -= (1000 * n); - _sec += n; - } - if(_sec >= 60) - { - n = _sec / 60; - _sec -= (n * 60); - _min += n; - } - if(_min >= 60) - { - n = _min / 60; - _min -= (n * 60); - _hour += n; - } -} - - -WebRtc_Word16 -ChooseCodec( - CodecInst& codecInst) -{ - - PrintCodecs(); - //AudioCodingModule* tmpACM = AudioCodingModule::Create(0); - WebRtc_UWord8 noCodec = AudioCodingModule::NumberOfCodecs(); - WebRtc_Word8 codecID; - bool outOfRange = false; - char myStr[15] = ""; - do - { - printf("\nChoose a codec [0]: "); - fgets(myStr, 10, stdin); - codecID = atoi(myStr); - if((codecID < 0) || (codecID >= noCodec)) - { - printf("\nOut of range.\n"); - outOfRange = true; - } - } while(outOfRange); - - CHECK_ERROR(AudioCodingModule::Codec((WebRtc_UWord8)codecID, codecInst)); - return 0; -} - -void -PrintCodecs() -{ - WebRtc_UWord8 noCodec = AudioCodingModule::NumberOfCodecs(); - - CodecInst codecInst; - printf("No Name [Hz] [bps]\n"); - for(WebRtc_UWord8 codecCntr = 0; codecCntr < noCodec; codecCntr++) - { - AudioCodingModule::Codec(codecCntr, codecInst); - printf("%2d- %-18s %5d %6d\n", - codecCntr, codecInst.plname, codecInst.plfreq, codecInst.rate); - } - -} - -CircularBuffer::CircularBuffer(WebRtc_UWord32 len): -_buffIsFull(false), -_calcAvg(false), -_calcVar(false), -_sum(0), -_sumSqr(0), -_idx(0), -_buff(NULL) -{ - _buff = new(double[len]); - if(_buff == NULL) - { - _buffLen = 0; - } - else - { - for(WebRtc_UWord32 n = 0; n < len; n++) - { - _buff[n] = 0; - } - _buffLen = len; - } -} - -CircularBuffer::~CircularBuffer() -{ - if(_buff != NULL) - { - delete [] _buff; - _buff = NULL; - } -} - -void -CircularBuffer::Update( - const double newVal) -{ - assert(_buffLen > 0); - - // store the value that is going to be overwritten - double oldVal = _buff[_idx]; - // record the new value - _buff[_idx] = newVal; - // increment the index, to point to where we would - // write next - _idx++; - // it is a circular buffer, if we are at the end - // we have to cycle to the beginning - if(_idx >= _buffLen) - { - // flag that the buffer is filled up. - _buffIsFull = true; - _idx = 0; - } - - // Update - - if(_calcAvg) - { - // for the average we have to update - // the sum - _sum += (newVal - oldVal); - } - - if(_calcVar) - { - // to calculate variance we have to update - // the sum of squares - _sumSqr += (double)(newVal - oldVal) * (double)(newVal + oldVal); - } -} - -void -CircularBuffer::SetArithMean( - bool enable) -{ - assert(_buffLen > 0); - - if(enable && !_calcAvg) - { - WebRtc_UWord32 lim; - if(_buffIsFull) - { - lim = _buffLen; - } - else - { - lim = _idx; - } - _sum = 0; - for(WebRtc_UWord32 n = 0; n < lim; n++) - { - _sum += _buff[n]; - } - } - _calcAvg = enable; -} - -void -CircularBuffer::SetVariance( - bool enable) -{ - assert(_buffLen > 0); - - if(enable && !_calcVar) - { - WebRtc_UWord32 lim; - if(_buffIsFull) - { - lim = _buffLen; - } - else - { - lim = _idx; - } - _sumSqr = 0; - for(WebRtc_UWord32 n = 0; n < lim; n++) - { - _sumSqr += _buff[n] * _buff[n]; - } - } - _calcAvg = enable; -} - -WebRtc_Word16 -CircularBuffer::ArithMean(double& mean) -{ - assert(_buffLen > 0); - - if(_buffIsFull) - { - - mean = _sum / (double)_buffLen; - return 0; - } - else - { - if(_idx > 0) - { - mean = _sum / (double)_idx; - return 0; - } - else - { - return -1; - } - - } -} - -WebRtc_Word16 -CircularBuffer::Variance(double& var) -{ - assert(_buffLen > 0); - - if(_buffIsFull) - { - var = _sumSqr / (double)_buffLen; - return 0; - } - else - { - if(_idx > 0) - { - var = _sumSqr / (double)_idx; - return 0; - } - else - { - return -1; - } - } -} - - - -bool -FixedPayloadTypeCodec(const char* payloadName) -{ - char fixPayloadTypeCodecs[NUM_CODECS_WITH_FIXED_PAYLOAD_TYPE][32] = { - "PCMU", - "PCMA", - "GSM", - "G723", - "DVI4", - "LPC", - "PCMA", - "G722", - "QCELP", - "CN", - "MPA", - "G728", - "G729" - }; - - for(int n = 0; n < NUM_CODECS_WITH_FIXED_PAYLOAD_TYPE; n++) - { - if(!STR_CASE_CMP(payloadName, fixPayloadTypeCodecs[n])) - { - return true; - } - } - return false; -} - -DTMFDetector::DTMFDetector() -{ - for(WebRtc_Word16 n = 0; n < 1000; n++) - { - _toneCntr[n] = 0; - } -} - -DTMFDetector::~DTMFDetector() -{ -} - -WebRtc_Word32 DTMFDetector::IncomingDtmf(const WebRtc_UWord8 digitDtmf, const bool /* toneEnded */) -{ - fprintf(stdout, "%d-",digitDtmf); - _toneCntr[digitDtmf]++; - return 0; -} - -void DTMFDetector::PrintDetectedDigits() -{ - for(WebRtc_Word16 n = 0; n < 1000; n++) - { - if(_toneCntr[n] > 0) - { - fprintf(stdout, "%d %u msec, \n", n, _toneCntr[n]*10); - } - } - fprintf(stdout, "\n"); - return; -} - -void -VADCallback::Reset() -{ - for(int n = 0; n < 6; n++) - { - _numFrameTypes[n] = 0; - } -} - -VADCallback::VADCallback() -{ - for(int n = 0; n < 6; n++) - { - _numFrameTypes[n] = 0; - } -} - -void -VADCallback::PrintFrameTypes() -{ - fprintf(stdout, "No encoding.................. %d\n", _numFrameTypes[0]); - fprintf(stdout, "Active normal encoded........ %d\n", _numFrameTypes[1]); - fprintf(stdout, "Passive normal encoded....... %d\n", _numFrameTypes[2]); - fprintf(stdout, "Passive DTX wideband......... %d\n", _numFrameTypes[3]); - fprintf(stdout, "Passive DTX narrowband....... %d\n", _numFrameTypes[4]); - fprintf(stdout, "Passive DTX super-wideband... %d\n", _numFrameTypes[5]); -} - -WebRtc_Word32 -VADCallback::InFrameType( - WebRtc_Word16 frameType) -{ - _numFrameTypes[frameType]++; - return 0; -} diff --git a/modules/audio_coding/main/test/utility.h b/modules/audio_coding/main/test/utility.h deleted file mode 100644 index b25de4452..000000000 --- a/modules/audio_coding/main/test/utility.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef ACM_TEST_UTILITY_H -#define ACM_TEST_UTILITY_H - -#include "audio_coding_module.h" - -//----------------------------- -#define CHECK_ERROR(f) \ - do { \ - if(f < 0) { \ - char errString[500]; \ - sprintf(errString, "Error Calling API in file %s at line %d \n", \ - __FILE__, __LINE__); \ - throw errString; \ - } \ - }while(0) - -//----------------------------- -#define CHECK_PROTECTED(f) \ - do { \ - if(f >= 0) { \ - char errString[500]; \ - sprintf(errString, "Error Calling API in file %s at line %d \n", \ - __FILE__, __LINE__); \ - throw errString; \ - } \ - else { \ - printf("An expected error is caught.\n"); \ - } \ - }while(0) - -//---------------------------- -#define CHECK_ERROR_MT(f) \ - do { \ - if(f < 0) { \ - fprintf(stderr, "Error Calling API in file %s at line %d \n", \ - __FILE__, __LINE__); \ - } \ - }while(0) - -//---------------------------- -#define CHECK_PROTECTED_MT(f) \ - do { \ - if(f >= 0) { \ - fprintf(stderr, "Error Calling API in file %s at line %d \n", \ - __FILE__, __LINE__); \ - } \ - else { \ - printf("An expected error is caught.\n"); \ - } \ - }while(0) - - - -#ifdef WIN32 - /* Exclude rarely-used stuff from Windows headers */ - //#define WIN32_LEAN_AND_MEAN - /* OS-dependent case-insensitive string comparison */ - #define STR_CASE_CMP(x,y) ::_stricmp(x,y) -#else - /* OS-dependent case-insensitive string comparison */ - #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) -#endif - -#define DESTROY_ACM(acm) \ - do { \ - if(acm != NULL) { \ - AudioCodingModule::Destroy(acm); \ - acm = NULL; \ - } \ - } while(0) - - -#define DELETE_POINTER(p) \ - do { \ - if(p != NULL) { \ - delete p; \ - p = NULL; \ - } \ - } while(0) - -using namespace webrtc; - -class ACMTestTimer -{ -public: - ACMTestTimer(); - ~ACMTestTimer(); - - void Reset(); - void Tick10ms(); - void Tick1ms(); - void Tick100ms(); - void Tick1sec(); - void CurrentTimeHMS( - char* currTime); - void CurrentTime( - unsigned long& h, - unsigned char& m, - unsigned char& s, - unsigned short& ms); - -private: - void Adjust(); - - unsigned short _msec; - unsigned char _sec; - unsigned char _min; - unsigned long _hour; -}; - - - -class CircularBuffer -{ -public: - CircularBuffer(WebRtc_UWord32 len); - ~CircularBuffer(); - - void SetArithMean( - bool enable); - void SetVariance( - bool enable); - - void Update( - const double newVal); - void IsBufferFull(); - - WebRtc_Word16 Variance(double& var); - WebRtc_Word16 ArithMean(double& mean); - -protected: - double* _buff; - WebRtc_UWord32 _idx; - WebRtc_UWord32 _buffLen; - - bool _buffIsFull; - bool _calcAvg; - bool _calcVar; - double _sum; - double _sumSqr; -}; - - - - - -WebRtc_Word16 ChooseCodec( - CodecInst& codecInst); - -void PrintCodecs(); - -bool FixedPayloadTypeCodec(const char* payloadName); - - - - -class DTMFDetector: public AudioCodingFeedback -{ -public: - DTMFDetector(); - ~DTMFDetector(); - // used for inband DTMF detection - WebRtc_Word32 IncomingDtmf(const WebRtc_UWord8 digitDtmf, const bool toneEnded); - void PrintDetectedDigits(); - -private: - WebRtc_UWord32 _toneCntr[1000]; - -}; - - - - -class VADCallback : public ACMVADCallback -{ -public: - VADCallback(); - ~VADCallback(){} - - WebRtc_Word32 InFrameType( - WebRtc_Word16 frameType); - - void PrintFrameTypes(); - void Reset(); - -private: - WebRtc_UWord32 _numFrameTypes[6]; -}; - - - -#endif // ACM_TEST_UTILITY_H diff --git a/modules/audio_conference_mixer/OWNERS b/modules/audio_conference_mixer/OWNERS deleted file mode 100644 index 0df1f3e59..000000000 --- a/modules/audio_conference_mixer/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -hellner@google.com -pwestin@google.com -ajm@google.com \ No newline at end of file diff --git a/modules/audio_conference_mixer/interface/audio_conference_mixer.h b/modules/audio_conference_mixer/interface/audio_conference_mixer.h deleted file mode 100644 index 546468931..000000000 --- a/modules/audio_conference_mixer/interface/audio_conference_mixer.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_H_ - -#include "audio_conference_mixer_defines.h" -#include "module.h" -#include "module_common_types.h" - -namespace webrtc { -class AudioMixerOutputReceiver; -class AudioMixerStatusReceiver; -class MixerParticipant; -class Trace; - -class AudioConferenceMixer : public Module -{ -public: - enum {kMaximumAmountOfMixedParticipants = 16}; - enum Frequency - { - kNbInHz = 8000, - kWbInHz = 16000, - kSwbInHz = 32000, - kLowestPossible = -1, - kDefaultFrequency = kWbInHz - }; - - // Factory method. Constructor disabled. - static AudioConferenceMixer* CreateAudioConferenceMixer( - const WebRtc_Word32 id); - virtual ~AudioConferenceMixer() {} - - // Returns version of the module and its components - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - // Module functions - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - virtual WebRtc_Word32 TimeUntilNextProcess() = 0 ; - virtual WebRtc_Word32 Process() = 0; - - // Register/unregister a callback class for receiving the mixed audio. - virtual WebRtc_Word32 RegisterMixedStreamCallback( - AudioMixerOutputReceiver& receiver) = 0; - virtual WebRtc_Word32 UnRegisterMixedStreamCallback() = 0; - - // Register/unregister a callback class for receiving status information. - virtual WebRtc_Word32 RegisterMixerStatusCallback( - AudioMixerStatusReceiver& mixerStatusCallback, - const WebRtc_UWord32 amountOf10MsBetweenCallbacks) = 0; - virtual WebRtc_Word32 UnRegisterMixerStatusCallback() = 0; - - // Add/remove participants as candidates for mixing. - virtual WebRtc_Word32 SetMixabilityStatus( - MixerParticipant& participant, - const bool mixable) = 0; - // mixable is set to true if a participant is a candidate for mixing. - virtual WebRtc_Word32 MixabilityStatus( - MixerParticipant& participant, - bool& mixable) = 0; - // amountOfMixableParticipants is set to the number of participants that are - // eligible for mixing. - virtual WebRtc_Word32 AmountOfMixables( - WebRtc_UWord32& amountOfMixableParticipants) = 0; - - // Set the minimum sampling frequency at which to mix. The mixing algorithm - // may still choose to mix at a higher samling frequency to avoid - // downsampling of audio contributing to the mixed audio. - virtual WebRtc_Word32 SetMinimumMixingFrequency(Frequency freq) = 0; - -protected: - AudioConferenceMixer() {} -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_H_ diff --git a/modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h b/modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h deleted file mode 100644 index f7f228306..000000000 --- a/modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_DEFINES_H_ - -#include "map_wrapper.h" -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc { -class MixHistory; - -// A callback class that all mixer participants must inherit from/implement. -class MixerParticipant -{ -public: - // The implementation of this function should update audioFrame with new - // audio every time it's called. - virtual WebRtc_Word32 GetAudioFrame(const WebRtc_Word32 id, - AudioFrame& audioFrame) = 0; - - // mixed will be set to true if the participant was mixed this mix iteration - WebRtc_Word32 IsMixed(bool& mixed) const; - - // This function specifies the sampling frequency needed for the AudioFrame - // for future GetAudioFrame(..) calls. - virtual WebRtc_Word32 NeededFrequency(const WebRtc_Word32 id) = 0; - - MixHistory* _mixHistory; -protected: - MixerParticipant(); - virtual ~MixerParticipant(); -}; - -// Container struct for participant statistics. -struct ParticipantStatistics -{ - WebRtc_Word32 participant; - WebRtc_Word32 level; -}; - -class AudioMixerStatusReceiver -{ -public: - // Callback function that provides an array of ParticipantStatistics for the - // participants that were mixed last mix iteration. - virtual void MixedParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size) = 0; - // Callback function that provides an array of the ParticipantStatistics for - // the participants that had a positiv VAD last mix iteration. - virtual void VADPositiveParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size) = 0; - // Callback function that provides the audio level of the mixed audio frame - // from the last mix iteration. - virtual void MixedAudioLevel( - const WebRtc_Word32 id, - const WebRtc_UWord32 level) = 0; -protected: - AudioMixerStatusReceiver() {} - virtual ~AudioMixerStatusReceiver() {} -}; - -class AudioMixerOutputReceiver -{ -public: - // This callback function provides the mixed audio for this mix iteration. - // Note that uniqueAudioFrames is an array of AudioFrame pointers with the - // size according to the size parameter. - virtual void NewMixedAudio(const WebRtc_Word32 id, - const AudioFrame& generalAudioFrame, - const AudioFrame** uniqueAudioFrames, - const WebRtc_UWord32 size) = 0; -protected: - AudioMixerOutputReceiver() {} - virtual ~AudioMixerOutputReceiver() {} -}; - -class AudioRelayReceiver -{ -public: - // This callback function provides the mix decision for this mix iteration. - // mixerList is a list of elements of the type - // [int,MixerParticipant*] - virtual void NewAudioToRelay(const WebRtc_Word32 id, - const MapWrapper& mixerList) = 0; -protected: - AudioRelayReceiver() {} - virtual ~AudioRelayReceiver() {} -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_INTERFACE_AUDIO_CONFERENCE_MIXER_DEFINES_H_ diff --git a/modules/audio_conference_mixer/source/Android.mk b/modules/audio_conference_mixer/source/Android.mk deleted file mode 100644 index 0769ebb0a..000000000 --- a/modules/audio_conference_mixer/source/Android.mk +++ /dev/null @@ -1,50 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_conference_mixer -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := audio_frame_manipulator.cc \ - level_indicator.cc \ - audio_conference_mixer_impl.cc \ - time_scheduler.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := -# Duplicate the static libraries to fix circular references -LOCAL_STATIC_LIBRARIES += $(LOCAL_STATIC_LIBRARIES) - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_conference_mixer/source/audio_conference_mixer.gyp b/modules/audio_conference_mixer/source/audio_conference_mixer.gyp deleted file mode 100644 index 8b06908fe..000000000 --- a/modules/audio_conference_mixer/source/audio_conference_mixer.gyp +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'audio_conference_mixer', - 'type': '<(library)', - 'dependencies': [ - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../interface', - ], - }, - 'sources': [ - '../interface/audio_conference_mixer.h', - '../interface/audio_conference_mixer_defines.h', - 'audio_frame_manipulator.cc', - 'audio_frame_manipulator.h', - 'level_indicator.cc', - 'level_indicator.h', - 'memory_pool.h', - 'memory_pool_generic.h', - 'memory_pool_windows.h', - 'audio_conference_mixer_impl.cc', - 'audio_conference_mixer_impl.h', - 'time_scheduler.cc', - 'time_scheduler.h', - ], - 'conditions': [ - ['OS=="win"', { - 'sources!': [ - 'memory_pool_generic.h', - ], - }], - ['OS!="win"', { - 'sources!': [ - 'memory_pool_windows.h', - ], - }], - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc b/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc deleted file mode 100644 index d153e43e0..000000000 --- a/modules/audio_conference_mixer/source/audio_conference_mixer_impl.cc +++ /dev/null @@ -1,780 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_conference_mixer_defines.h" -#include "audio_conference_mixer_impl.h" -#include "audio_frame_manipulator.h" -#include "critical_section_wrapper.h" -#include "map_wrapper.h" -#include "trace.h" - -namespace webrtc { -MixerParticipant::MixerParticipant() - : _mixHistory(new MixHistory()) -{ -} - -MixerParticipant::~MixerParticipant() -{ - delete _mixHistory; -} - -WebRtc_Word32 MixerParticipant::IsMixed(bool& mixed) const -{ - return _mixHistory->IsMixed(mixed); -} - -MixHistory::MixHistory() - : _isMixed(0) -{ -} - -MixHistory::~MixHistory() -{ -} - -WebRtc_Word32 MixHistory::IsMixed(bool& mixed) const -{ - mixed = (_isMixed.Value() == 1); - return 0; -} - -WebRtc_Word32 MixHistory::SetIsMixed(const bool mixed) -{ - _isMixed = mixed ? 1 : 0; - return 0; -} - -void MixHistory::ResetMixedStatus() -{ - _isMixed = 0; -} - -AudioConferenceMixer* AudioConferenceMixer::CreateAudioConferenceMixer( - const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, id, - "CreateAudioConferenceMixer"); - return new AudioConferenceMixerImpl(id); -} - -AudioConferenceMixerImpl::AudioConferenceMixerImpl(const WebRtc_Word32 id) - : _scratchParticipantsToMixAmount(0), - _scratchMixedParticipants(), - _scratchVadPositiveParticipantsAmount(0), - _scratchVadPositiveParticipants(), - _crit(CriticalSectionWrapper::CreateCriticalSection()), - _cbCrit(CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _minimumMixingFreq(kLowestPossible), - _mixReceiver(NULL), - _mixerStatusCallback(NULL), - _amountOf10MsBetweenCallbacks(1), - _amountOf10MsUntilNextCallback(0), - _mixerStatusCb(false), - _outputFrequency(kDefaultFrequency), - _sampleSize((_outputFrequency*kProcessPeriodicityInMs)/1000), - _participantList(), - _amountOfMixableParticipants(0), - _timeStamp(0), - _timeScheduler(kProcessPeriodicityInMs), - _mixedAudioLevel(), - _processCalls(0) -{ - MemoryPool::CreateMemoryPool(_audioFramePool, - DEFAULT_AUDIO_FRAME_POOLSIZE); - WEBRTC_TRACE(kTraceMemory, kTraceAudioMixerServer, _id, "%s created", - __FUNCTION__); -} - -AudioConferenceMixerImpl::~AudioConferenceMixerImpl() -{ - delete _crit; - delete _cbCrit; - - MemoryPool::DeleteMemoryPool(_audioFramePool); - assert(_audioFramePool==NULL); - WEBRTC_TRACE(kTraceMemory, kTraceAudioMixerServer, _id, "%s deleted", - __FUNCTION__); -} - -WebRtc_Word32 AudioConferenceMixerImpl::Version( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceAudioMixerServer, - _id, - "Version(version%s,remainingBufferLengthInBytes:%d,position:%d)", - (version==NULL) ? ":NULL" : "", - remainingBufferInBytes,position); - return GetVersion(version,remainingBufferInBytes,position); -} - -WebRtc_Word32 AudioConferenceMixerImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "ChangeUniqueId(new id:%d)", id); - _id = id; - return 0; -} - -WebRtc_Word32 AudioConferenceMixer::GetVersion( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceAudioMixerServer, - -1, - "Version(version%s,remainingBufferLengthInBytes:%d,position:%d)", - (version==NULL) ? "NULL" : "", - remainingBufferInBytes, - position); - if(version == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, -1, - "Invalid in argument to GetVersion()"); - return -1; - } - const char moduleVersion[] = VERSION_STRING; - if(remainingBufferInBytes < sizeof(moduleVersion)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, -1, - "Too small buffer in GetVersion()"); - return -1; - } - strncpy(version, moduleVersion, sizeof(moduleVersion)); // null termination - remainingBufferInBytes -= sizeof(moduleVersion); - position += sizeof(moduleVersion); - return 0; -} - -// Process should be called every kProcessPeriodicityInMs ms -WebRtc_Word32 AudioConferenceMixerImpl::TimeUntilNextProcess() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "TimeUntilNextProcess()"); - WebRtc_Word32 timeUntilNextProcess = 0; - CriticalSectionScoped cs(*_crit); - if(_timeScheduler.TimeToNextUpdate(timeUntilNextProcess) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, _id, - "failed in TimeToNextUpdate() call"); - // Sanity check - assert(false); - return -1; - } - return timeUntilNextProcess; -} - -WebRtc_Word32 AudioConferenceMixerImpl::Process() -{ - { - CriticalSectionScoped cs(*_crit); - assert(_processCalls == 0); - _processCalls++; - - // Let the scheduler know that we are running one iteration. - _timeScheduler.UpdateScheduler(); - } - - ListWrapper mixList; - MapWrapper mixedParticipantsMap; - { - CriticalSectionScoped cs(*_cbCrit); - - WebRtc_Word32 lowFreq = GetLowestMixingFrequency(); - // SILK can run in 12 kHz and 24 kHz. These frequencies are not - // supported so use closet higher frequency to not lose any information. - // TODO (hellner): this is probably more appropriate to do in - // GetLowestMixingFrequency(). - if (lowFreq == 12000) - { - lowFreq = 16000; - } else if (lowFreq == 24000) { - lowFreq = 32000; - } - if(lowFreq <= 0) - { - CriticalSectionScoped cs(*_crit); - _processCalls--; - return 0; - } else { - switch(lowFreq) - { - case 8000: - if(OutputFrequency() != kNbInHz) - { - SetOutputFrequency(kNbInHz); - } - break; - case 16000: - if(OutputFrequency() != kWbInHz) - { - SetOutputFrequency(kWbInHz); - } - break; - case 32000: - if(OutputFrequency() != kSwbInHz) - { - SetOutputFrequency(kSwbInHz); - } - break; - default: - assert(false); - - CriticalSectionScoped cs(*_crit); - _processCalls--; - return -1; - } - } - - UpdateToMix(mixList,mixedParticipantsMap); - UpdateMixedStatus(mixedParticipantsMap); - _scratchParticipantsToMixAmount = mixedParticipantsMap.Size(); - } - - // Clear mixedParticipantsMap to avoid memory leak warning. - // Please note that the mixedParticipantsMap doesn't own any dynamically - // allocated memory. - while(mixedParticipantsMap.Erase(mixedParticipantsMap.First()) == 0); - - // Get an AudioFrame for mixing from the memory pool. - AudioFrame* mixedAudio = NULL; - if(_audioFramePool->PopMemory(mixedAudio) == -1) - { - WEBRTC_TRACE(kTraceMemory, kTraceAudioMixerServer, _id, - "failed PopMemory() call"); - assert(false); - return -1; - } - - bool timeForMixerCallback = false; - WebRtc_Word32 audioLevel = 0; - { - const ListItem* firstItem = mixList.First(); - // Assume mono. - WebRtc_UWord8 numberOfChannels = 1; - if(firstItem != NULL) - { - // Use the same number of channels as the first frame to be mixed. - numberOfChannels = static_cast( - firstItem->GetItem())->_audioChannel; - } - // TODO (hellner): it might be better to decide the number of channels - // with an API instead of dynamically. - - - CriticalSectionScoped cs(*_crit); - - mixedAudio->UpdateFrame(-1, _timeStamp, NULL, 0, _outputFrequency, - AudioFrame::kNormalSpeech, - AudioFrame::kVadPassive, numberOfChannels); - - _timeStamp += _sampleSize; - - MixFromList(*mixedAudio,mixList); - - if(mixedAudio->_payloadDataLengthInSamples == 0) - { - // Nothing was mixed set the audio samples to silence. - memset(mixedAudio->_payloadData, 0, _sampleSize); - mixedAudio->_payloadDataLengthInSamples = _sampleSize; - } - - _mixedAudioLevel.ComputeLevel(mixedAudio->_payloadData,_sampleSize); - audioLevel = _mixedAudioLevel.GetLevel(); - - if(_mixerStatusCb) - { - _scratchVadPositiveParticipantsAmount = 0; - UpdateVADPositiveParticipants(mixList); - if(_amountOf10MsUntilNextCallback-- == 0) - { - _amountOf10MsUntilNextCallback = _amountOf10MsBetweenCallbacks; - timeForMixerCallback = true; - } - } - } - - { - CriticalSectionScoped cs(*_cbCrit); - if(_mixReceiver != NULL) - { - const AudioFrame** dummy = NULL; - _mixReceiver->NewMixedAudio( - _id, - *mixedAudio, - dummy, - 0); - } - - if((_mixerStatusCallback != NULL) && - timeForMixerCallback) - { - _mixerStatusCallback->MixedParticipants( - _id, - _scratchMixedParticipants, - _scratchParticipantsToMixAmount); - - _mixerStatusCallback->VADPositiveParticipants( - _id, - _scratchVadPositiveParticipants, - _scratchVadPositiveParticipantsAmount); - _mixerStatusCallback->MixedAudioLevel(_id,audioLevel); - } - } - - // Reclaim all outstanding memory. - _audioFramePool->PushMemory(mixedAudio); - ClearAudioFrameList(mixList); - { - CriticalSectionScoped cs(*_crit); - _processCalls--; - } - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::RegisterMixedStreamCallback( - AudioMixerOutputReceiver& mixReceiver) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "RegisterMixedStreamCallback(mixReceiver)"); - CriticalSectionScoped cs(*_cbCrit); - if(_mixReceiver != NULL) - { - return -1; - } - _mixReceiver = &mixReceiver; - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::UnRegisterMixedStreamCallback() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "UnRegisterMixedStreamCallback()"); - CriticalSectionScoped cs(*_cbCrit); - if(_mixReceiver == NULL) - { - return -1; - } - _mixReceiver = NULL; - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::SetOutputFrequency( - const Frequency frequency) -{ - CriticalSectionScoped cs(*_crit); - _outputFrequency = frequency; - _sampleSize = (_outputFrequency*kProcessPeriodicityInMs) / 1000; - return 0; -} - -AudioConferenceMixer::Frequency -AudioConferenceMixerImpl::OutputFrequency() const -{ - CriticalSectionScoped cs(*_crit); - return _outputFrequency; -} - -WebRtc_Word32 AudioConferenceMixerImpl::RegisterMixerStatusCallback( - AudioMixerStatusReceiver& mixerStatusCallback, - const WebRtc_UWord32 amountOf10MsBetweenCallbacks) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceAudioMixerServer, - _id, - "RegisterMixerStatusCallback(mixerStatusCallback,%d)", - amountOf10MsBetweenCallbacks); - if(amountOf10MsBetweenCallbacks == 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceAudioMixerServer, - _id, - "amountOf10MsBetweenCallbacks(%d) needs to be larger than 0"); - return -1; - } - { - CriticalSectionScoped cs(*_cbCrit); - if(_mixerStatusCallback != NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, _id, - "Mixer status callback already registered"); - return -1; - } - _mixerStatusCallback = &mixerStatusCallback; - } - { - CriticalSectionScoped cs(*_crit); - _amountOf10MsBetweenCallbacks = amountOf10MsBetweenCallbacks; - _amountOf10MsUntilNextCallback = 0; - _mixerStatusCb = true; - } - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::UnRegisterMixerStatusCallback() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "UnRegisterMixerStatusCallback()"); - { - CriticalSectionScoped cs(*_crit); - if(!_mixerStatusCb) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, _id, - "Mixer status callback not registered"); - return -1; - } - _mixerStatusCb = false; - } - { - CriticalSectionScoped cs(*_cbCrit); - _mixerStatusCallback = NULL; - } - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::SetMixabilityStatus( - MixerParticipant& participant, - const bool mixable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "SetMixabilityStatus(participant,mixable:%s)", - mixable ? "true" : "false"); - WebRtc_UWord32 amountOfMixableParticipants; - { - CriticalSectionScoped cs(*_cbCrit); - const bool isMixed = - IsParticipantInList(participant,_participantList); - // API must be called with a new state. - if(!(mixable ^ isMixed)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, _id, - "Mixable is aready %s", - isMixed ? "ON" : "off"); - return -1; - } - bool success = false; - if(mixable) - { - if(_amountOfMixableParticipants >= - kMaximumAmountOfMixedParticipants) - { - return -1; - } - success = AddParticipantToList(participant,_participantList); - } - else - { - success = RemoveParticipantFromList(participant,_participantList); - } - if(!success) - { - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, _id, - "failed to %s participant", - mixable ? "add" : "remove"); - assert(false); - return -1; - } - amountOfMixableParticipants = _participantList.GetSize(); - } - // A MixerParticipant was added or removed. Make sure the scratch - // buffer is updated if necessary. - // Note: The scratch buffer may only be updated in Process(). - CriticalSectionScoped cs(*_crit); - _amountOfMixableParticipants = amountOfMixableParticipants; - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::MixabilityStatus( - MixerParticipant& participant, - bool& mixable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "MixabilityStatus(participant,mixable)"); - CriticalSectionScoped cs(*_cbCrit); - mixable = IsParticipantInList(participant,_participantList); - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::AmountOfMixables( - WebRtc_UWord32& amountOfMixableParticipants) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioMixerServer, _id, - "AmountOfMixables(amountOfMixableParticipants)"); - CriticalSectionScoped cs(*_crit); - amountOfMixableParticipants = _amountOfMixableParticipants; - return 0; -} - -WebRtc_Word32 AudioConferenceMixerImpl::SetMinimumMixingFrequency( - Frequency freq) -{ - // Make sure that only allowed sampling frequencies are used. Use closest - // higher sampling frequency to avoid losing information. - if (static_cast(freq) == 12000) - { - freq = kWbInHz; - } else if (static_cast(freq) == 24000) { - freq = kSwbInHz; - } - - if((freq == kNbInHz) || (freq == kWbInHz) || (freq == kSwbInHz) || - (freq == kLowestPossible)) - { - _minimumMixingFreq=freq; - return 0; - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioMixerServer, _id, - "SetMinimumMixingFrequency incorrect frequency: %i",freq); - assert(false); - return -1; - } -} - -// Check all AudioFrames that are to be mixed. The highest sampling frequency -// found is the lowest that can be used without losing information. -WebRtc_Word32 AudioConferenceMixerImpl::GetLowestMixingFrequency() -{ - WebRtc_Word32 highestFreq = 8000; - ListItem* item = _participantList.First(); - while(item) - { - MixerParticipant* participant = - static_cast(item->GetItem()); - const WebRtc_Word32 neededFrequency = participant->NeededFrequency(_id); - if(neededFrequency > highestFreq) - { - highestFreq = neededFrequency; - } - item = _participantList.Next(item); - } - - // Check if the user specified a lowest mixing frequency. - if(_minimumMixingFreq != kLowestPossible) - { - if(_minimumMixingFreq > highestFreq) - { - return _minimumMixingFreq; - } - } - return highestFreq; -} - -void AudioConferenceMixerImpl::UpdateToMix( - ListWrapper& mixList, - MapWrapper& mixParticipantList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "UpdateToMix(mixList,mixParticipantList)"); - - ListItem* item = _participantList.First(); - while(item) - { - MixerParticipant* participant = static_cast( - item->GetItem()); - AudioFrame* audioFrame = NULL; - if(_audioFramePool->PopMemory(audioFrame) == -1) - { - WEBRTC_TRACE(kTraceMemory, kTraceAudioMixerServer, _id, - "failed PopMemory() call"); - assert(false); - return; - } - audioFrame->_frequencyInHz = _outputFrequency; - - if(participant->GetAudioFrame(_id,*audioFrame) != 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioMixerServer, _id, - "failed to GetAudioFrame() from participant"); - _audioFramePool->PushMemory(audioFrame); - item = _participantList.Next(item); - continue; - } - assert(audioFrame->_vadActivity != AudioFrame::kVadUnknown); - mixList.PushBack(static_cast(audioFrame)); - mixParticipantList.Insert(audioFrame->_id,static_cast( - participant)); - assert(mixParticipantList.Size() <= kMaximumAmountOfMixedParticipants); - item = _participantList.Next(item); - } -} - -void AudioConferenceMixerImpl::UpdateMixedStatus( - MapWrapper& mixedParticipantsMap) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "UpdateMixedStatus(mixedParticipantsMap)"); - assert(mixedParticipantsMap.Size() <= kMaximumAmountOfMixedParticipants); - - // Loop through all non-VIP participants. If they are in the mix map they - // were mixed. - ListItem* participantItem = _participantList.First(); - while(participantItem != NULL) - { - bool isMixed = false; - MixerParticipant* participant = - static_cast(participantItem->GetItem()); - - MapItem* mixedItem = mixedParticipantsMap.First(); - while(mixedItem) - { - if(participant == mixedItem->GetItem()) - { - isMixed = true; - break; - } - mixedItem = mixedParticipantsMap.Next(mixedItem); - } - participant->_mixHistory->SetIsMixed(isMixed); - participantItem = _participantList.Next(participantItem); - } -} - -void AudioConferenceMixerImpl::ClearAudioFrameList(ListWrapper& audioFrameList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "ClearAudioFrameList(audioFrameList)"); - ListItem* item = audioFrameList.First(); - while(item) - { - AudioFrame* audioFrame = static_cast(item->GetItem()); - _audioFramePool->PushMemory(audioFrame); - audioFrameList.Erase(item); - item = audioFrameList.First(); - } -} - -void AudioConferenceMixerImpl::UpdateVADPositiveParticipants( - ListWrapper& mixList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "UpdateVADPositiveParticipants(mixList)"); - - ListItem* item = mixList.First(); - while(item != NULL) - { - AudioFrame* audioFrame = static_cast(item->GetItem()); - CalculateEnergy(*audioFrame); - if(audioFrame->_vadActivity == AudioFrame::kVadActive) - { - _scratchVadPositiveParticipants[ - _scratchVadPositiveParticipantsAmount].participant = - audioFrame->_id; - _scratchVadPositiveParticipants[ - _scratchVadPositiveParticipantsAmount].level = - audioFrame->_volume; - _scratchVadPositiveParticipantsAmount++; - } - item = mixList.Next(item); - } -} - -bool AudioConferenceMixerImpl::IsParticipantInList( - MixerParticipant& participant, - ListWrapper& participantList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "IsParticipantInList(participant,participantList)"); - ListItem* item = participantList.First(); - while(item != NULL) - { - MixerParticipant* rhsParticipant = - static_cast(item->GetItem()); - if(&participant == rhsParticipant) - { - return true; - } - item = participantList.Next(item); - } - return false; -} - -bool AudioConferenceMixerImpl::AddParticipantToList( - MixerParticipant& participant, - ListWrapper& participantList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "AddParticipantToList(participant, participantList)"); - if(participantList.PushBack(static_cast(&participant)) == -1) - { - return false; - } - // Make sure that the mixed status is correct for new MixerParticipant. - participant._mixHistory->ResetMixedStatus(); - return true; -} - -bool AudioConferenceMixerImpl::RemoveParticipantFromList( - MixerParticipant& participant, - ListWrapper& participantList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "RemoveParticipantFromList(participant, participantList)"); - ListItem* item = participantList.First(); - while(item) - { - if(item->GetItem() == &participant) - { - participantList.Erase(item); - // Participant is no longer mixed, reset to default. - participant._mixHistory->ResetMixedStatus(); - return true; - } - item = participantList.Next(item); - } - return false; -} - -WebRtc_Word32 AudioConferenceMixerImpl::MixFromList(AudioFrame& mixedAudioFrame, - ListWrapper& audioFrameList) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioMixerServer, _id, - "MixFromList(mixedAudioFrame, audioFrameList)"); - WebRtc_UWord32 position = 0; - ListItem* item = audioFrameList.First(); - while(item != NULL) - { - if(position >= kMaximumAmountOfMixedParticipants) - { - WEBRTC_TRACE( - kTraceMemory, - kTraceAudioMixerServer, - _id, - "Trying to mix more than max amount of mixed participants:%d!", - kMaximumAmountOfMixedParticipants); - // Assert and avoid crash - assert(false); - position = 0; - } - AudioFrame* audioFrame = static_cast(item->GetItem()); - - // Divide the AudioFrame samples by 2 to avoid saturation. - *audioFrame >>= 1; - mixedAudioFrame += *audioFrame; - - _scratchMixedParticipants[position].participant = audioFrame->_id; - _scratchMixedParticipants[position].level = audioFrame->_volume; - - position++; - item = audioFrameList.Next(item); - } - return 0; -} -} // namespace webrtc diff --git a/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h b/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h deleted file mode 100644 index 7d29efca7..000000000 --- a/modules/audio_conference_mixer/source/audio_conference_mixer_impl.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_CONFERENCE_MIXER_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_CONFERENCE_MIXER_IMPL_H_ - -#include "atomic32_wrapper.h" -#include "audio_conference_mixer.h" -#include "engine_configurations.h" -#include "level_indicator.h" -#include "list_wrapper.h" -#include "memory_pool.h" -#include "module_common_types.h" -#include "time_scheduler.h" - -#define VERSION_STRING "Audio Conference Mixer Module 1.1.0" - -namespace webrtc { -class CriticalSectionWrapper; - -// Cheshire cat implementation of MixerParticipant's non virtual functions. -class MixHistory -{ -public: - MixHistory(); - ~MixHistory(); - - // MixerParticipant function - WebRtc_Word32 IsMixed(bool& mixed) const; - - // Updates the mixed status. - WebRtc_Word32 SetIsMixed(const bool mixed); - - void ResetMixedStatus(); -private: - Atomic32Wrapper _isMixed; // 0 = false, 1 = true -}; - -class AudioConferenceMixerImpl : public AudioConferenceMixer -{ -public: - enum {kProcessPeriodicityInMs = 10}; - - AudioConferenceMixerImpl(const WebRtc_Word32 id); - ~AudioConferenceMixerImpl(); - - // Module functions - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - virtual WebRtc_Word32 TimeUntilNextProcess(); - virtual WebRtc_Word32 Process(); - - // AudioConferenceMixer functions - virtual WebRtc_Word32 RegisterMixedStreamCallback( - AudioMixerOutputReceiver& mixReceiver); - virtual WebRtc_Word32 UnRegisterMixedStreamCallback(); - virtual WebRtc_Word32 RegisterMixerStatusCallback( - AudioMixerStatusReceiver& mixerStatusCallback, - const WebRtc_UWord32 amountOf10MsBetweenCallbacks); - virtual WebRtc_Word32 UnRegisterMixerStatusCallback(); - virtual WebRtc_Word32 SetMixabilityStatus(MixerParticipant& participant, - const bool mixable); - virtual WebRtc_Word32 MixabilityStatus(MixerParticipant& participant, - bool& mixable); - virtual WebRtc_Word32 SetMinimumMixingFrequency(Frequency freq); - virtual WebRtc_Word32 AmountOfMixables( - WebRtc_UWord32& amountOfMixableParticipants); -private: - enum{DEFAULT_AUDIO_FRAME_POOLSIZE = 50}; - - // Set/get mix frequency - WebRtc_Word32 SetOutputFrequency(const Frequency frequency); - Frequency OutputFrequency() const; - - // Fill mixList with the AudioFrames pointers that should be used when - // mixing. Fill mixParticipantList with ParticipantStatistics for the - // participants who's AudioFrames are inside mixList. - void UpdateToMix(ListWrapper& mixList, MapWrapper& mixParticipantList); - - // Return the lowest mixing frequency that can be used without having to - // downsample any audio. - WebRtc_Word32 GetLowestMixingFrequency(); - - // Update the MixHistory of all MixerParticipants. mixedParticipantsList - // should contain a map of MixerParticipants that have been mixed. - void UpdateMixedStatus(MapWrapper& mixedParticipantsList); - - // Clears audioFrameList and reclaims all memory associated with it. - void ClearAudioFrameList(ListWrapper& audioFrameList); - - // Update the list of MixerParticipants who have a positive VAD. mixList - // should be a list of AudioFrames - void UpdateVADPositiveParticipants( - ListWrapper& mixList); - - // This function returns true if it finds the MixerParticipant in the - // specified list of MixerParticipants. - bool IsParticipantInList( - MixerParticipant& participant, - ListWrapper& participantList); - - // Add/remove the MixerParticipant to the specified - // MixerParticipant list. - bool AddParticipantToList( - MixerParticipant& participant, - ListWrapper& participantList); - bool RemoveParticipantFromList( - MixerParticipant& removeParticipant, - ListWrapper& participantList); - - // Mix the AudioFrames stored in audioFrameList into mixedAudioFrame. - WebRtc_Word32 MixFromList( - AudioFrame& mixedAudioFrame, - ListWrapper& audioFrameList); - - // Scratch memory - // Note that the scratch memory may only be touched in the scope of - // Process(). - WebRtc_UWord32 _scratchParticipantsToMixAmount; - ParticipantStatistics _scratchMixedParticipants[ - kMaximumAmountOfMixedParticipants]; - WebRtc_UWord32 _scratchVadPositiveParticipantsAmount; - ParticipantStatistics _scratchVadPositiveParticipants[ - kMaximumAmountOfMixedParticipants]; - - CriticalSectionWrapper* _crit; - CriticalSectionWrapper* _cbCrit; - - WebRtc_Word32 _id; - - Frequency _minimumMixingFreq; - - // Mix result callback - AudioMixerOutputReceiver* _mixReceiver; - - AudioMixerStatusReceiver* _mixerStatusCallback; - WebRtc_UWord32 _amountOf10MsBetweenCallbacks; - WebRtc_UWord32 _amountOf10MsUntilNextCallback; - bool _mixerStatusCb; - - // The current sample frequency and sample size when mixing. - Frequency _outputFrequency; - WebRtc_UWord16 _sampleSize; - - // Memory pool to avoid allocating/deallocating AudioFrames - MemoryPool* _audioFramePool; - - // List of all participants. Note all lists are disjunct - ListWrapper _participantList; // May be mixed. - - WebRtc_UWord32 _amountOfMixableParticipants; - - WebRtc_UWord32 _timeStamp; - - // Metronome class. - TimeScheduler _timeScheduler; - - // Smooth level indicator. - LevelIndicator _mixedAudioLevel; - - // Counter keeping track of concurrent calls to process. - // Note: should never be higher than 1 or lower than 0. - WebRtc_Word16 _processCalls; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_CONFERENCE_MIXER_IMPL_H_ diff --git a/modules/audio_conference_mixer/source/audio_frame_manipulator.cc b/modules/audio_conference_mixer/source/audio_frame_manipulator.cc deleted file mode 100644 index 8c1d9650c..000000000 --- a/modules/audio_conference_mixer/source/audio_frame_manipulator.cc +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_frame_manipulator.h" -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc { -void CalculateEnergy(AudioFrame& audioFrame) -{ - if(audioFrame._energy != 0xffffffff) - { - return; - } - audioFrame._energy = 0; - for(int position = 0; position < audioFrame._payloadDataLengthInSamples; - position++) - { - audioFrame._energy += audioFrame._payloadData[position] * - audioFrame._payloadData[position]; - } -} -} // namespace webrtc diff --git a/modules/audio_conference_mixer/source/audio_frame_manipulator.h b/modules/audio_conference_mixer/source/audio_frame_manipulator.h deleted file mode 100644 index 5364e170d..000000000 --- a/modules/audio_conference_mixer/source/audio_frame_manipulator.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_FRAME_MANIPULATOR_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_FRAME_MANIPULATOR_H_ - -namespace webrtc { -class AudioFrame; -// Updates the audioFrame's energy (based on its samples). -void CalculateEnergy(AudioFrame& audioFrame); -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_AUDIO_FRAME_MANIPULATOR_H_ diff --git a/modules/audio_conference_mixer/source/level_indicator.cc b/modules/audio_conference_mixer/source/level_indicator.cc deleted file mode 100644 index 9e9133eda..000000000 --- a/modules/audio_conference_mixer/source/level_indicator.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "level_indicator.h" - -namespace webrtc { -// Array for adding smothing to level changes (ad-hoc). -WebRtc_UWord32 perm[] = - {0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9}; - -LevelIndicator::LevelIndicator() - : _max(0), - _count(0), - _currentLevel(0) -{ -} - -LevelIndicator::~LevelIndicator() -{ -} - -// Level is based on the highest absolute value for all samples. -void LevelIndicator::ComputeLevel(const WebRtc_Word16* speech, - const WebRtc_UWord16 nrOfSamples) -{ - WebRtc_Word32 min = 0; - for(WebRtc_UWord32 i = 0; i < nrOfSamples; i++) - { - if(_max < speech[i]) - { - _max = speech[i]; - } - if(min > speech[i]) - { - min = speech[i]; - } - } - - // Absolute max value. - if(-min > _max) - { - _max = -min; - } - - if(_count == TICKS_BEFORE_CALCULATION) - { - // Highest sample value maps directly to a level. - WebRtc_Word32 position = _max / 1000; - if ((position == 0) && - (_max > 250)) - { - position = 1; - } - _currentLevel = perm[position]; - // The max value is decayed and stored so that it can be reused to slow - // down decreases in level. - _max = _max >> 1; - _count = 0; - } else { - _count++; - } -} - -WebRtc_Word32 LevelIndicator::GetLevel() -{ - return _currentLevel; -} - -} // namespace webrtc diff --git a/modules/audio_conference_mixer/source/level_indicator.h b/modules/audio_conference_mixer/source/level_indicator.h deleted file mode 100644 index bdcdf8eb7..000000000 --- a/modules/audio_conference_mixer/source/level_indicator.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_LEVEL_INDICATOR_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_LEVEL_INDICATOR_H_ - -#include "typedefs.h" - -namespace webrtc { -class LevelIndicator -{ -public: - enum{TICKS_BEFORE_CALCULATION = 10}; - - LevelIndicator(); - ~LevelIndicator(); - - // Updates the level. - void ComputeLevel(const WebRtc_Word16* speech, - const WebRtc_UWord16 nrOfSamples); - - WebRtc_Word32 GetLevel(); -private: - WebRtc_Word32 _max; - WebRtc_UWord32 _count; - WebRtc_UWord32 _currentLevel; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_LEVEL_INDICATOR_H_ diff --git a/modules/audio_conference_mixer/source/memory_pool.h b/modules/audio_conference_mixer/source/memory_pool.h deleted file mode 100644 index c44a9d359..000000000 --- a/modules/audio_conference_mixer/source/memory_pool.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_H_ - -#include - -#include "typedefs.h" - -#if _WIN32 - #include "memory_pool_windows.h" -#else - #include "memory_pool_generic.h" -#endif - -namespace webrtc { - -template -class MemoryPool -{ -public: - // Factory method, constructor disabled. - static WebRtc_Word32 CreateMemoryPool(MemoryPool*& memoryPool, - WebRtc_UWord32 initialPoolSize); - - // Try to delete the memory pool. Fail with return value -1 if there is - // outstanding memory. - static WebRtc_Word32 DeleteMemoryPool( - MemoryPool*& memoryPool); - - // Get/return unused memory. - WebRtc_Word32 PopMemory(MemoryType*& memory); - WebRtc_Word32 PushMemory(MemoryType*& memory); -private: - MemoryPool(WebRtc_Word32 initialPoolSize); - ~MemoryPool(); - - MemoryPoolImpl* _ptrImpl; -}; - -template -MemoryPool::MemoryPool(WebRtc_Word32 initialPoolSize) -{ - _ptrImpl = new MemoryPoolImpl(initialPoolSize); -} - -template -MemoryPool::~MemoryPool() -{ - delete _ptrImpl; -} - -template WebRtc_Word32 -MemoryPool::CreateMemoryPool(MemoryPool*& memoryPool, - WebRtc_UWord32 initialPoolSize) -{ - memoryPool = new MemoryPool(initialPoolSize); - if(memoryPool == NULL) - { - return -1; - } - if(memoryPool->_ptrImpl == NULL) - { - delete memoryPool; - memoryPool = NULL; - return -1; - } - if(!memoryPool->_ptrImpl->Initialize()) - { - delete memoryPool; - memoryPool = NULL; - return -1; - } - return 0; -} - -template -WebRtc_Word32 MemoryPool::DeleteMemoryPool(MemoryPool*& memoryPool) -{ - if(memoryPool == NULL) - { - return -1; - } - if(memoryPool->_ptrImpl == NULL) - { - return -1; - } - if(memoryPool->_ptrImpl->Terminate() == -1) - { - return -1; - } - delete memoryPool; - memoryPool = NULL; - return 0; -} - -template -WebRtc_Word32 MemoryPool::PopMemory(MemoryType*& memory) -{ - return _ptrImpl->PopMemory(memory); -} - -template -WebRtc_Word32 MemoryPool::PushMemory(MemoryType*& memory) -{ - if(memory == NULL) - { - return -1; - } - return _ptrImpl->PushMemory(memory); -} -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_H_ diff --git a/modules/audio_conference_mixer/source/memory_pool_generic.h b/modules/audio_conference_mixer/source/memory_pool_generic.h deleted file mode 100644 index 56eabbdaf..000000000 --- a/modules/audio_conference_mixer/source/memory_pool_generic.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_GENERIC_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_GENERIC_H_ - -#include - -#include "critical_section_wrapper.h" -#include "list_wrapper.h" -#include "typedefs.h" - -namespace webrtc { -template -class MemoryPoolImpl -{ -public: - // MemoryPool functions. - WebRtc_Word32 PopMemory(MemoryType*& memory); - WebRtc_Word32 PushMemory(MemoryType*& memory); - - MemoryPoolImpl(WebRtc_Word32 initialPoolSize); - ~MemoryPoolImpl(); - - // Atomic functions - WebRtc_Word32 Terminate(); - bool Initialize(); -private: - // Non-atomic function. - WebRtc_Word32 CreateMemory(WebRtc_UWord32 amountToCreate); - - CriticalSectionWrapper* _crit; - - bool _terminate; - - ListWrapper _memoryPool; - - WebRtc_UWord32 _initialPoolSize; - WebRtc_UWord32 _createdMemory; - WebRtc_UWord32 _outstandingMemory; -}; - -template -MemoryPoolImpl::MemoryPoolImpl(WebRtc_Word32 initialPoolSize) - : _crit(CriticalSectionWrapper::CreateCriticalSection()), - _terminate(false), - _memoryPool(), - _initialPoolSize(initialPoolSize), - _createdMemory(0), - _outstandingMemory(0) -{ -} - -template -MemoryPoolImpl::~MemoryPoolImpl() -{ - // Trigger assert if there is outstanding memory. - assert(_createdMemory == 0); - assert(_outstandingMemory == 0); - delete _crit; -} - -template -WebRtc_Word32 MemoryPoolImpl::PopMemory(MemoryType*& memory) -{ - CriticalSectionScoped cs(*_crit); - if(_terminate) - { - memory = NULL; - return -1; - } - ListItem* item = _memoryPool.First(); - if(item == NULL) - { - // _memoryPool empty create new memory. - CreateMemory(_initialPoolSize); - item = _memoryPool.First(); - if(item == NULL) - { - memory = NULL; - return -1; - } - } - memory = static_cast(item->GetItem()); - _memoryPool.Erase(item); - _outstandingMemory++; - return 0; -} - -template -WebRtc_Word32 MemoryPoolImpl::PushMemory(MemoryType*& memory) -{ - if(memory == NULL) - { - return -1; - } - CriticalSectionScoped cs(*_crit); - _outstandingMemory--; - if(_memoryPool.GetSize() > (_initialPoolSize << 1)) - { - // Reclaim memory if less than half of the pool is unused. - _createdMemory--; - delete memory; - memory = NULL; - return 0; - } - _memoryPool.PushBack(static_cast(memory)); - memory = NULL; - return 0; -} - -template -bool MemoryPoolImpl::Initialize() -{ - CriticalSectionScoped cs(*_crit); - return CreateMemory(_initialPoolSize) == 0; -} - -template -WebRtc_Word32 MemoryPoolImpl::Terminate() -{ - CriticalSectionScoped cs(*_crit); - assert(_createdMemory == _outstandingMemory + _memoryPool.GetSize()); - - _terminate = true; - // Reclaim all memory. - while(_createdMemory > 0) - { - ListItem* item = _memoryPool.First(); - if(item == NULL) - { - // There is memory that hasn't been returned yet. - return -1; - } - MemoryType* memory = static_cast(item->GetItem()); - delete memory; - _memoryPool.Erase(item); - _createdMemory--; - } - return 0; -} - -template -WebRtc_Word32 MemoryPoolImpl::CreateMemory( - WebRtc_UWord32 amountToCreate) -{ - for(WebRtc_UWord32 i = 0; i < amountToCreate; i++) - { - MemoryType* memory = new MemoryType(); - if(memory == NULL) - { - return -1; - } - _memoryPool.PushBack(static_cast(memory)); - _createdMemory++; - } - return 0; -} -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_GENERIC_H_ diff --git a/modules/audio_conference_mixer/source/memory_pool_windows.h b/modules/audio_conference_mixer/source/memory_pool_windows.h deleted file mode 100644 index 1275ca1ba..000000000 --- a/modules/audio_conference_mixer/source/memory_pool_windows.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_WINDOWS_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_WINDOWS_H_ - -#include -#include - -#include "aligned_malloc.h" -#include "atomic32_wrapper.h" -#include "typedefs.h" - -namespace webrtc { -template struct MemoryPoolItem; - -template -struct MemoryPoolItemPayload -{ - MemoryPoolItemPayload() - : memoryType(), - base(NULL) - { - } - MemoryType memoryType; - MemoryPoolItem* base; -}; - -template -struct MemoryPoolItem -{ - // Atomic single linked list entry header. - SLIST_ENTRY itemEntry; - // Atomic single linked list payload. - MemoryPoolItemPayload* payload; -}; - -template -class MemoryPoolImpl -{ -public: - // MemoryPool functions. - WebRtc_Word32 PopMemory(MemoryType*& memory); - WebRtc_Word32 PushMemory(MemoryType*& memory); - - MemoryPoolImpl(WebRtc_Word32 /*initialPoolSize*/); - ~MemoryPoolImpl(); - - // Atomic functions. - WebRtc_Word32 Terminate(); - bool Initialize(); -private: - // Non-atomic function. - MemoryPoolItem* CreateMemory(); - - // Windows implementation of single linked atomic list, documented here: - // http://msdn.microsoft.com/en-us/library/ms686962(VS.85).aspx - - // Atomic single linked list head. - PSLIST_HEADER _pListHead; - - Atomic32Wrapper _createdMemory; - Atomic32Wrapper _outstandingMemory; -}; - -template -MemoryPoolImpl::MemoryPoolImpl( - WebRtc_Word32 /*initialPoolSize*/) - : _pListHead(NULL), - _createdMemory(0), - _outstandingMemory(0) -{ -} - -template -MemoryPoolImpl::~MemoryPoolImpl() -{ - Terminate(); - if(_pListHead != NULL) - { - AlignedFree(reinterpret_cast(_pListHead)); - _pListHead = NULL; - } - // Trigger assert if there is outstanding memory. - assert(_createdMemory.Value() == 0); - assert(_outstandingMemory.Value() == 0); -} - -template -WebRtc_Word32 MemoryPoolImpl::PopMemory(MemoryType*& memory) -{ - PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); - if(pListEntry == NULL) - { - MemoryPoolItem* item = CreateMemory(); - if(item == NULL) - { - return -1; - } - pListEntry = &(item->itemEntry); - } - ++_outstandingMemory; - memory = &((MemoryPoolItem*)pListEntry)->payload->memoryType; - return 0; -} - -template -WebRtc_Word32 MemoryPoolImpl::PushMemory(MemoryType*& memory) -{ - if(memory == NULL) - { - return -1; - } - - MemoryPoolItem* item = - ((MemoryPoolItemPayload*)memory)->base; - - const WebRtc_Word32 usedItems = --_outstandingMemory; - const WebRtc_Word32 totalItems = _createdMemory.Value(); - const WebRtc_Word32 freeItems = totalItems - usedItems; - if(freeItems < 0) - { - assert(false); - delete item->payload; - AlignedFree(item); - return -1; - } - if(freeItems >= totalItems>>1) - { - delete item->payload; - AlignedFree(item); - --_createdMemory; - return 0; - } - InterlockedPushEntrySList(_pListHead,&(item->itemEntry)); - return 0; -} - -template -bool MemoryPoolImpl::Initialize() -{ - _pListHead = (PSLIST_HEADER)AlignedMalloc(sizeof(SLIST_HEADER), - MEMORY_ALLOCATION_ALIGNMENT); - if(_pListHead == NULL) - { - return false; - } - InitializeSListHead(_pListHead); - return true; -} - -template -WebRtc_Word32 MemoryPoolImpl::Terminate() -{ - WebRtc_Word32 itemsFreed = 0; - PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); - while(pListEntry != NULL) - { - MemoryPoolItem* item = ((MemoryPoolItem*)pListEntry); - delete item->payload; - AlignedFree(item); - --_createdMemory; - itemsFreed++; - pListEntry = InterlockedPopEntrySList(_pListHead); - } - return itemsFreed; -} - -template -MemoryPoolItem* MemoryPoolImpl::CreateMemory() -{ - MemoryPoolItem* returnValue = (MemoryPoolItem*) - AlignedMalloc(sizeof(MemoryPoolItem), - MEMORY_ALLOCATION_ALIGNMENT); - if(returnValue == NULL) - { - return NULL; - } - - returnValue->payload = new MemoryPoolItemPayload(); - if(returnValue->payload == NULL) - { - delete returnValue; - return NULL; - } - returnValue->payload->base = returnValue; - ++_createdMemory; - return returnValue; -} -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_MEMORY_POOL_WINDOWS_H_ diff --git a/modules/audio_conference_mixer/source/time_scheduler.cc b/modules/audio_conference_mixer/source/time_scheduler.cc deleted file mode 100644 index ccfe719f0..000000000 --- a/modules/audio_conference_mixer/source/time_scheduler.cc +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "critical_section_wrapper.h" -#include "time_scheduler.h" - -namespace webrtc { -TimeScheduler::TimeScheduler(const WebRtc_UWord32 periodicityInMs) - : _crit(CriticalSectionWrapper::CreateCriticalSection()), - _isStarted(false), - _lastPeriodMark(), - _periodicityInMs(periodicityInMs), - _periodicityInTicks(TickTime::MillisecondsToTicks(periodicityInMs)), - _missedPeriods(0) - { - } - -TimeScheduler::~TimeScheduler() -{ - delete _crit; -} - -WebRtc_Word32 TimeScheduler::UpdateScheduler() -{ - CriticalSectionScoped cs(*_crit); - if(!_isStarted) - { - _isStarted = true; - _lastPeriodMark = TickTime::Now(); - return 0; - } - // Don't perform any calculations until the debt of pending periods have - // been worked off. - if(_missedPeriods > 0) - { - _missedPeriods--; - return 0; - } - - // Calculate the time that has past since previous call to this function. - TickTime tickNow = TickTime::Now(); - TickInterval amassedTicks = tickNow - _lastPeriodMark; - WebRtc_Word64 amassedMs = amassedTicks.Milliseconds(); - - // Calculate the number of periods the time that has passed correspond to. - WebRtc_Word32 periodsToClaim = (WebRtc_Word32)amassedMs / - ((WebRtc_Word32)_periodicityInMs); - - // One period will be worked off by this call. Make sure that the number of - // pending periods don't end up being negative (e.g. if this function is - // called to often). - if(periodsToClaim < 1) - { - periodsToClaim = 1; - } - - // Update the last period mark without introducing any drifting. - // Note that if this fuunction is called to often _lastPeriodMark can - // refer to a time in the future which in turn will yield TimeToNextUpdate - // that is greater than the periodicity - for(WebRtc_Word32 i = 0; i < periodsToClaim; i++) - { - _lastPeriodMark += _periodicityInTicks; - } - - // Update the total amount of missed periods note that we have processed - // one period hence the - 1 - _missedPeriods += periodsToClaim - 1; - return 0; -} - -WebRtc_Word32 TimeScheduler::TimeToNextUpdate( - WebRtc_Word32& updateTimeInMS) const -{ - CriticalSectionScoped cs(*_crit); - // Missed periods means that the next UpdateScheduler() should happen - // immediately. - if(_missedPeriods > 0) - { - updateTimeInMS = 0; - return 0; - } - - // Calculate the time (in ms) that has past since last call to - // UpdateScheduler() - TickTime tickNow = TickTime::Now(); - TickInterval ticksSinceLastUpdate = tickNow - _lastPeriodMark; - const WebRtc_Word32 millisecondsSinceLastUpdate = - (WebRtc_Word32) ticksSinceLastUpdate.Milliseconds(); - - updateTimeInMS = _periodicityInMs - millisecondsSinceLastUpdate; - updateTimeInMS = (updateTimeInMS < 0) ? 0 : updateTimeInMS; - return 0; -} -} // namespace webrtc diff --git a/modules/audio_conference_mixer/source/time_scheduler.h b/modules/audio_conference_mixer/source/time_scheduler.h deleted file mode 100644 index e2674d90c..000000000 --- a/modules/audio_conference_mixer/source/time_scheduler.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// The TimeScheduler class keeps track of periodic events. It is non-drifting -// and keeps track of any missed periods so that it is possible to catch up. -// (compare to a metronome) - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_TIME_SCHEDULER_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_TIME_SCHEDULER_H_ - -#include "tick_util.h" - -namespace webrtc { -class CriticalSectionWrapper; -class TimeScheduler -{ -public: - TimeScheduler(const WebRtc_UWord32 periodicityInMs); - ~TimeScheduler(); - - // Signal that a periodic event has been triggered. - WebRtc_Word32 UpdateScheduler(); - - // Set updateTimeInMs to the amount of time until UpdateScheduler() should - // be called. This time will never be negative. - WebRtc_Word32 TimeToNextUpdate(WebRtc_Word32& updateTimeInMS) const; - -private: - CriticalSectionWrapper* _crit; - - bool _isStarted; - TickTime _lastPeriodMark; - - WebRtc_UWord32 _periodicityInMs; - WebRtc_Word64 _periodicityInTicks; - WebRtc_UWord32 _missedPeriods; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_SOURCE_TIME_SCHEDULER_H_ diff --git a/modules/audio_conference_mixer/test/FunctionTest/functionTest.cpp b/modules/audio_conference_mixer/test/FunctionTest/functionTest.cpp deleted file mode 100644 index e34a3cbbb..000000000 --- a/modules/audio_conference_mixer/test/FunctionTest/functionTest.cpp +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "functionTest.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "webrtc_vad.h" - -#if (defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)) - #include - #define MY_PERMISSION_MASK S_IRWXU | S_IRWXG | S_IRWXO - #define MKDIR(directory) mkdir(directory,MY_PERMISSION_MASK) -#else // defined(WINDOWS) - #include - #define MKDIR(directory) mkdir(directory) -#endif - -int main(int /*argc*/, char* /*argv[]*/) -{ - // Initialize random number generator - //unsigned int seed = 1220716312; // just a seed that can be used - unsigned int seed = (unsigned)time( NULL ); - srand(seed); - std::cout << "Starting function test. Seed = " << seed << std::endl; - std::cout << "Press enter to continue" << std::endl; - getchar(); - MixerWrapper* testInstance1 = MixerWrapper::CreateMixerWrapper(); - MixerWrapper* testInstance2 = MixerWrapper::CreateMixerWrapper(); - if((testInstance1 == NULL) || - (testInstance2 == NULL)) - { - assert(false); - return 0; - } - - char versionString[256] = ""; - WebRtc_UWord32 remainingBufferInBytes = 256; - WebRtc_UWord32 position = 0; - AudioConferenceMixer::GetVersion(versionString,remainingBufferInBytes,position); - - int read = 1; - while(read != 0) - { - std::cout << versionString << std::endl; - std::cout << "--------Menu-----------" << std::endl; - std::cout << std::endl; - std::cout << "0. Quit" << std::endl; - std::cout << "2. StartMixing" << std::endl; - std::cout << "3. StopMixing" << std::endl; - std::cout << "4. Create participant(s)" << std::endl; - std::cout << "5. Delete participant(s)" << std::endl; - std::cout << "6. List participants " << std::endl; - std::cout << "7. Print mix status " << std::endl; - std::cout << "8. Run identical scenario:" << std::endl; - std::cout << " a. 1 VIP, 3 regular, amount of mixed = 3" << std::endl; - std::cout << " b. 1 anonymous, 3 regular, amount of mixed = 2" << std::endl; - scanf("%i",&read); - getchar(); - MixerParticipant::ParticipantType participantType; - int option = 0; - WebRtc_UWord32 id = 0; - ListItem* item = NULL; - ListWrapper participants; - if(read == 0) - { - // donothing - } - else if(read == 1) - { - } - else if(read == 2) - { - testInstance1->StartMixing(); - } - else if(read == 3) - { - testInstance1->StopMixing(); - } - else if(read == 4) - { - while(true) - { - std::cout << "VIP(music) = " << MixerParticipant::VIP << std::endl; - std::cout << "Regular(speech) = " << MixerParticipant::REGULAR << std::endl; - std::cout << "Anonymous(music) = " << MixerParticipant::MIXED_ANONYMOUS << std::endl; - std::cout << "Select type of participant: "; - scanf("%i",&option); - if(option == MixerParticipant::VIP || - option == MixerParticipant::REGULAR || - option == MixerParticipant::MIXED_ANONYMOUS) - { - break; - } - } - participantType = (MixerParticipant::ParticipantType)option; - testInstance1->CreateParticipant(participantType); - } - else if(read == 5) - { - std::cout << "Select participant to delete: "; - scanf("%i",&option); - id = option; - testInstance1->DeleteParticipant(id); - break; - } - else if(read == 6) - { - testInstance1->GetParticipantList(participants); - item = participants.First(); - std::cout << "The following participants have been created: " << std::endl; - while(item) - { - WebRtc_UWord32 id = item->GetUnsignedItem(); - std::cout << id; - item = participants.Next(item); - if(item != NULL) - { - std::cout << ", "; - } - else - { - std::cout << std::endl; - } - } - } - else if(read == 7) - { - std::cout << "-------------Mixer Status-------------" << std::endl; - testInstance1->PrintStatus(); - testInstance2->PrintStatus(); - std::cout << "Press enter to continue"; - getchar(); - std::cout << std::endl; - std::cout << std::endl; - } - else if(read == 8) - { - const WebRtc_Word32 amountOfParticipants = 4; - MixerParticipant::ParticipantType instance1Participants[] = - {MixerParticipant::VIP, - MixerParticipant::REGULAR, - MixerParticipant::REGULAR, - MixerParticipant::REGULAR}; - MixerParticipant::ParticipantType instance2Participants[] = - {MixerParticipant::MIXED_ANONYMOUS, - MixerParticipant::REGULAR, - MixerParticipant::REGULAR, - MixerParticipant::REGULAR}; - for(WebRtc_Word32 i = 0; i < amountOfParticipants; i++) - { - WebRtc_Word32 startPosition = 0; - GenerateRandomPosition(startPosition); - testInstance1->CreateParticipant(instance1Participants[i],startPosition); - testInstance2->CreateParticipant(instance2Participants[i],startPosition); - } - bool success = true; - success = testInstance1->StartMixing(); - assert(success); - success = testInstance2->StartMixing(2); - assert(success); - } - } - - std::cout << "Press enter to stop" << std::endl; - getchar(); - delete testInstance1; - delete testInstance2; - return 0; -} - -FileWriter::FileWriter() - : - _file(NULL) -{ -} - -FileWriter::~FileWriter() -{ - if(_file) - { - fclose(_file); - } -} - -bool -FileWriter::SetFileName( - const char* fileName) -{ - if(_file) - { - fclose(_file); - } - _file = fopen(fileName,"wb"); - return _file != NULL; -} - -bool -FileWriter::WriteToFile( - const AudioFrame& audioFrame) -{ - WebRtc_Word32 written = (WebRtc_Word32)fwrite(audioFrame._payloadData,sizeof(WebRtc_Word16),audioFrame._payloadDataLengthInSamples,_file); - // Do not flush buffers since that will add (a lot of) delay - return written == audioFrame._payloadDataLengthInSamples; -} - -FileReader::FileReader() - : - _frequency(kDefaultFrequency), - _sampleSize((_frequency*kProcessPeriodicityInMs)/1000), - _timeStamp(0), - _file(NULL), - _vadInstr(NULL), - _automaticVad(false), - _vad(false) -{ - if(WebRtcVad_Create(&_vadInstr) == 0) - { - if(WebRtcVad_Init(_vadInstr) != 0) - { - assert(false); - WebRtcVad_Free(_vadInstr); - _vadInstr = NULL; - } - } - else - { - assert(false); - } -} - -FileReader::~FileReader() -{ - if(_file) - { - fclose(_file); - } - if(_vadInstr) - { - WebRtcVad_Free(_vadInstr); - } -} - -bool -FileReader::SetFileName( - const char* fileName) -{ - if(_file) - { - fclose(_file); - } - _file = fopen(fileName,"rb"); - return _file != NULL; -} - -bool -FileReader::ReadFromFile( - AudioFrame& audioFrame) -{ - - WebRtc_Word16 buffer[AudioFrame::kMaxAudioFrameSizeSamples]; - LoopedFileRead(buffer,AudioFrame::kMaxAudioFrameSizeSamples,_sampleSize,_file); - - bool vad = false; - GetVAD(buffer,_sampleSize,vad); - AudioFrame::VADActivity activity = vad ? AudioFrame::kVadActive : - AudioFrame::kVadPassive; - - _volumeCalculator.ComputeLevel(buffer,_sampleSize); - const WebRtc_Word32 level = _volumeCalculator.GetLevel(); - return audioFrame.UpdateFrame( -1, - _timeStamp, - buffer, - _sampleSize, - _frequency, - AudioFrame::kNormalSpeech, - activity, - 0, - level) == 0; - -} - -bool -FileReader::FastForwardFile( - const WebRtc_Word32 samples) -{ - WebRtc_Word16* tempBuffer = new WebRtc_Word16[samples]; - bool success = LoopedFileRead(tempBuffer,samples,samples,_file); - delete[] tempBuffer; - return success; -} - -bool -FileReader::EnableAutomaticVAD( - bool enable, - int mode) -{ - if(!_automaticVad && - enable) - { - if(WebRtcVad_Init(_vadInstr) == -1) - { - return false; - } - } - WebRtcVad_set_mode(_vadInstr,mode); - _automaticVad = enable; - return true; -} - -bool -FileReader::SetVAD( - bool vad) -{ - if(_automaticVad) - { - return false; - } - _vad = vad; - return true; -} - -bool -FileReader::GetVAD( - WebRtc_Word16* buffer, - WebRtc_UWord8 bufferLengthInSamples, - bool& vad) -{ - if(_automaticVad) - { - WebRtc_Word16 result = WebRtcVad_Process(_vadInstr,_frequency,buffer,bufferLengthInSamples); - if(result == -1) - { - assert(false); - return false; - } - _vad = vad = (result == 1); - } - vad = _vad; - return true; -} - -MixerParticipant* -MixerParticipant::CreateParticipant( - const WebRtc_UWord32 id, - ParticipantType participantType, - const WebRtc_Word32 startPosition, - char* outputPath) -{ - if(participantType == RANDOM) - { - participantType = (ParticipantType)(rand() % 3); - } - MixerParticipant* participant = new MixerParticipant(id,participantType); - // Randomize the start position so we only need one input file - // assume file is smaller than 1 minute wideband = 60 * 16000 - // Always start at a multiple of 10ms wideband - if(!participant->InitializeFileReader(startPosition) || - !participant->InitializeFileWriter(outputPath)) - { - delete participant; - return NULL; - } - return participant; -} - -MixerParticipant::MixerParticipant( - const WebRtc_UWord32 id, - ParticipantType participantType) - : - _id(id), - _participantType(participantType), - _fileReader(), - _fileWriter() -{ -} - -MixerParticipant::~MixerParticipant() -{ -} - -WebRtc_Word32 -MixerParticipant::GetAudioFrame( - const WebRtc_Word32 /*id*/, - AudioFrame& audioFrame) -{ - if(!_fileReader.ReadFromFile(audioFrame)) - { - return -1; - } - audioFrame._id = _id; - return 0; -} - -WebRtc_Word32 -MixerParticipant::MixedAudioFrame( - const AudioFrame& audioFrame) -{ - return _fileWriter.WriteToFile(audioFrame); -} - -WebRtc_Word32 -MixerParticipant::GetParticipantType( - ParticipantType& participantType) -{ - participantType = _participantType; - return 0; -} - -bool -MixerParticipant::InitializeFileReader( - const WebRtc_Word32 startPositionInSamples) -{ - char fileName[128] = ""; - if(_participantType == REGULAR) - { - sprintf(fileName,"convFile.pcm"); - } - else - { - sprintf(fileName,"musicFile.pcm"); - } - if(!_fileReader.SetFileName(fileName)) - { - return false; - } - if(!_fileReader.EnableAutomaticVAD(true,2)) - { - assert(false); - } - return _fileReader.FastForwardFile(startPositionInSamples); -} - -bool -MixerParticipant::InitializeFileWriter( - char* outputPath) -{ - const WebRtc_Word32 stringsize = 128; - char fileName[stringsize] = ""; - strncpy(fileName,outputPath,stringsize); - fileName[stringsize-1] = '\0'; - - char tempName[stringsize]; - tempName[0] = '\0'; - sprintf(tempName,"outputFile%d.pcm",(int)_id); - strncat(fileName,tempName,(stringsize - strlen(fileName))); - fileName[stringsize-1] = '\0'; - - return _fileWriter.SetFileName(fileName); -} - -StatusReceiver::StatusReceiver( - const WebRtc_Word32 id) - : - _id(id), - _mixedParticipants(NULL), - _mixedParticipantsAmount(0), - _mixedParticipantsSize(0), - _vadPositiveParticipants(NULL), - _vadPositiveParticipantsAmount(0), - _vadPositiveParticipantsSize(0), - _mixedAudioLevel(0) -{ -} - -StatusReceiver::~StatusReceiver() -{ - delete[] _mixedParticipants; - delete[] _vadPositiveParticipants; -} - -void -StatusReceiver::MixedParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size) -{ - if(id != _id) - { - assert(false); - } - if(_mixedParticipantsSize < size) - { - delete[] _mixedParticipants; - _mixedParticipantsSize = size; - _mixedParticipants = new ParticipantStatistics[size]; - } - _mixedParticipantsAmount = size; - memcpy(_mixedParticipants,participantStatistics,sizeof(ParticipantStatistics)*size); -} - -void -StatusReceiver::VADPositiveParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size) -{ - if(id != _id) - { - assert(false); - } - - if(_vadPositiveParticipantsSize < size) - { - delete[] _vadPositiveParticipants; - _vadPositiveParticipantsSize = size; - _vadPositiveParticipants = new ParticipantStatistics[size]; - } - _vadPositiveParticipantsAmount = size; - memcpy(_vadPositiveParticipants,participantStatistics,sizeof(ParticipantStatistics)*size); -} - -void -StatusReceiver::MixedAudioLevel( - const WebRtc_Word32 id, - const WebRtc_UWord32 level) -{ - if(id != _id) - { - assert(false); - } - _mixedAudioLevel = level; -} - -void -StatusReceiver::PrintMixedParticipants() -{ - std::cout << "Mixed participants" << std::endl; - if(_mixedParticipantsAmount == 0) - { - std::cout << "N/A" << std::endl; - } - for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++) - { - std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl; - } -} - -void -StatusReceiver::PrintVadPositiveParticipants() -{ - std::cout << "VAD positive participants" << std::endl; - if(_mixedParticipantsAmount == 0) - { - std::cout << "N/A" << std::endl; - } - for(WebRtc_UWord16 i = 0; i < _mixedParticipantsAmount; i++) - { - std::cout << i + 1 << ". Participant " << _mixedParticipants[i].participant << ": level = " << _mixedParticipants[i].level << std::endl; - } -} - -void -StatusReceiver::PrintMixedAudioLevel() -{ - std::cout << "Mixed audio level = " << _mixedAudioLevel << std::endl; -} - -WebRtc_Word32 MixerWrapper::_mixerWrapperIdCounter = 0; - -MixerWrapper::MixerWrapper() - : - _processThread(NULL), - _threadId(0), - _firstProcessCall(true), - _previousTime(), - _periodicityInTicks(TickTime::MillisecondsToTicks(FileReader::kProcessPeriodicityInMs)), - _synchronizationEvent(EventWrapper::Create()), - _freeItemIds(), - _itemIdCounter(0), - _mixerParticipants(), - _mixerWrappererId(_mixerWrapperIdCounter++), - _instanceOutputPath(), - _trace(NULL), - _statusReceiver(_mixerWrappererId), - _generalAudioWriter() -{ - sprintf(_instanceOutputPath,"instance%d/",(int)_mixerWrappererId); - MKDIR(_instanceOutputPath); - _mixer = AudioConferenceMixer::CreateAudioConferenceMixer( - _mixerWrappererId); - if(_mixer != NULL) - { - bool success = true; - - success = _mixer->RegisterMixedStreamCallback(*this) == 0; - assert(success); - success = _mixer->RegisterMixedStreamCallback(*this) == -1; - assert(success); - success = _mixer->UnRegisterMixedStreamCallback() == 0; - assert(success); - success = _mixer->UnRegisterMixedStreamCallback() == -1; - assert(success); - success = _mixer->RegisterMixedStreamCallback(*this) == 0; - assert(success); - - success = _mixer->RegisterMixerStatusCallback(_statusReceiver,2) == 0; - assert(success); - success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == -1; - assert(success); - success = _mixer->UnRegisterMixerStatusCallback() == 0; - assert(success); - success = _mixer->UnRegisterMixerStatusCallback() == -1; - assert(success); - success = _mixer->RegisterMixerStatusCallback(_statusReceiver,1) == 0; - assert(success); - } - else - { - assert(false); - std::cout << "Failed to create mixer instance"; - } -} - -MixerWrapper* -MixerWrapper::CreateMixerWrapper() -{ - MixerWrapper* mixerWrapper = new MixerWrapper(); - if(!mixerWrapper->InitializeFileWriter()) - { - delete mixerWrapper; - return NULL; - } - return mixerWrapper; -} - -MixerWrapper::~MixerWrapper() -{ - StopMixing(); - ClearAllItemIds(); - _synchronizationEvent->StopTimer(); - delete _synchronizationEvent; - delete _mixer; -} - -bool -MixerWrapper::CreateParticipant( - MixerParticipant::ParticipantType participantType) -{ - WebRtc_Word32 startPosition = 0; - GenerateRandomPosition(startPosition); - return CreateParticipant(participantType,startPosition); -} - -bool -MixerWrapper::CreateParticipant( - MixerParticipant::ParticipantType participantType, - const WebRtc_Word32 startPosition) -{ - WebRtc_UWord32 id; - if(!GetFreeItemIds(id)) - { - return false; - } - - MixerParticipant* participant = MixerParticipant::CreateParticipant(id,participantType,startPosition,_instanceOutputPath); - if(!participant) - { - return false; - } - if(_mixerParticipants.Insert(id,static_cast(participant)) != 0) - { - delete participant; - return false; - } - if(!StartMixingParticipant(id)) - { - DeleteParticipant(id); - return false; - } - return true; -} - -bool -MixerWrapper::DeleteParticipant( - const WebRtc_UWord32 id) -{ - bool success = StopMixingParticipant(id); - if(!success) - { - assert(false); - return false; - } - MapItem* item = _mixerParticipants.Find(id); - if(item == NULL) - { - return false; - } - MixerParticipant* participant = static_cast(item->GetItem()); - delete participant; - _mixerParticipants.Erase(item); - AddFreeItemIds(id); - return true; -} - -bool -MixerWrapper::StartMixing( - const WebRtc_UWord32 mixedParticipants) -{ - if(_processThread) - { - return false; - } - if(_mixer->SetAmountOfMixedParticipants(mixedParticipants) != 0) - { - assert(false); - } - WebRtc_UWord32 mixedParticipantsTest = 0; - _mixer->AmountOfMixedParticipants(mixedParticipantsTest); - assert(mixedParticipantsTest == mixedParticipants); - - if(!_synchronizationEvent->StartTimer(true,10)) - { - assert(false); - return false; - } - _processThread = ThreadWrapper::CreateThread(Process, this, kLowPriority); - if(!_processThread->Start(_threadId)) - { - delete _processThread; - _processThread = NULL; - assert(false); - return false; - } - - return true; -} - -bool -MixerWrapper::StopMixing() -{ - while(_processThread && - !_processThread->Stop()) - {} - _synchronizationEvent->StopTimer(); - - delete _processThread; - _processThread = NULL; - return true; -} - -void -MixerWrapper::NewMixedAudio( - const WebRtc_Word32 id, - const AudioFrame& generalAudioFrame, - const AudioFrame** uniqueAudioFrames, - const WebRtc_UWord32 size) -{ - if(id < 0) - { - assert(false); - } - // Store the general audio - _generalAudioWriter.WriteToFile(generalAudioFrame); - - // Send the unique audio frames to its corresponding participants - ListWrapper uniqueAudioFrameList; - for(WebRtc_UWord32 i = 0; i < size; i++) - { - WebRtc_UWord32 id = (uniqueAudioFrames[i])->_id; - MapItem* resultItem = _mixerParticipants.Find(id); - if(resultItem == NULL) - { - assert(false); - continue; - } - MixerParticipant* participant = static_cast(resultItem->GetItem()); - participant->MixedAudioFrame(*(uniqueAudioFrames[i])); - uniqueAudioFrameList.PushBack(resultItem->GetItem()); - } - - // Send the general audio frames to the remaining participants - MapItem* item = _mixerParticipants.First(); - while(item) - { - bool isUnique = false; - ListItem* compareItem = uniqueAudioFrameList.First(); - while(compareItem) - { - if(compareItem->GetItem() == item->GetItem()) - { - isUnique = true; - break; - } - compareItem = uniqueAudioFrameList.Next(compareItem); - } - if(!isUnique) - { - MixerParticipant* participant = static_cast(item->GetItem()); - participant->MixedAudioFrame(generalAudioFrame); - } - item = _mixerParticipants.Next(item); - } -} - -bool -MixerWrapper::GetParticipantList( - ListWrapper& participants) -{ - MapItem* item = _mixerParticipants.First(); - while(item) - { - participants.PushBack(item->GetId()); - item = _mixerParticipants.Next(item); - } - return true; -} - -void -MixerWrapper::PrintStatus() -{ - std::cout << "instance " << _mixerWrappererId << std::endl; - std::cout << std::endl; - _statusReceiver.PrintMixedParticipants(); - std::cout << std::endl; - _statusReceiver.PrintVadPositiveParticipants(); - std::cout << std::endl; - _statusReceiver.PrintMixedAudioLevel(); - std::cout << "---------------------------------------" << std::endl; -} - -bool -MixerWrapper::InitializeFileWriter() -{ - const WebRtc_Word32 stringsize = 128; - char fileName[stringsize] = ""; - strncpy(fileName,_instanceOutputPath,stringsize); - fileName[stringsize-1] = '\0'; - - strncat(fileName,"generalOutputFile.pcm",(stringsize - strlen(fileName))); - fileName[stringsize-1] = '\0'; - return _generalAudioWriter.SetFileName(fileName); -} - -bool -MixerWrapper::Process( - void* instance) -{ - MixerWrapper* mixerWrapper = static_cast(instance); - return mixerWrapper->Process(); -} - -bool -MixerWrapper::Process() -{ - switch(_synchronizationEvent->Wait(1000)) - { - case kEventSignaled: - // Normal operation, ~10 ms has passed - break; - case kEventError: - // Error occured end the thread and throw an assertion - assert(false); - return false; - case kEventTimeout: - // One second has passed without a timeout something is wrong - // end the thread and throw an assertion - assert(false); - return false; - } - WebRtc_Word32 processOfset = 0; - const TickTime currentTime = TickTime::Now(); - if(_firstProcessCall) - { - _previousTime = TickTime::Now(); - _firstProcessCall = false; - } - else - { - TickInterval deltaTime = (currentTime - _previousTime); - _previousTime += _periodicityInTicks; - processOfset = (WebRtc_Word32) deltaTime.Milliseconds(); - processOfset -= FileReader::kProcessPeriodicityInMs; - } - - _mixer->Process(); - WebRtc_Word32 timeUntilNextProcess = _mixer->TimeUntilNextProcess(); - if(processOfset > FileReader::kProcessPeriodicityInMs) - { - std::cout << "Performance Warning: Process running " << processOfset << " too slow" << std::endl; - _previousTime = currentTime; - if(timeUntilNextProcess > 0) - { - std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl; - } - } - else if(processOfset < -FileReader::kProcessPeriodicityInMs) - { - std::cout << "Performance Warning: Process running " << -processOfset << " too fast" << std::endl; - _previousTime = currentTime; - if(timeUntilNextProcess < FileReader::kProcessPeriodicityInMs) - { - std::cout << "Performance Warning: test performance and module performance missmatch" << std::endl; - } - } - return true; -} - - -bool -MixerWrapper::StartMixingParticipant( - const WebRtc_UWord32 id) -{ - MapItem* item = _mixerParticipants.Find(id); - if(item == NULL) - { - return false; - } - MixerParticipant* participant = static_cast(item->GetItem()); - MixerParticipant::ParticipantType participantType = MixerParticipant::REGULAR; - participant->GetParticipantType(participantType); - if(participantType == MixerParticipant::MIXED_ANONYMOUS) - { - bool anonymouslyMixed = false; - bool success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0; - assert(success); - success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0; - assert(success); - assert(anonymouslyMixed); - success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == -1; - assert(success); - success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == 0; - assert(success); - success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0; - assert(success); - assert(!anonymouslyMixed); - success = _mixer->SetAnonymousMixabilityStatus(*participant,false) == -1; - assert(success); - success = _mixer->SetAnonymousMixabilityStatus(*participant,true) == 0; - assert(success); - success = _mixer->AnonymousMixabilityStatus(*participant,anonymouslyMixed) == 0; - assert(success); - assert(anonymouslyMixed); - return success; - } - WebRtc_UWord32 previousAmountOfMixableParticipants = 0; - bool success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0; - assert(success); - - success = _mixer->SetMixabilityStatus(*participant,true) == 0; - assert(success); - success = _mixer->SetMixabilityStatus(*participant,true) == -1; - assert(success); - success = _mixer->SetMixabilityStatus(*participant,false) == 0; - assert(success); - success = _mixer->SetMixabilityStatus(*participant,false) == -1; - assert(success); - success = _mixer->SetMixabilityStatus(*participant,true) == 0; - assert(success); - if(!success) - { - return false; - } - - WebRtc_UWord32 currentAmountOfMixableParticipants = 0; - success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0; - assert(currentAmountOfMixableParticipants == previousAmountOfMixableParticipants + 1); - - bool mixable = true; - success = _mixer->MixabilityStatus(*participant,mixable) == 0; - assert(success); - assert(mixable); - if(participantType == MixerParticipant::REGULAR) - { - return true; - } - bool IsVIP = false; - success = _mixer->SetVIPStatus(*participant,true) == 0; - assert(success); - success = _mixer->VIPStatus(*participant,IsVIP) == 0; - assert(success); - assert(IsVIP); - success = _mixer->SetVIPStatus(*participant,true) == -1; - assert(success); - success = _mixer->SetVIPStatus(*participant,false) == 0; - assert(success); - success = _mixer->VIPStatus(*participant,IsVIP) == 0; - assert(success); - assert(!IsVIP); - success = _mixer->SetVIPStatus(*participant,false) == -1; - assert(success); - success = _mixer->SetVIPStatus(*participant,true) == 0; - assert(success); - success = _mixer->VIPStatus(*participant,IsVIP) == 0; - assert(success); - assert(IsVIP); - assert(success); - return success; -} - -bool -MixerWrapper::StopMixingParticipant( - const WebRtc_UWord32 id) -{ - MapItem* item = _mixerParticipants.Find(id); - if(item == NULL) - { - return false; - } - MixerParticipant* participant = static_cast(item->GetItem()); - bool success = false; - WebRtc_UWord32 previousAmountOfMixableParticipants = 0; - success = _mixer->AmountOfMixables(previousAmountOfMixableParticipants) == 0; - assert(success); - success = _mixer->SetMixabilityStatus(*participant,false) == 0; - assert(success); - WebRtc_UWord32 currentAmountOfMixableParticipants = 0; - success = _mixer->AmountOfMixables(currentAmountOfMixableParticipants) == 0; - assert(success); - assert(success ? currentAmountOfMixableParticipants == previousAmountOfMixableParticipants -1 : - currentAmountOfMixableParticipants == previousAmountOfMixableParticipants); - return success; -} - -bool -MixerWrapper::GetFreeItemIds( - WebRtc_UWord32& itemId) -{ - if(!_freeItemIds.Empty()) - { - ListItem* item = _freeItemIds.First(); - WebRtc_UWord32* id = static_cast(item->GetItem()); - itemId = *id; - delete id; - return true; - } - if(_itemIdCounter == (WebRtc_UWord32) -1) - { - return false; - } - itemId = _itemIdCounter++; - return true; -} - -void -MixerWrapper::AddFreeItemIds( - const WebRtc_UWord32 itemId) -{ - WebRtc_UWord32* id = new WebRtc_UWord32; - *id = itemId; - _freeItemIds.PushBack(static_cast(id)); -} - -void -MixerWrapper::ClearAllItemIds() -{ - ListItem* item = _freeItemIds.First(); - while(item != NULL) - { - WebRtc_UWord32* id = static_cast(item->GetItem()); - delete id; - _freeItemIds.Erase(item); - item = _freeItemIds.First(); - } -} - -bool -LoopedFileRead( - WebRtc_Word16* buffer, - WebRtc_UWord32 bufferSizeInSamples, - WebRtc_UWord32 samplesToRead, - FILE* file) -{ - if(bufferSizeInSamples < samplesToRead) - { - return false; - } - WebRtc_UWord32 gottenSamples = (WebRtc_UWord32)fread(buffer,sizeof(WebRtc_Word16),samplesToRead,file); - if(gottenSamples != samplesToRead) - { - WebRtc_UWord32 missingSamples = samplesToRead - gottenSamples; - fseek(file,0,0); - gottenSamples += (WebRtc_UWord32)fread(&buffer[gottenSamples],sizeof(WebRtc_Word16),missingSamples,file); - } - if(gottenSamples != samplesToRead) - { - return false; - } - return true; -} - -void -GenerateRandomPosition( - WebRtc_Word32& startPosition) -{ - startPosition = (rand() % (60*16000/160)) * 160; -} diff --git a/modules/audio_conference_mixer/test/FunctionTest/functionTest.h b/modules/audio_conference_mixer/test/FunctionTest/functionTest.h deleted file mode 100644 index f25e5f16f..000000000 --- a/modules/audio_conference_mixer/test/FunctionTest/functionTest.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_TEST_FUNCTIONTEST_FUNCTIONTEST_H_ -#define WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_TEST_FUNCTIONTEST_FUNCTIONTEST_H_ - -#include "module_common_types.h" -#include "level_indicator.h" -#include "list_wrapper.h" -#include "map_wrapper.h" -#include "audio_conference_mixer.h" -#include "audio_conference_mixer_defines.h" -#include "tick_util.h" - -namespace webrtc { -class EventWrapper; -class ThreadWrapper; -class Trace; -} -struct WebRtcVadInst; - -class FileWriter -{ -public: - FileWriter(); - ~FileWriter(); - - bool SetFileName( - const char* fileName); - - bool WriteToFile( - const AudioFrame& audioFrame); -private: - FILE* _file; -}; - -class FileReader -{ -public: - enum {kProcessPeriodicityInMs = 10}; - enum Frequency - { - kNbInHz = 8000, - kWbInHz = 16000, - kDefaultFrequency = kWbInHz - }; - - FileReader(); - ~FileReader(); - - bool SetFileName( - const char* fileName); - - bool ReadFromFile( - AudioFrame& audioFrame); - - bool FastForwardFile( - const WebRtc_Word32 samples); - - bool EnableAutomaticVAD( - bool enable, - int mode); - - bool SetVAD( - bool vad); -private: - bool GetVAD( - WebRtc_Word16* buffer, - WebRtc_UWord8 bufferLengthInSamples, - bool& vad); - - Frequency _frequency; - WebRtc_UWord8 _sampleSize; - - WebRtc_UWord32 _timeStamp; - - FILE* _file; - - WebRtcVadInst* _vadInstr; - bool _automaticVad; - bool _vad; - - LevelIndicator _volumeCalculator; -}; - -class MixerParticipant : public MixerParticipant -{ -public: - enum ParticipantType - { - VIP = 0, - REGULAR = 1, - MIXED_ANONYMOUS = 2, - RANDOM = 3 - }; - - static MixerParticipant* CreateParticipant( - const WebRtc_UWord32 id, - ParticipantType participantType, - const WebRtc_Word32 startPosition, - char* outputPath); - ~MixerParticipant(); - - WebRtc_Word32 GetAudioFrame( - const WebRtc_Word32 id, - AudioFrame& audioFrame); - - WebRtc_Word32 MixedAudioFrame( - const AudioFrame& audioFrame); - - WebRtc_Word32 GetParticipantType( - ParticipantType& participantType); -private: - MixerParticipant( - const WebRtc_UWord32 id, - ParticipantType participantType); - - bool InitializeFileReader( - const WebRtc_Word32 startPositionInSamples); - - bool InitializeFileWriter( - char* outputPath); - - WebRtc_UWord32 _id; - ParticipantType _participantType; - - FileReader _fileReader; - FileWriter _fileWriter; -}; - -class StatusReceiver : public AudioMixerStatusReceiver -{ -public: - StatusReceiver( - const WebRtc_Word32 id); - ~StatusReceiver(); - - void MixedParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size); - - void VADPositiveParticipants( - const WebRtc_Word32 id, - const ParticipantStatistics* participantStatistics, - const WebRtc_UWord32 size); - - void MixedAudioLevel( - const WebRtc_Word32 id, - const WebRtc_UWord32 level); - - void PrintMixedParticipants(); - - void PrintVadPositiveParticipants(); - - void PrintMixedAudioLevel(); -private: - WebRtc_Word32 _id; - - ParticipantStatistics* _mixedParticipants; - WebRtc_UWord32 _mixedParticipantsAmount; - WebRtc_UWord32 _mixedParticipantsSize; - - ParticipantStatistics* _vadPositiveParticipants; - WebRtc_UWord32 _vadPositiveParticipantsAmount; - WebRtc_UWord32 _vadPositiveParticipantsSize; - - WebRtc_UWord32 _mixedAudioLevel; -}; - -class MixerWrapper : public AudioMixerOutputReceiver -{ -public: - static MixerWrapper* CreateMixerWrapper(); - ~MixerWrapper(); - - bool SetMixFrequency( - const AudioConferenceMixer::Frequency frequency); - - bool CreateParticipant( - MixerParticipant::ParticipantType participantType); - - bool CreateParticipant( - MixerParticipant::ParticipantType participantType, - const WebRtc_Word32 startPosition); - - bool DeleteParticipant( - const WebRtc_UWord32 id); - - bool StartMixing( - const WebRtc_UWord32 mixedParticipants = AudioConferenceMixer::kDefaultAmountOfMixedParticipants); - - bool StopMixing(); - - void NewMixedAudio( - const WebRtc_Word32 id, - const AudioFrame& generalAudioFrame, - const AudioFrame** uniqueAudioFrames, - const WebRtc_UWord32 size); - - bool GetParticipantList( - ListWrapper& participants); - - void PrintStatus(); -private: - MixerWrapper(); - - bool InitializeFileWriter(); - - static bool Process( - void* instance); - - bool Process(); - - bool StartMixingParticipant( - const WebRtc_UWord32 id); - - bool StopMixingParticipant( - const WebRtc_UWord32 id); - - bool GetFreeItemIds( - WebRtc_UWord32& itemId); - - void AddFreeItemIds( - const WebRtc_UWord32 itemId); - - void ClearAllItemIds(); - - webrtc::ThreadWrapper* _processThread; - unsigned int _threadId; - - // Performance hooks - enum{WARNING_COUNTER = 100}; - - bool _firstProcessCall; - TickTime _previousTime; // Tick time of previous process - const WebRtc_Word64 _periodicityInTicks; // Periodicity - - webrtc::EventWrapper* _synchronizationEvent; - - ListWrapper _freeItemIds; - WebRtc_UWord32 _itemIdCounter; - - MapWrapper _mixerParticipants; - - static WebRtc_Word32 _mixerWrapperIdCounter; - WebRtc_Word32 _mixerWrappererId; - char _instanceOutputPath[128]; - - webrtc::Trace* _trace; - AudioConferenceMixer* _mixer; - - StatusReceiver _statusReceiver; - - FileWriter _generalAudioWriter; -}; - -bool -LoopedFileRead( - WebRtc_Word16* buffer, - WebRtc_UWord32 bufferSizeInSamples, - WebRtc_UWord32 samplesToRead, - FILE* file); - -void -GenerateRandomPosition( - WebRtc_Word32& startPosition); - -#endif // WEBRTC_MODULES_AUDIO_CONFERENCE_MIXER_TEST_FUNCTIONTEST_FUNCTIONTEST_H_ diff --git a/modules/audio_device/OWNERS b/modules/audio_device/OWNERS deleted file mode 100644 index 1d503d299..000000000 --- a/modules/audio_device/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -grunell@google.com -henrika@google.com -niklase@google.com -xians@google.com diff --git a/modules/audio_device/main/interface/audio_device.h b/modules/audio_device/main/interface/audio_device.h deleted file mode 100644 index 1a2b81761..000000000 --- a/modules/audio_device/main/interface/audio_device.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_H - -#include "module.h" -#include "audio_device_defines.h" - -namespace webrtc { - -class AudioDeviceModule : public Module -{ -public: - enum ErrorCode - { - kAdmErrNone = 0, - kAdmErrArgument = 1 - }; - - enum AudioLayer - { - kPlatformDefaultAudio = 0, - kWindowsWaveAudio = 1, - kWindowsCoreAudio = 2, - kLinuxAlsaAudio = 3, - kLinuxPulseAudio = 4, - kDummyAudio = 5 - }; - - enum WindowsDeviceType - { - kDefaultCommunicationDevice = -1, - kDefaultDevice = -2 - }; - - enum BufferType - { - kFixedBufferSize = 0, - kAdaptiveBufferSize = 1 - }; - - enum ChannelType - { - kChannelLeft = 0, - kChannelRight = 1, - kChannelBoth = 2 - }; - -public: - // Factory methods (resource allocation/deallocation) - static AudioDeviceModule* Create( - const int32_t id, - const AudioLayer audioLayer = kPlatformDefaultAudio); - static void Destroy(AudioDeviceModule* module); - - // Retrieve the currently utilized audio layer - virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const = 0; - - // Module methods - static int32_t GetVersion(char* version, - uint32_t& remainingBufferInBytes, - uint32_t& position); - virtual int32_t ChangeUniqueId(const int32_t id) = 0; - - // Error handling - virtual ErrorCode LastError() const = 0; - virtual int32_t RegisterEventObserver( - AudioDeviceObserver* eventCallback) = 0; - - // Full-duplex transportation of PCM audio - virtual int32_t RegisterAudioCallback(AudioTransport* audioCallback) = 0; - - // Main initialization and termination - virtual int32_t Init() = 0; - virtual int32_t Terminate() = 0; - virtual bool Initialized() const = 0; - - // Device enumeration - virtual int16_t PlayoutDevices() = 0; - virtual int16_t RecordingDevices() = 0; - virtual int32_t PlayoutDeviceName(uint16_t index, - char name[kAdmMaxDeviceNameSize], - char guid[kAdmMaxGuidSize]) = 0; - virtual int32_t RecordingDeviceName(uint16_t index, - char name[kAdmMaxDeviceNameSize], - char guid[kAdmMaxGuidSize]) = 0; - - // Device selection - virtual int32_t SetPlayoutDevice(uint16_t index) = 0; - virtual int32_t SetPlayoutDevice(WindowsDeviceType device) = 0; - virtual int32_t SetRecordingDevice(uint16_t index) = 0; - virtual int32_t SetRecordingDevice(WindowsDeviceType device) = 0; - - // Audio transport initialization - virtual int32_t PlayoutIsAvailable(bool* available) = 0; - virtual int32_t InitPlayout() = 0; - virtual bool PlayoutIsInitialized() const = 0; - virtual int32_t RecordingIsAvailable(bool* available) = 0; - virtual int32_t InitRecording() = 0; - virtual bool RecordingIsInitialized() const = 0; - - // Audio transport control - virtual int32_t StartPlayout() = 0; - virtual int32_t StopPlayout() = 0; - virtual bool Playing() const = 0; - virtual int32_t StartRecording() = 0; - virtual int32_t StopRecording() = 0; - virtual bool Recording() const = 0; - - // Microphone Automatic Gain Control (AGC) - virtual int32_t SetAGC(bool enable) = 0; - virtual bool AGC() const = 0; - - // Volume control based on the Windows Wave API (Windows only) - virtual int32_t SetWaveOutVolume(uint16_t volumeLeft, - uint16_t volumeRight) = 0; - virtual int32_t WaveOutVolume(uint16_t* volumeLeft, - uint16_t* volumeRight) const = 0; - - // Audio mixer initialization - virtual int32_t SpeakerIsAvailable(bool* available) = 0; - virtual int32_t InitSpeaker() = 0; - virtual bool SpeakerIsInitialized() const = 0; - virtual int32_t MicrophoneIsAvailable(bool* available) = 0; - virtual int32_t InitMicrophone() = 0; - virtual bool MicrophoneIsInitialized() const = 0; - - // Speaker volume controls - virtual int32_t SpeakerVolumeIsAvailable(bool* available) = 0; - virtual int32_t SetSpeakerVolume(uint32_t volume) = 0; - virtual int32_t SpeakerVolume(uint32_t* volume) const = 0; - virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const = 0; - virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const = 0; - virtual int32_t SpeakerVolumeStepSize(uint16_t* stepSize) const = 0; - - // Microphone volume controls - virtual int32_t MicrophoneVolumeIsAvailable(bool* available) = 0; - virtual int32_t SetMicrophoneVolume(uint32_t volume) = 0; - virtual int32_t MicrophoneVolume(uint32_t* volume) const = 0; - virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const = 0; - virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const = 0; - virtual int32_t MicrophoneVolumeStepSize(uint16_t* stepSize) const = 0; - - // Speaker mute control - virtual int32_t SpeakerMuteIsAvailable(bool* available) = 0; - virtual int32_t SetSpeakerMute(bool enable) = 0; - virtual int32_t SpeakerMute(bool* enabled) const = 0; - - // Microphone mute control - virtual int32_t MicrophoneMuteIsAvailable(bool* available) = 0; - virtual int32_t SetMicrophoneMute(bool enable) = 0; - virtual int32_t MicrophoneMute(bool* enabled) const = 0; - - // Microphone boost control - virtual int32_t MicrophoneBoostIsAvailable(bool* available) = 0; - virtual int32_t SetMicrophoneBoost(bool enable) = 0; - virtual int32_t MicrophoneBoost(bool* enabled) const = 0; - - // Stereo support - virtual int32_t StereoPlayoutIsAvailable(bool* available) const = 0; - virtual int32_t SetStereoPlayout(bool enable) = 0; - virtual int32_t StereoPlayout(bool* enabled) const = 0; - virtual int32_t StereoRecordingIsAvailable(bool* available) const = 0; - virtual int32_t SetStereoRecording(bool enable) = 0; - virtual int32_t StereoRecording(bool* enabled) const = 0; - virtual int32_t SetRecordingChannel(const ChannelType channel) = 0; - virtual int32_t RecordingChannel(ChannelType* channel) const = 0; - - // Delay information and control - virtual int32_t SetPlayoutBuffer(const BufferType type, - uint16_t sizeMS = 0) = 0; - virtual int32_t PlayoutBuffer(BufferType* type, - uint16_t* sizeMS) const = 0; - virtual int32_t PlayoutDelay(uint16_t* delayMS) const = 0; - virtual int32_t RecordingDelay(uint16_t* delayMS) const = 0; - - // CPU load - virtual int32_t CPULoad(uint16_t* load) const = 0; - - // Recording of raw PCM data - virtual int32_t StartRawOutputFileRecording( - const char pcmFileNameUTF8[kAdmMaxFileNameSize]) = 0; - virtual int32_t StopRawOutputFileRecording() = 0; - virtual int32_t StartRawInputFileRecording( - const char pcmFileNameUTF8[kAdmMaxFileNameSize]) = 0; - virtual int32_t StopRawInputFileRecording() = 0; - - // Native sample rate controls (samples/sec) - virtual int32_t SetRecordingSampleRate(const uint32_t samplesPerSec) = 0; - virtual int32_t RecordingSampleRate(uint32_t* samplesPerSec) const = 0; - virtual int32_t SetPlayoutSampleRate(const uint32_t samplesPerSec) = 0; - virtual int32_t PlayoutSampleRate(uint32_t* samplesPerSec) const = 0; - - // Mobile device specific functions - virtual int32_t ResetAudioDevice() = 0; - virtual int32_t SetLoudspeakerStatus(bool enable) = 0; - virtual int32_t GetLoudspeakerStatus(bool* enabled) const = 0; - - // Android specific function - static int32_t SetAndroidObjects(void* javaVM, void* env, void* context); - -protected: - virtual ~AudioDeviceModule() {}; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_H diff --git a/modules/audio_device/main/interface/audio_device_defines.h b/modules/audio_device/main/interface/audio_device_defines.h deleted file mode 100644 index 67db65d9d..000000000 --- a/modules/audio_device/main/interface/audio_device_defines.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DEFINES_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DEFINES_H - -#include "typedefs.h" - -namespace webrtc { - -static const int kAdmMaxDeviceNameSize = 128; -static const int kAdmMaxFileNameSize = 512; -static const int kAdmMaxGuidSize = 128; - -static const int kAdmMinPlayoutBufferSizeMs = 10; -static const int kAdmMaxPlayoutBufferSizeMs = 250; - -// ---------------------------------------------------------------------------- -// AudioDeviceObserver -// ---------------------------------------------------------------------------- - -class AudioDeviceObserver -{ -public: - enum ErrorCode - { - kRecordingError = 0, - kPlayoutError = 1 - }; - enum WarningCode - { - kRecordingWarning = 0, - kPlayoutWarning = 1 - }; - - virtual void OnErrorIsReported(const ErrorCode error) = 0; - virtual void OnWarningIsReported(const WarningCode warning) = 0; - -protected: - virtual ~AudioDeviceObserver() {} -}; - -// ---------------------------------------------------------------------------- -// AudioTransport -// ---------------------------------------------------------------------------- - -class AudioTransport -{ -public: - virtual int32_t RecordedDataIsAvailable(const char* audioSamples, - const uint32_t nSamples, - const uint8_t nBytesPerSample, - const uint8_t nChannels, - const uint32_t samplesPerSec, - const uint32_t totalDelayMS, - const int32_t clockDrift, - const uint32_t currentMicLevel, - uint32_t& newMicLevel) = 0; - - virtual int32_t NeedMorePlayData(const uint32_t nSamples, - const uint8_t nBytesPerSample, - const uint8_t nChannels, - const uint32_t samplesPerSec, - char* audioSamples, - uint32_t& nSamplesOut) = 0; - -protected: - virtual ~AudioTransport() {} -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DEFINES_H diff --git a/modules/audio_device/main/source/Android.mk b/modules/audio_device/main/source/Android.mk deleted file mode 100644 index 043110aa4..000000000 --- a/modules/audio_device/main/source/Android.mk +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_audio_device -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := audio_device_buffer.cc \ - audio_device_generic.cc \ - audio_device_utility.cc \ - audio_device_impl.cc \ - Android/audio_device_android_native.cc \ - Android/audio_device_utility_android.cc \ - Linux/audio_device_utility_linux.cc \ - Dummy/audio_device_dummy.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_ANDROID' \ - '-DWEBRTC_ANDROID_NATIVE' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := \ - $(JNI_H_INCLUDE) \ - $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/. \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/Android \ - $(LOCAL_PATH)/Dummy \ - $(LOCAL_PATH)/Linux \ - $(LOCAL_PATH)/../../../../common_audio/resampler/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing_library/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - system/media/wilhelm/include/SLES - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport \ - libOpenSLES - -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_device/main/source/Android/audio_device_android_jni.cc b/modules/audio_device/main/source/Android/audio_device_android_jni.cc deleted file mode 100644 index f9e68b5fe..000000000 --- a/modules/audio_device/main/source/Android/audio_device_android_jni.cc +++ /dev/null @@ -1,3058 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device implementation (JNI/AudioTrack/AudioRecord usage) - */ - -// TODO(xians): Break out attach and detach current thread to JVM to -// separate functions. - -#include -#include "audio_device_utility.h" -#include "audio_device_android_jni.h" -#include "audio_device_config.h" - -#include "trace.h" -#include "thread_wrapper.h" -#include "event_wrapper.h" - -// Android logging, uncomment to print trace to logcat instead of -// trace file/callback -//#include -//#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, \ -// "WebRTC AD jni", __VA_ARGS__) - -namespace webrtc -{ - -JavaVM* globalJvm = NULL; -JNIEnv* globalJNIEnv = NULL; -jobject globalSndContext = NULL; -jclass globalScClass = NULL; - -// ---------------------------------------------------------------------------- -// SetAndroidAudioDeviceObjects -// -// Global function for setting Java pointers and creating Java -// objects that are global to all instances of VoiceEngine used -// by the same Java application. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 SetAndroidAudioDeviceObjects(void* javaVM, void* env, - void* context) -{ - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, -1, "%s", - __FUNCTION__); - - globalJvm = (JavaVM*) javaVM; - globalSndContext = (jobject) context; - - if (env) - { - globalJNIEnv = (JNIEnv *) env; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, -1, - "%s: will find class", __FUNCTION__); - - // get java class type (note path to class packet) - jclass - javaScClassLocal = - globalJNIEnv->FindClass( - "org/webrtc/voiceengine/AudioDeviceAndroid"); - if (!javaScClassLocal) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "%s: could not find java class", __FUNCTION__); - return -1; /* exception thrown */ - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, -1, - "%s: will create global reference", __FUNCTION__); - - // create a global reference to the class (to tell JNI that we are - // referencing it after this function has returned) - globalScClass - = reinterpret_cast (globalJNIEnv->NewGlobalRef( - javaScClassLocal)); - if (!globalScClass) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "%s: could not create reference", __FUNCTION__); - return -1; - } - - // Delete local class ref, we only use the global ref - globalJNIEnv->DeleteLocalRef(javaScClassLocal); - } - else // User is resetting the env variable - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, - "%s: env is NULL, assuming deinit", __FUNCTION__); - - if (!globalJNIEnv) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, - "%s: saved env already NULL", __FUNCTION__); - return 0; - } - - globalJNIEnv->DeleteGlobalRef(globalScClass); - globalJNIEnv = (JNIEnv *) NULL; - } - - return 0; -} - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceAndroidJni - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceAndroidJni::AudioDeviceAndroidJni(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _timeEventRec(*EventWrapper::Create()), - _timeEventPlay(*EventWrapper::Create()), - _recStartStopEvent(*EventWrapper::Create()), - _playStartStopEvent(*EventWrapper::Create()), - _ptrThreadPlay(NULL), - _ptrThreadRec(NULL), - _recThreadID(0), - _playThreadID(0), - _playThreadIsInitialized(false), - _recThreadIsInitialized(false), - _shutdownPlayThread(false), - _shutdownRecThread(false), - // _recBuffer[2*REC_BUF_SIZE_IN_SAMPLES] - _recordingDeviceIsSpecified(false), - _playoutDeviceIsSpecified(false), _initialized(false), - _recording(false), _playing(false), _recIsInitialized(false), - _playIsInitialized(false), _micIsInitialized(false), - _speakerIsInitialized(false), _startRec(false), _stopRec(false), - _startPlay(false), _stopPlay(false), _playWarning(0), - _playError(0), _recWarning(0), _recError(0), _delayPlayout(0), - _delayRecording(0), - _AGC(false), - _samplingFreqIn(0), - _samplingFreqOut(0), - _maxSpeakerVolume(0), - _loudSpeakerOn(false), - _recAudioSource(1), // 1 is AudioSource.MIC which is our default - _javaVM(NULL), _javaContext(NULL), _jniEnvPlay(NULL), - _jniEnvRec(NULL), _javaScClass(0), _javaScObj(0), - _javaPlayBuffer(0), _javaRecBuffer(0), _javaDirectPlayBuffer(NULL), - _javaDirectRecBuffer(NULL), _javaMidPlayAudio(0), - _javaMidRecAudio(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); - - memset(_recBuffer, 0, sizeof(_recBuffer)); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceAndroidJni - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceAndroidJni::~AudioDeviceAndroidJni() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - - Terminate(); - - delete &_recStartStopEvent; - delete &_playStartStopEvent; - delete &_timeEventRec; - delete &_timeEventPlay; - delete &_critSect; -} - -// ============================================================================ -// API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AttachAudioBuffer -// ---------------------------------------------------------------------------- - -void AudioDeviceAndroidJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // inform the AudioBuffer about default settings for this implementation - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); -} - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - audioLayer = AudioDeviceModule::kPlatformDefaultAudio; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Init -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - // Init Java member variables - // and set up JNI interface to - // AudioDeviceAndroid java class - if (InitJavaResources() != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Failed to init Java resources", __FUNCTION__); - return -1; - } - - // Check the sample rate to be used for playback and recording - // and the max playout volume - if (InitSampleRate() != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Failed to init samplerate", __FUNCTION__); - return -1; - } - - // RECORDING - const char* threadName = "webrtc_jni_audio_capture_thread"; - _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, - kRealtimePriority, threadName); - if (_ptrThreadRec == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the rec audio thread"); - return -1; - } - - unsigned int threadID(0); - if (!_ptrThreadRec->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the rec audio thread"); - delete _ptrThreadRec; - _ptrThreadRec = NULL; - return -1; - } - _recThreadID = threadID; - - // PLAYOUT - threadName = "webrtc_jni_audio_render_thread"; - _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, this, - kRealtimePriority, threadName); - if (_ptrThreadPlay == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the play audio thread"); - return -1; - } - - threadID = 0; - if (!_ptrThreadPlay->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the play audio thread"); - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - return -1; - } - _playThreadID = threadID; - - _initialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Terminate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) - { - return 0; - } - - // RECORDING - StopRecording(); - _shutdownRecThread = true; - _timeEventRec.Set(); // Release rec thread from waiting state - if (_ptrThreadRec) - { - // First, the thread must detach itself from Java VM - _critSect.Leave(); - if (kEventSignaled != _recStartStopEvent.Wait(5000)) - { - WEBRTC_TRACE( - kTraceError, - kTraceAudioDevice, - _id, - "%s: Recording thread shutdown timed out, cannot " - "terminate thread", - __FUNCTION__); - // If we close thread anyway, the app will crash - return -1; - } - _recStartStopEvent.Reset(); - _critSect.Enter(); - - // Close down rec thread - ThreadWrapper* tmpThread = _ptrThreadRec; - _ptrThreadRec = NULL; - _critSect.Leave(); - tmpThread->SetNotAlive(); - // Release again, we might have returned to waiting state - _timeEventRec.Set(); - if (tmpThread->Stop()) - { - delete tmpThread; - _jniEnvRec = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the rec audio thread"); - } - _critSect.Enter(); - - _recThreadIsInitialized = false; - } - _micIsInitialized = false; - _recordingDeviceIsSpecified = false; - - // PLAYOUT - StopPlayout(); - _shutdownPlayThread = true; - _timeEventPlay.Set(); // Release rec thread from waiting state - if (_ptrThreadPlay) - { - // First, the thread must detach itself from Java VM - _critSect.Leave(); - if (kEventSignaled != _playStartStopEvent.Wait(5000)) - { - WEBRTC_TRACE( - kTraceError, - kTraceAudioDevice, - _id, - "%s: Playout thread shutdown timed out, cannot " - "terminate thread", - __FUNCTION__); - // If we close thread anyway, the app will crash - return -1; - } - _playStartStopEvent.Reset(); - _critSect.Enter(); - - // Close down play thread - ThreadWrapper* tmpThread = _ptrThreadPlay; - _ptrThreadPlay = NULL; - _critSect.Leave(); - tmpThread->SetNotAlive(); - _timeEventPlay.Set(); - if (tmpThread->Stop()) - { - delete tmpThread; - _jniEnvPlay = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the play audio thread"); - } - _critSect.Enter(); - - _playThreadIsInitialized = false; - } - _speakerIsInitialized = false; - _playoutDeviceIsSpecified = false; - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - // Make method IDs and buffer pointers unusable - _javaMidPlayAudio = 0; - _javaMidRecAudio = 0; - _javaDirectPlayBuffer = NULL; - _javaDirectRecBuffer = NULL; - - // Delete the references to the java buffers, this allows the - // garbage collector to delete them - env->DeleteGlobalRef(_javaPlayBuffer); - _javaPlayBuffer = 0; - env->DeleteGlobalRef(_javaRecBuffer); - _javaRecBuffer = 0; - - // Delete the references to the java object and class, this allows the - // garbage collector to delete them - env->DeleteGlobalRef(_javaScObj); - _javaScObj = 0; - _javaScClass = 0; - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - _initialized = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Initialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return (_initialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // We always assume it's available - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout already started"); - return -1; - } - - if (!_playoutDeviceIsSpecified) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout device is not specified"); - return -1; - } - - // Nothing needs to be done here, we use a flag to have consistent - // behavior with other platforms - _speakerIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // We always assume it's available - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording already started"); - return -1; - } - - if (!_recordingDeviceIsSpecified) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording device is not specified"); - return -1; - } - - // Nothing needs to be done here, we use a flag to have consistent - // behavior with other platforms - _micIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _speakerIsInitialized; -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _micIsInitialized; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = true; // We assume we are always be able to set/get volume - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetSpeakerVolume(volume=%u)", volume); - - if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - if (!_javaContext) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Context is not set"); - return -1; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID setPlayoutVolumeID = env->GetMethodID(_javaScClass, - "SetPlayoutVolume", "(I)I"); - - // call java sc object method - jint res = env->CallIntMethod(_javaScObj, setPlayoutVolumeID, - static_cast (volume)); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "SetPlayoutVolume failed (%d)", res); - return -1; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - if (!_javaContext) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Context is not set"); - return -1; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID getPlayoutVolumeID = env->GetMethodID(_javaScClass, - "GetPlayoutVolume", "()I"); - - // call java sc object method - jint level = env->CallIntMethod(_javaScObj, getPlayoutVolumeID); - if (level < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "GetPlayoutVolume failed (%d)", level); - return -1; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - volume = static_cast (level); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetWaveOutVolume( - WebRtc_UWord16 /*volumeLeft*/, - WebRtc_UWord16 /*volumeRight*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s)", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::WaveOutVolume( - WebRtc_UWord16& /*volumeLeft*/, - WebRtc_UWord16& /*volumeRight*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MaxSpeakerVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - maxVolume = _maxSpeakerVolume; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MinSpeakerVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - minVolume = 0; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - stepSize = 1; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Speaker mute not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetSpeakerMute(bool /*enable*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SpeakerMute(bool& /*enabled*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic mute not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetMicrophoneMute(bool /*enable*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneMute(bool& /*enabled*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic boost not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_micIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - if (enable) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_micIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Stereo recording not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoRecording -// -// Specifies the number of input channels. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (enable) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Stereo playout not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (enable) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetAGC -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -// ---------------------------------------------------------------------------- -// AGC -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::AGC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _AGC; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneVolumeIsAvailable( - bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; // Mic volume not supported on Android - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetMicrophoneVolume( - WebRtc_UWord32 /*volume*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneVolume( - WebRtc_UWord32& /*volume*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MaxMicrophoneVolume( - WebRtc_UWord32& /*maxVolume*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MinMicrophoneVolume( - WebRtc_UWord32& /*minVolume*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::MicrophoneVolumeStepSize( - WebRtc_UWord16& /*stepSize*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceAndroidJni::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // There is one device only - return 1; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_playIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout already initialized"); - return -1; - } - - if (0 != index) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Do nothing but set a flag, this is to have consistent behavior - // with other platforms - _playoutDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (0 != index) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Return empty string - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (0 != index) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Return empty string - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceAndroidJni::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // There is one device only - return 1; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_recIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording already initialized"); - return -1; - } - - // Recording device index will be used for specifying recording - // audio source, allow any value - _recAudioSource = index; - _recordingDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - - return res; -} - -// ---------------------------------------------------------------------------- -// RecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - - return res; -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Not initialized"); - return -1; - } - - if (_playing) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout already started"); - return -1; - } - - if (!_playoutDeviceIsSpecified) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout device is not specified"); - return -1; - } - - if (_playIsInitialized) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already initialized"); - return 0; - } - - // Initialize the speaker - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "attaching"); - - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID initPlaybackID = env->GetMethodID(_javaScClass, "InitPlayback", - "(I)I"); - - int samplingFreq = 44100; - if (_samplingFreqOut != 44) - { - samplingFreq = _samplingFreqOut * 1000; - } - - int retVal = -1; - - // Call java sc object method - jint res = env->CallIntMethod(_javaScObj, initPlaybackID, samplingFreq); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "InitPlayback failed (%d)", res); - } - else - { - // Set the audio device buffer sampling rate - _ptrAudioBuffer->SetPlayoutSampleRate(_samplingFreqOut * 1000); - _playIsInitialized = true; - retVal = 0; - } - - // Detach this thread if it was attached - if (isAttached) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "detaching"); - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return retVal; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Not initialized"); - return -1; - } - - if (_recording) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording already started"); - return -1; - } - - if (!_recordingDeviceIsSpecified) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording device is not specified"); - return -1; - } - - if (_recIsInitialized) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already initialized"); - return 0; - } - - // Initialize the microphone - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID initRecordingID = env->GetMethodID(_javaScClass, "InitRecording", - "(II)I"); - - int samplingFreq = 44100; - if (_samplingFreqIn != 44) - { - samplingFreq = _samplingFreqIn * 1000; - } - - int retVal = -1; - - // call java sc object method - jint res = env->CallIntMethod(_javaScObj, initRecordingID, _recAudioSource, - samplingFreq); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "InitRecording failed (%d)", res); - } - else - { - // Set the audio device buffer sampling rate - _ptrAudioBuffer->SetRecordingSampleRate(_samplingFreqIn * 1000); - - // the init rec function returns a fixed delay - _delayRecording = res / _samplingFreqIn; - - _recIsInitialized = true; - retVal = 0; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return retVal; -} - -// ---------------------------------------------------------------------------- -// StartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording not initialized"); - return -1; - } - - if (_recording) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already started"); - return 0; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID startRecordingID = env->GetMethodID(_javaScClass, - "StartRecording", "()I"); - - // Call java sc object method - jint res = env->CallIntMethod(_javaScObj, startRecordingID); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "StartRecording failed (%d)", res); - return -1; - } - - _recWarning = 0; - _recError = 0; - - // Signal to recording thread that we want to start - _startRec = true; - _timeEventRec.Set(); // Release thread from waiting state - _critSect.Leave(); - // Wait for thread to init - if (kEventSignaled != _recStartStopEvent.Wait(5000)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Timeout or error starting"); - } - _recStartStopEvent.Reset(); - _critSect.Enter(); - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StopRecording() - -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording is not initialized"); - return 0; - } - - // make sure we don't start recording (it's asynchronous), - // assuming that we are under lock - _startRec = false; - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID stopRecordingID = env->GetMethodID(_javaScClass, "StopRecording", - "()I"); - - // Call java sc object method - jint res = env->CallIntMethod(_javaScObj, stopRecordingID); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "StopRecording failed (%d)", res); - } - - _recIsInitialized = false; - _recording = false; - _recWarning = 0; - _recError = 0; - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _recIsInitialized; -} - -// ---------------------------------------------------------------------------- -// Recording -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _recording; -} - -// ---------------------------------------------------------------------------- -// PlayoutIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _playIsInitialized; -} - -// ---------------------------------------------------------------------------- -// StartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout not initialized"); - return -1; - } - - if (_playing) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already started"); - return 0; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID startPlaybackID = env->GetMethodID(_javaScClass, "StartPlayback", - "()I"); - - // Call java sc object method - jint res = env->CallIntMethod(_javaScObj, startPlaybackID); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "StartPlayback failed (%d)", res); - return -1; - } - - _playWarning = 0; - _playError = 0; - - // Signal to playout thread that we want to start - _startPlay = true; - _timeEventPlay.Set(); // Release thread from waiting state - _critSect.Leave(); - // Wait for thread to init - if (kEventSignaled != _playStartStopEvent.Wait(5000)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Timeout or error starting"); - } - _playStartStopEvent.Reset(); - _critSect.Enter(); - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout is not initialized"); - return 0; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID stopPlaybackID = env->GetMethodID(_javaScClass, "StopPlayback", - "()I"); - - // Call java sc object method - jint res = env->CallIntMethod(_javaScObj, stopPlaybackID); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "StopPlayback failed (%d)", res); - } - - _playIsInitialized = false; - _playing = false; - _playWarning = 0; - _playError = 0; - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDelay -// -// Remaining amount of data still in the playout buffer. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - delayMS = _delayPlayout; - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDelay -// -// Remaining amount of data still in the recording buffer. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::RecordingDelay( - WebRtc_UWord16& delayMS) const -{ - delayMS = _delayRecording; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Playing -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _playing; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetPlayoutBuffer( - const AudioDeviceModule::BufferType /*type*/, - WebRtc_UWord16 /*sizeMS*/) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - type = AudioDeviceModule::kAdaptiveBufferSize; - sizeMS = _delayPlayout; // Set to current playout delay - - return 0; -} - -// ---------------------------------------------------------------------------- -// CPULoad -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::CPULoad(WebRtc_UWord16& /*load*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::PlayoutWarning() const -{ - return (_playWarning > 0); -} - -// ---------------------------------------------------------------------------- -// PlayoutError -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::PlayoutError() const -{ - return (_playError > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::RecordingWarning() const -{ - return (_recWarning > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingError -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::RecordingError() const -{ - return (_recError > 0); -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceAndroidJni::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutError -// ---------------------------------------------------------------------------- - -void AudioDeviceAndroidJni::ClearPlayoutError() -{ - _playError = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceAndroidJni::ClearRecordingWarning() -{ - _recWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingError -// ---------------------------------------------------------------------------- - -void AudioDeviceAndroidJni::ClearRecordingError() -{ - _recError = 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetRecordingSampleRate( - const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s(%d)", __FUNCTION__, samplesPerSec); - - if (samplesPerSec > 48000 || samplesPerSec < 8000) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Invalid sample rate"); - return -1; - } - - // set the recording sample rate to use - if (samplesPerSec == 44100) - { - _samplingFreqIn = 44; - } - else - { - _samplingFreqIn = samplesPerSec / 1000; - } - - // Update the AudioDeviceBuffer - _ptrAudioBuffer->SetRecordingSampleRate(samplesPerSec); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetPlayoutSampleRate( - const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s(%d)", __FUNCTION__, samplesPerSec); - - if (samplesPerSec > 48000 || samplesPerSec < 8000) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Invalid sample rate"); - return -1; - } - - // set the playout sample rate to use - if (samplesPerSec == 44100) - { - _samplingFreqOut = 44; - } - else - { - _samplingFreqOut = samplesPerSec / 1000; - } - - // Update the AudioDeviceBuffer - _ptrAudioBuffer->SetPlayoutSampleRate(samplesPerSec); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetLoudspeakerStatus -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::SetLoudspeakerStatus(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s(%d)", __FUNCTION__, enable); - - if (!_javaContext) - { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - " Context is not set"); - return -1; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - " Could not attach thread to JVM (%d, %p)", res, env); - return -1; - } - isAttached = true; - } - - // get the method ID - jmethodID setPlayoutSpeakerID = env->GetMethodID(_javaScClass, - "SetPlayoutSpeaker", - "(Z)I"); - - // call java sc object method - jint res = env->CallIntMethod(_javaScObj, setPlayoutSpeakerID, enable); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - " SetPlayoutSpeaker failed (%d)", res); - return -1; - } - - _loudSpeakerOn = enable; - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, -1, - " Could not detach thread from JVM"); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// GetLoudspeakerStatus -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::GetLoudspeakerStatus(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - enabled = _loudSpeakerOn; - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - - -// ---------------------------------------------------------------------------- -// InitJavaResources -// -// Initializes needed Java resources like the JNI interface to -// AudioDeviceAndroid.java -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitJavaResources() -{ - // todo: Check if we already have created the java object - _javaVM = globalJvm; - _javaContext = globalSndContext; - _javaScClass = globalScClass; - - // use the jvm that has been set - if (!_javaVM) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Not a valid Java VM pointer", __FUNCTION__); - return -1; - } - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "get method id"); - - // get the method ID for the void(void) constructor - jmethodID cid = env->GetMethodID(_javaScClass, "", "()V"); - if (cid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get constructor ID", __FUNCTION__); - return -1; /* exception thrown */ - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "construct object", __FUNCTION__); - - // construct the object - jobject javaScObjLocal = env->NewObject(_javaScClass, cid); - if (!javaScObjLocal) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "%s: could not create Java sc object", __FUNCTION__); - return -1; - } - - // create a reference to the object (to tell JNI that we are referencing it - // after this function has returned) - _javaScObj = env->NewGlobalRef(javaScObjLocal); - if (!_javaScObj) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not create Java sc object reference", - __FUNCTION__); - return -1; - } - - // Delete local object ref, we only use the global ref - env->DeleteLocalRef(javaScObjLocal); - - ////////////////////// - // AUDIO MANAGEMENT - - // This is not mandatory functionality - if (_javaContext) - { - // Get Context field ID - jfieldID fidContext = env->GetFieldID(_javaScClass, "_context", - "Landroid/content/Context;"); - if (!fidContext) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get Context fid", __FUNCTION__); - return -1; - } - - // Set the Java application Context so we can use AudioManager - // Get Context object and check it - jobject javaContext = (jobject) _javaContext; - env->SetObjectField(_javaScObj, fidContext, javaContext); - javaContext = env->GetObjectField(_javaScObj, fidContext); - if (!javaContext) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not set Context", __FUNCTION__); - return -1; - } - - // Delete local object ref - env->DeleteLocalRef(javaContext); - } - else - { - WEBRTC_TRACE( - kTraceWarning, - kTraceAudioDevice, - _id, - "%s: did not set Context - some functionality is not " - "supported", - __FUNCTION__); - } - - ///////////// - // PLAYOUT - - // Get play buffer field ID - jfieldID fidPlayBuffer = env->GetFieldID(_javaScClass, "_playBuffer", - "Ljava/nio/ByteBuffer;"); - if (!fidPlayBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get play buffer fid", __FUNCTION__); - return -1; - } - - // Get play buffer object - jobject javaPlayBufferLocal = - env->GetObjectField(_javaScObj, fidPlayBuffer); - if (!javaPlayBufferLocal) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get play buffer", __FUNCTION__); - return -1; - } - - // Create a global reference to the object (to tell JNI that we are - // referencing it after this function has returned) - // NOTE: we are referencing it only through the direct buffer (see below) - _javaPlayBuffer = env->NewGlobalRef(javaPlayBufferLocal); - if (!_javaPlayBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get play buffer reference", __FUNCTION__); - return -1; - } - - // Delete local object ref, we only use the global ref - env->DeleteLocalRef(javaPlayBufferLocal); - - // Get direct buffer - _javaDirectPlayBuffer = env->GetDirectBufferAddress(_javaPlayBuffer); - if (!_javaDirectPlayBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get direct play buffer", __FUNCTION__); - return -1; - } - - // Get the play audio method ID - _javaMidPlayAudio = env->GetMethodID(_javaScClass, "PlayAudio", "(I)I"); - if (!_javaMidPlayAudio) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get play audio mid", __FUNCTION__); - return -1; - } - - ////////////// - // RECORDING - - // Get rec buffer field ID - jfieldID fidRecBuffer = env->GetFieldID(_javaScClass, "_recBuffer", - "Ljava/nio/ByteBuffer;"); - if (!fidRecBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get rec buffer fid", __FUNCTION__); - return -1; - } - - // Get rec buffer object - jobject javaRecBufferLocal = env->GetObjectField(_javaScObj, fidRecBuffer); - if (!javaRecBufferLocal) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get rec buffer", __FUNCTION__); - return -1; - } - - // Create a global reference to the object (to tell JNI that we are - // referencing it after this function has returned) - // NOTE: we are referencing it only through the direct buffer (see below) - _javaRecBuffer = env->NewGlobalRef(javaRecBufferLocal); - if (!_javaRecBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get rec buffer reference", __FUNCTION__); - return -1; - } - - // Delete local object ref, we only use the global ref - env->DeleteLocalRef(javaRecBufferLocal); - - // Get direct buffer - _javaDirectRecBuffer = env->GetDirectBufferAddress(_javaRecBuffer); - if (!_javaDirectRecBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get direct rec buffer", __FUNCTION__); - return -1; - } - - // Get the rec audio method ID - _javaMidRecAudio = env->GetMethodID(_javaScClass, "RecordAudio", "(I)I"); - if (!_javaMidRecAudio) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get rec audio mid", __FUNCTION__); - return -1; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitSampleRate -// -// checks supported sample rates for playback -// and recording and initializes the rates to be used -// Also stores the max playout volume returned from InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidJni::InitSampleRate() -{ - int samplingFreq = 44100; - jint res = 0; - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - if (_samplingFreqIn > 0) - { - // read the configured sampling rate - samplingFreq = 44100; - if (_samplingFreqIn != 44) - { - samplingFreq = _samplingFreqIn * 1000; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - " Trying configured recording sampling rate %d", - samplingFreq); - } - - // get the method ID - jmethodID initRecordingID = env->GetMethodID(_javaScClass, "InitRecording", - "(II)I"); - - bool keepTrying = true; - while (keepTrying) - { - // call java sc object method - res = env->CallIntMethod(_javaScObj, initRecordingID, _recAudioSource, - samplingFreq); - if (res < 0) - { - switch (samplingFreq) - { - case 44100: - samplingFreq = 16000; - break; - case 16000: - samplingFreq = 8000; - break; - default: // error - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "%s: InitRecording failed (%d)", __FUNCTION__, - res); - return -1; - } - } - else - { - keepTrying = false; - } - } - - // set the recording sample rate to use - if (samplingFreq == 44100) - { - _samplingFreqIn = 44; - } - else - { - _samplingFreqIn = samplingFreq / 1000; - } - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - "Recording sample rate set to (%d)", _samplingFreqIn); - - // get the method ID - jmethodID stopRecordingID = env->GetMethodID(_javaScClass, "StopRecording", - "()I"); - - // Call java sc object method - res = env->CallIntMethod(_javaScObj, stopRecordingID); - if (res < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "StopRecording failed (%d)", res); - } - - // get the method ID - jmethodID initPlaybackID = env->GetMethodID(_javaScClass, "InitPlayback", - "(I)I"); - - if (_samplingFreqOut > 0) - { - // read the configured sampling rate - samplingFreq = 44100; - if (_samplingFreqOut != 44) - { - samplingFreq = _samplingFreqOut * 1000; - } - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - " Trying configured playback sampling rate %d", - samplingFreq); - } - else - { - // set the preferred sampling frequency - if (samplingFreq == 8000) - { - // try 16000 - samplingFreq = 16000; - } - // else use same as recording - } - - keepTrying = true; - while (keepTrying) - { - // call java sc object method - res = env->CallIntMethod(_javaScObj, initPlaybackID, samplingFreq); - if (res < 0) - { - switch (samplingFreq) - { - case 44100: - samplingFreq = 16000; - break; - case 16000: - samplingFreq = 8000; - break; - default: // error - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "InitPlayback failed (%d)", res); - return -1; - } - } - else - { - keepTrying = false; - } - } - - // Store max playout volume - _maxSpeakerVolume = static_cast (res); - if (_maxSpeakerVolume < 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Did not get valid max speaker volume value (%d)", - _maxSpeakerVolume); - } - - // set the playback sample rate to use - if (samplingFreq == 44100) - { - _samplingFreqOut = 44; - } - else - { - _samplingFreqOut = samplingFreq / 1000; - } - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - "Playback sample rate set to (%d)", _samplingFreqOut); - - // get the method ID - jmethodID stopPlaybackID = env->GetMethodID(_javaScClass, "StopPlayback", - "()I"); - - // Call java sc object method - res = env->CallIntMethod(_javaScObj, stopPlaybackID); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "StopPlayback failed (%d)", res); - } - - // Detach this thread if it was attached - if (isAttached) - { - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - return 0; -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// PlayThreadFunc -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::PlayThreadFunc(void* pThis) -{ - return (static_cast (pThis)->PlayThreadProcess()); -} - -// ---------------------------------------------------------------------------- -// RecThreadFunc -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::RecThreadFunc(void* pThis) -{ - return (static_cast (pThis)->RecThreadProcess()); -} - -// ---------------------------------------------------------------------------- -// PlayThreadProcess -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::PlayThreadProcess() -{ - if (!_playThreadIsInitialized) - { - // Do once when thread is started - - // Attach this thread to JVM and get the JNI env for this thread - jint res = _javaVM->AttachCurrentThread(&_jniEnvPlay, NULL); - if ((res < 0) || !_jniEnvPlay) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, - "Could not attach playout thread to JVM (%d, %p)", - res, _jniEnvPlay); - return false; // Close down thread - } - - _playThreadIsInitialized = true; - } - - if (!_playing) - { - switch (_timeEventPlay.Wait(1000)) - { - case kEventSignaled: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "Playout thread event signal"); - _timeEventPlay.Reset(); - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, "Playout thread event error"); - return true; - case kEventTimeout: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "Playout thread event timeout"); - return true; - } - } - - Lock(); - - if (_startPlay) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startPlay true, performing initial actions"); - _startPlay = false; - _playing = true; - _playWarning = 0; - _playError = 0; - _playStartStopEvent.Set(); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Sent signal"); - } - - if (_playing) - { - WebRtc_Word8 playBuffer[2 * 480]; // Max 10 ms @ 48 kHz / 16 bit - WebRtc_UWord32 samplesToPlay = _samplingFreqOut * 10; - - // ask for new PCM data to be played out using the AudioDeviceBuffer - // ensure that this callback is executed without taking the - // audio-thread lock - UnLock(); - WebRtc_UWord32 nSamples = - _ptrAudioBuffer->RequestPlayoutData(samplesToPlay); - Lock(); - - // Check again since play may have stopped during unlocked period - if (!_playing) - { - UnLock(); - return true; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != samplesToPlay) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " invalid number of output samples(%d)", nSamples); - _playWarning = 1; - } - - // Copy data to our direct buffer (held by java sc object) - // todo: Give _javaDirectPlayBuffer directly to VoE? - memcpy(_javaDirectPlayBuffer, playBuffer, nSamples * 2); - - UnLock(); - - // Call java sc object method to process data in direct buffer - // Will block until data has been put in OS playout buffer - // (see java sc class) - jint res = _jniEnvPlay->CallIntMethod(_javaScObj, _javaMidPlayAudio, - 2 * nSamples); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "PlayAudio failed (%d)", res); - _playWarning = 1; - } - else if (res > 0) - { - // we are not recording and have got a delay value from playback - _delayPlayout = res / _samplingFreqOut; - } - // If 0 is returned we are recording and then play delay is updated - // in RecordProcess - - Lock(); - - } // _playing - - if (_shutdownPlayThread) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Detaching thread from Java VM"); - - // Detach thread from Java VM - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, "Could not detach playout thread from JVM"); - _shutdownPlayThread = false; - // If we say OK (i.e. set event) and close thread anyway, - // app will crash - } - else - { - _jniEnvPlay = NULL; - _shutdownPlayThread = false; - _playStartStopEvent.Set(); // Signal to Terminate() that we are done - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Sent signal"); - } - } - - UnLock(); - return true; -} - -// ---------------------------------------------------------------------------- -// RecThreadProcess -// ---------------------------------------------------------------------------- - -bool AudioDeviceAndroidJni::RecThreadProcess() -{ - if (!_recThreadIsInitialized) - { - // Do once when thread is started - - // Attach this thread to JVM - jint res = _javaVM->AttachCurrentThread(&_jniEnvRec, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !_jniEnvRec) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, "Could not attach rec thread to JVM (%d, %p)", - res, _jniEnvRec); - return false; // Close down thread - } - - _recThreadIsInitialized = true; - } - - // just sleep if rec has not started - if (!_recording) - { - switch (_timeEventRec.Wait(1000)) - { - case kEventSignaled: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "Recording thread event signal"); - _timeEventRec.Reset(); - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, "Recording thread event error"); - return true; - case kEventTimeout: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, "Recording thread event timeout"); - return true; - } - } - - Lock(); - - if (_startRec) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startRec true, performing initial actions"); - _startRec = false; - _recording = true; - _recWarning = 0; - _recError = 0; - _recStartStopEvent.Set(); - } - - if (_recording) - { - WebRtc_UWord32 samplesToRec = _samplingFreqIn * 10; - - // Call java sc object method to record data to direct buffer - // Will block until data has been recorded (see java sc class), - // therefore we must release the lock - UnLock(); - jint playDelayInSamples = _jniEnvRec->CallIntMethod(_javaScObj, - _javaMidRecAudio, - 2 * samplesToRec); - if (playDelayInSamples < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "RecordAudio failed"); - _recWarning = 1; - } - else - { - _delayPlayout = playDelayInSamples / _samplingFreqOut; - } - Lock(); - - // Check again since recording may have stopped during Java call - if (_recording) - { -// WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, -// "total delay is %d", msPlayDelay + _delayRecording); - - // Copy data to our direct buffer (held by java sc object) - // todo: Give _javaDirectRecBuffer directly to VoE? - // todo: Check count <= 480 ? - memcpy(_recBuffer, _javaDirectRecBuffer, 2 * samplesToRec); - - // store the recorded buffer (no action will be taken if the - // #recorded samples is not a full buffer) - _ptrAudioBuffer->SetRecordedBuffer(_recBuffer, samplesToRec); - - // store vqe delay values - _ptrAudioBuffer->SetVQEData(_delayPlayout, _delayRecording, 0); - - // deliver recorded samples at specified sample rate, mic level - // etc. to the observer using callback - UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - Lock(); - } - - } // _recording - - if (_shutdownRecThread) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Detaching rec thread from Java VM"); - - // Detach thread from Java VM - if (_javaVM->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, "Could not detach recording thread from JVM"); - _shutdownRecThread = false; - // If we say OK (i.e. set event) and close thread anyway, - // app will crash - } - else - { - _jniEnvRec = NULL; - _shutdownRecThread = false; - _recStartStopEvent.Set(); // Signal to Terminate() that we are done - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Sent signal rec"); - } - } - - UnLock(); - return true; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Android/audio_device_android_jni.h b/modules/audio_device/main/source/Android/audio_device_android_jni.h deleted file mode 100644 index f0ba70155..000000000 --- a/modules/audio_device/main/source/Android/audio_device_android_jni.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device interface (JNI/AudioTrack/AudioRecord usage) - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_JNI_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_JNI_H - -#include "audio_device_generic.h" -#include "critical_section_wrapper.h" - -#include // For accessing AudioDeviceAndroid java class - -namespace webrtc -{ -class EventWrapper; - -const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 44000; // Default is 44.1 kHz -const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 44000; // Default is 44.1 kHz - -const WebRtc_UWord32 N_REC_CHANNELS = 1; // default is mono recording -const WebRtc_UWord32 N_PLAY_CHANNELS = 1; // default is mono playout - -const WebRtc_UWord32 REC_BUF_SIZE_IN_SAMPLES = 480; // Handle max 10 ms @ 48 kHz - - -WebRtc_Word32 SetAndroidAudioDeviceObjects(void* javaVM, void* env, - void* context); - -class ThreadWrapper; - -class AudioDeviceAndroidJni: public AudioDeviceGeneric -{ -public: - AudioDeviceAndroidJni(const WebRtc_Word32 id); - ~AudioDeviceAndroidJni(); - - virtual WebRtc_Word32 ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const; - - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device); - - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const; - - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) - const; - - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - virtual WebRtc_Word32 SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer( - AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - - virtual WebRtc_Word32 SetRecordingSampleRate( - const WebRtc_UWord32 samplesPerSec); - virtual WebRtc_Word32 SetPlayoutSampleRate( - const WebRtc_UWord32 samplesPerSec); - - virtual WebRtc_Word32 SetLoudspeakerStatus(bool enable); - virtual WebRtc_Word32 GetLoudspeakerStatus(bool& enable) const; - -private: - // Lock - void Lock() - { - _critSect.Enter(); - }; - void UnLock() - { - _critSect.Leave(); - }; - - // Init - WebRtc_Word32 InitJavaResources(); - WebRtc_Word32 InitSampleRate(); - - // Threads - static bool RecThreadFunc(void*); - static bool PlayThreadFunc(void*); - bool RecThreadProcess(); - bool PlayThreadProcess(); - - // Misc - AudioDeviceBuffer* _ptrAudioBuffer; - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - - // Events - EventWrapper& _timeEventRec; - EventWrapper& _timeEventPlay; - EventWrapper& _recStartStopEvent; - EventWrapper& _playStartStopEvent; - - // Threads - ThreadWrapper* _ptrThreadPlay; - ThreadWrapper* _ptrThreadRec; - WebRtc_UWord32 _recThreadID; - WebRtc_UWord32 _playThreadID; - bool _playThreadIsInitialized; - bool _recThreadIsInitialized; - bool _shutdownPlayThread; - bool _shutdownRecThread; - - // Rec buffer - WebRtc_Word8 _recBuffer[2 * REC_BUF_SIZE_IN_SAMPLES]; - - // States - bool _recordingDeviceIsSpecified; - bool _playoutDeviceIsSpecified; - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _micIsInitialized; - bool _speakerIsInitialized; - - // Signal flags to threads - bool _startRec; - bool _stopRec; - bool _startPlay; - bool _stopPlay; - - // Warnings and errors - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - // Delay - WebRtc_UWord16 _delayPlayout; - WebRtc_UWord16 _delayRecording; - - // AGC state - bool _AGC; - - // Stored device properties - WebRtc_UWord16 _samplingFreqIn; // Sampling frequency for Mic - WebRtc_UWord16 _samplingFreqOut; // Sampling frequency for Speaker - WebRtc_UWord32 _maxSpeakerVolume; // The maximum speaker volume value - bool _loudSpeakerOn; - // Stores the desired audio source to use, set in SetRecordingDevice - int _recAudioSource; - - // JNI and Java - JavaVM* _javaVM; // denotes a Java VM - jobject _javaContext; // the application context - - JNIEnv* _jniEnvPlay; // The JNI env for playout thread - JNIEnv* _jniEnvRec; // The JNI env for recording thread - - jclass _javaScClass; // AudioDeviceAndroid class - jobject _javaScObj; // AudioDeviceAndroid object - - // The play buffer field in AudioDeviceAndroid object (global ref) - jobject _javaPlayBuffer; - // The rec buffer field in AudioDeviceAndroid object (global ref) - jobject _javaRecBuffer; - void* _javaDirectPlayBuffer; // Direct buffer pointer to play buffer - void* _javaDirectRecBuffer; // Direct buffer pointer to rec buffer - jmethodID _javaMidPlayAudio; // Method ID of play in AudioDeviceAndroid - jmethodID _javaMidRecAudio; // Method ID of rec in AudioDeviceAndroid -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_JNI_H diff --git a/modules/audio_device/main/source/Android/audio_device_android_native.cc b/modules/audio_device/main/source/Android/audio_device_android_native.cc deleted file mode 100644 index a561850a5..000000000 --- a/modules/audio_device/main/source/Android/audio_device_android_native.cc +++ /dev/null @@ -1,2235 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "audio_device_utility.h" -#include "audio_device_android_native.h" -#include "audio_device_config.h" - -#include "trace.h" -#include "thread_wrapper.h" -#include "event_wrapper.h" - -#ifdef WEBRTC_ANDROID_DEBUG -#include -#define WEBRTC_TRACE(a,b,c,...) __android_log_print( \ - ANDROID_LOG_DEBUG, "WebRTC AD native", __VA_ARGS__) -#endif - -namespace webrtc { - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceAndroidNative - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceAndroidNative::AudioDeviceAndroidNative(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _slEngineObject(NULL), - _slPlayer(NULL), - _slEngine(NULL), - _slPlayerPlay(NULL), - _slOutputMixObject(NULL), - _slSpeakerVolume(NULL), - _slRecorder(NULL), - _slRecorderRecord(NULL), - _slAudioIODeviceCapabilities(NULL), - _slRecorderSimpleBufferQueue(NULL), - _slMicVolume(NULL), - _micDeviceId(0), - _recQueueSeq(0), - _timeEventRec(*EventWrapper::Create()), - _ptrThreadRec(NULL), - _recThreadID(0), - _playQueueSeq(0), - _recCurrentSeq(0), - _recBufferTotalSize(0), - _recordingDeviceIsSpecified(false), - _playoutDeviceIsSpecified(false), - _initialized(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _micIsInitialized(false), - _speakerIsInitialized(false), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _playoutDelay(0), - _recordingDelay(0), - _AGC(false), - _adbSampleRate(0), - _samplingRateIn(SL_SAMPLINGRATE_16), - _samplingRateOut(SL_SAMPLINGRATE_16), - _maxSpeakerVolume(0), - _minSpeakerVolume(0), - _loudSpeakerOn(false) { - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", - __FUNCTION__); - memset(_playQueueBuffer, 0, sizeof(_playQueueBuffer)); - memset(_recQueueBuffer, 0, sizeof(_recQueueBuffer)); - memset(_recBuffer, 0, sizeof(_recBuffer)); - memset(_recLength, 0, sizeof(_recLength)); - memset(_recSeqNumber, 0, sizeof(_recSeqNumber)); -} - -AudioDeviceAndroidNative::~AudioDeviceAndroidNative() { - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", - __FUNCTION__); - - Terminate(); - - delete &_timeEventRec; - delete &_critSect; -} - -// ============================================================================ -// API -// ============================================================================ - -void AudioDeviceAndroidNative::AttachAudioBuffer( - AudioDeviceBuffer* audioBuffer) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // inform the AudioBuffer about default settings for this implementation - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); -} - -WebRtc_Word32 AudioDeviceAndroidNative::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - audioLayer = AudioDeviceModule::kPlatformDefaultAudio; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::Init() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) { - return 0; - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - SLEngineOption EngineOption[] = { (SLuint32) SL_ENGINEOPTION_THREADSAFE, - (SLuint32) SL_BOOLEAN_TRUE }; - WebRtc_Word32 res = slCreateEngine(&_slEngineObject, 1, EngineOption, 0, - NULL, NULL); - //WebRtc_Word32 res = slCreateEngine( &_slEngineObject, 0, NULL, 0, NULL, - // NULL); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create SL Engine Object"); - return -1; - } - /* Realizing the SL Engine in synchronous mode. */ - if ((*_slEngineObject)->Realize(_slEngineObject, SL_BOOLEAN_FALSE) - != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to Realize SL Engine"); - return -1; - } - - if ((*_slEngineObject)->GetInterface(_slEngineObject, SL_IID_ENGINE, - (void*) &_slEngine) - != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get SL Engine interface"); - return -1; - } - - // Check the sample rate to be used for playback and recording - if (InitSampleRate() != 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: Failed to init samplerate", __FUNCTION__); - return -1; - } - - // Set the audio device buffer sampling rate, we assume we get the same - // for play and record - if (_ptrAudioBuffer->SetRecordingSampleRate(_adbSampleRate) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set audio device buffer recording " - "sampling rate (%d)", _adbSampleRate); - } - if (_ptrAudioBuffer->SetPlayoutSampleRate(_adbSampleRate) < 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Could not set audio device buffer playout sampling " - "rate (%d)", _adbSampleRate); - } - - _initialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::Terminate() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) { - return 0; - } - - // RECORDING - StopRecording(); - - _micIsInitialized = false; - _recordingDeviceIsSpecified = false; - - // PLAYOUT - StopPlayout(); - - if (_slEngineObject != NULL) { - (*_slEngineObject)->Destroy(_slEngineObject); - _slEngineObject = NULL; - _slEngine = NULL; - } - - _initialized = false; - - return 0; -} - -bool AudioDeviceAndroidNative::Initialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_initialized); -} - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // We always assume it's available - available = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::InitSpeaker() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout already started"); - return -1; - } - - if (!_playoutDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout device is not specified"); - return -1; - } - - // Nothing needs to be done here, we use a flag to have consistent - // behavior with other platforms - _speakerIsInitialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // We always assume it's available - available = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::InitMicrophone() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording already started"); - return -1; - } - - if (!_recordingDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording device is not specified"); - return -1; - } - - // Nothing needs to be done here, we use a flag to have consistent - // behavior with other platforms - _micIsInitialized = true; - - return 0; -} - -bool AudioDeviceAndroidNative::SpeakerIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _speakerIsInitialized; -} - -bool AudioDeviceAndroidNative::MicrophoneIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _micIsInitialized; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerVolumeIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; // We assume we are always be able to set/get volume - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetSpeakerVolume( - WebRtc_UWord32 volume) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetSpeakerVolume(volume=%u)", volume); - - if (!_speakerIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - if (_slEngineObject == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "SetSpeakerVolume, SL Engine object doesnt exist"); - return -1; - } - - if (_slEngine == NULL) { - // Get the SL Engine Interface which is implicit - if ((*_slEngineObject)->GetInterface(_slEngineObject, SL_IID_ENGINE, - (void*) &_slEngine) - != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to GetInterface SL Engine Interface"); - return -1; - } - } - /* if (_slOutputMixObject == NULL && _slEngine != NULL) - { - // Set arrays required[] and iidArray[] for VOLUME interface - const SLInterfaceID ids[1] = {SL_IID_VOLUME}; - const SLboolean req[1] = {SL_BOOLEAN_TRUE}; - // Create Output Mix object to be used by player - if ((*_slEngine)->CreateOutputMix(_slEngine, &_slOutputMixObject, 1, ids, - req) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Output Mix object"); - return -1; - } - - // Realizing the Output Mix object in synchronous mode. - if ((*_slOutputMixObject)->Realize(_slOutputMixObject, - SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to realize the output mix object"); - return -1; - } - } - if (_slSpeakerVolume == NULL && _slOutputMixObject != NULL) - { - if ((*_slOutputMixObject)->GetInterface(_slOutputMixObject, - SL_IID_VOLUME, (void*)&_slSpeakerVolume) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get interface for Speaker Volume"); - return -1; - } - } - - WebRtc_Word32 vol(0); - vol = ((volume*(_maxSpeakerVolume-_minSpeakerVolume) + (int)(255/2)) - / (255)) + _minSpeakerVolume; - if (_slSpeakerVolume != NULL) - { - if ((*_slSpeakerVolume)->SetVolumeLevel(_slSpeakerVolume, vol) - != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to set speaker volume"); - return -1; - } - } - */ - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerVolume( - WebRtc_UWord32& volume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - /* if (!_speakerIsInitialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - if (_slEngineObject == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "SetSpeakerVolume, SL Engine object doesnt exist"); - return -1; - } - - if (_slEngine == NULL && _slEngineObject != NULL) - { - // Get the SL Engine Interface which is implicit - if ((*_slEngineObject)->GetInterface(_slEngineObject, - SL_IID_ENGINE, (void*)&_slEngine) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get SL Engine interface"); - return -1; - } - } - if (_slOutputMixObject == NULL && _slEngine != NULL) - { - // Set arrays required[] and iidArray[] for VOLUME interface - const SLInterfaceID ids[1] = {SL_IID_VOLUME}; - const SLboolean req[1] = {SL_BOOLEAN_TRUE}; - // Create Output Mix object to be used by player - if ((*_slEngine)->CreateOutputMix(_slEngine, - &_slOutputMixObject, 1, ids, req) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Output Mixer object"); - return -1; - } - // Realizing the Output Mix object in synchronous mode. - if ((*_slOutputMixObject)->Realize(_slOutputMixObject, - SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to realize Output Mix object"); - return -1; - } - } - if (_slSpeakerVolume == NULL && _slOutputMixObject != NULL) - { - if ((*_slOutputMixObject)->GetInterface(_slOutputMixObject, - SL_IID_VOLUME, (void*)&_slSpeakerVolume) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get speaker volume interface"); - return -1; - } - } - - SLmillibel vol(0); - if (_slSpeakerVolume != NULL) - { - if ((*_slSpeakerVolume)->GetVolumeLevel(_slSpeakerVolume, &vol) - != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get speaker volume"); - return -1; - } - } - // volume has to be mapped from millibel to [0, 255] - // volume = (WebRtc_UWord32) (((vol - _minSpeakerVolume) * 255 + - * (int)((_maxSpeakerVolume - _minSpeakerVolume)/2)) / - * (_maxSpeakerVolume - _minSpeakerVolume)); - */ - return 0; -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidNative::SetWaveOutVolume( - WebRtc_UWord16 /*volumeLeft*/, - WebRtc_UWord16 /*volumeRight*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s)", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidNative::WaveOutVolume( - WebRtc_UWord16& /*volumeLeft*/, - WebRtc_UWord16& /*volumeRight*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MaxSpeakerVolume( - WebRtc_UWord32& maxVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - - maxVolume = _maxSpeakerVolume; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MinSpeakerVolume( - WebRtc_UWord32& minVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1;// - } - - minVolume = _minSpeakerVolume; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Speaker not initialized"); - return -1; - } - stepSize = 1; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerMuteIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; // Speaker mute not supported on Android - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetSpeakerMute(bool /*enable*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SpeakerMute(bool& /*enabled*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneMuteIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; // Mic mute not supported on Android - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetMicrophoneMute(bool /*enable*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneMute( - bool& /*enabled*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneBoostIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; // Mic boost not supported on Android - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetMicrophoneBoost(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_micIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - if (enable) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneBoost(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_micIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Microphone not initialized"); - return -1; - } - - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StereoRecordingIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; // Stereo recording not supported on Android - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetStereoRecording(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (enable) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StereoRecording(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StereoPlayoutIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; // Stereo playout not supported on Android - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetStereoPlayout(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (enable) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Enabling not available"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StereoPlayout(bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - - -WebRtc_Word32 AudioDeviceAndroidNative::SetAGC(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "SetAGC(enable=%d)", - enable); - - _AGC = enable; - - return 0; -} - -bool AudioDeviceAndroidNative::AGC() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _AGC; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneVolumeIsAvailable( - bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetMicrophoneVolume( - WebRtc_UWord32 volume) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_slEngineObject == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "SetMicrophoneVolume, SL Engine Object doesnt exist"); - return -1; - } - - /* Get the optional DEVICE VOLUME interface from the engine */ - if (_slMicVolume == NULL) { - // Get the optional DEVICE VOLUME interface from the engine - if ((*_slEngineObject)->GetInterface(_slEngineObject, - SL_IID_DEVICEVOLUME, - (void*) &_slMicVolume) - != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Output Mix object"); - } - } - - if (_slMicVolume != NULL) { - WebRtc_Word32 vol(0); - vol = ((volume * (_maxSpeakerVolume - _minSpeakerVolume) + (int) (255 - / 2)) / (255)) + _minSpeakerVolume; - if ((*_slMicVolume)->SetVolume(_slMicVolume, _micDeviceId, vol) - != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Output Mix object"); - } - } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneVolume( - WebRtc_UWord32& /*volume*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - /* if (_slEngineObject == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "MicrophoneVolume, SL Engine Object doesnt exist"); - return -1; - } - - // Get the optional DEVICE VOLUME interface from the engine - if (_slMicVolume == NULL) - { - // Get the optional DEVICE VOLUME interface from the engine - if ((*_slEngineObject)->GetInterface(_slEngineObject, - SL_IID_DEVICEVOLUME, (void*)&_slMicVolume) != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Microphone Volume interface"); - } - } - - SLint32 vol(0); - if (_slMicVolume != NULL) - { - if ((*_slMicVolume)->GetVolume(_slMicVolume, _micDeviceId, &vol) - != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Microphone Volume"); - } - } - // volume has to be mapped from millibel to [0, 255] - // volume = (WebRtc_UWord32) (((vol - _minSpeakerVolume) * 255 + - * (int)((_maxSpeakerVolume - _minSpeakerVolume)/2)) / - * (_maxSpeakerVolume - _minSpeakerVolume)); - */ - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MaxMicrophoneVolume( - WebRtc_UWord32& /*maxVolume*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MinMicrophoneVolume( - WebRtc_UWord32& minVolume) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - minVolume = 0; - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - stepSize = 1; - return 0; -} - -WebRtc_Word16 AudioDeviceAndroidNative::PlayoutDevices() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return 1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetPlayoutDevice(WebRtc_UWord16 index) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_playIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout already initialized"); - return -1; - } - - if (0 != index) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Do nothing but set a flag, this is to have consistent behaviour - // with other platforms - _playoutDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (0 != index) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Return empty string - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid) { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (0 != index) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Return empty string - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid) { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -WebRtc_Word16 AudioDeviceAndroidNative::RecordingDevices() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return 1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetRecordingDevice( - WebRtc_UWord16 index) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_recIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording already initialized"); - return -1; - } - - if (0 != index) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Device index is out of range [0,0]"); - return -1; - } - - // Do nothing but set a flag, this is to have consistent behaviour with - // other platforms - _recordingDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::PlayoutIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) { - available = true; - } - - return res; -} - -WebRtc_Word32 AudioDeviceAndroidNative::RecordingIsAvailable(bool& available) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) { - available = true; - } - - return res; -} - -WebRtc_Word32 AudioDeviceAndroidNative::InitPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Not initialized"); - return -1; - } - - if (_playing) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout already started"); - return -1; - } - - if (!_playoutDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout device is not specified"); - return -1; - } - - if (_playIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already initialized"); - return 0; - } - - // Initialize the speaker - if (InitSpeaker() == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - if (_slEngineObject == NULL || _slEngine == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " SLObject or Engiine is NULL"); - return -1; - } - - WebRtc_Word32 res(-1); - SLDataFormat_PCM pcm; - SLDataSource audioSource; - SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue; - SLDataSink audioSink; - SLDataLocator_OutputMix locator_outputmix; - - // Create Output Mix object to be used by player - SLInterfaceID ids[N_MAX_INTERFACES]; - SLboolean req[N_MAX_INTERFACES]; - for (unsigned int i = 0; i < N_MAX_INTERFACES; i++) { - ids[i] = SL_IID_NULL; - req[i] = SL_BOOLEAN_FALSE; - } - ids[0] = SL_IID_ENVIRONMENTALREVERB; - res = (*_slEngine)->CreateOutputMix(_slEngine, &_slOutputMixObject, 1, ids, - req); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get SL Output Mix object"); - return -1; - } - // Realizing the Output Mix object in synchronous mode. - res = (*_slOutputMixObject)->Realize(_slOutputMixObject, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to realize SL Output Mix object"); - return -1; - } - // Get the speaker mixer - /* res = (*_slOutputMixObject)->GetInterface(_slOutputMixObject, - * SL_IID_VOLUME, (void*)&_slSpeakerVolume); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Speaker Mixer"); - return -1; - } - */ - // The code below can be moved to startplayout instead - /* Setup the data source structure for the buffer queue */ - simpleBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - /* Two buffers in our buffer queue, to have low latency*/ - simpleBufferQueue.numBuffers = N_PLAY_QUEUE_BUFFERS; - // TODO(xians), figure out if we should support stereo playout for android - /* Setup the format of the content in the buffer queue */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - // _samplingRateOut is initilized in InitSampleRate() - pcm.samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - pcm.channelMask = SL_SPEAKER_FRONT_CENTER; - pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; - audioSource.pFormat = (void *) &pcm; - audioSource.pLocator = (void *) &simpleBufferQueue; - /* Setup the data sink structure */ - locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - locator_outputmix.outputMix = _slOutputMixObject; - audioSink.pLocator = (void *) &locator_outputmix; - audioSink.pFormat = NULL; - - // Set arrays required[] and iidArray[] for SEEK interface - // (PlayItf is implicit) - ids[0] = SL_IID_BUFFERQUEUE; - ids[1] = SL_IID_EFFECTSEND; - req[0] = SL_BOOLEAN_TRUE; - req[1] = SL_BOOLEAN_TRUE; - // Create the music player - res = (*_slEngine)->CreateAudioPlayer(_slEngine, &_slPlayer, &audioSource, - &audioSink, 2, ids, req); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Audio Player"); - return -1; - } - - // Realizing the player in synchronous mode. - res = (*_slPlayer)->Realize(_slPlayer, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to realize the player"); - return -1; - } - // Get seek and play interfaces - res = (*_slPlayer)->GetInterface(_slPlayer, SL_IID_PLAY, - (void*) &_slPlayerPlay); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Player interface"); - return -1; - } - res = (*_slPlayer)->GetInterface(_slPlayer, SL_IID_BUFFERQUEUE, - (void*) &_slPlayerSimpleBufferQueue); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Player Simple Buffer Queue interface"); - return -1; - } - - // Setup to receive buffer queue event callbacks - res = (*_slPlayerSimpleBufferQueue)->RegisterCallback( - _slPlayerSimpleBufferQueue, - PlayerSimpleBufferQueueCallback, - this); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to register Player Callback"); - return -1; - } - _playIsInitialized = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceAndroidNative::InitRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Not initialized"); - return -1; - } - - if (_recording) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording already started"); - return -1; - } - - if (!_recordingDeviceIsSpecified) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording device is not specified"); - return -1; - } - - if (_recIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already initialized"); - return 0; - } - - // Initialize the microphone - if (InitMicrophone() == -1) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - if (_slEngineObject == NULL || _slEngine == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording object is NULL"); - return -1; - } - WebRtc_Word32 res(-1); - WebRtc_Word32 numInputs(0); - WebRtc_UWord32 inputDeviceIDs[N_MAX_INPUT_DEVICES]; - SLAudioInputDescriptor audioInputDescriptor; - SLDataSource audioSource; - SLDataLocator_IODevice micLocator; - SLDataSink audioSink; - SLDataFormat_PCM pcm; - SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue; - bool micAvailable(false); - WebRtc_UWord32 micDeviceID(0); - - /* // Get the Audio IO DEVICE CAPABILITIES interface, which is also implicit - res = (*_slEngineObject)->GetInterface(_slEngineObject, - SL_IID_AUDIOIODEVICECAPABILITIES, (void*)&_slAudioIODeviceCapabilities); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Audio IO device Capacilities interface"); - return -1; - } - numInputs = N_MAX_OUTPUT_DEVICES; - - res = (*_slAudioIODeviceCapabilities)->GetAvailableAudioInputs( - _slAudioIODeviceCapabilities, &numInputs, inputDeviceIDs); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get the number of Input Devices"); - return -1; - } - - // Search for either earpiece microphone or headset microphone input - // device - with a preference for the latter - for (int i=0;iQueryAudioInputCapabilities( - _slAudioIODeviceCapabilities, inputDeviceIDs[i], &audioInputDescriptor); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to query info for the Input Devices"); - return -1; - } - if((audioInputDescriptor.deviceConnection == - SL_DEVCONNECTION_ATTACHED_WIRED) - && (audioInputDescriptor.deviceScope == SL_DEVSCOPE_USER) - && (audioInputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET)) - { - micDeviceID = inputDeviceIDs[i]; - micAvailable = true; - break; - } - else if((audioInputDescriptor.deviceConnection == - SL_DEVCONNECTION_INTEGRATED) - && (audioInputDescriptor.deviceScope == SL_DEVSCOPE_USER) - && (audioInputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET)) - { - micDeviceID = inputDeviceIDs[i]; - micAvailable = true; - break; - } - } - // If neither of the preferred input audio devices is available, - // no point in continuing - if (!micAvailable) - { - return -1; - } - */ - - // Get the optional DEVICE VOLUME interface from the engine, - // should this be done somewhere else - /* res = (*_slEngineObject)->GetInterface(_slEngineObject, - * SL_IID_DEVICEVOLUME, (void*)&_slMicVolume); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Microphone Volume interface"); - return -1; - } - */ - // Setup the data source structure - micLocator.locatorType = SL_DATALOCATOR_IODEVICE; - micLocator.deviceType = SL_IODEVICE_AUDIOINPUT; - micLocator.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; //micDeviceID; - micLocator.device = NULL; - audioSource.pLocator = (void *) &micLocator; - audioSource.pFormat = NULL; - - /* Setup the data source structure for the buffer queue */ - simpleBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - simpleBufferQueue.numBuffers = N_REC_QUEUE_BUFFERS; - /* Setup the format of the content in the buffer queue */ - pcm.formatType = SL_DATAFORMAT_PCM; - pcm.numChannels = 1; - // _samplingRateIn is initialized in initSampleRate() - pcm.samplesPerSec = SL_SAMPLINGRATE_16; - pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - pcm.containerSize = 16; - pcm.channelMask = SL_SPEAKER_FRONT_CENTER; - pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; - audioSink.pFormat = (void *) &pcm; - audioSink.pLocator = (void *) &simpleBufferQueue; - - // Create audio recorder - const SLInterfaceID id[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - const SLboolean req[1] = { SL_BOOLEAN_TRUE }; - res = (*_slEngine)->CreateAudioRecorder(_slEngine, &_slRecorder, - &audioSource, &audioSink, 1, id, - req); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create Recorder"); - return -1; - } - - // Realizing the recorder in synchronous mode. - res = (*_slRecorder)->Realize(_slRecorder, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to realize Recorder"); - return -1; - } - - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " get Recorder interface"); - // Get the RECORD interface - it is an implicit interface - res = (*_slRecorder)->GetInterface(_slRecorder, SL_IID_RECORD, - (void*) &_slRecorderRecord); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Recorder interface"); - return -1; - } - - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " get Recorder Simple Buffer Queue 2"); - // Get the simpleBufferQueue interface - res = (*_slRecorder)->GetInterface(_slRecorder, - SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - (void*) &_slRecorderSimpleBufferQueue); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Recorder Simple Buffer Queue"); - return -1; - } - - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " register Recorder Callback 2"); - // Setup to receive buffer queue event callbacks - res = (*_slRecorderSimpleBufferQueue)->RegisterCallback( - _slRecorderSimpleBufferQueue, - RecorderSimpleBufferQueueCallback, - this); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to register Recorder Callback"); - return -1; - } - - _recIsInitialized = true; - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StartRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording not initialized"); - return -1; - } - - if (_recording) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording already started"); - return 0; - } - - if (_slRecorderRecord == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " RecordITF is NULL"); - return -1; - } - - if (_slRecorderSimpleBufferQueue == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recorder Simple Buffer Queue is NULL"); - return -1; - } - - // Reset recording buffer - memset(_recQueueBuffer, 0, sizeof(_recQueueBuffer)); // empty the queue - _recQueueSeq = 0; - - const char* threadName = "webrtc_native_audio_capture_thread"; - _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, - kRealtimePriority, threadName); - if (_ptrThreadRec == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the rec audio thread"); - return -1; - } - - unsigned int threadID(0); - if (!_ptrThreadRec->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the rec audio thread"); - delete _ptrThreadRec; - _ptrThreadRec = NULL; - return -1; - } - _recThreadID = threadID; - _recThreadIsInitialized = true; - memset(_recBuffer, 0, sizeof(_recBuffer)); - memset(_recLength, 0, sizeof(_recLength)); - memset(_recSeqNumber, 0, sizeof(_recSeqNumber)); - _recCurrentSeq = 0; - _recBufferTotalSize = 0; - _recWarning = 0; - _recError = 0; - - // Enqueue N_REC_QUEUE_BUFFERS -1 zero buffers to get the ball rolling - // find out how it behaves when the sample rate is 44100 - WebRtc_Word32 res(-1); - WebRtc_UWord32 nSample10ms = _adbSampleRate / 100; - for (int i = 0; i < (N_REC_QUEUE_BUFFERS - 1); i++) { - // We assign 10ms buffer to each queue, size given in bytes. - res = (*_slRecorderSimpleBufferQueue)->Enqueue( - _slRecorderSimpleBufferQueue, - (void*) _recQueueBuffer[_recQueueSeq], - 2 * nSample10ms); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to Enqueue Empty Buffer to recorder"); - return -1; - } - _recQueueSeq++; - } - // Record the audio - res = (*_slRecorderRecord)->SetRecordState(_slRecorderRecord, - SL_RECORDSTATE_RECORDING); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to start recording"); - return -1; - } - _recording = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StopRecording() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recording is not initialized"); - return 0; - } - - // Stop the recording thread - if (_ptrThreadRec != NULL) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Stopping capture thread"); - bool res = _ptrThreadRec->Stop(); - if (!res) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Failed to stop Capture thread "); - } else { - delete _ptrThreadRec; - _ptrThreadRec = NULL; - _recThreadIsInitialized = false; - } - } - - if ((_slRecorderRecord != NULL) && (_slRecorder != NULL)) { - // Record the audio - WebRtc_Word32 res = (*_slRecorderRecord)->SetRecordState( - _slRecorderRecord, - SL_RECORDSTATE_STOPPED); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to stop recording"); - return -1; - } - res = (*_slRecorderSimpleBufferQueue)->Clear( - _slRecorderSimpleBufferQueue); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to clear recorder buffer queue"); - return -1; - } - - // Destroy the recorder object - (*_slRecorder)->Destroy(_slRecorder); - _slRecorder = NULL; - _slRecorderRecord = NULL; - _slRecorderRecord = NULL; - } - - _recIsInitialized = false; - _recording = false; - _recWarning = 0; - _recError = 0; - _recQueueSeq = 0; - return 0; -} - -bool AudioDeviceAndroidNative::RecordingIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _recIsInitialized; -} - - -bool AudioDeviceAndroidNative::Recording() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _recording; -} - -bool AudioDeviceAndroidNative::PlayoutIsInitialized() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _playIsInitialized; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StartPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playout not initialized"); - return -1; - } - - if (_playing) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout already started"); - return 0; - } - - if (_slPlayerPlay == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " PlayItf is NULL"); - return -1; - } - if (_slPlayerSimpleBufferQueue == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " PlayerSimpleBufferQueue is NULL"); - return -1; - } - - _recQueueSeq = 0; - - WebRtc_Word32 res(-1); - /* Enqueue a set of zero buffers to get the ball rolling */ - WebRtc_UWord32 nSample10ms = _adbSampleRate / 100; - WebRtc_Word8 playBuffer[2 * nSample10ms]; - WebRtc_UWord32 noSamplesOut(0); - /* res = (*_slPlayerSimpleBufferQueue)->Clear(_slPlayerSimpleBufferQueue); - if (res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " player simpler buffer queue clean failed"); - //return ; dong return - } - */// for (int i = 0; i<(N_PLAY_QUEUE_BUFFERS -1); i++) - { - noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(nSample10ms); - //Lock(); - // Get data from Audio Device Buffer - noSamplesOut = _ptrAudioBuffer->GetPlayoutData(playBuffer); - // Insert what we have in data buffer - memcpy(_playQueueBuffer[_playQueueSeq], playBuffer, 2 * noSamplesOut); - //UnLock(); - - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - // "_playQueueSeq (%u) noSamplesOut (%d)", _playQueueSeq, - //noSamplesOut); - // write the buffer data we got from VoE into the device - res = (*_slPlayerSimpleBufferQueue)->Enqueue( - _slPlayerSimpleBufferQueue, - (void*) _playQueueBuffer[_playQueueSeq], - 2 * noSamplesOut); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " player simpler buffer queue Enqueue failed, %d", - noSamplesOut); - //return ; dong return - } - _playQueueSeq = (_playQueueSeq + 1) % N_PLAY_QUEUE_BUFFERS; - } - - // Play the PCM samples using a buffer queue - res = (*_slPlayerPlay)->SetPlayState(_slPlayerPlay, SL_PLAYSTATE_PLAYING); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to start playout"); - return -1; - } - - _playWarning = 0; - _playError = 0; - _playing = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::StopPlayout() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout is not initialized"); - return 0; - } - - if ((_slPlayerPlay != NULL) && (_slOutputMixObject == NULL) && (_slPlayer - == NULL)) { - // Make sure player is stopped - WebRtc_Word32 res = - (*_slPlayerPlay)->SetPlayState(_slPlayerPlay, - SL_PLAYSTATE_STOPPED); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to stop playout"); - return -1; - } - res = (*_slPlayerSimpleBufferQueue)->Clear(_slPlayerSimpleBufferQueue); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to clear recorder buffer queue"); - return -1; - } - - // Destroy the player - (*_slPlayer)->Destroy(_slPlayer); - // Destroy Output Mix object - (*_slOutputMixObject)->Destroy(_slOutputMixObject); - _slPlayer = NULL; - _slPlayerPlay = NULL; - _slPlayerSimpleBufferQueue = NULL; - _slOutputMixObject = NULL; - } - - _playIsInitialized = false; - _playing = false; - _playWarning = 0; - _playError = 0; - _playQueueSeq = 0; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::PlayoutDelay(WebRtc_UWord16& delayMS) const { - delayMS = _playoutDelay; - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::RecordingDelay(WebRtc_UWord16& delayMS) const { - delayMS = _recordingDelay; - - return 0; -} - -bool AudioDeviceAndroidNative::Playing() const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return _playing; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetPlayoutBuffer( - const AudioDeviceModule::BufferType /*type*/, - WebRtc_UWord16 /*sizeMS*/) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceAndroidNative::PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - type = AudioDeviceModule::kAdaptiveBufferSize; - sizeMS = _playoutDelay; // Set to current playout delay - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::CPULoad(WebRtc_UWord16& /*load*/) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -bool AudioDeviceAndroidNative::PlayoutWarning() const { - return (_playWarning > 0); -} - -bool AudioDeviceAndroidNative::PlayoutError() const { - return (_playError > 0); -} - -bool AudioDeviceAndroidNative::RecordingWarning() const { - return (_recWarning > 0); -} - -bool AudioDeviceAndroidNative::RecordingError() const { - return (_recError > 0); -} - -void AudioDeviceAndroidNative::ClearPlayoutWarning() { - _playWarning = 0; -} - -void AudioDeviceAndroidNative::ClearPlayoutError() { - _playError = 0; -} - -void AudioDeviceAndroidNative::ClearRecordingWarning() { - _recWarning = 0; -} - -void AudioDeviceAndroidNative::ClearRecordingError() { - _recError = 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::SetLoudspeakerStatus(bool enable) { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s(%d)", - __FUNCTION__, enable); - - // if (!_javaContext) - // { - // WEBRTC_TRACE(kTraceError, kTraceUtility, -1, " Context is not set"); - // return -1; - // } - - // get the JNI env for this thread - // JNIEnv *env; - // bool isAttached = false; - - // if (_javaVM->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) - // { - // try to attach the thread and get the env - // Attach this thread to JVMslPlayoutCallback - // jint res = _javaVM->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - // if ((res < 0) || !env) - // { - // WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - // " Could not attach thread to JVM (%d, %p)", res, env); - // return -1; - // } - // isAttached = true; - // } - - // get the method ID - // jmethodID setPlayoutSpeakerID = env->GetMethodID(_javaScClass, - // "SetPlayoutSpeaker", "(Z)I"); - - // call java sc object method - // jint res = env->CallIntMethod(_javaScObj, setPlayoutSpeakerID, enable); - // if (res < 0) - // { - // WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - // " SetPlayoutSpeaker failed (%d)", res); - // return -1; - // } - - _loudSpeakerOn = enable; - - // Detach this thread if it was attached - // if (isAttached) - // { - // if (_javaVM->DetachCurrentThread() < 0) - // { - // WEBRTC_TRACE(kTraceWarning, kTraceUtility, -1, - // " Could not detach thread from JVM"); - // } - // } - - return 0; -} - -WebRtc_Word32 AudioDeviceAndroidNative::GetLoudspeakerStatus( - bool& enabled) const { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - enabled = _loudSpeakerOn; - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -void AudioDeviceAndroidNative::PlayerSimpleBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, - void *pContext) { - AudioDeviceAndroidNative* ptrThis = - static_cast (pContext); - ptrThis->PlayerSimpleBufferQueueCallbackHandler(queueItf); -} - -void AudioDeviceAndroidNative::PlayerSimpleBufferQueueCallbackHandler( - SLAndroidSimpleBufferQueueItf queueItf) { - WebRtc_Word32 res; - //Lock(); - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - //"_playQueueSeq (%u)", _playQueueSeq); - if (_playing && (_playQueueSeq < N_PLAY_QUEUE_BUFFERS)) { - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - //_id, "playout callback "); - unsigned int noSamp10ms = _adbSampleRate / 100; - // Max 10 ms @ samplerate kHz / 16 bit - WebRtc_Word8 playBuffer[2 * noSamp10ms]; - int noSamplesOut = 0; - - // Assumption for implementation - // assert(PLAYBUFSIZESAMPLES == noSamp10ms); - - // TODO(xians), update the playout delay - //UnLock(); - - noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(noSamp10ms); - //Lock(); - // Get data from Audio Device Buffer - noSamplesOut = _ptrAudioBuffer->GetPlayoutData(playBuffer); - // Cast OK since only equality comparison - if (noSamp10ms != (unsigned int) noSamplesOut) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "noSamp10ms (%u) != noSamplesOut (%d)", noSamp10ms, - noSamplesOut); - - if (_playWarning > 0) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Pending play warning exists"); - } - _playWarning = 1; - } - // Insert what we have in data buffer - memcpy(_playQueueBuffer[_playQueueSeq], playBuffer, 2 * noSamplesOut); - //UnLock(); - - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - //"_playQueueSeq (%u) noSamplesOut (%d)", _playQueueSeq, noSamplesOut); - // write the buffer data we got from VoE into the device - res - = (*_slPlayerSimpleBufferQueue)->Enqueue( - _slPlayerSimpleBufferQueue, - _playQueueBuffer[_playQueueSeq], - 2 * noSamplesOut); - if (res != SL_RESULT_SUCCESS) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " player simpler buffer queue Enqueue failed, %d", - noSamplesOut); - return; - } - // update the playout delay - UpdatePlayoutDelay(noSamplesOut); - // update the play buffer sequency - _playQueueSeq = (_playQueueSeq + 1) % N_PLAY_QUEUE_BUFFERS; - } -} - -void AudioDeviceAndroidNative::RecorderSimpleBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, - void *pContext) { - AudioDeviceAndroidNative* ptrThis = - static_cast (pContext); - ptrThis->RecorderSimpleBufferQueueCallbackHandler(queueItf); -} - -void AudioDeviceAndroidNative::RecorderSimpleBufferQueueCallbackHandler( - SLAndroidSimpleBufferQueueItf queueItf) { - WebRtc_Word32 res; - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - //" RecorderSimpleBufferQueueCallbackHandler"); - if (_recording) { - // Insert all data in temp buffer into recording buffers - // There is zero or one buffer partially full at any given time, - // all others are full or empty - // Full means filled with noSamp10ms samples. - - const unsigned int noSamp10ms = _adbSampleRate / 100; - // WebRtc_UWord16 queuePos = 0; - // WebRtc_UWord16 checkQueuePos = 0; - unsigned int dataPos = 0; - WebRtc_UWord16 bufPos = 0; - WebRtc_Word16 insertPos = -1; - unsigned int nCopy = 0; // Number of samples to copy - // WebRtc_Word32 isData = 0; - - while (dataPos < noSamp10ms)//REC_BUF_SIZE_IN_SAMPLES) //noSamp10ms) - - { - // Loop over all recording buffers or until we find the partially - // full buffer - // First choice is to insert into partially full buffer, - // second choice is to insert into empty buffer - bufPos = 0; - insertPos = -1; - nCopy = 0; - while (bufPos < N_REC_BUFFERS) - { - if ((_recLength[bufPos] > 0) && (_recLength[bufPos] - < noSamp10ms)) - { - // Found the partially full buffer - insertPos = static_cast (bufPos); - bufPos = N_REC_BUFFERS; // Don't need to search more - } - else if ((-1 == insertPos) && (0 == _recLength[bufPos])) - { - // Found an empty buffer - insertPos = static_cast (bufPos); - } - ++bufPos; - } - - if (insertPos > -1) - { - // We found a non-full buffer, copy data from the buffer queue - // o recBuffer - unsigned int dataToCopy = noSamp10ms - dataPos; - unsigned int currentRecLen = _recLength[insertPos]; - unsigned int roomInBuffer = noSamp10ms - currentRecLen; - nCopy = (dataToCopy < roomInBuffer ? dataToCopy : roomInBuffer); - memcpy(&_recBuffer[insertPos][currentRecLen], - &_recQueueBuffer[_recQueueSeq][dataPos], - nCopy * sizeof(short)); - if (0 == currentRecLen) - { - _recSeqNumber[insertPos] = _recCurrentSeq; - ++_recCurrentSeq; - } - _recBufferTotalSize += nCopy; - // Has to be done last to avoid interrupt problems - // between threads - _recLength[insertPos] += nCopy; - dataPos += nCopy; - } - else - { - // Didn't find a non-full buffer - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " Could not insert into recording buffer"); - if (_recWarning > 0) - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " Pending rec warning exists"); - } - _recWarning = 1; - dataPos = noSamp10ms; // Don't try to insert more - } - } - - // clean the queue buffer - // Start with empty buffer - memset(_recQueueBuffer[_recQueueSeq], 0, 2 * REC_BUF_SIZE_IN_SAMPLES); - // write the empty buffer to the queue - res = (*_slRecorderSimpleBufferQueue)->Enqueue( - _slRecorderSimpleBufferQueue, - (void*) _recQueueBuffer[_recQueueSeq], - 2 * noSamp10ms); - if (res != SL_RESULT_SUCCESS) { - return; - } - // update the rec queue seq - _recQueueSeq = (_recQueueSeq + 1) % N_REC_QUEUE_BUFFERS; - // wake up the recording thread - _timeEventRec.Set(); - } -} - -void AudioDeviceAndroidNative::CheckErr(SLresult res) { - if (res != SL_RESULT_SUCCESS) { - // Debug printing to be placed here - exit(-1); - } -} - -void AudioDeviceAndroidNative::UpdatePlayoutDelay(WebRtc_UWord32 nSamplePlayed) { - // currently just do some simple calculation, should we setup a timer for - // the callback to have a more accurate delay - // Android CCD asks for 10ms as the maximum warm output latency, so we - // simply add (nPlayQueueBuffer -1 + 0.5)*10ms - // This playout delay should be seldom changed - _playoutDelay = (N_PLAY_QUEUE_BUFFERS - 0.5) * 10 + N_PLAY_QUEUE_BUFFERS - * nSamplePlayed / (_adbSampleRate / 1000); -} - -void AudioDeviceAndroidNative::UpdateRecordingDelay() { - // // Android CCD asks for 10ms as the maximum warm input latency, - // so we simply add 10ms - _recordingDelay = 10; - const WebRtc_UWord32 noSamp10ms = _adbSampleRate / 100; - // if (_recBufferTotalSize > noSamp10ms) - // { - _recordingDelay += (N_REC_QUEUE_BUFFERS * noSamp10ms) / (_adbSampleRate - / 1000); - // } -} - -WebRtc_Word32 AudioDeviceAndroidNative::InitSampleRate() { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_Word32 res(-1); - WebRtc_Word32 numOutputs(0); - WebRtc_UWord32 headsetDeviceID(0); - WebRtc_UWord32 earpieceDeviceID(0); - bool headsetAvailable(false); - bool earpieceAvailable(false); - bool foundSampleRate(false); - WebRtc_UWord32 outputDeviceIDs[N_MAX_OUTPUT_DEVICES]; - SLAudioOutputDescriptor audioOutputDescriptor; - - if (_slEngineObject == NULL) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " SL Object is NULL"); - return -1; - } - - /* // Get the Audio IO DEVICE CAPABILITIES interface - res = (*_slEngineObject)->GetInterface(_slEngineObject, - SL_IID_AUDIOIODEVICECAPABILITIES, (void*)&_slAudioIODeviceCapabilities); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get Device Capabilities interface"); - return -1; - } - numOutputs = N_MAX_OUTPUT_DEVICES; - res = (*_slAudioIODeviceCapabilities)->GetAvailableAudioOutputs( - _slAudioIODeviceCapabilities, &numOutputs, outputDeviceIDs); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to get number of Output Devices"); - return -1; - } - - // Search for headset output and phone handsfree speaker device, - // we prefer headset to earpiece - for (int i=0;iQueryAudioOutputCapabilities( - _slAudioIODeviceCapabilities, outputDeviceIDs[i], &audioOutputDescriptor); - if ( res != SL_RESULT_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to query Output Devices info"); - return -1; - } - if((audioOutputDescriptor.deviceConnection == - SL_DEVCONNECTION_ATTACHED_WIRED)&& - (audioOutputDescriptor.deviceScope == SL_DEVSCOPE_USER)&& - (audioOutputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET)) - { - headsetDeviceID = outputDeviceIDs[i]; - headsetAvailable = true; - break; - } - else if((audioOutputDescriptor.deviceConnection == - SL_DEVCONNECTION_INTEGRATED)&& - (audioOutputDescriptor.deviceScope == SL_DEVSCOPE_ENVIRONMENT)&& - (audioOutputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET)) - { - earpieceDeviceID = outputDeviceIDs[i]; - earpieceAvailable = true; - break; - } - } - // Neither headset output nor phone handsfree speaker is available - if (headsetAvailable == false && earpieceAvailable == false) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " No playout device"); - return -1; - } - - _sampleRateInMilliHz = SL_SAMPLINGRATE_16; - if (audioOutputDescriptor.isFreqRangeContinuous == SL_BOOLEAN_FALSE) - { - while (!foundSampleRate) - { - for (int i=0; i (pThis)->RecThreadProcess()); -} - -bool AudioDeviceAndroidNative::RecThreadProcess() { - - // Lock(); - // Wait for 100ms for the signal from device callback - // In case no callback comes in 100ms, we check the buffer anyway - _timeEventRec.Wait(100); - - int bufPos = 0; - unsigned int lowestSeq = 0; - int lowestSeqBufPos = 0; - bool foundBuf = true; - const unsigned int noSamp10ms = _adbSampleRate / 100; - - while (foundBuf) - { - // Check if we have any buffer with data to insert into the - // Audio Device Buffer, - // and find the one with the lowest seq number - foundBuf = false; - - for (bufPos = 0; bufPos < N_REC_BUFFERS; ++bufPos) - { - if (noSamp10ms == _recLength[bufPos]) - { - if (!foundBuf) { - lowestSeq = _recSeqNumber[bufPos]; - lowestSeqBufPos = bufPos; - foundBuf = true; - } else if (_recSeqNumber[bufPos] < lowestSeq) - { - lowestSeq = _recSeqNumber[bufPos]; - lowestSeqBufPos = bufPos; - } - } - } // for - - // Insert data into the Audio Device Buffer if found any - if (foundBuf) - { - UpdateRecordingDelay(); - // Set the recorded buffer - _ptrAudioBuffer->SetRecordedBuffer(_recBuffer[lowestSeqBufPos], - noSamp10ms); - - // Don't need to set the current mic level in ADB since we only - // support digital AGC, - // and besides we cannot get or set the iPhone mic level anyway. - - // Set VQE info, use clockdrift == 0 - _ptrAudioBuffer->SetVQEData(_playoutDelay, _recordingDelay, 0); - - // Deliver recorded samples at specified sample rate, mic level - // etc. to the observer using callback - //UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - //Lock(); - - // Make buffer available - _recSeqNumber[lowestSeqBufPos] = 0; - _recBufferTotalSize -= _recLength[lowestSeqBufPos]; - // Must be done last to avoid interrupt problems between threads - _recLength[lowestSeqBufPos] = 0; - } - - } // while (foundBuf) - //UnLock(); - return true; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Android/audio_device_android_native.h b/modules/audio_device/main/source/Android/audio_device_android_native.h deleted file mode 100644 index c10b3125e..000000000 --- a/modules/audio_device/main/source/Android/audio_device_android_native.h +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_NATIVE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_NATIVE_H - -#include "audio_device_generic.h" -#include "critical_section_wrapper.h" - -#include // For accessing AudioDeviceAndroid.java -#include -#include - -#include -#include -#include - -namespace webrtc -{ -class EventWrapper; - -const WebRtc_UWord32 N_MAX_INTERFACES = 3; -const WebRtc_UWord32 N_MAX_OUTPUT_DEVICES = 6; -const WebRtc_UWord32 N_MAX_INPUT_DEVICES = 3; - -const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 16000;//44000; // Default fs -const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 16000;//44000; // Default fs - -const WebRtc_UWord32 N_REC_CHANNELS = 1; // default is mono recording -const WebRtc_UWord32 N_PLAY_CHANNELS = 1; // default is mono playout - -const WebRtc_UWord32 REC_BUF_SIZE_IN_SAMPLES = 480; // Handle max 10 ms @ 48 kHz -const WebRtc_UWord32 PLAY_BUF_SIZE_IN_SAMPLES = 480; - -// Number of the buffers in playout queue -const WebRtc_UWord16 N_PLAY_QUEUE_BUFFERS = 2; -// Number of buffers in recording queue -const WebRtc_UWord16 N_REC_QUEUE_BUFFERS = 2; -// Number of 10 ms recording blocks in rec buffer -const WebRtc_UWord16 N_REC_BUFFERS = 20; - -class ThreadWrapper; - -class AudioDeviceAndroidNative: public AudioDeviceGeneric -{ -public: - AudioDeviceAndroidNative(const WebRtc_Word32 id); - ~AudioDeviceAndroidNative(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 - ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 - PlayoutDeviceName(WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 - RecordingDeviceName(WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 - SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 - SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - SLPlayItf playItf; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 - MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 - SetPlayoutBuffer(const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - - // Error and warning information - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - - // Attach audio buffer - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - - // Speaker audio routing - virtual WebRtc_Word32 SetLoudspeakerStatus(bool enable); - virtual WebRtc_Word32 GetLoudspeakerStatus(bool& enable) const; - -private: - // Lock - void Lock() - { - _critSect.Enter(); - }; - void UnLock() - { - _critSect.Leave(); - }; - - static void PlayerSimpleBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, - void *pContext); - void PlayerSimpleBufferQueueCallbackHandler( - SLAndroidSimpleBufferQueueItf queueItf); - static void RecorderSimpleBufferQueueCallback( - SLAndroidSimpleBufferQueueItf queueItf, - void *pContext); - void RecorderSimpleBufferQueueCallbackHandler( - SLAndroidSimpleBufferQueueItf queueItf); - void CheckErr(SLresult res); - - // Delay updates - void UpdateRecordingDelay(); - void UpdatePlayoutDelay(WebRtc_UWord32 nSamplePlayed); - - // Init - WebRtc_Word32 InitSampleRate(); - - // Threads - static bool RecThreadFunc(void*); - static bool PlayThreadFunc(void*); - bool RecThreadProcess(); - bool PlayThreadProcess(); - - // Misc - AudioDeviceBuffer* _ptrAudioBuffer; - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - - // audio unit - SLObjectItf _slEngineObject; - - // playout device - SLObjectItf _slPlayer; - SLEngineItf _slEngine; - SLPlayItf _slPlayerPlay; - SLAndroidSimpleBufferQueueItf _slPlayerSimpleBufferQueue; - SLObjectItf _slOutputMixObject; - SLVolumeItf _slSpeakerVolume; - - // recording device - SLObjectItf _slRecorder; - SLRecordItf _slRecorderRecord; - SLAudioIODeviceCapabilitiesItf _slAudioIODeviceCapabilities; - SLAndroidSimpleBufferQueueItf _slRecorderSimpleBufferQueue; - SLDeviceVolumeItf _slMicVolume; - - WebRtc_UWord32 _micDeviceId; - - // Events - EventWrapper& _timeEventRec; - // Threads - ThreadWrapper* _ptrThreadRec; - WebRtc_UWord32 _recThreadID; - // TODO(xians), remove the following flag - bool _recThreadIsInitialized; - - // Playout buffer - WebRtc_Word8 _playQueueBuffer[N_PLAY_QUEUE_BUFFERS][2 - * PLAY_BUF_SIZE_IN_SAMPLES]; - WebRtc_UWord32 _playQueueSeq; - // Recording buffer - WebRtc_Word8 _recQueueBuffer[N_REC_QUEUE_BUFFERS][2 - * REC_BUF_SIZE_IN_SAMPLES]; - WebRtc_UWord32 _recQueueSeq; - WebRtc_Word8 _recBuffer[N_REC_BUFFERS][2*REC_BUF_SIZE_IN_SAMPLES]; - WebRtc_UWord32 _recLength[N_REC_BUFFERS]; - WebRtc_UWord32 _recSeqNumber[N_REC_BUFFERS]; - WebRtc_UWord32 _recCurrentSeq; - // Current total size all data in buffers, used for delay estimate - WebRtc_UWord32 _recBufferTotalSize; - - // States - bool _recordingDeviceIsSpecified; - bool _playoutDeviceIsSpecified; - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _micIsInitialized; - bool _speakerIsInitialized; - - // Warnings and errors - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - // Delay - WebRtc_UWord16 _playoutDelay; - WebRtc_UWord16 _recordingDelay; - - // AGC state - bool _AGC; - - // The sampling rate to use with Audio Device Buffer - WebRtc_UWord32 _adbSampleRate; - // Stored device properties - WebRtc_UWord32 _samplingRateIn; // Sampling frequency for Mic - WebRtc_UWord32 _samplingRateOut; // Sampling frequency for Speaker - WebRtc_UWord32 _maxSpeakerVolume; // The maximum speaker volume value - WebRtc_UWord32 _minSpeakerVolume; // The minimum speaker volume value - bool _loudSpeakerOn; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_ANDROID_NATIVE_H diff --git a/modules/audio_device/main/source/Android/audio_device_utility_android.cc b/modules/audio_device/main/source/Android/audio_device_utility_android.cc deleted file mode 100644 index 5f5aceb60..000000000 --- a/modules/audio_device/main/source/Android/audio_device_utility_android.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device utility implementation - */ - -#include "audio_device_utility_android.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -namespace webrtc -{ - -AudioDeviceUtilityAndroid::AudioDeviceUtilityAndroid(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _id(id), - _lastError(AudioDeviceModule::kAdmErrNone) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); -} - -AudioDeviceUtilityAndroid::~AudioDeviceUtilityAndroid() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - } - - delete &_critSect; -} - -WebRtc_Word32 AudioDeviceUtilityAndroid::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - " OS info: %s", "Android"); - - return 0; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Android/audio_device_utility_android.h b/modules/audio_device/main/source/Android/audio_device_utility_android.h deleted file mode 100644 index 81f685af3..000000000 --- a/modules/audio_device/main/source/Android/audio_device_utility_android.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device utility interface - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_ANDROID_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_ANDROID_H - -#include "audio_device_utility.h" -#include "audio_device.h" - -namespace webrtc -{ -class CriticalSectionWrapper; - -class AudioDeviceUtilityAndroid: public AudioDeviceUtility -{ -public: - AudioDeviceUtilityAndroid(const WebRtc_Word32 id); - ~AudioDeviceUtilityAndroid(); - - virtual WebRtc_Word32 Init(); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - AudioDeviceModule::ErrorCode _lastError; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_ANDROID_H diff --git a/modules/audio_device/main/source/Android/org/webrtc/voiceengine/AudioDeviceAndroid.java b/modules/audio_device/main/source/Android/org/webrtc/voiceengine/AudioDeviceAndroid.java deleted file mode 100644 index b56085bd3..000000000 --- a/modules/audio_device/main/source/Android/org/webrtc/voiceengine/AudioDeviceAndroid.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device test app - */ - -package org.webrtc.voiceengine; - -import java.nio.ByteBuffer; -import java.util.concurrent.locks.ReentrantLock; - -import android.content.Context; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.AudioTrack; -import android.util.Log; - - -class AudioDeviceAndroid { - private AudioTrack _audioTrack = null; - private AudioRecord _audioRecord = null; - - private Context _context; - private AudioManager _audioManager; - - private ByteBuffer _playBuffer; - private ByteBuffer _recBuffer; - private byte[] _tempBufPlay; - private byte[] _tempBufRec; - - private final ReentrantLock _playLock = new ReentrantLock(); - private final ReentrantLock _recLock = new ReentrantLock(); - - private boolean _doPlayInit = true; - private boolean _doRecInit = true; - private boolean _isRecording = false; - private boolean _isPlaying = false; - - private int _bufferedRecSamples = 0; - private int _bufferedPlaySamples = 0; - private int _playPosition = 0; - - AudioDeviceAndroid() { - try { - _playBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 - // kHz - _recBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 - // kHz - } catch (Exception e) { - DoLog(e.getMessage()); - } - - _tempBufPlay = new byte[2 * 480]; - _tempBufRec = new byte[2 * 480]; - } - - @SuppressWarnings("unused") - private int InitRecording(int audioSource, int sampleRate) { - // get the minimum buffer size that can be used - int minRecBufSize = - AudioRecord.getMinBufferSize(sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT); - - // DoLog("min rec buf size is " + minRecBufSize); - - // double size to be more safe - int recBufSize = minRecBufSize * 2; - _bufferedRecSamples = (5 * sampleRate) / 200; - // DoLog("rough rec delay set to " + _bufferedRecSamples); - - // release the object - if (_audioRecord != null) { - _audioRecord.release(); - _audioRecord = null; - } - - try { - _audioRecord = new AudioRecord( - audioSource, - sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT, - recBufSize); - - } catch (Exception e) { - DoLog(e.getMessage()); - return -1; - } - - // check that the audioRecord is ready to be used - if (_audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - // DoLog("rec not initialized " + sampleRate); - return -1; - } - - // DoLog("rec sample rate set to " + sampleRate); - - return _bufferedRecSamples; - } - - @SuppressWarnings("unused") - private int StartRecording() { - if (_isPlaying == false) { - SetAudioMode(true); - } - - // start recording - try { - _audioRecord.startRecording(); - - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - _isRecording = true; - return 0; - } - - @SuppressWarnings("unused") - private int InitPlayback(int sampleRate) { - // get the minimum buffer size that can be used - int minPlayBufSize = - AudioTrack.getMinBufferSize(sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT); - - // DoLog("min play buf size is " + minPlayBufSize); - - int playBufSize = minPlayBufSize; - if (playBufSize < 6000) { - playBufSize *= 2; - } - _bufferedPlaySamples = 0; - // DoLog("play buf size is " + playBufSize); - - // release the object - if (_audioTrack != null) { - _audioTrack.release(); - _audioTrack = null; - } - - try { - _audioTrack = new AudioTrack( - AudioManager.STREAM_VOICE_CALL, - sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT, - playBufSize, AudioTrack.MODE_STREAM); - } catch (Exception e) { - DoLog(e.getMessage()); - return -1; - } - - // check that the audioRecord is ready to be used - if (_audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - // DoLog("play not initialized " + sampleRate); - return -1; - } - - // DoLog("play sample rate set to " + sampleRate); - - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - // Return max playout volume - if (_audioManager == null) { - // Don't know the max volume but still init is OK for playout, - // so we should not return error. - return 0; - } - return _audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); - } - - @SuppressWarnings("unused") - private int StartPlayback() { - if (_isRecording == false) { - SetAudioMode(true); - } - - // start playout - try { - _audioTrack.play(); - - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - _isPlaying = true; - return 0; - } - - @SuppressWarnings("unused") - private int StopRecording() { - _recLock.lock(); - try { - // only stop if we are recording - if (_audioRecord.getRecordingState() == - AudioRecord.RECORDSTATE_RECORDING) { - // stop recording - try { - _audioRecord.stop(); - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - } - - // release the object - _audioRecord.release(); - _audioRecord = null; - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _doRecInit = true; - _recLock.unlock(); - } - - if (_isPlaying == false) { - SetAudioMode(false); - } - - _isRecording = false; - return 0; - } - - @SuppressWarnings("unused") - private int StopPlayback() { - _playLock.lock(); - try { - // only stop if we are playing - if (_audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { - // stop playout - try { - _audioTrack.stop(); - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - // flush the buffers - _audioTrack.flush(); - } - - // release the object - _audioTrack.release(); - _audioTrack = null; - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _doPlayInit = true; - _playLock.unlock(); - } - - if (_isRecording == false) { - SetAudioMode(false); - } - - _isPlaying = false; - return 0; - } - - @SuppressWarnings("unused") - private int PlayAudio(int lengthInBytes) { - - int bufferedSamples = 0; - - _playLock.lock(); - try { - if (_audioTrack == null) { - return -2; // We have probably closed down while waiting for - // play lock - } - - // Set priority, only do once - if (_doPlayInit == true) { - try { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - DoLog("Set play thread priority failed: " + e.getMessage()); - } - _doPlayInit = false; - } - - int written = 0; - _playBuffer.get(_tempBufPlay); - written = _audioTrack.write(_tempBufPlay, 0, lengthInBytes); - _playBuffer.rewind(); // Reset the position to start of buffer - - // DoLog("Wrote data to sndCard"); - - // increase by number of written samples - _bufferedPlaySamples += (written >> 1); - - // decrease by number of played samples - int pos = _audioTrack.getPlaybackHeadPosition(); - if (pos < _playPosition) { // wrap or reset by driver - _playPosition = 0; // reset - } - _bufferedPlaySamples -= (pos - _playPosition); - _playPosition = pos; - - if (!_isRecording) { - bufferedSamples = _bufferedPlaySamples; - } - - if (written != lengthInBytes) { - // DoLog("Could not write all data to sc (written = " + written - // + ", length = " + lengthInBytes + ")"); - return -1; - } - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _playLock.unlock(); - } - - return bufferedSamples; - } - - @SuppressWarnings("unused") - private int RecordAudio(int lengthInBytes) { - _recLock.lock(); - - try { - if (_audioRecord == null) { - return -2; // We have probably closed down while waiting for rec - // lock - } - - // Set priority, only do once - if (_doRecInit == true) { - try { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - DoLog("Set rec thread priority failed: " + e.getMessage()); - } - _doRecInit = false; - } - - int readBytes = 0; - _recBuffer.rewind(); // Reset the position to start of buffer - readBytes = _audioRecord.read(_tempBufRec, 0, lengthInBytes); - // DoLog("read " + readBytes + "from SC"); - _recBuffer.put(_tempBufRec); - - if (readBytes != lengthInBytes) { - // DoLog("Could not read all data from sc (read = " + readBytes - // + ", length = " + lengthInBytes + ")"); - return -1; - } - - } catch (Exception e) { - DoLogErr("RecordAudio try failed: " + e.getMessage()); - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _recLock.unlock(); - } - - return (_bufferedPlaySamples); - } - - @SuppressWarnings("unused") - private int SetPlayoutSpeaker(boolean loudspeakerOn) { - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - if (_audioManager == null) { - DoLogErr("Could not change audio routing - no audio manager"); - return -1; - } - - int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); - - if ((3 == apiLevel) || (4 == apiLevel)) { - // 1.5 and 1.6 devices - if (loudspeakerOn) { - // route audio to back speaker - _audioManager.setMode(AudioManager.MODE_NORMAL); - } else { - // route audio to earpiece - _audioManager.setMode(AudioManager.MODE_IN_CALL); - } - } else { - // 2.x devices - if ((android.os.Build.BRAND.equals("Samsung") || - android.os.Build.BRAND.equals("samsung")) && - ((5 == apiLevel) || (6 == apiLevel) || - (7 == apiLevel))) { - // Samsung 2.0, 2.0.1 and 2.1 devices - if (loudspeakerOn) { - // route audio to back speaker - _audioManager.setMode(AudioManager.MODE_IN_CALL); - _audioManager.setSpeakerphoneOn(loudspeakerOn); - } else { - // route audio to earpiece - _audioManager.setSpeakerphoneOn(loudspeakerOn); - _audioManager.setMode(AudioManager.MODE_NORMAL); - } - } else { - // Non-Samsung and Samsung 2.2 and up devices - _audioManager.setSpeakerphoneOn(loudspeakerOn); - } - } - - return 0; - } - - @SuppressWarnings("unused") - private int SetPlayoutVolume(int level) { - - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - int retVal = -1; - - if (_audioManager != null) { - _audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, - level, 0); - retVal = 0; - } - - return retVal; - } - - @SuppressWarnings("unused") - private int GetPlayoutVolume() { - - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - int level = -1; - - if (_audioManager != null) { - level = _audioManager.getStreamVolume( - AudioManager.STREAM_VOICE_CALL); - } - - return level; - } - - private void SetAudioMode(boolean startCall) { - int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); - - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - if (_audioManager == null) { - DoLogErr("Could not set audio mode - no audio manager"); - return; - } - - // ***IMPORTANT*** When the API level for honeycomb (H) has been - // decided, - // the condition should be changed to include API level 8 to H-1. - if ((android.os.Build.BRAND.equals("Samsung") || android.os.Build.BRAND - .equals("samsung")) && (8 == apiLevel)) { - // Set Samsung specific VoIP mode for 2.2 devices - int mode = - (startCall ? 4 /* VoIP mode */ - : AudioManager.MODE_NORMAL); - _audioManager.setMode(mode); - if (_audioManager.getMode() != mode) { - DoLogErr("Could not set audio mode for Samsung device"); - } - } - } - - final String logTag = "WebRTC AD java"; - - private void DoLog(String msg) { - Log.d(logTag, msg); - } - - private void DoLogErr(String msg) { - Log.e(logTag, msg); - } -} diff --git a/modules/audio_device/main/source/Dummy/audio_device_dummy.cc b/modules/audio_device/main/source/Dummy/audio_device_dummy.cc deleted file mode 100644 index eeb007c8f..000000000 --- a/modules/audio_device/main/source/Dummy/audio_device_dummy.cc +++ /dev/null @@ -1,1395 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_dummy.h" - -#include - -#include "trace.h" -#include "thread_wrapper.h" -#include "event_wrapper.h" - -// Enable to record playout data -//#define RECORD_PLAYOUT 1 - -namespace webrtc { - -const WebRtc_UWord32 REC_TIMER_PERIOD_MS = 10; -const WebRtc_UWord32 PLAY_TIMER_PERIOD_MS = 10; - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceDummy() - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceDummy::AudioDeviceDummy(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _timeEventRec(*EventWrapper::Create()), - _timeEventPlay(*EventWrapper::Create()), - _recStartEvent(*EventWrapper::Create()), - _playStartEvent(*EventWrapper::Create()), - _ptrThreadRec(NULL), - _ptrThreadPlay(NULL), - _recThreadID(0), - _playThreadID(0), - _initialized(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _speakerIsInitialized(false), - _microphoneIsInitialized(false), - _playDataFile(NULL) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); - - memset(_recBuffer, 0, sizeof(_recBuffer)); - WebRtc_Word16* tmp = (WebRtc_Word16*)_recBuffer; - - // Saw tooth -16000 to 16000, 100 Hz @ fs = 16 kHz -// for(int i=0; i<160; ++i) -// { -// tmp[i] = i*200-16000; -// } - - // Rough sinus 2 kHz @ fs = 16 kHz - for(int i=0; i<20; ++i) - { - tmp[i*8] = 0; - tmp[i*8+1] = -5000; - tmp[i*8+2] = -16000; - tmp[i*8+3] = -5000; - tmp[i*8+4] = 0; - tmp[i*8+5] = 5000; - tmp[i*8+6] = 16000; - tmp[i*8+7] = 5000; - } - -#ifdef RECORD_PLAYOUT - _playDataFile = fopen("webrtc_VoiceEngine_playout.pcm", "wb"); - if (!_playDataFile) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not open file for writing playout data"); - } -#endif -} - -// ---------------------------------------------------------------------------- -// AudioDeviceDummy() - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceDummy::~AudioDeviceDummy() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - - Terminate(); - - _ptrAudioBuffer = NULL; - - delete &_recStartEvent; - delete &_playStartEvent; - delete &_timeEventRec; - delete &_timeEventPlay; - delete &_critSect; - - if (_playDataFile) - { - fclose(_playDataFile); - } -} - -// ============================================================================ -// API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AttachAudioBuffer -// ---------------------------------------------------------------------------- - -void AudioDeviceDummy::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - _ptrAudioBuffer = audioBuffer; - - // Inform the AudioBuffer about default settings for this implementation. - _ptrAudioBuffer->SetRecordingSampleRate(16000); - _ptrAudioBuffer->SetPlayoutSampleRate(16000); - _ptrAudioBuffer->SetRecordingChannels(1); - _ptrAudioBuffer->SetPlayoutChannels(1); -} - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kDummyAudio; - return 0; -} - -// ---------------------------------------------------------------------------- -// Init -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - const bool periodic(true); - unsigned int threadID(0); - char threadName[64] = {0}; - - // RECORDING - strncpy(threadName, "webrtc_audio_module_rec_thread", 63); - _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, kRealtimePriority, threadName); - if (_ptrThreadRec == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to create the rec audio thread"); - return -1; - } - - if (!_ptrThreadRec->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to start the rec audio thread"); - delete _ptrThreadRec; - _ptrThreadRec = NULL; - return -1; - } - _recThreadID = threadID; - - if (!_timeEventRec.StartTimer(periodic, REC_TIMER_PERIOD_MS)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to start the rec timer event"); - if (_ptrThreadRec->Stop()) - { - delete _ptrThreadRec; - _ptrThreadRec = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unable to stop the activated rec thread"); - } - return -1; - } - - // PLAYOUT - strncpy(threadName, "webrtc_audio_module_play_thread", 63); - _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, this, kRealtimePriority, threadName); - if (_ptrThreadPlay == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to create the play audio thread"); - return -1; - } - - threadID = 0; - if (!_ptrThreadPlay->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to start the play audio thread"); - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - return -1; - } - _playThreadID = threadID; - - if (!_timeEventPlay.StartTimer(periodic, PLAY_TIMER_PERIOD_MS)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, " failed to start the play timer event"); - if (_ptrThreadPlay->Stop()) - { - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " unable to stop the activated play thread"); - } - return -1; - } - - _initialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Terminate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) - { - return 0; - } - - // RECORDING - if (_ptrThreadRec) - { - ThreadWrapper* tmpThread = _ptrThreadRec; - _ptrThreadRec = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventRec.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " failed to close down the rec audio thread"); - } - - _critSect.Enter(); - } - - _timeEventRec.StopTimer(); - - // PLAYOUT - if (_ptrThreadPlay) - { - ThreadWrapper* tmpThread = _ptrThreadPlay; - _ptrThreadPlay = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventPlay.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " failed to close down the play audio thread"); - } - - _critSect.Enter(); - } - - _timeEventPlay.StopTimer(); - - _initialized = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Initialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_initialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - _speakerIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - _microphoneIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_speakerIsInitialized); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_microphoneIsInitialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetSpeakerVolume(volume=%u)", volume); - - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - - return -1; -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetSpeakerMute(enable=%u)", enable); - - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetMicrophoneMute(enable=%u)", enable); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetMicrophoneBoost(enable=%u)", enable); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// StereoRecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetStereoRecording(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (enable) - { - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetStereoPlayout(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (enable) - { - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetAGC -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetAGC(enable=%d)", enable); - - return -1; -} - -// ---------------------------------------------------------------------------- -// AGC -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::AGC() const -{ - // WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return false; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - available = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "AudioDeviceDummy::SetMicrophoneVolume(volume=%u)", volume); - - CriticalSectionScoped lock(_critSect); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - // WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceDummy::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - return 1; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - if (index != 0) - { - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device) -{ - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::PlayoutDeviceName(index=%u)", index); - - if (index != 0) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::RecordingDeviceName(index=%u)", index); - - if (index != 0) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceDummy::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - return 1; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - if (index != 0 ) - { - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device) -{ - return -1; -} - -// ---------------------------------------------------------------------------- -// PlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " InitSpeaker() failed"); - } - - _playIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " InitMicrophone() failed"); - } - - _recIsInitialized = true; - - return 0; - -} - -// ---------------------------------------------------------------------------- -// StartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return -1; - } - - if (_recording) - { - return 0; - } - - _recording = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return 0; - } - - _recIsInitialized = false; - _recording = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recIsInitialized); -} - -// ---------------------------------------------------------------------------- -// Recording -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recording); -} - -// ---------------------------------------------------------------------------- -// PlayoutIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_playIsInitialized); -} - -// ---------------------------------------------------------------------------- -// StartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - return -1; - } - - if (_playing) - { - return 0; - } - - _playing = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return 0; - } - - _playIsInitialized = false; - _playing = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = 0; - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = 0; - return 0; -} - -// ---------------------------------------------------------------------------- -// Playing -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_playing); -} -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceDummy::SetPlayoutBuffer(type=%u, sizeMS=%u)", type, sizeMS); - - CriticalSectionScoped lock(_critSect); - - // Just ignore - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const -{ - CriticalSectionScoped lock(_critSect); - - type = AudioDeviceModule::kAdaptiveBufferSize; - sizeMS = 0; - - return 0; -} - -// ---------------------------------------------------------------------------- -// CPULoad -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceDummy::CPULoad(WebRtc_UWord16& load) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - load = 0; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::PlayoutWarning() const -{ - return false; -} - -// ---------------------------------------------------------------------------- -// PlayoutError -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::PlayoutError() const -{ - return false; -} - -// ---------------------------------------------------------------------------- -// RecordingWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::RecordingWarning() const -{ - return false; -} - -// ---------------------------------------------------------------------------- -// RecordingError -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::RecordingError() const -{ - return false; -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceDummy::ClearPlayoutWarning() -{ -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutError -// ---------------------------------------------------------------------------- - -void AudioDeviceDummy::ClearPlayoutError() -{ -} - -// ---------------------------------------------------------------------------- -// ClearRecordingWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceDummy::ClearRecordingWarning() -{ -} - -// ---------------------------------------------------------------------------- -// ClearRecordingError -// ---------------------------------------------------------------------------- - -void AudioDeviceDummy::ClearRecordingError() -{ -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// PlayThreadFunc -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::PlayThreadFunc(void* pThis) -{ - return (static_cast(pThis)->PlayThreadProcess()); -} - -// ---------------------------------------------------------------------------- -// RecThreadFunc -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::RecThreadFunc(void* pThis) -{ - return (static_cast(pThis)->RecThreadProcess()); -} - -// ---------------------------------------------------------------------------- -// PlayThreadProcess -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::PlayThreadProcess() -{ - switch (_timeEventPlay.Wait(1000)) - { - case kEventSignaled: - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed => restarting timer"); - _timeEventPlay.StopTimer(); - _timeEventPlay.StartTimer(true, PLAY_TIMER_PERIOD_MS); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if(_playing) - { - WebRtc_Word8 playBuffer[2*160]; - - UnLock(); - WebRtc_Word32 nSamples = (WebRtc_Word32)_ptrAudioBuffer->RequestPlayoutData(160); - Lock(); - - if (!_playing) - { - UnLock(); - return true; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != 160) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " invalid number of output samples(%d)", nSamples); - } - - if (_playDataFile) - { - int wr = fwrite(playBuffer, 2, 160, _playDataFile); - if (wr != 160) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Could not write playout data to file (%d) ferror = %d", - wr, ferror(_playDataFile)); - } - } - } - - UnLock(); - return true; -} - -// ---------------------------------------------------------------------------- -// RecThreadProcess -// ---------------------------------------------------------------------------- - -bool AudioDeviceDummy::RecThreadProcess() -{ - switch (_timeEventRec.Wait(1000)) - { - case kEventSignaled: - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed => restarting timer"); - _timeEventRec.StopTimer(); - _timeEventRec.StartTimer(true, REC_TIMER_PERIOD_MS); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if (_recording) - { - // store the recorded buffer - _ptrAudioBuffer->SetRecordedBuffer(_recBuffer, 160); - - // store vqe delay values - _ptrAudioBuffer->SetVQEData(0, 0, 0); - - // deliver recorded samples at specified sample rate, mic level etc. to the observer using callback - UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - } - else - { - UnLock(); - } - - return true; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Dummy/audio_device_dummy.h b/modules/audio_device/main/source/Dummy/audio_device_dummy.h deleted file mode 100644 index 9edfc6358..000000000 --- a/modules/audio_device/main/source/Dummy/audio_device_dummy.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DUMMY_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DUMMY_H - -#include - -#include "audio_device_generic.h" -#include "critical_section_wrapper.h" - -namespace webrtc { -class EventWrapper; -class ThreadWrapper; - -class AudioDeviceDummy : public AudioDeviceGeneric -{ -public: - AudioDeviceDummy(const WebRtc_Word32 id); - ~AudioDeviceDummy(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: - void Lock() { _critSect.Enter(); }; - void UnLock() { _critSect.Leave(); }; - - static bool RecThreadFunc(void*); - static bool PlayThreadFunc(void*); - bool RecThreadProcess(); - bool PlayThreadProcess(); - - AudioDeviceBuffer* _ptrAudioBuffer; - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - - EventWrapper& _timeEventRec; - EventWrapper& _timeEventPlay; - EventWrapper& _recStartEvent; - EventWrapper& _playStartEvent; - - ThreadWrapper* _ptrThreadPlay; - ThreadWrapper* _ptrThreadRec; - WebRtc_UWord32 _recThreadID; - WebRtc_UWord32 _playThreadID; - - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _speakerIsInitialized; - bool _microphoneIsInitialized; - - WebRtc_Word8 _recBuffer[2*160]; - - FILE* _playDataFile; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_DUMMY_H diff --git a/modules/audio_device/main/source/Linux/alsasymboltable.cc b/modules/audio_device/main/source/Linux/alsasymboltable.cc deleted file mode 100644 index f584c3ce9..000000000 --- a/modules/audio_device/main/source/Linux/alsasymboltable.cc +++ /dev/null @@ -1,39 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "alsasymboltable.h" - -namespace webrtc_adm_linux_alsa { - -LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(AlsaSymbolTable, "libasound.so.2") -#define X(sym) \ - LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(AlsaSymbolTable, sym) -ALSA_SYMBOLS_LIST -#undef X -LATE_BINDING_SYMBOL_TABLE_DEFINE_END(AlsaSymbolTable) - -} // namespace webrtc_adm_linux_alsa diff --git a/modules/audio_device/main/source/Linux/alsasymboltable.h b/modules/audio_device/main/source/Linux/alsasymboltable.h deleted file mode 100644 index c55ee5963..000000000 --- a/modules/audio_device/main/source/Linux/alsasymboltable.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_ALSASYMBOLTABLE_H -#define WEBRTC_AUDIO_DEVICE_ALSASYMBOLTABLE_H - -#include "latebindingsymboltable.h" - -namespace webrtc_adm_linux_alsa { - -// The ALSA symbols we need, as an X-Macro list. -// This list must contain precisely every libasound function that is used in -// alsasoundsystem.cc. -#define ALSA_SYMBOLS_LIST \ - X(snd_device_name_free_hint) \ - X(snd_device_name_get_hint) \ - X(snd_device_name_hint) \ - X(snd_pcm_avail_update) \ - X(snd_pcm_close) \ - X(snd_pcm_delay) \ - X(snd_pcm_drop) \ - X(snd_pcm_open) \ - X(snd_pcm_prepare) \ - X(snd_pcm_readi) \ - X(snd_pcm_recover) \ - X(snd_pcm_resume) \ - X(snd_pcm_reset) \ - X(snd_pcm_state) \ - X(snd_pcm_set_params) \ - X(snd_pcm_start) \ - X(snd_pcm_stream) \ - X(snd_pcm_wait) \ - X(snd_pcm_writei) \ - X(snd_pcm_info_get_class) \ - X(snd_pcm_info_get_subdevices_avail) \ - X(snd_pcm_info_get_subdevice_name) \ - X(snd_pcm_info_set_subdevice) \ - X(snd_pcm_info_get_id) \ - X(snd_pcm_info_set_device) \ - X(snd_pcm_info_set_stream) \ - X(snd_pcm_info_get_name) \ - X(snd_pcm_info_get_subdevices_count) \ - X(snd_pcm_info_sizeof) \ - X(snd_pcm_hw_params) \ - X(snd_pcm_hw_params_malloc) \ - X(snd_pcm_hw_params_free) \ - X(snd_pcm_hw_params_any) \ - X(snd_pcm_hw_params_set_access) \ - X(snd_pcm_hw_params_set_format) \ - X(snd_pcm_hw_params_set_channels) \ - X(snd_pcm_hw_params_set_rate_near) \ - X(snd_pcm_hw_params_set_buffer_size_near) \ - X(snd_card_next) \ - X(snd_card_get_name) \ - X(snd_config_update) \ - X(snd_config_copy) \ - X(snd_config_get_id) \ - X(snd_ctl_open) \ - X(snd_ctl_close) \ - X(snd_ctl_card_info) \ - X(snd_ctl_card_info_sizeof) \ - X(snd_ctl_card_info_get_id) \ - X(snd_ctl_card_info_get_name) \ - X(snd_ctl_pcm_next_device) \ - X(snd_ctl_pcm_info) \ - X(snd_mixer_load) \ - X(snd_mixer_free) \ - X(snd_mixer_detach) \ - X(snd_mixer_close) \ - X(snd_mixer_open) \ - X(snd_mixer_attach) \ - X(snd_mixer_first_elem) \ - X(snd_mixer_elem_next) \ - X(snd_mixer_selem_get_name) \ - X(snd_mixer_selem_is_active) \ - X(snd_mixer_selem_register) \ - X(snd_mixer_selem_set_playback_volume_all) \ - X(snd_mixer_selem_get_playback_volume) \ - X(snd_mixer_selem_has_playback_volume) \ - X(snd_mixer_selem_get_playback_volume_range) \ - X(snd_mixer_selem_has_playback_switch) \ - X(snd_mixer_selem_get_playback_switch) \ - X(snd_mixer_selem_set_playback_switch_all) \ - X(snd_mixer_selem_has_capture_switch) \ - X(snd_mixer_selem_get_capture_switch) \ - X(snd_mixer_selem_set_capture_switch_all) \ - X(snd_mixer_selem_has_capture_volume) \ - X(snd_mixer_selem_set_capture_volume_all) \ - X(snd_mixer_selem_get_capture_volume) \ - X(snd_mixer_selem_get_capture_volume_range) \ - X(snd_dlopen) \ - X(snd_dlclose) \ - X(snd_config) \ - X(snd_config_search) \ - X(snd_config_get_string) \ - X(snd_config_search_definition) \ - X(snd_config_get_type) \ - X(snd_config_delete) \ - X(snd_config_iterator_entry) \ - X(snd_config_iterator_first) \ - X(snd_config_iterator_next) \ - X(snd_config_iterator_end) \ - X(snd_config_delete_compound_members) \ - X(snd_config_get_integer) \ - X(snd_config_get_bool) \ - X(snd_dlsym) \ - X(snd_strerror) \ - X(snd_lib_error) \ - X(snd_lib_error_set_handler) - -LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(AlsaSymbolTable) -#define X(sym) \ - LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(AlsaSymbolTable, sym) -ALSA_SYMBOLS_LIST -#undef X -LATE_BINDING_SYMBOL_TABLE_DECLARE_END(AlsaSymbolTable) - -} // namespace webrtc_adm_linux_alsa - -#endif // WEBRTC_AUDIO_DEVICE_ALSASYMBOLTABLE_H diff --git a/modules/audio_device/main/source/Linux/audio_device_linux_alsa.cc b/modules/audio_device/main/source/Linux/audio_device_linux_alsa.cc deleted file mode 100644 index 6d389cc43..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_linux_alsa.cc +++ /dev/null @@ -1,3690 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "audio_device_utility.h" -#include "audio_device_linux_alsa.h" -#include "audio_device_config.h" - -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" - - -webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable; - -// Accesses ALSA functions through our late-binding symbol table instead of -// directly. This way we don't have to link to libasound, which means our binary -// will work on systems that don't have it. -#define LATE(sym) \ - LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym) - -// Redefine these here to be able to do late-binding -#undef snd_ctl_card_info_alloca -#define snd_ctl_card_info_alloca(ptr) \ - do { *ptr = (snd_ctl_card_info_t *) \ - __builtin_alloca (LATE(snd_ctl_card_info_sizeof)()); \ - memset(*ptr, 0, LATE(snd_ctl_card_info_sizeof)()); } while (0) - -#undef snd_pcm_info_alloca -#define snd_pcm_info_alloca(pInfo) \ - do { *pInfo = (snd_pcm_info_t *) \ - __builtin_alloca (LATE(snd_pcm_info_sizeof)()); \ - memset(*pInfo, 0, LATE(snd_pcm_info_sizeof)()); } while (0) - -// snd_lib_error_handler_t -void WebrtcAlsaErrorHandler(const char *file, - int line, - const char *function, - int err, - const char *fmt,...){}; - -namespace webrtc -{ -AudioDeviceLinuxALSA::AudioDeviceLinuxALSA(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _timeEventRec(*EventWrapper::Create()), - _timeEventPlay(*EventWrapper::Create()), - _recStartEvent(*EventWrapper::Create()), - _playStartEvent(*EventWrapper::Create()), - _ptrThreadRec(NULL), - _ptrThreadPlay(NULL), - _recThreadID(0), - _playThreadID(0), - _id(id), - _mixerManager(id), - _inputDeviceIndex(0), - _outputDeviceIndex(0), - _inputDeviceIsSpecified(false), - _outputDeviceIsSpecified(false), - _handleRecord(NULL), - _handlePlayout(NULL), - _recSndcardBuffsize(ALSA_SNDCARD_BUFF_SIZE_REC), - _playSndcardBuffsize(ALSA_SNDCARD_BUFF_SIZE_PLAY), - _initialized(false), - _recIsInitialized(false), - _playIsInitialized(false), - _recording(false), - _playing(false), - _startRec(false), - _stopRec(false), - _startPlay(false), - _stopPlay(false), - _AGC(false), - _buffersizeFromZeroAvail(true), - _buffersizeFromZeroDelay(true), - _sndCardPlayDelay(0), - _sndCardRecDelay(0), - _numReadyRecSamples(0), - _previousSndCardPlayDelay(0), - _delayMonitorStatePlay(0), - _largeDelayCountPlay(0), - _bufferCheckMethodPlay(0), - _bufferCheckMethodRec(0), - _bufferCheckErrorsPlay(0), - _bufferCheckErrorsRec(0), - _lastBufferCheckValuePlay(0), - _writeErrors(0), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _samplingFreqRec(REC_SAMPLES_PER_MS), - _samplingFreqPlay(PLAY_SAMPLES_PER_MS), - _recChannels(1), - _playChannels(1), - _playbackBufferSize(0), - _recordBufferSize(0), - _recBuffer(NULL), - _playBufDelay(80), - _playBufDelayFixed(80), - _playBufType(AudioDeviceModule::kAdaptiveBufferSize) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceLinuxALSA - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceLinuxALSA::~AudioDeviceLinuxALSA() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - - Terminate(); - - if (_recBuffer) - { - delete _recBuffer; - } - delete &_recStartEvent; - delete &_playStartEvent; - delete &_timeEventRec; - delete &_timeEventPlay; - delete &_critSect; -} - -void AudioDeviceLinuxALSA::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // Inform the AudioBuffer about default settings for this implementation. - // Set all values to zero here since the actual settings will be done by - // InitPlayout and InitRecording later. - _ptrAudioBuffer->SetRecordingSampleRate(0); - _ptrAudioBuffer->SetPlayoutSampleRate(0); - _ptrAudioBuffer->SetRecordingChannels(0); - _ptrAudioBuffer->SetPlayoutChannels(0); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kLinuxAlsaAudio; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - // Load libasound - if (!AlsaSymbolTable.Load()) - { - // Alsa is not installed on - // this system - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to load symbol table"); - return -1; - } - - if (_initialized) - { - return 0; - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - // RECORDING - const char* threadName = "webrtc_audio_module_rec_thread"; - _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, - this, - kRealtimePriority, - threadName); - if (_ptrThreadRec == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the rec audio thread"); - return -1; - } - - unsigned int threadID(0); - if (!_ptrThreadRec->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the rec audio thread"); - delete _ptrThreadRec; - _ptrThreadRec = NULL; - return -1; - } - _recThreadID = threadID; - - const bool periodic(true); - if (!_timeEventRec.StartTimer(periodic, REC_TIMER_PERIOD_MS)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the rec timer event"); - if (_ptrThreadRec->Stop()) - { - delete _ptrThreadRec; - _ptrThreadRec = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unable to stop the activated rec thread"); - } - return -1; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " periodic rec timer (dT=%d) is now active", - REC_TIMER_PERIOD_MS); - - // PLAYOUT - threadName = "webrtc_audio_module_play_thread"; - _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, - this, - kRealtimePriority, - threadName); - if (_ptrThreadPlay == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the play audio thread"); - return -1; - } - - threadID = 0; - if (!_ptrThreadPlay->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the play audio thread"); - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - return -1; - } - _playThreadID = threadID; - - if (!_timeEventPlay.StartTimer(periodic, PLAY_TIMER_PERIOD_MS)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the play timer event"); - if (_ptrThreadPlay->Stop()) - { - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unable to stop the activated play thread"); - } - return -1; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " periodic play timer (dT=%d) is now active", PLAY_TIMER_PERIOD_MS); - - _initialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_initialized) - { - return 0; - } - - CriticalSectionScoped lock(_critSect); - - _mixerManager.Close(); - - // RECORDING - if (_ptrThreadRec) - { - ThreadWrapper* tmpThread = _ptrThreadRec; - _ptrThreadRec = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventRec.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the rec audio thread"); - } - - _critSect.Enter(); - } - - _timeEventRec.StopTimer(); - - // PLAYOUT - if (_ptrThreadPlay) - { - ThreadWrapper* tmpThread = _ptrThreadPlay; - _ptrThreadPlay = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventPlay.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the play audio thread"); - } - - _critSect.Enter(); - } - - _timeEventPlay.StopTimer(); - - _initialized = false; - _outputDeviceIsSpecified = false; - _inputDeviceIsSpecified = false; - - return 0; -} - -bool AudioDeviceLinuxALSA::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_initialized); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker - // exists - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - char devName[kAdmMaxDeviceNameSize] = {0}; - GetDevicesInfo(2, true, _outputDeviceIndex, devName, kAdmMaxDeviceNameSize); - return _mixerManager.OpenSpeaker(devName); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid - // microphone exists - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - char devName[kAdmMaxDeviceNameSize] = {0}; - GetDevicesInfo(2, false, _inputDeviceIndex, devName, kAdmMaxDeviceNameSize); - return _mixerManager.OpenMicrophone(devName); -} - -bool AudioDeviceLinuxALSA::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.SpeakerIsInitialized()); -} - -bool AudioDeviceLinuxALSA::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.MicrophoneIsInitialized()); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control. - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a volume control - // exists - available = true; - - // Close the initialized output mixer - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetSpeakerVolume(volume=%u)", volume); - - return (_mixerManager.SetSpeakerVolume(volume)); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.SpeakerVolume(level) == -1) - { - return -1; - } - - volume = level; - - return 0; -} - - -WebRtc_Word32 AudioDeviceLinuxALSA::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::WaveOutVolume( - WebRtc_UWord16& /*volumeLeft*/, - WebRtc_UWord16& /*volumeRight*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MaxSpeakerVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MinSpeakerVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinSpeakerVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control, hence it is safe to state that there is no mute control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected speaker has a mute control - _mixerManager.SpeakerMuteIsAvailable(isAvailable); - - available = isAvailable; - - // Close the initialized output mixer - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetSpeakerMute(enable=%u)", enable); - return (_mixerManager.SetSpeakerMute(enable)); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.SpeakerMute(muted) == -1) - { - return -1; - } - - enabled = muted; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no mute control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a mute control - // - _mixerManager.MicrophoneMuteIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetMicrophoneMute(enable=%u)", enable); - return (_mixerManager.SetMicrophoneMute(enable)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.MicrophoneMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Enumerate all avaliable microphone and make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a boost control - _mixerManager.MicrophoneBoostIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetMicrophoneBoost(enable=%u)", enable); - - return (_mixerManager.SetMicrophoneBoost(enable)); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool onOff(0); - - if (_mixerManager.MicrophoneBoost(onOff) == -1) - { - return -1; - } - - enabled = onOff; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - // If we already have initialized in stereo it's obviously available - if (_recIsInitialized && (2 == _recChannels)) - { - available = true; - return 0; - } - - // Save rec states and the number of rec channels - bool recIsInitialized = _recIsInitialized; - bool recording = _recording; - int recChannels = _recChannels; - - available = false; - - // Stop/uninitialize recording if initialized (and possibly started) - if (_recIsInitialized) - { - StopRecording(); - } - - // Try init in stereo; - _recChannels = 2; - if (InitRecording() == 0) - { - available = true; - } - - // Stop/uninitialize recording - StopRecording(); - - // Recover previous states - _recChannels = recChannels; - if (recIsInitialized) - { - InitRecording(); - } - if (recording) - { - StartRecording(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetStereoRecording(enable=%u)", enable); - - if (enable) - _recChannels = 2; - else - _recChannels = 1; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_recChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - // If we already have initialized in stereo it's obviously available - if (_playIsInitialized && (2 == _playChannels)) - { - available = true; - return 0; - } - - // Save rec states and the number of rec channels - bool playIsInitialized = _playIsInitialized; - bool playing = _playing; - int playChannels = _playChannels; - - available = false; - - // Stop/uninitialize recording if initialized (and possibly started) - if (_playIsInitialized) - { - StopPlayout(); - } - - // Try init in stereo; - _playChannels = 2; - if (InitPlayout() == 0) - { - available = true; - } - - // Stop/uninitialize recording - StopPlayout(); - - // Recover previous states - _playChannels = playChannels; - if (playIsInitialized) - { - InitPlayout(); - } - if (playing) - { - StartPlayout(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetStereoPlayout(enable=%u)", enable); - - if (enable) - _playChannels = 2; - else - _playChannels = 1; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_playChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -bool AudioDeviceLinuxALSA::AGC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _AGC; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control. - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a volume control - // exists - available = true; - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetMicrophoneVolume(volume=%u)", volume); - - return (_mixerManager.SetMicrophoneVolume(volume)); - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.MicrophoneVolume(level) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to retrive current microphone level"); - return -1; - } - - volume = level; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MaxMicrophoneVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MinMicrophoneVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinMicrophoneVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - - return 0; -} - -WebRtc_Word16 AudioDeviceLinuxALSA::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return (WebRtc_Word16)GetDevicesInfo(0, true); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - WebRtc_UWord32 nDevices = GetDevicesInfo(0, true); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable audio output devices is %u", nDevices); - - if (index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - _outputDeviceIndex = index; - _outputDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "PlayoutDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(PlayoutDevices()); - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return GetDevicesInfo(1, true, index, name, kAdmMaxDeviceNameSize); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "RecordingDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(RecordingDevices()); - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return GetDevicesInfo(1, false, index, name, kAdmMaxDeviceNameSize); -} - -WebRtc_Word16 AudioDeviceLinuxALSA::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return (WebRtc_Word16)GetDevicesInfo(0, false); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - WebRtc_UWord32 nDevices = GetDevicesInfo(0, false); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable audio input devices is %u", nDevices); - - if (index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - _inputDeviceIndex = index; - _inputDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceLinuxALSA::SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side with mono - // Assumes that user set num channels after calling this function - _playChannels = 1; - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - else - { - // It may be possible to play out in stereo - res = StereoPlayoutIsAvailable(available); - if (available) - { - // Then set channels to 2 so InitPlayout doesn't fail - _playChannels = 2; - } - } - - return res; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the recording side with mono - // Assumes that user set num channels after calling this function - _recChannels = 1; - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - else - { - // It may be possible to record in stereo - res = StereoRecordingIsAvailable(available); - if (available) - { - // Then set channels to 2 so InitPlayout doesn't fail - _recChannels = 2; - } - } - - return res; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - int errVal = 0; - - snd_pcm_uframes_t numFrames = 0; - snd_pcm_hw_params_t *paramsPlayout; - - CriticalSectionScoped lock(_critSect); - if (_playing) - { - return -1; - } - - if (!_outputDeviceIsSpecified) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - // Start by closing any existing wave-output devices - // - if (_handlePlayout != NULL) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - _playIsInitialized = false; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing current playout sound device, error:" - " %s", LATE(snd_strerror)(errVal)); - } - } - - // Open PCM device for playout - char deviceName[kAdmMaxDeviceNameSize] = {0}; - GetDevicesInfo(2, true, _outputDeviceIndex, deviceName, - kAdmMaxDeviceNameSize); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " InitPlayout open (%s)", deviceName); - - errVal = LATE(snd_pcm_open) - (&_handlePlayout, - deviceName, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK); - - if (errVal == -EBUSY) // Device busy - try some more! - { - for (int i=0; i < 5; i++) - { - sleep(1); - errVal = LATE(snd_pcm_open) - (&_handlePlayout, - deviceName, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK); - if (errVal == 0) - { - break; - } - } - } - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " unable to open playback device: %s (%d)", - LATE(snd_strerror)(errVal), - errVal); - _handlePlayout=NULL; - return -1; - } - - // Allocate hardware paramterers - errVal = LATE(snd_pcm_hw_params_malloc)(¶msPlayout); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params malloc, error: %s", - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - errVal = LATE(snd_pcm_hw_params_any)(_handlePlayout, paramsPlayout); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params_any, error: %s", - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set stereo sample order - errVal = LATE(snd_pcm_hw_params_set_access) - (_handlePlayout, - paramsPlayout, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set access, error: %s", - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set sample format -#if defined(WEBRTC_BIG_ENDIAN) - errVal = LATE(snd_pcm_hw_params_set_format) - (_handlePlayout, - paramsPlayout, - SND_PCM_FORMAT_S16_BE); -#else - errVal = LATE(snd_pcm_hw_params_set_format) - (_handlePlayout, - paramsPlayout, - SND_PCM_FORMAT_S16_LE); -#endif - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set format, error: %s", - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set stereo/mono - errVal = LATE(snd_pcm_hw_params_set_channels) - (_handlePlayout, - paramsPlayout, - _playChannels); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set channels(%d), error: %s", - _playChannels, - LATE(snd_strerror)(errVal)); - - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set sampling rate to use - _samplingFreqPlay = PLAY_SAMPLES_PER_MS; - WebRtc_UWord32 samplingRate = _samplingFreqPlay*1000; - - // Set sample rate - unsigned int exactRate = samplingRate; - errVal = LATE(snd_pcm_hw_params_set_rate_near) - (_handlePlayout, - paramsPlayout, - &exactRate, - 0); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set rate near(%d), error: %s", - samplingRate, - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout=NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - if (exactRate != samplingRate) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Soundcard does not support sample rate %d Hz, %d Hz" - " used instead.", - samplingRate, - exactRate); - - // We use this rate instead - _samplingFreqPlay = (WebRtc_UWord32)(exactRate / 1000); - } - - // Set buffer size, in frames - numFrames = ALSA_SNDCARD_BUFF_SIZE_PLAY; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " set playout, numFrames: %d, bufer size: %d", - numFrames, - _playSndcardBuffsize); - errVal = LATE(snd_pcm_hw_params_set_buffer_size_near) - (_handlePlayout, - paramsPlayout, - &_playSndcardBuffsize); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set buffer size near(%d), error: %s", - (int) numFrames, - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - if (numFrames != _playSndcardBuffsize) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Allocated record buffersize: %d frames", - (int)_playSndcardBuffsize); - } - - // Write settings to the devices - errVal = LATE(snd_pcm_hw_params)(_handlePlayout, paramsPlayout); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params(_handlePlayout, paramsPlayout)," - " error: %s", - LATE(snd_strerror)(errVal)); - if (_handlePlayout) - { - LATE(snd_pcm_close)(_handlePlayout); - _handlePlayout = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Free parameter struct memory - LATE(snd_pcm_hw_params_free)(paramsPlayout); - paramsPlayout = NULL; - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetPlayoutSampleRate(_samplingFreqPlay*1000); - _ptrAudioBuffer->SetPlayoutChannels((WebRtc_UWord8)_playChannels); - } - - // Set play buffer size - _playbackBufferSize = _samplingFreqPlay * 10 * _playChannels * 2; - - // Init varaibles used for play - _previousSndCardPlayDelay = 0; - _largeDelayCountPlay = 0; - _delayMonitorStatePlay = 0; - _bufferCheckMethodPlay = 0; - _bufferCheckErrorsPlay = 0; - _lastBufferCheckValuePlay = 0; - _playWarning = 0; - _playError = 0; - - if (_handlePlayout != NULL) - { - _playIsInitialized = true; - return 0; - } - else - { - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - int errVal = 0; - snd_pcm_uframes_t numFrames = 0; - snd_pcm_hw_params_t *paramsRecord; - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (!_inputDeviceIsSpecified) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - // Start by closing any existing pcm-input devices - // - if (_handleRecord != NULL) - { - int errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - _recIsInitialized = false; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing current recording sound device," - " error: %s", - LATE(snd_strerror)(errVal)); - } - } - - // Open PCM device for recording - // The corresponding settings for playout are made after the record settings - char deviceName[kAdmMaxDeviceNameSize] = {0}; - GetDevicesInfo(2, false, _inputDeviceIndex, deviceName, - kAdmMaxDeviceNameSize); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "InitRecording open (%s)", deviceName); - errVal = LATE(snd_pcm_open) - (&_handleRecord, - deviceName, - SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK); - - // Available modes: 0 = blocking, SND_PCM_NONBLOCK, SND_PCM_ASYNC - if (errVal == -EBUSY) // Device busy - try some more! - { - for (int i=0; i < 5; i++) - { - sleep(1); - errVal = LATE(snd_pcm_open) - (&_handleRecord, - deviceName, - SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK); - if (errVal == 0) - { - break; - } - } - } - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " unable to open record device: %s", - LATE(snd_strerror)(errVal)); - _handleRecord = NULL; - return -1; - } - - // Allocate hardware paramterers - errVal = LATE(snd_pcm_hw_params_malloc)(¶msRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params malloc, error: %s", - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - errVal = LATE(snd_pcm_hw_params_any)(_handleRecord, paramsRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params any, error: %s", - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, error:" - " %s", LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set stereo sample order - errVal = LATE(snd_pcm_hw_params_set_access) - (_handleRecord, - paramsRecord, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " harware params set access, error: %s", - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, error:" - " %s", LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set sample format -#if defined(WEBRTC_BIG_ENDIAN) - errVal = LATE(snd_pcm_hw_params_set_format) - (_handleRecord, - paramsRecord, - SND_PCM_FORMAT_S16_BE); -#else - errVal = LATE(snd_pcm_hw_params_set_format) - (_handleRecord, - paramsRecord, - SND_PCM_FORMAT_S16_LE); -#endif - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " harware params set format, error: %s", - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device," - " error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set stereo/mono - errVal = LATE(snd_pcm_hw_params_set_channels)( - _handleRecord, paramsRecord, _recChannels); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " harware params set channels (%d), error: %s", - _recChannels, - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, " - "error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Set sampling rate to use - _samplingFreqRec = REC_SAMPLES_PER_MS; - WebRtc_UWord32 samplingRate = _samplingFreqRec*1000; - - // Set sample rate - unsigned int exactRate = samplingRate; - errVal = LATE(snd_pcm_hw_params_set_rate_near) - (_handleRecord, - paramsRecord, - &exactRate, - 0); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set rate near(%d), error: %s", - samplingRate, - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device," - " error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - if (exactRate != samplingRate) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Sound device does not support sample rate %d Hz, %d Hz" - " used instead.", - samplingRate, - exactRate); - - // We use this rate instead - _samplingFreqRec = (WebRtc_UWord32)(exactRate / 1000); - } - - // Set buffer size, in frames. - numFrames = ALSA_SNDCARD_BUFF_SIZE_REC; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " set record, numFrames: %d, buffer size: %d", - numFrames, - _recSndcardBuffsize); - - errVal = LATE(snd_pcm_hw_params_set_buffer_size_near) - (_handleRecord, - paramsRecord, - &_recSndcardBuffsize); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params set buffer size near(%d), error: %s", - (int) numFrames, - LATE(snd_strerror)(errVal)); - - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, " - "error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - if (numFrames != _recSndcardBuffsize) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Allocated record buffersize: %d frames", - (int)_recSndcardBuffsize); - } - - // Write settings to the devices - errVal = LATE(snd_pcm_hw_params)(_handleRecord, paramsRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " hardware params, error: %s", - LATE(snd_strerror)(errVal)); - if (_handleRecord) - { - errVal = LATE(snd_pcm_close)(_handleRecord); - _handleRecord = NULL; - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing recording sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - } - return -1; - } - - // Free prameter struct memory - LATE(snd_pcm_hw_params_free)(paramsRecord); - paramsRecord = NULL; - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetRecordingSampleRate(_samplingFreqRec*1000); - _ptrAudioBuffer->SetRecordingChannels((WebRtc_UWord8)_recChannels); - } - - // Set rec buffer size and create buffer - _recordBufferSize = _samplingFreqRec * 10 * _recChannels * 2; - _recBuffer = new WebRtc_Word16[_recordBufferSize / 2]; - - // Init rec varaibles - _bufferCheckMethodRec = 0; - _bufferCheckErrorsRec = 0; - - if (_handleRecord != NULL) - { - // Mark recording side as initialized - _recIsInitialized = true; - return 0; - } - else - { - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_recIsInitialized) - { - return -1; - } - - if (_recording) - { - return 0; - } - - // prepare and start the recording - int errVal=0; - errVal = LATE(snd_pcm_prepare)(_handleRecord); - if (errVal<0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " cannot prepare audio record interface for use (%s)\n", - LATE(snd_strerror)(errVal)); - return -1; - } - - errVal = LATE(snd_pcm_start)(_handleRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error starting record interface: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - -/* - // DEBUG: Write info about PCM - snd_output_t *output = NULL; - errVal = LATE(snd_output_stdio_attach)(&output, stdout, 0); - if (errVal < 0) { - printf("Output failed: %s\n", snd_strerror(errVal)); - return 0; - } - LATE(snd_pcm_dump)(_handleRecord, output); -*/ - - // set state to ensure that the recording starts from the audio thread - _startRec = true; - - // the audio thread will signal when recording has stopped - if (kEventTimeout == _recStartEvent.Wait(10000)) - { - _startRec = false; - StopRecording(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate recording"); - return -1; - } - - if (_recording) - { - // the recording state is set by the audio thread after recording has - // started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - " recording is now active"); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate recording"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return 0; - } - - if (_handleRecord == NULL) - { - return -1; - } - - // make sure we don't start recording (it's asynchronous), assuming that - // we are under lock - _startRec = false; - - // stop and close pcm recording device - int errVal = LATE(snd_pcm_drop)(_handleRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error stop recording: %s", - LATE(snd_strerror)(errVal)); - } - - errVal = LATE(snd_pcm_close)(_handleRecord); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing record sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - - // check if we have muted and unmute if so - bool muteEnabled = false; - MicrophoneMute(muteEnabled); - if (muteEnabled) - { - SetMicrophoneMute(false); - } - - _recIsInitialized = false; - _recording = false; - - // set the pcm input handle to NULL - _handleRecord = NULL; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " _handleRecord is now set to NULL"); - - // delete the rec buffer - if (_recBuffer) - { - delete _recBuffer; - _recBuffer = NULL; - } - - return 0; -} - -bool AudioDeviceLinuxALSA::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recIsInitialized); -} - -bool AudioDeviceLinuxALSA::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recording); -} - -bool AudioDeviceLinuxALSA::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playIsInitialized); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - if (!_playIsInitialized) - { - return -1; - } - - if (_playing) - { - return 0; - } - // prepare playout - int errVal=0; - errVal = LATE(snd_pcm_prepare)(_handlePlayout); - if (errVal<0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " cannot prepare audio playout interface for use: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - // Don't call snd_pcm_start here, it will start implicitly at first write. -/* - // DEBUG: Write info about PCM - snd_output_t *output = NULL; - errVal = LATE(snd_output_stdio_attach)(&output, stdout, 0); - if (errVal < 0) { - printf("Output failed: %s\n", snd_strerror(errVal)); - return 0; - } - LATE(snd_pcm_dump)(_handlePlayout, output); -*/ - - // set state to ensure that playout starts from the audio thread - _startPlay = true; - - // the audio thread will signal when recording has started - if (kEventTimeout == _playStartEvent.Wait(10000)) - { - _startPlay = false; - StopPlayout(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate playout"); - return -1; - } - - if (_playing) - { - // the playing state is set by the audio thread after playout has started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - " playing is now active"); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate playing"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - return 0; - } - - if (_handlePlayout == NULL) - { - return -1; - } - - _playIsInitialized = false; - _playing = false; - - // stop and close pcm playout device - int errVal = LATE(snd_pcm_drop)(_handlePlayout); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error stop playing: %s", - LATE(snd_strerror)(errVal)); - } - - errVal = LATE(snd_pcm_close)(_handlePlayout); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error closing playout sound device, error: %s", - LATE(snd_strerror)(errVal)); - } - - // set the pcm input handle to NULL - _handlePlayout = NULL; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " _handlePlayout is now set to NULL"); - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - delayMS = (WebRtc_UWord16)_sndCardPlayDelay; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - delayMS = (WebRtc_UWord16)_sndCardRecDelay; - return 0; -} - -bool AudioDeviceLinuxALSA::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playing); -} -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceLinuxALSA::SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetPlayoutBuffer(type=%u, sizeMS=%u)", type, sizeMS); - _playBufType = type; - if (type == AudioDeviceModule::kFixedBufferSize) - { - _playBufDelayFixed = sizeMS; - } - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - type = _playBufType; - if (type == AudioDeviceModule::kFixedBufferSize) - { - sizeMS = _playBufDelayFixed; - } - else - { - sizeMS = _playBufDelay; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::CPULoad(WebRtc_UWord16& load) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -bool AudioDeviceLinuxALSA::PlayoutWarning() const -{ - return (_playWarning > 0); -} - -bool AudioDeviceLinuxALSA::PlayoutError() const -{ - return (_playError > 0); -} - -bool AudioDeviceLinuxALSA::RecordingWarning() const -{ - return (_recWarning > 0); -} - -bool AudioDeviceLinuxALSA::RecordingError() const -{ - return (_recError > 0); -} - -void AudioDeviceLinuxALSA::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -void AudioDeviceLinuxALSA::ClearPlayoutError() -{ - _playError = 0; -} - -void AudioDeviceLinuxALSA::ClearRecordingWarning() -{ - _recWarning = 0; -} - -void AudioDeviceLinuxALSA::ClearRecordingError() -{ - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -WebRtc_Word32 AudioDeviceLinuxALSA::GetDevicesInfo( - const WebRtc_Word32 function, - const bool playback, - const WebRtc_Word32 enumDeviceNo, - char* enumDeviceName, - const WebRtc_Word32 ednLen) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - // Device enumeration based on libjingle implementation - // by Tristan Schmelcher at Google Inc. - - const char *type = playback ? "Output" : "Input"; - // dmix and dsnoop are only for playback and capture, respectively, but ALSA - // stupidly includes them in both lists. - const char *ignorePrefix = playback ? "dsnoop:" : "dmix:" ; - // (ALSA lists many more "devices" of questionable interest, but we show them - // just in case the weird devices may actually be desirable for some - // users/systems.) - - int err; - int enumCount(0); - bool keepSearching(true); - - void **hints; - err = LATE(snd_device_name_hint)(-1, // All cards - "pcm", // Only PCM devices - &hints); - if (err != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "GetDevicesInfo - device name hint error: %s", - LATE(snd_strerror)(err)); - return -1; - } - - for (void **list = hints; *list != NULL; ++list) - { - char *actualType = LATE(snd_device_name_get_hint)(*list, "IOID"); - if (actualType) - { // NULL means it's both. - bool wrongType = (strcmp(actualType, type) != 0); - free(actualType); - if (wrongType) - { - // Wrong type of device (i.e., input vs. output). - continue; - } - } - - char *name = LATE(snd_device_name_get_hint)(*list, "NAME"); - if (!name) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Device has no name"); - // Skip it. - continue; - } - - // Now check if we actually want to show this device. - if (strcmp(name, "default") != 0 && - strcmp(name, "null") != 0 && - strcmp(name, "pulse") != 0 && - strncmp(name, ignorePrefix, strlen(ignorePrefix)) != 0) - { - // Yes, we do. - char *desc = LATE(snd_device_name_get_hint)(*list, "DESC"); - if (!desc) - { - // Virtual devices don't necessarily have descriptions. - // Use their names instead - desc = name; - } - - if (0 == function) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Enum device %d - %s", enumCount, name); - - } - if ((1 == function) && (enumDeviceNo == enumCount)) - { - - // We have found the enum device, copy the name to buffer - strncpy(enumDeviceName, desc, ednLen); - enumDeviceName[ednLen-1] = '\0'; - keepSearching = false; - } - if ((2 == function) && (enumDeviceNo == enumCount)) - { - // We have found the enum device, copy the name to buffer - strncpy(enumDeviceName, name, ednLen); - enumDeviceName[ednLen-1] = '\0'; - keepSearching = false; - } - if (keepSearching) - { - ++enumCount; - } - - if (desc != name) - { - free(desc); - } - } - - free(name); - - if (!keepSearching) - { - break; - } - } - - err = LATE(snd_device_name_free_hint)(hints); - if (err != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "GetDevicesInfo - device name free hint error: %s", - LATE(snd_strerror)(err)); - // Continue and return true anyways, since we did get the whole list. - } - - if (0 == function) - { - return enumCount; // Normal return point for function 0 - } - - if (keepSearching) - { - // If we get here for function 1 and 2, we didn't find the specified - // enum device - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "GetDevicesInfo - Could not find device name or numbers"); - return -1; - } - - return 0; -} - -void AudioDeviceLinuxALSA::FillPlayoutBuffer() -{ - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Filling playout buffer"); - - WebRtc_Word32 sizeBytes = _playbackBufferSize; - WebRtc_Word32 blockFrames = sizeBytes / (2 * _playChannels); - WebRtc_Word16 sendoutOnCard[sizeBytes / 2]; - WebRtc_Word32 samplingFreq = _samplingFreqPlay * 1000; - - if (samplingFreq == 44000) - { - // Convert to sndcard samplerate - samplingFreq = 44100; - } - - memset(sendoutOnCard, 0, sizeBytes); - - int maxWrites = 3; - int avail = blockFrames+1; - if (0 == _bufferCheckMethodPlay) - { - // Normal case - maxWrites = (_playSndcardBuffsize / samplingFreq) / 10 + 3; - avail = LATE(snd_pcm_avail_update)(_handlePlayout); - } - - while ((avail >= blockFrames) && (maxWrites > 0)) - { - int written = LATE(snd_pcm_writei) - (_handlePlayout, - sendoutOnCard, - blockFrames); - - if (written != blockFrames) - { - if (written < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error writing to sound device (1), error: %s", - LATE(snd_strerror)(written)); - } - else - { - int remainingFrames = (blockFrames-written); - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, - "Written %d playout frames to soundcard, trying to " - "write the remaining %d frames", - written, remainingFrames); - - written = LATE(snd_pcm_writei) - (_handlePlayout, - &sendoutOnCard[written*2], - remainingFrames); - - if( written == remainingFrames ) - { - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, - _id, " %d frames were written", - written); - written = blockFrames; - } - else - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " Error writing to sound device (2)," - " error: %s", - LATE(snd_strerror)(written)); - - // Try to recover - ErrorRecovery(written, _handlePlayout); - } - } - } - - --maxWrites; - if (0 == _bufferCheckMethodPlay) - { - avail = LATE(snd_pcm_avail_update)(_handlePlayout); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " snd_pcm_avail_update returned %d", avail); - } - } - - // Write one extra so that we push the buffer full - LATE(snd_pcm_writei)(_handlePlayout, sendoutOnCard, blockFrames); - avail = LATE(snd_pcm_avail_update)(_handlePlayout); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " snd_pcm_avail_update returned %d", avail); -} - -WebRtc_Word32 AudioDeviceLinuxALSA::InputSanityCheckAfterUnlockedPeriod() const -{ - if (_handleRecord == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " input state has been modified during unlocked period"); - return -1; - } - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::OutputSanityCheckAfterUnlockedPeriod() const -{ - if (_handlePlayout == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " output state has been modified during unlocked period"); - return -1; - } - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::PrepareStartRecording() -{ - WebRtc_Word32 res(0); - snd_pcm_sframes_t delayInFrames(0); - - // Check if mic is muted - bool muteEnabled = false; - MicrophoneMute(muteEnabled); - if (muteEnabled) - { - SetMicrophoneMute(false); - } - - // Check delay and available frames before reset - delayInFrames = -1; - res = LATE(snd_pcm_delay)(_handleRecord, &delayInFrames); - res = LATE(snd_pcm_avail_update)(_handleRecord); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Before reset: delayInFrames = %d, available frames = %d", - delayInFrames, res); - - // Reset pcm - res = LATE(snd_pcm_reset)(_handleRecord); - if (res < 0 ) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "Error resetting pcm: %s (%d)", - LATE(snd_strerror)(res), res); - } - - // Check delay and available frames after reset - delayInFrames = -1; - res = LATE(snd_pcm_delay)(_handleRecord, &delayInFrames); - res = LATE(snd_pcm_avail_update)(_handleRecord); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "After reset: delayInFrames = %d, available frames = %d " - "(rec buf size = %u)", - delayInFrames, res, _recSndcardBuffsize); - - if (res < 0) - { - res = 0; - } - - if (delayInFrames < 0) - { - delayInFrames = 0; - } - - // True if the driver gives the actual number of frames in the buffer (normal case). - // Cast is safe after check above. - _buffersizeFromZeroAvail = (unsigned int)res < (_recSndcardBuffsize/2); - _buffersizeFromZeroDelay = (unsigned int)delayInFrames < (_recSndcardBuffsize/2); - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::GetPlayoutBufferDelay() -{ - WebRtc_Word32 msPlay(0); - WebRtc_Word32 res(0); - WebRtc_UWord32 samplesPerMs = _samplingFreqPlay; - - snd_pcm_sframes_t delayInFrames(0); - - // Check how much is in playout buffer and check delay - if (0 == _bufferCheckMethodPlay) - { - // Using snd_pcm_avail_update for checking buffer is the method that - // shall be used according to documentation. If we however detect that - // returned available buffer is larger than the buffer size, we switch - // to using snd_pcm_delay. See -391. - - // Get delay - distance between current application frame position and - // sound frame position. - // This is only used for giving delay measurement to VE. - bool calcDelayFromAvail = false; - res = LATE(snd_pcm_delay)(_handlePlayout, &delayInFrames); - if (res < 0) - { - _writeErrors++; - if ( _writeErrors > 50 ) - { - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " pending playout error exists"); - } - _playError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " kPlayoutError message posted: _writeErrors=%u", - _writeErrors); - } - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "LinuxALSASndCardStream::playThreadProcess(), " - "snd_pcm_delay error (1): %s (%d)", - LATE(snd_strerror)(res), res); - calcDelayFromAvail = true; - ErrorRecovery(res, _handlePlayout); - _delayMonitorStatePlay = 1; // Go to delay monitor state - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Going to delay monitor state"); - } - else - { - _writeErrors=0; - _sndCardPlayDelay = delayInFrames / samplesPerMs; - } - - // Check if we should write more data to the soundcard. Updates - // the r/w pointer. - int avail = LATE(snd_pcm_avail_update)(_handlePlayout); - if (avail < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "LinuxALSASndCardStream::playThreadProcess()," - " snd_pcm_avail_update error: %s (%d)", - LATE(snd_strerror)(avail), avail); - res = ErrorRecovery(avail, _handlePlayout); - if (avail == -EPIPE) - { - res = LATE(snd_pcm_prepare)(_handlePlayout); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, "ErrorRecovery failed: %s", - LATE(snd_strerror)(res)); - } - FillPlayoutBuffer(); - msPlay = 0; - } - else - { - msPlay = 25; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Guessed ms in playout buffer = %d", msPlay); - _delayMonitorStatePlay = 1; // Go to delay monitor state - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Going to delay monitor state"); - } - else - { - // Calculate filled part of playout buffer size in ms - // Safe since _playSndcardBuffsize is a small number - int pb = (int)_playSndcardBuffsize; - assert(pb >= 0); - // If avail_update returns a value larger than playout buffer and it - // doesn't keep decreasing we switch method of checking the buffer. - if ((avail > pb) && (avail >= _lastBufferCheckValuePlay)) - { - msPlay = 0; // Continue to write to buffer - ++_bufferCheckErrorsPlay; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " _bufferCheckErrorsPlay = %d", - _bufferCheckErrorsPlay); - if (_bufferCheckErrorsPlay > 50) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, - " Switching to delay buffer check method " - "for playout"); - _bufferCheckMethodPlay = 1; // Switch to using snd_pcm_delay - _bufferCheckErrorsPlay = 0; - } - } - else - { - msPlay = pb > avail ? (pb - avail) / samplesPerMs : 0; - _bufferCheckErrorsPlay = 0; - } - _lastBufferCheckValuePlay = avail; - } - - if (calcDelayFromAvail) - { - _sndCardPlayDelay = msPlay; - } - // Here we monitor the delay value if we had an error - if (0 == _delayMonitorStatePlay) - { - // Normal state, just store delay value - _previousSndCardPlayDelay = _sndCardPlayDelay; - } - else if (1 == _delayMonitorStatePlay) - { - // We had an error, check if we get stuck in a long delay in playout. - // If so, restart device completely. Workaround for PulseAudio. - if ((_sndCardPlayDelay > 200) && - ((_sndCardPlayDelay > _previousSndCardPlayDelay * 2) || - (_sndCardPlayDelay > _previousSndCardPlayDelay + 200))) - { - if (_largeDelayCountPlay < 0) _largeDelayCountPlay = 0; - ++_largeDelayCountPlay; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " _largeDelayCountPlay = %d", - _largeDelayCountPlay); - if (_largeDelayCountPlay > 50) - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " Detected stuck in long delay after error " - "- restarting playout device"); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, - " _previousSndCardPlayDelay = %d," - " _sndCardPlayDelay = %d", - _previousSndCardPlayDelay, _sndCardPlayDelay); - StopPlayout(); - InitPlayout(); - res = LATE(snd_pcm_prepare)(_handlePlayout); - if (res < 0) - { - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - " Cannot prepare audio playout " - "interface for use: %s (%d)", - LATE(snd_strerror)(res), res); - } - FillPlayoutBuffer(); - _startPlay = true; - _delayMonitorStatePlay = 0; - _largeDelayCountPlay = 0; - // Make sure we only restart the device once. We could have had - // an error due to e.g. changed sink route in PulseAudio which would correctly - // lead to a larger delay. In this case we shouldn't get stuck restarting. - _previousSndCardPlayDelay = _sndCardPlayDelay; - return -1; - } - } - else - { - // No error, keep count of OK tests - if (_largeDelayCountPlay > 0) _largeDelayCountPlay = 0; - --_largeDelayCountPlay; - if (_largeDelayCountPlay < -50) - { - // After a couple of OK monitor tests, go back to normal state - _delayMonitorStatePlay = 0; - _largeDelayCountPlay = 0; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " Leaving delay monitor state"); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, - " _previousSndCardPlayDelay = %d," - " _sndCardPlayDelay = %d", - _previousSndCardPlayDelay, _sndCardPlayDelay); - } - } - } - else - { - // Should never happen - assert(false); - } - } - else if (1 == _bufferCheckMethodPlay) - { - // Check if we should write more data to the soundcard - // alternative method to get the delay (snd_pcm_avail_update() seem to - // give unreliable vaules in some cases!, i.e. with dmix) <- TL - // distance between current application frame position and sound frame - // position - res = LATE(snd_pcm_delay)(_handlePlayout, &delayInFrames); - if (res < 0 || res > (int)_playSndcardBuffsize) - { - int recoveryRes = ErrorRecovery(res, _handlePlayout); - if (res == -EPIPE) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "LinuxALSASndCardStream::playThreadProcess(), " - "outbuffer underrun"); - if (recoveryRes < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "ErrorRecovery failed: %s", - LATE(snd_strerror)(res)); - } - msPlay = 0; - } - else - { - _writeErrors++; - if (_writeErrors > 50) - { - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " pending playout error exists"); - } - _playError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, - " kPlayoutError message posted:" - " _writeErrors=%u", _writeErrors); - } - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, - "LinuxALSASndCardStream::playThreadProcess()," - " snd_pcm_delay error (2): %s (%d)", - LATE(snd_strerror)(res), res); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Playout buffer size=%d", _playSndcardBuffsize); - msPlay = 25; - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, - " Guessed ms in playout buffer = %d", msPlay); - } - } - else - { - _writeErrors = 0; - msPlay = delayInFrames / samplesPerMs; // playout buffer delay in ms - _sndCardPlayDelay = msPlay; - } - } - else - { - // Unknown _bufferCheckMethodPlay value, should never happen - assert(false); - } - - /* - delayInFrames = -1; - snd_pcm_delay(_handlePlayout, &delayInFrames); - // DEBUG END -*/ - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "msplay = %d", msPlay); - return msPlay; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::GetRecordingBufferDelay(bool preRead) -{ - WebRtc_Word32 msRec(0); - WebRtc_Word32 res(0); - WebRtc_UWord32 samplesPerMs = _samplingFreqRec; - - snd_pcm_sframes_t delayInFrames(0); - - if ((0 == _bufferCheckMethodRec) || (1 == _bufferCheckMethodRec)) - { - // Get delay, only used for input to VE - bool calcDelayFromAvail = false; - res = LATE(snd_pcm_delay)(_handleRecord, &delayInFrames); - if (res < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "LinuxALSASndCardStream::recThreadfun()," - " snd_pcm_delay (3) error: %s (%d)", - LATE(snd_strerror)(res), res); - ErrorRecovery(res, _handleRecord); - calcDelayFromAvail = true; // Must get estimate below instead - } - else if (0 == _bufferCheckMethodRec) - { - if (_buffersizeFromZeroDelay) - { - // Normal case - _sndCardRecDelay = delayInFrames / samplesPerMs; - } - else - { - // Safe since _recSndcardBuffsize is a small number - int rb = (int)_recSndcardBuffsize; - assert(rb >= 0); - _sndCardRecDelay = (rb >= delayInFrames ? - rb - delayInFrames : rb) / samplesPerMs; - } - } - // if method == 1 we calculate delay below to keep algorithm same as - // when we didn't have method 0. - - // Check if we have data in rec buffer. Updates the r/w pointer. - int avail = -1; - if (0 == _bufferCheckMethodRec) - { - avail = res = LATE(snd_pcm_avail_update)(_handleRecord); - } - if (res >= 0) - { - // We must check that state == RUNNING, otherwise we might have a - // false buffer value. - // Normal case - if (LATE(snd_pcm_state)(_handleRecord) == SND_PCM_STATE_RUNNING) - { - if (0 == _bufferCheckMethodRec) - { // Safe since _recSndcardBuffsize is a small number - int rb = (int)_recSndcardBuffsize; - if (_buffersizeFromZeroAvail) - { - // Normal case - msRec = avail / samplesPerMs; - } - else - { - assert(rb >= 0); - msRec = (rb >= avail ? rb - avail : rb) / samplesPerMs; - } - - if (calcDelayFromAvail) - { - _sndCardRecDelay = msRec; - } - - if ((msRec == 0) || (avail > rb)) - { - ++_bufferCheckErrorsRec; - WEBRTC_TRACE(kTraceInfo, - kTraceAudioDevice, _id, - " _bufferCheckErrorsRec: %d (avail=%d)", - _bufferCheckErrorsRec, avail); - if (_bufferCheckErrorsRec >= THR_OLD_BUFFER_CHECK_METHOD) - { - WEBRTC_TRACE(kTraceInfo, - kTraceAudioDevice, _id, - " Switching to delay buffer check" - " method for recording"); - _bufferCheckMethodRec = 1; - _bufferCheckErrorsRec = 0; - } - } - else - { - _bufferCheckErrorsRec = 0; - } - } - else // 1 == _bufferCheckMethodRec - { - if (_buffersizeFromZeroDelay) - { - msRec = delayInFrames / samplesPerMs; - } - else - { - msRec = - (_recSndcardBuffsize - delayInFrames) / samplesPerMs; - } - _sndCardRecDelay = msRec; - - if (msRec == 0) - { - ++_bufferCheckErrorsRec; - WEBRTC_TRACE(kTraceInfo, - kTraceAudioDevice, _id, - " _bufferCheckErrorsRec: %d", - _bufferCheckErrorsRec); - if (_bufferCheckErrorsRec >= THR_IGNORE_BUFFER_CHECK) - { - // The delay has been zero too many times, ignore - // the delay value! - WEBRTC_TRACE(kTraceInfo, - kTraceAudioDevice, _id, - " Switching to Ignore Delay Mode"); - _bufferCheckMethodRec = 2; - _bufferCheckErrorsRec = 0; - } - } - } - } - else if (LATE(snd_pcm_state)(_handleRecord) == SND_PCM_STATE_XRUN) - { - // We've probably had a buffer overrun - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Record buffer overrun, trying to recover"); - // Handle pipe error (overrun) - res = ErrorRecovery(-EPIPE, _handleRecord); - if (res < 0) - { - // We were not able to recover from the error. - // CRITICAL? - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, - "Can't recover from buffer overrun, " - "error: %s (%d)", - LATE(snd_strerror)(res), res); - return -1; - } - msRec = _recSndcardBuffsize / samplesPerMs; - } - } - else - { - // Something went wrong asking for the delay / buffer. Try to - // recover and make a guess. - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "LinuxALSASndCardStream::recThreadfun(), " - "snd_pcm_avail_update: %s (%d)", - LATE(snd_strerror)(res), res); - res = ErrorRecovery(avail, _handleRecord); - if (preRead) - { - if (res == 1) - { - // Recovered from buffer overrun, continue and read data. - msRec = _recSndcardBuffsize / samplesPerMs; - } - else - { - return -1; - } - } - else // We have a previous msRec value and have read maximum 10 ms since then. - { - if (res < 0) - { - return -1; - } - - msRec = _sndCardRecDelay - 10; - - if (calcDelayFromAvail) - { - _sndCardRecDelay = msRec; - } - } - } - } - else if (2 == _bufferCheckMethodRec) - { - // We've stopped asking for the number of samples on soundcard. - msRec = 0; - } - else - { - // Should never happen - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - "Unknown buffer check method (%d)", _bufferCheckMethodRec); - assert(false); - } - - return msRec; -} - -WebRtc_Word32 AudioDeviceLinuxALSA::ErrorRecovery(WebRtc_Word32 error, - snd_pcm_t* deviceHandle) -{ - int st = LATE(snd_pcm_state)(deviceHandle); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Trying to recover from error: %s (%d) (state %d)", - LATE(snd_strerror)(error), error, st); - - // It is recommended to use snd_pcm_recover for all errors. If that function - // cannot handle the error, the input error code will be returned, otherwise - // 0 is returned. From snd_pcm_recover API doc: "This functions handles - // -EINTR (interrupted system call),-EPIPE (overrun or underrun) and - // -ESTRPIPE (stream is suspended) error codes trying to prepare given - // stream for next I/O." - - // snd_pcm_recover isn't available in older alsa, e.g. on the FC4 machine - // in Sthlm lab. - - int res = LATE(snd_pcm_recover)(deviceHandle, error, 1); - if (0 == res) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Recovery - snd_pcm_recover OK"); - - if (error == -EPIPE && // Buffer underrun/overrun. - LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_CAPTURE) - { - // For capture streams we also have to repeat the explicit start() - // to get data flowing again. - int err = LATE(snd_pcm_start)(deviceHandle); - if (err != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recovery - snd_pcm_start error: %u", err); - return -1; - } - } - - return -EPIPE == error ? 1 : 0; - } - - return res; -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -bool AudioDeviceLinuxALSA::PlayThreadFunc(void* pThis) -{ - return (static_cast(pThis)->PlayThreadProcess()); -} - -bool AudioDeviceLinuxALSA::RecThreadFunc(void* pThis) -{ - return (static_cast(pThis)->RecThreadProcess()); -} - -bool AudioDeviceLinuxALSA::PlayThreadProcess() -{ - WebRtc_Word32 written(0); - WebRtc_Word32 msPlay(0); - - // Number of (stereo) samples - WebRtc_Word32 numPlaySamples = _playbackBufferSize / (2 * _playChannels); - WebRtc_Word8 playBuffer[_playbackBufferSize]; - - switch (_timeEventPlay.Wait(1000)) - { - case kEventSignaled: - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed => restarting timer"); - _timeEventPlay.StopTimer(); - _timeEventPlay.StartTimer(true, PLAY_TIMER_PERIOD_MS); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if (_startPlay) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startPlay true, performing initial actions"); - - _startPlay = false; - - // Fill playout buffer with zeroes - FillPlayoutBuffer(); - - _bufferCheckErrorsPlay = 0; - _playing = true; - _playStartEvent.Set(); - } - - if(_playing) - { - // get number of ms of sound that remains in the sound card buffer for - // playback - msPlay = GetPlayoutBufferDelay(); - if (msPlay == -1) - { - UnLock(); - return true; - } - - // write more data if below threshold - if (msPlay < PLAYBACK_THRESHOLD) - { - // ask for new PCM data to be played out using the AudioDeviceBuffer - // ensure that this callback is executed without taking the - // audio-thread lock - // - UnLock(); - WebRtc_Word32 nSamples = - (WebRtc_Word32)_ptrAudioBuffer->RequestPlayoutData(numPlaySamples); - Lock(); - - if (OutputSanityCheckAfterUnlockedPeriod() == -1) - { - UnLock(); - return true; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != numPlaySamples) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " invalid number of output samples(%d)", nSamples); - } - - written = LATE(snd_pcm_writei)(_handlePlayout, playBuffer, numPlaySamples); - if (written != numPlaySamples) - { - if (written < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, - "Error writing to sound device (7), error: %d/%s", - written, - LATE(snd_strerror)(written)); - - // Try to recover - ErrorRecovery(written, _handlePlayout); - _delayMonitorStatePlay = 1; // Go to delay monitor state - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, - " Going to delay monitor state"); - } - else - { - _writeErrors = 0; - int remainingFrames = (numPlaySamples - written); - written = LATE(snd_pcm_writei) - (_handlePlayout, - &playBuffer[written*2], - remainingFrames); - if( written == remainingFrames ) - { - written = numPlaySamples; - } - else - { - if (written < 0) - { - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "Error writing to sound device (8), " - "error: %d/%s, numPlaySamples=%d, " - "remainingFrames=%d", - written, LATE(snd_strerror)(written), - numPlaySamples, remainingFrames); - - // Try to recover - ErrorRecovery(written, _handlePlayout); - } - else - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - "Could not write all playout data (1)," - " numPlaySamples=%d, remainingFrames=%d," - " written=%d", - numPlaySamples, remainingFrames, written); - } - } - } - } - else - { - _writeErrors = 0; - } - - // Write more data if we are more than 10 ms under the threshold. - if (msPlay < PLAYBACK_THRESHOLD - 10) - { - // ask for new PCM data to be played out using the - // AudioDeviceBuffer ensure that this callback is executed - // without taking the audio-thread lock - // - UnLock(); - WebRtc_Word32 nSamples = (WebRtc_Word32) - _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); - Lock(); - - if (OutputSanityCheckAfterUnlockedPeriod() == -1) - { - UnLock(); - return true; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != numPlaySamples) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " invalid number of output samples(%d)", - nSamples); - } - - written = LATE(snd_pcm_writei)( - _handlePlayout, playBuffer, numPlaySamples); - if (written != numPlaySamples) - { - if (written < 0) - { - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "Error writing to sound device (9), " - "error: %s", LATE(snd_strerror)(written)); - - // Try to recover - ErrorRecovery(written, _handlePlayout); - _delayMonitorStatePlay = 1; // Go to delay monitor state - WEBRTC_TRACE(kTraceInfo, - kTraceAudioDevice, _id, - " Going to delay monitor state"); - } - else - { - int remainingFrames = (numPlaySamples - written); - written = LATE(snd_pcm_writei) - (_handlePlayout, - &playBuffer[written*2], - remainingFrames); - if (written == remainingFrames) - { - written = numPlaySamples; - } - else - { - if (written < 0) - { - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "Error writing to sound device (10)," - " error: %d/%s, numPlaySamples=%d," - " remainingFrames=%d", - written, LATE(snd_strerror)(written), - numPlaySamples, remainingFrames); - - // Try to recover - ErrorRecovery(written, _handlePlayout); - } - else - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - "Could not write all playout data" - " (2), numPlaySamples=%d, " - "remainingFrames=%d, written=%d", - numPlaySamples, remainingFrames, - written); - } - } - } - - } - } // msPlay < PLAYBACK_THRESHOLD - 10 - - } // msPlay < PLAYBACK_THRESHOLD - - } // _playing - - UnLock(); - return true; -} - -bool AudioDeviceLinuxALSA::RecThreadProcess() -{ - WebRtc_Word32 msRec(0); - WebRtc_Word32 framesInRecData(0); - - // Number of (stereo) samples to record - WebRtc_Word32 recBufSizeInSamples = _recordBufferSize / (2 * _recChannels); - WebRtc_Word16 tmpBuffer[_recordBufferSize / 2]; - WebRtc_UWord32 samplesPerMs = _samplingFreqRec; - - switch (_timeEventRec.Wait(1000)) - { - case kEventSignaled: - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed => restarting timer"); - _timeEventRec.StopTimer(); - _timeEventRec.StartTimer(true, REC_TIMER_PERIOD_MS); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if (_startRec) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startRec true, performing initial actions"); - - if (PrepareStartRecording() == 0) - { - _bufferCheckErrorsRec = 0; - _startRec = false; - _recording = true; - _recStartEvent.Set(); - } - } - - if (_recording) - { - // get number of ms of sound that remains in the sound card buffer for - // playback - msRec = GetRecordingBufferDelay(true); - if (msRec == -1) - { - UnLock(); - return true; - } - - // read data if a whole frame has been captured - // or if we are in ignore delay mode (check method 2) - if ((msRec > 10) || (2 == _bufferCheckMethodRec)) - { - // Read 10 ms of data from soundcard - framesInRecData = LATE(snd_pcm_readi) - (_handleRecord, - tmpBuffer, - recBufSizeInSamples); - - if (framesInRecData < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "pcm read error (1)"); - ErrorRecovery(framesInRecData, _handleRecord); - UnLock(); - return true; - } - else if (framesInRecData + (WebRtc_Word32)_numReadyRecSamples < - recBufSizeInSamples) - { - for (int idx = 0; idx < framesInRecData*_recChannels; idx++) - { - _recBuffer[_numReadyRecSamples*_recChannels + idx] = - tmpBuffer[idx]; - } - _numReadyRecSamples += framesInRecData; - - framesInRecData = LATE(snd_pcm_readi) - (_handleRecord, - tmpBuffer, - recBufSizeInSamples - _numReadyRecSamples); - - if (framesInRecData < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, "pcm read error (2)"); - ErrorRecovery(framesInRecData, _handleRecord); - UnLock(); - return true; - } - else if (framesInRecData + (WebRtc_Word32)_numReadyRecSamples == - recBufSizeInSamples) - { - // We got all the data we need, go on as normal. - } - else - { - // We still don't have enough data, copy what we have and leave. - for (int idx = 0; idx < framesInRecData*_recChannels; idx++) - { - _recBuffer[_numReadyRecSamples*_recChannels + idx] = - tmpBuffer[idx]; - } - _numReadyRecSamples += framesInRecData; - WEBRTC_TRACE(kTraceStream, - kTraceAudioDevice, _id, - " %d samples copied. Not enough, return and" - " wait for more.", - framesInRecData); - UnLock(); - return true; - } - } - - // get recording buffer delay after reading - // to have a value to use for the AEC - msRec = GetRecordingBufferDelay(false); - if (msRec == -1) - { - UnLock(); - return true; - } - - // calculate the number of samples to copy - // to have a full buffer - int copySamples = 0; - if ((WebRtc_Word32)_numReadyRecSamples + framesInRecData >= - recBufSizeInSamples) - { - copySamples = recBufSizeInSamples - _numReadyRecSamples; - } - else - { - copySamples = framesInRecData; - } - - // fill up buffer - for (int idx = 0; idx < copySamples*_recChannels; idx++) - { - _recBuffer[_numReadyRecSamples*_recChannels + idx] = - tmpBuffer[idx]; - } - - _numReadyRecSamples += copySamples; - framesInRecData -= copySamples; - - // Send data, if we have 10ms data... - if ((WebRtc_Word32)_numReadyRecSamples == recBufSizeInSamples) - { - WebRtc_UWord32 currentMicLevel(0); - WebRtc_UWord32 newMicLevel(0); - WebRtc_Word32 msRecDelay = 0 == _bufferCheckMethodRec ? - _sndCardRecDelay : msRec; - WebRtc_Word32 msReady = _numReadyRecSamples / samplesPerMs; - WebRtc_Word32 msStored = framesInRecData / samplesPerMs; - WebRtc_Word32 blockSize = recBufSizeInSamples / samplesPerMs; - - // TODO(xians): The blockSize - 25 term brings the delay measurement - // into line with the Windows interpretation. Investigate if this - // works properly with different block sizes. - // TODO(xians): Should only the rec delay from snd_pcm_delay be taken - // into account? See ALSA API doc. - // Probably we want to add the remaining data in the buffer as - // well or is that already in any of the variables? - WebRtc_Word32 msTotalRecDelay = msRecDelay + msReady + - msStored + blockSize - 25; - if (msTotalRecDelay < 0) - { - msTotalRecDelay = 0; - } - // store the recorded buffer (no action will be taken if the - // #recorded samples is not a full buffer) - _ptrAudioBuffer->SetRecordedBuffer( - (WebRtc_Word8 *)&_recBuffer[0], _numReadyRecSamples); - - if (AGC()) - { - // store current mic level in the audio buffer if AGC is enabled - if (MicrophoneVolume(currentMicLevel) == 0) - { - if (currentMicLevel == 0xffffffff) - { - currentMicLevel = 100; - } - // this call does not affect the actual microphone volume - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); - } - } - - // store vqe delay values - _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, - msTotalRecDelay, - 0); - - // deliver recorded samples at specified sample rate, mic level - // etc. to the observer using callback - UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - Lock(); - - if (InputSanityCheckAfterUnlockedPeriod() == -1) - { - UnLock(); - return true; - } - - if (AGC()) - { - newMicLevel = _ptrAudioBuffer->NewMicLevel(); - if (newMicLevel != 0) - { - // The VQE will only deliver non-zero microphone levels - //when a change is needed. - // Set this new mic level (received from the observer - // as return value in the callback). - WEBRTC_TRACE(kTraceStream, - kTraceAudioDevice, _id, - " AGC change of volume: old=%u => new=%u", - currentMicLevel, newMicLevel); - if (SetMicrophoneVolume(newMicLevel) == -1) - { - WEBRTC_TRACE(kTraceWarning, - kTraceAudioDevice, _id, - " the required modification of the" - " microphone volume failed"); - } - } - } - - _numReadyRecSamples = 0; - - // if there are remaining samples in tmpBuffer - // copy those to _recBuffer - if (framesInRecData > 0) - { - WEBRTC_TRACE(kTraceStream, - kTraceAudioDevice, _id, - " Got rest samples, copy %d samples to rec" - " buffer", framesInRecData); - for (int idx = 0; idx < framesInRecData; idx++) - { - _recBuffer[idx] = tmpBuffer[copySamples+idx]; - } - - _numReadyRecSamples = framesInRecData; - } - - } // if (_numReadyRecSamples == recBufSizeInSamples) - - } // (msRec > 10) || (2 == _bufferCheckMethodRec) - - } // _recording - - UnLock(); - return true; -} - -} diff --git a/modules/audio_device/main/source/Linux/audio_device_linux_alsa.h b/modules/audio_device/main/source/Linux/audio_device_linux_alsa.h deleted file mode 100644 index 6f599ef4b..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_linux_alsa.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_LINUX_ALSA_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_LINUX_ALSA_H - -#include "audio_device_generic.h" -#include "critical_section_wrapper.h" -#include "audio_mixer_manager_linux_alsa.h" - -#include -#include - -#include - -namespace webrtc -{ -class EventWrapper; -class ThreadWrapper; - -// Number of continuous buffer check errors before going 0->1 -const WebRtc_UWord16 THR_OLD_BUFFER_CHECK_METHOD = 30; -// Number of buffer check errors before going 1->2 -const WebRtc_UWord16 THR_IGNORE_BUFFER_CHECK = 30; -// 2.7 seconds (decimal 131071) -const WebRtc_UWord32 ALSA_SNDCARD_BUFF_SIZE_REC = 0x1ffff; -// ~170 ms (decimal 8191) - enough since we only write to buffer if it contains -// less than 50 ms -const WebRtc_UWord32 ALSA_SNDCARD_BUFF_SIZE_PLAY = 0x1fff; - -const WebRtc_UWord32 REC_TIMER_PERIOD_MS = 2; -const WebRtc_UWord32 PLAY_TIMER_PERIOD_MS = 5; -const WebRtc_UWord16 PLAYBACK_THRESHOLD = 50; - -const WebRtc_UWord32 REC_SAMPLES_PER_MS = 48; -const WebRtc_UWord32 PLAY_SAMPLES_PER_MS = 48; - -class AudioDeviceLinuxALSA : public AudioDeviceGeneric -{ -public: - AudioDeviceLinuxALSA(const WebRtc_Word32 id); - ~AudioDeviceLinuxALSA(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: - WebRtc_Word32 GetDevicesInfo(const WebRtc_Word32 function, - const bool playback, - const WebRtc_Word32 enumDeviceNo = 0, - char* enumDeviceName = NULL, - const WebRtc_Word32 ednLen = 0) const; - WebRtc_Word32 ErrorRecovery(WebRtc_Word32 error, snd_pcm_t* deviceHandle); - void FillPlayoutBuffer(); - -private: - void Lock() { _critSect.Enter(); }; - void UnLock() { _critSect.Leave(); }; -private: - inline WebRtc_Word32 InputSanityCheckAfterUnlockedPeriod() const; - inline WebRtc_Word32 OutputSanityCheckAfterUnlockedPeriod() const; - - WebRtc_Word32 PrepareStartRecording(); - WebRtc_Word32 GetPlayoutBufferDelay(); - WebRtc_Word32 GetRecordingBufferDelay(bool preRead); - -private: - static bool RecThreadFunc(void*); - static bool PlayThreadFunc(void*); - bool RecThreadProcess(); - bool PlayThreadProcess(); - -private: - AudioDeviceBuffer* _ptrAudioBuffer; - - CriticalSectionWrapper& _critSect; - EventWrapper& _timeEventRec; - EventWrapper& _timeEventPlay; - EventWrapper& _recStartEvent; - EventWrapper& _playStartEvent; - - ThreadWrapper* _ptrThreadPlay; - ThreadWrapper* _ptrThreadRec; - WebRtc_UWord32 _recThreadID; - WebRtc_UWord32 _playThreadID; - - WebRtc_Word32 _id; - - AudioMixerManagerLinuxALSA _mixerManager; - - bool _usingInputDeviceIndex; - bool _usingOutputDeviceIndex; - AudioDeviceModule::WindowsDeviceType _inputDevice; - AudioDeviceModule::WindowsDeviceType _outputDevice; - WebRtc_UWord16 _inputDeviceIndex; - WebRtc_UWord16 _outputDeviceIndex; - bool _inputDeviceIsSpecified; - bool _outputDeviceIsSpecified; - - snd_pcm_t* _handleRecord; - snd_pcm_t* _handlePlayout; - - snd_pcm_uframes_t _recSndcardBuffsize; - snd_pcm_uframes_t _playSndcardBuffsize; - - WebRtc_UWord32 _samplingFreqRec; - WebRtc_UWord32 _samplingFreqPlay; - WebRtc_UWord8 _recChannels; - WebRtc_UWord8 _playChannels; - - WebRtc_UWord32 _playbackBufferSize; - WebRtc_UWord32 _recordBufferSize; - WebRtc_Word16* _recBuffer; - AudioDeviceModule::BufferType _playBufType; - -private: - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _startRec; - bool _stopRec; - bool _startPlay; - bool _stopPlay; - bool _AGC; - bool _buffersizeFromZeroAvail; - bool _buffersizeFromZeroDelay; - - WebRtc_UWord32 _sndCardPlayDelay; // Just to store last value - WebRtc_UWord32 _previousSndCardPlayDelay; // Stores previous _sndCardPlayDelay value - WebRtc_UWord8 _delayMonitorStatePlay; // 0 normal, 1 monitor delay change (after error) - WebRtc_Word16 _largeDelayCountPlay; // Used when monitoring delay change - WebRtc_UWord32 _sndCardRecDelay; - WebRtc_UWord32 _numReadyRecSamples; - - WebRtc_UWord8 _bufferCheckMethodPlay; - WebRtc_UWord8 _bufferCheckMethodRec; - WebRtc_UWord32 _bufferCheckErrorsPlay; - WebRtc_UWord32 _bufferCheckErrorsRec; - WebRtc_Word32 _lastBufferCheckValuePlay; - WebRtc_Word32 _writeErrors; - - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - WebRtc_UWord16 _playBufDelay; // playback delay - WebRtc_UWord16 _playBufDelayFixed; // fixed playback delay -}; - -} - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_DEVICE_LINUX_ALSA_H_ diff --git a/modules/audio_device/main/source/Linux/audio_device_linux_pulse.cc b/modules/audio_device/main/source/Linux/audio_device_linux_pulse.cc deleted file mode 100644 index 7d3942b6b..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_linux_pulse.cc +++ /dev/null @@ -1,3248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "audio_device_utility.h" -#include "audio_device_linux_pulse.h" -#include "audio_device_config.h" - -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" - -webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; - -// Accesses Pulse functions through our late-binding symbol table instead of -// directly. This way we don't have to link to libpulse, which means our binary -// will work on systems that don't have it. -#define LATE(sym) \ - LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) - -namespace webrtc -{ - -// ============================================================================ -// Static Methods -// ============================================================================ - -bool AudioDeviceLinuxPulse::PulseAudioIsSupported() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, "%s", - __FUNCTION__); - - bool pulseAudioIsSupported(true); - - // Check that we can initialize - AudioDeviceLinuxPulse* admPulse = new AudioDeviceLinuxPulse(-1); - if (admPulse->InitPulseAudio() == -1) - { - pulseAudioIsSupported = false; - } - admPulse->TerminatePulseAudio(); - delete admPulse; - - if (pulseAudioIsSupported) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, - "*** Linux Pulse Audio is supported ***"); - } else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, - "*** Linux Pulse Audio is NOT supported => will revert to the ALSA API ***"); - } - - return (pulseAudioIsSupported); -} - -AudioDeviceLinuxPulse::AudioDeviceLinuxPulse(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _timeEventRec(*EventWrapper::Create()), - _timeEventPlay(*EventWrapper::Create()), - _recStartEvent(*EventWrapper::Create()), - _playStartEvent(*EventWrapper::Create()), - _ptrThreadPlay(NULL), - _ptrThreadRec(NULL), - _recThreadID(0), - _playThreadID(0), - _id(id), - _mixerManager(id), - _inputDeviceIndex(0), - _outputDeviceIndex(0), - _inputDeviceIsSpecified(false), - _outputDeviceIsSpecified(false), - _samplingFreq(0), - _recChannels(1), - _playChannels(1), - _playBufType(AudioDeviceModule::kFixedBufferSize), - _initialized(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _startRec(false), - _stopRec(false), - _startPlay(false), - _stopPlay(false), - _AGC(false), - _playBufDelayFixed(20), - _sndCardPlayDelay(0), - _sndCardRecDelay(0), - _writeErrors(0), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _deviceIndex(-1), - _numPlayDevices(0), - _numRecDevices(0), - _playDeviceName(NULL), - _recDeviceName(NULL), - _playDisplayDeviceName(NULL), - _recDisplayDeviceName(NULL), - _playBuffer(NULL), - _playbackBufferSize(0), - _playbackBufferUnused(0), - _tempBufferSpace(0), - _recBuffer(NULL), - _recordBufferSize(0), - _recordBufferUsed(0), - _tempSampleData(NULL), - _tempSampleDataSize(0), - _configuredLatencyPlay(0), - _configuredLatencyRec(0), - _paDeviceIndex(-1), - _paStateChanged(false), - _paMainloop(NULL), - _paMainloopApi(NULL), - _paContext(NULL), - _recStream(NULL), - _playStream(NULL), - _recStreamFlags(0), - _playStreamFlags(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); - - memset(_paServerVersion, 0, sizeof(_paServerVersion)); -} - -AudioDeviceLinuxPulse::~AudioDeviceLinuxPulse() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - - Terminate(); - - if (_recBuffer) - { - delete _recBuffer; - } - if (_playBuffer) - { - delete _playBuffer; - } - if (_playDeviceName) - { - delete _playDeviceName; - } - if (_recDeviceName) - { - delete _recDeviceName; - } - if (_playDisplayDeviceName) - { - delete _playDisplayDeviceName; - } - if (_recDisplayDeviceName) - { - delete _recDisplayDeviceName; - } - - delete &_recStartEvent; - delete &_playStartEvent; - delete &_timeEventRec; - delete &_timeEventPlay; - delete &_critSect; -} - -void AudioDeviceLinuxPulse::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // Inform the AudioBuffer about default settings for this implementation. - // Set all values to zero here since the actual settings will be done by - // InitPlayout and InitRecording later. - _ptrAudioBuffer->SetRecordingSampleRate(0); - _ptrAudioBuffer->SetPlayoutSampleRate(0); - _ptrAudioBuffer->SetRecordingChannels(0); - _ptrAudioBuffer->SetPlayoutChannels(0); -} - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceLinuxPulse::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kLinuxPulseAudio; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - // Initialize PulseAudio - if (InitPulseAudio() < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize PulseAudio"); - - if (TerminatePulseAudio() < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to terminate PulseAudio"); - } - - return -1; - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - // RECORDING - const char* threadName = "webrtc_audio_module_rec_thread"; - _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, - kRealtimePriority, threadName); - if (_ptrThreadRec == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the rec audio thread"); - return -1; - } - - unsigned int threadID(0); - if (!_ptrThreadRec->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the rec audio thread"); - - delete _ptrThreadRec; - _ptrThreadRec = NULL; - return -1; - } - _recThreadID = threadID; - - // PLAYOUT - threadName = "webrtc_audio_module_play_thread"; - _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, this, - kRealtimePriority, threadName); - if (_ptrThreadPlay == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to create the play audio thread"); - return -1; - } - - threadID = 0; - if (!_ptrThreadPlay->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " failed to start the play audio thread"); - - delete _ptrThreadPlay; - _ptrThreadPlay = NULL; - return -1; - } - _playThreadID = threadID; - - _initialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_initialized) - { - return 0; - } - - _critSect.Enter(); - - _mixerManager.Close(); - - // RECORDING - if (_ptrThreadRec) - { - ThreadWrapper* tmpThread = _ptrThreadRec; - _ptrThreadRec = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventRec.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the rec audio thread"); - } - } - - // PLAYOUT - if (_ptrThreadPlay) - { - ThreadWrapper* tmpThread = _ptrThreadPlay; - _ptrThreadPlay = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEventPlay.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to close down the play audio thread"); - } - } - - // Terminate PulseAudio - if (TerminatePulseAudio() < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to terminate PulseAudio"); - return -1; - } - - _initialized = false; - _outputDeviceIsSpecified = false; - _inputDeviceIsSpecified = false; - - return 0; -} - -bool AudioDeviceLinuxPulse::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_initialized); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker exists - // - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (!_outputDeviceIsSpecified) - { - return -1; - } - - // check if default device - if (_outputDeviceIndex == 0) - { - WebRtc_UWord16 deviceIndex = 0; - GetDefaultDeviceInfo(false, NULL, deviceIndex); - _paDeviceIndex = deviceIndex; - } else - { - // get the PA device index from - // the callback - _deviceIndex = _outputDeviceIndex; - - // get playout devices - PlayoutDevices(); - } - - // the callback has now set the _paDeviceIndex to - // the PulseAudio index of the device - if (_mixerManager.OpenSpeaker(_paDeviceIndex) == -1) - { - return -1; - } - - // clear _deviceIndex - _deviceIndex = -1; - _paDeviceIndex = -1; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid microphone - // exists - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (!_inputDeviceIsSpecified) - { - return -1; - } - - // Check if default device - if (_inputDeviceIndex == 0) - { - WebRtc_UWord16 deviceIndex = 0; - GetDefaultDeviceInfo(true, NULL, deviceIndex); - _paDeviceIndex = deviceIndex; - } else - { - // Get the PA device index from - // the callback - _deviceIndex = _inputDeviceIndex; - - // get recording devices - RecordingDevices(); - } - - // The callback has now set the _paDeviceIndex to - // the PulseAudio index of the device - if (_mixerManager.OpenMicrophone(_paDeviceIndex) == -1) - { - return -1; - } - - // Clear _deviceIndex - _deviceIndex = -1; - _paDeviceIndex = -1; - - return 0; -} - -bool AudioDeviceLinuxPulse::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.SpeakerIsInitialized()); -} - -bool AudioDeviceLinuxPulse::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.MicrophoneIsInitialized()); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control. - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a volume control exists - available = true; - - // Close the initialized output mixer - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetSpeakerVolume(volume=%u)", volume); - - return (_mixerManager.SetSpeakerVolume(volume)); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.SpeakerVolume(level) == -1) - { - return -1; - } - - volume = level; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetWaveOutVolume( - WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, - volumeRight); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::WaveOutVolume( - WebRtc_UWord16& /*volumeLeft*/, - WebRtc_UWord16& /*volumeRight*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MaxSpeakerVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MinSpeakerVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinSpeakerVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control, hence it is safe to state that there is no mute control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected speaker has a mute control - _mixerManager.SpeakerMuteIsAvailable(isAvailable); - - available = isAvailable; - - // Close the initialized output mixer - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetSpeakerMute(enable=%u)", enable); - - return (_mixerManager.SetSpeakerMute(enable)); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - if (_mixerManager.SpeakerMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a mute control - // - _mixerManager.MicrophoneMuteIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "SetMicrophoneMute(enable=%u)", enable); - - return (_mixerManager.SetMicrophoneMute(enable)); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - if (_mixerManager.MicrophoneMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Enumerate all avaliable microphone and make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a boost control - _mixerManager.MicrophoneBoostIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetMicrophoneBoost(enable=%u)", enable); - - return (_mixerManager.SetMicrophoneBoost(enable)); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool onOff(0); - - if (_mixerManager.MicrophoneBoost(onOff) == -1) - { - return -1; - } - - enabled = onOff; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - if (!wasInitialized && InitMicrophone() == -1) - { - // Cannot open the specified device - available = false; - return 0; - } - -#ifndef WEBRTC_PA_GTALK - // Check if the selected microphone can record stereo - bool isAvailable(false); - _mixerManager.StereoRecordingIsAvailable(isAvailable); - available = isAvailable; -#endif - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetStereoRecording(enable=%u)", enable); - -#ifndef WEBRTC_PA_GTALK - if (enable) - _recChannels = 2; - else - _recChannels = 1; -#endif - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_recChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - if (!wasInitialized && InitSpeaker() == -1) - { - // Cannot open the specified device - available = false; - return 0; - } - -#ifndef WEBRTC_PA_GTALK - // Check if the selected microphone can record stereo - bool isAvailable(false); - _mixerManager.StereoPlayoutIsAvailable(isAvailable); - available = isAvailable; -#endif - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetStereoPlayout(enable=%u)", enable); - -#ifndef WEBRTC_PA_GTALK - if (enable) - _playChannels = 2; - else - _playChannels = 1; -#endif - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_playChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -bool AudioDeviceLinuxPulse::AGC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _AGC; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneVolumeIsAvailable( - bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control. - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a volume control - // exists - available = true; - - // Close the initialized input mixer - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetMicrophoneVolume(volume=%u)", - volume); - - return (_mixerManager.SetMicrophoneVolume(volume)); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneVolume( - WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.MicrophoneVolume(level) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to retrive current microphone level"); - return -1; - } - - volume = level; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MaxMicrophoneVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MinMicrophoneVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinMicrophoneVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - - return 0; -} - -WebRtc_Word16 AudioDeviceLinuxPulse::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - PaLock(); - - pa_operation* paOperation = NULL; - _numPlayDevices = 1; // init to 1 to account for "default" - - // get the whole list of devices and update _numPlayDevices - paOperation = LATE(pa_context_get_sink_info_list)(_paContext, - PaSinkInfoCallback, - this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - return _numPlayDevices; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - const WebRtc_UWord16 nDevices(PlayoutDevices()); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable output devices is %u", nDevices); - - if (index > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices - 1)); - return -1; - } - - _outputDeviceIndex = index; - _outputDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::PlayoutDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(PlayoutDevices()); - - if ((index > (nDevices - 1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - // Check if default device - if (index == 0) - { - WebRtc_UWord16 deviceIndex = 0; - return GetDefaultDeviceInfo(false, name, deviceIndex); - } - - // Tell the callback that we want - // The name for this device - _playDisplayDeviceName = name; - _deviceIndex = index; - - // get playout devices - PlayoutDevices(); - - // clear device name and index - _playDisplayDeviceName = NULL; - _deviceIndex = -1; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::RecordingDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(RecordingDevices()); - - if ((index > (nDevices - 1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - // Check if default device - if (index == 0) - { - WebRtc_UWord16 deviceIndex = 0; - return GetDefaultDeviceInfo(true, name, deviceIndex); - } - - // Tell the callback that we want - // the name for this device - _recDisplayDeviceName = name; - _deviceIndex = index; - - // Get recording devices - RecordingDevices(); - - // Clear device name and index - _recDisplayDeviceName = NULL; - _deviceIndex = -1; - - return 0; -} - -WebRtc_Word16 AudioDeviceLinuxPulse::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - PaLock(); - - pa_operation* paOperation = NULL; - _numRecDevices = 1; // Init to 1 to account for "default" - - // Get the whole list of devices and update _numRecDevices - paOperation = LATE(pa_context_get_source_info_list)(_paContext, - PaSourceInfoCallback, - this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - return _numRecDevices; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - const WebRtc_UWord16 nDevices(RecordingDevices()); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable input devices is %u", nDevices); - - if (index > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices - 1)); - return -1; - } - - _inputDeviceIndex = index; - _inputDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - - return res; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - - return res; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (!_outputDeviceIsSpecified) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - // Set sampling rate to use - WebRtc_UWord32 samplingRate = _samplingFreq * 1000; - if (samplingRate == 44000) - { - samplingRate = 44100; - } - - // Set the play sample specification - pa_sample_spec playSampleSpec; - playSampleSpec.channels = _playChannels; - playSampleSpec.format = PA_SAMPLE_S16LE; - playSampleSpec.rate = samplingRate; - - // Create a new play stream - _playStream = LATE(pa_stream_new)(_paContext, "playStream", - &playSampleSpec, NULL); - - if (!_playStream) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create play stream, err=%d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - // Provide the playStream to the mixer - _mixerManager.SetPlayStream(_playStream); - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetPlayoutSampleRate(_samplingFreq * 1000); - _ptrAudioBuffer->SetPlayoutChannels((WebRtc_UWord8) _playChannels); - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " stream state %d\n", LATE(pa_stream_get_state)(_playStream)); - - // Set stream flags - _playStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE - | PA_STREAM_INTERPOLATE_TIMING); - - if (_configuredLatencyPlay != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) - { - // If configuring a specific latency then we want to specify - // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters - // automatically to reach that target latency. However, that flag doesn't - // exist in Ubuntu 8.04 and many people still use that, so we have to check - // the protocol version of libpulse. - if (LATE(pa_context_get_protocol_version)(_paContext) - >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) - { - _playStreamFlags |= PA_STREAM_ADJUST_LATENCY; - } - - const pa_sample_spec *spec = - LATE(pa_stream_get_sample_spec)(_playStream); - if (!spec) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " pa_stream_get_sample_spec()"); - return -1; - } - - size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); - WebRtc_UWord32 latency = bytesPerSec - * WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS / WEBRTC_PA_MSECS_PER_SEC; - - // Set the play buffer attributes - _playBufferAttr.maxlength = latency; // num bytes stored in the buffer - _playBufferAttr.tlength = latency; // target fill level of play buffer - // minimum free num bytes before server request more data - _playBufferAttr.minreq = latency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; - _playBufferAttr.prebuf = _playBufferAttr.tlength - - _playBufferAttr.minreq; // prebuffer tlength before starting playout - - _configuredLatencyPlay = latency; - } - - // num samples in bytes * num channels - _playbackBufferSize = _samplingFreq * 10 * 2 * _playChannels; - _playbackBufferUnused = _playbackBufferSize; - _playBuffer = new WebRtc_Word8[_playbackBufferSize]; - - // Enable underflow callback - LATE(pa_stream_set_underflow_callback)(_playStream, - PaStreamUnderflowCallback, this); - - // Set the state callback function for the stream - LATE(pa_stream_set_state_callback)(_playStream, PaStreamStateCallback, this); - - // Mark playout side as initialized - _playIsInitialized = true; - _sndCardPlayDelay = 0; - _sndCardRecDelay = 0; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (!_inputDeviceIsSpecified) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - // Set sampling rate to use - WebRtc_UWord32 samplingRate = _samplingFreq * 1000; - if (samplingRate == 44000) - { - samplingRate = 44100; - } - - // Set the rec sample specification - pa_sample_spec recSampleSpec; - recSampleSpec.channels = _recChannels; - recSampleSpec.format = PA_SAMPLE_S16LE; - recSampleSpec.rate = samplingRate; - - // Create a new rec stream - _recStream = LATE(pa_stream_new)(_paContext, "recStream", &recSampleSpec, - NULL); - if (!_recStream) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create rec stream, err=%d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - // Provide the recStream to the mixer - _mixerManager.SetRecStream(_recStream); - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetRecordingSampleRate(_samplingFreq * 1000); - _ptrAudioBuffer->SetRecordingChannels((WebRtc_UWord8) _recChannels); - } - - if (_configuredLatencyRec != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) - { - _recStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE - | PA_STREAM_INTERPOLATE_TIMING); - - // If configuring a specific latency then we want to specify - // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters - // automatically to reach that target latency. However, that flag doesn't - // exist in Ubuntu 8.04 and many people still use that, so we have to check - // the protocol version of libpulse. - if (LATE(pa_context_get_protocol_version)(_paContext) - >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) - { - _recStreamFlags |= PA_STREAM_ADJUST_LATENCY; - } - - const pa_sample_spec *spec = - LATE(pa_stream_get_sample_spec)(_recStream); - if (!spec) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " pa_stream_get_sample_spec(rec)"); - return -1; - } - - size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); - WebRtc_UWord32 latency = bytesPerSec - * WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS / WEBRTC_PA_MSECS_PER_SEC; - - // Set the rec buffer attributes - // Note: fragsize specifies a maximum transfer size, not a minimum, so - // it is not possible to force a high latency setting, only a low one. - _recBufferAttr.fragsize = latency; // size of fragment - _recBufferAttr.maxlength = latency + bytesPerSec - * WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS / WEBRTC_PA_MSECS_PER_SEC; - - _configuredLatencyRec = latency; - } - - _recordBufferSize = _samplingFreq * 10 * 2 * _recChannels; - _recordBufferUsed = 0; - _recBuffer = new WebRtc_Word8[_recordBufferSize]; - - // Enable overflow callback - LATE(pa_stream_set_overflow_callback)(_recStream, PaStreamOverflowCallback, - this); - - // Set the state callback function for the stream - LATE(pa_stream_set_state_callback)(_recStream, PaStreamStateCallback, this); - - // Mark recording side as initialized - _recIsInitialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_recIsInitialized) - { - return -1; - } - - if (_recording) - { - return 0; - } - - // set state to ensure that the recording starts from the audio thread - _startRec = true; - - // the audio thread will signal when recording has started - _timeEventRec.Set(); - if (kEventTimeout == _recStartEvent.Wait(10000)) - { - _startRec = false; - StopRecording(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate recording"); - return -1; - } - - if (_recording) - { - // the recording state is set by the audio thread after recording has started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - " recording is now active"); - } else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate recording"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return 0; - } - - if (_recStream == NULL) - { - return -1; - } - - _recIsInitialized = false; - _recording = false; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " stopping recording"); - - // Stop Recording - PaLock(); - - DisableReadCallback(); - LATE(pa_stream_set_overflow_callback)(_recStream, NULL, NULL); - - // Unset this here so that we don't get a TERMINATED callback - LATE(pa_stream_set_state_callback)(_recStream, NULL, NULL); - - if (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_UNCONNECTED) - { - // Disconnect the stream - if (LATE(pa_stream_disconnect)(_recStream) != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to disconnect rec stream, err=%d\n", - LATE(pa_context_errno)(_paContext)); - PaUnLock(); - return -1; - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " disconnected recording"); - } - - LATE(pa_stream_unref)(_recStream); - _recStream = NULL; - - PaUnLock(); - - // Provide the recStream to the mixer - _mixerManager.SetRecStream(_recStream); - - if (_recBuffer) - { - delete _recBuffer; - _recBuffer = NULL; - } - - return 0; -} - -bool AudioDeviceLinuxPulse::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recIsInitialized); -} - -bool AudioDeviceLinuxPulse::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recording); -} - -bool AudioDeviceLinuxPulse::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playIsInitialized); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return -1; - } - - if (_playing) - { - return 0; - } - - // set state to ensure that playout starts from the audio thread - _startPlay = true; - - // the audio thread will signal when playout has started - _timeEventPlay.Set(); - if (kEventTimeout == _playStartEvent.Wait(10000)) - { - _startPlay = false; - StopPlayout(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate playout"); - return -1; - } - - if (_playing) - { - // the playing state is set by the audio thread after playout has started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - " playing is now active"); - } else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to activate playing"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - return 0; - } - - if (_playStream == NULL) - { - return -1; - } - - _playIsInitialized = false; - _playing = false; - _sndCardPlayDelay = 0; - _sndCardRecDelay = 0; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " stopping playback"); - - // Stop Playout - PaLock(); - - DisableWriteCallback(); - LATE(pa_stream_set_underflow_callback)(_playStream, NULL, NULL); - - // Unset this here so that we don't get a TERMINATED callback - LATE(pa_stream_set_state_callback)(_playStream, NULL, NULL); - - if (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_UNCONNECTED) - { - // Disconnect the stream - if (LATE(pa_stream_disconnect)(_playStream) != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to disconnect play stream, err=%d", - LATE(pa_context_errno)(_paContext)); - PaUnLock(); - return -1; - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " disconnected playback"); - } - - LATE(pa_stream_unref)(_playStream); - _playStream = NULL; - - PaUnLock(); - - // Provide the playStream to the mixer - _mixerManager.SetPlayStream(_playStream); - - if (_playBuffer) - { - delete _playBuffer; - _playBuffer = NULL; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - delayMS = (WebRtc_UWord16) _sndCardPlayDelay; - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - delayMS = (WebRtc_UWord16) _sndCardRecDelay; - return 0; -} - -bool AudioDeviceLinuxPulse::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playing); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceLinuxPulse::SetPlayoutBuffer(type=%u, sizeMS=%u)", - type, sizeMS); - - if (type != AudioDeviceModule::kFixedBufferSize) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Adaptive buffer size not supported on this platform"); - return -1; - } - - _playBufType = type; - _playBufDelayFixed = sizeMS; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - type = _playBufType; - sizeMS = _playBufDelayFixed; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::CPULoad(WebRtc_UWord16& /*load*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -bool AudioDeviceLinuxPulse::PlayoutWarning() const -{ - return (_playWarning > 0); -} - -bool AudioDeviceLinuxPulse::PlayoutError() const -{ - return (_playError > 0); -} - -bool AudioDeviceLinuxPulse::RecordingWarning() const -{ - return (_recWarning > 0); -} - -bool AudioDeviceLinuxPulse::RecordingError() const -{ - return (_recError > 0); -} - -void AudioDeviceLinuxPulse::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -void AudioDeviceLinuxPulse::ClearPlayoutError() -{ - _playError = 0; -} - -void AudioDeviceLinuxPulse::ClearRecordingWarning() -{ - _recWarning = 0; -} - -void AudioDeviceLinuxPulse::ClearRecordingError() -{ - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -void AudioDeviceLinuxPulse::PaContextStateCallback(pa_context *c, void *pThis) -{ - static_cast (pThis)->PaContextStateCallbackHandler( - c); -} - -// ---------------------------------------------------------------------------- -// PaSinkInfoCallback -// ---------------------------------------------------------------------------- - -void AudioDeviceLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, - const pa_sink_info *i, int eol, - void *pThis) -{ - static_cast (pThis)->PaSinkInfoCallbackHandler( - i, eol); -} - -void AudioDeviceLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, - const pa_source_info *i, - int eol, void *pThis) -{ - static_cast (pThis)->PaSourceInfoCallbackHandler( - i, eol); -} - -void AudioDeviceLinuxPulse::PaServerInfoCallback(pa_context */*c*/, - const pa_server_info *i, - void *pThis) -{ - static_cast (pThis)->PaServerInfoCallbackHandler(i); -} - -void AudioDeviceLinuxPulse::PaStreamStateCallback(pa_stream *p, void *pThis) -{ - static_cast (pThis)->PaStreamStateCallbackHandler(p); -} - -void AudioDeviceLinuxPulse::PaContextStateCallbackHandler(pa_context *c) -{ - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " context state cb"); - - pa_context_state_t state = LATE(pa_context_get_state)(c); - switch (state) - { - case PA_CONTEXT_UNCONNECTED: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " unconnected"); - break; - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - default: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " no state"); - break; - case PA_CONTEXT_FAILED: - case PA_CONTEXT_TERMINATED: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " failed"); - _paStateChanged = true; - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - break; - case PA_CONTEXT_READY: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " ready"); - _paStateChanged = true; - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - break; - } -} - -void AudioDeviceLinuxPulse::PaSinkInfoCallbackHandler(const pa_sink_info *i, - int eol) -{ - if (eol) - { - // Signal that we are done - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - return; - } - - if (_numPlayDevices == _deviceIndex) - { - // Convert the device index to the one of the sink - _paDeviceIndex = i->index; - - if (_playDeviceName) - { - // Copy the sink name - strncpy(_playDeviceName, i->name, kAdmMaxDeviceNameSize); - _playDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - if (_playDisplayDeviceName) - { - // Copy the sink display name - strncpy(_playDisplayDeviceName, i->description, - kAdmMaxDeviceNameSize); - _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - } - - _numPlayDevices++; -} - -void AudioDeviceLinuxPulse::PaSourceInfoCallbackHandler( - const pa_source_info *i, - int eol) -{ - if (eol) - { - // Signal that we are done - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - return; - } - - // We don't want to list output devices - if (!strncmp(i->name, "alsa_input", 10)) - { - if (_numRecDevices == _deviceIndex) - { - // Convert the device index to the one of the source - _paDeviceIndex = i->index; - - if (_recDeviceName) - { - // copy the source name - strncpy(_recDeviceName, i->name, kAdmMaxDeviceNameSize); - _recDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - if (_recDisplayDeviceName) - { - // Copy the source display name - strncpy(_recDisplayDeviceName, i->description, - kAdmMaxDeviceNameSize); - _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - } - - _numRecDevices++; - } -} - -void AudioDeviceLinuxPulse::PaServerInfoCallbackHandler(const pa_server_info *i) -{ - // Use PA native sampling rate - WebRtc_UWord32 paSampleRate = i->sample_spec.rate; - if (paSampleRate == 44100) - { -#ifdef WEBRTC_PA_GTALK - paSampleRate = 48000; -#else - paSampleRate = 44000; -#endif - } - - _samplingFreq = paSampleRate / 1000; - - // Copy the PA server version - if (_paServerVersion) - { - strncpy(_paServerVersion, i->server_version, 31); - _paServerVersion[31] = '\0'; - } - - if (_recDisplayDeviceName) - { - // Copy the source name - strncpy(_recDisplayDeviceName, i->default_source_name, - kAdmMaxDeviceNameSize); - _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - - if (_playDisplayDeviceName) - { - // Copy the sink name - strncpy(_playDisplayDeviceName, i->default_sink_name, - kAdmMaxDeviceNameSize); - _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; - } - - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); -} - -void AudioDeviceLinuxPulse::PaStreamStateCallbackHandler(pa_stream *p) -{ - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " stream state cb"); - - pa_stream_state_t state = LATE(pa_stream_get_state)(p); - switch (state) - { - case PA_STREAM_UNCONNECTED: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " unconnected"); - break; - case PA_STREAM_CREATING: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " creating"); - break; - default: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " no state"); - break; - case PA_STREAM_FAILED: - case PA_STREAM_TERMINATED: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " failed"); - break; - case PA_STREAM_READY: - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " ready"); - break; - } - - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::CheckPulseAudioVersion() -{ - /*WebRtc_Word32 index = 0; - WebRtc_Word32 partIndex = 0; - WebRtc_Word32 partNum = 1; - WebRtc_Word32 minVersion[3] = {0, 9, 15}; - bool versionOk = false; - char str[8] = {0};*/ - - PaLock(); - - pa_operation* paOperation = NULL; - - // get the server info and update deviceName - paOperation = LATE(pa_context_get_server_info)(_paContext, - PaServerInfoCallback, this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, - " checking PulseAudio version: %s", _paServerVersion); - - /* Saved because it may turn out that we need to check the version in the future - while (true) - { - if (_paServerVersion[index] == '.') - { - index++; - str[partIndex] = '\0'; - partIndex = 0; - - if(partNum == 2) - { - if (atoi(str) < minVersion[1]) - { - break; - } - partNum = 3; - } - else - { - if (atoi(str) > minVersion[0]) - { - versionOk = true; - break; - } - partNum = 2; - } - } - else if (_paServerVersion[index] == '\0' || _paServerVersion[index] == '-') - { - str[partIndex] = '\0'; - if (atoi(str) >= minVersion[2]) - { - versionOk = true; - } - break; - } - - str[partIndex] = _paServerVersion[index]; - index++; - partIndex++; - } - - if (!versionOk) - { - return -1; - } - */ - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitSamplingFrequency() -{ - PaLock(); - - pa_operation* paOperation = NULL; - - // Get the server info and update _samplingFreq - paOperation = LATE(pa_context_get_server_info)(_paContext, - PaServerInfoCallback, this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::GetDefaultDeviceInfo(bool recDevice, - WebRtc_Word8* name, - WebRtc_UWord16& index) -{ - WebRtc_Word8 tmpName[kAdmMaxDeviceNameSize]; - // subtract length of "default: " - WebRtc_UWord16 nameLen = kAdmMaxDeviceNameSize - 9; - WebRtc_Word8* pName = NULL; - - if (name) - { - // Add "default: " - strcpy(name, "default: "); - pName = &name[9]; - } - - // Tell the callback that we want - // the name for this device - if (recDevice) - { - _recDisplayDeviceName = tmpName; - } else - { - _playDisplayDeviceName = tmpName; - } - - // Set members - _paDeviceIndex = -1; - _deviceIndex = 0; - _numPlayDevices = 0; - _numRecDevices = 0; - - PaLock(); - - pa_operation* paOperation = NULL; - - // Get the server info and update deviceName - paOperation = LATE(pa_context_get_server_info)(_paContext, - PaServerInfoCallback, this); - - WaitForOperationCompletion(paOperation); - - // Get the device index - if (recDevice) - { - paOperation - = LATE(pa_context_get_source_info_by_name)(_paContext, - (char *) tmpName, - PaSourceInfoCallback, - this); - } else - { - paOperation - = LATE(pa_context_get_sink_info_by_name)(_paContext, - (char *) tmpName, - PaSinkInfoCallback, this); - } - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - // Set the index - index = _paDeviceIndex; - - if (name) - { - // Copy to name string - strncpy(pName, tmpName, nameLen); - } - - // Clear members - _playDisplayDeviceName = NULL; - _recDisplayDeviceName = NULL; - _paDeviceIndex = -1; - _deviceIndex = -1; - _numPlayDevices = 0; - _numRecDevices = 0; - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::InitPulseAudio() -{ - int retVal = 0; - - // Load libpulse - if (!PaSymbolTable.Load()) - { - // Most likely the Pulse library and sound server are not installed on - // this system - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to load symbol table"); - return -1; - } - - // Create a mainloop API and connection to the default server - // the mainloop is the internal asynchronous API event loop - _paMainloop = LATE(pa_threaded_mainloop_new)(); - if (!_paMainloop) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " could not create mainloop"); - return -1; - } - - // Start the threaded main loop - retVal = LATE(pa_threaded_mainloop_start)(_paMainloop); - if (retVal != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to start main loop, error=%d", retVal); - return -1; - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " mainloop running!"); - - PaLock(); - - _paMainloopApi = LATE(pa_threaded_mainloop_get_api)(_paMainloop); - if (!_paMainloopApi) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " could not create mainloop API"); - PaUnLock(); - return -1; - } - - // Create a new PulseAudio context - _paContext = LATE(pa_context_new)(_paMainloopApi, "WEBRTC VoiceEngine"); - - if (!_paContext) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " could not create context"); - PaUnLock(); - return -1; - } - - // Set state callback function - LATE(pa_context_set_state_callback)(_paContext, PaContextStateCallback, - this); - - // Connect the context to a server (default) - _paStateChanged = false; - retVal = LATE(pa_context_connect)(_paContext, NULL, PA_CONTEXT_NOAUTOSPAWN, - NULL); - - if (retVal != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to connect context, error=%d", retVal); - PaUnLock(); - return -1; - } - - // Wait for state change - while (!_paStateChanged) - { - LATE(pa_threaded_mainloop_wait)(_paMainloop); - } - - // Now check to see what final state we reached. - pa_context_state_t state = LATE(pa_context_get_state)(_paContext); - - if (state != PA_CONTEXT_READY) - { - if (state == PA_CONTEXT_FAILED) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to connect to PulseAudio sound server"); - } else if (state == PA_CONTEXT_TERMINATED) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " PulseAudio connection terminated early"); - } else - { - // Shouldn't happen, because we only signal on one of those three - // states - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " unknown problem connecting to PulseAudio"); - } - PaUnLock(); - return -1; - } - - PaUnLock(); - - // Give the objects to the mixer manager - _mixerManager.SetPulseAudioObjects(_paMainloop, _paContext); - - // Check the version - if (CheckPulseAudioVersion() < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " PulseAudio version %s not supported", _paServerVersion); - return -1; - } - - // Initialize sampling frequency - if (InitSamplingFrequency() < 0 || _samplingFreq == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize sampling frequency, set to %d", - _samplingFreq); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::TerminatePulseAudio() -{ - // Do nothing if the instance doesn't exist - // likely PaSymbolTable.Load() fails - if (!_paMainloop) { - return 0; - } - - PaLock(); - - // Disconnect the context - if (_paContext) - { - LATE(pa_context_disconnect)(_paContext); - } - - // Unreference the context - if (_paContext) - { - LATE(pa_context_unref)(_paContext); - } - - PaUnLock(); - _paContext = NULL; - - // Stop the threaded main loop - if (_paMainloop) - { - LATE(pa_threaded_mainloop_stop)(_paMainloop); - } - - // Free the mainloop - if (_paMainloop) - { - LATE(pa_threaded_mainloop_free)(_paMainloop); - } - - _paMainloop = NULL; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " PulseAudio terminated"); - - return 0; -} - -void AudioDeviceLinuxPulse::PaLock() -{ - LATE(pa_threaded_mainloop_lock)(_paMainloop); -} - -void AudioDeviceLinuxPulse::PaUnLock() -{ - LATE(pa_threaded_mainloop_unlock)(_paMainloop); -} - -void AudioDeviceLinuxPulse::WaitForOperationCompletion( - pa_operation* paOperation) const -{ - while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) - { - LATE(pa_threaded_mainloop_wait)(_paMainloop); - } - - LATE(pa_operation_unref)(paOperation); -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -void AudioDeviceLinuxPulse::EnableWriteCallback() -{ - if (LATE(pa_stream_get_state)(_playStream) == PA_STREAM_READY) - { - // May already have available space. Must check. - _tempBufferSpace = LATE(pa_stream_writable_size)(_playStream); - if (_tempBufferSpace > 0) - { - // Yup, there is already space available, so if we register a write - // callback then it will not receive any event. So dispatch one ourself - // instead - _timeEventPlay.Set(); - return; - } - } - - LATE(pa_stream_set_write_callback)(_playStream, &PaStreamWriteCallback, - this); -} - -void AudioDeviceLinuxPulse::DisableWriteCallback() -{ - LATE(pa_stream_set_write_callback)(_playStream, NULL, NULL); -} - -void AudioDeviceLinuxPulse::PaStreamWriteCallback(pa_stream */*unused*/, - size_t buffer_space, - void *pThis) -{ - static_cast (pThis)->PaStreamWriteCallbackHandler( - buffer_space); -} - -void AudioDeviceLinuxPulse::PaStreamWriteCallbackHandler(size_t bufferSpace) -{ - _tempBufferSpace = bufferSpace; - - // Since we write the data asynchronously on a different thread, we have - // to temporarily disable the write callback or else Pulse will call it - // continuously until we write the data. We re-enable it below. - DisableWriteCallback(); - _timeEventPlay.Set(); -} - -void AudioDeviceLinuxPulse::PaStreamUnderflowCallback(pa_stream */*unused*/, - void *pThis) -{ - static_cast (pThis)->PaStreamUnderflowCallbackHandler(); -} - -void AudioDeviceLinuxPulse::PaStreamUnderflowCallbackHandler() -{ - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Playout underflow"); - - if (_configuredLatencyPlay == WEBRTC_PA_NO_LATENCY_REQUIREMENTS) - { - // We didn't configure a pa_buffer_attr before, so switching to one now - // would be questionable. - return; - } - - // Otherwise reconfigure the stream with a higher target latency. - - const pa_sample_spec *spec = LATE(pa_stream_get_sample_spec)(_playStream); - if (!spec) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " pa_stream_get_sample_spec()"); - return; - } - - size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); - WebRtc_UWord32 newLatency = _configuredLatencyPlay + bytesPerSec - * WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS / WEBRTC_PA_MSECS_PER_SEC; - - // Set the play buffer attributes - _playBufferAttr.maxlength = newLatency; - _playBufferAttr.tlength = newLatency; - _playBufferAttr.minreq = newLatency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; - _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; - - pa_operation *op = LATE(pa_stream_set_buffer_attr)(_playStream, - &_playBufferAttr, NULL, - NULL); - if (!op) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " pa_stream_set_buffer_attr()"); - return; - } - - // Don't need to wait for this to complete. - LATE(pa_operation_unref)(op); - - // Save the new latency in case we underflow again. - _configuredLatencyPlay = newLatency; -} - -void AudioDeviceLinuxPulse::EnableReadCallback() -{ - LATE(pa_stream_set_read_callback)(_recStream, &PaStreamReadCallback, this); -} - -void AudioDeviceLinuxPulse::DisableReadCallback() -{ - LATE(pa_stream_set_read_callback)(_recStream, NULL, NULL); -} - -void AudioDeviceLinuxPulse::PaStreamReadCallback(pa_stream */*unused1*/, - size_t /*unused2*/, - void *pThis) -{ - static_cast (pThis)->PaStreamReadCallbackHandler(); -} - -void AudioDeviceLinuxPulse::PaStreamReadCallbackHandler() -{ - // We get the data pointer and size now in order to save one Lock/Unlock - // in the worker thread - if (LATE(pa_stream_peek)(_recStream, &_tempSampleData, &_tempSampleDataSize) - != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Can't read data!"); - return; - } - - // Since we consume the data asynchronously on a different thread, we have - // to temporarily disable the read callback or else Pulse will call it - // continuously until we consume the data. We re-enable it below - DisableReadCallback(); - _timeEventRec.Set(); -} - -void AudioDeviceLinuxPulse::PaStreamOverflowCallback(pa_stream */*unused*/, - void *pThis) -{ - static_cast (pThis)->PaStreamOverflowCallbackHandler(); -} - -void AudioDeviceLinuxPulse::PaStreamOverflowCallbackHandler() -{ - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Recording overflow"); -} - -WebRtc_Word32 AudioDeviceLinuxPulse::LatencyUsecs(pa_stream *stream) -{ - if (!WEBRTC_PA_REPORT_LATENCY) - { - return 0; - } - - if (!stream) - { - return 0; - } - - pa_usec_t latency; - int negative; - if (LATE(pa_stream_get_latency)(stream, &latency, &negative) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Can't query latency"); - // We'd rather continue playout/capture with an incorrect delay than stop - // it altogether, so return a valid value. - return 0; - } - - if (negative) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " warning: pa_stream_get_latency reported negative delay"); - - // The delay can be negative for monitoring streams if the captured - // samples haven't been played yet. In such a case, "latency" contains the - // magnitude, so we must negate it to get the real value. - WebRtc_Word32 tmpLatency = (WebRtc_Word32) -latency; - if (tmpLatency < 0) - { - // Make sure that we don't use a negative delay - tmpLatency = 0; - } - - return tmpLatency; - } else - { - return (WebRtc_Word32) latency; - } -} - -WebRtc_Word32 AudioDeviceLinuxPulse::ReadRecordedData(const void* bufferData, - size_t bufferSize) -{ - size_t size = bufferSize; - WebRtc_UWord32 numRecSamples = _recordBufferSize / (2 * _recChannels); - - // Account for the peeked data and the used data - WebRtc_UWord32 recDelay = (WebRtc_UWord32) ((LatencyUsecs(_recStream) - / 1000) + 10 * ((size + _recordBufferUsed) / _recordBufferSize)); - - _sndCardRecDelay = recDelay; - - if (_playStream) - { - // Get the playout delay - _sndCardPlayDelay = (WebRtc_UWord32) (LatencyUsecs(_playStream) / 1000); - } - - if (_recordBufferUsed > 0) - { - // Have to copy to the buffer until it is full - size_t copy = _recordBufferSize - _recordBufferUsed; - if (size < copy) - { - copy = size; - } - - memcpy(&_recBuffer[_recordBufferUsed], bufferData, copy); - _recordBufferUsed += copy; - bufferData = static_cast (bufferData) + copy; - size -= copy; - - if (_recordBufferUsed != _recordBufferSize) - { - // Not enough data yet to pass to VoE - return 0; - } - - // Provide data to VoiceEngine - if (ProcessRecordedData(_recBuffer, numRecSamples, recDelay) == -1) - { - // We have stopped recording - return -1; - } - - _recordBufferUsed = 0; - } - - // Now process full 10ms sample sets directly from the input - while (size >= _recordBufferSize) - { - // Provide data to VoiceEngine - if (ProcessRecordedData( - static_cast (const_cast (bufferData)), - numRecSamples, recDelay) == -1) - { - // We have stopped recording - return -1; - } - - bufferData = static_cast (bufferData) + _recordBufferSize; - size -= _recordBufferSize; - - // We have consumed 10ms of data - recDelay -= 10; - } - - // Now save any leftovers for later. - if (size > 0) - { - memcpy(_recBuffer, bufferData, size); - _recordBufferUsed = size; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceLinuxPulse::ProcessRecordedData( - WebRtc_Word8 *bufferData, - WebRtc_UWord32 bufferSizeInSamples, - WebRtc_UWord32 recDelay) -{ - WebRtc_UWord32 currentMicLevel(0); - WebRtc_UWord32 newMicLevel(0); - - _ptrAudioBuffer->SetRecordedBuffer(bufferData, bufferSizeInSamples); - - if (AGC()) - { - // Store current mic level in the audio buffer if AGC is enabled - if (MicrophoneVolume(currentMicLevel) == 0) - { - // This call does not affect the actual microphone volume - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); - } - } - - // Set vqe data - const WebRtc_UWord32 clockDrift(0); - _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, recDelay, clockDrift); - - // Deliver recorded samples at specified sample rate, - // mic level etc. to the observer using callback - UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - Lock(); - - // We have been unlocked - check the flag again - if (!_recording) - { - return -1; - } - - if (AGC()) - { - newMicLevel = _ptrAudioBuffer->NewMicLevel(); - if (newMicLevel != 0) - { - // The VQE will only deliver non-zero microphone levels when a - // change is needed. - // Set this new mic level (received from the observer as return - // value in the callback). - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, - " AGC change of volume: old=%u => new=%u", - currentMicLevel, newMicLevel); - if (SetMicrophoneVolume(newMicLevel) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, - " the required modification of the microphone " - "volume failed"); - } - } - } - - return 0; -} - -bool AudioDeviceLinuxPulse::PlayThreadFunc(void* pThis) -{ - return (static_cast (pThis)->PlayThreadProcess()); -} - -bool AudioDeviceLinuxPulse::RecThreadFunc(void* pThis) -{ - return (static_cast (pThis)->RecThreadProcess()); -} - -bool AudioDeviceLinuxPulse::PlayThreadProcess() -{ - switch (_timeEventPlay.Wait(1000)) - { - case kEventSignaled: - _timeEventPlay.Reset(); - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed"); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if (_startPlay) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startPlay true, performing initial actions"); - - _startPlay = false; - _playDeviceName = NULL; - - // Set if not default device - if (_outputDeviceIndex > 0) - { - // Get the playout device name - _playDeviceName = new WebRtc_Word8[kAdmMaxDeviceNameSize]; - _deviceIndex = _outputDeviceIndex; - PlayoutDevices(); - } - - // Start muted only supported on 0.9.11 and up - if (LATE(pa_context_get_protocol_version)(_paContext) - >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) - { - // Get the currently saved speaker mute status - // and set the initial mute status accordingly - bool enabled(false); - _mixerManager.SpeakerMute(enabled); - if (enabled) - { - _playStreamFlags |= PA_STREAM_START_MUTED; - } - } - - // Get the currently saved speaker volume - WebRtc_UWord32 volume = 0; - _mixerManager.SpeakerVolume(volume); - - PaLock(); - - // Set the same volume for all channels - pa_cvolume cVolumes; - const pa_sample_spec *spec = - LATE(pa_stream_get_sample_spec)(_playStream); - LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); - - // Connect the stream to a sink - if (LATE(pa_stream_connect_playback)( - _playStream, - _playDeviceName, - &_playBufferAttr, - (pa_stream_flags_t) _playStreamFlags, - &cVolumes, NULL) != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to connect play stream, err=%d", - LATE(pa_context_errno)(_paContext)); - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " play stream connected"); - - // Wait for state change - while (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_READY) - { - LATE(pa_threaded_mainloop_wait)(_paMainloop); - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " play stream ready"); - - // We can now handle write callbacks - EnableWriteCallback(); - - PaUnLock(); - - // Clear device name - if (_playDeviceName) - { - delete[] _playDeviceName; - _playDeviceName = NULL; - } - - _playing = true; - _playStartEvent.Set(); - - UnLock(); - return true; - } - - if (_playing) - { - if (!_recording) - { - // Update the playout delay - _sndCardPlayDelay = (WebRtc_UWord32) (LatencyUsecs(_playStream) - / 1000); - } - - if (_playbackBufferUnused < _playbackBufferSize) - { - - size_t write = _playbackBufferSize - _playbackBufferUnused; - if (_tempBufferSpace < write) - { - write = _tempBufferSpace; - } - - PaLock(); - if (LATE(pa_stream_write)( - _playStream, - (void *) &_playBuffer[_playbackBufferUnused], - write, NULL, (int64_t) 0, - PA_SEEK_RELATIVE) != PA_OK) - { - _writeErrors++; - if (_writeErrors > 10) - { - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, - kTraceUtility, _id, - " pending playout error exists"); - } - _playError = 1; // Triggers callback from module process thread - WEBRTC_TRACE( - kTraceError, - kTraceUtility, - _id, - " kPlayoutError message posted: " - "_writeErrors=%u, error=%d", - _writeErrors, - LATE(pa_context_errno)(_paContext)); - _writeErrors = 0; - } - } - PaUnLock(); - - _playbackBufferUnused += write; - _tempBufferSpace -= write; - } - - WebRtc_UWord32 numPlaySamples = _playbackBufferSize / (2 - * _playChannels); - if (_tempBufferSpace > 0) // Might have been reduced to zero by the above - { - // Ask for new PCM data to be played out using the AudioDeviceBuffer - // ensure that this callback is executed without taking the - // audio-thread lock - UnLock(); - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " requesting data"); - WebRtc_UWord32 nSamples = - _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); - Lock(); - - // We have been unlocked - check the flag again - if (!_playing) - { - UnLock(); - return true; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(_playBuffer); - if (nSamples != numPlaySamples) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " invalid number of output samples(%d)", - nSamples); - } - - size_t write = _playbackBufferSize; - if (_tempBufferSpace < write) - { - write = _tempBufferSpace; - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " will write"); - PaLock(); - if (LATE(pa_stream_write)(_playStream, (void *) &_playBuffer[0], - write, NULL, (int64_t) 0, - PA_SEEK_RELATIVE) != PA_OK) - { - _writeErrors++; - if (_writeErrors > 10) - { - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, - kTraceUtility, _id, - " pending playout error exists"); - } - _playError = 1; // triggers callback from module process thread - WEBRTC_TRACE( - kTraceError, - kTraceUtility, - _id, - " kPlayoutError message posted: " - "_writeErrors=%u, error=%d", - _writeErrors, - LATE(pa_context_errno)(_paContext)); - _writeErrors = 0; - } - } - PaUnLock(); - - _playbackBufferUnused = write; - } - - _tempBufferSpace = 0; - PaLock(); - EnableWriteCallback(); - PaUnLock(); - - } // _playing - - UnLock(); - return true; -} - -bool AudioDeviceLinuxPulse::RecThreadProcess() -{ - switch (_timeEventRec.Wait(1000)) - { - case kEventSignaled: - _timeEventRec.Reset(); - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "EventWrapper::Wait() failed"); - return true; - case kEventTimeout: - return true; - } - - Lock(); - - if (_startRec) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "_startRec true, performing initial actions"); - - _recDeviceName = NULL; - - // Set if not default device - if (_inputDeviceIndex > 0) - { - // Get the recording device name - _recDeviceName = new WebRtc_Word8[kAdmMaxDeviceNameSize]; - _deviceIndex = _inputDeviceIndex; - RecordingDevices(); - } - - PaLock(); - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " connecting stream"); - - // Connect the stream to a source - if (LATE(pa_stream_connect_record)(_recStream, _recDeviceName, - &_recBufferAttr, - (pa_stream_flags_t) _recStreamFlags) - != PA_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to connect rec stream, err=%d", - LATE(pa_context_errno)(_paContext)); - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " connected"); - - // Wait for state change - while (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_READY) - { - LATE(pa_threaded_mainloop_wait)(_paMainloop); - } - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " done"); - - // We can now handle read callbacks - EnableReadCallback(); - - PaUnLock(); - - // Clear device name - if (_recDeviceName) - { - delete[] _recDeviceName; - _recDeviceName = NULL; - } - - _startRec = false; - _recording = true; - _recStartEvent.Set(); - - UnLock(); - return true; - } - - if (_recording) - { - // Read data and provide it to VoiceEngine - if (ReadRecordedData(_tempSampleData, _tempSampleDataSize) == -1) - { - UnLock(); - return true; - } - - _tempSampleData = NULL; - _tempSampleDataSize = 0; - - PaLock(); - while (true) - { - // Ack the last thing we read - if (LATE(pa_stream_drop)(_recStream) != 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " failed to drop, err=%d\n", - LATE(pa_context_errno)(_paContext)); - } - - if (LATE(pa_stream_readable_size)(_recStream) <= 0) - { - // Then that was all the data - break; - } - - // Else more data. - const void *sampleData; - size_t sampleDataSize; - - if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) - != 0) - { - _recError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, - _id, " RECORD_ERROR message posted, error = %d", - LATE(pa_context_errno)(_paContext)); - break; - } - - _sndCardRecDelay = (WebRtc_UWord32) (LatencyUsecs(_recStream) - / 1000); - - // Drop lock for sigslot dispatch, which could take a while. - PaUnLock(); - // Read data and provide it to VoiceEngine - if (ReadRecordedData(sampleData, sampleDataSize) == -1) - { - UnLock(); - return true; - } - PaLock(); - - // Return to top of loop for the ack and the check for more data. - } - - EnableReadCallback(); - PaUnLock(); - - } // _recording - - UnLock(); - return true; -} - -} diff --git a/modules/audio_device/main/source/Linux/audio_device_linux_pulse.h b/modules/audio_device/main/source/Linux/audio_device_linux_pulse.h deleted file mode 100644 index 12f1c5ebd..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_linux_pulse.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_LINUX_PULSE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_LINUX_PULSE_H - -#include "audio_device_generic.h" -#include "audio_mixer_manager_linux_pulse.h" -#include "critical_section_wrapper.h" - -#include - -// Set this define to make the code behave like in GTalk/libjingle -//#define WEBRTC_PA_GTALK - -// We define this flag if it's missing from our headers, because we want to be -// able to compile against old headers but still use PA_STREAM_ADJUST_LATENCY -// if run against a recent version of the library. -#ifndef PA_STREAM_ADJUST_LATENCY -#define PA_STREAM_ADJUST_LATENCY 0x2000U -#endif -#ifndef PA_STREAM_START_MUTED -#define PA_STREAM_START_MUTED 0x1000U -#endif - -// Set this constant to 0 to disable latency reading -const WebRtc_UWord32 WEBRTC_PA_REPORT_LATENCY = 1; - -// Constants from implementation by Tristan Schmelcher [tschmelcher@google.com] - -// First PulseAudio protocol version that supports PA_STREAM_ADJUST_LATENCY. -const WebRtc_UWord32 WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION = 13; - -// Some timing constants for optimal operation. See -// https://tango.0pointer.de/pipermail/pulseaudio-discuss/2008-January/001170.html -// for a good explanation of some of the factors that go into this. - -// Playback. - -// For playback, there is a round-trip delay to fill the server-side playback -// buffer, so setting too low of a latency is a buffer underflow risk. We will -// automatically increase the latency if a buffer underflow does occur, but we -// also enforce a sane minimum at start-up time. Anything lower would be -// virtually guaranteed to underflow at least once, so there's no point in -// allowing lower latencies. -const WebRtc_UWord32 WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS = 20; - -// Every time a playback stream underflows, we will reconfigure it with target -// latency that is greater by this amount. -const WebRtc_UWord32 WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS = 20; - -// We also need to configure a suitable request size. Too small and we'd burn -// CPU from the overhead of transfering small amounts of data at once. Too large -// and the amount of data remaining in the buffer right before refilling it -// would be a buffer underflow risk. We set it to half of the buffer size. -const WebRtc_UWord32 WEBRTC_PA_PLAYBACK_REQUEST_FACTOR = 2; - -// Capture. - -// For capture, low latency is not a buffer overflow risk, but it makes us burn -// CPU from the overhead of transfering small amounts of data at once, so we set -// a recommended value that we use for the kLowLatency constant (but if the user -// explicitly requests something lower then we will honour it). -// 1ms takes about 6-7% CPU. 5ms takes about 5%. 10ms takes about 4.x%. -const WebRtc_UWord32 WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS = 10; - -// There is a round-trip delay to ack the data to the server, so the -// server-side buffer needs extra space to prevent buffer overflow. 20ms is -// sufficient, but there is no penalty to making it bigger, so we make it huge. -// (750ms is libpulse's default value for the _total_ buffer size in the -// kNoLatencyRequirements case.) -const WebRtc_UWord32 WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS = 750; - -const WebRtc_UWord32 WEBRTC_PA_MSECS_PER_SEC = 1000; - -// Init _configuredLatencyRec/Play to this value to disable latency requirements -const WebRtc_Word32 WEBRTC_PA_NO_LATENCY_REQUIREMENTS = -1; - -// Set this const to 1 to account for peeked and used data in latency calculation -const WebRtc_UWord32 WEBRTC_PA_CAPTURE_BUFFER_LATENCY_ADJUSTMENT = 0; - -namespace webrtc -{ -class EventWrapper; -class ThreadWrapper; - -class AudioDeviceLinuxPulse: public AudioDeviceGeneric -{ -public: - AudioDeviceLinuxPulse(const WebRtc_Word32 id); - ~AudioDeviceLinuxPulse(); - - static bool PulseAudioIsSupported(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 - ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 - SetPlayoutBuffer(const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: - void Lock() - { - _critSect.Enter(); - } - ; - void UnLock() - { - _critSect.Leave(); - } - ; - void WaitForOperationCompletion(pa_operation* paOperation) const; - void WaitForSuccess(pa_operation* paOperation) const; - -private: - static void PaContextStateCallback(pa_context *c, void *pThis); - static void PaSinkInfoCallback(pa_context *c, const pa_sink_info *i, - int eol, void *pThis); - static void PaSourceInfoCallback(pa_context *c, const pa_source_info *i, - int eol, void *pThis); - static void PaServerInfoCallback(pa_context *c, const pa_server_info *i, - void *pThis); - static void PaStreamStateCallback(pa_stream *p, void *pThis); - void PaContextStateCallbackHandler(pa_context *c); - void PaSinkInfoCallbackHandler(const pa_sink_info *i, int eol); - void PaSourceInfoCallbackHandler(const pa_source_info *i, int eol); - void PaServerInfoCallbackHandler(const pa_server_info *i); - void PaStreamStateCallbackHandler(pa_stream *p); - - void EnableWriteCallback(); - void DisableWriteCallback(); - static void PaStreamWriteCallback(pa_stream *unused, size_t buffer_space, - void *pThis); - void PaStreamWriteCallbackHandler(size_t buffer_space); - static void PaStreamUnderflowCallback(pa_stream *unused, void *pThis); - void PaStreamUnderflowCallbackHandler(); - void EnableReadCallback(); - void DisableReadCallback(); - static void PaStreamReadCallback(pa_stream *unused1, size_t unused2, - void *pThis); - void PaStreamReadCallbackHandler(); - static void PaStreamOverflowCallback(pa_stream *unused, void *pThis); - void PaStreamOverflowCallbackHandler(); - WebRtc_Word32 LatencyUsecs(pa_stream *stream); - WebRtc_Word32 ReadRecordedData(const void* bufferData, size_t bufferSize); - WebRtc_Word32 ProcessRecordedData(WebRtc_Word8 *bufferData, - WebRtc_UWord32 bufferSizeInSamples, - WebRtc_UWord32 recDelay); - - WebRtc_Word32 CheckPulseAudioVersion(); - WebRtc_Word32 InitSamplingFrequency(); - WebRtc_Word32 GetDefaultDeviceInfo(bool recDevice, WebRtc_Word8* name, - WebRtc_UWord16& index); - WebRtc_Word32 InitPulseAudio(); - WebRtc_Word32 TerminatePulseAudio(); - - void PaLock(); - void PaUnLock(); - - static bool RecThreadFunc(void*); - static bool PlayThreadFunc(void*); - bool RecThreadProcess(); - bool PlayThreadProcess(); - -private: - AudioDeviceBuffer* _ptrAudioBuffer; - - CriticalSectionWrapper& _critSect; - EventWrapper& _timeEventRec; - EventWrapper& _timeEventPlay; - EventWrapper& _recStartEvent; - EventWrapper& _playStartEvent; - - ThreadWrapper* _ptrThreadPlay; - ThreadWrapper* _ptrThreadRec; - WebRtc_UWord32 _recThreadID; - WebRtc_UWord32 _playThreadID; - WebRtc_Word32 _id; - - AudioMixerManagerLinuxPulse _mixerManager; - - WebRtc_UWord16 _inputDeviceIndex; - WebRtc_UWord16 _outputDeviceIndex; - bool _inputDeviceIsSpecified; - bool _outputDeviceIsSpecified; - - WebRtc_UWord32 _samplingFreq; - WebRtc_UWord8 _recChannels; - WebRtc_UWord8 _playChannels; - - AudioDeviceModule::BufferType _playBufType; - -private: - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _startRec; - bool _stopRec; - bool _startPlay; - bool _stopPlay; - bool _AGC; - -private: - WebRtc_UWord16 _playBufDelayFixed; // fixed playback delay - - WebRtc_UWord32 _sndCardPlayDelay; - WebRtc_UWord32 _sndCardRecDelay; - - WebRtc_Word32 _writeErrors; - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - WebRtc_UWord16 _deviceIndex; - WebRtc_Word16 _numPlayDevices; - WebRtc_Word16 _numRecDevices; - WebRtc_Word8* _playDeviceName; - WebRtc_Word8* _recDeviceName; - WebRtc_Word8* _playDisplayDeviceName; - WebRtc_Word8* _recDisplayDeviceName; - WebRtc_Word8 _paServerVersion[32]; - - WebRtc_Word8* _playBuffer; - size_t _playbackBufferSize; - size_t _playbackBufferUnused; - size_t _tempBufferSpace; - WebRtc_Word8* _recBuffer; - size_t _recordBufferSize; - size_t _recordBufferUsed; - const void* _tempSampleData; - size_t _tempSampleDataSize; - WebRtc_Word32 _configuredLatencyPlay; - WebRtc_Word32 _configuredLatencyRec; - - // PulseAudio - WebRtc_UWord16 _paDeviceIndex; - bool _paStateChanged; - - pa_threaded_mainloop* _paMainloop; - pa_mainloop_api* _paMainloopApi; - pa_context* _paContext; - - pa_stream* _recStream; - pa_stream* _playStream; - WebRtc_UWord32 _recStreamFlags; - WebRtc_UWord32 _playStreamFlags; - pa_buffer_attr _playBufferAttr; - pa_buffer_attr _recBufferAttr; -}; - -} - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_DEVICE_LINUX_PULSE_H_ diff --git a/modules/audio_device/main/source/Linux/audio_device_utility_linux.cc b/modules/audio_device/main/source/Linux/audio_device_utility_linux.cc deleted file mode 100644 index 9c0b5e170..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_utility_linux.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_utility_linux.h" -#include "audio_device_config.h" // DEBUG_PRINT() -#include "critical_section_wrapper.h" -#include "trace.h" - -namespace webrtc -{ - -AudioDeviceUtilityLinux::AudioDeviceUtilityLinux(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _id(id), - _lastError(AudioDeviceModule::kAdmErrNone) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); -} - -AudioDeviceUtilityLinux::~AudioDeviceUtilityLinux() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - - // free stuff here... - } - - delete &_critSect; -} - -// ============================================================================ -// API -// ============================================================================ - - -WebRtc_Word32 AudioDeviceUtilityLinux::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - " OS info: %s", "Linux"); - - return 0; -} - - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Linux/audio_device_utility_linux.h b/modules/audio_device/main/source/Linux/audio_device_utility_linux.h deleted file mode 100644 index 8df7acc29..000000000 --- a/modules/audio_device/main/source/Linux/audio_device_utility_linux.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_LINUX_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_LINUX_H - -#include "audio_device_utility.h" -#include "audio_device.h" - -namespace webrtc -{ -class CriticalSectionWrapper; - -class AudioDeviceUtilityLinux: public AudioDeviceUtility -{ -public: - AudioDeviceUtilityLinux(const WebRtc_Word32 id); - ~AudioDeviceUtilityLinux(); - - virtual WebRtc_Word32 Init(); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - AudioDeviceModule::ErrorCode _lastError; -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_DEVICE_UTILITY_LINUX_H_ diff --git a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.cc b/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.cc deleted file mode 100644 index 31d1c74d3..000000000 --- a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.cc +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "audio_mixer_manager_linux_alsa.h" -#include "trace.h" - -extern webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable; - -// Accesses ALSA functions through our late-binding symbol table instead of -// directly. This way we don't have to link to libalsa, which means our binary -// will work on systems that don't have it. -#define LATE(sym) \ - LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym) - -namespace webrtc -{ - -AudioMixerManagerLinuxALSA::AudioMixerManagerLinuxALSA(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _id(id), - _inputMixerHandle(NULL), _outputMixerHandle(NULL), - _inputMixerElement(NULL), _outputMixerElement(NULL) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s constructed", __FUNCTION__); - - memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize); - memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize); -} - -AudioMixerManagerLinuxALSA::~AudioMixerManagerLinuxALSA() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destructed", __FUNCTION__); - - Close(); - - delete &_critSect; -} - -// ============================================================================ -// PUBLIC METHODS -// ============================================================================ - -WebRtc_Word32 AudioMixerManagerLinuxALSA::Close() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - CloseSpeaker(); - CloseMicrophone(); - - return 0; - -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::CloseSpeaker() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - int errVal = 0; - - if (_outputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing playout mixer"); - LATE(snd_mixer_free)(_outputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error freeing playout mixer: %s", - LATE(snd_strerror)(errVal)); - } - errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error detachinging playout mixer: %s", - LATE(snd_strerror)(errVal)); - } - errVal = LATE(snd_mixer_close)(_outputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error snd_mixer_close(handleMixer) errVal=%d", - errVal); - } - _outputMixerHandle = NULL; - _outputMixerElement = NULL; - } - memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::CloseMicrophone() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - int errVal = 0; - - if (_inputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer"); - - LATE(snd_mixer_free)(_inputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error freeing record mixer: %s", - LATE(snd_strerror)(errVal)); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer 2"); - - errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error detachinging record mixer: %s", - LATE(snd_strerror)(errVal)); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer 3"); - - errVal = LATE(snd_mixer_close)(_inputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error snd_mixer_close(handleMixer) errVal=%d", - errVal); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer 4"); - _inputMixerHandle = NULL; - _inputMixerElement = NULL; - } - memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::OpenSpeaker(char* deviceName) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::OpenSpeaker(name=%s)", deviceName); - - CriticalSectionScoped lock(_critSect); - - int errVal = 0; - - // Close any existing output mixer handle - // - if (_outputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing playout mixer"); - - LATE(snd_mixer_free)(_outputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error freeing playout mixer: %s", - LATE(snd_strerror)(errVal)); - } - errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error detachinging playout mixer: %s", - LATE(snd_strerror)(errVal)); - } - errVal = LATE(snd_mixer_close)(_outputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error snd_mixer_close(handleMixer) errVal=%d", - errVal); - } - } - _outputMixerHandle = NULL; - _outputMixerElement = NULL; - - errVal = LATE(snd_mixer_open)(&_outputMixerHandle, 0); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "snd_mixer_open(&_outputMixerHandle, 0) - error"); - return -1; - } - - char controlName[kAdmMaxDeviceNameSize] = { 0 }; - GetControlName(controlName, deviceName); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " snd_mixer_attach(_outputMixerHandle, %s)", controlName); - - errVal = LATE(snd_mixer_attach)(_outputMixerHandle, controlName); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_attach(_outputMixerHandle, %s) error: %s", - controlName, LATE(snd_strerror)(errVal)); - _outputMixerHandle = NULL; - return -1; - } - strcpy(_outputMixerStr, controlName); - - errVal = LATE(snd_mixer_selem_register)(_outputMixerHandle, NULL, NULL); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_selem_register(_outputMixerHandle," - " NULL, NULL), error: %s", - LATE(snd_strerror)(errVal)); - _outputMixerHandle = NULL; - return -1; - } - - // Load and find the proper mixer element - if (LoadSpeakerMixerElement() < 0) - { - return -1; - } - - if (_outputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " the output mixer device is now open (0x%x)", - _outputMixerHandle); - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::OpenMicrophone(char *deviceName) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::OpenMicrophone(name=%s)", - deviceName); - - CriticalSectionScoped lock(_critSect); - - int errVal = 0; - - // Close any existing input mixer handle - // - if (_inputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer"); - - LATE(snd_mixer_free)(_inputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error freeing record mixer: %s", - LATE(snd_strerror)(errVal)); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer"); - - errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error detachinging record mixer: %s", - LATE(snd_strerror)(errVal)); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer"); - - errVal = LATE(snd_mixer_close)(_inputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error snd_mixer_close(handleMixer) errVal=%d", - errVal); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Closing record mixer"); - } - _inputMixerHandle = NULL; - _inputMixerElement = NULL; - - errVal = LATE(snd_mixer_open)(&_inputMixerHandle, 0); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_open(&_inputMixerHandle, 0) - error"); - return -1; - } - - char controlName[kAdmMaxDeviceNameSize] = { 0 }; - GetControlName(controlName, deviceName); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " snd_mixer_attach(_inputMixerHandle, %s)", controlName); - - errVal = LATE(snd_mixer_attach)(_inputMixerHandle, controlName); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_attach(_inputMixerHandle, %s) error: %s", - controlName, LATE(snd_strerror)(errVal)); - - _inputMixerHandle = NULL; - return -1; - } - strcpy(_inputMixerStr, controlName); - - errVal = LATE(snd_mixer_selem_register)(_inputMixerHandle, NULL, NULL); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_selem_register(_inputMixerHandle," - " NULL, NULL), error: %s", - LATE(snd_strerror)(errVal)); - - _inputMixerHandle = NULL; - return -1; - } - // Load and find the proper mixer element - if (LoadMicMixerElement() < 0) - { - return -1; - } - - if (_inputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " the input mixer device is now open (0x%x)", - _inputMixerHandle); - } - - return 0; -} - -bool AudioMixerManagerLinuxALSA::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_outputMixerHandle != NULL); -} - -bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - return (_inputMixerHandle != NULL); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SetSpeakerVolume( - WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::SetSpeakerVolume(volume=%u)", - volume); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - int errVal = - LATE(snd_mixer_selem_set_playback_volume_all)(_outputMixerElement, - volume); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error changing master volume: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - return (0); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SpeakerVolume( - WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int vol(0); - - int - errVal = LATE(snd_mixer_selem_get_playback_volume)( - _outputMixerElement, - (snd_mixer_selem_channel_id_t) 0, - &vol); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting outputvolume: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxALSA::SpeakerVolume() => vol=%i", - vol); - - volume = static_cast (vol); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MaxSpeakerVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avilable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = - LATE(snd_mixer_selem_get_playback_volume_range)(_outputMixerElement, - &minVol, &maxVol); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout hardware volume range, min: %d, max: %d", - minVol, maxVol); - - if (maxVol <= minVol) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting get_playback_volume_range: %s", - LATE(snd_strerror)(errVal)); - } - - maxVolume = static_cast (maxVol); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MinSpeakerVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = - LATE(snd_mixer_selem_get_playback_volume_range)(_outputMixerElement, - &minVol, &maxVol); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout hardware volume range, min: %d, max: %d", - minVol, maxVol); - - if (maxVol <= minVol) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting get_playback_volume_range: %s", - LATE(snd_strerror)(errVal)); - } - - minVolume = static_cast (minVol); - - return 0; -} - -// TL: Have done testnig with these but they don't seem reliable and -// they were therefore not added -/* - // ---------------------------------------------------------------------------- - // SetMaxSpeakerVolume - // ---------------------------------------------------------------------------- - - WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMaxSpeakerVolume( - WebRtc_UWord32 maxVolume) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = snd_mixer_selem_get_playback_volume_range( - _outputMixerElement, &minVol, &maxVol); - if ((maxVol <= minVol) || (errVal != 0)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error getting playback volume range: %s", snd_strerror(errVal)); - } - - maxVol = maxVolume; - errVal = snd_mixer_selem_set_playback_volume_range( - _outputMixerElement, minVol, maxVol); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout hardware volume range, min: %d, max: %d", minVol, maxVol); - if (errVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting playback volume range: %s", snd_strerror(errVal)); - return -1; - } - - return 0; - } - - // ---------------------------------------------------------------------------- - // SetMinSpeakerVolume - // ---------------------------------------------------------------------------- - - WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMinSpeakerVolume( - WebRtc_UWord32 minVolume) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = snd_mixer_selem_get_playback_volume_range( - _outputMixerElement, &minVol, &maxVol); - if ((maxVol <= minVol) || (errVal != 0)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error getting playback volume range: %s", snd_strerror(errVal)); - } - - minVol = minVolume; - errVal = snd_mixer_selem_set_playback_volume_range( - _outputMixerElement, minVol, maxVol); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Playout hardware volume range, min: %d, max: %d", minVol, maxVol); - if (errVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting playback volume range: %s", snd_strerror(errVal)); - return -1; - } - - return 0; - } - */ - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer exists"); - return -1; - } - - // The step size is always 1 for ALSA - stepSize = 1; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SpeakerVolumeIsAvailable( - bool& available) -{ - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - available = LATE(snd_mixer_selem_has_playback_volume)(_outputMixerElement); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SpeakerMuteIsAvailable( - bool& available) -{ - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - available = LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::SetSpeakerMute(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - // Ensure that the selected speaker destination has a valid mute control. - bool available(false); - SpeakerMuteIsAvailable(available); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to mute the speaker"); - return -1; - } - - // Note value = 0 (off) means muted - int errVal = - LATE(snd_mixer_selem_set_playback_switch_all)(_outputMixerElement, - !enable); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting playback switch: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - return (0); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer exists"); - return -1; - } - - // Ensure that the selected speaker destination has a valid mute control. - bool available = - LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to mute the speaker"); - return -1; - } - - int value(false); - - // Retrieve one boolean control value for a specified mute-control - // - int - errVal = LATE(snd_mixer_selem_get_playback_switch)( - _outputMixerElement, - (snd_mixer_selem_channel_id_t) 0, - &value); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting playback switch: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - // Note value = 0 (off) means muted - enabled = (bool) !value; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneMuteIsAvailable( - bool& available) -{ - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::SetMicrophoneMute(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - // Ensure that the selected microphone destination has a valid mute control. - bool available(false); - MicrophoneMuteIsAvailable(available); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to mute the microphone"); - return -1; - } - - // Note value = 0 (off) means muted - int errVal = - LATE(snd_mixer_selem_set_capture_switch_all)(_inputMixerElement, - !enable); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting capture switch: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - return (0); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected microphone destination has a valid mute control. - bool available = - LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to mute the microphone"); - return -1; - } - - int value(false); - - // Retrieve one boolean control value for a specified mute-control - // - int - errVal = LATE(snd_mixer_selem_get_capture_switch)( - _inputMixerElement, - (snd_mixer_selem_channel_id_t) 0, - &value); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting capture switch: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - // Note value = 0 (off) means muted - enabled = (bool) !value; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneBoostIsAvailable( - bool& available) -{ - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer exists"); - return -1; - } - - // Microphone boost cannot be enabled through ALSA Simple Mixer Interface - available = false; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::SetMicrophoneBoost(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected microphone destination has a valid mute control. - bool available(false); - MicrophoneMuteIsAvailable(available); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to enable microphone boost"); - return -1; - } - - // It is assumed that the call above fails! - - return (0); -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer exists"); - return -1; - } - - // Microphone boost cannot be enabled on this platform! - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneVolumeIsAvailable( - bool& available) -{ - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - available = LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMicrophoneVolume( - WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxALSA::SetMicrophoneVolume(volume=%u)", - volume); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - int - errVal = - LATE(snd_mixer_selem_set_capture_volume_all)(_inputMixerElement, - volume); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error changing microphone volume: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - - return (0); -} - -// TL: Have done testnig with these but they don't seem reliable and -// they were therefore not added -/* - // ---------------------------------------------------------------------------- - // SetMaxMicrophoneVolume - // ---------------------------------------------------------------------------- - - WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMaxMicrophoneVolume( - WebRtc_UWord32 maxVolume) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = snd_mixer_selem_get_capture_volume_range(_inputMixerElement, - &minVol, &maxVol); - if ((maxVol <= minVol) || (errVal != 0)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error getting capture volume range: %s", snd_strerror(errVal)); - } - - maxVol = (long int)maxVolume; - printf("min %d max %d", minVol, maxVol); - errVal = snd_mixer_selem_set_capture_volume_range(_inputMixerElement, minVol, maxVol); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Capture hardware volume range, min: %d, max: %d", minVol, maxVol); - if (errVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting capture volume range: %s", snd_strerror(errVal)); - return -1; - } - - return 0; - } - - // ---------------------------------------------------------------------------- - // SetMinMicrophoneVolume - // ---------------------------------------------------------------------------- - - WebRtc_Word32 AudioMixerManagerLinuxALSA::SetMinMicrophoneVolume( - WebRtc_UWord32 minVolume) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable output mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = snd_mixer_selem_get_capture_volume_range( - _inputMixerElement, &minVol, &maxVol); - if (maxVol <= minVol) - { - //maxVol = 255; - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Error getting capture volume range: %s", snd_strerror(errVal)); - } - - printf("min %d max %d", minVol, maxVol); - minVol = (long int)minVolume; - errVal = snd_mixer_selem_set_capture_volume_range( - _inputMixerElement, minVol, maxVol); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Capture hardware volume range, min: %d, max: %d", minVol, maxVol); - if (errVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error setting capture volume range: %s", snd_strerror(errVal)); - return -1; - } - - return 0; - } - */ - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneVolume( - WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - long int vol(0); - - int - errVal = - LATE(snd_mixer_selem_get_capture_volume)( - _inputMixerElement, - (snd_mixer_selem_channel_id_t) 0, - &vol); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting inputvolume: %s", - LATE(snd_strerror)(errVal)); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxALSA::MicrophoneVolume() => vol=%i", - vol); - - volume = static_cast (vol); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MaxMicrophoneVolume( - WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - // check if we have mic volume at all - if (!LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " No microphone volume available"); - return -1; - } - - int errVal = - LATE(snd_mixer_selem_get_capture_volume_range)(_inputMixerElement, - &minVol, &maxVol); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Microphone hardware volume range, min: %d, max: %d", - minVol, maxVol); - if (maxVol <= minVol) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting microphone volume range: %s", - LATE(snd_strerror)(errVal)); - } - - maxVolume = static_cast (maxVol); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MinMicrophoneVolume( - WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerElement == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer element exists"); - return -1; - } - - long int minVol(0); - long int maxVol(0); - - int errVal = - LATE(snd_mixer_selem_get_capture_volume_range)(_inputMixerElement, - &minVol, &maxVol); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Microphone hardware volume range, min: %d, max: %d", - minVol, maxVol); - if (maxVol <= minVol) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error getting microphone volume range: %s", - LATE(snd_strerror)(errVal)); - } - - minVolume = static_cast (minVol); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " no avaliable input mixer exists"); - return -1; - } - - // The step size is always 1 for ALSA - stepSize = 1; - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -WebRtc_Word32 AudioMixerManagerLinuxALSA::LoadMicMixerElement() const -{ - int errVal = LATE(snd_mixer_load)(_inputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "snd_mixer_load(_inputMixerHandle), error: %s", - LATE(snd_strerror)(errVal)); - _inputMixerHandle = NULL; - return -1; - } - - snd_mixer_elem_t *elem = NULL; - snd_mixer_elem_t *micElem = NULL; - unsigned mixerIdx = 0; - const char *selemName = NULL; - - // Find and store handles to the right mixer elements - for (elem = LATE(snd_mixer_first_elem)(_inputMixerHandle); elem; elem - = LATE(snd_mixer_elem_next)(elem), mixerIdx++) - { - if (LATE(snd_mixer_selem_is_active)(elem)) - { - selemName = LATE(snd_mixer_selem_get_name)(elem); - if (strcmp(selemName, "Capture") == 0) // "Capture", "Mic" - { - _inputMixerElement = elem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " Capture element set"); - } else if (strcmp(selemName, "Mic") == 0) - { - micElem = elem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " Mic element found"); - } - } - - if (_inputMixerElement) - { - // Use the first Capture element that is found - // The second one may not work - break; - } - } - - if (_inputMixerElement == NULL) - { - // We didn't find a Capture handle, use Mic. - if (micElem != NULL) - { - _inputMixerElement = micElem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Using Mic as capture volume."); - } else - { - _inputMixerElement = NULL; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Could not find capture volume on the mixer."); - - return -1; - } - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxALSA::LoadSpeakerMixerElement() const -{ - int errVal = LATE(snd_mixer_load)(_outputMixerHandle); - if (errVal < 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " snd_mixer_load(_outputMixerHandle), error: %s", - LATE(snd_strerror)(errVal)); - _outputMixerHandle = NULL; - return -1; - } - - snd_mixer_elem_t *elem = NULL; - snd_mixer_elem_t *masterElem = NULL; - snd_mixer_elem_t *speakerElem = NULL; - unsigned mixerIdx = 0; - const char *selemName = NULL; - - // Find and store handles to the right mixer elements - for (elem = LATE(snd_mixer_first_elem)(_outputMixerHandle); elem; elem - = LATE(snd_mixer_elem_next)(elem), mixerIdx++) - { - if (LATE(snd_mixer_selem_is_active)(elem)) - { - selemName = LATE(snd_mixer_selem_get_name)(elem); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "snd_mixer_selem_get_name %d: %s =%x", mixerIdx, - selemName, elem); - - // "Master", "PCM", "Wave", "Master Mono", "PC Speaker", "PCM", "Wave" - if (strcmp(selemName, "PCM") == 0) - { - _outputMixerElement = elem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " PCM element set"); - } else if (strcmp(selemName, "Master") == 0) - { - masterElem = elem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " Master element found"); - } else if (strcmp(selemName, "Speaker") == 0) - { - speakerElem = elem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, " Speaker element found"); - } - } - - if (_outputMixerElement) - { - // We have found the element we want - break; - } - } - - // If we didn't find a PCM Handle, use Master or Speaker - if (_outputMixerElement == NULL) - { - if (masterElem != NULL) - { - _outputMixerElement = masterElem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Using Master as output volume."); - } else if (speakerElem != NULL) - { - _outputMixerElement = speakerElem; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Using Speaker as output volume."); - } else - { - _outputMixerElement = NULL; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Could not find output volume in the mixer."); - return -1; - } - } - - return 0; -} - -void AudioMixerManagerLinuxALSA::GetControlName(char* controlName, - char* deviceName) const -{ - // Example - // deviceName: "front:CARD=Intel,DEV=0" - // controlName: "hw:CARD=Intel" - char* pos1 = strchr(deviceName, ':'); - char* pos2 = strchr(deviceName, ','); - if (!pos2) - { - // Can also be default:CARD=Intel - pos2 = &deviceName[strlen(deviceName)]; - } - if (pos1 && pos2) - { - strcpy(controlName, "hw"); - int nChar = (int) (pos2 - pos1); - strncpy(&controlName[2], pos1, nChar); - controlName[2 + nChar] = '\0'; - } else - { - strcpy(controlName, deviceName); - } - -} - -} diff --git a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.h b/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.h deleted file mode 100644 index 1e62d6762..000000000 --- a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_alsa.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_LINUX_ALSA_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_LINUX_ALSA_H - -#include "typedefs.h" -#include "audio_device.h" -#include "critical_section_wrapper.h" -#include "alsasymboltable.h" - -#include - -namespace webrtc -{ - -class AudioMixerManagerLinuxALSA -{ -public: - WebRtc_Word32 OpenSpeaker(char* deviceName); - WebRtc_Word32 OpenMicrophone(char* deviceName); - WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - WebRtc_Word32 SetSpeakerMute(bool enable); - WebRtc_Word32 SpeakerMute(bool& enabled) const; - WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneMute(bool enable); - WebRtc_Word32 MicrophoneMute(bool& enabled) const; - WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneBoost(bool enable); - WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 Close(); - WebRtc_Word32 CloseSpeaker(); - WebRtc_Word32 CloseMicrophone(); - bool SpeakerIsInitialized() const; - bool MicrophoneIsInitialized() const; - -public: - AudioMixerManagerLinuxALSA(const WebRtc_Word32 id); - ~AudioMixerManagerLinuxALSA(); - -private: - WebRtc_Word32 LoadMicMixerElement() const; - WebRtc_Word32 LoadSpeakerMixerElement() const; - void GetControlName(char *controlName, char* deviceName) const; - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - mutable snd_mixer_t* _outputMixerHandle; - char _outputMixerStr[kAdmMaxDeviceNameSize]; - mutable snd_mixer_t* _inputMixerHandle; - char _inputMixerStr[kAdmMaxDeviceNameSize]; - mutable snd_mixer_elem_t* _outputMixerElement; - mutable snd_mixer_elem_t* _inputMixerElement; -}; - -} - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_MIXER_MANAGER_LINUX_ALSA_H_ diff --git a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.cc b/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.cc deleted file mode 100644 index 17654c0fd..000000000 --- a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.cc +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "audio_mixer_manager_linux_pulse.h" -#include "trace.h" - -extern webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; - -// Accesses Pulse functions through our late-binding symbol table instead of -// directly. This way we don't have to link to libpulse, which means our binary -// will work on systems that don't have it. -#define LATE(sym) \ - LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) - -namespace webrtc -{ - -AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _paOutputDeviceIndex(-1), - _paInputDeviceIndex(-1), - _paPlayStream(NULL), - _paRecStream(NULL), - _paMainloop(NULL), - _paContext(NULL), - _paVolume(0), - _paMute(0), - _paVolSteps(0), - _paSpeakerMute(false), - _paSpeakerVolume(0), - _paChannels(0), - _paObjectsSet(false), - _callbackValues(false) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s constructed", __FUNCTION__); -} - -AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destructed", __FUNCTION__); - - Close(); - - delete &_critSect; -} - -// ============================================================================ -// PUBLIC METHODS -// ============================================================================ - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetPulseAudioObjects( - pa_threaded_mainloop* mainloop, - pa_context* context) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!mainloop || !context) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " could not set PulseAudio objects for mixer"); - return -1; - } - - _paMainloop = mainloop; - _paContext = context; - _paObjectsSet = true; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " the PulseAudio objects for the mixer has been set"); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::Close() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - CloseSpeaker(); - CloseMicrophone(); - - _paMainloop = NULL; - _paContext = NULL; - _paObjectsSet = false; - - return 0; - -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::CloseSpeaker() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - // Reset the index to -1 - _paOutputDeviceIndex = -1; - _paPlayStream = NULL; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::CloseMicrophone() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - // Reset the index to -1 - _paInputDeviceIndex = -1; - _paRecStream = NULL; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)"); - - CriticalSectionScoped lock(_critSect); - _paPlayStream = playStream; - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetRecStream(recStream)"); - - CriticalSectionScoped lock(_critSect); - _paRecStream = recStream; - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::OpenSpeaker( - WebRtc_UWord16 deviceIndex) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex=%d)", - deviceIndex); - - CriticalSectionScoped lock(_critSect); - - // No point in opening the speaker - // if PA objects have not been set - if (!_paObjectsSet) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " PulseAudio objects has not been set"); - return -1; - } - - // Set the index for the PulseAudio - // output device to control - _paOutputDeviceIndex = deviceIndex; - - // Init the speaker volume to the normal volume - _paSpeakerVolume = PA_VOLUME_NORM; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " the output mixer device is now open"); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::OpenMicrophone( - WebRtc_UWord16 deviceIndex) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex=%d)", - deviceIndex); - - CriticalSectionScoped lock(_critSect); - - // No point in opening the microphone - // if PA objects have not been set - if (!_paObjectsSet) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " PulseAudio objects have not been set"); - return -1; - } - - // Set the index for the PulseAudio - // input device to control - _paInputDeviceIndex = deviceIndex; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " the input mixer device is now open"); - - return 0; -} - -bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - return (_paOutputDeviceIndex != -1); -} - -bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - return (_paInputDeviceIndex != -1); -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetSpeakerVolume( - WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume=%u)", - volume); - - CriticalSectionScoped lock(_critSect); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - bool setFailed(false); - - if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) - != PA_STREAM_UNCONNECTED)) - { - // We can only really set the volume if we have a connected stream - PaLock(); - - // Get the number of channels from the sample specification - const pa_sample_spec *spec = - LATE(pa_stream_get_sample_spec)(_paPlayStream); - if (!spec) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " could not get sample specification"); - PaUnLock(); - return -1; - } - - // Set the same volume for all channels - pa_cvolume cVolumes; - LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); - - pa_operation* paOperation = NULL; - paOperation = LATE(pa_context_set_sink_input_volume)( - _paContext, - LATE(pa_stream_get_index)(_paPlayStream), - &cVolumes, - PaSetVolumeCallback, NULL); - if (!paOperation) - { - setFailed = true; - } - - // Don't need to wait for the completion - LATE(pa_operation_unref)(paOperation); - - PaUnLock(); - } else - { - // We have not created a stream or it's not connected to the sink - // Save the volume to be set at connection - _paSpeakerVolume = volume; - } - - if (setFailed) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " could not set speaker volume, error%d", - LATE(pa_context_errno)(_paContext)); - - return -1; - } - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) - != PA_STREAM_UNCONNECTED)) - { - // We can only get the volume if we have a connected stream - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - PaLock(); - - // Get info for this stream (sink input) - paOperation = LATE(pa_context_get_sink_input_info)( - _paContext, - LATE(pa_stream_get_index)(_paPlayStream), - PaSinkInputInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting output volume: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - volume = static_cast (_paVolume); - ResetCallbackVariables(); - } else - { - volume = _paSpeakerVolume; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::SpeakerVolume() => vol=%i", - volume); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - // PA_VOLUME_NORM corresponds to 100% (0db) - // but PA allows up to 150 db amplification - maxVolume = static_cast (PA_VOLUME_NORM); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - minVolume = static_cast (PA_VOLUME_MUTED); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - // The sink input (stream) will always have step size = 1 - // There are PA_VOLUME_NORM+1 steps - stepSize = 1; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize() => " - "size=%i, stepSize"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available) -{ - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - // Always available in Pulse Audio - available = true; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available) -{ - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - // Always available in Pulse Audio - available = true; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - bool setFailed(false); - - if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) - != PA_STREAM_UNCONNECTED)) - { - // We can only really mute if we have a connected stream - PaLock(); - - pa_operation* paOperation = NULL; - paOperation = LATE(pa_context_set_sink_input_mute)( - _paContext, - LATE(pa_stream_get_index)(_paPlayStream), - (int) enable, - PaSetVolumeCallback, - NULL); - if (!paOperation) - { - setFailed = true; - } - - // Don't need to wait for the completion - LATE(pa_operation_unref)(paOperation); - - PaUnLock(); - } else - { - // We have not created a stream or it's not connected to the sink - // Save the mute status to be set at connection - _paSpeakerMute = enable; - } - - if (setFailed) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " could not mute speaker, error%d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) - != PA_STREAM_UNCONNECTED)) - { - // We can only get the mute status if we have a connected stream - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - PaLock(); - - // Get info for this stream (sink input) - paOperation = LATE(pa_context_get_sink_input_info)( - _paContext, - LATE(pa_stream_get_index)(_paPlayStream), - PaSinkInputInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting output volume: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - enabled = static_cast (_paMute); - ResetCallbackVariables(); - } else - { - enabled = _paSpeakerMute; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::SpeakerMute() => " - "enabled=%i, enabled"); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) -{ - if (_paOutputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " output device index has not been set"); - return -1; - } - - uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream); - } - - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get info for this sink - // We want to know if the actual device can play out in stereo - paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext, - deviceIndex, - PaSinkInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting number of output channels: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - available = static_cast (_paChannels == 2); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable() " - "=> available=%i, available"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available) -{ - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get info for this source - // We want to know if the actual device can record in stereo - paOperation = LATE(pa_context_get_source_info_by_index)( - _paContext, deviceIndex, - PaSourceInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting number of input channels: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - available = static_cast (_paChannels == 2); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()" - " => available=%i, available"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable( - bool& available) -{ - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Always available in Pulse Audio - available = true; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - bool setFailed(false); - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - // Set mute switch for the source - paOperation = LATE(pa_context_set_source_mute_by_index)( - _paContext, deviceIndex, - enable, - PaSetVolumeCallback, NULL); - - if (!paOperation) - { - setFailed = true; - } - - // Don't need to wait for this to complete. - LATE(pa_operation_unref)(paOperation); - - PaUnLock(); - - // Reset variables altered by callback - ResetCallbackVariables(); - - if (setFailed) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " could not mute microphone, error%d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get info for this source - paOperation - = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, - PaSourceInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting input mute status: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - enabled = static_cast (_paMute); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::MicrophoneMute() =>" - " enabled=%i, enabled"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MicrophoneBoostIsAvailable(bool& available) -{ - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Always unavailable in Pulse Audio - // Could make it possible to use PA_VOLUME_MAX - // but that gives bad audio with some sound cards - available = false; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetMicrophoneBoost(enable=%u)", - enable); - - CriticalSectionScoped lock(_critSect); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Ensure that the selected microphone destination has a valid boost control - bool available(false); - MicrophoneBoostIsAvailable(available); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to enable microphone boost"); - return -1; - } - - // It is assumed that the call above fails! - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Microphone boost cannot be enabled on this platform! - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable( - bool& available) -{ - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Always available in Pulse Audio - available = true; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=%u)", - volume); - - CriticalSectionScoped lock(_critSect); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // Unlike output streams, input streams have no concept of a stream volume, - // only a device volume. So we have to change the volume of the device - // itself. - - // The device may have a different number of channels than the stream and - // their mapping may be different, so we don't want to use the channel count - // from our sample spec. We could use PA_CHANNELS_MAX to cover our bases, - // and the server allows that even if the device's channel count is lower, - // but some buggy PA clients don't like that (the pavucontrol on Hardy dies - // in an assert if the channel count is different). So instead we look up - // the actual number of channels that the device has. - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - bool setFailed(false); - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get the number of channels for this source - paOperation - = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, - PaSourceInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting input channels: %d", - LATE(pa_context_errno)(_paContext)); - PaUnLock(); - return -1; - } - - WebRtc_UWord8 channels = _paChannels; - ResetCallbackVariables(); - - pa_cvolume cVolumes; - LATE(pa_cvolume_set)(&cVolumes, channels, volume); - - // Set the volume for the source - paOperation - = LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex, - &cVolumes, - PaSetVolumeCallback, NULL); - - if (!paOperation) - { - setFailed = true; - } - - // Don't need to wait for this to complete. - LATE(pa_operation_unref)(paOperation); - - PaUnLock(); - - // Reset variables altered by callback - ResetCallbackVariables(); - - if (setFailed) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " could not set microphone volume, error%d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get info for this source - paOperation - = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, - PaSourceInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting input volume: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - volume = static_cast (_paVolume); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=%i, volume"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - // PA_VOLUME_NORM corresponds to 100% (0db) - // PA allows up to 150 db amplification (PA_VOLUME_MAX) - // but that doesn't work well for all sound cards - maxVolume = static_cast (PA_VOLUME_NORM); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerLinuxPulse::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - minVolume = static_cast (PA_VOLUME_MUTED); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_paInputDeviceIndex == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " input device index has not been set"); - return -1; - } - - uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; - - PaLock(); - - // Get the actual stream device index if we have a connected stream - // The device used by the stream can be changed - // during the call - if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) - != PA_STREAM_UNCONNECTED)) - { - deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); - } - - pa_operation* paOperation = NULL; - ResetCallbackVariables(); - - // Get info for this source - paOperation - = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, - PaSourceInfoCallback, - (void*) this); - - WaitForOperationCompletion(paOperation); - - PaUnLock(); - - if (!_callbackValues) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Error getting step size: %d", - LATE(pa_context_errno)(_paContext)); - return -1; - } - - stepSize = static_cast ((PA_VOLUME_NORM + 1) / _paVolSteps); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize()" - " => size=%i, stepSize"); - - // Reset members modified by callback - ResetCallbackVariables(); - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, - const pa_sink_info *i, - int eol, void *pThis) -{ - static_cast (pThis)-> PaSinkInfoCallbackHandler( - i, eol); -} - -void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback( - pa_context */*c*/, - const pa_sink_input_info *i, - int eol, void *pThis) -{ - static_cast (pThis)-> - PaSinkInputInfoCallbackHandler(i, eol); -} - - -void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, - const pa_source_info *i, - int eol, void *pThis) -{ - static_cast (pThis)-> - PaSourceInfoCallbackHandler(i, eol); -} - -void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c, - int success, void */*pThis*/) -{ - if (!success) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - " failed to set volume"); - } -} - -void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler( - const pa_sink_info *i, - int eol) -{ - if (eol) - { - // Signal that we are done - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - return; - } - - _callbackValues = true; - _paChannels = i->channel_map.channels; // Get number of channels - pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. - for (int j = 0; j < _paChannels; ++j) - { - if (paVolume < i->volume.values[j]) - { - paVolume = i->volume.values[j]; - } - } - _paVolume = paVolume; // get the max volume for any channel - _paMute = i->mute; // get mute status - - // supported since PA 0.9.15 - //_paVolSteps = i->n_volume_steps; // get the number of volume steps - // default value is PA_VOLUME_NORM+1 - _paVolSteps = PA_VOLUME_NORM + 1; -} - -void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler( - const pa_sink_input_info *i, - int eol) -{ - if (eol) - { - // Signal that we are done - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - return; - } - - _callbackValues = true; - _paChannels = i->channel_map.channels; // Get number of channels - pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. - for (int j = 0; j < _paChannels; ++j) - { - if (paVolume < i->volume.values[j]) - { - paVolume = i->volume.values[j]; - } - } - _paVolume = paVolume; // Get the max volume for any channel - _paMute = i->mute; // Get mute status -} - -void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler( - const pa_source_info *i, - int eol) -{ - if (eol) - { - // Signal that we are done - LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); - return; - } - - _callbackValues = true; - _paChannels = i->channel_map.channels; // Get number of channels - pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. - for (int j = 0; j < _paChannels; ++j) - { - if (paVolume < i->volume.values[j]) - { - paVolume = i->volume.values[j]; - } - } - _paVolume = paVolume; // Get the max volume for any channel - _paMute = i->mute; // Get mute status - - // supported since PA 0.9.15 - //_paVolSteps = i->n_volume_steps; // Get the number of volume steps - // default value is PA_VOLUME_NORM+1 - _paVolSteps = PA_VOLUME_NORM + 1; -} - -void AudioMixerManagerLinuxPulse::ResetCallbackVariables() const -{ - _paVolume = 0; - _paMute = 0; - _paVolSteps = 0; - _paChannels = 0; - _callbackValues = false; -} - -void AudioMixerManagerLinuxPulse::WaitForOperationCompletion( - pa_operation* paOperation) const -{ - while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) - { - LATE(pa_threaded_mainloop_wait)(_paMainloop); - } - - LATE(pa_operation_unref)(paOperation); -} - -void AudioMixerManagerLinuxPulse::PaLock() const -{ - LATE(pa_threaded_mainloop_lock)(_paMainloop); -} - -void AudioMixerManagerLinuxPulse::PaUnLock() const -{ - LATE(pa_threaded_mainloop_unlock)(_paMainloop); -} - -} diff --git a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.h b/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.h deleted file mode 100644 index 13096d7e2..000000000 --- a/modules/audio_device/main/source/Linux/audio_mixer_manager_linux_pulse.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_LINUX_PULSE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_LINUX_PULSE_H - -#include "typedefs.h" -#include "audio_device.h" -#include "critical_section_wrapper.h" -#include "pulseaudiosymboltable.h" - -#include -#include - -#ifndef UINT32_MAX -#define UINT32_MAX ((uint32_t)-1) -#endif - -namespace webrtc -{ - -class AudioMixerManagerLinuxPulse -{ -public: - WebRtc_Word32 SetPlayStream(pa_stream* playStream); - WebRtc_Word32 SetRecStream(pa_stream* recStream); - WebRtc_Word32 OpenSpeaker(WebRtc_UWord16 deviceIndex); - WebRtc_Word32 OpenMicrophone(WebRtc_UWord16 deviceIndex); - WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - WebRtc_Word32 SetSpeakerMute(bool enable); - WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - WebRtc_Word32 SpeakerMute(bool& enabled) const; - WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneMute(bool enable); - WebRtc_Word32 MicrophoneMute(bool& enabled) const; - WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneBoost(bool enable); - WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 SetPulseAudioObjects(pa_threaded_mainloop* mainloop, - pa_context* context); - WebRtc_Word32 Close(); - WebRtc_Word32 CloseSpeaker(); - WebRtc_Word32 CloseMicrophone(); - bool SpeakerIsInitialized() const; - bool MicrophoneIsInitialized() const; - -public: - AudioMixerManagerLinuxPulse(const WebRtc_Word32 id); - ~AudioMixerManagerLinuxPulse(); - -private: - static void PaSinkInfoCallback(pa_context *c, const pa_sink_info *i, - int eol, void *pThis); - static void PaSinkInputInfoCallback(pa_context *c, - const pa_sink_input_info *i, int eol, - void *pThis); - static void PaSourceInfoCallback(pa_context *c, const pa_source_info *i, - int eol, void *pThis); - static void - PaSetVolumeCallback(pa_context * /*c*/, int success, void */*pThis*/); - void PaSinkInfoCallbackHandler(const pa_sink_info *i, int eol); - void PaSinkInputInfoCallbackHandler(const pa_sink_input_info *i, int eol); - void PaSourceInfoCallbackHandler(const pa_source_info *i, int eol); - - void ResetCallbackVariables() const; - void WaitForOperationCompletion(pa_operation* paOperation) const; - void PaLock() const; - void PaUnLock() const; - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - WebRtc_Word16 _paOutputDeviceIndex; - WebRtc_Word16 _paInputDeviceIndex; - - pa_stream* _paPlayStream; - pa_stream* _paRecStream; - - pa_threaded_mainloop* _paMainloop; - pa_context* _paContext; - - mutable WebRtc_UWord32 _paVolume; - mutable WebRtc_UWord32 _paMute; - mutable WebRtc_UWord32 _paVolSteps; - bool _paSpeakerMute; - mutable WebRtc_UWord32 _paSpeakerVolume; - mutable WebRtc_UWord8 _paChannels; - bool _paObjectsSet; - mutable bool _callbackValues; - - WebRtc_UWord8 _micVolChannels; - WebRtc_UWord8 _spkVolChannels; -}; - -} - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_MIXER_MANAGER_LINUX_PULSE_H_ diff --git a/modules/audio_device/main/source/Linux/latebindingsymboltable.cc b/modules/audio_device/main/source/Linux/latebindingsymboltable.cc deleted file mode 100644 index 8035f8a22..000000000 --- a/modules/audio_device/main/source/Linux/latebindingsymboltable.cc +++ /dev/null @@ -1,116 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "latebindingsymboltable.h" - -#ifdef WEBRTC_LINUX -#include -#endif - -// TODO(grunell): Either put inside webrtc namespace or use webrtc:: instead. -using namespace webrtc; - -namespace webrtc_adm_linux { - -inline static const char *GetDllError() { -#ifdef WEBRTC_LINUX - char *err = dlerror(); - if (err) { - return err; - } else { - return "No error"; - } -#else -#error Not implemented -#endif -} - -DllHandle InternalLoadDll(const char dll_name[]) { -#ifdef WEBRTC_LINUX - DllHandle handle = dlopen(dll_name, RTLD_NOW); -#else -#error Not implemented -#endif - if (handle == kInvalidDllHandle) { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, - "Can't load %s : %d", dll_name, GetDllError()); - } - return handle; -} - -void InternalUnloadDll(DllHandle handle) { -#ifdef WEBRTC_LINUX - if (dlclose(handle) != 0) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "%d", GetDllError()); - } -#else -#error Not implemented -#endif -} - -static bool LoadSymbol(DllHandle handle, - const char *symbol_name, - void **symbol) { -#ifdef WEBRTC_LINUX - *symbol = dlsym(handle, symbol_name); - char *err = dlerror(); - if (err) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Error loading symbol %s : %d", symbol_name, err); - return false; - } else if (!*symbol) { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Symbol %s is NULL", symbol_name); - return false; - } - return true; -#else -#error Not implemented -#endif -} - -// This routine MUST assign SOME value for every symbol, even if that value is -// NULL, or else some symbols may be left with uninitialized data that the -// caller may later interpret as a valid address. -bool InternalLoadSymbols(DllHandle handle, - int num_symbols, - const char *const symbol_names[], - void *symbols[]) { -#ifdef WEBRTC_LINUX - // Clear any old errors. - dlerror(); -#endif - for (int i = 0; i < num_symbols; ++i) { - if (!LoadSymbol(handle, symbol_names[i], &symbols[i])) { - return false; - } - } - return true; -} - -} // namespace webrtc_adm_linux diff --git a/modules/audio_device/main/source/Linux/latebindingsymboltable.h b/modules/audio_device/main/source/Linux/latebindingsymboltable.h deleted file mode 100644 index fbc581d7c..000000000 --- a/modules/audio_device/main/source/Linux/latebindingsymboltable.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_H -#define WEBRTC_AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_H - -#include -#include // for NULL -#include - -#include "trace.h" - -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -// This file provides macros for creating "symbol table" classes to simplify the -// dynamic loading of symbols from DLLs. Currently the implementation only -// supports Linux and pure C symbols. -// See talk/sound/pulseaudiosymboltable.(h|cc) for an example. - -namespace webrtc_adm_linux { - -#ifdef WEBRTC_LINUX -typedef void *DllHandle; - -const DllHandle kInvalidDllHandle = NULL; -#else -#error Not implemented -#endif - -// These are helpers for use only by the class below. -DllHandle InternalLoadDll(const char dll_name[]); - -void InternalUnloadDll(DllHandle handle); - -bool InternalLoadSymbols(DllHandle handle, - int num_symbols, - const char *const symbol_names[], - void *symbols[]); - -template -class LateBindingSymbolTable { - public: - LateBindingSymbolTable() - : handle_(kInvalidDllHandle), - undefined_symbols_(false) { - memset(symbols_, 0, sizeof(symbols_)); - } - - ~LateBindingSymbolTable() { - Unload(); - } - - static int NumSymbols() { - return SYMBOL_TABLE_SIZE; - } - - // We do not use this, but we offer it for theoretical convenience. - static const char *GetSymbolName(int index) { - assert(index < NumSymbols()); - return kSymbolNames[index]; - } - - bool IsLoaded() const { - return handle_ != kInvalidDllHandle; - } - - // Loads the DLL and the symbol table. Returns true iff the DLL and symbol - // table loaded successfully. - bool Load() { - if (IsLoaded()) { - return true; - } - if (undefined_symbols_) { - // We do not attempt to load again because repeated attempts are not - // likely to succeed and DLL loading is costly. - //WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - // "We know there are undefined symbols"); - return false; - } - handle_ = InternalLoadDll(kDllName); - if (!IsLoaded()) { - return false; - } - if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) { - undefined_symbols_ = true; - Unload(); - return false; - } - return true; - } - - void Unload() { - if (!IsLoaded()) { - return; - } - InternalUnloadDll(handle_); - handle_ = kInvalidDllHandle; - memset(symbols_, 0, sizeof(symbols_)); - } - - // Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below - // instead of this. - void *GetSymbol(int index) const { - assert(IsLoaded()); - assert(index < NumSymbols()); - return symbols_[index]; - } - - private: - DllHandle handle_; - bool undefined_symbols_; - void *symbols_[SYMBOL_TABLE_SIZE]; - - DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable); -}; - -// This macro must be invoked in a header to declare a symbol table class. -#define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) \ -enum { - -// This macro must be invoked in the header declaration once for each symbol -// (recommended to use an X-Macro to avoid duplication). -// This macro defines an enum with names built from the symbols, which -// essentially creates a hash table in the compiler from symbol names to their -// indices in the symbol table class. -#define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \ - ClassName##_SYMBOL_TABLE_INDEX_##sym, - -// This macro completes the header declaration. -#define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName) \ - ClassName##_SYMBOL_TABLE_SIZE \ -}; \ -\ -extern const char ClassName##_kDllName[]; \ -extern const char *const \ - ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \ -\ -typedef ::webrtc_adm_linux::LateBindingSymbolTable \ - ClassName; - -// This macro must be invoked in a .cc file to define a previously-declared -// symbol table class. -#define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \ -const char ClassName##_kDllName[] = dllName; \ -const char *const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = { - -// This macro must be invoked in the .cc definition once for each symbol -// (recommended to use an X-Macro to avoid duplication). -// This would have to use the mangled name if we were to ever support C++ -// symbols. -#define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) \ - #sym, - -#define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \ -}; - -// Index of a given symbol in the given symbol table class. -#define LATESYM_INDEXOF(ClassName, sym) \ - (ClassName##_SYMBOL_TABLE_INDEX_##sym) - -// Returns a reference to the given late-binded symbol, with the correct type. -#define LATESYM_GET(ClassName, inst, sym) \ - (*reinterpret_cast( \ - (inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym)))) - -} // namespace webrtc_adm_linux - -#endif // WEBRTC_ADM_LATEBINDINGSYMBOLTABLE_H diff --git a/modules/audio_device/main/source/Linux/pulseaudiosymboltable.cc b/modules/audio_device/main/source/Linux/pulseaudiosymboltable.cc deleted file mode 100644 index a22f5b316..000000000 --- a/modules/audio_device/main/source/Linux/pulseaudiosymboltable.cc +++ /dev/null @@ -1,39 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "pulseaudiosymboltable.h" - -namespace webrtc_adm_linux_pulse { - -LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(PulseAudioSymbolTable, "libpulse.so.0") -#define X(sym) \ - LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(PulseAudioSymbolTable, sym) -PULSE_AUDIO_SYMBOLS_LIST -#undef X -LATE_BINDING_SYMBOL_TABLE_DEFINE_END(PulseAudioSymbolTable) - -} // namespace webrtc_adm_linux_pulse diff --git a/modules/audio_device/main/source/Linux/pulseaudiosymboltable.h b/modules/audio_device/main/source/Linux/pulseaudiosymboltable.h deleted file mode 100644 index c8cffb27a..000000000 --- a/modules/audio_device/main/source/Linux/pulseaudiosymboltable.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * libjingle - * Copyright 2004--2010, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_H -#define WEBRTC_AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_H - -#include "latebindingsymboltable.h" - -namespace webrtc_adm_linux_pulse { - -// The PulseAudio symbols we need, as an X-Macro list. -// This list must contain precisely every libpulse function that is used in -// the ADM LINUX PULSE Device and Mixer classes -#define PULSE_AUDIO_SYMBOLS_LIST \ - X(pa_bytes_per_second) \ - X(pa_context_connect) \ - X(pa_context_disconnect) \ - X(pa_context_errno) \ - X(pa_context_get_protocol_version) \ - X(pa_context_get_server_info) \ - X(pa_context_get_sink_info_list) \ - X(pa_context_get_sink_info_by_index) \ - X(pa_context_get_sink_info_by_name) \ - X(pa_context_get_sink_input_info) \ - X(pa_context_get_source_info_by_index) \ - X(pa_context_get_source_info_by_name) \ - X(pa_context_get_source_info_list) \ - X(pa_context_get_state) \ - X(pa_context_new) \ - X(pa_context_set_sink_input_volume) \ - X(pa_context_set_sink_input_mute) \ - X(pa_context_set_source_volume_by_index) \ - X(pa_context_set_source_mute_by_index) \ - X(pa_context_set_state_callback) \ - X(pa_context_unref) \ - X(pa_cvolume_set) \ - X(pa_operation_get_state) \ - X(pa_operation_unref) \ - X(pa_stream_connect_playback) \ - X(pa_stream_connect_record) \ - X(pa_stream_disconnect) \ - X(pa_stream_drop) \ - X(pa_stream_get_device_index) \ - X(pa_stream_get_index) \ - X(pa_stream_get_latency) \ - X(pa_stream_get_sample_spec) \ - X(pa_stream_get_state) \ - X(pa_stream_new) \ - X(pa_stream_peek) \ - X(pa_stream_readable_size) \ - X(pa_stream_set_buffer_attr) \ - X(pa_stream_set_overflow_callback) \ - X(pa_stream_set_read_callback) \ - X(pa_stream_set_state_callback) \ - X(pa_stream_set_underflow_callback) \ - X(pa_stream_set_write_callback) \ - X(pa_stream_unref) \ - X(pa_stream_writable_size) \ - X(pa_stream_write) \ - X(pa_strerror) \ - X(pa_threaded_mainloop_free) \ - X(pa_threaded_mainloop_get_api) \ - X(pa_threaded_mainloop_lock) \ - X(pa_threaded_mainloop_new) \ - X(pa_threaded_mainloop_signal) \ - X(pa_threaded_mainloop_start) \ - X(pa_threaded_mainloop_stop) \ - X(pa_threaded_mainloop_unlock) \ - X(pa_threaded_mainloop_wait) - -LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(PulseAudioSymbolTable) -#define X(sym) \ - LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(PulseAudioSymbolTable, sym) -PULSE_AUDIO_SYMBOLS_LIST -#undef X -LATE_BINDING_SYMBOL_TABLE_DECLARE_END(PulseAudioSymbolTable) - -} // namespace webrtc_adm_linux_pulse - -#endif // WEBRTC_AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_H diff --git a/modules/audio_device/main/source/Mac/audio_device_mac.cc b/modules/audio_device/main/source/Mac/audio_device_mac.cc deleted file mode 100644 index 8a360cca3..000000000 --- a/modules/audio_device/main/source/Mac/audio_device_mac.cc +++ /dev/null @@ -1,3431 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_utility.h" -#include "audio_device_mac.h" -#include "audio_device_config.h" - -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" - -#include - -#include // sysctlbyname() -#include // mach_task_self() -#include // OSAtomicCompareAndSwap() -#include "portaudio/pa_ringbuffer.h" - -namespace webrtc -{ - -#define WEBRTC_CA_RETURN_ON_ERR(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceError, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - return -1; \ - } \ - } while(0) - -#define WEBRTC_CA_LOG_ERR(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceError, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - } \ - } while(0) - -#define WEBRTC_CA_LOG_WARN(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceWarning, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - } \ - } while(0) - -enum -{ - MaxNumberDevices = 64 -}; - -void AudioDeviceMac::AtomicSet32(int32_t* theValue, int32_t newValue) -{ - while (1) - { - int32_t oldValue = *theValue; - if (OSAtomicCompareAndSwap32Barrier(oldValue, newValue, theValue) - == true) - { - return; - } - } -} - -int32_t AudioDeviceMac::AtomicGet32(int32_t* theValue) -{ - while (1) - { - WebRtc_Word32 value = *theValue; - if (OSAtomicCompareAndSwap32Barrier(value, value, theValue) == true) - { - return value; - } - } -} - -// CoreAudio errors are best interpreted as four character strings. -void AudioDeviceMac::logCAMsg(const TraceLevel level, - const TraceModule module, - const WebRtc_Word32 id, const char *msg, - const char *err) -{ - assert(msg != NULL); - assert(err != NULL); - -#ifdef WEBRTC_BIG_ENDIAN - WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err); -#else - // We need to flip the characters in this case. - WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err - + 2, err + 1, err); -#endif -} - -AudioDeviceMac::AudioDeviceMac(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _critSectCb(*CriticalSectionWrapper::CreateCriticalSection()), - _stopEventRec(*EventWrapper::Create()), - _stopEvent(*EventWrapper::Create()), - _captureWorkerThread(NULL), - _renderWorkerThread(NULL), - _captureWorkerThreadId(0), - _renderWorkerThreadId(0), - _id(id), - _mixerManager(id), - _inputDeviceIndex(0), - _outputDeviceIndex(0), - _inputDeviceID(kAudioObjectUnknown), - _outputDeviceID(kAudioObjectUnknown), - _inputDeviceIsSpecified(false), - _outputDeviceIsSpecified(false), - _recChannels(N_REC_CHANNELS), - _playChannels(N_PLAY_CHANNELS), - _captureBufData(NULL), - _renderBufData(NULL), - _playBufType(AudioDeviceModule::kFixedBufferSize), - _initialized(false), - _isShutDown(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _startRec(false), - _stopRec(false), - _stopPlay(false), - _AGC(false), - _renderDeviceIsAlive(1), - _captureDeviceIsAlive(1), - _twoDevices(true), - _doStop(false), - _doStopRec(false), - _macBookPro(false), - _macBookProPanRight(false), - _captureLatencyUs(0), - _renderLatencyUs(0), - _captureDelayUs(0), - _renderDelayUs(0), - _renderDelayOffsetSamples(0), - _playBufDelayFixed(20), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _paCaptureBuffer(NULL), - _paRenderBuffer(NULL), - _captureBufSizeSamples(0), - _renderBufSizeSamples(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); - - assert(&_stopEvent != NULL); - assert(&_stopEventRec != NULL); - - memset(_renderConvertData, 0, sizeof(_renderConvertData)); -} - - -AudioDeviceMac::~AudioDeviceMac() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - - if (!_isShutDown) - { - Terminate(); - } - - if (_captureWorkerThread) - { - delete _captureWorkerThread; - _captureWorkerThread = NULL; - } - - if (_renderWorkerThread) - { - delete _renderWorkerThread; - _renderWorkerThread = NULL; - } - - if (_paRenderBuffer) - { - delete _paRenderBuffer; - _paRenderBuffer = NULL; - } - - if (_paCaptureBuffer) - { - delete _paCaptureBuffer; - _paCaptureBuffer = NULL; - } - - if (_renderBufData) - { - delete[] _renderBufData; - _renderBufData = NULL; - } - - if (_captureBufData) - { - delete[] _captureBufData; - _captureBufData = NULL; - } - - kern_return_t kernErr = KERN_SUCCESS; - kernErr = semaphore_destroy(mach_task_self(), _renderSemaphore); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_destroy() error: %d", kernErr); - } - - kernErr = semaphore_destroy(mach_task_self(), _captureSemaphore); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_destroy() error: %d", kernErr); - } - - delete &_stopEvent; - delete &_stopEventRec; - delete &_critSect; - delete &_critSectCb; -} - -// ============================================================================ -// API -// ============================================================================ - -void AudioDeviceMac::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // inform the AudioBuffer about default settings for this implementation - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); -} - -WebRtc_Word32 AudioDeviceMac::ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kPlatformDefaultAudio; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - OSStatus err = noErr; - - _isShutDown = false; - - // PortAudio ring buffers require an elementCount which is a power of two. - if (_renderBufData == NULL) - { - UInt32 powerOfTwo = 1; - while (powerOfTwo < PLAY_BUF_SIZE_IN_SAMPLES) - { - powerOfTwo <<= 1; - } - _renderBufSizeSamples = powerOfTwo; - _renderBufData = new SInt16[_renderBufSizeSamples]; - } - - if (_paRenderBuffer == NULL) - { - _paRenderBuffer = new PaUtilRingBuffer; - ring_buffer_size_t bufSize = -1; - bufSize = PaUtil_InitializeRingBuffer(_paRenderBuffer, sizeof(SInt16), - _renderBufSizeSamples, - _renderBufData); - if (bufSize == -1) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, " PaUtil_InitializeRingBuffer() error"); - return -1; - } - } - - if (_captureBufData == NULL) - { - UInt32 powerOfTwo = 1; - while (powerOfTwo < REC_BUF_SIZE_IN_SAMPLES) - { - powerOfTwo <<= 1; - } - _captureBufSizeSamples = powerOfTwo; - _captureBufData = new Float32[_captureBufSizeSamples]; - } - - if (_paCaptureBuffer == NULL) - { - _paCaptureBuffer = new PaUtilRingBuffer; - ring_buffer_size_t bufSize = -1; - bufSize = PaUtil_InitializeRingBuffer(_paCaptureBuffer, - sizeof(Float32), - _captureBufSizeSamples, - _captureBufData); - if (bufSize == -1) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, " PaUtil_InitializeRingBuffer() error"); - return -1; - } - } - - if (_renderWorkerThread == NULL) - { - _renderWorkerThread - = ThreadWrapper::CreateThread(RunRender, this, kRealtimePriority, - "RenderWorkerThread"); - if (_renderWorkerThread == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, " Render CreateThread() error"); - return -1; - } - } - - if (_captureWorkerThread == NULL) - { - _captureWorkerThread - = ThreadWrapper::CreateThread(RunCapture, this, kRealtimePriority, - "CaptureWorkerThread"); - if (_captureWorkerThread == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, - _id, " Capture CreateThread() error"); - return -1; - } - } - - kern_return_t kernErr = KERN_SUCCESS; - kernErr = semaphore_create(mach_task_self(), &_renderSemaphore, - SYNC_POLICY_FIFO, 0); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " semaphore_create() error: %d", kernErr); - return -1; - } - - kernErr = semaphore_create(mach_task_self(), &_captureSemaphore, - SYNC_POLICY_FIFO, 0); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - " semaphore_create() error: %d", kernErr); - return -1; - } - - // Setting RunLoop to NULL here instructs HAL to manage its own thread for - // notifications. This was the default behaviour on OS X 10.5 and earlier, but now - // must be explicitly specified. HAL would otherwise try to use the main thread to - // issue notifications. - AudioObjectPropertyAddress propertyAddress = { - kAudioHardwarePropertyRunLoop, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - CFRunLoopRef runLoop = NULL; - UInt32 size = sizeof(CFRunLoopRef); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(kAudioObjectSystemObject, - &propertyAddress, 0, NULL, size, &runLoop)); - - // Listen for any device changes. - propertyAddress.mSelector = kAudioHardwarePropertyDevices; - WEBRTC_CA_LOG_ERR(AudioObjectAddPropertyListener(kAudioObjectSystemObject, - &propertyAddress, &objectListenerProc, this)); - - // Determine if this is a MacBook Pro - _macBookPro = false; - _macBookProPanRight = false; - char buf[128]; - size_t length = sizeof(buf); - memset(buf, 0, length); - - int intErr = sysctlbyname("hw.model", buf, &length, NULL, 0); - if (intErr != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error in sysctlbyname(): %d", err); - } else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Hardware model: %s", buf); - if (strncmp(buf, "MacBookPro", 10) == 0) - { - _macBookPro = true; - } - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - _initialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_initialized) - { - return 0; - } - - if (_recording) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording must be stopped"); - return -1; - } - - if (_playing) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Playback must be stopped"); - return -1; - } - - _critSect.Enter(); - - _mixerManager.Close(); - - OSStatus err = noErr; - int retVal = 0; - - AudioObjectPropertyAddress propertyAddress = { - kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(kAudioObjectSystemObject, - &propertyAddress, &objectListenerProc, this)); - - err = AudioHardwareUnload(); - if (err != noErr) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Error in AudioHardwareUnload()", (const char*) &err); - retVal = -1; - } - - _critSect.Leave(); - - _isShutDown = true; - _initialized = false; - _outputDeviceIsSpecified = false; - _inputDeviceIsSpecified = false; - - return retVal; -} - -bool AudioDeviceMac::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_initialized); -} - -WebRtc_Word32 AudioDeviceMac::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker exists - // - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (InitDevice(_outputDeviceIndex, _outputDeviceID, false) == -1) - { - return -1; - } - - if (_inputDeviceID == _outputDeviceID) - { - _twoDevices = false; - } else - { - _twoDevices = true; - } - - if (_mixerManager.OpenSpeaker(_outputDeviceID) == -1) - { - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid microphone exists - // - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - - -WebRtc_Word32 AudioDeviceMac::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (InitDevice(_inputDeviceIndex, _inputDeviceID, true) == -1) - { - return -1; - } - - if (_inputDeviceID == _outputDeviceID) - { - _twoDevices = false; - } else - { - _twoDevices = true; - } - - if (_mixerManager.OpenMicrophone(_inputDeviceID) == -1) - { - return -1; - } - - return 0; -} - -bool AudioDeviceMac::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.SpeakerIsInitialized()); -} - -bool AudioDeviceMac::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_mixerManager.MicrophoneIsInitialized()); -} - -WebRtc_Word32 AudioDeviceMac::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control. - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a volume control exists - // - available = true; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetSpeakerVolume(volume=%u)", volume); - - return (_mixerManager.SetSpeakerVolume(volume)); -} - -WebRtc_Word32 AudioDeviceMac::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.SpeakerVolume(level) == -1) - { - return -1; - } - - volume = level; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceAudioDevice, - _id, - "AudioDeviceMac::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 -AudioDeviceMac::WaveOutVolume(WebRtc_UWord16& /*volumeLeft*/, - WebRtc_UWord16& /*volumeRight*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceMac::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinSpeakerVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - return 0; -} - -WebRtc_Word32 -AudioDeviceMac::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - // Make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control, hence it is safe to state that there is no mute control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected speaker has a mute control - // - _mixerManager.SpeakerMuteIsAvailable(isAvailable); - - available = isAvailable; - - // Close the initialized output mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetSpeakerMute(enable=%u)", enable); - return (_mixerManager.SetSpeakerMute(enable)); -} - -WebRtc_Word32 AudioDeviceMac::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.SpeakerMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a mute control - // - _mixerManager.MicrophoneMuteIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceWindowsWave::SetMicrophoneMute(enable=%u)", enable); - return (_mixerManager.SetMicrophoneMute(enable)); -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.MicrophoneMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Enumerate all avaliable microphone and make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a boost control - // - _mixerManager.MicrophoneBoostIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetMicrophoneBoost(enable=%u)", enable); - - return (_mixerManager.SetMicrophoneBoost(enable)); -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool onOff(0); - - if (_mixerManager.MicrophoneBoost(onOff) == -1) - { - return -1; - } - - enabled = onOff; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - if (!wasInitialized && InitMicrophone() == -1) - { - // Cannot open the specified device - available = false; - return 0; - } - - // Check if the selected microphone can record stereo - // - _mixerManager.StereoRecordingIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetStereoRecording(enable=%u)", enable); - - if (enable) - _recChannels = 2; - else - _recChannels = 1; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_recChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool isAvailable(false); - bool wasInitialized = _mixerManager.SpeakerIsInitialized(); - - if (!wasInitialized && InitSpeaker() == -1) - { - // Cannot open the specified device - available = false; - return 0; - } - - // Check if the selected microphone can record stereo - // - _mixerManager.StereoPlayoutIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseSpeaker(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetStereoPlayout(enable=%u)", enable); - - if (enable) - _playChannels = 2; - else - _playChannels = 1; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_playChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -bool AudioDeviceMac::AGC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - return _AGC; -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); - - // Make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (!wasInitialized && InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control. - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a volume control - // exists - // - available = true; - - // Close the initialized input mixer - // - if (!wasInitialized) - { - _mixerManager.CloseMicrophone(); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetMicrophoneVolume(volume=%u)", volume); - - return (_mixerManager.SetMicrophoneVolume(volume)); -} - -WebRtc_Word32 AudioDeviceMac::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.MicrophoneVolume(level) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " failed to retrive current microphone level"); - return -1; - } - - volume = level; - return 0; -} - -WebRtc_Word32 -AudioDeviceMac::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - return 0; -} - -WebRtc_Word32 -AudioDeviceMac::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinMicrophoneVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - return 0; -} - -WebRtc_Word32 -AudioDeviceMac::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - return 0; -} - -WebRtc_Word16 AudioDeviceMac::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - AudioDeviceID playDevices[MaxNumberDevices]; - return GetNumberDevices(kAudioDevicePropertyScopeOutput, playDevices, - MaxNumberDevices); -} - -WebRtc_Word32 AudioDeviceMac::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - AudioDeviceID playDevices[MaxNumberDevices]; - WebRtc_UWord32 nDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput, - playDevices, MaxNumberDevices); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable waveform-audio output devices is %u", - nDevices); - - if (index > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices - 1)); - return -1; - } - - _outputDeviceIndex = index; - _outputDeviceIsSpecified = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceMac::PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::PlayoutDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(PlayoutDevices()); - - if ((index > (nDevices - 1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return GetDeviceName(kAudioDevicePropertyScopeOutput, index, name); -} - -WebRtc_Word32 AudioDeviceMac::RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::RecordingDeviceName(index=%u)", index); - - const WebRtc_UWord16 nDevices(RecordingDevices()); - - if ((index > (nDevices - 1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - return GetDeviceName(kAudioDevicePropertyScopeInput, index, name); -} - -WebRtc_Word16 AudioDeviceMac::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - AudioDeviceID recDevices[MaxNumberDevices]; - return GetNumberDevices(kAudioDevicePropertyScopeInput, recDevices, - MaxNumberDevices); -} - -WebRtc_Word32 AudioDeviceMac::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - AudioDeviceID recDevices[MaxNumberDevices]; - WebRtc_UWord32 nDevices = GetNumberDevices(kAudioDevicePropertyScopeInput, - recDevices, MaxNumberDevices); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " number of availiable waveform-audio input devices is %u", - nDevices); - - if (index > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " device index is out of range [0,%u]", (nDevices - 1)); - return -1; - } - - _inputDeviceIndex = index; - _inputDeviceIsSpecified = true; - - return 0; -} - - -WebRtc_Word32 -AudioDeviceMac::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType /*device*/) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "WindowsDeviceType not supported"); - return -1; -} - -WebRtc_Word32 AudioDeviceMac::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - available = false; - - // Try to initialize the recording side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (!_outputDeviceIsSpecified) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitSpeaker() failed"); - } - - if (!MicrophoneIsInitialized()) - { - // Make this call to check if we are using - // one or two devices (_twoDevices) - bool available = false; - if (MicrophoneIsAvailable(available) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " MicrophoneIsAvailable() failed"); - } - } - - PaUtil_FlushRingBuffer(_paRenderBuffer); - - OSStatus err = noErr; - UInt32 size = 0; - _renderDelayOffsetSamples = 0; - _renderDelayUs = 0; - _renderLatencyUs = 0; - _renderDeviceIsAlive = 1; - _doStop = false; - - // The internal microphone of a MacBook Pro is located under the left speaker - // grille. When the internal speakers are in use, we want to fully stereo - // pan to the right. - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeOutput, 0 }; - if (_macBookPro) - { - _macBookProPanRight = false; - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - UInt32 dataSource = 0; - size = sizeof(dataSource); - WEBRTC_CA_LOG_WARN(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &dataSource)); - - if (dataSource == 'ispk') - { - _macBookProPanRight = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, - "MacBook Pro using internal speakers; stereo" - " panning right"); - } else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, - _id, "MacBook Pro not using internal speakers"); - } - - // Add a listener to determine if the status changes. - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - } - } - - // Get current stream description - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; - memset(&_outStreamFormat, 0, sizeof(_outStreamFormat)); - size = sizeof(_outStreamFormat); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &_outStreamFormat)); - - if (_outStreamFormat.mFormatID != kAudioFormatLinearPCM) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Unacceptable output stream format -> mFormatID", - (const char *) &_outStreamFormat.mFormatID); - return -1; - } - - if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Too many channels on device -> mChannelsPerFrame = %d", - _outStreamFormat.mChannelsPerFrame); - return -1; - } - - if (_outStreamFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Non-interleaved audio data is not supported.", - "AudioHardware streams should not have this format."); - return -1; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Ouput stream format:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mSampleRate = %f, mChannelsPerFrame = %u", - _outStreamFormat.mSampleRate, - _outStreamFormat.mChannelsPerFrame); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mBytesPerPacket = %u, mFramesPerPacket = %u", - _outStreamFormat.mBytesPerPacket, - _outStreamFormat.mFramesPerPacket); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mBytesPerFrame = %u, mBitsPerChannel = %u", - _outStreamFormat.mBytesPerFrame, - _outStreamFormat.mBitsPerChannel); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mFormatFlags = %u, mChannelsPerFrame = %u", - _outStreamFormat.mFormatFlags, - _outStreamFormat.mChannelsPerFrame); - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", - (const char *) &_outStreamFormat.mFormatID); - - // Our preferred format to work with - _outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC; - if (_outStreamFormat.mChannelsPerFrame >= 2 && (_playChannels == 2)) - { - _outDesiredFormat.mChannelsPerFrame = 2; - } else - { - // Disable stereo playout when we only have one channel on the device. - _outDesiredFormat.mChannelsPerFrame = 1; - _playChannels = 1; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Stereo playout unavailable on this device"); - } - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutChannels((WebRtc_UWord8) _playChannels); - } - - _renderDelayOffsetSamples = _renderBufSizeSamples - N_BUFFERS_OUT - * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * _outDesiredFormat.mChannelsPerFrame; - - _outDesiredFormat.mBytesPerPacket = _outDesiredFormat.mChannelsPerFrame - * sizeof(SInt16); - _outDesiredFormat.mFramesPerPacket = 1; // In uncompressed audio, - // a packet is one frame. - _outDesiredFormat.mBytesPerFrame = _outDesiredFormat.mChannelsPerFrame - * sizeof(SInt16); - _outDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8; - - _outDesiredFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger - | kLinearPCMFormatFlagIsPacked; -#ifdef WEBRTC_BIG_ENDIAN - _outDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; -#endif - _outDesiredFormat.mFormatID = kAudioFormatLinearPCM; - - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_outDesiredFormat, &_outStreamFormat, - &_renderConverter)); - - // First try to set buffer size to desired value (_playBufDelayFixed) - UInt32 bufByteCount = (UInt32)((_outStreamFormat.mSampleRate / 1000.0) - * _playBufDelayFixed * _outStreamFormat.mChannelsPerFrame - * sizeof(Float32)); - if (_outStreamFormat.mFramesPerPacket != 0) - { - if (bufByteCount % _outStreamFormat.mFramesPerPacket != 0) - { - bufByteCount = ((UInt32)(bufByteCount - / _outStreamFormat.mFramesPerPacket) + 1) - * _outStreamFormat.mFramesPerPacket; - } - } - - // Ensure the buffer size is within the acceptable range provided by the device. - propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange; - AudioValueRange range; - size = sizeof(range); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &range)); - if (range.mMinimum > bufByteCount) - { - bufByteCount = range.mMinimum; - } else if (range.mMaximum < bufByteCount) - { - bufByteCount = range.mMaximum; - } - - propertyAddress.mSelector = kAudioDevicePropertyBufferSize; - size = sizeof(bufByteCount); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, size, &bufByteCount)); - - // Get render device latency - propertyAddress.mSelector = kAudioDevicePropertyLatency; - UInt32 latency = 0; - size = sizeof(UInt32); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &latency)); - _renderLatencyUs = (WebRtc_UWord32) ((1.0e6 * latency) - / _outStreamFormat.mSampleRate); - - // Get render stream latency - propertyAddress.mSelector = kAudioDevicePropertyStreams; - AudioStreamID stream = 0; - size = sizeof(AudioStreamID); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &stream)); - propertyAddress.mSelector = kAudioStreamPropertyLatency; - size = sizeof(UInt32); - latency = 0; - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &latency)); - _renderLatencyUs += (WebRtc_UWord32) ((1.0e6 * latency) - / _outStreamFormat.mSampleRate); - - // Listen for format changes - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; - WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - // Listen for processor overloads - propertyAddress.mSelector = kAudioDeviceProcessorOverload; - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - if (_twoDevices || !_recIsInitialized) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_outputDeviceID, - deviceIOProc, this, &_deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceAddIOProc(_outputDeviceID, deviceIOProc, this)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - - // Mark playout side as initialized - _playIsInitialized = true; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " initial playout status: _renderDelayOffsetSamples=%d," - " _renderDelayUs=%d, _renderLatencyUs=%d", - _renderDelayOffsetSamples, _renderDelayUs, _renderLatencyUs); - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (!_inputDeviceIsSpecified) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " InitMicrophone() failed"); - } - - if (!SpeakerIsInitialized()) - { - // Make this call to check if we are using - // one or two devices (_twoDevices) - bool available = false; - if (SpeakerIsAvailable(available) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " SpeakerIsAvailable() failed"); - } - } - - OSStatus err = noErr; - UInt32 size = 0; - - PaUtil_FlushRingBuffer(_paCaptureBuffer); - - _captureDelayUs = 0; - _captureLatencyUs = 0; - _captureDeviceIsAlive = 1; - _doStopRec = false; - - // Get current stream description - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeInput, 0 }; - memset(&_inStreamFormat, 0, sizeof(_inStreamFormat)); - size = sizeof(_inStreamFormat); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &_inStreamFormat)); - - if (_inStreamFormat.mFormatID != kAudioFormatLinearPCM) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Unacceptable input stream format -> mFormatID", - (const char *) &_inStreamFormat.mFormatID); - return -1; - } - - if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - ", Too many channels on device (mChannelsPerFrame = %d)", - _inStreamFormat.mChannelsPerFrame); - return -1; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Input stream format:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " mSampleRate = %f, mChannelsPerFrame = %u", - _inStreamFormat.mSampleRate, _inStreamFormat.mChannelsPerFrame); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " mBytesPerPacket = %u, mFramesPerPacket = %u", - _inStreamFormat.mBytesPerPacket, - _inStreamFormat.mFramesPerPacket); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " mBytesPerFrame = %u, mBitsPerChannel = %u", - _inStreamFormat.mBytesPerFrame, - _inStreamFormat.mBitsPerChannel); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " mFormatFlags = %u, mChannelsPerFrame = %u", - _inStreamFormat.mFormatFlags, - _inStreamFormat.mChannelsPerFrame); - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", - (const char *) &_inStreamFormat.mFormatID); - - // Our preferred format to work with - if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) - { - _inDesiredFormat.mChannelsPerFrame = 2; - } else - { - // Disable stereo recording when we only have one channel on the device. - _inDesiredFormat.mChannelsPerFrame = 1; - _recChannels = 1; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Stereo recording unavailable on this device"); - } - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels((WebRtc_UWord8) _recChannels); - } - - _inDesiredFormat.mSampleRate = N_REC_SAMPLES_PER_SEC; - _inDesiredFormat.mBytesPerPacket = _inDesiredFormat.mChannelsPerFrame - * sizeof(SInt16); - _inDesiredFormat.mFramesPerPacket = 1; - _inDesiredFormat.mBytesPerFrame = _inDesiredFormat.mChannelsPerFrame - * sizeof(SInt16); - _inDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8; - - _inDesiredFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger - | kLinearPCMFormatFlagIsPacked; -#ifdef WEBRTC_BIG_ENDIAN - _inDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; -#endif - _inDesiredFormat.mFormatID = kAudioFormatLinearPCM; - - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_inStreamFormat, &_inDesiredFormat, - &_captureConverter)); - - // First try to set buffer size to desired value (10 ms * N_BLOCKS_IO) - // TODO(xians): investigate this block. - UInt32 bufByteCount = (UInt32)((_inStreamFormat.mSampleRate / 1000.0) - * 10.0 * N_BLOCKS_IO * _inStreamFormat.mChannelsPerFrame - * sizeof(Float32)); - if (_inStreamFormat.mFramesPerPacket != 0) - { - if (bufByteCount % _inStreamFormat.mFramesPerPacket != 0) - { - bufByteCount = ((UInt32)(bufByteCount - / _inStreamFormat.mFramesPerPacket) + 1) - * _inStreamFormat.mFramesPerPacket; - } - } - - // Ensure the buffer size is within the acceptable range provided by the device. - propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange; - AudioValueRange range; - size = sizeof(range); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &range)); - if (range.mMinimum > bufByteCount) - { - bufByteCount = range.mMinimum; - } else if (range.mMaximum < bufByteCount) - { - bufByteCount = range.mMaximum; - } - - propertyAddress.mSelector = kAudioDevicePropertyBufferSize; - size = sizeof(bufByteCount); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, size, &bufByteCount)); - - // Get capture device latency - propertyAddress.mSelector = kAudioDevicePropertyLatency; - UInt32 latency = 0; - size = sizeof(UInt32); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &latency)); - _captureLatencyUs = (UInt32)((1.0e6 * latency) - / _inStreamFormat.mSampleRate); - - // Get capture stream latency - propertyAddress.mSelector = kAudioDevicePropertyStreams; - AudioStreamID stream = 0; - size = sizeof(AudioStreamID); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &stream)); - propertyAddress.mSelector = kAudioStreamPropertyLatency; - size = sizeof(UInt32); - latency = 0; - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &latency)); - _captureLatencyUs += (UInt32)((1.0e6 * latency) - / _inStreamFormat.mSampleRate); - - // Listen for format changes - // TODO(xians): should we be using kAudioDevicePropertyDeviceHasChanged? - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; - WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(_inputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - // Listen for processor overloads - propertyAddress.mSelector = kAudioDeviceProcessorOverload; - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_inputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - if (_twoDevices) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_inputDeviceID, - inDeviceIOProc, this, &_inDeviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceAddIOProc(_inputDeviceID, inDeviceIOProc, - this)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } else if (!_playIsInitialized) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_inputDeviceID, deviceIOProc, - this, &_deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceAddIOProc(_inputDeviceID, deviceIOProc, - this)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - - // Mark recording side as initialized - _recIsInitialized = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_recIsInitialized) - { - return -1; - } - - if (_recording) - { - return 0; - } - - if (!_initialized) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Recording worker thread has not been started"); - return -1; - } - - OSStatus err = noErr; - - unsigned int threadID(0); - if (_captureWorkerThread != NULL) - { - _captureWorkerThread->Start(threadID); - } - _captureWorkerThreadId = threadID; - - if (_twoDevices) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _inDeviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, inDeviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } else if (!_playing) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, deviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - - _recording = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return 0; - } - - OSStatus err = noErr; - - // Stop device - int32_t captureDeviceIsAlive = AtomicGet32(&_captureDeviceIsAlive); - if (_twoDevices) - { - if (_recording && captureDeviceIsAlive == 1) - { - _doStopRec = true; // Signal to io proc to stop audio device - _critSect.Leave(); // Cannot be under lock, risk of deadlock - if (kEventTimeout == _stopEventRec.Wait(2000)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " Timed out stopping the capture IOProc. " - "We likely failed to detect a device removal."); - } - _critSect.Enter(); - _doStopRec = false; - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Recording stopped"); - } -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceDestroyIOProcID != NULL) - { - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_inputDeviceID, - _inDeviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_LOG_WARN(AudioDeviceRemoveIOProc(_inputDeviceID, inDeviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } else if (!_playing) - { - // Stop the shared device if playing has stopped as well. - if (_recording && captureDeviceIsAlive == 1) - { - _doStop = true; // Signal to io proc to stop audio device - _critSect.Leave(); // Cannot be under lock, risk of deadlock - if (kEventTimeout == _stopEvent.Wait(2000)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " Timed out stopping the shared IOProc. " - "We likely failed to detect a device removal."); - } - _critSect.Enter(); - _doStop = false; - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - " Recording stopped (shared)"); - } -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceDestroyIOProcID != NULL) - { - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_inputDeviceID, _deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_LOG_WARN(AudioDeviceRemoveIOProc(_inputDeviceID, deviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - - // Setting this signal will allow the worker thread to be stopped. - AtomicSet32(&_captureDeviceIsAlive, 0); - _critSect.Leave(); - if (_captureWorkerThread != NULL) - { - if (!_captureWorkerThread->Stop()) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Timed out waiting for the render worker thread to " - "stop."); - } - } - _critSect.Enter(); - - WEBRTC_CA_LOG_WARN(AudioConverterDispose(_captureConverter)); - - // Remove listeners. - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeInput, 0 }; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_inputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - propertyAddress.mSelector = kAudioDeviceProcessorOverload; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_inputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - _recIsInitialized = false; - _recording = false; - - return 0; -} - -bool AudioDeviceMac::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recIsInitialized); -} - -bool AudioDeviceMac::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_recording); -} - -bool AudioDeviceMac::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playIsInitialized); -} - -WebRtc_Word32 AudioDeviceMac::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return -1; - } - - if (_playing) - { - return 0; - } - - OSStatus err = noErr; - - unsigned int threadID(0); - if (_renderWorkerThread != NULL) - { - _renderWorkerThread->Start(threadID); - } - _renderWorkerThreadId = threadID; - - if (_twoDevices || !_recording) - { -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_outputDeviceID, _deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_outputDeviceID, deviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - _playing = true; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - return 0; - } - - OSStatus err = noErr; - - int32_t renderDeviceIsAlive = AtomicGet32(&_renderDeviceIsAlive); - if (_twoDevices || !_recording) - { - // Stop the shared device if recording has stopped as well. - if (_playing && renderDeviceIsAlive == 1) - { - _doStop = true; // Signal to io proc to stop audio device - _critSect.Leave(); // Cannot be under lock, risk of deadlock - if (kEventTimeout == _stopEvent.Wait(2000)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " Timed out stopping the render IOProc. " - "We likely failed to detect a device removal."); - } - _critSect.Enter(); - _doStop = false; - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Playout stopped"); - } -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceDestroyIOProcID != NULL) - { - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_outputDeviceID, - _deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_LOG_WARN(AudioDeviceRemoveIOProc(_outputDeviceID, deviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - } - - // Setting this signal will allow the worker thread to be stopped. - AtomicSet32(&_renderDeviceIsAlive, 0); - _critSect.Leave(); - if (_renderWorkerThread != NULL) - { - if (!_renderWorkerThread->Stop()) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Timed out waiting for the render worker thread to " - "stop."); - } - } - _critSect.Enter(); - - WEBRTC_CA_LOG_WARN(AudioConverterDispose(_renderConverter)); - - // Remove listeners. - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, - 0 }; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - propertyAddress.mSelector = kAudioDeviceProcessorOverload; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - - if (_macBookPro) - { - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - propertyAddress.mSelector = kAudioDevicePropertyDataSource; - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID, - &propertyAddress, &objectListenerProc, this)); - } - } - - _playIsInitialized = false; - _playing = false; - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs); - delayMS = static_cast (1e-3 * (renderDelayUs - + _renderLatencyUs) + 0.5); - return 0; -} - -WebRtc_Word32 AudioDeviceMac::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - int32_t captureDelayUs = AtomicGet32(&_captureDelayUs); - delayMS = static_cast (1e-3 * (captureDelayUs - + _captureLatencyUs) + 0.5); - return 0; -} - -bool AudioDeviceMac::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - return (_playing); -} - -WebRtc_Word32 AudioDeviceMac::SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "AudioDeviceMac::SetPlayoutBuffer(type=%u, sizeMS=%u)", type, - sizeMS); - - if (type != AudioDeviceModule::kFixedBufferSize) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Adaptive buffer size not supported on this platform"); - return -1; - } - - _playBufType = type; - _playBufDelayFixed = sizeMS; - return 0; -} - -WebRtc_Word32 AudioDeviceMac::PlayoutBuffer( - AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - type = _playBufType; - sizeMS = _playBufDelayFixed; - - return 0; -} - -// Not implemented for Mac. -WebRtc_Word32 AudioDeviceMac::CPULoad(WebRtc_UWord16& /*load*/) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " API call not supported on this platform"); - - return -1; -} - -bool AudioDeviceMac::PlayoutWarning() const -{ - return (_playWarning > 0); -} - -bool AudioDeviceMac::PlayoutError() const -{ - return (_playError > 0); -} - -bool AudioDeviceMac::RecordingWarning() const -{ - return (_recWarning > 0); -} - -bool AudioDeviceMac::RecordingError() const -{ - return (_recError > 0); -} - -void AudioDeviceMac::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -void AudioDeviceMac::ClearPlayoutError() -{ - _playError = 0; -} - -void AudioDeviceMac::ClearRecordingWarning() -{ - _recWarning = 0; -} - -void AudioDeviceMac::ClearRecordingError() -{ - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -WebRtc_Word32 -AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope, - AudioDeviceID scopedDeviceIds[], - const WebRtc_UWord32 deviceListLength) -{ - OSStatus err = noErr; - - AudioObjectPropertyAddress propertyAddress = { - kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - UInt32 size = 0; - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, - &propertyAddress, 0, NULL, &size)); - if (size == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "No devices"); - return 0; - } - - AudioDeviceID* deviceIds = (AudioDeviceID*) malloc(size); - UInt32 numberDevices = size / sizeof(AudioDeviceID); - AudioBufferList* bufferList = NULL; - UInt32 numberScopedDevices = 0; - - // First check if there is a default device and list it - UInt32 hardwareProperty = 0; - if (scope == kAudioDevicePropertyScopeOutput) - { - hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice; - } else - { - hardwareProperty = kAudioHardwarePropertyDefaultInputDevice; - } - - AudioObjectPropertyAddress - propertyAddressDefault = { hardwareProperty, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - AudioDeviceID usedID; - UInt32 uintSize = sizeof(UInt32); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject, - &propertyAddressDefault, 0, NULL, &uintSize, &usedID)); - if (usedID != kAudioDeviceUnknown) - { - scopedDeviceIds[numberScopedDevices] = usedID; - numberScopedDevices++; - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "GetNumberDevices(): Default device unknown"); - } - - // Then list the rest of the devices - bool listOK = true; - - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject, - &propertyAddress, 0, NULL, &size, deviceIds)); - if (err != noErr) - { - listOK = false; - } else - { - propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration; - propertyAddress.mScope = scope; - propertyAddress.mElement = 0; - for (UInt32 i = 0; i < numberDevices; i++) - { - // Check for input channels - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyDataSize(deviceIds[i], - &propertyAddress, 0, NULL, &size)); - if (err == kAudioHardwareBadDeviceError) - { - // This device doesn't actually exist; continue iterating. - continue; - } else if (err != noErr) - { - listOK = false; - break; - } - - bufferList = (AudioBufferList*) malloc(size); - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(deviceIds[i], - &propertyAddress, 0, NULL, &size, bufferList)); - if (err != noErr) - { - listOK = false; - break; - } - - if (bufferList->mNumberBuffers > 0) - { - if (numberScopedDevices >= deviceListLength) - { - WEBRTC_TRACE(kTraceError, - kTraceAudioDevice, _id, - "Device list is not long enough"); - listOK = false; - break; - } - - scopedDeviceIds[numberScopedDevices] = deviceIds[i]; - numberScopedDevices++; - } - - free(bufferList); - bufferList = NULL; - } // for - } - - if (!listOK) - { - if (deviceIds) - { - free(deviceIds); - deviceIds = NULL; - } - - if (bufferList) - { - free(bufferList); - bufferList = NULL; - } - - return -1; - } - - // Happy ending - if (deviceIds) - { - free(deviceIds); - deviceIds = NULL; - } - - return numberScopedDevices; -} - -WebRtc_Word32 -AudioDeviceMac::GetDeviceName(const AudioObjectPropertyScope scope, - const WebRtc_UWord16 index, - char* name) -{ - OSStatus err = noErr; - UInt32 len = kAdmMaxDeviceNameSize; - AudioDeviceID deviceIds[MaxNumberDevices]; - - int numberDevices = GetNumberDevices(scope, deviceIds, MaxNumberDevices); - if (numberDevices < 0) - { - return -1; - } else if (numberDevices == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "No devices"); - return -1; - } - - // If the number is below the number of devices, assume it's "WEBRTC ID" - // otherwise assume it's a CoreAudio ID - AudioDeviceID usedID; - - // Check if there is a default device - bool isDefaultDevice = false; - if (index == 0) - { - UInt32 hardwareProperty = 0; - if (scope == kAudioDevicePropertyScopeOutput) - { - hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice; - } else - { - hardwareProperty = kAudioHardwarePropertyDefaultInputDevice; - } - AudioObjectPropertyAddress propertyAddress = { hardwareProperty, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - UInt32 size = sizeof(UInt32); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject, - &propertyAddress, 0, NULL, &size, &usedID)); - if (usedID == kAudioDeviceUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "GetDeviceName(): Default device unknown"); - } else - { - isDefaultDevice = true; - } - } - - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyDeviceName, scope, 0 }; - - if (isDefaultDevice) - { - char devName[len]; - - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, - &propertyAddress, 0, NULL, &len, devName)); - - sprintf(name, "default (%s)", devName); - } else - { - if (index < numberDevices) - { - usedID = deviceIds[index]; - } else - { - usedID = index; - } - - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, - &propertyAddress, 0, NULL, &len, name)); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::InitDevice(const WebRtc_UWord16 userDeviceIndex, - AudioDeviceID& deviceId, - const bool isInput) -{ - OSStatus err = noErr; - UInt32 size = 0; - AudioObjectPropertyScope deviceScope; - AudioObjectPropertySelector defaultDeviceSelector; - AudioDeviceID deviceIds[MaxNumberDevices]; - - if (isInput) - { - deviceScope = kAudioDevicePropertyScopeInput; - defaultDeviceSelector = kAudioHardwarePropertyDefaultInputDevice; - } else - { - deviceScope = kAudioDevicePropertyScopeOutput; - defaultDeviceSelector = kAudioHardwarePropertyDefaultOutputDevice; - } - - AudioObjectPropertyAddress - propertyAddress = { defaultDeviceSelector, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - // Get the actual device IDs - int numberDevices = GetNumberDevices(deviceScope, deviceIds, - MaxNumberDevices); - if (numberDevices < 0) - { - return -1; - } else if (numberDevices == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "InitDevice(): No devices"); - return -1; - } - - bool isDefaultDevice = false; - deviceId = kAudioDeviceUnknown; - if (userDeviceIndex == 0) - { - // Try to use default system device - size = sizeof(AudioDeviceID); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject, - &propertyAddress, 0, NULL, &size, &deviceId)); - if (deviceId == kAudioDeviceUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " No default device exists"); - } else - { - isDefaultDevice = true; - } - } - - if (!isDefaultDevice) - { - deviceId = deviceIds[userDeviceIndex]; - } - - // Obtain device name and manufacturer for logging. - // Also use this as a test to ensure a user-set device ID is valid. - char devName[128]; - char devManf[128]; - memset(devName, 0, sizeof(devName)); - memset(devManf, 0, sizeof(devManf)); - - propertyAddress.mSelector = kAudioDevicePropertyDeviceName; - propertyAddress.mScope = deviceScope; - propertyAddress.mElement = 0; - size = sizeof(devName); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, - &propertyAddress, 0, NULL, &size, devName)); - - propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturer; - size = sizeof(devManf); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, - &propertyAddress, 0, NULL, &size, devManf)); - - if (isInput) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Input device: %s %s", devManf, devName); - } else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Output device: %s %s", devManf, devName); - } - - return 0; -} - -OSStatus AudioDeviceMac::objectListenerProc( - AudioObjectID objectId, - UInt32 numberAddresses, - const AudioObjectPropertyAddress addresses[], - void* clientData) -{ - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData; - assert(ptrThis != NULL); - - ptrThis->implObjectListenerProc(objectId, numberAddresses, addresses); - - // AudioObjectPropertyListenerProc functions are supposed to return 0 - return 0; -} - -OSStatus AudioDeviceMac::implObjectListenerProc( - const AudioObjectID objectId, - const UInt32 numberAddresses, - const AudioObjectPropertyAddress addresses[]) -{ - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "AudioDeviceMac::implObjectListenerProc()"); - - for (UInt32 i = 0; i < numberAddresses; i++) - { - if (addresses[i].mSelector == kAudioHardwarePropertyDevices) - { - HandleDeviceChange(); - } else if (addresses[i].mSelector == kAudioDevicePropertyStreamFormat) - { - HandleStreamFormatChange(objectId, addresses[i]); - } else if (addresses[i].mSelector == kAudioDevicePropertyDataSource) - { - HandleDataSourceChange(objectId, addresses[i]); - } else if (addresses[i].mSelector == kAudioDeviceProcessorOverload) - { - HandleProcessorOverload(addresses[i]); - } - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::HandleDeviceChange() -{ - OSStatus err = noErr; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "kAudioHardwarePropertyDevices"); - - // A device has changed. Check if our registered devices have been removed. - // Ensure the devices have been initialized, meaning the IDs are valid. - if (MicrophoneIsInitialized()) - { - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyDeviceIsAlive, - kAudioDevicePropertyScopeInput, 0 }; - UInt32 deviceIsAlive = 1; - UInt32 size = sizeof(UInt32); - err = AudioObjectGetPropertyData(_inputDeviceID, &propertyAddress, 0, - NULL, &size, &deviceIsAlive); - - if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "Capture device is not alive (probably removed)"); - AtomicSet32(&_captureDeviceIsAlive, 0); - _mixerManager.CloseMicrophone(); - if (_recError == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " pending recording error exists"); - } - _recError = 1; // triggers callback from module process thread - } else if (err != noErr) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Error in AudioDeviceGetProperty()", (const char*) &err); - return -1; - } - } - - if (SpeakerIsInitialized()) - { - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyDeviceIsAlive, - kAudioDevicePropertyScopeOutput, 0 }; - UInt32 deviceIsAlive = 1; - UInt32 size = sizeof(UInt32); - err = AudioObjectGetPropertyData(_outputDeviceID, &propertyAddress, 0, - NULL, &size, &deviceIsAlive); - - if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "Render device is not alive (probably removed)"); - AtomicSet32(&_renderDeviceIsAlive, 0); - _mixerManager.CloseSpeaker(); - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, - _id, " pending playout error exists"); - } - _playError = 1; // triggers callback from module process thread - } else if (err != noErr) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Error in AudioDeviceGetProperty()", (const char*) &err); - return -1; - } - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::HandleStreamFormatChange( - const AudioObjectID objectId, - const AudioObjectPropertyAddress propertyAddress) -{ - OSStatus err = noErr; - - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Stream format changed"); - - if (objectId != _inputDeviceID && objectId != _outputDeviceID) - { - return 0; - } - - // Get the new device format - AudioStreamBasicDescription streamFormat; - UInt32 size = sizeof(streamFormat); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(objectId, - &propertyAddress, 0, NULL, &size, &streamFormat)); - - if (streamFormat.mFormatID != kAudioFormatLinearPCM) - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Unacceptable input stream format -> mFormatID", - (const char *) &streamFormat.mFormatID); - return -1; - } - - if (streamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "Too many channels on device (mChannelsPerFrame = %d)", - streamFormat.mChannelsPerFrame); - return -1; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Stream format:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mSampleRate = %f, mChannelsPerFrame = %u", - streamFormat.mSampleRate, streamFormat.mChannelsPerFrame); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mBytesPerPacket = %u, mFramesPerPacket = %u", - streamFormat.mBytesPerPacket, streamFormat.mFramesPerPacket); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mBytesPerFrame = %u, mBitsPerChannel = %u", - streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "mFormatFlags = %u, mChannelsPerFrame = %u", - streamFormat.mFormatFlags, streamFormat.mChannelsPerFrame); - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID", - (const char *) &streamFormat.mFormatID); - - if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) - { - memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat)); - - if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) - { - _inDesiredFormat.mChannelsPerFrame = 2; - } else - { - // Disable stereo recording when we only have one channel on the device. - _inDesiredFormat.mChannelsPerFrame = 1; - _recChannels = 1; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Stereo recording unavailable on this device"); - } - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels((WebRtc_UWord8) _recChannels); - } - - // Recreate the converter with the new format - // TODO(xians): make this thread safe - WEBRTC_CA_RETURN_ON_ERR(AudioConverterDispose(_captureConverter)); - - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&streamFormat, &_inDesiredFormat, - &_captureConverter)); - } else - { - memcpy(&_outStreamFormat, &streamFormat, sizeof(streamFormat)); - - if (_outStreamFormat.mChannelsPerFrame >= 2 && (_playChannels == 2)) - { - _outDesiredFormat.mChannelsPerFrame = 2; - } else - { - // Disable stereo playout when we only have one channel on the device. - _outDesiredFormat.mChannelsPerFrame = 1; - _playChannels = 1; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Stereo playout unavailable on this device"); - } - - if (_ptrAudioBuffer) - { - // Update audio buffer with the selected parameters - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutChannels((WebRtc_UWord8) _playChannels); - } - - _renderDelayOffsetSamples = _renderBufSizeSamples - N_BUFFERS_OUT - * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES - * _outDesiredFormat.mChannelsPerFrame; - - // Recreate the converter with the new format - // TODO(xians): make this thread safe - WEBRTC_CA_RETURN_ON_ERR(AudioConverterDispose(_renderConverter)); - - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_outDesiredFormat, &streamFormat, - &_renderConverter)); - } - - return 0; -} - -WebRtc_Word32 AudioDeviceMac::HandleDataSourceChange( - const AudioObjectID objectId, - const AudioObjectPropertyAddress propertyAddress) -{ - OSStatus err = noErr; - - if (_macBookPro && propertyAddress.mScope - == kAudioDevicePropertyScopeOutput) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, - "Data source changed"); - - _macBookProPanRight = false; - UInt32 dataSource = 0; - UInt32 size = sizeof(UInt32); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(objectId, - &propertyAddress, 0, NULL, &size, &dataSource)); - if (dataSource == 'ispk') - { - _macBookProPanRight = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "MacBook Pro using internal speakers; stereo panning right"); - } else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "MacBook Pro not using internal speakers"); - } - } - - return 0; -} -WebRtc_Word32 AudioDeviceMac::HandleProcessorOverload( - const AudioObjectPropertyAddress propertyAddress) -{ - // TODO(xians): we probably want to notify the user in some way of the - // overload. However, the Windows interpretations of these errors seem to - // be more severe than what ProcessorOverload is thrown for. - // - // We don't log the notification, as it's sent from the HAL's IO thread. We - // don't want to slow it down even further. - if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) - { - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Capture processor - // overload"); - //_callback->ProblemIsReported( - // SndCardStreamObserver::ERecordingProblem); - } else - { - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - // "Render processor overload"); - //_callback->ProblemIsReported( - // SndCardStreamObserver::EPlaybackProblem); - } - - return 0; -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -OSStatus AudioDeviceMac::deviceIOProc(AudioDeviceID, const AudioTimeStamp*, - const AudioBufferList* inputData, - const AudioTimeStamp* inputTime, - AudioBufferList* outputData, - const AudioTimeStamp* outputTime, - void *clientData) -{ - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData; - assert(ptrThis != NULL); - - ptrThis->implDeviceIOProc(inputData, inputTime, outputData, outputTime); - - // AudioDeviceIOProc functions are supposed to return 0 - return 0; -} - -OSStatus AudioDeviceMac::outConverterProc(AudioConverterRef, - UInt32 *numberDataPackets, - AudioBufferList *data, - AudioStreamPacketDescription **, - void *userData) -{ - AudioDeviceMac *ptrThis = (AudioDeviceMac *) userData; - assert(ptrThis != NULL); - - return ptrThis->implOutConverterProc(numberDataPackets, data); -} - -OSStatus AudioDeviceMac::inDeviceIOProc(AudioDeviceID, const AudioTimeStamp*, - const AudioBufferList* inputData, - const AudioTimeStamp* inputTime, - AudioBufferList*, - const AudioTimeStamp*, void* clientData) -{ - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData; - assert(ptrThis != NULL); - - ptrThis->implInDeviceIOProc(inputData, inputTime); - - // AudioDeviceIOProc functions are supposed to return 0 - return 0; -} - -OSStatus AudioDeviceMac::inConverterProc( - AudioConverterRef, - UInt32 *numberDataPackets, - AudioBufferList *data, - AudioStreamPacketDescription ** /*dataPacketDescription*/, - void *userData) -{ - AudioDeviceMac *ptrThis = static_cast (userData); - assert(ptrThis != NULL); - - return ptrThis->implInConverterProc(numberDataPackets, data); -} - -OSStatus AudioDeviceMac::implDeviceIOProc(const AudioBufferList *inputData, - const AudioTimeStamp *inputTime, - AudioBufferList *outputData, - const AudioTimeStamp *outputTime) -{ - OSStatus err = noErr; - UInt64 outputTimeNs = AudioConvertHostTimeToNanos(outputTime->mHostTime); - UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - - if (!_twoDevices && _recording) - { - implInDeviceIOProc(inputData, inputTime); - } - - // Check if we should close down audio device - // Double-checked locking optimization to remove locking overhead - if (_doStop) - { - _critSect.Enter(); - if (_doStop) - { - // This case is for stop play only or stop play+rec - // (in that case out and in device ids are equal) -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_outputDeviceID, _deviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_outputDeviceID, deviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - if (err == noErr) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, " Playout or shared device stopped"); - } - - _doStop = false; - _stopEvent.Set(); - _critSect.Leave(); - return 0; - } - _critSect.Leave(); - } - - if (!_playing) - { - // This can be the case when a shared device is capturing but not - // rendering. We allow the checks above before returning to avoid a - // timeout when capturing is stopped. - return 0; - } - - assert(_outStreamFormat.mBytesPerFrame != 0); - UInt32 size = outputData->mBuffers->mDataByteSize - / _outStreamFormat.mBytesPerFrame; - - // TODO(xians): signal an error somehow? - err = AudioConverterFillComplexBuffer(_renderConverter, outConverterProc, - this, &size, outputData, NULL); - if (err != noErr) - { - if (err == 1) - { - // This is our own error. - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " Error in AudioConverterFillComplexBuffer()"); - return 1; - } else - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Error in AudioConverterFillComplexBuffer()", - (const char *) &err); - return 1; - } - } - - ring_buffer_size_t bufSizeSamples = - PaUtil_GetRingBufferReadAvailable(_paRenderBuffer); - - int32_t renderDelayUs = static_cast (1e-3 * (outputTimeNs - nowNs) - + 0.5); - renderDelayUs += static_cast ((1.0e6 * bufSizeSamples) - / _outDesiredFormat.mChannelsPerFrame / _outDesiredFormat.mSampleRate - + 0.5); - - AtomicSet32(&_renderDelayUs, renderDelayUs); - - return 0; -} - -OSStatus AudioDeviceMac::implOutConverterProc(UInt32 *numberDataPackets, - AudioBufferList *data) -{ - assert(data->mNumberBuffers == 1); - ring_buffer_size_t numSamples = *numberDataPackets - * _outDesiredFormat.mChannelsPerFrame; - - data->mBuffers->mNumberChannels = _outDesiredFormat.mChannelsPerFrame; - // Always give the converter as much as it wants, zero padding as required. - data->mBuffers->mDataByteSize = *numberDataPackets - * _outDesiredFormat.mBytesPerPacket; - data->mBuffers->mData = _renderConvertData; - memset(_renderConvertData, 0, sizeof(_renderConvertData)); - - PaUtil_ReadRingBuffer(_paRenderBuffer, _renderConvertData, numSamples); - - kern_return_t kernErr = semaphore_signal_all(_renderSemaphore); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_signal_all() error: %d", kernErr); - return 1; - } - - return 0; -} - -OSStatus AudioDeviceMac::implInDeviceIOProc(const AudioBufferList *inputData, - const AudioTimeStamp *inputTime) -{ - OSStatus err = noErr; - UInt64 inputTimeNs = AudioConvertHostTimeToNanos(inputTime->mHostTime); - UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - - if (!_recording) - { - return 0; - } - - // Check if we should close down audio device - // Double-checked locking optimization to remove locking overhead - if (_doStopRec) - { - _critSect.Enter(); - if (_doStopRec) - { - // This case is for stop rec only -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - if (AudioDeviceCreateIOProcID != NULL) - { - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID)); - } - else - { -#endif - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_inputDeviceID, inDeviceIOProc)); -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - } -#endif - if (err == noErr) - { - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, - _id, " Recording device stopped"); - } - - _doStopRec = false; - _stopEventRec.Set(); - _critSect.Leave(); - return 0; - } - _critSect.Leave(); - } - - ring_buffer_size_t bufSizeSamples = - PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer); - - int32_t captureDelayUs = static_cast (1e-3 * (nowNs - inputTimeNs) - + 0.5); - captureDelayUs - += static_cast ((1.0e6 * bufSizeSamples) - / _inStreamFormat.mChannelsPerFrame / _inStreamFormat.mSampleRate - + 0.5); - - AtomicSet32(&_captureDelayUs, captureDelayUs); - - assert(inputData->mNumberBuffers == 1); - ring_buffer_size_t numSamples = inputData->mBuffers->mDataByteSize - * _inStreamFormat.mChannelsPerFrame / _inStreamFormat.mBytesPerPacket; - PaUtil_WriteRingBuffer(_paCaptureBuffer, inputData->mBuffers->mData, - numSamples); - - kern_return_t kernErr = semaphore_signal_all(_captureSemaphore); - if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_signal_all() error: %d", kernErr); - } - - return err; -} - -OSStatus AudioDeviceMac::implInConverterProc(UInt32 *numberDataPackets, - AudioBufferList *data) -{ - assert(data->mNumberBuffers == 1); - ring_buffer_size_t numSamples = *numberDataPackets - * _inStreamFormat.mChannelsPerFrame; - - while (PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer) < numSamples) - { - mach_timespec_t timeout; - timeout.tv_sec = 0; - timeout.tv_nsec = TIMER_PERIOD_MS; - - kern_return_t kernErr = semaphore_timedwait(_captureSemaphore, timeout); - if (kernErr == KERN_OPERATION_TIMED_OUT) - { - int32_t signal = AtomicGet32(&_captureDeviceIsAlive); - if (signal == 0) - { - // The capture device is no longer alive; stop the worker thread. - *numberDataPackets = 0; - return 1; - } - } else if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_wait() error: %d", kernErr); - } - } - - // Pass the read pointer directly to the converter to avoid a memcpy. - void* dummyPtr; - ring_buffer_size_t dummySize; - PaUtil_GetRingBufferReadRegions(_paCaptureBuffer, numSamples, - &data->mBuffers->mData, &numSamples, - &dummyPtr, &dummySize); - PaUtil_AdvanceRingBufferReadIndex(_paCaptureBuffer, numSamples); - - data->mBuffers->mNumberChannels = _inStreamFormat.mChannelsPerFrame; - *numberDataPackets = numSamples / _inStreamFormat.mChannelsPerFrame; - data->mBuffers->mDataByteSize = *numberDataPackets - * _inStreamFormat.mBytesPerPacket; - - return 0; -} - -bool AudioDeviceMac::RunRender(void* ptrThis) -{ - return static_cast (ptrThis)->RenderWorkerThread(); -} - -bool AudioDeviceMac::RenderWorkerThread() -{ - ring_buffer_size_t numSamples = ENGINE_PLAY_BUF_SIZE_IN_SAMPLES - * _outDesiredFormat.mChannelsPerFrame; - while (PaUtil_GetRingBufferWriteAvailable(_paRenderBuffer) - - _renderDelayOffsetSamples < numSamples) - { - mach_timespec_t timeout; - timeout.tv_sec = 0; - timeout.tv_nsec = TIMER_PERIOD_MS; - - kern_return_t kernErr = semaphore_timedwait(_renderSemaphore, timeout); - if (kernErr == KERN_OPERATION_TIMED_OUT) - { - int32_t signal = AtomicGet32(&_renderDeviceIsAlive); - if (signal == 0) - { - // The render device is no longer alive; stop the worker thread. - return false; - } - } else if (kernErr != KERN_SUCCESS) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " semaphore_timedwait() error: %d", kernErr); - } - } - - WebRtc_Word8 playBuffer[4 * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES]; - - if (!_ptrAudioBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " capture AudioBuffer is invalid"); - return false; - } - - // Ask for new PCM data to be played out using the AudioDeviceBuffer. - WebRtc_UWord32 nSamples = - _ptrAudioBuffer->RequestPlayoutData(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES); - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != ENGINE_PLAY_BUF_SIZE_IN_SAMPLES) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " invalid number of output samples(%d)", nSamples); - } - - WebRtc_UWord32 nOutSamples = nSamples * _outDesiredFormat.mChannelsPerFrame; - - SInt16 *pPlayBuffer = (SInt16 *) &playBuffer; - if (_macBookProPanRight && (_playChannels == 2)) - { - // Mix entirely into the right channel and zero the left channel. - SInt32 sampleInt32 = 0; - for (WebRtc_UWord32 sampleIdx = 0; sampleIdx < nOutSamples; sampleIdx - += 2) - { - sampleInt32 = pPlayBuffer[sampleIdx]; - sampleInt32 += pPlayBuffer[sampleIdx + 1]; - sampleInt32 /= 2; - - if (sampleInt32 > 32767) - { - sampleInt32 = 32767; - } else if (sampleInt32 < -32768) - { - sampleInt32 = -32768; - } - - pPlayBuffer[sampleIdx] = 0; - pPlayBuffer[sampleIdx + 1] = static_cast (sampleInt32); - } - } - - PaUtil_WriteRingBuffer(_paRenderBuffer, pPlayBuffer, nOutSamples); - - return true; -} - -bool AudioDeviceMac::RunCapture(void* ptrThis) -{ - return static_cast (ptrThis)->CaptureWorkerThread(); -} - -bool AudioDeviceMac::CaptureWorkerThread() -{ - OSStatus err = noErr; - UInt32 noRecSamples = ENGINE_REC_BUF_SIZE_IN_SAMPLES - * _inDesiredFormat.mChannelsPerFrame; - SInt16 recordBuffer[noRecSamples]; - UInt32 size = ENGINE_REC_BUF_SIZE_IN_SAMPLES; - - AudioBufferList engineBuffer; - engineBuffer.mNumberBuffers = 1; // Interleaved channels. - engineBuffer.mBuffers->mNumberChannels = _inDesiredFormat.mChannelsPerFrame; - engineBuffer.mBuffers->mDataByteSize = _inDesiredFormat.mBytesPerPacket - * noRecSamples; - engineBuffer.mBuffers->mData = recordBuffer; - - err = AudioConverterFillComplexBuffer(_captureConverter, inConverterProc, - this, &size, &engineBuffer, NULL); - if (err != noErr) - { - if (err == 1) - { - // This is our own error. - return false; - } else - { - logCAMsg(kTraceError, kTraceAudioDevice, _id, - "Error in AudioConverterFillComplexBuffer()", - (const char *) &err); - return false; - } - } - - // TODO(xians): what if the returned size is incorrect? - if (size == ENGINE_REC_BUF_SIZE_IN_SAMPLES) - { - WebRtc_UWord32 currentMicLevel(0); - WebRtc_UWord32 newMicLevel(0); - WebRtc_Word32 msecOnPlaySide; - WebRtc_Word32 msecOnRecordSide; - - int32_t captureDelayUs = AtomicGet32(&_captureDelayUs); - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs); - - msecOnPlaySide = static_cast (1e-3 * (renderDelayUs - + _renderLatencyUs) + 0.5); - msecOnRecordSide = static_cast (1e-3 * (captureDelayUs - + _captureLatencyUs) + 0.5); - - if (!_ptrAudioBuffer) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " capture AudioBuffer is invalid"); - return false; - } - - // store the recorded buffer (no action will be taken if the - // #recorded samples is not a full buffer) - _ptrAudioBuffer->SetRecordedBuffer((WebRtc_Word8*) &recordBuffer, - (WebRtc_UWord32) size); - - if (AGC()) - { - // store current mic level in the audio buffer if AGC is enabled - if (MicrophoneVolume(currentMicLevel) == 0) - { - // this call does not affect the actual microphone volume - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); - } - } - - _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, 0); - - // deliver recorded samples at specified sample rate, mic level etc. - // to the observer using callback - _ptrAudioBuffer->DeliverRecordedData(); - - if (AGC()) - { - newMicLevel = _ptrAudioBuffer->NewMicLevel(); - if (newMicLevel != 0) - { - // The VQE will only deliver non-zero microphone levels when - // a change is needed. - // Set this new mic level (received from the observer as return - // value in the callback). - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, - _id, " AGC change of volume: old=%u => new=%u", - currentMicLevel, newMicLevel); - if (SetMicrophoneVolume(newMicLevel) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " the required modification of the microphone " - "volume failed"); - } - } - } - } - - return true; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Mac/audio_device_mac.h b/modules/audio_device/main/source/Mac/audio_device_mac.h deleted file mode 100644 index 8069feeb9..000000000 --- a/modules/audio_device/main/source/Mac/audio_device_mac.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_MAC_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_MAC_H - -#include "audio_device_generic.h" -#include "critical_section_wrapper.h" -#include "audio_mixer_manager_mac.h" - -#include -#include -#include - -struct PaUtilRingBuffer; - -namespace webrtc -{ -class EventWrapper; -class ThreadWrapper; - -const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 48000; -const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 48000; - -const WebRtc_UWord32 N_REC_CHANNELS = 1; // default is mono recording -const WebRtc_UWord32 N_PLAY_CHANNELS = 2; // default is stereo playout -const WebRtc_UWord32 N_DEVICE_CHANNELS = 8; - -const WebRtc_UWord32 ENGINE_REC_BUF_SIZE_IN_SAMPLES = (N_REC_SAMPLES_PER_SEC - / 100); -const WebRtc_UWord32 ENGINE_PLAY_BUF_SIZE_IN_SAMPLES = (N_PLAY_SAMPLES_PER_SEC - / 100); - -enum -{ - N_BLOCKS_IO = 2 -}; -enum -{ - N_BUFFERS_IN = 10 -}; -enum -{ - N_BUFFERS_OUT = 3 -}; // Must be at least N_BLOCKS_IO - -const WebRtc_UWord32 TIMER_PERIOD_MS = (2 * 10 * N_BLOCKS_IO * 1000000); - -const WebRtc_UWord32 REC_BUF_SIZE_IN_SAMPLES = (ENGINE_REC_BUF_SIZE_IN_SAMPLES - * N_DEVICE_CHANNELS * N_BUFFERS_IN); -const WebRtc_UWord32 PLAY_BUF_SIZE_IN_SAMPLES = - (ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * N_PLAY_CHANNELS * N_BUFFERS_OUT); - -class AudioDeviceMac: public AudioDeviceGeneric -{ -public: - AudioDeviceMac(const WebRtc_Word32 id); - ~AudioDeviceMac(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 - ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 - MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 - SetPlayoutBuffer(const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, - WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: - void Lock() - { - _critSect.Enter(); - } - ; - void UnLock() - { - _critSect.Leave(); - } - ; - WebRtc_Word32 Id() - { - return _id; - } - - static void AtomicSet32(int32_t* theValue, int32_t newValue); - static int32_t AtomicGet32(int32_t* theValue); - - static void logCAMsg(const TraceLevel level, - const TraceModule module, - const WebRtc_Word32 id, const char *msg, - const char *err); - - WebRtc_Word32 GetNumberDevices(const AudioObjectPropertyScope scope, - AudioDeviceID scopedDeviceIds[], - const WebRtc_UWord32 deviceListLength); - - WebRtc_Word32 GetDeviceName(const AudioObjectPropertyScope scope, - const WebRtc_UWord16 index, char* name); - - WebRtc_Word32 InitDevice(WebRtc_UWord16 userDeviceIndex, - AudioDeviceID& deviceId, bool isInput); - - static OSStatus - objectListenerProc(AudioObjectID objectId, UInt32 numberAddresses, - const AudioObjectPropertyAddress addresses[], - void* clientData); - - OSStatus - implObjectListenerProc(AudioObjectID objectId, UInt32 numberAddresses, - const AudioObjectPropertyAddress addresses[]); - - WebRtc_Word32 HandleDeviceChange(); - - WebRtc_Word32 - HandleStreamFormatChange(AudioObjectID objectId, - AudioObjectPropertyAddress propertyAddress); - - WebRtc_Word32 - HandleDataSourceChange(AudioObjectID objectId, - AudioObjectPropertyAddress propertyAddress); - - WebRtc_Word32 - HandleProcessorOverload(AudioObjectPropertyAddress propertyAddress); - -private: - static OSStatus deviceIOProc(AudioDeviceID device, - const AudioTimeStamp *now, - const AudioBufferList *inputData, - const AudioTimeStamp *inputTime, - AudioBufferList *outputData, - const AudioTimeStamp* outputTime, - void *clientData); - - static OSStatus - outConverterProc(AudioConverterRef audioConverter, - UInt32 *numberDataPackets, AudioBufferList *data, - AudioStreamPacketDescription **dataPacketDescription, - void *userData); - - static OSStatus inDeviceIOProc(AudioDeviceID device, - const AudioTimeStamp *now, - const AudioBufferList *inputData, - const AudioTimeStamp *inputTime, - AudioBufferList *outputData, - const AudioTimeStamp *outputTime, - void *clientData); - - static OSStatus - inConverterProc(AudioConverterRef audioConverter, - UInt32 *numberDataPackets, AudioBufferList *data, - AudioStreamPacketDescription **dataPacketDescription, - void *inUserData); - - OSStatus implDeviceIOProc(const AudioBufferList *inputData, - const AudioTimeStamp *inputTime, - AudioBufferList *outputData, - const AudioTimeStamp *outputTime); - - OSStatus implOutConverterProc(UInt32 *numberDataPackets, - AudioBufferList *data); - - OSStatus implInDeviceIOProc(const AudioBufferList *inputData, - const AudioTimeStamp *inputTime); - - OSStatus implInConverterProc(UInt32 *numberDataPackets, - AudioBufferList *data); - - static bool RunCapture(void*); - static bool RunRender(void*); - bool CaptureWorkerThread(); - bool RenderWorkerThread(); - -private: - AudioDeviceBuffer* _ptrAudioBuffer; - - CriticalSectionWrapper& _critSect; - CriticalSectionWrapper& _critSectCb; - - EventWrapper& _stopEventRec; - EventWrapper& _stopEvent; - - ThreadWrapper* _captureWorkerThread; - ThreadWrapper* _renderWorkerThread; - WebRtc_UWord32 _captureWorkerThreadId; - WebRtc_UWord32 _renderWorkerThreadId; - - WebRtc_Word32 _id; - - AudioMixerManagerMac _mixerManager; - - WebRtc_UWord16 _inputDeviceIndex; - WebRtc_UWord16 _outputDeviceIndex; - AudioDeviceID _inputDeviceID; - AudioDeviceID _outputDeviceID; -#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 - AudioDeviceIOProcID _inDeviceIOProcID; - AudioDeviceIOProcID _deviceIOProcID; -#endif - bool _inputDeviceIsSpecified; - bool _outputDeviceIsSpecified; - - WebRtc_UWord8 _recChannels; - WebRtc_UWord8 _playChannels; - - Float32* _captureBufData; - SInt16* _renderBufData; - - SInt16 _renderConvertData[PLAY_BUF_SIZE_IN_SAMPLES]; - - AudioDeviceModule::BufferType _playBufType; - -private: - bool _initialized; - bool _isShutDown; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _startRec; - bool _stopRec; - bool _stopPlay; - bool _AGC; - - // Atomically set varaibles - int32_t _renderDeviceIsAlive; - int32_t _captureDeviceIsAlive; - - bool _twoDevices; - bool _doStop; // For play if not shared device or play+rec if shared device - bool _doStopRec; // For rec if not shared device - bool _macBookPro; - bool _macBookProPanRight; - bool _stereoRender; - bool _stereoRenderRequested; - - AudioConverterRef _captureConverter; - AudioConverterRef _renderConverter; - - AudioStreamBasicDescription _outStreamFormat; - AudioStreamBasicDescription _outDesiredFormat; - AudioStreamBasicDescription _inStreamFormat; - AudioStreamBasicDescription _inDesiredFormat; - - WebRtc_UWord32 _captureLatencyUs; - WebRtc_UWord32 _renderLatencyUs; - - // Atomically set variables - mutable int32_t _captureDelayUs; - mutable int32_t _renderDelayUs; - - WebRtc_Word32 _renderDelayOffsetSamples; - -private: - WebRtc_UWord16 _playBufDelay; // playback delay - WebRtc_UWord16 _playBufDelayFixed; // fixed playback delay - - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - PaUtilRingBuffer* _paCaptureBuffer; - PaUtilRingBuffer* _paRenderBuffer; - - semaphore_t _renderSemaphore; - semaphore_t _captureSemaphore; - - WebRtc_UWord32 _captureBufSizeSamples; - WebRtc_UWord32 _renderBufSizeSamples; -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_MAC_AUDIO_DEVICE_MAC_H_ diff --git a/modules/audio_device/main/source/Mac/audio_device_utility_mac.cc b/modules/audio_device/main/source/Mac/audio_device_utility_mac.cc deleted file mode 100644 index cc56bfb1d..000000000 --- a/modules/audio_device/main/source/Mac/audio_device_utility_mac.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_utility_mac.h" -#include "audio_device_config.h" // DEBUG_PRINT() -#include "critical_section_wrapper.h" -#include "trace.h" - -namespace webrtc -{ - -AudioDeviceUtilityMac::AudioDeviceUtilityMac(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _lastError(AudioDeviceModule::kAdmErrNone) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, - "%s created", __FUNCTION__); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceUtilityMac() - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceUtilityMac::~AudioDeviceUtilityMac() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - - // free stuff here... - } - - delete &_critSect; -} - -WebRtc_Word32 AudioDeviceUtilityMac::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, - " OS info: %s", "OS X"); - - return 0; -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Mac/audio_device_utility_mac.h b/modules/audio_device/main/source/Mac/audio_device_utility_mac.h deleted file mode 100644 index ccb3d9986..000000000 --- a/modules/audio_device/main/source/Mac/audio_device_utility_mac.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_MAC_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_MAC_H - -#include "audio_device_utility.h" -#include "audio_device.h" - -namespace webrtc -{ -class CriticalSectionWrapper; - -class AudioDeviceUtilityMac: public AudioDeviceUtility -{ -public: - AudioDeviceUtilityMac(const WebRtc_Word32 id); - ~AudioDeviceUtilityMac(); - - virtual WebRtc_Word32 Init(); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - AudioDeviceModule::ErrorCode _lastError; -}; - -} // namespace webrtc - -#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_MAC_AUDIO_DEVICE_UTILITY_MAC_H_ diff --git a/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.cc b/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.cc deleted file mode 100644 index 5f70c4082..000000000 --- a/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.cc +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_mixer_manager_mac.h" -#include "trace.h" - -#include // getpid() - -namespace webrtc { - -#define WEBRTC_CA_RETURN_ON_ERR(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceError, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - return -1; \ - } \ - } while(0) - -#define WEBRTC_CA_LOG_ERR(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceError, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - } \ - } while(0) - -#define WEBRTC_CA_LOG_WARN(expr) \ - do { \ - err = expr; \ - if (err != noErr) { \ - logCAMsg(kTraceWarning, kTraceAudioDevice, _id, \ - "Error in " #expr, (const char *)&err); \ - } \ - } while(0) - -AudioMixerManagerMac::AudioMixerManagerMac(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _inputDeviceID(kAudioObjectUnknown), - _outputDeviceID(kAudioObjectUnknown), - _noInputChannels(0), - _noOutputChannels(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s constructed", __FUNCTION__); -} - -AudioMixerManagerMac::~AudioMixerManagerMac() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, - "%s destructed", __FUNCTION__); - - Close(); - - delete &_critSect; -} - -// ============================================================================ -// PUBLIC METHODS -// ============================================================================ - -WebRtc_Word32 AudioMixerManagerMac::Close() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - CloseSpeaker(); - CloseMicrophone(); - - return 0; - -} - -WebRtc_Word32 AudioMixerManagerMac::CloseSpeaker() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _outputDeviceID = kAudioObjectUnknown; - _noOutputChannels = 0; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::CloseMicrophone() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _inputDeviceID = kAudioObjectUnknown; - _noInputChannels = 0; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::OpenSpeaker(id=%d)", deviceID); - - CriticalSectionScoped lock(_critSect); - - OSStatus err = noErr; - UInt32 size = 0; - pid_t hogPid = -1; - - _outputDeviceID = deviceID; - - // Check which process, if any, has hogged the device. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyHogMode, - kAudioDevicePropertyScopeOutput, 0 }; - - size = sizeof(hogPid); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &hogPid)); - - if (hogPid == -1) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " No process has hogged the input device"); - } - // getpid() is apparently "always successful" - else if (hogPid == getpid()) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Our process has hogged the input device"); - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Another process (pid = %d) has hogged the input device", - static_cast (hogPid)); - - return -1; - } - - // get number of channels from stream format - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; - - // Get the stream format, to be able to read the number of channels. - AudioStreamBasicDescription streamFormat; - size = sizeof(AudioStreamBasicDescription); - memset(&streamFormat, 0, size); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &streamFormat)); - - _noOutputChannels = streamFormat.mChannelsPerFrame; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::OpenMicrophone(AudioDeviceID deviceID) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::OpenMicrophone(id=%d)", deviceID); - - CriticalSectionScoped lock(_critSect); - - OSStatus err = noErr; - UInt32 size = 0; - pid_t hogPid = -1; - - _inputDeviceID = deviceID; - - // Check which process, if any, has hogged the device. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyHogMode, - kAudioDevicePropertyScopeInput, 0 }; - size = sizeof(hogPid); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &hogPid)); - if (hogPid == -1) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " No process has hogged the input device"); - } - // getpid() is apparently "always successful" - else if (hogPid == getpid()) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " Our process has hogged the input device"); - } else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Another process (pid = %d) has hogged the input device", - static_cast (hogPid)); - - return -1; - } - - // get number of channels from stream format - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat; - - // Get the stream format, to be able to read the number of channels. - AudioStreamBasicDescription streamFormat; - size = sizeof(AudioStreamBasicDescription); - memset(&streamFormat, 0, size); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &streamFormat)); - - _noInputChannels = streamFormat.mChannelsPerFrame; - - return 0; -} - -bool AudioMixerManagerMac::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - return (_outputDeviceID != kAudioObjectUnknown); -} - -bool AudioMixerManagerMac::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", - __FUNCTION__); - - return (_inputDeviceID != kAudioObjectUnknown); -} - -WebRtc_Word32 AudioMixerManagerMac::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::SetSpeakerVolume(volume=%u)", volume); - - CriticalSectionScoped lock(_critSect); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - bool success = false; - - // volume range is 0.0 - 1.0, convert from 0 -255 - const Float32 vol = (Float32)(volume / 255.0); - - assert(vol <= 1.0 && vol >= 0.0); - - // Does the capture device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, - 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, size, &vol)); - - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, size, &vol)); - } - success = true; - } - - if (!success) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to set a volume on any output channel"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - unsigned int channels = 0; - Float32 channelVol = 0; - Float32 vol = 0; - - // Does the device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, - 0 }; - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &vol)); - - // vol 0.0 to 1.0 -> convert to 0 - 255 - volume = static_cast (vol * 255 + 0.5); - } else - { - // Otherwise get the average volume across channels. - vol = 0; - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - channelVol = 0; - propertyAddress.mElement = i; - hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(channelVol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &channelVol)); - - vol += channelVol; - channels++; - } - } - - if (channels == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to get a volume on any channel"); - return -1; - } - - assert(channels > 0); - // vol 0.0 to 1.0 -> convert to 0 - 255 - volume = static_cast (255 * vol / channels + 0.5); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerMac::SpeakerVolume() => vol=%i", vol); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 255 - maxVolume = 255; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 255 - minVolume = 0; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 255 - stepSize = 1; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SpeakerVolumeIsAvailable(bool& available) -{ - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - - // Does the capture device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { - kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, - 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - available = true; - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err != noErr || !isSettable) - { - available = false; - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Volume cannot be set for output channel %d, err=%d", - i, err); - return -1; - } - } - - available = true; - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available) -{ - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - - // Does the capture device have a master mute control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeOutput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - available = true; - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err != noErr || !isSettable) - { - available = false; - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Mute cannot be set for output channel %d, err=%d", - i, err); - return -1; - } - } - - available = true; - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::SetSpeakerMute(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - UInt32 mute = enable ? 1 : 0; - bool success = false; - - // Does the render device have a master mute control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeOutput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(mute); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, size, &mute)); - - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(mute); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, size, &mute)); - } - success = true; - } - - if (!success) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to set mute on any input channel"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - unsigned int channels = 0; - UInt32 channelMuted = 0; - UInt32 muted = 0; - - // Does the device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeOutput, 0 }; - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(muted); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &muted)); - - // 1 means muted - enabled = static_cast (muted); - } else - { - // Otherwise check if all channels are muted. - for (UInt32 i = 1; i <= _noOutputChannels; i++) - { - muted = 0; - propertyAddress.mElement = i; - hasProperty = AudioObjectHasProperty(_outputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(channelMuted); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID, - &propertyAddress, 0, NULL, &size, &channelMuted)); - - muted = (muted && channelMuted); - channels++; - } - } - - if (channels == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to get mute for any channel"); - return -1; - } - - assert(channels > 0); - // 1 means muted - enabled = static_cast (muted); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerMac::SpeakerMute() => enabled=%d, enabled"); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available) -{ - if (_outputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - available = (_noOutputChannels == 2); - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available) -{ - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - available = (_noInputChannels == 2); - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available) -{ - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - - // Does the capture device have a master mute control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeInput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - available = true; - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err != noErr || !isSettable) - { - available = false; - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Mute cannot be set for output channel %d, err=%d", - i, err); - return -1; - } - } - - available = true; - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::SetMicrophoneMute(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - UInt32 mute = enable ? 1 : 0; - bool success = false; - - // Does the capture device have a master mute control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeInput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(mute); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, size, &mute)); - - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(mute); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, size, &mute)); - } - success = true; - } - - if (!success) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to set mute on any input channel"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - unsigned int channels = 0; - UInt32 channelMuted = 0; - UInt32 muted = 0; - - // Does the device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress propertyAddress = { kAudioDevicePropertyMute, - kAudioDevicePropertyScopeInput, 0 }; - Boolean hasProperty = AudioObjectHasProperty(_inputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(muted); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &muted)); - - // 1 means muted - enabled = static_cast (muted); - } else - { - // Otherwise check if all channels are muted. - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - muted = 0; - propertyAddress.mElement = i; - hasProperty = AudioObjectHasProperty(_inputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(channelMuted); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &channelMuted)); - - muted = (muted && channelMuted); - channels++; - } - } - - if (channels == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to get mute for any channel"); - return -1; - } - - assert(channels > 0); - // 1 means muted - enabled = static_cast (muted); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerMac::MicrophoneMute() => enabled=%d", - enabled); - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::MicrophoneBoostIsAvailable(bool& available) -{ - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - available = false; // No AudioObjectPropertySelector value for Mic Boost - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::SetMicrophoneBoost(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // Ensure that the selected microphone has a valid boost control. - bool available(false); - MicrophoneBoostIsAvailable(available); - if (!available) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " it is not possible to enable microphone boost"); - return -1; - } - - // It is assumed that the call above fails! - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // Microphone boost cannot be enabled on this platform! - enabled = false; - - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available) -{ - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - - // Does the capture device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - available = true; - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err != noErr || !isSettable) - { - available = false; - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Volume cannot be set for input channel %d, err=%d", - i, err); - return -1; - } - } - - available = true; - return 0; -} - -WebRtc_Word32 AudioMixerManagerMac::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioMixerManagerMac::SetMicrophoneVolume(volume=%u)", volume); - - CriticalSectionScoped lock(_critSect); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - bool success = false; - - // volume range is 0.0 - 1.0, convert from 0 - 255 - const Float32 vol = (Float32)(volume / 255.0); - - assert(vol <= 1.0 && vol >= 0.0); - - // Does the capture device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, 0 }; - Boolean isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, size, &vol)); - - return 0; - } - - // Otherwise try to set each channel. - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - propertyAddress.mElement = i; - isSettable = false; - err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress, - &isSettable); - if (err == noErr && isSettable) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, size, &vol)); - } - success = true; - } - - if (!success) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to set a level on any input channel"); - return -1; - } - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - OSStatus err = noErr; - UInt32 size = 0; - unsigned int channels = 0; - Float32 channelVol = 0; - Float32 vol = 0; - - // Does the device have a master volume control? - // If so, use it exclusively. - AudioObjectPropertyAddress - propertyAddress = { kAudioDevicePropertyVolumeScalar, - kAudioDevicePropertyScopeInput, 0 }; - Boolean hasProperty = AudioObjectHasProperty(_inputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(vol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &vol)); - - // vol 0.0 to 1.0 -> convert to 0 - 255 - volume = static_cast (vol * 255 + 0.5); - } else - { - // Otherwise get the average volume across channels. - vol = 0; - for (UInt32 i = 1; i <= _noInputChannels; i++) - { - channelVol = 0; - propertyAddress.mElement = i; - hasProperty = AudioObjectHasProperty(_inputDeviceID, - &propertyAddress); - if (hasProperty) - { - size = sizeof(channelVol); - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID, - &propertyAddress, 0, NULL, &size, &channelVol)); - - vol += channelVol; - channels++; - } - } - - if (channels == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " Unable to get a level on any channel"); - return -1; - } - - assert(channels > 0); - // vol 0.0 to 1.0 -> convert to 0 - 255 - volume = static_cast (255 * vol / channels + 0.5); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " AudioMixerManagerMac::MicrophoneVolume() => vol=%i", - vol); - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 255 - maxVolume = 255; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 10 - minVolume = 0; - - return 0; -} - -WebRtc_Word32 -AudioMixerManagerMac::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, - "%s", __FUNCTION__); - - if (_inputDeviceID == kAudioObjectUnknown) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " device ID has not been set"); - return -1; - } - - // volume range is 0.0 to 1.0 - // we convert that to 0 - 10 - stepSize = 1; - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -// CoreAudio errors are best interpreted as four character strings. -void AudioMixerManagerMac::logCAMsg(const TraceLevel level, - const TraceModule module, - const WebRtc_Word32 id, const char *msg, - const char *err) -{ - assert(msg != NULL); - assert(err != NULL); - -#ifdef WEBRTC_BIG_ENDIAN - WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err); -#else - // We need to flip the characters in this case. - WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err - + 2, err + 1, err); -#endif -} - -} // namespace webrtc -// EOF diff --git a/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.h b/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.h deleted file mode 100644 index 7209f9122..000000000 --- a/modules/audio_device/main/source/Mac/audio_mixer_manager_mac.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_MAC_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_MAC_H - -#include "typedefs.h" -#include "audio_device.h" -#include "critical_section_wrapper.h" - -#include - -namespace webrtc { - -class AudioMixerManagerMac -{ -public: - WebRtc_Word32 OpenSpeaker(AudioDeviceID deviceID); - WebRtc_Word32 OpenMicrophone(AudioDeviceID deviceID); - WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - WebRtc_Word32 SetSpeakerMute(bool enable); - WebRtc_Word32 SpeakerMute(bool& enabled) const; - WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneMute(bool enable); - WebRtc_Word32 MicrophoneMute(bool& enabled) const; - WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneBoost(bool enable); - WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 Close(); - WebRtc_Word32 CloseSpeaker(); - WebRtc_Word32 CloseMicrophone(); - bool SpeakerIsInitialized() const; - bool MicrophoneIsInitialized() const; - -public: - AudioMixerManagerMac(const WebRtc_Word32 id); - ~AudioMixerManagerMac(); - -private: - static void logCAMsg(const TraceLevel level, - const TraceModule module, - const WebRtc_Word32 id, const char *msg, - const char *err); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - - AudioDeviceID _inputDeviceID; - AudioDeviceID _outputDeviceID; - - WebRtc_UWord16 _noInputChannels; - WebRtc_UWord16 _noOutputChannels; - -}; - -} //namespace webrtc - -#endif // AUDIO_MIXER_MAC_H diff --git a/modules/audio_device/main/source/Mac/portaudio/pa_memorybarrier.h b/modules/audio_device/main/source/Mac/portaudio/pa_memorybarrier.h deleted file mode 100644 index f68962220..000000000 --- a/modules/audio_device/main/source/Mac/portaudio/pa_memorybarrier.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * $Id: pa_memorybarrier.h 1240 2007-07-17 13:05:07Z bjornroche $ - * Portable Audio I/O Library - * Memory barrier utilities - * - * Author: Bjorn Roche, XO Audio, LLC - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** - @file pa_memorybarrier.h - @ingroup common_src -*/ - -/**************** - * Some memory barrier primitives based on the system. - * right now only OS X, FreeBSD, and Linux are supported. In addition to providing - * memory barriers, these functions should ensure that data cached in registers - * is written out to cache where it can be snooped by other CPUs. (ie, the volatile - * keyword should not be required) - * - * the primitives that must be defined are: - * - * PaUtil_FullMemoryBarrier() - * PaUtil_ReadMemoryBarrier() - * PaUtil_WriteMemoryBarrier() - * - ****************/ - -#if defined(__APPLE__) -# include - /* Here are the memory barrier functions. Mac OS X only provides - full memory barriers, so the three types of barriers are the same, - however, these barriers are superior to compiler-based ones. */ -# define PaUtil_FullMemoryBarrier() OSMemoryBarrier() -# define PaUtil_ReadMemoryBarrier() OSMemoryBarrier() -# define PaUtil_WriteMemoryBarrier() OSMemoryBarrier() -#elif defined(__GNUC__) - /* GCC >= 4.1 has built-in intrinsics. We'll use those */ -# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) -# define PaUtil_FullMemoryBarrier() __sync_synchronize() -# define PaUtil_ReadMemoryBarrier() __sync_synchronize() -# define PaUtil_WriteMemoryBarrier() __sync_synchronize() - /* as a fallback, GCC understands volatile asm and "memory" to mean it - * should not reorder memory read/writes */ - /* Note that it is not clear that any compiler actually defines __PPC__, - * it can probably removed safely. */ -# elif defined( __ppc__ ) || defined( __powerpc__) || defined( __PPC__ ) -# define PaUtil_FullMemoryBarrier() asm volatile("sync":::"memory") -# define PaUtil_ReadMemoryBarrier() asm volatile("sync":::"memory") -# define PaUtil_WriteMemoryBarrier() asm volatile("sync":::"memory") -# elif defined( __i386__ ) || defined( __i486__ ) || defined( __i586__ ) || \ - defined( __i686__ ) || defined( __x86_64__ ) -# define PaUtil_FullMemoryBarrier() asm volatile("mfence":::"memory") -# define PaUtil_ReadMemoryBarrier() asm volatile("lfence":::"memory") -# define PaUtil_WriteMemoryBarrier() asm volatile("sfence":::"memory") -# else -# ifdef ALLOW_SMP_DANGERS -# warning Memory barriers not defined on this system or system unknown -# warning For SMP safety, you should fix this. -# define PaUtil_FullMemoryBarrier() -# define PaUtil_ReadMemoryBarrier() -# define PaUtil_WriteMemoryBarrier() -# else -# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed. -# endif -# endif -#elif (_MSC_VER >= 1400) && !defined(_WIN32_WCE) -# include -# pragma intrinsic(_ReadWriteBarrier) -# pragma intrinsic(_ReadBarrier) -# pragma intrinsic(_WriteBarrier) -# define PaUtil_FullMemoryBarrier() _ReadWriteBarrier() -# define PaUtil_ReadMemoryBarrier() _ReadBarrier() -# define PaUtil_WriteMemoryBarrier() _WriteBarrier() -#elif defined(_WIN32_WCE) -# define PaUtil_FullMemoryBarrier() -# define PaUtil_ReadMemoryBarrier() -# define PaUtil_WriteMemoryBarrier() -#elif defined(_MSC_VER) || defined(__BORLANDC__) -# define PaUtil_FullMemoryBarrier() _asm { lock add [esp], 0 } -# define PaUtil_ReadMemoryBarrier() _asm { lock add [esp], 0 } -# define PaUtil_WriteMemoryBarrier() _asm { lock add [esp], 0 } -#else -# ifdef ALLOW_SMP_DANGERS -# warning Memory barriers not defined on this system or system unknown -# warning For SMP safety, you should fix this. -# define PaUtil_FullMemoryBarrier() -# define PaUtil_ReadMemoryBarrier() -# define PaUtil_WriteMemoryBarrier() -# else -# error Memory barriers are not defined on this system. You can still compile by defining ALLOW_SMP_DANGERS, but SMP safety will not be guaranteed. -# endif -#endif diff --git a/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.c b/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.c deleted file mode 100644 index 310d719f2..000000000 --- a/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * $Id: pa_ringbuffer.c 1421 2009-11-18 16:09:05Z bjornroche $ - * Portable Audio I/O Library - * Ring Buffer utility. - * - * Author: Phil Burk, http://www.softsynth.com - * modified for SMP safety on Mac OS X by Bjorn Roche - * modified for SMP safety on Linux by Leland Lucius - * also, allowed for const where possible - * modified for multiple-byte-sized data elements by Sven Fischer - * - * Note that this is safe only for a single-thread reader and a - * single-thread writer. - * - * This program uses the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** - @file - @ingroup common_src -*/ - -#include -#include -#include -#include "pa_ringbuffer.h" -#include -#include "pa_memorybarrier.h" - -/*************************************************************************** - * Initialize FIFO. - * elementCount must be power of 2, returns -1 if not. - */ -ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr ) -{ - if( ((elementCount-1) & elementCount) != 0) return -1; /* Not Power of two. */ - rbuf->bufferSize = elementCount; - rbuf->buffer = (char *)dataPtr; - PaUtil_FlushRingBuffer( rbuf ); - rbuf->bigMask = (elementCount*2)-1; - rbuf->smallMask = (elementCount)-1; - rbuf->elementSizeBytes = elementSizeBytes; - return 0; -} - -/*************************************************************************** -** Return number of elements available for reading. */ -ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf ) -{ - PaUtil_ReadMemoryBarrier(); - return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); -} -/*************************************************************************** -** Return number of elements available for writing. */ -ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf ) -{ - /* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */ - return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf)); -} - -/*************************************************************************** -** Clear buffer. Should only be called when buffer is NOT being read. */ -void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf ) -{ - rbuf->writeIndex = rbuf->readIndex = 0; -} - -/*************************************************************************** -** Get address of region(s) to which we can write data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or elementCount, whichever is smaller. -*/ -ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, - void **dataPtr1, ring_buffer_size_t *sizePtr1, - void **dataPtr2, ring_buffer_size_t *sizePtr2 ) -{ - ring_buffer_size_t index; - ring_buffer_size_t available = PaUtil_GetRingBufferWriteAvailable( rbuf ); - if( elementCount > available ) elementCount = available; - /* Check to see if write is not contiguous. */ - index = rbuf->writeIndex & rbuf->smallMask; - if( (index + elementCount) > rbuf->bufferSize ) - { - /* Write data in two blocks that wrap the buffer. */ - ring_buffer_size_t firstHalf = rbuf->bufferSize - index; - *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; - *sizePtr1 = firstHalf; - *dataPtr2 = &rbuf->buffer[0]; - *sizePtr2 = elementCount - firstHalf; - } - else - { - *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; - *sizePtr1 = elementCount; - *dataPtr2 = NULL; - *sizePtr2 = 0; - } - return elementCount; -} - - -/*************************************************************************** -*/ -ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) -{ - /* we need to ensure that previous writes are seen before we update the write index */ - PaUtil_WriteMemoryBarrier(); - return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask; -} - -/*************************************************************************** -** Get address of region(s) from which we can read data. -** If the region is contiguous, size2 will be zero. -** If non-contiguous, size2 will be the size of second region. -** Returns room available to be written or elementCount, whichever is smaller. -*/ -ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, - void **dataPtr1, ring_buffer_size_t *sizePtr1, - void **dataPtr2, ring_buffer_size_t *sizePtr2 ) -{ - ring_buffer_size_t index; - ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); - if( elementCount > available ) elementCount = available; - /* Check to see if read is not contiguous. */ - index = rbuf->readIndex & rbuf->smallMask; - if( (index + elementCount) > rbuf->bufferSize ) - { - /* Write data in two blocks that wrap the buffer. */ - ring_buffer_size_t firstHalf = rbuf->bufferSize - index; - *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; - *sizePtr1 = firstHalf; - *dataPtr2 = &rbuf->buffer[0]; - *sizePtr2 = elementCount - firstHalf; - } - else - { - *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; - *sizePtr1 = elementCount; - *dataPtr2 = NULL; - *sizePtr2 = 0; - } - return elementCount; -} -/*************************************************************************** -*/ -ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) -{ - /* we need to ensure that previous writes are always seen before updating the index. */ - PaUtil_WriteMemoryBarrier(); - return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask; -} - -/*************************************************************************** -** Return elements written. */ -ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount ) -{ - ring_buffer_size_t size1, size2, numWritten; - void *data1, *data2; - numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); - if( size2 > 0 ) - { - - memcpy( data1, data, size1*rbuf->elementSizeBytes ); - data = ((char *)data) + size1*rbuf->elementSizeBytes; - memcpy( data2, data, size2*rbuf->elementSizeBytes ); - } - else - { - memcpy( data1, data, size1*rbuf->elementSizeBytes ); - } - PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten ); - return numWritten; -} - -/*************************************************************************** -** Return elements read. */ -ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount ) -{ - ring_buffer_size_t size1, size2, numRead; - void *data1, *data2; - numRead = PaUtil_GetRingBufferReadRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); - if( size2 > 0 ) - { - memcpy( data, data1, size1*rbuf->elementSizeBytes ); - data = ((char *)data) + size1*rbuf->elementSizeBytes; - memcpy( data, data2, size2*rbuf->elementSizeBytes ); - } - else - { - memcpy( data, data1, size1*rbuf->elementSizeBytes ); - } - PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead ); - return numRead; -} diff --git a/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.h b/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.h deleted file mode 100644 index 393f6f8c5..000000000 --- a/modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.h +++ /dev/null @@ -1,233 +0,0 @@ -#ifndef WEBRTC_AUDIO_DEVICE_PA_RINGBUFFER_H -#define WEBRTC_AUDIO_DEVICE_PA_RINGBUFFER_H -/* - * $Id: pa_ringbuffer.h 1421 2009-11-18 16:09:05Z bjornroche $ - * Portable Audio I/O Library - * Ring Buffer utility. - * - * Author: Phil Burk, http://www.softsynth.com - * modified for SMP safety on OS X by Bjorn Roche. - * also allowed for const where possible. - * modified for multiple-byte-sized data elements by Sven Fischer - * - * Note that this is safe only for a single-thread reader - * and a single-thread writer. - * - * This program is distributed with the PortAudio Portable Audio Library. - * For more information see: http://www.portaudio.com - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/** @file - @ingroup common_src - @brief Single-reader single-writer lock-free ring buffer - - PaUtilRingBuffer is a ring buffer used to transport samples between - different execution contexts (threads, OS callbacks, interrupt handlers) - without requiring the use of any locks. This only works when there is - a single reader and a single writer (ie. one thread or callback writes - to the ring buffer, another thread or callback reads from it). - - The PaUtilRingBuffer structure manages a ring buffer containing N - elements, where N must be a power of two. An element may be any size - (specified in bytes). - - The memory area used to store the buffer elements must be allocated by - the client prior to calling PaUtil_InitializeRingBuffer() and must outlive - the use of the ring buffer. -*/ - -#if defined(__APPLE__) -#include -typedef int32_t ring_buffer_size_t; -#elif defined( __GNUC__ ) -typedef long ring_buffer_size_t; -#elif (_MSC_VER >= 1400) -typedef long ring_buffer_size_t; -#elif defined(_MSC_VER) || defined(__BORLANDC__) -typedef long ring_buffer_size_t; -#else -typedef long ring_buffer_size_t; -#endif - - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -typedef struct PaUtilRingBuffer -{ - ring_buffer_size_t bufferSize; /**< Number of elements in FIFO. Power of 2. Set by PaUtil_InitRingBuffer. */ - ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */ - ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */ - ring_buffer_size_t bigMask; /**< Used for wrapping indices with extra bit to distinguish full/empty. */ - ring_buffer_size_t smallMask; /**< Used for fitting indices to buffer. */ - ring_buffer_size_t elementSizeBytes; /**< Number of bytes per element. */ - char *buffer; /**< Pointer to the buffer containing the actual data. */ -}PaUtilRingBuffer; - -/** Initialize Ring Buffer. - - @param rbuf The ring buffer. - - @param elementSizeBytes The size of a single data element in bytes. - - @param elementCount The number of elements in the buffer (must be power of 2). - - @param dataPtr A pointer to a previously allocated area where the data - will be maintained. It must be elementCount*elementSizeBytes long. - - @return -1 if elementCount is not a power of 2, otherwise 0. -*/ -ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr ); - -/** Clear buffer. Should only be called when buffer is NOT being read. - - @param rbuf The ring buffer. -*/ -void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf ); - -/** Retrieve the number of elements available in the ring buffer for writing. - - @param rbuf The ring buffer. - - @return The number of elements available for writing. -*/ -ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf ); - -/** Retrieve the number of elements available in the ring buffer for reading. - - @param rbuf The ring buffer. - - @return The number of elements available for reading. -*/ -ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf ); - -/** Write data to the ring buffer. - - @param rbuf The ring buffer. - - @param data The address of new data to write to the buffer. - - @param elementCount The number of elements to be written. - - @return The number of elements written. -*/ -ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount ); - -/** Read data from the ring buffer. - - @param rbuf The ring buffer. - - @param data The address where the data should be stored. - - @param elementCount The number of elements to be read. - - @return The number of elements read. -*/ -ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount ); - -/** Get address of region(s) to which we can write data. - - @param rbuf The ring buffer. - - @param elementCount The number of elements desired. - - @param dataPtr1 The address where the first (or only) region pointer will be - stored. - - @param sizePtr1 The address where the first (or only) region length will be - stored. - - @param dataPtr2 The address where the second region pointer will be stored if - the first region is too small to satisfy elementCount. - - @param sizePtr2 The address where the second region length will be stored if - the first region is too small to satisfy elementCount. - - @return The room available to be written or elementCount, whichever is smaller. -*/ -ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, - void **dataPtr1, ring_buffer_size_t *sizePtr1, - void **dataPtr2, ring_buffer_size_t *sizePtr2 ); - -/** Advance the write index to the next location to be written. - - @param rbuf The ring buffer. - - @param elementCount The number of elements to advance. - - @return The new position. -*/ -ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ); - -/** Get address of region(s) from which we can write data. - - @param rbuf The ring buffer. - - @param elementCount The number of elements desired. - - @param dataPtr1 The address where the first (or only) region pointer will be - stored. - - @param sizePtr1 The address where the first (or only) region length will be - stored. - - @param dataPtr2 The address where the second region pointer will be stored if - the first region is too small to satisfy elementCount. - - @param sizePtr2 The address where the second region length will be stored if - the first region is too small to satisfy elementCount. - - @return The number of elements available for reading. -*/ -ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, - void **dataPtr1, ring_buffer_size_t *sizePtr1, - void **dataPtr2, ring_buffer_size_t *sizePtr2 ); - -/** Advance the read index to the next location to be read. - - @param rbuf The ring buffer. - - @param elementCount The number of elements to advance. - - @return The new position. -*/ -ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* MODULES_AUDIO_DEVICE_MAIN_SOURCE_MAC_PORTAUDIO_PA_RINGBUFFER_H_ */ diff --git a/modules/audio_device/main/source/Windows/audio_device_utility_windows.cc b/modules/audio_device/main/source/Windows/audio_device_utility_windows.cc deleted file mode 100644 index 2e816c902..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_utility_windows.cc +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_utility_windows.h" -#include "audio_device_config.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -#include -#include -#include - -#define STRING_MAX_SIZE 256 - -typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); -typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); - -namespace webrtc -{ - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceUtilityWindows() - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceUtilityWindows::AudioDeviceUtilityWindows(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _lastError(AudioDeviceModule::kAdmErrNone) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceUtilityWindows() - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceUtilityWindows::~AudioDeviceUtilityWindows() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - - // free stuff here... - } - - delete &_critSect; -} - -// ============================================================================ -// API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// Init() -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceUtilityWindows::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - TCHAR szOS[STRING_MAX_SIZE]; - - if (GetOSDisplayString(szOS)) - { -#ifdef _UNICODE - char os[STRING_MAX_SIZE]; - if (WideCharToMultiByte(CP_UTF8, 0, szOS, -1, os, STRING_MAX_SIZE, NULL, NULL) == 0) - { - DWORD err = GetLastError(); - sprintf(os, "Could not get OS info"); - } - // DEBUG_PRINTP("OS info: %s\n", os); - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, " OS info: %s", os); -#else - // DEBUG_PRINTP("OS info: %s\n", szOS); - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, " OS info: %s", szOS); -#endif - } - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -BOOL AudioDeviceUtilityWindows::GetOSDisplayString(LPTSTR pszOS) -{ - OSVERSIONINFOEX osvi; - SYSTEM_INFO si; - PGNSI pGNSI; - BOOL bOsVersionInfoEx; - - ZeroMemory(&si, sizeof(SYSTEM_INFO)); - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - // Retrieve information about the current operating system - // - if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) - return FALSE; - - // Parse our OS version string - // - if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && osvi.dwMajorVersion > 4) - { - StringCchCopy(pszOS, STRING_MAX_SIZE, TEXT("Microsoft ")); - - // Test for the specific product - // - // Operating system Version number - // -------------------------------------- - // Windows 7 6.1 - // Windows Server 2008 R2 6.1 - // Windows Server 2008 6.0 - // Windows Vista 6.0 - // - - - - - - - - - - - - - - - - - - - - // Windows Server 2003 R2 5.2 - // Windows Server 2003 5.2 - // Windows XP 5.1 - // Windows 2000 5.0 - // - // see http://msdn.microsoft.com/en-us/library/ms724832(VS.85).aspx for details - // - if (osvi.dwMajorVersion == 6) - { - if (osvi.dwMinorVersion == 0) - { - // Windows Vista or Server 2008 - if (osvi.wProductType == VER_NT_WORKSTATION) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows Vista ")); - else - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows Server 2008 " )); - } - - if (osvi.dwMinorVersion == 1) - { - // Windows 7 or Server 2008 R2 - if (osvi.wProductType == VER_NT_WORKSTATION) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows 7 ")); - else - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows Server 2008 R2 " )); - } - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - { - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows Server 2003")); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) - { - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows XP ")); - if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Home Edition" )); - else - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Professional" )); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - { - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT("Windows 2000 ")); - - if (osvi.wProductType == VER_NT_WORKSTATION ) - { - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Professional" )); - } - else - { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Datacenter Server" )); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Advanced Server" )); - else StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( "Server" )); - } - } - - // Include service pack (if any) - // - if (_tcslen(osvi.szCSDVersion) > 0) - { - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT(" ")); - StringCchCat(pszOS, STRING_MAX_SIZE, osvi.szCSDVersion); - } - - TCHAR buf[80]; - - // Include build number - // - StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); - StringCchCat(pszOS, STRING_MAX_SIZE, buf); - - // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise - // - pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); - if (NULL != pGNSI) - pGNSI(&si); - else - GetSystemInfo(&si); - - // Add 64-bit or 32-bit for OS versions "later than" Vista - // - if (osvi.dwMajorVersion >= 6) - { - if ((si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) || - (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT( ", 64-bit" )); - else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ) - StringCchCat(pszOS, STRING_MAX_SIZE, TEXT(", 32-bit")); - } - - return TRUE; - } - else - { - return FALSE; - } -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Windows/audio_device_utility_windows.h b/modules/audio_device/main/source/Windows/audio_device_utility_windows.h deleted file mode 100644 index 6d0a1dfa7..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_utility_windows.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_WINDOWS_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_WINDOWS_H - -#include "audio_device_utility.h" -#include "audio_device.h" -#include - -namespace webrtc -{ -class CriticalSectionWrapper; - -class AudioDeviceUtilityWindows : public AudioDeviceUtility -{ -public: - AudioDeviceUtilityWindows(const WebRtc_Word32 id); - ~AudioDeviceUtilityWindows(); - - virtual WebRtc_Word32 Init(); - -private: - BOOL GetOSDisplayString(LPTSTR pszOS); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - AudioDeviceModule::ErrorCode _lastError; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_WINDOWS_H diff --git a/modules/audio_device/main/source/Windows/audio_device_windows_core.cc b/modules/audio_device/main/source/Windows/audio_device_windows_core.cc deleted file mode 100644 index 37c791161..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_windows_core.cc +++ /dev/null @@ -1,4473 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#pragma warning(disable: 4995) // name was marked as #pragma deprecated - -#if (_MSC_VER >= 1310) && (_MSC_VER < 1400) -// Reports the major and minor versions of the compiler. -// For example, 1310 for Microsoft Visual C++ .NET 2003. 1310 represents version 13 and a 1.0 point release. -// The Visual C++ 2005 compiler version is 1400. -// Type cl /? at the command line to see the major and minor versions of your compiler along with the build number. -#pragma message(">> INFO: Windows Core Audio is not supported in VS 2003") -#endif - -#include "audio_device_config.h" - -#if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) -#pragma message(">> INFO: WEBRTC_WINDOWS_CORE_AUDIO_BUILD is defined") -#else -#pragma message(">> INFO: WEBRTC_WINDOWS_CORE_AUDIO_BUILD is *not* defined") -#endif - -#ifdef WEBRTC_WINDOWS_CORE_AUDIO_BUILD - -#include "audio_device_utility.h" -#include "audio_device_windows_core.h" -#include "trace.h" - -#include -#include -#include - -#include -#include "Functiondiscoverykeys_devpkey.h" -#include - - -// Macro that calls a COM method returning HRESULT value. -#define EXIT_ON_ERROR(hres) do { if (FAILED(hres)) goto Exit; } while(0) - -// Macro that releases a COM object if not NULL. -#define SAFE_RELEASE(p) do { if ((p)) { (p)->Release(); (p) = NULL; } } while(0) - -#define ROUND(x) ((x) >=0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) - -// REFERENCE_TIME time units per millisecond -#define REFTIMES_PER_MILLISEC 10000 - -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero -} THREADNAME_INFO; - -enum { COM_THREADING_MODEL = COINIT_MULTITHREADED }; - -namespace webrtc { - -// ============================================================================ -// Static Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// CoreAudioIsSupported -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::CoreAudioIsSupported() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, "%s", __FUNCTION__); - - bool MMDeviceIsAvailable(false); - bool coreAudioIsSupported(false); - bool coUninitializeIsRequired(true); - - HRESULT hr(S_OK); - TCHAR buf[MAXERRORLENGTH]; - LPCTSTR errorText; - - // 1) Initialize the COM library (make Windows load the DLLs). - // - // CoInitializeEx must be called at least once, and is usually called only once, - // for each thread that uses the COM library. Multiple calls to CoInitializeEx - // by the same thread are allowed as long as they pass the same concurrency flag, - // but subsequent valid calls return S_FALSE. - // To close the COM library gracefully on a thread, each successful call to - // CoInitializeEx, including any call that returns S_FALSE, must be balanced - // by a corresponding call to CoUninitialize. - // - hr = CoInitializeEx(NULL, COM_THREADING_MODEL); - if (FAILED(hr)) - { - // Avoid calling CoUninitialize() since CoInitializeEx() failed. - coUninitializeIsRequired = false; - - if (RPC_E_CHANGED_MODE == hr) - { - // Calling thread has already initialized COM to be used in a single-threaded - // apartment (STA). We are then prevented from using MTA. - // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is set". - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() CoInitializeEx(NULL, COM_THREADING_MODEL) => RPC_E_CHANGED_MODE"); - } - _com_error error(hr); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() Failed to initialize the COM library", hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() CoInitializeEx(COM_THREADING_MODEL) failed (hr=0x%x)", hr); - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - errorText = error.ErrorMessage(); - StringCchCat(buf, MAXERRORLENGTH, errorText); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, "%s", buf); - } - - // ...it is OK to enter this scope even if CoInitializeEx() failed - - // 2) Check if the MMDevice API is available. - // - // The Windows Multimedia Device (MMDevice) API enables audio clients to - // discover audio endpoint devices, determine their capabilities, and create - // driver instances for those devices. - // Header file Mmdeviceapi.h defines the interfaces in the MMDevice API. - // The MMDevice API consists of several interfaces. The first of these is the - // IMMDeviceEnumerator interface. To access the interfaces in the MMDevice API, - // a client obtains a reference to the IMMDeviceEnumerator interface of a - // device-enumerator object by calling the CoCreateInstance function. - // - // Through the IMMDeviceEnumerator interface, the client can obtain references - // to the other interfaces in the MMDevice API. The MMDevice API implements - // the following interfaces: - // - // IMMDevice Represents an audio device. - // IMMDeviceCollection Represents a collection of audio devices. - // IMMDeviceEnumerator Provides methods for enumerating audio devices. - // IMMEndpoint Represents an audio endpoint device. - // - IMMDeviceEnumerator* pIMMD(NULL); - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); - - hr = CoCreateInstance( - CLSID_MMDeviceEnumerator, // GUID value of MMDeviceEnumerator coclass - NULL, - CLSCTX_ALL, - IID_IMMDeviceEnumerator, // GUID value of the IMMDeviceEnumerator interface - (void**)&pIMMD ); - - if (FAILED(hr)) - { - _com_error error(hr); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() Failed to create the required COM object", hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() CoCreateInstance(MMDeviceEnumerator) failed (hr=0x%x)", hr); - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - errorText = error.ErrorMessage(); - StringCchCat(buf, MAXERRORLENGTH, errorText); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, "%s", buf); - } - else - { - MMDeviceIsAvailable = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() CoCreateInstance(MMDeviceEnumerator) succeeded", hr); - SAFE_RELEASE(pIMMD); - } - - // 3) Uninitialize COM but only if required. - // - // COM will be reinitialized again when the Core Audio ADM is created. - // - if (coUninitializeIsRequired) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, -1, "AudioDeviceWindowsCore::CoreAudioIsSupported() calls CoUninitialize()"); - CoUninitialize(); - } - - // 4) Verify that we can create and initialize our Core Audio class. - // - // Also, perform a limited "API test" to ensure that Core Audio is supported for all devices. - // - if (MMDeviceIsAvailable) - { - coreAudioIsSupported = false; - - AudioDeviceWindowsCore* p = new AudioDeviceWindowsCore(-1); - if (p == NULL) - { - return false; - } - - int ok(0); - int temp_ok(0); - bool available(false); - - ok |= p->Init(); - - WebRtc_Word16 numDevsRec = p->RecordingDevices(); - for (WebRtc_UWord16 i = 0; i < numDevsRec; i++) - { - ok |= p->SetRecordingDevice(i); - temp_ok = p->RecordingIsAvailable(available); - ok |= temp_ok; - ok |= (available == false); - if (available) - { - ok |= p->InitMicrophone(); - } - if (ok) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, - "AudioDeviceWindowsCore::CoreAudioIsSupported() Failed to use Core Audio Recording for device id=%i", i); - } - } - - WebRtc_Word16 numDevsPlay = p->PlayoutDevices(); - for (WebRtc_UWord16 i = 0; i < numDevsPlay; i++) - { - ok |= p->SetPlayoutDevice(i); - temp_ok = p->PlayoutIsAvailable(available); - ok |= temp_ok; - ok |= (available == false); - if (available) - { - ok |= p->InitSpeaker(); - } - if (ok) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1 , - "AudioDeviceWindowsCore::CoreAudioIsSupported() Failed to use Core Audio Playout for device id=%i", i); - } - } - - ok |= p->Terminate(); - - if (ok == 0) - { - coreAudioIsSupported = true; - } - - delete p; - } - - if (coreAudioIsSupported) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, "*** Windows Core Audio is supported ***"); - } - else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, "*** Windows Core Audio is NOT supported => will revert to the Wave API ***"); - } - - return (coreAudioIsSupported); -} - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceWindowsCore() - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceWindowsCore::AudioDeviceWindowsCore(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _volumeMutex(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _ptrAudioBuffer(NULL), - _ptrEnumerator(NULL), - _ptrRenderCollection(NULL), - _ptrCaptureCollection(NULL), - _ptrDeviceOut(NULL), - _ptrDeviceIn(NULL), - _ptrClientOut(NULL), - _ptrClientIn(NULL), - _ptrRenderClient(NULL), - _ptrCaptureClient(NULL), - _ptrCaptureVolume(NULL), - _ptrRenderSimpleVolume(NULL), - _ptrRenderEndpointVolume(NULL), - _playAudioFrameSize(0), - _playSampleRate(0), - _playBlockSize(0), - _playChannels(2), - _sndCardPlayDelay(0), - _sndCardRecDelay(0), - _sampleDriftAt48kHz(0), - _driftAccumulator(0), - _writtenSamples(0), - _readSamples(0), - _playAcc(0), - _recAudioFrameSize(0), - _recSampleRate(0), - _recBlockSize(0), - _recChannels(2), - _avrtLibrary(NULL), - _winSupportAvrt(false), - _hRenderSamplesReadyEvent(NULL), - _hPlayThread(NULL), - _hCaptureSamplesReadyEvent(NULL), - _hRecThread(NULL), - _hShutdownRenderEvent(NULL), - _hShutdownCaptureEvent(NULL), - _hRenderStartedEvent(NULL), - _hCaptureStartedEvent(NULL), - _hGetCaptureVolumeThread(NULL), - _hSetCaptureVolumeThread(NULL), - _hSetCaptureVolumeEvent(NULL), - _coUninitializeIsRequired(true), - _initialized(false), - _recording(false), - _playing(false), - _recIsInitialized(false), - _playIsInitialized(false), - _speakerIsInitialized(false), - _microphoneIsInitialized(false), - _AGC(false), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _playBufType(AudioDeviceModule::kAdaptiveBufferSize), - _playBufDelay(80), - _playBufDelayFixed(80), - _usingInputDeviceIndex(false), - _usingOutputDeviceIndex(false), - _inputDevice(AudioDeviceModule::kDefaultCommunicationDevice), - _outputDevice(AudioDeviceModule::kDefaultCommunicationDevice), - _inputDeviceIndex(0), - _outputDeviceIndex(0), - _newMicLevel(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); - - // Try to load the Avrt DLL - if (!_avrtLibrary) - { - // Get handle to the Avrt DLL module. - _avrtLibrary = LoadLibrary(TEXT("Avrt.dll")); - if (_avrtLibrary) - { - // Handle is valid (should only happen if OS larger than vista & win7). - // Try to get the function addresses. - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::AudioDeviceWindowsCore() The Avrt DLL module is now loaded"); - - _PAvRevertMmThreadCharacteristics = (PAvRevertMmThreadCharacteristics)GetProcAddress(_avrtLibrary, "AvRevertMmThreadCharacteristics"); - _PAvSetMmThreadCharacteristicsA = (PAvSetMmThreadCharacteristicsA)GetProcAddress(_avrtLibrary, "AvSetMmThreadCharacteristicsA"); - _PAvSetMmThreadPriority = (PAvSetMmThreadPriority)GetProcAddress(_avrtLibrary, "AvSetMmThreadPriority"); - - if ( _PAvRevertMmThreadCharacteristics && - _PAvSetMmThreadCharacteristicsA && - _PAvSetMmThreadPriority) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::AudioDeviceWindowsCore() AvRevertMmThreadCharacteristics() is OK"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::AudioDeviceWindowsCore() AvSetMmThreadCharacteristicsA() is OK"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::AudioDeviceWindowsCore() AvSetMmThreadPriority() is OK"); - _winSupportAvrt = true; - } - } - } - - // Create our samples ready events - we want auto reset events that start in the not-signaled state. - // The state of an auto-reset event object remains signaled until a single waiting thread is released, - // at which time the system automatically sets the state to nonsignaled. If no threads are waiting, - // the event object's state remains signaled. - // (Except for _hShutdownCaptureEvent, which is used to shutdown multiple threads). - _hRenderSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hCaptureSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hShutdownRenderEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hShutdownCaptureEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - _hRenderStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hCaptureStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hSetCaptureVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - - _perfCounterFreq.QuadPart = 1; - _perfCounterFactor = 0.0; - _avgCPULoad = 0.0; - - // list of number of channels to use on recording side - _recChannelsPrioList[0] = 2; // stereo is prio 1 - _recChannelsPrioList[1] = 1; // mono is prio 2 - - // list of number of channels to use on playout side - _playChannelsPrioList[0] = 2; // stereo is prio 1 - _playChannelsPrioList[1] = 1; // mono is prio 2 - - // Initialize the COM library - HRESULT hr = CoInitializeEx(NULL, COM_THREADING_MODEL); - if (FAILED(hr)) - { - _coUninitializeIsRequired = false; - if (hr == RPC_E_CHANGED_MODE) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioDeviceWindowsCore::AudioDeviceWindowsCore() CoInitializeEx(NULL, COM_THREADING_MODEL) => RPC_E_CHANGED_MODE"); - } - } - - // We know that this API will work since it has already been verified in - // CoreAudioIsSupported, hence no need to check for errors here as well. - - // Retrive the IMMDeviceEnumerator API (should load the MMDevAPI.dll) - CoCreateInstance( - __uuidof(MMDeviceEnumerator), - NULL, - CLSCTX_ALL, - __uuidof(IMMDeviceEnumerator), - (void**)&_ptrEnumerator); - - if (_coUninitializeIsRequired) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "AudioDeviceWindowsCore::AudioDeviceWindowsCore() matching call to CoUninitialize() is required"); - } - assert(NULL != _ptrEnumerator); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceWindowsCore() - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceWindowsCore::~AudioDeviceWindowsCore() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - - Terminate(); - - _ptrAudioBuffer = NULL; - - SAFE_RELEASE(_ptrEnumerator); - SAFE_RELEASE(_ptrRenderCollection); - SAFE_RELEASE(_ptrCaptureCollection); - SAFE_RELEASE(_ptrDeviceOut); - SAFE_RELEASE(_ptrDeviceIn); - SAFE_RELEASE(_ptrClientOut); - SAFE_RELEASE(_ptrClientIn); - SAFE_RELEASE(_ptrRenderClient); - SAFE_RELEASE(_ptrCaptureClient); - SAFE_RELEASE(_ptrCaptureVolume); - SAFE_RELEASE(_ptrRenderSimpleVolume); - SAFE_RELEASE(_ptrRenderEndpointVolume); - - if (_coUninitializeIsRequired) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::~AudioDeviceWindowsCore() calling CoUninitialize()..."); - CoUninitialize(); - } - - if (NULL != _hRenderSamplesReadyEvent) - { - CloseHandle(_hRenderSamplesReadyEvent); - _hRenderSamplesReadyEvent = NULL; - } - - if (NULL != _hCaptureSamplesReadyEvent) - { - CloseHandle(_hCaptureSamplesReadyEvent); - _hCaptureSamplesReadyEvent = NULL; - } - - if (NULL != _hRenderStartedEvent) - { - CloseHandle(_hRenderStartedEvent); - _hRenderStartedEvent = NULL; - } - - if (NULL != _hCaptureStartedEvent) - { - CloseHandle(_hCaptureStartedEvent); - _hCaptureStartedEvent = NULL; - } - - if (NULL != _hShutdownRenderEvent) - { - CloseHandle(_hShutdownRenderEvent); - _hShutdownRenderEvent = NULL; - } - - if (NULL != _hShutdownCaptureEvent) - { - CloseHandle(_hShutdownCaptureEvent); - _hShutdownCaptureEvent = NULL; - } - - if (NULL != _hSetCaptureVolumeEvent) - { - CloseHandle(_hSetCaptureVolumeEvent); - _hSetCaptureVolumeEvent = NULL; - } - - if (_avrtLibrary) - { - BOOL freeOK = FreeLibrary(_avrtLibrary); - if (!freeOK) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "AudioDeviceWindowsCore::~AudioDeviceWindowsCore() failed to free the loaded Avrt DLL module correctly"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "AudioDeviceWindowsCore::~AudioDeviceWindowsCore() the Avrt DLL module is now unloaded"); - } - } - - delete &_critSect; - delete &_volumeMutex; -} - -// ============================================================================ -// API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AttachAudioBuffer -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - _ptrAudioBuffer = audioBuffer; - - // Inform the AudioBuffer about default settings for this implementation. - // Set all values to zero here since the actual settings will be done by - // InitPlayout and InitRecording later. - _ptrAudioBuffer->SetRecordingSampleRate(0); - _ptrAudioBuffer->SetPlayoutSampleRate(0); - _ptrAudioBuffer->SetRecordingChannels(0); - _ptrAudioBuffer->SetPlayoutChannels(0); -} - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kWindowsCoreAudio; - return 0; -} - -// ---------------------------------------------------------------------------- -// Init -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - // Enumerate all audio rendering and capturing endpoint devices. - // Note that, some of these will not be able to select by the user. - // The complete collection is for internal use only. - // - _EnumerateEndpointDevicesAll(eRender); - _EnumerateEndpointDevicesAll(eCapture); - - _initialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Terminate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_initialized) - { - return 0; - } - - _initialized = false; - _speakerIsInitialized = false; - _microphoneIsInitialized = false; - _playing = false; - _recording = false; - - SAFE_RELEASE(_ptrRenderCollection); - SAFE_RELEASE(_ptrCaptureCollection); - SAFE_RELEASE(_ptrDeviceOut); - SAFE_RELEASE(_ptrDeviceIn); - SAFE_RELEASE(_ptrClientOut); - SAFE_RELEASE(_ptrClientIn); - SAFE_RELEASE(_ptrRenderClient); - SAFE_RELEASE(_ptrCaptureClient); - SAFE_RELEASE(_ptrCaptureVolume); - SAFE_RELEASE(_ptrRenderSimpleVolume); - SAFE_RELEASE(_ptrRenderEndpointVolume); - - return 0; -} - -// ---------------------------------------------------------------------------- -// Initialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_initialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - if (_usingOutputDeviceIndex) - { - WebRtc_Word16 nDevices = PlayoutDevices(); - if (_outputDeviceIndex > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "current device selection is invalid => unable to initialize"); - return -1; - } - } - - WebRtc_Word32 ret(0); - - SAFE_RELEASE(_ptrDeviceOut); - if (_usingOutputDeviceIndex) - { - // Refresh the selected rendering endpoint device using current index - ret = _GetListDevice(eRender, _outputDeviceIndex, &_ptrDeviceOut); - } - else - { - ERole role; - (_outputDevice == AudioDeviceModule::kDefaultDevice) ? role = eConsole : role = eCommunications; - // Refresh the selected rendering endpoint device using role - ret = _GetDefaultDevice(eRender, role, &_ptrDeviceOut); - } - - if (ret != 0 || (_ptrDeviceOut == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to initialize the rendering enpoint device"); - SAFE_RELEASE(_ptrDeviceOut); - return -1; - } - - ret = _ptrDeviceOut->Activate( - __uuidof(IAudioEndpointVolume), - CLSCTX_ALL, - NULL, - reinterpret_cast(&_ptrRenderEndpointVolume)); - if (ret != 0 || _ptrRenderEndpointVolume == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize the render endpoint volume"); - SAFE_RELEASE(_ptrRenderEndpointVolume); - return -1; - } - - IAudioSessionManager* pManager = NULL; - ret = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), - CLSCTX_ALL, - NULL, - (void**)&pManager); - if (ret != 0 || pManager == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize the render manager"); - SAFE_RELEASE(pManager); - return -1; - } - - ret = pManager->GetSimpleAudioVolume(NULL, FALSE, &_ptrRenderSimpleVolume); - if (ret != 0 || _ptrRenderSimpleVolume == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize the render simple volume"); - SAFE_RELEASE(pManager); - SAFE_RELEASE(_ptrRenderSimpleVolume); - return -1; - } - SAFE_RELEASE(pManager); - - - _speakerIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - available = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - if (_usingInputDeviceIndex) - { - WebRtc_Word16 nDevices = RecordingDevices(); - if (_inputDeviceIndex > (nDevices - 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "current device selection is invalid => unable to initialize"); - return -1; - } - } - - WebRtc_Word32 ret(0); - - SAFE_RELEASE(_ptrDeviceIn); - if (_usingInputDeviceIndex) - { - // Refresh the selected capture endpoint device using current index - ret = _GetListDevice(eCapture, _inputDeviceIndex, &_ptrDeviceIn); - } - else - { - ERole role; - (_inputDevice == AudioDeviceModule::kDefaultDevice) ? role = eConsole : role = eCommunications; - // Refresh the selected capture endpoint device using role - ret = _GetDefaultDevice(eCapture, role, &_ptrDeviceIn); - } - - if (ret != 0 || (_ptrDeviceIn == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to initialize the capturing enpoint device"); - SAFE_RELEASE(_ptrDeviceIn); - return -1; - } - - ret = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), - CLSCTX_ALL, - NULL, - reinterpret_cast(&_ptrCaptureVolume)); - if (ret != 0 || _ptrCaptureVolume == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to initialize the capture volume"); - SAFE_RELEASE(_ptrCaptureVolume); - return -1; - } - - _microphoneIsInitialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_speakerIsInitialized); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_microphoneIsInitialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioSessionManager* pManager = NULL; - ISimpleAudioVolume* pVolume = NULL; - - hr = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL, NULL, (void**)&pManager); - EXIT_ON_ERROR(hr); - - hr = pManager->GetSimpleAudioVolume(NULL, FALSE, &pVolume); - EXIT_ON_ERROR(hr); - - float volume(0.0f); - hr = pVolume->GetMasterVolume(&volume); - if (FAILED(hr)) - { - available = false; - } - available = true; - - SAFE_RELEASE(pManager); - SAFE_RELEASE(pVolume); - - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pManager); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetSpeakerVolume(volume=%u)", volume); - - { - CriticalSectionScoped lock(_critSect); - - if (!_speakerIsInitialized) - { - return -1; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - } - - if (volume < (WebRtc_UWord32)MIN_CORE_SPEAKER_VOLUME || - volume > (WebRtc_UWord32)MAX_CORE_SPEAKER_VOLUME) - { - return -1; - } - - HRESULT hr = S_OK; - - // scale input volume to valid range (0.0 to 1.0) - const float fLevel = (float)volume/MAX_CORE_SPEAKER_VOLUME; - _volumeMutex.Enter(); - hr = _ptrRenderSimpleVolume->SetMasterVolume(fLevel,NULL); - _volumeMutex.Leave(); - EXIT_ON_ERROR(hr); - - return 0; - -Exit: - _TraceCOMError(hr); - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - { - CriticalSectionScoped lock(_critSect); - - if (!_speakerIsInitialized) - { - return -1; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - } - - HRESULT hr = S_OK; - float fLevel(0.0f); - - _volumeMutex.Enter(); - hr = _ptrRenderSimpleVolume->GetMasterVolume(&fLevel); - _volumeMutex.Leave(); - EXIT_ON_ERROR(hr); - - // scale input volume range [0.0,1.0] to valid output range - volume = static_cast (fLevel*MAX_CORE_SPEAKER_VOLUME); - - return 0; - -Exit: - _TraceCOMError(hr); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - return -1; -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// -// The internal range for Core Audio is 0.0 to 1.0, where 0.0 indicates -// silence and 1.0 indicates full volume (no attenuation). -// We add our (webrtc-internal) own max level to match the Wave API and -// how it is used today in VoE. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - return -1; - } - - maxVolume = static_cast (MAX_CORE_SPEAKER_VOLUME); - - return 0; -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - return -1; - } - - minVolume = static_cast (MIN_CORE_SPEAKER_VOLUME); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - return -1; - } - - stepSize = CORE_SPEAKER_VOLUME_STEP_SIZE; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioSessionManager* pManager = NULL; - ISimpleAudioVolume* pVolume = NULL; - - hr = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL,NULL, (void**)&pManager); - EXIT_ON_ERROR(hr); - - hr = pManager->GetSimpleAudioVolume(NULL, FALSE, &pVolume); - EXIT_ON_ERROR(hr); - - BOOL mute; - hr = pVolume->GetMute(&mute); - if (FAILED(hr)) - { - available = false; - } - available = true; - - SAFE_RELEASE(pManager); - SAFE_RELEASE(pVolume); - - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pManager); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetSpeakerMute(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (!_speakerIsInitialized) - { - return -1; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - const BOOL mute(enable); - hr = pVolume->SetMute(mute, NULL); - EXIT_ON_ERROR(hr); - - SAFE_RELEASE(pVolume); - - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_speakerIsInitialized) - { - return -1; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - BOOL mute; - hr = pVolume->GetMute(&mute); - EXIT_ON_ERROR(hr); - - enabled = (mute == TRUE) ? true : false; - - SAFE_RELEASE(pVolume); - - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - BOOL mute; - hr = pVolume->GetMute(&mute); - if (FAILED(hr)) - { - available = false; - } - available = true; - - SAFE_RELEASE(pVolume); - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetMicrophoneMute(enable=%u)", enable); - - if (!_microphoneIsInitialized) - { - return -1; - } - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - const BOOL mute(enable); - hr = pVolume->SetMute(mute, NULL); - EXIT_ON_ERROR(hr); - - SAFE_RELEASE(pVolume); - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_microphoneIsInitialized) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - BOOL mute; - hr = pVolume->GetMute(&mute); - EXIT_ON_ERROR(hr); - - enabled = (mute == TRUE) ? true : false; - - SAFE_RELEASE(pVolume); - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetMicrophoneBoost(enable=%u)", enable); - - if (!_microphoneIsInitialized) - { - return -1; - } - - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_microphoneIsInitialized) - { - return -1; - } - - return -1; -} - -// ---------------------------------------------------------------------------- -// StereoRecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetStereoRecording(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (enable) - { - _recChannelsPrioList[0] = 2; // try stereo first - _recChannelsPrioList[1] = 1; - _recChannels = 2; - } - else - { - _recChannelsPrioList[0] = 1; // try mono first - _recChannelsPrioList[1] = 2; - _recChannels = 1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_recChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetStereoPlayout(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (enable) - { - _playChannelsPrioList[0] = 2; // try stereo first - _playChannelsPrioList[1] = 1; - _playChannels = 2; - } - else - { - _playChannelsPrioList[0] = 1; // try mono first - _playChannelsPrioList[1] = 2; - _playChannels = 1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_playChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetAGC -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetAGC(enable=%d)", enable); - CriticalSectionScoped lock(_critSect); - _AGC = enable; - return 0; -} - -// ---------------------------------------------------------------------------- -// AGC -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::AGC() const -{ - CriticalSectionScoped lock(_critSect); - return _AGC; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - IAudioEndpointVolume* pVolume = NULL; - - hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, reinterpret_cast(&pVolume)); - EXIT_ON_ERROR(hr); - - float volume(0.0f); - hr = pVolume->GetMasterVolumeLevelScalar(&volume); - if (FAILED(hr)) - { - available = false; - } - available = true; - - SAFE_RELEASE(pVolume); - return 0; - -Exit: - _TraceCOMError(hr); - SAFE_RELEASE(pVolume); - return -1; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetMicrophoneVolume(volume=%u)", volume); - - { - CriticalSectionScoped lock(_critSect); - - if (!_microphoneIsInitialized) - { - return -1; - } - - if (_ptrDeviceIn == NULL) - { - return -1; - } - } - - if (volume < static_cast(MIN_CORE_MICROPHONE_VOLUME) || - volume > static_cast(MAX_CORE_MICROPHONE_VOLUME)) - { - return -1; - } - - HRESULT hr = S_OK; - // scale input volume to valid range (0.0 to 1.0) - const float fLevel = static_cast(volume)/MAX_CORE_MICROPHONE_VOLUME; - _volumeMutex.Enter(); - _ptrCaptureVolume->SetMasterVolumeLevelScalar(fLevel, NULL); - _volumeMutex.Leave(); - EXIT_ON_ERROR(hr); - - return 0; - -Exit: - _TraceCOMError(hr); - return -1; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - { - CriticalSectionScoped lock(_critSect); - - if (!_microphoneIsInitialized) - { - return -1; - } - - if (_ptrDeviceIn == NULL) - { - return -1; - } - } - - HRESULT hr = S_OK; - float fLevel(0.0f); - volume = 0; - _volumeMutex.Enter(); - hr = _ptrCaptureVolume->GetMasterVolumeLevelScalar(&fLevel); - _volumeMutex.Leave(); - EXIT_ON_ERROR(hr); - - // scale input volume range [0.0,1.0] to valid output range - volume = static_cast (fLevel*MAX_CORE_MICROPHONE_VOLUME); - - return 0; - -Exit: - _TraceCOMError(hr); - return -1; -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// -// The internal range for Core Audio is 0.0 to 1.0, where 0.0 indicates -// silence and 1.0 indicates full volume (no attenuation). -// We add our (webrtc-internal) own max level to match the Wave API and -// how it is used today in VoE. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_microphoneIsInitialized) - { - return -1; - } - - maxVolume = static_cast (MAX_CORE_MICROPHONE_VOLUME); - - return 0; -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_microphoneIsInitialized) - { - return -1; - } - - minVolume = static_cast (MIN_CORE_MICROPHONE_VOLUME); - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_microphoneIsInitialized) - { - return -1; - } - - stepSize = CORE_MICROPHONE_VOLUME_STEP_SIZE; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceWindowsCore::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_RefreshDeviceList(eRender) != -1) - { - return (_DeviceListCount(eRender)); - } - - return -1; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - // Get current number of available rendering endpoint devices and refresh the rendering collection. - UINT nDevices = PlayoutDevices(); - - if (index < 0 || index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - CriticalSectionScoped lock(_critSect); - - HRESULT hr(S_OK); - - assert(_ptrRenderCollection != NULL); - - // Select an endpoint rendering device given the specified index - SAFE_RELEASE(_ptrDeviceOut); - hr = _ptrRenderCollection->Item( - index, - &_ptrDeviceOut); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(_ptrDeviceOut); - return -1; - } - - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (_GetDeviceName(_ptrDeviceOut, szDeviceName, bufferLen) == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "friendly name: \"%S\"", szDeviceName); - } - - _usingOutputDeviceIndex = true; - _outputDeviceIndex = index; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device) -{ - if (_playIsInitialized) - { - return -1; - } - - ERole role(eCommunications); - - if (device == AudioDeviceModule::kDefaultDevice) - { - role = eConsole; - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetPlayoutDevice(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - role = eCommunications; - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetPlayoutDevice(kDefaultCommunicationDevice)"); - } - - CriticalSectionScoped lock(_critSect); - - // Refresh the list of rendering endpoint devices - _RefreshDeviceList(eRender); - - HRESULT hr(S_OK); - - assert(_ptrEnumerator != NULL); - - // Select an endpoint rendering device given the specified role - SAFE_RELEASE(_ptrDeviceOut); - hr = _ptrEnumerator->GetDefaultAudioEndpoint( - eRender, - role, - &_ptrDeviceOut); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(_ptrDeviceOut); - return -1; - } - - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (_GetDeviceName(_ptrDeviceOut, szDeviceName, bufferLen) == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "friendly name: \"%S\"", szDeviceName); - } - - _usingOutputDeviceIndex = false; - _outputDevice = device; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::PlayoutDeviceName(index=%u)", index); - - bool defaultCommunicationDevice(false); - const WebRtc_Word16 nDevices(PlayoutDevices()); // also updates the list of devices - - // Special fix for the case when the user selects '-1' as index (<=> Default Communication Device) - if (index == (WebRtc_UWord16)(-1)) - { - defaultCommunicationDevice = true; - index = 0; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Default Communication endpoint device will be used"); - } - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - CriticalSectionScoped lock(_critSect); - - HRESULT hr(S_OK); - WebRtc_Word32 ret(-1); - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (defaultCommunicationDevice) - { - ret = _GetDefaultDeviceName(eRender, eCommunications, szDeviceName, bufferLen); - } - else - { - ret = _GetListDeviceName(eRender, index, szDeviceName, bufferLen); - } - - if (ret == 0) - { - // Convert the endpoint device's friendly-name to UTF-8 - if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d", GetLastError()); - } - } - - // Get the endpoint ID string (uniquely identifies the device among all audio endpoint devices) - if (defaultCommunicationDevice) - { - ret = _GetDefaultDeviceID(eRender, eCommunications, szDeviceName, bufferLen); - } - else - { - ret = _GetListDeviceID(eRender, index, szDeviceName, bufferLen); - } - - if (guid != NULL && ret == 0) - { - // Convert the endpoint device's ID string to UTF-8 - if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d", GetLastError()); - } - } - - return ret; -} - -// ---------------------------------------------------------------------------- -// RecordingDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::RecordingDeviceName(index=%u)", index); - - bool defaultCommunicationDevice(false); - const WebRtc_Word16 nDevices(RecordingDevices()); // also updates the list of devices - - // Special fix for the case when the user selects '-1' as index (<=> Default Communication Device) - if (index == (WebRtc_UWord16)(-1)) - { - defaultCommunicationDevice = true; - index = 0; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Default Communication endpoint device will be used"); - } - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - CriticalSectionScoped lock(_critSect); - - HRESULT hr(S_OK); - WebRtc_Word32 ret(-1); - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (defaultCommunicationDevice) - { - ret = _GetDefaultDeviceName(eCapture, eCommunications, szDeviceName, bufferLen); - } - else - { - ret = _GetListDeviceName(eCapture, index, szDeviceName, bufferLen); - } - - if (ret == 0) - { - // Convert the endpoint device's friendly-name to UTF-8 - if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d", GetLastError()); - } - } - - // Get the endpoint ID string (uniquely identifies the device among all audio endpoint devices) - if (defaultCommunicationDevice) - { - ret = _GetDefaultDeviceID(eCapture, eCommunications, szDeviceName, bufferLen); - } - else - { - ret = _GetListDeviceID(eCapture, index, szDeviceName, bufferLen); - } - - if (guid != NULL && ret == 0) - { - // Convert the endpoint device's ID string to UTF-8 - if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d", GetLastError()); - } - } - - return ret; -} - -// ---------------------------------------------------------------------------- -// RecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceWindowsCore::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_RefreshDeviceList(eCapture) != -1) - { - return (_DeviceListCount(eCapture)); - } - - return -1; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - // Get current number of available capture endpoint devices and refresh the capture collection. - UINT nDevices = RecordingDevices(); - - if (index < 0 || index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - CriticalSectionScoped lock(_critSect); - - HRESULT hr(S_OK); - - assert(_ptrCaptureCollection != NULL); - - // Select an endpoint capture device given the specified index - SAFE_RELEASE(_ptrDeviceIn); - hr = _ptrCaptureCollection->Item( - index, - &_ptrDeviceIn); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(_ptrDeviceIn); - return -1; - } - - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (_GetDeviceName(_ptrDeviceIn, szDeviceName, bufferLen) == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "friendly name: \"%S\"", szDeviceName); - } - - _usingInputDeviceIndex = true; - _inputDeviceIndex = index; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device) -{ - if (_recIsInitialized) - { - return -1; - } - - ERole role(eCommunications); - - if (device == AudioDeviceModule::kDefaultDevice) - { - role = eConsole; - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetRecordingDevice(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - role = eCommunications; - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetRecordingDevice(kDefaultCommunicationDevice)"); - } - - CriticalSectionScoped lock(_critSect); - - // Refresh the list of capture endpoint devices - _RefreshDeviceList(eCapture); - - HRESULT hr(S_OK); - - assert(_ptrEnumerator != NULL); - - // Select an endpoint capture device given the specified role - SAFE_RELEASE(_ptrDeviceIn); - hr = _ptrEnumerator->GetDefaultAudioEndpoint( - eCapture, - role, - &_ptrDeviceIn); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(_ptrDeviceIn); - return -1; - } - - WCHAR szDeviceName[MAX_PATH]; - const int bufferLen = sizeof(szDeviceName)/sizeof(szDeviceName)[0]; - - // Get the endpoint device's friendly-name - if (_GetDeviceName(_ptrDeviceIn, szDeviceName, bufferLen) == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "friendly name: \"%S\"", szDeviceName); - } - - _usingInputDeviceIndex = false; - _inputDevice = device; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the recording side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - - if (_ptrDeviceOut == NULL) - { - return -1; - } - - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitSpeaker() failed"); - } - - // Ensure that the updated rendering endpoint device is valid - if (_ptrDeviceOut == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - WAVEFORMATEX* pWfxOut = NULL; - WAVEFORMATEX Wfx; - WAVEFORMATEX* pWfxClosestMatch = NULL; - - // Create COM object with IAudioClient interface. - SAFE_RELEASE(_ptrClientOut); - hr = _ptrDeviceOut->Activate( - __uuidof(IAudioClient), - CLSCTX_ALL, - NULL, - (void**)&_ptrClientOut); - EXIT_ON_ERROR(hr); - - // Retrieve the stream format that the audio engine uses for its internal - // processing (mixing) of shared-mode streams. - hr = _ptrClientOut->GetMixFormat(&pWfxOut); - if (SUCCEEDED(hr)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Audio Engine's current rendering mix format:"); - // format type - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wFormatTag : 0x%X (%u)", pWfxOut->wFormatTag, pWfxOut->wFormatTag); - // number of channels (i.e. mono, stereo...) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels : %d", pWfxOut->nChannels); - // sample rate - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nSamplesPerSec : %d", pWfxOut->nSamplesPerSec); - // for buffer estimation - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nAvgBytesPerSec: %d", pWfxOut->nAvgBytesPerSec); - // block size of data - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nBlockAlign : %d", pWfxOut->nBlockAlign); - // number of bits per sample of mono data - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wBitsPerSample : %d", pWfxOut->wBitsPerSample); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cbSize : %d", pWfxOut->cbSize); - } - - // Set wave format - Wfx.wFormatTag = WAVE_FORMAT_PCM; - Wfx.wBitsPerSample = 16; - Wfx.cbSize = 0; - - const int freqs[6] = {48000, 44100, 16000, 96000, 32000, 8000}; - hr = S_FALSE; - - // Iterate over frequencies and channels, in order of priority - for (int freq = 0; freq < sizeof(freqs)/sizeof(freqs[0]); freq++) - { - for (int chan = 0; chan < sizeof(_playChannelsPrioList)/sizeof(_playChannelsPrioList[0]); chan++) - { - Wfx.nChannels = _playChannelsPrioList[chan]; - Wfx.nSamplesPerSec = freqs[freq]; - Wfx.nBlockAlign = Wfx.nChannels * Wfx.wBitsPerSample / 8; - Wfx.nAvgBytesPerSec = Wfx.nSamplesPerSec * Wfx.nBlockAlign; - // If the method succeeds and the audio endpoint device supports the specified stream format, - // it returns S_OK. If the method succeeds and provides a closest match to the specified format, - // it returns S_FALSE. - hr = _ptrClientOut->IsFormatSupported( - AUDCLNT_SHAREMODE_SHARED, - &Wfx, - &pWfxClosestMatch); - if (hr == S_OK) - { - break; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels=%d, nSamplesPerSec=%d is not supported", - Wfx.nChannels, Wfx.nSamplesPerSec); - } - } - if (hr == S_OK) - break; - } - - if (hr == S_OK) - { - _playAudioFrameSize = Wfx.nBlockAlign; - _playBlockSize = Wfx.nSamplesPerSec/100; - _playSampleRate = Wfx.nSamplesPerSec; - _devicePlaySampleRate = Wfx.nSamplesPerSec; // The device itself continues to run at 44.1 kHz. - _devicePlayBlockSize = Wfx.nSamplesPerSec/100; - if (_playBlockSize == 441) - { - _playSampleRate = 44000; // we are actually running at 44000 Hz and *not* 44100 Hz - _playBlockSize = 440; // adjust to size we can handle - } - _playChannels = Wfx.nChannels; - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "VoE selected this rendering format:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wFormatTag : 0x%X (%u)", Wfx.wFormatTag, Wfx.wFormatTag); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels : %d", Wfx.nChannels); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nSamplesPerSec : %d", Wfx.nSamplesPerSec); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nAvgBytesPerSec : %d", Wfx.nAvgBytesPerSec); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nBlockAlign : %d", Wfx.nBlockAlign); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wBitsPerSample : %d", Wfx.wBitsPerSample); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cbSize : %d", Wfx.cbSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Additional settings:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_playAudioFrameSize: %d", _playAudioFrameSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_playBlockSize : %d", _playBlockSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_playChannels : %d", _playChannels); - } - - _Get44kHzDrift(); - - // Create a rendering stream. - // - // **************************************************************************** - // For a shared-mode stream that uses event-driven buffering, the caller must - // set both hnsPeriodicity and hnsBufferDuration to 0. The Initialize method - // determines how large a buffer to allocate based on the scheduling period - // of the audio engine. Although the client's buffer processing thread is - // event driven, the basic buffer management process, as described previously, - // is unaltered. - // Each time the thread awakens, it should call IAudioClient::GetCurrentPadding - // to determine how much data to write to a rendering buffer or read from a capture - // buffer. In contrast to the two buffers that the Initialize method allocates - // for an exclusive-mode stream that uses event-driven buffering, a shared-mode - // stream requires a single buffer. - // **************************************************************************** - // - REFERENCE_TIME hnsBufferDuration = 0; // ask for minimum buffer size (default) - if (_devicePlaySampleRate == 44100) - { - // Ask for a larger buffer size (30ms) when using 44.1kHz as render rate. - // There seems to be a larger risk of underruns for 44.1 compared - // with the default rate (48kHz). When using default, we set the requested - // buffer duration to 0, which sets the buffer to the minimum size - // required by the engine thread. The actual buffer size can then be - // read by GetBufferSize() and it is 20ms on most machines. - hnsBufferDuration = 30*10000; - } - hr = _ptrClientOut->Initialize( - AUDCLNT_SHAREMODE_SHARED, // share Audio Engine with other applications - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, // processing of the audio buffer by the client will be event driven - hnsBufferDuration, // requested buffer capacity as a time value (in 100-nanosecond units) - 0, // periodicity - &Wfx, // selected wave format - NULL); // session GUID - - if (FAILED(hr)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "IAudioClient::Initialize() failed:"); - if (pWfxClosestMatch != NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "closest mix format: #channels=%d, samples/sec=%d, bits/sample=%d", - pWfxClosestMatch->nChannels, pWfxClosestMatch->nSamplesPerSec, pWfxClosestMatch->wBitsPerSample); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "no format suggested"); - } - } - EXIT_ON_ERROR(hr); - - if (_ptrAudioBuffer) - { - // Update the audio buffer with the selected parameters - _ptrAudioBuffer->SetPlayoutSampleRate(_playSampleRate); - _ptrAudioBuffer->SetPlayoutChannels((WebRtc_UWord8)_playChannels); - } - else - { - // We can enter this state during CoreAudioIsSupported() when no AudioDeviceImplementation - // has been created, hence the AudioDeviceBuffer does not exist. - // It is OK to end up here since we don't initiate any media in CoreAudioIsSupported(). - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceBuffer must be attached before streaming can start"); - } - - // Get the actual size of the shared (endpoint buffer). - // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate. - UINT bufferFrameCount(0); - hr = _ptrClientOut->GetBufferSize( - &bufferFrameCount); - if (SUCCEEDED(hr)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "IAudioClient::GetBufferSize() => %u (<=> %u bytes)", - bufferFrameCount, bufferFrameCount*_playAudioFrameSize); - } - - // Set the event handle that the system signals when an audio buffer is ready - // to be processed by the client. - hr = _ptrClientOut->SetEventHandle( - _hRenderSamplesReadyEvent); - EXIT_ON_ERROR(hr); - - // Get an IAudioRenderClient interface. - SAFE_RELEASE(_ptrRenderClient); - hr = _ptrClientOut->GetService( - __uuidof(IAudioRenderClient), - (void**)&_ptrRenderClient); - EXIT_ON_ERROR(hr); - - // Mark playout side as initialized - _playIsInitialized = true; - - CoTaskMemFree(pWfxOut); - CoTaskMemFree(pWfxClosestMatch); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "render side is now initialized"); - return 0; - -Exit: - _TraceCOMError(hr); - CoTaskMemFree(pWfxOut); - CoTaskMemFree(pWfxClosestMatch); - SAFE_RELEASE(_ptrClientOut); - SAFE_RELEASE(_ptrRenderClient); - return -1; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - if (QueryPerformanceFrequency(&_perfCounterFreq) == 0) - { - return -1; - } - _perfCounterFactor = 10000000.0 / (double)_perfCounterFreq.QuadPart; - - if (_ptrDeviceIn == NULL) - { - return -1; - } - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitMicrophone() failed"); - } - - // Ensure that the updated capturing endpoint device is valid - if (_ptrDeviceIn == NULL) - { - return -1; - } - - HRESULT hr = S_OK; - WAVEFORMATEX* pWfxIn = NULL; - WAVEFORMATEX Wfx; - WAVEFORMATEX* pWfxClosestMatch = NULL; - - - // Create COM object with IAudioClient interface. - SAFE_RELEASE(_ptrClientIn); - hr = _ptrDeviceIn->Activate( - __uuidof(IAudioClient), - CLSCTX_ALL, - NULL, - (void**)&_ptrClientIn); - EXIT_ON_ERROR(hr); - - - // Retrieve the stream format that the audio engine uses for its internal - // processing (mixing) of shared-mode streams. - hr = _ptrClientIn->GetMixFormat(&pWfxIn); - if (SUCCEEDED(hr)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Audio Engine's current capturing mix format:"); - // format type - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wFormatTag : 0x%X (%u)", pWfxIn->wFormatTag, pWfxIn->wFormatTag); - // number of channels (i.e. mono, stereo...) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels : %d", pWfxIn->nChannels); - // sample rate - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nSamplesPerSec : %d", pWfxIn->nSamplesPerSec); - // for buffer estimation - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nAvgBytesPerSec: %d", pWfxIn->nAvgBytesPerSec); - // block size of data - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nBlockAlign : %d", pWfxIn->nBlockAlign); - // number of bits per sample of mono data - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wBitsPerSample : %d", pWfxIn->wBitsPerSample); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cbSize : %d", pWfxIn->cbSize); - } - - // Set wave format - Wfx.wFormatTag = WAVE_FORMAT_PCM; - Wfx.wBitsPerSample = 16; - Wfx.cbSize = 0; - - const int freqs[6] = {48000, 44100, 16000, 96000, 32000, 8000}; - hr = S_FALSE; - - // Iterate over frequencies and channels, in order of priority - for (int freq = 0; freq < sizeof(freqs)/sizeof(freqs[0]); freq++) - { - for (int chan = 0; chan < sizeof(_recChannelsPrioList)/sizeof(_recChannelsPrioList[0]); chan++) - { - Wfx.nChannels = _recChannelsPrioList[chan]; - Wfx.nSamplesPerSec = freqs[freq]; - Wfx.nBlockAlign = Wfx.nChannels * Wfx.wBitsPerSample / 8; - Wfx.nAvgBytesPerSec = Wfx.nSamplesPerSec * Wfx.nBlockAlign; - // If the method succeeds and the audio endpoint device supports the specified stream format, - // it returns S_OK. If the method succeeds and provides a closest match to the specified format, - // it returns S_FALSE. - hr = _ptrClientIn->IsFormatSupported( - AUDCLNT_SHAREMODE_SHARED, - &Wfx, - &pWfxClosestMatch); - if (hr == S_OK) - { - break; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels=%d, nSamplesPerSec=%d is not supported", - Wfx.nChannels, Wfx.nSamplesPerSec); - } - } - if (hr == S_OK) - break; - } - - if (hr == S_OK) - { - _recAudioFrameSize = Wfx.nBlockAlign; - _recSampleRate = Wfx.nSamplesPerSec; - _recBlockSize = Wfx.nSamplesPerSec/100; - _recChannels = Wfx.nChannels; - if (_recBlockSize == 441) - { - _recSampleRate = 44000; // we are actually using 44000 Hz and *not* 44100 Hz - _recBlockSize = 440; // adjust to size we can handle - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "VoE selected this capturing format:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wFormatTag : 0x%X (%u)", Wfx.wFormatTag, Wfx.wFormatTag); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nChannels : %d", Wfx.nChannels); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nSamplesPerSec : %d", Wfx.nSamplesPerSec); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nAvgBytesPerSec : %d", Wfx.nAvgBytesPerSec); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "nBlockAlign : %d", Wfx.nBlockAlign); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wBitsPerSample : %d", Wfx.wBitsPerSample); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cbSize : %d", Wfx.cbSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Additional settings:"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_recAudioFrameSize: %d", _recAudioFrameSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_recBlockSize : %d", _recBlockSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_recChannels : %d", _recChannels); - } - - _Get44kHzDrift(); - - // Create a capturing stream. - hr = _ptrClientIn->Initialize( - AUDCLNT_SHAREMODE_SHARED, // share Audio Engine with other applications - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | // processing of the audio buffer by the client will be event driven - AUDCLNT_STREAMFLAGS_NOPERSIST, // volume and mute settings for an audio session will not persist across system restarts - 0, // required for event-driven shared mode - 0, // periodicity - &Wfx, // selected wave format - NULL); // session GUID - - - if (hr != S_OK) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "IAudioClient::Initialize() failed:"); - if (pWfxClosestMatch != NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "closest mix format: #channels=%d, samples/sec=%d, bits/sample=%d", - pWfxClosestMatch->nChannels, pWfxClosestMatch->nSamplesPerSec, pWfxClosestMatch->wBitsPerSample); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "no format suggested"); - } - } - EXIT_ON_ERROR(hr); - - if (_ptrAudioBuffer) - { - // Update the audio buffer with the selected parameters - _ptrAudioBuffer->SetRecordingSampleRate(_recSampleRate); - _ptrAudioBuffer->SetRecordingChannels((WebRtc_UWord8)_recChannels); - } - else - { - // We can enter this state during CoreAudioIsSupported() when no AudioDeviceImplementation - // has been created, hence the AudioDeviceBuffer does not exist. - // It is OK to end up here since we don't initiate any media in CoreAudioIsSupported(). - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceBuffer must be attached before streaming can start"); - } - - // Get the actual size of the shared (endpoint buffer). - // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate. - UINT bufferFrameCount(0); - hr = _ptrClientIn->GetBufferSize( - &bufferFrameCount); - if (SUCCEEDED(hr)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "IAudioClient::GetBufferSize() => %u (<=> %u bytes)", - bufferFrameCount, bufferFrameCount*_recAudioFrameSize); - } - - // Set the event handle that the system signals when an audio buffer is ready - // to be processed by the client. - hr = _ptrClientIn->SetEventHandle( - _hCaptureSamplesReadyEvent); - EXIT_ON_ERROR(hr); - - // Get an IAudioCaptureClient interface. - SAFE_RELEASE(_ptrCaptureClient); - hr = _ptrClientIn->GetService( - __uuidof(IAudioCaptureClient), - (void**)&_ptrCaptureClient); - EXIT_ON_ERROR(hr); - - // Mark capture side as initialized - _recIsInitialized = true; - - CoTaskMemFree(pWfxIn); - CoTaskMemFree(pWfxClosestMatch); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "capture side is now initialized"); - return 0; - -Exit: - _TraceCOMError(hr); - CoTaskMemFree(pWfxIn); - CoTaskMemFree(pWfxClosestMatch); - SAFE_RELEASE(_ptrClientIn); - SAFE_RELEASE(_ptrCaptureClient); - return -1; -} - -// ---------------------------------------------------------------------------- -// StartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_recIsInitialized) - { - return -1; - } - - if (_hRecThread != NULL) - { - return 0; - } - - if (_recording) - { - return 0; - } - - HRESULT hr = S_OK; - - _Lock(); - - // Create thread which will drive the capturing - _hRecThread = CreateThread( - NULL, - 0, - WSAPICaptureThread, - this, - 0, - NULL); - if (_hRecThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "failed to create the recording thread"); - return -1; - } - - // Set thread priority to highest possible - SetThreadPriority(_hRecThread, THREAD_PRIORITY_TIME_CRITICAL); - - _hGetCaptureVolumeThread = CreateThread(NULL, - 0, - GetCaptureVolumeThread, - this, - 0, - NULL); - if (_hGetCaptureVolumeThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create the volume getter thread"); - return -1; - } - - SetThreadPriority(_hGetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); - - _hSetCaptureVolumeThread = CreateThread(NULL, - 0, - SetCaptureVolumeThread, - this, - 0, - NULL); - if (_hSetCaptureVolumeThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create the volume setter thread"); - return -1; - } - - SetThreadPriority(_hSetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); - - _UnLock(); - - DWORD ret = WaitForSingleObject(_hCaptureStartedEvent, 1000); - if (ret != WAIT_OBJECT_0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "capturing did not start up properly"); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "capture audio stream has now started..."); - - _avgCPULoad = 0.0f; - _playAcc = 0; - _recording = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - WebRtc_Word32 err = 0; - - if (!_recIsInitialized) - { - return 0; - } - - _Lock(); - - if (_hRecThread == NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "no capturing stream is active => close down WASAPI only"); - SAFE_RELEASE(_ptrClientIn); - SAFE_RELEASE(_ptrCaptureClient); - _recIsInitialized = false; - _recording = false; - _UnLock(); - return 0; - } - - // Stop the driving thread... - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "closing down the webrtc_core_audio_capture_thread..."); - // Manual-reset event; it will remain signalled to stop all capture threads. - SetEvent(_hShutdownCaptureEvent); - - _UnLock(); - DWORD ret = WaitForSingleObject(_hRecThread, 2000); - if (ret != WAIT_OBJECT_0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to close down webrtc_core_audio_capture_thread"); - err = -1; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "webrtc_core_audio_capture_thread is now closed"); - } - - ret = WaitForSingleObject(_hGetCaptureVolumeThread, 2000); - if (ret != WAIT_OBJECT_0) - { - // the thread did not stop as it should - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to close down volume getter thread"); - err = -1; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " volume getter thread is now closed"); - } - - ret = WaitForSingleObject(_hSetCaptureVolumeThread, 2000); - if (ret != WAIT_OBJECT_0) - { - // the thread did not stop as it should - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to close down volume setter thread"); - err = -1; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " volume setter thread is now closed"); - } - _Lock(); - - ResetEvent(_hShutdownCaptureEvent); // Must be manually reset. - // Ensure that the thread has released these interfaces properly. - assert(err == -1 || _ptrClientIn == NULL); - assert(err == -1 || _ptrCaptureClient == NULL); - - _recIsInitialized = false; - _recording = false; - - // These will create thread leaks in the result of an error, - // but we can at least resume the call. - CloseHandle(_hRecThread); - _hRecThread = NULL; - - CloseHandle(_hGetCaptureVolumeThread); - _hGetCaptureVolumeThread = NULL; - - CloseHandle(_hSetCaptureVolumeThread); - _hSetCaptureVolumeThread = NULL; - - _UnLock(); - - return err; -} - -// ---------------------------------------------------------------------------- -// RecordingIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recIsInitialized); -} - -// ---------------------------------------------------------------------------- -// Recording -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recording); -} - -// ---------------------------------------------------------------------------- -// PlayoutIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_playIsInitialized); -} - -// ---------------------------------------------------------------------------- -// StartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return -1; - } - - if (_hPlayThread != NULL) - { - return 0; - } - - if (_playing) - { - return 0; - } - - HRESULT hr = S_OK; - - _Lock(); - - // Create thread which will drive the rendering - _hPlayThread = CreateThread( - NULL, - 0, - WSAPIRenderThread, - this, - 0, - NULL); - if (_hPlayThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to create the playout thread"); - return -1; - } - - // Set thred priority to highest possible - SetThreadPriority(_hPlayThread, THREAD_PRIORITY_TIME_CRITICAL); - - _UnLock(); - - DWORD ret = WaitForSingleObject(_hRenderStartedEvent, 1000); - if (ret != WAIT_OBJECT_0) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "rendering did not start up properly"); - return -1; - } - - _playing = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "rendering audio stream has now started..."); - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return 0; - } - - _Lock(); - - if (_hPlayThread == NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "no rending stream is active => close down WASAPI only"); - SAFE_RELEASE(_ptrClientOut); - SAFE_RELEASE(_ptrRenderClient); - _playIsInitialized = false; - _playing = false; - _UnLock(); - return 0; - } - - // stop the driving thread... - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "closing down the webrtc_core_audio_render_thread..."); - SetEvent(_hShutdownRenderEvent); - - _UnLock(); - DWORD ret = WaitForSingleObject(_hPlayThread, 2000); - if (ret != WAIT_OBJECT_0) - { - // the thread did not stop as it should - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to close down webrtc_core_audio_render_thread"); - CloseHandle(_hPlayThread); - _hPlayThread = NULL; - _playIsInitialized = false; - _playing = false; - return -1; - } - _Lock(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "webrtc_core_audio_render_thread is now closed"); - - // Ensure that the thread has released these interfaces properly - assert(NULL == _ptrClientOut); - assert(NULL == _ptrRenderClient); - - _playIsInitialized = false; - _playing = false; - - CloseHandle(_hPlayThread); - _hPlayThread = NULL; - - _UnLock(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = (WebRtc_UWord16)_sndCardPlayDelay; - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = (WebRtc_UWord16)_sndCardRecDelay; - return 0; -} - -// ---------------------------------------------------------------------------- -// Playing -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_playing); -} -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsCore::SetPlayoutBuffer(type=%u, sizeMS=%u)", type, sizeMS); - - CriticalSectionScoped lock(_critSect); - - _playBufType = type; - - if (type == AudioDeviceModule::kFixedBufferSize) - { - _playBufDelayFixed = sizeMS; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const -{ - CriticalSectionScoped lock(_critSect); - - type = _playBufType; - - if (type == AudioDeviceModule::kFixedBufferSize) - { - sizeMS = _playBufDelayFixed; - } - else - { - // Use same value as for PlayoutDelay - sizeMS = (WebRtc_UWord16)_sndCardPlayDelay; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// CPULoad -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::CPULoad(WebRtc_UWord16& load) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - load = static_cast (100*_avgCPULoad); - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::PlayoutWarning() const -{ - return ( _playWarning > 0); -} - -// ---------------------------------------------------------------------------- -// PlayoutError -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::PlayoutError() const -{ - return ( _playError > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::RecordingWarning() const -{ - return ( _recWarning > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingError -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsCore::RecordingError() const -{ - return ( _recError > 0); -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::ClearPlayoutError() -{ - _playError = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::ClearRecordingWarning() -{ - _recWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::ClearRecordingError() -{ - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// [static] WSAPIRenderThread -// ---------------------------------------------------------------------------- - -DWORD WINAPI AudioDeviceWindowsCore::WSAPIRenderThread(LPVOID context) -{ - return(((AudioDeviceWindowsCore*)context)->DoRenderThread()); -} - -// ---------------------------------------------------------------------------- -// [static] WSAPICaptureThread -// ---------------------------------------------------------------------------- - -DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThread(LPVOID context) -{ - return(((AudioDeviceWindowsCore*)context)->DoCaptureThread()); -} - -DWORD WINAPI AudioDeviceWindowsCore::GetCaptureVolumeThread(LPVOID context) -{ - return(((AudioDeviceWindowsCore*)context)->DoGetCaptureVolumeThread()); -} - -DWORD WINAPI AudioDeviceWindowsCore::SetCaptureVolumeThread(LPVOID context) -{ - return(((AudioDeviceWindowsCore*)context)->DoSetCaptureVolumeThread()); -} - -DWORD AudioDeviceWindowsCore::DoGetCaptureVolumeThread() -{ - HANDLE waitObject = _hShutdownCaptureEvent; - - while (1) - { - DWORD waitResult = WaitForSingleObject(waitObject, - GET_MIC_VOLUME_INTERVAL_MS); - switch (waitResult) - { - case WAIT_OBJECT_0: // _hShutdownCaptureEvent - return 0; - case WAIT_TIMEOUT: // timeout notification - break; - default: // unexpected error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unknown wait termination on get volume thread"); - return -1; - } - - if (AGC()) - { - WebRtc_UWord32 currentMicLevel = 0; - if (MicrophoneVolume(currentMicLevel) == 0) - { - // This doesn't set the system volume, just stores it. - _Lock(); - if (_ptrAudioBuffer) - { - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); - } - _UnLock(); - } - } - } -} - -DWORD AudioDeviceWindowsCore::DoSetCaptureVolumeThread() -{ - HANDLE waitArray[2] = {_hShutdownCaptureEvent, _hSetCaptureVolumeEvent}; - - while (1) - { - DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE); - switch (waitResult) - { - case WAIT_OBJECT_0: // _hShutdownCaptureEvent - return 0; - case WAIT_OBJECT_0 + 1: // _hSetCaptureVolumeEvent - break; - default: // unexpected error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unknown wait termination on set volume thread"); - return -1; - } - - _Lock(); - WebRtc_UWord32 newMicLevel = _newMicLevel; - _UnLock(); - - if (SetMicrophoneVolume(newMicLevel) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " the required modification of the microphone volume failed"); - } - } -} - -// ---------------------------------------------------------------------------- -// DoRenderThread -// ---------------------------------------------------------------------------- - -DWORD AudioDeviceWindowsCore::DoRenderThread() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool keepPlaying = true; - HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent}; - HRESULT hr = S_OK; - HANDLE hMmTask = NULL; - - LARGE_INTEGER t1; - LARGE_INTEGER t2; - WebRtc_Word32 time(0); - - hr = CoInitializeEx(NULL, COM_THREADING_MODEL); - if (FAILED(hr)) - { - _TraceCOMError(hr); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "unable to initialize COM in render thread"); - return hr; - } - - _SetThreadName(-1, "webrtc_core_audio_render_thread"); - - // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority. - // - if (_winSupportAvrt) - { - DWORD taskIndex(0); - hMmTask = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex); - if (hMmTask) - { - if (FALSE == _PAvSetMmThreadPriority(hMmTask, AVRT_PRIORITY_CRITICAL)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to boost play-thread using MMCSS"); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "render thread is now registered with MMCSS (taskIndex=%d)", taskIndex); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to enable MMCSS on render thread (err=%d)", GetLastError()); - _TraceCOMError(GetLastError()); - } - } - - _Lock(); - - // Get size of rendering buffer (length is expressed as the number of audio frames the buffer can hold). - // This value is fixed during the rendering session. - // - UINT32 bufferLength = 0; - hr = _ptrClientOut->GetBufferSize(&bufferLength); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[REND] size of buffer : %u", bufferLength); - - // Get maximum latency for the current stream (will not change for the lifetime of the IAudioClient object). - // - REFERENCE_TIME latency; - _ptrClientOut->GetStreamLatency(&latency); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[REND] max stream latency : %u (%3.2f ms)", - (DWORD)latency, (double)(latency/10000.0)); - - // Get the length of the periodic interval separating successive processing passes by - // the audio engine on the data in the endpoint buffer. - // - // The period between processing passes by the audio engine is fixed for a particular - // audio endpoint device and represents the smallest processing quantum for the audio engine. - // This period plus the stream latency between the buffer and endpoint device represents - // the minimum possible latency that an audio application can achieve. - // Typical value: 100000 <=> 0.01 sec = 10ms. - // - REFERENCE_TIME devPeriod = 0; - _ptrClientOut->GetDevicePeriod(&devPeriod, NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[REND] device period : %u (%3.2f ms)", - (DWORD)devPeriod, (double)(devPeriod/10000.0)); - - // The Event Driven renderer will be woken up every defaultDevicePeriod hundred-nano-seconds. - // Convert that time into a number of frames. - // - double devicePeriodInSeconds = devPeriod / (10000.0*1000.0); - UINT32 devicePeriodInFrames = static_cast(_playSampleRate * devicePeriodInSeconds + 0.5); - - // Derive inital rendering delay. - // Example: 10*(960/480) + 15 = 20 + 15 = 35ms - // - _sndCardPlayDelay = 10 * (bufferLength / _playBlockSize) + (int)((latency + devPeriod) / 10000); - _writtenSamples = 0; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[REND] initial delay : %u", _sndCardPlayDelay); - - double endpointBufferSizeMS = 10.0 * ((double)bufferLength / (double)_devicePlayBlockSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[REND] endpointBufferSizeMS : %3.2f", endpointBufferSizeMS); - - // Before starting the stream, fill the rendering buffer with silence. - // - BYTE *pData = NULL; - hr = _ptrRenderClient->GetBuffer(bufferLength, &pData); - EXIT_ON_ERROR(hr); - - hr = _ptrRenderClient->ReleaseBuffer(bufferLength, AUDCLNT_BUFFERFLAGS_SILENT); - EXIT_ON_ERROR(hr); - - _writtenSamples += bufferLength; - - // Start up the rendering audio stream. - // - hr = _ptrClientOut->Start(); - EXIT_ON_ERROR(hr); - - _UnLock(); - - // Set event which will ensure that the calling thread modifies the playing state to true. - // - SetEvent(_hRenderStartedEvent); - - // >> ------------------ THREAD LOOP ------------------ - - while (keepPlaying) - { - // Wait for a capture notification event or a shutdown event - DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500); - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // _hShutdownRenderEvent - keepPlaying = false; - break; - case WAIT_OBJECT_0 + 1: // _hRenderSamplesReadyEvent - break; - case WAIT_TIMEOUT: // timeout notification - _ptrClientOut->Stop(); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "render event timed out after 0.5 seconds"); - goto Exit; - default: // unexpected error - _ptrClientOut->Stop(); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "unknown wait termination on render side"); - goto Exit; - } - - while (keepPlaying) - { - _Lock(); - - // Get the number of frames of padding (queued up to play) in the endpoint buffer. - UINT32 padding = 0; - hr = _ptrClientOut->GetCurrentPadding(&padding); - EXIT_ON_ERROR(hr); - - // Derive the amount of available space in the output buffer - WebRtc_UWord32 framesAvailable = bufferLength - padding; - // WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "#avaliable audio frames = %u", framesAvailable); - - // Do we have 10 ms available in the render buffer? - if (framesAvailable < _playBlockSize) - { - // Not enough space in render buffer to store next render packet. - _UnLock(); - break; - } - - // Write n*10ms buffers to the render buffer - const WebRtc_UWord32 n10msBuffers = (framesAvailable / _playBlockSize); - for (WebRtc_UWord32 n = 0; n < n10msBuffers; n++) - { - // Get pointer (i.e., grab the buffer) to next space in the shared render buffer. - hr = _ptrRenderClient->GetBuffer(_playBlockSize, &pData); - EXIT_ON_ERROR(hr); - - QueryPerformanceCounter(&t1); // measure time: START - - if (_ptrAudioBuffer) - { - // Request data to be played out (#bytes = _playBlockSize*_audioFrameSize) - _UnLock(); - WebRtc_UWord32 nSamples = _ptrAudioBuffer->RequestPlayoutData(_playBlockSize); - _Lock(); - - // Sanity check to ensure that essential states are not modified during the unlocked period - if (_ptrRenderClient == NULL || _ptrClientOut == NULL) - { - _UnLock(); - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, "output state has been modified during unlocked period"); - goto Exit; - } - if (nSamples != _playBlockSize) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "nSamples(%d) != _playBlockSize(%d)", nSamples, _playBlockSize); - } - - // Get the actual (stored) data - nSamples = _ptrAudioBuffer->GetPlayoutData((WebRtc_Word8*)pData); - } - - QueryPerformanceCounter(&t2); // measure time: STOP - time = (int)(t2.QuadPart-t1.QuadPart); - _playAcc += time; - - DWORD dwFlags(0); - hr = _ptrRenderClient->ReleaseBuffer(_playBlockSize, dwFlags); - // See http://msdn.microsoft.com/en-us/library/dd316605(VS.85).aspx - // for more details regarding AUDCLNT_E_DEVICE_INVALIDATED. - EXIT_ON_ERROR(hr); - - _writtenSamples += _playBlockSize; - } - - _UnLock(); - } - } - - // ------------------ THREAD LOOP ------------------ << - - Sleep(static_cast(endpointBufferSizeMS+0.5)); - hr = _ptrClientOut->Stop(); - -Exit: - if (FAILED(hr)) - { - _UnLock(); - _ptrClientOut->Stop(); - _TraceCOMError(hr); - } - - if (_winSupportAvrt) - { - if (NULL != hMmTask) - { - _PAvRevertMmThreadCharacteristics(hMmTask); - } - } - - if (keepPlaying) - { - hr = _ptrClientOut->Stop(); - if (FAILED(hr)) - { - _TraceCOMError(hr); - } - hr = _ptrClientOut->Reset(); - if (FAILED(hr)) - { - _TraceCOMError(hr); - } - - // Trigger callback from module process thread - _playError = 1; - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kPlayoutError message posted: rendering thread has ended pre-maturely"); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_Rendering thread is now terminated properly"); - } - - SAFE_RELEASE(_ptrClientOut); - SAFE_RELEASE(_ptrRenderClient); - - CoUninitialize(); - return (DWORD)hr; -} - -// ---------------------------------------------------------------------------- -// DoCaptureThread -// ---------------------------------------------------------------------------- - -DWORD AudioDeviceWindowsCore::DoCaptureThread() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool keepRecording = true; - HANDLE waitArray[2] = {_hShutdownCaptureEvent, _hCaptureSamplesReadyEvent}; - HRESULT hr = S_OK; - HANDLE hMmTask = NULL; - - - LARGE_INTEGER t1; - LARGE_INTEGER t2; - WebRtc_Word32 time(0); - - BYTE* syncBuffer = NULL; - UINT32 syncBufIndex = 0; - - WebRtc_UWord32 newMicLevel(0); - WebRtc_UWord32 currentMicLevel(0); - - _readSamples = 0; - - hr = CoInitializeEx(NULL, COM_THREADING_MODEL); - if (FAILED(hr)) - { - _TraceCOMError(hr); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "unable to initialize COM in capture thread"); - return hr; - } - - _SetThreadName(-1, "webrtc_core_audio_capture_thread"); - - // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority. - // - if (_winSupportAvrt) - { - DWORD taskIndex(0); - hMmTask = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex); - if (hMmTask) - { - if (FALSE == _PAvSetMmThreadPriority(hMmTask, AVRT_PRIORITY_CRITICAL)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to boost rec-thread using MMCSS"); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "capture thread is now registered with MMCSS (taskIndex=%d)", taskIndex); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to enable MMCSS on capture thread (err=%d)", GetLastError()); - _TraceCOMError(GetLastError()); - } - } - - _Lock(); - - // Get size of capturing buffer (length is expressed as the number of audio frames the buffer can hold). - // This value is fixed during the capturing session. - // - UINT32 bufferLength = 0; - hr = _ptrClientIn->GetBufferSize(&bufferLength); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of buffer : %u", bufferLength); - - // Allocate memory for sync buffer. - // It is used for compensation between native 44.1 and internal 44.0 and - // for cases when the capture buffer is larger than 10ms. - // - const UINT32 syncBufferSize = 2*(bufferLength * _recAudioFrameSize); - syncBuffer = new BYTE[syncBufferSize]; - if (syncBuffer == NULL) - { - return E_POINTER; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of sync buffer : %u [bytes]", syncBufferSize); - - // Get maximum latency for the current stream (will not change for the lifetime of the IAudioClient object). - // - REFERENCE_TIME latency; - _ptrClientIn->GetStreamLatency(&latency); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] max stream latency : %u (%3.2f ms)", - (DWORD)latency, (double)(latency / 10000.0)); - - // Get the length of the periodic interval separating successive processing passes by - // the audio engine on the data in the endpoint buffer. - // - REFERENCE_TIME devPeriod = 0; - _ptrClientIn->GetDevicePeriod(&devPeriod, NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] device period : %u (%3.2f ms)", - (DWORD)devPeriod, (double)(devPeriod / 10000.0)); - - double extraDelayMS = (double)((latency + devPeriod) / 10000.0); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] extraDelayMS : %3.2f", extraDelayMS); - - double endpointBufferSizeMS = 10.0 * ((double)bufferLength / (double)_recBlockSize); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] endpointBufferSizeMS : %3.2f", endpointBufferSizeMS); - - // Start up the capturing stream. - // - hr = _ptrClientIn->Start(); - EXIT_ON_ERROR(hr); - - _UnLock(); - - // Set event which will ensure that the calling thread modifies the recording state to true. - // - SetEvent(_hCaptureStartedEvent); - - // >> ---------------------------- THREAD LOOP ---------------------------- - - while (keepRecording) - { - // Wait for a capture notification event or a shutdown event - DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500); - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // _hShutdownCaptureEvent - keepRecording = false; - break; - case WAIT_OBJECT_0 + 1: // _hCaptureSamplesReadyEvent - break; - case WAIT_TIMEOUT: // timeout notification - _ptrClientIn->Stop(); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "capture event timed out after 0.5 seconds"); - goto Exit; - default: // unexpected error - _ptrClientIn->Stop(); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "unknown wait termination on capture side"); - goto Exit; - } - - while (keepRecording) - { - BYTE *pData = 0; - UINT32 framesAvailable = 0; - DWORD flags = 0; - UINT64 recTime = 0; - UINT64 recPos = 0; - - _Lock(); - - // Find out how much capture data is available - // - hr = _ptrCaptureClient->GetBuffer(&pData, // packet which is ready to be read by used - &framesAvailable, // #frames in the captured packet (can be zero) - &flags, // support flags (check) - &recPos, // device position of first audio frame in data packet - &recTime); // value of performance counter at the time of recording the first audio frame - - if (SUCCEEDED(hr)) - { - if (AUDCLNT_S_BUFFER_EMPTY == hr) - { - // Buffer was empty => start waiting for a new capture notification event - _UnLock(); - break; - } - - if (flags & AUDCLNT_BUFFERFLAGS_SILENT) - { - // Treat all of the data in the packet as silence and ignore the actual data values. - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "AUDCLNT_BUFFERFLAGS_SILENT"); - pData = NULL; - } - - assert(framesAvailable != 0); - - if (pData) - { - CopyMemory(&syncBuffer[syncBufIndex*_recAudioFrameSize], pData, framesAvailable*_recAudioFrameSize); - } - else - { - ZeroMemory(&syncBuffer[syncBufIndex*_recAudioFrameSize], framesAvailable*_recAudioFrameSize); - } - assert(syncBufferSize >= (syncBufIndex*_recAudioFrameSize)+framesAvailable*_recAudioFrameSize); - - // Release the capture buffer - // - hr = _ptrCaptureClient->ReleaseBuffer(framesAvailable); - EXIT_ON_ERROR(hr); - - _readSamples += framesAvailable; - syncBufIndex += framesAvailable; - - QueryPerformanceCounter(&t1); - - // Check the current delay on the recording side - // - _sndCardRecDelay = (WebRtc_UWord32)((((UINT64)t1.QuadPart * _perfCounterFactor) - recTime) / 10000) + (10*syncBufIndex) / _recBlockSize - 10; - - // Check the current delay on the playout side - // - if (_ptrClientOut) - { - IAudioClock* clock = NULL; - UINT64 pos = 0; - UINT64 freq = 1; - hr = _ptrClientOut->GetService(__uuidof(IAudioClock), (void**)&clock); - EXIT_ON_ERROR(hr); - clock->GetPosition(&pos, NULL); - clock->GetFrequency(&freq); - _sndCardPlayDelay = ROUND((double(_writtenSamples) / _devicePlaySampleRate - double(pos) / freq) * 1000.0); - clock->Release(); - } - - // Send the captured data to the registered consumer - // - WebRtc_UWord32 sndCardRecDelay = _sndCardRecDelay; // avoid modifying the "correct" delay - while (syncBufIndex >= _recBlockSize) - { - if (_ptrAudioBuffer) - { - _ptrAudioBuffer->SetRecordedBuffer((const WebRtc_Word8*)syncBuffer, _recBlockSize); - - _driftAccumulator += _sampleDriftAt48kHz; - const WebRtc_Word32 clockDrift = static_cast(_driftAccumulator); - _driftAccumulator -= clockDrift; - - _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, sndCardRecDelay, clockDrift); - - QueryPerformanceCounter(&t1); // measure time: START - - _UnLock(); // release lock while making the callback - _ptrAudioBuffer->DeliverRecordedData(); - _Lock(); // restore the lock - - QueryPerformanceCounter(&t2); // measure time: STOP - - // Measure "average CPU load". - // Basically what we do here is to measure how many percent of our 10ms period - // is used for encoding and decoding. This value shuld be used as a warning indicator - // only and not seen as an absolute value. Running at ~100% will lead to bad QoS. - time = (int)(t2.QuadPart - t1.QuadPart); - _avgCPULoad = (float)(_avgCPULoad*.99 + (time + _playAcc) / (double)(_perfCounterFreq.QuadPart)); - _playAcc = 0; - - // Sanity check to ensure that essential states are not modified during the unlocked period - if (_ptrCaptureClient == NULL || _ptrClientIn == NULL) - { - _UnLock(); - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, "input state has been modified during unlocked period"); - goto Exit; - } - } - - // store remaining data which was not able to deliver as 10ms segment - MoveMemory(&syncBuffer[0], &syncBuffer[_recBlockSize*_recAudioFrameSize], (syncBufIndex-_recBlockSize)*_recAudioFrameSize); - syncBufIndex -= _recBlockSize; - sndCardRecDelay -= 10; - } - - if (_AGC) - { - WebRtc_UWord32 newMicLevel = _ptrAudioBuffer->NewMicLevel(); - if (newMicLevel != 0) - { - // The VQE will only deliver non-zero microphone levels when a change is needed. - // Set this new mic level (received from the observer as return value in the callback). - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "AGC change of volume: new=%u", newMicLevel); - // We store this outside of the audio buffer to avoid - // having it overwritten by the getter thread. - _newMicLevel = newMicLevel; - SetEvent(_hSetCaptureVolumeEvent); - } - } - } - else - { - // If GetBuffer returns AUDCLNT_E_BUFFER_ERROR, the thread consuming the audio samples - // must wait for the next processing pass. The client might benefit from keeping a count - // of the failed GetBuffer calls. If GetBuffer returns this error repeatedly, the client - // can start a new processing loop after shutting down the current client by calling - // IAudioClient::Stop, IAudioClient::Reset, and releasing the audio client. - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "IAudioCaptureClient::GetBuffer returned AUDCLNT_E_BUFFER_ERROR, hr = 0x%08X", hr); - goto Exit; - } - - _UnLock(); - } - } - - // ---------------------------- THREAD LOOP ---------------------------- << - - hr = _ptrClientIn->Stop(); - -Exit: - if (FAILED(hr)) - { - _UnLock(); - _ptrClientIn->Stop(); - _TraceCOMError(hr); - } - - if (_winSupportAvrt) - { - if (NULL != hMmTask) - { - _PAvRevertMmThreadCharacteristics(hMmTask); - } - } - - if (keepRecording) - { - hr = _ptrClientIn->Stop(); - if (FAILED(hr)) - { - _TraceCOMError(hr); - } - hr = _ptrClientIn->Reset(); - if (FAILED(hr)) - { - _TraceCOMError(hr); - } - - // Trigger callback from module process thread - _recError = 1; - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kRecordingError message posted: capturing thread has ended pre-maturely"); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_Capturing thread is now terminated properly"); - } - - SAFE_RELEASE(_ptrClientIn); - SAFE_RELEASE(_ptrCaptureClient); - - if (syncBuffer) - { - delete [] syncBuffer; - } - - CoUninitialize(); - return (DWORD)hr; -} - -// ---------------------------------------------------------------------------- -// _RefreshDeviceList -// -// Creates a new list of endpoint rendering or capture devices after -// deleting any previously created (and possibly out-of-date) list of -// such devices. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_RefreshDeviceList(EDataFlow dir) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - IMMDeviceCollection *pCollection = NULL; - - assert(dir == eRender || dir == eCapture); - assert(_ptrEnumerator != NULL); - - // Create a fresh list of devices using the specified direction - hr = _ptrEnumerator->EnumAudioEndpoints( - dir, - DEVICE_STATE_ACTIVE, - &pCollection); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pCollection); - return -1; - } - - if (dir == eRender) - { - SAFE_RELEASE(_ptrRenderCollection); - _ptrRenderCollection = pCollection; - } - else - { - SAFE_RELEASE(_ptrCaptureCollection); - _ptrCaptureCollection = pCollection; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// _DeviceListCount -// -// Gets a count of the endpoint rendering or capture devices in the -// current list of such devices. -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceWindowsCore::_DeviceListCount(EDataFlow dir) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - UINT count = 0; - - assert(eRender == dir || eCapture == dir); - - if (eRender == dir && NULL != _ptrRenderCollection) - { - hr = _ptrRenderCollection->GetCount(&count); - } - else if (NULL != _ptrCaptureCollection) - { - hr = _ptrCaptureCollection->GetCount(&count); - } - - if (FAILED(hr)) - { - _TraceCOMError(hr); - return -1; - } - - return static_cast (count); -} - -// ---------------------------------------------------------------------------- -// _GetListDeviceName -// -// Gets the friendly name of an endpoint rendering or capture device -// from the current list of such devices. The caller uses an index -// into the list to identify the device. -// -// Uses: _ptrRenderCollection or _ptrCaptureCollection which is updated -// in _RefreshDeviceList(). -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetListDeviceName(EDataFlow dir, int index, LPWSTR szBuffer, int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - IMMDevice *pDevice = NULL; - - assert(dir == eRender || dir == eCapture); - - if (eRender == dir && NULL != _ptrRenderCollection) - { - hr = _ptrRenderCollection->Item(index, &pDevice); - } - else if (NULL != _ptrCaptureCollection) - { - hr = _ptrCaptureCollection->Item(index, &pDevice); - } - - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pDevice); - return -1; - } - - WebRtc_Word32 res = _GetDeviceName(pDevice, szBuffer, bufferLen); - SAFE_RELEASE(pDevice); - return res; -} - -// ---------------------------------------------------------------------------- -// _GetDefaultDeviceName -// -// Gets the friendly name of an endpoint rendering or capture device -// given a specified device role. -// -// Uses: _ptrEnumerator -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetDefaultDeviceName(EDataFlow dir, ERole role, LPWSTR szBuffer, int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - IMMDevice *pDevice = NULL; - - assert(dir == eRender || dir == eCapture); - assert(role == eConsole || role == eCommunications); - assert(_ptrEnumerator != NULL); - - hr = _ptrEnumerator->GetDefaultAudioEndpoint( - dir, - role, - &pDevice); - - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pDevice); - return -1; - } - - WebRtc_Word32 res = _GetDeviceName(pDevice, szBuffer, bufferLen); - SAFE_RELEASE(pDevice); - return res; -} - -// ---------------------------------------------------------------------------- -// _GetListDeviceID -// -// Gets the unique ID string of an endpoint rendering or capture device -// from the current list of such devices. The caller uses an index -// into the list to identify the device. -// -// Uses: _ptrRenderCollection or _ptrCaptureCollection which is updated -// in _RefreshDeviceList(). -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetListDeviceID(EDataFlow dir, int index, LPWSTR szBuffer, int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - IMMDevice *pDevice = NULL; - - assert(dir == eRender || dir == eCapture); - - if (eRender == dir && NULL != _ptrRenderCollection) - { - hr = _ptrRenderCollection->Item(index, &pDevice); - } - else if (NULL != _ptrCaptureCollection) - { - hr = _ptrCaptureCollection->Item(index, &pDevice); - } - - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pDevice); - return -1; - } - - WebRtc_Word32 res = _GetDeviceID(pDevice, szBuffer, bufferLen); - SAFE_RELEASE(pDevice); - return res; -} - -// ---------------------------------------------------------------------------- -// _GetDefaultDeviceID -// -// Gets the uniqe device ID of an endpoint rendering or capture device -// given a specified device role. -// -// Uses: _ptrEnumerator -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetDefaultDeviceID(EDataFlow dir, ERole role, LPWSTR szBuffer, int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr = S_OK; - IMMDevice *pDevice = NULL; - - assert(dir == eRender || dir == eCapture); - assert(role == eConsole || role == eCommunications); - assert(_ptrEnumerator != NULL); - - hr = _ptrEnumerator->GetDefaultAudioEndpoint( - dir, - role, - &pDevice); - - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pDevice); - return -1; - } - - WebRtc_Word32 res = _GetDeviceID(pDevice, szBuffer, bufferLen); - SAFE_RELEASE(pDevice); - return res; -} - -// ---------------------------------------------------------------------------- -// _GetDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetDeviceName(IMMDevice* pDevice, - LPWSTR pszBuffer, - int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - static const WCHAR szDefault[] = L""; - - HRESULT hr = E_FAIL; - IPropertyStore *pProps = NULL; - PROPVARIANT varName; - - assert(pszBuffer != NULL); - assert(bufferLen > 0); - - if (pDevice != NULL) - { - hr = pDevice->OpenPropertyStore(STGM_READ, &pProps); - if (FAILED(hr)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "IMMDevice::OpenPropertyStore failed, hr = 0x%08X", hr); - } - } - - // Initialize container for property value. - PropVariantInit(&varName); - - if (SUCCEEDED(hr)) - { - // Get the endpoint device's friendly-name property. - hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName); - if (FAILED(hr)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "IPropertyStore::GetValue failed, hr = 0x%08X", hr); - } - } - - if ((SUCCEEDED(hr)) && (VT_EMPTY == varName.vt)) - { - hr = E_FAIL; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "IPropertyStore::GetValue returned no value, hr = 0x%08X", hr); - } - - if ((SUCCEEDED(hr)) && (VT_LPWSTR != varName.vt)) - { - // The returned value is not a wide null terminated string. - hr = E_UNEXPECTED; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "IPropertyStore::GetValue returned unexpected type, hr = 0x%08X", hr); - } - - if (SUCCEEDED(hr) && (varName.pwszVal != NULL)) - { - // Copy the valid device name to the provided ouput buffer. - wcsncpy_s(pszBuffer, bufferLen, varName.pwszVal, _TRUNCATE); - } - else - { - // Failed to find the device name. - wcsncpy_s(pszBuffer, bufferLen, szDefault, _TRUNCATE); - } - - PropVariantClear(&varName); - SAFE_RELEASE(pProps); - - return 0; -} - -// ---------------------------------------------------------------------------- -// _GetDeviceID -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetDeviceID(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - static const WCHAR szDefault[] = L""; - - HRESULT hr = E_FAIL; - LPWSTR pwszID = NULL; - - assert(pszBuffer != NULL); - assert(bufferLen > 0); - - if (pDevice != NULL) - { - hr = pDevice->GetId(&pwszID); - } - - if (hr == S_OK) - { - // Found the device ID. - wcsncpy_s(pszBuffer, bufferLen, pwszID, _TRUNCATE); - } - else - { - // Failed to find the device ID. - wcsncpy_s(pszBuffer, bufferLen, szDefault, _TRUNCATE); - } - - CoTaskMemFree(pwszID); - return 0; -} - -// ---------------------------------------------------------------------------- -// _GetDefaultDevice -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetDefaultDevice(EDataFlow dir, ERole role, IMMDevice** ppDevice) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - HRESULT hr(S_OK); - - assert(_ptrEnumerator != NULL); - - hr = _ptrEnumerator->GetDefaultAudioEndpoint( - dir, - role, - ppDevice); - if (FAILED(hr)) - { - _TraceCOMError(hr); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// _GetListDevice -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_GetListDevice(EDataFlow dir, int index, IMMDevice** ppDevice) -{ - HRESULT hr(S_OK); - - assert(_ptrEnumerator != NULL); - - IMMDeviceCollection *pCollection = NULL; - - hr = _ptrEnumerator->EnumAudioEndpoints( - dir, - DEVICE_STATE_ACTIVE, // only active endpoints are OK - &pCollection); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pCollection); - return -1; - } - - hr = pCollection->Item( - index, - ppDevice); - if (FAILED(hr)) - { - _TraceCOMError(hr); - SAFE_RELEASE(pCollection); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// _EnumerateEndpointDevicesAll -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll(EDataFlow dataFlow) const -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - assert(_ptrEnumerator != NULL); - - HRESULT hr = S_OK; - IMMDeviceCollection *pCollection = NULL; - - // Generate a collection of audio endpoint devices in the system. - // Get states for *all* endpoint devices. - // Output: IMMDeviceCollection interface. - hr = _ptrEnumerator->EnumAudioEndpoints( - dataFlow, // data-flow direction (input parameter) - DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_NOTPRESENT | DEVICE_STATE_UNPLUGGED, - &pCollection); // release interface when done - - EXIT_ON_ERROR(hr); - - // use the IMMDeviceCollection interface... - - UINT count; - IMMDevice *pEndpoint = NULL; - IPropertyStore *pProps = NULL; - IAudioEndpointVolume* pEndpointVolume = NULL; - LPWSTR pwszID = NULL; - - // Retrieve a count of the devices in the device collection. - hr = pCollection->GetCount(&count); - EXIT_ON_ERROR(hr); - if (dataFlow == eRender) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#rendering endpoint devices (counting all): %u", count); - else if (dataFlow == eCapture) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#capturing endpoint devices (counting all): %u", count); - - if (count == 0) - { - return 0; - } - - // Each loop prints the name of an endpoint device. - for (ULONG i = 0; i < count; i++) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Endpoint %d:", i); - - // Get pointer to endpoint number i. - // Output: IMMDevice interface. - hr = pCollection->Item( - i, - &pEndpoint); - EXIT_ON_ERROR(hr); - - // use the IMMDevice interface of the specified endpoint device... - - // Get the endpoint ID string (uniquely identifies the device among all audio endpoint devices) - hr = pEndpoint->GetId(&pwszID); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "ID string : %S", pwszID); - - // Retrieve an interface to the device's property store. - // Output: IPropertyStore interface. - hr = pEndpoint->OpenPropertyStore( - STGM_READ, - &pProps); - EXIT_ON_ERROR(hr); - - // use the IPropertyStore interface... - - PROPVARIANT varName; - // Initialize container for property value. - PropVariantInit(&varName); - - // Get the endpoint's friendly-name property. - // Example: "Speakers (Realtek High Definition Audio)" - hr = pProps->GetValue( - PKEY_Device_FriendlyName, - &varName); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "friendly name: \"%S\"", varName.pwszVal); - - // Get the endpoint's current device state - DWORD dwState; - hr = pEndpoint->GetState(&dwState); - EXIT_ON_ERROR(hr); - if (dwState & DEVICE_STATE_ACTIVE) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "state (0x%x) : *ACTIVE*", dwState); - if (dwState & DEVICE_STATE_DISABLED) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "state (0x%x) : DISABLED", dwState); - if (dwState & DEVICE_STATE_NOTPRESENT) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "state (0x%x) : NOTPRESENT", dwState); - if (dwState & DEVICE_STATE_UNPLUGGED) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "state (0x%x) : UNPLUGGED", dwState); - - // Check the hardware volume capabilities. - DWORD dwHwSupportMask = 0; - hr = pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, - NULL, (void**)&pEndpointVolume); - EXIT_ON_ERROR(hr); - hr = pEndpointVolume->QueryHardwareSupport(&dwHwSupportMask); - EXIT_ON_ERROR(hr); - if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_VOLUME) - // The audio endpoint device supports a hardware volume control - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "hwmask (0x%x) : HARDWARE_SUPPORT_VOLUME", dwHwSupportMask); - if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_MUTE) - // The audio endpoint device supports a hardware mute control - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "hwmask (0x%x) : HARDWARE_SUPPORT_MUTE", dwHwSupportMask); - if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_METER) - // The audio endpoint device supports a hardware peak meter - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "hwmask (0x%x) : HARDWARE_SUPPORT_METER", dwHwSupportMask); - - // Check the channel count (#channels in the audio stream that enters or leaves the audio endpoint device) - UINT nChannelCount(0); - hr = pEndpointVolume->GetChannelCount( - &nChannelCount); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#channels : %u", nChannelCount); - - if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_VOLUME) - { - // Get the volume range. - float fLevelMinDB(0.0); - float fLevelMaxDB(0.0); - float fVolumeIncrementDB(0.0); - hr = pEndpointVolume->GetVolumeRange( - &fLevelMinDB, - &fLevelMaxDB, - &fVolumeIncrementDB); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "volume range : %4.2f (min), %4.2f (max), %4.2f (inc) [dB]", - fLevelMinDB, fLevelMaxDB, fVolumeIncrementDB); - - // The volume range from vmin = fLevelMinDB to vmax = fLevelMaxDB is divided - // into n uniform intervals of size vinc = fVolumeIncrementDB, where - // n = (vmax ?vmin) / vinc. - // The values vmin, vmax, and vinc are measured in decibels. The client can set - // the volume level to one of n + 1 discrete values in the range from vmin to vmax. - int n = (int)((fLevelMaxDB-fLevelMinDB)/fVolumeIncrementDB); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#intervals : %d", n); - - // Get information about the current step in the volume range. - // This method represents the volume level of the audio stream that enters or leaves - // the audio endpoint device as an index or "step" in a range of discrete volume levels. - // Output value nStepCount is the number of steps in the range. Output value nStep - // is the step index of the current volume level. If the number of steps is n = nStepCount, - // then step index nStep can assume values from 0 (minimum volume) to n ?1 (maximum volume). - UINT nStep(0); - UINT nStepCount(0); - hr = pEndpointVolume->GetVolumeStepInfo( - &nStep, - &nStepCount); - EXIT_ON_ERROR(hr); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "volume steps : %d (nStep), %d (nStepCount)", nStep, nStepCount); - } - - CoTaskMemFree(pwszID); - pwszID = NULL; - PropVariantClear(&varName); - SAFE_RELEASE(pProps); - SAFE_RELEASE(pEndpoint); - SAFE_RELEASE(pEndpointVolume); - } - SAFE_RELEASE(pCollection); - return 0; - -Exit: - _TraceCOMError(hr); - CoTaskMemFree(pwszID); - pwszID = NULL; - SAFE_RELEASE(pCollection); - SAFE_RELEASE(pEndpoint); - SAFE_RELEASE(pEndpointVolume); - SAFE_RELEASE(pProps); - return -1; -} - -// ---------------------------------------------------------------------------- -// _TraceCOMError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::_TraceCOMError(HRESULT hr) const -{ - TCHAR buf[MAXERRORLENGTH]; - LPCTSTR errorText; - - _com_error error(hr); - errorText = error.ErrorMessage(); - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Core Audio method failed (hr=0x%x)", hr); - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - StringCchCat(buf, MAXERRORLENGTH, errorText); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// _SetThreadName -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::_SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) -{ - // See http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx for details on the code - // in this function. Name of article is "Setting a Thread Name (Unmanaged)". - - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info ); - } - __except (EXCEPTION_CONTINUE_EXECUTION) - { - } -} - -// ---------------------------------------------------------------------------- -// _Get44kHzDrift -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsCore::_Get44kHzDrift() -{ - // We aren't able to resample at 44.1 kHz. Instead we run at 44 kHz and push/pull - // from the engine faster to compensate. If only one direction is set to 44.1 kHz - // the result is indistinguishable from clock drift to the AEC. We can compensate - // internally if we inform the AEC about the drift. - _sampleDriftAt48kHz = 0; - _driftAccumulator = 0; - - if (_playSampleRate == 44000 && _recSampleRate != 44000) - { - _sampleDriftAt48kHz = 480.0f/440; - } - else if(_playSampleRate != 44000 && _recSampleRate == 44000) - { - _sampleDriftAt48kHz = -480.0f/441; - } -} - -// ---------------------------------------------------------------------------- -// WideToUTF8 -// ---------------------------------------------------------------------------- - -char* AudioDeviceWindowsCore::WideToUTF8(const TCHAR* src) const { -#ifdef UNICODE - const size_t kStrLen = sizeof(_str); - memset(_str, 0, kStrLen); - // Get required size (in bytes) to be able to complete the conversion. - int required_size = WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, 0, 0, 0); - if (required_size <= kStrLen) - { - // Process the entire input string, including the terminating null char. - if (WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, kStrLen, 0, 0) == 0) - memset(_str, 0, kStrLen); - } - return _str; -#else - return const_cast(src); -#endif -} - -} // namespace webrtc - -#endif // WEBRTC_WINDOWS_CORE_AUDIO_BUILD diff --git a/modules/audio_device/main/source/Windows/audio_device_windows_core.h b/modules/audio_device/main/source/Windows/audio_device_windows_core.h deleted file mode 100644 index 8f9f6fbeb..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_windows_core.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_CORE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_CORE_H - -#if (_MSC_VER >= 1400) // only include for VS 2005 and higher - -#include "audio_device_generic.h" - -#pragma once -#include // MMDevice -#include // WASAPI -#include // Avrt -#include -#include - -#include "critical_section_wrapper.h" - -// Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority -#pragma comment( lib, "avrt.lib" ) -// AVRT function pointers -typedef BOOL (WINAPI *PAvRevertMmThreadCharacteristics)(HANDLE); -typedef HANDLE (WINAPI *PAvSetMmThreadCharacteristicsA)(LPCSTR, LPDWORD); -typedef BOOL (WINAPI *PAvSetMmThreadPriority)(HANDLE, AVRT_PRIORITY); - -namespace webrtc { - -const float MAX_CORE_SPEAKER_VOLUME = 255.0f; -const float MIN_CORE_SPEAKER_VOLUME = 0.0f; -const float MAX_CORE_MICROPHONE_VOLUME = 255.0f; -const float MIN_CORE_MICROPHONE_VOLUME = 0.0f; -const WebRtc_UWord16 CORE_SPEAKER_VOLUME_STEP_SIZE = 1; -const WebRtc_UWord16 CORE_MICROPHONE_VOLUME_STEP_SIZE = 1; - -class AudioDeviceWindowsCore : public AudioDeviceGeneric -{ -public: - AudioDeviceWindowsCore(const WebRtc_Word32 id); - ~AudioDeviceWindowsCore(); - - static bool CoreAudioIsSupported(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: // avrt function pointers - PAvRevertMmThreadCharacteristics _PAvRevertMmThreadCharacteristics; - PAvSetMmThreadCharacteristicsA _PAvSetMmThreadCharacteristicsA; - PAvSetMmThreadPriority _PAvSetMmThreadPriority; - HMODULE _avrtLibrary; - bool _winSupportAvrt; - -private: // thread functions - static DWORD WINAPI WSAPICaptureThread(LPVOID context); - DWORD DoCaptureThread(); - - static DWORD WINAPI WSAPIRenderThread(LPVOID context); - DWORD DoRenderThread(); - - static DWORD WINAPI GetCaptureVolumeThread(LPVOID context); - DWORD DoGetCaptureVolumeThread(); - - static DWORD WINAPI SetCaptureVolumeThread(LPVOID context); - DWORD DoSetCaptureVolumeThread(); - - void _SetThreadName(DWORD dwThreadID, LPCSTR szThreadName); - void _Lock() { _critSect.Enter(); }; - void _UnLock() { _critSect.Leave(); }; - -private: - WebRtc_Word32 Id() {return _id;} - -private: - WebRtc_Word32 _EnumerateEndpointDevicesAll(EDataFlow dataFlow) const; - void _TraceCOMError(HRESULT hr) const; - - WebRtc_Word32 _RefreshDeviceList(EDataFlow dir); - WebRtc_Word16 _DeviceListCount(EDataFlow dir); - WebRtc_Word32 _GetDefaultDeviceName(EDataFlow dir, ERole role, LPWSTR szBuffer, int bufferLen); - WebRtc_Word32 _GetListDeviceName(EDataFlow dir, int index, LPWSTR szBuffer, int bufferLen); - WebRtc_Word32 _GetDeviceName(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen); - WebRtc_Word32 _GetListDeviceID(EDataFlow dir, int index, LPWSTR szBuffer, int bufferLen); - WebRtc_Word32 _GetDefaultDeviceID(EDataFlow dir, ERole role, LPWSTR szBuffer, int bufferLen); - WebRtc_Word32 _GetDeviceID(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen); - WebRtc_Word32 _GetDefaultDevice(EDataFlow dir, ERole role, IMMDevice** ppDevice); - WebRtc_Word32 _GetListDevice(EDataFlow dir, int index, IMMDevice** ppDevice); - - void _Get44kHzDrift(); - - // Converts from wide-char to UTF-8 if UNICODE is defined. - // Does nothing if UNICODE is undefined. - char* WideToUTF8(const TCHAR* src) const; - -private: - AudioDeviceBuffer* _ptrAudioBuffer; - CriticalSectionWrapper& _critSect; - CriticalSectionWrapper& _volumeMutex; - WebRtc_Word32 _id; - -private: // MMDevice - IMMDeviceEnumerator* _ptrEnumerator; - IMMDeviceCollection* _ptrRenderCollection; - IMMDeviceCollection* _ptrCaptureCollection; - IMMDevice* _ptrDeviceOut; - IMMDevice* _ptrDeviceIn; - -private: // WASAPI - IAudioClient* _ptrClientOut; - IAudioClient* _ptrClientIn; - IAudioRenderClient* _ptrRenderClient; - IAudioCaptureClient* _ptrCaptureClient; - IAudioEndpointVolume* _ptrCaptureVolume; - ISimpleAudioVolume* _ptrRenderSimpleVolume; - IAudioEndpointVolume* _ptrRenderEndpointVolume; - - HANDLE _hRenderSamplesReadyEvent; - HANDLE _hPlayThread; - HANDLE _hRenderStartedEvent; - HANDLE _hShutdownRenderEvent; - - HANDLE _hCaptureSamplesReadyEvent; - HANDLE _hRecThread; - HANDLE _hCaptureStartedEvent; - HANDLE _hShutdownCaptureEvent; - - HANDLE _hGetCaptureVolumeThread; - HANDLE _hSetCaptureVolumeThread; - HANDLE _hSetCaptureVolumeEvent; - - UINT _playAudioFrameSize; - WebRtc_UWord32 _playSampleRate; - WebRtc_UWord32 _devicePlaySampleRate; - WebRtc_UWord32 _playBlockSize; - WebRtc_UWord32 _devicePlayBlockSize; - WebRtc_UWord32 _playChannels; - WebRtc_UWord32 _sndCardPlayDelay; - UINT64 _writtenSamples; - LONGLONG _playAcc; - - UINT _recAudioFrameSize; - WebRtc_UWord32 _recSampleRate; - WebRtc_UWord32 _recBlockSize; - WebRtc_UWord32 _recChannels; - UINT64 _readSamples; - WebRtc_UWord32 _sndCardRecDelay; - - float _sampleDriftAt48kHz; - float _driftAccumulator; - - WebRtc_UWord16 _recChannelsPrioList[2]; - WebRtc_UWord16 _playChannelsPrioList[2]; - - LARGE_INTEGER _perfCounterFreq; - double _perfCounterFactor; - float _avgCPULoad; - -private: - bool _coUninitializeIsRequired; - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _speakerIsInitialized; - bool _microphoneIsInitialized; - - bool _usingInputDeviceIndex; - bool _usingOutputDeviceIndex; - AudioDeviceModule::WindowsDeviceType _inputDevice; - AudioDeviceModule::WindowsDeviceType _outputDevice; - WebRtc_UWord16 _inputDeviceIndex; - WebRtc_UWord16 _outputDeviceIndex; - - bool _AGC; - - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - AudioDeviceModule::BufferType _playBufType; - WebRtc_UWord16 _playBufDelay; - WebRtc_UWord16 _playBufDelayFixed; - - WebRtc_UWord16 _newMicLevel; - - mutable char _str[512]; -}; - -#endif // #if (_MSC_VER >= 1400) - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_CORE_H - diff --git a/modules/audio_device/main/source/Windows/audio_device_windows_wave.cc b/modules/audio_device/main/source/Windows/audio_device_windows_wave.cc deleted file mode 100644 index 15b46bfca..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_windows_wave.cc +++ /dev/null @@ -1,3877 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_utility.h" -#include "audio_device_windows_wave.h" -#include "audio_device_config.h" - -#include "trace.h" -#include "thread_wrapper.h" -#include "event_wrapper.h" - -#include -#include // CoTaskMemAlloc, CoTaskMemFree -#include // StringCchCopy(), StringCchCat(), StringCchPrintf() -#include - -// Avoids the need of Windows 7 SDK -#ifndef WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE -#define WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE 0x0010 -#endif - -// Supported in Windows Vista and Windows 7. -// http://msdn.microsoft.com/en-us/library/dd370819(v=VS.85).aspx -// Taken from Mmddk.h. -#define DRV_RESERVED 0x0800 -#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) -#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) - -#define POW2(A) (2 << ((A) - 1)) - -namespace webrtc { - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceWindowsWave - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceWindowsWave::AudioDeviceWindowsWave(const WebRtc_Word32 id) : - _ptrAudioBuffer(NULL), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _timeEvent(*EventWrapper::Create()), - _recStartEvent(*EventWrapper::Create()), - _playStartEvent(*EventWrapper::Create()), - _hGetCaptureVolumeThread(NULL), - _hShutdownGetVolumeEvent(NULL), - _hSetCaptureVolumeThread(NULL), - _hShutdownSetVolumeEvent(NULL), - _hSetCaptureVolumeEvent(NULL), - _ptrThread(NULL), - _threadID(0), - _critSectCb(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _mixerManager(id), - _usingInputDeviceIndex(false), - _usingOutputDeviceIndex(false), - _inputDevice(AudioDeviceModule::kDefaultDevice), - _outputDevice(AudioDeviceModule::kDefaultDevice), - _inputDeviceIndex(0), - _outputDeviceIndex(0), - _inputDeviceIsSpecified(false), - _outputDeviceIsSpecified(false), - _initialized(false), - _recIsInitialized(false), - _playIsInitialized(false), - _recording(false), - _playing(false), - _startRec(false), - _stopRec(false), - _startPlay(false), - _stopPlay(false), - _AGC(false), - _hWaveIn(NULL), - _hWaveOut(NULL), - _recChannels(N_REC_CHANNELS), - _playChannels(N_PLAY_CHANNELS), - _recBufCount(0), - _recPutBackDelay(0), - _recDelayCount(0), - _playBufCount(0), - _prevPlayTime(0), - _prevRecTime(0), - _prevTimerCheckTime(0), - _timesdwBytes(0), - _timerFaults(0), - _timerRestartAttempts(0), - _no_of_msecleft_warnings(0), - _MAX_minBuffer(65), - _useHeader(0), - _dTcheckPlayBufDelay(10), - _playBufDelay(80), - _playBufDelayFixed(80), - _minPlayBufDelay(20), - _avgCPULoad(0), - _sndCardPlayDelay(0), - _sndCardRecDelay(0), - _plSampOld(0), - _rcSampOld(0), - _playBufType(AudioDeviceModule::kAdaptiveBufferSize), - _recordedBytes(0), - _playWarning(0), - _playError(0), - _recWarning(0), - _recError(0), - _newMicLevel(0), - _minMicVolume(0), - _maxMicVolume(1) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); - - // Initialize value, set to 0 if it fails - if (!QueryPerformanceFrequency(&_perfFreq)) - { - _perfFreq.QuadPart = 0; - } - - _hShutdownGetVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hShutdownSetVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - _hSetCaptureVolumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); -} - -// ---------------------------------------------------------------------------- -// AudioDeviceWindowsWave - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceWindowsWave::~AudioDeviceWindowsWave() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - - Terminate(); - - delete &_recStartEvent; - delete &_playStartEvent; - delete &_timeEvent; - delete &_critSect; - delete &_critSectCb; - - if (NULL != _hShutdownGetVolumeEvent) - { - CloseHandle(_hShutdownGetVolumeEvent); - _hShutdownGetVolumeEvent = NULL; - } - - if (NULL != _hShutdownSetVolumeEvent) - { - CloseHandle(_hShutdownSetVolumeEvent); - _hShutdownSetVolumeEvent = NULL; - } - - if (NULL != _hSetCaptureVolumeEvent) - { - CloseHandle(_hSetCaptureVolumeEvent); - _hSetCaptureVolumeEvent = NULL; - } -} - -// ============================================================================ -// API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AttachAudioBuffer -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _ptrAudioBuffer = audioBuffer; - - // inform the AudioBuffer about default settings for this implementation - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS); -} - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - audioLayer = AudioDeviceModule::kWindowsWaveAudio; - return 0; -} - -// ---------------------------------------------------------------------------- -// Init -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_initialized) - { - return 0; - } - - const WebRtc_UWord32 nowTime(AudioDeviceUtility::GetTimeInMS()); - - _recordedBytes = 0; - _prevRecByteCheckTime = nowTime; - _prevRecTime = nowTime; - _prevPlayTime = nowTime; - _prevTimerCheckTime = nowTime; - - _playWarning = 0; - _playError = 0; - _recWarning = 0; - _recError = 0; - - _mixerManager.EnumerateAll(); - - if (_ptrThread) - { - // thread is already created and active - return 0; - } - - const char* threadName = "webrtc_audio_module_thread"; - _ptrThread = ThreadWrapper::CreateThread(ThreadFunc, - this, - kRealtimePriority, - threadName); - if (_ptrThread == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - "failed to create the audio thread"); - return -1; - } - - unsigned int threadID(0); - if (!_ptrThread->Start(threadID)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - "failed to start the audio thread"); - delete _ptrThread; - _ptrThread = NULL; - return -1; - } - _threadID = threadID; - - const bool periodic(true); - if (!_timeEvent.StartTimer(periodic, TIMER_PERIOD_MS)) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, - "failed to start the timer event"); - if (_ptrThread->Stop()) - { - delete _ptrThread; - _ptrThread = NULL; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "unable to stop the activated thread"); - } - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "periodic timer (dT=%d) is now active", TIMER_PERIOD_MS); - - _hGetCaptureVolumeThread = CreateThread(NULL, - 0, - GetCaptureVolumeThread, - this, - 0, - NULL); - if (_hGetCaptureVolumeThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create the volume getter thread"); - return -1; - } - - SetThreadPriority(_hGetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); - - _hSetCaptureVolumeThread = CreateThread(NULL, - 0, - SetCaptureVolumeThread, - this, - 0, - NULL); - if (_hSetCaptureVolumeThread == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to create the volume setter thread"); - return -1; - } - - SetThreadPriority(_hSetCaptureVolumeThread, THREAD_PRIORITY_NORMAL); - - _initialized = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// Terminate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_initialized) - { - return 0; - } - - _critSect.Enter(); - - _mixerManager.Close(); - - if (_ptrThread) - { - ThreadWrapper* tmpThread = _ptrThread; - _ptrThread = NULL; - _critSect.Leave(); - - tmpThread->SetNotAlive(); - _timeEvent.Set(); - - if (tmpThread->Stop()) - { - delete tmpThread; - } - else - { - _critSect.Leave(); - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - "failed to close down the audio thread"); - return -1; - } - } - else - { - _critSect.Leave(); - } - - _critSect.Enter(); - SetEvent(_hShutdownGetVolumeEvent); - _critSect.Leave(); - WebRtc_Word32 ret = WaitForSingleObject(_hGetCaptureVolumeThread, 2000); - if (ret != WAIT_OBJECT_0) - { - // the thread did not stop as it should - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to close down volume getter thread"); - CloseHandle(_hGetCaptureVolumeThread); - _hGetCaptureVolumeThread = NULL; - return -1; - } - _critSect.Enter(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " volume getter thread is now closed"); - - SetEvent(_hShutdownSetVolumeEvent); - _critSect.Leave(); - ret = WaitForSingleObject(_hSetCaptureVolumeThread, 2000); - if (ret != WAIT_OBJECT_0) - { - // the thread did not stop as it should - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " failed to close down volume setter thread"); - CloseHandle(_hSetCaptureVolumeThread); - _hSetCaptureVolumeThread = NULL; - return -1; - } - _critSect.Enter(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - " volume setter thread is now closed"); - - CloseHandle(_hGetCaptureVolumeThread); - _hGetCaptureVolumeThread = NULL; - - CloseHandle(_hSetCaptureVolumeThread); - _hSetCaptureVolumeThread = NULL; - - _critSect.Leave(); - - _timeEvent.StopTimer(); - - _initialized = false; - _outputDeviceIsSpecified = false; - _inputDeviceIsSpecified = false; - - return 0; -} - - -DWORD WINAPI AudioDeviceWindowsWave::GetCaptureVolumeThread(LPVOID context) -{ - return(((AudioDeviceWindowsWave*)context)->DoGetCaptureVolumeThread()); -} - -DWORD WINAPI AudioDeviceWindowsWave::SetCaptureVolumeThread(LPVOID context) -{ - return(((AudioDeviceWindowsWave*)context)->DoSetCaptureVolumeThread()); -} - -DWORD AudioDeviceWindowsWave::DoGetCaptureVolumeThread() -{ - HANDLE waitObject = _hShutdownGetVolumeEvent; - - while (1) - { - DWORD waitResult = WaitForSingleObject(waitObject, - GET_MIC_VOLUME_INTERVAL_MS); - switch (waitResult) - { - case WAIT_OBJECT_0: // _hShutdownGetVolumeEvent - return 0; - case WAIT_TIMEOUT: // timeout notification - break; - default: // unexpected error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unknown wait termination on get volume thread"); - return -1; - } - - if (AGC()) - { - WebRtc_UWord32 currentMicLevel = 0; - if (MicrophoneVolume(currentMicLevel) == 0) - { - // This doesn't set the system volume, just stores it. - _critSect.Enter(); - if (_ptrAudioBuffer) - { - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); - } - _critSect.Leave(); - } - } - } -} - -DWORD AudioDeviceWindowsWave::DoSetCaptureVolumeThread() -{ - HANDLE waitArray[2] = {_hShutdownSetVolumeEvent, _hSetCaptureVolumeEvent}; - - while (1) - { - DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE); - switch (waitResult) - { - case WAIT_OBJECT_0: // _hShutdownSetVolumeEvent - return 0; - case WAIT_OBJECT_0 + 1: // _hSetCaptureVolumeEvent - break; - default: // unexpected error - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " unknown wait termination on set volume thread"); - return -1; - } - - _critSect.Enter(); - WebRtc_UWord32 newMicLevel = _newMicLevel; - _critSect.Leave(); - - if (SetMicrophoneVolume(newMicLevel) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, - " the required modification of the microphone volume failed"); - } - } - return 0; -} - -// ---------------------------------------------------------------------------- -// Initialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_initialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // Enumerate all avaliable speakers and make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (InitSpeaker() == -1) - { - available = false; - return 0; - } - - // Given that InitSpeaker was successful, we know that a valid speaker exists - // - available = true; - - // Close the initialized output mixer - // - _mixerManager.CloseSpeaker(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (_mixerManager.EnumerateSpeakers() == -1) - { - // failed to locate any valid/controllable speaker - return -1; - } - - if (IsUsingOutputDeviceIndex()) - { - if (_mixerManager.OpenSpeaker(OutputDeviceIndex()) == -1) - { - return -1; - } - } - else - { - if (_mixerManager.OpenSpeaker(OutputDevice()) == -1) - { - return -1; - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // Enumerate all avaliable microphones and make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (InitMicrophone() == -1) - { - available = false; - return 0; - } - - // Given that InitMicrophone was successful, we know that a valid microphone exists - // - available = true; - - // Close the initialized input mixer - // - _mixerManager.CloseMicrophone(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (_mixerManager.EnumerateMicrophones() == -1) - { - // failed to locate any valid/controllable microphone - return -1; - } - - if (IsUsingInputDeviceIndex()) - { - if (_mixerManager.OpenMicrophone(InputDeviceIndex()) == -1) - { - return -1; - } - } - else - { - if (_mixerManager.OpenMicrophone(InputDevice()) == -1) - { - return -1; - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_mixerManager.SpeakerIsInitialized()); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_mixerManager.MicrophoneIsInitialized()); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool isAvailable(false); - - // Enumerate all avaliable speakers and make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (InitSpeaker() == -1) - { - // failed to find a valid speaker - available = false; - return 0; - } - - // Check if the selected speaker has a volume control - // - _mixerManager.SpeakerVolumeIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized output mixer - // - _mixerManager.CloseSpeaker(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetSpeakerVolume(volume=%u)", volume); - - return (_mixerManager.SetSpeakerVolume(volume)); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord32 level(0); - - if (_mixerManager.SpeakerVolume(level) == -1) - { - return -1; - } - - volume = level; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// -// The low-order word contains the left-channel volume setting, and the -// high-order word contains the right-channel setting. -// A value of 0xFFFF represents full volume, and a value of 0x0000 is silence. -// -// If a device does not support both left and right volume control, -// the low-order word of dwVolume specifies the volume level, -// and the high-order word is ignored. -// -// Most devices do not support the full 16 bits of volume-level control -// and will not use the least-significant bits of the requested volume setting. -// For example, if a device supports 4 bits of volume control, the values -// 0x4000, 0x4FFF, and 0x43BE will all be truncated to 0x4000. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - - MMRESULT res(0); - WAVEOUTCAPS caps; - - CriticalSectionScoped lock(_critSect); - - if (_hWaveOut == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no open playout device exists => using default"); - } - - // To determine whether the device supports volume control on both - // the left and right channels, use the WAVECAPS_LRVOLUME flag. - // - res = waveOutGetDevCaps((UINT_PTR)_hWaveOut, &caps, sizeof(WAVEOUTCAPS)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); - TraceWaveOutError(res); - } - if (!(caps.dwSupport & WAVECAPS_VOLUME)) - { - // this device does not support volume control using the waveOutSetVolume API - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device does not support volume control using the Wave API"); - return -1; - } - if (!(caps.dwSupport & WAVECAPS_LRVOLUME)) - { - // high-order word (right channel) is ignored - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "device does not support volume control on both channels"); - } - - DWORD dwVolume(0x00000000); - dwVolume = (DWORD)(((volumeRight & 0xFFFF) << 16) | (volumeLeft & 0xFFFF)); - - res = waveOutSetVolume(_hWaveOut, dwVolume); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutSetVolume() failed (err=%d)", res); - TraceWaveOutError(res); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// -// The low-order word of this location contains the left-channel volume setting, -// and the high-order word contains the right-channel setting. -// A value of 0xFFFF (65535) represents full volume, and a value of 0x0000 -// is silence. -// -// If a device does not support both left and right volume control, -// the low-order word of the specified location contains the mono volume level. -// -// The full 16-bit setting(s) set with the waveOutSetVolume function is returned, -// regardless of whether the device supports the full 16 bits of volume-level -// control. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - MMRESULT res(0); - WAVEOUTCAPS caps; - - CriticalSectionScoped lock(_critSect); - - if (_hWaveOut == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no open playout device exists => using default"); - } - - // To determine whether the device supports volume control on both - // the left and right channels, use the WAVECAPS_LRVOLUME flag. - // - res = waveOutGetDevCaps((UINT_PTR)_hWaveOut, &caps, sizeof(WAVEOUTCAPS)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); - TraceWaveOutError(res); - } - if (!(caps.dwSupport & WAVECAPS_VOLUME)) - { - // this device does not support volume control using the waveOutSetVolume API - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device does not support volume control using the Wave API"); - return -1; - } - if (!(caps.dwSupport & WAVECAPS_LRVOLUME)) - { - // high-order word (right channel) is ignored - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "device does not support volume control on both channels"); - } - - DWORD dwVolume(0x00000000); - - res = waveOutGetVolume(_hWaveOut, &dwVolume); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutGetVolume() failed (err=%d)", res); - TraceWaveOutError(res); - return -1; - } - - WORD wVolumeLeft = LOWORD(dwVolume); - WORD wVolumeRight = HIWORD(dwVolume); - - volumeLeft = static_cast (wVolumeLeft); - volumeRight = static_cast (wVolumeRight); - - return 0; -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord32 maxVol(0); - - if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) - { - return -1; - } - - maxVolume = maxVol; - return 0; -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord32 minVol(0); - - if (_mixerManager.MinSpeakerVolume(minVol) == -1) - { - return -1; - } - - minVolume = minVol; - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool isAvailable(false); - - // Enumerate all avaliable speakers and make an attempt to open up the - // output mixer corresponding to the currently selected output device. - // - if (InitSpeaker() == -1) - { - // If we end up here it means that the selected speaker has no volume - // control, hence it is safe to state that there is no mute control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected speaker has a mute control - // - _mixerManager.SpeakerMuteIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized output mixer - // - _mixerManager.CloseSpeaker(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetSpeakerMute(enable=%u)", enable); - return (_mixerManager.SetSpeakerMute(enable)); -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.SpeakerMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneMuteIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool isAvailable(false); - - // Enumerate all avaliable microphones and make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a mute control - // - _mixerManager.MicrophoneMuteIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - _mixerManager.CloseMicrophone(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetMicrophoneMute(enable=%u)", enable); - return (_mixerManager.SetMicrophoneMute(enable)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool muted(0); - - if (_mixerManager.MicrophoneMute(muted) == -1) - { - return -1; - } - - enabled = muted; - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneBoostIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool isAvailable(false); - - // Enumerate all avaliable microphones and make an attempt to open up the - // input mixer corresponding to the currently selected input device. - // - if (InitMicrophone() == -1) - { - // If we end up here it means that the selected microphone has no volume - // control, hence it is safe to state that there is no boost control - // already at this stage. - available = false; - return 0; - } - - // Check if the selected microphone has a boost control - // - _mixerManager.MicrophoneBoostIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - _mixerManager.CloseMicrophone(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetMicrophoneBoost(enable=%u)", enable); - - return (_mixerManager.SetMicrophoneBoost(enable)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool onOff(0); - - if (_mixerManager.MicrophoneBoost(onOff) == -1) - { - return -1; - } - - enabled = onOff; - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StereoRecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - available = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetStereoRecording(enable=%u)", enable); - - if (enable) - _recChannels = 2; - else - _recChannels = 1; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StereoRecording(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_recChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StereoPlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - available = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetStereoPlayout -// -// Specifies the number of output channels. -// -// NOTE - the setting will only have an effect after InitPlayout has -// been called. -// -// 16-bit mono: -// -// Each sample is 2 bytes. Sample 1 is followed by samples 2, 3, 4, and so on. -// For each sample, the first byte is the low-order byte of channel 0 and the -// second byte is the high-order byte of channel 0. -// -// 16-bit stereo: -// -// Each sample is 4 bytes. Sample 1 is followed by samples 2, 3, 4, and so on. -// For each sample, the first byte is the low-order byte of channel 0 (left channel); -// the second byte is the high-order byte of channel 0; the third byte is the -// low-order byte of channel 1 (right channel); and the fourth byte is the -// high-order byte of channel 1. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetStereoPlayout(enable=%u)", enable); - - if (enable) - _playChannels = 2; - else - _playChannels = 1; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StereoPlayout(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_playChannels == 2) - enabled = true; - else - enabled = false; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetAGC -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetAGC(enable=%d)", enable); - - _AGC = enable; - - return 0; -} - -// ---------------------------------------------------------------------------- -// AGC -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::AGC() const -{ - return _AGC; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneVolumeIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - bool isAvailable(false); - - // Enumerate all avaliable microphones and make an attempt to open up the - // input mixer corresponding to the currently selected output device. - // - if (InitMicrophone() == -1) - { - // Failed to find valid microphone - available = false; - return 0; - } - - // Check if the selected microphone has a volume control - // - _mixerManager.MicrophoneVolumeIsAvailable(isAvailable); - available = isAvailable; - - // Close the initialized input mixer - // - _mixerManager.CloseMicrophone(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - return (_mixerManager.SetMicrophoneVolume(volume)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - WebRtc_UWord32 level(0); - - if (_mixerManager.MicrophoneVolume(level) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to retrive current microphone level"); - return -1; - } - - volume = level; - return 0; -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - maxVolume = _maxMicVolume;; - return 0; -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - minVolume = _minMicVolume; - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord16 delta(0); - - if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) - { - return -1; - } - - stepSize = delta; - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceWindowsWave::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (waveOutGetNumDevs()); -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetPlayoutDevice(index=%u)", index); - - if (_playIsInitialized) - { - return -1; - } - - UINT nDevices = waveOutGetNumDevs(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "number of availiable waveform-audio output devices is %u", nDevices); - - if (index < 0 || index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - _usingOutputDeviceIndex = true; - _outputDeviceIndex = index; - _outputDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device) -{ - if (_playIsInitialized) - { - return -1; - } - - if (device == AudioDeviceModule::kDefaultDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetPlayoutDevice(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetPlayoutDevice(kDefaultCommunicationDevice)"); - } - - _usingOutputDeviceIndex = false; - _outputDevice = device; - _outputDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::PlayoutDeviceName(index=%u)", index); - - WebRtc_UWord16 nDevices(PlayoutDevices()); - - // Special fix for the case when the user asks for the name of the default device. - // - if (index == (WebRtc_UWord16)(-1)) - { - index = 0; - } - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - WAVEOUTCAPSW caps; // szPname member (product name (NULL terminated) is a WCHAR - MMRESULT res; - - res = waveOutGetDevCapsW(index, &caps, sizeof(WAVEOUTCAPSW)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCapsW() failed (err=%d)", res); - return -1; - } - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 1", GetLastError()); - } - - if (guid == NULL) - { - return 0; - } - - // It is possible to get the unique endpoint ID string using the Wave API. - // However, it is only supported on Windows Vista and Windows 7. - - size_t cbEndpointId(0); - - // Get the size (including the terminating null) of the endpoint ID string of the waveOut device. - // Windows Vista supports the DRV_QUERYFUNCTIONINSTANCEIDSIZE and DRV_QUERYFUNCTIONINSTANCEID messages. - res = waveOutMessage((HWAVEOUT)IntToPtr(index), - DRV_QUERYFUNCTIONINSTANCEIDSIZE, - (DWORD_PTR)&cbEndpointId, NULL); - if (res != MMSYSERR_NOERROR) - { - // DRV_QUERYFUNCTIONINSTANCEIDSIZE is not supported <=> earlier version of Windows than Vista - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) failed (err=%d)", res); - TraceWaveOutError(res); - // Best we can do is to copy the friendly name and use it as guid - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 2", GetLastError()); - } - return 0; - } - - // waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) worked => we are on a Vista or Windows 7 device - - WCHAR *pstrEndpointId = NULL; - pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointId); - - // Get the endpoint ID string for this waveOut device. - res = waveOutMessage((HWAVEOUT)IntToPtr(index), - DRV_QUERYFUNCTIONINSTANCEID, - (DWORD_PTR)pstrEndpointId, - cbEndpointId); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveOutMessage(DRV_QUERYFUNCTIONINSTANCEID) failed (err=%d)", res); - TraceWaveOutError(res); - // Best we can do is to copy the friendly name and use it as guid - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 3", GetLastError()); - } - CoTaskMemFree(pstrEndpointId); - return 0; - } - - if (WideCharToMultiByte(CP_UTF8, 0, pstrEndpointId, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 4", GetLastError()); - } - CoTaskMemFree(pstrEndpointId); - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::RecordingDeviceName(index=%u)", index); - - WebRtc_UWord16 nDevices(RecordingDevices()); - - // Special fix for the case when the user asks for the name of the default device. - // - if (index == (WebRtc_UWord16)(-1)) - { - index = 0; - } - - if ((index > (nDevices-1)) || (name == NULL)) - { - return -1; - } - - memset(name, 0, kAdmMaxDeviceNameSize); - - if (guid != NULL) - { - memset(guid, 0, kAdmMaxGuidSize); - } - - WAVEINCAPSW caps; // szPname member (product name (NULL terminated) is a WCHAR - MMRESULT res; - - res = waveInGetDevCapsW(index, &caps, sizeof(WAVEINCAPSW)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCapsW() failed (err=%d)", res); - return -1; - } - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, name, kAdmMaxDeviceNameSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 1", GetLastError()); - } - - if (guid == NULL) - { - return 0; - } - - // It is possible to get the unique endpoint ID string using the Wave API. - // However, it is only supported on Windows Vista and Windows 7. - - size_t cbEndpointId(0); - - // Get the size (including the terminating null) of the endpoint ID string of the waveOut device. - // Windows Vista supports the DRV_QUERYFUNCTIONINSTANCEIDSIZE and DRV_QUERYFUNCTIONINSTANCEID messages. - res = waveInMessage((HWAVEIN)IntToPtr(index), - DRV_QUERYFUNCTIONINSTANCEIDSIZE, - (DWORD_PTR)&cbEndpointId, NULL); - if (res != MMSYSERR_NOERROR) - { - // DRV_QUERYFUNCTIONINSTANCEIDSIZE is not supported <=> earlier version of Windows than Vista - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) failed (err=%d)", res); - TraceWaveInError(res); - // Best we can do is to copy the friendly name and use it as guid - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 2", GetLastError()); - } - return 0; - } - - // waveOutMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE) worked => we are on a Vista or Windows 7 device - - WCHAR *pstrEndpointId = NULL; - pstrEndpointId = (WCHAR*)CoTaskMemAlloc(cbEndpointId); - - // Get the endpoint ID string for this waveOut device. - res = waveInMessage((HWAVEIN)IntToPtr(index), - DRV_QUERYFUNCTIONINSTANCEID, - (DWORD_PTR)pstrEndpointId, - cbEndpointId); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInMessage(DRV_QUERYFUNCTIONINSTANCEID) failed (err=%d)", res); - TraceWaveInError(res); - // Best we can do is to copy the friendly name and use it as guid - if (WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 3", GetLastError()); - } - CoTaskMemFree(pstrEndpointId); - return 0; - } - - if (WideCharToMultiByte(CP_UTF8, 0, pstrEndpointId, -1, guid, kAdmMaxGuidSize, NULL, NULL) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "WideCharToMultiByte(CP_UTF8) failed with error code %d - 4", GetLastError()); - } - CoTaskMemFree(pstrEndpointId); - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceWindowsWave::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (waveInGetNumDevs()); -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetRecordingDevice(index=%u)", index); - - if (_recIsInitialized) - { - return -1; - } - - UINT nDevices = waveInGetNumDevs(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "number of availiable waveform-audio input devices is %u", nDevices); - - if (index < 0 || index > (nDevices-1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "device index is out of range [0,%u]", (nDevices-1)); - return -1; - } - - _usingInputDeviceIndex = true; - _inputDeviceIndex = index; - _inputDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device) -{ - if (device == AudioDeviceModule::kDefaultDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetRecordingDevice(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetRecordingDevice(kDefaultCommunicationDevice)"); - } - - if (_recIsInitialized) - { - return -1; - } - - _usingInputDeviceIndex = false; - _inputDevice = device; - _inputDeviceIsSpecified = true; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PlayoutIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the playout side - WebRtc_Word32 res = InitPlayout(); - - // Cancel effect of initialization - StopPlayout(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::RecordingIsAvailable(bool& available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - available = false; - - // Try to initialize the recording side - WebRtc_Word32 res = InitRecording(); - - // Cancel effect of initialization - StopRecording(); - - if (res != -1) - { - available = true; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_playing) - { - return -1; - } - - if (!_outputDeviceIsSpecified) - { - return -1; - } - - if (_playIsInitialized) - { - return 0; - } - - // Initialize the speaker (devices might have been added or removed) - if (InitSpeaker() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitSpeaker() failed"); - } - - // Enumerate all availiable output devices - EnumeratePlayoutDevices(); - - // Start by closing any existing wave-output devices - // - MMRESULT res(MMSYSERR_ERROR); - - if (_hWaveOut != NULL) - { - res = waveOutClose(_hWaveOut); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutClose() failed (err=%d)", res); - TraceWaveOutError(res); - } - } - - // Set the output wave format - // - WAVEFORMATEX waveFormat; - - waveFormat.wFormatTag = WAVE_FORMAT_PCM; - waveFormat.nChannels = _playChannels; // mono <=> 1, stereo <=> 2 - waveFormat.nSamplesPerSec = N_PLAY_SAMPLES_PER_SEC; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * (waveFormat.wBitsPerSample/8); - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; - - // Open the given waveform-audio output device for playout - // - HWAVEOUT hWaveOut(NULL); - - if (IsUsingOutputDeviceIndex()) - { - // verify settings first - res = waveOutOpen(NULL, _outputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // open the given waveform-audio output device for recording - res = waveOutOpen(&hWaveOut, _outputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening output device corresponding to device ID %u", _outputDeviceIndex); - } - } - else - { - if (_outputDevice == AudioDeviceModule::kDefaultCommunicationDevice) - { - // check if it is possible to open the default communication device (supported on Windows 7) - res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // if so, open the default communication device for real - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); - } - else - { - // use default device since default communication device was not avaliable - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to open default communication device => using default instead"); - } - } - else if (_outputDevice == AudioDeviceModule::kDefaultDevice) - { - // open default device since it has been requested - res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default output device"); - } - } - } - - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutOpen() failed (err=%d)", res); - TraceWaveOutError(res); - return -1; - } - - // Log information about the aquired output device - // - WAVEOUTCAPS caps; - - res = waveOutGetDevCaps((UINT_PTR)hWaveOut, &caps, sizeof(WAVEOUTCAPS)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); - TraceWaveOutError(res); - } - - UINT deviceID(0); - res = waveOutGetID(hWaveOut, &deviceID); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetID() failed (err=%d)", res); - TraceWaveOutError(res); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "utilized device ID : %u", deviceID); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); - - // Store valid handle for the open waveform-audio output device - _hWaveOut = hWaveOut; - - // Store the input wave header as well - _waveFormatOut = waveFormat; - - // Prepare wave-out headers - // - const WebRtc_UWord8 bytesPerSample = 2*_playChannels; - - for (int n = 0; n < N_BUFFERS_OUT; n++) - { - // set up the output wave header - _waveHeaderOut[n].lpData = _playBuffer[n]; - _waveHeaderOut[n].dwBufferLength = bytesPerSample*PLAY_BUF_SIZE_IN_SAMPLES; - _waveHeaderOut[n].dwFlags = 0; - _waveHeaderOut[n].dwLoops = 0; - - memset(_playBuffer[n], 0, bytesPerSample*PLAY_BUF_SIZE_IN_SAMPLES); - - // The waveOutPrepareHeader function prepares a waveform-audio data block for playback. - // The lpData, dwBufferLength, and dwFlags members of the WAVEHDR structure must be set - // before calling this function. - // - res = waveOutPrepareHeader(_hWaveOut, &_waveHeaderOut[n], sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutPrepareHeader(%d) failed (err=%d)", n, res); - TraceWaveOutError(res); - } - - // perform extra check to ensure that the header is prepared - if (_waveHeaderOut[n].dwFlags != WHDR_PREPARED) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutPrepareHeader(%d) failed (dwFlags != WHDR_PREPARED)", n); - } - } - - // Mark playout side as initialized - _playIsInitialized = true; - - _dTcheckPlayBufDelay = 10; // check playback buffer delay every 10 ms - _playBufCount = 0; // index of active output wave header (<=> output buffer index) - _playBufDelay = 80; // buffer delay/size is initialized to 80 ms and slowly decreased until er < 25 - _minPlayBufDelay = 25; // minimum playout buffer delay - _MAX_minBuffer = 65; // adaptive minimum playout buffer delay cannot be larger than this value - _intro = 1; // Used to make sure that adaption starts after (2000-1700)/100 seconds - _waitCounter = 1700; // Counter for start of adaption of playback buffer - _erZeroCounter = 0; // Log how many times er = 0 in consequtive calls to RecTimeProc - _useHeader = 0; // Counts number of "useHeader" detections. Stops at 2. - - _writtenSamples = 0; - _writtenSamplesOld = 0; - _playedSamplesOld = 0; - _sndCardPlayDelay = 0; - _sndCardRecDelay = 0; - - WEBRTC_TRACE(kTraceInfo, kTraceUtility, _id,"initial playout status: _playBufDelay=%d, _minPlayBufDelay=%d", - _playBufDelay, _minPlayBufDelay); - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_recording) - { - return -1; - } - - if (!_inputDeviceIsSpecified) - { - return -1; - } - - if (_recIsInitialized) - { - return 0; - } - - _avgCPULoad = 0; - _playAcc = 0; - - // Initialize the microphone (devices might have been added or removed) - if (InitMicrophone() == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "InitMicrophone() failed"); - } - - // Enumerate all availiable input devices - EnumerateRecordingDevices(); - - // Start by closing any existing wave-input devices - // - MMRESULT res(MMSYSERR_ERROR); - - if (_hWaveIn != NULL) - { - res = waveInClose(_hWaveIn); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInClose() failed (err=%d)", res); - TraceWaveInError(res); - } - } - - // Set the input wave format - // - WAVEFORMATEX waveFormat; - - waveFormat.wFormatTag = WAVE_FORMAT_PCM; - waveFormat.nChannels = _recChannels; // mono <=> 1, stereo <=> 2 - waveFormat.nSamplesPerSec = N_REC_SAMPLES_PER_SEC; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * (waveFormat.wBitsPerSample/8); - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; - - // Open the given waveform-audio input device for recording - // - HWAVEIN hWaveIn(NULL); - - if (IsUsingInputDeviceIndex()) - { - // verify settings first - res = waveInOpen(NULL, _inputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // open the given waveform-audio input device for recording - res = waveInOpen(&hWaveIn, _inputDeviceIndex, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening input device corresponding to device ID %u", _inputDeviceIndex); - } - } - else - { - if (_inputDevice == AudioDeviceModule::kDefaultCommunicationDevice) - { - // check if it is possible to open the default communication device (supported on Windows 7) - res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // if so, open the default communication device for real - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); - } - else - { - // use default device since default communication device was not avaliable - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to open default communication device => using default instead"); - } - } - else if (_inputDevice == AudioDeviceModule::kDefaultDevice) - { - // open default device since it has been requested - res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default input device"); - } - } - } - - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInOpen() failed (err=%d)", res); - TraceWaveInError(res); - return -1; - } - - // Log information about the aquired input device - // - WAVEINCAPS caps; - - res = waveInGetDevCaps((UINT_PTR)hWaveIn, &caps, sizeof(WAVEINCAPS)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCaps() failed (err=%d)", res); - TraceWaveInError(res); - } - - UINT deviceID(0); - res = waveInGetID(hWaveIn, &deviceID); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetID() failed (err=%d)", res); - TraceWaveInError(res); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "utilized device ID : %u", deviceID); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); - - WebRtc_UWord32 maxVol = 0; - if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " unable to retrieve max microphone volume"); - return -1; - } - _maxMicVolume = maxVol; - - WebRtc_UWord32 minVol = 0; - if (_mixerManager.MinMicrophoneVolume(minVol) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - " unable to retrieve min microphone volume"); - return -1; - } - _minMicVolume = minVol; - - // Store valid handle for the open waveform-audio input device - _hWaveIn = hWaveIn; - - // Store the input wave header as well - _waveFormatIn = waveFormat; - - // Mark recording side as initialized - _recIsInitialized = true; - - _recBufCount = 0; // index of active input wave header (<=> input buffer index) - _recDelayCount = 0; // ensures that input buffers are returned with certain delay - - return 0; -} - -// ---------------------------------------------------------------------------- -// StartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_recIsInitialized) - { - return -1; - } - - if (_recording) - { - return 0; - } - - // set state to ensure that the recording starts from the audio thread - _startRec = true; - - // the audio thread will signal when recording has stopped - if (kEventTimeout == _recStartEvent.Wait(10000)) - { - _startRec = false; - StopRecording(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate recording"); - return -1; - } - - if (_recording) - { - // the recording state is set by the audio thread after recording has started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "recording is now active"); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate recording"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_recIsInitialized) - { - return 0; - } - - if (_hWaveIn == NULL) - { - return -1; - } - - bool wasRecording = _recording; - _recIsInitialized = false; - _recording = false; - - MMRESULT res; - - // Stop waveform-adio input. If there are any buffers in the queue, the - // current buffer will be marked as done (the dwBytesRecorded member in - // the header will contain the length of data), but any empty buffers in - // the queue will remain there. - // - res = waveInStop(_hWaveIn); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInStop() failed (err=%d)", res); - TraceWaveInError(res); - } - - // Stop input on the given waveform-audio input device and resets the current - // position to zero. All pending buffers are marked as done and returned to - // the application. - // - res = waveInReset(_hWaveIn); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInReset() failed (err=%d)", res); - TraceWaveInError(res); - } - - // Clean up the preparation performed by the waveInPrepareHeader function. - // Only unprepare header if recording was ever started (and headers are prepared). - // - if (wasRecording) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "waveInUnprepareHeader() will be performed"); - for (int n = 0; n < N_BUFFERS_IN; n++) - { - res = waveInUnprepareHeader(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInUnprepareHeader() failed (err=%d)", res); - TraceWaveInError(res); - } - } - } - - // Close the given waveform-audio input device. - // - res = waveInClose(_hWaveIn); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInClose() failed (err=%d)", res); - TraceWaveInError(res); - } - - // Set the wave input handle to NULL - // - _hWaveIn = NULL; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_hWaveIn is now set to NULL"); - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recIsInitialized); -} - -// ---------------------------------------------------------------------------- -// Recording -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_recording); -} - -// ---------------------------------------------------------------------------- -// PlayoutIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_playIsInitialized); -} - -// ---------------------------------------------------------------------------- -// StartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_playIsInitialized) - { - return -1; - } - - if (_playing) - { - return 0; - } - - // set state to ensure that playout starts from the audio thread - _startPlay = true; - - // the audio thread will signal when recording has started - if (kEventTimeout == _playStartEvent.Wait(10000)) - { - _startPlay = false; - StopPlayout(); - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate playout"); - return -1; - } - - if (_playing) - { - // the playing state is set by the audio thread after playout has started - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "playing is now active"); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to activate playing"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// StopPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (!_playIsInitialized) - { - return 0; - } - - if (_hWaveOut == NULL) - { - return -1; - } - - _playIsInitialized = false; - _playing = false; - _sndCardPlayDelay = 0; - _sndCardRecDelay = 0; - - MMRESULT res; - - // The waveOutReset function stops playback on the given waveform-audio - // output device and resets the current position to zero. All pending - // playback buffers are marked as done (WHDR_DONE) and returned to the application. - // After this function returns, the application can send new playback buffers - // to the device by calling waveOutWrite, or close the device by calling waveOutClose. - // - res = waveOutReset(_hWaveOut); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutReset() failed (err=%d)", res); - TraceWaveOutError(res); - } - - // The waveOutUnprepareHeader function cleans up the preparation performed - // by the waveOutPrepareHeader function. This function must be called after - // the device driver is finished with a data block. - // You must call this function before freeing the buffer. - // - for (int n = 0; n < N_BUFFERS_OUT; n++) - { - res = waveOutUnprepareHeader(_hWaveOut, &_waveHeaderOut[n], sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutUnprepareHeader() failed (err=%d)", res); - TraceWaveOutError(res); - } - } - - // The waveOutClose function closes the given waveform-audio output device. - // The close operation fails if the device is still playing a waveform-audio - // buffer that was previously sent by calling waveOutWrite. Before calling - // waveOutClose, the application must wait for all buffers to finish playing - // or call the waveOutReset function to terminate playback. - // - res = waveOutClose(_hWaveOut); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutClose() failed (err=%d)", res); - TraceWaveOutError(res); - } - - _hWaveOut = NULL; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "_hWaveOut is now set to NULL"); - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PlayoutDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = (WebRtc_UWord16)_sndCardPlayDelay; - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::RecordingDelay(WebRtc_UWord16& delayMS) const -{ - CriticalSectionScoped lock(_critSect); - delayMS = (WebRtc_UWord16)_sndCardRecDelay; - return 0; -} - -// ---------------------------------------------------------------------------- -// Playing -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - return (_playing); -} -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceWindowsWave::SetPlayoutBuffer(type=%u, sizeMS=%u)", type, sizeMS); - CriticalSectionScoped lock(_critSect); - _playBufType = type; - if (type == AudioDeviceModule::kFixedBufferSize) - { - _playBufDelayFixed = sizeMS; - } - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CriticalSectionScoped lock(_critSect); - type = _playBufType; - if (type == AudioDeviceModule::kFixedBufferSize) - { - sizeMS = _playBufDelayFixed; - } - else - { - sizeMS = _playBufDelay; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// CPULoad -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::CPULoad(WebRtc_UWord16& load) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - load = static_cast(100*_avgCPULoad); - - return 0; -} - -// ---------------------------------------------------------------------------- -// PlayoutWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::PlayoutWarning() const -{ - return ( _playWarning > 0); -} - -// ---------------------------------------------------------------------------- -// PlayoutError -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::PlayoutError() const -{ - return ( _playError > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingWarning -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::RecordingWarning() const -{ - return ( _recWarning > 0); -} - -// ---------------------------------------------------------------------------- -// RecordingError -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::RecordingError() const -{ - return ( _recError > 0); -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::ClearPlayoutWarning() -{ - _playWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearPlayoutError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::ClearPlayoutError() -{ - _playError = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingWarning -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::ClearRecordingWarning() -{ - _recWarning = 0; -} - -// ---------------------------------------------------------------------------- -// ClearRecordingError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::ClearRecordingError() -{ - _recError = 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// InputSanityCheckAfterUnlockedPeriod -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::InputSanityCheckAfterUnlockedPeriod() const -{ - if (_hWaveIn == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "input state has been modified during unlocked period"); - return -1; - } - return 0; -} - -// ---------------------------------------------------------------------------- -// OutputSanityCheckAfterUnlockedPeriod -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::OutputSanityCheckAfterUnlockedPeriod() const -{ - if (_hWaveOut == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "output state has been modified during unlocked period"); - return -1; - } - return 0; -} - -// ---------------------------------------------------------------------------- -// EnumeratePlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::EnumeratePlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord16 nDevices(PlayoutDevices()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#output devices: %u", nDevices); - - WAVEOUTCAPS caps; - MMRESULT res; - - for (UINT deviceID = 0; deviceID < nDevices; deviceID++) - { - res = waveOutGetDevCaps(deviceID, &caps, sizeof(WAVEOUTCAPS)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetDevCaps() failed (err=%d)", res); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Device ID %u:", deviceID); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", caps.wMid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u",caps.wPid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver : %u.%u", HIBYTE(caps.vDriverVersion), LOBYTE(caps.vDriverVersion)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwFormats : 0x%x", caps.dwFormats); - if (caps.dwFormats & WAVE_FORMAT_48S16) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : SUPPORTED"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : *NOT* SUPPORTED"); - } - if (caps.dwFormats & WAVE_FORMAT_48M16) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,mono,16bit : SUPPORTED"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,mono,16bit : *NOT* SUPPORTED"); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wChannels : %u", caps.wChannels); - TraceSupportFlags(caps.dwSupport); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// EnumerateRecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::EnumerateRecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WebRtc_UWord16 nDevices(RecordingDevices()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#input devices: %u", nDevices); - - WAVEINCAPS caps; - MMRESULT res; - - for (UINT deviceID = 0; deviceID < nDevices; deviceID++) - { - res = waveInGetDevCaps(deviceID, &caps, sizeof(WAVEINCAPS)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetDevCaps() failed (err=%d)", res); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Device ID %u:", deviceID); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", caps.wMid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u",caps.wPid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver : %u.%u", HIBYTE(caps.vDriverVersion), LOBYTE(caps.vDriverVersion)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", caps.szPname); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwFormats : 0x%x", caps.dwFormats); - if (caps.dwFormats & WAVE_FORMAT_48S16) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : SUPPORTED"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,stereo,16bit : *NOT* SUPPORTED"); - } - if (caps.dwFormats & WAVE_FORMAT_48M16) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " 48kHz,mono,16bit : SUPPORTED"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " 48kHz,mono,16bit : *NOT* SUPPORTED"); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "wChannels : %u", caps.wChannels); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// TraceSupportFlags -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::TraceSupportFlags(DWORD dwSupport) const -{ - TCHAR buf[256]; - - StringCchPrintf(buf, 128, TEXT("support flags : 0x%x "), dwSupport); - - if (dwSupport & WAVECAPS_PITCH) - { - // supports pitch control - StringCchCat(buf, 256, TEXT("(PITCH)")); - } - if (dwSupport & WAVECAPS_PLAYBACKRATE) - { - // supports playback rate control - StringCchCat(buf, 256, TEXT("(PLAYBACKRATE)")); - } - if (dwSupport & WAVECAPS_VOLUME) - { - // supports volume control - StringCchCat(buf, 256, TEXT("(VOLUME)")); - } - if (dwSupport & WAVECAPS_LRVOLUME) - { - // supports separate left and right volume control - StringCchCat(buf, 256, TEXT("(LRVOLUME)")); - } - if (dwSupport & WAVECAPS_SYNC) - { - // the driver is synchronous and will block while playing a buffer - StringCchCat(buf, 256, TEXT("(SYNC)")); - } - if (dwSupport & WAVECAPS_SAMPLEACCURATE) - { - // returns sample-accurate position information - StringCchCat(buf, 256, TEXT("(SAMPLEACCURATE)")); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", buf); -} - -// ---------------------------------------------------------------------------- -// TraceWaveInError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::TraceWaveInError(MMRESULT error) const -{ - TCHAR buf[MAXERRORLENGTH]; - TCHAR msg[MAXERRORLENGTH]; - - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - waveInGetErrorText(error, msg, MAXERRORLENGTH); - StringCchCat(buf, MAXERRORLENGTH, msg); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", buf); -} - -// ---------------------------------------------------------------------------- -// TraceWaveOutError -// ---------------------------------------------------------------------------- - -void AudioDeviceWindowsWave::TraceWaveOutError(MMRESULT error) const -{ - TCHAR buf[MAXERRORLENGTH]; - TCHAR msg[MAXERRORLENGTH]; - - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - waveOutGetErrorText(error, msg, MAXERRORLENGTH); - StringCchCat(buf, MAXERRORLENGTH, msg); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", buf); -} - -// ---------------------------------------------------------------------------- -// PrepareStartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PrepareStartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_hWaveOut == NULL) - { - return -1; - } - - // A total of 30ms of data is immediately placed in the SC buffer - // - WebRtc_Word8 zeroVec[4*PLAY_BUF_SIZE_IN_SAMPLES]; // max allocation - memset(zeroVec, 0, 4*PLAY_BUF_SIZE_IN_SAMPLES); - - { - Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); - Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); - Write(zeroVec, PLAY_BUF_SIZE_IN_SAMPLES); - } - - _playAcc = 0; - _playWarning = 0; - _playError = 0; - _dc_diff_mean = 0; - _dc_y_prev = 0; - _dc_penalty_counter = 20; - _dc_prevtime = 0; - _dc_prevplay = 0; - - return 0; -} - -// ---------------------------------------------------------------------------- -// PrepareStartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::PrepareStartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_hWaveIn == NULL) - { - return -1; - } - - _playAcc = 0; - _recordedBytes = 0; - _recPutBackDelay = REC_PUT_BACK_DELAY; - - MMRESULT res; - MMTIME mmtime; - mmtime.wType = TIME_SAMPLES; - - res = waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetPosition(TIME_SAMPLES) failed (err=%d)", res); - TraceWaveInError(res); - } - - _read_samples = mmtime.u.sample; - _read_samples_old = _read_samples; - _rec_samples_old = mmtime.u.sample; - _wrapCounter = 0; - - for (int n = 0; n < N_BUFFERS_IN; n++) - { - const WebRtc_UWord8 nBytesPerSample = 2*_recChannels; - - // set up the input wave header - _waveHeaderIn[n].lpData = _recBuffer[n]; - _waveHeaderIn[n].dwBufferLength = nBytesPerSample * REC_BUF_SIZE_IN_SAMPLES; - _waveHeaderIn[n].dwFlags = 0; - _waveHeaderIn[n].dwBytesRecorded = 0; - _waveHeaderIn[n].dwUser = 0; - - memset(_recBuffer[n], 0, nBytesPerSample * REC_BUF_SIZE_IN_SAMPLES); - - // prepare a buffer for waveform-audio input - res = waveInPrepareHeader(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInPrepareHeader(%d) failed (err=%d)", n, res); - TraceWaveInError(res); - } - - // send an input buffer to the given waveform-audio input device - res = waveInAddBuffer(_hWaveIn, &_waveHeaderIn[n], sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInAddBuffer(%d) failed (err=%d)", n, res); - TraceWaveInError(res); - } - } - - // start input on the given waveform-audio input device - res = waveInStart(_hWaveIn); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInStart() failed (err=%d)", res); - TraceWaveInError(res); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// GetPlayoutBufferDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::GetPlayoutBufferDelay(WebRtc_UWord32& writtenSamples, WebRtc_UWord32& playedSamples) -{ - int i; - int ms_Header; - long playedDifference; - int msecInPlayoutBuffer(0); // #milliseconds of audio in the playout buffer - - const WebRtc_UWord16 nSamplesPerMs = (WebRtc_UWord16)(N_PLAY_SAMPLES_PER_SEC/1000); // default is 48000/1000 = 48 - - MMRESULT res; - MMTIME mmtime; - - if (!_playing) - { - playedSamples = 0; - return (0); - } - - // Retrieve the current playback position. - // - mmtime.wType = TIME_SAMPLES; // number of waveform-audio samples - res = waveOutGetPosition(_hWaveOut, &mmtime, sizeof(mmtime)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutGetPosition() failed (err=%d)", res); - TraceWaveOutError(res); - } - - writtenSamples = _writtenSamples; // #samples written to the playout buffer - playedSamples = mmtime.u.sample; // current playout position in the playout buffer - - // derive remaining amount (in ms) of data in the playout buffer - msecInPlayoutBuffer = ((writtenSamples - playedSamples)/nSamplesPerMs); - // DEBUG_PRINTP("msecInPlayoutBuffer=%u\n", msecInPlayoutBuffer); - - playedDifference = (long) (_playedSamplesOld - playedSamples); - - if (playedDifference > 64000) - { - // If the sound cards number-of-played-out-samples variable wraps around before - // written_sampels wraps around this needs to be adjusted. This can happen on - // sound cards that uses less than 32 bits to keep track of number of played out - // sampels. To avoid being fooled by sound cards that sometimes produces false - // output we compare old value minus the new value with a large value. This is - // neccessary because some SC:s produce an output like 153, 198, 175, 230 which - // would trigger the wrap-around function if we didn't compare with a large value. - // The value 64000 is chosen because 2^16=65536 so we allow wrap around at 16 bits. - - i = 31; - while((_playedSamplesOld <= (unsigned long)POW2(i)) && (i > 14)) { - i--; - } - - if((i < 31) && (i > 14)) { - // Avoid adjusting when there is 32-bit wrap-around since that is - // something neccessary. - // - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "msecleft() => wrap around occured: %d bits used by sound card)", (i+1)); - - _writtenSamples = _writtenSamples - POW2(i + 1); - writtenSamples = _writtenSamples; - msecInPlayoutBuffer = ((writtenSamples - playedSamples)/nSamplesPerMs); - } - } - else if ((_writtenSamplesOld > POW2(31)) && (writtenSamples < 96000)) - { - // Wrap around as expected after having used all 32 bits. (But we still - // test if the wrap around happened earlier which it should not) - - i = 31; - while (_writtenSamplesOld <= (unsigned long)POW2(i)) { - i--; - } - - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, " msecleft() (wrap around occured after having used all 32 bits)"); - - _writtenSamplesOld = writtenSamples; - _playedSamplesOld = playedSamples; - msecInPlayoutBuffer = (int)((writtenSamples + POW2(i + 1) - playedSamples)/nSamplesPerMs); - - } - else if ((writtenSamples < 96000) && (playedSamples > POW2(31))) - { - // Wrap around has, as expected, happened for written_sampels before - // playedSampels so we have to adjust for this until also playedSampels - // has had wrap around. - - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, " msecleft() (wrap around occured: correction of output is done)"); - - _writtenSamplesOld = writtenSamples; - _playedSamplesOld = playedSamples; - msecInPlayoutBuffer = (int)((writtenSamples + POW2(32) - playedSamples)/nSamplesPerMs); - } - - _writtenSamplesOld = writtenSamples; - _playedSamplesOld = playedSamples; - - - // We use the following formaula to track that playout works as it should - // y=playedSamples/48 - timeGetTime(); - // y represent the clock drift between system clock and sound card clock - should be fairly stable - // When the exponential mean value of diff(y) goes away from zero something is wrong - // The exponential formula will accept 1% clock drift but not more - // The driver error means that we will play to little audio and have a high negative clock drift - // We kick in our alternative method when the clock drift reaches 20% - - int diff,y; - int unsigned time =0; - - // If we have other problems that causes playout glitches - // we don't want to switch playout method. - // Check if playout buffer is extremely low, or if we haven't been able to - // exectue our code in more than 40 ms - - time = timeGetTime(); - - if ((msecInPlayoutBuffer < 20) || (time - _dc_prevtime > 40)) - { - _dc_penalty_counter = 100; - } - - if ((playedSamples != 0)) - { - y = playedSamples/48 - time; - if ((_dc_y_prev != 0) && (_dc_penalty_counter == 0)) - { - diff = y - _dc_y_prev; - _dc_diff_mean = (990*_dc_diff_mean)/1000 + 10*diff; - } - _dc_y_prev = y; - } - - if (_dc_penalty_counter) - { - _dc_penalty_counter--; - } - - if (_dc_diff_mean < -200) - { - // Always reset the filter - _dc_diff_mean = 0; - - // Problem is detected. Switch delay method and set min buffer to 80. - // Reset the filter and keep monitoring the filter output. - // If issue is detected a second time, increase min buffer to 100. - // If that does not help, we must modify this scheme further. - - _useHeader++; - if (_useHeader == 1) - { - _minPlayBufDelay = 80; - _playWarning = 1; // only warn first time - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, "Modification #1: _useHeader = %d, _minPlayBufDelay = %d", _useHeader, _minPlayBufDelay); - } - else if (_useHeader == 2) - { - _minPlayBufDelay = 100; // add some more safety - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, "Modification #2: _useHeader = %d, _minPlayBufDelay = %d", _useHeader, _minPlayBufDelay); - } - else - { - // This state should not be entered... (HA) - WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1, "further actions are required!"); - } - if (_playWarning == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending playout warning exists"); - } - _playWarning = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "kPlayoutWarning message posted: switching to alternative playout delay method"); - } - _dc_prevtime = time; - _dc_prevplay = playedSamples; - - // Try a very rough method of looking at how many buffers are still playing - ms_Header = 0; - for (i = 0; i < N_BUFFERS_OUT; i++) { - if ((_waveHeaderOut[i].dwFlags & WHDR_INQUEUE)!=0) { - ms_Header += 10; - } - } - - if ((ms_Header-50) > msecInPlayoutBuffer) { - // Test for cases when GetPosition appears to be screwed up (currently just log....) - TCHAR infoStr[300]; - if (_no_of_msecleft_warnings%20==0) - { - StringCchPrintf(infoStr, 300, TEXT("writtenSamples=%i, playedSamples=%i, msecInPlayoutBuffer=%i, ms_Header=%i"), writtenSamples, playedSamples, msecInPlayoutBuffer, ms_Header); - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, (const char*)infoStr); - } - _no_of_msecleft_warnings++; - } - - // If this is true we have had a problem with the playout - if (_useHeader > 0) - { - return (ms_Header); - } - - - if (ms_Header < msecInPlayoutBuffer) - { - if (_no_of_msecleft_warnings % 100 == 0) - { - TCHAR str[300]; - StringCchPrintf(str, 300, TEXT("_no_of_msecleft_warnings=%i, msecInPlayoutBuffer=%i ms_Header=%i (minBuffer=%i buffersize=%i writtenSamples=%i playedSamples=%i)"), - _no_of_msecleft_warnings, msecInPlayoutBuffer, ms_Header, _minPlayBufDelay, _playBufDelay, writtenSamples, playedSamples); - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, (const char*)str); - } - _no_of_msecleft_warnings++; - ms_Header -= 6; // Round off as we only have 10ms resolution + Header info is usually slightly delayed compared to GetPosition - - if (ms_Header < 0) - ms_Header = 0; - - return (ms_Header); - } - else - { - return (msecInPlayoutBuffer); - } -} - -// ---------------------------------------------------------------------------- -// GetRecordingBufferDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::GetRecordingBufferDelay(WebRtc_UWord32& readSamples, WebRtc_UWord32& recSamples) -{ - long recDifference; - MMTIME mmtime; - MMRESULT mmr; - - const WebRtc_UWord16 nSamplesPerMs = (WebRtc_UWord16)(N_REC_SAMPLES_PER_SEC/1000); // default is 48000/1000 = 48 - - // Retrieve the current input position of the given waveform-audio input device - // - mmtime.wType = TIME_SAMPLES; - mmr = waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); - if (MMSYSERR_NOERROR != mmr) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInGetPosition() failed (err=%d)", mmr); - TraceWaveInError(mmr); - } - - readSamples = _read_samples; // updated for each full fram in RecProc() - recSamples = mmtime.u.sample; // remaining time in input queue (recorded but not read yet) - - recDifference = (long) (_rec_samples_old - recSamples); - - if( recDifference > 64000) { - WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 1 (recDifference =%d)", recDifference); - // If the sound cards number-of-recorded-samples variable wraps around before - // read_sampels wraps around this needs to be adjusted. This can happen on - // sound cards that uses less than 32 bits to keep track of number of played out - // sampels. To avoid being fooled by sound cards that sometimes produces false - // output we compare old value minus the new value with a large value. This is - // neccessary because some SC:s produce an output like 153, 198, 175, 230 which - // would trigger the wrap-around function if we didn't compare with a large value. - // The value 64000 is chosen because 2^16=65536 so we allow wrap around at 16 bits. - // - int i = 31; - while((_rec_samples_old <= (unsigned long)POW2(i)) && (i > 14)) - i--; - - if((i < 31) && (i > 14)) { - // Avoid adjusting when there is 32-bit wrap-around since that is - // somethying neccessary. - // - _read_samples = _read_samples - POW2(i + 1); - readSamples = _read_samples; - _wrapCounter++; - } else { - WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1,"AEC (_rec_samples_old %d recSamples %d)",_rec_samples_old, recSamples); - } - } - - if((_wrapCounter>200)){ - // Do nothing, handled later - } - else if((_rec_samples_old > POW2(31)) && (recSamples < 96000)) { - WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 2 (_rec_samples_old %d recSamples %d)",_rec_samples_old, recSamples); - // Wrap around as expected after having used all 32 bits. - _read_samples_old = readSamples; - _rec_samples_old = recSamples; - _wrapCounter++; - return (int)((recSamples + POW2(32) - readSamples)/nSamplesPerMs); - - - } else if((recSamples < 96000) && (readSamples > POW2(31))) { - WEBRTC_TRACE (kTraceDebug, kTraceUtility, -1,"WRAP 3 (readSamples %d recSamples %d)",readSamples, recSamples); - // Wrap around has, as expected, happened for rec_sampels before - // readSampels so we have to adjust for this until also readSampels - // has had wrap around. - _read_samples_old = readSamples; - _rec_samples_old = recSamples; - _wrapCounter++; - return (int)((recSamples + POW2(32) - readSamples)/nSamplesPerMs); - } - - _read_samples_old = _read_samples; - _rec_samples_old = recSamples; - int res=(((int)_rec_samples_old - (int)_read_samples_old)/nSamplesPerMs); - - if((res > 2000)||(res < 0)||(_wrapCounter>200)){ - // Reset everything - WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1,"msec_read error (res %d wrapCounter %d)",res, _wrapCounter); - MMTIME mmtime; - mmtime.wType = TIME_SAMPLES; - - mmr=waveInGetPosition(_hWaveIn, &mmtime, sizeof(mmtime)); - if (mmr != MMSYSERR_NOERROR) { - WEBRTC_TRACE (kTraceWarning, kTraceUtility, -1, "waveInGetPosition failed (mmr=%d)", mmr); - } - _read_samples=mmtime.u.sample; - _read_samples_old=_read_samples; - _rec_samples_old=mmtime.u.sample; - - // Guess a decent value - res = 20; - } - - _wrapCounter = 0; - return res; -} - -// ============================================================================ -// Thread Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// ThreadFunc -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::ThreadFunc(void* pThis) -{ - return (static_cast(pThis)->ThreadProcess()); -} - -// ---------------------------------------------------------------------------- -// ThreadProcess -// ---------------------------------------------------------------------------- - -bool AudioDeviceWindowsWave::ThreadProcess() -{ - WebRtc_UWord32 time(0); - WebRtc_UWord32 playDiff(0); - WebRtc_UWord32 recDiff(0); - - LONGLONG playTime(0); - LONGLONG recTime(0); - - switch (_timeEvent.Wait(1000)) - { - case kEventSignaled: - break; - case kEventError: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "EventWrapper::Wait() failed => restarting timer"); - _timeEvent.StopTimer(); - _timeEvent.StartTimer(true, TIMER_PERIOD_MS); - return true; - case kEventTimeout: - return true; - } - - time = AudioDeviceUtility::GetTimeInMS(); - - if (_startPlay) - { - if (PrepareStartPlayout() == 0) - { - _prevTimerCheckTime = time; - _prevPlayTime = time; - _startPlay = false; - _playing = true; - _playStartEvent.Set(); - } - } - - if (_startRec) - { - if (PrepareStartRecording() == 0) - { - _prevTimerCheckTime = time; - _prevRecTime = time; - _prevRecByteCheckTime = time; - _startRec = false; - _recording = true; - _recStartEvent.Set(); - } - } - - if (_playing) - { - playDiff = time - _prevPlayTime; - } - - if (_recording) - { - recDiff = time - _prevRecTime; - } - - if (_playing || _recording) - { - RestartTimerIfNeeded(time); - } - - if (_playing && - (playDiff > (WebRtc_UWord32)(_dTcheckPlayBufDelay - 1)) || - (playDiff < 0)) - { - Lock(); - if (_playing) - { - if (PlayProc(playTime) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "PlayProc() failed"); - } - _prevPlayTime = time; - if (playTime != 0) - _playAcc += playTime; - } - UnLock(); - } - - if (_playing && (playDiff > 12)) - { - // It has been a long time since we were able to play out, try to - // compensate by calling PlayProc again. - // - Lock(); - if (_playing) - { - if (PlayProc(playTime)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "PlayProc() failed"); - } - _prevPlayTime = time; - if (playTime != 0) - _playAcc += playTime; - } - UnLock(); - } - - if (_recording && - (recDiff > REC_CHECK_TIME_PERIOD_MS) || - (recDiff < 0)) - { - Lock(); - if (_recording) - { - WebRtc_Word32 nRecordedBytes(0); - WebRtc_UWord16 maxIter(10); - - // Deliver all availiable recorded buffers and update the CPU load measurement. - // We use a while loop here to compensate for the fact that the multi-media timer - // can sometimed enter a "bad state" after hibernation where the resolution is - // reduced from ~1ms to ~10-15 ms. - // - while ((nRecordedBytes = RecProc(recTime)) > 0) - { - maxIter--; - _recordedBytes += nRecordedBytes; - if (recTime && _perfFreq.QuadPart) - { - // Measure the average CPU load: - // This is a simplified expression where an exponential filter is used: - // _avgCPULoad = 0.99 * _avgCPULoad + 0.01 * newCPU, - // newCPU = (recTime+playAcc)/f is time in seconds - // newCPU / 0.01 is the fraction of a 10 ms period - // The two 0.01 cancels each other. - // NOTE - assumes 10ms audio buffers. - // - _avgCPULoad = (float)(_avgCPULoad*.99 + (recTime+_playAcc)/(double)(_perfFreq.QuadPart)); - _playAcc = 0; - } - if (maxIter == 0) - { - // If we get this message ofte, our compensation scheme is not sufficient. - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "failed to compensate for reduced MM-timer resolution"); - } - } - - if (nRecordedBytes == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "RecProc() failed"); - } - - _prevRecTime = time; - - // Monitor the recording process and generate error/warning callbacks if needed - MonitorRecording(time); - } - UnLock(); - } - - if (!_recording) - { - _prevRecByteCheckTime = time; - _avgCPULoad = 0; - } - - return true; -} - -// ---------------------------------------------------------------------------- -// RecProc -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::RecProc(LONGLONG& consumedTime) -{ - MMRESULT res; - WebRtc_UWord32 bufCount(0); - WebRtc_UWord32 nBytesRecorded(0); - - consumedTime = 0; - - // count modulo N_BUFFERS_IN (0,1,2,...,(N_BUFFERS_IN-1),0,1,2,..) - if (_recBufCount == N_BUFFERS_IN) - { - _recBufCount = 0; - } - - bufCount = _recBufCount; - - // take mono/stereo mode into account when deriving size of a full buffer - const WebRtc_UWord16 bytesPerSample = 2*_recChannels; - const WebRtc_UWord32 fullBufferSizeInBytes = bytesPerSample * REC_BUF_SIZE_IN_SAMPLES; - - // read number of recorded bytes for the given input-buffer - nBytesRecorded = _waveHeaderIn[bufCount].dwBytesRecorded; - - if (nBytesRecorded == fullBufferSizeInBytes || - (nBytesRecorded > 0)) - { - WebRtc_Word32 msecOnPlaySide; - WebRtc_Word32 msecOnRecordSide; - WebRtc_UWord32 writtenSamples; - WebRtc_UWord32 playedSamples; - WebRtc_UWord32 readSamples, recSamples; - bool send = true; - - WebRtc_UWord32 nSamplesRecorded = (nBytesRecorded/bytesPerSample); // divide by 2 or 4 depending on mono or stereo - - if (nBytesRecorded == fullBufferSizeInBytes) - { - _timesdwBytes = 0; - } - else - { - // Test if it is stuck on this buffer - _timesdwBytes++; - if (_timesdwBytes < 5) - { - // keep trying - return (0); - } - else - { - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id,"nBytesRecorded=%d => don't use", nBytesRecorded); - _timesdwBytes = 0; - send = false; - } - } - - // store the recorded buffer (no action will be taken if the #recorded samples is not a full buffer) - _ptrAudioBuffer->SetRecordedBuffer(_waveHeaderIn[bufCount].lpData, nSamplesRecorded); - - // update #samples read - _read_samples += nSamplesRecorded; - - // Check how large the playout and recording buffers are on the sound card. - // This info is needed by the AEC. - // - msecOnPlaySide = GetPlayoutBufferDelay(writtenSamples, playedSamples); - msecOnRecordSide = GetRecordingBufferDelay(readSamples, recSamples); - - // If we use the alternative playout delay method, skip the clock drift compensation - // since it will be an unreliable estimate and might degrade AEC performance. - WebRtc_Word32 drift = (_useHeader > 0) ? 0 : GetClockDrift(playedSamples, recSamples); - - _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, drift); - - // Store the play and rec delay values for video synchronization - _sndCardPlayDelay = msecOnPlaySide; - _sndCardRecDelay = msecOnRecordSide; - - LARGE_INTEGER t1,t2; - - if (send) - { - QueryPerformanceCounter(&t1); - - // deliver recorded samples at specified sample rate, mic level etc. to the observer using callback - UnLock(); - _ptrAudioBuffer->DeliverRecordedData(); - Lock(); - - QueryPerformanceCounter(&t2); - - if (InputSanityCheckAfterUnlockedPeriod() == -1) - { - // assert(false); - return -1; - } - } - - if (_AGC) - { - WebRtc_UWord32 newMicLevel = _ptrAudioBuffer->NewMicLevel(); - if (newMicLevel != 0) - { - // The VQE will only deliver non-zero microphone levels when a change is needed. - WEBRTC_TRACE(kTraceStream, kTraceUtility, _id,"AGC change of volume: => new=%u", newMicLevel); - - // We store this outside of the audio buffer to avoid - // having it overwritten by the getter thread. - _newMicLevel = newMicLevel; - SetEvent(_hSetCaptureVolumeEvent); - } - } - - // return utilized buffer to queue after specified delay (default is 4) - if (_recDelayCount > (_recPutBackDelay-1)) - { - // deley buffer counter to compensate for "put-back-delay" - bufCount = (bufCount + N_BUFFERS_IN - _recPutBackDelay) % N_BUFFERS_IN; - - // reset counter so we can make new detection - _waveHeaderIn[bufCount].dwBytesRecorded = 0; - - // return the utilized wave-header after certain delay (given by _recPutBackDelay) - res = waveInUnprepareHeader(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInUnprepareHeader(%d) failed (err=%d)", bufCount, res); - TraceWaveInError(res); - } - - // ensure that the utilized header can be used again - res = waveInPrepareHeader(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInPrepareHeader(%d) failed (err=%d)", bufCount, res); - TraceWaveInError(res); - return -1; - } - - // add the utilized buffer to the queue again - res = waveInAddBuffer(_hWaveIn, &(_waveHeaderIn[bufCount]), sizeof(WAVEHDR)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveInAddBuffer(%d) failed (err=%d)", bufCount, res); - TraceWaveInError(res); - if (_recPutBackDelay < 50) - { - _recPutBackDelay++; - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "_recPutBackDelay increased to %d", _recPutBackDelay); - } - else - { - if (_recError == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording error exists"); - } - _recError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kRecordingError message posted: _recPutBackDelay=%u", _recPutBackDelay); - } - } - } // if (_recDelayCount > (_recPutBackDelay-1)) - - if (_recDelayCount < (_recPutBackDelay+1)) - { - _recDelayCount++; - } - - // increase main buffer count since one complete buffer has now been delivered - _recBufCount++; - - // Calculate processing time - consumedTime = (int)(t2.QuadPart-t1.QuadPart); - // handle wraps, time should not be higher than a second - if ((consumedTime > _perfFreq.QuadPart) || (consumedTime < 0)) - consumedTime = 0; - - } // if ((nBytesRecorded == fullBufferSizeInBytes)) - - return nBytesRecorded; -} - -// ---------------------------------------------------------------------------- -// PlayProc -// ---------------------------------------------------------------------------- - -int AudioDeviceWindowsWave::PlayProc(LONGLONG& consumedTime) -{ - WebRtc_Word32 remTimeMS(0); - WebRtc_Word8 playBuffer[4*PLAY_BUF_SIZE_IN_SAMPLES]; - WebRtc_UWord32 writtenSamples(0); - WebRtc_UWord32 playedSamples(0); - - LARGE_INTEGER t1; - LARGE_INTEGER t2; - - consumedTime = 0; - _waitCounter++; - - // Get number of ms of sound that remains in the sound card buffer for playback. - // - remTimeMS = GetPlayoutBufferDelay(writtenSamples, playedSamples); - - // The threshold can be adaptive or fixed. The adaptive scheme is updated - // also for fixed mode but the updated threshold is not utilized. - // - const WebRtc_UWord16 thresholdMS = - (_playBufType == AudioDeviceModule::kAdaptiveBufferSize) ? _playBufDelay : _playBufDelayFixed; - - if (remTimeMS < thresholdMS + 9) - { - _dTcheckPlayBufDelay = 5; - - if (remTimeMS == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceUtility, _id, "playout buffer is empty => we must adapt..."); - if (_waitCounter > 30) - { - _erZeroCounter++; - if (_erZeroCounter == 2) - { - _playBufDelay += 15; - _minPlayBufDelay += 20; - _waitCounter = 50; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0,erZero=2): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); - } - else if (_erZeroCounter == 3) - { - _erZeroCounter = 0; - _playBufDelay += 30; - _minPlayBufDelay += 25; - _waitCounter = 0; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0, erZero=3): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); - } - else - { - _minPlayBufDelay += 10; - _playBufDelay += 15; - _waitCounter = 50; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "New playout states (er=0, erZero=1): minPlayBufDelay=%u, playBufDelay=%u", _minPlayBufDelay, _playBufDelay); - } - } - } - else if (remTimeMS < _minPlayBufDelay) - { - // If there is less than 25 ms of audio in the play out buffer - // increase the buffersize limit value. _waitCounter prevents - // _playBufDelay to be increased every time this function is called. - - if (_waitCounter > 30) - { - _playBufDelay += 10; - if (_intro == 0) - _waitCounter = 0; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is increased: playBufDelay=%u", _playBufDelay); - } - } - else if (remTimeMS < thresholdMS - 9) - { - _erZeroCounter = 0; - } - else - { - _erZeroCounter = 0; - _dTcheckPlayBufDelay = 10; - } - - QueryPerformanceCounter(&t1); // measure time: START - - // Ask for new PCM data to be played out using the AudioDeviceBuffer. - // Ensure that this callback is executed without taking the audio-thread lock. - // - UnLock(); - WebRtc_UWord32 nSamples = _ptrAudioBuffer->RequestPlayoutData(PLAY_BUF_SIZE_IN_SAMPLES); - Lock(); - - if (OutputSanityCheckAfterUnlockedPeriod() == -1) - { - // assert(false); - return -1; - } - - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer); - if (nSamples != PLAY_BUF_SIZE_IN_SAMPLES) - { - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "invalid number of output samples(%d)", nSamples); - } - - QueryPerformanceCounter(&t2); // measure time: STOP - consumedTime = (int)(t2.QuadPart - t1.QuadPart); - - Write(playBuffer, PLAY_BUF_SIZE_IN_SAMPLES); - - } // if (er < thresholdMS + 9) - else if (thresholdMS + 9 < remTimeMS ) - { - _erZeroCounter = 0; - _dTcheckPlayBufDelay = 2; // check buffer more often - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Need to check playout buffer more often (dT=%u, remTimeMS=%u)", _dTcheckPlayBufDelay, remTimeMS); - } - - // If the buffersize has been stable for 20 seconds try to decrease the buffer size - if (_waitCounter > 2000) - { - _intro = 0; - _playBufDelay--; - _waitCounter = 1990; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is decreased: playBufDelay=%u", _playBufDelay); - } - - // Limit the minimum sound card (playback) delay to adaptive minimum delay - if (_playBufDelay < _minPlayBufDelay) - { - _playBufDelay = _minPlayBufDelay; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is limited to %u", _minPlayBufDelay); - } - - // Limit the maximum sound card (playback) delay to 150 ms - if (_playBufDelay > 150) - { - _playBufDelay = 150; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Playout threshold is limited to %d", _playBufDelay); - } - - // Upper limit of the minimum sound card (playback) delay to 65 ms. - // Deactivated during "useHeader mode" (_useHeader > 0). - if (_minPlayBufDelay > _MAX_minBuffer && - (_useHeader == 0)) - { - _minPlayBufDelay = _MAX_minBuffer; - WEBRTC_TRACE(kTraceDebug, kTraceUtility, _id, "Minimum playout threshold is limited to %d", _MAX_minBuffer); - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// Write -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::Write(WebRtc_Word8* data, WebRtc_UWord16 nSamples) -{ - if (_hWaveOut == NULL) - { - return -1; - } - - if (_playIsInitialized) - { - MMRESULT res; - - const WebRtc_UWord16 bufCount(_playBufCount); - - // Place data in the memory associated with _waveHeaderOut[bufCount] - // - const WebRtc_Word16 nBytes = (2*_playChannels)*nSamples; - memcpy(&_playBuffer[bufCount][0], &data[0], nBytes); - - // Send a data block to the given waveform-audio output device. - // - // When the buffer is finished, the WHDR_DONE bit is set in the dwFlags - // member of the WAVEHDR structure. The buffer must be prepared with the - // waveOutPrepareHeader function before it is passed to waveOutWrite. - // Unless the device is paused by calling the waveOutPause function, - // playback begins when the first data block is sent to the device. - // - res = waveOutWrite(_hWaveOut, &_waveHeaderOut[bufCount], sizeof(_waveHeaderOut[bufCount])); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "waveOutWrite(%d) failed (err=%d)", bufCount, res); - TraceWaveOutError(res); - - _writeErrors++; - if (_writeErrors > 10) - { - if (_playError == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending playout error exists"); - } - _playError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kPlayoutError message posted: _writeErrors=%u", _writeErrors); - } - - return -1; - } - - _playBufCount = (_playBufCount+1) % N_BUFFERS_OUT; // increase buffer counter modulo size of total buffer - _writtenSamples += nSamples; // each sample is 2 or 4 bytes - _writeErrors = 0; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// GetClockDrift -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::GetClockDrift(const WebRtc_UWord32 plSamp, const WebRtc_UWord32 rcSamp) -{ - int drift = 0; - unsigned int plSampDiff = 0, rcSampDiff = 0; - - if (plSamp >= _plSampOld) - { - plSampDiff = plSamp - _plSampOld; - } - else - { - // Wrap - int i = 31; - while(_plSampOld <= (unsigned int)POW2(i)) - { - i--; - } - - // Add the amount remaining prior to wrapping - plSampDiff = plSamp + POW2(i + 1) - _plSampOld; - } - - if (rcSamp >= _rcSampOld) - { - rcSampDiff = rcSamp - _rcSampOld; - } - else - { // Wrap - int i = 31; - while(_rcSampOld <= (unsigned int)POW2(i)) - { - i--; - } - - rcSampDiff = rcSamp + POW2(i + 1) - _rcSampOld; - } - - drift = plSampDiff - rcSampDiff; - - _plSampOld = plSamp; - _rcSampOld = rcSamp; - - return drift; -} - -// ---------------------------------------------------------------------------- -// MonitorRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::MonitorRecording(const WebRtc_UWord32 time) -{ - const WebRtc_UWord16 bytesPerSample = 2*_recChannels; - const WebRtc_UWord32 nRecordedSamples = _recordedBytes/bytesPerSample; - - if (nRecordedSamples > 5*N_REC_SAMPLES_PER_SEC) - { - // 5 seconds of audio has been recorded... - if ((time - _prevRecByteCheckTime) > 5700) - { - // ...and it was more than 5.7 seconds since we last did this check <=> - // we have not been able to record 5 seconds of audio in 5.7 seconds, - // hence a problem should be reported. - // This problem can be related to USB overload. - // - if (_recWarning == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording warning exists"); - } - _recWarning = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "kRecordingWarning message posted: time-_prevRecByteCheckTime=%d", time - _prevRecByteCheckTime); - } - - _recordedBytes = 0; // restart "check again when 5 seconds are recorded" - _prevRecByteCheckTime = time; // reset timer to measure time for recording of 5 seconds - } - - if ((time - _prevRecByteCheckTime) > 8000) - { - // It has been more than 8 seconds since we able to confirm that 5 seconds of - // audio was recorded, hence we have not been able to record 5 seconds in - // 8 seconds => the complete recording process is most likely dead. - // - if (_recError == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "pending recording error exists"); - } - _recError = 1; // triggers callback from module process thread - WEBRTC_TRACE(kTraceError, kTraceUtility, _id, "kRecordingError message posted: time-_prevRecByteCheckTime=%d", time - _prevRecByteCheckTime); - - _prevRecByteCheckTime = time; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// MonitorRecording -// -// Restart timer if needed (they seem to be messed up after a hibernate). -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceWindowsWave::RestartTimerIfNeeded(const WebRtc_UWord32 time) -{ - const WebRtc_UWord32 diffMS = time - _prevTimerCheckTime; - _prevTimerCheckTime = time; - - if (diffMS > 7) - { - // one timer-issue detected... - _timerFaults++; - if (_timerFaults > 5 && _timerRestartAttempts < 2) - { - // Reinitialize timer event if event fails to execute at least every 5ms. - // On some machines it helps and the timer starts working as it should again; - // however, not all machines (we have seen issues on e.g. IBM T60). - // Therefore, the scheme below ensures that we do max 2 attempts to restart the timer. - // For the cases where restart does not do the trick, we compensate for the reduced - // resolution on both the recording and playout sides. - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, " timer issue detected => timer is restarted"); - _timeEvent.StopTimer(); - _timeEvent.StartTimer(true, TIMER_PERIOD_MS); - // make sure timer gets time to start up and we don't kill/start timer serveral times over and over again - _timerFaults = -20; - _timerRestartAttempts++; - } - } - else - { - // restart timer-check scheme since we are OK - _timerFaults = 0; - _timerRestartAttempts = 0; - } - - return 0; -} - -} // namespace webrtc - diff --git a/modules/audio_device/main/source/Windows/audio_device_windows_wave.h b/modules/audio_device/main/source/Windows/audio_device_windows_wave.h deleted file mode 100644 index 6eb2a585c..000000000 --- a/modules/audio_device/main/source/Windows/audio_device_windows_wave.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_WAVE_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_WAVE_H - -#include "audio_device_generic.h" -#include "audio_mixer_manager.h" - -#pragma comment( lib, "winmm.lib" ) - -namespace webrtc { -class EventWrapper; -class ThreadWrapper; - -const WebRtc_UWord32 TIMER_PERIOD_MS = 2; -const WebRtc_UWord32 REC_CHECK_TIME_PERIOD_MS = 4; -const WebRtc_UWord16 REC_PUT_BACK_DELAY = 4; - -const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 48000; -const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 48000; - -const WebRtc_UWord32 N_REC_CHANNELS = 1; // default is mono recording -const WebRtc_UWord32 N_PLAY_CHANNELS = 2; // default is stereo playout - -// NOTE - CPU load will not be correct for other sizes than 10ms -const WebRtc_UWord32 REC_BUF_SIZE_IN_SAMPLES = (N_REC_SAMPLES_PER_SEC/100); -const WebRtc_UWord32 PLAY_BUF_SIZE_IN_SAMPLES = (N_PLAY_SAMPLES_PER_SEC/100); - -enum { N_BUFFERS_IN = 200 }; -enum { N_BUFFERS_OUT = 200 }; - -class AudioDeviceWindowsWave : public AudioDeviceGeneric -{ -public: - AudioDeviceWindowsWave(const WebRtc_Word32 id); - ~AudioDeviceWindowsWave(); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer(AudioDeviceModule::AudioLayer& audioLayer) const; - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, WebRtc_UWord16& volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available); - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool& enabled) const; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer(const AudioDeviceModule::BufferType type, WebRtc_UWord16 sizeMS); - virtual WebRtc_Word32 PlayoutBuffer(AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const; - -public: - virtual bool PlayoutWarning() const; - virtual bool PlayoutError() const; - virtual bool RecordingWarning() const; - virtual bool RecordingError() const; - virtual void ClearPlayoutWarning(); - virtual void ClearPlayoutError(); - virtual void ClearRecordingWarning(); - virtual void ClearRecordingError(); - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer); - -private: - void Lock() { _critSect.Enter(); }; - void UnLock() { _critSect.Leave(); }; - WebRtc_Word32 Id() {return _id;} - bool IsUsingOutputDeviceIndex() const {return _usingOutputDeviceIndex;} - AudioDeviceModule::WindowsDeviceType OutputDevice() const {return _outputDevice;} - WebRtc_UWord16 OutputDeviceIndex() const {return _outputDeviceIndex;} - bool IsUsingInputDeviceIndex() const {return _usingInputDeviceIndex;} - AudioDeviceModule::WindowsDeviceType InputDevice() const {return _inputDevice;} - WebRtc_UWord16 InputDeviceIndex() const {return _inputDeviceIndex;} - -private: - inline WebRtc_Word32 InputSanityCheckAfterUnlockedPeriod() const; - inline WebRtc_Word32 OutputSanityCheckAfterUnlockedPeriod() const; - -private: - WebRtc_Word32 EnumeratePlayoutDevices(); - WebRtc_Word32 EnumerateRecordingDevices(); - void TraceSupportFlags(DWORD dwSupport) const; - void TraceWaveInError(MMRESULT error) const; - void TraceWaveOutError(MMRESULT error) const; - WebRtc_Word32 PrepareStartRecording(); - WebRtc_Word32 PrepareStartPlayout(); - - WebRtc_Word32 RecProc(LONGLONG& consumedTime); - int PlayProc(LONGLONG& consumedTime); - - WebRtc_Word32 GetPlayoutBufferDelay(WebRtc_UWord32& writtenSamples, WebRtc_UWord32& playedSamples); - WebRtc_Word32 GetRecordingBufferDelay(WebRtc_UWord32& readSamples, WebRtc_UWord32& recSamples); - WebRtc_Word32 Write(WebRtc_Word8* data, WebRtc_UWord16 nSamples); - WebRtc_Word32 GetClockDrift(const WebRtc_UWord32 plSamp, const WebRtc_UWord32 rcSamp); - WebRtc_Word32 MonitorRecording(const WebRtc_UWord32 time); - WebRtc_Word32 RestartTimerIfNeeded(const WebRtc_UWord32 time); - -private: - static bool ThreadFunc(void*); - bool ThreadProcess(); - - static DWORD WINAPI GetCaptureVolumeThread(LPVOID context); - DWORD DoGetCaptureVolumeThread(); - - static DWORD WINAPI SetCaptureVolumeThread(LPVOID context); - DWORD DoSetCaptureVolumeThread(); - -private: - AudioDeviceBuffer* _ptrAudioBuffer; - - CriticalSectionWrapper& _critSect; - EventWrapper& _timeEvent; - EventWrapper& _recStartEvent; - EventWrapper& _playStartEvent; - - HANDLE _hGetCaptureVolumeThread; - HANDLE _hShutdownGetVolumeEvent; - HANDLE _hSetCaptureVolumeThread; - HANDLE _hShutdownSetVolumeEvent; - HANDLE _hSetCaptureVolumeEvent; - - ThreadWrapper* _ptrThread; - WebRtc_UWord32 _threadID; - - CriticalSectionWrapper& _critSectCb; - - WebRtc_Word32 _id; - - AudioMixerManager _mixerManager; - - bool _usingInputDeviceIndex; - bool _usingOutputDeviceIndex; - AudioDeviceModule::WindowsDeviceType _inputDevice; - AudioDeviceModule::WindowsDeviceType _outputDevice; - WebRtc_UWord16 _inputDeviceIndex; - WebRtc_UWord16 _outputDeviceIndex; - bool _inputDeviceIsSpecified; - bool _outputDeviceIsSpecified; - - WAVEFORMATEX _waveFormatIn; - WAVEFORMATEX _waveFormatOut; - - HWAVEIN _hWaveIn; - HWAVEOUT _hWaveOut; - - WAVEHDR _waveHeaderIn[N_BUFFERS_IN]; - WAVEHDR _waveHeaderOut[N_BUFFERS_OUT]; - - WebRtc_UWord8 _recChannels; - WebRtc_UWord8 _playChannels; - WebRtc_UWord16 _recBufCount; - WebRtc_UWord16 _recDelayCount; - WebRtc_UWord16 _recPutBackDelay; - - WebRtc_Word8 _recBuffer[N_BUFFERS_IN][4*REC_BUF_SIZE_IN_SAMPLES]; - WebRtc_Word8 _playBuffer[N_BUFFERS_OUT][4*PLAY_BUF_SIZE_IN_SAMPLES]; - - AudioDeviceModule::BufferType _playBufType; - -private: - bool _initialized; - bool _recording; - bool _playing; - bool _recIsInitialized; - bool _playIsInitialized; - bool _startRec; - bool _stopRec; - bool _startPlay; - bool _stopPlay; - bool _AGC; - -private: - WebRtc_UWord32 _prevPlayTime; - WebRtc_UWord32 _prevRecTime; - WebRtc_UWord32 _prevTimerCheckTime; - - WebRtc_UWord16 _playBufCount; // playout buffer index - WebRtc_UWord16 _dTcheckPlayBufDelay; // dT for check of play buffer, {2,5,10} [ms] - WebRtc_UWord16 _playBufDelay; // playback delay - WebRtc_UWord16 _playBufDelayFixed; // fixed playback delay - WebRtc_UWord16 _minPlayBufDelay; // minimum playback delay - WebRtc_UWord16 _MAX_minBuffer; // level of (adaptive) min threshold must be < _MAX_minBuffer - - WebRtc_Word32 _erZeroCounter; // counts "buffer-is-empty" events - WebRtc_Word32 _intro; - WebRtc_Word32 _waitCounter; - - WebRtc_UWord32 _writtenSamples; - WebRtc_UWord32 _writtenSamplesOld; - WebRtc_UWord32 _playedSamplesOld; - - WebRtc_UWord32 _sndCardPlayDelay; - WebRtc_UWord32 _sndCardRecDelay; - - WebRtc_UWord32 _plSampOld; - WebRtc_UWord32 _rcSampOld; - - WebRtc_UWord32 _read_samples; - WebRtc_UWord32 _read_samples_old; - WebRtc_UWord32 _rec_samples_old; - - // State that detects driver problems: - WebRtc_Word32 _dc_diff_mean; - WebRtc_Word32 _dc_y_prev; - WebRtc_Word32 _dc_penalty_counter; - WebRtc_Word32 _dc_prevtime; - WebRtc_UWord32 _dc_prevplay; - - WebRtc_UWord32 _recordedBytes; // accumulated #recorded bytes (reset periodically) - WebRtc_UWord32 _prevRecByteCheckTime; // time when we last checked the recording process - - // CPU load measurements - LARGE_INTEGER _perfFreq; - LONGLONG _playAcc; // accumulated time for playout callback - float _avgCPULoad; // average total (rec+play) CPU load - - WebRtc_Word32 _wrapCounter; - - WebRtc_Word32 _useHeader; - WebRtc_Word16 _timesdwBytes; - WebRtc_Word32 _no_of_msecleft_warnings; - WebRtc_Word32 _writeErrors; - WebRtc_Word32 _timerFaults; - WebRtc_Word32 _timerRestartAttempts; - - WebRtc_UWord16 _playWarning; - WebRtc_UWord16 _playError; - WebRtc_UWord16 _recWarning; - WebRtc_UWord16 _recError; - - WebRtc_UWord32 _newMicLevel; - WebRtc_UWord32 _minMicVolume; - WebRtc_UWord32 _maxMicVolume; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_WINDOWS_WAVE_H diff --git a/modules/audio_device/main/source/Windows/audio_mixer_manager.cc b/modules/audio_device/main/source/Windows/audio_mixer_manager.cc deleted file mode 100644 index f97290c3a..000000000 --- a/modules/audio_device/main/source/Windows/audio_mixer_manager.cc +++ /dev/null @@ -1,2663 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_mixer_manager.h" -#include "trace.h" - -#include // StringCchCopy(), StringCchCat(), StringCchPrintf() -#include // assert() - -#ifdef _WIN32 -// removes warning: "reinterpret_cast: conversion from 'UINT' to 'HMIXEROBJ' -// of greater size" -#pragma warning(disable:4312) -#endif - -// Avoids the need of Windows 7 SDK -#ifndef WAVE_MAPPED_kDefaultCommunicationDevice -#define WAVE_MAPPED_kDefaultCommunicationDevice 0x0010 -#endif - -namespace webrtc { - -// ============================================================================ -// CONSTRUCTION/DESTRUCTION -// ============================================================================ - -AudioMixerManager::AudioMixerManager(const WebRtc_Word32 id) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _id(id), - _inputMixerHandle(NULL), - _outputMixerHandle(NULL) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s constructed", __FUNCTION__); - ClearSpeakerState(); - ClearMicrophoneState(); -} - -AudioMixerManager::~AudioMixerManager() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destructed", __FUNCTION__); - - Close(); - - delete &_critSect; -} - -// ============================================================================ -// PUBLIC METHODS -// ============================================================================ - -// ---------------------------------------------------------------------------- -// Close -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::Close() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerHandle != NULL) - { - mixerClose(_outputMixerHandle); - _outputMixerHandle = NULL; - } - if (_inputMixerHandle != NULL) - { - mixerClose(_inputMixerHandle); - _inputMixerHandle = NULL; - } - return 0; - -} - -// ---------------------------------------------------------------------------- -// CloseSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::CloseSpeaker() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerHandle == NULL) - { - return -1; - } - - ClearSpeakerState(_outputMixerID); - - mixerClose(_outputMixerHandle); - _outputMixerHandle = NULL; - - return 0; -} - -// ---------------------------------------------------------------------------- -// CloseMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::CloseMicrophone() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - return -1; - } - - ClearMicrophoneState(_inputMixerID); - - mixerClose(_inputMixerHandle); - _inputMixerHandle = NULL; - - return 0; -} - -// ---------------------------------------------------------------------------- -// EnumerateAll -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::EnumerateAll() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - UINT nDevices = mixerGetNumDevs(); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices); - - MIXERCAPS caps; - MIXERLINE destLine; - MIXERLINE sourceLine; - MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS]; - - UINT mixId(0); - UINT destId(0); - UINT sourceId(0); - - for (mixId = 0; mixId < nDevices; mixId++) - { - if (!GetCapabilities(mixId, caps, true)) - continue; - - for (destId = 0; destId < caps.cDestinations; destId++) - { - GetDestinationLineInfo(mixId, destId, destLine, true); - GetAllLineControls(mixId, destLine, controlArray, true); - - for (sourceId = 0; sourceId < destLine.cConnections; sourceId++) - { - GetSourceLineInfo(mixId, destId, sourceId, sourceLine, true); - GetAllLineControls(mixId, sourceLine, controlArray, true); - } - } - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// EnumerateSpeakers -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::EnumerateSpeakers() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - UINT nDevices = mixerGetNumDevs(); - if (nDevices > MAX_NUMBER_MIXER_DEVICES) - { - assert(false); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices); - - MIXERCAPS caps; - MIXERLINE destLine; - MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS]; - - UINT mixId(0); - UINT destId(0); - - ClearSpeakerState(); - - // scan all avaliable mixer devices - for (mixId = 0; mixId < nDevices; mixId++) - { - // get capabilities for the specified mixer ID - GetCapabilities(mixId, caps); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[mixerID=%d] %s: ", mixId, WideToUTF8(caps.szPname)); - // scan all avaliable destinations for this mixer - for (destId = 0; destId < caps.cDestinations; destId++) - { - GetDestinationLineInfo(mixId, destId, destLine); - if ((destLine.cControls == 0) || // no controls or - (destLine.cConnections == 0) || // no source lines or - (destLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED) || // disconnected or - !(destLine.fdwLine & MIXERLINE_LINEF_ACTIVE)) // inactive - { - // don't store this line ID since it will not be possible to control - continue; - } - if ((destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS) || - (destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_HEADPHONES)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found valid speaker/headphone (name: %s, ID: %u)", WideToUTF8(destLine.szName), destLine.dwLineID); - _speakerState[mixId].dwLineID = destLine.dwLineID; - _speakerState[mixId].speakerIsValid = true; - // retrieve all controls for the speaker component - GetAllLineControls(mixId, destLine, controlArray); - for (UINT c = 0; c < destLine.cControls; c++) - { - if (controlArray[c].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) - { - _speakerState[mixId].dwVolumeControlID = controlArray[c].dwControlID; - _speakerState[mixId].volumeControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[c].szName), controlArray[c].dwControlID); - } - else if (controlArray[c].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) - { - _speakerState[mixId].dwMuteControlID = controlArray[c].dwControlID; - _speakerState[mixId].muteControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[c].szName), controlArray[c].dwControlID); - } - } - break; - } - } - if (!SpeakerIsValid(mixId)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to find a valid speaker destination line", mixId); - } - } - - if (ValidSpeakers() == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to locate any valid speaker line"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// EnumerateMicrophones -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::EnumerateMicrophones() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - UINT nDevices = mixerGetNumDevs(); - if (nDevices > MAX_NUMBER_MIXER_DEVICES) - { - assert(false); - return -1; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "#mixer devices: %u", nDevices); - - MIXERCAPS caps; - MIXERLINE destLine; - MIXERLINE sourceLine; - MIXERCONTROL controlArray[MAX_NUMBER_OF_LINE_CONTROLS]; - - UINT mixId(0); - UINT destId(0); - - ClearMicrophoneState(); - - // scan all avaliable mixer devices - for (mixId = 0; mixId < nDevices; mixId++) - { - // get capabilities for the specified mixer ID - GetCapabilities(mixId, caps); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[mixerID=%d] %s: ", mixId, WideToUTF8(caps.szPname)); - // scan all avaliable destinations for this mixer - for (destId = 0; destId < caps.cDestinations; destId++) - { - GetDestinationLineInfo(mixId, destId, destLine); - - if ((destLine.cConnections == 0) || // no source lines or - (destLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED) || // disconnected or - !(destLine.fdwLine & MIXERLINE_LINEF_ACTIVE)) // inactive - { - // Don't store this line ID since there are no sources connected to this destination. - // Compare with the speaker side where we also exclude lines with no controls. - continue; - } - - if (destLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found valid Wave In destination (name: %s, ID: %u)", WideToUTF8(destLine.szName), destLine.dwLineID); - _microphoneState[mixId].dwLineID = destLine.dwLineID; - _microphoneState[mixId].microphoneIsValid = true; - - // retrieve all controls for the identified wave-in destination - GetAllLineControls(mixId, destLine, controlArray); - - // It seems like there are three different configurations we can find in this state: - // - // (1) The Wave-in destination contains one MUX control only - // (2) The Wave-in destination contains one or more controls where one is a volume control - // (3) On Vista and Win 7, it seems like case 2 above is extended. - // It is common that a Wave-in destination has two master controls (volume and mute), - // AND a microphone source as well with its own volume and mute controls with unique - // identifiers. Initial tests have shown that it is sufficient to modify the master - // controls only. The source controls will "follow" the master settings, hence the - // source controls seem to be redundant. - // - // For case 1, we should locate the selected source and its controls. The MUX setting will - // give us the selected source. NOTE - the selecion might not be a microphone. - // - // For case 2, the volume control works as a master level control and we should use that one. - // - // For case 3, we use the master controls only and assume that the source control will "follow". - // - // Examples of case 1: - SigmaTel Audio (built-in) - // - add more.. - // - // Examples of case 2: - Plantronics USB Headset - // - Eutectics IPP 200 USB phone - // - add more... - // - // Examples of case 3: - Realtek High Definition on Vista (TL) - // - add more... - - if ((destLine.cControls == 1) && - (controlArray[0].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)) - { - // Case 1: MUX control detected => locate the selected source and its volume control - // Note that, the selecion might not be a microphone. A warning is given for - // this case only, i.e., it is OK to control a selected Line In source as long - // as it is connected to the wave-in destination. - - UINT selection(0); - const DWORD nItemsInMux(controlArray[0].cMultipleItems); - - // decide which source line that is selected in the mux - if (GetSelectedMuxSource(mixId, controlArray[0].dwControlID, nItemsInMux, selection)) - { - // selection now contains the index of the selected source => - // read the line information for this source - if (!GetSourceLineInfo(mixId, destId, selection, sourceLine)) - { - return -1; - } - if (sourceLine.cControls == 0 || // no controls - (sourceLine.fdwLine & MIXERLINE_LINEF_DISCONNECTED) || // disconnected - !(sourceLine.fdwLine & MIXERLINE_LINEF_ACTIVE)) // inactive - { - continue; - } - - if (sourceLine.dwComponentType != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) - { - // add more details about the selected source (not a microphone) - TraceComponentType(sourceLine.dwComponentType); - // send a warning just to inform about the fact that a non-microphone source will be controlled - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "the selected (to be controlled) source is not a microphone type"); - } - - // retrieve all controls for the selected source - GetAllLineControls(mixId, sourceLine, controlArray); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "MUX selection is %u [0,%u]", selection, nItemsInMux-1); - - // scan the controls for this source and search for volume, mute and on/off (<=> boost) controls - for (UINT sc = 0; sc < sourceLine.cControls; sc++) - { - if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) - { - // store this volume control - _microphoneState[mixId].dwVolumeControlID = controlArray[sc].dwControlID; - _microphoneState[mixId].volumeControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID); - } - else if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) - { - // store this mute control - _microphoneState[mixId].dwMuteControlID = controlArray[sc].dwControlID; - _microphoneState[mixId].muteControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID); - } - else if (controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF || - controlArray[sc].dwControlType == MIXERCONTROL_CONTROLTYPE_LOUDNESS) - { - // store this on/off control (most likely a Boost control) - _microphoneState[mixId].dwOnOffControlID = controlArray[sc].dwControlID; - _microphoneState[mixId].onOffControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found on/off control (name: %s, ID: %u)", WideToUTF8(controlArray[sc].szName), controlArray[sc].dwControlID); - } - } - } - else - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to detect which source to control"); - } - - } - else if (destLine.cConnections == 1) - { - // Case 2 or Case 3: - - GetSourceLineInfo(mixId, destId, 0, sourceLine); - if ((sourceLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) && - (sourceLine.cControls > 0)) - { - // Case 3: same as Case 2 below but we have also detected a Microphone source - // with its own controls. So far, I have not been able to find any device - // where it is required to modify these controls. Until I have found such - // a device, this case will be handled as a Case 2 (see below). - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "microphone source controls will not be controlled"); - } - else if ((sourceLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) && - (sourceLine.cControls == 0)) - { - // default state on non Vista/Win 7 machines - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "microphone source has no controls => use master controls instead"); - } - else - { - // add more details about the selected source (not a microphone) - TraceComponentType(sourceLine.dwComponentType); - // send a warning just to inform about the fact that a non-microphone source will be controlled - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "the connected (to be controlled) source is not a microphone type"); - } - - // Case 2 : one source only and no MUX control detected => - // locate the master volume control (and mute + boost controls if possible) - - // scan the controls for this wave-in destination and search for volume, mute and on/off (<=> boost) controls - for (UINT dc = 0; dc < destLine.cControls; dc++) - { - if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) - { - // store this volume control - _microphoneState[mixId].dwVolumeControlID = controlArray[dc].dwControlID; - _microphoneState[mixId].volumeControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found volume control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID); - } - else if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) - { - // store this mute control - _microphoneState[mixId].dwMuteControlID = controlArray[dc].dwControlID; - _microphoneState[mixId].muteControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found mute control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID); - } - else if (controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF || - controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_LOUDNESS || - controlArray[dc].dwControlType == MIXERCONTROL_CONTROLTYPE_BOOLEAN) - { - // store this on/off control - _microphoneState[mixId].dwOnOffControlID = controlArray[dc].dwControlID; - _microphoneState[mixId].onOffControlIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "found on/off control (name: %s, ID: %u)", WideToUTF8(controlArray[dc].szName), controlArray[dc].dwControlID); - } - } - } - else - { - // We are in a state where more than one source is connected to the wave-in destination. - // I am bailing out here for now until I understand this case better. - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to locate valid microphone controls for this mixer"); - } - break; - } - } // for (destId = 0; destId < caps.cDestinations; destId++) - - if (!MicrophoneIsValid(mixId)) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unable to find a valid microphone destination line", mixId); - } - } // for (mixId = 0; mixId < nDevices; mixId++) - - if (ValidMicrophones() == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to locate any valid microphone line"); - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// OpenSpeaker I(II) -// -// Verifies that the mixer contains a valid speaker destination line. -// Avoids opening the mixer if valid control has not been found. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::OpenSpeaker(AudioDeviceModule::WindowsDeviceType device) -{ - if (device == AudioDeviceModule::kDefaultDevice) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(kDefaultCommunicationDevice)"); - } - - CriticalSectionScoped lock(_critSect); - - // Close any existing output mixer handle - // - if (_outputMixerHandle != NULL) - { - mixerClose(_outputMixerHandle); - _outputMixerHandle = NULL; - } - - MMRESULT res; - WAVEFORMATEX waveFormat; - HWAVEOUT hWaveOut(NULL); - - waveFormat.wFormatTag = WAVE_FORMAT_PCM ; - waveFormat.nChannels = 2; - waveFormat.nSamplesPerSec = 48000; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; - - // We need a waveform-audio output handle for the currently selected output device. - // This handle will then give us the corresponding mixer identifier. Once the mixer - // ID is known, it is possible to open the output mixer. - // - if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - // check if it is possible to open the default communication device (supported on Windows 7) - res = waveOutOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | - WAVE_MAPPED_kDefaultCommunicationDevice | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // if so, open the default communication device for real - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_kDefaultCommunicationDevice); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); - } - else - { - // use default device since default communication device was not avaliable - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "unable to open default communication device => using default instead"); - } - } - else if (device == AudioDeviceModule::kDefaultDevice) - { - // open default device since it has been requested - res = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default output device"); - } - - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutOpen() failed (err=%d)", res); - TraceWaveOutError(res); - } - - UINT mixerId(0); - HMIXER hMixer(NULL); - - // Retrieve the device identifier for a mixer device associated with the - // aquired waveform-audio output handle. - // - res = mixerGetID((HMIXEROBJ)hWaveOut, &mixerId, MIXER_OBJECTF_HWAVEOUT); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEOUT) failed (err=%d)", res); - // identification failed => use default mixer identifier (=0) - mixerId = 0; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified output device <=> mixer ID %u", mixerId); - - // The waveform-audio output handle is no longer needed. - // - waveOutClose(hWaveOut); - - // Verify that the mixer contains a valid speaker destination line. - // Avoid opening the mixer if valid control has not been found. - // - if (!SpeakerIsValid(mixerId)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the speaker volume for this mixer device"); - return -1; - } - - // Open the specified mixer device and ensure that the device will not - // be removed until the application closes the handle. - // - res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res); - } - - // Store the output mixer handle and active mixer identifier - // - _outputMixerHandle = hMixer; - _outputMixerID = mixerId; - - if (_outputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the output mixer device is now open (0x%x)", _outputMixerHandle); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// OpenSpeaker II(II) -// -// Verifies that the mixer contains a valid speaker destination line. -// Avoids opening the mixer if valid control has not been found. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::OpenSpeaker(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenSpeaker(index=%d)", index); - - CriticalSectionScoped lock(_critSect); - - // Close any existing output mixer handle - // - if (_outputMixerHandle != NULL) - { - mixerClose(_outputMixerHandle); - _outputMixerHandle = NULL; - } - - MMRESULT res; - WAVEFORMATEX waveFormat; - HWAVEOUT hWaveOut(NULL); - - const UINT deviceID(index); // use index parameter as device identifier - - waveFormat.wFormatTag = WAVE_FORMAT_PCM ; - waveFormat.nChannels = 2; - waveFormat.nSamplesPerSec = 48000; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; - - // We need a waveform-audio output handle for the currently selected output device. - // This handle will then give us the corresponding mixer identifier. Once the mixer - // ID is known, it is possible to open the output mixer. - // - res = waveOutOpen(&hWaveOut, deviceID, &waveFormat, 0, 0, CALLBACK_NULL); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveOutOpen(deviceID=%u) failed (err=%d)", index, res); - TraceWaveOutError(res); - } - - UINT mixerId(0); - HMIXER hMixer(NULL); - - // Retrieve the device identifier for a mixer device associated with the - // aquired waveform-audio output handle. - // - res = mixerGetID((HMIXEROBJ)hWaveOut, &mixerId, MIXER_OBJECTF_HWAVEOUT); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEOUT) failed (err=%d)", res); - // identification failed => use default mixer identifier (=0) - mixerId = 0; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified output device <=> mixer ID %u", mixerId); - - // The waveform-audio output handle is no longer needed. - // - waveOutClose(hWaveOut); - - // Verify that the mixer contains a valid speaker destination line. - // Avoid opening the mixer if valid control has not been found. - // - if (!SpeakerIsValid(mixerId)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the speaker volume for this mixer device"); - return -1; - } - - // Open the specified mixer device and ensure that the device will not - // be removed until the application closes the handle. - // - res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res); - } - - // Store the output mixer handle and active mixer identifier - // - _outputMixerHandle = hMixer; - _outputMixerID = mixerId; - - if (_outputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the output mixer device is now open (0x%x)", _outputMixerHandle); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// OpenMicrophone I(II) -// -// Verifies that the mixer contains a valid wave-in destination line. -// Avoids opening the mixer if valid control has not been found. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::OpenMicrophone(AudioDeviceModule::WindowsDeviceType device) -{ - if (device == AudioDeviceModule::kDefaultDevice) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(kDefaultDevice)"); - } - else if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(kDefaultCommunicationDevice)"); - } - - CriticalSectionScoped lock(_critSect); - - // Close any existing output mixer handle - // - if (_inputMixerHandle != NULL) - { - mixerClose(_inputMixerHandle); - _inputMixerHandle = NULL; - } - - MMRESULT res; - WAVEFORMATEX waveFormat; - HWAVEIN hWaveIn(NULL); - - waveFormat.wFormatTag = WAVE_FORMAT_PCM ; - waveFormat.nChannels = 1; - waveFormat.nSamplesPerSec = 48000; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0 ; - - // We need a waveform-audio input handle for the currently selected input device. - // This handle will then give us the corresponding mixer identifier. Once the mixer - // ID is known, it is possible to open the input mixer. - // - if (device == AudioDeviceModule::kDefaultCommunicationDevice) - { - // check if it is possible to open the default communication device (supported on Windows 7) - res = waveInOpen(NULL, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | - WAVE_MAPPED_kDefaultCommunicationDevice | WAVE_FORMAT_QUERY); - if (MMSYSERR_NOERROR == res) - { - // if so, open the default communication device for real - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL | WAVE_MAPPED_kDefaultCommunicationDevice); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default communication device"); - } - else - { - // use default device since default communication device was not avaliable - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "unable to open default communication device => using default instead"); - } - } - else if (device == AudioDeviceModule::kDefaultDevice) - { - // open default device since it has been requested - res = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "opening default input device"); - } - - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInOpen() failed (err=%d)", res); - TraceWaveInError(res); - } - - UINT mixerId(0); - HMIXER hMixer(NULL); - - // Retrieve the device identifier for a mixer device associated with the - // aquired waveform-audio input handle. - // - res = mixerGetID((HMIXEROBJ)hWaveIn, &mixerId, MIXER_OBJECTF_HWAVEIN); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEIN) failed (err=%d)", res); - // identification failed => use default mixer identifier (=0) - mixerId = 0; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified input device <=> mixer ID %u", mixerId); - - // The waveform-audio input handle is no longer needed. - // - waveInClose(hWaveIn); - - // Verify that the mixer contains a valid wave-in destination line and a volume control. - // Avoid opening the mixer if valid control has not been found. - // - if (!MicrophoneIsValid(mixerId)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the microphone volume for this mixer device"); - return -1; - } - - // Open the specified mixer device and ensure that the device will not - // be removed until the application closes the handle. - // - res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res); - } - - // Store the input mixer handle and active mixer identifier - // - _inputMixerHandle = hMixer; - _inputMixerID = mixerId; - - if (_inputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the input mixer device is now open (0x%x)", _inputMixerHandle); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// OpenMicrophone II(II) -// -// Verifies that the mixer contains a valid wave-in destination line. -// Avoids opening the mixer if valid control has not been found. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::OpenMicrophone(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::OpenMicrophone(index=%d)", index); - - CriticalSectionScoped lock(_critSect); - - // Close any existing input mixer handle - // - if (_inputMixerHandle != NULL) - { - mixerClose(_inputMixerHandle); - _inputMixerHandle = NULL; - } - - MMRESULT res; - WAVEFORMATEX waveFormat; - HWAVEIN hWaveIn(NULL); - - const UINT deviceID(index); // use index parameter as device identifier - - waveFormat.wFormatTag = WAVE_FORMAT_PCM ; - waveFormat.nChannels = 1; - waveFormat.nSamplesPerSec = 48000; - waveFormat.wBitsPerSample = 16; - waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; - waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; - waveFormat.cbSize = 0; - - // We need a waveform-audio input handle for the currently selected input device. - // This handle will then give us the corresponding mixer identifier. Once the mixer - // ID is known, it is possible to open the input mixer. - // - res = waveInOpen(&hWaveIn, deviceID, &waveFormat, 0, 0, CALLBACK_NULL); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "waveInOpen(deviceID=%u) failed (err=%d)", index, res); - TraceWaveInError(res); - } - - UINT mixerId(0); - HMIXER hMixer(NULL); - - // Retrieve the device identifier for a mixer device associated with the - // aquired waveform-audio input handle. - // - res = mixerGetID((HMIXEROBJ)hWaveIn, &mixerId, MIXER_OBJECTF_HWAVEIN); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetID(MIXER_OBJECTF_HWAVEIN) failed (err=%d)", res); - // identification failed => use default mixer identifier (=0) - mixerId = 0; - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "specified input device <=> mixer ID %u", mixerId); - - // The waveform-audio input handle is no longer needed. - // - waveInClose(hWaveIn); - - // Verify that the mixer contains a valid wave-in destination line. - // Avoid opening the mixer if valid control has not been found. - // - if (!MicrophoneIsValid(mixerId)) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to control the microphone volume for this mixer device"); - return -1; - } - - // Open the specified mixer device and ensure that the device will not - // be removed until the application closes the handle. - // - res = mixerOpen(&hMixer, mixerId, 0, 0, MIXER_OBJECTF_MIXER); - if (MMSYSERR_NOERROR != res) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerOpen() failed (err=%d)", res); - } - - // Store the input mixer handle and active mixer identifier - // - _inputMixerHandle = hMixer; - _inputMixerID = mixerId; - - if (_inputMixerHandle != NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "the input mixer device is now open (0x%x)", _inputMixerHandle); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_outputMixerHandle != NULL); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - return (_inputMixerHandle != NULL); -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetSpeakerVolume(volume=%u)", volume); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - const UINT mixerID(_outputMixerID); - const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID); - DWORD dwValue(volume); - - // Set one unsigned control value for a specified volume-control identifier - // - if (!SetUnsignedControlValue(mixerID, dwControlID, dwValue)) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// -// Note that (MIXERCONTROL_CONTROLTYPE_VOLUME & MIXERCONTROL_CT_UNITS_MASK) -// always equals MIXERCONTROL_CT_UNITS_UNSIGNED; -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SpeakerVolume(WebRtc_UWord32& volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - const UINT mixerID(_outputMixerID); - const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID); - DWORD dwValue(0); - - // Retrieve one unsigned control value for a specified volume-control identifier - // - if (!GetUnsignedControlValue(mixerID, dwControlID, dwValue)) - { - return -1; - } - - volume = dwValue; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// -// Note that (MIXERCONTROL_CONTROLTYPE_VOLUME & MIXERCONTROL_CT_UNITS_MASK) -// always equals MIXERCONTROL_CT_UNITS_UNSIGNED -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - const UINT mixerID(_outputMixerID); - const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, dwControlID, mixerControl)) - { - return -1; - } - - maxVolume = mixerControl.Bounds.dwMaximum; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MinSpeakerVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - const UINT mixerID(_outputMixerID); - const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, dwControlID, mixerControl)) - { - return -1; - } - - minVolume = mixerControl.Bounds.dwMinimum; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - const UINT mixerID(_outputMixerID); - const DWORD dwControlID(_speakerState[_outputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, _speakerState[mixerID].dwVolumeControlID, mixerControl)) - { - return -1; - } - - stepSize = static_cast (mixerControl.Metrics.cSteps); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SpeakerVolumeIsAvailable(bool& available) -{ - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - available = _speakerState[_outputMixerID].volumeControlIsValid; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SpeakerMuteIsAvailable(bool& available) -{ - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - available = _speakerState[_outputMixerID].muteControlIsValid; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// -// This mute function works a master mute for the output speaker. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetSpeakerMute(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - // Ensure that the selected speaker destination has a valid mute control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the output mixer handle exists. - // - if (!_speakerState[_outputMixerID].muteControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this speaker line"); - return -1; - } - - const DWORD dwControlID(_speakerState[_outputMixerID].dwMuteControlID); - - // Set one boolean control value for the specified mute-control - // - if (!SetBooleanControlValue(_outputMixerID, dwControlID, enable)) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SpeakerMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_outputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable output mixer exists"); - return -1; - } - - // Ensure that the selected speaker destination has a valid mute control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the output mixer handle exists. - // - if (!_speakerState[_outputMixerID].muteControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this speaker line"); - return -1; - } - - const DWORD dwControlID(_speakerState[_outputMixerID].dwMuteControlID); - bool value(false); - - // Retrieve one boolean control value for a specified mute-control identifier - // - if (!GetBooleanControlValue(_outputMixerID, dwControlID, value)) - { - return -1; - } - - enabled = value; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneMuteIsAvailable(bool& available) -{ - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - available = _microphoneState[_inputMixerID].muteControlIsValid; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// -// This mute function works a master mute for the input microphone. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetMicrophoneMute(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected wave-in destinationhas a valid mute control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the input mixer handle exists. - // - if (!_microphoneState[_inputMixerID].muteControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this microphone line"); - return -1; - } - - const DWORD dwControlID(_microphoneState[_inputMixerID].dwMuteControlID); - - // Set one boolean control value for the specified mute-control - // - if (!SetBooleanControlValue(_inputMixerID, dwControlID, enable)) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneMute(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected wave-in destinationhas a valid mute control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the input mixer handle exists. - // - if (!_microphoneState[_inputMixerID].muteControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "it is not possible to mute this microphone line"); - return -1; - } - - const DWORD dwControlID(_microphoneState[_inputMixerID].dwMuteControlID); - bool value(false); - - // Retrieve one boolean control value for a specified mute-control identifier - // - if (!GetBooleanControlValue(_inputMixerID, dwControlID, value)) - { - return -1; - } - - enabled = value; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneBoostIsAvailable(bool& available) -{ - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - available = _microphoneState[_inputMixerID].onOffControlIsValid; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetMicrophoneBoost(enable=%u)", enable); - - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected wave-in destination has a valid boost (on/off) control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the input mixer handle exists. - // - if (!_microphoneState[_inputMixerID].onOffControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no boost control exists for this wave-in line"); - return -1; - } - - const DWORD dwControlID(_microphoneState[_inputMixerID].dwOnOffControlID); - - // Set one boolean control value for the specified boost (on/off) control - // - if (!SetBooleanControlValue(_inputMixerID, dwControlID, enable)) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneBoost(bool& enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - // Ensure that the selected wave-in destination has a valid boost (on/off) control. - // If so, its identifier was stored during the enumeration phase which must - // have taken place since the input mixer handle exists. - // - if (!_microphoneState[_inputMixerID].onOffControlIsValid) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no boost control exists for this wave-in line"); - return -1; - } - - const DWORD dwControlID(_microphoneState[_inputMixerID].dwOnOffControlID); - bool value(false); - - // Retrieve one boolean control value for a specified boost-control identifier - // - if (!GetBooleanControlValue(_inputMixerID, dwControlID, value)) - { - return -1; - } - - enabled = value; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneVolumeIsAvailable(bool& available) -{ - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - available = _microphoneState[_inputMixerID].volumeControlIsValid; - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - const UINT mixerID(_inputMixerID); - const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID); - DWORD dwValue(volume); - - // Set one unsigned control value for a specified volume-control identifier - // - if (!SetUnsignedControlValue(mixerID, dwControlID, dwValue)) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneVolume(WebRtc_UWord32& volume) const -{ - CriticalSectionScoped lock(_critSect); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - const UINT mixerID(_inputMixerID); - const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID); - DWORD dwValue(0); - - // Retrieve one unsigned control value for a specified volume-control identifier - // - if (!GetUnsignedControlValue(mixerID, dwControlID, dwValue)) - { - return -1; - } - - volume = dwValue; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - const UINT mixerID(_inputMixerID); - const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, dwControlID, mixerControl)) - { - return -1; - } - - maxVolume = mixerControl.Bounds.dwMaximum; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - const UINT mixerID(_inputMixerID); - const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, dwControlID, mixerControl)) - { - return -1; - } - - minVolume = mixerControl.Bounds.dwMinimum; - - return 0; -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioMixerManager::MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_inputMixerHandle == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "no avaliable input mixer exists"); - return -1; - } - - const UINT mixerID(_inputMixerID); - const DWORD dwControlID(_microphoneState[_inputMixerID].dwVolumeControlID); - MIXERCONTROL mixerControl; - - // Retrieve one control line for a specified volume-control identifier - // - if (!GetLineControl(mixerID, dwControlID, mixerControl)) - { - return -1; - } - - stepSize = static_cast (mixerControl.Metrics.cSteps); - - return 0; -} - -// ============================================================================ -// PRIVATE METHODS -// ============================================================================ - -// ---------------------------------------------------------------------------- -// Devices -// -// A given audio card has one Mixer device associated with it. All of the -// various components on that card are controlled through that card's one -// Mixer device. -// ---------------------------------------------------------------------------- - -UINT AudioMixerManager::Devices() const -{ - UINT nDevs = mixerGetNumDevs(); - return nDevs; -} - -// ---------------------------------------------------------------------------- -// DestinationLines -// -// # destination lines given mixer ID. -// ---------------------------------------------------------------------------- - -UINT AudioMixerManager::DestinationLines(UINT mixId) const -{ - MIXERCAPS caps; - if (!GetCapabilities(mixId, caps)) - { - return 0; - } - return (caps.cDestinations); -} -// ---------------------------------------------------------------------------- -// DestinationLines -// -// # source lines given mixer ID and destination ID. -// ---------------------------------------------------------------------------- - -UINT AudioMixerManager::SourceLines(UINT mixId, DWORD destId) const -{ - MIXERLINE dline; - if (!GetDestinationLineInfo(mixId, destId, dline)) - { - return 0; - } - return (dline.cConnections); -} - -// ---------------------------------------------------------------------------- -// GetCapabilities -// -// Queries a specified mixer device to determine its capabilities. -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetCapabilities(UINT mixId, MIXERCAPS& caps, bool trace) const -{ - MMRESULT res; - MIXERCAPS mcaps; - - res = mixerGetDevCaps(mixId, &mcaps, sizeof(MIXERCAPS)); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetDevCaps() failed (err=%d)", res); - return false; - } - - memcpy(&caps, &mcaps, sizeof(MIXERCAPS)); - - if (trace) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Mixer ID %u:", mixId); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", caps.wMid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u", caps.wPid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "version of driver : %u", caps.vDriverVersion); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", WideToUTF8(caps.szPname)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "misc. support bits : %u", caps.fdwSupport); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "count of destinations: %u (+)", caps.cDestinations); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "==============================================================="); - } - - if (caps.cDestinations == 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "invalid number of mixer destinations"); - return false; - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetDestinationLineInfo -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetDestinationLineInfo(UINT mixId, DWORD destId, MIXERLINE& line, bool trace) const -{ - MMRESULT res; - MIXERLINE mline; - - mline.cbStruct = sizeof(MIXERLINE); - mline.dwDestination = destId; // max destination index is cDestinations-1 - mline.dwSource = 0; // not set for MIXER_GETLINEINFOF_DESTINATION - - // Retrieve information about the specified destination line of a mixer device. - // Note that we use the mixer ID here and not a handle to an opened mixer. - // It is not required to open the mixer for enumeration purposes only. - // - res = mixerGetLineInfo(reinterpret_cast(mixId), &mline, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_DESTINATION); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineInfo(MIXER_GETLINEINFOF_DESTINATION) failed (err=%d)", res); - return false; - } - - memcpy(&line, &mline, sizeof(MIXERLINE)); - - if (trace) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "> Destination Line ID %u:", destId); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "destination line index : %u", mline.dwDestination); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwLineID : %lu (unique)", mline.dwLineID); - TraceStatusAndSupportFlags(mline.fdwLine); - TraceComponentType(mline.dwComponentType); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "count of channels : %u", mline.cChannels); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# audio source lines : %u (+)", mline.cConnections); // valid only for destinations - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# controls : %u (*)", mline.cControls); // can be zero - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "short name : %s", WideToUTF8(mline.szShortName)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name : %s", WideToUTF8(mline.szName)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - TraceTargetType(mline.Target.dwType); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "target device ID : %lu", mline.Target.dwDeviceID); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "manufacturer ID : %u", mline.Target.wMid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product ID : %u", mline.Target.wPid); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "driver version : %u", mline.Target.vDriverVersion); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "product name : %s", WideToUTF8(mline.Target.szPname)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "---------------------------------------------------------------"); - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetSourceLineInfo -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetSourceLineInfo(UINT mixId, DWORD destId, DWORD srcId, MIXERLINE& line, bool trace) const -{ - MMRESULT res; - MIXERLINE mline; - - mline.cbStruct = sizeof(MIXERLINE); - mline.dwDestination = destId; // we want the source info for this destination - mline.dwSource = srcId; // source index (enumerate over these) - - // Retrieve information about the specified source line of a mixer device. - // Note that we use the mixer ID here and not a handle to an opened mixer. - // It is not required to open the mixer for enumeration purposes only. - // - res = mixerGetLineInfo(reinterpret_cast(mixId), &mline, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_SOURCE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineInfo(MIXER_GETLINEINFOF_SOURCE) failed (err=%d)", res); - return false; - } - - memcpy(&line, &mline, sizeof(MIXERLINE)); - - if (trace) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " >> Source Line ID %u:", srcId); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "destination line index : %u", mline.dwDestination); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwSource : %u", mline.dwSource); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwLineID : %lu (unique)", mline.dwLineID); - TraceStatusAndSupportFlags(mline.fdwLine); - TraceComponentType(mline.dwComponentType); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "# controls : %u (*)", mline.cControls); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name : %s", WideToUTF8(mline.szName)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - TraceTargetType(mline.Target.dwType); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "---------------------------------------------------------------"); - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetAllLineControls -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetAllLineControls(UINT mixId, const MIXERLINE& line, MIXERCONTROL* controlArray, bool trace) const -{ - // Ensure that we don't try to aquire information if there are no controls for this line - // - if (line.cControls == 0) - return false; - - MMRESULT res; - MIXERLINECONTROLS mlineControls; // contains information about the controls of an audio line - - mlineControls.dwLineID = line.dwLineID; // unique audio line identifier - mlineControls.cControls = line.cControls; // number of controls associated with the line - mlineControls.pamxctrl = controlArray; // points to the first MIXERCONTROL structure to be filled - mlineControls.cbStruct = sizeof(MIXERLINECONTROLS); - mlineControls.cbmxctrl = sizeof(MIXERCONTROL); - - // Get information on ALL controls associated with the specified audio line - // - res = mixerGetLineControls(reinterpret_cast(mixId), &mlineControls, MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ALL); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineControls(MIXER_GETLINECONTROLSF_ALL) failed (err=%d)", res); - return false; - } - - if (trace) - { - for (UINT c = 0; c < line.cControls; c++) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " >> Control ID %u:", c); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "dwControlID : %u (unique)", controlArray[c].dwControlID); - TraceControlType(controlArray[c].dwControlType); - TraceControlStatusAndSupportFlags(controlArray[c].fdwControl); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cMultipleItems : %u", controlArray[c].cMultipleItems); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "short name : %s", WideToUTF8(controlArray[c].szShortName)); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "full name : %s", WideToUTF8(controlArray[c].szName)); - if ((controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_SIGNED) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "min signed value : %d", controlArray[c].Bounds.lMinimum); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "max signed value : %d", controlArray[c].Bounds.lMaximum); - } - else if ((controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_UNSIGNED || - (controlArray[c].dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_BOOLEAN) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "min unsigned value : %u", controlArray[c].Bounds.dwMinimum); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "max unsigned value : %u", controlArray[c].Bounds.dwMaximum); - } - if (controlArray[c].dwControlType != MIXERCONTROL_CONTROLTYPE_CUSTOM) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "cSteps : %u", controlArray[c].Metrics.cSteps); - } - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "..............................................................."); - GetControlDetails(mixId, controlArray[c], true); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "..............................................................."); - - } - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetLineControls -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetLineControl(UINT mixId, DWORD dwControlID, MIXERCONTROL& control) const -{ - MMRESULT res; - MIXERLINECONTROLS mlineControl; - - mlineControl.dwControlID = dwControlID; - mlineControl.cControls = 1; - mlineControl.pamxctrl = &control; - mlineControl.cbStruct = sizeof(MIXERLINECONTROLS); - mlineControl.cbmxctrl = sizeof(MIXERCONTROL); - - // Get information on one controls associated with the specified conrol identifier - // - res = mixerGetLineControls(reinterpret_cast(mixId), &mlineControl, MIXER_OBJECTF_MIXER | MIXER_GETLINECONTROLSF_ONEBYID); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetLineControls(MIXER_GETLINECONTROLSF_ONEBYID) failed (err=%d)", res); - return false; - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetControlDetails -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetControlDetails(UINT mixId, MIXERCONTROL& controlArray, bool trace) const -{ - assert(controlArray.cMultipleItems <= MAX_NUMBER_OF_MULTIPLE_ITEMS); - - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - - MIXERCONTROLDETAILS_UNSIGNED valueUnsigned[MAX_NUMBER_OF_MULTIPLE_ITEMS]; - MIXERCONTROLDETAILS_SIGNED valueSigned[MAX_NUMBER_OF_MULTIPLE_ITEMS]; - MIXERCONTROLDETAILS_BOOLEAN valueBoolean[MAX_NUMBER_OF_MULTIPLE_ITEMS]; - - enum ControlType - { - CT_UNITS_UNSIGNED, - CT_UNITS_SIGNED, - CT_UNITS_BOOLEAN - }; - - ControlType ctype(CT_UNITS_UNSIGNED); - - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.dwControlID = controlArray.dwControlID; // control identifier - controlDetails.cChannels = 1; // we need to set values as if they were uniform - controlDetails.cMultipleItems = controlArray.cMultipleItems; // only nonzero for CONTROLF_MULTIPLE controls - // can e.g. happen for CONTROLTYPE_MUX - if (controlDetails.cMultipleItems > MAX_NUMBER_OF_MULTIPLE_ITEMS) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "cMultipleItems > %d", MAX_NUMBER_OF_MULTIPLE_ITEMS); - controlDetails.cMultipleItems = MAX_NUMBER_OF_MULTIPLE_ITEMS; - } - - if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_SIGNED) - { - ctype = CT_UNITS_SIGNED; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED); - controlDetails.paDetails = &valueSigned[0]; - } - else if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_UNSIGNED) - { - ctype = CT_UNITS_UNSIGNED; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - controlDetails.paDetails = &valueUnsigned[0]; - } - else if ((controlArray.dwControlType & MIXERCONTROL_CT_UNITS_MASK) == MIXERCONTROL_CT_UNITS_BOOLEAN) - { - ctype = CT_UNITS_BOOLEAN; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); - controlDetails.paDetails = &valueBoolean[0]; - } - - // Retrieve a control's value - // - res = mixerGetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - if (trace) - { - UINT nItems(1); - nItems = (controlDetails.cMultipleItems > 0 ? controlDetails.cMultipleItems : 1); - for (UINT i = 0; i < nItems; i++) - { - if (ctype == CT_UNITS_SIGNED) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "signed value : %d", valueSigned[i].lValue); - } - else if (ctype == CT_UNITS_UNSIGNED) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "unsigned value : %u", valueUnsigned[i].dwValue); - } - else if (ctype == CT_UNITS_BOOLEAN) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "boolean value : %u", valueBoolean[i].fValue); - } - } - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetUnsignedControlValue -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD& dwValue) const -{ - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - MIXERCONTROLDETAILS_UNSIGNED valueUnsigned; - - controlDetails.dwControlID = dwControlID; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - controlDetails.paDetails = &valueUnsigned; - - // Retrieve the unsigned value - // - res = mixerGetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - // Deliver the retrieved value - // - dwValue = valueUnsigned.dwValue; - - return true; -} - -// ---------------------------------------------------------------------------- -// SetUnsignedControlValue -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::SetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD dwValue) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "AudioMixerManager::SetUnsignedControlValue(mixId=%u, dwControlID=%d, dwValue=%d)", mixId, dwControlID, dwValue); - - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - MIXERCONTROLDETAILS_UNSIGNED valueUnsigned; - - controlDetails.dwControlID = dwControlID; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - controlDetails.paDetails = &valueUnsigned; - - valueUnsigned.dwValue = dwValue; - - // Set the unsigned value - // - res = mixerSetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerSetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - return true; -} - -// ---------------------------------------------------------------------------- -// SetBooleanControlValue -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::SetBooleanControlValue(UINT mixId, DWORD dwControlID, bool value) const -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioMixerManager::SetBooleanControlValue(mixId=%u, dwControlID=%d, value=%d)", mixId, dwControlID, value); - - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - MIXERCONTROLDETAILS_BOOLEAN valueBoolean; - - controlDetails.dwControlID = dwControlID; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); - controlDetails.paDetails = &valueBoolean; - - if (value == true) - valueBoolean.fValue = TRUE; - else - valueBoolean.fValue = FALSE; - - // Set the boolean value - // - res = mixerSetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerSetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - return true; -} - -// ---------------------------------------------------------------------------- -// GetBooleanControlValue -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetBooleanControlValue(UINT mixId, DWORD dwControlID, bool& value) const -{ - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - MIXERCONTROLDETAILS_BOOLEAN valueBoolean; - - controlDetails.dwControlID = dwControlID; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); - controlDetails.paDetails = &valueBoolean; - - // Retrieve the boolean value - // - res = mixerGetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - // Deliver the retrieved value - // - if (valueBoolean.fValue == 0) - value = false; - else - value = true; - - return true; -} - -// ---------------------------------------------------------------------------- -// GetSelectedMuxSource -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::GetSelectedMuxSource(UINT mixId, DWORD dwControlID, DWORD cMultipleItems, UINT& index) const -{ - assert(cMultipleItems <= MAX_NUMBER_OF_MULTIPLE_ITEMS); - - MMRESULT res; - MIXERCONTROLDETAILS controlDetails; - MIXERCONTROLDETAILS_BOOLEAN valueBoolean[MAX_NUMBER_OF_MULTIPLE_ITEMS]; - memset(&valueBoolean, 0, sizeof(valueBoolean)); - - controlDetails.dwControlID = dwControlID; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = cMultipleItems; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); - controlDetails.paDetails = &valueBoolean; - - // Retrieve the boolean values - // - res = mixerGetControlDetails(reinterpret_cast(mixId), &controlDetails, MIXER_OBJECTF_MIXER | MIXER_GETCONTROLDETAILSF_VALUE); - if (res != MMSYSERR_NOERROR) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "mixerGetControlDetails(MIXER_GETCONTROLDETAILSF_VALUE) failed (err=%d)", res); - return false; - } - - // Map the current MUX setting to an index corresponding to a source index. - // e.g. with cMultipleItems = 3, - // valueBoolean[] = {1,0,0} => index = 2 - // valueBoolean[] = {0,1,0} => index = 1 - // valueBoolean[] = {0,0,1} => index = 0 - // - // If there is no "1" in the array, we assume index should be 0. - index = 0; - for (DWORD i = 0; i < cMultipleItems; i++) - { - if (valueBoolean[i].fValue > 0) - { - index = (cMultipleItems - 1) - i; - break; - } - } - - return true; -} - -// ---------------------------------------------------------------------------- -// TraceStatusAndSupportFlags -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceStatusAndSupportFlags(DWORD fdwLine) const -{ - TCHAR buf[128]; - - StringCchPrintf(buf, 128, TEXT("status & support flags : 0x%x "), fdwLine); - - switch (fdwLine) - { - case MIXERLINE_LINEF_ACTIVE: - StringCchCat(buf, 128, TEXT("(ACTIVE DESTINATION)")); - break; - case MIXERLINE_LINEF_DISCONNECTED: - StringCchCat(buf, 128, TEXT("(DISCONNECTED)")); - break; - case MIXERLINE_LINEF_SOURCE: - StringCchCat(buf, 128, TEXT("(INACTIVE SOURCE)")); - break; - case MIXERLINE_LINEF_SOURCE | MIXERLINE_LINEF_ACTIVE: - StringCchCat(buf, 128, TEXT("(ACTIVE SOURCE)")); - break; - default: - StringCchCat(buf, 128, TEXT("(INVALID)")); - break; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// TraceComponentType -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceComponentType(DWORD dwComponentType) const -{ - TCHAR buf[128]; - - StringCchPrintf(buf, 128, TEXT("component type : 0x%x "), dwComponentType); - - switch (dwComponentType) - { - // Destination - case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED: - StringCchCat(buf, 128, TEXT("(DST_UNDEFINED)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_DIGITAL: - StringCchCat(buf, 128, TEXT("(DST_DIGITAL)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_LINE: - StringCchCat(buf, 128, TEXT("(DST_LINE)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_MONITOR: - StringCchCat(buf, 128, TEXT("(DST_MONITOR)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: - StringCchCat(buf, 128, TEXT("(DST_SPEAKERS)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: - StringCchCat(buf, 128, TEXT("(DST_HEADPHONES)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE: - StringCchCat(buf, 128, TEXT("(DST_TELEPHONE)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: - StringCchCat(buf, 128, TEXT("(DST_WAVEIN)")); - break; - case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: - StringCchCat(buf, 128, TEXT("(DST_VOICEIN)")); - break; - // Source - case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: - StringCchCat(buf, 128, TEXT("(SRC_UNDEFINED)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL: - StringCchCat(buf, 128, TEXT("(SRC_DIGITAL)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_LINE: - StringCchCat(buf, 128, TEXT("(SRC_LINE)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: - StringCchCat(buf, 128, TEXT("(SRC_MICROPHONE)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: - StringCchCat(buf, 128, TEXT("(SRC_SYNTHESIZER)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: - StringCchCat(buf, 128, TEXT("(SRC_COMPACTDISC)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE: - StringCchCat(buf, 128, TEXT("(SRC_TELEPHONE)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER: - StringCchCat(buf, 128, TEXT("(SRC_PCSPEAKER)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: - StringCchCat(buf, 128, TEXT("(SRC_WAVEOUT)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY: - StringCchCat(buf, 128, TEXT("(SRC_AUXILIARY)")); - break; - case MIXERLINE_COMPONENTTYPE_SRC_ANALOG: - StringCchCat(buf, 128, TEXT("(SRC_ANALOG)")); - break; - default: - StringCchCat(buf, 128, TEXT("(INVALID)")); - break; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// TraceTargetType -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceTargetType(DWORD dwType) const -{ - TCHAR buf[128]; - - StringCchPrintf(buf, 128, TEXT("media device type : 0x%x "), dwType); - - switch (dwType) - { - case MIXERLINE_TARGETTYPE_UNDEFINED: - StringCchCat(buf, 128, TEXT("(UNDEFINED)")); - break; - case MIXERLINE_TARGETTYPE_WAVEOUT: - StringCchCat(buf, 128, TEXT("(WAVEOUT)")); - break; - case MIXERLINE_TARGETTYPE_WAVEIN: - StringCchCat(buf, 128, TEXT("(WAVEIN)")); - break; - case MIXERLINE_TARGETTYPE_MIDIOUT: - StringCchCat(buf, 128, TEXT("(MIDIOUT)")); - break; - case MIXERLINE_TARGETTYPE_MIDIIN: - StringCchCat(buf, 128, TEXT("(MIDIIN)")); - break; - default: - StringCchCat(buf, 128, TEXT("(INVALID)")); - break; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// TraceControlType -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceControlType(DWORD dwControlType) const -{ - TCHAR buf[128]; - - // Class type classification - // - StringCchPrintf(buf, 128, TEXT("class type : 0x%x "), dwControlType); - - switch (dwControlType & MIXERCONTROL_CT_CLASS_MASK) - { - case MIXERCONTROL_CT_CLASS_CUSTOM: - StringCchCat(buf, 128, TEXT("(CT_CLASS_CUSTOM)")); - break; - case MIXERCONTROL_CT_CLASS_METER: - StringCchCat(buf, 128, TEXT("(CT_CLASS_METER)")); - break; - case MIXERCONTROL_CT_CLASS_SWITCH: - StringCchCat(buf, 128, TEXT("(CT_CLASS_SWITCH)")); - break; - case MIXERCONTROL_CT_CLASS_NUMBER: - StringCchCat(buf, 128, TEXT("(CT_CLASS_NUMBER)")); - break; - case MIXERCONTROL_CT_CLASS_SLIDER: - StringCchCat(buf, 128, TEXT("(CT_CLASS_SLIDER)")); - break; - case MIXERCONTROL_CT_CLASS_FADER: - StringCchCat(buf, 128, TEXT("(CT_CLASS_FADER)")); - break; - case MIXERCONTROL_CT_CLASS_TIME: - StringCchCat(buf, 128, TEXT("(CT_CLASS_TIME)")); - break; - case MIXERCONTROL_CT_CLASS_LIST: - StringCchCat(buf, 128, TEXT("(CT_CLASS_LIST)")); - break; - default: - StringCchCat(buf, 128, TEXT("(INVALID)")); - break; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); - - // Control type (for each class) - // - StringCchPrintf(buf, 128, TEXT("control type : 0x%x "), dwControlType); - - switch (dwControlType) - { - case MIXERCONTROL_CONTROLTYPE_CUSTOM: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_CUSTOM)")); - break; - case MIXERCONTROL_CONTROLTYPE_BOOLEANMETER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BOOLEANMETER)")); - break; - case MIXERCONTROL_CONTROLTYPE_SIGNEDMETER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SIGNEDMETER)")); - break; - case MIXERCONTROL_CONTROLTYPE_PEAKMETER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PEAKMETER)")); - break; - case MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_UNSIGNEDMETER)")); - break; - case MIXERCONTROL_CONTROLTYPE_BOOLEAN: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BOOLEAN)")); - break; - case MIXERCONTROL_CONTROLTYPE_ONOFF: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_ONOFF)")); - break; - case MIXERCONTROL_CONTROLTYPE_MUTE: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MUTE)")); - break; - case MIXERCONTROL_CONTROLTYPE_MONO: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MONO)")); - break; - case MIXERCONTROL_CONTROLTYPE_LOUDNESS: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_LOUDNESS)")); - break; - case MIXERCONTROL_CONTROLTYPE_STEREOENH: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_STEREOENH)")); - break; - case MIXERCONTROL_CONTROLTYPE_BASS_BOOST: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BASS_BOOST)")); - break; - case MIXERCONTROL_CONTROLTYPE_BUTTON: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BUTTON)")); - break; - case MIXERCONTROL_CONTROLTYPE_DECIBELS: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_DECIBELS)")); - break; - case MIXERCONTROL_CONTROLTYPE_SIGNED: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SIGNED)")); - break; - case MIXERCONTROL_CONTROLTYPE_UNSIGNED: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_UNSIGNED)")); - break; - case MIXERCONTROL_CONTROLTYPE_PERCENT: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PERCENT)")); - break; - case MIXERCONTROL_CONTROLTYPE_SLIDER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SLIDER)")); - break; - case MIXERCONTROL_CONTROLTYPE_PAN: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_PAN)")); - break; - case MIXERCONTROL_CONTROLTYPE_QSOUNDPAN: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_QSOUNDPAN)")); - break; - case MIXERCONTROL_CONTROLTYPE_FADER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_FADER)")); - break; - case MIXERCONTROL_CONTROLTYPE_VOLUME: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_VOLUME)")); - break; - case MIXERCONTROL_CONTROLTYPE_BASS: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_BASS)")); - break; - case MIXERCONTROL_CONTROLTYPE_TREBLE: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_TREBLE)")); - break; - case MIXERCONTROL_CONTROLTYPE_EQUALIZER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_EQUALIZER)")); - break; - case MIXERCONTROL_CONTROLTYPE_SINGLESELECT: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_SINGLESELECT)")); - break; - case MIXERCONTROL_CONTROLTYPE_MUX: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MUX)")); - break; - case MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MULTIPLESELECT)")); - break; - case MIXERCONTROL_CONTROLTYPE_MIXER: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MIXER)")); - break; - case MIXERCONTROL_CONTROLTYPE_MICROTIME: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MICROTIME)")); - break; - case MIXERCONTROL_CONTROLTYPE_MILLITIME: - StringCchCat(buf, 128, TEXT("(CONTROLTYPE_MILLITIME)")); - break; - default: - StringCchCat(buf, 128, TEXT("(INVALID)")); - break; - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// TraceControlStatusAndSupportFlags -// -// fdwControl -// -// Status and support flags for the audio line control. The following values -// are defined: -// -// MIXERCONTROL_CONTROLF_DISABLED -// -// The control is disabled, perhaps due to other settings for the mixer hardware, -// and cannot be used. An application can read current settings from a -// disabled control, but it cannot apply settings. -// -// MIXERCONTROL_CONTROLF_MULTIPLE -// -// The control has two or more settings per channel. An equalizer, for example, -// requires this flag because each frequency band can be set to a different value. -// An equalizer that affects both channels of a stereo line in a uniform fashion -// will also specify the MIXERCONTROL_CONTROLF_UNIFORM flag. -// -// MIXERCONTROL_CONTROLF_UNIFORM -// -// The control acts on all channels of a multichannel line in a uniform fashion. -// For example, a control that mutes both channels of a stereo line would set -// this flag. Most MIXERCONTROL_CONTROLTYPE_MUX and -// MIXERCONTROL_CONTROLTYPE_MIXER controls also specify the -// MIXERCONTROL_CONTROLF_UNIFORM flag. -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceControlStatusAndSupportFlags(DWORD fdwControl) const -{ - TCHAR buf[128]; - - StringCchPrintf(buf, 128, TEXT("control support flags : 0x%x "), fdwControl); - - if (fdwControl & MIXERCONTROL_CONTROLF_DISABLED) - { - // The control is disabled, perhaps due to other settings for the mixer hardware, - // and cannot be used. An application can read current settings from a disabled - // control, but it cannot apply settings. - StringCchCat(buf, 128, TEXT("(CONTROLF_DISABLED)")); - } - - if (fdwControl & MIXERCONTROL_CONTROLF_MULTIPLE) - { - // The control has two or more settings per channel. An equalizer, for example, - // requires this flag because each frequency band can be set to a different - // value. An equalizer that affects both channels of a stereo line in a - // uniform fashion will also specify the MIXERCONTROL_CONTROLF_UNIFORM flag. - StringCchCat(buf, 128, TEXT("(CONTROLF_MULTIPLE)")); - } - - if (fdwControl & MIXERCONTROL_CONTROLF_UNIFORM) - { - // The control acts on all channels of a multichannel line in a uniform - // fashion. For example, a control that mutes both channels of a stereo - // line would set this flag. Most MIXERCONTROL_CONTROLTYPE_MUX and - // MIXERCONTROL_CONTROLTYPE_MIXER controls also specify the - // MIXERCONTROL_CONTROLF_UNIFORM flag. - StringCchCat(buf, 128, TEXT("(CONTROLF_UNIFORM)")); - } - - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// ClearSpeakerState I (II) -// ---------------------------------------------------------------------------- - -void AudioMixerManager::ClearSpeakerState(UINT idx) -{ - _speakerState[idx].dwLineID = 0L; - _speakerState[idx].dwVolumeControlID = 0L; - _speakerState[idx].dwMuteControlID = 0L; - _speakerState[idx].speakerIsValid = false; - _speakerState[idx].muteControlIsValid = false; - _speakerState[idx].volumeControlIsValid = false; -} - -// ---------------------------------------------------------------------------- -// ClearSpeakerState II (II) -// ---------------------------------------------------------------------------- - -void AudioMixerManager::ClearSpeakerState() -{ - for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++) - { - ClearSpeakerState(i); - } -} - -// ---------------------------------------------------------------------------- -// SpeakerIsValid -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::SpeakerIsValid(UINT idx) const -{ - return (_speakerState[idx].speakerIsValid); -} - -// ---------------------------------------------------------------------------- -// ValidSpeakers -// -// Counts number of valid speaker destinations for all mixer devices. -// ---------------------------------------------------------------------------- - -UINT AudioMixerManager::ValidSpeakers() const -{ - UINT nSpeakers(0); - for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++) - { - if (SpeakerIsValid(i)) - nSpeakers++; - } - return nSpeakers; -} - -// ---------------------------------------------------------------------------- -// ClearMicrophoneState I (II) -// ---------------------------------------------------------------------------- - -void AudioMixerManager::ClearMicrophoneState(UINT idx) -{ - _microphoneState[idx].dwLineID = 0L; - _microphoneState[idx].dwVolumeControlID = 0L; - _microphoneState[idx].dwMuteControlID = 0L; - _microphoneState[idx].dwOnOffControlID = 0L; - _microphoneState[idx].microphoneIsValid = false; - _microphoneState[idx].muteControlIsValid = false; - _microphoneState[idx].volumeControlIsValid = false; - _microphoneState[idx].onOffControlIsValid = false; -} - -// ---------------------------------------------------------------------------- -// ClearMicrophoneState II (II) -// ---------------------------------------------------------------------------- - -void AudioMixerManager::ClearMicrophoneState() -{ - for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++) - { - ClearMicrophoneState(i); - } -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsValid -// ---------------------------------------------------------------------------- - -bool AudioMixerManager::MicrophoneIsValid(UINT idx) const -{ - return (_microphoneState[idx].microphoneIsValid); - -} - -// ---------------------------------------------------------------------------- -// ValidMicrophones -// -// Counts number of valid speaker destinations for all mixer devices. -// To be valid, a speaker destination line must exist. -// ---------------------------------------------------------------------------- - -UINT AudioMixerManager::ValidMicrophones() const -{ - UINT nMicrophones(0); - for (int i = 0; i < MAX_NUMBER_MIXER_DEVICES; i++) - { - if (MicrophoneIsValid(i)) - nMicrophones++; - } - return nMicrophones; -} - -// ---------------------------------------------------------------------------- -// TraceWaveInError -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceWaveInError(MMRESULT error) const -{ - TCHAR buf[MAXERRORLENGTH]; - TCHAR msg[MAXERRORLENGTH]; - - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - waveInGetErrorText(error, msg, MAXERRORLENGTH); - StringCchCat(buf, MAXERRORLENGTH, msg); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// TraceWaveOutError -// ---------------------------------------------------------------------------- - -void AudioMixerManager::TraceWaveOutError(MMRESULT error) const -{ - TCHAR buf[MAXERRORLENGTH]; - TCHAR msg[MAXERRORLENGTH]; - - StringCchPrintf(buf, MAXERRORLENGTH, TEXT("Error details: ")); - waveOutGetErrorText(error, msg, MAXERRORLENGTH); - StringCchCat(buf, MAXERRORLENGTH, msg); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", WideToUTF8(buf)); -} - -// ---------------------------------------------------------------------------- -// WideToUTF8 -// ---------------------------------------------------------------------------- - -char* AudioMixerManager::WideToUTF8(const TCHAR* src) const { -#ifdef UNICODE - const size_t kStrLen = sizeof(_str); - memset(_str, 0, kStrLen); - // Get required size (in bytes) to be able to complete the conversion. - int required_size = WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, 0, 0, 0); - if (required_size <= kStrLen) - { - // Process the entire input string, including the terminating null char. - if (WideCharToMultiByte(CP_UTF8, 0, src, -1, _str, kStrLen, 0, 0) == 0) - memset(_str, 0, kStrLen); - } - return _str; -#else - return const_cast(src); -#endif -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/Windows/audio_mixer_manager.h b/modules/audio_device/main/source/Windows/audio_mixer_manager.h deleted file mode 100644 index 7e9467347..000000000 --- a/modules/audio_device/main/source/Windows/audio_mixer_manager.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_H - -#include "typedefs.h" -#include "audio_device.h" -#include "critical_section_wrapper.h" -#include -#include - -namespace webrtc { - -class AudioMixerManager -{ -public: - enum { MAX_NUMBER_MIXER_DEVICES = 40 }; - enum { MAX_NUMBER_OF_LINE_CONTROLS = 20 }; - enum { MAX_NUMBER_OF_MULTIPLE_ITEMS = 20 }; - struct SpeakerLineInfo - { - DWORD dwLineID; - bool speakerIsValid; - DWORD dwVolumeControlID; - bool volumeControlIsValid; - DWORD dwMuteControlID; - bool muteControlIsValid; - }; - struct MicrophoneLineInfo - { - DWORD dwLineID; - bool microphoneIsValid; - DWORD dwVolumeControlID; - bool volumeControlIsValid; - DWORD dwMuteControlID; - bool muteControlIsValid; - DWORD dwOnOffControlID; - bool onOffControlIsValid; - }; -public: - WebRtc_Word32 EnumerateAll(); - WebRtc_Word32 EnumerateSpeakers(); - WebRtc_Word32 EnumerateMicrophones(); - WebRtc_Word32 OpenSpeaker(AudioDeviceModule::WindowsDeviceType device); - WebRtc_Word32 OpenSpeaker(WebRtc_UWord16 index); - WebRtc_Word32 OpenMicrophone(AudioDeviceModule::WindowsDeviceType device); - WebRtc_Word32 OpenMicrophone(WebRtc_UWord16 index); - WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available); - WebRtc_Word32 SpeakerMuteIsAvailable(bool& available); - WebRtc_Word32 SetSpeakerMute(bool enable); - WebRtc_Word32 SpeakerMute(bool& enabled) const; - WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneMute(bool enable); - WebRtc_Word32 MicrophoneMute(bool& enabled) const; - WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneBoost(bool enable); - WebRtc_Word32 MicrophoneBoost(bool& enabled) const; - WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available); - WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const; - WebRtc_Word32 MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const; - WebRtc_Word32 MinMicrophoneVolume(WebRtc_UWord32& minVolume) const; - WebRtc_Word32 MicrophoneVolumeStepSize(WebRtc_UWord16& stepSize) const; - WebRtc_Word32 Close(); - WebRtc_Word32 CloseSpeaker(); - WebRtc_Word32 CloseMicrophone(); - bool SpeakerIsInitialized() const; - bool MicrophoneIsInitialized() const; - UINT Devices() const; - -private: - UINT DestinationLines(UINT mixId) const; - UINT SourceLines(UINT mixId, DWORD destId) const; - bool GetCapabilities(UINT mixId, MIXERCAPS& caps, bool trace = false) const; - bool GetDestinationLineInfo(UINT mixId, DWORD destId, MIXERLINE& line, bool trace = false) const; - bool GetSourceLineInfo(UINT mixId, DWORD destId, DWORD srcId, MIXERLINE& line, bool trace = false) const; - - bool GetAllLineControls(UINT mixId, const MIXERLINE& line, MIXERCONTROL* controlArray, bool trace = false) const; - bool GetLineControl(UINT mixId, DWORD dwControlID, MIXERCONTROL& control) const; - bool GetControlDetails(UINT mixId, MIXERCONTROL& controlArray, bool trace = false) const; - bool GetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD& dwValue) const; - bool SetUnsignedControlValue(UINT mixId, DWORD dwControlID, DWORD dwValue) const; - bool SetBooleanControlValue(UINT mixId, DWORD dwControlID, bool value) const; - bool GetBooleanControlValue(UINT mixId, DWORD dwControlID, bool& value) const; - bool GetSelectedMuxSource(UINT mixId, DWORD dwControlID, DWORD cMultipleItems, UINT& index) const; - -private: - void ClearSpeakerState(); - void ClearSpeakerState(UINT idx); - void ClearMicrophoneState(); - void ClearMicrophoneState(UINT idx); - bool SpeakerIsValid(UINT idx) const; - UINT ValidSpeakers() const; - bool MicrophoneIsValid(UINT idx) const; - UINT ValidMicrophones() const; - - void TraceStatusAndSupportFlags(DWORD fdwLine) const; - void TraceTargetType(DWORD dwType) const; - void TraceComponentType(DWORD dwComponentType) const; - void TraceControlType(DWORD dwControlType) const; - void TraceControlStatusAndSupportFlags(DWORD fdwControl) const; - void TraceWaveInError(MMRESULT error) const; - void TraceWaveOutError(MMRESULT error) const; - // Converts from wide-char to UTF-8 if UNICODE is defined. - // Does nothing if UNICODE is undefined. - char* WideToUTF8(const TCHAR* src) const; - -public: - AudioMixerManager(const WebRtc_Word32 id); - ~AudioMixerManager(); - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _id; - HMIXER _outputMixerHandle; - UINT _outputMixerID; - HMIXER _inputMixerHandle; - UINT _inputMixerID; - SpeakerLineInfo _speakerState[MAX_NUMBER_MIXER_DEVICES]; - MicrophoneLineInfo _microphoneState[MAX_NUMBER_MIXER_DEVICES]; - mutable char _str[MAXERRORLENGTH]; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_MIXER_MANAGER_H diff --git a/modules/audio_device/main/source/audio_device.gyp b/modules/audio_device/main/source/audio_device.gyp deleted file mode 100644 index 1570b425b..000000000 --- a/modules/audio_device/main/source/audio_device.gyp +++ /dev/null @@ -1,203 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'audio_device', - 'type': '<(library)', - 'dependencies': [ - '../../../../common_audio/resampler/main/source/resampler.gyp:resampler', - '../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '.', - '../../../interface', - '../interface', - 'Dummy', # Dummy audio device - 'Linux', # Dummy audio device uses linux utility (empty) - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../../../../', - '../../../interface', - '../interface', - ], - }, - # TODO(xians): Rename files to e.g. *_linux.{ext}, remove sources in conditions section - 'sources': [ - '../interface/audio_device.h', - '../interface/audio_device_defines.h', - 'audio_device_buffer.cc', - 'audio_device_buffer.h', - 'audio_device_generic.cc', - 'audio_device_generic.h', - 'audio_device_utility.cc', - 'audio_device_utility.h', - 'audio_device_impl.cc', - 'audio_device_impl.h', - 'audio_device_config.h', - 'Dummy/audio_device_dummy.cc', - 'Dummy/audio_device_dummy.h', - 'Linux/alsasymboltable.cc', - 'Linux/alsasymboltable.h', - 'Linux/audio_device_linux_alsa.cc', - 'Linux/audio_device_linux_alsa.h', - 'Linux/audio_device_linux_pulse.cc', - 'Linux/audio_device_linux_pulse.h', - 'Linux/audio_device_utility_linux.cc', - 'Linux/audio_device_utility_linux.h', - 'Linux/audio_mixer_manager_linux_alsa.cc', - 'Linux/audio_mixer_manager_linux_alsa.h', - 'Linux/audio_mixer_manager_linux_pulse.cc', - 'Linux/audio_mixer_manager_linux_pulse.h', - 'Linux/latebindingsymboltable.cc', - 'Linux/latebindingsymboltable.h', - 'Linux/pulseaudiosymboltable.cc', - 'Linux/pulseaudiosymboltable.h', - 'Mac/audio_device_mac.cc', - 'Mac/audio_device_mac.h', - 'Mac/audio_device_utility_mac.cc', - 'Mac/audio_device_utility_mac.h', - 'Mac/audio_mixer_manager_mac.cc', - 'Mac/audio_mixer_manager_mac.h', - 'Mac/portaudio/pa_memorybarrier.h', - 'Mac/portaudio/pa_ringbuffer.c', - 'Mac/portaudio/pa_ringbuffer.h', - 'Windows/audio_device_utility_windows.cc', - 'Windows/audio_device_utility_windows.h', - 'Windows/audio_device_windows_core.cc', - 'Windows/audio_device_windows_core.h', - 'Windows/audio_device_windows_wave.cc', - 'Windows/audio_device_windows_wave.h', - 'Windows/audio_mixer_manager.cc', - 'Windows/audio_mixer_manager.h', - ], - 'conditions': [ - ['OS!="linux"', { - 'sources!': [ - 'Linux/alsasymboltable.cc', - 'Linux/alsasymboltable.h', - 'Linux/audio_device_linux_alsa.cc', - 'Linux/audio_device_linux_alsa.h', - 'Linux/audio_device_linux_pulse.cc', - 'Linux/audio_device_linux_pulse.h', - 'Linux/audio_mixer_manager_linux_alsa.cc', - 'Linux/audio_mixer_manager_linux_alsa.h', - 'Linux/audio_mixer_manager_linux_pulse.cc', - 'Linux/audio_mixer_manager_linux_pulse.h', - 'Linux/latebindingsymboltable.cc', - 'Linux/latebindingsymboltable.h', - 'Linux/pulseaudiosymboltable.cc', - 'Linux/pulseaudiosymboltable.h', - # Don't remove these, needed for dummy device - # 'Linux/audio_device_utility_linux.cc', - # 'Linux/audio_device_utility_linux.h', - ], - }], - ['OS!="mac"', { - 'sources!': [ - 'Mac/audio_device_mac.cc', - 'Mac/audio_device_mac.h', - 'Mac/audio_device_utility_mac.cc', - 'Mac/audio_device_utility_mac.h', - 'Mac/audio_mixer_manager_mac.cc', - 'Mac/audio_mixer_manager_mac.h', - 'Mac/portaudio/pa_memorybarrier.h', - 'Mac/portaudio/pa_ringbuffer.c', - 'Mac/portaudio/pa_ringbuffer.h', - ], - }], - ['OS!="win"', { - 'sources!': [ - 'Windows/audio_device_utility_windows.cc', - 'Windows/audio_device_utility_windows.h', - 'Windows/audio_device_windows_core.cc', - 'Windows/audio_device_windows_core.h', - 'Windows/audio_device_windows_wave.cc', - 'Windows/audio_device_windows_wave.h', - 'Windows/audio_mixer_manager.cc', - 'Windows/audio_mixer_manager.h', - ], - }], - ['OS=="linux"', { - 'defines': [ - 'LINUX_PULSE', - 'LINUX_ALSA', - ], - 'include_dirs': [ - 'Linux', - ], - 'link_settings': { - 'libraries': [ - '-ldl', - '-lasound', - '-lpulse', - ], - }, - }], - ['OS=="mac"', { - 'include_dirs': [ - 'Mac', - ], - 'link_settings': { - 'libraries': [ - '$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework', - '$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework', - ], - }, - }], - ['OS=="win"', { - 'include_dirs': [ - 'Windows', - '../../../../../..', - ], - }], - ] # conditions - }, - { - 'target_name': 'audio_device_test_api', - 'type': 'executable', - 'dependencies': [ - 'audio_device', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - ], - 'sources': [ - '../test/audio_device_test_api.cc', - '../test/audio_device_test_defines.h', - ], - }, - { - 'target_name': 'audio_device_test_func', - 'type': 'executable', - 'dependencies': [ - 'audio_device', - '../../../../common_audio/resampler/main/source/resampler.gyp:resampler', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - ], - 'sources': [ - '../test/audio_device_test_func.cc', - '../test/audio_device_test_defines.h', - '../test/func_test_manager.cc', - '../test/func_test_manager.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_device/main/source/audio_device_buffer.cc b/modules/audio_device/main/source/audio_device_buffer.cc deleted file mode 100644 index 5a77ff044..000000000 --- a/modules/audio_device/main/source/audio_device_buffer.cc +++ /dev/null @@ -1,644 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "critical_section_wrapper.h" -#include "audio_device_buffer.h" -#include "audio_device_utility.h" -#include "audio_device_config.h" - -#include -#include -#include - -#include "signal_processing_library.h" - -namespace webrtc { - -// ---------------------------------------------------------------------------- -// ctor -// ---------------------------------------------------------------------------- - -AudioDeviceBuffer::AudioDeviceBuffer() : - _id(-1), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _critSectCb(*CriticalSectionWrapper::CreateCriticalSection()), - _ptrCbAudioTransport(NULL), - _recSampleRate(0), - _playSampleRate(0), - _recChannels(0), - _playChannels(0), - _recChannel(AudioDeviceModule::kChannelBoth), - _recBytesPerSample(0), - _playBytesPerSample(0), - _recSamples(0), - _playSamples(0), - _recSize(0), - _playSize(0), - _recFile(*FileWrapper::Create()), - _playFile(*FileWrapper::Create()), - _newMicLevel(0), - _currentMicLevel(0), - _playDelayMS(0), - _recDelayMS(0), - _clockDrift(0), - _measureDelay(false), // should always be 'false' (EXPERIMENTAL) - _pulseList(), - _lastPulseTime(AudioDeviceUtility::GetTimeInMS()) -{ - // valid ID will be set later by SetId, use -1 for now - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s created", __FUNCTION__); -} - -// ---------------------------------------------------------------------------- -// dtor -// ---------------------------------------------------------------------------- - -AudioDeviceBuffer::~AudioDeviceBuffer() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - - _recFile.Flush(); - _recFile.CloseFile(); - delete &_recFile; - - _playFile.Flush(); - _playFile.CloseFile(); - delete &_playFile; - - _EmptyList(); - } - - delete &_critSect; - delete &_critSectCb; -} - -// ---------------------------------------------------------------------------- -// SetId -// ---------------------------------------------------------------------------- - -void AudioDeviceBuffer::SetId(WebRtc_UWord32 id) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "AudioDeviceBuffer::SetId(id=%d)", id); - _id = id; -} - -// ---------------------------------------------------------------------------- -// RegisterAudioCallback -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::RegisterAudioCallback(AudioTransport* audioCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceBuffer::RegisterAudioCallback(AudioTransport=0x%x)", audioCallback); - - CriticalSectionScoped lock(_critSectCb); - _ptrCbAudioTransport = audioCallback; - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::InitPlayout() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_measureDelay) - { - _EmptyList(); - _lastPulseTime = AudioDeviceUtility::GetTimeInMS(); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::InitRecording() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - if (_measureDelay) - { - _EmptyList(); - _lastPulseTime = AudioDeviceUtility::GetTimeInMS(); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetRecordingSampleRate(WebRtc_UWord32 fsHz) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "AudioDeviceBuffer::SetRecordingSampleRate(fsHz=%u)", fsHz); - - CriticalSectionScoped lock(_critSect); - _recSampleRate = fsHz; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetPlayoutSampleRate(WebRtc_UWord32 fsHz) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "AudioDeviceBuffer::SetPlayoutSampleRate(fsHz=%u)", fsHz); - - CriticalSectionScoped lock(_critSect); - _playSampleRate = fsHz; - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::RecordingSampleRate() const -{ - return _recSampleRate; -} - -// ---------------------------------------------------------------------------- -// PlayoutSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::PlayoutSampleRate() const -{ - return _playSampleRate; -} - -// ---------------------------------------------------------------------------- -// SetRecordingChannels -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetRecordingChannels(WebRtc_UWord8 channels) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "AudioDeviceBuffer::SetRecordingChannels(channels=%u)", channels); - - CriticalSectionScoped lock(_critSect); - _recChannels = channels; - _recBytesPerSample = 2*channels; // 16 bits per sample in mono, 32 bits in stereo - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutChannels -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetPlayoutChannels(WebRtc_UWord8 channels) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "AudioDeviceBuffer::SetPlayoutChannels(channels=%u)", channels); - - CriticalSectionScoped lock(_critSect); - _playChannels = channels; - // 16 bits per sample in mono, 32 bits in stereo - _playBytesPerSample = 2*channels; - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordingChannel -// -// Select which channel to use while recording. -// This API requires that stereo is enabled. -// -// Note that, the nChannel parameter in RecordedDataIsAvailable will be -// set to 2 even for kChannelLeft and kChannelRight. However, nBytesPerSample -// will be 2 instead of 4 four these cases. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetRecordingChannel(const AudioDeviceModule::ChannelType channel) -{ - CriticalSectionScoped lock(_critSect); - - if (_recChannels == 1) - { - return -1; - } - - if (channel == AudioDeviceModule::kChannelBoth) - { - // two bytes per channel - _recBytesPerSample = 4; - } - else - { - // only utilize one out of two possible channels (left or right) - _recBytesPerSample = 2; - } - _recChannel = channel; - - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingChannel -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::RecordingChannel(AudioDeviceModule::ChannelType& channel) const -{ - channel = _recChannel; - return 0; -} - -// ---------------------------------------------------------------------------- -// RecordingChannels -// ---------------------------------------------------------------------------- - -WebRtc_UWord8 AudioDeviceBuffer::RecordingChannels() const -{ - return _recChannels; -} - -// ---------------------------------------------------------------------------- -// PlayoutChannels -// ---------------------------------------------------------------------------- - -WebRtc_UWord8 AudioDeviceBuffer::PlayoutChannels() const -{ - return _playChannels; -} - -// ---------------------------------------------------------------------------- -// SetCurrentMicLevel -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetCurrentMicLevel(WebRtc_UWord32 level) -{ - _currentMicLevel = level; - return 0; -} - -// ---------------------------------------------------------------------------- -// NewMicLevel -// ---------------------------------------------------------------------------- - -WebRtc_UWord32 AudioDeviceBuffer::NewMicLevel() const -{ - return _newMicLevel; -} - -// ---------------------------------------------------------------------------- -// SetVQEData -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetVQEData(WebRtc_UWord32 playDelayMS, WebRtc_UWord32 recDelayMS, WebRtc_Word32 clockDrift) -{ - if ((playDelayMS + recDelayMS) > 300) - { - WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, "too long delay (play:%i rec:%i)", playDelayMS, recDelayMS, clockDrift); - } - - _playDelayMS = playDelayMS; - _recDelayMS = recDelayMS; - _clockDrift = clockDrift; - - return 0; -} - -// ---------------------------------------------------------------------------- -// StartInputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::StartInputFileRecording(const WebRtc_Word8 fileName[kAdmMaxFileNameSize]) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _recFile.Flush(); - _recFile.CloseFile(); - - return (_recFile.OpenFile(fileName, false, false, false)); -} - -// ---------------------------------------------------------------------------- -// StopInputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::StopInputFileRecording() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _recFile.Flush(); - _recFile.CloseFile(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// StartOutputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::StartOutputFileRecording(const WebRtc_Word8 fileName[kAdmMaxFileNameSize]) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _playFile.Flush(); - _playFile.CloseFile(); - - return (_playFile.OpenFile(fileName, false, false, false)); -} - -// ---------------------------------------------------------------------------- -// StopOutputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::StopOutputFileRecording() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - CriticalSectionScoped lock(_critSect); - - _playFile.Flush(); - _playFile.CloseFile(); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetRecordedBuffer -// -// Store recorded audio buffer in local memory ready for the actual -// "delivery" using a callback. -// -// This method can also parse out left or right channel from a stereo -// input signal, i.e., emulate mono. -// -// Examples: -// -// 16-bit,48kHz mono, 10ms => nSamples=480 => _recSize=2*480=960 bytes -// 16-bit,48kHz stereo,10ms => nSamples=480 => _recSize=4*960=1920 bytes -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::SetRecordedBuffer(const WebRtc_Word8* audioBuffer, WebRtc_UWord32 nSamples) -{ - CriticalSectionScoped lock(_critSect); - - if (_recBytesPerSample == 0) - { - assert(false); - return -1; - } - - _recSamples = nSamples; - _recSize = _recBytesPerSample*nSamples; // {2,4}*nSamples - if (_recSize > 1920) - { - assert(false); - return -1; - } - - if (nSamples != _recSamples) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "invalid number of recorded samples (%d)", nSamples); - return -1; - } - - if (_recChannel == AudioDeviceModule::kChannelBoth) - { - // (default) copy the complete input buffer to the local buffer - memcpy(&_recBuffer[0], audioBuffer, _recSize); - } - else - { - WebRtc_Word16* ptr16In = (WebRtc_Word16*)audioBuffer; - WebRtc_Word16* ptr16Out = (WebRtc_Word16*)&_recBuffer[0]; - - if (AudioDeviceModule::kChannelRight == _recChannel) - { - ptr16In++; - } - - // exctract left or right channel from input buffer to the local buffer - for (WebRtc_UWord32 i = 0; i < _recSamples; i++) - { - *ptr16Out = *ptr16In; - ptr16Out++; - ptr16In++; - ptr16In++; - } - } - - if (_recFile.Open()) - { - // write to binary file in mono or stereo (interleaved) - _recFile.Write(&_recBuffer[0], _recSize); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// DeliverRecordedData -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::DeliverRecordedData() -{ - CriticalSectionScoped lock(_critSectCb); - - // Ensure that user has initialized all essential members - if ((_recSampleRate == 0) || - (_recSamples == 0) || - (_recBytesPerSample == 0) || - (_recChannels == 0)) - { - assert(false); - return -1; - } - - if (_ptrCbAudioTransport == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to deliver recorded data (AudioTransport does not exist)"); - return 0; - } - - WebRtc_Word32 res(0); - WebRtc_UWord32 newMicLevel(0); - WebRtc_UWord32 totalDelayMS = _playDelayMS +_recDelayMS; - - if (_measureDelay) - { - CriticalSectionScoped lock(_critSect); - - memset(&_recBuffer[0], 0, _recSize); - WebRtc_UWord32 time = AudioDeviceUtility::GetTimeInMS(); - if (time - _lastPulseTime > 500) - { - _pulseList.PushBack(time); - _lastPulseTime = time; - - WebRtc_Word16* ptr16 = (WebRtc_Word16*)&_recBuffer[0]; - *ptr16 = 30000; - } - } - - res = _ptrCbAudioTransport->RecordedDataIsAvailable(&_recBuffer[0], - _recSamples, - _recBytesPerSample, - _recChannels, - _recSampleRate, - totalDelayMS, - _clockDrift, - _currentMicLevel, - newMicLevel); - if (res != -1) - { - _newMicLevel = newMicLevel; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// RequestPlayoutData -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::RequestPlayoutData(WebRtc_UWord32 nSamples) -{ - { - CriticalSectionScoped lock(_critSect); - - // Ensure that user has initialized all essential members - if ((_playBytesPerSample == 0) || - (_playChannels == 0) || - (_playSampleRate == 0)) - { - assert(false); - return -1; - } - - _playSamples = nSamples; - _playSize = _playBytesPerSample * nSamples; // {2,4}*nSamples - if (_playSize > 1920) - { - assert(false); - return -1; - } - - if (nSamples != _playSamples) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "invalid number of samples to be played out (%d)", nSamples); - return -1; - } - } - - WebRtc_UWord32 nSamplesOut(0); - - CriticalSectionScoped lock(_critSectCb); - - if (_ptrCbAudioTransport == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "failed to feed data to playout (AudioTransport does not exist)"); - return 0; - } - - if (_ptrCbAudioTransport) - { - WebRtc_UWord32 res(0); - - res = _ptrCbAudioTransport->NeedMorePlayData(_playSamples, - _playBytesPerSample, - _playChannels, - _playSampleRate, - &_playBuffer[0], - nSamplesOut); - if (res != 0) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "NeedMorePlayData() failed"); - } - - // --- Experimental delay-measurement implementation - // *** not be used in released code *** - - if (_measureDelay) - { - CriticalSectionScoped lock(_critSect); - - WebRtc_Word16 maxAbs = WebRtcSpl_MaxAbsValueW16((const WebRtc_Word16*)&_playBuffer[0], (WebRtc_Word16)nSamplesOut*_playChannels); - if (maxAbs > 1000) - { - WebRtc_UWord32 nowTime = AudioDeviceUtility::GetTimeInMS(); - - if (!_pulseList.Empty()) - { - ListItem* item = _pulseList.First(); - if (item) - { - WebRtc_Word16 maxIndex = WebRtcSpl_MaxAbsIndexW16((const WebRtc_Word16*)&_playBuffer[0], (WebRtc_Word16)nSamplesOut*_playChannels); - WebRtc_UWord32 pulseTime = item->GetUnsignedItem(); - WebRtc_UWord32 diff = nowTime - pulseTime + (10*maxIndex)/(nSamplesOut*_playChannels); - // DEBUG_PRINT("diff=%d", diff); - } - _pulseList.PopFront(); - } - } - } - } - - return nSamplesOut; -} - -// ---------------------------------------------------------------------------- -// GetPlayoutData -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceBuffer::GetPlayoutData(WebRtc_Word8* audioBuffer) -{ - CriticalSectionScoped lock(_critSect); - - memcpy(audioBuffer, &_playBuffer[0], _playSize); - - if (_playFile.Open()) - { - // write to binary file in mono or stereo (interleaved) - _playFile.Write(&_playBuffer[0], _playSize); - } - - return _playSamples; -} - -// ---------------------------------------------------------------------------- -// _EmptyList -// ---------------------------------------------------------------------------- - -void AudioDeviceBuffer::_EmptyList() -{ - while (!_pulseList.Empty()) - { - int n = _pulseList.GetSize(); - ListItem* item = _pulseList.First(); - if (item) - { - // WebRtc_UWord32 ts = item->GetUnsignedItem(); - } - _pulseList.PopFront(); - } -} - -} // namespace webrtc diff --git a/modules/audio_device/main/source/audio_device_buffer.h b/modules/audio_device/main/source/audio_device_buffer.h deleted file mode 100644 index c7d7e5d38..000000000 --- a/modules/audio_device/main/source/audio_device_buffer.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_BUFFER_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_BUFFER_H - -#include "typedefs.h" -#include "resampler.h" -#include "file_wrapper.h" -#include "audio_device.h" -#include "list_wrapper.h" - -namespace webrtc { -class CriticalSectionWrapper; - -const WebRtc_UWord32 kPulsePeriodMs = 1000; - -class AudioDeviceObserver; -class MediaFile; - -class AudioDeviceBuffer -{ -public: - void SetId(WebRtc_UWord32 id); - WebRtc_Word32 RegisterAudioCallback(AudioTransport* audioCallback); - - WebRtc_Word32 InitPlayout(); - WebRtc_Word32 InitRecording(); - - WebRtc_Word32 SetRecordingSampleRate(WebRtc_UWord32 fsHz); - WebRtc_Word32 SetPlayoutSampleRate(WebRtc_UWord32 fsHz); - WebRtc_Word32 RecordingSampleRate() const; - WebRtc_Word32 PlayoutSampleRate() const; - - WebRtc_Word32 SetRecordingChannels(WebRtc_UWord8 channels); - WebRtc_Word32 SetPlayoutChannels(WebRtc_UWord8 channels); - WebRtc_UWord8 RecordingChannels() const; - WebRtc_UWord8 PlayoutChannels() const; - WebRtc_Word32 SetRecordingChannel(const AudioDeviceModule::ChannelType channel); - WebRtc_Word32 RecordingChannel(AudioDeviceModule::ChannelType& channel) const; - - WebRtc_Word32 SetRecordedBuffer(const WebRtc_Word8* audioBuffer, WebRtc_UWord32 nSamples); - WebRtc_Word32 SetCurrentMicLevel(WebRtc_UWord32 level); - WebRtc_Word32 SetVQEData(WebRtc_UWord32 playDelayMS, WebRtc_UWord32 recDelayMS, WebRtc_Word32 clockDrift); - WebRtc_Word32 DeliverRecordedData(); - WebRtc_UWord32 NewMicLevel() const; - - WebRtc_Word32 RequestPlayoutData(WebRtc_UWord32 nSamples); - WebRtc_Word32 GetPlayoutData(WebRtc_Word8* audioBuffer); - - WebRtc_Word32 StartInputFileRecording(const WebRtc_Word8 fileName[kAdmMaxFileNameSize]); - WebRtc_Word32 StopInputFileRecording(); - WebRtc_Word32 StartOutputFileRecording(const WebRtc_Word8 fileName[kAdmMaxFileNameSize]); - WebRtc_Word32 StopOutputFileRecording(); - - AudioDeviceBuffer(); - ~AudioDeviceBuffer(); - -private: - void _EmptyList(); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _critSect; - CriticalSectionWrapper& _critSectCb; - - AudioTransport* _ptrCbAudioTransport; - - WebRtc_UWord32 _recSampleRate; - WebRtc_UWord32 _playSampleRate; - - WebRtc_UWord8 _recChannels; - WebRtc_UWord8 _playChannels; - - // selected recording channel (left/right/both) - AudioDeviceModule::ChannelType _recChannel; - - // 2 or 4 depending on mono or stereo - WebRtc_UWord8 _recBytesPerSample; - WebRtc_UWord8 _playBytesPerSample; - - // 10ms in stereo @ 48kHz - WebRtc_Word8 _recBuffer[1920]; - - // one sample <=> 2 or 4 bytes - WebRtc_UWord32 _recSamples; - WebRtc_UWord32 _recSize; // in bytes - - // 10ms in stereo @ 48kHz - WebRtc_Word8 _playBuffer[1920]; - - // one sample <=> 2 or 4 bytes - WebRtc_UWord32 _playSamples; - WebRtc_UWord32 _playSize; // in bytes - - FileWrapper& _recFile; - FileWrapper& _playFile; - - WebRtc_UWord32 _currentMicLevel; - WebRtc_UWord32 _newMicLevel; - - WebRtc_UWord32 _playDelayMS; - WebRtc_UWord32 _recDelayMS; - - WebRtc_Word32 _clockDrift; - - bool _measureDelay; - ListWrapper _pulseList; - WebRtc_UWord32 _lastPulseTime; -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_BUFFER_H diff --git a/modules/audio_device/main/source/audio_device_config.h b/modules/audio_device/main/source/audio_device_config.h deleted file mode 100644 index 23b9d5516..000000000 --- a/modules/audio_device/main/source/audio_device_config.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_CONFIG_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_CONFIG_H - -// Enumerators -// -enum { kAdmMaxIdleTimeProcess = 1000 }; -enum { GET_MIC_VOLUME_INTERVAL_MS = 1000 }; - -// Platform specifics -// -#if defined(_WIN32) -#if (_MSC_VER >= 1400) -// Windows Core Audio is the default audio layer in Windows. -// Only supported for VS 2005 and higher. -#define WEBRTC_WINDOWS_CORE_AUDIO_BUILD -#endif -#endif - -#if (defined(_DEBUG) && defined(_WIN32) && (_MSC_VER >= 1400)) -#include -#include -#include -#define DEBUG_PRINT(...) \ -{ \ - TCHAR msg[256]; \ - StringCchPrintf(msg, 256, __VA_ARGS__); \ - OutputDebugString(msg); \ -} -#else -#define DEBUG_PRINT(exp) ((void)0) -#endif - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_CONFIG_H - diff --git a/modules/audio_device/main/source/audio_device_generic.cc b/modules/audio_device/main/source/audio_device_generic.cc deleted file mode 100644 index 0e5fb25b0..000000000 --- a/modules/audio_device/main/source/audio_device_generic.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_generic.h" -#include "trace.h" - -namespace webrtc { - -WebRtc_Word32 AudioDeviceGeneric::SetRecordingSampleRate( - const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Set recording sample rate not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceGeneric::SetPlayoutSampleRate( - const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Set playout sample rate not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceGeneric::SetLoudspeakerStatus(bool enable) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Set loudspeaker status not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceGeneric::GetLoudspeakerStatus(bool& enable) const -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Get loudspeaker status not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceGeneric::ResetAudioDevice() -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Reset audio device not supported on this platform"); - return -1; -} - -WebRtc_Word32 AudioDeviceGeneric::SoundDeviceControl(unsigned int par1, - unsigned int par2, unsigned int par3, unsigned int par4) -{ - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, - "Reset audio device not supported on this platform"); - return -1; -} - -} // namespace webrtc - diff --git a/modules/audio_device/main/source/audio_device_generic.h b/modules/audio_device/main/source/audio_device_generic.h deleted file mode 100644 index 763a393c2..000000000 --- a/modules/audio_device/main/source/audio_device_generic.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_GENERIC_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_GENERIC_H - -#include "audio_device.h" -#include "audio_device_buffer.h" - -namespace webrtc { - -class AudioDeviceGeneric -{ - public: - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer( - AudioDeviceModule::AudioLayer& audioLayer) const = 0; - - // Main initializaton and termination - virtual WebRtc_Word32 Init() = 0; - virtual WebRtc_Word32 Terminate() = 0; - virtual bool Initialized() const = 0; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices() = 0; - virtual WebRtc_Word16 RecordingDevices() = 0; - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) = 0; - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]) = 0; - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index) = 0; - virtual WebRtc_Word32 SetPlayoutDevice( - AudioDeviceModule::WindowsDeviceType device) = 0; - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index) = 0; - virtual WebRtc_Word32 SetRecordingDevice( - AudioDeviceModule::WindowsDeviceType device) = 0; - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 InitPlayout() = 0; - virtual bool PlayoutIsInitialized() const = 0; - virtual WebRtc_Word32 RecordingIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 InitRecording() = 0; - virtual bool RecordingIsInitialized() const = 0; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout() = 0; - virtual WebRtc_Word32 StopPlayout() = 0; - virtual bool Playing() const = 0; - virtual WebRtc_Word32 StartRecording() = 0; - virtual WebRtc_Word32 StopRecording() = 0; - virtual bool Recording() const = 0; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable) = 0; - virtual bool AGC() const = 0; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight) = 0; - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16& volumeLeft, - WebRtc_UWord16& volumeRight) const = 0; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 InitSpeaker() = 0; - virtual bool SpeakerIsInitialized() const = 0; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 InitMicrophone() = 0; - virtual bool MicrophoneIsInitialized() const = 0; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume) = 0; - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32& volume) const = 0; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const = 0; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32& minVolume) const = 0; - virtual WebRtc_Word32 SpeakerVolumeStepSize( - WebRtc_UWord16& stepSize) const = 0; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume) = 0; - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32& volume) const = 0; - virtual WebRtc_Word32 MaxMicrophoneVolume( - WebRtc_UWord32& maxVolume) const = 0; - virtual WebRtc_Word32 MinMicrophoneVolume( - WebRtc_UWord32& minVolume) const = 0; - virtual WebRtc_Word32 MicrophoneVolumeStepSize( - WebRtc_UWord16& stepSize) const = 0; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetSpeakerMute(bool enable) = 0; - virtual WebRtc_Word32 SpeakerMute(bool& enabled) const = 0; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetMicrophoneMute(bool enable) = 0; - virtual WebRtc_Word32 MicrophoneMute(bool& enabled) const = 0; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable) = 0; - virtual WebRtc_Word32 MicrophoneBoost(bool& enabled) const = 0; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetStereoPlayout(bool enable) = 0; - virtual WebRtc_Word32 StereoPlayout(bool& enabled) const = 0; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool& available) = 0; - virtual WebRtc_Word32 SetStereoRecording(bool enable) = 0; - virtual WebRtc_Word32 StereoRecording(bool& enabled) const = 0; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer( - const AudioDeviceModule::BufferType type, - WebRtc_UWord16 sizeMS = 0) = 0; - virtual WebRtc_Word32 PlayoutBuffer( - AudioDeviceModule::BufferType& type, WebRtc_UWord16& sizeMS) const = 0; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16& delayMS) const = 0; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16& delayMS) const = 0; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const = 0; - - // Native sample rate controls (samples/sec) - virtual WebRtc_Word32 SetRecordingSampleRate( - const WebRtc_UWord32 samplesPerSec); - virtual WebRtc_Word32 SetPlayoutSampleRate( - const WebRtc_UWord32 samplesPerSec); - - // Speaker audio routing (for mobile devices) - virtual WebRtc_Word32 SetLoudspeakerStatus(bool enable); - virtual WebRtc_Word32 GetLoudspeakerStatus(bool& enable) const; - - // Reset Audio Device (for mobile devices) - virtual WebRtc_Word32 ResetAudioDevice(); - - // Sound Audio Device control (for WinCE only) - virtual WebRtc_Word32 SoundDeviceControl(unsigned int par1 = 0, - unsigned int par2 = 0, - unsigned int par3 = 0, - unsigned int par4 = 0); - -public: - virtual bool PlayoutWarning() const = 0; - virtual bool PlayoutError() const = 0; - virtual bool RecordingWarning() const = 0; - virtual bool RecordingError() const = 0; - virtual void ClearPlayoutWarning() = 0; - virtual void ClearPlayoutError() = 0; - virtual void ClearRecordingWarning() = 0; - virtual void ClearRecordingError() = 0; - -public: - virtual void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) = 0; - - virtual ~AudioDeviceGeneric() {} -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_GENERIC_H - diff --git a/modules/audio_device/main/source/audio_device_impl.cc b/modules/audio_device/main/source/audio_device_impl.cc deleted file mode 100644 index a8e7dd1c5..000000000 --- a/modules/audio_device/main/source/audio_device_impl.cc +++ /dev/null @@ -1,2185 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_device_impl.h" -#include "audio_device_config.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -#if defined(_WIN32) - #include "audio_device_utility_windows.h" - #include "audio_device_windows_wave.h" - #if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - #include "audio_device_windows_core.h" - #endif -#elif defined(WEBRTC_ANDROID_NATIVE) - #include - #include "audio_device_utility_android.h" - #include "audio_device_android_native.h" -#elif defined(ANDROID) - #include - #include "audio_device_utility_android.h" - #include "audio_device_android_jni.h" -#elif defined(WEBRTC_LINUX) - #include "audio_device_utility_linux.h" - #if defined(LINUX_ALSA) - #include "audio_device_linux_alsa.h" - #endif - #if defined(LINUX_PULSE) - #include "audio_device_linux_pulse.h" - #endif -#elif defined(MAC_IPHONE) - #include "audio_device_utility_iphone.h" - #include "audio_device_iphone.h" -#elif (defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC)) - #include "audio_device_utility_mac.h" - #include "audio_device_mac.h" -#endif -#include "audio_device_dummy.h" -// audio_device_utility_linux.h is used for both linux device and dummy device -#include "audio_device_utility_linux.h" - -#include - -#define CHECK_INITIALIZED() \ -{ \ - if (!_initialized) { \ - return -1; \ - }; \ -} - -#define CHECK_INITIALIZED_BOOL() \ -{ \ - if (!_initialized) { \ - return false; \ - }; \ -} - -namespace webrtc -{ - -// ============================================================================ -// Static methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceModule::Create() -// ---------------------------------------------------------------------------- - -AudioDeviceModule* AudioDeviceModule::Create(const WebRtc_Word32 id, - const AudioLayer audioLayer) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, id, "Create(audioLayer=%d)", (int)audioLayer); - - // Create the generic (platform independent) implementation - // - AudioDeviceModuleImpl* audioDevice = static_cast (new AudioDeviceModuleImpl(id, audioLayer)); - - if (audioDevice->CheckPlatform() == -1) - { - delete audioDevice; - return NULL; - } - - // Create the platform-dependent implementation - // - if (audioDevice->CreatePlatformSpecificObjects() == -1) - { - delete audioDevice; - return NULL; - } - - // Ensure that the generic audio buffer can communicate with the platform-specific parts - // - if (audioDevice->AttachAudioBuffer() == -1) - { - delete audioDevice; - return NULL; - } - - return audioDevice; -} - -// ---------------------------------------------------------------------------- -// AudioDeviceModule::Destroy() -// ---------------------------------------------------------------------------- - -void AudioDeviceModule::Destroy(AudioDeviceModule* module) -{ - if (module) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, static_cast(module)->Id(), "Destroy()"); - delete static_cast(module); - } -} - -// ---------------------------------------------------------------------------- -// AudioDeviceModule::GetVersion() -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModule::GetVersion(WebRtc_Word8* version, WebRtc_UWord32& remainingBufferInBytes, WebRtc_UWord32& position) -{ - if (version == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, "invalid buffer pointer in argument"); - return -1; - } - WebRtc_Word8 ourVersion[] = "AudioDevice 1.1.0"; - WebRtc_UWord32 ourLength = (WebRtc_UWord32)strlen(ourVersion); - if (remainingBufferInBytes < (ourLength + 1)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, "version string requires %d bytes", (ourLength + 1)); - return -1; - } - memcpy(&version[position], ourVersion, ourLength); - version[position + ourLength] = '\0'; // null terminaion - remainingBufferInBytes -= (ourLength + 1); - position += (ourLength + 1); - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, "version: %s", version); - return 0; -} - -// ---------------------------------------------------------------------------- -// SetAndroidObjects -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModule::SetAndroidObjects(void* javaVM, void* env, void* context) -{ -#if defined(ANDROID) && !defined(WEBRTC_ANDROID_NATIVE) - return SetAndroidAudioDeviceObjects(javaVM, env, context); -#else - return -1; -#endif -} - -// ============================================================================ -// Construction & Destruction -// ============================================================================ - -// ---------------------------------------------------------------------------- -// AudioDeviceModuleImpl - ctor -// ---------------------------------------------------------------------------- - -AudioDeviceModuleImpl::AudioDeviceModuleImpl(const WebRtc_Word32 id, const AudioLayer audioLayer) : - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _critSectEventCb(*CriticalSectionWrapper::CreateCriticalSection()), - _critSectAudioCb(*CriticalSectionWrapper::CreateCriticalSection()), - _ptrCbAudioDeviceObserver(NULL), - _id(id), - _platformAudioLayer(audioLayer), - _lastProcessTime(AudioDeviceUtility::GetTimeInMS()), - _lastError(kAdmErrNone), - _platformType(kPlatformNotSupported), - _ptrAudioDeviceUtility(NULL), - _ptrAudioDevice(NULL), - _initialized(false) -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); -} - -// ---------------------------------------------------------------------------- -// CheckPlatform -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::CheckPlatform() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - // Ensure that the current platform is supported - // - PlatformType platform(kPlatformNotSupported); - -#if defined(_WIN32) - platform = kPlatformWin32; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "current platform is WIN32"); -#elif defined(WEBRTC_LINUX) - platform = kPlatformLinux; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "current platform is LINUX"); -#elif (defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC)) - platform = kPlatformMac; - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "current platform is MAC"); -#endif - - if (platform == kPlatformNotSupported) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, "current platform is not supported => this module will self destruct!"); - return -1; - } - - // Store valid output results - // - _platformType = platform; - - return 0; -} - - -// ---------------------------------------------------------------------------- -// CreatePlatformSpecificObjects -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::CreatePlatformSpecificObjects() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - AudioDeviceGeneric* ptrAudioDevice(NULL); - AudioDeviceUtility* ptrAudioDeviceUtility(NULL); - - const AudioLayer audioLayer(PlatformAudioLayer()); - - // Create the *Windows* implementation of the Audio Device - // -#if defined(_WIN32) - if ((audioLayer == kWindowsWaveAudio) -#if !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - // Wave audio is default if Core audio is not supported in this build - || (audioLayer == kPlatformDefaultAudio) -#endif - ) - { - // create *Windows Wave Audio* implementation - ptrAudioDevice = new AudioDeviceWindowsWave(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Windows Wave APIs will be utilized"); - } -#if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - if ((audioLayer == kWindowsCoreAudio) || - (audioLayer == kPlatformDefaultAudio) - ) - { - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "attempting to use the Windows Core Audio APIs..."); - - if (AudioDeviceWindowsCore::CoreAudioIsSupported()) - { - // create *Windows Core Audio* implementation - ptrAudioDevice = new AudioDeviceWindowsCore(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Windows Core Audio APIs will be utilized"); - } - else - { - // create *Windows Wave Audio* implementation - ptrAudioDevice = new AudioDeviceWindowsWave(Id()); - if (ptrAudioDevice != NULL) - { - // Core Audio was not supported => revert to Windows Wave instead - _platformAudioLayer = kWindowsWaveAudio; // modify the state set at construction - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Windows Core Audio is *not* supported => Wave APIs will be utilized instead"); - } - } - } -#endif // defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - if (ptrAudioDevice != NULL) - { - // Create the Windows implementation of the Device Utility. - // This class is independent of the selected audio layer - // for Windows. - // - ptrAudioDeviceUtility = new AudioDeviceUtilityWindows(Id()); - } -#endif // #if defined(_WIN32) - - // Create the *Android Native* implementation of the Audio Device - // -#if defined(WEBRTC_ANDROID_NATIVE) - if (audioLayer == kPlatformDefaultAudio) - { - // Create *Android Native Audio* implementation - ptrAudioDevice = new AudioDeviceAndroidNative(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, - "Android NATIVE Audio APIs will be utilized"); - } - - if (ptrAudioDevice != NULL) - { - // Create the Android implementation of the Device Utility. - ptrAudioDeviceUtility = new AudioDeviceUtilityAndroid(Id()); - } - // END #if defined(WEBRTC_ANDROID_NATIVE) - - // Create the *Android Java* implementation of the Audio Device - // -#elif defined(ANDROID) - if (audioLayer == kPlatformDefaultAudio) - { - // Create *Android JNI Audio* implementation - ptrAudioDevice = new AudioDeviceAndroidJni(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Android JNI Audio APIs will be utilized"); - } - - if (ptrAudioDevice != NULL) - { - // Create the Android implementation of the Device Utility. - ptrAudioDeviceUtility = new AudioDeviceUtilityAndroid(Id()); - } - // END #if defined(ANDROID) - - // Create the *Linux* implementation of the Audio Device - // -#elif defined(WEBRTC_LINUX) - if ((audioLayer == kLinuxPulseAudio) || (audioLayer == kPlatformDefaultAudio)) - { -#if defined(LINUX_PULSE) - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "attempting to use the Linux PulseAudio APIs..."); - - if (AudioDeviceLinuxPulse::PulseAudioIsSupported()) - { - // create *Linux PulseAudio* implementation - ptrAudioDevice = new AudioDeviceLinuxPulse(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Linux PulseAudio APIs will be utilized"); - } - else - { -#endif -#if defined(LINUX_ALSA) - // create *Linux ALSA Audio* implementation - ptrAudioDevice = new AudioDeviceLinuxALSA(Id()); - if (ptrAudioDevice != NULL) - { - // Pulse Audio was not supported => revert to ALSA instead - _platformAudioLayer = kLinuxAlsaAudio; // modify the state set at construction - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Linux PulseAudio is *not* supported => ALSA APIs will be utilized instead"); - } -#endif -#if defined(LINUX_PULSE) - } -#endif - } - else if (audioLayer == kLinuxAlsaAudio) - { -#if defined(LINUX_ALSA) - // create *Linux ALSA Audio* implementation - ptrAudioDevice = new AudioDeviceLinuxALSA(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Linux ALSA APIs will be utilized"); -#endif - } - - if (ptrAudioDevice != NULL) - { - // Create the Linux implementation of the Device Utility. - // This class is independent of the selected audio layer - // for Linux. - // - ptrAudioDeviceUtility = new AudioDeviceUtilityLinux(Id()); - } -#endif // #if defined(WEBRTC_LINUX) - - // Create the *iPhone* implementation of the Audio Device - // -#if defined(MAC_IPHONE) - if (audioLayer == kPlatformDefaultAudio) - { - // Create *iPhone Audio* implementation - ptrAudioDevice = new AudioDeviceIPhone(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "iPhone Audio APIs will be utilized"); - } - - if (ptrAudioDevice != NULL) - { - // Create the Mac implementation of the Device Utility. - ptrAudioDeviceUtility = new AudioDeviceUtilityIPhone(Id()); - } - // END #if defined(MAC_IPHONE) - - // Create the *Mac* implementation of the Audio Device - // -#elif defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC) - if (audioLayer == kPlatformDefaultAudio) - { - // Create *Mac Audio* implementation - ptrAudioDevice = new AudioDeviceMac(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Mac OS X Audio APIs will be utilized"); - } - - if (ptrAudioDevice != NULL) - { - // Create the Mac implementation of the Device Utility. - ptrAudioDeviceUtility = new AudioDeviceUtilityMac(Id()); - } -#endif // #if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC) - - // Create the *Dummy* implementation of the Audio Device - // Available for all platforms - // - if (audioLayer == kDummyAudio) - { - // Create *Dummy Audio* implementation - assert(!ptrAudioDevice); - ptrAudioDevice = new AudioDeviceDummy(Id()); - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Dummy Audio APIs will be utilized"); - - if (ptrAudioDevice != NULL) - { - // Create the Linux implementation of the Device Utility. (It's empty.) - ptrAudioDeviceUtility = new AudioDeviceUtilityLinux(Id()); - } - } - - if (ptrAudioDevice == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, "unable to create the platform specific audio device implementation"); - return -1; - } - - if (ptrAudioDeviceUtility == NULL) - { - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, "unable to create the platform specific audio device utility"); - return -1; - } - - // Store valid output pointers - // - _ptrAudioDevice = ptrAudioDevice; - _ptrAudioDeviceUtility = ptrAudioDeviceUtility; - - return 0; -} - -// ---------------------------------------------------------------------------- -// AttachAudioBuffer -// -// Install "bridge" between the platform implemetation and the generic -// implementation. The "child" shall set the native sampling rate and the -// number of channels in this function call. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::AttachAudioBuffer() -{ - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - _audioDeviceBuffer.SetId(_id); - _ptrAudioDevice->AttachAudioBuffer(&_audioDeviceBuffer); - return 0; -} - -// ---------------------------------------------------------------------------- -// ~AudioDeviceModuleImpl - dtor -// ---------------------------------------------------------------------------- - -AudioDeviceModuleImpl::~AudioDeviceModuleImpl() -{ - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", __FUNCTION__); - { - CriticalSectionScoped lock(_critSect); - - if (_ptrAudioDevice) - { - delete _ptrAudioDevice; - _ptrAudioDevice = NULL; - } - - if (_ptrAudioDeviceUtility) - { - delete _ptrAudioDeviceUtility; - _ptrAudioDeviceUtility = NULL; - } - } - - delete &_critSect; - delete &_critSectEventCb; - delete &_critSectAudioCb; -} - -// ============================================================================ -// Module -// ============================================================================ - -// ---------------------------------------------------------------------------- -// Module::ChangeUniqueId -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "ChangeUniqueId(id=%d)", id); - _id = id; - return 0; -} - -// ---------------------------------------------------------------------------- -// Module::Version -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::Version(WebRtc_Word8* version, WebRtc_UWord32& remainingBufferInBytes, WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "Version(remainingBufferInBytes=%d)", remainingBufferInBytes); - return GetVersion(version, remainingBufferInBytes, position); -} - -// ---------------------------------------------------------------------------- -// Module::TimeUntilNextProcess -// -// Returns the number of milliseconds until the module want a worker thread -// to call Process(). -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::TimeUntilNextProcess() -{ - WebRtc_UWord32 now = AudioDeviceUtility::GetTimeInMS(); - WebRtc_Word32 deltaProcess = kAdmMaxIdleTimeProcess - (now - _lastProcessTime); - return (deltaProcess); -} - -// ---------------------------------------------------------------------------- -// Module::Process -// -// Check for posted error and warning reports. Generate callbacks if -// new reports exists. -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::Process() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "Process()"); - - _lastProcessTime = AudioDeviceUtility::GetTimeInMS(); - - // kPlayoutWarning - if (_ptrAudioDevice->PlayoutWarning()) - { - CriticalSectionScoped lock(_critSectEventCb); - if (_ptrCbAudioDeviceObserver) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "=> OnWarningIsReported(kPlayoutWarning)"); - _ptrCbAudioDeviceObserver->OnWarningIsReported(AudioDeviceObserver::kPlayoutWarning); - } - _ptrAudioDevice->ClearPlayoutWarning(); - } - - // kPlayoutError - if (_ptrAudioDevice->PlayoutError()) - { - CriticalSectionScoped lock(_critSectEventCb); - if (_ptrCbAudioDeviceObserver) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "=> OnErrorIsReported(kPlayoutError)"); - _ptrCbAudioDeviceObserver->OnErrorIsReported(AudioDeviceObserver::kPlayoutError); - } - _ptrAudioDevice->ClearPlayoutError(); - } - - // kRecordingWarning - if (_ptrAudioDevice->RecordingWarning()) - { - CriticalSectionScoped lock(_critSectEventCb); - if (_ptrCbAudioDeviceObserver) - { - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "=> OnWarningIsReported(kRecordingWarning)"); - _ptrCbAudioDeviceObserver->OnWarningIsReported(AudioDeviceObserver::kRecordingWarning); - } - _ptrAudioDevice->ClearRecordingWarning(); - } - - // kRecordingError - if (_ptrAudioDevice->RecordingError()) - { - CriticalSectionScoped lock(_critSectEventCb); - if (_ptrCbAudioDeviceObserver) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "=> OnErrorIsReported(kRecordingError)"); - _ptrCbAudioDeviceObserver->OnErrorIsReported(AudioDeviceObserver::kRecordingError); - } - _ptrAudioDevice->ClearRecordingError(); - } - - return 0; -} - -// ============================================================================ -// Public API -// ============================================================================ - -// ---------------------------------------------------------------------------- -// ActiveAudioLayer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::ActiveAudioLayer(AudioLayer* audioLayer) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - AudioLayer activeAudio; - - if (_ptrAudioDevice->ActiveAudioLayer(activeAudio) == -1) - { - return -1; - } - - *audioLayer = activeAudio; - - if (*audioLayer == AudioDeviceModule::kWindowsWaveAudio) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kWindowsWaveAudio"); - } - else if (*audioLayer == AudioDeviceModule::kWindowsCoreAudio) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kWindowsCoreAudio"); - } - else if (*audioLayer == AudioDeviceModule::kLinuxAlsaAudio) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kLinuxAlsaAudio"); - } - else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: NOT_SUPPORTED"); - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// LastError -// ---------------------------------------------------------------------------- - -AudioDeviceModule::ErrorCode AudioDeviceModuleImpl::LastError() const -{ - return _lastError; -} - -// ---------------------------------------------------------------------------- -// Init -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::Init() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (_initialized) - return 0; - - if (!_ptrAudioDeviceUtility) - return -1; - - if (!_ptrAudioDevice) - return -1; - - _ptrAudioDeviceUtility->Init(); - - if (_ptrAudioDevice->Init() == -1) - { - return -1; - } - - _initialized = true; - return 0; -} - -// ---------------------------------------------------------------------------- -// Terminate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::Terminate() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - if (!_initialized) - return 0; - - if (_ptrAudioDevice->Terminate() == -1) - { - return -1; - } - - _initialized = false; - return 0; -} - -// ---------------------------------------------------------------------------- -// Initialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::Initialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: %d", _initialized); - return (_initialized); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->SpeakerIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", available); - return (0); -} - -// ---------------------------------------------------------------------------- -// InitSpeaker -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::InitSpeaker() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->InitSpeaker()); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->MicrophoneIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// InitMicrophone -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::InitMicrophone() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->InitMicrophone()); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerVolumeIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->SpeakerVolumeIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetSpeakerVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetSpeakerVolume(volume=%u)", volume); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetSpeakerVolume(volume)); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerVolume(WebRtc_UWord32* volume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 level(0); - - if (_ptrAudioDevice->SpeakerVolume(level) == -1) - { - return -1; - } - - *volume = level; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: volume=%u", *volume); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetWaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetWaveOutVolume(WebRtc_UWord16 volumeLeft, WebRtc_UWord16 volumeRight) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetWaveOutVolume(volumeLeft=%u, volumeRight=%u)", - volumeLeft, volumeRight); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetWaveOutVolume(volumeLeft, volumeRight)); -} - -// ---------------------------------------------------------------------------- -// WaveOutVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::WaveOutVolume(WebRtc_UWord16* volumeLeft, WebRtc_UWord16* volumeRight) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 volLeft(0); - WebRtc_UWord16 volRight(0); - - if (_ptrAudioDevice->WaveOutVolume(volLeft, volRight) == -1) - { - return -1; - } - - *volumeLeft = volLeft; - *volumeRight = volRight; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "outputs: volumeLeft=%u, volumeRight=%u", - *volumeLeft, *volumeRight); - - return (0); -} - -// ---------------------------------------------------------------------------- -// SpeakerIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::SpeakerIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - - bool isInitialized = _ptrAudioDevice->SpeakerIsInitialized(); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: %d", isInitialized); - return (isInitialized); -} - -// ---------------------------------------------------------------------------- -// MicrophoneIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::MicrophoneIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - - bool isInitialized = _ptrAudioDevice->MicrophoneIsInitialized(); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: %d", isInitialized); - return (isInitialized); -} - -// ---------------------------------------------------------------------------- -// MaxSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MaxSpeakerVolume(WebRtc_UWord32* maxVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 maxVol(0); - - if (_ptrAudioDevice->MaxSpeakerVolume(maxVol) == -1) - { - return -1; - } - - *maxVolume = maxVol; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: maxVolume=%d", *maxVolume); - return (0); -} - -// ---------------------------------------------------------------------------- -// MinSpeakerVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MinSpeakerVolume(WebRtc_UWord32* minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 minVol(0); - - if (_ptrAudioDevice->MinSpeakerVolume(minVol) == -1) - { - return -1; - } - - *minVolume = minVol; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: minVolume=%u", *minVolume); - return (0); -} - -// ---------------------------------------------------------------------------- -// SpeakerVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerVolumeStepSize(WebRtc_UWord16* stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 delta(0); - - if (_ptrAudioDevice->SpeakerVolumeStepSize(delta) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the speaker-volume step size"); - return -1; - } - - *stepSize = delta; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: stepSize=%u", *stepSize); - return (0); -} - -// ---------------------------------------------------------------------------- -// SpeakerMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerMuteIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->SpeakerMuteIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetSpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetSpeakerMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetSpeakerMute(enable=%d)", enable); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetSpeakerMute(enable)); -} - -// ---------------------------------------------------------------------------- -// SpeakerMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SpeakerMute(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool muted(false); - - if (_ptrAudioDevice->SpeakerMute(muted) == -1) - { - return -1; - } - - *enabled = muted; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: enabled=%u", *enabled); - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneMuteIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneMuteIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->MicrophoneMuteIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetMicrophoneMute(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetMicrophoneMute(enable=%d)", enable); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetMicrophoneMute(enable)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneMute -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneMute(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool muted(false); - - if (_ptrAudioDevice->MicrophoneMute(muted) == -1) - { - return -1; - } - - *enabled = muted; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: enabled=%u", *enabled); - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoostIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneBoostIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->MicrophoneBoostIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetMicrophoneBoost(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetMicrophoneBoost(enable=%d)", enable); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetMicrophoneBoost(enable)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneBoost -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneBoost(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool onOff(false); - - if (_ptrAudioDevice->MicrophoneBoost(onOff) == -1) - { - return -1; - } - - *enabled = onOff; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: enabled=%u", *enabled); - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneVolumeIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->MicrophoneVolumeIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetMicrophoneVolume(WebRtc_UWord32 volume) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetMicrophoneVolume(volume=%u)", volume); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetMicrophoneVolume(volume)); -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneVolume(WebRtc_UWord32* volume) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 level(0); - - if (_ptrAudioDevice->MicrophoneVolume(level) == -1) - { - return -1; - } - - *volume = level; - - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "output: volume=%u", *volume); - return (0); -} - -// ---------------------------------------------------------------------------- -// StereoRecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StereoRecordingIsAvailable(bool* available) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->StereoRecordingIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetStereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetStereoRecording(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetStereoRecording(enable=%d)", enable); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->RecordingIsInitialized()) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "recording in stereo is not supported"); - return -1; - } - - if (_ptrAudioDevice->SetStereoRecording(enable) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to enable stereo recording"); - return -1; - } - - WebRtc_Word8 nChannels(1); - if (enable) - { - nChannels = 2; - } - _audioDeviceBuffer.SetRecordingChannels(nChannels); - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StereoRecording(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool stereo(false); - - if (_ptrAudioDevice->StereoRecording(stereo) == -1) - { - return -1; - } - - *enabled = stereo; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: enabled=%u", *enabled); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetRecordingChannel -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetRecordingChannel(const ChannelType channel) -{ - if (channel == kChannelBoth) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetRecordingChannel(kChannelBoth)"); - } - else if (channel == kChannelLeft) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetRecordingChannel(kChannelLeft)"); - } - else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetRecordingChannel(kChannelRight)"); - } - CHECK_INITIALIZED(); - - bool stereo(false); - - if (_ptrAudioDevice->StereoRecording(stereo) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "recording in stereo is not supported"); - return -1; - } - - return (_audioDeviceBuffer.SetRecordingChannel(channel)); -} - -// ---------------------------------------------------------------------------- -// RecordingChannel -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RecordingChannel(ChannelType* channel) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - ChannelType chType; - - if (_audioDeviceBuffer.RecordingChannel(chType) == -1) - { - return -1; - } - - *channel = chType; - - if (*channel == kChannelBoth) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "output: kChannelBoth)"); - } - else if (*channel == kChannelLeft) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "output: kChannelLeft"); - } - else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "output: kChannelRight"); - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// StereoPlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StereoPlayoutIsAvailable(bool* available) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->StereoPlayoutIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetStereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetStereoPlayout(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetStereoPlayout(enable=%d)", enable); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->PlayoutIsInitialized()) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "unable to set stereo mode while playing side is initialized"); - return -1; - } - - if (_ptrAudioDevice->SetStereoPlayout(enable)) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "stereo playout is not supported"); - return -1; - } - - WebRtc_Word8 nChannels(1); - if (enable) - { - nChannels = 2; - } - _audioDeviceBuffer.SetPlayoutChannels(nChannels); - - return 0; -} - -// ---------------------------------------------------------------------------- -// StereoPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StereoPlayout(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool stereo(false); - - if (_ptrAudioDevice->StereoPlayout(stereo) == -1) - { - return -1; - } - - *enabled = stereo; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: enabled=%u", *enabled); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetAGC -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetAGC(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetAGC(enable=%d)", enable); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetAGC(enable)); -} - -// ---------------------------------------------------------------------------- -// AGC -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::AGC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - return (_ptrAudioDevice->AGC()); -} - -// ---------------------------------------------------------------------------- -// PlayoutIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::PlayoutIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->PlayoutIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// RecordingIsAvailable -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RecordingIsAvailable(bool* available) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - bool isAvailable(0); - - if (_ptrAudioDevice->RecordingIsAvailable(isAvailable) == -1) - { - return -1; - } - - *available = isAvailable; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: available=%d", *available); - return (0); -} - -// ---------------------------------------------------------------------------- -// MaxMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MaxMicrophoneVolume(WebRtc_UWord32* maxVolume) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 maxVol(0); - - if (_ptrAudioDevice->MaxMicrophoneVolume(maxVol) == -1) - { - return -1; - } - - *maxVolume = maxVol; - - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "output: maxVolume=%d", *maxVolume); - return (0); -} - -// ---------------------------------------------------------------------------- -// MinMicrophoneVolume -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MinMicrophoneVolume(WebRtc_UWord32* minVolume) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord32 minVol(0); - - if (_ptrAudioDevice->MinMicrophoneVolume(minVol) == -1) - { - return -1; - } - - *minVolume = minVol; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: minVolume=%u", *minVolume); - return (0); -} - -// ---------------------------------------------------------------------------- -// MicrophoneVolumeStepSize -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::MicrophoneVolumeStepSize(WebRtc_UWord16* stepSize) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 delta(0); - - if (_ptrAudioDevice->MicrophoneVolumeStepSize(delta) == -1) - { - return -1; - } - - *stepSize = delta; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: stepSize=%u", *stepSize); - return (0); -} - -// ---------------------------------------------------------------------------- -// PlayoutDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceModuleImpl::PlayoutDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 nPlayoutDevices = _ptrAudioDevice->PlayoutDevices(); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: #playout devices=%d", nPlayoutDevices); - return ((WebRtc_Word16)(nPlayoutDevices)); -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetPlayoutDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetPlayoutDevice(index)); -} - -// ---------------------------------------------------------------------------- -// SetPlayoutDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetPlayoutDevice(WindowsDeviceType device) -{ - if (device == kDefaultDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetPlayoutDevice(kDefaultDevice)"); - } - else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetPlayoutDevice(kDefaultCommunicationDevice)"); - } - CHECK_INITIALIZED(); - - return (_ptrAudioDevice->SetPlayoutDevice(device)); -} - -// ---------------------------------------------------------------------------- -// PlayoutDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::PlayoutDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::PlayoutDeviceName(index=%u)", index); - CHECK_INITIALIZED(); - - if (name == NULL) - { - _lastError = kAdmErrArgument; - return -1; - } - - if (_ptrAudioDevice->PlayoutDeviceName(index, name, guid) == -1) - { - return -1; - } - - if (name != NULL) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: name=%s", name); - } - if (guid != NULL) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: guid=%s", guid); - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// RecordingDeviceName -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RecordingDeviceName(WebRtc_UWord16 index, WebRtc_Word8 name[kAdmMaxDeviceNameSize], WebRtc_Word8 guid[kAdmMaxGuidSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::RecordingDeviceName(index=%u)", index); - CHECK_INITIALIZED(); - - if (name == NULL) - { - _lastError = kAdmErrArgument; - return -1; - } - - if (_ptrAudioDevice->RecordingDeviceName(index, name, guid) == -1) - { - return -1; - } - - if (name != NULL) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: name=%s", name); - } - if (guid != NULL) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: guid=%s", guid); - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// RecordingDevices -// ---------------------------------------------------------------------------- - -WebRtc_Word16 AudioDeviceModuleImpl::RecordingDevices() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 nRecordingDevices = _ptrAudioDevice->RecordingDevices(); - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: #recording devices=%d", nRecordingDevices); - return ((WebRtc_Word16)nRecordingDevices); -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice I (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetRecordingDevice(WebRtc_UWord16 index) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->SetRecordingDevice(index)); -} - -// ---------------------------------------------------------------------------- -// SetRecordingDevice II (II) -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetRecordingDevice(WindowsDeviceType device) -{ - if (device == kDefaultDevice) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetRecordingDevice(kDefaultDevice)"); - } - else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetRecordingDevice(kDefaultCommunicationDevice)"); - } - CHECK_INITIALIZED(); - - return (_ptrAudioDevice->SetRecordingDevice(device)); -} - -// ---------------------------------------------------------------------------- -// InitPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::InitPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - _audioDeviceBuffer.InitPlayout(); - return (_ptrAudioDevice->InitPlayout()); -} - -// ---------------------------------------------------------------------------- -// InitRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::InitRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - _audioDeviceBuffer.InitRecording(); - return (_ptrAudioDevice->InitRecording()); -} - -// ---------------------------------------------------------------------------- -// PlayoutIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::PlayoutIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - return (_ptrAudioDevice->PlayoutIsInitialized()); -} - -// ---------------------------------------------------------------------------- -// RecordingIsInitialized -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::RecordingIsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - return (_ptrAudioDevice->RecordingIsInitialized()); -} - -// ---------------------------------------------------------------------------- -// StartPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StartPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->StartPlayout()); -} - -// ---------------------------------------------------------------------------- -// StopPlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StopPlayout() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->StopPlayout()); -} - -// ---------------------------------------------------------------------------- -// Playing -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::Playing() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - return (_ptrAudioDevice->Playing()); -} - -// ---------------------------------------------------------------------------- -// StartRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StartRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->StartRecording()); -} -// ---------------------------------------------------------------------------- -// StopRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - return (_ptrAudioDevice->StopRecording()); -} - -// ---------------------------------------------------------------------------- -// Recording -// ---------------------------------------------------------------------------- - -bool AudioDeviceModuleImpl::Recording() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED_BOOL(); - return (_ptrAudioDevice->Recording()); -} - -// ---------------------------------------------------------------------------- -// RegisterEventObserver -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RegisterEventObserver(AudioDeviceObserver* eventCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::RegisterEventObserver(AudioDeviceObserver=0x%x)", eventCallback); - - CriticalSectionScoped lock(_critSectEventCb); - _ptrCbAudioDeviceObserver = eventCallback; - - return 0; -} - -// ---------------------------------------------------------------------------- -// RegisterAudioCallback -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RegisterAudioCallback(AudioTransport* audioCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::RegisterAudioCallback(AudioTransport=0x%x)", audioCallback); - - CriticalSectionScoped lock(_critSectAudioCb); - _audioDeviceBuffer.RegisterAudioCallback(audioCallback); - - return 0; -} - -// ---------------------------------------------------------------------------- -// StartRawInputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StartRawInputFileRecording(const WebRtc_Word8 pcmFileNameUTF8[kAdmMaxFileNameSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::StartRawInputFileRecording(file=%s)", pcmFileNameUTF8); - CHECK_INITIALIZED(); - - if (NULL == pcmFileNameUTF8) - { - return -1; - } - - return (_audioDeviceBuffer.StartInputFileRecording(pcmFileNameUTF8)); -} - -// ---------------------------------------------------------------------------- -// StopRawInputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StopRawInputFileRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - return (_audioDeviceBuffer.StopInputFileRecording()); -} - -// ---------------------------------------------------------------------------- -// StartRawOutputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StartRawOutputFileRecording(const WebRtc_Word8 pcmFileNameUTF8[kAdmMaxFileNameSize]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::StartRawOutputFileRecording(file=%s)", pcmFileNameUTF8); - CHECK_INITIALIZED(); - - if (NULL == pcmFileNameUTF8) - { - return -1; - } - - return (_audioDeviceBuffer.StartOutputFileRecording(pcmFileNameUTF8)); -} - -// ---------------------------------------------------------------------------- -// StopRawOutputFileRecording -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::StopRawOutputFileRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - return (_audioDeviceBuffer.StopOutputFileRecording()); - - return 0; -} - -// ---------------------------------------------------------------------------- -// SetPlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetPlayoutBuffer(const BufferType type, WebRtc_UWord16 sizeMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetPlayoutBuffer(type=%u, sizeMS=%u)", type, sizeMS); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->PlayoutIsInitialized()) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "unable to modify the playout buffer while playing side is initialized"); - return -1; - } - - WebRtc_Word32 ret(0); - - if (kFixedBufferSize == type) - { - if (sizeMS < kAdmMinPlayoutBufferSizeMs || sizeMS > kAdmMaxPlayoutBufferSizeMs) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "size parameter is out of range"); - return -1; - } - } - - if ((ret = _ptrAudioDevice->SetPlayoutBuffer(type, sizeMS)) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to set the playout buffer (error: %d)", LastError()); - } - - return ret; -} - -// ---------------------------------------------------------------------------- -// PlayoutBuffer -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::PlayoutBuffer(BufferType* type, WebRtc_UWord16* sizeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - BufferType bufType; - WebRtc_UWord16 size(0); - - if (_ptrAudioDevice->PlayoutBuffer(bufType, size) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the buffer type and size"); - return -1; - } - - *type = bufType; - *sizeMS = size; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: type=%u, sizeMS=%u", *type, *sizeMS); - return (0); -} - -// ---------------------------------------------------------------------------- -// PlayoutDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::PlayoutDelay(WebRtc_UWord16* delayMS) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 delay(0); - - if (_ptrAudioDevice->PlayoutDelay(delay) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the playout delay"); - return -1; - } - - *delayMS = delay; - - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "output: delayMS=%u", *delayMS); - return (0); -} - -// ---------------------------------------------------------------------------- -// RecordingDelay -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RecordingDelay(WebRtc_UWord16* delayMS) const -{ - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 delay(0); - - if (_ptrAudioDevice->RecordingDelay(delay) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the recording delay"); - return -1; - } - - *delayMS = delay; - - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, "output: delayMS=%u", *delayMS); - return (0); -} - -// ---------------------------------------------------------------------------- -// CPULoad -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::CPULoad(WebRtc_UWord16* load) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_UWord16 cpuLoad(0); - - if (_ptrAudioDevice->CPULoad(cpuLoad) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the CPU load"); - return -1; - } - - *load = cpuLoad; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: load=%u", *load); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetRecordingSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetRecordingSampleRate(const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->SetRecordingSampleRate(samplesPerSec) != 0) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// RecordingSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::RecordingSampleRate(WebRtc_UWord32* samplesPerSec) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_Word32 sampleRate = _audioDeviceBuffer.RecordingSampleRate(); - - if (sampleRate == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the sample rate"); - return -1; - } - - *samplesPerSec = sampleRate; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: samplesPerSec=%u", *samplesPerSec); - return (0); -} - -// ---------------------------------------------------------------------------- -// SetPlayoutSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetPlayoutSampleRate(const WebRtc_UWord32 samplesPerSec) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->SetPlayoutSampleRate(samplesPerSec) != 0) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// PlayoutSampleRate -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::PlayoutSampleRate(WebRtc_UWord32* samplesPerSec) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - WebRtc_Word32 sampleRate = _audioDeviceBuffer.PlayoutSampleRate(); - - if (sampleRate == -1) - { - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "failed to retrieve the sample rate"); - return -1; - } - - *samplesPerSec = sampleRate; - - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: samplesPerSec=%u", *samplesPerSec); - return (0); -} - -// ---------------------------------------------------------------------------- -// ResetAudioDevice -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::ResetAudioDevice() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - - if (_ptrAudioDevice->ResetAudioDevice() == -1) - { - return -1; - } - - return (0); -} - -// ---------------------------------------------------------------------------- -// SetLoudspeakerStatus -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::SetLoudspeakerStatus(bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "AudioDeviceModuleImpl::SetLoudspeakerStatus(enable=%d)", (int)enable); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->SetLoudspeakerStatus(enable) != 0) - { - return -1; - } - - return 0; -} - -// ---------------------------------------------------------------------------- -// GetLoudspeakerStatus -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioDeviceModuleImpl::GetLoudspeakerStatus(bool* enabled) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - CHECK_INITIALIZED(); - - if (_ptrAudioDevice->GetLoudspeakerStatus(*enabled) != 0) - { - return -1; - } - - return 0; -} - -// ============================================================================ -// Private Methods -// ============================================================================ - -// ---------------------------------------------------------------------------- -// Platform -// ---------------------------------------------------------------------------- - -AudioDeviceModuleImpl::PlatformType AudioDeviceModuleImpl::Platform() const -{ - return _platformType; -} - -// ---------------------------------------------------------------------------- -// PlatformAudioLayer -// ---------------------------------------------------------------------------- - -AudioDeviceModule::AudioLayer AudioDeviceModuleImpl::PlatformAudioLayer() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__); - - switch (_platformAudioLayer) - { - case kPlatformDefaultAudio: - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kPlatformDefaultAudio"); - break; - case kWindowsWaveAudio: - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kWindowsWaveAudio"); - break; - case kWindowsCoreAudio: - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kWindowsCoreAudio"); - break; - case kLinuxAlsaAudio: - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kLinuxAlsaAudio"); - break; - case kDummyAudio: - WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, "output: kDummyAudio"); - break; - default: - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "output: INVALID"); - break; - } - - return _platformAudioLayer; -} - -} // namespace webrtc - diff --git a/modules/audio_device/main/source/audio_device_impl.h b/modules/audio_device/main/source/audio_device_impl.h deleted file mode 100644 index 9c8495211..000000000 --- a/modules/audio_device/main/source/audio_device_impl.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IMPL_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_IMPL_H - -#include "audio_device.h" -#include "audio_device_buffer.h" - -namespace webrtc -{ - -class AudioDeviceGeneric; -class AudioDeviceUtility; -class CriticalSectionWrapper; - -class AudioDeviceModuleImpl : public AudioDeviceModule -{ -public: - enum PlatformType - { - kPlatformNotSupported = 0, - kPlatformWin32 = 1, - kPlatformWinCe = 2, - kPlatformLinux = 3, - kPlatformMac = 4 - }; - - WebRtc_Word32 CheckPlatform(); - WebRtc_Word32 CreatePlatformSpecificObjects(); - WebRtc_Word32 AttachAudioBuffer(); - - AudioDeviceModuleImpl(const WebRtc_Word32 id, const AudioLayer audioLayer); - virtual ~AudioDeviceModuleImpl(); - -public: // Module - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - virtual WebRtc_Word32 Version( - WebRtc_Word8 *version, WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - virtual WebRtc_Word32 TimeUntilNextProcess(); - virtual WebRtc_Word32 Process(); - -public: - // Factory methods (resource allocation/deallocation) - static AudioDeviceModule* Create( - const WebRtc_Word32 id, - const AudioLayer audioLayer = kPlatformDefaultAudio); - static void Destroy(AudioDeviceModule* module); - - // Retrieve the currently utilized audio layer - virtual WebRtc_Word32 ActiveAudioLayer(AudioLayer* audioLayer) const; - - // Error handling - virtual ErrorCode LastError() const; - virtual WebRtc_Word32 RegisterEventObserver( - AudioDeviceObserver* eventCallback); - - // Full-duplex transportation of PCM audio - virtual WebRtc_Word32 RegisterAudioCallback( - AudioTransport* audioCallback); - - // Main initializaton and termination - virtual WebRtc_Word32 Init(); - virtual WebRtc_Word32 Terminate(); - virtual bool Initialized() const; - - // Device enumeration - virtual WebRtc_Word16 PlayoutDevices(); - virtual WebRtc_Word16 RecordingDevices(); - virtual WebRtc_Word32 PlayoutDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - virtual WebRtc_Word32 RecordingDeviceName( - WebRtc_UWord16 index, - WebRtc_Word8 name[kAdmMaxDeviceNameSize], - WebRtc_Word8 guid[kAdmMaxGuidSize]); - - // Device selection - virtual WebRtc_Word32 SetPlayoutDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetPlayoutDevice(WindowsDeviceType device); - virtual WebRtc_Word32 SetRecordingDevice(WebRtc_UWord16 index); - virtual WebRtc_Word32 SetRecordingDevice(WindowsDeviceType device); - - // Audio transport initialization - virtual WebRtc_Word32 PlayoutIsAvailable(bool* available); - virtual WebRtc_Word32 InitPlayout(); - virtual bool PlayoutIsInitialized() const; - virtual WebRtc_Word32 RecordingIsAvailable(bool* available); - virtual WebRtc_Word32 InitRecording(); - virtual bool RecordingIsInitialized() const; - - // Audio transport control - virtual WebRtc_Word32 StartPlayout(); - virtual WebRtc_Word32 StopPlayout(); - virtual bool Playing() const; - virtual WebRtc_Word32 StartRecording(); - virtual WebRtc_Word32 StopRecording(); - virtual bool Recording() const; - - // Microphone Automatic Gain Control (AGC) - virtual WebRtc_Word32 SetAGC(bool enable); - virtual bool AGC() const; - - // Volume control based on the Windows Wave API (Windows only) - virtual WebRtc_Word32 SetWaveOutVolume(WebRtc_UWord16 volumeLeft, - WebRtc_UWord16 volumeRight); - virtual WebRtc_Word32 WaveOutVolume(WebRtc_UWord16* volumeLeft, - WebRtc_UWord16* volumeRight) const; - - // Audio mixer initialization - virtual WebRtc_Word32 SpeakerIsAvailable(bool* available); - virtual WebRtc_Word32 InitSpeaker(); - virtual bool SpeakerIsInitialized() const; - virtual WebRtc_Word32 MicrophoneIsAvailable(bool* available); - virtual WebRtc_Word32 InitMicrophone(); - virtual bool MicrophoneIsInitialized() const; - - // Speaker volume controls - virtual WebRtc_Word32 SpeakerVolumeIsAvailable(bool* available); - virtual WebRtc_Word32 SetSpeakerVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 SpeakerVolume(WebRtc_UWord32* volume) const; - virtual WebRtc_Word32 MaxSpeakerVolume(WebRtc_UWord32* maxVolume) const; - virtual WebRtc_Word32 MinSpeakerVolume(WebRtc_UWord32* minVolume) const; - virtual WebRtc_Word32 SpeakerVolumeStepSize( - WebRtc_UWord16* stepSize) const; - - // Microphone volume controls - virtual WebRtc_Word32 MicrophoneVolumeIsAvailable(bool* available); - virtual WebRtc_Word32 SetMicrophoneVolume(WebRtc_UWord32 volume); - virtual WebRtc_Word32 MicrophoneVolume(WebRtc_UWord32* volume) const; - virtual WebRtc_Word32 MaxMicrophoneVolume( - WebRtc_UWord32* maxVolume) const; - virtual WebRtc_Word32 MinMicrophoneVolume( - WebRtc_UWord32* minVolume) const; - virtual WebRtc_Word32 MicrophoneVolumeStepSize( - WebRtc_UWord16* stepSize) const; - - // Speaker mute control - virtual WebRtc_Word32 SpeakerMuteIsAvailable(bool* available); - virtual WebRtc_Word32 SetSpeakerMute(bool enable); - virtual WebRtc_Word32 SpeakerMute(bool* enabled) const; - - // Microphone mute control - virtual WebRtc_Word32 MicrophoneMuteIsAvailable(bool* available); - virtual WebRtc_Word32 SetMicrophoneMute(bool enable); - virtual WebRtc_Word32 MicrophoneMute(bool* enabled) const; - - // Microphone boost control - virtual WebRtc_Word32 MicrophoneBoostIsAvailable(bool* available); - virtual WebRtc_Word32 SetMicrophoneBoost(bool enable); - virtual WebRtc_Word32 MicrophoneBoost(bool* enabled) const; - - // Stereo support - virtual WebRtc_Word32 StereoPlayoutIsAvailable(bool* available) const; - virtual WebRtc_Word32 SetStereoPlayout(bool enable); - virtual WebRtc_Word32 StereoPlayout(bool* enabled) const; - virtual WebRtc_Word32 StereoRecordingIsAvailable(bool* available) const; - virtual WebRtc_Word32 SetStereoRecording(bool enable); - virtual WebRtc_Word32 StereoRecording(bool* enabled) const; - virtual WebRtc_Word32 SetRecordingChannel(const ChannelType channel); - virtual WebRtc_Word32 RecordingChannel(ChannelType* channel) const; - - // Delay information and control - virtual WebRtc_Word32 SetPlayoutBuffer(const BufferType type, - WebRtc_UWord16 sizeMS = 0); - virtual WebRtc_Word32 PlayoutBuffer(BufferType* type, - WebRtc_UWord16* sizeMS) const; - virtual WebRtc_Word32 PlayoutDelay(WebRtc_UWord16* delayMS) const; - virtual WebRtc_Word32 RecordingDelay(WebRtc_UWord16* delayMS) const; - - // CPU load - virtual WebRtc_Word32 CPULoad(WebRtc_UWord16* load) const; - - // Recording of raw PCM data - virtual WebRtc_Word32 StartRawOutputFileRecording( - const WebRtc_Word8 pcmFileNameUTF8[kAdmMaxFileNameSize]); - virtual WebRtc_Word32 StopRawOutputFileRecording(); - virtual WebRtc_Word32 StartRawInputFileRecording( - const WebRtc_Word8 pcmFileNameUTF8[kAdmMaxFileNameSize]); - virtual WebRtc_Word32 StopRawInputFileRecording(); - - // Native sample rate controls (samples/sec) - virtual WebRtc_Word32 SetRecordingSampleRate( - const WebRtc_UWord32 samplesPerSec); - virtual WebRtc_Word32 RecordingSampleRate( - WebRtc_UWord32* samplesPerSec) const; - virtual WebRtc_Word32 SetPlayoutSampleRate( - const WebRtc_UWord32 samplesPerSec); - virtual WebRtc_Word32 PlayoutSampleRate( - WebRtc_UWord32* samplesPerSec) const; - - // Mobile device specific functions - virtual WebRtc_Word32 ResetAudioDevice(); - virtual WebRtc_Word32 SetLoudspeakerStatus(bool enable); - virtual WebRtc_Word32 GetLoudspeakerStatus(bool* enabled) const; - -public: - WebRtc_Word32 Id() {return _id;} - -private: - PlatformType Platform() const; - AudioLayer PlatformAudioLayer() const; - -private: - CriticalSectionWrapper& _critSect; - CriticalSectionWrapper& _critSectEventCb; - CriticalSectionWrapper& _critSectAudioCb; - - AudioDeviceObserver* _ptrCbAudioDeviceObserver; - - AudioDeviceUtility* _ptrAudioDeviceUtility; - AudioDeviceGeneric* _ptrAudioDevice; - - AudioDeviceBuffer _audioDeviceBuffer; - - WebRtc_Word32 _id; - AudioLayer _platformAudioLayer; - WebRtc_UWord32 _lastProcessTime; - PlatformType _platformType; - bool _initialized; - mutable ErrorCode _lastError; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_INTERFACE_AUDIO_DEVICE_IMPL_H_ diff --git a/modules/audio_device/main/source/audio_device_utility.cc b/modules/audio_device/main/source/audio_device_utility.cc deleted file mode 100644 index a01ba2e5b..000000000 --- a/modules/audio_device/main/source/audio_device_utility.cc +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "audio_device_utility.h" - -#if defined(_WIN32) - -// ============================================================================ -// Windows -// ============================================================================ - -#include -#include -#include -#include -#include - -namespace webrtc -{ - -void AudioDeviceUtility::Sleep(WebRtc_UWord32 milliseconds) -{ - return ::Sleep(milliseconds); -} - -void AudioDeviceUtility::WaitForKey() -{ - _getch(); -} - -WebRtc_UWord32 AudioDeviceUtility::GetTimeInMS() -{ - return timeGetTime(); -} - -bool AudioDeviceUtility::StringCompare(const WebRtc_Word8* str1 , const WebRtc_Word8* str2, const WebRtc_UWord32 length) -{ - return ((_strnicmp(str1, str2, length) == 0) ? true : false); -} - -} // namespace webrtc - -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - -// ============================================================================ -// Linux & Mac -// ============================================================================ - -#include // gettimeofday -#include // nanosleep, gettimeofday -#include // strncasecmp -#include // getchar -#include // tcgetattr - -#include - -namespace webrtc -{ - -void AudioDeviceUtility::WaitForKey() -{ - -struct termios oldt, newt; - -int ch; - - - -tcgetattr( STDIN_FILENO, &oldt ); - - - -// we don't want getchar to echo! - -newt = oldt; - -newt.c_lflag &= ~( ICANON | ECHO ); - -tcsetattr( STDIN_FILENO, TCSANOW, &newt ); - - - -// catch any newline that's hanging around... - -// you'll have to hit enter twice if you - -// choose enter out of all available keys - -if (getchar() == '\n') - -{ - - ch = getchar(); - -} - - -tcsetattr( STDIN_FILENO, TCSANOW, &oldt ); -} - -WebRtc_UWord32 AudioDeviceUtility::GetTimeInMS() -{ - struct timeval tv; - struct timezone tz; - WebRtc_UWord32 val; - - gettimeofday(&tv, &tz); - val = (WebRtc_UWord32)(tv.tv_sec*1000 + tv.tv_usec/1000); - return val; -} - -void AudioDeviceUtility::Sleep(WebRtc_UWord32 milliseconds) -{ - timespec t; - t.tv_sec = milliseconds/1000; - t.tv_nsec = (milliseconds-(milliseconds/1000)*1000)*1000000; - nanosleep(&t,NULL); -} - -bool AudioDeviceUtility::StringCompare(const WebRtc_Word8* str1 , const WebRtc_Word8* str2, const WebRtc_UWord32 length) -{ - return (strncasecmp(str1, str2, length) == 0)?true: false; -} - -} // namespace webrtc - -#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - - diff --git a/modules/audio_device/main/source/audio_device_utility.h b/modules/audio_device/main/source/audio_device_utility.h deleted file mode 100644 index b967950f0..000000000 --- a/modules/audio_device/main/source/audio_device_utility.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_H - -#include "typedefs.h" - -namespace webrtc -{ - -class AudioDeviceUtility -{ -public: - static WebRtc_UWord32 GetTimeInMS(); - static void Sleep(WebRtc_UWord32 milliseconds); - static void WaitForKey(); - static bool StringCompare(const WebRtc_Word8* str1, - const WebRtc_Word8* str2, - const WebRtc_UWord32 length); - virtual WebRtc_Word32 Init() = 0; - - virtual ~AudioDeviceUtility() {} -}; - -} // namespace webrtc - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_UTILITY_H - diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/.classpath b/modules/audio_device/main/test/Android/audio_device_android_test/.classpath deleted file mode 100644 index 6e9239ff0..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/.project b/modules/audio_device/main/test/Android/audio_device_android_test/.project deleted file mode 100644 index 38a630700..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - AudioDeviceAndroidTest - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/AndroidManifest.xml b/modules/audio_device/main/test/Android/audio_device_android_test/AndroidManifest.xml deleted file mode 100644 index a0c91bce5..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/default.properties b/modules/audio_device/main/test/Android/audio_device_android_test/default.properties deleted file mode 100644 index 19ddebd41..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/default.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-3 diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/gen/org/webrtc/voiceengine/test/R.java b/modules/audio_device/main/test/Android/audio_device_android_test/gen/org/webrtc/voiceengine/test/R.java deleted file mode 100644 index a29578086..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/gen/org/webrtc/voiceengine/test/R.java +++ /dev/null @@ -1,26 +0,0 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package org.webrtc.voiceengine.test; - -public final class R { - public static final class attr { - } - public static final class drawable { - public static final int icon=0x7f020000; - } - public static final class id { - public static final int Button01=0x7f050000; - } - public static final class layout { - public static final int main=0x7f030000; - } - public static final class string { - public static final int app_name=0x7f040000; - public static final int run_button=0x7f040001; - } -} diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/jni/audio_device_android_test.cc b/modules/audio_device/main/test/Android/audio_device_android_test/jni/audio_device_android_test.cc deleted file mode 100644 index f46c6b296..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/jni/audio_device_android_test.cc +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include // memset -#include - -#include "org_webrtc_voiceengine_test_AudioDeviceAndroidTest.h" - -#include "../../../../interface/audio_device.h" - -#define LOG_TAG "WebRTC ADM Native" - -void api_test(); -void func_test(int); - -typedef struct -{ - // Other - JavaVM* jvm; -} AdmData; - -static AdmData admData; - -jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) -{ - if (!vm) - { - __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, - "JNI_OnLoad did not receive a valid VM pointer"); - return -1; - } - - // Get JNI - JNIEnv* env; - if (JNI_OK != vm->GetEnv(reinterpret_cast (&env), - JNI_VERSION_1_4)) - { - __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, - "JNI_OnLoad could not get JNI env"); - return -1; - } - - // Get class to register the native functions with - // jclass regClass = - // env->FindClass("org/webrtc/voiceengine/test/AudioDeviceAndroidTest"); - // if (!regClass) { - // return -1; // Exception thrown - // } - - // Register native functions - // JNINativeMethod methods[1]; - // methods[0].name = NULL; - // methods[0].signature = NULL; - // methods[0].fnPtr = NULL; - // if (JNI_OK != env->RegisterNatives(regClass, methods, 1)) - // { - // return -1; - // } - - // Init VoiceEngine data - memset(&admData, 0, sizeof(admData)); - - // Store the JVM - admData.jvm = vm; - - return JNI_VERSION_1_4; -} - -JNIEXPORT jboolean JNICALL -Java_org_webrtc_voiceengine_test_AudioDeviceAndroidTest_NativeInit(JNIEnv * env, - jclass) -{ - // Look up and cache any interesting class, field and method IDs for - // any used java class here - - return true; -} - -JNIEXPORT jint JNICALL -Java_org_webrtc_voiceengine_test_AudioDeviceAndroidTest_RunTest(JNIEnv *env, - jobject context, - jint test) -{ - // Set instance independent Java objects - webrtc::AudioDeviceModule::SetAndroidObjects(admData.jvm, env, context); - - // Start test - if (0 == test) - { - api_test(); - } - else - { - func_test(test); - } - - // Clear instance independent Java objects - webrtc::AudioDeviceModule::SetAndroidObjects(NULL, NULL, NULL); - - return 0; -} diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/jni/org_webrtc_voiceengine_test_AudioDeviceAndroidTest.h b/modules/audio_device/main/test/Android/audio_device_android_test/jni/org_webrtc_voiceengine_test_AudioDeviceAndroidTest.h deleted file mode 100644 index 5cbc56fb8..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/jni/org_webrtc_voiceengine_test_AudioDeviceAndroidTest.h +++ /dev/null @@ -1,29 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_webrtc_voiceengine_test_AudioDeviceAndroidTest */ - -#ifndef _Included_org_webrtc_voiceengine_test_AudioDeviceAndroidTest -#define _Included_org_webrtc_voiceengine_test_AudioDeviceAndroidTest -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_webrtc_voiceengine_test_AudioDeviceAndroidTest - * Method: NativeInit - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_org_webrtc_voiceengine_test_AudioDeviceAndroidTest_NativeInit - (JNIEnv *, jclass); - -/* - * Class: org_webrtc_voiceengine_test_AudioDeviceAndroidTest - * Method: RunTest - * Signature: (I)I - */ -JNIEXPORT jint JNICALL Java_org_webrtc_voiceengine_test_AudioDeviceAndroidTest_RunTest - (JNIEnv *, jobject, jint); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/res/drawable/icon.png b/modules/audio_device/main/test/Android/audio_device_android_test/res/drawable/icon.png deleted file mode 100644 index a07c69fa5a0f4da5d5efe96eea12a543154dbab6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2574 zcmV+p3i0)cP)Q`Og{P|8RRXpj5bgrSmEzSMfBn+{{vpNxw?;5UX;iv9sYxy_`IQHs$i<61a_iv^L>h8s-`D(`e@|IgS*Fj zNGM876Gf;3D8*1UX9a%v>yJKD*QkCwW2AirU(L{qNA)JghmGItc;(H<$!ABY&gBy1vJIEUj-b8%el*o|VkG)LqNx#TG>Jvj^jIte!!+RY z)T4j$7+PoF1AkRBf}R#^T=-q|PaK1$c<4UH)Hpq3$4WA|xtr!ZQLC=*vNE>O6E9kp+5X0eKB$6>C(lPwI@3#oY zhS_%x7e|j!$yG?ECXmh~EH~^OeuK}+sWoJse3Z3?ha3n`MM9KvA?uqpEnBg4Q46)7 zM$p%a$@l;+O}vfvx%XjH`}a{(-HHth9!JaUwV0*VqGR48^gWNYN<&~7x)y$e!X>e` zZ5!6KZoxbKuV9XUDI%#M1~IVh?pNSdeb~6@$y`v|yk=XK+fHxnDqnUK4&=QRNyIVf zYbDM*cI>~qIy*a7=z7uqkw@agd(<=y-Q7L!ty_23SGdXmahO<;N=wB+j;lNm%=OHC zy zU|>La6h%92y4IPufI$9>Xu!@y`TaNgtg&41@PwMwBdmSm7)xAWDLoqjZ==P2#*k7! z3o1)cVSI3KP_!?d8G^Lg0FtLXC~JYdxi|c%h~lXEixY=%VSFF@!*3&&9>(Rb|iK54Cx5;s~PY5iaV1het%w`dgQFBAJ;aFK zImQC}(|QaCFYUm1JVfzSc)ebv=)ObI)0jwJb``}Zj9J0n0Xgn*Zc(rFM9$xh_makZbm-at_v5^SW zM1y1SW@%+FuIy*WR)i3A2N_q;(YO`O!A|Ts^%z}9ZepCj3ytlw#x%N_fNrKKtPh`< z|1{UqF`4LxHaCQ79+E=uUXCOZ35jAMRz%R%0(P!0FMv=sk>Nr8%+OzY^c-M9@+fz=G`qa@v4sF5u-2289-#$**LWnyNNDwDf1( zkUiMnw|y$tn>pQP=Vn!#|17L^5AGrjtBkN$D@v)Z7LXc5EFhLB4<;7Wehh)CMqX|W zqsiZaO^benJ_hwa&V0ub$-_HUk**?g6fm9|!@kguU6*zhK)$qn-<3*kFrYPIaqR=V zUaUvk>@F_89b@tHs8R!*QKY;INJ<2_U+K6Ca3e9Gsl2{qY0%a7J?uICWgHuLfj+MB z=GkAN1&ifT#2u}B+2S#~$5jA(Qn^;H%CCmIae4AE-Dsng|Hl*Ov!z72k3ZnJs{pp| z+pW`DDueC#mEWOf=ucJ!dTL}hzOeiS-i?m2E;`EKz4<&Lu~NnW?peqVU^@<+T3KKu z{yrI%Qy-Z%HEvLUz}n^~m?7x`xuCtNR#L2En!T>dQtIKdS#V-Hzt3RtwTeYtmQ&dR z6qXZvac*oc@BUYEH%@Ylv_1&tSjkbzzU6*h1(3^C`;1z;g_SmOtclS?KWk2VYE zM*oS<=C483XckW?GN|1jfh3Ro(h - - - - diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/res/values/strings.xml b/modules/audio_device/main/test/Android/audio_device_android_test/res/values/strings.xml deleted file mode 100644 index bbb6f51d4..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - WebRTC Audio Device Android Test -Run Test - diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/AudioDeviceAndroid.java b/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/AudioDeviceAndroid.java deleted file mode 100644 index f05f57910..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/AudioDeviceAndroid.java +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../source/Android/org/webrtc/voiceengine/AudioDeviceAndroid.java diff --git a/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/test/AudioDeviceAndroidTest.java b/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/test/AudioDeviceAndroidTest.java deleted file mode 100644 index b87af46a6..000000000 --- a/modules/audio_device/main/test/Android/audio_device_android_test/src/org/webrtc/voiceengine/test/AudioDeviceAndroidTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.webrtc.voiceengine.test; - -import android.app.Activity; -import android.media.AudioManager; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.Button; - -public class AudioDeviceAndroidTest extends Activity { - private Thread _testThread; - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - final Button buttonStart = (Button) findViewById(R.id.Button01); - // buttonStart.setWidth(200); - // button.layout(50, 50, 100, 40); - buttonStart.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - _testThread = new Thread(_testProc); - _testThread.start(); - } - }); - - // Suggest to use the voice call audio stream for hardware volume - // controls - setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); - - DoLog("Started WebRTC Android ADM Test"); - } - - private Runnable _testProc = new Runnable() { - public void run() { - // TODO(xians), choose test from GUI - // Select test here, 0 for API test, 1-> for Func tests - RunTest(5); - } - }; - - private void DoLog(String msg) { - Log.d("*WebRTC ADM*", msg); - } - - // //////////////// Native function prototypes //////////////////// - - // Init wrapper - private native static boolean NativeInit(); - - // Function used to call test - private native int RunTest(int testType); - - // Load native library - static { - Log.d("*WebRTC ADM*", "Loading audio_device_android_test..."); - System.loadLibrary("audio_device_android_test"); - - Log.d("*WebRTC ADM*", "Calling native init..."); - if (!NativeInit()) { - Log.e("*WebRTC ADM*", "Native init failed"); - throw new RuntimeException("Native init failed"); - } else { - Log.d("*WebRTC ADM*", "Native init successful"); - } - } -} diff --git a/modules/audio_device/main/test/README.txt b/modules/audio_device/main/test/README.txt deleted file mode 100644 index 7435ac5db..000000000 --- a/modules/audio_device/main/test/README.txt +++ /dev/null @@ -1,23 +0,0 @@ -INSTRUCTIONS: - -- Start with test #3 (Device enumeration) to get an overview of the available - audio devices. -- Next, proceed with test #4 (Device selection) to get more details about - the supported functions for each audio device. -- Verify two-way audio in test #5. - Repeat this test for different selections of playout and recording devices. -- More detailed tests (volume, mute etc.) can also be performed using #6-#11. - -NOTE: - -- Some tests requires that the user opens up the audio mixer dialog and - verifies that a certain action (e.g. Mute ON/OFF) is executed correctly. -- Files can be recorded during some tests to enable off-line analysis. -- Full support of 'Default Communication' devices requires Windows 7. -- If a test consists of several sub tests, press any key to start a new sub test. - -KNOWN ISSUES: - -- Microphone Boost control is not supported on Windows Vista or Windows 7. -- Speaker and microphone volume controls will not work as intended on Windows - Vista if a 'Default Communication' device is selected in any direction. diff --git a/modules/audio_device/main/test/audio_device_test_api.cc b/modules/audio_device/main/test/audio_device_test_api.cc deleted file mode 100644 index 9a467caa3..000000000 --- a/modules/audio_device/main/test/audio_device_test_api.cc +++ /dev/null @@ -1,2238 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "audio_device_test_defines.h" - -#include "../source/audio_device_config.h" -#include "../source/audio_device_utility.h" - -// Helper functions -#if defined(ANDROID) -char filenameStr[2][256] = -{ 0}; // Allow two buffers for those API calls taking two filenames -int currentStr = 0; - -char* GetFilename(char* filename) -{ - currentStr = !currentStr; - sprintf(filenameStr[currentStr], "/sdcard/admtest/%s", filename); - return filenameStr[currentStr]; -} -const char* GetFilename(const char* filename) -{ - currentStr = !currentStr; - sprintf(filenameStr[currentStr], "/sdcard/admtest/%s", filename); - return filenameStr[currentStr]; -} -int GetResource(char* resource, char* dest, int destLen) -{ - currentStr = !currentStr; - sprintf(filenameStr[currentStr], "/sdcard/admtest/%s", resource); - strncpy(dest, filenameStr[currentStr], destLen-1); - return 0; -} -char* GetResource(char* resource) -{ - currentStr = !currentStr; - sprintf(filenameStr[currentStr], "/sdcard/admtest/%s", resource); - return filenameStr[currentStr]; -} -const char* GetResource(const char* resource) -{ - currentStr = !currentStr; - sprintf(filenameStr[currentStr], "/sdcard/admtest/%s", resource); - return filenameStr[currentStr]; -} -#elif !defined(MAC_IPHONE) -char* GetFilename(char* filename) -{ - return filename; -} -const char* GetFilename(const char* filename) -{ - return filename; -} -char* GetResource(char* resource) -{ - return resource; -} -const char* GetResource(const char* resource) -{ - return resource; -} -#endif - -using namespace webrtc; - -// ---------------------------------------------------------------------------- -// AudioEventObserverAPI -// ---------------------------------------------------------------------------- - -class AudioEventObserverAPI: public AudioDeviceObserver -{ -public: - AudioEventObserverAPI(AudioDeviceModule* audioDevice) : - _audioDevice(audioDevice) - { - } - ; - ~AudioEventObserverAPI() - { - } - ; - virtual void OnErrorIsReported(const ErrorCode error) - { - TEST_LOG("\n[*** ERROR ***] => OnErrorIsReported(%d)\n\n", error); - _error = error; - // TEST(_audioDevice->StopRecording() == 0); - // TEST(_audioDevice->StopPlayout() == 0); - } - ; - virtual void OnWarningIsReported(const WarningCode warning) - { - TEST_LOG("\n[*** WARNING ***] => OnWarningIsReported(%d)\n\n", warning); - _warning = warning; - TEST(_audioDevice->StopRecording() == 0); - TEST(_audioDevice->StopPlayout() == 0); - } - ; -public: - ErrorCode _error; - WarningCode _warning; -private: - AudioDeviceModule* _audioDevice; -}; - -class AudioTransportAPI: public AudioTransport -{ -public: - AudioTransportAPI(AudioDeviceModule* audioDevice) : - _audioDevice(audioDevice), _recCount(0), _playCount(0) - { - } - ; - - ~AudioTransportAPI() - { - } - ; - - virtual WebRtc_Word32 RecordedDataIsAvailable( - const WebRtc_Word8* audioSamples, - const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 sampleRate, - const WebRtc_UWord32 totalDelay, - const WebRtc_Word32 clockSkew, - const WebRtc_UWord32 currentMicLevel, - WebRtc_UWord32& newMicLevel) - { - _recCount++; - if (_recCount % 100 == 0) - { - if (nChannels == 1) - { - // mono - TEST_LOG("-"); - } else if ((nChannels == 2) && (nBytesPerSample == 2)) - { - // stereo but only using one channel - TEST_LOG("-|"); - } else - { - // stereo - TEST_LOG("--"); - } - } - - return 0; - } - - virtual WebRtc_Word32 NeedMorePlayData(const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 sampleRate, - WebRtc_Word8* audioSamples, - WebRtc_UWord32& nSamplesOut) - { - _playCount++; - if (_playCount % 100 == 0) - { - if (nChannels == 1) - { - TEST_LOG("+"); - } else - { - TEST_LOG("++"); - } - } - - nSamplesOut = 480; - - return 0; - } - ; -private: - AudioDeviceModule* _audioDevice; - WebRtc_UWord32 _recCount; - WebRtc_UWord32 _playCount; -}; - -int api_test(); - - -#if !defined(MAC_IPHONE) && !defined(ANDROID) -int api_test(); - -int main(int /*argc*/, char* /*argv*/[]) -{ - api_test(); -} -#endif - -int api_test() -{ - int i(0); - - TEST_LOG("========================================\n"); - TEST_LOG("API Test of the WebRtcAudioDevice Module\n"); - TEST_LOG("========================================\n\n"); - - ProcessThread* processThread = ProcessThread::CreateProcessThread(); - processThread->Start(); - - // ======================================================= - // AudioDeviceModule::Create - // - // Windows: - // if (WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - // user can select between default (Core) or Wave - // else - // user can select between default (Wave) or Wave - // ======================================================= - - const WebRtc_Word32 myId = 444; - AudioDeviceModule* audioDevice(NULL); - -#if defined(_WIN32) - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxAlsaAudio)) == NULL); -#if defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - TEST_LOG("WEBRTC_WINDOWS_CORE_AUDIO_BUILD is defined!\n\n"); - // create default implementation (=Core Audio) instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); - AudioDeviceModule::Destroy(audioDevice); - // create non-default (=Wave Audio) instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsWaveAudio)) != NULL); - AudioDeviceModule::Destroy(audioDevice); - // explicitly specify usage of Core Audio (same as default) - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsCoreAudio)) != NULL); -#else - // TEST_LOG("WEBRTC_WINDOWS_CORE_AUDIO_BUILD is *not* defined!\n"); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsCoreAudio)) == NULL); - // create default implementation (=Wave Audio) instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); - AudioDeviceModule::Destroy(audioDevice); - // explicitly specify usage of Wave Audio (same as default) - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsWaveAudio)) != NULL); -#endif -#endif - -#if defined(ANDROID) - // Fails tests - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsWaveAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsCoreAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxAlsaAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxPulseAudio)) == NULL); - // Create default implementation instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); -#elif defined(WEBRTC_LINUX) - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsWaveAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsCoreAudio)) == NULL); - // create default implementation (=ALSA Audio) instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); - AudioDeviceModule::Destroy(audioDevice); - // explicitly specify usage of Pulse Audio (same as default) - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxPulseAudio)) != NULL); -#endif - -#if defined(WEBRTC_MAC) - // Fails tests - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsWaveAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kWindowsCoreAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxAlsaAudio)) == NULL); - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kLinuxPulseAudio)) == NULL); - // Create default implementation instance - TEST((audioDevice = AudioDeviceModule::Create( - myId, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); -#endif - - if (audioDevice == NULL) - { -#ifdef _WIN32 - goto Exit; -#else - TEST_LOG("Failed creating audio device object! \n"); - return 0; -#endif - } - - processThread->RegisterModule(audioDevice); - - // =============== - // Module::Version - // =============== - - WebRtc_Word8 version[256]; - WebRtc_UWord32 remainingBufferInBytes = 256; - WebRtc_UWord32 tooFewBytes = 10; - WebRtc_UWord32 position = 0; - - TEST(audioDevice->Version(version, tooFewBytes, position) == -1); - TEST(audioDevice->Version(NULL, remainingBufferInBytes, position) == -1); - - TEST(audioDevice->Version(version, remainingBufferInBytes, position) == 0); - TEST(position == 18); // assumes "AudioDevice x.y.z" + NULL - TEST(remainingBufferInBytes == (256-position)); - - TEST_LOG("Version: %s\n\n", version); - - TEST_LOG("Testing...\n\n"); - - // ===================== - // RegisterEventObserver - // ===================== - - AudioEventObserverAPI* eventObserver = - new AudioEventObserverAPI(audioDevice); - - TEST(audioDevice->RegisterEventObserver(NULL) == 0); - TEST(audioDevice->RegisterEventObserver(eventObserver) == 0); - TEST(audioDevice->RegisterEventObserver(NULL) == 0); - - // ===================== - // RegisterAudioCallback - // ===================== - - AudioTransportAPI* audioTransport = new AudioTransportAPI(audioDevice); - - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - // ==== - // Init - // ==== - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - // ========= - // Terminate - // ========= - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - // ------------------------------------------------------------------------ - // Ensure that we keep audio device initialized for all API tests: - // - TEST(audioDevice->Init() == 0); - // ------------------------------------------------------------------------ - - // goto SHORTCUT; - - WebRtc_Word16 nDevices(0); - - // ============== - // PlayoutDevices - // ============== - - TEST((nDevices = audioDevice->PlayoutDevices()) > 0); - TEST((nDevices = audioDevice->PlayoutDevices()) > 0); - - // ================ - // RecordingDevices - // ================ - - TEST((nDevices = audioDevice->RecordingDevices()) > 0); - TEST((nDevices = audioDevice->RecordingDevices()) > 0); - - // ================= - // PlayoutDeviceName - // ================= - - WebRtc_Word8 name[kAdmMaxDeviceNameSize]; - WebRtc_Word8 guid[kAdmMaxGuidSize]; - - nDevices = audioDevice->PlayoutDevices(); - - // fail tests - TEST(audioDevice->PlayoutDeviceName(-2, name, guid) == -1); - TEST(audioDevice->PlayoutDeviceName(nDevices, name, guid) == -1); - TEST(audioDevice->PlayoutDeviceName(0, NULL, guid) == -1); - - // bulk tests - TEST(audioDevice->PlayoutDeviceName(0, name, NULL) == 0); -#ifdef _WIN32 - TEST(audioDevice->PlayoutDeviceName(-1, name, NULL) == 0); // shall be mapped to 0 -#else - TEST(audioDevice->PlayoutDeviceName(-1, name, NULL) == -1); -#endif - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->PlayoutDeviceName(i, name, guid) == 0); - TEST(audioDevice->PlayoutDeviceName(i, name, NULL) == 0); - } - - // =================== - // RecordingDeviceName - // =================== - - nDevices = audioDevice->RecordingDevices(); - - // fail tests - TEST(audioDevice->RecordingDeviceName(-2, name, guid) == -1); - TEST(audioDevice->RecordingDeviceName(nDevices, name, guid) == -1); - TEST(audioDevice->RecordingDeviceName(0, NULL, guid) == -1); - - // bulk tests - TEST(audioDevice->RecordingDeviceName(0, name, NULL) == 0); -#ifdef _WIN32 - TEST(audioDevice->RecordingDeviceName(-1, name, NULL) == 0); // shall me mapped to 0 -#else - TEST(audioDevice->RecordingDeviceName(-1, name, NULL) == -1); -#endif - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->RecordingDeviceName(i, name, guid) == 0); - TEST(audioDevice->RecordingDeviceName(i, name, NULL) == 0); - } - - // ================ - // SetPlayoutDevice - // ================ - - nDevices = audioDevice->PlayoutDevices(); - - // fail tests - TEST(audioDevice->SetPlayoutDevice(-1) == -1); - TEST(audioDevice->SetPlayoutDevice(nDevices) == -1); - - // bulk tests -#ifdef _WIN32 - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == 0); -#else - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultCommunicationDevice) == -1); - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == -1); -#endif - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - } - - // ================== - // SetRecordingDevice - // ================== - - nDevices = audioDevice->RecordingDevices(); - - WebRtc_UWord16 nRecordingDevices(audioDevice->RecordingDevices()); - - // fail tests - TEST(audioDevice->SetRecordingDevice(-1) == -1); - TEST(audioDevice->SetRecordingDevice(nDevices) == -1); - - // bulk tests -#ifdef _WIN32 - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice) == 0); -#else - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == -1); - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultDevice) == -1); -#endif - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - } - - // ================== - // PlayoutIsAvailable - // ================== - - bool available(false); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - -#ifdef _WIN32 - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); // availability check should not initialize - - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); -#endif - - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); - } - - // ==================== - // RecordingIsAvailable - // ==================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - -#ifdef _WIN32 - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - TEST(audioDevice->RecordingIsInitialized() == false); - - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - TEST(audioDevice->RecordingIsInitialized() == false); -#endif - - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - TEST(audioDevice->RecordingIsInitialized() == false); - } - - // =========== - // InitPlayout - // =========== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial state - TEST(audioDevice->PlayoutIsInitialized() == false); - - // ensure that device must be set before we can initialize - TEST(audioDevice->InitPlayout() == -1); - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - - // bulk tests - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == -1); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); - } - - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - // Sleep is needed for e.g. iPhone since we after stopping then starting may - // have a hangover time of a couple of ms before initialized. - AudioDeviceUtility::Sleep(50); - TEST(audioDevice->PlayoutIsInitialized() == true); - } - - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - } - } - } - - TEST(audioDevice->StopPlayout() == 0); - - // ============= - // InitRecording - // ============= - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial state - TEST(audioDevice->RecordingIsInitialized() == false); - - // ensure that device must be set before we can initialize - TEST(audioDevice->InitRecording() == -1); - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == true); - - // bulk tests - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == true); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == -1); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == false); - } - - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - AudioDeviceUtility::Sleep(50); - TEST(audioDevice->RecordingIsInitialized() == true); - } - - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == true); - } - } - } - - TEST(audioDevice->StopRecording() == 0); - - // ============ - // StartPlayout - // StopPlayout - // ============ - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - TEST(audioDevice->StartPlayout() == -1); - TEST(audioDevice->StopPlayout() == 0); - -#ifdef _WIN32 - // kDefaultCommunicationDevice - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->Playing() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - } -#endif - - // repeat test but for kDefaultDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->Playing() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->Playing() == false); - } - - // repeat test for all devices - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->Playing() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->Playing() == false); - } - } - - // ============== - // StartRecording - // StopRecording - // ============== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - - TEST(audioDevice->StartRecording() == -1); - TEST(audioDevice->StopRecording() == 0); - -#ifdef _WIN32 - // kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->Recording() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - } -#endif - - // repeat test but for kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->Recording() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->Recording() == false); - } - - // repeat test for all devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->Recording() == true); - TEST(audioDevice->RegisterAudioCallback(audioTransport) == 0); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->Recording() == false); - } - } - - int vol(0); - -#if defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - - // ================ - // SetWaveOutVolume - // GetWaveOutVolume - // ================ - - // NOTE 1: Windows Wave only! - // NOTE 2: It seems like the waveOutSetVolume API returns - // MMSYSERR_NOTSUPPORTED on some Vista machines! - - const WebRtc_UWord16 maxVol(0xFFFF); - WebRtc_UWord16 volL, volR; - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // make dummy test to see if this API is supported - WebRtc_Word32 works = audioDevice->SetWaveOutVolume(vol, vol); - WARNING(works == 0); - - if (works == 0) - { - // set volume without open playout device - for (vol = 0; vol <= maxVol; vol += (maxVol/5)) - { - TEST(audioDevice->SetWaveOutVolume(vol, vol) == 0); - TEST(audioDevice->WaveOutVolume(volL, volR) == 0); - TEST((volL==vol) && (volR==vol)); - } - - // repeat test but this time with an open (default) output device - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - for (vol = 0; vol <= maxVol; vol += (maxVol/5)) - { - TEST(audioDevice->SetWaveOutVolume(vol, vol) == 0); - TEST(audioDevice->WaveOutVolume(volL, volR) == 0); - TEST((volL==vol) && (volR==vol)); - } - - // as above but while playout is active - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->Playing() == true); - for (vol = 0; vol <= maxVol; vol += (maxVol/5)) - { - TEST(audioDevice->SetWaveOutVolume(vol, vol) == 0); - TEST(audioDevice->WaveOutVolume(volL, volR) == 0); - TEST((volL==vol) && (volR==vol)); - } - } - - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->Playing() == false); - -#endif // defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - // ================== - // SpeakerIsAvailable - // ================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->SpeakerIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - } - - // =========== - // InitSpeaker - // =========== - - // NOTE: we call Terminate followed by Init to ensure that any existing output mixer - // handle is set to NULL. The mixer handle is closed and reopend again for each call to - // SetPlayoutDevice. - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // kDefaultCommunicationDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - } - - // fail tests - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->InitSpeaker() == -1); - TEST(audioDevice->StopPlayout() == 0); - } - - // kDefaultDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - } - - // repeat test for all devices - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - } - } - - // ===================== - // MicrophoneIsAvailable - // ===================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->MicrophoneIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - } - - // ============== - // InitMicrophone - // ============== - - // NOTE: we call Terminate followed by Init to ensure that any existing input mixer - // handle is set to NULL. The mixer handle is closed and reopend again for each call to - // SetRecordingDevice. - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - } - - // fail tests - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->InitMicrophone() == -1); - TEST(audioDevice->StopRecording() == 0); - } - - // kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - } - - // repeat test for all devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - } - } - - // ======================== - // SpeakerVolumeIsAvailable - // ======================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->SpeakerIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - } - - // ================ - // SetSpeakerVolume - // SpeakerVolume - // MaxSpeakerVolume - // MinSpeakerVolume - // ================ - - WebRtc_UWord32 volume(0); - WebRtc_UWord32 maxVolume(0); - WebRtc_UWord32 minVolume(0); - WebRtc_UWord16 stepSize(0); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // fail tests - TEST(audioDevice->SetSpeakerVolume(0) == -1); // speaker must be initialized first - TEST(audioDevice->SpeakerVolume(&volume) == -1); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == -1); - TEST(audioDevice->MinSpeakerVolume(&minVolume) == -1); - TEST(audioDevice->SpeakerVolumeStepSize(&stepSize) == -1); - -#if defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - // test for warning (can e.g. happen on Vista with Wave API) - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SetSpeakerVolume(19001) == 0); - TEST(audioDevice->SpeakerVolume(&volume) == 0); - WARNING(volume == 19001); - } -#endif - -#ifdef _WIN32 - // use kDefaultCommunicationDevice and modify/retrieve the volume - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(audioDevice->MinSpeakerVolume(&minVolume) == 0); - TEST(audioDevice->SpeakerVolumeStepSize(&stepSize) == 0); - for (vol = minVolume; vol < (int)maxVolume; vol += 20*stepSize) - { - TEST(audioDevice->SetSpeakerVolume(vol) == 0); - TEST(audioDevice->SpeakerVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } -#endif - - // use kDefaultDevice and modify/retrieve the volume - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(audioDevice->MinSpeakerVolume(&minVolume) == 0); - TEST(audioDevice->SpeakerVolumeStepSize(&stepSize) == 0); - WebRtc_UWord32 step = (maxVolume - minVolume) / 10; - step = (step < stepSize ? stepSize : step); - for (vol = minVolume; vol <= maxVolume; vol += step) - { - TEST(audioDevice->SetSpeakerVolume(vol) == 0); - TEST(audioDevice->SpeakerVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } - - // use all (indexed) devices and modify/retrieve the volume - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(audioDevice->MinSpeakerVolume(&minVolume) == 0); - TEST(audioDevice->SpeakerVolumeStepSize(&stepSize) == 0); - WebRtc_UWord32 step = (maxVolume - minVolume) / 10; - step = (step < stepSize ? stepSize : step); - for (vol = minVolume; vol <= maxVolume; vol += step) - { - TEST(audioDevice->SetSpeakerVolume(vol) == 0); - TEST(audioDevice->SpeakerVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } - } - - // restore reasonable level - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(audioDevice->SetSpeakerVolume(maxVolume < 10 ? - maxVolume/3 : maxVolume/10) == 0); - } - - // ====== - // SetAGC - // AGC - // ====== - - // NOTE: The AGC API only enables/disables the AGC. To ensure that it will - // have an effect, use it in combination with MicrophoneVolumeIsAvailable. - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - TEST(audioDevice->AGC() == false); - - // set/get tests - TEST(audioDevice->SetAGC(true) == 0); - TEST(audioDevice->AGC() == true); - TEST(audioDevice->SetAGC(false) == 0); - TEST(audioDevice->AGC() == false); - - // =========================== - // MicrophoneVolumeIsAvailable - // =========================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->MicrophoneIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - } - - // =================== - // SetMicrophoneVolume - // MicrophoneVolume - // MaxMicrophoneVolume - // MinMicrophoneVolume - // =================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // fail tests - TEST(audioDevice->SetMicrophoneVolume(0) == -1); // must be initialized first - TEST(audioDevice->MicrophoneVolume(&volume) == -1); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == -1); - TEST(audioDevice->MinMicrophoneVolume(&minVolume) == -1); - TEST(audioDevice->MicrophoneVolumeStepSize(&stepSize) == -1); - -#if defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - // test for warning (can e.g. happen on Vista with Wave API) - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneVolume(19001) == 0); - TEST(audioDevice->MicrophoneVolume(&volume) == 0); - WARNING(volume == 19001); - } -#endif - -#ifdef _WIN32 - // initialize kDefaultCommunicationDevice and modify/retrieve the volume - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->MinMicrophoneVolume(&minVolume) == 0); - TEST(audioDevice->MicrophoneVolumeStepSize(&stepSize) == 0); - for (vol = minVolume; vol < (int)maxVolume; vol += 10*stepSize) - { - TEST(audioDevice->SetMicrophoneVolume(vol) == 0); - TEST(audioDevice->MicrophoneVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } -#endif - - // reinitialize kDefaultDevice and modify/retrieve the volume - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->MinMicrophoneVolume(&minVolume) == 0); - TEST(audioDevice->MicrophoneVolumeStepSize(&stepSize) == 0); - for (vol = minVolume; vol < (int) maxVolume; vol += 10 * stepSize) - { - TEST(audioDevice->SetMicrophoneVolume(vol) == 0); - TEST(audioDevice->MicrophoneVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } - - // use all (indexed) devices and modify/retrieve the volume - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->MinMicrophoneVolume(&minVolume) == 0); - TEST(audioDevice->MicrophoneVolumeStepSize(&stepSize) == 0); - for (vol = minVolume; vol < (int) maxVolume; vol += 20 * stepSize) - { - TEST(audioDevice->SetMicrophoneVolume(vol) == 0); - TEST(audioDevice->MicrophoneVolume(&volume) == 0); - TEST((volume == vol) || (volume == vol-1)); - } - } - } - - // restore reasonable level - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->SetMicrophoneVolume(maxVolume/10) == 0); - } - - // ====================== - // SpeakerMuteIsAvailable - // ====================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->SpeakerIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->PlayoutDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - TEST(audioDevice->SpeakerIsInitialized() == false); - } - - // ========================= - // MicrophoneMuteIsAvailable - // ========================= - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - // check for availability should not lead to initialization - #endif - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // check the kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - } - - // ========================== - // MicrophoneBoostIsAvailable - // ========================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - -#ifdef _WIN32 - // check the kDefaultCommunicationDevice - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - // check for availability should not lead to initialization - TEST(audioDevice->MicrophoneIsInitialized() == false); -#endif - - // check the kDefaultDevice - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // check all availiable devices - nDevices = audioDevice->RecordingDevices(); - for (i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - TEST(audioDevice->MicrophoneIsInitialized() == false); - } - - // ============== - // SetSpeakerMute - // SpeakerMute - // ============== - - bool enabled(false); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->SpeakerIsInitialized() == false); - - // fail tests - TEST(audioDevice->SetSpeakerMute(true) == -1); // requires initialization - TEST(audioDevice->SpeakerMute(&enabled) == -1); - -#ifdef _WIN32 - // initialize kDefaultCommunicationDevice and modify/retrieve the mute state - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SetSpeakerMute(true) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetSpeakerMute(false) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == false); - } -#endif - - // reinitialize kDefaultDevice and modify/retrieve the mute state - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SetSpeakerMute(true) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetSpeakerMute(false) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == false); - } - - // reinitialize the default device (0) and modify/retrieve the mute state - TEST(audioDevice->SetPlayoutDevice(0) == 0); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SetSpeakerMute(true) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetSpeakerMute(false) == 0); - TEST(audioDevice->SpeakerMute(&enabled) == 0); - TEST(enabled == false); - } - - // ================== - // SetMicrophoneMute - // MicrophoneMute - // ================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // fail tests - TEST(audioDevice->SetMicrophoneMute(true) == -1); // requires initialization - TEST(audioDevice->MicrophoneMute(&enabled) == -1); - -#ifdef _WIN32 - // initialize kDefaultCommunicationDevice and modify/retrieve the mute - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneMute(true) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneMute(false) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == false); - } -#endif - - // reinitialize kDefaultDevice and modify/retrieve the mute - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneMute(true) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneMute(false) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == false); - } - - // reinitialize the default device (0) and modify/retrieve the Mute - TEST(audioDevice->SetRecordingDevice(0) == 0); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneMute(true) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneMute(false) == 0); - TEST(audioDevice->MicrophoneMute(&enabled) == 0); - TEST(enabled == false); - } - - // ================== - // SetMicrophoneBoost - // MicrophoneBoost - // ================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - TEST(audioDevice->MicrophoneIsInitialized() == false); - - // fail tests - TEST(audioDevice->SetMicrophoneBoost(true) == -1); // requires initialization - TEST(audioDevice->MicrophoneBoost(&enabled) == -1); - -#ifdef _WIN32 - // initialize kDefaultCommunicationDevice and modify/retrieve the boost - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneBoost(true) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneBoost(false) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == false); - } -#endif - - // reinitialize kDefaultDevice and modify/retrieve the boost - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneBoost(true) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneBoost(false) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == false); - } - - // reinitialize the default device (0) and modify/retrieve the boost - TEST(audioDevice->SetRecordingDevice(0) == 0); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->SetMicrophoneBoost(true) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetMicrophoneBoost(false) == 0); - TEST(audioDevice->MicrophoneBoost(&enabled) == 0); - TEST(enabled == false); - } - - // ================ - // SetStereoPlayout - // StereoPlayout - // ================ - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // fail tests - TEST(audioDevice->InitPlayout() == -1); - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - // must be performed before initialization - TEST(audioDevice->SetStereoPlayout(true) == -1); - - // ensure that we can set the stereo mode for playout - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == false); - - // initialize kDefaultCommunicationDevice and modify/retrieve stereo support - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoPlayout(false) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == false); - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - } - - // initialize kDefaultDevice and modify/retrieve stereo support - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoPlayout(false) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == false); - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - } - - // initialize default device (0) and modify/retrieve stereo support - TEST(audioDevice->SetPlayoutDevice(0) == 0); - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoPlayout(false) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == false); - TEST(audioDevice->SetStereoPlayout(true) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - TEST(enabled == true); - } - - // ================== - // SetStereoRecording - // StereoRecording - // ================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // fail tests - TEST(audioDevice->InitRecording() == -1); - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == true); - // must be performed before initialization - TEST(audioDevice->SetStereoRecording(true) == -1); - - // ensures that we can set the stereo mode for recording - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->RecordingIsInitialized() == false); - - // initialize kDefaultCommunicationDevice and modify/retrieve stereo support - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoRecording(false) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == false); - } - - // initialize kDefaultDevice and modify/retrieve stereo support - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoRecording(false) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == false); - } - - // initialize default device (0) and modify/retrieve stereo support - TEST(audioDevice->SetRecordingDevice(0) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == true); - TEST(audioDevice->SetStereoRecording(false) == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - TEST(enabled == false); - } - - // =================== - // SetRecordingChannel - // RecordingChannel - // ================== - - // the user in Win Core Audio - - AudioDeviceModule::ChannelType channelType(AudioDeviceModule::kChannelBoth); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // fail tests - TEST(audioDevice->SetStereoRecording(false) == 0); - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelBoth) == -1); - - // initialize kDefaultCommunicationDevice and modify/retrieve stereo support - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelBoth) == 0); - TEST(audioDevice->RecordingChannel(&channelType) == 0); - TEST(channelType == AudioDeviceModule::kChannelBoth); - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelLeft) == 0); - TEST(audioDevice->RecordingChannel(&channelType) == 0); - TEST(channelType == AudioDeviceModule::kChannelLeft); - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelRight) == 0); - TEST(audioDevice->RecordingChannel(&channelType) == 0); - TEST(channelType == AudioDeviceModule::kChannelRight); - } - - // ================ - // SetPlayoutBuffer - // PlayoutBuffer - // ================ - - AudioDeviceModule::BufferType bufferType; - WebRtc_UWord16 sizeMS(0); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); -#if defined(_WIN32) || defined(ANDROID) || defined(MAC_IPHONE) - TEST(bufferType == AudioDeviceModule::kAdaptiveBufferSize); -#else - TEST(bufferType == AudioDeviceModule::kFixedBufferSize); -#endif - - // fail tests - TEST(audioDevice->InitPlayout() == -1); // must set device first - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutIsInitialized() == true); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kAdaptiveBufferSize, - 100) == -1); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - kAdmMinPlayoutBufferSizeMs-1) == -1); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - kAdmMaxPlayoutBufferSizeMs+1) == -1); - - // bulk tests (all should be successful) - TEST(audioDevice->PlayoutIsInitialized() == false); -#ifdef _WIN32 - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kAdaptiveBufferSize, - 0) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); - TEST(bufferType == AudioDeviceModule::kAdaptiveBufferSize); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kAdaptiveBufferSize, - 10000) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); - TEST(bufferType == AudioDeviceModule::kAdaptiveBufferSize); -#endif -#if defined(ANDROID) || defined(MAC_IPHONE) - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - kAdmMinPlayoutBufferSizeMs) == -1); -#else - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - kAdmMinPlayoutBufferSizeMs) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); - TEST(bufferType == AudioDeviceModule::kFixedBufferSize); - TEST(sizeMS == kAdmMinPlayoutBufferSizeMs); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - kAdmMaxPlayoutBufferSizeMs) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); - TEST(bufferType == AudioDeviceModule::kFixedBufferSize); - TEST(sizeMS == kAdmMaxPlayoutBufferSizeMs); - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kFixedBufferSize, - 100) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); - TEST(bufferType == AudioDeviceModule::kFixedBufferSize); - TEST(sizeMS == 100); -#endif - -#ifdef _WIN32 - // restore default - TEST(audioDevice->SetPlayoutBuffer(AudioDeviceModule::kAdaptiveBufferSize, - 0) == 0); - TEST(audioDevice->PlayoutBuffer(&bufferType, &sizeMS) == 0); -#endif - - // ============ - // PlayoutDelay - // ============ - - // NOTE: this API is better tested in a functional test - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // bulk tests - TEST(audioDevice->PlayoutDelay(&sizeMS) == 0); - TEST(audioDevice->PlayoutDelay(&sizeMS) == 0); - - // ============== - // RecordingDelay - // ============== - - // NOTE: this API is better tested in a functional test - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - - // bulk tests - TEST(audioDevice->RecordingDelay(&sizeMS) == 0); - TEST(audioDevice->RecordingDelay(&sizeMS) == 0); - - // ======= - // CPULoad - // ======= - - // NOTE: this API is better tested in a functional test - - WebRtc_UWord16 load(0); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - - // bulk tests -#ifdef _WIN32 - TEST(audioDevice->CPULoad(&load) == 0); - TEST(load == 0); -#else - TEST(audioDevice->CPULoad(&load) == -1); -#endif - - // =========================== - // StartRawOutputFileRecording - // StopRawOutputFileRecording - // =========================== - - // NOTE: this API is better tested in a functional test - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // fail tests - TEST(audioDevice->StartRawOutputFileRecording(NULL) == -1); - - // bulk tests - TEST(audioDevice->StartRawOutputFileRecording( - GetFilename("raw_output_not_playing.pcm")) == 0); - TEST(audioDevice->StopRawOutputFileRecording() == 0); - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_COMMUNICATION_DEVICE) == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->StartRawOutputFileRecording( - GetFilename("raw_output_playing.pcm")) == 0); - AudioDeviceUtility::Sleep(100); - TEST(audioDevice->StopRawOutputFileRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->StartRawOutputFileRecording( - GetFilename("raw_output_not_playing.pcm")) == 0); - TEST(audioDevice->StopRawOutputFileRecording() == 0); - - // results after this test: - // - // - size of raw_output_not_playing.pcm shall be 0 - // - size of raw_output_playing.pcm shall be > 0 - - // ========================== - // StartRawInputFileRecording - // StopRawInputFileRecording - // ========================== - - // NOTE: this API is better tested in a functional test - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - // fail tests - TEST(audioDevice->StartRawInputFileRecording(NULL) == -1); - - // bulk tests - TEST(audioDevice->StartRawInputFileRecording( - GetFilename("raw_input_not_recording.pcm")) == 0); - TEST(audioDevice->StopRawInputFileRecording() == 0); - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->StartRawInputFileRecording( - GetFilename("raw_input_recording.pcm")) == 0); - AudioDeviceUtility::Sleep(100); - TEST(audioDevice->StopRawInputFileRecording() == 0); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StartRawInputFileRecording( - GetFilename("raw_input_not_recording.pcm")) == 0); - TEST(audioDevice->StopRawInputFileRecording() == 0); - - // results after this test: - // - // - size of raw_input_not_recording.pcm shall be 0 - // - size of raw_input_not_recording.pcm shall be > 0 - - // =================== - // RecordingSampleRate - // =================== - - WebRtc_UWord32 sampleRate(0); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - - // bulk tests - TEST(audioDevice->RecordingSampleRate(&sampleRate) == 0); -#if defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - TEST(sampleRate == 48000); -#elif defined(ANDROID) - TEST_LOG("Recording sample rate is %u\n\n", sampleRate); - TEST((sampleRate == 44000) || (sampleRate == 16000)); -#elif defined(MAC_IPHONE) - TEST_LOG("Recording sample rate is %u\n\n", sampleRate); - TEST((sampleRate == 44000) || (sampleRate == 16000) || (sampleRate == 8000)); -#endif - - // @TODO(xians) - add tests for all platforms here... - - // ================= - // PlayoutSampleRate - // ================= - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - - // bulk tests - TEST(audioDevice->PlayoutSampleRate(&sampleRate) == 0); -#if defined(_WIN32) && !defined(WEBRTC_WINDOWS_CORE_AUDIO_BUILD) - TEST(sampleRate == 48000); -#elif defined(ANDROID) - TEST_LOG("Playout sample rate is %u\n\n", sampleRate); - TEST((sampleRate == 44000) || (sampleRate == 16000)); -#elif defined(MAC_IPHONE) - TEST_LOG("Playout sample rate is %u\n\n", sampleRate); - TEST((sampleRate == 44000) || (sampleRate == 16000) || (sampleRate == 8000)); -#endif - - // ========================== - // ResetAudioDevice - // ========================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - TEST(audioDevice->RecordingIsInitialized() == false); - TEST(audioDevice->Recording() == false); - - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - TEST(audioDevice->SetRecordingDevice(MACRO_DEFAULT_DEVICE) == 0); - -#if defined(MAC_IPHONE) - // Not playing or recording, should just return 0 - TEST(audioDevice->ResetAudioDevice() == 0); - - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - for (int l=0; l<20; ++l) - { - TEST_LOG("Resetting sound device several time with pause %d ms\n", l); - TEST(audioDevice->ResetAudioDevice() == 0); - AudioDeviceUtility::Sleep(l); - } -#else - // Fail tests - TEST(audioDevice->ResetAudioDevice() == -1); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->ResetAudioDevice() == -1); -#endif - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - - // ========================== - // SetPlayoutSpeaker - // ========================== - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Init() == 0); - - // check initial states - TEST(audioDevice->Initialized() == true); - TEST(audioDevice->PlayoutIsInitialized() == false); - TEST(audioDevice->Playing() == false); - - TEST(audioDevice->SetPlayoutDevice(MACRO_DEFAULT_DEVICE) == 0); - - bool loudspeakerOn(false); -#if defined(MAC_IPHONE) - // Not playing or recording, should just return a success - TEST(audioDevice->SetLoudspeakerStatus(true) == 0); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == true); - TEST(audioDevice->SetLoudspeakerStatus(false) == 0); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == false); - - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->SetLoudspeakerStatus(true) == 0); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == true); - TEST(audioDevice->SetLoudspeakerStatus(false) == 0); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == false); - -#else - // Fail tests - TEST(audioDevice->SetLoudspeakerStatus(true) == -1); - TEST(audioDevice->SetLoudspeakerStatus(false) == -1); - TEST(audioDevice->SetLoudspeakerStatus(true) == -1); - TEST(audioDevice->SetLoudspeakerStatus(false) == -1); - - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - TEST(audioDevice->GetLoudspeakerStatus(&loudspeakerOn) == -1); -#endif - TEST(audioDevice->StopPlayout() == 0); - - Exit: - - // ------------------------------------------------------------------------ - // Terminate the module when all tests are done: - // - TEST(audioDevice->Terminate() == 0); - // ------------------------------------------------------------------------ - - // =================================================== - // AudioDeviceModule::Destroy - // =================================================== - - - // release the ProcessThread object - if (processThread) - { - processThread->DeRegisterModule(audioDevice); - processThread->Stop(); - ProcessThread::DestroyProcessThread(processThread); - } - - // delete the event observer - if (eventObserver) - { - delete eventObserver; - eventObserver = NULL; - } - - // delete the audio transport - if (audioTransport) - { - delete audioTransport; - audioTransport = NULL; - } - - // release the AudioDeviceModule object - if (audioDevice) - AudioDeviceModule::Destroy(audioDevice); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} diff --git a/modules/audio_device/main/test/audio_device_test_defines.h b/modules/audio_device/main/test/audio_device_test_defines.h deleted file mode 100644 index 6ed26059f..000000000 --- a/modules/audio_device/main/test/audio_device_test_defines.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_TEST_DEFINES_H -#define WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_TEST_DEFINES_H - -#include "common_types.h" -#include "audio_device.h" -#include "process_thread.h" -#include "trace.h" - -#ifdef _WIN32 -#define MACRO_DEFAULT_DEVICE AudioDeviceModule::kDefaultDevice -#define MACRO_DEFAULT_COMMUNICATION_DEVICE AudioDeviceModule::kDefaultCommunicationDevice -#else -#define MACRO_DEFAULT_DEVICE 0 -#define MACRO_DEFAULT_COMMUNICATION_DEVICE 0 -#endif - -#ifdef ANDROID -#include -#define LOG_TAG "WebRtc ADM TEST" -#define TEST_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#define TEST_LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#else -#define TEST_LOG printf -#define TEST_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__) -#endif - -static int testCount = 0; -static int errorCount = 0; -static int warningCount = 0; - -#define RESET_TEST \ - do { \ - testCount = 0; \ - errorCount = 0; \ - warningCount = 0; \ - } while(0) \ - -#define PRINT_ERR_MSG(msg) \ - do { \ - TEST_LOG_ERROR("Error at line %i of %s\n%s", \ - __LINE__, __FILE__, msg); \ - } while(0) - -#define WARNING(expr) \ - do { \ - testCount++; \ - if (!(expr)) { \ - TEST_LOG_ERROR("WARNING #%d: at line %i\n\n", \ - warningCount+1, __LINE__); \ - warningCount++; \ - } \ - } while(0) - - -#define TEST(expr) \ - do { \ - testCount++; \ - if (!(expr)) { \ - PRINT_ERR_MSG("Assertion failed: " #expr "\n\n"); \ - errorCount++; \ - } \ - } while(0) - -#define TEST_ERR(expr, err) \ - do { \ - testCount++; \ - if (!(expr)) { \ - PRINT_ERR_MSG("Assertion failed: " #expr "\n\n"); \ - errorCount++; \ - } \ - if (audioDevice->LastError() != err) { \ - PRINT_ERR_MSG("Assertion failed: " #err "\n\n"); \ - errorCount++; \ - } \ - } while(0) - - -#define PRINT_TEST_RESULTS \ - do { \ - TEST_LOG("\n>> %i tests completed <<\n", testCount); \ - if (errorCount > 0) { \ - TEST_LOG(">> %i FAILED! <<\n\n", errorCount); \ - } \ - else if (warningCount > 0) \ - { \ - TEST_LOG(">> ALL PASSED (with %d warnings) <<\n\n", \ - warningCount); \ - } \ - else \ - { \ - TEST_LOG(">> ALL PASSED <<\n\n"); \ - } \ - } while(0) - - -// Helper functions -// For iPhone, they are defined in iPhone specific test code. -// For Android, they are defined in API test only (since both -// API and Func tests are built into the same lib). -// For other, they are defined in both API test and Func test. -char* GetFilename(char* filename); -const char* GetFilename(const char* filename); -char* GetResource(char* resource); -const char* GetResource(const char* resource); - -#endif // WEBRTC_AUDIO_DEVICE_AUDIO_DEVICE_TEST_DEFINES_H - diff --git a/modules/audio_device/main/test/audio_device_test_func.cc b/modules/audio_device/main/test/audio_device_test_func.cc deleted file mode 100644 index d8b3582d3..000000000 --- a/modules/audio_device/main/test/audio_device_test_func.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include "audio_device_test_defines.h" -#include "func_test_manager.h" - -// Disable warning message 4996 ('scanf': This function or variable may be unsafe) -#pragma warning( disable : 4996 ) - -using namespace webrtc; - -int func_test(int); - -// ---------------------------------------------------------------------------- -// main() -// ---------------------------------------------------------------------------- - -#if !defined(MAC_IPHONE) && !defined(ANDROID) -int main(int /*argc*/, char* /*argv*/[]) -{ - func_test(0); -} -#endif - -// ---------------------------------------------------------------------------- -// func_test() -// ---------------------------------------------------------------------------- - -int func_test(int sel) -{ - TEST_LOG("=========================================\n"); - TEST_LOG("Func Test of the WebRtcAudioDevice Module\n"); - TEST_LOG("=========================================\n\n"); - - FuncTestManager funcMgr; - - funcMgr.Init(); - - bool quit(false); - - while (!quit) - { - TEST_LOG("---------------------------------------\n"); - TEST_LOG("Select type of test\n\n"); - TEST_LOG(" (0) Quit\n"); - TEST_LOG(" (1) All\n"); - TEST_LOG("- - - - - - - - - - - - - - - - - - - -\n"); - TEST_LOG(" (2) Audio-layer selection\n"); - TEST_LOG(" (3) Device enumeration\n"); - TEST_LOG(" (4) Device selection\n"); - TEST_LOG(" (5) Audio transport\n"); - TEST_LOG(" (6) Speaker volume\n"); - TEST_LOG(" (7) Microphone volume\n"); - TEST_LOG(" (8) Speaker mute\n"); - TEST_LOG(" (9) Microphone mute\n"); - TEST_LOG(" (10) Microphone boost\n"); - TEST_LOG(" (11) Microphone AGC\n"); - TEST_LOG(" (12) Loopback measurements\n"); - TEST_LOG(" (13) Device removal\n"); - TEST_LOG(" (14) Advanced mobile device API\n"); - TEST_LOG(" (66) XTEST\n"); - TEST_LOG("- - - - - - - - - - - - - - - - - - - -\n"); - TEST_LOG("\n: "); - - int dummy(0); - int selection(0); - enum TestType testType(TTInvalid); - -SHOW_MENU: - - if (sel > 0) - { - selection = sel; - } - else - { - dummy = scanf("%d", &selection); - } - - switch (selection) - { - case 0: - quit = true; - break; - case 1: - testType = TTAll; - break; - case 2: - testType = TTAudioLayerSelection; - break; - case 3: - testType = TTDeviceEnumeration; - break; - case 4: - testType = TTDeviceSelection; - break; - case 5: - testType = TTAudioTransport; - break; - case 6: - testType = TTSpeakerVolume; - break; - case 7: - testType = TTMicrophoneVolume; - break; - case 8: - testType = TTSpeakerMute; - break; - case 9: - testType = TTMicrophoneMute; - break; - case 10: - testType = TTMicrophoneBoost; - break; - case 11: - testType = TTMicrophoneAGC; - break; - case 12: - testType = TTLoopback; - break; - case 13: - testType = TTDeviceRemoval; - break; - case 14: - testType = TTMobileAPI; - break; - case 66: - testType = TTTest; - break; - default: - testType = TTInvalid; - TEST_LOG(": "); - goto SHOW_MENU; - break; - } - - WebRtc_Word32 ret = funcMgr.DoTest(testType); - - if (sel > 0) - { - quit = true; - } - } - - funcMgr.Close(); - - return 0; -} diff --git a/modules/audio_device/main/test/audio_short16.pcm b/modules/audio_device/main/test/audio_short16.pcm deleted file mode 100644 index 15a0f1811c7f327a4bac02c45b73502ee8c370dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 643632 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%0AITx~SAu2%d$JAxCnOL1zMQ%P$K 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ɸZMOXk0fu^emnHX=38 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

G{3k4H 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 D+0ME4Y1RqMSo)>e?&xa@b9?Z8ahs=R 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

}hd$}&z+-BLld7lmmy5d& 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

;4Epp>vyBeRS;qN? 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®LpGsuYmd!_4 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 D6|9^u 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

q3 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

Cooq}WP7 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%0A@RZi#%d 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

yK%Ab;jm5|5`E7Q|{I_MlzxJm?=}4PNCq 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�QJD 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 z

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%0A07`Yp5QXx_n3M`D-05Q}D_t}mG8!At0h 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 ()y?EOn+{j?HK1? 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

$o(RdmNS=LEZC-FcMs|y?RUfJ!1;xj~ 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

_9aIC;;HJJ& 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%0A+d9gbN{6>R!n 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

HOQ=XD}WjPGI*V< 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%0D_~zQV)Zk4y^eCz2>>Bfsq=|9hGhE3&@} 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

=kc>9btSo)IO6!ic=S)65!kfB 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�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

#z12n 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̗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νHn&khtBEj=Um~O?QH6FI!`Grl#l4LJ&mt&HIG)az=3v>z?4$yC*pztvOK8cLryp+u z=wLfNYIEpM3uVe<8c{_(^a6EwYw=1e>4Q2%AND=I_6fZKjn`jTq-Y}Q_3%-3sB*BU zPPOOU8TgQG_?iAh3PV8REAfGLb=4)T$1CpmDSq)BUs+C^QJ^%CoHf-1q8R1l7(t9c@7CFgm@N$^-5@qzbX zNQ#3Y2J;l=_#=*aemh+P&-I*)_)5@JRyzJhqsM$kZ-_?GT9jYYvuB5(Z8%47Z*(ws zTUmtVVz4}4ak?70TYE|eXsE@1349rNC~#C@rohPok)9ZLoa?*uin382EwvL%3UKb6 zjkV0={ldvAfCrTDe)F*ZWl)n^M<4e+sV)`RZ-~~Sp?OzR_4(HO+M+~z&%4yy$J>;y^4ciUzV!C@rRStA z(}r;Et;|rXEOls)g#~o89+7g$-B2O#hw6qT?~^Jq(=s_H`2ZGiu=N|X5XcFeL`UW( zs)?GB`>YMJ*hmat1h!^A@7_wE>>%Y5HX)@m*eN+bD|b-#7===qMooEFIUR_0A$lGk z>HnyMZoom%Q&Qm)HrPY&Pbup%_VF}6^}OCh|4Bz=DEFhPf7B${iDjBk>xZI950rn# z3}B`D`z)!cMozSH}IlXt+3 z|FTw5|9%m~y;h7DD=~|84caM}q%+bf)U1!=ZMRDsr9~*^*?J1A`Tu=%GaTXH4uQ$X z(?eODs`#Vyr{otO;;k~lhz??ZzhDowhkXbEjlLu1{Y>3-W}?e3L~yIXUVAwm8~xUx zBcA9F9NlQ{SwO+nRwuRSTHCE}LIG_I-_}NpYrk&1Wb3P|O=be0h(kCl_|V zJNthRmgyn6vn-sGneYyuS%1G4EQ=E?DKfoH9XfQWH?^ovqDi@xr$CietPVV>Elp+ z%&w=@ljuQuay<-tZE@y8PyV-DMed8z{X?pQ7hoM1a}H#7hQyz9Koxz7iF(6oPC`~D zf}X)oQc^TTYs;17Jg_(4=_EYG&fLm=-7f9o-!IW`bBp;0>(QdhC%r?D#3N1=o`SVH zTB}i}{7u(ZHEpDN)|c70+FQz-m>89KCGlrs74JcBdDH;ss8#4?KBxDhc5tB?jDI*m zWStJLkqhhn4}N3|exo8h+gsvdyL)pCsNSya-3scLVS>ltNq*qHx)Ey_PdpNT@?_)z8%4qb9N=tLus}+SM z*t<5^<9@8har|aml;gV4m9_+*`;z=n2{6)hFwkyJ$12e5B=A&T`17lD;Em$+RH7#| z1!!HOay*5RhMt=sl*hB+Z>!Q>-U%Dpg_k`?b_UuI`|;xsjbM=4MoyZoF;*P}$^=MZ z2;A2UNtb$oBitx2)>0ZM<&~_8fnIk6YU#&e#eOrL3d`}wTCb4E4d|Xy6=pUli z4|uWX;Ps7on_lErB(J11;UwRD1;H6p;X zobqz)>n3CxN*l9Kn#`-+M#pFm7S^j3Hs+ZP9TDPT`Kj}}`(wb)pyVN0k~~XNC+X0n z6_RdAayKM(NVVX!L6HF~J)PZg&PhsM`KP!?=;x?pH9={iD4K_-h>%Kh@?RM(!OSJG zQ6q^;L0iRGubD-_}NvbMOl~>CPP@4XN+Td#O0t%*))?Ra# z(MxZwwNiWemUurUh9!PVScyfLp0F+9bwatsor%T0(cT_tm+e-Iqx)5WS=^T10Q+(p zb*9Y3#`D2`Ibcj8VO>``QgCh(gcjm1@f(^871>=ih*@8WTm2n$XW;N8;S^kFSuz8^ zU=3yXgwb4SXF*1bp(p*zU*%0ob>}^2UDqbpRdhJ_y863PyVl~JM=_6}lspI3m~inD z*{=O?bWyC6$2v$qwh3mcM{i>kv)~J$JG4q`qovYbaNU#9(rCeJn7Tua;Vx!^gLJry!`|h zeWCfWVQJ){XpR3wFR7H$NNKE8RthNDlvGL}Goj+;5Aq%PKa}#PgHkG^#c>|JM^Rb| zP8m(jeIvj2;CM3IgHYRCsaMn`^h%GTD85$P$;6>MTx%Ls@xqvXK9nBT4xE~ddMvsq zGl@o$p-;YFouIZ;TdJ+pZt4v6r0P{GYIAsBetO66G1H|fKJ5#5we}UOtdx^0O9n`64Hn}&Goh#JHSv?J z^hQ{VN2npzp!2vQ(NAS&6kJ9b}F=}wN9vGv;a7T9CTNF`hTC0b- zk9f(gCu62eE#FLUOk&H#mkFa2dL}GQc*TCbo4C$9*4JJgq@B@Inmw6Zdr|l*#!DaN zZAv3l8+B(AS2|Sa(z@cDSDgo)dziQ|+Bwy^f#|BYvx+kt@l_O)Bt9|C-~p3CMu~Sp zud}IIDPWyqm$}Rv)E?}kFFGkbS?|q`Sjg_wbxfz*Z88(568$=aC+T+kMy{QnOY5Pj zVa+G$H&K^grWRIj`)2sM`WpMnF^?w$9<6|{q_2ptD6dNB%n$eN_I>u{S4X26)(;D} z2Aw+rv@whB$?_=BJ%D3MFYkx99jokBPAkWixk@$V8#Blx^w=%txR(*%llPxZ9$^^0 z9h;cbphV8)Gj;}c9F`HNm}Mmay; zI#7NIpOaBqBg$e;f$nwe#RcOrd$gQ3S^e%C2wH#dec*lU)x0%*tI@|FtbS2@qAt=5 zb)LNRh`wOw^@Z6z52K-jfzyLDtH4DM#jYPC^WkOAU<+{QBJyEpU{VjjP%nhDZ$qVE zKDeSJROQ*Tl=e~cRFCXjb!J?blTKpIj>+kiAw<1sT0m7pvXX3TierPz_MisU_9U z>U>VpPc<9!4W^!AexN1uq;_YBgvV?rD-4iuOC_*D%ZJS9ag}$6@XofMc;ij zcN>8p4j}WfkSwH=9AZycrpK^M0b)LsDF>iNHc%WU4gud55}(jl5iCS75iXUr7>?}~ zsuFpqwGBq+_n#Oi|8X94w+ToZG&HzG$dHgeA+OB5;!(shG(?98M7re!(|9^ z8Z_ru30LTT7|Zy+1v^a@JoRP0*0L33LR+@vD z-RJ~~2NS*5VUFMruK67GIu6TaW;E*RgS4IMZ(m1WuY^>Rp9}c}Z>fEM@|~1)GmViDsF8ok{X7u|}_rlHj%vWIP9f`)`0y0$`@| zvsZ%25!)SU>tG35!v+2$i?R{?l?6@guGoye`bVvc_E4>)uJ>i~9rH$b8+a>uTX~mw ze|ZP`QmEI|h3G?Pq=$LGIe_d;l%ppz@dn|?`oP6jlIO}VV2^q!tJ!nE6qhr-GaauW z=NILuGKL8#_n4aTgO2gnJh{EpfG!4oUII%rf#dE1fAdQ!t6v3-)$U64@}bcYhl_Kb-Y?Y z{pCC3Tf^&`FAd)9qdHnEf==^cV~|;jeBB&!;Nyg+L=ClJU+RjL>4r9`?m0*{EsQ*x zQ}{(6!9MaC)2PUZAa~FK3^9b$IF=fPJJfBZq04Il7Q`dAWi_*)BXwCSD9@5_$quCg z&!Ue~SIMFXN+fL5Q>l~mjUL|<@SmHhj+y|sF%<^pq|uD0zly18@%ZHGnor%PHc{P7 zv$*10;Tz{0>6=H~f86)lmql%_ZsINqXro~PLr~D0h+_FQn6a0rp~mQ!@enKd+7P`P z>J4LwH!p)l>XRFD5wD#=^JhLBc1Bp76KJa26Y0j&bM+B5u8N$s{bDrwY+2|U@5IxN zAR_KdcfTy1VaCogPUl!=$}&~ge1HnZD16>pbC=~tWBHzZ&N;!IC16G1wjfXNtDr$a zcLMJ-&nubxqw|;YPfjauB5uhg-So#}S5ac*<@#JhoV01b(_4dSDH}>l2ty)quDzKQa1erl{S;78G~1wq_8yrZ6U>OPP*oBnp17 z7qf`8OjsGn=eiIrf{Kd+UpqO(yDk&Y~Gn$?)pO z_1SQ$yZPCb$$hBCIC%H**zd07S#2$yx*vt!yM z(AH)Y%N(!=E8%3a!hP%}yO@?KZXJjx_mOw{NF-SXj%zCnQyI?GXELqJh3}}eFDIY# z10GmI@Ad-vUAe?a>a;A-Rt;FiuVh=!QRAGHGY`&i$kf|)rLt%PJt^zCx?bcbYLd^Z zO^xLM{Q7PbSkvJ@=EDSBU{60pMSK~SBN;PA^9ZY80;a=!)iuwf?eZL*yoTUK2}NOD zO98rUji%rLIFH_ZJr^9vJMDlrg{Z6puU`1N1E{uDWS!r#CbQ^D|4yE*7@3pQaQpX} z=+hc*C(wARUnJ_=0dM_EcZ1}Xq83vDJ~b5>`YSb(|8W=fs0)pulA|0GAs2I{zlaCg zfHxJ}#MsF)r zXd(?!TDvZ|zj$s2D1q|=qC6f?D)&QX-)xs1@=|bhxU@+U<(#1GOVV(3JFW{G9sR7d z<}rN~8l{(gseNO-$-s<16J6fw-mTsWK1uzpy7jikM>E{9OgM^OY%{R>CiEI7fYH}b zooge%V_@el)PFh1xjq8@+uHW=xpZPD+^kk|A@vHfUsJiTs@De7*SL}ET_kP6zoGm;Q_fa z^i-_UW)q{n-a?z8zV|Kg_46(Dd3_7jwpu%K4I|8U@Em(!EDDpa$|yX?a(f&bsBlUo zp6*Qb&P%N06qHI^pea=nk6HxOT@BowgT}inL-m2-y!xYsM^!G-h znS~0{;o?kbkeq?Jm2VVH`J!w?ZR$1ii!#Y~!Hr|EqCN2m<3N*(!IT}P4AK=mz$e(e zuRNoB5T}sE-NHo}bk`JFi&Ff)QU~ZH1+sW6;2;a#Grba`jDY5@8 zsx>~ar)$9n{Ifdqxw%-Iqnznr=Hg29LcFG8b{JW|H1PlDh@hHLqw|Z}&RKp&^&^@6 z$;@y4A4_Ke7RC3r;q3%OF;VPp|BBs(-G!}K7}$wocejGr-5uE7-C!#=NN&u|?tb@~ z-*?Tm?gF5} zO^I9PBjOTImxx(JFFL_@VR(r1k@c6sA$}?yJ*-^WhZ?$kL}f*kZVdSk-#mptFs|b< z%_JMB3$c`%_})79>6X^4tBRhEpigxjT^71jN8_7+#cL^vz8S1eLQ=jbvapDp=OirL zh9GV?@lqs55%lu_tfR{ArrzG_zS^n!4aS1z{FZN)x>m2HwxyZ5lPSzN*bu2t);$IL zKL-CiA#OcIcUhZ+RJ{vdXYss17rw%}KIWX|?CWgiY|s4B)6O(!3+b)2-?dWS;Qr$2 zrvd|}L#0y}rJt{_s^7poxxJcb?4fkLzfiJ@w-6_uNS#{-RdK7puI=<0d(EVo*HSiV zuCp=mgJaG{^dNM((&TUMpB@$Q-6G^=rfWaw?hr|AVN5XAG4(e!Gnq~Ijf0H$u;2#j z@94^qRn}Z9gRJz{$k0S9Dhd2m)Ju8_yM5{5oJ$IIUT}19)OD|Dg}(k{eisO4I=E`xYp575HE=Sp>h?zczG(cqp&NlP^ri;OVY{ z;8|;>8F(Bwq#{f-StLh+_&)M_RM|ED^jzBvuF#%&u?_TE=9SE22dn7{5tk@Mgk=_e zn$ObDa}K!3HFonT_FV|r<7cq;t7NkIkw<%!skPhC;wkhUnn=W}JrZmfS&~cDDy+hV zbXZ)E2jZt$jXgM?`<+WgL#_^w87eBIxvsI7cIz4=>YnQYnc%I zi!8S=wH*)d7IpvY=%2LP-4h&V6rEXXfxj=Jdtahzk-DH}h2}k|cRaXP58k&8jrfjr zZj5L54}|g#k-$Spi;2_@x5xU|f!u8)u2#bHk@#6xaMVl4iV0Yb{pbaDP0sBei={sf zi9Q1D>yMY#8cfPdoURAjb2=I?!M&NOAD!K;;J*Iu2>P@Sb@xJg>X^WET8@+(p;x^w zJ8)YLrVhu*YB=<*dl)?+=8~hlnM}M`Fs*@PMSRpqaG*hJ*Sw?8_GbEaj-}^R2sTGe zJi+XmbShq!An|V^@t=c0ZN&Bn!$0+rr_)ojEAwq8J6m&1XR6^B$>SRCuI#O%ZmDgc zuVl<^-es9&eQMSE+_Jv5tT#_Ituh`5kFKGw49$!JCD;UFT|-}2=c|3M9;@2pNt0t; ztC{!w-EqQ^1-#)RXhXX6giguz!MCb=Lsd~&y=9q?GZEZ>02wUD3$h{1BgkIDwmoxIrZe-gt+?ZcbAl_M`;BLUDqfvS z+kgq_wG1j_EYnJ#n1akr&E?H6OjS&MjFSvY^z*?l=4;+5)Nll!c|LpllnSvEphq9% zO|B?ul5>gUoqd5l#%^$&b)0khxYo-RiDg>V!E}W^t{Dsr}@1 z1*sp9)m4n#$xz}!?XhCIxxb_NO34?&qrKpRT_p*tbP2djjMM4dAuV$ql2!P^Nvf`z zJ=&YPb@Xy>0lx5$)%?y}u(RNr3G|?f(#(N(HbarSv2eQ6dv6E)A59c~oL9p434~u3 zK|9@{o)|2tj$|VBAyfE?YLmJz*3f-zYw~)&(iv{MuCi_|^8hWH`b0{9K*KukE70hB z;3gN*LTj+W&cS=vShdebA`~JhQ#+k#kSsz3Ox>tEh{?KdTKA z8A=>8Rksc+D@yy34j^&lS;k{!hGJ{BCp)XUqOW$MaW8;5oOE||XUJ!mu8=DC#G;$y zsYZp#ebr=oLoU#yXeyFFTSc26B>EaX@d{yG4J5ODI~6RCy;I=1k7SfiBXh9}mPZS; zegSCZ1Ko1E(_zp_XUq5arUmg=?C#m*yk4;a_2B!qbhW{gAB){`!Icam9nL-HAmcB|Jp}IC?hVEd=*ntT z!Ry<=oe#kda8nJ_1smfP{(-1gp9SJ{9K7iY9`IvgCHujgS|hLTp=X*P?T(OJXPFQxw31QG+bH%=Z$n}(FO)O^!{^C08w0u z96ha0)&%Qj>NglVF)<;V@qdQTx&qo_>e5~^M3}jAOnNPKa}|~|Y1wDV62Zp^o?+f=nOF0>%<@I=x!%BadngyISV-pI_EjtNmH3jIs+Rq zMlEZ~5JO+Czef~(mOiI`1-<*5Ygf~axVU;8*|}4u*p#Su4l)#8d3&kv3%pr>3*4v*p7sd+2_hH%+D4kK_?nxj z<}=Z=_5gWv&B40flfk!;ih!BusY~SU1@YIRFw-R zcW~LV_-A|zIX(?5CMj{bUiASzi`IEPK70j>%y{+34%AK!)+T4LEli*`7M`T8?7@)WCLK zNF8VkI=nB%Z(IaEI@0qQX=P&;OgZ@@vAd3BKK;b6xhcH>k)W)*XI^1W!PamJH)-jgC<|oE=hF#3I8HhF*K;GnVIt1#Nffk|(Q19_p^n8Y! zk6?+ubN+HR0Ozd>PqzU9ZS8isZ-V-~QZ>-r)3(uHG<-MCG%YphOwSB~`mEX;syUun z@;&Jgn0+bG?sigMW)&4elRS3ibrqB}&d+w8eT(g&t%|*-W4p7dD<>9cv?`xwv^G-L zQD4lEWH`tb>zaR=!z|1PxAYVBF|096j}d-oHpslG0w>lYEM z|G7pZ8LE;2Vdlz45Z{_ag%?p@bBEaCVIrZKbnLu{?^2&xTkoNt3uKb*$G7VX@_GoG z8{ws25K^bQyq3(Tbb8Krc0F^=mBZZ4$l*_7(q%iWt}0yNy2ee1@!Hx}NUf4uCsD{Q z8W%okFg1Cr$h!6ep%MM%4&epP0m&^6ubv@Wt}ob6S8#qiets3IP0p($LELU>>oRkA z4;p=mt`+iX1~X$1V zqucfn_X}ZiNF|WHGGM^($xJp;rzEl_Ut^U%h6CRrO|yY$A7h5^x8vVfQ5 zbyt>qU?m=PUUV28^X(z_e)c2wSzY>?VszuRkFapJP-An3%->iwb+Voqxsz1Pv7ZSe(V53HhuU7+Cplk9@vb}aOnlrt z$i8{r1l2W74ZYoXz>?SZhu@bh`?LJNNyYhojOn>sU7h8I?tY%L-cIV0+FW|0@vUiqWf4|L3!jJ9 zpOzT2k7G_S zCVK~ha`lksBjaP4C-^|>ihrHYUDeZtX;e#@N?sdlxhivMx9PT!v$qTGTA*9WF%KNE zJ96YYndf)#rWTPIAEX-MJ%=Yb*nL;7Dxap$f0A@YI>f|oEjFZvEHHXAda{uxR+_rX zMf4caGjZ$%xq@|=y>`+S;94&glj1?^=Q)QsJ2`6+pDp9e!%>OY*LpIJ>XLtw2m7-G z2xvFAisu~(DwwL;tL}z0nMDWn8srrZ1aC=W!k!OPy$zaK_y(`BJ3HWO79&gJ1-WV` zu$zP7Yd2cc;8{Vo!3cR56EZGg2^o>~Ki%;voo=Nu&g#iBIAC{+rg_HMNa@HEs2UO#3a@_lAqYpSA)@XnN0Wd^6#O}76vCj}-~OzD+=$F|Fv z;5y*`hF+IJLfc@c{PHAv_p0A&J^ByEsph4Y8AgT8lC#Kx=>9gRYy;(T+3Bcn(Zv%EavRzJnZz77Gc-yc2$=X znF#bEY^qNef+PWin8_PE~ValD+67J{n|p zs=lN?Ru@lhXI@QRbuZNd{IJg8zLEHgCES(Vz0n|*$XJ|7?&%jQ^*!ol;18?GU0Dh0 zyjSx^Q-Rq3Z*33V6-GKn0yK}77ccX2j*X5aM;>XMOxCc*&v@8c(*J$Hz?@HVIdh%J=?yUXU$GuH z9@XYoEpzMSs!a5$2Xc3n%z)gk|4DtE104hGX8RO-AIE9u3fE!x2k#;Eag8roTYEK! zv1VhaVt=Wgt-YczZt7sUXf5W`)tb{BqF=6J3c77+YHU*NKf`{@zfb+0_|KVk-KLjz z%8xw}szzjR>{S~;8K!8j>V_I>nr@pvSw>mASi>z>O-~F@b*aQnCR3~YojEJVH3ym9 z9j-f|ZK;{TY_6PgF6pGBs$-esh121>>(Oay>#LYfSr+lf;W>9gxs=@PXbrl`cJ`gX--g7cHZ z;E1*Fx9c5;9P^wp(tVeUEFq`o8M)Oy#MeYzptHItc;r2*zoS4TT9EVqp4_nNaOnc_ z>hEHYHo_x$N~Y3Gd9pm6N-Kvvk@*Pmp3~kfs&(q6nkclLnLL57`kaOwhCBLh`n&l5 z1CeIwWRJfm!XRo(t>jqNBG!Hi+GtHD)&RV_v&`S?q@7D%_cQVW67eNhlC4vZ_|RJB z*4?82)K8vW#^=>~r&HT7)h%JWmnD<_9Fq;NGeM~edDw1w6lha-JmQ}oe`4C-@rg@% zqKWvlV+xK_Y9*b(i{I;L>_}(g?=E=oojt2#nB$LQA~V(ErA4lG*qwIrL7G#SwU9{q zCbgF6_AgBtZAHdC{Pj2Ds^`C87iQkQQzuh<<5hhh?I=}( zyNVp)n(KNa7xV5{Yju0XELZ&}jnO+;nv?l9<@?`(zi<4C{~ekXn0hYrv^3n?PFvbg z-c;6H(LC35kgTvUYJ(b(7ZzZwZA>?K^gVPrHBsK*a*UMWDCvlFc$^*NK(DO6snZ)n zO@~c|O-@6oeya94QS>k($~V>FOl%!%Y;V41nP;76y=>`co@lJ0f20|X_E{@eaQRD( zoab5Zib%k3c7MloM`dS2_@c9$>BH(E?RMQMeMN(xp`BjVmek}@QLE$n?(FWUgKxRo z_R%)e9_nc443<7i9eC22ASxEuHR&jrP%Sc&9?Deoc(Q{Bs)Qwp)~TvnOmF-ool$Sr zf7V6mKH?$f)Fi16d#91LdJT+iH0ZU-GuB&LZPtF%tup*DCYbt|hnNlK&Zg1EQHBWp zQ1tOlaQmrLUq^trPa`A#9=PNbat6-g1J%b|)P)WJ)`Ac5-DJ zGl}6M^$qRGv8b$xRG*`|W`Oq>R6QQd=QLHDZOHIW1lJJV7RA(_z0hA0ooRl7q%A`` zMe5q?Q}y!=E<-6}KBLxn+0fOHKqmAKYE;^w8NO3_z7;EQJ&}$NSaZ9n;{N05M64+o zr1U*m0(L3|uMn{>s@h5Z+9ME7Km5xjAVP)RF7i`0lPOS}I<{zHsT$W&G9C&s9d|!A z2OQvO5*4rO1zK z3o;o@H>`?yR2T8z3Q#@zgGtz%nH=>G@~(F;b!~E{+c(>^w!O9;j>RrNZ*FZXquP4TcR&`c|LiO(e9STCI>teI zi*5i}i!F4e^w|ub4HJyRsZ8jr|D`Ra2~ySeTqoDEm@7kS?n-q%mXBf6P4;#H?^*;x zmqmM5J3?1l|3Dwa49YJ0%DRP`o~pZUgR7n6edhV}ptL%vxl)&>Zcl5H(cU(}F<**6 zL$o5NI+%!_lX??9b$eZ@T=z0L`U$b6UhYixvIq#(6fojVE(@`s5VsE&*GeYxZKS(- z2X;zJyr3h#cX$0ZgJcY*;!3jgvNpDEu`Dp>GYvPa(_I3yiSn4w%lQ$S6Hv}ZlDqT0!Ch~E!%g%BS&C$*K{ zJA09xIma=GDyk=rj!u6`2f7+W)=Ew|bsXsTJvu|wQysu745zz&8|<_>atFD*>?h|Z zUuy~T6&DiQtKt10I<`7_mp z3RC%50e|@*HCv)%=yuT4BXF0UU7zEr3HBdgaF|p5;sYk;`kJRn-rIrkxy%7!zE-{yKes=1zx6j`x&Q7tzim7psWAlzz2-E-2tDQy)tupC-ORz7Fek^8iCm zO}IPN(IwN7b|W=+TEmQ%_F__^9F8wC%iG4?ReEPDm!9%3SF$H*!M`qPjcw&z!K!|` zjz(YeF!KOYfFWH&HKbfbvN#^ume`Kk-q>>4N8l6vCYQgT?Yu44Ue)>5HQRelYd2pyq{$KfjS1`44cS(8)>L+NYU@5ZrMkF65K-~W4j-|%^7gao@BU` zrpC4n-kpf*-vWhwfqyqsQgXedKwsKBRACiOQnQ z5t8Rh-i&-*^ZDo5NcmGi1NlwP6B*so(o*}Ur)EYwKf0sUy>!J4seQ2=xN+$Ry8H*I#?r zjIya~|7HHmo;D-%mUFjfm^KIIQ3G>c(-!?p^%VC&r#*8*`j6CMsR3!d(r;uqGxOPX z)TH)wTyv~-UX`L`UvFd0Gkqy@iZ$6c!*8qKLm#&}#*nD7dLOzrILAA}9o?NFuD9-` zDzmn<{-?oh3^Rq8{7q+#-HnBfRgDXblCigGv+0Csmg%E0!LSue(@%SizD*T~gT~?c ziN8kZBO4Y~JoT{s@w4wr6QoK~rt>5S+)7?Mo!7~PD8lw;517l~ z|69mcIiu^M|E3Q$Tr`-Cm8cR)q$;GGVFTK#sqQGg{5VZA`P6}Ah)yEk+(kqyl6)x* z@tF-EE@JN75hW_}N?t(R!NxT3CD`Ux*Ar>8)Lkk{9>)c+oP*9d=Swi6DpII47pwiD zl)%)~?9{cj1NYy|%;Rq^f1ce-PW^SBeVVH~&%fD~K(uBaSaOKs`S?*4`CIKbTt zlwg9kx$(BOeAdS~jd|AvEzQ3x$S3egKu@0&x?1jIwu))>Qf{TpPXA{g>29mtV>;lI zHA@@+HU6<#Zu&O&Nw#Wye0=WsIDHTKmGK+wv%tJg|5|m^mCL>}JwA0`s&9G;+jQxT zCtOolzuWLXLyY#hH`Qfu^v%eT_9=CJ`Yc;dDc+Myn**$GpP_*+Of|`snRz4S;ot7R z+x>d>>-C?cZ5CITxKqA^;*-cHLanR;pP#{Ij?SPXpA&oH2#B!9vNGL!qw6n zbziAr{DG7ay=N0uGt{-fw(3*+cABWiORAvvQr)r#+a#QN?i<8pi>p^|+;MLu^^e4RR)Nb(Qnkn2;6ET3P@y^jZ>DxsN5MQL&F3Zo47+xDz8MBe4-3w{4(fHF~G)QFYX|!22CCHKW;@$M2e^4EzANON# z2|5e9rXyXSrD0SiI+$`>RT_n-*a+F3h55ew zsDsOe=TU*`%DU9MmBSmU%x^iUMw9VRE|KMXkgwn6{N%Fu(4B%R8Y<}P&@ZJt9JGy! z#0#1oWLXSmlC2-HxP3%$OM-H&q6<)V;z?7(1vMn1_DtMUGB)KL56Sfj$$TUR4j`}OApMcl#K2CH$5w_s{b_V{ zP`j^EcQcr}`8RNzk83%#1zDwoWXV3j-t8lum+Fxv@)$mv>k0y^>+XE!c<-3v^mn~< zuTl>+ytJ0cIx|Pd+;j6x%Y8BX4ZlUk$KIii@U)2JLP;}{%cUjTPkAQmms`8|Hwb8) z{boQ$*03x;eM(weT5RTtmL1lkJ|3TNp9_``#@4zCs${v66zw?a=q_2^GgYm1vy2PP zLoE-?gN?7XdR2tn#@W!GVr%7iE0y6ylIc|w;{iwxBiH(7FmK3V+}dKcGwF zk%+yu&8hePWT<6|G*7k^w2rd&qSC=;jyJ6V!=DV!Jzkwvb)TxDkJR!#A|vM@b=ULo zcJdLO^pdf7L){VlbranzSCa?T&imaxTy{zuoxL2L>@l`-wqKcFGmW;!wiC9>_Gk7e zM?GgwGGN|=SzL23@zeq{YK#pOtMt=$YNk2|wdA5tWnnshS@4}}5Ty+v ziu8djDFZd7Gszbpf(`YMoa=Yg9tmW)3o-psAWhZrr=nf2Tv_GrR4Sy&Wywz5NA_)L zdJ8S21JP6ZOKm6XsXBQRi{T$NcFkh?FD;d~z%~AOhZm`UTSBeGWO|h3qKEYk)eSm@ zhzz%$R5czWSNjt+=}qa$l1{&bAfhXW$c|b=Ci5U>zf@NDQBS8+$_DBHicy_C6l-8B z86#vy(%G#!*~J6tiFDfY&69`7bPck0+JH|qCi|rc-GHj&wRA*UbR{1&1dm2cbQnXl z;UziY-NUC;j3|lO+-{fpF zvRiYU49Mw!+0sz=LH4sR3&CYO)IJZeS(k(j=-Ddkc{<86XdJc_c~d@3Mg?ymcpgs!%N;Q@_1Pf8B7(*wu8> zbjdW@blrF#&wQn>kG6wmGCA;D(L_7I=IeuWd`9vQgVQb(FDU}O?bc>$TT*NAP!~@< z)hb;<5Vklh^}Xc1jP&#%Lc5)|U7MxZSVl=w7!%dw{RL6TM}1KuJ#N}l|^ouC;GV+R!68(ATZiuYU@MI_ZxGveHS?T9>k+@@V&lz;oMLNe^q_#V&>jRck z1~j^tTIYRo1-$%jo(G;PSY=zemjfV@q10}qdqh91`{aaWrIX`jItw(X|H?5c-1g!@ z4uR7Cke9TN3cX0Gz}J&6b&Rtf(dD-$H4H;Q>&MZ}&O^1d#XHHPL;9V=f6Mf=Q90Cp zx)z2)rdj52%RI|s^9^GqeQr%IZ+ZEI)5p=-9&5kn*eMy^%b?8wFx~rx5=PlD(Qrop zK$lAG(hQ`NjST(!=+cKwaehW^UNY54XVhD%O|a38;sVxXQ?mP-xK-ec7u<(DSIGNW zu938*^k&0+DwZ$k_vo5ySE=`tLDNkx=;}cYYblV%Bd*_a4Kh>qsCJX{_JSS&pYa6M znu+QJa?@Y(q%DyVQBabJ?$WlcVlR-1vX7K z`o|csL{8vm&L=DX8gYoOQ(J}S=rRGYA|CNnp21idca;U#P7nDLJM>;l|jeKJ_GlOZ~iEQzti zXX=nys3TY9Ht!Yn>V1hmG+|eJvR9|b#;*eXJ;%aqL|*$K{Ng&`)(-MFt~0IkxcVX< z&37{D(y0u*NSCSNN@CuQo3UJg;FzLKx+m0tIJWiP?qNP%+ zD{}p?G9|UNrV2KYg;@7yGHNTRUGzhEh6gQ!1%!Z*&LC<(4n5EqKRP#fX+HXWHRjAn z?7aD`NOL+8-NWuJPru(7s!j1g$tUPc{&9kP3mL49vFQS+9<^fC6(AS4G_mGN*n2hH zA!PiHp9z>I)>8JUOjtRNQdkP0h`-H@t zMV4PfI7vly+A!`>Oy+chr;Ueq@*$rZP>*z5Rf-(nn`)UH{!&z_rK|5!*|rc*Y5~c zyxE0(=3v&i0(HQ@skt~vG%AWH#U!eM=MX78Ky<>vgodVY)m%`|Bh-*wS6$_}LH@6p zkmMrsr4f8L6YjgHe#y>!2h)B{#PJX{xkKO&1D$ve@JWNPX8Ym;Mw7$%g9#%wnAX;h z=^_oWd-dvv?DBXr9P7~8q8Yq46dkacdyU4j31eN%NXupP7*}FHKLTf=ILSR7EwRx@aut2`P68#BwX|=t_L;HTcV5 zcBce5Wi@bu^+@#s)H8@awWo-PM6({Dy#GIZvm%r-fDBnNoANyWjUhL62pOV{;D1q} zKM_gznvStza=qxq+l}vHrl^{HxbH;tMAz{|qAb67*^wf-iE0kyNx!k*1F=`mlWnq} z>aJFFAifWu_NUshDm~U(vriL|GgF8o^`@($AN8Ui$##83-%l;lID|8Yf$KE~ZA(WI zPv<`KP?2(vYN}1--HpaS=uCZME4Xefz0=NmQarh_Zz7<^g&Ygup6h zkL<=e1|wlSoSR5~s){V<25|Q>p8GMKwM6IAAR@g*!Gx>Rk6p50W=#4ORXhrs@G^-nm?Ixz<)!;cM(S>CYa;ZK#GL24f z2k|9)ac7;e(%N%R?YPHbRNO?fKaZFqQiOA3=LL=_9HIEXeqY7O2zIUpdG&e8Se z>`b5EMaYsHtXBX{x#I(jsgZ6T$*YjM^tiu|c> z^Z+i+8ST&kqW5zX@~gTlGGY(U^8`715&5tfu5Uu`z5x1>XMwKTFoR$bx<^c^yNh&L z2}SmUGHVi%Z^Zj2AW_cKwc#bPzYl!B1QpCB&K3se_kzkNfuJ2ke!Ri+PE}=6DSs0Q z)t9`kcy_-nou5< zLkFm@K6mKG%UQ}QSA_Qcpxlz^)SjGqjD0X7<=SDHL?Kb8@~J|m+@ULS3(iY|=ijl8 z1)#&F*fl@swx0p7?t(wckO{Dl+^x3Eq9}|uZsh4iRqI$VZqd(kq@u;LQ%~q5d*F@x zH64$hu&+9D%d61)bvEyP#=a)=R5!8DmZJs4Z1~$$Ui5JfBroC|SrEhU&V8ZizhLIW zS+$!;@B-+C`BVe%M+d9X#w$UlvrsW#S37{LsD;`k+VS*d&%iQXM!i&H^maX}hTBm$ z+)`5kN-su*Vr}YqLdlB^Clb1e%I7C!`B=3T$gUqohD1+l-~6?2K_iCIIsOPX(g9y+=IqX%SBcA*wKAoR!w_H7Z; zqa|odHGHoBCN$q>J##~q{h)z8@X2#H=rYq$Mj{=5V+Rid6V%dy`xmra8i{(IKKP+@ zx;sn{cj5D8Vctf2>R5NN@*S{<+JbD%$LG7HaLrF5RJW;I6TNGT;t^h^wt6H!e{Q!O z|LipsMdHxJW(v_zor>-I-jg((G$4G9PnL85#(m1yhF;&i?M9HZ}`c zPb;XV4Pspu6YqM4cln)8ZC~hR^pxK|VQ=I{@Ag3h?ndi9Lj$IfEoRa9Q*#hRrbiLv zN07#f&6>e~esTO`D1275dR?LpJ*b@QrfGtt@uBwPG*(L+YOns`>+XP32SWEP;D8n! zjagw)9cdu5<^&qAJ^Iay2m1(VaTv^YA3eY~BS}`1{WS&}t;JJW={WY2h|CS@?xUHR zK8^alFnrjKL@nFUJ5juv;~fMOOV`q&^Z_|^>+xj|(Ua9prRF;79~)wcK4g-U5A`DX z*`t^AjM#>qHWRCK7^~PCojI4vpU1=q%VNJqgH?K{9H{_3bYfOaB*?^VxV=1*^OiaX zmgNR2q27UGy(Ib+O*Tvejh=1d+=$1PI*Rop=336g7n_AtTSC{a zeSE?LXy6$U;nQ?3?vIRepxx$UgS0@>_UEU=ND%`x-V(h~gT1>A*97BTUB=EBr*v@_ z9k`cZ4^P3~uLTAE!y`LM?aWqc&@Vx&I{GCxqKD}S&KLdnW}q)3v8$)>^L#YKDe~37 z;T>ebmZ^Z{-5otX8+p199uhMLP1uyRuoPoWL=TXPpPlZ=lL6jyXGz*~K`;YMcXSR^V9@sS(;sAIzmxe@(?^9FAlb zlPvy+g=Ql2E0Ip(ujv)|9@+N|8JOYG;B{Aqqk3}wdMy0oNan*>2s6=9Vy;vZG*vsU zvkJ|r!VaE@N|g z|7N+1&M*&@wJ)o#MQ4h(96jjxJC~kQZ|M442PE+T@;wjIU1VU6(2mrO(T>wjN77H^ z`(*wSM#qQp;46uAt~f+>`d+Zdmzq?KkD?P=X`5*)X|+^C9tJ%K*R(BedVD5OT}k0$}& zZaZ|pf~xL~{B#AK>%kvtK{YX*(&)8)7|Zo0TIM$Ue;@7h0;?&Fj5s3}NFMZi3Fxkl zqVESlp$o8^Pa-LAV6B``e1$nw9dt)hw^t-j1k^EunyG16Ho}@LMt8)A=$ctrw=MCq ztKlWIzz3K}O!+W7_7(KdMDCrCT*592fp2qzVSJ*F-+>k_PObi1YV*#)=a=Z*9*-sAKu4;m^7;i|J>c)rSQ$g8|Ez(1 zXeHO;D>Qi(8M2dF>@6I-=yY@1eFb~-AF=sT_&V*7HIYb<7_7|m>_YFSnH3wy%5`GZ+hhN=!Dke6wTEJpPC)03=a_;d zi{iZbNU~YH79!1-a%_ZFE+N(b(QUjG(y$ijK@in^U!bFvXoi;XLS8E5bCTOq0V}#8 z9%*yv?tf@sCu?(=nvkXVk^}H)I$^iAz%%KAH`7=7tqn4`J^$%P*OPAirx_Ncm1@JY z(EL=8kbcPWLHJ`6*|in;W=Gh8YjoYZiwE$>W5N3ANLE@5-oq>C>n}3=G+p1?B2#Vf z*I8^HvO(aoY}jCvkiX~AU++0D6Y6!NYear*7+Bpow4jmaD#uX*R3M2R+{+4#W|exP ztNJSw#ipaxcJtg~VylJeS?$T?oyx9`z%wt8uKdQT3p;-x?+QoW&ftiGDuy9{YlArF z!{XCpo#o`GQv6;W>rvExUBa4e4F>s|CmcZ!haz-UG0+!Da;rfDvmi@Dup35WMXZN{ zuVB4Bpt+dUx;|jjWzaUdAFSqEF(Z-{pA7_npvcGw`J}D0Cj0sS4Gb z61|MX6yrn=0~}wCwTi%Eh=IS)urI=Ux`94R0ImAM>p8Zh@PrmC-jbNqSx~_XUgO;+ z-~;&~lRBf}Hp7Jp95>*AEu0_CUQFOU%aCwk@IhJPhWF8{;dn6(kQ_y@z=QFH8e)(1 z$EF?zUCqWe7dXi{=(8?9p^?bo6}*oa;xe;Y`#xB7y}%>8V4I9ZLaYHlxaN7|k+A=A zf^qc2hZl2shoI?uBST9l+CGp^Yl&Rl1+^B(*4WN-`9MEy=$IDD>J33ww1?M+u%cpu z?oceX;e74L8C~GN;!H3S^M8G@w~TP20rVq)-cQ zZ$+znxHmoCUKX^Bo$ed)_;R=5s#s`iFn&T4x&{}bGpURgf2q*aRW$u6B1{jUrq{}} zpuEV=_Q+5%r9mK;;Y817al}BIXTe?%VO?)QVs_=|$$1mG`aNtKJu5PRwH?W-Zo!_} zfL$|^l_~3$$YeZ(WIl?Yz6U+`KhUOIP-htsp|RL3|5ejZL05^%ultz|3GCZflLvXa17x&0w(oQN_c*M! zz2Ff(tY3LncpS2JC-QX@7WM4_z>hSiS4Cwzit{D=*rqee{h{TICi#az4- z@W)3*%ljbT1Ci$ik?M^V#8IG=gWw}^^x^1>gdBx7KZE7_8O^7L68*8BvMEn(1hce2 znQlCcSM1k%G|WhNuqc@ZpRim`!iO`kEF$5Gm5MF?2;@&lrf00je(dv6SXH&?h4mG^ zz6Fo1A=+C_J^Ej22+Vk~J&}g-R0KBTI>)h!?vUH`083;W_HhI3SsTx}UP0B*gD@E2 zzeTKaI@65mz$f*P89Bg19`MYo73+K=5_u-*d_O#dT0~{ug2NwyzE6V6-(bC*WC_HB zQJlq_Jc^}}=4k;viw;g3*w^Dox4PIud(m!s?-Ru$*@4HokJ$1n`n~5w8k|GgbwgL@ zh0{kMDbGS}k#Kk&karV3B(6fysbm$|Su;14b%pV<<{(Ga;1>y+Y{bSS@{Ihka4(as z9nBn!ksukb>2whaMx2HnaF^Z}sZ>@JBC1;r`ket?Z$@$_K-Kr~4Y!gtR}1^(ITA4# z>bnWomGz4L_D*A`MfZ8ZSP=2Q1k)@c41w z`-Gq3@or0^g@@zQ#eun|;^PXv_do3DUTDOf+}&;br)Z?{JZz8d@W605VHW&+8ow(Z z?PDVT;seM1L5AOiQ(}KL%Mp7W$jbZ-*7?xrp6zjXgR$keiGar19#8ilfzi2Dj;xKkb_XvJ)qEA z=*oY}s-`HJ!;hf(sd$8WxWY>;r}0SS<-F%LlVP_i6U`Ta-B}n8Qq7nGLdXgq4a1d zUi5X|jU*P4wsUBoW4s<=MeJ8%AoqF44Uo+5SWWj8ALJIgeFk>XT&}p8o%@Wu2%sln zH!z1m90QTq5%iJCi^O=0&D9fbDn_P{2`QNi@1ZdAs1{aL1h17`Gm+yD*u_siJr=o? z7ivyMyY9sY8ORRxVecYX^ElS53UqdXyRQbfZ6@jx#ivqPfW~NrCsPSdXp1MknrB+i z)5fAtCSyBC;7RxAE^<&)ah#a~**I&EXOPOrmVma5UV)z-fvWdmRbPiz zHRz*qSWemTuo9v4jnN9Iu#Bp%TX3Y&sW)rV+WVRZ^L*n`#lOkdxf?9X%F z{}R2>30boZ$~BVWqA7H+@I+LqlJgk3JYWr5cQ9yN>J=(XL+5a0z^jFU&mK1WgWr@_R%7 z{g5*eNaLOG#9eIUhTQRDcy<>a_Yml$8*3mes~$+y+W03DF}K&O;$6PV^mi!1{SD#V z?NHD?&@UUu2Qa3C>`6r?kbOso&%~DQicR7`EBHaxVtV&@@Q|s{w3v2q5}EoO&Pjw1 z?(^GjWMDM^S&gop2F3S#&+-@i;5< zQyn<4fr8X`XT1hsaZE%`_s8z&glhnQG*GZN9wC+C4y8XyykAe|hn#xXRRnEzH4 z3o#2gVqu=I5Bo1BS{6mZ^=IEAv6h#>Q9JPOuX7wi(vAZkD8*In;7B>Snuue~rA~DN zJN*>9&VVJ+i(V2l>7o-&Ka{KLIP8e&;By7wBoW6t27fN*3Hn06BNUn14sG3xryRuR zZR82xVU?s2v-ygizsg_2SvQ#{ynv;*5v{lhir-8;eG?pcn>|e-kD~?rDB|7u*>wvX zQ;fCA%3WUN%r!jMR4nCL(9=eA^)e*=JoYYv9ozy9Kjo}*Q2rF;s)&pB;~1!{PGf!> z4AoBNefznC_xO-EIr|uT|028g4GCmJ$|pld_u2Q4&|4-e?}M~5u+Hz<%j=x^g1?Hm zPIV-Kh!S>&2BslDqFCc_eCqPtvz=?b$DWPncn^hs$J%#7*#bH0!`k##Y?i#>0b)9O z7LYG9XhC+qiaB~I%DbPSMb`2hJ@8U1Lb=t!P^NP40*`yo6~$Cb6{yP}Xze2WGYcvn zq}VO3q2K`=!+G}t_&`iI-46xdRe0tndQl6V`Ld?IOqCIncP~II7x?dQaFQ%YxU=fPrLqc=Qg z4nK59Dg2tA?A{u7eLeTp4}L6$e^UoO3?Av zBYw+TWO8%#jF?FJgFX7g?x@iyIoT00=cfoeRh)fH2RHf4`ih-@tn}B|$67>VJN{?M z6=7%kAPb{erF*QQn6Q1GDCs?T{~hajjpL!RTZzz?0Xh=9`w2Pqon6Sqll8^oh~|z( zOh&{^-hh5Q=9NVBOhx4Ur{X(C!)NYm_tk*_owzN^YAnW&=^C$X`2 zAJBH^(Lq}{Rx21~C{nBhtAh^a;2HivamgHRe3l^o(-4~K#d-)FViWsw=l}Wf8{dWY7kMoE zktH!uxX4_&2-p6_&LY7Lp8p5mCh(Lm*|Suz_Uy=_nnclxkt0-t&wiU@K^quex1SR2x;+*cb|unPIFH} ze_n&)9>9SKTqmAANZ>3n|L;2gI}FXOg%?+H=Lh(#3rMmv3XhBF-G1n!c36oaX!3^G z8$r-?J$@g|t2dwD0om0W$u$JIwE-=3lr>+>u1(_ZhT>B#f{(WFeCv3+5$KP>Jn>-O z*`0U&5A72G4U|>hKT5Gg+VEQrlty?L6nX z9>6DYJna$W`eG#c7M|iA94F=@eqkqsX4uTr2}>eTiFM?F%FR4Y13r5K_qPp8XfwO8 z9iD!O#xmnsVm464qTkx?5Mm&$$o1P2st^@|5$q z&K`L10vxsk&$9o#>)1fI zl&c^Ihrkc+;Q#-{XVw#qmhq>5l9Q4SW#$4C6xE=m$UP~-_om2gVQn13UfZMis4IBt zN7zF7@KCD31r^|fvi#JFy_Yb1QKlKT_gK1JN+GLrZhKOI-R;-$#*$!Pc?+_~WR ziSX1U_Nps85z61gcn#*&o|US_u4ci$@L*AVf%~8EdX5Z#$rFkEwGaIDtFl9xASxo} zQUY7JIP2|)h4D?25J}F-1|;?w&c4J>zvph=@OsK`N8z5G9EXuA z&-jn<1|00J8p%Z4eOvW#gy2#4i=UtCjiMR0iJLUBe3X$+RggqxPPd_XiHFbuvvJ&F{?D$h}(Sj$q zUM$BZ^wxgv;T)Rh4fm+yd1|3UIzg}PSc{50nHH-^?C&d9|CS=fKf%kTS;dj?#eO_g zfdM_ilKBQ5rPIk!;wA7Sq5U@^o2J1bv+xg=bHul>%N(5Ek^dQWRGq@ z*Po%SG_0a@_ScMuPznkWyi$aH7dCk!eCxnI*I`8@vc^x8o%zR#iVilx+-*6yIf!@q zAaBgb=s(Dt+wAW;=wUg#e1d1VrtGS)Ja6-}$T|DK%Kv6(lHfTPay*qIg-^2cU4kD) zcAlM2@r53Xb3X!SsLa=<$e-cp2cg%OV_glwFKmF!6FCFz6#5^i$k~p{&!dssvyca? z@Kt9Z#lrD)yC78>a7{mAX?LNQd3f%kmT?kVVj`GDcUHJAmRM!{`i87+TUNId7<~x5 zTNli$5!~OE)$IgFS7axOqy5V9pSsxlO?XE&{#r@#5vHQ~FF;!lq30(^#MeBn4z4N* zFP4D(|%=QU&v$|yWcJ3=^FNC=Vnk%OOwB_cX23HzxSTvSl;xW$UQK&tX0KgvRR6_Fw} zK~fs=NgepCe#nj19L@RcHmrL%8el&9OvFL@@|Ol^v&I~uirf``<78eF@gQTdJ*O$;on!R)M-UF9D;AJLMgY93ESc2RY=xZ zAbUcOi`Y_6s9IES4Pdt>AbpqOW37bVR`SzwWY9`1$}Ld3zylY<3sdlwr?SIid5z*- zL$C$}#xoaASY0DFj!j2flibb$WeX%?{qpLbYhazJ(jNb)9P?C38@zbpA zpU{s**rQ_bRcWMySo@A#rz>mMjQuIgE(UO>(3okgpyDD9h5$lwd=>Lk0L3ekOT8M7IOYJq;MP(_zG)&7-_MERf&OnkMMq>&4eC* zkFD{8r%h$Y?0j~nq9uRw7a=>cL6KHyP0S}18dm7ie^6Kw`}_|2e+(Bq<2q0I?Hm6Q z+%0C5<>P6K!H*@O$^hP<#(!V4=3?C+Dd+y?v(lhcJ=YIV-XYLOfi+c!n%W@MyR)`E zS=Ufjwl(sz3KU(4Gjp-7IrxNPihgX)iuYq5#vv7EU?(iVK3)$_B;q+!(2~8N{7S5P zA^0XgG%C2F8oSsD%OV`k>%%@YgoBFmtUAR5{*HdSjwD^n+Af6FL{6T-+7dWELnT6o zJ%f(_)9Ep+={Aldiq5~M$Wmd2h*f#T`6NHF4zOu8MP4 zWx0zYtXC19O;AcPeiF0ZO2Jzdc=}*Pp9pI<8;2h|lbtigstNut#mW_f%JOjJU_~v^ zlFV)ys>4wa-(g}7%Y=crhrtbA$~B%2n^WmjnM2WN_?*&9X63z`xr`x_|v zi=uu0aGjt0B($F|&ybgO`Y%H9jXk-?`DYYf5WFL#h|m%ISoNZ;vk%WF5ZJ6dtJseK=*!I-yx>{IjKFnBkLgHB5fkc;p6i1CYYIwH z9qY9YI<6Hc#4y%CU~yZJ5bKel^O2d6$k`RV{~)sHB0M1wAP zF3KpBQk-oWs?1$iWG#h+5a$XhQI0c3+p&j%XI z;1m8p88)cYSD{K@R@}%_3Cz<9MFlE$tgsrj?1G&$9Y`mUbtq&?7Oq>3U2Bc}=mC$@ z3l*8tggdCu(UPMThd}YWB0c&cC&r`aM7~*+@*0Cy6gh~C(2a{ZM4W0S*n_C$nX34> z{dk`0@P$Bs1s&VqsCUS=8wzHypEWy)%`d#>eaP&!tfQ!OnuqpU3#Dz~`$F{63@C66 zYafOL?+XIlm+xcPsp+i#YR+2(UCvY9B~a3{$lH&sN)oi0&L;}IE*VPw4p+Ux>iB|m z&VV8VSy91@wb1+Gdj-~9e6Pt-gH>tBt2RFcvlFFR`=T6TAIhMa1OKaHN_JMYqT)e3xu}CwS~kIR8xYZ%Ktx6ea;N# zy+VTKQm&VaPs@%>%dfmMH=L25{|dR32VT)YH5u^8CuE7pJA0$Z_2*Fd4e0!m;}EoV7(Q8rZ5Pd_Z-(FYuv-WCx?g$iit7CPI8UA&HGn#Zop zL;_6ZHJ_DU$UbdQo^2=Rh^N`h(;UKv6JGa8o)u3C|oS!tb@7JUfgMWsIWQFErBz&MQZlu)fL;T2iiWnJa_6^@&Dg?AO3Utq#FxXVl2tFT=K27O3*4zXK1 z&^bF4D@yQ~Kn?_waFFj}uf$Z3%e?az&-WA=C#*1`ebSJB;`k5u`2_dAU`^xUzi*0! z{KbF%^QBa12rYM+m0k5?e+Au%+zBgkTZ3;Vq^O8_i1+)ll3ax&ge88KuL2Xf0R7#@ z7I}_D7c%EDXWvy;O#J;0SNZ^*hdD(bzx&=@;Mo-ZziAqpLP*g z*<0nwMQksTy?%{Ed%+V6v`s|2KPui*ilTkB=wv;*TG+NiHyc@d@fQ<(?#G?xOp zuSE9nEvtDAYvl}<&jC0pM!{GF%DMpwv5`Z>bA{YK$WEM7_Ce(6JypCDfo7e8K2Pxe zv&w#mXZy|Pn7HqftZ7~Dwl$p58?GM;$B#u4Oh+Dwe5El+2N8|v58rfy!^G67mT=R5 zPbWC16WVeB=R|OJ1X7|m)}p{Vg4vTmWS|xakq#XS4B-{e@&x)4wN5wrE`Aa*F`zU{N|ZE^!$knKwYf^iGfz_i$LKXVB63NQOCjgIj#!Cpgp<}hE_|QQQ_Pa_ZpM| z&Z_}WwV)-{g_3LvZRi=`vO!XswM@#YrT~HZ51zD=FWH8&KpVm*CzQiu@c$IVV*b%0 zh4ZX(P%4i=>EIf66w2-*{GUzE{?LMq{Q|imPy)ys&f0NLlmxy2CE3=)(B^e0ZMcX7 zQppA-Z^0WauQ+Eb2*0Q^qh?kU_(smLWjzb6p)E*Rv=G&ToIRTC+t@Bp`$cS=4;6rX z6op=05}xAVDg@(Z9>`Z1WCNDkZRn+_qhk$Rhkx9^bOXXrC&9T9$~x?^Xb<=LGsazZl3~jXuz&C`J+BzvG(1wJXcblZ{T?g{}IPie_ zEB4oHFm~aHh4!i_aDE%g8|T8C@XLUAwRfFB=cGwa#{~`8=OV8O14kQK(U*f_4w&9NPk}TVv^?MFMRSC>u^fYefAUt!+18u zUtF{G*KSa*c)aQ`4&;Y4$|P&Nc2WY=PPUPnz(*x$Cy&E}Qmrbq2K-jO5tMclaMglx zeG=MOQOHd`C{+XUidHL>jFDu2%>lG=&kOci4O$JhUX*NSlYPY7b%kf{e4o@qQEx-3 z`wRT90J)57OejgnFY=TB%GDw3>6 z)D*Hp4Wk9aTLVgm+@UPPS*#9mcxpg)Xzyt0Ko{S_h zfzqcSO;LK-U@u|-YeD+>me4NlLXDh;HvA7rq5UAYc0oBFgm#3Q8uo;fP+Pbk4|~G} z_{CWmO0xqH?&)xz1s^SS=ttRyW3IoBaWC1=(vzv9bVEJx9MC@qJqhP>dtofw1#YkY za88FK-C5|7SAjDe5!0b(-Ga7@)~)k^i+d!_1H=Vr>Da1qOhY;8Kw|q)DkYhScLl}(S06j>u|DHpw@%> z+*;@_XvM}Agl);W0L})E035D3-G&Es_G^HTBN>+S$z*+=O2)d-c6BHzoYNJ7wuhqx zYR5S0;|_+($zQZI)JXOr96?ai!`{{)IXc&Z7_^_%07zWtDVvmXg_C^*M|NDfOaLF} zlkIB{aJf0D{o_u5w3}@OO>74|-h5zPQVy(4%83P_{mcL@73Z$=Kueth?x~>Jp{;&S zavt55G#mZ`Hgp73V9sQi1aYT)jXYNkX}!K7u_LZOz%>NrC#o zR)%#GgSy30`5xe*MsNn&@6lurI0f!Y5Q-YaHE>;qwt`;-p`^w&Tx>DeX0V5${Kh_p z?HhFt)H6{VcmUMnkP2EzvDc!FG%rYO&#sMQJdW0A8^YQ^xm69y7v(sP_o#){gkO|q zXnjR(s0!2#N{$jBMe@Nvjx8v8aE%*thq^7U8sq-PYru_LmRJ*`Qq(B{j|qP{TyKH^$%$@=aECXeR zGuJ{;SJ+ljbH}p59klq5R)+`BPSH|`I^0QUZ@Yj)b!?Ih<-K!ZSy6Ti1X zT@}B?>iW=JR&;pNb+OJz&8$>C9 zD~0%vb&YdF)Ui>|;2;xFhsJRT=TT_M!MaA>8%rPCJ6bHUR}_Xehq^V&gL+AA8M$l% zd8i9{sRn$Y##0e~QFdeQY9wm~>&27r6`=00^%jHvl^^KhsERGoffTVGaCA?Ha`R>_ zX!nRErqC z6@@zxjw1^8QIv6*Z+^OYpf))jQ?j`?kLdm0Uj6C4{5ohE$+TtuzfqsKC;}m#M z^GJcRC&_sR;-VgN1=6?%56Ty`s3G6@i!Bs2AsnONns&hS^cSRv-~OO(gDc>uk3~R| zVOuN?ZK*<17T`DYIOjvn0VQl*XnWp&We9yD+4oB)VORY|S`Uquy{D>Kv^Bc5>9h83DFY98pl#quhEFa*IzP=r=`^Zk!2Y zj;lgWa1=p_h@&@3I@GCf)e&ubI5PRxI-En~fO5s&fva=48xQp~tZUSdv39T@VO^mf z;#m$*lWE=|zc) zb%%2@JT1g)2RMuJM%bGG>*-A%Eb8BAUyFit!?MGFv>AE3VG$N}`y0RyN@U#8a~Gg- z6vm#053V<0|HB7uc=+#)aX5Z^R!+2jVn6l%(ISs64IlhPABK6M$w25)-W%s69$et} zG5wH?Zy(QpW1OFkPY2_Cw~vcq9uCF?E-yY~EV?sed(a3OxG<;wUl8hrcrkuF>iYQj z(Ej`3kcVOXc%P1s=X3Gza6cx1%;@sy1y~u=!%&P5Vtvfawd3>TzXK@G<%K^Si*zw8 zh|LJ`?=&B#>SOxh|F5(%Qugu_(^$W;l00AOVOw1zkMoR3O>%m+&N-&cz!=L zD8GzYeyoqA~Ij34H=O0UE{F8wq-I`|v3pMXOzJR>)# zA^84)e#Z0z&H|SY>2rn@yjYK(AMS+(co>d8AJ&fv=y`bxc=9O)Z%hgCf|N4mG@yhS znSDNwL3qH2?+ViScY6+CGSdyX^<2K+gAXX8E1(gCV_0U`%=8fwx$$8hj`hPcQ_NUC zK}>-AZ!Z5$$HU7Uo|%u#ahQ)F1XJ+1@XIoY3pmQ`^4qo_8u06f_-`-f;do2|pCH_K z`Dy!p|LxPujORmpv4JmWYXN>n*MG$aW#X3uQVl}AlrhY&Go%<$^K zd|tU^Y&#h4;rsAjNJgLU&Pdry)sG3vHgo=c4gJiGFVz6nclnfje^5q2h{xBze**|~ z1wNl-RA)#URe- zz=z8G_IwZj{#PCYiXJrj{j|Jy=9Dti^d+Z{gRuyoIahw35E|+G<>ICNFbA1ac-ZIT zV)(q^V_qIEy#W5d z^6XL0i0@Ml@G_=>93zew_Ap<8ALISU&qYA--)Urw^Jse^K1J{C`~1@J{T@#lY4{xa zRIuj%i;s+H`S?Cn-=DF4`t<%6Oc4HGJo%I|wnIP6PtBu{5&OS*^urL&$M7k5?FMs; zP#z@y`n+XyBTi5*2;oCv4F364ymW$Czn%H?y%^8uQAx4}6ay-r+l%!f{jmRf2dVik z|LsGg|9?};42^LA8~$Nfzg+#4ytj|&yDxSUwnLCe0U!!poDOKY{t;cVV)23?NjiuyfnO6&+YNzLHV#AFP<-e z^-~L={1AM5DIqjc4AKo;8KHeQ(n1_B6$}krK1^naAl`G~Z@^Iy>tP2mzRP<*3>TE@ zzhfQ_!M2q#3@K*x2Yh>J;jib)9OL&>zpZ*D6+n74AIA1m^56n!kG2=nGGNzO<2E#M@5&FNv{BV!j|N8xR$s(jjKVu8SFh7_7Rk~hWMxK2t2o@!eir#+>@)hi}jAyD&Zc^C3K6 z=G1){jPu>tE_^qJKa3yv5ej`lsb+-6(14Eb3b2uO=D&Z(`62!r)5^$S<}?B*FE<(e z8B@Y=pP!83ncV@l59_~uY=j7USZ3`1_B~9~^9P|mR*+KW5HCePEO`4A5YOWX>oBNC z49`r{hr)0VE1>4Pe5$_Re|zBpz5fM=@c$i`IYsmZ)cy<0tLMy|X2$S&`}f;R%Y*j% zj~C*l`CnB0uppmaNRSsSJ+A~kD!w~NJ%IJXur7SMeyMn2|8Lxf>HM!00@{9h0c7Ap zY5_&BG=kXwO#@>BJ~P63HS=)&`855s{5OVY#`Yoc?WKV69*hs^zca)7P#z}!`k0yX zmC=WMpda5}4+*IJS1L#~Bk!4g0Y>2RpuOLRQ}WVAn)vrHju+~2=Hq9E^B^+*W^7X# zdHZkLK?(@#rQwBn@EJq?mmA^zRDGBrHIG`rNkBJ{MLtZ%H&XJb;jd3G2=QD2MrIDY zSRX4Reh}v4W^4_9PW*cEOC=+gpR)fB;(Q)_%9-CjeCAN!7f|s-{5RrZEWR_-$XtT| zmAX&W#}7&ZU74w6j>FVF?98b=>_a%8j`z+C;l(1QjJyOs#K%7$3u*X}$%y2SpMn?S z`~D5*WBgZH_;DWnhar5wPt#As=PTd}LwqPNcOI;dfsnr2hx0=G(0~&R!N-U5Vtk(m zAN+dI80&>0v=8fH`sL`y2e_GG{aj_l_QQNi0qx8wVthb1BLzPkF?}4L6OU@(3tT}u z9*kdl{~N-C3w|^5;HT@;%KSzj()DN~te=_(7yM?Hh8~8OPR7)HI{3~Q?$PmL@%P_l z>&5vc6}0Ptkv4T;TSpXM6{AGQwerWb_A68FA5#P=2ic z_AtF)KP)p|5Q;8@^q~E+@#4Lb^xZy%02kx^SpSVs0R-kADF-1K@27=s{L7fv%yF6V zJm>%;Gi(s+K_G5sh|Ips@II`cBEq8E^ZTJbPG(9O`44dZ3)ZLlaB4m*zWuZU*o@E^ zn>qD|F+4mUCNsQWUzm;$@vu9fhcVt?=01g(8L`plbDc5fVYrN``;>k9`1WbwJ2T%I zp);o(#Q4~V8>EDFIewAzey>%;&1?a}o5dXQ_Mt`F})VJNyhpT{ji zJPhq&`OuyZaRQD43SMkL%l9Mpf71=ap@+@mSzqvE)OD_a8@C(tau-Exd_?CDG zoJm<7?nfv9XMbX*$*dp-G_U$8p17o|Ax`V z!8dNNz*(V(;C`#_a1Y9EdW||XD=Pr^tX5$y*ig2YRn@-N^6F#tbiJeTqjAH?YnCz} zH=i`ioAt~V=1XQ@^F6bNS;MrA?~KQcIeH;|9^A&goxK4!xLttzvZlZdd9~qO?^962 zUqVTYfD^;;Ht;JX0(Dd$?q+M7ysx(qy$_|c1YkzNIkR{#!(=G6-=Mq>!%1%h&I7Lw z_XYHVdjUS9U(#uCZ_8-7eW*L!pv!vdx8iQzwXa*`6Q`uY8tTEZ58%ixMwJ-bf>6rO3Ry zKdZ{(jQebA9XoGY<@jIm7Hm%}hn6+llRe=YiQe%y_*I!ppJ8@(pK^=4 zqpeH&2|H&N6`yrCp5>ta7Okju#f#|6)j?S>{H@VhUb2Vqk1b1I6uT`}@agvs)8$4p zH@~cUKPDUM_3oBsV|d?tR}#Ob?>7pWH|{Ttcjq^v#hvkLkvtY#a&1?%zrG+El`ZMg zyLEVv*jndU%H6wKw6h*^hr%}&R%;F->VlS4cU>@1~g0Tj;gDtXK<>HwQ|$P8F2YD*nV&Z<5CcegPq zUX~6w-wEeVnHQTRmxo?gdGy{!Q}(M~UyKOddt``qoNmvS!(178C0pOf-W>b%;ac^) zW3qpix*<|B^iOJ+7K+SD-K95-nA%pGWZfCLAmI+t#Pgz}$Qhrg z7nB?C)pC~0C3Kpc@*w2QiGFpnBe_gZM@QQX|CYimNU#W&yYVHKhs?M38$_Z#&ddAbQUUz-MFzQEnf;R=#p){rxrW#@j8^SE=ewk3`w~?L zOtur;nVmIjYE^rI_yiX($s@zJmiKc{R1Mkt+IRG1Xq0N8DJxHUFQ-t-qxxA{Gybc4l^l=Oqou=H z6N6*V$IH88%{=xB`=YfxRzAATcto@L#I$bdJ4pp+qNDV6{93$*{VO?^T3Egn>uHn` z-Q#(*RBa{e;gIx^<}hPZVij$m7RP49XSlJ)L>svy(kfzR*`clA2U2#M zmB<&7;nr)iifE?Q%-%D6hGtLsKs!tFhL@V(hjh{^eW=ktlp?O1A+xi{FW=y~jq%!V zX_f4o?#}oP@ikv8H{03oOed|wpYm~}H}4sLOQa^cvi+fo=`-TH=}-J|xVP{JxTP}^ zy&<1UsQ?ab@fpO+Ldz|}LqJTLprEA<4<;=&WP9JFJt-P#u+9Fya%PoCS%9G(o&1<2eq+yon z`Myxa#60sV{n)6lFE>Z0Tz2NEvDuo1b>lFd&$?Rkcw4Px_V3kxca-s$^;p(3dM}wb z)YNSp|1SKU*_jWBSBb~eEO)TIOk3dIdT=wkHvy-o$D7^$!<`(zbZ0-@L|Y~*9@Mzo z$E_tB-TyAuId<%JwFgDww^c|)@9uWTl1hm@%wpBWG|?a$r$bVXB-U_A)0`0dMC)j# zTfGxQM4j;4T1R8P_7aNqWAe<@_iHQ7u4<+Ai1mm$HgyueV6O|E)4!o`o4j<1 zX1C|ZjU0)u(mLA96O+hPUNPQ9El-4IaQraPVkgTrn zr(wR%_=c>p&xf{#8|g7x#o20%3P;2at5fJ1yQ0#~{^5pJs=J9cG>5B_;pbAXhBg~d zx-~+H?3Ywsvv#D1%%R;fXQq6R+RX{`ZlROp89I)Qmpk1{_gC?{k!M8-=b)Gn|JYfY zC@H=-Rz7HZ=d`@(yq(}qqr?+DE#}0VSbNp*^r!6)iq?1U84c2B-tVsV*#meX z_Ls1D&9pb-wXEXWV)D8+k~Xud85^v}a=oQL&(DW;!^rVrXg&*@1L=C%H|r0PKIA$* zL{95D&4H|5=x4P&_K}t=G$7Q|DlZG_1+Ia>uC3-(=?#)II-kFz z&8Mry#I$dm6nQjlk=jRdrdLjEg>i3$aXhiqE~Sn?=pWw=dNxs7e3Se+Qp+foGB;~& z{krCA>ESPO?54$4DskRj5*cK!RfqJoY6Pz!8tW^vE@3m>*=7M_ zzEL(bm+doph!N2~){AB&QJ>f5d3gg_NxW^`7My)+9&#U{(zh^nVfHY7FkH|i&veV`rfRK$!vBtq|CUE_2SY!P$pv3wgTsF&`=3)<)49df0yU!B%#GE#!4* zciuCxH0nk_)qa6CJj-vdZ=mGfUQd)>&%_$)bG`*-i6Xk%(@G;lPTED$GncD5Qf*#L9^|29vX# z4blGgd969mF4D;Mv;)zOvJ;u4rzT20n9iF=FQ?y9UF`E|p|oG3a8eaN9pA!9yi&Y@ zcwZIKYscB0kSOhb5POO~!vBbkNaV6hn?IXTm($$ZBGS@GnBgot%xzpm-p@WTbSl!) z+^wB6*3b)CU&vlSoFlJUpP2mv1dp8_j3v8G1IQBY!e6&DhF* zRpZ?Cu^lRn*Kq5Fr)hT++xTI+HGIWbEk7|1$qtEWT6ya;s~ffK*Np@Eb*H@*)%(P| z*?S#Jt8Sc5ERn~ws2HNBg%*Wgpf5)Y(7xtLyEW-U*V%)_33shtDK)G6B)?&fr0w;( z=6ZdBw$x6rSvd-XyPM0+R6S2EDZ7cDm8~*H87>)NROThz+^MgJD#N{E zr&SK)IxWQtL`uXrR5qo?YYinx0dm3s1Cbn)zMBS8mPZm zDg9YhHNBANsy>Q6Lmy#<>~>E6cr%sDcq3lIb!EM1A3jQJsqbfpwxlDS z;jR%Mpzc~jIvE2xSjPS^_bm^W|QC3 zqAZ7KKnuq!rM;}*XBYT;WVmRhh%CuX<3%w!EyDj%WoQB6LWioEnEPNa8$?Hd=5{n~ zn^TAAc9d3O3({^S@~$TV0)wi2>o3G=RXj!kJQCjm&7IYWLipk1$vnpWRHE>8K*55gTzU#zc`!tIvx_Qns4$3 ziRn~#WAzK3h%J-*aZUj`JR`s~wlOwAZcQ z5-nY2erpvL-ts$~Vypr|@drw?82kS@OZ0=H7I=s_-kvD~N z5-z&U)w^UO?O~hu=K_9FK9jtuD!4`6@9d9^0ilTfzT2OjQ(IYGnCV*DMLVB%HBv%< zgA|ts)K>bF^EX=&+L~x;x7B|z-U(4-yEeqUEVj}^*<|V%eWKdJPKB6O-h4aDS+d5d zLiV#FW*60l4>xYg3(i_IZVZsKoeI2$@g!|!zaVnjUDG-w963e%)2?)Xwp+^G89gli zZkvawb?QQJK!$|c%6CDk}>6z_L$U;K5EVmEfLB?iWa$3K5iS&lar##_ff z;`N>L;uE!4yRMr~ez16aX;n2+*<#vOn<0*CSyJ8*?>hC#t6HA$C;C(tH(T<*+@<07 zLePVRu-_MZ-1aJNwWN!k4t%>hqwf)S5+9J5T`qQ!!W>YP;8WZe^*y?!I_Om?vww1D z(Z{WGWO#hM_*Tqvrh(KfslK+Sr=L^V^n=b9WH9UKmR7CwW-05#U(4U<^VWAvXovMh z?4H<=*le`RGK7|9sYWSEjaQBFq3n^mqA{;zmDRh@sq72QX060Ty>^!K^scj=PmrUv z;iM>i*Ieo>OH>Ipgrg^_vlrDtR$Cqx*TRF$9j@(mCl!r4G$AJ#_eJ4&QPPODRk@r! zF``BFp5m}MEYwT>qplk3BgI2Y#p`hM&1&~)77C4qv1)sK8Jiw{l2q06m@ULhypvd^ zois$EmNSbjkTq3#){DI*KTqG~oYlV-kGLav4)=~|BYNm1B5QaXyMexuZKVIuGb#o> z{#DsBrG-^gWs`p^O}j0>Q4!;f#QOV(_$A`7?`eBJmcm(^qIP&nn9Ef>yKKcu(Q)!8 zV+336)Fmgh2Koph?Y3~}Pd`rdHQHXgm2;Ml)Z1HQcp-6CR*mm`2mAo5aVuW9=TKfVIP!XMaGu@w##f*g;Rxx1E?6W#*>~(8kTEl8TWLwkMUTj z8tCL_oZ0+M@tHjh?!&aApC%@%3FNqYEVf&YRny`l$RqljPDsYlI<@{~yDHlM<4iXvuV=0w*dDoY#A ziK)j+khr@yt*jbl#^6@=X0jhWp!Ja#jeDW5c|LJpYi=yoN(#8Glnvw;$SVdT3*Eos z*JK;>zUWPUGro1-}kFywwAd%Bb!+YStIkS zZ$6Esj2iOzdVIl2X_LKgJ%pUz&G}Ip@a8HL=PEKkA)b!BEY~}PaTGnv>aNSTj z?H_%M)s_6Hx6V2sWud;4K4ulq(#w3)vQu0&iMM7$t>KXlT3d1pr08?}y!nxtMLlJA zatcvR3~|zlBQ+iq)tNG15Vv9_*@yIsJA*0hIQd#E5K}_iLu2H3a0Gvw6~aUaYZBnA=!Ub%tc8-JDV^ zU&;?;fm{c*cg>ijK9#zD%AIZ-+7RZr=VLqVE98`_re`31Joh7+9sksY2}tb zktn+%*2@Vj-I{Lv?h^SnyF!@OT>DMyLc6SHlV)fd0Rt@5AJ`b@;!*m=0U zW2r15e-)qclhF>&U^c@o6kl%Jd;!TTw#wT2TW$qs9L$G55QFW8VD)&#xotP1H~B|t zmmcuMGb-xrNi>!n<2N5v6DQ#=>!xC=lc1l_N$zrXUY`VsOj1ASI^}Ro(g6LO652+q zz1dJrvlnRL&>N~4?ZcMwiu_}F&bVh5aCf`2S$ojvMw1GxwYV3}0ta4oq2)~;T%BAXOx+UC(4HM5=^CAtxeYFjw zpLt)KLc@{9DO*Wf)lM%H*%2OUnvv~h4Oxelv6h7+dU=`MSfo!EU+8_TA*8SDr*i0p zG|PFMZz6knPqB;U(;c}zK11y>a*%_xuy#+Ra*H?Dw}%I*BI0w_S~+3>Xbi9Nchj~+ zN6GPSKbyjek1mctTcV*Fa)~_~PCNewZt{MCjADCv1D2QBB?F`nm zm-sAJ(<~3#{XTh~^;Hesr;M=K%uR_ONjxg!val$`PO8!IG&@ATV-?kLCmSiJCMRa8 zR3iudOa3Y2`c~tFOT{m0L;Tx#bCE+jiNOz=BtBP9^U-1qnJV(y*P|ol)5hcOIJ>fX z%ZWG#)Gu0Y?J*~p(_Je`-{Fnrd{s>LbKWOK!ue<$_Y`SMx2b(H3Re0q_Vx6wB$xIN zAHeTJyE@4~;9IqdWOFR9(~4eUPsxeAhE|1~ahqGOr!Fy?5>1>YVRn``WE-_SiNdj| zGPhoczQ8{PtM|+9X=rOdvAOCBzpeERO;WAtX{$7Yv+0eZ;n8N8&*QC(ds;z#cIc#) zqP;J>8xykJGS z2mMA$4ZW5qXig4=wOlMeokYJ^yTux#iMf`$ZZ*0<-_8cAKUqVv!-;5-ltxxrtt6SH z|7A{Lb;JEr7torrGV5cm3{5hAv`&Y2Xtj)Ek?N62MqWC{To%@0wR4FPA#lgC_KvyA zyusVqaHl%|-Wd(+t9|VU59%e-X0k;(2Plgx|`2KhlW`Fyfd!nMc%L~P8 z#mcC>a(DEZcq8YQUBX!`SLu!AS8l}$cmhI)*zHY8XSA19NvI$B+Nmi-!znHNPRy2JV`)ZXf73^VV-D70St zEd*y6y3<82(vF5?eP~@-6AxmwRfKJlo1HoQGg04}N3Vv*7;vJa97CI$rRYx8NqYlk z@&$#XMvJ)JkawdqVO6#YYe~xUrfxBsCDfP=5Lwvg1kUU=bEdSkDyrM=b?uCCighz* zn1gA$tf$3N3Wv9A&CGn}DwbeF_1}#@)ih^>R^7@);J#ooMJ^S;0o-KzZemKJquc~% zTUMtzjXQLiJ4QR3@`O7F`}zB#VHgW=yXyY=x#V= zi<4G7MN~4jn7L_R_Yj{%ri;0%8EB+m@UpT58w4v(VctXys5VLK z4ENoCB;R+}iURt?P;r*kO_$Y3SiPqgF~6lF+$x}})S^GRwLvOvcV2O7;ng7IdFOtBFvAF9tcTD$hr@GR$ z6HYkZoHrFqMY{Z29^@a#Ur#&=EBZf(8GMiENmi>Gth1R5M)FPa2%W_a(JG`4dym{# zg+k?1zS4i9+1X>TEhS2yGwQRF;wqVGG&TREHN^?I7de~w2OxiqFG{~0n+W#Xs!(1+ zRJR+t^H~$Hk*;@|iFD}UOXxB^Cmkk>iH`I!eGzZ%<|Ko~L3h6FMOw(^_6qT)(OhrL zvNDrYlE3gOjD^~gqV5V+f_7oM_2%YGvpl&eb{lnIUFs}5ukFB(A;f2rw+(mw4vOsh&~%^CNX}AUbISSd05Oitaa9u-pm-M z?*QBIKI4M8}9gx`P^9}8Wz*E*3mVRp1u4U#{?8l$Dp6h9}3oFISaQMp#w_LgX$SWyMmM3GO-MRqrS+D7k;_I+v$cKz z?w9SZPCEPLZTg$r#cu2Da;2p5O9}TGQ0x+T)aW7s}2u%%a92GM2x{@5;jr zPBRqM)ku3&JT9ImE7dK2*4?aD%RBsU6_FdjK5NTE^eIu*DFx>{GEzk!tqmj7-3tkW zJYdb#RoLP589yc-)y{{On=MEeYOyqR4DNlNO)83`i2-sTJ>u5oh2$CD6?RxXN^g;I z_5$Z4dXeSQ9@T!79nGsxm~|OUnif_>1r)a zv--n`wo;bU=7)Bg9n7`ioccbsO7CfPu;%Is_LJ4$s&9Or(j_t-#=e8r%aQihPBM^P zWxeSHy}mg{ZzHeRTS+NB2dPO5lH<-tb|;t~p3o1=vGPk=4CXb(wVZl+vdKxvOKgdR zT{&`eq{%bhh8mm*|Grcu^MNB4_^qR7xJ(zD1`$3|$gL^^Wc4(qLX~S~E zn5KjwbrDf%dNyr7|CAS2Q=q0&<$I(xx9natWUgmD$Zffa+@+t9cKQi@yQs(ykcRpv zT0UkPPw7vS!umn$HDfb_8^$GZ>#@W<40Wthds37?t$u#^BJ4=CdT> zHYM|nuh}-x1G0NoLP2Lga zgpayAW4Yu?G778@k8)|ZNNlj*By-6=r=@#A-d5-2E9xF?A-;%z%WKkIs)c+>EKV@@kz_0<0LV#LxB*YN7EJNfl9M_2qtklhigpW%<-Ft(-Oy zY&$vipEQehCofxf!!H_V^_9je`c>^dSd%%s>@?@Uf+Ur2YHK6%l(3xPdVqlAIV|@cFio4_-ZKpL`yGCBo>X0LHlaVE|Ddn1RTpJ(C6CP%ji;PI=sl5O= zS*<2|3VDiEXQN=x)B|UF zbcB15E|7isW?qcXbT%c1@*3)wMCn*h_nK%1E0q)EC861qoUe(JeeCk_KkY7jyi-l& zWtT*XtC0oTK&=q@5sno;BpZv@*qhpFQPKHLEN7$1BGE#2c2>o5I0ef0d7G?^*ZhZ!!m~q{UbXt*ZX5u1J(rGN+h~ zRhJ9JY*E79!`~z|d8fn>ccofIr>KH5Cz%Oro#%;7OTZqHJ+4l&(0cTkc9XUdMdTaW zX!8hN$ji%-q9MOQYZ$#*A+=qW*2d}g$TWJ4wNi7$73lLNK`YHpH|T@3m^NA8%qUoD zU1M~(zjlwz(Ngpq`flsvl)|Awr+_6j}k-ghd>m+5}0I;uC zq6PH&`bgCq_I?cyjWu6o4YeoO7aW?$2btv?ENt$CHftCoW4Oz%Sp~SRT?z*3`S?ve>O6+H7r|qHb@nF}4@w$Z%Fh zJul|TvTPhHE^2dIM&uK+6pTBYxMN?IZS|52?s!uJc_a4|_ok9;2Crwo2Xlr`R3mnn zH6t&(Bi&X6c1`e`Zc|Ycbn&&SyS|+KEq-JX<9WTTc85-2)yXSruHl#;)3|I+K47oX z9Aq!;Eg!dgxqqrU^d77QxawJA$X)D!{)DRQHgj{UN9b_*44hk??%v=F*a=vvUnr`n z<)GsW346v>H<4GoN1rt)-2pjkOe>NGY#!^SvO1gHzRc9-fUU6_c>`KPBfVdE5{#!K zSQf3gzQ$}4x)o}z&!)%C4c2#>O|O#?st?$~KBw2@qwaRG6u4_iegyrZDBqe$7og3$ z+|K7@1AA6GS)Mi^zdAjf&0;^Sbo6#Ua}T&T?c;Ji%STR%qr90qO`E|zIN8VwXSdy6 z&Y??aYgtU{-S&-~uHA_w*BWVG)RebHh+1d1%sz-a!)k>FlMP=Gf{b;v#&hsC{PvSkX zL%z!Ywuh)@tdv~j=5XdYCtxr7%b>X0P8p{s@X|pIg}vsR+{TGBZY4UMd?j~?VRV7< zjQ*OrH?)UcP*mHfch(P+v8p32r#@G&>J#(_+R9$; zGO~|mCq>xbq%mkfYuv1CAv+|u%6BB#oYX;TfQ9rqyNnp2mW#4*@5e`~yy`FWvZ?HO ze$q+j>-ar)oBOgT!0Xsg$+CJ*eF-}YJ5gXQnr_uotRrCQ$wP0kiu%*qRrV*WojfM{ zv!~b;`H7o_SCk_}LDEQjL_NbbC}-FauXgb{@@u}xJwtl2qp(k*vFZuh!Hc90T_PE; zA}WzzS!MN?yIuVP<(kXvZ~RVjithY8>C3js%5u5*(S1(rp@WRJS`n~e&1Spk8`^U1 zSJs1eA`f(CO{eR~O?H9KhaK);fYokYqJnc%KB=~eRiKZh@HI|8kUcH=E;x?-S7#d7 z4qlhN)Eu{;+md!?-PK6(7OYMG1|#x&^s4;9EdsW`#oBi)%;H)Hl1~-{>G&EwDgTzG z*jdTDC03>BUvWQM3?}EO)Mh;L!+;FG5&pAcy zkzxj`Np|y&T<{w5Wzk&5)LO@mcM-eP$8v*sU91)z_-inNACP}L?VM9$Dt(n2tfrx9 zd&xz;nRVTm!FH*S=zThX)X;v{CaF7e3|nc~+GSN7c7(LzLt(dZQ&JUH67PshRFGw? zEo&-Usqcp8!8`9HiccX=84JDEkF;j5iDR6TM| zc9h-tYxcl+F1I9nX`rjJYCKs-Z_>B)u4X#vA%>B`bO)IPw-YQNcfb<7d0uZuBsI02|{iIZ3)=KGgIWT1b2;_o~vWpIFC>K`Wuo zI`t~MFMkvj)ElIi+AO~$f5-+-RrkDV%qozDAju}sJfxPM-@HIe$rnT!GKB7vgViju zOw5pL^@gF@#(cV%jn(_=H_dn`tL11N)Kmd=a%< zD+4vxnD#Qhx8}0HRD$JY@2LjxwbpDg$bD3H(HH8^(HnHH)=$rET(Me)b89u_U7F2! z3oH#6454>rAJNU`HLC)f0e2znkU7ON(T(huL)@GadJSDHD~f*dd2z|GO*j^m-pee^oy*TscXkn8!}uwqE8?z<@YKt?1*Shx%M-K znH`DiyYyB1O!JoBj&3C7jSHc%#^d@kp_-u-t8sXDxQO)=YtMRHWv%Y)Fm28Z+Mjka z8iuNBkBDLNJGdXDDj5U!V4QNV*%Qbm?WCF~tEx=`MgjM#IKY37FG`Gs)#h$|44(&I zlMUiwQU>bvmBh5fedmC?-EQG7J8)?hece@8@bM2VCtO};VbH4;ey1inTM>qA3seCe%SJq3euF2M-q%NS#c?rE7`Zn!HF6)=H zDSJx}@!9rC{>{Xw>MWP#0_QlvFOgngQy$y%cq@FSHe>*}phws^-AUaSzv*;m7yAeM zdKRh}*;Staue;tm=DyP#;C!3GYVnNplsaL)u*}dF_+(LlaFLFn$)6vGyB2^o8#h&dCx-G54bI}v)n8JY0JOxflw`O)SXFu zT~*XmFVtr-O%F$x6{-J-c_u!sp?o?KJRGCEooXGs# zf5<+lcpI|;Y#CW$e)A@LxlHdxUuOGZ*A` zu@4-|PB6k*RYUz+i~-x9m}N92WlN*zBQpWL&o1@a``25cYtw#eqSV;@a)oTNk~2O@5|@04YUob;u{nvwXX;dw@O+I_#i94^_bnlBwhvY!%r~n$=OHxmQ1JH zSREi`G4h?sO`7R0vZVSYW{C|n2`{EndV{?Z@+fMskNz+V#cX*7Z1pCSOg&QNO_LcY2>rBVT13F%ENMCVCn_@dW&(;p8^FlpSSjw>M7NJe5RW#JseS zOlCjrvHXC_s4K&7`AA=qKji=~u{+I-;+*V}9mFcRSLIYOKtB_B?}Fb|Q5qjlwH|BD zHkbx9mj9+Jb$#ISm=^j6J;e9Ycev}LFk{|Op+HF|sZZh@RBn-UqbY>{ zHcwPlKaEY(s1{-fo@o~r1x$V-wap3Q!IOB|)WX)FTx1yi4U}#JJ+Hb^Kc9e&c@51h zJw{J~rt`d&6YBk)KyN01Ej!O^lAbC^_d8qd0DDA6(3#9(_pEGoVLjfqp zOK%i+?ap;yH|smU#52P{{n7aylGPXF(|y}Q zb;yB`2!A@?P_TNZoQ}T6^qx7+Qdzm|+)gv+H#9aK!Nd8j79@^Z0=4>UL1{0mm-#4i zszbUtu>5~jd|k+!?e+&g)lo$Q7usQVsuuA2&iCR5ng?sj_BxKJ;}Y3k^c9uJISS>S zSJ?AZHnTuKCO$eHDk;rV`a9VNf#f!Tj%P3H$hXm^@O#W>hisdNvnbZ)?`#K7V;x;C zKcI?qB_8QYtI)*gN&lfq;1;MYkIDUN8z$_tEFr6pJ5sXmRwO%N8mYHl1vj~BM^l=e z(22%X3r$Y57=7<%(ap__cXGI_E~?1)dYgGjrtsP<1l&g-FFXyNRX2opOa~sCSqaq^usR+tN4I zNUOCuph}y2`l+su+u*r+E_#wqmc{OwSNfeApc|O^@MEtPbubCf<~6ORRzF^pzSr02 z8|S%wfL<_@h@jQkCF_s#51VXyvPeA9*2d9|%}m`!f2I>@O4Ur>GE>+I(o|nki@a9B zFmclyBPF4*dZ+$w);z>}S|d$0c#n<}O?v7by0-Cy!w<48Rsq^hT`;(#$wQKf{?J#| zX!FfF;XA{#vx-)6Yq~YgN#h%1rKc15OyG=XeT99wdgyH1;*>l$i9ciGI*-_27PXz|5r4{0^UQ z4FZEh!L5hWm)1L=Rt9eTwyd(9-5Jg+=rU>$xj+h;&U(0-Afx37`oT(w{%Nn800bck zT>D`rzUb_oB0qUGww1){j%ujm=!m=#8QGeu?^X01bYx~q!FgTTwbk@_aS+=%KM2k)dIO=+z3fjJ**8t66NGA}JK z`u@5XSqQ{82F_PTYlF9|t7^(hn4Zg6?d=S-zA3*dKb3FZW0J^MKE-z{f*<>gV5iDz=2ov{w46VD02e zQU+LuruFcQo64PDeQ-e~`2iBCU*nTclMj$}uu(h-+;SgaLhWo?(2F`k%|HmnF;)TZ z)@0-p4Av!)fsh&9XgG8kZTML_gn;!g^=2Dx;lo2SSg$ZxOtLTRarkw9INfqz7$pgHkh?%=iuMSLT{_$UUhlW zArLz#aC|Pemhjh9>55_%i;T{sZYM$x7 ze2bMCH_2c%P;?Z1^-WsU?DB?+M!GghL33a}{Lecbya(oSo-U=vV`3g|Qj=qJBl#uZ z(nR+=RIYZI{0V%7o6Yr`y!b<)ip#Bw8#)Z~c%Y~GyqXIV}74BWD}G!zOfsg>Fq4aWl`iTQFGLOSWU=-SfLt5!;< zH|Dm*b`oDnCyLi&Z^$VV16=tGv}TFSE0WflV+{l@dPLpx(m}`ah@J;Fuvh(*ne`dk zi7r(!-g~iHC6XsZHq}j}5i@jign8W6qhua%234B_A`HeXSLOf zS75E};=Y#bn`y&J!A+9SnvE&~tBC$aEjk#W;cj=1{Gh-mvKQ*NTU_MOWl24~P<-_2 z1&apTsqQAe`q#^de)F1afqu1`dp5RvAn0BYN!^FBpWKNeyIhYPkkj&``_3J&d(fvy zo2 zlclDPB2YQMRfm%1NX%4?!U6NKgdM`fV2<};$T`<5X$XoUV zsVL|4KS&iwqTkSObTe+(toV8Vkr=v<^iUbaZ@nM7cAHM6#@ryq`7V2?T@jdg3DSnX zr8C$`x&-%mT{_2J%KMOcW-?Iic$oisvpw3;_jqYva^E7mEHBM=(4oAAZ=)RrZe*ir z!XvCUw(XRK!oMfR`x5T9s0-Gp8Aad;)C zK0iS!va`&#(l{&Zv^-45fxfjKG?s^;w>pctoUyNP39JUf_`sAym!8l3r`C%IBsjFS z(HWTtwHio8Q?m!ounWQe0(-n7Y8`It;cBe2Nw_>!E0x%wE!XtTsCMD0%s0WYlnc(Yw+3!$z1;JA zT)!jHK%B~paB*A=RIxyMCwt=q#7zW##6{;Ckex+K_0$xF+O@s7CPUx`xr0=RZmPO` zs7lbMB(H9$^2(p$jS545nwFeXy=YHsuwBoW+3CzF+k*SBfv-Imv=l1Z5+{j&kUyc_ ziQeGU_reLca&-!_W2S;m&$?TCNpam@ZNy5^$|OKL zkQOqNsz)>N39Kk-uh*kLu5HqS`!1)m>+^W}+v$7ETuo#t?>~85ofl)>;m9dj0e$Uh zQ&wH|wu+&;1jUr6CaOK6rHrES?O1dbmEfN_4flOGyG5SMo1&ji#{zT}>f;yWE#0G; z*ba2ldyz~~5gvvasKbL)3SE;{v7X_R`_vLuR#zu!=|nPFhpOyQwq4;L*mt_t3^%Ju zYj%SsR913_C?i>FVe;<3S@8t}Cl_DwWwmvh&3DaSA3EylZNHgSNp=>e;3c zZ3+LyeegHy=vM0*KTO+`AL6}x8@EapvRr-lo{5LRTkzsUj?N$SIu;sHGy2%J^(USOcJo9$5DrDv^JlpZOn>SL@uuV(GD4)Uy4 zExVfkT}UIlHfFE5wzikU6Q0Kz#?#o3LYjsi@;$~J(~-r{dAz&52pr=wGMou$^%j$s zVAl!MUJuE4`P(Z6x5@>i4-JK9^||+47AIj)4$SvjdLzJ=g&IOJZHh@=3Yiov1eMvv z%HXwNZ?R2XmS?>rDjfTd3)1T7E|r|1K9hDloK(VOjOt4>+S^d^N0HMeKAVYLrUmQ* z>5EjXPpqC(k(WSj)+bCwN6|kwH?HU=&YRS%H{C!coAJQ>^N~+1H30@FqcFEC?6seX zJf91yEWJd-)d}ycd(L|zX2^4DzbG4Q?bcEgup7EN)H5}`T5xdgB2oILye~rZL2`odH@b{;1qZeRTiQcecAlM%0D{=cyrqxz3>gm?XKk;AE{0F_S1BA1xJo-x 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

PpeD2w%F+Y^Fu#2%iss$ls0)9|-?#mVe)H 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øu{sJ-bDX3-X0 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 ?N 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�`-}Lf zy2*Cn@hAN=Zm(D%N2{@*mn+=|nOuMUHpND9{+jK}Q}0BLP)(fGJ@s;smE`O#`L;B( zz)@}tJk_uqr2f?N}__>N?z?mc3Y$p*%L6+w}?5^LPgLjy=nTp@m{LX z88LwwN^_>k``nK*((Vbq;n0z~8ol2cG1nF0?6m}S(Rj0;k<0Zx+&Z=}pI+-8%3eCT zSnVGUZrCI47MZB9jIvw6lfGxq;|FfnxTq;Tq?UaFw}D z9dYxh>{hZD)=tONWChcN>fs~%WXbGF*qBjFB`?!;Ww(RGVRcF*v3t!7|Fw@MtCv`_ z$7ej(l&SngwLpvxYWn9*8&t+#FlQZt_v~HXd3kx-ZfA1RPNoxEQNo&w|7mfRGPF!5 zaDRYu41=e19j5JCcpN)jAMn^zGOvim)U~_o3M#fM7~}7=X<$-@)kG;=RPcbayOSGK z&U_Az|G#qiO78j1&SEQJ6O6rgxU4)eD4nV*($eMp01o|>Dy@vj5%dUepgLV9e|9Fw zZTr#(f2*d#E;b(6X;4Hfg(@3(crllj==OH$2fnM9~Q!=RW+WN_t_UPjomK zL)f&;p~~74;URD(dcdO^BR;Xaw9&Ow7j;S(3Qn1QcC#yk|I978^q=ely~3*tpD-6L z#ieX(GTK0QKIrcD$Ym-Glc|fMoZDp2;M(}mh@g=fBtGz7cMvrT1{L_^RrFplJ{TR` z49bEc6!-ecEbgVfC_MF5#F070TwICMiB#&T=mNr3Rjpw&D3Lk}&XCxY=j{FCGH_=) z+k5T$^>ys+3dL?e5 zBvx78)o(;5^7YTK1#`&hs6&k;BLAhf>6oIOIn2rH&B@r$J6>u(? z>hA?l+6W6Wx5@7|;gqmfeN>~t(OQbuFo({VwKlo%-EFcw8}9BOvsiT3%ftXO$ab=P zLGg)to826f5^PrBMl?XMZ|}b_8B`Y876*ck=+A~_YC4a!u-|Xk%=D#)MM{y4uKAkS z>wV`H6jy94vfoZW&&SqO4uU_m0hNUM3Fu)5B@m;7kYSVz~TRn82wg<6HJao_M$}-e-35k;1ReROhb!40WG5@M0Kf&GVWD<)5`iL3}irmD+2+o>u zqJVm7MUapzyHF(3ap*xinEy;1`J6886YgT`Y-~|b^

v7%{9QbLjCz%KK(~(BBw& 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%0AJndsJo!i- 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

=Su{bbyng?@!EHeuzQ+g@Ik`A|`zy}{1e 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 diff --git a/modules/audio_device/main/test/audio_short8.pcm b/modules/audio_device/main/test/audio_short8.pcm deleted file mode 100644 index 43afd5a182c2d5a0ef1a8ecc91b86336759aa603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321816 zcmX6_1$-3O^MA6tXK^7wa0?W7cc*A^*FtfMyK8~s#oeK}dvVv`?!=Qz?r!bLfB5~g zpXBcL_Sww5c_ZJ6loUA;LVg*L2w@bALQyozg9@OcC^xErnxbkb2IW9`Q9cxfT*xcE z(t#q88=3NlOq42$KukWBpCmyl3dkh+U4E5mGDD`z&+;|=jYavPWjZ`z!ZSfImL3>8P^V9nw16ocNQpjQQGi{O4y=syW)pDq>p8nuEM|NMzVJ>YYid;6qM*IoG%MtK8$yoY)I z0DAjC3JA3G1GR0S0h3`cViNG{p?o1f0Qa6kn^buEjl3pLgM2)bDZrcmpzpgdM!dWZ z(sM=LhaP`GyZ0bPZ{T|qWCg+cU9g&LunH&4&Icp?yf}=T{67hL01|Q@=5-IoxCiq* z553)l5r4q=$uJ`+f3EBu^!Xh=86Z0Z_C#KweHc(90w|AwhF?J965**YAXSIt8JP29 z=Eyh`Yq0DU8&$DguN2)I)St^)8I`#)ai z1Umn`Qy%1^DyjvXj)ql*!%RxSr#isv!q6rgv@Zs~q3}%%v?v0fezp$-iK8Hg1V*R~ zJyZuC7XwMI{y&*11MN$ritxD(Y7R2-E2@DS!JQHy!A0S>G<;JSB(VfM9S6@9{@(}) zsOSKxHh_Zy_DN2lwG*Do58s!DIaP+X#zE_R@U94aUm1Af1i8!u6!^)N zpY;0#JDdT15Zp7MM*-5H0A&Q+^#bjGawG+~fI!0-@aZl{Zam1x2jKQop!b#kQT`rC zRw8_x1|0qf)c*!8(tz>`vOzY!LO?1#*mFd>uwgf!^ zx&ZI5!QbQZfZPSI_kp)RCHbm62KV;LQ}Qs3`WX880;7HU-*}(l?|WEF3P^7{$O{Mh zyodFD{GUw!)M5_Mel}dX42RvRgOup7A}&K=?G~&z4J76}^r^$TGhvnic-88^)9dk@@pz-t^l_jBCr!1rt*nJVx%7hJjFHv(n_1c9HQ&$>ZQ zqyH!WF|a2H^x%e1KQ&z>yyk?qu^@xRK!S2WztPa|&(VK=jszL{c@m`oe^Y=`Keg9S zO6t%r2fF<{m3~q}0#&~OuhM}(X+XzM(CR&W{sOJi;GQ2^nb1BFc;y8u^8d|C{ErI~ z_?QCUX2O*OJo^YO0`SDoz57#h{(yU5pogFIRzbRTxR(IWW`PFy3{v`&mtUa8S9t!X zr2o`Ip`hvf|KoHL^mhj&>oG{mPYI&1n{oop5XypkC*&5nOs}44ed=&`{JD zxRC?Cj|ZvT2a%q6>G(dT1&d2)`@vF=CQu*p0`*nJ@!rus{qI-DN4X1!bWV z=o(sx1@>cHP%Xz%0FUQDjva_5bm*e8}6wJFEDlRX| zLSzgrMxM*DpnGlsB_qUC9wxit1Uv<86q7`}%!`VN1I){g^5Y-_@!ZRT5hDYl%Ke4>E

B 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 Az*YUzK&1H*?UeDXn#C&k54`~<$8 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

pE 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

k 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 diff --git a/modules/audio_device/main/test/func_test_manager.cc b/modules/audio_device/main/test/func_test_manager.cc deleted file mode 100644 index 9772af454..000000000 --- a/modules/audio_device/main/test/func_test_manager.cc +++ /dev/null @@ -1,2752 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include - -#include "func_test_manager.h" - -#include "../source/audio_device_config.h" - -// Disable warning message ('sprintf': name was marked as #pragma deprecated) -#pragma warning( disable : 4995 ) -// Disable warning message 4996 ('scanf': This function or variable may be unsafe) -#pragma warning( disable : 4996 ) - -const WebRtc_Word8 PlayoutFile48[] = "audio_short48.pcm"; -const WebRtc_Word8 PlayoutFile44[] = "audio_short44.pcm"; -const WebRtc_Word8 PlayoutFile16[] = "audio_short16.pcm"; -const WebRtc_Word8 PlayoutFile8[] = "audio_short8.pcm"; -const WebRtc_Word8 RecordedMicrophoneFile[] = "recorded_microphone_mono_48.pcm"; -const WebRtc_Word8 RecordedMicrophoneVolumeFile[] = - "recorded_microphone_volume_mono_48.pcm"; -const WebRtc_Word8 RecordedMicrophoneMuteFile[] = - "recorded_microphone_mute_mono_48.pcm"; -const WebRtc_Word8 RecordedMicrophoneBoostFile[] = - "recorded_microphone_boost_mono_48.pcm"; -const WebRtc_Word8 RecordedMicrophoneAGCFile[] = - "recorded_microphone_AGC_mono_48.pcm"; -const WebRtc_Word8 RecordedSpeakerFile[] = "recorded_speaker_48.pcm"; -const WebRtc_Word8 ReadMeFile[] = "README.txt"; - -struct AudioPacket -{ - WebRtc_UWord8 dataBuffer[4 * 960]; - WebRtc_UWord16 nSamples; - WebRtc_UWord16 nBytesPerSample; - WebRtc_UWord8 nChannels; - WebRtc_UWord32 samplesPerSec; -}; - -// Helper functions -#if !defined(MAC_IPHONE) && !defined(ANDROID) -char* GetFilename(char* filename) -{ - return filename; -} -const char* GetFilename(const char* filename) -{ - return filename; -} -char* GetResource(char* resource) -{ - return resource; -} -const char* GetResource(const char* resource) -{ - return resource; -} -#endif - -namespace webrtc -{ - -AudioEventObserver::AudioEventObserver(AudioDeviceModule* audioDevice) : - _audioDevice(audioDevice) -{ -} -; - -AudioEventObserver::~AudioEventObserver() -{ -} -; - -void AudioEventObserver::OnErrorIsReported(const ErrorCode error) -{ - TEST_LOG("\n[*** ERROR ***] => OnErrorIsReported(%d)\n \n", error); - _error = error; - // TEST(_audioDevice->StopRecording() == 0); - // TEST(_audioDevice->StopPlayout() == 0); -} -; - - -void AudioEventObserver::OnWarningIsReported(const WarningCode warning) -{ - TEST_LOG("\n[*** WARNING ***] => OnWarningIsReported(%d)\n \n", warning); - _warning = warning; - //TEST(_audioDevice->StopRecording() == 0); - //TEST(_audioDevice->StopPlayout() == 0); -} -; - -AudioTransportImpl::AudioTransportImpl(AudioDeviceModule* audioDevice) : - _audioDevice(audioDevice), _playFromFile(false), _fullDuplex(false), - _speakerVolume(false), _microphoneVolume(false), _speakerMute(false), - _microphoneMute(false), _microphoneBoost(false), - _loopBackMeasurements(false), _microphoneAGC(false), _recCount(0), - _playCount(0), _playFile(*FileWrapper::Create()), _audioList() -{ - _resampler.Reset(48000, 48000, kResamplerSynchronousStereo); -} -; - -AudioTransportImpl::~AudioTransportImpl() -{ - _playFile.Flush(); - _playFile.CloseFile(); - delete &_playFile; - - while (!_audioList.Empty()) - { - ListItem* item = _audioList.First(); - if (item) - { - AudioPacket* packet = static_cast (item->GetItem()); - if (packet) - { - delete packet; - } - } - _audioList.PopFront(); - } -} -; - -// ---------------------------------------------------------------------------- -// AudioTransportImpl::SetFilePlayout -// ---------------------------------------------------------------------------- - -WebRtc_Word32 AudioTransportImpl::SetFilePlayout(bool enable, - const WebRtc_Word8* fileName) -{ - _playFromFile = enable; - if (enable) - { - return (_playFile.OpenFile(fileName, true, true, false)); - } else - { - _playFile.Flush(); - return (_playFile.CloseFile()); - } -} -; - -void AudioTransportImpl::SetFullDuplex(bool enable) -{ - _fullDuplex = enable; - - while (!_audioList.Empty()) - { - ListItem* item = _audioList.First(); - if (item) - { - AudioPacket* packet = static_cast (item->GetItem()); - if (packet) - { - delete packet; - } - } - _audioList.PopFront(); - } -} -; - -WebRtc_Word32 AudioTransportImpl::RecordedDataIsAvailable( - const WebRtc_Word8* audioSamples, - const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 samplesPerSec, - const WebRtc_UWord32 totalDelayMS, - const WebRtc_Word32 clockDrift, - const WebRtc_UWord32 currentMicLevel, - WebRtc_UWord32& newMicLevel) -{ - WebRtc_UWord32 samples(nSamples); - WebRtc_UWord32 fs(samplesPerSec); - - if (_fullDuplex && _audioList.GetSize() < 15) - { - AudioPacket* packet = new AudioPacket(); - memcpy(packet->dataBuffer, audioSamples, nSamples * nBytesPerSample); - packet->nSamples = (WebRtc_UWord16) nSamples; - packet->nBytesPerSample = nBytesPerSample; - packet->nChannels = nChannels; - packet->samplesPerSec = samplesPerSec; - _audioList.PushBack(packet); - } - - _recCount++; - if (_recCount % 100 == 0) - { - bool addMarker(true); - - if (_loopBackMeasurements) - { - addMarker = false; - } - - if (_microphoneVolume) - { - WebRtc_UWord32 maxVolume(0); - WebRtc_UWord32 minVolume(0); - WebRtc_UWord32 volume(0); - WebRtc_UWord16 stepSize(0); - TEST(_audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(_audioDevice->MinMicrophoneVolume(&minVolume) == 0); - TEST(_audioDevice->MicrophoneVolumeStepSize(&stepSize) == 0); - TEST(_audioDevice->MicrophoneVolume(&volume) == 0); - if (volume == 0) - { - TEST_LOG("[0]"); - addMarker = false; - } - int stepScale = (int) ((maxVolume - minVolume) / (stepSize * 10)); - volume += (stepScale * stepSize); - if (volume > maxVolume) - { - TEST_LOG("[MAX]"); - volume = 0; - addMarker = false; - } - TEST(_audioDevice->SetMicrophoneVolume(volume) == 0); - } - - if (_microphoneAGC) - { - WebRtc_UWord32 maxVolume(0); - WebRtc_UWord32 minVolume(0); - WebRtc_UWord16 stepSize(0); - TEST(_audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(_audioDevice->MinMicrophoneVolume(&minVolume) == 0); - TEST(_audioDevice->MicrophoneVolumeStepSize(&stepSize) == 0); - // emulate real AGC (min->max->min->max etc.) - if (currentMicLevel <= 1) - { - TEST_LOG("[MIN]"); - addMarker = false; - } - int stepScale = (int) ((maxVolume - minVolume) / (stepSize * 10)); - newMicLevel = currentMicLevel + (stepScale * stepSize); - if (newMicLevel > maxVolume) - { - TEST_LOG("[MAX]"); - newMicLevel = 1; // set lowest (non-zero) AGC level - addMarker = false; - } - } - - if (_microphoneMute && (_recCount % 500 == 0)) - { - bool muted(false); - TEST(_audioDevice->MicrophoneMute(&muted) == 0); - muted = !muted; - TEST(_audioDevice->SetMicrophoneMute(muted) == 0); - if (muted) - { - TEST_LOG("[MUTE ON]"); - addMarker = false; - } else - { - TEST_LOG("[MUTE OFF]"); - addMarker = false; - } - } - - if (_microphoneBoost && (_recCount % 500 == 0)) - { - bool boosted(false); - TEST(_audioDevice->MicrophoneBoost(&boosted) == 0); - boosted = !boosted; - TEST(_audioDevice->SetMicrophoneBoost(boosted) == 0); - if (boosted) - { - TEST_LOG("[BOOST ON]"); - addMarker = false; - } else - { - TEST_LOG("[BOOST OFF]"); - addMarker = false; - } - } - - if ((nChannels == 1) && addMarker) - { - // mono - TEST_LOG("-"); - } else if ((nChannels == 2) && (nBytesPerSample == 2) && addMarker) - { - AudioDeviceModule::ChannelType - chType(AudioDeviceModule::kChannelLeft); - TEST(_audioDevice->RecordingChannel(&chType) == 0); - if (chType == AudioDeviceModule::kChannelLeft) - TEST_LOG("-|"); - else - TEST_LOG("|-"); - } else if (addMarker) - { - // stereo - TEST_LOG("--"); - } - - if (nChannels == 2 && nBytesPerSample == 2) - { - // TEST_LOG("=> emulated mono (one channel exctracted from stereo input)\n"); - } - } - - return 0; -} - - -WebRtc_Word32 AudioTransportImpl::NeedMorePlayData( - const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 samplesPerSec, - WebRtc_Word8* audioSamples, - WebRtc_UWord32& nSamplesOut) -{ - if (_fullDuplex) - { - if (_audioList.Empty()) - { - // use zero stuffing when not enough data - memset(audioSamples, 0, nBytesPerSample * nSamples); - } else - { - ListItem* item = _audioList.First(); - AudioPacket* packet = static_cast (item->GetItem()); - if (packet) - { - int ret(0); - int lenOut(0); - WebRtc_Word16 tmpBuf_96kHz[80 * 12]; - WebRtc_Word16* ptr16In = NULL; - WebRtc_Word16* ptr16Out = NULL; - - const WebRtc_UWord16 nSamplesIn = packet->nSamples; - const WebRtc_UWord16 nBytesIn = nSamplesIn - * packet->nBytesPerSample; - const WebRtc_UWord8 nChannelsIn = packet->nChannels; - const WebRtc_UWord32 samplesPerSecIn = packet->samplesPerSec; - const WebRtc_UWord16 nBytesPerSampleIn = - packet->nBytesPerSample; - - WebRtc_Word32 fsInHz(samplesPerSecIn); - WebRtc_Word32 fsOutHz(samplesPerSec); - - if (fsInHz == 44100) - fsInHz = 44000; - - if (fsOutHz == 44100) - fsOutHz = 44000; - - if (nChannelsIn == 2 && nBytesPerSampleIn == 4) - { - // input is stereo => we will resample in stereo - ret = _resampler.ResetIfNeeded(fsInHz, fsOutHz, - kResamplerSynchronousStereo); - if (ret == 0) - { - if (nChannels == 2) - { - _resampler.Push( - (const WebRtc_Word16*) packet->dataBuffer, - 2 * nSamplesIn, - (WebRtc_Word16*) audioSamples, 2 - * nSamples, lenOut); - } else - { - _resampler.Push( - (const WebRtc_Word16*) packet->dataBuffer, - 2 * nSamplesIn, tmpBuf_96kHz, 2 - * nSamples, lenOut); - - ptr16In = &tmpBuf_96kHz[0]; - ptr16Out = (WebRtc_Word16*) audioSamples; - - // do stereo -> mono - for (unsigned int i = 0; i < nSamples; i++) - { - *ptr16Out = *ptr16In; // use left channel - ptr16Out++; - ptr16In++; - ptr16In++; - } - } - assert(2*nSamples == (WebRtc_UWord32)lenOut); - } else - { - if (_playCount % 100 == 0) - TEST_LOG( - "ERROR: unable to resample from %d to %d\n", - samplesPerSecIn, samplesPerSec); - } - } else - { - // input is mono (can be "reduced from stereo" as well) => - // we will resample in mono - ret = _resampler.ResetIfNeeded(fsInHz, fsOutHz, - kResamplerSynchronous); - if (ret == 0) - { - if (nChannels == 1) - { - _resampler.Push( - (const WebRtc_Word16*) packet->dataBuffer, - nSamplesIn, - (WebRtc_Word16*) audioSamples, - nSamples, lenOut); - } else - { - _resampler.Push( - (const WebRtc_Word16*) packet->dataBuffer, - nSamplesIn, tmpBuf_96kHz, nSamples, - lenOut); - - ptr16In = &tmpBuf_96kHz[0]; - ptr16Out = (WebRtc_Word16*) audioSamples; - - // do mono -> stereo - for (unsigned int i = 0; i < nSamples; i++) - { - *ptr16Out = *ptr16In; // left - ptr16Out++; - *ptr16Out = *ptr16In; // right (same as left sample) - ptr16Out++; - ptr16In++; - } - } - assert(nSamples == (WebRtc_UWord32)lenOut); - } else - { - if (_playCount % 100 == 0) - TEST_LOG("ERROR: unable to resample from %d to %d\n", - samplesPerSecIn, samplesPerSec); - } - } - nSamplesOut = nSamples; - delete packet; - } - _audioList.PopFront(); - } - } // if (_fullDuplex) - - if (_playFromFile && _playFile.Open()) - { - WebRtc_Word16 fileBuf[480]; - - // read mono-file - WebRtc_Word32 len = _playFile.Read((WebRtc_Word8*) fileBuf, 2 - * nSamples); - if (len != 2 * (WebRtc_Word32) nSamples) - { - _playFile.Rewind(); - _playFile.Read((WebRtc_Word8*) fileBuf, 2 * nSamples); - } - - // convert to stero if required - if (nChannels == 1) - { - memcpy(audioSamples, fileBuf, 2 * nSamples); - } else - { - // mono sample from file is duplicated and sent to left and right - // channels - WebRtc_Word16* audio16 = (WebRtc_Word16*) audioSamples; - for (unsigned int i = 0; i < nSamples; i++) - { - (*audio16) = fileBuf[i]; // left - audio16++; - (*audio16) = fileBuf[i]; // right - audio16++; - } - } - } // if (_playFromFile && _playFile.Open()) - - _playCount++; - - if (_playCount % 100 == 0) - { - bool addMarker(true); - - if (_speakerVolume) - { - WebRtc_UWord32 maxVolume(0); - WebRtc_UWord32 minVolume(0); - WebRtc_UWord32 volume(0); - WebRtc_UWord16 stepSize(0); - TEST(_audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(_audioDevice->MinSpeakerVolume(&minVolume) == 0); - TEST(_audioDevice->SpeakerVolumeStepSize(&stepSize) == 0); - TEST(_audioDevice->SpeakerVolume(&volume) == 0); - if (volume == 0) - { - TEST_LOG("[0]"); - addMarker = false; - } - WebRtc_UWord32 step = (maxVolume - minVolume) / 10; - step = (step < stepSize ? stepSize : step); - volume += step; - if (volume > maxVolume) - { - TEST_LOG("[MAX]"); - volume = 0; - addMarker = false; - } - TEST(_audioDevice->SetSpeakerVolume(volume) == 0); - } - - if (_speakerMute && (_playCount % 500 == 0)) - { - bool muted(false); - TEST(_audioDevice->SpeakerMute(&muted) == 0); - muted = !muted; - TEST(_audioDevice->SetSpeakerMute(muted) == 0); - if (muted) - { - TEST_LOG("[MUTE ON]"); - addMarker = false; - } else - { - TEST_LOG("[MUTE OFF]"); - addMarker = false; - } - } - - if (_loopBackMeasurements) - { - WebRtc_UWord16 recDelayMS(0); - WebRtc_UWord16 playDelayMS(0); - WebRtc_UWord32 nItemsInList(0); - - nItemsInList = _audioList.GetSize(); - TEST(_audioDevice->RecordingDelay(&recDelayMS) == 0); - TEST(_audioDevice->PlayoutDelay(&playDelayMS) == 0); - TEST_LOG("Delay (rec+play)+buf: %3u (%3u+%3u)+%3u [ms]\n", - recDelayMS + playDelayMS + 10 * (nItemsInList + 1), - recDelayMS, playDelayMS, 10 * (nItemsInList + 1)); - - addMarker = false; - } - - if ((nChannels == 1) && addMarker) - { - TEST_LOG("+"); - } else if ((nChannels == 2) && addMarker) - { - TEST_LOG("++"); - } - } // if (_playCount % 100 == 0) - - nSamplesOut = nSamples; - - return 0; -} -; - -FuncTestManager::FuncTestManager() : - _audioDevice(NULL), _processThread(NULL), _audioEventObserver(NULL), - _audioTransport(NULL) -{ -} -; - -FuncTestManager::~FuncTestManager() -{ -} -; - -WebRtc_Word32 FuncTestManager::Init() -{ - TEST((_processThread = ProcessThread::CreateProcessThread()) != NULL); - if (_processThread == NULL) - { - return -1; - } - _processThread->Start(); - - // create the Audio Device module - TEST((_audioDevice = AudioDeviceModule::Create(555, ADM_AUDIO_LAYER)) != NULL); - if (_audioDevice == NULL) - { - return -1; - } - - // register the Audio Device module - _processThread->RegisterModule(_audioDevice); - - // register event observer - _audioEventObserver = new AudioEventObserver(_audioDevice); - TEST(_audioDevice->RegisterEventObserver(_audioEventObserver) == 0); - - // register audio transport - _audioTransport = new AudioTransportImpl(_audioDevice); - TEST(_audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - WebRtc_Word8 version[256]; - WebRtc_UWord32 remainingBufferInBytes = 256; - WebRtc_UWord32 tooFewBytes = 10; - WebRtc_UWord32 position = 0; - - // log version - TEST(_audioDevice->Version(version, remainingBufferInBytes, position) == 0); - TEST_LOG("Version: %s\n \n", version); - - return 0; -} - -WebRtc_Word32 FuncTestManager::Close() -{ - TEST(_audioDevice->RegisterEventObserver(NULL) == 0); - TEST(_audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(_audioDevice->Terminate() == 0); - - // release the ProcessThread object - if (_processThread) - { - _processThread->DeRegisterModule(_audioDevice); - _processThread->Stop(); - ProcessThread::DestroyProcessThread(_processThread); - } - - // delete the audio observer - if (_audioEventObserver) - { - delete _audioEventObserver; - _audioEventObserver = NULL; - } - - // delete the audio transport - if (_audioTransport) - { - delete _audioTransport; - _audioTransport = NULL; - } - - // release the AudioDeviceModule object - if (_audioDevice) - { - AudioDeviceModule::Destroy(_audioDevice); - _audioDevice = NULL; - } - - // return the ThreadWrapper (singleton) - Trace::ReturnTrace(); - - // PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::DoTest(const TestType testType) -{ - WebRtc_UWord32 ret(0); - - switch (testType) - { - case TTAll: - ret = TestAudioLayerSelection(); - ret = TestDeviceEnumeration(); - ret = TestDeviceSelection(); - ret = TestAudioTransport(); - ret = TestSpeakerVolume(); - ret = TestMicrophoneVolume(); - ret = TestLoopback(); - case TTAudioLayerSelection: - TestAudioLayerSelection(); - break; - case TTDeviceEnumeration: - ret = TestDeviceEnumeration(); - break; - case TTDeviceSelection: - ret = TestDeviceSelection(); - break; - case TTAudioTransport: - ret = TestAudioTransport(); - break; - case TTSpeakerVolume: - ret = TestSpeakerVolume(); - break; - case TTMicrophoneVolume: - ret = TestMicrophoneVolume(); - break; - case TTSpeakerMute: - ret = TestSpeakerMute(); - break; - case TTMicrophoneMute: - ret = TestMicrophoneMute(); - break; - case TTMicrophoneBoost: - ret = TestMicrophoneBoost(); - break; - case TTMicrophoneAGC: - ret = TestMicrophoneAGC(); - break; - case TTLoopback: - ret = TestLoopback(); - break; - case TTDeviceRemoval: - ret = TestDeviceRemoval(); - break; - case TTMobileAPI: - ret = TestAdvancedMBAPI(); - case TTTest: - ret = TestExtra(); - break; - default: - break; - } - - return 0; -} -; - -WebRtc_Word32 FuncTestManager::TestAudioLayerSelection() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Audio Layer test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - AudioDeviceModule::AudioLayer audioLayer; - TEST(audioDevice->ActiveAudioLayer(&audioLayer) == 0); - - if (audioLayer == AudioDeviceModule::kWindowsWaveAudio) - { - TEST_LOG("\nActiveAudioLayer: kWindowsWaveAudio\n \n"); - } else if (audioLayer == AudioDeviceModule::kWindowsCoreAudio) - { - TEST_LOG("\nActiveAudioLayer: kWindowsCoreAudio\n \n"); - } else if (audioLayer == AudioDeviceModule::kLinuxAlsaAudio) - { - TEST_LOG("\nActiveAudioLayer: kLinuxAlsaAudio\n \n"); - } else if (audioLayer == AudioDeviceModule::kLinuxPulseAudio) - { - TEST_LOG("\nActiveAudioLayer: kLinuxPulseAudio\n \n"); - } else - { - TEST_LOG("\nActiveAudioLayer: INVALID\n \n"); - } - - char ch; - int dummy(0); - bool tryWinWave(false); - bool tryWinCore(false); - - if (audioLayer == AudioDeviceModule::kWindowsWaveAudio) - { - TEST_LOG("Would you like to try kWindowsCoreAudio instead " - "[requires Win Vista or Win 7] (Y/N)?\n: "); - dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - tryWinCore = true; - } - } else if (audioLayer == AudioDeviceModule::kWindowsCoreAudio) - { - TEST_LOG("Would you like to try kWindowsWaveAudio instead (Y/N)?\n: "); - int dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - tryWinWave = true; - } - } - - if (tryWinWave || tryWinCore) - { - // ======================================= - // First, close down what we have started - - // terminate - TEST(_audioDevice->RegisterEventObserver(NULL) == 0); - TEST(_audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(_audioDevice->Terminate() == 0); - - // release the ProcessThread object - if (_processThread) - { - _processThread->DeRegisterModule(_audioDevice); - _processThread->Stop(); - ProcessThread::DestroyProcessThread(_processThread); - } - - // delete the audio observer - if (_audioEventObserver) - { - delete _audioEventObserver; - _audioEventObserver = NULL; - } - - // delete the audio transport - if (_audioTransport) - { - delete _audioTransport; - _audioTransport = NULL; - } - - // release the AudioDeviceModule object - if (_audioDevice) - { - AudioDeviceModule::Destroy(_audioDevice); - _audioDevice = NULL; - } - - // ================================================== - // Next, try to make fresh start with new audio layer - - TEST((_processThread = ProcessThread::CreateProcessThread()) != NULL); - if (_processThread == NULL) - { - return -1; - } - _processThread->Start(); - - // create the Audio Device module based on selected audio layer - if (tryWinWave) - { - _audioDevice = AudioDeviceModule::Create( - 555, - AudioDeviceModule::kWindowsWaveAudio); - } else if (tryWinCore) - { - _audioDevice = AudioDeviceModule::Create( - 555, - AudioDeviceModule::kWindowsCoreAudio); - } - - if (_audioDevice == NULL) - { - TEST_LOG("\nERROR: Switch of audio layer failed!\n"); - // restore default audio layer instead - TEST((_audioDevice = AudioDeviceModule::Create( - 555, AudioDeviceModule::kPlatformDefaultAudio)) != NULL); - } - - if (_audioDevice == NULL) - { - TEST_LOG("\nERROR: Failed to revert back to default audio layer!\n"); - return -1; - } - - // register the Audio Device module - _processThread->RegisterModule(_audioDevice); - - // register event observer - _audioEventObserver = new AudioEventObserver(_audioDevice); - TEST(_audioDevice->RegisterEventObserver(_audioEventObserver) == 0); - - // register audio transport - _audioTransport = new AudioTransportImpl(_audioDevice); - TEST(_audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - TEST(_audioDevice->ActiveAudioLayer(&audioLayer) == 0); - - if (audioLayer == AudioDeviceModule::kWindowsWaveAudio) - { - if (tryWinCore) - TEST_LOG("\nActiveAudioLayer: kWindowsWaveAudio <=> " - "switch was *not* possible\n \n"); - else - TEST_LOG("\nActiveAudioLayer: kWindowsWaveAudio <=> " - "switch was possible\n \n"); - } else if (audioLayer == AudioDeviceModule::kWindowsCoreAudio) - { - if (tryWinWave) - TEST_LOG("\nActiveAudioLayer: kWindowsCoreAudio <=> " - "switch was *not* possible\n \n"); - else - TEST_LOG("\nActiveAudioLayer: kWindowsCoreAudio <=> " - "switch was possible\n \n"); - } - } // if (tryWinWave || tryWinCore) - - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestDeviceEnumeration() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Device Enumeration test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - WebRtc_Word8 name[kAdmMaxDeviceNameSize]; - WebRtc_Word8 guid[kAdmMaxGuidSize]; - - const WebRtc_Word16 nPlayoutDevices(audioDevice->PlayoutDevices()); - TEST(nPlayoutDevices >= 0); - TEST_LOG("\nPlayoutDevices: %u\n \n", nPlayoutDevices); - for (int n = 0; n < nPlayoutDevices; n++) - { - TEST(audioDevice->PlayoutDeviceName(n, name, guid) == 0); - TEST_LOG( - "PlayoutDeviceName(%d) : name=%s \n \ - guid=%s\n", - n, name, guid); - } - -#ifdef _WIN32 - // default (-1) - TEST(audioDevice->PlayoutDeviceName(-1, name, guid) == 0); - TEST_LOG("PlayoutDeviceName(%d): default name=%s \n \ - default guid=%s\n", -1, name, guid); -#else - // should fail - TEST(audioDevice->PlayoutDeviceName(-1, name, guid) == -1); -#endif - - const WebRtc_Word16 nRecordingDevices(audioDevice->RecordingDevices()); - TEST(nRecordingDevices >= 0); - TEST_LOG("\nRecordingDevices: %u\n \n", nRecordingDevices); - for (int n = 0; n < nRecordingDevices; n++) - { - TEST(audioDevice->RecordingDeviceName(n, name, guid) == 0); - TEST_LOG( - "RecordingDeviceName(%d) : name=%s \n \ - guid=%s\n", - n, name, guid); - } - -#ifdef _WIN32 - // default (-1) - TEST(audioDevice->RecordingDeviceName(-1, name, guid) == 0); - TEST_LOG("RecordingDeviceName(%d): default name=%s \n \ - default guid=%s\n", -1, name, guid); -#else - // should fail - TEST(audioDevice->PlayoutDeviceName(-1, name, guid) == -1); -#endif - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestDeviceSelection() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Device Selection test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - -#define PRINT_HEADING(a, b) \ - { \ - TEST_LOG("Set" #a "Device(" #b ") => \n"); \ - } \ - -#define PRINT_HEADING_IDX(a, b,c ) \ - { \ - TEST_LOG("Set" #a "Device(%d) (%s) => \n", b, c); \ - } \ - -#define PRINT_STR(a, b) \ - { \ - char str[128]; \ - (b == true) ? (sprintf(str, " %-17s: available\n", #a)) : (sprintf(str, " %-17s: NA\n", #a)); \ - TEST_LOG(str); \ - } \ - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - bool available(false); - WebRtc_Word16 nDevices(-1); - WebRtc_Word8 name[kAdmMaxDeviceNameSize]; - WebRtc_Word8 guid[kAdmMaxGuidSize]; - - // ======= - // Playout - - nDevices = audioDevice->PlayoutDevices(); - TEST(nDevices >= 0); - - TEST_LOG("\n"); -#ifdef _WIN32 - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - PRINT_HEADING(Playout, kDefaultCommunicationDevice); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - PRINT_STR(Playout, available); - if (available) - { - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - PRINT_STR(Stereo Playout, available); - } - else - { - PRINT_STR(Stereo Playout, false); - } - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - PRINT_STR(Speaker, available); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - PRINT_STR(Speaker Volume, available); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - PRINT_STR(Speaker Mute, available); - - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == 0); - PRINT_HEADING(Playout, kDefaultDevice); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - PRINT_STR(Playout, available); - if (available) - { - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - PRINT_STR(Stereo Playout, available); - } - else - { - PRINT_STR(Stereo Playout, false); - } - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - PRINT_STR(Speaker, available); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - PRINT_STR(Speaker Volume, available); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - PRINT_STR(Speaker Mute, available); -#else - TEST(audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == -1); - TEST(audioDevice->SetPlayoutDevice(AudioDeviceModule::kDefaultDevice) == -1); -#endif - - for (int i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetPlayoutDevice(i) == 0); - TEST(audioDevice->PlayoutDeviceName(i, name, guid) == 0); - PRINT_HEADING_IDX(Playout, i, name); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - PRINT_STR(Playout, available); - if (available) - { - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - PRINT_STR(Stereo Playout, available); - } else - { - PRINT_STR(Stereo Playout, false); - } - TEST(audioDevice->SpeakerIsAvailable(&available) == 0); - PRINT_STR(Speaker, available); - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - PRINT_STR(Speaker Volume, available); - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - PRINT_STR(Speaker Mute, available); - } - - // ========= - // Recording - - nDevices = audioDevice->RecordingDevices(); - TEST(nDevices >= 0); - - TEST_LOG("\n"); -#ifdef _WIN32 - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == 0); - PRINT_HEADING(Recording, kDefaultCommunicationDevice); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - PRINT_STR(Recording, available); - if (available) - { - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - PRINT_STR(Stereo Recording, available); - } - else - { - // special fix to ensure that we don't log 'available' when recording is not OK - PRINT_STR(Stereo Recording, false); - } - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - PRINT_STR(Microphone, available); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - PRINT_STR(Microphone Volume, available); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - PRINT_STR(Microphone Mute, available); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - PRINT_STR(Microphone Boost, available); - - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice) == 0); - PRINT_HEADING(Recording, kDefaultDevice); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - PRINT_STR(Recording, available); - if (available) - { - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - PRINT_STR(Stereo Recording, available); - } - else - { - // special fix to ensure that we don't log 'available' when recording is not OK - PRINT_STR(Stereo Recording, false); - } - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - PRINT_STR(Microphone, available); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - PRINT_STR(Microphone Volume, available); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - PRINT_STR(Microphone Mute, available); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - PRINT_STR(Microphone Boost, available); -#else - TEST(audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice) == -1); - TEST(audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice) == -1); -#endif - - for (int i = 0; i < nDevices; i++) - { - TEST(audioDevice->SetRecordingDevice(i) == 0); - TEST(audioDevice->RecordingDeviceName(i, name, guid) == 0); - PRINT_HEADING_IDX(Recording, i, name); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - PRINT_STR(Recording, available); - if (available) - { - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - PRINT_STR(Stereo Recording, available); - } else - { - // special fix to ensure that we don't log 'available' when recording - // is not OK - PRINT_STR(Stereo Recording, false); - } - TEST(audioDevice->MicrophoneIsAvailable(&available) == 0); - PRINT_STR(Microphone, available); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - PRINT_STR(Microphone Volume, available); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - PRINT_STR(Microphone Mute, available); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - PRINT_STR(Microphone Boost, available); - } - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestAudioTransport() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Audio Transport test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - bool recIsAvailable(false); - bool playIsAvailable(false); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->RecordingIsAvailable(&recIsAvailable) == 0); - if (!recIsAvailable) - { - TEST_LOG( - "\nWARNING: Recording is not available for the selected device!\n \n"); - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&playIsAvailable) == 0); - if (recIsAvailable && playIsAvailable) - { - _audioTransport->SetFullDuplex(true); - } else if (!playIsAvailable) - { - TEST_LOG( - "\nWARNING: Playout is not available for the selected device!\n \n"); - } - - bool available(false); - WebRtc_UWord32 samplesPerSec(0); - - if (playIsAvailable) - { - // ========================================= - // Start by playing out an existing PCM file - - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - WebRtc_UWord32 maxVolume(0); - TEST(audioDevice->MaxSpeakerVolume(&maxVolume) == 0); - TEST(audioDevice->SetSpeakerVolume(maxVolume/2) == 0); - } - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&samplesPerSec) == 0); - if (samplesPerSec == 48000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile48)); - else if (samplesPerSec == 44100 || samplesPerSec == 44000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile44)); - else if (samplesPerSec == 16000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile16)); - else if (samplesPerSec == 8000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile8)); - else - { - TEST_LOG("\nERROR: Sample rate (%u) is not supported!\n \n", - samplesPerSec); - return -1; - } - TEST(audioDevice->StartPlayout() == 0); - - if (audioDevice->Playing()) - { - TEST_LOG("\n> Listen to the file being played (fs=%d) out " - "and verify that the audio quality is OK.\n" - "> Press any key to stop playing...\n \n", - samplesPerSec); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFilePlayout(false); - } - - bool enabled(false); - if (recIsAvailable) - { - // ==================================== - // Next, record from microphone to file - - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - WebRtc_UWord32 maxVolume(0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->SetMicrophoneVolume(maxVolume) == 0); - } - - TEST(audioDevice->StartRawInputFileRecording( - GetFilename(RecordedMicrophoneFile)) == 0); - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - // ensure file recording in mono - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelLeft) == 0); - } - TEST(audioDevice->StartRecording() == 0); - AudioDeviceUtility::Sleep(100); - - TEST(audioDevice->Recording() == true); - if (audioDevice->Recording()) - { - TEST_LOG("\n \n> The microphone input signal is now being recorded " - "to a PCM file.\n" - "> Speak into the microphone to ensure that your voice is" - " recorded.\n> Press any key to stop recording...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelBoth) == 0); - } - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(audioDevice->StopRawInputFileRecording() == 0); - } - - if (recIsAvailable && playIsAvailable) - { - // ========================== - // Play out the recorded file - - _audioTransport->SetFilePlayout(true, - GetFilename(RecordedMicrophoneFile)); - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - AudioDeviceUtility::Sleep(100); - } - - TEST(audioDevice->Playing() == true); - if (audioDevice->Playing()) - { - TEST_LOG("\n \n> Listen to the recorded file and verify that the " - "audio quality is OK.\n" - "> Press any key to stop listening...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFilePlayout(false); - } - - if (recIsAvailable && playIsAvailable) - { - // ============================== - // Finally, make full duplex test - - WebRtc_UWord32 playSamplesPerSec(0); - WebRtc_UWord32 recSamplesPerSecRec(0); - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - _audioTransport->SetFullDuplex(true); - - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - WebRtc_UWord32 maxVolume(0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->SetMicrophoneVolume(maxVolume) == 0); - } - - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&playSamplesPerSec) == 0); - TEST(audioDevice->RecordingSampleRate(&recSamplesPerSecRec) == 0); - if (playSamplesPerSec != recSamplesPerSecRec) - { - TEST_LOG("\nERROR: sample rates does not match (fs_play=%u, fs_rec=%u)", - playSamplesPerSec, recSamplesPerSecRec); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - _audioTransport->SetFullDuplex(false); - return -1; - } - - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->StartPlayout() == 0); - AudioDeviceUtility::Sleep(100); - - if (audioDevice->Playing() && audioDevice->Recording()) - { - TEST_LOG("\n \n> Full duplex audio (fs=%u) is now active.\n" - "> Speak into the microphone and verify that your voice is " - "played out in loopback.\n> Press any key to stop...\n \n", - playSamplesPerSec); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFullDuplex(false); - } - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestSpeakerVolume() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Speaker Volume test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - WebRtc_UWord32 startVolume(0); - WebRtc_UWord32 samplesPerSec(0); - - TEST(audioDevice->SpeakerVolumeIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetSpeakerVolume(true); - } else - { - TEST_LOG("\nERROR: Volume control is not available for the selected " - "device!\n \n"); - return -1; - } - - // store initial volume setting - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SpeakerVolume(&startVolume) == 0); - - // start at volume 0 - TEST(audioDevice->SetSpeakerVolume(0) == 0); - - // ====================================== - // Start playing out an existing PCM file - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&samplesPerSec) == 0); - if (48000 == samplesPerSec) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile48)); - else if (44100 == samplesPerSec || samplesPerSec == 44000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile44)); - else if (samplesPerSec == 16000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile16)); - else if (samplesPerSec == 8000) - _audioTransport->SetFilePlayout(true, GetResource(PlayoutFile8)); - else - { - TEST_LOG("\nERROR: Sample rate (%d) is not supported!\n \n", - samplesPerSec); - return -1; - } - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->Playing() == true); - if (audioDevice->Playing()) - { - TEST_LOG("\n> Listen to the file being played out and verify that the " - "selected speaker volume is varied between [~0] and [~MAX].\n" - "> The file shall be played out with an increasing volume level " - "correlated to the speaker volume.\n" - "> Press any key to stop playing...\n \n"); - PAUSE(10000); - } - - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetSpeakerVolume(false); - _audioTransport->SetFilePlayout(false); - - // restore volume setting - TEST(audioDevice->SetSpeakerVolume(startVolume) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestSpeakerMute() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Speaker Mute test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - bool startMute(false); - WebRtc_UWord32 samplesPerSec(0); - - TEST(audioDevice->SpeakerMuteIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetSpeakerMute(true); - } else - { - TEST_LOG( - "\nERROR: Mute control is not available for the selected" - " device!\n \n"); - return -1; - } - - // store initial mute setting - TEST(audioDevice->InitSpeaker() == 0); - TEST(audioDevice->SpeakerMute(&startMute) == 0); - - // start with no mute - TEST(audioDevice->SetSpeakerMute(false) == 0); - - // ====================================== - // Start playing out an existing PCM file - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&samplesPerSec) == 0); - if (48000 == samplesPerSec) - _audioTransport->SetFilePlayout(true, PlayoutFile48); - else if (44100 == samplesPerSec || 44000 == samplesPerSec) - _audioTransport->SetFilePlayout(true, PlayoutFile44); - else - { - TEST_LOG("\nERROR: Sample rate (%d) is not supported!\n \n", - samplesPerSec); - return -1; - } - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->Playing() == true); - if (audioDevice->Playing()) - { - TEST_LOG("\n> Listen to the file being played out and verify that the" - " selected speaker mute control is toggled between [MUTE ON] and" - " [MUTE OFF].\n> You should only hear the file during the" - " 'MUTE OFF' periods.\n" - "> Press any key to stop playing...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetSpeakerMute(false); - _audioTransport->SetFilePlayout(false); - - // restore mute setting - TEST(audioDevice->SetSpeakerMute(startMute) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestMicrophoneVolume() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Microphone Volume test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetMicrophoneVolume(true); - } else - { - TEST_LOG("\nERROR: Volume control is not available for the selected " - "device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetFullDuplex(true); - } else - { - TEST_LOG("\nERROR: Playout is not available for the selected " - "device!\n \n"); - return -1; - } - - TEST_LOG("\nEnable recording of microphone input to file (%s) during this" - " test (Y/N)?\n: ", - RecordedMicrophoneVolumeFile); - char ch; - bool fileRecording(false); - int dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - fileRecording = true; - } - - WebRtc_UWord32 startVolume(0); - bool enabled(false); - - // store initial volume setting - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MicrophoneVolume(&startVolume) == 0); - - // start at volume 0 - TEST(audioDevice->SetMicrophoneVolume(0) == 0); - - // ====================================================================== - // Start recording from the microphone while the mic volume is changed - // continuously. - // Also, start playing out the input to enable real-time verification. - - if (fileRecording) - { - TEST(audioDevice->StartRawInputFileRecording(RecordedMicrophoneVolumeFile) == 0); - } - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - // ensures a mono file - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelRight) == 0); - } - TEST(audioDevice->StartRecording() == 0); - } - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->Recording() == true); - TEST(audioDevice->Playing() == true); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the selected " - "microphone volume is varied between [~0] and [~MAX].\n" - "> You should hear your own voice with an increasing volume level" - " correlated to the microphone volume.\n" - "> After a finalized test (and if file recording was enabled) " - "verify the recorded result off line.\n" - "> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - if (fileRecording) - { - TEST(audioDevice->StopRawInputFileRecording() == 0); - } - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - - _audioTransport->SetMicrophoneVolume(false); - _audioTransport->SetFullDuplex(false); - - // restore volume setting - TEST(audioDevice->SetMicrophoneVolume(startVolume) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestMicrophoneMute() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Microphone Mute test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - TEST(audioDevice->MicrophoneMuteIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetMicrophoneMute(true); - } else - { - TEST_LOG("\nERROR: Mute control is not available for the selected" - " device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetFullDuplex(true); - } else - { - TEST_LOG("\nERROR: Playout is not available for the selected " - "device!\n \n"); - return -1; - } - - TEST_LOG("\nEnable recording of microphone input to file (%s) during this " - "test (Y/N)?\n: ", - RecordedMicrophoneMuteFile); - char ch; - bool fileRecording(false); - int dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - fileRecording = true; - } - - bool startMute(false); - bool enabled(false); - - // store initial volume setting - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MicrophoneMute(&startMute) == 0); - - // start at no mute - TEST(audioDevice->SetMicrophoneMute(false) == 0); - - // ================================================================== - // Start recording from the microphone while the mic mute is toggled - // continuously. - // Also, start playing out the input to enable real-time verification. - - if (fileRecording) - { - TEST(audioDevice->StartRawInputFileRecording(RecordedMicrophoneMuteFile) == 0); - } - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - // ensure file recording in mono - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelLeft) == 0); - } - TEST(audioDevice->StartRecording() == 0); - } - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->Recording() == true); - TEST(audioDevice->Playing() == true); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the selected " - "microphone mute control is toggled between [MUTE ON] and [MUTE OFF]." - "\n> You should only hear your own voice in loopback during the" - " 'MUTE OFF' periods.\n> After a finalized test (and if file " - "recording was enabled) verify the recorded result off line.\n" - "> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - if (fileRecording) - { - TEST(audioDevice->StopRawInputFileRecording() == 0); - } - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetMicrophoneMute(false); - _audioTransport->SetFullDuplex(false); - - // restore volume setting - TEST(audioDevice->SetMicrophoneMute(startMute) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestMicrophoneBoost() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Microphone Boost test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - TEST(audioDevice->MicrophoneBoostIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetMicrophoneBoost(true); - } else - { - TEST_LOG( - "\nERROR: Boost control is not available for the selected device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetFullDuplex(true); - } else - { - TEST_LOG("\nERROR: Playout is not available for the selected device!\n \n"); - return -1; - } - - TEST_LOG("\nEnable recording of microphone input to file (%s) during this " - "test (Y/N)?\n: ", - RecordedMicrophoneBoostFile); - char ch; - bool fileRecording(false); - int dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - fileRecording = true; - } - - bool startBoost(false); - bool enabled(false); - - // store initial volume setting - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MicrophoneBoost(&startBoost) == 0); - - // start at no boost - TEST(audioDevice->SetMicrophoneBoost(false) == 0); - - // ================================================================== - // Start recording from the microphone while the mic boost is toggled - // continuously. - // Also, start playing out the input to enable real-time verification. - - if (fileRecording) - { - TEST(audioDevice->StartRawInputFileRecording(RecordedMicrophoneBoostFile) == 0); - } - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - // ensure file recording in mono - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelLeft) == 0); - } - TEST(audioDevice->StartRecording() == 0); - } - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->Recording() == true); - TEST(audioDevice->Playing() == true); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the selected " - "microphone boost control is toggled between [BOOST ON] and [BOOST OFF].\n" - "> You should hear your own voice with an increased volume level " - "during the 'BOOST ON' periods.\n \n" - "> After a finalized test (and if file recording was enabled) verify" - " the recorded result off line.\n" - "> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - if (fileRecording) - { - TEST(audioDevice->StopRawInputFileRecording() == 0); - } - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetMicrophoneBoost(false); - _audioTransport->SetFullDuplex(false); - - // restore boost setting - TEST(audioDevice->SetMicrophoneBoost(startBoost) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestMicrophoneAGC() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Microphone AGC test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - bool available(false); - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetMicrophoneAGC(true); - } else - { - TEST_LOG("\nERROR: It is not possible to control the microphone volume" - " for the selected device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - _audioTransport->SetFullDuplex(true); - } else - { - TEST_LOG("\nERROR: Playout is not available for the selected device!\n \n"); - return -1; - } - - TEST_LOG("\nEnable recording of microphone input to file (%s) during " - "this test (Y/N)?\n: ", - RecordedMicrophoneAGCFile); - char ch; - bool fileRecording(false); - int dummy = scanf(" %c", &ch); - ch = toupper(ch); - if (ch == 'Y') - { - fileRecording = true; - } - - WebRtc_UWord32 startVolume(0); - bool enabled(false); - - // store initial volume setting - TEST(audioDevice->InitMicrophone() == 0); - TEST(audioDevice->MicrophoneVolume(&startVolume) == 0); - - // ==================================================================== - // Start recording from the microphone while the mic volume is changed - // continuously - // by the emulated AGC (implemented by our audio transport). - // Also, start playing out the input to enable real-time verification. - - if (fileRecording) - { - TEST(audioDevice->StartRawInputFileRecording(RecordedMicrophoneAGCFile) == 0); - } - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - TEST(audioDevice->RecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetAGC(true) == 0); - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StereoRecording(&enabled) == 0); - if (enabled) - { - // ensures a mono file - TEST(audioDevice->SetRecordingChannel(AudioDeviceModule::kChannelRight) == 0); - } - TEST(audioDevice->StartRecording() == 0); - } - TEST(audioDevice->PlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - } - - TEST(audioDevice->AGC() == true); - TEST(audioDevice->Recording() == true); - TEST(audioDevice->Playing() == true); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the volume of" - " the selected microphone is varied between [~0] and [~MAX].\n" - "> You should hear your own voice with an increasing volume level" - " correlated to an emulated AGC setting.\n" - "> After a finalized test (and if file recording was enabled) verify" - " the recorded result off line.\n" - "> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - if (fileRecording) - { - TEST(audioDevice->StopRawInputFileRecording() == 0); - } - TEST(audioDevice->SetAGC(false) == 0); - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - - _audioTransport->SetMicrophoneAGC(false); - _audioTransport->SetFullDuplex(false); - - // restore volume setting - TEST(audioDevice->SetMicrophoneVolume(startVolume) == 0); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestLoopback() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Loopback measurement test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - bool recIsAvailable(false); - bool playIsAvailable(false); - WebRtc_UWord8 nPlayChannels(0); - WebRtc_UWord8 nRecChannels(0); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->RecordingIsAvailable(&recIsAvailable) == 0); - if (!recIsAvailable) - { - TEST_LOG("\nERROR: Recording is not available for the selected device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&playIsAvailable) == 0); - if (recIsAvailable && playIsAvailable) - { - _audioTransport->SetFullDuplex(true); - _audioTransport->SetLoopbackMeasurements(true); - } else if (!playIsAvailable) - { - TEST_LOG("\nERROR: Playout is not available for the selected device!\n \n"); - return -1; - } - - bool enabled(false); - bool available(false); - - if (recIsAvailable && playIsAvailable) - { - WebRtc_UWord32 playSamplesPerSec(0); - WebRtc_UWord32 recSamplesPerSecRec(0); - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - _audioTransport->SetFullDuplex(true); - - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - } - - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoPlayout(true) == 0); - } - - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - WebRtc_UWord32 maxVolume(0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->SetMicrophoneVolume(maxVolume) == 0); - } - - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&playSamplesPerSec) == 0); - TEST(audioDevice->RecordingSampleRate(&recSamplesPerSecRec) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - enabled ? nPlayChannels = 2 : nPlayChannels = 1; - TEST(audioDevice->StereoRecording(&enabled) == 0); - enabled ? nRecChannels = 2 : nRecChannels = 1; - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->StartPlayout() == 0); - - if (audioDevice->Playing() && audioDevice->Recording()) - { - TEST_LOG("\n \n> Loopback audio is now active.\n" - "> Rec : fs=%u, #channels=%u.\n" - "> Play: fs=%u, #channels=%u.\n" - "> Speak into the microphone and verify that your voice is" - " played out in loopback.\n" - "> Press any key to stop...\n \n", - recSamplesPerSecRec, nRecChannels, playSamplesPerSec, - nPlayChannels); - PAUSE(30000); - } - - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFullDuplex(false); - _audioTransport->SetLoopbackMeasurements(false); - } - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestDeviceRemoval() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Device removal test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - bool recIsAvailable(false); - bool playIsAvailable(false); - WebRtc_UWord8 nPlayChannels(0); - WebRtc_UWord8 nRecChannels(0); - WebRtc_UWord8 loopCount(0); - - while (loopCount < 2) - { - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->RecordingIsAvailable(&recIsAvailable) == 0); - if (!recIsAvailable) - { - TEST_LOG("\nERROR: Recording is not available for the selected device!\n \n"); - return -1; - } - - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - - TEST(audioDevice->PlayoutIsAvailable(&playIsAvailable) == 0); - if (recIsAvailable && playIsAvailable) - { - _audioTransport->SetFullDuplex(true); - } else if (!playIsAvailable) - { - TEST_LOG("\nERROR: Playout is not available for the selected device!\n \n"); - return -1; - } - - bool available(false); - bool enabled(false); - WebRtc_UWord32 samplesPerSec(0); - - if (recIsAvailable && playIsAvailable) - { - WebRtc_UWord32 playSamplesPerSec(0); - WebRtc_UWord32 recSamplesPerSecRec(0); - WebRtc_UWord32 maxVolume(0); - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - - _audioTransport->SetFullDuplex(true); - - TEST(audioDevice->StereoRecordingIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoRecording(true) == 0); - } - - TEST(audioDevice->StereoPlayoutIsAvailable(&available) == 0); - if (available) - { - TEST(audioDevice->SetStereoPlayout(true) == 0); - } - - TEST(audioDevice->MicrophoneVolumeIsAvailable(&available) == 0); - if (available) - { - WebRtc_UWord32 maxVolume(0); - TEST(audioDevice->MaxMicrophoneVolume(&maxVolume) == 0); - TEST(audioDevice->SetMicrophoneVolume(maxVolume) == 0); - } - - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->PlayoutSampleRate(&playSamplesPerSec) == 0); - TEST(audioDevice->RecordingSampleRate(&recSamplesPerSecRec) == 0); - TEST(audioDevice->StereoPlayout(&enabled) == 0); - enabled ? nPlayChannels = 2 : nPlayChannels = 1; - TEST(audioDevice->StereoRecording(&enabled) == 0); - enabled ? nRecChannels = 2 : nRecChannels = 1; - TEST(audioDevice->StartRecording() == 0); - TEST(audioDevice->StartPlayout() == 0); - - AudioDeviceModule::AudioLayer audioLayer; - TEST(audioDevice->ActiveAudioLayer(&audioLayer) == 0); - - if (audioLayer == AudioDeviceModule::kLinuxPulseAudio) - { - TEST_LOG("\n \n> PulseAudio loopback audio is now active.\n" - "> Rec : fs=%u, #channels=%u.\n" - "> Play: fs=%u, #channels=%u.\n" - "> Speak into the microphone and verify that your voice is" - " played out in loopback.\n" - "> Unplug the device and make sure that your voice is played" - " out in loop back on the built-in soundcard.\n" - "> Then press any key...\n", - recSamplesPerSecRec, nRecChannels, playSamplesPerSec, - nPlayChannels); - - PAUSE(DEFAULT_PAUSE_TIME); - } else if (audioDevice->Playing() && audioDevice->Recording()) - { - if (loopCount < 1) - { - TEST_LOG("\n \n> Loopback audio is now active.\n" - "> Rec : fs=%u, #channels=%u.\n" - "> Play: fs=%u, #channels=%u.\n" - "> Speak into the microphone and verify that your voice" - " is played out in loopback.\n" - "> Unplug the device and wait for the error message...\n", - recSamplesPerSecRec, nRecChannels, - playSamplesPerSec, nPlayChannels); - - _audioEventObserver->_error - = (AudioDeviceObserver::ErrorCode) (-1); - while (_audioEventObserver->_error - == (AudioDeviceObserver::ErrorCode) (-1)) - { - SLEEP(500); - } - } else - { - TEST_LOG("\n \n> Loopback audio is now active.\n" - "> Rec : fs=%u, #channels=%u.\n" - "> Play: fs=%u, #channels=%u.\n" - "> Speak into the microphone and verify that your voice" - " is played out in loopback.\n" - "> Press any key to stop...\n", - recSamplesPerSecRec, nRecChannels, - playSamplesPerSec, nPlayChannels); - - PAUSE(DEFAULT_PAUSE_TIME); - } - } - - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFullDuplex(false); - - if (loopCount < 1) - { - TEST_LOG("\n \n> Stopped!\n"); - TEST_LOG("> Now reinsert device if you want to enumerate it.\n"); - TEST_LOG("> Press any key when done.\n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - loopCount++; - } - } // loopCount - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::TestExtra() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Extra test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - TEST(audioDevice->Terminate() == 0); - TEST(audioDevice->Initialized() == false); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -WebRtc_Word32 FuncTestManager::SelectRecordingDevice() -{ - WebRtc_Word16 nDevices = _audioDevice->RecordingDevices(); - WebRtc_Word8 name[kAdmMaxDeviceNameSize]; - WebRtc_Word8 guid[kAdmMaxGuidSize]; - WebRtc_Word32 ret(-1); - -#ifdef _WIN32 - TEST_LOG("\nSelect Recording Device\n \n"); - TEST_LOG(" (%d) Default\n", 0); - TEST_LOG(" (%d) Default Communication [Win 7]\n", 1); - TEST_LOG("- - - - - - - - - - - - - - - - - - - -\n"); - for (int i = 0; i < nDevices; i++) - { - TEST(_audioDevice->RecordingDeviceName(i, name, guid) == 0); - TEST_LOG(" (%d) Device %d (%s)\n", i+10, i, name); - } - TEST_LOG("\n: "); - - int dummy(0); - int sel(0); - - dummy = scanf("%u", &sel); - - if (sel == 0) - { - TEST((ret = _audioDevice->SetRecordingDevice(AudioDeviceModule::kDefaultDevice)) == 0); - } - else if (sel == 1) - { - TEST((ret = _audioDevice->SetRecordingDevice( - AudioDeviceModule::kDefaultCommunicationDevice)) == 0); - } - else if (sel < (nDevices+10)) - { - TEST((ret = _audioDevice->SetRecordingDevice(sel-10)) == 0); - } - else - { - return -1; - } -#else - TEST_LOG("\nSelect Recording Device\n \n"); - for (int i = 0; i < nDevices; i++) - { - TEST(_audioDevice->RecordingDeviceName(i, name, guid) == 0); - TEST_LOG(" (%d) Device %d (%s)\n", i, i, name); - } - TEST_LOG("\n: "); - - int dummy(0); - int sel(0); - - dummy = scanf("%u", &sel); - - if (sel < (nDevices)) - { - TEST((ret = _audioDevice->SetRecordingDevice(sel)) == 0); - } else - { - return -1; - } -#endif - - return ret; -} - -WebRtc_Word32 FuncTestManager::SelectPlayoutDevice() -{ - WebRtc_Word16 nDevices = _audioDevice->PlayoutDevices(); - WebRtc_Word8 name[kAdmMaxDeviceNameSize]; - WebRtc_Word8 guid[kAdmMaxGuidSize]; - -#ifdef _WIN32 - TEST_LOG("\nSelect Playout Device\n \n"); - TEST_LOG(" (%d) Default\n", 0); - TEST_LOG(" (%d) Default Communication [Win 7]\n", 1); - TEST_LOG("- - - - - - - - - - - - - - - - - - - -\n"); - for (int i = 0; i < nDevices; i++) - { - TEST(_audioDevice->PlayoutDeviceName(i, name, guid) == 0); - TEST_LOG(" (%d) Device %d (%s)\n", i+10, i, name); - } - TEST_LOG("\n: "); - - int dummy(0); - int sel(0); - - dummy = scanf("%u", &sel); - - WebRtc_Word32 ret(0); - - if (sel == 0) - { - TEST((ret = _audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultDevice)) == 0); - } - else if (sel == 1) - { - TEST((ret = _audioDevice->SetPlayoutDevice( - AudioDeviceModule::kDefaultCommunicationDevice)) == 0); - } - else if (sel < (nDevices+10)) - { - TEST((ret = _audioDevice->SetPlayoutDevice(sel-10)) == 0); - } - else - { - return -1; - } -#else - TEST_LOG("\nSelect Playout Device\n \n"); - for (int i = 0; i < nDevices; i++) - { - TEST(_audioDevice->PlayoutDeviceName(i, name, guid) == 0); - TEST_LOG(" (%d) Device %d (%s)\n", i, i, name); - } - TEST_LOG("\n: "); - - int dummy(0); - int sel(0); - - dummy = scanf("%u", &sel); - - WebRtc_Word32 ret(0); - - if (sel < (nDevices)) - { - TEST((ret = _audioDevice->SetPlayoutDevice(sel)) == 0); - } else - { - return -1; - } -#endif - - return ret; -} - -WebRtc_Word32 FuncTestManager::TestAdvancedMBAPI() -{ - TEST_LOG("\n=======================================\n"); - TEST_LOG(" Advanced mobile device API test:\n"); - TEST_LOG("=======================================\n"); - - if (_audioDevice == NULL) - { - return -1; - } - - RESET_TEST; - - AudioDeviceModule* audioDevice = _audioDevice; - - TEST(audioDevice->Init() == 0); - TEST(audioDevice->Initialized() == true); - - if (SelectRecordingDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - if (SelectPlayoutDevice() == -1) - { - TEST_LOG("\nERROR: Device selection failed!\n \n"); - return -1; - } - _audioTransport->SetFullDuplex(true); - _audioTransport->SetLoopbackMeasurements(true); - - TEST(audioDevice->RegisterAudioCallback(_audioTransport) == 0); - // Start recording - TEST(audioDevice->InitRecording() == 0); - TEST(audioDevice->StartRecording() == 0); - // Start playout - TEST(audioDevice->InitPlayout() == 0); - TEST(audioDevice->StartPlayout() == 0); - - TEST(audioDevice->Recording() == true); - TEST(audioDevice->Playing() == true); - -#if defined(_WIN32_WCE) || defined(MAC_IPHONE) - TEST_LOG("\nResetAudioDevice\n \n"); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the audio is good.\n\ -> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - for (int p=0; p<=60; p+=20) - { - TEST_LOG("Resetting sound device several time with pause %d ms\n", p); - for (int l=0; l<20; ++l) - { - TEST(audioDevice->ResetAudioDevice() == 0); - AudioDeviceUtility::Sleep(p); - } - TEST_LOG("\n> Speak into the microphone and verify that the audio is good.\n"); - AudioDeviceUtility::Sleep(2000); - } -#endif - -#if defined(MAC_IPHONE) - bool loudspeakerOn(false); - TEST_LOG("\nSet playout spaker\n \n"); - if (audioDevice->Recording() && audioDevice->Playing()) - { - TEST_LOG("\n> Speak into the microphone and verify that the audio is good.\n\ -> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - } - - TEST_LOG("Set to use speaker\n"); - TEST(audioDevice->SetLoudspeakerStatus(true) == 0); - TEST_LOG("\n> Speak into the microphone and verify that the audio is" - " from the loudspeaker.\n\ -> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == true); - - TEST_LOG("Set to not use speaker\n"); - TEST(audioDevice->SetLoudspeakerStatus(false) == 0); - TEST_LOG("\n> Speak into the microphone and verify that the audio is not" - " from the loudspeaker.\n\ -> Press any key to stop...\n \n"); - PAUSE(DEFAULT_PAUSE_TIME); - TEST(audioDevice->GetLoudspeakerStatus(loudspeakerOn) == 0); - TEST(loudspeakerOn == false); -#endif - - TEST(audioDevice->StopRecording() == 0); - TEST(audioDevice->StopPlayout() == 0); - TEST(audioDevice->RegisterAudioCallback(NULL) == 0); - - _audioTransport->SetFullDuplex(false); - - TEST_LOG("\n"); - PRINT_TEST_RESULTS; - - return 0; -} - -} // namespace webrtc - -// EOF diff --git a/modules/audio_device/main/test/func_test_manager.h b/modules/audio_device/main/test/func_test_manager.h deleted file mode 100644 index 3e85354a1..000000000 --- a/modules/audio_device/main/test/func_test_manager.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_AUDIO_DEVICE_FUNC_TEST_MANAGER_H -#define WEBRTC_AUDIO_DEVICE_FUNC_TEST_MANAGER_H - -#include "../source/audio_device_utility.h" - -#include "typedefs.h" -#include "audio_device.h" -#include "audio_device_test_defines.h" -#include "file_wrapper.h" -#include "list_wrapper.h" -#include "resampler.h" - -#if defined(MAC_IPHONE) || defined(ANDROID) -#define USE_SLEEP_AS_PAUSE -#else -//#define USE_SLEEP_AS_PAUSE -#endif - -// Sets the default pause time if using sleep as pause -#define DEFAULT_PAUSE_TIME 5000 - -#if defined(USE_SLEEP_AS_PAUSE) -#define PAUSE(a) AudioDeviceUtility::Sleep(a); -#else -#define PAUSE(a) AudioDeviceUtility::WaitForKey(); -#endif - -#define SLEEP(a) AudioDeviceUtility::Sleep(a); - -#define ADM_AUDIO_LAYER AudioDeviceModule::kPlatformDefaultAudio -//#define ADM_AUDIO_LAYER AudioDeviceModule::kLinuxPulseAudio - -enum TestType -{ - TTInvalid = -1, - TTAll = 0, - TTAudioLayerSelection = 1, - TTDeviceEnumeration = 2, - TTDeviceSelection = 3, - TTAudioTransport = 4, - TTSpeakerVolume = 5, - TTMicrophoneVolume = 6, - TTSpeakerMute = 7, - TTMicrophoneMute = 8, - TTMicrophoneBoost = 9, - TTMicrophoneAGC = 10, - TTLoopback = 11, - TTDeviceRemoval = 13, - TTMobileAPI = 14, - TTTest = 66, -}; - -class ProcessThread; - -namespace webrtc -{ - -class AudioDeviceModule; -class AudioEventObserver; -class AudioTransport; - -// ---------------------------------------------------------------------------- -// AudioEventObserver -// ---------------------------------------------------------------------------- - -class AudioEventObserver: public AudioDeviceObserver -{ -public: - virtual void OnErrorIsReported(const ErrorCode error); - virtual void OnWarningIsReported(const WarningCode warning); - AudioEventObserver(AudioDeviceModule* audioDevice); - ~AudioEventObserver(); -public: - ErrorCode _error; - WarningCode _warning; -private: - AudioDeviceModule* _audioDevice; -}; - -// ---------------------------------------------------------------------------- -// AudioTransport -// ---------------------------------------------------------------------------- - -class AudioTransportImpl: public AudioTransport -{ -public: - virtual WebRtc_Word32 - RecordedDataIsAvailable(const WebRtc_Word8* audioSamples, - const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 samplesPerSec, - const WebRtc_UWord32 totalDelayMS, - const WebRtc_Word32 clockDrift, - const WebRtc_UWord32 currentMicLevel, - WebRtc_UWord32& newMicLevel); - - virtual WebRtc_Word32 NeedMorePlayData(const WebRtc_UWord32 nSamples, - const WebRtc_UWord8 nBytesPerSample, - const WebRtc_UWord8 nChannels, - const WebRtc_UWord32 samplesPerSec, - WebRtc_Word8* audioSamples, - WebRtc_UWord32& nSamplesOut); - - AudioTransportImpl(AudioDeviceModule* audioDevice); - ~AudioTransportImpl(); - -public: - WebRtc_Word32 SetFilePlayout(bool enable, const WebRtc_Word8* fileName = - NULL); - void SetFullDuplex(bool enable); - void SetSpeakerVolume(bool enable) - { - _speakerVolume = enable; - } - ; - void SetSpeakerMute(bool enable) - { - _speakerMute = enable; - } - ; - void SetMicrophoneMute(bool enable) - { - _microphoneMute = enable; - } - ; - void SetMicrophoneVolume(bool enable) - { - _microphoneVolume = enable; - } - ; - void SetMicrophoneBoost(bool enable) - { - _microphoneBoost = enable; - } - ; - void SetLoopbackMeasurements(bool enable) - { - _loopBackMeasurements = enable; - } - ; - void SetMicrophoneAGC(bool enable) - { - _microphoneAGC = enable; - } - ; - -private: - AudioDeviceModule* _audioDevice; - - bool _playFromFile; - bool _fullDuplex; - bool _speakerVolume; - bool _speakerMute; - bool _microphoneVolume; - bool _microphoneMute; - bool _microphoneBoost; - bool _microphoneAGC; - bool _loopBackMeasurements; - - FileWrapper& _playFile; - - WebRtc_UWord32 _recCount; - WebRtc_UWord32 _playCount; - - ListWrapper _audioList; - - Resampler _resampler; -}; - -// ---------------------------------------------------------------------------- -// FuncTestManager -// ---------------------------------------------------------------------------- - -class FuncTestManager -{ -public: - FuncTestManager(); - ~FuncTestManager(); - WebRtc_Word32 Init(); - WebRtc_Word32 Close(); - WebRtc_Word32 DoTest(const TestType testType); -private: - WebRtc_Word32 TestAudioLayerSelection(); - WebRtc_Word32 TestDeviceEnumeration(); - WebRtc_Word32 TestDeviceSelection(); - WebRtc_Word32 TestAudioTransport(); - WebRtc_Word32 TestSpeakerVolume(); - WebRtc_Word32 TestMicrophoneVolume(); - WebRtc_Word32 TestSpeakerMute(); - WebRtc_Word32 TestMicrophoneMute(); - WebRtc_Word32 TestMicrophoneBoost(); - WebRtc_Word32 TestLoopback(); - WebRtc_Word32 TestDeviceRemoval(); - WebRtc_Word32 TestExtra(); - WebRtc_Word32 TestMicrophoneAGC(); - WebRtc_Word32 SelectPlayoutDevice(); - WebRtc_Word32 SelectRecordingDevice(); - WebRtc_Word32 TestAdvancedMBAPI(); -private: - ProcessThread* _processThread; - AudioDeviceModule* _audioDevice; - AudioEventObserver* _audioEventObserver; - AudioTransportImpl* _audioTransport; -}; - -} // namespace webrtc - -#endif // #ifndef WEBRTC_AUDIO_DEVICE_FUNC_TEST_MANAGER_H diff --git a/modules/audio_device/main/test/recordedRaw_48.pcm b/modules/audio_device/main/test/recordedRaw_48.pcm deleted file mode 100644 index 32fd79e6c1127556fd3837c96442efbb37598e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1917120 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�Y&R`+7OdL|G52RFrHfsb+qF?D@(8}~ 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

%}Iyu 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 @c3WjO6nV~1w$aLUaSeR z2Tjm+V3SsrG?wl{t6vEtOPN(>!faZ}{QV{;r|PQK;VGeA!4Gt$L2BzYJoxjOZmh+h zdq$p%0;VB%*gMjqvf^uSLw%bP6zQ;chTerLhj+7&OW-t?6xMP*7s8R*oq0#aU;o_C zPeDYbG@m)Xn`&7D{%224>yxO}B3+zMdjKN;|Cr7f!Xe{1{Pw@-$$EW`(1N`EUa~!g^!||K}2m(1ji)-68>wWii}N?IF5kpd71%nlOrPv=WYyy?QH*#yV)2rlPk0V35(N zoq}mG#PGx*8S10Hie-~phBKm;41>dltq>Fs8s_5FUaqgG@1wt_f3Md-_^iWDUu(Fp zw;O81S=8clT43C2Tw*M56pc}QmgUAGro*Q8IF>|9UUu-&mQ?dd{61yOL)pe(u(T#c z>YMF~{eitQ%Fju*fwm5|{I`^}=mqvDs)J3L}OFTG2vY)pd zGuP7()l3XERMyB><&Er!Q{Wc#l#BXry834fN)=ObrsPWPmi9iqHKf1WPAk+Pv!|

Xj~O;2qz?A5&#{?*RVRM%Wl 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%0Ab7p`~E!os)zn! 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 EOs7LsKhf@AegF9Yso*S&gTFCv|MQ!Uy9o#Yb zN%dr)zx8q@)x!srP5xK%{6X5ZN~XQpuCxuZm=4KcT5DojYi6oRl{TFN>UaAR52$9j z(16Q==lm&wj_P1aoR*4Z!F0BV;&=r-V6^G&6?}O|bvT8;s=3XKkNKBoDRon>S8JX( zsiml6L*-n223=oo`%wVG(N7JV3R!s7W~if~Goc?tTS8kY&uWBzma{fB_>O&o?d5sa z(Gz~{5sfnAby1UtOM}{E55#SJ$~UT_Au93_MPH*->Ik~G2RNvbaS-3y9M#ZHscqa+ z?W90G9@?Yw9)ZOCuIIeKn{^)A--mYkFZph7VE+Sr!#nBH?+Ue-?(rlA+;)h~Wcu*> zP@5HYW=u|P0-<@0hig?RiITi-YAE##ck^tzvPz*#^aC|KnrH1r{>{&NPw<75ZppO* zZ^F&LOe(K)D^LFoA4nXRa4~;oUiaKN68DCiBtBIA(z$7}?4*6&ezmzy=kjV!e>~T? zco!PXFCaME?R@Qp?S93Xbf92}zHODxH7l{T`!0y#90lo4m6X;`uQNT+0*=+j?u)f@ zzW#H@Y=gz`cadNxb{)_Ct^5`{s9Cn!qc{+2w}fxDfR7^E=J7W< za?eYg$l-~cXO5nL1-%ywdeOZ1A}7>InwtH#0&F(FzAurnaGy<pUKm zR{2I`dE{3*rP0vPTRFRWVH8K(dHx7LxjikGq+M_WpP?&Z=iAYVVPL+ZJzG%V!TacTV@SH>T_9s7k(xqdck(UXZBq zIj_@?Cd%J(o7uV18*BYCoPInF_*}194=3g|d1r0Mo9e3h+$o-&8_KIj>-Q1_a2VIw zb?Rs>pZ9=Qqd*$OII6)LY}sifx1qKAc2|+U?^=qqQ&Oas*yQn&ldT10Hl2p+V>>ZN zdL{R$xNrH2H$dZum{K2i@;xnYJrBFo#?;XWPVz{>x3msxi+G>G=9u-SgtCcqoxULq z!>2SNYZB_xs3rKfH>ern95H>Q47P>$&4A*L!~c!pA+3;(8OuE(8Uh%Qql ze^(_x$i3Ru`T60L^wM1|4TR5QeMxE^l15EJ(XzUzR;Hq0aWSrSD}drpJ) z_-^OXeO&J`n9i@0y3wd$H_juf@-3DawvsL&!?bvvEt@~kCKSs%jj>+lo@vb=o*<>> zGVhz{gI?opIKu5v113Kv_iNqJV9tcGc|UV8uUC<7D!7xzu$lhm<%0FuV82>K7dLBwXR$9&2Yv=^$8D zYgf#bHYqQY%+l3!duP#h#9)4*_aN8)>y?y%kL$$m;a1q0_cv^#x!OMxn)M!URwYyF z<#6JU)ax`;;ML~9augo7yDq-baomg{yb(rv6FvWGsT=boNxr6m%kx^1`~c$un{)A zIL5qGu$UT|FFhlkazV{Jn)0VVpHA5(rGBOq#mQ>t4x6C%%Smf11!|kRRG0*{oNDyW63o;MQ=3({Ftu_`HvJ&x72UW6M+GV^_ zCsX^T?lP%1wlQOMC^hvlyQ6mdTUM|>&Z4)>jLKsS}e6=sB!RU z@_LME;il;Aa?nz+wrvBS+0i>0>%S)dCs_o~bFhA?0_CfPEu|yu<38AL>&xZR5i0TJ z{mEt5pLcdR@4&o#JNIBhesGYu{Ylx z%xQCk{<$0${(X03-+~j=n%DX++wFBJ;m#W3o*Cu*s&0e8IOq3WCZLVVc&E{gM9jh2CB~>zv z&CG&QcZ@wr=~_(A)l}Z&PkhDcc2NB<-W9G~!dpXi>NS<^Ge6rdjNu+1GaVxOgtxXP zrPe*^d8^}O z+smc1%y(X>iXNq&s-Oxsr-*%oBDN|YW(rqxln=Q%?dubiAe-siuUFZsQAOURvUO1J zdP*p1XjfwsuB$dqph1}CFR3U0kzMe%*Qx=fRq4DVQmAHfA@3@3byYTh-XWu^BUesO zXXq+tRzE2F!yKhkyh>HEl`nBZeTJVrgP$y3@UI#f;nO>6H*sOw#SR{Tspfz&a$N?g ztvjjfe}y+zEZ9pg)Ytj=k5}_nu9mw@3=8wiI{(*V!Co~nF2rpP*P9h?S|5-Qr9_|4 zQN9@iH8Y`>gqMh1gdd9(k%MS3yFqNfHEHZtV~gtqyV~jT372pW35w6s?xoURyy~Q! zggJW)c9N`W&i65opVKJ9VWf}VczXU&FFO^Azeyc1QPlQhOhWI_*A4}Vc5u9NOj-t)aL zKTxoTTWzM!bfbIh9oNXPB1GX{SKap;30wj;$RNArdEI zh5NQ&cct!ky&jMMqjJB?ciFV)?)%N_IutGt;k4}FalEUC8EVYg&6!qP*HaAAd`*5m z>aV`;!v@ZP_RyspJeJh_BhZHZTzQY#Fa5o}%B#$>Us70CkS>*E3+it8ZcL{&QRn-H ztEdeJ;8BXQ9lV6=dD_QHh`iR$sxRFWt92B&OF>%(32Ujxy2?-fGY{N7>h_bAr6bhi z1C);aOsOZSJkQY*Rf5n*pxA$?pzrDD@8W){O?g(y12CfM{*#sG*#z7TFK?EoxhU4_Ui^X?F;hr`_Y>8r}BJ}>SV6JjnaX=;VK$L zvoq98Ivu8a63W|>GUf-K)av-}{*+~hxtz*k-|jIL*2T89O1WR%>yfgGs=AQxTP%I* z?Ucv;%j5Ee+re1xOlg97d&~dqk->36WxOpoDA-@>)KpHMmoNdX)!6IRw%#%oPX)ul zv``5t8g)W#sJDLMfBM?TO${EW=ek>cE6kmGQQZoJKI4o!p;DfpbSrBoebZ3gP$;xd z#ocHYJnDZ>1}m6D_sN@@uIAP?1#Yx2<4mxPjZoL9Q}a_#rCv;}m{yr%>YudB(r-%d zm;Oik)Qp&IiTg9_Ue@fgzcVLgcFSy$*&*}A%=($zGpc2DNWa8yHQesvuTwkIdJhSe zwFje}gvjH;9r7rvLu)U$b)&cZtFMEp7?2NShqQ;l#!@@*FgHw}nm#FgO?oW7M#g;^ z1?d^-m!&19&eavv4sFpjL{m<}rkf@Iku)^%orF_5?rkb%-Mm@)(G9tOsfd5Oy1T)` zdgv#grMPK4Lx3M`@&4u5{6T96OuIoJig{s@0koEu3996M@wX>e6mfY#H z7@sK+i!MI?5ekX-^xT+SHtdyCf$+rgQ;_~ zF0(2mY#R4Yd7AAI7Nk92>naHiM+v-6yMxxd)_d|06}pA*)}iLxopOt}=6I^<@RPZC zhE55SC_j=CuS-m#>R9IVJI@DH+qwFJ{x~_md)_MZQo2*1Cr!iz*%s407Yl4$3sVL* zR~25D0kAr+sk|w<_4sth<-KqMF2j(RuTIm3gM5?{wco)c2Q6WN)Y+chW5F zQmaauZzs^ErpXXEC%fPuiSF0R;2L1M{?rz`tEd|rQqyd6Hss{qsSds9G4IPOYgglA zYRbzvznUDuyUcsYA$b_@YX?#TD_Zy~{~X*3mxp4l_v$HDYl=U7ojTdiy5ky(2W1g*?Ds z3XIxxk7-G7;UEu4LKyA}xtH3!JI?DQeD|ni$3oxHRFAg~1aW%ako*@Ra~TPHc+0xD zYo1bJ6KNe!CG?du`nUYP^dep%EIqJe;${2|9q?N7RnR4n!lx;7FE_#U^`~c5%&@5^ z4`UlpBO)f^BzWcp?(JH+HRBsF{rjV}qpfJO-;rlFT%BBCnk+9T^+(M1(CiagUveAo z%Wjc#uPmx3ZAT}oPEqUK^39Z9sABk6zFZLPq;SMOon^2wB z{7${%Ptr^W;?ptpj~1nVj)tw=6HC(_mXiNn3(iwU_VtT$K>xsVXUA&92je)xazd9- zUO((nmeBJisim*#tU5?+xmI^|yQ_YTYd1@eJW6kSGXFPy(i=LctMc=`%AXb8_aD0& z2ILh;G`oz3pg)W%T?vw)MVw<(7C*Olk{5~&738m5M>gsO!%ST zdlPS*&-@(4&koOI*cDtJc6&4IcRXI{F@J8$GyFd_v@qT5S1yS^oJHsOI9t+_%}v~F z_FQa}+>fryAN*}z;G|ZS3!B>b~{z4=NwOwIw!E1clZFG@Cmr= zCC;7eyxok^E!2K?#d5xmrCcV9<4|&{q>UP8mfIn$J+PF0RMn>-qW8gGUopYV!TXKj zB%Wlxd=Kh6%9OfF)8SJ5=zHZ)JryBh$MgMO#>5zR$JI#~% zP|Ri!%=Tu)kgWX`l8SGmmtMz3v?kOub(JKkBkI)B)SFXlrj|^t%n`LJwJydmE3Iz& zP;RK|>0d}?{8OIBApcid>eUV?@7U18GWmai^=`!W4>HNtgP0x*#)3~s!D=g;V|Ho- z&f>D^RaCqC(hsNi&-gu~R_64~pEJu}mY=yg^U=&JGyl#AWY$z=XJx*TIV7`FW^Tsx zjBHs}GtxSyjZAHonvi-4YFj!~Cs;qFUh->!?U13%ld|2{5%=#Lm`2rtLHX0<&%WhE z+mm~hoX=TuoGwyUoTsGO#)%%JS=^cb9Q^)us{9yU;n&1gc#WF_y8}-o2PlbNg+qVj znf)!L1m1P0#L6bpOYVmKRt~K+!S~gRjKTz6+|XW4A^sUtga#h-#V_2wIW zM?bi|;Dmi+*V0G6@9HcIH|av3HWVB3s7l|_TRZ*#2ItsZj7O!!sKl6eimu}uWUe*k zHHhm9-oo)0OW4BqJ4qte6Ef_cR2y4Dev;*5wUS`g z_^+uEkC@#`NYBdPOiVSUwSoJ7E$^_p3BGPol2dOd{@YHcNM2zkRv2I^#&UJhs-#cN};jezcC;pNDFJ3BJ z%5@EDkJi3ITUF*x6=}HV^bv07!uR%2wRW;qo`autgUdCg&S=2N+)lmg+i>#>4c3Y~^rZF8M*8$8lXuF#hxZoEUG`DZ~TF28l= zjGg9+u?ZhzJJX?Z%M&YM`S#m>m7KH{`*2NC#QkwBF~qM{pI5y~;&aqQ=Q!sIci+86 z^kH@Ou(y*)w+2gW5 z$u6D!cvj`Cr?P&`8f_ERsq8i;&J6heZ!)ZlQ*VurJ);Ix(Un{WCEee}65f*huD zeAPc?^X_>0*^y3MythZDaP*F+S^J(->0?RgN5VVooBAc(mfq+J%Iw?h zH!eY8FoU+CAXXI$RFg9AG0DgGNJ!nldvq5xVVT!81cPpp_ZCmfIY~erq5Bu4!h~_% z33=;uUFo?8pgJq%bnJ!ed=o$A=S|98AHOwTuIQP(!>+uv_-A}k3vH^XN$LG9{rBPM zS+nnM6Yw^g^!eETB^dtI5TBcEg&c?L*e?&{8mz<2cq~2$^ZuB;nd5oI;Y}rq=G&_7 ztO5C7aPjxXvA$y>pW;frkz+CoX8i+idNt0;xT*YF*Iz03Nj=q$ncUib9IAMi7OnRMLm>Zzgs&Bo35s% z^(kR*C~#0!rK6gI-EB!pI^O(qUqV2|8&9W`%@U` zYDrU)O3J5tC;3%X_qpV0YUZKjqshmT6ABHGQ}t}2X!3Ww$zLYV_FZ>yUe!tNnfy@l z-N}C?)lO;~3MX#i?5fA(d)7?&YFyN;uxQ-&*s(DZpQ3|uzoK27YTi(Tx^$ZBb%IXv zNgV4~J$eH5+Q-peau!DO2sN^!wyZrOzw_!0Ro}+g#aNOrY>sE@_F#X{!fmuewat82 z+fKL01odHV~Fj zA~i7M|5GQ{!xr?-rY@(TgZwOxSzM+IAdfvxxA}SdbaelN96zI?E6TV0xe9p9R;EhW zi!X2(T}(u$%SyY&gy08S?JKgle}#1GNedsY^DoMWox@YzjW#YCd!x++XU!e%;XzK4 z{#r)g*OXW95#P6jKA<(;r8GzGUJCLToZ>e*cfMAO+QFs2d+uI?4Bvnam#Lu3{k~C` z9;2d_GAk+Lqp9y4iGkvA!#oJS;H?fo*5p(WCI`!Dtlhhn9y4_r7(sR&c{+{I4_DXz1vD4DmOS;eXbwH(DOGRRC zR=YalEH=?jc7t#~h;~vj15UCAYRfyivljHUhw_%{w;rMQ?56Xtfgj6~q_@Ge{RG6= z&DGM&DK;~9Hs3)-sr94rb5FbXr_AH7>EiZCFRF=ajO5+HKUzGdLtckykKEgGT4uMQ zzM4#(Jc|M?DKaYjY-X*DqUp8L#-$cc-J0@GN{iGLsXJ3sQfsB%oR*$?H8t0E#%XDl z(@&@WmGMXB{BR>W=aZ58HaJ|%T3{-;GH0wl;t~F;h@W{bdQei!85)*P@g9Ge<341T z@P-tL5|YqH;!Zv_V{DvbbATP?Ph?fZfZU{3q~h^&OdA_!--Nx`z$1KLRugmdac1Zb z+td3yP1zHXz2TR*zIuk+(%pV9@uG9MMEEeLRfo*}{Kv;LZ>9&F#Xps7qs0d%@wG82 zzh5+u8rqL!=jBE`5U=zp{KCe?8 z+xsc^$58UG^OfQr#Vii0YqlnL$a7x{S4yrPD5j+@M&YT%s) zOU5df^OKpyt5QoEdK|rJ1m|V1v`O(_^O*!LoyvOV&vWYIZT>KKZ|hyVHhQ^WiqPEE zwc2TV7mrLMAEj(#PccpZPXgpF|Mmf{yqf87p7(sJ&-7?-@5^zfvEP{zTrjCkat(Jf zsX8VnaHy+pF-`J*81ygo^Nf_CE4bbdVStIyKwp!A{0-uiDE|JAvvoDycPA6;|M6y) zpiTN-vS?lEuFveD{n^gdx*QSjbN?nKl;WxAWP)~zL-AExdM5LG>?D z0cx82g4;|lJK7U4i%xc;-#?btR4Z`|Ra}M~=E9!J!}PuZex#SEc5Vz7_YpNkjl;86 zwDacar0qzxKTP=Uk-o_5W9sJJECM<>K#>Q3_)tkXm1&-rZo%Jl71;^9Ig;ZOrs4($7AS>N_x~%!S$m4VUAm`}QqRuYI*}Ov3vbbv=INc~D}PLn z8^wYAf}5LSqO_cT?LTMYHVTMA90V8jc2l_AkC;@ocN+HOazCr*8%bf;Rvl^MBdQYY zl@wJ=UdB4PtWUv$Eiqa{V7sTdL7!J8>*`S3JCVPPme6P{D68?%6z1es#wp7U%ue)Euiov_S$cf z@pVvVbF*%vJtRGXa^qE6l(qccUvYfD?I+z78;sovyKgg7ndTl$P%-MNfNlMre=D=7 zK^9ULegv0Kr~3R6Z&ci4pDp*|5Jn;nTKdg9a=z|q6>L7!eLikZcg!RB0nVCB1@nYn z_%?OyH~sQ-PL4EqYzu7nk#72R&)sO9^9~)(k9z5$zMkL_wo-Yz(oy`4)vV+(zo&mm zrh<6`bGd_q@&kBolzaGqzNLoe;flWLI+gZ0_w^-A*IC^{MqV$~cPF;Jg?*b3*cEr1 zXW|);`z4Iy-#Yl)Wi1w!PPR7oyb3)9#%<}8I+yoZ%s@WHrT*V)*s_zJXak(v8ltS{ z86D*L``PCT_i?IM;X5Z+CrGuBXZI23Qr*~}^=u9F3+22f!#syiyFVXD_FCht+o<9< zl&+N$6Viu$ulvZvg|?11&W*`woc$*LvW4vyH6!1KZw|j^AL7U93)6l}eJ!=8)Z^f@=urd}uOF^0qFe;I`{yTfovwiGT7=Y^X?Na{<@H`$c2JyMHCw_(A~rLvkb+N7_aX zhhL`gZO+;Ji;CMtj^wJy{>TZcx$-s&-z3|79zEY#>bA9*-vO%QKwFP{!LIGp^P}|R zkIV3wMLSn2JX`&~m3}y!QCuoYDD%e5YME_qS^k9j{HM&a;r{O7hdgG*{hxD@SF(27 zhxL)8etPE>hkP$7K=wZ6p9IfEA| zLG3H7^1hfbEny(ucYw_Mq5PBT5k>7GS&-xG;_1aTd_G}`iE3q=4sKUx$KiuNmIl?A z7c&ySjpK2hTYCYvddBZp;yaqj*2Y!NQ)geYePMA^dqH+orBH=XVM$o0_*?!9CWf}acGvNYJP4sy48_`0H`{!+Mq=@VV0&$76>roczR!(663+LXKv(&3y!fU-)f3+{Cx%pEh_kQf!E;GyiaK~vF^Cmec zQv$a^{poQp@a-IoA0W}IFW$O{iSAAGNoQ2<)269&5}VRJ-J@SP&(Hh|KKwn;z!J#* zep>{}c@B2dK3C^89nAk(-2{5Lx$`*B&pcn9#6>lo!kwF$vc4_;{*J(YXK{Nw6zAe+ zN16pZTu`q%>g8uc4?jpkYX?cDI1O8R9ry9=Cc2ei+&-M~-+_2J7Uy`L=E4Q1eC2?N zz(5EgRZ2!S#m76;fcY6txx9BTs{wz>={^aquGX22^_7WKZinQ+=cg~-?Bn;?Il8v- zuDsivfxp7H_rzSp@QpGlyGN==-Mp}lG6|RXFvQlwua7`T>k6h`x8PxpxwjK_e8~{- z8dqRI_1@u6|D&+09Qe!iJBMa|zN`2RwWob-n)^5!?uqIUHbbCmZB+|!HMER*L`7QU z_D;Iz9o*&ub?qeA_I31n^(pWM>KvBI>R1V1?+mQfk36UcQBndsRFUU+@E=f98bL{& zY51mMd%jg$=fdf`)xZ5RrwZw=#_>0#;1znyycrhPT`EV@xXN5*Io?IT7bro;Q7YtX zUxw*d9+N@$sGlF@BO9R0=uS=8TwR&x_VN=DzrY_Xl^gsUJ?4C`+6K?=W;M4AbwfAL zNJ3zaig~?ProOB7=h!CFKaY7;o^>C~#qKSr^iy$Id$`nJGT-S};AG2B;~4Dccc-c; zY@cBz|6dDjt}JeSci1aXIf&-X!NuH0Ew+O9sVHNH6xKT38%2 zpMwRIND@t^L%qUt!p7;ZI=2wc&65?H%mzB0C5w%k`MRS+xgK z{JG6lwIch&7dfv2kqa>72=3}EYT+fB_hzPM?9SMnu}u2NOm%&IMoLDJ%c>opvcZM`d`0;oDbR1@sGp}8cwHv-0W-3LIVC8MZ>B5nLKiR&mZ_wAUWnd_>-`*a zv>!r^z^T7rdvfxM##}N@{n6jnVBqu5pJq10--RKLg|$ zYg73?-s45Q1D}zfa7aSdCsGRHA)yqgWud=aiMvC+t7{KygudtluFi3iZ`$I3OU1v& zy%ch$-0rN76gXA?$9uCIhQ1hEnmg+?r`Q3k;d_#PF2wy5KSb5|NJ_?AiNDB`{5df< zaexGmy}_B#;@dX<%neS*`IZW1(DFWGbHR%~o-R-a|6xiy3cERw7V2iz>(*dtbI=*k z-vHi{!!`)Lfopy(v1P)Y@x$Zl6!@yvOA+cCyT@*+uF<@l#o1eQW_u#(JbJ4+>DNkr zxkFD`S4L`2o1^j*$rGro%R+LwF&{dWPs9!hbcyTFq1sYX*Bx}kr({1~*2UxMdq48ucS$LHyf(xe;Jq4H03R(J5(w?O0l z2?bl3z#(JN7R}3TuwzzkByv>hbDJQ!!k4LwdvQEd; zjx`4yp@Zyzm$?_?w}306IY#kfUUT_f`RSFPbK3kEs6(ARpX+M73;zkAe!Yf$1Z&~<$%&l%DgR~^A!%YS^Cmw;04|2S-$5yY*3o}_$jy1t*UTYHF=~*Uz)2rjSBE7KDN_p z;Un@^TH{CN+nCxjZYCZ5x75681AHOV-;2dbKf?@_G(UOf!{hi#g*)E~F+DLsjKDe_(Di7#Dd@ z_Ho-0U&DKz&1yz{W@hG(YS2cN>KRyWc;)~}l5fkyx@L>> z!{Jv{!pAtbdrAiRA$&j9bsq2V*F43;BUR*2m6t#{Ob*Dx$z!$u-9%#K zy38#wagWTNFmb)i#&Gcksm`x@9v5f4laZNGB6DQs*~~oqoSScm2kZGa2gDv>uy1- zQq_E*G(XmZrVX7iV?|{4?WUvJPit@`dJp%_2zs!5_s*w&f1cK$kE;5isvJ)Vcf=Ka zD(0x)XX;s>RA0ZsNc~Q|bU@c$MBRMA`McU#awWE%B%)6G&1%lda)GVhU%$sb01Iu0 zS3ai!J zQwPWsx<#xz?`tjnWdfQm#&g<=%D9T^D~fZ~-Fv+GFek<$U1r9igHBH1FwXO3pjk z@2QVR%*5+qI@)3(R_U(#*znzwm%p1=Z(7boUg!3@nCEoVH)1DCIis=zE8`xFuOxZ8 zp8NB>d-ajsw_WIvFWHmZh7Pwq_WG2)?sq42NjPiX{F?l);_<6ICkrVPW1R6nLW5mp zWF2>AF5>|07WfIX@Dv8@TeHaKaKVde({8WB?-HPIBjuX$AxRE86gQiDco78jke!R| zp`Akh-oe>?4bEF>BJd)g-86mFz0TWNdZ%R)a~Jtst(K30r}9&&`odE?C0|9&(q6SS z<6ey4P#MGx+`qHBzwM@;b6l~TF}NkEcE|Jl)xhU1f!g~*zI$~CLu4-wu>q+ACEVTa z7Xm4oj$l`;=@Nq|nH71Fn-I_BZ_EW1f?~V_YjWCtlz19}Kz|k9|6DnFsrTY^|SN z&)2&zZ|Dw>+HyV0^dc5(QY)bWm0gR3_7Xul%NM_&W2l(V>l5O1J^SNNN{(8nCwY$6 z{UJa3hWPU8OicXG^5G6j7(V0HuIV?g#ubygQO`u=QAsU5G$yFvVEy6qq2kCegn z|7OS5Rv+Kl&6V$KDwA-KX7C$V;wJa4f$sKs313sBmh6a|u9AJ#438U3$#XwdFj;AOLOm_*t$LXj)aHyk_i?@ ziiOuoKirVMBfVb6a~Uf$j%A$9xFqXoYx=PCJJVOB56Ecbl>b6n$b&lE4z!>t-my&} z&2!P!c^%>2T3G(IuIGnj(3RscE$3=K5VITlZ5DIbshF>(DkBeM1xB}2?x(y%4`nZx z#x)t{EJo{{$+4WjY4Kh=5K3jOj!aiIZp5sf&bnRIESCNgk)!dmS#3lwdvym$tYLCg)!Lq$tk149c(_nBAN11W=F|gnc-G2=fmn`(@0Dt zSxU&?;j`gl^hT{!&0l4ZC8+1~GrLL!c`F0ciaZxNq*DF~J#KUFSNizd4xy@YEL+;M*BE1c5bkcQ=9iA- zd6ac<%dI@t$0Kh<9*uO7q4JuIKSv`aG0$)FMCahAdt&sy%TA(6e}xAB5(o1!ea?E0 zp!aD7yQ{4wy$+Y{%9{-5zv$U%=S*%1>9+IhgHGnEx|^;Pzi&xaX{N_Im=o1;4dR*W zZSU2Sw1LO5!Apz!^_b9=~C7)Ct==PEWOlmhyYbhGeSr z@@5D5O1E#}&#S!10rQ|=D4oVrh5wI^;1{#8y7G@kQkn#)CHDBY;rs?|IA2FmL0sVX zeOrIH6%Y77r(P))_9q-o1x#=?yjCK={Z}R!-(Y8cHI@CJv-c^d_A_ei0X%XGRbXpu z3mx`}z(BQgu^#nJSoO5t{~yV7FVYB)^|gN>*AL=Taknc}yf1hex9a5oj6W{f^#$+n zCML5%Zjg$$p_WRhB)@Kz$?Rv|tqr9xbaRRoP1r8``5os_H>bkGP*rApA2s+MQ`|w& z@27!@v3+8?(0zOksZD}@{_;-A;<>3emZug9G@x9HeM=`Qxe0W)x^&Ey=em2=TQ?uv?BJ+Pf(b+eh*VO-o~HK?i? zUJ+`Lu<7L+`k=8q2s30keqw5#nb#(!s|1b@DS+NnO`1Uzt6=y$+*)c}Q|T8+OtfqG z2;c)Q$EU>C_veNQ=RAXxN-+(@GUCnEJ+hplK!+LwB<+HrTOyWhE`p#09J9_2Rs9UnF!m`6kY z9A2V_x>p`go&t{q zc3Sn3qgYd)cQ0mru8H;4ge*Gg<2d-YB~Lw=&_PwbgNopG&am4GGD2dcUzJa&>#7)t zS6NI)w?k&*3jXSCa!1Nh2{fUqyIIoXbQ<@!FeuM(QqORo^JgLD5_Y-=jpNUI&s>h{ zKm)im{(4t_BZ{(Zdc%IM_=)<(;vU;KI?e=L+gsAV?{_V}k~q!mqZ6g}2)ga5R0m=E zqq_Kx*%W}y?BJT2aAU$sxngg6uGY9-HpZWzYG^23Ws2P-_b1%s+NtW6-|+a-6CN`; z87PzTsK=aQU&-6%5p!jY$N0(%I*C*@e3tj(Xx&3YeM7SDp^`+OYw&XhY#pPMIOp{a zc)fc{?8>s6YMrWoUPt*w>}dH`arRZrrBKhNr9Q(4`$<+p@8pDhB)c}BY&UM3!Wg`=PQE8%ikV_nhq|xniPr1bOJcB- z0()FRPh-`#QVJaQp1OtNsT!^43CiOE+>lG4K6{!z#o{|y$RwRKN>_w-FmhK{px4G8yR7a+fy~I9Lb?PI;N8UC&8qdG_F5+ zuq$P?l{m6Ms!Cx_u`%?+8+l+dXpI_R%x~kQj;9zt3uEr)yzY^GpRS_1pK=nC{ZG%5 zzd5iQBs)lw%8Klh=4zeszdLfSd)4##du}ohPE9$4m7@0CQ_;6C(8ib0BmF_E z@{8AUB4@+`Ua+@p+?K1$Q%-?5AWM&)T1LKVSV)?#<_; zuXu+xHnqAOTNf^0A1DturN%XZgkR!7S>ddWNjQn+`&iD?|C}j(oP#+uK{ew}>N8sU zXd`8@DJHIX;3N;s9xUc1O2wAm@fXZ0r})?Sz*5LCLv=f6&e|T2{sGr!S&1pv#k@wz zy3K2RAzFoN;4=!0Xi)6%USQ2&bKX_r7iRryGBd4rb( z?hC9_)7F}8*7GWSY!<#Pwu;P`$4n7>tK#co%JSE3z^_gW4D#KT1E=s07h`MdmJ;!- zyP=@Ialh-7s+!PMhhFdWY&3^8_M45BqmFqMX1;}T^B}bRk6kmlc{j)WZ5mPE%yx%c zA4Xe~ZidPRPwx$$#$#-rl z*b3apEq$8!rTN1VQ}Zb*)(G6jbk6D(oMAnv2>SVK?I%s<489-_`Um$YQ?=`paI^f@ z(dH(ty?YK9u~v-Bh~B7s2wOo}uZXH$z23gM+hG*u{yj zCoZEfc!kCws^3`Z`8eTP33Emb@r+ONUMwA7%~zkt;LfBjoB&}@Qga`YHddW??M{i9 z?|D8pyQRkyd%EYZtK07s=mdB1ukE+X>VjK-$SW|xJ$pkJ@wbGpiitfE=12q`6t|pa zX_@4N+Re=Bcj`KPv=a{?vZsvm}~5;p?a#ZY-^`6@D=R4m)j5H#1xU zuh@bIa&Ptsi4*gjg#YB0bC!KbLvlRk6Ft|XSd^Kr)h4Fkb#-wybbsq@JSrS}6&JS! zBIw4Ozmu~1I91VQK9K$rneM?P-_QSAoZ_v%>OMhjeO59+zRxj0U)qb}^feuMV$O6m zWg+DFFlXkUklHSqo~_&-FUvWuA1N0}57&Vc*GjakCbRMlE|gy-P5vYMvW*mv@hbB) z$n2aH#w#X+Nd-IRma8K1**{=q8%qeeF6(x?)!(GK{nb=<3h(n(wX&jJL;r*m)vS@~ z)EDa5k5Zhw*bx-At9_Fk=5ux;oRzhkzv*B>cyRdo@ZXt7q1U|3Faustpw6oXraV z`ksY0GrWs6euIYidzkgO3Gq@>-Z$0qac<{R|2sX>m_oUc1eEbu-gzdXmsHd;awA@q zmvq`QaE=}+Ki_1mT<4c5lGgg#+3Z;Uwcqi%8*#vMxMYXvVJ2nw_pB}D_dYLoq%t%* zm=buF%^1sR!M5^i@3Hf+o+)WRwewRhxBPl}z@&8!C);*T{0Y9Y+5Y)uD(W+MkeB>v zCk*?&l&5?T+-?e`Uu?qogBP(DFI-hl*@^1LpSa!=blVB8*bJ)eZ{XMsQWN``Rn>8| zR8qTp!??@%a?<5Bp0Qmq-;ccq&OIUd_d(f}E7aExG>C^N2>+!E+{AI%kP7fZ%*@zs zc*O6`%qns|G>P2}jcj+O@1+nqY69AgX6G}0fv@n&K|SF6c|G9SoR}~T-ZK)QUp9d$ zi$~6jeL=d&Lbr9ZGqj1_EvL+pqJdHH%VxE;uuPxzbS86P;1=E$D{+^leQg}&XlL`X z`sDeUiu8%C;UrOA5U#P$C;zeSF8jv8ipV`E;7?HFjPHgCqq&NR`y5kuEE z{ynOw!||6mhwhKR2ywSJ4J$>pbIeTg8#>s1TuOF?QD_I%ti|}I2Xq-#q%2p88LsQO zi393cPK_YVqPV3o0M^xFrFV#owqZnC2KYE}22TAs{U*>l13gM2^ zPrX4$(vnlKArC}LDb#B?V4Ik^eTdD?z&qb5|KWXgJ`eMMgXgKbS2`QNTL`~97Mr`l z89q=7L~`65>X^#7!F>GN-Rr)@G~p?Aw5Qj(Di*qlspcjso*K^mw_wSUT(M(K8TUxr z>S}M}W{l%nyyF&p>-%1tRq^}rogpccX;6Dvp)`N8-y z_}d=(qi^WUFD85rNB*ixo|nRPMPgTFTto}$8#g9q%F#Hha(%@my~X#Q^yhgAD`B<4 z&}dyXyp_bLDO3Zi;Je>!zRHq`S~i7~;JGzx2J75)g8tfv5G~@$hJKd!)XQP1P{kI=5auF98M)GikrJUnk<=iylYW*zQfCA(%?uAtEg_}%QJH+mY?JV^> ztk=!odQ_f=`%x3AigZ}tQeM91OxmVb>=^q#-P^bJ%{PZir^pJ*PgS~urz%-~-ZA+C z@5`@TMrX7`lH_+PT?W=Tz)_hkq4J*WbMhP8n+jDm4Xm8i1%A7nbtlg8I5$c^wRM8u z&)5z7ssDAC)Ug2tDH=b+&jA>>wA8~U1&JS3@nqw3F3_-ki!Hq~>u%VsKRnn@qE?y= z=2@LAT45L(18^vB$T1W60Ga?S6cS zsXNUBl+QJHQr9nFdP~?P|EILyNnWFgI+_(!P7A_wDTQBxf5)iC^WpR&wm-~KSC4Vo z^fRkH$OF^Vym=+m{jVgpo1xG<^(9qvm>$%`gRtlVRdtFEqXj&g;TioL?p*|x{^;|r z?4mX%JX_#mO``^mfnST|REJDIQAOX+S-~5#0!H1MvmL_!CTFd$J*vjO#9=-F+L!V# zFM5Yso10VKUex;}ebwQet#70C9WpoWqilLJcdk^4^{Q!Z?!z*9Czzu*l$BCMVoDQE zrwP<3|3pifs9rEby~G7ImKx=qEy)_VWXvqYvsKCrmRP4Df_`Y^Qa%M z!eZY*NrO0ZQmI1z=2hrzQah0*@-2*T4~Xb;UJH0MU8dFD(jBivZ!wSU=3l1B#Vq7Z z9^jPif=`TBP0sV|ua6l9mkf0}enK zBuq4+T$pBF76@#nm)v7gyGP#{ftY^c@c7qs?%updqy2N!bc~~M?G?NSWXxpUA1M>A zmibu5FZ2m>(s!pf$(ShD^688RGU`Ysc_d?3MyJdjnZ3i+P0V-FjvdeLXG7Pd+}k}X zi}ep}A;-0}Rr#8qs?;BS4aXj?{YW2Ly>|^x7gfs_F)c` zN!G~wd7x{gh92u&ZWo=wf6*d5i!Z`@Wq9arw2|Vs*xS{e2PlH}VQZ^WbM~i#`jQW& zv+2+mTz$(WR+ctPZ;i=++zB$*>ooySF`oaiK2^{|zLxKBtZGZ+WWJ6bd=iy06LnP4 zN7U&$6rB02SRcs#Hc2xn{E{iUu8}4W4;IWF`rR z3l9;trL8HB;F=1!m)_>I)a*;NXXky6rs=st13QWHq%z*PGaum?omh9hK}U1d$KA?; z*i_q)mYd?$;fG#>q1|Wu(UY)R&^F|5(B8xN*WP?rH*iAl$K}q3rJmxjt!tyjpZ>>a z)%Fpb$6$Q>yI#p9I<-H|9&V((nje2dLN90k;e?{CTTJWQyWT5SFM)flAknPXbN|>Pk4o=dZDT|R#Nn`gjos46KcqA?nht# z9rbqQV9j7tJFng;$Ow7Cwt=T)k2DC52=*+(4Pt|3xz{*No%;RTZGbre@ps087w3d zx`hOjs}yi|1`oL>`&IF!u;T}QO_sCuF30xn;4ktqyHl8V3H}tkG4!<5lW#e+d%67! z!5zU;ey+LFi52|?1&u`1~`+}3LgNe#z@7G_PoRxD2O4uCZ%zXoI*w^H(ru4e5 zIbWO7rDi<hzmO@lBCQ|+v>r|viCt%G`+1LM=S-U3 z1F*yEk)4s{n!%HE58cuPIAkFndH{XXEcx9PZB5#lRZ_mvG^la8y$8R-+ubQ^x8bl4$>LhX zU;kK6DUP9SvQ5{Rm)5`)|7$gWUtu%A`T@w`W4{Obt1u^BnPnZO>AbMc2P##Z?Ik&x+f zyzbwosQ=~O2x;Z-;u{R3yyp~aYXebnZo7-VW2bz!_4L9&EtjwP5!^eTj;)In$sXpkYv8WEyy%^slh4bCh|0m-;Unn8p5n~CoOdJsc11x} zNk8e&@$kqi1=Fx+`P90Zsh*N?@+Zdl=V%L4y&AApl5>1#!QP#os%Sd5_z-%g{^n&z z0zI&{kH_u7Abw@GS{-kEBgI}@?B65^<_4#_&IJ-wc9~+ zM>yPfM*q#t$oVsSXI2S4*x~H18Y{_^x^QG{nNJ>_1*t0Zs*IxWmtmKTPdF`Rb=M59bA4Xl_m! z@66o=saNkw?RbDTF-L}0MV(oku61Qj1?h96^+z{(T;u7$*V?MFE%$Mp!XWSSqV`lw zFK{tlai$E4jk9Z|7d22#EbK?RiJ=(z{Iu?Quzy2act`SIt(J*egAcAr?hseQG@Qpo zbD&Y2&GY1fAI)t?ReLGgR*jslvOdqN{II$C8@T>hGuvUF>sm6{2EopoOataR8PdnY*)gL9W~#zIs3&!cyLqs+3XPRO3%M9h1GDUpe=Zu?X$4YbVvh58?6d1=`Sl zl$!Ud`CH)h5e4a&!=Ty)ru5q&;Z)4UIdwRm^W<#`+7Hb{heG@pRJ|K;nlG8pos6%| zWxK|1gB4KWWcaUA!X_1Q5C*pg2KQ#uxMy&;ow4kdoT+8>XPs5h38tYdX=u*G6{Ve- zM3WOV1D)Quo?5$cY!BYN^xJ&-lQd=IMRU>Ye7k#7b=(70DlJ@-Mz^WfZ zoo^*cKs#8E8h3) z!;cP5k|Fi+E`|;)Et~#6L~(I56*} zXngL+cJhslOof#G%X}?7LdNl1ILw1t?Ija;Q!ie~Zp2MEJ$Ghws6CifT{)51ffUa5 z^+NmOdg2h~BvkW!ZMh z-!v^bBE{k^r`a$XfuGG!L$OcGJz8wKwgVG*h5x&!gsDDG*0(t-x_F0Pl3Z)EuB`{H zRn;C6f=cFu)rzy(cSA+DI9q?C3I3n=d`W8hN2%=2;dbsd?RbD^dx?2leBK7P`D%2z z&pV>ibN`jab&Z~;mc4%~)ycIu&qNc#XLw2)XJ7WUF?3Ha(+fAEg3OYk7m_`+-Nb2> zT*~F@-fWZG-=z;eCmk@4Tjn7<-G7SwBuQut&b6aSX%d$AoD_!H;S_Gq(dypckvdr7 zfu8Hdk{4?8c$T2=>7Yj4;UryTF8Zjt+7XZXo}7nw{rx`Y>o|2ag1J3_tIgk=n*nzS zqVV^1{(caDJ=2DTGMLwKaN~UX+Vt$w>f9vtvjz0mTR+ks<61ZOO3oRX71L!w4bAE4 zc@;UWhTji~=BtRBQxgr-n>5R=A}=Y2`YJ*1FdnyiBD+&gH~;HH8r{jN=PcFvL{19a z(NkXNNQiQ%+plR)z^%EJBpQz4o#`$ix|#h%f5`LP@8|B2huKoi>#L7hip~E9+Y9jL z#+vxm$K)1~72eQh+Y0{NMkmw=La2nL*p2BuV8+@5cD>#-J<*BUMcVW<9Pl=*@Dk~b zpE_qpSpP~cgu4ehMvrhb`s@CB-)joi`QxW`WAwK56Kpnogj)Sf|^l1iDm z!WsOed2F&%zO;O|d$@6~n&=(FBtLEw;IWvVymQ0}|E9)===@IK<~|he|$PX=~CdrFTo;oL)NPb9J;)=I+cV!c|=b zeO-6gcv;%$JU@_>^JVm%yecuL`NuzmaL#ep0x>{Pe3KnA*9+9uLj{19w z&vEuuJ?`A-t`of7byLajuX=B`(0A40W&2TuZzF5&Ykrp67~ro>MB7mV?cyUS9D4|I zyw#a7SDkB?S3+Mgi(mP?F6pM+t+a4Q%?k?WCg+QJT|bYhr4Q()ZgM3waP2*zVxNG# z-;`%JTutmvVR1LUzijj<%rH-9oqtWNGgH{2Lp!WH`_%twn0uR~(+(c*c%H6pxwXs* zjzx>=Kl)=Re$d?)mCH8Tt5-Sp7&Ll4ug-qgN`8t!zAn6sXYv%zcOKoxKeAlEFvaiV z+1x6VsJyR@Flim<*Y{)yEH?po)O`MDbJ~&cYi6Lbb8#W%Y9{ULJ*IfoOgd9-C;Hkn zbqcnipYE(L#<)HQ=O2Ne%vL`s(3fw+14l5*#c;@@IADG=oo~UvF`YK-kN7;Q@9L(S zE$kI~l7H%L`IbBJwtH2^KTR|zQL^<=X`9lyRit+7YWmq)>f)1BLa)#Xt;F44O=tz9 zev$Y+ezq_)*H&$MkM}9xZ}qg@32h`sKOcOIck6ML=Cldpn~9HNY~M}%T73)#uM0+S zy5rqOLm0P#9L?S^)^CaRWj@{#OqZUyR?X;RKKqFOQAK?`9Lx&VHuubeYX?h}ZXo}1 zR`9Q2(@;y9nBN9JaUYW4*a~t_4hNI?rH=;x2^I@=2n|#R7loFqxOc&p{{`F0`Wlv$ zk+e}dNTZ~kD(SpXch&l0Fea1%%kDO1t>=GyZoBj*yz;v$)+Q+XctVxL$4pvB!N{#K z-rZhD7&WT4bR^QVQ zA2YG-B}MlMGuDAwcd4mccxguZSj(|_HPRnjJ4-yKS3kcqnBfG?kcC_U({fR5-VDE+&m zzxVI0g59{kn$^vX#`DyylrwT#0_qSwWq)V)E~slLMErnT3gMDh=p7qia;wJdcdFjY zZJ!%GuBxofdx=x9z3aFSANKta=W1zZ^Jq~waLBguKc1y`X{?5R57$j{HgA%{73Ta2 zKvwx)**o)Ymj-%=ioJ%fwxM&jm5j78dG^fZHp!`&Jx$KX6pX}fOv54$<=UB*GrFcv zPP;d4MA|E9FQq-0Rx&Lk^}e)!(gvl!o4zM~U`A)k`6J;fRBD-#Ls^ru3%P1n>MT=p zw?$`q_YRZ0nB%=R+Qjt_r~50;^|d(ajULri32^&ytW~62=Sd(hDJ!firNle>q>1*f z^u;Ws=S*=0mB!@^!^Q`+Z|h~yKksw7Yi*|Nll^$1-kASZ*->fHKk5%-ZHxSrdLd*p zNlyy->6nr(G~N%(P}wQN`7G_zrQD{th;bBV8zISc9GXwah3)Bed)~&qva(~hVj>#r z!Up6VGr?b`Ypu(d^{#hHs{ZXV{$q;1?pNs_ZS3+`>RS9B$DfSV2yoQ>i9aqX3$%m; z+Fh=j?zDyLO&C7JpUmdh+Tp&>rd)pnf6~s+_y~VeNdn@dxqEU;xo2PLp-N)1zQPpu z)BXQL3AG!Wu$CHp3Y@x)_uzC)9At39w7m_UZHMf0<4Ea`PpH0c;(QwhoBQ{Q z!Sayf8mM$+f##_}@Ws0qjIN+1PqM;$7?`$U;Dpm8Y&@d{b7NL_;OWKBh57kLpnDn=t>0?PV>YWa6Er5(|kitT+R_2c$&%xXcKt`jwD3Mzow6F z4a+^KeopqY_xqPEI@=GiiualAg{9@5z+C>~yy>j!9)&IY(YDsHb-jFcDSs-8iT>S$ zG0aa}1+JV(+0#Te^-?=y>(PI#@pHCG=l@HxcfJSc62;aUIIgn_k?i(Qdar-utSCkc zby1~z0>Ao^?*Ba-S^vKypRdmw9Lf3JnevvCWU!k3sLgF{{Qj`C#pmgVw)xoya}u2^{XG7kDOCcn zVt2F4|Jn3@w}hjhjL?Qsjq2M%Qzn{We^JnU`=HAGqepVywX@dibx@XY0$kaSU$_r- z$w+L#aA)QFvZ?n$Pn-BN(ww8;N}YVdZMAlm=H-@GYxDDddpJ)^IpYI-A6ag1fsCuq zy%N=FjGlr#CSv8jquJez&)jNGmrXYpz~UIV#@}2?r zGpnJN<#{8b5141n%HET8#f1GVPs^C_=FCktGTfftFRe}Lu$0KPnb)4Yw%}Url#;1~ zQdda|%Sjz+PeVfb!1SHz>oOvlh3pg>ne|uJXezO_Ii)ZJCvulW-^&|?Va(qqe}!i} zfxhro@30T#aff17>p9EQ?XU4_+!38g5jF#Q+u~}!$*z$uIh`bJtTJc)JExXw;7d8N z8(rs9xRGy^8CKX#;4WAEbN=*&uJOr8)kq~A%*~O1!*_9HJr{XimRMdm%ar3ubBv*s za)mJ)Wo=M+PnBMuQw%eIHMbEKs--%e7(MBycPp5j55rlsGh2O=mi}8@Nk+kb%h~1( z59#u5(1F(SQ5Yw6+`Hgxb~jzyy>t~P&1<&9u}^r1HpE?2f@5o8gA3>W?n=KmCyS2m zLmX67*(EJF-|pwFz7v;JScXYee|wX9V6@H8DLMVzN-Qtn(C818YY#h#zV!NSR~bK% zX|ztB>pFS9=XLCR%-M&T&gSt-ufX|zN!@lXdOc4<3lsWIK5jM5eOJzTMGVE;TsB2H zvAUR+PQqfJ<#FgrUG_Z%+D@cqdbR_oTO;#%J{rozej=%zVA?1NJjK#6Mb>AGl1y5sv4h z93yFzWh-cy=1H>HqMDwe?X6a@b^ZhXn4Ppq|4{yvgef0SoN1dtDh{`LFie@dn&!78 z-nN)LtDf?d7n^h*u(4>n?|YX*w}b!F%htdPiA_u$C(+xk3QmP#pOrrHnqMc_KmSKC zVzbc?!EYs#tg}h5j9kk{ajhTG<<1RFvYD^9~-dht8O1zGE_5M6y;^ z;#C`xev}-tg*te-+MbozT|NFFR57XR)Ta)W0FG@Zoefr))V+%zK zl`34Pa7^J@g<2H~7Wyf9L-O3@t;s(pPfmV3xoL7nQnrN7C2IHEGCD^l-I=r?bY5pr zI`QN9jKJR#87AO!YMIs7#Oe&ns}oJkJ(&G_)`MARBZ($;3%NFzWmngSe&P%*rz=a# zeb@>2R$dW!w53fh1G#+rd#zrKugRA*9wr|p_v8kNnyKpc2G7-)-~+mmTJk*$2e+%G zHSyw6+Zq?qN)Mo>UPc@EIp588*UZU4T^hBNxN`CTsyk85i+JM zhtZDia5uI7je#FrgI{v3y~k14GIj?i;(O5U*Y-ekca}a&yHc`Xu6DQi&;!xGRib$q z=G)Cs4n_;x*qP(>ZK~QGz#Hy>js9}3Z!^IgYUkabvJ7hBxngm&-Ki=bFnPU>uDGS7 zp<1}v&$BD&o{#3VGT$48pVqC1^! zjpf!qYpQv-H2o3ktZGU63Rm=%6iC(6>_wh6K zCY<#ypKI$yTa}}d+{HVc5joJ?PB`^`KcfpauZQ{N`@AfRoo6d_^I`eMizzK{#kYNA z=F}acszjUF0Q(isY4QlK$zb~xlJowB{W|JNGCX5{si)=qj55({vXH*>NY|;R`&HIr zYVdR)GpK}SK%tAMV2;C~W1UbNO|o9|ry07T;Sk{cv?GJ~UY?@+DVuj%ojpgh+RmBv z4J0>}FR`P%{4`bbI>>L7>E0sRwY_=Q!-9<{Ez|N!z=ZQvioww6KY5)^-EN4P>E8Bp z%S~h@U2q?oN4L1rTR6SDL)lN_J0=(Gm+B3Dw}Xt&QO{hAR^)eXth=k_mdZ;i8*ftf zG$-~&-jyHlEw^xJXUGqjO2yfg$NWc1*miMgocfoXe)r1?_>9UZmHy;O9bMblFX)|$ z=XJ6*5bFxpuXJW+9;|2Ye^@wGOw8$U60qN4R7UJ zRH-Y?KD$%rwBbEFZ<_du?qaJxX)jh~zUyg#jx2+UaDb^vKfb7^bXHY(xWBX4Xb8st z9%wUqmALA0JYsF^nVu}a zqk@fEKjoatscF*i6}9#su8i-!k2ib1pLQKqF?U#oDcP^ub;&L%-}7~o`O&|TK6k@o`zM#Q-(iWEHu7MwOug8kaQxS{H3+d$Af73HM*)urE z)!&U4xwP$x@6lmY#hQHWe>|fvZi{EGkei^#zD4!!;t_4dq_o0;ow1d7np%7r%hr~5 zX^*+-gRYG|uB4jyxV+fI{H4=*S{Gp78&fDf=yj`W-rmcvb#!bAzILb0Gq1~*Si*5s zm=<^e7gbH#>d|WGKXLiHj(W)%_>96glV3BFw)Ysf<9w-4%{XP+%k&8G52cx>9>E^J z?0-Hh3#mHAR2J=21@4@M6ie|u#tXSvGqH{pX_mH_q`qMCnE?s*F|7;+TTv7}DaEp^ zl<4mG+MD6f>x186Ys<;**i7%ci8}Ziq}AH4`STRQwfIB7pb6Sww{Gv?>0mdip97(j zq3qE9(0u8Z#bjU}Fm)Ym7Fm;0>9^pn;GaGLyRbjvh&Ip%)TI^V8Io53$<2 zx}SXN8w1B;uFrcTx2#0@TPSl9eowA z*#+qoePyZ5HWip7^YR&61}pNNH{`O~D~V}M;NC!a`1E|>Zpkqhd{5QD%dVK(G1Ci9 z)7j?zyKubqY*!oWUH_LeZm!z7H@b?ms~a`mD^7^D(uD7p5@H{rsytU;wM9n8;~xL_ zbdyItg5NlvCpzf{%N(r`Gr_66khXBR%G||dZU=-p2s1PSF8x9s=tL{}tz`9F8kGt# zO=h3y=pQvn)c=teMPa{ zqE6{Yamo8~yFxu(aXpF7`g)R$OFHk1>lX6N-zpdUK2NRsO;XyuG#$I}TF-Ka)X6QA z`x}O!jhb29PbiHkU8n>9$L#1rPHh!%l-n+biEU3p^uOFgNOL@i!kUoXUPITuZ9z8u3Mj` zyFcponwVRzhL?`V{K~`GASQpS__vT`Yp;1eI&7g zKW{d92&jyoNom*x8Jx+xM=zG;(Qi^imza@Xv=d}d0TY*t+f8?``e85cbI+#CbW(`*5 z7MYeV!MN7Zsdn}Z6^Z_a>-gC9TP1g&>pO+Bsxfrig0pLkJdx78*vqKfD~B&-uI9w< zm^p=0tf};ss<7_Ekx_K?EhrJ*=DhBM`zZ!zj^vZR56Uc*{g0o#hI8#gRyh;@s@b9J zA0>aa0;{Q$2!WLy25mQVO96FH@10z ztMIPq8l2BayiP58^9p86H+eR9;_v!NF*$9&Lp2@m6t{9Dr@C{cpWbx4q|9D?J}q^O z55S~X)Y00Kd&j7qKlAn!;(=+0r@JWY_Cu94UZ1}jmiR<|)naKP?|bd;lQPlSZs^VY zJ{7#Sub8zZN+o_>Cgm^ki^jks>ujj6!B={RnwiGeoJnDJRYFfOXYuFM*&Vr87L`lcJ%7q=rd#ZU5Vtv8oWaH`!NS2XS`@9p&*(D&!v z5^b8>*A==tyMvFR-Z`tNMy~U!KWCa&S|@mmlkR)F#acT%HpN!L%Rd))cl_D-PL$C1 zd*rQoZvGB7P&0d&?beg>`Ml}tUTpOqPNB6l&4bOA8zjy)yZ%ki!~>EUH*lzZBU>Zc z416a|(qSnU@$s9?&rfm8jaJvbS3koN4?D>lo{lHmjPXiv24*^?KcPh^YihL?+xLoX zaUa8N!{Ly8mE*?}NUrddZ-~84^4LWfd23u{{nqm`E*9|FuEcB9mp5`o-e#)v?H#Gd zmEhVXIGO!6WM0cxGUAH+Q--dj9L>qS2g=ul9riMu)EwC``S zF&N{#)k)k-ciPq@FHw%&9Ci3M4D&_mg$?F2PuoH>2!h;aUYm~v15ig%^ROB{;MiJ1CQsR4c-mJ=;@Ub(ulW@>`Lk;F6&yH~R^pOgcLcOpm8PPuIpk{W z=55~3@2Z(AAn}^;+4;P|I^3o5%Cc~bSL{;A#5`W~UaP{_cs*Uo4mGN-jG5(7TOTSc za(b01|Gw3-F|V4R{ItsUEk5(0I(f!(lqz}PF`dc`e#<1c5g+@bJrQsEIsI^ww^6#D z!+oYwi*40qKW9(fSSW9>>Co#sjt!X5xBY3Pug>9(D(xBkP(AvZZm|@HW*N5^qH8@v zv9Xob+1vdd43!Cp4+@O z->DS2F)d8k*J4TQm;p}ZR;VCrvzIi54>&x-c1vEwe^1RTw$9!bvsS%b zl~>*4ns54*r6X%@vrO68pWNywE|04Gh=exIaxe7NyCAZ~m*4Mphrg|LbR}51T zn|GNO^<=cNd;E<1{efO7hUcP=Yq}>-*m;wNcXdmx^14Ku^W zc~^FE`CYr^hrMg3P&liZ?MR1gTfQmhyd=n%vX7X-x8dsgpS;Pp`MieknSB&VnUyjtX7=^BcCt#!b5_@fTTfw3PuhU=h$+PNCjZZx zBs@k{S2Aa>T#qi`1r_|5o2f*GLD5c8V=h(`JwZpi;$QO?=F znHia-xzSF9n{cW9trMB8%HGB|eK@Vya#GiP`Ul+8J$7-=g-09VyS~wt5A}{%rhi=G z*Tqh_+4fzoah)u{!;Em1m&?0oR{c8 zx-N!oACUYvObSI+wfi^ceQw+>IOYA)kcRSo&XR((3qt)0D|wKP_%|%^<7(^gIN3wk z;DvNYS76jjYViRdpYWvK4a0U*b9c&fcueZSZwY%a%tdg;Eo4yd#p9kfo&CmMho02A zCsf|8)JGFSm1uFNB>kM!DJeE-MyOWk=Fq`ldT_X1zh#nkt6L2?L;LvL3(t-!(SOVeY#3d%ACTvMu8k`o& z3pGnBn)G1keSV+uys+Qd8MTgfYCru})Z{qUF2M;>q$bI6JS(&5Yp*~9(F4cg8u*)q zPn=2BB|5%^%Y7y;Q$MsVPy)Nq(S2PdPkFzM1ZQ+yFRLssIG7{g{ca4`cJlJ#4Mym30$tKqY-r%M` z&BgEnWycWbbt_v68gR(X!w8qLjW1W9_>Efh0_WOzDu7=2xK^ADU385PL9$P%cyXM! z4{|K*)77_k<#&gcM&?z|J4p%EEcy#(aH&LrVz}mwPStdrPy_trLbWD6udgK7pJ_g? zx6LE~a~GjgEX1Yvm>F&#Kd)tMni|lUD(aksyKi`M&&qN?uAVHQi@6)SR9gkjP~t6h8J&|w)c*6A#5so6 zQU{zv)Tg|9s^-`K<#}$g^`6n*c6rtJ2<2S4<}*yD!Iv`0sJhGdymu!W`#$fN!?Wtq zGRLbM2$S=P56wge&F|>X!{7Q?N1>~7KXc{ETkw;(K z$;_FAj*_@7U8O?{}EdWd+T7hm7UBA3d^-IHb~ z?oP;&u!h@uRNRT!1pTbWNAGx+`&q-NKcCiq`ryf$Cu5&1bh-CQ5p^!{Pb){IJpJF( zZc$sKnms%HtaWrg(~>f&4E|r-5H9jka*gjK?o4dKTk~adcKE)4Dn5YfSdP~?e^7PE z?3*-{Gx%NxaWBTD=!mwTa|L#*ccr|zKxlNb8f&7N5u3nO`%jwSj_W!ZMTov8uK=e`ZzYXOlc=h z^8e!M%Za|s-JYD#A?Wa_B@gjWaQe_^{A`P1dP{oouR4$2#fJOC%?fc9*Ifxxy>Noy4vfK=z3lBV*4@i zR$^3QEgAh4N$GhC0=bOMviW!P20^q^GEqm$a-Y2=pMM2{ZZ}7Hi%drvp0-pb{AL#G z#=G^rc(*g{Cy z%&HTZJNAQKPk)6x6ekX*4NuS!Dg#7q2ywsD?eL)tYcK2OlJHab=uS1NEA&Yk>9o{7 z{+$||x$fC)!t6!0t~qqp?HKH=TUTe@Hag}t#$jgamseOWwp(NcZquB)O6HAh2MZ3; zb)Q6D;c{A`H$tEiwGG$Iz{vOIY|}*E#@B{ss@-i0rE0=D+G(*#Tn&x1d0@TQ!okK8)O;E;ls1quBw2 z=;^cZn#q2J;mM1VCMIr8_%Hr*Ie=t60vhnk)lnrE31yF4$QxpdLlmDC++04Q!wT%X)l>WTmjCPaehHBh$qzPY833o+(e)1 zdQn3+tLkP(<>L3sZ(FvN5ehH6Wy2Th9X19-tY<1wvW)9zycrookEjVK z2gjoEpx3NAMX{Y!AL91{Vx;LY-OE(8xEARf5uqBzr|^4=wJOGlv_d3941Th26Zv7$p3kWIm~@~=%6<- zk&G%4r|O%M+k1XVV>>MKvBtVONLQzJx{g;;WnLDRCfc`mL)ed?TTk!boq{t4_nD&C z%3w~Fj(s17t7BVjdS7w#Z*y`*oK|yHTL-JoJU7?$7b&Kv$X>H`%V3@xWHZAP)~jZn zrwaNbJ#S6?-CPOUxuH z@LMsBW2RuuMPkc%ze%Im_)|qhsVirJ+atRDy2(8#kWcGyf zeyy=)92Sl*KxuBQuC1*oqbp^&WPDW$cUx@!vAXi%oW!BL=>vKF{^8DS%3p9s{;4$g z+C*B~5_7gcqh3v*GA-9%x)dM5Mo~@Kr1VK=@!Wff0ngt^04&RxB=;#A_~w33z7Mf#>Z$$p)IKNH_h z5+Q#MN57^Bbqe;=n3VLATwZfW=`^#8QuLKLV1Iv2 z4BgOtyJ4=c6hRi%lfOUxccGQ1kHsQb2Yo(|(%3Cub81OF+AYu(osF#|ohkLkZJ>t?hw^1d3>V*E@Wr2aQ` z8uRTJsec)tdZH@UJ!T8m;@8bDtNJr;*?Uy1y%_G$Sv#kWO#M-+iIID0-(QC}3x9@tW)Eu+nl&U(%9f;$ z5*H>EO&AxSTW6lU@*gAZxx?)PxAfzAPfk4{VOHXIN#Ao34GHOk?GNOxE~ul`w-N0k zTcpavgM2bg)3g)Pwn-b6=17`RX}Vy^zpI@+BOCmm3Zk^B2bjNDJ@Sam_5(dDBXs93 zfc2jVKSz~+6y8oR{!OynCrw}MrX&AAdhW3Buf#aJ==7s`w(sIjx7Aej<_St;oi7hp zZtK-GTh_BU1dV_!OGC0^jfXhY&RCz{@Egza%hu%M=o^t*<;}V@^KeVe&`5g9WV42b(^g7Z!*9ZhK{U=Zp*zHtN2z}Yv9RAnjsZ44 zPL<-9av>3rcsi_!)fcrqcgqSM`6%`OBh=?kwMEBL&&P@^rt-E74lfsU4Xk!7WHzn& z0B8L%{YB^Lg}PDav;flm>Dm&$zc9RYq~qeIu4HBKtVZ(agR%O+%;Pi>r6Y3M*8|%4 zddEv|9W{!FKNo%UNo6 zf5;Yb+HdxQ{p6b4Pq-Ym={Z|TpYyqX)4Ja8b(kE5RsV(sUx4eyb&kKN=f^-(JC@5e z+z2fdHVD3tr(10{4Y4K1f3fhxu(_Gfb2e<5PHOM-SsW0r?S<_PL__I)w&$!4)2LXp z;Cc1185s0nd%#!bi+tht9c@dtqiHn?<W~l<>K&k!*W(mW^oT{oW!3kU6p5ia1(`JM}BX~ISwt=>v(omf_vM z$FAZi(mZ$7~t@r07sMip`9sPCeH=&l z2By#{`g0{L;~L*-Zp|-xTa2S4FQRl^mYZp0%{>5x(^K05t8i&0^K~o4XMU3D${XrM`$L6bax5E6-x{zA`+vtcqux>jPebY+bJN&e@X$VYeV>aB} z@OjpxGCJ|r)8jslSLa}4HSDbb$MDIZ9KYtx0ys0+&# z+AHPjq?(E46Kci(NGU1f*IuVuza3}8F%emRIfLc;PUD#%~XAxBGMdAy-??+)v4~&!t*%o z)5kw|MA~4-&X%U>ykO$oo#6bs@bC$?b{X3_N;W&Q^*#e%(O_Ms3x_VT3N6<4Yq|BU zelU~SRsQSC;CztHoQEy#0qZ%A%8K%Tre$1F8Er07Sl{q)p6$#!32%(tgO~nHD`=|f zDKIHvxys^_krPZ`XsNq(#<1no*>@qbbDG0~D1P=&!S=XBE^Q26AE_%Vo@w9}HBCT{f$| zvU;-b0&~)>z>!w+XDV+qnA>;9_m6}CjqPL0#B%jii%rAc{`9|^(|J?WCmd0i7RVBY z>3}?+4*3~P=NeyC4{^l<#aGEXmtA+%6!WfaxCcwnhvH!E6QBPLq znUghTT?SK=uGwx)P1BlS#^Y*;d{sB<$`p^Vh{dLwHSymzizr?fr|pHZfr(PziCjK~ znLRM$AAQH)=~yG_-`T^uQ^Ycf0Y>4phvn-ZhrOs@>bGHc{Nl$#yF$UR@Yt{#>fLL| z7u__UL1lD zs%_MJQNyEVL?uTpe%2HNJR7qs_E$Wrk(}b&s*sZtTBz?-%bWa}iN&SFVn^i;=F&6z zW9A#>KrYx{yYiV$lN0YIzPRrtc<4>>3=f zIPJ71)$|KBEUmD;uQ+k{#SDt6PtiRXlb~btJ%8uKycQF$JH)~0JJAhd4#X6Lf=kUJ zOpt{PH92XrTxnDJ%NnYb6BFWeBAc5qM#r?z;C5X)={B9crp4`spf}CIc+Zrimim&u zhd&;H&IMzhm}g(Y9Q!rq9WIG|8e2WCWZc=9K>izXv3rtEf!{UEk@?3I`?F*#x`+lH^id>xYsYn`P#$}aCX+usYZZ^&2Q)0Mb6 z*4bGvvCm~;&&QWYi1l})n%)ZHi5a?9=h3k&KkP3+OD@8%JRetoB{pqrbWG*grn;A< z)9163PM=@txw1L#0WbBv_)ijYt0p^<7!4EiCH-g8QZ7DT(jVVsq6xrE&V{Dqe3TX5nh;fbV|j*PE+WAR~vyYW*^@Surp2@%^2A znlJw>pYbm4jC_9cc=gh!=p^%L#$UqV-Mo2cxB?s7xOnQg*vy%-PqceKG+ES`qz6DiOIi+hV|Z&?%hr~m zHbvpNSFKXH{Bns0i9X?Ra z&k@>IC>QEoYt&Ud>MlODRClfuVPA!}F(KNQnF zhK7I%APT) zAF9$_8JTRIc}3>)U!59CTL+(t^k4Q#CW=@V>37}C>b%y<@>cj29=dc~r#%L%q-AXW>y4gop%rqf$IXU9EN)B^s7UC2+L;uYtOBt@q!Za+gkLk>2 zQeaYrO|DCk`(CBzWJXh#rs(WE!X%v5;eYCLeIj%QjGW^9p`2Y#{+mTq<7`~B4^y(x zVLuU-jj@j;>qLFZtn+@<&xX37zJg0W#3=v5d`jRZaroRfnAkjOa|`W>Lk}!r}8J*YWDDlVGnrNyV?6r z>Og#&*77K{G$&p%6dz7q{@wccnf35`NPQf833NXoL;Ra8>K30~Bm3r? zc+|Tx4lDKRycZFsqji+)ts)Y5`3A@umxMKi@UP7N>qWTIk4D>>Ryxz{k%X|9^{V>V z?1+zC4%ICsH@sUB@n1w~4%PDZv|^DrBVr>GsR0?ylK97btyz#IZ^UL=%&di1x-+TjY=_w zhBQ!zwZ}YigZTW~rFs%MJo1!iUc6h%CJ?E zan)n8M$de<;@Poh*Pb84Hxu)nQxtN=$b6hb~A#UJj zz0{;^<*aQGJ2U2o=xosspT#|!6aCP1s3tKDV~WR|iyjkQJGxeM`{>2d+01Adj8k8V zD?k^m%op?`j@sCNPNN^vB%QSusUHX^&NbCwpRiWtqBi%HGyO02YHW1u9XORHZi7CX z9b+HGETq{sF;i)I%o*;XTCrnezk|PgPBOSnO!plVbCK(5iiyN)WlOWhZis0ab31yE ziG`1&%X0s&fw+%il4EwqtoGclK5f64jWLZO>=U^9K`;W?z;zrO7e)&&OyT`d-?O_q zW#%=xv4EP>MESBcx{e3DSkrixevMybRh<$)89RN|EZ{SkY5^5DqvH3(hf(%B@e+@a zRjtgK)S5r|W-U)n5KdQPNu0Cm; z`tC*W_IJ!RT~Zod8nbgm^-%Zrr)q=rYOec3#{Il+CDm#*<)zQ04*CI?`(+-+(>hdL zRckPi1NpYzj$70yOjosjKdFL#P_vUyLE{Z_xcgP=WX2A*r@Y6Z@E)aTueIw^(3dg> zmY%ha4W`vJ*4@^R z*Y$uZrf=f)h@p`c)v7Mx3I5d4d4{^%i(BUhed%A|hpnWi@H;^V&pdtk=VMu)!^;6W zIpv6arVrE@wU}wbvxS9)UgE>apL|kf#c;m7R7q2~^0FtCh<`8cSGvfH)R=j(rMcOY z<35!MxsHQowhw$`pMBl$Rb2n?I5R8`nbi0O=CvC?|H{f&+Pc(Cw)dEhi+}pxgUkVZ zLx#A9UdCTT^#YMwIf>R`2^FoQ<7wR)tUVVfm+PQTL7X5zRxR?) z=-LBp@xEsqgs#P9>9fckR--gm=h-YF3i%KJ4Mb@H( zmDPvo*N{jvR*PWQ<@A5(39Z+_;}^oZTe~{L%qbYrT3N>}CVA|%b%%=3x^cqP#Rj88 z(~977$W6S4B@L8`Xz%_ubj%*6GM%+8GvSD#;epPYMWA4BQNmt4E`!{BX8Y2=ra_&= z=&n#p@2dQ%4s9yZ*6(o$?2_|dCx0I+w#kpRy%|xBKGMM+I^5jMrS`$1aOySaRhC2G zb(Pwc)NFU+0eBa;uOY)-RK`0KgYV;r^pVjxStdOHO}$XE@e&*|XX0waH4~MRxB)Vl z>y(|3tcKU-(iNptWNI~Rr%j)T@ITU;3-|~khePBSWloC7rYA-`gUM$+qY6htPyNbX z@JKmbKL0VHD26ZLGhJCeQ-8b%()KXv>1|%Nq{x3wcPOFT*k?TUr@b;X@{;Z#&mxYB zbTe}%{1h=zEKvd9J%O?QL+>AnkuP&Stxl!C;L5di8htL$qb{)te0I}m~G@SK$p zKfk$Qzem(CC+P`?!%x1)BhkneF?2O*)ZY;ub?z!08D|F6K@$njQoWxOe>|0KIIi-2 zznRwqG1qr7?#tdS$!~I>9?&c7XPVhbPLX;kZIZutte#A)lsG!!jF|)v<@Y|b&X0(-$O#7Ny1QpEWJERk2V%$4A2GvV=A+Z>L!(86ZfGg@vzUB-m zC1`jjWara_B+A^Dl_}k#%IWo#E0n-ts)y(3L9x&Z`bBbQ9W&uai1o|jin&=u^gx||_#^-a{+6Z=){HyB`ldT<9y zaydwOD`sBI0H>ibeKFV~F%g(@23X!Crf1AHrwcKccy_a!Usap(JU8}OY(ea{B8Tq? zd~^Ywcn9~xUucfR4>DyFTn_Nbli~we z)jBfHpIce~k}5dSz5mvz1XjRd#jkaY;q>D9Na6NrLV$W7XjV zI)GM7%CBo#HrIMV*BWlM)=9BBR4Bk>HcH3j>|ER}xt=EICcIbg=?K2MA@ttoWvv3! z=NiMow_JZwfB%BK>e-;;EBuJ>>o`0j=y571lTp{ChSi*a+hn4aa~9rH+3+-Fu-t21 zxz{|QA?hy%$;ghEr#&HuThfaAh{vS_^{bWmE>ev~Wvt-5X-gyJLegRpkL8<+>b97j zr+=nyGO6 zq4ZCJR!y=J?*#q1c@u{uuGW309)-S=$;k=H&EV5u8sVtWC!wvmNLEn0{%3meBXvZ1 zMfz*$dS6(x3+Pd`)aS0L!$vE5cwMJ1*ml;iMDfH$JpU#gI8gi40t(~|I}GRE67v?9 zRcud9U&^0nI3d;(JW-^#&^b_xQI9Js)5zML3|DftI5%?4N$Xd44wqXA7vh6pX z#_&4cJ4|Qahq%Eu`LB1N);=o4VxMh>+-Q4FqDs`g(9kRV8k3!Rz{%RW@{Y9Eya_84 zDauD_X1PUCKielRnoyZ6uODJxX)cTKh}O`aqh_0}7nn#NYNCA^JbZHaC#DZ><8Qmq z|8@;;iVDwS8s2F0kLE}GsDsE7j+PPD{B3w$hzO-MXVw@P_6g=QNUzIH6vMOnO+OUL zq);y&m`WR`7vv4Sq<7&)`*hFj46~}56__0^)qs4hXdN{$#|qAoVx8iUu{Nan3hP@H zIg9rFSL8|EzJBCJzX6Yuyl1XdiDnaKPL)oy_8PQWp`Y|?ra&BmUkl7jx~8Ye9iL%I z4IOVw0=%^!3VnW;jo1qOId$LN;kC#+Db{YL6ksV}A)E-%qR zZP~A>u5i2$PnE?K2zH26%YDMT&@Uz8C7*m$keLRi1!s%gsdgaoj7xu8n?yVez$aT8Ay-VKy@xJ+qppGam6pt zn|pg9RpApFc1&1l{JE;k{aTNYmm8ky?+TZz)TnQ!s_c*_se$ZH+0{nIuRjRHLftHQ6b&cjt% zty8nKRqgR4&v{P;Rv(JncK7Ym^&u@hjKl>pn8#F@67!C{L38z1EmaY94^~~h#MjhS zwAod4Q4gK#a;TV!lPOKmANCzi@|V5-mP)o>aLYU{Ue~#~ zueG5)-q`{7OJ(Nl9PXox@v(7*<#AU7+nBXeVprNQ2=m6;neh7u+{~14H$GVhg=g`p z^!wVOli+dlh4#xXp6A-RC`$NJXU&iFU1&{Xt)jA}pW2*BcyFEf@^Y%l`Zk5f=Y^}q zaK1|LE}uz)uVKYCRN8!QK4NZ&)=$M~6+J(P>WB1?TxDY&Alm3K(O-^u9{1B9IxfT} zR?*{gsL8l1d1}|{8SIk}q$~s@y zi_P@cEr~ztHoQ(ZlrQw1{)oS~kTp4*Sv{5Y#B3o;nn73an*MWl-2XR@qNnCfzUAFo z=-W_3JT*%8^b9mikOeKt`CmaVmVBz^7pQYPp~qREhh{`_Qc@NjpSFpATB~mT1ai*A zB>VCM2l`d-(itj64f$EGTB$w&IeMK*saZba{ zg&MAF=xMzo_axp|4L?*Lxdu9;^taDm;j>CqzxyQVx#Ys4hQca%S5l$#;a+Fd`PY)6 zcs}`G{bV9V8$(40-PK8sRjXT1f6o(1=AK~ZQ^b$|CTCFTch5E$M@gUUGY2L*WaY>^ ztS0z>V#z`@^L*HUWBKgXCZ4?Rm;ONq)ygJyM(Tr8jKjw&z);*itj ztXuG{mf_*PriTA4?I1)1JX%iggxbbYa)z}%v%X_~me<~q!zqZ%6;;ij$=dTkW;~ZF zyMVUUS?p7a%32z34u)p8_-3D&7MY^b;W@d(=Apf?|IMKxoSs!Mw~2CI%lJ4}icIoQ z6q|%sZ(5S4nMemCx`Q4owc^|nXT}w zy%^dm&-|C0I0S2{7<75pf<8U;jUJ8N9OLPp3L}2N@lQa8f->eICd==GS_|FZ44%wV zKQvp8x2N-9=sE*F%%yP_!VMeBMvvx+YY8!5!1BuBkTskdsFfZYSr4Oopx;+X|Kkh! zy5@Z7z2RR)y8D@kSE2NWSmq-R)s;e9k4xaL%w;@1wUc`f;$a&jCQ+3GoVTY4;R@9@&<8t>&oh}z z{s}ajWH#e69;B`6MOsm2!Xjqy8pnumx>1;iswXZgb}K?-2*jE@+|hP6?l;{@r&1_AynXT%fhMsTxeQuizNG8AP@aAWg#&6suA_~O|f$=bvgY+U@NE@ zbwW>Zeb-mx)5T1+${b;9)D8W>KU+fg_Z8eek2yZBi5c74m+Q-?@1eXjg@Pj-VIi^- zl|=o;J$pYa?CZ2omD2{V2$iiUDSlhW70^UYRb`WEDmuNX#;SqKQhE{H6bZk;J<`nT z^?~=DYz^z^NNcCw`CPE~L}0c|sD2q2)m)u)l;y)9iiu=vsPTG;tEQ-meyq~>fg}2& z?i@Qf5H@o*S5z0B6Bajd1b^?ePfc-uM{fzIVO#)X>3nDO_t>G=#|+OdA}{xl7P!=E zHB)DkVLFKPS7TGsx_<_G&!IIY;8dmby1#6Vo?zX1!y45fSb-Dh`t!C9*>!?5Tzd(Y>Fq&XA@HCnmL`5c9YpgA0)oSl~qFLj&?de1rTAMxeY~3bxiHv=YwnR zgM_s5-_Oy@Pv||7J@LH%uo$wpQ+IkX;i=k~aB%Kn z8P9D6K?3)-R4e(Vt#FO|>2DFinBaYVZJW7bi4AymZ}0!2?NHKIs+<^L;&~G@L!ffD zR}b53fk>i{NN%F2<1SUYmP~JB(ZuWGiWb3sr9COx-Nk%;#0VqA2vbBkeeEG3W?uf8 z_?)V(z;uF=BAc+JGoqoTsy83#NRyMlIlD*OnP2gVUg#s?@elTs17eB*#&w0VP3@Vl z=#5fLPt#2H$>;Q7t0clL>Dn{BS;OIJqP}bA>|+_ly(K;Ry4N&M9x0#vrOOo<-)S=y zbEw697p4t$iGLQVtruU=}mE} z>i!8TMb}Y!BXqsJf$62i=IddJ%dJ#%ta$!2g9wdI1HOv}xuTJbrr3OE0;0G+g(nvP{w)^Dx8tN1=QsF!%A z5_ugK`w=a86+F&P?Te$n{^aqcSo&7=9|3K8oo5G}Dl2sV2oHart0o`K`YCUCI-I4n zyxZhpb>Wvd&wB2(r}}g)G#7BA`m2@lV)4|y)DgNIo1i>J_HnwYgBkRzD8{psGh&zL zY}HL7Lqt)%o(h<2aITL%l!Tw}amM zmA+tKiwRcid_7x!w1W5}B;s~>ihSu&4EvJr^9L5U*8kfR{u5_UXEDszJZ=}{Dw8;J z@5omciHLSRCcJ>}5$H+wvIyq7+{arHRp_nZzR|zl>8NdW)qC!yRBv~=9@hRvjXs7w z?zH{Zc}{xIE(*6xd5;wJZTmd`lzZQ%otF1rskn4MhtR!o%f7ruHK?p<{XY5h{7#|~ z{LMXe)a73?z>lCV0|vLkc{$59vJe-;lH3=rw&sD%z=N=i_JCVqr}5QP_JnxPi}mWj zX_bucSDe4A;B_B9#X>4}10BCUkR$41FYRg%=>XyDQE$50&n8kS`oaD8Jl?@GGliuJ z`_IUdlDrpLTt6@SeUA6{E4hF=YLqKERhCOAXlC|xn%OMJ(;8K;>-6mEs%mSRV`~8i z+8p=KGSO?A(`@?46@HLFuk<;=BpEb-I+UH&QIjlhPf5EJr!r@s9(_lk^c}~tPXhoCX8shb?kjzfkp+paCX94%5Xeo!QIot z%4j{A4`Nl@vFDi(c(1O3lg0XdO!fKJdOFlfTh)466#|#i;h-A7O_QV-p>keI^m^Lp z6+ADVN_vv_^+$cF15=HLSmQV7o-p4uh{3MUv+n+^K4px@zA?3CZQ@9m6Zw07upWQG z4K%?sMnmNJ`dfSme?IVch|_Ec&_uocJJ9DX=zSWZ$764(TW#W=>N*3rsa=8vuJ^lq zjMKkwdd3S_|HppCZ~daP{cgYGh|6dL2mSh)b;L-DWaQ|i7j&e} zY_EMC#9frDPdW1rDMZ|jH?uPa*){;$l~XDn~f9egy(-;(Mp z)0;8)3>S^yv8b&(KzVU{YcAD+(6&3D(I4{DhoS2OmBcTrG7i-IU*-hLsZyk*3e2*y z+JRZaZ{QTKQrWWM3>DNO*QAk;aOxsUIhk@hj2mdWOz3KyWUUIC=+FqOSbA>F7oGFK z*#cq3;PT6=Q;YGIy-A(UNV$9Dc{#bY8uLTF=DF`+z)fh|jjfp@R5LE2_3hyDPG_|( zV#Us)G9-%{+1F*u+BtQWlN%ab*W0+~W8Rp~&~p+mY)6j_;;#OPhuFVo+h_`9FqJEQBKQ*rj1Aq%d$bU9Qu7_f>43#kvtK%OkRqFve9y(SJavO#T&iP z`LuiYnp}68KQ1*?PUNky^xCY|1}X z-Suo@jvO+XMZD%k3PTn5Wakhs!RhpZ>lq;KTQE193YY@U@xkqL$2_V)HP1O)B-W(H z+_K8a;pgR&4|06RQrn-CO+IQnpMu}lT^^8c{u5e{=Wh5y#ny1Y*9U&hGIWkg!BZ(H zT?}4FhDU@4Tw-_io<1+$Iu4U>nb5o&KmJ?nbple~mg7DzMmvi)|06~_FXyn|%HbMdzDh*FZPw16(Bg z^{Sc=xkm@~(dNvb02}`}uhA0_Smbr?J@Z zyZl-mvF3M0=k-(SP^#*>-p(x8%DR@+R4W$f{dC9Vi|qP6pWq!mnf#|NP=_GxK05a{ zn%HtZO}?eX1^P)&Gihxy6|Off(8~(d27jo59n_F3tf*JteM}?3A8ykB0yAs>G4mmS z!au0roMg3}#nl?ScZ&aVM#=5@R>i>w!tg%x$ zyetQ1_&jAWBegeA;?sms+2?RYk|p2*Ihg7 zSx4y8S1{XM2|FS5uQ2Whc(+z}rCpGE1=czOT295ZXUNYk#jD39OxEdQ0F<19X)nUV z*TC(a!RM~idpN+?zc*)KuzM%#i#bhaj~P&S0#u#^^*{G+^WgG0SUb}7QJDI&gg?A~ zgL^l*=WG9U7u>ukDv0(yj$_t;`p<{-b;_pi&y$2giTPv^(j-PDWH4PT0h&H@`9IsB zg!}KCGH?vu?($A2eZNqBLH@smjs<@^-HMl#z z{Ry-khb515Z4}I1D~9?;?9<K4*!t|e! zP<)eVKgaOfn8e7WIQ?ymGcs$W_XH({X=F7fJoxjZmmIDTt7GqYIy9~_u8)~oaOl9t?&iE(M=5O58 zYh`3s(w~13gYKX|uc7n)<=QSs$U%R9cRGL<#|0;xg>#Xm!+Y~lkqWAXYE3Pk%O^2a z->k7T;58gaM_}_VO46oaebh0EbFy4^K^;s}QHArVt1nIieuKL9CL}h|k=K7V1P)Z8 z&eYLkq4i;_T;^5%-g4^s@j7j;9Gopgvul90PLX5n8TzSvhUvpFM8|@!L#I*_r&9vw zdh~mjvpLHG&ztPA`Ox?qTzLcLy9MLDq&L?k-ERI1O^0J-mNm{xb?gUgJHXmL`0oT9 z<`>N87whIm$h*(ABbd<>%;*-TlQujsjqrcg+Xq(K7_*YHaCkn?@0pi_sxEg-S=Eh! zslBb#$aIIx{bce6m{Pb(&B-$CZ5|FcAM3qt<=?GBa|;w$hbx}M=Ptm6)0{3l-M0^S z{137|#TC!F@3EZiObZd>r)OsANC;J!C8*Xr=ECFa}-MpuV`eW>Q2di@Cgy15Y>VDTpqcP7N$ z0F9^P#EbCb&A9Ity6`6Wy9_5@27`Zc`bMARS)MTfl23K-0$ z{t8Q9;rE#%Hk#nKYARal#IN2#+*HxunzF9BY1gGG8_9n0^M3bd5cr%KlUvot{?3#3 z2cPpSIoW|&Z6|xtRBns`JZk~}Sv9J5ZF^Y*F11%ZIs`jz#zXOjIIlf#+uJzshxlLIdN2db}m7M9SXhV!A!a@37hTQ&+GuIt!rA!n1H zd$R)dJBO|n_bJymMB97yk+@A$o=*iICG$I)F4mcH+#4!4fWYO{lV#AuGKaW5B{@>A z_#UTevb@m^YsjC}6D!gYi^-^^)tA00hgKo|JMXA!`H#*R1&7bc1@6$9VTRRxP2y3!>arDk4@SI4 zrRYJA{$$lZ?%H9`TaF9QvhFXmzW2872fRs5X|OGE-bqUlE{WNmu-IxY8heMp} zSl~_UH&n-^B#2Xxl3GzdHm_?%b>hzIxh0@?dT3tGJjVQV=MYl>E%pndgC5A zZZFyJPAZ!M-sDkcI`&B@@A6%IcNEl}4Era;=#{Ye4_tktSm7!h4yAs_QP7L{wwW;9 z=Y7A_PS5+M58!TkpZp)Vzl)0fx64~_e~nKchy`|FyZ52_Kh%M|-XR`_7xldZ3VbW^ zO&ePBD8Itzw(Te4p3xMG{kHWtew7)1g)Od6pf~iTYJW_H80fOA>+g7F8xc_-SUe6c zPlmle`a8zuKH1T~FyZ4|)3Nfp&zX&TJ25|P`FW19D%9yNw32Fay*1qGD4Kg$4A)IW zr*DJ(>@alQr4!j>G2nfF<76zZ$wEJq=}l!X%|@@ONn0+&Ss$B}jt-rX5}nU1klHfb zd6PqvFX?G}QSSUdRmpz`_vITnbKseg>f5U5`QC_{-ID&(0|M`Y%eQp}e5N<~tB$L} zw5E6Q;l@ENdMq7!q8iJk^0CKL7R&PNmVY{o7hk}9uT!xus62UqA&1Jzmf#C%<lcC0Y6!L?6ZiT)!K2>s&U?F7yax$f76qYbPzc;e|H}F zwIZVZmu1nqVZbA0-G-_*?Fe({)9V&v3EOocI?OkGNPmYXQ1`HIT>0qKU9gH)_(f%v zAr-OS;?}u#RO^27dR<+1#76qa@_lOc{G5(Boxf!bugel_Wd;Y@BFgh0{+_^qj#+^N z?B|~K_X@^y2_EMRzaJKfEfv(4p(3wMZ7BSPij*(0-Cn^g@)Y^wMHug1`tu4%aGlQj z8(ac zEk38d;{|wDoO^hJUY#RgTrU{hg2vlccDV~~`!cn*y<4?al2!HG%FwqerkoXPPEFN* z$vq`q3$W0PFf2EOeBN_k_ljEBXaVoo#-ymB-uG?%wHh|s5QBZ+yElXFFG11<*llIX zav(SUgxZ`D5~de36!d5jj}`H*`P_aE2QDmnsDT;3?%t|COIEsZ0hbytsarh*O&|M~ zJ2`;&ibziBm$n*f-7210ZM&|Ln_mJur-);=xb>?ow$#>L6uyBDzJpFM4}$+;n;-M| z9#PJ(&L`pbG11H>k-w*%p96X(JD&s6cZuF9R>_NaFDv2iY6wr@3c4)`VOI9Kw#(imJ!ggbhkYW=Vd|*|${n{2BJaysZyB0&l;a~q zXbhx{kzY<5x|O2z$oZ^eYPa0adaQQ_#2p}~JVeiu+T4dt9g8J-2m>=45~$6A4gyK~ zN~H2Pn;PmYZv7wn25Kg*%R9$I;0t=6?3ejHjT!&q=>Jl7xSuMIDwuF1arq0P^eD0W zDM-7QCcOf~T@72m)the_^?4%i&39IrPhjs#PUZ=i?<9P809ASn|JP*C|AKNi02+7a z1A7;TcuSRMR8l4@Sd>-oUeXQNy94LgP6?a@cSlkYyUFd1v1WFV`RmBHQo~C693EVk zBeX8=Th3)A9H*qUwGh`=E|;0DyLs@R^qf9dtjS?XPp!=l;P5_-_);)0J0ELWX{GSPz}v<+)?2(xdEIX7T2-gY6woR(=y$NWY`PnUn?`jXyM2NI{!Tmo z0|t$SpR-}$=d!mm;L{vPIU6qhhV^d16BoHWh-3bP4_}2`*D=ln*Ka|!xP)@hJTIh^ ztjDUK!uZQ{>}_5<4=PWCvTr0bg3a|*F}2mxxo1KVN_21PbY-e^4cF@Mr45F_(;)FG zpK1|YJ&a}liBE5Yx4*i!$~}vGzA^3@38g1{ML#+2cj5I=O7$c-A5g7Vd)_`3(D%g% zcX8i96p@+cUcz@T4UNm&8pUj>*5ZkZ{=UWw_Pj@5m&;A(J6si?T!iWI-rRQcdk<~n4DOpopR=PBj|6U|TzXrzr;s#+wLR=bJ?YHfV78;V0@lIH!`uLy zMSI&R(Mzb)Ke+yj4Du+fcTzCw>nFnd3{PIkhqf#@Tk=-Yc^G{WMxRIuk^g-`AJmF! zm0y6-wRsOZ;KU;x0b@8EH;YV|Lh2(cx zi5sdO_S;h(%pe+AV6N{ReFGQj@h}4pA9v4bar-T6$79~&(sEuILK8zWi|$KuwUm|x zZW8R^JDPK-}n?ZWpw49^W*Z|S~HZjFif5-;sfKkh{l zp6Q-Z@H*g$YUSJyU+o%X(*vMzJFgk7(^g|#`YW%l;ctLFS95w-CbpT!1D!+y{X?sY z2;RYRdwYG4;D2iYbE~^n*Qe`fDpi2{*3&7XEG}Q(=MEt8+rCS@Z$wAd;^14Pc6zOh#)l%hR&u=UTn@l@-xB+k!EEzF;oA1X zVzR$wvE-NV<6?3pW%YeZ&%=;fujdpwl2qyw{-^5fzIZYs*oQbqY3-1<5XYYbX@Hg@BSg4od<;jy(R{UfCuqgv{0|{w#fKx z&Wq;w@0(PrqV%e=-1bk^^d*~15FYZ#-$<2q*L2ofumAfx`q$@<{1xKx+1%EH@#6lJ z=})-1-jKIyp&G9=r&lfOMLzDUqGI^4lq@N?@Zdu_s@~-|`G+FC9uxkO4`m)So{9;7 zOoQ$zXZC@NaZ6KeKEQyhW5PAe5`0M}xDZZ}#W|Gs>M=$fB`uc+8Tb|dq`Krpm=hrZxBAi-z)wx7ry0xL%VxsU?i=hJSU20t2l61N`k_ zj#XEFqn6O4L(qfNGO;IhHQ+#cMYb{@jvGL#40!MVaKc#J@GqxRuyhkW`jFnlM=h%XWtWHAt{c!pS zG~eZs4Z-a5T$hL7?jDbCgMB}`{S*d&3ki3M1J?ULhrRNE|FPYDzj)@KZg2OjK)(98 z`vP-p&&uzf6(a<2G#Q%S!;&*l;!DHYGC{6i2FGn{J5{lj>N>qiUmi-&?_}!*oLx1c z?8h*?w9I-N8gwTvuliJgx8;?qnWkCZV@dU)#5(9N}9%x&3pgj*3Cw0Mz``Ff# zAo6c=#NRn@-~~VFSNtdOk(lbTYT@&V+3?;MsULaiA@AE48d6FIIe$b^{#XPzLM%6t zLu&!tUCwp2%&k?fpLFZz;9ho>?sCS&!k^_u4)Z&o;!ijx3w%RQrt5lc6@a=C&PDjM zLu7p4;?8;p3YU=^F2#SN)}>O%WHMZ z#ivyR3$BKBw3YX5gs-%)I`)RUL*efn>*p7;zjK1ls^e6~Gx|JS#eW`KZ+E%Ou1Yw) zIXXqH$eCTQKzV+PQdyn)To3lP*5{y`etNAa&{g1YOY8f`R`>pL!=LK%HQlYrFgY-H z_%qz=Hz>Oj;x2`*i?F!eaAU6?M!TTMSx%+L*yA0FYDTyusMc-bHQCLq&N=ba0NQ57 zOe6Opw+q0xcVS=~DAxq`1+u=KU2jZx?gwETLE&DIc{Icwg1h#|jQi84KX!dI zEqN~fJV7RRuKQO~kw25=U4c(8_LSQ)7g4zKpY)R1cyl0@TPLR5NLBe-HQI+N(YkSG zb;W_(V#~E5a#^+ks1?Tr=Sb1A2nvbz5#&bw8*9oK_oz4Ltg_xG6b z2+?S7PKnX#2dmJdOS>!rg-c+@>HW>ai+za-eHjLCQSZ3k(e^Wqdb@1!G|}&;*l{;e z@o>@cpr9+VBIPQt>Va6?H-W1%fy?rd%uT6MXvUP{iX&LfvK>v`Umq@)tgu}N8y<6-VUILZU(^VYs|biD=8_cw~+YHQ~( z>*?oeVfs-PyQochkB7CF8kQESg^S_HdGKIUNzAcHjRil`$v1<`n8Xz7@->$!y8GVL z_u*gJ!o4)lBT#t1%@D@0{&v}>ocT;wMb>C9?-*2GfEc|r6TyH>+{)+BA1%Dk!nVuNT?*5;js}o*4 z6~g`oou^{CUwGx0GRqSo_#E#y%j?!~NiU%mZ}NXn{r`v{peXMNvh%xC=1BS8YjFOy z&lVO`q@Viam&6Kzi4S+g0*7eSS8(HJPO%XAnOtwY=Vrx|a|XR>r6F@h9Jv_vIlrw@ zUdH@2r>Y{95;${H|D!m*TneLZV2jnT{oZoBDlD%9dEbQ0UEy{IDBa$+Zv(5_LGfPB zpJBpZ(XYSazM6pzZxcry;nlh#!n#iFc&w*$4!(fA_JOpbvE1~Lnvl7cy`l;grGd$1 z?{gA#qp~cC%M{?>QuZm(|xiGQFAZ4iwvqaLk;$m@gI-~*13d&!A#II{>g)R7eF zZ)RHb>!RE`j<$X}UUuWnnu7mMR9UcGrNTzHugC}Qr$C>E#HqPD;>{(^8q~F3Q5W=z zW4xhrO+B9a{Qo$7gcaa>QTc+PM|!WzRn~~Z;`MzxSKq;eBgFD|^yqmSQb=8NVG49L z6Kq=Rp4r3N(}n`w$vPD1nE098@Ep~?KU$f7wmz-Yui`g+;or~%{Uq)~<4CICb5^=6 zd|5BKd?C2{mBUPWTM4T`=N_1F7rDAGD2$`v@kHHoH@UV9A70H_vlGKP$0NPbwcFO) z{~+{H7##zvZ*$Q;lr4VDIa`9tmJ(cF(FN zX2|Z{i$c&qui{KT!6TmMoI$VIJ&g4kA6aO`A>aDAh~t#+afmnklIQ;I(E#(kA}U!; z3%CbQ_fn--JCC9=AAqR)Y>6Xu>Df9w&ZaJW5k4ju)dV7*?_ug>SUQlR-&IWXp}cNG z_}a{4)x-1J{+;}71y?&zFj|YGUeX7$i=6KVvI#Fk+(7+Oe6VBj9he&@2YiD+;BUY6 zT^{MZIPWt1$5sl-kM@>XE~o1|GLGxHJ7v0i&|z<;upDuHj;rP_n?c?t{LuM1t*YAd z8bI9q*l;eV7kLK%3w=tsTZ-nXf!?D+{#VEKPeH#&V5-F^nW(TSDDtR;H3xrYi6@ zIVo#$0r|8G7{>oFiwltUU+dg{oOe5%-Kq}hN2}o|-qbE~yxm>4m&2>CaMAUEsnvS8YtwMtK<;oB#ySQ+ z3t;RtTx*Eczn|Owo*TML2eUk4f*UNpfB|Oyooc8-Bf8)}> zJFSAT7k#5Z?64F6zD6VehyH#N7Y;MuBEdE|LTyeBY4g#aBW<&Et`(zNKSxD0&fkiVzW2+0 z%T2f1Z@PoKZnvyL;B?06Kfm{jRF9_`!K6yMg%cM_icZXG-zX|Rd)eOd9*yKxI9$nI zRbCYLf8MOg*zqjvc$D~VHYEN|gtt|Gc%!{>v--8)?3-6y``g5=XdR>lc z&SZZsE!SHmIfq`@aQ&>jm>I(`9Ft$-vBWZ0zFCI>Nbl1^(XhH{Mi-=eVyj ztJf><*sA6jYpK@0zl`uGe?K$Xa-uV3o<%yfGI!tdz_9MK-Pm0C_0F~|G}Tf7~Dg6T0m}8A(Fr8pk-Nnue6+MX^2buTr3RX8eo_;T?Vi-kYjBm*IER>3^TV~XuD#uBQf7vROjCCIM4;MD}Ec`+%5h8RuDOm6@JTq zZ|?IomGcdBu*wM41H85{><@v-VeZfB`Q`i#cP$GfPVXCKfzdZa3}HHY#`^wOY?br4 z?7yy^!Cr5=ylERKW zt)FNb)xy!ip3BBFrXe;A5=LXi$ZuG>iNrQDR z7{-6qPJZug%qGBY+H+pLru#xJd^c9!H-!W1w!H5>r)&Hx)1KD)9x{<#gAUz#u(%Plcoim84D#Io4o5q`{%dmQBddgcSnIa#a{*rIo!_d{DS8q3av7v~Pv0!PXf=c6vvmV>V+CaR7PSMbX; zaI3RlsXI129%gS)zw|w4`wltbpG;m@2cZLqd@S*fec@^1EfvJc{8=xF+n$HVxsxJz z1zxxByy092Bd)4PNnQ7}lFw+XYN?GJ$0#cGT(RJE*^g;%Z-T~~sMEhvrvDZ%p5nE7 zYCk@yE6Ec+hR~qzDg#xzHY|QgwQxnb;l|K-tV)3KI+J_|iPy^zUv$LmO+GJIb3hm3 zEBauZ#E8@JWF^w5Yhu3{#k-9hf%W8oK5*{KnemCN@RtyI0-b7>%Ng>)r#Q5Z(x(G` zW>&H+Rl1d^n!|m>8Bn{E>4u6{H2^3@c(-MZ*ixsH^B1+!WLL-pbz{ zGWUYb9dP14*8Y(^MV~e1N8U4DLMQ5l$Cz)#GUEjatCd~79H{g&s{buEyc zec7XB@YBMS?BWo<3TB)ivgd{38NAC&+*8@OuM5NS>fWK6_X{BKE1sQQ_BN0aE(bdc z;k+T98|l6CLDqC|_dG^k3_DH(c?(m+^Qo-L<+DV=*E{mLdwuH1VvfVU=L4s|+DM!f8;{X%v8EiRTSLA}V~T4m>yu;TFdnHu2? zP&tO)af#0SKhfD=bdb#|fS2pfIfcSJiE=Ut3V#oITf*A@u(v1P+Y-b5$X-?j61S(j zyy#TWsW2TTXD|;OsF%A>tGTQiIL_aFt_3>J{%x=Q#c3h@{Z$>(D7<$(rKgn`bX;gx z5ortAnQ|i3YN17CZOYP!67-qbACf4NJ!+bLXy^&Ge#_MjZ*VNFfx-bS9#IveyYh8O3v#%82DrgIsh^KcA3^0XS^eY!xLp->*< z>{()k-r)3OkV^%+`~SetGn=LvK;u60jf<@8{ors{r!Lm{>P`jCV}A#8s{#k=$bA-| ztUeEY(?R57a3e3y7a=Q}4N_-HyaZ7m$+2d@4`X1<*`QYY7Yx2c!M((r^^@xdoPWSR zm-)LKW-W619yV`+%*))KM?vljpC?1-VGwa*&?!~V-wv?24=f!HQ%Awuw_)!X&lrFU z2b@*)A#*^R?(Ehm@Ae6V3@FlLz1!z_?{}Uv&9mqEBrAP}v9NU&2i8xnt-#g4_v+Pf zbG^$|@N~O~;w1g~|9oMWXy+S)-2bk4W0(5_nsvOm&Tw(mkAB5fQ1_hJYOUXP8%Df|d+WAe`+tcM_JH{R$I_j^*;N1k12<;wERucA zT8c_VsZgnCwPeeZuk5l%*~*$FQACOCkrb6e$r>#bkzEMcQg)Sf7BlmIe$MYd=W*_G z?lSkDd(P+ee!bqW_xr;aT?FGTs!pw_D6NFc#~nY9V!tgNOHJ_Mj#TLG&^Gw5x>BVl zN1eYuz=GFc#J@NegAZX{=2nqnSdU@ANA(d)$AkaLEF&Wvj|1P!(@@Q^d^cx9(xvY!{KS6QG=7ug8S4n=WGTgnx2-Hy2e+&HW!5`WL z4;}<>N2}2o27d>tLz;&-d?jDDQtoWCSU zH=l>F&D{#!J4bT7j*9w5&Z8_&)VFhydmGexZi2R3Fr=9AxgwsFPiC-y(Ym~Pm1;)t zM`R7_a@2Ohs_Mes_83VaZoAkjo)UD)zP#-UR*m zJX}hr%WlT_QdR%{4S$QM_bmYR9^z3h4jTj3JpkV-MA&zS%xet@`3#(WTz2;bdEEXO zX=6*^t{rXbV`~@7EAq9YWON6}><;(3PV&KT%jS-8`?aW_*I2%+sWQ5s%la;pBc98B z74pO%s5)Bg^@CI@g$m$_xbYyb{l=@mkm-HXI}EcWZmPs0S;c1!u^6wa7Ewj7S}~>c^Jm$#pesb@m$z{VZ3;cy%58aPf(?g!S-X6 z?ftU5feD|MJKyPgJ?;98WeatFy~t(1J$QzueLytxDb-iDN*W^QzTJS;kW zgjc$Z=rlKWTv)wQvMf$v?yQ}%!P}f)zhTGgIWWQuRPVv&$sE&z>C`V`$%AQC4b<@8 z2%F1uPS;k~pDV{1&aC6|O?#p6Qgfbu${8}BNEl)~hpA*~vVFuO>;{64j)9*py<@D(()Vbd7y(mxCNsUHB+_))y zI@DIypyCBgzLr_hb)Kvfx?&||Z8i?>qD+6q4Rgw^uknzzyfU-tgP(A!j^~wrMSW+l zXe51&*4WNi+7Q2~W`wQHkMp?Bgt;@b)e4_5_f8hQ`ZR_V^vYbu>EF%CvrqPLDPPYP zjCrnGV|abOhSt+$fxBYMA98EG#(&k53paFyYEHv$gq_{*vN80iA8FT*!I?Vr)_^zV zFyb0;I@A&8!R1Qf%XeVSIn)mq@|@dcOfSov7NFzCsqs1H_P_XHHmtkC_jEeyfVCG6 z{z?sA4C6LIz7v?^SCru2amO59wZPUDxaUFWz04~&(~iG`{wMgTeu3j(;^E)nslR&O zDoA?Jdz{7ELkF$1u=Pa7KUC$b;(~%Ah%&02&$*qg%hoxy&3XKXTlmNF`;4I`IFIX! z6#2WU$_>OLWqh|vwpJ0dl!K>bEziNvN5n0SWzKuq6U}7KpY?c8F-{jqI>g@UDtp`3 zwqDrmNcr}#zQ)_ukMcj7?))Bn4INY7z-Sj!C?<)TLQl`_5yPG37g}%oCK>(j>CL~= zIZj=Rz$B_0)<#>sDWpTbO~MC+EOW=sM2jbjQ+ETFVAm-w)iI16#j@ zxcegC!B6_A{f66KhOvL*x4-FVUXIp$zx57tU>2n^hu$*H9j{d!$InrG`g0d{;=X#* zk^L4A*7tJ0`{+WO@ZCQkZDGn$k}PjUXG>{b#ul7con?61X1_{x8YkO3$@&i0#Y~;< z7IH$ar8w`0w|`NcPiB|S5&p_KU+>Z-uY}A}C0(r=!r7r5&^;k+Uzw*?^yhWD>@DL0 z*#vR-(VGv8(+}xqe8|0cDq0!!jbZF~MBj}&XwJ_;+{SWWVHWU)Q7@}z_`oPNzn^e} zexUwnGOcd8dE>);1b@h!9gRzeve{~oZpCYF#xzRswG^ZK1vc`mijiiN=3dxrPYU#E z4EG1RVW>6QgS{Lxe*Vnax?Wy4Ri#mO&NwsN#A82&=+5amZ`PCgf*IIukRN=&Wdp3X zq0HiQ#^;xKXgfwO+CF&JKnm!HNc~(w4IOQCUkVL=hOQf7>o?YmnCveynCCgH0(;G) z<9%K_Y&O49K3USxbD|h53aqgSK3fqUHKy%0pegsH=RQr{Z9_MHjE>ySmR8VgxXW&o zxcOjeBGi3c%gawzI6XMfQ9=!xcE~O_5kEX0;ca6)yb2_K+LofedujXQ1`$Sn*qewo zSFkrq*+W4KNR2OqxrbRA{78dnZg z*Ndst^ZC->_sdN8OAW)Gr(62DeivJQ!>{_H7^-S~bF*~R6-Bk7QQzc9$dc>-J!j7N zW2(Z>L)}!mbeMbo7#(B=y<{$3+iuj6g zLEp=XRh|N!Tb=qzSzvFT^Qyc_2F)f^v7d=NSZlE0?;QKnRRz!Eb(rfc_|m!2haTOO zKd~Nv#Dikg>du*aKtIls2kk$$BuEs4BXQ|L^?MZE)Lh=;3Ac#1Q+ zyKQCF16Oyi4ClrL-pZ5N7ou9=9Oks&r~WYXnA`_<7a9xRM&^t;@}iS?g(X+oFj4fybGqt9B%`>ru*317-PT+pxS&M`(-xRG30 zZK&Ht@1Q54@D1iP%a0jfXN1eo4_(Y0T3j z_-2{TTKq(nA|170R1KV)Ln+acfiwRF|E|HkV|41hP;jHmUC?q>kK*D@igntJi5Z8lc>a_sl&6a<8j{UPW4V9(BmwpR^SsMg zeG(R%nNa}JUgN~d%t#hf+!ED67ZO>NgVQB3-74aY8=|P>5$yFjd#s;Gq!#qcOK2ENFl@(GkgrBq>SXj`q&a8xo}L) zw|wFDY?+WHj)u?6Wv<@Huve#Cs^*GL3E$TYiSEM~mqV98BmSQQi{CdM zeGY{eU+!JG@~cJd@-#g~IhNigkU%5kW%EeOABK)cZ8;&J)j zdtFzD)K5USCNjIv;k(a3?KW<|5IL3W!0w)&@iLTrCh|x3f!Xyi-ii2YbJt-`z1FTn z|LVS6&%?3hc3v@@8+r<#bg4rZF!F{TZ7Gla z7^HntY!N(Rm&{&O%{K{ENd++8O1{Uf@bv~vKStLsO1Vx<_={ehX(r4t_hYi>g{*L3 zzUfi6_#pjy4YfWvoWF{)>Kp9K^{_ZZJd54>8vedzZ-yQz3+(l`X#TzZ9&hc$A~7lgKZ0t9;E-SI4b}X6fh%h0DX>y1WTx#D}HbD?yV^i%S$m z7RtF_O!>Q<@I^ShUl!#L@#Y$i|E01lBOUo&xUwe540qF`<^?&J2c0L6@hLtEhs)x? z32F-db4C@8+el&h1LOTk^t(Em^KuC1MF&}*fxOU-u-@mXQ_nd|Yf-C$VpU4kD6e?B zu*!xM*`s6p&`D;{T5VZL@%or1Jy_mp5-;WmZ1_$2sHR4XE;?m3h`4VB9XjsTTdy=P zX96595Y4lC&>XDW@PLU?UGNV)(SzXbDC5y*DumnW;#VDuXoUkeR=;r%Ch@qDtbipB z`@O)GbrSQ)kR85+^ZttUF2{nG<0>n;W9AqghpQlYUCrjF*zPNqPIANjtA8P zufyE8Dbr)DrDNh;Af%8({N(|l&J)T>%iXDP>>SU-Z~cID7!k9*3<*-8+g~r(?l4;lFt?_B1+jvfGDM_lEA9XQ1%^bpk7d)0c&? z*D`K}t)a8@ed3Rs?2~F9xlbhWkbCuASE2^oj1Sj`$f4WVWAvdCt?)(`1ZDy&@YtfzpJrXyb z0*S*}IK@n`>tXOF*BhOi$Ft8yuIP*MzqzsEyWnqQI#yTphMh3tmw7UqyEnyIzm^kg zHh0KeZjo(9hwZengLrW=FUfVBSP5~`KRK$3>F+b1RD#CU>21xpv7Q(ChyKpPWqwCO z-#4-0sYa%K>Lq`H!Rz4hItcu)(JLL3xDms+pvPZ^u`Nx7a1*-UJ-YIC<9}{~xwJAy zHZUfB#78s6GSyf*j??CM2>cz_>#v-!XCmM8cA4L6a)Ft0g6FwoGq`msVMh<~WR-Gx zw~C~vjpB`{mK~zbl&`_tv6$66)+t=gbLr3D;KJWPicRo!4YXK<#T~%nPFVkhtQYl~ zJQ;r%l{tk=sT>71h0gr2jP8A~wJM~o16ONOoI@w6&=IQ-L~9H`1Gd(OeGNUM3yggc zPWFx-X$m9zVWz_&>?AC80)2TB^>`Te`Zitpeb=8`KY*w~aUOv6&cv7B$Bw7!gtZ92 zeZ%c{smt@C%B7{a@Iqa&rrY`fraJ+P-l@~&$KH3ISALBJ568Txd;h)=d!&0keWLCj zALd zL-Nq4d$ldgJo38VWh^E1bqqMArB@zwJ|)@uM!QQK+j>qpR^y7Z!V^L>&2~ z9L3*`j!TY_aNK<62>Okt@;x1TEk^tmh5A#v%P^QcnR|MMUl*MHe1)g*1tK7VHzbq0Pz`aH+jGXoEp0gXq>4ZjyTr3cFh_k_bQVh+!7tW=W` zz6TcHflY+E$|T&PoUtum*3rz|xbP7?c)w+xZ174oB2%LN2(w`E7}>iIsEornwub7q z(Ap^5&iFc%2Kf|*Q_~pyICZifBra%VzLOIN$m`INJM%A$>3}Ta9t!4ms^@kUN?&vG ztTpaW$92cySY2tRFXCj~qRZ#tMjaSY8|Jpdav$UAZ2@oZphXAgZ(SN~;J~-po&vkF zU|CoKYYbI9sSxxWq}>Bmf5c1Az`so}_Y0Zdd9ZN}C)BsFa)n#-A?pqdHguU<23Nn~ zhFZiEHO^)5PzO)cr?RzuEn{VT-?c5&DfQ#3>W}db<*fQh_IR#b@mp@cj1_+_uRJZv z_x9k!Y9m8D03#mkkzv^LM2`;kNmlqg!#(dS>^t!BrEn}46jG;)jsfs8i^!9kv zwg)ld2jTcL^zHlLbscd|e=2ob6-Wc^?WQ#AiEelBYfOUNp)bc^QPBX3^fbTNcd&S$ zIzRgh;WZN6Tfu5T0FQ@e~t}e#kj02S>gS1#HH2PJ;Z%j zN38h@WbO@#-_fmR5+*#BLt=^ZVhyjvF5RJ5>Lpi*>#7J(bTQ{pDd*Ay{8wF_UA4{L zFaRG8^FIx6{*AM~CmZ|~|J8=5d(I(!IsOtgpHk1CPb~c?ZR=sK%X)4-&xbkEx!%&{ z+uWMdjRbr6uD0O98&nC0N~B}zDe~%pm8=%&jGXYzJfEc~)M3WjdpNHeSh`T&8oBH) zAKVu*2X%T3mF_Ki8MQT@NGn(MgX*`QH=N z)2oo>D(065{wDDS--rvAq{8Onkh;%hF?rxBk-O>%SbI0bY7K3JKlwTL+u@yE==93OTz{pWlMfV`1mJbmY;t4~Nn7 zAoBuQ;3;yxF*PDg}!e> z-Qb|^4Vi=Uy0h<8*OumDiq0;d_02-=x~*?og&XVfH8EZcI7!gifOA7czJ5QI$?tsLy{{PEe*#%d7tialgQY*Wm`}s;B$Y{ulX>C-F@P$dtK+0nYgMyTBrps8h?N)vY8rv zm_K?6*T5{-%VdPZEXtGJABPW5bxZ{|+`)17EVn^>s!Us6g!>(d4{_<=sV4Y-Y`8eT zbc|ymje`{?%u76p`7a*Wr1_( zk@Y)I$FDGVBTw`v&h*d^V2WzskNB`!ac8wL5{wDOt{Js~>>9Xegk z_q-T?m&&9abhdAeH|zMYW>TTY7=7NN$-NrQh}cPWQU&g;GRCQLa=rIZ@oqBXX{sJW z$1)Sm2)75`o`Smju->2DTW6&EM6P!}wlbXZ+|6j&NKS7cA5BZ!UN(ld>WTd@|>ovbxrCd5_?dQn)kLr9#wl@`XTB#G_Hol!z`e98ob_cv2Yos5A@dmUF zdUFT7tTvQ=UOux4*HAM^aVySuC)RcgmUulhNzhaBFB#Q*e!{-Lr=-zylM*@`}0vv zhpzANEx!(5gD0yiXLB#JHgqw2!!xe?a%Da1HC_3u+H*R$i=5Lf;BJ4O=!V{ZoY%GI z-g?27S3Q3kwC(QQU-TbCujm0BTFu;_2(#zlJZLYrn${c=0nb18<6o2I$)HJdHXa@HElVY%F=U z$Y}}nVzHdUnrp|eUFLz_#|>|K?#z?wl1hlflEq)auUF5J^E9_}GuT_(WedkpSNY;m zuy&Fo>ILk$Jq>yxE*x?n3n|c_!rD)`14G4Ja3h@NecsPC80KrZ9*I1mv)ci;D$t^D zgTVF8{ngOwqFl`NZ8H16LVF{8;nbhSL{9vkDo-HkTA z_33yV0@tQS_rQpU@}j(JMEV@}{Sh}<2zkFTQXRpI)3Ju_*h9#%RWZI*z%pt^eGeXo z#*Y~VA2c!!fW6&}malMO^)gm|gZ+N&{%Sem1CVz;g>erQ{@qx6H0KRC&$XP_$rG1| zxKBCb@gu58?=w=@HAc6gKtEx*&$vC1AGaM}Pk)@a9|WF6f1WK1IT!c(PLA>`x7J|9 zpTLF9u;L&#cLu{tpf1PEvXF$krDMrC=(7p%CCt1~2pV56Lz;^_sj}+d-26)AWlIaI zP6}NmE6Af>XIspzsvdb3KdysaHp4ZW!nEd=HZZ!gTyhtv)&TeH3kzF$)-d?}BBTt= zx;Mmbg;%$A{bIyXtGW*LMgdKmVcrknsCV;7Kj=R_MOUui-p#f)!q4l$&im=h1-(Pq zdYAjRMS0$eK3i^h8Y+bEmH94-|^9w!U=cqPmBmO#HwtXTD{a7A;DMmY=!Z236Gf31k8Orvd`wy4-?e7;E zEqC9G=KP4f{mZ(kbjEWZ5 zdWKu@uw!qh?PpfG))_xg1D@=WL02q@do+1Qd(Bg|EJPttRHE`XhU~LlAEy>4HGb>F+@L&8b z3DmrtS^H&rle0EK+dam&jga>zW8c@VC&Anya(9hzl7>{~z+CzmJ0DTiS)RgJNp)~p zqidXd7dW4jD3Pc5t4>>g!GzA?LOU?s{Zz_L*M8-PUac}XxSvD)%0%P&ARQ^Y$UDAl zX#xe>8r!=-+nV_9;~Yg5akN{l#d)0b!QV&VL>>r{LqCa_>yq44*%=i)_Alr2MQk@7 z2LA?cjzwA1G^i9k@S`#JaXL_WLF3CQL+0hvC=)Z;c`_CDF2#Sp$A5p<+4Zbyf_&K*WQ9x7j>?H-Z&87C zH-BS&{W#i~r*OD@&oC7UA3@-?GQzWDhkmr~#CsFX6k3?}9Nd$Y&AighSzkXw-9{9y zu6!T$Mcw`Qv}VKI&pDuXaA)lkk?*EUhnbRom%B>gBq>QnOQJlNqp&rBv#rGV&2^>> zS>V@=Nh4^_V~kWojZ(i@LNDFr>NtLcx0{S@7kODO$0hQRUgjh%obx|5m%*7;&RF;` zHdzT`x2Iy>a$yXuQkvE4K5SiqP%2 z@D-!-HJW3lYNUjS8QrQk^uy|d8*^(lrAgPoh1=u8EqQEvxgIIk*pay+yU(E3N1AiH~zrgp75PR(ov732TbBuZ*{2heB_N94;?pm*Tr#jv@T;K2Un$Y@x{%_BSX?OHL zTEP6ka2tpP>P4)#j=11%ytjr|gleaXusB9vE)rpNW#6T~Zxecd6~JdN`hFF}6_;du zi-=3EickKwM?!V*c^UI9o)PA0*iK>o4IXcn4gSi$oEiC^7s;@{EB_n%g3hHojDpYe zMLX}?|8rsVP`Er%Mt%~M9xgxJ92URs)=-YVd@Ii-}xE|qq zuEnWPln=V9wFF$wp*40c*qo}D%oR~&rXx612rrHM-ZB2M*z!l-hskuGHk=W!IRjd{ zeq8^!Dm;w&^l>fb{K+GWvz52vpbXC%aqc|X;8pU#LtyfJ+Vfld7#*QMp-GPAci=KIB`m4{(G&M>~9Xe8e*BJjn8S;Ly$T+pf|`T z{>%YAhmyHTeam~YlOt)*9c6%f!-N;G-zR9zAxqhqQd$ucE{~zz3RB99{DaR{f(lO()Lwe z8gz(kLUmWAX#d>^v2)pazx{YS&f5e!mloqZ;C^+?_i6d#DsZ{8{ohx%y{=!SoeXjh z2;C7Pe=U8t*g&vIyx26VtCQ6;BYwKq!__jJnPT<3-Sp~8W zifeD-Y`j6NTfrIjP<97(z@b0KK=lVBcp#@aC#OK=#gR{Ak$ll98g!^dN*7h9$sy&% zgddgvt>*GR=lWB8A49n?`x+Od>)`Qn8uUrsIgZ2PB)Q)t@qIOM{yqP{ z7U?M(+yAI=!|>n^GGLEG;$gC3ljw18s-JuVI?syu?gL zxpjmN{g;tAlRr8quXITx^qn$-rE#iT+^9u{K8xWXKlR>>ZBxGgEiT%Z<84gJojcAY&94g_+}hs`e}}) zdu@9P(uG+RszbSZVBiyY>eJA%K4rQCRICRdyTH*Qcy32X+K+NPn4Ub9a{LNJoh+{# z<|-UTYaSxde}S=-=^&{W-~={mhgC$<3Et%>Eha>Q1o-Q2*P6-S@`RgbKb>V`Kt-Y0No&2Yqj z$q&(s2kRLcQFCZqPaRTK?&#cc`Qt80HzvlNpg~{LpYk7(?vIf86LTZY)EoAb9R1~j z2jzH;4n16tju%`%PJynZI=C_|Iv*t~H(zB!j<_6q%)auon0pZ&`sZkVhR`RvP1GCj zB~Gj^(d>oQ)KoO3p4}yPl}8>cT_hi${Ra&God*2_4E{;1zfrXR4W9fhjyykW8VznD zPxLUVTx+WITXed*cycXO9uIS{l)xdXn2F{po^e6%p+bCF@mc>-`3~@q}9Oj*NKJXnDfe~KHAr#_Fuy!zwBp%-yqUguUATbNO}F^1L=7Q6rzn#fc>BbRv- z^tcKCEsyO5UsZ|a6~~4Dfl{HWXa!X_%+Ig}?_C(VnRmgogVq6f@ElmT zA3~3VhofQR6ezt!7C3Al0+|_)7&oteZk>2SCygVT8Yk2sV-aX9eyB)_}0by6U zKJHtrSIhjjZ~3cF^D|}oXaDy{3iKw|t1#tlzF*Mg|G|a}nmhRUH=+NJ@1BZE2c-xl>@toxqYI9qvb6cp}Hd+Q?!UN%KC!Bd4 zXI!tSYUv}Kc(JW>{gNBiC++haZ&#`GFYG-j>u`w@asewY$deapg6|cT-7Ri=Os!g9 zDs&&HJc`!xifXoxAn$l8bU&Q9uVXDB@B&*yujpSKd0X8|b95e%@x7pXd4@PKXVxXh zY#|Q*C&iK#;P0c3>pnbK;~eE<^+q4<$p0#9h1!6P9MP9}uuenWi{jT5IpD{58LP?u zwt>1`WPgW1+$s8R4CcRB<{bP?UT7T`)>_!RS-$8pE&2?9^f?-IerIo%E%~Wkq3e4C zXLJ|m_(S}bonUV>40w@Q874yAFZ8PYQZIqyDklEoh0dZw=aIuI9(PeKMM3&n8G7`e zIjX_pDjX&?;!3N0Y+|HoLY00IA~(hd24e?PXm_(Qgsm9yA)P2!)Aw$~h7ZME$BlK5 zijOw* ztJz~cNP*mAgte2^qon6dHXh%N@jgR$t|{+ZO}4O;QTv66|8~H5`$FN@jP0YaubKGR zo4D5pu=WR-5gfcrRnml>lxKN&ckm2_K8{Cue`7Ey%=lXhgG|*eD(JcoK&TRQ;``{p z&18UU;h-<`8$aoG3&__8R`!LTgJI(g=s1Rtc{pcOA6q6u+F7u3uu7xR{8OP~_am4) z1J-^jA3R+i_Y3zX%J(jWz_Yw+yjrA>xU6P)-w$0+_q=(s$78)>ydKhTo1^f}$dNTO zLf|)}4DwX}vwOs$2l-U5_`J;^_{-GriazNbkTpHNY@|hJ`<`d<+}xD)()9C$gb;J& zNH~P$Ua}`jaYJ7+U-AXazcgg7;Z=#2)3o|qMJS0Pj_a}iD{8vRc`VK||EA6FwLil= zgu8IvA6<{Nx7W)5uECQ-ZSVq7(ImgdRH*x!UnKPMenswgOvHct!rvEdZ^naF7YA+x zgB$bMHCBsOl{@-&kymb5dD8}_0h@~bUNf)K{gAgiRpt(zVs4ckxyA8%W6mpa*NYu5;J|5)ybBAgRB;*L6}{?_O9DwS!4b8U_+&<8rv z^}vDK(4|^BI|CCgukyDb9-PJZd`4w|ksR4(X~>!5q&lQ8o#jX5m_Ega=eZXuk~To& znIiG=aCxxaJTJ*lRilyBGh#fho}x;2lDNINPSLx>_50N$?N)iQl{&q^XtPO&==m^s zOVp!&8eMKK7Cgbo)Da4|pw+c8Q*b9v(w5jleH^*HK7Yy7y*PSx0*yL_?)MuO@&?3R zNH<*PdbGT6|EQlrnECa7>U2G|COI&iDx5ZXGtW_-Q>|-p-M#R2i(6Zb&1+-tZe@JFcoCyf3BwjINiH%7|u zUfl6Nn3eO|chGq|oL)c~UJK{K{0v+1=rGsOQdl?(5`U}qXbj#u36hSLMV{>0p%Q5b zOdgCUkF`$pjF<7{_i@|Lye25x!{G85x7S6^>Cv9G+~-*oz263CyIxi|DAAk!&tLq< zZ{6NYUq0=zKYZp*l=NSH`lI58@9^H;KJ{_hdnVjX;y+6hbELZ$GQGEQfETAY=frTs z{ERt8E2ZhsCE;&DSX@!;@+e%cApcvJ)=&+v?JnM_NONcpWnUHXgslAk=+%R5c?o6* zFZxSbyomQEwUCUNG}z_!(yX9SnnqLe$CDcRUhi z?;REO*_aPa*Fogqq?!&b-+;icLD^Xt><}FHb+2m)(O-l3ZSm_ymij)yjn-Op@6Z=B zr%Y}hto3P_Sj;0u;p#mw_bKs0c-B)s>Ag5@$Qp-UU7@o1MYsF7)eskM;IcWt)l-)H zqbzoq4f#5G;(uXk=*y8P5;=|g9;ZFWCv3rQ4`Z`o{@>6~>o1w~zdau3k)v)Ow8yrK zXfAl{H~Vlqt>L7tNATn$UKjL-h4%4Pdv`a#)*9Ep*s|O*-HegrWe%qMO+wC~x87wP z#aFdOS$FUPglh3S)hZo#EEI`9LtyT4ixgPfBkSRF=ZGVHa|ENS-DW04X_XUT;+t%^=%?vg#$w=O-797v- zsmpu9S@4+n^C_24icO1&QOh|~@^dlf(BhXbqikJI{+RkvwNYoPVH<3> zneCm8pa0{*YRLcG$+q@*OnVIY1su3B6noB}{EJeA6ev`H7#+9he?P$s!G9JKOpQAKal<}>caYx2I^ya!5ck6>y z!~HrLWixKcsAX2*o4l?zr&f4P3o3Qco7>>V6CiWQ2EXY)j|OzuOI4q4(s;-y(?xTjdcFBlZ)GqMZ`yzXV`&Sk9CRft!YlPK?q86s9| z?bvBAf6>EH)!W)y?xKY(Mk`U?SVz`NaJavtZUl5*=&=co$H|VzHyxe(Wr!Ech0LJI z>{qMwJMY3)S(4u!!+FJvar}f~p07%{@ME}es0!|8{S4NA<8q07@LK1?a!$qd81W&h z(WR^$W)~|Zift&ut>pZw$M@Js)Z0rAsD<25FC99DI~PBbBN`Uf>MwM;R#&@{yjNFc zl+MZw7pF(x>9Qm*R(Y{@4PBx`ZBpoo^^!V>Ax44V$Xcad@jvSG1#CFKP8~;~Z>UMS z9`;`0PnkMsHOad`vZE z4otX0&hz?rgt;0nSW96Xjj4M#;vwCjZUw%X2eFjEQC`7PM$iw3sU6vX{Vs;J8)?!9 zEhjMFbiINP&>b)5%w@j1qEyKnRU;L~jhbOc_u#@Ech32CWhfM8N_w#x02fgZ1OD&ig@V*M~RbB7c zfifMsYc+u2efXhgxz{SHRl3M;{b5uCH3EjeDev4CLmq$~Hxes^9@fF{T!*Iql;|P+ z$A94LHHvc_R=kW}yqxx&kg&u4I!iU!WyZp-*zHP)dB$E`!@)d`vJiS!er@kg#Btx} zbsNrgHB?Twg*d3W2&gI^+gxN+QHJ+PzuR5>S9Pq#xUb4vLaq08@n>Ob1$o~S>Wfax z@*j`sFyUF3ViObjh}vKm3#u&uq^ zZ{tXF?o5NZpC6%;?ax>$azo$1mC!&(%Li%Ax!u1X557-dtW5re(ARCbD)-fJcOv&g z3;k+pLEx71zLiwUm*alECQePnch@_Q7VCbsnB#exX!cWVcbvR$dsPVUJJ-T&!JRlE zJ30ej5Cw}|4boWa z>-zJ(M4@ZTBRWvEWJ@UAA*u(iCu8=wdkNjT? z54%ziABt*x%gOd$pLvDvH3Q$hjOWB<9^=0{Zw$_G`wG?*pP3G4)2WaJd3CNq+kd%r zPV#E~NuxY!ygp5tKFHUzS~tUWkoOzCI_ALOccAZhT&r`WfhX2ZIn>);f>lRh@gCTG z5|RbBc?PP@fPe4PmEW;Wr5z82xNkt?Ca|<8e^p!f*;H<~HurO_jKqxe^oki3GH%T% zlo1b=i|G=5t63aMWL%eVyI#@tp!K~Ox0yeqy);TZ`2fFR&asX98D zbtWrIY?_AamXWvm?&phn?9;&h*wsflz}u0q_07NU@C@>l+I6 zMoL++xU;Gk?}|G|iw?bgVsXEFcfv(gC*?$qpZkZf|gFtNvM; zT+`{g7M_f1Rt`byk{DAROGEfPf*ESKFt7Hc0HQ+0D0X|M?93OJi_IN z(Y=>3=y8f`-{AqzwK4g1%XT*x_Lr@4F#;Rq?|EP`5Z~8lr?6>aZ&srkloTm=pb)Kwlmc|@fPsr8W!6jWvM~-+I z;hfeiXI4CAdJDhw_m-V7dNLOLD($Jax}^3TS1i>~nfw{mlt|*!(U#;q4wBf7a{#vsiq;#4^JO z)Q{>m9nQ{z!C_n)f%z^}ku)r-PYU_5*QwMkVQ>$`yQ3AdlA>!>xq>-V`D6QOLUw{$7ynJ&)g>b-BX*Kji`U zQ=B(L)>X2-pHQKHz%o;BH`-;(i zG{*HJpYC&9zoo2Yp-eTnB!r|zXJ!m|663TaMKl=s1%&`J4HL4}R)xa5>BqykCBHJdOE#nAuYfcLY>z!b?@ld6_9Qq%TXKl>T}8?DP-QSG!)4K0keiZU3b2P5&`{ zMf$txbJKrK-=BUueOLO%^eySXr{{*|W#M$~jOuvxgOGZlPq0uN5bA>$`TswP5H`Tb zi#}Jt$AcMpp!02(@Llsb7!e*nCDx(xM4)gzJ+8?>}?nt0M-$sv4;Z?g% z)N&WR4jK15qfBr!oOXfOXD~&10Co8@zr>fwlg&oMvC^(3_ci#Qd(hR+LB4*Y@#XR0muj~A<;ir@!T9W*pM zVRQVqCyl8m)oCzvoke?|?D{)342!rjR+^7tMfMy$D}Pa!bVP5Cb&aqk&!T`X(Og;07ryFLfIi*UKXL&`+y9wU-3Blpvu0y`cDA>M(FX?j)WO85^l2PaZ(2-M5c9xeqp)kp|5H0m_f9tQS>#ns_X z8|d^D_F5c@7N-hVa<2py`z`djj63ggeV7hA9uMCfvFzNm+I`g9G##iO!9s7wZTmw1 z@%ZW@-6)SITun$zSetMtAuHjpg#8KU6AmXFOE{HqF5yZ-f!Mi(%?YOymPefT6@IU0 zL#edT z!PlR~nRQsz`_JkD-hkhY)dDxw87sH`&pSk+hs3MrRTzXhfnU;nW0=claN(iWUe2Y? z?ma_$3R$50WP?Z9HWe06hs7P8XFX+y-}P8m@p2X1xTN#(2KnOK__Tg^MsJfTKI)8K zsUy#LOt=G`>``Mx>+H(-Zx-LxX`@G~sQr==W;C4r*l06_`qtk#R0HR2WgKcO&s&RA zvR2gUE5T>UEa2{8%5U zR2q*J_g0@&9*3z$W4zr6xkdhO2_^YZG~e&1#^MgLyZs{7@&zc`P>%5dBXdJaW^LP> z7<(V#*S!O;dlpLE3ppA<*XCYV&vT!Yr3|&eJ?PPM)WCG6dWL$KcU=y}x4PRp(e_VZ zMl0{v*yCNix{-0bF$Y&24EiyS=@M|}dfUP*O2@FlKXBbJdtVlfI@Hr1lPTVT^DV~s zR>8S2m(qLG-Pd8?bb4=4iCd~DYK+@HhQ-#TJQvR>myw)N7zZvuZ_br*106aBh4aDV z`!g=4XQn4-!N7jI@o`Keq23L zitq4w%g}>s!PAE*@P%;PELB&J!l_#*#06mHb{vK8j zbbl#sf0TPG^xa%|(S7BDN6Q3%MP**28(Ywwk8nB{)+e{tUXu3}iv1bpy!!@dqzBNDa1fHzV;qDRa zb}cNPBXYff@&1V~Z?!F8aY}Z6-R5e*;@Z%-jaq~evc99_hCbDkZW$E*9Of1gE8on| zLG-0%wZw8qI-`g2U4~B3XZWfP%S7$KVe{~6R--9bftmNK=zW=k>}g&sx}M`y74p4yc$&tE>{aU_(#-8d0B( z#;*SdQ!9AR6L6&)MpTLCxrO|0H;=qZ>uiN7HKIEY#jHZ*OW;y3;#G^deZDs0FTkcI z!=p7u{-rqTIvBKprusX!x)N>$J^DPIH$VI+0!OPr#RjmgKX=n4ovIEb{Fv}v!Y>Ki z+&T^&3&&DnVM44*tbD9y?5WtBu~%cAV{Kx0#u~bA7wZ+P9IF_s6sr+y9(yU)+txg> z(y=>i%}%&JmSH`da3LWT!tT%wdL4xQf=6q0I=*VGmrSJ7+`Yjd1fMl?!_@Jz*-9VS(TLDaS&Xfuky{ z&LvZEm~Il(>XqhE`oKC#USlBFeSb%2XLwwX@^U%u1~FiDcwE_$+m;`?184NBj@`DA zOM0Lqb`BT9M>5Pat)sXSX7hS2<<#FwpP4P|9D07N^ymKYsKY~{n8le717XszW!S%4*sGFCOndLC0~Nvp0G%^jSTSx)w>8b@hy9_X^J zpT;R_aI-w(8Lt`J#v09DG^WkOGUn)kKM3#MoV6FLSm@R#M!7GHe3Q9Q`@sLFjFWd5 zH7{nycxAS$ESY8f9Ey*UEqw?2!yLF}}36TY?tm)_>lFT5sH9dCDg0Zz3J4h-g`4s5!q zeoyVNvoM>|6)vU}*qjX$|KxW1jo)d%eDFFLyhxt)3kbZ$WADk^E{B+VG3jlt&smRn z)*h%CgM^tdd=DhNLhp`qFP&rg9Hsk`>*LnT&@XhP4t+GX`7g_G>Pa~DOgY_cUb~e$ z`g>~d4%&H|SC_F#Zox=w(sx}lsie=Pt5#>r#yjv+cK97!rxfE& zdOI)l{P$t(b1K!IQiXJ{9L9rm=X`tu*YV01qfVF1Jg-(QiBq7M&t2N*Z{qpwMR^NV z1;@*YTrbO84c5L%H;$>`4Q}Qd{8Dvs)uy7-E-Lok6r&E;zh<-??NDdITU=9PRS*n; zpbgd5xAmOIXi3k=+mzs7EYCZUPbF|(Rltw(Hx{5OH<9<9#{)S-KgzZu-F~VPHes=U zh=G5?WRJSH!TFbnzZN#z=$$-QO-0!uZhlw{T~sExqO5OyS*Ut0pLWi_qdH=V%82%Q zNB4^Q#(%3iX_dUyY8vxlaeQ`;95TN7;^K@P{piS3X?7bZb$`(4vSD&*6_l0b&O%PD z4g7u5NY(;lsGPH4&cvK2vE5=uq$s6=VsogQ;sH-j1`I1jn#=g6srzf`^SEYEsAZ5 zEr?Byjfu^ReHZ&ZwkLKnwj*{tmXNqDw%%jQVryf+dEWWh(%6Am$;2zME3q7j*|9%t zUl98yHXecxcG*7mcO~|k< zPK>bC|5(hiJvs7Q&-JZGVZ7~d-FA?7r2i43k|%tEXDQKd+x~|CG#u_u6hVxJsYApp zPrDUdXs@e;>c^Az4y8QIQv8UxrYwG(5nqMVJj}hA5OL+dY2bf~0j^8fgAsp)q3?~V zheK7;02%%M7;RQCxq8TqUwf^YV$#cICi&aebyVx+c=1Z_H-$gCEssQNUaf|3_XbXigEBrF zonxzHi9;8xvg&|umhs&SPv50l%~j?5l(W8_D7z8GE4QpvE}ZrxMeGz-TU=(UcGRP% zr%@x!(J)aoKG3V?(zbq4l{m#nGk``thDP?D`rvzwDfiQ;YZ{&Y71v+1Y=yTUK-=ZI zP_~cqyX7#464pZc+UL$%r@rGNUukZYz!#098JTe&P4@UhI(amNzx8>g+n8(kaSC>r ze`X{n*95v=U-(@^)n$EkOeOJ;d-_fCJaVo&1^q zUHeBG|BPk$%9YkIr%!RbHPx||mM zW%_2?b7p!We7db%bMQnTkf**XBDhtKb);`LfF97z^&GK8=!r4fw|rG}(u!NTp-0+a z#FeSa*JO^*Q?1vF879dz50-XYp8ae_8K3D#pC~uCSk;W8840%Z(AAa8u>OaZn^^wS6 z8ixIMRJ(SRlOQp(9IU>dVVO=pQ1=BY(qCQ6y> z$avqjU-%KSMG=QZWjnYIGOv}^i>r&@u)BC*DyQ{ATJ|blhud|9i}6!GF4pWVvK%Q# zGhPK#A9&x@@!n26*~a^_P^`Q-J=6uF0+-sV00L-LiiXjRsl@;Hub=rXv*WMZJ+8odCUlP z-Z)i+uQ_MVkRQvR^SJvNGG`}6``=oAlkYvwyE0F%?Hg4aKVsM4d;A>Sz7%&8e7*_e zc${TL)5mn;WtF%Qc4V;_Ecv;1l{n2J$;_DZZ~iXGm;htV!%y z&#WJ-iU&U&yDxTYEPred-j@{n1yb+F5Wj?jQ?SCtP;hfX!C21NA^iD%;#MXU%%qG znhGz6P={N?#@5jBHXJ?;n=MOAKW*7BBl|fmU=3Y3_{!$WLa(wHzLg6OGex~EA_;w~ zCQyI}+G}-b!H@DCR}s0K#r;B`<(6^=_fS4!nBCXrD0q@LzNAj#KkKjd7RB^-Tr5U` zS?_4-rH6UbNVTa*!70rdbXk`2YF3ilxw4PTR2ESuQy$(l5{EW-3=YNbzR_9reUa%x zvu$3N;{=WudYl)*>PlMj!>nR3t1M69ed5NdRM~oT*^=CbNj$`7Fv7Hm``zxjg*hK; z%dXbM2yamX(AeBzFVlr4%A`yYe||#uUFcZ<%&~q*K4pz+p>ryCuSQOWBUI#N;?R>E z4y*Vee$c;lyqdn&D(mkt!*&^Q?jrwhF>X2o+Km$vzo0tdRX*f8qU+{vJ?wIrn0c~q z^oi%j**odk@b|5G~lFf*%!I;N7oTNR(A6!v_VZ+ym_;M>IG7qYWCKT`QH3;X7G=E!3YCfZ|n z@O!n>2c)ZWd=^*7!5r85{er{tqBFg%EOvXbb{X3W(8sF89px`O?R!t9h5ye|OEmw0 z?;U18-OUyDj_>w?-@1P^i%cm_>_TFO)T})GEXksUQ&|<*MxLoD(?n`f9dg9Whi@#j#da={Rkr&%u>~^vH#hMq}Tzv52Ll=8poObc2i~n4_ z{!+V3{VpxPwD;0omj_;6ae3F}N3Xnmdw^tsduM6 zkTx{!jkF1A-=%F#Tc5T&?WeRAXz+)89-Vn*KuibLrL7E2URXPfE{` z{!QA~Y44=Hoi;FSbXt3#p;KC;v^r@wrQM+UbZTmR+JV$<{?p#nQ>l5BG8PiTbx_1on!@=6}0H>idf=b0bEvs4lJogAL8slV7kaocPLx5kj@Bl*8D2f?-Yhh*lO zamOy;*ZGomnU?Ti?15Mz-0fy~QI|p*4+&1;LFozk+}fD%4<>Y&zIvWInlp9@bIJ=_ zI^%l7Akl)@YFKnEmN&6L;ysCN6I&;COI(t8DlsGRNMgaHrb(|PeUr2&DI@8o*GhgMIW1{t(x{}=#J3WQCC>3lTE_Cm#^S0YF`?q| zTd?atseG56!_ADzf8heXMg1R%Rw`zlrjFDTKi@~!$RS2r!e^O7%v@FU)Ik^d-I@6) z8BOH`)@9u+8W}^|t7Ck=kRvaz_f!?1bzDw%X9@rT5LiB%IzC%zXO;rmsHo%4IuR&A69AtzGjyWn@j zX$vRRq<*SbVw3`sa*L*kCyjj2fxIp=1^L`aQ_{+c3#E!JY(}Y z{(!%-Z;@r}Wh9>feU{2m9HDm9HsZgko_i?{R~BFFr#5>W*YR(%?6>hsy)AY*u1dJN zeDEKX_XlFF#UjgN*J5Q7?@w%#I3w}T#K)8RCXGnikaRJrvhO`S`Ge#ilaC}d{ zBjv-C6DbL~QgU5LxhdBzxoYQXm#a~(4!LUPYM84*uBUR<&($VZy<87_tmlu|?v8_E;93;aibVKS%Qu zv1*p$qM8R|kHj()YPfY*tcTn6#4nvK6Fhfk>{Ki>b}?2Wu|i_Q#K#jmCXP;=n|L~L zbKNv|diPa2lAENNBJprl4gm6IM# z>XuYDse)g=TvFboGD-jWzvYukC;gH5YvNCd`x4J4CMO+FJfCRJMjU}jwR(vDxCC8(rroE_Qwg+>=jBnmbfbMbIY8>H|?Lsi4P|hP23Wj z>#SWQew<`~+#1_YQJG3D`I2v9rVP^YxLJH-zf#3A#0fWNzn@hw>tk$lI1ZBY+KP+033e_g#fwP+%huY+)P z+T|MjusJW*E!Q?sb{opPo=acjTa-)R33>XabxBJ~n~_@6r#^gj($&gW3t#>I%GXy` zUis)s-zzCszQ4Tb^32O4FL%5A#O2zT{~t+b0p8U8_3@TAu60KXZL#70$6baGci3rmEsX;6vP7$xNrcM#piDyNZXps6!bEWmtJ;@-KlxxW?*apj!|^f1Q|ic_)X_@RkngB| zZ*$$tX6}L0&!5$`DokuS*406zTyzBQoy0Zv2-?ki=rEmh90J(afApGR1$?NQ?vo1e zo@Bfw)XJ^!3azG=JV+m*$XyENz7}jGoBi~I%rq}`jLf;ACFL<{9US@%-kg;i>BB;hE)`=2`C9<@x3LP3R?D6|90s=p;TC_0nF+BXyV0 z%Qe00yjoug-&o&$Uqw9PIhiFmqmE`J!kZr;1x$;6auDwRCwz`sIrf|+KB>jjoDXm+ zA0}V^8Je+;R4}JVzO~VZB_9sGD5IPiV?=WoNhE{BfYk0tY)MjTYt8 z`q7H|Jm(JXE#d##$uloh%vC&7d{#W-{#MqRYplsWs>Baq|3S)3#SS|!le z4oq;{%~XW1U}G}p**E+bM>r3Tfag9U4X7qa*isZG^U+vUBuml*6Q=?<{)%I06C7JD z*1LN!qX)?>>J2vej_YPKFuHqTSf9nVqEJkMUwKU^)AddAQJ4)LtnORH!8^5Y7sB1+P$F>?Jl7e-{Udlf>!bcyX%uMEoiGNsXj2(oE^NbWUkvH>s{g8jje%>%|Jvz~?-gn+upTXDCH;xr`FN)zWzLMyVr-7KBM@cK9 zd@f3!Q%la(GoaGnvMQ6fR0p;(FQKb5>ab>fB1X_lRrY;w$V>3X<9K|ze5Q6gj-uF1 z|IB&xe^q@2ePO<>-jUu!Z;E_}4s(#)QtlvEXTMis_3kBi<61JB@AKq;Wj9w4qqn-Z z8&{nz-UHsZ^p>T49eum$q2Ktd`JG=!e7+Olc(3C3F@aRKhGAXK>%0x#y_Rd)Tl#|s zyq?R{C=XFReTQ{D%zH@YioP8@b|(n*-h5B=Y_RdwpushHj=%V69tyOjXpc{VwLV5a z{yeKGuI$+COpc;naP0kAZ%@JlWO4Ke)I9O<4S5-)t*Bu3b8T&rGXTBbd44*ELv{x~ z@iko44(045M{N!Iq$gBGKe??ZL+S?X{uCbZ5FOJt7>&~?@NeR(9l~#?@I2#CM!A@h zuoUOnWN_NK5jLmn+EMrOn_8(b7Y4i1<+9N z-7CNm8oJ}%fo_}2;RMqZUHPn;t`9qi{qzg&nGV#9X5aXra zrHfKSd7}J54$i;!FYrF&44dWq?289;?}w{2Pw?3KUDd*#FO5pHg|h_?yVZ$GwG-qRo=ejJ(o(9PeZs?0zspI6=9LyI8d zQY%g6{D}QukV>F0{imH|&N6rzrclYqWL}rRw>Sgkn2&0uJxK&BNb75Ye|R^#FFWW# z4OB4KS)B^f^&Leydjbb+b6DBwT$Asi3Kr!NIGqbNc}pOD}AwC8?ZcvGE1rAUR( z%$r}o1n#ms>sdA$3xQ)rmIHM&0kU7QGHN7P%TqURIT9teAPg@q+zPA zswQk*`Q0k+59eQ}s+OwusE*S?jaR)_eN??tolrfa&sL}n^kh!eYgI{5g7S1+Md_@k ztGB7|s!ypO^Yy0s2ir>Z67?0nzf)(b{WV%mutrcTH3c-;YOgw3eL}sD$ENe|7t|}% zbJZ8=)OV?X0KBK@Mm*HVK!5LpX`vupON?g}obKFmZ57J~7$h31M3WN#jjRtfX)%{_7mNwiF zRpG9-qb2Ev<}sC{rYwx&VvvbYaO!PvoGIYTQf>$;;Y*-ECE-@<;L07DKPPh+c+deR zg50Ba2tljtg8@lJ0rj4Q@^I>%ZfKvoqrK{k3c3_rZiW18)ZhZ=NOh}*XQ(cIAp;qb zx4^Tefb3SsN&g+BvJPoDPe`%2&oxDt^9WC3IW#QyxMJpML`!E~Ldja2uED}pw!N={ zFP4=|_MTyFZOQuFlxuOg*D1$(YkM1VWfnQdzOsD+GrTRkf?MGK&|tqh-c5_M}T3ZEzxtl6A(3e-6* zU;!ti3BH9><2&q@Eh~s?M;nlyGw4SxtlSmgvpS-0UWo_e2^~`o=c8Y)FQ**%@nBHe z_IR60qd`f8H%OxP2+#dWYSs&Gd%40cBg3E^$p(A4p4TGT`8nB0P3WDHQ7Vli>$q^{ zTi;teG~2l{Gy}J7Y&VI=H8J+cz=}05sW#7^@{N~#UdMnZ$ z*O4zv)udPAa8k9~xL!LiI!`!nJKs6I&gaf= zPMxcXtGjE7YrgB2E8A7cz1N-XF6!Cfnd5ovi4!^r$LZC3iVsA)7%3H#+DP-kFiOko z<>TPTxpIWJaDGpI&byzrLh@#M&2(FJ!H$bljr8{QXCDsr9p+fH__Uce_V#1nIeb;W zd>?$N?9CW0p58{@3SKkEeT+BQ`$OJ9J=%wQ zrjq{I9xm`UZ$R_B~_Q&NrR-nrE8K)swG#J+sh5)nXGf~!O4@r z;{O7Dc;F59wF7%U;(P6jgtwl|v3!y`SkIN~Pk7|l@Wo}>UxVmvx3Twq*?&;yUZi8M zf_keOh~u-|Nc=zaPa__Ahw1mi%@wkJ?Da z{6^H)ulQR99aUaRK_|9#Toqp<$TowI_hfzBfG*};0R_q_C2r0Zu#6MYKV1i1{Xyqv zfUk^&xg1BQw;C<)DR6Ru6)q6HP!@esAZWP|m>Z}^J*zB)*@dPa4O{_ydI0Pia>Ep=_(1tURfFt^AJ_@ukwDe4?ztns*DVJOz}{ zp(+A)SW{hE-AO$fymOGc9w=gM^+5FyP{ky*No`emRf()hKUf1FvF`0+1w93#xI;Av z+`Sff;X~ySVJIeL{1K7r37{6Hm>u`$o{oLRJ zT}l2v3)V7_4!;$(V_pjH2hRL6%%{GGR@9LlLmpsxdYtCunLfou9>P^}DCmnP*T_B! z#4%Q#d?lOT?__je11<5<#a073ze}xQL7h|;Hhert_BOP`dG4wPXr$_+gdT@RDlZZ9 z1)M`pU}Ii)V?`XjVO@or8Y2Te*Eagy5zIgAOowPeZM+%We;GAN7c_0>Q4sZkDQ=cq5iG3}?A<~TlEtVRPoe(51sAyw4U>~| zpc(9JPng{Aeig{bs86ro28`$;U3~~hYZo-oNAaU2Gg+%O{rqk`&;~Tt)lji*pyU2T z{6#3o(Q114PblKi?`hbw0)M=N_Miz!2urIjXd_avpHFfe58gTkVC zPsja)4toj5VNZH=v%-gS>;;~(Y2X#(KsLexKfnv^Mzh}o1%D(OvOEWXjhTuzJk3w= z3LGGR^&;7t2XMjf!*Q33@9rD^cN^13j5z0tz_%2Gm+y+oVG8Vhc0eQ?zlP4eJBq<( z?9V)<{8d!?H$YF!X!#m*ZX_ro(HWEipKnX;Gzn!vDb5!MXHQ`Q9e@~2N9o(PO(2KiWu0fY|d~Y@HLvii9CBL zba`>;@pj=uGcyruE-AJjK?Pg;mtX?bD_HQ_RC+C`8jhgdZj8S6469lt(6Hxdc7G!Y z`Udq$TxN<-#QAd%=j{Y)qg=1iXY-2GHU&X1biNp0j<+f+L~q|V(B4#^$5)O1XdG4I zKRA!_rs(znD|ksAQ=Sa#hOE^m(cM;~ySs^&&7Zog9O&F%usa`egF!l*z+}&cou7!m z?1f(>slDC&$1%b3I*yWUWFfR9W4}As-R1nY6Yi4?l1kITLC>H=n8!7@FNm4H-z%;L zD>?dGQFGSk`D1duAg{~O>Fvo@^1E_^^wu@gn<`LgI`VHxSWNGWF-m{HMs)4 zI8LqwhQeC*W-rfE903b+D_jbPqJ*-aazC8iY2_KEUFlMqxOSFNRfCmjrTPng=1)J8a@&gx}!EGN{mTB|9l z>8ANpGh4HP+Z@evjYb=&Eua-NjkS%nleHVQ$FQZ!GU826MK0;qnpQw-1i@Hd?T3g*Z^fUC` z_3iaj^)2;1^)vOI^~?3=^#}E5_2=|=^$xwCA(DqyG_*7fGxRr1G}JN7GVC$jG(0k- z8N7xf#)`(K#u3I%##hGY##_cW#=FL+#$==36lIDtWf-%JVWw&(v&m`HnyQ-=+-jLx zm`d=kCAjz6_|{nh0&MHbxt( zt*afT9jcw6U9DZNJ)#|@ovB@_eW(?*vR0w{O{dj`>r6U9>(y#>Pqa6+U$qCd8?=|S z%e3pX4YZ}SMYYBFnydMyNrTngtJ%#y{TueXvZkCysR@GZcEfJ2SC3N9V6AUO2dh#? zstc&!bA%07HC45Q-TSP(sGJH%xkkB6Sx@Pww1A;Lp{}?Gj=GqmyOE+9)kr0-_vwKO zg^L<#X|I z{lWU%m?Sk9HPRGntx;so7y}NY4hbyq3T!E!dhbuN@G_}dn&-Yk{Tz|A1Lse4_8pK* zUDh&^nUZ~{S)CrE&*@3Oo++P{+sF#}6)ff^X)H>imeL>6|D@U=hHa!o@aeJAP-%tq z4~X%55aU?+e{vIfI7sYX`nTJ1sw}|-Hv(;3>fJ$~anUPyOVdqNq95)@SHBR7aKyol7saL~k>1 z9nghNpa@mHg}mS7qw*>cyeoWPDbJ$U`BUx*irq;LhOsuwPP(k8(n;yFbWrLpwU(Ml z#U-2gQM@fKg}a?WXBYz3ei>f(tguX&k6NmY@IT>`=cVVM=bh)7=My;lJdpMco)LUs z&Aop-Cs9^i^X&IrfVZ=HPJ^Mo_5=xKLF@+$YlVx#7ePmFR#oiF|8ZUvMGbs&Evd6K zNm?r%rZanv{whR{=atTYAD%0pWxFk3lh1&XTtz!|9=zlrYO3pes*m|pGi05>v^h zy|*{Fo!+C~YhGVIBH7b7+V>Bi<`Y!Zj~S<$=S40-3YVG`kE-n7Ch#X+>9iC1m!~9t zT%=#!fD*f&ucR*ujyD|+`muMpcbvDgH;yh<=}nfO!`ZFo|DS`(>pa+v$UY4R%P9t{ zH_f}=dz?=B4RuX0yzorldj99PD4olLzWznl<__Fix6p9k03q3p-fBPU=qDtIoCEb3 z3#-?g+ZG(vf3h{FW@?sMH{Vy?hwPCgCP#IoB3h06`VG2je>~7V!RF84CH|0AD1Ua+ zMz9ABs{3x}?-!FKa~O{KXHK4zs3LWHbrb}{QNvsXJsSa^ISy^-VffF@c=BH08oG!o zW-Hj*9%}e`JmOz|I?U}9y}`EJ)I1Xqy@D3qUw4?~ekcVOv8|+9I_@{0TB|zw?GMpb zCc~UAp+g7@r;YTri%JXts}#{1t^V z`+H6d2>^Mo-kVzu&Eg_zx;L<%6{z_BrpMky6}THT@DX~Zm-J^>>64=9jdcYwaBsW@ zB}_*neVs?{MFX@Aec3hcpX8@|OoqG8CZU{mf+dP{GUY)$SJF|Z(|LWzN30>G;Wwt$ z<)D&kiSGIT{lc{ay3;3^aD!f<0}Kdw2`_pSwe?2&jAgK!Rly;vFn!KN_U|kHpSP&C zlX)HQ!3sS7Msh;R;S&7=m(&O}ba|eq1boOt>HM~%AkGOW$u;mXy?GJ*zcZ=xqtT*m zSEf>JwN(AB+Nk=b%2ma&hKyr%|ELx~f2(LpYFe|#FVf7=Y(tUc(u8Qu+Va{C+K$>W ztnFL0n_0u}Xw$S&x~jT1y3xATx|O$j&Cu3R-%!^OXDDVc7)1RGeTx2({-yqc{+j->ey@IzeyF}N z^;So|*4HQTwVb{>^;&g(Ykf2Q04lN``U(02`YZZZ`p^8W!cg2$i0ABL=x&%{7;0ET z6?TUz?7rctA=FsV*ugl@*wHxJIKjBac-nZ?XfcY^VU5r^B?A6=Jwo{npc@GnU|YKn){o_ znrHBDoy^mDOrkl`9ANf0e>QzI-7sx49X6de?dG{wn+BMYOm1G;A>&A6X`{oi-q6YL zf_l)%AgE;BG+nm#rS>=NH_a?ftme6TDs{nK)KH&Uv)d_ufU|@sE>i_|!Y6$iRG>(} zce1%w;HIk%%3TugQXVF7ievvh9=`J=Xr=m>Ak$+2{;0)hFwf^_zTKnOErEBgHCneF zWNAMmpDd0vnCA4f2XLM5prd_GKYf_BIy101{CTWGi%Qapvuqn(?;xDiL-B;=%?v3( z8rc*4?aP_RS)7C#8AtRC^m{XLWBm_RKnAbi4!zk-G$%o1%|0fp`VtD2S)g4l@h;Xv zrTq|8;a?Dz30$?h(FxT6p-!ep+7Hh>4-{I2Uw%$c)del1k?YK1{36ZCoq7*;H;^m* zS#aj@tgrDfwJBcNdj(a+LU8t>=x?i_JgVgV!`t6G*849y+0Nb-{L5Y#(~Ydsue=s- zj#tHXsu>vOFf_THSf&4Eo!;SFfjViiZxL(r0&=9z`p)}~;o>>!JL#K9mph98uNUgw z**JQdv#PYBzirL;y8KibCbk{bMWU~~&&=0cZz%Ws`C9mzpfnoB-!A2^{^HgfPtbf6 z!OhSfMf!fQx*X(HEy!Q-JEHeJ34`BVHpq7Al+=Uu+bg~k&#}6e6n~)GdLe8_vDHq9 z6bwSP=ZojFC(9Em6oLt_Ba{{@pxT-%oD!Z2p9N7!5UYq?#ec=8;&l`PA4FNq6~m+i zDx5#0no?mYSPGX4Np|s*ctl(;9spOZCJOxRDB*9Rrce%E{e)+aXObt$6X^Noe&oLC zUhMwc-GXg|d$fCxd!oCgyQ#Y`i1ktTd3P~Sv`6nL=V|O&M19Z;=CqyAUf3(#hm(IG zJP|$#CB;f&1+gXXWwO`~j=n|yEkXPx2*L?rvM^QXOFg0$j(Rr2^jGrudD7k2U}U?w zJHpR)c6WF8LG>N%PIJ9?U3A%92VDbQ9b872+nMeB;C$wsTeR&=#>kAAjBD05))Lk&mc|xO`u6nB>1EO{q^(aYo%SpBX6l^O z;cQ{4nJN2I4ySBMxtMY~<#@`ilmjWxQlymN)TGqPsr6F(rannMn))TRMcUT18)>ez z;PejZjBt)p$a ztqI>7*?#A1E1S+{w%yE_l`$q`G`F4^YQ_i8u@11dwN|o*S$|ocSgu$OShiReTNYT> zTjpCvvHfM~Vj07>+_KQJo!bh_I?D%3xHZyR&|1#=hqaEihjo;79j|1VbvXAsTU%M% z@r>22)vV=t);*SEmZg>fmYNnnOLqFB^yTTJ)9a^e)2(Tb(l(^cP5V2oRa*PBQfXz< z;?ok+YNZ*{g3=<=>ZXlJJDRpG?P=P-n=Q!oq;@IxE<@oH-I-{Kbb5?gwgbz`;3b~rP*11-=uDRa1 zA~`nZxtF-txL>%v?l4aRM^9Cb+9XdEPiapp&;LBNJPJ>W+v+|AzcL!$zli&_>#}RL zYpQFKtDCEctFSA|RofNrN^-@!{JGcERn67Uwa|6ImElU_=v(4G=$74jQ~^_{l2bkP zg^4f|k5EP`#qwff@t$}~{6g&;CAF6tN;9Mf(kn?V2g((33sjb?!uI?o7m}57iu6YM zg7)OKv>heNX0(~B=`RM-WlWVC^Sz@qKw8M}wn}rP3(`BuLf>JQ%gL?r8oZRtqU?O( z)%hB61)A$~<@55-aQhcRqwxnigO6E8bZc$EtQO{#Dd?`PZytGFUn@&q2Z6hec_1_eIlkrJde;s zHi%pt`8v`R)i!E&)V-*TsL1GQ(H)|@MIVn&i7p${KW1#q%9sZ+=Gexu(_`Pq{)ml@ z>mD~FZgbp)I4Q10!NvuL7d%+-O~I^!zJl?EIu+_sXmp`-g*=6F3RNv!qj1y0wF*xt zyoK#{;dh0f7xopl6fRt(Op)G2h7~zf&^hnWm}AmoRD}v@l@iS#4m|r zV!Nc@lUgTrNNStZH)(m&(WIwIhmvk3D%!QE zqsZVQMT=}L+@P?l(7r-}g$@+#RM4;Bow)vSsj*9AOT}7aw#1Z-@sIg0dUf=u=+NlY zsQposqZ&trMp+|sA`eGyiX0VLJF;?QaHKsVHR4&s{fI9S$r0%hnGq)5%}gSQ0- z2cHXS6ciKm)cmixvH6s#yQ!?{Ut>FCiea9ihT)}ttUg+QL^lDXbGNpn_Bp8F7)=q4 zSAAJMNnKri0YIp32zw|VcVg-(np&)k_D$vnn=KsP=xf<^4D^CAW`WKHdcJN+MhYbFUcAGld?M2^I7hTws8XCDm02Qr=0nL;R_Zupg-;+ld( z(#x!p+ouatJ9{jX!l9Pi2obX+Vq!fII`3fbsAdr76O2?Q;MnybbU2Sru2B39Xi%Zsy z%IG)H`w3Li$3bvC@U2Z@RmZ`K?IcmcUm1r>rV*H1U*!yNzcXAy4+_!E`u5Jgv1P6?!3G*+(ZOX2IflM7jc=>>%Y^CrZ9f!mx$$J-1Q za2Tv)G2Uk!IUFhEN89LMKclUhNKdkftM3NBA0g#v9Jxh#KC!$xu;ch%holBQ*@Xq^ zvWt^q=s;ozNbGP8&8 zGMbvba+o|vx+lhpCxs;84O+be?ojt|m(n%Ysd27$lyuy-_qTg&8*D+gi5Y)pG|o_E zyt8hj{;6t>wYn`Smfh4Vi!GBa)2N{SqJkP_*=V_LIcV8z*<(3FZFI*HU^QC{Sc9yh zCEN0rzsRRYodT7IjrHldO#MYR>jb7oiySXI<&do9~6H!Xt2M=kanzY#2- zEQc&dEEg?1EN3l?EL-^>XIYw9KBYfR|B!w;{X+Wb^i}CS(v|6%R8$wz#-)`@Q>A66 z{z$!&dNOrzY86fxB#gd{<=TVZ=!`&j!OdkreNOAe=_fpe$x zfb)?v)YaOxAGKv9F3x@^R}`LDPi@e~mmY%LM5?23g@9{K30kc zaTUxESD=-d&;5a*n4{^2T8s5WBVL1O5KW(uA^3@I90e($kzU~!_fv#eF+uFaUksxb z86>XadA^D0N(&9jh7pt|J_b?(;c_vP!OrFUaL2resG)b5NelC+#IC-*C7?S z38@N|GaHkLm^Tq%Ec4!%k<+veMfFtX_RJ$u;X3HmV(uRW``ZVu*9+axR8kN(u>Hr> z*klrf_2{^w&;-px**S+i$FaTgU|vB~X{Sj8e1!h`5OdJ|NfxL^Lgi>& zK&w&3o+R<|5PI3?s2Sdn_mPTL;R%Y_V`yVXz}8N{9Z?$2QJ3?8RHR#|S=NFhMrS`j ziKNMzMUMyC$ma`JQ_ohbKnp53c-@j?W@g zLwWs5%oeLjQ>bTOq0xzx|G__%BNqgJ?S#_WfJCfVn_;!Cwt0;zBVsy>o6G*lTcdMrmFqOo|uX& zss?)c;+d)FqeuG6`m%8PF7ysS169kLOFex9JbJiXQ!XPL|^MF&&U=z8O+)x*FkqX1s}1^`vXmIo+c^? zh1}mbonGO~{ej;poJ5S3Bvv{z+mf+y2IWykRwc5h=As=72jv~f-j#ETf#lA>*LWA+ zyL-N?C)qC!1<`sOv?{!}Q}Dy?1)VHTzWY2>3a?3@_d}&nnu&2m&=vF|!>0@PN|PQR zj&>oAygLtR1Xi>u8(CRvu_l!c@CVO~Cn@ne4vg2_Zcrhg2E9x{4{;H!=o83i5bIYf zro4;=Gup=gBIKvqD_Ik2@T_%UB!{BBSjlQ}oV8oPU2#1y8&foo0(^k>8f;FC?ZH~vX4~*(=c*zag5!!LuRoeStotLy< zv~RSpwNJExy861-x*obwx_@+wbQ^W&b$4{Pbdt`b&(WFnCG_R>|LC{r_vv@*=d&%; z@6lh^PlPpYNF7s2Z`LP(VQTdS^lxCKBXm2#>V9d~!FCRV;p~Wicay5M>Mifd!+UK@ ze#CRK2#W{4B@v@#z;))-v_+Mg4f?x*iAuxpR29b~lK^%-%YPNQ9m%9^)y_}b`khrY z0EBr4QxA&p9Is(Fi;z}v6kfj&o=}Ujw5m5Q)b+52R#kQN8nnG1)jwg@RVaTuXvX6^ zz6I)=tBKc^&^G1sX`-#JEvFr%ovJ;gb!ZFe>gf`6b#%>j!*zXiGjwZp`*lZjmvyIL z*Kg~V@#*x{HPUs{4dnC6(x!q+?&s5N0aN@7&*~1Gu1z(1%}RAuwL>)<9YD773-!rf ze5$d^!-{q=v0gCi{U~yppdWt15tq&}mp3hO5XaGACOCB>OS1*F&=Bs;rXo5{P4tnJ zOo7CZ1diOvXrcDP0fwQd8Ku~aN@y$DT2B>bWeiwoOXWP}Qdr8HxUpZub5~P!gTK9v zUiy{lAxDrGt#oa5OZ9T~QS||o&L7p^)H=A=PMQUDT&p!sO)a{uP12XrS70^l5T{q ztj-IXz6ft`d97QMr8%XUq^Y1$XkM!iWX)~#`S2y_o2KvrAp@Y zrs4Vipvq8bsfUwrO%G7_#ZkOZeH(@LF*x)6>dk5^to>W{L)_JS=^6K;^1jFIB}(t( ze7(Z^OIAO}f4*5goAahVN8(@jbjztj)C$g|viP%u)FOUu9h`QE`US7TiFf%Enz}P8 zJNGj9w@;|gm%zq9RUJTgevET*I>*;E)jz7v@X-HLl~z?nH_{!(p@OO;{cIPUxi0*> zTJF_?tNu(a8m-EqHr)$TeH*U&6JF_!XaPGZ>nKYqWyKfj+BP_|?C8IITp=cMW*h@M z-h|4nO2BPck|QK(>B-O9MP77uzr*0bdzfIP&$)s+coT_)pSWV~W7U%2{tEkUa+NK` z75p{pc7Hiiek`4azrBhEw2jnTT7bT^gH#{fG(z%|bnuYFrSYgvk4YP)%hD4`E0>iW zQkv98Ho-<7;yK>HPaU8FxGRT)8}~*tS{iP+7XGOT-j#5LH$Z3Sc}IaI&xZ|M<(-U2 zVhlL)PVZe3B({0~rf%rTt);gnuPWFpz<6ziO)L)=dl3e7K8Q{Y={1;0OHmRI3loH< zf(V~l%H!{O;ogl4CC9bi)y#F%dEYtPIlX0 z*6y|0ZP#oEZC7pQZEJ0dY+Gz&Z2dtKP2h+PY|U&-Z3}E8ZPRRj+a}sZ*;d#V+vZb| zt+nm2J+$4leY3f35BSM$`(!gy8^5*vvZ?JRdrf;s`|tKX_P^~z?91&bc85I%?9>QC zH`Fm3WbTo}?TB|8oeiBGoGYEDowuB?oM8-xZ{*tLI^(jq3cAaZ&e4UMU?%9=Puw$h zcc`bLCm83a+Ed6A;z@I7xu3b8x?j2HgT4)Qw+6>GxGTG(-Ra-5Mo;W@@1jixA5yz7Jdk-Bq9BGcP zjuc*}k7_gAsdxU)U;N?h$6MYEV?|M$7>aqcL$k-vJ&r?$d1!?o4b z-&Mtx>Kw_Qxd!50z~QrhwO_K=w%@dkx0SIeY-=+l>v(H%>n+Q0OACuDeOvmZ^oHp! z`iy32-qbg#n^XJHV>C@Ik@_&@W6HIZ%PC(|RH@FC%#^H@BB`BId!%+x?UedAj}%g? zr!7glnr2Mzmi|}zru2>JC({dBrh<0Av=jna?ql6=yDTKi%9ZH}=(N3J~%cpsfCBP^rA*4JB3 zTONXACt15%hgfG==YpXhwLY>+)`A($GKOWW&3KmaH6zXzWs8SXSO>cO$@bJ%-Co*0 z(0;~#5p+A;(ZaFXvD1<12yr%Y{^|UiBTnZk&hc~B_0-kQJ=Q&m^Ztdq5Y_brR2N#I zE}1oU*pXJ^0$iS##b~Z!3&BXAQ+>veN6?wnfyHp>m*iJ?wd{Bm4c^<-(I4g4@adQ2 zMJV29<2mjlN6A_AH6Pe+p#nLJu4Mr_kP2J}Bc&v%pcIF;#gXr1)8%tNHKYP)@dtp) zcHzo7MA`x(y%n{~Pw?3U9EeTjw$!;ZdG4|DP828SaFj)Q>v|_ry?5e@I0xp+p0Ca* z04m*(&S@6h*)C?X-1IrA=<8C2k3r`!3-|Oc=BxPd0mWqf$}B=={ctABl*JX)1;^+j z(hL@ncQ+MZ;yN6QtLcm8(j9H)Uf%Zw+_!^TAD|C-m=%mWr2^C0+mo@dCi`giCp;Kh z`ku;oU*_TF{!Hrm2hg7ycwQExcDRBU-0T-jT5%b;#}?!r)j=s8$ZtyLbCGXRna&|m zU>2U36KEUGWpAfzs)Dz1HraQN@ivvo+yIOBnk&81`yKVoICMj|(F;XOCrMQ337aJd zZ{fH`)BQ9=8#6!{!q0t#f%&~qj!=nCwII%g>Ed^>C~MLd>ALhAz2_P^onB}N-idwQ zpWfOiR{rzl_(Jg3&m_aX1POc-=znIC`hSi2YSqB=H=;iE@P2c$8|L&Ooo*P4*;(Yd zD@a<}hT7u@PWS7%8ggOVkP};hlz=|C3Ko&YwbO4po`Sr4e}Zn+Ln$8Ucbg=YD=583 z;BzQT`oLp4kg0ezLvoIwaBWVmOcMRpMLvh3%#HuZC$XAIcD-=+lt8iZ9q+pAbCY>^ z4ZnV-FDCN~_x_{f5nv~j`BQs3kgzcl|KNOh+ediZ{n-Eiqx0QDKJYL2ibVEeEwmnU z@EjM;DMH3x2huV=)79!pz$it&*OS~B_E{~|?PFk(1Mr0{g7L|Ia@;*BBbzuHG!T$dyd%Yw2jLx{` zI--KBPJY~9XyVqOjJt$a-4Fb&DX83js@2D!cxJqR3H3E)?xs_oC?4-%GL?PLi z)RjK41J~$(GS%VK+08ZMNDo@2xkycaMROKiVSrYRS~gb`PVZ9=-R5el*i+i`+LPKV zbU!DwR&9i?lrBeW)J5x*{8U|6QCC@K(8cHmP}NVTYF)4EgYKgneatxO{l~ftoxm+s zr`G?5E@PkmzCKwWXvo!1`Rsb#fGV+kA%>T2ntbQS0%R%vT#FVV?W)%-vSs#h;n z^;79p$*5JAp)!n8E5G~u{y~BGT~QYO<9TI{GFcg=Do=N`jx?J;sfkyii}|81rs=MErt#O-KSp+ zm>sPer&^t_$DYM|ysLVNzSm069II}GUbU@yJlOhX^=bNqS85ljM4|N0`!!q8c(K~r7D=x01-*lC!8@;U@A?5Fd@bO6SCm2n z^j-A*^;Pth_2Km3CH1xSb?Cubp&)8Ym-dH#B?_Y@`mOpC`v3IbIBLJ?ccNPQtWVR2 z8luq?RYtj#Xee#y#d~VQ*YSq(hAM_y29?3VV^8bv=nv_K>sRp{BYD>Za)aLMhcy_di{G-E#WmGr9-#%KLQBbWeD^Q)kuL;p~d&!$1c# z`dpqt&}s8K#sBCO-}0D?x~+7WdpR3=a7LQYejhk2mz-)2^H{fW<#*aIkd_WKW`MM;R z9L*k&R^}nAXVWadtQVxcx6BOBe1?wYDccp_0di>vp>U~>PqQrRbz@&4oTzzTzJ|VN zZo|OgCZkYU?7NH7G0(N(^qD~8qCwFDx!(5yo$CXJl{YtaIjY4oq`O)&3y|rmClBT} z$@A|}Lf*-r_abB#&a#4g{ztm)adPIj^B0prQsXi`%wIbO0vYT3h%RRgNp=oWQ>x2v zq$_a4Eu<823%L!E;(x+&vK_8@26zg4UcvAi-QQiiU42}oTu#vB?kEAiIQBU9IW{;3 zJBB-IIbzrX99H`W`+d93{=)9&cG-T^e$#%|o<^-miDi#+RB-fgjCcG)4LQzn3}wMh zhvd*W+c|4GJ3D(g*E+XSZ!U7)bOyT2bO3R#ajrqGHLhQ-|3IzlxF=AFf1<-l@YM8l zB%AQLC)ZO91?@`k<{+^%xbzNDL6tQIyz`=zBoCAKp?6H;J+`EmJcHx1gs&R9r9I57 z6In0IkeAyKoU%Xq(8;X&i}`F%F)#ihb8K9!x2a6CdBDo~9*yZW{^bdu=ToNW{3J8U z51cb^s*jQ>xTTn+GXQt+QYxko;EI(&lk;4!k8!F-Gi`7>4wPG@+~`P(oXxz4Qhv?p zx3AzGi-A=;O8SrBUy}(F8(_(Q7O0HRvrH|q-+${HGSW=_O&VPaLeYxEk!O+(Sll{0rXuQA)rRf9$b?GLg9RSBLMd_LGe zq-03LkS-x7L(HLZp9~vK;`oYGu@+s9#aiC!B0Cc0EiyO^Od^J4bJ9Etf76A)V@HZitIY`@rY zvBhHB#deFW6B{2}I94C~Jmyl&%$PPY^29X7wuf)j&P!R>>)2LB!0DY!4sdxuZyPOvjr6QT;K9?~*oM#%V(XCc2r3WYWZT^f2l z)D!wNG&rngSc|ZUVYkBEVG-e7!{>&-4|jzZh(LlFQ3qAS-w}Hvw(<$ygW28{u?4>S zWW?QwbrCZo#=~(}jVKdgibxHA7(PC{Gdy-ym@52L*uJnHVX)aKN zlj-O^`;+Q{T)x$w$;rZ?C_=Vr*d04F;hz2hbCW^YOFWB8PG zu(){XJiJ|J_}MGMJYgKkJpF|7f|7inU!L!tBP9GRWZR3+;Dg8K$@Vl8>I)6f3wB1o zIEEf-p>RNWFPOyQVofngY$A?;zndWbBW}gZaE~Orqu^;Nq8XI0g)|bcL4C=Cx=0pF zNe$3Ym7|kwAq{3V*(a@(X5mDbz@vIgWyrx(Oa3Sx9pZcV#IEEh-4c%Q+FA&19P};G zIX)m6rj0w?{ob{Qj{Svm49M3dkg!n4fA;zIKkQ}fg6+7ilkIuN+>9X^%`@UNGOdrT z=dAtUh3mlu7lj%A0ylgCetErR9*Dvi%N!WsF_y8G8Ek7UD=jlo-p#PAfn&Z6E4+nU zI)4!hmt5K!Vbxm|sPvj!M_5-_Ct1f@_gG(9zr(Ut&-h=)KN*`cZh}7;ZPmd17Jvl$ zY{kJpuG>G^3ph%H0d;encZ4}xI>$OUJF}bxQAjRzCA&i1LGDuI+e~opa7%87yR@f1 z+PJ?wGd$bLXj_3|a~p1te{p*ZVyoh5;3?~=hl;MbN9*yy{7rQib|>(ud~w}&U2+Wt zsS})EolfUD=XCUV{hb4xwVY*~#hhWzSXR=q&SuUUU~o;Gy_~(B!<_S+D|p;V=SQc* zSqRi@p=%Y}H&;1#EB7Y%Nq43@61C?BkBN?X8y)jT;U4L5Nn(F-mbi)3L7Ui`{ni$T z&S3C~NAPk#rT-+2oRptD*bHpsD(J{@lGlF7Ca>PB_ErXoZ4Nd&o{Zd^UX3pZjj)8i ztT1Zdp|HDem<=13*@b++v8=UL=Fz2OMlhFV7@hWotgqy1nCM8ClJfAJoQFtU;~mHZ z*otoCM~)ViNg49_D&|J!K4Sjh1(?^B%qdFDvBI}bBV(cv2_kV>PnlcmpLve8z8=Xu zBR~!&!!VxZeWc>Wi5HIvBhb1Y@^tbfdftJZc5?e%uU)f9cK8#NG05d}K6Soves{{? zi(%l1iLQ38`mQeIKMVj#TIo``+rgu6pc)AzTW>f#`g@N?sE2lUgWyL08!xsI$5Klj z6EnrmAVAHePSSMLhF>Iw+!fU58QWEyIPdw)N}-q@1>SvtOyuvdvJt-D@$`1b@v@7~ zem(vD99$az;$2!pR>>4xOjW5MUy(L62A5J%KJC+FO#C6o$RcQbZ7CO~zM&W(?j+SK zKzPi#-q521%be&g270-fcM5qp{ON^Vjwu1PYRHW@R=-^V@PyaN)l+U zw=!868<;L!m??<=WFCQmc#v6zB&I*mP43USmz9$h&orUROc*K$Z(lK6i%z{ci6#bS z7kyzu!4)d8m87*afbDlsv0b2go5ax;%hcj`D9yW|8ZYKk`+oAj9m791z}wDS4&I?T zy7f}%E=!PMRTDLMF%rqfke+$Y`vebC7^?LOIIiKRT>$xj`fo zwjiOhFL@R{;B(fJVX+f8(F?RaH~pTFJ8bdO;0Snt!m2Cj^2NxOH2ZzxultdN8ca&V zpEG2)i@`U9PWWk z8YxEV(08(IT4tTa1yv_AB=Zw~fnMarctHk_(>p|XAArBsltbkZxu`7AD_w!t>Wf2h zJnx_vC~rNfsZ>cS#hG50-!vj;r9Ug*4z$J}NU(j4b|H?G&Zgw7&w%Z^4w4)O+cgzN zt0mb)|B!qhND52~{0*Z}zgT?1%-e3r%&<>rgcDF0&jEkUo0Kvl`zbj??MZTbO}dbl z1kNeotEJeZ_elDx=|7YN6bnA8k<7+BLOOVT_}sa!Er_&^|H1K&KvSTSKR(8bb#M*_pcx| zr8~UgcT!s1IMt5l-e7Xh8WJsbfgFCv<@T1T(1n?o)rEwWwJ6N?@;al)GoMCk@PgmX zCuijZ{Ph&PLy%Ize#z5K;zlF2+c_Ty1z)Fb_29*))(VyyrdmfR>OplSA%F1HXIdA=63NI>G zZosq{FV~BTTp`@KCCFIW!F4j%uY7)9`8{xMe;m$rQMQjJ2j(oA*n@0`adw?%{`XOm z?rxH9w;Yv8MN}qv-m4erRX313)P)SzUrgTEM`Fe_W>H1@UEnkB!1bs|Zeb=eTxL7L zl*Q#a8^Didqadq6{!Kl$ZhXG8nA$joOGD<{E6jW@>bfA)6xYs zQVkrHYf&=I;+&bswg#8l@POV-^Zv?tx1TAh7sxSM!+Ew34U!#iWL^SB3DQ)0qCpzR z`Lu)iX}bc~kj#=vDtbBa_}>)`P`7RYh2KH0{|b135$J5IG7Uga){zzG`4HUXF@g8c zp$JUutjGMY6TEVb|4ew1<8Udb$phJs9&Zhkit^?twq+IS=}SekHkdW~q&xs$M}&Nl z{XanJO3%@kD{w2^T4t`r6Qy-*M^KR&PoFkLy_bBDlyJb=$Bir%@rUoTeHUd%Vi@cZeP+NMa+H9sUyer_(M7?0L^*`ckn9ROVF^6F*f76Fds`7aA zu5s-&`R(Issm|TYIhl~-Bj=9y3I69SK4%5T>0tONJ$&6Jrnz`Y@!!PCqGx{U0*<2MTyehy zw8g)8kt=9>5UMmq1K5_U%J<4*WHIg}gXF6!NZlVT&~s36i~2m;oCBz6ZmItxKk+l_ zpmg>7e7(+5^}pmHcEWinsIth#-wy}WfrN@^aQWlPg)l(DDAo6oy_1(jJf8n8LQ#ku z3bo=an(6&a)BBqhY!{h5yO~CGo0JM`ppJ~EvSbwvfiHSYVsK&fOMj8q@QrB_RnX7O zB75bEN>oXzvg!)zXmxw_e3U(B)GyScx+zMlM9u&5QzvFc8 z4ebMMw9W`K(@VDmMEeGawjCF+5|vvm5cc7Cc8BU`gOeZBFXA_4^*42A*;eYh>Oyq) zwIfLliP4VG1Zei6G4fMyhQIg$GI)&H1Ou3|JB!S!W(Cgp4`8m&0*=~e6g?XiK(Z{+$I}J<-?Z!DVh#A}G@%=O*XXj{kGU@k~ z@xOnC)2#{Oy*l?SwPsaj=WJp+iI*87rBRVj$Nzd5Jza6;9G*F`G29P$B&GOuhJGt580de&pr-AUJJ z^nD?X`86|M2C-^ZWL-)@HJT+yd(+6F$eyx3lf$YNc>{6Up zJIEet$UL8GRO;2pQaVR=cujKT6sQmXE-;5$Exmw_^tb`|gP)6uDDvJ_Xg19+aBa8h#P z5ZaQg^YN^PQ`nE(5&!$GQH9AsPqtEjiJ8<@Z@<0<1c<6~n2?4VL$ zJqJxMh=i;IiZd5(_ED6=0q_wHqr;WMS(s=2WEI&8(J74MFP@{D@WO{LjX%eG{5M+T zZ5jv5<2rgnDXaNfvV}HM#pH+kKAQQ?RVELWU?;TYOpB+=`weE|cH7y*D`E400|T0B zDPqYquLBtgApI%cbO^rrMAO*32~1B@8B>(W-&D+`G5L&@Oe;;R&}WV}Z6pgyVa@~% z5t*BVhwMg0eAk?cH+VZHNk`zMf3O7LHC~yTb^-`uC2HRZXc1qN@KF(Om#JKVrFrIk z>4Xf92K1aM&KmUWkC|`{r@s8cb6dT8{=TqMz3Xc$#4mfXUIIVW-v%! zz-|2`m%&Eet=I{7H4`u50A)Mo(GQu%e^UCZ>cOGzt6HHtq&fkM)}RVfE7*}_yeXL! zeN}B$&4aVt7?-Tq%2W6vR98OXcXc6EXauP+&t>uGq~1s;OaIb6R+X&A$8HsFAJ4fO z{e_=7R<#9*T+fy80U8S@s0^6GR@Rpupc^AdA?-FI53;bXk%pQEpT^?}#>09q z)ovr!mRz5dR9{10+v8mA*@8Z-_1A-56EJ2du1(N>?N}}N{uVqdq!u@Cl z%#W|q3~5bS3`b@qzD&PKu4#_L(-CTsFjAvtDz4_aaTQiBAS-GkE?|e0cW{*QBQ2<{ zs+_8_syw@EhVm?Y<+fn=1(n&jdt8FuD^;9%h2#(Oy8Pm6 zxQ@h#exg+y&$;u1+WbE-nEV1IDFcb*h^%7On}XJ@5y>-EK_Qm27o5vu4Cz_x9&`KW zOZVj`?XEMs>QeM?AMh+KgezbncGgk&YCff^e~-6BZP8XzCDTPE*i|FNP1pr);8_w% zYVcx|0BJZ!OoJh#Sw0bGY$@?coew-sJRbNKvaRE?NgY*YDEwbi55JJi|m z@M~!1!bA9lBS=YRf=#s5cpIvn2oEE7ir<>5n zfO3FxAa&jWu8s&*HIkzKDeJ2?s`jcLs*<^xsobop0Cgv(Kf~2y)KhS98Lz&f-ltxs zo(5l`9o&MdYN>j=YPPB=XU}1NXLl;dABs3dbwvse53+B0(bVJ` zD{@~#sZ|zS_8irf#+k*$;4escoTHZ`)R9f5pA;PSB}n->nBo`l%TKWXWUBuN49#Hw zg$78%{bUXk=y7#^W)!_pqT?2BBdPdJWja*wE#}}t^&e9Q0ghFr;m>VCC-#&qi4w3N zchdotDplfi_3INg4hs2SRc4P0?WSj7k8 zly(E3&=Po}_LDBgD=0Ua~>*gktmPUJ%kaQHv{4DNzTe2-b6USD)9WxkXELDYA>sT-*6c0m;q=EhVXei zcB6yLFZbYm7X+WG6#jnglw-IK8!GcF{gjz_$lIJz}S_&G(jZilBa z2+VOmI7tK2?9bz#CzOVv<(+`e_Xzv9lnl9kXfZyb8)^x6>>LRLc6l+Jmuuk**@}d> zQ5=~*>@D-)9}VFvW%AMpb6r5Yq4W^JNZZFzj+9xz9f#uWx0`2Pl8ct**b(^m3iqrqd#P|5Tbj1kNN z!@U8f@kH>K-KHAn*8m)}7qg2UBoR5+%P5M9sx_$BTk0f(s11om`^ERgMd?lAKu{m> ztgE0tI)Qq+BrXgWWM)}s{7s+oT%%Fa{8fb0E8kEms5;icM#)lDWc@tDp5s%u#wGNl zhKpV!#;3C`Zxgf|v|F@CVKd&+{?Ojjp4T4N#>0VJr(FVztRwpNx>~jN1Dv@vnpt>| zme&Y0@$f3^u=;w?Os!=n=)xY5#2o%UKBt?}uhmdSkn`tZ@B0e+@SHR8Gw0xMbXzXY zL@lbJqU;8J>D{m6FnK`v9ImHPS(J)k8a2`k)kLbK7b-1m%5SPv*qzPcFSb)Rql2GI z2fqSN=LB_4_+^XmOYH#LQ>ZS@Zs4aD^O%I{CpdrIVR!rl^Y0gXY4eWS#6crS|Kvf3gaXOk^Xc_59@o;r* zU^q&4p%SQo7706(NhuYsB$aq8J=u6t3Y=ud^zjzMo!f+;uGlN{rZIguM(>#Fk&-?C z8kOvAI_@04*8?PTz4s89<~yqMd~|lJ`5jB>=?>r((wi%3Iy~ro;H%dKfgpXk-f5fAcAY#yo^S5| z+~wS#P!acME@*&tcN2WR4o;9i>AQY0i#<*EzYu(FI*37jX0%cG=2dnGNt9>_lCaz{ ziQQ&6X|eN|;M~HqCBc!#v^(43#t}=74|6yU{Y9N!VD-(zn=b3i7k&0Iu7v)*-+O|@)Z{huQ{Q7@U!>URD$l-q#uSHo_m`1oq|Do0>D^v)p zz@_x?orZMu(NwyXaW#+NJgmjN#W@j=QX*t@7<2$~2FUzPkvs!m6&9_>}FS zwg`1T=S(P%F60aozPk3W@DG=QO?SYf$BgPA*(yf)@tdw+PmYnyCb0$LbvnX!i0Pn| z`;Vi($q#R$8#BROV7uQP6-b5n30nxN&p8wk)!ZpOrUN7{HHBsI z5vIaM9NI>agIt?)zKB!EoXdlc(L1h+&CG)K;t%(cn#{_SQ^@qa39O9Pa2$Fv=U>Z} zm(1*{9-f{Paqrws)=|2vuseo{`aI_7UUw-_s*S8XTKvIB(Icwqpw^M4D`)b!iTZ33 zz5i!nBz`5wQSg>ySH2(?;TOLibg>=oCVyb6SCGv{ANd)6cXLz*D^MT&AaS)RUcl?& zZC_QqW>@&BFrexz3Hv{XzC)w@1RneoRCptq32oV@i=n*zh)!V}=y6>%nPT}R*=lY> zWTo-Z*UGL_q0FF`ngT-Io(iixS;D{baL)6R=lJek!jCr@{(Jy)5Vyoe(rpCFg;n63 zBbY5JQEWz_+nj;M_BMTMQIOsGtl2wJ*FJ@DE>ctksh+Ip#iLn_^W+9Hj5qKIbJ)q= zqgVZ-kSe_jC8%^UdXG@{y^1&*R;AV&4}RSV1fUCC_zBcnBT#nFrc1d-pSw$`QwD-k zSAfYDis&%3&Z@Rga&@v>kFRm2B%IB7Az zJQ8mxYMiC0adQ7vW(D2n)538mB4`SVxRF`h5BLuiNH<=|zViiarVi`ryga|ETXZ-@ zQRPQ66^mnjd|LcMT$1&nF*Dv7xPY%BN8kir%GcO?4zP+0A~B-9BwUh02Q)?8nW|le z2hAZ-ThSjd$T|2S`&a>s2|nR$Qv;q-Gj9Ny7t>*W+~Rpf!`I#c=h)wMp7XyrJn$Tp zTzg?y)S`#+gKh8`Kipi0;iLAQsANy0{yL41>v3|#PLXPvLlW*~w6m#pGj0xHWPo&?+D{+2Rx!2rymUR4(NeS=5d+}aC|qpAA=hN&6GDo& zhR-zg3Dv<*>(e_o;&D|+BQ=Ol;Vk&$NSxj7lTY*p)atJ%oy;dWNqfOwiMKjf`B1z< z>U$%=m`9TW)szk-#@h`9tR-J-g*V7Bj&F7DUxxKG870|dTzm(Tu~gP`(cOn$xE_e~ zYkK_7F1hOgPL_A!ht!1Mbpr>x`>@BtZ3$#hK0({&Ha{@8GwV_O&M?(CiA~#$1&r?u zcVN$#GGyf(%4wA|IHx5(JYhM~9JAhv8_YRwKlBE;`MC`Jb9%2nGN)J0MD(1mQ0*2q z^fSDNQ5a#YY^-XW0>8~<9F6kzu<0_2*rI5O$C%fce}m@M#o_We?9eFdR2ZRG@wWUy z8gT?!K#iI3FGBZdu;s_iV=iazdUnH8oV91@+mq+<=Ar+hC+rKur>$7+dP2&^(Wl8O2)P*g%gZ+;}1~o&S2ozOa`)^#=>pNfXy@t zpU?LeKUl1%@YgF~n~TGW7;exUo?#&F%ikPAuIu5>NB^I_agH6lC_YM4+0(w^r4mUE zIsyE1H#PJhGCm7)j#%L_6{pHB<1K^-s|p8J6TFSr%w!g^#??k$lEj(57@a||`wds| zTo9lhE;Z_q6g>NGqH5R$Q?L*7uCYvkWARsw#TT_Oo~PCLd@OUT1t>%>qY?~Z4p0wX z)kbio52TzmcCTaJXC_zB=`PGXcs!G#WuUfusP6B1Y^Vcm9svm*O~`ld0DojOtcBgY zZO=oL?!XC2g-?5~%mL+H7JM5Au7!M15`|+DSNIrasAegDnAl8(-phB;i zD+f{UttB;NFc|MDF!TF3eU*f3l#V7sAqt~Xsmr?3g{pTnld>VKGn;S$ixSzXZ1$1y zw2&&lF?j1)JhW@G(uWJap|B_gl6IZPs$$aB8{fsn?r`@9((qKS>#Tk75^d6i^ku3v0+6-8)tx&(bw%mrTxCXb^)0T^z?YAwjEvNWD%kl@u^-yaRegflR zu%%kd;O2VW_K^8a8C(bYaIRkkvueR=e#;@kTfQT;|5t9I)HX*@kholp!JL+$z6=MI z7zq+~lUnX2uYWmus5$Ua;=Lg_j7P9@bitRmD_GPxCS<*Q{Wz9>xPbeSnsSGuzZ0ae znpZ?t%2iTQ#H^i%SPN@YnY{%a48VhZsk5HbipTd2*p1~J&)7{ilZ;%(F1J6R_8f_V zwzVxCciU4u>p1HcINp8mgl)uU4e)nr2}`ynpH+kdUJRa}Dvu9d6aHowc;TaA@+`D2 zB17z`^^x@rG@$~vs<;?+uWLe1*4MX|kV2!(hBa-zcJEr~~g}4KtcoxK4g$ z_V)(;%n_>GTOee!*xMJAK+{#&8iqwNcHu`%4i|7%$Why_rb_NiD!2$1(@z+5lUc0` zkXD|`RoIu^q8ePxFxVIVRJpkscV>FCmvBixfv4Ukf80iXsGMxOGVCIa;NnHG$8=}! zI*c;-Jibw1Jw>Qu+HxyH0&8FXZ_Pf?kaa%_#@;~oyTSa`!|Vi&>8G}_PjmqdZA$Kq z%v+a_y1?z9!XD9^pP2?udx@Fr8Sa15`@|dO>jp=&5g2MY>iuG5=yde;M^7}J$-*T# zLGS4olYD=v+P{)NmCSL|k|X4W75b4R?o{$XeN1EbfR1&dchhl%KXIf-@)+xSb=3Cr zn8-*x2bulHpevY(QtSZyktpXkROV$JTT!B0No3I2l5qfwfw%u3x_Oc1x_KB}b+c(6 z{B{GY3{c-p5|E#azSE4K4Qp%OhOLqVX+G#O+jP?F9dHE&BX4*lMGgOZ5d2o zL+Q-+SeK%DD`C}Hj^VylfT{0Ba|N@-^w_lB6iFW7T^{8Sro8QpQlpLMcMC>+Gq}^y z29d#(qtAJd`vul&IZtyE*irt$q!y!GFuj6%Nb&D_xfuHsD65$W;gJ{|#!U0sCj&pR2v?yal~aVHe+Jzb@sLyAP1aTF`Y5m1jTJz#4fzzhhyT zd+c(@335dS;rLM1UY30BOq)8-wYVZK5E0~0k06(NC6&}-az_r5eLR`^VjtbK#Zj4E zeHANSKo^^Q;_d+DkKL$1Eh;z_; z(4Lhbof~ZHJ4LM4d0-BQcq)nJzQT=f9jK{xEsku`11@%AX5!X%*A0Pt+-ISc!x2E~v^> zc@OLNb1HHTjP4@rAiK#^mBV}44muS~4>OzB{T-D?B+vDgP{!0?3CVNO;;~HQ^TR~E ziKB2M`tXxdl`Nd3o%iV33(NbGOmvMT;L>;lRA!d>7}VHKk6ltZ3DxifGKLnA=QNGk z;!I}6Etn=&RaQ|tnHQhOgK99GvQqdyZN_757;}+d_%rQiQZfp^u2Qm__-b8|u7e#p z5d@$r$$TZ42uWc`>cI=XNFFgwC@u}j%Rrt3A9)S^?o3kWGIcqFqrw?*gB5%>ovh=Ne7r|~k&o7pf;<}sgbvKaXVRZH;iEoe zChz9{dr2T$&D`cKK2>*^#`@!p)mhOGty*8^6UV_AD3syHnMt$CQaBYQn4`|&`A;F4 zX(AKa06R=nk+OG}xS<|g~vSQU%Xh1Z~7y2+XJi@m)d zv!UGlQIDrIRc;IF+IaYGE^j&V>el6PoC-0|-vra+y}-&E@>L)L#jYhBCLD>c+$`$Q zOH?C2saiutKB0>$*2KBC3noc3I^ZCo3#Qy@vP8R(?-#`?F`a5o<=aoS+>(_pnUud4 zoXbh@bsDn+ejCh^bb2g$-+R@lEKkdv9FbJ1Uqskpw~(5Sw@$&*glbDqzDq% z8=&JZXZPB2V2bBv|J(&%JdIZC2k-COZs8uT$4l|4EeT(9kF7G5U=eo0-n{kaw=6+J zlt>zoklnKfe=7|K#rE_Li_os8z?f~x{P_x3XeHbpH@Jh)t4!v2WqT^Y@>oc%@`-gf zS8K70T0<+)lKwedu$KARF~L7p8Zqc^F%akW!fw=P{kXM(CDV|sz(AoyXo5xfO^^e> z>jYjY^O+Zvq>jG|0yz?mO9x-NH;qi#15`zIKzRc3N4dtzwtz|A5axOXK#ZQD`YGk{ zpdZV{gy|pPf;a0AshV4Im{s^aL;HG7VqaBr>ofh}ea9BEvc!&f&X?7kHGcLBc-}{%g?kLIei!=Aq10C;t+DX4s#|-L z2wa^SKa^f*09DsUD*dz8r`BSqTsokwKZ(DQ-d5b+k~1a&9I7%&Sz}R1*&OBQcsr5a zu@$!ii?a!?17E=GN`loKrvfR%oV@`P*!lFcUp!)OEc^L7*zF>YX%ts=FFM78RIz3_ zlJ7yE#LS{(g8Njo@8P^(rH4GgQSC$h>%oEbBG+hN@*b7E?SR2t(euiE30Ct69F6|K zmN*9Ac`BZa}~meb_LhNT~-pCD~xGCH?D~*Zj-wLoxujOs>8g) zsYPCKR#ih=u#Lwb3od^XRK6kI$`@e-9c>C6o{?|~OyYKuy(F)NNLS#w_?k(39b9XY zWNP+}rli$hV0F%xmjSJA4WA@8!FRo456b_o%+nKbTG@_!;Z88&#dr{QBhRoD8GlyT z&6il~`;fL536DC1G|EfN>1WCs$!g1-(&q3{M6$17<6qIa9KccMHLQiVO#ENLUC7Uz za2vO;;N>dVD?zN%3vf-jD8HQNJdm4gxQRz`9{%=r#Zz{Lf9wiH;7rd@c4qDxuS`{1 z$>ck)yv!ulLpGlPeog?EY=4wKWg%6#Y9XrWg5*yIt2(Lmae^uwGi{bm>RGZXd@+N2U)?G7#+{ta4&3Hy0 zz&r8|3ho@udwflL;3l~ahtckuK#dOngKs#3^aH_erFQbymf_La0H)hD_HQebfR(VS zy*Mrwk$+`RDkIy=Jij%&@)5jUKQm36$CR;|_y*W#!#uv=WxCPTaBdsH7tAko2x<#k z!p&&P$Fa<^x>8%sVtTcQO7k5~qbZgQ0^efa0Py_k+NsZEUJ*=_!)nn8j@UI z&vbEFl6!BGjn$4xo146@sVJKo!sT(JeMsas7bmNZsAY1!ygIWlHm9Pj5Bk!Buh(JT zT9410kybn(^~*l^1v>=i1ZF{LyjKH-Le~5y9GU-w8`;_4GuMN>2ewmF)QO{-%dJqt zr>lGO1+hVCL*G>b5u#6;c<5@A#EGt}NVY&*IOz4n|pXRRL8B ze859YF)A?s`AVg7N-+y(7zKL$hw_!wM{T*9qUAPtb$XbW*U1b`JH+Ak_ju^QD{%HCkWn#RU=?q9cIw^mesxU7$FL7vh9e%t8Ml&7?i;m5HO|mu)LVt=PI_|Q zoW7hEHa<0G(6hU zRMS;(3aczHDYwFEGT^d%O12S((@bjb^0F_|XH@Oo$=pn3Dl~y9Q8CGSv{d2ZPoz25 zK;>hl?rRMGBp?-YGtaoP;4|#={UCa^s2>DAJu|9YE`1{V!&C1us)iKMQ$4eGg-=Zm zSAJ$j1HpD?@H3~tZ-jXA*W&k#MDO)L@P&z|jBBw6*U$#A(+|QhQ7F^g@-Ue9i$05r zlg_zX9FJ0~3UluTq*u%7FNVQ7%O)?rKDBn5tO`BAJB4MZmz1q&7l;j{8c4zQw!z{Y>Bg)66Rzt zRX^sAyWm?&)IqHNRbbj|#C7Ai`XU~|G@Dq-6M26U->L67PWd!e%@2)UQ;5gV zn`F01Foh0jw`q6b=&@RR0SB=&u!!bqw`zZC1%9d8-P+6AOl<)EA5nfWc#3uKYw0)N z?3$0T5dY$MZneRGW)6-bNBqzEU-5tFpW*NE4-Tk<`&KPHxWdVy>k)7{;Ap^{ zfQfjM?EEp&XtQc4;1aEc}ddXO7`R4oDY?_ z*C;We#Is_|2JQA1$beu9U+6u-3Zc&9Z*|Fst8n}Z4X8us37{EJsINw3V_bd(h4 zW=vd7>`%+ZJxG^#;gI*0;N*2IGn#XRk8+t=2Nu6<>BAW z0w4Iyls*>j<8ao502FFVa4ZhAB~mAk&c^G0Q}Q)0yZ0LWf(LL?zes+guR0IsU`w7x?jj%G z#w&g|tcVp<8)soqt>CjAsH=`M)xLvvZVh~@#*!wu{PkqTw4qe^jpw|JS@;gvsR^v5 zvtbgggJCm6+?DmcoVYwadMF;WOL2lM0FP!8nJaz8{rFycR1f`_kay(B4(9)5 zu$A_Jx_HC}j`J?~#8=>I?BhO7;U{&3rBoS3Qz)uXmnebs#G&YJ`@(}2iJl2>vf7QH za`-KH4nMvQyqpbmh9&UTL43qKra$D8O#yv=WSnR0fa^>zV-MUvzThqV2AuaPb5SE7-7!1^ zxBhDg!T~J;_u+0#Ne`j-aT#Mt;j51V=9=lZ$!RK$=f!UGLh~W>E3=4nl-`!zmg{Jk zjTVKqhPA$Rfpr_5)FHCEbZB7eFf}cW-)tw?Lfh!8j*^UVl4P=LI0t;8fB$A%%xtv? z2=#X|yf%aT4zw1u+Aa6+JR45p;B(w#%A5Z%r|pL-M~6dlYuu2}89Exu7#t+=^~9;y zraz(|gadGx{$ut_HZU}jCjdWdfEpXVatE;ao!ADhf1$0_n z9bJU3h^_(oA}e&Ox%VF3HT-XUy1;A)9=wgS`}5nDk@E5=J1slEK3d;eKTLm2f1O!t zKu$FC)dc+A0&o>xhzIW-gFhK>Jy1_QHx@9}Ben3RDS))XL*}36{CE#-vZPv)Ep^DO z*v2d=fT`L{+bP^ZLUGldh+h64o+-JSJ)gZ4X(3Ulqvmqk1|qT$_T2=$JK8$hIbxXW znPG*mu}??Y)X=W7C*T4;hsn8}Db*?_TP^8qHP%P`t*hiP4+rg!v{bQtHXG3@xy)gf z!j__VB}Jh{-^eT=9tFD7(u{t1Jqaeqc+CVj87;x#d=#3kG+Vg+m+d!BbIt7&z)*VH zN8%~Fp4s+cW^Fs{+w3ctxQ(ITp3ZHxeJx+v1=9Nvubiu}J2r#o-k^t`jq>as85ZgA zIr1?(903b#BhxY$Gh{!y_uQH8BIkZ*ma{ZofJ)H!&T!_Z;moc>v8i^4xE-!GVEkJ_ zW){(hCg72A3LlcMZYg@tV@$6O!Sv_~uH+BHz|MTIyQ>9wP6Tt~!~Ev1GvBxfo!l>DZFmb8O^GHUK1ZX>r*Y3I zL8jjgi-f;7kHpO1ID-raz4`-QRf!{M#Kmt2PVm`|B4mFK=P^HJimh^01La%oipSfw zB3zJ>a7PZi?~_QfpJ)8Y{R&RVMo{BDpvR-#gTR^H`1d^o8ytY^pFgVVsr2{(P7CVk znU3b1Ip;WQir6!4hnbEx#^L0wb(pn^wJgpIZ*VH;!(_;Td)5W>cJq4DxkqzrgO}3~ zI0|L)3omUB=Ccqu3ql;&Tl2N{Bp|jocf>_;Ezj`*>4iDC1mz}kPU7sk#&1oxi19C; z&MY|zU#zOOvA9BH;gR3nehDNl3Mc%na8XO*sqhL{-y$Fy*YH7UMJmfjI0WNCzJGgK zvHo30(a{}jWH}Q+HU7ihVBBtI#-0trlwX)nI0&S20DQnW;Wl{P%jikhaa+tsGx$mz zQ;hy7*NO|ngnsY<6Z7V#1L@-2J_D@!iJ+kUP%RaP5BQ7fb+7k4bC=fC<)KUub8*;s z^rTl|D(qtxIt4VQ4R~f#FxA1Z71zMDUr&X91BJy;PareL>L52my%XUU{saFB@JUIS z4hQM$3x2p71aS)3{RL|LET%SgUll<`YUpr5O+f>+pxsER8i@9If*^*zvpzjV8@^uw zrkBZAl6$m)?Nt}eX9SA<0fP42r#;nkV}7Cr_v=T}c5l949u~q-5aqITu-*Ah?fJO@ zsQV{z)b@c~&SmC2pRRQue=Ay$jt)KdX14g9Y@hm;e_Ta#%_h?}(@;G4-;!||Z4?>r7+$iw zoMApX+c1f#U^5(-S{M|Dl7`Hj9!yK)3=8nf&oY#U=Tik2&gI67##GkPa;E;K&0LYM zNZxFRpV&dN4bpJ(lH%w#%W|G1rUGbL7vhU~k(H}DUTyPjYuH=%vU+`D%2b#+Xixmb z&oLPlu}_K}MIC_-yZt#Dn_NGn2C$DTC~fBA&Xb#RpN6Mfdwz=r+;9g7p^F6iUZmlk zMe(UL$D@(WXWDI?WKa)uq!auYu zGwYe=d+a(bnJvGy6eBJ3id9Jh=vr2ElTFU7dmgEnE_)Rib%)V@TOHx#2zTTf+zZEb zI&0=0=RIcvi5S;$Wth$?5`)U9rPH5j`DIq_X*iJNV)QTAho*v(R<}O|F`Wd9rHk`(~RaR8jbok9AP%|*j72eIUBk{@sEv#EwmlK zwKX7@_gxxL(kl4W{&BU!v+4}|hQ0XItZ{$N<2k5cLKFwdEP`V|1Gu;6V5ltc^a68# zPfuP5uQdq{FhyXv^}|Ui4itAb-tV(OWFL9`af$g27Onx&?aZ1H$=WfI$zyq6HXZy0 zxa9ZYG)$nc_rf`Ri<%(bvkIQxYgXwR%y6!=#s#xBw}utDj6K~1+p)6!GhQEMZCPZS zjQG9k3g_pd@xzqW=Da0it9(TGC%!y9A zwM-fY!q(E^ztq6n9j?xL{OBaC2LGX1FOJ{)bXJTrd45~(LH8dr6LtwIG98X$PMXU- zb@H_bf^i^Yh0qT_L?eF=txT6~p(WA$#>*{`x7`SX_1DIpapb z;u1Q)qL1E+GeIkMhVHOMTRG~(Z3w2yt;CgE79_qLD(Zgts9k_@{gZTn0DQ|TIU6%~ zJ_?2{b_KHgk3(1Oa4F!CHF0-#?_-C|a7Xhx9|4#5gAFi^B&^E#f5f8~ssw*#5A&I> zFs|+h)hN;q;%+hs4Uhz!XDBM>26UDOq*m!D@GzH50m`)x{Hi|M&$(pf{8rS0dAteC z=@ngnAh=Q$I-UL~kSC!sUXZ7+*{}M7Ht1QNPUewnKiKeORWbZDzw>jG=?|OWx)H8S z1K&DL4?F_xR98^$5{fAN%u~S4t~0khjmmci-Pn5hDNwfSIL!Z)hbq*H82q^hfQ+Ap zAzc$iZv@)2Ey~BRo})-t>JRFClkUu<3dfgwmU<;T=aK3eAP$-8Q2eh8YJxST(c^T& z`MNL8*CX)gsH&Na1NM5&Jp8cxYgU6gFT;WSGI)f)HWn13DaxGn+7z6;oA^chxwKXM zhWO3$yMe>59gp3AIC~fLZ|6V5e-Zw+GyO;T-}1i+&T!NJJ&1rLAjjY7pX~4OuNTlZ zpmspE|0{nZ4&aIYQ~gW$fA%x@eeyekgK0iLo%XhNnAWU0&g1BSKWP%a=dtQ!P?qAV zW6CJhr#lp}iVNVzrR4kR9LveBq4W#EUGyuA=4KK*-acnZyV8hLN#lLXYkHhlb_@Kv ztuUF_!{<#P#rGyR6Un?bQGHbNn^2W}5|_<${z;Mur1^3A?~QARO>5&GS?@X9Ny>!X@lss5~%XhJoEG)Fbh!49%D z8oa}mT9-zEUw8;_HjN2He~G4~CP-scA4GW@3woP?mZ}dr-J>W6t0^~wiWFqp-bnr# z#a)c-w{)H~R{9YK<=!CL3&jy2kyl0YK>>2$iCq9Y8-`;^v`{UyzOE& zwxHXdOeOFinz)T5oo-=T7snOwoT+atIA$cX=-$lWa=j)Jg$c0Fexpnegr&HgqxW89 z78N3AYr6O?s@VGE6(5s4llVzXp$SNqYM8uiB-NkAK*3O4 z21qXZD@~Oim7d4H={48%Z*+|9W&S7=n#g*CH!TI*zJ|*0DhaYmGO4EFU)vVN=K?qf zdtf`PN9Sk-7tTVjPzLO}FB7W>g%E$ASSBlBAl7@ijU+Lv5$nTp#Xe?N%edbJkoNPS zPY>Zv?B%P^6gEY=LZS>r6KX}Lmw}5xVWkgueY--WwD8^oXjqxoUc+Y>(PCXXqYYWG#F> z*)ccZff7hHdYTH73e<5^1BhxJky$dgpFbO3j?5oi2E1PS|J3sOHk>(h}Hv zvK{G;f=(YwrWkOTZm6*LF@xRge9gXUbVlHgKbw7MDE-?J5=zp@TWL<(%nJ8*_!z(3 z9bh0|#{IQ0T*>)xCH2fvC(_$U=mVy}94^Ns{2{8Q7NS`qy{LkCjQA;d)i88k8K8At zc`bEPkF=s}ER$3_7=B?Uda=0s&&R`}x4aX|;)0scAhUsYgUso(hO8Qm-riVQL;qfVpI{`p%GU!r&x>TE|{A?vj8!gG6U-H z0ql+a(3|Z9o%_h63S+gbBW(hw=NQ-5HT;b(qPqSiwUf(Ofom^fP`jCUUMiF>T8@5!h5!ZlY}-T()M!aSQfFm{eIM>2A?DHR3a)I_2Vnu_bd6y})c z6*|uT=S&FyD)fq~Ai93cg(`wAZp`zxYQo-n2)51yWk>esgG_i{;Bqw^r1?8j+6?eZ zgVL)EQE5RXMYw@X1FtR4nsSY4?q2S_j(xc~DkA}_%QpVrOs?mb^49Vs(&%<_MX6=0 z@q$aopCbx3&srvWPf(r3GaVgBM=BDYqWfA6pS2Kdi;Yx~RlJ|!psAijjv&% zHwtZgciAx6cyeSqaz6NjNL%sNH~<>lmfv7yDmfK>VWi|6o-*}O0qOY5{b6owgMaK6 zoWe1tEIgJ3^m$U>8x)ki;5;OwN$&$w#p-@QRXGO+A89=Jb+*nLg#{||FDcOvy$gqMySHP}g}d+}7pCt!h?U`-Sg z7<>hBQK?Jc-;=#~4}0}rfr2@51-L|QU{|eTAASjI#wir#q5Pq|mx>&?^7)B`ux6y9 zRN*Gn`?=h#t32yAFpzq&hquDD><`SOAN;R3t;b`2Z=Ht4so!@hZ+4T*ofSdfC5K$6S?B{%T2QIo1d}S|;$^<+Zm7lJz9q{!W)8}U=e-k7m9)_EMwMJ!>}*%VDDbEz)_xmFdZA?(00+)%k_h< zvnm+XF+8fiflmdKp7;*;+2Z(y9s&>czzDu*8H0Db3f}TEbU~fW9k^A4m;DJ>CcUYE z`K{@=X@%)3{^Qq8Z(&$V@fhz(`tyA7<6ZEqi&(ngTs;MC&qunW)u;jPT7yX}YYYzC zm+Z!0wrKj!o~X`5sMFfPLc2{`^Lrc>{bB!ha`s?tzXT$eO=3waln!HA+xvsf4TUS* z0hMTTdXB0t7kkD&6thd<;FNZH=eD`XX}|P&Nl0{07q&Qv*|V()hBBiN@>3m&Omqvt*N8HjXlmHBRIG8gTHj#$)iy zhZrXtcH_}Bm|#3D0gDW9mqzFF z48PtwxD+o(#iPUDs4fbC@$i=qlNWs!PXArzzt`ZjT>!gVNDn;?J;DG~WWBu=U}jf< zqgL4KLw~b^wQnk{sb+Ab3o_xrwvMa+tveAI!@97OwPX_iuLJMhM|JTC z95|gc5|29!HA5M=a)a^48w?w60!qZQo@dP0zp!2e!Jzs`Ki`y|b|h@@g-oPh;EHO7 zmsP+w2#=yuxV_!P3sTG++e@b#0TMTuYI!KVY+DeweW<<<@cufrbUgj99WR-57}n)M z6vx1`4(Ix*lE+;e4qIk9c;iZ#ZtsOUuF2XYg5WyQVo&8XlD$xG7L*e%kpYBliW3*F2u21-Z&LW4$j>~g#Zht}KuY-3Dwl@N2 z836No5(xcau= znkIr7Q!tO$FcyXLXrA3*Q4ciS5llA4uyd~BfVTu}w+ejJB>1WA@R~KkI2(Wp>KT<> zdG9Ci*hrL)m(ZSuyWgVu8sRF%b5Dh(H`-Z~)kTiebvEa5B9+@5p5JUd4UUnEw+`>~ z-Ff`DmktfsQfZtIXW@J}m)iC<8Uw4drmGTp;qAH7_mfU{7az%AAPcqF({?d~xZ}R( zPUHM5j|J*Qq=>>f z1D;aZ2GhxHr;ZX)ht;Htngk>IGJLh4uo?{brFrO6zQJDGLH^qT=G+}gh6|MbWG;G+ ziawTV%_hDJEF(2l%w@V=w%VuCN^eai6d83vQDCsWR$%tklCzc|M^yPSFHNxrgy zvvWS*TO?k}Z66-ibMYJ4%16hUhR(#}dMv8r+;eyYb<%F?mE-tZKfzh)J3d3^Ja300 zctA!`aW({V8HILyB)q zsKkR?%`WD-2k-@oQ82rQB!(8dYxM;c>r2wKD$7elWZa|({X6W4xt*Yhl*zf=cXE$ zg;=h1v-|p)Q%v7^AH*)*%G?%BX*4r|!z3ldn?ITj%)*40;{3+3%pX&jK>lL?DQO*y zKjUo@Sp1kh?zTN)MpS|c=W6Ce>2@VY{va$?A=mN@)*}VG?RM8WSgY z#k3lYYkt%_vGjE#ag|+;+wyqtDfYpgJgS{cmXdj7Z}A_odvjPT{c$3$h)2L26#XN> zkr()G;g)Aa8z`stFGW>Yjp}edoa&pPuQ%ufs^BBi7>Aka%(bS&z&y&{vjU&2&0NE~ z`F|a|(njGSZfp7OVd}ID)`57Cq5DF!(1-pspQs$1>!x^cb%a%Q9Hz)c7$a{*L7)P; z*Y^;ZWlLd}nb?&Q#ec=sxzf5yeC$ms{8s`c$8Zo>j_>|bc%CoO&uqbsznbWxupOQv zCis(`1gRYDXrG-cs-SlXXuB7s;Q-FUW$YtMSX1x7`j2(0>8#V4&CaAw>WHiPFvn_Y zrsFV|Uw~BqVnW=NG?1sx3(ikm5vy498ibj z$eNVr&03bDHkI2BcI#x6KuzJ$T^4@h+W#grpazWS&K9Sz3icyEAPfajJo{Ej(R+6NuEK&i zKkS6dR-0Yw6ZsX@;Mm_|S6syzR+*{5Umo!lI+=rQD@gy0 zAmh+aouoPk_joiYofs4|0dMJLFjt#_V5Nfy_JQNs97NMcM*M9UK#3&Q|A2pemya&+ zv5xG?81TFZT+*f}_LD004F?brIO1%&`{$%jm4M0lA4uSQ@=5Q*a;=1SN36OLX^LIJ ziI=FC@-`FCs$=TYV2qbxbRAT0A)Ry<`05Jst(vNBaIo|9m2v9!@RfpbwQ7==qc}pn z2`2Vo_}Hs(ueQR=tBntrQd1a=d^UXR6>#&G<6*r?vrBUyZ?7M`p9U-N5ME!;G!Mys z+@qPp&5qaPvphcE6&QvUKsSeKM&Wvutv(Awdk~MHfLa7cY7LVDnaTo#{5O7Hb(AZ? z8Qy{JFO<*1k24N`x^wiMe$se$##DCUO5)4(nVn!1f5U&mUl>nS)lgt#l6(#&bxqJM zv)4rJt%GxU6&>yvZd>V=PoZaD#d@@db18(G&icG8-^*wbDlr|NLr-`X+^Gp@*)-M) zyRa>Z6+1+S@nQ<4rif#wPbM$5B)HEKxK`Idic%%{NQ5Sj)>4 z__|h>1>z+7oeCz6isd{wQ&(wO>6g5p9Ye*Ul-$7)u(8-5N2^>Wc0tj1nC6i}51B3< zP}?16+TY0cksjhOlV~6O*{Q7Y`Iw`Q)B$IFjRN!$S&`FWjFf>lQkD61aTp}8 zoNrL|++aF*gMMr$nTT6?KOc@sJT>7ty0nMRAI@jYz`0)`5ETk zc~HzgT+0!t5fiD;a^0ZY(s8(`BhBy^91fQ<-d>;0r|17G^g0K9-{9N6@O?mYl8N(w9X#~9Q=9jL6R?5m`mx}RKq&l3*2)Kg zNf5!=PzN-j5qrvJ;d#_MdQgT6xL-Dawblpcg4{{UHoBUFXi6Q_iod`?(nLmlB=sDD z+9*o1L2;k)bpcvSBR({@Noq)98uNmwi=MM<7n6m7I3Fx0MIesazqe>A-xJX7r1HD8 z;t+9FGFnPeQ?9@Zb|97IK`PyN>gFjNv3@Z1D$*Avv4eeMp0k{NEx+gs$j=PnJNC{p z9Dygk#=iI7xhQ|1g1U-42hdz5G5d7jUeVtvaz139SPXycjrLK@#=UTZdXo9pm&BWb zHa`+fnv!DPiG+_`@YbF%r+y1>CldV%I1rL(=m?u%mQBH zuX!HL-#NeX3yTOX@LjUl&8~9IFX2^>13_;9u9N}>w-_B)c@$e$(A;%FC0dajz4a)P zlIT=AfZ}^#Z|otbrIJhRdcz~#57Vfuvm_JOkMu#)sIRut6-~$W?EpRhA?C2B9FLj2 zzjT$exA?h5JIUpF2eon z1A3V%mCL%p>wb*hpdHNa9ZW;@@*>RNhQZW7$lT*GKF|qFRW2}b8BN-CG>p}1AjVmE zOFcm`IslK&2)SK$7LI#Q6ha|Pg9^!9On?%=MiXHePnY(EHQ!1)81DNFxUhT3#km8H z{fad$AFR~2{N!mK!Cl^}gQgE*BJ>{$)Z^@5X89j-7F_b$igqYTyD=4BhYIvQ@8_ah zI?AKkuei$|b_q4pFI*Y}l}3de_o^DoT42+2l~3`ZzRuetR13{jT~+Ov4flqrehRPZ zBvm>rT8+9J&Qz1tL)62V-tJWIf~9+o+Js}iWr z@(3F#KcdO1t1vK6lB0zfh-2pg*8OLAuKIy}AAr-E&1zMRdCNL_%pR;!uTWq&f(1|x zkN$!JKS4H{$xP7BP|nweC~Jm;KrX?RHx*QS8ty0$@NR2He|D5>-X2XvI0PMz)=q>=$Nsf0-ay;Qd!6Sz`*X_ga)EN9FIB z9puRKqd2iLK?orQIhZMeg)`_ey0aO)ik(SqZzMOPMmsL+i|?xmmDp@{TPGOrdJx{< z^pkDyzIwoF7m54T31*`)%#+r!-WFgTJ%V1hwXX__y*uo${kX0NfisoD1t}bas~Sj` z)gyvSQijz>?)`!TPHob;MxvgJL-FyEo!a3oj~~hae8Se_==B(vJ2Qz_g;~d|gJoak z_8x_)2qbusa3>XPqOc5l{$?afEn?NpLJu*BN4Qq}UR;A7 zYM}jNIZ7&&J?-&NU&QqC5l1GNJQ4vat2UgqeHB%?X3Ctncv9{mlz%5T{l^Wpp24g_ShV zlu3jH{dgswRSYP7EVGHCyeXtLKufBT#*>@a-iK+-6r5yFa~mU_!wlm9Q>Y~H`*UPo z-9Xjy3q4E*7)m~BDwkBua~Z+(X&le1Ha@?z_^3DM(_-0rJ{rvbxn8eDUYYmR@?RYNBgof%yq9a=tWR zPu@-M`3a1utoH|sKz;tC)KJ;@X3#aeA}go0enXKjAPJhmYGqH^fLACaSwo7_AI29(Yn0rEZz zl*&xME0K$9?f$2k8ki*Y(xz(jD8{oFG~^rb$#d_U*JaRqo%Qf3k8WPbKcb# z1&E4s9ee;sxd4VVlnS8$^W*n$2^Zjzc@hPCEBH~DsFJ#P|It0S<5{l6&#bunGYN%_ zz#rbAH|@bq2}XVj|IwK|w{md3e%RmP3wIHf_9^(o58>#229dvRzryDi;75Oi9sSGx z3`aNt$Dsu~RX_HvUGT%M;>Tfi2teyg;RH1b*NDN+Wgyl*jzxZI!y&N3Qe4q6gx7*5 zmjpMN#0)MGu4W$)>KtziTxQ;pkJX2n)?Yz2D!D&A>$W&>eiY@WhZ>4HKnUMqA-B)UX~!c`FB8Pq`Iz&{(~0yO}AbX)4b?$k$}sVet^{vJXD zwO90oYspOglt|4b5w`~Q>q9j(ohtG^*OQ7#Z8!SVRVX7KGMSYz+4aMXyB2H0PNrCk z!4Qw}wwv2++^?(4isEftQ5MBnKZ|pHAuIe4u7cCD1)S?kSv!(ZX+D7clmh#ys9Yld zD|5;^u*yzErz2w~I#PZfrbIIw@5iyioM3%Dp2tvnA@|Dvk&YKa8HG`dlPgzO}aC->zG&!lsKW*7Ifn97*#ZN~8dZ~cB zTZa6wn%r|coc+c8|ke0V(+g*hcb@|X98}ef2b`ym&ahb^VQXGJGR?h4^gUa_Foy{12Mo2IChB?A0oC+*d&F5iNw*mDxGfAHf z2fH|_s9)W8Q3v#K7vtu`!{Hez_s8IY=FTkUy0$Q>Yer`{jN4|{F4FEN@X;XF+$TKx z`}h@aqJ~`#E|q{|*EduL=eW-!kk>$dpBYZe5q{zjZgNM^B&6Wva)G*ct?RbyAKtY~ zm<_b!ubd>KUny)(39Dg4=7#-T zt;wxvP3A!q?)^Dr2g_ifyl^IxzLW`{@rH8)+>!)%u6JQxuOlD#Bx~Dr*7}9GYqsE4 z1sAld)a*k@5-3lefE$HIcl%G=k>bvNs6mMg0{3Y){aPr6iHPbjc683boTK;@KF3{3Y_R488kB}&@WP{EiZ8^i@Rq@4 zC}J$cq%+bOi~Cj|JgS=Dkr)ZDyrQwN(a-qV@COF@5cuR3__tpm% z{hjQd*zllyV5qK?M&N~wmxlJ+QPJzY5%1yNZX%wn%nENl(d|*KWPE~>i=u~ujjv2|Bn58 z^)Kn4I=yy!@AP@;o6`5CC#7ekSIDT7(I%r`#;J^Z87Ue1jQW}Xk;=U>^FikC%&J*+ zv#w-)%<^T$>Za)S>E7vt*?qG&p)x9^pQity56)?lvzu(<0*2+NvTE?$rx~-1bxl1? zYf*DqOjVd}A4I3|(_9`UPe;pK%N)xFlE!oOJXb;Mcay{P*7DAB*K)% zOG%`?p1;0FrS#_2i5^hyJZNYbMfl@Y-tX&4>TH2=2ENrN<5C+FkGlli*5O z#`DfS7*Ezm@VLg1NuqSUW5!>{`PnhW(Z%rt@2YmVwB57quua7wuqaNSC#_>q)f6BB z^i-ao=zpkx+FDxhe!Jx$UW>`7Vv6ENQVXv4F6%8A@de3`pU3g2gwpC3o+JL?;oa%I z!$HZ<fe{#{451lPb(N=FwL}TBVBV1D5j!j&>+3 z>K3>mI$=dBiCK8iAEt6piNED#WeU(F2CygD@ub&Kq2Hn2=}!-rPIdQPm<+EYlH`fY z>~tLjLAZo3^5yfrB4uqX{y~NCgxv_D;biu5f*H(oQn{7nk8RoA$=+hf$g-6 z&Mq8A*)!DOO<|+1A)~PnUJ_lD7vO~1l##H$wvvMrgkORL?P>#9rsLIp;F}J>33@d> z_e?&I#tXU-n(c?G2ddpLkh|gsT?N$k8(vU{@Q7-RH;98opg8zx^%X&izjU2%z_U-$ zg|5WyU_IUDc<_R=B!+yH3l%CwC!D!gzykdNYr6=Kr!*c0*I=djqZw~P%EK|ziwdY6 zDz~bJx{JCNk7z89W*6GzxuEbvct2Ae$D>=So{i>s9w|pB)z9z^3D9`dF13cvzn>-+ z+`4q$N0UIVM}bLi0I!bFYG54~)ixmiC=Lzz5$!u|mNo>&bz#3)zeXs>V^EDZ^i%jf z(C#Ol;;YuGy~#(@wTrobU+w=$It!?_vMmflh!78HsT+0bI(2t<_jc;;GIbZ~?(XjH z?ryZD1|%fJmFWB6t*kH%P>9@n&py9S!FfIs7k6D{b!8D{X*!cO;+F5I^eMI}uG3qQ zpy&_w@J)W7wud*M?xT3@h2<969$x9fyke_)wf>Upy$a>*c~F@Y@n)L9j*}+7UD$#w zjQ6m_7f=GU#O-V+HMMkP5_{lrAbIKVI(R+))L`S8nb zrkA-td#xRyOZJFru-g~Ot$G9BGm`ALWN^gZ;H{>pLNMm_QOkYfc*((TvI~>uQ+9{3 zwAr*qqc|9a-z+ev?yXgC+H=Tkg9SNzxo_u2XeGGF!x0>>+b?P-HT$x&fK^RZ`Kdm2FHZob5>OfednsP zGFTMmwh^3dE6%8*^bThxdJX5?sm27O^jpB*uApkz$Q=9ESJ)p<{*RD6LeGfl7n)6ysq&j=6I?3`y^ z@r6Al#itBf!?&n2D)BxnXV;M?C_$TT0=tVKNOnyW_B+|vWcEszXP4d;MZ#3}5~;j` z22ckB?b4rc;@@WPy;(G0)RWHKYV4X+^xNErIT*>AT3INh73d*4g3P_LhTU=sJlZ6> zh$Vu%sAjJ4-nB=qFa$iQF&gFS>@y>|=ZRFZpDqF}J%+Dms%@MI3BRLPdymeh7G1?1 z_}H1N-F;tb~RS<~|;$(2H zjr8pNrepXB2xw*6aV+fS-jK-AO=#hfzM!G`u;3~fP@G^9t#q?dEnPry^B$$C7oTDk zrsAJ47=pkkzYmnV3yL(M?*e<~?o5ducF`ADYwhG#zhIBO1)tSOy4t(Y*4~;A| z7r>Qdr+w}Ttn5+xPcEP@(M0_atQRb$2kHmObCqG6zkngm$?_W=uVle4nlG8;eTtc2~zeEStqi&Ugl=({o^tALFB3y%>L)ZiH&CR3mheDND$ zSXcxs;2U{YmBEXWScj&7yTyREZ6-&dgLE`HG3!i`9#{8=8>rNm}Uq$nU6I|d7J^adPe$*+?>X; z9wZChk$G5sX2J3vAuAYW zmMUK7e;fvr)5G(?!#S6q7ERuzHuen7VY#W@Tg+>2MOI7Qq`RqbH)2UkoA=EjDm=k0MiNEjsT~!n>f-@hFPEqmKFy z1hzicnPGf35R7I5|6L72a}Hn8TNo@Y*OnbodK!U;gZ}JAWt<&^axOgRL)6%<$fwFJ zsf=R(kR*cSs`5+(%kXghiO$7r(gTqXHiFC+;h z0G$Oxj};#Qu~LiltW`~g8BuG|LgbHngd$`VNZ(!*!OKXc=*lXbj8@nXaSPmjK5CS` zobk<3PRUurHj*2eKo0eG{Mh&L0Dk5Ef(G$|T&cD6xvfK$a+b`X6JUlCyhQa-`%OfD zaar(15D;|aYCDR*dzPz0RUX57TDPv#Au<*Il7m$uI>>x>9`vd|Jco|_(XnXcnu(>o5PxB6@PMW{#X>Su z*ReJ{Bi}U4*YA?IWfqsFb;TzBMSo;9Qc)tn?3;n9)yILP#?@2~ZeugtMt!nbHo$@% zWU@RF-?oa_dz+Obe%*CSCE8~ zkFM(Gf@7!t7?4WaZ3)vEGcf zQ2;y9ls!;GaM%iP)n!3?Ymute7G$<;R1B=oJI?7Gg4gKXw~{5$7k&F{eD#GRR*}(Y zg2AaAx{2=COD5+89Iapdb3t;Bp*TGWC)^WG`3>2lMNoe0X^r@Ts(QL-yr(f5aJi?n zr@SY^6Xgl9H_qmXBO_ |Bj`JSz0q-^4+)hKjlfaIJcvoz!_1sc}|zjm*TMPS`O zGRo0>G67su=kxmdGxhBR!FmC{RE+iQKJ4pY)Cxa9FXxapF979l0l)N{&gq(<^uHpC zv$xBfmfGVPJj~cqB&t5ws*6lJl~E>~V*O1OMv~X7g>{J~pD4&$R+!l>Ae4#xLN{yl zd>UeM2-Dzv>Y^D_M%`jv?gld;BgtkxNM0v6z{gA_^=OI736`08H=siJ3)}F3KJAm? z!`yDccz+CQP%;hVnzDr*>o;5pnLWc1D4ME)4~|3=G8HNyg=lns2df4f4<^}Crf`j~~D~2wI(e9b<>+T9D7-o3RdF-A> zU<@begzrob>0@-zKgi!00kZhYmklg)5V+-BFwX3xa13XcK8wVa9?VWJ$Xm%q+e`-N zN=?!)CXj=%9t>h7EiYBk$CPK%D#-klhBM$ex`!j|@ogZYt^6hZW-!ZrzScB$q=Qmw zGS+eCM|-b(>eEYc$lc6+lMJExt~{=j;7Ru#({MgqvTq?xCC0uQT(6o@z<(paLMZ^-XIh3Dm-V>gfgIBhx49go0wKcc?< z22PmGsdnb$CkN71eus3H6laDr!X-g_+l6GH1!T0$;5mMF<#S8jFF^~1nzIHr>-Y1lgrEhJHZrhgI%S&vb&#>_++BB;59eDtA^X{a--vQxpKI}S!&)p zu3s*ZTjutG14g^M@l5Kw+weZuBZapNxf=O7lB>|O(Sonlpp{)s7e_X?mj9}F#wU1m z-Ca%TUwDFcxGg>&ne!URV{;nWU(g^Rw@(JSK4a}>m0P!1npu*`&d6q7Zc>@{r*BSg znr;D^zHjJhs9}gOJV+ayRx9ni{w(OOK<@#+-K-m{Yo{xr3#FP<1$@k>tDqC!BO2+o1cZE2b~5Z>^uLU#Nepk4e*}6-sNGHa_h_S}09wsB7qISO_jI zGnO`%HMTXbGrElObYXh-^ik=@(!ZrwB3WvxX%=Z8KTSH5oK&h|=EdflW(!DoUCRQ? zHJXZElO&ao=TYC9&Dz9T!`i{x-P+VT&|1fu-x_6oWjVo-UDWc@+{G*~|1rHa%`gox zX-qfMW71z5R~aiA6L}25w9{!N(|r1``f>V#dY5jtuC(rY>Xg)xsijgQQg5aVPRX8P zP0pTDAf;qVTuNf{(_~Zf`Q*FF*OFf*AK|t?`Cf8ja(v3Ll$9x)QdXyINqLy^B}J23 zGPO}^pVV2YCsW_0rli)=O$QBM0WyAC7uJ>5x6?P$uhT!)OVT=|?M_>h_9@Mn7Heo= z7;9K-NH-KR_J=#L8e8*zyiRXpnn==#*4&tMl*eWjDO!sydP_~~GV5<^ylooEFjs78 z;M~1w%h*lkSU!h_ys?|4e%)~x93~PkBAmHlE(A`$BL>V`OHNaUL+?l=*=!@nMH@$P z`Zd&!w|J#4kcG0`-jtT7D3Y5JY&~rfo6IIAE$ost)oQT5wCb!O>nHjwepq8|a_lxj zTNUzX#@aT~x%A5xv{kkjx97KaqO0PuUFE2Qbl!c3w5JSrZ=7aDzgLm(U-T6W+fEDdh#oyqJ{n03X_Cw9wB;bH0fyO-1WXAv6L_ zNX;4q4s;6j;vYP8hnSQ8`t&{#x$3?BUH!di1G4z?feh89LuogB(L&!*TrHcq)+f@$ zD1#}=00)@I^!0|m93R6YK@ zSE2h{M3Ty5{15eLJ?ntN>^ZvWkL>u51jD4YmqhQ{3x!R8nA?7|&NxB47J&UvM6q8l zSb{90KY=#r;dXrDvvR56xL7 zaQ$Jtx7%@i-1b(*RktO>fbzX6y!16#4X>}2e*un$pZ=VIj(9d6;GECwd5?oQ#Z87{1YD(5GN z%?Z>xO_`TEfSuLCRZ}G@oeu9W>~g(=uplz3JQGzX`~Y*&X6`|yX-6xeLG2JD?8JPu zjh%0rFc)f`0jTUYqOZDzhU-1Msc889@_57hfq2bechOZm1r+ss7D9Gb{97zRTkwYj z&+6=UdP-W8!`lo`_&hX8^XOH0FUf;uDF52UH|K|Ye|r8U?q7LwVd3Yk}0jl|Y`WVIII){^Ab{9q?NXo)NYy8nb- z_*O!)#P{PLoLsHpOgqWhCxj-vR+9lU3sgg2Ol zpW=RKB)l9omb~gx?347&(KDI9$AZ9G;PJbH04%0)>OSd69nrl##oZnsTpsub-uxMU zjfwss*QQ3~M^$7Z>PTPkdpvk6n16=Aa1BIZP>R{9AlikNOuX|l=JK5b>~S{H0CO+n zFV{R@hLWDKDKrc}M6df1^^g<>t`U>!TsZfyV3WPT$}b1>0Ws}X?eIbk!jU6pXE+;A zh#DW_3DnM|BUVK`CugDyYUcasO^fh8eG+6Uw4X)=qVfof;s#%V;`FG{2)-N79{)ak zzz4kFpF|lT*=JFS-W7f3+**VtbOGv_6`;TMMR9bUCi6-i5H6x|x1lhv@DrH!G+Z=I z(e{|YKRcn-&y6oUmOaZIJk`hOguX(X#yDKl?a^5^LS0=CzxFin>{;*~d+_bdLFsoq zGC9ku5{S&ivuDFz+(r{_yRiTipIi%l^k1#!E`6X zOMIgFs6P4GU4lBU`-K8W@LOyA7tp#jCfP6&SKfM#l%3?5O+~-FiZnArMkQZ7tmyz0 z|5xcVXpP$9yMH8EiBed`emIVmFue&la&pmNkwI_45;}gqMyPO@&VgGD@p|`yDg7ys z<4%~4R_g`Fh=eOz6X6Wb2s?Sh0vL^%WNg30omm8>;6id{ev`xTnb*$;A5@wIoc=I= zOL#5zqqwXgiI%()KY~p<%{6Wk-UvC2S2y<8x5>}wf}dyy=cfi-@hIxAf`Uizwqe{} zy~)ywkFcN)uLa+w4@t=w)6@Rb0R3L?tdIR^cvFRJDA~$}>agPN#%=u|bQ{;iV=xgd zevq;7wyVME{o$G@#_yr=e+Y^v;piL1@#TrE#=d<6E~rxQsH4%uFAXk9@S_qa+i$J4W)~uPo#2~$_sddYM?~EA+HBsJ(;AaNZ7@m%GJsj z%518CR0UMsRQ**GRU1^t!FpG!PJ#0tQ(YmE`jhGssP9_UMpbcD1(j2&Ql%-MfEnK* z=ej1{?|GG9!I%%ye^8!0AeAB&AJ2N&*p6V`ZE@|yg4{;Y(d(1x@F?Y@XTBoNo*D9` zu%yTFC8d#Z6_l4p<2)P8`jbMYY_6OSTlqk_M(I>mS2b1jRrORYB(X}ZZm6!MZlms` zUZozRUa3w1H~y%0ky&-0bnM)k0-A=J0h*DT(VB7G4`>c*&TEo1cFlK9OibaJ&N0nn zy2MO~nHn=W=2*;)n7=XiV&vND+G5%s+J4%9xp(B=R69UBN;^S2TDwnsUwcV=L%Uvk zNPA3sS1ZZ(L3>5JN_$lMO6$=^X7gwtYR_peXn$)DYWHZ*Xs>JMYsYKb^81Qwb83@g z_Q%YLX%M6tV?pdLpQG@93GdWEi4blBx1bS`I(f7TIC&S0Swytzf91@eb5iOu!HIM3GzB zl4D|+a;|cZ@;&T;QdL{kh19TnsuwDQ>KDj=0d*a9Gj&sS4Rt&2_0(O}&G}eJEhVws zuez-|2sbTL-GOVKqijzmyB!64OGSRgCHU}I`4JfT8+0)>Bn9RT8EUa`%WJ_>Z!iT_ z6DNr-pbt)gUD|^O?lf3WE1>{Z_94gnE^x0dbj=SUow*A>z@7MqH-T&YpR0NfpK}M+ zGn%aVIALqfi?{SxoCYDg&Kj9h)PjVtIiSI}a5^c)HDG_cGQ%7L&5a;$rw?dyDo%xb zOeRUBJyn(VB9$gqmV;!r|77=I_iJ)If0R4rJrz4SLemwo%HkZO`(WfXs=}($s(~Dz zPgNIHcU5OpMiSO8ljVMjv*w6u3Fpli)lAh`lGVe?%dqlWl>L=uNEZ9X88cSV4o5&5 zivEA`>}8W*$0;|7zJsdFHPNzfa6CF1vR6o_Nh{E%A(F;QGg+Xr(w4MK43Jil)`p$h z2)5o&I)WseL*(e(0B5)G*z3v~la~{c<^gRV!EgPKM6c1ZwX&ZwKgsrDxc(`$j|||i z&f^HYk2~-X+{79D3cq=tf8;;p(Fy}+rcZ8`=TxW_HokTaHp44#P0xu6??O#Q9+K%i zs2PN~=Bkpc{#l+~(VR58p?K(KpknCE^INTWpm2w(+{QyGlrtzV@Bnh5& zB@FIyj{iHH4=r&y%!?96orIqj3v8^;1HmUVxR$n{1A8Y(<|Y)xed&Epr6H*R?GJ@< zueBigWKdSt)m&PVPLulbENlwrWoFupqCbwzt6m_>o6+0InGS})AiigwuE-I29LJcH zJfGU2tkYnf-v|pZWsYYGNkYS3S=fM;mFkNgyApl;ko0eh@2gS+Q2U8fIRP+hG&x3oCy+JKFv8xH&2c z(r}#(V^wsJOVg3G**s*#Za|Ydf@Y|AlEUmD?-xRg@XT*OlX)Ly<_0{jW3n5Tek@bw z8dT9?!9SdlkE6chV66#H{6we}btWI)Cn}ZoD!gUY9YxO7VXj9Fnf~=Ow-u%9X1DY+ z`SbtAQtnTq93pC0ivMMO#C6`f@(; z8`j{>=_8er%65%>wdLrQ^P*qQ7VU+_{l-*th%C5=uDnio zq5>KqH5{QqZl_Bqw<0r9?+0g`LvF+Ka}6)-EnKcUSw_69!RD4Gofib#c9{+gdjl&A2$RQ}a0`iqLgN5;$l@F+)cWhf?d(2z8ibpQRN z`^QEL&|B1<13AfRa)`M{zLy6490eB3TFfS8)7hEKrIKSqg8apm}0z3PG4xYmJ z#G0bYh!tley{rQHfW^c@{=X#ivX7tIi|@4>&X4S@nyZDA$*=fDcS9c(CgP|a9Knz1 z!5RhsnM?*$Q#^X_@c5S`3pJglx3XlGoMlJX4<0Ks!NI^T?=?za|lYq4j{JaY( zE7t=VWZX!>)YCxS^MYPvcJdAbcVEE2zebx(K^#U+!94OX`^_S8V0-8QXX4jTei9Gn zb5%M*GpLisIxWu7iAbD)Dd%jCR>>q~LrH&)gmurFFTrwnun>iN}#MG6df1Uv|XvaivXVx7Ub|+lA9n2E2|c35zrUO_`d#*FR`mGV{So z!e-Kw0)>y@aFq9rgMB#rS?pAyx|ESG0?}$VM5$V{Hhd`VvNNDNUp$1qaFR z+)bNeetc#->DGCJXXh>YtrEfM0YzXU&X#kuZ^!!X!3q}2*b3A5+S3c}AkjUWe(R#{ z_pSr9d`|(Xn$7J$(z)xnnz*Kd)W0VEf2nIX`Puc|Tiqd&*_)EOF$4A?5vEc>+u{;b zs6x7i*Rh}K?#m5x(~~{;bz1Kn=z?;QFrTT2e@zySB2W=EO(D>VDm0mnN7Z=&Md$^N z+&;Ko`;c%ql;gY?U9FQyDp|_&Iv%U?%& z(sowV8{|Qr3BAA{*qH(kD-mz?Y~ z;FVW+4*pO@T#=R7d!C2oy^c@*BYAhl@U@h}2iG)WCwSEfSo$k)zG6C0p0hLkP7-Jk zmB~}`wtmy5mV>WS+?&{e{zIry) z;7`3?Z=H z%oU@0M2?8h;iIeo+vyr;9(FR5%>gqolT@=LIEy@%f+Pf82}~z1>jcW|s&KR-8f6m5 zdF#(HlN)U1YsM8c8`t3-&V$p{8x$3~>)kIoo?JA7{z+j)f7>_o=GjGwr~^Wi2R zk52z6h~E=fiJ(v7&&lszMkn@t5YB&iwMTHYU8ZMVMz?WyQcs=)-JA)H!AHM@451F> z^Xw$wBog$eHxA;(92FAO8zVu(GCQ0r;+5KtTkgFeKUnid(B>mi8K~`spszXuOYaqm zaAH;wg@gq~rEqh1B}pp|uUVR)VEu2h?R9`Ud9IA9tgQ9K|cB zlop|lXhXJ}gtmwfud#!~vdd&8UqgSs2o!NO_{lyrPZLOlyTEszfxCVpFCjDWu{blV z228J2)?|N*oUJ}gCUh)>)6uB?NcWt};lQO-w>k&Z$5kS5|iS?V}K3;hDvzV&=%2}y-> z9NQhQ9Df|yomF7iM#32_fN$G{&f%f+r8Cu85H@@qZ7OG7zd0Y>uG}~&Cev@f#J!i^ z28TP%9m~~uE^PXFx&`jh<(lZpL%w4@TqZlbkMNQHA?Hz(@dNd%5w%eU{Sd3jz?(}R zUURx*b9mpe5>6yN|2y5V4c#T_Q<{t7;yCJrY|f`}jyWAKNM4M!KgFRm))r@bVx42{ zWlghevNW;evS=-z%%AW)EjEvV^DJzxWR8ZP&TiJ5^rlp9-)MxmOb^5plfxu6rY%mwMw6lz`OtdVuT*~Sq zst0#G#(L8F%9`8Ogsz$cwgyRgUY{_Q>mbGL!! z+^XD4!rFgxh4_8Pm?<`}CXYgwQiz6*rg)P|xc*`4$my!#YE36qZ`w5aGl?{S-=F4M z$b51gjmlR%F;`I+*ij(J>3`}Ad-@;Fp#vnV{dOx@@e6pedDLj88lt_}OMY4cU03UI z5hbw#dp$B-jupHEXdJraz3$z`_4lpUL%T&mG!~t>ZRGm2AFa#r3=7zBM|2nxx&}ru zVQj!D+zzBT(_!<6{(ud>L{?L&-$=eiRewIxu0=3}-T1sKxfzA=Jod%ux1ZYqoPtU^ zi0g9i7HEeqZ71HNRjA(92IMHhUXdW@3ADoxS)Lr*UwD#!an;|$ZFZnD>+XAgV;Wb< zW31Fcl-Et@fq3AZ>W%b{^3?RafNhkyr_lgW(j|9Ybrx}cb?j$e8Hy+C7QI{@?S1Tn z;i#M1>!V{CLt9sC`&j!;yTzWDd8aPkl(mytG#ZS(#<0l`?tMP!WI5(N6P0=MC!zY>Sk0lX&C+$Pe z1I1BSuczB0(@SL~)1(%zpuuo1`$JDi*!&Cz9SkM08(B?;Rbjf!Gf{UF4A?l_wGy%# zH}k5s3I20xgb%j;fAu=uf>@c<)_aIV)9M0$wgSmZs`rEc^MekQRFLkV^7qR)REq}syo8AR2Tn4 zE|Ng}hJDp~dMgX#iZH{oZ4+(;-x$SSqXYTddD$5}j!MhQHGc&Lc|GbpnThAvv;2&T z7v^TKF$msZ2prEr5)+=_^7g{9mLn^n7^rPFnA~FQO>2_l+=oWl?YJ-=phP#Kr}dH1 zq!fol`AOt1fm@*vIR-u1xlLyeHxur4DLcHQU`tm?)$oF1swJ^7umezot_97!E%`#O zzlNU1KA>fD(TWb_;}S5AL(#8Dss9%JG5SaJJJRfvN$d)Oc{;#OK14gCwbBq;^An_h zeI>8{Hp=sJB;zkealH(6Y63Yfx5!JGMq}qu8d#N5EC1>;srK)qUAUwq(!A0fQZxG1 zm)zEpt+Jj}?(S%{lX*;AVW1we3zdss@m#x!4D8v;3s1mRe_(g16bt}i7#dNY6q1E- z>2H`;(^0R*lS=m>Fcii;j#=1+XXYUD@kyNH{W;5bl9%(49gdFkJeK5uL7dTsKnZs3 zCxZskPAii3u@tt&jmNnO{QPUMJv$i!9oc91Wj=V!jQWTC~rVh{ZD|GyhJkHP9}lF=ubZ~1w3P2zQb1&aOdR|{6zz~ z49|2^=H6uTpE@!toy+Q8eb3{m7fK{qE948>Z)gUMJVx?jy-*yt46ad!c*h5lN|c+c z&lmPPr*O2K#UHhpiF^cD%bLKvKwqxo0|TQ->aWjrp)c3Ef0>cTz%MW1rw`-3yocxGObUi=|B4oXMp z9lf}(Y1Sza_VT>f^OJko?Y)PwdkliQ8Z31wdq5@JUCywF-BfBQZ@4RNoAKln?<3{y z0JpRBClrDEx1s7yrbDbb8h~b`>y+X(>q`UJzq~H}ISTjF`PB^-ZeM2Ct`VI{`KZt5 zC2`Y*LEDayUNaPrWo257a)uwVf2$R81dp>T*Rv-a!SnxxqqBja z_r9maaR&KVKTtTux*e_u?2%i$B3zfyM9p`0qb07DvxTz;^IH|PdwHCNo%5LC`jT-y zoS%5+{Nq&c^Z%i*EKPIRKkg~$Q`Me$l+k0@Rjc_MtGNDu^lHgJ=}E%JWpn`fSScr= zVY=a~i{E}F?EhN4^5Q$=ZvEzCj=? z*}P9!dj{cVF6NQZy7!o4O@Ge7&0iX>B4qy@eV|6%}K$rN@fTe*sT!fz)=4e=8n z;b%bx?5UGdc%G0 zCDrJbWE`KZ<-2p>L)O!;eg~bx16s?^N}l4SC=65bTT&PY#WL2JRU~-*i7pGXSdulU zBk28JxUs3U*lv<8roE{uT$zEDt_-WwaX5nNa4#jJ?~&cu3-5>>|6WT{59bOiu^y?S zW}=ZVBG^N|e}{-qxK-POlKcVZi43ja$gLE-j*ES1pbs;ph7^lTa(fos)Vu%we(tf~jdnuK63*vK;InyMfBA=L(bz|J8|(hoHeQ#=UpzQZ9AF`OIAALGIAPsaqPdxi}=+i22U%82R5Fx z{Ww%0Ysiw_09yFPANH5wdR>s!t_-@W2E2L=xo+>~T78ohTfjM9nOABse>Z_E!}Fjq zC?Y{*3+scO?5+Cv5o~nP)WKuCjO?RqI7(-dh+UZ#`4BA|ap-H-f*2PA#hr$q_qebS ze0gc|xBH*~YD^+kMRZfYgh?oEcHlqnjc-va^z$0t=3Huy_w)kmdQ+wl4_rql{7?hakk-rbdv^YQI&N8{9t@7Lyg^HCMGBQ2*V-psA=c{foP-6hA- z&BW1`d14s)q@J9wV^As?1vU_ayI=z`HNSMxjJ2U4sIP&-8F1{tjdsS5bs3}pW zvQoC&MHNT=*9ttpD0z)3Ty5NB2;4!NxDI#ME3QAi!7TRSdcFeAv5;%RDH4m~QOV^4 z|L=yTFhfuf=3xl8=%_Vtejf$dI7_zj*y8YSoCHZRu=;l z*$ZuE*E^EF$g3WYr@wbUD^7~H1F6`~3@H=LBC@m!`0JCLeaG*o^`H(pT4w`40+xWC zG_4_^*|WjYZj%{dC5ge$OgJdio%?z4PXk(=oUGcb;Dyt1CB@MH5$2rKuue~>U0^o2 zzKDsf8)-qy zKgsa7^ZWEkZLKS ze#&k}ASD_WEmUpkztUx_2v<-@6hLDUfx=@TeDXUI99^V024HF%(FWIpq=p2P5jE(D zUj;*}mw!R4v=%Paf|GYEeCr!{+V7+$oaZZ62XgIg8{qD|_!7ciSz3&oUV-ebz z9z2q5l1q|Tl6>^1bw$BDh~3{#bTSQK$)}+&J1G4v)yNvsGT#kf^9k8o*-M)BOgKA3 zvPS4yI>37OCvkcPZS%MJiBa?}7MD4tX6biQCTdEL^BbE-2T(m!l3Zd3o(mu69q)9$eCyo73O-h7MHqAu$^NzH;jXv{H>qx2dkN!H*hts&+e)vDV8N7yy5&f&A-Dr zKZ2jQ4c=~xc!zF5A5k8>cq+HWG?=B6neYpQz!VwJwRJq#)kPo$E&yuoe?8Y&36hyE5osOy==ypu9LOk1gC~MBj zBNR?J`2vboiXrqgPA7Z19lq=?idtxA#EP5n{?AG1PLTH`-Fl|HGMOgd$St`myF?dc zD_KohHCdAMA)eq9(uEwiotSGLu_NC`vsPakC#BH>+@CJABq7NsrlqG$MtjMl-o|4) zC^?4CbCF~u4VXvy`U}2)0^P}Z-p33{0rFKwlX7~6zPb;%%Iip5<7KYPy_j?Y`6lzx zR7^)xu|c|=$!Q~*r^(V+q_8}Ze&oEd@NTD(6#j#cugMDgLb6yIyK?iLKWMtsrFLmfnFz&(7$uJu{lPD(i0@ay4ICjmkH6k1{YREZCPQ7( zjo;B(R$n$)wgF|uZSM2YFZPt3l5Im9H9*z|4fuN==>WX>-I??(APjA3;Vc_{2Sw*f zaaHhz`=BZ}$%oI6daPvBMyBFByz}bF6%mCZo-un#!*@|;szR6Pma7=FGlgsh5$q4o z@|pCoy)c|_LDDw)_klJirIvH={xd_0d&n78wB_P zz6}HZ7#&13=u$cg{i3kjg;6bJXWITv(!ZH=b_i>)k4(`mxY^4HUf{fJP1eR|uI!44 z^JK3G!>7@o`&sj*(AMLD$>~8x$$OI2X8Sw%ZMggj3K;}JdGT&o%IGIV@DLxh1b|w4;KwGPjKQV#qpc&)_ z?ITGoKRI(HKo!ZUR`FlF(^K6qVO&vE+Q^YI!77wQ29ypOeeSG9A55qIOBWZrzl0^ z*C~*;x-{SEm<6Z98%0IECCPdoyh|Mt3A58;<7Bq_Ooz&4rmJgw7Nln+im&=3TQUU= zMdPp^M&=IlkCaI%iafxoXcR`0-ntV1`vQLN3Op3U$uimywU<2Ci+D+UqiC29>bDV| z>kJP0y|7jn*{g|ht(K#UFCN_SjZgyTRUY@oU>r|Pc$RBrZkzVS7CP^ZAfH;+n-ZdH ztk81d4lq*@N;<8eU*uyr&A5o?;14_4k)Oq*V;~c1DhY?nU?i`@CTe|VSk$|u5thOa z)rYxdAy`vCl-@^i38iF2!f{W6<6e$WC@)8$&R>Z0Xfg=(A9l#|gD;uPWblj2!K1%{ z=Z-)QbAnfb-AX`jXXuVhmoCaQ`owCJZBwdk^lc&gj`k z4Y{T~W}hfTsnP?jOc^{vHSoS{5{89U+2>ZqVbD#q7%xdK7}so~@3=^gGmQ;I^HN)= zXV)+j#ZFH0CDwoxSjc9YLjHGQ+M}Gi=*Tbg%y#7^OVMk=7c`MW1GtOh9K_}k*z}3pN7WdwH^e#^OUVC%9-?q}$#a7g& zwB5$N*A}NvtH?DjjhUbeip!`9)}80#rZXG@a#g1NpqpZS4lgvpwI zl{Cs7=_}IPrU#8*jV7GAvFX2!x4E4$o-)oh&LG+Hn^9-f8>vCy`<2p5rZ-L>o!%pT zO8SKKN$J~g@JdW&Oj=VYU1^Fo)iMn;tu)OuT`;{d6*sG4Y+INQnD3jD%!y{bIbiN% znQgga`C#!|23e0=pIGB;y=_ZvcWg!NE$r)Xv-|D!Nlv^*V|y*<2|ASQ&hl`|CtcrM zN?g#jdCgAXh4i_hquph2#j8C*_eWUP-7wH=QJ8eVeHcp{(>Yp`2I0_DljV5Lxe~W} z4`-ZH4jY zE?b);9lv;#Q{l8ZJdSoWI+b_kpxdbl9Zz#;pFiyU$*q8^2!C}X@AiJzeb)`wcl0la z!CWPH*GJOQc-wv1eG;_u8i;73`<+`1+S!^8g*90W{8c&?v~USCy?be*zlL9aFq~&* z%fL@^KC+{{$?0oF^V)OVUDwI!P4m@&f0<0X;!yMjN5Eu{lbV|3|ApS{DH*CqVD82R zRxk?`~Avh@&YLY>-eb-sBW5r36%&ugJ~?}ck&lTgY72s zm)GKeozCM*Bz2>RXQw-_+m0iCJ-llg-H~~n??}UR+i%$?*n8O<*ejDqV77g>$?U&u zCfh5Uk$1P0y*wV*IriiBU%b{qdmDJ#bk3CNsLAxsad6@$S8@34EAARNL9HC;-AT5# zdRMUjewtwd)gI4t&S1|yimSCeP&F&zd< zZwEEF$F=J_I;f${X$#SrtY-gl4_4_lX%o}IirRrRRYDn+BhredY6yF}Q{m<0M5@CZ zV8Fb=@1W$nScClF$y-4dtgIF@aqRW+KO|*#Cdl(;_~{|6EK(eJO~^4QPr`OCdLt_O zN|I60gpArKzX^``nokZYH<4Lw8^}S}FXnj+Aldv%AQ!knwxA_Y3+!zd$eRwwO(SB zbRp;FYII_B3!DirXuJ1G2gp{+zQ_df-0YHSqV29FFDQ4&^n4||JVo|Kc2IU!b^uj% zBUxqHb^6*9Kyl+}BiaTRITLS8FWj9iqDx07k&eG5t3BWmE$?gC?`%NDyn$WhE4&;L z(cfqikg+RR2`+pgS{EHFZ9y;5Lg_trE<$!1W)i(BKB1AbCw~X;>N~ADY$a@EHAdUUvP%6o(XVXc&rCij{ek z9h8f4d0bKkr~+@SYKE(7GA@v#ctTdIE~uX2syd+Bq1uJd>Vj&8YKE%2sF%_@K%k%K{H?Z45!r~a=U6PcaYO{NV!%yKzWL+t`}rIo+iPoE4hveWt1{r z8K;yh53#=#D9(^LIhtA8s=`Hlc*_qjv-+Q(zbfz zYu+n5gj4San(n`(64oQ@zdO3_u_T>OCzYudQ}SH#8q&@;;BPt#=Cn?{9PPIqpYjpZ z;JZMMT(I-w;FEtcl~*MdXq~_ak~$r1GF#*uW~67~QQ;7*U6YVD^nw&qb#NNfR&iSQ zgn?U}*QMCcRA;r8`7hC?xs_S98JOEV-v`or{-E4kgYqOmmSX|ba&5`B9g6~XJKUDl zpF1Fd1$YnsSe8BHIP?;OIH#;6@j2KhR)IgA$rN`2WZM%g%4^$@3GORNn9az`e#d;< zhM6-Fq(TW+oQfy7D>~|9u%19Gs#tj0_v7MR&*wIkgonmKL*^GnW%H=NfmNK2m? zUV>tOV|WKT#NFKAg+GHT{X)@Li58HaWD%_a>CT*ioXp+XxB}!N*ReOhZ96mA3a)EM z*hzVK>?vH;?vnzP+1$7bta}W%nOyZo(GGeGRD3;Oy9%>mWde&92vNt)heO|i+U*ri(40s8k$tZLzx-oTEW5*k)kA~b7X`&8aQo@_caPwp{|v6Ugoa8N zoG!DjR%6XfhD$2Nb@@7IZa>iLO>_|{*c)xbC;cN-ipSHE_v}FU2RerKFp1qcd&h%O zJ0fI}9m!kV&qSMTl6jctm;ezIqby}32&~CKM)6k;!_jToL z$-{b`*JsZ#((zD`d@VWo7d_x;zxwW=>v_w6i-OaSqX%p-ifbc#z;1!2fqqQNmjb78 zR$L4y>7n=+P4g43H??5r6VMwx4&~!a>A{+s1eZRFHRu-#e9LRdJf{1+e(sRS7|G0Mf&cB zfqu;=S+EnH7(3lRM%Iln(FI6Y^MaeLmQ17bpbF0F!jc%W4sN5pSw-$_ck#fiywFKp zo4SI4_a|3&oA?R{^;a?+r0lF)vdg{@H2C>{(nE~)mnbG8?MzIe4eh7s?}Qb$GE}i@k-9uiSG&r}^RKAxsXn7#tgfau<41m| zx~bZv>Yysg^G;RjmGhLXm46je6crUotiV;}HqgtXWPGlnFYq&yP!AAvJ-qjDk_4A? z#8d;V36oJUi7u<|zT`1cuZ^**90Nz zLdVuceESA;UY$q@P2$;~=E#kgb^*8DDUFgAs?zEX>hg<|wnk||a8k;6x%+Q!4G2d`B=g?Nw zPSJMJF4k_*-qB9gPQoqS1P^s>Z9Q!#e9w8fJ&idLQ#VE(W6=E7T-Ef}s&?X&-N+F&kk>7@%ArhRnz^a`%k2rd)f<%u$tL|*xt#kHlCMW_?@Vg-H1bO~ zDO)IeDgWcYkNFq-l)IJH@G8$@+FHd|XDg56SbnIyOTOt^?q`^fhGpeuU*q$WeEqia zC{xoZW~VpGWTl6^?8kieSNVg_>`Iri7Vl#nRYlH{fU=aTmuji15eeIMN!y;lIj{sz ze1Fv(&Wyo0(wnKKsy5@6@2k@A-oGX4xC#zmk?OPZH51%L9^Wgak$G*m^00C^F8NW) z89aw#O0yzGaaZ9`{DdRw2S0EI&uv?Hrs8tB{Ijet82TcvM#WLpZHoQ@`d67e2Z3ak z*h3=eKOovAg?C7CsX{Z!2AH51;1(@#UA%z1e~3z96m724!XHo>?4~bg9on}*Z~zSY z1zLU@p{SW0T7iaXD;Xen(ZiL&_fd43gHQRvxoHSf;58><=gE{|!>pwtNZM@(W zI9FybcnoQc*O?X9(rEK9I>}GrFx@&E!lU8bd%|jT#C=wY88<2%iHjx=c`pHc&e>70 z6%Om@-PO=xSd|vzc=mKP!wqOdQ{jb<3&-&D+30P_26L}NPx~2OJdp&8BQzoZ3uZr! zmYDIVhDXtm+zUPBT)w`M{QoU@pB`{5{h%LNg`O`Pe@O-kSc}g~qM2z8Hkj$Do}Y3t;crp}FTx}k z0x8TyM`2&|sQXruE|oiwgASp0=x;`Y7{vOYGo_FARYqknBf|pzn$K(Yd_#*KAj|F? zIF8hH%30j`pCi%UnLd)Wwg}rXYbWbplFoji{x_J;nC6?Bn69LEN{>u88lM^$8^;+N z7~_p)j6s9lpf?yW#4C+H!)L>DgUwLYIK+6AG`S3;EWI9>)PeMS>G@1WP1Q^-OiN6U zNm8qC?u$EMsd<!W+>$`I%4Sfg^CTd?CL!^gCDIxoqbr|PYpp=8 z+$8HP>oDtFu)a&yBnyG0iJ5r{n98cMkvMXh3O4pPjDQ!~PrPNAkpE7{VxhpA&DH~H3r94ZCP8Fn< zNDZWjQ|qNpO5K>cJN0bpkJS9S2D+TOI9+GmCEZ1OSVrmY>OSf6>HF$u>F4Ud=*y;! zN$Zl8R<7=?6K0 ze@wSbr^)fVXnIN_-aXS_lK^K(J#$I(a2{2PIm!|+7t3;XY_*)VTp|DOwZ&zLCkrqF zq_&}TA@9!+(gDj`n}OZdu^KHFi_ubuz8jO}3Hg_=EI;`dZ!MoKQW6DymfTjcwW4*Z zbt~`fb?YHob`<1kcD6ONtp~x1A?dE9eSrP6U1v|WC)!g;-mC9uMgL0y{@cMZ!!Ze* zu#lq`cwr7l6FOmHN#^@u|BBP#mi?3chW(y>xV@WwJ`RMPq;-z6ue2`%?cB`&FR(u& zjc<~@HZFy_V4xHDSjnEvUd5i%Uc~OO-L~xpk^W;n%5$1vjkUhDEawP*VBT(?ZZ?~` zn_^76(yOJ1jaQ7FjB&=BhR%j4!_2gzX%_u;{Xl(4cSJWy*GJb-S5=o!*IQRxC)RyT zO-}Wtw$}9`-?6r?wQia2kuFj{L4Q>5)W@aGNV}8fO)G2|YPdyrQ3MH*e~s1BXLD}d zP0wwrL+jB>(=!^2%9z{Zw&`ZxX?|%|SV~#S;^=8=sY7$o1d>MkT3T|J?YHdVm40rK z^SU?T^{sC$1D4*(T9nkrTvk8HhOMn5xrKRe5_v~rttW8?O|>+)G`8fnC^$w=;P3I8 z4x3t;ije2%OCOw`C;hH*o^cLcOKKXHwi?D5Vho$p`li)L>zGz7?U{ZgeM61({q$Y* zo%NOVGQCJ&hWmTnd)-f6cD;ofLILTI6ZB{FhxB{(|LOPW-{=kc()@-oX;;#2rn%Gd z85S7!bH2VdBpP-aOosOSwe!X#qZEvMO8VLKw;a!3 za!f9>Jhh}-!j=}+5nM3};(FR{J82WrBQ=tD`a2nky&O9oFTuAPf*9X$>YPqzoNEjo z;~RK;mf{Nf0%rW%Wg$N_L^h}m59JcqB)-3lW*j?c=^U`^InL%zIf5eTNH63{zg+a7?;};v~=mw6y*Kvy`BxhC^k`jG!aWLz~D2z{#aH?^MT|wtCvQ5vp zuHX^2g1{!xNMvzUc2~;siS0)Bb%)gDOpIDa7IRgw-#)l*+IvRffZBqmMDL05mPI?Y z6ld6J*tS!6N^;=mT$iy62J#0SHgzXH5j)XtxNf)8(6ybNXp&EZ%eD$y#yIp@MVKBs z_}kHgGYbUhACR3MOcZ0$Fb<|eXrR9y7C#rshM8*SqW);GpWdLa3-KN_^XKQ^Eo3LV zm7cMB{On}-!NGjJ4lN*Z|6kPDZP)`}><7}&Ko$!qfv8}O*I75Hg{@T9V zy4&B|&GshvsGrdDRG7Y~hfW!}!z<{S`h%WstlQ#ljEZ?H9=lSg?h?F*d7bjmFR>aA z^j7j4|Ao`nGdq=r&mV{8A(d>yx?uKa>C=|s0$NOdftD$#CDYFlP`?tipmsoGFe5yb z%ql~;4%*0$5vFh|&5SR|d-zWC-hVKHLs|73v(n#2^;$agffl$KXt}e#U15cl(YfI*By72(Y;Z_`9akCnaFr&-4L& zLEkS!N&au}7_*w2j-}3IeP2O^@dsD)6ucfYK(#C=-WoF3u7e*{W;KxLP*_BQ{-c;xn8T{7)F8oNbhqj#_WPBu&v$G7fR}i%M zESXQ8P_|3}=`JTz$mAscB%-MKE=?ivurPUtV`al-%VqmyH`%{Mfs&7uF96xSCch~U z$P3Xd(S^Up`YWQ{19=;t$HRphBU{4{qF=eN=r~6mlwuD(|rmT1FF4 zYq}rKvEz%R;~}4_8aw|TpwmxP5o)12mJBDKYKZ!(Izr=6?^K^x>(%)+%}B4BtU0GS z%Wm+ZX20gKMjKNnMiY~y`JlO}F;QcziqUI4n%kP^+*i=}uuF4Vb4Ale6RlBc>T9}b zdTJ_YVl^c+tu?jyZG|<(G^I65_^tKnh?uH5r1_=!s=2S3q*MiQtHTV>mk@igff{ud_F}FP>>a}WVA|kXa_h)_wjxb@6yCuNdI_1 z9#&oW?O$X;H5OJu=^H`jUmB?ruhBW0$Pz3pOhPH^ifTwI;5qn2UP+dsJGG4QeAX7`g3$X(r0=|+k>-rLZ>y449otg zlQMJHUc)s6q%u^7wPn+JzlZXUuI8P8LDRuKcI1a(8h+A)P()sVW8xj(ZHvySyv!~= zE1d~uPzps>8XB`Aq-2$%y=Xj$xLW*~Zi7hC98l5kBoNgg>+v(KNdl1kR%jxkVUhNr zRj$ifSrb%W9PtVy_e1z8i0*8BB%_(c?|~hDL``iByU95UhO0%yau&})@wyPrTmlUJ zPFzt^*!I8llbwP27Nd;#A4%r{*3|vBVOD^Uz1b+BGTmG2Zrxj}*0$DBt$VL(t=qb_ z?pgQf-&yzGd*A{D2_cL?0%5NZ624o$D_0;ufF$SSoZtJt&vQRL=!(vvtNV}ZVc3;N zsSYo=>dDXyb(1k&pGgPzJiJ4v+Z5hSW;(3ykhtcdIoFe~GLEfLNnbyZ=Wqrdk{Er< zU|^gB4Ay`cjsU?DkBs=dK4wt!yI052OvvlDdNygd`h`51^(ybw;w zWOOy59$!;u&N9mw2S=tC`sNxO*+uvkgs4|M-F?XpaJa>w^XQ8jdIH+$Q}ooYyMN(# z&d~vWf}7zPkM|jBo}Hw#O4`OduFdOJBD1jIH*2A6T?^fRsmCsWZFdo#}TE$puLr&U++H6-!zXw`l=mfJZu?O+--0K&?$ zs$(!ttqj(E;5YwVn=-lwa>OGzr$x9NzK6))pXv5i_{SC!i{v;dCZR#U%JUZC%OA;k z>I!K^H+klbYjxSznYh$*X~DcW-zAE7*&Tczv+3G@C5gtk??w$c5hh-WM{~M`HG+0@ z0^g%U?akbCzgMQ03?*tWddjD~3%osr4sSd2PBR|QWUkL~a-N|t_%C{BCGV>us8`ov zUPjY{eL}B#J@nz9Nx5p}842gNNT7o=oCx(dhpQJ;xG=uo5l`#{`i`%8=ic#j#l`s( z1kn?8A6>}>?%*Ac9yJP0>2zE;3%n2UI37aM`pPma@b1Y`v8m8YsNh)ENmvKX<_{b% zXQ3S4#If?C&_=@(w3$G3+*_ZG=Ex`yZJycN>R%b^dL!pGM1?sA!+z9q@1v$QQF!yxI8oa=T< z$I*SfCT%TK$n?@z^gLbY@BS(qBn#p^Gg>xJHeS|PmMyK3dcZX-mu{mM{5SL3ZPI$u z-_dbf#fRzLSw%0%@M$B;peJ91N;g1w3rEsR^mTKX6g+@u=;nEZytO`p3XXx(I1Wt4 z#}(*d<>=sq{~ClYP)=sqT4r_^d7r-Y$l*9Ssg@lV>NyofeJZ}*E*v4BdsTXc(L3Jj zecM|h^rt`ghj4}PrBLlt*XKLPX~&pSCqVVniM%03DfGHu zi)Aned()x*+W*Xvcrgta!?}MxlYoVEF6Tk}`~vH9n{+SJ>c!H*%neHET`ps-&_g1W zJQbf6ZxsiKZ@@9TOGkM=^tCdkCL&=T6O6s2(+ z5=)p%^pfn8c+=%x&&p;9Q2uyku@NBeRdT(n|Kdy^_96-!H>5+<*$8xwxKK#H*wc*N7^Z%9wBm z6!AGj6fJJRO0hm&_j%&|tQHekr#*pmc$F1m4Qn+|iA}6vl@^6Zz)eyk)=IQ=jq6H6 zBz}?t@q1QxSGbVGANt_O zBsV2R5?^T@XJ_$NhLj1+v#?ZhCuw((^LPCuDVbvp^x5GT2~q_ z6|xT%FjH}nS|kVQ=Zht8p+RmH_Y{|i4l|RfhMV^rJ@30DYfkWfi+lZhuX^l3-kz&) zt4;B^2XCvXdp_F#7H%o@y4%4y$>2<&$CcHCwM{U6?^) z@*%T4&flsB`P0fV}VF$Gm)uV5;B}>pX*CGP#j&S?7O%q#Qz$(=V(y4!{fC z$*SZG4n!Bo<>4IJj-VlVLoS;U*F#_Un;nHeGeca;T5kv0qUVKcnBT<->-mgAbCH5N z{vtm;fa`FGu%%GNvr8nu=_iiADPA8q_73Pt2`k#g z?@=PJVS-zLS8OIe&N$8p6J47#y&sA~QS5pj6=HpQ|5MQ|wt-&rFEjT~tji3pN>s3Q znGN*8+joQX`BHSut#En_XO%L8HP09KUi9hv;K6y~VPK7sNEcd7xBdXM_ce6rOW{^7 zKxJ{u)8W~W71#q9vR2sb2jB*nn4k8_2Acv+`8j;tu_%4IGAr#{s}7n0UpHTpEfK)Y-AdBl zchU+;jieWh;02t&qNFmCUcVlTEgZStFiWb0mAZy^d~-ODr$ik@^_VhF^U1~?a*$Np zKyQQBB$(eP;2^#yFYY(IL*r0`9EP`d0zK>q$csYAsIRDjm%}>P%M?8eCqj+uEf@!x zu7mIfw!n!n3H3-gguzqnBLOId4>Gf9z+S7yYc&f$Zh==cb;%XZ@q^H3twUwBTbM+x z)1LZe43+CLdC1k>tBX0Qw4!)_sqb_>VEBjQJJ zb-g9Y;_b|3pK{DQDwaT$eF4St7QZ{6ZPP$h=Cgpa-b?n83#W*UyGFH=%Swq)@E%^(C zWnXkP*Wi6z<}CaY4MA^M6&-M4{e{czC0@5@=-R$Voxg^P@Pb#4*AQH0OTEW4tE=+v z#yh<(83ZTsxkPeI`wbnL0B-3d7^X+y=yv2RyjJ{1+<>X%X{NEN41=Ly-V~ z`;Ow4B1;hoQG2zrx3Wah272}#ab8k|Phr8WZ*-AMVc6VE)KxyOeHdCf6)+&Bc{Hz$G_(@S;aZcWh zZp0ngC|bpg}N2bv~cu?!D7DJ1CP3_ehwpmXq)on2R6G#~hWc~by=K-`K zZ#@S>q1{JayM=XlrB_#0<-c=wIZvkFUGJCBXX^>Oqe|(5E~OU~>V+KTZVE4RZV~xN zaaFa(T{VxX{t7s*WATXnM)#-*UOF=?A1`Rn37n5+I?}r0da?cEuT90)r@+Nn8}X!&mh0-k~}D>K4kt^|1iT z$gj2Bxz&*CdZ1DrO`^+ADy!*qNjE^>`#^Vg3SGdGs@8TJl~qUjuRC$|{=wx06I)ev zC=UCfq?qqVTUSH2dXS?&Q^GDd$J;X{9M8vTxL?;mul&o=7u{VM_xlVp;%{_Y-K!PV zpX{6Ly?C6@t7cSH!5sR==0-03#>)DY56FNKSA0gFwW54DG_)6F1obF$mR6Q3QE)$j zI5`%r@N76uD@vC@%cxJURb2X<6t7g+XtztQl1=t!$&iwcOkQ3UZz}Fs9LVKu(ea`^ zq#JcD@+aTOUiiN7apA+lQ-ueZrov$75ayaw37wf3Jw&EE%>QmP(j~< z&ddt!`BHMF8WjW-RORR9`xLy+zny;;!r1xzBYZx{tZxHZO{bYN7Fv^;1i0jT=F9W% zTi03_Svy+WTWj)K^0|ezt2Hn0Xx>$n_KA6ynWbFJyO*~L)&1nWWqJSQo#Ixvys>%P z@)qXx%Im;&a9-!UZ}VdF#Ca}xpK}+Io0V&M%*=C@CDd}96s!L+!5Ed}k~1m0z;xU+ z&6L56a8G8d%y$`+GA^dKN>4VPGj=eZPSd2_FibIo8E&U8P7O&-(J#?A)tgi9rJPE+ znsO`UWQscF^_P+_p(#C5hNW~(iB3_be3P;^Wo63C6ibRi|E<2C{xAIv{aL+1pQg{# zN2U%+-In?>wJ23+kQsU!CK{F+P8l8>Dhxhperf538bd_dfV8=3f26J9w3caY)0_sgVWy#x;aTeB)W)eV_0#nV{o#}eDPO+K{v!OcA$eZ%;$&^| zt|X77ZJ)<|ZvWYycqH++#L0=B6I~M5CrnTHJK+|WKNFtEKaJ0i?~o9Xa3%hy_!IHZ zh9FJsU1^eQ%m%E{WJZq`d0b~eYn1%K0Ddzlo2h^zZa8sV!5Lsgl&F)Jdtor*2KXkh(T?SL&P88(b1o zZ>N^0CZyiu^TyQksV`CsQcF^;sRBc|p&gH^xuKz9oMDaOdqWe$Plg{1zZ+&7{xUo@ zd@|?_?rD)}?bBMNg>W6kc0ZbSH_eeIHO8_Z4rRMvFg`L?8&ix<~D>L?GI5P?|K4(}mY#H4%=VTtw+@5(l(~{XJDpXLU1;H%&DCWm;ewV*1&%pPafHlN?;5G+UM3kes@Hd=AKNo-N4! zXu4o}V#+f;Gi@}5nNDWS%G#7QA**p#T;}}D=uCaa&5Rit4Kl8!4@u85PBjJ@ZE00$ zFVg-=D=_?MDC2eblzJ?6R_e0U=+rd*WBqRZCH*7)J$=0Xvi_?63b*T~woi>o9l@5F zn7Tf-Fg2Ff=9nSZP-ciqTatDr&BNHsIN12N@upFg{(btcbW6G_BQ|4p#=4B;4A;zd znaeZpW|n1!X0^^5mvtvAKTFMge~Rg@>9R?d9g{sedrS7sY)AH}oFh5r9B*?J*?OnU z>1MSh#4_Hp!?MP5*|N`a$s*0|kUK56U2bS@aBezzd*9~fTkbOvZfW`1@{6UXrJ==Q zzGCiVzMHc%=ZBp3IbCwB**p3BF4>}Nd3J{Bq^YIJ*K`~8!4h-^JF?DX{hZY@D<(^p zwJhsl*6XZuS;KkN&Sk}CmGSE9vt*`FQycuN+_j&`|Ia-ZTgI+t6K z+lC`$abA#h0xqHeX3X>QZ{%C^Cov-q#Km;6P=LZ9t0R<@i9Om zjxK9f_A|srXPJtWHV6FKN;1bv$ze1=aWq1uy+RJz7Q8D9YLgQ^;8ljgd2C-cvTSkL zpV0p@;VXJTz-Utb4Su7!TB4w8&KCDg_-pQb$eY1Zey4bc0`$UheX>D5~yy&7ak7jrWh8k1%=rf>ee@C zrjlKI!X7zK?{*Ywa5-5?2kFMI!-cpU(oGvYoIB~$k8&UD_73MvXBgwX+*aZQ{DOP< z3V8>!Ac-ZSdeb;3;x=yX2t%vwTRqoqLUWvlk7y*SWIev-9q^ilLmSM1e|ClJ;viVL z-&C!Jn(fX#rdHi!&N!TPO{skhYlbt>#7^Mw*@FKe$MK#_uf?Ry3sE@*&_%w;>@yRN z++uY5R_Fo&uxM9OOT|DA>WiE7HVz0kCIlN{wXdcA$oGgOrQ#n!6cx%0^uy`o%umFd zU+ASJ<9#M%z6WqBR*~MZPB@QwRx{GY8^Nu}!lVC=e(GMlFDg9$x4p)B)uqxo$a-=F zj^7}>zcWycx(iAn8?I&6<>66`hddCDxhE5@muTo4Qt@v_XSat`mFFHt9*Y2;;Q~^T z0-y^bT0TnyEgdey?t+S)w_ianZ(V4{P6QX=Lh}=^E<#>q2x|otrLBo2J!kty(YL3x4{$ zc7}GOwy`!)8?CL-eAJxQ9M!DRtk=xctklfYY}M@7Bx{~&K50rcb-7OiZAa}y?I!JM z?LBRRHcMNe4c2wgHP!Xdb=OVRZPwk?CF{I>LwuX~cJm$S+rxK=?`Yo(zAJsF`hM^G zo9~~#2YnCtuJ;|!^|)`U?>pakUxTmMFVgQjKeeC8Z@AxizkELz|71UdpTd8d|3d#0 z{t5m@e^Ef4fVKgv0&WB-0=owG4jdY|ATTMgOVH$?mO-t8ItTq7^f<^6^eLzyXh87F z;Q7G;!NI{ZgHHz^3_cXxD>$0#rQl=14}y0EuMIvD{BQ8;;1R*IgZBk*3Z5AJWAISE zQxRMhd@cCz;OD{F!F@w|glIydLMDc63fUYoD`a-aKpn8j|Gr;uzg*uq-*vu~y1#TPol!eT zyH}H|exZJ%{#QLseNi<{rBwNvNm1YNViP4U3W&8p-a-$@onUL!uOuJNQrczv2JM-#ws9Kp@ux0hWNW0A*mmz~2Jb1>OsM z6&N2_9r%IQR}d5xG(5;RC^0Z0&=y!4_%5(zU`oK_08>Csz_ozbfCT@={)7G7`M>x3 z*6)_@7T;feGj$7fUOI2x32lGv15Hm&w)&>}w7R1@Q*~dpS@ny`L2Y?Ld5UDG67H&Gfs%D^=;|>)+k~u>XGl^Zsf6 zo&k*mqya7gcl|H;|Brogs(*-ooZk+=L+rJMJnxpidfk8Q|25iW+5uXH_K{|W#-Tp0 zKFN0LsTI=H z{e`PYV^kZ|Z`3>0f2c>R`>B6Y`>KWNx2lb*ANgBl%1_D-%3jKFWwv6cqQ7FeVw6Im zu*x4Y_y35lV7L4v*?vPi>TPwyZ4k*&O2$gk|E@W$sW}hoi7AoCU zvFvkAR1%e#{nAzSl1{-;WlyraS}MyFcNJ3=-zn6JYWn!=&>gtQKheMFPG6fgi%cZD zj(4FgQ=t)bVIrg&X;1oWN@*HhHW#T@noYN4KW>0)=p|-Lx-;|9i>JbEG0=w^M{?2? z`jt|jhe99WMsK-{mRZ6&c)w2F`Q1>H0Ho z6gh6fvRnkEY9@aFML1u7fsMGAiq=-$hSY^PM@WmAGl>pU0pU*Pt06hAZw{ zX4|Kr2Yx^WVZ>V&hwdewvxNt0f>}(f8$rEsqUZK!uKq8R@ZX>kwstRvCa{^aT0A*J z^<9gg_HRR5@C=4$ry3iR%&#=lMJRRi(Nxc^j)l*d0~sQOen5VernWEe2;Tf|&al&K za}U;6U8+hzb(C7=YPVNK;JsU9-$CL*j=gQIf9_g!9-8cLQDKk9Gj{?t@I@T97G9OU zxc4Tb#ZGdnYU-h8`1<;MB_MgYc)PTL&UKP}z-G*Q|HjJ{PM+gHCc~a+0}k>`HFUjK zF%^#xOvGuCLsnX6e1t|%H?LUE#1H7_rFqr&{*5#7Av*qJn5QnGH?s}m(PF;t?fosC zMhE@-D@>!V^BeQM8hWYFCg{AxUIx$4Os_U_R-TSuaS=|S?j$#jgw)X*mT((r9sT*q zXdH}_nWWCa{c+RtBU}!#mzs%33%r^0@fq!?opL>7s_NwKU6^uPD}WYBvJB6CPeg3?UUab|&MMEjVA zy%E`%397|*kuQ^~Nb-UNFo{;6Us^^7Y=ih5nSk%f=(>-GC`!^8FRPuoSxd=KoEQCY zVoZ@dq5~d>-{Fy@N>ZP9iU*DdlO&Y)&;`k1JQ)Ad?eD|;#lc@tOHPX0h%=c+&KLQJ zPWp`Wd5Z)1C)PCcNw}**Q{&<_#j^mHz+{+Tg&ylDd$+m&3bW-PUIR5*TRUOqRpD!U z2La+w6e@jPzU9){C5Ft_Htd1n=vF#&nFCp32D9@mP$bG+S}-M9!~`gg)z}ADAO2D& z^fB9DKpaII5`cT53khKH4jw z@ouuOLt`+Js|td@y{f)_n!N|?^LSJik?>c1;J!Yi)8=cx0>^i0)!nKW$#`m|A zWUyX%@3z-Gt!a&_XfE%tS1#^ofu`VIUs>z>ta43(zH${MP%ey7qw7;#p=%%^hQm_% z01J4(%kS`8Dr(;IUfn>4u@l~e^APxYI9<@3+=2{r-m%ru!l83yp_ceq{f=YdpS%k5 zAieg7xD*W+B?`@-y85mC4Q!?(Xaw(w-Pa9ncdb(P_n z4@D2snE&-FUf-$cLL&G-5%fLh@SpRl&O$)Q!)tOB73EkQj*HQT1fVz#f|Ae%n$kAg z5_pFebf~-F(|%3U{cO9)=WVcZ)KzXMLvNEq<3ZYPwe2oB9t&-!P_2HloglSi2XvX2 zl|3sf;Hn2#zCr=J7%E~z6uHW>laL?QpyYjpN_#y@@vx%9=*U|atje#K@0y=z{l}`a z{+8#PXUaW`E?beS%~j=ovYfTduq?NHvUud4u>5TqU^!{IWw~c@%bkD%|3PkMu7cnA zE^lVu={yx2jS*zn@34yUd*uI`KRSO!{*(Mx^ddVHOe@%1aIfGNAN2*+f?#yUYw2iS z!n5!%ea*ze+`_U#UpOXR;Rsb@|ld3&od;-@iIqqhO zInewII_=KpAoDQuBzho6%ooi6nRCrSJcikp&2&LtSyZ{bb34&ZxDTsjLEdgWAz67L z)~!~vH8j69dUbQYpg>yCwP0~Uae=z9f8q4PW8~Zi!AS8faumw&q{xdxi^>Zt3zbDF zg+)+G))bDWODrsmEBKzwiY&Uni}GXgGp)z1eXQNAb*vBa{>+<*3cr1xSKh1K^SLK- zi*s|>qPugK@+u|fzQbK1$kVcI`>=J_=e^Afv(B*oW!;B{e6RJj)ne7=hvr+YzWELC zK}ho3KxY}0|2>%&;(TF#bpAm0f%^F-Yo7IkwZiJ1UuG5Km}rvUkXshAF@DWIk^dQA z#Mb=l`L_IS1*;1-7i=teL${==proK7)S6rPS6*_tTWBpDUbGM$a|BA}1w~6xHUC^R znH-EyMW;!`7*%wjsGz6}-Sgz4`Ot9o;p*62bdb-N*qi1T^)Bkh@0=@&<8KTp>R;qn z6jk(1QH!FsMFWdGi;RVP3$GN$6+SJTQP{T7yD+a{8J+Z|1s(-&^Z&>X&#$sxu^!+x zGvD9o4f#Z1kqpYxi|oze8tblbF!-u4(%6Z(4%Oi5YK z=|U+?{Y|Y+(Wa^_ooT3P7Wc_Cg|dIG!tIj8el$Mkcuq}DL$j9s!))%rJMMrb%@V?P zxX+fT%#F@lnfEy_KQGkUiO2HH8k9ed$F@5E4v8V{ai;8rPE%3Py>J{3lB>LG?RX~~ z<`pnQGpQ*GEcQbgKBjmE@20KAABu~(J}N$5ypM^)1isf5`bo!PA=>a=MRSSI%r`3k*=Areewb*L6wq@I@^Bs6$ z8p6-{o_v(0`G?uIU-IMgo%t;}2JB)BC*XOBVhKY6|dpMNiQld z(iAr?UW1xFw>YF^c*)k1vn7coO~}X?UAnpSZfRj@E3!vUlr<_JPd?M3@|yB7WJOe0 zw5dE&X|AkGcV;)#m07G3TXOV0VlT7*RDG#-Rk#X&*aK+v%OC?MI$P6aT3quh4!D(M zcrJy%z8_x6S_t#oxZSVj8$4&ynmjy^S8>oUb}oneJ_kSBM!Hs;`5U{*zPQBC?&D%J zJ3FHr-$9mBYKC>wLwQ)~+7_Nv9|+CUA^9z0 z)pC$k_j@;;duc66yPkWb`(PMklW91Exo4U7_amaz%HqSZ~ZR%s@BW{+oSs$#?<=-vFb?>86OBOP!lf;{}Y;o0Y2p@ z&}OidOegrw+0s^&9tT$eMGb|bHOIP{W^sM!{Y z7qcGBMbAEfuS%HIcOY@oO;pM6HbIxZ9W_Q5;Z?LSPrMe9Gky^z*9+3uS3_7n2IJ0* z>UAEx@Ovcx-QtyK;-+Wqm4X^Y0&VLd+7wr}?qsL_&d;usoaBibE4Pp`884= zTeBv=AdoU=9qG9louQZ4K-SoQLAWe|A~OzR_a4+CF>-+aKzt`BrziwQSC{kpP7q)Lbr4e zWlA%2^3Bl5i6kFUaw){uME68t5NR%v^Xcsq$6C9SP>qgfC)>q@+k6Fc)nc@6r#<(f zWNYd9RXG_>H?18Cw-kXVv(|BZw=b*z5$I{QdamW3$31iCvIUUT5#rS!wbQiP?D4CR zY_h!!Z1Y5Vfl}7}p57&1N|bILc-AAS0w#IyK;8Zr&gC(f0Um4#Uo=Pu@Cpm#<0M6Y&8!#hqA>UuSjRh-v>Hs8yayA4%=>;zpo!{1H{k2H7e!Z97S& z9wuu|igQaej~}H*sa~3nQspKJm)>~c9jx>ZN`_MncuBI+FWg42F`K`b@=(TTS7Y2(w2YN8`lgo{2R zs+?$1jZXv7x1wJ~yYcxOL|@2E-6gt?QzA^<63T9&$cf{gyzkY}dySA27NRIQ zLRGW@m4=wdpb_Q!+(M_{iv4LauWAP$cle|OQ93r~k@%vr8N+)?5x(%K$|x~$+dgAc^^Xu>P4q^o~NJZZb1<$@Bz?lr@F7iw>{X^gGyjC z^~>^_IC3>C)VeCirRuftFdpGOvaqU5!M(h%>JJzi?p38E)O`y@x-%T;3R{uQyJ|2L zL05P+L+oSitL=#}Xu6Z@xBx2s`)WsZFBs4pNr*U2?w`rgmF&9N&J?E@$8Q?G-Y^{1 zQ*r6c;+T0H+SGO`=$x8?E=@`3_#4LG*QB%%2{iZfvdH z+a5=^eKKq1BHKH%>;8aty~MWDwv^1euC{3O=0DrEkSd{tgYCz>;$PcM+cUCD-s0$b zVT-e++7xI7rm}+fqoZ()%lfJlT-Tu4{;Ji!fSMo#uU0!=%h{;V=imU}ftui=-5bJK z1O)nC)yHeop90A_S_5M%+}Q{p@+oIiYLqkhBAUbg{@~()+U^Ic$$Zx+lvA@vY)g0R zfRA7g4(*ThP2$}@quE=I6MjDPiKgyu&?`RDh53hj*K@mxBfO)lne9A*T;>waPjz9c z6g%H@4(f^yTuq8cAzYwa^f#U}W7&>NYA@<|r$fgqL+f=Aq^9NB$4I(ik%#n_k|1@m<`09tar502##_v7} z;{H!$q&&pkFqYks3`TV!%AtBw){h+ zvMpj2J1~DLT8Z=duk-Kaf544>6ko9jSLn3D^M&f721QMaVpv;hi$ihO4J!V(SW)6v z(y(MGb=E#o?j*4GLh&ySAQ@#5-pTc)`$|vapfr{ymd2MRl&Wh}(`MtmODU^c9u4hh zEOf=y(CyD~Nq`sFyrNCTK+;n>!B<>Vv7UUA85Ike-gT`gF1Ntt`&53JRFa)=236%| zh;%d1pIpW(xu$ebX%#uBvr1Z*1eKgG?o#Yh{JH2M%95s34B3Tg@E~8XV*I^u6sxfX zI9g{HE@IVpn2&S#YGvWQ!a$tRF(lxPz|niX=m}IjVexD{z&okR9v6#o4!f4LDCtTb z*_M)jOE%!#d{81S4I+!~SF|tNc&@igua;h5!Wqe&atoPo|CD)=0Cy9Irlq_`#Xl8! zWUO|p+*g@hIRIYYYg?Xe0JZp6Ezbbn@h_m8j-3?ctT z$8<5!dCyTm_cNZPo@4AUv(WRjcKAC!@>>%j1@$ItqXx2|5#qhket~q<({uwK<5~Pb zr{pf&q}Q+q-O0Iajo)MmGt9Mg6AGDRn%UQdD1`b$b=XO#ay|+ z_;1G@8|XuP{lA+b`t5?ApTfJsjT9QKv&zu{>e(RYR#uDC`S=khWGgy2eQ~NRAwBhR zjlCw6Zf`eySEK279H$$V$&549H5y)NQ}W2hvwEEFx{h3k+bEO|@#!X%ed42rZp6~^ zSLHpKM9;6-Oj6xTs8+KouT_4mOn`@*SLtUP06#H}qjelvbzyX4j-UrCsxnjw==%ns z6&nwS@F||tcdYMCb^%A_mERWOIa^d-%V_S~kIi7|&Ii4<(#r~gtkiDC|hQ5x8PW0F+RaGui z;H9L`HNcmvAiel8+}^2V`&=VCYk%cRIKJmmhD{~gt~bwVDp^OrkeT@_)ybyHB{*qk z@EadtMBe98R9VNSv*p75)!15+wq+$**2e92Oq&PM$Jx%=>opX~6k9dPhEr-gb4xf| zeTD)2qy12A=Io5>7aWxfszW&k2sjE)hTxJ;(&~P^dsQ|4TsFa;t#%ngy2xW!iCYKG zz|%RI`e+{y_HHMqDFm z{A$#sy-cEhnoK8s6KjCfnuciV!yv(Y<1!Gc-)d-Ee~>e8mLj zHaa-YMksE1_-UHc+5bv~`5In_o*9K+pW<0$cp>8jO;-4o7$9=LI zJ?1)k@(WS+-uL|CDWi{XuBBj~^L)rO{EX)c`u9%mokJ)1pkRU^)nf^1e}#ClCc4*i z&)}7B>y}16_!H@Fv*4wDqTZa#tVUUL7Jrunviv6KZL>+V>Ex*6aBwF6k367La8IkM zJy0eF; zbt)DzVN+&FO8k~ZIxW*3R8avB%C(fXgO#SZSnmGkEYAQ32*7%J5s2l?%VUnhjI@E?bp5$B4%@a+gCQXDug%uUM0L6QnQK9BHTJrm!gB74In4rTi@alE*Q z`b;hH#u4MiUn|7}&uPF3cQ z!zL77)i`FDCvfPbNiU*l4neowM>b1#R(4soOLkOtNVY}xMfMt<_Hx;DD!+}gO|t#? zx=-S1+KKM`5Z5KL)3W=hw`ZaCz9!3)IdFXo^^ay-9S8%4};b%Ng{zX5$aqhwpl+%lG-Q1!3@dn?uiYjh4v)W-OR4$Q9a+Yfa zq**tr&(`c4i*bki34?YhK9tL%1ggOkj@@vk`RAB$7OpTw`krDByN zkgZfGF00LTNoT8GgyXdsSJ!O({k_Ct;ylqgd`&TU9^O#V{^(P$wlWk6ualSA#5<11 zI)d8&0mqmf?7Q*IzPby_J>ofPH74=e0xNYN4xs+-oj9B8V3=lcCcne^{%0squ5Mlh{W%fnISv&slylKBy5Kznvr*U81s^#PMhX9H#M99%DHFHy~qt z4qr`U#qxK}O*o%7VYnMfs)%CUG6LVYt7{9mo)cJ`7+pu>6#nAYmUsMZ_iXnDFf|WA z+VmlrrMX~5ZBk5yRd45lMA83N!tcMFQ8Quk?z6IawjqZLJG|&|!Ma<(?C;VcaV1G`*)3F0i=n>q}Ze*d2fp zj{7G)k9eNvxS!$KiHYk;=!|dZGSz_|P!|WyHvC3DLKopX=B*Czfx@|XRTn{icqo+k zTo-;4779b@11zWe(3)e+b03FK6y1j`pFE$=wb?(5uv&K8+nYpH6DtU@`>_Ms3G>F ziD)h_l9^=5vJ~zgEPE!M2zze3v=<$$C6aC&g$%f9g2hkidc7c@C<^~m0lmI!_ycrw zPqKyo!=G~qzwJi4gp2S_{UqEkG|(vwqPJBpY=f)j3jLbHxJcX6`@4*D(JmT}!_dJ# zuf_qokj_~OzNCgICD!2+O_df)qwuX9kXhkF4Ch^a6NiVFVxVHFB3_|WPKOe>Re4S+ zR1HuqSDjJitNN(-sxPX`)U7qYXl9|(+NXJ{Nz&wLtQx0As}*TKX}q->ZLqeHR;3ka zeY8%EMe|vcrMaXzqB*3Qs_CKWs1a)l)QPB(=BvA?N2u4R7jS8+uB#rT{y|+w?V|py ziswJQP+e7RLW#6ebwKq%wM#WlwOBPtwSz~|169j6s*$R`s!&xt3X*bV9$J!gFkFQ|L;y8QK%tKhgfwXma^GXHdFLK;_n2Q&(eE zTh!_5d+NjLh3e_(Y+9;=)fK98)k4*5RV$ud8y=ajN`+1$ZX(8Uxh z{)OVWRWVZ0Rnbe)SRqi{maky{`UT!bEX0pO*=<>>?2PPp+(m!LCc=SyNPn<9ZlIR3 zWwLQ}3H@cR9KCBe&xGQ6Yay$WzQqlD1_zN1|5{J>?#9wsxQ-9$tRCiEx|#j6LYzSl z_8TbGFT}^`AKt~Ux&zGlA+O+ReD!$N0AfTh}(@I9H=0Elu|Q1uW=*KaTlo^Jt6*8 zk!yPy>cAW_Z=;<~`ZBK^FX%PRL*v_$=k*OKx$Wo}9bxvqs=80LjO_Cke9YnT8_))? zfpK(+KI3+ni*xOR?P91%G6+ZiGCBXPY7)MlKG5c!HZ3_qjhSWx?6-wid6M8B02Nq3 zI*iFy!L^zMff(*N4;IsPYR^JAjcuVOErWaf$({jOKNJ?jVjMp2tAWo~_oFsHNpDQ= z@PHTbH7RyHl8ShbT)nILN#FC^9#@T2Q&+#Imi7@y)(9^1JzDX zxQD(d51P=gD&>+4!Tb~RqZ9Cg4)FJeLV4^=_2}o!hS6|={y-}#P=zDdVWJ+LPVb>R zl;V2y7Ft0rawUcOfAkW+b{6NO#c0DT@?GuqJ3LFVXauD8_f@&1)ECeJG2n{Ipr5O< z8>&3WK}_Izujdupg!*7m)$l4!l@u+2u&SJ-fzP%AD&=)dzed}}lh-`Mw%0b@)`?28 zgRL7Hg2`k84zRVbx!YbbC99_HOn}0%k81UBWm;tcB#zUhKX)V<`bRROdy*Tkt#qk$ zuPh+3!$}7FYZ8Hfu9%Bb{2tZ%CbEG$RSZK>?p_g6QK#Zg`7zQVCYH}B|C=O>-^v@2 zWbw4@IUTJx)cv!|ykS+Pl$uKqlnySHmM$$Z6dz(1*}mAl_zDx<`-NIqgtH1X1$**+ z^Ve7-tV{EH=9T0whoE@U(#Uen+?^SELC(gU;gHbo!w9So5AHsb)<>qjra`8jrU2CZ z3rx*T9;QUp{asDn;0tavy)bJ1knKv^2&6t_dJEJQ7H9nI|>ATZMrg!2JlHNJp zVYC}_@ESRdWyTH0`NrYKUyP%SLyW+^j6OzF+VeC^n#veqtVz3;mYpUtij9S7 zNnD=u)r++IX?xNhrDdhnGk(S=;%=;O{NA|UxXXCnXf)OsL(_jqUzPqa-3@n3n~YT% z*E7D!T$OnzQ<3!z&I(IbAJZ*UhDnhbf+K4o5!7r7myi{}<^D1L%s#JfaIKej)!p)`Y@bcY|<9@1G$4vQ9g?r@?-WSbmjcl>JqfV2kX+U=~lTJ{4RUA~8 zqwvqZ<>Gy!ny3*!)XFzolnj?Yz9BQVeRqd+o<5xIA`sr|TghZ8j73#`o z6{|_AR#Zgc1ewOWx_?DGy2)EB9#_Oe$J1AoR8|^EATyB;kdVt^OOhOi_Ua1{=f%ujyZuVy^Wta zQyswxQQ>Sv;?tCxw`8Y=a2~3Q&Z`Z{=7Hoe_9YE=3>oI%qSe!qRH=0N$OPdR=0bf) z)+*sVH_sX4Ol6k;3l7dmW)ZuXO{l6hba_o=4g1sgzt0?JGcz8~s)kI8qO0N|>Fu>0 zwcVqamd24Xi^~_3QMqeKOHlowa?0MX#x<;k+YD6Pt|pgCbbx1T^R5=~c@If3ztL|SMe1xbvMj%LlN-52x~N==YqT!zE=l-J9zY5D z0J-Zn``N9Udvu)6FlpPvtqpv>%JmEkJ{NvR%PK*KJ~D_`zdh@NpI9CAE!Kmc}&XI7njCp`}(1G_?C3?m2{TsFy-?kdGi~(OY4~LTihIU znC*C5zIK`;&`}d0W!#4=|5SMh&mcq@hBa4#lMsqIAFn@D1g8I41hFkKizer7f4 zo5$epw1cS>Op13qGS~0CMW!z1iF2TsDT%dvT}sZ z*(tu-C-?x*p)D%qVQ7{Agza>N*ZMRpo?I9Qu3lkY^RD`FJxibzEts+3`hE{e8_iTL>E zDxwwP3U7rMRZA)zsmIjEqv6XBlDCzI*Rlou}13%jizh|ZVha*(WRNf!dpVdiJrhlowR|l!9@uBokd#G=!daA1F(skf}$&@C=O2s=^6W%Z; zE<<{#E1M=Qg38~BK4=v^$*p)G?!Z$~(A|1~)@CWG6gHAu?^1Ea2^;vNl3lR_4%0%k zNsXW##`p+f6O^IQX$%u2k&djD--{!`qJ+JnKQ-L1@K+{N+dUOoMT6ib)e+0+qWwTd zauPgEg+v3NVK^+Ki}3k7QdjndUUZl)rxs4dLfH)TD@K`I9>R8rlQ(6LiBj}c^kbh{ zL0-X0#W+Q*LWsx2OA!LC(kb5~A1wEke}HB&R3?F3Q}`Lp%^Z?t%XyE!hW*hD&Ce~*Jk^}cd!!AkN`HB?!Cx$H^=)nwf(wFsLtjylc`%{ zcjh`@QvH2(RIXv#wX!z%PU=cMlZd)u5Y2W~KQcipqXDiP=jbJr|i$Oyf+Rri)pNvnIg{$j$savwfyI zGXdxI!i=67sp;3!2c*|Ye}^~!q;a2drcr6Uo3X+m{neBoVGC$e z9C+XFeM$Rr?8~$-UB8qi>v6>IOJ1McJ-JD;Ah|HflWSR$H7Pr(G$}S&oZKL}MRIg< zTdpz5Gm@_-zfVp`UYk5Od1>;VNP(M}F-1G37(k50~DLey@Fh`rXxc8{YMQCwg~3 z?ytBdans^9$DN8h5%(hQZQSd)`*E6gyWi>GoqxCMUEI4m@BjB+|32u$=?~hE(?1^j z`0``JPt!kr_~aQsHU3C^aePF=<^)GVyTtj4dlRz~b)TDkKK0onsaMkIq`67!lWry1 zlO`vhOZNDp{8E=^e*R0@m)}zEr+i59$Hjj}U#;(udMMSBy53M_(4R_3J4QJF(BWtsBK=9%Km zyBT{k{@^+%V{}H3jP@D98DSY6Ga6>-GMZ-e&*;qeI`F*_8Ot&rXDBl3WKPdqka;z; zAhT)K@vQQ!W~PBCZ(p0bXRpi7$(HB%<*dkgo})G|F=v>YTh>_aSjsH2+@866b5nBr z=bfkS5LmldhgjdDR_~L)1_r*WU|7MH0!KlY!ZU^T!Zt;Riry9VExuBmSG=esx1@~PA*lSjGPiPztzFeAwEb7eEc~7I{ybLsW1V^?usdqZHH(E9-P}YNa&=(7 z?V&qntEEvXnH!05=rogbZ_xDBA-CqD zEKL4A$G|W0PKvz>vtod98|RRJRTZi(xcD6E(Ku++G@*C`?`l1CRrqbIwJmf9acl0@ zt)n}6QMXCAnp+EXf9iJV=IW;De$j2!o#i|8aVGxGPlxLU>weSC)UD9{qr0Met1HxL zd3)gzk9y1zK8hhb#!aBDVjN&9QvImRd>}>`mh@18AYO8B_D#D?1;D7Ql#HN*nNCOk zyfjHFfrZyX76T8jE?0rgTgp4|~0ku+dCjiYNHA<3l^x{OCwDtf{k%2#v;mSY*~ zzILqZW}*Sz$GK(}8PD;8v4RgItb8OJ+uMB?%+q%;;mTRHi(J02>R*dOtH5b=KII&- zjM{85$NjEOtK%J&>TC$!QynWv(};CMp$YqX4P+W!Se;*8NqsvFo{nF&C;iN8_>F!; z5%RTH^$f32S3EM2bf#2Q_iQIgF`i@lk-k$STT_%G9=5W|#L9}wXO&+nJ^0QLdQ{V3 z3Y@0COrp|ifzo3XoxwBEQ*BjFR1T5o@D`x<*n%%`roA_O*@n0S_u;weNvh}*w0{k3 zMsyWjE6d;)466u&cd)p;Rk@SQ+QVg&$|A~AOW&35D%}I&yisY-(q5!~HYk;#i^?m} zl(sA#SURtCIlA)qrCw#FVtKdX&fa95vr9>y=Y3ue?I zW>ijCQ5D|bQxA0DNbwJ;nrHav^y$fJYBinmC2;Ok%z=Q=%;JL_&7MFTc6jurX7~ps#a=xy0HF^bmkA=@?^Zo`lz&2sBFqmo5CA8+3$ezmanS}o@N0cUd$V919 zB;$Q}iTQRVX_+s?C%H9KvH|^dB8ivD%*Ps#v-CTY;XI}Zjby`Fd7EY5vVwolQ7cD& zSMH{WP>iJ(7{MxgAZxuXioaMt|ELftToguXfu?e`+)aL%1gl(f$vX2s{afNA*(~l( zBG+t@6UJY4LF_9hRmC1^3Ji7D$1k(i_q2@Ian9!teg%|l9G=)-F zc)z>%KvUuFZbV^q-0d%vH$|=|VG;jIwJdQ>WmYi7r4xBPm#H!Puo?@kdF!0u3~{B*^~Y_>DQhZG3l4&~pyQo$$z6>6D-? zyAA`nJzf6!Xm0Mic)N}vZE-!i-1BHsD{vvClf-I3X}=34NPw#k$xUzRISs~dXT}5Z z7hQ_hHBXr<{6UgWJ9_9XoHa}v9-;wzxwjp;Cw zm-cgfd01nB+fh>E3uz;ouEZcz!Uvf)Z>{Yn^m1uO4t7mVHA&y)%$?2LyMmlPOO1%G zMFew-Q7*kC}duq^k?fKA*Gc5HXH+Bnan5IjwAiU0Ey;l_OAb(EEQ z2l$=t-d&+)EFlwZ1l#-(Q&0i#k$O}d&0!KQ6{(myS(rgpiPxe1aAR+2LcW(P6g;gg zo*C!&)PnAEv+NT*{Fe~#y36XxTx2CuFIfe8`T%K)#2sb*2Y05AU1G$pgd?ItOsk*wBVP)ks;GU3PZlHlU%1|-F|h4)?HIKzHZCV zO`!)u&xFdthKF4bI}&y;EIW)-b9hvEba=aPd3gQsZ~5FfTpjKVOAh-lY+Kl}uz;|@ zu!dpLVZLGVF#oW;(4^3Fq0>UUh4u^W6Z&mvOlVMOi_mXEn}tS)dWMFE_77bZx+C-~ z_eluN4}BH-K2*xe8x{6r*zT~?Vei5!!m7Dg!sOvy!##Nf zq2VpVgTl4kjt*}SzA}7&_`l(g!)@WRdX4J+P;Yv@|LT?23$H({{@?XK*RQPKuEF*O z<_7&D{))IAp^oeoc{;KpvUSv{D39oV(MO^aqs1}b#jK7w6C;Z4!DU74&Dbxo&RB87 z9~<^=*tFs3hLanPZ}?Ngjt%QKOp1LFdoA`v?7rCfv7KW_$411qi%p8jig{mqy$~}# zW=%~0nE8C}64NZkBgPS(ADt2XA^K7D6K*9(?~DFl^zLX&v^_dH#yciDrhZJDn65EV z+-ellAjUK19ryS%`dswq=nK&kqK8GtMmLVuM!QD8jxt6)jEajo8nq>=d(@Dq7Ev9d zyrR+~QzE^iY9hU&UPYdXyd4=IX^S*R-idr2nH`xE`6%*Gw1~Kf-4Tl;R!0nsXdV$9QP4mV zQP$vWgVqgP8$7T7U;UT$*VX^FzM%f+dLQaNs&}Mb{d$sm!g_x74B`J{zZem|Dtu6Q zOt>-ZRoI2FJ#4+GFhN*OXkzG_(37DZ+4f)RzWzUs&H}utvkk*_r_{YoQg`aO>u|<^ z4c*W&V89Id2Ml+Y;qLA-Tw178FEuJu)5fiB+Wz-;ovUe*wrO&5a=!0--{-j>(IL@D zQIqhkaJz7e&`KC3nD3M2y&VRM#%sOT6VG9u*FD-{e#qTsyFGW6x%6?_=KR4a+NsKM zqoa#s4~G%<`|Lz^d#FU5fY`HISt!CU-C&Ver&oF z_M^9{xoI(T{^2H1jn7aAR5SYf8|4@tgq-k(v*)LdA917|riZYf-s>y8&aV0+B+;Cr z2iQi1JG%KL^N&zngLW&4H}7!Ag=kz!l{e75RPSS6GfgcZiKj&M93J5W)eu#T%8neK zL|BAdm7A2iluMO^l*91V#wy$J-d$82S8P%Iq4-fTNYN~>!JYS7ew^&HRq|itedQzN zQ}CTCWHn@JjmAgpjfOr}-iO4QF7ipN`u~()!-IKW{#M>14^Pb$J-U0vg1qtF9Ba-zPNuWs!FW707q?7Ro5y_Wntyx$|se-R1U7RsMOQt z`wVeyZbetRdIKu@LUpTVRh?QcDeqkIy!=V|&2n*hTKU!Tjpe)P=#`bHvn?zCv3x!C zj(z2a$`?}m_^Ldt+_T)ET>06oT>sg={Osq&pMyVlz~b9kHX9nEvNXMPZt0hj(IuTr z3X0zpk14J#npX7w)1ptas75FYXBGZa;8c*DKPO+1FU|Xyw>!@(?^3QH_i@gioHaSC za=PZ2<=oDmmpw2$I(vBbsB9sX38QRrR(@7SmTmUf?8t1V?20VsY}f43*?Y22W-rV> zmR*o7&92P;l%1RHo6|F=e@^Edi=6iC2{}h|KIELqIi2%Q&W)VH9FyEmxt6(pxjl17 z<#x**p8I?5`P|lAO|C_rMV?LGfV^dS5A%xi{PV-}N90e+pPIivzbrqdV01xvK~?^v z{HAj!fF|4W{dEKKf`9FqKP^0?%l$(G5zl1C>$W?PfI zGC4QdI;DHc@RX4$zo%5CIHrcDPEOsB`XSXhZA{t^X=~DMrHRvA(oNG{(mm71r~i_^ zDE-^?Ug-hp!_#}D4@|d6uT8T}H&4Hxb|5V(%`q(?t(oU{oktj(Iy}`i)jG8_KevQ(2a z)3k)N9%&=edZi6d8=W>NZ36%2WZIRqLuq@`&ZM1B`;_)H?OocPw13jt(+t!5r}s>o6dp~zf-qyT~ zyy5v9AXJ7ITquwhOe#E4m`8nT!l$H9ov5H~FDff?Di##?FP>NYvDmw0Ps!Sn%_TQW z9ASnwmU@=il?^YuUZyV_`T5>wlk&CY+Vb%gzg6t1D6ZI7Ijrgh+{TYp!C(IVGPe2| zbigGw88uP03u;Hy-mMkZmDB~)$FQxfm(>qy*wk>XA-$oc!C(BV*pKszk9awb`Dfx` z;+qYJ8SzlJa7+T>7{Qe;*0Or=Uz!P{~tF4uxI@n*^rPi%hU1Lj%GFT5C`x`qYXxDNq7HO;XfCi z-&$1ZT6lEh(P{3WANGWbn4zhV73VUvqYoKHov5pyG1Hp$g28_WP4^ZYzu~BQ#g>zq zyQ{6nljtxFLQSR35)_FCY$Nco)^+Yncc%y4lM?&y9IiXGJ1lj)>nL*!fJD{k; z^;`~>El-!;E|XlAxg3Hc_r&G8%Wjw3km2sTT!0RD)a8RqB8;-P%&k)(#mQZ|x>mb% zcI}ED?>pCNu7g}x!7eMLrq$?bQ^c3f%-y}*quqNkliA{a#{IRs z!oA7e&%@Ir&|`?l0=Qgx9(O#Rc^qYSuJS1M=!9l9iRSZJ@%!jSzT zUxx&QWC#Bc>=N7(^op6~n!t>Jr2!iQ&IL>fc94q`z7$i&+q(jF` z6PygP5gZiE6Z|IlN$|U1m0*ORmteG@yTDJ-0%z}u&nBNAd^~*Kc^~ke zE7M_xSOHdP1kT&H`iq@tfSV#%1cZCAMNT{j*tN zu1DejZ_;=GtIqx;ag4^j-o@67Oq*a*0dBH-m39)?R#Nr72?Ji&$r%qbU1{g9)0Q9;kdx*xl@?) zeCLDCrEnByphSIyZq>%s*L65-#@}3XsqPj)Pkiv-%h&>Dxk4&gO7xTLw9aCznO zGyFh57mc%@ON>ir_JazQuN5j-FP8+DUtP|+WV?LgQ|9C2`?ERk>RM%*711*KY6KvfZA!)xruacgt}r=DvR^jK1zh?#}Ms?#Avh?)}_H zyI*qu2O6W3haaE1{vNwMWFEery*$@=p7u2M>gx5Y*K;qa*D&v$-gmrTdK>%9@p)$ z86yk^qLwp=ySIsVoOigl%Il5S7q3W;+OytIythK1eCb{7?cp=pXSGj}Pn(Z~bBn#8 zui%>CmLQqqf1%)%KrZMfoX%r^C0rsLFZ2~A2!{*rv0oktFY*~$DcmE>5ju%9LTizm z$WG)ViskdVSM*w>71{e5`F3JYt@hRX22xd6&&u!`TehDKEBOKb&i-1z4}N8S$Q=CN z_} zu5>bVRyiRtcP??-gGPIz(`;1aa>racAUEitcX90FSW9R97&!t_4pwNTE9u8iqk}B5 zQ`#ljokuw^)$SL&nRb29;umzzCn3?+&W>*KEp!3~BozkYnCWV_!fuz{TCxx3kcFt` z!|ZRbL#q&M-<^tOAGU$^uJ%gqd0}^fY=lu{K0N6>4^?%#?O9uE+r4;+F3|)?77tAj1;ZyfO zf!h!2VHMY=($^kHSK1zp+bAe4L*TKrlg}xnPyLL3_WA#k)yJ|Lw#6wv8g;S)ZSo`Z zzMb1&qmbT@R>!$Ll>b-#pDO1o{J6$w&=Sawi=fZ(FQ@DedfiK41GeH8jprx!?K{xv z*|+USaqXu!(5JLchMcynrLy@hK5`{4&ZpWA%{q-hQ=|Tej(53glIjZX=lzP|5OiV` zriv4+fVH@)?~u6tg3Rr?vi>q5#QuY_S8#cD$X3YCGCy-CU-yVyC-04ydLEg|N6AvI zRYbD_9!Co9R(g#yRI^oMRHIePRH^vHvs8u5-3F=`s=KP=)IZ>%&cb1vjlR}KGZ8QC zZcTy436Jdff5bcY!BzPU4M;f3hcUZ7G7BBkzI{PDlF{!RMAHP{AU z^BMzNHE<8kp%$EnUa+)zR`Y;nLGwr5bzZR{ioxHsF4`>3a!t6VlC<9)>Yi$e>K-2R zN}ky{)wf_uo0VQv6uzUw@(s@KWvU-lGjO|~;Mwa{0(F#n5-Q*wJYN-zYDf0p6wN%% zZOt3zq0connrw}+)x3H)K zVLTK!59irFLWwyD!rjqUa}@HG`go4CbTVD%Fjtkf|AewFA8+brYTNY&gWyekHk3kS zup-N5Hfc$_QKl_1n)#m}wx>}SBU7WNXe|E1ciljqs7L=c_zW#uW$?k^AiB{T)ZCZhHr375s}*djQ!_K2cVJJeoA{bbc^Nu%#=@K z?~boKx}t~ghf@9*USlh-xD$UL2uXV@%!z#{>_@dr{&PLFkOv&8PlFQPwe<@O&r-PA zQDkRq*5#3!v7GK&SB;_OwAxAiMisAms{C46rue9ERu(B9L95cpJE$JHE864%u&KH# z{1pL;F^cVq>k!o6l1-kYFd)tRl`@D#>Cqf-`&EZk)hb){2s(K)p(-p=zk#D}su5~} z*djE65E6byp?*ZON^=0!%59BUV@eNi7{}Zsj=2-se8}g6bwBDB>rUw^bUt(^Gn$QC z#_;($g7&@^QeuDf`v0aok7=Do{_qeeeqB0bz#PRDBlpac@;942rW%@cC$(HLB(mNyzGu80mDDaA;Ae3KYf(+! z!v$!9$2*EO;?L-*CesZ%XHsLbfR5-X(;KF*a2|dKFRRJS0&4kO^KIl{J~lV87zH7z zh@7(bWCH#SOFf&s<{qdI&p?NIMB;9vl@p0$1IhjBV?7Pu=Q6s2J8^@nI`zLupageDln4dP!BzZChTI+lmxaTbzE&8BLJ&&R@6^)7>#oq+0 z@2R~HAcbfIo3GViXi0;t?C7zcAro&T1p5Mua~9v@t^CN`MTVmHM^vY^ctu@I({OkC znpm2gH|}CwZ*+__*Gy^#2RMda815k3t`bf38T7WdAs^r8Yi2frU)W9XMHvAe|&q$8OAhh;@sI0xY=FV^E3`)oi{P#?1Z0H7+K+cidZs3ZK zb~zbnFQ6PfhQuK@9K~pIktE}0_Ujy~_r>UyFG9?ZA$9mNKKTT*jnw{CWZlegy zGq+~%%ta%(1WxZ|i%8K8Elr8eNZZ|q{9+J2DYDNkYzY|*at}G>uV_{HSrWqS{I0ALd#@}D`W^{pcSZw zY4-zqfpAM#DBAYW+-~7dokHhkDanaP$TwY!mb4hc+f1^oz9HdzuH}4E5zH-RB-t*o zsOJ;k7iL2N32hywhtVt8m=P$64^gD(e~O%X{Em2HptuA-lAN%y~rnQS)aU@KTo!GK`={P-8l4Mr%fDz9v(vRAa9F z6}QS`W`TZCRxasUbv>HLz`1?goZ9RLH}@wPUMpL!;R<{LRoAE0yVaq!4Ekd&9*{Q> zy_Q4tU5Z-B8&6?Pa|f!NTX=bfG7FnS-hkTjhi*7~AW+w;t=HCQ<=RKubLe_b zF$X<|@2y1pL91gPn#EY{w&|Yqme|UF7U|u=qKxELQb!4HPRcyFB*ai#!8=r z!!(#{CVB^aF=qNq(zKtnCPIbQLd?l;ea%tZ(rTzLBDp0JUh*tFrUM}+{{k^71&z`T zQrnW52kb`syHbCf<9@O}m!vjri*@T$D96KE>Y6vOLa60+Ow>8)R%%VPvowjUQ2MY* zRP$9jZ z6xlXegRGDIM<}E_djb(<<&J&qOA89ay&)aB|>=6?g29bM2AYiczbjg58!F0cLC z7pyVT_{!JTXHvjsr%t%JiyGYR{31HQ7KX$ zSBMnvA+7q#(`2VnIeW>9A+h$BHcLFEcG3fq8L)?In;c-{`aw$7;$<_FG&LnRr8gBe znMy)osH))L2BL`m26Ap!?&%M0Yzw@xsgj?d=f+7~`O5@;I#e=1(udy&mUv3~agUS4 zLQ>JBZ>njkfWqs>PZA{4ps!AVxSAxXl5~P#8!G*ge7KKNW1iW5*<)FoEJi*SMzT_# zp!lEStl}T$&m!eKHC^g&W0c@N|FCdqT> z;HPbu?3P@X?2>GQ4LeZM8%A`j#9Q*TX-(5Q*un#we45;vT$(hE_Dzb$^u|-9%^hpp z);JSJaTmHDJsV9L8zD}=7rzmwiQkF$i+>j{K+n5Vyg|H5yhywnit!1Yh6}|T#dpLH zxpxWoUl$({=ZmG#jOF5bagEp=UiIL{F^!XX#4$XgFHXcUjguPN#E-;RpzYR*pW{@_ z6|2Q9WZ1chO~n2@_8Rd7vAChEp}8Si>>{pd$ZB}lkVFd9^M-8=GaIfnn2X=R?48xH zyx|Gmpfe5L4U+oK4gL-9aYy#7Ur2sb-}(aj&5op3U4{GGQ8T)xi7xW*)p6AizJz{B zs+v*NUfI3!WyQRT5fvd7_sfmTSA4D_J8f}ULFt9kVWl>uFH5$Oc4uEAE^aG+TD-To zcd>7=uy}Iuvf}-GttcKDbbFrRz$U zkozSo9Z(im7G9Q6Hofdf*`u--W%Xs|pPkUeFCYQz>gSZt?PP#yKY#h$rF>rbt@3}$ zv&)Oh-73abOsg1OkyP=aqF3eg$_16H@Qb!o3aa{64XT=6^?TKtsy$VEtJYPGqPM-U z>SuhUovU6{Uas6J@^ZyC9H-6|Z_wmxKfnKc?DIFDpO<}G=2iBtbbIM6H1}tC zzJp6Tl^idg$}3q=JhwQsIIZY-(Mk03CyPcGEi77FbgO7X(Jw`diw+f?FM3^6QluKr(Z?$jsNTQrPrxX_T;@TsA;p}wKC!ABe|9z|Djn|K+H+h6%)>=%FFQ}RNb zK{nZ2j%Wk?wqG~KHBM|?)wsFwL1ST~8yx9LO>>);H{~{Ulf+A|N!lc{$&x%T?U3%3 zWyt)XK>x*=d$8iJ!U}c98fBHT5A~fxs$5lPwTQ&AlelJQQ3Xp=%V4^FVx~kdJE(V%y6I)YhEce1Ua#y>7kES=Xe!p^d|b zJ{{J5fm*0uM#^S2BgGcw5M@4RuFvxQ=vKak`)wg_;9UJgwpun5f_l14BC94{(@7o= z8~k6*$_q&1|5oPyQY0zdmH%V?-K5ki1DUt{soJK>q~=w__+rm2=vQhCJs4yD$E@Wq z#+iGJ5SPjSOj6%bBXp+n5Ws%$rP}a^<}AFsFB+|;k9N6sA6uC=ju}Cw&a^ok$NrJ# zdpJ-1aH&!c>bXidIjI~ej+dpN*EmS>0 z&Euw8$ap&m&CGhVIp*@GvRSz5UrK-FtUMlPeSlOWxhvTR(eec2!*R(2+zz#pROtMl zAzJ!C_Uqxk(JUewt)8zU{62&t^#&&h5jDN4;SGA7(tt zl^M#r$|uVkpni8&_$j(6!WF^jgyt%yE9O(_F@i^)D8B>0JydQfuZ6eiE31;OmJX0! zmMkQ>^kmbRCYz?z#?_6&#<$`_;^E>i4G$VNG>mO%tc;R%6rlQ1^GUmbCx1O$fT&t+O?Azynw4+^{A!fd$H{D5S3LxGe$AJ4IP=@8 zTB^iVH>-wL8CJWUQ=S8yI}su+q3|6aL*rCe-9FP!;C z6_w?$%hlz&a$nA!BPxUyEu>3&R7}CYKfWTq!nUFWKYnKU!}8PRE6N9y8k&wapM@jdRFU5z8FBKOS+m@skZz>*9 z+_m^C&X|)q|BdGR=;DB4=VDFKz~a?-mST&uaPe%%eRR6$Wzm+RenpB;Cq9k+)LwYC z@P|UT!uJJ(3ZCZ=%-82#%Uhpkn71z1D0g>`LC%%z$=S}?53}ZH`DLyBSd*EUc_j0f zOhM+CjLjM2GJeTeld&>mZbo3nHyL9yx@L^z`_7DvjIs>dOuI~RMz_p+nRS`tKFl{6gG{pfqY65j{XF|^wj}%eoF_SIbU)s?ak=Ai&*Z+#mF9NIi_e>%w=?e! z$)%?Garv|J7vw+4PtLE)k1se^P+H(yIJEH3!ncLNpQ1k<{iOXgpH~#XcyS(oR&dGD z5@m^qvF>o`o6-u->Rx5vluaRj^itWwvSVde8S9EU%TF!~EDI_ND6=oCFTGegsWhDA zQJc~t_UxLH=_P?BK_zv?e-}R}mXR}?Qv5rQos09yp~SMpv}8`nUNUG^CA~^lm8wen z;UBv}cINM&n?4UK-(H?qJ{oh12T6q&yBGv2i3!$;qsTRAO?nyG9 zsi(kmucr;VPqEh$?|7fbJ}Mt?!6<=Juuj;UF8)pQ43@t8eAT`leq;P{{CfE>^gr!? z!T*haLcl9j3$}qp0SK!yPXm`;0pqoKf!M_A&1(yY%3(g1*2)Px~ zIW#KN8r|%o(6Ug6u&U56VWenapZ-_4UzXF zGa^q$21lwQ$|4dX&qNN2tc>U#;Sljn#L|et5w+pl!iR@9hrJG4A2u;;ERSg$Ru$Sk z)GE{^)IGFsXj@2bNG3|#2K2WDAvGa`QAw;0{gW*)Y+2YN^uS4B^02PqZ^C;;?2D+0 z7!Y|N(z;7*m&IMSb}8txJ4zhYB|0{GL3DZasF))$B{ABV*w`hpM`9~uz2m-$J0DjY z=Nvycet7(?cy)Yq!t8`|31<>i2|itybWQ7O*llFDBi&wiv+Lfo`}*!_-M{Uzr$=3n zn4S}RF6eoyXH8GHUSoPq?zO(x$zJz*z3ipz^{LnUUTM7wd!_d}*Xxg7-}G|srSF;5 zb8pY_J^gz&_c-5UbPt;zr@GJXKBaqOxBK0G=+@fxe%H}m)d?>WeovT_5S>sNey9U_D4gUBF-v4B)(VtCZ5UB_($=b5)|}e!A*gRqMd06oE`bFB-v$^4 zobn&z-|TnOFV?RL<$NdKx1tRqk?6kALHI#%SP&=3@%hfj!sm^5cke>4Azqc9hdoDl zHh5g}(31AI0L_D``%bq$ZbfAHnYo^E8H6`(o^z0MzLTrdX~({fjSkx#YH^~UL|d{N zA55(6FP+rT?;l!=;p(5ZY@njM+ZYw)x~0{vS>AL}u- zuOC`3Fo#K|hj>osp}Vb(qBc;Z{!#6x{tOHI8|7L>HvLZz`7YM|L!|d429kiL35_$v zha1Z32h|U&8(EuBa|HG5R(LqC8DVdfU;JEHCLt|)M9G}u%%a>+w+derNbc*>EX>@SS)MT~LzwYT`nL4n(q(D;($wfBYg02**P>yxOpQu4 zMfZ3nWiV);pNPiLtYMkx%_3fmmV+s zz8v>*9^1f|vtM3*`Rb+dE3;SLull}P@han0>nq#Wk*}w|UiG@{wd}RSn{VD+e^dV^ z@NLhxL*H(EoBr19ox?lRcZ=R_d{^-9{=39?zKL@ZFD2$BrX*?-TN8&T{mScol~kK# z^nT6zwD;xjrSH3cxc{Mh@}J4s$)+i5Q%q9lroKw`PrH^jJ3T9XPR65*n9O&X!#`$! z9GP_~D>nOjc39529HZP-xw71^@~ZR3=3mdR&hJ$)x!`I+VS!8Gh{79%?+WV*qdrak zG~?5`Px+tp_^18RAs;EaP?S_uT-1d%ZFR9#$$*mY$p{Z46Z}+ZP}!og_Oh{`&wTbS zzgX^5aiPMn^5@D^m3^w7SB?Dg@yo302i4!#T&w9?yQTJH?J)RD{;a=G*H_e=G^{1F zr=uZ8d|s>xvY6hRVfyl&QWcFHc4xz`&l;>N1&N* zyzZ_pm73slw3dy!&}LDyC6F^=^G|f&`ZR|$kB3U*!aP5=r86mF;JuXwyQ z6|PWqYhUVewRDAL`XZ7ZJCm!t3I^OIT!gPAxBrx?d$E znjIvNSx~jxPS(LdXf{`y=QR7k2lF8*p@wI4i>l>LT{3>mN>(C;x@uNp;ZUa^H`})a zx6C2iEVsp*$NAFQjU0d*`Zj$Snhs~G&~C6mw{^Veh%%TB)!E+ghG8>oW-p@!MrB4b zNodJ3)*1K3&0NSF;xYuyaHz>2%rs_!FhH#6dnhf!=mDIz)L8Dsdl+SX&N|R$o{d4L z0iEu3N`O#l2R-luJlSz*U2^PN$p77LpA9p1zQY2zgxegF9LgNdLsZ=5@IMHQ*Bnl9 z?ORepT^yR>H|&Lsu@f4`1bavOf9aC8sMUPoILPem;W~ua3&{}ucRo^PUv6*c@RP#} zhcS*j9G{T1>h1I+nWPG*iI5ZZ&P!b0lb`AfXR*We2XvdO+%DlpUV>h%#ogPZm&Znr zhaNQ^v7W;`k9yX6dU&~d#d>Y=>hS8}{e$;S@3-EDJ~Mqb`#kh96)X^l1$~5lgqu($ zjuE{R1^F((KUL$q(y!QWkpEeK2|E!sfzf4ZSXh`e zv?a7Iv>~)8^ls?w&{LtiLT`kog}x6>46O;x;b*GQ0byUGy}TNB0XLD9#~l*>UHFRd zL*Zw_Uxgb-ct;G1=ofK1Vn1`9hY`;r?ndY$LL);X^%0#TKSpG7%P6uvA}G==CN5#&I9T7V9ljn8!HAbc-3m?SL5f7+thBS{3aZUE$sVL{9xW;XF^AXCWRad`7I=hG5b;Qs$hfQqM-Xh&xvA3QsF2cx?nB$|&DOnp=&}fhL(ng zg)Ir&7`8BMO4$FxwuJo?Rvae6_Z1(m4D;iC`91t{__^@s;icjKgdYyS7k-gz#t{|~ zZV}bt_7Q#&Ud%UFM!e*G`8(n^@79Ni^oU#Bei^YiVr0bFh|v+_BH|)!BAUa`hW{^o zH^+=L>`a(vSUWRL_t316=_qbr1WyRA3>qC|9&|qNhd|@Ny8$5q?{I6m``6>s8s^vH z`!{aASEyxw7WEVbq2#-ZM{Aq#PvJq~8sSFteZL472Ws3o5Wck-9(g;&w-?cnD-g?oi_(b;C8rMxV>Bz!8Y71p2>>?|@6 z`HFrO%@i#_S$R{GFDe!}`VRM<=)1vpukSlwJ3k}8ad;3Vel33f{=@y3_;2w);h*B4 z=AY}YK}$I!U=*6+T>%? z#*Ow4o#v0eR=&BSV<ukz5sVeYpwG_t zIqh?q`Y4TMk7EH6Z> zyizbwU@may@iKf4`;77#<}=G@t ztLFTXJ6^#R`_}Okgx@3Ze(yQvIMzBEI>}(*svNzX zW;p%rblvHl(>a(&7oDCty>Ythbc@^T;RMfcn(NftX^NA*Q@i6e$LWsa9WCKWY;;iA zKSS$T4ijp!osHcTD7Ob~Lu{X*=qR<>4fm)HWr&QtsS~h)Y^-ir&aoVBSw||@-(<86 zwg@4u%N!1u#Jt7a%VL;CsD&1uhc9Wu6D__&0WghJ;Z*4K`4%b*GjtJM`RlQ4+u_va zTV5y2tRAkk2ia0KRvnfuR%21E472)*M68{-`~OWhOSkg0wy=(|_91I)k@X>%xY^c) z);2b-q`1wtS!{E|#tKgJS`xo*k!TiSJHvJ{+gjV(wi4U$&b?5VZ-p`~?`&u%>l|q} z8tTixnz`+?~8H`TA z$#NL{>I2;0o9|cPOqs*`&4E8vY^k-hAqjbs)p)B>q~7_Voeh8pyoCK&2bDlgCO|8+ zgeuE_EI(TACPPhR>1;WOOubm%$9wopo}nzbXz_poTx<3b z3Y`R{#B3BB$)qRLnw~ZlnA)29lQnVHGnQsegvuGE}RY=u87z+vs4oQp>*&@%K6kxoM>4dbd>YI8%6ZC4KX7T{#N5;aXSiWlbHG^g(Jb z^$S%mRSAhg(aHkFT15{<2NmK=Bz{)QF32Xzy2<=x(J~!LlHXIGo+Mo*jVGJ(Pw8*m z4yO+}MY@7pe^B@CPsMpR9ob{jxAZCBlR9fiwx|)8VdNb3<+`sdUiO{rE3OZh4U$d! zuPd-ywv$ZSFJ#iz$P^?X{UqNk-ypw5+GrhK*3pU$sCEyMNNc7HRt`|ERn{v#ROVzJ zjV9aasY<01saXoE2C(8?sXniMML$HW9;oR>7r8%#(J3f)r)mDD`5rCeU`=05u*OZ3 zL!acTdWCwpIz;WP4phrkb!aI5P{pabt6Ww2%0p-b`zsxl29R@KD$*3&=!(y0zj!O6 z6=J!96x1yFD`-V`@#z-IofUE}%Sd|4hlFaX2vS%pIw|VqMRF}kp#9M*3lt%WKt-G) zl3M|aq4Y<3D|GzaTrrq!h@w?)hWfIfVu4};uXl^$Jc*%Y3Kblr!OF|ZR%L}!q4c36 za$c29N5l$M$ZC{`*XXpnYi5vKwE(T~O1eE~G!>e1jWbeBL z@6~738`UFGc@996VxYdG>Z+3CW6maTYozi$?@bV|_Y)bh-ttdm3@?>6OHb1ePmnf| zH5*9hU}w_^@?1ML9cWzGI2Pxyl!R3!4!d-^Wi{etI>^Vwcj+MigTL>-_>;In?9l zl{^F$#6apEPviyie|X2Fasx#_#T3$4GugN9yuV}V#h+0=A~P&snWc15wbOYjQsyWN zmDiLhd^c1X!&Y0sZ@NRYtx@bpU6F$$d$~MN-XeQWB3X^}pwwPkOMhl0NxM&*b~Qyc z<^4ps|D3H(6{WmNh6EJjB8Dupc(uZpdtKpno`?{`N}oXz?6+=1;|E#S(EK z9r4+X(;HVbR+H=8t7#FBc9<@Ys_ARVSjjQ+(Vj`}NgDAUPNB=RS-Mwx048sXG?sPq zZP^={UgjzvKw|GA`BS+ON|3E2H9x^uK3I8DnWQv8ue%f7Pnddv`Xir@sSu=JXgWcZ zzM(DD29TPx5Oquer0V|7^I4HTXL}EowEzmO7J92mOC#ry=P<7;o7*9`K7%#%C%^d> zJfQBZZ#|nethw`aNxGA$#%Jp$b9RiPPuN)(r1R7@YumKVT3a+f)>Nnlb6=FM2fE+M zBsQ%-YP zorI{-Bnu8}Ie{|$RExT$KP2Amtiw;k1|Cm_&u9I8eSyAQKO2&Ys!f6qVhC=?!)UC_ z$nNQmUi@sw33SyjI!Yj?=(#j7NPt5+2(oJ*e3C1nul|BIZJ@zVth~FCVc!+*>o|i+ z|NV3Vx0ga$8D=oiAdc%lK^2N*YoG@pA)WSl$89v@o6vj@Mf2SY=f-o~5I^I}aKW4T z6s~)B*fOQi<|pWV^s3hPJo6rq>7S!SdII_PD+svxq$&Q=JfXQ4ol806&J}b|TXY%Z zw`HS!%7-Ox+U(WrPxY^R^GG_nhu9uBJG4xK-M$5Kav}RKS|c$ z0QB!8AS2y}6fA5Hz}4{wju2V95Ka1wjx!xsJ1ij5PJ)&*l$^9fWRpqJWE!F1Txht{ z@Sj%%Z2bBpAsJdx}d&xc;itboN?unbh6S6F4 zqovzL?qDf8xoAlCi^zCPB^z#Lt6}Rk#<*zS;ac=&LCq@N2ga-UBzyV5Nq5$zYaeJc zwH6#X1G&##*Nt?q2{mPp0+$_Nv6gV z(kf{6$AK{i55`&;`Nz>;H?~^Biyy;&*+3Fqrv8&Yt}O}@)}b<$i)@X$^{lls}P$f0&4fC)-zd?jjx?AZTw6izm(a+M zOqe9YJe)oMGn`?lghPA<>hyFnx!O9c44#o0uo<_4h*T;q6x)BPkju#c==~q-emMHX za9qEm@co=cX?OtD{~25fIVk@xqyOL4eh7E)b}k>{4?fQIpW1u14{iSr*TZb`9oul^ zfE?tVK0_UA2Ba|yQa-)g22xx4nHq8&+y1r_XwV;VS>C2?t77ZiJ_oX8A{5L2!LxMc zsMyTw?~5PdWXCo944=@bdyzz1(s83>Eo5UMN6SEi3XYw19QF4*lA%qn#J3Paj^=$_ zM;0V%R`Qx!;W9_DPrLs23E1B@6Su_>h|TkOCK>vL`aygS8d`_yzvo?f$IPaJ3_9CZ zQEMJG?ibCm%}LCelBw`5*VRG<`>0vLxYeP~Q}0G8xe%q;1?7F^U&?UhNAe+$pg}M} ze{db5#YE25%cLUu>X9hMhD-gW|425W{n{frEZHboA{mULt1q<7za({%&eD0DWoJmc z(*1rct(NLZfRC4TkqxDjens|HRwA>MTfyV_EDI+Ay;xoX1wDf~KpmOrUW)EGlg28h zkxe<45x)zU%M_RKP%PssOJNQ3BU1U5@_S_;_#5+-{UC6(Dk>D^iq|ObUMg;q_PCHl zZWBcV$?B^ZX&#f4E|fhYD>IOs%sq@X_nL;$iN6lDzYe98yZCa${05H(DKyXi^~H6E z>$=oc)xN1cQ@f^C0E=H&lT%Y#lV9_pCacE0wh_MP+nOUar@2)Fr(ankt*NgWQM;`6 zVeRGGceP(?gV4h4s#{gJw(fpiWu2zZv%a$_&S5?wI1^L z0l46Qarq99(GGs&5bAw42Q1s#sWkW1i%I5O*!mTT#+f7*Pel`C-SRip^5Ev)&3c_{^JCot z-C9QIf2gZprlH!<-eiN^yux9(+s zJk>kFRomJ27klI;nUPMcwI*@ix{M#u9B<HCcHS6a5VtZQj%84hpp1$yD3RQ|_8*gvhGjz)bX zBc&4d+X_g-0Uc8~2Y=~UirZoz@=~EV9rtm{=ykb?Qe|<*4OTa=OA`Q`?gw-jzB*HJtA?bX9je|{_^#J@jp%AJ6 zrM5kR(OGLHx6(mVJVvsd!tyE}mxK5cVk{di?%0kH^e(7M1UvOzukJF^(RtbPcb)pPi>-s8E81l zxHo7DeY6j74-7?fsAEOiPyGq*$sep$!&NS-Qe~a8OnF23n{vN0Q)!_xQl)aXzog7i z3RDiNFG`)#m$l>=R$c+BpHY=xRUKDN!|D1FcS4jp0h(yA`YW`4OVsmGmakC%to|Lc z)&)L$C2A>NTr;?%j{Nr#tYm*en{$TM>`hG(A-y6!&fFuP_1*_`-BQUbu3bdQ z-A^Jyr(TY>&p^5lJ>d6}8i^HEmoTZ2xj}bnPpUIJq^{`1r^zDd_P0wnphBOBp3gxZ z!CdGxc`X}YEVs%93Rh-F^SJGx7>DEY2qW_Z`2kkSXXG>FUh+BeX>x!0dlZ|NRDynz zXR+EHih?jmF2q^zw``XzPnHV(W+SfaG4RRCsOUJu4BI0!mN&{q%brN@(bZK-(^#dJ zNb^aM*(oV+deS72{3z)x8PT-8v0vj5GHIL|Z;5A$1>&-X@eQi_EA{W{8|rNuX4k($ zHQ23gQ{Az;d3A<$JLxW8uDwuuy7pSFMP0u-O>G95I2LsW>Wu0wVE#O-Q`WWD4XeLd zUt1rDD*R?cVS~283mtkVbhyizC(UiFY7A?d-gLI<7CK2ulcA&++V~{)UT^6SkkaQ% z4@s|~lw2=6fQ$SQudY@W4Ogv#nTmjSGgh%jA!Y71745!?TETd>zd25Wn5&FdhpNYO zgl@q})4;L$kObxsb+u|K^@B2{Db!nya+NZd`QL0fYfbW9IM$c5KaFJvr2ARRuj74t ziK_E_)9I#yriP}>rnn|E^!VXT!`XhO>d}VA^ELI4{HCoEr6fX{4u|NoWF2`tf5{$@ z8r~|Kz+7=Av&9PeJVgg-PjjeP=@lobXPlZ^1>CgS@n{ck&oI^ZH#Z^nEEAk z9&dPc3UwDeNR=87Ql>WG%CON%w4JF?3RvYEv^*lqrM-DIJ`@YiXMeW3ko`3W_xDwL z;|r;j8o;hEZVPPR1R)@aGswI4&#Yl`+PA?BD1~>tlR1x1dtc5(;~C5TAxT%j+S92` ztban1@_T&+k8}tI(jBty4ciJhv%ST+6-d^Z2WQh2WQ^~@-TbaCi`4NzR$_m*ANlV* zChUml=nu1CD&Fiv_^Cy#pjNYTUCA{OmE5EMt-nuq+#zE#gX`BiGFg>RhJCb}8PY0N zwMv6g_|J~AGOIIeGjui@$EX-&Je!QmzHk{%!1@g~i6$Xn66ux4P41i2nk0}gd;z!L z|4e7&1iV3_z!lTqUPYVH%6K)0fLw z$i051&ZKk3;~up%9c*f5`o%rAHN5H&T)GR`x;!sr`roK03r z4_4J}=n6v(-;n7%i9F+dtjgNjKf*v4Gp9O%%WXtk9W%NGq@TSYMPL`{X9_CC87=pi zZ)9?<67SYas>iQeYFgsq6U@M&)}QS6U9EY{)xO1jbWDE>?sPX?cn8{Y@yD4#YZ$}a z^%c(ZzEE?cn5WI{*u(x5kc#jd6_Mf0I!_p+K$UW$p56mj=eOt|uNmHFW$Z=<_C)3s z$BiZP)}b+CB0K~ zitd@N6E&0{AvE}-VBgvNHg zWS_||CUZ^3kgIHDa?SWDSv8A{?U{v~F#3&I+X&`ukC_dxCa?QX!@h<>Lj%KXgB(1h=CY(ov54Z9hRH=Ic7&P3`n6QPY9Cmm-r%;H7dzr}E) z;g5y`45JMb*pP4`G3!k~!)%<0Hn?prIThndpdd zi1Axyo{n%)V&S6vY%~-y=OYrxkCU6d2nX^eqj~=^Bv!K@b}?@aHHs%?eJA@QhDu96 zBU_^il5tO?zI{xNlb_)YR!;K`;tiA?SE$4d#;V>QUMsT!E&@cTj-7Jfv&6Ntt7AiQ8ph16bleMW?KXs)>G?t{K zQSEVTUs2m!M&(D%nP4;Lf^ltjZR_-!*51_Cs#{*Q{6?zQT;@E}>0Di+_G_WNqw&)` zR(q<`m~|DXeuvY(4pQhlIuI8Whp8bir9aU{Q7aF}pOM7~SWRtWHXRwU!c^&^45E^; zh#B2OWx2A4>LFg07KjjeI0GHklhv!#v+1yOWA>J$u2Rd@&it>=ngq>c%~Z`PO+Lhg zQoOG|+MmcbJcIu)N$bsi4q|S3O4p%V)122_PZFgsYEnkx4sc9WVtu%#^qDB8J zlo<=^Vo9GS!4qaOqA$g2PJ7H$bWnL_9 zt7H4X$nraG-F|TQd$1OEXj7uOGHuJ!8$#0(0ZoAVaW(-S$ z+OKHyBI&XR)xmL4ZT3)?JH^`iNPB+!dk7vk$cI1QE@?O98GJ(mSsv@zGS>O-P(e-^ zl*0@8ml)Cu!uIbZMW2STU14a!2s8!4w#=xD@ps0vI3u1UXW81s#>4}*!XfxUIc&8i zolUEy2T9P6X&sQ#w+-I%Z=VZ2RAo*&&vCh;X!J|frhD^vrig4 zFt|tseIY4xT}esnO^t9JRnJvaKTknTIYnJLl=D#}XQbhVTN#P1jK1a5e#$7#C>M8s z6j{_Cj9+lJK%x#gX{$*J)cr`t%;lt5TblXv3OAF6Rby5U7h#6^Ci4_?TZ;Lt@7v>rCrXdgULul#nD|Z2gLRj{cvc zvjB@S?ZWVM(lN<^fF0Piy7t=Ly1M4t-QC?CYj?ZqZ^zoLl!YyVA!~C~ibAd1d zGxNy5-ajS%6>fdB`-2E4% zb)*96adNl2k|v^&{L6W4xcCfbyZUSw89CwWASzZ7x8bXwEA#7|2~UcjvNrdKbHtCu z6*t!NTtfkbJ5EC`zV4`~f@u@A{k!8U$PB3Tdl zpYmqBU-#q{6m1k86@$rQ|ElPq9H{Jp=l|ueY?3jm6{_8;@1$N&CB0%atLoo;YLl_i z4l>43GVh%}xjtqeUwBp`n9Yt~iT71WyuaYH+UKOt6QAvwe_;r}(tCeAp z2pXWQs6`HZpt2q**R_?tN(DdBNNH18AQ3!4fni0n_*`)Z zsL>0_j(7u^6d-NeTDB@-<7_t$d)<;;r#_UAeDR@_>Llz<(O|DQsf)@Rwt!J>U-Dc z<*$V8kK`V=_i0A=VFUSSnLNUESR*>0r|Q$tNKUC&kXK(GW_=%6C8gEZ+4kd9f3tnQ z^BSefBE=`g9z}b_2Q&+N<-O%`vaYfy*;gvYAn7vd0f9sY6>Or|#u3n36exN}Pm*f%kZ$5)q-KMhMc<>(j;jS+>9|$CsxZYL(eeD`;sd+ zOn#ViMIhAeaU?VEKp7)}?qJ}ZSV?m8V`ZqSlqwKKOl#GANGS_A##X^`nXLK~t;r}= zOH?1bNWk+|-lY$;N3N3ZkX?mw(OhrzDdKTFa_4rd9REKm66`*WIQ*&QMHVq-V)IR9Qvyx zSerWXINM;n=$Pm&HHDA3MATYbmkNJ`c$fH^_$#SE;Sz_qtfU!L)&r`p5=kXeJr+vO zlln9vI^Yt>52!6T!l_qML82K?PH}-rGcw*l4R>p6hGZKbL>+))V@Aa zas{+L)qDc!OnFGPiC15OO!JqzgZg)vHucqR)kD=B_SlIq(Q}n&=}vW}D|S<{igdw0 z6m?+C6v@BJKj8k!l7E(qP`Oy-wG~wrrO~{|6?NH?gQ20H#2K_su~~7BuO2|^0J5bF zPzu@0-oiE!;~ywecsPQ-({+pE;~npuSSdgUY=Y)d3*vekjybEMAqt&C9JA9PgFGb_ zWGvd8F{(3k&_W>X6p~aji{u*-%BsieFrUS+bT*=7TIzGaC&+gkES-uRBL{txNbu<3 zTg`X8?{nYnBp!K5@2TxOz;^^^m3KZNkawiM&wS4MT<}Ttk^6q~nd4KPp51bvSw6#j z^3{WL-pH7!_m!v`|TFABOPn;7H5<%vP=E5BLn-1|;h)OY{X6zwl zNfB=(YA+f>UD;JMNAwP=QcH0y&fx15?_Nu5u)XA#b=&u%W?og`Ean|dtj8cB(7|fTy<`L@3 zU-Q^CWVzDqtSzTVDoC=p;;a#u6`y71Scz_KG3S73!v561tymRYV;&GEkO~X3iPA0yW9fh?O_CGu|H?X+bZ@d z?kPShGDx9W!d@4vyoQRd0$16|oGaImI7nEMIz^SGidS7%-B7(yZRNf1$^O_^rBYe= z`5!3H#T%V`%-F(5qpSD)|BV> z4{W8r@)q*`e3c+il>d*<59FWZ7x+2}O=6tyoNlI zv`-^hQ(3ZOoSh40Auy=+%ck5jG<0IESjVrC_U9>wQd(T(0B^9p%BE%AVJxyuW5HREchM zT~{tEY+vT;-y9jtzz#w?Jj}X!zN0?`wv+fFkDwfx_Z))9bC+8Zq-(FI z9R%ye)Ys?fbl73;^khbKo(lglDKr94mWq}@LMtde>1Gp;Kz1PWmn#DOHF2#pWNH*ZZ(fLbERvS>x}CO+L8?DHs8plw$UwV?5^nkjr}If z?c;X9$*Jzv{qn~=fu|#Ji}+t7v;A`DKAbMCyB0ato7`KO{I6wd(h>q=3$o8zdiJxz zvZF%qVSoHW*5)<3Po=zvy-|W4O0M|((dR5?E@%|?VHMSYF2_irnVH}*(td}s z`nS>7yTs~hE4fP<%nKLMhxvz&)NA-4jIV0#RNQV)*jhAyy6!;tCD!~8U;y5MFQjybxNETG#=Gyh)7-D< zF12KG+#6lS509Sy=~edns;neivNtEelGLL*T0qC`I-JcWyw;VF{~ijHgcazPY$v7Z zjYvsVRhe#EU%D$|Neil*;gH-fNbJ-%9bvd%mBv%w^nx(vB{QWp+Sy&4i;`JOnB^hV z7tP{t;LA*^pm`tB3RZxLml)o@SSXJ0u*o5m^E8$;stPYUn{aBbPe9QFLpUzV+ zYQKM2KYl@l;}JEaXSRuT^gFRXjJ|D>|Iu%IC~405dk;RTOle)topG`+@bzl5>RnG| z?uAcSoh$7GYOG~s&umhhrFz=M_q7!*slkF2pE+;ek{=*lxq|!^T()h{&8Cy#oJ1YH zSGr6(Qu-IY-fGfZCWwi2@wQ9WbERBLwfncE12nz9l5#MBo6zC?T~e0!rY~#9S$toU zX=68fg8s}L#pKeKmgLeY+b!7uzyXVHrR^`Z5EZn z7AmLF)YlWl4cPmZvzi$vt|9(FMRA*K-8X2(mUG-Rq}sp3aXCnsjE z)Nu_;o_eOhW!=R2V?HT|8*$UcINzZNlsYcJ_zJT>wjCu2u$--!oWMm;a_z-mi=D+{ z>nRi-(~8fa@W`g;bO=Jozsxu?q2R`l&NCjZ&^9LaH(-OLTl1iU2yKy2aEH?g8*gjJ zZJ=!d6srB4ee-NZeBKY2>K6XOXAnd-(Dh4#iYv31u}9crP;d{X+c%jr^i%tL`c(yX z6}rXBj$ZWr+Cubg?x+i~s|=pQP7a;pHC?R=_FDGOw!IKSU-B##LTp(DRdgQO>-Tis zvLLQLXI9uGe|KIoK3#9_F$h$5a-wsR&D)t_Db1_M*pXxx!bIwjRWr-YoJ^Q?gY1hz zBxan?Jek=yvub7(ITi=G?{9K6=4Rf^Jd(LBa~AgjVj@-JYUUMYWVX!0%$J!rGLJEB zdzyKg`z~bO$}GXL`3BmJFslK7TauN*NB69qD1A<39nVsrRb3AAV?}mGb`SF&^Lw+! zEXt{qb0a4(w-36hSrDjdS^A-w+JYXpz|t84^Vz&7dA;C@{fjdC0$h<3Os9TorCe~v zR0Vg)H(F1^(U$zl`PEPktMbM9#d%4nSr;?C9F-TIr_C$2ytF)kkaNXy$8yPX#_}Iu zUAH_#lXZ*FIgqw|^BU!Khxt5=$>Yns>^wU<*r5=l=%lq#AtNrUmT_ z`l0{b$%J-g!GVH9D1mbdUKivQIPsdM7Kos84TBAPyzl}V^O|VEdqLUDENabkZ!vX1 zNpUA;%$e2>wz=@}5^d?Ws?g|C>}}C@{EbTHj3d|4lZxjsx1-KNr`0LuIy%^O-1R?5 zh0WpU{EY_lp6jD)939{OuEU)Fx4RNuQg;eE#0@Av*SQSTC9BXsa>AC3@XASbVbc5oMv%uH~m_%PeBKm#5H;a zop(QG551UA{42Bze`h`ZO=J}rxt6YB8qgLZ`fbTKu1TB8=PpG*xttA?m{sQ#gcw_o)bPV8npEnDe9EmVC{cB3a@VYTaoT-llR^Ajku z-Q=H1z}(8&*$a(!5M8=1)aes`_3DmuUKUGj%v_Y@fiH*Jxj=eN+E-Rp-hu9(K|VyD zB44M-f}~nRKkb^bKK_*Bs>heB&=_W<`%=hv5%p?=DAM4(izAC?#e)Gr}eCAi$ zzn_0w+_ZN|@!gH1>NNTF0fB2sl~)9rf|doDgN6ss!WX3rIT+Fy?)|G!b7)_UQTtqL z)XmiG(>ZjD^iTA5eYoKqPS+d8Vq>YWDq#!5zK3-)tuakBEiqj(Sxgb(g76ZPE?gZB z93cFN=?IAdx5*4R%Fiq^EhbH{qp6}vZTc2=IxHb9@YmJ*`Y9V zn9$^F>c`KnFl{rvFg-C9o3!B-!cF1z!)xP9I~abFHAdk>R1dzhfHKK7uOoW=ehL#cgBi=?_jhGm*J>qnP zveXcg`qW z9d#>o({-D5H*^n4%*fOk^ovMd-k{&Ezpwwuo{*qd8jAHXhWdu7hDC*B z2w4;o6LKlIV(_b=h4{Xk27M&owPK(r;3fO`PVyFp1pF4zI-nPMCBFqk1auB)8Biv` z6i_LkPryEs77oLuiN{wS5f~Bpdtlwb8G#E)gdH3>C2%UP!bO3*12+Y(3w#jxD9|^^ z6L=DzxEOD_A}Bl17&IbiT+qaz`9Up%_H*=v1=k7I2agTj9sD`CAh=dY?~pSgFG3`t zjY4aNZVJsL$7GUbwq~!UNYg;ukBsZ}+K1ZD+U~mX+?JDYJzIB5H&)kH*GV^A7plwA z{-YhGZO1EBY2!63HGgPKnn2CX&~c&K(87>}kRA9CM~4K5d<*`>Bkm496g(|>OmN%a z{=tKSCk4;rwm*1pa3h}2THKCnf`@M@qdP#!dt{EPUY#GrIO#|32sN$^~D=20643wSnFgS)^NsU930YzQtDTqXET z&>4RAO3+?-Kuv=h2h|Je6(kF?1bz*C!!x-W*e$RX@5x9qdrJa-zz>QH%;$M*3RoF% ziRT~h zPd89!te_8*E4{>Q<{ixC3~8}6n_54JY0+TWLOMWgWQ*xf-6XF$2{yS@KAP!QceH>% zuRBJ3pnoZ5DQ+p;ir!p(HY+FLdh{?&8AfMxhUyMosk1mtV^t|CfAxLU1FlJ9>3f@0 zIm)TbGyIg-cy)8-C+Y3KpthDvcfbcv7dI7e#09WP*h8oh9uh_~{ZR5%!e>&4E|{Otv?QxU&oq1q z)5%lb9o`%?{0C4BRN`vAl2vkD){>h=7Wfi_(Ucw$ui(9Kh}*%c`q>TrB=O~yDOe*^ zXT4BH>O#G8j{h6SO)2@QS((Brpp5uEbG%Wa5lrY4Sc~tZ-&jHT4mZeZ!8E3TgP2;3 z_@zWG(i#FQBaB|p-2!c$U_Z#jSkN& z(h_PjA-Ger3zhXr>X2$|Q-2aieBe0;m6U?c+zSPg(^Hvg`C4B473Q;BaRKQ_SD5KN zgr}p9pbWE=T0GJ>Soy`QI5PwWVR_!I-{>nehd;g=okS0mH_MrD?xN$6LN7I6;4A#V zGuH}y1Q9%|&#YsYFpJ*fos3hW1}P2Ayo0?Hphj2r)}aekhLvw^YNP=?=e{iqces3KJzDtT0(7{TjPt|~Y zlP<+ana)>5+qld6#HvNh+|4$hDlnT>kD2$=@bm$qbb{>5t-Pj=(wVx9KFMXx_P>@>TUVU3!{fUa~T!Z2=^d5b5~q_=$%#~dG@q(ptBVH z*ZZ(+s^A8@4Nay8nu$2uSz9a$ileAq|3Xc%56%E zI$Nuf@6i-D?O1%JSLs`3Syil^kMayIu`cR_lkbN8BKLnx_RI@__r@j6D~T(}N}d=dE}&u~I7r(fNPwb)C(tIl7Z z&7NBW{Y#vEHJ-BXw)NAcsnz_N<9ZVaT(S(alkwY?S%nL{dEI*am-<`w2e_4*G!MN^+6L=aF?Hk#5vR`FaH&1{VJjeXNTrp>6&iI^_IUjRk zc*fh&e_zFCe1<-Y(W14~u(Yyt$A900SKJv5@jlCOy!T<2@+iS4aj(#F7vk)l-2ZYf z=AOWZrRF!va^Ke6#M}z}MmI}!wpV-08@el^yk{1hr6oO=Zg~AW<(czh@>`%)e~NZ| z3JUg9`7yZYV)AQ1kIjV$5RXqkD*tR=277-G6zlVGr3K|a$hnyFKN6xp;bL|`^PZor z%5I(&oz)6&T3Y5nd}&8Aj%S>O5H=+vD5D~*1Ph-n%H-9yY zbIRj3@0l|<=S0qxoF6&Avv*9*eU~e-j71xtU{UZ2*XG&qqIJ)|$+5W%uYD|FDxphve`LX#R`|SyC&&^JAOitCDkQ_-) zy_^9#H&K1R&N%{|UYc7E`utGn?Wx?-VF1f;EiSQ~5^ULm{o+3YEYedjRQ685wv*A@K}#` zb)#d|5Fb@-S3ZiySbVl=bidByUHKmx$Fi=DBsX=2ByDyTxk@n&`k9unk-9dUe2!Y4 z_GpXGF{=`hwPBzx?MCf(3lH)Z^y7EPse6rMRO<~v?_1UzMYYhJdf+!wuqrbH=}bn_ zHRgV=nH2<37tRo@LT8pKXh5>j9$}X7i*UH;j_AFpsd$6fB_525R!wHhLTSFVHA=Qm zuvO=xuqdZ!0{KOvn5ej`h=KFcj@rGOa)t7V@{IDfGEJ#ay&&JDtFkXzs*j}T+#%zn z3b%dAd&+0ZE6V#&P;Rk0*hzwYSJnep=ua6S!hBY>S1(r&WW{q?t@6>JKPu}p&*zy> zx=#+Op$R^xeKLLO_y+mTfeG_myl$PB^ zK`xNKkW6L`(MtS9^h5MN{8^(#BGDo0(&5avx-jYgTUZ6f_3vnR-wXZlV26s#s2uck z+k?fe$;erS*2N}HC8H>dDt;(^w;|{n+M_Ryl{}K{MGw?i(j29GV@XAcN)jLuqRiRC z?+rnnJzQLmgu+tfLiG|Gm@lZQ>wn|9_%UhdL8X6F{27|&} z$~wOI3#k*v!VMgyTYSx$!W%tbP z1XE#Ab_2TQd0aET(!p*_PrGZDBI`nCo6Or8?=xPIHN7k&G~*#O>N)8X)17JS(mJQ< z)2u)C{HXsUet(2sc?T;L+#GpZAguf8b-=k1sz&emMNT_IuI$^GRKj9==O> zxBFeccdBR8AO^Fe2e*!mflh2}2U8jh9$H~ zsGaaz!nlNf3DXlsCsa)kCm0eMCM-%gl@On>Ghu$hiiGV6FB5_jm5EIfTP8*&*5uYJ z@ls;MtBS9R5T@layLSAV_Q`s(|u zvak2PR=w%T`|#yWtG7qr=DjWbuGhPL?_^08llCROO3F)W{eJ&@(T4#a4t#j}LHV)w z$Acd~e(d!r<5LVI>yMw?ep!Mfc)-{9UmJXT^ev2(^tj~O-(P$mopK_jRO-^y)2YfI zeSbXvQI5LmURp+4bb2HB>F?8nGQu+IW{5I47}Bd{l*;hUc$1!y{xtnc`s(y?+s#rI9aR!v3)#>n8@h$IL%U9=X^7TWxmVk=wBp*wB*7{6B z3AcrWxr09Y$(q~ea~y@~3%^kEApc(e^ZoDm z`v>Uhkt*n!PNu7t1%+)dozj!^|6&8~(SN%Va6MpdK-Ykp0sZN+?SKQ8=KsilvHwl~ zB>zW&-YC?2(Ku_{`m9r@GF=%B z11tjF-DkYB`MQ0^N$$3Y%1O=QH6;%hccWx#)d!(;XKJ_N?Z;9-U%pt2R4`-@A zTb?7IhNd?|QK0Cl)S~RlRV6|pEr-fl@m!OmDtLW#@vwy0;tQQ-8z!UW`_!(a4ck2JvcheW>F6gG| z{?Ij`Cx1vgR_oT>&}<@w{!(arc+IlV^C4?O>V=3y(wW;#3?3G&qpw;L6iA1)A*8#B zbY7PPKMyVrHik4LulRb1C!|;Ch0r9Dv|DNZ(U>)*v?H~T5wN!+BRf-9N#BC!a+37< z_xh@a@rH|rw}ud7W8)vj*~WRKV81mQ!&)uMyWy+TMRC-(KKTFpv{WN-h z^l#CD(Q#3;qN1bHBDXWIX&U*1>CFkKleao% zgGm%#F}zWDt#Bd#j)CR4KfG|gSsIv%eNCRQN=(Fhnx2J;!ZM9t zjD^Mw;|5a4O-6z7qoEO5)h@kPZ__{2m(%~JJ4t@ITK`EmNT*{$(+h^TNtdKehe}(b zJ*XX|?W{ej&0?<5R{L2Kr#Y`_u6Y94yA>b$(90qHLcX&%{1-fm9Ql&K#$-vSvETL# z`0BsSf3$zH-&DT~zEgbX`|jf0a*e%z8r5MNTH&`pNu5{H2frZ~N<@12&=i{wAOqQSvFT*zA{Cd%N%pjkm>gAxMI2ObE#9XKbjTA(WM zJ~IbNU=C-%&7^HB1H+juD4BUQ59}8>B5-ct(!h+s8jvgd2F)hV;CaydAZJiyaIN6( z!E=Mx1s~&83PO5?tm7zM9WsM?kRzl9M{di|@uAN`EungzMQ_bk&23Etlb%W1J6emj zoo=4)g)UFmL;qY~)-cBKkKrBD)-J|L9DQZNx`rJO^C6wRyJ?i^1XIzF@bTeuIri2= z3Oy5kC;V*qtMD((MUO%X9mJm3D!e8?^OF7ZfN3(5x94H&!sdi^3M(C!Y&>Qh%yuec z%x9jvoJX2q7!Ap|xuK>(V^A30>G$Zj>No3G>sRS}=vDesdXfIU?t|`?&d#%Yqx+AE z)ihlPT|-?hokI6idqn%M_M&zvucCoAOk1E)X{{QCHbwJ>RQ20rt3T0PVUN7W`RkJ= zi^*e(#-b5xeY91y9iY&!*52fnr2V3eV7@n1H%GTj_dr*uQ|T+{Tk5-WetfK#Fm2Qs zni_T(;tXF6Nd}LhvT=fOrm-dOemUb%;~e8b<2vIO<0s=+<6+}^<0<1uV~$Z4mTG)s z{9!B`)}IOF_^^gdoR5TU58J|g@lBWnj;YYJDr|k&qOe5r?mvfJL)g4?{Xr&{AxPb-FPegRgnjL$w*2F`DkYml>g-Lr;g+4b2XD z7m^<04fz_fg(=`bSfN!yI`P%&kZtS*dqU1~dlwSGHDOrj4Dv1>hW-fcs2R((p-@vp z+fBPxdsthjZKNBm+o(IOd#w}e2kMXM59@#E0}O-NuM>FfF~;%6{~4beoyJmO5n&U< z&WDLiU6`z{H61jSn1+Y1V?OQ-|07~4=Q#^B+MT5ymx_)Ij~p6#Au=aY9u*QbEb3y^ z&8Q^K&LPnyQQBxZ^XHr>Thz0tyHP`;N=F4nU50tvJTffu7&CHj#G;6{5t*F5GE93- zy-ZKTR)fNxO>O?S7x#3B z96M68Kr>3SOtVEZL9d#>>=kAq$tVJL05t-q$9 zrSGE;(_hjJ(KY4@{GRtZTKhq>isxzz-5lCKv?OE&@2`Kz>ENNkk-@&f*CE+XWIp^M z@N(dK&aW9vmk$I?3+NkAKOle_=aBzQDxzBcb^I0n7yK?!*|a2<|N{#Tt6FWHxK_NXcq( z1lqp8a3<6gUxqSf!tQaE6~9{`7oKD_o$amYy-LdY^pc2@ogODtdZjy`ywt(?qI*02 zU}sfvTx0^!+TMXqe~j&e^)yL~=U_fI!T)~)wcO_7We{#+=_X$xQES?-?$;@_a~kUt zvPoYSYpuN?X|7{Rbjg};Ee%C$>9gXUua_H8jqFX3$N8*lB3GWIP!f!1?J6ak2`XXp{DR^JsL+77? z*7k<04r%B!A@V0dZwiH8a=<zqu6*cQm4 zgULj_jy8K6TWW{(77A@M3b0gbWn8OWVZ1iyV-H(l6`HUOxPvQ`&RL73(oy7&{>ywO z-R=Wj>f$s zGs4X%p3lRFD)hEQ;a-mk=s5ge9q>`s;`P=Q_z7;4DL=;B3zyV;roaoKz05#CS%`Xi zpJzH0mln|8o}eZBhopjPX!#RJ7f5iOcI{*xHk(=0WV&=Q^zdR_Rb9St)|^g>E7qCF zbG+ePj~jl`FG7;S`5ndoX*B=K9kZZ9m2)`BF7QF|=z`MM*WQl&h3V)aXF=?n39YX& zTVRX*2y9!sUFdj-IlzH%eJiBE|b}&)D!90DVrsxK6VJ|>Pe<)Kl4wQvCk|e={C>S!rlkY*mtsZ$3Y`l=D6(4x z9@R<`DNdjs3?h54E$__*JmFTC2`~6rRQ`3*0nC8Q@WLZ!GCUJcgPcs_!QMyS3Mk=^ zlC9klFMk3~o{c0cm!fS3hvO_eb~Mo{uRU z3$r<5<9P4S!T?Ex0%Gz+qHG-R*@YkEGOQ7+CmNd32wdEkp%XsgC}~MD#&GXeZwaYe zli?nm!viUXThL565w*c9VX-hzSSXCQQ6I>aU2%f7fgJD~=aRQL1zw@d zXbW9T0-bRK8ib~T27(_Pc_&e>4@S8z^}d0wI1x=nRLL>?;OkIhRz#(I4=+h4Qg~{R z`(G+_QJrOxxXi8oKCR%!cPN-~9`6^lYfg2blVPz`ODm^39*c z8|nB|E-orMPXhJCq78+Cg~{-X(+ZjwOwLcs8w97O6X}RENvu6Wmf%&=Fm2|0=2Ogy zZe+L5K1#ageTag+NF@4)6v48Y+DutyZiYJ}CF3C^!Z8^uGOlDK@UfREQbERl8MiWS zWT-OhWH#c%lvyQnB1-2MnZ8-_tjx^FtlzUXX04^~yD#e*6C>a3lB`~^fZi})lafU{ z&fLYk+Z@lNsB2E2oFx!}E|U}74ZhMH*h-nXrAa*e2WC-eCSu3n02SpKn1}5ql}1$% zTHwwPhThz|pc}K54F!u)63>A57z6XELP5oXW$lSpkV@_j+H8#6kwkzv38Mebwbo*tM&Jtuj%Dk7kl#ddW*l&4FDfHs{ zW&KI&?TM`GWRO10vSfY8ipt)aos{h%VYilfta%f2NQ!X&`th8vIV$E(b;$w_w**5r zs$p5lq^Jp;scA5a3z&Ot$-e@#dL((osRb37FrO_Yx>N2DMiUVo5Ya?f-E4Y3%?j~GUrZU(0~th3lOw+m(m!=mFQld|Y8(oG=aD0jNFN z@spk442_^uxCJ%D3-=@U5A?81YA)N|K zs=&ke$;Ma&F?BzD!B^-`ib|TJ6gmM7$bpZjI^L3dsFR1on3*cPDa;f$77fE!kty=U zv#>&ZT>L`Zkt*ySPHD5mC7Fi5LxJbNHSU+mvdyxKGLNjId=j3Kez-n+;6e0J7-8ML z!AE14N5hyi;UgKTn8&?&a+zYdA`J(wO>syuS#d*Amd>S#TUq*)@A&>9>g9fTB7Kwx zs5<8;QgOl`SM=qteWRNE6YpO#uEtF2$RJsJX%(piMqQr7B)NvpY6RzPv3LXC&9cDO zPYdfqYq^SV<|uVh6SPIe-nMA08d5P7LhR{}x26ea_7QMm4x_r6&RH{CFq%2i7|wwY zgdw8FqDgR7ZiAw`I0xcmdL{NYG z;_u92?)Cw1^&qy>R#^Y_*>Xo<3Z9aUmifz8@Vevh;rdFpGmlyg=|vz~0Uu~FudNWh z`2k2D$GA3BEwQpc{K@U7pRkPQE&#f*omsFxY8wthpIQlIOby*Me zZ1)5XLAY>&a2R~b(y$ayie8EQxCXDsEmO!AY>i{~27T>Ri3+`9Pw5=#IUJ;K@Rd4o zYl>xQ(k!V9*25RJXDTa$lQ^vYhILWG{8B7^CD|{ThPrM5??N?69t?@gIC6(Dl`DeO zxC_tI1W^x>P540=iwFL&@DR?ZdHCTM!zgagbg(?r!v4aR5Pqsd0ctO7h?Xsy>*NfG z#QnH;ILxLt@B=CerSO}6&Qho2ud2cKGCo%lmJyCa!@rE0dn_ux)67|);C|bI&+a0R z8PC__Axe%CEr5}@5-(Cq9CKq?uMFTLlPT;dNLoX2W+za0sBj9;mac-5HH zQY||DFQ_%mY!N@1pG+#d0pDah{@X8XACv4pTVpM>t$F<3HT-Lb;BNWgYHf^jX&uC^ zKpx&=fCG^?(1C7%oX9f5|TdBF%qm298U7x9XdG}0rmRsd1 zaO*ga&7-Qg=6Q#*Q-ybPIF0;t-A!dSrzSgHS_t9wgj_?F-O8}jRF7}5pQ$i2{;Erze`WEBwRIzk;T zB0X@rL*y7s6;eW4#Uxujn~2na5muq~aPijS9r#~*ld^CMb%X-y?t`Ku@PdxOll!SI z{D;raiazmwcZ$9i6&F=QGdGHwcy#e1QkoBJ!CnXC=^!1rnDslxv$pmYB8SH{5+Iz`3V zo~x15l?G$82~~f9%RvVA9M&fdon@SFsN7DH!!e5$+%#6SW6_(Hha5J96+sLuf^gn> zEwxlVIQSZrWlbGj`KfM>ijMBkUKc^gAH~#b6x((aDz;^)_J%peLuX%)#%;c19qXY@ z5bhUn|9AYo1&&nqEsvuy>-7cjq9!@l!D+k8TI?o7vK>^*6J6C@y|_-c;|$goCv7m4 zw-k4@=dJs*y9(FCpFH(6xNU3U4Nvss<0Z{=cfs+z2r67tsOI%?bQik~p4#jK-92CN z4o_#hHGrm8!;{HXb|a3SE7abb@E}b@vGLS3n#X*Lg5v~Aj(4o#r=vg2fScBzBOw|0 zn+XQ!S5${{Z7y_yiS!VaR5L+=5#L$Dm35QSdQ;2Oh9(`Frw*=DVOl zr{!g$HA>*an^z^jTz&>bNp*f1SebM3ALA4&A|FFvFak}Sf=u!3f^ul{CKY}}r_&r? z)~upUD0PJFm+OoFL4WCIRa#qEC!ywhXU(wcsa7Z3E|bmTkJfPldPEJ`f3+N=QLftA zqeiiH&#)?qaJ9qDbd>ks0^#j54rT?e^xAlotFd3S#HDL=e{#)r4I~S_!1(~$vy(dL z5e(D_$4S<_wb0&fWHtBz0%=9-Q#3<<93Rh%7Lq0Mz3^S(_QFwxoeG78o`Ux%`fe3m zDwxf-_z&9mcSzq43X;&J8mN^sQ0YX#Ngan$Z+GE!SgFm6T0%yh3T5(b(HV}o6mmbR z7I#O1x{_-98;adZXfdad`heo#^IWSk8r6Jy5 zbCq>Z<0@E{V_}CU+0zy+_&)sf0i6F0N`YC#j2O`B0&Uxn=URqz81^jFR-Nqqju zs%R7X@GXM7FlgpbH}qkJR2Ln4J{-V!2=?o^8V-g~yn*x8K<{|(C~hk`bFJi_tMC`E zqi{}UMHR@JvL@uh7&;pD@#>Eh{EfzY0gt92agceg$b)f_nG~^jo@3>c$&UJ; zVhWo61&Z5fg{~?tDzX)gp%*1XCpxV#DmN&1D`zlemMAO0D4L3v=nrT|F%VepDmK7o z$-|en1Zv9{TxmyfT7Sla(3**90{-c8QZH1IG}JxE;YFE6AE9pa6#4eg?rgf@|db2M*K|l zKy(O3iuvKxGQ<#U$qL|H25G$Nt-&1hjtSoDEPkRg5M-s~yC!sW6?c<8*nVh$UAmTk#(9 zR(UeJI>W1KhTiRpvP2o9Dh+*Sp=ut4ty@g*ebxRjt|Sm@QdC{kQ_#ANQMZ79)J)wG z{?%$YSTX8gb(mViUZ7D2smrJx_%)ZZy^gDjRZdkZ+@ly0#kNAX*#`IJIQe5O;CS7p z`tQu%6r*|s8GI3Y*ihC+8Hz-72Y1nZ2o*nBDW_R$Es(vZa-0Y$EuD%oT|AfS&J4vT zh4o7n;T!h+)I4+k zyzdxEGH2<)y`^VmcUFeW_|A2oPU$(9jyj_itJ2Euc~Bb8;LYEpVmEqKmV;c)hc znztH0#ZD^mF~U`x<65DU+sj#RCN-apGi!I~tHty%(@4vx&ZCZTr@BUvIh#b6)b+euVt3XNncm01Hauh=+LYE8IaHz+QRl6*9j zlRt~sp>w@X_y2RT%IYKm>No1OzpVAFt*t%Cm|Begc_BUWCDs$T=s#E!tX8X%evpd3 z<^;M{$LNm6;wnytSzNM@o7f@y}`$5@)f_F&%spKW9bMKxSP?t-ALb7 zgO_w-|ei|Ix`Ts;dQKKwBJLiYz`IqKG)e`){NWeJm^_{SK&4hM(ZuOW%szoX@yo& zVQ!Kt^N@A<1+It9;b7Dj$e<>E^1i|;^V8>4!>h-!v<$aa2ewkBU-aX-BuNZ`hPKwb z7$?_xuRmU!SaC~+sNI05; zU*j>yTAI`A3?qM{JMYwBK2O6V^Tbt$_of%CrI8$UXILvWVU2F~s7vxZ27FN`=~z@1*8_$V4E3u#WhKe2qDw&v)9U&sp5SO^K}MCWVw=qIH|7kj`yK{p6kjZ zgTdgu&k8P^=|vyrH_5i@wkOuT*6Gxf1^9uck~`g!s#aW_SR^a1#4%$nG8Z|DqB*AS zQ}ND(5cH(jfD5TU8FPoNjcgUjU8_h%w!xM{*EXNyA>S6pRH!Z$^Xy-fp+~H7F3}10 zr;hxc8A>hI1xM-8u4h%?1LvnXj+Rx<1ZOGg{ds7p-@9tNkGM4ul7@LcpqlQ9LOQV| z8t>W*@`F0i!+8k(ZapNFBg7p4hzL)@q+ zNM(Pi2s_%t#Ayl+!RE}((l~?drQ_ZnigaCi?^-zBe|YM!Dz5fxg?f-<^$IJBeGrdJ z;LmzJM##`3Ak-{nAAgPi--)kq21oJjk|ZX2Yv{&oqH8{;WHfZzG5l0VTzCgK<~Msb z@ks0FwajIjGZ5laPgcOsJibh4&M?6VD4EOiDu$*i^co?#ia7&{$y+!h@E1Pgi21}3 zvt4+f{=-l*l@f$a;JHaeSwgvJ5Ok5BSs;2*1Ng|F=!DFJ`*WB2XPYok_?_C}PhpJk zHMQjvm}n=NHafj!*dk-tCas%wcjoz)RHeFz^vlMLI?Cq@WY zkU<`uj`dAmej3#McVq?Dmc2|HAPmgg^2ZE6|)RBi)WT+*?hVT5PrdX&2iM+B(>T zwo}$&)?8>-p~W}x7XH9nBr42dWz(YI4djig`OirKoIoBxDo&~KmhBd!B@bf#X8P;r zbN|iVOX||)+=pZrT*#NxPuYB>KI6t2QAmC8zSJoAWxq87NM zt`%;ChS(0xejD34dIrc><9#f$b4 z&)rkDL~qvoWuUnnwKug#Fh_rj>usT}GFQNK>t*JF^{h3lwMeD-jK?AyDvhN$jzr4C z#s3wjGe-{~OGahwfOBpLE4ROyA>OjS#6Ncn7v0Y*X>FUy=7fxFwknuOr!Z|k#}s<2 zwHx#3@yw!I@wY?q;zhzx?r4pIrkq<`$=aI<{sht~3Rv~ZZ8_F%%(e5F6-C*D*cW!# z{peY&D7*Cv<1^`5T5 zP;%J5(E;fS!6%9QpebZ=e1mc(wf#kHa)X4XGV~0BiVhZbEX?Jd^yDAL!7?g8nMB9R zBxBf793Qi^uyi7Kw2URh5^kw!`3=9*T+%WI;RrZx@g;Tg6vEN9JK zgM_U^g)a*KEZSeRhkU3K(xsmAu7uMmIBj)V8*r8$#4LX^dqV(ZpFk#QbD4v6r=!!6 zxmk*%ELljy*Tii?0F22TUe6L3|~~D$8j8n zXLmK{wD4bjmRZaAd=7)vM(Ov*(ht_&cw0G}q!Eq&xIr9^Z#oDdKvw+I5P& znaW%t@6(%X!d%4%Z~2RooD!wC0fa6i-S(|?0~D-K4{}{;P6E+3D63aQ&zPNrikq>H zZG-2e9JHzcZso<9cvdX1pDu}xz;2%hU(#Rn1!mV4)^5K+PJ19Y%k+8zIZr_Xw>MOf zM4$5;&fpBMoqAcs+(JpU)SjAX9aD`ox)KVZAN}m6tZkQ2DUU}Xa)-6wZB~BQndF>c zig8mIM=oEM@P=?M>&MmH7ta-NH^iwCkTKtqN>BtdES0%$7T3%GQ70%`-MGj};qf&q*#}H$N@QOl zZ->LYY=Z(QL-rA}SWEdRRxJUn`^Atu$3m&hVPzR69}G_|P3Gq7Hu9cKITGOR#_<~q zWqsjO){ zp(r`b^>ZlK?Az{zRI;N;4idT_!I!_t9y|)yR!ffdJSJ}^sFy#}OMT#SLE;w!si?}*TRzA-JXhe$tRNV=&n>oZme544 zF-o*abOZWYFzfi$5FGZ1uaX-zin+}Zy7QkT;Se-u!Q9zQZ~qb9n0I_6N^|J?Co)Ou z$~F$CE21I=p^HQ!xkLZHBTl1bqSB&otd8okzUwN85`6S7g%>uAY>Gs*0jtn~96?E- zVGCzq#HpS7Yko9{ z^T2wPH$m|D=hD4jQu4B-DOGzP??(1ze=3aW^kl9G-V4gmz1mMz5-$u!mCyj%^F-Kr z!{}ISf=Czx1-^$U0s{0)5;W(bg8DA_hkjc_fv?~{^bRVonR(_B=9?1EINzcDZ>PiC zgt~16y}jSj$5bNSQA-y*28Mk&^oSn6y5R$;>Y9<+Qi+*ZUD8$R^3~5QNC8vYG%`P? zu_o(J_fz7waLzx@mN>^*uNyo_r!$mF`3WoIf2kL4uvUJKtNJ#Yt_oy_u+B?GztaNEPA}?66VG%GHRJ+_7MnT0Ea$!sJpYb7 zBQHO_!M%XB*dWpmD!D(hr{0Gm8S5ItO0El6r0M9C{CLcYyw6(Br#6ylJWN<_qBNTT z6=6NqO&@2d^F7zQQ)tnYj(=Fs%tY0&$)>kmV7A@TdX4nJ{zb`!%L*G8zGrstAiuG5 z{?1>SJgqEWb6=ALX(Mr`Q%-F>n#;|D%zu#STfVGg=Gr=CdYCO$;4qW8;%v7+=zf*G9iWnBGR zp;TLBJ!+kAU4b&MGn19gP#lhvuTalg#Tr|@foqA7oT__8V~Vn=DI4G{pG6At>ip>Z z*u1fMHp^i=(>b~GbDQVJ=M2oLl#`G1c$nFWxAsc*$?TyxfGT9S$*z;#FgqOAa@Fi! z*@JN3E=5goAp3E4LbetzU3GI`^I7u?b2?6FuQ@VjH16A#xNo1}7%Isr$f<*)bvZTP z{M?1P2hb%X=ceF~6rPeIckZn}edyCIerCln1Ms@Irb=@6uuOE;Am|Oe- zu7idyN-wtQXsWCe)M~4!9d27=S=|;`ov;Lp_{!gA<~#SVuOIUWtC@0bqBd&`E5VPI zVMo@{-5@A*v~H(PTu#Ndowedye#6IRfho5h-bOsDMH|YS10*qLLfnXE))YtQB#wFF z2ol|IGl6*K)IgP+&6&IzN5oE&X`(o{Uqb2m0>0`C{=G*vSHMhTEl102_^jv9k(H%m z(T3UC5RTSX)NyU-S=4ZeU7;xcy3-{oi)JZ=vt~3oA#prLBKgCU$*c<`t8f{0%31p& z);AWkN#)T>u3=BB0=+N^n#Lp6Ha$r&?M_O_(R}~>wRs_Vr%2`QZIM}4=LYAdBk5UF{7d7qfIu7yQ#f%)sE-0A3_&NEkihkEMd=^f-Pr1|G z74C+7CT1`jyx}RLlGmGV>mpRT(VPpm7W%R-+)-2jLwEwSy-=zx_vkOyXO?%CGxiXU zihH=STQH0L1bd_dNzj*J2p;;Xp)sL*xD8AAUuZ)U8ArM^Qp7PvyhH1@i}dLAq!50? z81aQ_>Uk=pyHNv;<qw zO4M`X7>$NAGS&V%i|$Gdbr$15V!47b%eTCg*=JyR8L41aGQ6{4BB{}6D9ek1A%I=hIR&1+E6yZU#6TIB<`swUp&OM){f4yKWDXx>P#;fx%;|jPq&D{CYf@QC6QqpZ^k~4*2FWGigWlXpP?|D7v-b&weB&VKhl4 zHu@y-XfBSR%&-Uekvh{#IP$B$eJgX|>3IJ-GJowTtSR&nJ|m~5BYO6|(7J^5cD`qO zyzv`{JN-WmMmck#TCiWcP{WIVU*lC>EFO;-;vV#J8i{+0C*nz1 zN@r&|l!1rzeoDkTbW&1DCHeFuumwZoe=a}S__)PiHub<#D`^LR7Iz$$o0 zV#86ErA*2fR~hkU(!lYEwZAlWG%8`WBzX zSowANQ~7LpsVrO84EM!(c$vNBlel$Reo1}`$7iVG2Sr;&y8K`HM|o4l2*p%Iio!>6 zTb?HmP`u-LPT+bwC)di4leW@S_OmQVHbCkjb7rKZM7*C=o@DWDQBTpuz$T;{?Sb#_ zM=voY;3TBt+WwWKiuHw1;GmN@n$P^Vq|kNen3nmKkoFhBdP3t@mwmquV)k0H#rFCI z3VQJAUWMZAgP?=|Zz!WzvFgfU%@RmLOIpBK_{eL?H`tAC%N7vG>Mux`Mh|(i@GnTC zx1sz8kh9byP!ZS+*Xs*Na!p`H58-HWh{WVK^budCpVm>*lw_1ul440b$4gy0h;r#j zyeNxF=-DhiO&-y4j-IMgg;You!gh!fkI@AVXK!vnPxvbE4E|d)&uXPhJDTjkgXrN7 z3jW8cyOhMWYJM*2oKsmdl+m@nMNaT;j{AiWGIsF!-o#mPC%lX`XagVmeD=vk`PT?F zSTp*~OCgBo__};$Bn-AB3F23n=qt&vzu>o?^W{PEQ!J2MU4DLoa=%2uP{BBopSDAN z$b~5ph$5{eZ0M;F#;^HX{0sbhvA(+$5W*^U1%$oUBpSR6Ocu=*-QtK8ireGXK0v>( zGuxsS=?uVz}n1ROW*>A7nHq5|p! zgmFoy>w6!*^*UJG(f+}Z>z<(1Yfa_C!Wi(u@0j0Qj+qv$%&PiDqcg~ZE%KbL+lP+y zMBnAq9-2{!xWkqf_(suZZv;DUBJ}x=)R-60yO!ZsiSzr&|I}cW*8<*rBGq{l^@mq* zI+*NJ!Cs2yu7FNpk@;y^M@7!tb&93zP`1~29xvKLH!x*=qY>DSh)))&@i zWV3$es<(#ZxJhYkkW(wCMvf5XbeYvJN1Ic@_iU@dde{2Sy2ZNKy2g6Qy1}}~x|{#V zwg%+1;8`c+G|Cy5GYe&RMozG;F$AS9aH;=;L2eZU{*3{l~z&O@;+0fe{GOW;7)nCdUkX<`_ zdDb^s;;ajq^E1E6^knSL7@6_Ej3pT}GumYkm5~vj5s(p)QTfIEB_N}HM%|1K8ErGB zW?alD%qYvSWSBD2GDl|a&peR1J5!(OpB0xCkyVd&1y|YGTt;#x)m7i6Z zCCJXrs+QdtE=aTN(b=oAKV}zX$LnSKRQ*8x2YsTUAFt}9L1>(2JYXz`0x`!_XzF0z zY_^#jS*}~Yh41~?nwT>=Cy%7tGd2+^x5w;qM-NC1R~>a>Mg7S-bRis~`_9|W7hH}x zXF8`ldpp}W{UN99BMY~yqt@5C_-p$)`%e2nI9C(xW9$R$?d@ZsQ0;+gm17q=`Z<<3 z?m7He#dUE`blzZw-jX`(S`vOg<$ja5ByStcAz{8RjHsH_{@ars+=VRRZ^;jC24ksT z{s3-8!ct4luSZgF6l5t;eiEGX=zJmfbQZEs27^U5cM)3-umvNiztFy-MpcUtRn)OXGEE3ZJ8ORC1Ov%auV}d|dv3 zZj`K|6M3`aahv2-gjLpq;rJ3J;d^>gjY*QsLsQw9PO6Ij&|4~IqtM{RGrOIICccbL zaZ>3sY7&F#tHzaBQO)V-&)aclT=VYm-eq-q-+Rt`l7tTdYQdRw{w6_TdQd!!nVgbW z-h*zk2d&@nvLNQgQnUpB&<{(*>+-I$E{fw_J|3Tu)He%#+jExw;up>AwFeyjFh;+B z{EhzAsI!eCksw((72lzMV7I{QfvwOZm5I8F--+8w{*|btzmR#innb2~@@#pMVwS?F z=%#$2RN=#xt6D;bO;8WSaeYVqQ}EQ_UxJSXuMU13TtgG1DGGLQ5o#iEb)Scd`vv;J zJy^IKf`Tm`eMCy+ z+{o&1nEORek35ghba~`7?pcc$eMDrH$mGc3k&E!4ej6DP**x;k$eod=A}>ZBi+mL6 zuMOA6YUA-_KheI#GEoXSxt?ygZYqA{pKv7qqdTN~goF8p&Z&!x@{795t$TR62kQcL z_q2a$$7pM6jgbo@8%63PHbjh%2#q)r-alLz{v~W9eCLeNJ)x6A>x4eUwOThMHbffo zRx_J-Iz%%WU$R#7Ciq40^I#KO;0D{#%62*%e2DF~3tqs};NRJHQ*iCih2C2|m}N39 z*H!9+xcPrnPr&_NALjC2)ne5%yz|!~iqBBZRT)+3$mw3Pr<9O|X#l*+{5W6z=d2C5+mAE!>1LFRU%ZzIu-v@Tcj`%C_AL4@&Z1Iod@5Nu^=MnLB z;#mlOGW^GHMuGXnfRPQLCe7N41BUGCyi^ zRGX;UQT3u4Mw$3DKja;b)D3~9uhl)(-q(KC?#J2xx3;>rr}leoQ*9@0v{uUz-AUV@ z^Mg=ZSKCnAPuoY^SUXa?N_!MyNRHOZHHNdqSojX#!ZJ9eE7X0~y@D2D&?WO+pJBF4 z(S6jJq3bV!ZP1QwyOgaeVBgeYpZMwCLsIw?%F90OF}`- z_Y)L~kJ?NqC6^!%Jkkc}^x9Y2EBwz%XbYqG%P!ja{6{mbQhO${J|qcEq;KTWh!zps zA=spcor3n@3>_Z&AY^_>Qpjh`6wMslrtLs=YxSgU%_dE1xJ%D@H0B zDDvbtrns&+pxB@|itqlJB3%)V6Mq;SpV7)`$_dI#N~bb8C@?57 zXlT&-pbUucBGn|=?;llVsvva}^>pYSBZGeo9?f{OF8E1sgr=G%4)Tsd)0#136l@iZ zrm@BZ)8rD|^=}wqzNptjHIGr>RQ;?U!3pf6DgBuE_2{Wtl6RAnPle38iJ9tRGB)F0x-`V`Vd-Nd3Tl8{oPeksTmi zHIgkjKwh0!u~B|p9;9fcSgLrzSR108uY9Kr37QD|U8@?ZI-@eFQq_CZFVt?eJa~5S z>ELuiw_9pzX?})haYyq6()~w`U2|Xaiu3tl&3Vlh%}`BKjU5vH`rv87eb^ou>NRY| z8R~{=t=a)m|A6WuqsG6g{i^YhR935wsCKKiz>+_&dc?@#tL~!is;;eWsh*-f#7Mu6 z(dG~Sf2n$jdcJx&-1xofKh;Ck!_|ZM%Wu@()oRG`uXwbFJjOCrS5=a#fyx^cr5dOD zjc3rQ?!c`&AC#o(t?H$!qw1jgMKxR1gsgxYszOy$^><`o1ggJRUsE^YnCunYp8foY z*@H3I48x_S=2y)u%|K0>W`X83@Afav1b9ljc<;Yy>S=s6(PR+BYgBL`mO@N9Yq6zW~P_BhoMj?~A>=E@?)0;&{kn#*ibJ@=#9oiFOmt&1dkUKjl=T7?Gv1xcR^Maxm| z&LNL#-`Bj*Eut`SnaEe%5-;I!@g6Eduf_h7@hFOSlEyj}jqN*VTV?2O1Eno_<=>G* zT2o34FR7X*sYA4slrbB;MYZWI&le}UPby*mz%Ov}<-${}m8AjG{J%hgY(s+PY&w%S zNQ(T9%H47lcJ+O#;*ma#KfE0dqZwQpLUgQIDJ9!045g|Rz3NY>)tjO_>_b)7i0@Kg z`I(wyJ}!V0Y8K_znHu`pVT&sJrr@UM{CXQHxxeUj+{ZIy~(w@S5HAPeJ+rh@55{4!fo# zWv?QaaS2t44R{LM3WJ0N0a^IV-l6-RLRBCz;EVqZI{r4n4Z#XHmq+QxD*YsWr|H8s z#03|NUcrJRp@_jO)nD^CXQNkqRr!cD|9G_hUD0D*Sh| zt(=uD=zp)nxw(lxat->(3wX3uR2V97um$VYfmqJrr%WZwgf2R+|(mB zpoZ9iZf*e;io4W;c2R+S zw6Y5HL$gahqj#)YTu3J-40g!YB2CeV!V~aE9z##Kf{%RLS4RDP&lOxg8_~Pm_N?Rk zZBN~T#b_-T6*NQ3yo&D5$im+WPoPDPDe6dvr)SY}7#srnHD~C4Sc;nA{pkkZJO}+{ zTW>A=?7w>#pg)@Joq@_{I=3>tMpT&Zy+Osz=#lJ#(UDDZWoLNg+vzC(fagkp3n-=R zd%7OG(ZiINb)@RN7@qwQIv2;$Vp>=!uEkANgMRx<){pb4#n|xqNlKr8?MK%sxn7)D ze8k(t8-iDAby3|S8~wwy!qe#a5(=~hUb?#HQT23$Ti=QvR#*JlU>wYFyN;$V`D5)~@&JwL^NWT3c)Y_yYc31_P7{T%mADGn{6zg|!% zC>MMYycHZ4?7?^6Lm4(4UlT77&MQx(l%B#Fk~-M-=u^_l4MvnaUbWe?CR z)+x=v9r_*JYCT8s*{>?4xIet&Kx>0FH!Y68Z-4SO1{ud`;Fs z3YyqX=si==2u*@fI+@=008}I^pazX*JGL$v%02x{`r*v1O`^a^{DVX2rB3Jj6qK^t zaroNFz34=O+d9q}*3wpJSTf3Ll#it3JoU6qNP8~;M}pY78eC);W| zldCzGKJ$HuYV8QO7vl%<@x6!Bt{v+CGaSPym4*EObd=E_&=>t&-U1(|fB7D&y#1;5 z_Ghn!mZd_?Y0SB29Hf{Z;4usbt~(KW~m>31`$f8CS_8(jUikF zTwkLVSVqS-wKSIs+A-8KbxUfNXi7@St@sz+@Lotxm!XLqqSJVxF5CU6DSn!p~k5S10@;GOFtAu7wHf`#V_#=B~@hEf8?i@sHj@;Rfkq9h@b1@ zW8FqIZXn8`t))|`+I?YPzC!7+f_<VFFaZ>m9eIN!E=wm zv(WvWJD2|b@qBsy3G}9Ed6#nwP$tiXp;7E;?6^o}biI8IlnsUbB0T@UY*%dE;C9Gt zQBmT*n#|LI)gM93RjX z-KYL~+VQ~QaO63ZPQba25|R`fITLu+RA((Jt%ICXoomq%y>jNlfscYBG9B`+m9&YT zdE=q)9)nG?I`4X(BQGdlg=@BF{)qg}XfV?NUU2}7tW1Wqi z;ZD0_y(7sn%^qTZZ(Bhcfv@dE&XOEE%oShjJF3e6TB=)ikj&8D9Br;JeKuv0<6wsp zJloXUG|RNX^pJ#yKTJJMznji;yV6u_$}>sL!K6EMGXG>=Y(8&3YCd8fU_NWkG-sQ$ z%&*Mb`T0G$4pwuy`5oyFE{OjR_}Oc&W@&6mv7}n07MZ2Hdjz^AMM>RB8eVvO?7U@Yl zxb6Jte9Bf^kKTL-U*|iAIh#6bI13=`UveB`KTLp>(f}>WC;LnL4g0_L3+%67NN)JU zzQq1JimuJ}(e_UEg;c`_bN^y)b+!*-k1nzAg;+BOdjDqoD-tm8Kt_IGzfOhxHji-= zqRa>T8~X|S6qI3;?6XLT7-t`3Z$jrJ)t+E)j9M(o-WvMMR4VL~Iik+lP4>HFNYr<9 zL<93PIUEZe>l}N?0Vv@;wITK4XXii8=gxp!b?&s>acE-Nk@S#}+deNIomvBOFSnsJ zIhwaSZ#%h}kMk~YOgzi;Ln9xRpO^PE?_k~=^w3dcn0WF6@>BR;NjhdN9N1g(7gDJo zk-q~qO(7|nv$^j>ev+#M&DE{^;(ULX&?R$i&0oO%XY==wr}+sD{w{bpetF|_6LSll zHs=gyIl7%+A?P%A^s%2K&*DMOE=V;d>l|xytH%1;QrEJ=T+>|Fyxv^R{1}FH5+v&1 zO`}b9;ZdiUhMGp2TAGHKHk*9SQRX@vK~>Gu%%{yBb6ZPS%N~o=+TA+Ox}OZwVCXp8 zdF+QdnK@nf^hxcfZP#o{w&FDVDYjr0M{CCp$MLUOAhn!h_{=;d?Ln8@FZV`nB%grM zD8ub}$tZ|+7T>`XqP2FqUcDKUQ(9?@~<$gTYi72iok=OkTIroUT8Ld;^=MsL|d{n*0)=Jm;|llO(^8<9IQw`#7Fqi`yp zV5PH?ec8kr<4i#b^_{aT=_A8Y!ObR{qsUqAltAcdg$8atStUDiU*^W=4d#=5ge~pO zE6r<2j>BZU@~iVRNKO5SUQnMO?i%FkkG6fSYo%*D?(u9|h;`i4-M8IN_Y-$4I=|Z< z73oA{QPqnJN7FSCF#|k=4rmo>C;#FZ#czuT<9vKehhZZ91u>(>c*d$%Wwp^_{=wY# zA!IQR?weBjjT+_^vABZlWUxESU66I2mb-E4`Bb>MeYN~1N}(e-+%}i*g28qL_L>6~ zyu2cso=6qO=|vT@aRR-;!{er75?&ceqSXL&yZdm#ZDK`tkhwzzdhaAwA5l2B`a; z&1PnCOHhqo^a3=~pZ#m2GQJT|Q#hO+{Vrj)uqoa1(e%&@0=wWT{h4lZx+sCp{6g^* z@gnFm)zE09i{FbY#kD2VaHB@xevN`rvsTho(trfGNs{e&VSkYHlI)^moiBMU*-QT0 zI#$_E{IDN!$Bw`a+gRFA+7FknNamDomv(_N`9vBnD@Ik(4Sh&$=~GExbQje~jHn`c zAnqs@h>fBO(IaRK(hb?t5}`?O~GbV)Z6hcr=wJu?ROGR`cHmc zpqe>gn@Lz}{-@O0gV*^#CBpeCnsTo0|wX39ky% zg~Gs0VQgR;w4OV#di+I^qHwalRiXq@v}lBAGpwT3qF4BW1@;sN2GFcFUqAZ=>zFOQZSCACuxC};V9dwsYE4N zD*jfi7GJ`LoR6la8;Rnp;9>p|Pz}v@eg90sd@`L?8mQsZ z7GK|O=JaPOPvLc3%W-s(wZbjt_;=wDc<_j|@F}fqM3US*bPj8w2^?WfyPP9w9+#zP zARaJ3{V!u~9vX;UKGS>#aommIm>b1^MpM&h1<$8CdZ;S+D0_36iZ|gH{rylf-x_m$ z>HC>(e&?@=@nxvHY6)6?O&EU*;im>$d$Rw0{{yHXPWnHAM`NM_F^{Ce?qqHFQ&l)h zj$t?I3W0(%tTv=*QT9Qr3-SHTin~24tDVqVromB&!1boU`TDuy2A`pI_=xwT_}+%9 zd>($gIi&hEt0*eZ>qXRCwkwxjs=b?j`fVcKiFgK?`*%5dR3iN?FTsfNWRa((H5*I^Q4aJY_hF@Rrg4n1modrs-tfS%*Kpmi+i=YAui>EK zj^VDsW_V_>7-|~38Alq&85bIl8_hP)RntC;PbGnJXt=K7>|eP`}V62&ER z2BUSB+4OZ@SHm*OGKVCmp%D9yTfXpBZ2i`{(z>2>@%1E*x5^pM*87m7we961Vk<0y zTKBDEsw3YKfoA@iGlH>m71~&7Uj4k4c`upS*2^E5|2jX0*+@;-bd&+tU1ml+e|M$p z8+7}P-E~o{B)A8<>$xQ`R+R2CSGfBX8v4twTvFAn{C$S&jjI&i%4Krap18j7-yazN zL&BQW>K|zT4cp@De@K;MM6AX;C+QEvo34Q?o?Aw7fVEx{0YRof!+B#Ego=0m zD)Osm2h2pvbSB_qKnt{%$A#C02Za{lNnsoux-)^#14U#S{SBu=i#G9+*eg~@w34Bc z4XSLMa>NcuMq<-f`M%m0xdmS2&#mOEu$ znM7`+&k-u~mu;0!gyv!Ae4h@lVLarv6;MtTVuz?wbWe1XTJ&EKB<{cl*(u5pHA1UZ zP27x8)h6yF8A9&sB*|Zr7}^G|M2@*9pEvA6`gbV1Vi>IUSQp(})PVtn5Pq zM;&DwWwNr7QbzJ^EoCBn{`VwUv{lZb^EFi2QrSbfPx(UmobK0qvN(PVnh`WE=m?n~ z4}-E`_kRg`5frUTRaI4KRi2={p!)FdgH(-FX>=eKs7AubU!Z!f5~}TF^}eFx-;N~V z$#iJ0tHo@S6X*!u1vl61)`W%p8giTd{F_X{rzJ0CVGEI8~Qgzy@Wx6g-k4XG7UNx$iDO?^$G zCP?E9E<(TX2U){igA?Fzo77HqCVR)Cey9GZKA>K$UaW4QuA;6~g{p&iz57*T=%Msf zNy!y{7PKsA6qokojyxeHxDIUYGm5o}{)!fg9Mqt@!M zvn`1%na8;+Z7YqGx+GH6xxJ+U(o^L4mWs2*4soy~NRp2hu^}uUyQn|P+-WFeYoNe= z13Sel($L}gPAn0dA-@!el>9~_{=m_GoSgZ)a8o{Wok!oq8n`uZJv8u{WEyAC?YSJ# zJHX_>)V~*+!ZYyshWLGkW4#%-JKQMwfqtaK8%E?t| zPt)+FXF%JH$Dyk!HIvTv68~x}46ixV2G`*I7*g6CFY5}l`m2}~+=21cmx|GF=)1?u zM5yi+DAG4Fv#(##j@;Z9RQ`70<3A2N`93-*4gHD!hb)BDvr#n!~Z4Iv$DV1RkGS?waoI z?%tfs!`xDLHL_dPP-OjZLJh$Y)xbTLe4$ggp+JAok5|-UFD(Ii_%j$4Vk1Je_>o&9~3HOb@ zsI;g*d9D7%KSF2ADeh8oz9f>Zb-y%`dCpnb%WbK>Jz@3J2?Fs=I7m;i%rvGoy>Fu$8~1uaHT1p=L#Y9<>gx&TvwI<@hoEaTlBTyN$Sx8{*U)#=5OO z$B|F@6WkRq@JdwTn+Pk{^1Ec*6kYhsi>&E%kT3e7l7C+D4Xdy|P`NHtnkuU@`Yd4{ z90-4A1-+pYzDiaRPoP6RhY=MmI0i-Q1T)shf<&AR$*e>=Ly@*Xta=PrtH8e}^u}Ri zKWhSd25h3sX5jh)Cud9H1p1xNU~B!sr{Zkj$-s)h9;|Q5MWv#;;t25swCujDeZG+l zXFYWg_3u+I4W*N%16WA}!xEZBZ$3o&9&*ri#*bLZYgQg}VGj*yuY-D9447i74djhZTJv#nRke&+svRFgZL*@|79zEhbLeoI16%>0118dMI8BGa$~?gUnY9C}$>+k)!UCa3 zsDe~G7azmrKx?2hQK`FHpNGUa^~e<{io-IZs_r}-yHs2Yp{ zAXD|D`USQNUMp_ zl+wHZGkAV*&0w=yj~?!4vM=aVa`kb>&+F>BXdTX}FR1UTbJQ2rZ`Gd}$0CD) z(xNEnk6Lk5@B&ng&fp(Ouvw^?qq(4I#u)1e2_j|V-_Z0>aag^uePNEUN#Vxu@Q8$n zw1~eV#zkI=lxXdd4-6X)A40}$R)QGT-Va1`A zP+?dpuj6{?i_nds6GNMX7KD_BI71|%&l$V#prAOQIfZ^?Kl%s{Y7Viw8)+E}f~E#Z zgZeW5)>DWTkL7jbTV;13dhL}o#A`4@d=7mYO5mYK7ys^c z7R@e_7Tv?Ycna>-e$Pmc)m_`Y-PPG;%U=tHat3wt%G}$zD@jK_i-vlZbB;628R+zJ zT48Jd%lh#rGFzKF2H<2&bSNDa_DDy8{Vl1$o2lhW(aPVouVZz$)84}_wA;|7n{2CX zV{NT%!M4{q*K#&P@SaFryKPPYYqdit-N#u6TBnhBn`|w%L|bcG1=bvkhzj^3>rNE# z2d(+mB5QrtcK_z6P`v+YyNf$l#QJp}9flyXZWoee>~wUZ!hedK+b67};&Yp_-mRMJ z#cTVFY~%&brPRT@IGe*DPjm{LPaS`fNc%rWDt^pBJipg){T}5N@1-Vvj>`58-cPB$ z0zYpP>e9O%x2Z{2rHWpg{N#i5BLZ@ha|cnAo{_sCcQbX%d$}&^(qD2v=NfZg=RU>p zxPolr4rC06^7`d_Kq|7s@th7LUHK!` zSg+H;*Loxzb|zbSIgaGiJVjnM-$SYHhUY!vy?JsKdHGOkszbDS!m&}$nTaXba`j`5AeIvsvL!P0vv4OFNaXCrb0#l61ZS*zO zGc7Ycr~9HY2b$B&Gt3jneEikC)%@8UZHcrbS(H5UM9X-~e9H{WS}waRl^jv+tixD~ z-?a*HT_oV6$jYg2t8ME}U*(}K$X>%Px7VY)vXQRD2f8bXj@sm7*TAD2<9NaGaRJBs zLVH#FXWJ9oNP1tlaDd2iEY?%j`qqDWrV%7j?lsHJ=S<^FWybZ!E=GazhT(u=nW3)X zrhc}5G4F1v{zrXNeVV?8egqk+193WZ6hEg&&=1%&)E*6 z+5RWUd3|N+WxZ#O$@wYg7-_J7*a~eU@vC35Ploy5jxOH^*h4efuOq3vSI<9^UztDI zwZ&E9TI_z~j)KD45EaHwIJ0e_vV?M}Q!uC?xV@m%&S@_g^9;)$j1^qkr0 zV)rC6zje80^cS;|_)U0juP zp}HllN}A(z*b7l3uGF{G4Xe2ebde{JAbemqWK%ILfu#{c3UqwgyRQi}C*hHd#)Ba$ z9S(uoOAT-qtDeB()3CG(zOovOaFS$&$4DX)v&{~9YIz>GTe!8PZeMWy>gvQfJIqxG zJ8KL3Z4Ei)f5Q!$k9+zsSAD*b?v{@$!8HW3!Y0>o*DY6!yMucKWUXPWAkMLOPQ$OO zbbrfAq93b=uAV`jrJmiebxx3C`qJ~9JtZzs76d@@>dtEzLRV!|!PbHt6iUYmilJ~# zgtxG}u%Iv&*6>ew#IF_wqVkFIX2Ttp!_sa|n#KXx6ZJ{zm;~o1yXY868nfZ@l*0h1 zUl?8Zg2b5Uf=!-pcyD_54EJLQVNIcEt-u*`8*0ROT;h*8n-_2<|H8TKSI%6mNn8Jh ztJh(1bim(pj5B{PDrjrzf98-FF3hb@N9jiHF}g}ObHnn6DxBvd*}|h-ufBTEWw`t=xntNy zt33x$C5>S#R20;OVW21)1>vq5p0cG-$2y}iIfXKy9W~{EExT_B)HZ@KPw;$8kElinTDg zCc!Q{%72ORNynC|Q7zO)Q?{08nac6B5f=nXf;gDJVysVL_p=m9_C- z9H|3JdZ2JfMa|I!ws>!zy(UVAK|J;!Q1^tTeIUR8$42;tBRGg-HU-T@zq0Mr??TXe z4Tm>#fQ*12pzK^OPaqp0yy7{Xum|OODtq#Z*DzVGQH}o@=k-`PI)`xv1(HXlC12nd zlw*PAg=GpDEW+|T)aV;iv+v4^+emiC_w?Ydl+R^FH>mtB-L=m=ik=#OBQ#T9Zb#Bp z`9vyfXeJ)9)&7K7=3hLDgsw^$aYNv( z8_DmQ0f|ChXfJTU&3;OyvlmD9tb&E4+>e9*dAr~cXQe#o7fs;Itt(6~d|Buyloqw1 z+P=7GNzr|d?o{tM?-B0Xl!Q#9I&ob2;Oc(=Y+8=mnY6sxNQ>(Ea{3v!?v?1W|2&#xgh zpaDIpO=O-dAXDNkx0VWy@Y4|p+IR88 zUZ4(rfy*huX}DN71W&mCu;4d12pxIGd)&L7|J=l1ycOJssvH0Vp%XOcwf_52&DCJd z{2fl^)1*3lC)_T)iNZ}1IFt@-C#q);@uS6vyV6^{CXS#YFchbJSIK0$szs8{(j;jv z73Wuy6siG3r8T4x^iKQ07go_@9VYE5ogwXy+dd2e@mc93={b6>%jiLFlx9-rZbBmS z_p&*%HRLm|lsRM_$Z!rK*?1fd^&(XGR(S@s@7Hp-{I>jw{Jq>JPsh_Pk>8U|m91k1 z-c5l-xG|c`SK*@nTmDM^j9ls7iY1Cq3acVXIZt^T zZ-pu-Ip}v1r2DFN;3mjbHB>KT)pwkAZ40Wl{el-!FBWR#c+o> zoD!U?-lQI;maDI#>TjmXrcRv@^gy{;*;n~O@w*~QVUYh!g;p>7m#x}FW|MBiH!+O9 zu3BnDUG4|#K! zxc{U)smxG{gT|1UEu|9h4=K1aSk+%tt9ef=fNl0SI)R3_u z146om^rE^i#2-_n`9v+^PttMIH16QH!8h>`v`YH1iX2mtdCB+8V2YnQM61PWXa8o?nYkE7>0V@z|VEJXJUqUi>7jlns?FlyY*xHAUk#^e)>u+b1`{P4Z^U?lCy$SKAD?t&!KmXUZTI_yXAXrn58D-swfr_&ox zW}NI(@f*C8k&tM&R9vA>yNr=02mNYYs@E#&23;$gKzZ$od!Z$MgZU&aUafo$+b#_i zz-)8@D}CHNSyS>5aUG4aa-QGMerLy8;#_8{io5C*bQoNn6`VDkIPx$I(4&Q;E|1z=;Lr`IE zf^-*MHVAfg1f;fQRG_O=Os*Kg_WQ^)qNd;%nWlV*4o?mg0>UAm%_N(Q4$t~;-S2zmg>TU9uHba{GA8O+cU)kNMFt+QG zY0?|*>S{C#)1gn!fJRprm1uO?1r($spucq~{Q<3SN@-0fR?5;(jKxdQDg06LeTj%Q z$|p3R$BKXD9R}kpD24`h3u^5j%)ghCw6_Z$`E5r0tKJXfQtoBUfA4+DJ(s<=_@2QQ ze8INT6;I(2x)+Z|cd!Ug_68KMm+0l%QBc3dX}+2@&}X*WP10Sa6pv({+6z6gKigLX zBlj)O@(a&1-#dqy|1j8||52a2!rpA|{gyS?f0~8QMc2vilY8rUlUWhyy#A~WYVg}3 z-nqEJS8=%+8y1qeG(ZSWzwTO=TRac&? z$koB!!#&(R3N_tHceeYpJJ1vAX#}(M5m|CUtezUMzS&RykF%gPUG#m0S@6+fiUdV1 zSu@N-jk~z$GClNsTnTrIb{1_YTF*OfR}@tAnST2~IB&V+3hpfE3=8#FR!Dj7GjxL* zxmUURKu*5LTBQvvff80p9r7abGO60VW!nwF^W@fo$Tg74-dtE) z&q(yEhvso-erA3RR|;#K$*#k^8!xojG43g3woGv!bYF1WPz8uQ1@3rHO-~a~f6pp9 zO;=bI7V*x43Q}O}^`@V6mgLVHtkD}m%3oP{r?7U>UwndPe5P-CC9GuL)1R9KX}2%5 zO-pH0RM{V)Sv6u^aDy{jctuanPz&Hh7F6hPOEiJVomwfXtb%i@ExPX%awpqT;gmss zug!0w`92UT{us_%|8d{{q6)g9atY3<59sgTRr)~29Ezf89lo9%k}wlUv>Zvk*L-Hv zv*AtN;_Q{@8{(G&EqgR%@G0o>kN92j%l8ur{?jr^m~D;}Y-AoMLaUTOg4RTGwATCQ z`gaQW9_CX|(n0hA64Fz4;-lKdOfD+0b71qY`Z29&5YDvm=m6e{1~FIKB|b!!TerOE}JG>O1HNSo!?F{emcun$X9dO#r*7vT*Dl|B`=Ye z%Ev2?DPAdV(N!=hVw6Las~JPrDz7RH%3P%nqwoHp%%JQb4+-+IxDc26&f)U(FaFqhG}+cvNS#+T|#Du+zY8qm;O^|jj-==;NJ`j3;!QU z?+3%n!@rN%Mvis@+=AtimdF;`rCL8-5N_f^-E=(iAEIhTZ-_n}?H3ah(=O&vj5DSv zrdjOd*gdiOSVf#HEc^;#Q3hQmUg={v#c$&JKCJto3y7)~l^XS1)ZbAnq7Fn| ziOP;jh;GAcc*(0MiVlfMj#(6QAm)8cXl$R@r&C!q`J=i?I=BwS4>ObAVEm^dqOLE?_Y1BoXREr}sj2H>54RwXH^UDBSU z{G@?ZPgG5;Ho977HFLF@)t^^yUE^>K|C;}+`LO1bn$gJ%lQ$(BljBqRq})ldr_@Rv zklH_WN$R@Py{Xqz&!*lax@$slTVrPo0_CDm5xqof@4Qmin6i*_pB? zWm-zpl(dwpDUy`R*sv)Spu8q}EDpliD&h zF|}r@GF6lMDkVGRRf;?{f!EqQ)i2eX5}KOAtM1Lu3wV{2Q-`J=;<3i0txQ{zHYBZ2 z+R-#y+W%^`tW~?#pjrcK^{dsmR?}LuYyDm8T`gCw1+|j-&CpsGYyDa)sn)Z!vuX3w z#;4UzYn!G?6Q=#0+9LH_%AYCwQ{JQ;Na>vNh%GiNc~ElqWKHt%ny+i#tGTyk@0wL= z=GKtb46T__qfdkgN=i-IP$i;@ zC2@bEZ{n1M&+$v+o5Vkes~>kcHYN67j>dnYEm3`=e4?`HZoSu5*B*=<8d)519G%Ij z@ZZBHL2S4XHZ`m&?)2((&3wc9gjNad7`m8@`Cg&A(1g&)(DtFD$fo}>^nYa1Zx8*M zH2azS)*5;<^l<31&>c`K9);cr-5=4hsqE6c!z(3bTZoLSw@c!zy_E zwOqG^E(mQInn6!;d`R<L zN7-V$<$t66jg&rMz1UTfO}BPBUDYI#c#NWaJi1w;kD_y=+3Z6lmCLRF^tB(+n0!QG zD;AfCT0w-XEpE)^7x7y07V#+YSSWFG`97S^cOz(Vg*=iAAFwaJL} z6OTZ-TZ%4V2jrQ0P^!cHQ~hK8%LTEFA65M$&>)BS2hul;^e+$?1Y)R(Vg3SK#cu=- zL5{#fHQ`@+@0rvNt_a4Wd#fu*fg0|DzqOgV#W*;~Zj=e>ej(@Kg0}5QQ)Qv5#l-K-bGsPNc;!+t{2p^ z-??_X#*#t2jBL5Hu8pn@u=7`O-+9+3s;cp{kCs6Z@0IypR}deGU#$n%qfE$HXELp(0a-;)=~q%&~5Wm^K2YO z9l5kIH^eUzW_FqU%-@((%s-eT%r*G>ow=#Gf%#kW2=mYM!uFeYp-}x`zH6>Dzc5?O zAL)+0!;5sm{D|M~GXHJ<#k|No$GqEo(Y({VhWnjnm)RFKSUX%vLrIbcpDc&37z!{DUXymg9xPh#%#h z!%TPc0X@{;9FuXWBsfa#x6mG^*sItB?bq;(O|XU9{A_pW3Xgz>R%o?Y&s&dK=UPWv zzqi)1mZ707x5QhmmJqJ5ELKa2#mvv;7NIrVnrjj8)n#d7Ew%jT-dbZ>Xqk+UY6a@w z&Xy9hf%kn14e%ViSv}~Gy)W+2Gwwn!yvlgT_|X_((wYXEdYP)@;OT{5=Oul&Q>GiHr(7?g3QjjUO%diI zdXhCvt@+DMJbJiEX{v3CH#z9wO)!o&-Y`Dk>ovng!&5`DF~s=D@S|ax;jH1UVUJ;s zp@rcn+T&A(p@slDfTs-44O3!2% zq|ZwKJH1_c@%vBj1JVue_q|{De&>5_`l|Gw(`%;JO&^|qKVABv>WB6p5sGr#>cQvT|fQ%Y3--IpL{FG&8zU!HN`{a#E*B%^rvZ(Da!PeJy*|I zX-GHBGPE^((C^U?(Z}d5*=MuIWDn1-kuAwi&pMv9ENfU+udLx&sadgEFib7s>5tls}0sUQT{BS64cF6J00C`}~B` zI{~eBQyItUH=FyF6`X?`88_j>NX=GyAI z$=@A?75x?+_y<=2D)ENyL2%I5xc8y_KF?V&msM*p3dDLi@CTwN>_xBl40^)zp2OsB zT=YDk(`xiA7AAe6S$S^u5)A8WG z#w~G%s?0U|Q9J3YK0?KrNtW75{l;$3R7x>FwZsUevm% zqR_|+d_duAIzp`q!U~G%G5yx;Puyts z-3_+;8MeE}rA5V=L_W|pw+uG-GN=xxNo{p`>d;Z0TCkC~d)G6{!>r&vSR`R9^DxKj$@`SqapvXD;G z1hN}0)3y6driHxBMS6gN5%wNFv7* zX9*6$yBr}^3Zlr=>qBPpA$p__p;QDF{zPry0(~?o^_)1;f*KWdCYj}55^Ap(Ig3Qp zih6p7dpAR*Fn9yV1dE`uG#|(HXZr0wa6FtSahD`e{ra`^0c3{n;5uK2%}|TV=!5cP zav(P2u+xyxF^lxiTnG()AiQ4l@ue!+f?3fR2qF7?x04pKp7fKgq}?xu4|A?5B(olXAG?6-GA`5UM-AsPkIL^MSPPFKq&)XK z3l|_7_Huu+(nrwiIxBcV{h^M3PydzvcTi!P{UcE{UJb|(s0a|ENn3;O!57l5lIuux z*BeNhKM!B_W#D6cY-ND~ILii!!bCnIov0HTmsN4IrHHzU+M;%BDrzAbD%ycdVj9Wx z>zO-0WNuz8Dij5ZW5sQlB@Y(&gSNa!yjQ%D6w1eRrK-Znsm<4Ekca0%%t^uj_mkv1 z(mG=$PRPq?%-s)&Cc^LOC#om<06!-&Ff`By7Eq9Ib-*%Iqj@B_O!IHn&YA=J)riPGf5{ypu{~VnKbd%{8MdN9#Nt+5(pinPPad#h_!3UQ&xVyW%JA=&N z?(Xhdr9xejHjOp%Hms~bC2f;GzufQMbN1;hvI;*5#klzdY|A#HbyTM3AkQwLF7*@l z6aNEI+>g%rc+{!eBuNstq^h(kq=_Be-lO~sMRz^{n#5UIv8*o7p`Uy{gxn0dN$w`M zbr9=;^)Q4!u=4Q5f43Y`ke*jCP5BtsZY$L|y7?(8Z*^mJeKhlt>W=Ek>Z|HU>RWU_ zPVmuHl&N>s3z_`Hpi8wvEAGY=?i;Gm?#i1Gr6UyqihHn&vhblylDFg4--E~8Qzn-^ zm9~JltD)=vQ?i`Zfn4%cERr;kgyNQ~fkM6`?{gnXeLQk=C3|qqT|jyJS|X5ElE$*- z`a{n+12e-UwV`Bf3UB#OUgajH32RZ^9+K^n?S`N67{}yG*?SbO8hJUnk31X-^K$tX zl(>iFw^8GMmgmZ4ygvs#;^1Kiq3NGW8m&NCT{%~IOKBp^JlU46f@E(ORHkt`#UsZL;OG9Bhk7hnLmHgM)s%^~o?x-v(J>F0Ye#vm?Wc^8q z{TmO&aP@d5kvG&y>W}JVRKoA+KV9Wto7znu>Isw0zEH+y;HH_O?#c5Wq28>%&tFgB zXP&Axbk(|R`e`O;PBSZ%@O+2Rr^?2avQ~Rads_QNTN_qdlJ2`siQA+T^TPLDpSTH8 z*ZO()@gD9y+xxutCGRud<5A4+@)n@K{me(By!SFC9E_f}zISUlVr^r`F9fYtSNpJhJl zeYW}Z^-=qb*9-MVZFfjTN9|ed zXu8XLv`e(Bn9***yERh#hjyxVo_4wR0Neg=oLt?t zwcffIy2?^r8K%A(oq@UZapu0izJ3rNSSNl{1+AC%5stTVo;5rpJd-qQG@~?aG<`HG z%}4c4y5X}R1~yj*s-LQEsqS;`Sj1Ul0JrKYohlXO{aNVfKj|%nD3!_<%6asd?4)q- zrc2a<&d_D9XOSLS9OMnc(&{e&mt3o4Gk#oxFZXOo#8BAg{Cz=1yr#&|kY z(xc>i2I3xB&9qJCNF~YdSLNtKwo;t^D|P1s`&=Bs8>vB#P;s6^KQ$BA@JqW5om3EM zVt+vr*n*4bIF;9S9L76PA)O*W@CDg4-%1nUzbr02!~b3mxg+j(s?@(Yh~JVwmPAg? zuXMpYv_WBLgE~5W2^W-cIOcYLaIqbCxBmsn zQNdv)`({w717gf198?ZeivFm)%UX?0(MB<8nqw|+-di%7n(P^Fp|K_})3M^J#U)gz zr;BzMEhwr}q$zr2{Am0DWx;3!o?oObdTzXBbQnXaW*Zgxkq#9}kL3<^?JHIhsjMU7 zid4lFi(8>19K&j22x`wisfriUNoh$YP4nU~{vA_1oH3e41Cs+yGA$#aF ze79d3v`Lm_{2zjfvJk$7%5oQvZg*0NW^ff&)7SN^H7;BF^E`r>x;8b7%>Sa&4KCTwmNHQF zFDd>&w&XHnbECiU3bY_6TJVO2M+=%1yv`q#-<4bAd_n%|yv1;1w!;7#38iEV9GNwA ziB9LK(c5>U6ErJ-JYIs%IK36P4EmCh`liqg{i8d%sp+I&IZz@GFz&+pY+}zWVO`v( zXdO8~kI*G+m{GQ6ZySex^*hdG9oo@Q_Uv3!KuKLPSuZnb?Pq>#_CoLb!%~k|8ILlc zr|m!V21zzG%Dnv$(#w(7Q-}LFj-6?&4m;uq?1qv}2eJGz`pO76OJ{7uNQnH#x@$ga zzhG-I+pZ4zqOZ)m$x&@@w&9|C!~}FcYu^ebLrT^|^!Ft@n?IW1uFSM^`Wp)u4<5kWyuUOfMOPliS zdb94CRl1O~#>Y}U?!pGBF_&|ec+PpCm2-mg1Iac6@hD1J0j(uBvJFZM1+>Pwq%|0X zHSw9;5>*s;Z_8QlIN0(d~^~*#yzxZlSq^QmETi>hV3x4H8IS~ zJE9v<6#o#_5Gkn3AHzR9ME=8I$Xy-DeyA*rAzP@qa422qv8<_f3NHvhlWdtmrerR9 zMNevaG2SyjDtZ<9#6oI)8&&>w)Ff+g+)dzTtB@d(Dad5KwubI;1J+z&_+6xecO*L( zGto7W;%$PhRv!P0(;Wa^uNhfAt4KGxFDN8Kz5!k8UA$7aumOb0#jF|bG0p8QKEW#L ziTEYDK_RvEWXWHW!IA^K%7c(j1%|wZw&X(T#R+fH)oB-F?=Cbww+LIrPl;!h4Ym{)2Bh7~183%)^bS zXLFopyq0^Mv$(A!+p#$`x|wKw)}gyS45Ow31dOZX!5_xY=<6Pfa>vS+>ck2*fh~Dc z;1o1v>b-*ZWCU9}Q>Y=o!$ePC#Txb33Uvfih1IYzmopz|NM_|kQb~L8wLzkpJi9YI z$1G74YtcUVo5qXh^32WRROT6hl2TS-k6_iA*g8342c*$A;^jQ1sn}cmn3ShFqCEcM zFjBUULc@Cq5A_QQ8gJK42$6~SKxK|IBoREbudq8&+lSch(eaP4O08GudBj>uITP+D z9d0d0*jzG#|0Z*82YFuqk*2VBuReB(Q+6Iz-)ekD?Qd|kg(T0CUh4U3g$P3P7E(mlF(3+34Euw!F1L4nV z)!2l#t08w7-upYuYMq%pT(Tp&RXwVtC4!& z6J?Z?+^s$k?S|o~&a+n~g`p)mdlhgp=)X0k0aa541beJl(Aq^Dzf_>y-8{{YDaZ zILXRW`CC8iw;_)XVw+iTtyyedFz@oIYJ%8GV%rlkitlmldv1y6{Jt1}$h(p}R6$`S zS4^|XiPV_(7DpGqr5~DIpF zO)Snp>kc&T)KJ^C!LMv|oO0}?2AqLgx)Q3d$(;S&BzDxboA3g3v1v%;n{KU2?)EZjoobd7 z<_=~bp5<#a>|O9_Jf@BsNDsG~sUmsFwN2$pm0*@Hr1#_IXEJy+3T>{m8O++VFiM_O( ztsDE_ReHFIDCw-Wa`x8L#;k73UD}^xcWl#IROtc zA5C3H_bzha4xl`I;(h=T>m=D_gSa(x>)>U1xZjXHGMclZzbn~!fn#ehY@b;4j~__; z84XKO;_!4lq{e>8ak`q+#|@}lxoWD9$^qV+?A9e1AgVL7! zRvwkTWxzimxnd|`9^WA6;fhMNYPp#prpCwOVrk?>j#QrwQB0YyWZPo%=4dCNV9 zS?lNGEyZ(CnHh^KnF{D8>v#_R=p#=;Wwz4v%v2kFS^tuORPQ3X$NRY=>>+Wq9G&C& zmSK2}PEsX5qAJdSJ>!SIupaws6wa=%)Xu*uS&UfQdxTp zEdEl|8@}Ij<6LrJ)1V0c@=(6VHQA{!5q93of~5sV3#Q=MoW)0n`20-4HduVBx}CKMjVV{{x}W@=#-)OQOEp9~39uCc}tQaV=~4;Z%?&l=a`%X(zg78$8zZK&;Z zMX^i?)-$i(iKhslTv27_IV+gPq!*V%WjDk$7ZRTeuie&?gpwfhpYZFXmQGat8J3>b zH7GaB<5pV)wN;GAWIg+*3d-mKygpV)b^-8Um$E8+@jE?uFJy&(Anq*XIy4+FNTkEf zB;Xup@-_H00!qV34t`56^i=x-yg(D}UG2?a;tK8Qwwm@1wUsA1YI-Ch%CoXhnZ|;J-SyX zU-QQ4v!1mPDi$uq%M`<6#A}7Fb}rTW$u9T?>e2>o$!xaQvFFJ zhxtousG?hm>(qq}mjS+GcW%UeW<5EuGI^Yv$Da6C(`nrbm%{ zkGz$gu95G?tjOhI;2W1fZ57b%{AcV-UtWrDaSz3>e@?<9#;(pb7ox=MNw zPl;9*4B^5cRYRHBgW_^Ox5F||c}>_4ZE=#Um8ZzxGWkf8hcVX}!xUnb$2*V4ic_!* zMalqJg*nPPXnVe^YS169py@^5bSGYyX>EJ+a|KGFDR`q8(p>hpm9 zZ;^fkxg-tvTB6?9C(^A%LG}%$+A6A)O9>Uz?3SHf6^!1NrvKfg$?pb zAnG)uB2w9h{o@QQ$6qwZ70hzZC^s_Wy2d^erV^{}DQ_r?m6KFca9VX&^%j|mo8=IQ6c-VdRX!v(T5Hc@L3393}Qh_f2;Rt_KfT zkf)$_SAhgsn-%|R^k^4gLF!mpdXXzEgvI%tJlR6Nx>DFoC=`AZd_s-g6lcp*s=6w4 zTj%ooqg-D(|96JblLL2kG2FzG(qP9&dZ;7toi=5~;Z>RkFENdYe`Qu6?O97SFYQqp zijTDzPpumrz+1XFlj!A)$7dUj|JoB;x4?c2r|dM_F54Q`PUY#`oMdhJ!FmtfK}UQD zwWtunsc757{jX=0Tg6r#zJpQpz6Rh>u7>Vx15SzU)@xQ7{HY)`0j1XATx*Ruy*J^5 zNQ4~Q>36qA30Zz39@D1w5IQ&gaL$I>ze5O@QEPo?{aBl1;-Rbu9M&r&uz$xVy4Bj8 zzdV-z_fSda<7@ewb;DHL52snZ2H^Pq%bG;T&xbx-N9z;Ia(aC!bm_9`+-1|vk}#Pc zWL`zru_9`-|4h+1!B65xUJ8k~PSI{CyzTHacY@u0ma3#(;d4mZm-0`Lgm@M@!$x?_ z@A4A!&gK1?r-sWhGw&3-t$K9kuH}`@cjoQMTMN~>Eu0HW{xirFy`cgGqED*=o%eBJ zq@k$rdtpgocf(hMztPOSu5k(d;tw!+t;UwrGRM%Wd@oYa{oRO;ZYvs-tHm)WCXUc^ zeQ6qiYdpWiQPQ4jEYIB4($6x&a?TQocV`Um%xTu#WqvzNKHxCPumw_)G_?ETRjtB% zdJ~7xQ)^!wKatiVs=7$ab$Y{vbYv%&c$K_>prtddDfU1S^QveIE4w%{7ekFd4Iaig zdWq|)%%&UG8U8XXg#}#~M))wY8GAy0E-*GK>RvRc=weY$QFk*FI;l8Eqz9@bQ@m31T{XiwkhP<`g={}IZ} zE$&u2O>OCHB}*Dew{h*4%IcEN>m#2g-_2RnCU3%&{+IXiJjpquArxLzY=CK7UfDxg z3)XKacd62%P%3>n3maj?rEo31ph#D=cpq49=)6r9wCi3GybB$`8N4|61$N7BRK#NwtusE7rMn4ZVhY9fIuS zNnY4s@3UO#KYE9gH(7%`sE2Hg6t>$@{RRC2(kGuYosNe3w2*1_T(9rCrn)!OFUg)U zo;&fJglo2_FL0f^p?ruETCV)+kt81@zagJ4cgtqTDw1p-BHJmgE_F-(lPY8iStn_p zBpCi?WqizO=5D6!1QEt4Htl

TYqP7FPDY$4(WNv%FCW`202W2 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
X${8abcBWqXP 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
?IvY1#5xuqyvR{Y% znUhYy{2TD~fQqIiSo9khY=7w68^3+UUmODa2SNNv*zZ25`=d`9@bfBE-YP~2ez5y| z{@n02^gyZTzEco??VN0IuS`aezAKZg*vZDC4(5Gkmx3@*E5T4s{205 zVz*u3?I5~!1M2)k)b3DeTpgz8^lfFOT33YFP5&R6U!(cI;}_d3N3f5R{cpeHDab0N zn0#BmyIVYUnbioVwG@W554$fT`&^x3Qio3R5S_X=6HUmi63P-%+GPx13vY$~h>}r8pYvxuUA+ zwGn#lbb`A}@Y;1a?FW{b@OF}n@e2I+HCO6xm>bJ6nFhzLAcORX%Azs+8^bLjQh(8P zzaGC06~a@+=A*>qf%}G$V1?(y?JwFJXbqe z;kkOzZZraoHx4Z{HqAEn%yRote(XC{8)tMnN}$|jHKsk{)(!KW3TOL6*B)|it&Ec0 zFqa|n#v>u^hjClSxm4+wB6d6&r+X7VPsQhE+4C$^z7;vSazWlau=G(_TL+rmhBEiaQhuV=<{%XM zGd?rS%8X?`1QC15BTwT*n#cn+isHPGTKzs{c{JzIB&fL?uR4BPf9G+Uag|76F6$FDh4;lH&Rix!?ea3< z4SbH59Jf7%yKm&r8VKh*`)p182U)Q7e|go8;jCME&3E}e7UQ>jaNsj4u8#SiF8Q7g zVy}n2{-y73x^HkJHTiw9%mnL`aNb4s_(va+B2iqDMeLEmvHyj`K^<6xRqu8C0>fTI zjTpv9x5#%tlnyb%wk2+J9APSkJU+@Y^n=IG`UT5TmGg+L&N@H7wd}`;&#MGJ?Ogdu zCU=c33!NXcu;GbRmFHk-B@y0Jav+@`Z*^SxQC-LyyMG2Z?ypX%n~cX0I6W0kcXV!6 zQ}6dWn4HxWEv{< ze&Vf*^+<$>>p!+(42n5!<-1+T@1H>gb7B;(r`BMv<~-mo@?7WG(6 zM|J%T#mlAj;)=-@OdAE zUWZFfp2obkHt2H#| z!7z0D24{!rCDj$neFO6L^!Enh!R;{JmK5gFxN$a>zQy&bDy)B1 zH65$!!`l&XdMur~i@sES?b!lPZmn*(iC$IV^~Y2dKSPu5W__rm_7w+=fz1Pavezl( zt0>l^Z0k;m9s;>XLi`Wpm-l!*aQDw?-}5m2Pz!ySBE8RN-m7vt6Ku@`hs#3P`{~V@ zVe6w5g(t)%6+F*jYb7dlP!~d#Z#HY8N~;7EFGF<*&x3NE%RZU8$qGaNTF|+r-=?oP zD0ESKo31fEa)50VbM5963m*3qF#8m**coj2yb9s}Ao$IsTXM+RoDUDVRTs52a@IU0 zr_l=z_rZGm!Qer#dLYz(Qao3O+A`Got1x*2uKXGQ^m&TRN$2TbDxR+L0_IlF^srd5 zvDmUC4f-j)XC9*oRTo>fh$7E%bfdRKm0P$Uw#W|eSHJX^ioJ{Y?+vPSqRx-kQwpXQ z;!n&WyOWL6E6m%I+fq)>rxUM511fbJ7(Ey-o`DIkfy!TslDFW%-_xbz)hvY`9vP$g zAd9=sJLAG5B3zyZZ)X}2wqw8t;q6hm;n3MH^!GXL{(6MLS#{~iAtPLqN2?2`XE*3O zNUT2}{?67bu$4|hjrcsL%NBR_YM0noI8!F#23ur}&tMUO|F*<48u3h5G}g6LyOae3 zDIZq|Ln%ci3>8r2)i@Q<$>VY3=VasLCq~z=VenoKt&PUqZ?NB8nD8zgl0L$Pm&MJr z`~;nUgS)%q((=sSiOZw*>4C`SS`jKYzYDP3{cx=(i!;|CE+rL9M2sTn{d@WH}S$#Q0jNRrUK);NB!)r z`1@gARXjAm`|QxL2u57UeJ+Sp5KAse0nUw`W}_M3cFfXVy<3lrd>qa7Fyy{4^$}e4 zaR@sM-o6T>UxLhyVRB6f+*fXQBy1kdKQ+~DIE)^|YuwKM!|~(Z?#Idncjsx!;qPQ} z{~vam-d{?MFD^RBh5J_WIV$*64PbC_95;i#8p7h7_EFkoYbrfGz5hFrVxB(XXWzxI zQ4f>jBA2!Ba#MuLXQA>budI>-{#LB=1LgWVnEeZ`yxX$Qej6eC-l&3XqW$KIX;#y! zN5T17^7i8?(mnky&&khMqdDh`{|B@EMI836-+PCM>l?rHj}Ugfs@~9bV;;oaCk~5? z-zERB+u5-M4!@>~s1L`%^YRuiP)puYFVqUB9Sm>VQ?B1~F1-Y!r`ltNdf)+Z&%)|@ z5W0bL@&UIJ&b-pt@g14q%=DRpap~fc@Zb~rXuapW4&6DX(s#P(joH+#4n4X8RXQWb zL~2YNUHW&JdQQ~(Eidy&RHgYUfS2ltGfP%yitDY8yl-<3jRB(Go)qayY6t7U+sF0a z$e<_3Ew%c0WPwA*H!B3bqWWPc+&v`|{0U|HaFj=yfeH8I=j_DcS=(4pT^GE^_?big zYM&~L3@|uvk-CyX;zjCDh zBX_oks<#=^euTrW*5hinobS6vxDieH)CEv-hb%>t6-=|$JF4*b6S#S9y`VKzy|Bb=&1B?H}Hxzo3 z91wFHqh$ZV<&_;G2X}UM_raT1%kz7n>&?VsxOUL1%egNDaqHsGRiSTNnnG*8)kL`a ztY4?4U#zWnKM#q!%PCCYp<7SG*d}u7BO@GYxo65hYSXhQA=xmcLJl{tw0$mNlsaUCYV-W=%=fsqL&R@PC#|99>_i zY5JI6ebBR;aw)m#I>qTYb)5C3q6(+`)iIT!`&1WU*5V+nfi0J`)TaJC&Er1Mk-heyc&e(3oNo~<`#d>C^H4u2DqCF(73 zGv+=XtztCiNA$;PBPZ5A%8B(-uQU)2cY?#8#g0;cv5E5h3q9_q*t_D6bFlnOpZi9? z-|h0k|7c2gGWJ(2`GWO}oLqk!>#keN&0|^_23M31?$4(+&S?6&k+N-+Hy&U-4c#}k zL)|Zo&ATa&Ipfb5n@`gt|HP3`$^RC_lgi2ZmWp~RHIQR`3iiHYe18t!w!nXD80W|1 zSp7T?9BdpE_z*g*qn{qc&ep?{WjaG8yjRn|AaI9@WCCC_WHB^f1=c;ixk@H z@%KTqqVlYDsnFf&&@I&h)q`wxVC@4i?h!~=ml7N@uwASNM{&c5n^uc_#ZN=pfSXf2 z>ukM|_qNAuAEih)p%gy=2}>peaJWBa-JXhFA8NO7#0rj98D9=PzVboV@{U_XoNzzv&6e<&jPtEXea}dh-cL== zn(%|DBCy~u=1hO3KYe`i^y+rH73 z&_iphh~)=*|5-WZi&W|z{-^KY_(o_R`ecp6rrXNDEM5s+%DIpfE#hI0W1OF$gT`IcWF7p%m+KhFLO^SI% zC+ZQd!CWQobQLlSvs|0`w8rpT5x#S5C@z>n!TotdBIg8c7oodA>Nd(U$~Z!nw4*F35#jd5$_ z-4;Y@;Q%hHZqWG|7~P7l_&AKNYUHeoskEdn)`rbx+{$y<6wrwyE5~!Z=ZQ&I)dC+& zI<1E2H}|I@@n5pU$31SsiWbTt?x9zIi23%xfP2eCR)D-Epumm9f)vscGMM%GeQQ!o zD^pIJQ<>Y-pnJ$kc7P1^+#1P=R#JbPm3~_;@8h-1K^fNn;MO_J z@mCqwV_5SRx36K;FF5nJ5bH~vc@6FyI%DnhJaFg0KR3d?g>e0Ixb{Kf6ioOX`2W6b zpV%YJdb1n%{2FWCgI9m=Xo((oIL2Re;nN;>q+dcqGv-)P+;=TDjD+EU$wylZpu$|hiH*nbjF!p~{!^3D2bECSbaoFt( zFm{NXL8$7Q9@Tq|rCnT=7dWOaY_AN%cYe<_$rnUjMIdXUxGOm+hsV6MkI>bs8g;r0 z#2o^0`#OV`P@L`xxd1XY^FhXL&ZovuTiZkx1|zRcjwP2LsmrBujjRYx<~U z|DRCzm^!!H$#+p|GCGqV{{O0`I`FkV%&jlOGJqp-s>fGpL}SFMlXR##iTB1t$8{Weh>BrCj0>|yeeiT1l~b0%McsK9eo`K zj-{g&mJ2Q*?r%i2Np zm6*^*H6*KXqVMtEvoy(barxy5^Xi2aAAdz{Q&CLnR@{B^hc!5QLVxEc<$oJ_RzF&X z<6GTu;1RUuHe9za@kDpw9a;C(e(8P4t8JK-J7x1a6PXwdK2He1!u01UYstnCQ(8%Nk) zoibg_5tHK!LjFv+Y+lFB&c}5Ne~-h8;}iabgNJ>J?^ODP&R?>H8W7rJwbE_wj0LN0{gMW1x5FDcuiZPm%);d-n7h>iE6#$lW)H;;IMZFC$h7 ze$`VtM&I_^-lt;i9ob7B|YxjfT?pXID)xSS&303g*obz3r<&UWj zXz$sxaQ6WoiASxMr#RNw~&IYIF9KU(^*HG}axXI`4A(3S<6}{`{T&wyQVVtu854 zgN~#b4#1Y1QJjn6HTPpTB~+rM)%)spQqJVxqmCMx=*?SI-maJNeOs>g9q2m4NIn+F z9nHft+K3-!2=AeCrhz)6;Ad{HYW6W0(Ga3mgb|gz68t|kWhfs~FO-SLD&$uGf;5Sk zaj4O`NAB}xVp3x0brtH1((zfva{#AJ+J!IQ1jp;J#4?dJgQ}3#(7! zwqIFW;r<{5z91|7m;d94|KJkk{itmRJv%OzxDJE=gyn}I?{$CajQ==;e%F6fqi={p z(k5NtRZE~F+$STPhoT(IQC^Jx{G@NZ4OP0CZ@Ri|jm19YMLqd_x53Aj*PiLcEu}fQ zig>*T&f9}tF_;7Tb^67$sAhRCuDii+x|;KSoh~V7#aQrGHXh*#{LAcdKSJ8y%z2qM-Ta--w%WDhr1!J?5LwmbJ*KdUO4p7dV>NzR+i>-*_m~2J7s8or5)|1 zMBn1UDk`sYMo##4N+!POLgL*TuE5sf-MsQYt=0MUl>dE4C+RIz=f!mA@APy(pn|xN zY;cyC^m^PCR!!JaUg=rL`=t89!J_e*QGNe5c)OK@^9S9s&Z`stR`tZStUQT5D*~C!%_kl(>vhfBzQlb7I3mb?(U5GY|)UT5U_a$oo7R zCJciYGu&odR=F*Kq9Mz$ap1PH!2zw?V#cpR`5w@_t>YHuw0Z)!4qY$H;M_&{z(Rab+<$YQSM%Fe ziEf?6vy5iDO@iYW=>^Bd6Yo+J-qp2iEM8Ca+bhxU? zfz?#hSHqbXx<@xwFV~ii{UX*}4>vAAi@qKkN0m-=Mz7_v+AoUSVVMStKT{jJSL`xz8Y2O&{fRZ zPzMUP#fm$@<)N!xZUYfOrW`XY^bD|gl^*ZO+P4Ef;a;d2`vtbzPk zy>*w*2$f438LEllb5N?2`LK@gcbo9*l`VbT-iuk*I3)w=vr4^IIliRg;a~?<;%G**)(=?f!BC>66FJ9Ie60P zD19D_xb~ z-h?{`;8G&w`V%7Em2?g|-M}cf@bmr&k-ou8PuXK16kBQgHxTSQi1-ukcnnHy!bAhp z40G3=w9h8TykiN^&O`R&xbry`!HJ3YLb((;xZTkXM>E_Vwa*Hu`59e1;PY|}c_d6- z0KwmcnP@U6=EiR}lx&c{Zas1rSF$QARhoSfLlh>6ke}pQ~5<*t<9o2-j4MZ@H*j|!P ztFT)RigV#eCnzS$3GS^tj#Jp4_c+$wzSB?z9V5qH2uJSWNP{rs(U@{?RZ7oW@8SEO zNo$@&y;$H^3LUt1@VoEEhL7k|_LEG*6{^OXq-4MMk8%}z;PDN=c4249Bl5p3ogFQm zAFVmC8j9pP>-F56Q|mP>xDQpji}R`}&(>?!$8q_Evu;K5#pIZj8@&2S&c;ltk-~j` z$eY9YTY>7_&i#`#=thy>@KqQ*-74l)6N|W;g8JEPIp8H}RDo(Z7ml z_ou|9UXT&azztnl+*`#p)*S*riygPp2X-(&WOvu!DA(jnuINxX{EazPZsNli^su}} zn=XzCXV>|;GB$j#E59^G{3t|z-1Xgs8aA9K>#3M=6tj(@_01fb+aU6fZdagl5=>4L zd&6k*llcBlOjbVVr=fB)IK13^GNYmLObmA|3_j1HwGPMKXna~u#XD)a6q}CoG|W(U zHa5&x7f17}B-h(a9=JEX@HtB2a5=j-v6o)5cB3)g*NlaejG&t#@hm>qA$ZO_6`m{I z-mrGSXuHVc8sqF*y%qurU)2FKXMC>sf-;I#d9NPAdLO4tH-imrpl}z=syklX3M&ph zh$lyVi6_uf*TCTKJZ`{vmr+~4rn7zwY4$}*YjAD-0(DP${MLCkks>tY6`N&O%|n zK)=olH`BoU>(OVr?$iIn(|pmh*n~swi#SFhcpG?l0XgaF{^q?tXJMZ%DCnWq>R!t( z8uCdj|0rzTE_Rt`S%k%I@_kK!#Ovq-D=_8dP;`zhL!j@Q5P3Kp?EzO`#C&_;(Bolt z=&SJoZv3u2LLaliQEb#3T0iDDs>QGVG$(X=I`n1O8tTIT6mcDdsRua%K7zDMoDGX* zcW3IZ(bxIYhvwXrVp4;OQp$N%1&WrH)lJ|&$j9qk#99@Vz&Ush{#WmNB{nJQzj~ch z;d{$Bl$njPB;Vk?pV4WKn<;UXGx}4Ecb&8Q9nAP;y*GMsShdv8svIroL6!0WbrU=m z(`%y+B(AONMkYPwa#N80;ib5vFZ4!g(?+@A*X4j;guK06Z_W9w>T*jKh`Fo>^bMKd zv#EE*9F{}cANic;nT4T`t9hKh0G(a!&ERZ(xLV&RP!qc(1-tXR$&=zYD_d6WxDlZ3EZUY?w{WwE=$&fvvn_pwv+yl5m z4VmFEGed03W$gDfto;F>SdVLbh3#&_Jyu5bAa5D>7VCR79xs{Wel;KTT6BRUy3hE*ZY~QLz${LSQmKk9tPLa5^fLJ>uzi~ z)B|5gtPEL#_c<ouIn+8M)$6Z}cGNRV}&T(or_}A^xk*&^EU$ zaSg7kGVu0MGrm9UaiIKicSq=t^A4pf&xGDfaPYlY`e}-Gm>ns2!Y+y`jzQtzt_riY zU&r^)i3yT*zC29{2-UrHX#*W7^Y=sB8q|YkB9mOOIk=)z#R>)OlisU$%jy=iJt;BF z2o?4yA$BPQc`HNjO0vMA%IPuRcvrPwbs%(A$Eqqv{19aC;(IU4dDY6=NPlgj^$23DwnwaY);W2K@*JP?yQ<$rdEorAy!BMGXNYR+9ur3vRYzA4 z0+&%uS6r8lD*S|HVDR-W6BT)i@0M?aY4_YLJHOWx0_5YO&6in^Xw@B1#SwNdLK_i@2_fN7>-E z5Vs-itD~N?AMu7P6PIt%5ATp#hy#%IwA&u}ssl!hw6T|r9KS-}nAody=(E-e%6jE9 z(zJ=@N*ZSNh2a$EK5i4tA~S=|Jl{xl$mq4*%rb|tg^kcRGe2obI&*Q1H#gjU&{`dR z{KAa1MJa?27!mVWD@=!OpdRT_Yfs=Ry)czN(0Qz^@HjX-8xP)t{ceNE7xiG+g8?7a zci|qr8TR77xnXWWytgigbP*ZFn(A6=>poaP55oJ6=8cT$&)`*4u-|tu-*>rw_d=4L zvXx;bhRbm2tbQDKsnk88689?XI*;k4K;4TN@o9*BTy0W7q0_K74lk^S&DKu1g70R7 zS2f^jap-w9J~u8LJXbkj>b-C>R05TQ(Ph0}9{$#VcqJioT}SAF-3F#x!4b+v_hDv- z&?mDIEbPo%^$bosQUR`21 zY^&s1Z_GK&%k-2ZR>h_}>vz@CXKI4~7W9{zxi1F4^FjAe`w;q6XX8|Gk~Axc>)W*G-rpQk3jfWHx+488ft0~s|6kKlY3@q)etM0xod@kqJ z+ORpNzRsm&fiFSdW6t5VD%)3a{V$aXUM{EdnVR<{BF|1TE^T=ao6w@4fyUiEt4xV5 zrH{>Rb--WKkq)?$j)_~p6VraA`e3!Xzo9%?UvpZ#3ynjC!X(#X4|;TIS7u|Gq9-xp z=GH5TlS9|5`&A?*=zFylB5#meTBlO@5M_Fn3Wzr#^9Wb|G`e&O;wyQ~r^6`&(A{v=MQw3eFv}!gFNj`f+FtkkJcs zHjH-POBaquR0lWX(<)}|0m@@?QU&N-LI&_&N@M{u;gqvI#dsV8rIT@^1P-l(cv6_1 z;Q&Y1J|p)wBlugg!*e0?9HV<*+2Q&ywj9i;2%Ss9iOe1wLWOD&qmC?QDGKUiZpEQR z*2q(o0m>w+iVgGE7EL+@&yq38E3o)a%XN(QuB0_k_)}TdZ)IB-b91f7k`Lga+oA6A zDC7F2XCL9FA9^kH_E~wC~w#G9LUX)c)8J z-tzoQk4qdcaOJu5=xzRD$m?!|uAAI~U+QnXcVA*$(hol06?h98eDav2BmR?8u(S|W zIKBTTH8ITi@Sy)T@Y^P~w1d9?!{GMTpYnYTq8E3wwU7H6qL-esxuM$k8F_#KP`f+l z)a!8b6_L;wS%O6#!|WGt$;eNFt?#H2`+`?)n>cDJKD$ee?|OQ~V!uq#D0bMk%0A0Q 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ä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≧qd_|CKV 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%0DcZ>Kf`*K@Uf2P#+uEeGgRN-fo4W(uG?WlY&e(7 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 z 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 xTZi!Fy$NB*I}{0oIosuO>r$n3+C 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 NB`|rZv-?H5{{SH@O~e2I diff --git a/modules/audio_processing/OWNERS b/modules/audio_processing/OWNERS deleted file mode 100644 index aecf56ed3..000000000 --- a/modules/audio_processing/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -ajm@google.com -bjornv@google.com diff --git a/modules/audio_processing/aec/main/interface/echo_cancellation.h b/modules/audio_processing/aec/main/interface/echo_cancellation.h deleted file mode 100644 index 883357da1..000000000 --- a/modules/audio_processing/aec/main/interface/echo_cancellation.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_ - -#include "typedefs.h" - -// Errors -#define AEC_UNSPECIFIED_ERROR 12000 -#define AEC_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AEC_UNINITIALIZED_ERROR 12002 -#define AEC_NULL_POINTER_ERROR 12003 -#define AEC_BAD_PARAMETER_ERROR 12004 - -// Warnings -#define AEC_BAD_PARAMETER_WARNING 12050 - -enum { - kAecNlpConservative = 0, - kAecNlpModerate, - kAecNlpAggressive -}; - -enum { - kAecFalse = 0, - kAecTrue -}; - -typedef struct { - WebRtc_Word16 nlpMode; // default kAecNlpModerate - WebRtc_Word16 skewMode; // default kAecFalse - WebRtc_Word16 metricsMode; // default kAecFalse - //float realSkew; -} AecConfig; - -typedef struct { - WebRtc_Word16 instant; - WebRtc_Word16 average; - WebRtc_Word16 max; - WebRtc_Word16 min; -} AecLevel; - -typedef struct { - AecLevel rerl; - AecLevel erl; - AecLevel erle; - AecLevel aNlp; -} AecMetrics; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Allocates the memory needed by the AEC. The memory needs to be initialized - * separately using the WebRtcAec_Init() function. - * - * Inputs Description - * ------------------------------------------------------------------- - * void **aecInst Pointer to the AEC instance to be created - * and initilized - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_Create(void **aecInst); - -/* - * This function releases the memory allocated by WebRtcAec_Create(). - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_Free(void *aecInst); - -/* - * Initializes an AEC instance. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * WebRtc_Word32 sampFreq Sampling frequency of data - * WebRtc_Word32 scSampFreq Soundcard sampling frequency - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_Init(void *aecInst, - WebRtc_Word32 sampFreq, - WebRtc_Word32 scSampFreq); - -/* - * Inserts an 80 or 160 sample block of data into the farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * WebRtc_Word16 *farend In buffer containing one frame of - * farend signal for L band - * WebRtc_Word16 nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, - const WebRtc_Word16 *farend, - WebRtc_Word16 nrOfSamples); - -/* - * Runs the echo canceller on an 80 or 160 sample blocks of data. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * WebRtc_Word16 *nearend In buffer containing one frame of - * nearend+echo signal for L band - * WebRtc_Word16 *nearendH In buffer containing one frame of - * nearend+echo signal for H band - * WebRtc_Word16 nrOfSamples Number of samples in nearend buffer - * WebRtc_Word16 msInSndCardBuf Delay estimate for sound card and - * system buffers - * WebRtc_Word16 skew Difference between number of samples played - * and recorded at the soundcard (for clock skew - * compensation) - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word16 *out Out buffer, one frame of processed nearend - * for L band - * WebRtc_Word16 *outH Out buffer, one frame of processed nearend - * for H band - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_Process(void *aecInst, - const WebRtc_Word16 *nearend, - const WebRtc_Word16 *nearendH, - WebRtc_Word16 *out, - WebRtc_Word16 *outH, - WebRtc_Word16 nrOfSamples, - WebRtc_Word16 msInSndCardBuf, - WebRtc_Word32 skew); - -/* - * This function enables the user to set certain parameters on-the-fly. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * AecConfig config Config instance that contains all - * properties to be set - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_set_config(void *aecInst, AecConfig config); - -/* - * Gets the on-the-fly paramters. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecConfig *config Pointer to the config instance that - * all properties will be written to - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_get_config(void *aecInst, AecConfig *config); - -/* - * Gets the current echo status of the nearend signal. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word16 *status 0: Almost certainly nearend single-talk - * 1: Might not be neared single-talk - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_get_echo_status(void *aecInst, WebRtc_Word16 *status); - -/* - * Gets the current echo metrics for the session. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecMetrics *metrics Struct which will be filled out with the - * current echo metrics. - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_GetMetrics(void *aecInst, AecMetrics *metrics); - -/* - * Gets the last error code. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecInst Pointer to the AEC instance - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 11000-11100: error code - */ -WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst); - -/* - * Gets a version string. - * - * Inputs Description - * ------------------------------------------------------------------- - * char *versionStr Pointer to a string array - * WebRtc_Word16 len The maximum length of the string - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word8 *versionStr Pointer to a string array - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAec_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len); - -#ifdef __cplusplus -} -#endif -#endif /* WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_INTERFACE_ECHO_CANCELLATION_H_ */ diff --git a/modules/audio_processing/aec/main/matlab/fullaec.m b/modules/audio_processing/aec/main/matlab/fullaec.m deleted file mode 100644 index 0f86a8c58..000000000 --- a/modules/audio_processing/aec/main/matlab/fullaec.m +++ /dev/null @@ -1,953 +0,0 @@ -% Partitioned block frequency domain adaptive filtering NLMS and -% standard time-domain sample-based NLMS -%fid=fopen('aecFar-samsung.pcm', 'rb'); % Load far end -fid=fopen('aecFar.pcm', 'rb'); % Load far end -%fid=fopen(farFile, 'rb'); % Load far end -rrin=fread(fid,inf,'int16'); -fclose(fid); -%rrin=loadsl('data/far_me2.pcm'); % Load far end -%fid=fopen('aecNear-samsung.pcm', 'rb'); % Load near end -fid=fopen('aecNear.pcm', 'rb'); % Load near end -%fid=fopen(nearFile, 'rb'); % Load near end -ssin=fread(fid,inf,'int16'); -%ssin = [zeros(1024,1) ; ssin(1:end-1024)]; - -fclose(fid); -rand('state',13); -fs=16000; -mult=fs/8000; -%rrin=rrin(fs*0+1:round(fs*120)); -%ssin=ssin(fs*0+1:round(fs*120)); -if fs == 8000 - cohRange = 2:3; -elseif fs==16000 - cohRange = 2; -end - -% Flags -NLPon=1; % NLP -CNon=1; % Comfort noise -PLTon=1; % Plotting - -M = 16; % Number of partitions -N = 64; % Partition length -L = M*N; % Filter length -if fs == 8000 - mufb = 0.6; -else - mufb = 0.5; -end -%mufb=1; -VADtd=48; -alp = 0.1; % Power estimation factor alc = 0.1; % Coherence estimation factor -beta = 0.9; % Plotting factor -%% Changed a little %% -step = 0.3;%0.1875; % Downward step size -%% -if fs == 8000 - threshold=2e-6; % DTrob threshold -else - %threshold=0.7e-6; - threshold=1.5e-6; end - -if fs == 8000 - echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N); - %echoBandRange = ceil(1500*2/fs*N):floor(2500*2/fs*N); -else - echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N); - %echoBandRange = ceil(300*2/fs*N):floor(1800*2/fs*N); -end -%echoBandRange = ceil(1600*2/fs*N):floor(1900*2/fs*N); -%echoBandRange = ceil(2000*2/fs*N):floor(4000*2/fs*N); -suppState = 1; -transCtr = 0; - -Nt=1; -vt=1; - -ramp = 1.0003; % Upward ramp -rampd = 0.999; % Downward ramp -cvt = 20; % Subband VAD threshold; -nnthres = 20; % Noise threshold - -shh=logspace(-1.3,-2.2,N+1)'; -sh=[shh;flipud(shh(2:end-1))]; % Suppression profile - -len=length(ssin); -w=zeros(L,1); % Sample-based TD NLMS -WFb=zeros(N+1,M); % Block-based FD NLMS -WFbOld=zeros(N+1,M); % Block-based FD NLMS -YFb=zeros(N+1,M); -erfb=zeros(len,1); -erfb3=zeros(len,1); - -ercn=zeros(len,1); -zm=zeros(N,1); -XFm=zeros(N+1,M); -YFm=zeros(N+1,M); -pn0=10*ones(N+1,1); -pn=zeros(N+1,1); -NN=len; -Nb=floor(NN/N)-M; -erifb=zeros(Nb+1,1)+0.1; -erifb3=zeros(Nb+1,1)+0.1; -ericn=zeros(Nb+1,1)+0.1; -dri=zeros(Nb+1,1)+0.1; -start=1; -xo=zeros(N,1); -do=xo; -eo=xo; - -echoBands=zeros(Nb+1,1); -cohxdAvg=zeros(Nb+1,1); -cohxdSlow=zeros(Nb+1,N+1); -cohedSlow=zeros(Nb+1,N+1); -%overdriveM=zeros(Nb+1,N+1); -cohxdFastAvg=zeros(Nb+1,1); -cohxdAvgBad=zeros(Nb+1,1); -cohedAvg=zeros(Nb+1,1); -cohedFastAvg=zeros(Nb+1,1); -hnledAvg=zeros(Nb+1,1); -hnlxdAvg=zeros(Nb+1,1); -ovrdV=zeros(Nb+1,1); -dIdxV=zeros(Nb+1,1); -SLxV=zeros(Nb+1,1); -hnlSortQV=zeros(Nb+1,1); -hnlPrefAvgV=zeros(Nb+1,1); -mutInfAvg=zeros(Nb+1,1); -%overdrive=zeros(Nb+1,1); -hnled = zeros(N+1, 1); -weight=zeros(N+1,1); -hnlMax = zeros(N+1, 1); -hnl = zeros(N+1, 1); -overdrive = ones(1, N+1); -xfwm=zeros(N+1,M); -dfm=zeros(N+1,M); -WFbD=ones(N+1,1); - -fbSupp = 0; -hnlLocalMin = 1; -cohxdLocalMin = 1; -hnlLocalMinV=zeros(Nb+1,1); -cohxdLocalMinV=zeros(Nb+1,1); -hnlMinV=zeros(Nb+1,1); -dkEnV=zeros(Nb+1,1); -ekEnV=zeros(Nb+1,1); -ovrd = 2; -ovrdPos = floor((N+1)/4); -ovrdSm = 2; -hnlMin = 1; -minCtr = 0; -SeMin = 0; -SdMin = 0; -SeLocalAvg = 0; -SeMinSm = 0; -divergeFact = 1; -dIdx = 1; -hnlMinCtr = 0; -hnlNewMin = 0; -divergeState = 0; - -Sy=ones(N+1,1); -Sym=1e7*ones(N+1,1); - -wins=[0;sqrt(hanning(2*N-1))]; -ubufn=zeros(2*N,1); -ebuf=zeros(2*N,1); -ebuf2=zeros(2*N,1); -ebuf4=zeros(2*N,1); -mbuf=zeros(2*N,1); - -cohedFast = zeros(N+1,1); -cohxdFast = zeros(N+1,1); -cohxd = zeros(N+1,1); -Se = zeros(N+1,1); -Sd = zeros(N+1,1); -Sx = zeros(N+1,1); -SxBad = zeros(N+1,1); -Sed = zeros(N+1,1); -Sxd = zeros(N+1,1); -SxdBad = zeros(N+1,1); -hnledp=[]; - -cohxdMax = 0; - -%hh=waitbar(0,'Please wait...'); -progressbar(0); - -%spaces = ' '; -%spaces = repmat(spaces, 50, 1); -%spaces = ['[' ; spaces ; ']']; -%fprintf(1, spaces); -%fprintf(1, '\n'); - -for kk=1:Nb - pos = N * (kk-1) + start; - - % FD block method - % ---------------------- Organize data - xk = rrin(pos:pos+N-1); - dk = ssin(pos:pos+N-1); - - xx = [xo;xk]; - xo = xk; - tmp = fft(xx); - XX = tmp(1:N+1); - - dd = [do;dk]; % Overlap - do = dk; - tmp = fft(dd); % Frequency domain - DD = tmp(1:N+1); - - % ------------------------ Power estimation - pn0 = (1 - alp) * pn0 + alp * real(XX.* conj(XX)); - pn = pn0; - %pn = (1 - alp) * pn + alp * M * pn0; - if (CNon) - Yp = real(conj(DD).*DD); % Instantaneous power - Sy = (1 - alp) * Sy + alp * Yp; % Averaged power - - mm = min(Sy,Sym); - diff = Sym - mm; - if (kk>50) - Sym = (mm + step*diff) * ramp; % Estimated background noise power - end - end - - % ---------------------- Filtering - XFm(:,1) = XX; - for mm=0:(M-1) - m=mm+1; - YFb(:,m) = XFm(:,m) .* WFb(:,m); - end - yfk = sum(YFb,2); - tmp = [yfk ; flipud(conj(yfk(2:N)))]; - ykt = real(ifft(tmp)); - ykfb = ykt(end-N+1:end); - - % ---------------------- Error estimation - ekfb = dk - ykfb; - %if sum(abs(ekfb)) < sum(abs(dk)) - %ekfb = dk - ykfb; - % erfb(pos:pos+N-1) = ekfb; - %else - %ekfb = dk; - % erfb(pos:pos+N-1) = dk; - %end - %(kk-1)*(N*2)+1 - erfb(pos:pos+N-1) = ekfb; - tmp = fft([zm;ekfb]); % FD version for cancelling part (overlap-save) - Ek = tmp(1:N+1); - - % ------------------------ Adaptation - Ek2 = Ek ./(M*pn + 0.001); % Normalized error - %Ek2 = Ek ./(pn + 0.001); % Normalized error - %Ek2 = Ek ./(100*pn + 0.001); % Normalized error - - absEf = max(abs(Ek2), threshold); - absEf = ones(N+1,1)*threshold./absEf; - Ek2 = Ek2.*absEf; - - mEk = mufb.*Ek2; - PP = conj(XFm).*(ones(M,1) * mEk')'; - tmp = [PP ; flipud(conj(PP(2:N,:)))]; - IFPP = real(ifft(tmp)); - PH = IFPP(1:N,:); - tmp = fft([PH;zeros(N,M)]); - FPH = tmp(1:N+1,:); - WFb = WFb + FPH; - - if mod(kk, 10*mult) == 0 - WFbEn = sum(real(WFb.*conj(WFb))); - %WFbEn = sum(abs(WFb)); - [tmp, dIdx] = max(WFbEn); - - WFbD = sum(abs(WFb(:, dIdx)),2); - %WFbD = WFbD / (mean(WFbD) + 1e-10); - WFbD = min(max(WFbD, 0.5), 4); - end - dIdxV(kk) = dIdx; - - % NLP - if (NLPon) - - ee = [eo;ekfb]; - eo = ekfb; - window = wins; - if fs == 8000 - %gamma = 0.88; - gamma = 0.9; - else - %gamma = 0.92; - gamma = 0.93; - end - %gamma = 0.9; - - tmp = fft(xx.*window); - xf = tmp(1:N+1); - tmp = fft(dd.*window); - df = tmp(1:N+1); - tmp = fft(ee.*window); - ef = tmp(1:N+1); - - xfwm(:,1) = xf; - xf = xfwm(:,dIdx); - %fprintf(1,'%d: %f\n', kk, xf(4)); - dfm(:,1) = df; - - SxOld = Sx; - - Se = gamma*Se + (1-gamma)*real(ef.*conj(ef)); - Sd = gamma*Sd + (1-gamma)*real(df.*conj(df)); - Sx = gamma*Sx + (1 - gamma)*real(xf.*conj(xf)); - - %xRatio = real(xfwm(:,1).*conj(xfwm(:,1))) ./ ... - % (real(xfwm(:,2).*conj(xfwm(:,2))) + 1e-10); - %xRatio = Sx ./ (SxOld + 1e-10); - %SLx = log(1/(N+1)*sum(xRatio)) - 1/(N+1)*sum(log(xRatio)); - %SLxV(kk) = SLx; - - %freqSm = 0.9; - %Sx = filter(freqSm, [1 -(1-freqSm)], Sx); - %Sx(end:1) = filter(freqSm, [1 -(1-freqSm)], Sx(end:1)); - %Se = filter(freqSm, [1 -(1-freqSm)], Se); - %Se(end:1) = filter(freqSm, [1 -(1-freqSm)], Se(end:1)); - %Sd = filter(freqSm, [1 -(1-freqSm)], Sd); - %Sd(end:1) = filter(freqSm, [1 -(1-freqSm)], Sd(end:1)); - - %SeFast = ef.*conj(ef); - %SdFast = df.*conj(df); - %SxFast = xf.*conj(xf); - %cohedFast = 0.9*cohedFast + 0.1*SeFast ./ (SdFast + 1e-10); - %cohedFast(find(cohedFast > 1)) = 1; - %cohedFast(find(cohedFast > 1)) = 1 ./ cohedFast(find(cohedFast>1)); - %cohedFastAvg(kk) = mean(cohedFast(echoBandRange)); - %cohedFastAvg(kk) = min(cohedFast); - - %cohxdFast = 0.8*cohxdFast + 0.2*log(SdFast ./ (SxFast + 1e-10)); - %cohxdFastAvg(kk) = mean(cohxdFast(echoBandRange)); - - % coherence - Sxd = gamma*Sxd + (1 - gamma)*xf.*conj(df); - Sed = gamma*Sed + (1-gamma)*ef.*conj(df); - - %Sxd = filter(freqSm, [1 -(1-freqSm)], Sxd); - %Sxd(end:1) = filter(freqSm, [1 -(1-freqSm)], Sxd(end:1)); - %Sed = filter(freqSm, [1 -(1-freqSm)], Sed); - %Sed(end:1) = filter(freqSm, [1 -(1-freqSm)], Sed(end:1)); - - cohed = real(Sed.*conj(Sed))./(Se.*Sd + 1e-10); - %cohedAvg(kk) = mean(cohed(echoBandRange)); - %cohedAvg(kk) = cohed(6); - %cohedAvg(kk) = min(cohed); - - cohxd = real(Sxd.*conj(Sxd))./(Sx.*Sd + 1e-10); - %freqSm = 0.5; - %cohxd(3:end) = filter(freqSm, [1 -(1-freqSm)], cohxd(3:end)); - %cohxd(end:3) = filter(freqSm, [1 -(1-freqSm)], cohxd(end:3)); - %cohxdAvg(kk) = mean(cohxd(echoBandRange)); - %cohxdAvg(kk) = (cohxd(32)); - %cohxdAvg(kk) = max(cohxd); - - %xf = xfm(:,dIdx); - %SxBad = gamma*SxBad + (1 - gamma)*real(xf.*conj(xf)); - %SxdBad = gamma*SxdBad + (1 - gamma)*xf.*conj(df); - %cohxdBad = real(SxdBad.*conj(SxdBad))./(SxBad.*Sd + 0.01); - %cohxdAvgBad(kk) = mean(cohxdBad); - - %for j=1:N+1 - % mutInf(j) = 0.9*mutInf(j) + 0.1*information(abs(xfm(j,:)), abs(dfm(j,:))); - %end - %mutInfAvg(kk) = mean(mutInf); - - %hnled = cohedFast; - %xIdx = find(cohxd > 1 - cohed); - %hnled(xIdx) = 1 - cohxd(xIdx); - %hnled = 1 - max(cohxd, 1-cohedFast); - hnled = min(1 - cohxd, cohed); - %hnled = 1 - cohxd; - %hnled = max(1 - (cohxd + (1-cohedFast)), 0); - %hnled = 1 - max(cohxd, 1-cohed); - - if kk > 1 - cohxdSlow(kk,:) = 0.99*cohxdSlow(kk-1,:) + 0.01*cohxd'; - cohedSlow(kk,:) = 0.99*cohedSlow(kk-1,:) + 0.01*(1-cohed)'; - end - - - if 0 - %if kk > 50 - %idx = find(hnled > 0.3); - hnlMax = hnlMax*0.9999; - %hnlMax(idx) = max(hnlMax(idx), hnled(idx)); - hnlMax = max(hnlMax, hnled); - %overdrive(idx) = max(log(hnlMax(idx))/log(0.99), 1); - avgHnl = mean(hnlMax(echoBandRange)); - if avgHnl > 0.3 - overdrive = max(log(avgHnl)/log(0.99), 1); - end - weight(4:end) = max(hnlMax) - hnlMax(4:end); - end - - - - %[hg, gidx] = max(hnled); - %fnrg = Sx(gidx) / (Sd(gidx) + 1e-10); - - %[tmp, bidx] = find((Sx / Sd + 1e-10) > fnrg); - %hnled(bidx) = hg; - - - %cohed1 = mean(cohed(cohRange)); % range depends on bandwidth - %cohed1 = cohed1^2; - %echoBands(kk) = length(find(cohed(echoBandRange) < 0.25))/length(echoBandRange); - - %if (fbSupp == 0) - % if (echoBands(kk) > 0.8) - % fbSupp = 1; - % end - %else - % if (echoBands(kk) < 0.6) - % fbSupp = 0; - % end - %end - %overdrive(kk) = 7.5*echoBands(kk) + 0.5; - - % Factor by which to weight other bands - %if (cohed1 < 0.1) - % w = 0.8 - cohed1*10*0.4; - %else - % w = 0.4; - %end - - % Weight coherence subbands - %hnled = w*cohed1 + (1 - w)*cohed; - %hnled = (hnled).^2; - %cohed(floor(N/2):end) = cohed(floor(N/2):end).^2; - %if fbSupp == 1 - % cohed = zeros(size(cohed)); - %end - %cohed = cohed.^overdrive(kk); - - %hnled = gamma*hnled + (1 - gamma)*cohed; - % Additional hf suppression - %hnledp = [hnledp ; mean(hnled)]; - %hnled(floor(N/2):end) = hnled(floor(N/2):end).^2; - %ef = ef.*((weight*(min(1 - hnled)).^2 + (1 - weight).*(1 - hnled)).^2); - - cohedMean = mean(cohed(echoBandRange)); - %aggrFact = 4*(1-mean(hnled(echoBandRange))) + 1; - %[hnlSort, hnlSortIdx] = sort(hnled(echoBandRange)); - [hnlSort, hnlSortIdx] = sort(1-cohxd(echoBandRange)); - [xSort, xSortIdx] = sort(Sx); - %aggrFact = (1-mean(hnled(echoBandRange))); - %hnlSortQ = hnlSort(qIdx); - hnlSortQ = mean(1 - cohxd(echoBandRange)); - %hnlSortQ = mean(1 - cohxd); - - [hnlSort2, hnlSortIdx2] = sort(hnled(echoBandRange)); - %[hnlSort2, hnlSortIdx2] = sort(hnled); - hnlQuant = 0.75; - hnlQuantLow = 0.5; - qIdx = floor(hnlQuant*length(hnlSort2)); - qIdxLow = floor(hnlQuantLow*length(hnlSort2)); - hnlPrefAvg = hnlSort2(qIdx); - hnlPrefAvgLow = hnlSort2(qIdxLow); - %hnlPrefAvgLow = mean(hnled); - %hnlPrefAvg = max(hnlSort2); - %hnlPrefAvgLow = min(hnlSort2); - - %hnlPref = hnled(echoBandRange); - %hnlPrefAvg = mean(hnlPref(xSortIdx((0.5*length(xSortIdx)):end))); - - %hnlPrefAvg = min(hnlPrefAvg, hnlSortQ); - - %hnlSortQIdx = hnlSortIdx(qIdx); - %SeQ = Se(qIdx + echoBandRange(1) - 1); - %SdQ = Sd(qIdx + echoBandRange(1) - 1); - %SeQ = Se(qIdxLow + echoBandRange(1) - 1); - %SdQ = Sd(qIdxLow + echoBandRange(1) - 1); - %propLow = length(find(hnlSort < 0.1))/length(hnlSort); - %aggrFact = min((1 - hnlSortQ)/2, 0.5); - %aggrTerm = 1/aggrFact; - - %hnlg = mean(hnled(echoBandRange)); - %hnlg = hnlSortQ; - %if suppState == 0 - % if hnlg < 0.05 - % suppState = 2; - % transCtr = 0; - % elseif hnlg < 0.75 - % suppState = 1; - % transCtr = 0; - % end - %elseif suppState == 1 - % if hnlg > 0.8 - % suppState = 0; - % transCtr = 0; - % elseif hnlg < 0.05 - % suppState = 2; - % transCtr = 0; - % end - %else - % if hnlg > 0.8 - % suppState = 0; - % transCtr = 0; - % elseif hnlg > 0.25 - % suppState = 1; - % transCtr = 0; - % end - %end - %if kk > 50 - - if cohedMean > 0.98 & hnlSortQ > 0.9 - %if suppState == 1 - % hnled = 0.5*hnled + 0.5*cohed; - % %hnlSortQ = 0.5*hnlSortQ + 0.5*cohedMean; - % hnlPrefAvg = 0.5*hnlPrefAvg + 0.5*cohedMean; - %else - % hnled = cohed; - % %hnlSortQ = cohedMean; - % hnlPrefAvg = cohedMean; - %end - suppState = 0; - elseif cohedMean < 0.95 | hnlSortQ < 0.8 - %if suppState == 0 - % hnled = 0.5*hnled + 0.5*cohed; - % %hnlSortQ = 0.5*hnlSortQ + 0.5*cohedMean; - % hnlPrefAvg = 0.5*hnlPrefAvg + 0.5*cohedMean; - %end - suppState = 1; - end - - if hnlSortQ < cohxdLocalMin & hnlSortQ < 0.75 - cohxdLocalMin = hnlSortQ; - end - - if cohxdLocalMin == 1 - ovrd = 3; - hnled = 1-cohxd; - hnlPrefAvg = hnlSortQ; - hnlPrefAvgLow = hnlSortQ; - end - - if suppState == 0 - hnled = cohed; - hnlPrefAvg = cohedMean; - hnlPrefAvgLow = cohedMean; - end - - %if hnlPrefAvg < hnlLocalMin & hnlPrefAvg < 0.6 - if hnlPrefAvgLow < hnlLocalMin & hnlPrefAvgLow < 0.6 - %hnlLocalMin = hnlPrefAvg; - %hnlMin = hnlPrefAvg; - hnlLocalMin = hnlPrefAvgLow; - hnlMin = hnlPrefAvgLow; - hnlNewMin = 1; - hnlMinCtr = 0; - %if hnlMinCtr == 0 - % hnlMinCtr = hnlMinCtr + 1; - %else - % hnlMinCtr = 0; - % hnlMin = hnlLocalMin; - %SeLocalMin = SeQ; - %SdLocalMin = SdQ; - %SeLocalAvg = 0; - %minCtr = 0; - % ovrd = max(log(0.0001)/log(hnlMin), 2); - %divergeFact = hnlLocalMin; - end - - if hnlNewMin == 1 - hnlMinCtr = hnlMinCtr + 1; - end - if hnlMinCtr == 2 - hnlNewMin = 0; - hnlMinCtr = 0; - %ovrd = max(log(0.0001)/log(hnlMin), 2); - ovrd = max(log(0.00001)/(log(hnlMin + 1e-10) + 1e-10), 3); - %ovrd = max(log(0.00000001)/(log(hnlMin + 1e-10) + 1e-10), 5); - %ovrd = max(log(0.0001)/log(hnlPrefAvg), 2); - %ovrd = max(log(0.001)/log(hnlMin), 2); - end - hnlLocalMin = min(hnlLocalMin + 0.0008/mult, 1); - cohxdLocalMin = min(cohxdLocalMin + 0.0004/mult, 1); - %divergeFact = hnlSortQ; - - - %if minCtr > 0 & hnlLocalMin < 1 - % hnlMin = hnlLocalMin; - % %SeMin = 0.9*SeMin + 0.1*sqrt(SeLocalMin); - % SdMin = sqrt(SdLocalMin); - % %SeMin = sqrt(SeLocalMin)*hnlSortQ; - % SeMin = sqrt(SeLocalMin); - % %ovrd = log(100/SeMin)/log(hnlSortQ); - % %ovrd = log(100/SeMin)/log(hnlSortQ); - % ovrd = log(0.01)/log(hnlMin); - % ovrd = max(ovrd, 2); - % ovrdPos = hnlSortQIdx; - % %ovrd = max(ovrd, 1); - % %SeMin = sqrt(SeLocalAvg/5); - % minCtr = 0; - %else - % %SeLocalMin = 0.9*SeLocalMin +0.1*SeQ; - % SeLocalAvg = SeLocalAvg + SeQ; - % minCtr = minCtr + 1; - %end - - if ovrd < ovrdSm - ovrdSm = 0.99*ovrdSm + 0.01*ovrd; - else - ovrdSm = 0.9*ovrdSm + 0.1*ovrd; - end - %end - - %ekEn = sum(real(ekfb.^2)); - %dkEn = sum(real(dk.^2)); - ekEn = sum(Se); - dkEn = sum(Sd); - - if divergeState == 0 - if ekEn > dkEn - ef = df; - divergeState = 1; - %hnlPrefAvg = hnlSortQ; - %hnled = (1 - cohxd); - end - else - %if ekEn*1.1 < dkEn - %if ekEn*1.26 < dkEn - if ekEn*1.05 < dkEn - divergeState = 0; - else - ef = df; - end - end - - if ekEn > dkEn*19.95 - WFb=zeros(N+1,M); % Block-based FD NLMS - end - - ekEnV(kk) = ekEn; - dkEnV(kk) = dkEn; - - hnlLocalMinV(kk) = hnlLocalMin; - cohxdLocalMinV(kk) = cohxdLocalMin; - hnlMinV(kk) = hnlMin; - %cohxdMaxLocal = max(cohxdSlow(kk,:)); - %if kk > 50 - %cohxdMaxLocal = 1-hnlSortQ; - %if cohxdMaxLocal > 0.5 - % %if cohxdMaxLocal > cohxdMax - % odScale = max(log(cohxdMaxLocal)/log(0.95), 1); - % %overdrive(7:end) = max(log(cohxdSlow(kk,7:end))/log(0.9), 1); - % cohxdMax = cohxdMaxLocal; - % end - %end - %end - %cohxdMax = cohxdMax*0.999; - - %overdriveM(kk,:) = max(overdrive, 1); - %aggrFact = 0.25; - aggrFact = 0.3; - %aggrFact = 0.5*propLow; - %if fs == 8000 - % wCurve = [0 ; 0 ; aggrFact*sqrt(linspace(0,1,N-1))' + 0.1]; - %else - % wCurve = [0; 0; 0; aggrFact*sqrt(linspace(0,1,N-2))' + 0.1]; - %end - wCurve = [0; aggrFact*sqrt(linspace(0,1,N))' + 0.1]; - % For sync with C - %if fs == 8000 - % wCurve = wCurve(2:end); - %else - % wCurve = wCurve(1:end-1); - %end - %weight = aggrFact*(sqrt(linspace(0,1,N+1)')); - %weight = aggrFact*wCurve; - weight = wCurve; - %weight = aggrFact*ones(N+1,1); - %weight = zeros(N+1,1); - %hnled = weight.*min(hnled) + (1 - weight).*hnled; - %hnled = weight.*min(mean(hnled(echoBandRange)), hnled) + (1 - weight).*hnled; - %hnled = weight.*min(hnlSortQ, hnled) + (1 - weight).*hnled; - - %hnlSortQV(kk) = mean(hnled); - %hnlPrefAvgV(kk) = mean(hnled(echoBandRange)); - - hnled = weight.*min(hnlPrefAvg, hnled) + (1 - weight).*hnled; - - %od = aggrFact*(sqrt(linspace(0,1,N+1)') + aggrTerm); - %od = 4*(sqrt(linspace(0,1,N+1)') + 1/4); - - %ovrdFact = (ovrdSm - 1) / sqrt(ovrdPos/(N+1)); - %ovrdFact = ovrdSm / sqrt(echoBandRange(floor(length(echoBandRange)/2))/(N+1)); - %od = ovrdFact*sqrt(linspace(0,1,N+1))' + 1; - %od = ovrdSm*ones(N+1,1).*abs(WFb(:,dIdx))/(max(abs(WFb(:,dIdx)))+1e-10); - - %od = ovrdSm*ones(N+1,1); - %od = ovrdSm*WFbD.*(sqrt(linspace(0,1,N+1))' + 1); - - od = ovrdSm*(sqrt(linspace(0,1,N+1))' + 1); - %od = 4*(sqrt(linspace(0,1,N+1))' + 1); - - %od = 2*ones(N+1,1); - %od = 2*ones(N+1,1); - %sshift = ((1-hnled)*2-1).^3+1; - sshift = ones(N+1,1); - - hnled = hnled.^(od.*sshift); - - %if hnlg > 0.75 - %if (suppState ~= 0) - % transCtr = 0; - %end - % suppState = 0; - %elseif hnlg < 0.6 & hnlg > 0.2 - % suppState = 1; - %elseif hnlg < 0.1 - %hnled = zeros(N+1, 1); - %if (suppState ~= 2) - % transCtr = 0; - %end - % suppState = 2; - %else - % if (suppState ~= 2) - % transCtr = 0; - % end - % suppState = 2; - %end - %if suppState == 0 - % hnled = ones(N+1, 1); - %elseif suppState == 2 - % hnled = zeros(N+1, 1); - %end - %hnled(find(hnled < 0.1)) = 0; - %hnled = hnled.^2; - %if transCtr < 5 - %hnl = 0.75*hnl + 0.25*hnled; - % transCtr = transCtr + 1; - %else - hnl = hnled; - %end - %hnled(find(hnled < 0.05)) = 0; - ef = ef.*(hnl); - - %ef = ef.*(min(1 - cohxd, cohed).^2); - %ef = ef.*((1-cohxd).^2); - - ovrdV(kk) = ovrdSm; - %ovrdV(kk) = dIdx; - %ovrdV(kk) = divergeFact; - %hnledAvg(kk) = 1-mean(1-cohedFast(echoBandRange)); - hnledAvg(kk) = 1-mean(1-cohed(echoBandRange)); - hnlxdAvg(kk) = 1-mean(cohxd(echoBandRange)); - %hnlxdAvg(kk) = cohxd(5); - %hnlSortQV(kk) = mean(hnled); - hnlSortQV(kk) = hnlPrefAvgLow; - hnlPrefAvgV(kk) = hnlPrefAvg; - %hnlAvg(kk) = propLow; - %ef(N/2:end) = 0; - %ner = (sum(Sd) ./ (sum(Se.*(hnl.^2)) + 1e-10)); - - % Comfort noise - if (CNon) - snn=sqrt(Sym); - snn(1)=0; % Reject LF noise - Un=snn.*exp(j*2*pi.*[0;rand(N-1,1);0]); - - % Weight comfort noise by suppression - Un = sqrt(1-hnled.^2).*Un; - Fmix = ef + Un; - else - Fmix = ef; - end - - % Overlap and add in time domain for smoothness - tmp = [Fmix ; flipud(conj(Fmix(2:N)))]; - mixw = wins.*real(ifft(tmp)); - mola = mbuf(end-N+1:end) + mixw(1:N); - mbuf = mixw; - ercn(pos:pos+N-1) = mola; - end % NLPon - - % Filter update - %Ek2 = Ek ./(12*pn + 0.001); % Normalized error - %Ek2 = Ek2 * divergeFact; - %Ek2 = Ek ./(pn + 0.001); % Normalized error - %Ek2 = Ek ./(100*pn + 0.001); % Normalized error - - %divergeIdx = find(abs(Ek) > abs(DD)); - %divergeIdx = find(Se > Sd); - %threshMod = threshold*ones(N+1,1); - %if length(divergeIdx) > 0 - %if sum(abs(Ek)) > sum(abs(DD)) - %WFb(divergeIdx,:) = WFb(divergeIdx,:) .* repmat(sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10))),1,M); - %Ek2(divergeIdx) = Ek2(divergeIdx) .* sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10)); - %Ek2(divergeIdx) = Ek2(divergeIdx) .* abs(DD(divergeIdx))./(abs(Ek(divergeIdx))+1e-10); - %WFb(divergeIdx,:) = WFbOld(divergeIdx,:); - %WFb = WFbOld; - %threshMod(divergeIdx) = threshMod(divergeIdx) .* abs(DD(divergeIdx))./(abs(Ek(divergeIdx))+1e-10); - % threshMod(divergeIdx) = threshMod(divergeIdx) .* sqrt(Sd(divergeIdx)./(Se(divergeIdx)+1e-10)); - %end - - %absEf = max(abs(Ek2), threshold); - %absEf = ones(N+1,1)*threshold./absEf; - %absEf = max(abs(Ek2), threshMod); - %absEf = threshMod./absEf; - %Ek2 = Ek2.*absEf; - - %if sum(Se) <= sum(Sd) - - % mEk = mufb.*Ek2; - % PP = conj(XFm).*(ones(M,1) * mEk')'; - % tmp = [PP ; flipud(conj(PP(2:N,:)))]; - % IFPP = real(ifft(tmp)); - % PH = IFPP(1:N,:); - % tmp = fft([PH;zeros(N,M)]); - % FPH = tmp(1:N+1,:); - % %WFbOld = WFb; - % WFb = WFb + FPH; - - %else - % WF = WFbOld; - %end - - % Shift old FFTs - %for m=M:-1:2 - % XFm(:,m) = XFm(:,m-1); - % YFm(:,m) = YFm(:,m-1); - %end - XFm(:,2:end) = XFm(:,1:end-1); - YFm(:,2:end) = YFm(:,1:end-1); - xfwm(:,2:end) = xfwm(:,1:end-1); - dfm(:,2:end) = dfm(:,1:end-1); - - %if mod(kk, floor(Nb/50)) == 0 - % fprintf(1, '.'); - %end - - if mod(kk, floor(Nb/100)) == 0 - %if mod(kk, floor(Nb/500)) == 0 - progressbar(kk/Nb); - %figure(5) - %plot(abs(WFb)); - %legend('1','2','3','4','5','6','7','8','9','10','11','12'); - %title(kk*N/fs); - %figure(6) - %plot(WFbD); - %figure(6) - %plot(threshMod) - %if length(divergeIdx) > 0 - % plot(abs(DD)) - % hold on - % plot(abs(Ek), 'r') - % hold off - %plot(min(sqrt(Sd./(Se+1e-10)),1)) - %axis([0 N 0 1]); - %end - %figure(6) - %plot(cohedFast); - %axis([1 N+1 0 1]); - %plot(WFbEn); - - %figure(7) - %plot(weight); - %plot([cohxd 1-cohed]); - %plot([cohxd 1-cohed 1-cohedFast hnled]); - %plot([cohxd cohxdFast/max(cohxdFast)]); - %legend('cohxd', '1-cohed', '1-cohedFast'); - %axis([1 65 0 1]); - %pause(0.5); - %overdrive - end -end -progressbar(1); - -%figure(2); -%plot([feat(:,1) feat(:,2)+1 feat(:,3)+2 mfeat+3]); -%plot([feat(:,1) mfeat+1]); - -%figure(3); -%plot(10*log10([dri erifb erifb3 ericn])); -%legend('Near-end','Error','Post NLP','Final',4); -% Compensate for delay -%ercn=[ercn(N+1:end);zeros(N,1)]; -%ercn_=[ercn_(N+1:end);zeros(N,1)]; - -%figure(11); -%plot(cohxdSlow); - -%figure(12); -%surf(cohxdSlow); -%shading interp; - -%figure(13); -%plot(overdriveM); - -%figure(14); -%surf(overdriveM); -%shading interp; - -figure(10); -t = (0:Nb)*N/fs; -rrinSubSamp = rrin(N*(1:(Nb+1))); -plot(t, rrinSubSamp/max(abs(rrinSubSamp)),'b'); -hold on -plot(t, hnledAvg, 'r'); -plot(t, hnlxdAvg, 'g'); -plot(t, hnlSortQV, 'y'); -plot(t, hnlLocalMinV, 'k'); -plot(t, cohxdLocalMinV, 'c'); -plot(t, hnlPrefAvgV, 'm'); -%plot(t, cohxdAvg, 'r'); -%plot(cohxdFastAvg, 'r'); -%plot(cohxdAvgBad, 'k'); -%plot(t, cohedAvg, 'k'); -%plot(t, 1-cohedFastAvg, 'k'); -%plot(ssin(N*(1:floor(length(ssin)/N)))/max(abs(ssin))); -%plot(echoBands,'r'); -%plot(overdrive, 'g'); -%plot(erfb(N*(1:floor(length(erfb)/N)))/max(abs(erfb))); -hold off -tightx; - -figure(11) -plot(t, ovrdV); -tightx; -%plot(mfeat,'r'); -%plot(1-cohxyp_,'r'); -%plot(Hnlxydp,'y'); -%plot(hnledp,'k'); -%plot(Hnlxydp, 'c'); -%plot(ccohpd_,'k'); -%plot(supplot_, 'g'); -%plot(ones(length(mfeat),1)*rr1_, 'k'); -%plot(ones(length(mfeat),1)*rr2_, 'k'); -%plot(N*(1:length(feat)), feat); -%plot(Sep_,'r'); -%axis([1 floor(length(erfb)/N) -1 1]) -%hold off -%plot(10*log10([Se_, Sx_, Seu_, real(sf_.*conj(sf_))])); -%legend('Se','Sx','Seu','S'); -%figure(5) -%plot([ercn ercn_]); - -figure(12) -plot(t, dIdxV); -%plot(t, SLxV); -tightx; - -%figure(13) -%plot(t, [ekEnV dkEnV]); -%plot(t, dkEnV./(ekEnV+1e-10)); -%tightx; - -%close(hh); -%spclab(fs,ssin,erfb,ercn,'outxd.pcm'); -%spclab(fs,rrin,ssin,erfb,1.78*ercn,'vqeOut-1.pcm'); -%spclab(fs,erfb,'aecOutLp.pcm'); -%spclab(fs,rrin,ssin,erfb,1.78*ercn,'aecOut25.pcm','vqeOut-1.pcm'); -%spclab(fs,rrin,ssin,erfb,ercn,'aecOut-mba.pcm'); -%spclab(fs,rrin,ssin,erfb,ercn,'aecOut.pcm'); -%spclab(fs, ssin, erfb, ercn, 'out0.pcm'); diff --git a/modules/audio_processing/aec/main/source/Android.mk b/modules/audio_processing/aec/main/source/Android.mk deleted file mode 100644 index a0c1cd31d..000000000 --- a/modules/audio_processing/aec/main/source/Android.mk +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_aec -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - echo_cancellation.c \ - aec_core.c \ - aec_rdft.c \ - resampler.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../utility \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_processing/aec/main/source/aec.gyp b/modules/audio_processing/aec/main/source/aec.gyp deleted file mode 100644 index 0427e0021..000000000 --- a/modules/audio_processing/aec/main/source/aec.gyp +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'aec', - 'type': '<(library)', - 'dependencies': [ - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../utility/util.gyp:apm_util' - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/echo_cancellation.h', - 'echo_cancellation.c', - 'aec_core.c', - 'aec_core_sse2.c', - 'aec_rdft.h', - 'aec_rdft.c', - 'aec_rdft_sse2.c', - 'aec_core.h', - 'resampler.c', - 'resampler.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/aec/main/source/aec_core.c b/modules/audio_processing/aec/main/source/aec_core.c deleted file mode 100644 index 81197ea32..000000000 --- a/modules/audio_processing/aec/main/source/aec_core.c +++ /dev/null @@ -1,1456 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * The core AEC algorithm, which is presented with time-aligned signals. - */ - -#include -#include -#include - -#include "aec_core.h" -#include "aec_rdft.h" -#include "ring_buffer.h" -#include "system_wrappers/interface/cpu_features_wrapper.h" - -// Noise suppression -static const int converged = 250; - -// Metrics -static const int subCountLen = 4; -static const int countLen = 50; - -// Quantities to control H band scaling for SWB input -static const int flagHbandCn = 1; // flag for adding comfort noise in H band -static const float cnScaleHband = (float)0.4; // scale for comfort noise in H band -// Initial bin for averaging nlp gain in low band -static const int freqAvgIc = PART_LEN / 2; - -/* Matlab code to produce table: -win = sqrt(hanning(63)); win = [0 ; win(1:32)]; -fprintf(1, '\t%.14f, %.14f, %.14f,\n', win); -*/ -/* -static const float sqrtHanning[33] = { - 0.00000000000000, 0.04906767432742, 0.09801714032956, - 0.14673047445536, 0.19509032201613, 0.24298017990326, - 0.29028467725446, 0.33688985339222, 0.38268343236509, - 0.42755509343028, 0.47139673682600, 0.51410274419322, - 0.55557023301960, 0.59569930449243, 0.63439328416365, - 0.67155895484702, 0.70710678118655, 0.74095112535496, - 0.77301045336274, 0.80320753148064, 0.83146961230255, - 0.85772861000027, 0.88192126434835, 0.90398929312344, - 0.92387953251129, 0.94154406518302, 0.95694033573221, - 0.97003125319454, 0.98078528040323, 0.98917650996478, - 0.99518472667220, 0.99879545620517, 1.00000000000000 -}; -*/ - -static const float sqrtHanning[65] = { - 0.00000000000000f, 0.02454122852291f, 0.04906767432742f, - 0.07356456359967f, 0.09801714032956f, 0.12241067519922f, - 0.14673047445536f, 0.17096188876030f, 0.19509032201613f, - 0.21910124015687f, 0.24298017990326f, 0.26671275747490f, - 0.29028467725446f, 0.31368174039889f, 0.33688985339222f, - 0.35989503653499f, 0.38268343236509f, 0.40524131400499f, - 0.42755509343028f, 0.44961132965461f, 0.47139673682600f, - 0.49289819222978f, 0.51410274419322f, 0.53499761988710f, - 0.55557023301960f, 0.57580819141785f, 0.59569930449243f, - 0.61523159058063f, 0.63439328416365f, 0.65317284295378f, - 0.67155895484702f, 0.68954054473707f, 0.70710678118655f, - 0.72424708295147f, 0.74095112535496f, 0.75720884650648f, - 0.77301045336274f, 0.78834642762661f, 0.80320753148064f, - 0.81758481315158f, 0.83146961230255f, 0.84485356524971f, - 0.85772861000027f, 0.87008699110871f, 0.88192126434835f, - 0.89322430119552f, 0.90398929312344f, 0.91420975570353f, - 0.92387953251129f, 0.93299279883474f, 0.94154406518302f, - 0.94952818059304f, 0.95694033573221f, 0.96377606579544f, - 0.97003125319454f, 0.97570213003853f, 0.98078528040323f, - 0.98527764238894f, 0.98917650996478f, 0.99247953459871f, - 0.99518472667220f, 0.99729045667869f, 0.99879545620517f, - 0.99969881869620f, 1.00000000000000f -}; - -/* Matlab code to produce table: -weightCurve = [0 ; 0.3 * sqrt(linspace(0,1,64))' + 0.1]; -fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', weightCurve); -*/ -const float WebRtcAec_weightCurve[65] = { - 0.0000f, 0.1000f, 0.1378f, 0.1535f, 0.1655f, 0.1756f, - 0.1845f, 0.1926f, 0.2000f, 0.2069f, 0.2134f, 0.2195f, - 0.2254f, 0.2309f, 0.2363f, 0.2414f, 0.2464f, 0.2512f, - 0.2558f, 0.2604f, 0.2648f, 0.2690f, 0.2732f, 0.2773f, - 0.2813f, 0.2852f, 0.2890f, 0.2927f, 0.2964f, 0.3000f, - 0.3035f, 0.3070f, 0.3104f, 0.3138f, 0.3171f, 0.3204f, - 0.3236f, 0.3268f, 0.3299f, 0.3330f, 0.3360f, 0.3390f, - 0.3420f, 0.3449f, 0.3478f, 0.3507f, 0.3535f, 0.3563f, - 0.3591f, 0.3619f, 0.3646f, 0.3673f, 0.3699f, 0.3726f, - 0.3752f, 0.3777f, 0.3803f, 0.3828f, 0.3854f, 0.3878f, - 0.3903f, 0.3928f, 0.3952f, 0.3976f, 0.4000f -}; - -/* Matlab code to produce table: -overDriveCurve = [sqrt(linspace(0,1,65))' + 1]; -fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', overDriveCurve); -*/ -const float WebRtcAec_overDriveCurve[65] = { - 1.0000f, 1.1250f, 1.1768f, 1.2165f, 1.2500f, 1.2795f, - 1.3062f, 1.3307f, 1.3536f, 1.3750f, 1.3953f, 1.4146f, - 1.4330f, 1.4507f, 1.4677f, 1.4841f, 1.5000f, 1.5154f, - 1.5303f, 1.5449f, 1.5590f, 1.5728f, 1.5863f, 1.5995f, - 1.6124f, 1.6250f, 1.6374f, 1.6495f, 1.6614f, 1.6731f, - 1.6847f, 1.6960f, 1.7071f, 1.7181f, 1.7289f, 1.7395f, - 1.7500f, 1.7603f, 1.7706f, 1.7806f, 1.7906f, 1.8004f, - 1.8101f, 1.8197f, 1.8292f, 1.8385f, 1.8478f, 1.8570f, - 1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f, - 1.9186f, 1.9270f, 1.9354f, 1.9437f, 1.9520f, 1.9601f, - 1.9682f, 1.9763f, 1.9843f, 1.9922f, 2.0000f -}; - -// "Private" function prototypes. -static void ProcessBlock(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH); - -static void BufferFar(aec_t *aec, const short *farend, int farLen); -static void FetchFar(aec_t *aec, short *farend, int farLen, int knownDelay); - -static void NonLinearProcessing(aec_t *aec, short *output, short *outputH); - -static void GetHighbandGain(const float *lambda, float *nlpGainHband); - -// Comfort_noise also computes noise for H band returned in comfortNoiseHband -static void ComfortNoise(aec_t *aec, float efw[2][PART_LEN1], - complex_t *comfortNoiseHband, - const float *noisePow, const float *lambda); - -static void WebRtcAec_InitLevel(power_level_t *level); -static void WebRtcAec_InitStats(stats_t *stats); -static void UpdateLevel(power_level_t *level, const short *in); -static void UpdateMetrics(aec_t *aec); - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) -{ - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) -{ - return aRe * bIm + aIm * bRe; -} - -static int CmpFloat(const void *a, const void *b) -{ - const float *da = (const float *)a; - const float *db = (const float *)b; - - return (*da > *db) - (*da < *db); -} - -int WebRtcAec_CreateAec(aec_t **aecInst) -{ - aec_t *aec = malloc(sizeof(aec_t)); - *aecInst = aec; - if (aec == NULL) { - return -1; - } - - if (WebRtcApm_CreateBuffer(&aec->farFrBuf, FRAME_LEN + PART_LEN) == -1) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aec->nearFrBuf, FRAME_LEN + PART_LEN) == -1) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aec->outFrBuf, FRAME_LEN + PART_LEN) == -1) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aec->nearFrBufH, FRAME_LEN + PART_LEN) == -1) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aec->outFrBufH, FRAME_LEN + PART_LEN) == -1) { - WebRtcAec_FreeAec(aec); - aec = NULL; - return -1; - } - - return 0; -} - -int WebRtcAec_FreeAec(aec_t *aec) -{ - if (aec == NULL) { - return -1; - } - - WebRtcApm_FreeBuffer(aec->farFrBuf); - WebRtcApm_FreeBuffer(aec->nearFrBuf); - WebRtcApm_FreeBuffer(aec->outFrBuf); - - WebRtcApm_FreeBuffer(aec->nearFrBufH); - WebRtcApm_FreeBuffer(aec->outFrBufH); - - free(aec); - return 0; -} - -static void FilterFar(aec_t *aec, float yf[2][PART_LEN1]) -{ - int i; - for (i = 0; i < NR_PART; i++) { - int j; - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= NR_PART) { - xPos -= NR_PART*(PART_LEN1); - } - - for (j = 0; j < PART_LEN1; j++) { - yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], - aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); - yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], - aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); - } - } -} - -static void ScaleErrorSignal(aec_t *aec, float ef[2][PART_LEN1]) -{ - int i; - float absEf; - for (i = 0; i < (PART_LEN1); i++) { - ef[0][i] /= (aec->xPow[i] + 1e-10f); - ef[1][i] /= (aec->xPow[i] + 1e-10f); - absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (absEf > aec->errThresh) { - absEf = aec->errThresh / (absEf + 1e-10f); - ef[0][i] *= absEf; - ef[1][i] *= absEf; - } - - // Stepsize factor - ef[0][i] *= aec->mu; - ef[1][i] *= aec->mu; - } -} - -static void FilterAdaptation(aec_t *aec, float *fft, float ef[2][PART_LEN1]) { - int i, j; - for (i = 0; i < NR_PART; i++) { - int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); - int pos; - // Check for wrap - if (i + aec->xfBufBlockPos >= NR_PART) { - xPos -= NR_PART * PART_LEN1; - } - - pos = i * PART_LEN1; - -#ifdef UNCONSTR - for (j = 0; j < PART_LEN1; j++) { - aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0], - -aec->xfBuf[xPos + j][1], - ef[j][0], ef[j][1]); - aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0], - -aec->xfBuf[xPos + j][1], - ef[j][0], ef[j][1]); - } -#else - for (j = 0; j < PART_LEN; j++) { - - fft[2 * j] = MulRe(aec->xfBuf[0][xPos + j], - -aec->xfBuf[1][xPos + j], - ef[0][j], ef[1][j]); - fft[2 * j + 1] = MulIm(aec->xfBuf[0][xPos + j], - -aec->xfBuf[1][xPos + j], - ef[0][j], ef[1][j]); - } - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], - -aec->xfBuf[1][xPos + PART_LEN], - ef[0][PART_LEN], ef[1][PART_LEN]); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float) * PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - for (j = 0; j < PART_LEN; j++) { - fft[j] *= scale; - } - } - aec_rdft_forward_128(fft); - - aec->wfBuf[0][pos] += fft[0]; - aec->wfBuf[0][pos + PART_LEN] += fft[1]; - - for (j = 1; j < PART_LEN; j++) { - aec->wfBuf[0][pos + j] += fft[2 * j]; - aec->wfBuf[1][pos + j] += fft[2 * j + 1]; - } -#endif // UNCONSTR - } -} - -static void OverdriveAndSuppress(aec_t *aec, float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - for (i = 0; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters here - // because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -WebRtcAec_FilterFar_t WebRtcAec_FilterFar; -WebRtcAec_ScaleErrorSignal_t WebRtcAec_ScaleErrorSignal; -WebRtcAec_FilterAdaptation_t WebRtcAec_FilterAdaptation; -WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress; - -int WebRtcAec_InitAec(aec_t *aec, int sampFreq) -{ - int i; - - aec->sampFreq = sampFreq; - - if (sampFreq == 8000) { - aec->mu = 0.6f; - aec->errThresh = 2e-6f; - } - else { - aec->mu = 0.5f; - aec->errThresh = 1.5e-6f; - } - - if (WebRtcApm_InitBuffer(aec->farFrBuf) == -1) { - return -1; - } - - if (WebRtcApm_InitBuffer(aec->nearFrBuf) == -1) { - return -1; - } - - if (WebRtcApm_InitBuffer(aec->outFrBuf) == -1) { - return -1; - } - - if (WebRtcApm_InitBuffer(aec->nearFrBufH) == -1) { - return -1; - } - - if (WebRtcApm_InitBuffer(aec->outFrBufH) == -1) { - return -1; - } - - // Default target suppression level - aec->targetSupp = -11.5; - aec->minOverDrive = 2.0; - - // Sampling frequency multiplier - // SWB is processed as 160 frame size - if (aec->sampFreq == 32000) { - aec->mult = (short)aec->sampFreq / 16000; - } - else { - aec->mult = (short)aec->sampFreq / 8000; - } - - aec->farBufWritePos = 0; - aec->farBufReadPos = 0; - - aec->inSamples = 0; - aec->outSamples = 0; - aec->knownDelay = 0; - - // Initialize buffers - memset(aec->farBuf, 0, sizeof(aec->farBuf)); - memset(aec->xBuf, 0, sizeof(aec->xBuf)); - memset(aec->dBuf, 0, sizeof(aec->dBuf)); - memset(aec->eBuf, 0, sizeof(aec->eBuf)); - // For H band - memset(aec->dBufH, 0, sizeof(aec->dBufH)); - - memset(aec->xPow, 0, sizeof(aec->xPow)); - memset(aec->dPow, 0, sizeof(aec->dPow)); - memset(aec->dInitMinPow, 0, sizeof(aec->dInitMinPow)); - aec->noisePow = aec->dInitMinPow; - aec->noiseEstCtr = 0; - - // Initial comfort noise power - for (i = 0; i < PART_LEN1; i++) { - aec->dMinPow[i] = 1.0e6f; - } - - // Holds the last block written to - aec->xfBufBlockPos = 0; - // TODO: Investigate need for these initializations. Deleting them doesn't - // change the output at all and yields 0.4% overall speedup. - memset(aec->xfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1); - memset(aec->wfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1); - memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1); - memset(aec->xfwBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1); - memset(aec->se, 0, sizeof(float) * PART_LEN1); - - // To prevent numerical instability in the first block. - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = 1; - } - for (i = 0; i < PART_LEN1; i++) { - aec->sx[i] = 1; - } - - memset(aec->hNs, 0, sizeof(aec->hNs)); - memset(aec->outBuf, 0, sizeof(float) * PART_LEN); - - aec->hNlFbMin = 1; - aec->hNlFbLocalMin = 1; - aec->hNlXdAvgMin = 1; - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = 2; - aec->overDriveSm = 2; - aec->delayIdx = 0; - aec->stNearState = 0; - aec->echoState = 0; - aec->divergeState = 0; - - aec->seed = 777; - aec->delayEstCtr = 0; - - // Features on by default (G.167) -#ifdef G167 - aec->adaptToggle = 1; - aec->nlpToggle = 1; - aec->cnToggle = 1; -#endif - - // Metrics disabled by default - aec->metricsMode = 0; - WebRtcAec_InitMetrics(aec); - - // Assembly optimization - WebRtcAec_FilterFar = FilterFar; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignal; - WebRtcAec_FilterAdaptation = FilterAdaptation; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppress; - if (WebRtc_GetCPUInfo(kSSE2)) { -#if defined(__SSE2__) - WebRtcAec_InitAec_SSE2(); -#endif - } - aec_rdft_init(); - - return 0; -} - -void WebRtcAec_InitMetrics(aec_t *aec) -{ - aec->stateCounter = 0; - WebRtcAec_InitLevel(&aec->farlevel); - WebRtcAec_InitLevel(&aec->nearlevel); - WebRtcAec_InitLevel(&aec->linoutlevel); - WebRtcAec_InitLevel(&aec->nlpoutlevel); - - WebRtcAec_InitStats(&aec->erl); - WebRtcAec_InitStats(&aec->erle); - WebRtcAec_InitStats(&aec->aNlp); - WebRtcAec_InitStats(&aec->rerl); -} - - -void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH, - int knownDelay) -{ - short farBl[PART_LEN], nearBl[PART_LEN], outBl[PART_LEN]; - short farFr[FRAME_LEN]; - // For H band - short nearBlH[PART_LEN], outBlH[PART_LEN]; - - int size = 0; - - // initialize: only used for SWB - memset(nearBlH, 0, sizeof(nearBlH)); - memset(outBlH, 0, sizeof(outBlH)); - - // Buffer the current frame. - // Fetch an older one corresponding to the delay. - BufferFar(aec, farend, FRAME_LEN); - FetchFar(aec, farFr, FRAME_LEN, knownDelay); - - // Buffer the synchronized far and near frames, - // to pass the smaller blocks individually. - WebRtcApm_WriteBuffer(aec->farFrBuf, farFr, FRAME_LEN); - WebRtcApm_WriteBuffer(aec->nearFrBuf, nearend, FRAME_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_WriteBuffer(aec->nearFrBufH, nearendH, FRAME_LEN); - } - - // Process as many blocks as possible. - while (WebRtcApm_get_buffer_size(aec->farFrBuf) >= PART_LEN) { - - WebRtcApm_ReadBuffer(aec->farFrBuf, farBl, PART_LEN); - WebRtcApm_ReadBuffer(aec->nearFrBuf, nearBl, PART_LEN); - - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_ReadBuffer(aec->nearFrBufH, nearBlH, PART_LEN); - } - - ProcessBlock(aec, farBl, nearBl, nearBlH, outBl, outBlH); - - WebRtcApm_WriteBuffer(aec->outFrBuf, outBl, PART_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_WriteBuffer(aec->outFrBufH, outBlH, PART_LEN); - } - } - - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - size = WebRtcApm_get_buffer_size(aec->outFrBuf); - if (size < FRAME_LEN) { - WebRtcApm_StuffBuffer(aec->outFrBuf, FRAME_LEN - size); - if (aec->sampFreq == 32000) { - WebRtcApm_StuffBuffer(aec->outFrBufH, FRAME_LEN - size); - } - } - - // Obtain an output frame. - WebRtcApm_ReadBuffer(aec->outFrBuf, out, FRAME_LEN); - // For H band - if (aec->sampFreq == 32000) { - WebRtcApm_ReadBuffer(aec->outFrBufH, outH, FRAME_LEN); - } -} - -static void ProcessBlock(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *output, short *outputH) -{ - int i; - float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN]; - short eInt16[PART_LEN]; - float scale; - - float fft[PART_LEN2]; - float xf[2][PART_LEN1], yf[2][PART_LEN1], ef[2][PART_LEN1]; - complex_t df[PART_LEN1]; - - const float gPow[2] = {0.9f, 0.1f}; - - // Noise estimate constants. - const int noiseInitBlocks = 500 * aec->mult; - const float step = 0.1f; - const float ramp = 1.0002f; - const float gInitNoise[2] = {0.999f, 0.001f}; - -#ifdef AEC_DEBUG - fwrite(farend, sizeof(short), PART_LEN, aec->farFile); - fwrite(nearend, sizeof(short), PART_LEN, aec->nearFile); -#endif - - memset(dH, 0, sizeof(dH)); - - // ---------- Ooura fft ---------- - // Concatenate old and new farend blocks. - for (i = 0; i < PART_LEN; i++) { - aec->xBuf[i + PART_LEN] = (float)farend[i]; - d[i] = (float)nearend[i]; - } - - if (aec->sampFreq == 32000) { - for (i = 0; i < PART_LEN; i++) { - dH[i] = (float)nearendH[i]; - } - } - - - memcpy(fft, aec->xBuf, sizeof(float) * PART_LEN2); - memcpy(aec->dBuf + PART_LEN, d, sizeof(float) * PART_LEN); - // For H band - if (aec->sampFreq == 32000) { - memcpy(aec->dBufH + PART_LEN, dH, sizeof(float) * PART_LEN); - } - - aec_rdft_forward_128(fft); - - // Far fft - xf[1][0] = 0; - xf[1][PART_LEN] = 0; - xf[0][0] = fft[0]; - xf[0][PART_LEN] = fft[1]; - - for (i = 1; i < PART_LEN; i++) { - xf[0][i] = fft[2 * i]; - xf[1][i] = fft[2 * i + 1]; - } - - // Near fft - memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2); - aec_rdft_forward_128(fft); - df[0][1] = 0; - df[PART_LEN][1] = 0; - df[0][0] = fft[0]; - df[PART_LEN][0] = fft[1]; - - for (i = 1; i < PART_LEN; i++) { - df[i][0] = fft[2 * i]; - df[i][1] = fft[2 * i + 1]; - } - - // Power smoothing - for (i = 0; i < PART_LEN1; i++) { - aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * NR_PART * - (xf[0][i] * xf[0][i] + xf[1][i] * xf[1][i]); - aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * - (df[i][0] * df[i][0] + df[i][1] * df[i][1]); - } - - // Estimate noise power. Wait until dPow is more stable. - if (aec->noiseEstCtr > 50) { - for (i = 0; i < PART_LEN1; i++) { - if (aec->dPow[i] < aec->dMinPow[i]) { - aec->dMinPow[i] = (aec->dPow[i] + step * (aec->dMinPow[i] - - aec->dPow[i])) * ramp; - } - else { - aec->dMinPow[i] *= ramp; - } - } - } - - // Smooth increasing noise power from zero at the start, - // to avoid a sudden burst of comfort noise. - if (aec->noiseEstCtr < noiseInitBlocks) { - aec->noiseEstCtr++; - for (i = 0; i < PART_LEN1; i++) { - if (aec->dMinPow[i] > aec->dInitMinPow[i]) { - aec->dInitMinPow[i] = gInitNoise[0] * aec->dInitMinPow[i] + - gInitNoise[1] * aec->dMinPow[i]; - } - else { - aec->dInitMinPow[i] = aec->dMinPow[i]; - } - } - aec->noisePow = aec->dInitMinPow; - } - else { - aec->noisePow = aec->dMinPow; - } - - - // Update the xfBuf block position. - aec->xfBufBlockPos--; - if (aec->xfBufBlockPos == -1) { - aec->xfBufBlockPos = NR_PART - 1; - } - - // Buffer xf - memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, xf[0], - sizeof(float) * PART_LEN1); - memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, xf[1], - sizeof(float) * PART_LEN1); - - memset(yf[0], 0, sizeof(float) * (PART_LEN1 * 2)); - - // Filter far - WebRtcAec_FilterFar(aec, yf); - - // Inverse fft to obtain echo estimate and error. - fft[0] = yf[0][0]; - fft[1] = yf[0][PART_LEN]; - for (i = 1; i < PART_LEN; i++) { - fft[2 * i] = yf[0][i]; - fft[2 * i + 1] = yf[1][i]; - } - aec_rdft_inverse_128(fft); - - scale = 2.0f / PART_LEN2; - for (i = 0; i < PART_LEN; i++) { - y[i] = fft[PART_LEN + i] * scale; // fft scaling - } - - for (i = 0; i < PART_LEN; i++) { - e[i] = d[i] - y[i]; - } - - // Error fft - memcpy(aec->eBuf + PART_LEN, e, sizeof(float) * PART_LEN); - memset(fft, 0, sizeof(float) * PART_LEN); - memcpy(fft + PART_LEN, e, sizeof(float) * PART_LEN); - aec_rdft_forward_128(fft); - - ef[1][0] = 0; - ef[1][PART_LEN] = 0; - ef[0][0] = fft[0]; - ef[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - ef[0][i] = fft[2 * i]; - ef[1][i] = fft[2 * i + 1]; - } - - // Scale error signal inversely with far power. - WebRtcAec_ScaleErrorSignal(aec, ef); -#ifdef G167 - if (aec->adaptToggle) { -#endif - // Filter adaptation - WebRtcAec_FilterAdaptation(aec, fft, ef); -#ifdef G167 - } -#endif - - NonLinearProcessing(aec, output, outputH); - -#if defined(AEC_DEBUG) || defined(G167) - for (i = 0; i < PART_LEN; i++) { - eInt16[i] = (short)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i], - WEBRTC_SPL_WORD16_MIN); - } -#endif -#ifdef G167 - if (aec->nlpToggle == 0) { - memcpy(output, eInt16, sizeof(eInt16)); - } -#endif - - if (aec->metricsMode == 1) { - for (i = 0; i < PART_LEN; i++) { - eInt16[i] = (short)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, e[i], - WEBRTC_SPL_WORD16_MIN); - } - - // Update power levels and echo metrics - UpdateLevel(&aec->farlevel, farend); - UpdateLevel(&aec->nearlevel, nearend); - UpdateLevel(&aec->linoutlevel, eInt16); - UpdateLevel(&aec->nlpoutlevel, output); - UpdateMetrics(aec); - } - -#ifdef AEC_DEBUG - fwrite(eInt16, sizeof(short), PART_LEN, aec->outLpFile); - fwrite(output, sizeof(short), PART_LEN, aec->outFile); -#endif -} - -static void NonLinearProcessing(aec_t *aec, short *output, short *outputH) -{ - float efw[2][PART_LEN1], dfw[2][PART_LEN1]; - complex_t xfw[PART_LEN1]; - complex_t comfortNoiseHband[PART_LEN1]; - float fft[PART_LEN2]; - float scale, dtmp; - float nlpGainHband; - int i, j, pos; - - // Coherence and non-linear filter - float cohde[PART_LEN1], cohxd[PART_LEN1]; - float hNlDeAvg, hNlXdAvg; - float hNl[PART_LEN1]; - float hNlPref[PREF_BAND_SIZE]; - float hNlFb = 0, hNlFbLow = 0; - const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f; - const int prefBandSize = PREF_BAND_SIZE / aec->mult; - const int minPrefBand = 4 / aec->mult; - - // Near and error power sums - float sdSum = 0, seSum = 0; - - // Power estimate smoothing coefficients - const float gCoh[2][2] = {{0.9f, 0.1f}, {0.93f, 0.07f}}; - const float *ptrGCoh = gCoh[aec->mult - 1]; - - // Filter energey - float wfEnMax = 0, wfEn = 0; - const int delayEstInterval = 10 * aec->mult; - - aec->delayEstCtr++; - if (aec->delayEstCtr == delayEstInterval) { - aec->delayEstCtr = 0; - } - - // initialize comfort noise for H band - memset(comfortNoiseHband, 0, sizeof(comfortNoiseHband)); - nlpGainHband = (float)0.0; - dtmp = (float)0.0; - - // Measure energy in each filter partition to determine delay. - // TODO: Spread by computing one partition per block? - if (aec->delayEstCtr == 0) { - wfEnMax = 0; - aec->delayIdx = 0; - for (i = 0; i < NR_PART; i++) { - pos = i * PART_LEN1; - wfEn = 0; - for (j = 0; j < PART_LEN1; j++) { - wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] + - aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j]; - } - - if (wfEn > wfEnMax) { - wfEnMax = wfEn; - aec->delayIdx = i; - } - } - } - - // NLP - // Windowed far fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->xBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->xBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; - } - aec_rdft_forward_128(fft); - - xfw[0][1] = 0; - xfw[PART_LEN][1] = 0; - xfw[0][0] = fft[0]; - xfw[PART_LEN][0] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - xfw[i][0] = fft[2 * i]; - xfw[i][1] = fft[2 * i + 1]; - } - - // Buffer far. - memcpy(aec->xfwBuf, xfw, sizeof(xfw)); - - // Use delayed far. - memcpy(xfw, aec->xfwBuf + aec->delayIdx * PART_LEN1, sizeof(xfw)); - - // Windowed near fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->dBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->dBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; - } - aec_rdft_forward_128(fft); - - dfw[1][0] = 0; - dfw[1][PART_LEN] = 0; - dfw[0][0] = fft[0]; - dfw[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - dfw[0][i] = fft[2 * i]; - dfw[1][i] = fft[2 * i + 1]; - } - - // Windowed error fft - for (i = 0; i < PART_LEN; i++) { - fft[i] = aec->eBuf[i] * sqrtHanning[i]; - fft[PART_LEN + i] = aec->eBuf[PART_LEN + i] * sqrtHanning[PART_LEN - i]; - } - aec_rdft_forward_128(fft); - efw[1][0] = 0; - efw[1][PART_LEN] = 0; - efw[0][0] = fft[0]; - efw[0][PART_LEN] = fft[1]; - for (i = 1; i < PART_LEN; i++) { - efw[0][i] = fft[2 * i]; - efw[1][i] = fft[2 * i + 1]; - } - - // Smoothed PSD - for (i = 0; i < PART_LEN1; i++) { - aec->sd[i] = ptrGCoh[0] * aec->sd[i] + ptrGCoh[1] * - (dfw[0][i] * dfw[0][i] + dfw[1][i] * dfw[1][i]); - aec->se[i] = ptrGCoh[0] * aec->se[i] + ptrGCoh[1] * - (efw[0][i] * efw[0][i] + efw[1][i] * efw[1][i]); - // We threshold here to protect against the ill-effects of a zero farend. - // The threshold is not arbitrarily chosen, but balances protection and - // adverse interaction with the algorithm's tuning. - // TODO: investigate further why this is so sensitive. - aec->sx[i] = ptrGCoh[0] * aec->sx[i] + ptrGCoh[1] * - WEBRTC_SPL_MAX(xfw[i][0] * xfw[i][0] + xfw[i][1] * xfw[i][1], 15); - - aec->sde[i][0] = ptrGCoh[0] * aec->sde[i][0] + ptrGCoh[1] * - (dfw[0][i] * efw[0][i] + dfw[1][i] * efw[1][i]); - aec->sde[i][1] = ptrGCoh[0] * aec->sde[i][1] + ptrGCoh[1] * - (dfw[0][i] * efw[1][i] - dfw[1][i] * efw[0][i]); - - aec->sxd[i][0] = ptrGCoh[0] * aec->sxd[i][0] + ptrGCoh[1] * - (dfw[0][i] * xfw[i][0] + dfw[1][i] * xfw[i][1]); - aec->sxd[i][1] = ptrGCoh[0] * aec->sxd[i][1] + ptrGCoh[1] * - (dfw[0][i] * xfw[i][1] - dfw[1][i] * xfw[i][0]); - - sdSum += aec->sd[i]; - seSum += aec->se[i]; - } - - // Divergent filter safeguard. - if (aec->divergeState == 0) { - if (seSum > sdSum) { - aec->divergeState = 1; - } - } - else { - if (seSum * 1.05f < sdSum) { - aec->divergeState = 0; - } - } - - if (aec->divergeState == 1) { - memcpy(efw, dfw, sizeof(efw)); - } - - // Reset if error is significantly larger than nearend (13 dB). - if (seSum > (19.95f * sdSum)) { - memset(aec->wfBuf, 0, sizeof(aec->wfBuf)); - } - - // Subband coherence - for (i = 0; i < PART_LEN1; i++) { - cohde[i] = (aec->sde[i][0] * aec->sde[i][0] + aec->sde[i][1] * aec->sde[i][1]) / - (aec->sd[i] * aec->se[i] + 1e-10f); - cohxd[i] = (aec->sxd[i][0] * aec->sxd[i][0] + aec->sxd[i][1] * aec->sxd[i][1]) / - (aec->sx[i] * aec->sd[i] + 1e-10f); - } - - hNlXdAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlXdAvg += cohxd[i]; - } - hNlXdAvg /= prefBandSize; - hNlXdAvg = 1 - hNlXdAvg; - - hNlDeAvg = 0; - for (i = minPrefBand; i < prefBandSize + minPrefBand; i++) { - hNlDeAvg += cohde[i]; - } - hNlDeAvg /= prefBandSize; - - if (hNlXdAvg < 0.75f && hNlXdAvg < aec->hNlXdAvgMin) { - aec->hNlXdAvgMin = hNlXdAvg; - } - - if (hNlDeAvg > 0.98f && hNlXdAvg > 0.9f) { - aec->stNearState = 1; - } - else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) { - aec->stNearState = 0; - } - - if (aec->hNlXdAvgMin == 1) { - aec->echoState = 0; - aec->overDrive = aec->minOverDrive; - - if (aec->stNearState == 1) { - memcpy(hNl, cohde, sizeof(hNl)); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } - else { - for (i = 0; i < PART_LEN1; i++) { - hNl[i] = 1 - cohxd[i]; - } - hNlFb = hNlXdAvg; - hNlFbLow = hNlXdAvg; - } - } - else { - - if (aec->stNearState == 1) { - aec->echoState = 0; - memcpy(hNl, cohde, sizeof(hNl)); - hNlFb = hNlDeAvg; - hNlFbLow = hNlDeAvg; - } - else { - aec->echoState = 1; - for (i = 0; i < PART_LEN1; i++) { - hNl[i] = WEBRTC_SPL_MIN(cohde[i], 1 - cohxd[i]); - } - - // Select an order statistic from the preferred bands. - // TODO: Using quicksort now, but a selection algorithm may be preferred. - memcpy(hNlPref, &hNl[minPrefBand], sizeof(float) * prefBandSize); - qsort(hNlPref, prefBandSize, sizeof(float), CmpFloat); - hNlFb = hNlPref[(int)floor(prefBandQuant * (prefBandSize - 1))]; - hNlFbLow = hNlPref[(int)floor(prefBandQuantLow * (prefBandSize - 1))]; - } - } - - // Track the local filter minimum to determine suppression overdrive. - if (hNlFbLow < 0.6f && hNlFbLow < aec->hNlFbLocalMin) { - aec->hNlFbLocalMin = hNlFbLow; - aec->hNlFbMin = hNlFbLow; - aec->hNlNewMin = 1; - aec->hNlMinCtr = 0; - } - aec->hNlFbLocalMin = WEBRTC_SPL_MIN(aec->hNlFbLocalMin + 0.0008f / aec->mult, 1); - aec->hNlXdAvgMin = WEBRTC_SPL_MIN(aec->hNlXdAvgMin + 0.0006f / aec->mult, 1); - - if (aec->hNlNewMin == 1) { - aec->hNlMinCtr++; - } - if (aec->hNlMinCtr == 2) { - aec->hNlNewMin = 0; - aec->hNlMinCtr = 0; - aec->overDrive = WEBRTC_SPL_MAX(aec->targetSupp / - ((float)log(aec->hNlFbMin + 1e-10f) + 1e-10f), aec->minOverDrive); - } - - // Smooth the overdrive. - if (aec->overDrive < aec->overDriveSm) { - aec->overDriveSm = 0.99f * aec->overDriveSm + 0.01f * aec->overDrive; - } - else { - aec->overDriveSm = 0.9f * aec->overDriveSm + 0.1f * aec->overDrive; - } - - WebRtcAec_OverdriveAndSuppress(aec, hNl, hNlFb, efw); - -#ifdef G167 - if (aec->cnToggle) { - ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); - } -#else - // Add comfort noise. - ComfortNoise(aec, efw, comfortNoiseHband, aec->noisePow, hNl); -#endif - - // Inverse error fft. - fft[0] = efw[0][0]; - fft[1] = efw[0][PART_LEN]; - for (i = 1; i < PART_LEN; i++) { - fft[2*i] = efw[0][i]; - // Sign change required by Ooura fft. - fft[2*i + 1] = -efw[1][i]; - } - aec_rdft_inverse_128(fft); - - // Overlap and add to obtain output. - scale = 2.0f / PART_LEN2; - for (i = 0; i < PART_LEN; i++) { - fft[i] *= scale; // fft scaling - fft[i] = fft[i]*sqrtHanning[i] + aec->outBuf[i]; - - // Saturation protection - output[i] = (short)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, fft[i], - WEBRTC_SPL_WORD16_MIN); - - fft[PART_LEN + i] *= scale; // fft scaling - aec->outBuf[i] = fft[PART_LEN + i] * sqrtHanning[PART_LEN - i]; - } - - // For H band - if (aec->sampFreq == 32000) { - - // H band gain - // average nlp over low band: average over second half of freq spectrum - // (4->8khz) - GetHighbandGain(hNl, &nlpGainHband); - - // Inverse comfort_noise - if (flagHbandCn == 1) { - fft[0] = comfortNoiseHband[0][0]; - fft[1] = comfortNoiseHband[PART_LEN][0]; - for (i = 1; i < PART_LEN; i++) { - fft[2*i] = comfortNoiseHband[i][0]; - fft[2*i + 1] = comfortNoiseHband[i][1]; - } - aec_rdft_inverse_128(fft); - scale = 2.0f / PART_LEN2; - } - - // compute gain factor - for (i = 0; i < PART_LEN; i++) { - dtmp = (float)aec->dBufH[i]; - dtmp = (float)dtmp * nlpGainHband; // for variable gain - - // add some comfort noise where Hband is attenuated - if (flagHbandCn == 1) { - fft[i] *= scale; // fft scaling - dtmp += cnScaleHband * fft[i]; - } - - // Saturation protection - outputH[i] = (short)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, dtmp, - WEBRTC_SPL_WORD16_MIN); - } - } - - // Copy the current block to the old position. - memcpy(aec->xBuf, aec->xBuf + PART_LEN, sizeof(float) * PART_LEN); - memcpy(aec->dBuf, aec->dBuf + PART_LEN, sizeof(float) * PART_LEN); - memcpy(aec->eBuf, aec->eBuf + PART_LEN, sizeof(float) * PART_LEN); - - // Copy the current block to the old position for H band - if (aec->sampFreq == 32000) { - memcpy(aec->dBufH, aec->dBufH + PART_LEN, sizeof(float) * PART_LEN); - } - - memmove(aec->xfwBuf + PART_LEN1, aec->xfwBuf, sizeof(aec->xfwBuf) - - sizeof(complex_t) * PART_LEN1); -} - -static void GetHighbandGain(const float *lambda, float *nlpGainHband) -{ - int i; - - nlpGainHband[0] = (float)0.0; - for (i = freqAvgIc; i < PART_LEN1 - 1; i++) { - nlpGainHband[0] += lambda[i]; - } - nlpGainHband[0] /= (float)(PART_LEN1 - 1 - freqAvgIc); -} - -static void ComfortNoise(aec_t *aec, float efw[2][PART_LEN1], - complex_t *comfortNoiseHband, const float *noisePow, const float *lambda) -{ - int i, num; - float rand[PART_LEN]; - float noise, noiseAvg, tmp, tmpAvg; - WebRtc_Word16 randW16[PART_LEN]; - complex_t u[PART_LEN1]; - - const float pi2 = 6.28318530717959f; - - // Generate a uniform random array on [0 1] - WebRtcSpl_RandUArray(randW16, PART_LEN, &aec->seed); - for (i = 0; i < PART_LEN; i++) { - rand[i] = ((float)randW16[i]) / 32768; - } - - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; - - noise = sqrtf(noisePow[i]); - u[i][0] = noise * (float)cos(tmp); - u[i][1] = -noise * (float)sin(tmp); - } - u[PART_LEN][1] = 0; - - for (i = 0; i < PART_LEN1; i++) { - // This is the proper weighting to match the background noise power - tmp = sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - //tmp = 1 - lambda[i]; - efw[0][i] += tmp * u[i][0]; - efw[1][i] += tmp * u[i][1]; - } - - // For H band comfort noise - // TODO: don't compute noise and "tmp" twice. Use the previous results. - noiseAvg = 0.0; - tmpAvg = 0.0; - num = 0; - if (aec->sampFreq == 32000 && flagHbandCn == 1) { - - // average noise scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - noiseAvg += sqrtf(noisePow[i]); - } - noiseAvg /= (float)num; - - // average nlp scale - // average over second half of freq spectrum (i.e., 4->8khz) - // TODO: we shouldn't need num. We know how many elements we're summing. - num = 0; - for (i = PART_LEN1 >> 1; i < PART_LEN1; i++) { - num++; - tmpAvg += sqrtf(WEBRTC_SPL_MAX(1 - lambda[i] * lambda[i], 0)); - } - tmpAvg /= (float)num; - - // Use average noise for H band - // TODO: we should probably have a new random vector here. - // Reject LF noise - u[0][0] = 0; - u[0][1] = 0; - for (i = 1; i < PART_LEN1; i++) { - tmp = pi2 * rand[i - 1]; - - // Use average noise for H band - u[i][0] = noiseAvg * (float)cos(tmp); - u[i][1] = -noiseAvg * (float)sin(tmp); - } - u[PART_LEN][1] = 0; - - for (i = 0; i < PART_LEN1; i++) { - // Use average NLP weight for H band - comfortNoiseHband[i][0] = tmpAvg * u[i][0]; - comfortNoiseHband[i][1] = tmpAvg * u[i][1]; - } - } -} - -// Buffer the farend to account for knownDelay -static void BufferFar(aec_t *aec, const short *farend, int farLen) -{ - int writeLen = farLen, writePos = 0; - - // Check if the write position must be wrapped. - while (aec->farBufWritePos + writeLen > FAR_BUF_LEN) { - - // Write to remaining buffer space before wrapping. - writeLen = FAR_BUF_LEN - aec->farBufWritePos; - memcpy(aec->farBuf + aec->farBufWritePos, farend + writePos, - sizeof(short) * writeLen); - aec->farBufWritePos = 0; - writePos = writeLen; - writeLen = farLen - writeLen; - } - - memcpy(aec->farBuf + aec->farBufWritePos, farend + writePos, - sizeof(short) * writeLen); - aec->farBufWritePos += writeLen; -} - -static void FetchFar(aec_t *aec, short *farend, int farLen, int knownDelay) -{ - int readLen = farLen, readPos = 0, delayChange = knownDelay - aec->knownDelay; - - aec->farBufReadPos -= delayChange; - - // Check if delay forces a read position wrap. - while(aec->farBufReadPos < 0) { - aec->farBufReadPos += FAR_BUF_LEN; - } - while(aec->farBufReadPos > FAR_BUF_LEN - 1) { - aec->farBufReadPos -= FAR_BUF_LEN; - } - - aec->knownDelay = knownDelay; - - // Check if read position must be wrapped. - while (aec->farBufReadPos + readLen > FAR_BUF_LEN) { - - // Read from remaining buffer space before wrapping. - readLen = FAR_BUF_LEN - aec->farBufReadPos; - memcpy(farend + readPos, aec->farBuf + aec->farBufReadPos, - sizeof(short) * readLen); - aec->farBufReadPos = 0; - readPos = readLen; - readLen = farLen - readLen; - } - memcpy(farend + readPos, aec->farBuf + aec->farBufReadPos, - sizeof(short) * readLen); - aec->farBufReadPos += readLen; -} - -static void WebRtcAec_InitLevel(power_level_t *level) -{ - const float bigFloat = 1E17f; - - level->averagelevel = 0; - level->framelevel = 0; - level->minlevel = bigFloat; - level->frsum = 0; - level->sfrsum = 0; - level->frcounter = 0; - level->sfrcounter = 0; -} - -static void WebRtcAec_InitStats(stats_t *stats) -{ - stats->instant = offsetLevel; - stats->average = offsetLevel; - stats->max = offsetLevel; - stats->min = offsetLevel * (-1); - stats->sum = 0; - stats->hisum = 0; - stats->himean = offsetLevel; - stats->counter = 0; - stats->hicounter = 0; -} - -static void UpdateLevel(power_level_t *level, const short *in) -{ - int k; - - for (k = 0; k < PART_LEN; k++) { - level->sfrsum += in[k] * in[k]; - } - level->sfrcounter++; - - if (level->sfrcounter > subCountLen) { - level->framelevel = level->sfrsum / (subCountLen * PART_LEN); - level->sfrsum = 0; - level->sfrcounter = 0; - - if (level->framelevel > 0) { - if (level->framelevel < level->minlevel) { - level->minlevel = level->framelevel; // New minimum - } else { - level->minlevel *= (1 + 0.001f); // Small increase - } - } - level->frcounter++; - level->frsum += level->framelevel; - - if (level->frcounter > countLen) { - level->averagelevel = level->frsum / countLen; - level->frsum = 0; - level->frcounter = 0; - } - - } -} - -static void UpdateMetrics(aec_t *aec) -{ - float dtmp, dtmp2, dtmp3; - - const float actThresholdNoisy = 8.0f; - const float actThresholdClean = 40.0f; - const float safety = 0.99995f; - const float noisyPower = 300000.0f; - - float actThreshold; - float echo, suppressedEcho; - - if (aec->echoState) { // Check if echo is likely present - aec->stateCounter++; - } - - if (aec->farlevel.frcounter == countLen) { - - if (aec->farlevel.minlevel < noisyPower) { - actThreshold = actThresholdClean; - } - else { - actThreshold = actThresholdNoisy; - } - - if ((aec->stateCounter > (0.5f * countLen * subCountLen)) - && (aec->farlevel.sfrcounter == 0) - - // Estimate in active far-end segments only - && (aec->farlevel.averagelevel > (actThreshold * aec->farlevel.minlevel)) - ) { - - // Subtract noise power - echo = aec->nearlevel.averagelevel - safety * aec->nearlevel.minlevel; - - // ERL - dtmp = 10 * (float)log10(aec->farlevel.averagelevel / - aec->nearlevel.averagelevel + 1e-10f); - dtmp2 = 10 * (float)log10(aec->farlevel.averagelevel / echo + 1e-10f); - - aec->erl.instant = dtmp; - if (dtmp > aec->erl.max) { - aec->erl.max = dtmp; - } - - if (dtmp < aec->erl.min) { - aec->erl.min = dtmp; - } - - aec->erl.counter++; - aec->erl.sum += dtmp; - aec->erl.average = aec->erl.sum / aec->erl.counter; - - // Upper mean - if (dtmp > aec->erl.average) { - aec->erl.hicounter++; - aec->erl.hisum += dtmp; - aec->erl.himean = aec->erl.hisum / aec->erl.hicounter; - } - - // A_NLP - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - aec->linoutlevel.averagelevel + 1e-10f); - - // subtract noise power - suppressedEcho = aec->linoutlevel.averagelevel - safety * aec->linoutlevel.minlevel; - - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - dtmp3 = 10 * (float)log10(aec->nearlevel.averagelevel / suppressedEcho + 1e-10f); - - aec->aNlp.instant = dtmp2; - if (dtmp > aec->aNlp.max) { - aec->aNlp.max = dtmp; - } - - if (dtmp < aec->aNlp.min) { - aec->aNlp.min = dtmp; - } - - aec->aNlp.counter++; - aec->aNlp.sum += dtmp; - aec->aNlp.average = aec->aNlp.sum / aec->aNlp.counter; - - // Upper mean - if (dtmp > aec->aNlp.average) { - aec->aNlp.hicounter++; - aec->aNlp.hisum += dtmp; - aec->aNlp.himean = aec->aNlp.hisum / aec->aNlp.hicounter; - } - - // ERLE - - // subtract noise power - suppressedEcho = aec->nlpoutlevel.averagelevel - safety * aec->nlpoutlevel.minlevel; - - dtmp = 10 * (float)log10(aec->nearlevel.averagelevel / - aec->nlpoutlevel.averagelevel + 1e-10f); - dtmp2 = 10 * (float)log10(echo / suppressedEcho + 1e-10f); - - dtmp = dtmp2; - aec->erle.instant = dtmp; - if (dtmp > aec->erle.max) { - aec->erle.max = dtmp; - } - - if (dtmp < aec->erle.min) { - aec->erle.min = dtmp; - } - - aec->erle.counter++; - aec->erle.sum += dtmp; - aec->erle.average = aec->erle.sum / aec->erle.counter; - - // Upper mean - if (dtmp > aec->erle.average) { - aec->erle.hicounter++; - aec->erle.hisum += dtmp; - aec->erle.himean = aec->erle.hisum / aec->erle.hicounter; - } - } - - aec->stateCounter = 0; - } -} - diff --git a/modules/audio_processing/aec/main/source/aec_core.h b/modules/audio_processing/aec/main/source/aec_core.h deleted file mode 100644 index 3386b92fc..000000000 --- a/modules/audio_processing/aec/main/source/aec_core.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Specifies the interface for the AEC core. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_ - -#include -#include "typedefs.h" -#include "signal_processing_library.h" - -//#define G167 // for running G167 tests -//#define UNCONSTR // time-unconstrained filter -//#define AEC_DEBUG // for recording files - -#define FRAME_LEN 80 -#define PART_LEN 64 // Length of partition -#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients -#define PART_LEN2 (PART_LEN * 2) // Length of partition * 2 -#define NR_PART 12 // Number of partitions -#define FILT_LEN (PART_LEN * NR_PART) // Filter length -#define FILT_LEN2 (FILT_LEN * 2) // Double filter length -#define FAR_BUF_LEN (FILT_LEN2 * 2) -#define PREF_BAND_SIZE 24 - -#define BLOCKL_MAX FRAME_LEN - -typedef float complex_t[2]; -// For performance reasons, some arrays of complex numbers are replaced by twice -// as long arrays of float, all the real parts followed by all the imaginary -// ones (complex_t[SIZE] -> float[2][SIZE]). This allows SIMD optimizations and -// is better than two arrays (one for the real parts and one for the imaginary -// parts) as this other way would require two pointers instead of one and cause -// extra register spilling. This also allows the offsets to be calculated at -// compile time. - -// Metrics -enum {offsetLevel = -100}; - -typedef struct { - float sfrsum; - int sfrcounter; - float framelevel; - float frsum; - int frcounter; - float minlevel; - float averagelevel; -} power_level_t; - -typedef struct { - float instant; - float average; - float min; - float max; - float sum; - float hisum; - float himean; - int counter; - int hicounter; -} stats_t; - -typedef struct { - int farBufWritePos, farBufReadPos; - - int knownDelay; - int inSamples, outSamples; - int delayEstCtr; - - void *farFrBuf, *nearFrBuf, *outFrBuf; - - void *nearFrBufH; - void *outFrBufH; - - float xBuf[PART_LEN2]; // farend - float dBuf[PART_LEN2]; // nearend - float eBuf[PART_LEN2]; // error - - float dBufH[PART_LEN2]; // nearend - - float xPow[PART_LEN1]; - float dPow[PART_LEN1]; - float dMinPow[PART_LEN1]; - float dInitMinPow[PART_LEN1]; - float *noisePow; -#ifdef FFTW - float fftR[PART_LEN2]; - fftw_complex fftC[PART_LEN2]; - fftw_plan fftPlan, ifftPlan; - - fftw_complex xfBuf[NR_PART * PART_LEN1]; - fftw_complex wfBuf[NR_PART * PART_LEN1]; - fftw_complex sde[PART_LEN1]; -#else - float xfBuf[2][NR_PART * PART_LEN1]; // farend fft buffer - float wfBuf[2][NR_PART * PART_LEN1]; // filter fft - complex_t sde[PART_LEN1]; // cross-psd of nearend and error - complex_t sxd[PART_LEN1]; // cross-psd of farend and nearend - complex_t xfwBuf[NR_PART * PART_LEN1]; // farend windowed fft buffer -#endif - float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1]; // far, near and error psd - float hNs[PART_LEN1]; - float hNlFbMin, hNlFbLocalMin; - float hNlXdAvgMin; - int hNlNewMin, hNlMinCtr; - float overDrive, overDriveSm; - float targetSupp, minOverDrive; - float outBuf[PART_LEN]; - int delayIdx; - - short stNearState, echoState; - short divergeState; - - int xfBufBlockPos; - - short farBuf[FILT_LEN2 * 2]; - - short mult; // sampling frequency multiple - int sampFreq; - WebRtc_UWord32 seed; - - float mu; // stepsize - float errThresh; // error threshold - - int noiseEstCtr; - - // Toggles for G.167 testing -#ifdef G167 - short adaptToggle; // Filter adaptation - short nlpToggle; // Nonlinear processing - short cnToggle; // Comfort noise -#endif - - power_level_t farlevel; - power_level_t nearlevel; - power_level_t linoutlevel; - power_level_t nlpoutlevel; - - int metricsMode; - int stateCounter; - stats_t erl; - stats_t erle; - stats_t aNlp; - stats_t rerl; - - // Quantities to control H band scaling for SWB input - int freq_avg_ic; //initial bin for averaging nlp gain - int flag_Hband_cn; //for comfort noise - float cn_scale_Hband; //scale for comfort noise in H band - -#ifdef AEC_DEBUG - FILE *farFile; - FILE *nearFile; - FILE *outFile; - FILE *outLpFile; -#endif -} aec_t; - -typedef void (*WebRtcAec_FilterFar_t)(aec_t *aec, float yf[2][PART_LEN1]); -extern WebRtcAec_FilterFar_t WebRtcAec_FilterFar; -typedef void (*WebRtcAec_ScaleErrorSignal_t)(aec_t *aec, float ef[2][PART_LEN1]); -extern WebRtcAec_ScaleErrorSignal_t WebRtcAec_ScaleErrorSignal; -#define IP_LEN PART_LEN // this must be at least ceil(2 + sqrt(PART_LEN)) -#define W_LEN PART_LEN -typedef void (*WebRtcAec_FilterAdaptation_t) - (aec_t *aec, float *fft, float ef[2][PART_LEN1]); -extern WebRtcAec_FilterAdaptation_t WebRtcAec_FilterAdaptation; -typedef void (*WebRtcAec_OverdriveAndSuppress_t) - (aec_t *aec, float hNl[PART_LEN1], const float hNlFb, float efw[2][PART_LEN1]); -extern WebRtcAec_OverdriveAndSuppress_t WebRtcAec_OverdriveAndSuppress; - -int WebRtcAec_CreateAec(aec_t **aec); -int WebRtcAec_FreeAec(aec_t *aec); -int WebRtcAec_InitAec(aec_t *aec, int sampFreq); -void WebRtcAec_InitAec_SSE2(void); - -void WebRtcAec_InitMetrics(aec_t *aec); -void WebRtcAec_ProcessFrame(aec_t *aec, const short *farend, - const short *nearend, const short *nearendH, - short *out, short *outH, - int knownDelay); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_AEC_CORE_H_ - diff --git a/modules/audio_processing/aec/main/source/aec_core_sse2.c b/modules/audio_processing/aec/main/source/aec_core_sse2.c deleted file mode 100644 index 524669fe9..000000000 --- a/modules/audio_processing/aec/main/source/aec_core_sse2.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * The core AEC algorithm, SSE2 version of speed-critical functions. - */ - -#if defined(__SSE2__) -#include -#include - -#include "aec_core.h" -#include "aec_rdft.h" - -__inline static float MulRe(float aRe, float aIm, float bRe, float bIm) -{ - return aRe * bRe - aIm * bIm; -} - -__inline static float MulIm(float aRe, float aIm, float bRe, float bIm) -{ - return aRe * bIm + aIm * bRe; -} - -static void FilterFarSSE2(aec_t *aec, float yf[2][PART_LEN1]) -{ - int i; - for (i = 0; i < NR_PART; i++) { - int j; - int xPos = (i + aec->xfBufBlockPos) * PART_LEN1; - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= NR_PART) { - xPos -= NR_PART*(PART_LEN1); - } - - // vectorized code (four at once) - for (j = 0; j + 3 < PART_LEN1; j += 4) { - const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]); - const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]); - const __m128 wfBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); - const __m128 wfBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); - const __m128 yf_re = _mm_loadu_ps(&yf[0][j]); - const __m128 yf_im = _mm_loadu_ps(&yf[1][j]); - const __m128 a = _mm_mul_ps(xfBuf_re, wfBuf_re); - const __m128 b = _mm_mul_ps(xfBuf_im, wfBuf_im); - const __m128 c = _mm_mul_ps(xfBuf_re, wfBuf_im); - const __m128 d = _mm_mul_ps(xfBuf_im, wfBuf_re); - const __m128 e = _mm_sub_ps(a, b); - const __m128 f = _mm_add_ps(c, d); - const __m128 g = _mm_add_ps(yf_re, e); - const __m128 h = _mm_add_ps(yf_im, f); - _mm_storeu_ps(&yf[0][j], g); - _mm_storeu_ps(&yf[1][j], h); - } - // scalar code for the remaining items. - for (; j < PART_LEN1; j++) { - yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], - aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); - yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j], - aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]); - } - } -} - -static void ScaleErrorSignalSSE2(aec_t *aec, float ef[2][PART_LEN1]) -{ - const __m128 k1e_10f = _mm_set1_ps(1e-10f); - const __m128 kThresh = _mm_set1_ps(aec->errThresh); - const __m128 kMu = _mm_set1_ps(aec->mu); - - int i; - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i += 4) { - const __m128 xPow = _mm_loadu_ps(&aec->xPow[i]); - const __m128 ef_re_base = _mm_loadu_ps(&ef[0][i]); - const __m128 ef_im_base = _mm_loadu_ps(&ef[1][i]); - - const __m128 xPowPlus = _mm_add_ps(xPow, k1e_10f); - __m128 ef_re = _mm_div_ps(ef_re_base, xPowPlus); - __m128 ef_im = _mm_div_ps(ef_im_base, xPowPlus); - const __m128 ef_re2 = _mm_mul_ps(ef_re, ef_re); - const __m128 ef_im2 = _mm_mul_ps(ef_im, ef_im); - const __m128 ef_sum2 = _mm_add_ps(ef_re2, ef_im2); - const __m128 absEf = _mm_sqrt_ps(ef_sum2); - const __m128 bigger = _mm_cmpgt_ps(absEf, kThresh); - __m128 absEfPlus = _mm_add_ps(absEf, k1e_10f); - const __m128 absEfInv = _mm_div_ps(kThresh, absEfPlus); - __m128 ef_re_if = _mm_mul_ps(ef_re, absEfInv); - __m128 ef_im_if = _mm_mul_ps(ef_im, absEfInv); - ef_re_if = _mm_and_ps(bigger, ef_re_if); - ef_im_if = _mm_and_ps(bigger, ef_im_if); - ef_re = _mm_andnot_ps(bigger, ef_re); - ef_im = _mm_andnot_ps(bigger, ef_im); - ef_re = _mm_or_ps(ef_re, ef_re_if); - ef_im = _mm_or_ps(ef_im, ef_im_if); - ef_re = _mm_mul_ps(ef_re, kMu); - ef_im = _mm_mul_ps(ef_im, kMu); - - _mm_storeu_ps(&ef[0][i], ef_re); - _mm_storeu_ps(&ef[1][i], ef_im); - } - // scalar code for the remaining items. - for (; i < (PART_LEN1); i++) { - float absEf; - ef[0][i] /= (aec->xPow[i] + 1e-10f); - ef[1][i] /= (aec->xPow[i] + 1e-10f); - absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]); - - if (absEf > aec->errThresh) { - absEf = aec->errThresh / (absEf + 1e-10f); - ef[0][i] *= absEf; - ef[1][i] *= absEf; - } - - // Stepsize factor - ef[0][i] *= aec->mu; - ef[1][i] *= aec->mu; - } -} - -static void FilterAdaptationSSE2(aec_t *aec, float *fft, float ef[2][PART_LEN1]) { - int i, j; - for (i = 0; i < NR_PART; i++) { - int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1); - int pos = i * PART_LEN1; - // Check for wrap - if (i + aec->xfBufBlockPos >= NR_PART) { - xPos -= NR_PART * PART_LEN1; - } - -#ifdef UNCONSTR - for (j = 0; j < PART_LEN1; j++) { - aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0], - -aec->xfBuf[xPos + j][1], - ef[j][0], ef[j][1]); - aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0], - -aec->xfBuf[xPos + j][1], - ef[j][0], ef[j][1]); - } -#else - // Process the whole array... - for (j = 0; j < PART_LEN; j+= 4) { - // Load xfBuf and ef. - const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]); - const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]); - const __m128 ef_re = _mm_loadu_ps(&ef[0][j]); - const __m128 ef_im = _mm_loadu_ps(&ef[1][j]); - // Calculate the product of conjugate(xfBuf) by ef. - // re(conjugate(a) * b) = aRe * bRe + aIm * bIm - // im(conjugate(a) * b)= aRe * bIm - aIm * bRe - const __m128 a = _mm_mul_ps(xfBuf_re, ef_re); - const __m128 b = _mm_mul_ps(xfBuf_im, ef_im); - const __m128 c = _mm_mul_ps(xfBuf_re, ef_im); - const __m128 d = _mm_mul_ps(xfBuf_im, ef_re); - const __m128 e = _mm_add_ps(a, b); - const __m128 f = _mm_sub_ps(c, d); - // Interleave real and imaginary parts. - const __m128 g = _mm_unpacklo_ps(e, f); - const __m128 h = _mm_unpackhi_ps(e, f); - // Store - _mm_storeu_ps(&fft[2*j + 0], g); - _mm_storeu_ps(&fft[2*j + 4], h); - } - // ... and fixup the first imaginary entry. - fft[1] = MulRe(aec->xfBuf[0][xPos + PART_LEN], - -aec->xfBuf[1][xPos + PART_LEN], - ef[0][PART_LEN], ef[1][PART_LEN]); - - aec_rdft_inverse_128(fft); - memset(fft + PART_LEN, 0, sizeof(float)*PART_LEN); - - // fft scaling - { - float scale = 2.0f / PART_LEN2; - const __m128 scale_ps = _mm_load_ps1(&scale); - for (j = 0; j < PART_LEN; j+=4) { - const __m128 fft_ps = _mm_loadu_ps(&fft[j]); - const __m128 fft_scale = _mm_mul_ps(fft_ps, scale_ps); - _mm_storeu_ps(&fft[j], fft_scale); - } - } - aec_rdft_forward_128(fft); - - { - float wt1 = aec->wfBuf[1][pos]; - aec->wfBuf[0][pos + PART_LEN] += fft[1]; - for (j = 0; j < PART_LEN; j+= 4) { - __m128 wtBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]); - __m128 wtBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]); - const __m128 fft0 = _mm_loadu_ps(&fft[2 * j + 0]); - const __m128 fft4 = _mm_loadu_ps(&fft[2 * j + 4]); - const __m128 fft_re = _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(2, 0, 2 ,0)); - const __m128 fft_im = _mm_shuffle_ps(fft0, fft4, _MM_SHUFFLE(3, 1, 3 ,1)); - wtBuf_re = _mm_add_ps(wtBuf_re, fft_re); - wtBuf_im = _mm_add_ps(wtBuf_im, fft_im); - _mm_storeu_ps(&aec->wfBuf[0][pos + j], wtBuf_re); - _mm_storeu_ps(&aec->wfBuf[1][pos + j], wtBuf_im); - } - aec->wfBuf[1][pos] = wt1; - } -#endif // UNCONSTR - } -} - -#ifdef _MSC_VER /* visual c++ */ -# define ALIGN16_BEG __declspec(align(16)) -# define ALIGN16_END -#else /* gcc or icc */ -# define ALIGN16_BEG -# define ALIGN16_END __attribute__((aligned(16))) -#endif - -static __m128 mm_pow_ps(__m128 a, __m128 b) -{ - // a^b = exp2(b * log2(a)) - // exp2(x) and log2(x) are calculated using polynomial approximations. - __m128 log2_a, b_log2_a, a_exp_b; - - // Calculate log2(x), x = a. - { - // To calculate log2(x), we decompose x like this: - // x = y * 2^n - // n is an integer - // y is in the [1.0, 2.0) range - // - // log2(x) = log2(y) + n - // n can be evaluated by playing with float representation. - // log2(y) in a small range can be approximated, this code uses an order - // five polynomial approximation. The coefficients have been - // estimated with the Remez algorithm and the resulting - // polynomial has a maximum relative error of 0.00086%. - - // Compute n. - // This is done by masking the exponent, shifting it into the top bit of - // the mantissa, putting eight into the biased exponent (to shift/ - // compensate the fact that the exponent has been shifted in the top/ - // fractional part and finally getting rid of the implicit leading one - // from the mantissa by substracting it out. - static const ALIGN16_BEG int float_exponent_mask[4] ALIGN16_END = - {0x7F800000, 0x7F800000, 0x7F800000, 0x7F800000}; - static const ALIGN16_BEG int eight_biased_exponent[4] ALIGN16_END = - {0x43800000, 0x43800000, 0x43800000, 0x43800000}; - static const ALIGN16_BEG int implicit_leading_one[4] ALIGN16_END = - {0x43BF8000, 0x43BF8000, 0x43BF8000, 0x43BF8000}; - static const int shift_exponent_into_top_mantissa = 8; - const __m128 two_n = _mm_and_ps(a, *((__m128 *)float_exponent_mask)); - const __m128 n_1 = (__m128)_mm_srli_epi32((__m128i)two_n, - shift_exponent_into_top_mantissa); - const __m128 n_0 = _mm_or_ps( - (__m128)n_1, *((__m128 *)eight_biased_exponent)); - const __m128 n = _mm_sub_ps(n_0, *((__m128 *)implicit_leading_one)); - - // Compute y. - static const ALIGN16_BEG int mantissa_mask[4] ALIGN16_END = - {0x007FFFFF, 0x007FFFFF, 0x007FFFFF, 0x007FFFFF}; - static const ALIGN16_BEG int zero_biased_exponent_is_one[4] ALIGN16_END = - {0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000}; - const __m128 mantissa = _mm_and_ps(a, *((__m128 *)mantissa_mask)); - const __m128 y = _mm_or_ps( - mantissa, *((__m128 *)zero_biased_exponent_is_one)); - - // Approximate log2(y) ~= (y - 1) * pol5(y). - // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 - static const ALIGN16_BEG float ALIGN16_END C5[4] = - {-3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f, -3.4436006e-2f}; - static const ALIGN16_BEG float ALIGN16_END C4[4] = - {3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f, 3.1821337e-1f}; - static const ALIGN16_BEG float ALIGN16_END C3[4] = - {-1.2315303f, -1.2315303f, -1.2315303f, -1.2315303f}; - static const ALIGN16_BEG float ALIGN16_END C2[4] = - {2.5988452f, 2.5988452f, 2.5988452f, 2.5988452f}; - static const ALIGN16_BEG float ALIGN16_END C1[4] = - {-3.3241990f, -3.3241990f, -3.3241990f, -3.3241990f}; - static const ALIGN16_BEG float ALIGN16_END C0[4] = - {3.1157899f, 3.1157899f, 3.1157899f, 3.1157899f}; - const __m128 pol5_y_0 = _mm_mul_ps(y, *((__m128 *)C5)); - const __m128 pol5_y_1 = _mm_add_ps(pol5_y_0, *((__m128 *)C4)); - const __m128 pol5_y_2 = _mm_mul_ps(pol5_y_1, y); - const __m128 pol5_y_3 = _mm_add_ps(pol5_y_2, *((__m128 *)C3)); - const __m128 pol5_y_4 = _mm_mul_ps(pol5_y_3, y); - const __m128 pol5_y_5 = _mm_add_ps(pol5_y_4, *((__m128 *)C2)); - const __m128 pol5_y_6 = _mm_mul_ps(pol5_y_5, y); - const __m128 pol5_y_7 = _mm_add_ps(pol5_y_6, *((__m128 *)C1)); - const __m128 pol5_y_8 = _mm_mul_ps(pol5_y_7, y); - const __m128 pol5_y = _mm_add_ps(pol5_y_8, *((__m128 *)C0)); - const __m128 y_minus_one = _mm_sub_ps( - y, *((__m128 *)zero_biased_exponent_is_one)); - const __m128 log2_y = _mm_mul_ps(y_minus_one , pol5_y); - - // Combine parts. - log2_a = _mm_add_ps(n, log2_y); - } - - // b * log2(a) - b_log2_a = _mm_mul_ps(b, log2_a); - - // Calculate exp2(x), x = b * log2(a). - { - // To calculate 2^x, we decompose x like this: - // x = n + y - // n is an integer, the value of x - 0.5 rounded down, therefore - // y is in the [0.5, 1.5) range - // - // 2^x = 2^n * 2^y - // 2^n can be evaluated by playing with float representation. - // 2^y in a small range can be approximated, this code uses an order two - // polynomial approximation. The coefficients have been estimated - // with the Remez algorithm and the resulting polynomial has a - // maximum relative error of 0.17%. - - // To avoid over/underflow, we reduce the range of input to ]-127, 129]. - static const ALIGN16_BEG float max_input[4] ALIGN16_END = - {129.f, 129.f, 129.f, 129.f}; - static const ALIGN16_BEG float min_input[4] ALIGN16_END = - {-126.99999f, -126.99999f, -126.99999f, -126.99999f}; - const __m128 x_min = _mm_min_ps(b_log2_a, *((__m128 *)max_input)); - const __m128 x_max = _mm_max_ps(x_min, *((__m128 *)min_input)); - // Compute n. - static const ALIGN16_BEG float half[4] ALIGN16_END = - {0.5f, 0.5f, 0.5f, 0.5f}; - const __m128 x_minus_half = _mm_sub_ps(x_max, *((__m128 *)half)); - const __m128i x_minus_half_floor = _mm_cvtps_epi32(x_minus_half); - // Compute 2^n. - static const ALIGN16_BEG int float_exponent_bias[4] ALIGN16_END = - {127, 127, 127, 127}; - static const int float_exponent_shift = 23; - const __m128i two_n_exponent = _mm_add_epi32( - x_minus_half_floor, *((__m128i *)float_exponent_bias)); - const __m128 two_n = (__m128)_mm_slli_epi32( - two_n_exponent, float_exponent_shift); - // Compute y. - const __m128 y = _mm_sub_ps(x_max, _mm_cvtepi32_ps(x_minus_half_floor)); - // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. - static const ALIGN16_BEG float C2[4] ALIGN16_END = - {3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f, 3.3718944e-1f}; - static const ALIGN16_BEG float C1[4] ALIGN16_END = - {6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f, 6.5763628e-1f}; - static const ALIGN16_BEG float C0[4] ALIGN16_END = - {1.0017247f, 1.0017247f, 1.0017247f, 1.0017247f}; - const __m128 exp2_y_0 = _mm_mul_ps(y, *((__m128 *)C2)); - const __m128 exp2_y_1 = _mm_add_ps(exp2_y_0, *((__m128 *)C1)); - const __m128 exp2_y_2 = _mm_mul_ps(exp2_y_1, y); - const __m128 exp2_y = _mm_add_ps(exp2_y_2, *((__m128 *)C0)); - - // Combine parts. - a_exp_b = _mm_mul_ps(exp2_y, two_n); - } - return a_exp_b; -} - -extern const float WebRtcAec_weightCurve[65]; -extern const float WebRtcAec_overDriveCurve[65]; - -static void OverdriveAndSuppressSSE2(aec_t *aec, float hNl[PART_LEN1], - const float hNlFb, - float efw[2][PART_LEN1]) { - int i; - const __m128 vec_hNlFb = _mm_set1_ps(hNlFb); - const __m128 vec_one = _mm_set1_ps(1.0f); - const __m128 vec_minus_one = _mm_set1_ps(-1.0f); - const __m128 vec_overDriveSm = _mm_set1_ps(aec->overDriveSm); - // vectorized code (four at once) - for (i = 0; i + 3 < PART_LEN1; i+=4) { - // Weight subbands - __m128 vec_hNl = _mm_loadu_ps(&hNl[i]); - const __m128 vec_weightCurve = _mm_loadu_ps(&WebRtcAec_weightCurve[i]); - const __m128 bigger = _mm_cmpgt_ps(vec_hNl, vec_hNlFb); - const __m128 vec_weightCurve_hNlFb = _mm_mul_ps( - vec_weightCurve, vec_hNlFb); - const __m128 vec_one_weightCurve = _mm_sub_ps(vec_one, vec_weightCurve); - const __m128 vec_one_weightCurve_hNl = _mm_mul_ps( - vec_one_weightCurve, vec_hNl); - const __m128 vec_if0 = _mm_andnot_ps(bigger, vec_hNl); - const __m128 vec_if1 = _mm_and_ps( - bigger, _mm_add_ps(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl)); - vec_hNl = _mm_or_ps(vec_if0, vec_if1); - - { - const __m128 vec_overDriveCurve = _mm_loadu_ps( - &WebRtcAec_overDriveCurve[i]); - const __m128 vec_overDriveSm_overDriveCurve = _mm_mul_ps( - vec_overDriveSm, vec_overDriveCurve); - vec_hNl = mm_pow_ps(vec_hNl, vec_overDriveSm_overDriveCurve); - _mm_storeu_ps(&hNl[i], vec_hNl); - } - - // Suppress error signal - { - __m128 vec_efw_re = _mm_loadu_ps(&efw[0][i]); - __m128 vec_efw_im = _mm_loadu_ps(&efw[1][i]); - vec_efw_re = _mm_mul_ps(vec_efw_re, vec_hNl); - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_hNl); - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - vec_efw_im = _mm_mul_ps(vec_efw_im, vec_minus_one); - _mm_storeu_ps(&efw[0][i], vec_efw_re); - _mm_storeu_ps(&efw[1][i], vec_efw_im); - } - } - // scalar code for the remaining items. - for (; i < PART_LEN1; i++) { - // Weight subbands - if (hNl[i] > hNlFb) { - hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + - (1 - WebRtcAec_weightCurve[i]) * hNl[i]; - } - hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); - - // Suppress error signal - efw[0][i] *= hNl[i]; - efw[1][i] *= hNl[i]; - - // Ooura fft returns incorrect sign on imaginary component. It matters - // here because we are making an additive change with comfort noise. - efw[1][i] *= -1; - } -} - -void WebRtcAec_InitAec_SSE2(void) { - WebRtcAec_FilterFar = FilterFarSSE2; - WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2; - WebRtcAec_FilterAdaptation = FilterAdaptationSSE2; - WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2; -} - -#endif //__SSE2__ diff --git a/modules/audio_processing/aec/main/source/aec_rdft.c b/modules/audio_processing/aec/main/source/aec_rdft.c deleted file mode 100644 index 072a1c45c..000000000 --- a/modules/audio_processing/aec/main/source/aec_rdft.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html - * Copyright Takuya OOURA, 1996-2001 - * - * You may use, copy, modify and distribute this code for any purpose (include - * commercial use) and without fee. Please refer to this package when you modify - * this code. - * - * Changes by the WebRTC authors: - * - Trivial type modifications. - * - Minimal code subset to do rdft of length 128. - * - Optimizations because of known length. - * - * All changes are covered by the WebRTC license and IP grant: - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "aec_rdft.h" -#include "system_wrappers/interface/cpu_features_wrapper.h" - -float rdft_w[64]; -static int ip[16]; - -static void bitrv2_32or128(int n, int *ip, float *a) { - // n is 32 or 128 - int j, j1, k, k1, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - { - int l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - } - m2 = 2 * m; - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - j1 = 2 * k + m2 + ip[k]; - k1 = j1 + m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } -} - -static void makewt_32() { - const int nw = 32; - int j, nwh; - float delta, x, y; - - ip[0] = nw; - ip[1] = 1; - nwh = nw >> 1; - delta = atanf(1.0f) / nwh; - rdft_w[0] = 1; - rdft_w[1] = 0; - rdft_w[nwh] = cosf(delta * nwh); - rdft_w[nwh + 1] = rdft_w[nwh]; - for (j = 2; j < nwh; j += 2) { - x = cosf(delta * j); - y = sinf(delta * j); - rdft_w[j] = x; - rdft_w[j + 1] = y; - rdft_w[nw - j] = y; - rdft_w[nw - j + 1] = x; - } - bitrv2_32or128(nw, ip + 2, rdft_w); -} - -static void makect_32() { - float *c = rdft_w + 32; - const int nc = 32; - int j, nch; - float delta; - - ip[1] = nc; - nch = nc >> 1; - delta = atanf(1.0f) / nch; - c[0] = cosf(delta * nch); - c[nch] = 0.5f * c[0]; - for (j = 1; j < nch; j++) { - c[j] = 0.5f * cosf(delta * j); - c[nc - j] = 0.5f * sinf(delta * j); - } -} - -static void cft1st_128(float *a) { - const int n = 128; - int j, k1, k2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - x0r = a[0] + a[2]; - x0i = a[1] + a[3]; - x1r = a[0] - a[2]; - x1i = a[1] - a[3]; - x2r = a[4] + a[6]; - x2i = a[5] + a[7]; - x3r = a[4] - a[6]; - x3i = a[5] - a[7]; - a[0] = x0r + x2r; - a[1] = x0i + x2i; - a[4] = x0r - x2r; - a[5] = x0i - x2i; - a[2] = x1r - x3i; - a[3] = x1i + x3r; - a[6] = x1r + x3i; - a[7] = x1i - x3r; - wk1r = rdft_w[2]; - x0r = a[8] + a[10]; - x0i = a[9] + a[11]; - x1r = a[8] - a[10]; - x1i = a[9] - a[11]; - x2r = a[12] + a[14]; - x2i = a[13] + a[15]; - x3r = a[12] - a[14]; - x3i = a[13] - a[15]; - a[8] = x0r + x2r; - a[9] = x0i + x2i; - a[12] = x2i - x0i; - a[13] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[10] = wk1r * (x0r - x0i); - a[11] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[14] = wk1r * (x0i - x0r); - a[15] = wk1r * (x0i + x0r); - k1 = 0; - for (j = 16; j < n; j += 16) { - k1 += 2; - k2 = 2 * k1; - wk2r = rdft_w[k1]; - wk2i = rdft_w[k1 + 1]; - wk1r = rdft_w[k2]; - wk1i = rdft_w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - x0r = a[j] + a[j + 2]; - x0i = a[j + 1] + a[j + 3]; - x1r = a[j] - a[j + 2]; - x1i = a[j + 1] - a[j + 3]; - x2r = a[j + 4] + a[j + 6]; - x2i = a[j + 5] + a[j + 7]; - x3r = a[j + 4] - a[j + 6]; - x3i = a[j + 5] - a[j + 7]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 4] = wk2r * x0r - wk2i * x0i; - a[j + 5] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 2] = wk1r * x0r - wk1i * x0i; - a[j + 3] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 6] = wk3r * x0r - wk3i * x0i; - a[j + 7] = wk3r * x0i + wk3i * x0r; - wk1r = rdft_w[k2 + 2]; - wk1i = rdft_w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - x0r = a[j + 8] + a[j + 10]; - x0i = a[j + 9] + a[j + 11]; - x1r = a[j + 8] - a[j + 10]; - x1i = a[j + 9] - a[j + 11]; - x2r = a[j + 12] + a[j + 14]; - x2i = a[j + 13] + a[j + 15]; - x3r = a[j + 12] - a[j + 14]; - x3i = a[j + 13] - a[j + 15]; - a[j + 8] = x0r + x2r; - a[j + 9] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 12] = -wk2i * x0r - wk2r * x0i; - a[j + 13] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 10] = wk1r * x0r - wk1i * x0i; - a[j + 11] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 14] = wk3r * x0r - wk3i * x0i; - a[j + 15] = wk3r * x0i + wk3i * x0r; - } -} - -static void cftmdl_128(int l, float *a) { - const int n = 128; - int j, j1, j2, j3, k, k1, k2, m, m2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - m = l << 2; - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } - wk1r = rdft_w[2]; - for (j = m; j < l + m; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x2i - x0i; - a[j2 + 1] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * (x0r - x0i); - a[j1 + 1] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[j3] = wk1r * (x0i - x0r); - a[j3 + 1] = wk1r * (x0i + x0r); - } - k1 = 0; - m2 = 2 * m; - for (k = m2; k < n; k += m2) { - k1 += 2; - k2 = 2 * k1; - wk2r = rdft_w[k1]; - wk2i = rdft_w[k1 + 1]; - wk1r = rdft_w[k2]; - wk1i = rdft_w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - for (j = k; j < l + k; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = wk2r * x0r - wk2i * x0i; - a[j2 + 1] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - wk1r = rdft_w[k2 + 2]; - wk1i = rdft_w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - for (j = k + m; j < l + (k + m); j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = -wk2i * x0r - wk2r * x0i; - a[j2 + 1] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - } -} - -static void cftfsub_128(float *a) { - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - cft1st_128(a); - cftmdl_128(8, a); - l = 32; - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } -} - -static void cftbsub_128(float *a) { - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - cft1st_128(a); - cftmdl_128(8, a); - l = 32; - - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = -a[j + 1] - a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = -a[j + 1] + a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i - x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i + x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i - x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i + x3r; - } -} - -static void rftfsub_128_C(float *a) { - const float *c = rdft_w + 32; - int j1, j2, k1, k2; - float wkr, wki, xr, xi, yr, yi; - - for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) { - k2 = 128 - j2; - k1 = 32 - j1; - wkr = 0.5f - c[k1]; - wki = c[j1]; - xr = a[j2 + 0] - a[k2 + 0]; - xi = a[j2 + 1] + a[k2 + 1]; - yr = wkr * xr - wki * xi; - yi = wkr * xi + wki * xr; - a[j2 + 0] -= yr; - a[j2 + 1] -= yi; - a[k2 + 0] += yr; - a[k2 + 1] -= yi; - } -} - -static void rftbsub_128_C(float *a) { - const float *c = rdft_w + 32; - int j1, j2, k1, k2; - float wkr, wki, xr, xi, yr, yi; - - a[1] = -a[1]; - for (j1 = 1, j2 = 2; j2 < 64; j1 += 1, j2 += 2) { - k2 = 128 - j2; - k1 = 32 - j1; - wkr = 0.5f - c[k1]; - wki = c[j1]; - xr = a[j2 + 0] - a[k2 + 0]; - xi = a[j2 + 1] + a[k2 + 1]; - yr = wkr * xr + wki * xi; - yi = wkr * xi - wki * xr; - a[j2 + 0] = a[j2 + 0] - yr; - a[j2 + 1] = yi - a[j2 + 1]; - a[k2 + 0] = yr + a[k2 + 0]; - a[k2 + 1] = yi - a[k2 + 1]; - } - a[65] = -a[65]; -} - -void aec_rdft_forward_128(float *a) { - const int n = 128; - int nw; - float xi; - - nw = ip[0]; - bitrv2_32or128(n, ip + 2, a); - cftfsub_128(a); - rftfsub_128(a); - xi = a[0] - a[1]; - a[0] += a[1]; - a[1] = xi; -} - -void aec_rdft_inverse_128(float *a) { - const int n = 128; - int nw; - float xi; - - nw = ip[0]; - a[1] = 0.5f * (a[0] - a[1]); - a[0] -= a[1]; - rftbsub_128(a); - bitrv2_32or128(n, ip + 2, a); - cftbsub_128(a); -} - -// code path selection -rft_sub_128_t rftfsub_128; -rft_sub_128_t rftbsub_128; - -void aec_rdft_init(void) { - rftfsub_128 = rftfsub_128_C; - rftbsub_128 = rftbsub_128_C; - if (WebRtc_GetCPUInfo(kSSE2)) { -#if defined(__SSE2__) - aec_rdft_init_sse2(); -#endif - } - // init library constants. - makewt_32(); - makect_32(); -} diff --git a/modules/audio_processing/aec/main/source/aec_rdft.h b/modules/audio_processing/aec/main/source/aec_rdft.h deleted file mode 100644 index cf908822a..000000000 --- a/modules/audio_processing/aec/main/source/aec_rdft.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// constants shared by all paths (C, SSE2). -extern float rdft_w[64]; - -// code path selection function pointers -typedef void (*rft_sub_128_t)(float *a); -extern rft_sub_128_t rftfsub_128; -extern rft_sub_128_t rftbsub_128; - -// entry points -void aec_rdft_init(void); -void aec_rdft_init_sse2(void); -void aec_rdft_forward_128(float *a); -void aec_rdft_inverse_128(float *a); diff --git a/modules/audio_processing/aec/main/source/aec_rdft_sse2.c b/modules/audio_processing/aec/main/source/aec_rdft_sse2.c deleted file mode 100644 index 901a1b146..000000000 --- a/modules/audio_processing/aec/main/source/aec_rdft_sse2.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "aec_rdft.h" - -#ifdef _MSC_VER /* visual c++ */ -# define ALIGN16_BEG __declspec(align(16)) -# define ALIGN16_END -#else /* gcc or icc */ -# define ALIGN16_BEG -# define ALIGN16_END __attribute__((aligned(16))) -#endif - -static void rftfsub_128_SSE2(float *a) { - const float *c = rdft_w + 32; - int j1, j2, k1, k2; - float wkr, wki, xr, xi, yr, yi; - - static const ALIGN16_BEG float ALIGN16_END k_half[4] = - {0.5f, 0.5f, 0.5f, 0.5f}; - const __m128 mm_half = _mm_load_ps(k_half); - - // Vectorized code (four at once). - // Note: commented number are indexes for the first iteration of the loop. - for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { - // Load 'wk'. - const __m128 c_j1 = _mm_loadu_ps(&c[ j1]); // 1, 2, 3, 4, - const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31, - const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31, - const __m128 wkr_ = - _mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28, - const __m128 wki_ = c_j1; // 1, 2, 3, 4, - // Load and shuffle 'a'. - const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5, - const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9, - const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123, - const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127, - const __m128 a_j2_p0 = _mm_shuffle_ps(a_j2_0, a_j2_4, - _MM_SHUFFLE(2, 0, 2 ,0)); // 2, 4, 6, 8, - const __m128 a_j2_p1 = _mm_shuffle_ps(a_j2_0, a_j2_4, - _MM_SHUFFLE(3, 1, 3 ,1)); // 3, 5, 7, 9, - const __m128 a_k2_p0 = _mm_shuffle_ps(a_k2_4, a_k2_0, - _MM_SHUFFLE(0, 2, 0 ,2)); // 126, 124, 122, 120, - const __m128 a_k2_p1 = _mm_shuffle_ps(a_k2_4, a_k2_0, - _MM_SHUFFLE(1, 3, 1 ,3)); // 127, 125, 123, 121, - // Calculate 'x'. - const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0); - // 2-126, 4-124, 6-122, 8-120, - const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1); - // 3-127, 5-125, 7-123, 9-121, - // Calculate product into 'y'. - // yr = wkr * xr - wki * xi; - // yi = wkr * xi + wki * xr; - const __m128 a_ = _mm_mul_ps(wkr_, xr_); - const __m128 b_ = _mm_mul_ps(wki_, xi_); - const __m128 c_ = _mm_mul_ps(wkr_, xi_); - const __m128 d_ = _mm_mul_ps(wki_, xr_); - const __m128 yr_ = _mm_sub_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120, - const __m128 yi_ = _mm_add_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121, - // Update 'a'. - // a[j2 + 0] -= yr; - // a[j2 + 1] -= yi; - // a[k2 + 0] += yr; - // a[k2 + 1] -= yi; - const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8, - const __m128 a_j2_p1n = _mm_sub_ps(a_j2_p1, yi_); // 3, 5, 7, 9, - const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120, - const __m128 a_k2_p1n = _mm_sub_ps(a_k2_p1, yi_); // 127, 125, 123, 121, - // Shuffle in right order and store. - const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n); - // 2, 3, 4, 5, - const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n); - // 6, 7, 8, 9, - const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n); - // 122, 123, 120, 121, - const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n); - // 126, 127, 124, 125, - const __m128 a_k2_0n = _mm_shuffle_ps(a_k2_0nt, a_k2_0nt, - _MM_SHUFFLE(1, 0, 3 ,2)); // 120, 121, 122, 123, - const __m128 a_k2_4n = _mm_shuffle_ps(a_k2_4nt, a_k2_4nt, - _MM_SHUFFLE(1, 0, 3 ,2)); // 124, 125, 126, 127, - _mm_storeu_ps(&a[0 + j2], a_j2_0n); - _mm_storeu_ps(&a[4 + j2], a_j2_4n); - _mm_storeu_ps(&a[122 - j2], a_k2_0n); - _mm_storeu_ps(&a[126 - j2], a_k2_4n); - } - // Scalar code for the remaining items. - for (; j2 < 64; j1 += 1, j2 += 2) { - k2 = 128 - j2; - k1 = 32 - j1; - wkr = 0.5f - c[k1]; - wki = c[j1]; - xr = a[j2 + 0] - a[k2 + 0]; - xi = a[j2 + 1] + a[k2 + 1]; - yr = wkr * xr - wki * xi; - yi = wkr * xi + wki * xr; - a[j2 + 0] -= yr; - a[j2 + 1] -= yi; - a[k2 + 0] += yr; - a[k2 + 1] -= yi; - } -} - -static void rftbsub_128_SSE2(float *a) { - const float *c = rdft_w + 32; - int j1, j2, k1, k2; - float wkr, wki, xr, xi, yr, yi; - - static const ALIGN16_BEG float ALIGN16_END k_half[4] = - {0.5f, 0.5f, 0.5f, 0.5f}; - const __m128 mm_half = _mm_load_ps(k_half); - - a[1] = -a[1]; - // Vectorized code (four at once). - // Note: commented number are indexes for the first iteration of the loop. - for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { - // Load 'wk'. - const __m128 c_j1 = _mm_loadu_ps(&c[ j1]); // 1, 2, 3, 4, - const __m128 c_k1 = _mm_loadu_ps(&c[29 - j1]); // 28, 29, 30, 31, - const __m128 wkrt = _mm_sub_ps(mm_half, c_k1); // 28, 29, 30, 31, - const __m128 wkr_ = - _mm_shuffle_ps(wkrt, wkrt, _MM_SHUFFLE(0, 1, 2, 3)); // 31, 30, 29, 28, - const __m128 wki_ = c_j1; // 1, 2, 3, 4, - // Load and shuffle 'a'. - const __m128 a_j2_0 = _mm_loadu_ps(&a[0 + j2]); // 2, 3, 4, 5, - const __m128 a_j2_4 = _mm_loadu_ps(&a[4 + j2]); // 6, 7, 8, 9, - const __m128 a_k2_0 = _mm_loadu_ps(&a[122 - j2]); // 120, 121, 122, 123, - const __m128 a_k2_4 = _mm_loadu_ps(&a[126 - j2]); // 124, 125, 126, 127, - const __m128 a_j2_p0 = _mm_shuffle_ps(a_j2_0, a_j2_4, - _MM_SHUFFLE(2, 0, 2 ,0)); // 2, 4, 6, 8, - const __m128 a_j2_p1 = _mm_shuffle_ps(a_j2_0, a_j2_4, - _MM_SHUFFLE(3, 1, 3 ,1)); // 3, 5, 7, 9, - const __m128 a_k2_p0 = _mm_shuffle_ps(a_k2_4, a_k2_0, - _MM_SHUFFLE(0, 2, 0 ,2)); // 126, 124, 122, 120, - const __m128 a_k2_p1 = _mm_shuffle_ps(a_k2_4, a_k2_0, - _MM_SHUFFLE(1, 3, 1 ,3)); // 127, 125, 123, 121, - // Calculate 'x'. - const __m128 xr_ = _mm_sub_ps(a_j2_p0, a_k2_p0); - // 2-126, 4-124, 6-122, 8-120, - const __m128 xi_ = _mm_add_ps(a_j2_p1, a_k2_p1); - // 3-127, 5-125, 7-123, 9-121, - // Calculate product into 'y'. - // yr = wkr * xr + wki * xi; - // yi = wkr * xi - wki * xr; - const __m128 a_ = _mm_mul_ps(wkr_, xr_); - const __m128 b_ = _mm_mul_ps(wki_, xi_); - const __m128 c_ = _mm_mul_ps(wkr_, xi_); - const __m128 d_ = _mm_mul_ps(wki_, xr_); - const __m128 yr_ = _mm_add_ps(a_, b_); // 2-126, 4-124, 6-122, 8-120, - const __m128 yi_ = _mm_sub_ps(c_, d_); // 3-127, 5-125, 7-123, 9-121, - // Update 'a'. - // a[j2 + 0] = a[j2 + 0] - yr; - // a[j2 + 1] = yi - a[j2 + 1]; - // a[k2 + 0] = yr + a[k2 + 0]; - // a[k2 + 1] = yi - a[k2 + 1]; - const __m128 a_j2_p0n = _mm_sub_ps(a_j2_p0, yr_); // 2, 4, 6, 8, - const __m128 a_j2_p1n = _mm_sub_ps(yi_, a_j2_p1); // 3, 5, 7, 9, - const __m128 a_k2_p0n = _mm_add_ps(a_k2_p0, yr_); // 126, 124, 122, 120, - const __m128 a_k2_p1n = _mm_sub_ps(yi_, a_k2_p1); // 127, 125, 123, 121, - // Shuffle in right order and store. - // Shuffle in right order and store. - const __m128 a_j2_0n = _mm_unpacklo_ps(a_j2_p0n, a_j2_p1n); - // 2, 3, 4, 5, - const __m128 a_j2_4n = _mm_unpackhi_ps(a_j2_p0n, a_j2_p1n); - // 6, 7, 8, 9, - const __m128 a_k2_0nt = _mm_unpackhi_ps(a_k2_p0n, a_k2_p1n); - // 122, 123, 120, 121, - const __m128 a_k2_4nt = _mm_unpacklo_ps(a_k2_p0n, a_k2_p1n); - // 126, 127, 124, 125, - const __m128 a_k2_0n = _mm_shuffle_ps(a_k2_0nt, a_k2_0nt, - _MM_SHUFFLE(1, 0, 3 ,2)); // 120, 121, 122, 123, - const __m128 a_k2_4n = _mm_shuffle_ps(a_k2_4nt, a_k2_4nt, - _MM_SHUFFLE(1, 0, 3 ,2)); // 124, 125, 126, 127, - _mm_storeu_ps(&a[0 + j2], a_j2_0n); - _mm_storeu_ps(&a[4 + j2], a_j2_4n); - _mm_storeu_ps(&a[122 - j2], a_k2_0n); - _mm_storeu_ps(&a[126 - j2], a_k2_4n); - } - // Scalar code for the remaining items. - for (; j2 < 64; j1 += 1, j2 += 2) { - k2 = 128 - j2; - k1 = 32 - j1; - wkr = 0.5f - c[k1]; - wki = c[j1]; - xr = a[j2 + 0] - a[k2 + 0]; - xi = a[j2 + 1] + a[k2 + 1]; - yr = wkr * xr + wki * xi; - yi = wkr * xi - wki * xr; - a[j2 + 0] = a[j2 + 0] - yr; - a[j2 + 1] = yi - a[j2 + 1]; - a[k2 + 0] = yr + a[k2 + 0]; - a[k2 + 1] = yi - a[k2 + 1]; - } - a[65] = -a[65]; -} - -void aec_rdft_init_sse2(void) { - rftfsub_128 = rftfsub_128_SSE2; - rftbsub_128 = rftbsub_128_SSE2; -} diff --git a/modules/audio_processing/aec/main/source/echo_cancellation.c b/modules/audio_processing/aec/main/source/echo_cancellation.c deleted file mode 100644 index 1313e358f..000000000 --- a/modules/audio_processing/aec/main/source/echo_cancellation.c +++ /dev/null @@ -1,821 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Contains the API functions for the AEC. - */ -#include -#include - -#include "echo_cancellation.h" -#include "aec_core.h" -#include "ring_buffer.h" -#include "resampler.h" -#ifdef AEC_DEBUG - #include -#endif - -#define BUF_SIZE_FRAMES 50 // buffer size (frames) -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -static const int bufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) -static const int sampMsNb = 8; // samples per ms in nb -// Target suppression levels for nlp modes -// log{0.001, 0.00001, 0.00000001} -static const float targetSupp[3] = {-6.9f, -11.5f, -18.4f}; -static const float minOverDrive[3] = {1.0f, 2.0f, 5.0f}; -static const int initCheck = 42; - -typedef struct { - int delayCtr; - int sampFreq; - int splitSampFreq; - int scSampFreq; - float sampFactor; // scSampRate / sampFreq - short nlpMode; - short autoOnOff; - short activity; - short skewMode; - short bufSizeStart; - //short bufResetCtr; // counts number of noncausal frames - int knownDelay; - - // Stores the last frame added to the farend buffer - short farendOld[2][FRAME_LEN]; - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - short sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; - int timeForDelayChange; - int ECstartup; - int checkBuffSize; - int delayChange; - short lastDelayDiff; - -#ifdef AEC_DEBUG - FILE *bufFile; - FILE *delayFile; - FILE *skewFile; - FILE *preCompFile; - FILE *postCompFile; -#endif // AEC_DEBUG - - // Structures - void *farendBuf; - void *resampler; - - int skewFrCtr; - int resample; // if the skew is small enough we don't resample - int highSkewCtr; - float skew; - - int lastError; - - aec_t *aec; -} aecpc_t; - -// Estimates delay to set the position of the farend buffer read pointer -// (controlled by knownDelay) -static int EstBufDelay(aecpc_t *aecInst, short msInSndCardBuf); - -// Stuffs the farend buffer if the estimated delay is too large -static int DelayComp(aecpc_t *aecInst); - -WebRtc_Word32 WebRtcAec_Create(void **aecInst) -{ - aecpc_t *aecpc; - if (aecInst == NULL) { - return -1; - } - - aecpc = malloc(sizeof(aecpc_t)); - *aecInst = aecpc; - if (aecpc == NULL) { - return -1; - } - - if (WebRtcAec_CreateAec(&aecpc->aec) == -1) { - WebRtcAec_Free(aecpc); - aecpc = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecpc->farendBuf, bufSizeSamp) == -1) { - WebRtcAec_Free(aecpc); - aecpc = NULL; - return -1; - } - - if (WebRtcAec_CreateResampler(&aecpc->resampler) == -1) { - WebRtcAec_Free(aecpc); - aecpc = NULL; - return -1; - } - - aecpc->initFlag = 0; - aecpc->lastError = 0; - -#ifdef AEC_DEBUG - aecpc->aec->farFile = fopen("aecFar.pcm","wb"); - aecpc->aec->nearFile = fopen("aecNear.pcm","wb"); - aecpc->aec->outFile = fopen("aecOut.pcm","wb"); - aecpc->aec->outLpFile = fopen("aecOutLp.pcm","wb"); - - aecpc->bufFile = fopen("aecBuf.dat", "wb"); - aecpc->skewFile = fopen("aecSkew.dat", "wb"); - aecpc->delayFile = fopen("aecDelay.dat", "wb"); - aecpc->preCompFile = fopen("preComp.pcm", "wb"); - aecpc->postCompFile = fopen("postComp.pcm", "wb"); -#endif // AEC_DEBUG - - return 0; -} - -WebRtc_Word32 WebRtcAec_Free(void *aecInst) -{ - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - -#ifdef AEC_DEBUG - fclose(aecpc->aec->farFile); - fclose(aecpc->aec->nearFile); - fclose(aecpc->aec->outFile); - fclose(aecpc->aec->outLpFile); - - fclose(aecpc->bufFile); - fclose(aecpc->skewFile); - fclose(aecpc->delayFile); - fclose(aecpc->preCompFile); - fclose(aecpc->postCompFile); -#endif // AEC_DEBUG - - WebRtcAec_FreeAec(aecpc->aec); - WebRtcApm_FreeBuffer(aecpc->farendBuf); - WebRtcAec_FreeResampler(aecpc->resampler); - free(aecpc); - - return 0; -} - -WebRtc_Word32 WebRtcAec_Init(void *aecInst, WebRtc_Word32 sampFreq, WebRtc_Word32 scSampFreq) -{ - aecpc_t *aecpc = aecInst; - AecConfig aecConfig; - - if (aecpc == NULL) { - return -1; - } - - if (sampFreq != 8000 && sampFreq != 16000 && sampFreq != 32000) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->sampFreq = sampFreq; - - if (scSampFreq < 1 || scSampFreq > 96000) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->scSampFreq = scSampFreq; - - // Initialize echo canceller core - if (WebRtcAec_InitAec(aecpc->aec, aecpc->sampFreq) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - // Initialize farend buffer - if (WebRtcApm_InitBuffer(aecpc->farendBuf) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - if (WebRtcAec_InitResampler(aecpc->resampler, aecpc->scSampFreq) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - aecpc->initFlag = initCheck; // indicates that initilisation has been done - - if (aecpc->sampFreq == 32000) { - aecpc->splitSampFreq = 16000; - } - else { - aecpc->splitSampFreq = sampFreq; - } - - aecpc->skewFrCtr = 0; - aecpc->activity = 0; - - aecpc->delayChange = 1; - aecpc->delayCtr = 0; - - aecpc->sum = 0; - aecpc->counter = 0; - aecpc->checkBuffSize = 1; - aecpc->firstVal = 0; - - aecpc->ECstartup = 1; - aecpc->bufSizeStart = 0; - aecpc->checkBufSizeCtr = 0; - aecpc->filtDelay = 0; - aecpc->timeForDelayChange =0; - aecpc->knownDelay = 0; - aecpc->lastDelayDiff = 0; - - aecpc->skew = 0; - aecpc->resample = kAecFalse; - aecpc->highSkewCtr = 0; - aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq; - - memset(&aecpc->farendOld[0][0], 0, 160); - - // Default settings. - aecConfig.nlpMode = kAecNlpModerate; - aecConfig.skewMode = kAecFalse; - aecConfig.metricsMode = kAecFalse; - - if (WebRtcAec_set_config(aecpc, aecConfig) == -1) { - aecpc->lastError = AEC_UNSPECIFIED_ERROR; - return -1; - } - - return 0; -} - -// only buffer L band for farend -WebRtc_Word32 WebRtcAec_BufferFarend(void *aecInst, const WebRtc_Word16 *farend, - WebRtc_Word16 nrOfSamples) -{ - aecpc_t *aecpc = aecInst; - WebRtc_Word32 retVal = 0; - short newNrOfSamples; - short newFarend[MAX_RESAMP_LEN]; - float skew; - - if (aecpc == NULL) { - return -1; - } - - if (farend == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - skew = aecpc->skew; - - // TODO: Is this really a good idea? - if (!aecpc->ECstartup) { - DelayComp(aecpc); - } - - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - // Resample and get a new number of samples - newNrOfSamples = WebRtcAec_ResampleLinear(aecpc->resampler, - farend, - nrOfSamples, - skew, - newFarend); - WebRtcApm_WriteBuffer(aecpc->farendBuf, newFarend, newNrOfSamples); - -#ifdef AEC_DEBUG - fwrite(farend, 2, nrOfSamples, aecpc->preCompFile); - fwrite(newFarend, 2, newNrOfSamples, aecpc->postCompFile); -#endif - } - else { - WebRtcApm_WriteBuffer(aecpc->farendBuf, farend, nrOfSamples); - } - - return retVal; -} - -WebRtc_Word32 WebRtcAec_Process(void *aecInst, const WebRtc_Word16 *nearend, - const WebRtc_Word16 *nearendH, WebRtc_Word16 *out, WebRtc_Word16 *outH, - WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf, WebRtc_Word32 skew) -{ - aecpc_t *aecpc = aecInst; - WebRtc_Word32 retVal = 0; - short i; - short farend[FRAME_LEN]; - short nmbrOfFilledBuffers; - short nBlocks10ms; - short nFrames; -#ifdef AEC_DEBUG - short msInAECBuf; -#endif - // Limit resampling to doubling/halving of signal - const float minSkewEst = -0.5f; - const float maxSkewEst = 1.0f; - - if (aecpc == NULL) { - return -1; - } - - if (nearend == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (out == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - // number of samples == 160 for SWB input - if (nrOfSamples != 80 && nrOfSamples != 160) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - - // Check for valid pointers based on sampling rate - if (aecpc->sampFreq == 32000 && nearendH == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (msInSndCardBuf < 0) { - msInSndCardBuf = 0; - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - retVal = -1; - } - else if (msInSndCardBuf > 500) { - msInSndCardBuf = 500; - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - retVal = -1; - } - msInSndCardBuf += 10; - aecpc->msInSndCardBuf = msInSndCardBuf; - - if (aecpc->skewMode == kAecTrue) { - if (aecpc->skewFrCtr < 25) { - aecpc->skewFrCtr++; - } - else { - retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew); - if (retVal == -1) { - aecpc->skew = 0; - aecpc->lastError = AEC_BAD_PARAMETER_WARNING; - } - - aecpc->skew /= aecpc->sampFactor*nrOfSamples; - - if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) { - aecpc->resample = kAecFalse; - } - else { - aecpc->resample = kAecTrue; - } - - if (aecpc->skew < minSkewEst) { - aecpc->skew = minSkewEst; - } - else if (aecpc->skew > maxSkewEst) { - aecpc->skew = maxSkewEst; - } - -#ifdef AEC_DEBUG - fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile); -#endif - } - } - - nFrames = nrOfSamples / FRAME_LEN; - nBlocks10ms = nFrames / aecpc->aec->mult; - - if (aecpc->ECstartup) { - memcpy(out, nearend, sizeof(short) * nrOfSamples); - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN; - - // The AEC is in the start up mode - // AEC is disabled until the soundcard buffer and farend buffers are OK - - // Mechanism to ensure that the soundcard buffer is reasonably stable. - if (aecpc->checkBuffSize) { - - aecpc->checkBufSizeCtr++; - // Before we fill up the far end buffer we require the amount of data on the - // sound card to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 4 consecutive frames. If it seems - // to be stable then we start to fill up the far end buffer. - - if (aecpc->counter == 0) { - aecpc->firstVal = aecpc->msInSndCardBuf; - aecpc->sum = 0; - } - - if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) < - WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) { - aecpc->sum += aecpc->msInSndCardBuf; - aecpc->counter++; - } - else { - aecpc->counter = 0; - } - - if (aecpc->counter*nBlocks10ms >= 6) { - // The farend buffer size is determined in blocks of 80 samples - // Use 75% of the average value of the soundcard buffer - aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->sum * - aecpc->aec->mult) / (aecpc->counter * 10)), BUF_SIZE_FRAMES); - // buffersize has now been determined - aecpc->checkBuffSize = 0; - } - - if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) { - // for really bad sound cards, don't disable echocanceller for more than 0.5 sec - aecpc->bufSizeStart = WEBRTC_SPL_MIN((int) (0.75 * (aecpc->msInSndCardBuf * - aecpc->aec->mult) / 10), BUF_SIZE_FRAMES); - aecpc->checkBuffSize = 0; - } - } - - // if checkBuffSize changed in the if-statement above - if (!aecpc->checkBuffSize) { - // soundcard buffer is now reasonably stable - // When the far end buffer is filled with approximately the same amount of - // data as the amount on the sound card we end the start up phase and start - // to cancel echoes. - - if (nmbrOfFilledBuffers == aecpc->bufSizeStart) { - aecpc->ECstartup = 0; // Enable the AEC - } - else if (nmbrOfFilledBuffers > aecpc->bufSizeStart) { - WebRtcApm_FlushBuffer(aecpc->farendBuf, WebRtcApm_get_buffer_size(aecpc->farendBuf) - - aecpc->bufSizeStart * FRAME_LEN); - aecpc->ECstartup = 0; - } - } - - } - else { - // AEC is enabled - - // Note only 1 block supported for nb and 2 blocks for wb - for (i = 0; i < nFrames; i++) { - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecpc->farendBuf) / FRAME_LEN; - - // Check that there is data in the far end buffer - if (nmbrOfFilledBuffers > 0) { - // Get the next 80 samples from the farend buffer - WebRtcApm_ReadBuffer(aecpc->farendBuf, farend, FRAME_LEN); - - // Always store the last frame for use when we run out of data - memcpy(&(aecpc->farendOld[i][0]), farend, FRAME_LEN * sizeof(short)); - } - else { - // We have no data so we use the last played frame - memcpy(farend, &(aecpc->farendOld[i][0]), FRAME_LEN * sizeof(short)); - } - - // Call buffer delay estimator when all data is extracted, - // i.e. i = 0 for NB and i = 1 for WB or SWB - if ((i == 0 && aecpc->splitSampFreq == 8000) || - (i == 1 && (aecpc->splitSampFreq == 16000))) { - EstBufDelay(aecpc, aecpc->msInSndCardBuf); - } - - // Call the AEC - WebRtcAec_ProcessFrame(aecpc->aec, farend, &nearend[FRAME_LEN * i], &nearendH[FRAME_LEN * i], - &out[FRAME_LEN * i], &outH[FRAME_LEN * i], aecpc->knownDelay); - } - } - -#ifdef AEC_DEBUG - msInAECBuf = WebRtcApm_get_buffer_size(aecpc->farendBuf) / (sampMsNb*aecpc->aec->mult); - fwrite(&msInAECBuf, 2, 1, aecpc->bufFile); - fwrite(&(aecpc->knownDelay), sizeof(aecpc->knownDelay), 1, aecpc->delayFile); -#endif - - return retVal; -} - -WebRtc_Word32 WebRtcAec_set_config(void *aecInst, AecConfig config) -{ - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - if (config.skewMode != kAecFalse && config.skewMode != kAecTrue) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->skewMode = config.skewMode; - - if (config.nlpMode != kAecNlpConservative && config.nlpMode != - kAecNlpModerate && config.nlpMode != kAecNlpAggressive) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->nlpMode = config.nlpMode; - aecpc->aec->targetSupp = targetSupp[aecpc->nlpMode]; - aecpc->aec->minOverDrive = minOverDrive[aecpc->nlpMode]; - - if (config.metricsMode != kAecFalse && config.metricsMode != kAecTrue) { - aecpc->lastError = AEC_BAD_PARAMETER_ERROR; - return -1; - } - aecpc->aec->metricsMode = config.metricsMode; - if (aecpc->aec->metricsMode == kAecTrue) { - WebRtcAec_InitMetrics(aecpc->aec); - } - - return 0; -} - -WebRtc_Word32 WebRtcAec_get_config(void *aecInst, AecConfig *config) -{ - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - - if (config == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - config->nlpMode = aecpc->nlpMode; - config->skewMode = aecpc->skewMode; - config->metricsMode = aecpc->aec->metricsMode; - - return 0; -} - -WebRtc_Word32 WebRtcAec_get_echo_status(void *aecInst, WebRtc_Word16 *status) -{ - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - - if (status == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - *status = aecpc->aec->echoState; - - return 0; -} - -WebRtc_Word32 WebRtcAec_GetMetrics(void *aecInst, AecMetrics *metrics) -{ - const float upweight = 0.7f; - float dtmp; - short stmp; - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - - if (metrics == NULL) { - aecpc->lastError = AEC_NULL_POINTER_ERROR; - return -1; - } - - if (aecpc->initFlag != initCheck) { - aecpc->lastError = AEC_UNINITIALIZED_ERROR; - return -1; - } - - // ERL - metrics->erl.instant = (short) aecpc->aec->erl.instant; - - if ((aecpc->aec->erl.himean > offsetLevel) && (aecpc->aec->erl.average > offsetLevel)) { - // Use a mix between regular average and upper part average - dtmp = upweight * aecpc->aec->erl.himean + (1 - upweight) * aecpc->aec->erl.average; - metrics->erl.average = (short) dtmp; - } - else { - metrics->erl.average = offsetLevel; - } - - metrics->erl.max = (short) aecpc->aec->erl.max; - - if (aecpc->aec->erl.min < (offsetLevel * (-1))) { - metrics->erl.min = (short) aecpc->aec->erl.min; - } - else { - metrics->erl.min = offsetLevel; - } - - // ERLE - metrics->erle.instant = (short) aecpc->aec->erle.instant; - - if ((aecpc->aec->erle.himean > offsetLevel) && (aecpc->aec->erle.average > offsetLevel)) { - // Use a mix between regular average and upper part average - dtmp = upweight * aecpc->aec->erle.himean + (1 - upweight) * aecpc->aec->erle.average; - metrics->erle.average = (short) dtmp; - } - else { - metrics->erle.average = offsetLevel; - } - - metrics->erle.max = (short) aecpc->aec->erle.max; - - if (aecpc->aec->erle.min < (offsetLevel * (-1))) { - metrics->erle.min = (short) aecpc->aec->erle.min; - } else { - metrics->erle.min = offsetLevel; - } - - // RERL - if ((metrics->erl.average > offsetLevel) && (metrics->erle.average > offsetLevel)) { - stmp = metrics->erl.average + metrics->erle.average; - } - else { - stmp = offsetLevel; - } - metrics->rerl.average = stmp; - - // No other statistics needed, but returned for completeness - metrics->rerl.instant = stmp; - metrics->rerl.max = stmp; - metrics->rerl.min = stmp; - - // A_NLP - metrics->aNlp.instant = (short) aecpc->aec->aNlp.instant; - - if ((aecpc->aec->aNlp.himean > offsetLevel) && (aecpc->aec->aNlp.average > offsetLevel)) { - // Use a mix between regular average and upper part average - dtmp = upweight * aecpc->aec->aNlp.himean + (1 - upweight) * aecpc->aec->aNlp.average; - metrics->aNlp.average = (short) dtmp; - } - else { - metrics->aNlp.average = offsetLevel; - } - - metrics->aNlp.max = (short) aecpc->aec->aNlp.max; - - if (aecpc->aec->aNlp.min < (offsetLevel * (-1))) { - metrics->aNlp.min = (short) aecpc->aec->aNlp.min; - } - else { - metrics->aNlp.min = offsetLevel; - } - - return 0; -} - -WebRtc_Word32 WebRtcAec_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len) -{ - const char version[] = "AEC 2.5.0"; - const short versionLen = (short)strlen(version) + 1; // +1 for null-termination - - if (versionStr == NULL) { - return -1; - } - - if (versionLen > len) { - return -1; - } - - strncpy(versionStr, version, versionLen); - return 0; -} - -WebRtc_Word32 WebRtcAec_get_error_code(void *aecInst) -{ - aecpc_t *aecpc = aecInst; - - if (aecpc == NULL) { - return -1; - } - - return aecpc->lastError; -} - -static int EstBufDelay(aecpc_t *aecpc, short msInSndCardBuf) -{ - short delayNew, nSampFar, nSampSndCard; - short diff; - - nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf); - nSampSndCard = msInSndCardBuf * sampMsNb * aecpc->aec->mult; - - delayNew = nSampSndCard - nSampFar; - - // Account for resampling frame delay - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - delayNew -= kResamplingDelay; - } - - if (delayNew < FRAME_LEN) { - WebRtcApm_FlushBuffer(aecpc->farendBuf, FRAME_LEN); - delayNew += FRAME_LEN; - } - - aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short)(0.8*aecpc->filtDelay + 0.2*delayNew)); - - diff = aecpc->filtDelay - aecpc->knownDelay; - if (diff > 224) { - if (aecpc->lastDelayDiff < 96) { - aecpc->timeForDelayChange = 0; - } - else { - aecpc->timeForDelayChange++; - } - } - else if (diff < 96 && aecpc->knownDelay > 0) { - if (aecpc->lastDelayDiff > 224) { - aecpc->timeForDelayChange = 0; - } - else { - aecpc->timeForDelayChange++; - } - } - else { - aecpc->timeForDelayChange = 0; - } - aecpc->lastDelayDiff = diff; - - if (aecpc->timeForDelayChange > 25) { - aecpc->knownDelay = WEBRTC_SPL_MAX((int)aecpc->filtDelay - 160, 0); - } - return 0; -} - -static int DelayComp(aecpc_t *aecpc) -{ - int nSampFar, nSampSndCard, delayNew, nSampAdd; - const int maxStuffSamp = 10 * FRAME_LEN; - - nSampFar = WebRtcApm_get_buffer_size(aecpc->farendBuf); - nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->aec->mult; - delayNew = nSampSndCard - nSampFar; - - // Account for resampling frame delay - if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) { - delayNew -= kResamplingDelay; - } - - if (delayNew > FAR_BUF_LEN - FRAME_LEN*aecpc->aec->mult) { - // The difference of the buffersizes is larger than the maximum - // allowed known delay. Compensate by stuffing the buffer. - nSampAdd = (int)(WEBRTC_SPL_MAX((int)(0.5 * nSampSndCard - nSampFar), - FRAME_LEN)); - nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); - - WebRtcApm_StuffBuffer(aecpc->farendBuf, nSampAdd); - aecpc->delayChange = 1; // the delay needs to be updated - } - - return 0; -} diff --git a/modules/audio_processing/aec/main/source/resampler.c b/modules/audio_processing/aec/main/source/resampler.c deleted file mode 100644 index 4caa6f4c8..000000000 --- a/modules/audio_processing/aec/main/source/resampler.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* Resamples a signal to an arbitrary rate. Used by the AEC to compensate for clock - * skew by resampling the farend signal. - */ - -#include -#include -#include -#include - -#include "resampler.h" -#include "aec_core.h" - -enum { kFrameBufferSize = FRAME_LEN * 4 }; -enum { kEstimateLengthFrames = 400 }; - -typedef struct { - short buffer[kFrameBufferSize]; - float position; - - int deviceSampleRateHz; - int skewData[kEstimateLengthFrames]; - int skewDataIndex; - float skewEstimate; -} resampler_t; - -static int EstimateSkew(const int* rawSkew, - int size, - int absLimit, - float *skewEst); - -int WebRtcAec_CreateResampler(void **resampInst) -{ - resampler_t *obj = malloc(sizeof(resampler_t)); - *resampInst = obj; - if (obj == NULL) { - return -1; - } - - return 0; -} - -int WebRtcAec_InitResampler(void *resampInst, int deviceSampleRateHz) -{ - resampler_t *obj = (resampler_t*) resampInst; - memset(obj->buffer, 0, sizeof(obj->buffer)); - obj->position = 0.0; - - obj->deviceSampleRateHz = deviceSampleRateHz; - memset(obj->skewData, 0, sizeof(obj->skewData)); - obj->skewDataIndex = 0; - obj->skewEstimate = 0.0; - - return 0; -} - -int WebRtcAec_FreeResampler(void *resampInst) -{ - resampler_t *obj = (resampler_t*) resampInst; - free(obj); - - return 0; -} - -int WebRtcAec_ResampleLinear(void *resampInst, - const short *inspeech, - int size, - float skew, - short *outspeech) -{ - resampler_t *obj = (resampler_t*) resampInst; - - short *y; - float be, tnew, interp; - int tn, outsize, mm; - - if (size < 0 || size > 2 * FRAME_LEN) { - return -1; - } - - // Add new frame data in lookahead - memcpy(&obj->buffer[FRAME_LEN + kResamplingDelay], - inspeech, - size * sizeof(short)); - - // Sample rate ratio - be = 1 + skew; - - // Loop over input frame - mm = 0; - y = &obj->buffer[FRAME_LEN]; // Point at current frame - - tnew = be * mm + obj->position; - tn = (int) tnew; - - while (tn < size) { - - // Interpolation - interp = y[tn] + (tnew - tn) * (y[tn+1] - y[tn]); - - if (interp > 32767) { - interp = 32767; - } - else if (interp < -32768) { - interp = -32768; - } - - outspeech[mm] = (short) interp; - mm++; - - tnew = be * mm + obj->position; - tn = (int) tnew; - } - - outsize = mm; - obj->position += outsize * be - size; - - // Shift buffer - memmove(obj->buffer, - &obj->buffer[size], - (kFrameBufferSize - size) * sizeof(short)); - - return outsize; -} - -int WebRtcAec_GetSkew(void *resampInst, int rawSkew, float *skewEst) -{ - resampler_t *obj = (resampler_t*)resampInst; - int err = 0; - - if (obj->skewDataIndex < kEstimateLengthFrames) { - obj->skewData[obj->skewDataIndex] = rawSkew; - obj->skewDataIndex++; - } - else if (obj->skewDataIndex == kEstimateLengthFrames) { - err = EstimateSkew(obj->skewData, - kEstimateLengthFrames, - obj->deviceSampleRateHz, - skewEst); - obj->skewEstimate = *skewEst; - obj->skewDataIndex++; - } - else { - *skewEst = obj->skewEstimate; - } - - return err; -} - -int EstimateSkew(const int* rawSkew, - const int size, - const int deviceSampleRateHz, - float *skewEst) -{ - const int absLimitOuter = (int)(0.04f * deviceSampleRateHz); - const int absLimitInner = (int)(0.0025f * deviceSampleRateHz); - int i = 0; - int n = 0; - float rawAvg = 0; - float err = 0; - float rawAbsDev = 0; - int upperLimit = 0; - int lowerLimit = 0; - float cumSum = 0; - float x = 0; - float x2 = 0; - float y = 0; - float xy = 0; - float xAvg = 0; - float yAvg = 0; - float denom = 0; - float skew = 0; - - *skewEst = 0; // Set in case of error below. - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - n++; - rawAvg += rawSkew[i]; - } - } - - if (n == 0) { - return -1; - } - assert(n > 0); - rawAvg /= n; - - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitOuter && rawSkew[i] > -absLimitOuter)) { - err = rawSkew[i] - rawAvg; - rawAbsDev += err >= 0 ? err : -err; - } - } - assert(n > 0); - rawAbsDev /= n; - upperLimit = (int)(rawAvg + 5 * rawAbsDev + 1); // +1 for ceiling. - lowerLimit = (int)(rawAvg - 5 * rawAbsDev - 1); // -1 for floor. - - n = 0; - for (i = 0; i < size; i++) { - if ((rawSkew[i] < absLimitInner && rawSkew[i] > -absLimitInner) || - (rawSkew[i] < upperLimit && rawSkew[i] > lowerLimit)) { - n++; - cumSum += rawSkew[i]; - x += n; - x2 += n*n; - y += cumSum; - xy += n * cumSum; - } - } - - if (n == 0) { - return -1; - } - assert(n > 0); - xAvg = x / n; - yAvg = y / n; - denom = x2 - xAvg*x; - - if (denom != 0) { - skew = (xy - xAvg*y) / denom; - } - - *skewEst = skew; - return 0; -} diff --git a/modules/audio_processing/aec/main/source/resampler.h b/modules/audio_processing/aec/main/source/resampler.h deleted file mode 100644 index 9cb283729..000000000 --- a/modules/audio_processing/aec/main/source/resampler.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ - -enum { kResamplingDelay = 1 }; - -// Unless otherwise specified, functions return 0 on success and -1 on error -int WebRtcAec_CreateResampler(void **resampInst); -int WebRtcAec_InitResampler(void *resampInst, int deviceSampleRateHz); -int WebRtcAec_FreeResampler(void *resampInst); - -// Estimates skew from raw measurement. -int WebRtcAec_GetSkew(void *resampInst, int rawSkew, float *skewEst); - -// Resamples input using linear interpolation. -// Returns size of resampled array. -int WebRtcAec_ResampleLinear(void *resampInst, - const short *inspeech, - int size, - float skew, - short *outspeech); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_MAIN_SOURCE_RESAMPLER_H_ diff --git a/modules/audio_processing/aecm/main/interface/echo_control_mobile.h b/modules/audio_processing/aecm/main/interface/echo_control_mobile.h deleted file mode 100644 index 26b117272..000000000 --- a/modules/audio_processing/aecm/main/interface/echo_control_mobile.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_ - -#include "typedefs.h" - -enum { - AecmFalse = 0, - AecmTrue -}; - -// Errors -#define AECM_UNSPECIFIED_ERROR 12000 -#define AECM_UNSUPPORTED_FUNCTION_ERROR 12001 -#define AECM_UNINITIALIZED_ERROR 12002 -#define AECM_NULL_POINTER_ERROR 12003 -#define AECM_BAD_PARAMETER_ERROR 12004 - -// Warnings -#define AECM_BAD_PARAMETER_WARNING 12100 - -typedef struct { - WebRtc_Word16 cngMode; // AECM_FALSE, AECM_TRUE (default) - WebRtc_Word16 echoMode; // 0, 1, 2, 3 (default), 4 -} AecmConfig; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Allocates the memory needed by the AECM. The memory needs to be - * initialized separately using the WebRtcAecm_Init() function. - * - * Inputs Description - * ------------------------------------------------------------------- - * void **aecmInst Pointer to the AECM instance to be - * created and initialized - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_Create(void **aecmInst); - -/* - * This function releases the memory allocated by WebRtcAecm_Create() - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_Free(void *aecmInst); - -/* - * Initializes an AECM instance. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * WebRtc_Word32 sampFreq Sampling frequency of data - * WebRtc_Word32 scSampFreq Soundcard sampling frequency - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_Init(void* aecmInst, - WebRtc_Word32 sampFreq, - WebRtc_Word32 scSampFreq); - -/* - * Inserts an 80 or 160 sample block of data into the farend buffer. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * WebRtc_Word16 *farend In buffer containing one frame of - * farend signal - * WebRtc_Word16 nrOfSamples Number of samples in farend buffer - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_BufferFarend(void* aecmInst, - const WebRtc_Word16* farend, - WebRtc_Word16 nrOfSamples); - -/* - * Runs the AECM on an 80 or 160 sample blocks of data. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * WebRtc_Word16 *nearendNoisy In buffer containing one frame of - * reference nearend+echo signal. If - * noise reduction is active, provide - * the noisy signal here. - * WebRtc_Word16 *nearendClean In buffer containing one frame of - * nearend+echo signal. If noise - * reduction is active, provide the - * clean signal here. Otherwise pass a - * NULL pointer. - * WebRtc_Word16 nrOfSamples Number of samples in nearend buffer - * WebRtc_Word16 msInSndCardBuf Delay estimate for sound card and - * system buffers - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word16 *out Out buffer, one frame of processed nearend - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_Process(void* aecmInst, - const WebRtc_Word16* nearendNoisy, - const WebRtc_Word16* nearendClean, - WebRtc_Word16* out, - WebRtc_Word16 nrOfSamples, - WebRtc_Word16 msInSndCardBuf); - -/* - * This function enables the user to set certain parameters on-the-fly - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * AecmConfig config Config instance that contains all - * properties to be set - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_set_config(void* aecmInst, - AecmConfig config); - -/* - * This function enables the user to set certain parameters on-the-fly - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * - * Outputs Description - * ------------------------------------------------------------------- - * AecmConfig *config Pointer to the config instance that - * all properties will be written to - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, - AecmConfig *config); - -/* - * Gets the last error code. - * - * Inputs Description - * ------------------------------------------------------------------- - * void *aecmInst Pointer to the AECM instance - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word32 return 11000-11100: error code - */ -WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst); - -/* - * Gets a version string - * - * Inputs Description - * ------------------------------------------------------------------- - * char *versionStr Pointer to a string array - * WebRtc_Word16 len The maximum length of the string - * - * Outputs Description - * ------------------------------------------------------------------- - * WebRtc_Word8 *versionStr Pointer to a string array - * WebRtc_Word32 return 0: OK - * -1: error - */ -WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, - WebRtc_Word16 len); - -#ifdef __cplusplus -} -#endif -#endif /* WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_INTERFACE_ECHO_CONTROL_MOBILE_H_ */ diff --git a/modules/audio_processing/aecm/main/matlab/compsup.m b/modules/audio_processing/aecm/main/matlab/compsup.m deleted file mode 100644 index 9575ec40f..000000000 --- a/modules/audio_processing/aecm/main/matlab/compsup.m +++ /dev/null @@ -1,447 +0,0 @@ -function [emicrophone,aaa]=compsup(microphone,TheFarEnd,avtime,samplingfreq); -% microphone = microphone signal -% aaa = nonlinearity input variable -% TheFarEnd = far end signal -% avtime = interval to compute suppression from (seconds) -% samplingfreq = sampling frequency - -%if(nargin==6) -% fprintf(1,'suppress has received a delay sequence\n'); -%end - - -Ap500=[ 1.00, -4.95, 9.801, -9.70299, 4.80298005, -0.9509900499]; -Bp500=[ 0.662743088639636, -2.5841655608125, 3.77668102146288, -2.45182477425154, 0.596566274575251, 0.0]; - - -Ap200=[ 1.00, -4.875, 9.50625, -9.26859375, 4.518439453125, -0.881095693359375]; -Bp200=[ 0.862545460994275, -3.2832804496114, 4.67892032308828, -2.95798023879133, 0.699796870041299, 0.0]; - -maxDelay=0.4; %[s] -histLen=1; %[s] - - -% CONSTANTS THAT YOU CAN EXPERIMENT WITH -A_GAIN=10.0; % for the suppress case -oversampling = 2; % must be power of 2; minimum is 2; 4 works -% fine for support=64, but for support=128, -% 8 gives better results. -support=64; %512 % fft support (frequency resolution; at low -% settings you can hear more distortion -% (e.g. pitch that is left-over from far-end)) -% 128 works well, 64 is ok) - -lowlevel = mean(abs(microphone))*0.0001; - -G_ol = 0; % Use overlapping sets of estimates - -% ECHO SUPPRESSION SPECIFIC PARAMETERS -suppress_overdrive=1.0; % overdrive factor for suppression 1.4 is good -gamma_echo=1.0; % same as suppress_overdrive but at different place -de_echo_bound=0.0; -mLim=10; % rank of matrix G -%limBW = 1; % use bandwidth-limited response for G -if mLim > (support/2+1) - error('mLim in suppress.m too large\n'); -end - - -dynrange=1.0000e-004; - -% other, constants -hsupport = support/2; -hsupport1 = hsupport+1; -factor = 2 / oversampling; -updatel = support/oversampling; -win=sqrt(designwindow(0,support)); -estLen = round(avtime * samplingfreq/updatel) - -runningfmean =0.0; - -mLim = floor(hsupport1/2); -V = sqrt(2/hsupport1)*cos(pi/hsupport1*(repmat((0:hsupport1-1) + 0.5, mLim, 1).* ... - repmat((0:mLim-1)' + 0.5, 1, hsupport1))); - -fprintf(1,'updatel is %5.3f s\n', updatel/samplingfreq); - - - -bandfirst=8; bandlast=25; -dosmooth=0; % to get rid of wavy bin counts (can be worse or better) - -% compute some constants -blockLen = support/oversampling; -maxDelayb = floor(samplingfreq*maxDelay/updatel); % in blocks -histLenb = floor(samplingfreq*histLen/updatel); % in blocks - -x0=TheFarEnd; -y0=microphone; - - -%input -tlength=min([length(microphone),length(TheFarEnd)]); -updateno=floor(tlength/updatel); -tlength=updatel*updateno; -updateno = updateno - oversampling + 1; - -TheFarEnd =TheFarEnd(1:tlength); -microphone =microphone(1:tlength); - -TheFarEnd =[zeros(hsupport,1);TheFarEnd(1:tlength)]; -microphone =[zeros(hsupport,1);microphone(1:tlength)]; - - -% signal length -n = min([floor(length(x0)/support)*support,floor(length(y0)/support)*support]); -nb = n/blockLen - oversampling + 1; % in blocks - -% initialize space -win = sqrt([0 ; hanning(support-1)]); -sxAll2 = zeros(hsupport1,nb); -syAll2 = zeros(hsupport1,nb); - -z500=zeros(5,maxDelayb+1); -z200=zeros(5,hsupport1); - -bxspectrum=uint32(zeros(nb,1)); -bxhist=uint32(zeros(maxDelayb+1,1)); -byspectrum=uint32(zeros(nb,1)); -bcount=zeros(1+maxDelayb,nb); -fcount=zeros(1+maxDelayb,nb); -fout=zeros(1+maxDelayb,nb); -delay=zeros(nb,1); -tdelay=zeros(nb,1); -nlgains=zeros(nb,1); - -% create space (mainly for debugging) -emicrophone=zeros(tlength,1); -femicrophone=complex(zeros(hsupport1,updateno)); -thefilter=zeros(hsupport1,updateno); -thelimiter=ones(hsupport1,updateno); -fTheFarEnd=complex(zeros(hsupport1,updateno)); -afTheFarEnd=zeros(hsupport1,updateno); -fmicrophone=complex(zeros(hsupport1,updateno)); -afmicrophone=zeros(hsupport1,updateno); - -G = zeros(hsupport1, hsupport1); -zerovec = zeros(hsupport1,1); -zeromat = zeros(hsupport1); - -% Reset sums -mmxs_a = zerovec; -mmys_a = zerovec; -s2xs_a = zerovec; -s2ys_a = zerovec; -Rxxs_a = zeromat; -Ryxs_a = zeromat; -count_a = 1; - -mmxs_b = zerovec; -mmys_b = zerovec; -s2xs_b = zerovec; -s2ys_b = zerovec; -Rxxs_b = zeromat; -Ryxs_b = zeromat; -count_b = 1; - -nog=0; - -aaa=zeros(size(TheFarEnd)); - -% loop over signal blocks -fprintf(1,'.. Suppression; averaging G over %5.1f seconds; file length %5.1f seconds ..\n',avtime, length(microphone)/samplingfreq); -fprintf(1,'.. SUPPRESSING ONLY AFTER %5.1f SECONDS! ..\n',avtime); -fprintf(1,'.. 20 seconds is good ..\n'); -hh = waitbar_j(0,'Please wait...'); - - -for i=1:updateno - - sb = (i-1)*updatel + 1; - se=sb+support-1; - - % analysis FFTs - temp=fft(win .* TheFarEnd(sb:se)); - fTheFarEnd(:,i)=temp(1:hsupport1); - xf=fTheFarEnd(:,i); - afTheFarEnd(:,i)= abs(fTheFarEnd(:,i)); - - temp=win .* microphone(sb:se); - - temp=fft(win .* microphone(sb:se)); - fmicrophone(:,i)=temp(1:hsupport1); - yf=fmicrophone(:,i); - afmicrophone(:,i)= abs(fmicrophone(:,i)); - - - ener_orig = afmicrophone(:,i)'*afmicrophone(:,i); - if( ener_orig == 0) - afmicrophone(:,i)=lowlevel*ones(size(afmicrophone(:,i))); - end - - - % use log domain (showed improved performance) -xxf= sqrt(real(xf.*conj(xf))+1e-20); -yyf= sqrt(real(yf.*conj(yf))+1e-20); - sxAll2(:,i) = 20*log10(xxf); - syAll2(:,i) = 20*log10(yyf); - - mD=min(i-1,maxDelayb); - xthreshold = sum(sxAll2(:,i-mD:i),2)/(maxDelayb+1); - - [yout, z200] = filter(Bp200,Ap200,syAll2(:,i),z200,2); - yout=yout/(maxDelayb+1); - ythreshold = mean(syAll2(:,i-mD:i),2); - - - bxspectrum(i)=getBspectrum(sxAll2(:,i),xthreshold,bandfirst,bandlast); - byspectrum(i)=getBspectrum(syAll2(:,i),yout,bandfirst,bandlast); - - bxhist(end-mD:end)=bxspectrum(i-mD:i); - - bcount(:,i)=hisser2( ... - byspectrum(i),flipud(bxhist),bandfirst,bandlast); - - - [fout(:,i), z500] = filter(Bp500,Ap500,bcount(:,i),z500,2); - fcount(:,i)=sum(bcount(:,max(1,i-histLenb+1):i),2); % using the history range - fout(:,i)=round(fout(:,i)); - [value,delay(i)]=min(fout(:,i),[],1); - tdelay(i)=(delay(i)-1)*support/(samplingfreq*oversampling); - - % compensate - - idel = max(i - delay(i) + 1,1); - - - % echo suppression - - noisyspec = afmicrophone(:,i); - - % Estimate G using covariance matrices - - % Cumulative estimates - xx = afTheFarEnd(:,idel); - yy = afmicrophone(:,i); - - % Means - mmxs_a = mmxs_a + xx; - mmys_a = mmys_a + yy; - if (G_ol) - mmxs_b = mmxs_b + xx; - mmys_b = mmys_b + yy; - mmy = mean([mmys_a/count_a mmys_b/count_b],2); - mmx = mean([mmxs_a/count_a mmxs_b/count_b],2); - else - mmx = mmxs_a/count_a; - mmy = mmys_a/count_a; - end - count_a = count_a + 1; - count_b = count_b + 1; - - % Mean removal - xxm = xx - mmx; - yym = yy - mmy; - - % Variances - s2xs_a = s2xs_a + xxm .* xxm; - s2ys_a = s2ys_a + yym .* yym; - s2xs_b = s2xs_b + xxm .* xxm; - s2ys_b = s2ys_b + yym .* yym; - - % Correlation matrices - Rxxs_a = Rxxs_a + xxm * xxm'; - Ryxs_a = Ryxs_a + yym * xxm'; - Rxxs_b = Rxxs_b + xxm * xxm'; - Ryxs_b = Ryxs_b + yym * xxm'; - - - % Gain matrix A - - if mod(i, estLen) == 0 - - - % Cumulative based estimates - Rxxf = Rxxs_a / (estLen - 1); - Ryxf = Ryxs_a / (estLen - 1); - - % Variance normalization - s2x2 = s2xs_a / (estLen - 1); - s2x2 = sqrt(s2x2); - % Sx = diag(max(s2x2,dynrange*max(s2x2))); - Sx = diag(s2x2); - if (sum(s2x2) > 0) - iSx = inv(Sx); - else - iSx= Sx + 0.01; - end - - s2y2 = s2ys_a / (estLen - 1); - s2y2 = sqrt(s2y2); - % Sy = diag(max(s2y2,dynrange*max(s2y2))); - Sy = diag(s2y2); - iSy = inv(Sy); - rx = iSx * Rxxf * iSx; - ryx = iSy * Ryxf * iSx; - - - - dbd= 7; % Us less than the full matrix - - % k x m - % Bandlimited structure on G - LSEon = 0; % Default is using MMSE - if (LSEon) - ryx = ryx*rx; - rx = rx*rx; - end - p = dbd-1; - gaj = min(min(hsupport1,2*p+1),min([p+(1:hsupport1); hsupport1+p+1-(1:hsupport1)])); - cgaj = [0 cumsum(gaj)]; - - G3 = zeros(hsupport1); - for kk=1:hsupport1 - ki = max(0,kk-p-1); - if (sum(sum(rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk))))>0) - G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk))/rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk)); - else - G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk)); - end - end - % End Bandlimited structure - - G = G3; - G(abs(G)<0.01)=0; - G = suppress_overdrive * Sy * G * iSx; - - if 1 - figure(32); mi=2; - surf(max(min(G,mi),-mi)); view(2) - title('Unscaled Masked Limited-bandwidth G'); - end - pause(0.05); - - % Reset sums - mmxs_a = zerovec; - mmys_a = zerovec; - s2xs_a = zerovec; - s2ys_a = zerovec; - Rxxs_a = zeromat; - Ryxs_a = zeromat; - count_a = 1; - - end - - if (G_ol) - % Gain matrix B - - if ((mod((i-estLen/2), estLen) == 0) & i>estLen) - - - % Cumulative based estimates - Rxxf = Rxxs_b / (estLen - 1); - Ryxf = Ryxs_b / (estLen - 1); - - % Variance normalization - s2x2 = s2xs_b / (estLen - 1); - s2x2 = sqrt(s2x2); - Sx = diag(max(s2x2,dynrange*max(s2x2))); - iSx = inv(Sx); - s2y2 = s2ys_b / (estLen - 1); - s2y2 = sqrt(s2y2); - Sy = diag(max(s2y2,dynrange*max(s2y2))); - iSy = inv(Sy); - rx = iSx * Rxxf * iSx; - ryx = iSy * Ryxf * iSx; - - - % Bandlimited structure on G - LSEon = 0; % Default is using MMSE - if (LSEon) - ryx = ryx*rx; - rx = rx*rx; - end - p = dbd-1; - gaj = min(min(hsupport1,2*p+1),min([p+(1:hsupport1); hsupport1+p+1-(1:hsupport1)])); - cgaj = [0 cumsum(gaj)]; - - G3 = zeros(hsupport1); - for kk=1:hsupport1 - ki = max(0,kk-p-1); - G3(kk,ki+1:ki+gaj(kk)) = ryx(kk,ki+1:ki+gaj(kk))/rx(ki+1:ki+gaj(kk),ki+1:ki+gaj(kk)); - end - % End Bandlimited structure - - G = G3; - G(abs(G)<0.01)=0; - G = suppress_overdrive * Sy * G * iSx; - - if 1 - figure(32); mi=2; - surf(max(min(G,mi),-mi)); view(2) - title('Unscaled Masked Limited-bandwidth G'); - end - pause(0.05); - - - % Reset sums - mmxs_b = zerovec; - mmys_b = zerovec; - s2xs_b = zerovec; - s2ys_b = zerovec; - Rxxs_b = zeromat; - Ryxs_b = zeromat; - count_b = 1; - - end - - end - - FECestimate2 = G*afTheFarEnd(:,idel); - - % compute Wiener filter and suppressor function - thefilter(:,i) = (noisyspec - gamma_echo*FECestimate2) ./ noisyspec; - ix0 = find(thefilter(:,i)1); % bounding in reasonable range - thefilter(ix0,i) = 1; - - % NONLINEARITY - nl_alpha=0.8; % memory; seems not very critical - nlSeverity=0.3; % nonlinearity severity: 0 does nothing; 1 suppresses all - thefmean=mean(thefilter(8:16,i)); - if (thefmean<1) - disp(''); - end - runningfmean = nl_alpha*runningfmean + (1-nl_alpha)*thefmean; - aaa(sb+20+1:sb+20+updatel)=10000*runningfmean* ones(updatel,1); % debug - slope0=1.0/(1.0-nlSeverity); % - thegain = max(0.0,min(1.0,slope0*(runningfmean-nlSeverity))); - % END NONLINEARITY - thefilter(:,i) = thegain*thefilter(:,i); - - - % Wiener filtering - femicrophone(:,i) = fmicrophone(:,i) .* thefilter(:,i); - thelimiter(:,i) = (noisyspec - A_GAIN*FECestimate2) ./ noisyspec; - index = find(thelimiter(:,i)>1.0); - thelimiter(index,i) = 1.0; - index = find(thelimiter(:,i)<0.0); - thelimiter(index,i) = 0.0; - - if (rem(i,floor(updateno/20))==0) - fprintf(1,'.'); - end - if mod(i,50)==0 - waitbar_j(i/updateno,hh); - end - - - % reconstruction; first make spectrum odd - temp=[femicrophone(:,i);flipud(conj(femicrophone(2:hsupport,i)))]; - emicrophone(sb:se) = emicrophone(sb:se) + factor * win .* real(ifft(temp)); - -end -fprintf(1,'\n'); - -close(hh); \ No newline at end of file diff --git a/modules/audio_processing/aecm/main/matlab/getBspectrum.m b/modules/audio_processing/aecm/main/matlab/getBspectrum.m deleted file mode 100644 index a4a533d60..000000000 --- a/modules/audio_processing/aecm/main/matlab/getBspectrum.m +++ /dev/null @@ -1,22 +0,0 @@ -function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast) -% function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast) -% compute binary spectrum using threshold spectrum as pivot -% bspectrum = binary spectrum (binary) -% ps=current power spectrum (float) -% threshold=threshold spectrum (float) -% bandfirst = first band considered -% bandlast = last band considered - -% initialization stuff - if( length(ps)32 | length(ps)~=length(threshold)) - error('BinDelayEst:spectrum:invalid','Dimensionality error'); -end - -% get current binary spectrum -diff = ps - threshold; -bspectrum=uint32(0); -for(i=bandfirst:bandlast) - if( diff(i)>0 ) - bspectrum = bitset(bspectrum,i); - end -end diff --git a/modules/audio_processing/aecm/main/matlab/hisser2.m b/modules/audio_processing/aecm/main/matlab/hisser2.m deleted file mode 100644 index 5a414f9da..000000000 --- a/modules/audio_processing/aecm/main/matlab/hisser2.m +++ /dev/null @@ -1,21 +0,0 @@ -function bcount=hisser2(bs,bsr,bandfirst,bandlast) -% function bcount=hisser(bspectrum,bandfirst,bandlast) -% histogram for the binary spectra -% bcount= array of bit counts -% bs=binary spectrum (one int32 number each) -% bsr=reference binary spectra (one int32 number each) -% blockSize = histogram over blocksize blocks -% bandfirst = first band considered -% bandlast = last band considered - -% weight all delays equally -maxDelay = length(bsr); - -% compute counts (two methods; the first works better and is operational) -bcount=zeros(maxDelay,1); -for(i=1:maxDelay) - % the delay should have low count for low-near&high-far and high-near&low-far - bcount(i)= sum(bitget(bitxor(bs,bsr(i)),bandfirst:bandlast)); - % the delay should have low count for low-near&high-far (works less well) -% bcount(i)= sum(bitget(bitand(bsr(i),bitxor(bs,bsr(i))),bandfirst:bandlast)); -end diff --git a/modules/audio_processing/aecm/main/matlab/main2.m b/modules/audio_processing/aecm/main/matlab/main2.m deleted file mode 100644 index 7e24c69cc..000000000 --- a/modules/audio_processing/aecm/main/matlab/main2.m +++ /dev/null @@ -1,19 +0,0 @@ - -fid=fopen('aecfar.pcm'); far=fread(fid,'short'); fclose(fid); -fid=fopen('aecnear.pcm'); mic=fread(fid,'short'); fclose(fid); - -%fid=fopen('QA1far.pcm'); far=fread(fid,'short'); fclose(fid); -%fid=fopen('QA1near.pcm'); mic=fread(fid,'short'); fclose(fid); - -start=0 * 8000+1; -stop= 30 * 8000; -microphone=mic(start:stop); -TheFarEnd=far(start:stop); -avtime=1; - -% 16000 to make it compatible with the C-version -[emicrophone,tdel]=compsup(microphone,TheFarEnd,avtime,16000); - -spclab(8000,TheFarEnd,microphone,emicrophone); - - diff --git a/modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m b/modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m deleted file mode 100644 index 2d3e6867d..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/AECMobile.m +++ /dev/null @@ -1,269 +0,0 @@ -function [femicrophone, aecmStructNew, enerNear, enerFar] = AECMobile(fmicrophone, afTheFarEnd, setupStruct, aecmStruct) -global NEARENDFFT; -global F; - -aecmStructNew = aecmStruct; - -% Magnitude spectrum of near end signal -afmicrophone = abs(fmicrophone); -%afmicrophone = NEARENDFFT(setupStruct.currentBlock,:)'/2^F(setupStruct.currentBlock,end); -% Near end energy level -ener_orig = afmicrophone'*afmicrophone; -if( ener_orig == 0) - lowlevel = 0.01; - afmicrophone = lowlevel*ones(size(afmicrophone)); -end -%adiff = max(abs(afmicrophone - afTheFarEnd)); -%if (adiff > 0) -% disp([setupStruct.currentBlock adiff]) -%end - -% Store the near end energy -%aecmStructNew.enerNear(setupStruct.currentBlock) = log(afmicrophone'*afmicrophone); -aecmStructNew.enerNear(setupStruct.currentBlock) = log(sum(afmicrophone)); -% Store the far end energy -%aecmStructNew.enerFar(setupStruct.currentBlock) = log(afTheFarEnd'*afTheFarEnd); -aecmStructNew.enerFar(setupStruct.currentBlock) = log(sum(afTheFarEnd)); - -% Update subbands (We currently use all frequency bins, hence .useSubBand is turned off) -if aecmStructNew.useSubBand - internalIndex = 1; - for kk=1:setupStruct.subBandLength+1 - ySubBand(kk) = mean(afmicrophone(internalIndex:internalIndex+setupStruct.numInBand(kk)-1).^aecmStructNew.bandFactor); - xSubBand(kk) = mean(afTheFarEnd(internalIndex:internalIndex+setupStruct.numInBand(kk)-1).^aecmStructNew.bandFactor); - internalIndex = internalIndex + setupStruct.numInBand(kk); - end -else - ySubBand = afmicrophone.^aecmStructNew.bandFactor; - xSubBand = afTheFarEnd.^aecmStructNew.bandFactor; -end - -% Estimated echo energy -if (aecmStructNew.bandFactor == 1) - %aecmStructNew.enerEcho(setupStruct.currentBlock) = log((aecmStructNew.H.*xSubBand)'*(aecmStructNew.H.*xSubBand)); - %aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log((aecmStructNew.HStored.*xSubBand)'*(aecmStructNew.HStored.*xSubBand)); - aecmStructNew.enerEcho(setupStruct.currentBlock) = log(sum(aecmStructNew.H.*xSubBand)); - aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log(sum(aecmStructNew.HStored.*xSubBand)); -elseif (aecmStructNew.bandFactor == 2) - aecmStructNew.enerEcho(setupStruct.currentBlock) = log(aecmStructNew.H'*xSubBand); - aecmStructNew.enerEchoStored(setupStruct.currentBlock) = log(aecmStructNew.HStored'*xSubBand); -end - -% Last 100 blocks of data, used for plotting -n100 = max(1,setupStruct.currentBlock-99):setupStruct.currentBlock; -enerError = aecmStructNew.enerNear(n100)-aecmStructNew.enerEcho(n100); -enerErrorStored = aecmStructNew.enerNear(n100)-aecmStructNew.enerEchoStored(n100); - -% Store the far end sub band. This is needed if we use LSE instead of NLMS -aecmStructNew.X = [xSubBand aecmStructNew.X(:,1:end-1)]; - -% Update energy levels, which control the VAD -if ((aecmStructNew.enerFar(setupStruct.currentBlock) < aecmStructNew.energyMin) & (aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStruct.FAR_ENERGY_MIN)) - aecmStructNew.energyMin = aecmStructNew.enerFar(setupStruct.currentBlock); - %aecmStructNew.energyMin = max(aecmStructNew.energyMin,12); - aecmStructNew.energyMin = max(aecmStructNew.energyMin,aecmStruct.FAR_ENERGY_MIN); - aecmStructNew.energyLevel = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThres+aecmStructNew.energyMin; - aecmStructNew.energyLevelMSE = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThresMSE+aecmStructNew.energyMin; -end -if (aecmStructNew.enerFar(setupStruct.currentBlock) > aecmStructNew.energyMax) - aecmStructNew.energyMax = aecmStructNew.enerFar(setupStruct.currentBlock); - aecmStructNew.energyLevel = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThres+aecmStructNew.energyMin; - aecmStructNew.energyLevelMSE = (aecmStructNew.energyMax-aecmStructNew.energyMin)*aecmStructNew.energyThresMSE+aecmStructNew.energyMin; -end - -% Calculate current energy error in near end (estimated echo vs. near end) -dE = aecmStructNew.enerNear(setupStruct.currentBlock)-aecmStructNew.enerEcho(setupStruct.currentBlock); - -%%%%%%%% -% Calculate step size used in LMS algorithm, based on current far end energy and near end energy error (dE) -%%%%%%%% -if setupStruct.stepSize_flag - [mu, aecmStructNew] = calcStepSize(aecmStructNew.enerFar(setupStruct.currentBlock), dE, aecmStructNew, setupStruct.currentBlock, 1); -else - mu = 0.25; -end -aecmStructNew.muLog(setupStruct.currentBlock) = mu; % Store the step size - -% Estimate Echo Spectral Shape -[U, aecmStructNew.H] = fallerEstimator(ySubBand,aecmStructNew.X,aecmStructNew.H,mu); - -%%%%% -% Determine if we should store or restore the channel -%%%%% -if ((setupStruct.currentBlock <= aecmStructNew.convLength) | (~setupStruct.channelUpdate_flag)) - aecmStructNew.HStored = aecmStructNew.H; % Store what you have after startup -elseif ((setupStruct.currentBlock > aecmStructNew.convLength) & (setupStruct.channelUpdate_flag)) - if ((aecmStructNew.enerFar(setupStruct.currentBlock) < aecmStructNew.energyLevelMSE) & (aecmStructNew.enerFar(setupStruct.currentBlock-1) >= aecmStructNew.energyLevelMSE)) - xxx = aecmStructNew.countMseH; - if (xxx > 20) - mseStored = mean(abs(aecmStructNew.enerEchoStored(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)-aecmStructNew.enerNear(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1))); - mseLatest = mean(abs(aecmStructNew.enerEcho(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1)-aecmStructNew.enerNear(setupStruct.currentBlock-xxx:setupStruct.currentBlock-1))); - %fprintf('Stored: %4f Latest: %4f\n', mseStored, mseLatest) % Uncomment if you want to display the MSE values - if ((mseStored < 0.8*mseLatest) & (aecmStructNew.mseHStoredOld < 0.8*aecmStructNew.mseHLatestOld)) - aecmStructNew.H = aecmStructNew.HStored; - fprintf('Restored H at block %d\n',setupStruct.currentBlock) - elseif (((0.8*mseStored > mseLatest) & (mseLatest < aecmStructNew.mseHThreshold) & (aecmStructNew.mseHLatestOld < aecmStructNew.mseHThreshold)) | (mseStored == Inf)) - aecmStructNew.HStored = aecmStructNew.H; - fprintf('Stored new H at block %d\n',setupStruct.currentBlock) - end - aecmStructNew.mseHStoredOld = mseStored; - aecmStructNew.mseHLatestOld = mseLatest; - end - elseif ((aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStructNew.energyLevelMSE) & (aecmStructNew.enerFar(setupStruct.currentBlock-1) < aecmStructNew.energyLevelMSE)) - aecmStructNew.countMseH = 1; - elseif (aecmStructNew.enerFar(setupStruct.currentBlock) >= aecmStructNew.energyLevelMSE) - aecmStructNew.countMseH = aecmStructNew.countMseH + 1; - end -end - -%%%%% -% Check delay (calculate the delay offset (if we can)) -% The algorithm is not tuned and should be used with care. It runs separately from Bastiaan's algorithm. -%%%%% -yyy = 31; % Correlation buffer length (currently unfortunately hard coded) -dxxx = 25; % Maximum offset (currently unfortunately hard coded) -if (setupStruct.currentBlock > aecmStructNew.convLength) - if (aecmStructNew.enerFar(setupStruct.currentBlock-(yyy+2*dxxx-1):setupStruct.currentBlock) > aecmStructNew.energyLevelMSE) - for xxx = -dxxx:dxxx - aecmStructNew.delayLatestS(xxx+dxxx+1) = sum(sign(aecmStructNew.enerEcho(setupStruct.currentBlock-(yyy+dxxx-xxx)+1:setupStruct.currentBlock+xxx-dxxx)-mean(aecmStructNew.enerEcho(setupStruct.currentBlock-(yyy++dxxx-xxx)+1:setupStruct.currentBlock+xxx-dxxx))).*sign(aecmStructNew.enerNear(setupStruct.currentBlock-yyy-dxxx+1:setupStruct.currentBlock-dxxx)-mean(aecmStructNew.enerNear(setupStruct.currentBlock-yyy-dxxx+1:setupStruct.currentBlock-dxxx)))); - end - aecmStructNew.newDelayCurve = 1; - end -end -if ((setupStruct.currentBlock > 2*aecmStructNew.convLength) & ~rem(setupStruct.currentBlock,yyy*2) & aecmStructNew.newDelayCurve) - [maxV,maxP] = max(aecmStructNew.delayLatestS); - if ((maxP > 2) & (maxP < 2*dxxx)) - maxVLeft = aecmStructNew.delayLatestS(max(1,maxP-4)); - maxVRight = aecmStructNew.delayLatestS(min(2*dxxx+1,maxP+4)); - %fprintf('Max %d, Left %d, Right %d\n',maxV,maxVLeft,maxVRight) % Uncomment if you want to see max value - if ((maxV > 24) & (maxVLeft < maxV - 10) & (maxVRight < maxV - 10)) - aecmStructNew.feedbackDelay = maxP-dxxx-1; - aecmStructNew.newDelayCurve = 0; - aecmStructNew.feedbackDelayUpdate = 1; - fprintf('Feedback Update at block %d\n',setupStruct.currentBlock) - end - end -end -% End of "Check delay" -%%%%%%%% - -%%%%% -% Calculate suppression gain, based on far end energy and near end energy error (dE) -if (setupStruct.supGain_flag) - [gamma_echo, aecmStructNew.cntIn, aecmStructNew.cntOut] = calcFilterGain(aecmStructNew.enerFar(setupStruct.currentBlock), dE, aecmStructNew, setupStruct.currentBlock, aecmStructNew.convLength, aecmStructNew.cntIn, aecmStructNew.cntOut); -else - gamma_echo = 1; -end -aecmStructNew.gammaLog(setupStruct.currentBlock) = gamma_echo; % Store the gain -gamma_use = gamma_echo; - -% Use the stored channel -U = aecmStructNew.HStored.*xSubBand; - -% compute Wiener filter and suppressor function -Iy = find(ySubBand); -subBandFilter = zeros(size(ySubBand)); -if (aecmStructNew.bandFactor == 2) - subBandFilter(Iy) = (1 - gamma_use*sqrt(U(Iy)./ySubBand(Iy))); % For Faller -else - subBandFilter(Iy) = (1 - gamma_use*(U(Iy)./ySubBand(Iy))); % For COV -end -ix0 = find(subBandFilter < 0); % bounding trick 1 -subBandFilter(ix0) = 0; -ix0 = find(subBandFilter > 1); % bounding trick 1 -subBandFilter(ix0) = 1; - -% Interpolate back to normal frequency bins if we use sub bands -if aecmStructNew.useSubBand - thefilter = interp1(setupStruct.centerFreq,subBandFilter,linspace(0,setupStruct.samplingfreq/2,setupStruct.hsupport1)','nearest'); - testfilter = interp1(setupStruct.centerFreq,subBandFilter,linspace(0,setupStruct.samplingfreq/2,1000),'nearest'); - thefilter(end) = subBandFilter(end); - - internalIndex = 1; - for kk=1:setupStruct.subBandLength+1 - internalIndex:internalIndex+setupStruct.numInBand(kk)-1; - thefilter(internalIndex:internalIndex+setupStruct.numInBand(kk)-1) = subBandFilter(kk); - internalIndex = internalIndex + setupStruct.numInBand(kk); - end -else - thefilter = subBandFilter; - testfilter = subBandFilter; -end - -% Bound the filter -ix0 = find(thefilter < setupStruct.de_echo_bound); % bounding trick 1 -thefilter(ix0) = setupStruct.de_echo_bound; % bounding trick 2 -ix0 = find(thefilter > 1); % bounding in reasonable range -thefilter(ix0) = 1; - -%%%% -% NLP -%%%% -thefmean = mean(thefilter(8:16)); -if (thefmean < 1) - disp(''); -end -aecmStructNew.runningfmean = setupStruct.nl_alpha*aecmStructNew.runningfmean + (1-setupStruct.nl_alpha)*thefmean; -slope0 = 1.0/(1.0 - setupStruct.nlSeverity); % -thegain = max(0.0, min(1.0, slope0*(aecmStructNew.runningfmean - setupStruct.nlSeverity))); -if ~setupStruct.nlp_flag - thegain = 1; -end -% END NONLINEARITY -thefilter = thegain*thefilter; - -%%%% -% The suppression -%%%% -femicrophone = fmicrophone .* thefilter; -% Store the output energy (used for plotting) -%aecmStructNew.enerOut(setupStruct.currentBlock) = log(abs(femicrophone)'*abs(femicrophone)); -aecmStructNew.enerOut(setupStruct.currentBlock) = log(sum(abs(femicrophone))); - -if aecmStructNew.plotIt - figure(13) - subplot(311) - %plot(n100,enerFar(n100),'b-',n100,enerNear(n100),'k--',n100,enerEcho(n100),'r-',[n100(1) n100(end)],[1 1]*vadThNew,'b:',[n100(1) n100(end)],[1 1]*((energyMax-energyMin)/4+energyMin),'r-.',[n100(1) n100(end)],[1 1]*vadNearThNew,'g:',[n100(1) n100(end)],[1 1]*energyMax,'r-.',[n100(1) n100(end)],[1 1]*energyMin,'r-.','LineWidth',2) - plot(n100,aecmStructNew.enerFar(n100),'b-',n100,aecmStructNew.enerNear(n100),'k--',n100,aecmStructNew.enerOut(n100),'r-.',n100,aecmStructNew.enerEcho(n100),'r-',n100,aecmStructNew.enerEchoStored(n100),'c-',[n100(1) n100(end)],[1 1]*((aecmStructNew.energyMax-aecmStructNew.energyMin)/4+aecmStructNew.energyMin),'g-.',[n100(1) n100(end)],[1 1]*aecmStructNew.energyMax,'g-.',[n100(1) n100(end)],[1 1]*aecmStructNew.energyMin,'g-.','LineWidth',2) - %title(['Frame ',int2str(i),' av ',int2str(setupStruct.updateno),' State = ',int2str(speechState),' \mu = ',num2str(mu)]) - title(['\gamma = ',num2str(gamma_echo),' \mu = ',num2str(mu)]) - subplot(312) - %plot(n100,enerError,'b-',[n100(1) n100(end)],[1 1]*vadNearTh,'r:',[n100(1) n100(end)],[-1.5 -1.5]*vadNearTh,'r:','LineWidth',2) - %plot(n100,enerError,'b-',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2) - plot(n100,enerError,'b-',n100,enerErrorStored,'c-',[n100(1) n100(end)],[1 1]*aecmStructNew.varMean,'k--',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2) - % Plot mu - %plot(n100,log2(aecmStructNew.muLog(n100)),'b-','LineWidth',2) - %plot(n100,log2(aecmStructNew.HGain(n100)),'b-',[n100(1) n100(end)],[1 1]*log2(sum(aecmStructNew.HStored)),'r:','LineWidth',2) - title(['Block ',int2str(setupStruct.currentBlock),' av ',int2str(setupStruct.updateno)]) - subplot(313) - %plot(n100,enerVar(n100),'b-',[n100(1) n100(end)],[1 1],'r:',[n100(1) n100(end)],[-2 -2],'r:','LineWidth',2) - %plot(n100,enerVar(n100),'b-','LineWidth',2) - % Plot correlation curve - - %plot(-25:25,aecmStructNew.delayStored/max(aecmStructNew.delayStored),'c-',-25:25,aecmStructNew.delayLatest/max(aecmStructNew.delayLatest),'r-',-25:25,(max(aecmStructNew.delayStoredS)-aecmStructNew.delayStoredS)/(max(aecmStructNew.delayStoredS)-min(aecmStructNew.delayStoredS)),'c:',-25:25,(max(aecmStructNew.delayLatestS)-aecmStructNew.delayLatestS)/(max(aecmStructNew.delayLatestS)-min(aecmStructNew.delayLatestS)),'r:','LineWidth',2) - %plot(-25:25,aecmStructNew.delayStored,'c-',-25:25,aecmStructNew.delayLatest,'r-',-25:25,(max(aecmStructNew.delayStoredS)-aecmStructNew.delayStoredS)/(max(aecmStructNew.delayStoredS)-min(aecmStructNew.delayStoredS)),'c:',-25:25,(max(aecmStructNew.delayLatestS)-aecmStructNew.delayLatestS)/(max(aecmStructNew.delayLatestS)-min(aecmStructNew.delayLatestS)),'r:','LineWidth',2) - %plot(-25:25,aecmStructNew.delayLatest,'r-',-25:25,(50-aecmStructNew.delayLatestS)/100,'r:','LineWidth',2) - plot(-25:25,aecmStructNew.delayLatestS,'r:','LineWidth',2) - %plot(-25:25,aecmStructNew.delayStored,'c-',-25:25,aecmStructNew.delayLatest,'r-','LineWidth',2) - plot(0:32,aecmStruct.HStored,'bo-','LineWidth',2) - %title(['\gamma | In = ',int2str(aecmStructNew.muStruct.countInInterval),' | Out High = ',int2str(aecmStructNew.muStruct.countOutHighInterval),' | Out Low = ',int2str(aecmStructNew.muStruct.countOutLowInterval)]) - pause(1) - %if ((setupStruct.currentBlock == 860) | (setupStruct.currentBlock == 420) | (setupStruct.currentBlock == 960)) - if 0%(setupStruct.currentBlock == 960) - figure(60) - plot(n100,aecmStructNew.enerNear(n100),'k--',n100,aecmStructNew.enerEcho(n100),'k:','LineWidth',2) - legend('Near End','Estimated Echo') - title('Signal Energy witH offset compensation') - figure(61) - subplot(211) - stem(sign(aecmStructNew.enerNear(n100)-mean(aecmStructNew.enerNear(n100)))) - title('Near End Energy Pattern (around mean value)') - subplot(212) - stem(sign(aecmStructNew.enerEcho(n100)-mean(aecmStructNew.enerEcho(n100)))) - title('Estimated Echo Energy Pattern (around mean value)') - pause - end - drawnow%,pause -elseif ~rem(setupStruct.currentBlock,100) - fprintf('Block %d of %d\n',setupStruct.currentBlock,setupStruct.updateno) -end diff --git a/modules/audio_processing/aecm/main/matlab/matlab/align.m b/modules/audio_processing/aecm/main/matlab/matlab/align.m deleted file mode 100644 index 9b9c0baf3..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/align.m +++ /dev/null @@ -1,98 +0,0 @@ -function [delayStructNew] = align(xf, yf, delayStruct, i, trueDelay); - -%%%%%%% -% Bastiaan's algorithm copied -%%%%%%% -Ap500 = [1.00, -4.95, 9.801, -9.70299, 4.80298005, -0.9509900499]; -Bp500 = [0.662743088639636, -2.5841655608125, 3.77668102146288, -2.45182477425154, 0.596566274575251, 0.0]; -Ap200 = [1.00, -4.875, 9.50625, -9.26859375, 4.518439453125, -0.881095693359375]; -Bp200 = [0.862545460994275, -3.2832804496114, 4.67892032308828, -2.95798023879133, 0.699796870041299, 0.0]; - -oldMethod = 1; % Turn on or off the old method. The new one is Bastiaan's August 2008 updates -THReSHoLD = 2.0; % ADJUSTABLE threshold factor; 4.0 seems good -%%%%%%%%%%%%%%%%%%% -% use log domain (showed improved performance) -xxf = sqrt(real(xf.*conj(xf))+1e-20); -yyf = sqrt(real(yf.*conj(yf))+1e-20); -delayStruct.sxAll2(:,i) = 20*log10(xxf); -delayStruct.syAll2(:,i) = 20*log10(yyf); - -mD = min(i-1,delayStruct.maxDelayb); -if oldMethod - factor = 1.0; - histLenb = 250; - xthreshold = factor*median(delayStruct.sxAll2(:,i-mD:i),2); - ythreshold = factor*median(delayStruct.syAll2(:,i-mD:i),2); -else - xthreshold = sum(delayStruct.sxAll2(:,i-mD:i),2)/(delayStruct.maxDelayb+1); - - [yout, delayStruct.z200] = filter(Bp200, Ap200, delayStruct.syAll2(:,i), delayStruct.z200, 2); - yout = yout/(delayStruct.maxDelayb+1); - ythreshold = mean(delayStruct.syAll2(:,i-mD:i),2); - ythreshold = yout; -end - -delayStruct.bxspectrum(i) = getBspectrum(delayStruct.sxAll2(:,i), xthreshold, delayStruct.bandfirst, delayStruct.bandlast); -delayStruct.byspectrum(i) = getBspectrum(delayStruct.syAll2(:,i), ythreshold, delayStruct.bandfirst, delayStruct.bandlast); - -delayStruct.bxhist(end-mD:end) = delayStruct.bxspectrum(i-mD:i); - -delayStruct.bcount(:,i) = hisser2(delayStruct.byspectrum(i), flipud(delayStruct.bxhist), delayStruct.bandfirst, delayStruct.bandlast); -[delayStruct.fout(:,i), delayStruct.z500] = filter(Bp500, Ap500, delayStruct.bcount(:,i), delayStruct.z500, 2); -if oldMethod - %delayStruct.new(:,i) = sum(delayStruct.bcount(:,max(1,i-histLenb+1):i),2); % using the history range - tmpVec = [delayStruct.fout(1,i)*ones(2,1); delayStruct.fout(:,i); delayStruct.fout(end,i)*ones(2,1)]; % using the history range - tmpVec = filter(ones(1,5), 1, tmpVec); - delayStruct.new(:,i) = tmpVec(5:end); - %delayStruct.new(:,i) = delayStruct.fout(:,i); % using the history range -else - [delayStruct.fout(:,i), delayStruct.z500] = filter(Bp500, Ap500, delayStruct.bcount(:,i), delayStruct.z500, 2); - % NEW CODE - delayStruct.new(:,i) = filter([-1,-2,1,4,1,-2,-1], 1, delayStruct.fout(:,i)); %remv smth component - delayStruct.new(1:end-3,i) = delayStruct.new(1+3:end,i); - delayStruct.new(1:6,i) = 0.0; - delayStruct.new(end-6:end,i) = 0.0; % ends are no good -end -[valuen, tempdelay] = min(delayStruct.new(:,i)); % find minimum -if oldMethod - threshold = valuen + (max(delayStruct.new(:,i)) - valuen)/4; - thIndex = find(delayStruct.new(:,i) <= threshold); - if (i > 1) - delayDiff = abs(delayStruct.delay(i-1)-tempdelay+1); - if (delayStruct.oneGoodEstimate & (max(diff(thIndex)) > 1) & (delayDiff < 10)) - % We consider this minimum to be significant, hence update the delay - delayStruct.delay(i) = tempdelay; - elseif (~delayStruct.oneGoodEstimate & (max(diff(thIndex)) > 1)) - delayStruct.delay(i) = tempdelay; - if (i > histLenb) - delayStruct.oneGoodEstimate = 1; - end - else - delayStruct.delay(i) = delayStruct.delay(i-1); - end - else - delayStruct.delay(i) = tempdelay; - end -else - threshold = THReSHoLD*std(delayStruct.new(:,i)); % set updata threshold - if ((-valuen > threshold) | (i < delayStruct.smlength)) % see if you want to update delay - delayStruct.delay(i) = tempdelay; - else - delayStruct.delay(i) = delayStruct.delay(i-1); - end - % END NEW CODE -end -delayStructNew = delayStruct; - -% administrative and plotting stuff -if( 0) - figure(10); - plot([1:length(delayStructNew.new(:,i))],delayStructNew.new(:,i),trueDelay*[1 1],[min(delayStructNew.new(:,i)),max(delayStructNew.new(:,i))],'r',[1 length(delayStructNew.new(:,i))],threshold*[1 1],'r:', 'LineWidth',2); - %plot([1:length(delayStructNew.bcount(:,i))],delayStructNew.bcount(:,i),trueDelay*[1 1],[min(delayStructNew.bcount(:,i)),max(delayStructNew.bcount(:,i))],'r','LineWidth',2); - %plot([thedelay,thedelay],[min(fcount(:,i)),max(fcount(:,i))],'r'); - %title(sprintf('bin count and known delay at time %5.1f s\n',(i-1)*(support/(fs*oversampling)))); - title(delayStructNew.oneGoodEstimate) - xlabel('delay in frames'); - %hold off; - drawnow -end diff --git a/modules/audio_processing/aecm/main/matlab/matlab/calcFilterGain.m b/modules/audio_processing/aecm/main/matlab/matlab/calcFilterGain.m deleted file mode 100644 index a09a7f222..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/calcFilterGain.m +++ /dev/null @@ -1,88 +0,0 @@ -function [gam, cntIn2, cntOut2] = calcFilterGain(energy, dE, aecmStruct, t, T, cntIn, cntOut) - -defaultLevel = 1.2; -cntIn2 = cntIn; -cntOut2 = cntOut; -if (t < T) - gam = 1; -else - dE1 = -5; - dE2 = 1; - gamMid = 0.2; - gam = max(0,min((energy - aecmStruct.energyMin)/(aecmStruct.energyLevel - aecmStruct.energyMin), 1-(1-gamMid)*(aecmStruct.energyMax-energy)/(aecmStruct.energyMax-aecmStruct.energyLevel))); - - dEOffset = -0.5; - dEWidth = 1.5; - %gam2 = max(1,2-((dE-dEOffset)/(dE2-dEOffset)).^2); - gam2 = 1+(abs(dE-dEOffset)<(dE2-dEOffset)); - - gam = gam*gam2; - - - if (energy < aecmStruct.energyLevel) - gam = 0; - else - gam = defaultLevel; - end - dEVec = aecmStruct.enerNear(t-63:t)-aecmStruct.enerEcho(t-63:t); - %dEVec = aecmStruct.enerNear(t-20:t)-aecmStruct.enerEcho(t-20:t); - numCross = 0; - currentState = 0; - for ii=1:64 - if (currentState == 0) - currentState = (dEVec(ii) > dE2) - (dEVec(ii) < -2); - elseif ((currentState == 1) & (dEVec(ii) < -2)) - numCross = numCross + 1; - currentState = -1; - elseif ((currentState == -1) & (dEVec(ii) > dE2)) - numCross = numCross + 1; - currentState = 1; - end - end - gam = max(0, gam - numCross/25); - gam = 1; - - ener_A = 1; - ener_B = 0.8; - ener_C = aecmStruct.energyLevel + (aecmStruct.energyMax-aecmStruct.energyLevel)/5; - dE_A = 4;%2; - dE_B = 3.6;%1.8; - dE_C = 0.9*dEWidth; - dE_D = 1; - timeFactorLength = 10; - ddE = abs(dE-dEOffset); - if (energy < aecmStruct.energyLevel) - gam = 0; - else - gam = 1; - gam2 = max(0, min(ener_B*(energy-aecmStruct.energyLevel)/(ener_C-aecmStruct.energyLevel), ener_B+(ener_A-ener_B)*(energy-ener_C)/(aecmStruct.energyMax-ener_C))); - if (ddE < dEWidth) - % Update counters - cntIn2 = cntIn2 + 1; - if (cntIn2 > 2) - cntOut2 = 0; - end - gam3 = max(dE_D, min(dE_A-(dE_A-dE_B)*(ddE/dE_C), dE_D+(dE_B-dE_D)*(dEWidth-ddE)/(dEWidth-dE_C))); - gam3 = dE_A; - else - % Update counters - cntOut2 = cntOut2 + 1; - if (cntOut2 > 2) - cntIn2 = 0; - end - %gam2 = 1; - gam3 = dE_D; - end - timeFactor = min(1, cntIn2/timeFactorLength); - gam = gam*(1-timeFactor) + timeFactor*gam2*gam3; - end - %gam = gam/floor(numCross/2+1); -end -if isempty(gam) - numCross - timeFactor - cntIn2 - cntOut2 - gam2 - gam3 -end diff --git a/modules/audio_processing/aecm/main/matlab/matlab/calcStepSize.m b/modules/audio_processing/aecm/main/matlab/matlab/calcStepSize.m deleted file mode 100644 index ae1365fa4..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/calcStepSize.m +++ /dev/null @@ -1,105 +0,0 @@ -function [mu, aecmStructNew] = calcStepSize(energy, dE, aecmStruct, t, logscale) - -if (nargin < 4) - t = 1; - logscale = 1; -elseif (nargin == 4) - logscale = 1; -end -T = aecmStruct.convLength; - -if logscale - currentMuMax = aecmStruct.MU_MIN + (aecmStruct.MU_MAX-aecmStruct.MU_MIN)*min(t,T)/T; - if (aecmStruct.energyMin >= aecmStruct.energyMax) - mu = aecmStruct.MU_MIN; - else - mu = (energy - aecmStruct.energyMin)/(aecmStruct.energyMax - aecmStruct.energyMin)*(currentMuMax-aecmStruct.MU_MIN) + aecmStruct.MU_MIN; - end - mu = 2^mu; - if (energy < aecmStruct.energyLevel) - mu = 0; - end -else - muMin = 0; - muMax = 0.5; - currentMuMax = muMin + (muMax-muMin)*min(t,T)/T; - if (aecmStruct.energyMin >= aecmStruct.energyMax) - mu = muMin; - else - mu = (energy - aecmStruct.energyMin)/(aecmStruct.energyMax - aecmStruct.energyMin)*(currentMuMax-muMin) + muMin; - end -end -dE2 = 1; -dEOffset = -0.5; -offBoost = 5; -if (mu > 0) - if (abs(dE-aecmStruct.ENERGY_DEV_OFFSET) > aecmStruct.ENERGY_DEV_TOL) - aecmStruct.muStruct.countInInterval = 0; - else - aecmStruct.muStruct.countInInterval = aecmStruct.muStruct.countInInterval + 1; - end - if (dE < aecmStruct.ENERGY_DEV_OFFSET - aecmStruct.ENERGY_DEV_TOL) - aecmStruct.muStruct.countOutLowInterval = aecmStruct.muStruct.countOutLowInterval + 1; - else - aecmStruct.muStruct.countOutLowInterval = 0; - end - if (dE > aecmStruct.ENERGY_DEV_OFFSET + aecmStruct.ENERGY_DEV_TOL) - aecmStruct.muStruct.countOutHighInterval = aecmStruct.muStruct.countOutHighInterval + 1; - else - aecmStruct.muStruct.countOutHighInterval = 0; - end -end -muVar = 2^min(-3,5/50*aecmStruct.muStruct.countInInterval-3); -muOff = 2^max(offBoost,min(0,offBoost*(aecmStruct.muStruct.countOutLowInterval-aecmStruct.muStruct.minOutLowInterval)/(aecmStruct.muStruct.maxOutLowInterval-aecmStruct.muStruct.minOutLowInterval))); - -muLow = 1/64; -muVar = 1; -if (t < 2*T) - muDT = 1; - muVar = 1; - mdEVec = 0; - numCross = 0; -else - muDT = min(1,max(muLow,1-(1-muLow)*(dE-aecmStruct.ENERGY_DEV_OFFSET)/aecmStruct.ENERGY_DEV_TOL)); - dEVec = aecmStruct.enerNear(t-63:t)-aecmStruct.enerEcho(t-63:t); - %dEVec = aecmStruct.enerNear(t-20:t)-aecmStruct.enerEcho(t-20:t); - numCross = 0; - currentState = 0; - for ii=1:64 - if (currentState == 0) - currentState = (dEVec(ii) > dE2) - (dEVec(ii) < -2); - elseif ((currentState == 1) & (dEVec(ii) < -2)) - numCross = numCross + 1; - currentState = -1; - elseif ((currentState == -1) & (dEVec(ii) > dE2)) - numCross = numCross + 1; - currentState = 1; - end - end - - %logicDEVec = (dEVec > dE2) - (dEVec < -2); - %numCross = sum(abs(diff(logicDEVec))); - %mdEVec = mean(abs(dEVec-dEOffset)); - %mdEVec = mean(abs(dEVec-mean(dEVec))); - %mdEVec = max(dEVec)-min(dEVec); - %if (mdEVec > 4)%1.5) - % muVar = 0; - %end - muVar = 2^(-floor(numCross/2)); - muVar = 2^(-numCross); -end -%muVar = 1; - - -% if (eStd > (dE2-dEOffset)) -% muVar = 1/8; -% else -% muVar = 1; -% end - -%mu = mu*muDT*muVar*muOff; -mu = mu*muDT*muVar; -mu = min(mu,0.25); -aecmStructNew = aecmStruct; -%aecmStructNew.varMean = mdEVec; -aecmStructNew.varMean = numCross; diff --git a/modules/audio_processing/aecm/main/matlab/matlab/fallerEstimator.m b/modules/audio_processing/aecm/main/matlab/matlab/fallerEstimator.m deleted file mode 100644 index d038b519c..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/fallerEstimator.m +++ /dev/null @@ -1,42 +0,0 @@ -function [U, Hnew] = fallerEstimator(Y, X, H, mu) - -% Near end signal is stacked frame by frame columnwise in matrix Y and far end in X -% -% Possible estimation procedures are -% 1) LSE -% 2) NLMS -% 3) Separated numerator and denomerator filters -regParam = 1; -[numFreqs, numFrames] = size(Y); -[numFreqs, Q] = size(X); -U = zeros(numFreqs, 1); - -if ((nargin == 3) | (nargin == 5)) - dtd = 0; -end -if (nargin == 4) - dtd = H; -end -Emax = 7; -dEH = Emax-sum(sum(H)); -nu = 2*mu; -% if (nargin < 5) -% H = zeros(numFreqs, Q); -% for kk = 1:numFreqs -% Xmatrix = hankel(X(kk,1:Q),X(kk,Q:end)); -% y = Y(kk,1:end-Q+1)'; -% H(kk,:) = (y'*Xmatrix')*inv(Xmatrix*Xmatrix'+regParam); -% U(kk,1) = H(kk,:)*Xmatrix(:,1); -% end -% else - for kk = 1:numFreqs - x = X(kk,1:Q)'; - y = Y(kk,1); - Htmp = mu*(y-H(kk,:)*x)/(x'*x+regParam)*x; - %Htmp = (mu*(y-H(kk,:)*x)/(x'*x+regParam) - nu/dEH)*x; - H(kk,:) = H(kk,:) + Htmp'; - U(kk,1) = H(kk,:)*x; - end -% end - -Hnew = H; diff --git a/modules/audio_processing/aecm/main/matlab/matlab/getBspectrum.m b/modules/audio_processing/aecm/main/matlab/matlab/getBspectrum.m deleted file mode 100644 index a4a533d60..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/getBspectrum.m +++ /dev/null @@ -1,22 +0,0 @@ -function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast) -% function bspectrum=getBspectrum(ps,threshold,bandfirst,bandlast) -% compute binary spectrum using threshold spectrum as pivot -% bspectrum = binary spectrum (binary) -% ps=current power spectrum (float) -% threshold=threshold spectrum (float) -% bandfirst = first band considered -% bandlast = last band considered - -% initialization stuff - if( length(ps)32 | length(ps)~=length(threshold)) - error('BinDelayEst:spectrum:invalid','Dimensionality error'); -end - -% get current binary spectrum -diff = ps - threshold; -bspectrum=uint32(0); -for(i=bandfirst:bandlast) - if( diff(i)>0 ) - bspectrum = bitset(bspectrum,i); - end -end diff --git a/modules/audio_processing/aecm/main/matlab/matlab/hisser2.m b/modules/audio_processing/aecm/main/matlab/matlab/hisser2.m deleted file mode 100644 index 5a414f9da..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/hisser2.m +++ /dev/null @@ -1,21 +0,0 @@ -function bcount=hisser2(bs,bsr,bandfirst,bandlast) -% function bcount=hisser(bspectrum,bandfirst,bandlast) -% histogram for the binary spectra -% bcount= array of bit counts -% bs=binary spectrum (one int32 number each) -% bsr=reference binary spectra (one int32 number each) -% blockSize = histogram over blocksize blocks -% bandfirst = first band considered -% bandlast = last band considered - -% weight all delays equally -maxDelay = length(bsr); - -% compute counts (two methods; the first works better and is operational) -bcount=zeros(maxDelay,1); -for(i=1:maxDelay) - % the delay should have low count for low-near&high-far and high-near&low-far - bcount(i)= sum(bitget(bitxor(bs,bsr(i)),bandfirst:bandlast)); - % the delay should have low count for low-near&high-far (works less well) -% bcount(i)= sum(bitget(bitand(bsr(i),bitxor(bs,bsr(i))),bandfirst:bandlast)); -end diff --git a/modules/audio_processing/aecm/main/matlab/matlab/mainProgram.m b/modules/audio_processing/aecm/main/matlab/matlab/mainProgram.m deleted file mode 100644 index eeb2aaa79..000000000 --- a/modules/audio_processing/aecm/main/matlab/matlab/mainProgram.m +++ /dev/null @@ -1,283 +0,0 @@ -useHTC = 1; % Set this if you want to run a single file and set file names below. Otherwise use simEnvironment to run from several scenarios in a row -delayCompensation_flag = 0; % Set this flag to one if you want to turn on the delay compensation/enhancement -global FARENDFFT; -global NEARENDFFT; -global F; - -if useHTC -% fid=fopen('./htcTouchHd/nb/aecFar.pcm'); xFar=fread(fid,'short'); fclose(fid); -% fid=fopen('./htcTouchHd/nb/aecNear.pcm'); yNear=fread(fid,'short'); fclose(fid); -% fid=fopen('./samsungBlackjack/nb/aecFar.pcm'); xFar=fread(fid,'short'); fclose(fid); -% fid=fopen('./samsungBlackjack/nb/aecNear.pcm'); yNear=fread(fid,'short'); fclose(fid); -% fid=fopen('aecFarPoor.pcm'); xFar=fread(fid,'short'); fclose(fid); -% fid=fopen('aecNearPoor.pcm'); yNear=fread(fid,'short'); fclose(fid); -% fid=fopen('out_aes.pcm'); outAES=fread(fid,'short'); fclose(fid); - fid=fopen('aecFar4.pcm'); xFar=fread(fid,'short'); fclose(fid); - fid=fopen('aecNear4.pcm'); yNear=fread(fid,'short'); fclose(fid); - yNearSpeech = zeros(size(xFar)); - fs = 8000; - frameSize = 64; -% frameSize = 128; - fs = 16000; -% frameSize = 256; -%F = load('fftValues.txt'); -%FARENDFFT = F(:,1:33); -%NEARENDFFT = F(:,34:66); - -else - loadFileFar = [speakerType, '_s_',scenario,'_far_b.wav']; - [xFar,fs,nbits] = wavread(loadFileFar); - xFar = xFar*2^(nbits-1); - loadFileNear = [speakerType, '_s_',scenario,'_near_b.wav']; - [yNear,fs,nbits] = wavread(loadFileNear); - yNear = yNear*2^(nbits-1); - loadFileNearSpeech = [speakerType, '_s_',scenario,'_nearSpeech_b.wav']; - [yNearSpeech,fs,nbits] = wavread(loadFileNearSpeech); - yNearSpeech = yNearSpeech*2^(nbits-1); - frameSize = 256; -end - -dtRegions = []; - -% General settings for the AECM -setupStruct = struct(... - 'stepSize_flag', 1,... % This flag turns on the step size calculation. If turned off, mu = 0.25. - 'supGain_flag', 0,... % This flag turns on the suppression gain calculation. If turned off, gam = 1. - 'channelUpdate_flag', 0,... % This flag turns on the channel update. If turned off, H is updated for convLength and then kept constant. - 'nlp_flag', 0,... % Turn on/off NLP - 'withVAD_flag', 0,... % Turn on/off NLP - 'useSubBand', 0,... % Set to 1 if to use subBands - 'useDelayEstimation', 1,... % Set to 1 if to use delay estimation - 'support', frameSize,... % # of samples per frame - 'samplingfreq',fs,... % Sampling frequency - 'oversampling', 2,... % Overlap between blocks/frames - 'updatel', 0,... % # of samples between blocks - 'hsupport1', 0,... % # of bins in frequency domain - 'factor', 0,... % synthesis window amplification - 'tlength', 0,... % # of samples of entire file - 'updateno', 0,... % # of updates - 'nb', 1,... % # of blocks - 'currentBlock', 0,... % - 'win', zeros(frameSize,1),...% Window to apply for fft and synthesis - 'avtime', 1,... % Time (in sec.) to perform averaging - 'estLen', 0,... % Averaging in # of blocks - 'A_GAIN', 10.0,... % - 'suppress_overdrive', 1.0,... % overdrive factor for suppression 1.4 is good - 'gamma_echo', 1.0,... % same as suppress_overdrive but at different place - 'de_echo_bound', 0.0,... % - 'nl_alpha', 0.4,... % memory; seems not very critical - 'nlSeverity', 0.2,... % nonlinearity severity: 0 does nothing; 1 suppresses all - 'numInBand', [],... % # of frequency bins in resp. subBand - 'centerFreq', [],... % Center frequency of resp. subBand - 'dtRegions', dtRegions,... % Regions where we have DT - 'subBandLength', frameSize/2);%All bins - %'subBandLength', 11); %Something's wrong when subBandLength even - %'nl_alpha', 0.8,... % memory; seems not very critical - -delayStruct = struct(... - 'bandfirst', 8,... - 'bandlast', 25,... - 'smlength', 600,... - 'maxDelay', 0.4,... - 'oneGoodEstimate', 0,... - 'delayAdjust', 0,... - 'maxDelayb', 0); -% More parameters in delayStruct are constructed in "updateSettings" below - -% Make struct settings -[setupStruct, delayStruct] = updateSettings(yNear, xFar, setupStruct, delayStruct); -setupStruct.numInBand = ones(setupStruct.hsupport1,1); - -Q = 1; % Time diversity in channel -% General settings for the step size calculation -muStruct = struct(... - 'countInInterval', 0,... - 'countOutHighInterval', 0,... - 'countOutLowInterval', 0,... - 'minInInterval', 50,... - 'minOutHighInterval', 10,... - 'minOutLowInterval', 10,... - 'maxOutLowInterval', 50); -% General settings for the AECM -aecmStruct = struct(... - 'plotIt', 0,... % Set to 0 to turn off plotting - 'useSubBand', 0,... - 'bandFactor', 1,... - 'H', zeros(setupStruct.subBandLength+1,Q),... - 'HStored', zeros(setupStruct.subBandLength+1,Q),... - 'X', zeros(setupStruct.subBandLength+1,Q),... - 'energyThres', 0.28,... - 'energyThresMSE', 0.4,... - 'energyMin', inf,... - 'energyMax', -inf,... - 'energyLevel', 0,... - 'energyLevelMSE', 0,... - 'convLength', 100,... - 'gammaLog', ones(setupStruct.updateno,1),... - 'muLog', ones(setupStruct.updateno,1),... - 'enerFar', zeros(setupStruct.updateno,1),... - 'enerNear', zeros(setupStruct.updateno,1),... - 'enerEcho', zeros(setupStruct.updateno,1),... - 'enerEchoStored', zeros(setupStruct.updateno,1),... - 'enerOut', zeros(setupStruct.updateno,1),... - 'runningfmean', 0,... - 'muStruct', muStruct,... - 'varMean', 0,... - 'countMseH', 0,... - 'mseHThreshold', 1.1,... - 'mseHStoredOld', inf,... - 'mseHLatestOld', inf,... - 'delayLatestS', zeros(1,51),... - 'feedbackDelay', 0,... - 'feedbackDelayUpdate', 0,... - 'cntIn', 0,... - 'cntOut', 0,... - 'FAR_ENERGY_MIN', 1,... - 'ENERGY_DEV_OFFSET', 0.5,... - 'ENERGY_DEV_TOL', 1.5,... - 'MU_MIN', -16,... - 'MU_MAX', -2,... - 'newDelayCurve', 0); - -% Adjust speech signals -xFar = [zeros(setupStruct.hsupport1-1,1);xFar(1:setupStruct.tlength)]; -yNear = [zeros(setupStruct.hsupport1-1,1);yNear(1:setupStruct.tlength)]; -yNearSpeech = [zeros(setupStruct.hsupport1-1,1);yNearSpeech(1:setupStruct.tlength)]; -xFar = xFar(1:setupStruct.tlength); -yNear = yNear(1:setupStruct.tlength); - -% Set figure settings -if aecmStruct.plotIt - figure(13) - set(gcf,'doublebuffer','on') -end -%%%%%%%%%% -% Here starts the algorithm -% Dividing into frames and then estimating the near end speech -%%%%%%%%%% -fTheFarEnd = complex(zeros(setupStruct.hsupport1,1)); -afTheFarEnd = zeros(setupStruct.hsupport1,setupStruct.updateno+1); -fFar = zeros(setupStruct.hsupport1,setupStruct.updateno+1); -fmicrophone = complex(zeros(setupStruct.hsupport1,1)); -afmicrophone = zeros(setupStruct.hsupport1,setupStruct.updateno+1); -fNear = zeros(setupStruct.hsupport1,setupStruct.updateno+1); -femicrophone = complex(zeros(setupStruct.hsupport1,1)); -emicrophone = zeros(setupStruct.tlength,1); - -if (setupStruct.useDelayEstimation == 2) - delSamples = [1641 1895 2032 1895 2311 2000 2350 2222 NaN 2332 2330 2290 2401 2415 NaN 2393 2305 2381 2398]; - delBlocks = round(delSamples/setupStruct.updatel); - delStarts = floor([25138 46844 105991 169901 195739 218536 241803 333905 347703 362660 373753 745135 765887 788078 806257 823835 842443 860139 881869]/setupStruct.updatel); -else - delStarts = []; -end - -for i=1:setupStruct.updateno - setupStruct.currentBlock = i; - - sb = (i-1)*setupStruct.updatel + 1; - se = sb + setupStruct.support - 1; - - %%%%%%% - % Analysis FFTs - %%%%%%% - % Far end signal - temp = fft(setupStruct.win .* xFar(sb:se))/frameSize; - fTheFarEnd = temp(1:setupStruct.hsupport1); - afTheFarEnd(:,i) = abs(fTheFarEnd); - fFar(:,i) = fTheFarEnd; - % Near end signal - temp = fft(setupStruct.win .* yNear(sb:se))/frameSize;%,pause - fmicrophone = temp(1:setupStruct.hsupport1); - afmicrophone(:,i) = abs(fmicrophone); - fNear(:,i) = fmicrophone; - %abs(fmicrophone),pause - % The true near end speaker (if we have such info) - temp = fft(setupStruct.win .* yNearSpeech(sb:se)); - aftrueSpeech = abs(temp(1:setupStruct.hsupport1)); - - if(i == 1000) - %break; - end - - % Perform delay estimation - if (setupStruct.useDelayEstimation == 1) - % Delay Estimation - delayStruct = align(fTheFarEnd, fmicrophone, delayStruct, i); - %delayStruct.delay(i) = 39;%19; - idel = max(i - delayStruct.delay(i) + 1,1); - - if delayCompensation_flag - % If we have a new delay estimate from Bastiaan's alg. update the offset - if (delayStruct.delay(i) ~= delayStruct.delay(max(1,i-1))) - delayStruct.delayAdjust = delayStruct.delayAdjust + delayStruct.delay(i) - delayStruct.delay(i-1); - end - % Store the compensated delay - delayStruct.delayNew(i) = delayStruct.delay(i) - delayStruct.delayAdjust; - if (delayStruct.delayNew(i) < 1) - % Something's wrong - pause,break - end - % Compensate with the offset estimate - idel = idel + delayStruct.delayAdjust; - end - if 0%aecmStruct.plotIt - figure(1) - plot(1:i,delayStruct.delay(1:i),'k:',1:i,delayStruct.delayNew(1:i),'k--','LineWidth',2),drawnow - end - elseif (setupStruct.useDelayEstimation == 2) - % Use "manual delay" - delIndex = find(delStarts=xk(kk))); - else - numInBand(kk) = length(intersect(find(xh>=xk(kk)),find(xh=2 - if ischar(whichbar) - type=2; %we are initializing - name=whichbar; - elseif isnumeric(whichbar) - type=1; %we are updating, given a handle - f=whichbar; - else - error(['Input arguments of type ' class(whichbar) ' not valid.']) - end -elseif nargin==1 - f = findobj(allchild(0),'flat','Tag','TMWWaitbar'); - - if isempty(f) - type=2; - name='Waitbar'; - else - type=1; - f=f(1); - end -else - error('Input arguments not valid.'); -end - -x = max(0,min(100*x,100)); - -switch type - case 1, % waitbar(x) update - p = findobj(f,'Type','patch'); - l = findobj(f,'Type','line'); - if isempty(f) | isempty(p) | isempty(l), - error('Couldn''t find waitbar handles.'); - end - xpatch = get(p,'XData'); - xpatch = [0 x x 0]; - set(p,'XData',xpatch) - xline = get(l,'XData'); - set(l,'XData',xline); - - if nargin>2, - % Update waitbar title: - hAxes = findobj(f,'type','axes'); - hTitle = get(hAxes,'title'); - set(hTitle,'string',varargin{1}); - end - - case 2, % waitbar(x,name) initialize - vertMargin = 0; - if nargin > 2, - % we have optional arguments: property-value pairs - if rem (nargin, 2 ) ~= 0 - error( 'Optional initialization arguments must be passed in pairs' ); - end - end - - oldRootUnits = get(0,'Units'); - - set(0, 'Units', 'points'); - screenSize = get(0,'ScreenSize'); - - axFontSize=get(0,'FactoryAxesFontSize'); - - pointsPerPixel = 72/get(0,'ScreenPixelsPerInch'); - - width = 360 * pointsPerPixel; - height = 75 * pointsPerPixel; - pos = [screenSize(3)/2-width/2 screenSize(4)/2-height/2 width height]; - -%pos= [501.75 589.5 393.75 52.5]; - f = figure(... - 'Units', 'points', ... - 'BusyAction', 'queue', ... - 'Position', pos, ... - 'Resize','on', ... - 'CreateFcn','', ... - 'NumberTitle','off', ... - 'IntegerHandle','off', ... - 'MenuBar', 'none', ... - 'Tag','TMWWaitbar',... - 'Interruptible', 'off', ... - 'Visible','on'); - - %%%%%%%%%%%%%%%%%%%%% - % set figure properties as passed to the fcn - % pay special attention to the 'cancel' request - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - if nargin > 2, - propList = varargin(1:2:end); - valueList = varargin(2:2:end); - cancelBtnCreated = 0; - for ii = 1:length( propList ) - try - if strcmp(lower(propList{ii}), 'createcancelbtn' ) & ~cancelBtnCreated - cancelBtnHeight = 23 * pointsPerPixel; - cancelBtnWidth = 60 * pointsPerPixel; - newPos = pos; - vertMargin = vertMargin + cancelBtnHeight; - newPos(4) = newPos(4)+vertMargin; - callbackFcn = [valueList{ii}]; - set( f, 'Position', newPos, 'CloseRequestFcn', callbackFcn ); - cancelButt = uicontrol('Parent',f, ... - 'Units','points', ... - 'Callback',callbackFcn, ... - 'ButtonDownFcn', callbackFcn, ... - 'Enable','on', ... - 'Interruptible','off', ... - 'Position', [pos(3)-cancelBtnWidth*1.4, 7, ... - cancelBtnWidth, cancelBtnHeight], ... - 'String','Cancel', ... - 'Tag','TMWWaitbarCancelButton'); - cancelBtnCreated = 1; - else - % simply set the prop/value pair of the figure - set( f, propList{ii}, valueList{ii}); - end - catch - disp ( ['Warning: could not set property ''' propList{ii} ''' with value ''' num2str(valueList{ii}) '''' ] ); - end - end - end - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - - colormap([]); - - axNorm=[.05 .3 .9 .2]; - % axNorm=[1 1 1 1]; - axPos=axNorm.*[pos(3:4),pos(3:4)] + [0 vertMargin 0 0]; - - h = axes('XLim',[0 100],... - 'YLim',[0 1],... - 'Box','on', ... - 'Units','Points',... - 'FontSize', axFontSize,... - 'Position',axPos,... - 'XTickMode','manual',... - 'YTickMode','manual',... - 'XTick',[],... - 'YTick',[],... - 'XTickLabelMode','manual',... - 'XTickLabel',[],... - 'YTickLabelMode','manual',... - 'YTickLabel',[]); - - tHandle=title(name); - tHandle=get(h,'title'); - oldTitleUnits=get(tHandle,'Units'); - set(tHandle,... - 'Units', 'points',... - 'String', name); - - tExtent=get(tHandle,'Extent'); - set(tHandle,'Units',oldTitleUnits); - - titleHeight=tExtent(4)+axPos(2)+axPos(4)+5; - if titleHeight>pos(4) - pos(4)=titleHeight; - pos(2)=screenSize(4)/2-pos(4)/2; - figPosDirty=logical(1); - else - figPosDirty=logical(0); - end - - if tExtent(3)>pos(3)*1.10; - pos(3)=min(tExtent(3)*1.10,screenSize(3)); - pos(1)=screenSize(3)/2-pos(3)/2; - - axPos([1,3])=axNorm([1,3])*pos(3); - set(h,'Position',axPos); - - figPosDirty=logical(1); - end - - if figPosDirty - set(f,'Position',pos); - end - - xpatch = [0 x x 0]; - ypatch = [0 0 1 1]; - xline = [100 0 0 100 100]; - yline = [0 0 1 1 0]; - - p = patch(xpatch,ypatch,'r','EdgeColor','r','EraseMode','none'); - l = line(xline,yline,'EraseMode','none'); - set(l,'Color',get(gca,'XColor')); - - - set(f,'HandleVisibility','callback','visible','on', 'resize','off'); - - set(0, 'Units', oldRootUnits); -end % case -drawnow; - -if nargout==1, - fout = f; -end diff --git a/modules/audio_processing/aecm/main/source/Android.mk b/modules/audio_processing/aecm/main/source/Android.mk deleted file mode 100644 index ec18258d2..000000000 --- a/modules/audio_processing/aecm/main/source/Android.mk +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_aecm -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := echo_control_mobile.c \ - aecm_core.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../utility \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_processing/aecm/main/source/aecm.gyp b/modules/audio_processing/aecm/main/source/aecm.gyp deleted file mode 100644 index a535d2b29..000000000 --- a/modules/audio_processing/aecm/main/source/aecm.gyp +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'aecm', - 'type': '<(library)', - 'dependencies': [ - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../utility/util.gyp:apm_util' - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/echo_control_mobile.h', - 'echo_control_mobile.c', - 'aecm_core.c', - 'aecm_core.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/aecm/main/source/aecm_core.c b/modules/audio_processing/aecm/main/source/aecm_core.c deleted file mode 100644 index 032aa4c00..000000000 --- a/modules/audio_processing/aecm/main/source/aecm_core.c +++ /dev/null @@ -1,2452 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "aecm_core.h" -#include "ring_buffer.h" -#include "echo_control_mobile.h" -#include "typedefs.h" - -#ifdef ARM_WINM_LOG -#include -#include -#endif - -// BANDLAST - BANDFIRST must be < 32 -#define BANDFIRST 12 // Only bit BANDFIRST through bit BANDLAST are processed -#define BANDLAST 43 - -#ifdef ARM_WINM -#define WebRtcSpl_AddSatW32(a,b) _AddSatInt(a,b) -#define WebRtcSpl_SubSatW32(a,b) _SubSatInt(a,b) -#endif -// 16 instructions on most risc machines for 32-bit bitcount ! - -#ifdef AEC_DEBUG -FILE *dfile; -FILE *testfile; -#endif - -#ifdef AECM_SHORT - -// Square root of Hanning window in Q14 -static const WebRtc_Word16 kSqrtHanning[] = -{ - 0, 804, 1606, 2404, 3196, 3981, 4756, 5520, - 6270, 7005, 7723, 8423, 9102, 9760, 10394, 11003, - 11585, 12140, 12665, 13160, 13623, 14053, 14449, 14811, - 15137, 15426, 15679, 15893, 16069, 16207, 16305, 16364, - 16384 -}; - -#else - -// Square root of Hanning window in Q14 -static const WebRtc_Word16 kSqrtHanning[] = {0, 399, 798, 1196, 1594, 1990, 2386, 2780, 3172, - 3562, 3951, 4337, 4720, 5101, 5478, 5853, 6224, 6591, 6954, 7313, 7668, 8019, 8364, - 8705, 9040, 9370, 9695, 10013, 10326, 10633, 10933, 11227, 11514, 11795, 12068, 12335, - 12594, 12845, 13089, 13325, 13553, 13773, 13985, 14189, 14384, 14571, 14749, 14918, - 15079, 15231, 15373, 15506, 15631, 15746, 15851, 15947, 16034, 16111, 16179, 16237, - 16286, 16325, 16354, 16373, 16384}; - -#endif - -//Q15 alpha = 0.99439986968132 const Factor for magnitude approximation -static const WebRtc_UWord16 kAlpha1 = 32584; -//Q15 beta = 0.12967166976970 const Factor for magnitude approximation -static const WebRtc_UWord16 kBeta1 = 4249; -//Q15 alpha = 0.94234827210087 const Factor for magnitude approximation -static const WebRtc_UWord16 kAlpha2 = 30879; -//Q15 beta = 0.33787806009150 const Factor for magnitude approximation -static const WebRtc_UWord16 kBeta2 = 11072; -//Q15 alpha = 0.82247698684306 const Factor for magnitude approximation -static const WebRtc_UWord16 kAlpha3 = 26951; -//Q15 beta = 0.57762063060713 const Factor for magnitude approximation -static const WebRtc_UWord16 kBeta3 = 18927; - -#ifdef ARM_WINM_LOG -HANDLE logFile = NULL; -#endif - -static void WebRtcAecm_ComfortNoise(AecmCore_t* const aecm, const WebRtc_UWord16 * const dfa, - WebRtc_Word16 * const outReal, - WebRtc_Word16 * const outImag, - const WebRtc_Word16 * const lambda); - -static __inline WebRtc_UWord32 WebRtcAecm_SetBit(WebRtc_UWord32 in, WebRtc_Word32 pos) -{ - WebRtc_UWord32 mask, out; - - mask = WEBRTC_SPL_SHIFT_W32(1, pos); - out = (in | mask); - - return out; -} - -// WebRtcAecm_Hisser(...) -// -// This function compares the binary vector specvec with all rows of the binary matrix specmat -// and counts per row the number of times they have the same value. -// Input: -// - specvec : binary "vector" that is stored in a long -// - specmat : binary "matrix" that is stored as a vector of long -// Output: -// - bcount : "Vector" stored as a long, containing for each row the number of times -// the matrix row and the input vector have the same value -// -// -void WebRtcAecm_Hisser(const WebRtc_UWord32 specvec, const WebRtc_UWord32 * const specmat, - WebRtc_UWord32 * const bcount) -{ - int n, sum; - WebRtc_UWord32 a, b; - register WebRtc_UWord32 tmp; - - a = specvec; - // compare binary vector specvec with all rows of the binary matrix specmat - for (n = 0; n < MAX_DELAY; n++) - { - sum = 0; - b = specmat[n]; - a = (specvec ^ b); - // Returns bit counts in tmp - tmp = a - ((a >> 1) & 033333333333) - ((a >> 2) & 011111111111); - tmp = ((tmp + (tmp >> 3)) & 030707070707); - tmp = (tmp + (tmp >> 6)); - tmp = (tmp + (tmp >> 12) + (tmp >> 24)) & 077; - - bcount[n] = tmp; - } -} - -// WebRtcAecm_BSpectrum(...) -// -// Computes the binary spectrum by comparing the input spectrum with a threshold spectrum. -// -// Input: -// - spectrum : Spectrum of which the binary spectrum should be calculated. -// - thresvec : Threshold spectrum with which the input spectrum is compared. -// Return: -// - out : Binary spectrum -// -WebRtc_UWord32 WebRtcAecm_BSpectrum(const WebRtc_UWord16 * const spectrum, - const WebRtc_UWord16 * const thresvec) -{ - int k; - WebRtc_UWord32 out; - - out = 0; - for (k = BANDFIRST; k <= BANDLAST; k++) - { - if (spectrum[k] > thresvec[k]) - { - out = WebRtcAecm_SetBit(out, k - BANDFIRST); - } - } - - return out; -} - -// WebRtcAecm_MedianEstimator(...) -// -// Calculates the median recursively. -// -// Input: -// - newVal : new additional value -// - medianVec : vector with current medians -// - factor : factor for smoothing -// -// Output: -// - medianVec : vector with updated median -// -int WebRtcAecm_MedianEstimator(const WebRtc_UWord16 newVal, WebRtc_UWord16 * const medianVec, - const int factor) -{ - WebRtc_Word32 median; - WebRtc_Word32 diff; - - median = (WebRtc_Word32)medianVec[0]; - - //median = median + ((newVal-median)>>factor); - diff = (WebRtc_Word32)newVal - median; - diff = WEBRTC_SPL_SHIFT_W32(diff, -factor); - median = median + diff; - - medianVec[0] = (WebRtc_UWord16)median; - - return 0; -} - -int WebRtcAecm_CreateCore(AecmCore_t **aecmInst) -{ - AecmCore_t *aecm = malloc(sizeof(AecmCore_t)); - *aecmInst = aecm; - if (aecm == NULL) - { - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecm->farFrameBuf, FRAME_LEN + PART_LEN) == -1) - { - WebRtcAecm_FreeCore(aecm); - aecm = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecm->nearNoisyFrameBuf, FRAME_LEN + PART_LEN) == -1) - { - WebRtcAecm_FreeCore(aecm); - aecm = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecm->nearCleanFrameBuf, FRAME_LEN + PART_LEN) == -1) - { - WebRtcAecm_FreeCore(aecm); - aecm = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecm->outFrameBuf, FRAME_LEN + PART_LEN) == -1) - { - WebRtcAecm_FreeCore(aecm); - aecm = NULL; - return -1; - } - - return 0; -} - -// WebRtcAecm_InitCore(...) -// -// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...) -// Input: -// - aecm : Pointer to the Echo Suppression instance -// - samplingFreq : Sampling Frequency -// -// Output: -// - aecm : Initialized instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_InitCore(AecmCore_t * const aecm, int samplingFreq) -{ - int retVal = 0; - WebRtc_Word16 i; - WebRtc_Word16 tmp16; - - if (samplingFreq != 8000 && samplingFreq != 16000) - { - samplingFreq = 8000; - retVal = -1; - } - // sanity check of sampling frequency - aecm->mult = (WebRtc_Word16)samplingFreq / 8000; - - aecm->farBufWritePos = 0; - aecm->farBufReadPos = 0; - aecm->knownDelay = 0; - aecm->lastKnownDelay = 0; - - WebRtcApm_InitBuffer(aecm->farFrameBuf); - WebRtcApm_InitBuffer(aecm->nearNoisyFrameBuf); - WebRtcApm_InitBuffer(aecm->nearCleanFrameBuf); - WebRtcApm_InitBuffer(aecm->outFrameBuf); - - memset(aecm->xBuf, 0, sizeof(aecm->xBuf)); - memset(aecm->dBufClean, 0, sizeof(aecm->dBufClean)); - memset(aecm->dBufNoisy, 0, sizeof(aecm->dBufNoisy)); - memset(aecm->outBuf, 0, sizeof(WebRtc_Word16) * PART_LEN); - - aecm->seed = 666; - aecm->totCount = 0; - - memset(aecm->xfaHistory, 0, sizeof(WebRtc_UWord16) * (PART_LEN1) * MAX_DELAY); - - aecm->delHistoryPos = MAX_DELAY; - - memset(aecm->medianYlogspec, 0, sizeof(WebRtc_UWord16) * PART_LEN1); - memset(aecm->medianXlogspec, 0, sizeof(WebRtc_UWord16) * PART_LEN1); - memset(aecm->medianBCount, 0, sizeof(WebRtc_UWord16) * MAX_DELAY); - memset(aecm->bxHistory, 0, sizeof(aecm->bxHistory)); - - // Initialize to reasonable values - aecm->currentDelay = 8; - aecm->previousDelay = 8; - aecm->delayAdjust = 0; - - aecm->nlpFlag = 1; - aecm->fixedDelay = -1; - - memset(aecm->xfaQDomainBuf, 0, sizeof(WebRtc_Word16) * MAX_DELAY); - aecm->dfaCleanQDomain = 0; - aecm->dfaCleanQDomainOld = 0; - aecm->dfaNoisyQDomain = 0; - aecm->dfaNoisyQDomainOld = 0; - - memset(aecm->nearLogEnergy, 0, sizeof(WebRtc_Word16) * MAX_BUF_LEN); - memset(aecm->farLogEnergy, 0, sizeof(WebRtc_Word16) * MAX_BUF_LEN); - memset(aecm->echoAdaptLogEnergy, 0, sizeof(WebRtc_Word16) * MAX_BUF_LEN); - memset(aecm->echoStoredLogEnergy, 0, sizeof(WebRtc_Word16) * MAX_BUF_LEN); - - memset(aecm->channelAdapt16, 0, sizeof(WebRtc_Word16) * PART_LEN1); - memset(aecm->channelAdapt32, 0, sizeof(WebRtc_Word32) * PART_LEN1); - memset(aecm->channelStored, 0, sizeof(WebRtc_Word16) * PART_LEN1); - memset(aecm->echoFilt, 0, sizeof(WebRtc_Word32) * PART_LEN1); - memset(aecm->nearFilt, 0, sizeof(WebRtc_Word16) * PART_LEN1); - aecm->noiseEstCtr = 0; - - aecm->cngMode = AecmTrue; - - // Increase the noise Q domain with increasing frequency, to correspond to the - // expected energy levels. - // Also shape the initial noise level with this consideration. -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (i = 0; i < PART_LEN1; i++) - { - if (i < PART_LEN1 >> 2) - { - aecm->noiseEstQDomain[i] = 10; - tmp16 = PART_LEN1 - i; - aecm->noiseEst[i] = (tmp16 * tmp16) << 4; - } else if (i < PART_LEN1 >> 1) - { - aecm->noiseEstQDomain[i] = 11; - tmp16 = PART_LEN1 - i; - aecm->noiseEst[i] = ((tmp16 * tmp16) << 4) << 1; - } else - { - aecm->noiseEstQDomain[i] = 12; - aecm->noiseEst[i] = aecm->noiseEst[(PART_LEN1 >> 1) - 1] << 1; - } - } -#else - for (i = 0; i < PART_LEN1 >> 2; i++) - { - aecm->noiseEstQDomain[i] = 10; - tmp16 = PART_LEN1 - i; - aecm->noiseEst[i] = (tmp16 * tmp16) << 4; - } - for (; i < PART_LEN1 >> 1; i++) - { - aecm->noiseEstQDomain[i] = 11; - tmp16 = PART_LEN1 - i; - aecm->noiseEst[i] = ((tmp16 * tmp16) << 4) << 1; - } - for (; i < PART_LEN1; i++) - { - aecm->noiseEstQDomain[i] = 12; - aecm->noiseEst[i] = aecm->noiseEst[(PART_LEN1 >> 1) - 1] << 1; - } -#endif - - aecm->mseAdaptOld = 1000; - aecm->mseStoredOld = 1000; - aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX; - - aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX; - aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN; - aecm->farEnergyMaxMin = 0; - aecm->farEnergyVAD = 0; - aecm->farEnergyMSE = 0; - aecm->currentVADValue = 0; - aecm->vadUpdateCount = 0; - - aecm->delayCount = 0; - aecm->newDelayCorrData = 0; - aecm->lastDelayUpdateCount = 0; - memset(aecm->delayCorrelation, 0, sizeof(WebRtc_Word16) * ((CORR_MAX << 1) + 1)); - - aecm->startupState = 0; - aecm->mseChannelCount = 0; - aecm->supGain = SUPGAIN_DEFAULT; - aecm->supGainOld = SUPGAIN_DEFAULT; - aecm->delayOffsetFlag = 0; - - memset(aecm->delayHistogram, 0, sizeof(aecm->delayHistogram)); - aecm->delayVadCount = 0; - aecm->maxDelayHistIdx = 0; - aecm->lastMinPos = 0; - - aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - - return 0; -} - -int WebRtcAecm_Control(AecmCore_t *aecm, int delay, int nlpFlag, int delayOffsetFlag) -{ - aecm->nlpFlag = nlpFlag; - aecm->fixedDelay = delay; - aecm->delayOffsetFlag = delayOffsetFlag; - - return 0; -} - -// WebRtcAecm_GetNewDelPos(...) -// -// Moves the pointer to the next entry. Returns to zero if max position reached. -// -// Input: -// - aecm : Pointer to the AECM instance -// Return: -// - pos : New position in the history. -// -// -WebRtc_Word16 WebRtcAecm_GetNewDelPos(AecmCore_t * const aecm) -{ - WebRtc_Word16 pos; - - pos = aecm->delHistoryPos; - pos++; - if (pos >= MAX_DELAY) - { - pos = 0; - } - aecm->delHistoryPos = pos; - - return pos; -} - -// WebRtcAecm_EstimateDelay(...) -// -// Estimate the delay of the echo signal. -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farSpec : Delayed farend magnitude spectrum -// - nearSpec : Nearend magnitude spectrum -// - stages : Q-domain of xxFIX and yyFIX (without dynamic Q-domain) -// - xfaQ : normalization factor, i.e., Q-domain before FFT -// Return: -// - delay : Estimated delay -// -WebRtc_Word16 WebRtcAecm_EstimateDelay(AecmCore_t * const aecm, - const WebRtc_UWord16 * const farSpec, - const WebRtc_UWord16 * const nearSpec, - const WebRtc_Word16 xfaQ) -{ - WebRtc_UWord32 bxspectrum, byspectrum; - WebRtc_UWord32 bcount[MAX_DELAY]; - - int i, res; - - WebRtc_UWord16 xmean[PART_LEN1], ymean[PART_LEN1]; - WebRtc_UWord16 dtmp1; - WebRtc_Word16 fcount[MAX_DELAY]; - - //WebRtc_Word16 res; - WebRtc_Word16 histpos; - WebRtc_Word16 maxHistLvl; - WebRtc_UWord16 *state; - WebRtc_Word16 minpos = -1; - - enum - { - kVadCountThreshold = 25 - }; - enum - { - kMaxHistogram = 600 - }; - - histpos = WebRtcAecm_GetNewDelPos(aecm); - - for (i = 0; i < PART_LEN1; i++) - { - aecm->xfaHistory[i][histpos] = farSpec[i]; - - state = &(aecm->medianXlogspec[i]); - res = WebRtcAecm_MedianEstimator(farSpec[i], state, 6); - - state = &(aecm->medianYlogspec[i]); - res = WebRtcAecm_MedianEstimator(nearSpec[i], state, 6); - - // Mean: - // FLOAT: - // ymean = dtmp2/MAX_DELAY - // - // FIX: - // input: dtmp2FIX in Q0 - // output: ymeanFIX in Q8 - // 20 = 1/MAX_DELAY in Q13 = 1/MAX_DELAY * 2^13 - xmean[i] = (aecm->medianXlogspec[i]); - ymean[i] = (aecm->medianYlogspec[i]); - - } - // Update Q-domain buffer - aecm->xfaQDomainBuf[histpos] = xfaQ; - - // Get binary spectra - // FLOAT: - // bxspectrum = bspectrum(xlogspec, xmean); - // - // FIX: - // input: xlogspecFIX,ylogspecFIX in Q8 - // xmeanFIX, ymeanFIX in Q8 - // output: unsigned long bxspectrum, byspectrum in Q0 - bxspectrum = WebRtcAecm_BSpectrum(farSpec, xmean); - byspectrum = WebRtcAecm_BSpectrum(nearSpec, ymean); - - // Shift binary spectrum history - memmove(&(aecm->bxHistory[1]), &(aecm->bxHistory[0]), - (MAX_DELAY - 1) * sizeof(WebRtc_UWord32)); - - aecm->bxHistory[0] = bxspectrum; - - // Compare with delayed spectra - WebRtcAecm_Hisser(byspectrum, aecm->bxHistory, bcount); - - for (i = 0; i < MAX_DELAY; i++) - { - // Update sum - // bcount is constrained to [0, 32], meaning we can smooth with a factor up to 2^11. - dtmp1 = (WebRtc_UWord16)bcount[i]; - dtmp1 = WEBRTC_SPL_LSHIFT_W16(dtmp1, 9); - state = &(aecm->medianBCount[i]); - res = WebRtcAecm_MedianEstimator(dtmp1, state, 9); - fcount[i] = (aecm->medianBCount[i]); - } - - // Find minimum - minpos = WebRtcSpl_MinIndexW16(fcount, MAX_DELAY); - - // If the farend has been active sufficiently long, begin accumulating a histogram - // of the minimum positions. Search for the maximum bin to determine the delay. - if (aecm->currentVADValue == 1) - { - if (aecm->delayVadCount >= kVadCountThreshold) - { - // Increment the histogram at the current minimum position. - if (aecm->delayHistogram[minpos] < kMaxHistogram) - { - aecm->delayHistogram[minpos] += 3; - } - -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - // Decrement the entire histogram. - for (i = 0; i < MAX_DELAY; i++) - { - if (aecm->delayHistogram[i] > 0) - { - aecm->delayHistogram[i]--; - } - } - - // Select the histogram index corresponding to the maximum bin as the delay. - maxHistLvl = 0; - aecm->maxDelayHistIdx = 0; - for (i = 0; i < MAX_DELAY; i++) - { - if (aecm->delayHistogram[i] > maxHistLvl) - { - maxHistLvl = aecm->delayHistogram[i]; - aecm->maxDelayHistIdx = i; - } - } -#else - maxHistLvl = 0; - aecm->maxDelayHistIdx = 0; - - for (i = 0; i < MAX_DELAY; i++) - { - WebRtc_Word16 tempVar = aecm->delayHistogram[i]; - - // Decrement the entire histogram. - if (tempVar > 0) - { - tempVar--; - aecm->delayHistogram[i] = tempVar; - - // Select the histogram index corresponding to the maximum bin as the delay. - if (tempVar > maxHistLvl) - { - maxHistLvl = tempVar; - aecm->maxDelayHistIdx = i; - } - } - } -#endif - } else - { - aecm->delayVadCount++; - } - } else - { - aecm->delayVadCount = 0; - } - - return aecm->maxDelayHistIdx; -} - -int WebRtcAecm_FreeCore(AecmCore_t *aecm) -{ - if (aecm == NULL) - { - return -1; - } - - WebRtcApm_FreeBuffer(aecm->farFrameBuf); - WebRtcApm_FreeBuffer(aecm->nearNoisyFrameBuf); - WebRtcApm_FreeBuffer(aecm->nearCleanFrameBuf); - WebRtcApm_FreeBuffer(aecm->outFrameBuf); - - free(aecm); - - return 0; -} - -void WebRtcAecm_ProcessFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const WebRtc_Word16 * const nearendNoisy, - const WebRtc_Word16 * const nearendClean, - WebRtc_Word16 * const out) -{ - WebRtc_Word16 farBlock[PART_LEN]; - WebRtc_Word16 nearNoisyBlock[PART_LEN]; - WebRtc_Word16 nearCleanBlock[PART_LEN]; - WebRtc_Word16 outBlock[PART_LEN]; - WebRtc_Word16 farFrame[FRAME_LEN]; - int size = 0; - - // Buffer the current frame. - // Fetch an older one corresponding to the delay. - WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN); - WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay); - - // Buffer the synchronized far and near frames, - // to pass the smaller blocks individually. - WebRtcApm_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN); - WebRtcApm_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN); - if (nearendClean != NULL) - { - WebRtcApm_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN); - } - - // Process as many blocks as possible. - while (WebRtcApm_get_buffer_size(aecm->farFrameBuf) >= PART_LEN) - { - WebRtcApm_ReadBuffer(aecm->farFrameBuf, farBlock, PART_LEN); - WebRtcApm_ReadBuffer(aecm->nearNoisyFrameBuf, nearNoisyBlock, PART_LEN); - if (nearendClean != NULL) - { - WebRtcApm_ReadBuffer(aecm->nearCleanFrameBuf, nearCleanBlock, PART_LEN); - WebRtcAecm_ProcessBlock(aecm, farBlock, nearNoisyBlock, nearCleanBlock, outBlock); - } else - { - WebRtcAecm_ProcessBlock(aecm, farBlock, nearNoisyBlock, NULL, outBlock); - } - - WebRtcApm_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN); - } - - // Stuff the out buffer if we have less than a frame to output. - // This should only happen for the first frame. - size = WebRtcApm_get_buffer_size(aecm->outFrameBuf); - if (size < FRAME_LEN) - { - WebRtcApm_StuffBuffer(aecm->outFrameBuf, FRAME_LEN - size); - } - - // Obtain an output frame. - WebRtcApm_ReadBuffer(aecm->outFrameBuf, out, FRAME_LEN); -} - -// WebRtcAecm_AsymFilt(...) -// -// Performs asymmetric filtering. -// -// Inputs: -// - filtOld : Previous filtered value. -// - inVal : New input value. -// - stepSizePos : Step size when we have a positive contribution. -// - stepSizeNeg : Step size when we have a negative contribution. -// -// Output: -// -// Return: - Filtered value. -// -WebRtc_Word16 WebRtcAecm_AsymFilt(const WebRtc_Word16 filtOld, const WebRtc_Word16 inVal, - const WebRtc_Word16 stepSizePos, - const WebRtc_Word16 stepSizeNeg) -{ - WebRtc_Word16 retVal; - - if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) - { - return inVal; - } - retVal = filtOld; - if (filtOld > inVal) - { - retVal -= WEBRTC_SPL_RSHIFT_W16(filtOld - inVal, stepSizeNeg); - } else - { - retVal += WEBRTC_SPL_RSHIFT_W16(inVal - filtOld, stepSizePos); - } - - return retVal; -} - -// WebRtcAecm_CalcEnergies(...) -// -// This function calculates the log of energies for nearend, farend and estimated -// echoes. There is also an update of energy decision levels, i.e. internl VAD. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param delayDiff [in] Delay position in farend buffer. -// @param nearEner [in] Near end energy for current block (Q[aecm->dfaQDomain]). -// @param echoEst [i/o] Estimated echo -// (Q[aecm->xfaQDomain[delayDiff]+RESOLUTION_CHANNEL16]). -// -void WebRtcAecm_CalcEnergies(AecmCore_t * const aecm, const WebRtc_Word16 delayDiff, - const WebRtc_UWord32 nearEner, WebRtc_Word32 * const echoEst) -{ - // Local variables - WebRtc_UWord32 tmpAdapt, tmpStored, tmpFar; - - int i; - - WebRtc_Word16 zeros, frac; - WebRtc_Word16 tmp16; - - // Get log of near end energy and store in buffer - - // Shift buffer - memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy, - sizeof(WebRtc_Word16) * (MAX_BUF_LEN - 1)); - - // Logarithm of integrated magnitude spectrum (nearEner) - if (nearEner) - { - zeros = WebRtcSpl_NormU32(nearEner); - frac - = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(nearEner, zeros) & 0x7FFFFFFF), 23); - // log2 in Q8 - aecm->nearLogEnergy[0] = WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - aecm->nearLogEnergy[0] -= WEBRTC_SPL_LSHIFT_W16(aecm->dfaNoisyQDomain, 8); - } else - { - aecm->nearLogEnergy[0] = 0; - } - aecm->nearLogEnergy[0] += WEBRTC_SPL_LSHIFT_W16(PART_LEN_SHIFT, 7); - // END: Get log of near end energy - - // Get energy for the delayed far end signal and estimated - // echo using both stored and adapted channels. - tmpAdapt = 0; - tmpStored = 0; - tmpFar = 0; - - for (i = 0; i < PART_LEN1; i++) - { - tmpFar += (WebRtc_UWord32)aecm->xfaHistory[i][delayDiff]; - // Get estimated echo energies for adaptive channel and stored channel - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - tmpFar += (WebRtc_UWord32)(aecm->xfaHistory[i][delayDiff]); - tmpAdapt += WEBRTC_SPL_UMUL_16_16(aecm->channelAdapt16[i], - aecm->xfaHistory[i][delayDiff]); - tmpStored += (WebRtc_UWord32)echoEst[i]; - } - // Shift buffers - memmove(aecm->farLogEnergy + 1, aecm->farLogEnergy, - sizeof(WebRtc_Word16) * (MAX_BUF_LEN - 1)); - memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy, - sizeof(WebRtc_Word16) * (MAX_BUF_LEN - 1)); - memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy, - sizeof(WebRtc_Word16) * (MAX_BUF_LEN - 1)); - - // Logarithm of delayed far end energy - if (tmpFar) - { - zeros = WebRtcSpl_NormU32(tmpFar); - frac = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpFar, zeros) - & 0x7FFFFFFF), 23); - // log2 in Q8 - aecm->farLogEnergy[0] = WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - aecm->farLogEnergy[0] -= WEBRTC_SPL_LSHIFT_W16(aecm->xfaQDomainBuf[delayDiff], 8); - } else - { - aecm->farLogEnergy[0] = 0; - } - aecm->farLogEnergy[0] += WEBRTC_SPL_LSHIFT_W16(PART_LEN_SHIFT, 7); - - // Logarithm of estimated echo energy through adapted channel - if (tmpAdapt) - { - zeros = WebRtcSpl_NormU32(tmpAdapt); - frac = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpAdapt, zeros) - & 0x7FFFFFFF), 23); - //log2 in Q8 - aecm->echoAdaptLogEnergy[0] = WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - aecm->echoAdaptLogEnergy[0] - -= WEBRTC_SPL_LSHIFT_W16(RESOLUTION_CHANNEL16 + aecm->xfaQDomainBuf[delayDiff], 8); - } else - { - aecm->echoAdaptLogEnergy[0] = 0; - } - aecm->echoAdaptLogEnergy[0] += WEBRTC_SPL_LSHIFT_W16(PART_LEN_SHIFT, 7); - - // Logarithm of estimated echo energy through stored channel - if (tmpStored) - { - zeros = WebRtcSpl_NormU32(tmpStored); - frac = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32((WEBRTC_SPL_LSHIFT_U32(tmpStored, zeros) - & 0x7FFFFFFF), 23); - //log2 in Q8 - aecm->echoStoredLogEnergy[0] = WEBRTC_SPL_LSHIFT_W16((31 - zeros), 8) + frac; - aecm->echoStoredLogEnergy[0] - -= WEBRTC_SPL_LSHIFT_W16(RESOLUTION_CHANNEL16 + aecm->xfaQDomainBuf[delayDiff], 8); - } else - { - aecm->echoStoredLogEnergy[0] = 0; - } - aecm->echoStoredLogEnergy[0] += WEBRTC_SPL_LSHIFT_W16(PART_LEN_SHIFT, 7); - - // Update farend energy levels (min, max, vad, mse) - if (aecm->farLogEnergy[0] > FAR_ENERGY_MIN) - { - tmp16 = 11; - if (aecm->startupState == 0) - { - tmp16 = 8; - } - - aecm->farEnergyMin = WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy[0], - tmp16, 3); - aecm->farEnergyMax = WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy[0], 4, - 11); - aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin); - - // Dynamic VAD region size - tmp16 = 2560 - aecm->farEnergyMin; - if (tmp16 > 0) - { - tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16, FAR_ENERGY_VAD_REGION, 9); - } else - { - tmp16 = 0; - } - tmp16 += FAR_ENERGY_VAD_REGION; - - if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) - { - // In startup phase or VAD update halted - aecm->farEnergyVAD = aecm->farEnergyMin + tmp16; - } else - { - if (aecm->farEnergyVAD > aecm->farLogEnergy[0]) - { - aecm->farEnergyVAD += WEBRTC_SPL_RSHIFT_W16(aecm->farLogEnergy[0] + tmp16 - - aecm->farEnergyVAD, 6); - aecm->vadUpdateCount = 0; - } else - { - aecm->vadUpdateCount++; - } - } - // Put MSE threshold higher than VAD - aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8); - } - - // Update VAD variables - if (aecm->farLogEnergy[0] > aecm->farEnergyVAD) - { - if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) - { - // We are in startup or have significant dynamics in input speech level - aecm->currentVADValue = 1; - } - } else - { - aecm->currentVADValue = 0; - } - // END: Energies of delayed far, echo estimates -} - -// WebRtcAecm_CalcStepSize(...) -// -// This function calculates the step size used in channel estimation -// -// -// @param aecm [in] Handle of the AECM instance. -// @param mu [out] (Return value) Stepsize in log2(), i.e. number of shifts. -// -// -WebRtc_Word16 WebRtcAecm_CalcStepSize(AecmCore_t * const aecm) -{ - - WebRtc_Word32 tmp32; - WebRtc_Word16 tmp16; - WebRtc_Word16 mu; - - // Here we calculate the step size mu used in the - // following NLMS based Channel estimation algorithm - mu = MU_MAX; - if (!aecm->currentVADValue) - { - // Far end energy level too low, no channel update - mu = 0; - } else if (aecm->startupState > 0) - { - if (aecm->farEnergyMin >= aecm->farEnergyMax) - { - mu = MU_MIN; - } else - { - tmp16 = (aecm->farLogEnergy[0] - aecm->farEnergyMin); - tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, MU_DIFF); - tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin); - mu = MU_MIN - 1 - (WebRtc_Word16)(tmp32); - // The -1 is an alternative to rounding. This way we get a larger - // stepsize, so we in some sense compensate for truncation in NLMS - } - if (mu < MU_MAX) - { - mu = MU_MAX; // Equivalent with maximum step size of 2^-MU_MAX - } - } - // END: Update step size - - return mu; -} - -// WebRtcAecm_UpdateChannel(...) -// -// This function performs channel estimation. NLMS and decision on channel storage. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// @param dfa [in] Absolute value of the nearend signal (Q[aecm->dfaQDomain]) -// @param delayDiff [in] Delay position in farend buffer. -// @param mu [in] NLMS step size. -// @param echoEst [i/o] Estimated echo -// (Q[aecm->xfaQDomain[delayDiff]+RESOLUTION_CHANNEL16]). -// -void WebRtcAecm_UpdateChannel(AecmCore_t * const aecm, const WebRtc_UWord16 * const dfa, - const WebRtc_Word16 delayDiff, const WebRtc_Word16 mu, - WebRtc_Word32 * const echoEst) -{ - - WebRtc_UWord32 tmpU32no1, tmpU32no2; - WebRtc_Word32 tmp32no1, tmp32no2; - WebRtc_Word32 mseStored; - WebRtc_Word32 mseAdapt; - - int i; - - WebRtc_Word16 zerosFar, zerosNum, zerosCh, zerosDfa; - WebRtc_Word16 shiftChFar, shiftNum, shift2ResChan; - WebRtc_Word16 tmp16no1; - WebRtc_Word16 xfaQ, dfaQ; - - // This is the channel estimation algorithm. It is base on NLMS but has a variable step - // length, which was calculated above. - if (mu) - { - for (i = 0; i < PART_LEN1; i++) - { - // Determine norm of channel and farend to make sure we don't get overflow in - // multiplication - zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]); - zerosFar = WebRtcSpl_NormU32((WebRtc_UWord32)aecm->xfaHistory[i][delayDiff]); - if (zerosCh + zerosFar > 31) - { - // Multiplication is safe - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], - aecm->xfaHistory[i][delayDiff]); - shiftChFar = 0; - } else - { - // We need to shift down before multiplication - shiftChFar = 32 - zerosCh - zerosFar; - tmpU32no1 - = WEBRTC_SPL_UMUL_32_16(WEBRTC_SPL_RSHIFT_W32(aecm->channelAdapt32[i], - shiftChFar), - aecm->xfaHistory[i][delayDiff]); - } - // Determine Q-domain of numerator - zerosNum = WebRtcSpl_NormU32(tmpU32no1); - if (dfa[i]) - { - zerosDfa = WebRtcSpl_NormU32((WebRtc_UWord32)dfa[i]); - } else - { - zerosDfa = 32; - } - tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - RESOLUTION_CHANNEL32 - - aecm->xfaQDomainBuf[delayDiff] + shiftChFar; - if (zerosNum > tmp16no1 + 1) - { - xfaQ = tmp16no1; - dfaQ = zerosDfa - 2; - } else - { - xfaQ = zerosNum - 2; - dfaQ = RESOLUTION_CHANNEL32 + aecm->xfaQDomainBuf[delayDiff] - - aecm->dfaNoisyQDomain - shiftChFar + xfaQ; - } - // Add in the same Q-domain - tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ); - tmpU32no2 = WEBRTC_SPL_SHIFT_W32((WebRtc_UWord32)dfa[i], dfaQ); - tmp32no1 = (WebRtc_Word32)tmpU32no2 - (WebRtc_Word32)tmpU32no1; - zerosNum = WebRtcSpl_NormW32(tmp32no1); - if ((tmp32no1) && (aecm->xfaHistory[i][delayDiff] > (CHANNEL_VAD - << aecm->xfaQDomainBuf[delayDiff]))) - { - // - // Update is needed - // - // This is what we would like to compute - // - // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * aecm->xfaHistory[i][delayDiff]) - // tmp32norm = (i + 1) - // aecm->channelAdapt[i] += (2^mu) * tmp32no1 - // / (tmp32norm * aecm->xfaHistory[i][delayDiff]) - // - - // Make sure we don't get overflow in multiplication. - if (zerosNum + zerosFar > 31) - { - if (tmp32no1 > 0) - { - tmp32no2 = (WebRtc_Word32)WEBRTC_SPL_UMUL_32_16(tmp32no1, - aecm->xfaHistory[i][delayDiff]); - } else - { - tmp32no2 = -(WebRtc_Word32)WEBRTC_SPL_UMUL_32_16(-tmp32no1, - aecm->xfaHistory[i][delayDiff]); - } - shiftNum = 0; - } else - { - shiftNum = 32 - (zerosNum + zerosFar); - if (tmp32no1 > 0) - { - tmp32no2 = (WebRtc_Word32)WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_RSHIFT_W32(tmp32no1, shiftNum), - aecm->xfaHistory[i][delayDiff]); - } else - { - tmp32no2 = -(WebRtc_Word32)WEBRTC_SPL_UMUL_32_16( - WEBRTC_SPL_RSHIFT_W32(-tmp32no1, shiftNum), - aecm->xfaHistory[i][delayDiff]); - } - } - // Normalize with respect to frequency bin - tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1); - // Make sure we are in the right Q-domain - shift2ResChan = shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1); - if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) - { - tmp32no2 = WEBRTC_SPL_WORD32_MAX; - } else - { - tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan); - } - aecm->channelAdapt32[i] = WEBRTC_SPL_ADD_SAT_W32(aecm->channelAdapt32[i], - tmp32no2); - if (aecm->channelAdapt32[i] < 0) - { - // We can never have negative channel gain - aecm->channelAdapt32[i] = 0; - } - aecm->channelAdapt16[i] - = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(aecm->channelAdapt32[i], 16); - } - } - } - // END: Adaptive channel update - - // Determine if we should store or restore the channel - if ((aecm->startupState == 0) & (aecm->currentVADValue)) - { - // During startup we store the channel every block. - memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(WebRtc_Word16) * PART_LEN1); - // Recalculate echo estimate -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (i = 0; i < PART_LEN1; i++) - { - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - } -#else - for (i = 0; i < PART_LEN; ) //assume PART_LEN is 4's multiples - - { - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); - i++; - } - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], - aecm->xfaHistory[i][delayDiff]); -#endif - } else - { - if (aecm->farLogEnergy[0] < aecm->farEnergyMSE) - { - aecm->mseChannelCount = 0; - aecm->delayCount = 0; - } else - { - aecm->mseChannelCount++; - aecm->delayCount++; - } - // Enough data for validation. Store channel if we can. - if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) - { - // We have enough data. - // Calculate MSE of "Adapt" and "Stored" versions. - // It is actually not MSE, but average absolute error. - mseStored = 0; - mseAdapt = 0; - for (i = 0; i < MIN_MSE_COUNT; i++) - { - tmp32no1 = ((WebRtc_Word32)aecm->echoStoredLogEnergy[i] - - (WebRtc_Word32)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseStored += tmp32no2; - - tmp32no1 = ((WebRtc_Word32)aecm->echoAdaptLogEnergy[i] - - (WebRtc_Word32)aecm->nearLogEnergy[i]); - tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1); - mseAdapt += tmp32no2; - } - if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) - & ((aecm->mseStoredOld << MSE_RESOLUTION) < (MIN_MSE_DIFF - * aecm->mseAdaptOld))) - { - // The stored channel has a significantly lower MSE than the adaptive one for - // two consecutive calculations. Reset the adaptive channel. - memcpy(aecm->channelAdapt16, aecm->channelStored, - sizeof(WebRtc_Word16) * PART_LEN1); - // Restore the W32 channel -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (i = 0; i < PART_LEN1; i++) - { - aecm->channelAdapt32[i] - = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); - } -#else - for (i = 0; i < PART_LEN; ) //assume PART_LEN is 4's multiples - - { - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); - i++; - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); - i++; - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); - i++; - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); - i++; - } - aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)aecm->channelStored[i], 16); -#endif - - } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) & (mseAdapt - < aecm->mseThreshold) & (aecm->mseAdaptOld < aecm->mseThreshold)) - { - // The adaptive channel has a significantly lower MSE than the stored one. - // The MSE for the adaptive channel has also been low for two consecutive - // calculations. Store the adaptive channel. - memcpy(aecm->channelStored, aecm->channelAdapt16, - sizeof(WebRtc_Word16) * PART_LEN1); - // Recalculate echo estimate -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (i = 0; i < PART_LEN1; i++) - { - echoEst[i] - = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); - } -#else - for (i = 0; i < PART_LEN; ) //assume PART_LEN is 4's multiples - - { - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); - i++; - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); - i++; - } - echoEst[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], aecm->xfaHistory[i][delayDiff]); -#endif - // Update threshold - if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) - { - aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld); - } else - { - aecm->mseThreshold += WEBRTC_SPL_MUL_16_16_RSFT(mseAdapt - - WEBRTC_SPL_MUL_16_16_RSFT(aecm->mseThreshold, 5, 3), 205, 8); - } - - } - - // Reset counter - aecm->mseChannelCount = 0; - - // Store the MSE values. - aecm->mseStoredOld = mseStored; - aecm->mseAdaptOld = mseAdapt; - } - } - // END: Determine if we should store or reset channel estimate. -} - -// WebRtcAecm_CalcSuppressionGain(...) -// -// This function calculates the suppression gain that is used in the Wiener filter. -// -// -// @param aecm [i/n] Handle of the AECM instance. -// @param supGain [out] (Return value) Suppression gain with which to scale the noise -// level (Q14). -// -// -WebRtc_Word16 WebRtcAecm_CalcSuppressionGain(AecmCore_t * const aecm) -{ - WebRtc_Word32 tmp32no1; - - WebRtc_Word16 supGain; - WebRtc_Word16 tmp16no1; - WebRtc_Word16 dE = 0; - - // Determine suppression gain used in the Wiener filter. The gain is based on a mix of far - // end energy and echo estimation error. - supGain = SUPGAIN_DEFAULT; - // Adjust for the far end signal level. A low signal level indicates no far end signal, - // hence we set the suppression gain to 0 - if (!aecm->currentVADValue) - { - supGain = 0; - } else - { - // Adjust for possible double talk. If we have large variations in estimation error we - // likely have double talk (or poor channel). - tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] - ENERGY_DEV_OFFSET); - dE = WEBRTC_SPL_ABS_W16(tmp16no1); - - if (dE < ENERGY_DEV_TOL) - { - // Likely no double talk. The better estimation, the more we can suppress signal. - // Update counters - if (dE < SUPGAIN_EPC_DT) - { - tmp32no1 = WEBRTC_SPL_MUL_16_16(aecm->supGainErrParamDiffAB, dE); - tmp32no1 += (SUPGAIN_EPC_DT >> 1); - tmp16no1 = (WebRtc_Word16)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT); - supGain = aecm->supGainErrParamA - tmp16no1; - } else - { - tmp32no1 = WEBRTC_SPL_MUL_16_16(aecm->supGainErrParamDiffBD, - (ENERGY_DEV_TOL - dE)); - tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1); - tmp16no1 = (WebRtc_Word16)WebRtcSpl_DivW32W16(tmp32no1, (ENERGY_DEV_TOL - - SUPGAIN_EPC_DT)); - supGain = aecm->supGainErrParamD + tmp16no1; - } - } else - { - // Likely in double talk. Use default value - supGain = aecm->supGainErrParamD; - } - } - - if (supGain > aecm->supGainOld) - { - tmp16no1 = supGain; - } else - { - tmp16no1 = aecm->supGainOld; - } - aecm->supGainOld = supGain; - if (tmp16no1 < aecm->supGain) - { - aecm->supGain += (WebRtc_Word16)((tmp16no1 - aecm->supGain) >> 4); - } else - { - aecm->supGain += (WebRtc_Word16)((tmp16no1 - aecm->supGain) >> 4); - } - - // END: Update suppression gain - - return aecm->supGain; -} - -// WebRtcAecm_DelayCompensation(...) -// -// Secondary delay estimation that can be used as a backup or for validation. This function is -// still under construction and not activated in current version. -// -// -// @param aecm [i/o] Handle of the AECM instance. -// -// -void WebRtcAecm_DelayCompensation(AecmCore_t * const aecm) -{ - int i, j; - WebRtc_Word32 delayMeanEcho[CORR_BUF_LEN]; - WebRtc_Word32 delayMeanNear[CORR_BUF_LEN]; - WebRtc_Word16 sumBitPattern, bitPatternEcho, bitPatternNear, maxPos, maxValue, - maxValueLeft, maxValueRight; - - // Check delay (calculate the delay offset (if we can)). - if ((aecm->startupState > 0) & (aecm->delayCount >= CORR_MAX_BUF) & aecm->delayOffsetFlag) - { - // Calculate mean values - for (i = 0; i < CORR_BUF_LEN; i++) - { - delayMeanEcho[i] = 0; - delayMeanNear[i] = 0; -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (j = 0; j < CORR_WIDTH; j++) - { - delayMeanEcho[i] += (WebRtc_Word32)aecm->echoStoredLogEnergy[i + j]; - delayMeanNear[i] += (WebRtc_Word32)aecm->nearLogEnergy[i + j]; - } -#else - for (j = 0; j < CORR_WIDTH -1; ) - { - delayMeanEcho[i] += (WebRtc_Word32)aecm->echoStoredLogEnergy[i + j]; - delayMeanNear[i] += (WebRtc_Word32)aecm->nearLogEnergy[i + j]; - j++; - delayMeanEcho[i] += (WebRtc_Word32)aecm->echoStoredLogEnergy[i + j]; - delayMeanNear[i] += (WebRtc_Word32)aecm->nearLogEnergy[i + j]; - j++; - } - delayMeanEcho[i] += (WebRtc_Word32)aecm->echoStoredLogEnergy[i + j]; - delayMeanNear[i] += (WebRtc_Word32)aecm->nearLogEnergy[i + j]; -#endif - } - // Calculate correlation values - for (i = 0; i < CORR_BUF_LEN; i++) - { - sumBitPattern = 0; -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (j = 0; j < CORR_WIDTH; j++) - { - bitPatternEcho = (WebRtc_Word16)((WebRtc_Word32)aecm->echoStoredLogEnergy[i - + j] * CORR_WIDTH > delayMeanEcho[i]); - bitPatternNear = (WebRtc_Word16)((WebRtc_Word32)aecm->nearLogEnergy[CORR_MAX - + j] * CORR_WIDTH > delayMeanNear[CORR_MAX]); - sumBitPattern += !(bitPatternEcho ^ bitPatternNear); - } -#else - for (j = 0; j < CORR_WIDTH -1; ) - { - bitPatternEcho = (WebRtc_Word16)((WebRtc_Word32)aecm->echoStoredLogEnergy[i - + j] * CORR_WIDTH > delayMeanEcho[i]); - bitPatternNear = (WebRtc_Word16)((WebRtc_Word32)aecm->nearLogEnergy[CORR_MAX - + j] * CORR_WIDTH > delayMeanNear[CORR_MAX]); - sumBitPattern += !(bitPatternEcho ^ bitPatternNear); - j++; - bitPatternEcho = (WebRtc_Word16)((WebRtc_Word32)aecm->echoStoredLogEnergy[i - + j] * CORR_WIDTH > delayMeanEcho[i]); - bitPatternNear = (WebRtc_Word16)((WebRtc_Word32)aecm->nearLogEnergy[CORR_MAX - + j] * CORR_WIDTH > delayMeanNear[CORR_MAX]); - sumBitPattern += !(bitPatternEcho ^ bitPatternNear); - j++; - } - bitPatternEcho = (WebRtc_Word16)((WebRtc_Word32)aecm->echoStoredLogEnergy[i + j] - * CORR_WIDTH > delayMeanEcho[i]); - bitPatternNear = (WebRtc_Word16)((WebRtc_Word32)aecm->nearLogEnergy[CORR_MAX + j] - * CORR_WIDTH > delayMeanNear[CORR_MAX]); - sumBitPattern += !(bitPatternEcho ^ bitPatternNear); -#endif - aecm->delayCorrelation[i] = sumBitPattern; - } - aecm->newDelayCorrData = 1; // Indicate we have new correlation data to evaluate - } - if ((aecm->startupState == 2) & (aecm->lastDelayUpdateCount > (CORR_WIDTH << 1)) - & aecm->newDelayCorrData) - { - // Find maximum value and maximum position as well as values on the sides. - maxPos = 0; - maxValue = aecm->delayCorrelation[0]; - maxValueLeft = maxValue; - maxValueRight = aecm->delayCorrelation[CORR_DEV]; - for (i = 1; i < CORR_BUF_LEN; i++) - { - if (aecm->delayCorrelation[i] > maxValue) - { - maxValue = aecm->delayCorrelation[i]; - maxPos = i; - if (maxPos < CORR_DEV) - { - maxValueLeft = aecm->delayCorrelation[0]; - maxValueRight = aecm->delayCorrelation[i + CORR_DEV]; - } else if (maxPos > (CORR_MAX << 1) - CORR_DEV) - { - maxValueLeft = aecm->delayCorrelation[i - CORR_DEV]; - maxValueRight = aecm->delayCorrelation[(CORR_MAX << 1)]; - } else - { - maxValueLeft = aecm->delayCorrelation[i - CORR_DEV]; - maxValueRight = aecm->delayCorrelation[i + CORR_DEV]; - } - } - } - if ((maxPos > 0) & (maxPos < (CORR_MAX << 1))) - { - // Avoid maximum at boundaries. The maximum peak has to be higher than - // CORR_MAX_LEVEL. It also has to be sharp, i.e. the value CORR_DEV bins off should - // be CORR_MAX_LOW lower than the maximum. - if ((maxValue > CORR_MAX_LEVEL) & (maxValueLeft < maxValue - CORR_MAX_LOW) - & (maxValueRight < maxValue - CORR_MAX_LOW)) - { - aecm->delayAdjust += CORR_MAX - maxPos; - aecm->newDelayCorrData = 0; - aecm->lastDelayUpdateCount = 0; - } - } - } - // END: "Check delay" -} - -void WebRtcAecm_ProcessBlock(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const WebRtc_Word16 * const nearendNoisy, - const WebRtc_Word16 * const nearendClean, - WebRtc_Word16 * const output) -{ - int i, j; - - WebRtc_UWord32 xfaSum; - WebRtc_UWord32 dfaNoisySum; - WebRtc_UWord32 echoEst32Gained; - WebRtc_UWord32 tmpU32; - - WebRtc_Word32 tmp32no1; - WebRtc_Word32 tmp32no2; - WebRtc_Word32 echoEst32[PART_LEN1]; - - WebRtc_UWord16 xfa[PART_LEN1]; - WebRtc_UWord16 dfaNoisy[PART_LEN1]; - WebRtc_UWord16 dfaClean[PART_LEN1]; - WebRtc_UWord16* ptrDfaClean = dfaClean; - - int outCFFT; - - WebRtc_Word16 fft[PART_LEN4]; -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - WebRtc_Word16 postFft[PART_LEN4]; -#else - WebRtc_Word16 postFft[PART_LEN2]; -#endif - WebRtc_Word16 dfwReal[PART_LEN1]; - WebRtc_Word16 dfwImag[PART_LEN1]; - WebRtc_Word16 xfwReal[PART_LEN1]; - WebRtc_Word16 xfwImag[PART_LEN1]; - WebRtc_Word16 efwReal[PART_LEN1]; - WebRtc_Word16 efwImag[PART_LEN1]; - WebRtc_Word16 hnl[PART_LEN1]; - WebRtc_Word16 numPosCoef; - WebRtc_Word16 nlpGain; - WebRtc_Word16 delay, diff, diffMinusOne; - WebRtc_Word16 tmp16no1; - WebRtc_Word16 tmp16no2; -#ifdef AECM_WITH_ABS_APPROX - WebRtc_Word16 maxValue; - WebRtc_Word16 minValue; -#endif - WebRtc_Word16 mu; - WebRtc_Word16 supGain; - WebRtc_Word16 zeros32, zeros16; - WebRtc_Word16 zerosDBufNoisy, zerosDBufClean, zerosXBuf; - WebRtc_Word16 resolutionDiff, qDomainDiff; - -#ifdef ARM_WINM_LOG_ - DWORD temp; - static int flag0 = 0; - __int64 freq, start, end, diff__; - unsigned int milliseconds; -#endif - -#ifdef AECM_WITH_ABS_APPROX - WebRtc_UWord16 alpha, beta; -#endif - - // Determine startup state. There are three states: - // (0) the first CONV_LEN blocks - // (1) another CONV_LEN blocks - // (2) the rest - - if (aecm->startupState < 2) - { - aecm->startupState = (aecm->totCount >= CONV_LEN) + (aecm->totCount >= CONV_LEN2); - } - // END: Determine startup state - - // Buffer near and far end signals - memcpy(aecm->xBuf + PART_LEN, farend, sizeof(WebRtc_Word16) * PART_LEN); - memcpy(aecm->dBufNoisy + PART_LEN, nearendNoisy, sizeof(WebRtc_Word16) * PART_LEN); - if (nearendClean != NULL) - { - memcpy(aecm->dBufClean + PART_LEN, nearendClean, sizeof(WebRtc_Word16) * PART_LEN); - } - -#ifdef AECM_DYNAMIC_Q - tmp16no1 = WebRtcSpl_MaxAbsValueW16(aecm->dBufNoisy, PART_LEN2); - tmp16no2 = WebRtcSpl_MaxAbsValueW16(aecm->xBuf, PART_LEN2); - zerosDBufNoisy = WebRtcSpl_NormW16(tmp16no1); - zerosXBuf = WebRtcSpl_NormW16(tmp16no2); -#else - zerosDBufNoisy = 0; - zerosXBuf = 0; -#endif - aecm->dfaNoisyQDomainOld = aecm->dfaNoisyQDomain; - aecm->dfaNoisyQDomain = zerosDBufNoisy; - - if (nearendClean != NULL) - { -#ifdef AECM_DYNAMIC_Q - tmp16no1 = WebRtcSpl_MaxAbsValueW16(aecm->dBufClean, PART_LEN2); - zerosDBufClean = WebRtcSpl_NormW16(tmp16no1); -#else - zerosDBufClean = 0; -#endif - aecm->dfaCleanQDomainOld = aecm->dfaCleanQDomain; - aecm->dfaCleanQDomain = zerosDBufClean; - } else - { - zerosDBufClean = zerosDBufNoisy; - aecm->dfaCleanQDomainOld = aecm->dfaNoisyQDomainOld; - aecm->dfaCleanQDomain = aecm->dfaNoisyQDomain; - } - -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceFrequency((LARGE_INTEGER*)&freq); - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - - // FFT of noisy near end signal - for (i = 0; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - // Window near end - fft[j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((aecm->dBufNoisy[i] - << zerosDBufNoisy), kSqrtHanning[i], 14); - fft[PART_LEN2 + j] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT( - (aecm->dBufNoisy[PART_LEN + i] << zerosDBufNoisy), - kSqrtHanning[PART_LEN - i], 14); - // Inserting zeros in imaginary parts - fft[j + 1] = 0; - fft[PART_LEN2 + j + 1] = 0; - } - - // Fourier transformation of near end signal. - // The result is scaled with 1/PART_LEN2, that is, the result is in Q(-6) for PART_LEN = 32 - -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - outCFFT = WebRtcSpl_ComplexFFT2(fft, postFft, PART_LEN_SHIFT, 1); - - // The imaginary part has to switch sign - for(i = 1; i < PART_LEN2-1;) - { - postFft[i] = -postFft[i]; - i += 2; - postFft[i] = -postFft[i]; - i += 2; - } -#else - WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT); - outCFFT = WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); - - // Take only the first PART_LEN2 samples - for (i = 0; i < PART_LEN2; i++) - { - postFft[i] = fft[i]; - } - // The imaginary part has to switch sign - for (i = 1; i < PART_LEN2;) - { - postFft[i] = -postFft[i]; - i += 2; - } -#endif - - // Extract imaginary and real part, calculate the magnitude for all frequency bins - dfwImag[0] = 0; - dfwImag[PART_LEN] = 0; - dfwReal[0] = postFft[0]; -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - dfwReal[PART_LEN] = postFft[PART_LEN2]; -#else - dfwReal[PART_LEN] = fft[PART_LEN2]; -#endif - dfaNoisy[0] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[0]); - dfaNoisy[PART_LEN] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[PART_LEN]); - dfaNoisySum = (WebRtc_UWord32)(dfaNoisy[0]); - dfaNoisySum += (WebRtc_UWord32)(dfaNoisy[PART_LEN]); - - for (i = 1; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - dfwReal[i] = postFft[j]; - dfwImag[i] = postFft[j + 1]; - - if (dfwReal[i] == 0 || dfwImag[i] == 0) - { - dfaNoisy[i] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[i] + dfwImag[i]); - } else - { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) - // - // The parameters alpha and beta are stored in Q15 - - tmp16no1 = WEBRTC_SPL_ABS_W16(postFft[j]); - tmp16no2 = WEBRTC_SPL_ABS_W16(postFft[j + 1]); - -#ifdef AECM_WITH_ABS_APPROX - if(tmp16no1 > tmp16no2) - { - maxValue = tmp16no1; - minValue = tmp16no2; - } else - { - maxValue = tmp16no2; - minValue = tmp16no1; - } - - // Magnitude in Q-6 - if ((maxValue >> 2) > minValue) - { - alpha = kAlpha1; - beta = kBeta1; - } else if ((maxValue >> 1) > minValue) - { - alpha = kAlpha2; - beta = kBeta2; - } else - { - alpha = kAlpha3; - beta = kBeta3; - } - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(maxValue, alpha, 15); - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(minValue, beta, 15); - dfaNoisy[i] = (WebRtc_UWord16)tmp16no1 + (WebRtc_UWord16)tmp16no2; -#else - tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); - tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2); - tmp32no2 = WEBRTC_SPL_ADD_SAT_W32(tmp32no1, tmp32no2); - tmp32no1 = WebRtcSpl_Sqrt(tmp32no2); - dfaNoisy[i] = (WebRtc_UWord16)tmp32no1; -#endif - } - dfaNoisySum += (WebRtc_UWord32)dfaNoisy[i]; - } - // END: FFT of noisy near end signal - - if (nearendClean == NULL) - { - ptrDfaClean = dfaNoisy; - } else - { - // FFT of clean near end signal - for (i = 0; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - // Window near end - fft[j] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((aecm->dBufClean[i] << zerosDBufClean), kSqrtHanning[i], 14); - fft[PART_LEN2 + j] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((aecm->dBufClean[PART_LEN + i] << zerosDBufClean), kSqrtHanning[PART_LEN - i], 14); - // Inserting zeros in imaginary parts - fft[j + 1] = 0; - fft[PART_LEN2 + j + 1] = 0; - } - - // Fourier transformation of near end signal. - // The result is scaled with 1/PART_LEN2, that is, in Q(-6) for PART_LEN = 32 - -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - outCFFT = WebRtcSpl_ComplexFFT2(fft, postFft, PART_LEN_SHIFT, 1); - - // The imaginary part has to switch sign - for(i = 1; i < PART_LEN2-1;) - { - postFft[i] = -postFft[i]; - i += 2; - postFft[i] = -postFft[i]; - i += 2; - } -#else - WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT); - outCFFT = WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); - - // Take only the first PART_LEN2 samples - for (i = 0; i < PART_LEN2; i++) - { - postFft[i] = fft[i]; - } - // The imaginary part has to switch sign - for (i = 1; i < PART_LEN2;) - { - postFft[i] = -postFft[i]; - i += 2; - } -#endif - - // Extract imaginary and real part, calculate the magnitude for all frequency bins - dfwImag[0] = 0; - dfwImag[PART_LEN] = 0; - dfwReal[0] = postFft[0]; -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - dfwReal[PART_LEN] = postFft[PART_LEN2]; -#else - dfwReal[PART_LEN] = fft[PART_LEN2]; -#endif - dfaClean[0] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[0]); - dfaClean[PART_LEN] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[PART_LEN]); - - for (i = 1; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - dfwReal[i] = postFft[j]; - dfwImag[i] = postFft[j + 1]; - - if (dfwReal[i] == 0 || dfwImag[i] == 0) - { - dfaClean[i] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(dfwReal[i] + dfwImag[i]); - } else - { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) - // - // The parameters alpha and beta are stored in Q15 - - tmp16no1 = WEBRTC_SPL_ABS_W16(postFft[j]); - tmp16no2 = WEBRTC_SPL_ABS_W16(postFft[j + 1]); - -#ifdef AECM_WITH_ABS_APPROX - if(tmp16no1 > tmp16no2) - { - maxValue = tmp16no1; - minValue = tmp16no2; - } else - { - maxValue = tmp16no2; - minValue = tmp16no1; - } - - // Magnitude in Q-6 - if ((maxValue >> 2) > minValue) - { - alpha = kAlpha1; - beta = kBeta1; - } else if ((maxValue >> 1) > minValue) - { - alpha = kAlpha2; - beta = kBeta2; - } else - { - alpha = kAlpha3; - beta = kBeta3; - } - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(maxValue, alpha, 15); - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(minValue, beta, 15); - dfaClean[i] = (WebRtc_UWord16)tmp16no1 + (WebRtc_UWord16)tmp16no2; -#else - tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); - tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2); - tmp32no2 = WEBRTC_SPL_ADD_SAT_W32(tmp32no1, tmp32no2); - tmp32no1 = WebRtcSpl_Sqrt(tmp32no2); - dfaClean[i] = (WebRtc_UWord16)tmp32no1; -#endif - } - } - } - // END: FFT of clean near end signal - - // FFT of far end signal - for (i = 0; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - // Window farend - fft[j] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((aecm->xBuf[i] << zerosXBuf), kSqrtHanning[i], 14); - fft[PART_LEN2 + j] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT((aecm->xBuf[PART_LEN + i] << zerosXBuf), kSqrtHanning[PART_LEN - i], 14); - // Inserting zeros in imaginary parts - fft[j + 1] = 0; - fft[PART_LEN2 + j + 1] = 0; - } - // Fourier transformation of far end signal. - // The result is scaled with 1/PART_LEN2, that is the result is in Q(-6) for PART_LEN = 32 -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - outCFFT = WebRtcSpl_ComplexFFT2(fft, postFft, PART_LEN_SHIFT, 1); - - // The imaginary part has to switch sign - for(i = 1; i < PART_LEN2-1;) - { - postFft[i] = -postFft[i]; - i += 2; - postFft[i] = -postFft[i]; - i += 2; - } -#else - WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT); - outCFFT = WebRtcSpl_ComplexFFT(fft, PART_LEN_SHIFT, 1); - - // Take only the first PART_LEN2 samples - for (i = 0; i < PART_LEN2; i++) - { - postFft[i] = fft[i]; - } - // The imaginary part has to switch sign - for (i = 1; i < PART_LEN2;) - { - postFft[i] = -postFft[i]; - i += 2; - } -#endif - - // Extract imaginary and real part, calculate the magnitude for all frequency bins - xfwImag[0] = 0; - xfwImag[PART_LEN] = 0; - xfwReal[0] = postFft[0]; -#if (defined ARM_WINM) || (defined ARM9E_GCC) || (defined ANDROID_AECOPT) - xfwReal[PART_LEN] = postFft[PART_LEN2]; -#else - xfwReal[PART_LEN] = fft[PART_LEN2]; -#endif - xfa[0] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(xfwReal[0]); - xfa[PART_LEN] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(xfwReal[PART_LEN]); - xfaSum = (WebRtc_UWord32)(xfa[0]) + (WebRtc_UWord32)(xfa[PART_LEN]); - - for (i = 1; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i,1); - xfwReal[i] = postFft[j]; - xfwImag[i] = postFft[j + 1]; - - if (xfwReal[i] == 0 || xfwImag[i] == 0) - { - xfa[i] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(xfwReal[i] + xfwImag[i]); - } else - { - // Approximation for magnitude of complex fft output - // magn = sqrt(real^2 + imag^2) - // magn ~= alpha * max(|imag|,|real|) + beta * min(|imag|,|real|) - // - // The parameters alpha and beta are stored in Q15 - - tmp16no1 = WEBRTC_SPL_ABS_W16(postFft[j]); - tmp16no2 = WEBRTC_SPL_ABS_W16(postFft[j + 1]); - -#ifdef AECM_WITH_ABS_APPROX - if(tmp16no1 > xfwImag[i]) - { - maxValue = tmp16no1; - minValue = tmp16no2; - } else - { - maxValue = tmp16no2; - minValue = tmp16no1; - } - // Magnitude in Q-6 - if ((maxValue >> 2) > minValue) - { - alpha = kAlpha1; - beta = kBeta1; - } else if ((maxValue >> 1) > minValue) - { - alpha = kAlpha2; - beta = kBeta2; - } else - { - alpha = kAlpha3; - beta = kBeta3; - } - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(maxValue, alpha, 15); - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(minValue, beta, 15); - xfa[i] = (WebRtc_UWord16)tmp16no1 + (WebRtc_UWord16)tmp16no2; -#else - tmp32no1 = WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); - tmp32no2 = WEBRTC_SPL_MUL_16_16(tmp16no2, tmp16no2); - tmp32no2 = WEBRTC_SPL_ADD_SAT_W32(tmp32no1, tmp32no2); - tmp32no1 = WebRtcSpl_Sqrt(tmp32no2); - xfa[i] = (WebRtc_UWord16)tmp32no1; -#endif - } - xfaSum += (WebRtc_UWord32)xfa[i]; - } - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - // END: FFT of far end signal - - // Get the delay - - // Fixed delay estimation - // input: dfaFIX, xfaFIX in Q-stages - // output: delay in Q0 - // - // comment on the fixed point accuracy of estimate_delayFIX - // -> due to rounding the fixed point variables xfa and dfa contain a lot more zeros - // than the corresponding floating point variables this results in big differences - // between the floating point and the fixed point logarithmic spectra for small values -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - - // Save far-end history and estimate delay - delay = WebRtcAecm_EstimateDelay(aecm, xfa, dfaNoisy, zerosXBuf); - - if (aecm->fixedDelay >= 0) - { - // Use fixed delay - delay = aecm->fixedDelay; - } - - aecm->currentDelay = delay; - - if ((aecm->delayOffsetFlag) & (aecm->startupState > 0)) // If delay compensation is on - { - // If the delay estimate changed from previous block, update the offset - if ((aecm->currentDelay != aecm->previousDelay) & !aecm->currentDelay - & !aecm->previousDelay) - { - aecm->delayAdjust += (aecm->currentDelay - aecm->previousDelay); - } - // Compensate with the offset estimate - aecm->currentDelay -= aecm->delayAdjust; - aecm->previousDelay = delay; - } - - diff = aecm->delHistoryPos - aecm->currentDelay; - if (diff < 0) - { - diff = diff + MAX_DELAY; - } - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - - // END: Get the delay - -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - // Calculate log(energy) and update energy threshold levels - WebRtcAecm_CalcEnergies(aecm, diff, dfaNoisySum, echoEst32); - - // Calculate stepsize - mu = WebRtcAecm_CalcStepSize(aecm); - - // END: Update step size - - // Update counters - aecm->totCount++; - aecm->lastDelayUpdateCount++; - - // This is the channel estimation algorithm. - // It is base on NLMS but has a variable step length, which was calculated above. - WebRtcAecm_UpdateChannel(aecm, dfaNoisy, diff, mu, echoEst32); - WebRtcAecm_DelayCompensation(aecm); - supGain = WebRtcAecm_CalcSuppressionGain(aecm); - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - - // END: Update suppression gain - -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - - // Calculate Wiener filter hnl[] - numPosCoef = 0; - diffMinusOne = diff - 1; - if (diff == 0) - { - diffMinusOne = MAX_DELAY; - } - for (i = 0; i < PART_LEN1; i++) - { - // Far end signal through channel estimate in Q8 - // How much can we shift right to preserve resolution - tmp32no1 = echoEst32[i] - aecm->echoFilt[i]; - aecm->echoFilt[i] += WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(tmp32no1, 50), 8); - - zeros32 = WebRtcSpl_NormW32(aecm->echoFilt[i]) + 1; - zeros16 = WebRtcSpl_NormW16(supGain) + 1; - if (zeros32 + zeros16 > 16) - { - // Multiplication is safe - // Result in Q(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN+aecm->xfaQDomainBuf[diff]) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((WebRtc_UWord32)aecm->echoFilt[i], - (WebRtc_UWord16)supGain); - resolutionDiff = 14 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - aecm->xfaQDomainBuf[diff]); - } else - { - tmp16no1 = 17 - zeros32 - zeros16; - resolutionDiff = 14 + tmp16no1 - RESOLUTION_CHANNEL16 - RESOLUTION_SUPGAIN; - resolutionDiff += (aecm->dfaCleanQDomain - aecm->xfaQDomainBuf[diff]); - if (zeros32 > tmp16no1) - { - echoEst32Gained = WEBRTC_SPL_UMUL_32_16((WebRtc_UWord32)aecm->echoFilt[i], - (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_W16(supGain, - tmp16no1)); // Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - } else - { - // Result in Q-(RESOLUTION_CHANNEL+RESOLUTION_SUPGAIN-16) - echoEst32Gained = WEBRTC_SPL_UMUL_32_16( - (WebRtc_UWord32)WEBRTC_SPL_RSHIFT_W32(aecm->echoFilt[i], tmp16no1), - (WebRtc_UWord16)supGain); - } - } - - zeros16 = WebRtcSpl_NormW16(aecm->nearFilt[i]); - if ((zeros16 < (aecm->dfaCleanQDomain - aecm->dfaCleanQDomainOld)) - & (aecm->nearFilt[i])) - { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], zeros16); - qDomainDiff = zeros16 - aecm->dfaCleanQDomain + aecm->dfaCleanQDomainOld; - } else - { - tmp16no1 = WEBRTC_SPL_SHIFT_W16(aecm->nearFilt[i], aecm->dfaCleanQDomain - - aecm->dfaCleanQDomainOld); - qDomainDiff = 0; - } - tmp16no2 = WEBRTC_SPL_SHIFT_W16(ptrDfaClean[i], qDomainDiff); - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no2 - tmp16no1, 1, 4); - tmp16no2 += tmp16no1; - zeros16 = WebRtcSpl_NormW16(tmp16no2); - if ((tmp16no2) & (-qDomainDiff > zeros16)) - { - aecm->nearFilt[i] = WEBRTC_SPL_WORD16_MAX; - } else - { - aecm->nearFilt[i] = WEBRTC_SPL_SHIFT_W16(tmp16no2, -qDomainDiff); - } - - // Wiener filter coefficients, resulting hnl in Q14 - if (echoEst32Gained == 0) - { - hnl[i] = ONE_Q14; - } else if (aecm->nearFilt[i] == 0) - { - hnl[i] = 0; - } else - { - // Multiply the suppression gain - // Rounding - echoEst32Gained += (WebRtc_UWord32)(aecm->nearFilt[i] >> 1); - tmpU32 = WebRtcSpl_DivU32U16(echoEst32Gained, (WebRtc_UWord16)aecm->nearFilt[i]); - - // Current resolution is - // Q-(RESOLUTION_CHANNEL + RESOLUTION_SUPGAIN - max(0, 17 - zeros16 - zeros32)) - // Make sure we are in Q14 - tmp32no1 = (WebRtc_Word32)WEBRTC_SPL_SHIFT_W32(tmpU32, resolutionDiff); - if (tmp32no1 > ONE_Q14) - { - hnl[i] = 0; - } else if (tmp32no1 < 0) - { - hnl[i] = ONE_Q14; - } else - { - // 1-echoEst/dfa -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - hnl[i] = ONE_Q14 - (WebRtc_Word16)tmp32no1; - if (hnl[i] < 0) - { - hnl[i] = 0; - } -#else - hnl[i] = ((ONE_Q14 - (WebRtc_Word16)tmp32no1) > 0) ? (ONE_Q14 - (WebRtc_Word16)tmp32no1) : 0; -#endif - } - } - if (hnl[i]) - { - numPosCoef++; - } - } - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - - // Calculate NLP gain, result is in Q14 - for (i = 0; i < PART_LEN1; i++) - { - if (aecm->nlpFlag) - { - // Truncate values close to zero and one. - if (hnl[i] > NLP_COMP_HIGH) - { - hnl[i] = ONE_Q14; - } else if (hnl[i] < NLP_COMP_LOW) - { - hnl[i] = 0; - } - - // Remove outliers - if (numPosCoef < 3) - { - nlpGain = 0; - } else - { - nlpGain = ONE_Q14; - } - // NLP - if ((hnl[i] == ONE_Q14) && (nlpGain == ONE_Q14)) - { - hnl[i] = ONE_Q14; - } else - { - hnl[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(hnl[i], nlpGain, 14); - } - } - - // multiply with Wiener coefficients - efwReal[i] = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfwReal[i], hnl[i], - 14)); - efwImag[i] = (WebRtc_Word16)(WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(dfwImag[i], hnl[i], - 14)); - } - - if (aecm->cngMode == AecmTrue) - { - WebRtcAecm_ComfortNoise(aecm, ptrDfaClean, efwReal, efwImag, hnl); - } - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - -#ifdef ARM_WINM_LOG_ - // measure tick start - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#endif - - // Synthesis - for (i = 1; i < PART_LEN; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - fft[j] = efwReal[i]; - - // mirrored data, even - fft[PART_LEN4 - j] = efwReal[i]; - fft[j + 1] = -efwImag[i]; - - //mirrored data, odd - fft[PART_LEN4 - (j - 1)] = efwImag[i]; - } - fft[0] = efwReal[0]; - fft[1] = -efwImag[0]; - - fft[PART_LEN2] = efwReal[PART_LEN]; - fft[PART_LEN2 + 1] = -efwImag[PART_LEN]; - -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - // inverse FFT, result should be scaled with outCFFT - WebRtcSpl_ComplexBitReverse(fft, PART_LEN_SHIFT); - outCFFT = WebRtcSpl_ComplexIFFT(fft, PART_LEN_SHIFT, 1); - - //take only the real values and scale with outCFFT - for (i = 0; i < PART_LEN2; i++) - { - j = WEBRTC_SPL_LSHIFT_W32(i, 1); - fft[i] = fft[j]; - } -#else - outCFFT = WebRtcSpl_ComplexIFFT2(fft, postFft, PART_LEN_SHIFT, 1); - - //take only the real values and scale with outCFFT - for(i = 0, j = 0; i < PART_LEN2;) - { - fft[i] = postFft[j]; - i += 1; - j += 2; - fft[i] = postFft[j]; - i += 1; - j += 2; - } -#endif - - for (i = 0; i < PART_LEN; i++) - { - fft[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - fft[i], - kSqrtHanning[i], - 14); - tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)fft[i], - outCFFT - aecm->dfaCleanQDomain); - fft[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, - tmp32no1 + aecm->outBuf[i], - WEBRTC_SPL_WORD16_MIN); - output[i] = fft[i]; - - tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT( - fft[PART_LEN + i], - kSqrtHanning[PART_LEN - i], - 14); - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, - outCFFT - aecm->dfaCleanQDomain); - aecm->outBuf[i] = (WebRtc_Word16)WEBRTC_SPL_SAT( - WEBRTC_SPL_WORD16_MAX, - tmp32no1, - WEBRTC_SPL_WORD16_MIN); - } - -#ifdef ARM_WINM_LOG_ - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - diff__ = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff__ & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); -#endif - // Copy the current block to the old position (outBuf is shifted elsewhere) - memcpy(aecm->xBuf, aecm->xBuf + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN); - memcpy(aecm->dBufNoisy, aecm->dBufNoisy + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN); - if (nearendClean != NULL) - { - memcpy(aecm->dBufClean, aecm->dBufClean + PART_LEN, sizeof(WebRtc_Word16) * PART_LEN); - } -} - -// Generate comfort noise and add to output signal. -// -// \param[in] aecm Handle of the AECM instance. -// \param[in] dfa Absolute value of the nearend signal (Q[aecm->dfaQDomain]). -// \param[in,out] outReal Real part of the output signal (Q[aecm->dfaQDomain]). -// \param[in,out] outImag Imaginary part of the output signal (Q[aecm->dfaQDomain]). -// \param[in] lambda Suppression gain with which to scale the noise level (Q14). -// -static void WebRtcAecm_ComfortNoise(AecmCore_t * const aecm, const WebRtc_UWord16 * const dfa, - WebRtc_Word16 * const outReal, - WebRtc_Word16 * const outImag, - const WebRtc_Word16 * const lambda) -{ - WebRtc_Word16 i; - WebRtc_Word16 tmp16; - WebRtc_Word32 tmp32; - - WebRtc_Word16 randW16[PART_LEN]; - WebRtc_Word16 uReal[PART_LEN1]; - WebRtc_Word16 uImag[PART_LEN1]; - WebRtc_Word32 outLShift32[PART_LEN1]; - WebRtc_Word16 noiseRShift16[PART_LEN1]; - - WebRtc_Word16 shiftFromNearToNoise[PART_LEN1]; - WebRtc_Word16 minTrackShift; - WebRtc_Word32 upper32; - WebRtc_Word32 lower32; - - if (aecm->noiseEstCtr < 100) - { - // Track the minimum more quickly initially. - aecm->noiseEstCtr++; - minTrackShift = 7; - } else - { - minTrackShift = 9; - } - - // Estimate noise power. - for (i = 0; i < PART_LEN1; i++) - { - shiftFromNearToNoise[i] = aecm->noiseEstQDomain[i] - aecm->dfaCleanQDomain; - - // Shift to the noise domain. - tmp32 = (WebRtc_Word32)dfa[i]; - outLShift32[i] = WEBRTC_SPL_SHIFT_W32(tmp32, shiftFromNearToNoise[i]); - - if (outLShift32[i] < aecm->noiseEst[i]) - { - // Track the minimum. - aecm->noiseEst[i] += ((outLShift32[i] - aecm->noiseEst[i]) >> minTrackShift); - } else - { - // Ramp slowly upwards until we hit the minimum again. - - // Avoid overflow. - if (aecm->noiseEst[i] < 2146435583) - { - // Store the fractional portion. - upper32 = (aecm->noiseEst[i] & 0xffff0000) >> 16; - lower32 = aecm->noiseEst[i] & 0x0000ffff; - upper32 = ((upper32 * 2049) >> 11); - lower32 = ((lower32 * 2049) >> 11); - aecm->noiseEst[i] = WEBRTC_SPL_ADD_SAT_W32(upper32 << 16, lower32); - } - } - } - - for (i = 0; i < PART_LEN1; i++) - { - tmp32 = WEBRTC_SPL_SHIFT_W32(aecm->noiseEst[i], -shiftFromNearToNoise[i]); - if (tmp32 > 32767) - { - tmp32 = 32767; - aecm->noiseEst[i] = WEBRTC_SPL_SHIFT_W32(tmp32, shiftFromNearToNoise[i]); - } - noiseRShift16[i] = (WebRtc_Word16)tmp32; - - tmp16 = ONE_Q14 - lambda[i]; - noiseRShift16[i] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16, noiseRShift16[i], 14); - } - - // Generate a uniform random array on [0 2^15-1]. - WebRtcSpl_RandUArray(randW16, PART_LEN, &aecm->seed); - - // Generate noise according to estimated energy. - uReal[0] = 0; // Reject LF noise. - uImag[0] = 0; - for (i = 1; i < PART_LEN1; i++) - { - // Get a random index for the cos and sin tables over [0 359]. - tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(359, randW16[i - 1], 15); - - // Tables are in Q13. - uReal[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(noiseRShift16[i], - WebRtcSpl_kCosTable[tmp16], 13); - uImag[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(-noiseRShift16[i], - WebRtcSpl_kSinTable[tmp16], 13); - } - uImag[PART_LEN] = 0; - -#if (!defined ARM_WINM) && (!defined ARM9E_GCC) && (!defined ANDROID_AECOPT) - for (i = 0; i < PART_LEN1; i++) - { - outReal[i] = WEBRTC_SPL_ADD_SAT_W16(outReal[i], uReal[i]); - outImag[i] = WEBRTC_SPL_ADD_SAT_W16(outImag[i], uImag[i]); - } -#else - for (i = 0; i < PART_LEN1 -1; ) - { - outReal[i] = WEBRTC_SPL_ADD_SAT_W16(outReal[i], uReal[i]); - outImag[i] = WEBRTC_SPL_ADD_SAT_W16(outImag[i], uImag[i]); - i++; - - outReal[i] = WEBRTC_SPL_ADD_SAT_W16(outReal[i], uReal[i]); - outImag[i] = WEBRTC_SPL_ADD_SAT_W16(outImag[i], uImag[i]); - i++; - } - outReal[i] = WEBRTC_SPL_ADD_SAT_W16(outReal[i], uReal[i]); - outImag[i] = WEBRTC_SPL_ADD_SAT_W16(outImag[i], uImag[i]); -#endif -} - -void WebRtcAecm_BufferFarFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const int farLen) -{ - int writeLen = farLen, writePos = 0; - - // Check if the write position must be wrapped - while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) - { - // Write to remaining buffer space before wrapping - writeLen = FAR_BUF_LEN - aecm->farBufWritePos; - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(WebRtc_Word16) * writeLen); - aecm->farBufWritePos = 0; - writePos = writeLen; - writeLen = farLen - writeLen; - } - - memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos, - sizeof(WebRtc_Word16) * writeLen); - aecm->farBufWritePos += writeLen; -} - -void WebRtcAecm_FetchFarFrame(AecmCore_t * const aecm, WebRtc_Word16 * const farend, - const int farLen, const int knownDelay) -{ - int readLen = farLen; - int readPos = 0; - int delayChange = knownDelay - aecm->lastKnownDelay; - - aecm->farBufReadPos -= delayChange; - - // Check if delay forces a read position wrap - while (aecm->farBufReadPos < 0) - { - aecm->farBufReadPos += FAR_BUF_LEN; - } - while (aecm->farBufReadPos > FAR_BUF_LEN - 1) - { - aecm->farBufReadPos -= FAR_BUF_LEN; - } - - aecm->lastKnownDelay = knownDelay; - - // Check if read position must be wrapped - while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) - { - - // Read from remaining buffer space before wrapping - readLen = FAR_BUF_LEN - aecm->farBufReadPos; - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(WebRtc_Word16) * readLen); - aecm->farBufReadPos = 0; - readPos = readLen; - readLen = farLen - readLen; - } - memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos, - sizeof(WebRtc_Word16) * readLen); - aecm->farBufReadPos += readLen; -} diff --git a/modules/audio_processing/aecm/main/source/aecm_core.h b/modules/audio_processing/aecm/main/source/aecm_core.h deleted file mode 100644 index a85b3ff44..000000000 --- a/modules/audio_processing/aecm/main/source/aecm_core.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Performs echo control (suppression) with fft routines in fixed-point - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_SOURCE_AECM_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AECM_MAIN_SOURCE_AECM_CORE_H_ - -#define AECM_DYNAMIC_Q // turn on/off dynamic Q-domain -//#define AECM_WITH_ABS_APPROX -//#define AECM_SHORT // for 32 sample partition length (otherwise 64) - -#include "typedefs.h" -#include "signal_processing_library.h" -#include "typedefs.h" - -// Algorithm parameters - -#define FRAME_LEN 80 // Total frame length, 10 ms -#ifdef AECM_SHORT - -#define PART_LEN 32 // Length of partition -#define PART_LEN_SHIFT 6 // Length of (PART_LEN * 2) in base 2 - -#else - -#define PART_LEN 64 // Length of partition -#define PART_LEN_SHIFT 7 // Length of (PART_LEN * 2) in base 2 - -#endif - -#define PART_LEN1 (PART_LEN + 1) // Unique fft coefficients -#define PART_LEN2 (PART_LEN << 1) // Length of partition * 2 -#define PART_LEN4 (PART_LEN << 2) // Length of partition * 4 -#define FAR_BUF_LEN PART_LEN4 // Length of buffers -#define MAX_DELAY 100 - -// Counter parameters -#ifdef AECM_SHORT - -#define CONV_LEN 1024 // Convergence length used at startup -#else - -#define CONV_LEN 512 // Convergence length used at startup -#endif - -#define CONV_LEN2 (CONV_LEN << 1) // Convergence length * 2 used at startup -// Energy parameters -#define MAX_BUF_LEN 64 // History length of energy signals -#define FAR_ENERGY_MIN 1025 // Lowest Far energy level: At least 2 in energy -#define FAR_ENERGY_DIFF 511 // Difference between max and min should be at least 2 (Q8) -#define ENERGY_DEV_OFFSET 0 // The energy error offset in Q8 -#define ENERGY_DEV_TOL 400 // The energy estimation tolerance in Q8 -#define FAR_ENERGY_VAD_REGION 230 // Far VAD tolerance region -// Stepsize parameters -#define MU_MIN 10 // Min stepsize 2^-MU_MIN (far end energy dependent) -#define MU_MAX 1 // Max stepsize 2^-MU_MAX (far end energy dependent) -#define MU_DIFF 9 // MU_MIN - MU_MAX -// Channel parameters -#define MIN_MSE_COUNT 20 // Min number of consecutive blocks with enough far end - // energy to compare channel estimates -#define MIN_MSE_DIFF 29 // The ratio between adapted and stored channel to - // accept a new storage (0.8 in Q-MSE_RESOLUTION) -#define MSE_RESOLUTION 5 // MSE parameter resolution -#define RESOLUTION_CHANNEL16 12 // W16 Channel in Q-RESOLUTION_CHANNEL16 -#define RESOLUTION_CHANNEL32 28 // W32 Channel in Q-RESOLUTION_CHANNEL -#define CHANNEL_VAD 16 // Minimum energy in frequency band to update channel -// Suppression gain parameters: SUPGAIN_ parameters in Q-(RESOLUTION_SUPGAIN) -#define RESOLUTION_SUPGAIN 8 // Channel in Q-(RESOLUTION_SUPGAIN) -#define SUPGAIN_DEFAULT (1 << RESOLUTION_SUPGAIN) // Default suppression gain -#define SUPGAIN_ERROR_PARAM_A 3072 // Estimation error parameter (Maximum gain) (8 in Q8) -#define SUPGAIN_ERROR_PARAM_B 1536 // Estimation error parameter (Gain before going down) -#define SUPGAIN_ERROR_PARAM_D SUPGAIN_DEFAULT // Estimation error parameter - // (Should be the same as Default) (1 in Q8) -#define SUPGAIN_EPC_DT 200 // = SUPGAIN_ERROR_PARAM_C * ENERGY_DEV_TOL -// Defines for "check delay estimation" -#define CORR_WIDTH 31 // Number of samples to correlate over. -#define CORR_MAX 16 // Maximum correlation offset -#define CORR_MAX_BUF 63 -#define CORR_DEV 4 -#define CORR_MAX_LEVEL 20 -#define CORR_MAX_LOW 4 -#define CORR_BUF_LEN (CORR_MAX << 1) + 1 -// Note that CORR_WIDTH + 2*CORR_MAX <= MAX_BUF_LEN - -#define ONE_Q14 (1 << 14) - -// NLP defines -#define NLP_COMP_LOW 3277 // 0.2 in Q14 -#define NLP_COMP_HIGH ONE_Q14 // 1 in Q14 - -typedef struct -{ - int farBufWritePos; - int farBufReadPos; - int knownDelay; - int lastKnownDelay; - - void *farFrameBuf; - void *nearNoisyFrameBuf; - void *nearCleanFrameBuf; - void *outFrameBuf; - - WebRtc_Word16 xBuf[PART_LEN2]; // farend - WebRtc_Word16 dBufClean[PART_LEN2]; // nearend - WebRtc_Word16 dBufNoisy[PART_LEN2]; // nearend - WebRtc_Word16 outBuf[PART_LEN]; - - WebRtc_Word16 farBuf[FAR_BUF_LEN]; - - WebRtc_Word16 mult; - WebRtc_UWord32 seed; - - // Delay estimation variables - WebRtc_UWord16 medianYlogspec[PART_LEN1]; - WebRtc_UWord16 medianXlogspec[PART_LEN1]; - WebRtc_UWord16 medianBCount[MAX_DELAY]; - WebRtc_UWord16 xfaHistory[PART_LEN1][MAX_DELAY]; - WebRtc_Word16 delHistoryPos; - WebRtc_UWord32 bxHistory[MAX_DELAY]; - WebRtc_UWord16 currentDelay; - WebRtc_UWord16 previousDelay; - WebRtc_Word16 delayAdjust; - - WebRtc_Word16 nlpFlag; - WebRtc_Word16 fixedDelay; - - WebRtc_UWord32 totCount; - - WebRtc_Word16 xfaQDomainBuf[MAX_DELAY]; - WebRtc_Word16 dfaCleanQDomain; - WebRtc_Word16 dfaCleanQDomainOld; - WebRtc_Word16 dfaNoisyQDomain; - WebRtc_Word16 dfaNoisyQDomainOld; - - WebRtc_Word16 nearLogEnergy[MAX_BUF_LEN]; - WebRtc_Word16 farLogEnergy[MAX_BUF_LEN]; - WebRtc_Word16 echoAdaptLogEnergy[MAX_BUF_LEN]; - WebRtc_Word16 echoStoredLogEnergy[MAX_BUF_LEN]; - - WebRtc_Word16 channelAdapt16[PART_LEN1]; - WebRtc_Word32 channelAdapt32[PART_LEN1]; - WebRtc_Word16 channelStored[PART_LEN1]; - WebRtc_Word32 echoFilt[PART_LEN1]; - WebRtc_Word16 nearFilt[PART_LEN1]; - WebRtc_Word32 noiseEst[PART_LEN1]; - WebRtc_Word16 noiseEstQDomain[PART_LEN1]; - WebRtc_Word16 noiseEstCtr; - WebRtc_Word16 cngMode; - - WebRtc_Word32 mseAdaptOld; - WebRtc_Word32 mseStoredOld; - WebRtc_Word32 mseThreshold; - - WebRtc_Word16 farEnergyMin; - WebRtc_Word16 farEnergyMax; - WebRtc_Word16 farEnergyMaxMin; - WebRtc_Word16 farEnergyVAD; - WebRtc_Word16 farEnergyMSE; - WebRtc_Word16 currentVADValue; - WebRtc_Word16 vadUpdateCount; - - WebRtc_Word16 delayHistogram[MAX_DELAY]; - WebRtc_Word16 delayVadCount; - WebRtc_Word16 maxDelayHistIdx; - WebRtc_Word16 lastMinPos; - - WebRtc_Word16 startupState; - WebRtc_Word16 mseChannelCount; - WebRtc_Word16 delayCount; - WebRtc_Word16 newDelayCorrData; - WebRtc_Word16 lastDelayUpdateCount; - WebRtc_Word16 delayCorrelation[CORR_BUF_LEN]; - WebRtc_Word16 supGain; - WebRtc_Word16 supGainOld; - WebRtc_Word16 delayOffsetFlag; - - WebRtc_Word16 supGainErrParamA; - WebRtc_Word16 supGainErrParamD; - WebRtc_Word16 supGainErrParamDiffAB; - WebRtc_Word16 supGainErrParamDiffBD; - -#ifdef AEC_DEBUG - FILE *farFile; - FILE *nearFile; - FILE *outFile; -#endif -} AecmCore_t; - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_CreateCore(...) -// -// Allocates the memory needed by the AECM. The memory needs to be -// initialized separately using the WebRtcAecm_InitCore() function. -// -// Input: -// - aecm : Instance that should be created -// -// Output: -// - aecm : Created instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_CreateCore(AecmCore_t **aecm); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_InitCore(...) -// -// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...) -// Input: -// - aecm : Pointer to the AECM instance -// - samplingFreq : Sampling Frequency -// -// Output: -// - aecm : Initialized instance -// -// Return value : 0 - Ok -// -1 - Error -// -int WebRtcAecm_InitCore(AecmCore_t * const aecm, int samplingFreq); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_FreeCore(...) -// -// This function releases the memory allocated by WebRtcAecm_CreateCore() -// Input: -// - aecm : Pointer to the AECM instance -// -// Return value : 0 - Ok -// -1 - Error -// 11001-11016: Error -// -int WebRtcAecm_FreeCore(AecmCore_t *aecm); - -int WebRtcAecm_Control(AecmCore_t *aecm, int delay, int nlpFlag, int delayOffsetFlag); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_ProcessFrame(...) -// -// This function processes frames and sends blocks to WebRtcAecm_ProcessBlock(...) -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of echo signal -// - nearendNoisy : In buffer containing one frame of nearend+echo signal without NS -// - nearendClean : In buffer containing one frame of nearend+echo signal with NS -// -// Output: -// - out : Out buffer, one frame of nearend signal : -// -// -void WebRtcAecm_ProcessFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const WebRtc_Word16 * const nearendNoisy, - const WebRtc_Word16 * const nearendClean, - WebRtc_Word16 * const out); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_ProcessBlock(...) -// -// This function is called for every block within one frame -// This function is called by WebRtcAecm_ProcessFrame(...) -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one block of echo signal -// - nearendNoisy : In buffer containing one frame of nearend+echo signal without NS -// - nearendClean : In buffer containing one frame of nearend+echo signal with NS -// -// Output: -// - out : Out buffer, one block of nearend signal : -// -// -void WebRtcAecm_ProcessBlock(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const WebRtc_Word16 * const nearendNoisy, - const WebRtc_Word16 * const noisyClean, - WebRtc_Word16 * const out); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_BufferFarFrame() -// -// Inserts a frame of data into farend buffer. -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of farend signal -// - farLen : Length of frame -// -void WebRtcAecm_BufferFarFrame(AecmCore_t * const aecm, const WebRtc_Word16 * const farend, - const int farLen); - -/////////////////////////////////////////////////////////////////////////////////////////////// -// WebRtcAecm_FetchFarFrame() -// -// Read the farend buffer to account for known delay -// -// Inputs: -// - aecm : Pointer to the AECM instance -// - farend : In buffer containing one frame of farend signal -// - farLen : Length of frame -// - knownDelay : known delay -// -void WebRtcAecm_FetchFarFrame(AecmCore_t * const aecm, WebRtc_Word16 * const farend, - const int farLen, const int knownDelay); - -#endif diff --git a/modules/audio_processing/aecm/main/source/echo_control_mobile.c b/modules/audio_processing/aecm/main/source/echo_control_mobile.c deleted file mode 100644 index f9d84f0c4..000000000 --- a/modules/audio_processing/aecm/main/source/echo_control_mobile.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -//#include - -#include "echo_control_mobile.h" -#include "aecm_core.h" -#include "ring_buffer.h" -#ifdef AEC_DEBUG -#include -#endif -#ifdef MAC_IPHONE_PRINT -#include -#include -#elif defined ARM_WINM_LOG -#include "windows.h" -extern HANDLE logFile; -#endif - -#define BUF_SIZE_FRAMES 50 // buffer size (frames) -// Maximum length of resampled signal. Must be an integer multiple of frames -// (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN -// The factor of 2 handles wb, and the + 1 is as a safety margin -#define MAX_RESAMP_LEN (5 * FRAME_LEN) - -static const int kBufSizeSamp = BUF_SIZE_FRAMES * FRAME_LEN; // buffer size (samples) -static const int kSampMsNb = 8; // samples per ms in nb -// Target suppression levels for nlp modes -// log{0.001, 0.00001, 0.00000001} -static const int kInitCheck = 42; - -typedef struct -{ - int sampFreq; - int scSampFreq; - short bufSizeStart; - int knownDelay; - - // Stores the last frame added to the farend buffer - short farendOld[2][FRAME_LEN]; - short initFlag; // indicates if AEC has been initialized - - // Variables used for averaging far end buffer size - short counter; - short sum; - short firstVal; - short checkBufSizeCtr; - - // Variables used for delay shifts - short msInSndCardBuf; - short filtDelay; - int timeForDelayChange; - int ECstartup; - int checkBuffSize; - int delayChange; - short lastDelayDiff; - - WebRtc_Word16 echoMode; - -#ifdef AEC_DEBUG - FILE *bufFile; - FILE *delayFile; - FILE *preCompFile; - FILE *postCompFile; -#endif // AEC_DEBUG - // Structures - void *farendBuf; - - int lastError; - - AecmCore_t *aecmCore; -} aecmob_t; - -// Estimates delay to set the position of the farend buffer read pointer -// (controlled by knownDelay) -static int WebRtcAecm_EstBufDelay(aecmob_t *aecmInst, short msInSndCardBuf); - -// Stuffs the farend buffer if the estimated delay is too large -static int WebRtcAecm_DelayComp(aecmob_t *aecmInst); - -WebRtc_Word32 WebRtcAecm_Create(void **aecmInst) -{ - aecmob_t *aecm; - if (aecmInst == NULL) - { - return -1; - } - - aecm = malloc(sizeof(aecmob_t)); - *aecmInst = aecm; - if (aecm == NULL) - { - return -1; - } - - if (WebRtcAecm_CreateCore(&aecm->aecmCore) == -1) - { - WebRtcAecm_Free(aecm); - aecm = NULL; - return -1; - } - - if (WebRtcApm_CreateBuffer(&aecm->farendBuf, kBufSizeSamp) == -1) - { - WebRtcAecm_Free(aecm); - aecm = NULL; - return -1; - } - - aecm->initFlag = 0; - aecm->lastError = 0; - -#ifdef AEC_DEBUG - aecm->aecmCore->farFile = fopen("aecFar.pcm","wb"); - aecm->aecmCore->nearFile = fopen("aecNear.pcm","wb"); - aecm->aecmCore->outFile = fopen("aecOut.pcm","wb"); - //aecm->aecmCore->outLpFile = fopen("aecOutLp.pcm","wb"); - - aecm->bufFile = fopen("aecBuf.dat", "wb"); - aecm->delayFile = fopen("aecDelay.dat", "wb"); - aecm->preCompFile = fopen("preComp.pcm", "wb"); - aecm->postCompFile = fopen("postComp.pcm", "wb"); -#endif // AEC_DEBUG - return 0; -} - -WebRtc_Word32 WebRtcAecm_Free(void *aecmInst) -{ - aecmob_t *aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - -#ifdef AEC_DEBUG - fclose(aecm->aecmCore->farFile); - fclose(aecm->aecmCore->nearFile); - fclose(aecm->aecmCore->outFile); - //fclose(aecm->aecmCore->outLpFile); - - fclose(aecm->bufFile); - fclose(aecm->delayFile); - fclose(aecm->preCompFile); - fclose(aecm->postCompFile); -#endif // AEC_DEBUG - WebRtcAecm_FreeCore(aecm->aecmCore); - WebRtcApm_FreeBuffer(aecm->farendBuf); - free(aecm); - - return 0; -} - -WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq, WebRtc_Word32 scSampFreq) -{ - aecmob_t *aecm = aecmInst; - AecmConfig aecConfig; - - if (aecm == NULL) - { - return -1; - } - - if (sampFreq != 8000 && sampFreq != 16000) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->sampFreq = sampFreq; - - if (scSampFreq < 1 || scSampFreq > 96000) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->scSampFreq = scSampFreq; - - // Initialize AECM core - if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1) - { - aecm->lastError = AECM_UNSPECIFIED_ERROR; - return -1; - } - - // Initialize farend buffer - if (WebRtcApm_InitBuffer(aecm->farendBuf) == -1) - { - aecm->lastError = AECM_UNSPECIFIED_ERROR; - return -1; - } - - aecm->initFlag = kInitCheck; // indicates that initialization has been done - - aecm->delayChange = 1; - - aecm->sum = 0; - aecm->counter = 0; - aecm->checkBuffSize = 1; - aecm->firstVal = 0; - - aecm->ECstartup = 1; - aecm->bufSizeStart = 0; - aecm->checkBufSizeCtr = 0; - aecm->filtDelay = 0; - aecm->timeForDelayChange = 0; - aecm->knownDelay = 0; - aecm->lastDelayDiff = 0; - - memset(&aecm->farendOld[0][0], 0, 160); - - // Default settings. - aecConfig.cngMode = AecmTrue; - aecConfig.echoMode = 3; - - if (WebRtcAecm_set_config(aecm, aecConfig) == -1) - { - aecm->lastError = AECM_UNSPECIFIED_ERROR; - return -1; - } - - return 0; -} - -WebRtc_Word32 WebRtcAecm_BufferFarend(void *aecmInst, const WebRtc_Word16 *farend, - WebRtc_Word16 nrOfSamples) -{ - aecmob_t *aecm = aecmInst; - WebRtc_Word32 retVal = 0; - - if (aecm == NULL) - { - return -1; - } - - if (farend == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (nrOfSamples != 80 && nrOfSamples != 160) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - - // TODO: Is this really a good idea? - if (!aecm->ECstartup) - { - WebRtcAecm_DelayComp(aecm); - } - - WebRtcApm_WriteBuffer(aecm->farendBuf, farend, nrOfSamples); - - return retVal; -} - -WebRtc_Word32 WebRtcAecm_Process(void *aecmInst, const WebRtc_Word16 *nearendNoisy, - const WebRtc_Word16 *nearendClean, WebRtc_Word16 *out, - WebRtc_Word16 nrOfSamples, WebRtc_Word16 msInSndCardBuf) -{ - aecmob_t *aecm = aecmInst; - WebRtc_Word32 retVal = 0; - short i; - short farend[FRAME_LEN]; - short nmbrOfFilledBuffers; - short nBlocks10ms; - short nFrames; -#ifdef AEC_DEBUG - short msInAECBuf; -#endif - -#ifdef ARM_WINM_LOG - __int64 freq, start, end, diff; - unsigned int milliseconds; - DWORD temp; -#elif defined MAC_IPHONE_PRINT - // double endtime = 0, starttime = 0; - struct timeval starttime; - struct timeval endtime; - static long int timeused = 0; - static int timecount = 0; -#endif - - if (aecm == NULL) - { - return -1; - } - - if (nearendNoisy == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (out == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (nrOfSamples != 80 && nrOfSamples != 160) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - - if (msInSndCardBuf < 0) - { - msInSndCardBuf = 0; - aecm->lastError = AECM_BAD_PARAMETER_WARNING; - retVal = -1; - } else if (msInSndCardBuf > 500) - { - msInSndCardBuf = 500; - aecm->lastError = AECM_BAD_PARAMETER_WARNING; - retVal = -1; - } - msInSndCardBuf += 10; - aecm->msInSndCardBuf = msInSndCardBuf; - - nFrames = nrOfSamples / FRAME_LEN; - nBlocks10ms = nFrames / aecm->aecmCore->mult; - - if (aecm->ECstartup) - { - if (nearendClean == NULL) - { - memcpy(out, nearendNoisy, sizeof(short) * nrOfSamples); - } else - { - memcpy(out, nearendClean, sizeof(short) * nrOfSamples); - } - - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN; - // The AECM is in the start up mode - // AECM is disabled until the soundcard buffer and farend buffers are OK - - // Mechanism to ensure that the soundcard buffer is reasonably stable. - if (aecm->checkBuffSize) - { - aecm->checkBufSizeCtr++; - // Before we fill up the far end buffer we require the amount of data on the - // sound card to be stable (+/-8 ms) compared to the first value. This - // comparison is made during the following 4 consecutive frames. If it seems - // to be stable then we start to fill up the far end buffer. - - if (aecm->counter == 0) - { - aecm->firstVal = aecm->msInSndCardBuf; - aecm->sum = 0; - } - - if (abs(aecm->firstVal - aecm->msInSndCardBuf) - < WEBRTC_SPL_MAX(0.2 * aecm->msInSndCardBuf, kSampMsNb)) - { - aecm->sum += aecm->msInSndCardBuf; - aecm->counter++; - } else - { - aecm->counter = 0; - } - - if (aecm->counter * nBlocks10ms >= 6) - { - // The farend buffer size is determined in blocks of 80 samples - // Use 75% of the average value of the soundcard buffer - aecm->bufSizeStart - = WEBRTC_SPL_MIN((3 * aecm->sum - * aecm->aecmCore->mult) / (aecm->counter * 40), BUF_SIZE_FRAMES); - // buffersize has now been determined - aecm->checkBuffSize = 0; - } - - if (aecm->checkBufSizeCtr * nBlocks10ms > 50) - { - // for really bad sound cards, don't disable echocanceller for more than 0.5 sec - aecm->bufSizeStart = WEBRTC_SPL_MIN((3 * aecm->msInSndCardBuf - * aecm->aecmCore->mult) / 40, BUF_SIZE_FRAMES); - aecm->checkBuffSize = 0; - } - } - - // if checkBuffSize changed in the if-statement above - if (!aecm->checkBuffSize) - { - // soundcard buffer is now reasonably stable - // When the far end buffer is filled with approximately the same amount of - // data as the amount on the sound card we end the start up phase and start - // to cancel echoes. - - if (nmbrOfFilledBuffers == aecm->bufSizeStart) - { - aecm->ECstartup = 0; // Enable the AECM - } else if (nmbrOfFilledBuffers > aecm->bufSizeStart) - { - WebRtcApm_FlushBuffer( - aecm->farendBuf, - WebRtcApm_get_buffer_size(aecm->farendBuf) - - aecm->bufSizeStart * FRAME_LEN); - aecm->ECstartup = 0; - } - } - - } else - { - // AECM is enabled - - // Note only 1 block supported for nb and 2 blocks for wb - for (i = 0; i < nFrames; i++) - { - nmbrOfFilledBuffers = WebRtcApm_get_buffer_size(aecm->farendBuf) / FRAME_LEN; - - // Check that there is data in the far end buffer - if (nmbrOfFilledBuffers > 0) - { - // Get the next 80 samples from the farend buffer - WebRtcApm_ReadBuffer(aecm->farendBuf, farend, FRAME_LEN); - - // Always store the last frame for use when we run out of data - memcpy(&(aecm->farendOld[i][0]), farend, FRAME_LEN * sizeof(short)); - } else - { - // We have no data so we use the last played frame - memcpy(farend, &(aecm->farendOld[i][0]), FRAME_LEN * sizeof(short)); - } - - // Call buffer delay estimator when all data is extracted, - // i,e. i = 0 for NB and i = 1 for WB - if ((i == 0 && aecm->sampFreq == 8000) || (i == 1 && aecm->sampFreq == 16000)) - { - WebRtcAecm_EstBufDelay(aecm, aecm->msInSndCardBuf); - } - -#ifdef ARM_WINM_LOG - // measure tick start - QueryPerformanceFrequency((LARGE_INTEGER*)&freq); - QueryPerformanceCounter((LARGE_INTEGER*)&start); -#elif defined MAC_IPHONE_PRINT - // starttime = clock()/(double)CLOCKS_PER_SEC; - gettimeofday(&starttime, NULL); -#endif - // Call the AECM - /*WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearend[FRAME_LEN * i], - &out[FRAME_LEN * i], aecm->knownDelay);*/ - if (nearendClean == NULL) - { - WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i], - NULL, &out[FRAME_LEN * i]); - } else - { - WebRtcAecm_ProcessFrame(aecm->aecmCore, farend, &nearendNoisy[FRAME_LEN * i], - &nearendClean[FRAME_LEN * i], &out[FRAME_LEN * i]); - } - -#ifdef ARM_WINM_LOG - - // measure tick end - QueryPerformanceCounter((LARGE_INTEGER*)&end); - - if(end > start) - { - diff = ((end - start) * 1000) / (freq/1000); - milliseconds = (unsigned int)(diff & 0xffffffff); - WriteFile (logFile, &milliseconds, sizeof(unsigned int), &temp, NULL); - } -#elif defined MAC_IPHONE_PRINT - // endtime = clock()/(double)CLOCKS_PER_SEC; - // printf("%f\n", endtime - starttime); - - gettimeofday(&endtime, NULL); - - if( endtime.tv_usec > starttime.tv_usec) - { - timeused += endtime.tv_usec - starttime.tv_usec; - } else - { - timeused += endtime.tv_usec + 1000000 - starttime.tv_usec; - } - - if(++timecount == 1000) - { - timecount = 0; - printf("AEC: %ld\n", timeused); - timeused = 0; - } -#endif - - } - } - -#ifdef AEC_DEBUG - msInAECBuf = WebRtcApm_get_buffer_size(aecm->farendBuf) / (kSampMsNb*aecm->aecmCore->mult); - fwrite(&msInAECBuf, 2, 1, aecm->bufFile); - fwrite(&(aecm->knownDelay), sizeof(aecm->knownDelay), 1, aecm->delayFile); -#endif - - return retVal; -} - -WebRtc_Word32 WebRtcAecm_set_config(void *aecmInst, AecmConfig config) -{ - aecmob_t *aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - if (config.cngMode != AecmFalse && config.cngMode != AecmTrue) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->aecmCore->cngMode = config.cngMode; - - if (config.echoMode < 0 || config.echoMode > 4) - { - aecm->lastError = AECM_BAD_PARAMETER_ERROR; - return -1; - } - aecm->echoMode = config.echoMode; - - if (aecm->echoMode == 0) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 3; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 3; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 3; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 3) - - (SUPGAIN_ERROR_PARAM_B >> 3); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 3) - - (SUPGAIN_ERROR_PARAM_D >> 3); - } else if (aecm->echoMode == 1) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 2; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 2; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 2; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 2) - - (SUPGAIN_ERROR_PARAM_B >> 2); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 2) - - (SUPGAIN_ERROR_PARAM_D >> 2); - } else if (aecm->echoMode == 2) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT >> 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A >> 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D >> 1; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A >> 1) - - (SUPGAIN_ERROR_PARAM_B >> 1); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B >> 1) - - (SUPGAIN_ERROR_PARAM_D >> 1); - } else if (aecm->echoMode == 3) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D; - aecm->aecmCore->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B; - aecm->aecmCore->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D; - } else if (aecm->echoMode == 4) - { - aecm->aecmCore->supGain = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainOld = SUPGAIN_DEFAULT << 1; - aecm->aecmCore->supGainErrParamA = SUPGAIN_ERROR_PARAM_A << 1; - aecm->aecmCore->supGainErrParamD = SUPGAIN_ERROR_PARAM_D << 1; - aecm->aecmCore->supGainErrParamDiffAB = (SUPGAIN_ERROR_PARAM_A << 1) - - (SUPGAIN_ERROR_PARAM_B << 1); - aecm->aecmCore->supGainErrParamDiffBD = (SUPGAIN_ERROR_PARAM_B << 1) - - (SUPGAIN_ERROR_PARAM_D << 1); - } - - return 0; -} - -WebRtc_Word32 WebRtcAecm_get_config(void *aecmInst, AecmConfig *config) -{ - aecmob_t *aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - if (config == NULL) - { - aecm->lastError = AECM_NULL_POINTER_ERROR; - return -1; - } - - if (aecm->initFlag != kInitCheck) - { - aecm->lastError = AECM_UNINITIALIZED_ERROR; - return -1; - } - - config->cngMode = aecm->aecmCore->cngMode; - config->echoMode = aecm->echoMode; - - return 0; -} - -WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len) -{ - const char version[] = "AECM 1.2.0"; - const short versionLen = (short)strlen(version) + 1; // +1 for null-termination - - if (versionStr == NULL) - { - return -1; - } - - if (versionLen > len) - { - return -1; - } - - strncpy(versionStr, version, versionLen); - return 0; -} - -WebRtc_Word32 WebRtcAecm_get_error_code(void *aecmInst) -{ - aecmob_t *aecm = aecmInst; - - if (aecm == NULL) - { - return -1; - } - - return aecm->lastError; -} - -static int WebRtcAecm_EstBufDelay(aecmob_t *aecm, short msInSndCardBuf) -{ - short delayNew, nSampFar, nSampSndCard; - short diff; - - nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf); - nSampSndCard = msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - - delayNew = nSampSndCard - nSampFar; - - if (delayNew < FRAME_LEN) - { - WebRtcApm_FlushBuffer(aecm->farendBuf, FRAME_LEN); - delayNew += FRAME_LEN; - } - - aecm->filtDelay = WEBRTC_SPL_MAX(0, (8 * aecm->filtDelay + 2 * delayNew) / 10); - - diff = aecm->filtDelay - aecm->knownDelay; - if (diff > 224) - { - if (aecm->lastDelayDiff < 96) - { - aecm->timeForDelayChange = 0; - } else - { - aecm->timeForDelayChange++; - } - } else if (diff < 96 && aecm->knownDelay > 0) - { - if (aecm->lastDelayDiff > 224) - { - aecm->timeForDelayChange = 0; - } else - { - aecm->timeForDelayChange++; - } - } else - { - aecm->timeForDelayChange = 0; - } - aecm->lastDelayDiff = diff; - - if (aecm->timeForDelayChange > 25) - { - aecm->knownDelay = WEBRTC_SPL_MAX((int)aecm->filtDelay - 160, 0); - } - return 0; -} - -static int WebRtcAecm_DelayComp(aecmob_t *aecm) -{ - int nSampFar, nSampSndCard, delayNew, nSampAdd; - const int maxStuffSamp = 10 * FRAME_LEN; - - nSampFar = WebRtcApm_get_buffer_size(aecm->farendBuf); - nSampSndCard = aecm->msInSndCardBuf * kSampMsNb * aecm->aecmCore->mult; - delayNew = nSampSndCard - nSampFar; - - if (delayNew > FAR_BUF_LEN - FRAME_LEN * aecm->aecmCore->mult) - { - // The difference of the buffer sizes is larger than the maximum - // allowed known delay. Compensate by stuffing the buffer. - nSampAdd = (int)(WEBRTC_SPL_MAX(((nSampSndCard >> 1) - nSampFar), - FRAME_LEN)); - nSampAdd = WEBRTC_SPL_MIN(nSampAdd, maxStuffSamp); - - WebRtcApm_StuffBuffer(aecm->farendBuf, nSampAdd); - aecm->delayChange = 1; // the delay needs to be updated - } - - return 0; -} diff --git a/modules/audio_processing/agc/main/interface/gain_control.h b/modules/audio_processing/agc/main/interface/gain_control.h deleted file mode 100644 index 2893331fa..000000000 --- a/modules/audio_processing/agc/main/interface/gain_control.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_ - -#include "typedefs.h" - -// Errors -#define AGC_UNSPECIFIED_ERROR 18000 -#define AGC_UNSUPPORTED_FUNCTION_ERROR 18001 -#define AGC_UNINITIALIZED_ERROR 18002 -#define AGC_NULL_POINTER_ERROR 18003 -#define AGC_BAD_PARAMETER_ERROR 18004 - -// Warnings -#define AGC_BAD_PARAMETER_WARNING 18050 - -enum -{ - kAgcModeUnchanged, - kAgcModeAdaptiveAnalog, - kAgcModeAdaptiveDigital, - kAgcModeFixedDigital -}; - -enum -{ - kAgcFalse = 0, - kAgcTrue -}; - -typedef struct -{ - WebRtc_Word16 targetLevelDbfs; // default 3 (-3 dBOv) - WebRtc_Word16 compressionGaindB; // default 9 dB - WebRtc_UWord8 limiterEnable; // default kAgcTrue (on) -} WebRtcAgc_config_t; - -#if defined(__cplusplus) -extern "C" -{ -#endif - -/* - * This function processes a 10/20ms frame of far-end speech to determine - * if there is active speech. Far-end speech length can be either 10ms or - * 20ms. The length of the input speech vector must be given in samples - * (80/160 when FS=8000, and 160/320 when FS=16000 or FS=32000). - * - * Input: - * - agcInst : AGC instance. - * - inFar : Far-end input speech vector (10 or 20ms) - * - samples : Number of samples in input vector - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_AddFarend(void* agcInst, - const WebRtc_Word16* inFar, - WebRtc_Word16 samples); - -/* - * This function processes a 10/20ms frame of microphone speech to determine - * if there is active speech. Microphone speech length can be either 10ms or - * 20ms. The length of the input speech vector must be given in samples - * (80/160 when FS=8000, and 160/320 when FS=16000 or FS=32000). For very low - * input levels, the input signal is increased in level by multiplying and - * overwriting the samples in inMic[]. - * - * This function should be called before any further processing of the - * near-end microphone signal. - * - * Input: - * - agcInst : AGC instance. - * - inMic : Microphone input speech vector (10 or 20 ms) for - * L band - * - inMic_H : Microphone input speech vector (10 or 20 ms) for - * H band - * - samples : Number of samples in input vector - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_AddMic(void* agcInst, - WebRtc_Word16* inMic, - WebRtc_Word16* inMic_H, - WebRtc_Word16 samples); - -/* - * This function replaces the analog microphone with a virtual one. - * It is a digital gain applied to the input signal and is used in the - * agcAdaptiveDigital mode where no microphone level is adjustable. - * Microphone speech length can be either 10ms or 20ms. The length of the - * input speech vector must be given in samples (80/160 when FS=8000, and - * 160/320 when FS=16000 or FS=32000). - * - * Input: - * - agcInst : AGC instance. - * - inMic : Microphone input speech vector for (10 or 20 ms) - * L band - * - inMic_H : Microphone input speech vector for (10 or 20 ms) - * H band - * - samples : Number of samples in input vector - * - micLevelIn : Input level of microphone (static) - * - * Output: - * - inMic : Microphone output after processing (L band) - * - inMic_H : Microphone output after processing (H band) - * - micLevelOut : Adjusted microphone level after processing - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_VirtualMic(void* agcInst, - WebRtc_Word16* inMic, - WebRtc_Word16* inMic_H, - WebRtc_Word16 samples, - WebRtc_Word32 micLevelIn, - WebRtc_Word32* micLevelOut); - -/* - * This function processes a 10/20ms frame and adjusts (normalizes) the gain - * both analog and digitally. The gain adjustments are done only during - * active periods of speech. The input speech length can be either 10ms or - * 20ms and the output is of the same length. The length of the speech - * vectors must be given in samples (80/160 when FS=8000, and 160/320 when - * FS=16000 or FS=32000). The echo parameter can be used to ensure the AGC will - * not adjust upward in the presence of echo. - * - * This function should be called after processing the near-end microphone - * signal, in any case after any echo cancellation. - * - * Input: - * - agcInst : AGC instance - * - inNear : Near-end input speech vector (10 or 20 ms) for - * L band - * - inNear_H : Near-end input speech vector (10 or 20 ms) for - * H band - * - samples : Number of samples in input/output vector - * - inMicLevel : Current microphone volume level - * - echo : Set to 0 if the signal passed to add_mic is - * almost certainly free of echo; otherwise set - * to 1. If you have no information regarding echo - * set to 0. - * - * Output: - * - outMicLevel : Adjusted microphone volume level - * - out : Gain-adjusted near-end speech vector (L band) - * : May be the same vector as the input. - * - out_H : Gain-adjusted near-end speech vector (H band) - * - saturationWarning : A returned value of 1 indicates a saturation event - * has occurred and the volume cannot be further - * reduced. Otherwise will be set to 0. - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_Process(void* agcInst, - const WebRtc_Word16* inNear, - const WebRtc_Word16* inNear_H, - WebRtc_Word16 samples, - WebRtc_Word16* out, - WebRtc_Word16* out_H, - WebRtc_Word32 inMicLevel, - WebRtc_Word32* outMicLevel, - WebRtc_Word16 echo, - WebRtc_UWord8* saturationWarning); - -/* - * This function sets the config parameters (targetLevelDbfs, - * compressionGaindB and limiterEnable). - * - * Input: - * - agcInst : AGC instance - * - config : config struct - * - * Output: - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_set_config(void* agcInst, WebRtcAgc_config_t config); - -/* - * This function returns the config parameters (targetLevelDbfs, - * compressionGaindB and limiterEnable). - * - * Input: - * - agcInst : AGC instance - * - * Output: - * - config : config struct - * - * Return value: - * : 0 - Normal operation. - * : -1 - Error - */ -int WebRtcAgc_get_config(void* agcInst, WebRtcAgc_config_t* config); - -/* - * This function creates an AGC instance, which will contain the state - * information for one (duplex) channel. - * - * Return value : AGC instance if successful - * : 0 (i.e., a NULL pointer) if unsuccessful - */ -int WebRtcAgc_Create(void **agcInst); - -/* - * This function frees the AGC instance created at the beginning. - * - * Input: - * - agcInst : AGC instance. - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcAgc_Free(void *agcInst); - -/* - * This function initializes an AGC instance. - * - * Input: - * - agcInst : AGC instance. - * - minLevel : Minimum possible mic level - * - maxLevel : Maximum possible mic level - * - agcMode : 0 - Unchanged - * : 1 - Adaptive Analog Automatic Gain Control -3dBOv - * : 2 - Adaptive Digital Automatic Gain Control -3dBOv - * : 3 - Fixed Digital Gain 0dB - * - fs : Sampling frequency - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcAgc_Init(void *agcInst, - WebRtc_Word32 minLevel, - WebRtc_Word32 maxLevel, - WebRtc_Word16 agcMode, - WebRtc_UWord32 fs); - -/* - * This function returns a text string containing the version. - * - * Input: - * - length : Length of the char array pointed to by version - * Output: - * - version : Pointer to a char array of to which the version - * : string will be copied. - * - * Return value : 0 - OK - * -1 - Error - */ -int WebRtcAgc_Version(WebRtc_Word8 *versionStr, WebRtc_Word16 length); - -#if defined(__cplusplus) -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_INTERFACE_GAIN_CONTROL_H_ diff --git a/modules/audio_processing/agc/main/matlab/getGains.m b/modules/audio_processing/agc/main/matlab/getGains.m deleted file mode 100644 index e0234b859..000000000 --- a/modules/audio_processing/agc/main/matlab/getGains.m +++ /dev/null @@ -1,32 +0,0 @@ -% Outputs a file for testing purposes. -% -% Adjust the following parameters to suit. Their purpose becomes more clear on -% viewing the gain plots. -% MaxGain: Max gain in dB -% MinGain: Min gain at overload (0 dBov) in dB -% CompRatio: Compression ratio, essentially determines the slope of the gain -% function between the max and min gains -% Knee: The smoothness of the transition to max gain (smaller is smoother) -MaxGain = 5; MinGain = 0; CompRatio = 3; Knee = 1; - -% Compute gains -zeros = 0:31; lvl = 2.^(1-zeros); -A = -10*log10(lvl) * (CompRatio - 1) / CompRatio; -B = MaxGain - MinGain; -gains = round(2^16*10.^(0.05 * (MinGain + B * ( log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / log(1/(1+exp(Knee*B)))))); -fprintf(1, '\t%i, %i, %i, %i,\n', gains); - -% Save gains to file -fid = fopen('gains', 'wb'); -if fid == -1 - error(sprintf('Unable to open file %s', filename)); - return -end -fwrite(fid, gains, 'int32'); -fclose(fid); - -% Plotting -in = 10*log10(lvl); out = 20*log10(gains/65536); -subplot(121); plot(in, out); axis([-60, 0, -5, 30]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)'); -subplot(122); plot(in, in+out); axis([-60, 0, -60, 10]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)'); -zoom on; diff --git a/modules/audio_processing/agc/main/source/Android.mk b/modules/audio_processing/agc/main/source/Android.mk deleted file mode 100644 index 2fd97bdf8..000000000 --- a/modules/audio_processing/agc/main/source/Android.mk +++ /dev/null @@ -1,46 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_agc -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := analog_agc.c \ - digital_agc.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := -# Duplicate the static libraries to fix circular references -LOCAL_STATIC_LIBRARIES += $(LOCAL_STATIC_LIBRARIES) - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_processing/agc/main/source/agc.gyp b/modules/audio_processing/agc/main/source/agc.gyp deleted file mode 100644 index e28a4c8c6..000000000 --- a/modules/audio_processing/agc/main/source/agc.gyp +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'agc', - 'type': '<(library)', - 'dependencies': [ - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/gain_control.h', - 'analog_agc.c', - 'analog_agc.h', - 'digital_agc.c', - 'digital_agc.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/agc/main/source/analog_agc.c b/modules/audio_processing/agc/main/source/analog_agc.c deleted file mode 100644 index dfb7adc62..000000000 --- a/modules/audio_processing/agc/main/source/analog_agc.c +++ /dev/null @@ -1,1700 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* analog_agc.c - * - * Using a feedback system, determines an appropriate analog volume level - * given an input signal and current volume level. Targets a conservative - * signal level and is intended for use with a digital AGC to apply - * additional gain. - * - */ - -#include -#include -#ifdef AGC_DEBUG //test log -#include -#endif -#include "analog_agc.h" - -/* The slope of in Q13*/ -static const WebRtc_Word16 kSlope1[8] = {21793, 12517, 7189, 4129, 2372, 1362, 472, 78}; - -/* The offset in Q14 */ -static const WebRtc_Word16 kOffset1[8] = {25395, 23911, 22206, 20737, 19612, 18805, 17951, - 17367}; - -/* The slope of in Q13*/ -static const WebRtc_Word16 kSlope2[8] = {2063, 1731, 1452, 1218, 1021, 857, 597, 337}; - -/* The offset in Q14 */ -static const WebRtc_Word16 kOffset2[8] = {18432, 18379, 18290, 18177, 18052, 17920, 17670, - 17286}; - -static const WebRtc_Word16 kMuteGuardTimeMs = 8000; -static const WebRtc_Word16 kInitCheck = 42; - -/* Default settings if config is not used */ -#define AGC_DEFAULT_TARGET_LEVEL 3 -#define AGC_DEFAULT_COMP_GAIN 9 -/* This is the target level for the analog part in ENV scale. To convert to RMS scale you - * have to add OFFSET_ENV_TO_RMS. - */ -#define ANALOG_TARGET_LEVEL 11 -#define ANALOG_TARGET_LEVEL_2 5 // ANALOG_TARGET_LEVEL / 2 -/* Offset between RMS scale (analog part) and ENV scale (digital part). This value actually - * varies with the FIXED_ANALOG_TARGET_LEVEL, hence we should in the future replace it with - * a table. - */ -#define OFFSET_ENV_TO_RMS 9 -/* The reference input level at which the digital part gives an output of targetLevelDbfs - * (desired level) if we have no compression gain. This level should be set high enough not - * to compress the peaks due to the dynamics. - */ -#define DIGITAL_REF_AT_0_COMP_GAIN 4 -/* Speed of reference level decrease. - */ -#define DIFF_REF_TO_ANALOG 5 - -#ifdef MIC_LEVEL_FEEDBACK -#define NUM_BLOCKS_IN_SAT_BEFORE_CHANGE_TARGET 7 -#endif -/* Size of analog gain table */ -#define GAIN_TBL_LEN 32 -/* Matlab code: - * fprintf(1, '\t%i, %i, %i, %i,\n', round(10.^(linspace(0,10,32)/20) * 2^12)); - */ -/* Q12 */ -static const WebRtc_UWord16 kGainTableAnalog[GAIN_TBL_LEN] = {4096, 4251, 4412, 4579, 4752, - 4932, 5118, 5312, 5513, 5722, 5938, 6163, 6396, 6638, 6889, 7150, 7420, 7701, 7992, - 8295, 8609, 8934, 9273, 9623, 9987, 10365, 10758, 11165, 11587, 12025, 12480, 12953}; - -/* Gain/Suppression tables for virtual Mic (in Q10) */ -static const WebRtc_UWord16 kGainTableVirtualMic[128] = {1052, 1081, 1110, 1141, 1172, 1204, - 1237, 1271, 1305, 1341, 1378, 1416, 1454, 1494, 1535, 1577, 1620, 1664, 1710, 1757, - 1805, 1854, 1905, 1957, 2010, 2065, 2122, 2180, 2239, 2301, 2364, 2428, 2495, 2563, - 2633, 2705, 2779, 2855, 2933, 3013, 3096, 3180, 3267, 3357, 3449, 3543, 3640, 3739, - 3842, 3947, 4055, 4166, 4280, 4397, 4517, 4640, 4767, 4898, 5032, 5169, 5311, 5456, - 5605, 5758, 5916, 6078, 6244, 6415, 6590, 6770, 6956, 7146, 7341, 7542, 7748, 7960, - 8178, 8402, 8631, 8867, 9110, 9359, 9615, 9878, 10148, 10426, 10711, 11004, 11305, - 11614, 11932, 12258, 12593, 12938, 13292, 13655, 14029, 14412, 14807, 15212, 15628, - 16055, 16494, 16945, 17409, 17885, 18374, 18877, 19393, 19923, 20468, 21028, 21603, - 22194, 22801, 23425, 24065, 24724, 25400, 26095, 26808, 27541, 28295, 29069, 29864, - 30681, 31520, 32382}; -static const WebRtc_UWord16 kSuppressionTableVirtualMic[128] = {1024, 1006, 988, 970, 952, - 935, 918, 902, 886, 870, 854, 839, 824, 809, 794, 780, 766, 752, 739, 726, 713, 700, - 687, 675, 663, 651, 639, 628, 616, 605, 594, 584, 573, 563, 553, 543, 533, 524, 514, - 505, 496, 487, 478, 470, 461, 453, 445, 437, 429, 421, 414, 406, 399, 392, 385, 378, - 371, 364, 358, 351, 345, 339, 333, 327, 321, 315, 309, 304, 298, 293, 288, 283, 278, - 273, 268, 263, 258, 254, 249, 244, 240, 236, 232, 227, 223, 219, 215, 211, 208, 204, - 200, 197, 193, 190, 186, 183, 180, 176, 173, 170, 167, 164, 161, 158, 155, 153, 150, - 147, 145, 142, 139, 137, 134, 132, 130, 127, 125, 123, 121, 118, 116, 114, 112, 110, - 108, 106, 104, 102}; - -/* Table for target energy levels. Values in Q(-7) - * Matlab code - * targetLevelTable = fprintf('%d,\t%d,\t%d,\t%d,\n', round((32767*10.^(-(0:63)'/20)).^2*16/2^7) */ - -static const WebRtc_Word32 kTargetLevelTable[64] = {134209536, 106606424, 84680493, 67264106, - 53429779, 42440782, 33711911, 26778323, 21270778, 16895980, 13420954, 10660642, - 8468049, 6726411, 5342978, 4244078, 3371191, 2677832, 2127078, 1689598, 1342095, - 1066064, 846805, 672641, 534298, 424408, 337119, 267783, 212708, 168960, 134210, - 106606, 84680, 67264, 53430, 42441, 33712, 26778, 21271, 16896, 13421, 10661, 8468, - 6726, 5343, 4244, 3371, 2678, 2127, 1690, 1342, 1066, 847, 673, 534, 424, 337, 268, - 213, 169, 134, 107, 85, 67}; - -int WebRtcAgc_AddMic(void *state, WebRtc_Word16 *in_mic, WebRtc_Word16 *in_mic_H, - WebRtc_Word16 samples) -{ - WebRtc_Word32 nrg, max_nrg, sample, tmp32; - WebRtc_Word32 *ptr; - WebRtc_UWord16 targetGainIdx, gain; - WebRtc_Word16 i, n, L, M, subFrames, tmp16, tmp_speech[16]; - Agc_t *stt; - stt = (Agc_t *)state; - - //default/initial values corresponding to 10ms for wb and swb - M = 10; - L = 16; - subFrames = 160; - - if (stt->fs == 8000) - { - if (samples == 80) - { - subFrames = 80; - M = 10; - L = 8; - } else if (samples == 160) - { - subFrames = 80; - M = 20; - L = 8; - } else - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_mic, frame %d: Invalid number of samples\n\n", - (stt->fcount + 1)); -#endif - return -1; - } - } else if (stt->fs == 16000) - { - if (samples == 160) - { - subFrames = 160; - M = 10; - L = 16; - } else if (samples == 320) - { - subFrames = 160; - M = 20; - L = 16; - } else - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_mic, frame %d: Invalid number of samples\n\n", - (stt->fcount + 1)); -#endif - return -1; - } - } else if (stt->fs == 32000) - { - /* SWB is processed as 160 sample for L and H bands */ - if (samples == 160) - { - subFrames = 160; - M = 10; - L = 16; - } else - { -#ifdef AGC_DEBUG - fprintf(stt->fpt, - "AGC->add_mic, frame %d: Invalid sample rate\n\n", - (stt->fcount + 1)); -#endif - return -1; - } - } - - /* Check for valid pointers based on sampling rate */ - if ((stt->fs == 32000) && (in_mic_H == NULL)) - { - return -1; - } - /* Check for valid pointer for low band */ - if (in_mic == NULL) - { - return -1; - } - - /* apply slowly varying digital gain */ - if (stt->micVol > stt->maxAnalog) - { - /* Q1 */ - tmp16 = (WebRtc_Word16)(stt->micVol - stt->maxAnalog); - tmp32 = WEBRTC_SPL_MUL_16_16(GAIN_TBL_LEN - 1, tmp16); - tmp16 = (WebRtc_Word16)(stt->maxLevel - stt->maxAnalog); - targetGainIdx = (WebRtc_UWord16)WEBRTC_SPL_DIV(tmp32, tmp16); - assert(targetGainIdx < GAIN_TBL_LEN); - - /* Increment through the table towards the target gain. - * If micVol drops below maxAnalog, we allow the gain - * to be dropped immediately. */ - if (stt->gainTableIdx < targetGainIdx) - { - stt->gainTableIdx++; - } else if (stt->gainTableIdx > targetGainIdx) - { - stt->gainTableIdx--; - } - - /* Q12 */ - gain = kGainTableAnalog[stt->gainTableIdx]; - - for (i = 0; i < samples; i++) - { - // For lower band - tmp32 = WEBRTC_SPL_MUL_16_U16(in_mic[i], gain); - sample = WEBRTC_SPL_RSHIFT_W32(tmp32, 12); - if (sample > 32767) - { - in_mic[i] = 32767; - } else if (sample < -32768) - { - in_mic[i] = -32768; - } else - { - in_mic[i] = (WebRtc_Word16)sample; - } - - // For higher band - if (stt->fs == 32000) - { - tmp32 = WEBRTC_SPL_MUL_16_U16(in_mic_H[i], gain); - sample = WEBRTC_SPL_RSHIFT_W32(tmp32, 12); - if (sample > 32767) - { - in_mic_H[i] = 32767; - } else if (sample < -32768) - { - in_mic_H[i] = -32768; - } else - { - in_mic_H[i] = (WebRtc_Word16)sample; - } - } - } - } else - { - stt->gainTableIdx = 0; - } - - /* compute envelope */ - if ((M == 10) && (stt->inQueue > 0)) - { - ptr = stt->env[1]; - } else - { - ptr = stt->env[0]; - } - - for (i = 0; i < M; i++) - { - /* iterate over samples */ - max_nrg = 0; - for (n = 0; n < L; n++) - { - nrg = WEBRTC_SPL_MUL_16_16(in_mic[i * L + n], in_mic[i * L + n]); - if (nrg > max_nrg) - { - max_nrg = nrg; - } - } - ptr[i] = max_nrg; - } - - /* compute energy */ - if ((M == 10) && (stt->inQueue > 0)) - { - ptr = stt->Rxx16w32_array[1]; - } else - { - ptr = stt->Rxx16w32_array[0]; - } - - for (i = 0; i < WEBRTC_SPL_RSHIFT_W16(M, 1); i++) - { - if (stt->fs == 16000) - { - WebRtcSpl_DownsampleBy2(&in_mic[i * 32], 32, tmp_speech, stt->filterState); - } else - { - memcpy(tmp_speech, &in_mic[i * 16], 16 * sizeof(short)); - } - /* Compute energy in blocks of 16 samples */ - ptr[i] = WebRtcSpl_DotProductWithScale(tmp_speech, tmp_speech, 16, 4); - } - - /* update queue information */ - if ((stt->inQueue == 0) && (M == 10)) - { - stt->inQueue = 1; - } else - { - stt->inQueue = 2; - } - - /* call VAD (use low band only) */ - for (i = 0; i < samples; i += subFrames) - { - WebRtcAgc_ProcessVad(&stt->vadMic, &in_mic[i], subFrames); - } - - return 0; -} - -int WebRtcAgc_AddFarend(void *state, const WebRtc_Word16 *in_far, WebRtc_Word16 samples) -{ - WebRtc_Word32 errHandle = 0; - WebRtc_Word16 i, subFrames; - Agc_t *stt; - stt = (Agc_t *)state; - - if (stt == NULL) - { - return -1; - } - - if (stt->fs == 8000) - { - if ((samples != 80) && (samples != 160)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_far_end, frame %d: Invalid number of samples\n\n", - stt->fcount); -#endif - return -1; - } - subFrames = 80; - } else if (stt->fs == 16000) - { - if ((samples != 160) && (samples != 320)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_far_end, frame %d: Invalid number of samples\n\n", - stt->fcount); -#endif - return -1; - } - subFrames = 160; - } else if (stt->fs == 32000) - { - if ((samples != 160) && (samples != 320)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_far_end, frame %d: Invalid number of samples\n\n", - stt->fcount); -#endif - return -1; - } - subFrames = 160; - } else - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->add_far_end, frame %d: Invalid sample rate\n\n", - stt->fcount + 1); -#endif - return -1; - } - - for (i = 0; i < samples; i += subFrames) - { - errHandle += WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, &in_far[i], subFrames); - } - - return errHandle; -} - -int WebRtcAgc_VirtualMic(void *agcInst, WebRtc_Word16 *in_near, WebRtc_Word16 *in_near_H, - WebRtc_Word16 samples, WebRtc_Word32 micLevelIn, - WebRtc_Word32 *micLevelOut) -{ - WebRtc_Word32 tmpFlt, micLevelTmp, gainIdx; - WebRtc_UWord16 gain; - WebRtc_Word16 ii; - Agc_t *stt; - - WebRtc_UWord32 nrg; - WebRtc_Word16 sampleCntr; - WebRtc_UWord32 frameNrg = 0; - WebRtc_UWord32 frameNrgLimit = 5500; - WebRtc_Word16 numZeroCrossing = 0; - const WebRtc_Word16 kZeroCrossingLowLim = 15; - const WebRtc_Word16 kZeroCrossingHighLim = 20; - - stt = (Agc_t *)agcInst; - - /* - * Before applying gain decide if this is a low-level signal. - * The idea is that digital AGC will not adapt to low-level - * signals. - */ - if (stt->fs != 8000) - { - frameNrgLimit = frameNrgLimit << 1; - } - - frameNrg = WEBRTC_SPL_MUL_16_16(in_near[0], in_near[0]); - for (sampleCntr = 1; sampleCntr < samples; sampleCntr++) - { - - // increment frame energy if it is less than the limit - // the correct value of the energy is not important - if (frameNrg < frameNrgLimit) - { - nrg = WEBRTC_SPL_MUL_16_16(in_near[sampleCntr], in_near[sampleCntr]); - frameNrg += nrg; - } - - // Count the zero crossings - numZeroCrossing += ((in_near[sampleCntr] ^ in_near[sampleCntr - 1]) < 0); - } - - if ((frameNrg < 500) || (numZeroCrossing <= 5)) - { - stt->lowLevelSignal = 1; - } else if (numZeroCrossing <= kZeroCrossingLowLim) - { - stt->lowLevelSignal = 0; - } else if (frameNrg <= frameNrgLimit) - { - stt->lowLevelSignal = 1; - } else if (numZeroCrossing >= kZeroCrossingHighLim) - { - stt->lowLevelSignal = 1; - } else - { - stt->lowLevelSignal = 0; - } - - micLevelTmp = WEBRTC_SPL_LSHIFT_W32(micLevelIn, stt->scale); - /* Set desired level */ - gainIdx = stt->micVol; - if (stt->micVol > stt->maxAnalog) - { - gainIdx = stt->maxAnalog; - } - if (micLevelTmp != stt->micRef) - { - /* Something has happened with the physical level, restart. */ - stt->micRef = micLevelTmp; - stt->micVol = 127; - *micLevelOut = 127; - stt->micGainIdx = 127; - gainIdx = 127; - } - /* Pre-process the signal to emulate the microphone level. */ - /* Take one step at a time in the gain table. */ - if (gainIdx > 127) - { - gain = kGainTableVirtualMic[gainIdx - 128]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - for (ii = 0; ii < samples; ii++) - { - tmpFlt = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_U16(in_near[ii], gain), 10); - if (tmpFlt > 32767) - { - tmpFlt = 32767; - gainIdx--; - if (gainIdx >= 127) - { - gain = kGainTableVirtualMic[gainIdx - 127]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - } - if (tmpFlt < -32768) - { - tmpFlt = -32768; - gainIdx--; - if (gainIdx >= 127) - { - gain = kGainTableVirtualMic[gainIdx - 127]; - } else - { - gain = kSuppressionTableVirtualMic[127 - gainIdx]; - } - } - in_near[ii] = (WebRtc_Word16)tmpFlt; - if (stt->fs == 32000) - { - tmpFlt = WEBRTC_SPL_MUL_16_U16(in_near_H[ii], gain); - tmpFlt = WEBRTC_SPL_RSHIFT_W32(tmpFlt, 10); - if (tmpFlt > 32767) - { - tmpFlt = 32767; - } - if (tmpFlt < -32768) - { - tmpFlt = -32768; - } - in_near_H[ii] = (WebRtc_Word16)tmpFlt; - } - } - /* Set the level we (finally) used */ - stt->micGainIdx = gainIdx; -// *micLevelOut = stt->micGainIdx; - *micLevelOut = WEBRTC_SPL_RSHIFT_W32(stt->micGainIdx, stt->scale); - /* Add to Mic as if it was the output from a true microphone */ - if (WebRtcAgc_AddMic(agcInst, in_near, in_near_H, samples) != 0) - { - return -1; - } - return 0; -} - -void WebRtcAgc_UpdateAgcThresholds(Agc_t *stt) -{ - - WebRtc_Word16 tmp16; -#ifdef MIC_LEVEL_FEEDBACK - int zeros; - - if (stt->micLvlSat) - { - /* Lower the analog target level since we have reached its maximum */ - zeros = WebRtcSpl_NormW32(stt->Rxx160_LPw32); - stt->targetIdxOffset = WEBRTC_SPL_RSHIFT_W16((3 * zeros) - stt->targetIdx - 2, 2); - } -#endif - - /* Set analog target level in envelope dBOv scale */ - tmp16 = (DIFF_REF_TO_ANALOG * stt->compressionGaindB) + ANALOG_TARGET_LEVEL_2; - tmp16 = WebRtcSpl_DivW32W16ResW16((WebRtc_Word32)tmp16, ANALOG_TARGET_LEVEL); - stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN + tmp16; - if (stt->analogTarget < DIGITAL_REF_AT_0_COMP_GAIN) - { - stt->analogTarget = DIGITAL_REF_AT_0_COMP_GAIN; - } - if (stt->agcMode == kAgcModeFixedDigital) - { - /* Adjust for different parameter interpretation in FixedDigital mode */ - stt->analogTarget = stt->compressionGaindB; - } -#ifdef MIC_LEVEL_FEEDBACK - stt->analogTarget += stt->targetIdxOffset; -#endif - /* Since the offset between RMS and ENV is not constant, we should make this into a - * table, but for now, we'll stick with a constant, tuned for the chosen analog - * target level. - */ - stt->targetIdx = ANALOG_TARGET_LEVEL + OFFSET_ENV_TO_RMS; -#ifdef MIC_LEVEL_FEEDBACK - stt->targetIdx += stt->targetIdxOffset; -#endif - /* Analog adaptation limits */ - /* analogTargetLevel = round((32767*10^(-targetIdx/20))^2*16/2^7) */ - stt->analogTargetLevel = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx]; /* ex. -20 dBov */ - stt->startUpperLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 1];/* -19 dBov */ - stt->startLowerLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 1];/* -21 dBov */ - stt->upperPrimaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 2];/* -18 dBov */ - stt->lowerPrimaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 2];/* -22 dBov */ - stt->upperSecondaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx - 5];/* -15 dBov */ - stt->lowerSecondaryLimit = RXX_BUFFER_LEN * kTargetLevelTable[stt->targetIdx + 5];/* -25 dBov */ - stt->upperLimit = stt->startUpperLimit; - stt->lowerLimit = stt->startLowerLimit; -} - -void WebRtcAgc_SaturationCtrl(Agc_t *stt, WebRtc_UWord8 *saturated, WebRtc_Word32 *env) -{ - WebRtc_Word16 i, tmpW16; - - /* Check if the signal is saturated */ - for (i = 0; i < 10; i++) - { - tmpW16 = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(env[i], 20); - if (tmpW16 > 875) - { - stt->envSum += tmpW16; - } - } - - if (stt->envSum > 25000) - { - *saturated = 1; - stt->envSum = 0; - } - - /* stt->envSum *= 0.99; */ - stt->envSum = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(stt->envSum, - (WebRtc_Word16)32440, 15); -} - -void WebRtcAgc_ZeroCtrl(Agc_t *stt, WebRtc_Word32 *inMicLevel, WebRtc_Word32 *env) -{ - WebRtc_Word16 i; - WebRtc_Word32 tmp32 = 0; - WebRtc_Word32 midVal; - - /* Is the input signal zero? */ - for (i = 0; i < 10; i++) - { - tmp32 += env[i]; - } - - /* Each block is allowed to have a few non-zero - * samples. - */ - if (tmp32 < 500) - { - stt->msZero += 10; - } else - { - stt->msZero = 0; - } - - if (stt->muteGuardMs > 0) - { - stt->muteGuardMs -= 10; - } - - if (stt->msZero > 500) - { - stt->msZero = 0; - - /* Increase microphone level only if it's less than 50% */ - midVal = WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog + stt->minLevel + 1, 1); - if (*inMicLevel < midVal) - { - /* *inMicLevel *= 1.1; */ - tmp32 = WEBRTC_SPL_MUL(1126, *inMicLevel); - *inMicLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 10); - /* Reduces risk of a muted mic repeatedly triggering excessive levels due - * to zero signal detection. */ - *inMicLevel = WEBRTC_SPL_MIN(*inMicLevel, stt->zeroCtrlMax); - stt->micVol = *inMicLevel; - } - -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\t\tAGC->zeroCntrl, frame %d: 500 ms under threshold, micVol:\n", - stt->fcount, stt->micVol); -#endif - - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - /* The AGC has a tendency (due to problems with the VAD parameters), to - * vastly increase the volume after a muting event. This timer prevents - * upwards adaptation for a short period. */ - stt->muteGuardMs = kMuteGuardTimeMs; - } -} - -void WebRtcAgc_SpeakerInactiveCtrl(Agc_t *stt) -{ - /* Check if the near end speaker is inactive. - * If that is the case the VAD threshold is - * increased since the VAD speech model gets - * more sensitive to any sound after a long - * silence. - */ - - WebRtc_Word32 tmp32; - WebRtc_Word16 vadThresh; - - if (stt->vadMic.stdLongTerm < 2500) - { - stt->vadThreshold = 1500; - } else - { - vadThresh = kNormalVadThreshold; - if (stt->vadMic.stdLongTerm < 4500) - { - /* Scale between min and max threshold */ - vadThresh += WEBRTC_SPL_RSHIFT_W16(4500 - stt->vadMic.stdLongTerm, 1); - } - - /* stt->vadThreshold = (31 * stt->vadThreshold + vadThresh) / 32; */ - tmp32 = (WebRtc_Word32)vadThresh; - tmp32 += WEBRTC_SPL_MUL_16_16((WebRtc_Word16)31, stt->vadThreshold); - stt->vadThreshold = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 5); - } -} - -void WebRtcAgc_ExpCurve(WebRtc_Word16 volume, WebRtc_Word16 *index) -{ - // volume in Q14 - // index in [0-7] - /* 8 different curves */ - if (volume > 5243) - { - if (volume > 7864) - { - if (volume > 12124) - { - *index = 7; - } else - { - *index = 6; - } - } else - { - if (volume > 6554) - { - *index = 5; - } else - { - *index = 4; - } - } - } else - { - if (volume > 2621) - { - if (volume > 3932) - { - *index = 3; - } else - { - *index = 2; - } - } else - { - if (volume > 1311) - { - *index = 1; - } else - { - *index = 0; - } - } - } -} - -WebRtc_Word32 WebRtcAgc_ProcessAnalog(void *state, WebRtc_Word32 inMicLevel, - WebRtc_Word32 *outMicLevel, - WebRtc_Word16 vadLogRatio, - WebRtc_Word16 echo, WebRtc_UWord8 *saturationWarning) -{ - WebRtc_UWord32 tmpU32; - WebRtc_Word32 Rxx16w32, tmp32; - WebRtc_Word32 inMicLevelTmp, lastMicVol; - WebRtc_Word16 i; - WebRtc_UWord8 saturated = 0; - Agc_t *stt; - - stt = (Agc_t *)state; - inMicLevelTmp = WEBRTC_SPL_LSHIFT_W32(inMicLevel, stt->scale); - - if (inMicLevelTmp > stt->maxAnalog) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: micLvl > maxAnalog\n", stt->fcount); -#endif - return -1; - } else if (inMicLevelTmp < stt->minLevel) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel\n", stt->fcount); -#endif - return -1; - } - - if (stt->firstCall == 0) - { - WebRtc_Word32 tmpVol; - stt->firstCall = 1; - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (WebRtc_Word32)51, 9); - tmpVol = (stt->minLevel + tmp32); - - /* If the mic level is very low at start, increase it! */ - if ((inMicLevelTmp < tmpVol) && (stt->agcMode == kAgcModeAdaptiveAnalog)) - { - inMicLevelTmp = tmpVol; - } - stt->micVol = inMicLevelTmp; - } - - /* Set the mic level to the previous output value if there is digital input gain */ - if ((inMicLevelTmp == stt->maxAnalog) && (stt->micVol > stt->maxAnalog)) - { - inMicLevelTmp = stt->micVol; - } - - /* If the mic level was manually changed to a very low value raise it! */ - if ((inMicLevelTmp != stt->micVol) && (inMicLevelTmp < stt->minOutput)) - { - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (WebRtc_Word32)51, 9); - inMicLevelTmp = (stt->minLevel + tmp32); - stt->micVol = inMicLevelTmp; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: micLvl < minLevel by manual decrease, raise vol\n", - stt->fcount); -#endif - } - - if (inMicLevelTmp != stt->micVol) - { - // Incoming level mismatch; update our level. - // This could be the case if the volume is changed manually, or if the - // sound device has a low volume resolution. - stt->micVol = inMicLevelTmp; - } - - if (inMicLevelTmp > stt->maxLevel) - { - // Always allow the user to raise the volume above the maxLevel. - stt->maxLevel = inMicLevelTmp; - } - - // Store last value here, after we've taken care of manual updates etc. - lastMicVol = stt->micVol; - - /* Checks if the signal is saturated. Also a check if individual samples - * are larger than 12000 is done. If they are the counter for increasing - * the volume level is set to -100ms - */ - WebRtcAgc_SaturationCtrl(stt, &saturated, stt->env[0]); - - /* The AGC is always allowed to lower the level if the signal is saturated */ - if (saturated == 1) - { - /* Lower the recording level - * Rxx160_LP is adjusted down because it is so slow it could - * cause the AGC to make wrong decisions. */ - /* stt->Rxx160_LPw32 *= 0.875; */ - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 3), 7); - - stt->zeroCtrlMax = stt->micVol; - - /* stt->micVol *= 0.903; */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(29591, (WebRtc_UWord32)(tmp32)); - stt->micVol = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 2) - { - stt->micVol = lastMicVol - 2; - } - inMicLevelTmp = stt->micVol; - -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: saturated, micVol = %d\n", - stt->fcount, stt->micVol); -#endif - - if (stt->micVol < stt->minOutput) - { - *saturationWarning = 1; - } - - /* Reset counter for decrease of volume level to avoid - * decreasing too much. The saturation control can still - * lower the level if needed. */ - stt->msTooHigh = -100; - - /* Enable the control mechanism to ensure that our measure, - * Rxx160_LP, is in the correct range. This must be done since - * the measure is very slow. */ - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - /* Reset to initial values */ - stt->msecSpeechInnerChange = kMsecSpeechInner; - stt->msecSpeechOuterChange = kMsecSpeechOuter; - stt->changeToSlowMode = 0; - - stt->muteGuardMs = 0; - - stt->upperLimit = stt->startUpperLimit; - stt->lowerLimit = stt->startLowerLimit; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif - } - - /* Check if the input speech is zero. If so the mic volume - * is increased. On some computers the input is zero up as high - * level as 17% */ - WebRtcAgc_ZeroCtrl(stt, &inMicLevelTmp, stt->env[0]); - - /* Check if the near end speaker is inactive. - * If that is the case the VAD threshold is - * increased since the VAD speech model gets - * more sensitive to any sound after a long - * silence. - */ - WebRtcAgc_SpeakerInactiveCtrl(stt); - - for (i = 0; i < 5; i++) - { - /* Computed on blocks of 16 samples */ - - Rxx16w32 = stt->Rxx16w32_array[0][i]; - - /* Rxx160w32 in Q(-7) */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(Rxx16w32 - stt->Rxx16_vectorw32[stt->Rxx16pos], 3); - stt->Rxx160w32 = stt->Rxx160w32 + tmp32; - stt->Rxx16_vectorw32[stt->Rxx16pos] = Rxx16w32; - - /* Circular buffer */ - stt->Rxx16pos = stt->Rxx16pos++; - if (stt->Rxx16pos == RXX_BUFFER_LEN) - { - stt->Rxx16pos = 0; - } - - /* Rxx16_LPw32 in Q(-4) */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(Rxx16w32 - stt->Rxx16_LPw32, kAlphaShortTerm); - stt->Rxx16_LPw32 = (stt->Rxx16_LPw32) + tmp32; - - if (vadLogRatio > stt->vadThreshold) - { - /* Speech detected! */ - - /* Check if Rxx160_LP is in the correct range. If - * it is too high/low then we set it to the maximum of - * Rxx16_LPw32 during the first 200ms of speech. - */ - if (stt->activeSpeech < 250) - { - stt->activeSpeech += 2; - - if (stt->Rxx16_LPw32 > stt->Rxx16_LPw32Max) - { - stt->Rxx16_LPw32Max = stt->Rxx16_LPw32; - } - } else if (stt->activeSpeech == 250) - { - stt->activeSpeech += 2; - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx16_LPw32Max, 3); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, RXX_BUFFER_LEN); - } - - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160w32 - stt->Rxx160_LPw32, kAlphaLongTerm); - stt->Rxx160_LPw32 = stt->Rxx160_LPw32 + tmp32; - - if (stt->Rxx160_LPw32 > stt->upperSecondaryLimit) - { - stt->msTooHigh += 2; - stt->msTooLow = 0; - stt->changeToSlowMode = 0; - - if (stt->msTooHigh > stt->msecSpeechOuterChange) - { - stt->msTooHigh = 0; - - /* Lower the recording level */ - /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 53); - - /* Reduce the max gain to avoid excessive oscillation - * (but never drop below the maximum analog level). - * stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; - */ - tmp32 = (15 * stt->maxLevel) + stt->micVol; - stt->maxLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); - stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); - - stt->zeroCtrlMax = stt->micVol; - - /* 0.95 in Q15 */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(31130, (WebRtc_UWord32)(tmp32)); - stt->micVol = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 1) - { - stt->micVol = lastMicVol - 1; - } - inMicLevelTmp = stt->micVol; - - /* Enable the control mechanism to ensure that our measure, - * Rxx160_LP, is in the correct range. - */ - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure > 2ndUpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, stt->micVol, stt->maxLevel); -#endif - } - } else if (stt->Rxx160_LPw32 > stt->upperLimit) - { - stt->msTooHigh += 2; - stt->msTooLow = 0; - stt->changeToSlowMode = 0; - - if (stt->msTooHigh > stt->msecSpeechInnerChange) - { - /* Lower the recording level */ - stt->msTooHigh = 0; - /* Multiply by 0.828125 which corresponds to decreasing ~0.8dB */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 53); - - /* Reduce the max gain to avoid excessive oscillation - * (but never drop below the maximum analog level). - * stt->maxLevel = (15 * stt->maxLevel + stt->micVol) / 16; - */ - tmp32 = (15 * stt->maxLevel) + stt->micVol; - stt->maxLevel = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); - stt->maxLevel = WEBRTC_SPL_MAX(stt->maxLevel, stt->maxAnalog); - - stt->zeroCtrlMax = stt->micVol; - - /* 0.965 in Q15 */ - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = WEBRTC_SPL_UMUL(31621, (WebRtc_UWord32)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(tmpU32, 15) + stt->minLevel; - if (stt->micVol > lastMicVol - 1) - { - stt->micVol = lastMicVol - 1; - } - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - //stt->numBlocksMicLvlSat = 0; -#endif -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure > UpperLim, micVol = %d, maxLevel = %d\n", - stt->fcount, stt->micVol, stt->maxLevel); -#endif - } - } else if (stt->Rxx160_LPw32 < stt->lowerSecondaryLimit) - { - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->msTooLow += 2; - - if (stt->msTooLow > stt->msecSpeechOuterChange) - { - /* Raise the recording level */ - WebRtc_Word16 index, weightFIX; - WebRtc_Word16 volNormFIX = 16384; // =1 in Q14. - - stt->msTooLow = 0; - - /* Normalize the volume level */ - tmp32 = WEBRTC_SPL_LSHIFT_W32(inMicLevelTmp - stt->minLevel, 14); - if (stt->maxInit != stt->minLevel) - { - volNormFIX = (WebRtc_Word16)WEBRTC_SPL_DIV(tmp32, - (stt->maxInit - stt->minLevel)); - } - - /* Find correct curve */ - WebRtcAgc_ExpCurve(volNormFIX, &index); - - /* Compute weighting factor for the volume increase, 32^(-2*X)/2+1.05 */ - weightFIX = kOffset1[index] - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(kSlope1[index], - volNormFIX, 13); - - /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 67); - - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = ((WebRtc_UWord32)weightFIX * (WebRtc_UWord32)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(tmpU32, 14) + stt->minLevel; - if (stt->micVol < lastMicVol + 2) - { - stt->micVol = lastMicVol + 2; - } - - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - /* Count ms in level saturation */ - //if (stt->micVol > stt->maxAnalog) { - if (stt->micVol > 150) - { - /* mic level is saturated */ - stt->numBlocksMicLvlSat++; - fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); - } -#endif -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure < 2ndLowerLim, micVol = %d\n", - stt->fcount, stt->micVol); -#endif - } - } else if (stt->Rxx160_LPw32 < stt->lowerLimit) - { - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->msTooLow += 2; - - if (stt->msTooLow > stt->msecSpeechInnerChange) - { - /* Raise the recording level */ - WebRtc_Word16 index, weightFIX; - WebRtc_Word16 volNormFIX = 16384; // =1 in Q14. - - stt->msTooLow = 0; - - /* Normalize the volume level */ - tmp32 = WEBRTC_SPL_LSHIFT_W32(inMicLevelTmp - stt->minLevel, 14); - if (stt->maxInit != stt->minLevel) - { - volNormFIX = (WebRtc_Word16)WEBRTC_SPL_DIV(tmp32, - (stt->maxInit - stt->minLevel)); - } - - /* Find correct curve */ - WebRtcAgc_ExpCurve(volNormFIX, &index); - - /* Compute weighting factor for the volume increase, (3.^(-2.*X))/8+1 */ - weightFIX = kOffset2[index] - - (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(kSlope2[index], - volNormFIX, 13); - - /* stt->Rxx160_LPw32 *= 1.047 [~0.2 dB]; */ - tmp32 = WEBRTC_SPL_RSHIFT_W32(stt->Rxx160_LPw32, 6); - stt->Rxx160_LPw32 = WEBRTC_SPL_MUL(tmp32, 67); - - tmp32 = inMicLevelTmp - stt->minLevel; - tmpU32 = ((WebRtc_UWord32)weightFIX * (WebRtc_UWord32)(inMicLevelTmp - stt->minLevel)); - stt->micVol = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(tmpU32, 14) + stt->minLevel; - if (stt->micVol < lastMicVol + 1) - { - stt->micVol = lastMicVol + 1; - } - - inMicLevelTmp = stt->micVol; - -#ifdef MIC_LEVEL_FEEDBACK - /* Count ms in level saturation */ - //if (stt->micVol > stt->maxAnalog) { - if (stt->micVol > 150) - { - /* mic level is saturated */ - stt->numBlocksMicLvlSat++; - fprintf(stderr, "Sat mic Level: %d\n", stt->numBlocksMicLvlSat); - } -#endif -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "\tAGC->ProcessAnalog, frame %d: measure < LowerLim, micVol = %d\n", - stt->fcount, stt->micVol); -#endif - - } - } else - { - /* The signal is inside the desired range which is: - * lowerLimit < Rxx160_LP/640 < upperLimit - */ - if (stt->changeToSlowMode > 4000) - { - stt->msecSpeechInnerChange = 1000; - stt->msecSpeechOuterChange = 500; - stt->upperLimit = stt->upperPrimaryLimit; - stt->lowerLimit = stt->lowerPrimaryLimit; - } else - { - stt->changeToSlowMode += 2; // in milliseconds - } - stt->msTooLow = 0; - stt->msTooHigh = 0; - - stt->micVol = inMicLevelTmp; - - } -#ifdef MIC_LEVEL_FEEDBACK - if (stt->numBlocksMicLvlSat > NUM_BLOCKS_IN_SAT_BEFORE_CHANGE_TARGET) - { - stt->micLvlSat = 1; - fprintf(stderr, "target before = %d (%d)\n", stt->analogTargetLevel, stt->targetIdx); - WebRtcAgc_UpdateAgcThresholds(stt); - WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), - stt->compressionGaindB, stt->targetLevelDbfs, stt->limiterEnable, - stt->analogTarget); - stt->numBlocksMicLvlSat = 0; - stt->micLvlSat = 0; - fprintf(stderr, "target offset = %d\n", stt->targetIdxOffset); - fprintf(stderr, "target after = %d (%d)\n", stt->analogTargetLevel, stt->targetIdx); - } -#endif - } - } - - /* Ensure gain is not increased in presence of echo or after a mute event - * (but allow the zeroCtrl() increase on the frame of a mute detection). - */ - if (echo == 1 || (stt->muteGuardMs > 0 && stt->muteGuardMs < kMuteGuardTimeMs)) - { - if (stt->micVol > lastMicVol) - { - stt->micVol = lastMicVol; - } - } - - /* limit the gain */ - if (stt->micVol > stt->maxLevel) - { - stt->micVol = stt->maxLevel; - } else if (stt->micVol < stt->minOutput) - { - stt->micVol = stt->minOutput; - } - - *outMicLevel = WEBRTC_SPL_RSHIFT_W32(stt->micVol, stt->scale); - if (*outMicLevel > WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog, stt->scale)) - { - *outMicLevel = WEBRTC_SPL_RSHIFT_W32(stt->maxAnalog, stt->scale); - } - - return 0; -} - -int WebRtcAgc_Process(void *agcInst, const WebRtc_Word16 *in_near, - const WebRtc_Word16 *in_near_H, WebRtc_Word16 samples, - WebRtc_Word16 *out, WebRtc_Word16 *out_H, WebRtc_Word32 inMicLevel, - WebRtc_Word32 *outMicLevel, WebRtc_Word16 echo, - WebRtc_UWord8 *saturationWarning) -{ - Agc_t *stt; - WebRtc_Word32 inMicLevelTmp; - WebRtc_Word16 subFrames, i; - WebRtc_UWord8 satWarningTmp = 0; - - stt = (Agc_t *)agcInst; - - // - if (stt == NULL) - { - return -1; - } - // - - - if (stt->fs == 8000) - { - if ((samples != 80) && (samples != 160)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); -#endif - return -1; - } - subFrames = 80; - } else if (stt->fs == 16000) - { - if ((samples != 160) && (samples != 320)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); -#endif - return -1; - } - subFrames = 160; - } else if (stt->fs == 32000) - { - if ((samples != 160) && (samples != 320)) - { -#ifdef AGC_DEBUG //test log - fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid number of samples\n\n", stt->fcount); -#endif - return -1; - } - subFrames = 160; - } else - { -#ifdef AGC_DEBUG// test log - fprintf(stt->fpt, - "AGC->Process, frame %d: Invalid sample rate\n\n", stt->fcount); -#endif - return -1; - } - - /* Check for valid pointers based on sampling rate */ - if (stt->fs == 32000 && in_near_H == NULL) - { - return -1; - } - /* Check for valid pointers for low band */ - if (in_near == NULL) - { - return -1; - } - - *saturationWarning = 0; - //TODO: PUT IN RANGE CHECKING FOR INPUT LEVELS - *outMicLevel = inMicLevel; - inMicLevelTmp = inMicLevel; - - memcpy(out, in_near, samples * sizeof(WebRtc_Word16)); - if (stt->fs == 32000) - { - memcpy(out_H, in_near_H, samples * sizeof(WebRtc_Word16)); - } - -#ifdef AGC_DEBUG//test log - stt->fcount++; -#endif - - for (i = 0; i < samples; i += subFrames) - { - if (WebRtcAgc_ProcessDigital(&stt->digitalAgc, &in_near[i], &in_near_H[i], &out[i], &out_H[i], - stt->fs, stt->lowLevelSignal) == -1) - { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "AGC->Process, frame %d: Error from DigAGC\n\n", stt->fcount); -#endif - return -1; - } - if ((stt->agcMode < kAgcModeFixedDigital) && ((stt->lowLevelSignal == 0) - || (stt->agcMode != kAgcModeAdaptiveDigital))) - { - if (WebRtcAgc_ProcessAnalog(agcInst, inMicLevelTmp, outMicLevel, - stt->vadMic.logRatio, echo, saturationWarning) == -1) - { - return -1; - } - } -#ifdef AGC_DEBUG//test log - fprintf(stt->agcLog, "%5d\t%d\t%d\t%d\n", stt->fcount, inMicLevelTmp, *outMicLevel, stt->maxLevel, stt->micVol); -#endif - - /* update queue */ - if (stt->inQueue > 1) - { - memcpy(stt->env[0], stt->env[1], 10 * sizeof(WebRtc_Word32)); - memcpy(stt->Rxx16w32_array[0], stt->Rxx16w32_array[1], 5 * sizeof(WebRtc_Word32)); - } - - if (stt->inQueue > 0) - { - stt->inQueue--; - } - - /* If 20ms frames are used the input mic level must be updated so that - * the analog AGC does not think that there has been a manual volume - * change. */ - inMicLevelTmp = *outMicLevel; - - /* Store a positive saturation warning. */ - if (*saturationWarning == 1) - { - satWarningTmp = 1; - } - } - - /* Trigger the saturation warning if displayed by any of the frames. */ - *saturationWarning = satWarningTmp; - - return 0; -} - -int WebRtcAgc_set_config(void *agcInst, WebRtcAgc_config_t agcConfig) -{ - Agc_t *stt; - stt = (Agc_t *)agcInst; - - if (stt == NULL) - { - return -1; - } - - if (stt->initFlag != kInitCheck) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - if (agcConfig.limiterEnable != kAgcFalse && agcConfig.limiterEnable != kAgcTrue) - { - stt->lastError = AGC_BAD_PARAMETER_ERROR; - return -1; - } - stt->limiterEnable = agcConfig.limiterEnable; - stt->compressionGaindB = agcConfig.compressionGaindB; - if ((agcConfig.targetLevelDbfs < 0) || (agcConfig.targetLevelDbfs > 31)) - { - stt->lastError = AGC_BAD_PARAMETER_ERROR; - return -1; - } - stt->targetLevelDbfs = agcConfig.targetLevelDbfs; - - if (stt->agcMode == kAgcModeFixedDigital) - { - /* Adjust for different parameter interpretation in FixedDigital mode */ - stt->compressionGaindB += agcConfig.targetLevelDbfs; - } - - /* Update threshold levels for analog adaptation */ - WebRtcAgc_UpdateAgcThresholds(stt); - - /* Recalculate gain table */ - if (WebRtcAgc_CalculateGainTable(&(stt->digitalAgc.gainTable[0]), stt->compressionGaindB, - stt->targetLevelDbfs, stt->limiterEnable, stt->analogTarget) == -1) - { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "AGC->set_config, frame %d: Error from calcGainTable\n\n", stt->fcount); -#endif - return -1; - } - /* Store the config in a WebRtcAgc_config_t */ - stt->usedConfig.compressionGaindB = agcConfig.compressionGaindB; - stt->usedConfig.limiterEnable = agcConfig.limiterEnable; - stt->usedConfig.targetLevelDbfs = agcConfig.targetLevelDbfs; - - return 0; -} - -int WebRtcAgc_get_config(void *agcInst, WebRtcAgc_config_t *config) -{ - Agc_t *stt; - stt = (Agc_t *)agcInst; - - if (stt == NULL) - { - return -1; - } - - if (config == NULL) - { - stt->lastError = AGC_NULL_POINTER_ERROR; - return -1; - } - - if (stt->initFlag != kInitCheck) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - config->limiterEnable = stt->usedConfig.limiterEnable; - config->targetLevelDbfs = stt->usedConfig.targetLevelDbfs; - config->compressionGaindB = stt->usedConfig.compressionGaindB; - - return 0; -} - -int WebRtcAgc_Create(void **agcInst) -{ - Agc_t *stt; - if (agcInst == NULL) - { - return -1; - } - stt = (Agc_t *)malloc(sizeof(Agc_t)); - - *agcInst = stt; - if (stt == NULL) - { - return -1; - } - -#ifdef AGC_DEBUG - stt->fpt = fopen("./agc_test_log.txt", "wt"); - stt->agcLog = fopen("./agc_debug_log.txt", "wt"); - stt->digitalAgc.logFile = fopen("./agc_log.txt", "wt"); -#endif - - stt->initFlag = 0; - stt->lastError = 0; - - return 0; -} - -int WebRtcAgc_Free(void *state) -{ - Agc_t *stt; - - stt = (Agc_t *)state; -#ifdef AGC_DEBUG - fclose(stt->fpt); - fclose(stt->agcLog); - fclose(stt->digitalAgc.logFile); -#endif - free(stt); - - return 0; -} - -/* minLevel - Minimum volume level - * maxLevel - Maximum volume level - */ -int WebRtcAgc_Init(void *agcInst, WebRtc_Word32 minLevel, WebRtc_Word32 maxLevel, - WebRtc_Word16 agcMode, WebRtc_UWord32 fs) -{ - WebRtc_Word32 max_add, tmp32; - WebRtc_Word16 i; - int tmpNorm; - Agc_t *stt; - - /* typecast state pointer */ - stt = (Agc_t *)agcInst; - - if (WebRtcAgc_InitDigital(&stt->digitalAgc, agcMode) != 0) - { - stt->lastError = AGC_UNINITIALIZED_ERROR; - return -1; - } - - /* Analog AGC variables */ - stt->envSum = 0; - - /* mode = 0 - Only saturation protection - * 1 - Analog Automatic Gain Control [-targetLevelDbfs (default -3 dBOv)] - * 2 - Digital Automatic Gain Control [-targetLevelDbfs (default -3 dBOv)] - * 3 - Fixed Digital Gain [compressionGaindB (default 8 dB)] - */ -#ifdef AGC_DEBUG//test log - stt->fcount = 0; - fprintf(stt->fpt, "AGC->Init\n"); -#endif - if (agcMode < kAgcModeUnchanged || agcMode > kAgcModeFixedDigital) - { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "AGC->Init: error, incorrect mode\n\n"); -#endif - return -1; - } - stt->agcMode = agcMode; - stt->fs = fs; - - /* initialize input VAD */ - WebRtcAgc_InitVad(&stt->vadMic); - - /* If the volume range is smaller than 0-256 then - * the levels are shifted up to Q8-domain */ - tmpNorm = WebRtcSpl_NormU32((WebRtc_UWord32)maxLevel); - stt->scale = tmpNorm - 23; - if (stt->scale < 0) - { - stt->scale = 0; - } - // TODO(bjornv): Investigate if we really need to scale up a small range now when we have - // a guard against zero-increments. For now, we do not support scale up (scale = 0). - stt->scale = 0; - maxLevel = WEBRTC_SPL_LSHIFT_W32(maxLevel, stt->scale); - minLevel = WEBRTC_SPL_LSHIFT_W32(minLevel, stt->scale); - - /* Make minLevel and maxLevel static in AdaptiveDigital */ - if (stt->agcMode == kAgcModeAdaptiveDigital) - { - minLevel = 0; - maxLevel = 255; - stt->scale = 0; - } - /* The maximum supplemental volume range is based on a vague idea - * of how much lower the gain will be than the real analog gain. */ - max_add = WEBRTC_SPL_RSHIFT_W32(maxLevel - minLevel, 2); - - /* Minimum/maximum volume level that can be set */ - stt->minLevel = minLevel; - stt->maxAnalog = maxLevel; - stt->maxLevel = maxLevel + max_add; - stt->maxInit = stt->maxLevel; - - stt->zeroCtrlMax = stt->maxAnalog; - - /* Initialize micVol parameter */ - stt->micVol = stt->maxAnalog; - if (stt->agcMode == kAgcModeAdaptiveDigital) - { - stt->micVol = 127; /* Mid-point of mic level */ - } - stt->micRef = stt->micVol; - stt->micGainIdx = 127; -#ifdef MIC_LEVEL_FEEDBACK - stt->numBlocksMicLvlSat = 0; - stt->micLvlSat = 0; -#endif -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, - "AGC->Init: minLevel = %d, maxAnalog = %d, maxLevel = %d\n", - stt->minLevel, stt->maxAnalog, stt->maxLevel); -#endif - - /* Minimum output volume is 4% higher than the available lowest volume level */ - tmp32 = WEBRTC_SPL_RSHIFT_W32((stt->maxLevel - stt->minLevel) * (WebRtc_Word32)10, 8); - stt->minOutput = (stt->minLevel + tmp32); - - stt->msTooLow = 0; - stt->msTooHigh = 0; - stt->changeToSlowMode = 0; - stt->firstCall = 0; - stt->msZero = 0; - stt->muteGuardMs = 0; - stt->gainTableIdx = 0; - - stt->msecSpeechInnerChange = kMsecSpeechInner; - stt->msecSpeechOuterChange = kMsecSpeechOuter; - - stt->activeSpeech = 0; - stt->Rxx16_LPw32Max = 0; - - stt->vadThreshold = kNormalVadThreshold; - stt->inActive = 0; - - for (i = 0; i < RXX_BUFFER_LEN; i++) - { - stt->Rxx16_vectorw32[i] = (WebRtc_Word32)1000; /* -54dBm0 */ - } - stt->Rxx160w32 = 125 * RXX_BUFFER_LEN; /* (stt->Rxx16_vectorw32[0]>>3) = 125 */ - - stt->Rxx16pos = 0; - stt->Rxx16_LPw32 = (WebRtc_Word32)16284; /* Q(-4) */ - - for (i = 0; i < 5; i++) - { - stt->Rxx16w32_array[0][i] = 0; - } - for (i = 0; i < 20; i++) - { - stt->env[0][i] = 0; - } - stt->inQueue = 0; - -#ifdef MIC_LEVEL_FEEDBACK - stt->targetIdxOffset = 0; -#endif - - WebRtcSpl_MemSetW32(stt->filterState, 0, 8); - - stt->initFlag = kInitCheck; - // Default config settings. - stt->defaultConfig.limiterEnable = kAgcTrue; - stt->defaultConfig.targetLevelDbfs = AGC_DEFAULT_TARGET_LEVEL; - stt->defaultConfig.compressionGaindB = AGC_DEFAULT_COMP_GAIN; - - if (WebRtcAgc_set_config(stt, stt->defaultConfig) == -1) - { - stt->lastError = AGC_UNSPECIFIED_ERROR; - return -1; - } - stt->Rxx160_LPw32 = stt->analogTargetLevel; // Initialize rms value - - stt->lowLevelSignal = 0; - - /* Only positive values are allowed that are not too large */ - if ((minLevel >= maxLevel) || (maxLevel & 0xFC000000)) - { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "minLevel, maxLevel value(s) are invalid\n\n"); -#endif - return -1; - } else - { -#ifdef AGC_DEBUG//test log - fprintf(stt->fpt, "\n"); -#endif - return 0; - } -} - -int WebRtcAgc_Version(WebRtc_Word8 *versionStr, WebRtc_Word16 length) -{ - const WebRtc_Word8 version[] = "AGC 1.7.0"; - const WebRtc_Word16 versionLen = (WebRtc_Word16)strlen(version) + 1; - - if (versionStr == NULL) - { - return -1; - } - - if (versionLen > length) - { - return -1; - } - - strncpy(versionStr, version, versionLen); - return 0; -} diff --git a/modules/audio_processing/agc/main/source/analog_agc.h b/modules/audio_processing/agc/main/source/analog_agc.h deleted file mode 100644 index b32ac6581..000000000 --- a/modules/audio_processing/agc/main/source/analog_agc.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_ - -#include "typedefs.h" -#include "gain_control.h" -#include "digital_agc.h" - -//#define AGC_DEBUG -//#define MIC_LEVEL_FEEDBACK -#ifdef AGC_DEBUG -#include -#endif - -/* Analog Automatic Gain Control variables: - * Constant declarations (inner limits inside which no changes are done) - * In the beginning the range is narrower to widen as soon as the measure - * 'Rxx160_LP' is inside it. Currently the starting limits are -22.2+/-1dBm0 - * and the final limits -22.2+/-2.5dBm0. These levels makes the speech signal - * go towards -25.4dBm0 (-31.4dBov). Tuned with wbfile-31.4dBov.pcm - * The limits are created by running the AGC with a file having the desired - * signal level and thereafter plotting Rxx160_LP in the dBm0-domain defined - * by out=10*log10(in/260537279.7); Set the target level to the average level - * of our measure Rxx160_LP. Remember that the levels are in blocks of 16 in - * Q(-7). (Example matlab code: round(db2pow(-21.2)*16/2^7) ) - */ -#define RXX_BUFFER_LEN 10 - -static const WebRtc_Word16 kMsecSpeechInner = 520; -static const WebRtc_Word16 kMsecSpeechOuter = 340; - -static const WebRtc_Word16 kNormalVadThreshold = 400; - -static const WebRtc_Word16 kAlphaShortTerm = 6; // 1 >> 6 = 0.0156 -static const WebRtc_Word16 kAlphaLongTerm = 10; // 1 >> 10 = 0.000977 - -typedef struct -{ - // Configurable parameters/variables - WebRtc_UWord32 fs; // Sampling frequency - WebRtc_Word16 compressionGaindB; // Fixed gain level in dB - WebRtc_Word16 targetLevelDbfs; // Target level in -dBfs of envelope (default -3) - WebRtc_Word16 agcMode; // Hard coded mode (adaptAna/adaptDig/fixedDig) - WebRtc_UWord8 limiterEnable; // Enabling limiter (on/off (default off)) - WebRtcAgc_config_t defaultConfig; - WebRtcAgc_config_t usedConfig; - - // General variables - WebRtc_Word16 initFlag; - WebRtc_Word16 lastError; - - // Target level parameters - // Based on the above: analogTargetLevel = round((32767*10^(-22/20))^2*16/2^7) - WebRtc_Word32 analogTargetLevel; // = RXX_BUFFER_LEN * 846805; -22 dBfs - WebRtc_Word32 startUpperLimit; // = RXX_BUFFER_LEN * 1066064; -21 dBfs - WebRtc_Word32 startLowerLimit; // = RXX_BUFFER_LEN * 672641; -23 dBfs - WebRtc_Word32 upperPrimaryLimit; // = RXX_BUFFER_LEN * 1342095; -20 dBfs - WebRtc_Word32 lowerPrimaryLimit; // = RXX_BUFFER_LEN * 534298; -24 dBfs - WebRtc_Word32 upperSecondaryLimit;// = RXX_BUFFER_LEN * 2677832; -17 dBfs - WebRtc_Word32 lowerSecondaryLimit;// = RXX_BUFFER_LEN * 267783; -27 dBfs - WebRtc_UWord16 targetIdx; // Table index for corresponding target level -#ifdef MIC_LEVEL_FEEDBACK - WebRtc_UWord16 targetIdxOffset; // Table index offset for level compensation -#endif - WebRtc_Word16 analogTarget; // Digital reference level in ENV scale - - // Analog AGC specific variables - WebRtc_Word32 filterState[8]; // For downsampling wb to nb - WebRtc_Word32 upperLimit; // Upper limit for mic energy - WebRtc_Word32 lowerLimit; // Lower limit for mic energy - WebRtc_Word32 Rxx160w32; // Average energy for one frame - WebRtc_Word32 Rxx16_LPw32; // Low pass filtered subframe energies - WebRtc_Word32 Rxx160_LPw32; // Low pass filtered frame energies - WebRtc_Word32 Rxx16_LPw32Max; // Keeps track of largest energy subframe - WebRtc_Word32 Rxx16_vectorw32[RXX_BUFFER_LEN];// Array with subframe energies - WebRtc_Word32 Rxx16w32_array[2][5];// Energy values of microphone signal - WebRtc_Word32 env[2][10]; // Envelope values of subframes - - WebRtc_Word16 Rxx16pos; // Current position in the Rxx16_vectorw32 - WebRtc_Word16 envSum; // Filtered scaled envelope in subframes - WebRtc_Word16 vadThreshold; // Threshold for VAD decision - WebRtc_Word16 inActive; // Inactive time in milliseconds - WebRtc_Word16 msTooLow; // Milliseconds of speech at a too low level - WebRtc_Word16 msTooHigh; // Milliseconds of speech at a too high level - WebRtc_Word16 changeToSlowMode; // Change to slow mode after some time at target - WebRtc_Word16 firstCall; // First call to the process-function - WebRtc_Word16 msZero; // Milliseconds of zero input - WebRtc_Word16 msecSpeechOuterChange;// Min ms of speech between volume changes - WebRtc_Word16 msecSpeechInnerChange;// Min ms of speech between volume changes - WebRtc_Word16 activeSpeech; // Milliseconds of active speech - WebRtc_Word16 muteGuardMs; // Counter to prevent mute action - WebRtc_Word16 inQueue; // 10 ms batch indicator - - // Microphone level variables - WebRtc_Word32 micRef; // Remember ref. mic level for virtual mic - WebRtc_UWord16 gainTableIdx; // Current position in virtual gain table - WebRtc_Word32 micGainIdx; // Gain index of mic level to increase slowly - WebRtc_Word32 micVol; // Remember volume between frames - WebRtc_Word32 maxLevel; // Max possible vol level, incl dig gain - WebRtc_Word32 maxAnalog; // Maximum possible analog volume level - WebRtc_Word32 maxInit; // Initial value of "max" - WebRtc_Word32 minLevel; // Minimum possible volume level - WebRtc_Word32 minOutput; // Minimum output volume level - WebRtc_Word32 zeroCtrlMax; // Remember max gain => don't amp low input - - WebRtc_Word16 scale; // Scale factor for internal volume levels -#ifdef MIC_LEVEL_FEEDBACK - WebRtc_Word16 numBlocksMicLvlSat; - WebRtc_UWord8 micLvlSat; -#endif - // Structs for VAD and digital_agc - AgcVad_t vadMic; - DigitalAgc_t digitalAgc; - -#ifdef AGC_DEBUG - FILE* fpt; - FILE* agcLog; - WebRtc_Word32 fcount; -#endif - - WebRtc_Word16 lowLevelSignal; -} Agc_t; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_ diff --git a/modules/audio_processing/agc/main/source/digital_agc.c b/modules/audio_processing/agc/main/source/digital_agc.c deleted file mode 100644 index 2966586e4..000000000 --- a/modules/audio_processing/agc/main/source/digital_agc.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* digital_agc.c - * - */ - -#include -#ifdef AGC_DEBUG -#include -#endif -#include "digital_agc.h" -#include "gain_control.h" - -// To generate the gaintable, copy&paste the following lines to a Matlab window: -// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1; -// zeros = 0:31; lvl = 2.^(1-zeros); -// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio; -// B = MaxGain - MinGain; -// gains = round(2^16*10.^(0.05 * (MinGain + B * ( log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / log(1/(1+exp(Knee*B)))))); -// fprintf(1, '\t%i, %i, %i, %i,\n', gains); -// % Matlab code for plotting the gain and input/output level characteristic (copy/paste the following 3 lines): -// in = 10*log10(lvl); out = 20*log10(gains/65536); -// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)'); -// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)'); -// zoom on; - -// Generator table for y=log2(1+e^x) in Q8. -static const WebRtc_UWord16 kGenFuncTable[128] = { - 256, 485, 786, 1126, 1484, 1849, 2217, 2586, - 2955, 3324, 3693, 4063, 4432, 4801, 5171, 5540, - 5909, 6279, 6648, 7017, 7387, 7756, 8125, 8495, - 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, - 11819, 12188, 12557, 12927, 13296, 13665, 14035, 14404, - 14773, 15143, 15512, 15881, 16251, 16620, 16989, 17359, - 17728, 18097, 18466, 18836, 19205, 19574, 19944, 20313, - 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, - 23637, 24006, 24376, 24745, 25114, 25484, 25853, 26222, - 26592, 26961, 27330, 27700, 28069, 28438, 28808, 29177, - 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132, - 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, - 35456, 35825, 36194, 36564, 36933, 37302, 37672, 38041, - 38410, 38780, 39149, 39518, 39888, 40257, 40626, 40996, - 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, - 44320, 44689, 45058, 45428, 45797, 46166, 46536, 46905 -}; - -static const WebRtc_Word16 kAvgDecayTime = 250; // frames; < 3000 - -WebRtc_Word32 WebRtcAgc_CalculateGainTable(WebRtc_Word32 *gainTable, // Q16 - WebRtc_Word16 digCompGaindB, // Q0 - WebRtc_Word16 targetLevelDbfs,// Q0 - WebRtc_UWord8 limiterEnable, - WebRtc_Word16 analogTarget) // Q0 -{ - // This function generates the compressor gain table used in the fixed digital part. - WebRtc_UWord32 tmpU32no1, tmpU32no2, absInLevel, logApprox; - WebRtc_Word32 inLevel, limiterLvl; - WebRtc_Word32 tmp32, tmp32no1, tmp32no2, numFIX, den, y32; - const WebRtc_UWord16 kLog10 = 54426; // log2(10) in Q14 - const WebRtc_UWord16 kLog10_2 = 49321; // 10*log10(2) in Q14 - const WebRtc_UWord16 kLogE_1 = 23637; // log2(e) in Q14 - WebRtc_UWord16 constMaxGain; - WebRtc_UWord16 tmpU16, intPart, fracPart; - const WebRtc_Word16 kCompRatio = 3; - const WebRtc_Word16 kSoftLimiterLeft = 1; - WebRtc_Word16 limiterOffset = 0; // Limiter offset - WebRtc_Word16 limiterIdx, limiterLvlX; - WebRtc_Word16 constLinApprox, zeroGainLvl, maxGain, diffGain; - WebRtc_Word16 i, tmp16, tmp16no1; - int zeros, zerosScale; - - // Constants -// kLogE_1 = 23637; // log2(e) in Q14 -// kLog10 = 54426; // log2(10) in Q14 -// kLog10_2 = 49321; // 10*log10(2) in Q14 - - // Calculate maximum digital gain and zero gain level - tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB - analogTarget, kCompRatio - 1); - tmp16no1 = analogTarget - targetLevelDbfs; - tmp16no1 += WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); - maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs)); - tmp32no1 = WEBRTC_SPL_MUL_16_16(maxGain, kCompRatio); - zeroGainLvl = digCompGaindB; - zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1), - kCompRatio - 1); - if ((digCompGaindB <= analogTarget) && (limiterEnable)) - { - zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft); - limiterOffset = 0; - } - - // Calculate the difference between maximum gain and gain at 0dB0v: - // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio - // = (compRatio-1)*digCompGaindB/compRatio - tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB, kCompRatio - 1); - diffGain = WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio); - if (diffGain < 0) - { - return -1; - } - - // Calculate the limiter level and index: - // limiterLvlX = analogTarget - limiterOffset - // limiterLvl = targetLevelDbfs + limiterOffset/compRatio - limiterLvlX = analogTarget - limiterOffset; - limiterIdx = 2 - + WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)limiterLvlX, 13), - WEBRTC_SPL_RSHIFT_U16(kLog10_2, 1)); - tmp16no1 = WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio); - limiterLvl = targetLevelDbfs + tmp16no1; - - // Calculate (through table lookup): - // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8) - constMaxGain = kGenFuncTable[diffGain]; // in Q8 - - // Calculate a parameter used to approximate the fractional part of 2^x with a - // piecewise linear function in Q14: - // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14); - constLinApprox = 22817; // in Q14 - - // Calculate a denominator used in the exponential part to convert from dB to linear scale: - // den = 20*constMaxGain (in Q8) - den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8 - - for (i = 0; i < 32; i++) - { - // Calculate scaled input level (compressor): - // inLevel = fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio) - tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(kCompRatio - 1, i - 1); // Q0 - tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14 - inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14 - - // Calculate diffGain-inLevel, to map using the genFuncTable - inLevel = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)diffGain, 14) - inLevel; // Q14 - - // Make calculations on abs(inLevel) and compensate for the sign afterwards. - absInLevel = (WebRtc_UWord32)WEBRTC_SPL_ABS_W32(inLevel); // Q14 - - // LUT with interpolation - intPart = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_U32(absInLevel, 14); - fracPart = (WebRtc_UWord16)(absInLevel & 0x00003FFF); // extract the fractional part - tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8 - tmpU32no1 = WEBRTC_SPL_UMUL_16_16(tmpU16, fracPart); // Q22 - tmpU32no1 += WEBRTC_SPL_LSHIFT_U32((WebRtc_UWord32)kGenFuncTable[intPart], 14); // Q22 - logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 8); // Q14 - // Compensate for negative exponent using the relation: - // log2(1 + 2^-x) = log2(1 + 2^x) - x - if (inLevel < 0) - { - zeros = WebRtcSpl_NormU32(absInLevel); - zerosScale = 0; - if (zeros < 15) - { - // Not enough space for multiplication - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(absInLevel, 15 - zeros); // Q(zeros-1) - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13) - if (zeros < 9) - { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 9 - zeros); // Q(zeros+13) - zerosScale = 9 - zeros; - } else - { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, zeros - 9); // Q22 - } - } else - { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28 - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); // Q22 - } - logApprox = 0; - if (tmpU32no2 < tmpU32no1) - { - logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1 - tmpU32no2, 8 - zerosScale); //Q14 - } - } - numFIX = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_U16(maxGain, constMaxGain), 6); // Q14 - numFIX -= WEBRTC_SPL_MUL_32_16((WebRtc_Word32)logApprox, diffGain); // Q14 - - // Calculate ratio - // Shift numFIX as much as possible - zeros = WebRtcSpl_NormW32(numFIX); - numFIX = WEBRTC_SPL_LSHIFT_W32(numFIX, zeros); // Q(14+zeros) - - // Shift den so we end up in Qy1 - tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 8); // Q(zeros) - if (numFIX < 0) - { - numFIX -= WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1); - } else - { - numFIX += WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1); - } - y32 = WEBRTC_SPL_DIV(numFIX, tmp32no1); // in Q14 - if (limiterEnable && (i < limiterIdx)) - { - tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14 - tmp32 -= WEBRTC_SPL_LSHIFT_W32(limiterLvl, 14); // Q14 - y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20); - } - if (y32 > 39000) - { - tmp32 = WEBRTC_SPL_MUL(y32 >> 1, kLog10) + 4096; // in Q27 - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 13); // in Q14 - } else - { - tmp32 = WEBRTC_SPL_MUL(y32, kLog10) + 8192; // in Q28 - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 14); // in Q14 - } - tmp32 += WEBRTC_SPL_LSHIFT_W32(16, 14); // in Q14 (Make sure final output is in Q16) - - // Calculate power - if (tmp32 > 0) - { - intPart = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 14); - fracPart = (WebRtc_UWord16)(tmp32 & 0x00003FFF); // in Q14 - if (WEBRTC_SPL_RSHIFT_W32(fracPart, 13)) - { - tmp16 = WEBRTC_SPL_LSHIFT_W16(2, 14) - constLinApprox; - tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - fracPart; - tmp32no2 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16); - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13); - tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - tmp32no2; - } else - { - tmp16 = constLinApprox - WEBRTC_SPL_LSHIFT_W16(1, 14); - tmp32no2 = WEBRTC_SPL_MUL_32_16(fracPart, tmp16); - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13); - } - fracPart = (WebRtc_UWord16)tmp32no2; - gainTable[i] = WEBRTC_SPL_LSHIFT_W32(1, intPart) - + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14); - } else - { - gainTable[i] = 0; - } - } - - return 0; -} - -WebRtc_Word32 WebRtcAgc_InitDigital(DigitalAgc_t *stt, WebRtc_Word16 agcMode) -{ - - if (agcMode == kAgcModeFixedDigital) - { - // start at minimum to find correct gain faster - stt->capacitorSlow = 0; - } else - { - // start out with 0 dB gain - stt->capacitorSlow = 134217728; // (WebRtc_Word32)(0.125f * 32768.0f * 32768.0f); - } - stt->capacitorFast = 0; - stt->gain = 65536; - stt->gatePrevious = 0; - stt->agcMode = agcMode; -#ifdef AGC_DEBUG - stt->frameCounter = 0; -#endif - - // initialize VADs - WebRtcAgc_InitVad(&stt->vadNearend); - WebRtcAgc_InitVad(&stt->vadFarend); - - return 0; -} - -WebRtc_Word32 WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const WebRtc_Word16 *in_far, - WebRtc_Word16 nrSamples) -{ - // Check for valid pointer - if (&stt->vadFarend == NULL) - { - return -1; - } - - // VAD for far end - WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples); - - return 0; -} - -WebRtc_Word32 WebRtcAgc_ProcessDigital(DigitalAgc_t *stt, const WebRtc_Word16 *in_near, - const WebRtc_Word16 *in_near_H, WebRtc_Word16 *out, - WebRtc_Word16 *out_H, WebRtc_UWord32 FS, - WebRtc_Word16 lowlevelSignal) -{ - // array for gains (one value per ms, incl start & end) - WebRtc_Word32 gains[11]; - - WebRtc_Word32 out_tmp, tmp32; - WebRtc_Word32 env[10]; - WebRtc_Word32 nrg, max_nrg; - WebRtc_Word32 cur_level; - WebRtc_Word32 gain32, delta; - WebRtc_Word16 logratio; - WebRtc_Word16 lower_thr, upper_thr; - WebRtc_Word16 zeros, zeros_fast, frac; - WebRtc_Word16 decay; - WebRtc_Word16 gate, gain_adj; - WebRtc_Word16 k, n; - WebRtc_Word16 L, L2; // samples/subframe - - // determine number of samples per ms - if (FS == 8000) - { - L = 8; - L2 = 3; - } else if (FS == 16000) - { - L = 16; - L2 = 4; - } else if (FS == 32000) - { - L = 16; - L2 = 4; - } else - { - return -1; - } - - memcpy(out, in_near, 10 * L * sizeof(WebRtc_Word16)); - if (FS == 32000) - { - memcpy(out_H, in_near_H, 10 * L * sizeof(WebRtc_Word16)); - } - // VAD for near end - logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out, L * 10); - - // Account for far end VAD - if (stt->vadFarend.counter > 10) - { - tmp32 = WEBRTC_SPL_MUL_16_16(3, logratio); - logratio = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 - stt->vadFarend.logRatio, 2); - } - - // Determine decay factor depending on VAD - // upper_thr = 1.0f; - // lower_thr = 0.25f; - upper_thr = 1024; // Q10 - lower_thr = 0; // Q10 - if (logratio > upper_thr) - { - // decay = -2^17 / DecayTime; -> -65 - decay = -65; - } else if (logratio < lower_thr) - { - decay = 0; - } else - { - // decay = (WebRtc_Word16)(((lower_thr - logratio) - // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10); - // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65 - tmp32 = WEBRTC_SPL_MUL_16_16((lower_thr - logratio), 65); - decay = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 10); - } - - // adjust decay factor for long silence (detected as low standard deviation) - // This is only done in the adaptive modes - if (stt->agcMode != kAgcModeFixedDigital) - { - if (stt->vadNearend.stdLongTerm < 4000) - { - decay = 0; - } else if (stt->vadNearend.stdLongTerm < 8096) - { - // decay = (WebRtc_Word16)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12); - tmp32 = WEBRTC_SPL_MUL_16_16((stt->vadNearend.stdLongTerm - 4000), decay); - decay = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 12); - } - - if (lowlevelSignal != 0) - { - decay = 0; - } - } -#ifdef AGC_DEBUG - stt->frameCounter++; - fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100, logratio, decay, stt->vadNearend.stdLongTerm); -#endif - // Find max amplitude per sub frame - // iterate over sub frames - for (k = 0; k < 10; k++) - { - // iterate over samples - max_nrg = 0; - for (n = 0; n < L; n++) - { - nrg = WEBRTC_SPL_MUL_16_16(out[k * L + n], out[k * L + n]); - if (nrg > max_nrg) - { - max_nrg = nrg; - } - } - env[k] = max_nrg; - } - - // Calculate gain per sub frame - gains[0] = stt->gain; - for (k = 0; k < 10; k++) - { - // Fast envelope follower - // decay time = -131000 / -1000 = 131 (ms) - stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast); - if (env[k] > stt->capacitorFast) - { - stt->capacitorFast = env[k]; - } - // Slow envelope follower - if (env[k] > stt->capacitorSlow) - { - // increase capacitorSlow - stt->capacitorSlow - = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow); - } else - { - // decrease capacitorSlow - stt->capacitorSlow - = AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow); - } - - // use maximum of both capacitors as current level - if (stt->capacitorFast > stt->capacitorSlow) - { - cur_level = stt->capacitorFast; - } else - { - cur_level = stt->capacitorSlow; - } - // Translate signal level into gain, using a piecewise linear approximation - // find number of leading zeros - zeros = WebRtcSpl_NormU32((WebRtc_UWord32)cur_level); - if (cur_level == 0) - { - zeros = 31; - } - tmp32 = (WEBRTC_SPL_LSHIFT_W32(cur_level, zeros) & 0x7FFFFFFF); - frac = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 19); // Q12 - tmp32 = WEBRTC_SPL_MUL((stt->gainTable[zeros-1] - stt->gainTable[zeros]), frac); - gains[k + 1] = stt->gainTable[zeros] + WEBRTC_SPL_RSHIFT_W32(tmp32, 12); -#ifdef AGC_DEBUG - if (k == 0) - { - fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros); - } -#endif - } - - // Gate processing (lower gain during absence of speech) - zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3); - // find number of leading zeros - zeros_fast = WebRtcSpl_NormU32((WebRtc_UWord32)stt->capacitorFast); - if (stt->capacitorFast == 0) - { - zeros_fast = 31; - } - tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF); - zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9); - zeros_fast -= (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 22); - - gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; - - if (gate < 0) - { - stt->gatePrevious = 0; - } else - { - tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7); - gate = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32((WebRtc_Word32)gate + tmp32, 3); - stt->gatePrevious = gate; - } - // gate < 0 -> no gate - // gate > 2500 -> max gate - if (gate > 0) - { - if (gate < 2500) - { - gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5); - } else - { - gain_adj = 0; - } - for (k = 0; k < 10; k++) - { - if ((gains[k + 1] - stt->gainTable[0]) > 8388608) - { - // To prevent wraparound - tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8); - tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj)); - } else - { - tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj)); - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8); - } - gains[k + 1] = stt->gainTable[0] + tmp32; - } - } - - // Limit gain to avoid overload distortion - for (k = 0; k < 10; k++) - { - // To prevent wrap around - zeros = 10; - if (gains[k + 1] > 47453132) - { - zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]); - } - gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1; - gain32 = WEBRTC_SPL_MUL(gain32, gain32); - // check for overflow - while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32) - > WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)32767, 2 * (1 - zeros + 10))) - { - // multiply by 253/256 ==> -0.1 dB - if (gains[k + 1] > 8388607) - { - // Prevent wrap around - gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253); - } else - { - gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8); - } - gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1; - gain32 = WEBRTC_SPL_MUL(gain32, gain32); - } - } - // gain reductions should be done 1 ms earlier than gain increases - for (k = 1; k < 10; k++) - { - if (gains[k] > gains[k + 1]) - { - gains[k] = gains[k + 1]; - } - } - // save start gain for next frame - stt->gain = gains[10]; - - // Apply gain - // handle first sub frame separately - delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2)); - gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4); - // iterate over samples - for (n = 0; n < L; n++) - { - // For lower band - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7)); - out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - if (out_tmp > 4095) - { - out[n] = (WebRtc_Word16)32767; - } else if (out_tmp < -4096) - { - out[n] = (WebRtc_Word16)-32768; - } else - { - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out[n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - } - // For higher band - if (FS == 32000) - { - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[n], - WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7)); - out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - if (out_tmp > 4095) - { - out_H[n] = (WebRtc_Word16)32767; - } else if (out_tmp < -4096) - { - out_H[n] = (WebRtc_Word16)-32768; - } else - { - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out_H[n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - } - } - // - - gain32 += delta; - } - // iterate over subframes - for (k = 1; k < 10; k++) - { - delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2)); - gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4); - // iterate over samples - for (n = 0; n < L; n++) - { - // For lower band - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out[k * L + n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out[k * L + n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - // For higher band - if (FS == 32000) - { - tmp32 = WEBRTC_SPL_MUL((WebRtc_Word32)out_H[k * L + n], - WEBRTC_SPL_RSHIFT_W32(gain32, 4)); - out_H[k * L + n] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16); - } - gain32 += delta; - } - } - - return 0; -} - -void WebRtcAgc_InitVad(AgcVad_t *state) -{ - WebRtc_Word16 k; - - state->HPstate = 0; // state of high pass filter - state->logRatio = 0; // log( P(active) / P(inactive) ) - // average input level (Q10) - state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10); - - // variance of input level (Q8) - state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8); - - state->stdLongTerm = 0; // standard deviation of input level in dB - // short-term average input level (Q10) - state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10); - - // short-term variance of input level (Q8) - state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8); - - state->stdShortTerm = 0; // short-term standard deviation of input level in dB - state->counter = 3; // counts updates - for (k = 0; k < 8; k++) - { - // downsampling filter - state->downState[k] = 0; - } -} - -WebRtc_Word16 WebRtcAgc_ProcessVad(AgcVad_t *state, // (i) VAD state - const WebRtc_Word16 *in, // (i) Speech signal - WebRtc_Word16 nrSamples) // (i) number of samples -{ - WebRtc_Word32 out, nrg, tmp32, tmp32b; - WebRtc_UWord16 tmpU16; - WebRtc_Word16 k, subfr, tmp16; - WebRtc_Word16 buf1[8]; - WebRtc_Word16 buf2[4]; - WebRtc_Word16 HPstate; - WebRtc_Word16 zeros, dB; - WebRtc_Word16 *buf1_ptr; - - // process in 10 sub frames of 1 ms (to save on memory) - nrg = 0; - buf1_ptr = &buf1[0]; - HPstate = state->HPstate; - for (subfr = 0; subfr < 10; subfr++) - { - // downsample to 4 kHz - if (nrSamples == 160) - { - for (k = 0; k < 8; k++) - { - tmp32 = (WebRtc_Word32)in[2 * k] + (WebRtc_Word32)in[2 * k + 1]; - tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1); - buf1[k] = (WebRtc_Word16)tmp32; - } - in += 16; - - WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState); - } else - { - WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState); - in += 8; - } - - // high pass filter and compute energy - for (k = 0; k < 4; k++) - { - out = buf2[k] + HPstate; - tmp32 = WEBRTC_SPL_MUL(600, out); - HPstate = (WebRtc_Word16)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]); - tmp32 = WEBRTC_SPL_MUL(out, out); - nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6); - } - } - state->HPstate = HPstate; - - // find number of leading zeros - if (!(0xFFFF0000 & nrg)) - { - zeros = 16; - } else - { - zeros = 0; - } - if (!(0xFF000000 & (nrg << zeros))) - { - zeros += 8; - } - if (!(0xF0000000 & (nrg << zeros))) - { - zeros += 4; - } - if (!(0xC0000000 & (nrg << zeros))) - { - zeros += 2; - } - if (!(0x80000000 & (nrg << zeros))) - { - zeros += 1; - } - - // energy level (range {-32..30}) (Q10) - dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11); - - // Update statistics - - if (state->counter < kAvgDecayTime) - { - // decay time = AvgDecTime * 10 ms - state->counter++; - } - - // update short-term estimate of mean energy level (Q10) - tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (WebRtc_Word32)dB); - state->meanShortTerm = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 4); - - // update short-term estimate of variance in energy level (Q8) - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12); - tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15); - state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4); - - // update short-term estimate of standard deviation in energy level (Q10) - tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm); - tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32; - state->stdShortTerm = (WebRtc_Word16)WebRtcSpl_Sqrt(tmp32); - - // update long-term estimate of mean energy level (Q10) - tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (WebRtc_Word32)dB; - state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(tmp32, - WEBRTC_SPL_ADD_SAT_W16(state->counter, 1)); - - // update long-term estimate of variance in energy level (Q8) - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12); - tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter); - state->varianceLongTerm = WebRtcSpl_DivW32W16(tmp32, - WEBRTC_SPL_ADD_SAT_W16(state->counter, 1)); - - // update long-term estimate of standard deviation in energy level (Q10) - tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm); - tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32; - state->stdLongTerm = (WebRtc_Word16)WebRtcSpl_Sqrt(tmp32); - - // update voice activity measure (Q10) - tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12); - tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm)); - tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm); - tmpU16 = WEBRTC_SPL_LSHIFT_U16((WebRtc_UWord16)13, 12); - tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16); - tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10); - - state->logRatio = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32, 6); - - // limit - if (state->logRatio > 2048) - { - state->logRatio = 2048; - } - if (state->logRatio < -2048) - { - state->logRatio = -2048; - } - - return state->logRatio; // Q10 -} diff --git a/modules/audio_processing/agc/main/source/digital_agc.h b/modules/audio_processing/agc/main/source/digital_agc.h deleted file mode 100644 index 240b22066..000000000 --- a/modules/audio_processing/agc/main/source/digital_agc.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_DIGITAL_AGC_H_ - -#ifdef AGC_DEBUG -#include -#endif -#include "typedefs.h" -#include "signal_processing_library.h" - -// the 32 most significant bits of A(19) * B(26) >> 13 -#define AGC_MUL32(A, B) (((B)>>13)*(A) + ( ((0x00001FFF & (B))*(A)) >> 13 )) -// C + the 32 most significant bits of A * B -#define AGC_SCALEDIFF32(A, B, C) ((C) + ((B)>>16)*(A) + ( ((0x0000FFFF & (B))*(A)) >> 16 )) - -typedef struct -{ - WebRtc_Word32 downState[8]; - WebRtc_Word16 HPstate; - WebRtc_Word16 counter; - WebRtc_Word16 logRatio; // log( P(active) / P(inactive) ) (Q10) - WebRtc_Word16 meanLongTerm; // Q10 - WebRtc_Word32 varianceLongTerm; // Q8 - WebRtc_Word16 stdLongTerm; // Q10 - WebRtc_Word16 meanShortTerm; // Q10 - WebRtc_Word32 varianceShortTerm; // Q8 - WebRtc_Word16 stdShortTerm; // Q10 -} AgcVad_t; // total = 54 bytes - -typedef struct -{ - WebRtc_Word32 capacitorSlow; - WebRtc_Word32 capacitorFast; - WebRtc_Word32 gain; - WebRtc_Word32 gainTable[32]; - WebRtc_Word16 gatePrevious; - WebRtc_Word16 agcMode; - AgcVad_t vadNearend; - AgcVad_t vadFarend; -#ifdef AGC_DEBUG - FILE* logFile; - int frameCounter; -#endif -} DigitalAgc_t; - -WebRtc_Word32 WebRtcAgc_InitDigital(DigitalAgc_t *digitalAgcInst, WebRtc_Word16 agcMode); - -WebRtc_Word32 WebRtcAgc_ProcessDigital(DigitalAgc_t *digitalAgcInst, const WebRtc_Word16 *inNear, - const WebRtc_Word16 *inNear_H, WebRtc_Word16 *out, - WebRtc_Word16 *out_H, WebRtc_UWord32 FS, - WebRtc_Word16 lowLevelSignal); - -WebRtc_Word32 WebRtcAgc_AddFarendToDigital(DigitalAgc_t *digitalAgcInst, const WebRtc_Word16 *inFar, - WebRtc_Word16 nrSamples); - -void WebRtcAgc_InitVad(AgcVad_t *vadInst); - -WebRtc_Word16 WebRtcAgc_ProcessVad(AgcVad_t *vadInst, // (i) VAD state - const WebRtc_Word16 *in, // (i) Speech signal - WebRtc_Word16 nrSamples); // (i) number of samples - -WebRtc_Word32 WebRtcAgc_CalculateGainTable(WebRtc_Word32 *gainTable, // Q16 - WebRtc_Word16 compressionGaindB, // Q0 (in dB) - WebRtc_Word16 targetLevelDbfs,// Q0 (in dB) - WebRtc_UWord8 limiterEnable, WebRtc_Word16 analogTarget); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_MAIN_SOURCE_ANALOG_AGC_H_ diff --git a/modules/audio_processing/main/apm_tests.gyp b/modules/audio_processing/main/apm_tests.gyp deleted file mode 100644 index 441abebb4..000000000 --- a/modules/audio_processing/main/apm_tests.gyp +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'unit_test', - 'type': 'executable', - 'dependencies': [ - 'source/apm.gyp:audio_processing', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - - '../../../../testing/gtest.gyp:gtest', - '../../../../testing/gtest.gyp:gtest_main', - '../../../../third_party/protobuf/protobuf.gyp:protobuf_lite', - ], - 'include_dirs': [ - '../../../../testing/gtest/include', - ], - 'sources': [ - 'test/unit_test/unit_test.cc', - 'test/unit_test/audio_processing_unittest.pb.cc', - 'test/unit_test/audio_processing_unittest.pb.h', - ], - }, - { - 'target_name': 'process_test', - 'type': 'executable', - 'dependencies': [ - 'source/apm.gyp:audio_processing', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - - '../../../../testing/gtest.gyp:gtest', - '../../../../testing/gtest.gyp:gtest_main', - ], - 'include_dirs': [ - '../../../../testing/gtest/include', - ], - 'sources': [ - 'test/process_test/process_test.cc', - ], - }, - - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/main/interface/audio_processing.h b/modules/audio_processing/main/interface/audio_processing.h deleted file mode 100644 index dc9c2325a..000000000 --- a/modules/audio_processing/main/interface/audio_processing.h +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_ - -#include "typedefs.h" -#include "module.h" - -namespace webrtc { - -class AudioFrame; -class EchoCancellation; -class EchoControlMobile; -class GainControl; -class HighPassFilter; -class LevelEstimator; -class NoiseSuppression; -class VoiceDetection; - -// The Audio Processing Module (APM) provides a collection of voice processing -// components designed for real-time communications software. -// -// APM operates on two audio streams on a frame-by-frame basis. Frames of the -// primary stream, on which all processing is applied, are passed to -// |ProcessStream()|. Frames of the reverse direction stream, which are used for -// analysis by some components, are passed to |AnalyzeReverseStream()|. On the -// client-side, this will typically be the near-end (capture) and far-end -// (render) streams, respectively. APM should be placed in the signal chain as -// close to the audio hardware abstraction layer (HAL) as possible. -// -// On the server-side, the reverse stream will normally not be used, with -// processing occurring on each incoming stream. -// -// Component interfaces follow a similar pattern and are accessed through -// corresponding getters in APM. All components are disabled at create-time, -// with default settings that are recommended for most situations. New settings -// can be applied without enabling a component. Enabling a component triggers -// memory allocation and initialization to allow it to start processing the -// streams. -// -// Thread safety is provided with the following assumptions to reduce locking -// overhead: -// 1. The stream getters and setters are called from the same thread as -// ProcessStream(). More precisely, stream functions are never called -// concurrently with ProcessStream(). -// 2. Parameter getters are never called concurrently with the corresponding -// setter. -// -// APM accepts only 16-bit linear PCM audio data in frames of 10 ms. Multiple -// channels should be interleaved. -// -// Usage example, omitting error checking: -// AudioProcessing* apm = AudioProcessing::Create(0); -// apm->set_sample_rate_hz(32000); // Super-wideband processing. -// -// // Mono capture and stereo render. -// apm->set_num_channels(1, 1); -// apm->set_num_reverse_channels(2); -// -// apm->high_pass_filter()->Enable(true); -// -// apm->echo_cancellation()->enable_drift_compensation(false); -// apm->echo_cancellation()->Enable(true); -// -// apm->noise_reduction()->set_level(kHighSuppression); -// apm->noise_reduction()->Enable(true); -// -// apm->gain_control()->set_analog_level_limits(0, 255); -// apm->gain_control()->set_mode(kAdaptiveAnalog); -// apm->gain_control()->Enable(true); -// -// apm->voice_detection()->Enable(true); -// -// // Start a voice call... -// -// // ... Render frame arrives bound for the audio HAL ... -// apm->AnalyzeReverseStream(render_frame); -// -// // ... Capture frame arrives from the audio HAL ... -// // Call required set_stream_ functions. -// apm->set_stream_delay_ms(delay_ms); -// apm->gain_control()->set_stream_analog_level(analog_level); -// -// apm->ProcessStream(capture_frame); -// -// // Call required stream_ functions. -// analog_level = apm->gain_control()->stream_analog_level(); -// has_voice = apm->stream_has_voice(); -// -// // Repeate render and capture processing for the duration of the call... -// // Start a new call... -// apm->Initialize(); -// -// // Close the application... -// AudioProcessing::Destroy(apm); -// apm = NULL; -// -class AudioProcessing : public Module { - public: - // Creates a APM instance, with identifier |id|. Use one instance for every - // primary audio stream requiring processing. On the client-side, this would - // typically be one instance for the near-end stream, and additional instances - // for each far-end stream which requires processing. On the server-side, - // this would typically be one instance for every incoming stream. - static AudioProcessing* Create(int id); - - // Destroys a |apm| instance. - static void Destroy(AudioProcessing* apm); - - // Initializes internal states, while retaining all user settings. This - // should be called before beginning to process a new audio stream. However, - // it is not necessary to call before processing the first stream after - // creation. - virtual int Initialize() = 0; - - // Sets the sample |rate| in Hz for both the primary and reverse audio - // streams. 8000, 16000 or 32000 Hz are permitted. - virtual int set_sample_rate_hz(int rate) = 0; - virtual int sample_rate_hz() const = 0; - - // Sets the number of channels for the primary audio stream. Input frames must - // contain a number of channels given by |input_channels|, while output frames - // will be returned with number of channels given by |output_channels|. - virtual int set_num_channels(int input_channels, int output_channels) = 0; - virtual int num_input_channels() const = 0; - virtual int num_output_channels() const = 0; - - // Sets the number of channels for the reverse audio stream. Input frames must - // contain a number of channels given by |channels|. - virtual int set_num_reverse_channels(int channels) = 0; - virtual int num_reverse_channels() const = 0; - - // Processes a 10 ms |frame| of the primary audio stream. On the client-side, - // this is the near-end (or captured) audio. - // - // If needed for enabled functionality, any function with the set_stream_ tag - // must be called prior to processing the current frame. Any getter function - // with the stream_ tag which is needed should be called after processing. - // - // The |_frequencyInHz|, |_audioChannel|, and |_payloadDataLengthInSamples| - // members of |frame| must be valid, and correspond to settings supplied - // to APM. - virtual int ProcessStream(AudioFrame* frame) = 0; - - // Analyzes a 10 ms |frame| of the reverse direction audio stream. The frame - // will not be modified. On the client-side, this is the far-end (or to be - // rendered) audio. - // - // It is only necessary to provide this if echo processing is enabled, as the - // reverse stream forms the echo reference signal. It is recommended, but not - // necessary, to provide if gain control is enabled. On the server-side this - // typically will not be used. If you're not sure what to pass in here, - // chances are you don't need to use it. - // - // The |_frequencyInHz|, |_audioChannel|, and |_payloadDataLengthInSamples| - // members of |frame| must be valid. - // - // TODO(ajm): add const to input; requires an implementation fix. - virtual int AnalyzeReverseStream(AudioFrame* frame) = 0; - - // This must be called if and only if echo processing is enabled. - // - // Sets the |delay| in ms between AnalyzeReverseStream() receiving a far-end - // frame and ProcessStream() receiving a near-end frame containing the - // corresponding echo. On the client-side this can be expressed as - // delay = (t_render - t_analyze) + (t_process - t_capture) - // where, - // - t_analyze is the time a frame is passed to AnalyzeReverseStream() and - // t_render is the time the first sample of the same frame is rendered by - // the audio hardware. - // - t_capture is the time the first sample of a frame is captured by the - // audio hardware and t_pull is the time the same frame is passed to - // ProcessStream(). - virtual int set_stream_delay_ms(int delay) = 0; - virtual int stream_delay_ms() const = 0; - - // Starts recording debugging information to a file specified by |filename|, - // a NULL-terminated string. If there is an ongoing recording, the old file - // will be closed, and recording will continue in the newly specified file. - // An already existing file will be overwritten without warning. - static const int kMaxFilenameSize = 1024; - virtual int StartDebugRecording(const char filename[kMaxFilenameSize]) = 0; - - // Stops recording debugging information, and closes the file. Recording - // cannot be resumed in the same file (without overwriting it). - virtual int StopDebugRecording() = 0; - - // These provide access to the component interfaces and should never return - // NULL. The pointers will be valid for the lifetime of the APM instance. - // The memory for these objects is entirely managed internally. - virtual EchoCancellation* echo_cancellation() const = 0; - virtual EchoControlMobile* echo_control_mobile() const = 0; - virtual GainControl* gain_control() const = 0; - virtual HighPassFilter* high_pass_filter() const = 0; - virtual LevelEstimator* level_estimator() const = 0; - virtual NoiseSuppression* noise_suppression() const = 0; - virtual VoiceDetection* voice_detection() const = 0; - - struct Statistic { - int instant; // Instantaneous value. - int average; // Long-term average. - int maximum; // Long-term maximum. - int minimum; // Long-term minimum. - }; - - // Fatal errors. - enum Errors { - kNoError = 0, - kUnspecifiedError = -1, - kCreationFailedError = -2, - kUnsupportedComponentError = -3, - kUnsupportedFunctionError = -4, - kNullPointerError = -5, - kBadParameterError = -6, - kBadSampleRateError = -7, - kBadDataLengthError = -8, - kBadNumberChannelsError = -9, - kFileError = -10, - kStreamParameterNotSetError = -11, - kNotEnabledError = -12 - }; - - // Warnings are non-fatal. - enum Warnings { - // This results when a set_stream_ parameter is out of range. Processing - // will continue, but the parameter may have been truncated. - kBadStreamParameterWarning = -13, - }; - - // Inherited from Module. - virtual WebRtc_Word32 TimeUntilNextProcess() { return -1; }; - virtual WebRtc_Word32 Process() { return -1; }; - - protected: - virtual ~AudioProcessing() {}; -}; - -// The acoustic echo cancellation (AEC) component provides better performance -// than AECM but also requires more processing power and is dependent on delay -// stability and reporting accuracy. As such it is well-suited and recommended -// for PC and IP phone applications. -// -// Not recommended to be enabled on the server-side. -class EchoCancellation { - public: - // EchoCancellation and EchoControlMobile may not be enabled simultaneously. - // Enabling one will disable the other. - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Differences in clock speed on the primary and reverse streams can impact - // the AEC performance. On the client-side, this could be seen when different - // render and capture devices are used, particularly with webcams. - // - // This enables a compensation mechanism, and requires that - // |set_device_sample_rate_hz()| and |set_stream_drift_samples()| be called. - virtual int enable_drift_compensation(bool enable) = 0; - virtual bool is_drift_compensation_enabled() const = 0; - - // Provides the sampling rate of the audio devices. It is assumed the render - // and capture devices use the same nominal sample rate. Required if and only - // if drift compensation is enabled. - virtual int set_device_sample_rate_hz(int rate) = 0; - virtual int device_sample_rate_hz() const = 0; - - // Sets the difference between the number of samples rendered and captured by - // the audio devices since the last call to |ProcessStream()|. Must be called - // if and only if drift compensation is enabled, prior to |ProcessStream()|. - virtual int set_stream_drift_samples(int drift) = 0; - virtual int stream_drift_samples() const = 0; - - enum SuppressionLevel { - kLowSuppression, - kModerateSuppression, - kHighSuppression - }; - - // Sets the aggressiveness of the suppressor. A higher level trades off - // double-talk performance for increased echo suppression. - virtual int set_suppression_level(SuppressionLevel level) = 0; - virtual SuppressionLevel suppression_level() const = 0; - - // Returns false if the current frame almost certainly contains no echo - // and true if it _might_ contain echo. - virtual bool stream_has_echo() const = 0; - - // Enables the computation of various echo metrics. These are obtained - // through |GetMetrics()|. - virtual int enable_metrics(bool enable) = 0; - virtual bool are_metrics_enabled() const = 0; - - // Each statistic is reported in dB. - // P_far: Far-end (render) signal power. - // P_echo: Near-end (capture) echo signal power. - // P_out: Signal power at the output of the AEC. - // P_a: Internal signal power at the point before the AEC's non-linear - // processor. - struct Metrics { - // RERL = ERL + ERLE - AudioProcessing::Statistic residual_echo_return_loss; - - // ERL = 10log_10(P_far / P_echo) - AudioProcessing::Statistic echo_return_loss; - - // ERLE = 10log_10(P_echo / P_out) - AudioProcessing::Statistic echo_return_loss_enhancement; - - // (Pre non-linear processing suppression) A_NLP = 10log_10(P_echo / P_a) - AudioProcessing::Statistic a_nlp; - }; - - // TODO(ajm): discuss the metrics update period. - virtual int GetMetrics(Metrics* metrics) = 0; - - protected: - virtual ~EchoCancellation() {}; -}; - -// The acoustic echo control for mobile (AECM) component is a low complexity -// robust option intended for use on mobile devices. -// -// Not recommended to be enabled on the server-side. -class EchoControlMobile { - public: - // EchoCancellation and EchoControlMobile may not be enabled simultaneously. - // Enabling one will disable the other. - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Recommended settings for particular audio routes. In general, the louder - // the echo is expected to be, the higher this value should be set. The - // preferred setting may vary from device to device. - enum RoutingMode { - kQuietEarpieceOrHeadset, - kEarpiece, - kLoudEarpiece, - kSpeakerphone, - kLoudSpeakerphone - }; - - // Sets echo control appropriate for the audio routing |mode| on the device. - // It can and should be updated during a call if the audio routing changes. - virtual int set_routing_mode(RoutingMode mode) = 0; - virtual RoutingMode routing_mode() const = 0; - - // Comfort noise replaces suppressed background noise to maintain a - // consistent signal level. - virtual int enable_comfort_noise(bool enable) = 0; - virtual bool is_comfort_noise_enabled() const = 0; - - protected: - virtual ~EchoControlMobile() {}; -}; - -// The automatic gain control (AGC) component brings the signal to an -// appropriate range. This is done by applying a digital gain directly and, in -// the analog mode, prescribing an analog gain to be applied at the audio HAL. -// -// Recommended to be enabled on the client-side. -class GainControl { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // When an analog mode is set, this must be called prior to |ProcessStream()| - // to pass the current analog level from the audio HAL. Must be within the - // range provided to |set_analog_level_limits()|. - virtual int set_stream_analog_level(int level) = 0; - - // When an analog mode is set, this should be called after |ProcessStream()| - // to obtain the recommended new analog level for the audio HAL. It is the - // users responsibility to apply this level. - virtual int stream_analog_level() = 0; - - enum Mode { - // Adaptive mode intended for use if an analog volume control is available - // on the capture device. It will require the user to provide coupling - // between the OS mixer controls and AGC through the |stream_analog_level()| - // functions. - // - // It consists of an analog gain prescription for the audio device and a - // digital compression stage. - kAdaptiveAnalog, - - // Adaptive mode intended for situations in which an analog volume control - // is unavailable. It operates in a similar fashion to the adaptive analog - // mode, but with scaling instead applied in the digital domain. As with - // the analog mode, it additionally uses a digital compression stage. - kAdaptiveDigital, - - // Fixed mode which enables only the digital compression stage also used by - // the two adaptive modes. - // - // It is distinguished from the adaptive modes by considering only a - // short time-window of the input signal. It applies a fixed gain through - // most of the input level range, and compresses (gradually reduces gain - // with increasing level) the input signal at higher levels. This mode is - // preferred on embedded devices where the capture signal level is - // predictable, so that a known gain can be applied. - kFixedDigital - }; - - virtual int set_mode(Mode mode) = 0; - virtual Mode mode() const = 0; - - // Sets the target peak |level| (or envelope) of the AGC in dBFs (decibels - // from digital full-scale). The convention is to use positive values. For - // instance, passing in a value of 3 corresponds to -3 dBFs, or a target - // level 3 dB below full-scale. Limited to [0, 31]. - // - // TODO(ajm): use a negative value here instead, if/when VoE will similarly - // update its interface. - virtual int set_target_level_dbfs(int level) = 0; - virtual int target_level_dbfs() const = 0; - - // Sets the maximum |gain| the digital compression stage may apply, in dB. A - // higher number corresponds to greater compression, while a value of 0 will - // leave the signal uncompressed. Limited to [0, 90]. - virtual int set_compression_gain_db(int gain) = 0; - virtual int compression_gain_db() const = 0; - - // When enabled, the compression stage will hard limit the signal to the - // target level. Otherwise, the signal will be compressed but not limited - // above the target level. - virtual int enable_limiter(bool enable) = 0; - virtual bool is_limiter_enabled() const = 0; - - // Sets the |minimum| and |maximum| analog levels of the audio capture device. - // Must be set if and only if an analog mode is used. Limited to [0, 65535]. - virtual int set_analog_level_limits(int minimum, - int maximum) = 0; - virtual int analog_level_minimum() const = 0; - virtual int analog_level_maximum() const = 0; - - // Returns true if the AGC has detected a saturation event (period where the - // signal reaches digital full-scale) in the current frame and the analog - // level cannot be reduced. - // - // This could be used as an indicator to reduce or disable analog mic gain at - // the audio HAL. - virtual bool stream_is_saturated() const = 0; - - protected: - virtual ~GainControl() {}; -}; - -// A filtering component which removes DC offset and low-frequency noise. -// Recommended to be enabled on the client-side. -class HighPassFilter { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - protected: - virtual ~HighPassFilter() {}; -}; - -// An estimation component used to retrieve level metrics. -class LevelEstimator { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // The metrics are reported in dBFs calculated as: - // Level = 10log_10(P_s / P_max) [dBFs], where - // P_s is the signal power and P_max is the maximum possible (or peak) - // power. With 16-bit signals, P_max = (2^15)^2. - struct Metrics { - AudioProcessing::Statistic signal; // Overall signal level. - AudioProcessing::Statistic speech; // Speech level. - AudioProcessing::Statistic noise; // Noise level. - }; - - virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics) = 0; - - //virtual int enable_noise_warning(bool enable) = 0; - //bool is_noise_warning_enabled() const = 0; - //virtual bool stream_has_high_noise() const = 0; - - protected: - virtual ~LevelEstimator() {}; -}; - -// The noise suppression (NS) component attempts to remove noise while -// retaining speech. Recommended to be enabled on the client-side. -// -// Recommended to be enabled on the client-side. -class NoiseSuppression { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Determines the aggressiveness of the suppression. Increasing the level - // will reduce the noise level at the expense of a higher speech distortion. - enum Level { - kLow, - kModerate, - kHigh, - kVeryHigh - }; - - virtual int set_level(Level level) = 0; - virtual Level level() const = 0; - - protected: - virtual ~NoiseSuppression() {}; -}; - -// The voice activity detection (VAD) component analyzes the stream to -// determine if voice is present. A facility is also provided to pass in an -// external VAD decision. -class VoiceDetection { - public: - virtual int Enable(bool enable) = 0; - virtual bool is_enabled() const = 0; - - // Returns true if voice is detected in the current frame. Should be called - // after |ProcessStream()|. - virtual bool stream_has_voice() const = 0; - - // Some of the APM functionality requires a VAD decision. In the case that - // a decision is externally available for the current frame, it can be passed - // in here, before |ProcessStream()| is called. - // - // VoiceDetection does _not_ need to be enabled to use this. If it happens to - // be enabled, detection will be skipped for any frame in which an external - // VAD decision is provided. - virtual int set_stream_has_voice(bool has_voice) = 0; - - // Specifies the likelihood that a frame will be declared to contain voice. - // A higher value makes it more likely that speech will not be clipped, at - // the expense of more noise being detected as voice. - enum Likelihood { - kVeryLowLikelihood, - kLowLikelihood, - kModerateLikelihood, - kHighLikelihood - }; - - virtual int set_likelihood(Likelihood likelihood) = 0; - virtual Likelihood likelihood() const = 0; - - // Sets the |size| of the frames in ms on which the VAD will operate. Larger - // frames will improve detection accuracy, but reduce the frequency of - // updates. - // - // This does not impact the size of frames passed to |ProcessStream()|. - virtual int set_frame_size_ms(int size) = 0; - virtual int frame_size_ms() const = 0; - - protected: - virtual ~VoiceDetection() {}; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_INTERFACE_AUDIO_PROCESSING_H_ diff --git a/modules/audio_processing/main/source/Android.mk b/modules/audio_processing/main/source/Android.mk deleted file mode 100644 index 12c7bc758..000000000 --- a/modules/audio_processing/main/source/Android.mk +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := libwebrtc_apm -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := audio_buffer.cc \ - audio_processing_impl.cc \ - echo_cancellation_impl.cc \ - echo_control_mobile_impl.cc \ - gain_control_impl.cc \ - high_pass_filter_impl.cc \ - level_estimator_impl.cc \ - noise_suppression_impl.cc \ - splitting_filter.cc \ - processing_component.cc \ - voice_detection_impl.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' \ - '-DWEBRTC_NS_FIXED' -# floating point -# -DWEBRTC_NS_FLOAT' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../aec/main/interface \ - $(LOCAL_PATH)/../../aecm/main/interface \ - $(LOCAL_PATH)/../../agc/main/interface \ - $(LOCAL_PATH)/../../ns/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing_library/main/interface \ - $(LOCAL_PATH)/../../../../common_audio/vad/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport - -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) - diff --git a/modules/audio_processing/main/source/apm.gyp b/modules/audio_processing/main/source/apm.gyp deleted file mode 100644 index 93811c71f..000000000 --- a/modules/audio_processing/main/source/apm.gyp +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'audio_processing', - 'type': '<(library)', - 'conditions': [ - ['prefer_fixed_point==1', { - 'dependencies': ['../../ns/main/source/ns.gyp:ns_fix'], - 'defines': ['WEBRTC_NS_FIXED'], - }, { # else: prefer_fixed_point==0 - 'dependencies': ['../../ns/main/source/ns.gyp:ns'], - 'defines': ['WEBRTC_NS_FLOAT'], - }], - ], - 'dependencies': [ - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../aec/main/source/aec.gyp:aec', - '../../aecm/main/source/aecm.gyp:aecm', - '../../agc/main/source/agc.gyp:agc', - '../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../../common_audio/vad/main/source/vad.gyp:vad', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../interface', - ], - }, - 'sources': [ - '../interface/audio_processing.h', - 'audio_buffer.cc', - 'audio_buffer.h', - 'audio_processing_impl.cc', - 'audio_processing_impl.h', - 'echo_cancellation_impl.cc', - 'echo_cancellation_impl.h', - 'echo_control_mobile_impl.cc', - 'echo_control_mobile_impl.h', - 'gain_control_impl.cc', - 'gain_control_impl.h', - 'high_pass_filter_impl.cc', - 'high_pass_filter_impl.h', - 'level_estimator_impl.cc', - 'level_estimator_impl.h', - 'noise_suppression_impl.cc', - 'noise_suppression_impl.h', - 'splitting_filter.cc', - 'splitting_filter.h', - 'processing_component.cc', - 'processing_component.h', - 'voice_detection_impl.cc', - 'voice_detection_impl.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/main/source/audio_buffer.cc b/modules/audio_processing/main/source/audio_buffer.cc deleted file mode 100644 index 6b20fcece..000000000 --- a/modules/audio_processing/main/source/audio_buffer.cc +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_buffer.h" - -#include "module_common_types.h" - -namespace webrtc { -namespace { - -enum { - kSamplesPer8kHzChannel = 80, - kSamplesPer16kHzChannel = 160, - kSamplesPer32kHzChannel = 320 -}; - -void StereoToMono(const WebRtc_Word16* left, const WebRtc_Word16* right, - WebRtc_Word16* out, int samples_per_channel) { - WebRtc_Word32 data_int32 = 0; - for (int i = 0; i < samples_per_channel; i++) { - data_int32 = (left[i] + right[i]) >> 1; - if (data_int32 > 32767) { - data_int32 = 32767; - } else if (data_int32 < -32768) { - data_int32 = -32768; - } - - out[i] = static_cast(data_int32); - } -} -} // namespace - -struct AudioChannel { - AudioChannel() { - memset(data, 0, sizeof(data)); - } - - WebRtc_Word16 data[kSamplesPer32kHzChannel]; -}; - -struct SplitAudioChannel { - SplitAudioChannel() { - memset(low_pass_data, 0, sizeof(low_pass_data)); - memset(high_pass_data, 0, sizeof(high_pass_data)); - memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1)); - memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2)); - memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1)); - memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2)); - } - - WebRtc_Word16 low_pass_data[kSamplesPer16kHzChannel]; - WebRtc_Word16 high_pass_data[kSamplesPer16kHzChannel]; - - WebRtc_Word32 analysis_filter_state1[6]; - WebRtc_Word32 analysis_filter_state2[6]; - WebRtc_Word32 synthesis_filter_state1[6]; - WebRtc_Word32 synthesis_filter_state2[6]; -}; - -// TODO(am): check range of input parameters? -AudioBuffer::AudioBuffer(WebRtc_Word32 max_num_channels, - WebRtc_Word32 samples_per_channel) - : max_num_channels_(max_num_channels), - num_channels_(0), - num_mixed_channels_(0), - num_mixed_low_pass_channels_(0), - samples_per_channel_(samples_per_channel), - samples_per_split_channel_(samples_per_channel), - reference_copied_(false), - data_(NULL), - channels_(NULL), - split_channels_(NULL), - mixed_low_pass_channels_(NULL), - low_pass_reference_channels_(NULL) { - if (max_num_channels_ > 1) { - channels_ = new AudioChannel[max_num_channels_]; - mixed_low_pass_channels_ = new AudioChannel[max_num_channels_]; - } - low_pass_reference_channels_ = new AudioChannel[max_num_channels_]; - - if (samples_per_channel_ == kSamplesPer32kHzChannel) { - split_channels_ = new SplitAudioChannel[max_num_channels_]; - samples_per_split_channel_ = kSamplesPer16kHzChannel; - } -} - -AudioBuffer::~AudioBuffer() { - if (channels_ != NULL) { - delete [] channels_; - } - - if (mixed_low_pass_channels_ != NULL) { - delete [] mixed_low_pass_channels_; - } - - if (low_pass_reference_channels_ != NULL) { - delete [] low_pass_reference_channels_; - } - - if (split_channels_ != NULL) { - delete [] split_channels_; - } -} - -WebRtc_Word16* AudioBuffer::data(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - if (data_ != NULL) { - return data_; - } - - return channels_[channel].data; -} - -WebRtc_Word16* AudioBuffer::low_pass_split_data(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - if (split_channels_ == NULL) { - return data(channel); - } - - return split_channels_[channel].low_pass_data; -} - -WebRtc_Word16* AudioBuffer::high_pass_split_data(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - if (split_channels_ == NULL) { - return NULL; - } - - return split_channels_[channel].high_pass_data; -} - -WebRtc_Word16* AudioBuffer::mixed_low_pass_data(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_mixed_low_pass_channels_); - - return mixed_low_pass_channels_[channel].data; -} - -WebRtc_Word16* AudioBuffer::low_pass_reference(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - if (!reference_copied_) { - return NULL; - } - - return low_pass_reference_channels_[channel].data; -} - -WebRtc_Word32* AudioBuffer::analysis_filter_state1(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].analysis_filter_state1; -} - -WebRtc_Word32* AudioBuffer::analysis_filter_state2(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].analysis_filter_state2; -} - -WebRtc_Word32* AudioBuffer::synthesis_filter_state1(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].synthesis_filter_state1; -} - -WebRtc_Word32* AudioBuffer::synthesis_filter_state2(WebRtc_Word32 channel) const { - assert(channel >= 0 && channel < num_channels_); - return split_channels_[channel].synthesis_filter_state2; -} - -WebRtc_Word32 AudioBuffer::num_channels() const { - return num_channels_; -} - -WebRtc_Word32 AudioBuffer::samples_per_channel() const { - return samples_per_channel_; -} - -WebRtc_Word32 AudioBuffer::samples_per_split_channel() const { - return samples_per_split_channel_; -} - -// TODO(ajm): Do deinterleaving and mixing in one step? -void AudioBuffer::DeinterleaveFrom(AudioFrame* audioFrame) { - assert(audioFrame->_audioChannel <= max_num_channels_); - assert(audioFrame->_payloadDataLengthInSamples == samples_per_channel_); - - num_channels_ = audioFrame->_audioChannel; - num_mixed_channels_ = 0; - num_mixed_low_pass_channels_ = 0; - reference_copied_ = false; - - if (num_channels_ == 1) { - // We can get away with a pointer assignment in this case. - data_ = audioFrame->_payloadData; - return; - } - - for (int i = 0; i < num_channels_; i++) { - WebRtc_Word16* deinterleaved = channels_[i].data; - WebRtc_Word16* interleaved = audioFrame->_payloadData; - WebRtc_Word32 interleaved_idx = i; - for (int j = 0; j < samples_per_channel_; j++) { - deinterleaved[j] = interleaved[interleaved_idx]; - interleaved_idx += num_channels_; - } - } -} - -void AudioBuffer::InterleaveTo(AudioFrame* audioFrame) const { - assert(audioFrame->_audioChannel == num_channels_); - assert(audioFrame->_payloadDataLengthInSamples == samples_per_channel_); - - if (num_channels_ == 1) { - if (num_mixed_channels_ == 1) { - memcpy(audioFrame->_payloadData, - channels_[0].data, - sizeof(WebRtc_Word16) * samples_per_channel_); - } else { - // These should point to the same buffer in this case. - assert(data_ == audioFrame->_payloadData); - } - - return; - } - - for (int i = 0; i < num_channels_; i++) { - WebRtc_Word16* deinterleaved = channels_[i].data; - WebRtc_Word16* interleaved = audioFrame->_payloadData; - WebRtc_Word32 interleaved_idx = i; - for (int j = 0; j < samples_per_channel_; j++) { - interleaved[interleaved_idx] = deinterleaved[j]; - interleaved_idx += num_channels_; - } - } -} - -// TODO(ajm): would be good to support the no-mix case with pointer assignment. -// TODO(ajm): handle mixing to multiple channels? -void AudioBuffer::Mix(WebRtc_Word32 num_mixed_channels) { - // We currently only support the stereo to mono case. - assert(num_channels_ == 2); - assert(num_mixed_channels == 1); - - StereoToMono(channels_[0].data, - channels_[1].data, - channels_[0].data, - samples_per_channel_); - - num_channels_ = num_mixed_channels; - num_mixed_channels_ = num_mixed_channels; -} - -void AudioBuffer::CopyAndMixLowPass(WebRtc_Word32 num_mixed_channels) { - // We currently only support the stereo to mono case. - assert(num_channels_ == 2); - assert(num_mixed_channels == 1); - - StereoToMono(low_pass_split_data(0), - low_pass_split_data(1), - mixed_low_pass_channels_[0].data, - samples_per_split_channel_); - - num_mixed_low_pass_channels_ = num_mixed_channels; -} - -void AudioBuffer::CopyLowPassToReference() { - reference_copied_ = true; - for (int i = 0; i < num_channels_; i++) { - memcpy(low_pass_reference_channels_[i].data, - low_pass_split_data(i), - sizeof(WebRtc_Word16) * samples_per_split_channel_); - } -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/audio_buffer.h b/modules/audio_processing/main/source/audio_buffer.h deleted file mode 100644 index 15f850b67..000000000 --- a/modules/audio_processing/main/source/audio_buffer.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ - -#include "typedefs.h" - - -namespace webrtc { - -struct AudioChannel; -struct SplitAudioChannel; -class AudioFrame; - -class AudioBuffer { - public: - AudioBuffer(WebRtc_Word32 max_num_channels, WebRtc_Word32 samples_per_channel); - virtual ~AudioBuffer(); - - WebRtc_Word32 num_channels() const; - WebRtc_Word32 samples_per_channel() const; - WebRtc_Word32 samples_per_split_channel() const; - - WebRtc_Word16* data(WebRtc_Word32 channel) const; - WebRtc_Word16* low_pass_split_data(WebRtc_Word32 channel) const; - WebRtc_Word16* high_pass_split_data(WebRtc_Word32 channel) const; - WebRtc_Word16* mixed_low_pass_data(WebRtc_Word32 channel) const; - WebRtc_Word16* low_pass_reference(WebRtc_Word32 channel) const; - - WebRtc_Word32* analysis_filter_state1(WebRtc_Word32 channel) const; - WebRtc_Word32* analysis_filter_state2(WebRtc_Word32 channel) const; - WebRtc_Word32* synthesis_filter_state1(WebRtc_Word32 channel) const; - WebRtc_Word32* synthesis_filter_state2(WebRtc_Word32 channel) const; - - void DeinterleaveFrom(AudioFrame* audioFrame); - void InterleaveTo(AudioFrame* audioFrame) const; - void Mix(WebRtc_Word32 num_mixed_channels); - void CopyAndMixLowPass(WebRtc_Word32 num_mixed_channels); - void CopyLowPassToReference(); - - private: - const WebRtc_Word32 max_num_channels_; - WebRtc_Word32 num_channels_; - WebRtc_Word32 num_mixed_channels_; - WebRtc_Word32 num_mixed_low_pass_channels_; - const WebRtc_Word32 samples_per_channel_; - WebRtc_Word32 samples_per_split_channel_; - bool reference_copied_; - - WebRtc_Word16* data_; - // TODO(ajm): Prefer to make these vectors if permitted... - AudioChannel* channels_; - SplitAudioChannel* split_channels_; - // TODO(ajm): improve this, we don't need the full 32 kHz space here. - AudioChannel* mixed_low_pass_channels_; - AudioChannel* low_pass_reference_channels_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_BUFFER_H_ diff --git a/modules/audio_processing/main/source/audio_processing_impl.cc b/modules/audio_processing/main/source/audio_processing_impl.cc deleted file mode 100644 index 6440e36ec..000000000 --- a/modules/audio_processing/main/source/audio_processing_impl.cc +++ /dev/null @@ -1,636 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "audio_processing_impl.h" - -#include - -#include "module_common_types.h" - -#include "critical_section_wrapper.h" -#include "file_wrapper.h" - -#include "audio_buffer.h" -#include "echo_cancellation_impl.h" -#include "echo_control_mobile_impl.h" -#include "high_pass_filter_impl.h" -#include "gain_control_impl.h" -#include "level_estimator_impl.h" -#include "noise_suppression_impl.h" -#include "processing_component.h" -#include "splitting_filter.h" -#include "voice_detection_impl.h" - -namespace webrtc { -namespace { - -enum Events { - kInitializeEvent, - kRenderEvent, - kCaptureEvent -}; - -const char kMagicNumber[] = "#!vqetrace1.2"; -} // namespace - -AudioProcessing* AudioProcessing::Create(int id) { - /*WEBRTC_TRACE(webrtc::kTraceModuleCall, - webrtc::kTraceAudioProcessing, - id, - "AudioProcessing::Create()");*/ - - AudioProcessingImpl* apm = new AudioProcessingImpl(id); - if (apm->Initialize() != kNoError) { - delete apm; - apm = NULL; - } - - return apm; -} - -void AudioProcessing::Destroy(AudioProcessing* apm) { - delete static_cast(apm); -} - -AudioProcessingImpl::AudioProcessingImpl(int id) - : id_(id), - echo_cancellation_(NULL), - echo_control_mobile_(NULL), - gain_control_(NULL), - high_pass_filter_(NULL), - level_estimator_(NULL), - noise_suppression_(NULL), - voice_detection_(NULL), - debug_file_(FileWrapper::Create()), - crit_(CriticalSectionWrapper::CreateCriticalSection()), - render_audio_(NULL), - capture_audio_(NULL), - sample_rate_hz_(kSampleRate16kHz), - split_sample_rate_hz_(kSampleRate16kHz), - samples_per_channel_(sample_rate_hz_ / 100), - stream_delay_ms_(0), - was_stream_delay_set_(false), - num_render_input_channels_(1), - num_capture_input_channels_(1), - num_capture_output_channels_(1) { - - echo_cancellation_ = new EchoCancellationImpl(this); - component_list_.push_back(echo_cancellation_); - - echo_control_mobile_ = new EchoControlMobileImpl(this); - component_list_.push_back(echo_control_mobile_); - - gain_control_ = new GainControlImpl(this); - component_list_.push_back(gain_control_); - - high_pass_filter_ = new HighPassFilterImpl(this); - component_list_.push_back(high_pass_filter_); - - level_estimator_ = new LevelEstimatorImpl(this); - component_list_.push_back(level_estimator_); - - noise_suppression_ = new NoiseSuppressionImpl(this); - component_list_.push_back(noise_suppression_); - - voice_detection_ = new VoiceDetectionImpl(this); - component_list_.push_back(voice_detection_); -} - -AudioProcessingImpl::~AudioProcessingImpl() { - while (!component_list_.empty()) { - ProcessingComponent* component = component_list_.front(); - component->Destroy(); - delete component; - component_list_.pop_front(); - } - - if (debug_file_->Open()) { - debug_file_->CloseFile(); - } - delete debug_file_; - debug_file_ = NULL; - - delete crit_; - crit_ = NULL; - - if (render_audio_ != NULL) { - delete render_audio_; - render_audio_ = NULL; - } - - if (capture_audio_ != NULL) { - delete capture_audio_; - capture_audio_ = NULL; - } -} - -CriticalSectionWrapper* AudioProcessingImpl::crit() const { - return crit_; -} - -int AudioProcessingImpl::split_sample_rate_hz() const { - return split_sample_rate_hz_; -} - -int AudioProcessingImpl::Initialize() { - CriticalSectionScoped crit_scoped(*crit_); - return InitializeLocked(); -} - -int AudioProcessingImpl::InitializeLocked() { - if (render_audio_ != NULL) { - delete render_audio_; - render_audio_ = NULL; - } - - if (capture_audio_ != NULL) { - delete capture_audio_; - capture_audio_ = NULL; - } - - render_audio_ = new AudioBuffer(num_render_input_channels_, - samples_per_channel_); - capture_audio_ = new AudioBuffer(num_capture_input_channels_, - samples_per_channel_); - - was_stream_delay_set_ = false; - - // Initialize all components. - std::list::iterator it; - for (it = component_list_.begin(); it != component_list_.end(); it++) { - int err = (*it)->Initialize(); - if (err != kNoError) { - return err; - } - } - - return kNoError; -} - -int AudioProcessingImpl::set_sample_rate_hz(int rate) { - CriticalSectionScoped crit_scoped(*crit_); - if (rate != kSampleRate8kHz && - rate != kSampleRate16kHz && - rate != kSampleRate32kHz) { - return kBadParameterError; - } - - sample_rate_hz_ = rate; - samples_per_channel_ = rate / 100; - - if (sample_rate_hz_ == kSampleRate32kHz) { - split_sample_rate_hz_ = kSampleRate16kHz; - } else { - split_sample_rate_hz_ = sample_rate_hz_; - } - - return InitializeLocked(); -} - -int AudioProcessingImpl::sample_rate_hz() const { - return sample_rate_hz_; -} - -int AudioProcessingImpl::set_num_reverse_channels(int channels) { - CriticalSectionScoped crit_scoped(*crit_); - // Only stereo supported currently. - if (channels > 2 || channels < 1) { - return kBadParameterError; - } - - num_render_input_channels_ = channels; - - return InitializeLocked(); -} - -int AudioProcessingImpl::num_reverse_channels() const { - return num_render_input_channels_; -} - -int AudioProcessingImpl::set_num_channels( - int input_channels, - int output_channels) { - CriticalSectionScoped crit_scoped(*crit_); - if (output_channels > input_channels) { - return kBadParameterError; - } - - // Only stereo supported currently. - if (input_channels > 2 || input_channels < 1) { - return kBadParameterError; - } - - if (output_channels > 2 || output_channels < 1) { - return kBadParameterError; - } - - num_capture_input_channels_ = input_channels; - num_capture_output_channels_ = output_channels; - - return InitializeLocked(); -} - -int AudioProcessingImpl::num_input_channels() const { - return num_capture_input_channels_; -} - -int AudioProcessingImpl::num_output_channels() const { - return num_capture_output_channels_; -} - -int AudioProcessingImpl::ProcessStream(AudioFrame* frame) { - CriticalSectionScoped crit_scoped(*crit_); - int err = kNoError; - - if (frame == NULL) { - return kNullPointerError; - } - - if (frame->_frequencyInHz != - static_cast(sample_rate_hz_)) { - return kBadSampleRateError; - } - - if (frame->_audioChannel != num_capture_input_channels_) { - return kBadNumberChannelsError; - } - - if (frame->_payloadDataLengthInSamples != samples_per_channel_) { - return kBadDataLengthError; - } - - if (debug_file_->Open()) { - WebRtc_UWord8 event = kCaptureEvent; - if (!debug_file_->Write(&event, sizeof(event))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_frequencyInHz, - sizeof(frame->_frequencyInHz))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_audioChannel, - sizeof(frame->_audioChannel))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_payloadDataLengthInSamples, - sizeof(frame->_payloadDataLengthInSamples))) { - return kFileError; - } - - if (!debug_file_->Write(frame->_payloadData, - sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples * - frame->_audioChannel)) { - return kFileError; - } - } - - capture_audio_->DeinterleaveFrom(frame); - - // TODO(ajm): experiment with mixing and AEC placement. - if (num_capture_output_channels_ < num_capture_input_channels_) { - capture_audio_->Mix(num_capture_output_channels_); - - frame->_audioChannel = num_capture_output_channels_; - } - - if (sample_rate_hz_ == kSampleRate32kHz) { - for (int i = 0; i < num_capture_input_channels_; i++) { - // Split into a low and high band. - SplittingFilterAnalysis(capture_audio_->data(i), - capture_audio_->low_pass_split_data(i), - capture_audio_->high_pass_split_data(i), - capture_audio_->analysis_filter_state1(i), - capture_audio_->analysis_filter_state2(i)); - } - } - - err = high_pass_filter_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = gain_control_->AnalyzeCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = echo_cancellation_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - if (echo_control_mobile_->is_enabled() && - noise_suppression_->is_enabled()) { - capture_audio_->CopyLowPassToReference(); - } - - err = noise_suppression_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = echo_control_mobile_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = voice_detection_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - err = gain_control_->ProcessCaptureAudio(capture_audio_); - if (err != kNoError) { - return err; - } - - //err = level_estimator_->ProcessCaptureAudio(capture_audio_); - //if (err != kNoError) { - // return err; - //} - - if (sample_rate_hz_ == kSampleRate32kHz) { - for (int i = 0; i < num_capture_output_channels_; i++) { - // Recombine low and high bands. - SplittingFilterSynthesis(capture_audio_->low_pass_split_data(i), - capture_audio_->high_pass_split_data(i), - capture_audio_->data(i), - capture_audio_->synthesis_filter_state1(i), - capture_audio_->synthesis_filter_state2(i)); - } - } - - capture_audio_->InterleaveTo(frame); - - return kNoError; -} - -int AudioProcessingImpl::AnalyzeReverseStream(AudioFrame* frame) { - CriticalSectionScoped crit_scoped(*crit_); - int err = kNoError; - - if (frame == NULL) { - return kNullPointerError; - } - - if (frame->_frequencyInHz != - static_cast(sample_rate_hz_)) { - return kBadSampleRateError; - } - - if (frame->_audioChannel != num_render_input_channels_) { - return kBadNumberChannelsError; - } - - if (frame->_payloadDataLengthInSamples != samples_per_channel_) { - return kBadDataLengthError; - } - - if (debug_file_->Open()) { - WebRtc_UWord8 event = kRenderEvent; - if (!debug_file_->Write(&event, sizeof(event))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_frequencyInHz, - sizeof(frame->_frequencyInHz))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_audioChannel, - sizeof(frame->_audioChannel))) { - return kFileError; - } - - if (!debug_file_->Write(&frame->_payloadDataLengthInSamples, - sizeof(frame->_payloadDataLengthInSamples))) { - return kFileError; - } - - if (!debug_file_->Write(frame->_payloadData, - sizeof(WebRtc_Word16) * frame->_payloadDataLengthInSamples * - frame->_audioChannel)) { - return kFileError; - } - } - - render_audio_->DeinterleaveFrom(frame); - - // TODO(ajm): turn the splitting filter into a component? - if (sample_rate_hz_ == kSampleRate32kHz) { - for (int i = 0; i < num_render_input_channels_; i++) { - // Split into low and high band. - SplittingFilterAnalysis(render_audio_->data(i), - render_audio_->low_pass_split_data(i), - render_audio_->high_pass_split_data(i), - render_audio_->analysis_filter_state1(i), - render_audio_->analysis_filter_state2(i)); - } - } - - // TODO(ajm): warnings possible from components? - err = echo_cancellation_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } - - err = echo_control_mobile_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } - - err = gain_control_->ProcessRenderAudio(render_audio_); - if (err != kNoError) { - return err; - } - - //err = level_estimator_->AnalyzeReverseStream(render_audio_); - //if (err != kNoError) { - // return err; - //} - - was_stream_delay_set_ = false; - return err; // TODO(ajm): this is for returning warnings; necessary? -} - -int AudioProcessingImpl::set_stream_delay_ms(int delay) { - was_stream_delay_set_ = true; - if (delay < 0) { - return kBadParameterError; - } - - // TODO(ajm): the max is rather arbitrarily chosen; investigate. - if (delay > 500) { - stream_delay_ms_ = 500; - return kBadStreamParameterWarning; - } - - stream_delay_ms_ = delay; - return kNoError; -} - -int AudioProcessingImpl::stream_delay_ms() const { - return stream_delay_ms_; -} - -bool AudioProcessingImpl::was_stream_delay_set() const { - return was_stream_delay_set_; -} - -int AudioProcessingImpl::StartDebugRecording( - const char filename[AudioProcessing::kMaxFilenameSize]) { - CriticalSectionScoped crit_scoped(*crit_); - assert(kMaxFilenameSize == FileWrapper::kMaxFileNameSize); - - if (filename == NULL) { - return kNullPointerError; - } - - // Stop any ongoing recording. - if (debug_file_->Open()) { - if (debug_file_->CloseFile() == -1) { - return kFileError; - } - } - - if (debug_file_->OpenFile(filename, false) == -1) { - debug_file_->CloseFile(); - return kFileError; - } - - if (debug_file_->WriteText("%s\n", kMagicNumber) == -1) { - debug_file_->CloseFile(); - return kFileError; - } - - // TODO(ajm): should we do this? If so, we need the number of channels etc. - // Record the default sample rate. - WebRtc_UWord8 event = kInitializeEvent; - if (!debug_file_->Write(&event, sizeof(event))) { - return kFileError; - } - - if (!debug_file_->Write(&sample_rate_hz_, sizeof(sample_rate_hz_))) { - return kFileError; - } - - return kNoError; -} - -int AudioProcessingImpl::StopDebugRecording() { - CriticalSectionScoped crit_scoped(*crit_); - // We just return if recording hasn't started. - if (debug_file_->Open()) { - if (debug_file_->CloseFile() == -1) { - return kFileError; - } - } - - return kNoError; -} - -EchoCancellation* AudioProcessingImpl::echo_cancellation() const { - return echo_cancellation_; -} - -EchoControlMobile* AudioProcessingImpl::echo_control_mobile() const { - return echo_control_mobile_; -} - -GainControl* AudioProcessingImpl::gain_control() const { - return gain_control_; -} - -HighPassFilter* AudioProcessingImpl::high_pass_filter() const { - return high_pass_filter_; -} - -LevelEstimator* AudioProcessingImpl::level_estimator() const { - return level_estimator_; -} - -NoiseSuppression* AudioProcessingImpl::noise_suppression() const { - return noise_suppression_; -} - -VoiceDetection* AudioProcessingImpl::voice_detection() const { - return voice_detection_; -} - -WebRtc_Word32 AudioProcessingImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& bytes_remaining, WebRtc_UWord32& position) const { - if (version == NULL) { - /*WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceAudioProcessing, - -1, - "Null version pointer");*/ - return kNullPointerError; - } - memset(&version[position], 0, bytes_remaining); - - WebRtc_Word8 my_version[] = "AudioProcessing 1.0.0"; - // Includes null termination. - WebRtc_UWord32 length = static_cast(strlen(my_version)); - if (bytes_remaining < length) { - /*WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceAudioProcessing, - -1, - "Buffer of insufficient length");*/ - return kBadParameterError; - } - memcpy(&version[position], my_version, length); - bytes_remaining -= length; - position += length; - - std::list::const_iterator it; - for (it = component_list_.begin(); it != component_list_.end(); it++) { - char component_version[256]; - strcpy(component_version, "\n"); - int err = (*it)->get_version(&component_version[1], - sizeof(component_version) - 1); - if (err != kNoError) { - return err; - } - if (strncmp(&component_version[1], "\0", 1) == 0) { - // Assume empty if first byte is NULL. - continue; - } - - length = static_cast(strlen(component_version)); - if (bytes_remaining < length) { - /*WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceAudioProcessing, - -1, - "Buffer of insufficient length");*/ - return kBadParameterError; - } - memcpy(&version[position], component_version, length); - bytes_remaining -= length; - position += length; - } - - return kNoError; -} - -WebRtc_Word32 AudioProcessingImpl::ChangeUniqueId(const WebRtc_Word32 id) { - CriticalSectionScoped crit_scoped(*crit_); - /*WEBRTC_TRACE(webrtc::kTraceModuleCall, - webrtc::kTraceAudioProcessing, - id_, - "ChangeUniqueId(new id = %d)", - id);*/ - id_ = id; - - return kNoError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/audio_processing_impl.h b/modules/audio_processing/main/source/audio_processing_impl.h deleted file mode 100644 index 9707bde24..000000000 --- a/modules/audio_processing/main/source/audio_processing_impl.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_ - -#include - -#include "audio_processing.h" - -namespace webrtc { -class CriticalSectionWrapper; -class FileWrapper; - -class AudioBuffer; -class EchoCancellationImpl; -class EchoControlMobileImpl; -class GainControlImpl; -class HighPassFilterImpl; -class LevelEstimatorImpl; -class NoiseSuppressionImpl; -class ProcessingComponent; -class VoiceDetectionImpl; - -class AudioProcessingImpl : public AudioProcessing { - public: - enum { - kSampleRate8kHz = 8000, - kSampleRate16kHz = 16000, - kSampleRate32kHz = 32000 - }; - - explicit AudioProcessingImpl(int id); - virtual ~AudioProcessingImpl(); - - CriticalSectionWrapper* crit() const; - - int split_sample_rate_hz() const; - bool was_stream_delay_set() const; - - // AudioProcessing methods. - virtual int Initialize(); - virtual int InitializeLocked(); - virtual int set_sample_rate_hz(int rate); - virtual int sample_rate_hz() const; - virtual int set_num_channels(int input_channels, int output_channels); - virtual int num_input_channels() const; - virtual int num_output_channels() const; - virtual int set_num_reverse_channels(int channels); - virtual int num_reverse_channels() const; - virtual int ProcessStream(AudioFrame* frame); - virtual int AnalyzeReverseStream(AudioFrame* frame); - virtual int set_stream_delay_ms(int delay); - virtual int stream_delay_ms() const; - virtual int StartDebugRecording(const char filename[kMaxFilenameSize]); - virtual int StopDebugRecording(); - virtual EchoCancellation* echo_cancellation() const; - virtual EchoControlMobile* echo_control_mobile() const; - virtual GainControl* gain_control() const; - virtual HighPassFilter* high_pass_filter() const; - virtual LevelEstimator* level_estimator() const; - virtual NoiseSuppression* noise_suppression() const; - virtual VoiceDetection* voice_detection() const; - - // Module methods. - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - private: - int id_; - - EchoCancellationImpl* echo_cancellation_; - EchoControlMobileImpl* echo_control_mobile_; - GainControlImpl* gain_control_; - HighPassFilterImpl* high_pass_filter_; - LevelEstimatorImpl* level_estimator_; - NoiseSuppressionImpl* noise_suppression_; - VoiceDetectionImpl* voice_detection_; - - std::list component_list_; - - FileWrapper* debug_file_; - CriticalSectionWrapper* crit_; - - AudioBuffer* render_audio_; - AudioBuffer* capture_audio_; - - int sample_rate_hz_; - int split_sample_rate_hz_; - int samples_per_channel_; - int stream_delay_ms_; - bool was_stream_delay_set_; - - int num_render_input_channels_; - int num_capture_input_channels_; - int num_capture_output_channels_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_AUDIO_PROCESSING_IMPL_H_ diff --git a/modules/audio_processing/main/source/echo_cancellation_impl.cc b/modules/audio_processing/main/source/echo_cancellation_impl.cc deleted file mode 100644 index 886d5f158..000000000 --- a/modules/audio_processing/main/source/echo_cancellation_impl.cc +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "echo_cancellation_impl.h" - -#include -#include - -#include "critical_section_wrapper.h" -#include "echo_cancellation.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { - -typedef void Handle; - -namespace { -WebRtc_Word16 MapSetting(EchoCancellation::SuppressionLevel level) { - switch (level) { - case EchoCancellation::kLowSuppression: - return kAecNlpConservative; - case EchoCancellation::kModerateSuppression: - return kAecNlpModerate; - case EchoCancellation::kHighSuppression: - return kAecNlpAggressive; - default: - return -1; - } -} - -int MapError(int err) { - switch (err) { - case AEC_UNSUPPORTED_FUNCTION_ERROR: - return AudioProcessing::kUnsupportedFunctionError; - break; - case AEC_BAD_PARAMETER_ERROR: - return AudioProcessing::kBadParameterError; - break; - case AEC_BAD_PARAMETER_WARNING: - return AudioProcessing::kBadStreamParameterWarning; - break; - default: - // AEC_UNSPECIFIED_ERROR - // AEC_UNINITIALIZED_ERROR - // AEC_NULL_POINTER_ERROR - return AudioProcessing::kUnspecifiedError; - } -} -} // namespace - -EchoCancellationImpl::EchoCancellationImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm), - drift_compensation_enabled_(false), - metrics_enabled_(false), - suppression_level_(kModerateSuppression), - device_sample_rate_hz_(48000), - stream_drift_samples_(0), - was_stream_drift_set_(false), - stream_has_echo_(false) {} - -EchoCancellationImpl::~EchoCancellationImpl() {} - -int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == apm_->num_reverse_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AEC. - size_t handle_index = 0; - for (int i = 0; i < apm_->num_output_channels(); i++) { - for (int j = 0; j < audio->num_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAec_BufferFarend( - my_handle, - audio->low_pass_split_data(j), - static_cast(audio->samples_per_split_channel())); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } - - handle_index++; - } - } - - return apm_->kNoError; -} - -int EchoCancellationImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (!apm_->was_stream_delay_set()) { - return apm_->kStreamParameterNotSetError; - } - - if (drift_compensation_enabled_ && !was_stream_drift_set_) { - return apm_->kStreamParameterNotSetError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == apm_->num_output_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AEC. - size_t handle_index = 0; - stream_has_echo_ = false; - for (int i = 0; i < audio->num_channels(); i++) { - for (int j = 0; j < apm_->num_reverse_channels(); j++) { - Handle* my_handle = handle(handle_index); - err = WebRtcAec_Process( - my_handle, - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - static_cast(audio->samples_per_split_channel()), - apm_->stream_delay_ms(), - stream_drift_samples_); - - if (err != apm_->kNoError) { - err = GetHandleError(my_handle); - // TODO(ajm): Figure out how to return warnings properly. - if (err != apm_->kBadStreamParameterWarning) { - return err; - } - } - - WebRtc_Word16 status = 0; - err = WebRtcAec_get_echo_status(my_handle, &status); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - if (status == 1) { - stream_has_echo_ = true; - } - - handle_index++; - } - } - - was_stream_drift_set_ = false; - return apm_->kNoError; -} - -int EchoCancellationImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - // Ensure AEC and AECM are not both enabled. - if (enable && apm_->echo_control_mobile()->is_enabled()) { - return apm_->kBadParameterError; - } - - return EnableComponent(enable); -} - -bool EchoCancellationImpl::is_enabled() const { - return is_component_enabled(); -} - -int EchoCancellationImpl::set_suppression_level(SuppressionLevel level) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (MapSetting(level) == -1) { - return apm_->kBadParameterError; - } - - suppression_level_ = level; - return Configure(); -} - -EchoCancellation::SuppressionLevel EchoCancellationImpl::suppression_level() - const { - return suppression_level_; -} - -int EchoCancellationImpl::enable_drift_compensation(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - drift_compensation_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::is_drift_compensation_enabled() const { - return drift_compensation_enabled_; -} - -int EchoCancellationImpl::set_device_sample_rate_hz(int rate) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (rate < 8000 || rate > 96000) { - return apm_->kBadParameterError; - } - - device_sample_rate_hz_ = rate; - return Initialize(); -} - -int EchoCancellationImpl::device_sample_rate_hz() const { - return device_sample_rate_hz_; -} - -int EchoCancellationImpl::set_stream_drift_samples(int drift) { - was_stream_drift_set_ = true; - stream_drift_samples_ = drift; - return apm_->kNoError; -} - -int EchoCancellationImpl::stream_drift_samples() const { - return stream_drift_samples_; -} - -int EchoCancellationImpl::enable_metrics(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - metrics_enabled_ = enable; - return Configure(); -} - -bool EchoCancellationImpl::are_metrics_enabled() const { - return metrics_enabled_; -} - -// TODO(ajm): we currently just use the metrics from the first AEC. Think more -// aboue the best way to extend this to multi-channel. -int EchoCancellationImpl::GetMetrics(Metrics* metrics) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (metrics == NULL) { - return apm_->kNullPointerError; - } - - if (!is_component_enabled() || !metrics_enabled_) { - return apm_->kNotEnabledError; - } - - AecMetrics my_metrics; - memset(&my_metrics, 0, sizeof(my_metrics)); - memset(metrics, 0, sizeof(Metrics)); - - Handle* my_handle = static_cast(handle(0)); - int err = WebRtcAec_GetMetrics(my_handle, &my_metrics); - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - metrics->residual_echo_return_loss.instant = my_metrics.rerl.instant; - metrics->residual_echo_return_loss.average = my_metrics.rerl.average; - metrics->residual_echo_return_loss.maximum = my_metrics.rerl.max; - metrics->residual_echo_return_loss.minimum = my_metrics.rerl.min; - - metrics->echo_return_loss.instant = my_metrics.erl.instant; - metrics->echo_return_loss.average = my_metrics.erl.average; - metrics->echo_return_loss.maximum = my_metrics.erl.max; - metrics->echo_return_loss.minimum = my_metrics.erl.min; - - metrics->echo_return_loss_enhancement.instant = my_metrics.erle.instant; - metrics->echo_return_loss_enhancement.average = my_metrics.erle.average; - metrics->echo_return_loss_enhancement.maximum = my_metrics.erle.max; - metrics->echo_return_loss_enhancement.minimum = my_metrics.erle.min; - - metrics->a_nlp.instant = my_metrics.aNlp.instant; - metrics->a_nlp.average = my_metrics.aNlp.average; - metrics->a_nlp.maximum = my_metrics.aNlp.max; - metrics->a_nlp.minimum = my_metrics.aNlp.min; - - return apm_->kNoError; -} - -bool EchoCancellationImpl::stream_has_echo() const { - return stream_has_echo_; -} - -int EchoCancellationImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } - - was_stream_drift_set_ = false; - - return apm_->kNoError; -} - -int EchoCancellationImpl::get_version(char* version, - int version_len_bytes) const { - if (WebRtcAec_get_version(version, version_len_bytes) != 0) { - return apm_->kBadParameterError; - } - - return apm_->kNoError; -} - -void* EchoCancellationImpl::CreateHandle() const { - Handle* handle = NULL; - if (WebRtcAec_Create(&handle) != apm_->kNoError) { - handle = NULL; - } else { - assert(handle != NULL); - } - - return handle; -} - -int EchoCancellationImpl::DestroyHandle(void* handle) const { - assert(handle != NULL); - return WebRtcAec_Free(static_cast(handle)); -} - -int EchoCancellationImpl::InitializeHandle(void* handle) const { - assert(handle != NULL); - return WebRtcAec_Init(static_cast(handle), - apm_->sample_rate_hz(), - device_sample_rate_hz_); -} - -int EchoCancellationImpl::ConfigureHandle(void* handle) const { - assert(handle != NULL); - AecConfig config; - config.metricsMode = metrics_enabled_; - config.nlpMode = MapSetting(suppression_level_); - config.skewMode = drift_compensation_enabled_; - - return WebRtcAec_set_config(static_cast(handle), config); -} - -int EchoCancellationImpl::num_handles_required() const { - return apm_->num_output_channels() * - apm_->num_reverse_channels(); -} - -int EchoCancellationImpl::GetHandleError(void* handle) const { - assert(handle != NULL); - return MapError(WebRtcAec_get_error_code(static_cast(handle))); -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/echo_cancellation_impl.h b/modules/audio_processing/main/source/echo_cancellation_impl.h deleted file mode 100644 index 380a69849..000000000 --- a/modules/audio_processing/main/source/echo_cancellation_impl.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class EchoCancellationImpl : public EchoCancellation, - public ProcessingComponent { - public: - explicit EchoCancellationImpl(const AudioProcessingImpl* apm); - virtual ~EchoCancellationImpl(); - - int ProcessRenderAudio(const AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // EchoCancellation implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int Initialize(); - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // EchoCancellation implementation. - virtual int Enable(bool enable); - virtual int enable_drift_compensation(bool enable); - virtual bool is_drift_compensation_enabled() const; - virtual int set_device_sample_rate_hz(int rate); - virtual int device_sample_rate_hz() const; - virtual int set_stream_drift_samples(int drift); - virtual int stream_drift_samples() const; - virtual int set_suppression_level(SuppressionLevel level); - virtual SuppressionLevel suppression_level() const; - virtual int enable_metrics(bool enable); - virtual bool are_metrics_enabled() const; - virtual bool stream_has_echo() const; - virtual int GetMetrics(Metrics* metrics); - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; - bool drift_compensation_enabled_; - bool metrics_enabled_; - SuppressionLevel suppression_level_; - int device_sample_rate_hz_; - int stream_drift_samples_; - bool was_stream_drift_set_; - bool stream_has_echo_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CANCELLATION_IMPL_H_ diff --git a/modules/audio_processing/main/source/echo_control_mobile_impl.cc b/modules/audio_processing/main/source/echo_control_mobile_impl.cc deleted file mode 100644 index 1cd2502e2..000000000 --- a/modules/audio_processing/main/source/echo_control_mobile_impl.cc +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "echo_control_mobile_impl.h" - -#include - -#include "critical_section_wrapper.h" -#include "echo_control_mobile.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { - -typedef void Handle; - -namespace { -WebRtc_Word16 MapSetting(EchoControlMobile::RoutingMode mode) { - switch (mode) { - case EchoControlMobile::kQuietEarpieceOrHeadset: - return 0; - case EchoControlMobile::kEarpiece: - return 1; - case EchoControlMobile::kLoudEarpiece: - return 2; - case EchoControlMobile::kSpeakerphone: - return 3; - case EchoControlMobile::kLoudSpeakerphone: - return 4; - default: - return -1; - } -} - -int MapError(int err) { - switch (err) { - case AECM_UNSUPPORTED_FUNCTION_ERROR: - return AudioProcessing::kUnsupportedFunctionError; - case AECM_BAD_PARAMETER_ERROR: - return AudioProcessing::kBadParameterError; - case AECM_BAD_PARAMETER_WARNING: - return AudioProcessing::kBadStreamParameterWarning; - default: - // AECMOBFIX_UNSPECIFIED_ERROR - // AECMOBFIX_UNINITIALIZED_ERROR - // AECMOBFIX_NULL_POINTER_ERROR - return AudioProcessing::kUnspecifiedError; - } -} -} // namespace - -EchoControlMobileImpl::EchoControlMobileImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm), - routing_mode_(kSpeakerphone), - comfort_noise_enabled_(true) {} - -EchoControlMobileImpl::~EchoControlMobileImpl() {} - -int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == apm_->num_reverse_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AECM. - size_t handle_index = 0; - for (int i = 0; i < apm_->num_output_channels(); i++) { - for (int j = 0; j < audio->num_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAecm_BufferFarend( - my_handle, - audio->low_pass_split_data(j), - static_cast(audio->samples_per_split_channel())); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } - - handle_index++; - } - } - - return apm_->kNoError; -} - -int EchoControlMobileImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (!apm_->was_stream_delay_set()) { - return apm_->kStreamParameterNotSetError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == apm_->num_output_channels()); - - int err = apm_->kNoError; - - // The ordering convention must be followed to pass to the correct AECM. - size_t handle_index = 0; - for (int i = 0; i < audio->num_channels(); i++) { - // TODO(ajm): improve how this works, possibly inside AECM. - // This is kind of hacked up. - WebRtc_Word16* noisy = audio->low_pass_reference(i); - WebRtc_Word16* clean = audio->low_pass_split_data(i); - if (noisy == NULL) { - noisy = clean; - clean = NULL; - } - for (int j = 0; j < apm_->num_reverse_channels(); j++) { - Handle* my_handle = static_cast(handle(handle_index)); - err = WebRtcAecm_Process( - my_handle, - noisy, - clean, - audio->low_pass_split_data(i), - static_cast(audio->samples_per_split_channel()), - apm_->stream_delay_ms()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); // TODO(ajm): warning possible? - } - - handle_index++; - } - } - - return apm_->kNoError; -} - -int EchoControlMobileImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - // Ensure AEC and AECM are not both enabled. - if (enable && apm_->echo_cancellation()->is_enabled()) { - return apm_->kBadParameterError; - } - - return EnableComponent(enable); -} - -bool EchoControlMobileImpl::is_enabled() const { - return is_component_enabled(); -} - -int EchoControlMobileImpl::set_routing_mode(RoutingMode mode) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (MapSetting(mode) == -1) { - return apm_->kBadParameterError; - } - - routing_mode_ = mode; - return Configure(); -} - -EchoControlMobile::RoutingMode EchoControlMobileImpl::routing_mode() - const { - return routing_mode_; -} - -int EchoControlMobileImpl::enable_comfort_noise(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - comfort_noise_enabled_ = enable; - return Configure(); -} - -bool EchoControlMobileImpl::is_comfort_noise_enabled() const { - return comfort_noise_enabled_; -} - -int EchoControlMobileImpl::Initialize() { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (apm_->sample_rate_hz() == apm_->kSampleRate32kHz) { - // AECM doesn't support super-wideband. - return apm_->kBadSampleRateError; - } - - return ProcessingComponent::Initialize(); -} - -int EchoControlMobileImpl::get_version(char* version, - int version_len_bytes) const { - if (WebRtcAecm_get_version(version, version_len_bytes) != 0) { - return apm_->kBadParameterError; - } - - return apm_->kNoError; -} - -void* EchoControlMobileImpl::CreateHandle() const { - Handle* handle = NULL; - if (WebRtcAecm_Create(&handle) != apm_->kNoError) { - handle = NULL; - } else { - assert(handle != NULL); - } - - return handle; -} - -int EchoControlMobileImpl::DestroyHandle(void* handle) const { - return WebRtcAecm_Free(static_cast(handle)); -} - -int EchoControlMobileImpl::InitializeHandle(void* handle) const { - return WebRtcAecm_Init(static_cast(handle), - apm_->sample_rate_hz(), - 48000); // Dummy value. This isn't actually - // required by AECM. -} - -int EchoControlMobileImpl::ConfigureHandle(void* handle) const { - AecmConfig config; - config.cngMode = comfort_noise_enabled_; - config.echoMode = MapSetting(routing_mode_); - - return WebRtcAecm_set_config(static_cast(handle), config); -} - -int EchoControlMobileImpl::num_handles_required() const { - return apm_->num_output_channels() * - apm_->num_reverse_channels(); -} - -int EchoControlMobileImpl::GetHandleError(void* handle) const { - assert(handle != NULL); - return MapError(WebRtcAecm_get_error_code(static_cast(handle))); -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/echo_control_mobile_impl.h b/modules/audio_processing/main/source/echo_control_mobile_impl.h deleted file mode 100644 index 2fd624810..000000000 --- a/modules/audio_processing/main/source/echo_control_mobile_impl.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class EchoControlMobileImpl : public EchoControlMobile, - public ProcessingComponent { - public: - explicit EchoControlMobileImpl(const AudioProcessingImpl* apm); - virtual ~EchoControlMobileImpl(); - - int ProcessRenderAudio(const AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // EchoControlMobile implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int Initialize(); - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // EchoControlMobile implementation. - virtual int Enable(bool enable); - virtual int set_routing_mode(RoutingMode mode); - virtual RoutingMode routing_mode() const; - virtual int enable_comfort_noise(bool enable); - virtual bool is_comfort_noise_enabled() const; - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; - RoutingMode routing_mode_; - bool comfort_noise_enabled_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_ECHO_CONTROL_MOBILE_IMPL_H_ diff --git a/modules/audio_processing/main/source/gain_control_impl.cc b/modules/audio_processing/main/source/gain_control_impl.cc deleted file mode 100644 index dc3e56558..000000000 --- a/modules/audio_processing/main/source/gain_control_impl.cc +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "gain_control_impl.h" - -#include - -#include "critical_section_wrapper.h" -#include "gain_control.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { - -typedef void Handle; - -/*template -class GainControlHandle : public ComponentHandle { - public: - GainControlHandle(); - virtual ~GainControlHandle(); - - virtual int Create(); - virtual T* ptr() const; - - private: - T* handle; -};*/ - -namespace { -WebRtc_Word16 MapSetting(GainControl::Mode mode) { - switch (mode) { - case GainControl::kAdaptiveAnalog: - return kAgcModeAdaptiveAnalog; - break; - case GainControl::kAdaptiveDigital: - return kAgcModeAdaptiveDigital; - break; - case GainControl::kFixedDigital: - return kAgcModeFixedDigital; - break; - default: - return -1; - } -} -} // namespace - -GainControlImpl::GainControlImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm), - mode_(kAdaptiveAnalog), - minimum_capture_level_(0), - maximum_capture_level_(255), - limiter_enabled_(true), - target_level_dbfs_(3), - compression_gain_db_(9), - analog_capture_level_(0), - was_analog_level_set_(false), - stream_is_saturated_(false) {} - -GainControlImpl::~GainControlImpl() {} - -int GainControlImpl::ProcessRenderAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->samples_per_split_channel() <= 160); - - WebRtc_Word16* mixed_data = audio->low_pass_split_data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMixLowPass(1); - mixed_data = audio->mixed_low_pass_data(0); - } - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - int err = WebRtcAgc_AddFarend( - my_handle, - mixed_data, - static_cast(audio->samples_per_split_channel())); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - } - - return apm_->kNoError; -} - -int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == num_handles()); - - int err = apm_->kNoError; - - if (mode_ == kAdaptiveAnalog) { - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - err = WebRtcAgc_AddMic( - my_handle, - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - static_cast(audio->samples_per_split_channel())); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - } - } else if (mode_ == kAdaptiveDigital) { - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - WebRtc_Word32 capture_level_out = 0; - - err = WebRtcAgc_VirtualMic( - my_handle, - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - static_cast(audio->samples_per_split_channel()), - //capture_levels_[i], - analog_capture_level_, - &capture_level_out); - - capture_levels_[i] = capture_level_out; - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - } - } - - return apm_->kNoError; -} - -int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (mode_ == kAdaptiveAnalog && !was_analog_level_set_) { - return apm_->kStreamParameterNotSetError; - } - - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == num_handles()); - - stream_is_saturated_ = false; - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - WebRtc_Word32 capture_level_out = 0; - WebRtc_UWord8 saturation_warning = 0; - - int err = WebRtcAgc_Process( - my_handle, - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - static_cast(audio->samples_per_split_channel()), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - capture_levels_[i], - &capture_level_out, - apm_->echo_cancellation()->stream_has_echo(), - &saturation_warning); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - - capture_levels_[i] = capture_level_out; - if (saturation_warning == 1) { - stream_is_saturated_ = true; - } - } - - if (mode_ == kAdaptiveAnalog) { - // Take the analog level to be the average across the handles. - analog_capture_level_ = 0; - for (int i = 0; i < num_handles(); i++) { - analog_capture_level_ += capture_levels_[i]; - } - - analog_capture_level_ /= num_handles(); - } - - was_analog_level_set_ = false; - return apm_->kNoError; -} - -// TODO(ajm): ensure this is called under kAdaptiveAnalog. -int GainControlImpl::set_stream_analog_level(int level) { - was_analog_level_set_ = true; - if (level < minimum_capture_level_ || level > maximum_capture_level_) { - return apm_->kBadParameterError; - } - - if (mode_ == kAdaptiveAnalog) { - if (level != analog_capture_level_) { - // The analog level has been changed; update our internal levels. - capture_levels_.assign(num_handles(), level); - } - } - analog_capture_level_ = level; - - return apm_->kNoError; -} - -int GainControlImpl::stream_analog_level() { - // TODO(ajm): enable this assertion? - //assert(mode_ == kAdaptiveAnalog); - - return analog_capture_level_; -} - -int GainControlImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - return EnableComponent(enable); -} - -bool GainControlImpl::is_enabled() const { - return is_component_enabled(); -} - -int GainControlImpl::set_mode(Mode mode) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (MapSetting(mode) == -1) { - return apm_->kBadParameterError; - } - - mode_ = mode; - return Initialize(); -} - -GainControl::Mode GainControlImpl::mode() const { - return mode_; -} - -int GainControlImpl::set_analog_level_limits(int minimum, - int maximum) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (minimum < 0) { - return apm_->kBadParameterError; - } - - if (maximum > 65535) { - return apm_->kBadParameterError; - } - - if (maximum < minimum) { - return apm_->kBadParameterError; - } - - minimum_capture_level_ = minimum; - maximum_capture_level_ = maximum; - - return Initialize(); -} - -int GainControlImpl::analog_level_minimum() const { - return minimum_capture_level_; -} - -int GainControlImpl::analog_level_maximum() const { - return maximum_capture_level_; -} - -bool GainControlImpl::stream_is_saturated() const { - return stream_is_saturated_; -} - -int GainControlImpl::set_target_level_dbfs(int level) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (level > 31 || level < 0) { - return apm_->kBadParameterError; - } - - target_level_dbfs_ = level; - return Configure(); -} - -int GainControlImpl::target_level_dbfs() const { - return target_level_dbfs_; -} - -int GainControlImpl::set_compression_gain_db(int gain) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (gain < 0 || gain > 90) { - return apm_->kBadParameterError; - } - - compression_gain_db_ = gain; - return Configure(); -} - -int GainControlImpl::compression_gain_db() const { - return compression_gain_db_; -} - -int GainControlImpl::enable_limiter(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - limiter_enabled_ = enable; - return Configure(); -} - -bool GainControlImpl::is_limiter_enabled() const { - return limiter_enabled_; -} - -int GainControlImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } - - analog_capture_level_ = - (maximum_capture_level_ - minimum_capture_level_) >> 1; - capture_levels_.assign(num_handles(), analog_capture_level_); - was_analog_level_set_ = false; - - return apm_->kNoError; -} - -int GainControlImpl::get_version(char* version, int version_len_bytes) const { - if (WebRtcAgc_Version(version, version_len_bytes) != 0) { - return apm_->kBadParameterError; - } - - return apm_->kNoError; -} - -void* GainControlImpl::CreateHandle() const { - Handle* handle = NULL; - if (WebRtcAgc_Create(&handle) != apm_->kNoError) { - handle = NULL; - } else { - assert(handle != NULL); - } - - return handle; -} - -int GainControlImpl::DestroyHandle(void* handle) const { - return WebRtcAgc_Free(static_cast(handle)); -} - -int GainControlImpl::InitializeHandle(void* handle) const { - return WebRtcAgc_Init(static_cast(handle), - minimum_capture_level_, - maximum_capture_level_, - MapSetting(mode_), - apm_->sample_rate_hz()); -} - -int GainControlImpl::ConfigureHandle(void* handle) const { - WebRtcAgc_config_t config; - // TODO(ajm): Flip the sign here (since AGC expects a positive value) if we - // change the interface. - //assert(target_level_dbfs_ <= 0); - //config.targetLevelDbfs = static_cast(-target_level_dbfs_); - config.targetLevelDbfs = static_cast(target_level_dbfs_); - config.compressionGaindB = - static_cast(compression_gain_db_); - config.limiterEnable = limiter_enabled_; - - return WebRtcAgc_set_config(static_cast(handle), config); -} - -int GainControlImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int GainControlImpl::GetHandleError(void* handle) const { - // The AGC has no get_error() function. - // (Despite listing errors in its interface...) - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/gain_control_impl.h b/modules/audio_processing/main/source/gain_control_impl.h deleted file mode 100644 index a11d606f4..000000000 --- a/modules/audio_processing/main/source/gain_control_impl.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_ - -#include - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class GainControlImpl : public GainControl, - public ProcessingComponent { - public: - explicit GainControlImpl(const AudioProcessingImpl* apm); - virtual ~GainControlImpl(); - - int ProcessRenderAudio(AudioBuffer* audio); - int AnalyzeCaptureAudio(AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // ProcessingComponent implementation. - virtual int Initialize(); - virtual int get_version(char* version, int version_len_bytes) const; - - // GainControl implementation. - virtual bool is_enabled() const; - - private: - // GainControl implementation. - virtual int Enable(bool enable); - virtual int set_stream_analog_level(int level); - virtual int stream_analog_level(); - virtual int set_mode(Mode mode); - virtual Mode mode() const; - virtual int set_target_level_dbfs(int level); - virtual int target_level_dbfs() const; - virtual int set_compression_gain_db(int gain); - virtual int compression_gain_db() const; - virtual int enable_limiter(bool enable); - virtual bool is_limiter_enabled() const; - virtual int set_analog_level_limits(int minimum, int maximum); - virtual int analog_level_minimum() const; - virtual int analog_level_maximum() const; - virtual bool stream_is_saturated() const; - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; - Mode mode_; - int minimum_capture_level_; - int maximum_capture_level_; - bool limiter_enabled_; - int target_level_dbfs_; - int compression_gain_db_; - std::vector capture_levels_; - int analog_capture_level_; - bool was_analog_level_set_; - bool stream_is_saturated_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_GAIN_CONTROL_IMPL_H_ diff --git a/modules/audio_processing/main/source/high_pass_filter_impl.cc b/modules/audio_processing/main/source/high_pass_filter_impl.cc deleted file mode 100644 index fa6d5d5ec..000000000 --- a/modules/audio_processing/main/source/high_pass_filter_impl.cc +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "high_pass_filter_impl.h" - -#include - -#include "critical_section_wrapper.h" -#include "typedefs.h" -#include "signal_processing_library.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { -namespace { -const WebRtc_Word16 kFilterCoefficients8kHz[5] = - {3798, -7596, 3798, 7807, -3733}; - -const WebRtc_Word16 kFilterCoefficients[5] = - {4012, -8024, 4012, 8002, -3913}; - -struct FilterState { - WebRtc_Word16 y[4]; - WebRtc_Word16 x[2]; - const WebRtc_Word16* ba; -}; - -int InitializeFilter(FilterState* hpf, int sample_rate_hz) { - assert(hpf != NULL); - - if (sample_rate_hz == AudioProcessingImpl::kSampleRate8kHz) { - hpf->ba = kFilterCoefficients8kHz; - } else { - hpf->ba = kFilterCoefficients; - } - - WebRtcSpl_MemSetW16(hpf->x, 0, 2); - WebRtcSpl_MemSetW16(hpf->y, 0, 4); - - return AudioProcessing::kNoError; -} - -int Filter(FilterState* hpf, WebRtc_Word16* data, int length) { - assert(hpf != NULL); - - WebRtc_Word32 tmp_int32 = 0; - WebRtc_Word16* y = hpf->y; - WebRtc_Word16* x = hpf->x; - const WebRtc_Word16* ba = hpf->ba; - - for (int i = 0; i < length; i++) { - // y[i] = b[0] * x[i] + b[1] * x[i-1] + b[2] * x[i-2] - // + -a[1] * y[i-1] + -a[2] * y[i-2]; - - tmp_int32 = - WEBRTC_SPL_MUL_16_16(y[1], ba[3]); // -a[1] * y[i-1] (low part) - tmp_int32 += - WEBRTC_SPL_MUL_16_16(y[3], ba[4]); // -a[2] * y[i-2] (low part) - tmp_int32 = (tmp_int32 >> 15); - tmp_int32 += - WEBRTC_SPL_MUL_16_16(y[0], ba[3]); // -a[1] * y[i-1] (high part) - tmp_int32 += - WEBRTC_SPL_MUL_16_16(y[2], ba[4]); // -a[2] * y[i-2] (high part) - tmp_int32 = (tmp_int32 << 1); - - tmp_int32 += WEBRTC_SPL_MUL_16_16(data[i], ba[0]); // b[0]*x[0] - tmp_int32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]); // b[1]*x[i-1] - tmp_int32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]); // b[2]*x[i-2] - - // Update state (input part) - x[1] = x[0]; - x[0] = data[i]; - - // Update state (filtered part) - y[2] = y[0]; - y[3] = y[1]; - y[0] = static_cast(tmp_int32 >> 13); - y[1] = static_cast((tmp_int32 - - WEBRTC_SPL_LSHIFT_W32(static_cast(y[0]), 13)) << 2); - - // Rounding in Q12, i.e. add 2^11 - tmp_int32 += 2048; - - // Saturate (to 2^27) so that the HP filtered signal does not overflow - tmp_int32 = WEBRTC_SPL_SAT(static_cast(134217727), - tmp_int32, - static_cast(-134217728)); - - // Convert back to Q0 and use rounding - data[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp_int32, 12); - - } - - return AudioProcessing::kNoError; -} -} // namespace - -typedef FilterState Handle; - -HighPassFilterImpl::HighPassFilterImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm) {} - -HighPassFilterImpl::~HighPassFilterImpl() {} - -int HighPassFilterImpl::ProcessCaptureAudio(AudioBuffer* audio) { - int err = apm_->kNoError; - - if (!is_component_enabled()) { - return apm_->kNoError; - } - - assert(audio->samples_per_split_channel() <= 160); - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); - err = Filter(my_handle, - audio->low_pass_split_data(i), - audio->samples_per_split_channel()); - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - } - - return apm_->kNoError; -} - -int HighPassFilterImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - return EnableComponent(enable); -} - -bool HighPassFilterImpl::is_enabled() const { - return is_component_enabled(); -} - -int HighPassFilterImpl::get_version(char* version, - int version_len_bytes) const { - // An empty string is used to indicate no version information. - memset(version, 0, version_len_bytes); - return apm_->kNoError; -} - -void* HighPassFilterImpl::CreateHandle() const { - return new FilterState; -} - -int HighPassFilterImpl::DestroyHandle(void* handle) const { - delete static_cast(handle); - return apm_->kNoError; -} - -int HighPassFilterImpl::InitializeHandle(void* handle) const { - return InitializeFilter(static_cast(handle), - apm_->sample_rate_hz()); -} - -int HighPassFilterImpl::ConfigureHandle(void* /*handle*/) const { - return apm_->kNoError; // Not configurable. -} - -int HighPassFilterImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int HighPassFilterImpl::GetHandleError(void* handle) const { - // The component has no detailed errors. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/high_pass_filter_impl.h b/modules/audio_processing/main/source/high_pass_filter_impl.h deleted file mode 100644 index 4c2375427..000000000 --- a/modules/audio_processing/main/source/high_pass_filter_impl.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class HighPassFilterImpl : public HighPassFilter, - public ProcessingComponent { - public: - explicit HighPassFilterImpl(const AudioProcessingImpl* apm); - virtual ~HighPassFilterImpl(); - - int ProcessCaptureAudio(AudioBuffer* audio); - - // HighPassFilter implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // HighPassFilter implementation. - virtual int Enable(bool enable); - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_HIGH_PASS_FILTER_IMPL_H_ diff --git a/modules/audio_processing/main/source/level_estimator_impl.cc b/modules/audio_processing/main/source/level_estimator_impl.cc deleted file mode 100644 index 799a9624f..000000000 --- a/modules/audio_processing/main/source/level_estimator_impl.cc +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "level_estimator_impl.h" - -#include -#include - -#include "critical_section_wrapper.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -// TODO(ajm): implement the underlying level estimator component. - -namespace webrtc { - -typedef void Handle; - -namespace { -/*int EstimateLevel(AudioBuffer* audio, Handle* my_handle) { - assert(audio->samples_per_split_channel() <= 160); - - WebRtc_Word16* mixed_data = audio->low_pass_split_data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMixLowPass(1); - mixed_data = audio->mixed_low_pass_data(0); - } - - int err = UpdateLvlEst(my_handle, - mixed_data, - audio->samples_per_split_channel()); - if (err != AudioProcessing::kNoError) { - return GetHandleError(my_handle); - } - - return AudioProcessing::kNoError; -} - -int GetMetricsLocal(Handle* my_handle, LevelEstimator::Metrics* metrics) { - level_t levels; - memset(&levels, 0, sizeof(levels)); - - int err = ExportLevels(my_handle, &levels, 2); - if (err != AudioProcessing::kNoError) { - return err; - } - metrics->signal.instant = levels.instant; - metrics->signal.average = levels.average; - metrics->signal.maximum = levels.max; - metrics->signal.minimum = levels.min; - - err = ExportLevels(my_handle, &levels, 1); - if (err != AudioProcessing::kNoError) { - return err; - } - metrics->speech.instant = levels.instant; - metrics->speech.average = levels.average; - metrics->speech.maximum = levels.max; - metrics->speech.minimum = levels.min; - - err = ExportLevels(my_handle, &levels, 0); - if (err != AudioProcessing::kNoError) { - return err; - } - metrics->noise.instant = levels.instant; - metrics->noise.average = levels.average; - metrics->noise.maximum = levels.max; - metrics->noise.minimum = levels.min; - - return AudioProcessing::kNoError; -}*/ -} // namespace - -LevelEstimatorImpl::LevelEstimatorImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm) {} - -LevelEstimatorImpl::~LevelEstimatorImpl() {} - -int LevelEstimatorImpl::AnalyzeReverseStream(AudioBuffer* /*audio*/) { - return apm_->kUnsupportedComponentError; - /*if (!is_component_enabled()) { - return apm_->kNoError; - } - - return EstimateLevel(audio, static_cast(handle(1)));*/ -} - -int LevelEstimatorImpl::ProcessCaptureAudio(AudioBuffer* /*audio*/) { - return apm_->kUnsupportedComponentError; - /*if (!is_component_enabled()) { - return apm_->kNoError; - } - - return EstimateLevel(audio, static_cast(handle(0)));*/ -} - -int LevelEstimatorImpl::Enable(bool /*enable*/) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - return apm_->kUnsupportedComponentError; - //return EnableComponent(enable); -} - -bool LevelEstimatorImpl::is_enabled() const { - return is_component_enabled(); -} - -int LevelEstimatorImpl::GetMetrics(LevelEstimator::Metrics* /*metrics*/, - LevelEstimator::Metrics* /*reverse_metrics*/) { - return apm_->kUnsupportedComponentError; - /*if (!is_component_enabled()) { - return apm_->kNotEnabledError; - } - - int err = GetMetricsLocal(static_cast(handle(0)), metrics); - if (err != apm_->kNoError) { - return err; - } - - err = GetMetricsLocal(static_cast(handle(1)), reverse_metrics); - if (err != apm_->kNoError) { - return err; - } - - return apm_->kNoError;*/ -} - -int LevelEstimatorImpl::get_version(char* version, - int version_len_bytes) const { - // An empty string is used to indicate no version information. - memset(version, 0, version_len_bytes); - return apm_->kNoError; -} - -void* LevelEstimatorImpl::CreateHandle() const { - Handle* handle = NULL; - /*if (CreateLvlEst(&handle) != apm_->kNoError) { - handle = NULL; - } else { - assert(handle != NULL); - }*/ - - return handle; -} - -int LevelEstimatorImpl::DestroyHandle(void* /*handle*/) const { - return apm_->kUnsupportedComponentError; - //return FreeLvlEst(static_cast(handle)); -} - -int LevelEstimatorImpl::InitializeHandle(void* /*handle*/) const { - return apm_->kUnsupportedComponentError; - /*const double kIntervalSeconds = 1.5; - return InitLvlEst(static_cast(handle), - apm_->sample_rate_hz(), - kIntervalSeconds);*/ -} - -int LevelEstimatorImpl::ConfigureHandle(void* /*handle*/) const { - return apm_->kUnsupportedComponentError; - //return apm_->kNoError; -} - -int LevelEstimatorImpl::num_handles_required() const { - return apm_->kUnsupportedComponentError; - //return 2; -} - -int LevelEstimatorImpl::GetHandleError(void* handle) const { - // The component has no detailed errors. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/level_estimator_impl.h b/modules/audio_processing/main/source/level_estimator_impl.h deleted file mode 100644 index 1515722df..000000000 --- a/modules/audio_processing/main/source/level_estimator_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class LevelEstimatorImpl : public LevelEstimator, - public ProcessingComponent { - public: - explicit LevelEstimatorImpl(const AudioProcessingImpl* apm); - virtual ~LevelEstimatorImpl(); - - int AnalyzeReverseStream(AudioBuffer* audio); - int ProcessCaptureAudio(AudioBuffer* audio); - - // LevelEstimator implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // LevelEstimator implementation. - virtual int Enable(bool enable); - virtual int GetMetrics(Metrics* metrics, Metrics* reverse_metrics); - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_LEVEL_ESTIMATOR_IMPL_H_ diff --git a/modules/audio_processing/main/source/noise_suppression_impl.cc b/modules/audio_processing/main/source/noise_suppression_impl.cc deleted file mode 100644 index f899f350c..000000000 --- a/modules/audio_processing/main/source/noise_suppression_impl.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "noise_suppression_impl.h" - -#include - -#include "critical_section_wrapper.h" -#if defined(WEBRTC_NS_FLOAT) -#include "noise_suppression.h" -#elif defined(WEBRTC_NS_FIXED) -#include "noise_suppression_x.h" -#endif - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { - -#if defined(WEBRTC_NS_FLOAT) -typedef NsHandle Handle; -#elif defined(WEBRTC_NS_FIXED) -typedef NsxHandle Handle; -#endif - -namespace { -int MapSetting(NoiseSuppression::Level level) { - switch (level) { - case NoiseSuppression::kLow: - return 0; - case NoiseSuppression::kModerate: - return 1; - case NoiseSuppression::kHigh: - return 2; - case NoiseSuppression::kVeryHigh: - return 3; - default: - return -1; - } -} -} // namespace - -NoiseSuppressionImpl::NoiseSuppressionImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm), - level_(kModerate) {} - -NoiseSuppressionImpl::~NoiseSuppressionImpl() {} - -int NoiseSuppressionImpl::ProcessCaptureAudio(AudioBuffer* audio) { - int err = apm_->kNoError; - - if (!is_component_enabled()) { - return apm_->kNoError; - } - assert(audio->samples_per_split_channel() <= 160); - assert(audio->num_channels() == num_handles()); - - for (int i = 0; i < num_handles(); i++) { - Handle* my_handle = static_cast(handle(i)); -#if defined(WEBRTC_NS_FLOAT) - err = WebRtcNs_Process(static_cast(handle(i)), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i)); -#elif defined(WEBRTC_NS_FIXED) - err = WebRtcNsx_Process(static_cast(handle(i)), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i), - audio->low_pass_split_data(i), - audio->high_pass_split_data(i)); -#endif - - if (err != apm_->kNoError) { - return GetHandleError(my_handle); - } - } - - return apm_->kNoError; -} - -int NoiseSuppressionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - return EnableComponent(enable); -} - -bool NoiseSuppressionImpl::is_enabled() const { - return is_component_enabled(); -} - -int NoiseSuppressionImpl::set_level(Level level) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (MapSetting(level) == -1) { - return apm_->kBadParameterError; - } - - level_ = level; - return Configure(); -} - -NoiseSuppression::Level NoiseSuppressionImpl::level() const { - return level_; -} - -int NoiseSuppressionImpl::get_version(char* version, - int version_len_bytes) const { -#if defined(WEBRTC_NS_FLOAT) - if (WebRtcNs_get_version(version, version_len_bytes) != 0) -#elif defined(WEBRTC_NS_FIXED) - if (WebRtcNsx_get_version(version, version_len_bytes) != 0) -#endif - { - return apm_->kBadParameterError; - } - - return apm_->kNoError; -} - -void* NoiseSuppressionImpl::CreateHandle() const { - Handle* handle = NULL; -#if defined(WEBRTC_NS_FLOAT) - if (WebRtcNs_Create(&handle) != apm_->kNoError) -#elif defined(WEBRTC_NS_FIXED) - if (WebRtcNsx_Create(&handle) != apm_->kNoError) -#endif - { - handle = NULL; - } else { - assert(handle != NULL); - } - - return handle; -} - -int NoiseSuppressionImpl::DestroyHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Free(static_cast(handle)); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Free(static_cast(handle)); -#endif -} - -int NoiseSuppressionImpl::InitializeHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_Init(static_cast(handle), apm_->sample_rate_hz()); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_Init(static_cast(handle), apm_->sample_rate_hz()); -#endif -} - -int NoiseSuppressionImpl::ConfigureHandle(void* handle) const { -#if defined(WEBRTC_NS_FLOAT) - return WebRtcNs_set_policy(static_cast(handle), - MapSetting(level_)); -#elif defined(WEBRTC_NS_FIXED) - return WebRtcNsx_set_policy(static_cast(handle), - MapSetting(level_)); -#endif -} - -int NoiseSuppressionImpl::num_handles_required() const { - return apm_->num_output_channels(); -} - -int NoiseSuppressionImpl::GetHandleError(void* handle) const { - // The NS has no get_error() function. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc - diff --git a/modules/audio_processing/main/source/noise_suppression_impl.h b/modules/audio_processing/main/source/noise_suppression_impl.h deleted file mode 100644 index c9ff9b31a..000000000 --- a/modules/audio_processing/main/source/noise_suppression_impl.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class NoiseSuppressionImpl : public NoiseSuppression, - public ProcessingComponent { - public: - explicit NoiseSuppressionImpl(const AudioProcessingImpl* apm); - virtual ~NoiseSuppressionImpl(); - - int ProcessCaptureAudio(AudioBuffer* audio); - - // NoiseSuppression implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // NoiseSuppression implementation. - virtual int Enable(bool enable); - virtual int set_level(Level level); - virtual Level level() const; - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; - Level level_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_NOISE_SUPPRESSION_IMPL_H_ diff --git a/modules/audio_processing/main/source/processing_component.cc b/modules/audio_processing/main/source/processing_component.cc deleted file mode 100644 index 9ac125794..000000000 --- a/modules/audio_processing/main/source/processing_component.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "processing_component.h" - -#include - -#include "audio_processing_impl.h" - -namespace webrtc { - -ProcessingComponent::ProcessingComponent(const AudioProcessingImpl* apm) - : apm_(apm), - initialized_(false), - enabled_(false), - num_handles_(0) {} - -ProcessingComponent::~ProcessingComponent() { - assert(initialized_ == false); -} - -int ProcessingComponent::Destroy() { - while (!handles_.empty()) { - DestroyHandle(handles_.back()); - handles_.pop_back(); - } - initialized_ = false; - - return apm_->kNoError; -} - -int ProcessingComponent::EnableComponent(bool enable) { - if (enable && !enabled_) { - enabled_ = enable; // Must be set before Initialize() is called. - - int err = Initialize(); - if (err != apm_->kNoError) { - enabled_ = false; - return err; - } - } else { - enabled_ = enable; - } - - return apm_->kNoError; -} - -bool ProcessingComponent::is_component_enabled() const { - return enabled_; -} - -void* ProcessingComponent::handle(int index) const { - assert(index < num_handles_); - return handles_[index]; -} - -int ProcessingComponent::num_handles() const { - return num_handles_; -} - -int ProcessingComponent::Initialize() { - if (!enabled_) { - return apm_->kNoError; - } - - num_handles_ = num_handles_required(); - if (num_handles_ > static_cast(handles_.size())) { - handles_.resize(num_handles_, NULL); - } - - assert(static_cast(handles_.size()) >= num_handles_); - for (int i = 0; i < num_handles_; i++) { - if (handles_[i] == NULL) { - handles_[i] = CreateHandle(); - if (handles_[i] == NULL) { - return apm_->kCreationFailedError; - } - } - - int err = InitializeHandle(handles_[i]); - if (err != apm_->kNoError) { - return GetHandleError(handles_[i]); - } - } - - initialized_ = true; - return Configure(); -} - -int ProcessingComponent::Configure() { - if (!initialized_) { - return apm_->kNoError; - } - - assert(static_cast(handles_.size()) >= num_handles_); - for (int i = 0; i < num_handles_; i++) { - int err = ConfigureHandle(handles_[i]); - if (err != apm_->kNoError) { - return GetHandleError(handles_[i]); - } - } - - return apm_->kNoError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/processing_component.h b/modules/audio_processing/main/source/processing_component.h deleted file mode 100644 index 3d8a02bd3..000000000 --- a/modules/audio_processing/main/source/processing_component.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H_ - -#include - -#include "audio_processing.h" - -namespace webrtc { -class AudioProcessingImpl; - -/*template -class ComponentHandle { - public: - ComponentHandle(); - virtual ~ComponentHandle(); - - virtual int Create() = 0; - virtual T* ptr() const = 0; -};*/ - -class ProcessingComponent { - public: - explicit ProcessingComponent(const AudioProcessingImpl* apm); - virtual ~ProcessingComponent(); - - virtual int Initialize(); - virtual int Destroy(); - virtual int get_version(char* version, int version_len_bytes) const = 0; - - protected: - virtual int Configure(); - int EnableComponent(bool enable); - bool is_component_enabled() const; - void* handle(int index) const; - int num_handles() const; - - private: - virtual void* CreateHandle() const = 0; - virtual int InitializeHandle(void* handle) const = 0; - virtual int ConfigureHandle(void* handle) const = 0; - virtual int DestroyHandle(void* handle) const = 0; - virtual int num_handles_required() const = 0; - virtual int GetHandleError(void* handle) const = 0; - - const AudioProcessingImpl* apm_; - std::vector handles_; - bool initialized_; - bool enabled_; - int num_handles_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_PROCESSING_COMPONENT_H__ diff --git a/modules/audio_processing/main/source/splitting_filter.cc b/modules/audio_processing/main/source/splitting_filter.cc deleted file mode 100644 index 1526141cc..000000000 --- a/modules/audio_processing/main/source/splitting_filter.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "splitting_filter.h" -#include "signal_processing_library.h" - -namespace webrtc { - -void SplittingFilterAnalysis(const WebRtc_Word16* in_data, - WebRtc_Word16* low_band, - WebRtc_Word16* high_band, - WebRtc_Word32* filter_state1, - WebRtc_Word32* filter_state2) -{ - WebRtcSpl_AnalysisQMF(in_data, low_band, high_band, filter_state1, filter_state2); -} - -void SplittingFilterSynthesis(const WebRtc_Word16* low_band, - const WebRtc_Word16* high_band, - WebRtc_Word16* out_data, - WebRtc_Word32* filt_state1, - WebRtc_Word32* filt_state2) -{ - WebRtcSpl_SynthesisQMF(low_band, high_band, out_data, filt_state1, filt_state2); -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/splitting_filter.h b/modules/audio_processing/main/source/splitting_filter.h deleted file mode 100644 index 661bfb2f6..000000000 --- a/modules/audio_processing/main/source/splitting_filter.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_ - -#include "typedefs.h" -#include "signal_processing_library.h" - -namespace webrtc { -/* - * SplittingFilterbank_analysisQMF(...) - * - * Splits a super-wb signal into two subbands: 0-8 kHz and 8-16 kHz. - * - * Input: - * - in_data : super-wb audio signal - * - * Input & Output: - * - filt_state1: Filter state for first all-pass filter - * - filt_state2: Filter state for second all-pass filter - * - * Output: - * - low_band : The signal from the 0-4 kHz band - * - high_band : The signal from the 4-8 kHz band - */ -void SplittingFilterAnalysis(const WebRtc_Word16* in_data, - WebRtc_Word16* low_band, - WebRtc_Word16* high_band, - WebRtc_Word32* filt_state1, - WebRtc_Word32* filt_state2); - -/* - * SplittingFilterbank_synthesisQMF(...) - * - * Combines the two subbands (0-8 and 8-16 kHz) into a super-wb signal. - * - * Input: - * - low_band : The signal with the 0-8 kHz band - * - high_band : The signal with the 8-16 kHz band - * - * Input & Output: - * - filt_state1: Filter state for first all-pass filter - * - filt_state2: Filter state for second all-pass filter - * - * Output: - * - out_data : super-wb speech signal - */ -void SplittingFilterSynthesis(const WebRtc_Word16* low_band, - const WebRtc_Word16* high_band, - WebRtc_Word16* out_data, - WebRtc_Word32* filt_state1, - WebRtc_Word32* filt_state2); -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_SPLITTING_FILTER_H_ diff --git a/modules/audio_processing/main/source/voice_detection_impl.cc b/modules/audio_processing/main/source/voice_detection_impl.cc deleted file mode 100644 index 3eb446e91..000000000 --- a/modules/audio_processing/main/source/voice_detection_impl.cc +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "voice_detection_impl.h" - -#include - -#include "critical_section_wrapper.h" -#include "webrtc_vad.h" - -#include "audio_processing_impl.h" -#include "audio_buffer.h" - -namespace webrtc { - -typedef VadInst Handle; - -namespace { -WebRtc_Word16 MapSetting(VoiceDetection::Likelihood likelihood) { - switch (likelihood) { - case VoiceDetection::kVeryLowLikelihood: - return 3; - break; - case VoiceDetection::kLowLikelihood: - return 2; - break; - case VoiceDetection::kModerateLikelihood: - return 1; - break; - case VoiceDetection::kHighLikelihood: - return 0; - break; - default: - return -1; - } -} -} // namespace - - -VoiceDetectionImpl::VoiceDetectionImpl(const AudioProcessingImpl* apm) - : ProcessingComponent(apm), - apm_(apm), - stream_has_voice_(false), - using_external_vad_(false), - likelihood_(kLowLikelihood), - frame_size_ms_(10), - frame_size_samples_(0) {} - -VoiceDetectionImpl::~VoiceDetectionImpl() {} - -int VoiceDetectionImpl::ProcessCaptureAudio(AudioBuffer* audio) { - if (!is_component_enabled()) { - return apm_->kNoError; - } - - if (using_external_vad_) { - using_external_vad_ = false; - return apm_->kNoError; - } - assert(audio->samples_per_split_channel() <= 160); - - WebRtc_Word16* mixed_data = audio->low_pass_split_data(0); - if (audio->num_channels() > 1) { - audio->CopyAndMixLowPass(1); - mixed_data = audio->mixed_low_pass_data(0); - } - - // TODO(ajm): concatenate data in frame buffer here. - - int vad_ret_val; - vad_ret_val = WebRtcVad_Process(static_cast(handle(0)), - apm_->split_sample_rate_hz(), - mixed_data, - frame_size_samples_); - - if (vad_ret_val == 0) { - stream_has_voice_ = false; - } else if (vad_ret_val == 1) { - stream_has_voice_ = true; - } else { - return apm_->kUnspecifiedError; - } - - return apm_->kNoError; -} - -int VoiceDetectionImpl::Enable(bool enable) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - return EnableComponent(enable); -} - -bool VoiceDetectionImpl::is_enabled() const { - return is_component_enabled(); -} - -int VoiceDetectionImpl::set_stream_has_voice(bool has_voice) { - using_external_vad_ = true; - stream_has_voice_ = has_voice; - return apm_->kNoError; -} - -bool VoiceDetectionImpl::stream_has_voice() const { - // TODO(ajm): enable this assertion? - //assert(using_external_vad_ || is_component_enabled()); - return stream_has_voice_; -} - -int VoiceDetectionImpl::set_likelihood(VoiceDetection::Likelihood likelihood) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - if (MapSetting(likelihood) == -1) { - return apm_->kBadParameterError; - } - - likelihood_ = likelihood; - return Configure(); -} - -VoiceDetection::Likelihood VoiceDetectionImpl::likelihood() const { - return likelihood_; -} - -int VoiceDetectionImpl::set_frame_size_ms(int size) { - CriticalSectionScoped crit_scoped(*apm_->crit()); - assert(size == 10); // TODO(ajm): remove when supported. - if (size != 10 && - size != 20 && - size != 30) { - return apm_->kBadParameterError; - } - - frame_size_ms_ = size; - - return Initialize(); -} - -int VoiceDetectionImpl::frame_size_ms() const { - return frame_size_ms_; -} - -int VoiceDetectionImpl::Initialize() { - int err = ProcessingComponent::Initialize(); - if (err != apm_->kNoError || !is_component_enabled()) { - return err; - } - - using_external_vad_ = false; - frame_size_samples_ = frame_size_ms_ * (apm_->split_sample_rate_hz() / 1000); - // TODO(ajm): intialize frame buffer here. - - return apm_->kNoError; -} - -int VoiceDetectionImpl::get_version(char* version, - int version_len_bytes) const { - if (WebRtcVad_get_version(version, version_len_bytes) != 0) { - return apm_->kBadParameterError; - } - - return apm_->kNoError; -} - -void* VoiceDetectionImpl::CreateHandle() const { - Handle* handle = NULL; - if (WebRtcVad_Create(&handle) != apm_->kNoError) { - handle = NULL; - } else { - assert(handle != NULL); - } - - return handle; -} - -int VoiceDetectionImpl::DestroyHandle(void* handle) const { - return WebRtcVad_Free(static_cast(handle)); -} - -int VoiceDetectionImpl::InitializeHandle(void* handle) const { - return WebRtcVad_Init(static_cast(handle)); -} - -int VoiceDetectionImpl::ConfigureHandle(void* handle) const { - return WebRtcVad_set_mode(static_cast(handle), - MapSetting(likelihood_)); -} - -int VoiceDetectionImpl::num_handles_required() const { - return 1; -} - -int VoiceDetectionImpl::GetHandleError(void* handle) const { - // The VAD has no get_error() function. - assert(handle != NULL); - return apm_->kUnspecifiedError; -} -} // namespace webrtc diff --git a/modules/audio_processing/main/source/voice_detection_impl.h b/modules/audio_processing/main/source/voice_detection_impl.h deleted file mode 100644 index ef212d11b..000000000 --- a/modules/audio_processing/main/source/voice_detection_impl.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_ - -#include "audio_processing.h" -#include "processing_component.h" - -namespace webrtc { -class AudioProcessingImpl; -class AudioBuffer; - -class VoiceDetectionImpl : public VoiceDetection, - public ProcessingComponent { - public: - explicit VoiceDetectionImpl(const AudioProcessingImpl* apm); - virtual ~VoiceDetectionImpl(); - - int ProcessCaptureAudio(AudioBuffer* audio); - - // VoiceDetection implementation. - virtual bool is_enabled() const; - - // ProcessingComponent implementation. - virtual int Initialize(); - virtual int get_version(char* version, int version_len_bytes) const; - - private: - // VoiceDetection implementation. - virtual int Enable(bool enable); - virtual int set_stream_has_voice(bool has_voice); - virtual bool stream_has_voice() const; - virtual int set_likelihood(Likelihood likelihood); - virtual Likelihood likelihood() const; - virtual int set_frame_size_ms(int size); - virtual int frame_size_ms() const; - - // ProcessingComponent implementation. - virtual void* CreateHandle() const; - virtual int InitializeHandle(void* handle) const; - virtual int ConfigureHandle(void* handle) const; - virtual int DestroyHandle(void* handle) const; - virtual int num_handles_required() const; - virtual int GetHandleError(void* handle) const; - - const AudioProcessingImpl* apm_; - bool stream_has_voice_; - bool using_external_vad_; - Likelihood likelihood_; - int frame_size_ms_; - int frame_size_samples_; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_MAIN_SOURCE_VOICE_DETECTION_IMPL_H_ diff --git a/modules/audio_processing/main/test/android/apmtest/AndroidManifest.xml b/modules/audio_processing/main/test/android/apmtest/AndroidManifest.xml deleted file mode 100644 index c6063b3d7..000000000 --- a/modules/audio_processing/main/test/android/apmtest/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/modules/audio_processing/main/test/android/apmtest/default.properties b/modules/audio_processing/main/test/android/apmtest/default.properties deleted file mode 100644 index 9a2c9f6c8..000000000 --- a/modules/audio_processing/main/test/android/apmtest/default.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-9 diff --git a/modules/audio_processing/main/test/android/apmtest/jni/Android.mk b/modules/audio_processing/main/test/android/apmtest/jni/Android.mk deleted file mode 100644 index eaf3c9d86..000000000 --- a/modules/audio_processing/main/test/android/apmtest/jni/Android.mk +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := apmtest-activity -LOCAL_SRC_FILES := main.c -LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM -LOCAL_STATIC_LIBRARIES := android_native_app_glue - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,android/native_app_glue) diff --git a/modules/audio_processing/main/test/android/apmtest/jni/Application.mk b/modules/audio_processing/main/test/android/apmtest/jni/Application.mk deleted file mode 100644 index 22d188e59..000000000 --- a/modules/audio_processing/main/test/android/apmtest/jni/Application.mk +++ /dev/null @@ -1 +0,0 @@ -APP_PLATFORM := android-9 diff --git a/modules/audio_processing/main/test/android/apmtest/jni/main.c b/modules/audio_processing/main/test/android/apmtest/jni/main.c deleted file mode 100644 index 2e1963568..000000000 --- a/modules/audio_processing/main/test/android/apmtest/jni/main.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -//BEGIN_INCLUDE(all) -#include -#include - -#include -#include - -#include -#include -#include - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) - -/** - * Our saved state data. - */ -struct saved_state { - float angle; - int32_t x; - int32_t y; -}; - -/** - * Shared state for our app. - */ -struct engine { - struct android_app* app; - - ASensorManager* sensorManager; - const ASensor* accelerometerSensor; - ASensorEventQueue* sensorEventQueue; - - int animating; - EGLDisplay display; - EGLSurface surface; - EGLContext context; - int32_t width; - int32_t height; - struct saved_state state; -}; - -/** - * Initialize an EGL context for the current display. - */ -static int engine_init_display(struct engine* engine) { - // initialize OpenGL ES and EGL - - /* - * Here specify the attributes of the desired configuration. - * Below, we select an EGLConfig with at least 8 bits per color - * component compatible with on-screen windows - */ - const EGLint attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BLUE_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_RED_SIZE, 8, - EGL_NONE - }; - EGLint w, h, dummy, format; - EGLint numConfigs; - EGLConfig config; - EGLSurface surface; - EGLContext context; - - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - - eglInitialize(display, 0, 0); - - /* Here, the application chooses the configuration it desires. In this - * sample, we have a very simplified selection process, where we pick - * the first EGLConfig that matches our criteria */ - eglChooseConfig(display, attribs, &config, 1, &numConfigs); - - /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is - * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). - * As soon as we picked a EGLConfig, we can safely reconfigure the - * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ - eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); - - ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); - - surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); - context = eglCreateContext(display, config, NULL, NULL); - - if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { - LOGW("Unable to eglMakeCurrent"); - return -1; - } - - eglQuerySurface(display, surface, EGL_WIDTH, &w); - eglQuerySurface(display, surface, EGL_HEIGHT, &h); - - engine->display = display; - engine->context = context; - engine->surface = surface; - engine->width = w; - engine->height = h; - engine->state.angle = 0; - - // Initialize GL state. - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); - glEnable(GL_CULL_FACE); - glShadeModel(GL_SMOOTH); - glDisable(GL_DEPTH_TEST); - - return 0; -} - -/** - * Just the current frame in the display. - */ -static void engine_draw_frame(struct engine* engine) { - if (engine->display == NULL) { - // No display. - return; - } - - // Just fill the screen with a color. - glClearColor(((float)engine->state.x)/engine->width, engine->state.angle, - ((float)engine->state.y)/engine->height, 1); - glClear(GL_COLOR_BUFFER_BIT); - - eglSwapBuffers(engine->display, engine->surface); -} - -/** - * Tear down the EGL context currently associated with the display. - */ -static void engine_term_display(struct engine* engine) { - if (engine->display != EGL_NO_DISPLAY) { - eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (engine->context != EGL_NO_CONTEXT) { - eglDestroyContext(engine->display, engine->context); - } - if (engine->surface != EGL_NO_SURFACE) { - eglDestroySurface(engine->display, engine->surface); - } - eglTerminate(engine->display); - } - engine->animating = 0; - engine->display = EGL_NO_DISPLAY; - engine->context = EGL_NO_CONTEXT; - engine->surface = EGL_NO_SURFACE; -} - -/** - * Process the next input event. - */ -static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { - struct engine* engine = (struct engine*)app->userData; - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - engine->animating = 1; - engine->state.x = AMotionEvent_getX(event, 0); - engine->state.y = AMotionEvent_getY(event, 0); - return 1; - } - return 0; -} - -/** - * Process the next main command. - */ -static void engine_handle_cmd(struct android_app* app, int32_t cmd) { - struct engine* engine = (struct engine*)app->userData; - switch (cmd) { - case APP_CMD_SAVE_STATE: - // The system has asked us to save our current state. Do so. - engine->app->savedState = malloc(sizeof(struct saved_state)); - *((struct saved_state*)engine->app->savedState) = engine->state; - engine->app->savedStateSize = sizeof(struct saved_state); - break; - case APP_CMD_INIT_WINDOW: - // The window is being shown, get it ready. - if (engine->app->window != NULL) { - engine_init_display(engine); - engine_draw_frame(engine); - } - break; - case APP_CMD_TERM_WINDOW: - // The window is being hidden or closed, clean it up. - engine_term_display(engine); - break; - case APP_CMD_GAINED_FOCUS: - // When our app gains focus, we start monitoring the accelerometer. - if (engine->accelerometerSensor != NULL) { - ASensorEventQueue_enableSensor(engine->sensorEventQueue, - engine->accelerometerSensor); - // We'd like to get 60 events per second (in us). - ASensorEventQueue_setEventRate(engine->sensorEventQueue, - engine->accelerometerSensor, (1000L/60)*1000); - } - break; - case APP_CMD_LOST_FOCUS: - // When our app loses focus, we stop monitoring the accelerometer. - // This is to avoid consuming battery while not being used. - if (engine->accelerometerSensor != NULL) { - ASensorEventQueue_disableSensor(engine->sensorEventQueue, - engine->accelerometerSensor); - } - // Also stop animating. - engine->animating = 0; - engine_draw_frame(engine); - break; - } -} - -/** - * This is the main entry point of a native application that is using - * android_native_app_glue. It runs in its own thread, with its own - * event loop for receiving input events and doing other things. - */ -void android_main(struct android_app* state) { - struct engine engine; - - // Make sure glue isn't stripped. - app_dummy(); - - memset(&engine, 0, sizeof(engine)); - state->userData = &engine; - state->onAppCmd = engine_handle_cmd; - state->onInputEvent = engine_handle_input; - engine.app = state; - - // Prepare to monitor accelerometer - engine.sensorManager = ASensorManager_getInstance(); - engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_ACCELEROMETER); - engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, - state->looper, LOOPER_ID_USER, NULL, NULL); - - if (state->savedState != NULL) { - // We are starting with a previous saved state; restore from it. - engine.state = *(struct saved_state*)state->savedState; - } - - // loop waiting for stuff to do. - - while (1) { - // Read all pending events. - int ident; - int events; - struct android_poll_source* source; - - // If not animating, we will block forever waiting for events. - // If animating, we loop until all events are read, then continue - // to draw the next frame of animation. - while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, - (void**)&source)) >= 0) { - - // Process this event. - if (source != NULL) { - source->process(state, source); - } - - // If a sensor has data, process it now. - if (ident == LOOPER_ID_USER) { - if (engine.accelerometerSensor != NULL) { - ASensorEvent event; - while (ASensorEventQueue_getEvents(engine.sensorEventQueue, - &event, 1) > 0) { - LOGI("accelerometer: x=%f y=%f z=%f", - event.acceleration.x, event.acceleration.y, - event.acceleration.z); - } - } - } - - // Check if we are exiting. - if (state->destroyRequested != 0) { - engine_term_display(&engine); - return; - } - } - - if (engine.animating) { - // Done with events; draw next animation frame. - engine.state.angle += .01f; - if (engine.state.angle > 1) { - engine.state.angle = 0; - } - - // Drawing is throttled to the screen update rate, so there - // is no need to do timing here. - engine_draw_frame(&engine); - } - } -} -//END_INCLUDE(all) diff --git a/modules/audio_processing/main/test/android/apmtest/res/values/strings.xml b/modules/audio_processing/main/test/android/apmtest/res/values/strings.xml deleted file mode 100644 index d0bd0f305..000000000 --- a/modules/audio_processing/main/test/android/apmtest/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - apmtest - diff --git a/modules/audio_processing/main/test/process_test/Android.mk b/modules/audio_processing/main/test/process_test/Android.mk deleted file mode 100644 index 23080aab2..000000000 --- a/modules/audio_processing/main/test/process_test/Android.mk +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -# apm test app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - process_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CPPFLAGS := -LOCAL_LDFLAGS := -LOCAL_C_INCLUDES := \ - external/gtest/include \ - $(LOCAL_PATH)/../../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../../interface \ - $(LOCAL_PATH)/../../../../.. - -LOCAL_STATIC_LIBRARIES := \ - libgtest - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libwebrtc_audio_preprocessing - -LOCAL_MODULE:= webrtc_apm_process_test - -include external/stlport/libstlport.mk -include $(BUILD_EXECUTABLE) diff --git a/modules/audio_processing/main/test/process_test/apmtest.m b/modules/audio_processing/main/test/process_test/apmtest.m deleted file mode 100644 index 6152bb5a9..000000000 --- a/modules/audio_processing/main/test/process_test/apmtest.m +++ /dev/null @@ -1,360 +0,0 @@ -function apmtest(task, testname, casenumber, legacy) -%APMTEST is a tool to process APM file sets and easily display the output. -% APMTEST(TASK, TESTNAME, CASENUMBER) performs one of several TASKs: -% 'test' Processes the files to produce test output. -% 'list' Prints a list of cases in the test set, preceded by their -% CASENUMBERs. -% 'show' Uses spclab to show the test case specified by the -% CASENUMBER parameter. -% -% using a set of test files determined by TESTNAME: -% 'all' All tests. -% 'apm' The standard APM test set (default). -% 'apmm' The mobile APM test set. -% 'aec' The AEC test set. -% 'aecm' The AECM test set. -% 'agc' The AGC test set. -% 'ns' The NS test set. -% 'vad' The VAD test set. -% -% CASENUMBER can be used to select a single test case. Omit CASENUMBER, -% or set to zero, to use all test cases. -% - -if nargin < 4 - % Set to true to run old VQE recordings. - legacy = false; -end - -if nargin < 3 - casenumber = 0; -end - -if nargin < 2 - task = 'test'; -end - -if nargin < 1 - testname = 'all'; -end - -if ~strcmp(task, 'test') && ~strcmp(task, 'list') && ~strcmp(task, 'show') - error(['TASK ' task ' is not recognized']); -end - -if casenumber == 0 && strcmp(task, 'show') - error(['CASENUMBER must be specified for TASK ' task]); -end - -filepath = 'data/'; -inpath = [filepath 'input/']; -outpath = [filepath 'output/']; -refpath = [filepath 'reference/']; - -% Temporary -if legacy - refpath = [filepath 'output/']; - outpath = [filepath 'reference/']; -end - -if strcmp(testname, 'all') - tests = {'apm','apmm','aec','aecm','agc','ns','vad'}; -else - tests = {testname}; -end - -if legacy - progname = '/usr/local/google/p4/dev/depot/test'; -else - progname = './process_test'; -end - -global farFile; -global nearFile; -global eventFile; -global delayFile; -global driftFile; - -if legacy - farFile = 'vqeFar.pcm'; - nearFile = 'vqeNear.pcm'; - eventFile = 'vqeEvent.dat'; - delayFile = 'vqeBuf.dat'; - driftFile = 'vqeDrift.dat'; -else - farFile = 'apm_far.pcm'; - nearFile = 'apm_near.pcm'; - eventFile = 'apm_event.dat'; - delayFile = 'apm_delay.dat'; - driftFile = 'apm_drift.dat'; -end - -simulateMode = false; -nErr = 0; -nCases = 0; -for i=1:length(tests) - simulateMode = false; - - if strcmp(tests{i}, 'apm') - testdir = ['apm/']; - outfile = ['out']; - if legacy - opt = ['-ec 1 -agc 2 -nc 2 -vad 3']; - else - opt = ['--no_progress -hpf' ... - ' -aec --drift_compensation -agc --fixed_digital' ... - ' -ns --ns_moderate -vad']; - end - - elseif strcmp(tests{i}, 'apm-swb') - simulateMode = true; - testdir = ['apm-swb/']; - outfile = ['out']; - if legacy - opt = ['-fs 32000 -ec 1 -agc 2 -nc 2']; - else - opt = ['--no_progress -fs 32000 -hpf' ... - ' -aec --drift_compensation -agc --adaptive_digital' ... - ' -ns --ns_moderate -vad']; - end - elseif strcmp(tests{i}, 'apmm') - testdir = ['apmm/']; - outfile = ['out']; - opt = ['-aec --drift_compensation -agc --fixed_digital -hpf -ns ' ... - '--ns_moderate']; - - else - error(['TESTNAME ' tests{i} ' is not recognized']); - end - - inpath = [inpath testdir]; - outpath = [outpath testdir]; - refpath = [refpath testdir]; - - if ~exist(inpath,'dir') - error(['Input directory ' inpath ' does not exist']); - end - - if ~exist(refpath,'dir') - warning(['Reference directory ' refpath ' does not exist']); - end - - [status, errMsg] = mkdir(outpath); - if (status == 0) - error(errMsg); - end - - [nErr, nCases] = recurseDir(inpath, outpath, refpath, outfile, ... - progname, opt, simulateMode, nErr, nCases, task, casenumber, legacy); - - if strcmp(task, 'test') || strcmp(task, 'show') - system(['rm ' farFile]); - system(['rm ' nearFile]); - if simulateMode == false - system(['rm ' eventFile]); - system(['rm ' delayFile]); - system(['rm ' driftFile]); - end - end -end - -if ~strcmp(task, 'list') - if nErr == 0 - fprintf(1, '\nAll files are bit-exact to reference\n', nErr); - else - fprintf(1, '\n%d files are NOT bit-exact to reference\n', nErr); - end -end - - -function [nErrOut, nCases] = recurseDir(inpath, outpath, refpath, ... - outfile, progname, opt, simulateMode, nErr, nCases, task, casenumber, ... - legacy) - -global farFile; -global nearFile; -global eventFile; -global delayFile; -global driftFile; - -dirs = dir(inpath); -nDirs = 0; -nErrOut = nErr; -for i=3:length(dirs) % skip . and .. - nDirs = nDirs + dirs(i).isdir; -end - - -if nDirs == 0 - nCases = nCases + 1; - - if casenumber == nCases || casenumber == 0 - - if strcmp(task, 'list') - fprintf([num2str(nCases) '. ' outfile '\n']) - else - vadoutfile = ['vad_' outfile '.dat']; - outfile = [outfile '.pcm']; - - % Check for VAD test - vadTest = 0; - if ~isempty(findstr(opt, '-vad')) - vadTest = 1; - if legacy - opt = [opt ' ' outpath vadoutfile]; - else - opt = [opt ' --vad_out_file ' outpath vadoutfile]; - end - end - - if exist([inpath 'vqeFar.pcm']) - system(['ln -s -f ' inpath 'vqeFar.pcm ' farFile]); - elseif exist([inpath 'apm_far.pcm']) - system(['ln -s -f ' inpath 'apm_far.pcm ' farFile]); - end - - if exist([inpath 'vqeNear.pcm']) - system(['ln -s -f ' inpath 'vqeNear.pcm ' nearFile]); - elseif exist([inpath 'apm_near.pcm']) - system(['ln -s -f ' inpath 'apm_near.pcm ' nearFile]); - end - - if exist([inpath 'vqeEvent.dat']) - system(['ln -s -f ' inpath 'vqeEvent.dat ' eventFile]); - elseif exist([inpath 'apm_event.day']) - system(['ln -s -f ' inpath 'apm_event.dat ' eventFile]); - end - - if exist([inpath 'vqeBuf.dat']) - system(['ln -s -f ' inpath 'vqeBuf.dat ' delayFile]); - elseif exist([inpath 'apm_delay.day']) - system(['ln -s -f ' inpath 'apm_delay.dat ' delayFile]); - end - - if exist([inpath 'vqeSkew.dat']) - system(['ln -s -f ' inpath 'vqeSkew.dat ' driftFile]); - elseif exist([inpath 'vqeDrift.dat']) - system(['ln -s -f ' inpath 'vqeDrift.dat ' driftFile]); - elseif exist([inpath 'apm_drift.dat']) - system(['ln -s -f ' inpath 'apm_drift.dat ' driftFile]); - end - - if simulateMode == false - command = [progname ' -o ' outpath outfile ' ' opt]; - else - if legacy - inputCmd = [' -in ' nearFile]; - else - inputCmd = [' -i ' nearFile]; - end - - if exist([farFile]) - if legacy - inputCmd = [' -if ' farFile inputCmd]; - else - inputCmd = [' -ir ' farFile inputCmd]; - end - end - command = [progname inputCmd ' -o ' outpath outfile ' ' opt]; - end - % This prevents MATLAB from using its own C libraries. - shellcmd = ['bash -c "unset LD_LIBRARY_PATH;']; - fprintf([command '\n']); - [status, result] = system([shellcmd command '"']); - fprintf(result); - - fprintf(['Reference file: ' refpath outfile '\n']); - - if vadTest == 1 - equal_to_ref = are_files_equal([outpath vadoutfile], ... - [refpath vadoutfile], ... - 'int8'); - if ~equal_to_ref - nErr = nErr + 1; - end - end - - [equal_to_ref, diffvector] = are_files_equal([outpath outfile], ... - [refpath outfile], ... - 'int16'); - if ~equal_to_ref - nErr = nErr + 1; - end - - if strcmp(task, 'show') - % Assume the last init gives the sample rate of interest. - str_idx = strfind(result, 'Sample rate:'); - fs = str2num(result(str_idx(end) + 13:str_idx(end) + 17)); - fprintf('Using %d Hz\n', fs); - - if exist([farFile]) - spclab(fs, farFile, nearFile, [refpath outfile], ... - [outpath outfile], diffvector); - %spclab(fs, diffvector); - else - spclab(fs, nearFile, [refpath outfile], [outpath outfile], ... - diffvector); - %spclab(fs, diffvector); - end - - if vadTest == 1 - spclab([refpath vadoutfile], [outpath vadoutfile]); - end - end - end - end -else - - for i=3:length(dirs) - if dirs(i).isdir - [nErr, nCases] = recurseDir([inpath dirs(i).name '/'], outpath, ... - refpath,[outfile '_' dirs(i).name], progname, opt, ... - simulateMode, nErr, nCases, task, casenumber, legacy); - end - end -end -nErrOut = nErr; - -function [are_equal, diffvector] = ... - are_files_equal(newfile, reffile, precision, diffvector) - -are_equal = false; -diffvector = 0; -if ~exist(newfile,'file') - warning(['Output file ' newfile ' does not exist']); - return -end - -if ~exist(reffile,'file') - warning(['Reference file ' reffile ' does not exist']); - return -end - -fid = fopen(newfile,'rb'); -new = fread(fid,inf,precision); -fclose(fid); - -fid = fopen(reffile,'rb'); -ref = fread(fid,inf,precision); -fclose(fid); - -if length(new) ~= length(ref) - warning('Reference is not the same length as output'); - minlength = min(length(new), length(ref)); - new = new(1:minlength); - ref = ref(1:minlength); -end -diffvector = new - ref; - -if isequal(new, ref) - fprintf([newfile ' is bit-exact to reference\n']); - are_equal = true; -else - if isempty(new) - warning([newfile ' is empty']); - return - end - snr = snrseg(new,ref,80); - fprintf('\n'); - are_equal = false; -end diff --git a/modules/audio_processing/main/test/process_test/process_test.cc b/modules/audio_processing/main/test/process_test/process_test.cc deleted file mode 100644 index c62345fcf..000000000 --- a/modules/audio_processing/main/test/process_test/process_test.cc +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#ifdef WEBRTC_ANDROID -#include -#endif - -#include "tick_util.h" -#include "gtest/gtest.h" -#include "module_common_types.h" - -#include "audio_processing.h" - -#include "cpu_features_wrapper.h" - -using webrtc::AudioFrame; -using webrtc::TickInterval; -using webrtc::TickTime; - -using webrtc::AudioProcessing; -using webrtc::GainControl; -using webrtc::NoiseSuppression; - -void usage() { - printf( - "Usage: process_test [options] [-ir REVERSE_FILE] [-i PRIMARY_FILE]\n"); - printf( - " [-o OUT_FILE]\n"); - printf( - "process_test is a test application for AudioProcessing.\n\n" - "When -ir or -i is specified the files will be processed directly in a\n" - "simulation mode. Otherwise the full set of test files is expected to be\n" - "present in the working directory.\n"); - printf("\n"); - printf("Options\n"); - printf("General configuration:\n"); - printf(" -fs SAMPLE_RATE_HZ\n"); - printf(" -ch CHANNELS_IN CHANNELS_OUT\n"); - printf(" -rch REVERSE_CHANNELS\n"); - printf("\n"); - printf("Component configuration:\n"); - printf( - "All components are disabled by default. Each block below begins with a\n" - "flag to enable the component with default settings. The subsequent flags\n" - "in the block are used to provide configuration settings.\n"); - printf("\n -aec Echo cancellation\n"); - printf(" --drift_compensation\n"); - printf(" --no_drift_compensation\n"); - printf("\n -aecm Echo control mobile\n"); - printf("\n -agc Gain control\n"); - printf(" --analog\n"); - printf(" --adaptive_digital\n"); - printf(" --fixed_digital\n"); - printf(" --target_level LEVEL\n"); - printf(" --compression_gain GAIN\n"); - printf(" --limiter\n"); - printf(" --no_limiter\n"); - printf("\n -hpf High pass filter\n"); - printf("\n -ns Noise suppression\n"); - printf(" --ns_low\n"); - printf(" --ns_moderate\n"); - printf(" --ns_high\n"); - printf(" --ns_very_high\n"); - printf("\n -vad Voice activity detection\n"); - printf(" --vad_out_file FILE"); - printf("\n"); - printf("Modifiers:\n"); - printf(" --perf Measure performance.\n"); - printf(" --quiet Suppress text output.\n"); - printf(" --no_progress Suppress progress.\n"); - printf(" --version Print version information and exit.\n"); -} - -// void function for gtest. -void void_main(int argc, char* argv[]) { - if (argc > 1 && strcmp(argv[1], "--help") == 0) { - usage(); - return; - } - - if (argc < 2) { - printf("Did you mean to run without arguments?\n"); - printf("Try `process_test --help' for more information.\n\n"); - } - - AudioProcessing* apm = AudioProcessing::Create(0); - ASSERT_TRUE(apm != NULL); - - WebRtc_Word8 version[1024]; - WebRtc_UWord32 version_bytes_remaining = sizeof(version); - WebRtc_UWord32 version_position = 0; - - const char* far_filename = NULL; - const char* near_filename = NULL; - const char* out_filename = NULL; - const char* vad_out_filename = NULL; - - int32_t sample_rate_hz = 16000; - int32_t device_sample_rate_hz = 16000; - - int num_capture_input_channels = 1; - int num_capture_output_channels = 1; - int num_render_channels = 1; - - int samples_per_channel = sample_rate_hz / 100; - - bool simulating = false; - bool perf_testing = false; - bool verbose = true; - bool progress = true; - //bool interleaved = true; - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "-ir") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify filename after -ir"; - far_filename = argv[i]; - simulating = true; - - } else if (strcmp(argv[i], "-i") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify filename after -i"; - near_filename = argv[i]; - simulating = true; - - } else if (strcmp(argv[i], "-o") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify filename after -o"; - out_filename = argv[i]; - - } else if (strcmp(argv[i], "-fs") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify sample rate after -fs"; - ASSERT_EQ(1, sscanf(argv[i], "%d", &sample_rate_hz)); - samples_per_channel = sample_rate_hz / 100; - - ASSERT_EQ(apm->kNoError, - apm->set_sample_rate_hz(sample_rate_hz)); - - } else if (strcmp(argv[i], "-ch") == 0) { - i++; - ASSERT_LT(i + 1, argc) << "Specify number of channels after -ch"; - ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_input_channels)); - i++; - ASSERT_EQ(1, sscanf(argv[i], "%d", &num_capture_output_channels)); - - ASSERT_EQ(apm->kNoError, - apm->set_num_channels(num_capture_input_channels, - num_capture_output_channels)); - - } else if (strcmp(argv[i], "-rch") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify number of channels after -rch"; - ASSERT_EQ(1, sscanf(argv[i], "%d", &num_render_channels)); - - ASSERT_EQ(apm->kNoError, - apm->set_num_reverse_channels(num_render_channels)); - - } else if (strcmp(argv[i], "-aec") == 0) { - ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true)); - - } else if (strcmp(argv[i], "-noasm") == 0) { - WebRtc_GetCPUInfo = WebRtc_GetCPUInfoNoASM; - - } else if (strcmp(argv[i], "--drift_compensation") == 0) { - ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true)); - // TODO(ajm): this is enabled in the VQE test app by default. Investigate - // why it can give better performance despite passing zeros. - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->enable_drift_compensation(true)); - } else if (strcmp(argv[i], "--no_drift_compensation") == 0) { - ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->enable_drift_compensation(false)); - - } else if (strcmp(argv[i], "-aecm") == 0) { - ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true)); - - } else if (strcmp(argv[i], "-agc") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - - } else if (strcmp(argv[i], "--analog") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); - - } else if (strcmp(argv[i], "--adaptive_digital") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_mode(GainControl::kAdaptiveDigital)); - - } else if (strcmp(argv[i], "--fixed_digital") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_mode(GainControl::kFixedDigital)); - - } else if (strcmp(argv[i], "--target_level") == 0) { - i++; - int level; - ASSERT_EQ(1, sscanf(argv[i], "%d", &level)); - - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_target_level_dbfs(level)); - - } else if (strcmp(argv[i], "--compression_gain") == 0) { - i++; - int gain; - ASSERT_EQ(1, sscanf(argv[i], "%d", &gain)); - - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_compression_gain_db(gain)); - - } else if (strcmp(argv[i], "--limiter") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->enable_limiter(true)); - - } else if (strcmp(argv[i], "--no_limiter") == 0) { - ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->gain_control()->enable_limiter(false)); - - } else if (strcmp(argv[i], "-hpf") == 0) { - ASSERT_EQ(apm->kNoError, apm->high_pass_filter()->Enable(true)); - - } else if (strcmp(argv[i], "-ns") == 0) { - ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true)); - - } else if (strcmp(argv[i], "--ns_low") == 0) { - ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->noise_suppression()->set_level(NoiseSuppression::kLow)); - - } else if (strcmp(argv[i], "--ns_moderate") == 0) { - ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->noise_suppression()->set_level(NoiseSuppression::kModerate)); - - } else if (strcmp(argv[i], "--ns_high") == 0) { - ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->noise_suppression()->set_level(NoiseSuppression::kHigh)); - - } else if (strcmp(argv[i], "--ns_very_high") == 0) { - ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true)); - ASSERT_EQ(apm->kNoError, - apm->noise_suppression()->set_level(NoiseSuppression::kVeryHigh)); - - } else if (strcmp(argv[i], "-vad") == 0) { - ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true)); - - } else if (strcmp(argv[i], "--vad_out_file") == 0) { - i++; - ASSERT_LT(i, argc) << "Specify filename after --vad_out_file"; - vad_out_filename = argv[i]; - - } else if (strcmp(argv[i], "--perf") == 0) { - perf_testing = true; - - } else if (strcmp(argv[i], "--quiet") == 0) { - verbose = false; - progress = false; - - } else if (strcmp(argv[i], "--no_progress") == 0) { - progress = false; - - } else if (strcmp(argv[i], "--version") == 0) { - ASSERT_EQ(apm->kNoError, apm->Version(version, - version_bytes_remaining, - version_position)); - printf("%s\n", version); - return; - - } else { - FAIL() << "Unrecognized argument " << argv[i]; - } - } - - if (verbose) { - printf("Sample rate: %d Hz\n", sample_rate_hz); - printf("Primary channels: %d (in), %d (out)\n", - num_capture_input_channels, - num_capture_output_channels); - printf("Reverse channels: %d \n", num_render_channels); - } - - const char far_file_default[] = "apm_far.pcm"; - const char near_file_default[] = "apm_near.pcm"; - const char out_file_default[] = "out.pcm"; - const char event_filename[] = "apm_event.dat"; - const char delay_filename[] = "apm_delay.dat"; - const char drift_filename[] = "apm_drift.dat"; - const char vad_file_default[] = "vad_out.dat"; - - if (!simulating) { - far_filename = far_file_default; - near_filename = near_file_default; - } - - if (out_filename == NULL) { - out_filename = out_file_default; - } - - if (vad_out_filename == NULL) { - vad_out_filename = vad_file_default; - } - - FILE* far_file = NULL; - FILE* near_file = NULL; - FILE* out_file = NULL; - FILE* event_file = NULL; - FILE* delay_file = NULL; - FILE* drift_file = NULL; - FILE* vad_out_file = NULL; - - if (far_filename != NULL) { - far_file = fopen(far_filename, "rb"); - ASSERT_TRUE(NULL != far_file) << "Unable to open far-end audio file " - << far_filename; - } - - near_file = fopen(near_filename, "rb"); - ASSERT_TRUE(NULL != near_file) << "Unable to open near-end audio file " - << near_filename; - struct stat st; - stat(near_filename, &st); - int near_size_samples = st.st_size / sizeof(int16_t); - - out_file = fopen(out_filename, "wb"); - ASSERT_TRUE(NULL != out_file) << "Unable to open output audio file " - << out_filename; - - if (!simulating) { - event_file = fopen(event_filename, "rb"); - ASSERT_TRUE(NULL != event_file) << "Unable to open event file " - << event_filename; - - delay_file = fopen(delay_filename, "rb"); - ASSERT_TRUE(NULL != delay_file) << "Unable to open buffer file " - << delay_filename; - - drift_file = fopen(drift_filename, "rb"); - ASSERT_TRUE(NULL != drift_file) << "Unable to open drift file " - << drift_filename; - } - - if (apm->voice_detection()->is_enabled()) { - vad_out_file = fopen(vad_out_filename, "wb"); - ASSERT_TRUE(NULL != vad_out_file) << "Unable to open VAD output file " - << vad_out_file; - } - - enum Events { - kInitializeEvent, - kRenderEvent, - kCaptureEvent, - kResetEventDeprecated - }; - int16_t event = 0; - size_t read_count = 0; - int reverse_count = 0; - int primary_count = 0; - int near_read_samples = 0; - TickInterval acc_ticks; - - AudioFrame far_frame; - far_frame._frequencyInHz = sample_rate_hz; - - AudioFrame near_frame; - near_frame._frequencyInHz = sample_rate_hz; - - int delay_ms = 0; - int drift_samples = 0; - int capture_level = 127; - int8_t stream_has_voice = 0; - - TickTime t0 = TickTime::Now(); - TickTime t1 = t0; - WebRtc_Word64 max_time_us = 0; - WebRtc_Word64 max_time_reverse_us = 0; - WebRtc_Word64 min_time_us = 1e6; - WebRtc_Word64 min_time_reverse_us = 1e6; - - while (simulating || feof(event_file) == 0) { - std::ostringstream trace_stream; - trace_stream << "Processed frames: " << reverse_count << " (reverse), " - << primary_count << " (primary)"; - SCOPED_TRACE(trace_stream.str()); - - - if (simulating) { - if (far_file == NULL) { - event = kCaptureEvent; - } else { - if (event == kRenderEvent) { - event = kCaptureEvent; - } else { - event = kRenderEvent; - } - } - } else { - read_count = fread(&event, sizeof(event), 1, event_file); - if (read_count != 1) { - break; - } - //if (fread(&event, sizeof(event), 1, event_file) != 1) { - // break; // This is expected. - //} - } - - if (event == kInitializeEvent || event == kResetEventDeprecated) { - ASSERT_EQ(1u, - fread(&sample_rate_hz, sizeof(sample_rate_hz), 1, event_file)); - samples_per_channel = sample_rate_hz / 100; - - ASSERT_EQ(1u, - fread(&device_sample_rate_hz, - sizeof(device_sample_rate_hz), - 1, - event_file)); - - ASSERT_EQ(apm->kNoError, - apm->set_sample_rate_hz(sample_rate_hz)); - - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->set_device_sample_rate_hz( - device_sample_rate_hz)); - - far_frame._frequencyInHz = sample_rate_hz; - near_frame._frequencyInHz = sample_rate_hz; - - if (verbose) { - printf("Init at frame: %d (primary), %d (reverse)\n", - primary_count, reverse_count); - printf(" Sample rate: %d Hz\n", sample_rate_hz); - } - - } else if (event == kRenderEvent) { - reverse_count++; - far_frame._audioChannel = num_render_channels; - far_frame._payloadDataLengthInSamples = - num_render_channels * samples_per_channel; - - read_count = fread(far_frame._payloadData, - sizeof(WebRtc_Word16), - far_frame._payloadDataLengthInSamples, - far_file); - - if (simulating) { - if (read_count != far_frame._payloadDataLengthInSamples) { - break; // This is expected. - } - } else { - ASSERT_EQ(read_count, - far_frame._payloadDataLengthInSamples); - } - - if (perf_testing) { - t0 = TickTime::Now(); - } - - ASSERT_EQ(apm->kNoError, - apm->AnalyzeReverseStream(&far_frame)); - - if (perf_testing) { - t1 = TickTime::Now(); - TickInterval tick_diff = t1 - t0; - acc_ticks += tick_diff; - if (tick_diff.Microseconds() > max_time_reverse_us) { - max_time_reverse_us = tick_diff.Microseconds(); - } - if (tick_diff.Microseconds() < min_time_reverse_us) { - min_time_reverse_us = tick_diff.Microseconds(); - } - } - - } else if (event == kCaptureEvent) { - primary_count++; - near_frame._audioChannel = num_capture_input_channels; - near_frame._payloadDataLengthInSamples = - num_capture_input_channels * samples_per_channel; - - read_count = fread(near_frame._payloadData, - sizeof(WebRtc_Word16), - near_frame._payloadDataLengthInSamples, - near_file); - - near_read_samples += read_count; - if (progress && primary_count % 100 == 0) { - printf("%.0f%% complete\r", - (near_read_samples * 100.0) / near_size_samples); - fflush(stdout); - } - if (simulating) { - if (read_count != near_frame._payloadDataLengthInSamples) { - break; // This is expected. - } - - delay_ms = 0; - drift_samples = 0; - } else { - ASSERT_EQ(read_count, - near_frame._payloadDataLengthInSamples); - - // TODO(ajm): sizeof(delay_ms) for current files? - ASSERT_EQ(1u, - fread(&delay_ms, 2, 1, delay_file)); - ASSERT_EQ(1u, - fread(&drift_samples, sizeof(drift_samples), 1, drift_file)); - } - - if (perf_testing) { - t0 = TickTime::Now(); - } - - // TODO(ajm): fake an analog gain while simulating. - - int capture_level_in = capture_level; - ASSERT_EQ(apm->kNoError, - apm->gain_control()->set_stream_analog_level(capture_level)); - ASSERT_EQ(apm->kNoError, - apm->set_stream_delay_ms(delay_ms)); - ASSERT_EQ(apm->kNoError, - apm->echo_cancellation()->set_stream_drift_samples(drift_samples)); - - int err = apm->ProcessStream(&near_frame); - if (err == apm->kBadStreamParameterWarning) { - printf("Bad parameter warning. %s\n", trace_stream.str().c_str()); - } - ASSERT_TRUE(err == apm->kNoError || - err == apm->kBadStreamParameterWarning); - - capture_level = apm->gain_control()->stream_analog_level(); - - stream_has_voice = - static_cast(apm->voice_detection()->stream_has_voice()); - if (vad_out_file != NULL) { - ASSERT_EQ(1u, fwrite(&stream_has_voice, - sizeof(stream_has_voice), - 1, - vad_out_file)); - } - - if (apm->gain_control()->mode() != GainControl::kAdaptiveAnalog) { - ASSERT_EQ(capture_level_in, capture_level); - } - - if (perf_testing) { - t1 = TickTime::Now(); - TickInterval tick_diff = t1 - t0; - acc_ticks += tick_diff; - if (tick_diff.Microseconds() > max_time_us) { - max_time_us = tick_diff.Microseconds(); - } - if (tick_diff.Microseconds() < min_time_us) { - min_time_us = tick_diff.Microseconds(); - } - } - - ASSERT_EQ(near_frame._payloadDataLengthInSamples, - fwrite(near_frame._payloadData, - sizeof(WebRtc_Word16), - near_frame._payloadDataLengthInSamples, - out_file)); - } - else { - FAIL() << "Event " << event << " is unrecognized"; - } - } - - if (verbose) { - printf("\nProcessed frames: %d (primary), %d (reverse)\n", - primary_count, reverse_count); - } - - int8_t temp_int8; - if (far_file != NULL) { - read_count = fread(&temp_int8, sizeof(temp_int8), 1, far_file); - EXPECT_NE(0, feof(far_file)) << "Far-end file not fully processed"; - } - read_count = fread(&temp_int8, sizeof(temp_int8), 1, near_file); - EXPECT_NE(0, feof(near_file)) << "Near-end file not fully processed"; - - if (!simulating) { - read_count = fread(&temp_int8, sizeof(temp_int8), 1, event_file); - EXPECT_NE(0, feof(event_file)) << "Event file not fully processed"; - read_count = fread(&temp_int8, sizeof(temp_int8), 1, delay_file); - EXPECT_NE(0, feof(delay_file)) << "Delay file not fully processed"; - read_count = fread(&temp_int8, sizeof(temp_int8), 1, drift_file); - EXPECT_NE(0, feof(drift_file)) << "Drift file not fully processed"; - } - - if (perf_testing) { - if (primary_count > 0) { - WebRtc_Word64 exec_time = acc_ticks.Milliseconds(); - printf("\nTotal time: %.3f s, file time: %.2f s\n", - exec_time * 0.001, primary_count * 0.01); - printf("Time per frame: %.3f ms (average), %.3f ms (max)," - " %.3f ms (min)\n", - (exec_time * 1.0) / primary_count, - (max_time_us + max_time_reverse_us) / 1000.0, - (min_time_us + min_time_reverse_us) / 1000.0); - } else { - printf("Warning: no capture frames\n"); - } - } - - AudioProcessing::Destroy(apm); - apm = NULL; -} - -int main(int argc, char* argv[]) -{ - void_main(argc, argv); - - return 0; -} diff --git a/modules/audio_processing/main/test/unit_test/Android.mk b/modules/audio_processing/main/test/unit_test/Android.mk deleted file mode 100644 index b2029cfb4..000000000 --- a/modules/audio_processing/main/test/unit_test/Android.mk +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH:= $(call my-dir) - -# apm test app - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES:= \ - unit_test.cc - -# Flags passed to both C and C++ files. -LOCAL_CFLAGS := \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CPPFLAGS := -LOCAL_LDFLAGS := -LOCAL_C_INCLUDES := \ - external/gtest/include \ - $(LOCAL_PATH)/../../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../../interface \ - $(LOCAL_PATH)/../../../../.. - -LOCAL_STATIC_LIBRARIES := \ - libgtest - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - libstlport \ - libwebrtc_audio_preprocessing - -LOCAL_MODULE:= webrtc_apm_unit_test - -include external/stlport/libstlport.mk -include $(BUILD_EXECUTABLE) diff --git a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.cc b/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.cc deleted file mode 100644 index c82ffdb43..000000000 --- a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.cc +++ /dev/null @@ -1,1111 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! - -#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION -#include "audio_processing_unittest.pb.h" - -#include - -#include -#include -#include -// @@protoc_insertion_point(includes) - -namespace audio_processing_unittest { - -void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto() { - delete Test::default_instance_; - delete Test_Statistic::default_instance_; - delete Test_EchoMetrics::default_instance_; - delete OutputData::default_instance_; -} - -void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto() { - static bool already_here = false; - if (already_here) return; - already_here = true; - GOOGLE_PROTOBUF_VERIFY_VERSION; - - Test::default_instance_ = new Test(); - Test_Statistic::default_instance_ = new Test_Statistic(); - Test_EchoMetrics::default_instance_ = new Test_EchoMetrics(); - OutputData::default_instance_ = new OutputData(); - Test::default_instance_->InitAsDefaultInstance(); - Test_Statistic::default_instance_->InitAsDefaultInstance(); - Test_EchoMetrics::default_instance_->InitAsDefaultInstance(); - OutputData::default_instance_->InitAsDefaultInstance(); - ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto); -} - -// Force AddDescriptors() to be called at static initialization time. -struct StaticDescriptorInitializer_audio_5fprocessing_5funittest_2eproto { - StaticDescriptorInitializer_audio_5fprocessing_5funittest_2eproto() { - protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); - } -} static_descriptor_initializer_audio_5fprocessing_5funittest_2eproto_; - - -// =================================================================== - -#ifndef _MSC_VER -const int Test_Statistic::kInstantFieldNumber; -const int Test_Statistic::kAverageFieldNumber; -const int Test_Statistic::kMaximumFieldNumber; -const int Test_Statistic::kMinimumFieldNumber; -#endif // !_MSC_VER - -Test_Statistic::Test_Statistic() - : ::google::protobuf::MessageLite() { - SharedCtor(); -} - -void Test_Statistic::InitAsDefaultInstance() { -} - -Test_Statistic::Test_Statistic(const Test_Statistic& from) - : ::google::protobuf::MessageLite() { - SharedCtor(); - MergeFrom(from); -} - -void Test_Statistic::SharedCtor() { - _cached_size_ = 0; - instant_ = 0; - average_ = 0; - maximum_ = 0; - minimum_ = 0; - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -Test_Statistic::~Test_Statistic() { - SharedDtor(); -} - -void Test_Statistic::SharedDtor() { - if (this != default_instance_) { - } -} - -void Test_Statistic::SetCachedSize(int size) const { - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); -} -const Test_Statistic& Test_Statistic::default_instance() { - if (default_instance_ == NULL) protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); return *default_instance_; -} - -Test_Statistic* Test_Statistic::default_instance_ = NULL; - -Test_Statistic* Test_Statistic::New() const { - return new Test_Statistic; -} - -void Test_Statistic::Clear() { - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - instant_ = 0; - average_ = 0; - maximum_ = 0; - minimum_ = 0; - } - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -bool Test_Statistic::MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false - ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { - switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // optional int32 instant = 1; - case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &instant_))); - set_has_instant(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(16)) goto parse_average; - break; - } - - // optional int32 average = 2; - case 2: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_average: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &average_))); - set_has_average(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(24)) goto parse_maximum; - break; - } - - // optional int32 maximum = 3; - case 3: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_maximum: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &maximum_))); - set_has_maximum(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(32)) goto parse_minimum; - break; - } - - // optional int32 minimum = 4; - case 4: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_minimum: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &minimum_))); - set_has_minimum(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectAtEnd()) return true; - break; - } - - default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; - } - DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag, NULL)); - break; - } - } - } - return true; -#undef DO_ -} - -void Test_Statistic::SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const { - // optional int32 instant = 1; - if (has_instant()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->instant(), output); - } - - // optional int32 average = 2; - if (has_average()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->average(), output); - } - - // optional int32 maximum = 3; - if (has_maximum()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->maximum(), output); - } - - // optional int32 minimum = 4; - if (has_minimum()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->minimum(), output); - } - -} - -int Test_Statistic::ByteSize() const { - int total_size = 0; - - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - // optional int32 instant = 1; - if (has_instant()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->instant()); - } - - // optional int32 average = 2; - if (has_average()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->average()); - } - - // optional int32 maximum = 3; - if (has_maximum()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->maximum()); - } - - // optional int32 minimum = 4; - if (has_minimum()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->minimum()); - } - - } - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = total_size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); - return total_size; -} - -void Test_Statistic::CheckTypeAndMergeFrom( - const ::google::protobuf::MessageLite& from) { - MergeFrom(*::google::protobuf::down_cast(&from)); -} - -void Test_Statistic::MergeFrom(const Test_Statistic& from) { - GOOGLE_CHECK_NE(&from, this); - if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from.has_instant()) { - set_instant(from.instant()); - } - if (from.has_average()) { - set_average(from.average()); - } - if (from.has_maximum()) { - set_maximum(from.maximum()); - } - if (from.has_minimum()) { - set_minimum(from.minimum()); - } - } -} - -void Test_Statistic::CopyFrom(const Test_Statistic& from) { - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Test_Statistic::IsInitialized() const { - - return true; -} - -void Test_Statistic::Swap(Test_Statistic* other) { - if (other != this) { - std::swap(instant_, other->instant_); - std::swap(average_, other->average_); - std::swap(maximum_, other->maximum_); - std::swap(minimum_, other->minimum_); - std::swap(_has_bits_[0], other->_has_bits_[0]); - std::swap(_cached_size_, other->_cached_size_); - } -} - -::std::string Test_Statistic::GetTypeName() const { - return "audio_processing_unittest.Test.Statistic"; -} - - -// ------------------------------------------------------------------- - -#ifndef _MSC_VER -const int Test_EchoMetrics::kResidualEchoReturnLossFieldNumber; -const int Test_EchoMetrics::kEchoReturnLossFieldNumber; -const int Test_EchoMetrics::kEchoReturnLossEnhancementFieldNumber; -const int Test_EchoMetrics::kANlpFieldNumber; -#endif // !_MSC_VER - -Test_EchoMetrics::Test_EchoMetrics() - : ::google::protobuf::MessageLite() { - SharedCtor(); -} - -void Test_EchoMetrics::InitAsDefaultInstance() { - residualechoreturnloss_ = const_cast< ::audio_processing_unittest::Test_Statistic*>(&::audio_processing_unittest::Test_Statistic::default_instance()); - echoreturnloss_ = const_cast< ::audio_processing_unittest::Test_Statistic*>(&::audio_processing_unittest::Test_Statistic::default_instance()); - echoreturnlossenhancement_ = const_cast< ::audio_processing_unittest::Test_Statistic*>(&::audio_processing_unittest::Test_Statistic::default_instance()); - anlp_ = const_cast< ::audio_processing_unittest::Test_Statistic*>(&::audio_processing_unittest::Test_Statistic::default_instance()); -} - -Test_EchoMetrics::Test_EchoMetrics(const Test_EchoMetrics& from) - : ::google::protobuf::MessageLite() { - SharedCtor(); - MergeFrom(from); -} - -void Test_EchoMetrics::SharedCtor() { - _cached_size_ = 0; - residualechoreturnloss_ = NULL; - echoreturnloss_ = NULL; - echoreturnlossenhancement_ = NULL; - anlp_ = NULL; - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -Test_EchoMetrics::~Test_EchoMetrics() { - SharedDtor(); -} - -void Test_EchoMetrics::SharedDtor() { - if (this != default_instance_) { - delete residualechoreturnloss_; - delete echoreturnloss_; - delete echoreturnlossenhancement_; - delete anlp_; - } -} - -void Test_EchoMetrics::SetCachedSize(int size) const { - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); -} -const Test_EchoMetrics& Test_EchoMetrics::default_instance() { - if (default_instance_ == NULL) protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); return *default_instance_; -} - -Test_EchoMetrics* Test_EchoMetrics::default_instance_ = NULL; - -Test_EchoMetrics* Test_EchoMetrics::New() const { - return new Test_EchoMetrics; -} - -void Test_EchoMetrics::Clear() { - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (has_residualechoreturnloss()) { - if (residualechoreturnloss_ != NULL) residualechoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear(); - } - if (has_echoreturnloss()) { - if (echoreturnloss_ != NULL) echoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear(); - } - if (has_echoreturnlossenhancement()) { - if (echoreturnlossenhancement_ != NULL) echoreturnlossenhancement_->::audio_processing_unittest::Test_Statistic::Clear(); - } - if (has_anlp()) { - if (anlp_ != NULL) anlp_->::audio_processing_unittest::Test_Statistic::Clear(); - } - } - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -bool Test_EchoMetrics::MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false - ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { - switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1; - case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, mutable_residualechoreturnloss())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(18)) goto parse_echoReturnLoss; - break; - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2; - case 2: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - parse_echoReturnLoss: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, mutable_echoreturnloss())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(26)) goto parse_echoReturnLossEnhancement; - break; - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3; - case 3: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - parse_echoReturnLossEnhancement: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, mutable_echoreturnlossenhancement())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(34)) goto parse_aNlp; - break; - } - - // optional .audio_processing_unittest.Test.Statistic aNlp = 4; - case 4: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - parse_aNlp: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, mutable_anlp())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectAtEnd()) return true; - break; - } - - default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; - } - DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag, NULL)); - break; - } - } - } - return true; -#undef DO_ -} - -void Test_EchoMetrics::SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const { - // optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1; - if (has_residualechoreturnloss()) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 1, this->residualechoreturnloss(), output); - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2; - if (has_echoreturnloss()) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 2, this->echoreturnloss(), output); - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3; - if (has_echoreturnlossenhancement()) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 3, this->echoreturnlossenhancement(), output); - } - - // optional .audio_processing_unittest.Test.Statistic aNlp = 4; - if (has_anlp()) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 4, this->anlp(), output); - } - -} - -int Test_EchoMetrics::ByteSize() const { - int total_size = 0; - - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - // optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1; - if (has_residualechoreturnloss()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->residualechoreturnloss()); - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2; - if (has_echoreturnloss()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->echoreturnloss()); - } - - // optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3; - if (has_echoreturnlossenhancement()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->echoreturnlossenhancement()); - } - - // optional .audio_processing_unittest.Test.Statistic aNlp = 4; - if (has_anlp()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->anlp()); - } - - } - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = total_size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); - return total_size; -} - -void Test_EchoMetrics::CheckTypeAndMergeFrom( - const ::google::protobuf::MessageLite& from) { - MergeFrom(*::google::protobuf::down_cast(&from)); -} - -void Test_EchoMetrics::MergeFrom(const Test_EchoMetrics& from) { - GOOGLE_CHECK_NE(&from, this); - if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from.has_residualechoreturnloss()) { - mutable_residualechoreturnloss()->::audio_processing_unittest::Test_Statistic::MergeFrom(from.residualechoreturnloss()); - } - if (from.has_echoreturnloss()) { - mutable_echoreturnloss()->::audio_processing_unittest::Test_Statistic::MergeFrom(from.echoreturnloss()); - } - if (from.has_echoreturnlossenhancement()) { - mutable_echoreturnlossenhancement()->::audio_processing_unittest::Test_Statistic::MergeFrom(from.echoreturnlossenhancement()); - } - if (from.has_anlp()) { - mutable_anlp()->::audio_processing_unittest::Test_Statistic::MergeFrom(from.anlp()); - } - } -} - -void Test_EchoMetrics::CopyFrom(const Test_EchoMetrics& from) { - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Test_EchoMetrics::IsInitialized() const { - - return true; -} - -void Test_EchoMetrics::Swap(Test_EchoMetrics* other) { - if (other != this) { - std::swap(residualechoreturnloss_, other->residualechoreturnloss_); - std::swap(echoreturnloss_, other->echoreturnloss_); - std::swap(echoreturnlossenhancement_, other->echoreturnlossenhancement_); - std::swap(anlp_, other->anlp_); - std::swap(_has_bits_[0], other->_has_bits_[0]); - std::swap(_cached_size_, other->_cached_size_); - } -} - -::std::string Test_EchoMetrics::GetTypeName() const { - return "audio_processing_unittest.Test.EchoMetrics"; -} - - -// ------------------------------------------------------------------- - -#ifndef _MSC_VER -const int Test::kNumReverseChannelsFieldNumber; -const int Test::kNumChannelsFieldNumber; -const int Test::kSampleRateFieldNumber; -const int Test::kHasEchoCountFieldNumber; -const int Test::kHasVoiceCountFieldNumber; -const int Test::kIsSaturatedCountFieldNumber; -const int Test::kEchoMetricsFieldNumber; -#endif // !_MSC_VER - -Test::Test() - : ::google::protobuf::MessageLite() { - SharedCtor(); -} - -void Test::InitAsDefaultInstance() { - echometrics_ = const_cast< ::audio_processing_unittest::Test_EchoMetrics*>(&::audio_processing_unittest::Test_EchoMetrics::default_instance()); -} - -Test::Test(const Test& from) - : ::google::protobuf::MessageLite() { - SharedCtor(); - MergeFrom(from); -} - -void Test::SharedCtor() { - _cached_size_ = 0; - numreversechannels_ = 0; - numchannels_ = 0; - samplerate_ = 0; - hasechocount_ = 0; - hasvoicecount_ = 0; - issaturatedcount_ = 0; - echometrics_ = NULL; - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -Test::~Test() { - SharedDtor(); -} - -void Test::SharedDtor() { - if (this != default_instance_) { - delete echometrics_; - } -} - -void Test::SetCachedSize(int size) const { - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); -} -const Test& Test::default_instance() { - if (default_instance_ == NULL) protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); return *default_instance_; -} - -Test* Test::default_instance_ = NULL; - -Test* Test::New() const { - return new Test; -} - -void Test::Clear() { - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - numreversechannels_ = 0; - numchannels_ = 0; - samplerate_ = 0; - hasechocount_ = 0; - hasvoicecount_ = 0; - issaturatedcount_ = 0; - if (has_echometrics()) { - if (echometrics_ != NULL) echometrics_->::audio_processing_unittest::Test_EchoMetrics::Clear(); - } - } - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -bool Test::MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false - ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { - switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // optional int32 numReverseChannels = 1; - case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &numreversechannels_))); - set_has_numreversechannels(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(16)) goto parse_numChannels; - break; - } - - // optional int32 numChannels = 2; - case 2: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_numChannels: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &numchannels_))); - set_has_numchannels(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(24)) goto parse_sampleRate; - break; - } - - // optional int32 sampleRate = 3; - case 3: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_sampleRate: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &samplerate_))); - set_has_samplerate(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(32)) goto parse_hasEchoCount; - break; - } - - // optional int32 hasEchoCount = 4; - case 4: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_hasEchoCount: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &hasechocount_))); - set_has_hasechocount(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(40)) goto parse_hasVoiceCount; - break; - } - - // optional int32 hasVoiceCount = 5; - case 5: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_hasVoiceCount: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &hasvoicecount_))); - set_has_hasvoicecount(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(48)) goto parse_isSaturatedCount; - break; - } - - // optional int32 isSaturatedCount = 6; - case 6: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { - parse_isSaturatedCount: - DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< - ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( - input, &issaturatedcount_))); - set_has_issaturatedcount(); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(58)) goto parse_echoMetrics; - break; - } - - // optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7; - case 7: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - parse_echoMetrics: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, mutable_echometrics())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectAtEnd()) return true; - break; - } - - default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; - } - DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag, NULL)); - break; - } - } - } - return true; -#undef DO_ -} - -void Test::SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const { - // optional int32 numReverseChannels = 1; - if (has_numreversechannels()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->numreversechannels(), output); - } - - // optional int32 numChannels = 2; - if (has_numchannels()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->numchannels(), output); - } - - // optional int32 sampleRate = 3; - if (has_samplerate()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->samplerate(), output); - } - - // optional int32 hasEchoCount = 4; - if (has_hasechocount()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(4, this->hasechocount(), output); - } - - // optional int32 hasVoiceCount = 5; - if (has_hasvoicecount()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(5, this->hasvoicecount(), output); - } - - // optional int32 isSaturatedCount = 6; - if (has_issaturatedcount()) { - ::google::protobuf::internal::WireFormatLite::WriteInt32(6, this->issaturatedcount(), output); - } - - // optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7; - if (has_echometrics()) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 7, this->echometrics(), output); - } - -} - -int Test::ByteSize() const { - int total_size = 0; - - if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { - // optional int32 numReverseChannels = 1; - if (has_numreversechannels()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->numreversechannels()); - } - - // optional int32 numChannels = 2; - if (has_numchannels()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->numchannels()); - } - - // optional int32 sampleRate = 3; - if (has_samplerate()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->samplerate()); - } - - // optional int32 hasEchoCount = 4; - if (has_hasechocount()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->hasechocount()); - } - - // optional int32 hasVoiceCount = 5; - if (has_hasvoicecount()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->hasvoicecount()); - } - - // optional int32 isSaturatedCount = 6; - if (has_issaturatedcount()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::Int32Size( - this->issaturatedcount()); - } - - // optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7; - if (has_echometrics()) { - total_size += 1 + - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->echometrics()); - } - - } - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = total_size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); - return total_size; -} - -void Test::CheckTypeAndMergeFrom( - const ::google::protobuf::MessageLite& from) { - MergeFrom(*::google::protobuf::down_cast(&from)); -} - -void Test::MergeFrom(const Test& from) { - GOOGLE_CHECK_NE(&from, this); - if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { - if (from.has_numreversechannels()) { - set_numreversechannels(from.numreversechannels()); - } - if (from.has_numchannels()) { - set_numchannels(from.numchannels()); - } - if (from.has_samplerate()) { - set_samplerate(from.samplerate()); - } - if (from.has_hasechocount()) { - set_hasechocount(from.hasechocount()); - } - if (from.has_hasvoicecount()) { - set_hasvoicecount(from.hasvoicecount()); - } - if (from.has_issaturatedcount()) { - set_issaturatedcount(from.issaturatedcount()); - } - if (from.has_echometrics()) { - mutable_echometrics()->::audio_processing_unittest::Test_EchoMetrics::MergeFrom(from.echometrics()); - } - } -} - -void Test::CopyFrom(const Test& from) { - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Test::IsInitialized() const { - - return true; -} - -void Test::Swap(Test* other) { - if (other != this) { - std::swap(numreversechannels_, other->numreversechannels_); - std::swap(numchannels_, other->numchannels_); - std::swap(samplerate_, other->samplerate_); - std::swap(hasechocount_, other->hasechocount_); - std::swap(hasvoicecount_, other->hasvoicecount_); - std::swap(issaturatedcount_, other->issaturatedcount_); - std::swap(echometrics_, other->echometrics_); - std::swap(_has_bits_[0], other->_has_bits_[0]); - std::swap(_cached_size_, other->_cached_size_); - } -} - -::std::string Test::GetTypeName() const { - return "audio_processing_unittest.Test"; -} - - -// =================================================================== - -#ifndef _MSC_VER -const int OutputData::kTestFieldNumber; -#endif // !_MSC_VER - -OutputData::OutputData() - : ::google::protobuf::MessageLite() { - SharedCtor(); -} - -void OutputData::InitAsDefaultInstance() { -} - -OutputData::OutputData(const OutputData& from) - : ::google::protobuf::MessageLite() { - SharedCtor(); - MergeFrom(from); -} - -void OutputData::SharedCtor() { - _cached_size_ = 0; - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -OutputData::~OutputData() { - SharedDtor(); -} - -void OutputData::SharedDtor() { - if (this != default_instance_) { - } -} - -void OutputData::SetCachedSize(int size) const { - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); -} -const OutputData& OutputData::default_instance() { - if (default_instance_ == NULL) protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); return *default_instance_; -} - -OutputData* OutputData::default_instance_ = NULL; - -OutputData* OutputData::New() const { - return new OutputData; -} - -void OutputData::Clear() { - test_.Clear(); - ::memset(_has_bits_, 0, sizeof(_has_bits_)); -} - -bool OutputData::MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!(EXPRESSION)) return false - ::google::protobuf::uint32 tag; - while ((tag = input->ReadTag()) != 0) { - switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated .audio_processing_unittest.Test test = 1; - case 1: { - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { - parse_test: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( - input, add_test())); - } else { - goto handle_uninterpreted; - } - if (input->ExpectTag(10)) goto parse_test; - if (input->ExpectAtEnd()) return true; - break; - } - - default: { - handle_uninterpreted: - if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == - ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { - return true; - } - DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag, NULL)); - break; - } - } - } - return true; -#undef DO_ -} - -void OutputData::SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const { - // repeated .audio_processing_unittest.Test test = 1; - for (int i = 0; i < this->test_size(); i++) { - ::google::protobuf::internal::WireFormatLite::WriteMessage( - 1, this->test(i), output); - } - -} - -int OutputData::ByteSize() const { - int total_size = 0; - - // repeated .audio_processing_unittest.Test test = 1; - total_size += 1 * this->test_size(); - for (int i = 0; i < this->test_size(); i++) { - total_size += - ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( - this->test(i)); - } - - GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); - _cached_size_ = total_size; - GOOGLE_SAFE_CONCURRENT_WRITES_END(); - return total_size; -} - -void OutputData::CheckTypeAndMergeFrom( - const ::google::protobuf::MessageLite& from) { - MergeFrom(*::google::protobuf::down_cast(&from)); -} - -void OutputData::MergeFrom(const OutputData& from) { - GOOGLE_CHECK_NE(&from, this); - test_.MergeFrom(from.test_); -} - -void OutputData::CopyFrom(const OutputData& from) { - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool OutputData::IsInitialized() const { - - return true; -} - -void OutputData::Swap(OutputData* other) { - if (other != this) { - test_.Swap(&other->test_); - std::swap(_has_bits_[0], other->_has_bits_[0]); - std::swap(_cached_size_, other->_cached_size_); - } -} - -::std::string OutputData::GetTypeName() const { - return "audio_processing_unittest.OutputData"; -} - - -// @@protoc_insertion_point(namespace_scope) - -} // namespace audio_processing_unittest - -// @@protoc_insertion_point(global_scope) diff --git a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.h b/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.h deleted file mode 100644 index 34c21b2f4..000000000 --- a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.pb.h +++ /dev/null @@ -1,862 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: audio_processing_unittest.proto - -#ifndef PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED -#define PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED - -#include - -#include - -#if GOOGLE_PROTOBUF_VERSION < 2004000 -#error This file was generated by a newer version of protoc which is -#error incompatible with your Protocol Buffer headers. Please update -#error your headers. -#endif -#if 2004000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION -#error This file was generated by an older version of protoc which is -#error incompatible with your Protocol Buffer headers. Please -#error regenerate this file with a newer version of protoc. -#endif - -#include -#include -#include -// @@protoc_insertion_point(includes) - -namespace audio_processing_unittest { - -// Internal implementation detail -- do not call these. -void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); -void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto(); -void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto(); - -class Test; -class Test_Statistic; -class Test_EchoMetrics; -class OutputData; - -// =================================================================== - -class Test_Statistic : public ::google::protobuf::MessageLite { - public: - Test_Statistic(); - virtual ~Test_Statistic(); - - Test_Statistic(const Test_Statistic& from); - - inline Test_Statistic& operator=(const Test_Statistic& from) { - CopyFrom(from); - return *this; - } - - static const Test_Statistic& default_instance(); - - void Swap(Test_Statistic* other); - - // implements Message ---------------------------------------------- - - Test_Statistic* New() const; - void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); - void CopyFrom(const Test_Statistic& from); - void MergeFrom(const Test_Statistic& from); - void Clear(); - bool IsInitialized() const; - - int ByteSize() const; - bool MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input); - void SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const; - int GetCachedSize() const { return _cached_size_; } - private: - void SharedCtor(); - void SharedDtor(); - void SetCachedSize(int size) const; - public: - - ::std::string GetTypeName() const; - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - // optional int32 instant = 1; - inline bool has_instant() const; - inline void clear_instant(); - static const int kInstantFieldNumber = 1; - inline ::google::protobuf::int32 instant() const; - inline void set_instant(::google::protobuf::int32 value); - - // optional int32 average = 2; - inline bool has_average() const; - inline void clear_average(); - static const int kAverageFieldNumber = 2; - inline ::google::protobuf::int32 average() const; - inline void set_average(::google::protobuf::int32 value); - - // optional int32 maximum = 3; - inline bool has_maximum() const; - inline void clear_maximum(); - static const int kMaximumFieldNumber = 3; - inline ::google::protobuf::int32 maximum() const; - inline void set_maximum(::google::protobuf::int32 value); - - // optional int32 minimum = 4; - inline bool has_minimum() const; - inline void clear_minimum(); - static const int kMinimumFieldNumber = 4; - inline ::google::protobuf::int32 minimum() const; - inline void set_minimum(::google::protobuf::int32 value); - - // @@protoc_insertion_point(class_scope:audio_processing_unittest.Test.Statistic) - private: - inline void set_has_instant(); - inline void clear_has_instant(); - inline void set_has_average(); - inline void clear_has_average(); - inline void set_has_maximum(); - inline void clear_has_maximum(); - inline void set_has_minimum(); - inline void clear_has_minimum(); - - ::google::protobuf::int32 instant_; - ::google::protobuf::int32 average_; - ::google::protobuf::int32 maximum_; - ::google::protobuf::int32 minimum_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; - - friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto(); - - void InitAsDefaultInstance(); - static Test_Statistic* default_instance_; -}; -// ------------------------------------------------------------------- - -class Test_EchoMetrics : public ::google::protobuf::MessageLite { - public: - Test_EchoMetrics(); - virtual ~Test_EchoMetrics(); - - Test_EchoMetrics(const Test_EchoMetrics& from); - - inline Test_EchoMetrics& operator=(const Test_EchoMetrics& from) { - CopyFrom(from); - return *this; - } - - static const Test_EchoMetrics& default_instance(); - - void Swap(Test_EchoMetrics* other); - - // implements Message ---------------------------------------------- - - Test_EchoMetrics* New() const; - void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); - void CopyFrom(const Test_EchoMetrics& from); - void MergeFrom(const Test_EchoMetrics& from); - void Clear(); - bool IsInitialized() const; - - int ByteSize() const; - bool MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input); - void SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const; - int GetCachedSize() const { return _cached_size_; } - private: - void SharedCtor(); - void SharedDtor(); - void SetCachedSize(int size) const; - public: - - ::std::string GetTypeName() const; - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - // optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1; - inline bool has_residualechoreturnloss() const; - inline void clear_residualechoreturnloss(); - static const int kResidualEchoReturnLossFieldNumber = 1; - inline const ::audio_processing_unittest::Test_Statistic& residualechoreturnloss() const; - inline ::audio_processing_unittest::Test_Statistic* mutable_residualechoreturnloss(); - inline ::audio_processing_unittest::Test_Statistic* release_residualechoreturnloss(); - - // optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2; - inline bool has_echoreturnloss() const; - inline void clear_echoreturnloss(); - static const int kEchoReturnLossFieldNumber = 2; - inline const ::audio_processing_unittest::Test_Statistic& echoreturnloss() const; - inline ::audio_processing_unittest::Test_Statistic* mutable_echoreturnloss(); - inline ::audio_processing_unittest::Test_Statistic* release_echoreturnloss(); - - // optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3; - inline bool has_echoreturnlossenhancement() const; - inline void clear_echoreturnlossenhancement(); - static const int kEchoReturnLossEnhancementFieldNumber = 3; - inline const ::audio_processing_unittest::Test_Statistic& echoreturnlossenhancement() const; - inline ::audio_processing_unittest::Test_Statistic* mutable_echoreturnlossenhancement(); - inline ::audio_processing_unittest::Test_Statistic* release_echoreturnlossenhancement(); - - // optional .audio_processing_unittest.Test.Statistic aNlp = 4; - inline bool has_anlp() const; - inline void clear_anlp(); - static const int kANlpFieldNumber = 4; - inline const ::audio_processing_unittest::Test_Statistic& anlp() const; - inline ::audio_processing_unittest::Test_Statistic* mutable_anlp(); - inline ::audio_processing_unittest::Test_Statistic* release_anlp(); - - // @@protoc_insertion_point(class_scope:audio_processing_unittest.Test.EchoMetrics) - private: - inline void set_has_residualechoreturnloss(); - inline void clear_has_residualechoreturnloss(); - inline void set_has_echoreturnloss(); - inline void clear_has_echoreturnloss(); - inline void set_has_echoreturnlossenhancement(); - inline void clear_has_echoreturnlossenhancement(); - inline void set_has_anlp(); - inline void clear_has_anlp(); - - ::audio_processing_unittest::Test_Statistic* residualechoreturnloss_; - ::audio_processing_unittest::Test_Statistic* echoreturnloss_; - ::audio_processing_unittest::Test_Statistic* echoreturnlossenhancement_; - ::audio_processing_unittest::Test_Statistic* anlp_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; - - friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto(); - - void InitAsDefaultInstance(); - static Test_EchoMetrics* default_instance_; -}; -// ------------------------------------------------------------------- - -class Test : public ::google::protobuf::MessageLite { - public: - Test(); - virtual ~Test(); - - Test(const Test& from); - - inline Test& operator=(const Test& from) { - CopyFrom(from); - return *this; - } - - static const Test& default_instance(); - - void Swap(Test* other); - - // implements Message ---------------------------------------------- - - Test* New() const; - void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); - void CopyFrom(const Test& from); - void MergeFrom(const Test& from); - void Clear(); - bool IsInitialized() const; - - int ByteSize() const; - bool MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input); - void SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const; - int GetCachedSize() const { return _cached_size_; } - private: - void SharedCtor(); - void SharedDtor(); - void SetCachedSize(int size) const; - public: - - ::std::string GetTypeName() const; - - // nested types ---------------------------------------------------- - - typedef Test_Statistic Statistic; - typedef Test_EchoMetrics EchoMetrics; - - // accessors ------------------------------------------------------- - - // optional int32 numReverseChannels = 1; - inline bool has_numreversechannels() const; - inline void clear_numreversechannels(); - static const int kNumReverseChannelsFieldNumber = 1; - inline ::google::protobuf::int32 numreversechannels() const; - inline void set_numreversechannels(::google::protobuf::int32 value); - - // optional int32 numChannels = 2; - inline bool has_numchannels() const; - inline void clear_numchannels(); - static const int kNumChannelsFieldNumber = 2; - inline ::google::protobuf::int32 numchannels() const; - inline void set_numchannels(::google::protobuf::int32 value); - - // optional int32 sampleRate = 3; - inline bool has_samplerate() const; - inline void clear_samplerate(); - static const int kSampleRateFieldNumber = 3; - inline ::google::protobuf::int32 samplerate() const; - inline void set_samplerate(::google::protobuf::int32 value); - - // optional int32 hasEchoCount = 4; - inline bool has_hasechocount() const; - inline void clear_hasechocount(); - static const int kHasEchoCountFieldNumber = 4; - inline ::google::protobuf::int32 hasechocount() const; - inline void set_hasechocount(::google::protobuf::int32 value); - - // optional int32 hasVoiceCount = 5; - inline bool has_hasvoicecount() const; - inline void clear_hasvoicecount(); - static const int kHasVoiceCountFieldNumber = 5; - inline ::google::protobuf::int32 hasvoicecount() const; - inline void set_hasvoicecount(::google::protobuf::int32 value); - - // optional int32 isSaturatedCount = 6; - inline bool has_issaturatedcount() const; - inline void clear_issaturatedcount(); - static const int kIsSaturatedCountFieldNumber = 6; - inline ::google::protobuf::int32 issaturatedcount() const; - inline void set_issaturatedcount(::google::protobuf::int32 value); - - // optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7; - inline bool has_echometrics() const; - inline void clear_echometrics(); - static const int kEchoMetricsFieldNumber = 7; - inline const ::audio_processing_unittest::Test_EchoMetrics& echometrics() const; - inline ::audio_processing_unittest::Test_EchoMetrics* mutable_echometrics(); - inline ::audio_processing_unittest::Test_EchoMetrics* release_echometrics(); - - // @@protoc_insertion_point(class_scope:audio_processing_unittest.Test) - private: - inline void set_has_numreversechannels(); - inline void clear_has_numreversechannels(); - inline void set_has_numchannels(); - inline void clear_has_numchannels(); - inline void set_has_samplerate(); - inline void clear_has_samplerate(); - inline void set_has_hasechocount(); - inline void clear_has_hasechocount(); - inline void set_has_hasvoicecount(); - inline void clear_has_hasvoicecount(); - inline void set_has_issaturatedcount(); - inline void clear_has_issaturatedcount(); - inline void set_has_echometrics(); - inline void clear_has_echometrics(); - - ::google::protobuf::int32 numreversechannels_; - ::google::protobuf::int32 numchannels_; - ::google::protobuf::int32 samplerate_; - ::google::protobuf::int32 hasechocount_; - ::google::protobuf::int32 hasvoicecount_; - ::google::protobuf::int32 issaturatedcount_; - ::audio_processing_unittest::Test_EchoMetrics* echometrics_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; - - friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto(); - - void InitAsDefaultInstance(); - static Test* default_instance_; -}; -// ------------------------------------------------------------------- - -class OutputData : public ::google::protobuf::MessageLite { - public: - OutputData(); - virtual ~OutputData(); - - OutputData(const OutputData& from); - - inline OutputData& operator=(const OutputData& from) { - CopyFrom(from); - return *this; - } - - static const OutputData& default_instance(); - - void Swap(OutputData* other); - - // implements Message ---------------------------------------------- - - OutputData* New() const; - void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from); - void CopyFrom(const OutputData& from); - void MergeFrom(const OutputData& from); - void Clear(); - bool IsInitialized() const; - - int ByteSize() const; - bool MergePartialFromCodedStream( - ::google::protobuf::io::CodedInputStream* input); - void SerializeWithCachedSizes( - ::google::protobuf::io::CodedOutputStream* output) const; - int GetCachedSize() const { return _cached_size_; } - private: - void SharedCtor(); - void SharedDtor(); - void SetCachedSize(int size) const; - public: - - ::std::string GetTypeName() const; - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - // repeated .audio_processing_unittest.Test test = 1; - inline int test_size() const; - inline void clear_test(); - static const int kTestFieldNumber = 1; - inline const ::audio_processing_unittest::Test& test(int index) const; - inline ::audio_processing_unittest::Test* mutable_test(int index); - inline ::audio_processing_unittest::Test* add_test(); - inline const ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >& - test() const; - inline ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >* - mutable_test(); - - // @@protoc_insertion_point(class_scope:audio_processing_unittest.OutputData) - private: - - ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test > test_; - - mutable int _cached_size_; - ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; - - friend void protobuf_AddDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_AssignDesc_audio_5fprocessing_5funittest_2eproto(); - friend void protobuf_ShutdownFile_audio_5fprocessing_5funittest_2eproto(); - - void InitAsDefaultInstance(); - static OutputData* default_instance_; -}; -// =================================================================== - - -// =================================================================== - -// Test_Statistic - -// optional int32 instant = 1; -inline bool Test_Statistic::has_instant() const { - return (_has_bits_[0] & 0x00000001u) != 0; -} -inline void Test_Statistic::set_has_instant() { - _has_bits_[0] |= 0x00000001u; -} -inline void Test_Statistic::clear_has_instant() { - _has_bits_[0] &= ~0x00000001u; -} -inline void Test_Statistic::clear_instant() { - instant_ = 0; - clear_has_instant(); -} -inline ::google::protobuf::int32 Test_Statistic::instant() const { - return instant_; -} -inline void Test_Statistic::set_instant(::google::protobuf::int32 value) { - set_has_instant(); - instant_ = value; -} - -// optional int32 average = 2; -inline bool Test_Statistic::has_average() const { - return (_has_bits_[0] & 0x00000002u) != 0; -} -inline void Test_Statistic::set_has_average() { - _has_bits_[0] |= 0x00000002u; -} -inline void Test_Statistic::clear_has_average() { - _has_bits_[0] &= ~0x00000002u; -} -inline void Test_Statistic::clear_average() { - average_ = 0; - clear_has_average(); -} -inline ::google::protobuf::int32 Test_Statistic::average() const { - return average_; -} -inline void Test_Statistic::set_average(::google::protobuf::int32 value) { - set_has_average(); - average_ = value; -} - -// optional int32 maximum = 3; -inline bool Test_Statistic::has_maximum() const { - return (_has_bits_[0] & 0x00000004u) != 0; -} -inline void Test_Statistic::set_has_maximum() { - _has_bits_[0] |= 0x00000004u; -} -inline void Test_Statistic::clear_has_maximum() { - _has_bits_[0] &= ~0x00000004u; -} -inline void Test_Statistic::clear_maximum() { - maximum_ = 0; - clear_has_maximum(); -} -inline ::google::protobuf::int32 Test_Statistic::maximum() const { - return maximum_; -} -inline void Test_Statistic::set_maximum(::google::protobuf::int32 value) { - set_has_maximum(); - maximum_ = value; -} - -// optional int32 minimum = 4; -inline bool Test_Statistic::has_minimum() const { - return (_has_bits_[0] & 0x00000008u) != 0; -} -inline void Test_Statistic::set_has_minimum() { - _has_bits_[0] |= 0x00000008u; -} -inline void Test_Statistic::clear_has_minimum() { - _has_bits_[0] &= ~0x00000008u; -} -inline void Test_Statistic::clear_minimum() { - minimum_ = 0; - clear_has_minimum(); -} -inline ::google::protobuf::int32 Test_Statistic::minimum() const { - return minimum_; -} -inline void Test_Statistic::set_minimum(::google::protobuf::int32 value) { - set_has_minimum(); - minimum_ = value; -} - -// ------------------------------------------------------------------- - -// Test_EchoMetrics - -// optional .audio_processing_unittest.Test.Statistic residualEchoReturnLoss = 1; -inline bool Test_EchoMetrics::has_residualechoreturnloss() const { - return (_has_bits_[0] & 0x00000001u) != 0; -} -inline void Test_EchoMetrics::set_has_residualechoreturnloss() { - _has_bits_[0] |= 0x00000001u; -} -inline void Test_EchoMetrics::clear_has_residualechoreturnloss() { - _has_bits_[0] &= ~0x00000001u; -} -inline void Test_EchoMetrics::clear_residualechoreturnloss() { - if (residualechoreturnloss_ != NULL) residualechoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear(); - clear_has_residualechoreturnloss(); -} -inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::residualechoreturnloss() const { - return residualechoreturnloss_ != NULL ? *residualechoreturnloss_ : *default_instance_->residualechoreturnloss_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_residualechoreturnloss() { - set_has_residualechoreturnloss(); - if (residualechoreturnloss_ == NULL) residualechoreturnloss_ = new ::audio_processing_unittest::Test_Statistic; - return residualechoreturnloss_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_residualechoreturnloss() { - clear_has_residualechoreturnloss(); - ::audio_processing_unittest::Test_Statistic* temp = residualechoreturnloss_; - residualechoreturnloss_ = NULL; - return temp; -} - -// optional .audio_processing_unittest.Test.Statistic echoReturnLoss = 2; -inline bool Test_EchoMetrics::has_echoreturnloss() const { - return (_has_bits_[0] & 0x00000002u) != 0; -} -inline void Test_EchoMetrics::set_has_echoreturnloss() { - _has_bits_[0] |= 0x00000002u; -} -inline void Test_EchoMetrics::clear_has_echoreturnloss() { - _has_bits_[0] &= ~0x00000002u; -} -inline void Test_EchoMetrics::clear_echoreturnloss() { - if (echoreturnloss_ != NULL) echoreturnloss_->::audio_processing_unittest::Test_Statistic::Clear(); - clear_has_echoreturnloss(); -} -inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::echoreturnloss() const { - return echoreturnloss_ != NULL ? *echoreturnloss_ : *default_instance_->echoreturnloss_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_echoreturnloss() { - set_has_echoreturnloss(); - if (echoreturnloss_ == NULL) echoreturnloss_ = new ::audio_processing_unittest::Test_Statistic; - return echoreturnloss_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_echoreturnloss() { - clear_has_echoreturnloss(); - ::audio_processing_unittest::Test_Statistic* temp = echoreturnloss_; - echoreturnloss_ = NULL; - return temp; -} - -// optional .audio_processing_unittest.Test.Statistic echoReturnLossEnhancement = 3; -inline bool Test_EchoMetrics::has_echoreturnlossenhancement() const { - return (_has_bits_[0] & 0x00000004u) != 0; -} -inline void Test_EchoMetrics::set_has_echoreturnlossenhancement() { - _has_bits_[0] |= 0x00000004u; -} -inline void Test_EchoMetrics::clear_has_echoreturnlossenhancement() { - _has_bits_[0] &= ~0x00000004u; -} -inline void Test_EchoMetrics::clear_echoreturnlossenhancement() { - if (echoreturnlossenhancement_ != NULL) echoreturnlossenhancement_->::audio_processing_unittest::Test_Statistic::Clear(); - clear_has_echoreturnlossenhancement(); -} -inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::echoreturnlossenhancement() const { - return echoreturnlossenhancement_ != NULL ? *echoreturnlossenhancement_ : *default_instance_->echoreturnlossenhancement_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_echoreturnlossenhancement() { - set_has_echoreturnlossenhancement(); - if (echoreturnlossenhancement_ == NULL) echoreturnlossenhancement_ = new ::audio_processing_unittest::Test_Statistic; - return echoreturnlossenhancement_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_echoreturnlossenhancement() { - clear_has_echoreturnlossenhancement(); - ::audio_processing_unittest::Test_Statistic* temp = echoreturnlossenhancement_; - echoreturnlossenhancement_ = NULL; - return temp; -} - -// optional .audio_processing_unittest.Test.Statistic aNlp = 4; -inline bool Test_EchoMetrics::has_anlp() const { - return (_has_bits_[0] & 0x00000008u) != 0; -} -inline void Test_EchoMetrics::set_has_anlp() { - _has_bits_[0] |= 0x00000008u; -} -inline void Test_EchoMetrics::clear_has_anlp() { - _has_bits_[0] &= ~0x00000008u; -} -inline void Test_EchoMetrics::clear_anlp() { - if (anlp_ != NULL) anlp_->::audio_processing_unittest::Test_Statistic::Clear(); - clear_has_anlp(); -} -inline const ::audio_processing_unittest::Test_Statistic& Test_EchoMetrics::anlp() const { - return anlp_ != NULL ? *anlp_ : *default_instance_->anlp_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::mutable_anlp() { - set_has_anlp(); - if (anlp_ == NULL) anlp_ = new ::audio_processing_unittest::Test_Statistic; - return anlp_; -} -inline ::audio_processing_unittest::Test_Statistic* Test_EchoMetrics::release_anlp() { - clear_has_anlp(); - ::audio_processing_unittest::Test_Statistic* temp = anlp_; - anlp_ = NULL; - return temp; -} - -// ------------------------------------------------------------------- - -// Test - -// optional int32 numReverseChannels = 1; -inline bool Test::has_numreversechannels() const { - return (_has_bits_[0] & 0x00000001u) != 0; -} -inline void Test::set_has_numreversechannels() { - _has_bits_[0] |= 0x00000001u; -} -inline void Test::clear_has_numreversechannels() { - _has_bits_[0] &= ~0x00000001u; -} -inline void Test::clear_numreversechannels() { - numreversechannels_ = 0; - clear_has_numreversechannels(); -} -inline ::google::protobuf::int32 Test::numreversechannels() const { - return numreversechannels_; -} -inline void Test::set_numreversechannels(::google::protobuf::int32 value) { - set_has_numreversechannels(); - numreversechannels_ = value; -} - -// optional int32 numChannels = 2; -inline bool Test::has_numchannels() const { - return (_has_bits_[0] & 0x00000002u) != 0; -} -inline void Test::set_has_numchannels() { - _has_bits_[0] |= 0x00000002u; -} -inline void Test::clear_has_numchannels() { - _has_bits_[0] &= ~0x00000002u; -} -inline void Test::clear_numchannels() { - numchannels_ = 0; - clear_has_numchannels(); -} -inline ::google::protobuf::int32 Test::numchannels() const { - return numchannels_; -} -inline void Test::set_numchannels(::google::protobuf::int32 value) { - set_has_numchannels(); - numchannels_ = value; -} - -// optional int32 sampleRate = 3; -inline bool Test::has_samplerate() const { - return (_has_bits_[0] & 0x00000004u) != 0; -} -inline void Test::set_has_samplerate() { - _has_bits_[0] |= 0x00000004u; -} -inline void Test::clear_has_samplerate() { - _has_bits_[0] &= ~0x00000004u; -} -inline void Test::clear_samplerate() { - samplerate_ = 0; - clear_has_samplerate(); -} -inline ::google::protobuf::int32 Test::samplerate() const { - return samplerate_; -} -inline void Test::set_samplerate(::google::protobuf::int32 value) { - set_has_samplerate(); - samplerate_ = value; -} - -// optional int32 hasEchoCount = 4; -inline bool Test::has_hasechocount() const { - return (_has_bits_[0] & 0x00000008u) != 0; -} -inline void Test::set_has_hasechocount() { - _has_bits_[0] |= 0x00000008u; -} -inline void Test::clear_has_hasechocount() { - _has_bits_[0] &= ~0x00000008u; -} -inline void Test::clear_hasechocount() { - hasechocount_ = 0; - clear_has_hasechocount(); -} -inline ::google::protobuf::int32 Test::hasechocount() const { - return hasechocount_; -} -inline void Test::set_hasechocount(::google::protobuf::int32 value) { - set_has_hasechocount(); - hasechocount_ = value; -} - -// optional int32 hasVoiceCount = 5; -inline bool Test::has_hasvoicecount() const { - return (_has_bits_[0] & 0x00000010u) != 0; -} -inline void Test::set_has_hasvoicecount() { - _has_bits_[0] |= 0x00000010u; -} -inline void Test::clear_has_hasvoicecount() { - _has_bits_[0] &= ~0x00000010u; -} -inline void Test::clear_hasvoicecount() { - hasvoicecount_ = 0; - clear_has_hasvoicecount(); -} -inline ::google::protobuf::int32 Test::hasvoicecount() const { - return hasvoicecount_; -} -inline void Test::set_hasvoicecount(::google::protobuf::int32 value) { - set_has_hasvoicecount(); - hasvoicecount_ = value; -} - -// optional int32 isSaturatedCount = 6; -inline bool Test::has_issaturatedcount() const { - return (_has_bits_[0] & 0x00000020u) != 0; -} -inline void Test::set_has_issaturatedcount() { - _has_bits_[0] |= 0x00000020u; -} -inline void Test::clear_has_issaturatedcount() { - _has_bits_[0] &= ~0x00000020u; -} -inline void Test::clear_issaturatedcount() { - issaturatedcount_ = 0; - clear_has_issaturatedcount(); -} -inline ::google::protobuf::int32 Test::issaturatedcount() const { - return issaturatedcount_; -} -inline void Test::set_issaturatedcount(::google::protobuf::int32 value) { - set_has_issaturatedcount(); - issaturatedcount_ = value; -} - -// optional .audio_processing_unittest.Test.EchoMetrics echoMetrics = 7; -inline bool Test::has_echometrics() const { - return (_has_bits_[0] & 0x00000040u) != 0; -} -inline void Test::set_has_echometrics() { - _has_bits_[0] |= 0x00000040u; -} -inline void Test::clear_has_echometrics() { - _has_bits_[0] &= ~0x00000040u; -} -inline void Test::clear_echometrics() { - if (echometrics_ != NULL) echometrics_->::audio_processing_unittest::Test_EchoMetrics::Clear(); - clear_has_echometrics(); -} -inline const ::audio_processing_unittest::Test_EchoMetrics& Test::echometrics() const { - return echometrics_ != NULL ? *echometrics_ : *default_instance_->echometrics_; -} -inline ::audio_processing_unittest::Test_EchoMetrics* Test::mutable_echometrics() { - set_has_echometrics(); - if (echometrics_ == NULL) echometrics_ = new ::audio_processing_unittest::Test_EchoMetrics; - return echometrics_; -} -inline ::audio_processing_unittest::Test_EchoMetrics* Test::release_echometrics() { - clear_has_echometrics(); - ::audio_processing_unittest::Test_EchoMetrics* temp = echometrics_; - echometrics_ = NULL; - return temp; -} - -// ------------------------------------------------------------------- - -// OutputData - -// repeated .audio_processing_unittest.Test test = 1; -inline int OutputData::test_size() const { - return test_.size(); -} -inline void OutputData::clear_test() { - test_.Clear(); -} -inline const ::audio_processing_unittest::Test& OutputData::test(int index) const { - return test_.Get(index); -} -inline ::audio_processing_unittest::Test* OutputData::mutable_test(int index) { - return test_.Mutable(index); -} -inline ::audio_processing_unittest::Test* OutputData::add_test() { - return test_.Add(); -} -inline const ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >& -OutputData::test() const { - return test_; -} -inline ::google::protobuf::RepeatedPtrField< ::audio_processing_unittest::Test >* -OutputData::mutable_test() { - return &test_; -} - - -// @@protoc_insertion_point(namespace_scope) - -} // namespace audio_processing_unittest - -// @@protoc_insertion_point(global_scope) - -#endif // PROTOBUF_audio_5fprocessing_5funittest_2eproto__INCLUDED diff --git a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.proto b/modules/audio_processing/main/test/unit_test/audio_processing_unittest.proto deleted file mode 100644 index 8520e64f2..000000000 --- a/modules/audio_processing/main/test/unit_test/audio_processing_unittest.proto +++ /dev/null @@ -1,33 +0,0 @@ -package audio_processing_unittest; -option optimize_for = LITE_RUNTIME; - -message Test { - optional int32 numReverseChannels = 1; - optional int32 numChannels = 2; - optional int32 sampleRate = 3; - - optional int32 hasEchoCount = 4; - optional int32 hasVoiceCount = 5; - optional int32 isSaturatedCount = 6; - - message Statistic { - optional int32 instant = 1; - optional int32 average = 2; - optional int32 maximum = 3; - optional int32 minimum = 4; - } - - message EchoMetrics { - optional Statistic residualEchoReturnLoss = 1; - optional Statistic echoReturnLoss = 2; - optional Statistic echoReturnLossEnhancement = 3; - optional Statistic aNlp = 4; - } - - optional EchoMetrics echoMetrics = 7; -} - -message OutputData { - repeated Test test = 1; -} - diff --git a/modules/audio_processing/main/test/unit_test/unit_test.cc b/modules/audio_processing/main/test/unit_test/unit_test.cc deleted file mode 100644 index 3a6fce5a3..000000000 --- a/modules/audio_processing/main/test/unit_test/unit_test.cc +++ /dev/null @@ -1,881 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include - -#include "audio_processing.h" -#include "audio_processing_unittest.pb.h" -#include "event_wrapper.h" -#include "module_common_types.h" -#include "thread_wrapper.h" -#include "trace.h" -#include "signal_processing_library.h" - -using webrtc::AudioProcessing; -using webrtc::AudioFrame; -using webrtc::GainControl; -using webrtc::NoiseSuppression; -using webrtc::EchoCancellation; -using webrtc::EventWrapper; -using webrtc::Trace; -using webrtc::LevelEstimator; -using webrtc::EchoCancellation; -using webrtc::EchoControlMobile; -using webrtc::VoiceDetection; - -namespace { -// When true, this will compare the output data with the results stored to -// file. This is the typical case. When the file should be updated, it can -// be set to false with the command-line switch --write_output_data. -bool global_read_output_data = true; - -class ApmEnvironment : public ::testing::Environment { - public: - virtual void SetUp() { - Trace::CreateTrace(); - ASSERT_EQ(0, Trace::SetTraceFile("apm_trace.txt")); - } - - virtual void TearDown() { - Trace::ReturnTrace(); - } -}; - -class ApmTest : public ::testing::Test { - protected: - ApmTest(); - virtual void SetUp(); - virtual void TearDown(); - - webrtc::AudioProcessing* apm_; - webrtc::AudioFrame* frame_; - webrtc::AudioFrame* revframe_; - FILE* far_file_; - FILE* near_file_; - bool update_output_data_; -}; - -ApmTest::ApmTest() - : apm_(NULL), - far_file_(NULL), - near_file_(NULL), - frame_(NULL), - revframe_(NULL) {} - -void ApmTest::SetUp() { - apm_ = AudioProcessing::Create(0); - ASSERT_TRUE(apm_ != NULL); - - frame_ = new AudioFrame(); - revframe_ = new AudioFrame(); - - ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000)); - ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(2, 2)); - ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(2)); - - frame_->_payloadDataLengthInSamples = 320; - frame_->_audioChannel = 2; - frame_->_frequencyInHz = 32000; - revframe_->_payloadDataLengthInSamples = 320; - revframe_->_audioChannel = 2; - revframe_->_frequencyInHz = 32000; - - far_file_ = fopen("aec_far.pcm", "rb"); - ASSERT_TRUE(far_file_ != NULL) << "Could not open input file aec_far.pcm\n"; - near_file_ = fopen("aec_near.pcm", "rb"); - ASSERT_TRUE(near_file_ != NULL) << "Could not open input file aec_near.pcm\n"; -} - -void ApmTest::TearDown() { - if (frame_) { - delete frame_; - } - frame_ = NULL; - - if (revframe_) { - delete revframe_; - } - revframe_ = NULL; - - if (far_file_) { - ASSERT_EQ(0, fclose(far_file_)); - } - far_file_ = NULL; - - if (near_file_) { - ASSERT_EQ(0, fclose(near_file_)); - } - near_file_ = NULL; - - if (apm_ != NULL) { - AudioProcessing::Destroy(apm_); - } - apm_ = NULL; -} - -void MixStereoToMono(WebRtc_Word16* stereo, - WebRtc_Word16* mono, - int numSamples) { - for (int i = 0; i < numSamples; i++) { - int int32 = (static_cast(stereo[i * 2]) + - static_cast(stereo[i * 2 + 1])) >> 1; - mono[i] = static_cast(int32); - } -} - -void WriteMessageLiteToFile(const char* filename, - const ::google::protobuf::MessageLite& message) { - assert(filename != NULL); - - FILE* file = fopen(filename, "wb"); - ASSERT_TRUE(file != NULL) << "Could not open " << filename; - int size = message.ByteSize(); - ASSERT_GT(size, 0); - unsigned char* array = new unsigned char[size]; - ASSERT_TRUE(message.SerializeToArray(array, size)); - - ASSERT_EQ(1, fwrite(&size, sizeof(int), 1, file)); - ASSERT_EQ(size, fwrite(array, sizeof(unsigned char), size, file)); - - delete [] array; - fclose(file); -} - -void ReadMessageLiteFromFile(const char* filename, - ::google::protobuf::MessageLite* message) { - assert(filename != NULL); - assert(message != NULL); - - FILE* file = fopen(filename, "rb"); - ASSERT_TRUE(file != NULL) << "Could not open " << filename; - int size = 0; - ASSERT_EQ(1, fread(&size, sizeof(int), 1, file)); - ASSERT_GT(size, 0); - unsigned char* array = new unsigned char[size]; - ASSERT_EQ(size, fread(array, sizeof(unsigned char), size, file)); - - ASSERT_TRUE(message->ParseFromArray(array, size)); - - delete [] array; - fclose(file); -} - -struct ThreadData { - ThreadData(int thread_num_, AudioProcessing* ap_) - : thread_num(thread_num_), - error(false), - ap(ap_) {} - int thread_num; - bool error; - AudioProcessing* ap; -}; - -// Don't use GTest here; non-thread-safe on Windows (as of 1.5.0). -bool DeadlockProc(void* thread_object) { - ThreadData* thread_data = static_cast(thread_object); - AudioProcessing* ap = thread_data->ap; - int err = ap->kNoError; - - AudioFrame primary_frame; - AudioFrame reverse_frame; - primary_frame._payloadDataLengthInSamples = 320; - primary_frame._audioChannel = 2; - primary_frame._frequencyInHz = 32000; - reverse_frame._payloadDataLengthInSamples = 320; - reverse_frame._audioChannel = 2; - reverse_frame._frequencyInHz = 32000; - - ap->echo_cancellation()->Enable(true); - ap->gain_control()->Enable(true); - ap->high_pass_filter()->Enable(true); - ap->level_estimator()->Enable(true); - ap->noise_suppression()->Enable(true); - ap->voice_detection()->Enable(true); - - if (thread_data->thread_num % 2 == 0) { - err = ap->AnalyzeReverseStream(&reverse_frame); - if (err != ap->kNoError) { - printf("Error in AnalyzeReverseStream(): %d\n", err); - thread_data->error = true; - return false; - } - } - - if (thread_data->thread_num % 2 == 1) { - ap->set_stream_delay_ms(0); - ap->echo_cancellation()->set_stream_drift_samples(0); - ap->gain_control()->set_stream_analog_level(0); - err = ap->ProcessStream(&primary_frame); - if (err == ap->kStreamParameterNotSetError) { - printf("Expected kStreamParameterNotSetError in ProcessStream(): %d\n", - err); - } else if (err != ap->kNoError) { - printf("Error in ProcessStream(): %d\n", err); - thread_data->error = true; - return false; - } - ap->gain_control()->stream_analog_level(); - } - - EventWrapper* event = EventWrapper::Create(); - event->Wait(1); - delete event; - event = NULL; - - return true; -} - -/*TEST_F(ApmTest, Deadlock) { - const int num_threads = 16; - std::vector threads(num_threads); - std::vector thread_data(num_threads); - - ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000)); - ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(2, 2)); - ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(2)); - - for (int i = 0; i < num_threads; i++) { - thread_data[i] = new ThreadData(i, apm_); - threads[i] = ThreadWrapper::CreateThread(DeadlockProc, - thread_data[i], - kNormalPriority, - 0); - ASSERT_TRUE(threads[i] != NULL); - unsigned int thread_id = 0; - threads[i]->Start(thread_id); - } - - EventWrapper* event = EventWrapper::Create(); - ASSERT_EQ(kEventTimeout, event->Wait(5000)); - delete event; - event = NULL; - - for (int i = 0; i < num_threads; i++) { - // This will return false if the thread has deadlocked. - ASSERT_TRUE(threads[i]->Stop()); - ASSERT_FALSE(thread_data[i]->error); - delete threads[i]; - threads[i] = NULL; - delete thread_data[i]; - thread_data[i] = NULL; - } -}*/ - -TEST_F(ApmTest, StreamParameters) { - // No errors when the components are disabled. - EXPECT_EQ(apm_->kNoError, - apm_->ProcessStream(frame_)); - - // Missing agc level - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_stream_drift_samples(0)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); - - // Missing delay - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_stream_drift_samples(0)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); - - // Missing drift - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_drift_compensation(true)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - - // No stream parameters - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - EXPECT_EQ(apm_->kNoError, - apm_->AnalyzeReverseStream(revframe_)); - EXPECT_EQ(apm_->kStreamParameterNotSetError, - apm_->ProcessStream(frame_)); - - // All there - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_stream_drift_samples(0)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_stream_analog_level(127)); - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); -} - -TEST_F(ApmTest, Channels) { - // Testing number of invalid channels - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(0, 1)); - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 0)); - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(3, 1)); - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 3)); - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(0)); - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(3)); - // Testing number of valid channels - for (int i = 1; i < 3; i++) { - for (int j = 1; j < 3; j++) { - if (j > i) { - EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(i, j)); - } else { - EXPECT_EQ(apm_->kNoError, apm_->set_num_channels(i, j)); - EXPECT_EQ(j, apm_->num_output_channels()); - } - } - EXPECT_EQ(i, apm_->num_input_channels()); - EXPECT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(i)); - EXPECT_EQ(i, apm_->num_reverse_channels()); - } -} - -TEST_F(ApmTest, SampleRates) { - // Testing invalid sample rates - EXPECT_EQ(apm_->kBadParameterError, apm_->set_sample_rate_hz(10000)); - // Testing valid sample rates - int fs[] = {8000, 16000, 32000}; - for (size_t i = 0; i < sizeof(fs) / sizeof(*fs); i++) { - EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(fs[i])); - EXPECT_EQ(fs[i], apm_->sample_rate_hz()); - } -} - -TEST_F(ApmTest, Process) { - GOOGLE_PROTOBUF_VERIFY_VERSION; - audio_processing_unittest::OutputData output_data; - - if (global_read_output_data) { - ReadMessageLiteFromFile("output_data.pb", &output_data); - - } else { - // We don't have a file; add the required tests to the protobuf. - int rev_ch[] = {1, 2}; - int ch[] = {1, 2}; - int fs[] = {8000, 16000, 32000}; - for (size_t i = 0; i < sizeof(rev_ch) / sizeof(*rev_ch); i++) { - for (size_t j = 0; j < sizeof(ch) / sizeof(*ch); j++) { - for (size_t k = 0; k < sizeof(fs) / sizeof(*fs); k++) { - audio_processing_unittest::Test* test = output_data.add_test(); - test->set_numreversechannels(rev_ch[i]); - test->set_numchannels(ch[j]); - test->set_samplerate(fs[k]); - } - } - } - } - - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_drift_compensation(true)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_metrics(true)); - EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_analog_level_limits(0, 255)); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->high_pass_filter()->Enable(true)); - - //EXPECT_EQ(apm_->kNoError, - // apm_->level_estimator()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->noise_suppression()->Enable(true)); - - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->Enable(true)); - - for (int i = 0; i < output_data.test_size(); i++) { - printf("Running test %d of %d...\n", i + 1, output_data.test_size()); - - audio_processing_unittest::Test* test = output_data.mutable_test(i); - const int num_samples = test->samplerate() / 100; - revframe_->_payloadDataLengthInSamples = num_samples; - revframe_->_audioChannel = test->numreversechannels(); - revframe_->_frequencyInHz = test->samplerate(); - frame_->_payloadDataLengthInSamples = num_samples; - frame_->_audioChannel = test->numchannels(); - frame_->_frequencyInHz = test->samplerate(); - - EXPECT_EQ(apm_->kNoError, apm_->Initialize()); - ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(test->samplerate())); - ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(frame_->_audioChannel, - frame_->_audioChannel)); - ASSERT_EQ(apm_->kNoError, - apm_->set_num_reverse_channels(revframe_->_audioChannel)); - - - int has_echo_count = 0; - int has_voice_count = 0; - int is_saturated_count = 0; - - while (1) { - WebRtc_Word16 temp_data[640]; - int analog_level = 127; - - // Read far-end frame - size_t read_count = fread(temp_data, - sizeof(WebRtc_Word16), - num_samples * 2, - far_file_); - if (read_count != static_cast(num_samples * 2)) { - // Check that the file really ended. - ASSERT_NE(0, feof(far_file_)); - break; // This is expected. - } - - if (revframe_->_audioChannel == 1) { - MixStereoToMono(temp_data, revframe_->_payloadData, - revframe_->_payloadDataLengthInSamples); - } else { - memcpy(revframe_->_payloadData, - &temp_data[0], - sizeof(WebRtc_Word16) * read_count); - } - - EXPECT_EQ(apm_->kNoError, - apm_->AnalyzeReverseStream(revframe_)); - - EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_stream_drift_samples(0)); - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_stream_analog_level(analog_level)); - - // Read near-end frame - read_count = fread(temp_data, - sizeof(WebRtc_Word16), - num_samples * 2, - near_file_); - if (read_count != static_cast(num_samples * 2)) { - // Check that the file really ended. - ASSERT_NE(0, feof(near_file_)); - break; // This is expected. - } - - if (frame_->_audioChannel == 1) { - MixStereoToMono(temp_data, frame_->_payloadData, num_samples); - } else { - memcpy(frame_->_payloadData, - &temp_data[0], - sizeof(WebRtc_Word16) * read_count); - } - - EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); - - if (apm_->echo_cancellation()->stream_has_echo()) { - has_echo_count++; - } - - analog_level = apm_->gain_control()->stream_analog_level(); - if (apm_->gain_control()->stream_is_saturated()) { - is_saturated_count++; - } - if (apm_->voice_detection()->stream_has_voice()) { - has_voice_count++; - } - } - - //<-- Statistics --> - //LevelEstimator::Metrics far_metrics; - //LevelEstimator::Metrics near_metrics; - //EchoCancellation::Metrics echo_metrics; - //LevelEstimator::Metrics far_metrics_ref_; - //LevelEstimator::Metrics near_metrics_ref_; - //EchoCancellation::Metrics echo_metrics_ref_; - //EXPECT_EQ(apm_->kNoError, - // apm_->echo_cancellation()->GetMetrics(&echo_metrics)); - //EXPECT_EQ(apm_->kNoError, - // apm_->level_estimator()->GetMetrics(&near_metrics, - - // TODO(ajm): check echo metrics and output audio. - if (global_read_output_data) { - EXPECT_EQ(has_echo_count, - test->hasechocount()); - EXPECT_EQ(has_voice_count, - test->hasvoicecount()); - EXPECT_EQ(is_saturated_count, - test->issaturatedcount()); - } else { - test->set_hasechocount(has_echo_count); - test->set_hasvoicecount(has_voice_count); - test->set_issaturatedcount(is_saturated_count); - } - - rewind(far_file_); - rewind(near_file_); - } - - if (!global_read_output_data) { - WriteMessageLiteToFile("output_data.pb", output_data); - } - - google::protobuf::ShutdownProtobufLibrary(); -} - -TEST_F(ApmTest, EchoCancellation) { - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_drift_compensation(true)); - EXPECT_TRUE(apm_->echo_cancellation()->is_drift_compensation_enabled()); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_drift_compensation(false)); - EXPECT_FALSE(apm_->echo_cancellation()->is_drift_compensation_enabled()); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_device_sample_rate_hz(4000)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_device_sample_rate_hz(100000)); - - int rate[] = {16000, 44100, 48000}; - for (size_t i = 0; i < sizeof(rate)/sizeof(*rate); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_device_sample_rate_hz(rate[i])); - EXPECT_EQ(rate[i], - apm_->echo_cancellation()->device_sample_rate_hz()); - } - - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_suppression_level( - static_cast(-1))); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_cancellation()->set_suppression_level( - static_cast(4))); - - EchoCancellation::SuppressionLevel level[] = { - EchoCancellation::kLowSuppression, - EchoCancellation::kModerateSuppression, - EchoCancellation::kHighSuppression, - }; - for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->set_suppression_level(level[i])); - EXPECT_EQ(level[i], - apm_->echo_cancellation()->suppression_level()); - } - - EchoCancellation::Metrics metrics; - EXPECT_EQ(apm_->kNotEnabledError, - apm_->echo_cancellation()->GetMetrics(&metrics)); - - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_metrics(true)); - EXPECT_TRUE(apm_->echo_cancellation()->are_metrics_enabled()); - EXPECT_EQ(apm_->kNoError, - apm_->echo_cancellation()->enable_metrics(false)); - EXPECT_FALSE(apm_->echo_cancellation()->are_metrics_enabled()); - - EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); - EXPECT_TRUE(apm_->echo_cancellation()->is_enabled()); - EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false)); - EXPECT_FALSE(apm_->echo_cancellation()->is_enabled()); -} - -TEST_F(ApmTest, EchoControlMobile) { - // AECM won't use super-wideband. - EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000)); - EXPECT_EQ(apm_->kBadSampleRateError, apm_->echo_control_mobile()->Enable(true)); - EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000)); - // Turn AECM on (and AEC off) - EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); - EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled()); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_control_mobile()->set_routing_mode( - static_cast(-1))); - EXPECT_EQ(apm_->kBadParameterError, - apm_->echo_control_mobile()->set_routing_mode( - static_cast(5))); - - // Toggle routing modes - EchoControlMobile::RoutingMode mode[] = { - EchoControlMobile::kQuietEarpieceOrHeadset, - EchoControlMobile::kEarpiece, - EchoControlMobile::kLoudEarpiece, - EchoControlMobile::kSpeakerphone, - EchoControlMobile::kLoudSpeakerphone, - }; - for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->echo_control_mobile()->set_routing_mode(mode[i])); - EXPECT_EQ(mode[i], - apm_->echo_control_mobile()->routing_mode()); - } - // Turn comfort noise off/on - EXPECT_EQ(apm_->kNoError, - apm_->echo_control_mobile()->enable_comfort_noise(false)); - EXPECT_FALSE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); - EXPECT_EQ(apm_->kNoError, - apm_->echo_control_mobile()->enable_comfort_noise(true)); - EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); - // Turn AECM off - EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false)); - EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled()); -} - -TEST_F(ApmTest, GainControl) { - // Testing gain modes - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_mode(static_cast(-1))); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_mode(static_cast(3))); - - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_mode( - apm_->gain_control()->mode())); - - GainControl::Mode mode[] = { - GainControl::kAdaptiveAnalog, - GainControl::kAdaptiveDigital, - GainControl::kFixedDigital - }; - for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_mode(mode[i])); - EXPECT_EQ(mode[i], apm_->gain_control()->mode()); - } - // Testing invalid target levels - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_target_level_dbfs(-3)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_target_level_dbfs(-40)); - // Testing valid target levels - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_target_level_dbfs( - apm_->gain_control()->target_level_dbfs())); - - int level_dbfs[] = {0, 6, 31}; - for (size_t i = 0; i < sizeof(level_dbfs)/sizeof(*level_dbfs); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_target_level_dbfs(level_dbfs[i])); - EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs()); - } - - // Testing invalid compression gains - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_compression_gain_db(-1)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_compression_gain_db(100)); - - // Testing valid compression gains - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_compression_gain_db( - apm_->gain_control()->compression_gain_db())); - - int gain_db[] = {0, 10, 90}; - for (size_t i = 0; i < sizeof(gain_db)/sizeof(*gain_db); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_compression_gain_db(gain_db[i])); - EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db()); - } - - // Testing limiter off/on - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(false)); - EXPECT_FALSE(apm_->gain_control()->is_limiter_enabled()); - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true)); - EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled()); - - // Testing invalid level limits - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(-1, 512)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(100000, 512)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, -1)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, 100000)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, 255)); - - // Testing valid level limits - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_analog_level_limits( - apm_->gain_control()->analog_level_minimum(), - apm_->gain_control()->analog_level_maximum())); - - int min_level[] = {0, 255, 1024}; - for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_analog_level_limits(min_level[i], 1024)); - EXPECT_EQ(min_level[i], apm_->gain_control()->analog_level_minimum()); - } - - int max_level[] = {0, 1024, 65535}; - for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->gain_control()->set_analog_level_limits(0, max_level[i])); - EXPECT_EQ(max_level[i], apm_->gain_control()->analog_level_maximum()); - } - - // TODO(ajm): stream_is_saturated() and stream_analog_level() - - // Turn AGC off - EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); - EXPECT_FALSE(apm_->gain_control()->is_enabled()); -} - -TEST_F(ApmTest, NoiseSuppression) { - // Tesing invalid suppression levels - EXPECT_EQ(apm_->kBadParameterError, - apm_->noise_suppression()->set_level( - static_cast(-1))); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->noise_suppression()->set_level( - static_cast(5))); - - // Tesing valid suppression levels - NoiseSuppression::Level level[] = { - NoiseSuppression::kLow, - NoiseSuppression::kModerate, - NoiseSuppression::kHigh, - NoiseSuppression::kVeryHigh - }; - for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->noise_suppression()->set_level(level[i])); - EXPECT_EQ(level[i], apm_->noise_suppression()->level()); - } - - // Turing NS on/off - EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); - EXPECT_TRUE(apm_->noise_suppression()->is_enabled()); - EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(false)); - EXPECT_FALSE(apm_->noise_suppression()->is_enabled()); -} - -TEST_F(ApmTest, HighPassFilter) { - // Turing HP filter on/off - EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true)); - EXPECT_TRUE(apm_->high_pass_filter()->is_enabled()); - EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(false)); - EXPECT_FALSE(apm_->high_pass_filter()->is_enabled()); -} - -TEST_F(ApmTest, LevelEstimator) { - // Turing Level estimator on/off - EXPECT_EQ(apm_->kUnsupportedComponentError, - apm_->level_estimator()->Enable(true)); - EXPECT_FALSE(apm_->level_estimator()->is_enabled()); - EXPECT_EQ(apm_->kUnsupportedComponentError, - apm_->level_estimator()->Enable(false)); - EXPECT_FALSE(apm_->level_estimator()->is_enabled()); -} - -TEST_F(ApmTest, VoiceDetection) { - // Test external VAD - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->set_stream_has_voice(true)); - EXPECT_TRUE(apm_->voice_detection()->stream_has_voice()); - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->set_stream_has_voice(false)); - EXPECT_FALSE(apm_->voice_detection()->stream_has_voice()); - - // Tesing invalid likelihoods - EXPECT_EQ(apm_->kBadParameterError, - apm_->voice_detection()->set_likelihood( - static_cast(-1))); - - EXPECT_EQ(apm_->kBadParameterError, - apm_->voice_detection()->set_likelihood( - static_cast(5))); - - // Tesing valid likelihoods - VoiceDetection::Likelihood likelihood[] = { - VoiceDetection::kVeryLowLikelihood, - VoiceDetection::kLowLikelihood, - VoiceDetection::kModerateLikelihood, - VoiceDetection::kHighLikelihood - }; - for (size_t i = 0; i < sizeof(likelihood)/sizeof(*likelihood); i++) { - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->set_likelihood(likelihood[i])); - EXPECT_EQ(likelihood[i], apm_->voice_detection()->likelihood()); - } - - /* TODO(bjornv): Enable once VAD supports other frame lengths than 10 ms - // Tesing invalid frame sizes - EXPECT_EQ(apm_->kBadParameterError, - apm_->voice_detection()->set_frame_size_ms(12)); - - // Tesing valid frame sizes - for (int i = 10; i <= 30; i += 10) { - EXPECT_EQ(apm_->kNoError, - apm_->voice_detection()->set_frame_size_ms(i)); - EXPECT_EQ(i, apm_->voice_detection()->frame_size_ms()); - } - */ - - // Turing VAD on/off - EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); - EXPECT_TRUE(apm_->voice_detection()->is_enabled()); - EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); - EXPECT_FALSE(apm_->voice_detection()->is_enabled()); - - // TODO(bjornv): Add tests for streamed voice; stream_has_voice() -} - -// Below are some ideas for tests from VPM. - -/*TEST_F(VideoProcessingModuleTest, GetVersionTest) -{ -} - -TEST_F(VideoProcessingModuleTest, HandleNullBuffer) -{ -} - -TEST_F(VideoProcessingModuleTest, HandleBadSize) -{ -} - -TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset) -{ -} -*/ -} // namespace - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - ApmEnvironment* env = new ApmEnvironment; // GTest takes ownership. - ::testing::AddGlobalTestEnvironment(env); - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--write_output_data") == 0) { - global_read_output_data = false; - } - } - - return RUN_ALL_TESTS(); -} diff --git a/modules/audio_processing/ns/main/interface/noise_suppression.h b/modules/audio_processing/ns/main/interface/noise_suppression.h deleted file mode 100644 index b8983b077..000000000 --- a/modules/audio_processing/ns/main/interface/noise_suppression.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_ - -#include "typedefs.h" - -typedef struct NsHandleT NsHandle; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This function returns the version number of the code. - * - * Input: - * - version : Pointer to a character array where the version - * info is stored. - * - length : Length of version. - * - * Return value : 0 - Ok - * -1 - Error (probably length is not sufficient) - */ -int WebRtcNs_get_version(char *version, short length); - - -/* - * This function creates an instance to the noise reduction structure - * - * Input: - * - NS_inst : Pointer to noise reduction instance that should be - * created - * - * Output: - * - NS_inst : Pointer to created noise reduction instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_Create(NsHandle **NS_inst); - - -/* - * This function frees the dynamic memory of a specified Noise Reduction - * instance. - * - * Input: - * - NS_inst : Pointer to NS instance that should be freed - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_Free(NsHandle *NS_inst); - - -/* - * This function initializes a NS instance - * - * Input: - * - NS_inst : Instance that should be initialized - * - fs : sampling frequency - * - * Output: - * - NS_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_Init(NsHandle *NS_inst, WebRtc_UWord32 fs); - -/* - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - NS_inst : Instance that should be initialized - * - mode : 0: Mild, 1: Medium , 2: Aggressive - * - * Output: - * - NS_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_set_policy(NsHandle *NS_inst, int mode); - - -/* - * This functions does Noise Suppression for the inserted speech frame. The - * input and output signals should always be 10ms (80 or 160 samples). - * - * Input - * - NS_inst : VAD Instance. Needs to be initiated before call. - * - spframe : Pointer to speech frame buffer for L band - * - spframe_H : Pointer to speech frame buffer for H band - * - fs : sampling frequency - * - * Output: - * - NS_inst : Updated NS instance - * - outframe : Pointer to output frame for L band - * - outframe_H : Pointer to output frame for H band - * - * Return value : 0 - OK - * -1 - Error - */ -int WebRtcNs_Process(NsHandle *NS_inst, - short *spframe, - short *spframe_H, - short *outframe, - short *outframe_H); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_H_ diff --git a/modules/audio_processing/ns/main/interface/noise_suppression_x.h b/modules/audio_processing/ns/main/interface/noise_suppression_x.h deleted file mode 100644 index 35fea2f02..000000000 --- a/modules/audio_processing/ns/main/interface/noise_suppression_x.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_ - -#include "signal_processing_library.h" - -typedef struct NsxHandleT NsxHandle; - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * This function returns the version number of the code. - * - * Input: - * - version : Pointer to a character array where the version - * info is stored. - * - length : Length of version. - * - * Return value : 0 - Ok - * -1 - Error (probably length is not sufficient) - */ -int WebRtcNsx_get_version(char *version, short length); - - -/* - * This function creates an instance to the noise reduction structure - * - * Input: - * - nsxInst : Pointer to noise reduction instance that should be - * created - * - * Output: - * - nsxInst : Pointer to created noise reduction instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_Create(NsxHandle **nsxInst); - - -/* - * This function frees the dynamic memory of a specified Noise Suppression - * instance. - * - * Input: - * - nsxInst : Pointer to NS instance that should be freed - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_Free(NsxHandle *nsxInst); - - -/* - * This function initializes a NS instance - * - * Input: - * - nsxInst : Instance that should be initialized - * - fs : sampling frequency - * - * Output: - * - nsxInst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_Init(NsxHandle *nsxInst, WebRtc_UWord32 fs); - -/* - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - nsxInst : Instance that should be initialized - * - mode : 0: Mild, 1: Medium , 2: Aggressive - * - * Output: - * - nsxInst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_set_policy(NsxHandle *nsxInst, int mode); - -/* - * This functions does noise suppression for the inserted speech frame. The - * input and output signals should always be 10ms (80 or 160 samples). - * - * Input - * - nsxInst : NSx instance. Needs to be initiated before call. - * - speechFrame : Pointer to speech frame buffer for L band - * - speechFrameHB : Pointer to speech frame buffer for H band - * - fs : sampling frequency - * - * Output: - * - nsxInst : Updated NSx instance - * - outFrame : Pointer to output frame for L band - * - outFrameHB : Pointer to output frame for H band - * - * Return value : 0 - OK - * -1 - Error - */ -int WebRtcNsx_Process(NsxHandle *nsxInst, - short *speechFrame, - short *speechFrameHB, - short *outFrame, - short *outFrameHB); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_INTERFACE_NOISE_SUPPRESSION_X_H_ diff --git a/modules/audio_processing/ns/main/source/Android.mk b/modules/audio_processing/ns/main/source/Android.mk deleted file mode 100644 index f7444d52e..000000000 --- a/modules/audio_processing/ns/main/source/Android.mk +++ /dev/null @@ -1,50 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_ns -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := \ - noise_suppression_x.c \ - nsx_core.c - -# floating point -# noise_suppression.c ns_core.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../utility \ - $(LOCAL_PATH)/../../../../../common_audio/signal_processing_library/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_processing/ns/main/source/defines.h b/modules/audio_processing/ns/main/source/defines.h deleted file mode 100644 index d25396793..000000000 --- a/modules/audio_processing/ns/main/source/defines.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ - -//#define PROCESS_FLOW_0 // Use the traditional method. -//#define PROCESS_FLOW_1 // Use traditional with DD estimate of prior SNR. -#define PROCESS_FLOW_2 // Use the new method of speech/noise classification. - -#define BLOCKL_MAX 160 // max processing block length: 160 -#define ANAL_BLOCKL_MAX 256 // max analysis block length: 256 -#define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1 - -#define QUANTILE (float)0.25 - -#define SIMULT 3 -#define END_STARTUP_LONG 200 -#define END_STARTUP_SHORT 50 -#define FACTOR (float)40.0 -#define WIDTH (float)0.01 - -#define SMOOTH (float)0.75 // filter smoothing -// Length of fft work arrays. -#define IP_LENGTH (ANAL_BLOCKL_MAX >> 1) // must be at least ceil(2 + sqrt(ANAL_BLOCKL_MAX/2)) -#define W_LENGTH (ANAL_BLOCKL_MAX >> 1) - -//PARAMETERS FOR NEW METHOD -#define DD_PR_SNR (float)0.98 // DD update of prior SNR -#define LRT_TAVG (float)0.50 // tavg parameter for LRT (previously 0.90) -#define SPECT_FL_TAVG (float)0.30 // tavg parameter for spectral flatness measure -#define SPECT_DIFF_TAVG (float)0.30 // tavg parameter for spectral difference measure -#define PRIOR_UPDATE (float)0.10 // update parameter of prior model -#define NOISE_UPDATE (float)0.90 // update parameter for noise -#define SPEECH_UPDATE (float)0.99 // update parameter when likely speech -#define WIDTH_PR_MAP (float)4.0 // width parameter in sigmoid map for prior model -#define LRT_FEATURE_THR (float)0.5 // default threshold for LRT feature -#define SF_FEATURE_THR (float)0.5 // default threshold for Spectral Flatness feature -#define SD_FEATURE_THR (float)0.5 // default threshold for Spectral Difference feature -#define PROB_RANGE (float)0.20 // probability threshold for noise state in - // speech/noise likelihood -#define HIST_PAR_EST 1000 // histogram size for estimation of parameters -#define GAMMA_PAUSE (float)0.05 // update for conservative noise estimate -// -#define B_LIM (float)0.5 // threshold in final energy gain factor calculation -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_DEFINES_H_ diff --git a/modules/audio_processing/ns/main/source/noise_suppression.c b/modules/audio_processing/ns/main/source/noise_suppression.c deleted file mode 100644 index aed10b146..000000000 --- a/modules/audio_processing/ns/main/source/noise_suppression.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "noise_suppression.h" -#include "ns_core.h" -#include "defines.h" - -int WebRtcNs_get_version(char *versionStr, short length) -{ - const char version[] = "NS 2.2.0"; - const short versionLen = (short)strlen(version) + 1; // +1 for null-termination - - if (versionStr == NULL) { - return -1; - } - - if (versionLen > length) { - return -1; - } - - strncpy(versionStr, version, versionLen); - - return 0; -} - -int WebRtcNs_Create(NsHandle **NS_inst) -{ - *NS_inst = (NsHandle*) malloc(sizeof(NSinst_t)); - if (*NS_inst!=NULL) { - (*(NSinst_t**)NS_inst)->initFlag=0; - return 0; - } else { - return -1; - } - -} - -int WebRtcNs_Free(NsHandle *NS_inst) -{ - free(NS_inst); - return 0; -} - - -int WebRtcNs_Init(NsHandle *NS_inst, WebRtc_UWord32 fs) -{ - return WebRtcNs_InitCore((NSinst_t*) NS_inst, fs); -} - -int WebRtcNs_set_policy(NsHandle *NS_inst, int mode) -{ - return WebRtcNs_set_policy_core((NSinst_t*) NS_inst, mode); -} - - -int WebRtcNs_Process(NsHandle *NS_inst, short *spframe, short *spframe_H, short *outframe, short *outframe_H) -{ - return WebRtcNs_ProcessCore((NSinst_t*) NS_inst, spframe, spframe_H, outframe, outframe_H); -} diff --git a/modules/audio_processing/ns/main/source/noise_suppression_x.c b/modules/audio_processing/ns/main/source/noise_suppression_x.c deleted file mode 100644 index f1ad73061..000000000 --- a/modules/audio_processing/ns/main/source/noise_suppression_x.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "noise_suppression_x.h" -#include "nsx_core.h" -#include "nsx_defines.h" - -int WebRtcNsx_get_version(char *versionStr, short length) -{ - const char version[] = "NS\t3.1.0"; - const short versionLen = (short)strlen(version) + 1; // +1 for null-termination - - if (versionStr == NULL) - { - return -1; - } - - if (versionLen > length) - { - return -1; - } - - strncpy(versionStr, version, versionLen); - - return 0; -} - -int WebRtcNsx_Create(NsxHandle **nsxInst) -{ - *nsxInst = (NsxHandle*)malloc(sizeof(NsxInst_t)); - if (*nsxInst != NULL) - { - (*(NsxInst_t**)nsxInst)->initFlag = 0; - return 0; - } else - { - return -1; - } - -} - -int WebRtcNsx_Free(NsxHandle *nsxInst) -{ - free(nsxInst); - return 0; -} - -int WebRtcNsx_Init(NsxHandle *nsxInst, WebRtc_UWord32 fs) -{ - return WebRtcNsx_InitCore((NsxInst_t*)nsxInst, fs); -} - -int WebRtcNsx_set_policy(NsxHandle *nsxInst, int mode) -{ - return WebRtcNsx_set_policy_core((NsxInst_t*)nsxInst, mode); -} - -int WebRtcNsx_Process(NsxHandle *nsxInst, short *speechFrame, short *speechFrameHB, - short *outFrame, short *outFrameHB) -{ - return WebRtcNsx_ProcessCore((NsxInst_t*)nsxInst, speechFrame, speechFrameHB, outFrame, - outFrameHB); -} - diff --git a/modules/audio_processing/ns/main/source/ns.gyp b/modules/audio_processing/ns/main/source/ns.gyp deleted file mode 100644 index c8488b27e..000000000 --- a/modules/audio_processing/ns/main/source/ns.gyp +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'ns', - 'type': '<(library)', - 'dependencies': [ - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../utility/util.gyp:apm_util' - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/noise_suppression.h', - 'noise_suppression.c', - 'windows_private.h', - 'defines.h', - 'ns_core.c', - 'ns_core.h', - ], - }, - { - 'target_name': 'ns_fix', - 'type': '<(library)', - 'dependencies': [ - '../../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - '../interface/noise_suppression_x.h', - 'noise_suppression_x.c', - 'nsx_defines.h', - 'nsx_core.c', - 'nsx_core.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/audio_processing/ns/main/source/ns_core.c b/modules/audio_processing/ns/main/source/ns_core.c deleted file mode 100644 index 20425e74d..000000000 --- a/modules/audio_processing/ns/main/source/ns_core.c +++ /dev/null @@ -1,1500 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -//#include -#include -#include "noise_suppression.h" -#include "ns_core.h" -#include "windows_private.h" -#include "fft4g.h" -#include "signal_processing_library.h" - -// Set Feature Extraction Parameters -void WebRtcNs_set_feature_extraction_parameters(NSinst_t *inst) -{ - //bin size of histogram - inst->featureExtractionParams.binSizeLrt = (float)0.1; - inst->featureExtractionParams.binSizeSpecFlat = (float)0.05; - inst->featureExtractionParams.binSizeSpecDiff = (float)0.1; - - //range of histogram over which lrt threshold is computed - inst->featureExtractionParams.rangeAvgHistLrt = (float)1.0; - - //scale parameters: multiply dominant peaks of the histograms by scale factor to obtain - // thresholds for prior model - inst->featureExtractionParams.factor1ModelPars = (float)1.20; //for lrt and spectral diff - inst->featureExtractionParams.factor2ModelPars = (float)0.9; //for spectral_flatness: - // used when noise is flatter than speech - - //peak limit for spectral flatness (varies between 0 and 1) - inst->featureExtractionParams.thresPosSpecFlat = (float)0.6; - - //limit on spacing of two highest peaks in histogram: spacing determined by bin size - inst->featureExtractionParams.limitPeakSpacingSpecFlat = 2 - * inst->featureExtractionParams.binSizeSpecFlat; - inst->featureExtractionParams.limitPeakSpacingSpecDiff = 2 - * inst->featureExtractionParams.binSizeSpecDiff; - - //limit on relevance of second peak: - inst->featureExtractionParams.limitPeakWeightsSpecFlat = (float)0.5; - inst->featureExtractionParams.limitPeakWeightsSpecDiff = (float)0.5; - - // fluctuation limit of lrt feature - inst->featureExtractionParams.thresFluctLrt = (float)0.05; - - //limit on the max and min values for the feature thresholds - inst->featureExtractionParams.maxLrt = (float)1.0; - inst->featureExtractionParams.minLrt = (float)0.20; - - inst->featureExtractionParams.maxSpecFlat = (float)0.95; - inst->featureExtractionParams.minSpecFlat = (float)0.10; - - inst->featureExtractionParams.maxSpecDiff = (float)1.0; - inst->featureExtractionParams.minSpecDiff = (float)0.16; - - //criteria of weight of histogram peak to accept/reject feature - inst->featureExtractionParams.thresWeightSpecFlat = (int)(0.3 - * (inst->modelUpdatePars[1])); //for spectral flatness - inst->featureExtractionParams.thresWeightSpecDiff = (int)(0.3 - * (inst->modelUpdatePars[1])); //for spectral difference -} - -// Initialize state -int WebRtcNs_InitCore(NSinst_t *inst, WebRtc_UWord32 fs) -{ - int i; - //We only support 10ms frames - - //check for valid pointer - if (inst == NULL) - { - return -1; - } - - // Initialization of struct - if (fs == 8000 || fs == 16000 || fs == 32000) - { - inst->fs = fs; - } - else - { - return -1; - } - inst->windShift = 0; - if (fs == 8000) - { - // We only support 10ms frames - inst->blockLen = 80; - inst->blockLen10ms = 80; - inst->anaLen = 128; - inst->window = kBlocks80w128; - inst->outLen = 0; - } - else if (fs == 16000) - { - // We only support 10ms frames - inst->blockLen = 160; - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->window = kBlocks160w256; - inst->outLen = 0; - } - else if (fs==32000) - { - // We only support 10ms frames - inst->blockLen = 160; - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->window = kBlocks160w256; - inst->outLen = 0; - } - inst->magnLen = inst->anaLen / 2 + 1; // Number of frequency bins - - // Initialize fft work arrays. - inst->ip[0] = 0; // Setting this triggers initialization. - memset(inst->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - rdft(inst->anaLen, 1, inst->dataBuf, inst->ip, inst->wfft); - - memset(inst->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - memset(inst->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); - - //for HB processing - memset(inst->dataBufHB, 0, sizeof(float) * ANAL_BLOCKL_MAX); - - //for quantile noise estimation - memset(inst->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL); - for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) - { - inst->lquantile[i] = (float)8.0; - inst->density[i] = (float)0.3; - } - - for (i = 0; i < SIMULT; i++) - { - inst->counter[i] = (int)floor((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT); - } - - inst->updates = 0; - - // Wiener filter initialization - for (i = 0; i < HALF_ANAL_BLOCKL; i++) - { - inst->smooth[i] = (float)1.0; - } - - // Set the aggressiveness: default - inst->aggrMode = 0; - - //initialize variables for new method - inst->priorSpeechProb = (float)0.5; //prior prob for speech/noise - for (i = 0; i < HALF_ANAL_BLOCKL; i++) - { - inst->magnPrev[i] = (float)0.0; //previous mag spectrum - inst->noisePrev[i] = (float)0.0; //previous noise-spectrum - inst->logLrtTimeAvg[i] = LRT_FEATURE_THR; //smooth LR ratio (same as threshold) - inst->magnAvgPause[i] = (float)0.0; //conservative noise spectrum estimate - inst->speechProbHB[i] = (float)0.0; //for estimation of HB in second pass - inst->initMagnEst[i] = (float)0.0; //initial average mag spectrum - } - - //feature quantities - inst->featureData[0] = SF_FEATURE_THR; //spectral flatness (start on threshold) - inst->featureData[1] = (float)0.0; //spectral entropy: not used in this version - inst->featureData[2] = (float)0.0; //spectral variance: not used in this version - inst->featureData[3] = LRT_FEATURE_THR; //average lrt factor (start on threshold) - inst->featureData[4] = SF_FEATURE_THR; //spectral template diff (start on threshold) - inst->featureData[5] = (float)0.0; //normalization for spectral-diff - inst->featureData[6] = (float)0.0; //window time-average of input magnitude spectrum - - //histogram quantities: used to estimate/update thresholds for features - for (i = 0; i < HIST_PAR_EST; i++) - { - inst->histLrt[i] = 0; - inst->histSpecFlat[i] = 0; - inst->histSpecDiff[i] = 0; - } - - inst->blockInd = -1; //frame counter - inst->priorModelPars[0] = LRT_FEATURE_THR; //default threshold for lrt feature - inst->priorModelPars[1] = (float)0.5; //threshold for spectral flatness: - // determined on-line - inst->priorModelPars[2] = (float)1.0; //sgn_map par for spectral measure: - // 1 for flatness measure - inst->priorModelPars[3] = (float)0.5; //threshold for template-difference feature: - // determined on-line - inst->priorModelPars[4] = (float)1.0; //default weighting parameter for lrt feature - inst->priorModelPars[5] = (float)0.0; //default weighting parameter for - // spectral flatness feature - inst->priorModelPars[6] = (float)0.0; //default weighting parameter for - // spectral difference feature - - inst->modelUpdatePars[0] = 2; //update flag for parameters: - // 0 no update, 1=update once, 2=update every window - inst->modelUpdatePars[1] = 500; //window for update - inst->modelUpdatePars[2] = 0; //counter for update of conservative noise spectrum - //counter if the feature thresholds are updated during the sequence - inst->modelUpdatePars[3] = inst->modelUpdatePars[1]; - - inst->signalEnergy = 0.0; - inst->sumMagn = 0.0; - inst->whiteNoiseLevel = 0.0; - inst->pinkNoiseNumerator = 0.0; - inst->pinkNoiseExp = 0.0; - - WebRtcNs_set_feature_extraction_parameters(inst); // Set feature configuration - - //default mode - WebRtcNs_set_policy_core(inst, 0); - - - memset(inst->outBuf, 0, sizeof(float) * 3 * BLOCKL_MAX); - - inst->initFlag = 1; - return 0; -} - -int WebRtcNs_set_policy_core(NSinst_t *inst, int mode) -{ - // allow for modes:0,1,2,3 - if (mode < 0 || mode > 3) - { - return (-1); - } - - inst->aggrMode = mode; - if (mode == 0) - { - inst->overdrive = (float)1.0; - inst->denoiseBound = (float)0.5; - inst->gainmap = 0; - } - else if (mode == 1) - { - //inst->overdrive = (float)1.25; - inst->overdrive = (float)1.0; - inst->denoiseBound = (float)0.25; - inst->gainmap = 1; - } - else if (mode == 2) - { - //inst->overdrive = (float)1.25; - inst->overdrive = (float)1.1; - inst->denoiseBound = (float)0.125; - inst->gainmap = 1; - } - else if (mode == 3) - { - //inst->overdrive = (float)1.30; - inst->overdrive = (float)1.25; - inst->denoiseBound = (float)0.09; - inst->gainmap = 1; - } - return 0; -} - -// Estimate noise -void WebRtcNs_NoiseEstimation(NSinst_t *inst, float *magn, float *noise) -{ - int i, s, offset; - float lmagn[HALF_ANAL_BLOCKL], delta; - - if (inst->updates < END_STARTUP_LONG) - { - inst->updates++; - } - - for (i = 0; i < inst->magnLen; i++) - { - lmagn[i] = (float)log(magn[i]); - } - - // loop over simultaneous estimates - for (s = 0; s < SIMULT; s++) - { - offset = s * inst->magnLen; - - // newquantest(...) - for (i = 0; i < inst->magnLen; i++) - { - // compute delta - if (inst->density[offset + i] > 1.0) - { - delta = FACTOR * (float)1.0 / inst->density[offset + i]; - } - else - { - delta = FACTOR; - } - - // update log quantile estimate - if (lmagn[i] > inst->lquantile[offset + i]) - { - inst->lquantile[offset + i] += QUANTILE * delta - / (float)(inst->counter[s] + 1); - } - else - { - inst->lquantile[offset + i] -= ((float)1.0 - QUANTILE) * delta - / (float)(inst->counter[s] + 1); - } - - // update density estimate - if (fabs(lmagn[i] - inst->lquantile[offset + i]) < WIDTH) - { - inst->density[offset + i] = ((float)inst->counter[s] * inst->density[offset - + i] + (float)1.0 / ((float)2.0 * WIDTH)) / (float)(inst->counter[s] - + 1); - } - } // end loop over magnitude spectrum - - if (inst->counter[s] >= END_STARTUP_LONG) - { - inst->counter[s] = 0; - if (inst->updates >= END_STARTUP_LONG) - { - for (i = 0; i < inst->magnLen; i++) - { - inst->quantile[i] = (float)exp(inst->lquantile[offset + i]); - } - } - } - - inst->counter[s]++; - } // end loop over simultaneous estimates - - // Sequentially update the noise during startup - if (inst->updates < END_STARTUP_LONG) - { - // Use the last "s" to get noise during startup that differ from zero. - for (i = 0; i < inst->magnLen; i++) - { - inst->quantile[i] = (float)exp(inst->lquantile[offset + i]); - } - } - - for (i = 0; i < inst->magnLen; i++) - { - noise[i] = inst->quantile[i]; - } -} - -// Extract thresholds for feature parameters -// histograms are computed over some window_size (given by inst->modelUpdatePars[1]) -// thresholds and weights are extracted every window -// flag 0 means update histogram only, flag 1 means compute the thresholds/weights -// threshold and weights are returned in: inst->priorModelPars -void WebRtcNs_FeatureParameterExtraction(NSinst_t *inst, int flag) -{ - int i, useFeatureSpecFlat, useFeatureSpecDiff, numHistLrt; - int maxPeak1, maxPeak2; - int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff, weightPeak2SpecDiff; - - float binMid, featureSum; - float posPeak1SpecFlat, posPeak2SpecFlat, posPeak1SpecDiff, posPeak2SpecDiff; - float fluctLrt, avgHistLrt, avgSquareHistLrt, avgHistLrtCompl; - - //3 features: lrt, flatness, difference - //lrt_feature = inst->featureData[3]; - //flat_feature = inst->featureData[0]; - //diff_feature = inst->featureData[4]; - - //update histograms - if (flag == 0) - { - // LRT - if ((inst->featureData[3] < HIST_PAR_EST * inst->featureExtractionParams.binSizeLrt) - && (inst->featureData[3] >= 0.0)) - { - i = (int)(inst->featureData[3] / inst->featureExtractionParams.binSizeLrt); - inst->histLrt[i]++; - } - // Spectral flatness - if ((inst->featureData[0] < HIST_PAR_EST - * inst->featureExtractionParams.binSizeSpecFlat) - && (inst->featureData[0] >= 0.0)) - { - i = (int)(inst->featureData[0] / inst->featureExtractionParams.binSizeSpecFlat); - inst->histSpecFlat[i]++; - } - // Spectral difference - if ((inst->featureData[4] < HIST_PAR_EST - * inst->featureExtractionParams.binSizeSpecDiff) - && (inst->featureData[4] >= 0.0)) - { - i = (int)(inst->featureData[4] / inst->featureExtractionParams.binSizeSpecDiff); - inst->histSpecDiff[i]++; - } - } - - // extract parameters for speech/noise probability - if (flag == 1) - { - //lrt feature: compute the average over inst->featureExtractionParams.rangeAvgHistLrt - avgHistLrt = 0.0; - avgHistLrtCompl = 0.0; - avgSquareHistLrt = 0.0; - numHistLrt = 0; - for (i = 0; i < HIST_PAR_EST; i++) - { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeLrt; - if (binMid <= inst->featureExtractionParams.rangeAvgHistLrt) - { - avgHistLrt += inst->histLrt[i] * binMid; - numHistLrt += inst->histLrt[i]; - } - avgSquareHistLrt += inst->histLrt[i] * binMid * binMid; - avgHistLrtCompl += inst->histLrt[i] * binMid; - } - if (numHistLrt > 0) - { - avgHistLrt = avgHistLrt / ((float)numHistLrt); - } - avgHistLrtCompl = avgHistLrtCompl / ((float)inst->modelUpdatePars[1]); - avgSquareHistLrt = avgSquareHistLrt / ((float)inst->modelUpdatePars[1]); - fluctLrt = avgSquareHistLrt - avgHistLrt * avgHistLrtCompl; - // get threshold for lrt feature: - if (fluctLrt < inst->featureExtractionParams.thresFluctLrt) - { - //very low fluct, so likely noise - inst->priorModelPars[0] = inst->featureExtractionParams.maxLrt; - } - else - { - inst->priorModelPars[0] = inst->featureExtractionParams.factor1ModelPars - * avgHistLrt; - // check if value is within min/max range - if (inst->priorModelPars[0] < inst->featureExtractionParams.minLrt) - { - inst->priorModelPars[0] = inst->featureExtractionParams.minLrt; - } - if (inst->priorModelPars[0] > inst->featureExtractionParams.maxLrt) - { - inst->priorModelPars[0] = inst->featureExtractionParams.maxLrt; - } - } - // done with lrt feature - - // - // for spectral flatness and spectral difference: compute the main peaks of histogram - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecFlat = 0.0; - posPeak2SpecFlat = 0.0; - weightPeak1SpecFlat = 0; - weightPeak2SpecFlat = 0; - - // peaks for flatness - for (i = 0; i < HIST_PAR_EST; i++) - { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeSpecFlat; - if (inst->histSpecFlat[i] > maxPeak1) - { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecFlat = weightPeak1SpecFlat; - posPeak2SpecFlat = posPeak1SpecFlat; - - maxPeak1 = inst->histSpecFlat[i]; - weightPeak1SpecFlat = inst->histSpecFlat[i]; - posPeak1SpecFlat = binMid; - } - else if (inst->histSpecFlat[i] > maxPeak2) - { - // Found new "second" peak - maxPeak2 = inst->histSpecFlat[i]; - weightPeak2SpecFlat = inst->histSpecFlat[i]; - posPeak2SpecFlat = binMid; - } - } - - //compute two peaks for spectral difference - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecDiff = 0.0; - posPeak2SpecDiff = 0.0; - weightPeak1SpecDiff = 0; - weightPeak2SpecDiff = 0; - // peaks for spectral difference - for (i = 0; i < HIST_PAR_EST; i++) - { - binMid = ((float)i + (float)0.5) * inst->featureExtractionParams.binSizeSpecDiff; - if (inst->histSpecDiff[i] > maxPeak1) - { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecDiff = weightPeak1SpecDiff; - posPeak2SpecDiff = posPeak1SpecDiff; - - maxPeak1 = inst->histSpecDiff[i]; - weightPeak1SpecDiff = inst->histSpecDiff[i]; - posPeak1SpecDiff = binMid; - } - else if (inst->histSpecDiff[i] > maxPeak2) - { - // Found new "second" peak - maxPeak2 = inst->histSpecDiff[i]; - weightPeak2SpecDiff = inst->histSpecDiff[i]; - posPeak2SpecDiff = binMid; - } - } - - // for spectrum flatness feature - useFeatureSpecFlat = 1; - // merge the two peaks if they are close - if ((fabs(posPeak2SpecFlat - posPeak1SpecFlat) - < inst->featureExtractionParams.limitPeakSpacingSpecFlat) - && (weightPeak2SpecFlat - > inst->featureExtractionParams.limitPeakWeightsSpecFlat - * weightPeak1SpecFlat)) - { - weightPeak1SpecFlat += weightPeak2SpecFlat; - posPeak1SpecFlat = (float)0.5 * (posPeak1SpecFlat + posPeak2SpecFlat); - } - //reject if weight of peaks is not large enough, or peak value too small - if (weightPeak1SpecFlat < inst->featureExtractionParams.thresWeightSpecFlat - || posPeak1SpecFlat < inst->featureExtractionParams.thresPosSpecFlat) - { - useFeatureSpecFlat = 0; - } - // if selected, get the threshold - if (useFeatureSpecFlat == 1) - { - // compute the threshold - inst->priorModelPars[1] = inst->featureExtractionParams.factor2ModelPars - * posPeak1SpecFlat; - //check if value is within min/max range - if (inst->priorModelPars[1] < inst->featureExtractionParams.minSpecFlat) - { - inst->priorModelPars[1] = inst->featureExtractionParams.minSpecFlat; - } - if (inst->priorModelPars[1] > inst->featureExtractionParams.maxSpecFlat) - { - inst->priorModelPars[1] = inst->featureExtractionParams.maxSpecFlat; - } - } - // done with flatness feature - - // for template feature - useFeatureSpecDiff = 1; - // merge the two peaks if they are close - if ((fabs(posPeak2SpecDiff - posPeak1SpecDiff) - < inst->featureExtractionParams.limitPeakSpacingSpecDiff) - && (weightPeak2SpecDiff - > inst->featureExtractionParams.limitPeakWeightsSpecDiff - * weightPeak1SpecDiff)) - { - weightPeak1SpecDiff += weightPeak2SpecDiff; - posPeak1SpecDiff = (float)0.5 * (posPeak1SpecDiff + posPeak2SpecDiff); - } - // get the threshold value - inst->priorModelPars[3] = inst->featureExtractionParams.factor1ModelPars - * posPeak1SpecDiff; - //reject if weight of peaks is not large enough - if (weightPeak1SpecDiff < inst->featureExtractionParams.thresWeightSpecDiff) - { - useFeatureSpecDiff = 0; - } - //check if value is within min/max range - if (inst->priorModelPars[3] < inst->featureExtractionParams.minSpecDiff) - { - inst->priorModelPars[3] = inst->featureExtractionParams.minSpecDiff; - } - if (inst->priorModelPars[3] > inst->featureExtractionParams.maxSpecDiff) - { - inst->priorModelPars[3] = inst->featureExtractionParams.maxSpecDiff; - } - // done with spectral difference feature - - // don't use template feature if fluctuation of lrt feature is very low: - // most likely just noise state - if (fluctLrt < inst->featureExtractionParams.thresFluctLrt) - { - useFeatureSpecDiff = 0; - } - - // select the weights between the features - // inst->priorModelPars[4] is weight for lrt: always selected - // inst->priorModelPars[5] is weight for spectral flatness - // inst->priorModelPars[6] is weight for spectral difference - featureSum = (float)(1 + useFeatureSpecFlat + useFeatureSpecDiff); - inst->priorModelPars[4] = (float)1.0 / featureSum; - inst->priorModelPars[5] = ((float)useFeatureSpecFlat) / featureSum; - inst->priorModelPars[6] = ((float)useFeatureSpecDiff) / featureSum; - - // set hists to zero for next update - if (inst->modelUpdatePars[0] >= 1) - { - for (i = 0; i < HIST_PAR_EST; i++) - { - inst->histLrt[i] = 0; - inst->histSpecFlat[i] = 0; - inst->histSpecDiff[i] = 0; - } - } - } // end of flag == 1 -} - -// Compute spectral flatness on input spectrum -// magnIn is the magnitude spectrum -// spectral flatness is returned in inst->featureData[0] -void WebRtcNs_ComputeSpectralFlatness(NSinst_t *inst, float *magnIn) -{ - int i; - int shiftLP = 1; //option to remove first bin(s) from spectral measures - float avgSpectralFlatnessNum, avgSpectralFlatnessDen, spectralTmp; - - // comute spectral measures - // for flatness - avgSpectralFlatnessNum = 0.0; - avgSpectralFlatnessDen = inst->sumMagn; - for (i = 0; i < shiftLP; i++) - { - avgSpectralFlatnessDen -= magnIn[i]; - } - // compute log of ratio of the geometric to arithmetic mean: check for log(0) case - for (i = shiftLP; i < inst->magnLen; i++) - { - if (magnIn[i] > 0.0) - { - avgSpectralFlatnessNum += (float)log(magnIn[i]); - } - else - { - inst->featureData[0] -= SPECT_FL_TAVG * inst->featureData[0]; - return; - } - } - //normalize - avgSpectralFlatnessDen = avgSpectralFlatnessDen / inst->magnLen; - avgSpectralFlatnessNum = avgSpectralFlatnessNum / inst->magnLen; - - //ratio and inverse log: check for case of log(0) - spectralTmp = (float)exp(avgSpectralFlatnessNum) / avgSpectralFlatnessDen; - - //time-avg update of spectral flatness feature - inst->featureData[0] += SPECT_FL_TAVG * (spectralTmp - inst->featureData[0]); - // done with flatness feature -} - -// Compute the difference measure between input spectrum and a template/learned noise spectrum -// magnIn is the input spectrum -// the reference/template spectrum is inst->magnAvgPause[i] -// returns (normalized) spectral difference in inst->featureData[4] -void WebRtcNs_ComputeSpectralDifference(NSinst_t *inst, float *magnIn) -{ - // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / var(magnAvgPause) - int i; - float avgPause, avgMagn, covMagnPause, varPause, varMagn, avgDiffNormMagn; - - avgPause = 0.0; - avgMagn = inst->sumMagn; - // compute average quantities - for (i = 0; i < inst->magnLen; i++) - { - //conservative smooth noise spectrum from pause frames - avgPause += inst->magnAvgPause[i]; - } - avgPause = avgPause / ((float)inst->magnLen); - avgMagn = avgMagn / ((float)inst->magnLen); - - covMagnPause = 0.0; - varPause = 0.0; - varMagn = 0.0; - // compute variance and covariance quantities - for (i = 0; i < inst->magnLen; i++) - { - covMagnPause += (magnIn[i] - avgMagn) * (inst->magnAvgPause[i] - avgPause); - varPause += (inst->magnAvgPause[i] - avgPause) * (inst->magnAvgPause[i] - avgPause); - varMagn += (magnIn[i] - avgMagn) * (magnIn[i] - avgMagn); - } - covMagnPause = covMagnPause / ((float)inst->magnLen); - varPause = varPause / ((float)inst->magnLen); - varMagn = varMagn / ((float)inst->magnLen); - // update of average magnitude spectrum - inst->featureData[6] += inst->signalEnergy; - - avgDiffNormMagn = varMagn - (covMagnPause * covMagnPause) / (varPause + (float)0.0001); - // normalize and compute time-avg update of difference feature - avgDiffNormMagn = (float)(avgDiffNormMagn / (inst->featureData[5] + (float)0.0001)); - inst->featureData[4] += SPECT_DIFF_TAVG * (avgDiffNormMagn - inst->featureData[4]); -} - -// Compute speech/noise probability -// speech/noise probability is returned in: probSpeechFinal -//magn is the input magnitude spectrum -//noise is the noise spectrum -//snrLocPrior is the prior snr for each freq. -//snr loc_post is the post snr for each freq. -void WebRtcNs_SpeechNoiseProb(NSinst_t *inst, float *probSpeechFinal, float *snrLocPrior, - float *snrLocPost) -{ - int i, sgnMap; - float invLrt, gainPrior, indPrior; - float logLrtTimeAvgKsum, besselTmp; - float indicator0, indicator1, indicator2; - float tmpFloat1, tmpFloat2; - float weightIndPrior0, weightIndPrior1, weightIndPrior2; - float threshPrior0, threshPrior1, threshPrior2; - float widthPrior, widthPrior0, widthPrior1, widthPrior2; - - widthPrior0 = WIDTH_PR_MAP; - widthPrior1 = (float)2.0 * WIDTH_PR_MAP; //width for pause region: - // lower range, so increase width in tanh map - widthPrior2 = (float)2.0 * WIDTH_PR_MAP; //for spectral-difference measure - - //threshold parameters for features - threshPrior0 = inst->priorModelPars[0]; - threshPrior1 = inst->priorModelPars[1]; - threshPrior2 = inst->priorModelPars[3]; - - //sign for flatness feature - sgnMap = (int)(inst->priorModelPars[2]); - - //weight parameters for features - weightIndPrior0 = inst->priorModelPars[4]; - weightIndPrior1 = inst->priorModelPars[5]; - weightIndPrior2 = inst->priorModelPars[6]; - - // compute feature based on average LR factor - // this is the average over all frequencies of the smooth log lrt - logLrtTimeAvgKsum = 0.0; - for (i = 0; i < inst->magnLen; i++) - { - tmpFloat1 = (float)1.0 + (float)2.0 * snrLocPrior[i]; - tmpFloat2 = (float)2.0 * snrLocPrior[i] / (tmpFloat1 + (float)0.0001); - besselTmp = (snrLocPost[i] + (float)1.0) * tmpFloat2; - inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - (float)log(tmpFloat1) - - inst->logLrtTimeAvg[i]); - logLrtTimeAvgKsum += inst->logLrtTimeAvg[i]; - } - logLrtTimeAvgKsum = (float)logLrtTimeAvgKsum / (inst->magnLen); - inst->featureData[3] = logLrtTimeAvgKsum; - // done with computation of LR factor - - // - //compute the indicator functions - // - - // average lrt feature - widthPrior = widthPrior0; - //use larger width in tanh map for pause regions - if (logLrtTimeAvgKsum < threshPrior0) - { - widthPrior = widthPrior1; - } - // compute indicator function: sigmoid map - indicator0 = (float)0.5 * ((float)tanh(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) - + (float)1.0); - - //spectral flatness feature - tmpFloat1 = inst->featureData[0]; - widthPrior = widthPrior0; - //use larger width in tanh map for pause regions - if (sgnMap == 1 && (tmpFloat1 > threshPrior1)) - { - widthPrior = widthPrior1; - } - if (sgnMap == -1 && (tmpFloat1 < threshPrior1)) - { - widthPrior = widthPrior1; - } - // compute indicator function: sigmoid map - indicator1 = (float)0.5 * ((float)tanh( - (float)sgnMap * widthPrior * (threshPrior1 - - tmpFloat1)) + (float)1.0); - - //for template spectrum-difference - tmpFloat1 = inst->featureData[4]; - widthPrior = widthPrior0; - //use larger width in tanh map for pause regions - if (tmpFloat1 < threshPrior2) - { - widthPrior = widthPrior2; - } - // compute indicator function: sigmoid map - indicator2 = (float)0.5 * ((float)tanh(widthPrior * (tmpFloat1 - threshPrior2)) - + (float)1.0); - - //combine the indicator function with the feature weights - indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 + weightIndPrior2 - * indicator2; - // done with computing indicator function - - //compute the prior probability - inst->priorSpeechProb += PRIOR_UPDATE * (indPrior - inst->priorSpeechProb); - // make sure probabilities are within range: keep floor to 0.01 - if (inst->priorSpeechProb > 1.0) - { - inst->priorSpeechProb = (float)1.0; - } - if (inst->priorSpeechProb < 0.01) - { - inst->priorSpeechProb = (float)0.01; - } - - //final speech probability: combine prior model with LR factor: - gainPrior = ((float)1.0 - inst->priorSpeechProb) / (inst->priorSpeechProb + (float)0.0001); - for (i = 0; i < inst->magnLen; i++) - { - invLrt = (float)exp(-inst->logLrtTimeAvg[i]); - invLrt = (float)gainPrior * invLrt; - probSpeechFinal[i] = (float)1.0 / ((float)1.0 + invLrt); - } -} - -int WebRtcNs_ProcessCore(NSinst_t *inst, - short *speechFrame, - short *speechFrameHB, - short *outFrame, - short *outFrameHB) -{ - // main routine for noise reduction - - int flagHB = 0; - int i; - const int kStartBand = 5; // Skip first frequency bins during estimation. - int updateParsFlag; - - float energy1, energy2, gain, factor, factor1, factor2; - float signalEnergy, sumMagn; - float snrPrior, currentEstimateStsa; - float tmpFloat1, tmpFloat2, tmpFloat3, probSpeech, probNonSpeech; - float gammaNoiseTmp, gammaNoiseOld; - float noiseUpdateTmp, fTmp, dTmp; - float fin[BLOCKL_MAX], fout[BLOCKL_MAX]; - float winData[ANAL_BLOCKL_MAX]; - float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL]; - float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL]; - float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL]; - float probSpeechFinal[HALF_ANAL_BLOCKL], previousEstimateStsa[HALF_ANAL_BLOCKL]; - float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; - // Variables during startup - float sum_log_i = 0.0; - float sum_log_i_square = 0.0; - float sum_log_magn = 0.0; - float sum_log_i_log_magn = 0.0; - float parametric_noise = 0.0; - float parametric_exp = 0.0; - float parametric_num = 0.0; - - // SWB variables - int deltaBweHB = 1; - int deltaGainHB = 1; - float decayBweHB = 1.0; - float gainMapParHB = 1.0; - float gainTimeDomainHB = 1.0; - float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB; - - // Check that initiation has been done - if (inst->initFlag != 1) - { - return (-1); - } - // Check for valid pointers based on sampling rate - if (inst->fs == 32000) - { - if (speechFrameHB == NULL) - { - return -1; - } - flagHB = 1; - // range for averaging low band quantities for H band gain - deltaBweHB = (int)inst->magnLen / 4; - deltaGainHB = deltaBweHB; - } - // - inst->blockInd++; - // - updateParsFlag = inst->modelUpdatePars[0]; - // - - //for LB do all processing - // convert to float - for (i = 0; i < inst->blockLen10ms; i++) - { - fin[i] = (float)speechFrame[i]; - } - // update analysis buffer for L band - memcpy(inst->dataBuf, inst->dataBuf + inst->blockLen10ms, - sizeof(float) * (inst->anaLen - inst->blockLen10ms)); - memcpy(inst->dataBuf + inst->anaLen - inst->blockLen10ms, fin, - sizeof(float) * inst->blockLen10ms); - - if (flagHB == 1) - { - // convert to float - for (i = 0; i < inst->blockLen10ms; i++) - { - fin[i] = (float)speechFrameHB[i]; - } - // update analysis buffer for H band - memcpy(inst->dataBufHB, inst->dataBufHB + inst->blockLen10ms, - sizeof(float) * (inst->anaLen - inst->blockLen10ms)); - memcpy(inst->dataBufHB + inst->anaLen - inst->blockLen10ms, fin, - sizeof(float) * inst->blockLen10ms); - } - - // check if processing needed - if (inst->outLen == 0) - { - // windowing - energy1 = 0.0; - for (i = 0; i < inst->anaLen; i++) - { - winData[i] = inst->window[i] * inst->dataBuf[i]; - energy1 += winData[i] * winData[i]; - } - if (energy1 == 0.0) - { - // synthesize the special case of zero input - // we want to avoid updating statistics in this case: - // Updating feature statistics when we have zeros only will cause thresholds to - // move towards zero signal situations. This in turn has the effect that once the - // signal is "turned on" (non-zero values) everything will be treated as speech - // and there is no noise suppression effect. Depending on the duration of the - // inactive signal it takes a considerable amount of time for the system to learn - // what is noise and what is speech. - - // read out fully processed segment - for (i = inst->windShift; i < inst->blockLen + inst->windShift; i++) - { - fout[i - inst->windShift] = inst->syntBuf[i]; - } - // update synthesis buffer - memcpy(inst->syntBuf, inst->syntBuf + inst->blockLen, - sizeof(float) * (inst->anaLen - inst->blockLen)); - memset(inst->syntBuf + inst->anaLen - inst->blockLen, 0, - sizeof(float) * inst->blockLen); - - // out buffer - inst->outLen = inst->blockLen - inst->blockLen10ms; - if (inst->blockLen > inst->blockLen10ms) - { - for (i = 0; i < inst->outLen; i++) - { - inst->outBuf[i] = fout[i + inst->blockLen10ms]; - } - } - // convert to short - for (i = 0; i < inst->blockLen10ms; i++) - { - dTmp = fout[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) - { - dTmp = WEBRTC_SPL_WORD16_MIN; - } - else if (dTmp > WEBRTC_SPL_WORD16_MAX) - { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrame[i] = (short)dTmp; - } - - // for time-domain gain of HB - if (flagHB == 1) - { - for (i = 0; i < inst->blockLen10ms; i++) - { - dTmp = inst->dataBufHB[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) - { - dTmp = WEBRTC_SPL_WORD16_MIN; - } - else if (dTmp > WEBRTC_SPL_WORD16_MAX) - { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrameHB[i] = (short)dTmp; - } - } // end of H band gain computation - // - return 0; - } - - // FFT - rdft(inst->anaLen, 1, winData, inst->ip, inst->wfft); - - imag[0] = 0; - real[0] = winData[0]; - magn[0] = (float)(fabs(real[0]) + 1.0f); - imag[inst->magnLen - 1] = 0; - real[inst->magnLen - 1] = winData[1]; - magn[inst->magnLen - 1] = (float)(fabs(real[inst->magnLen - 1]) + 1.0f); - signalEnergy = (float)(real[0] * real[0]) + (float)(real[inst->magnLen - 1] - * real[inst->magnLen - 1]); - sumMagn = magn[0] + magn[inst->magnLen - 1]; - if (inst->blockInd < END_STARTUP_SHORT) - { - inst->initMagnEst[0] += magn[0]; - inst->initMagnEst[inst->magnLen - 1] += magn[inst->magnLen - 1]; - tmpFloat2 = log((float)(inst->magnLen - 1)); - sum_log_i = tmpFloat2; - sum_log_i_square = tmpFloat2 * tmpFloat2; - tmpFloat1 = log(magn[inst->magnLen - 1]); - sum_log_magn = tmpFloat1; - sum_log_i_log_magn = tmpFloat2 * tmpFloat1; - } - for (i = 1; i < inst->magnLen - 1; i++) - { - real[i] = winData[2 * i]; - imag[i] = winData[2 * i + 1]; - // magnitude spectrum - fTmp = real[i] * real[i]; - fTmp += imag[i] * imag[i]; - signalEnergy += fTmp; - magn[i] = ((float)sqrt(fTmp)) + 1.0f; - sumMagn += magn[i]; - if (inst->blockInd < END_STARTUP_SHORT) - { - inst->initMagnEst[i] += magn[i]; - if (i >= kStartBand) - { - tmpFloat2 = log((float)i); - sum_log_i += tmpFloat2; - sum_log_i_square += tmpFloat2 * tmpFloat2; - tmpFloat1 = log(magn[i]); - sum_log_magn += tmpFloat1; - sum_log_i_log_magn += tmpFloat2 * tmpFloat1; - } - } - } - signalEnergy = signalEnergy / ((float)inst->magnLen); - inst->signalEnergy = signalEnergy; - inst->sumMagn = sumMagn; - - //compute spectral flatness on input spectrum - WebRtcNs_ComputeSpectralFlatness(inst, magn); - // quantile noise estimate - WebRtcNs_NoiseEstimation(inst, magn, noise); - //compute simplified noise model during startup - if (inst->blockInd < END_STARTUP_SHORT) - { - // Estimate White noise - inst->whiteNoiseLevel += sumMagn / ((float)inst->magnLen) * inst->overdrive; - // Estimate Pink noise parameters - tmpFloat1 = sum_log_i_square * ((float)(inst->magnLen - kStartBand)); - tmpFloat1 -= (sum_log_i * sum_log_i); - tmpFloat2 = (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn); - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the estimated spectrum to be positive - if (tmpFloat3 < 0.0f) - { - tmpFloat3 = 0.0f; - } - inst->pinkNoiseNumerator += tmpFloat3; - tmpFloat2 = (sum_log_i * sum_log_magn); - tmpFloat2 -= ((float)(inst->magnLen - kStartBand)) * sum_log_i_log_magn; - tmpFloat3 = tmpFloat2 / tmpFloat1; - // Constrain the pink noise power to be in the interval [0, 1]; - if (tmpFloat3 < 0.0f) - { - tmpFloat3 = 0.0f; - } - if (tmpFloat3 > 1.0f) - { - tmpFloat3 = 1.0f; - } - inst->pinkNoiseExp += tmpFloat3; - - // Calculate frequency independent parts of parametric noise estimate. - if (inst->pinkNoiseExp == 0.0f) - { - // Use white noise estimate - parametric_noise = inst->whiteNoiseLevel; - } - else - { - // Use pink noise estimate - parametric_num = exp(inst->pinkNoiseNumerator / (float)(inst->blockInd + 1)); - parametric_num *= (float)(inst->blockInd + 1); - parametric_exp = inst->pinkNoiseExp / (float)(inst->blockInd + 1); - parametric_noise = parametric_num / pow((float)kStartBand, parametric_exp); - } - for (i = 0; i < inst->magnLen; i++) - { - // Estimate the background noise using the white and pink noise parameters - if ((inst->pinkNoiseExp > 0.0f) && (i >= kStartBand)) - { - // Use pink noise estimate - parametric_noise = parametric_num / pow((float)i, parametric_exp); - } - theFilterTmp[i] = (inst->initMagnEst[i] - inst->overdrive * parametric_noise); - theFilterTmp[i] /= (inst->initMagnEst[i] + (float)0.0001); - // Weight quantile noise with modeled noise - noise[i] *= (inst->blockInd); - tmpFloat2 = parametric_noise * (END_STARTUP_SHORT - inst->blockInd); - noise[i] += (tmpFloat2 / (float)(inst->blockInd + 1)); - noise[i] /= END_STARTUP_SHORT; - } - } - //compute average signal during END_STARTUP_LONG time: - // used to normalize spectral difference measure - if (inst->blockInd < END_STARTUP_LONG) - { - inst->featureData[5] *= inst->blockInd; - inst->featureData[5] += signalEnergy; - inst->featureData[5] /= (inst->blockInd + 1); - } - -#ifdef PROCESS_FLOW_0 - if (inst->blockInd > END_STARTUP_LONG) - { - //option: average the quantile noise: for check with AEC2 - for (i = 0; i < inst->magnLen; i++) - { - noise[i] = (float)0.6 * inst->noisePrev[i] + (float)0.4 * noise[i]; - } - for (i = 0; i < inst->magnLen; i++) - { - // Wiener with over sub-substraction: - theFilter[i] = (magn[i] - inst->overdrive * noise[i]) / (magn[i] + (float)0.0001); - } - } -#else - //start processing at frames == converged+1 - // - // STEP 1: compute prior and post snr based on quantile noise est - // - - // compute DD estimate of prior SNR: needed for new method - for (i = 0; i < inst->magnLen; i++) - { - // post snr - snrLocPost[i] = (float)0.0; - if (magn[i] > noise[i]) - { - snrLocPost[i] = magn[i] / (noise[i] + (float)0.0001) - (float)1.0; - } - // previous post snr - // previous estimate: based on previous frame with gain filter - previousEstimateStsa[i] = inst->magnPrev[i] / (inst->noisePrev[i] + (float)0.0001) - * (inst->smooth[i]); - // DD estimate is sum of two terms: current estimate and previous estimate - // directed decision update of snrPrior - snrLocPrior[i] = DD_PR_SNR * previousEstimateStsa[i] + ((float)1.0 - DD_PR_SNR) - * snrLocPost[i]; - // post and prior snr needed for step 2 - } // end of loop over freqs -#ifdef PROCESS_FLOW_1 - for (i = 0; i < inst->magnLen; i++) - { - // gain filter - tmpFloat1 = inst->overdrive + snrLocPrior[i]; - tmpFloat2 = (float)snrLocPrior[i] / tmpFloat1; - theFilter[i] = (float)tmpFloat2; - } // end of loop over freqs -#endif - // done with step 1: dd computation of prior and post snr - - // - //STEP 2: compute speech/noise likelihood - // -#ifdef PROCESS_FLOW_2 - // compute difference of input spectrum with learned/estimated noise spectrum - WebRtcNs_ComputeSpectralDifference(inst, magn); - // compute histograms for parameter decisions (thresholds and weights for features) - // parameters are extracted once every window time (=inst->modelUpdatePars[1]) - if (updateParsFlag >= 1) - { - // counter update - inst->modelUpdatePars[3]--; - // update histogram - if (inst->modelUpdatePars[3] > 0) - { - WebRtcNs_FeatureParameterExtraction(inst, 0); - } - // compute model parameters - if (inst->modelUpdatePars[3] == 0) - { - WebRtcNs_FeatureParameterExtraction(inst, 1); - inst->modelUpdatePars[3] = inst->modelUpdatePars[1]; - // if wish to update only once, set flag to zero - if (updateParsFlag == 1) - { - inst->modelUpdatePars[0] = 0; - } - else - { - // update every window: - // get normalization for spectral difference for next window estimate - inst->featureData[6] = inst->featureData[6] - / ((float)inst->modelUpdatePars[1]); - inst->featureData[5] = (float)0.5 * (inst->featureData[6] - + inst->featureData[5]); - inst->featureData[6] = (float)0.0; - } - } - } - // compute speech/noise probability - WebRtcNs_SpeechNoiseProb(inst, probSpeechFinal, snrLocPrior, snrLocPost); - // time-avg parameter for noise update - gammaNoiseTmp = NOISE_UPDATE; - for (i = 0; i < inst->magnLen; i++) - { - probSpeech = probSpeechFinal[i]; - probNonSpeech = (float)1.0 - probSpeech; - // temporary noise update: - // use it for speech frames if update value is less than previous - noiseUpdateTmp = gammaNoiseTmp * inst->noisePrev[i] + ((float)1.0 - gammaNoiseTmp) - * (probNonSpeech * magn[i] + probSpeech * inst->noisePrev[i]); - // - // time-constant based on speech/noise state - gammaNoiseOld = gammaNoiseTmp; - gammaNoiseTmp = NOISE_UPDATE; - // increase gamma (i.e., less noise update) for frame likely to be speech - if (probSpeech > PROB_RANGE) - { - gammaNoiseTmp = SPEECH_UPDATE; - } - // conservative noise update - if (probSpeech < PROB_RANGE) - { - inst->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - inst->magnAvgPause[i]); - } - // noise update - if (gammaNoiseTmp == gammaNoiseOld) - { - noise[i] = noiseUpdateTmp; - } - else - { - noise[i] = gammaNoiseTmp * inst->noisePrev[i] + ((float)1.0 - gammaNoiseTmp) - * (probNonSpeech * magn[i] + probSpeech * inst->noisePrev[i]); - // allow for noise update downwards: - // if noise update decreases the noise, it is safe, so allow it to happen - if (noiseUpdateTmp < noise[i]) - { - noise[i] = noiseUpdateTmp; - } - } - } // end of freq loop - // done with step 2: noise update - - // - // STEP 3: compute dd update of prior snr and post snr based on new noise estimate - // - for (i = 0; i < inst->magnLen; i++) - { - // post and prior snr - currentEstimateStsa = (float)0.0; - if (magn[i] > noise[i]) - { - currentEstimateStsa = magn[i] / (noise[i] + (float)0.0001) - (float)1.0; - } - // DD estimate is sume of two terms: current estimate and previous estimate - // directed decision update of snrPrior - snrPrior = DD_PR_SNR * previousEstimateStsa[i] + ((float)1.0 - DD_PR_SNR) - * currentEstimateStsa; - // gain filter - tmpFloat1 = inst->overdrive + snrPrior; - tmpFloat2 = (float)snrPrior / tmpFloat1; - theFilter[i] = (float)tmpFloat2; - } // end of loop over freqs - // done with step3 -#endif -#endif - - for (i = 0; i < inst->magnLen; i++) - { - // flooring bottom - if (theFilter[i] < inst->denoiseBound) - { - theFilter[i] = inst->denoiseBound; - } - // flooring top - if (theFilter[i] > (float)1.0) - { - theFilter[i] = 1.0; - } - if (inst->blockInd < END_STARTUP_SHORT) - { - // flooring bottom - if (theFilterTmp[i] < inst->denoiseBound) - { - theFilterTmp[i] = inst->denoiseBound; - } - // flooring top - if (theFilterTmp[i] > (float)1.0) - { - theFilterTmp[i] = 1.0; - } - // Weight the two suppression filters - theFilter[i] *= (inst->blockInd); - theFilterTmp[i] *= (END_STARTUP_SHORT - inst->blockInd); - theFilter[i] += theFilterTmp[i]; - theFilter[i] /= (END_STARTUP_SHORT); - } - // smoothing -#ifdef PROCESS_FLOW_0 - inst->smooth[i] *= SMOOTH; // value set to 0.7 in define.h file - inst->smooth[i] += ((float)1.0 - SMOOTH) * theFilter[i]; -#else - inst->smooth[i] = theFilter[i]; -#endif - real[i] *= inst->smooth[i]; - imag[i] *= inst->smooth[i]; - } - // keep track of noise and magn spectrum for next frame - for (i = 0; i < inst->magnLen; i++) - { - inst->noisePrev[i] = noise[i]; - inst->magnPrev[i] = magn[i]; - } - // back to time domain - winData[0] = real[0]; - winData[1] = real[inst->magnLen - 1]; - for (i = 1; i < inst->magnLen - 1; i++) - { - winData[2 * i] = real[i]; - winData[2 * i + 1] = imag[i]; - } - rdft(inst->anaLen, -1, winData, inst->ip, inst->wfft); - - for (i = 0; i < inst->anaLen; i++) - { - real[i] = 2.0f * winData[i] / inst->anaLen; // fft scaling - } - - //scale factor: only do it after END_STARTUP_LONG time - factor = (float)1.0; - if (inst->gainmap == 1 && inst->blockInd > END_STARTUP_LONG) - { - factor1 = (float)1.0; - factor2 = (float)1.0; - - energy2 = 0.0; - for (i = 0; i < inst->anaLen;i++) - { - energy2 += (float)real[i] * (float)real[i]; - } - gain = (float)sqrt(energy2 / (energy1 + (float)1.0)); - -#ifdef PROCESS_FLOW_2 - // scaling for new version - if (gain > B_LIM) - { - factor1 = (float)1.0 + (float)1.3 * (gain - B_LIM); - if (gain * factor1 > (float)1.0) - { - factor1 = (float)1.0 / gain; - } - } - if (gain < B_LIM) - { - //don't reduce scale too much for pause regions: - // attenuation here should be controlled by flooring - if (gain <= inst->denoiseBound) - { - gain = inst->denoiseBound; - } - factor2 = (float)1.0 - (float)0.3 * (B_LIM - gain); - } - //combine both scales with speech/noise prob: - // note prior (priorSpeechProb) is not frequency dependent - factor = inst->priorSpeechProb * factor1 + ((float)1.0 - inst->priorSpeechProb) - * factor2; -#else - if (gain > B_LIM) - { - factor = (float)1.0 + (float)1.3 * (gain - B_LIM); - } - else - { - factor = (float)1.0 + (float)2.0 * (gain - B_LIM); - } - if (gain * factor > (float)1.0) - { - factor = (float)1.0 / gain; - } -#endif - } // out of inst->gainmap==1 - - // synthesis - for (i = 0; i < inst->anaLen; i++) - { - inst->syntBuf[i] += factor * inst->window[i] * (float)real[i]; - } - // read out fully processed segment - for (i = inst->windShift; i < inst->blockLen + inst->windShift; i++) - { - fout[i - inst->windShift] = inst->syntBuf[i]; - } - // update synthesis buffer - memcpy(inst->syntBuf, inst->syntBuf + inst->blockLen, - sizeof(float) * (inst->anaLen - inst->blockLen)); - memset(inst->syntBuf + inst->anaLen - inst->blockLen, 0, - sizeof(float) * inst->blockLen); - - // out buffer - inst->outLen = inst->blockLen - inst->blockLen10ms; - if (inst->blockLen > inst->blockLen10ms) - { - for (i = 0; i < inst->outLen; i++) - { - inst->outBuf[i] = fout[i + inst->blockLen10ms]; - } - } - } // end of if out.len==0 - else - { - for (i = 0; i < inst->blockLen10ms; i++) - { - fout[i] = inst->outBuf[i]; - } - memcpy(inst->outBuf, inst->outBuf + inst->blockLen10ms, - sizeof(float) * (inst->outLen - inst->blockLen10ms)); - memset(inst->outBuf + inst->outLen - inst->blockLen10ms, 0, - sizeof(float) * inst->blockLen10ms); - inst->outLen -= inst->blockLen10ms; - } - - // convert to short - for (i = 0; i < inst->blockLen10ms; i++) - { - dTmp = fout[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) - { - dTmp = WEBRTC_SPL_WORD16_MIN; - } - else if (dTmp > WEBRTC_SPL_WORD16_MAX) - { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrame[i] = (short)dTmp; - } - - // for time-domain gain of HB - if (flagHB == 1) - { - for (i = 0; i < inst->magnLen; i++) - { - inst->speechProbHB[i] = probSpeechFinal[i]; - } - if (inst->blockInd > END_STARTUP_LONG) - { - // average speech prob from low band - // avg over second half (i.e., 4->8kHz) of freq. spectrum - avgProbSpeechHB = 0.0; - for (i = inst->magnLen - deltaBweHB - 1; i < inst->magnLen - 1; i++) - { - avgProbSpeechHB += inst->speechProbHB[i]; - } - avgProbSpeechHB = avgProbSpeechHB / ((float)deltaBweHB); - // average filter gain from low band - // average over second half (i.e., 4->8kHz) of freq. spectrum - avgFilterGainHB = 0.0; - for (i = inst->magnLen - deltaGainHB - 1; i < inst->magnLen - 1; i++) - { - avgFilterGainHB += inst->smooth[i]; - } - avgFilterGainHB = avgFilterGainHB / ((float)(deltaGainHB)); - avgProbSpeechHBTmp = (float)2.0 * avgProbSpeechHB - (float)1.0; - // gain based on speech prob: - gainModHB = (float)0.5 * ((float)1.0 + (float)tanh(gainMapParHB * avgProbSpeechHBTmp)); - //combine gain with low band gain - gainTimeDomainHB = (float)0.5 * gainModHB + (float)0.5 * avgFilterGainHB; - if (avgProbSpeechHB >= (float)0.5) - { - gainTimeDomainHB = (float)0.25 * gainModHB + (float)0.75 * avgFilterGainHB; - } - gainTimeDomainHB = gainTimeDomainHB * decayBweHB; - } // end of converged - //make sure gain is within flooring range - // flooring bottom - if (gainTimeDomainHB < inst->denoiseBound) - { - gainTimeDomainHB = inst->denoiseBound; - } - // flooring top - if (gainTimeDomainHB > (float)1.0) - { - gainTimeDomainHB = 1.0; - } - //apply gain - for (i = 0; i < inst->blockLen10ms; i++) - { - dTmp = gainTimeDomainHB * inst->dataBufHB[i]; - if (dTmp < WEBRTC_SPL_WORD16_MIN) - { - dTmp = WEBRTC_SPL_WORD16_MIN; - } - else if (dTmp > WEBRTC_SPL_WORD16_MAX) - { - dTmp = WEBRTC_SPL_WORD16_MAX; - } - outFrameHB[i] = (short)dTmp; - } - } // end of H band gain computation - // - - return 0; -} diff --git a/modules/audio_processing/ns/main/source/ns_core.h b/modules/audio_processing/ns/main/source/ns_core.h deleted file mode 100644 index f72e22bf1..000000000 --- a/modules/audio_processing/ns/main/source/ns_core.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ - -#include "defines.h" - -typedef struct NSParaExtract_t_ { - - //bin size of histogram - float binSizeLrt; - float binSizeSpecFlat; - float binSizeSpecDiff; - //range of histogram over which lrt threshold is computed - float rangeAvgHistLrt; - //scale parameters: multiply dominant peaks of the histograms by scale factor to obtain - //thresholds for prior model - float factor1ModelPars; //for lrt and spectral difference - float factor2ModelPars; //for spectral_flatness: used when noise is flatter than speech - //peak limit for spectral flatness (varies between 0 and 1) - float thresPosSpecFlat; - //limit on spacing of two highest peaks in histogram: spacing determined by bin size - float limitPeakSpacingSpecFlat; - float limitPeakSpacingSpecDiff; - //limit on relevance of second peak: - float limitPeakWeightsSpecFlat; - float limitPeakWeightsSpecDiff; - //limit on fluctuation of lrt feature - float thresFluctLrt; - //limit on the max and min values for the feature thresholds - float maxLrt; - float minLrt; - float maxSpecFlat; - float minSpecFlat; - float maxSpecDiff; - float minSpecDiff; - //criteria of weight of histogram peak to accept/reject feature - int thresWeightSpecFlat; - int thresWeightSpecDiff; - -} NSParaExtract_t; - -typedef struct NSinst_t_ { - - WebRtc_UWord32 fs; - int blockLen; - int blockLen10ms; - int windShift; - int outLen; - int anaLen; - int magnLen; - int aggrMode; - const float* window; - float dataBuf[ANAL_BLOCKL_MAX]; - float syntBuf[ANAL_BLOCKL_MAX]; - float outBuf[3 * BLOCKL_MAX]; - - int initFlag; - // parameters for quantile noise estimation - float density[SIMULT * HALF_ANAL_BLOCKL]; - float lquantile[SIMULT * HALF_ANAL_BLOCKL]; - float quantile[HALF_ANAL_BLOCKL]; - int counter[SIMULT]; - int updates; - // parameters for Wiener filter - float smooth[HALF_ANAL_BLOCKL]; - float overdrive; - float denoiseBound; - int gainmap; - // fft work arrays. - int ip[IP_LENGTH]; - float wfft[W_LENGTH]; - - // parameters for new method: some not needed, will reduce/cleanup later - WebRtc_Word32 blockInd; //frame index counter - int modelUpdatePars[4]; //parameters for updating or estimating - // thresholds/weights for prior model - float priorModelPars[7]; //parameters for prior model - float noisePrev[HALF_ANAL_BLOCKL]; //noise spectrum from previous frame - float magnPrev[HALF_ANAL_BLOCKL]; //magnitude spectrum of previous frame - float logLrtTimeAvg[HALF_ANAL_BLOCKL]; //log lrt factor with time-smoothing - float priorSpeechProb; //prior speech/noise probability - float featureData[7]; //data for features - float magnAvgPause[HALF_ANAL_BLOCKL]; //conservative noise spectrum estimate - float signalEnergy; //energy of magn - float sumMagn; //sum of magn - float whiteNoiseLevel; //initial noise estimate - float initMagnEst[HALF_ANAL_BLOCKL]; //initial magnitude spectrum estimate - float pinkNoiseNumerator; //pink noise parameter: numerator - float pinkNoiseExp; //pink noise parameter: power of freq - NSParaExtract_t featureExtractionParams; //parameters for feature extraction - //histograms for parameter estimation - int histLrt[HIST_PAR_EST]; - int histSpecFlat[HIST_PAR_EST]; - int histSpecDiff[HIST_PAR_EST]; - //quantities for high band estimate - float speechProbHB[HALF_ANAL_BLOCKL]; //final speech/noise prob: prior + LRT - float dataBufHB[ANAL_BLOCKL_MAX]; //buffering data for HB - -} NSinst_t; - - -#ifdef __cplusplus -extern "C" { -#endif - -/**************************************************************************** - * WebRtcNs_InitCore(...) - * - * This function initializes a noise suppression instance - * - * Input: - * - inst : Instance that should be initialized - * - fs : Sampling frequency - * - * Output: - * - inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_InitCore(NSinst_t *inst, WebRtc_UWord32 fs); - -/**************************************************************************** - * WebRtcNs_set_policy_core(...) - * - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - inst : Instance that should be initialized - * - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB) - * - * Output: - * - NS_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNs_set_policy_core(NSinst_t *inst, int mode); - -/**************************************************************************** - * WebRtcNs_ProcessCore - * - * Do noise suppression. - * - * Input: - * - inst : Instance that should be initialized - * - inFrameLow : Input speech frame for lower band - * - inFrameHigh : Input speech frame for higher band - * - * Output: - * - inst : Updated instance - * - outFrameLow : Output speech frame for lower band - * - outFrameHigh : Output speech frame for higher band - * - * Return value : 0 - OK - * -1 - Error - */ - - -int WebRtcNs_ProcessCore(NSinst_t *inst, - short *inFrameLow, - short *inFrameHigh, - short *outFrameLow, - short *outFrameHigh); - - -#ifdef __cplusplus -} -#endif -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NS_CORE_H_ diff --git a/modules/audio_processing/ns/main/source/nsx_core.c b/modules/audio_processing/ns/main/source/nsx_core.c deleted file mode 100644 index 6c62d64e6..000000000 --- a/modules/audio_processing/ns/main/source/nsx_core.c +++ /dev/null @@ -1,2493 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "noise_suppression_x.h" - -#include -#include -#include -#include - -#include "nsx_core.h" - -// Skip first frequency bins during estimation. (0 <= value < 64) -static const int kStartBand = 5; - -// Rounding -static const WebRtc_Word16 kRoundTable[16] = {0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, - 2048, 4096, 8192, 16384}; - -// Constants to compensate for shifting signal log(2^shifts). -static const WebRtc_Word16 kLogTable[9] = {0, 177, 355, 532, 710, 887, 1065, 1242, 1420}; - -static const WebRtc_Word16 kCounterDiv[201] = {32767, 16384, 10923, 8192, 6554, 5461, 4681, - 4096, 3641, 3277, 2979, 2731, 2521, 2341, 2185, 2048, 1928, 1820, 1725, 1638, 1560, - 1489, 1425, 1365, 1311, 1260, 1214, 1170, 1130, 1092, 1057, 1024, 993, 964, 936, 910, - 886, 862, 840, 819, 799, 780, 762, 745, 728, 712, 697, 683, 669, 655, 643, 630, 618, - 607, 596, 585, 575, 565, 555, 546, 537, 529, 520, 512, 504, 496, 489, 482, 475, 468, - 462, 455, 449, 443, 437, 431, 426, 420, 415, 410, 405, 400, 395, 390, 386, 381, 377, - 372, 368, 364, 360, 356, 352, 349, 345, 341, 338, 334, 331, 328, 324, 321, 318, 315, - 312, 309, 306, 303, 301, 298, 295, 293, 290, 287, 285, 282, 280, 278, 275, 273, 271, - 269, 266, 264, 262, 260, 258, 256, 254, 252, 250, 248, 246, 245, 243, 241, 239, 237, - 236, 234, 232, 231, 229, 228, 226, 224, 223, 221, 220, 218, 217, 216, 214, 213, 211, - 210, 209, 207, 206, 205, 204, 202, 201, 200, 199, 197, 196, 195, 194, 193, 192, 191, - 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, - 172, 172, 171, 170, 169, 168, 167, 166, 165, 165, 164, 163}; - -static const WebRtc_Word16 kLogTableFrac[256] = { - 0, 1, 3, 4, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, - 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 42, - 44, 45, 46, 47, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, - 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, - 82, 84, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, - 100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116, 117, - 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, - 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 178, - 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 190, 191, 192, 192, - 193, 194, 195, 196, 197, 198, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, - 207, 208, 208, 209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 219, - 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 228, 228, 229, 230, 231, 231, - 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, 244, - 244, 245, 246, 247, 247, 248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255 -}; - -static const WebRtc_Word16 kPowTableFrac[1024] = { - 0, 1, 1, 2, 3, 3, 4, 5, - 6, 6, 7, 8, 8, 9, 10, 10, - 11, 12, 13, 13, 14, 15, 15, 16, - 17, 17, 18, 19, 20, 20, 21, 22, - 22, 23, 24, 25, 25, 26, 27, 27, - 28, 29, 30, 30, 31, 32, 32, 33, - 34, 35, 35, 36, 37, 37, 38, 39, - 40, 40, 41, 42, 42, 43, 44, 45, - 45, 46, 47, 48, 48, 49, 50, 50, - 51, 52, 53, 53, 54, 55, 56, 56, - 57, 58, 58, 59, 60, 61, 61, 62, - 63, 64, 64, 65, 66, 67, 67, 68, - 69, 69, 70, 71, 72, 72, 73, 74, - 75, 75, 76, 77, 78, 78, 79, 80, - 81, 81, 82, 83, 84, 84, 85, 86, - 87, 87, 88, 89, 90, 90, 91, 92, - 93, 93, 94, 95, 96, 96, 97, 98, - 99, 100, 100, 101, 102, 103, 103, 104, - 105, 106, 106, 107, 108, 109, 109, 110, - 111, 112, 113, 113, 114, 115, 116, 116, - 117, 118, 119, 119, 120, 121, 122, 123, - 123, 124, 125, 126, 126, 127, 128, 129, - 130, 130, 131, 132, 133, 133, 134, 135, - 136, 137, 137, 138, 139, 140, 141, 141, - 142, 143, 144, 144, 145, 146, 147, 148, - 148, 149, 150, 151, 152, 152, 153, 154, - 155, 156, 156, 157, 158, 159, 160, 160, - 161, 162, 163, 164, 164, 165, 166, 167, - 168, 168, 169, 170, 171, 172, 173, 173, - 174, 175, 176, 177, 177, 178, 179, 180, - 181, 181, 182, 183, 184, 185, 186, 186, - 187, 188, 189, 190, 190, 191, 192, 193, - 194, 195, 195, 196, 197, 198, 199, 200, - 200, 201, 202, 203, 204, 205, 205, 206, - 207, 208, 209, 210, 210, 211, 212, 213, - 214, 215, 215, 216, 217, 218, 219, 220, - 220, 221, 222, 223, 224, 225, 225, 226, - 227, 228, 229, 230, 231, 231, 232, 233, - 234, 235, 236, 237, 237, 238, 239, 240, - 241, 242, 243, 243, 244, 245, 246, 247, - 248, 249, 249, 250, 251, 252, 253, 254, - 255, 255, 256, 257, 258, 259, 260, 261, - 262, 262, 263, 264, 265, 266, 267, 268, - 268, 269, 270, 271, 272, 273, 274, 275, - 276, 276, 277, 278, 279, 280, 281, 282, - 283, 283, 284, 285, 286, 287, 288, 289, - 290, 291, 291, 292, 293, 294, 295, 296, - 297, 298, 299, 299, 300, 301, 302, 303, - 304, 305, 306, 307, 308, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, - 318, 319, 320, 321, 322, 323, 324, 325, - 326, 327, 328, 328, 329, 330, 331, 332, - 333, 334, 335, 336, 337, 338, 339, 339, - 340, 341, 342, 343, 344, 345, 346, 347, - 348, 349, 350, 351, 352, 352, 353, 354, - 355, 356, 357, 358, 359, 360, 361, 362, - 363, 364, 365, 366, 367, 367, 368, 369, - 370, 371, 372, 373, 374, 375, 376, 377, - 378, 379, 380, 381, 382, 383, 384, 385, - 385, 386, 387, 388, 389, 390, 391, 392, - 393, 394, 395, 396, 397, 398, 399, 400, - 401, 402, 403, 404, 405, 406, 407, 408, - 409, 410, 410, 411, 412, 413, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, - 424, 425, 426, 427, 428, 429, 430, 431, - 432, 433, 434, 435, 436, 437, 438, 439, - 440, 441, 442, 443, 444, 445, 446, 447, - 448, 449, 450, 451, 452, 453, 454, 455, - 456, 457, 458, 459, 460, 461, 462, 463, - 464, 465, 466, 467, 468, 469, 470, 471, - 472, 473, 474, 475, 476, 477, 478, 479, - 480, 481, 482, 483, 484, 485, 486, 487, - 488, 489, 490, 491, 492, 493, 494, 495, - 496, 498, 499, 500, 501, 502, 503, 504, - 505, 506, 507, 508, 509, 510, 511, 512, - 513, 514, 515, 516, 517, 518, 519, 520, - 521, 522, 523, 525, 526, 527, 528, 529, - 530, 531, 532, 533, 534, 535, 536, 537, - 538, 539, 540, 541, 542, 544, 545, 546, - 547, 548, 549, 550, 551, 552, 553, 554, - 555, 556, 557, 558, 560, 561, 562, 563, - 564, 565, 566, 567, 568, 569, 570, 571, - 572, 574, 575, 576, 577, 578, 579, 580, - 581, 582, 583, 584, 585, 587, 588, 589, - 590, 591, 592, 593, 594, 595, 596, 597, - 599, 600, 601, 602, 603, 604, 605, 606, - 607, 608, 610, 611, 612, 613, 614, 615, - 616, 617, 618, 620, 621, 622, 623, 624, - 625, 626, 627, 628, 630, 631, 632, 633, - 634, 635, 636, 637, 639, 640, 641, 642, - 643, 644, 645, 646, 648, 649, 650, 651, - 652, 653, 654, 656, 657, 658, 659, 660, - 661, 662, 664, 665, 666, 667, 668, 669, - 670, 672, 673, 674, 675, 676, 677, 678, - 680, 681, 682, 683, 684, 685, 687, 688, - 689, 690, 691, 692, 693, 695, 696, 697, - 698, 699, 700, 702, 703, 704, 705, 706, - 708, 709, 710, 711, 712, 713, 715, 716, - 717, 718, 719, 720, 722, 723, 724, 725, - 726, 728, 729, 730, 731, 732, 733, 735, - 736, 737, 738, 739, 741, 742, 743, 744, - 745, 747, 748, 749, 750, 751, 753, 754, - 755, 756, 757, 759, 760, 761, 762, 763, - 765, 766, 767, 768, 770, 771, 772, 773, - 774, 776, 777, 778, 779, 780, 782, 783, - 784, 785, 787, 788, 789, 790, 792, 793, - 794, 795, 796, 798, 799, 800, 801, 803, - 804, 805, 806, 808, 809, 810, 811, 813, - 814, 815, 816, 818, 819, 820, 821, 823, - 824, 825, 826, 828, 829, 830, 831, 833, - 834, 835, 836, 838, 839, 840, 841, 843, - 844, 845, 846, 848, 849, 850, 851, 853, - 854, 855, 857, 858, 859, 860, 862, 863, - 864, 866, 867, 868, 869, 871, 872, 873, - 874, 876, 877, 878, 880, 881, 882, 883, - 885, 886, 887, 889, 890, 891, 893, 894, - 895, 896, 898, 899, 900, 902, 903, 904, - 906, 907, 908, 909, 911, 912, 913, 915, - 916, 917, 919, 920, 921, 923, 924, 925, - 927, 928, 929, 931, 932, 933, 935, 936, - 937, 938, 940, 941, 942, 944, 945, 946, - 948, 949, 950, 952, 953, 955, 956, 957, - 959, 960, 961, 963, 964, 965, 967, 968, - 969, 971, 972, 973, 975, 976, 977, 979, - 980, 981, 983, 984, 986, 987, 988, 990, - 991, 992, 994, 995, 996, 998, 999, 1001, - 1002, 1003, 1005, 1006, 1007, 1009, 1010, 1012, - 1013, 1014, 1016, 1017, 1018, 1020, 1021, 1023 -}; - -static const WebRtc_Word16 kIndicatorTable[17] = {0, 2017, 3809, 5227, 6258, 6963, 7424, 7718, - 7901, 8014, 8084, 8126, 8152, 8168, 8177, 8183, 8187}; - -// hybrib Hanning & flat window -static const WebRtc_Word16 kBlocks80w128x[128] = { - 0, 536, 1072, 1606, 2139, 2669, 3196, 3720, 4240, 4756, 5266, - 5771, 6270, 6762, 7246, 7723, 8192, 8652, 9102, 9543, 9974, 10394, - 10803, 11200, 11585, 11958, 12318, 12665, 12998, 13318, 13623, 13913, 14189, - 14449, 14694, 14924, 15137, 15334, 15515, 15679, 15826, 15956, 16069, 16165, - 16244, 16305, 16349, 16375, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, - 16384, 16384, 16384, 16384, 16375, 16349, 16305, 16244, 16165, 16069, 15956, - 15826, 15679, 15515, 15334, 15137, 14924, 14694, 14449, 14189, 13913, 13623, - 13318, 12998, 12665, 12318, 11958, 11585, 11200, 10803, 10394, 9974, 9543, - 9102, 8652, 8192, 7723, 7246, 6762, 6270, 5771, 5266, 4756, 4240, - 3720, 3196, 2669, 2139, 1606, 1072, 536 -}; - -// hybrib Hanning & flat window -static const WebRtc_Word16 kBlocks160w256x[256] = { - 0, 268, 536, 804, 1072, 1339, 1606, 1872, - 2139, 2404, 2669, 2933, 3196, 3459, 3720, 3981, - 4240, 4499, 4756, 5012, 5266, 5520, 5771, 6021, - 6270, 6517, 6762, 7005, 7246, 7486, 7723, 7959, - 8192, 8423, 8652, 8878, 9102, 9324, 9543, 9760, - 9974, 10185, 10394, 10600, 10803, 11003, 11200, 11394, -11585, 11773, 11958, 12140, 12318, 12493, 12665, 12833, -12998, 13160, 13318, 13472, 13623, 13770, 13913, 14053, -14189, 14321, 14449, 14574, 14694, 14811, 14924, 15032, -15137, 15237, 15334, 15426, 15515, 15599, 15679, 15754, -15826, 15893, 15956, 16015, 16069, 16119, 16165, 16207, -16244, 16277, 16305, 16329, 16349, 16364, 16375, 16382, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, -16384, 16382, 16375, 16364, 16349, 16329, 16305, 16277, -16244, 16207, 16165, 16119, 16069, 16015, 15956, 15893, -15826, 15754, 15679, 15599, 15515, 15426, 15334, 15237, -15137, 15032, 14924, 14811, 14694, 14574, 14449, 14321, -14189, 14053, 13913, 13770, 13623, 13472, 13318, 13160, -12998, 12833, 12665, 12493, 12318, 12140, 11958, 11773, -11585, 11394, 11200, 11003, 10803, 10600, 10394, 10185, - 9974, 9760, 9543, 9324, 9102, 8878, 8652, 8423, - 8192, 7959, 7723, 7486, 7246, 7005, 6762, 6517, - 6270, 6021, 5771, 5520, 5266, 5012, 4756, 4499, - 4240, 3981, 3720, 3459, 3196, 2933, 2669, 2404, - 2139, 1872, 1606, 1339, 1072, 804, 536, 268 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const WebRtc_Word16 kFactor1Table[257] = { - 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8233, 8274, 8315, 8355, 8396, 8436, 8475, 8515, 8554, 8592, 8631, 8669, - 8707, 8745, 8783, 8820, 8857, 8894, 8931, 8967, 9003, 9039, 9075, 9111, 9146, 9181, - 9216, 9251, 9286, 9320, 9354, 9388, 9422, 9456, 9489, 9523, 9556, 9589, 9622, 9655, - 9687, 9719, 9752, 9784, 9816, 9848, 9879, 9911, 9942, 9973, 10004, 10035, 10066, - 10097, 10128, 10158, 10188, 10218, 10249, 10279, 10308, 10338, 10368, 10397, 10426, - 10456, 10485, 10514, 10543, 10572, 10600, 10629, 10657, 10686, 10714, 10742, 10770, - 10798, 10826, 10854, 10882, 10847, 10810, 10774, 10737, 10701, 10666, 10631, 10596, - 10562, 10527, 10494, 10460, 10427, 10394, 10362, 10329, 10297, 10266, 10235, 10203, - 10173, 10142, 10112, 10082, 10052, 10023, 9994, 9965, 9936, 9908, 9879, 9851, 9824, - 9796, 9769, 9742, 9715, 9689, 9662, 9636, 9610, 9584, 9559, 9534, 9508, 9484, 9459, - 9434, 9410, 9386, 9362, 9338, 9314, 9291, 9268, 9245, 9222, 9199, 9176, 9154, 9132, - 9110, 9088, 9066, 9044, 9023, 9002, 8980, 8959, 8939, 8918, 8897, 8877, 8857, 8836, - 8816, 8796, 8777, 8757, 8738, 8718, 8699, 8680, 8661, 8642, 8623, 8605, 8586, 8568, - 8550, 8532, 8514, 8496, 8478, 8460, 8443, 8425, 8408, 8391, 8373, 8356, 8339, 8323, - 8306, 8289, 8273, 8256, 8240, 8224, 8208, 8192 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const WebRtc_Word16 kFactor2Aggressiveness1[257] = { - 7577, 7577, 7577, 7577, 7577, 7577, - 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const WebRtc_Word16 kFactor2Aggressiveness2[257] = { - 7270, 7270, 7270, 7270, 7270, 7306, - 7339, 7369, 7397, 7424, 7448, 7472, 7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// Gain factor table: Input value in Q8 and output value in Q13 -static const WebRtc_Word16 kFactor2Aggressiveness3[257] = { - 7184, 7184, 7184, 7229, 7270, 7306, - 7339, 7369, 7397, 7424, 7448, 7472, 7495, 7517, 7537, 7558, 7577, 7596, 7614, 7632, - 7650, 7667, 7683, 7699, 7715, 7731, 7746, 7761, 7775, 7790, 7804, 7818, 7832, 7845, - 7858, 7871, 7884, 7897, 7910, 7922, 7934, 7946, 7958, 7970, 7982, 7993, 8004, 8016, - 8027, 8038, 8049, 8060, 8070, 8081, 8091, 8102, 8112, 8122, 8132, 8143, 8152, 8162, - 8172, 8182, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, - 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192 -}; - -// sum of log2(i) from table index to inst->anaLen2 in Q5 -// Note that the first table value is invalid, since log2(0) = -infinity -static const WebRtc_Word16 kSumLogIndex[66] = { - 0, 22917, 22917, 22885, 22834, 22770, 22696, 22613, - 22524, 22428, 22326, 22220, 22109, 21994, 21876, 21754, - 21629, 21501, 21370, 21237, 21101, 20963, 20822, 20679, - 20535, 20388, 20239, 20089, 19937, 19783, 19628, 19470, - 19312, 19152, 18991, 18828, 18664, 18498, 18331, 18164, - 17994, 17824, 17653, 17480, 17306, 17132, 16956, 16779, - 16602, 16423, 16243, 16063, 15881, 15699, 15515, 15331, - 15146, 14960, 14774, 14586, 14398, 14209, 14019, 13829, - 13637, 13445 -}; - -// sum of log2(i)^2 from table index to inst->anaLen2 in Q2 -// Note that the first table value is invalid, since log2(0) = -infinity -static const WebRtc_Word16 kSumSquareLogIndex[66] = { - 0, 16959, 16959, 16955, 16945, 16929, 16908, 16881, - 16850, 16814, 16773, 16729, 16681, 16630, 16575, 16517, - 16456, 16392, 16325, 16256, 16184, 16109, 16032, 15952, - 15870, 15786, 15700, 15612, 15521, 15429, 15334, 15238, - 15140, 15040, 14938, 14834, 14729, 14622, 14514, 14404, - 14292, 14179, 14064, 13947, 13830, 13710, 13590, 13468, - 13344, 13220, 13094, 12966, 12837, 12707, 12576, 12444, - 12310, 12175, 12039, 11902, 11763, 11624, 11483, 11341, - 11198, 11054 -}; - -// log2(table index) in Q12 -// Note that the first table value is invalid, since log2(0) = -infinity -static const WebRtc_Word16 kLogIndex[129] = { - 0, 0, 4096, 6492, 8192, 9511, 10588, 11499, - 12288, 12984, 13607, 14170, 14684, 15157, 15595, 16003, - 16384, 16742, 17080, 17400, 17703, 17991, 18266, 18529, - 18780, 19021, 19253, 19476, 19691, 19898, 20099, 20292, - 20480, 20662, 20838, 21010, 21176, 21338, 21496, 21649, - 21799, 21945, 22087, 22226, 22362, 22495, 22625, 22752, - 22876, 22998, 23117, 23234, 23349, 23462, 23572, 23680, - 23787, 23892, 23994, 24095, 24195, 24292, 24388, 24483, - 24576, 24668, 24758, 24847, 24934, 25021, 25106, 25189, - 25272, 25354, 25434, 25513, 25592, 25669, 25745, 25820, - 25895, 25968, 26041, 26112, 26183, 26253, 26322, 26390, - 26458, 26525, 26591, 26656, 26721, 26784, 26848, 26910, - 26972, 27033, 27094, 27154, 27213, 27272, 27330, 27388, - 27445, 27502, 27558, 27613, 27668, 27722, 27776, 27830, - 27883, 27935, 27988, 28039, 28090, 28141, 28191, 28241, - 28291, 28340, 28388, 28437, 28484, 28532, 28579, 28626, - 28672 -}; - -// determinant of estimation matrix in Q0 corresponding to the log2 tables above -// Note that the first table value is invalid, since log2(0) = -infinity -static const WebRtc_Word16 kDeterminantEstMatrix[66] = { - 0, 29814, 25574, 22640, 20351, 18469, 16873, 15491, - 14277, 13199, 12233, 11362, 10571, 9851, 9192, 8587, - 8030, 7515, 7038, 6596, 6186, 5804, 5448, 5115, - 4805, 4514, 4242, 3988, 3749, 3524, 3314, 3116, - 2930, 2755, 2590, 2435, 2289, 2152, 2022, 1900, - 1785, 1677, 1575, 1478, 1388, 1302, 1221, 1145, - 1073, 1005, 942, 881, 825, 771, 721, 674, - 629, 587, 547, 510, 475, 442, 411, 382, - 355, 330 -}; - -void WebRtcNsx_UpdateNoiseEstimate(NsxInst_t *inst, int offset) -{ - WebRtc_Word32 tmp32no1 = 0; - WebRtc_Word32 tmp32no2 = 0; - - WebRtc_Word16 tmp16no1 = 0; - WebRtc_Word16 tmp16no2 = 0; - WebRtc_Word16 exp2Const = 11819; // Q13 - - int i = 0; - - tmp16no2 = WebRtcSpl_MaxValueW16(inst->noiseEstLogQuantile + offset, inst->magnLen); - inst->qNoise = 14 - - (int)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(exp2Const, tmp16no2, 21); - for (i = 0; i < inst->magnLen; i++) - { - // inst->quantile[i]=exp(inst->lquantile[offset+i]); - // in Q21 - tmp32no2 = WEBRTC_SPL_MUL_16_16(exp2Const, inst->noiseEstLogQuantile[offset + i]); - tmp32no1 = (0x00200000 | (tmp32no2 & 0x001FFFFF)); - tmp16no1 = -(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32no2, 21); - tmp16no1 += 21;// shift 21 to get result in Q0 - tmp16no1 -= (WebRtc_Word16)inst->qNoise; //shift to get result in Q(qNoise) - if (tmp16no1 > 0) - { - inst->noiseEstQuantile[i] = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32no1 + - kRoundTable[tmp16no1], tmp16no1); - } - else - { - inst->noiseEstQuantile[i] = (WebRtc_Word16)WEBRTC_SPL_LSHIFT_W32(tmp32no1, - -tmp16no1); - } - } -} - -void WebRtcNsx_CalcParametricNoiseEstimate(NsxInst_t *inst, - WebRtc_Word16 pink_noise_exp_avg, - WebRtc_Word32 pink_noise_num_avg, - int freq_index, - WebRtc_UWord32 *noise_estimate, - WebRtc_UWord32 *noise_estimate_avg) -{ - WebRtc_Word32 tmp32no1 = 0; - WebRtc_Word32 tmp32no2 = 0; - - WebRtc_Word16 int_part = 0; - WebRtc_Word16 frac_part = 0; - - // Use pink noise estimate - // noise_estimate = 2^(pinkNoiseNumerator + pinkNoiseExp * log2(j)) - assert(freq_index > 0); - tmp32no2 = WEBRTC_SPL_MUL_16_16(pink_noise_exp_avg, kLogIndex[freq_index]); // Q26 - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 15); // Q11 - tmp32no1 = pink_noise_num_avg - tmp32no2; // Q11 - - // Calculate output: 2^tmp32no1 - // Output in Q(minNorm-stages) - tmp32no1 += WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)(inst->minNorm - inst->stages), 11); - if (tmp32no1 > 0) - { - int_part = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 11); - frac_part = (WebRtc_Word16)(tmp32no1 & 0x000007ff); // Q11 - // Piecewise linear approximation of 'b' in - // 2^(int_part+frac_part) = 2^int_part * (1 + b) - // 'b' is given in Q11 and below stored in frac_part. - if (WEBRTC_SPL_RSHIFT_W32(frac_part, 10)) - { - // Upper fractional part - tmp32no2 = WEBRTC_SPL_MUL_32_16(2048 - frac_part, 1244); // Q21 - tmp32no2 = 2048 - WEBRTC_SPL_RSHIFT_W32(tmp32no2, 10); - } - else - { - // Lower fractional part - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(frac_part, 804), 10); - } - // Shift fractional part to Q(minNorm-stages) - tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, int_part - 11); - *noise_estimate_avg = WEBRTC_SPL_LSHIFT_U32(1, int_part) + (WebRtc_UWord32)tmp32no2; - // Scale up to initMagnEst, which is not block averaged - *noise_estimate = (*noise_estimate_avg) * (WebRtc_UWord32)(inst->blockIndex + 1); - } -} - -// Initialize state -WebRtc_Word32 WebRtcNsx_InitCore(NsxInst_t *inst, WebRtc_UWord32 fs) -{ - int i; - - //check for valid pointer - if (inst == NULL) - { - return -1; - } - // - - // Initialization of struct - if (fs == 8000 || fs == 16000 || fs == 32000) - { - inst->fs = fs; - } else - { - return -1; - } - - if (fs == 8000) - { - inst->blockLen10ms = 80; - inst->anaLen = 128; - inst->stages = 7; - inst->window = kBlocks80w128x; - inst->thresholdLogLrt = 131072; //default threshold for LRT feature - inst->maxLrt = 0x0040000; - inst->minLrt = 52429; - } else if (fs == 16000) - { - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->stages = 8; - inst->window = kBlocks160w256x; - inst->thresholdLogLrt = 212644; //default threshold for LRT feature - inst->maxLrt = 0x0080000; - inst->minLrt = 104858; - } else if (fs == 32000) - { - inst->blockLen10ms = 160; - inst->anaLen = 256; - inst->stages = 8; - inst->window = kBlocks160w256x; - inst->thresholdLogLrt = 212644; //default threshold for LRT feature - inst->maxLrt = 0x0080000; - inst->minLrt = 104858; - } - inst->anaLen2 = WEBRTC_SPL_RSHIFT_W16(inst->anaLen, 1); - inst->magnLen = inst->anaLen2 + 1; - - WebRtcSpl_ZerosArrayW16(inst->analysisBuffer, ANAL_BLOCKL_MAX); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer, ANAL_BLOCKL_MAX); - - // for HB processing - WebRtcSpl_ZerosArrayW16(inst->dataBufHBFX, ANAL_BLOCKL_MAX); - // for quantile noise estimation - WebRtcSpl_ZerosArrayW16(inst->noiseEstQuantile, HALF_ANAL_BLOCKL); - for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) - { - inst->noiseEstLogQuantile[i] = 2048; // Q8 - inst->noiseEstDensity[i] = 153; // Q9 - } - for (i = 0; i < SIMULT; i++) - { - inst->noiseEstCounter[i] = (WebRtc_Word16)(END_STARTUP_LONG * (i + 1)) / SIMULT; - } - - // Initialize suppression filter with ones - WebRtcSpl_MemSetW16((WebRtc_Word16*)inst->noiseSupFilter, 16384, HALF_ANAL_BLOCKL); - - // Set the aggressiveness: default - inst->aggrMode = 0; - - //initialize variables for new method - inst->priorNonSpeechProb = 8192; // Q14(0.5) prior probability for speech/noise - for (i = 0; i < HALF_ANAL_BLOCKL; i++) - { - inst->prevMagnU16[i] = 0; - inst->prevNoiseU32[i] = 0; //previous noise-spectrum - inst->logLrtTimeAvgW32[i] = 0; //smooth LR ratio - inst->avgMagnPause[i] = 0; //conservative noise spectrum estimate - inst->initMagnEst[i] = 0; //initial average magnitude spectrum - } - - //feature quantities - inst->thresholdSpecDiff = 50; //threshold for difference feature: determined on-line - inst->thresholdSpecFlat = 20480; //threshold for flatness: determined on-line - inst->featureLogLrt = inst->thresholdLogLrt; //average LRT factor (= threshold) - inst->featureSpecFlat = inst->thresholdSpecFlat; //spectral flatness (= threshold) - inst->featureSpecDiff = inst->thresholdSpecDiff; //spectral difference (= threshold) - inst->weightLogLrt = 6; //default weighting par for LRT feature - inst->weightSpecFlat = 0; //default weighting par for spectral flatness feature - inst->weightSpecDiff = 0; //default weighting par for spectral difference feature - - inst->curAvgMagnEnergy = 0; //window time-average of input magnitude spectrum - inst->timeAvgMagnEnergy = 0; //normalization for spectral difference - inst->timeAvgMagnEnergyTmp = 0; //normalization for spectral difference - - //histogram quantities: used to estimate/update thresholds for features - WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST); - - inst->blockIndex = -1; //frame counter - - //inst->modelUpdate = 500; //window for update - inst->modelUpdate = (1 << STAT_UPDATES); //window for update - inst->cntThresUpdate = 0; //counter feature thresholds updates - - inst->sumMagn = 0; - inst->magnEnergy = 0; - inst->prevQMagn = 0; - inst->qNoise = 0; - inst->prevQNoise = 0; - - inst->energyIn = 0; - inst->scaleEnergyIn = 0; - - inst->whiteNoiseLevel = 0; - inst->pinkNoiseNumerator = 0; - inst->pinkNoiseExp = 0; - inst->minNorm = 15; // Start with full scale - inst->zeroInputSignal = 0; - - //default mode - WebRtcNsx_set_policy_core(inst, 0); - -#ifdef NS_FILEDEBUG - inst->infile=fopen("indebug.pcm","wb"); - inst->outfile=fopen("outdebug.pcm","wb"); - inst->file1=fopen("file1.pcm","wb"); - inst->file2=fopen("file2.pcm","wb"); - inst->file3=fopen("file3.pcm","wb"); - inst->file4=fopen("file4.pcm","wb"); - inst->file5=fopen("file5.pcm","wb"); -#endif - - inst->initFlag = 1; - - return 0; -} - -int WebRtcNsx_set_policy_core(NsxInst_t *inst, int mode) -{ - // allow for modes:0,1,2,3 - if (mode < 0 || mode > 3) - { - return -1; - } - - inst->aggrMode = mode; - if (mode == 0) - { - inst->overdrive = 256; // Q8(1.0) - inst->denoiseBound = 8192; // Q14(0.5) - inst->gainMap = 0; // No gain compensation - } else if (mode == 1) - { - inst->overdrive = 256; // Q8(1.0) - inst->denoiseBound = 4096; // Q14(0.25) - inst->factor2Table = kFactor2Aggressiveness1; - inst->gainMap = 1; - } else if (mode == 2) - { - inst->overdrive = 282; // ~= Q8(1.1) - inst->denoiseBound = 2048; // Q14(0.125) - inst->factor2Table = kFactor2Aggressiveness2; - inst->gainMap = 1; - } else if (mode == 3) - { - inst->overdrive = 320; // Q8(1.25) - inst->denoiseBound = 1475; // ~= Q14(0.09) - inst->factor2Table = kFactor2Aggressiveness3; - inst->gainMap = 1; - } - return 0; -} - -void WebRtcNsx_NoiseEstimation(NsxInst_t *inst, WebRtc_UWord16 *magn, WebRtc_UWord32 *noise, - WebRtc_Word16 *qNoise) -{ - WebRtc_Word32 numerator; - - WebRtc_Word16 lmagn[HALF_ANAL_BLOCKL], counter, countDiv, countProd, delta, zeros, frac; - WebRtc_Word16 log2, tabind, logval, tmp16, tmp16no1, tmp16no2; - WebRtc_Word16 log2Const = 22713; // Q15 - WebRtc_Word16 widthFactor = 21845; - - int i, s, offset; - - numerator = FACTOR_Q16; - - tabind = inst->stages - inst->normData; - if (tabind < 0) - { - logval = -kLogTable[-tabind]; - } else - { - logval = kLogTable[tabind]; - } - - // lmagn(i)=log(magn(i))=log(2)*log2(magn(i)) - // magn is in Q(-stages), and the real lmagn values are: - // real_lmagn(i)=log(magn(i)*2^stages)=log(magn(i))+log(2^stages) - // lmagn in Q8 - for (i = 0; i < inst->magnLen; i++) - { - if (magn[i]) - { - zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magn[i]); - frac = (WebRtc_Word16)((((WebRtc_UWord32)magn[i] << zeros) & 0x7FFFFFFF) >> 23); - // log2(magn(i)) - log2 = (WebRtc_Word16)(((31 - zeros) << 8) + kLogTableFrac[frac]); - // log2(magn(i))*log(2) - lmagn[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(log2, log2Const, 15); - // + log(2^stages) - lmagn[i] += logval; - } else - { - lmagn[i] = logval;//0; - } - } - - // loop over simultaneous estimates - for (s = 0; s < SIMULT; s++) - { - offset = s * inst->magnLen; - - // Get counter values from state - counter = inst->noiseEstCounter[s]; - countDiv = kCounterDiv[counter]; - countProd = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16(counter, countDiv); - - // quant_est(...) - for (i = 0; i < inst->magnLen; i++) - { - // compute delta - if (inst->noiseEstDensity[offset + i] > 512) - { - delta = WebRtcSpl_DivW32W16ResW16(numerator, - inst->noiseEstDensity[offset + i]); - } else - { - delta = FACTOR_Q7; - } - - // update log quantile estimate - tmp16 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(delta, countDiv, 14); - if (lmagn[i] > inst->noiseEstLogQuantile[offset + i]) - { - // +=QUANTILE*delta/(inst->counter[s]+1) QUANTILE=0.25, =1 in Q2 - // CounterDiv=1/inst->counter[s] in Q15 - tmp16 += 2; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 2); - inst->noiseEstLogQuantile[offset + i] += tmp16no1; - } else - { - tmp16 += 1; - tmp16no1 = WEBRTC_SPL_RSHIFT_W16(tmp16, 1); - // *(1-QUANTILE), in Q2 QUANTILE=0.25, 1-0.25=0.75=3 in Q2 - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, 3, 1); - inst->noiseEstLogQuantile[offset + i] -= tmp16no2; - } - - // update density estimate - if (WEBRTC_SPL_ABS_W16(lmagn[i] - inst->noiseEstLogQuantile[offset + i]) - < WIDTH_Q8) - { - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND( - inst->noiseEstDensity[offset + i], countProd, 15); - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(widthFactor, - countDiv, 15); - inst->noiseEstDensity[offset + i] = tmp16no1 + tmp16no2; - } - } // end loop over magnitude spectrum - - if (counter >= END_STARTUP_LONG) - { - inst->noiseEstCounter[s] = 0; - if (inst->blockIndex >= END_STARTUP_LONG) - { - WebRtcNsx_UpdateNoiseEstimate(inst, offset); - } - } - inst->noiseEstCounter[s]++; - - } // end loop over simultaneous estimates - - // Sequentially update the noise during startup - if (inst->blockIndex < END_STARTUP_LONG) - { - WebRtcNsx_UpdateNoiseEstimate(inst, offset); - } - - for (i = 0; i < inst->magnLen; i++) - { - noise[i] = (WebRtc_UWord32)(inst->noiseEstQuantile[i]); // Q(qNoise) - } - (*qNoise) = (WebRtc_Word16)inst->qNoise; -} - -// Extract thresholds for feature parameters -// histograms are computed over some window_size (given by window_pars) -// thresholds and weights are extracted every window -// flag 0 means update histogram only, flag 1 means compute the thresholds/weights -// threshold and weights are returned in: inst->priorModelPars -void WebRtcNsx_FeatureParameterExtraction(NsxInst_t *inst, int flag) -{ - WebRtc_UWord32 tmpU32; - WebRtc_UWord32 histIndex; - WebRtc_UWord32 posPeak1SpecFlatFX, posPeak2SpecFlatFX; - WebRtc_UWord32 posPeak1SpecDiffFX, posPeak2SpecDiffFX; - - WebRtc_Word32 tmp32; - WebRtc_Word32 fluctLrtFX, thresFluctLrtFX; - WebRtc_Word32 avgHistLrtFX, avgSquareHistLrtFX, avgHistLrtComplFX; - - WebRtc_Word16 j; - WebRtc_Word16 numHistLrt; - - int i; - int useFeatureSpecFlat, useFeatureSpecDiff, featureSum; - int maxPeak1, maxPeak2; - int weightPeak1SpecFlat, weightPeak2SpecFlat; - int weightPeak1SpecDiff, weightPeak2SpecDiff; - - //update histograms - if (!flag) - { - // LRT - // Type casting to UWord32 is safe since negative values will not be wrapped to larger - // values than HIST_PAR_EST - histIndex = (WebRtc_UWord32)(inst->featureLogLrt); - if (histIndex < HIST_PAR_EST) - { - inst->histLrt[histIndex]++; - } - // Spectral flatness - // (inst->featureSpecFlat*20)>>10 = (inst->featureSpecFlat*5)>>8 - histIndex = WEBRTC_SPL_RSHIFT_U32(inst->featureSpecFlat * 5, 8); - if (histIndex < HIST_PAR_EST) - { - inst->histSpecFlat[histIndex]++; - } - // Spectral difference - histIndex = HIST_PAR_EST; - if (inst->timeAvgMagnEnergy) - { - // Guard against division by zero - // If timeAvgMagnEnergy == 0 we have no normalizing statistics and therefore can't - // update the histogram - histIndex = WEBRTC_SPL_UDIV((inst->featureSpecDiff * 5) >> inst->stages, - inst->timeAvgMagnEnergy); - } - if (histIndex < HIST_PAR_EST) - { - inst->histSpecDiff[histIndex]++; - } - } - - // extract parameters for speech/noise probability - if (flag) - { - useFeatureSpecDiff = 1; - //for LRT feature: - // compute the average over inst->featureExtractionParams.rangeAvgHistLrt - avgHistLrtFX = 0; - avgSquareHistLrtFX = 0; - numHistLrt = 0; - for (i = 0; i < BIN_SIZE_LRT; i++) - { - j = (2 * i + 1); - tmp32 = WEBRTC_SPL_MUL_16_16(inst->histLrt[i], j); - avgHistLrtFX += tmp32; - numHistLrt += inst->histLrt[i]; - avgSquareHistLrtFX += WEBRTC_SPL_MUL_32_16(tmp32, j); - } - avgHistLrtComplFX = avgHistLrtFX; - for (; i < HIST_PAR_EST; i++) - { - j = (2 * i + 1); - tmp32 = WEBRTC_SPL_MUL_16_16(inst->histLrt[i], j); - avgHistLrtComplFX += tmp32; - avgSquareHistLrtFX += WEBRTC_SPL_MUL_32_16(tmp32, j); - } - fluctLrtFX = WEBRTC_SPL_MUL(avgSquareHistLrtFX, numHistLrt); - fluctLrtFX -= WEBRTC_SPL_MUL(avgHistLrtFX, avgHistLrtComplFX); - thresFluctLrtFX = THRES_FLUCT_LRT * numHistLrt; - // get threshold for LRT feature: - tmpU32 = (FACTOR_1_LRT_DIFF * (WebRtc_UWord32)avgHistLrtFX); - if ((fluctLrtFX < thresFluctLrtFX) || (numHistLrt == 0) || (tmpU32 - > (WebRtc_UWord32)(100 * numHistLrt))) - { - inst->thresholdLogLrt = inst->maxLrt; //very low fluctuation, so likely noise - } else - { - tmp32 = (WebRtc_Word32)((tmpU32 << (9 + inst->stages)) / numHistLrt / 25); - // check if value is within min/max range - inst->thresholdLogLrt = WEBRTC_SPL_SAT(inst->maxLrt, tmp32, inst->minLrt); - } - if (fluctLrtFX < thresFluctLrtFX) - { - // Do not use difference feature if fluctuation of LRT feature is very low: - // most likely just noise state - useFeatureSpecDiff = 0; - } - - // for spectral flatness and spectral difference: compute the main peaks of histogram - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecFlatFX = 0; - posPeak2SpecFlatFX = 0; - weightPeak1SpecFlat = 0; - weightPeak2SpecFlat = 0; - - // peaks for flatness - for (i = 0; i < HIST_PAR_EST; i++) - { - if (inst->histSpecFlat[i] > maxPeak1) - { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecFlat = weightPeak1SpecFlat; - posPeak2SpecFlatFX = posPeak1SpecFlatFX; - - maxPeak1 = inst->histSpecFlat[i]; - weightPeak1SpecFlat = inst->histSpecFlat[i]; - posPeak1SpecFlatFX = (WebRtc_UWord32)(2 * i + 1); - } else if (inst->histSpecFlat[i] > maxPeak2) - { - // Found new "second" peak - maxPeak2 = inst->histSpecFlat[i]; - weightPeak2SpecFlat = inst->histSpecFlat[i]; - posPeak2SpecFlatFX = (WebRtc_UWord32)(2 * i + 1); - } - } - - // for spectral flatness feature - useFeatureSpecFlat = 1; - // merge the two peaks if they are close - if ((posPeak1SpecFlatFX - posPeak2SpecFlatFX < LIM_PEAK_SPACE_FLAT_DIFF) - && (weightPeak2SpecFlat * LIM_PEAK_WEIGHT_FLAT_DIFF > weightPeak1SpecFlat)) - { - weightPeak1SpecFlat += weightPeak2SpecFlat; - posPeak1SpecFlatFX = (posPeak1SpecFlatFX + posPeak2SpecFlatFX) >> 1; - } - //reject if weight of peaks is not large enough, or peak value too small - if (weightPeak1SpecFlat < THRES_WEIGHT_FLAT_DIFF || posPeak1SpecFlatFX - < THRES_PEAK_FLAT) - { - useFeatureSpecFlat = 0; - } else // if selected, get the threshold - { - // compute the threshold and check if value is within min/max range - inst->thresholdSpecFlat = WEBRTC_SPL_SAT(MAX_FLAT_Q10, FACTOR_2_FLAT_Q10 - * posPeak1SpecFlatFX, MIN_FLAT_Q10); //Q10 - } - // done with flatness feature - - if (useFeatureSpecDiff) - { - //compute two peaks for spectral difference - maxPeak1 = 0; - maxPeak2 = 0; - posPeak1SpecDiffFX = 0; - posPeak2SpecDiffFX = 0; - weightPeak1SpecDiff = 0; - weightPeak2SpecDiff = 0; - // peaks for spectral difference - for (i = 0; i < HIST_PAR_EST; i++) - { - if (inst->histSpecDiff[i] > maxPeak1) - { - // Found new "first" peak - maxPeak2 = maxPeak1; - weightPeak2SpecDiff = weightPeak1SpecDiff; - posPeak2SpecDiffFX = posPeak1SpecDiffFX; - - maxPeak1 = inst->histSpecDiff[i]; - weightPeak1SpecDiff = inst->histSpecDiff[i]; - posPeak1SpecDiffFX = (WebRtc_UWord32)(2 * i + 1); - } else if (inst->histSpecDiff[i] > maxPeak2) - { - // Found new "second" peak - maxPeak2 = inst->histSpecDiff[i]; - weightPeak2SpecDiff = inst->histSpecDiff[i]; - posPeak2SpecDiffFX = (WebRtc_UWord32)(2 * i + 1); - } - } - - // merge the two peaks if they are close - if ((posPeak1SpecDiffFX - posPeak2SpecDiffFX < LIM_PEAK_SPACE_FLAT_DIFF) - && (weightPeak2SpecDiff * LIM_PEAK_WEIGHT_FLAT_DIFF > weightPeak1SpecDiff)) - { - weightPeak1SpecDiff += weightPeak2SpecDiff; - posPeak1SpecDiffFX = (posPeak1SpecDiffFX + posPeak2SpecDiffFX) >> 1; - } - // get the threshold value and check if value is within min/max range - inst->thresholdSpecDiff = WEBRTC_SPL_SAT(MAX_DIFF, FACTOR_1_LRT_DIFF - * posPeak1SpecDiffFX, MIN_DIFF); //5x bigger - //reject if weight of peaks is not large enough - if (weightPeak1SpecDiff < THRES_WEIGHT_FLAT_DIFF) - { - useFeatureSpecDiff = 0; - } - // done with spectral difference feature - } - - // select the weights between the features - // inst->priorModelPars[4] is weight for LRT: always selected - featureSum = 6 / (1 + useFeatureSpecFlat + useFeatureSpecDiff); - inst->weightLogLrt = featureSum; - inst->weightSpecFlat = useFeatureSpecFlat * featureSum; - inst->weightSpecDiff = useFeatureSpecDiff * featureSum; - - // set histograms to zero for next update - WebRtcSpl_ZerosArrayW16(inst->histLrt, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecDiff, HIST_PAR_EST); - WebRtcSpl_ZerosArrayW16(inst->histSpecFlat, HIST_PAR_EST); - } // end of flag == 1 -} - - -// Compute spectral flatness on input spectrum -// magn is the magnitude spectrum -// spectral flatness is returned in inst->featureSpecFlat -void WebRtcNsx_ComputeSpectralFlatness(NsxInst_t *inst, WebRtc_UWord16 *magn) -{ - WebRtc_UWord32 tmpU32; - WebRtc_UWord32 avgSpectralFlatnessNum, avgSpectralFlatnessDen; - - WebRtc_Word32 tmp32; - WebRtc_Word32 currentSpectralFlatness, logCurSpectralFlatness; - - WebRtc_Word16 zeros, frac, intPart; - - int i; - - // for flatness - avgSpectralFlatnessNum = 0; - avgSpectralFlatnessDen = inst->sumMagn - (WebRtc_UWord32)magn[0]; // Q(normData-stages) - - // compute log of ratio of the geometric to arithmetic mean: check for log(0) case - // flatness = exp( sum(log(magn[i]))/N - log(sum(magn[i])/N) ) - // = exp( sum(log(magn[i]))/N ) * N / sum(magn[i]) - // = 2^( sum(log2(magn[i]))/N - (log2(sum(magn[i])) - log2(N)) ) [This is used] - for (i = 1; i < inst->magnLen; i++) - { - // First bin is excluded from spectrum measures. Number of bins is now a power of 2 - if (magn[i]) - { - zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magn[i]); - frac = (WebRtc_Word16)(((WebRtc_UWord32)((WebRtc_UWord32)(magn[i]) << zeros) - & 0x7FFFFFFF) >> 23); - // log2(magn(i)) - tmpU32 = (WebRtc_UWord32)(((31 - zeros) << 8) + kLogTableFrac[frac]); // Q8 - avgSpectralFlatnessNum += tmpU32; // Q8 - } else - { - //if at least one frequency component is zero, treat separately - tmpU32 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecFlat, SPECT_FLAT_TAVG_Q14); // Q24 - inst->featureSpecFlat -= WEBRTC_SPL_RSHIFT_U32(tmpU32, 14); // Q10 - return; - } - } - //ratio and inverse log: check for case of log(0) - zeros = WebRtcSpl_NormU32(avgSpectralFlatnessDen); - frac = (WebRtc_Word16)(((avgSpectralFlatnessDen << zeros) & 0x7FFFFFFF) >> 23); - // log2(avgSpectralFlatnessDen) - tmp32 = (WebRtc_Word32)(((31 - zeros) << 8) + kLogTableFrac[frac]); // Q8 - logCurSpectralFlatness = (WebRtc_Word32)avgSpectralFlatnessNum; - logCurSpectralFlatness += ((WebRtc_Word32)(inst->stages - 1) << (inst->stages + 7)); // Q(8+stages-1) - logCurSpectralFlatness -= (tmp32 << (inst->stages - 1)); - logCurSpectralFlatness = WEBRTC_SPL_LSHIFT_W32(logCurSpectralFlatness, 10 - inst->stages); // Q17 - tmp32 = (WebRtc_Word32)(0x00020000 | (WEBRTC_SPL_ABS_W32(logCurSpectralFlatness) - & 0x0001FFFF)); //Q17 - intPart = -(WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(logCurSpectralFlatness, 17); - intPart += 7; // Shift 7 to get the output in Q10 (from Q17 = -17+10) - if (intPart > 0) - { - currentSpectralFlatness = WEBRTC_SPL_RSHIFT_W32(tmp32, intPart); - } else - { - currentSpectralFlatness = WEBRTC_SPL_LSHIFT_W32(tmp32, -intPart); - } - - //time average update of spectral flatness feature - tmp32 = currentSpectralFlatness - (WebRtc_Word32)inst->featureSpecFlat; // Q10 - tmp32 = WEBRTC_SPL_MUL_32_16(SPECT_FLAT_TAVG_Q14, tmp32); // Q24 - inst->featureSpecFlat = (WebRtc_UWord32)((WebRtc_Word32)inst->featureSpecFlat - + WEBRTC_SPL_RSHIFT_W32(tmp32, 14)); // Q10 - // done with flatness feature -} - - -// Compute the difference measure between input spectrum and a template/learned noise spectrum -// magn_tmp is the input spectrum -// the reference/template spectrum is inst->magn_avg_pause[i] -// returns (normalized) spectral difference in inst->featureSpecDiff -void WebRtcNsx_ComputeSpectralDifference(NsxInst_t *inst, WebRtc_UWord16 *magnIn) -{ - // This is to be calculated: - // avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 / var(magnAvgPause) - - WebRtc_UWord32 tmpU32no1, tmpU32no2; - WebRtc_UWord32 varMagnUFX, varPauseUFX, avgDiffNormMagnUFX; - - WebRtc_Word32 tmp32no1, tmp32no2; - WebRtc_Word32 avgPauseFX, avgMagnFX, covMagnPauseFX; - WebRtc_Word32 maxPause, minPause; - - WebRtc_Word16 tmp16no1; - - int i, norm32, nShifts; - - avgPauseFX = 0; - maxPause = 0; - minPause = inst->avgMagnPause[0]; // Q(prevQMagn) - // compute average quantities - for (i = 0; i < inst->magnLen; i++) - { - // Compute mean of magn_pause - avgPauseFX += inst->avgMagnPause[i]; // in Q(prevQMagn) - maxPause = WEBRTC_SPL_MAX(maxPause, inst->avgMagnPause[i]); - minPause = WEBRTC_SPL_MIN(minPause, inst->avgMagnPause[i]); - } - // normalize by replacing div of "inst->magnLen" with "inst->stages-1" shifts - avgPauseFX = WEBRTC_SPL_RSHIFT_W32(avgPauseFX, inst->stages - 1); - avgMagnFX = (WebRtc_Word32)WEBRTC_SPL_RSHIFT_U32(inst->sumMagn, inst->stages - 1); - // Largest possible deviation in magnPause for (co)var calculations - tmp32no1 = WEBRTC_SPL_MAX(maxPause - avgPauseFX, avgPauseFX - minPause); - // Get number of shifts to make sure we don't get wrap around in varPause - nShifts = WEBRTC_SPL_MAX(0, 10 + inst->stages - WebRtcSpl_NormW32(tmp32no1)); - - varMagnUFX = 0; - varPauseUFX = 0; - covMagnPauseFX = 0; - for (i = 0; i < inst->magnLen; i++) - { - // Compute var and cov of magn and magn_pause - tmp16no1 = (WebRtc_Word16)((WebRtc_Word32)magnIn[i] - avgMagnFX); - tmp32no2 = inst->avgMagnPause[i] - avgPauseFX; - varMagnUFX += (WebRtc_UWord32)WEBRTC_SPL_MUL_16_16(tmp16no1, tmp16no1); // Q(2*qMagn) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16no1); // Q(prevQMagn+qMagn) - covMagnPauseFX += tmp32no1; // Q(prevQMagn+qMagn) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, nShifts); // Q(prevQMagn-minPause) - varPauseUFX += (WebRtc_UWord32)WEBRTC_SPL_MUL(tmp32no1, tmp32no1); // Q(2*(prevQMagn-minPause)) - } - //update of average magnitude spectrum: Q(-2*stages) and averaging replaced by shifts - inst->curAvgMagnEnergy += WEBRTC_SPL_RSHIFT_U32(inst->magnEnergy, 2 * inst->normData - + inst->stages - 1); - - avgDiffNormMagnUFX = varMagnUFX; // Q(2*qMagn) - if ((varPauseUFX) && (covMagnPauseFX)) - { - tmpU32no1 = (WebRtc_UWord32)WEBRTC_SPL_ABS_W32(covMagnPauseFX); // Q(prevQMagn+qMagn) - norm32 = WebRtcSpl_NormU32(tmpU32no1) - 16; - if (norm32 > 0) - { - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, norm32); // Q(prevQMagn+qMagn+norm32) - } else - { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, -norm32); // Q(prevQMagn+qMagn+norm32) - } - tmpU32no2 = WEBRTC_SPL_UMUL(tmpU32no1, tmpU32no1); // Q(2*(prevQMagn+qMagn-norm32)) - - nShifts += norm32; - nShifts <<= 1; - if (nShifts < 0) - { - varPauseUFX >>= (-nShifts); // Q(2*(qMagn+norm32+minPause)) - nShifts = 0; - } - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no2, varPauseUFX); // Q(2*(qMagn+norm32-16+minPause)) - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, nShifts); - - avgDiffNormMagnUFX -= WEBRTC_SPL_MIN(avgDiffNormMagnUFX, tmpU32no1); // Q(2*qMagn) - } - //normalize and compute time average update of difference feature - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(avgDiffNormMagnUFX, 2 * inst->normData); - if (inst->featureSpecDiff > tmpU32no1) - { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(inst->featureSpecDiff - tmpU32no1, - SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff -= WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 8); // Q(-2*stages) - } else - { - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no1 - inst->featureSpecDiff, - SPECT_DIFF_TAVG_Q8); // Q(8-2*stages) - inst->featureSpecDiff += WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 8); // Q(-2*stages) - } -} - -// Compute speech/noise probability -// speech/noise probability is returned in: probSpeechFinal -//snrLocPrior is the prior SNR for each frequency (in Q11) -//snrLocPost is the post SNR for each frequency (in Q11) -void WebRtcNsx_SpeechNoiseProb(NsxInst_t *inst, WebRtc_UWord16 *nonSpeechProbFinal, - WebRtc_UWord32 *priorLocSnr, WebRtc_UWord32 *postLocSnr) -{ - WebRtc_UWord32 zeros, num, den, tmpU32no1, tmpU32no2, tmpU32no3; - - WebRtc_Word32 invLrtFX, indPriorFX, tmp32, tmp32no1, tmp32no2, besselTmpFX32; - WebRtc_Word32 frac32, logTmp; - WebRtc_Word32 logLrtTimeAvgKsumFX; - - WebRtc_Word16 indPriorFX16; - WebRtc_Word16 tmp16, tmp16no1, tmp16no2, tmpIndFX, tableIndex, frac, intPart; - - int i, normTmp, normTmp2, nShifts; - - // compute feature based on average LR factor - // this is the average over all frequencies of the smooth log LRT - logLrtTimeAvgKsumFX = 0; - for (i = 0; i < inst->magnLen; i++) - { - besselTmpFX32 = (WebRtc_Word32)postLocSnr[i]; // Q11 - normTmp = WebRtcSpl_NormU32(postLocSnr[i]); - num = WEBRTC_SPL_LSHIFT_U32(postLocSnr[i], normTmp); // Q(11+normTmp) - if (normTmp > 10) - { - den = WEBRTC_SPL_LSHIFT_U32(priorLocSnr[i], normTmp - 11); // Q(normTmp) - } else - { - den = WEBRTC_SPL_RSHIFT_U32(priorLocSnr[i], 11 - normTmp); // Q(normTmp) - } - besselTmpFX32 -= WEBRTC_SPL_UDIV(num, den); // Q11 - - // inst->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - log(snrLocPrior) - inst->logLrtTimeAvg[i]); - // Here, LRT_TAVG = 0.5 - zeros = WebRtcSpl_NormU32(priorLocSnr[i]); - frac32 = (WebRtc_Word32)(((priorLocSnr[i] << zeros) & 0x7FFFFFFF) >> 19); - tmp32 = WEBRTC_SPL_MUL(frac32, frac32); - tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(tmp32, -43), 19); - tmp32 += WEBRTC_SPL_MUL_16_16_RSFT((WebRtc_Word16)frac32, 5412, 12); - frac32 = tmp32 + 37; - // tmp32 = log2(priorLocSnr[i]) - tmp32 = (WebRtc_Word32)(((31 - zeros) << 12) + frac32) - (11 << 12); // Q12 - logTmp = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_32_16(tmp32, 178), 8); // log2(priorLocSnr[i])*log(2) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(logTmp + inst->logLrtTimeAvgW32[i], 1); // Q12 - inst->logLrtTimeAvgW32[i] += (besselTmpFX32 - tmp32no1); // Q12 - - logLrtTimeAvgKsumFX += inst->logLrtTimeAvgW32[i]; // Q12 - } - inst->featureLogLrt = WEBRTC_SPL_RSHIFT_W32(logLrtTimeAvgKsumFX * 5, inst->stages + 10); // 5 = BIN_SIZE_LRT / 2 - // done with computation of LR factor - - // - //compute the indicator functions - // - - // average LRT feature - // FLOAT code - // indicator0 = 0.5 * (tanh(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) + 1.0); - tmpIndFX = 16384; // Q14(1.0) - tmp32no1 = logLrtTimeAvgKsumFX - inst->thresholdLogLrt; // Q12 - nShifts = 7 - inst->stages; // WIDTH_PR_MAP_SHIFT - inst->stages + 5; - //use larger width in tanh map for pause regions - if (tmp32no1 < 0) - { - tmpIndFX = 0; - tmp32no1 = -tmp32no1; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmp32no1 = WEBRTC_SPL_SHIFT_W32(tmp32no1, nShifts); // Q14 - // compute indicator function: sigmoid map - tableIndex = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 14); - if ((tableIndex < 16) && (tableIndex >= 0)) - { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (WebRtc_Word16)(tmp32no1 & 0x00003fff); // Q14 - tmp16no2 += (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, frac, 14); - if (tmpIndFX == 0) - { - tmpIndFX = 8192 - tmp16no2; // Q14 - } else - { - tmpIndFX = 8192 + tmp16no2; // Q14 - } - } - indPriorFX = WEBRTC_SPL_MUL_16_16(inst->weightLogLrt, tmpIndFX); // 6*Q14 - - //spectral flatness feature - if (inst->weightSpecFlat) - { - tmpU32no1 = WEBRTC_SPL_UMUL(inst->featureSpecFlat, 400); // Q10 - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - tmpU32no2 = inst->thresholdSpecFlat - tmpU32no1; //Q10 - nShifts = 4; - if (inst->thresholdSpecFlat < tmpU32no1) - { - tmpIndFX = 0; - tmpU32no2 = tmpU32no1 - inst->thresholdSpecFlat; - //widthPrior = widthPrior * 2.0; - nShifts++; - } - tmp32no1 = (WebRtc_Word32)WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, - nShifts), 25); //Q14 - tmpU32no1 = WebRtcSpl_DivU32U16(WEBRTC_SPL_LSHIFT_U32(tmpU32no2, nShifts), 25); //Q14 - // compute indicator function: sigmoid map - // FLOAT code - // indicator1 = 0.5 * (tanh(sgnMap * widthPrior * (threshPrior1 - tmpFloat1)) + 1.0); - tableIndex = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); - if (tableIndex < 16) - { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (WebRtc_Word16)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(tmp16no1, frac, 14); - if (tmpIndFX) - { - tmpIndFX = 8192 + tmp16no2; // Q14 - } else - { - tmpIndFX = 8192 - tmp16no2; // Q14 - } - } - indPriorFX += WEBRTC_SPL_MUL_16_16(inst->weightSpecFlat, tmpIndFX); // 6*Q14 - } - - //for template spectral-difference - if (inst->weightSpecDiff) - { - tmpU32no1 = 0; - if (inst->featureSpecDiff) - { - normTmp = WEBRTC_SPL_MIN(20 - inst->stages, - WebRtcSpl_NormU32(inst->featureSpecDiff)); - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(inst->featureSpecDiff, normTmp); // Q(normTmp-2*stages) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(inst->timeAvgMagnEnergy, 20 - inst->stages - - normTmp); - if (tmpU32no2) - { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q14?? Q(20 - inst->stages) - } else - { - tmpU32no1 = (WebRtc_UWord32)(0x7fffffff); - } - } - tmpU32no3 = WEBRTC_SPL_UDIV(WEBRTC_SPL_LSHIFT_U32(inst->thresholdSpecDiff, 17), 25); - tmpU32no2 = tmpU32no1 - tmpU32no3; - nShifts = 1; - tmpIndFX = 16384; // Q14(1.0) - //use larger width in tanh map for pause regions - if (tmpU32no2 & 0x80000000) - { - tmpIndFX = 0; - tmpU32no2 = tmpU32no3 - tmpU32no1; - //widthPrior = widthPrior * 2.0; - nShifts--; - } - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, nShifts); - // compute indicator function: sigmoid map - /* FLOAT code - indicator2 = 0.5 * (tanh(widthPrior * (tmpFloat1 - threshPrior2)) + 1.0); - */ - tableIndex = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 14); - if (tableIndex < 16) - { - tmp16no2 = kIndicatorTable[tableIndex]; - tmp16no1 = kIndicatorTable[tableIndex + 1] - kIndicatorTable[tableIndex]; - frac = (WebRtc_Word16)(tmpU32no1 & 0x00003fff); // Q14 - tmp16no2 += (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16no1, frac, - 14); - if (tmpIndFX) - { - tmpIndFX = 8192 + tmp16no2; - } else - { - tmpIndFX = 8192 - tmp16no2; - } - } - indPriorFX += WEBRTC_SPL_MUL_16_16(inst->weightSpecDiff, tmpIndFX); // 6*Q14 - } - - //combine the indicator function with the feature weights - // FLOAT code - // indPrior = 1 - (weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 + weightIndPrior2 * indicator2); - indPriorFX16 = WebRtcSpl_DivW32W16ResW16(98307 - indPriorFX, 6); // Q14 - // done with computing indicator function - - //compute the prior probability - // FLOAT code - // inst->priorNonSpeechProb += PRIOR_UPDATE * (indPriorNonSpeech - inst->priorNonSpeechProb); - tmp16 = indPriorFX16 - inst->priorNonSpeechProb; // Q14 - inst->priorNonSpeechProb += (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(PRIOR_UPDATE_Q14, - tmp16, 14); // Q14 - - //final speech probability: combine prior model with LR factor: - for (i = 0; i < inst->magnLen; i++) - { - // FLOAT code - // invLrt = exp(inst->logLrtTimeAvg[i]); - // invLrt = inst->priorSpeechProb * invLrt; - // nonSpeechProbFinal[i] = (1.0 - inst->priorSpeechProb) / (1.0 - inst->priorSpeechProb + invLrt); - // invLrt = (1.0 - inst->priorNonSpeechProb) * invLrt; - // nonSpeechProbFinal[i] = inst->priorNonSpeechProb / (inst->priorNonSpeechProb + invLrt); - nonSpeechProbFinal[i] = 0; // Q8 - if ((inst->logLrtTimeAvgW32[i] < 65300) && (inst->priorNonSpeechProb > 0)) - { - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(inst->logLrtTimeAvgW32[i], 23637), - 14); // Q12 - intPart = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_W32(tmp32no1, 12); - if (intPart < -8) - { - intPart = -8; - } - frac = (WebRtc_Word16)(tmp32no1 & 0x00000fff); // Q12 - // Quadratic approximation of 2^frac - tmp32no2 = WEBRTC_SPL_RSHIFT_W32(frac * frac * 44, 19); // Q12 - tmp32no2 += WEBRTC_SPL_MUL_16_16_RSFT(frac, 84, 7); // Q12 - invLrtFX = WEBRTC_SPL_LSHIFT_W32(1, 8 + intPart) - + WEBRTC_SPL_SHIFT_W32(tmp32no2, intPart - 4); // Q8 - - normTmp = WebRtcSpl_NormW32(invLrtFX); - normTmp2 = WebRtcSpl_NormW16((16384 - inst->priorNonSpeechProb)); - if (normTmp + normTmp2 < 15) - { - invLrtFX = WEBRTC_SPL_RSHIFT_W32(invLrtFX, 15 - normTmp2 - normTmp); // Q(normTmp+normTmp2-7) - tmp32no1 = WEBRTC_SPL_MUL_32_16(invLrtFX, (16384 - inst->priorNonSpeechProb)); // Q(normTmp+normTmp2+7) - invLrtFX = WEBRTC_SPL_SHIFT_W32(tmp32no1, 7 - normTmp - normTmp2); // Q14 - } else - { - tmp32no1 = WEBRTC_SPL_MUL_32_16(invLrtFX, (16384 - inst->priorNonSpeechProb)); // Q22 - invLrtFX = WEBRTC_SPL_RSHIFT_W32(tmp32no1, 8); // Q14 - } - - tmp32no1 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)inst->priorNonSpeechProb, 8); // Q22 - nonSpeechProbFinal[i] = (WebRtc_UWord16)WEBRTC_SPL_DIV(tmp32no1, - (WebRtc_Word32)inst->priorNonSpeechProb - + invLrtFX); // Q8 - if (7 - normTmp - normTmp2 > 0) - { - nonSpeechProbFinal[i] = 0; // Q8 - } - } - } -} - -// Transform input (speechFrame) to frequency domain magnitude (magnU16) -void WebRtcNsx_DataAnalysis(NsxInst_t *inst, short *speechFrame, WebRtc_UWord16 *magnU16) -{ - - WebRtc_UWord32 tmpU32no1, tmpU32no2; - - WebRtc_Word32 tmp_1_w32 = 0; - WebRtc_Word32 tmp_2_w32 = 0; - WebRtc_Word32 sum_log_magn = 0; - WebRtc_Word32 sum_log_i_log_magn = 0; - - WebRtc_UWord16 sum_log_magn_u16 = 0; - WebRtc_UWord16 tmp_u16 = 0; - - WebRtc_Word16 sum_log_i = 0; - WebRtc_Word16 sum_log_i_square = 0; - WebRtc_Word16 frac = 0; - WebRtc_Word16 log2 = 0; - WebRtc_Word16 matrix_determinant = 0; - WebRtc_Word16 winData[ANAL_BLOCKL_MAX], maxWinData; - WebRtc_Word16 realImag[ANAL_BLOCKL_MAX << 1]; - - int i, j; - int outCFFT; - int zeros; - int net_norm = 0; - int right_shifts_in_magnU16 = 0; - int right_shifts_in_initMagnEst = 0; - - // For lower band do all processing - // update analysis buffer for L band - WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer, inst->analysisBuffer + inst->blockLen10ms, - inst->anaLen - inst->blockLen10ms); - WEBRTC_SPL_MEMCPY_W16(inst->analysisBuffer + inst->anaLen - inst->blockLen10ms, - speechFrame, inst->blockLen10ms); - - // Window data before FFT - for (i = 0; i < inst->anaLen; i++) - { - winData[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(inst->window[i], - inst->analysisBuffer[i], - 14); // Q0 - } - // Get input energy - inst->energyIn = WebRtcSpl_Energy(winData, (int)inst->anaLen, &(inst->scaleEnergyIn)); - - // Reset zero input flag - inst->zeroInputSignal = 0; - // Acquire norm for winData - maxWinData = WebRtcSpl_MaxAbsValueW16(winData, inst->anaLen); - inst->normData = WebRtcSpl_NormW16(maxWinData); - if (maxWinData == 0) - { - // Treat zero input separately. - inst->zeroInputSignal = 1; - return; - } - - // Determine the net normalization in the frequency domain - net_norm = inst->stages - inst->normData; - // Track lowest normalization factor and use it to prevent wrap around in shifting - right_shifts_in_magnU16 = inst->normData - inst->minNorm; - right_shifts_in_initMagnEst = WEBRTC_SPL_MAX(-right_shifts_in_magnU16, 0); - inst->minNorm -= right_shifts_in_initMagnEst; - right_shifts_in_magnU16 = WEBRTC_SPL_MAX(right_shifts_in_magnU16, 0); - - // create realImag as winData interleaved with zeros (= imag. part), normalize it - for (i = 0; i < inst->anaLen; i++) - { - j = WEBRTC_SPL_LSHIFT_W16(i, 1); - realImag[j] = WEBRTC_SPL_LSHIFT_W16(winData[i], inst->normData); // Q(normData) - realImag[j + 1] = 0; // Insert zeros in imaginary part - } - - // bit-reverse position of elements in array and FFT the array - WebRtcSpl_ComplexBitReverse(realImag, inst->stages); // Q(normData-stages) - outCFFT = WebRtcSpl_ComplexFFT(realImag, inst->stages, 1); - - inst->imag[0] = 0; // Q(normData-stages) - inst->imag[inst->anaLen2] = 0; - inst->real[0] = realImag[0]; // Q(normData-stages) - inst->real[inst->anaLen2] = realImag[inst->anaLen]; - // Q(2*(normData-stages)) - inst->magnEnergy = (WebRtc_UWord32)WEBRTC_SPL_MUL_16_16(inst->real[0], inst->real[0]); - inst->magnEnergy += (WebRtc_UWord32)WEBRTC_SPL_MUL_16_16(inst->real[inst->anaLen2], - inst->real[inst->anaLen2]); - magnU16[0] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(inst->real[0]); // Q(normData-stages) - magnU16[inst->anaLen2] = (WebRtc_UWord16)WEBRTC_SPL_ABS_W16(inst->real[inst->anaLen2]); - inst->sumMagn = (WebRtc_UWord32)magnU16[0]; // Q(normData-stages) - inst->sumMagn += (WebRtc_UWord32)magnU16[inst->anaLen2]; - - // Gather information during startup for noise parameter estimation - if (inst->blockIndex < END_STARTUP_SHORT) - { - // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[0] = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[0], - right_shifts_in_initMagnEst); - inst->initMagnEst[inst->anaLen2] = - WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[inst->anaLen2], - right_shifts_in_initMagnEst); // Q(minNorm-stages) - - // Shift magnU16 to same domain as initMagnEst - tmpU32no1 = WEBRTC_SPL_RSHIFT_W32((WebRtc_UWord32)magnU16[0], - right_shifts_in_magnU16); // Q(minNorm-stages) - tmpU32no2 = WEBRTC_SPL_RSHIFT_W32((WebRtc_UWord32)magnU16[inst->anaLen2], - right_shifts_in_magnU16); // Q(minNorm-stages) - - // Update initMagnEst - inst->initMagnEst[0] += tmpU32no1; // Q(minNorm-stages) - inst->initMagnEst[inst->anaLen2] += tmpU32no2; // Q(minNorm-stages) - - log2 = 0; - if (magnU16[inst->anaLen2]) - { - // Calculate log2(magnU16[inst->anaLen2]) - zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magnU16[inst->anaLen2]); - frac = (WebRtc_Word16)((((WebRtc_UWord32)magnU16[inst->anaLen2] << zeros) & - 0x7FFFFFFF) >> 23); // Q8 - // log2(magnU16(i)) in Q8 - log2 = (WebRtc_Word16)(((31 - zeros) << 8) + kLogTableFrac[frac]); - } - - sum_log_magn = (WebRtc_Word32)log2; // Q8 - // sum_log_i_log_magn in Q17 - sum_log_i_log_magn = (WEBRTC_SPL_MUL_16_16(kLogIndex[inst->anaLen2], log2) >> 3); - } - - for (i = 1; i < inst->anaLen2; i++) - { - j = WEBRTC_SPL_LSHIFT_W16(i, 1); - inst->real[i] = realImag[j]; - inst->imag[i] = -realImag[j + 1]; - // magnitude spectrum - // energy in Q(2*(normData-stages)) - tmpU32no1 = (WebRtc_UWord32)WEBRTC_SPL_MUL_16_16(realImag[j], realImag[j]); - tmpU32no1 += (WebRtc_UWord32)WEBRTC_SPL_MUL_16_16(realImag[j + 1], realImag[j + 1]); - inst->magnEnergy += tmpU32no1; // Q(2*(normData-stages)) - - magnU16[i] = (WebRtc_UWord16)WebRtcSpl_Sqrt(tmpU32no1); // Q(normData-stages) - inst->sumMagn += (WebRtc_UWord32)magnU16[i]; // Q(normData-stages) - if (inst->blockIndex < END_STARTUP_SHORT) - { - // Switch initMagnEst to Q(minNorm-stages) - inst->initMagnEst[i] = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[i], - right_shifts_in_initMagnEst); - - // Shift magnU16 to same domain as initMagnEst, i.e., Q(minNorm-stages) - tmpU32no1 = WEBRTC_SPL_RSHIFT_W32((WebRtc_UWord32)magnU16[i], - right_shifts_in_magnU16); - // Update initMagnEst - inst->initMagnEst[i] += tmpU32no1; // Q(minNorm-stages) - - if (i >= kStartBand) - { - // For pink noise estimation. Collect data neglecting lower frequency band - log2 = 0; - if (magnU16[i]) - { - zeros = WebRtcSpl_NormU32((WebRtc_UWord32)magnU16[i]); - frac = (WebRtc_Word16)((((WebRtc_UWord32)magnU16[i] << zeros) & - 0x7FFFFFFF) >> 23); - // log2(magnU16(i)) in Q8 - log2 = (WebRtc_Word16)(((31 - zeros) << 8) + kLogTableFrac[frac]); - } - sum_log_magn += (WebRtc_Word32)log2; // Q8 - // sum_log_i_log_magn in Q17 - sum_log_i_log_magn += (WEBRTC_SPL_MUL_16_16(kLogIndex[i], log2) >> 3); - } - } - } - - //compute simplified noise model during startup - if (inst->blockIndex < END_STARTUP_SHORT) - { - // Estimate White noise - // Switch whiteNoiseLevel to Q(minNorm-stages) - inst->whiteNoiseLevel = WEBRTC_SPL_RSHIFT_U32(inst->whiteNoiseLevel, - right_shifts_in_initMagnEst); - - // Update the average magnitude spectrum, used as noise estimate. - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(inst->sumMagn, inst->overdrive); - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, inst->stages + 8); - - // Replacing division above with 'stages' shifts - // Shift to same Q-domain as whiteNoiseLevel - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, right_shifts_in_magnU16); - // This operation is safe from wrap around as long as END_STARTUP_SHORT < 128 - assert(END_STARTUP_SHORT < 128); - inst->whiteNoiseLevel += tmpU32no1; // Q(minNorm-stages) - - // Estimate Pink noise parameters - // Denominator used in both parameter estimates. - // The value is only dependent on the size of the frequency band (kStartBand) - // and to reduce computational complexity stored in a table (kDeterminantEstMatrix[]) - matrix_determinant = kDeterminantEstMatrix[kStartBand]; // Q0 - sum_log_i = kSumLogIndex[kStartBand]; // Q5 - sum_log_i_square = kSumSquareLogIndex[kStartBand]; // Q2 - if (inst->fs == 8000) - { - // Adjust values to shorter blocks in narrow band. - tmp_1_w32 = (WebRtc_Word32)matrix_determinant; - tmp_1_w32 += WEBRTC_SPL_MUL_16_16_RSFT(kSumLogIndex[65], sum_log_i, 9); - tmp_1_w32 -= WEBRTC_SPL_MUL_16_16_RSFT(kSumLogIndex[65], kSumLogIndex[65], 10); - tmp_1_w32 -= WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)sum_log_i_square, 4); - tmp_1_w32 -= WEBRTC_SPL_MUL_16_16_RSFT((WebRtc_Word16)(inst->magnLen - - kStartBand), kSumSquareLogIndex[65], 2); - matrix_determinant = (WebRtc_Word16)tmp_1_w32; - sum_log_i -= kSumLogIndex[65]; // Q5 - sum_log_i_square -= kSumSquareLogIndex[65]; // Q2 - } - - // Necessary number of shifts to fit sum_log_magn in a word16 - zeros = 16 - WebRtcSpl_NormW32(sum_log_magn); - if (zeros < 0) - { - zeros = 0; - } - tmp_1_w32 = WEBRTC_SPL_LSHIFT_W32(sum_log_magn, 1); // Q9 - sum_log_magn_u16 = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_W32(tmp_1_w32, zeros);//Q(9-zeros) - - // Calculate and update pinkNoiseNumerator. Result in Q11. - tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i_square, sum_log_magn_u16); // Q(11-zeros) - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32((WebRtc_UWord32)sum_log_i_log_magn, 12); // Q5 - - // Shift the largest value of sum_log_i and tmp32no3 before multiplication - tmp_u16 = WEBRTC_SPL_LSHIFT_U16((WebRtc_UWord16)sum_log_i, 1); // Q6 - if ((WebRtc_UWord32)sum_log_i > tmpU32no1) - { - tmp_u16 = WEBRTC_SPL_RSHIFT_U16(tmp_u16, zeros); - } - else - { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, zeros); - } - tmp_2_w32 -= (WebRtc_Word32)WEBRTC_SPL_UMUL_32_16(tmpU32no1, tmp_u16); // Q(11-zeros) - matrix_determinant = WEBRTC_SPL_RSHIFT_W16(matrix_determinant, zeros); // Q(-zeros) - tmp_2_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant); // Q11 - tmp_2_w32 += WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)net_norm, 11); // Q11 - if (tmp_2_w32 < 0) - { - tmp_2_w32 = 0; - } - inst->pinkNoiseNumerator += tmp_2_w32; // Q11 - - // Calculate and update pinkNoiseExp. Result in Q14. - tmp_2_w32 = WEBRTC_SPL_MUL_16_U16(sum_log_i, sum_log_magn_u16); // Q(14-zeros) - tmp_1_w32 = WEBRTC_SPL_RSHIFT_W32(sum_log_i_log_magn, 3 + zeros); - tmp_1_w32 = WEBRTC_SPL_MUL((WebRtc_Word32)(inst->magnLen - kStartBand), - tmp_1_w32); - tmp_2_w32 -= tmp_1_w32; // Q(14-zeros) - if (tmp_2_w32 > 0) - { - // If the exponential parameter is negative force it to zero, which means a - // flat spectrum. - tmp_1_w32 = WebRtcSpl_DivW32W16(tmp_2_w32, matrix_determinant); // Q14 - inst->pinkNoiseExp += WEBRTC_SPL_SAT(16384, tmp_1_w32, 0); // Q14 - } - } -} - -void WebRtcNsx_DataSynthesis(NsxInst_t *inst, short *outFrame) -{ - WebRtc_Word32 tmp32no1; - WebRtc_Word32 energyOut; - - WebRtc_Word16 realImag[ANAL_BLOCKL_MAX << 1]; - WebRtc_Word16 tmp16no1, tmp16no2; - WebRtc_Word16 energyRatio; - WebRtc_Word16 gainFactor, gainFactor1, gainFactor2; - - int i, j; - int outCIFFT; - int scaleEnergyOut = 0; - - if (inst->zeroInputSignal) - { - // synthesize the special case of zero input - // read out fully processed segment - for (i = 0; i < inst->blockLen10ms; i++) - { - outFrame[i] = inst->synthesisBuffer[i]; // Q0 - } - // update synthesis buffer - WEBRTC_SPL_MEMCPY_W16(inst->synthesisBuffer, - inst->synthesisBuffer + inst->blockLen10ms, - inst->anaLen - inst->blockLen10ms); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms, - inst->blockLen10ms); - return; - } - // Filter the data in the frequency domain - for (i = 0; i < inst->magnLen; i++) - { - inst->real[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->real[i], - (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages) - inst->imag[i] = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->imag[i], - (WebRtc_Word16)(inst->noiseSupFilter[i]), 14); // Q(normData-stages) - } - // back to time domain - // Create spectrum - realImag[0] = inst->real[0]; - realImag[1] = -inst->imag[0]; - for (i = 1; i < inst->anaLen2; i++) - { - j = WEBRTC_SPL_LSHIFT_W16(i, 1); - tmp16no1 = (inst->anaLen << 1) - j; - realImag[j] = inst->real[i]; - realImag[j + 1] = -inst->imag[i]; - realImag[tmp16no1] = inst->real[i]; - realImag[tmp16no1 + 1] = inst->imag[i]; - } - realImag[inst->anaLen] = inst->real[inst->anaLen2]; - realImag[inst->anaLen + 1] = -inst->imag[inst->anaLen2]; - - // bit-reverse position of elements in array and IFFT it - WebRtcSpl_ComplexBitReverse(realImag, inst->stages); - outCIFFT = WebRtcSpl_ComplexIFFT(realImag, inst->stages, 1); - - for (i = 0; i < inst->anaLen; i++) - { - j = WEBRTC_SPL_LSHIFT_W16(i, 1); - tmp32no1 = WEBRTC_SPL_SHIFT_W32((WebRtc_Word32)realImag[j], outCIFFT - inst->normData); - inst->real[i] = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1, - WEBRTC_SPL_WORD16_MIN); - } - - //scale factor: only do it after END_STARTUP_LONG time - gainFactor = 8192; // 8192 = Q13(1.0) - if (inst->gainMap == 1 && - inst->blockIndex > END_STARTUP_LONG && - inst->energyIn > 0) - { - energyOut = WebRtcSpl_Energy(inst->real, (int)inst->anaLen, &scaleEnergyOut); // Q(-scaleEnergyOut) - if (scaleEnergyOut == 0 && !(energyOut & 0x7f800000)) - { - energyOut = WEBRTC_SPL_SHIFT_W32(energyOut, 8 + scaleEnergyOut - - inst->scaleEnergyIn); - } else - { - inst->energyIn = WEBRTC_SPL_RSHIFT_W32(inst->energyIn, 8 + scaleEnergyOut - - inst->scaleEnergyIn); // Q(-8-scaleEnergyOut) - } - - assert(inst->energyIn > 0); - energyRatio = (WebRtc_Word16)WEBRTC_SPL_DIV(energyOut - + WEBRTC_SPL_RSHIFT_W32(inst->energyIn, 1), inst->energyIn); // Q8 - - // // original FLOAT code - // if (gain > blim) { - // factor1=1.0+1.3*(gain-blim); - // if (gain*factor1 > 1.0) { // FLOAT - // factor1 = 1.0/gain; // FLOAT - // } - // } - // else { - // factor1=1.0; // FLOAT - // } - // - // if (gain > blim) { - // factor2=1.0; //FLOAT - // } - // else { - // //don't reduce scale too much for pause regions: attenuation here should be controlled by flooring - // factor2=1.0-0.3*(blim-gain); // FLOAT - // if (gain <= inst->denoiseBound) { - // factor2=1.0-0.3*(blim-inst->denoiseBound); // FLOAT - // } - // } - - // all done in lookup tables now - gainFactor1 = kFactor1Table[energyRatio]; // Q8 - gainFactor2 = inst->factor2Table[energyRatio]; // Q8 - - //combine both scales with speech/noise prob: note prior (priorSpeechProb) is not frequency dependent - - // factor = inst->priorSpeechProb*factor1 + (1.0-inst->priorSpeechProb)*factor2; // original code - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(16384 - inst->priorNonSpeechProb, - gainFactor1, 14); // Q13 16384 = Q14(1.0) - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(inst->priorNonSpeechProb, - gainFactor2, 14); // Q13; - gainFactor = tmp16no1 + tmp16no2; // Q13 - } // out of flag_gain_map==1 - - // synthesis - for (i = 0; i < inst->anaLen; i++) - { - tmp16no1 = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(inst->window[i], - inst->real[i], 14); // Q0, window in Q14 - tmp32no1 = WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(tmp16no1, gainFactor, 13); // Q0 - // Down shift with rounding - tmp16no2 = (WebRtc_Word16)WEBRTC_SPL_SAT(WEBRTC_SPL_WORD16_MAX, tmp32no1, - WEBRTC_SPL_WORD16_MIN); // Q0 - inst->synthesisBuffer[i] = WEBRTC_SPL_ADD_SAT_W16(inst->synthesisBuffer[i], tmp16no2); // Q0 - } - - // read out fully processed segment - for (i = 0; i < inst->blockLen10ms; i++) - { - outFrame[i] = inst->synthesisBuffer[i]; // Q0 - } - // update synthesis buffer - WEBRTC_SPL_MEMCPY_W16(inst->synthesisBuffer, inst->synthesisBuffer + inst->blockLen10ms, - inst->anaLen - inst->blockLen10ms); - WebRtcSpl_ZerosArrayW16(inst->synthesisBuffer + inst->anaLen - inst->blockLen10ms, - inst->blockLen10ms); -} - -int WebRtcNsx_ProcessCore(NsxInst_t *inst, short *speechFrame, short *speechFrameHB, - short *outFrame, short *outFrameHB) -{ - // main routine for noise suppression - - WebRtc_UWord32 tmpU32no1, tmpU32no2, tmpU32no3; - WebRtc_UWord32 satMax, maxNoiseU32; - WebRtc_UWord32 tmpMagnU32, tmpNoiseU32; - WebRtc_UWord32 nearMagnEst; - WebRtc_UWord32 noiseUpdateU32; - WebRtc_UWord32 noiseU32[HALF_ANAL_BLOCKL]; - WebRtc_UWord32 postLocSnr[HALF_ANAL_BLOCKL]; - WebRtc_UWord32 priorLocSnr[HALF_ANAL_BLOCKL]; - WebRtc_UWord32 prevNearSnr[HALF_ANAL_BLOCKL]; - WebRtc_UWord32 curNearSnr; - WebRtc_UWord32 priorSnr; - WebRtc_UWord32 noise_estimate = 0; - WebRtc_UWord32 noise_estimate_avg = 0; - WebRtc_UWord32 numerator = 0; - - WebRtc_Word32 tmp32no1, tmp32no2; - WebRtc_Word32 pink_noise_num_avg = 0; - - WebRtc_UWord16 tmpU16no1; - WebRtc_UWord16 magnU16[HALF_ANAL_BLOCKL]; - WebRtc_UWord16 prevNoiseU16[HALF_ANAL_BLOCKL]; - WebRtc_UWord16 nonSpeechProbFinal[HALF_ANAL_BLOCKL]; - WebRtc_UWord16 gammaNoise, prevGammaNoise; - WebRtc_UWord16 noiseSupFilterTmp[HALF_ANAL_BLOCKL]; - - WebRtc_Word16 qMagn, qNoise; - WebRtc_Word16 avgProbSpeechHB, gainModHB, avgFilterGainHB, gainTimeDomainHB; - WebRtc_Word16 tmp16no1; - WebRtc_Word16 int_part = 0; - WebRtc_Word16 frac_part = 0; - WebRtc_Word16 pink_noise_exp_avg = 0; - - int i; - int nShifts, postShifts; - int norm32no1, norm32no2; - int flag, sign; - int q_domain_to_use = 0; - -#ifdef NS_FILEDEBUG - fwrite(spframe, sizeof(short), inst->blockLen10ms, inst->infile); -#endif - - // Check that initialization has been done - if (inst->initFlag != 1) - { - return -1; - } - // Check for valid pointers based on sampling rate - if ((inst->fs == 32000) && (speechFrameHB == NULL)) - { - return -1; - } - - // Update block index - inst->blockIndex++; - // - - // Store speechFrame and transform to frequency domain - WebRtcNsx_DataAnalysis(inst, speechFrame, magnU16); - - if (inst->zeroInputSignal) - { - WebRtcNsx_DataSynthesis(inst, outFrame); - - if (inst->fs == 32000) - { - // update analysis buffer for H band - // append new data to buffer FX - WEBRTC_SPL_MEMCPY_W16(inst->dataBufHBFX, inst->dataBufHBFX + inst->blockLen10ms, - inst->anaLen - inst->blockLen10ms); - WEBRTC_SPL_MEMCPY_W16(inst->dataBufHBFX + inst->anaLen - inst->blockLen10ms, - speechFrameHB, inst->blockLen10ms); - for (i = 0; i < inst->blockLen10ms; i++) - { - outFrameHB[i] = inst->dataBufHBFX[i]; // Q0 - } - } // end of H band gain computation - return 0; - } - - // Norm of magn - qMagn = inst->normData - inst->stages; - - // Compute spectral flatness on input spectrum - WebRtcNsx_ComputeSpectralFlatness(inst, magnU16); - - // quantile noise estimate - WebRtcNsx_NoiseEstimation(inst, magnU16, noiseU32, &qNoise); - - //noise estimate from previous frame - for (i = 0; i < inst->magnLen; i++) - { - prevNoiseU16[i] = (WebRtc_UWord16)WEBRTC_SPL_RSHIFT_U32(inst->prevNoiseU32[i], 11); // Q(prevQNoise) - } - - if (inst->blockIndex < END_STARTUP_SHORT) - { - // Noise Q-domain to be used later; see description at end of section. - q_domain_to_use = WEBRTC_SPL_MIN((int)qNoise, inst->minNorm - inst->stages); - - // Calculate frequency independent parts in parametric noise estimate and calculate - // the estimate for the lower frequency band (same values for all frequency bins) - if (inst->pinkNoiseExp) - { - pink_noise_exp_avg = (WebRtc_Word16)WebRtcSpl_DivW32W16(inst->pinkNoiseExp, - (WebRtc_Word16)(inst->blockIndex + 1)); // Q14 - pink_noise_num_avg = WebRtcSpl_DivW32W16(inst->pinkNoiseNumerator, - (WebRtc_Word16)(inst->blockIndex + 1)); // Q11 - WebRtcNsx_CalcParametricNoiseEstimate(inst, - pink_noise_exp_avg, - pink_noise_num_avg, - kStartBand, - &noise_estimate, - &noise_estimate_avg); - } - else - { - // Use white noise estimate if we have poor pink noise parameter estimates - noise_estimate = inst->whiteNoiseLevel; // Q(minNorm-stages) - noise_estimate_avg = noise_estimate / (inst->blockIndex + 1); // Q(minNorm-stages) - } - for (i = 0; i < inst->magnLen; i++) - { - // Estimate the background noise using the pink noise parameters if permitted - if ((inst->pinkNoiseExp) && (i >= kStartBand)) - { - // Reset noise_estimate - noise_estimate = 0; - noise_estimate_avg = 0; - // Calculate the parametric noise estimate for current frequency bin - WebRtcNsx_CalcParametricNoiseEstimate(inst, - pink_noise_exp_avg, - pink_noise_num_avg, - i, - &noise_estimate, - &noise_estimate_avg); - } - // Calculate parametric Wiener filter - noiseSupFilterTmp[i] = inst->denoiseBound; - if (inst->initMagnEst[i]) - { - // numerator = (initMagnEst - noise_estimate * overdrive) - // Result in Q(8+minNorm-stages) - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(noise_estimate, inst->overdrive); - numerator = WEBRTC_SPL_LSHIFT_U32(inst->initMagnEst[i], 8); - if (numerator > tmpU32no1) - { - // Suppression filter coefficient larger than zero, so calculate. - numerator -= tmpU32no1; - - // Determine number of left shifts in numerator for best accuracy after - // division - nShifts = WebRtcSpl_NormU32(numerator); - nShifts = WEBRTC_SPL_SAT(6, nShifts, 0); - - // Shift numerator to Q(nShifts+8+minNorm-stages) - numerator = WEBRTC_SPL_LSHIFT_U32(numerator, nShifts); - - // Shift denominator to Q(nShifts-6+minNorm-stages) - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(inst->initMagnEst[i], 6 - nShifts); - tmpU32no2 = WEBRTC_SPL_UDIV(numerator, tmpU32no1); // Q14 - noiseSupFilterTmp[i] = (WebRtc_UWord16)WEBRTC_SPL_SAT(16384, tmpU32no2, - (WebRtc_UWord32)(inst->denoiseBound)); // Q14 - } - } - // Weight quantile noise 'noiseU32' with modeled noise 'noise_estimate_avg' - // 'noiseU32 is in Q(qNoise) and 'noise_estimate' in Q(minNorm-stages) - // To guarantee that we do not get wrap around when shifting to the same domain - // we use the lowest one. Furthermore, we need to save 6 bits for the weighting. - // 'noise_estimate_avg' can handle this operation by construction, but 'noiseU32' - // may not. - - // Shift 'noiseU32' to 'q_domain_to_use' - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], (int)qNoise - q_domain_to_use); - // Shift 'noise_estimate_avg' to 'q_domain_to_use' - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(noise_estimate_avg, inst->minNorm - inst->stages - - q_domain_to_use); - // Make a simple check to see if we have enough room for weighting 'tmpU32no1' - // without wrap around - nShifts = 0; - if (tmpU32no1 & 0xfc000000) { - tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 6); - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); - nShifts = 6; - } - // Add them together and divide by startup length - noiseU32[i] = WebRtcSpl_DivU32U16(tmpU32no1 + tmpU32no2, END_STARTUP_SHORT); - // Shift back if necessary - noiseU32[i] = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], nShifts); - } - // Update new Q-domain for 'noiseU32' - qNoise = q_domain_to_use; - } - // compute average signal during END_STARTUP_LONG time: - // used to normalize spectral difference measure - if (inst->blockIndex < END_STARTUP_LONG) - { - // substituting division with shift ending up in Q(-2*stages) - inst->timeAvgMagnEnergyTmp - += WEBRTC_SPL_RSHIFT_U32(inst->magnEnergy, - 2 * inst->normData + inst->stages - 1); - inst->timeAvgMagnEnergy = WebRtcSpl_DivU32U16(inst->timeAvgMagnEnergyTmp, - inst->blockIndex + 1); - } - - //start processing at frames == converged+1 - // STEP 1: compute prior and post SNR based on quantile noise estimates - - // compute direct decision (DD) estimate of prior SNR: needed for new method - satMax = (WebRtc_UWord32)1048575;// Largest possible value without getting overflow despite shifting 12 steps - postShifts = 6 + qMagn - qNoise; - nShifts = 5 - inst->prevQMagn + inst->prevQNoise; - for (i = 0; i < inst->magnLen; i++) - { - // FLOAT: - // post SNR - // postLocSnr[i] = 0.0; - // if (magn[i] > noise[i]) - // { - // postLocSnr[i] = magn[i] / (noise[i] + 0.0001); - // } - // // previous post SNR - // // previous estimate: based on previous frame with gain filter (smooth is previous filter) - // - // prevNearSnr[i] = inst->prevMagnU16[i] / (inst->noisePrev[i] + 0.0001) * (inst->smooth[i]); - // - // // DD estimate is sum of two terms: current estimate and previous estimate - // // directed decision update of priorSnr (or we actually store [2*priorSnr+1]) - // - // priorLocSnr[i] = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) * (postLocSnr[i] - 1.0); - - // calculate post SNR: output in Q11 - postLocSnr[i] = 2048; // 1.0 in Q11 - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32((WebRtc_UWord32)magnU16[i], 6); // Q(6+qMagn) - if (postShifts < 0) - { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], -postShifts); // Q(6+qMagn) - } else - { - tmpU32no2 = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], postShifts); // Q(6+qMagn) - } - if (tmpU32no1 > tmpU32no2) - { - // Current magnitude larger than noise - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, 11); // Q(17+qMagn) - if (tmpU32no2) - { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q11 - postLocSnr[i] = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } else - { - postLocSnr[i] = satMax; - } - } - - // calculate prevNearSnr[i] and save for later instead of recalculating it later - nearMagnEst = WEBRTC_SPL_UMUL_16_16(inst->prevMagnU16[i], inst->noiseSupFilter[i]); // Q(prevQMagn+14) - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(nearMagnEst, 3); // Q(prevQMagn+17) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(inst->prevNoiseU32[i], nShifts); // Q(prevQMagn+6) - - if (tmpU32no2) - { - tmpU32no1 = WEBRTC_SPL_DIV(tmpU32no1, tmpU32no2); // Q11 - tmpU32no1 = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } else - { - tmpU32no1 = satMax; // Q11 - } - prevNearSnr[i] = tmpU32no1; // Q11 - - //directed decision update of priorSnr - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11); // Q22 - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(postLocSnr[i] - 2048, ONE_MINUS_DD_PR_SNR_Q11); // Q22 - priorSnr = tmpU32no1 + tmpU32no2 + 512; // Q22 (added 512 for rounding) - // priorLocSnr = 1 + 2*priorSnr - priorLocSnr[i] = 2048 + WEBRTC_SPL_RSHIFT_U32(priorSnr, 10); // Q11 - } // end of loop over frequencies - // done with step 1: DD computation of prior and post SNR - - // STEP 2: compute speech/noise likelihood - - //compute difference of input spectrum with learned/estimated noise spectrum - WebRtcNsx_ComputeSpectralDifference(inst, magnU16); - //compute histograms for determination of parameters (thresholds and weights for features) - //parameters are extracted once every window time (=inst->modelUpdate) - //counter update - inst->cntThresUpdate++; - flag = (int)(inst->cntThresUpdate == inst->modelUpdate); - //update histogram - WebRtcNsx_FeatureParameterExtraction(inst, flag); - //compute model parameters - if (flag) - { - inst->cntThresUpdate = 0; // Reset counter - //update every window: - // get normalization for spectral difference for next window estimate - - // Shift to Q(-2*stages) - inst->curAvgMagnEnergy = WEBRTC_SPL_RSHIFT_U32(inst->curAvgMagnEnergy, STAT_UPDATES); - - tmpU32no1 = (inst->curAvgMagnEnergy + inst->timeAvgMagnEnergy + 1) >> 1; //Q(-2*stages) - // Update featureSpecDiff - if ((tmpU32no1 != inst->timeAvgMagnEnergy) && (inst->featureSpecDiff)) - { - norm32no1 = 0; - tmpU32no3 = tmpU32no1; - while (0xFFFF0000 & tmpU32no3) - { - tmpU32no3 >>= 1; - norm32no1++; - } - tmpU32no2 = inst->featureSpecDiff; - while (0xFFFF0000 & tmpU32no2) - { - tmpU32no2 >>= 1; - norm32no1++; - } - tmpU32no3 = WEBRTC_SPL_UMUL(tmpU32no3, tmpU32no2); - tmpU32no3 = WEBRTC_SPL_UDIV(tmpU32no3, inst->timeAvgMagnEnergy); - if (WebRtcSpl_NormU32(tmpU32no3) < norm32no1) - { - inst->featureSpecDiff = 0x007FFFFF; - } else - { - inst->featureSpecDiff = WEBRTC_SPL_MIN(0x007FFFFF, - WEBRTC_SPL_LSHIFT_U32(tmpU32no3, norm32no1)); - } - } - - inst->timeAvgMagnEnergy = tmpU32no1; // Q(-2*stages) - inst->curAvgMagnEnergy = 0; - } - - //compute speech/noise probability - WebRtcNsx_SpeechNoiseProb(inst, nonSpeechProbFinal, priorLocSnr, postLocSnr); - - //time-avg parameter for noise update - gammaNoise = NOISE_UPDATE_Q8; // Q8 - - maxNoiseU32 = 0; - postShifts = inst->prevQNoise - qMagn; - nShifts = inst->prevQMagn - qMagn; - for (i = 0; i < inst->magnLen; i++) - { - // temporary noise update: use it for speech frames if update value is less than previous - // the formula has been rewritten into: - // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] - noisePrev[i]) - - if (postShifts < 0) - { - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(magnU16[i], -postShifts); // Q(prevQNoise) - } else - { - tmpU32no2 = WEBRTC_SPL_LSHIFT_U32(magnU16[i], postShifts); // Q(prevQNoise) - } - if (prevNoiseU16[i] > tmpU32no2) - { - sign = -1; - tmpU32no1 = prevNoiseU16[i] - tmpU32no2; - } else - { - sign = 1; - tmpU32no1 = tmpU32no2 - prevNoiseU16[i]; - } - noiseUpdateU32 = inst->prevNoiseU32[i]; // Q(prevQNoise+11) - tmpU32no3 = 0; - if ((tmpU32no1) && (nonSpeechProbFinal[i])) - { - // This value will be used later, if gammaNoise changes - tmpU32no3 = WEBRTC_SPL_UMUL_32_16(tmpU32no1, nonSpeechProbFinal[i]); // Q(prevQNoise+8) - if (0x7c000000 & tmpU32no3) - { - // Shifting required before multiplication - tmpU32no2 - = WEBRTC_SPL_UMUL_32_16(WEBRTC_SPL_RSHIFT_U32(tmpU32no3, 5), gammaNoise); // Q(prevQNoise+11) - } else - { - // We can do shifting after multiplication - tmpU32no2 - = WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_UMUL_32_16(tmpU32no3, gammaNoise), 5); // Q(prevQNoise+11) - } - if (sign > 0) - { - noiseUpdateU32 += tmpU32no2; // Q(prevQNoise+11) - } else - { - // This operation is safe. We can never get wrap around, since worst - // case scenario means magnU16 = 0 - noiseUpdateU32 -= tmpU32no2; // Q(prevQNoise+11) - } - } - - //increase gamma (i.e., less noise update) for frame likely to be speech - prevGammaNoise = gammaNoise; - gammaNoise = NOISE_UPDATE_Q8; - //time-constant based on speech/noise state - //increase gamma (i.e., less noise update) for frames likely to be speech - if (nonSpeechProbFinal[i] < ONE_MINUS_PROB_RANGE_Q8) - { - gammaNoise = GAMMA_NOISE_TRANS_AND_SPEECH_Q8; - } - - if (prevGammaNoise != gammaNoise) - { - // new noise update - // this line is the same as above, only that the result is stored in a different variable and the gammaNoise - // has changed - // - // noiseUpdate = noisePrev[i] + (1 - gammaNoise) * nonSpeechProb * (magn[i] - noisePrev[i]) - - if (0x7c000000 & tmpU32no3) - { - // Shifting required before multiplication - tmpU32no2 - = WEBRTC_SPL_UMUL_32_16(WEBRTC_SPL_RSHIFT_U32(tmpU32no3, 5), gammaNoise); // Q(prevQNoise+11) - } else - { - // We can do shifting after multiplication - tmpU32no2 - = WEBRTC_SPL_RSHIFT_U32(WEBRTC_SPL_UMUL_32_16(tmpU32no3, gammaNoise), 5); // Q(prevQNoise+11) - } - if (sign > 0) - { - tmpU32no1 = inst->prevNoiseU32[i] + tmpU32no2; // Q(prevQNoise+11) - } else - { - tmpU32no1 = inst->prevNoiseU32[i] - tmpU32no2; // Q(prevQNoise+11) - } - if (noiseUpdateU32 > tmpU32no1) - { - noiseUpdateU32 = tmpU32no1; // Q(prevQNoise+11) - } - } - noiseU32[i] = noiseUpdateU32; // Q(prevQNoise+11) - if (noiseUpdateU32 > maxNoiseU32) - { - maxNoiseU32 = noiseUpdateU32; - } - - // conservative noise update - // // original FLOAT code - // if (prob_speech < PROB_RANGE) { - // inst->avgMagnPause[i] = inst->avgMagnPause[i] + (1.0 - gamma_pause)*(magn[i] - inst->avgMagnPause[i]); - // } - - tmp32no2 = WEBRTC_SPL_SHIFT_W32(inst->avgMagnPause[i], -nShifts); - if (nonSpeechProbFinal[i] > ONE_MINUS_PROB_RANGE_Q8) - { - if (nShifts < 0) - { - tmp32no1 = (WebRtc_Word32)magnU16[i] - tmp32no2; // Q(qMagn) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no1, ONE_MINUS_GAMMA_PAUSE_Q8); // Q(8+prevQMagn+nShifts) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1 + 128, 8); // Q(qMagn) - } else - { - tmp32no1 = WEBRTC_SPL_LSHIFT_W32((WebRtc_Word32)magnU16[i], nShifts) - - inst->avgMagnPause[i]; // Q(qMagn+nShifts) - tmp32no1 = WEBRTC_SPL_MUL_32_16(tmp32no1, ONE_MINUS_GAMMA_PAUSE_Q8); // Q(8+prevQMagn+nShifts) - tmp32no1 = WEBRTC_SPL_RSHIFT_W32(tmp32no1 + (128 << nShifts), 8 + nShifts); // Q(qMagn) - } - tmp32no2 += tmp32no1; // Q(qMagn) - } - inst->avgMagnPause[i] = tmp32no2; - } // end of frequency loop - - norm32no1 = WebRtcSpl_NormU32(maxNoiseU32); - qNoise = inst->prevQNoise + norm32no1 - 5; - // done with step 2: noise update - - // STEP 3: compute dd update of prior snr and post snr based on new noise estimate - nShifts = inst->prevQNoise + 11 - qMagn; - for (i = 0; i < inst->magnLen; i++) - { - // FLOAT code - // // post and prior SNR - // curNearSnr = 0.0; - // if (magn[i] > noise[i]) - // { - // curNearSnr = magn[i] / (noise[i] + 0.0001) - 1.0; - // } - // // DD estimate is sum of two terms: current estimate and previous estimate - // // directed decision update of snrPrior - // snrPrior = DD_PR_SNR * prevNearSnr[i] + (1.0 - DD_PR_SNR) * curNearSnr; - // // gain filter - // tmpFloat1 = inst->overdrive + snrPrior; - // tmpFloat2 = snrPrior / tmpFloat1; - // theFilter[i] = tmpFloat2; - - // calculate curNearSnr again, this is necessary because a new noise estimate has been made since then. for the original - curNearSnr = 0; // Q11 - if (nShifts < 0) - { - // This case is equivalent with magn < noise which implies curNearSnr = 0; - tmpMagnU32 = (WebRtc_UWord32)magnU16[i]; // Q(qMagn) - tmpNoiseU32 = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], -nShifts); // Q(qMagn) - } else if (nShifts > 17) - { - tmpMagnU32 = WEBRTC_SPL_LSHIFT_U32(magnU16[i], 17); // Q(qMagn+17) - tmpNoiseU32 = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], nShifts - 17); // Q(qMagn+17) - } else - { - tmpMagnU32 = WEBRTC_SPL_LSHIFT_U32((WebRtc_UWord32)magnU16[i], nShifts); // Q(qNoise_prev+11) - tmpNoiseU32 = noiseU32[i]; // Q(qNoise_prev+11) - } - if (tmpMagnU32 > tmpNoiseU32) - { - tmpU32no1 = tmpMagnU32 - tmpNoiseU32; // Q(qCur) - norm32no2 = WEBRTC_SPL_MIN(11, WebRtcSpl_NormU32(tmpU32no1)); - tmpU32no1 = WEBRTC_SPL_LSHIFT_U32(tmpU32no1, norm32no2); // Q(qCur+norm32no2) - tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpNoiseU32, 11 - norm32no2); // Q(qCur+norm32no2-11) - if (tmpU32no2) - { - tmpU32no1 = WEBRTC_SPL_UDIV(tmpU32no1, tmpU32no2); // Q11 - } - curNearSnr = WEBRTC_SPL_MIN(satMax, tmpU32no1); // Q11 - } - - //directed decision update of priorSnr - // FLOAT - // priorSnr = DD_PR_SNR * prevNearSnr + (1.0-DD_PR_SNR) * curNearSnr; - - tmpU32no1 = WEBRTC_SPL_UMUL_32_16(prevNearSnr[i], DD_PR_SNR_Q11); // Q22 - tmpU32no2 = WEBRTC_SPL_UMUL_32_16(curNearSnr, ONE_MINUS_DD_PR_SNR_Q11); // Q22 - priorSnr = tmpU32no1 + tmpU32no2; // Q22 - - //gain filter - tmpU32no1 = (WebRtc_UWord32)(inst->overdrive) - + WEBRTC_SPL_RSHIFT_U32(priorSnr + 8192, 14); // Q8 - tmpU16no1 = (WebRtc_UWord16)WEBRTC_SPL_UDIV(priorSnr + (tmpU32no1 >> 1), tmpU32no1); // Q14 - inst->noiseSupFilter[i] = WEBRTC_SPL_SAT(16384, tmpU16no1, inst->denoiseBound); // 16384 = Q14(1.0) // Q14 - - // Weight in the parametric Wiener filter during startup - if (inst->blockIndex < END_STARTUP_SHORT) - { - // Weight the two suppression filters - tmpU32no1 = WEBRTC_SPL_UMUL_16_16(inst->noiseSupFilter[i], - (WebRtc_UWord16)inst->blockIndex); - tmpU32no2 = WEBRTC_SPL_UMUL_16_16(noiseSupFilterTmp[i], - (WebRtc_UWord16)(END_STARTUP_SHORT - - inst->blockIndex)); - tmpU32no1 += tmpU32no2; - inst->noiseSupFilter[i] = (WebRtc_UWord16)WebRtcSpl_DivU32U16(tmpU32no1, - END_STARTUP_SHORT); - } - } // end of loop over frequencies - //done with step3 - - // save noise and magnitude spectrum for next frame - inst->prevQNoise = qNoise; - inst->prevQMagn = qMagn; - if (norm32no1 > 5) - { - for (i = 0; i < inst->magnLen; i++) - { - inst->prevNoiseU32[i] = WEBRTC_SPL_LSHIFT_U32(noiseU32[i], norm32no1 - 5); // Q(qNoise+11) - inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) - } - } else - { - for (i = 0; i < inst->magnLen; i++) - { - inst->prevNoiseU32[i] = WEBRTC_SPL_RSHIFT_U32(noiseU32[i], 5 - norm32no1); // Q(qNoise+11) - inst->prevMagnU16[i] = magnU16[i]; // Q(qMagn) - } - } - - WebRtcNsx_DataSynthesis(inst, outFrame); -#ifdef NS_FILEDEBUG - fwrite(outframe, sizeof(short), inst->blockLen10ms, inst->outfile); -#endif - - //for H band: - // only update data buffer, then apply time-domain gain is applied derived from L band - if (inst->fs == 32000) - { - // update analysis buffer for H band - // append new data to buffer FX - WEBRTC_SPL_MEMCPY_W16(inst->dataBufHBFX, inst->dataBufHBFX + inst->blockLen10ms, inst->anaLen - inst->blockLen10ms); - WEBRTC_SPL_MEMCPY_W16(inst->dataBufHBFX + inst->anaLen - inst->blockLen10ms, speechFrameHB, inst->blockLen10ms); - // range for averaging low band quantities for H band gain - - gainTimeDomainHB = 16384; // 16384 = Q14(1.0) - //average speech prob from low band - //average filter gain from low band - //avg over second half (i.e., 4->8kHz) of freq. spectrum - tmpU32no1 = 0; // Q12 - tmpU16no1 = 0; // Q8 - for (i = inst->anaLen2 - (inst->anaLen2 >> 2); i < inst->anaLen2; i++) - { - tmpU16no1 += nonSpeechProbFinal[i]; // Q8 - tmpU32no1 += (WebRtc_UWord32)(inst->noiseSupFilter[i]); // Q14 - } - avgProbSpeechHB = (WebRtc_Word16)(4096 - - WEBRTC_SPL_RSHIFT_U16(tmpU16no1, inst->stages - 7)); // Q12 - avgFilterGainHB = (WebRtc_Word16)WEBRTC_SPL_RSHIFT_U32(tmpU32no1, inst->stages - 3); // Q14 - - // // original FLOAT code - // // gain based on speech probability: - // avg_prob_speech_tt=(float)2.0*avg_prob_speech-(float)1.0; - // gain_mod=(float)0.5*((float)1.0+(float)tanh(avg_prob_speech_tt)); // between 0 and 1 - - // gain based on speech probability: - // original expression: "0.5 * (1 + tanh(2x-1))" - // avgProbSpeechHB has been anyway saturated to a value between 0 and 1 so the other cases don't have to be dealt with - // avgProbSpeechHB and gainModHB are in Q12, 3607 = Q12(0.880615234375) which is a zero point of - // |0.5 * (1 + tanh(2x-1)) - x| - |0.5 * (1 + tanh(2x-1)) - 0.880615234375| meaning that from that point the error of approximating - // the expression with f(x) = x would be greater than the error of approximating the expression with f(x) = 0.880615234375 - // error: "|0.5 * (1 + tanh(2x-1)) - x| from x=0 to 0.880615234375" -> http://www.wolframalpha.com/input/?i=|0.5+*+(1+%2B+tanh(2x-1))+-+x|+from+x%3D0+to+0.880615234375 - // and: "|0.5 * (1 + tanh(2x-1)) - 0.880615234375| from x=0.880615234375 to 1" -> http://www.wolframalpha.com/input/?i=+|0.5+*+(1+%2B+tanh(2x-1))+-+0.880615234375|+from+x%3D0.880615234375+to+1 - gainModHB = WEBRTC_SPL_MIN(avgProbSpeechHB, 3607); - - // // original FLOAT code - // //combine gain with low band gain - // if (avg_prob_speech < (float)0.5) { - // gain_time_domain_HB=(float)0.5*gain_mod+(float)0.5*avg_filter_gain; - // } - // else { - // gain_time_domain_HB=(float)0.25*gain_mod+(float)0.75*avg_filter_gain; - // } - - - //combine gain with low band gain - if (avgProbSpeechHB < 2048) - { // 2048 = Q12(0.5) - // the next two lines in float are "gain_time_domain = 0.5 * gain_mod + 0.5 * avg_filter_gain"; Q2(0.5) = 2 equals one left shift - gainTimeDomainHB = (gainModHB << 1) + (avgFilterGainHB >> 1); // Q14 - } else - { - // "gain_time_domain = 0.25 * gain_mod + 0.75 * agv_filter_gain;" - gainTimeDomainHB = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(3, avgFilterGainHB, 2); // 3 = Q2(0.75); Q14 - gainTimeDomainHB += gainModHB; // Q14 - } - //make sure gain is within flooring range - gainTimeDomainHB - = WEBRTC_SPL_SAT(16384, gainTimeDomainHB, (WebRtc_Word16)(inst->denoiseBound)); // 16384 = Q14(1.0) - - - //apply gain - for (i = 0; i < inst->blockLen10ms; i++) - { - outFrameHB[i] - = (WebRtc_Word16)WEBRTC_SPL_MUL_16_16_RSFT(gainTimeDomainHB, inst->dataBufHBFX[i], 14); // Q0 - } - } // end of H band gain computation - - return 0; -} diff --git a/modules/audio_processing/ns/main/source/nsx_core.h b/modules/audio_processing/ns/main/source/nsx_core.h deleted file mode 100644 index 2e7430350..000000000 --- a/modules/audio_processing/ns/main/source/nsx_core.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ - -#include "typedefs.h" -#include "signal_processing_library.h" - -#include "nsx_defines.h" - -#ifdef NS_FILEDEBUG -#include -#endif - -typedef struct NsxInst_t_ -{ - WebRtc_UWord32 fs; - - const WebRtc_Word16* window; - WebRtc_Word16 analysisBuffer[ANAL_BLOCKL_MAX]; - WebRtc_Word16 synthesisBuffer[ANAL_BLOCKL_MAX]; - WebRtc_UWord16 noiseSupFilter[HALF_ANAL_BLOCKL]; - WebRtc_UWord16 overdrive; /* Q8 */ - WebRtc_UWord16 denoiseBound; /* Q14 */ - const WebRtc_Word16* factor2Table; - WebRtc_Word16 noiseEstLogQuantile[SIMULT * HALF_ANAL_BLOCKL]; - WebRtc_Word16 noiseEstDensity[SIMULT * HALF_ANAL_BLOCKL]; - WebRtc_Word16 noiseEstCounter[SIMULT]; - WebRtc_Word16 noiseEstQuantile[HALF_ANAL_BLOCKL]; - - WebRtc_Word16 anaLen; - int anaLen2; - int magnLen; - int aggrMode; - int stages; - int initFlag; - int gainMap; - - WebRtc_Word32 maxLrt; - WebRtc_Word32 minLrt; - WebRtc_Word32 logLrtTimeAvgW32[HALF_ANAL_BLOCKL]; //log lrt factor with time-smoothing in Q8 - WebRtc_Word32 featureLogLrt; - WebRtc_Word32 thresholdLogLrt; - WebRtc_Word16 weightLogLrt; - - WebRtc_UWord32 featureSpecDiff; - WebRtc_UWord32 thresholdSpecDiff; - WebRtc_Word16 weightSpecDiff; - - WebRtc_UWord32 featureSpecFlat; - WebRtc_UWord32 thresholdSpecFlat; - WebRtc_Word16 weightSpecFlat; - - WebRtc_Word32 avgMagnPause[HALF_ANAL_BLOCKL]; //conservative estimate of noise spectrum - WebRtc_UWord32 magnEnergy; - WebRtc_UWord32 sumMagn; - WebRtc_UWord32 curAvgMagnEnergy; - WebRtc_UWord32 timeAvgMagnEnergy; - WebRtc_UWord32 timeAvgMagnEnergyTmp; - - WebRtc_UWord32 whiteNoiseLevel; //initial noise estimate - WebRtc_UWord32 initMagnEst[HALF_ANAL_BLOCKL];//initial magnitude spectrum estimate - WebRtc_Word32 pinkNoiseNumerator; //pink noise parameter: numerator - WebRtc_Word32 pinkNoiseExp; //pink noise parameter: power of freq - int minNorm; //smallest normalization factor - int zeroInputSignal; //zero input signal flag - - WebRtc_UWord32 prevNoiseU32[HALF_ANAL_BLOCKL]; //noise spectrum from previous frame - WebRtc_UWord16 prevMagnU16[HALF_ANAL_BLOCKL]; //magnitude spectrum from previous frame - WebRtc_Word16 priorNonSpeechProb; //prior speech/noise probability // Q14 - - int blockIndex; //frame index counter - int modelUpdate; //parameter for updating or estimating thresholds/weights for prior model - int cntThresUpdate; - - //histograms for parameter estimation - WebRtc_Word16 histLrt[HIST_PAR_EST]; - WebRtc_Word16 histSpecFlat[HIST_PAR_EST]; - WebRtc_Word16 histSpecDiff[HIST_PAR_EST]; - - //quantities for high band estimate - WebRtc_Word16 dataBufHBFX[ANAL_BLOCKL_MAX]; /* Q0 */ - - int qNoise; - int prevQNoise; - int prevQMagn; - int blockLen10ms; - - WebRtc_Word16 real[ANAL_BLOCKL_MAX]; - WebRtc_Word16 imag[ANAL_BLOCKL_MAX]; - WebRtc_Word32 energyIn; - int scaleEnergyIn; - int normData; - -} NsxInst_t; - -#ifdef __cplusplus -extern "C" -{ -#endif - -/**************************************************************************** - * WebRtcNsx_InitCore(...) - * - * This function initializes a noise suppression instance - * - * Input: - * - inst : Instance that should be initialized - * - fs : Sampling frequency - * - * Output: - * - inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -WebRtc_Word32 WebRtcNsx_InitCore(NsxInst_t *inst, WebRtc_UWord32 fs); - -/**************************************************************************** - * WebRtcNsx_set_policy_core(...) - * - * This changes the aggressiveness of the noise suppression method. - * - * Input: - * - inst : Instance that should be initialized - * - mode : 0: Mild (6 dB), 1: Medium (10 dB), 2: Aggressive (15 dB) - * - * Output: - * - NS_inst : Initialized instance - * - * Return value : 0 - Ok - * -1 - Error - */ -int WebRtcNsx_set_policy_core(NsxInst_t *inst, int mode); - -/**************************************************************************** - * WebRtcNsx_ProcessCore - * - * Do noise suppression. - * - * Input: - * - inst : Instance that should be initialized - * - inFrameLow : Input speech frame for lower band - * - inFrameHigh : Input speech frame for higher band - * - * Output: - * - inst : Updated instance - * - outFrameLow : Output speech frame for lower band - * - outFrameHigh : Output speech frame for higher band - * - * Return value : 0 - OK - * -1 - Error - */ -int WebRtcNsx_ProcessCore(NsxInst_t *inst, short *inFrameLow, short *inFrameHigh, - short *outFrameLow, short *outFrameHigh); - -#ifdef __cplusplus -} -#endif - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_CORE_H_ diff --git a/modules/audio_processing/ns/main/source/nsx_defines.h b/modules/audio_processing/ns/main/source/nsx_defines.h deleted file mode 100644 index 58796b9a3..000000000 --- a/modules/audio_processing/ns/main/source/nsx_defines.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ - -#define ANAL_BLOCKL_MAX 256 // max analysis block length -#define HALF_ANAL_BLOCKL 129 // half max analysis block length + 1 -#define SIMULT 3 -#define END_STARTUP_LONG 200 -#define END_STARTUP_SHORT 50 -#define FACTOR_Q16 (WebRtc_Word32)2621440 // 40 in Q16 -#define FACTOR_Q7 (WebRtc_Word16)5120 // 40 in Q7 -#define WIDTH_Q8 3 // 0.01 in Q8 (or 25 ) -//PARAMETERS FOR NEW METHOD -#define DD_PR_SNR_Q11 2007 // ~= Q11(0.98) DD update of prior SNR -#define ONE_MINUS_DD_PR_SNR_Q11 41 // DD update of prior SNR -#define SPECT_FLAT_TAVG_Q14 4915 // (0.30) tavg parameter for spectral flatness measure -#define SPECT_DIFF_TAVG_Q8 77 // (0.30) tavg parameter for spectral flatness measure -#define PRIOR_UPDATE_Q14 1638 // Q14(0.1) update parameter of prior model -#define NOISE_UPDATE_Q8 26 // 26 ~= Q8(0.1) update parameter for noise -// probability threshold for noise state in speech/noise likelihood -#define ONE_MINUS_PROB_RANGE_Q8 205 // 205 ~= Q8(0.8) -#define HIST_PAR_EST 1000 // histogram size for estimation of parameters -//FEATURE EXTRACTION CONFIG -//bin size of histogram -#define BIN_SIZE_LRT 10 -//scale parameters: multiply dominant peaks of the histograms by scale factor to obtain -// thresholds for prior model -#define FACTOR_1_LRT_DIFF 6 //for LRT and spectral difference (5 times bigger) -//for spectral_flatness: used when noise is flatter than speech (10 times bigger) -#define FACTOR_2_FLAT_Q10 922 -//peak limit for spectral flatness (varies between 0 and 1) -#define THRES_PEAK_FLAT 24 // * 2 * BIN_SIZE_FLAT_FX -//limit on spacing of two highest peaks in histogram: spacing determined by bin size -#define LIM_PEAK_SPACE_FLAT_DIFF 4 // * 2 * BIN_SIZE_DIFF_FX -//limit on relevance of second peak: -#define LIM_PEAK_WEIGHT_FLAT_DIFF 2 -#define THRES_FLUCT_LRT 10240 //=20 * inst->modelUpdate; fluctuation limit of LRT feat. -//limit on the max and min values for the feature thresholds -#define MAX_FLAT_Q10 38912 // * 2 * BIN_SIZE_FLAT_FX -#define MIN_FLAT_Q10 4096 // * 2 * BIN_SIZE_FLAT_FX -#define MAX_DIFF 100 // * 2 * BIN_SIZE_DIFF_FX -#define MIN_DIFF 16 // * 2 * BIN_SIZE_DIFF_FX -//criteria of weight of histogram peak to accept/reject feature -#define THRES_WEIGHT_FLAT_DIFF 154//(int)(0.3*(inst->modelUpdate)) for flatness and difference -// -#define STAT_UPDATES 9 // Update every 512 = 1 << 9 block -#define ONE_MINUS_GAMMA_PAUSE_Q8 13 // ~= Q8(0.05) update for conservative noise estimate -#define GAMMA_NOISE_TRANS_AND_SPEECH_Q8 3 // ~= Q8(0.01) update for transition and noise region -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_NSX_DEFINES_H_ diff --git a/modules/audio_processing/ns/main/source/windows_private.h b/modules/audio_processing/ns/main/source/windows_private.h deleted file mode 100644 index 8f9006ed7..000000000 --- a/modules/audio_processing/ns/main/source/windows_private.h +++ /dev/null @@ -1,573 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ - -// Hanning window for 4ms 16kHz -static const float kHanning64w128[128] = { -0.00000000000000f, 0.02454122852291f, 0.04906767432742f, -0.07356456359967f, 0.09801714032956f, 0.12241067519922f, -0.14673047445536f, 0.17096188876030f, 0.19509032201613f, -0.21910124015687f, 0.24298017990326f, 0.26671275747490f, -0.29028467725446f, 0.31368174039889f, 0.33688985339222f, -0.35989503653499f, 0.38268343236509f, 0.40524131400499f, -0.42755509343028f, 0.44961132965461f, 0.47139673682600f, -0.49289819222978f, 0.51410274419322f, 0.53499761988710f, -0.55557023301960f, 0.57580819141785f, 0.59569930449243f, -0.61523159058063f, 0.63439328416365f, 0.65317284295378f, -0.67155895484702f, 0.68954054473707f, 0.70710678118655f, -0.72424708295147f, 0.74095112535496f, 0.75720884650648f, -0.77301045336274f, 0.78834642762661f, 0.80320753148064f, -0.81758481315158f, 0.83146961230255f, 0.84485356524971f, -0.85772861000027f, 0.87008699110871f, 0.88192126434835f, -0.89322430119552f, 0.90398929312344f, 0.91420975570353f, -0.92387953251129f, 0.93299279883474f, 0.94154406518302f, -0.94952818059304f, 0.95694033573221f, 0.96377606579544f, -0.97003125319454f, 0.97570213003853f, 0.98078528040323f, -0.98527764238894f, 0.98917650996478f, 0.99247953459871f, -0.99518472667220f, 0.99729045667869f, 0.99879545620517f, -0.99969881869620f, 1.00000000000000f, -0.99969881869620f, 0.99879545620517f, 0.99729045667869f, -0.99518472667220f, 0.99247953459871f, 0.98917650996478f, -0.98527764238894f, 0.98078528040323f, 0.97570213003853f, -0.97003125319454f, 0.96377606579544f, 0.95694033573221f, -0.94952818059304f, 0.94154406518302f, 0.93299279883474f, -0.92387953251129f, 0.91420975570353f, 0.90398929312344f, -0.89322430119552f, 0.88192126434835f, 0.87008699110871f, -0.85772861000027f, 0.84485356524971f, 0.83146961230255f, -0.81758481315158f, 0.80320753148064f, 0.78834642762661f, -0.77301045336274f, 0.75720884650648f, 0.74095112535496f, -0.72424708295147f, 0.70710678118655f, 0.68954054473707f, -0.67155895484702f, 0.65317284295378f, 0.63439328416365f, -0.61523159058063f, 0.59569930449243f, 0.57580819141785f, -0.55557023301960f, 0.53499761988710f, 0.51410274419322f, -0.49289819222978f, 0.47139673682600f, 0.44961132965461f, -0.42755509343028f, 0.40524131400499f, 0.38268343236509f, -0.35989503653499f, 0.33688985339222f, 0.31368174039889f, -0.29028467725446f, 0.26671275747490f, 0.24298017990326f, -0.21910124015687f, 0.19509032201613f, 0.17096188876030f, -0.14673047445536f, 0.12241067519922f, 0.09801714032956f, -0.07356456359967f, 0.04906767432742f, 0.02454122852291f -}; - - - -// hybrib Hanning & flat window -static const float kBlocks80w128[128] = { -(float)0.00000000, (float)0.03271908, (float)0.06540313, (float)0.09801714, (float)0.13052619, -(float)0.16289547, (float)0.19509032, (float)0.22707626, (float)0.25881905, (float)0.29028468, -(float)0.32143947, (float)0.35225005, (float)0.38268343, (float)0.41270703, (float)0.44228869, -(float)0.47139674, (float)0.50000000, (float)0.52806785, (float)0.55557023, (float)0.58247770, -(float)0.60876143, (float)0.63439328, (float)0.65934582, (float)0.68359230, (float)0.70710678, -(float)0.72986407, (float)0.75183981, (float)0.77301045, (float)0.79335334, (float)0.81284668, -(float)0.83146961, (float)0.84920218, (float)0.86602540, (float)0.88192126, (float)0.89687274, -(float)0.91086382, (float)0.92387953, (float)0.93590593, (float)0.94693013, (float)0.95694034, -(float)0.96592583, (float)0.97387698, (float)0.98078528, (float)0.98664333, (float)0.99144486, -(float)0.99518473, (float)0.99785892, (float)0.99946459, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)0.99946459, (float)0.99785892, (float)0.99518473, (float)0.99144486, -(float)0.98664333, (float)0.98078528, (float)0.97387698, (float)0.96592583, (float)0.95694034, -(float)0.94693013, (float)0.93590593, (float)0.92387953, (float)0.91086382, (float)0.89687274, -(float)0.88192126, (float)0.86602540, (float)0.84920218, (float)0.83146961, (float)0.81284668, -(float)0.79335334, (float)0.77301045, (float)0.75183981, (float)0.72986407, (float)0.70710678, -(float)0.68359230, (float)0.65934582, (float)0.63439328, (float)0.60876143, (float)0.58247770, -(float)0.55557023, (float)0.52806785, (float)0.50000000, (float)0.47139674, (float)0.44228869, -(float)0.41270703, (float)0.38268343, (float)0.35225005, (float)0.32143947, (float)0.29028468, -(float)0.25881905, (float)0.22707626, (float)0.19509032, (float)0.16289547, (float)0.13052619, -(float)0.09801714, (float)0.06540313, (float)0.03271908 -}; - -// hybrib Hanning & flat window -static const float kBlocks160w256[256] = { -(float)0.00000000, (float)0.01636173, (float)0.03271908, (float)0.04906767, (float)0.06540313, -(float)0.08172107, (float)0.09801714, (float)0.11428696, (float)0.13052619, (float)0.14673047, -(float)0.16289547, (float)0.17901686, (float)0.19509032, (float)0.21111155, (float)0.22707626, -(float)0.24298018, (float)0.25881905, (float)0.27458862, (float)0.29028468, (float)0.30590302, -(float)0.32143947, (float)0.33688985, (float)0.35225005, (float)0.36751594, (float)0.38268343, -(float)0.39774847, (float)0.41270703, (float)0.42755509, (float)0.44228869, (float)0.45690388, -(float)0.47139674, (float)0.48576339, (float)0.50000000, (float)0.51410274, (float)0.52806785, -(float)0.54189158, (float)0.55557023, (float)0.56910015, (float)0.58247770, (float)0.59569930, -(float)0.60876143, (float)0.62166057, (float)0.63439328, (float)0.64695615, (float)0.65934582, -(float)0.67155895, (float)0.68359230, (float)0.69544264, (float)0.70710678, (float)0.71858162, -(float)0.72986407, (float)0.74095113, (float)0.75183981, (float)0.76252720, (float)0.77301045, -(float)0.78328675, (float)0.79335334, (float)0.80320753, (float)0.81284668, (float)0.82226822, -(float)0.83146961, (float)0.84044840, (float)0.84920218, (float)0.85772861, (float)0.86602540, -(float)0.87409034, (float)0.88192126, (float)0.88951608, (float)0.89687274, (float)0.90398929, -(float)0.91086382, (float)0.91749450, (float)0.92387953, (float)0.93001722, (float)0.93590593, -(float)0.94154407, (float)0.94693013, (float)0.95206268, (float)0.95694034, (float)0.96156180, -(float)0.96592583, (float)0.97003125, (float)0.97387698, (float)0.97746197, (float)0.98078528, -(float)0.98384601, (float)0.98664333, (float)0.98917651, (float)0.99144486, (float)0.99344778, -(float)0.99518473, (float)0.99665524, (float)0.99785892, (float)0.99879546, (float)0.99946459, -(float)0.99986614, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)0.99986614, (float)0.99946459, (float)0.99879546, (float)0.99785892, -(float)0.99665524, (float)0.99518473, (float)0.99344778, (float)0.99144486, (float)0.98917651, -(float)0.98664333, (float)0.98384601, (float)0.98078528, (float)0.97746197, (float)0.97387698, -(float)0.97003125, (float)0.96592583, (float)0.96156180, (float)0.95694034, (float)0.95206268, -(float)0.94693013, (float)0.94154407, (float)0.93590593, (float)0.93001722, (float)0.92387953, -(float)0.91749450, (float)0.91086382, (float)0.90398929, (float)0.89687274, (float)0.88951608, -(float)0.88192126, (float)0.87409034, (float)0.86602540, (float)0.85772861, (float)0.84920218, -(float)0.84044840, (float)0.83146961, (float)0.82226822, (float)0.81284668, (float)0.80320753, -(float)0.79335334, (float)0.78328675, (float)0.77301045, (float)0.76252720, (float)0.75183981, -(float)0.74095113, (float)0.72986407, (float)0.71858162, (float)0.70710678, (float)0.69544264, -(float)0.68359230, (float)0.67155895, (float)0.65934582, (float)0.64695615, (float)0.63439328, -(float)0.62166057, (float)0.60876143, (float)0.59569930, (float)0.58247770, (float)0.56910015, -(float)0.55557023, (float)0.54189158, (float)0.52806785, (float)0.51410274, (float)0.50000000, -(float)0.48576339, (float)0.47139674, (float)0.45690388, (float)0.44228869, (float)0.42755509, -(float)0.41270703, (float)0.39774847, (float)0.38268343, (float)0.36751594, (float)0.35225005, -(float)0.33688985, (float)0.32143947, (float)0.30590302, (float)0.29028468, (float)0.27458862, -(float)0.25881905, (float)0.24298018, (float)0.22707626, (float)0.21111155, (float)0.19509032, -(float)0.17901686, (float)0.16289547, (float)0.14673047, (float)0.13052619, (float)0.11428696, -(float)0.09801714, (float)0.08172107, (float)0.06540313, (float)0.04906767, (float)0.03271908, -(float)0.01636173 -}; - -// hybrib Hanning & flat window: for 20ms -static const float kBlocks320w512[512] = { -(float)0.00000000, (float)0.00818114, (float)0.01636173, (float)0.02454123, (float)0.03271908, -(float)0.04089475, (float)0.04906767, (float)0.05723732, (float)0.06540313, (float)0.07356456, -(float)0.08172107, (float)0.08987211, (float)0.09801714, (float)0.10615561, (float)0.11428696, -(float)0.12241068, (float)0.13052619, (float)0.13863297, (float)0.14673047, (float)0.15481816, -(float)0.16289547, (float)0.17096189, (float)0.17901686, (float)0.18705985, (float)0.19509032, -(float)0.20310773, (float)0.21111155, (float)0.21910124, (float)0.22707626, (float)0.23503609, -(float)0.24298018, (float)0.25090801, (float)0.25881905, (float)0.26671276, (float)0.27458862, -(float)0.28244610, (float)0.29028468, (float)0.29810383, (float)0.30590302, (float)0.31368174, -(float)0.32143947, (float)0.32917568, (float)0.33688985, (float)0.34458148, (float)0.35225005, -(float)0.35989504, (float)0.36751594, (float)0.37511224, (float)0.38268343, (float)0.39022901, -(float)0.39774847, (float)0.40524131, (float)0.41270703, (float)0.42014512, (float)0.42755509, -(float)0.43493645, (float)0.44228869, (float)0.44961133, (float)0.45690388, (float)0.46416584, -(float)0.47139674, (float)0.47859608, (float)0.48576339, (float)0.49289819, (float)0.50000000, -(float)0.50706834, (float)0.51410274, (float)0.52110274, (float)0.52806785, (float)0.53499762, -(float)0.54189158, (float)0.54874927, (float)0.55557023, (float)0.56235401, (float)0.56910015, -(float)0.57580819, (float)0.58247770, (float)0.58910822, (float)0.59569930, (float)0.60225052, -(float)0.60876143, (float)0.61523159, (float)0.62166057, (float)0.62804795, (float)0.63439328, -(float)0.64069616, (float)0.64695615, (float)0.65317284, (float)0.65934582, (float)0.66547466, -(float)0.67155895, (float)0.67759830, (float)0.68359230, (float)0.68954054, (float)0.69544264, -(float)0.70129818, (float)0.70710678, (float)0.71286806, (float)0.71858162, (float)0.72424708, -(float)0.72986407, (float)0.73543221, (float)0.74095113, (float)0.74642045, (float)0.75183981, -(float)0.75720885, (float)0.76252720, (float)0.76779452, (float)0.77301045, (float)0.77817464, -(float)0.78328675, (float)0.78834643, (float)0.79335334, (float)0.79830715, (float)0.80320753, -(float)0.80805415, (float)0.81284668, (float)0.81758481, (float)0.82226822, (float)0.82689659, -(float)0.83146961, (float)0.83598698, (float)0.84044840, (float)0.84485357, (float)0.84920218, -(float)0.85349396, (float)0.85772861, (float)0.86190585, (float)0.86602540, (float)0.87008699, -(float)0.87409034, (float)0.87803519, (float)0.88192126, (float)0.88574831, (float)0.88951608, -(float)0.89322430, (float)0.89687274, (float)0.90046115, (float)0.90398929, (float)0.90745693, -(float)0.91086382, (float)0.91420976, (float)0.91749450, (float)0.92071783, (float)0.92387953, -(float)0.92697940, (float)0.93001722, (float)0.93299280, (float)0.93590593, (float)0.93875641, -(float)0.94154407, (float)0.94426870, (float)0.94693013, (float)0.94952818, (float)0.95206268, -(float)0.95453345, (float)0.95694034, (float)0.95928317, (float)0.96156180, (float)0.96377607, -(float)0.96592583, (float)0.96801094, (float)0.97003125, (float)0.97198664, (float)0.97387698, -(float)0.97570213, (float)0.97746197, (float)0.97915640, (float)0.98078528, (float)0.98234852, -(float)0.98384601, (float)0.98527764, (float)0.98664333, (float)0.98794298, (float)0.98917651, -(float)0.99034383, (float)0.99144486, (float)0.99247953, (float)0.99344778, (float)0.99434953, -(float)0.99518473, (float)0.99595331, (float)0.99665524, (float)0.99729046, (float)0.99785892, -(float)0.99836060, (float)0.99879546, (float)0.99916346, (float)0.99946459, (float)0.99969882, -(float)0.99986614, (float)0.99996653, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, (float)1.00000000, -(float)1.00000000, (float)0.99996653, (float)0.99986614, (float)0.99969882, (float)0.99946459, -(float)0.99916346, (float)0.99879546, (float)0.99836060, (float)0.99785892, (float)0.99729046, -(float)0.99665524, (float)0.99595331, (float)0.99518473, (float)0.99434953, (float)0.99344778, -(float)0.99247953, (float)0.99144486, (float)0.99034383, (float)0.98917651, (float)0.98794298, -(float)0.98664333, (float)0.98527764, (float)0.98384601, (float)0.98234852, (float)0.98078528, -(float)0.97915640, (float)0.97746197, (float)0.97570213, (float)0.97387698, (float)0.97198664, -(float)0.97003125, (float)0.96801094, (float)0.96592583, (float)0.96377607, (float)0.96156180, -(float)0.95928317, (float)0.95694034, (float)0.95453345, (float)0.95206268, (float)0.94952818, -(float)0.94693013, (float)0.94426870, (float)0.94154407, (float)0.93875641, (float)0.93590593, -(float)0.93299280, (float)0.93001722, (float)0.92697940, (float)0.92387953, (float)0.92071783, -(float)0.91749450, (float)0.91420976, (float)0.91086382, (float)0.90745693, (float)0.90398929, -(float)0.90046115, (float)0.89687274, (float)0.89322430, (float)0.88951608, (float)0.88574831, -(float)0.88192126, (float)0.87803519, (float)0.87409034, (float)0.87008699, (float)0.86602540, -(float)0.86190585, (float)0.85772861, (float)0.85349396, (float)0.84920218, (float)0.84485357, -(float)0.84044840, (float)0.83598698, (float)0.83146961, (float)0.82689659, (float)0.82226822, -(float)0.81758481, (float)0.81284668, (float)0.80805415, (float)0.80320753, (float)0.79830715, -(float)0.79335334, (float)0.78834643, (float)0.78328675, (float)0.77817464, (float)0.77301045, -(float)0.76779452, (float)0.76252720, (float)0.75720885, (float)0.75183981, (float)0.74642045, -(float)0.74095113, (float)0.73543221, (float)0.72986407, (float)0.72424708, (float)0.71858162, -(float)0.71286806, (float)0.70710678, (float)0.70129818, (float)0.69544264, (float)0.68954054, -(float)0.68359230, (float)0.67759830, (float)0.67155895, (float)0.66547466, (float)0.65934582, -(float)0.65317284, (float)0.64695615, (float)0.64069616, (float)0.63439328, (float)0.62804795, -(float)0.62166057, (float)0.61523159, (float)0.60876143, (float)0.60225052, (float)0.59569930, -(float)0.58910822, (float)0.58247770, (float)0.57580819, (float)0.56910015, (float)0.56235401, -(float)0.55557023, (float)0.54874927, (float)0.54189158, (float)0.53499762, (float)0.52806785, -(float)0.52110274, (float)0.51410274, (float)0.50706834, (float)0.50000000, (float)0.49289819, -(float)0.48576339, (float)0.47859608, (float)0.47139674, (float)0.46416584, (float)0.45690388, -(float)0.44961133, (float)0.44228869, (float)0.43493645, (float)0.42755509, (float)0.42014512, -(float)0.41270703, (float)0.40524131, (float)0.39774847, (float)0.39022901, (float)0.38268343, -(float)0.37511224, (float)0.36751594, (float)0.35989504, (float)0.35225005, (float)0.34458148, -(float)0.33688985, (float)0.32917568, (float)0.32143947, (float)0.31368174, (float)0.30590302, -(float)0.29810383, (float)0.29028468, (float)0.28244610, (float)0.27458862, (float)0.26671276, -(float)0.25881905, (float)0.25090801, (float)0.24298018, (float)0.23503609, (float)0.22707626, -(float)0.21910124, (float)0.21111155, (float)0.20310773, (float)0.19509032, (float)0.18705985, -(float)0.17901686, (float)0.17096189, (float)0.16289547, (float)0.15481816, (float)0.14673047, -(float)0.13863297, (float)0.13052619, (float)0.12241068, (float)0.11428696, (float)0.10615561, -(float)0.09801714, (float)0.08987211, (float)0.08172107, (float)0.07356456, (float)0.06540313, -(float)0.05723732, (float)0.04906767, (float)0.04089475, (float)0.03271908, (float)0.02454123, -(float)0.01636173, (float)0.00818114 -}; - - -// Hanning window: for 15ms at 16kHz with symmetric zeros -static const float kBlocks240w512[512] = { -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00654494, (float)0.01308960, (float)0.01963369, -(float)0.02617695, (float)0.03271908, (float)0.03925982, (float)0.04579887, (float)0.05233596, -(float)0.05887080, (float)0.06540313, (float)0.07193266, (float)0.07845910, (float)0.08498218, -(float)0.09150162, (float)0.09801714, (float)0.10452846, (float)0.11103531, (float)0.11753740, -(float)0.12403446, (float)0.13052620, (float)0.13701233, (float)0.14349262, (float)0.14996676, -(float)0.15643448, (float)0.16289547, (float)0.16934951, (float)0.17579629, (float)0.18223552, -(float)0.18866697, (float)0.19509032, (float)0.20150533, (float)0.20791170, (float)0.21430916, -(float)0.22069745, (float)0.22707628, (float)0.23344538, (float)0.23980446, (float)0.24615330, -(float)0.25249159, (float)0.25881904, (float)0.26513544, (float)0.27144045, (float)0.27773386, -(float)0.28401536, (float)0.29028466, (float)0.29654160, (float)0.30278578, (float)0.30901700, -(float)0.31523499, (float)0.32143945, (float)0.32763019, (float)0.33380687, (float)0.33996925, -(float)0.34611708, (float)0.35225007, (float)0.35836795, (float)0.36447051, (float)0.37055743, -(float)0.37662852, (float)0.38268346, (float)0.38872197, (float)0.39474389, (float)0.40074885, -(float)0.40673664, (float)0.41270703, (float)0.41865975, (float)0.42459452, (float)0.43051112, -(float)0.43640924, (float)0.44228873, (float)0.44814920, (float)0.45399052, (float)0.45981237, -(float)0.46561453, (float)0.47139674, (float)0.47715878, (float)0.48290035, (float)0.48862126, -(float)0.49432120, (float)0.50000000, (float)0.50565743, (float)0.51129311, (float)0.51690692, -(float)0.52249855, (float)0.52806789, (float)0.53361452, (float)0.53913832, (float)0.54463905, -(float)0.55011642, (float)0.55557024, (float)0.56100029, (float)0.56640625, (float)0.57178795, -(float)0.57714522, (float)0.58247769, (float)0.58778524, (float)0.59306765, (float)0.59832460, -(float)0.60355598, (float)0.60876143, (float)0.61394083, (float)0.61909395, (float)0.62422055, -(float)0.62932038, (float)0.63439333, (float)0.63943899, (float)0.64445734, (float)0.64944810, -(float)0.65441096, (float)0.65934587, (float)0.66425246, (float)0.66913062, (float)0.67398012, -(float)0.67880076, (float)0.68359232, (float)0.68835455, (float)0.69308740, (float)0.69779050, -(float)0.70246369, (float)0.70710677, (float)0.71171963, (float)0.71630198, (float)0.72085363, -(float)0.72537440, (float)0.72986406, (float)0.73432255, (float)0.73874950, (float)0.74314487, -(float)0.74750835, (float)0.75183982, (float)0.75613910, (float)0.76040596, (float)0.76464027, -(float)0.76884186, (float)0.77301043, (float)0.77714598, (float)0.78124821, (float)0.78531694, -(float)0.78935206, (float)0.79335338, (float)0.79732066, (float)0.80125386, (float)0.80515265, -(float)0.80901700, (float)0.81284672, (float)0.81664157, (float)0.82040149, (float)0.82412618, -(float)0.82781565, (float)0.83146966, (float)0.83508795, (float)0.83867061, (float)0.84221727, -(float)0.84572780, (float)0.84920216, (float)0.85264021, (float)0.85604161, (float)0.85940641, -(float)0.86273444, (float)0.86602545, (float)0.86927933, (float)0.87249607, (float)0.87567532, -(float)0.87881714, (float)0.88192129, (float)0.88498765, (float)0.88801610, (float)0.89100653, -(float)0.89395881, (float)0.89687276, (float)0.89974827, (float)0.90258533, (float)0.90538365, -(float)0.90814316, (float)0.91086388, (float)0.91354549, (float)0.91618794, (float)0.91879123, -(float)0.92135513, (float)0.92387950, (float)0.92636442, (float)0.92880958, (float)0.93121493, -(float)0.93358046, (float)0.93590593, (float)0.93819135, (float)0.94043654, (float)0.94264150, -(float)0.94480604, (float)0.94693011, (float)0.94901365, (float)0.95105654, (float)0.95305866, -(float)0.95501995, (float)0.95694035, (float)0.95881975, (float)0.96065807, (float)0.96245527, -(float)0.96421117, (float)0.96592581, (float)0.96759909, (float)0.96923089, (float)0.97082120, -(float)0.97236991, (float)0.97387701, (float)0.97534233, (float)0.97676587, (float)0.97814763, -(float)0.97948742, (float)0.98078531, (float)0.98204112, (float)0.98325491, (float)0.98442656, -(float)0.98555607, (float)0.98664331, (float)0.98768836, (float)0.98869103, (float)0.98965138, -(float)0.99056935, (float)0.99144489, (float)0.99227792, (float)0.99306846, (float)0.99381649, -(float)0.99452192, (float)0.99518472, (float)0.99580491, (float)0.99638247, (float)0.99691731, -(float)0.99740952, (float)0.99785894, (float)0.99826562, (float)0.99862951, (float)0.99895066, -(float)0.99922901, (float)0.99946457, (float)0.99965733, (float)0.99980724, (float)0.99991435, -(float)0.99997860, (float)1.00000000, (float)0.99997860, (float)0.99991435, (float)0.99980724, -(float)0.99965733, (float)0.99946457, (float)0.99922901, (float)0.99895066, (float)0.99862951, -(float)0.99826562, (float)0.99785894, (float)0.99740946, (float)0.99691731, (float)0.99638247, -(float)0.99580491, (float)0.99518472, (float)0.99452192, (float)0.99381644, (float)0.99306846, -(float)0.99227792, (float)0.99144489, (float)0.99056935, (float)0.98965138, (float)0.98869103, -(float)0.98768836, (float)0.98664331, (float)0.98555607, (float)0.98442656, (float)0.98325491, -(float)0.98204112, (float)0.98078525, (float)0.97948742, (float)0.97814757, (float)0.97676587, -(float)0.97534227, (float)0.97387695, (float)0.97236991, (float)0.97082120, (float)0.96923089, -(float)0.96759909, (float)0.96592581, (float)0.96421117, (float)0.96245521, (float)0.96065807, -(float)0.95881969, (float)0.95694029, (float)0.95501995, (float)0.95305860, (float)0.95105648, -(float)0.94901365, (float)0.94693011, (float)0.94480604, (float)0.94264150, (float)0.94043654, -(float)0.93819129, (float)0.93590593, (float)0.93358046, (float)0.93121493, (float)0.92880952, -(float)0.92636436, (float)0.92387950, (float)0.92135507, (float)0.91879123, (float)0.91618794, -(float)0.91354543, (float)0.91086382, (float)0.90814310, (float)0.90538365, (float)0.90258527, -(float)0.89974827, (float)0.89687276, (float)0.89395875, (float)0.89100647, (float)0.88801610, -(float)0.88498759, (float)0.88192123, (float)0.87881714, (float)0.87567532, (float)0.87249595, -(float)0.86927933, (float)0.86602539, (float)0.86273432, (float)0.85940641, (float)0.85604161, -(float)0.85264009, (float)0.84920216, (float)0.84572780, (float)0.84221715, (float)0.83867055, -(float)0.83508795, (float)0.83146954, (float)0.82781565, (float)0.82412612, (float)0.82040137, -(float)0.81664157, (float)0.81284660, (float)0.80901700, (float)0.80515265, (float)0.80125374, -(float)0.79732066, (float)0.79335332, (float)0.78935200, (float)0.78531694, (float)0.78124815, -(float)0.77714586, (float)0.77301049, (float)0.76884180, (float)0.76464021, (float)0.76040596, -(float)0.75613904, (float)0.75183970, (float)0.74750835, (float)0.74314481, (float)0.73874938, -(float)0.73432249, (float)0.72986400, (float)0.72537428, (float)0.72085363, (float)0.71630186, -(float)0.71171951, (float)0.70710677, (float)0.70246363, (float)0.69779032, (float)0.69308734, -(float)0.68835449, (float)0.68359220, (float)0.67880070, (float)0.67398006, (float)0.66913044, -(float)0.66425240, (float)0.65934575, (float)0.65441096, (float)0.64944804, (float)0.64445722, -(float)0.63943905, (float)0.63439327, (float)0.62932026, (float)0.62422055, (float)0.61909389, -(float)0.61394072, (float)0.60876143, (float)0.60355592, (float)0.59832448, (float)0.59306765, -(float)0.58778518, (float)0.58247757, (float)0.57714522, (float)0.57178789, (float)0.56640613, -(float)0.56100023, (float)0.55557019, (float)0.55011630, (float)0.54463905, (float)0.53913826, -(float)0.53361434, (float)0.52806783, (float)0.52249849, (float)0.51690674, (float)0.51129305, -(float)0.50565726, (float)0.50000006, (float)0.49432117, (float)0.48862115, (float)0.48290038, -(float)0.47715873, (float)0.47139663, (float)0.46561456, (float)0.45981231, (float)0.45399037, -(float)0.44814920, (float)0.44228864, (float)0.43640912, (float)0.43051112, (float)0.42459446, -(float)0.41865960, (float)0.41270703, (float)0.40673658, (float)0.40074870, (float)0.39474386, -(float)0.38872188, (float)0.38268328, (float)0.37662849, (float)0.37055734, (float)0.36447033, -(float)0.35836792, (float)0.35224995, (float)0.34611690, (float)0.33996922, (float)0.33380675, -(float)0.32763001, (float)0.32143945, (float)0.31523487, (float)0.30901679, (float)0.30278572, -(float)0.29654145, (float)0.29028472, (float)0.28401530, (float)0.27773371, (float)0.27144048, -(float)0.26513538, (float)0.25881892, (float)0.25249159, (float)0.24615324, (float)0.23980433, -(float)0.23344538, (float)0.22707619, (float)0.22069728, (float)0.21430916, (float)0.20791161, -(float)0.20150517, (float)0.19509031, (float)0.18866688, (float)0.18223536, (float)0.17579627, -(float)0.16934940, (float)0.16289529, (float)0.15643445, (float)0.14996666, (float)0.14349243, -(float)0.13701232, (float)0.13052608, (float)0.12403426, (float)0.11753736, (float)0.11103519, -(float)0.10452849, (float)0.09801710, (float)0.09150149, (float)0.08498220, (float)0.07845904, -(float)0.07193252, (float)0.06540315, (float)0.05887074, (float)0.05233581, (float)0.04579888, -(float)0.03925974, (float)0.03271893, (float)0.02617695, (float)0.01963361, (float)0.01308943, -(float)0.00654493, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000 -}; - - -// Hanning window: for 30ms with 1024 fft with symmetric zeros at 16kHz -static const float kBlocks480w1024[1024] = { -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00327249, (float)0.00654494, -(float)0.00981732, (float)0.01308960, (float)0.01636173, (float)0.01963369, (float)0.02290544, -(float)0.02617695, (float)0.02944817, (float)0.03271908, (float)0.03598964, (float)0.03925982, -(float)0.04252957, (float)0.04579887, (float)0.04906768, (float)0.05233596, (float)0.05560368, -(float)0.05887080, (float)0.06213730, (float)0.06540313, (float)0.06866825, (float)0.07193266, -(float)0.07519628, (float)0.07845910, (float)0.08172107, (float)0.08498218, (float)0.08824237, -(float)0.09150162, (float)0.09475989, (float)0.09801714, (float)0.10127335, (float)0.10452846, -(float)0.10778246, (float)0.11103531, (float)0.11428697, (float)0.11753740, (float)0.12078657, -(float)0.12403446, (float)0.12728101, (float)0.13052620, (float)0.13376999, (float)0.13701233, -(float)0.14025325, (float)0.14349262, (float)0.14673047, (float)0.14996676, (float)0.15320145, -(float)0.15643448, (float)0.15966582, (float)0.16289547, (float)0.16612339, (float)0.16934951, -(float)0.17257382, (float)0.17579629, (float)0.17901687, (float)0.18223552, (float)0.18545224, -(float)0.18866697, (float)0.19187967, (float)0.19509032, (float)0.19829889, (float)0.20150533, -(float)0.20470962, (float)0.20791170, (float)0.21111156, (float)0.21430916, (float)0.21750447, -(float)0.22069745, (float)0.22388805, (float)0.22707628, (float)0.23026206, (float)0.23344538, -(float)0.23662618, (float)0.23980446, (float)0.24298020, (float)0.24615330, (float)0.24932377, -(float)0.25249159, (float)0.25565669, (float)0.25881904, (float)0.26197866, (float)0.26513544, -(float)0.26828939, (float)0.27144045, (float)0.27458861, (float)0.27773386, (float)0.28087610, -(float)0.28401536, (float)0.28715158, (float)0.29028466, (float)0.29341471, (float)0.29654160, -(float)0.29966527, (float)0.30278578, (float)0.30590302, (float)0.30901700, (float)0.31212768, -(float)0.31523499, (float)0.31833893, (float)0.32143945, (float)0.32453656, (float)0.32763019, -(float)0.33072028, (float)0.33380687, (float)0.33688986, (float)0.33996925, (float)0.34304500, -(float)0.34611708, (float)0.34918544, (float)0.35225007, (float)0.35531089, (float)0.35836795, -(float)0.36142117, (float)0.36447051, (float)0.36751595, (float)0.37055743, (float)0.37359497, -(float)0.37662852, (float)0.37965801, (float)0.38268346, (float)0.38570479, (float)0.38872197, -(float)0.39173502, (float)0.39474389, (float)0.39774847, (float)0.40074885, (float)0.40374491, -(float)0.40673664, (float)0.40972406, (float)0.41270703, (float)0.41568562, (float)0.41865975, -(float)0.42162940, (float)0.42459452, (float)0.42755508, (float)0.43051112, (float)0.43346250, -(float)0.43640924, (float)0.43935132, (float)0.44228873, (float)0.44522133, (float)0.44814920, -(float)0.45107228, (float)0.45399052, (float)0.45690390, (float)0.45981237, (float)0.46271592, -(float)0.46561453, (float)0.46850815, (float)0.47139674, (float)0.47428030, (float)0.47715878, -(float)0.48003215, (float)0.48290035, (float)0.48576337, (float)0.48862126, (float)0.49147385, -(float)0.49432120, (float)0.49716330, (float)0.50000000, (float)0.50283140, (float)0.50565743, -(float)0.50847799, (float)0.51129311, (float)0.51410276, (float)0.51690692, (float)0.51970553, -(float)0.52249855, (float)0.52528602, (float)0.52806789, (float)0.53084403, (float)0.53361452, -(float)0.53637928, (float)0.53913832, (float)0.54189163, (float)0.54463905, (float)0.54738063, -(float)0.55011642, (float)0.55284631, (float)0.55557024, (float)0.55828828, (float)0.56100029, -(float)0.56370628, (float)0.56640625, (float)0.56910014, (float)0.57178795, (float)0.57446963, -(float)0.57714522, (float)0.57981455, (float)0.58247769, (float)0.58513463, (float)0.58778524, -(float)0.59042960, (float)0.59306765, (float)0.59569931, (float)0.59832460, (float)0.60094351, -(float)0.60355598, (float)0.60616195, (float)0.60876143, (float)0.61135441, (float)0.61394083, -(float)0.61652070, (float)0.61909395, (float)0.62166059, (float)0.62422055, (float)0.62677383, -(float)0.62932038, (float)0.63186020, (float)0.63439333, (float)0.63691956, (float)0.63943899, -(float)0.64195162, (float)0.64445734, (float)0.64695615, (float)0.64944810, (float)0.65193301, -(float)0.65441096, (float)0.65688187, (float)0.65934587, (float)0.66180271, (float)0.66425246, -(float)0.66669512, (float)0.66913062, (float)0.67155898, (float)0.67398012, (float)0.67639405, -(float)0.67880076, (float)0.68120021, (float)0.68359232, (float)0.68597710, (float)0.68835455, -(float)0.69072467, (float)0.69308740, (float)0.69544262, (float)0.69779050, (float)0.70013082, -(float)0.70246369, (float)0.70478904, (float)0.70710677, (float)0.70941699, (float)0.71171963, -(float)0.71401459, (float)0.71630198, (float)0.71858168, (float)0.72085363, (float)0.72311789, -(float)0.72537440, (float)0.72762316, (float)0.72986406, (float)0.73209721, (float)0.73432255, -(float)0.73653996, (float)0.73874950, (float)0.74095118, (float)0.74314487, (float)0.74533057, -(float)0.74750835, (float)0.74967808, (float)0.75183982, (float)0.75399351, (float)0.75613910, -(float)0.75827658, (float)0.76040596, (float)0.76252723, (float)0.76464027, (float)0.76674515, -(float)0.76884186, (float)0.77093029, (float)0.77301043, (float)0.77508241, (float)0.77714598, -(float)0.77920127, (float)0.78124821, (float)0.78328675, (float)0.78531694, (float)0.78733873, -(float)0.78935206, (float)0.79135692, (float)0.79335338, (float)0.79534125, (float)0.79732066, -(float)0.79929149, (float)0.80125386, (float)0.80320752, (float)0.80515265, (float)0.80708915, -(float)0.80901700, (float)0.81093621, (float)0.81284672, (float)0.81474853, (float)0.81664157, -(float)0.81852591, (float)0.82040149, (float)0.82226825, (float)0.82412618, (float)0.82597536, -(float)0.82781565, (float)0.82964706, (float)0.83146966, (float)0.83328325, (float)0.83508795, -(float)0.83688378, (float)0.83867061, (float)0.84044838, (float)0.84221727, (float)0.84397703, -(float)0.84572780, (float)0.84746957, (float)0.84920216, (float)0.85092574, (float)0.85264021, -(float)0.85434544, (float)0.85604161, (float)0.85772866, (float)0.85940641, (float)0.86107504, -(float)0.86273444, (float)0.86438453, (float)0.86602545, (float)0.86765707, (float)0.86927933, -(float)0.87089235, (float)0.87249607, (float)0.87409031, (float)0.87567532, (float)0.87725097, -(float)0.87881714, (float)0.88037390, (float)0.88192129, (float)0.88345921, (float)0.88498765, -(float)0.88650668, (float)0.88801610, (float)0.88951612, (float)0.89100653, (float)0.89248741, -(float)0.89395881, (float)0.89542055, (float)0.89687276, (float)0.89831537, (float)0.89974827, -(float)0.90117162, (float)0.90258533, (float)0.90398932, (float)0.90538365, (float)0.90676826, -(float)0.90814316, (float)0.90950841, (float)0.91086388, (float)0.91220951, (float)0.91354549, -(float)0.91487163, (float)0.91618794, (float)0.91749454, (float)0.91879123, (float)0.92007810, -(float)0.92135513, (float)0.92262226, (float)0.92387950, (float)0.92512691, (float)0.92636442, -(float)0.92759192, (float)0.92880958, (float)0.93001723, (float)0.93121493, (float)0.93240267, -(float)0.93358046, (float)0.93474817, (float)0.93590593, (float)0.93705362, (float)0.93819135, -(float)0.93931901, (float)0.94043654, (float)0.94154406, (float)0.94264150, (float)0.94372880, -(float)0.94480604, (float)0.94587320, (float)0.94693011, (float)0.94797695, (float)0.94901365, -(float)0.95004016, (float)0.95105654, (float)0.95206273, (float)0.95305866, (float)0.95404440, -(float)0.95501995, (float)0.95598525, (float)0.95694035, (float)0.95788521, (float)0.95881975, -(float)0.95974404, (float)0.96065807, (float)0.96156180, (float)0.96245527, (float)0.96333838, -(float)0.96421117, (float)0.96507370, (float)0.96592581, (float)0.96676767, (float)0.96759909, -(float)0.96842021, (float)0.96923089, (float)0.97003126, (float)0.97082120, (float)0.97160077, -(float)0.97236991, (float)0.97312868, (float)0.97387701, (float)0.97461486, (float)0.97534233, -(float)0.97605932, (float)0.97676587, (float)0.97746199, (float)0.97814763, (float)0.97882277, -(float)0.97948742, (float)0.98014158, (float)0.98078531, (float)0.98141843, (float)0.98204112, -(float)0.98265332, (float)0.98325491, (float)0.98384601, (float)0.98442656, (float)0.98499662, -(float)0.98555607, (float)0.98610497, (float)0.98664331, (float)0.98717111, (float)0.98768836, -(float)0.98819500, (float)0.98869103, (float)0.98917651, (float)0.98965138, (float)0.99011570, -(float)0.99056935, (float)0.99101239, (float)0.99144489, (float)0.99186671, (float)0.99227792, -(float)0.99267852, (float)0.99306846, (float)0.99344778, (float)0.99381649, (float)0.99417448, -(float)0.99452192, (float)0.99485862, (float)0.99518472, (float)0.99550015, (float)0.99580491, -(float)0.99609905, (float)0.99638247, (float)0.99665523, (float)0.99691731, (float)0.99716878, -(float)0.99740952, (float)0.99763954, (float)0.99785894, (float)0.99806762, (float)0.99826562, -(float)0.99845290, (float)0.99862951, (float)0.99879545, (float)0.99895066, (float)0.99909520, -(float)0.99922901, (float)0.99935216, (float)0.99946457, (float)0.99956632, (float)0.99965733, -(float)0.99973762, (float)0.99980724, (float)0.99986613, (float)0.99991435, (float)0.99995178, -(float)0.99997860, (float)0.99999464, (float)1.00000000, (float)0.99999464, (float)0.99997860, -(float)0.99995178, (float)0.99991435, (float)0.99986613, (float)0.99980724, (float)0.99973762, -(float)0.99965733, (float)0.99956632, (float)0.99946457, (float)0.99935216, (float)0.99922901, -(float)0.99909520, (float)0.99895066, (float)0.99879545, (float)0.99862951, (float)0.99845290, -(float)0.99826562, (float)0.99806762, (float)0.99785894, (float)0.99763954, (float)0.99740946, -(float)0.99716872, (float)0.99691731, (float)0.99665523, (float)0.99638247, (float)0.99609905, -(float)0.99580491, (float)0.99550015, (float)0.99518472, (float)0.99485862, (float)0.99452192, -(float)0.99417448, (float)0.99381644, (float)0.99344778, (float)0.99306846, (float)0.99267852, -(float)0.99227792, (float)0.99186671, (float)0.99144489, (float)0.99101239, (float)0.99056935, -(float)0.99011564, (float)0.98965138, (float)0.98917651, (float)0.98869103, (float)0.98819494, -(float)0.98768836, (float)0.98717111, (float)0.98664331, (float)0.98610497, (float)0.98555607, -(float)0.98499656, (float)0.98442656, (float)0.98384601, (float)0.98325491, (float)0.98265326, -(float)0.98204112, (float)0.98141843, (float)0.98078525, (float)0.98014158, (float)0.97948742, -(float)0.97882277, (float)0.97814757, (float)0.97746193, (float)0.97676587, (float)0.97605932, -(float)0.97534227, (float)0.97461486, (float)0.97387695, (float)0.97312862, (float)0.97236991, -(float)0.97160077, (float)0.97082120, (float)0.97003126, (float)0.96923089, (float)0.96842015, -(float)0.96759909, (float)0.96676761, (float)0.96592581, (float)0.96507365, (float)0.96421117, -(float)0.96333838, (float)0.96245521, (float)0.96156180, (float)0.96065807, (float)0.95974404, -(float)0.95881969, (float)0.95788515, (float)0.95694029, (float)0.95598525, (float)0.95501995, -(float)0.95404440, (float)0.95305860, (float)0.95206267, (float)0.95105648, (float)0.95004016, -(float)0.94901365, (float)0.94797695, (float)0.94693011, (float)0.94587314, (float)0.94480604, -(float)0.94372880, (float)0.94264150, (float)0.94154406, (float)0.94043654, (float)0.93931895, -(float)0.93819129, (float)0.93705362, (float)0.93590593, (float)0.93474817, (float)0.93358046, -(float)0.93240267, (float)0.93121493, (float)0.93001723, (float)0.92880952, (float)0.92759192, -(float)0.92636436, (float)0.92512691, (float)0.92387950, (float)0.92262226, (float)0.92135507, -(float)0.92007804, (float)0.91879123, (float)0.91749448, (float)0.91618794, (float)0.91487157, -(float)0.91354543, (float)0.91220951, (float)0.91086382, (float)0.90950835, (float)0.90814310, -(float)0.90676820, (float)0.90538365, (float)0.90398932, (float)0.90258527, (float)0.90117157, -(float)0.89974827, (float)0.89831525, (float)0.89687276, (float)0.89542055, (float)0.89395875, -(float)0.89248741, (float)0.89100647, (float)0.88951600, (float)0.88801610, (float)0.88650662, -(float)0.88498759, (float)0.88345915, (float)0.88192123, (float)0.88037384, (float)0.87881714, -(float)0.87725091, (float)0.87567532, (float)0.87409031, (float)0.87249595, (float)0.87089223, -(float)0.86927933, (float)0.86765701, (float)0.86602539, (float)0.86438447, (float)0.86273432, -(float)0.86107504, (float)0.85940641, (float)0.85772860, (float)0.85604161, (float)0.85434544, -(float)0.85264009, (float)0.85092574, (float)0.84920216, (float)0.84746951, (float)0.84572780, -(float)0.84397697, (float)0.84221715, (float)0.84044844, (float)0.83867055, (float)0.83688372, -(float)0.83508795, (float)0.83328319, (float)0.83146954, (float)0.82964706, (float)0.82781565, -(float)0.82597530, (float)0.82412612, (float)0.82226813, (float)0.82040137, (float)0.81852591, -(float)0.81664157, (float)0.81474847, (float)0.81284660, (float)0.81093609, (float)0.80901700, -(float)0.80708915, (float)0.80515265, (float)0.80320752, (float)0.80125374, (float)0.79929143, -(float)0.79732066, (float)0.79534125, (float)0.79335332, (float)0.79135686, (float)0.78935200, -(float)0.78733861, (float)0.78531694, (float)0.78328675, (float)0.78124815, (float)0.77920121, -(float)0.77714586, (float)0.77508223, (float)0.77301049, (float)0.77093029, (float)0.76884180, -(float)0.76674509, (float)0.76464021, (float)0.76252711, (float)0.76040596, (float)0.75827658, -(float)0.75613904, (float)0.75399339, (float)0.75183970, (float)0.74967796, (float)0.74750835, -(float)0.74533057, (float)0.74314481, (float)0.74095106, (float)0.73874938, (float)0.73653996, -(float)0.73432249, (float)0.73209721, (float)0.72986400, (float)0.72762305, (float)0.72537428, -(float)0.72311789, (float)0.72085363, (float)0.71858162, (float)0.71630186, (float)0.71401453, -(float)0.71171951, (float)0.70941705, (float)0.70710677, (float)0.70478898, (float)0.70246363, -(float)0.70013070, (float)0.69779032, (float)0.69544268, (float)0.69308734, (float)0.69072461, -(float)0.68835449, (float)0.68597704, (float)0.68359220, (float)0.68120021, (float)0.67880070, -(float)0.67639399, (float)0.67398006, (float)0.67155886, (float)0.66913044, (float)0.66669512, -(float)0.66425240, (float)0.66180259, (float)0.65934575, (float)0.65688181, (float)0.65441096, -(float)0.65193301, (float)0.64944804, (float)0.64695609, (float)0.64445722, (float)0.64195150, -(float)0.63943905, (float)0.63691956, (float)0.63439327, (float)0.63186014, (float)0.62932026, -(float)0.62677372, (float)0.62422055, (float)0.62166059, (float)0.61909389, (float)0.61652064, -(float)0.61394072, (float)0.61135429, (float)0.60876143, (float)0.60616189, (float)0.60355592, -(float)0.60094339, (float)0.59832448, (float)0.59569913, (float)0.59306765, (float)0.59042960, -(float)0.58778518, (float)0.58513451, (float)0.58247757, (float)0.57981461, (float)0.57714522, -(float)0.57446963, (float)0.57178789, (float)0.56910002, (float)0.56640613, (float)0.56370628, -(float)0.56100023, (float)0.55828822, (float)0.55557019, (float)0.55284619, (float)0.55011630, -(float)0.54738069, (float)0.54463905, (float)0.54189152, (float)0.53913826, (float)0.53637916, -(float)0.53361434, (float)0.53084403, (float)0.52806783, (float)0.52528596, (float)0.52249849, -(float)0.51970541, (float)0.51690674, (float)0.51410276, (float)0.51129305, (float)0.50847787, -(float)0.50565726, (float)0.50283122, (float)0.50000006, (float)0.49716327, (float)0.49432117, -(float)0.49147379, (float)0.48862115, (float)0.48576325, (float)0.48290038, (float)0.48003212, -(float)0.47715873, (float)0.47428021, (float)0.47139663, (float)0.46850798, (float)0.46561456, -(float)0.46271589, (float)0.45981231, (float)0.45690379, (float)0.45399037, (float)0.45107210, -(float)0.44814920, (float)0.44522130, (float)0.44228864, (float)0.43935123, (float)0.43640912, -(float)0.43346232, (float)0.43051112, (float)0.42755505, (float)0.42459446, (float)0.42162928, -(float)0.41865960, (float)0.41568545, (float)0.41270703, (float)0.40972400, (float)0.40673658, -(float)0.40374479, (float)0.40074870, (float)0.39774850, (float)0.39474386, (float)0.39173496, -(float)0.38872188, (float)0.38570464, (float)0.38268328, (float)0.37965804, (float)0.37662849, -(float)0.37359491, (float)0.37055734, (float)0.36751580, (float)0.36447033, (float)0.36142117, -(float)0.35836792, (float)0.35531086, (float)0.35224995, (float)0.34918529, (float)0.34611690, -(float)0.34304500, (float)0.33996922, (float)0.33688980, (float)0.33380675, (float)0.33072016, -(float)0.32763001, (float)0.32453656, (float)0.32143945, (float)0.31833887, (float)0.31523487, -(float)0.31212750, (float)0.30901679, (float)0.30590302, (float)0.30278572, (float)0.29966521, -(float)0.29654145, (float)0.29341453, (float)0.29028472, (float)0.28715155, (float)0.28401530, -(float)0.28087601, (float)0.27773371, (float)0.27458847, (float)0.27144048, (float)0.26828936, -(float)0.26513538, (float)0.26197854, (float)0.25881892, (float)0.25565651, (float)0.25249159, -(float)0.24932374, (float)0.24615324, (float)0.24298008, (float)0.23980433, (float)0.23662600, -(float)0.23344538, (float)0.23026201, (float)0.22707619, (float)0.22388794, (float)0.22069728, -(float)0.21750426, (float)0.21430916, (float)0.21111152, (float)0.20791161, (float)0.20470949, -(float)0.20150517, (float)0.19829892, (float)0.19509031, (float)0.19187963, (float)0.18866688, -(float)0.18545210, (float)0.18223536, (float)0.17901689, (float)0.17579627, (float)0.17257376, -(float)0.16934940, (float)0.16612324, (float)0.16289529, (float)0.15966584, (float)0.15643445, -(float)0.15320137, (float)0.14996666, (float)0.14673033, (float)0.14349243, (float)0.14025325, -(float)0.13701232, (float)0.13376991, (float)0.13052608, (float)0.12728085, (float)0.12403426, -(float)0.12078657, (float)0.11753736, (float)0.11428688, (float)0.11103519, (float)0.10778230, -(float)0.10452849, (float)0.10127334, (float)0.09801710, (float)0.09475980, (float)0.09150149, -(float)0.08824220, (float)0.08498220, (float)0.08172106, (float)0.07845904, (float)0.07519618, -(float)0.07193252, (float)0.06866808, (float)0.06540315, (float)0.06213728, (float)0.05887074, -(float)0.05560357, (float)0.05233581, (float)0.04906749, (float)0.04579888, (float)0.04252954, -(float)0.03925974, (float)0.03598953, (float)0.03271893, (float)0.02944798, (float)0.02617695, -(float)0.02290541, (float)0.01963361, (float)0.01636161, (float)0.01308943, (float)0.00981712, -(float)0.00654493, (float)0.00327244, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000, -(float)0.00000000, (float)0.00000000, (float)0.00000000, (float)0.00000000}; - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_NS_MAIN_SOURCE_WINDOWS_PRIVATE_H_ diff --git a/modules/audio_processing/utility/Android.mk b/modules/audio_processing/utility/Android.mk deleted file mode 100644 index 7e758cea2..000000000 --- a/modules/audio_processing/utility/Android.mk +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_apm_utility -LOCAL_MODULE_TAGS := optional -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := fft4g.c \ - ring_buffer.c - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH) - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/audio_processing/utility/fft4g.c b/modules/audio_processing/utility/fft4g.c deleted file mode 100644 index 9a84368c4..000000000 --- a/modules/audio_processing/utility/fft4g.c +++ /dev/null @@ -1,1356 +0,0 @@ -/* - * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html - * Copyright Takuya OOURA, 1996-2001 - * - * You may use, copy, modify and distribute this code for any purpose (include - * commercial use) and without fee. Please refer to this package when you modify - * this code. - * - * Changes: - * Trivial type modifications by the WebRTC authors. - */ - -/* -Fast Fourier/Cosine/Sine Transform - dimension :one - data length :power of 2 - decimation :frequency - radix :4, 2 - data :inplace - table :use -functions - cdft: Complex Discrete Fourier Transform - rdft: Real Discrete Fourier Transform - ddct: Discrete Cosine Transform - ddst: Discrete Sine Transform - dfct: Cosine Transform of RDFT (Real Symmetric DFT) - dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) -function prototypes - void cdft(int, int, float *, int *, float *); - void rdft(int, int, float *, int *, float *); - void ddct(int, int, float *, int *, float *); - void ddst(int, int, float *, int *, float *); - void dfct(int, float *, float *, int *, float *); - void dfst(int, float *, float *, int *, float *); - - --------- Complex DFT (Discrete Fourier Transform) -------- - [definition] - - X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k - X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k - ip[0] = 0; // first time only - cdft(2*n, 1, a, ip, w); - - ip[0] = 0; // first time only - cdft(2*n, -1, a, ip, w); - [parameters] - 2*n :data length (int) - n >= 1, n = power of 2 - a[0...2*n-1] :input/output data (float *) - input data - a[2*j] = Re(x[j]), - a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) - strictly, - length of ip >= - 2+(1<<(int)(log(n+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n/2-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - cdft(2*n, -1, a, ip, w); - is - cdft(2*n, 1, a, ip, w); - for (j = 0; j <= 2 * n - 1; j++) { - a[j] *= 1.0 / n; - } - . - - --------- Real DFT / Inverse of Real DFT -------- - [definition] - RDFT - R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 - I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) - a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + - sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + - sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k - ip[0] = 0; // first time only - rdft(n, 1, a, ip, w); - - ip[0] = 0; // first time only - rdft(n, -1, a, ip, w); - [parameters] - n :data length (int) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - - output data - a[2*k] = R[k], 0<=k - input data - a[2*j] = R[j], 0<=j= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n/2-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - rdft(n, 1, a, ip, w); - is - rdft(n, -1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- - [definition] - IDCT (excluding scale) - C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT - C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k - ip[0] = 0; // first time only - ddct(n, 1, a, ip, w); - - ip[0] = 0; // first time only - ddct(n, -1, a, ip, w); - [parameters] - n :data length (int) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - output data - a[k] = C[k], 0<=k= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/4-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - ddct(n, -1, a, ip, w); - is - a[0] *= 0.5; - ddct(n, 1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- DST (Discrete Sine Transform) / Inverse of DST -------- - [definition] - IDST (excluding scale) - S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST - S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 - ip[0] = 0; // first time only - ddst(n, 1, a, ip, w); - - ip[0] = 0; // first time only - ddst(n, -1, a, ip, w); - [parameters] - n :data length (int) - n >= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - - input data - a[j] = A[j], 0 - output data - a[k] = S[k], 0= 2+sqrt(n/2) - strictly, - length of ip >= - 2+(1<<(int)(log(n/2+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/4-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - ddst(n, -1, a, ip, w); - is - a[0] *= 0.5; - ddst(n, 1, a, ip, w); - for (j = 0; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - --------- Cosine Transform of RDFT (Real Symmetric DFT) -------- - [definition] - C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n - [usage] - ip[0] = 0; // first time only - dfct(n, a, t, ip, w); - [parameters] - n :data length - 1 (int) - n >= 2, n = power of 2 - a[0...n] :input/output data (float *) - output data - a[k] = C[k], 0<=k<=n - t[0...n/2] :work area (float *) - ip[0...*] :work area for bit reversal (int *) - length of ip >= 2+sqrt(n/4) - strictly, - length of ip >= - 2+(1<<(int)(log(n/4+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/8-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - a[0] *= 0.5; - a[n] *= 0.5; - dfct(n, a, t, ip, w); - is - a[0] *= 0.5; - a[n] *= 0.5; - dfct(n, a, t, ip, w); - for (j = 0; j <= n; j++) { - a[j] *= 2.0 / n; - } - . - - --------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- - [definition] - S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 - a[0...n-1] :input/output data (float *) - output data - a[k] = S[k], 0= 2+sqrt(n/4) - strictly, - length of ip >= - 2+(1<<(int)(log(n/4+0.5)/log(2))/2). - ip[0],ip[1] are pointers of the cos/sin table. - w[0...n*5/8-1] :cos/sin table (float *) - w[],ip[] are initialized if ip[0] == 0. - [remark] - Inverse of - dfst(n, a, t, ip, w); - is - dfst(n, a, t, ip, w); - for (j = 1; j <= n - 1; j++) { - a[j] *= 2.0 / n; - } - . - - -Appendix : - The cos/sin table is recalculated when the larger table required. - w[] and ip[] are compatible with all routines. -*/ - -void cdft(int n, int isgn, float *a, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void bitrv2(int n, int *ip, float *a); - void bitrv2conj(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void cftbsub(int n, float *a, float *w); - - if (n > (ip[0] << 2)) { - makewt(n >> 2, ip, w); - } - if (n > 4) { - if (isgn >= 0) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - } else { - bitrv2conj(n, ip + 2, a); - cftbsub(n, a, w); - } - } else if (n == 4) { - cftfsub(n, a, w); - } -} - - -void rdft(int n, int isgn, float *a, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void makect(int nc, int *ip, float *c); - void bitrv2(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void cftbsub(int n, float *a, float *w); - void rftfsub(int n, float *a, int nc, float *c); - void rftbsub(int n, float *a, int nc, float *c); - int nw, nc; - float xi; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 2)) { - nc = n >> 2; - makect(nc, ip, w + nw); - } - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xi = a[0] - a[1]; - a[0] += a[1]; - a[1] = xi; - } else { - a[1] = 0.5f * (a[0] - a[1]); - a[0] -= a[1]; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } -} - - -void ddct(int n, int isgn, float *a, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void makect(int nc, int *ip, float *c); - void bitrv2(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void cftbsub(int n, float *a, float *w); - void rftfsub(int n, float *a, int nc, float *c); - void rftbsub(int n, float *a, int nc, float *c); - void dctsub(int n, float *a, int nc, float *c); - int j, nw, nc; - float xr; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > nc) { - nc = n; - makect(nc, ip, w + nw); - } - if (isgn < 0) { - xr = a[n - 1]; - for (j = n - 2; j >= 2; j -= 2) { - a[j + 1] = a[j] - a[j - 1]; - a[j] += a[j - 1]; - } - a[1] = a[0] - xr; - a[0] += xr; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } - dctsub(n, a, nc, w + nw); - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xr = a[0] - a[1]; - a[0] += a[1]; - for (j = 2; j < n; j += 2) { - a[j - 1] = a[j] - a[j + 1]; - a[j] += a[j + 1]; - } - a[n - 1] = xr; - } -} - - -void ddst(int n, int isgn, float *a, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void makect(int nc, int *ip, float *c); - void bitrv2(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void cftbsub(int n, float *a, float *w); - void rftfsub(int n, float *a, int nc, float *c); - void rftbsub(int n, float *a, int nc, float *c); - void dstsub(int n, float *a, int nc, float *c); - int j, nw, nc; - float xr; - - nw = ip[0]; - if (n > (nw << 2)) { - nw = n >> 2; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > nc) { - nc = n; - makect(nc, ip, w + nw); - } - if (isgn < 0) { - xr = a[n - 1]; - for (j = n - 2; j >= 2; j -= 2) { - a[j + 1] = -a[j] - a[j - 1]; - a[j] -= a[j - 1]; - } - a[1] = a[0] + xr; - a[0] -= xr; - if (n > 4) { - rftbsub(n, a, nc, w + nw); - bitrv2(n, ip + 2, a); - cftbsub(n, a, w); - } else if (n == 4) { - cftfsub(n, a, w); - } - } - dstsub(n, a, nc, w + nw); - if (isgn >= 0) { - if (n > 4) { - bitrv2(n, ip + 2, a); - cftfsub(n, a, w); - rftfsub(n, a, nc, w + nw); - } else if (n == 4) { - cftfsub(n, a, w); - } - xr = a[0] - a[1]; - a[0] += a[1]; - for (j = 2; j < n; j += 2) { - a[j - 1] = -a[j] - a[j + 1]; - a[j] -= a[j + 1]; - } - a[n - 1] = -xr; - } -} - - -void dfct(int n, float *a, float *t, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void makect(int nc, int *ip, float *c); - void bitrv2(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void rftfsub(int n, float *a, int nc, float *c); - void dctsub(int n, float *a, int nc, float *c); - int j, k, l, m, mh, nw, nc; - float xr, xi, yr, yi; - - nw = ip[0]; - if (n > (nw << 3)) { - nw = n >> 3; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 1)) { - nc = n >> 1; - makect(nc, ip, w + nw); - } - m = n >> 1; - yi = a[m]; - xi = a[0] + a[n]; - a[0] -= a[n]; - t[0] = xi - yi; - t[m] = xi + yi; - if (n > 2) { - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - xr = a[j] - a[n - j]; - xi = a[j] + a[n - j]; - yr = a[k] - a[n - k]; - yi = a[k] + a[n - k]; - a[j] = xr; - a[k] = yr; - t[j] = xi - yi; - t[k] = xi + yi; - } - t[mh] = a[mh] + a[n - mh]; - a[mh] -= a[n - mh]; - dctsub(m, a, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, a); - cftfsub(m, a, w); - rftfsub(m, a, nc, w + nw); - } else if (m == 4) { - cftfsub(m, a, w); - } - a[n - 1] = a[0] - a[1]; - a[1] = a[0] + a[1]; - for (j = m - 2; j >= 2; j -= 2) { - a[2 * j + 1] = a[j] + a[j + 1]; - a[2 * j - 1] = a[j] - a[j + 1]; - } - l = 2; - m = mh; - while (m >= 2) { - dctsub(m, t, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, t); - cftfsub(m, t, w); - rftfsub(m, t, nc, w + nw); - } else if (m == 4) { - cftfsub(m, t, w); - } - a[n - l] = t[0] - t[1]; - a[l] = t[0] + t[1]; - k = 0; - for (j = 2; j < m; j += 2) { - k += l << 2; - a[k - l] = t[j] - t[j + 1]; - a[k + l] = t[j] + t[j + 1]; - } - l <<= 1; - mh = m >> 1; - for (j = 0; j < mh; j++) { - k = m - j; - t[j] = t[m + k] - t[m + j]; - t[k] = t[m + k] + t[m + j]; - } - t[mh] = t[m + mh]; - m = mh; - } - a[l] = t[0]; - a[n] = t[2] - t[1]; - a[0] = t[2] + t[1]; - } else { - a[1] = a[0]; - a[2] = t[0]; - a[0] = t[1]; - } -} - - -void dfst(int n, float *a, float *t, int *ip, float *w) -{ - void makewt(int nw, int *ip, float *w); - void makect(int nc, int *ip, float *c); - void bitrv2(int n, int *ip, float *a); - void cftfsub(int n, float *a, float *w); - void rftfsub(int n, float *a, int nc, float *c); - void dstsub(int n, float *a, int nc, float *c); - int j, k, l, m, mh, nw, nc; - float xr, xi, yr, yi; - - nw = ip[0]; - if (n > (nw << 3)) { - nw = n >> 3; - makewt(nw, ip, w); - } - nc = ip[1]; - if (n > (nc << 1)) { - nc = n >> 1; - makect(nc, ip, w + nw); - } - if (n > 2) { - m = n >> 1; - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - xr = a[j] + a[n - j]; - xi = a[j] - a[n - j]; - yr = a[k] + a[n - k]; - yi = a[k] - a[n - k]; - a[j] = xr; - a[k] = yr; - t[j] = xi + yi; - t[k] = xi - yi; - } - t[0] = a[mh] - a[n - mh]; - a[mh] += a[n - mh]; - a[0] = a[m]; - dstsub(m, a, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, a); - cftfsub(m, a, w); - rftfsub(m, a, nc, w + nw); - } else if (m == 4) { - cftfsub(m, a, w); - } - a[n - 1] = a[1] - a[0]; - a[1] = a[0] + a[1]; - for (j = m - 2; j >= 2; j -= 2) { - a[2 * j + 1] = a[j] - a[j + 1]; - a[2 * j - 1] = -a[j] - a[j + 1]; - } - l = 2; - m = mh; - while (m >= 2) { - dstsub(m, t, nc, w + nw); - if (m > 4) { - bitrv2(m, ip + 2, t); - cftfsub(m, t, w); - rftfsub(m, t, nc, w + nw); - } else if (m == 4) { - cftfsub(m, t, w); - } - a[n - l] = t[1] - t[0]; - a[l] = t[0] + t[1]; - k = 0; - for (j = 2; j < m; j += 2) { - k += l << 2; - a[k - l] = -t[j] - t[j + 1]; - a[k + l] = t[j] - t[j + 1]; - } - l <<= 1; - mh = m >> 1; - for (j = 1; j < mh; j++) { - k = m - j; - t[j] = t[m + k] + t[m + j]; - t[k] = t[m + k] - t[m + j]; - } - t[0] = t[m + mh]; - m = mh; - } - a[l] = t[0]; - } - a[0] = 0; -} - - -/* -------- initializing routines -------- */ - - -#include - -void makewt(int nw, int *ip, float *w) -{ - void bitrv2(int n, int *ip, float *a); - int j, nwh; - float delta, x, y; - - ip[0] = nw; - ip[1] = 1; - if (nw > 2) { - nwh = nw >> 1; - delta = (float)atan(1.0f) / nwh; - w[0] = 1; - w[1] = 0; - w[nwh] = (float)cos(delta * nwh); - w[nwh + 1] = w[nwh]; - if (nwh > 2) { - for (j = 2; j < nwh; j += 2) { - x = (float)cos(delta * j); - y = (float)sin(delta * j); - w[j] = x; - w[j + 1] = y; - w[nw - j] = y; - w[nw - j + 1] = x; - } - bitrv2(nw, ip + 2, w); - } - } -} - - -void makect(int nc, int *ip, float *c) -{ - int j, nch; - float delta; - - ip[1] = nc; - if (nc > 1) { - nch = nc >> 1; - delta = (float)atan(1.0f) / nch; - c[0] = (float)cos(delta * nch); - c[nch] = 0.5f * c[0]; - for (j = 1; j < nch; j++) { - c[j] = 0.5f * (float)cos(delta * j); - c[nc - j] = 0.5f * (float)sin(delta * j); - } - } -} - - -/* -------- child routines -------- */ - - -void bitrv2(int n, int *ip, float *a) -{ - int j, j1, k, k1, l, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - m2 = 2 * m; - if ((m << 3) == l) { - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - j1 = 2 * k + m2 + ip[k]; - k1 = j1 + m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - } else { - for (k = 1; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += m2; - xr = a[j1]; - xi = a[j1 + 1]; - yr = a[k1]; - yi = a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - } - } -} - - -void bitrv2conj(int n, int *ip, float *a) -{ - int j, j1, k, k1, l, m, m2; - float xr, xi, yr, yi; - - ip[0] = 0; - l = n; - m = 1; - while ((m << 3) < l) { - l >>= 1; - for (j = 0; j < m; j++) { - ip[m + j] = ip[j] + l; - } - m <<= 1; - } - m2 = 2 * m; - if ((m << 3) == l) { - for (k = 0; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 -= m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += 2 * m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - k1 = 2 * k + ip[k]; - a[k1 + 1] = -a[k1 + 1]; - j1 = k1 + m2; - k1 = j1 + m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - k1 += m2; - a[k1 + 1] = -a[k1 + 1]; - } - } else { - a[1] = -a[1]; - a[m2 + 1] = -a[m2 + 1]; - for (k = 1; k < m; k++) { - for (j = 0; j < k; j++) { - j1 = 2 * j + ip[k]; - k1 = 2 * k + ip[j]; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - j1 += m2; - k1 += m2; - xr = a[j1]; - xi = -a[j1 + 1]; - yr = a[k1]; - yi = -a[k1 + 1]; - a[j1] = yr; - a[j1 + 1] = yi; - a[k1] = xr; - a[k1 + 1] = xi; - } - k1 = 2 * k + ip[k]; - a[k1 + 1] = -a[k1 + 1]; - a[k1 + m2 + 1] = -a[k1 + m2 + 1]; - } - } -} - - -void cftfsub(int n, float *a, float *w) -{ - void cft1st(int n, float *a, float *w); - void cftmdl(int n, int l, float *a, float *w); - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - l = 2; - if (n > 8) { - cft1st(n, a, w); - l = 8; - while ((l << 2) < n) { - cftmdl(n, l, a, w); - l <<= 2; - } - } - if ((l << 2) == n) { - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } - } else { - for (j = 0; j < l; j += 2) { - j1 = j + l; - x0r = a[j] - a[j1]; - x0i = a[j + 1] - a[j1 + 1]; - a[j] += a[j1]; - a[j + 1] += a[j1 + 1]; - a[j1] = x0r; - a[j1 + 1] = x0i; - } - } -} - - -void cftbsub(int n, float *a, float *w) -{ - void cft1st(int n, float *a, float *w); - void cftmdl(int n, int l, float *a, float *w); - int j, j1, j2, j3, l; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - l = 2; - if (n > 8) { - cft1st(n, a, w); - l = 8; - while ((l << 2) < n) { - cftmdl(n, l, a, w); - l <<= 2; - } - } - if ((l << 2) == n) { - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = -a[j + 1] - a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = -a[j + 1] + a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i - x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i + x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i - x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i + x3r; - } - } else { - for (j = 0; j < l; j += 2) { - j1 = j + l; - x0r = a[j] - a[j1]; - x0i = -a[j + 1] + a[j1 + 1]; - a[j] += a[j1]; - a[j + 1] = -a[j + 1] - a[j1 + 1]; - a[j1] = x0r; - a[j1 + 1] = x0i; - } - } -} - - -void cft1st(int n, float *a, float *w) -{ - int j, k1, k2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - x0r = a[0] + a[2]; - x0i = a[1] + a[3]; - x1r = a[0] - a[2]; - x1i = a[1] - a[3]; - x2r = a[4] + a[6]; - x2i = a[5] + a[7]; - x3r = a[4] - a[6]; - x3i = a[5] - a[7]; - a[0] = x0r + x2r; - a[1] = x0i + x2i; - a[4] = x0r - x2r; - a[5] = x0i - x2i; - a[2] = x1r - x3i; - a[3] = x1i + x3r; - a[6] = x1r + x3i; - a[7] = x1i - x3r; - wk1r = w[2]; - x0r = a[8] + a[10]; - x0i = a[9] + a[11]; - x1r = a[8] - a[10]; - x1i = a[9] - a[11]; - x2r = a[12] + a[14]; - x2i = a[13] + a[15]; - x3r = a[12] - a[14]; - x3i = a[13] - a[15]; - a[8] = x0r + x2r; - a[9] = x0i + x2i; - a[12] = x2i - x0i; - a[13] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[10] = wk1r * (x0r - x0i); - a[11] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[14] = wk1r * (x0i - x0r); - a[15] = wk1r * (x0i + x0r); - k1 = 0; - for (j = 16; j < n; j += 16) { - k1 += 2; - k2 = 2 * k1; - wk2r = w[k1]; - wk2i = w[k1 + 1]; - wk1r = w[k2]; - wk1i = w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - x0r = a[j] + a[j + 2]; - x0i = a[j + 1] + a[j + 3]; - x1r = a[j] - a[j + 2]; - x1i = a[j + 1] - a[j + 3]; - x2r = a[j + 4] + a[j + 6]; - x2i = a[j + 5] + a[j + 7]; - x3r = a[j + 4] - a[j + 6]; - x3i = a[j + 5] - a[j + 7]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 4] = wk2r * x0r - wk2i * x0i; - a[j + 5] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 2] = wk1r * x0r - wk1i * x0i; - a[j + 3] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 6] = wk3r * x0r - wk3i * x0i; - a[j + 7] = wk3r * x0i + wk3i * x0r; - wk1r = w[k2 + 2]; - wk1i = w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - x0r = a[j + 8] + a[j + 10]; - x0i = a[j + 9] + a[j + 11]; - x1r = a[j + 8] - a[j + 10]; - x1i = a[j + 9] - a[j + 11]; - x2r = a[j + 12] + a[j + 14]; - x2i = a[j + 13] + a[j + 15]; - x3r = a[j + 12] - a[j + 14]; - x3i = a[j + 13] - a[j + 15]; - a[j + 8] = x0r + x2r; - a[j + 9] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j + 12] = -wk2i * x0r - wk2r * x0i; - a[j + 13] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j + 10] = wk1r * x0r - wk1i * x0i; - a[j + 11] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j + 14] = wk3r * x0r - wk3i * x0i; - a[j + 15] = wk3r * x0i + wk3i * x0r; - } -} - - -void cftmdl(int n, int l, float *a, float *w) -{ - int j, j1, j2, j3, k, k1, k2, m, m2; - float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; - float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; - - m = l << 2; - for (j = 0; j < l; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x0r - x2r; - a[j2 + 1] = x0i - x2i; - a[j1] = x1r - x3i; - a[j1 + 1] = x1i + x3r; - a[j3] = x1r + x3i; - a[j3 + 1] = x1i - x3r; - } - wk1r = w[2]; - for (j = m; j < l + m; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - a[j2] = x2i - x0i; - a[j2 + 1] = x0r - x2r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * (x0r - x0i); - a[j1 + 1] = wk1r * (x0r + x0i); - x0r = x3i + x1r; - x0i = x3r - x1i; - a[j3] = wk1r * (x0i - x0r); - a[j3 + 1] = wk1r * (x0i + x0r); - } - k1 = 0; - m2 = 2 * m; - for (k = m2; k < n; k += m2) { - k1 += 2; - k2 = 2 * k1; - wk2r = w[k1]; - wk2i = w[k1 + 1]; - wk1r = w[k2]; - wk1i = w[k2 + 1]; - wk3r = wk1r - 2 * wk2i * wk1i; - wk3i = 2 * wk2i * wk1r - wk1i; - for (j = k; j < l + k; j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = wk2r * x0r - wk2i * x0i; - a[j2 + 1] = wk2r * x0i + wk2i * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - wk1r = w[k2 + 2]; - wk1i = w[k2 + 3]; - wk3r = wk1r - 2 * wk2r * wk1i; - wk3i = 2 * wk2r * wk1r - wk1i; - for (j = k + m; j < l + (k + m); j += 2) { - j1 = j + l; - j2 = j1 + l; - j3 = j2 + l; - x0r = a[j] + a[j1]; - x0i = a[j + 1] + a[j1 + 1]; - x1r = a[j] - a[j1]; - x1i = a[j + 1] - a[j1 + 1]; - x2r = a[j2] + a[j3]; - x2i = a[j2 + 1] + a[j3 + 1]; - x3r = a[j2] - a[j3]; - x3i = a[j2 + 1] - a[j3 + 1]; - a[j] = x0r + x2r; - a[j + 1] = x0i + x2i; - x0r -= x2r; - x0i -= x2i; - a[j2] = -wk2i * x0r - wk2r * x0i; - a[j2 + 1] = -wk2i * x0i + wk2r * x0r; - x0r = x1r - x3i; - x0i = x1i + x3r; - a[j1] = wk1r * x0r - wk1i * x0i; - a[j1 + 1] = wk1r * x0i + wk1i * x0r; - x0r = x1r + x3i; - x0i = x1i - x3r; - a[j3] = wk3r * x0r - wk3i * x0i; - a[j3 + 1] = wk3r * x0i + wk3i * x0r; - } - } -} - - -void rftfsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr, xi, yr, yi; - - m = n >> 1; - ks = 2 * nc / m; - kk = 0; - for (j = 2; j < m; j += 2) { - k = n - j; - kk += ks; - wkr = 0.5f - c[nc - kk]; - wki = c[kk]; - xr = a[j] - a[k]; - xi = a[j + 1] + a[k + 1]; - yr = wkr * xr - wki * xi; - yi = wkr * xi + wki * xr; - a[j] -= yr; - a[j + 1] -= yi; - a[k] += yr; - a[k + 1] -= yi; - } -} - - -void rftbsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr, xi, yr, yi; - - a[1] = -a[1]; - m = n >> 1; - ks = 2 * nc / m; - kk = 0; - for (j = 2; j < m; j += 2) { - k = n - j; - kk += ks; - wkr = 0.5f - c[nc - kk]; - wki = c[kk]; - xr = a[j] - a[k]; - xi = a[j + 1] + a[k + 1]; - yr = wkr * xr + wki * xi; - yi = wkr * xi - wki * xr; - a[j] -= yr; - a[j + 1] = yi - a[j + 1]; - a[k] += yr; - a[k + 1] = yi - a[k + 1]; - } - a[m + 1] = -a[m + 1]; -} - - -void dctsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr; - - m = n >> 1; - ks = nc / n; - kk = 0; - for (j = 1; j < m; j++) { - k = n - j; - kk += ks; - wkr = c[kk] - c[nc - kk]; - wki = c[kk] + c[nc - kk]; - xr = wki * a[j] - wkr * a[k]; - a[j] = wkr * a[j] + wki * a[k]; - a[k] = xr; - } - a[m] *= c[0]; -} - - -void dstsub(int n, float *a, int nc, float *c) -{ - int j, k, kk, ks, m; - float wkr, wki, xr; - - m = n >> 1; - ks = nc / n; - kk = 0; - for (j = 1; j < m; j++) { - k = n - j; - kk += ks; - wkr = c[kk] - c[nc - kk]; - wki = c[kk] + c[nc - kk]; - xr = wki * a[k] - wkr * a[j]; - a[k] = wkr * a[k] + wki * a[j]; - a[j] = xr; - } - a[m] *= c[0]; -} - diff --git a/modules/audio_processing/utility/fft4g.h b/modules/audio_processing/utility/fft4g.h deleted file mode 100644 index 373ff1489..000000000 --- a/modules/audio_processing/utility/fft4g.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_FFT4G_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_FFT4G_H_ - -void rdft(int, int, float *, int *, float *); -void cdft(int, int, float *, int *, float *); - -#endif - diff --git a/modules/audio_processing/utility/ring_buffer.c b/modules/audio_processing/utility/ring_buffer.c deleted file mode 100644 index ea2e3544b..000000000 --- a/modules/audio_processing/utility/ring_buffer.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Provides a generic ring buffer that can be written to and read from with - * arbitrarily sized blocks. The AEC uses this for several different tasks. - */ - -#include -#include -#include "ring_buffer.h" - -typedef struct { - int readPos; - int writePos; - int size; - char rwWrap; - bufdata_t *data; -} buf_t; - -enum {SAME_WRAP, DIFF_WRAP}; - -int WebRtcApm_CreateBuffer(void **bufInst, int size) -{ - buf_t *buf = NULL; - - if (size < 0) { - return -1; - } - - buf = malloc(sizeof(buf_t)); - *bufInst = buf; - if (buf == NULL) { - return -1; - } - - buf->data = malloc(size*sizeof(bufdata_t)); - if (buf->data == NULL) { - free(buf); - buf = NULL; - return -1; - } - - buf->size = size; - return 0; -} - -int WebRtcApm_InitBuffer(void *bufInst) -{ - buf_t *buf = (buf_t*)bufInst; - - buf->readPos = 0; - buf->writePos = 0; - buf->rwWrap = SAME_WRAP; - - // Initialize buffer to zeros - memset(buf->data, 0, sizeof(bufdata_t)*buf->size); - - return 0; -} - -int WebRtcApm_FreeBuffer(void *bufInst) -{ - buf_t *buf = (buf_t*)bufInst; - - if (buf == NULL) { - return -1; - } - - free(buf->data); - free(buf); - - return 0; -} - -int WebRtcApm_ReadBuffer(void *bufInst, bufdata_t *data, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; - - if (size <= 0 || size > buf->size) { - return -1; - } - - n = size; - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->size - buf->readPos; - if (n > margin) { - buf->rwWrap = SAME_WRAP; - memcpy(data, buf->data + buf->readPos, - sizeof(bufdata_t)*margin); - buf->readPos = 0; - n = size - margin; - } - else { - memcpy(data, buf->data + buf->readPos, - sizeof(bufdata_t)*n); - buf->readPos += n; - return n; - } - } - - if (buf->rwWrap == SAME_WRAP) { - margin = buf->writePos - buf->readPos; - if (margin > n) - margin = n; - memcpy(data + size - n, buf->data + buf->readPos, - sizeof(bufdata_t)*margin); - buf->readPos += margin; - n -= margin; - } - - return size - n; -} - -int WebRtcApm_WriteBuffer(void *bufInst, const bufdata_t *data, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; - - if (size < 0 || size > buf->size) { - return -1; - } - - n = size; - if (buf->rwWrap == SAME_WRAP) { - margin = buf->size - buf->writePos; - if (n > margin) { - buf->rwWrap = DIFF_WRAP; - memcpy(buf->data + buf->writePos, data, - sizeof(bufdata_t)*margin); - buf->writePos = 0; - n = size - margin; - } - else { - memcpy(buf->data + buf->writePos, data, - sizeof(bufdata_t)*n); - buf->writePos += n; - return n; - } - } - - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->readPos - buf->writePos; - if (margin > n) - margin = n; - memcpy(buf->data + buf->writePos, data + size - n, - sizeof(bufdata_t)*margin); - buf->writePos += margin; - n -= margin; - } - - return size - n; -} - -int WebRtcApm_FlushBuffer(void *bufInst, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; - - if (size <= 0 || size > buf->size) { - return -1; - } - - n = size; - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->size - buf->readPos; - if (n > margin) { - buf->rwWrap = SAME_WRAP; - buf->readPos = 0; - n = size - margin; - } - else { - buf->readPos += n; - return n; - } - } - - if (buf->rwWrap == SAME_WRAP) { - margin = buf->writePos - buf->readPos; - if (margin > n) - margin = n; - buf->readPos += margin; - n -= margin; - } - - return size - n; -} - -int WebRtcApm_StuffBuffer(void *bufInst, int size) -{ - buf_t *buf = (buf_t*)bufInst; - int n = 0, margin = 0; - - if (size <= 0 || size > buf->size) { - return -1; - } - - n = size; - if (buf->rwWrap == SAME_WRAP) { - margin = buf->readPos; - if (n > margin) { - buf->rwWrap = DIFF_WRAP; - buf->readPos = buf->size - 1; - n -= margin + 1; - } - else { - buf->readPos -= n; - return n; - } - } - - if (buf->rwWrap == DIFF_WRAP) { - margin = buf->readPos - buf->writePos; - if (margin > n) - margin = n; - buf->readPos -= margin; - n -= margin; - } - - return size - n; -} - -int WebRtcApm_get_buffer_size(const void *bufInst) -{ - const buf_t *buf = (buf_t*)bufInst; - - if (buf->rwWrap == SAME_WRAP) - return buf->writePos - buf->readPos; - else - return buf->size - buf->readPos + buf->writePos; -} diff --git a/modules/audio_processing/utility/ring_buffer.h b/modules/audio_processing/utility/ring_buffer.h deleted file mode 100644 index 0fd261dfe..000000000 --- a/modules/audio_processing/utility/ring_buffer.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Specifies the interface for the AEC generic buffer. - */ - -#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ -#define WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ - -// Determines buffer datatype -typedef short bufdata_t; - -// Unless otherwise specified, functions return 0 on success and -1 on error -int WebRtcApm_CreateBuffer(void **bufInst, int size); -int WebRtcApm_InitBuffer(void *bufInst); -int WebRtcApm_FreeBuffer(void *bufInst); - -// Returns number of samples read -int WebRtcApm_ReadBuffer(void *bufInst, bufdata_t *data, int size); - -// Returns number of samples written -int WebRtcApm_WriteBuffer(void *bufInst, const bufdata_t *data, int size); - -// Returns number of samples flushed -int WebRtcApm_FlushBuffer(void *bufInst, int size); - -// Returns number of samples stuffed -int WebRtcApm_StuffBuffer(void *bufInst, int size); - -// Returns number of samples in buffer -int WebRtcApm_get_buffer_size(const void *bufInst); - -#endif // WEBRTC_MODULES_AUDIO_PROCESSING_UTILITY_RING_BUFFER_H_ diff --git a/modules/audio_processing/utility/util.gyp b/modules/audio_processing/utility/util.gyp deleted file mode 100644 index 3348da8a9..000000000 --- a/modules/audio_processing/utility/util.gyp +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../common_settings.gypi', - ], - 'targets': [ - { - 'target_name': 'apm_util', - 'type': '<(library)', - 'direct_dependent_settings': { - 'include_dirs': [ - '.', - ], - }, - 'sources': [ - 'ring_buffer.c', - 'ring_buffer.h', - 'fft4g.c', - 'fft4g.h', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/interface/module.h b/modules/interface/module.h deleted file mode 100644 index f2709789e..000000000 --- a/modules/interface/module.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef MODULE_H -#define MODULE_H - -#include "typedefs.h" - -namespace webrtc -{ - -class Module -{ -public: - // Returns version of the module and its components. - virtual int32_t Version(char* version, - uint32_t& remainingBufferInBytes, - uint32_t& position) const = 0; - - // Change the unique identifier of this object. - virtual int32_t ChangeUniqueId(const int32_t id) = 0; - - // Returns the number of milliseconds until the module want a worker - // thread to call Process. - virtual int32_t TimeUntilNextProcess() = 0 ; - - // Process any pending tasks such as timeouts. - virtual int32_t Process() = 0 ; - -protected: - virtual ~Module() {} -}; - -} // namespace webrtc - -#endif // MODULE_H diff --git a/modules/interface/module_common_types.h b/modules/interface/module_common_types.h deleted file mode 100644 index 1865b0f60..000000000 --- a/modules/interface/module_common_types.h +++ /dev/null @@ -1,1012 +0,0 @@ -#ifndef MODULE_COMMON_TYPES_H -#define MODULE_COMMON_TYPES_H - -#include // memcpy -#include - -#include "typedefs.h" -#include "common_types.h" - -#ifdef _WIN32 - #pragma warning(disable:4351) // remove warning "new behavior: elements of array - // 'array' will be default initialized" -#endif - -namespace webrtc -{ -struct RTPHeader -{ - bool markerBit; - WebRtc_UWord8 payloadType; - WebRtc_UWord16 sequenceNumber; - WebRtc_UWord32 timestamp; - WebRtc_UWord32 ssrc; - WebRtc_UWord8 numCSRCs; - WebRtc_UWord32 arrOfCSRCs[kRtpCsrcSize]; - WebRtc_UWord8 paddingLength; - WebRtc_UWord16 headerLength; -}; - -struct RTPAudioHeader -{ - WebRtc_UWord8 numEnergy; // number of valid entries in arrOfEnergy - WebRtc_UWord8 arrOfEnergy[kRtpCsrcSize]; // one energy byte (0-9) per channel - bool isCNG; // is this CNG - WebRtc_UWord8 channel; // number of channels 2 = stereo -}; - -struct RTPVideoHeaderH263 -{ - bool independentlyDecodable; // H.263-1998 if no P bit it's not independently decodable - bool bits; // H.263 mode B, Xor the lasy byte of previus packet with the - // first byte of this packet -}; -enum {kNoPictureId = -1}; -struct RTPVideoHeaderVP8 -{ - bool startBit; // Start of partition. - bool stopBit; // Stop of partition. - WebRtc_Word16 pictureId; // Picture ID index, 15 bits; - // kNoPictureId if PictureID does not exist. - bool nonReference; // Frame is discardable. -}; -union RTPVideoTypeHeader -{ - RTPVideoHeaderH263 H263; - RTPVideoHeaderVP8 VP8; -}; - -enum RTPVideoCodecTypes -{ - kRTPVideoGeneric = 0, - kRTPVideoH263 = 1, - kRTPVideoMPEG4 = 5, - kRTPVideoVP8 = 8, - kRTPVideoNoVideo = 10, - kRTPVideoFEC = 11, - kRTPVideoI420 = 12 -}; -struct RTPVideoHeader -{ - WebRtc_UWord16 width; // size - WebRtc_UWord16 height; - - bool isFirstPacket; // first packet in frame - RTPVideoCodecTypes codec; - RTPVideoTypeHeader codecHeader; -}; -union RTPTypeHeader -{ - RTPAudioHeader Audio; - RTPVideoHeader Video; -}; - -struct WebRtcRTPHeader -{ - RTPHeader header; - FrameType frameType; - RTPTypeHeader type; -}; - -class RTPFragmentationHeader -{ -public: - RTPFragmentationHeader() : - fragmentationVectorSize(0), - fragmentationOffset(NULL), - fragmentationLength(NULL), - fragmentationTimeDiff(NULL), - fragmentationPlType(NULL) - {}; - - ~RTPFragmentationHeader() - { - delete [] fragmentationOffset; - delete [] fragmentationLength; - delete [] fragmentationTimeDiff; - delete [] fragmentationPlType; - } - - RTPFragmentationHeader& operator=(const RTPFragmentationHeader& header) - { - if(this == &header) - { - return *this; - } - - if(header.fragmentationVectorSize != fragmentationVectorSize) - { - // new size of vectors - - // delete old - delete [] fragmentationOffset; - fragmentationOffset = NULL; - delete [] fragmentationLength; - fragmentationLength = NULL; - delete [] fragmentationTimeDiff; - fragmentationTimeDiff = NULL; - delete [] fragmentationPlType; - fragmentationPlType = NULL; - - if(header.fragmentationVectorSize > 0) - { - // allocate new - if(header.fragmentationOffset) - { - fragmentationOffset = new WebRtc_UWord32[header.fragmentationVectorSize]; - } - if(header.fragmentationLength) - { - fragmentationLength = new WebRtc_UWord32[header.fragmentationVectorSize]; - } - if(header.fragmentationTimeDiff) - { - fragmentationTimeDiff = new WebRtc_UWord16[header.fragmentationVectorSize]; - } - if(header.fragmentationPlType) - { - fragmentationPlType = new WebRtc_UWord8[header.fragmentationVectorSize]; - } - } - // set new size - fragmentationVectorSize = header.fragmentationVectorSize; - } - - if(header.fragmentationVectorSize > 0) - { - // copy values - if(header.fragmentationOffset) - { - memcpy(fragmentationOffset, header.fragmentationOffset, - header.fragmentationVectorSize * sizeof(WebRtc_UWord32)); - } - if(header.fragmentationLength) - { - memcpy(fragmentationLength, header.fragmentationLength, - header.fragmentationVectorSize * sizeof(WebRtc_UWord32)); - } - if(header.fragmentationTimeDiff) - { - memcpy(fragmentationTimeDiff, header.fragmentationTimeDiff, - header.fragmentationVectorSize * sizeof(WebRtc_UWord16)); - } - if(header.fragmentationPlType) - { - memcpy(fragmentationPlType, header.fragmentationPlType, - header.fragmentationVectorSize * sizeof(WebRtc_UWord8)); - } - } - return *this; - } - void VerifyAndAllocateFragmentationHeader( const WebRtc_UWord16 size) - { - if( fragmentationVectorSize < size) - { - WebRtc_UWord16 oldVectorSize = fragmentationVectorSize; - { - // offset - WebRtc_UWord32* oldOffsets = fragmentationOffset; - fragmentationOffset = new WebRtc_UWord32[size]; - memset(fragmentationOffset+oldVectorSize, 0, - sizeof(WebRtc_UWord32)*(size-oldVectorSize)); - // copy old values - memcpy(fragmentationOffset,oldOffsets, sizeof(WebRtc_UWord32) * oldVectorSize); - delete[] oldOffsets; - } - // length - { - WebRtc_UWord32* oldLengths = fragmentationLength; - fragmentationLength = new WebRtc_UWord32[size]; - memset(fragmentationLength+oldVectorSize, 0, - sizeof(WebRtc_UWord32) * (size- oldVectorSize)); - memcpy(fragmentationLength, oldLengths, - sizeof(WebRtc_UWord32) * oldVectorSize); - delete[] oldLengths; - } - // time diff - { - WebRtc_UWord16* oldTimeDiffs = fragmentationTimeDiff; - fragmentationTimeDiff = new WebRtc_UWord16[size]; - memset(fragmentationTimeDiff+oldVectorSize, 0, - sizeof(WebRtc_UWord16) * (size- oldVectorSize)); - memcpy(fragmentationTimeDiff, oldTimeDiffs, - sizeof(WebRtc_UWord16) * oldVectorSize); - delete[] oldTimeDiffs; - } - // payload type - { - WebRtc_UWord8* oldTimePlTypes = fragmentationPlType; - fragmentationPlType = new WebRtc_UWord8[size]; - memset(fragmentationPlType+oldVectorSize, 0, - sizeof(WebRtc_UWord8) * (size- oldVectorSize)); - memcpy(fragmentationPlType, oldTimePlTypes, - sizeof(WebRtc_UWord8) * oldVectorSize); - delete[] oldTimePlTypes; - } - fragmentationVectorSize = size; - } - } - - WebRtc_UWord16 fragmentationVectorSize; // Number of fragmentations - WebRtc_UWord32* fragmentationOffset; // Offset of pointer to data for each fragm. - WebRtc_UWord32* fragmentationLength; // Data size for each fragmentation - WebRtc_UWord16* fragmentationTimeDiff; // Timestamp difference relative "now" for - // each fragmentation - WebRtc_UWord8* fragmentationPlType; // Payload type of each fragmentation -}; - -struct RTCPVoIPMetric -{ - // RFC 3611 4.7 - WebRtc_UWord8 lossRate; - WebRtc_UWord8 discardRate; - WebRtc_UWord8 burstDensity; - WebRtc_UWord8 gapDensity; - WebRtc_UWord16 burstDuration; - WebRtc_UWord16 gapDuration; - WebRtc_UWord16 roundTripDelay; - WebRtc_UWord16 endSystemDelay; - WebRtc_UWord8 signalLevel; - WebRtc_UWord8 noiseLevel; - WebRtc_UWord8 RERL; - WebRtc_UWord8 Gmin; - WebRtc_UWord8 Rfactor; - WebRtc_UWord8 extRfactor; - WebRtc_UWord8 MOSLQ; - WebRtc_UWord8 MOSCQ; - WebRtc_UWord8 RXconfig; - WebRtc_UWord16 JBnominal; - WebRtc_UWord16 JBmax; - WebRtc_UWord16 JBabsMax; -}; - -// class describing a complete, or parts of an encoded frame. -class EncodedVideoData -{ -public: - EncodedVideoData() : - completeFrame(false), - missingFrame(false), - payloadData(NULL), - payloadSize(0), - bufferSize(0) - {}; - - EncodedVideoData(const EncodedVideoData& data) - { - payloadType = data.payloadType; - timeStamp = data.timeStamp; - renderTimeMs = data.renderTimeMs; - encodedWidth = data.encodedWidth; - encodedHeight = data.encodedHeight; - completeFrame = data.completeFrame; - missingFrame = data.missingFrame; - payloadSize = data.payloadSize; - fragmentationHeader = data.fragmentationHeader; - frameType = data.frameType; - codec = data.codec; - if (data.payloadSize > 0) - { - payloadData = new WebRtc_UWord8[data.payloadSize]; - memcpy(payloadData, data.payloadData, data.payloadSize); - } - else - { - payloadData = NULL; - } - } - - - ~EncodedVideoData() - { - delete [] payloadData; - }; - - EncodedVideoData& operator=(const EncodedVideoData& data) - { - if (this == &data) - { - return *this; - } - payloadType = data.payloadType; - timeStamp = data.timeStamp; - renderTimeMs = data.renderTimeMs; - encodedWidth = data.encodedWidth; - encodedHeight = data.encodedHeight; - completeFrame = data.completeFrame; - missingFrame = data.missingFrame; - payloadSize = data.payloadSize; - fragmentationHeader = data.fragmentationHeader; - frameType = data.frameType; - codec = data.codec; - if (data.payloadSize > 0) - { - delete [] payloadData; - payloadData = new WebRtc_UWord8[data.payloadSize]; - memcpy(payloadData, data.payloadData, data.payloadSize); - bufferSize = data.payloadSize; - } - return *this; - }; - void VerifyAndAllocate( const WebRtc_UWord32 size) - { - if (bufferSize < size) - { - WebRtc_UWord8* oldPayload = payloadData; - payloadData = new WebRtc_UWord8[size]; - memcpy(payloadData, oldPayload, sizeof(WebRtc_UWord8) * payloadSize); - - bufferSize = size; - delete[] oldPayload; - } - } - - WebRtc_UWord8 payloadType; - WebRtc_UWord32 timeStamp; - WebRtc_Word64 renderTimeMs; - WebRtc_UWord32 encodedWidth; - WebRtc_UWord32 encodedHeight; - bool completeFrame; - bool missingFrame; - WebRtc_UWord8* payloadData; - WebRtc_UWord32 payloadSize; - WebRtc_UWord32 bufferSize; - RTPFragmentationHeader fragmentationHeader; - FrameType frameType; - VideoCodecType codec; -}; - -// Video Content Metrics -struct VideoContentMetrics -{ - VideoContentMetrics(): motionMagnitudeNZ(0), sizeZeroMotion(0), spatialPredErr(0), - spatialPredErrH(0), spatialPredErrV(0), motionPredErr(0), - motionHorizontalness(0), motionClusterDistortion(0), - nativeWidth(0), nativeHeight(0), contentChange(false) { } - void Reset(){ motionMagnitudeNZ = 0; sizeZeroMotion = 0; spatialPredErr = 0; - spatialPredErrH = 0; spatialPredErrV = 0; motionPredErr = 0; - motionHorizontalness = 0; motionClusterDistortion = 0; - nativeWidth = 0; nativeHeight = 0; contentChange = false; } - - float motionMagnitudeNZ; - float sizeZeroMotion; - float spatialPredErr; - float spatialPredErrH; - float spatialPredErrV; - float motionPredErr; - float motionHorizontalness; - float motionClusterDistortion; - WebRtc_UWord32 nativeWidth; - WebRtc_UWord32 nativeHeight; - WebRtc_UWord32 nativeFrameRate; - bool contentChange; -}; - -/************************************************* - * - * VideoFrame class - * - * The VideoFrame class allows storing and - * handling of video frames. - * - * - *************************************************/ -class VideoFrame -{ -public: - VideoFrame(); - ~VideoFrame(); - /** - * Verifies that current allocated buffer size is larger than or equal to the input size. - * If the current buffer size is smaller, a new allocation is made and the old buffer data - * is copied to the new buffer. - * Buffer size is updated to minimumSize. - */ - WebRtc_Word32 VerifyAndAllocate(const WebRtc_UWord32 minimumSize); - /** - * Update length of data buffer in frame. Function verifies that new length is less or - * equal to allocated size. - */ - WebRtc_Word32 SetLength(const WebRtc_UWord32 newLength); - /* - * Swap buffer and size data - */ - WebRtc_Word32 Swap(WebRtc_UWord8*& newMemory, - WebRtc_UWord32& newLength, - WebRtc_UWord32& newSize); - /* - * Swap buffer and size data - */ - WebRtc_Word32 SwapFrame(VideoFrame& videoFrame); - /** - * Copy buffer: If newLength is bigger than allocated size, a new buffer of size length - * is allocated. - */ - WebRtc_Word32 CopyFrame(const VideoFrame& videoFrame); - /** - * Copy buffer: If newLength is bigger than allocated size, a new buffer of size length - * is allocated. - */ - WebRtc_Word32 CopyFrame(WebRtc_UWord32 length, const WebRtc_UWord8* sourceBuffer); - /** - * Delete VideoFrame and resets members to zero - */ - void Free(); - /** - * Set frame timestamp (90kHz) - */ - void SetTimeStamp(const WebRtc_UWord32 timeStamp) {_timeStamp = timeStamp;} - /** - * Get pointer to frame buffer - */ - WebRtc_UWord8* Buffer() const {return _buffer;} - - WebRtc_UWord8*& Buffer() {return _buffer;} - - /** - * Get allocated buffer size - */ - WebRtc_UWord32 Size() const {return _bufferSize;} - /** - * Get frame length - */ - WebRtc_UWord32 Length() const {return _bufferLength;} - /** - * Get frame timestamp (90kHz) - */ - WebRtc_UWord32 TimeStamp() const {return _timeStamp;} - /** - * Get frame width - */ - WebRtc_UWord32 Width() const {return _width;} - /** - * Get frame height - */ - WebRtc_UWord32 Height() const {return _height;} - /** - * Set frame width - */ - void SetWidth(const WebRtc_UWord32 width) {_width = width;} - /** - * Set frame height - */ - void SetHeight(const WebRtc_UWord32 height) {_height = height;} - /** - * Set render time in miliseconds - */ - void SetRenderTime(const WebRtc_Word64 renderTimeMs) {_renderTimeMs = renderTimeMs;} - /** - * Get render time in miliseconds - */ - WebRtc_Word64 RenderTimeMs() const {return _renderTimeMs;} - -private: - void Set(WebRtc_UWord8* buffer, - WebRtc_UWord32 size, - WebRtc_UWord32 length, - WebRtc_UWord32 timeStamp); - - WebRtc_UWord8* _buffer; // Pointer to frame buffer - WebRtc_UWord32 _bufferSize; // Allocated buffer size - WebRtc_UWord32 _bufferLength; // Length (in bytes) of buffer - WebRtc_UWord32 _timeStamp; // Timestamp of frame (90kHz) - WebRtc_UWord32 _width; - WebRtc_UWord32 _height; - WebRtc_Word64 _renderTimeMs; -}; // end of VideoFrame class declaration - -// inline implementation of VideoFrame class: -inline -VideoFrame::VideoFrame(): - _buffer(0), - _bufferSize(0), - _bufferLength(0), - _timeStamp(0), - _width(0), - _height(0), - _renderTimeMs(0) -{ - // -} -inline -VideoFrame::~VideoFrame() -{ - if(_buffer) - { - delete [] _buffer; - _buffer = NULL; - } -} - - -inline -WebRtc_Word32 -VideoFrame::VerifyAndAllocate(const WebRtc_UWord32 minimumSize) -{ - if (minimumSize < 1) - { - return -1; - } - if(minimumSize > _bufferSize) - { - // create buffer of sufficient size - WebRtc_UWord8* newBufferBuffer = new WebRtc_UWord8[minimumSize]; - if(_buffer) - { - // copy old data - memcpy(newBufferBuffer, _buffer, _bufferSize); - delete [] _buffer; - } - _buffer = newBufferBuffer; - _bufferSize = minimumSize; - } - return 0; -} - -inline -WebRtc_Word32 -VideoFrame::SetLength(const WebRtc_UWord32 newLength) -{ - if (newLength >_bufferSize ) - { // can't accomodate new value - return -1; - } - _bufferLength = newLength; - return 0; -} - -inline -WebRtc_Word32 -VideoFrame::SwapFrame(VideoFrame& videoFrame) -{ - WebRtc_UWord32 tmpTimeStamp = _timeStamp; - WebRtc_UWord32 tmpWidth = _width; - WebRtc_UWord32 tmpHeight = _height; - WebRtc_Word64 tmpRenderTime = _renderTimeMs; - - _timeStamp = videoFrame._timeStamp; - _width = videoFrame._width; - _height = videoFrame._height; - _renderTimeMs = videoFrame._renderTimeMs; - - videoFrame._timeStamp = tmpTimeStamp; - videoFrame._width = tmpWidth; - videoFrame._height = tmpHeight; - videoFrame._renderTimeMs = tmpRenderTime; - - return Swap(videoFrame._buffer, videoFrame._bufferLength, videoFrame._bufferSize); -} - -inline -WebRtc_Word32 -VideoFrame::Swap(WebRtc_UWord8*& newMemory, WebRtc_UWord32& newLength, WebRtc_UWord32& newSize) -{ - WebRtc_UWord8* tmpBuffer = _buffer; - WebRtc_UWord32 tmpLength = _bufferLength; - WebRtc_UWord32 tmpSize = _bufferSize; - _buffer = newMemory; - _bufferLength = newLength; - _bufferSize = newSize; - newMemory = tmpBuffer; - newLength = tmpLength; - newSize = tmpSize; - return 0; -} - -inline -WebRtc_Word32 -VideoFrame::CopyFrame(WebRtc_UWord32 length, const WebRtc_UWord8* sourceBuffer) -{ - if (length > _bufferSize) - { - WebRtc_Word32 ret = VerifyAndAllocate(length); - if (ret < 0) - { - return ret; - } - } - memcpy(_buffer, sourceBuffer, length); - _bufferLength = length; - return 0; -} - -inline -WebRtc_Word32 -VideoFrame::CopyFrame(const VideoFrame& videoFrame) -{ - if(CopyFrame(videoFrame.Length(), videoFrame.Buffer()) != 0) - { - return -1; - } - _timeStamp = videoFrame._timeStamp; - _width = videoFrame._width; - _height = videoFrame._height; - _renderTimeMs = videoFrame._renderTimeMs; - return 0; -} - -inline -void -VideoFrame::Free() -{ - _timeStamp = 0; - _bufferLength = 0; - _bufferSize = 0; - _height = 0; - _width = 0; - _renderTimeMs = 0; - - if(_buffer) - { - delete [] _buffer; - _buffer = NULL; - } -} - - -/************************************************* - * - * AudioFrame class - * - * The AudioFrame class holds up to 60 ms wideband - * audio. It allows for adding and subtracting frames - * while keeping track of the resulting states. - * - * Note - * - The +operator assume that you would never add - * exact opposite frames when deciding the resulting - * state. To do this use the -operator. - * - * - _audioChannel of 1 indicated mono, and 2 - * indicates stereo. - * - * - _payloadDataLengthInSamples is the number of - * samples per channel. Therefore, the total - * number of samples in _payloadData is - * (_payloadDataLengthInSamples * _audioChannel). - * - * - Stereo data is stored in interleaved fashion - * starting with the left channel. - * - *************************************************/ -class AudioFrame -{ -public: - enum{kMaxAudioFrameSizeSamples = 3840}; // stereo 32KHz 60ms 2*32*60 - - enum VADActivity - { - kVadActive = 0, - kVadPassive = 1, - kVadUnknown = 2 - }; - enum SpeechType - { - kNormalSpeech = 0, - kPLC = 1, - kCNG = 2, - kPLCCNG = 3, - kUndefined = 4 - }; - - AudioFrame(); - virtual ~AudioFrame(); - - WebRtc_Word32 UpdateFrame( - const WebRtc_Word32 id, - const WebRtc_UWord32 timeStamp, - const WebRtc_Word16* payloadData, - const WebRtc_UWord16 payloadDataLengthInSamples, - const WebRtc_UWord32 frequencyInHz, - const SpeechType speechType, - const VADActivity vadActivity, - const WebRtc_UWord8 audioChannel = 1, - const WebRtc_Word32 volume = -1, - const WebRtc_Word32 energy = -1); - - AudioFrame& Append(const AudioFrame& rhs); - - void Mute() const; - - AudioFrame& operator=(const AudioFrame& rhs); - AudioFrame& operator>>=(const WebRtc_Word32 rhs); - AudioFrame& operator+=(const AudioFrame& rhs); - AudioFrame& operator-=(const AudioFrame& rhs); - - WebRtc_Word32 _id; - WebRtc_UWord32 _timeStamp; - - // Supporting Stereo, stereo samples are interleaved - mutable WebRtc_Word16 _payloadData[kMaxAudioFrameSizeSamples]; - WebRtc_UWord16 _payloadDataLengthInSamples; - WebRtc_UWord32 _frequencyInHz; - WebRtc_UWord8 _audioChannel; - SpeechType _speechType; - VADActivity _vadActivity; - - WebRtc_UWord32 _energy; - WebRtc_Word32 _volume; -}; - -inline -AudioFrame::AudioFrame() - : - _id(-1), - _timeStamp(0), - _payloadData(), - _payloadDataLengthInSamples(0), - _frequencyInHz(0), - _audioChannel(1), - _speechType(kUndefined), - _vadActivity(kVadUnknown), - _energy(0xffffffff), - _volume(0xffffffff) -{ -} - -inline -AudioFrame::~AudioFrame() -{ -} - -inline -WebRtc_Word32 -AudioFrame::UpdateFrame( - const WebRtc_Word32 id, - const WebRtc_UWord32 timeStamp, - const WebRtc_Word16* payloadData, - const WebRtc_UWord16 payloadDataLengthInSamples, - const WebRtc_UWord32 frequencyInHz, - const SpeechType speechType, - const VADActivity vadActivity, - const WebRtc_UWord8 audioChannel, - const WebRtc_Word32 volume, - const WebRtc_Word32 energy) -{ - _id = id; - _timeStamp = timeStamp; - _frequencyInHz = frequencyInHz; - _speechType = speechType; - _vadActivity = vadActivity; - _volume = volume; - _audioChannel = audioChannel; - _energy = energy; - - if((payloadDataLengthInSamples > kMaxAudioFrameSizeSamples) || - (audioChannel > 2) || (audioChannel < 1)) - { - _payloadDataLengthInSamples = 0; - return -1; - } - _payloadDataLengthInSamples = payloadDataLengthInSamples; - if(payloadData != NULL) - { - memcpy(_payloadData, payloadData, sizeof(WebRtc_Word16) * - payloadDataLengthInSamples * _audioChannel); - } - else - { - memset(_payloadData,0,sizeof(WebRtc_Word16) * - payloadDataLengthInSamples * _audioChannel); - } - return 0; -} - -inline -void -AudioFrame::Mute() const -{ - memset(_payloadData, 0, _payloadDataLengthInSamples * sizeof(WebRtc_Word16)); -} - -inline -AudioFrame& -AudioFrame::operator=(const AudioFrame& rhs) -{ - // Sanity Check - if((rhs._payloadDataLengthInSamples > kMaxAudioFrameSizeSamples) || - (rhs._audioChannel > 2) || - (rhs._audioChannel < 1)) - { - return *this; - } - if(this == &rhs) - { - return *this; - } - _id = rhs._id; - _timeStamp = rhs._timeStamp; - _frequencyInHz = rhs._frequencyInHz; - _speechType = rhs._speechType; - _vadActivity = rhs._vadActivity; - _volume = rhs._volume; - _audioChannel = rhs._audioChannel; - _energy = rhs._energy; - - _payloadDataLengthInSamples = rhs._payloadDataLengthInSamples; - memcpy(_payloadData, rhs._payloadData, - sizeof(WebRtc_Word16) * rhs._payloadDataLengthInSamples * _audioChannel); - - return *this; -} - -inline -AudioFrame& -AudioFrame::operator>>=(const WebRtc_Word32 rhs) -{ - assert((_audioChannel > 0) && (_audioChannel < 3)); - if((_audioChannel > 2) || - (_audioChannel < 1)) - { - return *this; - } - for(WebRtc_UWord16 i = 0; i < _payloadDataLengthInSamples * _audioChannel; i++) - { - _payloadData[i] = WebRtc_Word16(_payloadData[i] >> rhs); - } - return *this; -} - -inline -AudioFrame& -AudioFrame::Append(const AudioFrame& rhs) -{ - // Sanity check - assert((_audioChannel > 0) && (_audioChannel < 3)); - if((_audioChannel > 2) || - (_audioChannel < 1)) - { - return *this; - } - if(_audioChannel != rhs._audioChannel) - { - return *this; - } - if((_vadActivity == kVadActive) || - rhs._vadActivity == kVadActive) - { - _vadActivity = kVadActive; - } - else if((_vadActivity == kVadUnknown) || - rhs._vadActivity == kVadUnknown) - { - _vadActivity = kVadUnknown; - } - if(_speechType != rhs._speechType) - { - _speechType = kUndefined; - } - - WebRtc_UWord16 offset = _payloadDataLengthInSamples * _audioChannel; - for(WebRtc_UWord16 i = 0; - i < rhs._payloadDataLengthInSamples * rhs._audioChannel; - i++) - { - _payloadData[offset+i] = rhs._payloadData[i]; - } - _payloadDataLengthInSamples += rhs._payloadDataLengthInSamples; - return *this; -} - -// merge vectors -inline -AudioFrame& -AudioFrame::operator+=(const AudioFrame& rhs) -{ - // Sanity check - assert((_audioChannel > 0) && (_audioChannel < 3)); - if((_audioChannel > 2) || - (_audioChannel < 1)) - { - return *this; - } - if(_audioChannel != rhs._audioChannel) - { - return *this; - } - bool noPrevData = false; - if(_payloadDataLengthInSamples != rhs._payloadDataLengthInSamples) - { - if(_payloadDataLengthInSamples == 0) - { - // special case we have no data to start with - _payloadDataLengthInSamples = rhs._payloadDataLengthInSamples; - noPrevData = true; - } else - { - return *this; - } - } - - if((_vadActivity == kVadActive) || - rhs._vadActivity == kVadActive) - { - _vadActivity = kVadActive; - } - else if((_vadActivity == kVadUnknown) || - rhs._vadActivity == kVadUnknown) - { - _vadActivity = kVadUnknown; - } - - if(_speechType != rhs._speechType) - { - _speechType = kUndefined; - } - - if(noPrevData) - { - memcpy(_payloadData, rhs._payloadData, - sizeof(WebRtc_Word16) * rhs._payloadDataLengthInSamples * _audioChannel); - } else - { - // IMPROVEMENT this can be done very fast in assembly - for(WebRtc_UWord16 i = 0; i < _payloadDataLengthInSamples * _audioChannel; i++) - { - WebRtc_Word32 wrapGuard = (WebRtc_Word32)_payloadData[i] + - (WebRtc_Word32)rhs._payloadData[i]; - if(wrapGuard < -32768) - { - _payloadData[i] = -32768; - }else if(wrapGuard > 32767) - { - _payloadData[i] = 32767; - }else - { - _payloadData[i] = (WebRtc_Word16)wrapGuard; - } - } - } - _energy = 0xffffffff; - _volume = 0xffffffff; - return *this; -} - -inline -AudioFrame& -AudioFrame::operator-=(const AudioFrame& rhs) -{ - // Sanity check - assert((_audioChannel > 0) && (_audioChannel < 3)); - if((_audioChannel > 2)|| - (_audioChannel < 1)) - { - return *this; - } - if((_payloadDataLengthInSamples != rhs._payloadDataLengthInSamples) || - (_audioChannel != rhs._audioChannel)) - { - return *this; - } - if((_vadActivity != kVadPassive) || - rhs._vadActivity != kVadPassive) - { - _vadActivity = kVadUnknown; - } - _speechType = kUndefined; - - for(WebRtc_UWord16 i = 0; i < _payloadDataLengthInSamples * _audioChannel; i++) - { - WebRtc_Word32 wrapGuard = (WebRtc_Word32)_payloadData[i] - - (WebRtc_Word32)rhs._payloadData[i]; - if(wrapGuard < -32768) - { - _payloadData[i] = -32768; - } - else if(wrapGuard > 32767) - { - _payloadData[i] = 32767; - } - else - { - _payloadData[i] = (WebRtc_Word16)wrapGuard; - } - } - _energy = 0xffffffff; - _volume = 0xffffffff; - return *this; -} - -} // namespace webrtc - -#endif // MODULE_COMMON_TYPES_H diff --git a/modules/media_file/OWNERS b/modules/media_file/OWNERS deleted file mode 100644 index 8d73cd9d2..000000000 --- a/modules/media_file/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -hellner@google.com -pwestin@google.com -perkj@google.com -niklase@google.com \ No newline at end of file diff --git a/modules/media_file/interface/media_file.h b/modules/media_file/interface/media_file.h deleted file mode 100644 index 66851d3ca..000000000 --- a/modules/media_file/interface/media_file.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_H_ -#define WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_H_ - -#include "common_types.h" -#include "typedefs.h" -#include "module.h" -#include "module_common_types.h" -#include "media_file_defines.h" - -namespace webrtc { -class MediaFile : public Module -{ -public: - // Factory method. Constructor disabled. id is the identifier for the - // MediaFile instance. - static MediaFile* CreateMediaFile(const WebRtc_Word32 id); - static void DestroyMediaFile(MediaFile* module); - - // Writes the version of the MediaFile to version. remainingBufferInBytes - // is both an input parameter and an output parameter. It indicates the size - // of version less messages in it. position is both an input parameter and - // an output parameter. It indicates the position of the NULL termination - // in the version string. - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - // Set the MediaFile instance identifier. - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - // Put 10-60ms of audio data from file into the audioBuffer depending on - // codec frame size. dataLengthInBytes is both an input and output - // parameter. As input parameter it indicates the size of audioBuffer. - // As output parameter it indicates the number of bytes written to - // audioBuffer. - // Note: This API only play mono audio but can be used on file containing - // audio with more channels (in which case the audio will be converted to - // mono). - virtual WebRtc_Word32 PlayoutAudioData( - WebRtc_Word8* audioBuffer, - WebRtc_UWord32& dataLengthInBytes) = 0; - - // Put one video frame into videoBuffer. dataLengthInBytes is both an input - // and output parameter. As input parameter it indicates the size of - // videoBuffer. As output parameter it indicates the number of bytes written - // to videoBuffer. - virtual WebRtc_Word32 PlayoutAVIVideoData( - WebRtc_Word8* videoBuffer, - WebRtc_UWord32& dataLengthInBytes) = 0; - - // Put 10-60ms, depending on codec frame size, of audio data from file into - // audioBufferLeft and audioBufferRight. The buffers contain the left and - // right channel of played out stereo audio. - // dataLengthInBytes is both an input and output parameter. As input - // parameter it indicates the size of both audioBufferLeft and - // audioBufferRight. As output parameter it indicates the number of bytes - // written to both audio buffers. - // Note: This API can only be successfully called for WAV files with stereo - // audio. - virtual WebRtc_Word32 PlayoutStereoData( - WebRtc_Word8* audioBufferLeft, - WebRtc_Word8* audioBufferRight, - WebRtc_UWord32& dataLengthInBytes) = 0; - - // Open the file specified by fileName (relative path is allowed) for - // reading. FileCallback::PlayNotification(..) will be called after - // notificationTimeMs of the file has been played if notificationTimeMs is - // greater than zero. If loop is true the file will be played until - // StopPlaying() is called. When end of file is reached the file is read - // from the start. format specifies the type of file fileName refers to. - // codecInst specifies the encoding of the audio data. Note that - // file formats that contain this information (like WAV files) don't need to - // provide a non-NULL codecInst. startPointMs and stopPointMs, unless zero, - // specify what part of the file should be read. From startPointMs ms to - // stopPointMs ms. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo audio is only supported for WAV files. - virtual WebRtc_Word32 StartPlayingAudioFile( - const WebRtc_Word8* fileName, - const WebRtc_UWord32 notificationTimeMs = 0, - const bool loop = false, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0) = 0; - - // Open the file specified by fileName for reading (relative path is - // allowed). If loop is true the file will be played until StopPlaying() is - // called. When end of file is reached the file is read from the start. - // format specifies the type of file fileName refers to. Only video will be - // read if videoOnly is true. - virtual WebRtc_Word32 StartPlayingVideoFile(const WebRtc_Word8* fileName, - const bool loop, - bool videoOnly, - const FileFormats format) = 0; - - // Prepare for playing audio from stream. - // FileCallback::PlayNotification(..) will be called after - // notificationTimeMs of the file has been played if notificationTimeMs is - // greater than zero. format specifies the type of file fileName refers to. - // codecInst specifies the encoding of the audio data. Note that - // file formats that contain this information (like WAV files) don't need to - // provide a non-NULL codecInst. startPointMs and stopPointMs, unless zero, - // specify what part of the file should be read. From startPointMs ms to - // stopPointMs ms. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo audio is only supported for WAV files. - virtual WebRtc_Word32 StartPlayingAudioStream( - InStream& stream, - const WebRtc_UWord32 notificationTimeMs = 0, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0) = 0; - - // Stop playing from file or stream. - virtual WebRtc_Word32 StopPlaying() = 0; - - // Return true if playing. - virtual bool IsPlaying() = 0; - - - // Set durationMs to the number of ms that has been played from file. - virtual WebRtc_Word32 PlayoutPositionMs( - WebRtc_UWord32& durationMs) const = 0; - - // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, - // to file. The audio frame size is determined by the codecInst.pacsize - // parameter of the last sucessfull StartRecordingAudioFile(..) call. - // Note: bufferLength must be exactly one frame. - virtual WebRtc_Word32 IncomingAudioData( - const WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 bufferLength) = 0; - - // Write one video frame, i.e. the bufferLength first bytes of videoBuffer, - // to file. - // Note: videoBuffer can contain encoded data. The codec used must be the - // same as what was specified by videoCodecInst for the last successfull - // StartRecordingVideoFile(..) call. The videoBuffer must contain exactly - // one video frame. - virtual WebRtc_Word32 IncomingAVIVideoData( - const WebRtc_Word8* videoBuffer, - const WebRtc_UWord32 bufferLength) = 0; - - // Open/creates file specified by fileName for writing (relative path is - // allowed). FileCallback::RecordNotification(..) will be called after - // notificationTimeMs of audio data has been recorded if - // notificationTimeMs is greater than zero. - // format specifies the type of file that should be created/opened. - // codecInst specifies the encoding of the audio data. maxSizeBytes - // specifies the number of bytes allowed to be written to file if it is - // greater than zero. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo is only supported for WAV files. - virtual WebRtc_Word32 StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs = 0, - const WebRtc_UWord32 maxSizeBytes = 0) = 0; - - // Open/create the file specified by fileName for writing audio/video data - // (relative path is allowed). format specifies the type of file fileName - // should be. codecInst specifies the encoding of the audio data. - // videoCodecInst specifies the encoding of the video data. Only video data - // will be recorded if videoOnly is true. - virtual WebRtc_Word32 StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - bool videoOnly = false) = 0; - - // Prepare for recording audio to stream. - // FileCallback::RecordNotification(..) will be called after - // notificationTimeMs of audio data has been recorded if - // notificationTimeMs is greater than zero. - // format specifies the type of file that stream should correspond to. - // codecInst specifies the encoding of the audio data. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo is only supported for WAV files. - virtual WebRtc_Word32 StartRecordingAudioStream( - OutStream& stream, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs = 0) = 0; - - // Stop recording to file or stream. - virtual WebRtc_Word32 StopRecording() = 0; - - // Return true if recording. - virtual bool IsRecording() = 0; - - // Set durationMs to the number of ms that has been recorded to file. - virtual WebRtc_Word32 RecordDurationMs(WebRtc_UWord32& durationMs) = 0; - - // Return true if recording or playing is stereo. - virtual bool IsStereo() = 0; - - // Register callback to receive media file related notifications. Disables - // callbacks if callback is NULL. - virtual WebRtc_Word32 SetModuleFileCallback(FileCallback* callback) = 0; - - // Set durationMs to the size of the file (in ms) specified by fileName. - // format specifies the type of file fileName refers to. freqInHz specifies - // the sampling frequency of the file. - virtual WebRtc_Word32 FileDurationMs( - const WebRtc_Word8* fileName, - WebRtc_UWord32& durationMs, - const FileFormats format, - const WebRtc_UWord32 freqInHz = 16000) = 0; - - // Update codecInst according to the current audio codec being used for - // reading or writing. - virtual WebRtc_Word32 codec_info(CodecInst& codecInst) const = 0; - - // Update videoCodecInst according to the current video codec being used for - // reading or writing. - virtual WebRtc_Word32 VideoCodecInst(VideoCodec& videoCodecInst) const = 0; - -protected: - MediaFile() {} - virtual ~MediaFile() {} -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_H_ diff --git a/modules/media_file/interface/media_file_defines.h b/modules/media_file/interface/media_file_defines.h deleted file mode 100644 index 38af562b9..000000000 --- a/modules/media_file/interface/media_file_defines.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_DEFINES_H_ -#define WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_DEFINES_H_ - -#include "engine_configurations.h" -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc { -// Callback class for the MediaFile class. -class FileCallback -{ -public: - virtual ~FileCallback(){} - - // This function is called by MediaFile when a file has been playing for - // durationMs ms. id is the identifier for the MediaFile instance calling - // the callback. - virtual void PlayNotification(const WebRtc_Word32 id, - const WebRtc_UWord32 durationMs) = 0; - - // This function is called by MediaFile when a file has been recording for - // durationMs ms. id is the identifier for the MediaFile instance calling - // the callback. - virtual void RecordNotification(const WebRtc_Word32 id, - const WebRtc_UWord32 durationMs) = 0; - - // This function is called by MediaFile when a file has been stopped - // playing. id is the identifier for the MediaFile instance calling the - // callback. - virtual void PlayFileEnded(const WebRtc_Word32 id) = 0; - - // This function is called by MediaFile when a file has been stopped - // recording. id is the identifier for the MediaFile instance calling the - // callback. - virtual void RecordFileEnded(const WebRtc_Word32 id) = 0; - -protected: - FileCallback() {} -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_MEDIA_FILE_INTERFACE_MEDIA_FILE_DEFINES_H_ diff --git a/modules/media_file/source/Android.mk b/modules/media_file/source/Android.mk deleted file mode 100644 index 3adcc5963..000000000 --- a/modules/media_file/source/Android.mk +++ /dev/null @@ -1,52 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_media_file -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := avi_file.cc \ - media_file_impl.cc \ - media_file_utility.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_MODULE_UTILITY_VIDEO' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../utility/interface \ - $(LOCAL_PATH)/../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/media_file/source/avi_file.cc b/modules/media_file/source/avi_file.cc deleted file mode 100644 index 7c4d07a5e..000000000 --- a/modules/media_file/source/avi_file.cc +++ /dev/null @@ -1,1700 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "avi_file.h" - -#include - -#ifdef _WIN32 -#include -#endif - -#include "critical_section_wrapper.h" -#include "file_wrapper.h" -#include "list_wrapper.h" -#include "trace.h" - -// http://msdn2.microsoft.com/en-us/library/ms779636.aspx -// A chunk has the following form: -// ckID ckSize ckData -// where ckID is a FOURCC that identifies the data contained in the -// chunk, ckData is a 4-byte value giving the size of the data in -// ckData, and ckData is zero or more bytes of data. The data is always -// padded to nearest WORD boundary. ckSize gives the size of the valid -// data in the chunk; it does not include the padding, the size of -// ckID, or the size of ckSize. -//http://msdn2.microsoft.com/en-us/library/ms779632.aspx -//NOTE: Workaround to make MPEG4 files play on WMP. MPEG files -// place the config parameters efter the BITMAPINFOHEADER and -// *NOT* in the 'strd'! -// http://msdn.microsoft.com/en-us/library/dd183375.aspx -// http://msdn.microsoft.com/en-us/library/dd183376.aspx - -namespace webrtc { -namespace { - static const WebRtc_UWord32 kAvifHasindex = 0x00000010; - static const WebRtc_UWord32 kAvifMustuseindex = 0x00000020; - static const WebRtc_UWord32 kAvifIsinterleaved = 0x00000100; - static const WebRtc_UWord32 kAvifTrustcktype = 0x00000800; - static const WebRtc_UWord32 kAvifWascapturefile = 0x00010000; -} - - -AviFile::AVIMAINHEADER::AVIMAINHEADER() - : fcc( 0), - cb( 0), - dwMicroSecPerFrame( 0), - dwMaxBytesPerSec( 0), - dwPaddingGranularity( 0), - dwFlags( 0), - dwTotalFrames( 0), - dwInitialFrames( 0), - dwStreams( 0), - dwSuggestedBufferSize(0), - dwWidth( 0), - dwHeight( 0) -{ - dwReserved[0] = 0; - dwReserved[1] = 0; - dwReserved[2] = 0; - dwReserved[3] = 0; -} - -AVISTREAMHEADER::AVISTREAMHEADER() - : fcc( 0), - cb( 0), - fccType( 0), - fccHandler( 0), - dwFlags( 0), - wPriority( 0), - wLanguage( 0), - dwInitialFrames( 0), - dwScale( 0), - dwRate( 0), - dwStart( 0), - dwLength( 0), - dwSuggestedBufferSize(0), - dwQuality( 0), - dwSampleSize( 0) -{ - rcFrame.left = 0; - rcFrame.top = 0; - rcFrame.right = 0; - rcFrame.bottom = 0; -} - -BITMAPINFOHEADER::BITMAPINFOHEADER() - : biSize( 0), - biWidth( 0), - biHeight( 0), - biPlanes( 0), - biBitCount( 0), - biCompression( 0), - biSizeImage( 0), - biXPelsPerMeter(0), - biYPelsPerMeter(0), - biClrUsed( 0), - biClrImportant( 0) -{ -} - -WAVEFORMATEX::WAVEFORMATEX() - : wFormatTag( 0), - nChannels( 0), - nSamplesPerSec( 0), - nAvgBytesPerSec(0), - nBlockAlign( 0), - wBitsPerSample( 0), - cbSize( 0) -{ -} - -AviFile::AVIINDEXENTRY::AVIINDEXENTRY(WebRtc_UWord32 inckid, - WebRtc_UWord32 indwFlags, - WebRtc_UWord32 indwChunkOffset, - WebRtc_UWord32 indwChunkLength) - : ckid(inckid), - dwFlags(indwFlags), - dwChunkOffset(indwChunkOffset), - dwChunkLength(indwChunkLength) -{ -} - -AviFile::AviFile() -{ - _crit = CriticalSectionWrapper::CreateCriticalSection(); - _indexList = new ListWrapper(); - - ResetMembers(); -} - -AviFile::~AviFile() -{ - Close(); - - delete _indexList; - delete[] _videoCodecConfigParams; - delete _crit; -} - -WebRtc_Word32 AviFile::Open(AVIStreamType streamType, const char* fileName, - bool loop) -{ - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, "OpenAVIFile(%s)", - fileName); - _crit->Enter(); - - if (_aviMode != NotSet) - { - _crit->Leave(); - return -1; - } - - _aviMode = Read; - - if (!fileName) - { - _crit->Leave(); - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "\tfileName not valid!"); - return -1; - } - -#ifdef _WIN32 - // fopen does not support wide characters on Windows, ergo _wfopen. - wchar_t wideFileName[FileWrapper::kMaxFileNameSize]; - wideFileName[0] = 0; - MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string - wideFileName, FileWrapper::kMaxFileNameSize); - - _aviFile = _wfopen(wideFileName, L"rb"); -#else - _aviFile = fopen(fileName, "rb"); -#endif - - if (!_aviFile) - { - _crit->Leave(); - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Could not open file!"); - return -1; - } - - // ReadRIFF verifies that the file is AVI and figures out the file length. - WebRtc_Word32 err = ReadRIFF(); - if (err) - { - if (_aviFile) - { - fclose(_aviFile); - _aviFile = NULL; - } - _crit->Leave(); - return -1; - } - - err = ReadHeaders(); - if (err) - { - if (_aviFile) - { - fclose(_aviFile); - _aviFile = NULL; - } - _crit->Leave(); - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Unsupported or corrupt AVI format"); - return -1; - } - - _dataStartByte = _bytesRead; - _reading = true; - _openedAs = streamType; - _loop = loop; - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::Close() -{ - _crit->Enter(); - switch (_aviMode) - { - case Read: - CloseRead(); - break; - case Write: - CloseWrite(); - break; - default: - break; - } - - if (_videoCodecConfigParams) - { - delete [] _videoCodecConfigParams; - _videoCodecConfigParams = 0; - } - ResetMembers(); - _crit->Leave(); - return 0; -} - -WebRtc_UWord32 AviFile::MakeFourCc(WebRtc_UWord8 ch0, WebRtc_UWord8 ch1, - WebRtc_UWord8 ch2, WebRtc_UWord8 ch3) -{ - return ((WebRtc_UWord32)(WebRtc_UWord8)(ch0) | - ((WebRtc_UWord32)(WebRtc_UWord8)(ch1) << 8) | - ((WebRtc_UWord32)(WebRtc_UWord8)(ch2) << 16) | - ((WebRtc_UWord32)(WebRtc_UWord8)(ch3) << 24 )); -} - -WebRtc_Word32 AviFile::GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader, - BITMAPINFOHEADER& bitmapInfo, - char* codecConfigParameters, - WebRtc_Word32& configLength) -{ - _crit->Enter(); - if (!_reading && !_created) - { - _crit->Leave(); - return -1; - } - - memcpy(&videoStreamHeader, &_videoStreamHeader, sizeof(_videoStreamHeader)); - memcpy(&bitmapInfo, &_videoFormatHeader, sizeof(_videoFormatHeader)); - - if (_videoConfigParameters && configLength <= _videoConfigLength) - { - memcpy(codecConfigParameters, _videoConfigParameters, - _videoConfigLength); - configLength = _videoConfigLength; - } - else - { - configLength = 0; - } - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::GetDuration(WebRtc_Word32& durationMs) -{ - _crit->Enter(); - if (_videoStreamHeader.dwRate==0 || _videoStreamHeader.dwScale==0) - { - _crit->Leave(); - return -1; - } - - durationMs = _videoStreamHeader.dwLength * 1000 / - (_videoStreamHeader.dwRate/_videoStreamHeader.dwScale); - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::GetAudioStreamInfo(WAVEFORMATEX& waveHeader) -{ - _crit->Enter(); - if (_aviMode != Read) - { - _crit->Leave(); - return -1; - } - if (!_reading && !_created) - { - _crit->Leave(); - return -1; - } - memcpy(&waveHeader, &_audioFormatHeader, sizeof(_audioFormatHeader)); - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::WriteAudio(const WebRtc_UWord8* data, - WebRtc_Word32 length) -{ - _crit->Enter(); - size_t newBytesWritten = _bytesWritten; - - if (_aviMode != Write) - { - _crit->Leave(); - return -1; - } - if (!_created) - { - _crit->Leave(); - return -1; - } - if (!_writeAudioStream) - { - _crit->Leave(); - return -1; - } - - // Start of chunk. - const WebRtc_UWord32 chunkOffset = ftell(_aviFile) - _moviListOffset; - _bytesWritten += PutLE32(_audioStreamDataChunkPrefix); - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t chunkSizeMark = _bytesWritten; - - _bytesWritten += PutBuffer(data, length); - - const long chunkSize = PutLE32LengthFromCurrent( - static_cast(chunkSizeMark)); - - // Make sure that the chunk is aligned on 2 bytes (= 1 sample). - if (chunkSize % 2) - { - _bytesWritten += PutByte(0); - } - // End of chunk - - // Save chunk information for use when closing file. - AddChunkToIndexList(_audioStreamDataChunkPrefix, 0, // No flags. - chunkOffset, chunkSize); - - ++_audioFrames; - newBytesWritten = _bytesWritten - newBytesWritten; - _crit->Leave(); - return static_cast(newBytesWritten); -} - -WebRtc_Word32 AviFile::WriteVideo(const WebRtc_UWord8* data, - WebRtc_Word32 length) -{ - _crit->Enter(); - size_t newBytesWritten = _bytesWritten; - if (_aviMode != Write) - { - _crit->Leave(); - return -1; - } - if (!_created) - { - _crit->Leave(); - return -1; - } - if (!_writeVideoStream) - { - _crit->Leave(); - return -1; - } - - // Start of chunk. - const WebRtc_UWord32 chunkOffset = ftell(_aviFile) - _moviListOffset; - _bytesWritten += PutLE32(_videoStreamDataChunkPrefix); - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t chunkSizeMark = _bytesWritten; - - _bytesWritten += PutBuffer(data, length); - - const long chunkSize = PutLE32LengthFromCurrent( - static_cast(chunkSizeMark)); - - // Make sure that the chunk is aligned on 2 bytes (= 1 sample). - if (chunkSize % 2) - { - //Pad one byte, to WORD align. - _bytesWritten += PutByte(0); - } - //End chunk! - AddChunkToIndexList(_videoStreamDataChunkPrefix, 0, // No flags. - chunkOffset, static_cast(chunkSize)); - - ++_videoFrames; - newBytesWritten = _bytesWritten - newBytesWritten; - _crit->Leave(); - return static_cast(newBytesWritten); -} - -WebRtc_Word32 AviFile::PrepareDataChunkHeaders() -{ - // 00 video stream, 01 audio stream. - // db uncompresses video, dc compressed video, wb WAV audio - if (_writeVideoStream) - { - if (strncmp((const char*) &_videoStreamHeader.fccHandler, "I420", 4) == - 0) - { - _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'b'); - } - else - { - _videoStreamDataChunkPrefix = MakeFourCc('0', '0', 'd', 'c'); - } - _audioStreamDataChunkPrefix = MakeFourCc('0', '1', 'w', 'b'); - } - else - { - _audioStreamDataChunkPrefix = MakeFourCc('0', '0', 'w', 'b'); - } - return 0; -} - -WebRtc_Word32 AviFile::ReadMoviSubChunk(WebRtc_UWord8* data, - WebRtc_Word32& length, - WebRtc_UWord32 tag1, - WebRtc_UWord32 tag2) -{ - if (!_reading) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, - "AviFile::ReadMoviSubChunk(): File not open!"); - length = 0; - return -1; - } - - WebRtc_UWord32 size; - bool isEOFReached = false; - // Try to read one data chunk header - while (true) - { - // TODO (hellner): what happens if an empty AVI file is opened with - // _loop set to true? Seems like this while-loop would never exit! - - // tag = db uncompresses video, dc compressed video or wb WAV audio. - WebRtc_UWord32 tag; - _bytesRead += GetLE32(tag); - _bytesRead += GetLE32(size); - - const WebRtc_Word32 eof = feof(_aviFile); - if (!eof) - { - if (tag == tag1) - { - // Supported tag found. - break; - } - else if ((tag == tag2) && (tag2 != 0)) - { - // Supported tag found. - break; - } - - // Jump to next chunk. The size is in bytes but chunks are aligned - // on 2 byte boundaries. - const WebRtc_UWord32 seekSize = (size % 2) ? size + 1 : size; - const WebRtc_Word32 err = fseek(_aviFile, seekSize, SEEK_CUR); - - if (err) - { - isEOFReached = true; - } - } - else - { - isEOFReached = true; - } - - if (isEOFReached) - { - clearerr(_aviFile); - - if (_loop) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, - "AviFile::ReadMoviSubChunk(): Reached end of AVI\ - data file, starting from the beginning."); - - fseek(_aviFile, static_cast(_dataStartByte), SEEK_SET); - - _bytesRead = _dataStartByte; - _framesRead = 0; - isEOFReached = false; - } - else - { - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, - "AviFile::ReadMoviSubChunk(): Reached end of AVI\ - file!"); - length = 0; - return -1; - } - } - _bytesRead += size; - } - - if (static_cast(size) > length) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, - "AviFile::ReadMoviSubChunk(): AVI read buffer too small!"); - - // Jump to next chunk. The size is in bytes but chunks are aligned - // on 2 byte boundaries. - const WebRtc_UWord32 seekSize = (size % 2) ? size + 1 : size; - fseek(_aviFile, seekSize, SEEK_CUR); - _bytesRead += seekSize; - length = 0; - return -1; - } - _bytesRead += GetBuffer(data, size); - - // The size is in bytes but chunks are aligned on 2 byte boundaries. - if (size % 2) - { - WebRtc_UWord8 dummy_byte; - _bytesRead += GetByte(dummy_byte); - } - length = size; - ++_framesRead; - return 0; -} - -WebRtc_Word32 AviFile::ReadAudio(WebRtc_UWord8* data, WebRtc_Word32& length) -{ - _crit->Enter(); - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "AviFile::ReadAudio()"); - - if (_aviMode != Read) - { - _crit->Leave(); - return -1; - } - if (_openedAs != AVI_AUDIO) - { - length = 0; - _crit->Leave(); - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "File not open as audio!"); - return -1; - } - - const WebRtc_Word32 ret = ReadMoviSubChunk( - data, - length, - StreamAndTwoCharCodeToTag(_audioStream.streamNumber, "wb")); - - _crit->Leave(); - return ret; -} - -WebRtc_Word32 AviFile::ReadVideo(WebRtc_UWord8* data, WebRtc_Word32& length) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "AviFile::ReadVideo()"); - - _crit->Enter(); - if (_aviMode != Read) - { - //Has to be Read! - _crit->Leave(); - return -1; - } - if (_openedAs != AVI_VIDEO) - { - length = 0; - _crit->Leave(); - WEBRTC_TRACE(kTraceDebug, kTraceVideo, -1, "File not open as video!"); - return -1; - } - - const WebRtc_Word32 ret = ReadMoviSubChunk( - data, - length, - StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "dc"), - StreamAndTwoCharCodeToTag(_videoStream.streamNumber, "db")); - _crit->Leave(); - return ret; -} - -WebRtc_Word32 AviFile::Create(const char* fileName) -{ - _crit->Enter(); - if (_aviMode != Write) - { - _crit->Leave(); - return -1; - } - - if (!_writeVideoStream && !_writeAudioStream) - { - _crit->Leave(); - return -1; - } - if (_created) - { - _crit->Leave(); - return -1; - } - -#ifdef _WIN32 - // fopen does not support wide characters on Windows, ergo _wfopen. - wchar_t wideFileName[FileWrapper::kMaxFileNameSize]; - wideFileName[0] = 0; - - MultiByteToWideChar(CP_UTF8,0,fileName, -1, // convert the whole string - wideFileName, FileWrapper::kMaxFileNameSize); - - _aviFile = _wfopen(wideFileName, L"w+b"); - if (!_aviFile) - { - _crit->Leave(); - return -1; - } -#else - _aviFile = fopen(fileName, "w+b"); - if (!_aviFile) - { - _crit->Leave(); - return -1; - } -#endif - - WriteRIFF(); - WriteHeaders(); - - _created = true; - - PrepareDataChunkHeaders(); - ClearIndexList(); - WriteMoviStart(); - _aviMode = Write; - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::CreateVideoStream( - const AVISTREAMHEADER& videoStreamHeader, - const BITMAPINFOHEADER& bitMapInfoHeader, - const WebRtc_UWord8* codecConfigParams, - WebRtc_Word32 codecConfigParamsLength) -{ - _crit->Enter(); - if (_aviMode == Read) - { - _crit->Leave(); - return -1; - } - - if (_created) - { - _crit->Leave(); - return -1; - } - - _aviMode = Write; - _writeVideoStream = true; - - _videoStreamHeader = videoStreamHeader; - _videoFormatHeader = bitMapInfoHeader; - - if (codecConfigParams && codecConfigParamsLength > 0) - { - if (_videoCodecConfigParams) - { - delete [] _videoCodecConfigParams; - _videoCodecConfigParams = 0; - } - - _videoCodecConfigParams = new WebRtc_UWord8[codecConfigParamsLength]; - _videoCodecConfigParamsLength = codecConfigParamsLength; - - memcpy(_videoCodecConfigParams, codecConfigParams, - _videoCodecConfigParamsLength); - } - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::CreateAudioStream( - const AVISTREAMHEADER& audioStreamHeader, - const WAVEFORMATEX& waveFormatHeader) -{ - _crit->Enter(); - - if (_aviMode == Read) - { - _crit->Leave(); - return -1; - } - - if (_created) - { - _crit->Leave(); - return -1; - } - - _aviMode = Write; - _writeAudioStream = true; - _audioStreamHeader = audioStreamHeader; - _audioFormatHeader = waveFormatHeader; - _crit->Leave(); - return 0; -} - -WebRtc_Word32 AviFile::WriteRIFF() -{ - const WebRtc_UWord32 riffTag = MakeFourCc('R', 'I', 'F', 'F'); - _bytesWritten += PutLE32(riffTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - _riffSizeMark = _bytesWritten; - - const WebRtc_UWord32 aviTag = MakeFourCc('A', 'V', 'I', ' '); - _bytesWritten += PutLE32(aviTag); - - return 0; -} - - -WebRtc_Word32 AviFile::WriteHeaders() -{ - // Main AVI header list. - const WebRtc_UWord32 listTag = MakeFourCc('L', 'I', 'S', 'T'); - _bytesWritten += PutLE32(listTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t listhdrlSizeMark = _bytesWritten; - - const WebRtc_UWord32 hdrlTag = MakeFourCc('h', 'd', 'r', 'l'); - _bytesWritten += PutLE32(hdrlTag); - - WriteAVIMainHeader(); - WriteAVIStreamHeaders(); - - const long hdrlLen = PutLE32LengthFromCurrent( - static_cast(listhdrlSizeMark)); - - // Junk chunk to align on 2048 boundry (CD-ROM sector boundary). - const WebRtc_UWord32 junkTag = MakeFourCc('J', 'U', 'N', 'K'); - _bytesWritten += PutLE32(junkTag); - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t junkSizeMark = _bytesWritten; - - const WebRtc_UWord32 junkBufferSize = - 0x800 // 2048 byte alignment - - 12 // RIFF SIZE 'AVI ' - - 8 // LIST SIZE - - hdrlLen // - - 8 // JUNK SIZE - - 12; // LIST SIZE 'MOVI' - - // TODO (hellner): why not just fseek here? - WebRtc_UWord8* junkBuffer = new WebRtc_UWord8[junkBufferSize]; - memset(junkBuffer, 0, junkBufferSize); - _bytesWritten += PutBuffer(junkBuffer, junkBufferSize); - delete [] junkBuffer; - - PutLE32LengthFromCurrent(static_cast(junkSizeMark)); - // End of JUNK chunk. - // End of main AVI header list. - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIMainHeader() -{ - const WebRtc_UWord32 avihTag = MakeFourCc('a', 'v', 'i', 'h'); - _bytesWritten += PutLE32(avihTag); - _bytesWritten += PutLE32(14 * sizeof(WebRtc_UWord32)); - - const WebRtc_UWord32 scale = _videoStreamHeader.dwScale ? - _videoStreamHeader.dwScale : 1; - const WebRtc_UWord32 microSecPerFrame = 1000000 / - (_videoStreamHeader.dwRate / scale); - _bytesWritten += PutLE32(microSecPerFrame); - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - - WebRtc_UWord32 numStreams = 0; - if (_writeVideoStream) - { - ++numStreams; - } - if (_writeAudioStream) - { - ++numStreams; - } - - if (numStreams == 1) - { - _bytesWritten += PutLE32( - kAvifTrustcktype - | kAvifHasindex - | kAvifWascapturefile); - } - else - { - _bytesWritten += PutLE32( - kAvifTrustcktype - | kAvifHasindex - | kAvifWascapturefile - | kAvifIsinterleaved); - } - - _totNumFramesMark = _bytesWritten; - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(numStreams); - - if (_writeVideoStream) - { - _bytesWritten += PutLE32( - _videoStreamHeader.dwSuggestedBufferSize); - _bytesWritten += PutLE32( - _videoStreamHeader.rcFrame.right-_videoStreamHeader.rcFrame.left); - _bytesWritten += PutLE32( - _videoStreamHeader.rcFrame.bottom-_videoStreamHeader.rcFrame.top); - } else { - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - } - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - _bytesWritten += PutLE32(0); - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIStreamHeaders() -{ - if (_writeVideoStream) - { - WriteAVIVideoStreamHeaders(); - } - if (_writeAudioStream) - { - WriteAVIAudioStreamHeaders(); - } - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIVideoStreamHeaders() -{ - const WebRtc_UWord32 listTag = MakeFourCc('L', 'I', 'S', 'T'); - _bytesWritten += PutLE32(listTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t liststrlSizeMark = _bytesWritten; - - const WebRtc_UWord32 hdrlTag = MakeFourCc('s', 't', 'r', 'l'); - _bytesWritten += PutLE32(hdrlTag); - - WriteAVIVideoStreamHeaderChunks(); - - PutLE32LengthFromCurrent(static_cast(liststrlSizeMark)); - - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIVideoStreamHeaderChunks() -{ - // Start of strh - const WebRtc_UWord32 strhTag = MakeFourCc('s', 't', 'r', 'h'); - _bytesWritten += PutLE32(strhTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strhSizeMark = _bytesWritten; - - _bytesWritten += PutLE32(_videoStreamHeader.fccType); - _bytesWritten += PutLE32(_videoStreamHeader.fccHandler); - _bytesWritten += PutLE32(_videoStreamHeader.dwFlags); - _bytesWritten += PutLE16(_videoStreamHeader.wPriority); - _bytesWritten += PutLE16(_videoStreamHeader.wLanguage); - _bytesWritten += PutLE32(_videoStreamHeader.dwInitialFrames); - _bytesWritten += PutLE32(_videoStreamHeader.dwScale); - _bytesWritten += PutLE32(_videoStreamHeader.dwRate); - _bytesWritten += PutLE32(_videoStreamHeader.dwStart); - - _videoStreamLengthMark = _bytesWritten; - _bytesWritten += PutLE32(_videoStreamHeader.dwLength); - - _bytesWritten += PutLE32(_videoStreamHeader.dwSuggestedBufferSize); - _bytesWritten += PutLE32(_videoStreamHeader.dwQuality); - _bytesWritten += PutLE32(_videoStreamHeader.dwSampleSize); - _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.left); - _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.top); - _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.right); - _bytesWritten += PutLE16(_videoStreamHeader.rcFrame.bottom); - - PutLE32LengthFromCurrent(static_cast(strhSizeMark)); - // End of strh - - // Start of strf - const WebRtc_UWord32 strfTag = MakeFourCc('s', 't', 'r', 'f'); - _bytesWritten += PutLE32(strfTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strfSizeMark = _bytesWritten; - - _bytesWritten += PutLE32(_videoFormatHeader.biSize); - _bytesWritten += PutLE32(_videoFormatHeader.biWidth); - _bytesWritten += PutLE32(_videoFormatHeader.biHeight); - _bytesWritten += PutLE16(_videoFormatHeader.biPlanes); - _bytesWritten += PutLE16(_videoFormatHeader.biBitCount); - _bytesWritten += PutLE32(_videoFormatHeader.biCompression); - _bytesWritten += PutLE32(_videoFormatHeader.biSizeImage); - _bytesWritten += PutLE32(_videoFormatHeader.biXPelsPerMeter); - _bytesWritten += PutLE32(_videoFormatHeader.biYPelsPerMeter); - _bytesWritten += PutLE32(_videoFormatHeader.biClrUsed); - _bytesWritten += PutLE32(_videoFormatHeader.biClrImportant); - - const bool isMpegFile = _videoStreamHeader.fccHandler == - AviFile::MakeFourCc('M','4','S','2'); - if (isMpegFile) - { - if (_videoCodecConfigParams && _videoCodecConfigParamsLength > 0) - { - _bytesWritten += PutBuffer(_videoCodecConfigParams, - _videoCodecConfigParamsLength); - } - } - - PutLE32LengthFromCurrent(static_cast(strfSizeMark)); - // End of strf - - if ( _videoCodecConfigParams - && (_videoCodecConfigParamsLength > 0) - && !isMpegFile) - { - // Write strd, unless it's an MPEG file - const WebRtc_UWord32 strdTag = MakeFourCc('s', 't', 'r', 'd'); - _bytesWritten += PutLE32(strdTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strdSizeMark = _bytesWritten; - - _bytesWritten += PutBuffer(_videoCodecConfigParams, - _videoCodecConfigParamsLength); - - PutLE32LengthFromCurrent(static_cast(strdSizeMark)); - // End of strd - } - - // Start of strn - const WebRtc_UWord32 strnTag = MakeFourCc('s', 't', 'r', 'n'); - _bytesWritten += PutLE32(strnTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strnSizeMark = _bytesWritten; - - _bytesWritten += PutBufferZ("WebRtc.avi "); - - PutLE32LengthFromCurrent(static_cast(strnSizeMark)); - // End of strd - - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIAudioStreamHeaders() -{ - // Start of LIST - WebRtc_UWord32 listTag = MakeFourCc('L', 'I', 'S', 'T'); - _bytesWritten += PutLE32(listTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t liststrlSizeMark = _bytesWritten; - - WebRtc_UWord32 hdrlTag = MakeFourCc('s', 't', 'r', 'l'); - _bytesWritten += PutLE32(hdrlTag); - - WriteAVIAudioStreamHeaderChunks(); - - PutLE32LengthFromCurrent(static_cast(liststrlSizeMark)); - //End of LIST - return 0; -} - -WebRtc_Word32 AviFile::WriteAVIAudioStreamHeaderChunks() -{ - // Start of strh - const WebRtc_UWord32 strhTag = MakeFourCc('s', 't', 'r', 'h'); - _bytesWritten += PutLE32(strhTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strhSizeMark = _bytesWritten; - - _bytesWritten += PutLE32(_audioStreamHeader.fccType); - _bytesWritten += PutLE32(_audioStreamHeader.fccHandler); - _bytesWritten += PutLE32(_audioStreamHeader.dwFlags); - _bytesWritten += PutLE16(_audioStreamHeader.wPriority); - _bytesWritten += PutLE16(_audioStreamHeader.wLanguage); - _bytesWritten += PutLE32(_audioStreamHeader.dwInitialFrames); - _bytesWritten += PutLE32(_audioStreamHeader.dwScale); - _bytesWritten += PutLE32(_audioStreamHeader.dwRate); - _bytesWritten += PutLE32(_audioStreamHeader.dwStart); - - _audioStreamLengthMark = _bytesWritten; - _bytesWritten += PutLE32(_audioStreamHeader.dwLength); - - _bytesWritten += PutLE32(_audioStreamHeader.dwSuggestedBufferSize); - _bytesWritten += PutLE32(_audioStreamHeader.dwQuality); - _bytesWritten += PutLE32(_audioStreamHeader.dwSampleSize); - _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.left); - _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.top); - _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.right); - _bytesWritten += PutLE16(_audioStreamHeader.rcFrame.bottom); - - PutLE32LengthFromCurrent(static_cast(strhSizeMark)); - // End of strh - - // Start of strf - const WebRtc_UWord32 strfTag = MakeFourCc('s', 't', 'r', 'f'); - _bytesWritten += PutLE32(strfTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strfSizeMark = _bytesWritten; - - _bytesWritten += PutLE16(_audioFormatHeader.wFormatTag); - _bytesWritten += PutLE16(_audioFormatHeader.nChannels); - _bytesWritten += PutLE32(_audioFormatHeader.nSamplesPerSec); - _bytesWritten += PutLE32(_audioFormatHeader.nAvgBytesPerSec); - _bytesWritten += PutLE16(_audioFormatHeader.nBlockAlign); - _bytesWritten += PutLE16(_audioFormatHeader.wBitsPerSample); - _bytesWritten += PutLE16(_audioFormatHeader.cbSize); - - PutLE32LengthFromCurrent(static_cast(strfSizeMark)); - // End end of strf. - - // Audio doesn't have strd. - - // Start of strn - const WebRtc_UWord32 strnTag = MakeFourCc('s', 't', 'r', 'n'); - _bytesWritten += PutLE32(strnTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t strnSizeMark = _bytesWritten; - - _bytesWritten += PutBufferZ("WebRtc.avi "); - - PutLE32LengthFromCurrent(static_cast(strnSizeMark)); - // End of strd. - - return 0; -} - -WebRtc_Word32 AviFile::WriteMoviStart() -{ - // Create template movi list. Fill out size when known (i.e. when closing - // file). - const WebRtc_UWord32 listTag = MakeFourCc('L', 'I', 'S', 'T'); - _bytesWritten += PutLE32(listTag); - - _bytesWritten += PutLE32(0); //Size! Change later! - _moviSizeMark = _bytesWritten; - _moviListOffset = ftell(_aviFile); - - const WebRtc_UWord32 moviTag = MakeFourCc('m', 'o', 'v', 'i'); - _bytesWritten += PutLE32(moviTag); - - return 0; -} - -size_t AviFile::PutByte(WebRtc_UWord8 byte) -{ - return fwrite(&byte, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord8), - _aviFile); -} - -size_t AviFile::PutLE16(WebRtc_UWord16 word) -{ - return fwrite(&word, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord16), - _aviFile); -} - -size_t AviFile::PutLE32(WebRtc_UWord32 word) -{ - return fwrite(&word, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord32), - _aviFile); -} - -size_t AviFile::PutBuffer(const WebRtc_UWord8* str, size_t size) -{ - return fwrite(str, sizeof(WebRtc_UWord8), size, - _aviFile); -} - -size_t AviFile::PutBufferZ(const char* str) -{ - // Include NULL charachter, hence the + 1 - return PutBuffer(reinterpret_cast(str), - strlen(str) + 1); -} - -long AviFile::PutLE32LengthFromCurrent(long startPos) -{ - const long endPos = ftell(_aviFile); - WebRtc_Word32 error = fseek(_aviFile, startPos - 4, SEEK_SET); - const long len = endPos - startPos; - PutLE32(len); - error = fseek(_aviFile, endPos, SEEK_SET); - return len; -} - -void AviFile::PutLE32AtPos(long pos, WebRtc_UWord32 word) -{ - const long currPos = ftell(_aviFile); - WebRtc_Word32 error = fseek(_aviFile, pos, SEEK_SET); - PutLE32(word); - error = fseek(_aviFile, currPos, SEEK_SET); -} - -void AviFile::CloseRead() -{ - if (_aviFile) - { - fclose(_aviFile); - _aviFile = NULL; - } -} - -void AviFile::CloseWrite() -{ - if (_created) - { - // Update everything that isn't known until the file is closed. The - // marks indicate where in the headers this update should be. - PutLE32LengthFromCurrent(static_cast(_moviSizeMark)); - - PutLE32AtPos(static_cast(_totNumFramesMark), _videoFrames); - - if (_writeVideoStream) - { - PutLE32AtPos(static_cast(_videoStreamLengthMark), - _videoFrames); - } - - if (_writeAudioStream) - { - PutLE32AtPos(static_cast(_audioStreamLengthMark), - _audioFrames); - } - - WriteIndex(); - PutLE32LengthFromCurrent(static_cast(_riffSizeMark)); - ClearIndexList(); - - if (_aviFile) - { - fclose(_aviFile); - _aviFile = NULL; - } - } -} - -void AviFile::ResetMembers() -{ - _aviFile = NULL; - - memset(&_aviHeader, 0, sizeof(AVIMAINHEADER)); - memset(&_videoStreamHeader, 0, sizeof(AVISTREAMHEADER)); - memset(&_audioStreamHeader, 0, sizeof(AVISTREAMHEADER)); - memset(&_videoFormatHeader, 0, sizeof(BITMAPINFOHEADER)); - memset(&_audioFormatHeader, 0, sizeof(WAVEFORMATEX)); - memset(_videoConfigParameters, 0, CODEC_CONFIG_LENGTH); - memset(_videoStreamName, 0, STREAM_NAME_LENGTH); - memset(_audioStreamName, 0, STREAM_NAME_LENGTH); - memset(&_videoStream, 0, sizeof(AVIStream)); - memset(&_audioStream, 0, sizeof(AVIStream)); - - _nrStreams = 0; - _aviLength = 0; - _dataLength = 0; - _bytesRead = 0; - _dataStartByte = 0; - _framesRead = 0; - _videoFrames = 0; - _audioFrames = 0; - - _reading = false; - _openedAs = AVI_AUDIO; - _loop = false; - _writing = false; - - _bytesWritten = 0; - - _riffSizeMark = 0; - _moviSizeMark = 0; - _totNumFramesMark = 0; - _videoStreamLengthMark = 0; - _audioStreamLengthMark = 0; - - _writeAudioStream = false; - _writeVideoStream = false; - - _aviMode = NotSet; - _videoCodecConfigParams = 0; - _videoCodecConfigParamsLength = 0; - - _videoStreamDataChunkPrefix = 0; - _audioStreamDataChunkPrefix = 0; - - _created = false; - - _moviListOffset = 0; - - _videoConfigLength = 0; -} - -size_t AviFile::GetByte(WebRtc_UWord8& word) -{ - return fread(&word, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord8), _aviFile); -} - -size_t AviFile::GetLE16(WebRtc_UWord16& word) -{ - return fread(&word, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord16), - _aviFile); -} - -size_t AviFile::GetLE32(WebRtc_UWord32& word) -{ - return fread(&word, sizeof(WebRtc_UWord8), sizeof(WebRtc_UWord32), - _aviFile); -} - -size_t AviFile::GetBuffer(WebRtc_UWord8* str, size_t size) -{ - return fread(str, sizeof(WebRtc_UWord8), size, _aviFile); -} - -WebRtc_Word32 AviFile::ReadRIFF() -{ - WebRtc_UWord32 tag; - _bytesRead = GetLE32(tag); - if (tag != MakeFourCc('R', 'I', 'F', 'F')) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not a RIFF file!"); - return -1; - } - - WebRtc_UWord32 size; - _bytesRead += GetLE32(size); - _aviLength = size; - - _bytesRead += GetLE32(tag); - if (tag != MakeFourCc('A', 'V', 'I', ' ')) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not an AVI file!"); - return -1; - } - - return 0; -} - -WebRtc_Word32 AviFile::ReadHeaders() -{ - WebRtc_UWord32 tag; - _bytesRead += GetLE32(tag); - WebRtc_UWord32 size; - _bytesRead += GetLE32(size); - - if (tag != MakeFourCc('L', 'I', 'S', 'T')) - { - return -1; - } - - WebRtc_UWord32 listTag; - _bytesRead += GetLE32(listTag); - if (listTag != MakeFourCc('h', 'd', 'r', 'l')) - { - return -1; - } - - WebRtc_Word32 err = ReadAVIMainHeader(); - if (err) - { - return -1; - } - - return 0; -} - -WebRtc_Word32 AviFile::ReadAVIMainHeader() -{ - _bytesRead += GetLE32(_aviHeader.fcc); - _bytesRead += GetLE32(_aviHeader.cb); - _bytesRead += GetLE32(_aviHeader.dwMicroSecPerFrame); - _bytesRead += GetLE32(_aviHeader.dwMaxBytesPerSec); - _bytesRead += GetLE32(_aviHeader.dwPaddingGranularity); - _bytesRead += GetLE32(_aviHeader.dwFlags); - _bytesRead += GetLE32(_aviHeader.dwTotalFrames); - _bytesRead += GetLE32(_aviHeader.dwInitialFrames); - _bytesRead += GetLE32(_aviHeader.dwStreams); - _bytesRead += GetLE32(_aviHeader.dwSuggestedBufferSize); - _bytesRead += GetLE32(_aviHeader.dwWidth); - _bytesRead += GetLE32(_aviHeader.dwHeight); - _bytesRead += GetLE32(_aviHeader.dwReserved[0]); - _bytesRead += GetLE32(_aviHeader.dwReserved[1]); - _bytesRead += GetLE32(_aviHeader.dwReserved[2]); - _bytesRead += GetLE32(_aviHeader.dwReserved[3]); - - if (_aviHeader.fcc != MakeFourCc('a', 'v', 'i', 'h')) - { - return -1; - } - - if (_aviHeader.dwFlags & kAvifMustuseindex) - { - return -1; - } - - bool readVideoStreamHeader = false; - bool readAudioStreamHeader = false; - unsigned int streamsRead = 0; - while (_aviHeader.dwStreams > streamsRead) - { - WebRtc_UWord32 strltag; - _bytesRead += GetLE32(strltag); - WebRtc_UWord32 strlsize; - _bytesRead += GetLE32(strlsize); - const long endSeekPos = ftell(_aviFile) + - static_cast(strlsize); - - if (strltag != MakeFourCc('L', 'I', 'S', 'T')) - { - return -1; - } - - WebRtc_UWord32 listTag; - _bytesRead += GetLE32(listTag); - if (listTag != MakeFourCc('s', 't', 'r', 'l')) - { - return -1; - } - - WebRtc_UWord32 chunktag; - _bytesRead += GetLE32(chunktag); - WebRtc_UWord32 chunksize; - _bytesRead += GetLE32(chunksize); - - if (chunktag != MakeFourCc('s', 't', 'r', 'h')) - { - return -1; - } - - AVISTREAMHEADER tmpStreamHeader; - tmpStreamHeader.fcc = chunktag; - tmpStreamHeader.cb = chunksize; - - _bytesRead += GetLE32(tmpStreamHeader.fccType); - _bytesRead += GetLE32(tmpStreamHeader.fccHandler); - _bytesRead += GetLE32(tmpStreamHeader.dwFlags); - _bytesRead += GetLE16(tmpStreamHeader.wPriority); - _bytesRead += GetLE16(tmpStreamHeader.wLanguage); - _bytesRead += GetLE32(tmpStreamHeader.dwInitialFrames); - _bytesRead += GetLE32(tmpStreamHeader.dwScale); - _bytesRead += GetLE32(tmpStreamHeader.dwRate); - _bytesRead += GetLE32(tmpStreamHeader.dwStart); - _bytesRead += GetLE32(tmpStreamHeader.dwLength); - _bytesRead += GetLE32(tmpStreamHeader.dwSuggestedBufferSize); - _bytesRead += GetLE32(tmpStreamHeader.dwQuality); - _bytesRead += GetLE32(tmpStreamHeader.dwSampleSize); - - WebRtc_UWord16 left; - _bytesRead += GetLE16(left); - tmpStreamHeader.rcFrame.left = left; - WebRtc_UWord16 top; - _bytesRead += GetLE16(top); - tmpStreamHeader.rcFrame.top = top; - WebRtc_UWord16 right; - _bytesRead += GetLE16(right); - tmpStreamHeader.rcFrame.right = right; - WebRtc_UWord16 bottom; - _bytesRead += GetLE16(bottom); - tmpStreamHeader.rcFrame.bottom = bottom; - - if (!readVideoStreamHeader - && (tmpStreamHeader.fccType == MakeFourCc('v', 'i', 'd', 's'))) - { - _videoStreamHeader = tmpStreamHeader; //Bitwise copy is OK! - const WebRtc_Word32 err = ReadAVIVideoStreamHeader(endSeekPos); - if (err) - { - return -1; - } - // Make sure there actually is video data in the file... - if (_videoStreamHeader.dwLength == 0) - { - return -1; - } - readVideoStreamHeader = true; - } else if(!readAudioStreamHeader && - (tmpStreamHeader.fccType == MakeFourCc('a', 'u', 'd', 's'))) { - _audioStreamHeader = tmpStreamHeader; - const WebRtc_Word32 err = ReadAVIAudioStreamHeader(endSeekPos); - if (err) - { - return -1; - } - readAudioStreamHeader = true; - } - else - { - fseek(_aviFile, endSeekPos, SEEK_SET); - _bytesRead += endSeekPos; - } - - ++streamsRead; - } - - if (!readVideoStreamHeader && !readAudioStreamHeader) - { - return -1; - } - - WebRtc_UWord32 tag; - _bytesRead += GetLE32(tag); - WebRtc_UWord32 size; - _bytesRead += GetLE32(size); - - if (tag == MakeFourCc('J', 'U', 'N', 'K')) - { - fseek(_aviFile, size, SEEK_CUR); - _bytesRead += size; - _bytesRead += GetLE32(tag); - _bytesRead += GetLE32(size); - } - if (tag != MakeFourCc('L', 'I', 'S', 'T')) - { - return -1; - } - WebRtc_UWord32 listTag; - _bytesRead += GetLE32(listTag); - if (listTag != MakeFourCc('m', 'o', 'v', 'i')) - { - return -1; - } - _dataLength = size; - return 0; -} - -WebRtc_Word32 AviFile::ReadAVIVideoStreamHeader(WebRtc_Word32 endpos) -{ - WebRtc_UWord32 chunktag; - _bytesRead += GetLE32(chunktag); - WebRtc_UWord32 chunksize; - _bytesRead += GetLE32(chunksize); - - if (chunktag != MakeFourCc('s', 't', 'r', 'f')) - { - return -1; - } - - _bytesRead += GetLE32(_videoFormatHeader.biSize); - _bytesRead += GetLE32(_videoFormatHeader.biWidth); - _bytesRead += GetLE32(_videoFormatHeader.biHeight); - _bytesRead += GetLE16(_videoFormatHeader.biPlanes); - _bytesRead += GetLE16(_videoFormatHeader.biBitCount); - _bytesRead += GetLE32(_videoFormatHeader.biCompression); - _bytesRead += GetLE32(_videoFormatHeader.biSizeImage); - _bytesRead += GetLE32(_videoFormatHeader.biXPelsPerMeter); - _bytesRead += GetLE32(_videoFormatHeader.biYPelsPerMeter); - _bytesRead += GetLE32(_videoFormatHeader.biClrUsed); - _bytesRead += GetLE32(_videoFormatHeader.biClrImportant); - - if (chunksize > _videoFormatHeader.biSize) - { - const WebRtc_Word32 size = chunksize - _videoFormatHeader.biSize; - const WebRtc_Word32 readSize = (size > CODEC_CONFIG_LENGTH) ? - CODEC_CONFIG_LENGTH : size; - _bytesRead += GetBuffer( - reinterpret_cast(_videoConfigParameters), readSize); - _videoConfigLength = readSize; - WebRtc_Word32 skipSize = chunksize - _videoFormatHeader.biSize - - readSize; - if (skipSize > 0) - { - fseek(_aviFile, skipSize, SEEK_CUR); - _bytesRead += skipSize; - } - } - - while (static_cast(_bytesRead) < endpos) - { - WebRtc_UWord32 chunktag; - _bytesRead += GetLE32(chunktag); - WebRtc_UWord32 chunksize; - _bytesRead += GetLE32(chunksize); - - if (chunktag == MakeFourCc('s', 't', 'r', 'n')) - { - WebRtc_Word32 size = (chunksize > STREAM_NAME_LENGTH) ? - STREAM_NAME_LENGTH : chunksize; - _bytesRead += GetBuffer( - reinterpret_cast(_videoStreamName), size); - } - else if (chunktag == MakeFourCc('s', 't', 'r', 'd')) - { - WebRtc_Word32 size = (chunksize > CODEC_CONFIG_LENGTH) ? - CODEC_CONFIG_LENGTH : chunksize; - _bytesRead += GetBuffer( - reinterpret_cast(_videoConfigParameters), size); - _videoConfigLength = size; - } - else - { - fseek(_aviFile, chunksize, SEEK_CUR); - _bytesRead += chunksize; - } - - if (feof(_aviFile)) - { - return -1; - } - } - _videoStream.streamType = AviFile::AVI_VIDEO; - _videoStream.streamNumber = _nrStreams++; - - return 0; -} - -WebRtc_Word32 AviFile::ReadAVIAudioStreamHeader(WebRtc_Word32 endpos) -{ - WebRtc_UWord32 chunktag; - _bytesRead += GetLE32(chunktag); - WebRtc_UWord32 chunksize; - _bytesRead += GetLE32(chunksize); - - if (chunktag != MakeFourCc('s', 't', 'r', 'f')) - { - return -1; - } - - const size_t startRead = _bytesRead; - _bytesRead += GetLE16(_audioFormatHeader.wFormatTag); - _bytesRead += GetLE16(_audioFormatHeader.nChannels); - _bytesRead += GetLE32(_audioFormatHeader.nSamplesPerSec); - _bytesRead += GetLE32(_audioFormatHeader.nAvgBytesPerSec); - _bytesRead += GetLE16(_audioFormatHeader.nBlockAlign); - _bytesRead += GetLE16(_audioFormatHeader.wBitsPerSample); - _bytesRead += GetLE16(_audioFormatHeader.cbSize); - - const WebRtc_Word32 diffRead = chunksize - (_bytesRead - startRead); - if (diffRead > 0) - { - size_t size = (diffRead > CODEC_CONFIG_LENGTH) ? - CODEC_CONFIG_LENGTH : diffRead; - _bytesRead += GetBuffer( - reinterpret_cast(_audioConfigParameters), size); - } - - while (static_cast(_bytesRead) < endpos) - { - WebRtc_UWord32 chunktag; - _bytesRead += GetLE32(chunktag); - WebRtc_UWord32 chunksize; - _bytesRead += GetLE32(chunksize); - - if (chunktag == MakeFourCc('s', 't', 'r', 'n')) - { - WebRtc_Word32 size = (chunksize > STREAM_NAME_LENGTH) ? - STREAM_NAME_LENGTH : chunksize; - _bytesRead += GetBuffer( - reinterpret_cast(_audioStreamName), size); - } - else if (chunktag == MakeFourCc('s', 't', 'r', 'd')) - { - WebRtc_Word32 size = (chunksize > CODEC_CONFIG_LENGTH) ? - CODEC_CONFIG_LENGTH : chunksize; - _bytesRead += GetBuffer( - reinterpret_cast(_audioConfigParameters), size); - } - else - { - fseek(_aviFile, chunksize, SEEK_CUR); - _bytesRead += chunksize; - } - - if (feof(_aviFile)) - { - return -1; - } - } - _audioStream.streamType = AviFile::AVI_AUDIO; - _audioStream.streamNumber = _nrStreams++; - return 0; -} - -WebRtc_UWord32 AviFile::StreamAndTwoCharCodeToTag(WebRtc_Word32 streamNum, - const char* twoCharCode) -{ - WebRtc_UWord8 a = '0'; - WebRtc_UWord8 b; - switch (streamNum) - { - case 1: - b = '1'; - break; - case 2: - b = '2'; - break; - default: - b = '0'; - } - return MakeFourCc(a, b, twoCharCode[0], twoCharCode[1]); -} - -void AviFile::ClearIndexList() -{ - while (!_indexList->Empty()) - { - ListItem* listItem = _indexList->First(); - if (listItem == 0) - { - break; - } - - AVIINDEXENTRY* item = static_cast(listItem->GetItem()); - if (item != NULL) - { - delete item; - } - _indexList->PopFront(); - } -} - -void AviFile::AddChunkToIndexList(WebRtc_UWord32 inChunkId, - WebRtc_UWord32 inFlags, - WebRtc_UWord32 inOffset, - WebRtc_UWord32 inSize) -{ - _indexList->PushBack(new AVIINDEXENTRY(inChunkId, inFlags, inOffset, - inSize)); -} - -void AviFile::WriteIndex() -{ - const WebRtc_UWord32 idxTag = MakeFourCc('i', 'd', 'x', '1'); - _bytesWritten += PutLE32(idxTag); - - // Size is unknown at this point. Update later. - _bytesWritten += PutLE32(0); - const size_t idxChunkSize = _bytesWritten; - - for (ListItem* listItem = _indexList->First(); - listItem != NULL; - listItem = _indexList->Next(listItem)) - { - const AVIINDEXENTRY* item = - static_cast(listItem->GetItem()); - if (item != NULL) - { - _bytesWritten += PutLE32(item->ckid); - _bytesWritten += PutLE32(item->dwFlags); - _bytesWritten += PutLE32(item->dwChunkOffset); - _bytesWritten += PutLE32(item->dwChunkLength); - } - } - PutLE32LengthFromCurrent(static_cast(idxChunkSize)); -} -} // namespace webrtc diff --git a/modules/media_file/source/avi_file.h b/modules/media_file/source/avi_file.h deleted file mode 100644 index 6a30aba66..000000000 --- a/modules/media_file/source/avi_file.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Class for reading (x)or writing to an AVI file. -// Note: the class cannot be used for reading and writing at the same time. -#ifndef WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_ -#define WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_ - -#include - -#include "typedefs.h" - -namespace webrtc { -class CriticalSectionWrapper; -class ListWrapper; - -struct AVISTREAMHEADER -{ - AVISTREAMHEADER(); - WebRtc_UWord32 fcc; - WebRtc_UWord32 cb; - WebRtc_UWord32 fccType; - WebRtc_UWord32 fccHandler; - WebRtc_UWord32 dwFlags; - WebRtc_UWord16 wPriority; - WebRtc_UWord16 wLanguage; - WebRtc_UWord32 dwInitialFrames; - WebRtc_UWord32 dwScale; - WebRtc_UWord32 dwRate; - WebRtc_UWord32 dwStart; - WebRtc_UWord32 dwLength; - WebRtc_UWord32 dwSuggestedBufferSize; - WebRtc_UWord32 dwQuality; - WebRtc_UWord32 dwSampleSize; - struct - { - WebRtc_Word16 left; - WebRtc_Word16 top; - WebRtc_Word16 right; - WebRtc_Word16 bottom; - } rcFrame; -}; - -struct BITMAPINFOHEADER -{ - BITMAPINFOHEADER(); - WebRtc_UWord32 biSize; - WebRtc_UWord32 biWidth; - WebRtc_UWord32 biHeight; - WebRtc_UWord16 biPlanes; - WebRtc_UWord16 biBitCount; - WebRtc_UWord32 biCompression; - WebRtc_UWord32 biSizeImage; - WebRtc_UWord32 biXPelsPerMeter; - WebRtc_UWord32 biYPelsPerMeter; - WebRtc_UWord32 biClrUsed; - WebRtc_UWord32 biClrImportant; -}; - -struct WAVEFORMATEX -{ - WAVEFORMATEX(); - WebRtc_UWord16 wFormatTag; - WebRtc_UWord16 nChannels; - WebRtc_UWord32 nSamplesPerSec; - WebRtc_UWord32 nAvgBytesPerSec; - WebRtc_UWord16 nBlockAlign; - WebRtc_UWord16 wBitsPerSample; - WebRtc_UWord16 cbSize; -}; - -class AviFile -{ -public: - enum AVIStreamType - { - AVI_AUDIO = 0, - AVI_VIDEO = 1 - }; - - static const WebRtc_UWord32 CODEC_CONFIG_LENGTH = 64; - static const WebRtc_UWord32 STREAM_NAME_LENGTH = 32; - - AviFile(); - ~AviFile(); - - WebRtc_Word32 Open(AVIStreamType streamType, const char* fileName, - bool loop = false); - - WebRtc_Word32 CreateVideoStream(const AVISTREAMHEADER& videoStreamHeader, - const BITMAPINFOHEADER& bitMapInfoHeader, - const WebRtc_UWord8* codecConfigParams, - WebRtc_Word32 codecConfigParamsLength); - - WebRtc_Word32 CreateAudioStream(const AVISTREAMHEADER& audioStreamHeader, - const WAVEFORMATEX& waveFormatHeader); - WebRtc_Word32 Create(const WebRtc_Word8* fileName); - - WebRtc_Word32 WriteAudio(const WebRtc_UWord8* data, WebRtc_Word32 length); - WebRtc_Word32 WriteVideo(const WebRtc_UWord8* data, WebRtc_Word32 length); - - WebRtc_Word32 GetVideoStreamInfo(AVISTREAMHEADER& videoStreamHeader, - BITMAPINFOHEADER& bitmapInfo, - WebRtc_Word8* codecConfigParameters, - WebRtc_Word32& configLength); - - WebRtc_Word32 GetDuration(WebRtc_Word32& durationMs); - - WebRtc_Word32 GetAudioStreamInfo(WAVEFORMATEX& waveHeader); - - WebRtc_Word32 ReadAudio(WebRtc_UWord8* data, WebRtc_Word32& length); - WebRtc_Word32 ReadVideo(WebRtc_UWord8* data, WebRtc_Word32& length); - - WebRtc_Word32 Close(); - - static WebRtc_UWord32 MakeFourCc(WebRtc_UWord8 ch0, WebRtc_UWord8 ch1, - WebRtc_UWord8 ch2, WebRtc_UWord8 ch3); - -private: - enum AVIFileMode - { - NotSet, - Read, - Write - }; - - struct AVIINDEXENTRY - { - AVIINDEXENTRY(WebRtc_UWord32 inckid, WebRtc_UWord32 indwFlags, - WebRtc_UWord32 indwChunkOffset, - WebRtc_UWord32 indwChunkLength); - WebRtc_UWord32 ckid; - WebRtc_UWord32 dwFlags; - WebRtc_UWord32 dwChunkOffset; - WebRtc_UWord32 dwChunkLength; - }; - - WebRtc_Word32 PrepareDataChunkHeaders(); - - WebRtc_Word32 ReadMoviSubChunk(WebRtc_UWord8* data, WebRtc_Word32& length, - WebRtc_UWord32 tag1, - WebRtc_UWord32 tag2 = 0); - - WebRtc_Word32 WriteRIFF(); - WebRtc_Word32 WriteHeaders(); - WebRtc_Word32 WriteAVIMainHeader(); - WebRtc_Word32 WriteAVIStreamHeaders(); - WebRtc_Word32 WriteAVIVideoStreamHeaders(); - WebRtc_Word32 WriteAVIVideoStreamHeaderChunks(); - WebRtc_Word32 WriteAVIAudioStreamHeaders(); - WebRtc_Word32 WriteAVIAudioStreamHeaderChunks(); - - WebRtc_Word32 WriteMoviStart(); - - size_t PutByte(WebRtc_UWord8 byte); - size_t PutLE16(WebRtc_UWord16 word); - size_t PutLE32(WebRtc_UWord32 word); - size_t PutBuffer(const WebRtc_UWord8* str, size_t size); - size_t PutBufferZ(const char* str); - long PutLE32LengthFromCurrent(long startPos); - void PutLE32AtPos(long pos, WebRtc_UWord32 word); - - size_t GetByte(WebRtc_UWord8& word); - size_t GetLE16(WebRtc_UWord16& word); - size_t GetLE32(WebRtc_UWord32& word); - size_t GetBuffer(WebRtc_UWord8* str, size_t size); - - void CloseRead(); - void CloseWrite(); - - void ResetMembers(); - - WebRtc_Word32 ReadRIFF(); - WebRtc_Word32 ReadHeaders(); - WebRtc_Word32 ReadAVIMainHeader(); - WebRtc_Word32 ReadAVIVideoStreamHeader(WebRtc_Word32 endpos); - WebRtc_Word32 ReadAVIAudioStreamHeader(WebRtc_Word32 endpos); - - WebRtc_UWord32 StreamAndTwoCharCodeToTag(WebRtc_Word32 streamNum, - const char* twoCharCode); - - void ClearIndexList(); - void AddChunkToIndexList(WebRtc_UWord32 inChunkId, WebRtc_UWord32 inFlags, - WebRtc_UWord32 inOffset, WebRtc_UWord32 inSize); - - void WriteIndex(); - -private: - struct AVIMAINHEADER - { - AVIMAINHEADER(); - WebRtc_UWord32 fcc; - WebRtc_UWord32 cb; - WebRtc_UWord32 dwMicroSecPerFrame; - WebRtc_UWord32 dwMaxBytesPerSec; - WebRtc_UWord32 dwPaddingGranularity; - WebRtc_UWord32 dwFlags; - WebRtc_UWord32 dwTotalFrames; - WebRtc_UWord32 dwInitialFrames; - WebRtc_UWord32 dwStreams; - WebRtc_UWord32 dwSuggestedBufferSize; - WebRtc_UWord32 dwWidth; - WebRtc_UWord32 dwHeight; - WebRtc_UWord32 dwReserved[4]; - }; - - struct AVIStream - { - AVIStreamType streamType; - int streamNumber; - }; - - CriticalSectionWrapper* _crit; - FILE* _aviFile; - AVIMAINHEADER _aviHeader; - AVISTREAMHEADER _videoStreamHeader; - AVISTREAMHEADER _audioStreamHeader; - BITMAPINFOHEADER _videoFormatHeader; - WAVEFORMATEX _audioFormatHeader; - - WebRtc_Word8 _videoConfigParameters[CODEC_CONFIG_LENGTH]; - WebRtc_Word32 _videoConfigLength; - WebRtc_Word8 _videoStreamName[STREAM_NAME_LENGTH]; - WebRtc_Word32 _videoStreamNameLength; - WebRtc_Word8 _audioConfigParameters[CODEC_CONFIG_LENGTH]; - WebRtc_Word8 _audioStreamName[STREAM_NAME_LENGTH]; - - AVIStream _videoStream; - AVIStream _audioStream; - - WebRtc_Word32 _nrStreams; - WebRtc_Word32 _aviLength; - WebRtc_Word32 _dataLength; - size_t _bytesRead; - size_t _dataStartByte; - WebRtc_Word32 _framesRead; - WebRtc_Word32 _videoFrames; - WebRtc_Word32 _audioFrames; - - bool _reading; - AVIStreamType _openedAs; - bool _loop; - bool _writing; - - size_t _bytesWritten; - - size_t _riffSizeMark; - size_t _moviSizeMark; - size_t _totNumFramesMark; - size_t _videoStreamLengthMark; - size_t _audioStreamLengthMark; - WebRtc_Word32 _moviListOffset; - - bool _writeAudioStream; - bool _writeVideoStream; - - AVIFileMode _aviMode; - WebRtc_UWord8* _videoCodecConfigParams; - WebRtc_Word32 _videoCodecConfigParamsLength; - - WebRtc_UWord32 _videoStreamDataChunkPrefix; - WebRtc_UWord32 _audioStreamDataChunkPrefix; - bool _created; - - ListWrapper* _indexList; // Elements are of type AVIINDEXENTRY. -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_MEDIA_FILE_SOURCE_AVI_FILE_H_ diff --git a/modules/media_file/source/media_file.gyp b/modules/media_file/source/media_file.gyp deleted file mode 100644 index 0aff1956a..000000000 --- a/modules/media_file/source/media_file.gyp +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'media_file', - 'type': '<(library)', - 'dependencies': [ - '../../utility/source/utility.gyp:webrtc_utility', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'defines': [ - 'WEBRTC_MODULE_UTILITY_VIDEO', # for compiling support for video recording - ], - 'include_dirs': [ - '../interface', - '../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../interface', - ], - }, - 'sources': [ - '../interface/media_file.h', - '../interface/media_file_defines.h', - 'avi_file.cc', - 'avi_file.h', - 'media_file_impl.cc', - 'media_file_impl.h', - 'media_file_utility.cc', - 'media_file_utility.h', - ], # source - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/media_file/source/media_file_impl.cc b/modules/media_file/source/media_file_impl.cc deleted file mode 100644 index 785da493d..000000000 --- a/modules/media_file/source/media_file_impl.cc +++ /dev/null @@ -1,1473 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "critical_section_wrapper.h" -#include "file_wrapper.h" -#include "media_file_impl.h" -#include "tick_util.h" -#include "trace.h" - -#if (defined(WIN32) || defined(WINCE)) - #define STR_CASE_CMP _stricmp - #define STR_NCASE_CMP _strnicmp -#else - #define STR_CASE_CMP strcasecmp - #define STR_NCASE_CMP strncasecmp -#endif - -namespace webrtc { -MediaFile* MediaFile::CreateMediaFile(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, id, "CreateMediaFile()"); - return new MediaFileImpl(id); -} - -void MediaFile::DestroyMediaFile(MediaFile* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, -1, "DestroyMediaFile()"); - delete static_cast(module); -} - -MediaFileImpl::MediaFileImpl(const WebRtc_Word32 id) - : _id(id), - _crit(*CriticalSectionWrapper::CreateCriticalSection()), - _callbackCrit(*CriticalSectionWrapper::CreateCriticalSection()), - _ptrFileUtilityObj(NULL), - codec_info_(), - _ptrInStream(NULL), - _ptrOutStream(NULL), - _fileFormat((FileFormats)-1), - _recordDurationMs(0), - _playoutPositionMs(0), - _notificationMs(0), - _playingActive(false), - _recordingActive(false), - _isStereo(false), - _openFile(false), - _fileName(), - _ptrCallback(NULL) -{ - WEBRTC_TRACE(kTraceMemory, kTraceFile, id, "Created"); - - codec_info_.plname[0] = '\0'; - _fileName[0] = '\0'; -} - - -MediaFileImpl::~MediaFileImpl() -{ - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, "~MediaFileImpl()"); - { - CriticalSectionScoped lock(_crit); - - if(_playingActive) - { - StopPlaying(); - } - - if(_recordingActive) - { - StopRecording(); - } - - delete _ptrFileUtilityObj; - - if(_openFile) - { - delete _ptrInStream; - _ptrInStream = NULL; - delete _ptrOutStream; - _ptrOutStream = NULL; - } - } - - delete &_crit; - delete &_callbackCrit; -} - -WebRtc_Word32 MediaFileImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "Version(?,bufferLength:%d,position:%d)", - remainingBufferInBytes, position); - return GetVersion(version, remainingBufferInBytes, position); -} - -WebRtc_Word32 MediaFile::GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, -1, - "GetVersion(?,bufferLength:%d,position:%d)", - remainingBufferInBytes, position); - if(version == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, -1, - "Invalid buffer pointer in argument to Version()"); - return -1; - } - const WebRtc_Word8 ourVersion[] = "MediaFile 1.0.0"; - const WebRtc_UWord32 ourLength =(WebRtc_UWord32)strlen(ourVersion); - if(remainingBufferInBytes <(ourLength + 1)) - { - return -1; - } - memcpy(&version[position], ourVersion, ourLength); - version[position + ourLength] = '\0'; - remainingBufferInBytes -=(ourLength + 1); - position +=(ourLength + 1); - return 0; -} - -WebRtc_Word32 MediaFileImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, "ChangeUniqueId(new id:%d)", - id); - _id = id; - return 0; -} - -WebRtc_Word32 MediaFileImpl::TimeUntilNextProcess() -{ - WEBRTC_TRACE( - kTraceWarning, - kTraceFile, - _id, - "TimeUntilNextProcess: This method is not used by MediaFile class."); - return -1; -} - -WebRtc_Word32 MediaFileImpl::Process() -{ - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Process: This method is not used by MediaFile class."); - return -1; -} - -WebRtc_Word32 MediaFileImpl::PlayoutAVIVideoData( - WebRtc_Word8* buffer, - WebRtc_UWord32& dataLengthInBytes) -{ - return PlayoutData( buffer, dataLengthInBytes, true); -} - -WebRtc_Word32 MediaFileImpl::PlayoutAudioData(WebRtc_Word8* buffer, - WebRtc_UWord32& dataLengthInBytes) -{ - return PlayoutData( buffer, dataLengthInBytes, false); -} - -WebRtc_Word32 MediaFileImpl::PlayoutData(WebRtc_Word8* buffer, - WebRtc_UWord32& dataLengthInBytes, - bool video) -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "MediaFileImpl::PlayoutData(buffer= 0x%x, bufLen= %ld)", - buffer, dataLengthInBytes); - - const WebRtc_UWord32 bufferLengthInBytes = dataLengthInBytes; - dataLengthInBytes = 0; - - if(buffer == NULL || bufferLengthInBytes == 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Buffer pointer or length is NULL!"); - return -1; - } - - WebRtc_Word32 bytesRead = 0; - { - CriticalSectionScoped lock(_crit); - - if(!_playingActive) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Not currently playing!"); - return -1; - } - - if(!_ptrFileUtilityObj) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Playing, but no FileUtility object!"); - StopPlaying(); - return -1; - } - - switch(_fileFormat) - { - case kFileFormatPcm32kHzFile: - case kFileFormatPcm16kHzFile: - case kFileFormatPcm8kHzFile: - bytesRead = _ptrFileUtilityObj->ReadPCMData( - *_ptrInStream, - buffer, - bufferLengthInBytes); - break; - case kFileFormatCompressedFile: - bytesRead = _ptrFileUtilityObj->ReadCompressedData( - *_ptrInStream, - buffer, - bufferLengthInBytes); - break; - case kFileFormatWavFile: - bytesRead = _ptrFileUtilityObj->ReadWavDataAsMono( - *_ptrInStream, - buffer, - bufferLengthInBytes); - break; - case kFileFormatPreencodedFile: - bytesRead = _ptrFileUtilityObj->ReadPreEncodedData( - *_ptrInStream, - buffer, - bufferLengthInBytes); - if(bytesRead > 0) - { - dataLengthInBytes = bytesRead; - return 0; - } - break; -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - { - if(video) - { - bytesRead = _ptrFileUtilityObj->ReadAviVideoData( - buffer, - bufferLengthInBytes); - } - else - { - bytesRead = _ptrFileUtilityObj->ReadAviAudioData( - buffer, - bufferLengthInBytes); - } - break; - } -#endif - default: - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Playing file, but file format invalid!"); - assert(false); - break; - } - - if( bytesRead > 0) - { - dataLengthInBytes =(WebRtc_UWord32) bytesRead; - } - } - HandlePlayCallbacks(bytesRead); - return 0; -} - -void MediaFileImpl::HandlePlayCallbacks(WebRtc_Word32 bytesRead) -{ - bool playEnded = false; - WebRtc_UWord32 callbackNotifyMs = 0; - - if(bytesRead > 0) - { - // Check if it's time for PlayNotification(..). - _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); - if(_notificationMs) - { - if(_playoutPositionMs >= _notificationMs) - { - _notificationMs = 0; - callbackNotifyMs = _playoutPositionMs; - } - } - } - else - { - // If no bytes were read assume end of file. - StopPlaying(); - playEnded = true; - } - - // Only _callbackCrit may and should be taken when making callbacks. - CriticalSectionScoped lock(_callbackCrit); - if(_ptrCallback) - { - if(callbackNotifyMs) - { - _ptrCallback->PlayNotification(_id, callbackNotifyMs); - } - if(playEnded) - { - _ptrCallback->PlayFileEnded(_id); - } - } -} - -WebRtc_Word32 MediaFileImpl::PlayoutStereoData( - WebRtc_Word8* bufferLeft, - WebRtc_Word8* bufferRight, - WebRtc_UWord32& dataLengthInBytes) -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "MediaFileImpl::PlayoutStereoData(Left = 0x%x, Right = 0x%x,\ - Len= %ld)", - bufferLeft, - bufferRight, - dataLengthInBytes); - - const WebRtc_UWord32 bufferLengthInBytes = dataLengthInBytes; - dataLengthInBytes = 0; - - if(bufferLeft == NULL || bufferRight == NULL || bufferLengthInBytes == 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "A buffer pointer or the length is NULL!"); - return -1; - } - - bool playEnded = false; - WebRtc_UWord32 callbackNotifyMs = 0; - { - CriticalSectionScoped lock(_crit); - - if(!_playingActive || !_isStereo) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Not currently playing stereo!"); - return -1; - } - - if(!_ptrFileUtilityObj) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "Playing stereo, but the FileUtility objects is NULL!"); - StopPlaying(); - return -1; - } - - // Stereo playout only supported for WAV files. - WebRtc_Word32 bytesRead = 0; - switch(_fileFormat) - { - case kFileFormatWavFile: - bytesRead = _ptrFileUtilityObj->ReadWavDataAsStereo( - *_ptrInStream, - bufferLeft, - bufferRight, - bufferLengthInBytes); - break; - default: - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Trying to read non-WAV as stereo audio\ - (not supported)"); - break; - } - - if(bytesRead > 0) - { - dataLengthInBytes = bytesRead; - - // Check if it's time for PlayNotification(..). - _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); - if(_notificationMs) - { - if(_playoutPositionMs >= _notificationMs) - { - _notificationMs = 0; - callbackNotifyMs = _playoutPositionMs; - } - } - } - else - { - // If no bytes were read assume end of file. - StopPlaying(); - playEnded = true; - } - } - - CriticalSectionScoped lock(_callbackCrit); - if(_ptrCallback) - { - if(callbackNotifyMs) - { - _ptrCallback->PlayNotification(_id, callbackNotifyMs); - } - if(playEnded) - { - _ptrCallback->PlayFileEnded(_id); - } - } - return 0; -} - -WebRtc_Word32 MediaFileImpl::StartPlayingAudioFile( - const WebRtc_Word8* fileName, - const WebRtc_UWord32 notificationTimeMs, - const bool loop, - const FileFormats format, - const CodecInst* codecInst, - const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs) -{ - const bool videoOnly = false; - return StartPlayingFile(fileName, notificationTimeMs, loop, videoOnly, - format, codecInst, startPointMs, stopPointMs); -} - - -WebRtc_Word32 MediaFileImpl::StartPlayingVideoFile(const WebRtc_Word8* fileName, - const bool loop, - bool videoOnly, - const FileFormats format) -{ - - const WebRtc_UWord32 notificationTimeMs = 0; - const WebRtc_UWord32 startPointMs = 0; - const WebRtc_UWord32 stopPointMs = 0; - return StartPlayingFile(fileName, notificationTimeMs, loop, videoOnly, - format, 0, startPointMs, stopPointMs); -} - -WebRtc_Word32 MediaFileImpl::StartPlayingFile( - const WebRtc_Word8* fileName, - const WebRtc_UWord32 notificationTimeMs, - const bool loop, - bool videoOnly, - const FileFormats format, - const CodecInst* codecInst, - const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "MediaFileImpl::StartPlayingFile: fileName= %s, notify= %d, loop= %d,\ - format= %d, codecInst=%s, start= %d, stop= %d", - (fileName == NULL) ? "NULL" : fileName, - notificationTimeMs, - loop, - format,(codecInst == NULL) ? "NULL" : codecInst->plname, - startPointMs, - stopPointMs); - - if(!ValidFileName(fileName)) - { - return -1; - } - if(!ValidFileFormat(format,codecInst)) - { - return -1; - } - if(!ValidFilePositions(startPointMs,stopPointMs)) - { - return -1; - } - - // Check that the file will play longer than notificationTimeMs ms. - if((startPointMs && stopPointMs && !loop) && - (notificationTimeMs > (stopPointMs - startPointMs))) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "specified notification time is longer than amount of ms that will\ - be played"); - return -1; - } - - FileWrapper* inputStream = FileWrapper::Create(); - if(inputStream == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "Failed to allocate input stream for file %s", fileName); - return -1; - } - - // TODO (hellner): make all formats support reading from stream. - bool useStream = (format != kFileFormatAviFile); - if( useStream) - { - if(inputStream->OpenFile(fileName, true, loop) != 0) - { - delete inputStream; - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Could not open input file %s", fileName); - return -1; - } - } - - if(StartPlayingStream(*inputStream, fileName, loop, notificationTimeMs, - format, codecInst, startPointMs, stopPointMs, - videoOnly) == -1) - { - if( useStream) - { - inputStream->CloseFile(); - } - delete inputStream; - return -1; - } - - CriticalSectionScoped lock(_crit); - _openFile = true; - strncpy(_fileName, fileName, sizeof(_fileName)); - _fileName[sizeof(_fileName) - 1] = '\0'; - return 0; -} - -WebRtc_Word32 MediaFileImpl::StartPlayingAudioStream( - InStream& stream, - const WebRtc_UWord32 notificationTimeMs, - const FileFormats format, - const CodecInst* codecInst, - const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs) -{ - return StartPlayingStream(stream, 0, false, notificationTimeMs, format, - codecInst, startPointMs, stopPointMs); -} - -WebRtc_Word32 MediaFileImpl::StartPlayingStream( - InStream& stream, - const WebRtc_Word8* filename, - bool loop, - const WebRtc_UWord32 notificationTimeMs, - const FileFormats format, - const CodecInst* codecInst, - const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs, - bool videoOnly) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "MediaFileImpl::StartPlayingStream(stream=0x%x, notify=%ld, format=%d,\ - codecInst=%s, start=%ld, stop=%ld", - &stream, - notificationTimeMs, - format, - (codecInst == NULL) ? "NULL" : codecInst->plname, - startPointMs, - stopPointMs); - - if(!ValidFileFormat(format,codecInst)) - { - return -1; - } - - if(!ValidFilePositions(startPointMs,stopPointMs)) - { - return -1; - } - - CriticalSectionScoped lock(_crit); - if(_playingActive || _recordingActive) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "StartPlaying called, but already playing or recording file %s", - (_fileName == NULL) ? "NULL" : _fileName); - return -1; - } - - if(_ptrFileUtilityObj != NULL) - { - WEBRTC_TRACE(kTraceError, - kTraceFile, - _id, - "StartPlaying called, but FileUtilityObj already exists!"); - StopPlaying(); - return -1; - } - - _ptrFileUtilityObj = new ModuleFileUtility(_id); - if(_ptrFileUtilityObj == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "Failed to create FileUtilityObj!"); - return -1; - } - - switch(format) - { - case kFileFormatWavFile: - { - if(_ptrFileUtilityObj->InitWavReading(stream, startPointMs, - stopPointMs) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a valid WAV file!"); - StopPlaying(); - return -1; - } - _fileFormat = kFileFormatWavFile; - break; - } - case kFileFormatCompressedFile: - { - if(_ptrFileUtilityObj->InitCompressedReading(stream, startPointMs, - stopPointMs) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a valid Compressed file!"); - StopPlaying(); - return -1; - } - _fileFormat = kFileFormatCompressedFile; - break; - } - case kFileFormatPcm8kHzFile: - case kFileFormatPcm16kHzFile: - case kFileFormatPcm32kHzFile: - { - if(!ValidFrequency(codecInst->plfreq) || - _ptrFileUtilityObj->InitPCMReading(stream, startPointMs, - stopPointMs, - codecInst->plfreq) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a valid raw 8 or 16 KHz PCM file!"); - StopPlaying(); - return -1; - } - - _fileFormat = format; - break; - } - case kFileFormatPreencodedFile: - { - if(_ptrFileUtilityObj->InitPreEncodedReading(stream, *codecInst) == - -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a valid PreEncoded file!"); - StopPlaying(); - return -1; - } - - _fileFormat = kFileFormatPreencodedFile; - break; - } -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - { - if(_ptrFileUtilityObj->InitAviReading( filename, videoOnly, loop)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a valid AVI file!"); - StopPlaying(); - - return -1; - } - - _ptrFileUtilityObj->codec_info(codec_info_); - - _fileFormat = kFileFormatAviFile; - break; - } -#endif - default: - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Invalid file format specified!"); - StopPlaying(); - return -1; - } - } - if(_ptrFileUtilityObj->codec_info(codec_info_) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to retrieve codec info!"); - StopPlaying(); - return -1; - } - - _isStereo = (codec_info_.channels == 2); - if(_isStereo && (_fileFormat != kFileFormatWavFile)) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Stereo is only allowed for WAV files"); - StopPlaying(); - return -1; - } - _playingActive = true; - _playoutPositionMs = _ptrFileUtilityObj->PlayoutPositionMs(); - _ptrInStream = &stream; - _notificationMs = notificationTimeMs; - - return 0; -} - -WebRtc_Word32 MediaFileImpl::StopPlaying() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::StopPlaying()"); - - CriticalSectionScoped lock(_crit); - _isStereo = false; - if(_ptrFileUtilityObj) - { - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - } - if(_ptrInStream) - { - // If MediaFileImpl opened the InStream it must be reclaimed here. - if(_openFile) - { - delete _ptrInStream; - _openFile = false; - } - _ptrInStream = NULL; - } - - codec_info_.pltype = 0; - codec_info_.plname[0] = '\0'; - - if(!_playingActive) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "playing is not active!"); - return -1; - } - - _playingActive = false; - return 0; -} - -bool MediaFileImpl::IsPlaying() -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsPlaying()"); - CriticalSectionScoped lock(_crit); - return _playingActive; -} - -WebRtc_Word32 MediaFileImpl::IncomingAudioData( - const WebRtc_Word8* buffer, - const WebRtc_UWord32 bufferLengthInBytes) -{ - return IncomingAudioVideoData( buffer, bufferLengthInBytes, false); -} - -WebRtc_Word32 MediaFileImpl::IncomingAVIVideoData( - const WebRtc_Word8* buffer, - const WebRtc_UWord32 bufferLengthInBytes) -{ - return IncomingAudioVideoData( buffer, bufferLengthInBytes, true); -} - -WebRtc_Word32 MediaFileImpl::IncomingAudioVideoData( - const WebRtc_Word8* buffer, - const WebRtc_UWord32 bufferLengthInBytes, - const bool video) -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "MediaFile::IncomingData(buffer= 0x%x, bufLen= %hd", - buffer, bufferLengthInBytes); - - if(buffer == NULL || bufferLengthInBytes == 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Buffer pointer or length is NULL!"); - return -1; - } - - bool recordingEnded = false; - WebRtc_UWord32 callbackNotifyMs = 0; - { - CriticalSectionScoped lock(_crit); - - if(!_recordingActive) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Not currently recording!"); - return -1; - } - if(_ptrOutStream == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Recording is active, but output stream is NULL!"); - assert(false); - return -1; - } - - WebRtc_Word32 bytesWritten = 0; - WebRtc_UWord32 samplesWritten = codec_info_.pacsize; - if(_ptrFileUtilityObj) - { - switch(_fileFormat) - { - case kFileFormatPcm8kHzFile: - case kFileFormatPcm16kHzFile: - case kFileFormatPcm32kHzFile: - bytesWritten = _ptrFileUtilityObj->WritePCMData( - *_ptrOutStream, - buffer, - bufferLengthInBytes); - - // Sample size is 2 bytes. - if(bytesWritten > 0) - { - samplesWritten = bytesWritten/sizeof(WebRtc_Word16); - } - break; - case kFileFormatCompressedFile: - bytesWritten = _ptrFileUtilityObj->WriteCompressedData( - *_ptrOutStream, buffer, bufferLengthInBytes); - break; - case kFileFormatWavFile: - bytesWritten = _ptrFileUtilityObj->WriteWavData( - *_ptrOutStream, - buffer, - bufferLengthInBytes); - if(bytesWritten > 0 && STR_NCASE_CMP(codec_info_.plname, - "L16", 4) == 0) - { - // Sample size is 2 bytes. - samplesWritten = bytesWritten/sizeof(WebRtc_Word16); - } - break; - case kFileFormatPreencodedFile: - bytesWritten = _ptrFileUtilityObj->WritePreEncodedData( - *_ptrOutStream, buffer, bufferLengthInBytes); - break; -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - if(video) - { - bytesWritten = _ptrFileUtilityObj->WriteAviVideoData( - buffer, bufferLengthInBytes); - }else - { - bytesWritten = _ptrFileUtilityObj->WriteAviAudioData( - buffer, bufferLengthInBytes); - } - break; -#endif - default: - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "recording active, but file format invalid!"); - break; - } - } else { - // TODO (hellner): quick look at the code makes me think that this - // code is never executed. Remove? - if(_ptrOutStream) - { - if(_ptrOutStream->Write(buffer, bufferLengthInBytes)) - { - bytesWritten = bufferLengthInBytes; - } - } - } - - if(!video) - { - _recordDurationMs += samplesWritten / (codec_info_.plfreq / 1000); - } - - // Check if it's time for RecordNotification(..). - if(_notificationMs) - { - if(_recordDurationMs >= _notificationMs) - { - _notificationMs = 0; - callbackNotifyMs = _recordDurationMs; - } - } - if(bytesWritten < (WebRtc_Word32)bufferLengthInBytes) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Failed to write all requested bytes!"); - StopRecording(); - recordingEnded = true; - } - } - - // Only _callbackCrit may and should be taken when making callbacks. - CriticalSectionScoped lock(_callbackCrit); - if(_ptrCallback) - { - if(callbackNotifyMs) - { - _ptrCallback->RecordNotification(_id, callbackNotifyMs); - } - if(recordingEnded) - { - _ptrCallback->RecordFileEnded(_id); - return -1; - } - } - return 0; -} - -WebRtc_Word32 MediaFileImpl::StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs, - const WebRtc_UWord32 maxSizeBytes) -{ - VideoCodec dummyCodecInst; - return StartRecordingFile(fileName, format, codecInst, dummyCodecInst, - notificationTimeMs, maxSizeBytes); -} - - -WebRtc_Word32 MediaFileImpl::StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - bool videoOnly) -{ - const WebRtc_UWord32 notificationTimeMs = 0; - const WebRtc_UWord32 maxSizeBytes = 0; - - return StartRecordingFile(fileName, format, codecInst, videoCodecInst, - notificationTimeMs, maxSizeBytes, videoOnly); -} - -WebRtc_Word32 MediaFileImpl::StartRecordingFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - const WebRtc_UWord32 notificationTimeMs, - const WebRtc_UWord32 maxSizeBytes, - bool videoOnly) -{ - WEBRTC_TRACE(kTraceModuleCall, - kTraceFile, - _id, - "MediaFileImpl::StartRecordingFile(fileName= %s, format= %d,\ - codecInst= %s, notificationMs= %d, maxSize= %d", - (fileName == NULL) ? "NULL" : fileName, - format, - (codecInst.plname[0] == '\0') ? "NULL" : codecInst.plname, - notificationTimeMs, - maxSizeBytes); - - if(!ValidFileName(fileName)) - { - return -1; - } - if(!ValidFileFormat(format,&codecInst)) - { - return -1; - } - - FileWrapper* outputStream = FileWrapper::Create(); - if(outputStream == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "Failed to allocate memory for output stream"); - return -1; - } - - // TODO (hellner): make all formats support writing to stream. - const bool useStream = ( format != kFileFormatAviFile); - if( useStream) - { - if(outputStream->OpenFile(fileName, false) != 0) - { - delete outputStream; - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Could not open output file '%s' for writing!", - fileName); - return -1; - } - } - if(maxSizeBytes) - { - outputStream->SetMaxFileSize(maxSizeBytes); - } - - if(StartRecordingStream(*outputStream, fileName, format, codecInst, - videoCodecInst, notificationTimeMs, - videoOnly) == -1) - { - if( useStream) - { - outputStream->CloseFile(); - } - delete outputStream; - return -1; - } - - CriticalSectionScoped lock(_crit); - _openFile = true; - strncpy(_fileName, fileName, sizeof(_fileName)); - _fileName[sizeof(_fileName) - 1] = '\0'; - return 0; -} - -WebRtc_Word32 MediaFileImpl::StartRecordingAudioStream( - OutStream& stream, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs) -{ - VideoCodec dummyCodecInst; - return StartRecordingStream(stream, 0, format, codecInst, dummyCodecInst, - notificationTimeMs); -} - -WebRtc_Word32 MediaFileImpl::StartRecordingStream( - OutStream& stream, - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - const WebRtc_UWord32 notificationTimeMs, - bool videoOnly) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "MediaFileImpl::StartRecordingStream: format= %d, codec= %s,\ - notify= %d", - format,(codecInst.plname[0] == '\0') ? "NULL" : codecInst.plname, - notificationTimeMs); - - // Check codec info - if(!ValidFileFormat(format,&codecInst)) - { - return -1; - } - - CriticalSectionScoped lock(_crit); - if(_recordingActive || _playingActive) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "StartRecording called, but already recording or playing file %s!", - _fileName); - return -1; - } - - if(_ptrFileUtilityObj != NULL) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "StartRecording called, but fileUtilityObj already exists!"); - StopRecording(); - return -1; - } - - _ptrFileUtilityObj = new ModuleFileUtility(_id); - if(_ptrFileUtilityObj == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "Cannot allocate fileUtilityObj!"); - return -1; - } - - CodecInst tmpAudioCodec; - memcpy(&tmpAudioCodec, &codecInst, sizeof(CodecInst)); - switch(format) - { - case kFileFormatWavFile: - { - if(_ptrFileUtilityObj->InitWavWriting(stream, codecInst) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to initialize WAV file!"); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - _fileFormat = kFileFormatWavFile; - break; - } - case kFileFormatCompressedFile: - { - // Write compression codec name at beginning of file - if(_ptrFileUtilityObj->InitCompressedWriting(stream, codecInst) == - -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to initialize Compressed file!"); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - _fileFormat = kFileFormatCompressedFile; - break; - } - case kFileFormatPcm8kHzFile: - case kFileFormatPcm16kHzFile: - { - if(!ValidFrequency(codecInst.plfreq) || - _ptrFileUtilityObj->InitPCMWriting(stream, codecInst.plfreq) == - -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to initialize 8 or 16KHz PCM file!"); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - _fileFormat = format; - break; - } - case kFileFormatPreencodedFile: - { - if(_ptrFileUtilityObj->InitPreEncodedWriting(stream, codecInst) == - -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to initialize Pre-Encoded file!"); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - - _fileFormat = kFileFormatPreencodedFile; - break; - } -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - { - if( (_ptrFileUtilityObj->InitAviWriting( - fileName, - codecInst, - videoCodecInst,videoOnly) == -1) || - (_ptrFileUtilityObj->codec_info(tmpAudioCodec) != 0)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Failed to initialize AVI file!"); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - _fileFormat = kFileFormatAviFile; - break; - } -#endif - default: - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Invalid file format %d specified!", format); - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - return -1; - } - } - _isStereo = (tmpAudioCodec.channels == 2); - if(_isStereo) - { - if(_fileFormat != kFileFormatWavFile) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "Stereo is only allowed for WAV files"); - StopRecording(); - return -1; - } - if((STR_NCASE_CMP(codec_info_.plname, "L16", 4) != 0) && - (STR_NCASE_CMP(codec_info_.plname, "PCMU", 5) != 0) && - (STR_NCASE_CMP(codec_info_.plname, "PCMA", 5) != 0)) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceFile, - _id, - "Stereo is only allowed for codec PCMU, PCMA and L16 "); - StopRecording(); - return -1; - } - } - memcpy(&codec_info_, &tmpAudioCodec, sizeof(CodecInst)); - _recordingActive = true; - _ptrOutStream = &stream; - _notificationMs = notificationTimeMs; - _recordDurationMs = 0; - return 0; -} - -WebRtc_Word32 MediaFileImpl::StopRecording() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::StopRecording()"); - - CriticalSectionScoped lock(_crit); - if(!_recordingActive) - { - WEBRTC_TRACE(kTraceWarning, kTraceFile, _id, - "recording is not active!"); - return -1; - } - - _isStereo = false; - - if(_ptrFileUtilityObj != NULL) - { - // Both AVI and WAV header has to be updated before closing the stream - // because they contain size information. - if((_fileFormat == kFileFormatWavFile) && - (_ptrOutStream != NULL)) - { - _ptrFileUtilityObj->UpdateWavHeader(*_ptrOutStream); - } -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - else if( _fileFormat == kFileFormatAviFile) - { - _ptrFileUtilityObj->CloseAviFile( ); - } -#endif - delete _ptrFileUtilityObj; - _ptrFileUtilityObj = NULL; - } - - if(_ptrOutStream != NULL) - { - // If MediaFileImpl opened the OutStream it must be reclaimed here. - if(_openFile) - { - delete _ptrOutStream; - _openFile = false; - } - _ptrOutStream = NULL; - } - - _recordingActive = false; - codec_info_.pltype = 0; - codec_info_.plname[0] = '\0'; - - return 0; -} - -bool MediaFileImpl::IsRecording() -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsRecording()"); - CriticalSectionScoped lock(_crit); - return _recordingActive; -} - -WebRtc_Word32 MediaFileImpl::RecordDurationMs(WebRtc_UWord32& durationMs) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::RecordDurationMs()"); - - CriticalSectionScoped lock(_crit); - if(!_recordingActive) - { - durationMs = 0; - return -1; - } - durationMs = _recordDurationMs; - return 0; -} - -bool MediaFileImpl::IsStereo() -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, "MediaFileImpl::IsStereo()"); - CriticalSectionScoped lock(_crit); - return _isStereo; -} - -WebRtc_Word32 MediaFileImpl::SetModuleFileCallback(FileCallback* callback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::SetModuleFileCallback(callback= 0x%x)", - &callback); - - CriticalSectionScoped lock(_callbackCrit); - - _ptrCallback = callback; - return 0; -} - -WebRtc_Word32 MediaFileImpl::FileDurationMs(const WebRtc_Word8* fileName, - WebRtc_UWord32& durationMs, - const FileFormats format, - const WebRtc_UWord32 freqInHz) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "MediaFileImpl::FileDurationMs(%s, -, format= %d, freqInHz= %d)", - (fileName == NULL) ? "NULL" : fileName, - format, - freqInHz); - - if(!ValidFileName(fileName)) - { - return -1; - } - if(!ValidFrequency(freqInHz)) - { - return -1; - } - - ModuleFileUtility* utilityObj = new ModuleFileUtility(_id); - if(utilityObj == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "failed to allocate utility object!"); - return -1; - } - - const WebRtc_Word32 duration = utilityObj->FileDurationMs(fileName, format, - freqInHz); - delete utilityObj; - if(duration == -1) - { - durationMs = 0; - return -1; - } - - durationMs = duration; - return 0; -} - -WebRtc_Word32 MediaFileImpl::PlayoutPositionMs(WebRtc_UWord32& positionMs) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::PlayoutPositionMS(?)"); - CriticalSectionScoped lock(_crit); - if(!_playingActive) - { - positionMs = 0; - return -1; - } - positionMs = _playoutPositionMs; - return 0; -} - -WebRtc_Word32 MediaFileImpl::codec_info(CodecInst& codecInst) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::codec_info(CodecInst= 0x%x)", &codecInst); - CriticalSectionScoped lock(_crit); - if(!_playingActive && !_recordingActive) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Neither playout nor recording has been initialized!"); - return -1; - } - if (codec_info_.pltype == 0 && codec_info_.plname[0] == '\0') - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "The CodecInst for %s is unknown!", - _playingActive ? "Playback" : "Recording"); - return -1; - } - memcpy(&codecInst,&codec_info_,sizeof(CodecInst)); - return 0; -} - -WebRtc_Word32 MediaFileImpl::VideoCodecInst(VideoCodec& codecInst) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "MediaFileImpl::VideoCodecInst(CodecInst= 0x%x)", &codecInst); - CriticalSectionScoped lock(_crit); - if(!_playingActive && !_recordingActive) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Neither playout nor recording has been initialized!"); - return -1; - } - if( _ptrFileUtilityObj == NULL) - { - return -1; - } -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - VideoCodec videoCodec; - if( _ptrFileUtilityObj->VideoCodecInst( videoCodec) != 0) - { - return -1; - } - memcpy(&codecInst,&videoCodec,sizeof(VideoCodec)); - return 0; -#else - return -1; -#endif -} - -bool MediaFileImpl::ValidFileFormat(const FileFormats format, - const CodecInst* codecInst) -{ - if(codecInst == NULL) - { - if(format == kFileFormatPreencodedFile || - format == kFileFormatPcm8kHzFile || - format == kFileFormatPcm16kHzFile || - format == kFileFormatPcm32kHzFile) - { - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "Codec info required for file format specified!"); - return false; - } - } - return true; -} - -bool MediaFileImpl::ValidFileName(const WebRtc_Word8* fileName) -{ - if((fileName == NULL) ||(fileName[0] == '\0')) - { - WEBRTC_TRACE(kTraceError, kTraceFile, -1, "FileName not specified!"); - return false; - } - return true; -} - - -bool MediaFileImpl::ValidFilePositions(const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs) -{ - if(startPointMs == 0 && stopPointMs == 0) // Default values - { - return true; - } - if(stopPointMs &&(startPointMs >= stopPointMs)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "startPointMs must be less than stopPointMs!"); - return false; - } - if(stopPointMs &&((stopPointMs - startPointMs) < 20)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "minimum play duration for files is 20 ms!"); - return false; - } - return true; -} - -bool MediaFileImpl::ValidFrequency(const WebRtc_UWord32 frequency) -{ - if((frequency == 8000) || (frequency == 16000)|| (frequency == 32000)) - { - return true; - } - WEBRTC_TRACE(kTraceError, kTraceFile, -1, - "Frequency should be 8000, 16000 or 32000 (Hz)"); - return false; -} -} // namespace webrtc diff --git a/modules/media_file/source/media_file_impl.h b/modules/media_file/source/media_file_impl.h deleted file mode 100644 index c0676ef36..000000000 --- a/modules/media_file/source/media_file_impl.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_IMPL_H_ -#define WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_IMPL_H_ - -#include "common_types.h" -#include "media_file.h" -#include "media_file_defines.h" -#include "media_file_utility.h" -#include "module_common_types.h" - -namespace webrtc { -class MediaFileImpl : public MediaFile -{ - -public: - MediaFileImpl(const WebRtc_Word32 id); - ~MediaFileImpl(); - - // Module functions - WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - WebRtc_Word32 Process(); - WebRtc_Word32 TimeUntilNextProcess(); - - // MediaFile functions - WebRtc_Word32 PlayoutAudioData(WebRtc_Word8* audioBuffer, - WebRtc_UWord32& dataLengthInBytes); - WebRtc_Word32 PlayoutAVIVideoData(WebRtc_Word8* videoBuffer, - WebRtc_UWord32& dataLengthInBytes); - WebRtc_Word32 PlayoutStereoData(WebRtc_Word8* audioBufferLeft, - WebRtc_Word8* audioBufferRight, - WebRtc_UWord32& dataLengthInBytes); - virtual WebRtc_Word32 StartPlayingAudioFile( - const WebRtc_Word8* fileName, - const WebRtc_UWord32 notificationTimeMs = 0, - const bool loop = false, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0); - WebRtc_Word32 StartPlayingVideoFile(const WebRtc_Word8* fileName, - const bool loop, - bool videoOnly, - const FileFormats format); - WebRtc_Word32 StartPlayingAudioStream( - InStream& stream, - const WebRtc_UWord32 notificationTimeMs = 0, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0); - WebRtc_Word32 StopPlaying(); - bool IsPlaying(); - WebRtc_Word32 PlayoutPositionMs(WebRtc_UWord32& positionMs) const; - WebRtc_Word32 IncomingAudioData(const WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 bufferLength); - WebRtc_Word32 IncomingAVIVideoData(const WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 bufferLength); - WebRtc_Word32 StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs = 0, - const WebRtc_UWord32 maxSizeBytes = 0); - WebRtc_Word32 StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - bool videoOnly = false); - WebRtc_Word32 StartRecordingAudioStream( - OutStream& stream, - const FileFormats format, - const CodecInst& codecInst, - const WebRtc_UWord32 notificationTimeMs = 0); - WebRtc_Word32 StopRecording(); - bool IsRecording(); - WebRtc_Word32 RecordDurationMs(WebRtc_UWord32& durationMs); - bool IsStereo(); - WebRtc_Word32 SetModuleFileCallback(FileCallback* callback); - WebRtc_Word32 FileDurationMs( - const WebRtc_Word8* fileName, - WebRtc_UWord32& durationMs, - const FileFormats format, - const WebRtc_UWord32 freqInHz = 16000); - WebRtc_Word32 codec_info(CodecInst& codecInst) const; - WebRtc_Word32 VideoCodecInst(VideoCodec& codecInst) const; - -private: - // Returns true if the combination of format and codecInst is valid. - static bool ValidFileFormat(const FileFormats format, - const CodecInst* codecInst); - - - // Returns true if the filename is valid - static bool ValidFileName(const WebRtc_Word8* fileName); - - // Returns true if the combination of startPointMs and stopPointMs is valid. - static bool ValidFilePositions(const WebRtc_UWord32 startPointMs, - const WebRtc_UWord32 stopPointMs); - - // Open the file specified by fileName for reading (relative path is - // allowed). FileCallback::PlayNotification(..) will be called after - // notificationTimeMs of the file has been played if notificationTimeMs is - // greater than zero. If loop is true the file will be played until - // StopPlaying() is called. When end of file is reached the file is read - // from the start. format specifies the type of file fileName refers to. - // codecInst specifies the encoding of the audio data. Note that - // file formats that contain this information (like WAV files) don't need to - // provide a non-NULL codecInst. Only video will be read if videoOnly is - // true. startPointMs and stopPointMs, unless zero, - // specify what part of the file should be read. From startPointMs ms to - // stopPointMs ms. - WebRtc_Word32 StartPlayingFile( - const WebRtc_Word8* fileName, - const WebRtc_UWord32 notificationTimeMs = 0, - const bool loop = false, - bool videoOnly = false, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0); - - // Opens the file specified by fileName for reading (relative path is - // allowed) if format is kFileFormatAviFile otherwise use stream for - // reading. FileCallback::PlayNotification(..) will be called after - // notificationTimeMs of the file has been played if notificationTimeMs is - // greater than zero. If loop is true the file will be played until - // StopPlaying() is called. When end of file is reached the file is read - // from the start. format specifies the type of file fileName refers to. - // codecInst specifies the encoding of the audio data. Note that - // file formats that contain this information (like WAV files) don't need to - // provide a non-NULL codecInst. Only video will be read if videoOnly is - // true. startPointMs and stopPointMs, unless zero, - // specify what part of the file should be read. From startPointMs ms to - // stopPointMs ms. - // TODO (hellner): there is no reason why fileName should be needed here. - WebRtc_Word32 StartPlayingStream( - InStream& stream, - const WebRtc_Word8* fileName, - bool loop, - const WebRtc_UWord32 notificationTimeMs = 0, - const FileFormats format = kFileFormatPcm16kHzFile, - const CodecInst* codecInst = NULL, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0, - bool videoOnly = true); - - // Writes one frame into dataBuffer. dataLengthInBytes is both an input and - // output parameter. As input parameter it indicates the size of - // audioBuffer. As output parameter it indicates the number of bytes - // written to audioBuffer. If video is true the data written is a video - // frame otherwise it is an audio frame. - WebRtc_Word32 PlayoutData(WebRtc_Word8* dataBuffer, - WebRtc_UWord32& dataLengthInBytes, bool video); - - // Write one frame, i.e. the bufferLength first bytes of audioBuffer, - // to file. The frame is an audio frame if video is true otherwise it is an - // audio frame. - WebRtc_Word32 IncomingAudioVideoData(const WebRtc_Word8* buffer, - const WebRtc_UWord32 bufferLength, - const bool video); - - // Open/creates file specified by fileName for writing (relative path is - // allowed) if format is kFileFormatAviFile otherwise use stream for - // writing. FileCallback::RecordNotification(..) will be called after - // notificationTimeMs of audio data has been recorded if - // notificationTimeMs is greater than zero. - // format specifies the type of file that should be created/opened. - // codecInst specifies the encoding of the audio data. videoCodecInst - // specifies the encoding of the video data. maxSizeBytes specifies the - // number of bytes allowed to be written to file if it is greater than zero. - // If format is kFileFormatAviFile and videoOnly is true the AVI file will - // only contain video frames. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo is only supported for WAV files. - WebRtc_Word32 StartRecordingFile( - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - const WebRtc_UWord32 notificationTimeMs = 0, - const WebRtc_UWord32 maxSizeBytes = 0, - bool videoOnly = false); - - // Open/creates file specified by fileName for writing (relative path is - // allowed). FileCallback::RecordNotification(..) will be called after - // notificationTimeMs of audio data has been recorded if - // notificationTimeMs is greater than zero. - // format specifies the type of file that should be created/opened. - // codecInst specifies the encoding of the audio data. videoCodecInst - // specifies the encoding of the video data. maxSizeBytes specifies the - // number of bytes allowed to be written to file if it is greater than zero. - // If format is kFileFormatAviFile and videoOnly is true the AVI file will - // only contain video frames. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo is only supported for WAV files. - // TODO (hellner): there is no reason why fileName should be needed here. - WebRtc_Word32 StartRecordingStream( - OutStream& stream, - const WebRtc_Word8* fileName, - const FileFormats format, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - const WebRtc_UWord32 notificationTimeMs = 0, - const bool videoOnly = false); - - // Returns true if frequencyInHz is a supported frequency. - static bool ValidFrequency(const WebRtc_UWord32 frequencyInHz); - - void HandlePlayCallbacks(WebRtc_Word32 bytesRead); - - WebRtc_Word32 _id; - CriticalSectionWrapper& _crit; - CriticalSectionWrapper& _callbackCrit; - - ModuleFileUtility* _ptrFileUtilityObj; - CodecInst codec_info_; - - InStream* _ptrInStream; - OutStream* _ptrOutStream; - - FileFormats _fileFormat; - WebRtc_UWord32 _recordDurationMs; - WebRtc_UWord32 _playoutPositionMs; - WebRtc_UWord32 _notificationMs; - - bool _playingActive; - bool _recordingActive; - bool _isStereo; - bool _openFile; - - WebRtc_Word8 _fileName[512]; - - FileCallback* _ptrCallback; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_IMPL_H_ diff --git a/modules/media_file/source/media_file_utility.cc b/modules/media_file/source/media_file_utility.cc deleted file mode 100644 index 18d6b3dc2..000000000 --- a/modules/media_file/source/media_file_utility.cc +++ /dev/null @@ -1,2659 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include "common_types.h" -#include "engine_configurations.h" -#include "file_wrapper.h" -#include "media_file_utility.h" -#include "module_common_types.h" -#include "trace.h" - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - #include "avi_file.h" -#endif - -#if (defined(WIN32) || defined(WINCE)) - #define STR_CASE_CMP _stricmp - #define STR_NCASE_CMP _strnicmp -#else - #define STR_CASE_CMP strcasecmp - #define STR_NCASE_CMP strncasecmp -#endif - -namespace { -enum WaveFormats -{ - kWaveFormatPcm = 0x0001, - kWaveFormatALaw = 0x0006, - kWaveFormatMuLaw = 0x0007 -}; - -// First 16 bytes the WAVE header. ckID should be "RIFF", wave_ckID should be -// "WAVE" and ckSize is the chunk size (4 + n) -struct WAVE_RIFF_header -{ - WebRtc_Word8 ckID[4]; - WebRtc_Word32 ckSize; - WebRtc_Word8 wave_ckID[4]; -}; - -// First 8 byte of the format chunk. fmt_ckID should be "fmt ". fmt_ckSize is -// the chunk size (16, 18 or 40 byte) -struct WAVE_CHUNK_header -{ - WebRtc_Word8 fmt_ckID[4]; - WebRtc_Word32 fmt_ckSize; -}; -} // unnamed namespace - -namespace webrtc { -ModuleFileUtility::ModuleFileUtility(const WebRtc_Word32 id) - : _wavFormatObj(), - _dataSize(0), - _readSizeBytes(0), - _id(id), - _stopPointInMs(0), - _startPointInMs(0), - _playoutPositionMs(0), - _bytesWritten(0), - codec_info_(), - _codecId(kCodecNoCodec), - _bytesPerSample(0), - _readPos(0), - _reading(false), - _writing(false), - _tempData() -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - , - _aviAudioInFile(0), - _aviVideoInFile(0), - _aviOutFile(0) -#endif -{ - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "ModuleFileUtility::ModuleFileUtility()"); - memset(&codec_info_,0,sizeof(CodecInst)); - codec_info_.pltype = -1; -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - memset(&_videoCodec,0,sizeof(_videoCodec)); -#endif -} - -ModuleFileUtility::~ModuleFileUtility() -{ - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "ModuleFileUtility::~ModuleFileUtility()"); -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - delete _aviAudioInFile; - delete _aviVideoInFile; -#endif -} - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -WebRtc_Word32 ModuleFileUtility::InitAviWriting( - const WebRtc_Word8* filename, - const CodecInst& audioCodecInst, - const VideoCodec& videoCodecInst, - const bool videoOnly /*= false*/) -{ - _writing = false; - - delete _aviOutFile; - _aviOutFile = new AviFile( ); - - AVISTREAMHEADER videoStreamHeader; - videoStreamHeader.fccType = AviFile::MakeFourCc('v', 'i', 'd', 's'); - -#ifdef VIDEOCODEC_I420 - if (strncmp(videoCodecInst.plName, "I420", 7) == 0) - { - videoStreamHeader.fccHandler = AviFile::MakeFourCc('I','4','2','0'); - } -#endif -#ifdef VIDEOCODEC_VP8 - if (strncmp(videoCodecInst.plName, "VP8", 7) == 0) - { - videoStreamHeader.fccHandler = AviFile::MakeFourCc('V','P','8','0'); - } -#endif - if (videoStreamHeader.fccHandler == 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "InitAviWriting() Codec not supported"); - - return -1; - } - videoStreamHeader.dwScale = 1; - videoStreamHeader.dwRate = videoCodecInst.maxFramerate; - videoStreamHeader.dwSuggestedBufferSize = videoCodecInst.height * - (videoCodecInst.width >> 1) * 3; - videoStreamHeader.dwQuality = (WebRtc_UWord32)-1; - videoStreamHeader.dwSampleSize = 0; - videoStreamHeader.rcFrame.top = 0; - videoStreamHeader.rcFrame.bottom = videoCodecInst.height; - videoStreamHeader.rcFrame.left = 0; - videoStreamHeader.rcFrame.right = videoCodecInst.width; - - BITMAPINFOHEADER bitMapInfoHeader; - bitMapInfoHeader.biSize = sizeof(BITMAPINFOHEADER); - bitMapInfoHeader.biHeight = videoCodecInst.height; - bitMapInfoHeader.biWidth = videoCodecInst.width; - bitMapInfoHeader.biPlanes = 1; - bitMapInfoHeader.biBitCount = 12; - bitMapInfoHeader.biClrImportant = 0; - bitMapInfoHeader.biClrUsed = 0; - bitMapInfoHeader.biCompression = videoStreamHeader.fccHandler; - bitMapInfoHeader.biSizeImage = bitMapInfoHeader.biWidth * - bitMapInfoHeader.biHeight * bitMapInfoHeader.biBitCount / 8; - - if(videoCodecInst.codecType == kVideoCodecMPEG4) - { - if(_aviOutFile->CreateVideoStream( - videoStreamHeader, - bitMapInfoHeader, - videoCodecInst.codecSpecific.MPEG4.configParameters, - videoCodecInst.codecSpecific.MPEG4.configParametersSize) != 0) - { - return -1; - } - } else - { - if(_aviOutFile->CreateVideoStream( - videoStreamHeader, - bitMapInfoHeader, - NULL, - 0) != 0) - { - return -1; - } - } - - if(!videoOnly) - { - AVISTREAMHEADER audioStreamHeader; - audioStreamHeader.fccType = AviFile::MakeFourCc('a', 'u', 'd', 's'); - // fccHandler is the FOURCC of the codec for decoding the stream. - // It's an optional parameter that is not used by audio streams. - audioStreamHeader.fccHandler = 0; - audioStreamHeader.dwScale = 1; - - WAVEFORMATEX waveFormatHeader; - waveFormatHeader.cbSize = 0; - waveFormatHeader.nChannels = 1; - - if (strncmp(audioCodecInst.plname, "PCMU", 4) == 0) - { - audioStreamHeader.dwSampleSize = 1; - audioStreamHeader.dwRate = 8000; - audioStreamHeader.dwQuality = (WebRtc_UWord32)-1; - audioStreamHeader.dwSuggestedBufferSize = 80; - - waveFormatHeader.nAvgBytesPerSec = 8000; - waveFormatHeader.nSamplesPerSec = 8000; - waveFormatHeader.wBitsPerSample = 8; - waveFormatHeader.nBlockAlign = 1; - waveFormatHeader.wFormatTag = kWaveFormatMuLaw; - - } else if (strncmp(audioCodecInst.plname, "PCMA", 4) == 0) - { - audioStreamHeader.dwSampleSize = 1; - audioStreamHeader.dwRate = 8000; - audioStreamHeader.dwQuality = (WebRtc_UWord32)-1; - audioStreamHeader.dwSuggestedBufferSize = 80; - - waveFormatHeader.nAvgBytesPerSec = 8000; - waveFormatHeader.nSamplesPerSec = 8000; - waveFormatHeader.wBitsPerSample = 8; - waveFormatHeader.nBlockAlign = 1; - waveFormatHeader.wFormatTag = kWaveFormatALaw; - - } else if (strncmp(audioCodecInst.plname, "L16", 3) == 0) - { - audioStreamHeader.dwSampleSize = 2; - audioStreamHeader.dwRate = audioCodecInst.plfreq; - audioStreamHeader.dwQuality = (WebRtc_UWord32)-1; - audioStreamHeader.dwSuggestedBufferSize = - (audioCodecInst.plfreq/100) * 2; - - waveFormatHeader.nAvgBytesPerSec = audioCodecInst.plfreq * 2; - waveFormatHeader.nSamplesPerSec = audioCodecInst.plfreq; - waveFormatHeader.wBitsPerSample = 16; - waveFormatHeader.nBlockAlign = 2; - waveFormatHeader.wFormatTag = kWaveFormatPcm; - } else - { - return -1; - } - - if(_aviOutFile->CreateAudioStream( - audioStreamHeader, - waveFormatHeader) != 0) - { - return -1; - } - - - if( InitWavCodec(waveFormatHeader.nSamplesPerSec, - waveFormatHeader.nChannels, - waveFormatHeader.wBitsPerSample, - waveFormatHeader.wFormatTag) != 0) - { - return -1; - } - } - _aviOutFile->Create(filename); - _writing = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::WriteAviAudioData( - const WebRtc_Word8* buffer, - WebRtc_UWord32 bufferLengthInBytes) -{ - if( _aviOutFile != 0) - { - return _aviOutFile->WriteAudio( - reinterpret_cast(buffer), - bufferLengthInBytes); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "AVI file not initialized"); - return -1; - } -} - -WebRtc_Word32 ModuleFileUtility::WriteAviVideoData( - const WebRtc_Word8* buffer, - WebRtc_UWord32 bufferLengthInBytes) -{ - if( _aviOutFile != 0) - { - return _aviOutFile->WriteVideo( - reinterpret_cast(buffer), - bufferLengthInBytes); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "AVI file not initialized"); - return -1; - } -} - - -WebRtc_Word32 ModuleFileUtility::CloseAviFile( ) -{ - if( _reading && _aviAudioInFile) - { - delete _aviAudioInFile; - _aviAudioInFile = 0; - } - - if( _reading && _aviVideoInFile) - { - delete _aviVideoInFile; - _aviVideoInFile = 0; - } - - if( _writing && _aviOutFile) - { - delete _aviOutFile; - _aviOutFile = 0; - } - return 0; -} - - -WebRtc_Word32 ModuleFileUtility::InitAviReading(const WebRtc_Word8* filename, - bool videoOnly, bool loop) -{ - _reading = false; - delete _aviVideoInFile; - _aviVideoInFile = new AviFile( ); - - if ((_aviVideoInFile != 0) && _aviVideoInFile->Open(AviFile::AVI_VIDEO, - filename, loop) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Unable to open AVI file (video)"); - return -1; - } - - - AVISTREAMHEADER videoInStreamHeader; - BITMAPINFOHEADER bitmapInfo; - char codecConfigParameters[AviFile::CODEC_CONFIG_LENGTH] = {}; - WebRtc_Word32 configLength = 0; - if( _aviVideoInFile->GetVideoStreamInfo(videoInStreamHeader, bitmapInfo, - codecConfigParameters, - configLength) != 0) - { - return -1; - } - _videoCodec.width = static_cast( - videoInStreamHeader.rcFrame.right); - _videoCodec.height = static_cast( - videoInStreamHeader.rcFrame.bottom); - _videoCodec.maxFramerate = static_cast( - videoInStreamHeader.dwRate); - - const size_t plnameLen = sizeof(_videoCodec.plName) / sizeof(char); - if (bitmapInfo.biCompression == AviFile::MakeFourCc('M','4','S','2')) - { - strncpy(_videoCodec.plName, "MP4V-ES", plnameLen); - if (configLength > 0) - { - if (configLength < kConfigParameterSize) - { - _videoCodec.codecSpecific.MPEG4.configParametersSize = - (WebRtc_UWord8)configLength; - memcpy(_videoCodec.codecSpecific.MPEG4.configParameters, - &codecConfigParameters, - _videoCodec.codecSpecific.MPEG4.configParametersSize); - } - else - { - return -1; - } - } - } - else if (bitmapInfo.biCompression == AviFile::MakeFourCc('I','4','2','0')) - { - strncpy(_videoCodec.plName, "I420", plnameLen); - _videoCodec.codecType = kVideoCodecI420; - } - else if (bitmapInfo.biCompression == AviFile::MakeFourCc('H','2','6','3')) - { - strncpy(_videoCodec.plName, "H263", plnameLen); - _videoCodec.codecType = kVideoCodecH263; - } - else if (bitmapInfo.biCompression == - AviFile::MakeFourCc('V', 'P', '8', '0')) - { - strncpy(_videoCodec.plName, "VP8", plnameLen); - _videoCodec.codecType = kVideoCodecVP8; - } - else - { - return -1; - } - - if(!videoOnly) - { - delete _aviAudioInFile; - _aviAudioInFile = new AviFile(); - - if ( (_aviAudioInFile != 0) && - _aviAudioInFile->Open(AviFile::AVI_AUDIO, filename, loop) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Unable to open AVI file (audio)"); - return -1; - } - - WAVEFORMATEX waveHeader; - if(_aviAudioInFile->GetAudioStreamInfo(waveHeader) != 0) - { - return -1; - } - if(InitWavCodec(waveHeader.nSamplesPerSec, waveHeader.nChannels, - waveHeader.wBitsPerSample, waveHeader.wFormatTag) != 0) - { - return -1; - } - } - _reading = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::ReadAviAudioData( - WebRtc_Word8* outBuffer, - const WebRtc_UWord32 bufferLengthInBytes) -{ - if(_aviAudioInFile == 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "AVI file not opened."); - return -1; - } - - WebRtc_Word32 length = bufferLengthInBytes; - if(_aviAudioInFile->ReadAudio( - reinterpret_cast(outBuffer), - length) != 0) - { - return -1; - } - else - { - return length; - } -} - -WebRtc_Word32 ModuleFileUtility::ReadAviVideoData( - WebRtc_Word8* outBuffer, - const WebRtc_UWord32 bufferLengthInBytes) -{ - if(_aviVideoInFile == 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "AVI file not opened."); - return -1; - } - - WebRtc_Word32 length = bufferLengthInBytes; - if( _aviVideoInFile->ReadVideo( - reinterpret_cast(outBuffer), - length) != 0) - { - return -1; - } else { - return length; - } -} - -WebRtc_Word32 ModuleFileUtility::VideoCodecInst(VideoCodec& codecInst) -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "ModuleFileUtility::CodecInst(codecInst= 0x%x)", &codecInst); - - if(!_reading) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "CodecInst: not currently reading audio file!"); - return -1; - } - memcpy(&codecInst,&_videoCodec,sizeof(VideoCodec)); - return 0; -} -#endif - -WebRtc_Word32 ModuleFileUtility::ReadWavHeader(InStream& wav) -{ - WAVE_RIFF_header RIFFheaderObj; - WAVE_CHUNK_header CHUNKheaderObj; - // TODO (hellner): tmpStr and tmpStr2 seems unnecessary here. - WebRtc_Word8 tmpStr[6] = "FOUR"; - WebRtc_UWord8 tmpStr2[4]; - WebRtc_Word32 i, len; - bool dataFound = false; - bool fmtFound = false; - WebRtc_Word8 dummyRead; - - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "ModuleFileUtility::ReadWavHeader(wav= 0x%x)", &wav); - - _dataSize = 0; - len = wav.Read(&RIFFheaderObj, sizeof(WAVE_RIFF_header)); - if(len != sizeof(WAVE_RIFF_header)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a wave file (too short)"); - return -1; - } - - for (i = 0; i < 4; i++) - { - tmpStr[i] = RIFFheaderObj.ckID[i]; - } - if(strcmp(tmpStr, "RIFF") != 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a wave file (does not have RIFF)"); - return -1; - } - for (i = 0; i < 4; i++) - { - tmpStr[i] = RIFFheaderObj.wave_ckID[i]; - } - if(strcmp(tmpStr, "WAVE") != 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Not a wave file (does not have WAVE)"); - return -1; - } - - len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header)); - - // WAVE files are stored in little endian byte order. Make sure that the - // data can be read on big endian as well. - // TODO (hellner): little endian to system byte order should be done in - // in a subroutine. - memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4); - CHUNKheaderObj.fmt_ckSize = - (WebRtc_Word32) ((WebRtc_UWord32) tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8) + - (((WebRtc_UWord32)tmpStr2[2])<<16) + - (((WebRtc_UWord32)tmpStr2[3])<<24)); - - memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4); - - while ((len == sizeof(WAVE_CHUNK_header)) && (!fmtFound || !dataFound)) - { - if(strcmp(tmpStr, "fmt ") == 0) - { - len = wav.Read(&_wavFormatObj, sizeof(WAVE_FMTINFO_header)); - - memcpy(tmpStr2, &_wavFormatObj.formatTag, 2); - _wavFormatObj.formatTag = - (WaveFormats) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8)); - memcpy(tmpStr2, &_wavFormatObj.nChannels, 2); - _wavFormatObj.nChannels = - (WebRtc_Word16) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8)); - memcpy(tmpStr2, &_wavFormatObj.nSamplesPerSec, 4); - _wavFormatObj.nSamplesPerSec = - (WebRtc_Word32) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8) + - (((WebRtc_UWord32)tmpStr2[2])<<16) + - (((WebRtc_UWord32)tmpStr2[3])<<24)); - memcpy(tmpStr2, &_wavFormatObj.nAvgBytesPerSec, 4); - _wavFormatObj.nAvgBytesPerSec = - (WebRtc_Word32) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8) + - (((WebRtc_UWord32)tmpStr2[2])<<16) + - (((WebRtc_UWord32)tmpStr2[3])<<24)); - memcpy(tmpStr2, &_wavFormatObj.nBlockAlign, 2); - _wavFormatObj.nBlockAlign = - (WebRtc_Word16) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8)); - memcpy(tmpStr2, &_wavFormatObj.nBitsPerSample, 2); - _wavFormatObj.nBitsPerSample = - (WebRtc_Word16) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8)); - - for (i = 0; - i < (CHUNKheaderObj.fmt_ckSize - - (WebRtc_Word32)sizeof(WAVE_FMTINFO_header)); - i++) - { - len = wav.Read(&dummyRead, 1); - if(len != 1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "File corrupted, reached EOF (reading fmt)"); - return -1; - } - } - fmtFound = true; - } - else if(strcmp(tmpStr, "data") == 0) - { - _dataSize = CHUNKheaderObj.fmt_ckSize; - dataFound = true; - break; - } - else - { - for (i = 0; i < (CHUNKheaderObj.fmt_ckSize); i++) - { - len = wav.Read(&dummyRead, 1); - if(len != 1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "File corrupted, reached EOF (reading other)"); - return -1; - } - } - } - - len = wav.Read(&CHUNKheaderObj, sizeof(WAVE_CHUNK_header)); - - memcpy(tmpStr2, &CHUNKheaderObj.fmt_ckSize, 4); - CHUNKheaderObj.fmt_ckSize = - (WebRtc_Word32) ((WebRtc_UWord32)tmpStr2[0] + - (((WebRtc_UWord32)tmpStr2[1])<<8) + - (((WebRtc_UWord32)tmpStr2[2])<<16) + - (((WebRtc_UWord32)tmpStr2[3])<<24)); - - memcpy(tmpStr, CHUNKheaderObj.fmt_ckID, 4); - } - - // Either a proper format chunk has been read or a data chunk was come - // across. - if( (_wavFormatObj.formatTag != kWaveFormatPcm) && - (_wavFormatObj.formatTag != kWaveFormatALaw) && - (_wavFormatObj.formatTag != kWaveFormatMuLaw)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Coding formatTag value=%d not supported!", - _wavFormatObj.formatTag); - return -1; - } - if((_wavFormatObj.nChannels < 1) || - (_wavFormatObj.nChannels > 2)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "nChannels value=%d not supported!", - _wavFormatObj.nChannels); - return -1; - } - - if((_wavFormatObj.nBitsPerSample != 8) && - (_wavFormatObj.nBitsPerSample != 16)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "nBitsPerSample value=%d not supported!", - _wavFormatObj.nBitsPerSample); - return -1; - } - - // Calculate the number of bytes that 10 ms of audio data correspond to. - if(_wavFormatObj.formatTag == kWaveFormatPcm) - { - // TODO (hellner): integer division for 22050 and 11025 would yield - // the same result as the else statement. Remove those - // special cases? - if(_wavFormatObj.nSamplesPerSec == 44100) - { - _readSizeBytes = 440 * _wavFormatObj.nChannels * - (_wavFormatObj.nBitsPerSample / 8); - } else if(_wavFormatObj.nSamplesPerSec == 22050) { - _readSizeBytes = 220 * _wavFormatObj.nChannels * - (_wavFormatObj.nBitsPerSample / 8); - } else if(_wavFormatObj.nSamplesPerSec == 11025) { - _readSizeBytes = 110 * _wavFormatObj.nChannels * - (_wavFormatObj.nBitsPerSample / 8); - } else { - _readSizeBytes = (_wavFormatObj.nSamplesPerSec/100) * - _wavFormatObj.nChannels * (_wavFormatObj.nBitsPerSample / 8); - } - - } else { - _readSizeBytes = (_wavFormatObj.nSamplesPerSec/100) * - _wavFormatObj.nChannels * (_wavFormatObj.nBitsPerSample / 8); - } - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "ModuleFileUtility::ReadWavHeader: format=PCM %d KHz, sampleSize=%d,\ - nChannels=%d, readSize=%d, dataSize=%d, rate=%d", - _wavFormatObj.nSamplesPerSec/1000, - _wavFormatObj.nBitsPerSample, - _wavFormatObj.nChannels, - _readSizeBytes, - _dataSize, - _wavFormatObj.nAvgBytesPerSec * 8); - return 0; -} - -WebRtc_Word32 ModuleFileUtility::InitWavCodec(WebRtc_UWord32 samplesPerSec, - WebRtc_UWord32 channels, - WebRtc_UWord32 bitsPerSample, - WebRtc_UWord32 formatTag) -{ - codec_info_.pltype = -1; - codec_info_.plfreq = samplesPerSec; - codec_info_.channels = channels; - codec_info_.rate = bitsPerSample * samplesPerSec; - - // Calculate the packet size for 10ms frames - switch(formatTag) - { - case kWaveFormatALaw: - strcpy(codec_info_.plname, "PCMA"); - _codecId = kCodecPcma; - codec_info_.pltype = 8; - codec_info_.pacsize = codec_info_.plfreq / 100; - break; - case kWaveFormatMuLaw: - strcpy(codec_info_.plname, "PCMU"); - _codecId = kCodecPcmu; - codec_info_.pltype = 0; - codec_info_.pacsize = codec_info_.plfreq / 100; - break; - case kWaveFormatPcm: - codec_info_.pacsize = (bitsPerSample * (codec_info_.plfreq / 100)) / 8; - if(samplesPerSec == 8000) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_8Khz; - } - else if(samplesPerSec == 16000) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_16kHz; - } - else if(samplesPerSec == 32000) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_32Khz; - } - // Set the packet size for "odd" sampling frequencies so that it - // properly corresponds to _readSizeBytes. - else if(samplesPerSec == 11025) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_16kHz; - codec_info_.pacsize = 110; - codec_info_.plfreq = 11000; - } - else if(samplesPerSec == 22050) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_16kHz; - codec_info_.pacsize = 220; - codec_info_.plfreq = 22000; - } - else if(samplesPerSec == 44100) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_16kHz; - codec_info_.pacsize = 440; - codec_info_.plfreq = 44000; - } - else if(samplesPerSec == 48000) - { - strcpy(codec_info_.plname, "L16"); - _codecId = kCodecL16_16kHz; - codec_info_.pacsize = 480; - codec_info_.plfreq = 48000; - } - else - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Unsupported PCM frequency!"); - return -1; - } - break; - default: - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "unknown WAV format TAG!"); - return -1; - break; - } - return 0; -} - -WebRtc_Word32 ModuleFileUtility::InitWavReading(InStream& wav, - const WebRtc_UWord32 start, - const WebRtc_UWord32 stop) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "ModuleFileUtility::InitWavReading(wav= 0x%x, start= %d, stop=%d)", - &wav, - start, - stop); - - _reading = false; - - if(ReadWavHeader(wav) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "failed to read WAV header!"); - return -1; - } - - _playoutPositionMs = 0; - _readPos = 0; - - if(start > 0) - { - WebRtc_UWord8 dummy[WAV_MAX_BUFFER_SIZE]; - WebRtc_Word32 readLength; - if(_readSizeBytes <= WAV_MAX_BUFFER_SIZE) - { - while (_playoutPositionMs < start) - { - readLength = wav.Read(dummy, _readSizeBytes); - if(readLength == _readSizeBytes) - { - _readPos += readLength; - _playoutPositionMs += 10; - } - else // Must have reached EOF before start position! - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "InitWavReading(), EOF before start position"); - return -1; - } - } - } - else - { - return -1; - } - } - if( InitWavCodec(_wavFormatObj.nSamplesPerSec, _wavFormatObj.nChannels, - _wavFormatObj.nBitsPerSample, - _wavFormatObj.formatTag) != 0) - { - return -1; - } - _bytesPerSample = _wavFormatObj.nBitsPerSample / 8; - - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "WAV header: codecName= %s, sampleSize= %d, freq= %d", - codec_info_.plname, _bytesPerSample, codec_info_.plfreq); - - _startPointInMs = start; - _stopPointInMs = stop; - _reading = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::ReadWavDataAsMono( - InStream& wav, - WebRtc_Word8* outData, - const WebRtc_UWord32 bufferSize) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadWavDataAsMono(wav= 0x%x, outData= 0x%d,\ - bufSize= %ld)", - &wav, - outData, - bufferSize); - - // The number of bytes that should be read from file. - const WebRtc_UWord32 totalBytesNeeded = _readSizeBytes; - // The number of bytes that will be written to outData. - const WebRtc_UWord32 bytesRequested = (codec_info_.channels == 2) ? - totalBytesNeeded >> 1 : totalBytesNeeded; - if(bufferSize < bytesRequested) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: output buffer is too short!"); - return -1; - } - if(outData == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: output buffer NULL!"); - return -1; - } - - if(!_reading) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: no longer reading file."); - return -1; - } - - WebRtc_Word32 bytesRead = ReadWavData( - wav, - (codec_info_.channels == 2) ? _tempData : (WebRtc_UWord8*)outData, - totalBytesNeeded); - if(bytesRead == 0) - { - return 0; - } - if(bytesRead < 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: failed to read data from WAV file."); - return -1; - } - // Output data is should be mono. - if(codec_info_.channels == 2) - { - for (WebRtc_UWord32 i = 0; i < bytesRequested; i++) - { - // Sample value is the average of left and right buffer rounded to - // closest integer value. Note samples can be either 1 or 2 byte. - if(_bytesPerSample == 1) - { - _tempData[i] = ((_tempData[2 * i] + _tempData[(2 * i) + 1] + - 1) >> 1); - } - else - { - WebRtc_Word16* sampleData = (WebRtc_Word16*) _tempData; - sampleData[i] = ((sampleData[2 * i] + sampleData[(2 * i) + 1] + - 1) >> 1); - } - } - memcpy(outData, _tempData, bytesRequested); - } - return bytesRequested; -} - -WebRtc_Word32 ModuleFileUtility::ReadWavDataAsStereo( - InStream& wav, - WebRtc_Word8* outDataLeft, - WebRtc_Word8* outDataRight, - const WebRtc_UWord32 bufferSize) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadWavDataAsStereo(wav= 0x%x, outLeft= 0x%x,\ - outRight= 0x%x, bufSize= %ld)", - &wav, - outDataLeft, - outDataRight, - bufferSize); - - if((outDataLeft == NULL) || - (outDataRight == NULL)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: an input buffer is NULL!"); - return -1; - } - if(codec_info_.channels != 2) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "ReadWavDataAsStereo: WAV file does not contain stereo data!"); - return -1; - } - if(! _reading) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsStereo: no longer reading file."); - return -1; - } - - // The number of bytes that should be read from file. - const WebRtc_UWord32 totalBytesNeeded = _readSizeBytes; - // The number of bytes that will be written to the left and the right - // buffers. - const WebRtc_UWord32 bytesRequested = totalBytesNeeded >> 1; - if(bufferSize < bytesRequested) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavData: Output buffers are too short!"); - assert(false); - return -1; - } - - WebRtc_Word32 bytesRead = ReadWavData(wav, _tempData, totalBytesNeeded); - if(bytesRead <= 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsStereo: failed to read data from WAV file."); - return -1; - } - - // Turn interleaved audio to left and right buffer. Note samples can be - // either 1 or 2 bytes - if(_bytesPerSample == 1) - { - for (WebRtc_UWord32 i = 0; i < bytesRequested; i++) - { - outDataLeft[i] = _tempData[2 * i]; - outDataRight[i] = _tempData[(2 * i) + 1]; - } - } - else if(_bytesPerSample == 2) - { - WebRtc_Word16* sampleData = reinterpret_cast(_tempData); - WebRtc_Word16* outLeft = reinterpret_cast(outDataLeft); - WebRtc_Word16* outRight = reinterpret_cast( - outDataRight); - - // Bytes requested to samples requested. - WebRtc_UWord32 sampleCount = bytesRequested >> 1; - for (WebRtc_UWord32 i = 0; i < sampleCount; i++) - { - outLeft[i] = sampleData[2 * i]; - outRight[i] = sampleData[(2 * i) + 1]; - } - } else { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavStereoData: unsupported sample size %d!", - _bytesPerSample); - assert(false); - return -1; - } - return bytesRequested; -} - -WebRtc_Word32 ModuleFileUtility::ReadWavData( - InStream& wav, - WebRtc_UWord8* buffer, - const WebRtc_UWord32 dataLengthInBytes) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadWavData(wav= 0x%x, buffer= 0x%x, dataLen= %ld)", - &wav, - buffer, - dataLengthInBytes); - - - if(buffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadWavDataAsMono: output buffer NULL!"); - return -1; - } - - // Make sure that a read won't return too few samples. - // TODO (hellner): why not read the remaining bytes needed from the start - // of the file? - if((_dataSize - _readPos) < (WebRtc_Word32)dataLengthInBytes) - { - // Rewind() being -1 may be due to the file not supposed to be looped. - if(wav.Rewind() == -1) - { - _reading = false; - return 0; - } - if(InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1) - { - _reading = false; - return -1; - } - } - - WebRtc_Word32 bytesRead = wav.Read(buffer, dataLengthInBytes); - if(bytesRead < 0) - { - _reading = false; - return -1; - } - - // This should never happen due to earlier sanity checks. - // TODO (hellner): change to an assert and fail here since this should - // never happen... - if(bytesRead < (WebRtc_Word32)dataLengthInBytes) - { - if((wav.Rewind() == -1) || - (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1)) - { - _reading = false; - return -1; - } - else - { - bytesRead = wav.Read(buffer, dataLengthInBytes); - if(bytesRead < (WebRtc_Word32)dataLengthInBytes) - { - _reading = false; - return -1; - } - } - } - - _readPos += bytesRead; - - // TODO (hellner): Why is dataLengthInBytes let dictate the number of bytes - // to read when exactly 10ms should be read?! - _playoutPositionMs += 10; - if((_stopPointInMs > 0) && - (_playoutPositionMs >= _stopPointInMs)) - { - if((wav.Rewind() == -1) || - (InitWavReading(wav, _startPointInMs, _stopPointInMs) == -1)) - { - _reading = false; - } - } - return bytesRead; -} - -WebRtc_Word32 ModuleFileUtility::InitWavWriting(OutStream& wav, - const CodecInst& codecInst) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "ModuleFileUtility::InitWavWriting(wav= 0x%x, codec=%s)", - &wav, codecInst.plname); - - if(set_codec_info(codecInst) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "codecInst identifies unsupported codec!"); - return -1; - } - _writing = false; - WebRtc_UWord32 channels = (codecInst.channels == 0) ? - 1 : codecInst.channels; - - if(STR_CASE_CMP(codecInst.plname, "PCMU") == 0) - { - _bytesPerSample = 1; - if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, - kWaveFormatMuLaw, 0) == -1) - { - return -1; - } - }else if(STR_CASE_CMP(codecInst.plname, "PCMA") == 0) - { - _bytesPerSample = 1; - if(WriteWavHeader(wav, 8000, _bytesPerSample, channels, kWaveFormatALaw, - 0) == -1) - { - return -1; - } - } - else if(STR_CASE_CMP(codecInst.plname, "L16") == 0) - { - _bytesPerSample = 2; - if(WriteWavHeader(wav, codecInst.plfreq, _bytesPerSample, channels, - kWaveFormatPcm, 0) == -1) - { - return -1; - } - } - else - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "codecInst identifies unsupported codec for WAV file!"); - return -1; - } - _writing = true; - _bytesWritten = 0; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::WriteWavData(OutStream& out, - const WebRtc_Word8* buffer, - const WebRtc_UWord32 dataLength) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::WriteWavData(out= 0x%x, buf= 0x%x, dataLen= %d)", - &out, - buffer, - dataLength); - - if(buffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "WriteWavData: input buffer NULL!"); - return -1; - } - - if(!out.Write(buffer, dataLength)) - { - return -1; - } - _bytesWritten += dataLength; - return dataLength; -} - - -WebRtc_Word32 ModuleFileUtility::WriteWavHeader( - OutStream& wav, - const WebRtc_UWord32 freq, - const WebRtc_UWord32 bytesPerSample, - const WebRtc_UWord32 channels, - const WebRtc_UWord32 format, - const WebRtc_UWord32 lengthInBytes) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "ModuleFileUtility::WriteWavHeader(format= PCM %d KHz,\ - bytesPerSample= %d, channels= %d, format= %d, dataLength= %d)", - freq / 1000, - bytesPerSample, - channels, - format, - lengthInBytes); - - // Frame size in bytes for 10 ms of audio. - // TODO (hellner): 44.1 kHz has 440 samples frame size. Doesn't seem to - // be taken into consideration here! - WebRtc_Word32 frameSize = (freq / 100) * bytesPerSample * channels; - - // Calculate the number of full frames that the wave file contain. - const WebRtc_Word32 dataLengthInBytes = frameSize * - (lengthInBytes / frameSize); - - WebRtc_Word8 tmpStr[4]; - WebRtc_Word8 tmpChar; - WebRtc_UWord32 tmpLong; - - memcpy(tmpStr, "RIFF", 4); - wav.Write(tmpStr, 4); - - tmpLong = dataLengthInBytes + 36; - tmpChar = (WebRtc_Word8)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - memcpy(tmpStr, "WAVE", 4); - wav.Write(tmpStr, 4); - - memcpy(tmpStr, "fmt ", 4); - wav.Write(tmpStr, 4); - - tmpChar = 16; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (WebRtc_Word8)(format); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (WebRtc_Word8)(channels); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpLong = freq; - tmpChar = (WebRtc_Word8)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - // nAverageBytesPerSec = Sample rate * Bytes per sample * Channels - tmpLong = bytesPerSample * freq * channels; - tmpChar = (WebRtc_Word8)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - // nBlockAlign = Bytes per sample * Channels - tmpChar = (WebRtc_Word8)(bytesPerSample * channels); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - tmpChar = (WebRtc_Word8)(bytesPerSample*8); - wav.Write(&tmpChar, 1); - tmpChar = 0; - wav.Write(&tmpChar, 1); - - memcpy(tmpStr, "data", 4); - wav.Write(tmpStr, 4); - - tmpLong = dataLengthInBytes; - tmpChar = (WebRtc_Word8)(tmpLong); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 8); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 16); - wav.Write(&tmpChar, 1); - tmpChar = (WebRtc_Word8)(tmpLong >> 24); - wav.Write(&tmpChar, 1); - - return 0; -} - -WebRtc_Word32 ModuleFileUtility::UpdateWavHeader(OutStream& wav) -{ - WebRtc_Word32 res = -1; - if(wav.Rewind() == -1) - { - return -1; - } - WebRtc_UWord32 channels = (codec_info_.channels == 0) ? - 1 : codec_info_.channels; - - if(STR_CASE_CMP(codec_info_.plname, "L16") == 0) - { - res = WriteWavHeader(wav, codec_info_.plfreq, 2, channels, - kWaveFormatPcm, _bytesWritten); - } else if(STR_CASE_CMP(codec_info_.plname, "PCMU") == 0) { - res = WriteWavHeader(wav, 8000, 1, channels, kWaveFormatMuLaw, - _bytesWritten); - } else if(STR_CASE_CMP(codec_info_.plname, "PCMU") == 0) { - res = WriteWavHeader(wav, 8000, 1, channels, kWaveFormatALaw, - _bytesWritten); - } else { - // Allow calling this API even if not writing to a WAVE file. - // TODO (hellner): why?! - return 0; - } - return res; -} - - -WebRtc_Word32 ModuleFileUtility::InitPreEncodedReading(InStream& in, - const CodecInst& cinst) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "ModuleFileUtility::InitPreEncodedReading(in=0x%x, codec='%s')", - &in, cinst.plname); - - WebRtc_UWord8 preEncodedID; - in.Read(&preEncodedID, 1); - - MediaFileUtility_CodecType codecType = - (MediaFileUtility_CodecType)preEncodedID; - - if(set_codec_info(cinst) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Pre-encoded file send codec mismatch!"); - return -1; - } - if(codecType != _codecId) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "Pre-encoded file format codec mismatch!"); - return -1; - } - memcpy(&codec_info_,&cinst,sizeof(CodecInst)); - _reading = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::ReadPreEncodedData( - InStream& in, - WebRtc_Word8* outData, - const WebRtc_UWord32 bufferSize) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadPreEncodedData(in= 0x%x, outData= 0x%x,\ - bufferSize= %d)", - &in, - outData, - bufferSize); - - if(outData == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "output buffer NULL"); - } - - WebRtc_UWord32 frameLen; - WebRtc_UWord8 buf[64]; - // Each frame has a two byte header containing the frame length. - WebRtc_Word32 res = in.Read(buf, 2); - if(res != 2) - { - if(!in.Rewind()) - { - // The first byte is the codec identifier. - in.Read(buf, 1); - res = in.Read(buf, 2); - } - else - { - return -1; - } - } - frameLen = buf[0] + buf[1] * 256; - if(bufferSize < frameLen) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "buffer not large enough to read %d bytes of pre-encoded data!", - frameLen); - return -1; - } - return in.Read(outData, frameLen); -} - -WebRtc_Word32 ModuleFileUtility::InitPreEncodedWriting( - OutStream& out, - const CodecInst& codecInst) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "ModuleFileUtility::InitFormatedWriting(out=0x%x, codecInst= %s)", - &out, - codecInst.plname); - - if(set_codec_info(codecInst) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "CodecInst not recognized!"); - return -1; - } - _writing = true; - _bytesWritten = 1; - out.Write(&_codecId, 1); - return 0; -} - -WebRtc_Word32 ModuleFileUtility::WritePreEncodedData( - OutStream& out, - const WebRtc_Word8* buffer, - const WebRtc_UWord32 dataLength) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::WritePreEncodedData(out= 0x%x, inData= 0x%x,\ - dataLen= %d)", - &out, - buffer, - dataLength); - - if(buffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL"); - } - - WebRtc_Word32 bytesWritten = 0; - // The first two bytes is the size of the frame. - WebRtc_Word16 lengthBuf; - lengthBuf = (WebRtc_Word16)dataLength; - if(!out.Write(&lengthBuf, 2)) - { - return -1; - } - bytesWritten = 2; - - if(!out.Write(buffer, dataLength)) - { - return -1; - } - bytesWritten += dataLength; - return bytesWritten; -} - -WebRtc_Word32 ModuleFileUtility::InitCompressedReading( - InStream& in, - const WebRtc_UWord32 start, - const WebRtc_UWord32 stop) -{ - WEBRTC_TRACE( - kTraceDebug, - kTraceFile, - _id, - "ModuleFileUtility::InitCompressedReading(in= 0x%x, start= %d,\ - stop= %d)", - &in, - start, - stop); - - WebRtc_Word16 read_len = 0; - _codecId = kCodecNoCodec; - _playoutPositionMs = 0; - _reading = false; - - _startPointInMs = start; - _stopPointInMs = stop; - -#ifdef WEBRTC_CODEC_GSMAMR - WebRtc_Word32 AMRmode2bytes[9]={12,13,15,17,19,20,26,31,5}; -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - WebRtc_Word32 AMRWBmode2bytes[10]={17,23,32,36,40,46,50,58,60,6}; -#endif - - // Read the codec name - WebRtc_Word32 cnt = 0; - WebRtc_Word8 buf[64]; - do - { - in.Read(&buf[cnt++], 1); - } while ((buf[cnt-1] != '\n') && (64 > cnt)); - - if(cnt==64) - { - return -1; - } else { - buf[cnt]=0; - } - -#ifdef WEBRTC_CODEC_GSMAMR - if(!strcmp("#!AMR\n", buf)) - { - strcpy(codec_info_.plname, "amr"); - codec_info_.pacsize = 160; - _codecId = kCodecAmr; - codec_info_.pltype = 112; - codec_info_.rate = 12200; - codec_info_.plfreq = 8000; - codec_info_.channels = 1; - - WebRtc_Word16 mode = 0; - if(_startPointInMs > 0) - { - while (_playoutPositionMs <= _startPointInMs) - { - // First read byte contain the AMR mode. - read_len = in.Read(buf, 1); - if(read_len != 1) - { - return -1; - } - - mode = (buf[0]>>3)&0xF; - if((mode < 0) || (mode > 8)) - { - if(mode != 15) - { - return -1; - } - } - if(mode != 15) - { - read_len = in.Read(&buf[1], AMRmode2bytes[mode]); - if(read_len != AMRmode2bytes[mode]) - { - return -1; - } - } - _playoutPositionMs += 20; - } - } - } -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - if(!strcmp("#!AMRWB\n", buf)) - { - strcpy(codec_info_.plname, "amr-wb"); - codec_info_.pacsize = 320; - _codecId = kCodecAmrWb; - codec_info_.pltype = 120; - codec_info_.rate = 20000; - codec_info_.plfreq = 16000; - codec_info_.channels = 1; - - WebRtc_Word16 mode = 0; - if(_startPointInMs > 0) - { - while (_playoutPositionMs <= _startPointInMs) - { - // First read byte contain the AMR mode. - read_len = in.Read(buf, 1); - if(read_len != 1) - { - return -1; - } - - mode = (buf[0]>>3)&0xF; - if((mode < 0) || (mode > 9)) - { - if(mode != 15) - { - return -1; - } - } - if(mode != 15) - { - read_len = in.Read(&buf[1], AMRWBmode2bytes[mode]); - if(read_len != AMRWBmode2bytes[mode]) - { - return -1; - } - } - _playoutPositionMs += 20; - } - } - } -#endif -#ifdef WEBRTC_CODEC_ILBC - if(!strcmp("#!iLBC20\n", buf)) - { - codec_info_.pltype = 102; - strcpy(codec_info_.plname, "ilbc"); - codec_info_.plfreq = 8000; - codec_info_.pacsize = 160; - codec_info_.channels = 1; - codec_info_.rate = 13300; - _codecId = kCodecIlbc20Ms; - - if(_startPointInMs > 0) - { - while (_playoutPositionMs <= _startPointInMs) - { - read_len = in.Read(buf, 38); - if(read_len == 38) - { - _playoutPositionMs += 20; - } - else - { - return -1; - } - } - } - } - - if(!strcmp("#!iLBC30\n", buf)) - { - codec_info_.pltype = 102; - strcpy(codec_info_.plname, "ilbc"); - codec_info_.plfreq = 8000; - codec_info_.pacsize = 240; - codec_info_.channels = 1; - codec_info_.rate = 13300; - _codecId = kCodecIlbc30Ms; - - if(_startPointInMs > 0) - { - while (_playoutPositionMs <= _startPointInMs) - { - read_len = in.Read(buf, 50); - if(read_len == 50) - { - _playoutPositionMs += 20; - } - else - { - return -1; - } - } - } - } -#endif - if(_codecId == kCodecNoCodec) - { - return -1; - } - _reading = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::ReadCompressedData(InStream& in, - WebRtc_Word8* outData, - WebRtc_UWord32 bufferSize) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadCompressedData(in=0x%x, outData=0x%x,\ - bytes=%ld)", - &in, - outData, - bufferSize); - -#ifdef WEBRTC_CODEC_GSMAMR - WebRtc_UWord32 AMRmode2bytes[9]={12,13,15,17,19,20,26,31,5}; -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - WebRtc_UWord32 AMRWBmode2bytes[10]={17,23,32,36,40,46,50,58,60,6}; -#endif - WebRtc_UWord32 bytesRead = 0; - - if(! _reading) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "not currently reading!"); - return -1; - } - -#ifdef WEBRTC_CODEC_GSMAMR - if(_codecId == kCodecAmr) - { - WebRtc_Word32 res = in.Read(outData, 1); - if(res != 1) - { - if(!in.Rewind()) - { - InitCompressedReading(in, _startPointInMs, _stopPointInMs); - res = in.Read(outData, 1); - if(res != 1) - { - _reading = false; - return -1; - } - } - else - { - _reading = false; - return -1; - } - } - const WebRtc_Word16 mode = (outData[0]>>3)&0xF; - if((mode < 0) || - (mode > 8)) - { - if(mode != 15) - { - return -1; - } - } - if(mode != 15) - { - if(bufferSize < AMRmode2bytes[mode] + 1) - { - WEBRTC_TRACE( - kTraceError, - kTraceFile, - _id, - "output buffer is too short to read AMR compressed data."); - assert(false); - return -1; - } - bytesRead = in.Read(&outData[1], AMRmode2bytes[mode]); - if(bytesRead != AMRmode2bytes[mode]) - { - _reading = false; - return -1; - } - // Count the mode byte to bytes read. - bytesRead++; - } - else - { - bytesRead = 1; - } - } -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - if(_codecId == kCodecAmrWb) - { - WebRtc_Word32 res = in.Read(outData, 1); - if(res != 1) - { - if(!in.Rewind()) - { - InitCompressedReading(in, _startPointInMs, _stopPointInMs); - res = in.Read(outData, 1); - if(res != 1) - { - _reading = false; - return -1; - } - } - else - { - _reading = false; - return -1; - } - } - WebRtc_Word16 mode = (outData[0]>>3)&0xF; - if((mode < 0) || - (mode > 8)) - { - if(mode != 15) - { - return -1; - } - } - if(mode != 15) - { - if(bufferSize < AMRWBmode2bytes[mode] + 1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "output buffer is too short to read AMRWB\ - compressed."); - assert(false); - return -1; - } - bytesRead = in.Read(&outData[1], AMRWBmode2bytes[mode]); - if(bytesRead != AMRWBmode2bytes[mode]) - { - _reading = false; - return -1; - } - bytesRead++; - } - else - { - bytesRead = 1; - } - } -#endif -#ifdef WEBRTC_CODEC_ILBC - if((_codecId == kCodecIlbc20Ms) || - (_codecId == kCodecIlbc30Ms)) - { - WebRtc_UWord32 byteSize = 0; - if(_codecId == kCodecIlbc30Ms) - { - byteSize = 50; - } - if(_codecId == kCodecIlbc20Ms) - { - byteSize = 38; - } - if(bufferSize < byteSize) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "output buffer is too short to read ILBC compressed\ - data."); - assert(false); - return -1; - } - - bytesRead = in.Read(outData, byteSize); - if(bytesRead != byteSize) - { - if(!in.Rewind()) - { - InitCompressedReading(in, _startPointInMs, _stopPointInMs); - bytesRead = in.Read(outData, byteSize); - if(bytesRead != byteSize) - { - _reading = false; - return -1; - } - } - else - { - _reading = false; - return -1; - } - } - } -#endif - if(bytesRead == 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadCompressedData() no bytes read, codec not supported"); - return -1; - } - - _playoutPositionMs += 20; - if((_stopPointInMs > 0) && - (_playoutPositionMs >= _stopPointInMs)) - { - if(!in.Rewind()) - { - InitCompressedReading(in, _startPointInMs, _stopPointInMs); - } - else - { - _reading = false; - } - } - return bytesRead; -} - -WebRtc_Word32 ModuleFileUtility::InitCompressedWriting( - OutStream& out, - const CodecInst& codecInst) -{ - WEBRTC_TRACE(kTraceDebug, kTraceFile, _id, - "ModuleFileUtility::InitCompressedWriting(out= 0x%x,\ - codecName= %s)", - &out, codecInst.plname); - - _writing = false; - -#ifdef WEBRTC_CODEC_GSMAMR - if(STR_CASE_CMP(codecInst.plname, "amr") == 0) - { - if(codecInst.pacsize == 160) - { - memcpy(&codec_info_,&codecInst,sizeof(CodecInst)); - _codecId = kCodecAmr; - out.Write("#!AMR\n",6); - _writing = true; - return 0; - } - } -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - if(STR_CASE_CMP(codecInst.plname, "amr-wb") == 0) - { - if(codecInst.pacsize == 320) - { - memcpy(&codec_info_,&codecInst,sizeof(CodecInst)); - _codecId = kCodecAmrWb; - out.Write("#!AMRWB\n",8); - _writing = true; - return 0; - } - } -#endif -#ifdef WEBRTC_CODEC_ILBC - if(STR_CASE_CMP(codecInst.plname, "ilbc") == 0) - { - if(codecInst.pacsize == 160) - { - _codecId = kCodecIlbc20Ms; - out.Write("#!iLBC20\n",9); - } - else if(codecInst.pacsize == 240) - { - _codecId = kCodecIlbc30Ms; - out.Write("#!iLBC30\n",9); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "codecInst defines unsupported compression codec!"); - return -1; - } - memcpy(&codec_info_,&codecInst,sizeof(CodecInst)); - _writing = true; - return 0; - } -#endif - - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "codecInst defines unsupported compression codec!"); - return -1; -} - -WebRtc_Word32 ModuleFileUtility::WriteCompressedData( - OutStream& out, - const WebRtc_Word8* buffer, - const WebRtc_UWord32 dataLength) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::WriteCompressedData(out= 0x%x, buf= 0x%x,\ - dataLen= %d)", - &out, - buffer, - dataLength); - - if(buffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL"); - } - - if(!out.Write(buffer, dataLength)) - { - return -1; - } - return dataLength; -} - -WebRtc_Word32 ModuleFileUtility::InitPCMReading(InStream& pcm, - const WebRtc_UWord32 start, - const WebRtc_UWord32 stop, - WebRtc_UWord32 freq) -{ - WEBRTC_TRACE( - kTraceInfo, - kTraceFile, - _id, - "ModuleFileUtility::InitPCMReading(pcm= 0x%x, start=%d, stop=%d,\ - freq=%d)", - &pcm, - start, - stop, - freq); - - WebRtc_Word8 dummy[320]; - WebRtc_Word32 read_len; - - _playoutPositionMs = 0; - _startPointInMs = start; - _stopPointInMs = stop; - _reading = false; - - if(freq == 8000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 8000; - codec_info_.pacsize = 160; - codec_info_.channels = 1; - codec_info_.rate = 128000; - _codecId = kCodecL16_8Khz; - } - else if(freq == 16000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 16000; - codec_info_.pacsize = 320; - codec_info_.channels = 1; - codec_info_.rate = 256000; - _codecId = kCodecL16_16kHz; - } - else if(freq == 32000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 32000; - codec_info_.pacsize = 320; - codec_info_.channels = 1; - codec_info_.rate = 512000; - _codecId = kCodecL16_32Khz; - } - - // Readsize for 10ms of audio data (2 bytes per sample). - _readSizeBytes = 2 * codec_info_. plfreq / 100; - if(_startPointInMs > 0) - { - while (_playoutPositionMs < _startPointInMs) - { - read_len = pcm.Read(dummy, _readSizeBytes); - if(read_len == _readSizeBytes) - { - _playoutPositionMs += 10; - } - else // Must have reached EOF before start position! - { - return -1; - } - } - } - _reading = true; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::ReadPCMData(InStream& pcm, - WebRtc_Word8* outData, - WebRtc_UWord32 bufferSize) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::ReadPCMData(pcm= 0x%x, outData= 0x%x, bufSize= %d)", - &pcm, - outData, - bufferSize); - - if(outData == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id,"buffer NULL"); - } - - // Readsize for 10ms of audio data (2 bytes per sample). - WebRtc_UWord32 bytesRequested = 2 * codec_info_.plfreq / 100; - if(bufferSize < bytesRequested) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadPCMData: buffer not long enough for a 10ms frame."); - assert(false); - return -1; - } - - WebRtc_UWord32 bytesRead = pcm.Read(outData, bytesRequested); - if(bytesRead < bytesRequested) - { - if(pcm.Rewind() == -1) - { - _reading = false; - } - else - { - if(InitPCMReading(pcm, _startPointInMs, _stopPointInMs, - codec_info_.plfreq) == -1) - { - _reading = false; - } - else - { - WebRtc_Word32 rest = bytesRequested - bytesRead; - WebRtc_Word32 len = pcm.Read(&(outData[bytesRead]), rest); - if(len == rest) - { - bytesRead += len; - } - else - { - _reading = false; - } - } - if(bytesRead <= 0) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "ReadPCMData: Failed to rewind audio file."); - return -1; - } - } - } - - if(bytesRead <= 0) - { - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "ReadPCMData: end of file"); - return -1; - } - _playoutPositionMs += 10; - if(_stopPointInMs && _playoutPositionMs >= _stopPointInMs) - { - if(!pcm.Rewind()) - { - if(InitPCMReading(pcm, _startPointInMs, _stopPointInMs, - codec_info_.plfreq) == -1) - { - _reading = false; - } - } - } - return bytesRead; -} - -WebRtc_Word32 ModuleFileUtility::InitPCMWriting(OutStream& out, - WebRtc_UWord32 freq) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "ModuleFileUtility::InitPCMWriting(out=0x%x, freq= %ld)", &out, - freq); - - if(freq == 8000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 8000; - codec_info_.pacsize = 160; - codec_info_.channels = 1; - codec_info_.rate = 128000; - - _codecId = kCodecL16_8Khz; - } - else if(freq == 16000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 16000; - codec_info_.pacsize = 320; - codec_info_.channels = 1; - codec_info_.rate = 256000; - - _codecId = kCodecL16_16kHz; - } - else if(freq == 32000) - { - strcpy(codec_info_.plname, "L16"); - codec_info_.pltype = -1; - codec_info_.plfreq = 32000; - codec_info_.pacsize = 320; - codec_info_.channels = 1; - codec_info_.rate = 512000; - - _codecId = kCodecL16_32Khz; - } - if((_codecId != kCodecL16_8Khz) && - (_codecId != kCodecL16_16kHz) && - (_codecId != kCodecL16_32Khz)) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "CodecInst is not 8KHz PCM or 16KHz PCM!"); - return -1; - } - _writing = true; - _bytesWritten = 0; - return 0; -} - -WebRtc_Word32 ModuleFileUtility::WritePCMData(OutStream& out, - const WebRtc_Word8* buffer, - const WebRtc_UWord32 dataLength) -{ - WEBRTC_TRACE( - kTraceStream, - kTraceFile, - _id, - "ModuleFileUtility::WritePCMData(out= 0x%x, buf= 0x%x, dataLen= %d)", - &out, - buffer, - dataLength); - - if(buffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "buffer NULL"); - } - - if(!out.Write(buffer, dataLength)) - { - return -1; - } - - _bytesWritten += dataLength; - return dataLength; -} - -WebRtc_Word32 ModuleFileUtility::codec_info(CodecInst& codecInst) -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "ModuleFileUtility::codec_info(codecInst= 0x%x)", &codecInst); - - if(!_reading && !_writing) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "CodecInst: not currently reading audio file!"); - return -1; - } - memcpy(&codecInst,&codec_info_,sizeof(CodecInst)); - return 0; -} - -WebRtc_Word32 ModuleFileUtility::set_codec_info(const CodecInst& codecInst) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceFile, _id, - "ModuleFileUtility::set_codec_info(codecName= %s)", - codecInst.plname); - - _codecId = kCodecNoCodec; - if(STR_CASE_CMP(codecInst.plname, "PCMU") == 0) - { - _codecId = kCodecPcmu; - } - else if(STR_CASE_CMP(codecInst.plname, "PCMA") == 0) - { - _codecId = kCodecPcma; - } - else if(STR_CASE_CMP(codecInst.plname, "L16") == 0) - { - if(codecInst.plfreq == 8000) - { - _codecId = kCodecL16_8Khz; - } - else if(codecInst.plfreq == 16000) - { - _codecId = kCodecL16_16kHz; - } - else if(codecInst.plfreq == 32000) - { - _codecId = kCodecL16_32Khz; - } - } -#ifdef WEBRTC_CODEC_GSMAMR - else if(STR_CASE_CMP(codecInst.plname, "amr") == 0) - { - _codecId = kCodecAmr; - } -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - else if(STR_CASE_CMP(codecInst.plname, "amr-wb") == 0) - { - _codecId = kCodecAmrWb; - } -#endif -#ifdef WEBRTC_CODEC_ILBC - else if(STR_CASE_CMP(codecInst.plname, "ilbc") == 0) - { - if(codecInst.pacsize == 160) - { - _codecId = kCodecIlbc20Ms; - } - else if(codecInst.pacsize == 240) - { - _codecId = kCodecIlbc30Ms; - } - } -#endif -#if(defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)) - else if(STR_CASE_CMP(codecInst.plname, "isac") == 0) - { - if(codecInst.plfreq == 16000) - { - _codecId = kCodecIsac; - } - else if(codecInst.plfreq == 32000) - { - _codecId = kCodecIsacSwb; - } - } -#endif -#ifdef WEBRTC_CODEC_ISACLC - else if(STR_CASE_CMP(codecInst.plname, "isaclc") == 0) - { - _codecId = kCodecIsacLc; - } -#endif -#ifdef WEBRTC_CODEC_G722 - else if(STR_CASE_CMP(codecInst.plname, "G722") == 0) - { - _codecId = kCodecG722; - } -#endif - else if(STR_CASE_CMP(codecInst.plname, "G7221") == 0) - { -#ifdef WEBRTC_CODEC_G722_1 - if(codecInst.plfreq == 16000) - { - if(codecInst.rate == 16000) - { - _codecId = kCodecG722_1_16Kbps; - } - else if(codecInst.rate == 24000) - { - _codecId = kCodecG722_1_24Kbps; - } - else if(codecInst.rate == 32000) - { - _codecId = kCodecG722_1_32Kbps; - } - } -#endif -#ifdef WEBRTC_CODEC_G722_1C - if(codecInst.plfreq == 32000) - { - if(codecInst.rate == 48000) - { - _codecId = kCodecG722_1c_48; - } - else if(codecInst.rate == 32000) - { - _codecId = kCodecG722_1c_32; - } - else if(codecInst.rate == 24000) - { - _codecId = kCodecG722_1c_24; - } - } -#endif - } -#ifdef WEBRTC_CODEC_G726 - else if(STR_CASE_CMP(codecInst.plname, "G726-40") == 0) - { - _codecId = kCodecG726_40; - } - else if(STR_CASE_CMP(codecInst.plname, "G726-32") == 0) - { - _codecId = kCodecG726_24; - } - else if(STR_CASE_CMP(codecInst.plname, "G726-24") == 0) - { - _codecId = kCodecG726_32; - } - else if(STR_CASE_CMP(codecInst.plname, "G726-16") == 0) - { - _codecId = kCodecG726_16; - } -#endif -#ifdef WEBRTC_CODEC_G729 - else if(STR_CASE_CMP(codecInst.plname, "G729") == 0) - { - _codecId = kCodecG729; - } -#endif -#ifdef WEBRTC_CODEC_G729_1 - else if(STR_CASE_CMP(codecInst.plname, "G7291") == 0) - { - _codecId = kCodecG729_1; - } -#endif -#ifdef WEBRTC_CODEC_SPEEX - else if(STR_CASE_CMP(codecInst.plname, "speex") == 0) - { - if(codecInst.plfreq == 8000) - { - _codecId = kCodecSpeex8Khz; - } - else if(codecInst.plfreq == 16000) - { - _codecId = kCodecSpeex16Khz; - } - } -#endif - if(_codecId == kCodecNoCodec) - { - return -1; - } - memcpy(&codec_info_, &codecInst, sizeof(CodecInst)); - return 0; -} - -WebRtc_Word32 ModuleFileUtility::FileDurationMs(const WebRtc_Word8* fileName, - const FileFormats fileFormat, - const WebRtc_UWord32 freqInHz) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceFile, - _id, - "ModuleFileUtility::FileDuration(%s, format= %d, frequency %d)", - fileName, - fileFormat, - freqInHz); - - if(fileName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, "filename NULL"); - } - - WebRtc_Word32 time_in_ms = -1; - struct stat file_size; - if(stat(fileName,&file_size) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "failed to retrieve file size with stat!"); - return -1; - } - FileWrapper* inStreamObj = FileWrapper::Create(); - if(inStreamObj == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceFile, _id, - "failed to create InStream object!"); - return -1; - } - if(inStreamObj->OpenFile(fileName, true) == -1) - { - delete inStreamObj; - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "failed to open file %s!", fileName); - return -1; - } - - switch (fileFormat) - { - case kFileFormatWavFile: - { - if(ReadWavHeader(*inStreamObj) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "failed to read WAV file header!"); - return -1; - } - time_in_ms = ((file_size.st_size - 44) / - (_wavFormatObj.nAvgBytesPerSec/1000)); - break; - } - case kFileFormatPcm16kHzFile: - { - // 16 samples per ms. 2 bytes per sample. - WebRtc_Word32 denominator = 16*2; - time_in_ms = (file_size.st_size)/denominator; - break; - } - case kFileFormatPcm8kHzFile: - { - // 8 samples per ms. 2 bytes per sample. - WebRtc_Word32 denominator = 8*2; - time_in_ms = (file_size.st_size)/denominator; - break; - } - case kFileFormatCompressedFile: - { - WebRtc_Word32 cnt = 0; - WebRtc_Word32 read_len = 0; - WebRtc_Word8 buf[64]; - do - { - read_len = inStreamObj->Read(&buf[cnt++], 1); - if(read_len != 1) - { - return -1; - } - } while ((buf[cnt-1] != '\n') && (64 > cnt)); - - if(cnt == 64) - { - return -1; - } - else - { - buf[cnt] = 0; - } -#ifdef WEBRTC_CODEC_GSMAMR - if(!strcmp("#!AMR\n", buf)) - { - WebRtc_UWord8 dummy; - read_len = inStreamObj->Read(&dummy, 1); - if(read_len != 1) - { - return -1; - } - - WebRtc_Word16 AMRMode = (dummy>>3)&0xF; - - // TODO (hellner): use tables instead of hardcoding like this! - // Additionally, this calculation does not - // take octet alignment into consideration. - switch (AMRMode) - { - // Mode 0: 4.75 kbit/sec -> 95 bits per 20 ms frame. - // 20 ms = 95 bits -> - // file size in bytes * 8 / 95 is the number of - // 20 ms frames in the file -> - // time_in_ms = file size * 8 / 95 * 20 - case 0: - time_in_ms = ((file_size.st_size)*160)/95; - break; - // Mode 1: 5.15 kbit/sec -> 103 bits per 20 ms frame. - case 1: - time_in_ms = ((file_size.st_size)*160)/103; - break; - // Mode 2: 5.90 kbit/sec -> 118 bits per 20 ms frame. - case 2: - time_in_ms = ((file_size.st_size)*160)/118; - break; - // Mode 3: 6.70 kbit/sec -> 134 bits per 20 ms frame. - case 3: - time_in_ms = ((file_size.st_size)*160)/134; - break; - // Mode 4: 7.40 kbit/sec -> 148 bits per 20 ms frame. - case 4: - time_in_ms = ((file_size.st_size)*160)/148; - break; - // Mode 5: 7.95 bit/sec -> 159 bits per 20 ms frame. - case 5: - time_in_ms = ((file_size.st_size)*160)/159; - break; - // Mode 6: 10.2 bit/sec -> 204 bits per 20 ms frame. - case 6: - time_in_ms = ((file_size.st_size)*160)/204; - break; - // Mode 7: 12.2 bit/sec -> 244 bits per 20 ms frame. - case 7: - time_in_ms = ((file_size.st_size)*160)/244; - break; - // Mode 8: SID Mode -> 39 bits per 20 ms frame. - case 8: - time_in_ms = ((file_size.st_size)*160)/39; - break; - default: - break; - } - } -#endif -#ifdef WEBRTC_CODEC_GSMAMRWB - if(!strcmp("#!AMRWB\n", buf)) - { - WebRtc_UWord8 dummy; - read_len = inStreamObj->Read(&dummy, 1); - if(read_len != 1) - { - return -1; - } - - // TODO (hellner): use tables instead of hardcoding like this! - WebRtc_Word16 AMRWBMode = (dummy>>3)&0xF; - switch(AMRWBMode) - { - // Mode 0: 6.6 kbit/sec -> 132 bits per 20 ms frame. - case 0: - time_in_ms = ((file_size.st_size)*160)/132; - break; - // Mode 1: 8.85 kbit/sec -> 177 bits per 20 ms frame. - case 1: - time_in_ms = ((file_size.st_size)*160)/177; - break; - // Mode 2: 12.65 kbit/sec -> 253 bits per 20 ms frame. - case 2: - time_in_ms = ((file_size.st_size)*160)/253; - break; - // Mode 3: 14.25 kbit/sec -> 285 bits per 20 ms frame. - case 3: - time_in_ms = ((file_size.st_size)*160)/285; - break; - // Mode 4: 15.85 kbit/sec -> 317 bits per 20 ms frame. - case 4: - time_in_ms = ((file_size.st_size)*160)/317; - break; - // Mode 5: 18.25 kbit/sec -> 365 bits per 20 ms frame. - case 5: - time_in_ms = ((file_size.st_size)*160)/365; - break; - // Mode 6: 19.85 kbit/sec -> 397 bits per 20 ms frame. - case 6: - time_in_ms = ((file_size.st_size)*160)/397; - break; - // Mode 7: 23.05 kbit/sec -> 461 bits per 20 ms frame. - case 7: - time_in_ms = ((file_size.st_size)*160)/461; - break; - // Mode 8: 23.85 kbit/sec -> 477 bits per 20 ms frame. - case 8: - time_in_ms = ((file_size.st_size)*160)/477; - break; - default: - delete inStreamObj; - return -1; - } - } -#endif -#ifdef WEBRTC_CODEC_ILBC - if(!strcmp("#!iLBC20\n", buf)) - { - // 20 ms is 304 bits - time_in_ms = ((file_size.st_size)*160)/304; - break; - } - if(!strcmp("#!iLBC30\n", buf)) - { - // 30 ms takes 400 bits. - // file size in bytes * 8 / 400 is the number of - // 30 ms frames in the file -> - // time_in_ms = file size * 8 / 400 * 30 - time_in_ms = ((file_size.st_size)*240)/400; - break; - } -#endif - } - case kFileFormatPreencodedFile: - { - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "cannot determine duration of Pre-Encoded file!"); - break; - } - default: - WEBRTC_TRACE(kTraceError, kTraceFile, _id, - "unsupported file format %d!", fileFormat); - break; - } - inStreamObj->CloseFile(); - delete inStreamObj; - return time_in_ms; -} - -WebRtc_UWord32 ModuleFileUtility::PlayoutPositionMs() -{ - WEBRTC_TRACE(kTraceStream, kTraceFile, _id, - "ModuleFileUtility::PlayoutPosition()"); - - if(_reading) - { - return _playoutPositionMs; - } - else - { - return 0; - } -} -} // namespace webrtc diff --git a/modules/media_file/source/media_file_utility.h b/modules/media_file/source/media_file_utility.h deleted file mode 100644 index f711b6136..000000000 --- a/modules/media_file/source/media_file_utility.h +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Note: the class cannot be used for reading and writing at the same time. -#ifndef WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_UTILITY_H_ -#define WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_UTILITY_H_ - -#include - -#include "common_types.h" -#include "media_file_defines.h" - -namespace webrtc { -class AviFile; -class InStream; -class OutStream; - -class ModuleFileUtility -{ -public: - - ModuleFileUtility(const WebRtc_Word32 id); - ~ModuleFileUtility(); - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - // Open the file specified by fileName for reading (relative path is - // allowed). If loop is true the file will be played until StopPlaying() is - // called. When end of file is reached the file is read from the start. - // Only video will be read if videoOnly is true. - WebRtc_Word32 InitAviReading(const WebRtc_Word8* fileName, bool videoOnly, - bool loop); - - // Put 10-60ms of audio data from file into the outBuffer depending on - // codec frame size. bufferLengthInBytes indicates the size of outBuffer. - // The return value is the number of bytes written to audioBuffer. - // Note: This API only play mono audio but can be used on file containing - // audio with more channels (in which case the audio will be coverted to - // mono). - WebRtc_Word32 ReadAviAudioData(WebRtc_Word8* outBuffer, - const WebRtc_UWord32 bufferLengthInBytes); - - // Put one video frame into outBuffer. bufferLengthInBytes indicates the - // size of outBuffer. - // The return value is the number of bytes written to videoBuffer. - WebRtc_Word32 ReadAviVideoData(WebRtc_Word8* videoBuffer, - const WebRtc_UWord32 bufferLengthInBytes); - - // Open/create the file specified by fileName for writing audio/video data - // (relative path is allowed). codecInst specifies the encoding of the audio - // data. videoCodecInst specifies the encoding of the video data. Only video - // data will be recorded if videoOnly is true. - WebRtc_Word32 InitAviWriting(const WebRtc_Word8* filename, - const CodecInst& codecInst, - const VideoCodec& videoCodecInst, - const bool videoOnly); - - // Write one audio frame, i.e. the bufferLengthinBytes first bytes of - // audioBuffer, to file. The audio frame size is determined by the - // codecInst.pacsize parameter of the last sucessfull - // InitAviWriting(..) call. - // Note: bufferLength must be exactly one frame. - WebRtc_Word32 WriteAviAudioData(const WebRtc_Word8* audioBuffer, - WebRtc_UWord32 bufferLengthInBytes); - - - // Write one video frame, i.e. the bufferLength first bytes of videoBuffer, - // to file. - // Note: videoBuffer can contain encoded data. The codec used must be the - // same as what was specified by videoCodecInst for the last successfull - // InitAviWriting(..) call. The videoBuffer must contain exactly - // one video frame. - WebRtc_Word32 WriteAviVideoData(const WebRtc_Word8* videoBuffer, - WebRtc_UWord32 bufferLengthInBytes); - - // Stop recording to file or stream. - WebRtc_Word32 CloseAviFile(); - - WebRtc_Word32 VideoCodecInst(VideoCodec& codecInst); -#endif // #ifdef WEBRTC_MODULE_UTILITY_VIDEO - - // Prepare for playing audio from stream. - // startPointMs and stopPointMs, unless zero, specify what part of the file - // should be read. From startPointMs ms to stopPointMs ms. - WebRtc_Word32 InitWavReading(InStream& stream, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0); - - // Put 10-60ms of audio data from stream into the audioBuffer depending on - // codec frame size. dataLengthInBytes indicates the size of audioBuffer. - // The return value is the number of bytes written to audioBuffer. - // Note: This API only play mono audio but can be used on file containing - // audio with more channels (in which case the audio will be converted to - // mono). - WebRtc_Word32 ReadWavDataAsMono(InStream& stream, WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 dataLengthInBytes); - - // Put 10-60ms, depending on codec frame size, of audio data from file into - // audioBufferLeft and audioBufferRight. The buffers contain the left and - // right channel of played out stereo audio. - // dataLengthInBytes indicates the size of both audioBufferLeft and - // audioBufferRight. - // The return value is the number of bytes read for each buffer. - // Note: This API can only be successfully called for WAV files with stereo - // audio. - WebRtc_Word32 ReadWavDataAsStereo(InStream& wav, - WebRtc_Word8* audioBufferLeft, - WebRtc_Word8* audioBufferRight, - const WebRtc_UWord32 bufferLength); - - // Prepare for recording audio to stream. - // codecInst specifies the encoding of the audio data. - // Note: codecInst.channels should be set to 2 for stereo (and 1 for - // mono). Stereo is only supported for WAV files. - WebRtc_Word32 InitWavWriting(OutStream& stream, const CodecInst& codecInst); - - // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, - // to file. The audio frame size is determined by the codecInst.pacsize - // parameter of the last sucessfull StartRecordingAudioFile(..) call. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 WriteWavData(OutStream& stream, - const WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 bufferLength); - - // Finalizes the WAV header so that it is correct if nothing more will be - // written to stream. - // Note: this API must be called before closing stream to ensure that the - // WAVE header is updated with the file size. Don't call this API - // if more samples are to be written to stream. - WebRtc_Word32 UpdateWavHeader(OutStream& stream); - - // Prepare for playing audio from stream. - // startPointMs and stopPointMs, unless zero, specify what part of the file - // should be read. From startPointMs ms to stopPointMs ms. - // freqInHz is the PCM sampling frequency. - // NOTE, allowed frequencies are 8000, 16000 and 32000 (Hz) - WebRtc_Word32 InitPCMReading(InStream& stream, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0, - const WebRtc_UWord32 freqInHz = 16000); - - // Put 10-60ms of audio data from stream into the audioBuffer depending on - // codec frame size. dataLengthInBytes indicates the size of audioBuffer. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 ReadPCMData(InStream& stream, WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 dataLengthInBytes); - - // Prepare for recording audio to stream. - // freqInHz is the PCM sampling frequency. - // NOTE, allowed frequencies are 8000, 16000 and 32000 (Hz) - WebRtc_Word32 InitPCMWriting(OutStream& stream, - const WebRtc_UWord32 freqInHz = 16000); - - // Write one 10ms audio frame, i.e. the bufferLength first bytes of - // audioBuffer, to file. The audio frame size is determined by the freqInHz - // parameter of the last sucessfull InitPCMWriting(..) call. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 WritePCMData(OutStream& stream, - const WebRtc_Word8* audioBuffer, - WebRtc_UWord32 bufferLength); - - // Prepare for playing audio from stream. - // startPointMs and stopPointMs, unless zero, specify what part of the file - // should be read. From startPointMs ms to stopPointMs ms. - WebRtc_Word32 InitCompressedReading(InStream& stream, - const WebRtc_UWord32 startPointMs = 0, - const WebRtc_UWord32 stopPointMs = 0); - - // Put 10-60ms of audio data from stream into the audioBuffer depending on - // codec frame size. dataLengthInBytes indicates the size of audioBuffer. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 ReadCompressedData(InStream& stream, - WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 dataLengthInBytes); - - // Prepare for recording audio to stream. - // codecInst specifies the encoding of the audio data. - WebRtc_Word32 InitCompressedWriting(OutStream& stream, - const CodecInst& codecInst); - - // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, - // to file. The audio frame size is determined by the codecInst.pacsize - // parameter of the last sucessfull InitCompressedWriting(..) call. - // The return value is the number of bytes written to stream. - // Note: bufferLength must be exactly one frame. - WebRtc_Word32 WriteCompressedData(OutStream& stream, - const WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 bufferLength); - - // Prepare for playing audio from stream. - // codecInst specifies the encoding of the audio data. - WebRtc_Word32 InitPreEncodedReading(InStream& stream, - const CodecInst& codecInst); - - // Put 10-60ms of audio data from stream into the audioBuffer depending on - // codec frame size. dataLengthInBytes indicates the size of audioBuffer. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 ReadPreEncodedData(InStream& stream, - WebRtc_Word8* audioBuffer, - const WebRtc_UWord32 dataLengthInBytes); - - // Prepare for recording audio to stream. - // codecInst specifies the encoding of the audio data. - WebRtc_Word32 InitPreEncodedWriting(OutStream& stream, - const CodecInst& codecInst); - - // Write one audio frame, i.e. the bufferLength first bytes of audioBuffer, - // to stream. The audio frame size is determined by the codecInst.pacsize - // parameter of the last sucessfull InitPreEncodedWriting(..) call. - // The return value is the number of bytes written to stream. - // Note: bufferLength must be exactly one frame. - WebRtc_Word32 WritePreEncodedData(OutStream& stream, - const WebRtc_Word8* inData, - const WebRtc_UWord32 dataLengthInBytes); - - // Set durationMs to the size of the file (in ms) specified by fileName. - // freqInHz specifies the sampling frequency of the file. - WebRtc_Word32 FileDurationMs(const WebRtc_Word8* fileName, - const FileFormats fileFormat, - const WebRtc_UWord32 freqInHz = 16000); - - // Return the number of ms that have been played so far. - WebRtc_UWord32 PlayoutPositionMs(); - - // Update codecInst according to the current audio codec being used for - // reading or writing. - WebRtc_Word32 codec_info(CodecInst& codecInst); - -private: - // Biggest WAV frame supported is 10 ms at 32kHz of 2 channel, 16 bit audio. - enum{WAV_MAX_BUFFER_SIZE = 320*2*2}; - - - WebRtc_Word32 InitWavCodec(WebRtc_UWord32 samplesPerSec, - WebRtc_UWord32 channels, - WebRtc_UWord32 bitsPerSample, - WebRtc_UWord32 formatTag); - - // Parse the WAV header in stream. - WebRtc_Word32 ReadWavHeader(InStream& stream); - - // Update the WAV header. freqInHz, bytesPerSample, channels, format, - // lengthInBytes specify characterists of the audio data. - // freqInHz is the sampling frequency. bytesPerSample is the sample size in - // bytes. channels is the number of channels, e.g. 1 is mono and 2 is - // stereo. format is the encode format (e.g. PCMU, PCMA, PCM etc). - // lengthInBytes is the number of bytes the audio samples are using up. - WebRtc_Word32 WriteWavHeader(OutStream& stream, - const WebRtc_UWord32 freqInHz, - const WebRtc_UWord32 bytesPerSample, - const WebRtc_UWord32 channels, - const WebRtc_UWord32 format, - const WebRtc_UWord32 lengthInBytes); - - // Put dataLengthInBytes of audio data from stream into the audioBuffer. - // The return value is the number of bytes written to audioBuffer. - WebRtc_Word32 ReadWavData(InStream& stream, WebRtc_UWord8* audioBuffer, - const WebRtc_UWord32 dataLengthInBytes); - - // Update the current audio codec being used for reading or writing - // according to codecInst. - WebRtc_Word32 set_codec_info(const CodecInst& codecInst); - - struct WAVE_FMTINFO_header - { - WebRtc_Word16 formatTag; - WebRtc_Word16 nChannels; - WebRtc_Word32 nSamplesPerSec; - WebRtc_Word32 nAvgBytesPerSec; - WebRtc_Word16 nBlockAlign; - WebRtc_Word16 nBitsPerSample; - }; - // Identifiers for preencoded files. - enum MediaFileUtility_CodecType - { - kCodecNoCodec = 0, - kCodecIsac, - kCodecIsacSwb, - kCodecIsacLc, - kCodecL16_8Khz, - kCodecL16_16kHz, - kCodecL16_32Khz, - kCodecPcmu, - kCodecPcma, - kCodecIlbc20Ms, - kCodecIlbc30Ms, - kCodecG722, - kCodecG722_1_32Kbps, - kCodecG722_1_24Kbps, - kCodecG722_1_16Kbps, - kCodecG722_1c_48, - kCodecG722_1c_32, - kCodecG722_1c_24, - kCodecAmr, - kCodecAmrWb, - kCodecG729, - kCodecG729_1, - kCodecG726_40, - kCodecG726_32, - kCodecG726_24, - kCodecG726_16, - kCodecSpeex8Khz, - kCodecSpeex16Khz - }; - - // TODO (hellner): why store multiple formats. Just store either codec_info_ - // or _wavFormatObj and supply conversion functions. - WAVE_FMTINFO_header _wavFormatObj; - WebRtc_Word32 _dataSize; // Chunk size if reading a WAV file - // Number of bytes to read. I.e. frame size in bytes. May be multiple - // chunks if reading WAV. - WebRtc_Word32 _readSizeBytes; - - WebRtc_Word32 _id; - - WebRtc_UWord32 _stopPointInMs; - WebRtc_UWord32 _startPointInMs; - WebRtc_UWord32 _playoutPositionMs; - WebRtc_UWord32 _bytesWritten; - - CodecInst codec_info_; - MediaFileUtility_CodecType _codecId; - - // The amount of bytes, on average, used for one audio sample. - WebRtc_Word32 _bytesPerSample; - WebRtc_Word32 _readPos; - - // Only reading or writing can be enabled, not both. - bool _reading; - bool _writing; - - // Scratch buffer used for turning stereo audio to mono. - WebRtc_UWord8 _tempData[WAV_MAX_BUFFER_SIZE]; - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - AviFile* _aviAudioInFile; - AviFile* _aviVideoInFile; - AviFile* _aviOutFile; - VideoCodec _videoCodec; -#endif -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_MEDIA_FILE_SOURCE_MEDIA_FILE_UTILITY_H_ diff --git a/modules/rtp_rtcp/OWNERS b/modules/rtp_rtcp/OWNERS deleted file mode 100644 index a308006d5..000000000 --- a/modules/rtp_rtcp/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -hellner@google.com -pwestin@google.com -holmer@google.com -hlundin@google.com -mflodman@google.com -asapersson@google.com \ No newline at end of file diff --git a/modules/rtp_rtcp/interface/rtp_rtcp.h b/modules/rtp_rtcp/interface/rtp_rtcp.h deleted file mode 100644 index b460a03b2..000000000 --- a/modules/rtp_rtcp/interface/rtp_rtcp.h +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_H_ -#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_H_ - -#include "module.h" -#include "rtp_rtcp_defines.h" - -namespace webrtc { -// forward declaration -class Transport; - -class RtpRtcp : public Module -{ -public: - /* - * create a RTP/RTCP module object - * - * id - unique identifier of this RTP/RTCP module object - * audio - true for a audio version of the RTP/RTCP module object false will create a video version - */ - static RtpRtcp* CreateRtpRtcp(const WebRtc_Word32 id, - const bool audio); - - /* - * destroy a RTP/RTCP module object - * - * module - object to destroy - */ - static void DestroyRtpRtcp(RtpRtcp* module); - - /* - * Returns version of the module and its components - * - * version - buffer to which the version will be written - * remainingBufferInBytes - remaining number of WebRtc_Word8 in the version buffer - * position - position of the next empty WebRtc_Word8 in the version buffer - */ - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - /* - * Change the unique identifier of this object - * - * id - new unique identifier of this RTP/RTCP module object - */ - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - /* - * De-muxing functionality for conferencing - * - * register a module that will act as a default module for this module - * used for feedback messages back to the encoder when one encoded stream - * is sent to multiple destinations - * - * module - default module - */ - virtual WebRtc_Word32 RegisterDefaultModule(RtpRtcp* module) = 0; - - /* - * unregister the default module - * will stop the demuxing feedback - */ - virtual WebRtc_Word32 DeRegisterDefaultModule() = 0; - - /* - * returns true if a default module is registered, false otherwise - */ - virtual bool DefaultModuleRegistered() = 0; - - /* - * returns number of registered child modules - */ - virtual WebRtc_UWord32 NumberChildModules() = 0; - - /* - * Lip-sync between voice-video - * - * module - audio module - * - * Note: only allowed on a video module - */ - virtual WebRtc_Word32 RegisterSyncModule(RtpRtcp* module) = 0; - - /* - * Turn off lip-sync between voice-video - */ - virtual WebRtc_Word32 DeRegisterSyncModule() = 0; - - /************************************************************************** - * - * Receiver functions - * - ***************************************************************************/ - - /* - * Initialize receive side - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 InitReceiver() = 0; - - /* - * Used by the module to deliver the incoming data to the codec module - * - * incomingDataCallback - callback object that will receive the incoming data - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterIncomingDataCallback(RtpData* incomingDataCallback) = 0; - - /* - * Used by the module to deliver messages to the codec module/appliation - * - * incomingMessagesCallback - callback object that will receive the incoming messages - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback) = 0; - - /* - * configure a RTP packet timeout value - * - * RTPtimeoutMS - time in milliseconds after last received RTP packet - * RTCPtimeoutMS - time in milliseconds after last received RTCP packet - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetPacketTimeout(const WebRtc_UWord32 RTPtimeoutMS, - const WebRtc_UWord32 RTCPtimeoutMS) = 0; - - /* - * Set periodic dead or alive notification - * - * enable - turn periodic dead or alive notification on/off - * sampleTimeSeconds - sample interval in seconds for dead or alive notifications - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetPeriodicDeadOrAliveStatus(const bool enable, - const WebRtc_UWord8 sampleTimeSeconds) = 0; - - /* - * Get periodic dead or alive notification status - * - * enable - periodic dead or alive notification on/off - * sampleTimeSeconds - sample interval in seconds for dead or alive notifications - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 PeriodicDeadOrAliveStatus(bool &enable, - WebRtc_UWord8 &sampleTimeSeconds) = 0; - - /* - * set codec name and payload type - * - * payloadName - payload name of codec - * payloadType - payload type of codec - * frequency - (audio specific) frequency of codec - * channels - (audio specific) number of channels in codec (1 = mono, 2 = stereo) - * rate - (audio) rate of codec - * (video) maxBitrate of codec, bits/sec - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterReceivePayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency = 0, - const WebRtc_UWord8 channels = 1, - const WebRtc_UWord32 rate = 0) = 0; - - /* - * Remove a registerd payload type from list of accepted payloads - * - * payloadType - payload type of codec - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 DeRegisterReceivePayload(const WebRtc_Word8 payloadType) = 0; - - /* - * get configured payload type - * - * payloadName - payload name of codec - * frequency - frequency of codec, ignored for video - * payloadType - payload type of codec, ignored for video - * channels - number of channels in codec (1 = mono, 2 = stereo) - * rate - (audio) rate of codec (ignored if set to 0) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - WebRtc_Word8* payloadType, - const WebRtc_UWord32 rate = 0) const = 0; - - /* - * get configured payload - * - * payloadType - payload type of codec - * payloadName - payload name of codec - * frequency - frequency of codec - * channels - number of channels in codec (1 = mono, 2 = stereo) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ReceivePayload(const WebRtc_Word8 payloadType, - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels, - WebRtc_UWord32* rate = NULL) const = 0; - - /* - * Get last received remote timestamp - */ - virtual WebRtc_UWord32 RemoteTimestamp() const = 0; - - /* - * Get the current estimated remote timestamp - * - * timestamp - estimated timestamp - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const = 0; - - /* - * Get incoming SSRC - */ - virtual WebRtc_UWord32 RemoteSSRC() const = 0; - - /* - * Get remote CSRC - * - * arrOfCSRC - array that will receive the CSRCs - * - * return -1 on failure else the number of valid entries in the list - */ - virtual WebRtc_Word32 RemoteCSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const = 0; - - /* - * get Current incoming payload - * - * payloadName - payload name of codec - * payloadType - payload type of codec - * frequency - frequency of codec - * channels - number of channels in codec (2 = stereo) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_Word8* payloadType, - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels) const = 0; - - /* - * get the currently configured SSRC filter - * - * allowedSSRC - SSRC that will be allowed through - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SSRCFilter(WebRtc_UWord32& allowedSSRC) const = 0; - - /* - * set a SSRC to be used as a filter for incoming RTP streams - * - * allowedSSRC - SSRC that will be allowed through - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC) = 0; - - /* - * called by the network module when we receive a packet - * - * incomingPacket - incoming packet buffer - * packetLength - length of incoming buffer - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 IncomingPacket( const WebRtc_UWord8* incomingPacket, - const WebRtc_UWord16 packetLength) = 0; - - - /* - * Option when not using the RegisterSyncModule function - * - * Inform the module about the received audion NTP - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 IncomingAudioNTP(const WebRtc_UWord32 audioReceivedNTPsecs, - const WebRtc_UWord32 audioReceivedNTPfrac, - const WebRtc_UWord32 audioRTCPArrivalTimeSecs, - const WebRtc_UWord32 audioRTCPArrivalTimeFrac) = 0; - - - /************************************************************************** - * - * Sender - * - ***************************************************************************/ - - /* - * Initialize send side - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 InitSender() = 0; - - /* - * Used by the module to send RTP and RTCP packet to the network module - * - * outgoingTransport - transport object that will be called when packets are ready to be sent out on the network - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport) = 0; - - /* - * set MTU - * - * size - Max transfer unit in bytes, default is 1500 - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetMaxTransferUnit(const WebRtc_UWord16 size) = 0; - - /* - * set transtport overhead - * default is IPv4 and UDP with no encryption - * - * TCP - true for TCP false UDP - * IPv6 - true for IP version 6 false for version 4 - * authenticationOverhead - number of bytes to leave for an authentication header - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetTransportOverhead(const bool TCP, - const bool IPV6, - const WebRtc_UWord8 authenticationOverhead = 0) = 0; - - /* - * Get max payload length - * - * A combination of the configuration MaxTransferUnit and TransportOverhead. - * Does not account FEC/ULP/RED overhead if FEC is enabled. - * Does not account for RTP headers - */ - virtual WebRtc_UWord16 MaxPayloadLength() const = 0; - - /* - * Get max data payload length - * - * A combination of the configuration MaxTransferUnit, headers and TransportOverhead. - * Takes into account FEC/ULP/RED overhead if FEC is enabled. - * Takes into account RTP headers - */ - virtual WebRtc_UWord16 MaxDataPayloadLength() const = 0; - - /* - * set RTPKeepaliveStatus - * - * enable - on/off - * unknownPayloadType - payload type to use for RTP keepalive - * deltaTransmitTimeMS - delta time between RTP keepalive packets - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetRTPKeepaliveStatus(const bool enable, - const WebRtc_Word8 unknownPayloadType, - const WebRtc_UWord16 deltaTransmitTimeMS) = 0; - - /* - * Get RTPKeepaliveStatus - * - * enable - on/off - * unknownPayloadType - payload type in use for RTP keepalive - * deltaTransmitTimeMS - delta time between RTP keepalive packets - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RTPKeepaliveStatus(bool* enable, - WebRtc_Word8* unknownPayloadType, - WebRtc_UWord16* deltaTransmitTimeMS) const = 0; - - /* - * check if RTPKeepaliveStatus is enabled - */ - virtual bool RTPKeepalive() const = 0; - - /* - * set codec name and payload type - * - * payloadName - payload name of codec - * payloadType - payload type of codec - * frequency - frequency of codec - * channels - number of channels in codec (1 = mono, 2 = stereo) - * rate - (audio) rate of codec - * (video) maxBitrate of codec, bits/sec - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterSendPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency = 0, - const WebRtc_UWord8 channels = 1, - const WebRtc_UWord32 rate = 0) = 0; - - /* - * Unregister a send payload - * - * payloadType - payload type of codec - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 DeRegisterSendPayload(const WebRtc_Word8 payloadType) = 0; - - /* - * get start timestamp - */ - virtual WebRtc_UWord32 StartTimestamp() const = 0; - - /* - * configure start timestamp, default is a random number - * - * timestamp - start timestamp - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetStartTimestamp(const WebRtc_UWord32 timestamp) = 0; - - /* - * Get SequenceNumber - */ - virtual WebRtc_UWord16 SequenceNumber() const = 0; - - /* - * Set SequenceNumber, default is a random number - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSequenceNumber(const WebRtc_UWord16 seq) = 0; - - /* - * Get SSRC - */ - virtual WebRtc_UWord32 SSRC() const = 0; - - /* - * configure SSRC, default is a random number - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSSRC(const WebRtc_UWord32 ssrc) = 0; - - /* - * Get CSRC - * - * arrOfCSRC - array of CSRCs - * - * return -1 on failure else number of valid entries in the array - */ - virtual WebRtc_Word32 CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const = 0; - - /* - * Set CSRC - * - * arrOfCSRC - array of CSRCs - * arrLength - number of valid entries in the array - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetCSRCs( const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength) = 0; - - /* - * includes CSRCs in RTP header if enabled - * - * include CSRC - on/off - * - * default:on - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetCSRCStatus(const bool include) = 0; - - /* - * sends kRtcpByeCode when going from true to false - * - * sending - on/off - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSendingStatus(const bool sending) = 0; - - /* - * get send status - */ - virtual bool Sending() const = 0; - - /* - * Starts/Stops media packets, on by default - * - * sending - on/off - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSendingMediaStatus(const bool sending) = 0; - - /* - * get send status - */ - virtual bool SendingMedia() const = 0; - - /* - * get sent bitrate in Kbit/s - */ - virtual WebRtc_UWord32 BitrateSent() const = 0; - - /* - * Used by the codec module to deliver a video or audio frame for packetization - * - * frameType - type of frame to send - * payloadType - payload type of frame to send - * timestamp - timestamp of frame to send - * payloadData - payload buffer of frame to send - * payloadSize - size of payload buffer to send - * fragmentation - fragmentation offset data for fragmented frames such as layers or RED - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 - SendOutgoingData(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation = NULL, - const RTPVideoTypeHeader* rtpTypeHdr = NULL) = 0; - - /************************************************************************** - * - * RTCP - * - ***************************************************************************/ - - /* - * RegisterIncomingRTCPCallback - * - * incomingMessagesCallback - callback object that will receive messages from RTCP - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterIncomingRTCPCallback(RtcpFeedback* incomingMessagesCallback) = 0; - - /* - * Get RTCP status - */ - virtual RTCPMethod RTCP() const = 0; - - /* - * configure RTCP status i.e on(compound or non- compound)/off - * - * method - RTCP method to use - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetRTCPStatus(const RTCPMethod method) = 0; - - /* - * Set RTCP CName (i.e unique identifier) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) = 0; - - /* - * Get RTCP CName (i.e unique identifier) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 CNAME(WebRtc_Word8 cName[RTCP_CNAME_SIZE]) = 0; - - /* - * Get remote CName - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoteCNAME(const WebRtc_UWord32 remoteSSRC, - WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const = 0; - - /* - * Get remote NTP - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoteNTP(WebRtc_UWord32 *ReceivedNTPsecs, - WebRtc_UWord32 *ReceivedNTPfrac, - WebRtc_UWord32 *RTCPArrivalTimeSecs, - WebRtc_UWord32 *RTCPArrivalTimeFrac) const = 0; - - /* - * AddMixedCNAME - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 AddMixedCNAME(const WebRtc_UWord32 SSRC, - const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) = 0; - - /* - * RemoveMixedCNAME - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoveMixedCNAME(const WebRtc_UWord32 SSRC) = 0; - - /* - * Get RoundTripTime - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RTT(const WebRtc_UWord32 remoteSSRC, - WebRtc_UWord16* RTT, - WebRtc_UWord16* avgRTT, - WebRtc_UWord16* minRTT, - WebRtc_UWord16* maxRTT) const = 0 ; - - /* - * Reset RoundTripTime statistics - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ResetRTT(const WebRtc_UWord32 remoteSSRC)= 0 ; - - /* - * Force a send of a RTCP packet - * normal SR and RR are triggered via the process function - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SendRTCP(WebRtc_UWord32 rtcpPacketType = kRtcpReport) = 0; - - /* - * Good state of RTP receiver inform sender - */ - virtual WebRtc_Word32 SendRTCPReferencePictureSelection(const WebRtc_UWord64 pictureID) = 0; - - /* - * Send a RTCP Slice Loss Indication (SLI) - * 6 least significant bits of pictureID - */ - virtual WebRtc_Word32 SendRTCPSliceLossIndication(const WebRtc_UWord8 pictureID) = 0; - - /* - * Reset RTP statistics - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ResetStatisticsRTP() = 0; - - /* - * statistics of our localy created statistics of the received RTP stream - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 StatisticsRTP(WebRtc_UWord8 *fraction_lost, // scale 0 to 255 - WebRtc_UWord32 *cum_lost, // number of lost packets - WebRtc_UWord32 *ext_max, // highest sequence number received - WebRtc_UWord32 *jitter, - WebRtc_UWord32 *max_jitter = NULL) const = 0; - - /* - * Reset RTP data counters for the receiving side - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ResetReceiveDataCountersRTP() = 0; - - /* - * Reset RTP data counters for the sending side - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 ResetSendDataCountersRTP() = 0; - - /* - * statistics of the amount of data sent and received - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 DataCountersRTP(WebRtc_UWord32 *bytesSent, - WebRtc_UWord32 *packetsSent, - WebRtc_UWord32 *bytesReceived, - WebRtc_UWord32 *packetsReceived) const = 0; - /* - * Get received RTCP sender info - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoteRTCPStat( RTCPSenderInfo* senderInfo) = 0; - - /* - * Get received RTCP report block - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoteRTCPStat( const WebRtc_UWord32 remoteSSRC, - RTCPReportBlock* receiveBlock) = 0; - /* - * Set received RTCP report block - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 AddRTCPReportBlock(const WebRtc_UWord32 SSRC, - const RTCPReportBlock* receiveBlock) = 0; - - /* - * RemoveRTCPReportBlock - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RemoveRTCPReportBlock(const WebRtc_UWord32 SSRC) = 0; - - /* - * (APP) Application specific data - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetRTCPApplicationSpecificData(const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord8* data, - const WebRtc_UWord16 length) = 0; - /* - * (XR) VOIP metric - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) = 0; - - /* - * (TMMBR) Temporary Max Media Bit Rate - */ - virtual bool TMMBR() const = 0; - - /* - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetTMMBRStatus(const bool enable) = 0; - - /* - * local bw estimation changed - * - * for video called by internal estimator - * for audio (iSAC) called by engine, geting the data from the decoder - */ - virtual void OnBandwidthEstimateUpdate(WebRtc_UWord16 bandWidthKbit) = 0; - - /* - * (NACK) - */ - virtual NACKMethod NACK() const = 0; - - /* - * Turn negative acknowledgement requests on/off - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetNACKStatus(const NACKMethod method) = 0; - - /* - * Send a Negative acknowledgement packet - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SendNACK(const WebRtc_UWord16* nackList, - const WebRtc_UWord16 size) = 0; - - /* - * Store the sent packets, needed to answer to a Negative acknowledgement requests - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore = 200) = 0; - - /************************************************************************** - * - * Audio - * - ***************************************************************************/ - - /* - * RegisterAudioCallback - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterAudioCallback(RtpAudioFeedback* messagesCallback) = 0; - - /* - * set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples) = 0; - - /* - * Outband TelephoneEvent(DTMF) detection - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetTelephoneEventStatus(const bool enable, - const bool forwardToDecoder, - const bool detectEndOfTone = false) = 0; - - /* - * Is outband TelephoneEvent(DTMF) turned on/off? - */ - virtual bool TelephoneEvent() const = 0; - - /* - * Returns true if received DTMF events are forwarded to the decoder using - * the OnPlayTelephoneEvent callback. - */ - virtual bool TelephoneEventForwardToDecoder() const = 0; - - /* - * SendTelephoneEventActive - * - * return true if we currently send a telephone event and 100 ms after an event is sent - * used to prevent teh telephone event tone to be recorded by the microphone and send inband - * just after the tone has ended - */ - virtual bool SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const = 0; - - /* - * Send a TelephoneEvent tone using RFC 2833 (4733) - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SendTelephoneEventOutband(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level) = 0; - - /* - * Set payload type for Redundant Audio Data RFC 2198 - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSendREDPayloadType(const WebRtc_Word8 payloadType) = 0; - - /* - * Get payload type for Redundant Audio Data RFC 2198 - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SendREDPayloadType(WebRtc_Word8& payloadType) const = 0; - - /* - * Set status and ID for header-extension-for-audio-level-indication. - * See https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ - * for more details. - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetRTPAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID) = 0; - - /* - * Get status and ID for header-extension-for-audio-level-indication. - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 GetRTPAudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const = 0; - - /* - * Store the audio level in dBov for header-extension-for-audio-level-indication. - * This API shall be called before transmision of an RTP packet to ensure - * that the |level| part of the extended RTP header is updated. - * - * return -1 on failure else 0. - */ - virtual WebRtc_Word32 SetAudioLevel(const WebRtc_UWord8 level_dBov) = 0; - - /************************************************************************** - * - * Video - * - ***************************************************************************/ - - /* - * Register a callback object that will receive callbacks for video related events - * such as an incoming key frame request. - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback) = 0; - - /* - * Set the estimated camera delay in MS - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetCameraDelay(const WebRtc_Word32 delayMS) = 0; - - /* - * Set the start and max send bitrate - * used by the bandwidth management - * - * Not calling this or setting startBitrateKbit to 0 disables the bandwidth management - * - * minBitrateKbit = 0 equals no min bitrate - * maxBitrateKbit = 0 equals no max bitrate - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetSendBitrate(const WebRtc_UWord32 startBitrate, - const WebRtc_UWord16 minBitrateKbit, - const WebRtc_UWord16 maxBitrateKbit) = 0; - - /* - * Turn on/off generic FEC - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC) = 0; - - /* - * Get generic FEC setting - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC) = 0; - - /* - * Set FEC code rate of key and delta frames - * codeRate on a scale of 0 to 255 where 255 is 100% added packets, hence protect up to 50% packet loss - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate) = 0; - - /* - * Set method for requestion a new key frame - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 SetKeyFrameRequestMethod(const KeyFrameRequestMethod method) = 0; - - /* - * send a request for a keyframe - * - * return -1 on failure else 0 - */ - virtual WebRtc_Word32 RequestKeyFrame(const FrameType frameType = kVideoFrameKey) = 0; - - /* - * Only for H.263 to interop with bad endpoints - */ - virtual WebRtc_Word32 SetH263InverseLogic(const bool enable) = 0; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_H_ diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h deleted file mode 100644 index e29a62d5a..000000000 --- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_ -#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_ - -#include "typedefs.h" -#include "module_common_types.h" - -#ifndef NULL - #define NULL 0 -#endif - -#define RTCP_CNAME_SIZE 256 // RFC 3550 page 44, including null termination -#define IP_PACKET_SIZE 1500 // we assume ethernet -#define RTP_PAYLOAD_NAME_SIZE 32 -#define MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS 10 -#define TIMEOUT_SEI_MESSAGES_MS 30000 // in milliseconds - -namespace webrtc{ -enum RTCPMethod -{ - kRtcpOff = 0, - kRtcpCompound = 1, - kRtcpNonCompound = 2 -}; - -enum RTPAliveType -{ - kRtpDead = 0, - kRtpNoRtp = 1, - kRtpAlive = 2 -}; - -enum RTCPAppSubTypes -{ - kAppSubtypeBwe = 0x00 -}; - -enum RTCPPacketType -{ - kRtcpReport = 0x0001, - kRtcpSr = 0x0002, - kRtcpRr = 0x0004, - kRtcpBye = 0x0008, - kRtcpPli = 0x0010, - kRtcpNack = 0x0020, - kRtcpFir = 0x0040, - kRtcpTmmbr = 0x0080, - kRtcpTmmbn = 0x0100, - kRtcpSrReq = 0x0200, - kRtcpXrVoipMetric = 0x0400, - kRtcpApp = 0x0800, - kRtcpAppBwe = 0x0801, - kRtcpSli = 0x4000, - kRtcpRpsi = 0x8000 -}; - -enum KeyFrameRequestMethod -{ - kKeyFrameReqFirRtp = 1, - kKeyFrameReqPliRtcp = 2, - kKeyFrameReqFirRtcp = 3 -}; - -enum RtpRtcpPacketType -{ - kPacketRtp = 0, - kPacketKeepAlive = 1 -}; - -enum NACKMethod -{ - kNackOff = 0, - kNackRtcp = 2 -}; - -struct RTCPSenderInfo -{ - WebRtc_UWord32 NTPseconds; - WebRtc_UWord32 NTPfraction; - WebRtc_UWord32 RTPtimeStamp; - WebRtc_UWord32 sendPacketCount; - WebRtc_UWord32 sendOctetCount; -}; - -struct RTCPReportBlock -{ - WebRtc_UWord8 fractionLost; - WebRtc_UWord32 cumulativeLost; // 24 bits valid - WebRtc_UWord32 extendedHighSeqNum; - WebRtc_UWord32 jitter; - WebRtc_UWord32 lastSR; - WebRtc_UWord32 delaySinceLastSR; -}; - -class RtpData -{ -public: - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader) = 0; -protected: - virtual ~RtpData() {} -}; - -class RtcpFeedback -{ -public: - // if audioVideoOffset > 0 video is behind audio - virtual void OnLipSyncUpdate(const WebRtc_Word32 /*id*/, - const WebRtc_Word32 /*audioVideoOffset*/) {}; - - virtual void OnApplicationDataReceived(const WebRtc_Word32 /*id*/, - const WebRtc_UWord8 /*subType*/, - const WebRtc_UWord32 /*name*/, - const WebRtc_UWord16 /*length*/, - const WebRtc_UWord8* /*data*/) {}; - - virtual void OnXRVoIPMetricReceived(const WebRtc_Word32 /*id*/, - const RTCPVoIPMetric* /*metric*/, - const WebRtc_Word8 /*VoIPmetricBuffer*/[28]) {}; - - virtual void OnRTCPPacketTimeout(const WebRtc_Word32 /*id*/) {}; - - virtual void OnTMMBRReceived(const WebRtc_Word32 /*id*/, - const WebRtc_UWord16 /*bwEstimateKbit*/) {}; - - virtual void OnSLIReceived(const WebRtc_Word32 /*id*/, - const WebRtc_UWord8 /*pictureId*/) {}; - - virtual void OnRPSIReceived(const WebRtc_Word32 /*id*/, - const WebRtc_UWord64 /*pictureId*/) {}; - - virtual void OnSendReportReceived(const WebRtc_Word32 id, - const WebRtc_UWord32 senderSSRC) {}; - - virtual void OnReceiveReportReceived(const WebRtc_Word32 id, - const WebRtc_UWord32 senderSSRC) {}; - -protected: - virtual ~RtcpFeedback() {} -}; - -class RtpFeedback -{ -public: - // Receiving payload change or SSRC change. (return success!) - /* - * channels - number of channels in codec (1 = mono, 2 = stereo) - */ - virtual WebRtc_Word32 OnInitializeDecoder(const WebRtc_Word32 id, - const WebRtc_Word8 payloadType, - const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) = 0; - - virtual void OnPacketTimeout(const WebRtc_Word32 id) = 0; - - virtual void OnReceivedPacket(const WebRtc_Word32 id, - const RtpRtcpPacketType packetType) = 0; - - virtual void OnPeriodicDeadOrAlive(const WebRtc_Word32 id, - const RTPAliveType alive) = 0; - - virtual void OnIncomingSSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 SSRC) = 0; - - virtual void OnIncomingCSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 CSRC, - const bool added) = 0; - -protected: - virtual ~RtpFeedback() {} -}; - -class RtpAudioFeedback -{ -public: - virtual void OnReceivedTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const bool endOfEvent) = 0; - - virtual void OnPlayTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const WebRtc_UWord16 lengthMs, - const WebRtc_UWord8 volume) = 0; - -protected: - virtual ~RtpAudioFeedback() {} -}; - - -class RtpVideoFeedback -{ -public: - // this function should call codec module to inform it about the request - virtual void OnReceivedIntraFrameRequest(const WebRtc_Word32 id, - const WebRtc_UWord8 message = 0) = 0; - - virtual void OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord32 minBitrateBps, - const WebRtc_UWord32 maxBitrateBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) = 0; - -protected: - virtual ~RtpVideoFeedback() {} -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_ diff --git a/modules/rtp_rtcp/source/Android.mk b/modules/rtp_rtcp/source/Android.mk deleted file mode 100644 index d2141fe82..000000000 --- a/modules/rtp_rtcp/source/Android.mk +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_rtp_rtcp -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := bitrate.cc \ - rtp_rtcp_impl.cc \ - rtcp_receiver.cc \ - rtcp_receiver_help.cc \ - rtcp_sender.cc \ - rtcp_utility.cc \ - rtp_receiver.cc \ - rtp_sender.cc \ - rtp_utility.cc \ - ssrc_database.cc \ - tmmbr_help.cc \ - dtmf_queue.cc \ - rtp_receiver_audio.cc \ - rtp_sender_audio.cc \ - bandwidth_management.cc \ - forward_error_correction.cc \ - forward_error_correction_internal.cc \ - overuse_detector.cc \ - h263_information.cc \ - remote_rate_control.cc \ - receiver_fec.cc \ - rtp_receiver_video.cc \ - rtp_sender_video.cc \ - rtp_format_vp8.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/rtp_rtcp/source/Bitrate.h b/modules/rtp_rtcp/source/Bitrate.h deleted file mode 100644 index 61345634d..000000000 --- a/modules/rtp_rtcp/source/Bitrate.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_ - -#include "typedefs.h" -#include "rtp_rtcp_config.h" // misc. defines (e.g. MAX_PACKET_LENGTH) -#include "common_types.h" // Transport -#include -#include - -namespace webrtc { -class Bitrate -{ -public: - Bitrate(); - - // initialize members - void Init(); - - // calculate rates - void Process(); - - // update with a packet - void Update(const WebRtc_Word32 bytes); - - // packet rate last second, updated roughly every 100 ms - WebRtc_UWord32 PacketRate() const; - - // bitrate last second, updated roughly every 100 ms - WebRtc_UWord32 BitrateLast() const; - - // bitrate last second, updated now - WebRtc_UWord32 BitrateNow() const; - -private: - WebRtc_UWord32 _packetRate; - WebRtc_UWord32 _bitrate; - WebRtc_UWord8 _bitrateNextIdx; - WebRtc_UWord32 _packetRateArray[10]; - WebRtc_UWord32 _bitrateArray[10]; - WebRtc_UWord32 _bitrateDiffMS[10]; - WebRtc_UWord32 _timeLastRateUpdate; - WebRtc_UWord32 _bytesCount; - WebRtc_UWord32 _packetCount; -}; - -struct DataTimeSizeTuple -{ - DataTimeSizeTuple(WebRtc_UWord32 sizeBytes, WebRtc_Word64 timeCompleteMs) : - _sizeBytes(sizeBytes), - _timeCompleteMs(timeCompleteMs) {} - - WebRtc_UWord32 _sizeBytes; - WebRtc_Word64 _timeCompleteMs; -}; - -class BitRateStats -{ -public: - BitRateStats(); - ~BitRateStats(); - - void Init(); - void Update(WebRtc_UWord32 packetSizeBytes, WebRtc_Word64 nowMs); - WebRtc_UWord32 BitRate(WebRtc_Word64 nowMs); - -private: - void EraseOld(WebRtc_Word64 nowMs); - - std::list _dataSamples; - WebRtc_UWord32 _accumulatedBytes; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_ diff --git a/modules/rtp_rtcp/source/H264/bitstream_builder.cc b/modules/rtp_rtcp/source/H264/bitstream_builder.cc deleted file mode 100644 index 05b7e2f0a..000000000 --- a/modules/rtp_rtcp/source/H264/bitstream_builder.cc +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "bitstream_builder.h" - -#include - -namespace webrtc { -BitstreamBuilder::BitstreamBuilder(WebRtc_UWord8* data, const WebRtc_UWord32 dataSize) : - _data(data), - _dataSize(dataSize), - _byteOffset(0), - _bitOffset(0) -{ - memset(data, 0, dataSize); -} - -WebRtc_UWord32 -BitstreamBuilder::Length() const -{ - return _byteOffset+ (_bitOffset?1:0); -} - -WebRtc_Word32 -BitstreamBuilder::Add1Bit(const WebRtc_UWord8 bit) -{ - // sanity - if(_bitOffset + 1 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bit); - return 0; -} - -void -BitstreamBuilder::Add1BitWithoutSanity(const WebRtc_UWord8 bit) -{ - if(bit & 0x1) - { - _data[_byteOffset] += (1 << (7-_bitOffset)); - } - - if(_bitOffset == 7) - { - // last bit in byte - _bitOffset = 0; - _byteOffset++; - } else - { - _bitOffset++; - } -} - -WebRtc_Word32 -BitstreamBuilder::Add2Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 2 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add3Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 3 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add4Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 4 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add5Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 5 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add6Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 6 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 5); - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add7Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_bitOffset + 7 > 8) - { - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - } - Add1BitWithoutSanity(bits >> 6); - Add1BitWithoutSanity(bits >> 5); - Add1BitWithoutSanity(bits >> 4); - Add1BitWithoutSanity(bits >> 3); - Add1BitWithoutSanity(bits >> 2); - Add1BitWithoutSanity(bits >> 1); - Add1BitWithoutSanity(bits); - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add8Bits(const WebRtc_UWord8 bits) -{ - // sanity - if(_dataSize < Length()+1) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = bits; - } else - { - _data[_byteOffset] += (bits >> _bitOffset); - _data[_byteOffset+1] += (bits << (8-_bitOffset)); - } - _byteOffset++; - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add16Bits(const WebRtc_UWord16 bits) -{ - // sanity - if(_dataSize < Length()+2) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (WebRtc_UWord8)(bits >> 8); - _data[_byteOffset+1] = (WebRtc_UWord8)(bits); - } else - { - _data[_byteOffset] += (WebRtc_UWord8)(bits >> (_bitOffset + 8)); - _data[_byteOffset+1] += (WebRtc_UWord8)(bits >> _bitOffset); - _data[_byteOffset+2] += (WebRtc_UWord8)(bits << (8-_bitOffset)); - } - _byteOffset += 2; - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add24Bits(const WebRtc_UWord32 bits) -{ - // sanity - if(_dataSize < Length()+3) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (WebRtc_UWord8)(bits >> 16); - _data[_byteOffset+1] = (WebRtc_UWord8)(bits >> 8); - _data[_byteOffset+2] = (WebRtc_UWord8)(bits); - } else - { - _data[_byteOffset] += (WebRtc_UWord8)(bits >> (_bitOffset+16)); - _data[_byteOffset+1] += (WebRtc_UWord8)(bits >> (_bitOffset+8)); - _data[_byteOffset+2] += (WebRtc_UWord8)(bits >> (_bitOffset)); - _data[_byteOffset+3] += (WebRtc_UWord8)(bits << (8-_bitOffset)); - } - _byteOffset += 3; - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::Add32Bits(const WebRtc_UWord32 bits) -{ - // sanity - if(_dataSize < Length()+4) - { - // not enough space in buffer - return -1; - } - if(_bitOffset == 0) - { - _data[_byteOffset] = (WebRtc_UWord8)(bits >> 24); - _data[_byteOffset+1] = (WebRtc_UWord8)(bits >> 16); - _data[_byteOffset+2] = (WebRtc_UWord8)(bits >> 8); - _data[_byteOffset+3] = (WebRtc_UWord8)(bits); - } else - { - _data[_byteOffset] += (WebRtc_UWord8)(bits >> (_bitOffset+24)); - _data[_byteOffset+1] += (WebRtc_UWord8)(bits >> (_bitOffset+16)); - _data[_byteOffset+2] += (WebRtc_UWord8)(bits >> (_bitOffset+8)); - _data[_byteOffset+3] += (WebRtc_UWord8)(bits >> (_bitOffset)); - _data[_byteOffset+4] += (WebRtc_UWord8)(bits << (8-_bitOffset)); - } - _byteOffset += 4; - return 0; -} - -// Exp-Golomb codes -/* - with "prefix" and "suffix" bits and assignment to codeNum ranges (informative) - Bit string form Range of codeNum - 1 0 - 0 1 x0 1..2 2bits-1 - 0 0 1 x1 x0 3..6 3bits-1 - 0 0 0 1 x2 x1 x0 7..14 4bits-1 - 0 0 0 0 1 x3 x2 x1 x0 15..30 - 0 0 0 0 0 1 x4 x3 x2 x1 x0 31..62 -*/ -WebRtc_Word32 -BitstreamBuilder::AddUE(const WebRtc_UWord32 value) -{ - // un-rolled on 8 bit base to avoid too deep if else chain - if(value < 0x0000ffff) - { - if(value < 0x000000ff) - { - if(value == 0) - { - if(AddPrefix(0) != 0) - { - return -1; - } - } else if(value < 3) - { - if(AddPrefix(1) != 0) - { - return -1; - } - AddSuffix(1, value-1); - } else if(value < 7) - { - if(AddPrefix(2) != 0) - { - return -1; - } - AddSuffix(2, value-3); - } else if(value < 15) - { - if(AddPrefix(3) != 0) - { - return -1; - } - AddSuffix(3, value-7); - } else if(value < 31) - { - if(AddPrefix(4) != 0) - { - return -1; - } - AddSuffix(4, value-15); - } else if(value < 63) - { - if(AddPrefix(5) != 0) - { - return -1; - } - AddSuffix(5, value-31); - } else if(value < 127) - { - if(AddPrefix(6) != 0) - { - return -1; - } - AddSuffix(6, value-63); - } else - { - if(AddPrefix(7) != 0) - { - return -1; - } - AddSuffix(7, value-127); - } - }else - { - if(value < 0x000001ff) - { - if(AddPrefix(8) != 0) - { - return -1; - } - AddSuffix(8, value-0x000000ff); - } else if(value < 0x000003ff) - { - if(AddPrefix(9) != 0) - { - return -1; - } - AddSuffix(9, value-0x000001ff); - } else if(value < 0x000007ff) - { - if(AddPrefix(10) != 0) - { - return -1; - } - AddSuffix(10, value-0x000003ff); - } else if(value < 0x00000fff) - { - if(AddPrefix(11) != 0) - { - return -1; - } - AddSuffix(1, value-0x000007ff); - } else if(value < 0x00001fff) - { - if(AddPrefix(12) != 0) - { - return -1; - } - AddSuffix(12, value-0x00000fff); - } else if(value < 0x00003fff) - { - if(AddPrefix(13) != 0) - { - return -1; - } - AddSuffix(13, value-0x00001fff); - } else if(value < 0x00007fff) - { - if(AddPrefix(14) != 0) - { - return -1; - } - AddSuffix(14, value-0x00003fff); - } else - { - if(AddPrefix(15) != 0) - { - return -1; - } - AddSuffix(15, value-0x00007fff); - } - } - }else - { - if(value < 0x00ffffff) - { - if(value < 0x0001ffff) - { - if(AddPrefix(16) != 0) - { - return -1; - } - AddSuffix(16, value-0x0000ffff); - } else if(value < 0x0003ffff) - { - if(AddPrefix(17) != 0) - { - return -1; - } - AddSuffix(17, value-0x0001ffff); - } else if(value < 0x0007ffff) - { - if(AddPrefix(18) != 0) - { - return -1; - } - AddSuffix(18, value-0x0003ffff); - } else if(value < 0x000fffff) - { - if(AddPrefix(19) != 0) - { - return -1; - } - AddSuffix(19, value-0x0007ffff); - } else if(value < 0x001fffff) - { - if(AddPrefix(20) != 0) - { - return -1; - } - AddSuffix(20, value-0x000fffff); - } else if(value < 0x003fffff) - { - if(AddPrefix(21) != 0) - { - return -1; - } - AddSuffix(21, value-0x001fffff); - } else if(value < 0x007fffff) - { - if(AddPrefix(22) != 0) - { - return -1; - } - AddSuffix(22, value-0x003fffff); - } else - { - if(AddPrefix(23) != 0) - { - return -1; - } - AddSuffix(23, value-0x007fffff); - } - } else - { - if(value < 0x01ffffff) - { - if(AddPrefix(24) != 0) - { - return -1; - } - AddSuffix(24, value-0x00ffffff); - } else if(value < 0x03ffffff) - { - if(AddPrefix(25) != 0) - { - return -1; - } - AddSuffix(25, value-0x01ffffff); - } else if(value < 0x07ffffff) - { - if(AddPrefix(26) != 0) - { - return -1; - } - AddSuffix(26, value-0x03ffffff); - } else if(value < 0x0fffffff) - { - if(AddPrefix(27) != 0) - { - return -1; - } - AddSuffix(27, value-0x07ffffff); - } else if(value < 0x1fffffff) - { - if(AddPrefix(28) != 0) - { - return -1; - } - AddSuffix(28, value-0x0fffffff); - } else if(value < 0x3fffffff) - { - if(AddPrefix(29) != 0) - { - return -1; - } - AddSuffix(29, value-0x1fffffff); - } else if(value < 0x7fffffff) - { - if(AddPrefix(30) != 0) - { - return -1; - } - AddSuffix(30, value-0x3fffffff); - } else if(value < 0xffffffff) - { - if(AddPrefix(31) != 0) - { - return -1; - } - AddSuffix(31, value-0x7ffffff); - } else - { - if(AddPrefix(32) != 0) - { - return -1; - } - AddSuffix(32, 0); // exactly 0xffffffff - } - } - } - return 0; -} - -WebRtc_Word32 -BitstreamBuilder::AddPrefix(const WebRtc_UWord8 numZeros) -{ - // sanity for the sufix too - WebRtc_UWord32 numBitsToAdd = numZeros * 2 + 1; - if(((_dataSize - _byteOffset) *8 + 8-_bitOffset) < numBitsToAdd) - { - return -1; - } - - // add numZeros - for (WebRtc_UWord32 i = 0; i < numZeros; i++) - { - Add1Bit(0); - } - Add1Bit(1); - return 0; -} - -void -BitstreamBuilder::AddSuffix(const WebRtc_UWord8 numBits, const WebRtc_UWord32 rest) -{ - // most significant bit first - for(WebRtc_Word32 i = numBits - 1; i >= 0; i--) - { - if(( rest >> i) & 0x1) - { - Add1Bit(1); - }else - { - Add1Bit(0); - } - } -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/H264/bitstream_builder.h b/modules/rtp_rtcp/source/H264/bitstream_builder.h deleted file mode 100644 index c88ef8f5c..000000000 --- a/modules/rtp_rtcp/source/H264/bitstream_builder.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ - -#include "typedefs.h" - -namespace webrtc { -class BitstreamBuilder -{ -public: - BitstreamBuilder(WebRtc_UWord8* data, const WebRtc_UWord32 dataSize); - - WebRtc_UWord32 Length() const; - - WebRtc_Word32 Add1Bit(const WebRtc_UWord8 bit); - WebRtc_Word32 Add2Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add3Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add4Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add5Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add6Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add7Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add8Bits(const WebRtc_UWord8 bits); - WebRtc_Word32 Add16Bits(const WebRtc_UWord16 bits); - WebRtc_Word32 Add24Bits(const WebRtc_UWord32 bits); - WebRtc_Word32 Add32Bits(const WebRtc_UWord32 bits); - - // Exp-Golomb codes - WebRtc_Word32 AddUE(const WebRtc_UWord32 value); - -private: - WebRtc_Word32 AddPrefix(const WebRtc_UWord8 numZeros); - void AddSuffix(const WebRtc_UWord8 numBits, const WebRtc_UWord32 rest); - void Add1BitWithoutSanity(const WebRtc_UWord8 bit); - - WebRtc_UWord8* _data; - WebRtc_UWord32 _dataSize; - - WebRtc_UWord32 _byteOffset; - WebRtc_UWord8 _bitOffset; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_BUILDER_H_ diff --git a/modules/rtp_rtcp/source/H264/bitstream_parser.cc b/modules/rtp_rtcp/source/H264/bitstream_parser.cc deleted file mode 100644 index 79ec9671a..000000000 --- a/modules/rtp_rtcp/source/H264/bitstream_parser.cc +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "bitstream_parser.h" - -namespace webrtc { -BitstreamParser::BitstreamParser(const WebRtc_UWord8* data, const WebRtc_UWord32 dataLength) : - _data(data), - _dataLength(dataLength), - _byteOffset(0), - _bitOffset(0) -{ -} - // todo should we have any error codes from this? - -WebRtc_UWord8 -BitstreamParser::Get1Bit() -{ - WebRtc_UWord8 retVal = 0x1 & (_data[_byteOffset] >> (7-_bitOffset++)); - - // prepare next byte - if(_bitOffset == 8) - { - _bitOffset = 0; - _byteOffset++; - } - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get2Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get3Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get4Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get5Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get6Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 5); - retVal += (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get7Bits() -{ - WebRtc_UWord8 retVal = (Get1Bit() << 6); - retVal += (Get1Bit() << 5); - retVal += (Get1Bit() << 4); - retVal += (Get1Bit() << 3); - retVal += (Get1Bit() << 2); - retVal += (Get1Bit() << 1); - retVal += Get1Bit(); - return retVal; -} - -WebRtc_UWord8 -BitstreamParser::Get8Bits() -{ - WebRtc_UWord16 retVal; - - if(_bitOffset != 0) - { - // read 16 bits - retVal = (_data[_byteOffset] << 8)+ (_data[_byteOffset+1]) ; - retVal = retVal >> (8-_bitOffset); - } else - { - retVal = _data[_byteOffset]; - } - _byteOffset++; - return (WebRtc_UWord8)retVal; -} - -WebRtc_UWord16 -BitstreamParser::Get16Bits() -{ - WebRtc_UWord32 retVal; - - if(_bitOffset != 0) - { - // read 24 bits - retVal = (_data[_byteOffset] << 16) + (_data[_byteOffset+1] << 8) + (_data[_byteOffset+2]); - retVal = retVal >> (8-_bitOffset); - }else - { - // read 16 bits - retVal = (_data[_byteOffset] << 8) + (_data[_byteOffset+1]) ; - } - _byteOffset += 2; - return (WebRtc_UWord16)retVal; -} - -WebRtc_UWord32 -BitstreamParser::Get24Bits() -{ - WebRtc_UWord32 retVal; - - if(_bitOffset != 0) - { - // read 32 bits - retVal = (_data[_byteOffset] << 24) + (_data[_byteOffset+1] << 16) + (_data[_byteOffset+2] << 8) + (_data[_byteOffset+3]); - retVal = retVal >> (8-_bitOffset); - }else - { - // read 24 bits - retVal = (_data[_byteOffset] << 16) + (_data[_byteOffset+1] << 8) + (_data[_byteOffset+2]) ; - } - _byteOffset += 3; - return retVal & 0x00ffffff; // we need to clean up the high 8 bits -} - -WebRtc_UWord32 -BitstreamParser::Get32Bits() -{ - WebRtc_UWord32 retVal; - - if(_bitOffset != 0) - { - // read 40 bits - WebRtc_UWord64 tempVal = _data[_byteOffset]; - tempVal <<= 8; - tempVal += _data[_byteOffset+1]; - tempVal <<= 8; - tempVal += _data[_byteOffset+2]; - tempVal <<= 8; - tempVal += _data[_byteOffset+3]; - tempVal <<= 8; - tempVal += _data[_byteOffset+4]; - tempVal >>= (8-_bitOffset); - - retVal = WebRtc_UWord32(tempVal); - }else - { - // read 32 bits - retVal = (_data[_byteOffset]<< 24) + (_data[_byteOffset+1] << 16) + (_data[_byteOffset+2] << 8) + (_data[_byteOffset+3]) ; - } - _byteOffset += 4; - return retVal; -} - -// Exp-Golomb codes -/* - with "prefix" and "suffix" bits and assignment to codeNum ranges (informative) - Bit string form Range of codeNum - 1 0 - 0 1 x0 1..2 - 0 0 1 x1 x0 3..6 - 0 0 0 1 x2 x1 x0 7..14 - 0 0 0 0 1 x3 x2 x1 x0 15..30 - 0 0 0 0 0 1 x4 x3 x2 x1 x0 31..62 -*/ - -WebRtc_UWord32 -BitstreamParser::GetUE() -{ - WebRtc_UWord32 retVal = 0; - WebRtc_UWord8 numLeadingZeros = 0; - - while (Get1Bit() != 1) - { - numLeadingZeros++; - } - // prefix - retVal = (1 << numLeadingZeros) - 1; - - // suffix - while (numLeadingZeros) - { - retVal += (Get1Bit() << --numLeadingZeros); - } - return retVal; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/H264/bitstream_parser.h b/modules/rtp_rtcp/source/H264/bitstream_parser.h deleted file mode 100644 index 3d8f9ef9f..000000000 --- a/modules/rtp_rtcp/source/H264/bitstream_parser.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ - -#include "typedefs.h" - -namespace webrtc { -class BitstreamParser -{ -public: - BitstreamParser(const WebRtc_UWord8* data, const WebRtc_UWord32 dataLength); - - WebRtc_UWord8 Get1Bit(); - WebRtc_UWord8 Get2Bits(); - WebRtc_UWord8 Get3Bits(); - WebRtc_UWord8 Get4Bits(); - WebRtc_UWord8 Get5Bits(); - WebRtc_UWord8 Get6Bits(); - WebRtc_UWord8 Get7Bits(); - WebRtc_UWord8 Get8Bits(); - WebRtc_UWord16 Get16Bits(); - WebRtc_UWord32 Get24Bits(); - WebRtc_UWord32 Get32Bits(); - - // Exp-Golomb codes - WebRtc_UWord32 GetUE(); - -private: - const WebRtc_UWord8* _data; - const WebRtc_UWord32 _dataLength; - - WebRtc_UWord32 _byteOffset; - WebRtc_UWord8 _bitOffset; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_BITSTREAM_PARSER_H_ diff --git a/modules/rtp_rtcp/source/H264/h264_information.cc b/modules/rtp_rtcp/source/H264/h264_information.cc deleted file mode 100644 index cf6b549ea..000000000 --- a/modules/rtp_rtcp/source/H264/h264_information.cc +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include "h264_information.h" - -//#define DEBUG_SEI_MESSAGE 1 - -#ifdef DEBUG_SEI_MESSAGE - #include "bitstream_parser.h" - #include - #include - - WebRtc_UWord32 BitRateBPS(WebRtc_UWord16 x ) - { - return (x & 0x3fff) * WebRtc_UWord32(pow(10.0f,(2 + (x >> 14)))); - } - -#endif - -namespace webrtc { -H264Information::H264Information(const bool SVC) - : _SVC(SVC) - -{ -} - -H264Information::~H264Information() -{ - -} - -void -H264Information::Reset() -{ - _parsedLength = 0; - _remLength = 0; - _length = 0; - _info.numNALUs = 0; - _info.numLayers = 0; - - memset(_info.startCodeSize, 0, sizeof(_info.startCodeSize)); - memset(_info.payloadSize, 0, sizeof(_info.payloadSize)); - memset(_info.NRI, 0, sizeof(_info.NRI)); - memset(_info.type, 0, sizeof(_info.type)); - memset(_info.accLayerSize, 0, sizeof(_info.accLayerSize)); - - for (WebRtc_Word32 i = 0; i < KMaxNumberOfNALUs; i++) - { - _info.SVCheader[i].idr = 0; - _info.SVCheader[i].priorityID = 0; - _info.SVCheader[i].interLayerPred = 0; - _info.SVCheader[i].dependencyID = 0; - _info.SVCheader[i].qualityID = 0; - _info.SVCheader[i].temporalID = 0; - _info.SVCheader[i].useRefBasePic = 0; - _info.SVCheader[i].discardable = 0; - _info.SVCheader[i].output = 0; - - _info.PACSI[i].X = 0; - _info.PACSI[i].Y = 0; -// _info.PACSI[i].T = 0; - _info.PACSI[i].A = 0; - _info.PACSI[i].P = 0; - _info.PACSI[i].C = 0; - _info.PACSI[i].S = 0; - _info.PACSI[i].E = 0; - _info.PACSI[i].TL0picIDx = 0; - _info.PACSI[i].IDRpicID = 0; - _info.PACSI[i].DONC = 0; - _info.PACSI[i].numSEINALUs = 0; - _info.PACSI[i].NALlength = 5; - } -} - -/******************************************************************************* - * WebRtc_Word32 GetInfo(const WebRtc_UWord8* ptrEncodedBuffer, - * const WebRtc_UWord32 length, - * const H264Info*& ptrInfo); - * - * Gets information from an encoded stream. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Output: - * - ptrInfo : Pointer to struct with H.264 info. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::GetInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length, - const H264Info*& ptrInfo) -{ - if (!ptrEncodedBuffer || length < 4) - { - return -1; - } - - if (!HasInfo(length)) - { - if (-1 == FindInfo(ptrEncodedBuffer, length)) - { - Reset(); - return -1; - } - } - ptrInfo = &_info; - return 0; -} - -RtpVideoCodecTypes -H264Information::Type() -{ - if(_SVC) - { - return RTP_H264_SVCVideo; - } - return RTP_H264Video; -} - - -/******************************************************************************* - * bool HasInfo(const WebRtc_UWord32 length); - * - * Checks if information has already been stored for this encoded stream. - * - * Input: - * - length : Length in bytes of encoded stream. - * - * Return value: - * - true (false) : Information has (not) been stored. - */ - -bool -H264Information::HasInfo(const WebRtc_UWord32 length) -{ - if (!_info.numNALUs) - { - return false; - } - - // has info, make sure current length matches info length - if (length != _length) - { - Reset(); - return false; - } - - return true; -} - -/******************************************************************************* - * WebRtc_Word32 FindInfo(const WebRtc_UWord8* ptrEncodedBuffer, - * const WebRtc_UWord32 length); - * - * Parses the encoded stream. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::FindInfo(const WebRtc_UWord8* ptrEncodedBuffer, const WebRtc_UWord32 length) -{ - _ptrData = ptrEncodedBuffer; - _length = length; - _parsedLength = 0; - _remLength = length; - - do - { - // Get start code length - if (FindNALUStartCodeSize() == -1) - { - Reset(); - return -1; - } - - // Get NAL unit payload size - WebRtc_Word32 foundLast = FindNALU(); - if (foundLast == -1) - { - Reset(); - return -1; - } - - // Validate parsed length - if (_parsedLength > _length) - { - Reset(); - return -1; - } - - // Get NRI - GetNRI(); - - // Get type - if (FindNALUType() == -1) - { - Reset(); - return -1; - } - - // Set layer start end bit - SetLayerSEBit(foundLast); - - - // Last NAL unit found? - if (foundLast == 1) - { - if (_parsedLength != _length) - { - Reset(); - return -1; - } - _info.numNALUs++; - return SetLayerLengths(); - } - - // Next NAL unit - _ptrData += (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]); - _remLength -= (_info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]); - _info.numNALUs++; - - // Validate memory allocation - if (_info.numNALUs >= KMaxNumberOfNALUs) - { - Reset(); - return -1; - } - } - while(true); - - return 0; -} - -/******************************************************************************* - * WebRtc_Word32 FindNALUStartCodeSize(); - * - * Finds the start code length of the current NAL unit. - * - * Output: - * - _info.startCodeSize[currentNALU] : Start code length in bytes of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::FindNALUStartCodeSize() -{ - // NAL unit start code. Ex. {0,0,1} or {0,0,0,1} - for (WebRtc_UWord32 i = 2; i < _remLength; i++) - { - if (_ptrData[i] == 1 && _ptrData[i - 1] == 0 && _ptrData[i - 2] == 0) - { - _info.startCodeSize[_info.numNALUs] = WebRtc_UWord8(i + 1); - return 0; - } - } - return -1; -} - -/******************************************************************************* - * WebRtc_Word32 FindNALU(); - * - * Finds the length of the current NAL unit. - * - * Output: - * - _info.payloadSize[currentNALU] : Payload length in bytes of NAL unit - * (start code length not included). - * - _parsedLength : Current parsed length in bytes. - * - * Return value: - * - 1 : ok. Last NAL unit found. - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::FindNALU() -{ - for (WebRtc_UWord32 i = _info.startCodeSize[_info.numNALUs]; i < _remLength - 2; i += 2) - { - if (_ptrData[i] == 0) - { - WebRtc_Word32 size = 0; - if ((_ptrData[i + 1] == 1 && _ptrData[i - 1] == 0) || - (_ptrData[i + 2] == 1 && _ptrData[i + 1] == 0)) - { - // Found a header - // Reduce size by preceding zeroes - while (_ptrData[i - 1] == 0) - { - i--; - } - size = i; - } - if (size > 0) - { - _info.payloadSize[_info.numNALUs] = size - _info.startCodeSize[_info.numNALUs]; - _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]; - return 0; - } - } - } - // Last NAL unit - _info.payloadSize[_info.numNALUs] = _remLength - _info.startCodeSize[_info.numNALUs]; - if (_info.payloadSize[_info.numNALUs] > 0) - { - _parsedLength += _info.startCodeSize[_info.numNALUs] + _info.payloadSize[_info.numNALUs]; - return 1; - } - return -1; -} - -/******************************************************************************* - * void GetNRI(); - * - * Finds the NRI of the current NAL unit. - * - * Output: - * - _info.NRI[currentNALU] : NRI of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -void -H264Information::GetNRI() -{ - // NAL unit header (1 byte) - // --------------------------------- - // | start code |F|NRI| Type | - // --------------------------------- - - // NRI (2 bits) - nal_ref_idc. '00' - the NAL unit is not used to reconstruct reference pictures. - // >00 - the NAL unit is required to reconstruct reference pictures - // in the same layer, or contains a parameter set. - - - const WebRtc_UWord8 type = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f; - - // NALU type of 5, 7 and 8 shoud have NRI to b011 - if( type == 5 || - type == 7 || - type == 8) - { - _info.NRI[_info.numNALUs] = 0x60; - }else - { - _info.NRI[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x60; - } -} - - -/******************************************************************************* - * WebRtc_Word32 FindNALUType(); - * - * Finds the type of the current NAL unit. - * - * Output: - * - _info.type[currentNALU] : Type of NAL unit - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::FindNALUType() -{ - // NAL unit header (1 byte) - // --------------------------------- - // | start code |F|NRI| Type | - // --------------------------------- - - _info.type[_info.numNALUs] = _ptrData[_info.startCodeSize[_info.numNALUs]] & 0x1f; - - if (_info.type[_info.numNALUs] == 0) - { - return -1; - } - - // SVC NAL units, extended header - if (ParseSVCNALUHeader() == -1) - { - return -1; - } - - return 0; -} - -/******************************************************************************* - * WebRtc_Word32 ParseSVCNALUHeader(); - * - * Finds the extended header of the current NAL unit. Included for NAL unit types 14 and 20. - * - * Output: - * - _info.SVCheader[currentNALU] : SVC header of NAL unit. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H264Information::ParseSVCNALUHeader() -{ - if (_info.type[_info.numNALUs] == 5) - { - _info.SVCheader[_info.numNALUs].idr = 1; - } - if (_info.type[_info.numNALUs] == 6) - { - WebRtc_UWord32 seiPayloadSize; - do - { - // SEI message - seiPayloadSize = 0; - - WebRtc_UWord32 curByte = _info.startCodeSize[_info.numNALUs] + 1; - const WebRtc_UWord32 seiStartOffset = curByte; - - WebRtc_UWord32 seiPayloadType = 0; - while(_ptrData[curByte] == 0xff) - { - seiPayloadType += 255; - curByte++; - } - seiPayloadType += _ptrData[curByte++]; - - while(_ptrData[curByte] == 0xff) - { - seiPayloadSize += 255; - curByte++; - } - seiPayloadSize += _ptrData[curByte++]; - - if(_info.payloadSize[_info.numNALUs] < _info.startCodeSize[_info.numNALUs] + seiPayloadSize) - { - // sanity of remaining buffer - // return 0 since no one "need" SEI messages - assert(false); - return 0; - } - - if(seiPayloadType == 24) - { - // we add this to NALU 0 to be signaled in the first PACSI packet - _info.PACSI[0].numSEINALUs = 1; // we allways add this to NALU 0 to send it in the first packet - if(_info.PACSI[0].seiMessageLength[0] != seiPayloadSize) - { - _info.PACSI[0].seiMessageLength[0] = seiPayloadSize; - delete [] _info.PACSI[0].seiMessageData[0]; - _info.PACSI[0].seiMessageData[0] = new WebRtc_UWord8[seiPayloadSize]; - } - memcpy(_info.PACSI[0].seiMessageData[0], _ptrData+seiStartOffset, seiPayloadSize); - - _info.PACSI[0].NALlength += seiPayloadSize + 2; // additional 2 is the length - -#ifdef DEBUG_SEI_MESSAGE - const WebRtc_UWord8 numberOfLayers = 10; - WebRtc_UWord16 avgBitrate[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0}; - WebRtc_UWord16 maxBitrateLayer[numberOfLayers]= {0,0,0,0,0,0,0,0,0,0}; - WebRtc_UWord16 maxBitrateLayerRepresentation[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0}; - WebRtc_UWord16 maxBitrareCalcWindow[numberOfLayers] = {0,0,0,0,0,0,0,0,0,0}; - - BitstreamParser parserScalabilityInfo(_ptrData+curByte, seiPayloadSize); - - parserScalabilityInfo.Get1Bit(); // not used in futher parsing - const WebRtc_UWord8 priority_layer_info_present = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 priority_id_setting_flag = parserScalabilityInfo.Get1Bit(); - - WebRtc_UWord32 numberOfLayersMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 j = 0; j<= numberOfLayersMinusOne; j++) - { - printf("\nLayer ID:%d \n",parserScalabilityInfo.GetUE()); - printf("Priority ID:%d \n", parserScalabilityInfo.Get6Bits()); - printf("Discardable:%d \n", parserScalabilityInfo.Get1Bit()); - - printf("Dependency ID:%d \n", parserScalabilityInfo.Get3Bits()); - printf("Quality ID:%d \n", parserScalabilityInfo.Get4Bits()); - printf("Temporal ID:%d \n", parserScalabilityInfo.Get3Bits()); - - const WebRtc_UWord8 sub_pic_layer_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 sub_region_layer_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 iroi_division_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 profile_level_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 bitrate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 frm_rate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 frm_size_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 layer_dependency_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 parameter_sets_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 bitstream_restriction_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 exact_inter_layer_pred_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(sub_pic_layer_flag || iroi_division_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - } - const WebRtc_UWord8 layer_conversion_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 layer_output_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(profile_level_info_present_flag) - { - parserScalabilityInfo.Get24Bits(); - } - if(bitrate_info_present_flag) - { - // this is what we want - avgBitrate[j] = parserScalabilityInfo.Get16Bits(); - maxBitrateLayer[j] = parserScalabilityInfo.Get16Bits(); - maxBitrateLayerRepresentation[j] = parserScalabilityInfo.Get16Bits(); - maxBitrareCalcWindow[j] = parserScalabilityInfo.Get16Bits(); - - printf("\tAvg:%d\n", BitRateBPS(avgBitrate[j])); - printf("\tmaxBitrate:%d\n", BitRateBPS(maxBitrateLayer[j])); - printf("\tmaxBitrate rep:%d\n", BitRateBPS(maxBitrateLayerRepresentation[j])); - printf("\tCalcWindow:%d\n", maxBitrareCalcWindow[j]); - } - if(frm_rate_info_present_flag) - { - printf("\tFrame rate constant:%d\n", parserScalabilityInfo.Get2Bits()); // 0 = not constant, 1 = constant, 2 = maybe... - printf("\tFrame rate avg:%d\n", parserScalabilityInfo.Get16Bits()/256); - } - if(frm_size_info_present_flag || iroi_division_info_present_flag) - { - printf("\tFrame Width:%d\n",(parserScalabilityInfo.GetUE()+1)*16); - printf("\tFrame Height:%d\n",(parserScalabilityInfo.GetUE()+1)*16); - } - if(sub_region_layer_flag) - { - parserScalabilityInfo.GetUE(); - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - if(sub_pic_layer_flag) - { - parserScalabilityInfo.GetUE(); - } - if(iroi_division_info_present_flag) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - }else - { - const WebRtc_UWord32 numRoisMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= numRoisMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - } - } - if(layer_dependency_info_present_flag) - { - const WebRtc_UWord32 numDirectlyDependentLayers = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k < numDirectlyDependentLayers; k++) - { - parserScalabilityInfo.GetUE(); - } - } else - { - parserScalabilityInfo.GetUE(); - } - if(parameter_sets_info_present_flag) - { - const WebRtc_UWord32 numSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= numSeqParameterSetMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - } - const WebRtc_UWord32 numSubsetSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 l = 0; l <= numSubsetSeqParameterSetMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - } - const WebRtc_UWord32 numPicParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 m = 0; m <= numPicParameterSetMinusOne; m++) - { - parserScalabilityInfo.GetUE(); - } - }else - { - parserScalabilityInfo.GetUE(); - } - if(bitstream_restriction_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - if(layer_conversion_flag) - { - parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <2;k++) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - } - if(priority_layer_info_present) - { - const WebRtc_UWord32 prNumDidMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= prNumDidMinusOne;k++) - { - parserScalabilityInfo.Get3Bits(); - const WebRtc_UWord32 prNumMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 l = 0; l <= prNumMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - if(priority_id_setting_flag) - { - WebRtc_UWord8 priorityIdSettingUri; - WebRtc_UWord32 priorityIdSettingUriIdx = 0; - do - { - priorityIdSettingUri = parserScalabilityInfo.Get8Bits(); - } while (priorityIdSettingUri != 0); - } -#endif - } else - { - // not seiPayloadType 24 ignore - } - //check if we have more SEI in NALU - } while (_info.payloadSize[_info.numNALUs] > _info.startCodeSize[_info.numNALUs] + seiPayloadSize); - } - - // Extended NAL unit header (3 bytes). - // +---------------+---------------+---------------+ - // |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |R|I| PRID |N| DID | QID | TID |U|D|O| RR| - // +---------------+---------------+---------------+ - - // R - Reserved for future extensions (MUST be 1). Receivers SHOULD ignore the value of R. - // I - Is layer representation an IDR layer (1) or not (0). - // PRID - Priority identifier for the NAL unit. - // N - Specifies whether inter-layer prediction may be used for decoding the coded slice (1) or not (0). - // DID - Indicates the inter-layer coding dependency level of a layer representation. - // QID - Indicates the quality level of an MGS layer representation. - // TID - Indicates the temporal level of a layer representation. - // U - Use only reference base pictures during the inter prediction process (1) or not (0). - // D - Discardable flag. - // O - Output_flag. Affects the decoded picture output process as defined in Annex C of [H.264]. - // RR - Reserved_three_2bits (MUST be '11'). Receivers SHOULD ignore the value of RR. - - if (_info.type[_info.numNALUs] == 14 || - _info.type[_info.numNALUs] == 20) - { - WebRtc_UWord32 curByte = _info.startCodeSize[_info.numNALUs] + 1; - - if (_remLength < curByte + 3) - { - return -1; - } - - _info.SVCheader[_info.numNALUs].idr = (_ptrData[curByte] >> 6) & 0x01; - _info.SVCheader[_info.numNALUs].priorityID = (_ptrData[curByte++] & 0x3F); - - _info.SVCheader[_info.numNALUs].interLayerPred = (_ptrData[curByte] >> 7) & 0x01; - _info.SVCheader[_info.numNALUs].dependencyID = (_ptrData[curByte] >> 4) & 0x07; - _info.SVCheader[_info.numNALUs].qualityID = (_ptrData[curByte++] & 0x0F); - - _info.SVCheader[_info.numNALUs].temporalID = (_ptrData[curByte] >> 5) & 0x07; - _info.SVCheader[_info.numNALUs].useRefBasePic = (_ptrData[curByte] >> 4) & 0x01; - _info.SVCheader[_info.numNALUs].discardable = (_ptrData[curByte] >> 3) & 0x01; - _info.SVCheader[_info.numNALUs].output = (_ptrData[curByte] >> 2) & 0x01; - - if (_info.type[_info.numNALUs] == 14) - { - // inform the next NALU - memcpy(&(_info.SVCheader[_info.numNALUs+1]), &(_info.SVCheader[_info.numNALUs]), sizeof(_H264_SVC_NALUHeader)); - } - } - return 0; -} - - -/******************************************************************************* - * void SetLayerSEBit(); - * - * Sets start and end bits for the current NAL unit. - * - * Output: - * - _info.PACSI[currentNALU].S : First NAL unit in a layer (S = 1). - * - _info.PACSI[currentNALU].E : Last NAL unit in a layer (E = 1). - * - */ -void -H264Information::SetLayerSEBit(WebRtc_Word32 foundLast) -{ - if (_info.numNALUs == 0) - { - // First NAL unit - _info.PACSI[_info.numNALUs].S = 1; - } - - if (_info.numNALUs > 0) - { - if (_info.type[_info.numNALUs] != _info.type[_info.numNALUs-1] && - (_info.type[_info.numNALUs] == 20)) - { - // First layer in scalable extension - _info.PACSI[_info.numNALUs].S = 1; - _info.PACSI[_info.numNALUs-1].E = 1; - } - - if (_info.type[_info.numNALUs] == 20 && _info.type[_info.numNALUs-1] == 20) - { - if (_info.SVCheader[_info.numNALUs].temporalID != _info.SVCheader[_info.numNALUs-1].temporalID || - _info.SVCheader[_info.numNALUs].dependencyID != _info.SVCheader[_info.numNALUs-1].dependencyID || - _info.SVCheader[_info.numNALUs].qualityID != _info.SVCheader[_info.numNALUs-1].qualityID) - { - // New layer in scalable extension - _info.PACSI[_info.numNALUs].S = 1; - _info.PACSI[_info.numNALUs-1].E = 1; - } - } - } - - if (foundLast) - { - // Last NAL unit - _info.PACSI[_info.numNALUs].E = 1; - } - -} - -/******************************************************************************* - * WebRtc_Word32 SetLayerLengths(); - * - * Sets the accumulated layer length. - * - * Output: - * - _info.accLayerSize[currentLayer] : Size in bytes of layer: 0 - currentLayer. - * - * Return value: - * - 0 : ok - * - (-1) : Error - * - */ -WebRtc_Word32 -H264Information::SetLayerLengths() -{ - for (WebRtc_UWord32 curNALU = 0; curNALU < _info.numNALUs; curNALU++) - { - _info.accLayerSize[_info.numLayers] += _info.startCodeSize[curNALU] + _info.payloadSize[curNALU]; - - if (_info.PACSI[curNALU].E == 1) - { - _info.numLayers++; - if (curNALU == WebRtc_UWord32(_info.numNALUs - 1)) - { - break; - } - if (_info.numLayers >= KMaxNumberOfLayers) - { - Reset(); - return -1; - } - _info.accLayerSize[_info.numLayers] += _info.accLayerSize[_info.numLayers - 1]; - } - } - - if (_info.numLayers < 1 && _info.numLayers > KMaxNumberOfLayers) - { - Reset(); - return -1; - } - - if (_info.accLayerSize[_info.numLayers - 1] != WebRtc_Word32(_length)) - { - Reset(); - return -1; - } - - return 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/H264/h264_information.h b/modules/rtp_rtcp/source/H264/h264_information.h deleted file mode 100644 index c7f5214bd..000000000 --- a/modules/rtp_rtcp/source/H264/h264_information.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_H264_INFORMATION_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_H264_INFORMATION_H_ - -#include "VideoCodecInformation.h" -#include "typedefs.h" - -namespace webrtc { -enum -{ - KMaxNumberOfNALUs = 128, - KMaxNumberOfSEINALUs = 2, - KMaxNumberOfLayers = 16 -}; - -struct H264_SVC_NALUHeader -{ - H264_SVC_NALUHeader() - : - r(1), - idr(0), - priorityID(0), - interLayerPred(0), - dependencyID(0), - qualityID(0), - temporalID(0), - useRefBasePic(0), - discardable(0), - output(0), - rr(3), - length(3) - { - } - const WebRtc_UWord8 r; - WebRtc_UWord8 idr; - WebRtc_UWord8 priorityID; - WebRtc_UWord8 interLayerPred; - WebRtc_UWord8 dependencyID; - WebRtc_UWord8 qualityID; - WebRtc_UWord8 temporalID; - WebRtc_UWord8 useRefBasePic; - WebRtc_UWord8 discardable; - WebRtc_UWord8 output; - const WebRtc_UWord8 rr; - const WebRtc_UWord8 length; -}; - -class H264_PACSI_NALU -{ -public: - H264_PACSI_NALU() : - NALlength(5), - type(30), - X(0), - Y(0), -// T(0), - A(0), - P(0), - C(0), - S(0), - E(0), - TL0picIDx(0), - IDRpicID(0), - DONC(0), - numSEINALUs(0) - { - memset(seiMessageLength, 0, sizeof(seiMessageLength)); - memset(seiMessageData, 0, sizeof(seiMessageData)); - } - ~H264_PACSI_NALU() - { - for(int i = 0; i> 8); - databuffer[curByte++] = (WebRtc_UWord8)(pacsi.IDRpicID); - } - // Decoding order number - if (addDONC) // pacsi.T - { - databuffer[curByte++] = (WebRtc_UWord8)(DONC >> 8); - databuffer[curByte++] = (WebRtc_UWord8)(DONC); - } - - // SEI NALU - if(firstPacketInNALU) // IMPROVEMENT duplicate it to make sure it arrives... - { - // we only set this for NALU 0 to make sure we send it only once per frame - for (WebRtc_UWord32 i = 0; i < pacsi.numSEINALUs; i++) - { - // NALU size - databuffer[curByte++] = (WebRtc_UWord8)(pacsi.seiMessageLength[i] >> 8); - databuffer[curByte++] = (WebRtc_UWord8)(pacsi.seiMessageLength[i]); - - // NALU data - memcpy(databuffer + curByte, pacsi.seiMessageData[i], pacsi.seiMessageLength[i]); - curByte += pacsi.seiMessageLength[i]; - } - } - return curByte - startByte; -} - -WebRtc_Word32 -RTPSenderH264::SetH264RelaySequenceNumber(const WebRtc_UWord16 seqNum) -{ - _h264SVCRelaySequenceNumber = seqNum; - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SetH264RelayCompleteLayer(const bool complete) -{ - _h264SVCRelayLayerComplete = complete; - return 0; -} - -/* - 12 Filler data - - The only restriction of filler data NAL units within an - access unit is that they shall not precede the first VCL - NAL unit with the same access unit. -*/ -WebRtc_Word32 -RTPSenderH264::SendH264FillerData(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord16 bytesToSend, - const WebRtc_UWord32 ssrc) -{ - WebRtc_UWord16 fillerLength = bytesToSend - 12 - 1; - - if (fillerLength > WEBRTC_IP_PACKET_SIZE - 12 - 1) - { - return 0; - } - - if (fillerLength == 0) - { - // do not send an empty packet, will not reach JB - fillerLength = 1; - } - - // send codec valid data, H.264 has defined data which is binary 1111111 - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - dataBuffer[0] = static_cast(0x80); // version 2 - dataBuffer[1] = rtpHeader->header.payloadType; - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _rtpSender.IncrementSequenceNumber()); // get the current SequenceNumber and add by 1 after returning - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, rtpHeader->header.timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, rtpHeader->header.ssrc); - - // set filler NALU type - dataBuffer[12] = 12; // NRI field = 0, type 12 - - // fill with 0xff - memset(dataBuffer + 12 + 1, 0xff, fillerLength); - - return _rtpSender.SendToNetwork(dataBuffer, - fillerLength, - 12 + 1); -} - -WebRtc_Word32 -RTPSenderH264::SendH264FillerData(const WebRtc_UWord32 captureTimestamp, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 bytes - ) -{ - - const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - WebRtc_UWord16 maxLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - _rtpSender.RTPHeaderLength(); - - WebRtc_Word32 bytesToSend=bytes; - WebRtc_UWord16 fillerLength=0; - - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - while(bytesToSend>0) - { - fillerLength=maxLength; - if(fillerLength WEBRTC_IP_PACKET_SIZE - 12 - 1) - { - return 0; - } - - if (fillerLength == 0) - { - // do not send an empty packet, will not reach JB - fillerLength = 1; - } - - // send paded data - // correct seq num, time stamp and payloadtype - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false,captureTimestamp, true, true); - - // set filler NALU type - dataBuffer[12] = 12; // NRI field = 0, type 12 - - // send codec valid data, H.264 has defined data which is binary 1111111 - // fill with 0xff - memset(dataBuffer + 12 + 1, 0xff, fillerLength-1); - - if( _rtpSender.SendToNetwork(dataBuffer, - fillerLength, - 12)<0) - { - - return -1;; - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SendH264SVCRelayPacket(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRTPPacket, - const WebRtc_UWord16 incomingRTPPacketSize, - const WebRtc_UWord32 ssrc, - const bool higestLayer) -{ - if (rtpHeader->header.sequenceNumber != (WebRtc_UWord16)(_h264SVCRelaySequenceNumber + 1)) - { - // not continous, signal loss - _rtpSender.IncrementSequenceNumber(); - } - _h264SVCRelaySequenceNumber = rtpHeader->header.sequenceNumber; - - - if (rtpHeader->header.timestamp != _h264SVCRelayTimeStamp) - { - // new frame - _h264SVCRelayLayerComplete = false; - } - - if (rtpHeader->header.timestamp == _h264SVCRelayTimeStamp && - _h264SVCRelayLayerComplete) - { - // sanity, end of layer already sent - // Could happened for fragmented packet with missing PACSI info (PACSI packet reorded and received after packet it belongs to) - // fragmented packet has no layer info set (default info 0) - return 0; - } - _h264SVCRelayTimeStamp = rtpHeader->header.timestamp; - - // re-packetize H.264-SVC packets - // we keep the timestap unchanged - // make a copy and only change the SSRC and seqNum - - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - memcpy(dataBuffer, incomingRTPPacket, incomingRTPPacketSize); - - // _sequenceNumber initiated in Init() - // _ssrc initiated in constructor - - // re-write payload type - if(_h264SVCPayloadType != -1) - { - dataBuffer[1] &= kRtpMarkerBitMask; - dataBuffer[1] += _h264SVCPayloadType; - } - - // _sequenceNumber will not work for re-ordering by NACK from original sender - // engine responsible for this - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _rtpSender.IncrementSequenceNumber()); // get the current SequenceNumber and add by 1 after returning - //ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, ssrc); - - // how do we know it's the last relayed packet in a frame? - // 1) packets arrive in order, the engine manages that - // 2) highest layer that we relay - // 3) the end bit is set for the highest layer - - if(higestLayer && rtpHeader->type.Video.codecHeader.H264.relayE) - { - // set marker bit - dataBuffer[1] |= kRtpMarkerBitMask; - - // set relayed layer as complete - _h264SVCRelayLayerComplete = true; - } - return _rtpSender.SendToNetwork(dataBuffer, - incomingRTPPacketSize - rtpHeader->header.headerLength, - rtpHeader->header.headerLength); -} - -WebRtc_Word32 -RTPSenderH264::SendH264_STAP_A(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - bool& switchToFUA, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength) -{ - const WebRtc_Word32 H264_NALU_LENGTH = 2; - - WebRtc_UWord16 h264HeaderLength = 1; // normal header length - WebRtc_UWord16 maxPayloadLengthSTAP_A = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength - - h264HeaderLength - H264_NALU_LENGTH; - - WebRtc_Word32 dataOffset = rtpHeaderLength + h264HeaderLength; - WebRtc_UWord8 NRI = 0; - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - - if (ptrH264Info->payloadSize[idxNALU] > maxPayloadLengthSTAP_A) - { - // we need to fragment NAL switch to mode FU-A - switchToFUA = true; - } else - { - // combine as many NAL units in every IP packet - do - { - if(!_h264SendPPS_SPS) - { - // don't send NALU of type 7 and 8 SPS and PPS - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - } - if(ptrH264Info->payloadSize[idxNALU] + payloadBytesInPacket <= maxPayloadLengthSTAP_A) - { - if(ptrH264Info->NRI[idxNALU] > NRI) - { - NRI = ptrH264Info->NRI[idxNALU]; - } - // put NAL size into packet - dataBuffer[dataOffset] = (WebRtc_UWord8)(ptrH264Info->payloadSize[idxNALU] >> 8); - dataOffset++; - dataBuffer[dataOffset] = (WebRtc_UWord8)(ptrH264Info->payloadSize[idxNALU] & 0xff); - dataOffset++; - // Put payload in packet - memcpy(&dataBuffer[dataOffset], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - dataOffset += ptrH264Info->payloadSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - payloadBytesInPacket += (WebRtc_UWord16)(ptrH264Info->payloadSize[idxNALU] + H264_NALU_LENGTH); - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } else - { - // we don't fitt the next NALU in this packet - break; - } - idxNALU++; - }while(payloadBytesToSend); - } - - // sanity - // don't send empty packets - if (payloadBytesInPacket) - { - // add RTP header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, (payloadBytesToSend==0)?true:false, captureTimeStamp); - dataBuffer[rtpHeaderLength] = 24 + NRI; // STAP-A == 24 - WebRtc_UWord16 payloadLength = payloadBytesInPacket + h264HeaderLength; - - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength)) - { - return -1; - } - } - return 0; -} // end STAP-A - -// STAP-A for H.264 SVC -WebRtc_Word32 -RTPSenderH264::SendH264_STAP_A_PACSI(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - bool& switchToFUA, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength, - WebRtc_UWord16& decodingOrderNumber) -{ - const WebRtc_Word32 H264_NALU_LENGTH = 2; - - WebRtc_UWord16 h264HeaderLength = 1; // normal header length - WebRtc_UWord16 maxPayloadLengthSTAP_A = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - h264HeaderLength - H264_NALU_LENGTH; - WebRtc_Word32 dataOffset = rtpHeaderLength + h264HeaderLength; - WebRtc_UWord8 NRI = 0; - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - bool firstNALUNotIDR = true; //delta - - // Put PACSI NAL unit into packet - WebRtc_Word32 lengthPACSI = 0; - WebRtc_UWord32 PACSI_NALlength = ptrH264Info->PACSI[idxNALU].NALlength; - if (PACSI_NALlength > maxPayloadLengthSTAP_A) - { - return -1; - } - dataBuffer[dataOffset++] = (WebRtc_UWord8)(PACSI_NALlength >> 8); - dataBuffer[dataOffset++] = (WebRtc_UWord8)(PACSI_NALlength & 0xff); - - // end bit will be updated later, since another NALU in this packet might be the last - WebRtc_Word32 lengthPASCINALU = AddH264PACSINALU(true, - false, - ptrH264Info->PACSI[idxNALU], - ptrH264Info->SVCheader[idxNALU], - decodingOrderNumber, - dataBuffer, - dataOffset); - if (lengthPASCINALU <= 0) - { - return -1; - } - decodingOrderNumber++; - - lengthPACSI = H264_NALU_LENGTH + lengthPASCINALU; - maxPayloadLengthSTAP_A -= (WebRtc_UWord16)lengthPACSI; - if (ptrH264Info->payloadSize[idxNALU] > maxPayloadLengthSTAP_A) - { - // we need to fragment NAL switch to mode FU-A - switchToFUA = true; - return 0; - } - if(!ptrH264Info->SVCheader[idxNALU].idr) - { - firstNALUNotIDR = true; - } - - WebRtc_UWord32 layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - { - // combine as many NAL units in every IP packet, with the same priorityID - // Improvement we could allow several very small MGS NALU from different layers to be sent in one packet - - do - { - if(!_h264SendPPS_SPS) - { - // Don't send NALU of type 7 and 8 SPS and PPS, - // they could be signaled outofband - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - } - // don't send NALU type 6 (SEI message) not allowed when we send it in PACSI - if(ptrH264Info->type[idxNALU] == 6) - { - // SEI NALU Don't send, not allowed when we send it in PACSI - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - continue; - } - - const WebRtc_UWord32 layerNALU = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - // we need to break on a new layer - if( ptrH264Info->payloadSize[idxNALU] + payloadBytesInPacket <= maxPayloadLengthSTAP_A && - layerNALU == layer) - { - if(ptrH264Info->NRI[idxNALU] > NRI) - { - NRI = ptrH264Info->NRI[idxNALU]; - } - // put NAL size into packet - dataBuffer[dataOffset] = (WebRtc_UWord8)(ptrH264Info->payloadSize[idxNALU] >> 8); - dataOffset++; - dataBuffer[dataOffset] = (WebRtc_UWord8)(ptrH264Info->payloadSize[idxNALU] & 0xff); - dataOffset++; - // Put payload in packet - memcpy(&dataBuffer[dataOffset], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - dataOffset += ptrH264Info->payloadSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - payloadBytesInPacket += (WebRtc_UWord16)(ptrH264Info->payloadSize[idxNALU] + H264_NALU_LENGTH); - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } else - { - // we don't fitt the next NALU in this packet or, - // it's the next layer - - // check if we should send this NALU - // based on the layer - - if(_useHighestSendLayer && layerNALU != layer) - { - // we don't send this NALU due to it's a new layer - // check if we should send the next or if this is the last - const WebRtc_UWord8 dependencyQualityID = (ptrH264Info->SVCheader[idxNALU].dependencyID << 4) + ptrH264Info->SVCheader[idxNALU].qualityID; - - bool highestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[idxNALU].temporalID, - dependencyQualityID, - highestLayer) == false) - { - // will trigger markerbit and stop sending this frame - payloadBytesToSend = 0; - } - } - break; - } - idxNALU++; - - }while(payloadBytesToSend); - } - - // sanity, don't send empty packets - if (payloadBytesInPacket) - { - // add RTP header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, (payloadBytesToSend==0)?true:false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] = 24 + NRI; // STAP-A == 24 - - // NRI for PACSI - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 1] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 1] |= NRI; - - if(ptrH264Info->PACSI[idxNALU-1].E) - { - // update end bit - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 5] |= 0x01; - } - if(firstNALUNotIDR) - { - // we have to check if any of the NALU in this packet is an IDR NALU - bool setIBit = false; - for(int i = 0; i < idxNALU; i++) - { - if(ptrH264Info->SVCheader[i].idr) - { - setIBit = true; - break; - } - } - if(setIBit) - { - // update I bit - dataBuffer[rtpHeaderLength + H264_NALU_LENGTH + 2] |= 0x40; - } - } - const WebRtc_UWord16 payloadLength = payloadBytesInPacket + h264HeaderLength + (WebRtc_UWord16)lengthPACSI; - if(-1 == SendVideoPacket(frameType, - dataBuffer, - payloadLength, - rtpHeaderLength, - layer==0)) - { - return -1; - } - } - return 0; -} // end STAP-A - -WebRtc_Word32 -RTPSenderH264::SendH264_FU_A(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength, - WebRtc_UWord16& decodingOrderNumber, - const bool sendSVCPACSI) -{ - - // FUA for the rest of the frame - WebRtc_UWord16 maxPayloadLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength; - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - WebRtc_UWord32 payloadBytesRemainingInNALU = ptrH264Info->payloadSize[idxNALU]; - - bool isBaseLayer=false; - - if(payloadBytesRemainingInNALU > maxPayloadLength) - { - // we need to fragment NALU - const WebRtc_UWord16 H264_FUA_LENGTH = 2; // FU-a H.264 header is 2 bytes - - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - true, - false); - - WebRtc_UWord32 layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - isBaseLayer=(layer==0); - } - - // First packet - _rtpSender.BuildRTPheader(dataBuffer,payloadType, false, captureTimeStamp); - - WebRtc_UWord16 maxPayloadLengthFU_A = maxPayloadLength - H264_FUA_LENGTH ; - WebRtc_UWord8 fuaIndc = 28 + ptrH264Info->NRI[idxNALU]; - dataBuffer[rtpHeaderLength] = fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = (WebRtc_UWord8)(ptrH264Info->type[idxNALU] + 0x80)/*start*/; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength + H264_FUA_LENGTH], &data[ptrH264Info->startCodeSize[idxNALU]+1], maxPayloadLengthFU_A); - WebRtc_UWord16 payloadLength = maxPayloadLengthFU_A + H264_FUA_LENGTH; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength, isBaseLayer)) - { - return -1; - } - - //+1 is from the type that is coded into the FU-a header - data += maxPayloadLengthFU_A + 1 + ptrH264Info->startCodeSize[idxNALU]; // inc data ptr - payloadBytesToSend -= maxPayloadLengthFU_A+1+ptrH264Info->startCodeSize[idxNALU]; - payloadBytesRemainingInNALU -= maxPayloadLengthFU_A+1; - - // all non first/last packets - while(payloadBytesRemainingInNALU > maxPayloadLengthFU_A) - { - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - false, - false); - } - - // prepare next header - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] = (WebRtc_UWord8)fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU]; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength+H264_FUA_LENGTH], data, maxPayloadLengthFU_A); - payloadLength = maxPayloadLengthFU_A + H264_FUA_LENGTH; - - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - data += maxPayloadLengthFU_A; // inc data ptr - payloadBytesToSend -= maxPayloadLengthFU_A; - payloadBytesRemainingInNALU -= maxPayloadLengthFU_A; - dataBuffer[rtpHeaderLength] = fuaIndc; // FU-A indicator - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU]; // FU-A header - } - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - false, - true); // last packet in NALU - - if(_useHighestSendLayer && idxNALU+1 < ptrH264Info->numNALUs) - { - // not last NALU in frame - // check if it's the the next layer should not be sent - - // check if we should send the next or if this is the last - const WebRtc_UWord8 dependencyQualityID = (ptrH264Info->SVCheader[idxNALU+1].dependencyID << 4) + - ptrH264Info->SVCheader[idxNALU+1].qualityID; - - bool highestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[idxNALU+1].temporalID, - dependencyQualityID, - highestLayer) == false) - { - // will trigger markerbit and stop sending this frame - payloadBytesToSend = payloadBytesRemainingInNALU; - } - } - } - // last packet in NALU - _rtpSender.BuildRTPheader(dataBuffer, payloadType,(payloadBytesToSend == (WebRtc_Word32)payloadBytesRemainingInNALU)?true:false, captureTimeStamp); - dataBuffer[rtpHeaderLength+1] = ptrH264Info->type[idxNALU] + 0x40/*stop*/; // FU-A header - - memcpy(&dataBuffer[rtpHeaderLength+H264_FUA_LENGTH], data, payloadBytesRemainingInNALU); - payloadLength = (WebRtc_UWord16)payloadBytesRemainingInNALU + H264_FUA_LENGTH; - payloadBytesToSend -= payloadBytesRemainingInNALU; - if(payloadBytesToSend != 0) - { - data += payloadBytesRemainingInNALU; // inc data ptr - } - idxNALU++; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadLength, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - } else - { - // send NAL unit in singel mode - return SendH264_SingleMode(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - sendSVCPACSI); - } - // end FU-a - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SendH264_SingleMode(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength, - WebRtc_UWord16& decodingOrderNumber, - const bool sendSVCPACSI) -{ - // no H.264 header lenght in single mode - // we use WEBRTC_IP_PACKET_SIZE instead of the configured MTU since it's better to send fragmented UDP than not to send - const WebRtc_UWord16 maxPayloadLength = WEBRTC_IP_PACKET_SIZE - _rtpSender.PacketOverHead() - FECPacketOverhead() - rtpHeaderLength; - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - bool isBaseLayer=false; - - if(ptrH264Info->payloadSize[idxNALU] > maxPayloadLength) - { - return -3; - } - if(!_h264SendPPS_SPS) - { - // don't send NALU of type 7 and 8 SPS and PPS - if(ptrH264Info->type[idxNALU] == 7 || ptrH264Info->type[idxNALU] == 8) - { - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - idxNALU++; - return 0; - } - } - if(sendSVCPACSI) - { - SendH264_SinglePACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - true, - true); - - WebRtc_UWord32 layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - isBaseLayer=(layer==0); - } - - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength], &data[ptrH264Info->startCodeSize[idxNALU]], ptrH264Info->payloadSize[idxNALU]); - - WebRtc_UWord16 payloadBytesInPacket = (WebRtc_UWord16)ptrH264Info->payloadSize[idxNALU]; - payloadBytesToSend -= ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; // left to send - - // - _rtpSender.BuildRTPheader(dataBuffer,payloadType,(payloadBytesToSend ==0)?true:false, captureTimeStamp); - - dataBuffer[rtpHeaderLength] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength] |= ptrH264Info->NRI[idxNALU]; // nri - if(payloadBytesToSend > 0) - { - data += ptrH264Info->payloadSize[idxNALU] + ptrH264Info->startCodeSize[idxNALU]; - } - idxNALU++; - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, rtpHeaderLength,isBaseLayer)) - { - return -1; - } - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SendH264_SinglePACSI(const FrameType frameType, - const H264Info* ptrH264Info, - const WebRtc_UWord16 idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const bool firstPacketInNALU, - const bool lastPacketInNALU); -{ - // Send PACSI in single mode - WebRtc_UWord8 dataBuffer[WEBRTC_IP_PACKET_SIZE]; - WebRtc_UWord16 rtpHeaderLength = (WebRtc_UWord16)_rtpSender.BuildRTPheader(dataBuffer, payloadType,false, captureTimeStamp); - WebRtc_Word32 dataOffset = rtpHeaderLength; - - WebRtc_Word32 lengthPASCINALU = AddH264PACSINALU(firstPacketInNALU, - lastPacketInNALU, - ptrH264Info->PACSI[idxNALU], - ptrH264Info->SVCheader[idxNALU], - decodingOrderNumber, - dataBuffer, - dataOffset); - - if (lengthPASCINALU <= 0) - { - return -1; - } - decodingOrderNumber++; - - WebRtc_UWord16 payloadBytesInPacket = (WebRtc_UWord16)lengthPASCINALU; - - // Set payload header (first payload byte co-serves as the payload header) - dataBuffer[rtpHeaderLength] &= 0x1f; // zero out NRI field - dataBuffer[rtpHeaderLength] |= ptrH264Info->NRI[idxNALU]; // nri - - const WebRtc_UWord32 layer = (ptrH264Info->SVCheader[idxNALU].dependencyID << 16)+ - (ptrH264Info->SVCheader[idxNALU].qualityID << 8) + - ptrH264Info->SVCheader[idxNALU].temporalID; - - if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, rtpHeaderLength,layer==0)) - { - return -1; - } - return 0; -} - - - - -WebRtc_Word32 -RTPSenderH264::SendH264SVC(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - H264Information& h264Information, - WebRtc_UWord16& decodingOrderNumber) -{ - WebRtc_Word32 payloadBytesToSend = payloadSize; - const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - const H264Info* ptrH264Info = NULL; - if (h264Information.GetInfo(payloadData,payloadSize, ptrH264Info) == -1) - { - return -1; - } - if(_useHighestSendLayer) - { - // we need to check if we should drop the frame - // it could be a temporal layer (aka a temporal frame) - const WebRtc_UWord8 dependencyQualityID = (ptrH264Info->SVCheader[0].dependencyID << 4) + ptrH264Info->SVCheader[0].qualityID; - - bool dummyHighestLayer; - if(SendH264SVCLayer(frameType, - ptrH264Info->SVCheader[0].temporalID, - dependencyQualityID, - dummyHighestLayer) == false) - { - // skip send this frame - return 0; - } - } - - WebRtc_UWord16 idxNALU = 0; - while (payloadBytesToSend > 0) - { - bool switchToFUA = false; - if (SendH264_STAP_A_PACSI(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - switchToFUA, - payloadBytesToSend, - payloadData, - rtpHeaderLength, - decodingOrderNumber) != 0) - { - return -1; - } - if(switchToFUA) - { - // FU_A for this NALU - if (SendH264_FU_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - payloadData, - rtpHeaderLength, - true) != 0) - { - return -1; - } - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SetH264PacketizationMode(const H264PacketizationMode mode) -{ - _h264Mode = mode; - return 0; -} - -WebRtc_Word32 -RTPSenderH264::SetH264SendModeNALU_PPS_SPS(const bool dontSend) -{ - _h264SendPPS_SPS = !dontSend; - return 0; -} - -bool -RTPSenderH264::SendH264SVCLayer(const FrameType frameType, - const WebRtc_UWord8 temporalID, - const WebRtc_UWord8 dependencyQualityID, - bool& higestLayer) -{ - WebRtc_UWord8 dependencyID = dependencyQualityID >> 4; - - // keyframe required to switch between dependency layers not quality and temporal - if( _highestDependencyLayer != _highestDependencyLayerOld) - { - // we want to switch dependency layer - if(frameType == kVideoFrameKey) - { - // key frame we can change layer if it's correct layer - if(_highestDependencyLayer > _highestDependencyLayerOld) - { - // we want to switch up - // does this packet belong to a new layer? - - if( dependencyID > _highestDependencyLayerOld && - dependencyID <= _highestDependencyLayer) - { - _highestDependencyLayerOld = dependencyID; - _highestDependencyQualityIDOld = _highestDependencyQualityID; - - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - // relay - return true; - } - } - if(_highestDependencyLayer < _highestDependencyLayerOld) - { - // we want to switch down - // does this packet belong to a low layer? - if( dependencyID <= _highestDependencyLayer) - { - _highestDependencyLayerOld = dependencyID; - _highestDependencyQualityIDOld = _highestDependencyQualityID; - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - // relay - return true; - } - } - } else - { - // Delta frame and we are waiting to switch dependency layer - if(_highestDependencyLayer > _highestDependencyLayerOld) - { - // we want to switch up to a higher dependency layer - // use old setting until we get a key-frame - - // filter based on old dependency - // we could have allowed to add a MGS layer lower than the dependency ID - // but then we can't know the highest layer relayed we assume that the user - // will add one layer at a time - if( _highestTemporalLayer < temporalID || - _highestDependencyLayerOld < dependencyID || - _highestDependencyQualityIDOld < dependencyQualityID) - { - // drop - return false; - } - // highest layer based on old - if( dependencyID == _highestDependencyLayerOld && - dependencyQualityID == _highestDependencyQualityIDOld) - { - higestLayer = true; - } - } else - { - // we want to switch down to a lower dependency layer, - // use old setting, done bellow - // drop all temporal layers while waiting for the key-frame - if(temporalID > 0) - { - // drop - return false; - } - // we can't drop a lower MGS layer since this might depend on it - // however we can drop MGS layers larger than dependecyQualityId - // with dependency from old and quality 0 - if( _highestDependencyLayerOld < dependencyID || - (_highestDependencyQualityIDOld & 0xf0) < dependencyQualityID) - { - // drop - return false; - } - if( dependencyID == _highestDependencyLayerOld && - dependencyQualityID == (_highestDependencyQualityIDOld & 0xf0)) - { - higestLayer = true; - } - } - } - } else - { - // filter based on current state - if( _highestTemporalLayer < temporalID || - _highestDependencyLayer < dependencyID || - _highestDependencyQualityID < dependencyQualityID) - { - // drop - return false; - } - if( dependencyID == _highestDependencyLayer && - dependencyQualityID == _highestDependencyQualityID) - { - higestLayer = true; - } - } - return true; -} - -WebRtc_Word32 -RTPSenderH264::SetHighestSendLayer(const WebRtc_UWord8 dependencyQualityLayer, - const WebRtc_UWord8 temporalLayer) -{ - const WebRtc_UWord8 dependencyLayer = (dependencyQualityLayer >> 4); - - if(_highestDependencyLayerOld != _highestDependencyLayer) - { - // we have not switched to the new dependency yet - } else - { - if(_highestDependencyLayer == dependencyLayer) - { - // no change of dependency - // switch now _highestDependencyQualityIDOld - _highestDependencyQualityIDOld = dependencyQualityLayer; - }else - { - // change of dependency, update _highestDependencyQualityIDOld store as old - _highestDependencyQualityIDOld = _highestDependencyQualityID; - } - } - _useHighestSendLayer = true; - _highestDependencyLayer = dependencyLayer; - _highestDependencyQualityID = dependencyQualityLayer; - _highestTemporalLayer = temporalLayer; - return 0; -} - -WebRtc_Word32 -RTPSenderH264::HighestSendLayer(WebRtc_UWord8& dependencyQualityLayer, - WebRtc_UWord8& temporalLayer) -{ - if (!_useHighestSendLayer) - { - // No information set - return -1; - } - dependencyQualityLayer = _highestDependencyQualityID; - temporalLayer = _highestTemporalLayer; - return 0; -} -/* -* H.264 -*/ -WebRtc_Word32 -RTPSenderH264::SendH264(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - H264Information& h264Information) -{ - WebRtc_Word32 payloadBytesToSend = payloadSize; - const WebRtc_UWord8* data = payloadData; - bool switchToFUA = false; - const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - const H264Info* ptrH264Info = NULL; - if (h264Information.GetInfo(payloadData,payloadSize, ptrH264Info) == -1) - { - return -1; - } - WebRtc_UWord16 idxNALU = 0; - WebRtc_UWord16 DONCdummy = 0; - - while (payloadBytesToSend > 0) - { - switch(_h264Mode) - { - case H264_NON_INTERLEAVED_MODE: - - if(!switchToFUA) - { - if(SendH264_STAP_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - switchToFUA, - payloadBytesToSend, - data, - rtpHeaderLength) != 0) - { - return -1; - } - } - else - { - // FUA for the rest of the frame - if(SendH264_FU_A(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - DONCdummy) != 0) - { - return -1; - } - // try to go back to STAP_A - switchToFUA = false; - } - break; - case H264_SINGLE_NAL_MODE: - { - // modeSingleU - if(SendH264_SingleMode(frameType, - ptrH264Info, - idxNALU, - payloadType, - captureTimeStamp, - payloadBytesToSend, - data, - rtpHeaderLength, - DONCdummy) != 0) - { - return -1; - } - break; - } - case H264_INTERLEAVED_MODE: - // not supported - assert(false); - return -1; - } - } - return 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/H264/rtp_sender_h264.h b/modules/rtp_rtcp/source/H264/rtp_sender_h264.h deleted file mode 100644 index 564b8700d..000000000 --- a/modules/rtp_rtcp/source/H264/rtp_sender_h264.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ - -#include "typedefs.h" -#include "ModuleRTPRTCPConfig.h" -#include "rtp_rtcp_defines.h" -#include "h264_information.h" - -#include "RTPSender.h" - -namespace webrtc { -class RTPSenderH264 -{ -public: - WebRtc_Word32 SendH264(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - H264Information& h264Information); - - WebRtc_Word32 SendH264SVC(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - H264Information& h264Information); - - // H.264 AVC - WebRtc_Word32 SetH264PacketizationMode(const H264PacketizationMode mode); - - WebRtc_Word32 SetH264SendModeNALU_PPS_SPS(const bool dontSend); - - // H.264 SVC - WebRtc_Word32 SetHighestSendLayer(const WebRtc_UWord8 dependencyQualityLayer, - const WebRtc_UWord8 temporalLayer); - - WebRtc_Word32 HighestSendLayer(WebRtc_UWord8& dependencyQualityLayer, - WebRtc_UWord8& temporalLayer); - -protected: - RTPSenderH264(RTPSenderInterface* rtpSender); - virtual ~RTPSenderH264(); - - WebRtc_Word32 Init(); - - virtual WebRtc_UWord16 FECPacketOverhead() const = 0; - virtual RtpVideoCodecTypes VideoCodecType() const = 0; - - virtual WebRtc_Word32 SendVideoPacket(const FrameType frameType, - const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength, - bool baseLayerVideoPacket=false) = 0; - - - bool SendH264SVCLayer(const FrameType frameType, - const WebRtc_UWord8 temporalID, - const WebRtc_UWord8 dependencyQualityID, - bool& higestLayer); - - // H.264 SVC - WebRtc_Word32 AddH264PACSINALU(const bool firstPacketInNALU, - const bool lastPacketInNALU, - const H264_PACSI_NALU& paci, - const H264_SVC_NALUHeader& svc, - const WebRtc_UWord16 DONC, - WebRtc_UWord8* databuffer, - WebRtc_Word32& curByte) const; - - WebRtc_Word32 SendH264FillerData(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord16 bytesToSend, - const WebRtc_UWord32 ssrc); - - WebRtc_Word32 SendH264FillerData(const WebRtc_UWord32 captureTimestamp, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 bytesToSend); - - WebRtc_Word32 SendH264SVCRelayPacket(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRTPPacket, - const WebRtc_UWord16 incomingRTPPacketSize, - const WebRtc_UWord32 ssrc, - const bool higestLayer); - - WebRtc_Word32 SetH264RelaySequenceNumber(const WebRtc_UWord16 seqNum); - - WebRtc_Word32 SetH264RelayCompleteLayer(const bool complete); - - // H.264 - H264PacketizationMode _h264Mode; - bool _h264SendPPS_SPS; - - // H.264-SVC - WebRtc_Word8 _h264SVCPayloadType; - WebRtc_UWord16 _h264SVCRelaySequenceNumber; - WebRtc_UWord32 _h264SVCRelayTimeStamp; - bool _h264SVCRelayLayerComplete; - - -private: - // H.264 - WebRtc_Word32 SendH264_SingleMode(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength, - const bool sendSVCPACSI=false); - - WebRtc_Word32 SendH264_FU_A(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength, - const bool sendSVCPACSI = false); - - WebRtc_Word32 SendH264_STAP_A(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - bool& switchToFUA, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLength); - - WebRtc_Word32 SendH264_STAP_A_PACSI(const FrameType frameType, - const H264Info* ptrH264Info, - WebRtc_UWord16 &idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - bool& switchToFUA, - WebRtc_Word32 &payloadBytesToSend, - const WebRtc_UWord8*& data, - const WebRtc_UWord16 rtpHeaderLengh) - - WebRtc_Word32 SendH264_SinglePACSI(const FrameType frameType, - const H264Info* ptrH264Info, - const WebRtc_UWord16 idxNALU, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const bool firstPacketInNALU, - const bool lastPacketInNALU); - - bool AddH264SVCNALUHeader(const H264_SVC_NALUHeader& svc, - WebRtc_UWord8* databuffer, - WebRtc_Word32& curByte) const; - - RTPSenderInterface& _rtpSender; - - // relay - bool _useHighestSendLayer; - WebRtc_UWord8 _highestDependencyLayerOld; - WebRtc_UWord8 _highestDependencyQualityIDOld; - WebRtc_UWord8 _highestDependencyLayer; - WebRtc_UWord8 _highestDependencyQualityID; - WebRtc_UWord8 _highestTemporalLayer; - - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H264_RTP_SENDER_H264_H_ diff --git a/modules/rtp_rtcp/source/bandwidth_management.cc b/modules/rtp_rtcp/source/bandwidth_management.cc deleted file mode 100644 index 8dab36e3e..000000000 --- a/modules/rtp_rtcp/source/bandwidth_management.cc +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "bandwidth_management.h" -#include "trace.h" -#include "rtp_utility.h" -#include "rtp_rtcp_config.h" - -#include // sqrt() - -namespace webrtc { -BandwidthManagement::BandwidthManagement(const WebRtc_Word32 id) : - _id(id), - - _critsect(*CriticalSectionWrapper::CreateCriticalSection()), - - _lastPacketLossExtendedHighSeqNum(0), - _lastReportAllLost(false), - _lastLoss(0), - _accumulateLostPacketsQ8(0), - _accumulateExpectedPackets(0), - - _bitRate(0), - _minBitRateConfigured(0), - _maxBitRateConfigured(0), - - _last_fraction_loss(0), - _last_round_trip_time(0), - - // bandwidth estimate - _bwEstimateIncoming(0), - _bwEstimateIncomingMax(0), - _smoothedFractionLostQ4(-1), // indicate uninitialized - _sFLFactorQ4(14) // 0.875 in Q4 -{ -} - -BandwidthManagement::~BandwidthManagement() -{ - delete &_critsect; -} - -WebRtc_Word32 -BandwidthManagement::SetSendBitrate(const WebRtc_UWord32 startBitrate, - const WebRtc_UWord16 minBitrateKbit, - const WebRtc_UWord16 maxBitrateKbit) -{ - CriticalSectionScoped cs(_critsect); - - _bitRate = startBitrate; - _minBitRateConfigured = minBitrateKbit*1000; - if(maxBitrateKbit == 0) - { - // no max configured use 1Gbit/s - _maxBitRateConfigured = 1000000000; - } else - { - _maxBitRateConfigured = maxBitrateKbit*1000; - } - return 0; -} - -WebRtc_Word32 -BandwidthManagement::MaxConfiguredBitrate(WebRtc_UWord16& maxBitrateKbit) -{ - CriticalSectionScoped cs(_critsect); - - if(_maxBitRateConfigured == 0) - { - return -1; - } - maxBitrateKbit = (WebRtc_UWord16)(_maxBitRateConfigured/1000); - return 0; -} - -WebRtc_Word32 -BandwidthManagement::UpdateBandwidthEstimate(const WebRtc_UWord16 bandWidthMinKbit, - const WebRtc_UWord16 bandWidthMaxKbit, - WebRtc_UWord32& newBitrate, - WebRtc_UWord8& fractionLost, - WebRtc_UWord16& roundTripTime) -{ - newBitrate = 0; - CriticalSectionScoped cs(_critsect); - - _bwEstimateIncoming = bandWidthMinKbit*1000; - _bwEstimateIncomingMax = bandWidthMaxKbit*1000; - - if(_bitRate == 0) - { - // BandwidthManagement off - return -1; - } - - if (_bwEstimateIncoming > 0 && _bitRate > _bwEstimateIncoming) - { - _bitRate = _bwEstimateIncoming; - } - else - { - return -1; - } - newBitrate = _bitRate; - fractionLost = _last_fraction_loss; - roundTripTime = _last_round_trip_time; - return 0; -} - -WebRtc_Word32 -BandwidthManagement::UpdatePacketLoss(const WebRtc_UWord32 lastReceivedExtendedHighSeqNum, - const bool defaultCodec, - const WebRtc_UWord8 lossInput, - const WebRtc_UWord16 rtt, - WebRtc_UWord32& newBitrate, - WebRtc_UWord16& bwEstimateKbitMin, - WebRtc_UWord16& bwEstimateKbitMax) -{ - CriticalSectionScoped cs(_critsect); - - WebRtc_UWord8 loss = lossInput; // Local copy to modify. - - _last_fraction_loss = loss; - _last_round_trip_time = rtt; - - if(_bitRate == 0) - { - // BandwidthManagement off - return -1; - } - - // Check sequence number diff and weight loss report - if (_lastPacketLossExtendedHighSeqNum > 0 && - (lastReceivedExtendedHighSeqNum >= _lastPacketLossExtendedHighSeqNum)) - { - // This is not the first loss report and the sequence number is - // non-decreasing. Calculate sequence number diff. - WebRtc_UWord32 seqNumDiff = lastReceivedExtendedHighSeqNum - - _lastPacketLossExtendedHighSeqNum; - - // Check if this report and the last was 100% loss, then report - // 100% loss even though seqNumDiff is small. - // If not, go on with the checks. - if (!(_lastReportAllLost && loss == 255)) - { - _lastReportAllLost = (loss == 255); - - // Calculate number of lost packets. - // loss = 256 * numLostPackets / expectedPackets. - const int numLostPacketsQ8 = loss * seqNumDiff; - - // Accumulate reports. - _accumulateLostPacketsQ8 += numLostPacketsQ8; - _accumulateExpectedPackets += seqNumDiff; - - // Report loss if the total report is based on sufficiently - // many packets. - const int limitNumPackets = 10; - if (_accumulateExpectedPackets >= limitNumPackets) - { - loss = _accumulateLostPacketsQ8 / _accumulateExpectedPackets; - - // Reset accumulators - _accumulateLostPacketsQ8 = 0; - _accumulateExpectedPackets = 0; - } - else - { - // Report same loss as before and keep the accumulators until - // the next report. - loss = _lastLoss; - } - } - } - - // Keep for next time. - _lastLoss = loss; - - // Remember the sequence number until next time - _lastPacketLossExtendedHighSeqNum = lastReceivedExtendedHighSeqNum; - - bwEstimateKbitMax = static_cast(_bwEstimateIncomingMax / 1000); - bwEstimateKbitMin = static_cast(_bwEstimateIncoming / 1000); - - newBitrate = 0; - - if (defaultCodec) - { - return 0; - } - WebRtc_UWord32 bitRate = ShapeSimple(loss, rtt); - if(bitRate == 0) - { - // no change - return -1; - } - _bitRate = bitRate; - newBitrate = bitRate; - return 0; -} - -/* Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. - * The formula in RFC 3448, Section 3.1, is used. - */ - -// protected -WebRtc_Word32 -BandwidthManagement::CalcTFRCbps(WebRtc_Word16 avgPackSizeBytes, WebRtc_Word32 rttMs, WebRtc_Word32 packetLoss) -{ - if (avgPackSizeBytes <= 0 || rttMs <= 0 || packetLoss <= 0) - { - // input variables out of range; return -1 - return -1; - } - - double R = static_cast(rttMs)/1000; // RTT in seconds - int b = 1; // number of packets acknowledged by a single TCP acknowledgement; recommended = 1 - double t_RTO = 4.0 * R; // TCP retransmission timeout value in seconds; recommended = 4*R - double p = static_cast(packetLoss)/255; // packet loss rate in [0, 1) - double s = static_cast(avgPackSizeBytes); - - // calculate send rate in bytes/second - double X = s / (R * sqrt(2 * b * p / 3) + (t_RTO * (3 * sqrt( 3 * b * p / 8) * p * (1 + 32 * p * p)))); - - return (static_cast(X*8)); // bits/second -} - -/* -* Simple bandwidth estimation. Depends a lot on bwEstimateIncoming and packetLoss. -*/ -// protected -WebRtc_UWord32 -BandwidthManagement::ShapeSimple(WebRtc_Word32 packetLoss, WebRtc_Word32 rtt) -{ - WebRtc_UWord32 newBitRate = 0; - bool reducing = false; - - if (packetLoss > 5 && packetLoss <= 26) - { - // 2% - 10% - newBitRate = _bitRate; - } - else if (packetLoss > 26) - { - // 26/256 ~= 10% - // reduce rate: newRate = rate * (1 - 0.5*lossRate) - // packetLoss = 256*lossRate - newBitRate = (_bitRate * (512 - packetLoss)) / 512; - reducing = true; - } - else - { - // increase rate by 5% - newBitRate = static_cast(_bitRate * 1.05 + 0.5); - - // add 1 kbps extra, just to make sure that we do not get stuck - // (gives a little extra increase at low rates, negligible at higher rates) - newBitRate += 1000; - } - - // Calculate smoothed loss number - if (_smoothedFractionLostQ4 < 0) - { - // startup - _smoothedFractionLostQ4 = static_cast(packetLoss); - } - else - { - _smoothedFractionLostQ4 = ((_sFLFactorQ4 * _smoothedFractionLostQ4 + 8) >> 4) // Q4*Q4 = Q8; down to Q4 again with proper rounding - + (16 - _sFLFactorQ4) * static_cast(packetLoss); // Q4 * Q0 = Q4 - } - - // Calculate what rate TFRC would apply in this situation - //WebRtc_Word32 tfrcRate = CalcTFRCbps(1000, rtt, _smoothedFractionLostQ4 >> 4); // scale loss to Q0 (back to [0, 255]) - WebRtc_Word32 tfrcRate = CalcTFRCbps(1000, rtt, packetLoss); // scale loss to Q0 (back to [0, 255]) - - if (reducing && - tfrcRate > 0 && - static_cast(tfrcRate) > newBitRate) - { - // do not reduce further if rate is below TFRC rate - newBitRate = _bitRate; - } - - if (_bwEstimateIncoming > 0 && newBitRate > _bwEstimateIncoming) - { - newBitRate = _bwEstimateIncoming; - } - - if (newBitRate > _maxBitRateConfigured) - { - newBitRate = _maxBitRateConfigured; - } - - if (newBitRate < _minBitRateConfigured) - { - newBitRate = _minBitRateConfigured; - } - - return newBitRate; -} - -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/bandwidth_management.h b/modules/rtp_rtcp/source/bandwidth_management.h deleted file mode 100644 index 62a3f4e1a..000000000 --- a/modules/rtp_rtcp/source/bandwidth_management.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_BANDWIDTH_MANAGEMENT_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_BANDWIDTH_MANAGEMENT_H_ - -#include "typedefs.h" -#include "rtp_rtcp_config.h" -#include "critical_section_wrapper.h" - -/* -* FEC and NACK added bitrate is handled outside class -*/ - -namespace webrtc { -class BandwidthManagement -{ -public: - BandwidthManagement(const WebRtc_Word32 id); - ~BandwidthManagement(); - - WebRtc_Word32 UpdateBandwidthEstimate(const WebRtc_UWord16 bandWidthMinKbit, - const WebRtc_UWord16 bandWidthMaxKbit, - WebRtc_UWord32& newBitrate, - WebRtc_UWord8& fractionLost, - WebRtc_UWord16& roundTripTime); - - WebRtc_Word32 UpdatePacketLoss(const WebRtc_UWord32 lastReceivedExtendedHighSeqNum, - const bool defaultCodec, - const WebRtc_UWord8 lossInput, - const WebRtc_UWord16 rtt, - WebRtc_UWord32& newBitrate, - WebRtc_UWord16& bwEstimateKbitMin, - WebRtc_UWord16& bwEstimateKbitMax); - - WebRtc_Word32 AvailableBandwidth(WebRtc_UWord16& bandwidthKbit) const; - - WebRtc_Word32 SetSendBitrate(const WebRtc_UWord32 startBitrate, - const WebRtc_UWord16 minBitrateKbit, - const WebRtc_UWord16 maxBitrateKbit); - - WebRtc_Word32 MaxConfiguredBitrate(WebRtc_UWord16& maxBitrateKbit); - -protected: - WebRtc_UWord32 ShapeSimple(WebRtc_Word32 packetLoss, WebRtc_Word32 rtt); - - WebRtc_Word32 CalcTFRCbps(WebRtc_Word16 avgPackSizeBytes, - WebRtc_Word32 rttMs, - WebRtc_Word32 packetLoss); - -private: - WebRtc_Word32 _id; - - CriticalSectionWrapper& _critsect; - - // incoming filters - WebRtc_UWord32 _lastPacketLossExtendedHighSeqNum; - bool _lastReportAllLost; - WebRtc_UWord8 _lastLoss; - int _accumulateLostPacketsQ8; - int _accumulateExpectedPackets; - - // bitrate - WebRtc_UWord32 _bitRate; - WebRtc_UWord32 _minBitRateConfigured; - WebRtc_UWord32 _maxBitRateConfigured; - - WebRtc_UWord8 _last_fraction_loss; - WebRtc_UWord16 _last_round_trip_time; - - // bandwidth estimate - WebRtc_UWord32 _bwEstimateIncoming; - WebRtc_UWord32 _bwEstimateIncomingMax; - WebRtc_Word16 _smoothedFractionLostQ4; - WebRtc_Word16 _sFLFactorQ4; // forgetting factor for _smoothedFractionLostQ4 -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_BANDWIDTH_MANAGEMENT_H_ diff --git a/modules/rtp_rtcp/source/bitrate.cc b/modules/rtp_rtcp/source/bitrate.cc deleted file mode 100644 index ebeb89a44..000000000 --- a/modules/rtp_rtcp/source/bitrate.cc +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "Bitrate.h" -#include "rtp_utility.h" -#include "tick_util.h" - -#define BITRATE_AVERAGE_WINDOW 2000 - -namespace webrtc { -Bitrate::Bitrate() : - _packetRate(0), - _bitrate(0), - _bitrateNextIdx(0), - _timeLastRateUpdate(0), - _bytesCount(0), - _packetCount(0) -{ - memset(_packetRateArray, 0, sizeof(_packetRateArray)); - memset(_bitrateDiffMS, 0, sizeof(_bitrateDiffMS)); - memset(_bitrateArray, 0, sizeof(_bitrateArray)); -} - -void -Bitrate::Init() -{ - _packetRate = 0; - _bitrate = 0; - _timeLastRateUpdate = 0; - _bytesCount = 0; - _packetCount = 0; - _bitrateNextIdx = 0; - - memset(_packetRateArray, 0, sizeof(_packetRateArray)); - memset(_bitrateDiffMS, 0, sizeof(_bitrateDiffMS)); - memset(_bitrateArray, 0, sizeof(_bitrateArray)); -} - -void -Bitrate::Update(const WebRtc_Word32 bytes) -{ - _bytesCount += bytes; - _packetCount++; -} - -WebRtc_UWord32 -Bitrate::PacketRate() const -{ - return _packetRate; -} - -WebRtc_UWord32 -Bitrate::BitrateLast() const -{ - return _bitrate; -} - -WebRtc_UWord32 -Bitrate::BitrateNow() const -{ - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - WebRtc_UWord32 diffMS = now -_timeLastRateUpdate; - - if(diffMS > 10000) // 10 sec - { - // too high diff ignore - return _bitrate; // bits/s - } - WebRtc_UWord64 bitsSinceLastRateUpdate = 8*_bytesCount*1000; - - // have to consider the time when the measurement was done - // ((bits/sec * sec) + (bits)) / sec - WebRtc_UWord64 bitrate = (((WebRtc_UWord64)_bitrate * 1000) + bitsSinceLastRateUpdate)/(1000+diffMS); - return (WebRtc_UWord32)bitrate; -} - -void -Bitrate::Process() -{ - // triggered by timer - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - WebRtc_UWord32 diffMS = now -_timeLastRateUpdate; - - if(diffMS > 100) - { - if(diffMS > 10000) // 10 sec - { - // too high diff ignore - _timeLastRateUpdate = now; - _bytesCount = 0; - _packetCount = 0; - return; - } - _packetRateArray[_bitrateNextIdx] = (_packetCount*1000)/diffMS; - _bitrateArray[_bitrateNextIdx] = 8*((_bytesCount*1000)/diffMS); - // will overflow at ~34 Mbit/s - _bitrateDiffMS[_bitrateNextIdx] = diffMS; - _bitrateNextIdx++; - if(_bitrateNextIdx >= 10) - { - _bitrateNextIdx = 0; - } - - WebRtc_UWord32 sumDiffMS = 0; - WebRtc_UWord64 sumBitrateMS = 0; - WebRtc_UWord32 sumPacketrateMS = 0; - for(int i= 0; i <10; i++) - { - // sum of time - sumDiffMS += _bitrateDiffMS[i]; - sumBitrateMS += _bitrateArray[i] * _bitrateDiffMS[i]; - sumPacketrateMS += _packetRateArray[i] * _bitrateDiffMS[i]; - } - _timeLastRateUpdate = now; - _bytesCount = 0; - _packetCount = 0; - - _packetRate = sumPacketrateMS/sumDiffMS; - _bitrate = WebRtc_UWord32(sumBitrateMS / sumDiffMS); - } -} - -BitRateStats::BitRateStats() - :_dataSamples(), _accumulatedBytes(0) -{ -} - -BitRateStats::~BitRateStats() -{ - while (_dataSamples.size() > 0) - { - delete _dataSamples.front(); - _dataSamples.pop_front(); - } -} - -void BitRateStats::Init() -{ - _accumulatedBytes = 0; - while (_dataSamples.size() > 0) - { - delete _dataSamples.front(); - _dataSamples.pop_front(); - } -} - -void BitRateStats::Update(WebRtc_UWord32 packetSizeBytes, WebRtc_Word64 nowMs) -{ - // Find an empty slot for storing the new sample and at the same time - // accumulate the history. - _dataSamples.push_back(new DataTimeSizeTuple(packetSizeBytes, nowMs)); - _accumulatedBytes += packetSizeBytes; - EraseOld(nowMs); -} - -void BitRateStats::EraseOld(WebRtc_Word64 nowMs) -{ - while (_dataSamples.size() > 0) - { - if (nowMs - _dataSamples.front()->_timeCompleteMs > - BITRATE_AVERAGE_WINDOW) - { - // Delete old sample - _accumulatedBytes -= _dataSamples.front()->_sizeBytes; - delete _dataSamples.front(); - _dataSamples.pop_front(); - } - else - { - break; - } - } -} - -WebRtc_UWord32 BitRateStats::BitRate(WebRtc_Word64 nowMs) -{ - // Calculate the average bit rate the past BITRATE_AVERAGE_WINDOW ms. - // Removes any old samples from the list. - EraseOld(nowMs); - WebRtc_Word64 timeOldest = nowMs; - if (_dataSamples.size() > 0) - { - timeOldest = _dataSamples.front()->_timeCompleteMs; - } - // Update average bit rate - float denom = static_cast(nowMs - timeOldest); - if (nowMs == timeOldest) - { - // Calculate with a one second window when we haven't - // received more than one packet. - denom = 1000.0; - } - return static_cast(_accumulatedBytes * 8.0f * 1000.0f / - denom + 0.5f); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/bwe_defines.h b/modules/rtp_rtcp/source/bwe_defines.h deleted file mode 100644 index 8fdc985c8..000000000 --- a/modules/rtp_rtcp/source/bwe_defines.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_BWE_DEFINES_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_BWE_DEFINES_H_ - -#include "typedefs.h" - -#define BWE_MAX(a,b) ((a)>(b)?(a):(b)) -#define BWE_MIN(a,b) ((a)<(b)?(a):(b)) - -namespace webrtc { -enum BandwidthUsage -{ - kBwNormal, - kBwOverusing, - kBwUnderUsing -}; - -enum RateControlState -{ - kRcHold, - kRcIncrease, - kRcDecrease -}; - -enum RateControlRegion -{ - kRcNearMax, - kRcAboveMax, - kRcMaxUnknown -}; - -class RateControlInput -{ -public: - RateControlInput(BandwidthUsage bwState, - WebRtc_UWord32 incomingBitRate, - double noiseVar) : - _bwState(bwState), _incomingBitRate(incomingBitRate), _noiseVar(noiseVar) - {}; - - BandwidthUsage _bwState; - WebRtc_UWord32 _incomingBitRate; - double _noiseVar; -}; -} //namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_BWE_DEFINES_H_ diff --git a/modules/rtp_rtcp/source/dtmf_queue.cc b/modules/rtp_rtcp/source/dtmf_queue.cc deleted file mode 100644 index 72cd735dd..000000000 --- a/modules/rtp_rtcp/source/dtmf_queue.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "dtmf_queue.h" - -#include //memset - -namespace webrtc { -DTMFqueue::DTMFqueue(): - _DTMFCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _nextEmptyIndex(0) -{ - memset(_DTMFKey,0, sizeof(_DTMFKey)); - memset(_DTMFLen,0, sizeof(_DTMFLen)); - memset(_DTMFLevel,0, sizeof(_DTMFLevel)); -} - -DTMFqueue::~DTMFqueue() -{ - delete &_DTMFCritsect; -} - -WebRtc_Word32 -DTMFqueue::AddDTMF(WebRtc_UWord8 key, WebRtc_UWord16 len, WebRtc_UWord8 level) -{ - CriticalSectionScoped lock(_DTMFCritsect); - - if(_nextEmptyIndex >= DTMF_OUTBAND_MAX) - { - return -1; - } - WebRtc_Word32 index = _nextEmptyIndex; - _DTMFKey[index] = key; - _DTMFLen[index] = len; - _DTMFLevel[index] = level; - _nextEmptyIndex++; - return 0; -} - -WebRtc_Word8 -DTMFqueue::NextDTMF(WebRtc_UWord8* DTMFKey, WebRtc_UWord16* len, WebRtc_UWord8* level) -{ - CriticalSectionScoped lock(_DTMFCritsect); - - if(!PendingDTMF()) - { - return -1; - } - *DTMFKey=_DTMFKey[0]; - *len=_DTMFLen[0]; - *level=_DTMFLevel[0]; - - memmove(&(_DTMFKey[0]), &(_DTMFKey[1]), _nextEmptyIndex*sizeof(WebRtc_UWord8)); - memmove(&(_DTMFLen[0]), &(_DTMFLen[1]), _nextEmptyIndex*sizeof(WebRtc_UWord16)); - memmove(&(_DTMFLevel[0]), &(_DTMFLevel[1]), _nextEmptyIndex*sizeof(WebRtc_UWord8)); - - _nextEmptyIndex--; - return 0; -} - -bool -DTMFqueue::PendingDTMF() -{ - return(_nextEmptyIndex>0); -} - -void -DTMFqueue::ResetDTMF() -{ - _nextEmptyIndex = 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/dtmf_queue.h b/modules/rtp_rtcp/source/dtmf_queue.h deleted file mode 100644 index 67c47d0c7..000000000 --- a/modules/rtp_rtcp/source/dtmf_queue.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_ - -#include "typedefs.h" -#include "rtp_rtcp_config.h" - -#include "critical_section_wrapper.h" - -namespace webrtc { -class DTMFqueue -{ -public: - DTMFqueue(); - virtual ~DTMFqueue(); - - WebRtc_Word32 AddDTMF(WebRtc_UWord8 DTMFKey, WebRtc_UWord16 len, WebRtc_UWord8 level); - WebRtc_Word8 NextDTMF(WebRtc_UWord8* DTMFKey, WebRtc_UWord16 * len, WebRtc_UWord8 * level); - bool PendingDTMF(); - void ResetDTMF(); - -private: - CriticalSectionWrapper& _DTMFCritsect; - WebRtc_UWord8 _nextEmptyIndex; - WebRtc_UWord8 _DTMFKey[DTMF_OUTBAND_MAX]; - WebRtc_UWord16 _DTMFLen[DTMF_OUTBAND_MAX]; - WebRtc_UWord8 _DTMFLevel[DTMF_OUTBAND_MAX]; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_DTMF_QUEUE_H_ diff --git a/modules/rtp_rtcp/source/fec_private_tables.h b/modules/rtp_rtcp/source/fec_private_tables.h deleted file mode 100644 index a13e910bd..000000000 --- a/modules/rtp_rtcp/source/fec_private_tables.h +++ /dev/null @@ -1,25741 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_H_ - -// Ensure the tables have internal linkage, as we only require them in one file. -namespace -{ - const WebRtc_UWord8 mask10_1[2] = - { - 0xff, 0xc0 - }; - - const WebRtc_UWord8 mask10_10[20] = - { - 0x4c, 0x00, - 0x51, 0x00, - 0xa0, 0x40, - 0x04, 0xc0, - 0x03, 0x80, - 0x86, 0x00, - 0x29, 0x00, - 0x42, 0x40, - 0x98, 0x00, - 0x30, 0x80 - }; - - const WebRtc_UWord8 mask10_2[4] = - { - 0xee, 0x00, - 0x99, 0xc0 - }; - - const WebRtc_UWord8 mask10_3[6] = - { - 0xe6, 0x00, - 0x95, 0x80, - 0x78, 0x40 - }; - - const WebRtc_UWord8 mask10_4[8] = - { - 0xea, 0x00, - 0x33, 0x80, - 0xa4, 0xc0, - 0x1d, 0x40 - }; - - const WebRtc_UWord8 mask10_5[10] = - { - 0xc6, 0x00, - 0x23, 0x80, - 0x1a, 0x40, - 0x24, 0xc0, - 0x71, 0x00 - }; - - const WebRtc_UWord8 mask10_6[12] = - { - 0x0e, 0x00, - 0x33, 0x00, - 0x10, 0xc0, - 0x45, 0x40, - 0x88, 0x80, - 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask10_7[14] = - { - 0x46, 0x00, - 0x33, 0x00, - 0x10, 0xc0, - 0x0c, 0x40, - 0x28, 0x80, - 0x94, 0x00, - 0xc1, 0x00 - }; - - const WebRtc_UWord8 mask10_8[16] = - { - 0x2c, 0x00, - 0x81, 0x80, - 0xa0, 0x40, - 0x05, 0x40, - 0x18, 0x80, - 0xc2, 0x00, - 0x22, 0x80, - 0x50, 0x40 - }; - - const WebRtc_UWord8 mask10_9[18] = - { - 0x4e, 0x00, - 0xe3, 0x00, - 0x81, 0xc0, - 0x21, 0x40, - 0x52, 0x80, - 0xb4, 0x00, - 0x26, 0x80, - 0x58, 0x40, - 0x19, 0x80 - }; - - const WebRtc_UWord8 mask11_1[2] = - { - 0xff, 0xe0 - }; - - const WebRtc_UWord8 mask11_10[20] = - { - 0x64, 0x40, - 0x51, 0x40, - 0xa9, 0x00, - 0x04, 0xc0, - 0xd0, 0x00, - 0x82, 0x40, - 0x21, 0x20, - 0x0c, 0x20, - 0x4a, 0x00, - 0x12, 0xa0 - }; - - const WebRtc_UWord8 mask11_11[22] = - { - 0x46, 0x40, - 0x33, 0x20, - 0x99, 0x00, - 0x05, 0x80, - 0x80, 0xa0, - 0x84, 0x40, - 0x40, 0x60, - 0x0a, 0x80, - 0x68, 0x00, - 0x10, 0x20, - 0x30, 0x40 - }; - - const WebRtc_UWord8 mask11_2[4] = - { - 0xec, 0xc0, - 0x9b, 0xa0 - }; - - const WebRtc_UWord8 mask11_3[6] = - { - 0xca, 0xc0, - 0xf1, 0x40, - 0xb6, 0x20 - }; - - const WebRtc_UWord8 mask11_4[8] = - { - 0xc4, 0xc0, - 0x31, 0x60, - 0x4b, 0x20, - 0x2c, 0xa0 - }; - - const WebRtc_UWord8 mask11_5[10] = - { - 0x86, 0x80, - 0x23, 0x20, - 0x16, 0x20, - 0x4c, 0x20, - 0x41, 0xc0 - }; - - const WebRtc_UWord8 mask11_6[12] = - { - 0x64, 0x40, - 0x51, 0x40, - 0x0c, 0xa0, - 0xa1, 0x20, - 0x12, 0xa0, - 0x8a, 0x40 - }; - - const WebRtc_UWord8 mask11_7[14] = - { - 0x46, 0x40, - 0x33, 0x20, - 0x91, 0x80, - 0xa4, 0x20, - 0x50, 0xa0, - 0x84, 0xc0, - 0x09, 0x60 - }; - - const WebRtc_UWord8 mask11_8[16] = - { - 0x0c, 0x80, - 0x80, 0x60, - 0xa0, 0x80, - 0x05, 0x40, - 0x43, 0x00, - 0x1a, 0x00, - 0x60, 0x20, - 0x14, 0x20 - }; - - const WebRtc_UWord8 mask11_9[18] = - { - 0x46, 0x40, - 0x62, 0x60, - 0x8c, 0x00, - 0x01, 0x60, - 0x07, 0x80, - 0xa0, 0x80, - 0x18, 0xa0, - 0x91, 0x00, - 0x78, 0x00 - }; - - const WebRtc_UWord8 mask12_1[2] = - { - 0xff, 0xf0 - }; - - const WebRtc_UWord8 mask12_10[20] = - { - 0x51, 0x40, - 0x45, 0x10, - 0x80, 0xd0, - 0x24, 0x20, - 0x0a, 0x20, - 0x00, 0xe0, - 0xb8, 0x00, - 0x09, 0x10, - 0x56, 0x00, - 0xa2, 0x80 - }; - - const WebRtc_UWord8 mask12_11[22] = - { - 0x53, 0x60, - 0x21, 0x30, - 0x10, 0x90, - 0x00, 0x70, - 0x0c, 0x10, - 0x40, 0xc0, - 0x6a, 0x00, - 0x86, 0x00, - 0x24, 0x80, - 0x89, 0x00, - 0xc0, 0x20 - }; - - const WebRtc_UWord8 mask12_12[24] = - { - 0x10, 0x60, - 0x02, 0x30, - 0x40, 0x50, - 0x21, 0x80, - 0x81, 0x10, - 0x14, 0x80, - 0x98, 0x00, - 0x08, 0x90, - 0x62, 0x00, - 0x24, 0x20, - 0x8a, 0x00, - 0x84, 0x40 - }; - - const WebRtc_UWord8 mask12_2[4] = - { - 0xec, 0xc0, - 0x93, 0xb0 - }; - - const WebRtc_UWord8 mask12_3[6] = - { - 0x9b, 0x80, - 0x4f, 0x10, - 0x3c, 0x60 - }; - - const WebRtc_UWord8 mask12_4[8] = - { - 0x8b, 0x20, - 0x14, 0xb0, - 0x22, 0xd0, - 0x45, 0x50 - }; - - const WebRtc_UWord8 mask12_5[10] = - { - 0x53, 0x60, - 0x64, 0x20, - 0x0c, 0xc0, - 0x82, 0xa0, - 0x09, 0x30 - }; - - const WebRtc_UWord8 mask12_6[12] = - { - 0x51, 0x40, - 0xc5, 0x10, - 0x21, 0x80, - 0x12, 0x30, - 0x08, 0xe0, - 0x2e, 0x00 - }; - - const WebRtc_UWord8 mask12_7[14] = - { - 0x53, 0x60, - 0x21, 0x30, - 0x90, 0x90, - 0x02, 0x50, - 0x06, 0xa0, - 0x2c, 0x00, - 0x88, 0x60 - }; - - const WebRtc_UWord8 mask12_8[16] = - { - 0x20, 0x60, - 0x80, 0x30, - 0x42, 0x40, - 0x01, 0x90, - 0x14, 0x10, - 0x0a, 0x80, - 0x38, 0x00, - 0xc5, 0x00 - }; - - const WebRtc_UWord8 mask12_9[18] = - { - 0x53, 0x60, - 0xe4, 0x20, - 0x24, 0x40, - 0xa1, 0x10, - 0x18, 0x30, - 0x03, 0x90, - 0x8a, 0x10, - 0x04, 0x90, - 0x00, 0xe0 - }; - - const WebRtc_UWord8 mask13_1[2] = - { - 0xff, 0xf8 - }; - - const WebRtc_UWord8 mask13_10[20] = - { - 0xd1, 0x00, - 0x44, 0x50, - 0x10, 0x98, - 0xa0, 0x50, - 0x4a, 0x08, - 0x40, 0x30, - 0x80, 0x28, - 0x0c, 0x90, - 0x05, 0x88, - 0x62, 0x20 - }; - - const WebRtc_UWord8 mask13_11[22] = - { - 0x51, 0x20, - 0x22, 0x10, - 0x13, 0x40, - 0x25, 0x00, - 0x18, 0x18, - 0x0a, 0x20, - 0x88, 0x88, - 0x06, 0x80, - 0xe0, 0x20, - 0x84, 0x40, - 0x44, 0x18 - }; - - const WebRtc_UWord8 mask13_12[24] = - { - 0x28, 0x28, - 0x84, 0x50, - 0x60, 0x40, - 0x05, 0x48, - 0x02, 0x98, - 0x01, 0x30, - 0x48, 0x10, - 0x24, 0x80, - 0x94, 0x00, - 0x8a, 0x00, - 0x11, 0x80, - 0x52, 0x20 - }; - - const WebRtc_UWord8 mask13_13[26] = - { - 0x51, 0x20, - 0x66, 0x40, - 0x05, 0x48, - 0x81, 0x20, - 0x94, 0x00, - 0x30, 0x80, - 0x21, 0x10, - 0x03, 0xc0, - 0xe8, 0x00, - 0x0a, 0x10, - 0x80, 0x18, - 0x04, 0x90, - 0x08, 0xa8 - }; - - const WebRtc_UWord8 mask13_2[4] = - { - 0xec, 0xc0, - 0x1b, 0x38 - }; - - const WebRtc_UWord8 mask13_3[6] = - { - 0x99, 0xb0, - 0x46, 0xd8, - 0x37, 0x28 - }; - - const WebRtc_UWord8 mask13_4[8] = - { - 0x49, 0xb0, - 0x26, 0xd0, - 0x85, 0x68, - 0x52, 0x58 - }; - - const WebRtc_UWord8 mask13_5[10] = - { - 0x51, 0x30, - 0x66, 0x40, - 0x0c, 0x68, - 0xa1, 0xc0, - 0x22, 0x98 - }; - - const WebRtc_UWord8 mask13_6[12] = - { - 0xd1, 0x20, - 0x46, 0xd0, - 0x15, 0x48, - 0x21, 0x70, - 0x28, 0xc8, - 0xaa, 0x20 - }; - - const WebRtc_UWord8 mask13_7[14] = - { - 0x59, 0x20, - 0x26, 0x50, - 0xb1, 0x40, - 0x2b, 0x08, - 0x14, 0xc8, - 0xc8, 0x88, - 0x84, 0xb0 - }; - - const WebRtc_UWord8 mask13_8[16] = - { - 0x80, 0xa8, - 0x30, 0x90, - 0x16, 0x08, - 0x03, 0x30, - 0x44, 0x60, - 0x08, 0x18, - 0xd8, 0x00, - 0xa1, 0x40 - }; - - const WebRtc_UWord8 mask13_9[18] = - { - 0x59, 0x20, - 0x66, 0x40, - 0x14, 0x40, - 0x21, 0x48, - 0x02, 0xc8, - 0x94, 0x10, - 0x80, 0xa8, - 0x0a, 0x90, - 0x40, 0x18 - }; - - const WebRtc_UWord8 mask14_1[2] = - { - 0xff, 0xfc - }; - - const WebRtc_UWord8 mask14_10[20] = - { - 0xc0, 0xd4, - 0x1d, 0x40, - 0xd4, 0x08, - 0x02, 0x60, - 0x04, 0x28, - 0x20, 0x98, - 0x40, 0x44, - 0x08, 0x84, - 0x68, 0x00, - 0x23, 0x10 - }; - - const WebRtc_UWord8 mask14_11[22] = - { - 0x62, 0xd0, - 0x35, 0x20, - 0x14, 0x14, - 0xc5, 0x08, - 0x22, 0x0c, - 0x88, 0xb8, - 0x42, 0x54, - 0x28, 0xa4, - 0x94, 0x20, - 0x1b, 0x04, - 0x22, 0xc0 - }; - - const WebRtc_UWord8 mask14_12[24] = - { - 0x81, 0x04, - 0x40, 0x68, - 0x90, 0x24, - 0x28, 0x28, - 0x52, 0x10, - 0x41, 0x88, - 0x09, 0x30, - 0x48, 0x44, - 0x04, 0x44, - 0x0e, 0x80, - 0xa5, 0x90, - 0x12, 0x0c - }; - - const WebRtc_UWord8 mask14_13[26] = - { - 0x62, 0x54, - 0x34, 0x60, - 0x48, 0x04, - 0x00, 0xac, - 0x28, 0x08, - 0x81, 0x08, - 0x23, 0x04, - 0x06, 0x80, - 0x80, 0x14, - 0x30, 0x10, - 0x8c, 0x20, - 0x54, 0x00, - 0x80, 0xc0 - }; - - const WebRtc_UWord8 mask14_14[28] = - { - 0x40, 0x54, - 0x15, 0x40, - 0xc0, 0x04, - 0x28, 0x10, - 0x05, 0x0c, - 0x64, 0x80, - 0x81, 0x80, - 0x10, 0x98, - 0x84, 0x20, - 0x12, 0x30, - 0x62, 0x00, - 0x28, 0x60, - 0x0e, 0x08, - 0x10, 0x84 - }; - - const WebRtc_UWord8 mask14_2[4] = - { - 0xec, 0xe8, - 0x3b, 0x9c - }; - - const WebRtc_UWord8 mask14_3[6] = - { - 0xac, 0xd8, - 0x55, 0x6c, - 0x27, 0xb4 - }; - - const WebRtc_UWord8 mask14_4[8] = - { - 0x2c, 0xd8, - 0x93, 0x68, - 0x1a, 0xb4, - 0x47, 0x2c - }; - - const WebRtc_UWord8 mask14_5[10] = - { - 0x64, 0xd8, - 0xa5, 0x68, - 0x52, 0xb4, - 0x1d, 0xa8, - 0x9c, 0x54 - }; - - const WebRtc_UWord8 mask14_6[12] = - { - 0x4a, 0x54, - 0x95, 0x48, - 0x14, 0xb4, - 0x51, 0xa8, - 0x22, 0x6c, - 0x88, 0x8c - }; - - const WebRtc_UWord8 mask14_7[14] = - { - 0x62, 0x54, - 0xb9, 0x20, - 0x18, 0xb4, - 0x54, 0x98, - 0x06, 0x6c, - 0x85, 0x54, - 0xaa, 0x88 - }; - - const WebRtc_UWord8 mask14_8[16] = - { - 0xc0, 0x14, - 0x41, 0x60, - 0x88, 0x30, - 0x20, 0xa4, - 0x0a, 0x48, - 0x04, 0x98, - 0x94, 0x40, - 0x72, 0x00 - }; - - const WebRtc_UWord8 mask14_9[18] = - { - 0xa2, 0x54, - 0x34, 0x60, - 0x4a, 0x24, - 0x20, 0xa8, - 0x11, 0x84, - 0x49, 0x08, - 0x86, 0x0c, - 0x20, 0xd4, - 0x88, 0x48 - }; - - const WebRtc_UWord8 mask15_1[2] = - { - 0xff, 0xfe - }; - - const WebRtc_UWord8 mask15_10[20] = - { - 0xc0, 0xa0, - 0x15, 0x56, - 0x74, 0x40, - 0x00, 0x9c, - 0x01, 0x2c, - 0x44, 0x92, - 0x88, 0x50, - 0x20, 0xa4, - 0xaa, 0x04, - 0x02, 0x62 - }; - - const WebRtc_UWord8 mask15_11[22] = - { - 0x62, 0x22, - 0xf1, 0x10, - 0x10, 0x0e, - 0x10, 0xb0, - 0x24, 0x24, - 0x01, 0x12, - 0x00, 0xc4, - 0x04, 0xa2, - 0x02, 0x58, - 0x2b, 0x00, - 0x98, 0x40 - }; - - const WebRtc_UWord8 mask15_12[24] = - { - 0x88, 0x90, - 0x40, 0x54, - 0x82, 0x62, - 0x21, 0xa4, - 0x10, 0x64, - 0x44, 0x0a, - 0x10, 0xc8, - 0x4d, 0x2a, - 0x38, 0x02, - 0x17, 0x48, - 0x90, 0x84, - 0x72, 0x14 - }; - - const WebRtc_UWord8 mask15_13[26] = - { - 0x62, 0xa2, - 0x34, 0x44, - 0x40, 0x4a, - 0xc4, 0x04, - 0x08, 0x60, - 0x94, 0x12, - 0x88, 0xc0, - 0x21, 0x32, - 0xc1, 0x40, - 0x10, 0x68, - 0x06, 0x90, - 0x59, 0x00, - 0x0a, 0x0c - }; - - const WebRtc_UWord8 mask15_14[28] = - { - 0x40, 0x82, - 0x15, 0x54, - 0x88, 0x12, - 0xc0, 0x10, - 0x80, 0xa0, - 0x01, 0x22, - 0x40, 0x2c, - 0x22, 0x02, - 0x90, 0x04, - 0x12, 0x40, - 0x5d, 0x00, - 0x20, 0x54, - 0x86, 0x08, - 0x28, 0x88 - }; - - const WebRtc_UWord8 mask15_15[30] = - { - 0x62, 0x22, - 0x31, 0x10, - 0x58, 0x00, - 0x01, 0x12, - 0x88, 0x20, - 0x44, 0x02, - 0x29, 0x04, - 0x82, 0xa0, - 0x0a, 0x1a, - 0x11, 0xe0, - 0x84, 0x04, - 0x86, 0x40, - 0x00, 0x86, - 0x44, 0x48, - 0x10, 0x98 - }; - - const WebRtc_UWord8 mask15_2[4] = - { - 0xec, 0xea, - 0xbb, 0x9c - }; - - const WebRtc_UWord8 mask15_3[6] = - { - 0xac, 0x92, - 0x55, 0x4a, - 0x43, 0x36 - }; - - const WebRtc_UWord8 mask15_4[8] = - { - 0x25, 0xaa, - 0x95, 0x54, - 0x1a, 0x6a, - 0x43, 0xd4 - }; - - const WebRtc_UWord8 mask15_5[10] = - { - 0x64, 0xa2, - 0x25, 0x54, - 0x49, 0x68, - 0x53, 0x90, - 0x8e, 0x30 - }; - - const WebRtc_UWord8 mask15_6[12] = - { - 0x62, 0x8a, - 0x15, 0x54, - 0x4c, 0x46, - 0x52, 0x94, - 0x23, 0x64, - 0x8a, 0x58 - }; - - const WebRtc_UWord8 mask15_7[14] = - { - 0x62, 0xa2, - 0xb1, 0x14, - 0x18, 0x6a, - 0x44, 0xd4, - 0x13, 0x64, - 0x49, 0x1a, - 0x86, 0x8c - }; - - const WebRtc_UWord8 mask15_8[16] = - { - 0x90, 0x22, - 0x09, 0x50, - 0x00, 0x6a, - 0x20, 0x34, - 0x14, 0x44, - 0xc2, 0x10, - 0x00, 0xc6, - 0x65, 0x80 - }; - - const WebRtc_UWord8 mask15_9[18] = - { - 0x62, 0x22, - 0x24, 0x44, - 0xc0, 0x50, - 0x03, 0x0c, - 0x16, 0x28, - 0x89, 0x00, - 0x82, 0x90, - 0x08, 0xa4, - 0x90, 0x48 - }; - - const WebRtc_UWord8 mask16_1[2] = - { - 0xff, 0xff - }; - - const WebRtc_UWord8 mask16_10[20] = - { - 0x45, 0x51, - 0x10, 0xa2, - 0x01, 0x25, - 0x0b, 0x42, - 0xd8, 0x20, - 0x82, 0x8c, - 0x24, 0x4a, - 0x38, 0x18, - 0x2a, 0x25, - 0x84, 0x92 - }; - - const WebRtc_UWord8 mask16_11[22] = - { - 0x55, 0x55, - 0x2a, 0x22, - 0x31, 0x11, - 0x83, 0x42, - 0x06, 0x98, - 0x40, 0xe1, - 0x2c, 0x44, - 0xd8, 0x28, - 0x92, 0x81, - 0x84, 0x32, - 0x68, 0x0c - }; - - const WebRtc_UWord8 mask16_12[24] = - { - 0x84, 0x31, - 0x18, 0xa2, - 0x4e, 0x01, - 0x44, 0xc8, - 0x0e, 0x90, - 0x20, 0xcc, - 0x93, 0x40, - 0x2d, 0x10, - 0x31, 0x44, - 0xc0, 0x23, - 0x11, 0x25, - 0xe8, 0x80 - }; - - const WebRtc_UWord8 mask16_13[26] = - { - 0x45, 0x15, - 0x22, 0x22, - 0x96, 0x0c, - 0x0c, 0x50, - 0x62, 0x04, - 0x49, 0x06, - 0x11, 0x82, - 0x12, 0x38, - 0x40, 0x71, - 0xa8, 0x8a, - 0x08, 0xa1, - 0xa0, 0xc0, - 0xc5, 0x10 - }; - - const WebRtc_UWord8 mask16_14[28] = - { - 0x45, 0x51, - 0x22, 0x0a, - 0x84, 0xd0, - 0x0c, 0x8a, - 0x18, 0x06, - 0x30, 0x03, - 0x61, 0x08, - 0x40, 0x11, - 0x10, 0x2c, - 0x09, 0x60, - 0x00, 0x94, - 0x52, 0x40, - 0xa4, 0x24, - 0x82, 0x88 - }; - - const WebRtc_UWord8 mask16_15[30] = - { - 0x55, 0x11, - 0x22, 0x22, - 0x11, 0x11, - 0x80, 0x45, - 0x20, 0x1a, - 0x08, 0x68, - 0x22, 0x84, - 0x48, 0x09, - 0x07, 0x01, - 0x94, 0x20, - 0x82, 0x06, - 0x60, 0x48, - 0x89, 0x80, - 0x00, 0x8e, - 0x18, 0x22 - }; - - const WebRtc_UWord8 mask16_16[32] = - { - 0xa4, 0x10, - 0x01, 0x2a, - 0x06, 0x42, - 0x08, 0x68, - 0x81, 0x90, - 0x00, 0xf0, - 0x50, 0x05, - 0x20, 0x51, - 0x43, 0x08, - 0x68, 0x80, - 0x80, 0x0b, - 0x10, 0x4c, - 0x12, 0x30, - 0x40, 0x85, - 0x0e, 0x04, - 0x18, 0x12 - }; - - const WebRtc_UWord8 mask16_2[4] = - { - 0xae, 0xae, - 0x79, 0x79 - }; - - const WebRtc_UWord8 mask16_3[6] = - { - 0xad, 0x2d, - 0x76, 0x36, - 0x26, 0xdb - }; - - const WebRtc_UWord8 mask16_4[8] = - { - 0x55, 0x55, - 0xaa, 0xaa, - 0x35, 0x35, - 0xca, 0xca - }; - - const WebRtc_UWord8 mask16_5[10] = - { - 0x55, 0x55, - 0x2a, 0x2a, - 0x24, 0x25, - 0x84, 0xc8, - 0x10, 0xb6 - }; - - const WebRtc_UWord8 mask16_6[12] = - { - 0x51, 0x51, - 0x0a, 0x2a, - 0xa2, 0x15, - 0x84, 0x4a, - 0x30, 0x92, - 0x04, 0xac - }; - - const WebRtc_UWord8 mask16_7[14] = - { - 0x45, 0x51, - 0x22, 0x2a, - 0x91, 0x11, - 0x2e, 0x08, - 0x48, 0x34, - 0x90, 0x29, - 0x09, 0x86 - }; - - const WebRtc_UWord8 mask16_8[16] = - { - 0x20, 0x54, - 0x18, 0x88, - 0x84, 0x07, - 0x60, 0x48, - 0x12, 0x82, - 0x81, 0x41, - 0x40, 0x62, - 0x16, 0x30 - }; - - const WebRtc_UWord8 mask16_9[18] = - { - 0x55, 0x51, - 0x22, 0x2a, - 0x05, 0x85, - 0x09, 0x4a, - 0x84, 0x32, - 0xc0, 0x0d, - 0x20, 0xa6, - 0x1a, 0x09, - 0x44, 0x64 - }; - - const WebRtc_UWord8 mask17_1[6] = - { - 0xff, 0xff, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_10[60] = - { - 0x55, 0x8c, 0x80, 0x00, 0x00, 0x00, - 0xaa, 0x27, 0x00, 0x00, 0x00, 0x00, - 0xa5, 0x32, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x61, 0x80, 0x00, 0x00, 0x00, - 0x3c, 0x5c, 0x00, 0x00, 0x00, 0x00, - 0x8e, 0xcc, 0x00, 0x00, 0x00, 0x00, - 0x6a, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x32, 0x80, 0x00, 0x00, 0x00, - 0xd1, 0x25, 0x80, 0x00, 0x00, 0x00, - 0xc8, 0x02, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_11[66] = - { - 0x55, 0x8c, 0x80, 0x00, 0x00, 0x00, - 0xaa, 0x27, 0x00, 0x00, 0x00, 0x00, - 0xa5, 0x32, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x61, 0x80, 0x00, 0x00, 0x00, - 0x3c, 0x5c, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x84, 0x80, 0x00, 0x00, 0x00, - 0xa2, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x51, 0x80, 0x00, 0x00, 0x00, - 0x4a, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x89, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_12[72] = - { - 0x51, 0x84, 0x80, 0x00, 0x00, 0x00, - 0xa2, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x51, 0x80, 0x00, 0x00, 0x00, - 0x4a, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x89, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x8c, 0x80, 0x00, 0x00, 0x00, - 0xaa, 0x27, 0x00, 0x00, 0x00, 0x00, - 0xa5, 0x32, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x61, 0x80, 0x00, 0x00, 0x00, - 0x3c, 0x5c, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x35, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_13[78] = - { - 0x51, 0x84, 0x80, 0x00, 0x00, 0x00, - 0xa2, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x51, 0x80, 0x00, 0x00, 0x00, - 0x4a, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x89, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x81, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x28, 0x80, 0x00, 0x00, 0x00, - 0x83, 0x34, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_14[84] = - { - 0x15, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x81, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x28, 0x80, 0x00, 0x00, 0x00, - 0x83, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x84, 0x80, 0x00, 0x00, 0x00, - 0xa2, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x51, 0x80, 0x00, 0x00, 0x00, - 0x4a, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x89, 0x00, 0x00, 0x00, 0x00, - 0xb0, 0xde, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_15[90] = - { - 0x15, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x81, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x28, 0x80, 0x00, 0x00, 0x00, - 0x83, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x91, 0xc0, 0x80, 0x00, 0x00, 0x00, - 0x68, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x32, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x45, 0x00, 0x00, 0x00, 0x00, - 0xc4, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x1c, 0xa2, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_16[96] = - { - 0x25, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x91, 0xc0, 0x80, 0x00, 0x00, 0x00, - 0x68, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x32, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x45, 0x00, 0x00, 0x00, 0x00, - 0xc4, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x1c, 0xa2, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x8c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x81, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x28, 0x80, 0x00, 0x00, 0x00, - 0x83, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x1c, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_17[102] = - { - 0x25, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x91, 0xc0, 0x80, 0x00, 0x00, 0x00, - 0x68, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x32, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x45, 0x00, 0x00, 0x00, 0x00, - 0xc4, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x1c, 0xa2, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x8a, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x42, 0x80, 0x00, 0x00, 0x00, - 0x32, 0xa4, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x13, 0x00, 0x00, 0x00, 0x00, - 0xc4, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x1c, 0x88, 0x80, 0x00, 0x00, 0x00, - 0x3c, 0x09, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_2[12] = - { - 0xce, 0xce, 0x00, 0x00, 0x00, 0x00, - 0xb9, 0x39, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_3[18] = - { - 0xcd, 0xcc, 0x00, 0x00, 0x00, 0x00, - 0x97, 0x27, 0x00, 0x00, 0x00, 0x00, - 0xb8, 0xd1, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_4[24] = - { - 0xca, 0xec, 0x00, 0x00, 0x00, 0x00, - 0xa9, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0xb1, 0x80, 0x00, 0x00, 0x00, - 0x55, 0x5a, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_5[30] = - { - 0x55, 0x44, 0x80, 0x00, 0x00, 0x00, - 0x2a, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x25, 0xa1, 0x80, 0x00, 0x00, 0x00, - 0xe2, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x99, 0x98, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_6[36] = - { - 0xd1, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0xa2, 0xc5, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x30, 0x80, 0x00, 0x00, 0x00, - 0xca, 0x0a, 0x80, 0x00, 0x00, 0x00, - 0xa4, 0xaa, 0x00, 0x00, 0x00, 0x00, - 0x78, 0x15, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_7[42] = - { - 0x15, 0x44, 0x80, 0x00, 0x00, 0x00, - 0x8a, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x85, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x32, 0x0a, 0x80, 0x00, 0x00, 0x00, - 0x58, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x43, 0xc8, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_8[48] = - { - 0x64, 0x16, 0x00, 0x00, 0x00, 0x00, - 0xa2, 0xc2, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x60, 0x80, 0x00, 0x00, 0x00, - 0x4a, 0x85, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x29, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x94, 0xb0, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask17_9[54] = - { - 0x8e, 0xcc, 0x00, 0x00, 0x00, 0x00, - 0x6a, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x32, 0x80, 0x00, 0x00, 0x00, - 0xd1, 0x25, 0x80, 0x00, 0x00, 0x00, - 0x55, 0x8c, 0x80, 0x00, 0x00, 0x00, - 0xaa, 0x27, 0x00, 0x00, 0x00, 0x00, - 0xa5, 0x32, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x61, 0x80, 0x00, 0x00, 0x00, - 0x3c, 0x5c, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_1[6] = - { - 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_10[60] = - { - 0x8c, 0xc6, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x32, 0x99, 0x40, 0x00, 0x00, 0x00, - 0x61, 0xb0, 0xc0, 0x00, 0x00, 0x00, - 0x5c, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0xcc, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x15, 0x80, 0x00, 0x00, 0x00, - 0x32, 0x99, 0x40, 0x00, 0x00, 0x00, - 0x25, 0x92, 0xc0, 0x00, 0x00, 0x00, - 0xfd, 0x9d, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_11[66] = - { - 0x8c, 0xc6, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x32, 0x99, 0x40, 0x00, 0x00, 0x00, - 0x61, 0xb0, 0xc0, 0x00, 0x00, 0x00, - 0x5c, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x84, 0xc2, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x51, 0xa8, 0xc0, 0x00, 0x00, 0x00, - 0x1a, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x44, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_12[72] = - { - 0x84, 0xc2, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x51, 0xa8, 0xc0, 0x00, 0x00, 0x00, - 0x1a, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x44, 0x80, 0x00, 0x00, 0x00, - 0x8c, 0xc6, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x32, 0x99, 0x40, 0x00, 0x00, 0x00, - 0x61, 0xb0, 0xc0, 0x00, 0x00, 0x00, - 0x5c, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x5b, 0x0c, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_13[78] = - { - 0x84, 0xc2, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x51, 0xa8, 0xc0, 0x00, 0x00, 0x00, - 0x1a, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x44, 0x80, 0x00, 0x00, 0x00, - 0x8c, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x23, 0x80, 0x00, 0x00, 0x00, - 0x81, 0xc0, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0x89, 0x40, 0x00, 0x00, 0x00, - 0x58, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x40, 0x00, 0x00, 0x00, - 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_14[84] = - { - 0x8c, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x23, 0x80, 0x00, 0x00, 0x00, - 0x81, 0xc0, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0x89, 0x40, 0x00, 0x00, 0x00, - 0x58, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x40, 0x00, 0x00, 0x00, - 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x84, 0xc2, 0x40, 0x00, 0x00, 0x00, - 0x27, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x51, 0xa8, 0xc0, 0x00, 0x00, 0x00, - 0x1a, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x34, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x44, 0x80, 0x00, 0x00, 0x00, - 0x7f, 0x4f, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_15[90] = - { - 0x8c, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x23, 0x80, 0x00, 0x00, 0x00, - 0x81, 0xc0, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0x89, 0x40, 0x00, 0x00, 0x00, - 0x58, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x40, 0x00, 0x00, 0x00, - 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0xc0, 0xe0, 0x40, 0x00, 0x00, 0x00, - 0x06, 0x83, 0x40, 0x00, 0x00, 0x00, - 0xc8, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x51, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_16[96] = - { - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0xc0, 0xe0, 0x40, 0x00, 0x00, 0x00, - 0x06, 0x83, 0x40, 0x00, 0x00, 0x00, - 0xc8, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x8c, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x23, 0x80, 0x00, 0x00, 0x00, - 0x81, 0xc0, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0x89, 0x40, 0x00, 0x00, 0x00, - 0x58, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x40, 0x00, 0x00, 0x00, - 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0xef, 0xf2, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_17[102] = - { - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0xc0, 0xe0, 0x40, 0x00, 0x00, 0x00, - 0x06, 0x83, 0x40, 0x00, 0x00, 0x00, - 0xc8, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0x42, 0xa1, 0x40, 0x00, 0x00, 0x00, - 0xa4, 0x52, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x09, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0x88, 0xc4, 0x40, 0x00, 0x00, 0x00, - 0x09, 0x04, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_18[108] = - { - 0x4c, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0x42, 0xa1, 0x40, 0x00, 0x00, 0x00, - 0xa4, 0x52, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x09, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0x88, 0xc4, 0x40, 0x00, 0x00, 0x00, - 0x09, 0x04, 0x80, 0x00, 0x00, 0x00, - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0xc0, 0xe0, 0x40, 0x00, 0x00, 0x00, - 0x06, 0x83, 0x40, 0x00, 0x00, 0x00, - 0xc8, 0x64, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x22, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x98, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x51, 0x00, 0x00, 0x00, 0x00, - 0xd0, 0x03, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_2[12] = - { - 0xce, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x39, 0x9c, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_3[18] = - { - 0xcc, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x15, 0x80, 0x00, 0x00, 0x00, - 0x92, 0xc9, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_4[24] = - { - 0xec, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x33, 0x80, 0x00, 0x00, 0x00, - 0xb1, 0xd8, 0xc0, 0x00, 0x00, 0x00, - 0x5a, 0xad, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_5[30] = - { - 0x4c, 0xa6, 0x40, 0x00, 0x00, 0x00, - 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x19, 0xd0, 0xc0, 0x00, 0x00, 0x00, - 0x9c, 0x89, 0x40, 0x00, 0x00, 0x00, - 0xe3, 0x4c, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_6[36] = - { - 0xcc, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x62, 0x80, 0x00, 0x00, 0x00, - 0xb0, 0x98, 0x40, 0x00, 0x00, 0x00, - 0x8a, 0x85, 0x40, 0x00, 0x00, 0x00, - 0x29, 0x53, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0x0a, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_7[42] = - { - 0x44, 0xa2, 0x40, 0x00, 0x00, 0x00, - 0x23, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x48, 0x80, 0x00, 0x00, 0x00, - 0x0a, 0x85, 0x40, 0x00, 0x00, 0x00, - 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x06, 0x80, 0x00, 0x00, 0x00, - 0xe0, 0x64, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_8[48] = - { - 0x16, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0xc2, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x60, 0xb0, 0x40, 0x00, 0x00, 0x00, - 0x85, 0x42, 0x80, 0x00, 0x00, 0x00, - 0x4c, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x29, 0x14, 0x80, 0x00, 0x00, 0x00, - 0x11, 0x88, 0xc0, 0x00, 0x00, 0x00, - 0xb0, 0x58, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask18_9[54] = - { - 0x44, 0xa2, 0x40, 0x00, 0x00, 0x00, - 0x66, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x90, 0x49, 0x40, 0x00, 0x00, 0x00, - 0x01, 0xa5, 0x80, 0x00, 0x00, 0x00, - 0x0e, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x20, 0xd0, 0x40, 0x00, 0x00, 0x00, - 0xc2, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x29, 0x0c, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_1[6] = - { - 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_10[60] = - { - 0x8c, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x11, 0xc0, 0x00, 0x00, 0x00, - 0x32, 0x8d, 0x20, 0x00, 0x00, 0x00, - 0x61, 0x92, 0x60, 0x00, 0x00, 0x00, - 0x5c, 0x38, 0x80, 0x00, 0x00, 0x00, - 0xcc, 0x75, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x19, 0xc0, 0x00, 0x00, 0x00, - 0x32, 0xd2, 0x60, 0x00, 0x00, 0x00, - 0x25, 0x8e, 0xa0, 0x00, 0x00, 0x00, - 0x50, 0x88, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_11[66] = - { - 0x8c, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x11, 0xc0, 0x00, 0x00, 0x00, - 0x32, 0x8d, 0x20, 0x00, 0x00, 0x00, - 0x61, 0x92, 0x60, 0x00, 0x00, 0x00, - 0x5c, 0x38, 0x80, 0x00, 0x00, 0x00, - 0x84, 0x87, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x51, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x22, 0xa0, 0x00, 0x00, 0x00, - 0x68, 0x44, 0x40, 0x00, 0x00, 0x00, - 0x89, 0x70, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_12[72] = - { - 0x84, 0x87, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x51, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x22, 0xa0, 0x00, 0x00, 0x00, - 0x68, 0x44, 0x40, 0x00, 0x00, 0x00, - 0x89, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x8c, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x11, 0xc0, 0x00, 0x00, 0x00, - 0x32, 0x8d, 0x20, 0x00, 0x00, 0x00, - 0x61, 0x92, 0x60, 0x00, 0x00, 0x00, - 0x5c, 0x38, 0x80, 0x00, 0x00, 0x00, - 0x90, 0xc8, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_13[78] = - { - 0x84, 0x87, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x51, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x22, 0xa0, 0x00, 0x00, 0x00, - 0x68, 0x44, 0x40, 0x00, 0x00, 0x00, - 0x89, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x8c, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x81, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x12, 0x86, 0x20, 0x00, 0x00, 0x00, - 0x58, 0x14, 0x40, 0x00, 0x00, 0x00, - 0x28, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x60, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_14[84] = - { - 0x8c, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x81, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x12, 0x86, 0x20, 0x00, 0x00, 0x00, - 0x58, 0x14, 0x40, 0x00, 0x00, 0x00, - 0x28, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x60, 0x80, 0x00, 0x00, 0x00, - 0x84, 0x87, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x51, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x22, 0xa0, 0x00, 0x00, 0x00, - 0x68, 0x44, 0x40, 0x00, 0x00, 0x00, - 0x89, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x6e, 0x27, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_15[90] = - { - 0x8c, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x81, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x12, 0x86, 0x20, 0x00, 0x00, 0x00, - 0x58, 0x14, 0x40, 0x00, 0x00, 0x00, - 0x28, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x60, 0x80, 0x00, 0x00, 0x00, - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xc0, 0x00, 0x00, 0x00, - 0xc0, 0xd0, 0x20, 0x00, 0x00, 0x00, - 0x06, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0xc8, 0x0c, 0x40, 0x00, 0x00, 0x00, - 0x45, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x91, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x28, 0x20, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_16[96] = - { - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xc0, 0x00, 0x00, 0x00, - 0xc0, 0xd0, 0x20, 0x00, 0x00, 0x00, - 0x06, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0xc8, 0x0c, 0x40, 0x00, 0x00, 0x00, - 0x45, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x91, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x8c, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x81, 0x88, 0x60, 0x00, 0x00, 0x00, - 0x12, 0x86, 0x20, 0x00, 0x00, 0x00, - 0x58, 0x14, 0x40, 0x00, 0x00, 0x00, - 0x28, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x34, 0x60, 0x80, 0x00, 0x00, 0x00, - 0x7e, 0x75, 0xe0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_17[102] = - { - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xc0, 0x00, 0x00, 0x00, - 0xc0, 0xd0, 0x20, 0x00, 0x00, 0x00, - 0x06, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0xc8, 0x0c, 0x40, 0x00, 0x00, 0x00, - 0x45, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x91, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x4c, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x71, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xe0, 0x00, 0x00, 0x00, - 0x42, 0x90, 0xa0, 0x00, 0x00, 0x00, - 0xa4, 0x29, 0x40, 0x00, 0x00, 0x00, - 0x13, 0x5a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x93, 0x40, 0x00, 0x00, 0x00, - 0x88, 0xac, 0x20, 0x00, 0x00, 0x00, - 0x09, 0x0c, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_18[108] = - { - 0x4c, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x71, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xe0, 0x00, 0x00, 0x00, - 0x42, 0x90, 0xa0, 0x00, 0x00, 0x00, - 0xa4, 0x29, 0x40, 0x00, 0x00, 0x00, - 0x13, 0x5a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x93, 0x40, 0x00, 0x00, 0x00, - 0x88, 0xac, 0x20, 0x00, 0x00, 0x00, - 0x09, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x2c, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xc0, 0x00, 0x00, 0x00, - 0xc0, 0xd0, 0x20, 0x00, 0x00, 0x00, - 0x06, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0xc8, 0x0c, 0x40, 0x00, 0x00, 0x00, - 0x45, 0x61, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x91, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x51, 0x97, 0x20, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_19[114] = - { - 0x4c, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x71, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x40, 0xe0, 0x00, 0x00, 0x00, - 0x42, 0x90, 0xa0, 0x00, 0x00, 0x00, - 0xa4, 0x29, 0x40, 0x00, 0x00, 0x00, - 0x13, 0x5a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x93, 0x40, 0x00, 0x00, 0x00, - 0x88, 0xac, 0x20, 0x00, 0x00, 0x00, - 0x09, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x4c, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x28, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x50, 0x20, 0x00, 0x00, 0x00, - 0x42, 0x82, 0x60, 0x00, 0x00, 0x00, - 0xa4, 0x01, 0xc0, 0x00, 0x00, 0x00, - 0x13, 0x43, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x94, 0x80, 0x00, 0x00, 0x00, - 0x88, 0xa1, 0x20, 0x00, 0x00, 0x00, - 0x09, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0xcd, 0x98, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_2[12] = - { - 0xce, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x39, 0xcc, 0xe0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_3[18] = - { - 0xcc, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x2c, 0xc0, 0x00, 0x00, 0x00, - 0x92, 0xd2, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_4[24] = - { - 0xec, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x19, 0xc0, 0x00, 0x00, 0x00, - 0xb1, 0xcc, 0x60, 0x00, 0x00, 0x00, - 0x5a, 0x96, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_5[30] = - { - 0x4c, 0xe7, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x31, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0xcc, 0x60, 0x00, 0x00, 0x00, - 0x92, 0xa6, 0xa0, 0x00, 0x00, 0x00, - 0xb8, 0x99, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_6[36] = - { - 0x4c, 0x36, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x68, 0x80, 0x00, 0x00, 0x00, - 0x30, 0xd0, 0x60, 0x00, 0x00, 0x00, - 0x8a, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0x26, 0x0b, 0x40, 0x00, 0x00, 0x00, - 0x95, 0x45, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_7[42] = - { - 0xc4, 0xa3, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x19, 0x80, 0x00, 0x00, 0x00, - 0x91, 0x1c, 0x20, 0x00, 0x00, 0x00, - 0x4a, 0x82, 0xa0, 0x00, 0x00, 0x00, - 0x34, 0x49, 0x40, 0x00, 0x00, 0x00, - 0x8b, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0xc8, 0x24, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_8[48] = - { - 0x16, 0x13, 0x80, 0x00, 0x00, 0x00, - 0xc2, 0x44, 0xc0, 0x00, 0x00, 0x00, - 0x60, 0xe8, 0x20, 0x00, 0x00, 0x00, - 0x85, 0x12, 0x60, 0x00, 0x00, 0x00, - 0xcc, 0x21, 0x40, 0x00, 0x00, 0x00, - 0x29, 0x63, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x98, 0xc0, 0x00, 0x00, 0x00, - 0xb0, 0x0c, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask19_9[54] = - { - 0x44, 0xa7, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x70, 0x80, 0x00, 0x00, 0x00, - 0x12, 0xc0, 0xe0, 0x00, 0x00, 0x00, - 0xc3, 0x10, 0xa0, 0x00, 0x00, 0x00, - 0x8c, 0x29, 0x40, 0x00, 0x00, 0x00, - 0x11, 0x5b, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x93, 0x40, 0x00, 0x00, 0x00, - 0xa2, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x0c, 0xe0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask1_1[2] = - { - 0x80, 0x00 - }; - - const WebRtc_UWord8 mask20_1[6] = - { - 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_10[60] = - { - 0x4c, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x40, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0x80, 0xe0, 0x00, 0x00, 0x00, - 0x86, 0x21, 0x80, 0x00, 0x00, 0x00, - 0x29, 0x0a, 0x40, 0x00, 0x00, 0x00, - 0x42, 0x50, 0x90, 0x00, 0x00, 0x00, - 0x98, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x8c, 0x20, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_11[66] = - { - 0xc6, 0x31, 0x80, 0x00, 0x00, 0x00, - 0x23, 0x88, 0xe0, 0x00, 0x00, 0x00, - 0x1a, 0x46, 0x90, 0x00, 0x00, 0x00, - 0x24, 0xc9, 0x30, 0x00, 0x00, 0x00, - 0x71, 0x1c, 0x40, 0x00, 0x00, 0x00, - 0x0e, 0x03, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x45, 0x51, 0x50, 0x00, 0x00, 0x00, - 0x88, 0xa2, 0x20, 0x00, 0x00, 0x00, - 0xe0, 0x38, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_12[72] = - { - 0x0e, 0x03, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x45, 0x51, 0x50, 0x00, 0x00, 0x00, - 0x88, 0xa2, 0x20, 0x00, 0x00, 0x00, - 0xe0, 0x38, 0x00, 0x00, 0x00, 0x00, - 0xc6, 0x31, 0x80, 0x00, 0x00, 0x00, - 0x23, 0x88, 0xe0, 0x00, 0x00, 0x00, - 0x1a, 0x46, 0x90, 0x00, 0x00, 0x00, - 0x24, 0xc9, 0x30, 0x00, 0x00, 0x00, - 0x71, 0x1c, 0x40, 0x00, 0x00, 0x00, - 0xf5, 0xdc, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_13[78] = - { - 0x0e, 0x03, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x45, 0x51, 0x50, 0x00, 0x00, 0x00, - 0x88, 0xa2, 0x20, 0x00, 0x00, 0x00, - 0xe0, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x0c, 0x43, 0x10, 0x00, 0x00, 0x00, - 0x28, 0x8a, 0x20, 0x00, 0x00, 0x00, - 0x94, 0x25, 0x00, 0x00, 0x00, 0x00, - 0xc1, 0x30, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_14[84] = - { - 0x46, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x0c, 0x43, 0x10, 0x00, 0x00, 0x00, - 0x28, 0x8a, 0x20, 0x00, 0x00, 0x00, - 0x94, 0x25, 0x00, 0x00, 0x00, 0x00, - 0xc1, 0x30, 0x40, 0x00, 0x00, 0x00, - 0x0e, 0x03, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x45, 0x51, 0x50, 0x00, 0x00, 0x00, - 0x88, 0xa2, 0x20, 0x00, 0x00, 0x00, - 0xe0, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x56, 0x3e, 0x20, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_15[90] = - { - 0x46, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x0c, 0x43, 0x10, 0x00, 0x00, 0x00, - 0x28, 0x8a, 0x20, 0x00, 0x00, 0x00, - 0x94, 0x25, 0x00, 0x00, 0x00, 0x00, - 0xc1, 0x30, 0x40, 0x00, 0x00, 0x00, - 0x2c, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x86, 0x20, 0x00, 0x00, 0x00, - 0xc2, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x88, 0xa0, 0x00, 0x00, 0x00, - 0x50, 0x54, 0x10, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_16[96] = - { - 0x2c, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x86, 0x20, 0x00, 0x00, 0x00, - 0xc2, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x88, 0xa0, 0x00, 0x00, 0x00, - 0x50, 0x54, 0x10, 0x00, 0x00, 0x00, - 0x46, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc0, 0x00, 0x00, 0x00, - 0x10, 0xc4, 0x30, 0x00, 0x00, 0x00, - 0x0c, 0x43, 0x10, 0x00, 0x00, 0x00, - 0x28, 0x8a, 0x20, 0x00, 0x00, 0x00, - 0x94, 0x25, 0x00, 0x00, 0x00, 0x00, - 0xc1, 0x30, 0x40, 0x00, 0x00, 0x00, - 0x28, 0x1c, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_17[102] = - { - 0x2c, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x86, 0x20, 0x00, 0x00, 0x00, - 0xc2, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x88, 0xa0, 0x00, 0x00, 0x00, - 0x50, 0x54, 0x10, 0x00, 0x00, 0x00, - 0x4e, 0x13, 0x80, 0x00, 0x00, 0x00, - 0xe3, 0x38, 0xc0, 0x00, 0x00, 0x00, - 0x81, 0xe0, 0x70, 0x00, 0x00, 0x00, - 0x21, 0x48, 0x50, 0x00, 0x00, 0x00, - 0x52, 0x94, 0xa0, 0x00, 0x00, 0x00, - 0xb4, 0x2d, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x89, 0xa0, 0x00, 0x00, 0x00, - 0x58, 0x56, 0x10, 0x00, 0x00, 0x00, - 0x19, 0x86, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_18[108] = - { - 0x4e, 0x13, 0x80, 0x00, 0x00, 0x00, - 0xe3, 0x38, 0xc0, 0x00, 0x00, 0x00, - 0x81, 0xe0, 0x70, 0x00, 0x00, 0x00, - 0x21, 0x48, 0x50, 0x00, 0x00, 0x00, - 0x52, 0x94, 0xa0, 0x00, 0x00, 0x00, - 0xb4, 0x2d, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x89, 0xa0, 0x00, 0x00, 0x00, - 0x58, 0x56, 0x10, 0x00, 0x00, 0x00, - 0x19, 0x86, 0x60, 0x00, 0x00, 0x00, - 0x2c, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x60, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x86, 0x20, 0x00, 0x00, 0x00, - 0xc2, 0x30, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x88, 0xa0, 0x00, 0x00, 0x00, - 0x50, 0x54, 0x10, 0x00, 0x00, 0x00, - 0x21, 0x7b, 0xf0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_19[114] = - { - 0x4e, 0x13, 0x80, 0x00, 0x00, 0x00, - 0xe3, 0x38, 0xc0, 0x00, 0x00, 0x00, - 0x81, 0xe0, 0x70, 0x00, 0x00, 0x00, - 0x21, 0x48, 0x50, 0x00, 0x00, 0x00, - 0x52, 0x94, 0xa0, 0x00, 0x00, 0x00, - 0xb4, 0x2d, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x89, 0xa0, 0x00, 0x00, 0x00, - 0x58, 0x56, 0x10, 0x00, 0x00, 0x00, - 0x19, 0x86, 0x60, 0x00, 0x00, 0x00, - 0x4c, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x40, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0x80, 0xe0, 0x00, 0x00, 0x00, - 0x86, 0x21, 0x80, 0x00, 0x00, 0x00, - 0x29, 0x0a, 0x40, 0x00, 0x00, 0x00, - 0x42, 0x50, 0x90, 0x00, 0x00, 0x00, - 0x98, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x8c, 0x20, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_2[12] = - { - 0xee, 0x3b, 0x80, 0x00, 0x00, 0x00, - 0x99, 0xe6, 0x70, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_20[120] = - { - 0x4c, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x40, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x10, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0x80, 0xe0, 0x00, 0x00, 0x00, - 0x86, 0x21, 0x80, 0x00, 0x00, 0x00, - 0x29, 0x0a, 0x40, 0x00, 0x00, 0x00, - 0x42, 0x50, 0x90, 0x00, 0x00, 0x00, - 0x98, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x8c, 0x20, 0x00, 0x00, 0x00, - 0x4e, 0x13, 0x80, 0x00, 0x00, 0x00, - 0xe3, 0x38, 0xc0, 0x00, 0x00, 0x00, - 0x81, 0xe0, 0x70, 0x00, 0x00, 0x00, - 0x21, 0x48, 0x50, 0x00, 0x00, 0x00, - 0x52, 0x94, 0xa0, 0x00, 0x00, 0x00, - 0xb4, 0x2d, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x89, 0xa0, 0x00, 0x00, 0x00, - 0x58, 0x56, 0x10, 0x00, 0x00, 0x00, - 0x19, 0x86, 0x60, 0x00, 0x00, 0x00, - 0xf7, 0x8d, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_3[18] = - { - 0xce, 0x33, 0x80, 0x00, 0x00, 0x00, - 0x55, 0x95, 0x60, 0x00, 0x00, 0x00, - 0xb1, 0x6a, 0x30, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_4[24] = - { - 0xe6, 0x39, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x8c, 0xe0, 0x00, 0x00, 0x00, - 0x98, 0xe6, 0x30, 0x00, 0x00, 0x00, - 0x2d, 0x4b, 0x50, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_5[30] = - { - 0xce, 0x33, 0x80, 0x00, 0x00, 0x00, - 0x63, 0x98, 0xe0, 0x00, 0x00, 0x00, - 0x98, 0xe5, 0x30, 0x00, 0x00, 0x00, - 0x2b, 0x53, 0x50, 0x00, 0x00, 0x00, - 0xb4, 0x5c, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_6[36] = - { - 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x34, 0x40, 0x00, 0x00, 0x00, - 0x20, 0xe8, 0x30, 0x00, 0x00, 0x00, - 0x85, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x06, 0x86, 0xa0, 0x00, 0x00, 0x00, - 0x9a, 0x21, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_7[42] = - { - 0x4e, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x33, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x0e, 0xb0, 0x00, 0x00, 0x00, - 0x81, 0x51, 0x50, 0x00, 0x00, 0x00, - 0x24, 0xc4, 0xa0, 0x00, 0x00, 0x00, - 0xd4, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0xa2, 0x60, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_8[48] = - { - 0x27, 0x09, 0xc0, 0x00, 0x00, 0x00, - 0x89, 0xa2, 0x60, 0x00, 0x00, 0x00, - 0xd0, 0x74, 0x10, 0x00, 0x00, 0x00, - 0x24, 0xc9, 0x30, 0x00, 0x00, 0x00, - 0xe2, 0x90, 0xa0, 0x00, 0x00, 0x00, - 0xc6, 0x31, 0x80, 0x00, 0x00, 0x00, - 0x31, 0x8c, 0x60, 0x00, 0x00, 0x00, - 0x18, 0xc6, 0x30, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask20_9[54] = - { - 0x4e, 0x13, 0x80, 0x00, 0x00, 0x00, - 0x62, 0x38, 0xc0, 0x00, 0x00, 0x00, - 0x81, 0xe0, 0x70, 0x00, 0x00, 0x00, - 0xe1, 0x48, 0x50, 0x00, 0x00, 0x00, - 0x13, 0x94, 0xa0, 0x00, 0x00, 0x00, - 0xb4, 0x2d, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x89, 0xa0, 0x00, 0x00, 0x00, - 0x58, 0x56, 0x10, 0x00, 0x00, 0x00, - 0x49, 0x86, 0x50, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_1[6] = - { - 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_10[60] = - { - 0x4c, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x50, 0x00, 0x00, 0x00, - 0xa0, 0x6a, 0x40, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0xb4, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x20, 0x90, 0x00, 0x00, 0x00, - 0x29, 0x08, 0x48, 0x00, 0x00, 0x00, - 0x42, 0x43, 0x08, 0x00, 0x00, 0x00, - 0x98, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x84, 0xa8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_11[66] = - { - 0xc6, 0x21, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x88, 0xc8, 0x00, 0x00, 0x00, - 0x1a, 0x45, 0x88, 0x00, 0x00, 0x00, - 0x24, 0xd3, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x10, 0x70, 0x00, 0x00, 0x00, - 0x0e, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x33, 0x14, 0x50, 0x00, 0x00, 0x00, - 0x10, 0xc3, 0x28, 0x00, 0x00, 0x00, - 0x45, 0x68, 0x48, 0x00, 0x00, 0x00, - 0x88, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0xe0, 0x22, 0x90, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_12[72] = - { - 0x0e, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x33, 0x14, 0x50, 0x00, 0x00, 0x00, - 0x10, 0xc3, 0x28, 0x00, 0x00, 0x00, - 0x45, 0x68, 0x48, 0x00, 0x00, 0x00, - 0x88, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0xe0, 0x22, 0x90, 0x00, 0x00, 0x00, - 0xc6, 0x21, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x88, 0xc8, 0x00, 0x00, 0x00, - 0x1a, 0x45, 0x88, 0x00, 0x00, 0x00, - 0x24, 0xd3, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x10, 0x70, 0x00, 0x00, 0x00, - 0xa0, 0x65, 0x18, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_13[78] = - { - 0x0e, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x33, 0x14, 0x50, 0x00, 0x00, 0x00, - 0x10, 0xc3, 0x28, 0x00, 0x00, 0x00, - 0x45, 0x68, 0x48, 0x00, 0x00, 0x00, - 0x88, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0xe0, 0x22, 0x90, 0x00, 0x00, 0x00, - 0x46, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc8, 0x00, 0x00, 0x00, - 0x10, 0xe4, 0x60, 0x00, 0x00, 0x00, - 0x0c, 0x69, 0x08, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x28, 0x00, 0x00, 0x00, - 0x94, 0x21, 0x30, 0x00, 0x00, 0x00, - 0xc1, 0x02, 0x58, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_14[84] = - { - 0x46, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc8, 0x00, 0x00, 0x00, - 0x10, 0xe4, 0x60, 0x00, 0x00, 0x00, - 0x0c, 0x69, 0x08, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x28, 0x00, 0x00, 0x00, - 0x94, 0x21, 0x30, 0x00, 0x00, 0x00, - 0xc1, 0x02, 0x58, 0x00, 0x00, 0x00, - 0x0e, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x33, 0x14, 0x50, 0x00, 0x00, 0x00, - 0x10, 0xc3, 0x28, 0x00, 0x00, 0x00, - 0x45, 0x68, 0x48, 0x00, 0x00, 0x00, - 0x88, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0xe0, 0x22, 0x90, 0x00, 0x00, 0x00, - 0x4d, 0xd0, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_15[90] = - { - 0x46, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc8, 0x00, 0x00, 0x00, - 0x10, 0xe4, 0x60, 0x00, 0x00, 0x00, - 0x0c, 0x69, 0x08, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x28, 0x00, 0x00, 0x00, - 0x94, 0x21, 0x30, 0x00, 0x00, 0x00, - 0xc1, 0x02, 0x58, 0x00, 0x00, 0x00, - 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x18, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0xc2, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x98, 0x08, 0x00, 0x00, 0x00, - 0x50, 0x45, 0x08, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_16[96] = - { - 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x18, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0xc2, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x98, 0x08, 0x00, 0x00, 0x00, - 0x50, 0x45, 0x08, 0x00, 0x00, 0x00, - 0x46, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x33, 0x0c, 0xc8, 0x00, 0x00, 0x00, - 0x10, 0xe4, 0x60, 0x00, 0x00, 0x00, - 0x0c, 0x69, 0x08, 0x00, 0x00, 0x00, - 0x28, 0x94, 0x28, 0x00, 0x00, 0x00, - 0x94, 0x21, 0x30, 0x00, 0x00, 0x00, - 0xc1, 0x02, 0x58, 0x00, 0x00, 0x00, - 0x3b, 0xf5, 0x38, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_17[102] = - { - 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x18, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0xc2, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x98, 0x08, 0x00, 0x00, 0x00, - 0x50, 0x45, 0x08, 0x00, 0x00, 0x00, - 0x4e, 0x11, 0x90, 0x00, 0x00, 0x00, - 0xe3, 0x18, 0x98, 0x00, 0x00, 0x00, - 0x81, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x40, 0x58, 0x00, 0x00, 0x00, - 0x52, 0x81, 0xe0, 0x00, 0x00, 0x00, - 0xb4, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x26, 0x86, 0x28, 0x00, 0x00, 0x00, - 0x58, 0x64, 0x40, 0x00, 0x00, 0x00, - 0x19, 0x9e, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_18[108] = - { - 0x4e, 0x11, 0x90, 0x00, 0x00, 0x00, - 0xe3, 0x18, 0x98, 0x00, 0x00, 0x00, - 0x81, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x40, 0x58, 0x00, 0x00, 0x00, - 0x52, 0x81, 0xe0, 0x00, 0x00, 0x00, - 0xb4, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x26, 0x86, 0x28, 0x00, 0x00, 0x00, - 0x58, 0x64, 0x40, 0x00, 0x00, 0x00, - 0x19, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, - 0x81, 0xa0, 0x18, 0x00, 0x00, 0x00, - 0xa0, 0x68, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x41, 0x50, 0x00, 0x00, 0x00, - 0x18, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0xc2, 0x06, 0x80, 0x00, 0x00, 0x00, - 0x22, 0x98, 0x08, 0x00, 0x00, 0x00, - 0x50, 0x45, 0x08, 0x00, 0x00, 0x00, - 0x5a, 0x56, 0x58, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_19[114] = - { - 0x4e, 0x11, 0x90, 0x00, 0x00, 0x00, - 0xe3, 0x18, 0x98, 0x00, 0x00, 0x00, - 0x81, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x40, 0x58, 0x00, 0x00, 0x00, - 0x52, 0x81, 0xe0, 0x00, 0x00, 0x00, - 0xb4, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x26, 0x86, 0x28, 0x00, 0x00, 0x00, - 0x58, 0x64, 0x40, 0x00, 0x00, 0x00, - 0x19, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x50, 0x00, 0x00, 0x00, - 0xa0, 0x6a, 0x40, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0xb4, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x20, 0x90, 0x00, 0x00, 0x00, - 0x29, 0x08, 0x48, 0x00, 0x00, 0x00, - 0x42, 0x43, 0x08, 0x00, 0x00, 0x00, - 0x98, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x84, 0xa8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_2[12] = - { - 0xee, 0x3b, 0x30, 0x00, 0x00, 0x00, - 0x99, 0xe6, 0xe8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_20[120] = - { - 0x4c, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x50, 0x00, 0x00, 0x00, - 0xa0, 0x6a, 0x40, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0xb4, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x20, 0x90, 0x00, 0x00, 0x00, - 0x29, 0x08, 0x48, 0x00, 0x00, 0x00, - 0x42, 0x43, 0x08, 0x00, 0x00, 0x00, - 0x98, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0x4e, 0x11, 0x90, 0x00, 0x00, 0x00, - 0xe3, 0x18, 0x98, 0x00, 0x00, 0x00, - 0x81, 0xe3, 0x00, 0x00, 0x00, 0x00, - 0x21, 0x40, 0x58, 0x00, 0x00, 0x00, - 0x52, 0x81, 0xe0, 0x00, 0x00, 0x00, - 0xb4, 0x28, 0x20, 0x00, 0x00, 0x00, - 0x26, 0x86, 0x28, 0x00, 0x00, 0x00, - 0x58, 0x64, 0x40, 0x00, 0x00, 0x00, - 0x19, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x03, 0x30, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_21[126] = - { - 0x4c, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x50, 0x00, 0x00, 0x00, - 0xa0, 0x6a, 0x40, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x30, 0x00, 0x00, 0x00, - 0x03, 0xb4, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x20, 0x90, 0x00, 0x00, 0x00, - 0x29, 0x08, 0x48, 0x00, 0x00, 0x00, - 0x42, 0x43, 0x08, 0x00, 0x00, 0x00, - 0x98, 0x12, 0x80, 0x00, 0x00, 0x00, - 0x30, 0x84, 0xa8, 0x00, 0x00, 0x00, - 0x4c, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x51, 0x0c, 0xc8, 0x00, 0x00, 0x00, - 0xa0, 0x66, 0x40, 0x00, 0x00, 0x00, - 0x04, 0xc1, 0x60, 0x00, 0x00, 0x00, - 0x03, 0xa0, 0x28, 0x00, 0x00, 0x00, - 0x86, 0x21, 0x10, 0x00, 0x00, 0x00, - 0x29, 0x10, 0x18, 0x00, 0x00, 0x00, - 0x42, 0x42, 0xa0, 0x00, 0x00, 0x00, - 0x98, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x84, 0x08, 0x00, 0x00, 0x00, - 0xdf, 0x4c, 0x10, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_3[18] = - { - 0xce, 0x32, 0xb0, 0x00, 0x00, 0x00, - 0x55, 0xdc, 0x50, 0x00, 0x00, 0x00, - 0xa8, 0xed, 0x88, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_4[24] = - { - 0xe6, 0x31, 0x30, 0x00, 0x00, 0x00, - 0x33, 0x8c, 0x58, 0x00, 0x00, 0x00, - 0x98, 0xd2, 0xc8, 0x00, 0x00, 0x00, - 0x2d, 0x4b, 0x28, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_5[30] = - { - 0xce, 0x31, 0xb0, 0x00, 0x00, 0x00, - 0x63, 0x98, 0xd8, 0x00, 0x00, 0x00, - 0x98, 0xc7, 0x68, 0x00, 0x00, 0x00, - 0x4d, 0x6b, 0x50, 0x00, 0x00, 0x00, - 0xb2, 0x6c, 0xa8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_6[36] = - { - 0x4c, 0x19, 0x10, 0x00, 0x00, 0x00, - 0x51, 0x14, 0x50, 0x00, 0x00, 0x00, - 0x20, 0xea, 0x08, 0x00, 0x00, 0x00, - 0x85, 0x41, 0x28, 0x00, 0x00, 0x00, - 0x06, 0x80, 0xd8, 0x00, 0x00, 0x00, - 0x8a, 0x24, 0x30, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_7[42] = - { - 0xc6, 0x11, 0x90, 0x00, 0x00, 0x00, - 0x33, 0x04, 0xc8, 0x00, 0x00, 0x00, - 0x18, 0x67, 0x40, 0x00, 0x00, 0x00, - 0x45, 0x42, 0xd0, 0x00, 0x00, 0x00, - 0x12, 0xd4, 0x28, 0x00, 0x00, 0x00, - 0xb4, 0x28, 0x30, 0x00, 0x00, 0x00, - 0x29, 0x92, 0x18, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_8[48] = - { - 0x07, 0x0a, 0x70, 0x00, 0x00, 0x00, - 0x49, 0xa8, 0x28, 0x00, 0x00, 0x00, - 0xb0, 0x7a, 0x00, 0x00, 0x00, 0x00, - 0x24, 0xc5, 0xc0, 0x00, 0x00, 0x00, - 0x52, 0x80, 0xe8, 0x00, 0x00, 0x00, - 0xc6, 0x31, 0x80, 0x00, 0x00, 0x00, - 0x31, 0x94, 0x18, 0x00, 0x00, 0x00, - 0x18, 0xc7, 0x08, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask21_9[54] = - { - 0x4e, 0x11, 0x10, 0x00, 0x00, 0x00, - 0x62, 0x1a, 0x08, 0x00, 0x00, 0x00, - 0x80, 0xe9, 0x40, 0x00, 0x00, 0x00, - 0xa1, 0x50, 0x50, 0x00, 0x00, 0x00, - 0x53, 0x00, 0x68, 0x00, 0x00, 0x00, - 0xa4, 0x24, 0x30, 0x00, 0x00, 0x00, - 0x16, 0xa0, 0x88, 0x00, 0x00, 0x00, - 0x58, 0x45, 0x20, 0x00, 0x00, 0x00, - 0x29, 0x86, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_1[6] = - { - 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_10[60] = - { - 0xc0, 0x38, 0x88, 0x00, 0x00, 0x00, - 0x30, 0x0e, 0x28, 0x00, 0x00, 0x00, - 0xe8, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x85, 0x08, 0xa8, 0x00, 0x00, 0x00, - 0xd0, 0x92, 0x10, 0x00, 0x00, 0x00, - 0x86, 0x50, 0x48, 0x00, 0x00, 0x00, - 0x4a, 0x68, 0x0c, 0x00, 0x00, 0x00, - 0x01, 0xa0, 0x74, 0x00, 0x00, 0x00, - 0x4c, 0x81, 0x90, 0x00, 0x00, 0x00, - 0x62, 0x24, 0x04, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_11[66] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x99, 0x13, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x80, 0xb0, 0x00, 0x00, 0x00, - 0x80, 0xb0, 0x14, 0x00, 0x00, 0x00, - 0x84, 0x50, 0x88, 0x00, 0x00, 0x00, - 0x40, 0x68, 0x0c, 0x00, 0x00, 0x00, - 0x0a, 0x81, 0x50, 0x00, 0x00, 0x00, - 0x68, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x22, 0x04, 0x00, 0x00, 0x00, - 0x30, 0x46, 0x08, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_12[72] = - { - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x0c, 0xa1, 0x94, 0x00, 0x00, 0x00, - 0xa1, 0x34, 0x24, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x8a, 0x51, 0x48, 0x00, 0x00, 0x00, - 0x86, 0x90, 0xd0, 0x00, 0x00, 0x00, - 0x23, 0x24, 0x64, 0x00, 0x00, 0x00, - 0x16, 0x22, 0xc4, 0x00, 0x00, 0x00, - 0x4c, 0x29, 0x84, 0x00, 0x00, 0x00, - 0x41, 0xc8, 0x38, 0x00, 0x00, 0x00, - 0xf4, 0x18, 0x9c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_13[78] = - { - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x0c, 0xa1, 0x94, 0x00, 0x00, 0x00, - 0xa1, 0x34, 0x24, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x8a, 0x51, 0x48, 0x00, 0x00, 0x00, - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x30, 0x00, 0x00, 0x00, - 0xa4, 0x34, 0x84, 0x00, 0x00, 0x00, - 0x50, 0xaa, 0x14, 0x00, 0x00, 0x00, - 0x84, 0xd0, 0x98, 0x00, 0x00, 0x00, - 0x09, 0x61, 0x2c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_14[84] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x30, 0x00, 0x00, 0x00, - 0xa4, 0x34, 0x84, 0x00, 0x00, 0x00, - 0x50, 0xaa, 0x14, 0x00, 0x00, 0x00, - 0x84, 0xd0, 0x98, 0x00, 0x00, 0x00, - 0x09, 0x61, 0x2c, 0x00, 0x00, 0x00, - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x0c, 0xa1, 0x94, 0x00, 0x00, 0x00, - 0xa1, 0x34, 0x24, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x8a, 0x51, 0x48, 0x00, 0x00, 0x00, - 0xc6, 0xca, 0xe8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_15[90] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x30, 0x00, 0x00, 0x00, - 0xa4, 0x34, 0x84, 0x00, 0x00, 0x00, - 0x50, 0xaa, 0x14, 0x00, 0x00, 0x00, - 0x84, 0xd0, 0x98, 0x00, 0x00, 0x00, - 0x09, 0x61, 0x2c, 0x00, 0x00, 0x00, - 0x0c, 0x81, 0x90, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x0c, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x40, 0xa8, 0x00, 0x00, 0x00, - 0x43, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x03, 0x40, 0x00, 0x00, 0x00, - 0x60, 0x2c, 0x04, 0x00, 0x00, 0x00, - 0x14, 0x22, 0x84, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_16[96] = - { - 0x0c, 0x81, 0x90, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x0c, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x40, 0xa8, 0x00, 0x00, 0x00, - 0x43, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x03, 0x40, 0x00, 0x00, 0x00, - 0x60, 0x2c, 0x04, 0x00, 0x00, 0x00, - 0x14, 0x22, 0x84, 0x00, 0x00, 0x00, - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x30, 0x00, 0x00, 0x00, - 0xa4, 0x34, 0x84, 0x00, 0x00, 0x00, - 0x50, 0xaa, 0x14, 0x00, 0x00, 0x00, - 0x84, 0xd0, 0x98, 0x00, 0x00, 0x00, - 0x09, 0x61, 0x2c, 0x00, 0x00, 0x00, - 0x86, 0xc1, 0x44, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_17[102] = - { - 0x0c, 0x81, 0x90, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x0c, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x40, 0xa8, 0x00, 0x00, 0x00, - 0x43, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x03, 0x40, 0x00, 0x00, 0x00, - 0x60, 0x2c, 0x04, 0x00, 0x00, 0x00, - 0x14, 0x22, 0x84, 0x00, 0x00, 0x00, - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x4c, 0x00, 0x00, 0x00, - 0x8c, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x01, 0x60, 0x2c, 0x00, 0x00, 0x00, - 0x07, 0x80, 0xf0, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x18, 0xa3, 0x14, 0x00, 0x00, 0x00, - 0x91, 0x12, 0x20, 0x00, 0x00, 0x00, - 0x78, 0x0f, 0x00, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_18[108] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x4c, 0x00, 0x00, 0x00, - 0x8c, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x01, 0x60, 0x2c, 0x00, 0x00, 0x00, - 0x07, 0x80, 0xf0, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x18, 0xa3, 0x14, 0x00, 0x00, 0x00, - 0x91, 0x12, 0x20, 0x00, 0x00, 0x00, - 0x78, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x81, 0x90, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x0c, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x40, 0xa8, 0x00, 0x00, 0x00, - 0x43, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x1a, 0x03, 0x40, 0x00, 0x00, 0x00, - 0x60, 0x2c, 0x04, 0x00, 0x00, 0x00, - 0x14, 0x22, 0x84, 0x00, 0x00, 0x00, - 0xe4, 0xd4, 0x6c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_19[114] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x4c, 0x00, 0x00, 0x00, - 0x8c, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x01, 0x60, 0x2c, 0x00, 0x00, 0x00, - 0x07, 0x80, 0xf0, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x18, 0xa3, 0x14, 0x00, 0x00, 0x00, - 0x91, 0x12, 0x20, 0x00, 0x00, 0x00, - 0x78, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0xa9, 0x15, 0x20, 0x00, 0x00, 0x00, - 0x04, 0xc0, 0x98, 0x00, 0x00, 0x00, - 0xd0, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x50, 0x48, 0x00, 0x00, 0x00, - 0x21, 0x24, 0x24, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x84, 0x00, 0x00, 0x00, - 0x4a, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_2[12] = - { - 0xec, 0xdd, 0x98, 0x00, 0x00, 0x00, - 0x9b, 0xb3, 0x74, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_20[120] = - { - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0xa9, 0x15, 0x20, 0x00, 0x00, 0x00, - 0x04, 0xc0, 0x98, 0x00, 0x00, 0x00, - 0xd0, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x50, 0x48, 0x00, 0x00, 0x00, - 0x21, 0x24, 0x24, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x84, 0x00, 0x00, 0x00, - 0x4a, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x62, 0x6c, 0x4c, 0x00, 0x00, 0x00, - 0x8c, 0x11, 0x80, 0x00, 0x00, 0x00, - 0x01, 0x60, 0x2c, 0x00, 0x00, 0x00, - 0x07, 0x80, 0xf0, 0x00, 0x00, 0x00, - 0xa0, 0x94, 0x10, 0x00, 0x00, 0x00, - 0x18, 0xa3, 0x14, 0x00, 0x00, 0x00, - 0x91, 0x12, 0x20, 0x00, 0x00, 0x00, - 0x78, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x48, 0xc4, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_21[126] = - { - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0xa9, 0x15, 0x20, 0x00, 0x00, 0x00, - 0x04, 0xc0, 0x98, 0x00, 0x00, 0x00, - 0xd0, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x50, 0x48, 0x00, 0x00, 0x00, - 0x21, 0x24, 0x24, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x84, 0x00, 0x00, 0x00, - 0x4a, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x99, 0x13, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x80, 0xb0, 0x00, 0x00, 0x00, - 0x80, 0xb0, 0x14, 0x00, 0x00, 0x00, - 0x84, 0x50, 0x88, 0x00, 0x00, 0x00, - 0x40, 0x68, 0x0c, 0x00, 0x00, 0x00, - 0x0a, 0x81, 0x50, 0x00, 0x00, 0x00, - 0x68, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x22, 0x04, 0x00, 0x00, 0x00, - 0x30, 0x46, 0x08, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_22[132] = - { - 0x46, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x33, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x99, 0x13, 0x20, 0x00, 0x00, 0x00, - 0x05, 0x80, 0xb0, 0x00, 0x00, 0x00, - 0x80, 0xb0, 0x14, 0x00, 0x00, 0x00, - 0x84, 0x50, 0x88, 0x00, 0x00, 0x00, - 0x40, 0x68, 0x0c, 0x00, 0x00, 0x00, - 0x0a, 0x81, 0x50, 0x00, 0x00, 0x00, - 0x68, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x22, 0x04, 0x00, 0x00, 0x00, - 0x30, 0x46, 0x08, 0x00, 0x00, 0x00, - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0xa9, 0x15, 0x20, 0x00, 0x00, 0x00, - 0x04, 0xc0, 0x98, 0x00, 0x00, 0x00, - 0xd0, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x50, 0x48, 0x00, 0x00, 0x00, - 0x21, 0x24, 0x24, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x84, 0x00, 0x00, 0x00, - 0x4a, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x12, 0xa2, 0x54, 0x00, 0x00, 0x00, - 0x9e, 0xce, 0x88, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_3[18] = - { - 0xca, 0xd9, 0x58, 0x00, 0x00, 0x00, - 0xf1, 0x5e, 0x28, 0x00, 0x00, 0x00, - 0xb6, 0x35, 0xc4, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_4[24] = - { - 0xc4, 0xd8, 0x98, 0x00, 0x00, 0x00, - 0x31, 0x66, 0x2c, 0x00, 0x00, 0x00, - 0x4b, 0x29, 0x64, 0x00, 0x00, 0x00, - 0x2c, 0xa5, 0x94, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_5[30] = - { - 0xc6, 0xd8, 0xd8, 0x00, 0x00, 0x00, - 0x63, 0x6c, 0x6c, 0x00, 0x00, 0x00, - 0x1d, 0xa3, 0xb4, 0x00, 0x00, 0x00, - 0xad, 0x55, 0xa8, 0x00, 0x00, 0x00, - 0xb2, 0xb6, 0x54, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_6[36] = - { - 0x64, 0x4c, 0x88, 0x00, 0x00, 0x00, - 0x51, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0xa8, 0x35, 0x04, 0x00, 0x00, 0x00, - 0xc4, 0xa0, 0x94, 0x00, 0x00, 0x00, - 0x03, 0x60, 0x6c, 0x00, 0x00, 0x00, - 0x90, 0xd2, 0x18, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_7[42] = - { - 0xc6, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x13, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x8d, 0x13, 0xa0, 0x00, 0x00, 0x00, - 0x8b, 0x41, 0x68, 0x00, 0x00, 0x00, - 0x52, 0xaa, 0x14, 0x00, 0x00, 0x00, - 0xa2, 0xd4, 0x18, 0x00, 0x00, 0x00, - 0x61, 0xa8, 0x2c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_8[48] = - { - 0x28, 0x85, 0x38, 0x00, 0x00, 0x00, - 0x21, 0xf4, 0x04, 0x00, 0x00, 0x00, - 0xe9, 0x1d, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x02, 0xe0, 0x00, 0x00, 0x00, - 0x83, 0xa0, 0x54, 0x00, 0x00, 0x00, - 0x46, 0x18, 0xe8, 0x00, 0x00, 0x00, - 0x50, 0x6a, 0x0c, 0x00, 0x00, 0x00, - 0x1c, 0x23, 0x84, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask22_9[54] = - { - 0x44, 0x48, 0xc8, 0x00, 0x00, 0x00, - 0x28, 0x2d, 0x0c, 0x00, 0x00, 0x00, - 0x25, 0x14, 0xa0, 0x00, 0x00, 0x00, - 0x59, 0x0a, 0x20, 0x00, 0x00, 0x00, - 0x03, 0xa0, 0x34, 0x00, 0x00, 0x00, - 0xc0, 0xd0, 0x18, 0x00, 0x00, 0x00, - 0xa2, 0x30, 0x44, 0x00, 0x00, 0x00, - 0x14, 0x82, 0xd0, 0x00, 0x00, 0x00, - 0x9a, 0x03, 0x80, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_1[6] = - { - 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_10[60] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x48, 0xa2, 0x00, 0x00, 0x00, - 0xa9, 0x10, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0xc4, 0x84, 0x00, 0x00, 0x00, - 0xd0, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1c, 0x00, 0x00, 0x00, - 0x21, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x22, 0x00, 0x00, 0x00, - 0x4a, 0x0a, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0xb4, 0x50, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_11[66] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x99, 0x02, 0x12, 0x00, 0x00, 0x00, - 0x05, 0x80, 0x0e, 0x00, 0x00, 0x00, - 0x80, 0xa1, 0x82, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x18, 0x00, 0x00, 0x00, - 0x40, 0x6d, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0x68, 0x04, 0x90, 0x00, 0x00, 0x00, - 0x10, 0x31, 0x20, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x04, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_12[72] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x58, 0xa2, 0x00, 0x00, 0x00, - 0x0c, 0xa4, 0x30, 0x00, 0x00, 0x00, - 0xa1, 0x22, 0x46, 0x00, 0x00, 0x00, - 0x12, 0xa1, 0x1c, 0x00, 0x00, 0x00, - 0x8a, 0x45, 0xc0, 0x00, 0x00, 0x00, - 0x86, 0x8a, 0x6c, 0x00, 0x00, 0x00, - 0x23, 0x2c, 0x84, 0x00, 0x00, 0x00, - 0x16, 0x21, 0x98, 0x00, 0x00, 0x00, - 0x4c, 0x30, 0x54, 0x00, 0x00, 0x00, - 0x41, 0xc1, 0x26, 0x00, 0x00, 0x00, - 0x19, 0x56, 0xe4, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_13[78] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x58, 0xa2, 0x00, 0x00, 0x00, - 0x0c, 0xa4, 0x30, 0x00, 0x00, 0x00, - 0xa1, 0x22, 0x46, 0x00, 0x00, 0x00, - 0x12, 0xa1, 0x1c, 0x00, 0x00, 0x00, - 0x8a, 0x45, 0xc0, 0x00, 0x00, 0x00, - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x12, 0x00, 0x00, 0x00, - 0xa4, 0x20, 0x4a, 0x00, 0x00, 0x00, - 0x50, 0xa0, 0xd4, 0x00, 0x00, 0x00, - 0x84, 0xc5, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x71, 0x0c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_14[84] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x12, 0x00, 0x00, 0x00, - 0xa4, 0x20, 0x4a, 0x00, 0x00, 0x00, - 0x50, 0xa0, 0xd4, 0x00, 0x00, 0x00, - 0x84, 0xc5, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x71, 0x0c, 0x00, 0x00, 0x00, - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x58, 0xa2, 0x00, 0x00, 0x00, - 0x0c, 0xa4, 0x30, 0x00, 0x00, 0x00, - 0xa1, 0x22, 0x46, 0x00, 0x00, 0x00, - 0x12, 0xa1, 0x1c, 0x00, 0x00, 0x00, - 0x8a, 0x45, 0xc0, 0x00, 0x00, 0x00, - 0x9c, 0x3f, 0xb2, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_15[90] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x12, 0x00, 0x00, 0x00, - 0xa4, 0x20, 0x4a, 0x00, 0x00, 0x00, - 0x50, 0xa0, 0xd4, 0x00, 0x00, 0x00, - 0x84, 0xc5, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x71, 0x0c, 0x00, 0x00, 0x00, - 0x0c, 0x84, 0x0c, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x88, 0x48, 0x00, 0x00, 0x00, - 0x05, 0x40, 0x32, 0x00, 0x00, 0x00, - 0x43, 0x02, 0x82, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x60, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x38, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_16[96] = - { - 0x0c, 0x84, 0x0c, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x88, 0x48, 0x00, 0x00, 0x00, - 0x05, 0x40, 0x32, 0x00, 0x00, 0x00, - 0x43, 0x02, 0x82, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x60, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x38, 0xa0, 0x00, 0x00, 0x00, - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x12, 0x00, 0x00, 0x00, - 0xa4, 0x20, 0x4a, 0x00, 0x00, 0x00, - 0x50, 0xa0, 0xd4, 0x00, 0x00, 0x00, - 0x84, 0xc5, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x71, 0x0c, 0x00, 0x00, 0x00, - 0xfa, 0xd9, 0xf4, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_17[102] = - { - 0x0c, 0x84, 0x0c, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x88, 0x48, 0x00, 0x00, 0x00, - 0x05, 0x40, 0x32, 0x00, 0x00, 0x00, - 0x43, 0x02, 0x82, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x60, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x38, 0xa0, 0x00, 0x00, 0x00, - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x62, 0x7c, 0x84, 0x00, 0x00, 0x00, - 0x8c, 0x04, 0x88, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x22, 0x00, 0x00, 0x00, - 0x07, 0x83, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x80, 0x72, 0x00, 0x00, 0x00, - 0x18, 0xb1, 0x42, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x1c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_18[108] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x62, 0x7c, 0x84, 0x00, 0x00, 0x00, - 0x8c, 0x04, 0x88, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x22, 0x00, 0x00, 0x00, - 0x07, 0x83, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x80, 0x72, 0x00, 0x00, 0x00, - 0x18, 0xb1, 0x42, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x0c, 0x84, 0x0c, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x88, 0x48, 0x00, 0x00, 0x00, - 0x05, 0x40, 0x32, 0x00, 0x00, 0x00, - 0x43, 0x02, 0x82, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x60, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x38, 0xa0, 0x00, 0x00, 0x00, - 0x82, 0x32, 0x56, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_19[114] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x62, 0x7c, 0x84, 0x00, 0x00, 0x00, - 0x8c, 0x04, 0x88, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x22, 0x00, 0x00, 0x00, - 0x07, 0x83, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x80, 0x72, 0x00, 0x00, 0x00, - 0x18, 0xb1, 0x42, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x48, 0xa2, 0x00, 0x00, 0x00, - 0xa9, 0x10, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0xc4, 0x84, 0x00, 0x00, 0x00, - 0xd0, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1c, 0x00, 0x00, 0x00, - 0x21, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x22, 0x00, 0x00, 0x00, - 0x4a, 0x0a, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0xb4, 0x50, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_2[12] = - { - 0xec, 0xdd, 0x98, 0x00, 0x00, 0x00, - 0x9b, 0xb2, 0x76, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_20[120] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x48, 0xa2, 0x00, 0x00, 0x00, - 0xa9, 0x10, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0xc4, 0x84, 0x00, 0x00, 0x00, - 0xd0, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1c, 0x00, 0x00, 0x00, - 0x21, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x22, 0x00, 0x00, 0x00, - 0x4a, 0x0a, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0xb4, 0x50, 0x00, 0x00, 0x00, - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x62, 0x7c, 0x84, 0x00, 0x00, 0x00, - 0x8c, 0x04, 0x88, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x22, 0x00, 0x00, 0x00, - 0x07, 0x83, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x80, 0x72, 0x00, 0x00, 0x00, - 0x18, 0xb1, 0x42, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0xdb, 0x4a, 0x7a, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_21[126] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x48, 0xa2, 0x00, 0x00, 0x00, - 0xa9, 0x10, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0xc4, 0x84, 0x00, 0x00, 0x00, - 0xd0, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1c, 0x00, 0x00, 0x00, - 0x21, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x22, 0x00, 0x00, 0x00, - 0x4a, 0x0a, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0xb4, 0x50, 0x00, 0x00, 0x00, - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x99, 0x02, 0x12, 0x00, 0x00, 0x00, - 0x05, 0x80, 0x0e, 0x00, 0x00, 0x00, - 0x80, 0xa1, 0x82, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x18, 0x00, 0x00, 0x00, - 0x40, 0x6d, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0x68, 0x04, 0x90, 0x00, 0x00, 0x00, - 0x10, 0x31, 0x20, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x04, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_22[132] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x99, 0x02, 0x12, 0x00, 0x00, 0x00, - 0x05, 0x80, 0x0e, 0x00, 0x00, 0x00, - 0x80, 0xa1, 0x82, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x18, 0x00, 0x00, 0x00, - 0x40, 0x6d, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0x68, 0x04, 0x90, 0x00, 0x00, 0x00, - 0x10, 0x31, 0x20, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x04, 0x00, 0x00, 0x00, - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x48, 0xa2, 0x00, 0x00, 0x00, - 0xa9, 0x10, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0xc4, 0x84, 0x00, 0x00, 0x00, - 0xd0, 0x01, 0x44, 0x00, 0x00, 0x00, - 0x82, 0x40, 0x1c, 0x00, 0x00, 0x00, - 0x21, 0x37, 0x00, 0x00, 0x00, 0x00, - 0x0c, 0x21, 0x22, 0x00, 0x00, 0x00, - 0x4a, 0x0a, 0xc0, 0x00, 0x00, 0x00, - 0x12, 0xb4, 0x50, 0x00, 0x00, 0x00, - 0xea, 0x8d, 0x1a, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_23[138] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x99, 0x02, 0x12, 0x00, 0x00, 0x00, - 0x05, 0x80, 0x0e, 0x00, 0x00, 0x00, - 0x80, 0xa1, 0x82, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x18, 0x00, 0x00, 0x00, - 0x40, 0x6d, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x90, 0xc0, 0x00, 0x00, 0x00, - 0x68, 0x04, 0x90, 0x00, 0x00, 0x00, - 0x10, 0x31, 0x20, 0x00, 0x00, 0x00, - 0x30, 0x58, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x42, 0x0c, 0x00, 0x00, 0x00, - 0x33, 0x20, 0x46, 0x00, 0x00, 0x00, - 0x99, 0x08, 0x0a, 0x00, 0x00, 0x00, - 0x05, 0x84, 0x30, 0x00, 0x00, 0x00, - 0x80, 0xb0, 0x22, 0x00, 0x00, 0x00, - 0x84, 0x42, 0x90, 0x00, 0x00, 0x00, - 0x40, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x81, 0x12, 0x00, 0x00, 0x00, - 0x68, 0x0c, 0x40, 0x00, 0x00, 0x00, - 0x10, 0x24, 0x84, 0x00, 0x00, 0x00, - 0x30, 0x51, 0x40, 0x00, 0x00, 0x00, - 0x5f, 0x50, 0x88, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_3[18] = - { - 0xca, 0xd3, 0x64, 0x00, 0x00, 0x00, - 0xf1, 0x49, 0x3a, 0x00, 0x00, 0x00, - 0x76, 0x27, 0xd0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_4[24] = - { - 0xc4, 0xd1, 0x64, 0x00, 0x00, 0x00, - 0x31, 0x62, 0x96, 0x00, 0x00, 0x00, - 0x4b, 0x24, 0x5a, 0x00, 0x00, 0x00, - 0x2c, 0xa8, 0xaa, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_5[30] = - { - 0xc6, 0xca, 0x6c, 0x00, 0x00, 0x00, - 0x63, 0x6c, 0x96, 0x00, 0x00, 0x00, - 0x1d, 0xa1, 0xdc, 0x00, 0x00, 0x00, - 0xad, 0x55, 0x38, 0x00, 0x00, 0x00, - 0xb2, 0xb7, 0x06, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_6[36] = - { - 0x64, 0x4a, 0x28, 0x00, 0x00, 0x00, - 0x51, 0x58, 0xa2, 0x00, 0x00, 0x00, - 0x0c, 0xa4, 0x30, 0x00, 0x00, 0x00, - 0xa1, 0x22, 0x46, 0x00, 0x00, 0x00, - 0x12, 0xa1, 0x1c, 0x00, 0x00, 0x00, - 0x8a, 0x45, 0xc0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_7[42] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x33, 0x24, 0x26, 0x00, 0x00, 0x00, - 0x91, 0x92, 0x12, 0x00, 0x00, 0x00, - 0xa4, 0x20, 0x4a, 0x00, 0x00, 0x00, - 0x50, 0xa0, 0xd4, 0x00, 0x00, 0x00, - 0x84, 0xc5, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x71, 0x0c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_8[48] = - { - 0x0c, 0x84, 0x0c, 0x00, 0x00, 0x00, - 0x80, 0x70, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x88, 0x48, 0x00, 0x00, 0x00, - 0x05, 0x40, 0x32, 0x00, 0x00, 0x00, - 0x43, 0x02, 0x82, 0x00, 0x00, 0x00, - 0x1a, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x60, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x38, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask23_9[54] = - { - 0x46, 0x4a, 0x6c, 0x00, 0x00, 0x00, - 0x62, 0x7c, 0x84, 0x00, 0x00, 0x00, - 0x8c, 0x04, 0x88, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x22, 0x00, 0x00, 0x00, - 0x07, 0x83, 0x06, 0x00, 0x00, 0x00, - 0xa0, 0x80, 0x72, 0x00, 0x00, 0x00, - 0x18, 0xb1, 0x42, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x1c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_1[6] = - { - 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_10[60] = - { - 0x11, 0x45, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x34, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x48, 0x05, 0x00, 0x00, 0x00, - 0x10, 0x83, 0x09, 0x00, 0x00, 0x00, - 0x4a, 0x14, 0xa1, 0x00, 0x00, 0x00, - 0x40, 0xa4, 0x0a, 0x00, 0x00, 0x00, - 0xa0, 0x6a, 0x02, 0x00, 0x00, 0x00, - 0x88, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x86, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x54, 0x0d, 0x40, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_11[66] = - { - 0x53, 0x65, 0x34, 0x00, 0x00, 0x00, - 0xa0, 0x32, 0x11, 0x00, 0x00, 0x00, - 0x15, 0x11, 0x41, 0x00, 0x00, 0x00, - 0x03, 0x50, 0x15, 0x00, 0x00, 0x00, - 0x8c, 0x88, 0xc8, 0x00, 0x00, 0x00, - 0x28, 0x82, 0x88, 0x00, 0x00, 0x00, - 0x08, 0x48, 0x84, 0x00, 0x00, 0x00, - 0x99, 0x01, 0x90, 0x00, 0x00, 0x00, - 0x22, 0x92, 0x29, 0x00, 0x00, 0x00, - 0x46, 0x04, 0x60, 0x00, 0x00, 0x00, - 0x8c, 0x2c, 0x02, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_12[72] = - { - 0x10, 0x61, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x30, 0x23, 0x00, 0x00, 0x00, - 0x40, 0x54, 0x05, 0x00, 0x00, 0x00, - 0x21, 0x82, 0x18, 0x00, 0x00, 0x00, - 0x81, 0x18, 0x11, 0x00, 0x00, 0x00, - 0x14, 0x81, 0x48, 0x00, 0x00, 0x00, - 0x98, 0x09, 0x80, 0x00, 0x00, 0x00, - 0x08, 0x90, 0x89, 0x00, 0x00, 0x00, - 0x62, 0x06, 0x20, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x8a, 0x08, 0xa0, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x44, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_13[78] = - { - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0xc5, 0x1c, 0x51, 0x00, 0x00, 0x00, - 0x21, 0x82, 0x18, 0x00, 0x00, 0x00, - 0x12, 0x31, 0x23, 0x00, 0x00, 0x00, - 0x08, 0xe0, 0x8e, 0x00, 0x00, 0x00, - 0x2e, 0x02, 0xe0, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x90, 0x99, 0x09, 0x00, 0x00, 0x00, - 0x02, 0x50, 0x25, 0x00, 0x00, 0x00, - 0x06, 0xa0, 0x6a, 0x00, 0x00, 0x00, - 0x2c, 0x02, 0xc0, 0x00, 0x00, 0x00, - 0x88, 0x68, 0x86, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_14[84] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x90, 0x99, 0x09, 0x00, 0x00, 0x00, - 0x02, 0x50, 0x25, 0x00, 0x00, 0x00, - 0x06, 0xa0, 0x6a, 0x00, 0x00, 0x00, - 0x2c, 0x02, 0xc0, 0x00, 0x00, 0x00, - 0x88, 0x68, 0x86, 0x00, 0x00, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0xc5, 0x1c, 0x51, 0x00, 0x00, 0x00, - 0x21, 0x82, 0x18, 0x00, 0x00, 0x00, - 0x12, 0x31, 0x23, 0x00, 0x00, 0x00, - 0x08, 0xe0, 0x8e, 0x00, 0x00, 0x00, - 0x2e, 0x02, 0xe0, 0x00, 0x00, 0x00, - 0xf2, 0xd6, 0x8e, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_15[90] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x90, 0x99, 0x09, 0x00, 0x00, 0x00, - 0x02, 0x50, 0x25, 0x00, 0x00, 0x00, - 0x06, 0xa0, 0x6a, 0x00, 0x00, 0x00, - 0x2c, 0x02, 0xc0, 0x00, 0x00, 0x00, - 0x88, 0x68, 0x86, 0x00, 0x00, 0x00, - 0x20, 0x62, 0x06, 0x00, 0x00, 0x00, - 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, - 0x42, 0x44, 0x24, 0x00, 0x00, 0x00, - 0x01, 0x90, 0x19, 0x00, 0x00, 0x00, - 0x14, 0x11, 0x41, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0xa8, 0x00, 0x00, 0x00, - 0x38, 0x03, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0c, 0x50, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_16[96] = - { - 0x20, 0x62, 0x06, 0x00, 0x00, 0x00, - 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, - 0x42, 0x44, 0x24, 0x00, 0x00, 0x00, - 0x01, 0x90, 0x19, 0x00, 0x00, 0x00, - 0x14, 0x11, 0x41, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0xa8, 0x00, 0x00, 0x00, - 0x38, 0x03, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0c, 0x50, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x90, 0x99, 0x09, 0x00, 0x00, 0x00, - 0x02, 0x50, 0x25, 0x00, 0x00, 0x00, - 0x06, 0xa0, 0x6a, 0x00, 0x00, 0x00, - 0x2c, 0x02, 0xc0, 0x00, 0x00, 0x00, - 0x88, 0x68, 0x86, 0x00, 0x00, 0x00, - 0xff, 0x6e, 0x0a, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_17[102] = - { - 0x20, 0x62, 0x06, 0x00, 0x00, 0x00, - 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, - 0x42, 0x44, 0x24, 0x00, 0x00, 0x00, - 0x01, 0x90, 0x19, 0x00, 0x00, 0x00, - 0x14, 0x11, 0x41, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0xa8, 0x00, 0x00, 0x00, - 0x38, 0x03, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0c, 0x50, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0xe4, 0x2e, 0x42, 0x00, 0x00, 0x00, - 0x24, 0x42, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x1a, 0x11, 0x00, 0x00, 0x00, - 0x18, 0x31, 0x83, 0x00, 0x00, 0x00, - 0x03, 0x90, 0x39, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0xa1, 0x00, 0x00, 0x00, - 0x04, 0x90, 0x49, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_18[108] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0xe4, 0x2e, 0x42, 0x00, 0x00, 0x00, - 0x24, 0x42, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x1a, 0x11, 0x00, 0x00, 0x00, - 0x18, 0x31, 0x83, 0x00, 0x00, 0x00, - 0x03, 0x90, 0x39, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0xa1, 0x00, 0x00, 0x00, - 0x04, 0x90, 0x49, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0x20, 0x62, 0x06, 0x00, 0x00, 0x00, - 0x80, 0x38, 0x03, 0x00, 0x00, 0x00, - 0x42, 0x44, 0x24, 0x00, 0x00, 0x00, - 0x01, 0x90, 0x19, 0x00, 0x00, 0x00, - 0x14, 0x11, 0x41, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0xa8, 0x00, 0x00, 0x00, - 0x38, 0x03, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0c, 0x50, 0x00, 0x00, 0x00, - 0x34, 0x50, 0xae, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_19[114] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0xe4, 0x2e, 0x42, 0x00, 0x00, 0x00, - 0x24, 0x42, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x1a, 0x11, 0x00, 0x00, 0x00, - 0x18, 0x31, 0x83, 0x00, 0x00, 0x00, - 0x03, 0x90, 0x39, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0xa1, 0x00, 0x00, 0x00, - 0x04, 0x90, 0x49, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x51, 0x00, 0x00, 0x00, - 0x80, 0xd8, 0x0d, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x0a, 0x20, 0xa2, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0xb8, 0x0b, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x10, 0x91, 0x00, 0x00, 0x00, - 0x56, 0x05, 0x60, 0x00, 0x00, 0x00, - 0xa2, 0x8a, 0x28, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_2[12] = - { - 0xec, 0xce, 0xcc, 0x00, 0x00, 0x00, - 0x93, 0xb9, 0x3b, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_20[120] = - { - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x51, 0x00, 0x00, 0x00, - 0x80, 0xd8, 0x0d, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x0a, 0x20, 0xa2, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0xb8, 0x0b, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x10, 0x91, 0x00, 0x00, 0x00, - 0x56, 0x05, 0x60, 0x00, 0x00, 0x00, - 0xa2, 0x8a, 0x28, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0xe4, 0x2e, 0x42, 0x00, 0x00, 0x00, - 0x24, 0x42, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x1a, 0x11, 0x00, 0x00, 0x00, - 0x18, 0x31, 0x83, 0x00, 0x00, 0x00, - 0x03, 0x90, 0x39, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0xa1, 0x00, 0x00, 0x00, - 0x04, 0x90, 0x49, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0x98, 0xa2, 0x95, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_21[126] = - { - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x51, 0x00, 0x00, 0x00, - 0x80, 0xd8, 0x0d, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x0a, 0x20, 0xa2, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0xb8, 0x0b, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x10, 0x91, 0x00, 0x00, 0x00, - 0x56, 0x05, 0x60, 0x00, 0x00, 0x00, - 0xa2, 0x8a, 0x28, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x70, 0x07, 0x00, 0x00, 0x00, - 0x0c, 0x10, 0xc1, 0x00, 0x00, 0x00, - 0x40, 0xc4, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x06, 0xa0, 0x00, 0x00, 0x00, - 0x86, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x24, 0x82, 0x48, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x90, 0x00, 0x00, 0x00, - 0xc0, 0x2c, 0x02, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_22[132] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x70, 0x07, 0x00, 0x00, 0x00, - 0x0c, 0x10, 0xc1, 0x00, 0x00, 0x00, - 0x40, 0xc4, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x06, 0xa0, 0x00, 0x00, 0x00, - 0x86, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x24, 0x82, 0x48, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x90, 0x00, 0x00, 0x00, - 0xc0, 0x2c, 0x02, 0x00, 0x00, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x51, 0x00, 0x00, 0x00, - 0x80, 0xd8, 0x0d, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x0a, 0x20, 0xa2, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x0e, 0x00, 0x00, 0x00, - 0xb8, 0x0b, 0x80, 0x00, 0x00, 0x00, - 0x09, 0x10, 0x91, 0x00, 0x00, 0x00, - 0x56, 0x05, 0x60, 0x00, 0x00, 0x00, - 0xa2, 0x8a, 0x28, 0x00, 0x00, 0x00, - 0x1a, 0xaa, 0xee, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_23[138] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x70, 0x07, 0x00, 0x00, 0x00, - 0x0c, 0x10, 0xc1, 0x00, 0x00, 0x00, - 0x40, 0xc4, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x06, 0xa0, 0x00, 0x00, 0x00, - 0x86, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x24, 0x82, 0x48, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x90, 0x00, 0x00, 0x00, - 0xc0, 0x2c, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x61, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x30, 0x23, 0x00, 0x00, 0x00, - 0x40, 0x54, 0x05, 0x00, 0x00, 0x00, - 0x21, 0x82, 0x18, 0x00, 0x00, 0x00, - 0x81, 0x18, 0x11, 0x00, 0x00, 0x00, - 0x14, 0x81, 0x48, 0x00, 0x00, 0x00, - 0x98, 0x09, 0x80, 0x00, 0x00, 0x00, - 0x08, 0x90, 0x89, 0x00, 0x00, 0x00, - 0x62, 0x06, 0x20, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x8a, 0x08, 0xa0, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x44, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_24[144] = - { - 0x10, 0x61, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x30, 0x23, 0x00, 0x00, 0x00, - 0x40, 0x54, 0x05, 0x00, 0x00, 0x00, - 0x21, 0x82, 0x18, 0x00, 0x00, 0x00, - 0x81, 0x18, 0x11, 0x00, 0x00, 0x00, - 0x14, 0x81, 0x48, 0x00, 0x00, 0x00, - 0x98, 0x09, 0x80, 0x00, 0x00, 0x00, - 0x08, 0x90, 0x89, 0x00, 0x00, 0x00, - 0x62, 0x06, 0x20, 0x00, 0x00, 0x00, - 0x24, 0x22, 0x42, 0x00, 0x00, 0x00, - 0x8a, 0x08, 0xa0, 0x00, 0x00, 0x00, - 0x84, 0x48, 0x44, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x70, 0x07, 0x00, 0x00, 0x00, - 0x0c, 0x10, 0xc1, 0x00, 0x00, 0x00, - 0x40, 0xc4, 0x0c, 0x00, 0x00, 0x00, - 0x6a, 0x06, 0xa0, 0x00, 0x00, 0x00, - 0x86, 0x08, 0x60, 0x00, 0x00, 0x00, - 0x24, 0x82, 0x48, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x90, 0x00, 0x00, 0x00, - 0xc0, 0x2c, 0x02, 0x00, 0x00, 0x00, - 0x88, 0x32, 0x59, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_3[18] = - { - 0x9b, 0x29, 0xb2, 0x00, 0x00, 0x00, - 0x49, 0xd4, 0x9d, 0x00, 0x00, 0x00, - 0x3e, 0x83, 0xe8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_4[24] = - { - 0x8b, 0x28, 0xb2, 0x00, 0x00, 0x00, - 0x14, 0xb1, 0x4b, 0x00, 0x00, 0x00, - 0x22, 0xd2, 0x2d, 0x00, 0x00, 0x00, - 0x45, 0x54, 0x55, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_5[30] = - { - 0x53, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x64, 0xb6, 0x4b, 0x00, 0x00, 0x00, - 0x0e, 0xe0, 0xee, 0x00, 0x00, 0x00, - 0xa9, 0xca, 0x9c, 0x00, 0x00, 0x00, - 0xb8, 0x3b, 0x83, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_6[36] = - { - 0xd1, 0x4d, 0x14, 0x00, 0x00, 0x00, - 0x45, 0x34, 0x53, 0x00, 0x00, 0x00, - 0x22, 0xd2, 0x2d, 0x00, 0x00, 0x00, - 0x16, 0xc1, 0x6c, 0x00, 0x00, 0x00, - 0x0b, 0xa0, 0xba, 0x00, 0x00, 0x00, - 0xe8, 0x8e, 0x88, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_7[42] = - { - 0xd3, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x25, 0x32, 0x53, 0x00, 0x00, 0x00, - 0x30, 0xd3, 0x05, 0x00, 0x00, 0x00, - 0x06, 0x48, 0x6c, 0x00, 0x00, 0x00, - 0xc0, 0xb8, 0x1b, 0x00, 0x00, 0x00, - 0x2a, 0xa2, 0xaa, 0x00, 0x00, 0x00, - 0xa8, 0x4e, 0x84, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_8[48] = - { - 0x81, 0x60, 0x16, 0x00, 0x00, 0x00, - 0x40, 0x3c, 0x03, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x09, 0x00, 0x00, 0x00, - 0x06, 0x50, 0x65, 0x00, 0x00, 0x00, - 0x20, 0x4a, 0x84, 0x00, 0x00, 0x00, - 0x8a, 0xa0, 0xaa, 0x00, 0x00, 0x00, - 0x33, 0x03, 0x30, 0x00, 0x00, 0x00, - 0x4c, 0x84, 0xc8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask24_9[54] = - { - 0xd3, 0x65, 0x36, 0x00, 0x00, 0x00, - 0x64, 0x26, 0x42, 0x00, 0x00, 0x00, - 0x18, 0x41, 0xc4, 0x00, 0x00, 0x00, - 0xa0, 0x4a, 0x04, 0x00, 0x00, 0x00, - 0x81, 0x38, 0x13, 0x00, 0x00, 0x00, - 0x22, 0xa2, 0x2a, 0x00, 0x00, 0x00, - 0x08, 0x70, 0x87, 0x00, 0x00, 0x00, - 0x04, 0x90, 0x49, 0x00, 0x00, 0x00, - 0x01, 0xc0, 0x1c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_1[6] = - { - 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_10[60] = - { - 0x51, 0x4d, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x45, 0x00, 0x00, 0x00, - 0x80, 0xd1, 0x09, 0x80, 0x00, 0x00, - 0x24, 0x2a, 0x05, 0x00, 0x00, 0x00, - 0x0a, 0x24, 0xa0, 0x80, 0x00, 0x00, - 0x00, 0xe4, 0x03, 0x00, 0x00, 0x00, - 0xb8, 0x08, 0x02, 0x80, 0x00, 0x00, - 0x09, 0x10, 0xc9, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x58, 0x80, 0x00, 0x00, - 0xa2, 0x86, 0x22, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_11[66] = - { - 0x53, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x21, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x72, 0x50, 0x00, 0x00, 0x00, - 0x0c, 0x11, 0x81, 0x80, 0x00, 0x00, - 0x40, 0xc0, 0xa2, 0x00, 0x00, 0x00, - 0x6a, 0x08, 0x88, 0x80, 0x00, 0x00, - 0x86, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x24, 0x8e, 0x02, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x44, 0x00, 0x00, 0x00, - 0xc0, 0x24, 0x41, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_12[72] = - { - 0x10, 0x62, 0x82, 0x80, 0x00, 0x00, - 0x02, 0x38, 0x45, 0x00, 0x00, 0x00, - 0x40, 0x56, 0x04, 0x00, 0x00, 0x00, - 0x21, 0x80, 0x54, 0x80, 0x00, 0x00, - 0x81, 0x10, 0x29, 0x80, 0x00, 0x00, - 0x14, 0x80, 0x13, 0x00, 0x00, 0x00, - 0x98, 0x04, 0x81, 0x00, 0x00, 0x00, - 0x08, 0x92, 0x48, 0x00, 0x00, 0x00, - 0x62, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x24, 0x28, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x84, 0x45, 0x22, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_13[78] = - { - 0x51, 0x4d, 0x12, 0x00, 0x00, 0x00, - 0xc5, 0x14, 0x6d, 0x00, 0x00, 0x00, - 0x21, 0x81, 0x54, 0x80, 0x00, 0x00, - 0x12, 0x32, 0x17, 0x00, 0x00, 0x00, - 0x08, 0xe2, 0x8c, 0x80, 0x00, 0x00, - 0x2e, 0x0a, 0xa2, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x65, 0x00, 0x00, 0x00, - 0x90, 0x9b, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x52, 0xb0, 0x80, 0x00, 0x00, - 0x06, 0xa1, 0x4c, 0x80, 0x00, 0x00, - 0x2c, 0x0c, 0x88, 0x80, 0x00, 0x00, - 0x88, 0x68, 0x4b, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_14[84] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x65, 0x00, 0x00, 0x00, - 0x90, 0x9b, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x52, 0xb0, 0x80, 0x00, 0x00, - 0x06, 0xa1, 0x4c, 0x80, 0x00, 0x00, - 0x2c, 0x0c, 0x88, 0x80, 0x00, 0x00, - 0x88, 0x68, 0x4b, 0x00, 0x00, 0x00, - 0x51, 0x4d, 0x12, 0x00, 0x00, 0x00, - 0xc5, 0x14, 0x6d, 0x00, 0x00, 0x00, - 0x21, 0x81, 0x54, 0x80, 0x00, 0x00, - 0x12, 0x32, 0x17, 0x00, 0x00, 0x00, - 0x08, 0xe2, 0x8c, 0x80, 0x00, 0x00, - 0x2e, 0x0a, 0xa2, 0x00, 0x00, 0x00, - 0x73, 0x76, 0x61, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_15[90] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x65, 0x00, 0x00, 0x00, - 0x90, 0x9b, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x52, 0xb0, 0x80, 0x00, 0x00, - 0x06, 0xa1, 0x4c, 0x80, 0x00, 0x00, - 0x2c, 0x0c, 0x88, 0x80, 0x00, 0x00, - 0x88, 0x68, 0x4b, 0x00, 0x00, 0x00, - 0x20, 0x68, 0x0a, 0x80, 0x00, 0x00, - 0x80, 0x33, 0x09, 0x00, 0x00, 0x00, - 0x42, 0x41, 0x60, 0x80, 0x00, 0x00, - 0x01, 0x90, 0x33, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0x81, 0x80, 0x00, 0x00, - 0x38, 0x0d, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0a, 0x14, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_16[96] = - { - 0x20, 0x68, 0x0a, 0x80, 0x00, 0x00, - 0x80, 0x33, 0x09, 0x00, 0x00, 0x00, - 0x42, 0x41, 0x60, 0x80, 0x00, 0x00, - 0x01, 0x90, 0x33, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0x81, 0x80, 0x00, 0x00, - 0x38, 0x0d, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0a, 0x14, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x65, 0x00, 0x00, 0x00, - 0x90, 0x9b, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x52, 0xb0, 0x80, 0x00, 0x00, - 0x06, 0xa1, 0x4c, 0x80, 0x00, 0x00, - 0x2c, 0x0c, 0x88, 0x80, 0x00, 0x00, - 0x88, 0x68, 0x4b, 0x00, 0x00, 0x00, - 0x16, 0xe8, 0xdc, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_17[102] = - { - 0x20, 0x68, 0x0a, 0x80, 0x00, 0x00, - 0x80, 0x33, 0x09, 0x00, 0x00, 0x00, - 0x42, 0x41, 0x60, 0x80, 0x00, 0x00, - 0x01, 0x90, 0x33, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0x81, 0x80, 0x00, 0x00, - 0x38, 0x0d, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0a, 0x14, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0xe4, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x24, 0x41, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x12, 0x14, 0x80, 0x00, 0x00, - 0x18, 0x30, 0x2c, 0x80, 0x00, 0x00, - 0x03, 0x99, 0x41, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0x0a, 0x80, 0x00, 0x00, - 0x04, 0x90, 0xa9, 0x00, 0x00, 0x00, - 0x00, 0xe4, 0x01, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_18[108] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0xe4, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x24, 0x41, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x12, 0x14, 0x80, 0x00, 0x00, - 0x18, 0x30, 0x2c, 0x80, 0x00, 0x00, - 0x03, 0x99, 0x41, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0x0a, 0x80, 0x00, 0x00, - 0x04, 0x90, 0xa9, 0x00, 0x00, 0x00, - 0x00, 0xe4, 0x01, 0x80, 0x00, 0x00, - 0x20, 0x68, 0x0a, 0x80, 0x00, 0x00, - 0x80, 0x33, 0x09, 0x00, 0x00, 0x00, - 0x42, 0x41, 0x60, 0x80, 0x00, 0x00, - 0x01, 0x90, 0x33, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0x81, 0x80, 0x00, 0x00, - 0x38, 0x0d, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0a, 0x14, 0x00, 0x00, 0x00, - 0xce, 0x9b, 0xe1, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_19[114] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0xe4, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x24, 0x41, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x12, 0x14, 0x80, 0x00, 0x00, - 0x18, 0x30, 0x2c, 0x80, 0x00, 0x00, - 0x03, 0x99, 0x41, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0x0a, 0x80, 0x00, 0x00, - 0x04, 0x90, 0xa9, 0x00, 0x00, 0x00, - 0x00, 0xe4, 0x01, 0x80, 0x00, 0x00, - 0x51, 0x4d, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x45, 0x00, 0x00, 0x00, - 0x80, 0xd1, 0x09, 0x80, 0x00, 0x00, - 0x24, 0x2a, 0x05, 0x00, 0x00, 0x00, - 0x0a, 0x24, 0xa0, 0x80, 0x00, 0x00, - 0x00, 0xe4, 0x03, 0x00, 0x00, 0x00, - 0xb8, 0x08, 0x02, 0x80, 0x00, 0x00, - 0x09, 0x10, 0xc9, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x58, 0x80, 0x00, 0x00, - 0xa2, 0x86, 0x22, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_2[12] = - { - 0xec, 0xce, 0xcc, 0x00, 0x00, 0x00, - 0x93, 0xb1, 0xb3, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_20[120] = - { - 0x51, 0x4d, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x45, 0x00, 0x00, 0x00, - 0x80, 0xd1, 0x09, 0x80, 0x00, 0x00, - 0x24, 0x2a, 0x05, 0x00, 0x00, 0x00, - 0x0a, 0x24, 0xa0, 0x80, 0x00, 0x00, - 0x00, 0xe4, 0x03, 0x00, 0x00, 0x00, - 0xb8, 0x08, 0x02, 0x80, 0x00, 0x00, - 0x09, 0x10, 0xc9, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x58, 0x80, 0x00, 0x00, - 0xa2, 0x86, 0x22, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0xe4, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x24, 0x41, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x12, 0x14, 0x80, 0x00, 0x00, - 0x18, 0x30, 0x2c, 0x80, 0x00, 0x00, - 0x03, 0x99, 0x41, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0x0a, 0x80, 0x00, 0x00, - 0x04, 0x90, 0xa9, 0x00, 0x00, 0x00, - 0x00, 0xe4, 0x01, 0x80, 0x00, 0x00, - 0x1b, 0x8a, 0xa0, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_21[126] = - { - 0x51, 0x4d, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x45, 0x00, 0x00, 0x00, - 0x80, 0xd1, 0x09, 0x80, 0x00, 0x00, - 0x24, 0x2a, 0x05, 0x00, 0x00, 0x00, - 0x0a, 0x24, 0xa0, 0x80, 0x00, 0x00, - 0x00, 0xe4, 0x03, 0x00, 0x00, 0x00, - 0xb8, 0x08, 0x02, 0x80, 0x00, 0x00, - 0x09, 0x10, 0xc9, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x58, 0x80, 0x00, 0x00, - 0xa2, 0x86, 0x22, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x21, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x72, 0x50, 0x00, 0x00, 0x00, - 0x0c, 0x11, 0x81, 0x80, 0x00, 0x00, - 0x40, 0xc0, 0xa2, 0x00, 0x00, 0x00, - 0x6a, 0x08, 0x88, 0x80, 0x00, 0x00, - 0x86, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x24, 0x8e, 0x02, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x44, 0x00, 0x00, 0x00, - 0xc0, 0x24, 0x41, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_22[132] = - { - 0x53, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x21, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x72, 0x50, 0x00, 0x00, 0x00, - 0x0c, 0x11, 0x81, 0x80, 0x00, 0x00, - 0x40, 0xc0, 0xa2, 0x00, 0x00, 0x00, - 0x6a, 0x08, 0x88, 0x80, 0x00, 0x00, - 0x86, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x24, 0x8e, 0x02, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x44, 0x00, 0x00, 0x00, - 0xc0, 0x24, 0x41, 0x80, 0x00, 0x00, - 0x51, 0x4d, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x14, 0x45, 0x00, 0x00, 0x00, - 0x80, 0xd1, 0x09, 0x80, 0x00, 0x00, - 0x24, 0x2a, 0x05, 0x00, 0x00, 0x00, - 0x0a, 0x24, 0xa0, 0x80, 0x00, 0x00, - 0x00, 0xe4, 0x03, 0x00, 0x00, 0x00, - 0xb8, 0x08, 0x02, 0x80, 0x00, 0x00, - 0x09, 0x10, 0xc9, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x58, 0x80, 0x00, 0x00, - 0xa2, 0x86, 0x22, 0x00, 0x00, 0x00, - 0x15, 0xa2, 0x99, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_23[138] = - { - 0x53, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x21, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x72, 0x50, 0x00, 0x00, 0x00, - 0x0c, 0x11, 0x81, 0x80, 0x00, 0x00, - 0x40, 0xc0, 0xa2, 0x00, 0x00, 0x00, - 0x6a, 0x08, 0x88, 0x80, 0x00, 0x00, - 0x86, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x24, 0x8e, 0x02, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x44, 0x00, 0x00, 0x00, - 0xc0, 0x24, 0x41, 0x80, 0x00, 0x00, - 0x10, 0x62, 0x82, 0x80, 0x00, 0x00, - 0x02, 0x38, 0x45, 0x00, 0x00, 0x00, - 0x40, 0x56, 0x04, 0x00, 0x00, 0x00, - 0x21, 0x80, 0x54, 0x80, 0x00, 0x00, - 0x81, 0x10, 0x29, 0x80, 0x00, 0x00, - 0x14, 0x80, 0x13, 0x00, 0x00, 0x00, - 0x98, 0x04, 0x81, 0x00, 0x00, 0x00, - 0x08, 0x92, 0x48, 0x00, 0x00, 0x00, - 0x62, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x24, 0x28, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x84, 0x45, 0x22, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_24[144] = - { - 0x10, 0x62, 0x82, 0x80, 0x00, 0x00, - 0x02, 0x38, 0x45, 0x00, 0x00, 0x00, - 0x40, 0x56, 0x04, 0x00, 0x00, 0x00, - 0x21, 0x80, 0x54, 0x80, 0x00, 0x00, - 0x81, 0x10, 0x29, 0x80, 0x00, 0x00, - 0x14, 0x80, 0x13, 0x00, 0x00, 0x00, - 0x98, 0x04, 0x81, 0x00, 0x00, 0x00, - 0x08, 0x92, 0x48, 0x00, 0x00, 0x00, - 0x62, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x24, 0x28, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x84, 0x45, 0x22, 0x00, 0x00, 0x00, - 0x53, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x21, 0x00, 0x00, 0x00, - 0x10, 0x91, 0x34, 0x00, 0x00, 0x00, - 0x00, 0x72, 0x50, 0x00, 0x00, 0x00, - 0x0c, 0x11, 0x81, 0x80, 0x00, 0x00, - 0x40, 0xc0, 0xa2, 0x00, 0x00, 0x00, - 0x6a, 0x08, 0x88, 0x80, 0x00, 0x00, - 0x86, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x24, 0x8e, 0x02, 0x00, 0x00, 0x00, - 0x89, 0x08, 0x44, 0x00, 0x00, 0x00, - 0xc0, 0x24, 0x41, 0x80, 0x00, 0x00, - 0xf9, 0x0c, 0x14, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_25[150] = - { - 0x10, 0x62, 0x82, 0x80, 0x00, 0x00, - 0x02, 0x38, 0x45, 0x00, 0x00, 0x00, - 0x40, 0x56, 0x04, 0x00, 0x00, 0x00, - 0x21, 0x80, 0x54, 0x80, 0x00, 0x00, - 0x81, 0x10, 0x29, 0x80, 0x00, 0x00, - 0x14, 0x80, 0x13, 0x00, 0x00, 0x00, - 0x98, 0x04, 0x81, 0x00, 0x00, 0x00, - 0x08, 0x92, 0x48, 0x00, 0x00, 0x00, - 0x62, 0x09, 0x40, 0x00, 0x00, 0x00, - 0x24, 0x28, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x01, 0x18, 0x00, 0x00, 0x00, - 0x84, 0x45, 0x22, 0x00, 0x00, 0x00, - 0x10, 0x65, 0x12, 0x00, 0x00, 0x00, - 0x02, 0x36, 0x64, 0x00, 0x00, 0x00, - 0x40, 0x50, 0x54, 0x80, 0x00, 0x00, - 0x21, 0x88, 0x12, 0x00, 0x00, 0x00, - 0x81, 0x19, 0x40, 0x00, 0x00, 0x00, - 0x14, 0x83, 0x08, 0x00, 0x00, 0x00, - 0x98, 0x02, 0x11, 0x00, 0x00, 0x00, - 0x08, 0x90, 0x3c, 0x00, 0x00, 0x00, - 0x62, 0x0e, 0x80, 0x00, 0x00, 0x00, - 0x24, 0x20, 0xa1, 0x00, 0x00, 0x00, - 0x8a, 0x08, 0x01, 0x80, 0x00, 0x00, - 0x84, 0x40, 0x49, 0x00, 0x00, 0x00, - 0x1c, 0x20, 0x8a, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_3[18] = - { - 0x9b, 0x89, 0x9b, 0x00, 0x00, 0x00, - 0x4f, 0x14, 0x6d, 0x80, 0x00, 0x00, - 0x3c, 0x63, 0x72, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_4[24] = - { - 0x8b, 0x24, 0x9b, 0x00, 0x00, 0x00, - 0x14, 0xb2, 0x6d, 0x00, 0x00, 0x00, - 0x22, 0xd8, 0x56, 0x80, 0x00, 0x00, - 0x45, 0x55, 0x25, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_5[30] = - { - 0x53, 0x65, 0x13, 0x00, 0x00, 0x00, - 0x64, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x0c, 0xc0, 0xc6, 0x80, 0x00, 0x00, - 0x82, 0xaa, 0x1c, 0x00, 0x00, 0x00, - 0x09, 0x32, 0x29, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_6[36] = - { - 0x51, 0x4d, 0x12, 0x00, 0x00, 0x00, - 0xc5, 0x14, 0x6d, 0x00, 0x00, 0x00, - 0x21, 0x81, 0x54, 0x80, 0x00, 0x00, - 0x12, 0x32, 0x17, 0x00, 0x00, 0x00, - 0x08, 0xe2, 0x8c, 0x80, 0x00, 0x00, - 0x2e, 0x0a, 0xa2, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_7[42] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0x21, 0x32, 0x65, 0x00, 0x00, 0x00, - 0x90, 0x9b, 0x14, 0x00, 0x00, 0x00, - 0x02, 0x52, 0xb0, 0x80, 0x00, 0x00, - 0x06, 0xa1, 0x4c, 0x80, 0x00, 0x00, - 0x2c, 0x0c, 0x88, 0x80, 0x00, 0x00, - 0x88, 0x68, 0x4b, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_8[48] = - { - 0x20, 0x68, 0x0a, 0x80, 0x00, 0x00, - 0x80, 0x33, 0x09, 0x00, 0x00, 0x00, - 0x42, 0x41, 0x60, 0x80, 0x00, 0x00, - 0x01, 0x90, 0x33, 0x00, 0x00, 0x00, - 0x14, 0x14, 0x46, 0x00, 0x00, 0x00, - 0x0a, 0x80, 0x81, 0x80, 0x00, 0x00, - 0x38, 0x0d, 0x80, 0x00, 0x00, 0x00, - 0xc5, 0x0a, 0x14, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask25_9[54] = - { - 0x53, 0x65, 0x92, 0x00, 0x00, 0x00, - 0xe4, 0x26, 0x64, 0x00, 0x00, 0x00, - 0x24, 0x41, 0x44, 0x00, 0x00, 0x00, - 0xa1, 0x12, 0x14, 0x80, 0x00, 0x00, - 0x18, 0x30, 0x2c, 0x80, 0x00, 0x00, - 0x03, 0x99, 0x41, 0x00, 0x00, 0x00, - 0x8a, 0x18, 0x0a, 0x80, 0x00, 0x00, - 0x04, 0x90, 0xa9, 0x00, 0x00, 0x00, - 0x00, 0xe4, 0x01, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_1[6] = - { - 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_10[60] = - { - 0xd1, 0x06, 0x88, 0x00, 0x00, 0x00, - 0x44, 0x52, 0x22, 0x80, 0x00, 0x00, - 0x10, 0x98, 0x84, 0xc0, 0x00, 0x00, - 0xa0, 0x55, 0x02, 0x80, 0x00, 0x00, - 0x4a, 0x0a, 0x50, 0x40, 0x00, 0x00, - 0x40, 0x32, 0x01, 0x80, 0x00, 0x00, - 0x80, 0x2c, 0x01, 0x40, 0x00, 0x00, - 0x0c, 0x90, 0x64, 0x80, 0x00, 0x00, - 0x05, 0x88, 0x2c, 0x40, 0x00, 0x00, - 0x62, 0x23, 0x11, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_11[66] = - { - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x22, 0x11, 0x10, 0x80, 0x00, 0x00, - 0x13, 0x40, 0x9a, 0x00, 0x00, 0x00, - 0x25, 0x01, 0x28, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xc0, 0xc0, 0x00, 0x00, - 0x0a, 0x20, 0x51, 0x00, 0x00, 0x00, - 0x88, 0x8c, 0x44, 0x40, 0x00, 0x00, - 0x06, 0x80, 0x34, 0x00, 0x00, 0x00, - 0xe0, 0x27, 0x01, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x22, 0x00, 0x00, 0x00, - 0x44, 0x1a, 0x20, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_12[72] = - { - 0x28, 0x29, 0x41, 0x40, 0x00, 0x00, - 0x84, 0x54, 0x22, 0x80, 0x00, 0x00, - 0x60, 0x43, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x02, 0x98, 0x14, 0xc0, 0x00, 0x00, - 0x01, 0x30, 0x09, 0x80, 0x00, 0x00, - 0x48, 0x12, 0x40, 0x80, 0x00, 0x00, - 0x24, 0x81, 0x24, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x04, 0x50, 0x00, 0x00, 0x00, - 0x11, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x52, 0x22, 0x91, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_13[78] = - { - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x81, 0x24, 0x09, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x30, 0x81, 0x84, 0x00, 0x00, 0x00, - 0x21, 0x11, 0x08, 0x80, 0x00, 0x00, - 0x03, 0xc0, 0x1e, 0x00, 0x00, 0x00, - 0xe8, 0x07, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x10, 0x50, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x00, 0xc0, 0x00, 0x00, - 0x04, 0x90, 0x24, 0x80, 0x00, 0x00, - 0x08, 0xa8, 0x45, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_14[84] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x26, 0x51, 0x32, 0x80, 0x00, 0x00, - 0xb1, 0x45, 0x8a, 0x00, 0x00, 0x00, - 0x2b, 0x09, 0x58, 0x40, 0x00, 0x00, - 0x14, 0xc8, 0xa6, 0x40, 0x00, 0x00, - 0xc8, 0x8e, 0x44, 0x40, 0x00, 0x00, - 0x84, 0xb4, 0x25, 0x80, 0x00, 0x00, - 0xd1, 0x26, 0x89, 0x00, 0x00, 0x00, - 0x46, 0xd2, 0x36, 0x80, 0x00, 0x00, - 0x15, 0x48, 0xaa, 0x40, 0x00, 0x00, - 0x21, 0x71, 0x0b, 0x80, 0x00, 0x00, - 0x28, 0xc9, 0x46, 0x40, 0x00, 0x00, - 0xaa, 0x25, 0x51, 0x00, 0x00, 0x00, - 0x5d, 0xa7, 0x78, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_15[90] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x26, 0x51, 0x32, 0x80, 0x00, 0x00, - 0xb1, 0x45, 0x8a, 0x00, 0x00, 0x00, - 0x2b, 0x09, 0x58, 0x40, 0x00, 0x00, - 0x14, 0xc8, 0xa6, 0x40, 0x00, 0x00, - 0xc8, 0x8e, 0x44, 0x40, 0x00, 0x00, - 0x84, 0xb4, 0x25, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x30, 0x91, 0x84, 0x80, 0x00, 0x00, - 0x16, 0x08, 0xb0, 0x40, 0x00, 0x00, - 0x03, 0x30, 0x19, 0x80, 0x00, 0x00, - 0x44, 0x62, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x18, 0x40, 0xc0, 0x00, 0x00, - 0xd8, 0x06, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0x45, 0x0a, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_16[96] = - { - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x30, 0x91, 0x84, 0x80, 0x00, 0x00, - 0x16, 0x08, 0xb0, 0x40, 0x00, 0x00, - 0x03, 0x30, 0x19, 0x80, 0x00, 0x00, - 0x44, 0x62, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x18, 0x40, 0xc0, 0x00, 0x00, - 0xd8, 0x06, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0x45, 0x0a, 0x00, 0x00, 0x00, - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x26, 0x51, 0x32, 0x80, 0x00, 0x00, - 0xb1, 0x45, 0x8a, 0x00, 0x00, 0x00, - 0x2b, 0x09, 0x58, 0x40, 0x00, 0x00, - 0x14, 0xc8, 0xa6, 0x40, 0x00, 0x00, - 0xc8, 0x8e, 0x44, 0x40, 0x00, 0x00, - 0x84, 0xb4, 0x25, 0x80, 0x00, 0x00, - 0x3c, 0xaf, 0x88, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_17[102] = - { - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x30, 0x91, 0x84, 0x80, 0x00, 0x00, - 0x16, 0x08, 0xb0, 0x40, 0x00, 0x00, - 0x03, 0x30, 0x19, 0x80, 0x00, 0x00, - 0x44, 0x62, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x18, 0x40, 0xc0, 0x00, 0x00, - 0xd8, 0x06, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0x45, 0x0a, 0x00, 0x00, 0x00, - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x14, 0x40, 0xa2, 0x00, 0x00, 0x00, - 0x21, 0x49, 0x0a, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x16, 0x40, 0x00, 0x00, - 0x94, 0x14, 0xa0, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x90, 0x54, 0x80, 0x00, 0x00, - 0x40, 0x1a, 0x00, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_18[108] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x14, 0x40, 0xa2, 0x00, 0x00, 0x00, - 0x21, 0x49, 0x0a, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x16, 0x40, 0x00, 0x00, - 0x94, 0x14, 0xa0, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x90, 0x54, 0x80, 0x00, 0x00, - 0x40, 0x1a, 0x00, 0xc0, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x30, 0x91, 0x84, 0x80, 0x00, 0x00, - 0x16, 0x08, 0xb0, 0x40, 0x00, 0x00, - 0x03, 0x30, 0x19, 0x80, 0x00, 0x00, - 0x44, 0x62, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x18, 0x40, 0xc0, 0x00, 0x00, - 0xd8, 0x06, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0x45, 0x0a, 0x00, 0x00, 0x00, - 0xaa, 0x0c, 0x83, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_19[114] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x14, 0x40, 0xa2, 0x00, 0x00, 0x00, - 0x21, 0x49, 0x0a, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x16, 0x40, 0x00, 0x00, - 0x94, 0x14, 0xa0, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x90, 0x54, 0x80, 0x00, 0x00, - 0x40, 0x1a, 0x00, 0xc0, 0x00, 0x00, - 0xd1, 0x06, 0x88, 0x00, 0x00, 0x00, - 0x44, 0x52, 0x22, 0x80, 0x00, 0x00, - 0x10, 0x98, 0x84, 0xc0, 0x00, 0x00, - 0xa0, 0x55, 0x02, 0x80, 0x00, 0x00, - 0x4a, 0x0a, 0x50, 0x40, 0x00, 0x00, - 0x40, 0x32, 0x01, 0x80, 0x00, 0x00, - 0x80, 0x2c, 0x01, 0x40, 0x00, 0x00, - 0x0c, 0x90, 0x64, 0x80, 0x00, 0x00, - 0x05, 0x88, 0x2c, 0x40, 0x00, 0x00, - 0x62, 0x23, 0x11, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_2[12] = - { - 0xec, 0xc7, 0x66, 0x00, 0x00, 0x00, - 0x1b, 0x38, 0xd9, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_20[120] = - { - 0xd1, 0x06, 0x88, 0x00, 0x00, 0x00, - 0x44, 0x52, 0x22, 0x80, 0x00, 0x00, - 0x10, 0x98, 0x84, 0xc0, 0x00, 0x00, - 0xa0, 0x55, 0x02, 0x80, 0x00, 0x00, - 0x4a, 0x0a, 0x50, 0x40, 0x00, 0x00, - 0x40, 0x32, 0x01, 0x80, 0x00, 0x00, - 0x80, 0x2c, 0x01, 0x40, 0x00, 0x00, - 0x0c, 0x90, 0x64, 0x80, 0x00, 0x00, - 0x05, 0x88, 0x2c, 0x40, 0x00, 0x00, - 0x62, 0x23, 0x11, 0x00, 0x00, 0x00, - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x14, 0x40, 0xa2, 0x00, 0x00, 0x00, - 0x21, 0x49, 0x0a, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x16, 0x40, 0x00, 0x00, - 0x94, 0x14, 0xa0, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x90, 0x54, 0x80, 0x00, 0x00, - 0x40, 0x1a, 0x00, 0xc0, 0x00, 0x00, - 0xf4, 0x08, 0xec, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_21[126] = - { - 0xd1, 0x06, 0x88, 0x00, 0x00, 0x00, - 0x44, 0x52, 0x22, 0x80, 0x00, 0x00, - 0x10, 0x98, 0x84, 0xc0, 0x00, 0x00, - 0xa0, 0x55, 0x02, 0x80, 0x00, 0x00, - 0x4a, 0x0a, 0x50, 0x40, 0x00, 0x00, - 0x40, 0x32, 0x01, 0x80, 0x00, 0x00, - 0x80, 0x2c, 0x01, 0x40, 0x00, 0x00, - 0x0c, 0x90, 0x64, 0x80, 0x00, 0x00, - 0x05, 0x88, 0x2c, 0x40, 0x00, 0x00, - 0x62, 0x23, 0x11, 0x00, 0x00, 0x00, - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x22, 0x11, 0x10, 0x80, 0x00, 0x00, - 0x13, 0x40, 0x9a, 0x00, 0x00, 0x00, - 0x25, 0x01, 0x28, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xc0, 0xc0, 0x00, 0x00, - 0x0a, 0x20, 0x51, 0x00, 0x00, 0x00, - 0x88, 0x8c, 0x44, 0x40, 0x00, 0x00, - 0x06, 0x80, 0x34, 0x00, 0x00, 0x00, - 0xe0, 0x27, 0x01, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x22, 0x00, 0x00, 0x00, - 0x44, 0x1a, 0x20, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_22[132] = - { - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x22, 0x11, 0x10, 0x80, 0x00, 0x00, - 0x13, 0x40, 0x9a, 0x00, 0x00, 0x00, - 0x25, 0x01, 0x28, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xc0, 0xc0, 0x00, 0x00, - 0x0a, 0x20, 0x51, 0x00, 0x00, 0x00, - 0x88, 0x8c, 0x44, 0x40, 0x00, 0x00, - 0x06, 0x80, 0x34, 0x00, 0x00, 0x00, - 0xe0, 0x27, 0x01, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x22, 0x00, 0x00, 0x00, - 0x44, 0x1a, 0x20, 0xc0, 0x00, 0x00, - 0xd1, 0x06, 0x88, 0x00, 0x00, 0x00, - 0x44, 0x52, 0x22, 0x80, 0x00, 0x00, - 0x10, 0x98, 0x84, 0xc0, 0x00, 0x00, - 0xa0, 0x55, 0x02, 0x80, 0x00, 0x00, - 0x4a, 0x0a, 0x50, 0x40, 0x00, 0x00, - 0x40, 0x32, 0x01, 0x80, 0x00, 0x00, - 0x80, 0x2c, 0x01, 0x40, 0x00, 0x00, - 0x0c, 0x90, 0x64, 0x80, 0x00, 0x00, - 0x05, 0x88, 0x2c, 0x40, 0x00, 0x00, - 0x62, 0x23, 0x11, 0x00, 0x00, 0x00, - 0x13, 0xc6, 0x6b, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_23[138] = - { - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x22, 0x11, 0x10, 0x80, 0x00, 0x00, - 0x13, 0x40, 0x9a, 0x00, 0x00, 0x00, - 0x25, 0x01, 0x28, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xc0, 0xc0, 0x00, 0x00, - 0x0a, 0x20, 0x51, 0x00, 0x00, 0x00, - 0x88, 0x8c, 0x44, 0x40, 0x00, 0x00, - 0x06, 0x80, 0x34, 0x00, 0x00, 0x00, - 0xe0, 0x27, 0x01, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x22, 0x00, 0x00, 0x00, - 0x44, 0x1a, 0x20, 0xc0, 0x00, 0x00, - 0x28, 0x29, 0x41, 0x40, 0x00, 0x00, - 0x84, 0x54, 0x22, 0x80, 0x00, 0x00, - 0x60, 0x43, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x02, 0x98, 0x14, 0xc0, 0x00, 0x00, - 0x01, 0x30, 0x09, 0x80, 0x00, 0x00, - 0x48, 0x12, 0x40, 0x80, 0x00, 0x00, - 0x24, 0x81, 0x24, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x04, 0x50, 0x00, 0x00, 0x00, - 0x11, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x52, 0x22, 0x91, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_24[144] = - { - 0x28, 0x29, 0x41, 0x40, 0x00, 0x00, - 0x84, 0x54, 0x22, 0x80, 0x00, 0x00, - 0x60, 0x43, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x02, 0x98, 0x14, 0xc0, 0x00, 0x00, - 0x01, 0x30, 0x09, 0x80, 0x00, 0x00, - 0x48, 0x12, 0x40, 0x80, 0x00, 0x00, - 0x24, 0x81, 0x24, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x04, 0x50, 0x00, 0x00, 0x00, - 0x11, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x52, 0x22, 0x91, 0x00, 0x00, 0x00, - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x22, 0x11, 0x10, 0x80, 0x00, 0x00, - 0x13, 0x40, 0x9a, 0x00, 0x00, 0x00, - 0x25, 0x01, 0x28, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xc0, 0xc0, 0x00, 0x00, - 0x0a, 0x20, 0x51, 0x00, 0x00, 0x00, - 0x88, 0x8c, 0x44, 0x40, 0x00, 0x00, - 0x06, 0x80, 0x34, 0x00, 0x00, 0x00, - 0xe0, 0x27, 0x01, 0x00, 0x00, 0x00, - 0x84, 0x44, 0x22, 0x00, 0x00, 0x00, - 0x44, 0x1a, 0x20, 0xc0, 0x00, 0x00, - 0xdb, 0x4d, 0xd8, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_25[150] = - { - 0x28, 0x29, 0x41, 0x40, 0x00, 0x00, - 0x84, 0x54, 0x22, 0x80, 0x00, 0x00, - 0x60, 0x43, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x02, 0x98, 0x14, 0xc0, 0x00, 0x00, - 0x01, 0x30, 0x09, 0x80, 0x00, 0x00, - 0x48, 0x12, 0x40, 0x80, 0x00, 0x00, - 0x24, 0x81, 0x24, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x04, 0x50, 0x00, 0x00, 0x00, - 0x11, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x52, 0x22, 0x91, 0x00, 0x00, 0x00, - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x81, 0x24, 0x09, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x30, 0x81, 0x84, 0x00, 0x00, 0x00, - 0x21, 0x11, 0x08, 0x80, 0x00, 0x00, - 0x03, 0xc0, 0x1e, 0x00, 0x00, 0x00, - 0xe8, 0x07, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x10, 0x50, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x00, 0xc0, 0x00, 0x00, - 0x04, 0x90, 0x24, 0x80, 0x00, 0x00, - 0x08, 0xa8, 0x45, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_26[156] = - { - 0x51, 0x22, 0x89, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x81, 0x24, 0x09, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x30, 0x81, 0x84, 0x00, 0x00, 0x00, - 0x21, 0x11, 0x08, 0x80, 0x00, 0x00, - 0x03, 0xc0, 0x1e, 0x00, 0x00, 0x00, - 0xe8, 0x07, 0x40, 0x00, 0x00, 0x00, - 0x0a, 0x10, 0x50, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x00, 0xc0, 0x00, 0x00, - 0x04, 0x90, 0x24, 0x80, 0x00, 0x00, - 0x08, 0xa8, 0x45, 0x40, 0x00, 0x00, - 0x28, 0x29, 0x41, 0x40, 0x00, 0x00, - 0x84, 0x54, 0x22, 0x80, 0x00, 0x00, - 0x60, 0x43, 0x02, 0x00, 0x00, 0x00, - 0x05, 0x48, 0x2a, 0x40, 0x00, 0x00, - 0x02, 0x98, 0x14, 0xc0, 0x00, 0x00, - 0x01, 0x30, 0x09, 0x80, 0x00, 0x00, - 0x48, 0x12, 0x40, 0x80, 0x00, 0x00, - 0x24, 0x81, 0x24, 0x00, 0x00, 0x00, - 0x94, 0x04, 0xa0, 0x00, 0x00, 0x00, - 0x8a, 0x04, 0x50, 0x00, 0x00, 0x00, - 0x11, 0x80, 0x8c, 0x00, 0x00, 0x00, - 0x52, 0x22, 0x91, 0x00, 0x00, 0x00, - 0xf9, 0x13, 0x51, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_3[18] = - { - 0x99, 0xb4, 0xcd, 0x80, 0x00, 0x00, - 0x46, 0xda, 0x36, 0xc0, 0x00, 0x00, - 0x37, 0x29, 0xb9, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_4[24] = - { - 0x49, 0xb2, 0x4d, 0x80, 0x00, 0x00, - 0x26, 0xd1, 0x36, 0x80, 0x00, 0x00, - 0x85, 0x6c, 0x2b, 0x40, 0x00, 0x00, - 0x52, 0x5a, 0x92, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_5[30] = - { - 0x51, 0x32, 0x89, 0x80, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x0c, 0x68, 0x63, 0x40, 0x00, 0x00, - 0xa1, 0xc5, 0x0e, 0x00, 0x00, 0x00, - 0x22, 0x99, 0x14, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_6[36] = - { - 0xd1, 0x26, 0x89, 0x00, 0x00, 0x00, - 0x46, 0xd2, 0x36, 0x80, 0x00, 0x00, - 0x15, 0x48, 0xaa, 0x40, 0x00, 0x00, - 0x21, 0x71, 0x0b, 0x80, 0x00, 0x00, - 0x28, 0xc9, 0x46, 0x40, 0x00, 0x00, - 0xaa, 0x25, 0x51, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_7[42] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x26, 0x51, 0x32, 0x80, 0x00, 0x00, - 0xb1, 0x45, 0x8a, 0x00, 0x00, 0x00, - 0x2b, 0x09, 0x58, 0x40, 0x00, 0x00, - 0x14, 0xc8, 0xa6, 0x40, 0x00, 0x00, - 0xc8, 0x8e, 0x44, 0x40, 0x00, 0x00, - 0x84, 0xb4, 0x25, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_8[48] = - { - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x30, 0x91, 0x84, 0x80, 0x00, 0x00, - 0x16, 0x08, 0xb0, 0x40, 0x00, 0x00, - 0x03, 0x30, 0x19, 0x80, 0x00, 0x00, - 0x44, 0x62, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x18, 0x40, 0xc0, 0x00, 0x00, - 0xd8, 0x06, 0xc0, 0x00, 0x00, 0x00, - 0xa1, 0x45, 0x0a, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask26_9[54] = - { - 0x59, 0x22, 0xc9, 0x00, 0x00, 0x00, - 0x66, 0x43, 0x32, 0x00, 0x00, 0x00, - 0x14, 0x40, 0xa2, 0x00, 0x00, 0x00, - 0x21, 0x49, 0x0a, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x16, 0x40, 0x00, 0x00, - 0x94, 0x14, 0xa0, 0x80, 0x00, 0x00, - 0x80, 0xac, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x90, 0x54, 0x80, 0x00, 0x00, - 0x40, 0x1a, 0x00, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_1[6] = - { - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_10[60] = - { - 0xd1, 0x06, 0x06, 0xa0, 0x00, 0x00, - 0x44, 0x50, 0xea, 0x00, 0x00, 0x00, - 0x10, 0x9e, 0xa0, 0x40, 0x00, 0x00, - 0xa0, 0x50, 0x13, 0x00, 0x00, 0x00, - 0x4a, 0x08, 0x21, 0x40, 0x00, 0x00, - 0x40, 0x31, 0x04, 0xc0, 0x00, 0x00, - 0x80, 0x2a, 0x02, 0x20, 0x00, 0x00, - 0x0c, 0x90, 0x44, 0x20, 0x00, 0x00, - 0x05, 0x8b, 0x40, 0x00, 0x00, 0x00, - 0x62, 0x21, 0x18, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_11[66] = - { - 0x51, 0x23, 0x16, 0x80, 0x00, 0x00, - 0x22, 0x11, 0xa9, 0x00, 0x00, 0x00, - 0x13, 0x40, 0xa0, 0xa0, 0x00, 0x00, - 0x25, 0x06, 0x28, 0x40, 0x00, 0x00, - 0x18, 0x19, 0x10, 0x60, 0x00, 0x00, - 0x0a, 0x24, 0x45, 0xc0, 0x00, 0x00, - 0x88, 0x8a, 0x12, 0xa0, 0x00, 0x00, - 0x06, 0x81, 0x45, 0x20, 0x00, 0x00, - 0xe0, 0x24, 0xa1, 0x00, 0x00, 0x00, - 0x84, 0x40, 0xd8, 0x20, 0x00, 0x00, - 0x44, 0x19, 0x16, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_12[72] = - { - 0x28, 0x2c, 0x08, 0x20, 0x00, 0x00, - 0x84, 0x52, 0x03, 0x40, 0x00, 0x00, - 0x60, 0x44, 0x81, 0x20, 0x00, 0x00, - 0x05, 0x49, 0x41, 0x40, 0x00, 0x00, - 0x02, 0x9a, 0x90, 0x80, 0x00, 0x00, - 0x01, 0x32, 0x0c, 0x40, 0x00, 0x00, - 0x48, 0x10, 0x49, 0x80, 0x00, 0x00, - 0x24, 0x82, 0x42, 0x20, 0x00, 0x00, - 0x94, 0x00, 0x22, 0x20, 0x00, 0x00, - 0x8a, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x11, 0x85, 0x2c, 0x80, 0x00, 0x00, - 0x52, 0x20, 0x90, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_13[78] = - { - 0x51, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x05, 0x4a, 0x40, 0x20, 0x00, 0x00, - 0x81, 0x20, 0x05, 0x60, 0x00, 0x00, - 0x94, 0x01, 0x40, 0x40, 0x00, 0x00, - 0x30, 0x84, 0x08, 0x40, 0x00, 0x00, - 0x21, 0x11, 0x18, 0x20, 0x00, 0x00, - 0x03, 0xc0, 0x34, 0x00, 0x00, 0x00, - 0xe8, 0x04, 0x00, 0xa0, 0x00, 0x00, - 0x0a, 0x11, 0x80, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x61, 0x00, 0x00, 0x00, - 0x04, 0x92, 0xa0, 0x00, 0x00, 0x00, - 0x08, 0xac, 0x06, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_14[84] = - { - 0x59, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x26, 0x55, 0xc9, 0x00, 0x00, 0x00, - 0xb1, 0x40, 0xc5, 0xa0, 0x00, 0x00, - 0x2b, 0x0a, 0xa4, 0xc0, 0x00, 0x00, - 0x14, 0xc8, 0x33, 0x60, 0x00, 0x00, - 0xc8, 0x8c, 0x2a, 0xa0, 0x00, 0x00, - 0x84, 0xb5, 0x54, 0x40, 0x00, 0x00, - 0xd1, 0x22, 0x52, 0xa0, 0x00, 0x00, - 0x46, 0xd4, 0xaa, 0x40, 0x00, 0x00, - 0x15, 0x48, 0xa5, 0xa0, 0x00, 0x00, - 0x21, 0x72, 0x8d, 0x40, 0x00, 0x00, - 0x28, 0xc9, 0x13, 0x60, 0x00, 0x00, - 0xaa, 0x24, 0x44, 0x60, 0x00, 0x00, - 0x0a, 0xe7, 0x3b, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_15[90] = - { - 0x59, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x26, 0x55, 0xc9, 0x00, 0x00, 0x00, - 0xb1, 0x40, 0xc5, 0xa0, 0x00, 0x00, - 0x2b, 0x0a, 0xa4, 0xc0, 0x00, 0x00, - 0x14, 0xc8, 0x33, 0x60, 0x00, 0x00, - 0xc8, 0x8c, 0x2a, 0xa0, 0x00, 0x00, - 0x84, 0xb5, 0x54, 0x40, 0x00, 0x00, - 0x80, 0xae, 0x00, 0xa0, 0x00, 0x00, - 0x30, 0x92, 0x0b, 0x00, 0x00, 0x00, - 0x16, 0x0c, 0x41, 0x80, 0x00, 0x00, - 0x03, 0x31, 0x05, 0x20, 0x00, 0x00, - 0x44, 0x60, 0x52, 0x40, 0x00, 0x00, - 0x08, 0x18, 0x24, 0xc0, 0x00, 0x00, - 0xd8, 0x04, 0xa2, 0x00, 0x00, 0x00, - 0xa1, 0x43, 0x90, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_16[96] = - { - 0x80, 0xae, 0x00, 0xa0, 0x00, 0x00, - 0x30, 0x92, 0x0b, 0x00, 0x00, 0x00, - 0x16, 0x0c, 0x41, 0x80, 0x00, 0x00, - 0x03, 0x31, 0x05, 0x20, 0x00, 0x00, - 0x44, 0x60, 0x52, 0x40, 0x00, 0x00, - 0x08, 0x18, 0x24, 0xc0, 0x00, 0x00, - 0xd8, 0x04, 0xa2, 0x00, 0x00, 0x00, - 0xa1, 0x43, 0x90, 0x00, 0x00, 0x00, - 0x59, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x26, 0x55, 0xc9, 0x00, 0x00, 0x00, - 0xb1, 0x40, 0xc5, 0xa0, 0x00, 0x00, - 0x2b, 0x0a, 0xa4, 0xc0, 0x00, 0x00, - 0x14, 0xc8, 0x33, 0x60, 0x00, 0x00, - 0xc8, 0x8c, 0x2a, 0xa0, 0x00, 0x00, - 0x84, 0xb5, 0x54, 0x40, 0x00, 0x00, - 0x01, 0x50, 0xfb, 0xe0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_17[102] = - { - 0x80, 0xae, 0x00, 0xa0, 0x00, 0x00, - 0x30, 0x92, 0x0b, 0x00, 0x00, 0x00, - 0x16, 0x0c, 0x41, 0x80, 0x00, 0x00, - 0x03, 0x31, 0x05, 0x20, 0x00, 0x00, - 0x44, 0x60, 0x52, 0x40, 0x00, 0x00, - 0x08, 0x18, 0x24, 0xc0, 0x00, 0x00, - 0xd8, 0x04, 0xa2, 0x00, 0x00, 0x00, - 0xa1, 0x43, 0x90, 0x00, 0x00, 0x00, - 0x59, 0x25, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x14, 0x42, 0x51, 0x20, 0x00, 0x00, - 0x21, 0x49, 0x05, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x8c, 0x20, 0x00, 0x00, - 0x94, 0x12, 0x48, 0x40, 0x00, 0x00, - 0x80, 0xac, 0x30, 0x60, 0x00, 0x00, - 0x0a, 0x91, 0x06, 0xa0, 0x00, 0x00, - 0x40, 0x1c, 0x42, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_18[108] = - { - 0x59, 0x25, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x14, 0x42, 0x51, 0x20, 0x00, 0x00, - 0x21, 0x49, 0x05, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x8c, 0x20, 0x00, 0x00, - 0x94, 0x12, 0x48, 0x40, 0x00, 0x00, - 0x80, 0xac, 0x30, 0x60, 0x00, 0x00, - 0x0a, 0x91, 0x06, 0xa0, 0x00, 0x00, - 0x40, 0x1c, 0x42, 0x40, 0x00, 0x00, - 0x80, 0xae, 0x00, 0xa0, 0x00, 0x00, - 0x30, 0x92, 0x0b, 0x00, 0x00, 0x00, - 0x16, 0x0c, 0x41, 0x80, 0x00, 0x00, - 0x03, 0x31, 0x05, 0x20, 0x00, 0x00, - 0x44, 0x60, 0x52, 0x40, 0x00, 0x00, - 0x08, 0x18, 0x24, 0xc0, 0x00, 0x00, - 0xd8, 0x04, 0xa2, 0x00, 0x00, 0x00, - 0xa1, 0x43, 0x90, 0x00, 0x00, 0x00, - 0x53, 0xc3, 0x33, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_19[114] = - { - 0x59, 0x25, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x14, 0x42, 0x51, 0x20, 0x00, 0x00, - 0x21, 0x49, 0x05, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x8c, 0x20, 0x00, 0x00, - 0x94, 0x12, 0x48, 0x40, 0x00, 0x00, - 0x80, 0xac, 0x30, 0x60, 0x00, 0x00, - 0x0a, 0x91, 0x06, 0xa0, 0x00, 0x00, - 0x40, 0x1c, 0x42, 0x40, 0x00, 0x00, - 0xd1, 0x06, 0x06, 0xa0, 0x00, 0x00, - 0x44, 0x50, 0xea, 0x00, 0x00, 0x00, - 0x10, 0x9e, 0xa0, 0x40, 0x00, 0x00, - 0xa0, 0x50, 0x13, 0x00, 0x00, 0x00, - 0x4a, 0x08, 0x21, 0x40, 0x00, 0x00, - 0x40, 0x31, 0x04, 0xc0, 0x00, 0x00, - 0x80, 0x2a, 0x02, 0x20, 0x00, 0x00, - 0x0c, 0x90, 0x44, 0x20, 0x00, 0x00, - 0x05, 0x8b, 0x40, 0x00, 0x00, 0x00, - 0x62, 0x21, 0x18, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_2[12] = - { - 0xec, 0xc7, 0x67, 0x40, 0x00, 0x00, - 0x1b, 0x39, 0xdc, 0xe0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_20[120] = - { - 0xd1, 0x06, 0x06, 0xa0, 0x00, 0x00, - 0x44, 0x50, 0xea, 0x00, 0x00, 0x00, - 0x10, 0x9e, 0xa0, 0x40, 0x00, 0x00, - 0xa0, 0x50, 0x13, 0x00, 0x00, 0x00, - 0x4a, 0x08, 0x21, 0x40, 0x00, 0x00, - 0x40, 0x31, 0x04, 0xc0, 0x00, 0x00, - 0x80, 0x2a, 0x02, 0x20, 0x00, 0x00, - 0x0c, 0x90, 0x44, 0x20, 0x00, 0x00, - 0x05, 0x8b, 0x40, 0x00, 0x00, 0x00, - 0x62, 0x21, 0x18, 0x80, 0x00, 0x00, - 0x59, 0x25, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x14, 0x42, 0x51, 0x20, 0x00, 0x00, - 0x21, 0x49, 0x05, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x8c, 0x20, 0x00, 0x00, - 0x94, 0x12, 0x48, 0x40, 0x00, 0x00, - 0x80, 0xac, 0x30, 0x60, 0x00, 0x00, - 0x0a, 0x91, 0x06, 0xa0, 0x00, 0x00, - 0x40, 0x1c, 0x42, 0x40, 0x00, 0x00, - 0xcb, 0xff, 0x6f, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_21[126] = - { - 0xd1, 0x06, 0x06, 0xa0, 0x00, 0x00, - 0x44, 0x50, 0xea, 0x00, 0x00, 0x00, - 0x10, 0x9e, 0xa0, 0x40, 0x00, 0x00, - 0xa0, 0x50, 0x13, 0x00, 0x00, 0x00, - 0x4a, 0x08, 0x21, 0x40, 0x00, 0x00, - 0x40, 0x31, 0x04, 0xc0, 0x00, 0x00, - 0x80, 0x2a, 0x02, 0x20, 0x00, 0x00, - 0x0c, 0x90, 0x44, 0x20, 0x00, 0x00, - 0x05, 0x8b, 0x40, 0x00, 0x00, 0x00, - 0x62, 0x21, 0x18, 0x80, 0x00, 0x00, - 0x51, 0x23, 0x16, 0x80, 0x00, 0x00, - 0x22, 0x11, 0xa9, 0x00, 0x00, 0x00, - 0x13, 0x40, 0xa0, 0xa0, 0x00, 0x00, - 0x25, 0x06, 0x28, 0x40, 0x00, 0x00, - 0x18, 0x19, 0x10, 0x60, 0x00, 0x00, - 0x0a, 0x24, 0x45, 0xc0, 0x00, 0x00, - 0x88, 0x8a, 0x12, 0xa0, 0x00, 0x00, - 0x06, 0x81, 0x45, 0x20, 0x00, 0x00, - 0xe0, 0x24, 0xa1, 0x00, 0x00, 0x00, - 0x84, 0x40, 0xd8, 0x20, 0x00, 0x00, - 0x44, 0x19, 0x16, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_22[132] = - { - 0x51, 0x23, 0x16, 0x80, 0x00, 0x00, - 0x22, 0x11, 0xa9, 0x00, 0x00, 0x00, - 0x13, 0x40, 0xa0, 0xa0, 0x00, 0x00, - 0x25, 0x06, 0x28, 0x40, 0x00, 0x00, - 0x18, 0x19, 0x10, 0x60, 0x00, 0x00, - 0x0a, 0x24, 0x45, 0xc0, 0x00, 0x00, - 0x88, 0x8a, 0x12, 0xa0, 0x00, 0x00, - 0x06, 0x81, 0x45, 0x20, 0x00, 0x00, - 0xe0, 0x24, 0xa1, 0x00, 0x00, 0x00, - 0x84, 0x40, 0xd8, 0x20, 0x00, 0x00, - 0x44, 0x19, 0x16, 0x00, 0x00, 0x00, - 0xd1, 0x06, 0x06, 0xa0, 0x00, 0x00, - 0x44, 0x50, 0xea, 0x00, 0x00, 0x00, - 0x10, 0x9e, 0xa0, 0x40, 0x00, 0x00, - 0xa0, 0x50, 0x13, 0x00, 0x00, 0x00, - 0x4a, 0x08, 0x21, 0x40, 0x00, 0x00, - 0x40, 0x31, 0x04, 0xc0, 0x00, 0x00, - 0x80, 0x2a, 0x02, 0x20, 0x00, 0x00, - 0x0c, 0x90, 0x44, 0x20, 0x00, 0x00, - 0x05, 0x8b, 0x40, 0x00, 0x00, 0x00, - 0x62, 0x21, 0x18, 0x80, 0x00, 0x00, - 0xf5, 0x2d, 0x52, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_23[138] = - { - 0x51, 0x23, 0x16, 0x80, 0x00, 0x00, - 0x22, 0x11, 0xa9, 0x00, 0x00, 0x00, - 0x13, 0x40, 0xa0, 0xa0, 0x00, 0x00, - 0x25, 0x06, 0x28, 0x40, 0x00, 0x00, - 0x18, 0x19, 0x10, 0x60, 0x00, 0x00, - 0x0a, 0x24, 0x45, 0xc0, 0x00, 0x00, - 0x88, 0x8a, 0x12, 0xa0, 0x00, 0x00, - 0x06, 0x81, 0x45, 0x20, 0x00, 0x00, - 0xe0, 0x24, 0xa1, 0x00, 0x00, 0x00, - 0x84, 0x40, 0xd8, 0x20, 0x00, 0x00, - 0x44, 0x19, 0x16, 0x00, 0x00, 0x00, - 0x28, 0x2c, 0x08, 0x20, 0x00, 0x00, - 0x84, 0x52, 0x03, 0x40, 0x00, 0x00, - 0x60, 0x44, 0x81, 0x20, 0x00, 0x00, - 0x05, 0x49, 0x41, 0x40, 0x00, 0x00, - 0x02, 0x9a, 0x90, 0x80, 0x00, 0x00, - 0x01, 0x32, 0x0c, 0x40, 0x00, 0x00, - 0x48, 0x10, 0x49, 0x80, 0x00, 0x00, - 0x24, 0x82, 0x42, 0x20, 0x00, 0x00, - 0x94, 0x00, 0x22, 0x20, 0x00, 0x00, - 0x8a, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x11, 0x85, 0x2c, 0x80, 0x00, 0x00, - 0x52, 0x20, 0x90, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_24[144] = - { - 0x28, 0x2c, 0x08, 0x20, 0x00, 0x00, - 0x84, 0x52, 0x03, 0x40, 0x00, 0x00, - 0x60, 0x44, 0x81, 0x20, 0x00, 0x00, - 0x05, 0x49, 0x41, 0x40, 0x00, 0x00, - 0x02, 0x9a, 0x90, 0x80, 0x00, 0x00, - 0x01, 0x32, 0x0c, 0x40, 0x00, 0x00, - 0x48, 0x10, 0x49, 0x80, 0x00, 0x00, - 0x24, 0x82, 0x42, 0x20, 0x00, 0x00, - 0x94, 0x00, 0x22, 0x20, 0x00, 0x00, - 0x8a, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x11, 0x85, 0x2c, 0x80, 0x00, 0x00, - 0x52, 0x20, 0x90, 0x60, 0x00, 0x00, - 0x51, 0x23, 0x16, 0x80, 0x00, 0x00, - 0x22, 0x11, 0xa9, 0x00, 0x00, 0x00, - 0x13, 0x40, 0xa0, 0xa0, 0x00, 0x00, - 0x25, 0x06, 0x28, 0x40, 0x00, 0x00, - 0x18, 0x19, 0x10, 0x60, 0x00, 0x00, - 0x0a, 0x24, 0x45, 0xc0, 0x00, 0x00, - 0x88, 0x8a, 0x12, 0xa0, 0x00, 0x00, - 0x06, 0x81, 0x45, 0x20, 0x00, 0x00, - 0xe0, 0x24, 0xa1, 0x00, 0x00, 0x00, - 0x84, 0x40, 0xd8, 0x20, 0x00, 0x00, - 0x44, 0x19, 0x16, 0x00, 0x00, 0x00, - 0xa2, 0x85, 0xdb, 0xa0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_25[150] = - { - 0x28, 0x2c, 0x08, 0x20, 0x00, 0x00, - 0x84, 0x52, 0x03, 0x40, 0x00, 0x00, - 0x60, 0x44, 0x81, 0x20, 0x00, 0x00, - 0x05, 0x49, 0x41, 0x40, 0x00, 0x00, - 0x02, 0x9a, 0x90, 0x80, 0x00, 0x00, - 0x01, 0x32, 0x0c, 0x40, 0x00, 0x00, - 0x48, 0x10, 0x49, 0x80, 0x00, 0x00, - 0x24, 0x82, 0x42, 0x20, 0x00, 0x00, - 0x94, 0x00, 0x22, 0x20, 0x00, 0x00, - 0x8a, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x11, 0x85, 0x2c, 0x80, 0x00, 0x00, - 0x52, 0x20, 0x90, 0x60, 0x00, 0x00, - 0x51, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x05, 0x4a, 0x40, 0x20, 0x00, 0x00, - 0x81, 0x20, 0x05, 0x60, 0x00, 0x00, - 0x94, 0x01, 0x40, 0x40, 0x00, 0x00, - 0x30, 0x84, 0x08, 0x40, 0x00, 0x00, - 0x21, 0x11, 0x18, 0x20, 0x00, 0x00, - 0x03, 0xc0, 0x34, 0x00, 0x00, 0x00, - 0xe8, 0x04, 0x00, 0xa0, 0x00, 0x00, - 0x0a, 0x11, 0x80, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x61, 0x00, 0x00, 0x00, - 0x04, 0x92, 0xa0, 0x00, 0x00, 0x00, - 0x08, 0xac, 0x06, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_26[156] = - { - 0x51, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x05, 0x4a, 0x40, 0x20, 0x00, 0x00, - 0x81, 0x20, 0x05, 0x60, 0x00, 0x00, - 0x94, 0x01, 0x40, 0x40, 0x00, 0x00, - 0x30, 0x84, 0x08, 0x40, 0x00, 0x00, - 0x21, 0x11, 0x18, 0x20, 0x00, 0x00, - 0x03, 0xc0, 0x34, 0x00, 0x00, 0x00, - 0xe8, 0x04, 0x00, 0xa0, 0x00, 0x00, - 0x0a, 0x11, 0x80, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x61, 0x00, 0x00, 0x00, - 0x04, 0x92, 0xa0, 0x00, 0x00, 0x00, - 0x08, 0xac, 0x06, 0x00, 0x00, 0x00, - 0x28, 0x2c, 0x08, 0x20, 0x00, 0x00, - 0x84, 0x52, 0x03, 0x40, 0x00, 0x00, - 0x60, 0x44, 0x81, 0x20, 0x00, 0x00, - 0x05, 0x49, 0x41, 0x40, 0x00, 0x00, - 0x02, 0x9a, 0x90, 0x80, 0x00, 0x00, - 0x01, 0x32, 0x0c, 0x40, 0x00, 0x00, - 0x48, 0x10, 0x49, 0x80, 0x00, 0x00, - 0x24, 0x82, 0x42, 0x20, 0x00, 0x00, - 0x94, 0x00, 0x22, 0x20, 0x00, 0x00, - 0x8a, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x11, 0x85, 0x2c, 0x80, 0x00, 0x00, - 0x52, 0x20, 0x90, 0x60, 0x00, 0x00, - 0xcd, 0x41, 0xa2, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_27[162] = - { - 0x51, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x05, 0x4a, 0x40, 0x20, 0x00, 0x00, - 0x81, 0x20, 0x05, 0x60, 0x00, 0x00, - 0x94, 0x01, 0x40, 0x40, 0x00, 0x00, - 0x30, 0x84, 0x08, 0x40, 0x00, 0x00, - 0x21, 0x11, 0x18, 0x20, 0x00, 0x00, - 0x03, 0xc0, 0x34, 0x00, 0x00, 0x00, - 0xe8, 0x04, 0x00, 0xa0, 0x00, 0x00, - 0x0a, 0x11, 0x80, 0x80, 0x00, 0x00, - 0x80, 0x1c, 0x61, 0x00, 0x00, 0x00, - 0x04, 0x92, 0xa0, 0x00, 0x00, 0x00, - 0x08, 0xac, 0x06, 0x00, 0x00, 0x00, - 0x51, 0x22, 0x02, 0xa0, 0x00, 0x00, - 0x66, 0x40, 0xaa, 0x00, 0x00, 0x00, - 0x05, 0x4e, 0x00, 0x20, 0x00, 0x00, - 0x81, 0x21, 0x40, 0x80, 0x00, 0x00, - 0x94, 0x00, 0x28, 0x60, 0x00, 0x00, - 0x30, 0x83, 0x24, 0x00, 0x00, 0x00, - 0x21, 0x14, 0x0c, 0x00, 0x00, 0x00, - 0x03, 0xc0, 0x84, 0xc0, 0x00, 0x00, - 0xe8, 0x04, 0x21, 0x00, 0x00, 0x00, - 0x0a, 0x10, 0x91, 0x80, 0x00, 0x00, - 0x80, 0x1b, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x91, 0x43, 0x00, 0x00, 0x00, - 0x08, 0xa8, 0x70, 0x40, 0x00, 0x00, - 0x9c, 0xc0, 0x84, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_3[18] = - { - 0x99, 0xb5, 0x66, 0xc0, 0x00, 0x00, - 0x46, 0xda, 0xab, 0x60, 0x00, 0x00, - 0x37, 0x29, 0x3d, 0xa0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_4[24] = - { - 0x49, 0xb1, 0x66, 0xc0, 0x00, 0x00, - 0x26, 0xd4, 0x9b, 0x40, 0x00, 0x00, - 0x85, 0x68, 0xd5, 0xa0, 0x00, 0x00, - 0x52, 0x5a, 0x39, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_5[30] = - { - 0x51, 0x33, 0x26, 0xc0, 0x00, 0x00, - 0x66, 0x45, 0x2b, 0x40, 0x00, 0x00, - 0x0c, 0x6a, 0x95, 0xa0, 0x00, 0x00, - 0xa1, 0xc0, 0xed, 0x40, 0x00, 0x00, - 0x22, 0x9c, 0xe2, 0xa0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_6[36] = - { - 0xd1, 0x22, 0x52, 0xa0, 0x00, 0x00, - 0x46, 0xd4, 0xaa, 0x40, 0x00, 0x00, - 0x15, 0x48, 0xa5, 0xa0, 0x00, 0x00, - 0x21, 0x72, 0x8d, 0x40, 0x00, 0x00, - 0x28, 0xc9, 0x13, 0x60, 0x00, 0x00, - 0xaa, 0x24, 0x44, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_7[42] = - { - 0x59, 0x23, 0x12, 0xa0, 0x00, 0x00, - 0x26, 0x55, 0xc9, 0x00, 0x00, 0x00, - 0xb1, 0x40, 0xc5, 0xa0, 0x00, 0x00, - 0x2b, 0x0a, 0xa4, 0xc0, 0x00, 0x00, - 0x14, 0xc8, 0x33, 0x60, 0x00, 0x00, - 0xc8, 0x8c, 0x2a, 0xa0, 0x00, 0x00, - 0x84, 0xb5, 0x54, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_8[48] = - { - 0x80, 0xae, 0x00, 0xa0, 0x00, 0x00, - 0x30, 0x92, 0x0b, 0x00, 0x00, 0x00, - 0x16, 0x0c, 0x41, 0x80, 0x00, 0x00, - 0x03, 0x31, 0x05, 0x20, 0x00, 0x00, - 0x44, 0x60, 0x52, 0x40, 0x00, 0x00, - 0x08, 0x18, 0x24, 0xc0, 0x00, 0x00, - 0xd8, 0x04, 0xa2, 0x00, 0x00, 0x00, - 0xa1, 0x43, 0x90, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask27_9[54] = - { - 0x59, 0x25, 0x12, 0xa0, 0x00, 0x00, - 0x66, 0x41, 0xa3, 0x00, 0x00, 0x00, - 0x14, 0x42, 0x51, 0x20, 0x00, 0x00, - 0x21, 0x49, 0x05, 0x40, 0x00, 0x00, - 0x02, 0xc8, 0x8c, 0x20, 0x00, 0x00, - 0x94, 0x12, 0x48, 0x40, 0x00, 0x00, - 0x80, 0xac, 0x30, 0x60, 0x00, 0x00, - 0x0a, 0x91, 0x06, 0xa0, 0x00, 0x00, - 0x40, 0x1c, 0x42, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_1[6] = - { - 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_10[60] = - { - 0xc0, 0xd7, 0x03, 0x50, 0x00, 0x00, - 0x1d, 0x40, 0x75, 0x00, 0x00, 0x00, - 0xd4, 0x0b, 0x50, 0x20, 0x00, 0x00, - 0x02, 0x60, 0x09, 0x80, 0x00, 0x00, - 0x04, 0x28, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0x98, 0x82, 0x60, 0x00, 0x00, - 0x40, 0x45, 0x01, 0x10, 0x00, 0x00, - 0x08, 0x84, 0x22, 0x10, 0x00, 0x00, - 0x68, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x10, 0x8c, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_11[66] = - { - 0x62, 0xd1, 0x8b, 0x40, 0x00, 0x00, - 0x35, 0x20, 0xd4, 0x80, 0x00, 0x00, - 0x14, 0x14, 0x50, 0x50, 0x00, 0x00, - 0xc5, 0x0b, 0x14, 0x20, 0x00, 0x00, - 0x22, 0x0c, 0x88, 0x30, 0x00, 0x00, - 0x88, 0xba, 0x22, 0xe0, 0x00, 0x00, - 0x42, 0x55, 0x09, 0x50, 0x00, 0x00, - 0x28, 0xa4, 0xa2, 0x90, 0x00, 0x00, - 0x94, 0x22, 0x50, 0x80, 0x00, 0x00, - 0x1b, 0x04, 0x6c, 0x10, 0x00, 0x00, - 0x22, 0xc0, 0x8b, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_12[72] = - { - 0x81, 0x06, 0x04, 0x10, 0x00, 0x00, - 0x40, 0x69, 0x01, 0xa0, 0x00, 0x00, - 0x90, 0x26, 0x40, 0x90, 0x00, 0x00, - 0x28, 0x28, 0xa0, 0xa0, 0x00, 0x00, - 0x52, 0x11, 0x48, 0x40, 0x00, 0x00, - 0x41, 0x89, 0x06, 0x20, 0x00, 0x00, - 0x09, 0x30, 0x24, 0xc0, 0x00, 0x00, - 0x48, 0x45, 0x21, 0x10, 0x00, 0x00, - 0x04, 0x44, 0x11, 0x10, 0x00, 0x00, - 0x0e, 0x80, 0x3a, 0x00, 0x00, 0x00, - 0xa5, 0x92, 0x96, 0x40, 0x00, 0x00, - 0x12, 0x0c, 0x48, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_13[78] = - { - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x48, 0x05, 0x20, 0x10, 0x00, 0x00, - 0x00, 0xac, 0x02, 0xb0, 0x00, 0x00, - 0x28, 0x08, 0xa0, 0x20, 0x00, 0x00, - 0x81, 0x0a, 0x04, 0x20, 0x00, 0x00, - 0x23, 0x04, 0x8c, 0x10, 0x00, 0x00, - 0x06, 0x80, 0x1a, 0x00, 0x00, 0x00, - 0x80, 0x16, 0x00, 0x50, 0x00, 0x00, - 0x30, 0x10, 0xc0, 0x40, 0x00, 0x00, - 0x8c, 0x22, 0x30, 0x80, 0x00, 0x00, - 0x54, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x80, 0xc2, 0x03, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_14[84] = - { - 0x40, 0x55, 0x01, 0x50, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x00, 0x00, 0x00, - 0xc0, 0x07, 0x00, 0x10, 0x00, 0x00, - 0x28, 0x10, 0xa0, 0x40, 0x00, 0x00, - 0x05, 0x0c, 0x14, 0x30, 0x00, 0x00, - 0x64, 0x81, 0x92, 0x00, 0x00, 0x00, - 0x81, 0x82, 0x06, 0x00, 0x00, 0x00, - 0x10, 0x98, 0x42, 0x60, 0x00, 0x00, - 0x84, 0x22, 0x10, 0x80, 0x00, 0x00, - 0x12, 0x30, 0x48, 0xc0, 0x00, 0x00, - 0x62, 0x01, 0x88, 0x00, 0x00, 0x00, - 0x28, 0x60, 0xa1, 0x80, 0x00, 0x00, - 0x0e, 0x08, 0x38, 0x20, 0x00, 0x00, - 0x10, 0x84, 0x42, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_15[90] = - { - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0xb9, 0x22, 0xe4, 0x80, 0x00, 0x00, - 0x18, 0xb4, 0x62, 0xd0, 0x00, 0x00, - 0x54, 0x99, 0x52, 0x60, 0x00, 0x00, - 0x06, 0x6c, 0x19, 0xb0, 0x00, 0x00, - 0x85, 0x56, 0x15, 0x50, 0x00, 0x00, - 0xaa, 0x8a, 0xaa, 0x20, 0x00, 0x00, - 0xc0, 0x17, 0x00, 0x50, 0x00, 0x00, - 0x41, 0x61, 0x05, 0x80, 0x00, 0x00, - 0x88, 0x32, 0x20, 0xc0, 0x00, 0x00, - 0x20, 0xa4, 0x82, 0x90, 0x00, 0x00, - 0x0a, 0x48, 0x29, 0x20, 0x00, 0x00, - 0x04, 0x98, 0x12, 0x60, 0x00, 0x00, - 0x94, 0x42, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x01, 0xc8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_16[96] = - { - 0xc0, 0x17, 0x00, 0x50, 0x00, 0x00, - 0x41, 0x61, 0x05, 0x80, 0x00, 0x00, - 0x88, 0x32, 0x20, 0xc0, 0x00, 0x00, - 0x20, 0xa4, 0x82, 0x90, 0x00, 0x00, - 0x0a, 0x48, 0x29, 0x20, 0x00, 0x00, - 0x04, 0x98, 0x12, 0x60, 0x00, 0x00, - 0x94, 0x42, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x01, 0xc8, 0x00, 0x00, 0x00, - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0xb9, 0x22, 0xe4, 0x80, 0x00, 0x00, - 0x18, 0xb4, 0x62, 0xd0, 0x00, 0x00, - 0x54, 0x99, 0x52, 0x60, 0x00, 0x00, - 0x06, 0x6c, 0x19, 0xb0, 0x00, 0x00, - 0x85, 0x56, 0x15, 0x50, 0x00, 0x00, - 0xaa, 0x8a, 0xaa, 0x20, 0x00, 0x00, - 0xed, 0x76, 0x36, 0x50, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_17[102] = - { - 0xc0, 0x17, 0x00, 0x50, 0x00, 0x00, - 0x41, 0x61, 0x05, 0x80, 0x00, 0x00, - 0x88, 0x32, 0x20, 0xc0, 0x00, 0x00, - 0x20, 0xa4, 0x82, 0x90, 0x00, 0x00, - 0x0a, 0x48, 0x29, 0x20, 0x00, 0x00, - 0x04, 0x98, 0x12, 0x60, 0x00, 0x00, - 0x94, 0x42, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x01, 0xc8, 0x00, 0x00, 0x00, - 0xa2, 0x56, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x4a, 0x25, 0x28, 0x90, 0x00, 0x00, - 0x20, 0xa8, 0x82, 0xa0, 0x00, 0x00, - 0x11, 0x84, 0x46, 0x10, 0x00, 0x00, - 0x49, 0x09, 0x24, 0x20, 0x00, 0x00, - 0x86, 0x0e, 0x18, 0x30, 0x00, 0x00, - 0x20, 0xd4, 0x83, 0x50, 0x00, 0x00, - 0x88, 0x4a, 0x21, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_18[108] = - { - 0xa2, 0x56, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x4a, 0x25, 0x28, 0x90, 0x00, 0x00, - 0x20, 0xa8, 0x82, 0xa0, 0x00, 0x00, - 0x11, 0x84, 0x46, 0x10, 0x00, 0x00, - 0x49, 0x09, 0x24, 0x20, 0x00, 0x00, - 0x86, 0x0e, 0x18, 0x30, 0x00, 0x00, - 0x20, 0xd4, 0x83, 0x50, 0x00, 0x00, - 0x88, 0x4a, 0x21, 0x20, 0x00, 0x00, - 0xc0, 0x17, 0x00, 0x50, 0x00, 0x00, - 0x41, 0x61, 0x05, 0x80, 0x00, 0x00, - 0x88, 0x32, 0x20, 0xc0, 0x00, 0x00, - 0x20, 0xa4, 0x82, 0x90, 0x00, 0x00, - 0x0a, 0x48, 0x29, 0x20, 0x00, 0x00, - 0x04, 0x98, 0x12, 0x60, 0x00, 0x00, - 0x94, 0x42, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x01, 0xc8, 0x00, 0x00, 0x00, - 0x6e, 0x9f, 0x98, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_19[114] = - { - 0xa2, 0x56, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x4a, 0x25, 0x28, 0x90, 0x00, 0x00, - 0x20, 0xa8, 0x82, 0xa0, 0x00, 0x00, - 0x11, 0x84, 0x46, 0x10, 0x00, 0x00, - 0x49, 0x09, 0x24, 0x20, 0x00, 0x00, - 0x86, 0x0e, 0x18, 0x30, 0x00, 0x00, - 0x20, 0xd4, 0x83, 0x50, 0x00, 0x00, - 0x88, 0x4a, 0x21, 0x20, 0x00, 0x00, - 0xc0, 0xd7, 0x03, 0x50, 0x00, 0x00, - 0x1d, 0x40, 0x75, 0x00, 0x00, 0x00, - 0xd4, 0x0b, 0x50, 0x20, 0x00, 0x00, - 0x02, 0x60, 0x09, 0x80, 0x00, 0x00, - 0x04, 0x28, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0x98, 0x82, 0x60, 0x00, 0x00, - 0x40, 0x45, 0x01, 0x10, 0x00, 0x00, - 0x08, 0x84, 0x22, 0x10, 0x00, 0x00, - 0x68, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x10, 0x8c, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_2[12] = - { - 0xec, 0xeb, 0xb3, 0xa0, 0x00, 0x00, - 0x3b, 0x9c, 0xee, 0x70, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_20[120] = - { - 0xc0, 0xd7, 0x03, 0x50, 0x00, 0x00, - 0x1d, 0x40, 0x75, 0x00, 0x00, 0x00, - 0xd4, 0x0b, 0x50, 0x20, 0x00, 0x00, - 0x02, 0x60, 0x09, 0x80, 0x00, 0x00, - 0x04, 0x28, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0x98, 0x82, 0x60, 0x00, 0x00, - 0x40, 0x45, 0x01, 0x10, 0x00, 0x00, - 0x08, 0x84, 0x22, 0x10, 0x00, 0x00, - 0x68, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x10, 0x8c, 0x40, 0x00, 0x00, - 0xa2, 0x56, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x4a, 0x25, 0x28, 0x90, 0x00, 0x00, - 0x20, 0xa8, 0x82, 0xa0, 0x00, 0x00, - 0x11, 0x84, 0x46, 0x10, 0x00, 0x00, - 0x49, 0x09, 0x24, 0x20, 0x00, 0x00, - 0x86, 0x0e, 0x18, 0x30, 0x00, 0x00, - 0x20, 0xd4, 0x83, 0x50, 0x00, 0x00, - 0x88, 0x4a, 0x21, 0x20, 0x00, 0x00, - 0xea, 0x1b, 0x3a, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_21[126] = - { - 0xc0, 0xd7, 0x03, 0x50, 0x00, 0x00, - 0x1d, 0x40, 0x75, 0x00, 0x00, 0x00, - 0xd4, 0x0b, 0x50, 0x20, 0x00, 0x00, - 0x02, 0x60, 0x09, 0x80, 0x00, 0x00, - 0x04, 0x28, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0x98, 0x82, 0x60, 0x00, 0x00, - 0x40, 0x45, 0x01, 0x10, 0x00, 0x00, - 0x08, 0x84, 0x22, 0x10, 0x00, 0x00, - 0x68, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x10, 0x8c, 0x40, 0x00, 0x00, - 0x62, 0xd1, 0x8b, 0x40, 0x00, 0x00, - 0x35, 0x20, 0xd4, 0x80, 0x00, 0x00, - 0x14, 0x14, 0x50, 0x50, 0x00, 0x00, - 0xc5, 0x0b, 0x14, 0x20, 0x00, 0x00, - 0x22, 0x0c, 0x88, 0x30, 0x00, 0x00, - 0x88, 0xba, 0x22, 0xe0, 0x00, 0x00, - 0x42, 0x55, 0x09, 0x50, 0x00, 0x00, - 0x28, 0xa4, 0xa2, 0x90, 0x00, 0x00, - 0x94, 0x22, 0x50, 0x80, 0x00, 0x00, - 0x1b, 0x04, 0x6c, 0x10, 0x00, 0x00, - 0x22, 0xc0, 0x8b, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_22[132] = - { - 0x62, 0xd1, 0x8b, 0x40, 0x00, 0x00, - 0x35, 0x20, 0xd4, 0x80, 0x00, 0x00, - 0x14, 0x14, 0x50, 0x50, 0x00, 0x00, - 0xc5, 0x0b, 0x14, 0x20, 0x00, 0x00, - 0x22, 0x0c, 0x88, 0x30, 0x00, 0x00, - 0x88, 0xba, 0x22, 0xe0, 0x00, 0x00, - 0x42, 0x55, 0x09, 0x50, 0x00, 0x00, - 0x28, 0xa4, 0xa2, 0x90, 0x00, 0x00, - 0x94, 0x22, 0x50, 0x80, 0x00, 0x00, - 0x1b, 0x04, 0x6c, 0x10, 0x00, 0x00, - 0x22, 0xc0, 0x8b, 0x00, 0x00, 0x00, - 0xc0, 0xd7, 0x03, 0x50, 0x00, 0x00, - 0x1d, 0x40, 0x75, 0x00, 0x00, 0x00, - 0xd4, 0x0b, 0x50, 0x20, 0x00, 0x00, - 0x02, 0x60, 0x09, 0x80, 0x00, 0x00, - 0x04, 0x28, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0x98, 0x82, 0x60, 0x00, 0x00, - 0x40, 0x45, 0x01, 0x10, 0x00, 0x00, - 0x08, 0x84, 0x22, 0x10, 0x00, 0x00, - 0x68, 0x01, 0xa0, 0x00, 0x00, 0x00, - 0x23, 0x10, 0x8c, 0x40, 0x00, 0x00, - 0x45, 0x05, 0x10, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_23[138] = - { - 0x62, 0xd1, 0x8b, 0x40, 0x00, 0x00, - 0x35, 0x20, 0xd4, 0x80, 0x00, 0x00, - 0x14, 0x14, 0x50, 0x50, 0x00, 0x00, - 0xc5, 0x0b, 0x14, 0x20, 0x00, 0x00, - 0x22, 0x0c, 0x88, 0x30, 0x00, 0x00, - 0x88, 0xba, 0x22, 0xe0, 0x00, 0x00, - 0x42, 0x55, 0x09, 0x50, 0x00, 0x00, - 0x28, 0xa4, 0xa2, 0x90, 0x00, 0x00, - 0x94, 0x22, 0x50, 0x80, 0x00, 0x00, - 0x1b, 0x04, 0x6c, 0x10, 0x00, 0x00, - 0x22, 0xc0, 0x8b, 0x00, 0x00, 0x00, - 0x81, 0x06, 0x04, 0x10, 0x00, 0x00, - 0x40, 0x69, 0x01, 0xa0, 0x00, 0x00, - 0x90, 0x26, 0x40, 0x90, 0x00, 0x00, - 0x28, 0x28, 0xa0, 0xa0, 0x00, 0x00, - 0x52, 0x11, 0x48, 0x40, 0x00, 0x00, - 0x41, 0x89, 0x06, 0x20, 0x00, 0x00, - 0x09, 0x30, 0x24, 0xc0, 0x00, 0x00, - 0x48, 0x45, 0x21, 0x10, 0x00, 0x00, - 0x04, 0x44, 0x11, 0x10, 0x00, 0x00, - 0x0e, 0x80, 0x3a, 0x00, 0x00, 0x00, - 0xa5, 0x92, 0x96, 0x40, 0x00, 0x00, - 0x12, 0x0c, 0x48, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_24[144] = - { - 0x81, 0x06, 0x04, 0x10, 0x00, 0x00, - 0x40, 0x69, 0x01, 0xa0, 0x00, 0x00, - 0x90, 0x26, 0x40, 0x90, 0x00, 0x00, - 0x28, 0x28, 0xa0, 0xa0, 0x00, 0x00, - 0x52, 0x11, 0x48, 0x40, 0x00, 0x00, - 0x41, 0x89, 0x06, 0x20, 0x00, 0x00, - 0x09, 0x30, 0x24, 0xc0, 0x00, 0x00, - 0x48, 0x45, 0x21, 0x10, 0x00, 0x00, - 0x04, 0x44, 0x11, 0x10, 0x00, 0x00, - 0x0e, 0x80, 0x3a, 0x00, 0x00, 0x00, - 0xa5, 0x92, 0x96, 0x40, 0x00, 0x00, - 0x12, 0x0c, 0x48, 0x30, 0x00, 0x00, - 0x62, 0xd1, 0x8b, 0x40, 0x00, 0x00, - 0x35, 0x20, 0xd4, 0x80, 0x00, 0x00, - 0x14, 0x14, 0x50, 0x50, 0x00, 0x00, - 0xc5, 0x0b, 0x14, 0x20, 0x00, 0x00, - 0x22, 0x0c, 0x88, 0x30, 0x00, 0x00, - 0x88, 0xba, 0x22, 0xe0, 0x00, 0x00, - 0x42, 0x55, 0x09, 0x50, 0x00, 0x00, - 0x28, 0xa4, 0xa2, 0x90, 0x00, 0x00, - 0x94, 0x22, 0x50, 0x80, 0x00, 0x00, - 0x1b, 0x04, 0x6c, 0x10, 0x00, 0x00, - 0x22, 0xc0, 0x8b, 0x00, 0x00, 0x00, - 0x6f, 0xd8, 0xee, 0xa0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_25[150] = - { - 0x81, 0x06, 0x04, 0x10, 0x00, 0x00, - 0x40, 0x69, 0x01, 0xa0, 0x00, 0x00, - 0x90, 0x26, 0x40, 0x90, 0x00, 0x00, - 0x28, 0x28, 0xa0, 0xa0, 0x00, 0x00, - 0x52, 0x11, 0x48, 0x40, 0x00, 0x00, - 0x41, 0x89, 0x06, 0x20, 0x00, 0x00, - 0x09, 0x30, 0x24, 0xc0, 0x00, 0x00, - 0x48, 0x45, 0x21, 0x10, 0x00, 0x00, - 0x04, 0x44, 0x11, 0x10, 0x00, 0x00, - 0x0e, 0x80, 0x3a, 0x00, 0x00, 0x00, - 0xa5, 0x92, 0x96, 0x40, 0x00, 0x00, - 0x12, 0x0c, 0x48, 0x30, 0x00, 0x00, - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x48, 0x05, 0x20, 0x10, 0x00, 0x00, - 0x00, 0xac, 0x02, 0xb0, 0x00, 0x00, - 0x28, 0x08, 0xa0, 0x20, 0x00, 0x00, - 0x81, 0x0a, 0x04, 0x20, 0x00, 0x00, - 0x23, 0x04, 0x8c, 0x10, 0x00, 0x00, - 0x06, 0x80, 0x1a, 0x00, 0x00, 0x00, - 0x80, 0x16, 0x00, 0x50, 0x00, 0x00, - 0x30, 0x10, 0xc0, 0x40, 0x00, 0x00, - 0x8c, 0x22, 0x30, 0x80, 0x00, 0x00, - 0x54, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x80, 0xc2, 0x03, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_26[156] = - { - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x48, 0x05, 0x20, 0x10, 0x00, 0x00, - 0x00, 0xac, 0x02, 0xb0, 0x00, 0x00, - 0x28, 0x08, 0xa0, 0x20, 0x00, 0x00, - 0x81, 0x0a, 0x04, 0x20, 0x00, 0x00, - 0x23, 0x04, 0x8c, 0x10, 0x00, 0x00, - 0x06, 0x80, 0x1a, 0x00, 0x00, 0x00, - 0x80, 0x16, 0x00, 0x50, 0x00, 0x00, - 0x30, 0x10, 0xc0, 0x40, 0x00, 0x00, - 0x8c, 0x22, 0x30, 0x80, 0x00, 0x00, - 0x54, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x80, 0xc2, 0x03, 0x00, 0x00, 0x00, - 0x81, 0x06, 0x04, 0x10, 0x00, 0x00, - 0x40, 0x69, 0x01, 0xa0, 0x00, 0x00, - 0x90, 0x26, 0x40, 0x90, 0x00, 0x00, - 0x28, 0x28, 0xa0, 0xa0, 0x00, 0x00, - 0x52, 0x11, 0x48, 0x40, 0x00, 0x00, - 0x41, 0x89, 0x06, 0x20, 0x00, 0x00, - 0x09, 0x30, 0x24, 0xc0, 0x00, 0x00, - 0x48, 0x45, 0x21, 0x10, 0x00, 0x00, - 0x04, 0x44, 0x11, 0x10, 0x00, 0x00, - 0x0e, 0x80, 0x3a, 0x00, 0x00, 0x00, - 0xa5, 0x92, 0x96, 0x40, 0x00, 0x00, - 0x12, 0x0c, 0x48, 0x30, 0x00, 0x00, - 0xf1, 0x64, 0xbe, 0x40, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_27[162] = - { - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x48, 0x05, 0x20, 0x10, 0x00, 0x00, - 0x00, 0xac, 0x02, 0xb0, 0x00, 0x00, - 0x28, 0x08, 0xa0, 0x20, 0x00, 0x00, - 0x81, 0x0a, 0x04, 0x20, 0x00, 0x00, - 0x23, 0x04, 0x8c, 0x10, 0x00, 0x00, - 0x06, 0x80, 0x1a, 0x00, 0x00, 0x00, - 0x80, 0x16, 0x00, 0x50, 0x00, 0x00, - 0x30, 0x10, 0xc0, 0x40, 0x00, 0x00, - 0x8c, 0x22, 0x30, 0x80, 0x00, 0x00, - 0x54, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x80, 0xc2, 0x03, 0x00, 0x00, 0x00, - 0x40, 0x55, 0x01, 0x50, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x00, 0x00, 0x00, - 0xc0, 0x07, 0x00, 0x10, 0x00, 0x00, - 0x28, 0x10, 0xa0, 0x40, 0x00, 0x00, - 0x05, 0x0c, 0x14, 0x30, 0x00, 0x00, - 0x64, 0x81, 0x92, 0x00, 0x00, 0x00, - 0x81, 0x82, 0x06, 0x00, 0x00, 0x00, - 0x10, 0x98, 0x42, 0x60, 0x00, 0x00, - 0x84, 0x22, 0x10, 0x80, 0x00, 0x00, - 0x12, 0x30, 0x48, 0xc0, 0x00, 0x00, - 0x62, 0x01, 0x88, 0x00, 0x00, 0x00, - 0x28, 0x60, 0xa1, 0x80, 0x00, 0x00, - 0x0e, 0x08, 0x38, 0x20, 0x00, 0x00, - 0x10, 0x84, 0x42, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_28[168] = - { - 0x40, 0x55, 0x01, 0x50, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x00, 0x00, 0x00, - 0xc0, 0x07, 0x00, 0x10, 0x00, 0x00, - 0x28, 0x10, 0xa0, 0x40, 0x00, 0x00, - 0x05, 0x0c, 0x14, 0x30, 0x00, 0x00, - 0x64, 0x81, 0x92, 0x00, 0x00, 0x00, - 0x81, 0x82, 0x06, 0x00, 0x00, 0x00, - 0x10, 0x98, 0x42, 0x60, 0x00, 0x00, - 0x84, 0x22, 0x10, 0x80, 0x00, 0x00, - 0x12, 0x30, 0x48, 0xc0, 0x00, 0x00, - 0x62, 0x01, 0x88, 0x00, 0x00, 0x00, - 0x28, 0x60, 0xa1, 0x80, 0x00, 0x00, - 0x0e, 0x08, 0x38, 0x20, 0x00, 0x00, - 0x10, 0x84, 0x42, 0x10, 0x00, 0x00, - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x48, 0x05, 0x20, 0x10, 0x00, 0x00, - 0x00, 0xac, 0x02, 0xb0, 0x00, 0x00, - 0x28, 0x08, 0xa0, 0x20, 0x00, 0x00, - 0x81, 0x0a, 0x04, 0x20, 0x00, 0x00, - 0x23, 0x04, 0x8c, 0x10, 0x00, 0x00, - 0x06, 0x80, 0x1a, 0x00, 0x00, 0x00, - 0x80, 0x16, 0x00, 0x50, 0x00, 0x00, - 0x30, 0x10, 0xc0, 0x40, 0x00, 0x00, - 0x8c, 0x22, 0x30, 0x80, 0x00, 0x00, - 0x54, 0x01, 0x50, 0x00, 0x00, 0x00, - 0x80, 0xc2, 0x03, 0x00, 0x00, 0x00, - 0x36, 0x4f, 0x1f, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_3[18] = - { - 0xac, 0xda, 0xb3, 0x60, 0x00, 0x00, - 0x55, 0x6d, 0x55, 0xb0, 0x00, 0x00, - 0x27, 0xb4, 0x9e, 0xd0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_4[24] = - { - 0x2c, 0xd8, 0xb3, 0x60, 0x00, 0x00, - 0x93, 0x6a, 0x4d, 0xa0, 0x00, 0x00, - 0x1a, 0xb4, 0x6a, 0xd0, 0x00, 0x00, - 0x47, 0x2d, 0x1c, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_5[30] = - { - 0x64, 0xd9, 0x93, 0x60, 0x00, 0x00, - 0xa5, 0x6a, 0x95, 0xa0, 0x00, 0x00, - 0x52, 0xb5, 0x4a, 0xd0, 0x00, 0x00, - 0x1d, 0xa8, 0x76, 0xa0, 0x00, 0x00, - 0x9c, 0x56, 0x71, 0x50, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_6[36] = - { - 0x4a, 0x55, 0x29, 0x50, 0x00, 0x00, - 0x95, 0x4a, 0x55, 0x20, 0x00, 0x00, - 0x14, 0xb4, 0x52, 0xd0, 0x00, 0x00, - 0x51, 0xa9, 0x46, 0xa0, 0x00, 0x00, - 0x22, 0x6c, 0x89, 0xb0, 0x00, 0x00, - 0x88, 0x8e, 0x22, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_7[42] = - { - 0x62, 0x55, 0x89, 0x50, 0x00, 0x00, - 0xb9, 0x22, 0xe4, 0x80, 0x00, 0x00, - 0x18, 0xb4, 0x62, 0xd0, 0x00, 0x00, - 0x54, 0x99, 0x52, 0x60, 0x00, 0x00, - 0x06, 0x6c, 0x19, 0xb0, 0x00, 0x00, - 0x85, 0x56, 0x15, 0x50, 0x00, 0x00, - 0xaa, 0x8a, 0xaa, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_8[48] = - { - 0xc0, 0x17, 0x00, 0x50, 0x00, 0x00, - 0x41, 0x61, 0x05, 0x80, 0x00, 0x00, - 0x88, 0x32, 0x20, 0xc0, 0x00, 0x00, - 0x20, 0xa4, 0x82, 0x90, 0x00, 0x00, - 0x0a, 0x48, 0x29, 0x20, 0x00, 0x00, - 0x04, 0x98, 0x12, 0x60, 0x00, 0x00, - 0x94, 0x42, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x01, 0xc8, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask28_9[54] = - { - 0xa2, 0x56, 0x89, 0x50, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x80, 0x00, 0x00, - 0x4a, 0x25, 0x28, 0x90, 0x00, 0x00, - 0x20, 0xa8, 0x82, 0xa0, 0x00, 0x00, - 0x11, 0x84, 0x46, 0x10, 0x00, 0x00, - 0x49, 0x09, 0x24, 0x20, 0x00, 0x00, - 0x86, 0x0e, 0x18, 0x30, 0x00, 0x00, - 0x20, 0xd4, 0x83, 0x50, 0x00, 0x00, - 0x88, 0x4a, 0x21, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_1[6] = - { - 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_10[60] = - { - 0xc0, 0xd7, 0x02, 0x80, 0x00, 0x00, - 0x1d, 0x40, 0x55, 0x58, 0x00, 0x00, - 0xd4, 0x09, 0xd1, 0x00, 0x00, 0x00, - 0x02, 0x60, 0x02, 0x70, 0x00, 0x00, - 0x04, 0x28, 0x04, 0xb0, 0x00, 0x00, - 0x20, 0x99, 0x12, 0x48, 0x00, 0x00, - 0x40, 0x46, 0x21, 0x40, 0x00, 0x00, - 0x08, 0x84, 0x82, 0x90, 0x00, 0x00, - 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, - 0x23, 0x10, 0x09, 0x88, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_11[66] = - { - 0x62, 0xd1, 0x88, 0x88, 0x00, 0x00, - 0x35, 0x23, 0xc4, 0x40, 0x00, 0x00, - 0x14, 0x14, 0x40, 0x38, 0x00, 0x00, - 0xc5, 0x08, 0x42, 0xc0, 0x00, 0x00, - 0x22, 0x0c, 0x90, 0x90, 0x00, 0x00, - 0x88, 0xb8, 0x04, 0x48, 0x00, 0x00, - 0x42, 0x54, 0x03, 0x10, 0x00, 0x00, - 0x28, 0xa4, 0x12, 0x88, 0x00, 0x00, - 0x94, 0x20, 0x09, 0x60, 0x00, 0x00, - 0x1b, 0x04, 0xac, 0x00, 0x00, 0x00, - 0x22, 0xc2, 0x61, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_12[72] = - { - 0x81, 0x06, 0x22, 0x40, 0x00, 0x00, - 0x40, 0x69, 0x01, 0x50, 0x00, 0x00, - 0x90, 0x26, 0x09, 0x88, 0x00, 0x00, - 0x28, 0x28, 0x86, 0x90, 0x00, 0x00, - 0x52, 0x10, 0x41, 0x90, 0x00, 0x00, - 0x41, 0x89, 0x10, 0x28, 0x00, 0x00, - 0x09, 0x30, 0x43, 0x20, 0x00, 0x00, - 0x48, 0x45, 0x34, 0xa8, 0x00, 0x00, - 0x04, 0x44, 0xe0, 0x08, 0x00, 0x00, - 0x0e, 0x80, 0x5d, 0x20, 0x00, 0x00, - 0xa5, 0x92, 0x42, 0x10, 0x00, 0x00, - 0x12, 0x0d, 0xc8, 0x50, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_13[78] = - { - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x10, 0x00, 0x00, - 0x48, 0x05, 0x01, 0x28, 0x00, 0x00, - 0x00, 0xaf, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x08, 0x21, 0x80, 0x00, 0x00, - 0x81, 0x0a, 0x50, 0x48, 0x00, 0x00, - 0x23, 0x06, 0x23, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x84, 0xc8, 0x00, 0x00, - 0x80, 0x17, 0x05, 0x00, 0x00, 0x00, - 0x30, 0x10, 0x41, 0xa0, 0x00, 0x00, - 0x8c, 0x20, 0x1a, 0x40, 0x00, 0x00, - 0x54, 0x01, 0x64, 0x00, 0x00, 0x00, - 0x80, 0xc0, 0x28, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_14[84] = - { - 0x40, 0x55, 0x02, 0x08, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x50, 0x00, 0x00, - 0xc0, 0x06, 0x20, 0x48, 0x00, 0x00, - 0x28, 0x13, 0x00, 0x40, 0x00, 0x00, - 0x05, 0x0e, 0x02, 0x80, 0x00, 0x00, - 0x64, 0x80, 0x04, 0x88, 0x00, 0x00, - 0x81, 0x81, 0x00, 0xb0, 0x00, 0x00, - 0x10, 0x98, 0x88, 0x08, 0x00, 0x00, - 0x84, 0x22, 0x40, 0x10, 0x00, 0x00, - 0x12, 0x30, 0x49, 0x00, 0x00, 0x00, - 0x62, 0x01, 0x74, 0x00, 0x00, 0x00, - 0x28, 0x60, 0x81, 0x50, 0x00, 0x00, - 0x0e, 0x0a, 0x18, 0x20, 0x00, 0x00, - 0x10, 0x84, 0xa2, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_15[90] = - { - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0xb9, 0x22, 0xc4, 0x50, 0x00, 0x00, - 0x18, 0xb4, 0x61, 0xa8, 0x00, 0x00, - 0x54, 0x99, 0x13, 0x50, 0x00, 0x00, - 0x06, 0x6c, 0x4d, 0x90, 0x00, 0x00, - 0x85, 0x55, 0x24, 0x68, 0x00, 0x00, - 0xaa, 0x8a, 0x1a, 0x30, 0x00, 0x00, - 0xc0, 0x16, 0x40, 0x88, 0x00, 0x00, - 0x41, 0x60, 0x25, 0x40, 0x00, 0x00, - 0x88, 0x30, 0x01, 0xa8, 0x00, 0x00, - 0x20, 0xa4, 0x80, 0xd0, 0x00, 0x00, - 0x0a, 0x48, 0x51, 0x10, 0x00, 0x00, - 0x04, 0x9b, 0x08, 0x40, 0x00, 0x00, - 0x94, 0x40, 0x03, 0x18, 0x00, 0x00, - 0x72, 0x01, 0x96, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_16[96] = - { - 0xc0, 0x16, 0x40, 0x88, 0x00, 0x00, - 0x41, 0x60, 0x25, 0x40, 0x00, 0x00, - 0x88, 0x30, 0x01, 0xa8, 0x00, 0x00, - 0x20, 0xa4, 0x80, 0xd0, 0x00, 0x00, - 0x0a, 0x48, 0x51, 0x10, 0x00, 0x00, - 0x04, 0x9b, 0x08, 0x40, 0x00, 0x00, - 0x94, 0x40, 0x03, 0x18, 0x00, 0x00, - 0x72, 0x01, 0x96, 0x00, 0x00, 0x00, - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0xb9, 0x22, 0xc4, 0x50, 0x00, 0x00, - 0x18, 0xb4, 0x61, 0xa8, 0x00, 0x00, - 0x54, 0x99, 0x13, 0x50, 0x00, 0x00, - 0x06, 0x6c, 0x4d, 0x90, 0x00, 0x00, - 0x85, 0x55, 0x24, 0x68, 0x00, 0x00, - 0xaa, 0x8a, 0x1a, 0x30, 0x00, 0x00, - 0x0d, 0x2c, 0xf2, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_17[102] = - { - 0xc0, 0x16, 0x40, 0x88, 0x00, 0x00, - 0x41, 0x60, 0x25, 0x40, 0x00, 0x00, - 0x88, 0x30, 0x01, 0xa8, 0x00, 0x00, - 0x20, 0xa4, 0x80, 0xd0, 0x00, 0x00, - 0x0a, 0x48, 0x51, 0x10, 0x00, 0x00, - 0x04, 0x9b, 0x08, 0x40, 0x00, 0x00, - 0x94, 0x40, 0x03, 0x18, 0x00, 0x00, - 0x72, 0x01, 0x96, 0x00, 0x00, 0x00, - 0xa2, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x34, 0x60, 0x91, 0x10, 0x00, 0x00, - 0x4a, 0x27, 0x01, 0x40, 0x00, 0x00, - 0x20, 0xa8, 0x0c, 0x30, 0x00, 0x00, - 0x11, 0x84, 0x58, 0xa0, 0x00, 0x00, - 0x49, 0x0a, 0x24, 0x00, 0x00, 0x00, - 0x86, 0x0e, 0x0a, 0x40, 0x00, 0x00, - 0x20, 0xd4, 0x22, 0x90, 0x00, 0x00, - 0x88, 0x4a, 0x41, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_18[108] = - { - 0xa2, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x34, 0x60, 0x91, 0x10, 0x00, 0x00, - 0x4a, 0x27, 0x01, 0x40, 0x00, 0x00, - 0x20, 0xa8, 0x0c, 0x30, 0x00, 0x00, - 0x11, 0x84, 0x58, 0xa0, 0x00, 0x00, - 0x49, 0x0a, 0x24, 0x00, 0x00, 0x00, - 0x86, 0x0e, 0x0a, 0x40, 0x00, 0x00, - 0x20, 0xd4, 0x22, 0x90, 0x00, 0x00, - 0x88, 0x4a, 0x41, 0x20, 0x00, 0x00, - 0xc0, 0x16, 0x40, 0x88, 0x00, 0x00, - 0x41, 0x60, 0x25, 0x40, 0x00, 0x00, - 0x88, 0x30, 0x01, 0xa8, 0x00, 0x00, - 0x20, 0xa4, 0x80, 0xd0, 0x00, 0x00, - 0x0a, 0x48, 0x51, 0x10, 0x00, 0x00, - 0x04, 0x9b, 0x08, 0x40, 0x00, 0x00, - 0x94, 0x40, 0x03, 0x18, 0x00, 0x00, - 0x72, 0x01, 0x96, 0x00, 0x00, 0x00, - 0x71, 0x36, 0xf2, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_19[114] = - { - 0xa2, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x34, 0x60, 0x91, 0x10, 0x00, 0x00, - 0x4a, 0x27, 0x01, 0x40, 0x00, 0x00, - 0x20, 0xa8, 0x0c, 0x30, 0x00, 0x00, - 0x11, 0x84, 0x58, 0xa0, 0x00, 0x00, - 0x49, 0x0a, 0x24, 0x00, 0x00, 0x00, - 0x86, 0x0e, 0x0a, 0x40, 0x00, 0x00, - 0x20, 0xd4, 0x22, 0x90, 0x00, 0x00, - 0x88, 0x4a, 0x41, 0x20, 0x00, 0x00, - 0xc0, 0xd7, 0x02, 0x80, 0x00, 0x00, - 0x1d, 0x40, 0x55, 0x58, 0x00, 0x00, - 0xd4, 0x09, 0xd1, 0x00, 0x00, 0x00, - 0x02, 0x60, 0x02, 0x70, 0x00, 0x00, - 0x04, 0x28, 0x04, 0xb0, 0x00, 0x00, - 0x20, 0x99, 0x12, 0x48, 0x00, 0x00, - 0x40, 0x46, 0x21, 0x40, 0x00, 0x00, - 0x08, 0x84, 0x82, 0x90, 0x00, 0x00, - 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, - 0x23, 0x10, 0x09, 0x88, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_2[12] = - { - 0xec, 0xeb, 0xb3, 0xa8, 0x00, 0x00, - 0x3b, 0x9e, 0xee, 0x70, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_20[120] = - { - 0xc0, 0xd7, 0x02, 0x80, 0x00, 0x00, - 0x1d, 0x40, 0x55, 0x58, 0x00, 0x00, - 0xd4, 0x09, 0xd1, 0x00, 0x00, 0x00, - 0x02, 0x60, 0x02, 0x70, 0x00, 0x00, - 0x04, 0x28, 0x04, 0xb0, 0x00, 0x00, - 0x20, 0x99, 0x12, 0x48, 0x00, 0x00, - 0x40, 0x46, 0x21, 0x40, 0x00, 0x00, - 0x08, 0x84, 0x82, 0x90, 0x00, 0x00, - 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, - 0x23, 0x10, 0x09, 0x88, 0x00, 0x00, - 0xa2, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x34, 0x60, 0x91, 0x10, 0x00, 0x00, - 0x4a, 0x27, 0x01, 0x40, 0x00, 0x00, - 0x20, 0xa8, 0x0c, 0x30, 0x00, 0x00, - 0x11, 0x84, 0x58, 0xa0, 0x00, 0x00, - 0x49, 0x0a, 0x24, 0x00, 0x00, 0x00, - 0x86, 0x0e, 0x0a, 0x40, 0x00, 0x00, - 0x20, 0xd4, 0x22, 0x90, 0x00, 0x00, - 0x88, 0x4a, 0x41, 0x20, 0x00, 0x00, - 0xe7, 0xec, 0xdc, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_21[126] = - { - 0xc0, 0xd7, 0x02, 0x80, 0x00, 0x00, - 0x1d, 0x40, 0x55, 0x58, 0x00, 0x00, - 0xd4, 0x09, 0xd1, 0x00, 0x00, 0x00, - 0x02, 0x60, 0x02, 0x70, 0x00, 0x00, - 0x04, 0x28, 0x04, 0xb0, 0x00, 0x00, - 0x20, 0x99, 0x12, 0x48, 0x00, 0x00, - 0x40, 0x46, 0x21, 0x40, 0x00, 0x00, - 0x08, 0x84, 0x82, 0x90, 0x00, 0x00, - 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, - 0x23, 0x10, 0x09, 0x88, 0x00, 0x00, - 0x62, 0xd1, 0x88, 0x88, 0x00, 0x00, - 0x35, 0x23, 0xc4, 0x40, 0x00, 0x00, - 0x14, 0x14, 0x40, 0x38, 0x00, 0x00, - 0xc5, 0x08, 0x42, 0xc0, 0x00, 0x00, - 0x22, 0x0c, 0x90, 0x90, 0x00, 0x00, - 0x88, 0xb8, 0x04, 0x48, 0x00, 0x00, - 0x42, 0x54, 0x03, 0x10, 0x00, 0x00, - 0x28, 0xa4, 0x12, 0x88, 0x00, 0x00, - 0x94, 0x20, 0x09, 0x60, 0x00, 0x00, - 0x1b, 0x04, 0xac, 0x00, 0x00, 0x00, - 0x22, 0xc2, 0x61, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_22[132] = - { - 0x62, 0xd1, 0x88, 0x88, 0x00, 0x00, - 0x35, 0x23, 0xc4, 0x40, 0x00, 0x00, - 0x14, 0x14, 0x40, 0x38, 0x00, 0x00, - 0xc5, 0x08, 0x42, 0xc0, 0x00, 0x00, - 0x22, 0x0c, 0x90, 0x90, 0x00, 0x00, - 0x88, 0xb8, 0x04, 0x48, 0x00, 0x00, - 0x42, 0x54, 0x03, 0x10, 0x00, 0x00, - 0x28, 0xa4, 0x12, 0x88, 0x00, 0x00, - 0x94, 0x20, 0x09, 0x60, 0x00, 0x00, - 0x1b, 0x04, 0xac, 0x00, 0x00, 0x00, - 0x22, 0xc2, 0x61, 0x00, 0x00, 0x00, - 0xc0, 0xd7, 0x02, 0x80, 0x00, 0x00, - 0x1d, 0x40, 0x55, 0x58, 0x00, 0x00, - 0xd4, 0x09, 0xd1, 0x00, 0x00, 0x00, - 0x02, 0x60, 0x02, 0x70, 0x00, 0x00, - 0x04, 0x28, 0x04, 0xb0, 0x00, 0x00, - 0x20, 0x99, 0x12, 0x48, 0x00, 0x00, - 0x40, 0x46, 0x21, 0x40, 0x00, 0x00, - 0x08, 0x84, 0x82, 0x90, 0x00, 0x00, - 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, - 0x23, 0x10, 0x09, 0x88, 0x00, 0x00, - 0x1c, 0x90, 0xa9, 0xa0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_23[138] = - { - 0x62, 0xd1, 0x88, 0x88, 0x00, 0x00, - 0x35, 0x23, 0xc4, 0x40, 0x00, 0x00, - 0x14, 0x14, 0x40, 0x38, 0x00, 0x00, - 0xc5, 0x08, 0x42, 0xc0, 0x00, 0x00, - 0x22, 0x0c, 0x90, 0x90, 0x00, 0x00, - 0x88, 0xb8, 0x04, 0x48, 0x00, 0x00, - 0x42, 0x54, 0x03, 0x10, 0x00, 0x00, - 0x28, 0xa4, 0x12, 0x88, 0x00, 0x00, - 0x94, 0x20, 0x09, 0x60, 0x00, 0x00, - 0x1b, 0x04, 0xac, 0x00, 0x00, 0x00, - 0x22, 0xc2, 0x61, 0x00, 0x00, 0x00, - 0x81, 0x06, 0x22, 0x40, 0x00, 0x00, - 0x40, 0x69, 0x01, 0x50, 0x00, 0x00, - 0x90, 0x26, 0x09, 0x88, 0x00, 0x00, - 0x28, 0x28, 0x86, 0x90, 0x00, 0x00, - 0x52, 0x10, 0x41, 0x90, 0x00, 0x00, - 0x41, 0x89, 0x10, 0x28, 0x00, 0x00, - 0x09, 0x30, 0x43, 0x20, 0x00, 0x00, - 0x48, 0x45, 0x34, 0xa8, 0x00, 0x00, - 0x04, 0x44, 0xe0, 0x08, 0x00, 0x00, - 0x0e, 0x80, 0x5d, 0x20, 0x00, 0x00, - 0xa5, 0x92, 0x42, 0x10, 0x00, 0x00, - 0x12, 0x0d, 0xc8, 0x50, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_24[144] = - { - 0x81, 0x06, 0x22, 0x40, 0x00, 0x00, - 0x40, 0x69, 0x01, 0x50, 0x00, 0x00, - 0x90, 0x26, 0x09, 0x88, 0x00, 0x00, - 0x28, 0x28, 0x86, 0x90, 0x00, 0x00, - 0x52, 0x10, 0x41, 0x90, 0x00, 0x00, - 0x41, 0x89, 0x10, 0x28, 0x00, 0x00, - 0x09, 0x30, 0x43, 0x20, 0x00, 0x00, - 0x48, 0x45, 0x34, 0xa8, 0x00, 0x00, - 0x04, 0x44, 0xe0, 0x08, 0x00, 0x00, - 0x0e, 0x80, 0x5d, 0x20, 0x00, 0x00, - 0xa5, 0x92, 0x42, 0x10, 0x00, 0x00, - 0x12, 0x0d, 0xc8, 0x50, 0x00, 0x00, - 0x62, 0xd1, 0x88, 0x88, 0x00, 0x00, - 0x35, 0x23, 0xc4, 0x40, 0x00, 0x00, - 0x14, 0x14, 0x40, 0x38, 0x00, 0x00, - 0xc5, 0x08, 0x42, 0xc0, 0x00, 0x00, - 0x22, 0x0c, 0x90, 0x90, 0x00, 0x00, - 0x88, 0xb8, 0x04, 0x48, 0x00, 0x00, - 0x42, 0x54, 0x03, 0x10, 0x00, 0x00, - 0x28, 0xa4, 0x12, 0x88, 0x00, 0x00, - 0x94, 0x20, 0x09, 0x60, 0x00, 0x00, - 0x1b, 0x04, 0xac, 0x00, 0x00, 0x00, - 0x22, 0xc2, 0x61, 0x00, 0x00, 0x00, - 0xbd, 0x86, 0x97, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_25[150] = - { - 0x81, 0x06, 0x22, 0x40, 0x00, 0x00, - 0x40, 0x69, 0x01, 0x50, 0x00, 0x00, - 0x90, 0x26, 0x09, 0x88, 0x00, 0x00, - 0x28, 0x28, 0x86, 0x90, 0x00, 0x00, - 0x52, 0x10, 0x41, 0x90, 0x00, 0x00, - 0x41, 0x89, 0x10, 0x28, 0x00, 0x00, - 0x09, 0x30, 0x43, 0x20, 0x00, 0x00, - 0x48, 0x45, 0x34, 0xa8, 0x00, 0x00, - 0x04, 0x44, 0xe0, 0x08, 0x00, 0x00, - 0x0e, 0x80, 0x5d, 0x20, 0x00, 0x00, - 0xa5, 0x92, 0x42, 0x10, 0x00, 0x00, - 0x12, 0x0d, 0xc8, 0x50, 0x00, 0x00, - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x10, 0x00, 0x00, - 0x48, 0x05, 0x01, 0x28, 0x00, 0x00, - 0x00, 0xaf, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x08, 0x21, 0x80, 0x00, 0x00, - 0x81, 0x0a, 0x50, 0x48, 0x00, 0x00, - 0x23, 0x06, 0x23, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x84, 0xc8, 0x00, 0x00, - 0x80, 0x17, 0x05, 0x00, 0x00, 0x00, - 0x30, 0x10, 0x41, 0xa0, 0x00, 0x00, - 0x8c, 0x20, 0x1a, 0x40, 0x00, 0x00, - 0x54, 0x01, 0x64, 0x00, 0x00, 0x00, - 0x80, 0xc0, 0x28, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_26[156] = - { - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x10, 0x00, 0x00, - 0x48, 0x05, 0x01, 0x28, 0x00, 0x00, - 0x00, 0xaf, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x08, 0x21, 0x80, 0x00, 0x00, - 0x81, 0x0a, 0x50, 0x48, 0x00, 0x00, - 0x23, 0x06, 0x23, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x84, 0xc8, 0x00, 0x00, - 0x80, 0x17, 0x05, 0x00, 0x00, 0x00, - 0x30, 0x10, 0x41, 0xa0, 0x00, 0x00, - 0x8c, 0x20, 0x1a, 0x40, 0x00, 0x00, - 0x54, 0x01, 0x64, 0x00, 0x00, 0x00, - 0x80, 0xc0, 0x28, 0x30, 0x00, 0x00, - 0x81, 0x06, 0x22, 0x40, 0x00, 0x00, - 0x40, 0x69, 0x01, 0x50, 0x00, 0x00, - 0x90, 0x26, 0x09, 0x88, 0x00, 0x00, - 0x28, 0x28, 0x86, 0x90, 0x00, 0x00, - 0x52, 0x10, 0x41, 0x90, 0x00, 0x00, - 0x41, 0x89, 0x10, 0x28, 0x00, 0x00, - 0x09, 0x30, 0x43, 0x20, 0x00, 0x00, - 0x48, 0x45, 0x34, 0xa8, 0x00, 0x00, - 0x04, 0x44, 0xe0, 0x08, 0x00, 0x00, - 0x0e, 0x80, 0x5d, 0x20, 0x00, 0x00, - 0xa5, 0x92, 0x42, 0x10, 0x00, 0x00, - 0x12, 0x0d, 0xc8, 0x50, 0x00, 0x00, - 0xb5, 0x4c, 0xa9, 0x70, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_27[162] = - { - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x10, 0x00, 0x00, - 0x48, 0x05, 0x01, 0x28, 0x00, 0x00, - 0x00, 0xaf, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x08, 0x21, 0x80, 0x00, 0x00, - 0x81, 0x0a, 0x50, 0x48, 0x00, 0x00, - 0x23, 0x06, 0x23, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x84, 0xc8, 0x00, 0x00, - 0x80, 0x17, 0x05, 0x00, 0x00, 0x00, - 0x30, 0x10, 0x41, 0xa0, 0x00, 0x00, - 0x8c, 0x20, 0x1a, 0x40, 0x00, 0x00, - 0x54, 0x01, 0x64, 0x00, 0x00, 0x00, - 0x80, 0xc0, 0x28, 0x30, 0x00, 0x00, - 0x40, 0x55, 0x02, 0x08, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x50, 0x00, 0x00, - 0xc0, 0x06, 0x20, 0x48, 0x00, 0x00, - 0x28, 0x13, 0x00, 0x40, 0x00, 0x00, - 0x05, 0x0e, 0x02, 0x80, 0x00, 0x00, - 0x64, 0x80, 0x04, 0x88, 0x00, 0x00, - 0x81, 0x81, 0x00, 0xb0, 0x00, 0x00, - 0x10, 0x98, 0x88, 0x08, 0x00, 0x00, - 0x84, 0x22, 0x40, 0x10, 0x00, 0x00, - 0x12, 0x30, 0x49, 0x00, 0x00, 0x00, - 0x62, 0x01, 0x74, 0x00, 0x00, 0x00, - 0x28, 0x60, 0x81, 0x50, 0x00, 0x00, - 0x0e, 0x0a, 0x18, 0x20, 0x00, 0x00, - 0x10, 0x84, 0xa2, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_28[168] = - { - 0x40, 0x55, 0x02, 0x08, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x50, 0x00, 0x00, - 0xc0, 0x06, 0x20, 0x48, 0x00, 0x00, - 0x28, 0x13, 0x00, 0x40, 0x00, 0x00, - 0x05, 0x0e, 0x02, 0x80, 0x00, 0x00, - 0x64, 0x80, 0x04, 0x88, 0x00, 0x00, - 0x81, 0x81, 0x00, 0xb0, 0x00, 0x00, - 0x10, 0x98, 0x88, 0x08, 0x00, 0x00, - 0x84, 0x22, 0x40, 0x10, 0x00, 0x00, - 0x12, 0x30, 0x49, 0x00, 0x00, 0x00, - 0x62, 0x01, 0x74, 0x00, 0x00, 0x00, - 0x28, 0x60, 0x81, 0x50, 0x00, 0x00, - 0x0e, 0x0a, 0x18, 0x20, 0x00, 0x00, - 0x10, 0x84, 0xa2, 0x20, 0x00, 0x00, - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0x34, 0x60, 0xd1, 0x10, 0x00, 0x00, - 0x48, 0x05, 0x01, 0x28, 0x00, 0x00, - 0x00, 0xaf, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x08, 0x21, 0x80, 0x00, 0x00, - 0x81, 0x0a, 0x50, 0x48, 0x00, 0x00, - 0x23, 0x06, 0x23, 0x00, 0x00, 0x00, - 0x06, 0x80, 0x84, 0xc8, 0x00, 0x00, - 0x80, 0x17, 0x05, 0x00, 0x00, 0x00, - 0x30, 0x10, 0x41, 0xa0, 0x00, 0x00, - 0x8c, 0x20, 0x1a, 0x40, 0x00, 0x00, - 0x54, 0x01, 0x64, 0x00, 0x00, 0x00, - 0x80, 0xc0, 0x28, 0x30, 0x00, 0x00, - 0xbe, 0x1f, 0x99, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_29[174] = - { - 0x40, 0x55, 0x02, 0x08, 0x00, 0x00, - 0x15, 0x40, 0x55, 0x50, 0x00, 0x00, - 0xc0, 0x06, 0x20, 0x48, 0x00, 0x00, - 0x28, 0x13, 0x00, 0x40, 0x00, 0x00, - 0x05, 0x0e, 0x02, 0x80, 0x00, 0x00, - 0x64, 0x80, 0x04, 0x88, 0x00, 0x00, - 0x81, 0x81, 0x00, 0xb0, 0x00, 0x00, - 0x10, 0x98, 0x88, 0x08, 0x00, 0x00, - 0x84, 0x22, 0x40, 0x10, 0x00, 0x00, - 0x12, 0x30, 0x49, 0x00, 0x00, 0x00, - 0x62, 0x01, 0x74, 0x00, 0x00, 0x00, - 0x28, 0x60, 0x81, 0x50, 0x00, 0x00, - 0x0e, 0x0a, 0x18, 0x20, 0x00, 0x00, - 0x10, 0x84, 0xa2, 0x20, 0x00, 0x00, - 0x40, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x15, 0x40, 0xc4, 0x40, 0x00, 0x00, - 0xc0, 0x05, 0x60, 0x00, 0x00, 0x00, - 0x28, 0x10, 0x04, 0x48, 0x00, 0x00, - 0x05, 0x0e, 0x20, 0x80, 0x00, 0x00, - 0x64, 0x81, 0x10, 0x08, 0x00, 0x00, - 0x81, 0x80, 0xa4, 0x10, 0x00, 0x00, - 0x10, 0x9a, 0x0a, 0x80, 0x00, 0x00, - 0x84, 0x20, 0x28, 0x68, 0x00, 0x00, - 0x12, 0x30, 0x47, 0x80, 0x00, 0x00, - 0x62, 0x02, 0x10, 0x10, 0x00, 0x00, - 0x28, 0x62, 0x19, 0x00, 0x00, 0x00, - 0x0e, 0x08, 0x02, 0x18, 0x00, 0x00, - 0x10, 0x85, 0x11, 0x20, 0x00, 0x00, - 0x29, 0x50, 0x42, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_3[18] = - { - 0xac, 0xda, 0xb2, 0x48, 0x00, 0x00, - 0x55, 0x6d, 0x55, 0x28, 0x00, 0x00, - 0x27, 0xb5, 0x0c, 0xd8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_4[24] = - { - 0x2c, 0xd8, 0x96, 0xa8, 0x00, 0x00, - 0x93, 0x6a, 0x55, 0x50, 0x00, 0x00, - 0x1a, 0xb4, 0x69, 0xa8, 0x00, 0x00, - 0x47, 0x2d, 0x0f, 0x50, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_5[30] = - { - 0x64, 0xd9, 0x92, 0x88, 0x00, 0x00, - 0xa5, 0x68, 0x95, 0x50, 0x00, 0x00, - 0x52, 0xb5, 0x25, 0xa0, 0x00, 0x00, - 0x1d, 0xa9, 0x4e, 0x40, 0x00, 0x00, - 0x9c, 0x56, 0x38, 0xc0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_6[36] = - { - 0x4a, 0x55, 0x8a, 0x28, 0x00, 0x00, - 0x95, 0x48, 0x55, 0x50, 0x00, 0x00, - 0x14, 0xb5, 0x31, 0x18, 0x00, 0x00, - 0x51, 0xa9, 0x4a, 0x50, 0x00, 0x00, - 0x22, 0x6c, 0x8d, 0x90, 0x00, 0x00, - 0x88, 0x8e, 0x29, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_7[42] = - { - 0x62, 0x55, 0x8a, 0x88, 0x00, 0x00, - 0xb9, 0x22, 0xc4, 0x50, 0x00, 0x00, - 0x18, 0xb4, 0x61, 0xa8, 0x00, 0x00, - 0x54, 0x99, 0x13, 0x50, 0x00, 0x00, - 0x06, 0x6c, 0x4d, 0x90, 0x00, 0x00, - 0x85, 0x55, 0x24, 0x68, 0x00, 0x00, - 0xaa, 0x8a, 0x1a, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_8[48] = - { - 0xc0, 0x16, 0x40, 0x88, 0x00, 0x00, - 0x41, 0x60, 0x25, 0x40, 0x00, 0x00, - 0x88, 0x30, 0x01, 0xa8, 0x00, 0x00, - 0x20, 0xa4, 0x80, 0xd0, 0x00, 0x00, - 0x0a, 0x48, 0x51, 0x10, 0x00, 0x00, - 0x04, 0x9b, 0x08, 0x40, 0x00, 0x00, - 0x94, 0x40, 0x03, 0x18, 0x00, 0x00, - 0x72, 0x01, 0x96, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask29_9[54] = - { - 0xa2, 0x55, 0x88, 0x88, 0x00, 0x00, - 0x34, 0x60, 0x91, 0x10, 0x00, 0x00, - 0x4a, 0x27, 0x01, 0x40, 0x00, 0x00, - 0x20, 0xa8, 0x0c, 0x30, 0x00, 0x00, - 0x11, 0x84, 0x58, 0xa0, 0x00, 0x00, - 0x49, 0x0a, 0x24, 0x00, 0x00, 0x00, - 0x86, 0x0e, 0x0a, 0x40, 0x00, 0x00, - 0x20, 0xd4, 0x22, 0x90, 0x00, 0x00, - 0x88, 0x4a, 0x41, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask2_1[2] = - { - 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask2_2[4] = - { - 0xc0, 0x00, - 0x80, 0x00 - }; - - const WebRtc_UWord8 mask30_1[6] = - { - 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_10[60] = - { - 0xc0, 0xa1, 0x81, 0x40, 0x00, 0x00, - 0x15, 0x56, 0x2a, 0xac, 0x00, 0x00, - 0x74, 0x40, 0xe8, 0x80, 0x00, 0x00, - 0x00, 0x9c, 0x01, 0x38, 0x00, 0x00, - 0x01, 0x2c, 0x02, 0x58, 0x00, 0x00, - 0x44, 0x92, 0x89, 0x24, 0x00, 0x00, - 0x88, 0x51, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0xa4, 0x41, 0x48, 0x00, 0x00, - 0xaa, 0x05, 0x54, 0x08, 0x00, 0x00, - 0x02, 0x62, 0x04, 0xc4, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_11[66] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0xf1, 0x11, 0xe2, 0x20, 0x00, 0x00, - 0x10, 0x0e, 0x20, 0x1c, 0x00, 0x00, - 0x10, 0xb0, 0x21, 0x60, 0x00, 0x00, - 0x24, 0x24, 0x48, 0x48, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x00, 0xc4, 0x01, 0x88, 0x00, 0x00, - 0x04, 0xa2, 0x09, 0x44, 0x00, 0x00, - 0x02, 0x58, 0x04, 0xb0, 0x00, 0x00, - 0x2b, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x98, 0x41, 0x30, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_12[72] = - { - 0x88, 0x91, 0x11, 0x20, 0x00, 0x00, - 0x40, 0x54, 0x80, 0xa8, 0x00, 0x00, - 0x82, 0x63, 0x04, 0xc4, 0x00, 0x00, - 0x21, 0xa4, 0x43, 0x48, 0x00, 0x00, - 0x10, 0x64, 0x20, 0xc8, 0x00, 0x00, - 0x44, 0x0a, 0x88, 0x14, 0x00, 0x00, - 0x10, 0xc8, 0x21, 0x90, 0x00, 0x00, - 0x4d, 0x2a, 0x9a, 0x54, 0x00, 0x00, - 0x38, 0x02, 0x70, 0x04, 0x00, 0x00, - 0x17, 0x48, 0x2e, 0x90, 0x00, 0x00, - 0x90, 0x85, 0x21, 0x08, 0x00, 0x00, - 0x72, 0x14, 0xe4, 0x28, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_13[78] = - { - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0x34, 0x44, 0x68, 0x88, 0x00, 0x00, - 0x40, 0x4a, 0x80, 0x94, 0x00, 0x00, - 0xc4, 0x05, 0x88, 0x08, 0x00, 0x00, - 0x08, 0x60, 0x10, 0xc0, 0x00, 0x00, - 0x94, 0x13, 0x28, 0x24, 0x00, 0x00, - 0x88, 0xc1, 0x11, 0x80, 0x00, 0x00, - 0x21, 0x32, 0x42, 0x64, 0x00, 0x00, - 0xc1, 0x41, 0x82, 0x80, 0x00, 0x00, - 0x10, 0x68, 0x20, 0xd0, 0x00, 0x00, - 0x06, 0x90, 0x0d, 0x20, 0x00, 0x00, - 0x59, 0x00, 0xb2, 0x00, 0x00, 0x00, - 0x0a, 0x0c, 0x14, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_14[84] = - { - 0x40, 0x82, 0x81, 0x04, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x88, 0x13, 0x10, 0x24, 0x00, 0x00, - 0xc0, 0x11, 0x80, 0x20, 0x00, 0x00, - 0x80, 0xa1, 0x01, 0x40, 0x00, 0x00, - 0x01, 0x22, 0x02, 0x44, 0x00, 0x00, - 0x40, 0x2c, 0x80, 0x58, 0x00, 0x00, - 0x22, 0x02, 0x44, 0x04, 0x00, 0x00, - 0x90, 0x05, 0x20, 0x08, 0x00, 0x00, - 0x12, 0x40, 0x24, 0x80, 0x00, 0x00, - 0x5d, 0x00, 0xba, 0x00, 0x00, 0x00, - 0x20, 0x54, 0x40, 0xa8, 0x00, 0x00, - 0x86, 0x09, 0x0c, 0x10, 0x00, 0x00, - 0x28, 0x88, 0x51, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_15[90] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x31, 0x10, 0x62, 0x20, 0x00, 0x00, - 0x58, 0x00, 0xb0, 0x00, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x88, 0x21, 0x10, 0x40, 0x00, 0x00, - 0x44, 0x02, 0x88, 0x04, 0x00, 0x00, - 0x29, 0x04, 0x52, 0x08, 0x00, 0x00, - 0x82, 0xa1, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x1a, 0x14, 0x34, 0x00, 0x00, - 0x11, 0xe0, 0x23, 0xc0, 0x00, 0x00, - 0x84, 0x05, 0x08, 0x08, 0x00, 0x00, - 0x86, 0x41, 0x0c, 0x80, 0x00, 0x00, - 0x00, 0x86, 0x01, 0x0c, 0x00, 0x00, - 0x44, 0x48, 0x88, 0x90, 0x00, 0x00, - 0x10, 0x98, 0x21, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_16[96] = - { - 0x90, 0x23, 0x20, 0x44, 0x00, 0x00, - 0x09, 0x50, 0x12, 0xa0, 0x00, 0x00, - 0x00, 0x6a, 0x00, 0xd4, 0x00, 0x00, - 0x20, 0x34, 0x40, 0x68, 0x00, 0x00, - 0x14, 0x44, 0x28, 0x88, 0x00, 0x00, - 0xc2, 0x11, 0x84, 0x20, 0x00, 0x00, - 0x00, 0xc6, 0x01, 0x8c, 0x00, 0x00, - 0x65, 0x80, 0xcb, 0x00, 0x00, 0x00, - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0xb1, 0x15, 0x62, 0x28, 0x00, 0x00, - 0x18, 0x6a, 0x30, 0xd4, 0x00, 0x00, - 0x44, 0xd4, 0x89, 0xa8, 0x00, 0x00, - 0x13, 0x64, 0x26, 0xc8, 0x00, 0x00, - 0x49, 0x1a, 0x92, 0x34, 0x00, 0x00, - 0x86, 0x8d, 0x0d, 0x18, 0x00, 0x00, - 0xce, 0x58, 0xa0, 0x14, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_17[102] = - { - 0x90, 0x23, 0x20, 0x44, 0x00, 0x00, - 0x09, 0x50, 0x12, 0xa0, 0x00, 0x00, - 0x00, 0x6a, 0x00, 0xd4, 0x00, 0x00, - 0x20, 0x34, 0x40, 0x68, 0x00, 0x00, - 0x14, 0x44, 0x28, 0x88, 0x00, 0x00, - 0xc2, 0x11, 0x84, 0x20, 0x00, 0x00, - 0x00, 0xc6, 0x01, 0x8c, 0x00, 0x00, - 0x65, 0x80, 0xcb, 0x00, 0x00, 0x00, - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x24, 0x44, 0x48, 0x88, 0x00, 0x00, - 0xc0, 0x51, 0x80, 0xa0, 0x00, 0x00, - 0x03, 0x0c, 0x06, 0x18, 0x00, 0x00, - 0x16, 0x28, 0x2c, 0x50, 0x00, 0x00, - 0x89, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x91, 0x05, 0x20, 0x00, 0x00, - 0x08, 0xa4, 0x11, 0x48, 0x00, 0x00, - 0x90, 0x49, 0x20, 0x90, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_18[108] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x24, 0x44, 0x48, 0x88, 0x00, 0x00, - 0xc0, 0x51, 0x80, 0xa0, 0x00, 0x00, - 0x03, 0x0c, 0x06, 0x18, 0x00, 0x00, - 0x16, 0x28, 0x2c, 0x50, 0x00, 0x00, - 0x89, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x91, 0x05, 0x20, 0x00, 0x00, - 0x08, 0xa4, 0x11, 0x48, 0x00, 0x00, - 0x90, 0x49, 0x20, 0x90, 0x00, 0x00, - 0x90, 0x23, 0x20, 0x44, 0x00, 0x00, - 0x09, 0x50, 0x12, 0xa0, 0x00, 0x00, - 0x00, 0x6a, 0x00, 0xd4, 0x00, 0x00, - 0x20, 0x34, 0x40, 0x68, 0x00, 0x00, - 0x14, 0x44, 0x28, 0x88, 0x00, 0x00, - 0xc2, 0x11, 0x84, 0x20, 0x00, 0x00, - 0x00, 0xc6, 0x01, 0x8c, 0x00, 0x00, - 0x65, 0x80, 0xcb, 0x00, 0x00, 0x00, - 0x00, 0xb2, 0x47, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_19[114] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x24, 0x44, 0x48, 0x88, 0x00, 0x00, - 0xc0, 0x51, 0x80, 0xa0, 0x00, 0x00, - 0x03, 0x0c, 0x06, 0x18, 0x00, 0x00, - 0x16, 0x28, 0x2c, 0x50, 0x00, 0x00, - 0x89, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x91, 0x05, 0x20, 0x00, 0x00, - 0x08, 0xa4, 0x11, 0x48, 0x00, 0x00, - 0x90, 0x49, 0x20, 0x90, 0x00, 0x00, - 0xc0, 0xa1, 0x81, 0x40, 0x00, 0x00, - 0x15, 0x56, 0x2a, 0xac, 0x00, 0x00, - 0x74, 0x40, 0xe8, 0x80, 0x00, 0x00, - 0x00, 0x9c, 0x01, 0x38, 0x00, 0x00, - 0x01, 0x2c, 0x02, 0x58, 0x00, 0x00, - 0x44, 0x92, 0x89, 0x24, 0x00, 0x00, - 0x88, 0x51, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0xa4, 0x41, 0x48, 0x00, 0x00, - 0xaa, 0x05, 0x54, 0x08, 0x00, 0x00, - 0x02, 0x62, 0x04, 0xc4, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_2[12] = - { - 0xec, 0xeb, 0xd9, 0xd4, 0x00, 0x00, - 0xbb, 0x9d, 0x77, 0x38, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_20[120] = - { - 0xc0, 0xa1, 0x81, 0x40, 0x00, 0x00, - 0x15, 0x56, 0x2a, 0xac, 0x00, 0x00, - 0x74, 0x40, 0xe8, 0x80, 0x00, 0x00, - 0x00, 0x9c, 0x01, 0x38, 0x00, 0x00, - 0x01, 0x2c, 0x02, 0x58, 0x00, 0x00, - 0x44, 0x92, 0x89, 0x24, 0x00, 0x00, - 0x88, 0x51, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0xa4, 0x41, 0x48, 0x00, 0x00, - 0xaa, 0x05, 0x54, 0x08, 0x00, 0x00, - 0x02, 0x62, 0x04, 0xc4, 0x00, 0x00, - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x24, 0x44, 0x48, 0x88, 0x00, 0x00, - 0xc0, 0x51, 0x80, 0xa0, 0x00, 0x00, - 0x03, 0x0c, 0x06, 0x18, 0x00, 0x00, - 0x16, 0x28, 0x2c, 0x50, 0x00, 0x00, - 0x89, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x91, 0x05, 0x20, 0x00, 0x00, - 0x08, 0xa4, 0x11, 0x48, 0x00, 0x00, - 0x90, 0x49, 0x20, 0x90, 0x00, 0x00, - 0x51, 0x88, 0xd1, 0x78, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_21[126] = - { - 0xc0, 0xa1, 0x81, 0x40, 0x00, 0x00, - 0x15, 0x56, 0x2a, 0xac, 0x00, 0x00, - 0x74, 0x40, 0xe8, 0x80, 0x00, 0x00, - 0x00, 0x9c, 0x01, 0x38, 0x00, 0x00, - 0x01, 0x2c, 0x02, 0x58, 0x00, 0x00, - 0x44, 0x92, 0x89, 0x24, 0x00, 0x00, - 0x88, 0x51, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0xa4, 0x41, 0x48, 0x00, 0x00, - 0xaa, 0x05, 0x54, 0x08, 0x00, 0x00, - 0x02, 0x62, 0x04, 0xc4, 0x00, 0x00, - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0xf1, 0x11, 0xe2, 0x20, 0x00, 0x00, - 0x10, 0x0e, 0x20, 0x1c, 0x00, 0x00, - 0x10, 0xb0, 0x21, 0x60, 0x00, 0x00, - 0x24, 0x24, 0x48, 0x48, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x00, 0xc4, 0x01, 0x88, 0x00, 0x00, - 0x04, 0xa2, 0x09, 0x44, 0x00, 0x00, - 0x02, 0x58, 0x04, 0xb0, 0x00, 0x00, - 0x2b, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x98, 0x41, 0x30, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_22[132] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0xf1, 0x11, 0xe2, 0x20, 0x00, 0x00, - 0x10, 0x0e, 0x20, 0x1c, 0x00, 0x00, - 0x10, 0xb0, 0x21, 0x60, 0x00, 0x00, - 0x24, 0x24, 0x48, 0x48, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x00, 0xc4, 0x01, 0x88, 0x00, 0x00, - 0x04, 0xa2, 0x09, 0x44, 0x00, 0x00, - 0x02, 0x58, 0x04, 0xb0, 0x00, 0x00, - 0x2b, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x98, 0x41, 0x30, 0x80, 0x00, 0x00, - 0xc0, 0xa1, 0x81, 0x40, 0x00, 0x00, - 0x15, 0x56, 0x2a, 0xac, 0x00, 0x00, - 0x74, 0x40, 0xe8, 0x80, 0x00, 0x00, - 0x00, 0x9c, 0x01, 0x38, 0x00, 0x00, - 0x01, 0x2c, 0x02, 0x58, 0x00, 0x00, - 0x44, 0x92, 0x89, 0x24, 0x00, 0x00, - 0x88, 0x51, 0x10, 0xa0, 0x00, 0x00, - 0x20, 0xa4, 0x41, 0x48, 0x00, 0x00, - 0xaa, 0x05, 0x54, 0x08, 0x00, 0x00, - 0x02, 0x62, 0x04, 0xc4, 0x00, 0x00, - 0x03, 0x10, 0x18, 0x74, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_23[138] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0xf1, 0x11, 0xe2, 0x20, 0x00, 0x00, - 0x10, 0x0e, 0x20, 0x1c, 0x00, 0x00, - 0x10, 0xb0, 0x21, 0x60, 0x00, 0x00, - 0x24, 0x24, 0x48, 0x48, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x00, 0xc4, 0x01, 0x88, 0x00, 0x00, - 0x04, 0xa2, 0x09, 0x44, 0x00, 0x00, - 0x02, 0x58, 0x04, 0xb0, 0x00, 0x00, - 0x2b, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x98, 0x41, 0x30, 0x80, 0x00, 0x00, - 0x88, 0x91, 0x11, 0x20, 0x00, 0x00, - 0x40, 0x54, 0x80, 0xa8, 0x00, 0x00, - 0x82, 0x63, 0x04, 0xc4, 0x00, 0x00, - 0x21, 0xa4, 0x43, 0x48, 0x00, 0x00, - 0x10, 0x64, 0x20, 0xc8, 0x00, 0x00, - 0x44, 0x0a, 0x88, 0x14, 0x00, 0x00, - 0x10, 0xc8, 0x21, 0x90, 0x00, 0x00, - 0x4d, 0x2a, 0x9a, 0x54, 0x00, 0x00, - 0x38, 0x02, 0x70, 0x04, 0x00, 0x00, - 0x17, 0x48, 0x2e, 0x90, 0x00, 0x00, - 0x90, 0x85, 0x21, 0x08, 0x00, 0x00, - 0x72, 0x14, 0xe4, 0x28, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_24[144] = - { - 0x88, 0x91, 0x11, 0x20, 0x00, 0x00, - 0x40, 0x54, 0x80, 0xa8, 0x00, 0x00, - 0x82, 0x63, 0x04, 0xc4, 0x00, 0x00, - 0x21, 0xa4, 0x43, 0x48, 0x00, 0x00, - 0x10, 0x64, 0x20, 0xc8, 0x00, 0x00, - 0x44, 0x0a, 0x88, 0x14, 0x00, 0x00, - 0x10, 0xc8, 0x21, 0x90, 0x00, 0x00, - 0x4d, 0x2a, 0x9a, 0x54, 0x00, 0x00, - 0x38, 0x02, 0x70, 0x04, 0x00, 0x00, - 0x17, 0x48, 0x2e, 0x90, 0x00, 0x00, - 0x90, 0x85, 0x21, 0x08, 0x00, 0x00, - 0x72, 0x14, 0xe4, 0x28, 0x00, 0x00, - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0xf1, 0x11, 0xe2, 0x20, 0x00, 0x00, - 0x10, 0x0e, 0x20, 0x1c, 0x00, 0x00, - 0x10, 0xb0, 0x21, 0x60, 0x00, 0x00, - 0x24, 0x24, 0x48, 0x48, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x00, 0xc4, 0x01, 0x88, 0x00, 0x00, - 0x04, 0xa2, 0x09, 0x44, 0x00, 0x00, - 0x02, 0x58, 0x04, 0xb0, 0x00, 0x00, - 0x2b, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x98, 0x41, 0x30, 0x80, 0x00, 0x00, - 0xf3, 0x4d, 0x1c, 0x70, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_25[150] = - { - 0x88, 0x91, 0x11, 0x20, 0x00, 0x00, - 0x40, 0x54, 0x80, 0xa8, 0x00, 0x00, - 0x82, 0x63, 0x04, 0xc4, 0x00, 0x00, - 0x21, 0xa4, 0x43, 0x48, 0x00, 0x00, - 0x10, 0x64, 0x20, 0xc8, 0x00, 0x00, - 0x44, 0x0a, 0x88, 0x14, 0x00, 0x00, - 0x10, 0xc8, 0x21, 0x90, 0x00, 0x00, - 0x4d, 0x2a, 0x9a, 0x54, 0x00, 0x00, - 0x38, 0x02, 0x70, 0x04, 0x00, 0x00, - 0x17, 0x48, 0x2e, 0x90, 0x00, 0x00, - 0x90, 0x85, 0x21, 0x08, 0x00, 0x00, - 0x72, 0x14, 0xe4, 0x28, 0x00, 0x00, - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0x34, 0x44, 0x68, 0x88, 0x00, 0x00, - 0x40, 0x4a, 0x80, 0x94, 0x00, 0x00, - 0xc4, 0x05, 0x88, 0x08, 0x00, 0x00, - 0x08, 0x60, 0x10, 0xc0, 0x00, 0x00, - 0x94, 0x13, 0x28, 0x24, 0x00, 0x00, - 0x88, 0xc1, 0x11, 0x80, 0x00, 0x00, - 0x21, 0x32, 0x42, 0x64, 0x00, 0x00, - 0xc1, 0x41, 0x82, 0x80, 0x00, 0x00, - 0x10, 0x68, 0x20, 0xd0, 0x00, 0x00, - 0x06, 0x90, 0x0d, 0x20, 0x00, 0x00, - 0x59, 0x00, 0xb2, 0x00, 0x00, 0x00, - 0x0a, 0x0c, 0x14, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_26[156] = - { - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0x34, 0x44, 0x68, 0x88, 0x00, 0x00, - 0x40, 0x4a, 0x80, 0x94, 0x00, 0x00, - 0xc4, 0x05, 0x88, 0x08, 0x00, 0x00, - 0x08, 0x60, 0x10, 0xc0, 0x00, 0x00, - 0x94, 0x13, 0x28, 0x24, 0x00, 0x00, - 0x88, 0xc1, 0x11, 0x80, 0x00, 0x00, - 0x21, 0x32, 0x42, 0x64, 0x00, 0x00, - 0xc1, 0x41, 0x82, 0x80, 0x00, 0x00, - 0x10, 0x68, 0x20, 0xd0, 0x00, 0x00, - 0x06, 0x90, 0x0d, 0x20, 0x00, 0x00, - 0x59, 0x00, 0xb2, 0x00, 0x00, 0x00, - 0x0a, 0x0c, 0x14, 0x18, 0x00, 0x00, - 0x88, 0x91, 0x11, 0x20, 0x00, 0x00, - 0x40, 0x54, 0x80, 0xa8, 0x00, 0x00, - 0x82, 0x63, 0x04, 0xc4, 0x00, 0x00, - 0x21, 0xa4, 0x43, 0x48, 0x00, 0x00, - 0x10, 0x64, 0x20, 0xc8, 0x00, 0x00, - 0x44, 0x0a, 0x88, 0x14, 0x00, 0x00, - 0x10, 0xc8, 0x21, 0x90, 0x00, 0x00, - 0x4d, 0x2a, 0x9a, 0x54, 0x00, 0x00, - 0x38, 0x02, 0x70, 0x04, 0x00, 0x00, - 0x17, 0x48, 0x2e, 0x90, 0x00, 0x00, - 0x90, 0x85, 0x21, 0x08, 0x00, 0x00, - 0x72, 0x14, 0xe4, 0x28, 0x00, 0x00, - 0x83, 0x11, 0xad, 0xe8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_27[162] = - { - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0x34, 0x44, 0x68, 0x88, 0x00, 0x00, - 0x40, 0x4a, 0x80, 0x94, 0x00, 0x00, - 0xc4, 0x05, 0x88, 0x08, 0x00, 0x00, - 0x08, 0x60, 0x10, 0xc0, 0x00, 0x00, - 0x94, 0x13, 0x28, 0x24, 0x00, 0x00, - 0x88, 0xc1, 0x11, 0x80, 0x00, 0x00, - 0x21, 0x32, 0x42, 0x64, 0x00, 0x00, - 0xc1, 0x41, 0x82, 0x80, 0x00, 0x00, - 0x10, 0x68, 0x20, 0xd0, 0x00, 0x00, - 0x06, 0x90, 0x0d, 0x20, 0x00, 0x00, - 0x59, 0x00, 0xb2, 0x00, 0x00, 0x00, - 0x0a, 0x0c, 0x14, 0x18, 0x00, 0x00, - 0x40, 0x82, 0x81, 0x04, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x88, 0x13, 0x10, 0x24, 0x00, 0x00, - 0xc0, 0x11, 0x80, 0x20, 0x00, 0x00, - 0x80, 0xa1, 0x01, 0x40, 0x00, 0x00, - 0x01, 0x22, 0x02, 0x44, 0x00, 0x00, - 0x40, 0x2c, 0x80, 0x58, 0x00, 0x00, - 0x22, 0x02, 0x44, 0x04, 0x00, 0x00, - 0x90, 0x05, 0x20, 0x08, 0x00, 0x00, - 0x12, 0x40, 0x24, 0x80, 0x00, 0x00, - 0x5d, 0x00, 0xba, 0x00, 0x00, 0x00, - 0x20, 0x54, 0x40, 0xa8, 0x00, 0x00, - 0x86, 0x09, 0x0c, 0x10, 0x00, 0x00, - 0x28, 0x88, 0x51, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_28[168] = - { - 0x40, 0x82, 0x81, 0x04, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x88, 0x13, 0x10, 0x24, 0x00, 0x00, - 0xc0, 0x11, 0x80, 0x20, 0x00, 0x00, - 0x80, 0xa1, 0x01, 0x40, 0x00, 0x00, - 0x01, 0x22, 0x02, 0x44, 0x00, 0x00, - 0x40, 0x2c, 0x80, 0x58, 0x00, 0x00, - 0x22, 0x02, 0x44, 0x04, 0x00, 0x00, - 0x90, 0x05, 0x20, 0x08, 0x00, 0x00, - 0x12, 0x40, 0x24, 0x80, 0x00, 0x00, - 0x5d, 0x00, 0xba, 0x00, 0x00, 0x00, - 0x20, 0x54, 0x40, 0xa8, 0x00, 0x00, - 0x86, 0x09, 0x0c, 0x10, 0x00, 0x00, - 0x28, 0x88, 0x51, 0x10, 0x00, 0x00, - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0x34, 0x44, 0x68, 0x88, 0x00, 0x00, - 0x40, 0x4a, 0x80, 0x94, 0x00, 0x00, - 0xc4, 0x05, 0x88, 0x08, 0x00, 0x00, - 0x08, 0x60, 0x10, 0xc0, 0x00, 0x00, - 0x94, 0x13, 0x28, 0x24, 0x00, 0x00, - 0x88, 0xc1, 0x11, 0x80, 0x00, 0x00, - 0x21, 0x32, 0x42, 0x64, 0x00, 0x00, - 0xc1, 0x41, 0x82, 0x80, 0x00, 0x00, - 0x10, 0x68, 0x20, 0xd0, 0x00, 0x00, - 0x06, 0x90, 0x0d, 0x20, 0x00, 0x00, - 0x59, 0x00, 0xb2, 0x00, 0x00, 0x00, - 0x0a, 0x0c, 0x14, 0x18, 0x00, 0x00, - 0x94, 0x59, 0x03, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_29[174] = - { - 0x40, 0x82, 0x81, 0x04, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x88, 0x13, 0x10, 0x24, 0x00, 0x00, - 0xc0, 0x11, 0x80, 0x20, 0x00, 0x00, - 0x80, 0xa1, 0x01, 0x40, 0x00, 0x00, - 0x01, 0x22, 0x02, 0x44, 0x00, 0x00, - 0x40, 0x2c, 0x80, 0x58, 0x00, 0x00, - 0x22, 0x02, 0x44, 0x04, 0x00, 0x00, - 0x90, 0x05, 0x20, 0x08, 0x00, 0x00, - 0x12, 0x40, 0x24, 0x80, 0x00, 0x00, - 0x5d, 0x00, 0xba, 0x00, 0x00, 0x00, - 0x20, 0x54, 0x40, 0xa8, 0x00, 0x00, - 0x86, 0x09, 0x0c, 0x10, 0x00, 0x00, - 0x28, 0x88, 0x51, 0x10, 0x00, 0x00, - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x31, 0x10, 0x62, 0x20, 0x00, 0x00, - 0x58, 0x00, 0xb0, 0x00, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x88, 0x21, 0x10, 0x40, 0x00, 0x00, - 0x44, 0x02, 0x88, 0x04, 0x00, 0x00, - 0x29, 0x04, 0x52, 0x08, 0x00, 0x00, - 0x82, 0xa1, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x1a, 0x14, 0x34, 0x00, 0x00, - 0x11, 0xe0, 0x23, 0xc0, 0x00, 0x00, - 0x84, 0x05, 0x08, 0x08, 0x00, 0x00, - 0x86, 0x41, 0x0c, 0x80, 0x00, 0x00, - 0x00, 0x86, 0x01, 0x0c, 0x00, 0x00, - 0x44, 0x48, 0x88, 0x90, 0x00, 0x00, - 0x10, 0x98, 0x21, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_3[18] = - { - 0xac, 0x93, 0x59, 0x24, 0x00, 0x00, - 0x55, 0x4a, 0xaa, 0x94, 0x00, 0x00, - 0x43, 0x36, 0x86, 0x6c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_30[180] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x31, 0x10, 0x62, 0x20, 0x00, 0x00, - 0x58, 0x00, 0xb0, 0x00, 0x00, 0x00, - 0x01, 0x12, 0x02, 0x24, 0x00, 0x00, - 0x88, 0x21, 0x10, 0x40, 0x00, 0x00, - 0x44, 0x02, 0x88, 0x04, 0x00, 0x00, - 0x29, 0x04, 0x52, 0x08, 0x00, 0x00, - 0x82, 0xa1, 0x05, 0x40, 0x00, 0x00, - 0x0a, 0x1a, 0x14, 0x34, 0x00, 0x00, - 0x11, 0xe0, 0x23, 0xc0, 0x00, 0x00, - 0x84, 0x05, 0x08, 0x08, 0x00, 0x00, - 0x86, 0x41, 0x0c, 0x80, 0x00, 0x00, - 0x00, 0x86, 0x01, 0x0c, 0x00, 0x00, - 0x44, 0x48, 0x88, 0x90, 0x00, 0x00, - 0x10, 0x98, 0x21, 0x30, 0x00, 0x00, - 0x40, 0x82, 0x81, 0x04, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x88, 0x13, 0x10, 0x24, 0x00, 0x00, - 0xc0, 0x11, 0x80, 0x20, 0x00, 0x00, - 0x80, 0xa1, 0x01, 0x40, 0x00, 0x00, - 0x01, 0x22, 0x02, 0x44, 0x00, 0x00, - 0x40, 0x2c, 0x80, 0x58, 0x00, 0x00, - 0x22, 0x02, 0x44, 0x04, 0x00, 0x00, - 0x90, 0x05, 0x20, 0x08, 0x00, 0x00, - 0x12, 0x40, 0x24, 0x80, 0x00, 0x00, - 0x5d, 0x00, 0xba, 0x00, 0x00, 0x00, - 0x20, 0x54, 0x40, 0xa8, 0x00, 0x00, - 0x86, 0x09, 0x0c, 0x10, 0x00, 0x00, - 0x28, 0x88, 0x51, 0x10, 0x00, 0x00, - 0x46, 0xf1, 0xef, 0xec, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_4[24] = - { - 0x25, 0xaa, 0x4b, 0x54, 0x00, 0x00, - 0x95, 0x55, 0x2a, 0xa8, 0x00, 0x00, - 0x1a, 0x6a, 0x34, 0xd4, 0x00, 0x00, - 0x43, 0xd4, 0x87, 0xa8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_5[30] = - { - 0x64, 0xa2, 0xc9, 0x44, 0x00, 0x00, - 0x25, 0x54, 0x4a, 0xa8, 0x00, 0x00, - 0x49, 0x68, 0x92, 0xd0, 0x00, 0x00, - 0x53, 0x90, 0xa7, 0x20, 0x00, 0x00, - 0x8e, 0x31, 0x1c, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_6[36] = - { - 0x62, 0x8a, 0xc5, 0x14, 0x00, 0x00, - 0x15, 0x54, 0x2a, 0xa8, 0x00, 0x00, - 0x4c, 0x46, 0x98, 0x8c, 0x00, 0x00, - 0x52, 0x94, 0xa5, 0x28, 0x00, 0x00, - 0x23, 0x64, 0x46, 0xc8, 0x00, 0x00, - 0x8a, 0x59, 0x14, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_7[42] = - { - 0x62, 0xa2, 0xc5, 0x44, 0x00, 0x00, - 0xb1, 0x15, 0x62, 0x28, 0x00, 0x00, - 0x18, 0x6a, 0x30, 0xd4, 0x00, 0x00, - 0x44, 0xd4, 0x89, 0xa8, 0x00, 0x00, - 0x13, 0x64, 0x26, 0xc8, 0x00, 0x00, - 0x49, 0x1a, 0x92, 0x34, 0x00, 0x00, - 0x86, 0x8d, 0x0d, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_8[48] = - { - 0x90, 0x23, 0x20, 0x44, 0x00, 0x00, - 0x09, 0x50, 0x12, 0xa0, 0x00, 0x00, - 0x00, 0x6a, 0x00, 0xd4, 0x00, 0x00, - 0x20, 0x34, 0x40, 0x68, 0x00, 0x00, - 0x14, 0x44, 0x28, 0x88, 0x00, 0x00, - 0xc2, 0x11, 0x84, 0x20, 0x00, 0x00, - 0x00, 0xc6, 0x01, 0x8c, 0x00, 0x00, - 0x65, 0x80, 0xcb, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask30_9[54] = - { - 0x62, 0x22, 0xc4, 0x44, 0x00, 0x00, - 0x24, 0x44, 0x48, 0x88, 0x00, 0x00, - 0xc0, 0x51, 0x80, 0xa0, 0x00, 0x00, - 0x03, 0x0c, 0x06, 0x18, 0x00, 0x00, - 0x16, 0x28, 0x2c, 0x50, 0x00, 0x00, - 0x89, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x82, 0x91, 0x05, 0x20, 0x00, 0x00, - 0x08, 0xa4, 0x11, 0x48, 0x00, 0x00, - 0x90, 0x49, 0x20, 0x90, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_1[6] = - { - 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_10[60] = - { - 0xc0, 0xa0, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x56, 0x21, 0x44, 0x00, 0x00, - 0x74, 0x40, 0x02, 0x4a, 0x00, 0x00, - 0x00, 0x9c, 0x16, 0x84, 0x00, 0x00, - 0x01, 0x2d, 0xb0, 0x40, 0x00, 0x00, - 0x44, 0x93, 0x05, 0x18, 0x00, 0x00, - 0x88, 0x50, 0x48, 0x94, 0x00, 0x00, - 0x20, 0xa4, 0x70, 0x30, 0x00, 0x00, - 0xaa, 0x04, 0x54, 0x4a, 0x00, 0x00, - 0x02, 0x63, 0x09, 0x24, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_11[66] = - { - 0x62, 0x22, 0xaa, 0xaa, 0x00, 0x00, - 0xf1, 0x10, 0x54, 0x44, 0x00, 0x00, - 0x10, 0x0e, 0x62, 0x22, 0x00, 0x00, - 0x10, 0xb1, 0x06, 0x84, 0x00, 0x00, - 0x24, 0x24, 0x0d, 0x30, 0x00, 0x00, - 0x01, 0x12, 0x81, 0xc2, 0x00, 0x00, - 0x00, 0xc4, 0x58, 0x88, 0x00, 0x00, - 0x04, 0xa3, 0xb0, 0x50, 0x00, 0x00, - 0x02, 0x59, 0x25, 0x02, 0x00, 0x00, - 0x2b, 0x01, 0x08, 0x64, 0x00, 0x00, - 0x98, 0x40, 0xd0, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_12[72] = - { - 0x88, 0x91, 0x08, 0x62, 0x00, 0x00, - 0x40, 0x54, 0x31, 0x44, 0x00, 0x00, - 0x82, 0x62, 0x9c, 0x02, 0x00, 0x00, - 0x21, 0xa4, 0x89, 0x90, 0x00, 0x00, - 0x10, 0x64, 0x1d, 0x20, 0x00, 0x00, - 0x44, 0x0a, 0x41, 0x98, 0x00, 0x00, - 0x10, 0xc9, 0x26, 0x80, 0x00, 0x00, - 0x4d, 0x2a, 0x5a, 0x20, 0x00, 0x00, - 0x38, 0x02, 0x62, 0x88, 0x00, 0x00, - 0x17, 0x49, 0x80, 0x46, 0x00, 0x00, - 0x90, 0x84, 0x22, 0x4a, 0x00, 0x00, - 0x72, 0x15, 0xd1, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_13[78] = - { - 0x62, 0xa2, 0x8a, 0x2a, 0x00, 0x00, - 0x34, 0x44, 0x44, 0x44, 0x00, 0x00, - 0x40, 0x4b, 0x2c, 0x18, 0x00, 0x00, - 0xc4, 0x04, 0x18, 0xa0, 0x00, 0x00, - 0x08, 0x60, 0xc4, 0x08, 0x00, 0x00, - 0x94, 0x12, 0x92, 0x0c, 0x00, 0x00, - 0x88, 0xc0, 0x23, 0x04, 0x00, 0x00, - 0x21, 0x32, 0x24, 0x70, 0x00, 0x00, - 0xc1, 0x40, 0x80, 0xe2, 0x00, 0x00, - 0x10, 0x69, 0x51, 0x14, 0x00, 0x00, - 0x06, 0x90, 0x11, 0x42, 0x00, 0x00, - 0x59, 0x01, 0x41, 0x80, 0x00, 0x00, - 0x0a, 0x0d, 0x8a, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_14[84] = - { - 0x40, 0x82, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x44, 0x14, 0x00, 0x00, - 0x88, 0x13, 0x09, 0xa0, 0x00, 0x00, - 0xc0, 0x10, 0x19, 0x14, 0x00, 0x00, - 0x80, 0xa0, 0x30, 0x0c, 0x00, 0x00, - 0x01, 0x22, 0x60, 0x06, 0x00, 0x00, - 0x40, 0x2c, 0xc2, 0x10, 0x00, 0x00, - 0x22, 0x02, 0x80, 0x22, 0x00, 0x00, - 0x90, 0x04, 0x20, 0x58, 0x00, 0x00, - 0x12, 0x40, 0x12, 0xc0, 0x00, 0x00, - 0x5d, 0x00, 0x01, 0x28, 0x00, 0x00, - 0x20, 0x54, 0xa4, 0x80, 0x00, 0x00, - 0x86, 0x09, 0x48, 0x48, 0x00, 0x00, - 0x28, 0x89, 0x05, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_15[90] = - { - 0x62, 0x22, 0xaa, 0x22, 0x00, 0x00, - 0x31, 0x10, 0x44, 0x44, 0x00, 0x00, - 0x58, 0x00, 0x22, 0x22, 0x00, 0x00, - 0x01, 0x13, 0x00, 0x8a, 0x00, 0x00, - 0x88, 0x20, 0x40, 0x34, 0x00, 0x00, - 0x44, 0x02, 0x10, 0xd0, 0x00, 0x00, - 0x29, 0x04, 0x45, 0x08, 0x00, 0x00, - 0x82, 0xa0, 0x90, 0x12, 0x00, 0x00, - 0x0a, 0x1a, 0x0e, 0x02, 0x00, 0x00, - 0x11, 0xe1, 0x28, 0x40, 0x00, 0x00, - 0x84, 0x05, 0x04, 0x0c, 0x00, 0x00, - 0x86, 0x40, 0xc0, 0x90, 0x00, 0x00, - 0x00, 0x87, 0x13, 0x00, 0x00, 0x00, - 0x44, 0x48, 0x01, 0x1c, 0x00, 0x00, - 0x10, 0x98, 0x30, 0x44, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_16[96] = - { - 0x90, 0x22, 0x40, 0xa8, 0x00, 0x00, - 0x09, 0x50, 0x31, 0x10, 0x00, 0x00, - 0x00, 0x6b, 0x08, 0x0e, 0x00, 0x00, - 0x20, 0x34, 0xc0, 0x90, 0x00, 0x00, - 0x14, 0x44, 0x25, 0x04, 0x00, 0x00, - 0xc2, 0x11, 0x02, 0x82, 0x00, 0x00, - 0x00, 0xc6, 0x80, 0xc4, 0x00, 0x00, - 0x65, 0x80, 0x2c, 0x60, 0x00, 0x00, - 0x62, 0xa2, 0x8a, 0xa2, 0x00, 0x00, - 0xb1, 0x14, 0x44, 0x54, 0x00, 0x00, - 0x18, 0x6b, 0x22, 0x22, 0x00, 0x00, - 0x44, 0xd4, 0x5c, 0x10, 0x00, 0x00, - 0x13, 0x64, 0x90, 0x68, 0x00, 0x00, - 0x49, 0x1b, 0x20, 0x52, 0x00, 0x00, - 0x86, 0x8c, 0x13, 0x0c, 0x00, 0x00, - 0x8d, 0x94, 0xa9, 0xe0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_17[102] = - { - 0x90, 0x22, 0x40, 0xa8, 0x00, 0x00, - 0x09, 0x50, 0x31, 0x10, 0x00, 0x00, - 0x00, 0x6b, 0x08, 0x0e, 0x00, 0x00, - 0x20, 0x34, 0xc0, 0x90, 0x00, 0x00, - 0x14, 0x44, 0x25, 0x04, 0x00, 0x00, - 0xc2, 0x11, 0x02, 0x82, 0x00, 0x00, - 0x00, 0xc6, 0x80, 0xc4, 0x00, 0x00, - 0x65, 0x80, 0x2c, 0x60, 0x00, 0x00, - 0x62, 0x22, 0xaa, 0xa2, 0x00, 0x00, - 0x24, 0x44, 0x44, 0x54, 0x00, 0x00, - 0xc0, 0x50, 0x0b, 0x0a, 0x00, 0x00, - 0x03, 0x0c, 0x12, 0x94, 0x00, 0x00, - 0x16, 0x29, 0x08, 0x64, 0x00, 0x00, - 0x89, 0x01, 0x80, 0x1a, 0x00, 0x00, - 0x82, 0x90, 0x41, 0x4c, 0x00, 0x00, - 0x08, 0xa4, 0x34, 0x12, 0x00, 0x00, - 0x90, 0x48, 0x88, 0xc8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_18[108] = - { - 0x62, 0x22, 0xaa, 0xa2, 0x00, 0x00, - 0x24, 0x44, 0x44, 0x54, 0x00, 0x00, - 0xc0, 0x50, 0x0b, 0x0a, 0x00, 0x00, - 0x03, 0x0c, 0x12, 0x94, 0x00, 0x00, - 0x16, 0x29, 0x08, 0x64, 0x00, 0x00, - 0x89, 0x01, 0x80, 0x1a, 0x00, 0x00, - 0x82, 0x90, 0x41, 0x4c, 0x00, 0x00, - 0x08, 0xa4, 0x34, 0x12, 0x00, 0x00, - 0x90, 0x48, 0x88, 0xc8, 0x00, 0x00, - 0x90, 0x22, 0x40, 0xa8, 0x00, 0x00, - 0x09, 0x50, 0x31, 0x10, 0x00, 0x00, - 0x00, 0x6b, 0x08, 0x0e, 0x00, 0x00, - 0x20, 0x34, 0xc0, 0x90, 0x00, 0x00, - 0x14, 0x44, 0x25, 0x04, 0x00, 0x00, - 0xc2, 0x11, 0x02, 0x82, 0x00, 0x00, - 0x00, 0xc6, 0x80, 0xc4, 0x00, 0x00, - 0x65, 0x80, 0x2c, 0x60, 0x00, 0x00, - 0xe3, 0xd1, 0x2e, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_19[114] = - { - 0x62, 0x22, 0xaa, 0xa2, 0x00, 0x00, - 0x24, 0x44, 0x44, 0x54, 0x00, 0x00, - 0xc0, 0x50, 0x0b, 0x0a, 0x00, 0x00, - 0x03, 0x0c, 0x12, 0x94, 0x00, 0x00, - 0x16, 0x29, 0x08, 0x64, 0x00, 0x00, - 0x89, 0x01, 0x80, 0x1a, 0x00, 0x00, - 0x82, 0x90, 0x41, 0x4c, 0x00, 0x00, - 0x08, 0xa4, 0x34, 0x12, 0x00, 0x00, - 0x90, 0x48, 0x88, 0xc8, 0x00, 0x00, - 0xc0, 0xa0, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x56, 0x21, 0x44, 0x00, 0x00, - 0x74, 0x40, 0x02, 0x4a, 0x00, 0x00, - 0x00, 0x9c, 0x16, 0x84, 0x00, 0x00, - 0x01, 0x2d, 0xb0, 0x40, 0x00, 0x00, - 0x44, 0x93, 0x05, 0x18, 0x00, 0x00, - 0x88, 0x50, 0x48, 0x94, 0x00, 0x00, - 0x20, 0xa4, 0x70, 0x30, 0x00, 0x00, - 0xaa, 0x04, 0x54, 0x4a, 0x00, 0x00, - 0x02, 0x63, 0x09, 0x24, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_2[12] = - { - 0xec, 0xeb, 0x5d, 0x5c, 0x00, 0x00, - 0xbb, 0x9c, 0xf2, 0xf2, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_20[120] = - { - 0xc0, 0xa0, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x56, 0x21, 0x44, 0x00, 0x00, - 0x74, 0x40, 0x02, 0x4a, 0x00, 0x00, - 0x00, 0x9c, 0x16, 0x84, 0x00, 0x00, - 0x01, 0x2d, 0xb0, 0x40, 0x00, 0x00, - 0x44, 0x93, 0x05, 0x18, 0x00, 0x00, - 0x88, 0x50, 0x48, 0x94, 0x00, 0x00, - 0x20, 0xa4, 0x70, 0x30, 0x00, 0x00, - 0xaa, 0x04, 0x54, 0x4a, 0x00, 0x00, - 0x02, 0x63, 0x09, 0x24, 0x00, 0x00, - 0x62, 0x22, 0xaa, 0xa2, 0x00, 0x00, - 0x24, 0x44, 0x44, 0x54, 0x00, 0x00, - 0xc0, 0x50, 0x0b, 0x0a, 0x00, 0x00, - 0x03, 0x0c, 0x12, 0x94, 0x00, 0x00, - 0x16, 0x29, 0x08, 0x64, 0x00, 0x00, - 0x89, 0x01, 0x80, 0x1a, 0x00, 0x00, - 0x82, 0x90, 0x41, 0x4c, 0x00, 0x00, - 0x08, 0xa4, 0x34, 0x12, 0x00, 0x00, - 0x90, 0x48, 0x88, 0xc8, 0x00, 0x00, - 0x9a, 0xd4, 0x6a, 0x36, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_21[126] = - { - 0xc0, 0xa0, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x56, 0x21, 0x44, 0x00, 0x00, - 0x74, 0x40, 0x02, 0x4a, 0x00, 0x00, - 0x00, 0x9c, 0x16, 0x84, 0x00, 0x00, - 0x01, 0x2d, 0xb0, 0x40, 0x00, 0x00, - 0x44, 0x93, 0x05, 0x18, 0x00, 0x00, - 0x88, 0x50, 0x48, 0x94, 0x00, 0x00, - 0x20, 0xa4, 0x70, 0x30, 0x00, 0x00, - 0xaa, 0x04, 0x54, 0x4a, 0x00, 0x00, - 0x02, 0x63, 0x09, 0x24, 0x00, 0x00, - 0x62, 0x22, 0xaa, 0xaa, 0x00, 0x00, - 0xf1, 0x10, 0x54, 0x44, 0x00, 0x00, - 0x10, 0x0e, 0x62, 0x22, 0x00, 0x00, - 0x10, 0xb1, 0x06, 0x84, 0x00, 0x00, - 0x24, 0x24, 0x0d, 0x30, 0x00, 0x00, - 0x01, 0x12, 0x81, 0xc2, 0x00, 0x00, - 0x00, 0xc4, 0x58, 0x88, 0x00, 0x00, - 0x04, 0xa3, 0xb0, 0x50, 0x00, 0x00, - 0x02, 0x59, 0x25, 0x02, 0x00, 0x00, - 0x2b, 0x01, 0x08, 0x64, 0x00, 0x00, - 0x98, 0x40, 0xd0, 0x18, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_22[132] = - { - 0x62, 0x22, 0xaa, 0xaa, 0x00, 0x00, - 0xf1, 0x10, 0x54, 0x44, 0x00, 0x00, - 0x10, 0x0e, 0x62, 0x22, 0x00, 0x00, - 0x10, 0xb1, 0x06, 0x84, 0x00, 0x00, - 0x24, 0x24, 0x0d, 0x30, 0x00, 0x00, - 0x01, 0x12, 0x81, 0xc2, 0x00, 0x00, - 0x00, 0xc4, 0x58, 0x88, 0x00, 0x00, - 0x04, 0xa3, 0xb0, 0x50, 0x00, 0x00, - 0x02, 0x59, 0x25, 0x02, 0x00, 0x00, - 0x2b, 0x01, 0x08, 0x64, 0x00, 0x00, - 0x98, 0x40, 0xd0, 0x18, 0x00, 0x00, - 0xc0, 0xa0, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x56, 0x21, 0x44, 0x00, 0x00, - 0x74, 0x40, 0x02, 0x4a, 0x00, 0x00, - 0x00, 0x9c, 0x16, 0x84, 0x00, 0x00, - 0x01, 0x2d, 0xb0, 0x40, 0x00, 0x00, - 0x44, 0x93, 0x05, 0x18, 0x00, 0x00, - 0x88, 0x50, 0x48, 0x94, 0x00, 0x00, - 0x20, 0xa4, 0x70, 0x30, 0x00, 0x00, - 0xaa, 0x04, 0x54, 0x4a, 0x00, 0x00, - 0x02, 0x63, 0x09, 0x24, 0x00, 0x00, - 0x32, 0x23, 0x73, 0x8e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_23[138] = - { - 0x62, 0x22, 0xaa, 0xaa, 0x00, 0x00, - 0xf1, 0x10, 0x54, 0x44, 0x00, 0x00, - 0x10, 0x0e, 0x62, 0x22, 0x00, 0x00, - 0x10, 0xb1, 0x06, 0x84, 0x00, 0x00, - 0x24, 0x24, 0x0d, 0x30, 0x00, 0x00, - 0x01, 0x12, 0x81, 0xc2, 0x00, 0x00, - 0x00, 0xc4, 0x58, 0x88, 0x00, 0x00, - 0x04, 0xa3, 0xb0, 0x50, 0x00, 0x00, - 0x02, 0x59, 0x25, 0x02, 0x00, 0x00, - 0x2b, 0x01, 0x08, 0x64, 0x00, 0x00, - 0x98, 0x40, 0xd0, 0x18, 0x00, 0x00, - 0x88, 0x91, 0x08, 0x62, 0x00, 0x00, - 0x40, 0x54, 0x31, 0x44, 0x00, 0x00, - 0x82, 0x62, 0x9c, 0x02, 0x00, 0x00, - 0x21, 0xa4, 0x89, 0x90, 0x00, 0x00, - 0x10, 0x64, 0x1d, 0x20, 0x00, 0x00, - 0x44, 0x0a, 0x41, 0x98, 0x00, 0x00, - 0x10, 0xc9, 0x26, 0x80, 0x00, 0x00, - 0x4d, 0x2a, 0x5a, 0x20, 0x00, 0x00, - 0x38, 0x02, 0x62, 0x88, 0x00, 0x00, - 0x17, 0x49, 0x80, 0x46, 0x00, 0x00, - 0x90, 0x84, 0x22, 0x4a, 0x00, 0x00, - 0x72, 0x15, 0xd1, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_24[144] = - { - 0x88, 0x91, 0x08, 0x62, 0x00, 0x00, - 0x40, 0x54, 0x31, 0x44, 0x00, 0x00, - 0x82, 0x62, 0x9c, 0x02, 0x00, 0x00, - 0x21, 0xa4, 0x89, 0x90, 0x00, 0x00, - 0x10, 0x64, 0x1d, 0x20, 0x00, 0x00, - 0x44, 0x0a, 0x41, 0x98, 0x00, 0x00, - 0x10, 0xc9, 0x26, 0x80, 0x00, 0x00, - 0x4d, 0x2a, 0x5a, 0x20, 0x00, 0x00, - 0x38, 0x02, 0x62, 0x88, 0x00, 0x00, - 0x17, 0x49, 0x80, 0x46, 0x00, 0x00, - 0x90, 0x84, 0x22, 0x4a, 0x00, 0x00, - 0x72, 0x15, 0xd1, 0x00, 0x00, 0x00, - 0x62, 0x22, 0xaa, 0xaa, 0x00, 0x00, - 0xf1, 0x10, 0x54, 0x44, 0x00, 0x00, - 0x10, 0x0e, 0x62, 0x22, 0x00, 0x00, - 0x10, 0xb1, 0x06, 0x84, 0x00, 0x00, - 0x24, 0x24, 0x0d, 0x30, 0x00, 0x00, - 0x01, 0x12, 0x81, 0xc2, 0x00, 0x00, - 0x00, 0xc4, 0x58, 0x88, 0x00, 0x00, - 0x04, 0xa3, 0xb0, 0x50, 0x00, 0x00, - 0x02, 0x59, 0x25, 0x02, 0x00, 0x00, - 0x2b, 0x01, 0x08, 0x64, 0x00, 0x00, - 0x98, 0x40, 0xd0, 0x18, 0x00, 0x00, - 0xf0, 0xdf, 0x91, 0xb6, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_25[150] = - { - 0x88, 0x91, 0x08, 0x62, 0x00, 0x00, - 0x40, 0x54, 0x31, 0x44, 0x00, 0x00, - 0x82, 0x62, 0x9c, 0x02, 0x00, 0x00, - 0x21, 0xa4, 0x89, 0x90, 0x00, 0x00, - 0x10, 0x64, 0x1d, 0x20, 0x00, 0x00, - 0x44, 0x0a, 0x41, 0x98, 0x00, 0x00, - 0x10, 0xc9, 0x26, 0x80, 0x00, 0x00, - 0x4d, 0x2a, 0x5a, 0x20, 0x00, 0x00, - 0x38, 0x02, 0x62, 0x88, 0x00, 0x00, - 0x17, 0x49, 0x80, 0x46, 0x00, 0x00, - 0x90, 0x84, 0x22, 0x4a, 0x00, 0x00, - 0x72, 0x15, 0xd1, 0x00, 0x00, 0x00, - 0x62, 0xa2, 0x8a, 0x2a, 0x00, 0x00, - 0x34, 0x44, 0x44, 0x44, 0x00, 0x00, - 0x40, 0x4b, 0x2c, 0x18, 0x00, 0x00, - 0xc4, 0x04, 0x18, 0xa0, 0x00, 0x00, - 0x08, 0x60, 0xc4, 0x08, 0x00, 0x00, - 0x94, 0x12, 0x92, 0x0c, 0x00, 0x00, - 0x88, 0xc0, 0x23, 0x04, 0x00, 0x00, - 0x21, 0x32, 0x24, 0x70, 0x00, 0x00, - 0xc1, 0x40, 0x80, 0xe2, 0x00, 0x00, - 0x10, 0x69, 0x51, 0x14, 0x00, 0x00, - 0x06, 0x90, 0x11, 0x42, 0x00, 0x00, - 0x59, 0x01, 0x41, 0x80, 0x00, 0x00, - 0x0a, 0x0d, 0x8a, 0x20, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_26[156] = - { - 0x62, 0xa2, 0x8a, 0x2a, 0x00, 0x00, - 0x34, 0x44, 0x44, 0x44, 0x00, 0x00, - 0x40, 0x4b, 0x2c, 0x18, 0x00, 0x00, - 0xc4, 0x04, 0x18, 0xa0, 0x00, 0x00, - 0x08, 0x60, 0xc4, 0x08, 0x00, 0x00, - 0x94, 0x12, 0x92, 0x0c, 0x00, 0x00, - 0x88, 0xc0, 0x23, 0x04, 0x00, 0x00, - 0x21, 0x32, 0x24, 0x70, 0x00, 0x00, - 0xc1, 0x40, 0x80, 0xe2, 0x00, 0x00, - 0x10, 0x69, 0x51, 0x14, 0x00, 0x00, - 0x06, 0x90, 0x11, 0x42, 0x00, 0x00, - 0x59, 0x01, 0x41, 0x80, 0x00, 0x00, - 0x0a, 0x0d, 0x8a, 0x20, 0x00, 0x00, - 0x88, 0x91, 0x08, 0x62, 0x00, 0x00, - 0x40, 0x54, 0x31, 0x44, 0x00, 0x00, - 0x82, 0x62, 0x9c, 0x02, 0x00, 0x00, - 0x21, 0xa4, 0x89, 0x90, 0x00, 0x00, - 0x10, 0x64, 0x1d, 0x20, 0x00, 0x00, - 0x44, 0x0a, 0x41, 0x98, 0x00, 0x00, - 0x10, 0xc9, 0x26, 0x80, 0x00, 0x00, - 0x4d, 0x2a, 0x5a, 0x20, 0x00, 0x00, - 0x38, 0x02, 0x62, 0x88, 0x00, 0x00, - 0x17, 0x49, 0x80, 0x46, 0x00, 0x00, - 0x90, 0x84, 0x22, 0x4a, 0x00, 0x00, - 0x72, 0x15, 0xd1, 0x00, 0x00, 0x00, - 0xc5, 0x75, 0x48, 0xba, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_27[162] = - { - 0x62, 0xa2, 0x8a, 0x2a, 0x00, 0x00, - 0x34, 0x44, 0x44, 0x44, 0x00, 0x00, - 0x40, 0x4b, 0x2c, 0x18, 0x00, 0x00, - 0xc4, 0x04, 0x18, 0xa0, 0x00, 0x00, - 0x08, 0x60, 0xc4, 0x08, 0x00, 0x00, - 0x94, 0x12, 0x92, 0x0c, 0x00, 0x00, - 0x88, 0xc0, 0x23, 0x04, 0x00, 0x00, - 0x21, 0x32, 0x24, 0x70, 0x00, 0x00, - 0xc1, 0x40, 0x80, 0xe2, 0x00, 0x00, - 0x10, 0x69, 0x51, 0x14, 0x00, 0x00, - 0x06, 0x90, 0x11, 0x42, 0x00, 0x00, - 0x59, 0x01, 0x41, 0x80, 0x00, 0x00, - 0x0a, 0x0d, 0x8a, 0x20, 0x00, 0x00, - 0x40, 0x82, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x44, 0x14, 0x00, 0x00, - 0x88, 0x13, 0x09, 0xa0, 0x00, 0x00, - 0xc0, 0x10, 0x19, 0x14, 0x00, 0x00, - 0x80, 0xa0, 0x30, 0x0c, 0x00, 0x00, - 0x01, 0x22, 0x60, 0x06, 0x00, 0x00, - 0x40, 0x2c, 0xc2, 0x10, 0x00, 0x00, - 0x22, 0x02, 0x80, 0x22, 0x00, 0x00, - 0x90, 0x04, 0x20, 0x58, 0x00, 0x00, - 0x12, 0x40, 0x12, 0xc0, 0x00, 0x00, - 0x5d, 0x00, 0x01, 0x28, 0x00, 0x00, - 0x20, 0x54, 0xa4, 0x80, 0x00, 0x00, - 0x86, 0x09, 0x48, 0x48, 0x00, 0x00, - 0x28, 0x89, 0x05, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_28[168] = - { - 0x40, 0x82, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x44, 0x14, 0x00, 0x00, - 0x88, 0x13, 0x09, 0xa0, 0x00, 0x00, - 0xc0, 0x10, 0x19, 0x14, 0x00, 0x00, - 0x80, 0xa0, 0x30, 0x0c, 0x00, 0x00, - 0x01, 0x22, 0x60, 0x06, 0x00, 0x00, - 0x40, 0x2c, 0xc2, 0x10, 0x00, 0x00, - 0x22, 0x02, 0x80, 0x22, 0x00, 0x00, - 0x90, 0x04, 0x20, 0x58, 0x00, 0x00, - 0x12, 0x40, 0x12, 0xc0, 0x00, 0x00, - 0x5d, 0x00, 0x01, 0x28, 0x00, 0x00, - 0x20, 0x54, 0xa4, 0x80, 0x00, 0x00, - 0x86, 0x09, 0x48, 0x48, 0x00, 0x00, - 0x28, 0x89, 0x05, 0x10, 0x00, 0x00, - 0x62, 0xa2, 0x8a, 0x2a, 0x00, 0x00, - 0x34, 0x44, 0x44, 0x44, 0x00, 0x00, - 0x40, 0x4b, 0x2c, 0x18, 0x00, 0x00, - 0xc4, 0x04, 0x18, 0xa0, 0x00, 0x00, - 0x08, 0x60, 0xc4, 0x08, 0x00, 0x00, - 0x94, 0x12, 0x92, 0x0c, 0x00, 0x00, - 0x88, 0xc0, 0x23, 0x04, 0x00, 0x00, - 0x21, 0x32, 0x24, 0x70, 0x00, 0x00, - 0xc1, 0x40, 0x80, 0xe2, 0x00, 0x00, - 0x10, 0x69, 0x51, 0x14, 0x00, 0x00, - 0x06, 0x90, 0x11, 0x42, 0x00, 0x00, - 0x59, 0x01, 0x41, 0x80, 0x00, 0x00, - 0x0a, 0x0d, 0x8a, 0x20, 0x00, 0x00, - 0xbc, 0x0d, 0xca, 0x28, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_29[174] = - { - 0x40, 0x82, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x44, 0x14, 0x00, 0x00, - 0x88, 0x13, 0x09, 0xa0, 0x00, 0x00, - 0xc0, 0x10, 0x19, 0x14, 0x00, 0x00, - 0x80, 0xa0, 0x30, 0x0c, 0x00, 0x00, - 0x01, 0x22, 0x60, 0x06, 0x00, 0x00, - 0x40, 0x2c, 0xc2, 0x10, 0x00, 0x00, - 0x22, 0x02, 0x80, 0x22, 0x00, 0x00, - 0x90, 0x04, 0x20, 0x58, 0x00, 0x00, - 0x12, 0x40, 0x12, 0xc0, 0x00, 0x00, - 0x5d, 0x00, 0x01, 0x28, 0x00, 0x00, - 0x20, 0x54, 0xa4, 0x80, 0x00, 0x00, - 0x86, 0x09, 0x48, 0x48, 0x00, 0x00, - 0x28, 0x89, 0x05, 0x10, 0x00, 0x00, - 0x62, 0x22, 0xaa, 0x22, 0x00, 0x00, - 0x31, 0x10, 0x44, 0x44, 0x00, 0x00, - 0x58, 0x00, 0x22, 0x22, 0x00, 0x00, - 0x01, 0x13, 0x00, 0x8a, 0x00, 0x00, - 0x88, 0x20, 0x40, 0x34, 0x00, 0x00, - 0x44, 0x02, 0x10, 0xd0, 0x00, 0x00, - 0x29, 0x04, 0x45, 0x08, 0x00, 0x00, - 0x82, 0xa0, 0x90, 0x12, 0x00, 0x00, - 0x0a, 0x1a, 0x0e, 0x02, 0x00, 0x00, - 0x11, 0xe1, 0x28, 0x40, 0x00, 0x00, - 0x84, 0x05, 0x04, 0x0c, 0x00, 0x00, - 0x86, 0x40, 0xc0, 0x90, 0x00, 0x00, - 0x00, 0x87, 0x13, 0x00, 0x00, 0x00, - 0x44, 0x48, 0x01, 0x1c, 0x00, 0x00, - 0x10, 0x98, 0x30, 0x44, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_3[18] = - { - 0xac, 0x93, 0x5a, 0x5a, 0x00, 0x00, - 0x55, 0x4a, 0xec, 0x6c, 0x00, 0x00, - 0x43, 0x36, 0x4d, 0xb6, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_30[180] = - { - 0x62, 0x22, 0xaa, 0x22, 0x00, 0x00, - 0x31, 0x10, 0x44, 0x44, 0x00, 0x00, - 0x58, 0x00, 0x22, 0x22, 0x00, 0x00, - 0x01, 0x13, 0x00, 0x8a, 0x00, 0x00, - 0x88, 0x20, 0x40, 0x34, 0x00, 0x00, - 0x44, 0x02, 0x10, 0xd0, 0x00, 0x00, - 0x29, 0x04, 0x45, 0x08, 0x00, 0x00, - 0x82, 0xa0, 0x90, 0x12, 0x00, 0x00, - 0x0a, 0x1a, 0x0e, 0x02, 0x00, 0x00, - 0x11, 0xe1, 0x28, 0x40, 0x00, 0x00, - 0x84, 0x05, 0x04, 0x0c, 0x00, 0x00, - 0x86, 0x40, 0xc0, 0x90, 0x00, 0x00, - 0x00, 0x87, 0x13, 0x00, 0x00, 0x00, - 0x44, 0x48, 0x01, 0x1c, 0x00, 0x00, - 0x10, 0x98, 0x30, 0x44, 0x00, 0x00, - 0x40, 0x82, 0x8a, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x44, 0x14, 0x00, 0x00, - 0x88, 0x13, 0x09, 0xa0, 0x00, 0x00, - 0xc0, 0x10, 0x19, 0x14, 0x00, 0x00, - 0x80, 0xa0, 0x30, 0x0c, 0x00, 0x00, - 0x01, 0x22, 0x60, 0x06, 0x00, 0x00, - 0x40, 0x2c, 0xc2, 0x10, 0x00, 0x00, - 0x22, 0x02, 0x80, 0x22, 0x00, 0x00, - 0x90, 0x04, 0x20, 0x58, 0x00, 0x00, - 0x12, 0x40, 0x12, 0xc0, 0x00, 0x00, - 0x5d, 0x00, 0x01, 0x28, 0x00, 0x00, - 0x20, 0x54, 0xa4, 0x80, 0x00, 0x00, - 0x86, 0x09, 0x48, 0x48, 0x00, 0x00, - 0x28, 0x89, 0x05, 0x10, 0x00, 0x00, - 0xe1, 0x4f, 0xe0, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_31[186] = - { - 0x62, 0x22, 0xaa, 0x22, 0x00, 0x00, - 0x31, 0x10, 0x44, 0x44, 0x00, 0x00, - 0x58, 0x00, 0x22, 0x22, 0x00, 0x00, - 0x01, 0x13, 0x00, 0x8a, 0x00, 0x00, - 0x88, 0x20, 0x40, 0x34, 0x00, 0x00, - 0x44, 0x02, 0x10, 0xd0, 0x00, 0x00, - 0x29, 0x04, 0x45, 0x08, 0x00, 0x00, - 0x82, 0xa0, 0x90, 0x12, 0x00, 0x00, - 0x0a, 0x1a, 0x0e, 0x02, 0x00, 0x00, - 0x11, 0xe1, 0x28, 0x40, 0x00, 0x00, - 0x84, 0x05, 0x04, 0x0c, 0x00, 0x00, - 0x86, 0x40, 0xc0, 0x90, 0x00, 0x00, - 0x00, 0x87, 0x13, 0x00, 0x00, 0x00, - 0x44, 0x48, 0x01, 0x1c, 0x00, 0x00, - 0x10, 0x98, 0x30, 0x44, 0x00, 0x00, - 0x62, 0x23, 0x48, 0x20, 0x00, 0x00, - 0x31, 0x10, 0x02, 0x54, 0x00, 0x00, - 0x58, 0x00, 0x0c, 0x84, 0x00, 0x00, - 0x01, 0x12, 0x10, 0xd0, 0x00, 0x00, - 0x88, 0x21, 0x03, 0x20, 0x00, 0x00, - 0x44, 0x02, 0x01, 0xe0, 0x00, 0x00, - 0x29, 0x04, 0xa0, 0x0a, 0x00, 0x00, - 0x82, 0xa0, 0x40, 0xa2, 0x00, 0x00, - 0x0a, 0x1a, 0x86, 0x10, 0x00, 0x00, - 0x11, 0xe0, 0xd1, 0x00, 0x00, 0x00, - 0x84, 0x05, 0x00, 0x16, 0x00, 0x00, - 0x86, 0x40, 0x20, 0x98, 0x00, 0x00, - 0x00, 0x86, 0x24, 0x60, 0x00, 0x00, - 0x44, 0x48, 0x81, 0x0a, 0x00, 0x00, - 0x10, 0x98, 0x1c, 0x08, 0x00, 0x00, - 0x87, 0x74, 0x30, 0x24, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_4[24] = - { - 0x25, 0xaa, 0xaa, 0xaa, 0x00, 0x00, - 0x95, 0x55, 0x55, 0x54, 0x00, 0x00, - 0x1a, 0x6a, 0x6a, 0x6a, 0x00, 0x00, - 0x43, 0xd5, 0x95, 0x94, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_5[30] = - { - 0x64, 0xa2, 0xaa, 0xaa, 0x00, 0x00, - 0x25, 0x54, 0x54, 0x54, 0x00, 0x00, - 0x49, 0x68, 0x48, 0x4a, 0x00, 0x00, - 0x53, 0x91, 0x09, 0x90, 0x00, 0x00, - 0x8e, 0x30, 0x21, 0x6c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_6[36] = - { - 0x62, 0x8a, 0xa2, 0xa2, 0x00, 0x00, - 0x15, 0x54, 0x14, 0x54, 0x00, 0x00, - 0x4c, 0x47, 0x44, 0x2a, 0x00, 0x00, - 0x52, 0x95, 0x08, 0x94, 0x00, 0x00, - 0x23, 0x64, 0x61, 0x24, 0x00, 0x00, - 0x8a, 0x58, 0x09, 0x58, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_7[42] = - { - 0x62, 0xa2, 0x8a, 0xa2, 0x00, 0x00, - 0xb1, 0x14, 0x44, 0x54, 0x00, 0x00, - 0x18, 0x6b, 0x22, 0x22, 0x00, 0x00, - 0x44, 0xd4, 0x5c, 0x10, 0x00, 0x00, - 0x13, 0x64, 0x90, 0x68, 0x00, 0x00, - 0x49, 0x1b, 0x20, 0x52, 0x00, 0x00, - 0x86, 0x8c, 0x13, 0x0c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_8[48] = - { - 0x90, 0x22, 0x40, 0xa8, 0x00, 0x00, - 0x09, 0x50, 0x31, 0x10, 0x00, 0x00, - 0x00, 0x6b, 0x08, 0x0e, 0x00, 0x00, - 0x20, 0x34, 0xc0, 0x90, 0x00, 0x00, - 0x14, 0x44, 0x25, 0x04, 0x00, 0x00, - 0xc2, 0x11, 0x02, 0x82, 0x00, 0x00, - 0x00, 0xc6, 0x80, 0xc4, 0x00, 0x00, - 0x65, 0x80, 0x2c, 0x60, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask31_9[54] = - { - 0x62, 0x22, 0xaa, 0xa2, 0x00, 0x00, - 0x24, 0x44, 0x44, 0x54, 0x00, 0x00, - 0xc0, 0x50, 0x0b, 0x0a, 0x00, 0x00, - 0x03, 0x0c, 0x12, 0x94, 0x00, 0x00, - 0x16, 0x29, 0x08, 0x64, 0x00, 0x00, - 0x89, 0x01, 0x80, 0x1a, 0x00, 0x00, - 0x82, 0x90, 0x41, 0x4c, 0x00, 0x00, - 0x08, 0xa4, 0x34, 0x12, 0x00, 0x00, - 0x90, 0x48, 0x88, 0xc8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_10[60] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x10, 0xa2, 0x10, 0xa2, 0x00, 0x00, - 0x01, 0x25, 0x01, 0x25, 0x00, 0x00, - 0x0b, 0x42, 0x0b, 0x42, 0x00, 0x00, - 0xd8, 0x20, 0xd8, 0x20, 0x00, 0x00, - 0x82, 0x8c, 0x82, 0x8c, 0x00, 0x00, - 0x24, 0x4a, 0x24, 0x4a, 0x00, 0x00, - 0x38, 0x18, 0x38, 0x18, 0x00, 0x00, - 0x2a, 0x25, 0x2a, 0x25, 0x00, 0x00, - 0x84, 0x92, 0x84, 0x92, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_11[66] = - { - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x22, 0x2a, 0x22, 0x00, 0x00, - 0x31, 0x11, 0x31, 0x11, 0x00, 0x00, - 0x83, 0x42, 0x83, 0x42, 0x00, 0x00, - 0x06, 0x98, 0x06, 0x98, 0x00, 0x00, - 0x40, 0xe1, 0x40, 0xe1, 0x00, 0x00, - 0x2c, 0x44, 0x2c, 0x44, 0x00, 0x00, - 0xd8, 0x28, 0xd8, 0x28, 0x00, 0x00, - 0x92, 0x81, 0x92, 0x81, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0x68, 0x0c, 0x68, 0x0c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_12[72] = - { - 0x84, 0x31, 0x84, 0x31, 0x00, 0x00, - 0x18, 0xa2, 0x18, 0xa2, 0x00, 0x00, - 0x4e, 0x01, 0x4e, 0x01, 0x00, 0x00, - 0x44, 0xc8, 0x44, 0xc8, 0x00, 0x00, - 0x0e, 0x90, 0x0e, 0x90, 0x00, 0x00, - 0x20, 0xcc, 0x20, 0xcc, 0x00, 0x00, - 0x93, 0x40, 0x93, 0x40, 0x00, 0x00, - 0x2d, 0x10, 0x2d, 0x10, 0x00, 0x00, - 0x31, 0x44, 0x31, 0x44, 0x00, 0x00, - 0xc0, 0x23, 0xc0, 0x23, 0x00, 0x00, - 0x11, 0x25, 0x11, 0x25, 0x00, 0x00, - 0xe8, 0x80, 0xe8, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_13[78] = - { - 0x45, 0x15, 0x45, 0x15, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x96, 0x0c, 0x96, 0x0c, 0x00, 0x00, - 0x0c, 0x50, 0x0c, 0x50, 0x00, 0x00, - 0x62, 0x04, 0x62, 0x04, 0x00, 0x00, - 0x49, 0x06, 0x49, 0x06, 0x00, 0x00, - 0x11, 0x82, 0x11, 0x82, 0x00, 0x00, - 0x12, 0x38, 0x12, 0x38, 0x00, 0x00, - 0x40, 0x71, 0x40, 0x71, 0x00, 0x00, - 0xa8, 0x8a, 0xa8, 0x8a, 0x00, 0x00, - 0x08, 0xa1, 0x08, 0xa1, 0x00, 0x00, - 0xa0, 0xc0, 0xa0, 0xc0, 0x00, 0x00, - 0xc5, 0x10, 0xc5, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_14[84] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x0a, 0x22, 0x0a, 0x00, 0x00, - 0x84, 0xd0, 0x84, 0xd0, 0x00, 0x00, - 0x0c, 0x8a, 0x0c, 0x8a, 0x00, 0x00, - 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, - 0x30, 0x03, 0x30, 0x03, 0x00, 0x00, - 0x61, 0x08, 0x61, 0x08, 0x00, 0x00, - 0x40, 0x11, 0x40, 0x11, 0x00, 0x00, - 0x10, 0x2c, 0x10, 0x2c, 0x00, 0x00, - 0x09, 0x60, 0x09, 0x60, 0x00, 0x00, - 0x00, 0x94, 0x00, 0x94, 0x00, 0x00, - 0x52, 0x40, 0x52, 0x40, 0x00, 0x00, - 0xa4, 0x24, 0xa4, 0x24, 0x00, 0x00, - 0x82, 0x88, 0x82, 0x88, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_15[90] = - { - 0x55, 0x11, 0x55, 0x11, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, - 0x80, 0x45, 0x80, 0x45, 0x00, 0x00, - 0x20, 0x1a, 0x20, 0x1a, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x22, 0x84, 0x22, 0x84, 0x00, 0x00, - 0x48, 0x09, 0x48, 0x09, 0x00, 0x00, - 0x07, 0x01, 0x07, 0x01, 0x00, 0x00, - 0x94, 0x20, 0x94, 0x20, 0x00, 0x00, - 0x82, 0x06, 0x82, 0x06, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x89, 0x80, 0x89, 0x80, 0x00, 0x00, - 0x00, 0x8e, 0x00, 0x8e, 0x00, 0x00, - 0x18, 0x22, 0x18, 0x22, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_16[96] = - { - 0xa4, 0x10, 0xa4, 0x10, 0x00, 0x00, - 0x01, 0x2a, 0x01, 0x2a, 0x00, 0x00, - 0x06, 0x42, 0x06, 0x42, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x81, 0x90, 0x81, 0x90, 0x00, 0x00, - 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x00, - 0x50, 0x05, 0x50, 0x05, 0x00, 0x00, - 0x20, 0x51, 0x20, 0x51, 0x00, 0x00, - 0x43, 0x08, 0x43, 0x08, 0x00, 0x00, - 0x68, 0x80, 0x68, 0x80, 0x00, 0x00, - 0x80, 0x0b, 0x80, 0x0b, 0x00, 0x00, - 0x10, 0x4c, 0x10, 0x4c, 0x00, 0x00, - 0x12, 0x30, 0x12, 0x30, 0x00, 0x00, - 0x40, 0x85, 0x40, 0x85, 0x00, 0x00, - 0x0e, 0x04, 0x0e, 0x04, 0x00, 0x00, - 0x18, 0x12, 0x18, 0x12, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_17[102] = - { - 0x20, 0x54, 0x20, 0x54, 0x00, 0x00, - 0x18, 0x88, 0x18, 0x88, 0x00, 0x00, - 0x84, 0x07, 0x84, 0x07, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x12, 0x82, 0x12, 0x82, 0x00, 0x00, - 0x81, 0x41, 0x81, 0x41, 0x00, 0x00, - 0x40, 0x62, 0x40, 0x62, 0x00, 0x00, - 0x16, 0x30, 0x16, 0x30, 0x00, 0x00, - 0x55, 0x51, 0x55, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, - 0x09, 0x4a, 0x09, 0x4a, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x00, - 0x20, 0xa6, 0x20, 0xa6, 0x00, 0x00, - 0x1a, 0x09, 0x1a, 0x09, 0x00, 0x00, - 0x44, 0x64, 0x44, 0x64, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_18[108] = - { - 0x55, 0x51, 0x55, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, - 0x09, 0x4a, 0x09, 0x4a, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x00, - 0x20, 0xa6, 0x20, 0xa6, 0x00, 0x00, - 0x1a, 0x09, 0x1a, 0x09, 0x00, 0x00, - 0x44, 0x64, 0x44, 0x64, 0x00, 0x00, - 0x20, 0x54, 0x20, 0x54, 0x00, 0x00, - 0x18, 0x88, 0x18, 0x88, 0x00, 0x00, - 0x84, 0x07, 0x84, 0x07, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x12, 0x82, 0x12, 0x82, 0x00, 0x00, - 0x81, 0x41, 0x81, 0x41, 0x00, 0x00, - 0x40, 0x62, 0x40, 0x62, 0x00, 0x00, - 0x16, 0x30, 0x16, 0x30, 0x00, 0x00, - 0x1e, 0xb2, 0xd8, 0x53, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_19[114] = - { - 0x55, 0x51, 0x55, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, - 0x09, 0x4a, 0x09, 0x4a, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x00, - 0x20, 0xa6, 0x20, 0xa6, 0x00, 0x00, - 0x1a, 0x09, 0x1a, 0x09, 0x00, 0x00, - 0x44, 0x64, 0x44, 0x64, 0x00, 0x00, - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x10, 0xa2, 0x10, 0xa2, 0x00, 0x00, - 0x01, 0x25, 0x01, 0x25, 0x00, 0x00, - 0x0b, 0x42, 0x0b, 0x42, 0x00, 0x00, - 0xd8, 0x20, 0xd8, 0x20, 0x00, 0x00, - 0x82, 0x8c, 0x82, 0x8c, 0x00, 0x00, - 0x24, 0x4a, 0x24, 0x4a, 0x00, 0x00, - 0x38, 0x18, 0x38, 0x18, 0x00, 0x00, - 0x2a, 0x25, 0x2a, 0x25, 0x00, 0x00, - 0x84, 0x92, 0x84, 0x92, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_2[12] = - { - 0xae, 0xae, 0xae, 0xae, 0x00, 0x00, - 0x79, 0x79, 0x79, 0x79, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_20[120] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x10, 0xa2, 0x10, 0xa2, 0x00, 0x00, - 0x01, 0x25, 0x01, 0x25, 0x00, 0x00, - 0x0b, 0x42, 0x0b, 0x42, 0x00, 0x00, - 0xd8, 0x20, 0xd8, 0x20, 0x00, 0x00, - 0x82, 0x8c, 0x82, 0x8c, 0x00, 0x00, - 0x24, 0x4a, 0x24, 0x4a, 0x00, 0x00, - 0x38, 0x18, 0x38, 0x18, 0x00, 0x00, - 0x2a, 0x25, 0x2a, 0x25, 0x00, 0x00, - 0x84, 0x92, 0x84, 0x92, 0x00, 0x00, - 0x55, 0x51, 0x55, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, - 0x09, 0x4a, 0x09, 0x4a, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x00, - 0x20, 0xa6, 0x20, 0xa6, 0x00, 0x00, - 0x1a, 0x09, 0x1a, 0x09, 0x00, 0x00, - 0x44, 0x64, 0x44, 0x64, 0x00, 0x00, - 0x96, 0xd3, 0xf6, 0xac, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_21[126] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x10, 0xa2, 0x10, 0xa2, 0x00, 0x00, - 0x01, 0x25, 0x01, 0x25, 0x00, 0x00, - 0x0b, 0x42, 0x0b, 0x42, 0x00, 0x00, - 0xd8, 0x20, 0xd8, 0x20, 0x00, 0x00, - 0x82, 0x8c, 0x82, 0x8c, 0x00, 0x00, - 0x24, 0x4a, 0x24, 0x4a, 0x00, 0x00, - 0x38, 0x18, 0x38, 0x18, 0x00, 0x00, - 0x2a, 0x25, 0x2a, 0x25, 0x00, 0x00, - 0x84, 0x92, 0x84, 0x92, 0x00, 0x00, - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x22, 0x2a, 0x22, 0x00, 0x00, - 0x31, 0x11, 0x31, 0x11, 0x00, 0x00, - 0x83, 0x42, 0x83, 0x42, 0x00, 0x00, - 0x06, 0x98, 0x06, 0x98, 0x00, 0x00, - 0x40, 0xe1, 0x40, 0xe1, 0x00, 0x00, - 0x2c, 0x44, 0x2c, 0x44, 0x00, 0x00, - 0xd8, 0x28, 0xd8, 0x28, 0x00, 0x00, - 0x92, 0x81, 0x92, 0x81, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0x68, 0x0c, 0x68, 0x0c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_22[132] = - { - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x22, 0x2a, 0x22, 0x00, 0x00, - 0x31, 0x11, 0x31, 0x11, 0x00, 0x00, - 0x83, 0x42, 0x83, 0x42, 0x00, 0x00, - 0x06, 0x98, 0x06, 0x98, 0x00, 0x00, - 0x40, 0xe1, 0x40, 0xe1, 0x00, 0x00, - 0x2c, 0x44, 0x2c, 0x44, 0x00, 0x00, - 0xd8, 0x28, 0xd8, 0x28, 0x00, 0x00, - 0x92, 0x81, 0x92, 0x81, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0x68, 0x0c, 0x68, 0x0c, 0x00, 0x00, - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x10, 0xa2, 0x10, 0xa2, 0x00, 0x00, - 0x01, 0x25, 0x01, 0x25, 0x00, 0x00, - 0x0b, 0x42, 0x0b, 0x42, 0x00, 0x00, - 0xd8, 0x20, 0xd8, 0x20, 0x00, 0x00, - 0x82, 0x8c, 0x82, 0x8c, 0x00, 0x00, - 0x24, 0x4a, 0x24, 0x4a, 0x00, 0x00, - 0x38, 0x18, 0x38, 0x18, 0x00, 0x00, - 0x2a, 0x25, 0x2a, 0x25, 0x00, 0x00, - 0x84, 0x92, 0x84, 0x92, 0x00, 0x00, - 0xeb, 0xb2, 0x22, 0x89, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_23[138] = - { - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x22, 0x2a, 0x22, 0x00, 0x00, - 0x31, 0x11, 0x31, 0x11, 0x00, 0x00, - 0x83, 0x42, 0x83, 0x42, 0x00, 0x00, - 0x06, 0x98, 0x06, 0x98, 0x00, 0x00, - 0x40, 0xe1, 0x40, 0xe1, 0x00, 0x00, - 0x2c, 0x44, 0x2c, 0x44, 0x00, 0x00, - 0xd8, 0x28, 0xd8, 0x28, 0x00, 0x00, - 0x92, 0x81, 0x92, 0x81, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0x68, 0x0c, 0x68, 0x0c, 0x00, 0x00, - 0x84, 0x31, 0x84, 0x31, 0x00, 0x00, - 0x18, 0xa2, 0x18, 0xa2, 0x00, 0x00, - 0x4e, 0x01, 0x4e, 0x01, 0x00, 0x00, - 0x44, 0xc8, 0x44, 0xc8, 0x00, 0x00, - 0x0e, 0x90, 0x0e, 0x90, 0x00, 0x00, - 0x20, 0xcc, 0x20, 0xcc, 0x00, 0x00, - 0x93, 0x40, 0x93, 0x40, 0x00, 0x00, - 0x2d, 0x10, 0x2d, 0x10, 0x00, 0x00, - 0x31, 0x44, 0x31, 0x44, 0x00, 0x00, - 0xc0, 0x23, 0xc0, 0x23, 0x00, 0x00, - 0x11, 0x25, 0x11, 0x25, 0x00, 0x00, - 0xe8, 0x80, 0xe8, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_24[144] = - { - 0x84, 0x31, 0x84, 0x31, 0x00, 0x00, - 0x18, 0xa2, 0x18, 0xa2, 0x00, 0x00, - 0x4e, 0x01, 0x4e, 0x01, 0x00, 0x00, - 0x44, 0xc8, 0x44, 0xc8, 0x00, 0x00, - 0x0e, 0x90, 0x0e, 0x90, 0x00, 0x00, - 0x20, 0xcc, 0x20, 0xcc, 0x00, 0x00, - 0x93, 0x40, 0x93, 0x40, 0x00, 0x00, - 0x2d, 0x10, 0x2d, 0x10, 0x00, 0x00, - 0x31, 0x44, 0x31, 0x44, 0x00, 0x00, - 0xc0, 0x23, 0xc0, 0x23, 0x00, 0x00, - 0x11, 0x25, 0x11, 0x25, 0x00, 0x00, - 0xe8, 0x80, 0xe8, 0x80, 0x00, 0x00, - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x22, 0x2a, 0x22, 0x00, 0x00, - 0x31, 0x11, 0x31, 0x11, 0x00, 0x00, - 0x83, 0x42, 0x83, 0x42, 0x00, 0x00, - 0x06, 0x98, 0x06, 0x98, 0x00, 0x00, - 0x40, 0xe1, 0x40, 0xe1, 0x00, 0x00, - 0x2c, 0x44, 0x2c, 0x44, 0x00, 0x00, - 0xd8, 0x28, 0xd8, 0x28, 0x00, 0x00, - 0x92, 0x81, 0x92, 0x81, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0x68, 0x0c, 0x68, 0x0c, 0x00, 0x00, - 0xf3, 0x5a, 0x2f, 0x5d, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_25[150] = - { - 0x84, 0x31, 0x84, 0x31, 0x00, 0x00, - 0x18, 0xa2, 0x18, 0xa2, 0x00, 0x00, - 0x4e, 0x01, 0x4e, 0x01, 0x00, 0x00, - 0x44, 0xc8, 0x44, 0xc8, 0x00, 0x00, - 0x0e, 0x90, 0x0e, 0x90, 0x00, 0x00, - 0x20, 0xcc, 0x20, 0xcc, 0x00, 0x00, - 0x93, 0x40, 0x93, 0x40, 0x00, 0x00, - 0x2d, 0x10, 0x2d, 0x10, 0x00, 0x00, - 0x31, 0x44, 0x31, 0x44, 0x00, 0x00, - 0xc0, 0x23, 0xc0, 0x23, 0x00, 0x00, - 0x11, 0x25, 0x11, 0x25, 0x00, 0x00, - 0xe8, 0x80, 0xe8, 0x80, 0x00, 0x00, - 0x45, 0x15, 0x45, 0x15, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x96, 0x0c, 0x96, 0x0c, 0x00, 0x00, - 0x0c, 0x50, 0x0c, 0x50, 0x00, 0x00, - 0x62, 0x04, 0x62, 0x04, 0x00, 0x00, - 0x49, 0x06, 0x49, 0x06, 0x00, 0x00, - 0x11, 0x82, 0x11, 0x82, 0x00, 0x00, - 0x12, 0x38, 0x12, 0x38, 0x00, 0x00, - 0x40, 0x71, 0x40, 0x71, 0x00, 0x00, - 0xa8, 0x8a, 0xa8, 0x8a, 0x00, 0x00, - 0x08, 0xa1, 0x08, 0xa1, 0x00, 0x00, - 0xa0, 0xc0, 0xa0, 0xc0, 0x00, 0x00, - 0xc5, 0x10, 0xc5, 0x10, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_26[156] = - { - 0x45, 0x15, 0x45, 0x15, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x96, 0x0c, 0x96, 0x0c, 0x00, 0x00, - 0x0c, 0x50, 0x0c, 0x50, 0x00, 0x00, - 0x62, 0x04, 0x62, 0x04, 0x00, 0x00, - 0x49, 0x06, 0x49, 0x06, 0x00, 0x00, - 0x11, 0x82, 0x11, 0x82, 0x00, 0x00, - 0x12, 0x38, 0x12, 0x38, 0x00, 0x00, - 0x40, 0x71, 0x40, 0x71, 0x00, 0x00, - 0xa8, 0x8a, 0xa8, 0x8a, 0x00, 0x00, - 0x08, 0xa1, 0x08, 0xa1, 0x00, 0x00, - 0xa0, 0xc0, 0xa0, 0xc0, 0x00, 0x00, - 0xc5, 0x10, 0xc5, 0x10, 0x00, 0x00, - 0x84, 0x31, 0x84, 0x31, 0x00, 0x00, - 0x18, 0xa2, 0x18, 0xa2, 0x00, 0x00, - 0x4e, 0x01, 0x4e, 0x01, 0x00, 0x00, - 0x44, 0xc8, 0x44, 0xc8, 0x00, 0x00, - 0x0e, 0x90, 0x0e, 0x90, 0x00, 0x00, - 0x20, 0xcc, 0x20, 0xcc, 0x00, 0x00, - 0x93, 0x40, 0x93, 0x40, 0x00, 0x00, - 0x2d, 0x10, 0x2d, 0x10, 0x00, 0x00, - 0x31, 0x44, 0x31, 0x44, 0x00, 0x00, - 0xc0, 0x23, 0xc0, 0x23, 0x00, 0x00, - 0x11, 0x25, 0x11, 0x25, 0x00, 0x00, - 0xe8, 0x80, 0xe8, 0x80, 0x00, 0x00, - 0x52, 0x15, 0x62, 0x0a, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_27[162] = - { - 0x45, 0x15, 0x45, 0x15, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x96, 0x0c, 0x96, 0x0c, 0x00, 0x00, - 0x0c, 0x50, 0x0c, 0x50, 0x00, 0x00, - 0x62, 0x04, 0x62, 0x04, 0x00, 0x00, - 0x49, 0x06, 0x49, 0x06, 0x00, 0x00, - 0x11, 0x82, 0x11, 0x82, 0x00, 0x00, - 0x12, 0x38, 0x12, 0x38, 0x00, 0x00, - 0x40, 0x71, 0x40, 0x71, 0x00, 0x00, - 0xa8, 0x8a, 0xa8, 0x8a, 0x00, 0x00, - 0x08, 0xa1, 0x08, 0xa1, 0x00, 0x00, - 0xa0, 0xc0, 0xa0, 0xc0, 0x00, 0x00, - 0xc5, 0x10, 0xc5, 0x10, 0x00, 0x00, - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x0a, 0x22, 0x0a, 0x00, 0x00, - 0x84, 0xd0, 0x84, 0xd0, 0x00, 0x00, - 0x0c, 0x8a, 0x0c, 0x8a, 0x00, 0x00, - 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, - 0x30, 0x03, 0x30, 0x03, 0x00, 0x00, - 0x61, 0x08, 0x61, 0x08, 0x00, 0x00, - 0x40, 0x11, 0x40, 0x11, 0x00, 0x00, - 0x10, 0x2c, 0x10, 0x2c, 0x00, 0x00, - 0x09, 0x60, 0x09, 0x60, 0x00, 0x00, - 0x00, 0x94, 0x00, 0x94, 0x00, 0x00, - 0x52, 0x40, 0x52, 0x40, 0x00, 0x00, - 0xa4, 0x24, 0xa4, 0x24, 0x00, 0x00, - 0x82, 0x88, 0x82, 0x88, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_28[168] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x0a, 0x22, 0x0a, 0x00, 0x00, - 0x84, 0xd0, 0x84, 0xd0, 0x00, 0x00, - 0x0c, 0x8a, 0x0c, 0x8a, 0x00, 0x00, - 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, - 0x30, 0x03, 0x30, 0x03, 0x00, 0x00, - 0x61, 0x08, 0x61, 0x08, 0x00, 0x00, - 0x40, 0x11, 0x40, 0x11, 0x00, 0x00, - 0x10, 0x2c, 0x10, 0x2c, 0x00, 0x00, - 0x09, 0x60, 0x09, 0x60, 0x00, 0x00, - 0x00, 0x94, 0x00, 0x94, 0x00, 0x00, - 0x52, 0x40, 0x52, 0x40, 0x00, 0x00, - 0xa4, 0x24, 0xa4, 0x24, 0x00, 0x00, - 0x82, 0x88, 0x82, 0x88, 0x00, 0x00, - 0x45, 0x15, 0x45, 0x15, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x96, 0x0c, 0x96, 0x0c, 0x00, 0x00, - 0x0c, 0x50, 0x0c, 0x50, 0x00, 0x00, - 0x62, 0x04, 0x62, 0x04, 0x00, 0x00, - 0x49, 0x06, 0x49, 0x06, 0x00, 0x00, - 0x11, 0x82, 0x11, 0x82, 0x00, 0x00, - 0x12, 0x38, 0x12, 0x38, 0x00, 0x00, - 0x40, 0x71, 0x40, 0x71, 0x00, 0x00, - 0xa8, 0x8a, 0xa8, 0x8a, 0x00, 0x00, - 0x08, 0xa1, 0x08, 0xa1, 0x00, 0x00, - 0xa0, 0xc0, 0xa0, 0xc0, 0x00, 0x00, - 0xc5, 0x10, 0xc5, 0x10, 0x00, 0x00, - 0x7f, 0xe2, 0xbc, 0x01, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_29[174] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x0a, 0x22, 0x0a, 0x00, 0x00, - 0x84, 0xd0, 0x84, 0xd0, 0x00, 0x00, - 0x0c, 0x8a, 0x0c, 0x8a, 0x00, 0x00, - 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, - 0x30, 0x03, 0x30, 0x03, 0x00, 0x00, - 0x61, 0x08, 0x61, 0x08, 0x00, 0x00, - 0x40, 0x11, 0x40, 0x11, 0x00, 0x00, - 0x10, 0x2c, 0x10, 0x2c, 0x00, 0x00, - 0x09, 0x60, 0x09, 0x60, 0x00, 0x00, - 0x00, 0x94, 0x00, 0x94, 0x00, 0x00, - 0x52, 0x40, 0x52, 0x40, 0x00, 0x00, - 0xa4, 0x24, 0xa4, 0x24, 0x00, 0x00, - 0x82, 0x88, 0x82, 0x88, 0x00, 0x00, - 0x55, 0x11, 0x55, 0x11, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, - 0x80, 0x45, 0x80, 0x45, 0x00, 0x00, - 0x20, 0x1a, 0x20, 0x1a, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x22, 0x84, 0x22, 0x84, 0x00, 0x00, - 0x48, 0x09, 0x48, 0x09, 0x00, 0x00, - 0x07, 0x01, 0x07, 0x01, 0x00, 0x00, - 0x94, 0x20, 0x94, 0x20, 0x00, 0x00, - 0x82, 0x06, 0x82, 0x06, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x89, 0x80, 0x89, 0x80, 0x00, 0x00, - 0x00, 0x8e, 0x00, 0x8e, 0x00, 0x00, - 0x18, 0x22, 0x18, 0x22, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_3[18] = - { - 0xad, 0x2d, 0xad, 0x2d, 0x00, 0x00, - 0x76, 0x36, 0x76, 0x36, 0x00, 0x00, - 0x26, 0xdb, 0x26, 0xdb, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_30[180] = - { - 0x55, 0x11, 0x55, 0x11, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, - 0x80, 0x45, 0x80, 0x45, 0x00, 0x00, - 0x20, 0x1a, 0x20, 0x1a, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x22, 0x84, 0x22, 0x84, 0x00, 0x00, - 0x48, 0x09, 0x48, 0x09, 0x00, 0x00, - 0x07, 0x01, 0x07, 0x01, 0x00, 0x00, - 0x94, 0x20, 0x94, 0x20, 0x00, 0x00, - 0x82, 0x06, 0x82, 0x06, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x89, 0x80, 0x89, 0x80, 0x00, 0x00, - 0x00, 0x8e, 0x00, 0x8e, 0x00, 0x00, - 0x18, 0x22, 0x18, 0x22, 0x00, 0x00, - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x0a, 0x22, 0x0a, 0x00, 0x00, - 0x84, 0xd0, 0x84, 0xd0, 0x00, 0x00, - 0x0c, 0x8a, 0x0c, 0x8a, 0x00, 0x00, - 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, - 0x30, 0x03, 0x30, 0x03, 0x00, 0x00, - 0x61, 0x08, 0x61, 0x08, 0x00, 0x00, - 0x40, 0x11, 0x40, 0x11, 0x00, 0x00, - 0x10, 0x2c, 0x10, 0x2c, 0x00, 0x00, - 0x09, 0x60, 0x09, 0x60, 0x00, 0x00, - 0x00, 0x94, 0x00, 0x94, 0x00, 0x00, - 0x52, 0x40, 0x52, 0x40, 0x00, 0x00, - 0xa4, 0x24, 0xa4, 0x24, 0x00, 0x00, - 0x82, 0x88, 0x82, 0x88, 0x00, 0x00, - 0x1e, 0x27, 0xe2, 0xd8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_31[186] = - { - 0x55, 0x11, 0x55, 0x11, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, - 0x80, 0x45, 0x80, 0x45, 0x00, 0x00, - 0x20, 0x1a, 0x20, 0x1a, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x22, 0x84, 0x22, 0x84, 0x00, 0x00, - 0x48, 0x09, 0x48, 0x09, 0x00, 0x00, - 0x07, 0x01, 0x07, 0x01, 0x00, 0x00, - 0x94, 0x20, 0x94, 0x20, 0x00, 0x00, - 0x82, 0x06, 0x82, 0x06, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x89, 0x80, 0x89, 0x80, 0x00, 0x00, - 0x00, 0x8e, 0x00, 0x8e, 0x00, 0x00, - 0x18, 0x22, 0x18, 0x22, 0x00, 0x00, - 0xa4, 0x10, 0xa4, 0x10, 0x00, 0x00, - 0x01, 0x2a, 0x01, 0x2a, 0x00, 0x00, - 0x06, 0x42, 0x06, 0x42, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x81, 0x90, 0x81, 0x90, 0x00, 0x00, - 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x00, - 0x50, 0x05, 0x50, 0x05, 0x00, 0x00, - 0x20, 0x51, 0x20, 0x51, 0x00, 0x00, - 0x43, 0x08, 0x43, 0x08, 0x00, 0x00, - 0x68, 0x80, 0x68, 0x80, 0x00, 0x00, - 0x80, 0x0b, 0x80, 0x0b, 0x00, 0x00, - 0x10, 0x4c, 0x10, 0x4c, 0x00, 0x00, - 0x12, 0x30, 0x12, 0x30, 0x00, 0x00, - 0x40, 0x85, 0x40, 0x85, 0x00, 0x00, - 0x0e, 0x04, 0x0e, 0x04, 0x00, 0x00, - 0x18, 0x12, 0x18, 0x12, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_32[192] = - { - 0xa4, 0x10, 0xa4, 0x10, 0x00, 0x00, - 0x01, 0x2a, 0x01, 0x2a, 0x00, 0x00, - 0x06, 0x42, 0x06, 0x42, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x81, 0x90, 0x81, 0x90, 0x00, 0x00, - 0x00, 0xf0, 0x00, 0xf0, 0x00, 0x00, - 0x50, 0x05, 0x50, 0x05, 0x00, 0x00, - 0x20, 0x51, 0x20, 0x51, 0x00, 0x00, - 0x43, 0x08, 0x43, 0x08, 0x00, 0x00, - 0x68, 0x80, 0x68, 0x80, 0x00, 0x00, - 0x80, 0x0b, 0x80, 0x0b, 0x00, 0x00, - 0x10, 0x4c, 0x10, 0x4c, 0x00, 0x00, - 0x12, 0x30, 0x12, 0x30, 0x00, 0x00, - 0x40, 0x85, 0x40, 0x85, 0x00, 0x00, - 0x0e, 0x04, 0x0e, 0x04, 0x00, 0x00, - 0x18, 0x12, 0x18, 0x12, 0x00, 0x00, - 0x55, 0x11, 0x55, 0x11, 0x00, 0x00, - 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, - 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, - 0x80, 0x45, 0x80, 0x45, 0x00, 0x00, - 0x20, 0x1a, 0x20, 0x1a, 0x00, 0x00, - 0x08, 0x68, 0x08, 0x68, 0x00, 0x00, - 0x22, 0x84, 0x22, 0x84, 0x00, 0x00, - 0x48, 0x09, 0x48, 0x09, 0x00, 0x00, - 0x07, 0x01, 0x07, 0x01, 0x00, 0x00, - 0x94, 0x20, 0x94, 0x20, 0x00, 0x00, - 0x82, 0x06, 0x82, 0x06, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x89, 0x80, 0x89, 0x80, 0x00, 0x00, - 0x00, 0x8e, 0x00, 0x8e, 0x00, 0x00, - 0x18, 0x22, 0x18, 0x22, 0x00, 0x00, - 0x60, 0xc4, 0x02, 0x02, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_4[24] = - { - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, - 0x35, 0x35, 0x35, 0x35, 0x00, 0x00, - 0xca, 0xca, 0xca, 0xca, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_5[30] = - { - 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, - 0x2a, 0x2a, 0x2a, 0x2a, 0x00, 0x00, - 0x24, 0x25, 0x24, 0x25, 0x00, 0x00, - 0x84, 0xc8, 0x84, 0xc8, 0x00, 0x00, - 0x10, 0xb6, 0x10, 0xb6, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_6[36] = - { - 0x51, 0x51, 0x51, 0x51, 0x00, 0x00, - 0x0a, 0x2a, 0x0a, 0x2a, 0x00, 0x00, - 0xa2, 0x15, 0xa2, 0x15, 0x00, 0x00, - 0x84, 0x4a, 0x84, 0x4a, 0x00, 0x00, - 0x30, 0x92, 0x30, 0x92, 0x00, 0x00, - 0x04, 0xac, 0x04, 0xac, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_7[42] = - { - 0x45, 0x51, 0x45, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x91, 0x11, 0x91, 0x11, 0x00, 0x00, - 0x2e, 0x08, 0x2e, 0x08, 0x00, 0x00, - 0x48, 0x34, 0x48, 0x34, 0x00, 0x00, - 0x90, 0x29, 0x90, 0x29, 0x00, 0x00, - 0x09, 0x86, 0x09, 0x86, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_8[48] = - { - 0x20, 0x54, 0x20, 0x54, 0x00, 0x00, - 0x18, 0x88, 0x18, 0x88, 0x00, 0x00, - 0x84, 0x07, 0x84, 0x07, 0x00, 0x00, - 0x60, 0x48, 0x60, 0x48, 0x00, 0x00, - 0x12, 0x82, 0x12, 0x82, 0x00, 0x00, - 0x81, 0x41, 0x81, 0x41, 0x00, 0x00, - 0x40, 0x62, 0x40, 0x62, 0x00, 0x00, - 0x16, 0x30, 0x16, 0x30, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask32_9[54] = - { - 0x55, 0x51, 0x55, 0x51, 0x00, 0x00, - 0x22, 0x2a, 0x22, 0x2a, 0x00, 0x00, - 0x05, 0x85, 0x05, 0x85, 0x00, 0x00, - 0x09, 0x4a, 0x09, 0x4a, 0x00, 0x00, - 0x84, 0x32, 0x84, 0x32, 0x00, 0x00, - 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x00, - 0x20, 0xa6, 0x20, 0xa6, 0x00, 0x00, - 0x1a, 0x09, 0x1a, 0x09, 0x00, 0x00, - 0x44, 0x64, 0x44, 0x64, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_10[60] = - { - 0x45, 0x51, 0x55, 0x8c, 0x80, 0x00, - 0x10, 0xa2, 0xaa, 0x27, 0x00, 0x00, - 0x01, 0x25, 0xa5, 0x32, 0x80, 0x00, - 0x0b, 0x42, 0x62, 0x61, 0x80, 0x00, - 0xd8, 0x20, 0x3c, 0x5c, 0x00, 0x00, - 0x82, 0x8c, 0x8e, 0xcc, 0x00, 0x00, - 0x24, 0x4a, 0x6a, 0x2b, 0x00, 0x00, - 0x38, 0x18, 0x36, 0x32, 0x80, 0x00, - 0x2a, 0x25, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x92, 0xc8, 0x02, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_11[66] = - { - 0x55, 0x55, 0x55, 0x8c, 0x80, 0x00, - 0x2a, 0x22, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x11, 0xa5, 0x32, 0x80, 0x00, - 0x83, 0x42, 0x62, 0x61, 0x80, 0x00, - 0x06, 0x98, 0x3c, 0x5c, 0x00, 0x00, - 0x40, 0xe1, 0x51, 0x84, 0x80, 0x00, - 0x2c, 0x44, 0xa2, 0x27, 0x00, 0x00, - 0xd8, 0x28, 0x95, 0x51, 0x80, 0x00, - 0x92, 0x81, 0x4a, 0x1a, 0x00, 0x00, - 0x84, 0x32, 0x30, 0x68, 0x00, 0x00, - 0x68, 0x0c, 0x2c, 0x89, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_12[72] = - { - 0x84, 0x31, 0x51, 0x84, 0x80, 0x00, - 0x18, 0xa2, 0xa2, 0x27, 0x00, 0x00, - 0x4e, 0x01, 0x95, 0x51, 0x80, 0x00, - 0x44, 0xc8, 0x4a, 0x1a, 0x00, 0x00, - 0x0e, 0x90, 0x30, 0x68, 0x00, 0x00, - 0x20, 0xcc, 0x2c, 0x89, 0x00, 0x00, - 0x93, 0x40, 0x55, 0x8c, 0x80, 0x00, - 0x2d, 0x10, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x44, 0xa5, 0x32, 0x80, 0x00, - 0xc0, 0x23, 0x62, 0x61, 0x80, 0x00, - 0x11, 0x25, 0x3c, 0x5c, 0x00, 0x00, - 0xe8, 0x80, 0x51, 0x35, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_13[78] = - { - 0x45, 0x15, 0x51, 0x84, 0x80, 0x00, - 0x22, 0x22, 0xa2, 0x27, 0x00, 0x00, - 0x96, 0x0c, 0x95, 0x51, 0x80, 0x00, - 0x0c, 0x50, 0x4a, 0x1a, 0x00, 0x00, - 0x62, 0x04, 0x30, 0x68, 0x00, 0x00, - 0x49, 0x06, 0x2c, 0x89, 0x00, 0x00, - 0x11, 0x82, 0x15, 0x8c, 0x00, 0x00, - 0x12, 0x38, 0x8a, 0x47, 0x00, 0x00, - 0x40, 0x71, 0x25, 0x81, 0x80, 0x00, - 0xa8, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x08, 0xa1, 0x58, 0x58, 0x00, 0x00, - 0xa0, 0xc0, 0x0e, 0x28, 0x80, 0x00, - 0xc5, 0x10, 0x83, 0x34, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_14[84] = - { - 0x45, 0x51, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x0a, 0x8a, 0x47, 0x00, 0x00, - 0x84, 0xd0, 0x25, 0x81, 0x80, 0x00, - 0x0c, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x18, 0x06, 0x58, 0x58, 0x00, 0x00, - 0x30, 0x03, 0x0e, 0x28, 0x80, 0x00, - 0x61, 0x08, 0x83, 0x34, 0x00, 0x00, - 0x40, 0x11, 0x51, 0x84, 0x80, 0x00, - 0x10, 0x2c, 0xa2, 0x27, 0x00, 0x00, - 0x09, 0x60, 0x95, 0x51, 0x80, 0x00, - 0x00, 0x94, 0x4a, 0x1a, 0x00, 0x00, - 0x52, 0x40, 0x30, 0x68, 0x00, 0x00, - 0xa4, 0x24, 0x2c, 0x89, 0x00, 0x00, - 0x82, 0x88, 0xb0, 0xde, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_15[90] = - { - 0x55, 0x11, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x22, 0x8a, 0x47, 0x00, 0x00, - 0x11, 0x11, 0x25, 0x81, 0x80, 0x00, - 0x80, 0x45, 0x62, 0x12, 0x80, 0x00, - 0x20, 0x1a, 0x58, 0x58, 0x00, 0x00, - 0x08, 0x68, 0x0e, 0x28, 0x80, 0x00, - 0x22, 0x84, 0x83, 0x34, 0x00, 0x00, - 0x48, 0x09, 0x25, 0x2c, 0x00, 0x00, - 0x07, 0x01, 0x8a, 0x91, 0x00, 0x00, - 0x94, 0x20, 0x91, 0xc0, 0x80, 0x00, - 0x82, 0x06, 0x68, 0x06, 0x80, 0x00, - 0x60, 0x48, 0x32, 0xc8, 0x00, 0x00, - 0x89, 0x80, 0x43, 0x45, 0x00, 0x00, - 0x00, 0x8e, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x22, 0x1c, 0xa2, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_16[96] = - { - 0xa4, 0x10, 0x25, 0x2c, 0x00, 0x00, - 0x01, 0x2a, 0x8a, 0x91, 0x00, 0x00, - 0x06, 0x42, 0x91, 0xc0, 0x80, 0x00, - 0x08, 0x68, 0x68, 0x06, 0x80, 0x00, - 0x81, 0x90, 0x32, 0xc8, 0x00, 0x00, - 0x00, 0xf0, 0x43, 0x45, 0x00, 0x00, - 0x50, 0x05, 0xc4, 0x30, 0x80, 0x00, - 0x20, 0x51, 0x1c, 0xa2, 0x00, 0x00, - 0x43, 0x08, 0x15, 0x8c, 0x00, 0x00, - 0x68, 0x80, 0x8a, 0x47, 0x00, 0x00, - 0x80, 0x0b, 0x25, 0x81, 0x80, 0x00, - 0x10, 0x4c, 0x62, 0x12, 0x80, 0x00, - 0x12, 0x30, 0x58, 0x58, 0x00, 0x00, - 0x40, 0x85, 0x0e, 0x28, 0x80, 0x00, - 0x0e, 0x04, 0x83, 0x34, 0x00, 0x00, - 0x18, 0x12, 0x0a, 0x1c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_17[102] = - { - 0x20, 0x54, 0x64, 0x16, 0x00, 0x00, - 0x18, 0x88, 0xa2, 0xc2, 0x00, 0x00, - 0x84, 0x07, 0x51, 0x60, 0x80, 0x00, - 0x60, 0x48, 0x4a, 0x85, 0x00, 0x00, - 0x12, 0x82, 0x38, 0x4c, 0x00, 0x00, - 0x81, 0x41, 0x89, 0x29, 0x00, 0x00, - 0x40, 0x62, 0x07, 0x11, 0x80, 0x00, - 0x16, 0x30, 0x94, 0xb0, 0x00, 0x00, - 0x55, 0x51, 0x8e, 0xcc, 0x00, 0x00, - 0x22, 0x2a, 0x6a, 0x2b, 0x00, 0x00, - 0x05, 0x85, 0x36, 0x32, 0x80, 0x00, - 0x09, 0x4a, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x32, 0x55, 0x8c, 0x80, 0x00, - 0xc0, 0x0d, 0xaa, 0x27, 0x00, 0x00, - 0x20, 0xa6, 0xa5, 0x32, 0x80, 0x00, - 0x1a, 0x09, 0x62, 0x61, 0x80, 0x00, - 0x44, 0x64, 0x3c, 0x5c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_18[108] = - { - 0x55, 0x51, 0x8e, 0xcc, 0x00, 0x00, - 0x22, 0x2a, 0x6a, 0x2b, 0x00, 0x00, - 0x05, 0x85, 0x36, 0x32, 0x80, 0x00, - 0x09, 0x4a, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x32, 0x55, 0x8c, 0x80, 0x00, - 0xc0, 0x0d, 0xaa, 0x27, 0x00, 0x00, - 0x20, 0xa6, 0xa5, 0x32, 0x80, 0x00, - 0x1a, 0x09, 0x62, 0x61, 0x80, 0x00, - 0x44, 0x64, 0x3c, 0x5c, 0x00, 0x00, - 0x20, 0x54, 0x64, 0x16, 0x00, 0x00, - 0x18, 0x88, 0xa2, 0xc2, 0x00, 0x00, - 0x84, 0x07, 0x51, 0x60, 0x80, 0x00, - 0x60, 0x48, 0x4a, 0x85, 0x00, 0x00, - 0x12, 0x82, 0x38, 0x4c, 0x00, 0x00, - 0x81, 0x41, 0x89, 0x29, 0x00, 0x00, - 0x40, 0x62, 0x07, 0x11, 0x80, 0x00, - 0x16, 0x30, 0x94, 0xb0, 0x00, 0x00, - 0x89, 0x53, 0x03, 0xad, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_19[114] = - { - 0x55, 0x51, 0x8e, 0xcc, 0x00, 0x00, - 0x22, 0x2a, 0x6a, 0x2b, 0x00, 0x00, - 0x05, 0x85, 0x36, 0x32, 0x80, 0x00, - 0x09, 0x4a, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x32, 0x55, 0x8c, 0x80, 0x00, - 0xc0, 0x0d, 0xaa, 0x27, 0x00, 0x00, - 0x20, 0xa6, 0xa5, 0x32, 0x80, 0x00, - 0x1a, 0x09, 0x62, 0x61, 0x80, 0x00, - 0x44, 0x64, 0x3c, 0x5c, 0x00, 0x00, - 0x45, 0x51, 0x55, 0x8c, 0x80, 0x00, - 0x10, 0xa2, 0xaa, 0x27, 0x00, 0x00, - 0x01, 0x25, 0xa5, 0x32, 0x80, 0x00, - 0x0b, 0x42, 0x62, 0x61, 0x80, 0x00, - 0xd8, 0x20, 0x3c, 0x5c, 0x00, 0x00, - 0x82, 0x8c, 0x8e, 0xcc, 0x00, 0x00, - 0x24, 0x4a, 0x6a, 0x2b, 0x00, 0x00, - 0x38, 0x18, 0x36, 0x32, 0x80, 0x00, - 0x2a, 0x25, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x92, 0xc8, 0x02, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_2[12] = - { - 0xae, 0xae, 0xce, 0xce, 0x00, 0x00, - 0x79, 0x79, 0xb9, 0x39, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_20[120] = - { - 0x45, 0x51, 0x55, 0x8c, 0x80, 0x00, - 0x10, 0xa2, 0xaa, 0x27, 0x00, 0x00, - 0x01, 0x25, 0xa5, 0x32, 0x80, 0x00, - 0x0b, 0x42, 0x62, 0x61, 0x80, 0x00, - 0xd8, 0x20, 0x3c, 0x5c, 0x00, 0x00, - 0x82, 0x8c, 0x8e, 0xcc, 0x00, 0x00, - 0x24, 0x4a, 0x6a, 0x2b, 0x00, 0x00, - 0x38, 0x18, 0x36, 0x32, 0x80, 0x00, - 0x2a, 0x25, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x92, 0xc8, 0x02, 0x80, 0x00, - 0x55, 0x51, 0x8e, 0xcc, 0x00, 0x00, - 0x22, 0x2a, 0x6a, 0x2b, 0x00, 0x00, - 0x05, 0x85, 0x36, 0x32, 0x80, 0x00, - 0x09, 0x4a, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x32, 0x55, 0x8c, 0x80, 0x00, - 0xc0, 0x0d, 0xaa, 0x27, 0x00, 0x00, - 0x20, 0xa6, 0xa5, 0x32, 0x80, 0x00, - 0x1a, 0x09, 0x62, 0x61, 0x80, 0x00, - 0x44, 0x64, 0x3c, 0x5c, 0x00, 0x00, - 0x73, 0x5f, 0x5b, 0x0e, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_21[126] = - { - 0x45, 0x51, 0x55, 0x8c, 0x80, 0x00, - 0x10, 0xa2, 0xaa, 0x27, 0x00, 0x00, - 0x01, 0x25, 0xa5, 0x32, 0x80, 0x00, - 0x0b, 0x42, 0x62, 0x61, 0x80, 0x00, - 0xd8, 0x20, 0x3c, 0x5c, 0x00, 0x00, - 0x82, 0x8c, 0x8e, 0xcc, 0x00, 0x00, - 0x24, 0x4a, 0x6a, 0x2b, 0x00, 0x00, - 0x38, 0x18, 0x36, 0x32, 0x80, 0x00, - 0x2a, 0x25, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x92, 0xc8, 0x02, 0x80, 0x00, - 0x55, 0x55, 0x55, 0x8c, 0x80, 0x00, - 0x2a, 0x22, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x11, 0xa5, 0x32, 0x80, 0x00, - 0x83, 0x42, 0x62, 0x61, 0x80, 0x00, - 0x06, 0x98, 0x3c, 0x5c, 0x00, 0x00, - 0x40, 0xe1, 0x51, 0x84, 0x80, 0x00, - 0x2c, 0x44, 0xa2, 0x27, 0x00, 0x00, - 0xd8, 0x28, 0x95, 0x51, 0x80, 0x00, - 0x92, 0x81, 0x4a, 0x1a, 0x00, 0x00, - 0x84, 0x32, 0x30, 0x68, 0x00, 0x00, - 0x68, 0x0c, 0x2c, 0x89, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_22[132] = - { - 0x55, 0x55, 0x55, 0x8c, 0x80, 0x00, - 0x2a, 0x22, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x11, 0xa5, 0x32, 0x80, 0x00, - 0x83, 0x42, 0x62, 0x61, 0x80, 0x00, - 0x06, 0x98, 0x3c, 0x5c, 0x00, 0x00, - 0x40, 0xe1, 0x51, 0x84, 0x80, 0x00, - 0x2c, 0x44, 0xa2, 0x27, 0x00, 0x00, - 0xd8, 0x28, 0x95, 0x51, 0x80, 0x00, - 0x92, 0x81, 0x4a, 0x1a, 0x00, 0x00, - 0x84, 0x32, 0x30, 0x68, 0x00, 0x00, - 0x68, 0x0c, 0x2c, 0x89, 0x00, 0x00, - 0x45, 0x51, 0x55, 0x8c, 0x80, 0x00, - 0x10, 0xa2, 0xaa, 0x27, 0x00, 0x00, - 0x01, 0x25, 0xa5, 0x32, 0x80, 0x00, - 0x0b, 0x42, 0x62, 0x61, 0x80, 0x00, - 0xd8, 0x20, 0x3c, 0x5c, 0x00, 0x00, - 0x82, 0x8c, 0x8e, 0xcc, 0x00, 0x00, - 0x24, 0x4a, 0x6a, 0x2b, 0x00, 0x00, - 0x38, 0x18, 0x36, 0x32, 0x80, 0x00, - 0x2a, 0x25, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x92, 0xc8, 0x02, 0x80, 0x00, - 0xcc, 0xe3, 0x42, 0x6b, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_23[138] = - { - 0x55, 0x55, 0x55, 0x8c, 0x80, 0x00, - 0x2a, 0x22, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x11, 0xa5, 0x32, 0x80, 0x00, - 0x83, 0x42, 0x62, 0x61, 0x80, 0x00, - 0x06, 0x98, 0x3c, 0x5c, 0x00, 0x00, - 0x40, 0xe1, 0x51, 0x84, 0x80, 0x00, - 0x2c, 0x44, 0xa2, 0x27, 0x00, 0x00, - 0xd8, 0x28, 0x95, 0x51, 0x80, 0x00, - 0x92, 0x81, 0x4a, 0x1a, 0x00, 0x00, - 0x84, 0x32, 0x30, 0x68, 0x00, 0x00, - 0x68, 0x0c, 0x2c, 0x89, 0x00, 0x00, - 0x84, 0x31, 0x51, 0x84, 0x80, 0x00, - 0x18, 0xa2, 0xa2, 0x27, 0x00, 0x00, - 0x4e, 0x01, 0x95, 0x51, 0x80, 0x00, - 0x44, 0xc8, 0x4a, 0x1a, 0x00, 0x00, - 0x0e, 0x90, 0x30, 0x68, 0x00, 0x00, - 0x20, 0xcc, 0x2c, 0x89, 0x00, 0x00, - 0x93, 0x40, 0x55, 0x8c, 0x80, 0x00, - 0x2d, 0x10, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x44, 0xa5, 0x32, 0x80, 0x00, - 0xc0, 0x23, 0x62, 0x61, 0x80, 0x00, - 0x11, 0x25, 0x3c, 0x5c, 0x00, 0x00, - 0xe8, 0x80, 0x51, 0x35, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_24[144] = - { - 0x84, 0x31, 0x51, 0x84, 0x80, 0x00, - 0x18, 0xa2, 0xa2, 0x27, 0x00, 0x00, - 0x4e, 0x01, 0x95, 0x51, 0x80, 0x00, - 0x44, 0xc8, 0x4a, 0x1a, 0x00, 0x00, - 0x0e, 0x90, 0x30, 0x68, 0x00, 0x00, - 0x20, 0xcc, 0x2c, 0x89, 0x00, 0x00, - 0x93, 0x40, 0x55, 0x8c, 0x80, 0x00, - 0x2d, 0x10, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x44, 0xa5, 0x32, 0x80, 0x00, - 0xc0, 0x23, 0x62, 0x61, 0x80, 0x00, - 0x11, 0x25, 0x3c, 0x5c, 0x00, 0x00, - 0xe8, 0x80, 0x51, 0x35, 0x00, 0x00, - 0x55, 0x55, 0x55, 0x8c, 0x80, 0x00, - 0x2a, 0x22, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x11, 0xa5, 0x32, 0x80, 0x00, - 0x83, 0x42, 0x62, 0x61, 0x80, 0x00, - 0x06, 0x98, 0x3c, 0x5c, 0x00, 0x00, - 0x40, 0xe1, 0x51, 0x84, 0x80, 0x00, - 0x2c, 0x44, 0xa2, 0x27, 0x00, 0x00, - 0xd8, 0x28, 0x95, 0x51, 0x80, 0x00, - 0x92, 0x81, 0x4a, 0x1a, 0x00, 0x00, - 0x84, 0x32, 0x30, 0x68, 0x00, 0x00, - 0x68, 0x0c, 0x2c, 0x89, 0x00, 0x00, - 0xdc, 0x4e, 0xfc, 0x70, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_25[150] = - { - 0x84, 0x31, 0x51, 0x84, 0x80, 0x00, - 0x18, 0xa2, 0xa2, 0x27, 0x00, 0x00, - 0x4e, 0x01, 0x95, 0x51, 0x80, 0x00, - 0x44, 0xc8, 0x4a, 0x1a, 0x00, 0x00, - 0x0e, 0x90, 0x30, 0x68, 0x00, 0x00, - 0x20, 0xcc, 0x2c, 0x89, 0x00, 0x00, - 0x93, 0x40, 0x55, 0x8c, 0x80, 0x00, - 0x2d, 0x10, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x44, 0xa5, 0x32, 0x80, 0x00, - 0xc0, 0x23, 0x62, 0x61, 0x80, 0x00, - 0x11, 0x25, 0x3c, 0x5c, 0x00, 0x00, - 0xe8, 0x80, 0x51, 0x35, 0x00, 0x00, - 0x45, 0x15, 0x51, 0x84, 0x80, 0x00, - 0x22, 0x22, 0xa2, 0x27, 0x00, 0x00, - 0x96, 0x0c, 0x95, 0x51, 0x80, 0x00, - 0x0c, 0x50, 0x4a, 0x1a, 0x00, 0x00, - 0x62, 0x04, 0x30, 0x68, 0x00, 0x00, - 0x49, 0x06, 0x2c, 0x89, 0x00, 0x00, - 0x11, 0x82, 0x15, 0x8c, 0x00, 0x00, - 0x12, 0x38, 0x8a, 0x47, 0x00, 0x00, - 0x40, 0x71, 0x25, 0x81, 0x80, 0x00, - 0xa8, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x08, 0xa1, 0x58, 0x58, 0x00, 0x00, - 0xa0, 0xc0, 0x0e, 0x28, 0x80, 0x00, - 0xc5, 0x10, 0x83, 0x34, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_26[156] = - { - 0x45, 0x15, 0x51, 0x84, 0x80, 0x00, - 0x22, 0x22, 0xa2, 0x27, 0x00, 0x00, - 0x96, 0x0c, 0x95, 0x51, 0x80, 0x00, - 0x0c, 0x50, 0x4a, 0x1a, 0x00, 0x00, - 0x62, 0x04, 0x30, 0x68, 0x00, 0x00, - 0x49, 0x06, 0x2c, 0x89, 0x00, 0x00, - 0x11, 0x82, 0x15, 0x8c, 0x00, 0x00, - 0x12, 0x38, 0x8a, 0x47, 0x00, 0x00, - 0x40, 0x71, 0x25, 0x81, 0x80, 0x00, - 0xa8, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x08, 0xa1, 0x58, 0x58, 0x00, 0x00, - 0xa0, 0xc0, 0x0e, 0x28, 0x80, 0x00, - 0xc5, 0x10, 0x83, 0x34, 0x00, 0x00, - 0x84, 0x31, 0x51, 0x84, 0x80, 0x00, - 0x18, 0xa2, 0xa2, 0x27, 0x00, 0x00, - 0x4e, 0x01, 0x95, 0x51, 0x80, 0x00, - 0x44, 0xc8, 0x4a, 0x1a, 0x00, 0x00, - 0x0e, 0x90, 0x30, 0x68, 0x00, 0x00, - 0x20, 0xcc, 0x2c, 0x89, 0x00, 0x00, - 0x93, 0x40, 0x55, 0x8c, 0x80, 0x00, - 0x2d, 0x10, 0xaa, 0x27, 0x00, 0x00, - 0x31, 0x44, 0xa5, 0x32, 0x80, 0x00, - 0xc0, 0x23, 0x62, 0x61, 0x80, 0x00, - 0x11, 0x25, 0x3c, 0x5c, 0x00, 0x00, - 0xe8, 0x80, 0x51, 0x35, 0x00, 0x00, - 0xa4, 0xa4, 0xfc, 0x91, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_27[162] = - { - 0x45, 0x15, 0x51, 0x84, 0x80, 0x00, - 0x22, 0x22, 0xa2, 0x27, 0x00, 0x00, - 0x96, 0x0c, 0x95, 0x51, 0x80, 0x00, - 0x0c, 0x50, 0x4a, 0x1a, 0x00, 0x00, - 0x62, 0x04, 0x30, 0x68, 0x00, 0x00, - 0x49, 0x06, 0x2c, 0x89, 0x00, 0x00, - 0x11, 0x82, 0x15, 0x8c, 0x00, 0x00, - 0x12, 0x38, 0x8a, 0x47, 0x00, 0x00, - 0x40, 0x71, 0x25, 0x81, 0x80, 0x00, - 0xa8, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x08, 0xa1, 0x58, 0x58, 0x00, 0x00, - 0xa0, 0xc0, 0x0e, 0x28, 0x80, 0x00, - 0xc5, 0x10, 0x83, 0x34, 0x00, 0x00, - 0x45, 0x51, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x0a, 0x8a, 0x47, 0x00, 0x00, - 0x84, 0xd0, 0x25, 0x81, 0x80, 0x00, - 0x0c, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x18, 0x06, 0x58, 0x58, 0x00, 0x00, - 0x30, 0x03, 0x0e, 0x28, 0x80, 0x00, - 0x61, 0x08, 0x83, 0x34, 0x00, 0x00, - 0x40, 0x11, 0x51, 0x84, 0x80, 0x00, - 0x10, 0x2c, 0xa2, 0x27, 0x00, 0x00, - 0x09, 0x60, 0x95, 0x51, 0x80, 0x00, - 0x00, 0x94, 0x4a, 0x1a, 0x00, 0x00, - 0x52, 0x40, 0x30, 0x68, 0x00, 0x00, - 0xa4, 0x24, 0x2c, 0x89, 0x00, 0x00, - 0x82, 0x88, 0xb0, 0xde, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_28[168] = - { - 0x45, 0x51, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x0a, 0x8a, 0x47, 0x00, 0x00, - 0x84, 0xd0, 0x25, 0x81, 0x80, 0x00, - 0x0c, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x18, 0x06, 0x58, 0x58, 0x00, 0x00, - 0x30, 0x03, 0x0e, 0x28, 0x80, 0x00, - 0x61, 0x08, 0x83, 0x34, 0x00, 0x00, - 0x40, 0x11, 0x51, 0x84, 0x80, 0x00, - 0x10, 0x2c, 0xa2, 0x27, 0x00, 0x00, - 0x09, 0x60, 0x95, 0x51, 0x80, 0x00, - 0x00, 0x94, 0x4a, 0x1a, 0x00, 0x00, - 0x52, 0x40, 0x30, 0x68, 0x00, 0x00, - 0xa4, 0x24, 0x2c, 0x89, 0x00, 0x00, - 0x82, 0x88, 0xb0, 0xde, 0x80, 0x00, - 0x45, 0x15, 0x51, 0x84, 0x80, 0x00, - 0x22, 0x22, 0xa2, 0x27, 0x00, 0x00, - 0x96, 0x0c, 0x95, 0x51, 0x80, 0x00, - 0x0c, 0x50, 0x4a, 0x1a, 0x00, 0x00, - 0x62, 0x04, 0x30, 0x68, 0x00, 0x00, - 0x49, 0x06, 0x2c, 0x89, 0x00, 0x00, - 0x11, 0x82, 0x15, 0x8c, 0x00, 0x00, - 0x12, 0x38, 0x8a, 0x47, 0x00, 0x00, - 0x40, 0x71, 0x25, 0x81, 0x80, 0x00, - 0xa8, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x08, 0xa1, 0x58, 0x58, 0x00, 0x00, - 0xa0, 0xc0, 0x0e, 0x28, 0x80, 0x00, - 0xc5, 0x10, 0x83, 0x34, 0x00, 0x00, - 0x1b, 0xf4, 0xaa, 0xec, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_29[174] = - { - 0x45, 0x51, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x0a, 0x8a, 0x47, 0x00, 0x00, - 0x84, 0xd0, 0x25, 0x81, 0x80, 0x00, - 0x0c, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x18, 0x06, 0x58, 0x58, 0x00, 0x00, - 0x30, 0x03, 0x0e, 0x28, 0x80, 0x00, - 0x61, 0x08, 0x83, 0x34, 0x00, 0x00, - 0x40, 0x11, 0x51, 0x84, 0x80, 0x00, - 0x10, 0x2c, 0xa2, 0x27, 0x00, 0x00, - 0x09, 0x60, 0x95, 0x51, 0x80, 0x00, - 0x00, 0x94, 0x4a, 0x1a, 0x00, 0x00, - 0x52, 0x40, 0x30, 0x68, 0x00, 0x00, - 0xa4, 0x24, 0x2c, 0x89, 0x00, 0x00, - 0x82, 0x88, 0xb0, 0xde, 0x80, 0x00, - 0x55, 0x11, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x22, 0x8a, 0x47, 0x00, 0x00, - 0x11, 0x11, 0x25, 0x81, 0x80, 0x00, - 0x80, 0x45, 0x62, 0x12, 0x80, 0x00, - 0x20, 0x1a, 0x58, 0x58, 0x00, 0x00, - 0x08, 0x68, 0x0e, 0x28, 0x80, 0x00, - 0x22, 0x84, 0x83, 0x34, 0x00, 0x00, - 0x48, 0x09, 0x25, 0x2c, 0x00, 0x00, - 0x07, 0x01, 0x8a, 0x91, 0x00, 0x00, - 0x94, 0x20, 0x91, 0xc0, 0x80, 0x00, - 0x82, 0x06, 0x68, 0x06, 0x80, 0x00, - 0x60, 0x48, 0x32, 0xc8, 0x00, 0x00, - 0x89, 0x80, 0x43, 0x45, 0x00, 0x00, - 0x00, 0x8e, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x22, 0x1c, 0xa2, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_3[18] = - { - 0xad, 0x2d, 0xcd, 0xcc, 0x00, 0x00, - 0x76, 0x36, 0x97, 0x27, 0x00, 0x00, - 0x26, 0xdb, 0xb8, 0xd1, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_30[180] = - { - 0x55, 0x11, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x22, 0x8a, 0x47, 0x00, 0x00, - 0x11, 0x11, 0x25, 0x81, 0x80, 0x00, - 0x80, 0x45, 0x62, 0x12, 0x80, 0x00, - 0x20, 0x1a, 0x58, 0x58, 0x00, 0x00, - 0x08, 0x68, 0x0e, 0x28, 0x80, 0x00, - 0x22, 0x84, 0x83, 0x34, 0x00, 0x00, - 0x48, 0x09, 0x25, 0x2c, 0x00, 0x00, - 0x07, 0x01, 0x8a, 0x91, 0x00, 0x00, - 0x94, 0x20, 0x91, 0xc0, 0x80, 0x00, - 0x82, 0x06, 0x68, 0x06, 0x80, 0x00, - 0x60, 0x48, 0x32, 0xc8, 0x00, 0x00, - 0x89, 0x80, 0x43, 0x45, 0x00, 0x00, - 0x00, 0x8e, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x22, 0x1c, 0xa2, 0x00, 0x00, - 0x45, 0x51, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x0a, 0x8a, 0x47, 0x00, 0x00, - 0x84, 0xd0, 0x25, 0x81, 0x80, 0x00, - 0x0c, 0x8a, 0x62, 0x12, 0x80, 0x00, - 0x18, 0x06, 0x58, 0x58, 0x00, 0x00, - 0x30, 0x03, 0x0e, 0x28, 0x80, 0x00, - 0x61, 0x08, 0x83, 0x34, 0x00, 0x00, - 0x40, 0x11, 0x51, 0x84, 0x80, 0x00, - 0x10, 0x2c, 0xa2, 0x27, 0x00, 0x00, - 0x09, 0x60, 0x95, 0x51, 0x80, 0x00, - 0x00, 0x94, 0x4a, 0x1a, 0x00, 0x00, - 0x52, 0x40, 0x30, 0x68, 0x00, 0x00, - 0xa4, 0x24, 0x2c, 0x89, 0x00, 0x00, - 0x82, 0x88, 0xb0, 0xde, 0x80, 0x00, - 0x6d, 0xd2, 0x8c, 0x00, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_31[186] = - { - 0x55, 0x11, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x22, 0x8a, 0x47, 0x00, 0x00, - 0x11, 0x11, 0x25, 0x81, 0x80, 0x00, - 0x80, 0x45, 0x62, 0x12, 0x80, 0x00, - 0x20, 0x1a, 0x58, 0x58, 0x00, 0x00, - 0x08, 0x68, 0x0e, 0x28, 0x80, 0x00, - 0x22, 0x84, 0x83, 0x34, 0x00, 0x00, - 0x48, 0x09, 0x25, 0x2c, 0x00, 0x00, - 0x07, 0x01, 0x8a, 0x91, 0x00, 0x00, - 0x94, 0x20, 0x91, 0xc0, 0x80, 0x00, - 0x82, 0x06, 0x68, 0x06, 0x80, 0x00, - 0x60, 0x48, 0x32, 0xc8, 0x00, 0x00, - 0x89, 0x80, 0x43, 0x45, 0x00, 0x00, - 0x00, 0x8e, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x22, 0x1c, 0xa2, 0x00, 0x00, - 0xa4, 0x10, 0x25, 0x2c, 0x00, 0x00, - 0x01, 0x2a, 0x8a, 0x91, 0x00, 0x00, - 0x06, 0x42, 0x91, 0xc0, 0x80, 0x00, - 0x08, 0x68, 0x68, 0x06, 0x80, 0x00, - 0x81, 0x90, 0x32, 0xc8, 0x00, 0x00, - 0x00, 0xf0, 0x43, 0x45, 0x00, 0x00, - 0x50, 0x05, 0xc4, 0x30, 0x80, 0x00, - 0x20, 0x51, 0x1c, 0xa2, 0x00, 0x00, - 0x43, 0x08, 0x15, 0x8c, 0x00, 0x00, - 0x68, 0x80, 0x8a, 0x47, 0x00, 0x00, - 0x80, 0x0b, 0x25, 0x81, 0x80, 0x00, - 0x10, 0x4c, 0x62, 0x12, 0x80, 0x00, - 0x12, 0x30, 0x58, 0x58, 0x00, 0x00, - 0x40, 0x85, 0x0e, 0x28, 0x80, 0x00, - 0x0e, 0x04, 0x83, 0x34, 0x00, 0x00, - 0x18, 0x12, 0x0a, 0x1c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_32[192] = - { - 0xa4, 0x10, 0x25, 0x2c, 0x00, 0x00, - 0x01, 0x2a, 0x8a, 0x91, 0x00, 0x00, - 0x06, 0x42, 0x91, 0xc0, 0x80, 0x00, - 0x08, 0x68, 0x68, 0x06, 0x80, 0x00, - 0x81, 0x90, 0x32, 0xc8, 0x00, 0x00, - 0x00, 0xf0, 0x43, 0x45, 0x00, 0x00, - 0x50, 0x05, 0xc4, 0x30, 0x80, 0x00, - 0x20, 0x51, 0x1c, 0xa2, 0x00, 0x00, - 0x43, 0x08, 0x15, 0x8c, 0x00, 0x00, - 0x68, 0x80, 0x8a, 0x47, 0x00, 0x00, - 0x80, 0x0b, 0x25, 0x81, 0x80, 0x00, - 0x10, 0x4c, 0x62, 0x12, 0x80, 0x00, - 0x12, 0x30, 0x58, 0x58, 0x00, 0x00, - 0x40, 0x85, 0x0e, 0x28, 0x80, 0x00, - 0x0e, 0x04, 0x83, 0x34, 0x00, 0x00, - 0x18, 0x12, 0x0a, 0x1c, 0x00, 0x00, - 0x55, 0x11, 0x15, 0x8c, 0x00, 0x00, - 0x22, 0x22, 0x8a, 0x47, 0x00, 0x00, - 0x11, 0x11, 0x25, 0x81, 0x80, 0x00, - 0x80, 0x45, 0x62, 0x12, 0x80, 0x00, - 0x20, 0x1a, 0x58, 0x58, 0x00, 0x00, - 0x08, 0x68, 0x0e, 0x28, 0x80, 0x00, - 0x22, 0x84, 0x83, 0x34, 0x00, 0x00, - 0x48, 0x09, 0x25, 0x2c, 0x00, 0x00, - 0x07, 0x01, 0x8a, 0x91, 0x00, 0x00, - 0x94, 0x20, 0x91, 0xc0, 0x80, 0x00, - 0x82, 0x06, 0x68, 0x06, 0x80, 0x00, - 0x60, 0x48, 0x32, 0xc8, 0x00, 0x00, - 0x89, 0x80, 0x43, 0x45, 0x00, 0x00, - 0x00, 0x8e, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x22, 0x1c, 0xa2, 0x00, 0x00, - 0x73, 0x8e, 0x12, 0xca, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_33[198] = - { - 0xa4, 0x10, 0x25, 0x2c, 0x00, 0x00, - 0x01, 0x2a, 0x8a, 0x91, 0x00, 0x00, - 0x06, 0x42, 0x91, 0xc0, 0x80, 0x00, - 0x08, 0x68, 0x68, 0x06, 0x80, 0x00, - 0x81, 0x90, 0x32, 0xc8, 0x00, 0x00, - 0x00, 0xf0, 0x43, 0x45, 0x00, 0x00, - 0x50, 0x05, 0xc4, 0x30, 0x80, 0x00, - 0x20, 0x51, 0x1c, 0xa2, 0x00, 0x00, - 0x43, 0x08, 0x15, 0x8c, 0x00, 0x00, - 0x68, 0x80, 0x8a, 0x47, 0x00, 0x00, - 0x80, 0x0b, 0x25, 0x81, 0x80, 0x00, - 0x10, 0x4c, 0x62, 0x12, 0x80, 0x00, - 0x12, 0x30, 0x58, 0x58, 0x00, 0x00, - 0x40, 0x85, 0x0e, 0x28, 0x80, 0x00, - 0x0e, 0x04, 0x83, 0x34, 0x00, 0x00, - 0x18, 0x12, 0x0a, 0x1c, 0x00, 0x00, - 0xa4, 0x10, 0x25, 0x2c, 0x00, 0x00, - 0x01, 0x2a, 0x8a, 0x91, 0x00, 0x00, - 0x06, 0x42, 0x91, 0xc0, 0x80, 0x00, - 0x08, 0x68, 0x68, 0x06, 0x80, 0x00, - 0x81, 0x90, 0x32, 0xc8, 0x00, 0x00, - 0x00, 0xf0, 0x43, 0x45, 0x00, 0x00, - 0x50, 0x05, 0xc4, 0x30, 0x80, 0x00, - 0x20, 0x51, 0x1c, 0xa2, 0x00, 0x00, - 0x43, 0x08, 0x25, 0x4c, 0x00, 0x00, - 0x68, 0x80, 0x8a, 0x66, 0x00, 0x00, - 0x80, 0x0b, 0x91, 0x91, 0x00, 0x00, - 0x10, 0x4c, 0x68, 0x42, 0x80, 0x00, - 0x12, 0x30, 0x32, 0xa4, 0x00, 0x00, - 0x40, 0x85, 0x43, 0x13, 0x00, 0x00, - 0x0e, 0x04, 0xc4, 0x30, 0x80, 0x00, - 0x18, 0x12, 0x1c, 0x88, 0x80, 0x00, - 0xdb, 0x10, 0x3c, 0x09, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_4[24] = - { - 0x55, 0x55, 0xca, 0xec, 0x00, 0x00, - 0xaa, 0xaa, 0xa9, 0x67, 0x00, 0x00, - 0x35, 0x35, 0x3a, 0xb1, 0x80, 0x00, - 0xca, 0xca, 0x55, 0x5a, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask33_5[30] = - { - 0x55, 0x55, 0x55, 0x44, 0x80, 0x00, - 0x2a, 0x2a, 0x2a, 0x66, 0x00, 0x00, - 0x24, 0x25, 0x25, 0xa1, 0x80, 0x00, - 0x84, 0xc8, 0xe2, 0x12, 0x80, 0x00, - 0x10, 0xb6, 0x99, 0x98, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_6[36] = - { - 0x51, 0x51, 0xd1, 0x4c, 0x00, 0x00, - 0x0a, 0x2a, 0xa2, 0xc5, 0x00, 0x00, - 0xa2, 0x15, 0x95, 0x30, 0x80, 0x00, - 0x84, 0x4a, 0xca, 0x0a, 0x80, 0x00, - 0x30, 0x92, 0xa4, 0xaa, 0x00, 0x00, - 0x04, 0xac, 0x78, 0x15, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_7[42] = - { - 0x45, 0x51, 0x15, 0x44, 0x80, 0x00, - 0x22, 0x2a, 0x8a, 0x23, 0x00, 0x00, - 0x91, 0x11, 0x85, 0x91, 0x00, 0x00, - 0x2e, 0x08, 0x32, 0x0a, 0x80, 0x00, - 0x48, 0x34, 0x58, 0x34, 0x00, 0x00, - 0x90, 0x29, 0x2c, 0x0d, 0x00, 0x00, - 0x09, 0x86, 0x43, 0xc8, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_8[48] = - { - 0x20, 0x54, 0x64, 0x16, 0x00, 0x00, - 0x18, 0x88, 0xa2, 0xc2, 0x00, 0x00, - 0x84, 0x07, 0x51, 0x60, 0x80, 0x00, - 0x60, 0x48, 0x4a, 0x85, 0x00, 0x00, - 0x12, 0x82, 0x38, 0x4c, 0x00, 0x00, - 0x81, 0x41, 0x89, 0x29, 0x00, 0x00, - 0x40, 0x62, 0x07, 0x11, 0x80, 0x00, - 0x16, 0x30, 0x94, 0xb0, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask33_9[54] = - { - 0x55, 0x51, 0x8e, 0xcc, 0x00, 0x00, - 0x22, 0x2a, 0x6a, 0x2b, 0x00, 0x00, - 0x05, 0x85, 0x36, 0x32, 0x80, 0x00, - 0x09, 0x4a, 0xd1, 0x25, 0x80, 0x00, - 0x84, 0x32, 0x55, 0x8c, 0x80, 0x00, - 0xc0, 0x0d, 0xaa, 0x27, 0x00, 0x00, - 0x20, 0xa6, 0xa5, 0x32, 0x80, 0x00, - 0x1a, 0x09, 0x62, 0x61, 0x80, 0x00, - 0x44, 0x64, 0x3c, 0x5c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask34_10[60] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0xc8, 0x02, 0xe4, 0x01, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_11[66] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_12[72] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x35, 0x28, 0x9a, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_13[78] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_14[84] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0xb0, 0xde, 0xd8, 0x6f, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_15[90] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_16[96] = - { - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x0a, 0x1c, 0x05, 0x0e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_17[102] = - { - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x25, 0x4c, 0x12, 0xa6, 0x00, 0x00, - 0x8a, 0x66, 0x45, 0x33, 0x00, 0x00, - 0x91, 0x91, 0x48, 0xc8, 0x80, 0x00, - 0x68, 0x42, 0xb4, 0x21, 0x40, 0x00, - 0x32, 0xa4, 0x19, 0x52, 0x00, 0x00, - 0x43, 0x13, 0x21, 0x89, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0x88, 0x8e, 0x44, 0x40, 0x00, - 0x3c, 0x09, 0x1e, 0x04, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_18[108] = - { - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x64, 0x16, 0x32, 0x0b, 0x00, 0x00, - 0xa2, 0xc2, 0x51, 0x61, 0x00, 0x00, - 0x51, 0x60, 0xa8, 0xb0, 0x40, 0x00, - 0x4a, 0x85, 0x25, 0x42, 0x80, 0x00, - 0x38, 0x4c, 0x1c, 0x26, 0x00, 0x00, - 0x89, 0x29, 0x44, 0x94, 0x80, 0x00, - 0x07, 0x11, 0x83, 0x88, 0xc0, 0x00, - 0x94, 0xb0, 0x4a, 0x58, 0x00, 0x00, - 0x89, 0x70, 0xf3, 0xf7, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_19[114] = - { - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0xc8, 0x02, 0xe4, 0x01, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_2[12] = - { - 0xce, 0xce, 0x67, 0x67, 0x00, 0x00, - 0xb9, 0x39, 0xdc, 0x9c, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask34_20[120] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0xc8, 0x02, 0xe4, 0x01, 0x40, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x5d, 0xc5, 0xfe, 0xd8, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_21[126] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0xc8, 0x02, 0xe4, 0x01, 0x40, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_22[132] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0xc8, 0x02, 0xe4, 0x01, 0x40, 0x00, - 0x2a, 0xf7, 0x4f, 0xf5, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_23[138] = - { - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x35, 0x28, 0x9a, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_24[144] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x35, 0x28, 0x9a, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x4c, 0xb8, 0x04, 0x74, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask34_25[150] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x35, 0x28, 0x9a, 0x80, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_26[156] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00, - 0x51, 0x35, 0x28, 0x9a, 0x80, 0x00, - 0x95, 0x20, 0xe9, 0xef, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask34_27[162] = - { - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0xb0, 0xde, 0xd8, 0x6f, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_28[168] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0xb0, 0xde, 0xd8, 0x6f, 0x40, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x10, 0x6c, 0xff, 0x60, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_29[174] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0xb0, 0xde, 0xd8, 0x6f, 0x40, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_3[18] = - { - 0xcd, 0xcc, 0x66, 0xe6, 0x00, 0x00, - 0x97, 0x27, 0x4b, 0x93, 0x80, 0x00, - 0xb8, 0xd1, 0xdc, 0x68, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask34_30[180] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x51, 0x84, 0xa8, 0xc2, 0x40, 0x00, - 0xa2, 0x27, 0x51, 0x13, 0x80, 0x00, - 0x95, 0x51, 0xca, 0xa8, 0xc0, 0x00, - 0x4a, 0x1a, 0x25, 0x0d, 0x00, 0x00, - 0x30, 0x68, 0x18, 0x34, 0x00, 0x00, - 0x2c, 0x89, 0x16, 0x44, 0x80, 0x00, - 0xb0, 0xde, 0xd8, 0x6f, 0x40, 0x00, - 0x87, 0x93, 0x96, 0xc7, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_31[186] = - { - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x0a, 0x1c, 0x05, 0x0e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_32[192] = - { - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x0a, 0x1c, 0x05, 0x0e, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0xa6, 0x27, 0xa9, 0x4a, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_33[198] = - { - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x0a, 0x1c, 0x05, 0x0e, 0x00, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x25, 0x4c, 0x12, 0xa6, 0x00, 0x00, - 0x8a, 0x66, 0x45, 0x33, 0x00, 0x00, - 0x91, 0x91, 0x48, 0xc8, 0x80, 0x00, - 0x68, 0x42, 0xb4, 0x21, 0x40, 0x00, - 0x32, 0xa4, 0x19, 0x52, 0x00, 0x00, - 0x43, 0x13, 0x21, 0x89, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0x88, 0x8e, 0x44, 0x40, 0x00, - 0x3c, 0x09, 0x1e, 0x04, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_34[204] = - { - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x25, 0x4c, 0x12, 0xa6, 0x00, 0x00, - 0x8a, 0x66, 0x45, 0x33, 0x00, 0x00, - 0x91, 0x91, 0x48, 0xc8, 0x80, 0x00, - 0x68, 0x42, 0xb4, 0x21, 0x40, 0x00, - 0x32, 0xa4, 0x19, 0x52, 0x00, 0x00, - 0x43, 0x13, 0x21, 0x89, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0x88, 0x8e, 0x44, 0x40, 0x00, - 0x3c, 0x09, 0x1e, 0x04, 0x80, 0x00, - 0x25, 0x2c, 0x12, 0x96, 0x00, 0x00, - 0x8a, 0x91, 0x45, 0x48, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xe0, 0x40, 0x00, - 0x68, 0x06, 0xb4, 0x03, 0x40, 0x00, - 0x32, 0xc8, 0x19, 0x64, 0x00, 0x00, - 0x43, 0x45, 0x21, 0xa2, 0x80, 0x00, - 0xc4, 0x30, 0xe2, 0x18, 0x40, 0x00, - 0x1c, 0xa2, 0x0e, 0x51, 0x00, 0x00, - 0x15, 0x8c, 0x0a, 0xc6, 0x00, 0x00, - 0x8a, 0x47, 0x45, 0x23, 0x80, 0x00, - 0x25, 0x81, 0x92, 0xc0, 0xc0, 0x00, - 0x62, 0x12, 0xb1, 0x09, 0x40, 0x00, - 0x58, 0x58, 0x2c, 0x2c, 0x00, 0x00, - 0x0e, 0x28, 0x87, 0x14, 0x40, 0x00, - 0x83, 0x34, 0x41, 0x9a, 0x00, 0x00, - 0x0a, 0x1c, 0x05, 0x0e, 0x00, 0x00, - 0x30, 0x3c, 0xb3, 0xe6, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_4[24] = - { - 0xca, 0xec, 0x65, 0x76, 0x00, 0x00, - 0xa9, 0x67, 0x54, 0xb3, 0x80, 0x00, - 0x3a, 0xb1, 0x9d, 0x58, 0xc0, 0x00, - 0x55, 0x5a, 0xaa, 0xad, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask34_5[30] = - { - 0x55, 0x44, 0xaa, 0xa2, 0x40, 0x00, - 0x2a, 0x66, 0x15, 0x33, 0x00, 0x00, - 0x25, 0xa1, 0x92, 0xd0, 0xc0, 0x00, - 0xe2, 0x12, 0xf1, 0x09, 0x40, 0x00, - 0x99, 0x98, 0x4c, 0xcc, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_6[36] = - { - 0xd1, 0x4c, 0x68, 0xa6, 0x00, 0x00, - 0xa2, 0xc5, 0x51, 0x62, 0x80, 0x00, - 0x95, 0x30, 0xca, 0x98, 0x40, 0x00, - 0xca, 0x0a, 0xe5, 0x05, 0x40, 0x00, - 0xa4, 0xaa, 0x52, 0x55, 0x00, 0x00, - 0x78, 0x15, 0x3c, 0x0a, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask34_7[42] = - { - 0x15, 0x44, 0x8a, 0xa2, 0x40, 0x00, - 0x8a, 0x23, 0x45, 0x11, 0x80, 0x00, - 0x85, 0x91, 0x42, 0xc8, 0x80, 0x00, - 0x32, 0x0a, 0x99, 0x05, 0x40, 0x00, - 0x58, 0x34, 0x2c, 0x1a, 0x00, 0x00, - 0x2c, 0x0d, 0x16, 0x06, 0x80, 0x00, - 0x43, 0xc8, 0x21, 0xe4, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_8[48] = - { - 0x64, 0x16, 0x32, 0x0b, 0x00, 0x00, - 0xa2, 0xc2, 0x51, 0x61, 0x00, 0x00, - 0x51, 0x60, 0xa8, 0xb0, 0x40, 0x00, - 0x4a, 0x85, 0x25, 0x42, 0x80, 0x00, - 0x38, 0x4c, 0x1c, 0x26, 0x00, 0x00, - 0x89, 0x29, 0x44, 0x94, 0x80, 0x00, - 0x07, 0x11, 0x83, 0x88, 0xc0, 0x00, - 0x94, 0xb0, 0x4a, 0x58, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask34_9[54] = - { - 0x8e, 0xcc, 0x47, 0x66, 0x00, 0x00, - 0x6a, 0x2b, 0x35, 0x15, 0x80, 0x00, - 0x36, 0x32, 0x9b, 0x19, 0x40, 0x00, - 0xd1, 0x25, 0xe8, 0x92, 0xc0, 0x00, - 0x55, 0x8c, 0xaa, 0xc6, 0x40, 0x00, - 0xaa, 0x27, 0x55, 0x13, 0x80, 0x00, - 0xa5, 0x32, 0xd2, 0x99, 0x40, 0x00, - 0x62, 0x61, 0xb1, 0x30, 0xc0, 0x00, - 0x3c, 0x5c, 0x1e, 0x2e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask35_10[60] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x8e, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x6a, 0x2b, 0x15, 0x8a, 0xc0, 0x00, - 0x36, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0xd1, 0x25, 0x92, 0xc9, 0x60, 0x00, - 0xc8, 0x02, 0xfe, 0xce, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask35_11[66] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_12[72] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x35, 0x2d, 0x86, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask35_13[78] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_14[84] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0xb0, 0xde, 0xbf, 0xa7, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask35_15[90] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask35_16[96] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x0a, 0x1c, 0x77, 0xf9, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_17[102] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x25, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x8a, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x91, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x68, 0x42, 0xa1, 0x50, 0xa0, 0x00, - 0x32, 0xa4, 0x52, 0x29, 0x00, 0x00, - 0x43, 0x13, 0x09, 0x84, 0xc0, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0x88, 0xc4, 0x62, 0x20, 0x00, - 0x3c, 0x09, 0x04, 0x82, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_18[108] = - { - 0x8e, 0xcc, 0x22, 0x51, 0x20, 0x00, - 0x6a, 0x2b, 0x33, 0x13, 0x00, 0x00, - 0x36, 0x32, 0xc8, 0x24, 0xa0, 0x00, - 0xd1, 0x25, 0x80, 0xd2, 0xc0, 0x00, - 0x55, 0x8c, 0x87, 0x09, 0x40, 0x00, - 0xaa, 0x27, 0x09, 0x85, 0x80, 0x00, - 0xa5, 0x32, 0x90, 0x68, 0x20, 0x00, - 0x62, 0x61, 0xe1, 0x28, 0x80, 0x00, - 0x3c, 0x5c, 0x14, 0x86, 0x40, 0x00, - 0x64, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0xa2, 0xc2, 0x61, 0x30, 0x80, 0x00, - 0x51, 0x60, 0xb0, 0x58, 0x20, 0x00, - 0x4a, 0x85, 0x42, 0xa1, 0x40, 0x00, - 0x38, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x89, 0x29, 0x14, 0x8a, 0x40, 0x00, - 0x07, 0x11, 0x88, 0xc4, 0x60, 0x00, - 0x94, 0xb0, 0x58, 0x2c, 0x00, 0x00, - 0x40, 0xc9, 0x65, 0xbe, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask35_19[114] = - { - 0x8e, 0xcc, 0x22, 0x51, 0x20, 0x00, - 0x6a, 0x2b, 0x33, 0x13, 0x00, 0x00, - 0x36, 0x32, 0xc8, 0x24, 0xa0, 0x00, - 0xd1, 0x25, 0x80, 0xd2, 0xc0, 0x00, - 0x55, 0x8c, 0x87, 0x09, 0x40, 0x00, - 0xaa, 0x27, 0x09, 0x85, 0x80, 0x00, - 0xa5, 0x32, 0x90, 0x68, 0x20, 0x00, - 0x62, 0x61, 0xe1, 0x28, 0x80, 0x00, - 0x3c, 0x5c, 0x14, 0x86, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x8e, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x6a, 0x2b, 0x15, 0x8a, 0xc0, 0x00, - 0x36, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0xd1, 0x25, 0x92, 0xc9, 0x60, 0x00, - 0xc8, 0x02, 0xfe, 0xce, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask35_2[12] = - { - 0xce, 0xce, 0x67, 0x33, 0x80, 0x00, - 0xb9, 0x39, 0x9c, 0xce, 0x60, 0x00 - }; - - const WebRtc_UWord8 mask35_20[120] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x8e, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x6a, 0x2b, 0x15, 0x8a, 0xc0, 0x00, - 0x36, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0xd1, 0x25, 0x92, 0xc9, 0x60, 0x00, - 0xc8, 0x02, 0xfe, 0xce, 0xe0, 0x00, - 0x8e, 0xcc, 0x22, 0x51, 0x20, 0x00, - 0x6a, 0x2b, 0x33, 0x13, 0x00, 0x00, - 0x36, 0x32, 0xc8, 0x24, 0xa0, 0x00, - 0xd1, 0x25, 0x80, 0xd2, 0xc0, 0x00, - 0x55, 0x8c, 0x87, 0x09, 0x40, 0x00, - 0xaa, 0x27, 0x09, 0x85, 0x80, 0x00, - 0xa5, 0x32, 0x90, 0x68, 0x20, 0x00, - 0x62, 0x61, 0xe1, 0x28, 0x80, 0x00, - 0x3c, 0x5c, 0x14, 0x86, 0x40, 0x00, - 0x63, 0x36, 0x5c, 0xd3, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask35_21[126] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x8e, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x6a, 0x2b, 0x15, 0x8a, 0xc0, 0x00, - 0x36, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0xd1, 0x25, 0x92, 0xc9, 0x60, 0x00, - 0xc8, 0x02, 0xfe, 0xce, 0xe0, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_22[132] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x8e, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x6a, 0x2b, 0x15, 0x8a, 0xc0, 0x00, - 0x36, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0xd1, 0x25, 0x92, 0xc9, 0x60, 0x00, - 0xc8, 0x02, 0xfe, 0xce, 0xe0, 0x00, - 0x84, 0xc7, 0xbc, 0xcc, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_23[138] = - { - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x35, 0x2d, 0x86, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask35_24[144] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x35, 0x2d, 0x86, 0x20, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x0d, 0xfb, 0x06, 0x89, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_25[150] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x35, 0x2d, 0x86, 0x20, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_26[156] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x55, 0x8c, 0xc6, 0x63, 0x20, 0x00, - 0xaa, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0xa5, 0x32, 0x99, 0x4c, 0xa0, 0x00, - 0x62, 0x61, 0xb0, 0xd8, 0x60, 0x00, - 0x3c, 0x5c, 0x2e, 0x17, 0x00, 0x00, - 0x51, 0x35, 0x2d, 0x86, 0x20, 0x00, - 0xc4, 0x57, 0x70, 0x47, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_27[162] = - { - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0xb0, 0xde, 0xbf, 0xa7, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask35_28[168] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0xb0, 0xde, 0xbf, 0xa7, 0xe0, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x34, 0x4a, 0x80, 0x94, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_29[174] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0xb0, 0xde, 0xbf, 0xa7, 0xe0, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask35_3[18] = - { - 0xcd, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x97, 0x27, 0x13, 0x8a, 0xc0, 0x00, - 0xb8, 0xd1, 0xc9, 0x64, 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask35_30[180] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x51, 0x84, 0xc2, 0x61, 0x20, 0x00, - 0xa2, 0x27, 0x13, 0x89, 0xc0, 0x00, - 0x95, 0x51, 0xa8, 0xd4, 0x60, 0x00, - 0x4a, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x30, 0x68, 0x34, 0x1a, 0x00, 0x00, - 0x2c, 0x89, 0x44, 0xa2, 0x40, 0x00, - 0xb0, 0xde, 0xbf, 0xa7, 0xe0, 0x00, - 0x32, 0x1b, 0x9f, 0x09, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask35_31[186] = - { - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x0a, 0x1c, 0x77, 0xf9, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_32[192] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x0a, 0x1c, 0x77, 0xf9, 0x00, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0xeb, 0x31, 0x7b, 0x80, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_33[198] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x0a, 0x1c, 0x77, 0xf9, 0x00, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x25, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x8a, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x91, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x68, 0x42, 0xa1, 0x50, 0xa0, 0x00, - 0x32, 0xa4, 0x52, 0x29, 0x00, 0x00, - 0x43, 0x13, 0x09, 0x84, 0xc0, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0x88, 0xc4, 0x62, 0x20, 0x00, - 0x3c, 0x09, 0x04, 0x82, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_34[204] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x25, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x8a, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x91, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x68, 0x42, 0xa1, 0x50, 0xa0, 0x00, - 0x32, 0xa4, 0x52, 0x29, 0x00, 0x00, - 0x43, 0x13, 0x09, 0x84, 0xc0, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0x88, 0xc4, 0x62, 0x20, 0x00, - 0x3c, 0x09, 0x04, 0x82, 0x40, 0x00, - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x15, 0x8c, 0x46, 0x23, 0x00, 0x00, - 0x8a, 0x47, 0x23, 0x91, 0xc0, 0x00, - 0x25, 0x81, 0xc0, 0xe0, 0x60, 0x00, - 0x62, 0x12, 0x89, 0x44, 0xa0, 0x00, - 0x58, 0x58, 0x2c, 0x16, 0x00, 0x00, - 0x0e, 0x28, 0x94, 0x4a, 0x20, 0x00, - 0x83, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x0a, 0x1c, 0x77, 0xf9, 0x00, 0x00, - 0x70, 0x07, 0xcd, 0x8c, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask35_35[210] = - { - 0x25, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x8a, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x91, 0xc0, 0xe0, 0x70, 0x20, 0x00, - 0x68, 0x06, 0x83, 0x41, 0xa0, 0x00, - 0x32, 0xc8, 0x64, 0x32, 0x00, 0x00, - 0x43, 0x45, 0x22, 0x91, 0x40, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x51, 0x28, 0x80, 0x00, - 0x25, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x8a, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x91, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x68, 0x42, 0xa1, 0x50, 0xa0, 0x00, - 0x32, 0xa4, 0x52, 0x29, 0x00, 0x00, - 0x43, 0x13, 0x09, 0x84, 0xc0, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0x88, 0xc4, 0x62, 0x20, 0x00, - 0x3c, 0x09, 0x04, 0x82, 0x40, 0x00, - 0x25, 0x2c, 0x26, 0x13, 0x00, 0x00, - 0x8a, 0x91, 0x33, 0x19, 0x80, 0x00, - 0x91, 0xc0, 0xc8, 0xa4, 0x40, 0x00, - 0x68, 0x06, 0xa1, 0x50, 0xa0, 0x00, - 0x32, 0xc8, 0x52, 0x29, 0x00, 0x00, - 0x43, 0x45, 0x09, 0x84, 0xc0, 0x00, - 0xc4, 0x30, 0x98, 0x4c, 0x20, 0x00, - 0x1c, 0xa2, 0x44, 0x62, 0x20, 0x00, - 0x25, 0x4c, 0x04, 0x82, 0x40, 0x00, - 0x8a, 0x66, 0x16, 0x0b, 0x00, 0x00, - 0x91, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x68, 0x42, 0xe0, 0x70, 0x20, 0x00, - 0x32, 0xa4, 0x03, 0x41, 0xa0, 0x00, - 0x43, 0x13, 0x64, 0x32, 0x00, 0x00, - 0xc4, 0x30, 0xa2, 0x91, 0x40, 0x00, - 0x1c, 0x88, 0x98, 0x4c, 0x20, 0x00, - 0x3c, 0x09, 0x51, 0x28, 0x80, 0x00, - 0xc2, 0x1c, 0x68, 0x01, 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask35_4[24] = - { - 0xca, 0xec, 0x76, 0x3b, 0x00, 0x00, - 0xa9, 0x67, 0x33, 0x99, 0xc0, 0x00, - 0x3a, 0xb1, 0xd8, 0xec, 0x60, 0x00, - 0x55, 0x5a, 0xad, 0x56, 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask35_5[30] = - { - 0x55, 0x44, 0xa6, 0x53, 0x20, 0x00, - 0x2a, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x25, 0xa1, 0x8c, 0xe8, 0x60, 0x00, - 0xe2, 0x12, 0xce, 0x44, 0xa0, 0x00, - 0x99, 0x98, 0x71, 0xa6, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_6[36] = - { - 0xd1, 0x4c, 0x66, 0x13, 0x00, 0x00, - 0xa2, 0xc5, 0x22, 0xb1, 0x40, 0x00, - 0x95, 0x30, 0xd8, 0x4c, 0x20, 0x00, - 0xca, 0x0a, 0xc5, 0x42, 0xa0, 0x00, - 0xa4, 0xaa, 0x14, 0xa9, 0x80, 0x00, - 0x78, 0x15, 0x53, 0x05, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask35_7[42] = - { - 0x15, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8a, 0x23, 0x11, 0x88, 0xc0, 0x00, - 0x85, 0x91, 0x48, 0xa4, 0x40, 0x00, - 0x32, 0x0a, 0x85, 0x42, 0xa0, 0x00, - 0x58, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x2c, 0x0d, 0x05, 0x83, 0x40, 0x00, - 0x43, 0xc8, 0x70, 0x32, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_8[48] = - { - 0x64, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0xa2, 0xc2, 0x61, 0x30, 0x80, 0x00, - 0x51, 0x60, 0xb0, 0x58, 0x20, 0x00, - 0x4a, 0x85, 0x42, 0xa1, 0x40, 0x00, - 0x38, 0x4c, 0x26, 0x13, 0x00, 0x00, - 0x89, 0x29, 0x14, 0x8a, 0x40, 0x00, - 0x07, 0x11, 0x88, 0xc4, 0x60, 0x00, - 0x94, 0xb0, 0x58, 0x2c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask35_9[54] = - { - 0x8e, 0xcc, 0x22, 0x51, 0x20, 0x00, - 0x6a, 0x2b, 0x33, 0x13, 0x00, 0x00, - 0x36, 0x32, 0xc8, 0x24, 0xa0, 0x00, - 0xd1, 0x25, 0x80, 0xd2, 0xc0, 0x00, - 0x55, 0x8c, 0x87, 0x09, 0x40, 0x00, - 0xaa, 0x27, 0x09, 0x85, 0x80, 0x00, - 0xa5, 0x32, 0x90, 0x68, 0x20, 0x00, - 0x62, 0x61, 0xe1, 0x28, 0x80, 0x00, - 0x3c, 0x5c, 0x14, 0x86, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask36_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask36_10[60] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x2b, 0x15, 0x8a, 0xc5, 0x60, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x25, 0x92, 0xc9, 0x64, 0xb0, 0x00, - 0xfd, 0x9d, 0xff, 0x67, 0x70, 0x00 - }; - - const WebRtc_UWord8 mask36_11[66] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask36_12[72] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x5b, 0x0c, 0x56, 0xc3, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask36_13[78] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_14[84] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x7f, 0x4f, 0xdf, 0xd3, 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask36_15[90] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask36_16[96] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0xef, 0xf2, 0x3b, 0xfc, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_17[102] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask36_18[108] = - { - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0xd0, 0x03, 0x74, 0x00, 0xd0, 0x00 - }; - - const WebRtc_UWord8 mask36_19[114] = - { - 0x44, 0xa2, 0x51, 0x28, 0x90, 0x00, - 0x66, 0x26, 0x19, 0x89, 0x80, 0x00, - 0x90, 0x49, 0x64, 0x12, 0x50, 0x00, - 0x01, 0xa5, 0x80, 0x69, 0x60, 0x00, - 0x0e, 0x12, 0x83, 0x84, 0xa0, 0x00, - 0x13, 0x0b, 0x04, 0xc2, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x34, 0x10, 0x00, - 0xc2, 0x51, 0x30, 0x94, 0x40, 0x00, - 0x29, 0x0c, 0x8a, 0x43, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x2b, 0x15, 0x8a, 0xc5, 0x60, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x25, 0x92, 0xc9, 0x64, 0xb0, 0x00, - 0xfd, 0x9d, 0xff, 0x67, 0x70, 0x00 - }; - - const WebRtc_UWord8 mask36_2[12] = - { - 0xce, 0x67, 0x33, 0x99, 0xc0, 0x00, - 0x39, 0x9c, 0xce, 0x67, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask36_20[120] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x2b, 0x15, 0x8a, 0xc5, 0x60, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x25, 0x92, 0xc9, 0x64, 0xb0, 0x00, - 0xfd, 0x9d, 0xff, 0x67, 0x70, 0x00, - 0x44, 0xa2, 0x51, 0x28, 0x90, 0x00, - 0x66, 0x26, 0x19, 0x89, 0x80, 0x00, - 0x90, 0x49, 0x64, 0x12, 0x50, 0x00, - 0x01, 0xa5, 0x80, 0x69, 0x60, 0x00, - 0x0e, 0x12, 0x83, 0x84, 0xa0, 0x00, - 0x13, 0x0b, 0x04, 0xc2, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x34, 0x10, 0x00, - 0xc2, 0x51, 0x30, 0x94, 0x40, 0x00, - 0x29, 0x0c, 0x8a, 0x43, 0x20, 0x00, - 0x45, 0xb9, 0x08, 0x16, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask36_21[126] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x2b, 0x15, 0x8a, 0xc5, 0x60, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x25, 0x92, 0xc9, 0x64, 0xb0, 0x00, - 0xfd, 0x9d, 0xff, 0x67, 0x70, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask36_22[132] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x2b, 0x15, 0x8a, 0xc5, 0x60, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x25, 0x92, 0xc9, 0x64, 0xb0, 0x00, - 0xfd, 0x9d, 0xff, 0x67, 0x70, 0x00, - 0x71, 0x04, 0xba, 0x7b, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask36_23[138] = - { - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x5b, 0x0c, 0x56, 0xc3, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask36_24[144] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x5b, 0x0c, 0x56, 0xc3, 0x10, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x76, 0x3a, 0xeb, 0x17, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask36_25[150] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x5b, 0x0c, 0x56, 0xc3, 0x10, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_26[156] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x31, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x32, 0x99, 0x4c, 0xa6, 0x50, 0x00, - 0x61, 0xb0, 0xd8, 0x6c, 0x30, 0x00, - 0x5c, 0x2e, 0x17, 0x0b, 0x80, 0x00, - 0x5b, 0x0c, 0x56, 0xc3, 0x10, 0x00, - 0xec, 0x58, 0x0e, 0x6c, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask36_27[162] = - { - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x7f, 0x4f, 0xdf, 0xd3, 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask36_28[168] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x7f, 0x4f, 0xdf, 0xd3, 0xf0, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x29, 0xfd, 0x91, 0x6f, 0xd0, 0x00 - }; - - const WebRtc_UWord8 mask36_29[174] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x7f, 0x4f, 0xdf, 0xd3, 0xf0, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask36_3[18] = - { - 0xcc, 0x66, 0x33, 0x19, 0x80, 0x00, - 0x27, 0x15, 0x89, 0xc5, 0x60, 0x00, - 0x92, 0xc9, 0x64, 0xb2, 0x50, 0x00 - }; - - const WebRtc_UWord8 mask36_30[180] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x84, 0xc2, 0x61, 0x30, 0x90, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0xe0, 0x00, - 0x51, 0xa8, 0xd4, 0x6a, 0x30, 0x00, - 0x1a, 0x0d, 0x06, 0x83, 0x40, 0x00, - 0x68, 0x34, 0x1a, 0x0d, 0x00, 0x00, - 0x89, 0x44, 0xa2, 0x51, 0x20, 0x00, - 0x7f, 0x4f, 0xdf, 0xd3, 0xf0, 0x00, - 0xc5, 0x38, 0xbb, 0x98, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_31[186] = - { - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0xef, 0xf2, 0x3b, 0xfc, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_32[192] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0xef, 0xf2, 0x3b, 0xfc, 0x80, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x3a, 0x28, 0x9c, 0x2f, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask36_33[198] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0xef, 0xf2, 0x3b, 0xfc, 0x80, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask36_34[204] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x8c, 0x46, 0x23, 0x11, 0x80, 0x00, - 0x47, 0x23, 0x91, 0xc8, 0xe0, 0x00, - 0x81, 0xc0, 0xe0, 0x70, 0x30, 0x00, - 0x12, 0x89, 0x44, 0xa2, 0x50, 0x00, - 0x58, 0x2c, 0x16, 0x0b, 0x00, 0x00, - 0x28, 0x94, 0x4a, 0x25, 0x10, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0xef, 0xf2, 0x3b, 0xfc, 0x80, 0x00, - 0xf7, 0x5e, 0x66, 0x5b, 0x60, 0x00 - }; - - const WebRtc_UWord8 mask36_35[210] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0xd0, 0x03, 0x74, 0x00, 0xd0, 0x00 - }; - - const WebRtc_UWord8 mask36_36[216] = - { - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0xd0, 0x03, 0x74, 0x00, 0xd0, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0xc0, 0xe0, 0x70, 0x38, 0x10, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xd0, 0x00, - 0xc8, 0x64, 0x32, 0x19, 0x00, 0x00, - 0x45, 0x22, 0x91, 0x48, 0xa0, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0xa2, 0x51, 0x28, 0x94, 0x40, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x42, 0xa1, 0x50, 0xa8, 0x50, 0x00, - 0xa4, 0x52, 0x29, 0x14, 0x80, 0x00, - 0x13, 0x09, 0x84, 0xc2, 0x60, 0x00, - 0x30, 0x98, 0x4c, 0x26, 0x10, 0x00, - 0x88, 0xc4, 0x62, 0x31, 0x10, 0x00, - 0x09, 0x04, 0x82, 0x41, 0x20, 0x00, - 0xa4, 0x9c, 0x31, 0x13, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask36_4[24] = - { - 0xec, 0x76, 0x3b, 0x1d, 0x80, 0x00, - 0x67, 0x33, 0x99, 0xcc, 0xe0, 0x00, - 0xb1, 0xd8, 0xec, 0x76, 0x30, 0x00, - 0x5a, 0xad, 0x56, 0xab, 0x50, 0x00 - }; - - const WebRtc_UWord8 mask36_5[30] = - { - 0x4c, 0xa6, 0x53, 0x29, 0x90, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0xc0, 0x00, - 0x19, 0xd0, 0xc6, 0x74, 0x30, 0x00, - 0x9c, 0x89, 0x67, 0x22, 0x50, 0x00, - 0xe3, 0x4c, 0x38, 0xd3, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask36_6[36] = - { - 0xcc, 0x26, 0x33, 0x09, 0x80, 0x00, - 0x45, 0x62, 0x91, 0x58, 0xa0, 0x00, - 0xb0, 0x98, 0x6c, 0x26, 0x10, 0x00, - 0x8a, 0x85, 0x62, 0xa1, 0x50, 0x00, - 0x29, 0x53, 0x0a, 0x54, 0xc0, 0x00, - 0xa6, 0x0a, 0xa9, 0x82, 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask36_7[42] = - { - 0x44, 0xa2, 0x51, 0x28, 0x90, 0x00, - 0x23, 0x11, 0x88, 0xc4, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x52, 0x20, 0x00, - 0x0a, 0x85, 0x42, 0xa1, 0x50, 0x00, - 0x34, 0x1a, 0x0d, 0x06, 0x80, 0x00, - 0x0b, 0x06, 0x82, 0xc1, 0xa0, 0x00, - 0xe0, 0x64, 0x38, 0x19, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask36_8[48] = - { - 0x16, 0x0b, 0x05, 0x82, 0xc0, 0x00, - 0xc2, 0x61, 0x30, 0x98, 0x40, 0x00, - 0x60, 0xb0, 0x58, 0x2c, 0x10, 0x00, - 0x85, 0x42, 0xa1, 0x50, 0xa0, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0x80, 0x00, - 0x29, 0x14, 0x8a, 0x45, 0x20, 0x00, - 0x11, 0x88, 0xc4, 0x62, 0x30, 0x00, - 0xb0, 0x58, 0x2c, 0x16, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask36_9[54] = - { - 0x44, 0xa2, 0x51, 0x28, 0x90, 0x00, - 0x66, 0x26, 0x19, 0x89, 0x80, 0x00, - 0x90, 0x49, 0x64, 0x12, 0x50, 0x00, - 0x01, 0xa5, 0x80, 0x69, 0x60, 0x00, - 0x0e, 0x12, 0x83, 0x84, 0xa0, 0x00, - 0x13, 0x0b, 0x04, 0xc2, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x34, 0x10, 0x00, - 0xc2, 0x51, 0x30, 0x94, 0x40, 0x00, - 0x29, 0x0c, 0x8a, 0x43, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00 - }; - - const WebRtc_UWord8 mask37_10[60] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0xcc, 0x66, 0x33, 0x1d, 0x40, 0x00, - 0x2b, 0x15, 0x8a, 0xc6, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xb4, 0x98, 0x00, - 0x25, 0x92, 0xc9, 0x63, 0xa8, 0x00, - 0xfd, 0x9d, 0xd4, 0x22, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask37_11[66] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask37_12[72] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x5b, 0x0c, 0x64, 0x32, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_13[78] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_14[84] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x7f, 0x4f, 0xdb, 0x89, 0xd8, 0x00 - }; - - const WebRtc_UWord8 mask37_15[90] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask37_16[96] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0xef, 0xf2, 0x1f, 0x9d, 0x78, 0x00 - }; - - const WebRtc_UWord8 mask37_17[102] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask37_18[108] = - { - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0xd0, 0x03, 0x54, 0x65, 0xc8, 0x00 - }; - - const WebRtc_UWord8 mask37_19[114] = - { - 0x44, 0xa2, 0x51, 0x29, 0xc0, 0x00, - 0x66, 0x26, 0x19, 0x9c, 0x20, 0x00, - 0x90, 0x49, 0x44, 0xb0, 0x38, 0x00, - 0x01, 0xa5, 0xb0, 0xc4, 0x28, 0x00, - 0x0e, 0x12, 0xa3, 0x0a, 0x50, 0x00, - 0x13, 0x0b, 0x04, 0x56, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x64, 0xd0, 0x00, - 0xc2, 0x51, 0x28, 0x8b, 0x00, 0x00, - 0x29, 0x0c, 0x86, 0x03, 0x38, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0xcc, 0x66, 0x33, 0x1d, 0x40, 0x00, - 0x2b, 0x15, 0x8a, 0xc6, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xb4, 0x98, 0x00, - 0x25, 0x92, 0xc9, 0x63, 0xa8, 0x00, - 0xfd, 0x9d, 0xd4, 0x22, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask37_2[12] = - { - 0xce, 0x67, 0x33, 0x9d, 0xc0, 0x00, - 0x39, 0x9c, 0xce, 0x73, 0x38, 0x00 - }; - - const WebRtc_UWord8 mask37_20[120] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0xcc, 0x66, 0x33, 0x1d, 0x40, 0x00, - 0x2b, 0x15, 0x8a, 0xc6, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xb4, 0x98, 0x00, - 0x25, 0x92, 0xc9, 0x63, 0xa8, 0x00, - 0xfd, 0x9d, 0xd4, 0x22, 0x30, 0x00, - 0x44, 0xa2, 0x51, 0x29, 0xc0, 0x00, - 0x66, 0x26, 0x19, 0x9c, 0x20, 0x00, - 0x90, 0x49, 0x44, 0xb0, 0x38, 0x00, - 0x01, 0xa5, 0xb0, 0xc4, 0x28, 0x00, - 0x0e, 0x12, 0xa3, 0x0a, 0x50, 0x00, - 0x13, 0x0b, 0x04, 0x56, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x64, 0xd0, 0x00, - 0xc2, 0x51, 0x28, 0x8b, 0x00, 0x00, - 0x29, 0x0c, 0x86, 0x03, 0x38, 0x00, - 0xe5, 0x44, 0xda, 0x3a, 0xc8, 0x00 - }; - - const WebRtc_UWord8 mask37_21[126] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0xcc, 0x66, 0x33, 0x1d, 0x40, 0x00, - 0x2b, 0x15, 0x8a, 0xc6, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xb4, 0x98, 0x00, - 0x25, 0x92, 0xc9, 0x63, 0xa8, 0x00, - 0xfd, 0x9d, 0xd4, 0x22, 0x30, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask37_22[132] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0xcc, 0x66, 0x33, 0x1d, 0x40, 0x00, - 0x2b, 0x15, 0x8a, 0xc6, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xb4, 0x98, 0x00, - 0x25, 0x92, 0xc9, 0x63, 0xa8, 0x00, - 0xfd, 0x9d, 0xd4, 0x22, 0x30, 0x00, - 0xe4, 0xd3, 0xff, 0x5a, 0x28, 0x00 - }; - - const WebRtc_UWord8 mask37_23[138] = - { - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x5b, 0x0c, 0x64, 0x32, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_24[144] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x5b, 0x0c, 0x64, 0x32, 0x20, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0xad, 0x58, 0xb2, 0x36, 0x68, 0x00 - }; - - const WebRtc_UWord8 mask37_25[150] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x5b, 0x0c, 0x64, 0x32, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_26[156] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0xc6, 0x63, 0x38, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc4, 0x70, 0x00, - 0x32, 0x99, 0x4c, 0xa3, 0x48, 0x00, - 0x61, 0xb0, 0xd8, 0x64, 0x98, 0x00, - 0x5c, 0x2e, 0x17, 0x0e, 0x20, 0x00, - 0x5b, 0x0c, 0x64, 0x32, 0x20, 0x00, - 0x7f, 0xb2, 0x5a, 0xaa, 0x20, 0x00 - }; - - const WebRtc_UWord8 mask37_27[162] = - { - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x7f, 0x4f, 0xdb, 0x89, 0xd8, 0x00 - }; - - const WebRtc_UWord8 mask37_28[168] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x7f, 0x4f, 0xdb, 0x89, 0xd8, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x7b, 0xc4, 0x24, 0xbf, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask37_29[174] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x7f, 0x4f, 0xdb, 0x89, 0xd8, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask37_3[18] = - { - 0xcc, 0x66, 0x33, 0x19, 0xc0, 0x00, - 0x27, 0x15, 0x89, 0xcb, 0x30, 0x00, - 0x92, 0xc9, 0x64, 0xb4, 0x98, 0x00 - }; - - const WebRtc_UWord8 mask37_30[180] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x84, 0xc2, 0x61, 0x21, 0xc0, 0x00, - 0x27, 0x13, 0x89, 0xc6, 0x60, 0x00, - 0x51, 0xa8, 0xd4, 0x62, 0x18, 0x00, - 0x1a, 0x0d, 0x06, 0x88, 0xa8, 0x00, - 0x68, 0x34, 0x1a, 0x11, 0x10, 0x00, - 0x89, 0x44, 0xa2, 0x5c, 0x00, 0x00, - 0x7f, 0x4f, 0xdb, 0x89, 0xd8, 0x00, - 0x1d, 0x8e, 0x11, 0xb0, 0xe8, 0x00 - }; - - const WebRtc_UWord8 mask37_31[186] = - { - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0xef, 0xf2, 0x1f, 0x9d, 0x78, 0x00 - }; - - const WebRtc_UWord8 mask37_32[192] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0xef, 0xf2, 0x1f, 0x9d, 0x78, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0xf7, 0x95, 0x57, 0x8c, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask37_33[198] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0xef, 0xf2, 0x1f, 0x9d, 0x78, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask37_34[204] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x8c, 0x46, 0x23, 0x08, 0xc0, 0x00, - 0x47, 0x23, 0x91, 0xc6, 0x60, 0x00, - 0x81, 0xc0, 0xe0, 0x62, 0x18, 0x00, - 0x12, 0x89, 0x44, 0xa1, 0x88, 0x00, - 0x58, 0x2c, 0x16, 0x05, 0x10, 0x00, - 0x28, 0x94, 0x4a, 0x32, 0x80, 0x00, - 0x34, 0x1a, 0x0d, 0x18, 0x20, 0x00, - 0xef, 0xf2, 0x1f, 0x9d, 0x78, 0x00, - 0x31, 0x9c, 0xfb, 0x37, 0xc0, 0x00 - }; - - const WebRtc_UWord8 mask37_35[210] = - { - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0xd0, 0x03, 0x54, 0x65, 0xc8, 0x00 - }; - - const WebRtc_UWord8 mask37_36[216] = - { - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0xd0, 0x03, 0x54, 0x65, 0xc8, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0xc3, 0xc7, 0xce, 0xd8, 0x50, 0x00 - }; - - const WebRtc_UWord8 mask37_37[222] = - { - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x0b, 0x05, 0x80, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x30, 0x00, - 0xc0, 0xe0, 0x70, 0x34, 0x08, 0x00, - 0x06, 0x83, 0x41, 0xa0, 0xa8, 0x00, - 0xc8, 0x64, 0x32, 0x03, 0x10, 0x00, - 0x45, 0x22, 0x91, 0x58, 0x40, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0x50, 0x00, - 0xa2, 0x51, 0x28, 0x8a, 0x08, 0x00, - 0xd0, 0x03, 0x54, 0x65, 0xc8, 0x00, - 0x4c, 0x26, 0x13, 0x09, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x9c, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x50, 0x38, 0x00, - 0x42, 0xa1, 0x50, 0xa4, 0x28, 0x00, - 0xa4, 0x52, 0x29, 0x0a, 0x50, 0x00, - 0x13, 0x09, 0x84, 0xd6, 0x80, 0x00, - 0x30, 0x98, 0x4c, 0x24, 0xd0, 0x00, - 0x88, 0xc4, 0x62, 0x2b, 0x08, 0x00, - 0x09, 0x04, 0x82, 0x43, 0x30, 0x00, - 0x2c, 0x16, 0x13, 0x09, 0x80, 0x00, - 0x91, 0x48, 0x99, 0x8a, 0x20, 0x00, - 0xc0, 0xe0, 0x64, 0x54, 0x08, 0x00, - 0x06, 0x83, 0x50, 0xa0, 0x98, 0x00, - 0xc8, 0x64, 0x29, 0x00, 0x70, 0x00, - 0x45, 0x22, 0x84, 0xd0, 0xc0, 0x00, - 0x30, 0x98, 0x4c, 0x25, 0x20, 0x00, - 0xa2, 0x51, 0x22, 0x28, 0x48, 0x00, - 0xd0, 0x03, 0x42, 0x53, 0x00, 0x00, - 0xee, 0xf5, 0xb3, 0x66, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask37_4[24] = - { - 0xec, 0x76, 0x3b, 0x1c, 0xc0, 0x00, - 0x67, 0x33, 0x99, 0xc6, 0x70, 0x00, - 0xb1, 0xd8, 0xec, 0x73, 0x18, 0x00, - 0x5a, 0xad, 0x56, 0xa5, 0xa8, 0x00 - }; - - const WebRtc_UWord8 mask37_5[30] = - { - 0x4c, 0xa6, 0x53, 0x39, 0xc0, 0x00, - 0x66, 0x33, 0x19, 0x8c, 0x70, 0x00, - 0x19, 0xd0, 0xe8, 0x73, 0x18, 0x00, - 0x9c, 0x89, 0x64, 0xa9, 0xa8, 0x00, - 0xe3, 0x4c, 0x2e, 0x26, 0x60, 0x00 - }; - - const WebRtc_UWord8 mask37_6[36] = - { - 0xcc, 0x26, 0x13, 0x0d, 0x80, 0x00, - 0x45, 0x62, 0x91, 0x5a, 0x20, 0x00, - 0xb0, 0x98, 0x4c, 0x34, 0x18, 0x00, - 0x8a, 0x85, 0x62, 0xa0, 0xa8, 0x00, - 0x29, 0x53, 0x09, 0x82, 0xd0, 0x00, - 0xa6, 0x0a, 0xa5, 0x51, 0x40, 0x00 - }; - - const WebRtc_UWord8 mask37_7[42] = - { - 0x44, 0xa2, 0x71, 0x28, 0xc0, 0x00, - 0x23, 0x11, 0x88, 0xc6, 0x60, 0x00, - 0x91, 0x48, 0xa4, 0x47, 0x08, 0x00, - 0x0a, 0x85, 0x52, 0xa0, 0xa8, 0x00, - 0x34, 0x1a, 0x0d, 0x12, 0x50, 0x00, - 0x0b, 0x06, 0xa2, 0xd2, 0x80, 0x00, - 0xe0, 0x64, 0x32, 0x09, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask37_8[48] = - { - 0x16, 0x0b, 0x05, 0x84, 0xe0, 0x00, - 0xc2, 0x61, 0x30, 0x91, 0x30, 0x00, - 0x60, 0xb0, 0x58, 0x3a, 0x08, 0x00, - 0x85, 0x42, 0xa1, 0x44, 0x98, 0x00, - 0x4c, 0x26, 0x33, 0x08, 0x50, 0x00, - 0x29, 0x14, 0x8a, 0x58, 0xc0, 0x00, - 0x11, 0x88, 0xc4, 0x66, 0x30, 0x00, - 0xb0, 0x58, 0x2c, 0x03, 0x18, 0x00 - }; - - const WebRtc_UWord8 mask37_9[54] = - { - 0x44, 0xa2, 0x51, 0x29, 0xc0, 0x00, - 0x66, 0x26, 0x19, 0x9c, 0x20, 0x00, - 0x90, 0x49, 0x44, 0xb0, 0x38, 0x00, - 0x01, 0xa5, 0xb0, 0xc4, 0x28, 0x00, - 0x0e, 0x12, 0xa3, 0x0a, 0x50, 0x00, - 0x13, 0x0b, 0x04, 0x56, 0xc0, 0x00, - 0x20, 0xd0, 0x48, 0x64, 0xd0, 0x00, - 0xc2, 0x51, 0x28, 0x8b, 0x00, 0x00, - 0x29, 0x0c, 0x86, 0x03, 0x38, 0x00 - }; - - const WebRtc_UWord8 mask38_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00 - }; - - const WebRtc_UWord8 mask38_10[60] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0xcc, 0x75, 0x19, 0x8e, 0xa0, 0x00, - 0x2b, 0x19, 0xc5, 0x63, 0x38, 0x00, - 0x32, 0xd2, 0x66, 0x5a, 0x4c, 0x00, - 0x25, 0x8e, 0xa4, 0xb1, 0xd4, 0x00, - 0x50, 0x88, 0xca, 0x11, 0x18, 0x00 - }; - - const WebRtc_UWord8 mask38_11[66] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask38_12[72] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x90, 0xc8, 0x92, 0x19, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask38_13[78] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask38_14[84] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x6e, 0x27, 0x6d, 0xc4, 0xec, 0x00 - }; - - const WebRtc_UWord8 mask38_15[90] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00 - }; - - const WebRtc_UWord8 mask38_16[96] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x7e, 0x75, 0xef, 0xce, 0xbc, 0x00 - }; - - const WebRtc_UWord8 mask38_17[102] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00 - }; - - const WebRtc_UWord8 mask38_18[108] = - { - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x51, 0x97, 0x2a, 0x32, 0xe4, 0x00 - }; - - const WebRtc_UWord8 mask38_19[114] = - { - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x4c, 0x26, 0x09, 0x84, 0xc0, 0x00, - 0x66, 0x28, 0x8c, 0xc5, 0x10, 0x00, - 0x91, 0x50, 0x32, 0x2a, 0x04, 0x00, - 0x42, 0x82, 0x68, 0x50, 0x4c, 0x00, - 0xa4, 0x01, 0xd4, 0x80, 0x38, 0x00, - 0x13, 0x43, 0x02, 0x68, 0x60, 0x00, - 0x30, 0x94, 0x86, 0x12, 0x90, 0x00, - 0x88, 0xa1, 0x31, 0x14, 0x24, 0x00, - 0x09, 0x4c, 0x01, 0x29, 0x80, 0x00, - 0xcd, 0x98, 0x59, 0xb3, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask38_2[12] = - { - 0xce, 0x77, 0x19, 0xce, 0xe0, 0x00, - 0x39, 0xcc, 0xe7, 0x39, 0x9c, 0x00 - }; - - const WebRtc_UWord8 mask38_20[120] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0xcc, 0x75, 0x19, 0x8e, 0xa0, 0x00, - 0x2b, 0x19, 0xc5, 0x63, 0x38, 0x00, - 0x32, 0xd2, 0x66, 0x5a, 0x4c, 0x00, - 0x25, 0x8e, 0xa4, 0xb1, 0xd4, 0x00, - 0x50, 0x88, 0xca, 0x11, 0x18, 0x00, - 0x44, 0xa7, 0x08, 0x94, 0xe0, 0x00, - 0x66, 0x70, 0x8c, 0xce, 0x10, 0x00, - 0x12, 0xc0, 0xe2, 0x58, 0x1c, 0x00, - 0xc3, 0x10, 0xb8, 0x62, 0x14, 0x00, - 0x8c, 0x29, 0x51, 0x85, 0x28, 0x00, - 0x11, 0x5b, 0x02, 0x2b, 0x60, 0x00, - 0x21, 0x93, 0x44, 0x32, 0x68, 0x00, - 0xa2, 0x2c, 0x14, 0x45, 0x80, 0x00, - 0x18, 0x0c, 0xe3, 0x01, 0x9c, 0x00, - 0xe6, 0xbc, 0x88, 0xe3, 0x78, 0x00 - }; - - const WebRtc_UWord8 mask38_21[126] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0xcc, 0x75, 0x19, 0x8e, 0xa0, 0x00, - 0x2b, 0x19, 0xc5, 0x63, 0x38, 0x00, - 0x32, 0xd2, 0x66, 0x5a, 0x4c, 0x00, - 0x25, 0x8e, 0xa4, 0xb1, 0xd4, 0x00, - 0x50, 0x88, 0xca, 0x11, 0x18, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask38_22[132] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0xcc, 0x75, 0x19, 0x8e, 0xa0, 0x00, - 0x2b, 0x19, 0xc5, 0x63, 0x38, 0x00, - 0x32, 0xd2, 0x66, 0x5a, 0x4c, 0x00, - 0x25, 0x8e, 0xa4, 0xb1, 0xd4, 0x00, - 0x50, 0x88, 0xca, 0x11, 0x18, 0x00, - 0x0c, 0x3c, 0x48, 0x3d, 0x58, 0x00 - }; - - const WebRtc_UWord8 mask38_23[138] = - { - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x90, 0xc8, 0x92, 0x19, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask38_24[144] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x90, 0xc8, 0x92, 0x19, 0x10, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x93, 0xc8, 0xb3, 0xbe, 0x5c, 0x00 - }; - - const WebRtc_UWord8 mask38_25[150] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x90, 0xc8, 0x92, 0x19, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00 - }; - - const WebRtc_UWord8 mask38_26[156] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0xe3, 0x11, 0x9c, 0x60, 0x00, - 0x27, 0x11, 0xc4, 0xe2, 0x38, 0x00, - 0x32, 0x8d, 0x26, 0x51, 0xa4, 0x00, - 0x61, 0x92, 0x6c, 0x32, 0x4c, 0x00, - 0x5c, 0x38, 0x8b, 0x87, 0x10, 0x00, - 0x90, 0xc8, 0x92, 0x19, 0x10, 0x00, - 0x4b, 0xab, 0xfc, 0xe6, 0xe8, 0x00 - }; - - const WebRtc_UWord8 mask38_27[162] = - { - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x6e, 0x27, 0x6d, 0xc4, 0xec, 0x00 - }; - - const WebRtc_UWord8 mask38_28[168] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x6e, 0x27, 0x6d, 0xc4, 0xec, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x70, 0x1b, 0x5b, 0x2c, 0x0c, 0x00 - }; - - const WebRtc_UWord8 mask38_29[174] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x6e, 0x27, 0x6d, 0xc4, 0xec, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00 - }; - - const WebRtc_UWord8 mask38_3[18] = - { - 0xcc, 0x67, 0x19, 0x8c, 0xe0, 0x00, - 0x27, 0x2c, 0xc4, 0xe5, 0x98, 0x00, - 0x92, 0xd2, 0x72, 0x5a, 0x4c, 0x00 - }; - - const WebRtc_UWord8 mask38_30[180] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x84, 0x87, 0x10, 0x90, 0xe0, 0x00, - 0x27, 0x19, 0x84, 0xe3, 0x30, 0x00, - 0x51, 0x88, 0x6a, 0x31, 0x0c, 0x00, - 0x1a, 0x22, 0xa3, 0x44, 0x54, 0x00, - 0x68, 0x44, 0x4d, 0x08, 0x88, 0x00, - 0x89, 0x70, 0x11, 0x2e, 0x00, 0x00, - 0x6e, 0x27, 0x6d, 0xc4, 0xec, 0x00, - 0x5b, 0x16, 0xdf, 0xb8, 0xd0, 0x00 - }; - - const WebRtc_UWord8 mask38_31[186] = - { - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x7e, 0x75, 0xef, 0xce, 0xbc, 0x00 - }; - - const WebRtc_UWord8 mask38_32[192] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x7e, 0x75, 0xef, 0xce, 0xbc, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x33, 0x10, 0x02, 0x4e, 0x54, 0x00 - }; - - const WebRtc_UWord8 mask38_33[198] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x7e, 0x75, 0xef, 0xce, 0xbc, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00 - }; - - const WebRtc_UWord8 mask38_34[204] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x8c, 0x23, 0x11, 0x84, 0x60, 0x00, - 0x47, 0x19, 0x88, 0xe3, 0x30, 0x00, - 0x81, 0x88, 0x70, 0x31, 0x0c, 0x00, - 0x12, 0x86, 0x22, 0x50, 0xc4, 0x00, - 0x58, 0x14, 0x4b, 0x02, 0x88, 0x00, - 0x28, 0xca, 0x05, 0x19, 0x40, 0x00, - 0x34, 0x60, 0x86, 0x8c, 0x10, 0x00, - 0x7e, 0x75, 0xef, 0xce, 0xbc, 0x00, - 0x91, 0x48, 0xfa, 0xf0, 0xd8, 0x00 - }; - - const WebRtc_UWord8 mask38_35[210] = - { - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x51, 0x97, 0x2a, 0x32, 0xe4, 0x00 - }; - - const WebRtc_UWord8 mask38_36[216] = - { - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x51, 0x97, 0x2a, 0x32, 0xe4, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x80, 0x95, 0xc2, 0x68, 0x28, 0x00 - }; - - const WebRtc_UWord8 mask38_37[222] = - { - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x51, 0x97, 0x2a, 0x32, 0xe4, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x4c, 0x26, 0x09, 0x84, 0xc0, 0x00, - 0x66, 0x28, 0x8c, 0xc5, 0x10, 0x00, - 0x91, 0x50, 0x32, 0x2a, 0x04, 0x00, - 0x42, 0x82, 0x68, 0x50, 0x4c, 0x00, - 0xa4, 0x01, 0xd4, 0x80, 0x38, 0x00, - 0x13, 0x43, 0x02, 0x68, 0x60, 0x00, - 0x30, 0x94, 0x86, 0x12, 0x90, 0x00, - 0x88, 0xa1, 0x31, 0x14, 0x24, 0x00, - 0x09, 0x4c, 0x01, 0x29, 0x80, 0x00, - 0xcd, 0x98, 0x59, 0xb3, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask38_38[228] = - { - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x4c, 0x26, 0x09, 0x84, 0xc0, 0x00, - 0x66, 0x28, 0x8c, 0xc5, 0x10, 0x00, - 0x91, 0x50, 0x32, 0x2a, 0x04, 0x00, - 0x42, 0x82, 0x68, 0x50, 0x4c, 0x00, - 0xa4, 0x01, 0xd4, 0x80, 0x38, 0x00, - 0x13, 0x43, 0x02, 0x68, 0x60, 0x00, - 0x30, 0x94, 0x86, 0x12, 0x90, 0x00, - 0x88, 0xa1, 0x31, 0x14, 0x24, 0x00, - 0x09, 0x4c, 0x01, 0x29, 0x80, 0x00, - 0xcd, 0x98, 0x59, 0xb3, 0x08, 0x00, - 0x4c, 0x27, 0x09, 0x84, 0xe0, 0x00, - 0x66, 0x71, 0x8c, 0xce, 0x30, 0x00, - 0x91, 0x40, 0xf2, 0x28, 0x1c, 0x00, - 0x42, 0x90, 0xa8, 0x52, 0x14, 0x00, - 0xa4, 0x29, 0x54, 0x85, 0x28, 0x00, - 0x13, 0x5a, 0x02, 0x6b, 0x40, 0x00, - 0x30, 0x93, 0x46, 0x12, 0x68, 0x00, - 0x88, 0xac, 0x31, 0x15, 0x84, 0x00, - 0x09, 0x0c, 0xc1, 0x21, 0x98, 0x00, - 0x2c, 0x16, 0x05, 0x82, 0xc0, 0x00, - 0x91, 0x40, 0xd2, 0x28, 0x18, 0x00, - 0xc0, 0xd0, 0x38, 0x1a, 0x04, 0x00, - 0x06, 0x82, 0xa0, 0xd0, 0x54, 0x00, - 0xc8, 0x0c, 0x59, 0x01, 0x88, 0x00, - 0x45, 0x61, 0x08, 0xac, 0x20, 0x00, - 0x30, 0x91, 0x46, 0x12, 0x28, 0x00, - 0xa2, 0x28, 0x34, 0x45, 0x04, 0x00, - 0x51, 0x97, 0x2a, 0x32, 0xe4, 0x00, - 0x8c, 0xed, 0x11, 0x5f, 0x24, 0x00 - }; - - const WebRtc_UWord8 mask38_4[24] = - { - 0xec, 0x73, 0x1d, 0x8e, 0x60, 0x00, - 0x67, 0x19, 0xcc, 0xe3, 0x38, 0x00, - 0xb1, 0xcc, 0x76, 0x39, 0x8c, 0x00, - 0x5a, 0x96, 0xab, 0x52, 0xd4, 0x00 - }; - - const WebRtc_UWord8 mask38_5[30] = - { - 0x4c, 0xe7, 0x09, 0x9c, 0xe0, 0x00, - 0x66, 0x31, 0xcc, 0xc6, 0x38, 0x00, - 0xa1, 0xcc, 0x74, 0x39, 0x8c, 0x00, - 0x92, 0xa6, 0xb2, 0x54, 0xd4, 0x00, - 0xb8, 0x99, 0x97, 0x13, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask38_6[36] = - { - 0x4c, 0x36, 0x09, 0x86, 0xc0, 0x00, - 0x45, 0x68, 0x88, 0xad, 0x10, 0x00, - 0x30, 0xd0, 0x66, 0x1a, 0x0c, 0x00, - 0x8a, 0x82, 0xb1, 0x50, 0x54, 0x00, - 0x26, 0x0b, 0x44, 0xc1, 0x68, 0x00, - 0x95, 0x45, 0x12, 0xa8, 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask38_7[42] = - { - 0xc4, 0xa3, 0x18, 0x94, 0x60, 0x00, - 0x23, 0x19, 0x84, 0x63, 0x30, 0x00, - 0x91, 0x1c, 0x32, 0x23, 0x84, 0x00, - 0x4a, 0x82, 0xa9, 0x50, 0x54, 0x00, - 0x34, 0x49, 0x46, 0x89, 0x28, 0x00, - 0x8b, 0x4a, 0x11, 0x69, 0x40, 0x00, - 0xc8, 0x24, 0xd9, 0x04, 0x98, 0x00 - }; - - const WebRtc_UWord8 mask38_8[48] = - { - 0x16, 0x13, 0x82, 0xc2, 0x70, 0x00, - 0xc2, 0x44, 0xd8, 0x48, 0x98, 0x00, - 0x60, 0xe8, 0x2c, 0x1d, 0x04, 0x00, - 0x85, 0x12, 0x70, 0xa2, 0x4c, 0x00, - 0xcc, 0x21, 0x59, 0x84, 0x28, 0x00, - 0x29, 0x63, 0x05, 0x2c, 0x60, 0x00, - 0x11, 0x98, 0xc2, 0x33, 0x18, 0x00, - 0xb0, 0x0c, 0x76, 0x01, 0x8c, 0x00 - }; - - const WebRtc_UWord8 mask38_9[54] = - { - 0x44, 0xa7, 0x08, 0x94, 0xe0, 0x00, - 0x66, 0x70, 0x8c, 0xce, 0x10, 0x00, - 0x12, 0xc0, 0xe2, 0x58, 0x1c, 0x00, - 0xc3, 0x10, 0xb8, 0x62, 0x14, 0x00, - 0x8c, 0x29, 0x51, 0x85, 0x28, 0x00, - 0x11, 0x5b, 0x02, 0x2b, 0x60, 0x00, - 0x21, 0x93, 0x44, 0x32, 0x68, 0x00, - 0xa2, 0x2c, 0x14, 0x45, 0x80, 0x00, - 0x18, 0x0c, 0xe3, 0x01, 0x9c, 0x00 - }; - - const WebRtc_UWord8 mask39_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00 - }; - - const WebRtc_UWord8 mask39_10[60] = - { - 0x8c, 0xe3, 0x09, 0x82, 0x60, 0x00, - 0x27, 0x11, 0xca, 0x22, 0x88, 0x00, - 0x32, 0x8d, 0x34, 0x0d, 0x02, 0x00, - 0x61, 0x92, 0x60, 0x98, 0x26, 0x00, - 0x5c, 0x38, 0x80, 0x70, 0x1c, 0x00, - 0xcc, 0x75, 0x10, 0xc4, 0x30, 0x00, - 0x2b, 0x19, 0xc5, 0x21, 0x48, 0x00, - 0x32, 0xd2, 0x68, 0x4a, 0x12, 0x00, - 0x25, 0x8e, 0xb3, 0x04, 0xc0, 0x00, - 0x50, 0x88, 0xc6, 0x11, 0x84, 0x00 - }; - - const WebRtc_UWord8 mask39_11[66] = - { - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask39_12[72] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x90, 0xc8, 0x9e, 0xbb, 0x88, 0x00 - }; - - const WebRtc_UWord8 mask39_13[78] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask39_14[84] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x6e, 0x27, 0x6a, 0xc7, 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask39_15[90] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00 - }; - - const WebRtc_UWord8 mask39_16[96] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x7e, 0x75, 0xe5, 0x03, 0x8c, 0x00 - }; - - const WebRtc_UWord8 mask39_17[102] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00 - }; - - const WebRtc_UWord8 mask39_18[108] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x51, 0x97, 0x24, 0x2f, 0x7e, 0x00 - }; - - const WebRtc_UWord8 mask39_19[114] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x4c, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x66, 0x28, 0x8a, 0x22, 0x88, 0x00, - 0x91, 0x50, 0x34, 0x0d, 0x02, 0x00, - 0x42, 0x82, 0x60, 0x98, 0x26, 0x00, - 0xa4, 0x01, 0xc0, 0x70, 0x1c, 0x00, - 0x13, 0x43, 0x10, 0xc4, 0x30, 0x00, - 0x30, 0x94, 0x85, 0x21, 0x48, 0x00, - 0x88, 0xa1, 0x28, 0x4a, 0x12, 0x00, - 0x09, 0x4c, 0x13, 0x04, 0xc0, 0x00, - 0xcd, 0x98, 0x46, 0x11, 0x84, 0x00 - }; - - const WebRtc_UWord8 mask39_2[12] = - { - 0xce, 0x77, 0x1d, 0xc7, 0x70, 0x00, - 0x39, 0xcc, 0xf3, 0x3c, 0xce, 0x00 - }; - - const WebRtc_UWord8 mask39_20[120] = - { - 0x8c, 0xe3, 0x09, 0x82, 0x60, 0x00, - 0x27, 0x11, 0xca, 0x22, 0x88, 0x00, - 0x32, 0x8d, 0x34, 0x0d, 0x02, 0x00, - 0x61, 0x92, 0x60, 0x98, 0x26, 0x00, - 0x5c, 0x38, 0x80, 0x70, 0x1c, 0x00, - 0xcc, 0x75, 0x10, 0xc4, 0x30, 0x00, - 0x2b, 0x19, 0xc5, 0x21, 0x48, 0x00, - 0x32, 0xd2, 0x68, 0x4a, 0x12, 0x00, - 0x25, 0x8e, 0xb3, 0x04, 0xc0, 0x00, - 0x50, 0x88, 0xc6, 0x11, 0x84, 0x00, - 0x44, 0xa7, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x70, 0x8c, 0x47, 0x18, 0x00, - 0x12, 0xc0, 0xf0, 0x3c, 0x0e, 0x00, - 0xc3, 0x10, 0xbc, 0x29, 0x0a, 0x00, - 0x8c, 0x29, 0x42, 0x72, 0x94, 0x00, - 0x11, 0x5b, 0x16, 0x85, 0xa0, 0x00, - 0x21, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0xa2, 0x2c, 0x0b, 0x0a, 0xc2, 0x00, - 0x18, 0x0c, 0xe9, 0x30, 0xca, 0x00, - 0x0d, 0xba, 0x52, 0x38, 0xbc, 0x00 - }; - - const WebRtc_UWord8 mask39_21[126] = - { - 0x8c, 0xe3, 0x09, 0x82, 0x60, 0x00, - 0x27, 0x11, 0xca, 0x22, 0x88, 0x00, - 0x32, 0x8d, 0x34, 0x0d, 0x02, 0x00, - 0x61, 0x92, 0x60, 0x98, 0x26, 0x00, - 0x5c, 0x38, 0x80, 0x70, 0x1c, 0x00, - 0xcc, 0x75, 0x10, 0xc4, 0x30, 0x00, - 0x2b, 0x19, 0xc5, 0x21, 0x48, 0x00, - 0x32, 0xd2, 0x68, 0x4a, 0x12, 0x00, - 0x25, 0x8e, 0xb3, 0x04, 0xc0, 0x00, - 0x50, 0x88, 0xc6, 0x11, 0x84, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00 - }; - - const WebRtc_UWord8 mask39_22[132] = - { - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x09, 0x82, 0x60, 0x00, - 0x27, 0x11, 0xca, 0x22, 0x88, 0x00, - 0x32, 0x8d, 0x34, 0x0d, 0x02, 0x00, - 0x61, 0x92, 0x60, 0x98, 0x26, 0x00, - 0x5c, 0x38, 0x80, 0x70, 0x1c, 0x00, - 0xcc, 0x75, 0x10, 0xc4, 0x30, 0x00, - 0x2b, 0x19, 0xc5, 0x21, 0x48, 0x00, - 0x32, 0xd2, 0x68, 0x4a, 0x12, 0x00, - 0x25, 0x8e, 0xb3, 0x04, 0xc0, 0x00, - 0x50, 0x88, 0xc6, 0x11, 0x84, 0x00, - 0xfc, 0x5a, 0xb2, 0x13, 0x12, 0x00 - }; - - const WebRtc_UWord8 mask39_23[138] = - { - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x90, 0xc8, 0x9e, 0xbb, 0x88, 0x00 - }; - - const WebRtc_UWord8 mask39_24[144] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x90, 0xc8, 0x9e, 0xbb, 0x88, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0xac, 0xbc, 0xf0, 0xff, 0x62, 0x00 - }; - - const WebRtc_UWord8 mask39_25[150] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x90, 0xc8, 0x9e, 0xbb, 0x88, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00 - }; - - const WebRtc_UWord8 mask39_26[156] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0xe3, 0x18, 0xc6, 0x30, 0x00, - 0x27, 0x11, 0xc4, 0x71, 0x1c, 0x00, - 0x32, 0x8d, 0x23, 0x48, 0xd2, 0x00, - 0x61, 0x92, 0x64, 0x99, 0x26, 0x00, - 0x5c, 0x38, 0x8e, 0x23, 0x88, 0x00, - 0x90, 0xc8, 0x9e, 0xbb, 0x88, 0x00, - 0x10, 0x17, 0x44, 0x72, 0xec, 0x00 - }; - - const WebRtc_UWord8 mask39_27[162] = - { - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x6e, 0x27, 0x6a, 0xc7, 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask39_28[168] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x6e, 0x27, 0x6a, 0xc7, 0xc4, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x86, 0xb6, 0x04, 0xbc, 0x1e, 0x00 - }; - - const WebRtc_UWord8 mask39_29[174] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x6e, 0x27, 0x6a, 0xc7, 0xc4, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00 - }; - - const WebRtc_UWord8 mask39_3[18] = - { - 0xcc, 0x67, 0x19, 0xc6, 0x70, 0x00, - 0x27, 0x2c, 0xca, 0xb2, 0xac, 0x00, - 0x92, 0xd2, 0x76, 0x2d, 0x46, 0x00 - }; - - const WebRtc_UWord8 mask39_30[180] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x84, 0x87, 0x01, 0xc0, 0x70, 0x00, - 0x27, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x51, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x1a, 0x22, 0xa8, 0xaa, 0x2a, 0x00, - 0x68, 0x44, 0x51, 0x14, 0x44, 0x00, - 0x89, 0x70, 0x1c, 0x07, 0x00, 0x00, - 0x6e, 0x27, 0x6a, 0xc7, 0xc4, 0x00, - 0xb3, 0x1d, 0x13, 0x03, 0x5a, 0x00 - }; - - const WebRtc_UWord8 mask39_31[186] = - { - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x7e, 0x75, 0xe5, 0x03, 0x8c, 0x00 - }; - - const WebRtc_UWord8 mask39_32[192] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x7e, 0x75, 0xe5, 0x03, 0x8c, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x83, 0x1a, 0x3c, 0x2a, 0x7a, 0x00 - }; - - const WebRtc_UWord8 mask39_33[198] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x7e, 0x75, 0xe5, 0x03, 0x8c, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00 - }; - - const WebRtc_UWord8 mask39_34[204] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x8c, 0x23, 0x08, 0xc2, 0x30, 0x00, - 0x47, 0x19, 0x86, 0x61, 0x98, 0x00, - 0x81, 0x88, 0x62, 0x18, 0x86, 0x00, - 0x12, 0x86, 0x21, 0x88, 0x62, 0x00, - 0x58, 0x14, 0x45, 0x11, 0x44, 0x00, - 0x28, 0xca, 0x12, 0x84, 0xa0, 0x00, - 0x34, 0x60, 0x98, 0x26, 0x08, 0x00, - 0x7e, 0x75, 0xe5, 0x03, 0x8c, 0x00, - 0xc6, 0xbb, 0x7e, 0xd9, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask39_35[210] = - { - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x51, 0x97, 0x24, 0x2f, 0x7e, 0x00 - }; - - const WebRtc_UWord8 mask39_36[216] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x51, 0x97, 0x24, 0x2f, 0x7e, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x11, 0x78, 0xfe, 0x43, 0xd6, 0x00 - }; - - const WebRtc_UWord8 mask39_37[222] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x51, 0x97, 0x24, 0x2f, 0x7e, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x4c, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x66, 0x28, 0x8a, 0x22, 0x88, 0x00, - 0x91, 0x50, 0x34, 0x0d, 0x02, 0x00, - 0x42, 0x82, 0x60, 0x98, 0x26, 0x00, - 0xa4, 0x01, 0xc0, 0x70, 0x1c, 0x00, - 0x13, 0x43, 0x10, 0xc4, 0x30, 0x00, - 0x30, 0x94, 0x85, 0x21, 0x48, 0x00, - 0x88, 0xa1, 0x28, 0x4a, 0x12, 0x00, - 0x09, 0x4c, 0x13, 0x04, 0xc0, 0x00, - 0xcd, 0x98, 0x46, 0x11, 0x84, 0x00 - }; - - const WebRtc_UWord8 mask39_38[228] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x4c, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x66, 0x28, 0x8a, 0x22, 0x88, 0x00, - 0x91, 0x50, 0x34, 0x0d, 0x02, 0x00, - 0x42, 0x82, 0x60, 0x98, 0x26, 0x00, - 0xa4, 0x01, 0xc0, 0x70, 0x1c, 0x00, - 0x13, 0x43, 0x10, 0xc4, 0x30, 0x00, - 0x30, 0x94, 0x85, 0x21, 0x48, 0x00, - 0x88, 0xa1, 0x28, 0x4a, 0x12, 0x00, - 0x09, 0x4c, 0x13, 0x04, 0xc0, 0x00, - 0xcd, 0x98, 0x46, 0x11, 0x84, 0x00, - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x2c, 0x16, 0x05, 0x81, 0x60, 0x00, - 0x91, 0x40, 0xd0, 0x34, 0x0c, 0x00, - 0xc0, 0xd0, 0x34, 0x0d, 0x02, 0x00, - 0x06, 0x82, 0xa0, 0xa8, 0x2a, 0x00, - 0xc8, 0x0c, 0x43, 0x10, 0xc4, 0x00, - 0x45, 0x61, 0x18, 0x46, 0x10, 0x00, - 0x30, 0x91, 0x44, 0x51, 0x14, 0x00, - 0xa2, 0x28, 0x2a, 0x0a, 0x82, 0x00, - 0x51, 0x97, 0x24, 0x2f, 0x7e, 0x00, - 0x9e, 0xd8, 0x3c, 0x7e, 0x2e, 0x00 - }; - - const WebRtc_UWord8 mask39_39[234] = - { - 0x4c, 0x27, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x71, 0x9c, 0x67, 0x18, 0x00, - 0x91, 0x40, 0xf0, 0x3c, 0x0e, 0x00, - 0x42, 0x90, 0xa4, 0x29, 0x0a, 0x00, - 0xa4, 0x29, 0x4a, 0x52, 0x94, 0x00, - 0x13, 0x5a, 0x16, 0x85, 0xa0, 0x00, - 0x30, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0x88, 0xac, 0x2b, 0x0a, 0xc2, 0x00, - 0x09, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x4c, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x66, 0x28, 0x8a, 0x22, 0x88, 0x00, - 0x91, 0x50, 0x34, 0x0d, 0x02, 0x00, - 0x42, 0x82, 0x60, 0x98, 0x26, 0x00, - 0xa4, 0x01, 0xc0, 0x70, 0x1c, 0x00, - 0x13, 0x43, 0x10, 0xc4, 0x30, 0x00, - 0x30, 0x94, 0x85, 0x21, 0x48, 0x00, - 0x88, 0xa1, 0x28, 0x4a, 0x12, 0x00, - 0x09, 0x4c, 0x13, 0x04, 0xc0, 0x00, - 0xcd, 0x98, 0x46, 0x11, 0x84, 0x00, - 0x4c, 0x27, 0x09, 0x82, 0x60, 0x00, - 0x66, 0x71, 0x8a, 0x22, 0x88, 0x00, - 0x91, 0x40, 0xf4, 0x0d, 0x02, 0x00, - 0x42, 0x90, 0xa0, 0x98, 0x26, 0x00, - 0xa4, 0x29, 0x40, 0x70, 0x1c, 0x00, - 0x13, 0x5a, 0x10, 0xc4, 0x30, 0x00, - 0x30, 0x93, 0x45, 0x21, 0x48, 0x00, - 0x88, 0xac, 0x28, 0x4a, 0x12, 0x00, - 0x09, 0x0c, 0xd3, 0x04, 0xc0, 0x00, - 0x4c, 0x26, 0x06, 0x11, 0x84, 0x00, - 0x66, 0x28, 0x89, 0xc2, 0x70, 0x00, - 0x91, 0x50, 0x3c, 0x67, 0x18, 0x00, - 0x42, 0x82, 0x70, 0x3c, 0x0e, 0x00, - 0xa4, 0x01, 0xc4, 0x29, 0x0a, 0x00, - 0x13, 0x43, 0x0a, 0x52, 0x94, 0x00, - 0x30, 0x94, 0x96, 0x85, 0xa0, 0x00, - 0x88, 0xa1, 0x24, 0xd1, 0x34, 0x00, - 0x09, 0x4c, 0x0b, 0x0a, 0xc2, 0x00, - 0xcd, 0x98, 0x43, 0x30, 0xcc, 0x00, - 0x1d, 0x04, 0x3e, 0xf1, 0xb4, 0x00 - }; - - const WebRtc_UWord8 mask39_4[24] = - { - 0xec, 0x73, 0x1c, 0xc7, 0x30, 0x00, - 0x67, 0x19, 0xc6, 0x71, 0x9c, 0x00, - 0xb1, 0xcc, 0x73, 0x1c, 0xc6, 0x00, - 0x5a, 0x96, 0xa5, 0xa9, 0x6a, 0x00 - }; - - const WebRtc_UWord8 mask39_5[30] = - { - 0x4c, 0xe7, 0x19, 0xc6, 0x70, 0x00, - 0x66, 0x31, 0xcc, 0x73, 0x1c, 0x00, - 0xa1, 0xcc, 0x73, 0x1c, 0xa6, 0x00, - 0x92, 0xa6, 0xa5, 0x6a, 0x6a, 0x00, - 0xb8, 0x99, 0x96, 0x8b, 0x94, 0x00 - }; - - const WebRtc_UWord8 mask39_6[36] = - { - 0x4c, 0x36, 0x09, 0x83, 0x60, 0x00, - 0x45, 0x68, 0x8a, 0x26, 0x88, 0x00, - 0x30, 0xd0, 0x64, 0x1d, 0x06, 0x00, - 0x8a, 0x82, 0xb0, 0xa8, 0x2a, 0x00, - 0x26, 0x0b, 0x40, 0xd0, 0xd4, 0x00, - 0x95, 0x45, 0x13, 0x44, 0x30, 0x00 - }; - - const WebRtc_UWord8 mask39_7[42] = - { - 0xc4, 0xa3, 0x09, 0xc2, 0x30, 0x00, - 0x23, 0x19, 0x86, 0x65, 0x80, 0x00, - 0x91, 0x1c, 0x22, 0x01, 0xd6, 0x00, - 0x4a, 0x82, 0xb0, 0x2a, 0x2a, 0x00, - 0x34, 0x49, 0x44, 0x98, 0x94, 0x00, - 0x8b, 0x4a, 0x1a, 0x84, 0x60, 0x00, - 0xc8, 0x24, 0xc1, 0x94, 0x4c, 0x00 - }; - - const WebRtc_UWord8 mask39_8[48] = - { - 0x16, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xc2, 0x44, 0xd1, 0x34, 0x4c, 0x00, - 0x60, 0xe8, 0x3a, 0x0e, 0x82, 0x00, - 0x85, 0x12, 0x64, 0x99, 0x26, 0x00, - 0xcc, 0x21, 0x5c, 0x52, 0x14, 0x00, - 0x29, 0x63, 0x18, 0xc6, 0x30, 0x00, - 0x11, 0x98, 0xc6, 0x31, 0x8c, 0x00, - 0xb0, 0x0c, 0x63, 0x18, 0xc6, 0x00 - }; - - const WebRtc_UWord8 mask39_9[54] = - { - 0x44, 0xa7, 0x09, 0xc2, 0x70, 0x00, - 0x66, 0x70, 0x8c, 0x47, 0x18, 0x00, - 0x12, 0xc0, 0xf0, 0x3c, 0x0e, 0x00, - 0xc3, 0x10, 0xbc, 0x29, 0x0a, 0x00, - 0x8c, 0x29, 0x42, 0x72, 0x94, 0x00, - 0x11, 0x5b, 0x16, 0x85, 0xa0, 0x00, - 0x21, 0x93, 0x44, 0xd1, 0x34, 0x00, - 0xa2, 0x2c, 0x0b, 0x0a, 0xc2, 0x00, - 0x18, 0x0c, 0xe9, 0x30, 0xca, 0x00 - }; - - const WebRtc_UWord8 mask3_1[2] = - { - 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask3_2[4] = - { - 0xc0, 0x00, - 0xa0, 0x00 - }; - - const WebRtc_UWord8 mask3_3[6] = - { - 0xc0, 0x00, - 0xa0, 0x00, - 0x60, 0x00 - }; - - const WebRtc_UWord8 mask40_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 - }; - - const WebRtc_UWord8 mask40_10[60] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00 - }; - - const WebRtc_UWord8 mask40_11[66] = - { - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask40_12[72] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0xf5, 0xdc, 0x4f, 0x5d, 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask40_13[78] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00 - }; - - const WebRtc_UWord8 mask40_14[84] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x56, 0x3e, 0x25, 0x63, 0xe2, 0x00 - }; - - const WebRtc_UWord8 mask40_15[90] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00 - }; - - const WebRtc_UWord8 mask40_16[96] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x28, 0x1c, 0x62, 0x81, 0xc6, 0x00 - }; - - const WebRtc_UWord8 mask40_17[102] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00 - }; - - const WebRtc_UWord8 mask40_18[108] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x21, 0x7b, 0xf2, 0x17, 0xbf, 0x00 - }; - - const WebRtc_UWord8 mask40_19[114] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00 - }; - - const WebRtc_UWord8 mask40_2[12] = - { - 0xee, 0x3b, 0x8e, 0xe3, 0xb8, 0x00, - 0x99, 0xe6, 0x79, 0x9e, 0x67, 0x00 - }; - - const WebRtc_UWord8 mask40_20[120] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0xf7, 0x8d, 0xaf, 0x78, 0xda, 0x00 - }; - - const WebRtc_UWord8 mask40_21[126] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00 - }; - - const WebRtc_UWord8 mask40_22[132] = - { - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x89, 0xee, 0x1f, 0x38, 0xca, 0x00 - }; - - const WebRtc_UWord8 mask40_23[138] = - { - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0xf5, 0xdc, 0x4f, 0x5d, 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask40_24[144] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0xf5, 0xdc, 0x4f, 0x5d, 0xc4, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x68, 0xde, 0x83, 0xa9, 0xcf, 0x00 - }; - - const WebRtc_UWord8 mask40_25[150] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0xf5, 0xdc, 0x4f, 0x5d, 0xc4, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00 - }; - - const WebRtc_UWord8 mask40_26[156] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8e, 0x00, - 0x1a, 0x46, 0x91, 0xa4, 0x69, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0x71, 0x1c, 0x47, 0x11, 0xc4, 0x00, - 0xf5, 0xdc, 0x4f, 0x5d, 0xc4, 0x00, - 0x06, 0x8e, 0x8c, 0x1a, 0xd2, 0x00 - }; - - const WebRtc_UWord8 mask40_27[162] = - { - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x56, 0x3e, 0x25, 0x63, 0xe2, 0x00 - }; - - const WebRtc_UWord8 mask40_28[168] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x56, 0x3e, 0x25, 0x63, 0xe2, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x68, 0x0e, 0x9b, 0x52, 0xb6, 0x00 - }; - - const WebRtc_UWord8 mask40_29[174] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x56, 0x3e, 0x25, 0x63, 0xe2, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00 - }; - - const WebRtc_UWord8 mask40_3[18] = - { - 0xce, 0x33, 0x8c, 0xe3, 0x38, 0x00, - 0x55, 0x95, 0x65, 0x59, 0x56, 0x00, - 0xb1, 0x6a, 0x3b, 0x16, 0xa3, 0x00 - }; - - const WebRtc_UWord8 mask40_30[180] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x0e, 0x03, 0x80, 0xe0, 0x38, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x45, 0x51, 0x54, 0x55, 0x15, 0x00, - 0x88, 0xa2, 0x28, 0x8a, 0x22, 0x00, - 0xe0, 0x38, 0x0e, 0x03, 0x80, 0x00, - 0x56, 0x3e, 0x25, 0x63, 0xe2, 0x00, - 0xe1, 0x47, 0x04, 0x05, 0x47, 0x00 - }; - - const WebRtc_UWord8 mask40_31[186] = - { - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x28, 0x1c, 0x62, 0x81, 0xc6, 0x00 - }; - - const WebRtc_UWord8 mask40_32[192] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x28, 0x1c, 0x62, 0x81, 0xc6, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x03, 0x0c, 0x46, 0x10, 0xc5, 0x00 - }; - - const WebRtc_UWord8 mask40_33[198] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x28, 0x1c, 0x62, 0x81, 0xc6, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00 - }; - - const WebRtc_UWord8 mask40_34[204] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x18, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x43, 0x00, - 0x0c, 0x43, 0x10, 0xc4, 0x31, 0x00, - 0x28, 0x8a, 0x22, 0x88, 0xa2, 0x00, - 0x94, 0x25, 0x09, 0x42, 0x50, 0x00, - 0xc1, 0x30, 0x4c, 0x13, 0x04, 0x00, - 0x28, 0x1c, 0x62, 0x81, 0xc6, 0x00, - 0x87, 0x3c, 0x08, 0x19, 0x31, 0x00 - }; - - const WebRtc_UWord8 mask40_35[210] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x21, 0x7b, 0xf2, 0x17, 0xbf, 0x00 - }; - - const WebRtc_UWord8 mask40_36[216] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x21, 0x7b, 0xf2, 0x17, 0xbf, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x1e, 0xb9, 0x3d, 0x25, 0xcc, 0x00 - }; - - const WebRtc_UWord8 mask40_37[222] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x21, 0x7b, 0xf2, 0x17, 0xbf, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00 - }; - - const WebRtc_UWord8 mask40_38[228] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0xb0, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x06, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x88, 0x62, 0x00, - 0xc2, 0x30, 0x8c, 0x23, 0x08, 0x00, - 0x22, 0x88, 0xa2, 0x28, 0x8a, 0x00, - 0x50, 0x54, 0x15, 0x05, 0x41, 0x00, - 0x21, 0x7b, 0xf2, 0x17, 0xbf, 0x00, - 0xea, 0xaa, 0x20, 0xa2, 0x1b, 0x00 - }; - - const WebRtc_UWord8 mask40_39[234] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0xf7, 0x8d, 0xaf, 0x78, 0xda, 0x00 - }; - - const WebRtc_UWord8 mask40_4[24] = - { - 0xe6, 0x39, 0x8e, 0x63, 0x98, 0x00, - 0x33, 0x8c, 0xe3, 0x38, 0xce, 0x00, - 0x98, 0xe6, 0x39, 0x8e, 0x63, 0x00, - 0x2d, 0x4b, 0x52, 0xd4, 0xb5, 0x00 - }; - - const WebRtc_UWord8 mask40_40[240] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0xf7, 0x8d, 0xaf, 0x78, 0xda, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0xe3, 0x38, 0xce, 0x33, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x85, 0x00, - 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x19, 0x86, 0x61, 0x98, 0x66, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x30, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x44, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0x81, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x38, 0x0e, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x18, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0xa4, 0x00, - 0x42, 0x50, 0x94, 0x25, 0x09, 0x00, - 0x98, 0x26, 0x09, 0x82, 0x60, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0xc2, 0x00, - 0xa6, 0xf3, 0xab, 0x1b, 0x87, 0x00 - }; - - const WebRtc_UWord8 mask40_5[30] = - { - 0xce, 0x33, 0x8c, 0xe3, 0x38, 0x00, - 0x63, 0x98, 0xe6, 0x39, 0x8e, 0x00, - 0x98, 0xe5, 0x39, 0x8e, 0x53, 0x00, - 0x2b, 0x53, 0x52, 0xb5, 0x35, 0x00, - 0xb4, 0x5c, 0xab, 0x45, 0xca, 0x00 - }; - - const WebRtc_UWord8 mask40_6[36] = - { - 0x4c, 0x1b, 0x04, 0xc1, 0xb0, 0x00, - 0x51, 0x34, 0x45, 0x13, 0x44, 0x00, - 0x20, 0xe8, 0x32, 0x0e, 0x83, 0x00, - 0x85, 0x41, 0x58, 0x54, 0x15, 0x00, - 0x06, 0x86, 0xa0, 0x68, 0x6a, 0x00, - 0x9a, 0x21, 0x89, 0xa2, 0x18, 0x00 - }; - - const WebRtc_UWord8 mask40_7[42] = - { - 0x4e, 0x11, 0x84, 0xe1, 0x18, 0x00, - 0x33, 0x2c, 0x03, 0x32, 0xc0, 0x00, - 0x10, 0x0e, 0xb1, 0x00, 0xeb, 0x00, - 0x81, 0x51, 0x58, 0x15, 0x15, 0x00, - 0x24, 0xc4, 0xa2, 0x4c, 0x4a, 0x00, - 0xd4, 0x23, 0x0d, 0x42, 0x30, 0x00, - 0x0c, 0xa2, 0x60, 0xca, 0x26, 0x00 - }; - - const WebRtc_UWord8 mask40_8[48] = - { - 0x27, 0x09, 0xc2, 0x70, 0x9c, 0x00, - 0x89, 0xa2, 0x68, 0x9a, 0x26, 0x00, - 0xd0, 0x74, 0x1d, 0x07, 0x41, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x93, 0x00, - 0xe2, 0x90, 0xae, 0x29, 0x0a, 0x00, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x00, - 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x00 - }; - - const WebRtc_UWord8 mask40_9[54] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x38, 0x00, - 0x62, 0x38, 0xc6, 0x23, 0x8c, 0x00, - 0x81, 0xe0, 0x78, 0x1e, 0x07, 0x00, - 0xe1, 0x48, 0x5e, 0x14, 0x85, 0x00, - 0x13, 0x94, 0xa1, 0x39, 0x4a, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0xd0, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x9a, 0x00, - 0x58, 0x56, 0x15, 0x85, 0x61, 0x00, - 0x49, 0x86, 0x54, 0x98, 0x65, 0x00 - }; - - const WebRtc_UWord8 mask41_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 - }; - - const WebRtc_UWord8 mask41_10[60] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80 - }; - - const WebRtc_UWord8 mask41_11[66] = - { - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00 - }; - - const WebRtc_UWord8 mask41_12[72] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0xf5, 0xdc, 0x4a, 0x06, 0x51, 0x80 - }; - - const WebRtc_UWord8 mask41_13[78] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80 - }; - - const WebRtc_UWord8 mask41_14[84] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x56, 0x3e, 0x24, 0xdd, 0x0c, 0x00 - }; - - const WebRtc_UWord8 mask41_15[90] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80 - }; - - const WebRtc_UWord8 mask41_16[96] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x28, 0x1c, 0x63, 0xbf, 0x53, 0x80 - }; - - const WebRtc_UWord8 mask41_17[102] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask41_18[108] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x21, 0x7b, 0xf5, 0xa5, 0x65, 0x80 - }; - - const WebRtc_UWord8 mask41_19[114] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80 - }; - - const WebRtc_UWord8 mask41_2[12] = - { - 0xee, 0x3b, 0x8e, 0xe3, 0xb3, 0x00, - 0x99, 0xe6, 0x79, 0x9e, 0x6e, 0x80 - }; - - const WebRtc_UWord8 mask41_20[120] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0xf7, 0x8d, 0xa2, 0xa0, 0x33, 0x00 - }; - - const WebRtc_UWord8 mask41_21[126] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00 - }; - - const WebRtc_UWord8 mask41_22[132] = - { - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x33, 0x09, 0x6e, 0x49, 0x6b, 0x80 - }; - - const WebRtc_UWord8 mask41_23[138] = - { - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0xf5, 0xdc, 0x4a, 0x06, 0x51, 0x80 - }; - - const WebRtc_UWord8 mask41_24[144] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0xf5, 0xdc, 0x4a, 0x06, 0x51, 0x80, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x45, 0xa6, 0xef, 0xc9, 0xc3, 0x00 - }; - - const WebRtc_UWord8 mask41_25[150] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0xf5, 0xdc, 0x4a, 0x06, 0x51, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80 - }; - - const WebRtc_UWord8 mask41_26[156] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0xc6, 0x31, 0x8c, 0x62, 0x1a, 0x00, - 0x23, 0x88, 0xe2, 0x38, 0x8c, 0x80, - 0x1a, 0x46, 0x91, 0xa4, 0x58, 0x80, - 0x24, 0xc9, 0x32, 0x4d, 0x30, 0x80, - 0x71, 0x1c, 0x47, 0x11, 0x07, 0x00, - 0xf5, 0xdc, 0x4a, 0x06, 0x51, 0x80, - 0x6f, 0x72, 0xf1, 0xe7, 0x1a, 0x80 - }; - - const WebRtc_UWord8 mask41_27[162] = - { - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x56, 0x3e, 0x24, 0xdd, 0x0c, 0x00 - }; - - const WebRtc_UWord8 mask41_28[168] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x56, 0x3e, 0x24, 0xdd, 0x0c, 0x00, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x61, 0x2c, 0xfa, 0x25, 0x38, 0x00 - }; - - const WebRtc_UWord8 mask41_29[174] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x56, 0x3e, 0x24, 0xdd, 0x0c, 0x00, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80 - }; - - const WebRtc_UWord8 mask41_3[18] = - { - 0xce, 0x33, 0x8c, 0xe3, 0x2b, 0x00, - 0x55, 0x95, 0x65, 0x5d, 0xc5, 0x00, - 0xb1, 0x6a, 0x3a, 0x8e, 0xd8, 0x80 - }; - - const WebRtc_UWord8 mask41_30[180] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x0e, 0x03, 0x80, 0xe1, 0x91, 0x00, - 0x33, 0x0c, 0xc3, 0x31, 0x45, 0x00, - 0x10, 0xc4, 0x31, 0x0c, 0x32, 0x80, - 0x45, 0x51, 0x54, 0x56, 0x84, 0x80, - 0x88, 0xa2, 0x28, 0x88, 0x4a, 0x80, - 0xe0, 0x38, 0x0e, 0x02, 0x29, 0x00, - 0x56, 0x3e, 0x24, 0xdd, 0x0c, 0x00, - 0x59, 0x53, 0x31, 0x62, 0x15, 0x00 - }; - - const WebRtc_UWord8 mask41_31[186] = - { - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x28, 0x1c, 0x63, 0xbf, 0x53, 0x80 - }; - - const WebRtc_UWord8 mask41_32[192] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x28, 0x1c, 0x63, 0xbf, 0x53, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0xca, 0xbb, 0xcb, 0x6d, 0xaa, 0x00 - }; - - const WebRtc_UWord8 mask41_33[198] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x28, 0x1c, 0x63, 0xbf, 0x53, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask41_34[204] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x46, 0x11, 0x84, 0x61, 0x19, 0x00, - 0x33, 0x0c, 0xc3, 0x30, 0xcc, 0x80, - 0x10, 0xc4, 0x31, 0x0e, 0x46, 0x00, - 0x0c, 0x43, 0x10, 0xc6, 0x90, 0x80, - 0x28, 0x8a, 0x22, 0x89, 0x42, 0x80, - 0x94, 0x25, 0x09, 0x42, 0x13, 0x00, - 0xc1, 0x30, 0x4c, 0x10, 0x25, 0x80, - 0x28, 0x1c, 0x63, 0xbf, 0x53, 0x80, - 0xbd, 0x37, 0x3f, 0x75, 0x36, 0x80 - }; - - const WebRtc_UWord8 mask41_35[210] = - { - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x21, 0x7b, 0xf5, 0xa5, 0x65, 0x80 - }; - - const WebRtc_UWord8 mask41_36[216] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x21, 0x7b, 0xf5, 0xa5, 0x65, 0x80, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0xc1, 0xb1, 0x80, 0xbe, 0x3e, 0x00 - }; - - const WebRtc_UWord8 mask41_37[222] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x21, 0x7b, 0xf5, 0xa5, 0x65, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80 - }; - - const WebRtc_UWord8 mask41_38[228] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x2c, 0x0b, 0x02, 0xc0, 0x32, 0x00, - 0x81, 0xa0, 0x68, 0x1a, 0x01, 0x80, - 0xa0, 0x68, 0x1a, 0x06, 0x82, 0x00, - 0x05, 0x41, 0x50, 0x54, 0x15, 0x00, - 0x18, 0x86, 0x21, 0x89, 0x0c, 0x00, - 0xc2, 0x30, 0x8c, 0x20, 0x68, 0x00, - 0x22, 0x88, 0xa2, 0x29, 0x80, 0x80, - 0x50, 0x54, 0x15, 0x04, 0x50, 0x80, - 0x21, 0x7b, 0xf5, 0xa5, 0x65, 0x80, - 0xea, 0xc8, 0xbb, 0xd4, 0x5d, 0x00 - }; - - const WebRtc_UWord8 mask41_39[234] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0xf7, 0x8d, 0xa2, 0xa0, 0x33, 0x00 - }; - - const WebRtc_UWord8 mask41_4[24] = - { - 0xe6, 0x39, 0x8e, 0x63, 0x13, 0x00, - 0x33, 0x8c, 0xe3, 0x38, 0xc5, 0x80, - 0x98, 0xe6, 0x39, 0x8d, 0x2c, 0x80, - 0x2d, 0x4b, 0x52, 0xd4, 0xb2, 0x80 - }; - - const WebRtc_UWord8 mask41_40[240] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0xf7, 0x8d, 0xa2, 0xa0, 0x33, 0x00, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0xe8, 0x07, 0x18, 0x9a, 0x02, 0x00 - }; - - const WebRtc_UWord8 mask41_41[246] = - { - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xe1, 0x19, 0x00, - 0xe3, 0x38, 0xce, 0x31, 0x89, 0x80, - 0x81, 0xe0, 0x78, 0x1e, 0x30, 0x00, - 0x21, 0x48, 0x52, 0x14, 0x05, 0x80, - 0x52, 0x94, 0xa5, 0x28, 0x1e, 0x00, - 0xb4, 0x2d, 0x0b, 0x42, 0x82, 0x00, - 0x26, 0x89, 0xa2, 0x68, 0x62, 0x80, - 0x58, 0x56, 0x15, 0x86, 0x44, 0x00, - 0x19, 0x86, 0x61, 0x99, 0xe0, 0x00, - 0xf7, 0x8d, 0xa2, 0xa0, 0x33, 0x00, - 0x4c, 0x13, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x14, 0x45, 0x11, 0x45, 0x00, - 0xa0, 0x68, 0x1a, 0x06, 0xa4, 0x00, - 0x04, 0xc1, 0x30, 0x4c, 0x13, 0x00, - 0x03, 0x80, 0xe0, 0x3b, 0x40, 0x00, - 0x86, 0x21, 0x88, 0x62, 0x09, 0x00, - 0x29, 0x0a, 0x42, 0x90, 0x84, 0x80, - 0x42, 0x50, 0x94, 0x24, 0x30, 0x80, - 0x98, 0x26, 0x09, 0x81, 0x28, 0x00, - 0x30, 0x8c, 0x23, 0x08, 0x4a, 0x80, - 0x4e, 0x13, 0x84, 0xc1, 0x19, 0x00, - 0xe3, 0x38, 0xc5, 0x10, 0xcc, 0x80, - 0x81, 0xe0, 0x7a, 0x06, 0x64, 0x00, - 0x21, 0x48, 0x50, 0x4c, 0x16, 0x00, - 0x52, 0x94, 0xa0, 0x3a, 0x02, 0x80, - 0xb4, 0x2d, 0x08, 0x62, 0x11, 0x00, - 0x26, 0x89, 0xa2, 0x91, 0x01, 0x80, - 0x58, 0x56, 0x14, 0x24, 0x2a, 0x00, - 0x19, 0x86, 0x69, 0x81, 0xa0, 0x00, - 0xf7, 0x8d, 0xa3, 0x08, 0x40, 0x80, - 0x2b, 0xea, 0x4d, 0xf4, 0xc1, 0x00 - }; - - const WebRtc_UWord8 mask41_5[30] = - { - 0xce, 0x33, 0x8c, 0xe3, 0x1b, 0x00, - 0x63, 0x98, 0xe6, 0x39, 0x8d, 0x80, - 0x98, 0xe5, 0x39, 0x8c, 0x76, 0x80, - 0x2b, 0x53, 0x54, 0xd6, 0xb5, 0x00, - 0xb4, 0x5c, 0xab, 0x26, 0xca, 0x80 - }; - - const WebRtc_UWord8 mask41_6[36] = - { - 0x4c, 0x1b, 0x04, 0xc1, 0x91, 0x00, - 0x51, 0x34, 0x45, 0x11, 0x45, 0x00, - 0x20, 0xe8, 0x32, 0x0e, 0xa0, 0x80, - 0x85, 0x41, 0x58, 0x54, 0x12, 0x80, - 0x06, 0x86, 0xa0, 0x68, 0x0d, 0x80, - 0x9a, 0x21, 0x88, 0xa2, 0x43, 0x00 - }; - - const WebRtc_UWord8 mask41_7[42] = - { - 0x4e, 0x11, 0x8c, 0x61, 0x19, 0x00, - 0x33, 0x2c, 0x03, 0x30, 0x4c, 0x80, - 0x10, 0x0e, 0xb1, 0x86, 0x74, 0x00, - 0x81, 0x51, 0x54, 0x54, 0x2d, 0x00, - 0x24, 0xc4, 0xa1, 0x2d, 0x42, 0x80, - 0xd4, 0x23, 0x0b, 0x42, 0x83, 0x00, - 0x0c, 0xa2, 0x62, 0x99, 0x21, 0x80 - }; - - const WebRtc_UWord8 mask41_8[48] = - { - 0x27, 0x09, 0xc0, 0x70, 0xa7, 0x00, - 0x89, 0xa2, 0x64, 0x9a, 0x82, 0x80, - 0xd0, 0x74, 0x1b, 0x07, 0xa0, 0x00, - 0x24, 0xc9, 0x32, 0x4c, 0x5c, 0x00, - 0xe2, 0x90, 0xa5, 0x28, 0x0e, 0x80, - 0xc6, 0x31, 0x8c, 0x63, 0x18, 0x00, - 0x31, 0x8c, 0x63, 0x19, 0x41, 0x80, - 0x18, 0xc6, 0x31, 0x8c, 0x70, 0x80 - }; - - const WebRtc_UWord8 mask41_9[54] = - { - 0x4e, 0x13, 0x84, 0xe1, 0x11, 0x00, - 0x62, 0x38, 0xc6, 0x21, 0xa0, 0x80, - 0x81, 0xe0, 0x78, 0x0e, 0x94, 0x00, - 0xe1, 0x48, 0x5a, 0x15, 0x05, 0x00, - 0x13, 0x94, 0xa5, 0x30, 0x06, 0x80, - 0xb4, 0x2d, 0x0a, 0x42, 0x43, 0x00, - 0x26, 0x89, 0xa1, 0x6a, 0x08, 0x80, - 0x58, 0x56, 0x15, 0x84, 0x52, 0x00, - 0x49, 0x86, 0x52, 0x98, 0x68, 0x00 - }; - - const WebRtc_UWord8 mask42_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 - }; - - const WebRtc_UWord8 mask42_10[60] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40 - }; - - const WebRtc_UWord8 mask42_11[66] = - { - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80 - }; - - const WebRtc_UWord8 mask42_12[72] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0xa0, 0x65, 0x1d, 0x03, 0x28, 0xc0 - }; - - const WebRtc_UWord8 mask42_13[78] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0 - }; - - const WebRtc_UWord8 mask42_14[84] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4d, 0xd0, 0xc2, 0x6e, 0x86, 0x00 - }; - - const WebRtc_UWord8 mask42_15[90] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40 - }; - - const WebRtc_UWord8 mask42_16[96] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x3b, 0xf5, 0x39, 0xdf, 0xa9, 0xc0 - }; - - const WebRtc_UWord8 mask42_17[102] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask42_18[108] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x5a, 0x56, 0x5a, 0xd2, 0xb2, 0xc0 - }; - - const WebRtc_UWord8 mask42_19[114] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40 - }; - - const WebRtc_UWord8 mask42_2[12] = - { - 0xee, 0x3b, 0x37, 0x71, 0xd9, 0x80, - 0x99, 0xe6, 0xec, 0xcf, 0x37, 0x40 - }; - - const WebRtc_UWord8 mask42_20[120] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2a, 0x03, 0x31, 0x50, 0x19, 0x80 - }; - - const WebRtc_UWord8 mask42_21[126] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4c, 0x11, 0x92, 0x60, 0x8c, 0x80, - 0x51, 0x0c, 0xca, 0x88, 0x66, 0x40, - 0xa0, 0x66, 0x45, 0x03, 0x32, 0x00, - 0x04, 0xc1, 0x60, 0x26, 0x0b, 0x00, - 0x03, 0xa0, 0x28, 0x1d, 0x01, 0x40, - 0x86, 0x21, 0x14, 0x31, 0x08, 0x80, - 0x29, 0x10, 0x19, 0x48, 0x80, 0xc0, - 0x42, 0x42, 0xa2, 0x12, 0x15, 0x00, - 0x98, 0x1a, 0x04, 0xc0, 0xd0, 0x00, - 0x30, 0x84, 0x09, 0x84, 0x20, 0x40, - 0xdf, 0x4c, 0x16, 0xfa, 0x60, 0x80 - }; - - const WebRtc_UWord8 mask42_22[132] = - { - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0xdb, 0x36, 0xb0, 0x33, 0x14, 0x80 - }; - - const WebRtc_UWord8 mask42_23[138] = - { - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0xa0, 0x65, 0x1d, 0x03, 0x28, 0xc0 - }; - - const WebRtc_UWord8 mask42_24[144] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0xa0, 0x65, 0x1d, 0x03, 0x28, 0xc0, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x2e, 0x1c, 0x92, 0xbb, 0x07, 0xc0 - }; - - const WebRtc_UWord8 mask42_25[150] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0xa0, 0x65, 0x1d, 0x03, 0x28, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0 - }; - - const WebRtc_UWord8 mask42_26[156] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0xc6, 0x21, 0xa6, 0x31, 0x0d, 0x00, - 0x23, 0x88, 0xc9, 0x1c, 0x46, 0x40, - 0x1a, 0x45, 0x88, 0xd2, 0x2c, 0x40, - 0x24, 0xd3, 0x09, 0x26, 0x98, 0x40, - 0x71, 0x10, 0x73, 0x88, 0x83, 0x80, - 0xa0, 0x65, 0x1d, 0x03, 0x28, 0xc0, - 0xb8, 0x41, 0xed, 0xa3, 0x77, 0xc0 - }; - - const WebRtc_UWord8 mask42_27[162] = - { - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4d, 0xd0, 0xc2, 0x6e, 0x86, 0x00 - }; - - const WebRtc_UWord8 mask42_28[168] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4d, 0xd0, 0xc2, 0x6e, 0x86, 0x00, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0xc3, 0x3c, 0x56, 0xc2, 0x30, 0x40 - }; - - const WebRtc_UWord8 mask42_29[174] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4d, 0xd0, 0xc2, 0x6e, 0x86, 0x00, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40 - }; - - const WebRtc_UWord8 mask42_3[18] = - { - 0xce, 0x32, 0xb6, 0x71, 0x95, 0x80, - 0x55, 0xdc, 0x52, 0xae, 0xe2, 0x80, - 0xa8, 0xed, 0x8d, 0x47, 0x6c, 0x40 - }; - - const WebRtc_UWord8 mask42_30[180] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x0e, 0x19, 0x10, 0x70, 0xc8, 0x80, - 0x33, 0x14, 0x51, 0x98, 0xa2, 0x80, - 0x10, 0xc3, 0x28, 0x86, 0x19, 0x40, - 0x45, 0x68, 0x4a, 0x2b, 0x42, 0x40, - 0x88, 0x84, 0xac, 0x44, 0x25, 0x40, - 0xe0, 0x22, 0x97, 0x01, 0x14, 0x80, - 0x4d, 0xd0, 0xc2, 0x6e, 0x86, 0x00, - 0xf5, 0xdd, 0x0d, 0x58, 0xeb, 0x00 - }; - - const WebRtc_UWord8 mask42_31[186] = - { - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x3b, 0xf5, 0x39, 0xdf, 0xa9, 0xc0 - }; - - const WebRtc_UWord8 mask42_32[192] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x3b, 0xf5, 0x39, 0xdf, 0xa9, 0xc0, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0xf9, 0x1f, 0xb6, 0xe1, 0x09, 0xc0 - }; - - const WebRtc_UWord8 mask42_33[198] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x3b, 0xf5, 0x39, 0xdf, 0xa9, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask42_34[204] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x46, 0x11, 0x92, 0x30, 0x8c, 0x80, - 0x33, 0x0c, 0xc9, 0x98, 0x66, 0x40, - 0x10, 0xe4, 0x60, 0x87, 0x23, 0x00, - 0x0c, 0x69, 0x08, 0x63, 0x48, 0x40, - 0x28, 0x94, 0x29, 0x44, 0xa1, 0x40, - 0x94, 0x21, 0x34, 0xa1, 0x09, 0x80, - 0xc1, 0x02, 0x5e, 0x08, 0x12, 0xc0, - 0x3b, 0xf5, 0x39, 0xdf, 0xa9, 0xc0, - 0xf8, 0xbf, 0xf6, 0x76, 0x1b, 0x80 - }; - - const WebRtc_UWord8 mask42_35[210] = - { - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x5a, 0x56, 0x5a, 0xd2, 0xb2, 0xc0 - }; - - const WebRtc_UWord8 mask42_36[216] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x5a, 0x56, 0x5a, 0xd2, 0xb2, 0xc0, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x57, 0xc7, 0x03, 0xf9, 0xc6, 0x00 - }; - - const WebRtc_UWord8 mask42_37[222] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x5a, 0x56, 0x5a, 0xd2, 0xb2, 0xc0, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40 - }; - - const WebRtc_UWord8 mask42_38[228] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2c, 0x03, 0x21, 0x60, 0x19, 0x00, - 0x81, 0xa0, 0x1c, 0x0d, 0x00, 0xc0, - 0xa0, 0x68, 0x25, 0x03, 0x41, 0x00, - 0x05, 0x41, 0x50, 0x2a, 0x0a, 0x80, - 0x18, 0x90, 0xc0, 0xc4, 0x86, 0x00, - 0xc2, 0x06, 0x86, 0x10, 0x34, 0x00, - 0x22, 0x98, 0x09, 0x14, 0xc0, 0x40, - 0x50, 0x45, 0x0a, 0x82, 0x28, 0x40, - 0x5a, 0x56, 0x5a, 0xd2, 0xb2, 0xc0, - 0x05, 0x19, 0x55, 0xee, 0xe2, 0xc0 - }; - - const WebRtc_UWord8 mask42_39[234] = - { - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2a, 0x03, 0x31, 0x50, 0x19, 0x80 - }; - - const WebRtc_UWord8 mask42_4[24] = - { - 0xe6, 0x31, 0x37, 0x31, 0x89, 0x80, - 0x33, 0x8c, 0x59, 0x9c, 0x62, 0xc0, - 0x98, 0xd2, 0xcc, 0xc6, 0x96, 0x40, - 0x2d, 0x4b, 0x29, 0x6a, 0x59, 0x40 - }; - - const WebRtc_UWord8 mask42_40[240] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2a, 0x03, 0x31, 0x50, 0x19, 0x80, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0xf9, 0xdb, 0x5d, 0x7a, 0xd4, 0x40 - }; - - const WebRtc_UWord8 mask42_41[246] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2a, 0x03, 0x31, 0x50, 0x19, 0x80, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4c, 0x11, 0x92, 0x60, 0x8c, 0x80, - 0x51, 0x0c, 0xca, 0x88, 0x66, 0x40, - 0xa0, 0x66, 0x45, 0x03, 0x32, 0x00, - 0x04, 0xc1, 0x60, 0x26, 0x0b, 0x00, - 0x03, 0xa0, 0x28, 0x1d, 0x01, 0x40, - 0x86, 0x21, 0x14, 0x31, 0x08, 0x80, - 0x29, 0x10, 0x19, 0x48, 0x80, 0xc0, - 0x42, 0x42, 0xa2, 0x12, 0x15, 0x00, - 0x98, 0x1a, 0x04, 0xc0, 0xd0, 0x00, - 0x30, 0x84, 0x09, 0x84, 0x20, 0x40, - 0xdf, 0x4c, 0x16, 0xfa, 0x60, 0x80 - }; - - const WebRtc_UWord8 mask42_42[252] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4c, 0x11, 0x92, 0x60, 0x8c, 0x80, - 0x51, 0x0c, 0xca, 0x88, 0x66, 0x40, - 0xa0, 0x66, 0x45, 0x03, 0x32, 0x00, - 0x04, 0xc1, 0x60, 0x26, 0x0b, 0x00, - 0x03, 0xa0, 0x28, 0x1d, 0x01, 0x40, - 0x86, 0x21, 0x14, 0x31, 0x08, 0x80, - 0x29, 0x10, 0x19, 0x48, 0x80, 0xc0, - 0x42, 0x42, 0xa2, 0x12, 0x15, 0x00, - 0x98, 0x1a, 0x04, 0xc0, 0xd0, 0x00, - 0x30, 0x84, 0x09, 0x84, 0x20, 0x40, - 0xdf, 0x4c, 0x16, 0xfa, 0x60, 0x80, - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0xa0, 0x6a, 0x45, 0x03, 0x52, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x09, 0x80, - 0x03, 0xb4, 0x00, 0x1d, 0xa0, 0x00, - 0x86, 0x20, 0x94, 0x31, 0x04, 0x80, - 0x29, 0x08, 0x49, 0x48, 0x42, 0x40, - 0x42, 0x43, 0x0a, 0x12, 0x18, 0x40, - 0x98, 0x12, 0x84, 0xc0, 0x94, 0x00, - 0x30, 0x84, 0xa9, 0x84, 0x25, 0x40, - 0x4e, 0x11, 0x92, 0x70, 0x8c, 0x80, - 0xe3, 0x18, 0x9f, 0x18, 0xc4, 0xc0, - 0x81, 0xe3, 0x04, 0x0f, 0x18, 0x00, - 0x21, 0x40, 0x59, 0x0a, 0x02, 0xc0, - 0x52, 0x81, 0xe2, 0x94, 0x0f, 0x00, - 0xb4, 0x28, 0x25, 0xa1, 0x41, 0x00, - 0x26, 0x86, 0x29, 0x34, 0x31, 0x40, - 0x58, 0x64, 0x42, 0xc3, 0x22, 0x00, - 0x19, 0x9e, 0x00, 0xcc, 0xf0, 0x00, - 0x2a, 0x03, 0x31, 0x50, 0x19, 0x80, - 0xea, 0x9e, 0x23, 0xb3, 0x65, 0x00 - }; - - const WebRtc_UWord8 mask42_5[30] = - { - 0xce, 0x31, 0xb6, 0x71, 0x8d, 0x80, - 0x63, 0x98, 0xdb, 0x1c, 0xc6, 0xc0, - 0x98, 0xc7, 0x6c, 0xc6, 0x3b, 0x40, - 0x4d, 0x6b, 0x52, 0x6b, 0x5a, 0x80, - 0xb2, 0x6c, 0xad, 0x93, 0x65, 0x40 - }; - - const WebRtc_UWord8 mask42_6[36] = - { - 0x4c, 0x19, 0x12, 0x60, 0xc8, 0x80, - 0x51, 0x14, 0x52, 0x88, 0xa2, 0x80, - 0x20, 0xea, 0x09, 0x07, 0x50, 0x40, - 0x85, 0x41, 0x2c, 0x2a, 0x09, 0x40, - 0x06, 0x80, 0xd8, 0x34, 0x06, 0xc0, - 0x8a, 0x24, 0x34, 0x51, 0x21, 0x80 - }; - - const WebRtc_UWord8 mask42_7[42] = - { - 0xc6, 0x11, 0x96, 0x30, 0x8c, 0x80, - 0x33, 0x04, 0xc9, 0x98, 0x26, 0x40, - 0x18, 0x67, 0x40, 0xc3, 0x3a, 0x00, - 0x45, 0x42, 0xd2, 0x2a, 0x16, 0x80, - 0x12, 0xd4, 0x28, 0x96, 0xa1, 0x40, - 0xb4, 0x28, 0x35, 0xa1, 0x41, 0x80, - 0x29, 0x92, 0x19, 0x4c, 0x90, 0xc0 - }; - - const WebRtc_UWord8 mask42_8[48] = - { - 0x07, 0x0a, 0x70, 0x38, 0x53, 0x80, - 0x49, 0xa8, 0x2a, 0x4d, 0x41, 0x40, - 0xb0, 0x7a, 0x05, 0x83, 0xd0, 0x00, - 0x24, 0xc5, 0xc1, 0x26, 0x2e, 0x00, - 0x52, 0x80, 0xea, 0x94, 0x07, 0x40, - 0xc6, 0x31, 0x86, 0x31, 0x8c, 0x00, - 0x31, 0x94, 0x19, 0x8c, 0xa0, 0xc0, - 0x18, 0xc7, 0x08, 0xc6, 0x38, 0x40 - }; - - const WebRtc_UWord8 mask42_9[54] = - { - 0x4e, 0x11, 0x12, 0x70, 0x88, 0x80, - 0x62, 0x1a, 0x0b, 0x10, 0xd0, 0x40, - 0x80, 0xe9, 0x44, 0x07, 0x4a, 0x00, - 0xa1, 0x50, 0x55, 0x0a, 0x82, 0x80, - 0x53, 0x00, 0x6a, 0x98, 0x03, 0x40, - 0xa4, 0x24, 0x35, 0x21, 0x21, 0x80, - 0x16, 0xa0, 0x88, 0xb5, 0x04, 0x40, - 0x58, 0x45, 0x22, 0xc2, 0x29, 0x00, - 0x29, 0x86, 0x81, 0x4c, 0x34, 0x00 - }; - - const WebRtc_UWord8 mask43_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0 - }; - - const WebRtc_UWord8 mask43_10[60] = - { - 0x4c, 0x19, 0x16, 0x01, 0xc4, 0x40, - 0x51, 0x14, 0x51, 0x80, 0x71, 0x40, - 0xa0, 0x6a, 0x47, 0x40, 0x38, 0x00, - 0x04, 0xc1, 0x34, 0x28, 0x45, 0x40, - 0x03, 0xb4, 0x06, 0x84, 0x90, 0x80, - 0x86, 0x20, 0x94, 0x32, 0x82, 0x40, - 0x29, 0x08, 0x4a, 0x53, 0x40, 0x60, - 0x42, 0x43, 0x08, 0x0d, 0x03, 0xa0, - 0x98, 0x12, 0x82, 0x64, 0x0c, 0x80, - 0x30, 0x84, 0xab, 0x11, 0x20, 0x20 - }; - - const WebRtc_UWord8 mask43_11[66] = - { - 0xc6, 0x21, 0xa2, 0x32, 0x46, 0x40, - 0x23, 0x88, 0xc9, 0x99, 0x33, 0x20, - 0x1a, 0x45, 0x8c, 0xc8, 0x99, 0x00, - 0x24, 0xd3, 0x08, 0x2c, 0x05, 0x80, - 0x71, 0x10, 0x74, 0x05, 0x80, 0xa0, - 0x0e, 0x19, 0x14, 0x22, 0x84, 0x40, - 0x33, 0x14, 0x52, 0x03, 0x40, 0x60, - 0x10, 0xc3, 0x28, 0x54, 0x0a, 0x80, - 0x45, 0x68, 0x4b, 0x40, 0x68, 0x00, - 0x88, 0x84, 0xa8, 0x81, 0x10, 0x20, - 0xe0, 0x22, 0x91, 0x82, 0x30, 0x40 - }; - - const WebRtc_UWord8 mask43_12[72] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0xc6, 0x21, 0xa4, 0x34, 0x86, 0x80, - 0x23, 0x88, 0xc9, 0x19, 0x23, 0x20, - 0x1a, 0x45, 0x88, 0xb1, 0x16, 0x20, - 0x24, 0xd3, 0x0a, 0x61, 0x4c, 0x20, - 0x71, 0x10, 0x72, 0x0e, 0x41, 0xc0, - 0xa0, 0x65, 0x1f, 0xa0, 0xc4, 0xe0 - }; - - const WebRtc_UWord8 mask43_13[78] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60 - }; - - const WebRtc_UWord8 mask43_14[84] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x4d, 0xd0, 0xc6, 0x36, 0x57, 0x40 - }; - - const WebRtc_UWord8 mask43_15[90] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20 - }; - - const WebRtc_UWord8 mask43_16[96] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x3b, 0xf5, 0x3c, 0x36, 0x0a, 0x20 - }; - - const WebRtc_UWord8 mask43_17[102] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00 - }; - - const WebRtc_UWord8 mask43_18[108] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x5a, 0x56, 0x5f, 0x26, 0xa3, 0x60 - }; - - const WebRtc_UWord8 mask43_19[114] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0 - }; - - const WebRtc_UWord8 mask43_2[12] = - { - 0xee, 0x3b, 0x37, 0x66, 0xec, 0xc0, - 0x99, 0xe6, 0xec, 0xdd, 0x9b, 0xa0 - }; - - const WebRtc_UWord8 mask43_20[120] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2a, 0x03, 0x31, 0xda, 0x46, 0x20 - }; - - const WebRtc_UWord8 mask43_21[126] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4c, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x51, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0xa0, 0x66, 0x44, 0xc8, 0x99, 0x00, - 0x04, 0xc1, 0x60, 0x2c, 0x05, 0x80, - 0x03, 0xa0, 0x2c, 0x05, 0x80, 0xa0, - 0x86, 0x21, 0x14, 0x22, 0x84, 0x40, - 0x29, 0x10, 0x1a, 0x03, 0x40, 0x60, - 0x42, 0x42, 0xa0, 0x54, 0x0a, 0x80, - 0x98, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x30, 0x84, 0x08, 0x81, 0x10, 0x20, - 0xdf, 0x4c, 0x11, 0x82, 0x30, 0x40 - }; - - const WebRtc_UWord8 mask43_22[132] = - { - 0xc6, 0x21, 0xa2, 0x32, 0x46, 0x40, - 0x23, 0x88, 0xc9, 0x99, 0x33, 0x20, - 0x1a, 0x45, 0x8c, 0xc8, 0x99, 0x00, - 0x24, 0xd3, 0x08, 0x2c, 0x05, 0x80, - 0x71, 0x10, 0x74, 0x05, 0x80, 0xa0, - 0x0e, 0x19, 0x14, 0x22, 0x84, 0x40, - 0x33, 0x14, 0x52, 0x03, 0x40, 0x60, - 0x10, 0xc3, 0x28, 0x54, 0x0a, 0x80, - 0x45, 0x68, 0x4b, 0x40, 0x68, 0x00, - 0x88, 0x84, 0xa8, 0x81, 0x10, 0x20, - 0xe0, 0x22, 0x91, 0x82, 0x30, 0x40, - 0x4c, 0x19, 0x16, 0x01, 0xc4, 0x40, - 0x51, 0x14, 0x51, 0x80, 0x71, 0x40, - 0xa0, 0x6a, 0x47, 0x40, 0x38, 0x00, - 0x04, 0xc1, 0x34, 0x28, 0x45, 0x40, - 0x03, 0xb4, 0x06, 0x84, 0x90, 0x80, - 0x86, 0x20, 0x94, 0x32, 0x82, 0x40, - 0x29, 0x08, 0x4a, 0x53, 0x40, 0x60, - 0x42, 0x43, 0x08, 0x0d, 0x03, 0xa0, - 0x98, 0x12, 0x82, 0x64, 0x0c, 0x80, - 0x30, 0x84, 0xab, 0x11, 0x20, 0x20, - 0xfe, 0x2c, 0x85, 0xcc, 0x24, 0x80 - }; - - const WebRtc_UWord8 mask43_23[138] = - { - 0xc6, 0x21, 0xa2, 0x32, 0x46, 0x40, - 0x23, 0x88, 0xc9, 0x99, 0x33, 0x20, - 0x1a, 0x45, 0x8c, 0xc8, 0x99, 0x00, - 0x24, 0xd3, 0x08, 0x2c, 0x05, 0x80, - 0x71, 0x10, 0x74, 0x05, 0x80, 0xa0, - 0x0e, 0x19, 0x14, 0x22, 0x84, 0x40, - 0x33, 0x14, 0x52, 0x03, 0x40, 0x60, - 0x10, 0xc3, 0x28, 0x54, 0x0a, 0x80, - 0x45, 0x68, 0x4b, 0x40, 0x68, 0x00, - 0x88, 0x84, 0xa8, 0x81, 0x10, 0x20, - 0xe0, 0x22, 0x91, 0x82, 0x30, 0x40, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0xc6, 0x21, 0xa4, 0x34, 0x86, 0x80, - 0x23, 0x88, 0xc9, 0x19, 0x23, 0x20, - 0x1a, 0x45, 0x88, 0xb1, 0x16, 0x20, - 0x24, 0xd3, 0x0a, 0x61, 0x4c, 0x20, - 0x71, 0x10, 0x72, 0x0e, 0x41, 0xc0, - 0xa0, 0x65, 0x1f, 0xa0, 0xc4, 0xe0 - }; - - const WebRtc_UWord8 mask43_24[144] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0xc6, 0x21, 0xa4, 0x34, 0x86, 0x80, - 0x23, 0x88, 0xc9, 0x19, 0x23, 0x20, - 0x1a, 0x45, 0x88, 0xb1, 0x16, 0x20, - 0x24, 0xd3, 0x0a, 0x61, 0x4c, 0x20, - 0x71, 0x10, 0x72, 0x0e, 0x41, 0xc0, - 0xa0, 0x65, 0x1f, 0xa0, 0xc4, 0xe0, - 0xc6, 0x21, 0xa2, 0x32, 0x46, 0x40, - 0x23, 0x88, 0xc9, 0x99, 0x33, 0x20, - 0x1a, 0x45, 0x8c, 0xc8, 0x99, 0x00, - 0x24, 0xd3, 0x08, 0x2c, 0x05, 0x80, - 0x71, 0x10, 0x74, 0x05, 0x80, 0xa0, - 0x0e, 0x19, 0x14, 0x22, 0x84, 0x40, - 0x33, 0x14, 0x52, 0x03, 0x40, 0x60, - 0x10, 0xc3, 0x28, 0x54, 0x0a, 0x80, - 0x45, 0x68, 0x4b, 0x40, 0x68, 0x00, - 0x88, 0x84, 0xa8, 0x81, 0x10, 0x20, - 0xe0, 0x22, 0x91, 0x82, 0x30, 0x40, - 0xf9, 0xb1, 0x26, 0x6c, 0x51, 0xe0 - }; - - const WebRtc_UWord8 mask43_25[150] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0xc6, 0x21, 0xa4, 0x34, 0x86, 0x80, - 0x23, 0x88, 0xc9, 0x19, 0x23, 0x20, - 0x1a, 0x45, 0x88, 0xb1, 0x16, 0x20, - 0x24, 0xd3, 0x0a, 0x61, 0x4c, 0x20, - 0x71, 0x10, 0x72, 0x0e, 0x41, 0xc0, - 0xa0, 0x65, 0x1f, 0xa0, 0xc4, 0xe0, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60 - }; - - const WebRtc_UWord8 mask43_26[156] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0xc6, 0x21, 0xa4, 0x34, 0x86, 0x80, - 0x23, 0x88, 0xc9, 0x19, 0x23, 0x20, - 0x1a, 0x45, 0x88, 0xb1, 0x16, 0x20, - 0x24, 0xd3, 0x0a, 0x61, 0x4c, 0x20, - 0x71, 0x10, 0x72, 0x0e, 0x41, 0xc0, - 0xa0, 0x65, 0x1f, 0xa0, 0xc4, 0xe0, - 0xef, 0x84, 0x77, 0xca, 0x0d, 0x40 - }; - - const WebRtc_UWord8 mask43_27[162] = - { - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x4d, 0xd0, 0xc6, 0x36, 0x57, 0x40 - }; - - const WebRtc_UWord8 mask43_28[168] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x4d, 0xd0, 0xc6, 0x36, 0x57, 0x40, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x16, 0xc9, 0x53, 0x1e, 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask43_29[174] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x4d, 0xd0, 0xc6, 0x36, 0x57, 0x40, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20 - }; - - const WebRtc_UWord8 mask43_3[18] = - { - 0xce, 0x32, 0xb6, 0x56, 0xca, 0xc0, - 0x55, 0xdc, 0x57, 0x8a, 0xf1, 0x40, - 0xa8, 0xed, 0x8d, 0xb1, 0xae, 0x20 - }; - - const WebRtc_UWord8 mask43_30[180] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x0e, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x33, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x10, 0xc3, 0x28, 0x65, 0x0c, 0xa0, - 0x45, 0x68, 0x4d, 0x09, 0xa1, 0x20, - 0x88, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0xe0, 0x22, 0x94, 0x52, 0x8a, 0x40, - 0x4d, 0xd0, 0xc6, 0x36, 0x57, 0x40, - 0x79, 0x4a, 0x8f, 0x42, 0x79, 0x40 - }; - - const WebRtc_UWord8 mask43_31[186] = - { - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x3b, 0xf5, 0x3c, 0x36, 0x0a, 0x20 - }; - - const WebRtc_UWord8 mask43_32[192] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x3b, 0xf5, 0x3c, 0x36, 0x0a, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0xd1, 0xd1, 0x11, 0xa4, 0xed, 0xc0 - }; - - const WebRtc_UWord8 mask43_33[198] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x3b, 0xf5, 0x3c, 0x36, 0x0a, 0x20, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00 - }; - - const WebRtc_UWord8 mask43_34[204] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x46, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x33, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0x10, 0xe4, 0x64, 0x8c, 0x91, 0x80, - 0x0c, 0x69, 0x0d, 0x21, 0xa4, 0x20, - 0x28, 0x94, 0x2a, 0x85, 0x50, 0xa0, - 0x94, 0x21, 0x34, 0x26, 0x84, 0xc0, - 0xc1, 0x02, 0x58, 0x4b, 0x09, 0x60, - 0x3b, 0xf5, 0x3c, 0x36, 0x0a, 0x20, - 0x76, 0x81, 0x4d, 0x33, 0x66, 0x00 - }; - - const WebRtc_UWord8 mask43_35[210] = - { - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x5a, 0x56, 0x5f, 0x26, 0xa3, 0x60 - }; - - const WebRtc_UWord8 mask43_36[216] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x5a, 0x56, 0x5f, 0x26, 0xa3, 0x60, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0xa3, 0x85, 0x0a, 0xb5, 0x11, 0x60 - }; - - const WebRtc_UWord8 mask43_37[222] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x5a, 0x56, 0x5f, 0x26, 0xa3, 0x60, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0 - }; - - const WebRtc_UWord8 mask43_38[228] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2c, 0x03, 0x20, 0x64, 0x0c, 0x80, - 0x81, 0xa0, 0x1c, 0x03, 0x80, 0x60, - 0xa0, 0x68, 0x25, 0x04, 0xa0, 0x80, - 0x05, 0x41, 0x50, 0x2a, 0x05, 0x40, - 0x18, 0x90, 0xc2, 0x18, 0x43, 0x00, - 0xc2, 0x06, 0x80, 0xd0, 0x1a, 0x00, - 0x22, 0x98, 0x0b, 0x01, 0x60, 0x20, - 0x50, 0x45, 0x08, 0xa1, 0x14, 0x20, - 0x5a, 0x56, 0x5f, 0x26, 0xa3, 0x60, - 0x9a, 0x16, 0x97, 0x21, 0xb9, 0x80 - }; - - const WebRtc_UWord8 mask43_39[234] = - { - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2a, 0x03, 0x31, 0xda, 0x46, 0x20 - }; - - const WebRtc_UWord8 mask43_4[24] = - { - 0xe6, 0x31, 0x36, 0x26, 0xc4, 0xc0, - 0x33, 0x8c, 0x59, 0x8b, 0x31, 0x60, - 0x98, 0xd2, 0xca, 0x59, 0x4b, 0x20, - 0x2d, 0x4b, 0x29, 0x65, 0x2c, 0xa0 - }; - - const WebRtc_UWord8 mask43_40[240] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2a, 0x03, 0x31, 0xda, 0x46, 0x20, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x3a, 0xab, 0x77, 0x63, 0xef, 0x60 - }; - - const WebRtc_UWord8 mask43_41[246] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2a, 0x03, 0x31, 0xda, 0x46, 0x20, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4c, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x51, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0xa0, 0x66, 0x44, 0xc8, 0x99, 0x00, - 0x04, 0xc1, 0x60, 0x2c, 0x05, 0x80, - 0x03, 0xa0, 0x2c, 0x05, 0x80, 0xa0, - 0x86, 0x21, 0x14, 0x22, 0x84, 0x40, - 0x29, 0x10, 0x1a, 0x03, 0x40, 0x60, - 0x42, 0x42, 0xa0, 0x54, 0x0a, 0x80, - 0x98, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x30, 0x84, 0x08, 0x81, 0x10, 0x20, - 0xdf, 0x4c, 0x11, 0x82, 0x30, 0x40 - }; - - const WebRtc_UWord8 mask43_42[252] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4c, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x51, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0xa0, 0x66, 0x44, 0xc8, 0x99, 0x00, - 0x04, 0xc1, 0x60, 0x2c, 0x05, 0x80, - 0x03, 0xa0, 0x2c, 0x05, 0x80, 0xa0, - 0x86, 0x21, 0x14, 0x22, 0x84, 0x40, - 0x29, 0x10, 0x1a, 0x03, 0x40, 0x60, - 0x42, 0x42, 0xa0, 0x54, 0x0a, 0x80, - 0x98, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x30, 0x84, 0x08, 0x81, 0x10, 0x20, - 0xdf, 0x4c, 0x11, 0x82, 0x30, 0x40, - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4e, 0x11, 0x92, 0x32, 0x46, 0x40, - 0xe3, 0x18, 0x9b, 0x13, 0x62, 0x60, - 0x81, 0xe3, 0x04, 0x60, 0x8c, 0x00, - 0x21, 0x40, 0x58, 0x0b, 0x01, 0x60, - 0x52, 0x81, 0xe0, 0x3c, 0x07, 0x80, - 0xb4, 0x28, 0x25, 0x04, 0xa0, 0x80, - 0x26, 0x86, 0x28, 0xc5, 0x18, 0xa0, - 0x58, 0x64, 0x44, 0x88, 0x91, 0x00, - 0x19, 0x9e, 0x03, 0xc0, 0x78, 0x00, - 0x2a, 0x03, 0x31, 0xda, 0x46, 0x20, - 0x26, 0x84, 0x10, 0xcd, 0xf7, 0x60 - }; - - const WebRtc_UWord8 mask43_43[258] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0xa0, 0x6a, 0x45, 0x48, 0xa9, 0x00, - 0x04, 0xc1, 0x30, 0x26, 0x04, 0xc0, - 0x03, 0xb4, 0x06, 0x80, 0xd0, 0x00, - 0x86, 0x20, 0x94, 0x12, 0x82, 0x40, - 0x29, 0x08, 0x49, 0x09, 0x21, 0x20, - 0x42, 0x43, 0x08, 0x61, 0x0c, 0x20, - 0x98, 0x12, 0x82, 0x50, 0x4a, 0x00, - 0x30, 0x84, 0xa8, 0x95, 0x12, 0xa0, - 0x4c, 0x11, 0x92, 0x32, 0x46, 0x40, - 0x51, 0x0c, 0xc9, 0x99, 0x33, 0x20, - 0xa0, 0x66, 0x44, 0xc8, 0x99, 0x00, - 0x04, 0xc1, 0x60, 0x2c, 0x05, 0x80, - 0x03, 0xa0, 0x2c, 0x05, 0x80, 0xa0, - 0x86, 0x21, 0x14, 0x22, 0x84, 0x40, - 0x29, 0x10, 0x1a, 0x03, 0x40, 0x60, - 0x42, 0x42, 0xa0, 0x54, 0x0a, 0x80, - 0x98, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x30, 0x84, 0x08, 0x81, 0x10, 0x20, - 0xdf, 0x4c, 0x11, 0x82, 0x30, 0x40, - 0x4c, 0x19, 0x12, 0x32, 0x46, 0x40, - 0x51, 0x14, 0x51, 0x99, 0x33, 0x20, - 0xa0, 0x6a, 0x44, 0xc8, 0x99, 0x00, - 0x04, 0xc1, 0x30, 0x2c, 0x05, 0x80, - 0x03, 0xb4, 0x04, 0x05, 0x80, 0xa0, - 0x86, 0x20, 0x94, 0x22, 0x84, 0x40, - 0x29, 0x08, 0x4a, 0x03, 0x40, 0x60, - 0x42, 0x43, 0x08, 0x54, 0x0a, 0x80, - 0x98, 0x12, 0x83, 0x40, 0x68, 0x00, - 0x30, 0x84, 0xa8, 0x81, 0x10, 0x20, - 0x4c, 0x11, 0x91, 0x82, 0x30, 0x40, - 0x51, 0x0c, 0xcb, 0x22, 0x64, 0x40, - 0xa0, 0x66, 0x42, 0x8a, 0x51, 0x40, - 0x04, 0xc1, 0x65, 0x48, 0xa9, 0x00, - 0x03, 0xa0, 0x28, 0x26, 0x04, 0xc0, - 0x86, 0x21, 0x16, 0x80, 0xd0, 0x00, - 0x29, 0x10, 0x1c, 0x12, 0x82, 0x40, - 0x42, 0x42, 0xa1, 0x09, 0x21, 0x20, - 0x98, 0x1a, 0x00, 0x61, 0x0c, 0x20, - 0x30, 0x84, 0x0a, 0x50, 0x4a, 0x00, - 0xdf, 0x4c, 0x10, 0x95, 0x12, 0xa0, - 0x72, 0x06, 0x94, 0xf6, 0x74, 0x40 - }; - - const WebRtc_UWord8 mask43_5[30] = - { - 0xce, 0x31, 0xb6, 0x36, 0xc6, 0xc0, - 0x63, 0x98, 0xdb, 0x1b, 0x63, 0x60, - 0x98, 0xc7, 0x68, 0xed, 0x1d, 0xa0, - 0x4d, 0x6b, 0x55, 0x6a, 0xad, 0x40, - 0xb2, 0x6c, 0xad, 0x95, 0xb2, 0xa0 - }; - - const WebRtc_UWord8 mask43_6[36] = - { - 0x4c, 0x19, 0x13, 0x22, 0x64, 0x40, - 0x51, 0x14, 0x52, 0x8a, 0x51, 0x40, - 0x20, 0xea, 0x0d, 0x41, 0xa8, 0x20, - 0x85, 0x41, 0x2e, 0x25, 0x04, 0xa0, - 0x06, 0x80, 0xd8, 0x1b, 0x03, 0x60, - 0x8a, 0x24, 0x34, 0x86, 0x90, 0xc0 - }; - - const WebRtc_UWord8 mask43_7[42] = - { - 0xc6, 0x11, 0x96, 0x32, 0x46, 0x40, - 0x33, 0x04, 0xc8, 0x99, 0x33, 0x20, - 0x18, 0x67, 0x44, 0x68, 0x9d, 0x00, - 0x45, 0x42, 0xd4, 0x5a, 0x0b, 0x40, - 0x12, 0xd4, 0x2a, 0x95, 0x50, 0xa0, - 0xb4, 0x28, 0x35, 0x16, 0xa0, 0xc0, - 0x29, 0x92, 0x1b, 0x0d, 0x41, 0x60 - }; - - const WebRtc_UWord8 mask43_8[48] = - { - 0x07, 0x0a, 0x71, 0x44, 0x29, 0xc0, - 0x49, 0xa8, 0x29, 0x0f, 0xa0, 0x20, - 0xb0, 0x7a, 0x07, 0x48, 0xe8, 0x00, - 0x24, 0xc5, 0xc0, 0xb8, 0x17, 0x00, - 0x52, 0x80, 0xec, 0x1d, 0x02, 0xa0, - 0xc6, 0x31, 0x82, 0x30, 0xc7, 0x40, - 0x31, 0x94, 0x1a, 0x83, 0x50, 0x60, - 0x18, 0xc7, 0x08, 0xe1, 0x1c, 0x20 - }; - - const WebRtc_UWord8 mask43_9[54] = - { - 0x4e, 0x11, 0x12, 0x22, 0x46, 0x40, - 0x62, 0x1a, 0x09, 0x41, 0x68, 0x60, - 0x80, 0xe9, 0x41, 0x28, 0xa5, 0x00, - 0xa1, 0x50, 0x52, 0xc8, 0x51, 0x00, - 0x53, 0x00, 0x68, 0x1d, 0x01, 0xa0, - 0xa4, 0x24, 0x36, 0x06, 0x80, 0xc0, - 0x16, 0xa0, 0x8d, 0x11, 0x82, 0x20, - 0x58, 0x45, 0x20, 0xa4, 0x16, 0x80, - 0x29, 0x86, 0x84, 0xd0, 0x1c, 0x00 - }; - - const WebRtc_UWord8 mask44_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 - }; - - const WebRtc_UWord8 mask44_10[60] = - { - 0xc0, 0x38, 0x8b, 0x00, 0xe2, 0x20, - 0x30, 0x0e, 0x28, 0xc0, 0x38, 0xa0, - 0xe8, 0x07, 0x03, 0xa0, 0x1c, 0x00, - 0x85, 0x08, 0xaa, 0x14, 0x22, 0xa0, - 0xd0, 0x92, 0x13, 0x42, 0x48, 0x40, - 0x86, 0x50, 0x4a, 0x19, 0x41, 0x20, - 0x4a, 0x68, 0x0d, 0x29, 0xa0, 0x30, - 0x01, 0xa0, 0x74, 0x06, 0x81, 0xd0, - 0x4c, 0x81, 0x91, 0x32, 0x06, 0x40, - 0x62, 0x24, 0x05, 0x88, 0x90, 0x10 - }; - - const WebRtc_UWord8 mask44_11[66] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20 - }; - - const WebRtc_UWord8 mask44_12[72] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x86, 0x90, 0xd2, 0x1a, 0x43, 0x40, - 0x23, 0x24, 0x64, 0x8c, 0x91, 0x90, - 0x16, 0x22, 0xc4, 0x58, 0x8b, 0x10, - 0x4c, 0x29, 0x85, 0x30, 0xa6, 0x10, - 0x41, 0xc8, 0x39, 0x07, 0x20, 0xe0, - 0xf4, 0x18, 0x9f, 0xd0, 0x62, 0x70 - }; - - const WebRtc_UWord8 mask44_13[78] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0 - }; - - const WebRtc_UWord8 mask44_14[84] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0xc6, 0xca, 0xeb, 0x1b, 0x2b, 0xa0 - }; - - const WebRtc_UWord8 mask44_15[90] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10 - }; - - const WebRtc_UWord8 mask44_16[96] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x86, 0xc1, 0x46, 0x1b, 0x05, 0x10 - }; - - const WebRtc_UWord8 mask44_17[102] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00 - }; - - const WebRtc_UWord8 mask44_18[108] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0xe4, 0xd4, 0x6f, 0x93, 0x51, 0xb0 - }; - - const WebRtc_UWord8 mask44_19[114] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50 - }; - - const WebRtc_UWord8 mask44_2[12] = - { - 0xec, 0xdd, 0x9b, 0xb3, 0x76, 0x60, - 0x9b, 0xb3, 0x76, 0x6e, 0xcd, 0xd0 - }; - - const WebRtc_UWord8 mask44_20[120] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x3b, 0x48, 0xc4, 0xed, 0x23, 0x10 - }; - - const WebRtc_UWord8 mask44_21[126] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20 - }; - - const WebRtc_UWord8 mask44_22[132] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x9e, 0xce, 0x8a, 0x7b, 0x3a, 0x20 - }; - - const WebRtc_UWord8 mask44_23[138] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x86, 0x90, 0xd2, 0x1a, 0x43, 0x40, - 0x23, 0x24, 0x64, 0x8c, 0x91, 0x90, - 0x16, 0x22, 0xc4, 0x58, 0x8b, 0x10, - 0x4c, 0x29, 0x85, 0x30, 0xa6, 0x10, - 0x41, 0xc8, 0x39, 0x07, 0x20, 0xe0, - 0xf4, 0x18, 0x9f, 0xd0, 0x62, 0x70 - }; - - const WebRtc_UWord8 mask44_24[144] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x86, 0x90, 0xd2, 0x1a, 0x43, 0x40, - 0x23, 0x24, 0x64, 0x8c, 0x91, 0x90, - 0x16, 0x22, 0xc4, 0x58, 0x8b, 0x10, - 0x4c, 0x29, 0x85, 0x30, 0xa6, 0x10, - 0x41, 0xc8, 0x39, 0x07, 0x20, 0xe0, - 0xf4, 0x18, 0x9f, 0xd0, 0x62, 0x70, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x15, 0x0f, 0x44, 0x6d, 0x9d, 0xa0 - }; - - const WebRtc_UWord8 mask44_25[150] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x86, 0x90, 0xd2, 0x1a, 0x43, 0x40, - 0x23, 0x24, 0x64, 0x8c, 0x91, 0x90, - 0x16, 0x22, 0xc4, 0x58, 0x8b, 0x10, - 0x4c, 0x29, 0x85, 0x30, 0xa6, 0x10, - 0x41, 0xc8, 0x39, 0x07, 0x20, 0xe0, - 0xf4, 0x18, 0x9f, 0xd0, 0x62, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0 - }; - - const WebRtc_UWord8 mask44_26[156] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x86, 0x90, 0xd2, 0x1a, 0x43, 0x40, - 0x23, 0x24, 0x64, 0x8c, 0x91, 0x90, - 0x16, 0x22, 0xc4, 0x58, 0x8b, 0x10, - 0x4c, 0x29, 0x85, 0x30, 0xa6, 0x10, - 0x41, 0xc8, 0x39, 0x07, 0x20, 0xe0, - 0xf4, 0x18, 0x9f, 0xd0, 0x62, 0x70, - 0x02, 0xcb, 0x64, 0xb8, 0x55, 0x80 - }; - - const WebRtc_UWord8 mask44_27[162] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0xc6, 0xca, 0xeb, 0x1b, 0x2b, 0xa0 - }; - - const WebRtc_UWord8 mask44_28[168] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0xc6, 0xca, 0xeb, 0x1b, 0x2b, 0xa0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x66, 0x26, 0x6c, 0x91, 0xc7, 0x20 - }; - - const WebRtc_UWord8 mask44_29[174] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0xc6, 0xca, 0xeb, 0x1b, 0x2b, 0xa0, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10 - }; - - const WebRtc_UWord8 mask44_3[18] = - { - 0xca, 0xd9, 0x5b, 0x2b, 0x65, 0x60, - 0xf1, 0x5e, 0x2b, 0xc5, 0x78, 0xa0, - 0xb6, 0x35, 0xc6, 0xd8, 0xd7, 0x10 - }; - - const WebRtc_UWord8 mask44_30[180] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0x0c, 0xa1, 0x94, 0x32, 0x86, 0x50, - 0xa1, 0x34, 0x26, 0x84, 0xd0, 0x90, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x8a, 0x51, 0x4a, 0x29, 0x45, 0x20, - 0xc6, 0xca, 0xeb, 0x1b, 0x2b, 0xa0, - 0x60, 0xf4, 0x75, 0x84, 0x90, 0xc0 - }; - - const WebRtc_UWord8 mask44_31[186] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x86, 0xc1, 0x46, 0x1b, 0x05, 0x10 - }; - - const WebRtc_UWord8 mask44_32[192] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x86, 0xc1, 0x46, 0x1b, 0x05, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x3e, 0x39, 0x86, 0x5c, 0xd9, 0xd0 - }; - - const WebRtc_UWord8 mask44_33[198] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x86, 0xc1, 0x46, 0x1b, 0x05, 0x10, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00 - }; - - const WebRtc_UWord8 mask44_34[204] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x91, 0x92, 0x32, 0x46, 0x48, 0xc0, - 0xa4, 0x34, 0x86, 0x90, 0xd2, 0x10, - 0x50, 0xaa, 0x15, 0x42, 0xa8, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x42, 0x60, - 0x09, 0x61, 0x2c, 0x25, 0x84, 0xb0, - 0x86, 0xc1, 0x46, 0x1b, 0x05, 0x10, - 0xb5, 0xc7, 0xe8, 0x0c, 0xb9, 0x90 - }; - - const WebRtc_UWord8 mask44_35[210] = - { - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0xe4, 0xd4, 0x6f, 0x93, 0x51, 0xb0 - }; - - const WebRtc_UWord8 mask44_36[216] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0xe4, 0xd4, 0x6f, 0x93, 0x51, 0xb0, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0xa6, 0x92, 0x01, 0x65, 0x91, 0x20 - }; - - const WebRtc_UWord8 mask44_37[222] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0xe4, 0xd4, 0x6f, 0x93, 0x51, 0xb0, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50 - }; - - const WebRtc_UWord8 mask44_38[228] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x0c, 0x81, 0x90, 0x32, 0x06, 0x40, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x30, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x05, 0x40, 0xa8, 0x15, 0x02, 0xa0, - 0x43, 0x08, 0x61, 0x0c, 0x21, 0x80, - 0x1a, 0x03, 0x40, 0x68, 0x0d, 0x00, - 0x60, 0x2c, 0x05, 0x80, 0xb0, 0x10, - 0x14, 0x22, 0x84, 0x50, 0x8a, 0x10, - 0xe4, 0xd4, 0x6f, 0x93, 0x51, 0xb0, - 0x43, 0x64, 0xf2, 0xe5, 0x5d, 0x10 - }; - - const WebRtc_UWord8 mask44_39[234] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x3b, 0x48, 0xc4, 0xed, 0x23, 0x10 - }; - - const WebRtc_UWord8 mask44_4[24] = - { - 0xc4, 0xd8, 0x9b, 0x13, 0x62, 0x60, - 0x31, 0x66, 0x2c, 0xc5, 0x98, 0xb0, - 0x4b, 0x29, 0x65, 0x2c, 0xa5, 0x90, - 0x2c, 0xa5, 0x94, 0xb2, 0x96, 0x50 - }; - - const WebRtc_UWord8 mask44_40[240] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x3b, 0x48, 0xc4, 0xed, 0x23, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0xd8, 0x2a, 0x16, 0x26, 0x51, 0x40 - }; - - const WebRtc_UWord8 mask44_41[246] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x3b, 0x48, 0xc4, 0xed, 0x23, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20 - }; - - const WebRtc_UWord8 mask44_42[252] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x62, 0x6c, 0x4d, 0x89, 0xb1, 0x30, - 0x8c, 0x11, 0x82, 0x30, 0x46, 0x00, - 0x01, 0x60, 0x2c, 0x05, 0x80, 0xb0, - 0x07, 0x80, 0xf0, 0x1e, 0x03, 0xc0, - 0xa0, 0x94, 0x12, 0x82, 0x50, 0x40, - 0x18, 0xa3, 0x14, 0x62, 0x8c, 0x50, - 0x91, 0x12, 0x22, 0x44, 0x48, 0x80, - 0x78, 0x0f, 0x01, 0xe0, 0x3c, 0x00, - 0x3b, 0x48, 0xc4, 0xed, 0x23, 0x10, - 0xd9, 0xc1, 0x6f, 0xa8, 0x1c, 0x90 - }; - - const WebRtc_UWord8 mask44_43[258] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x9e, 0xce, 0x8a, 0x7b, 0x3a, 0x20 - }; - - const WebRtc_UWord8 mask44_44[264] = - { - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x9e, 0xce, 0x8a, 0x7b, 0x3a, 0x20, - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa9, 0x15, 0x22, 0xa4, 0x54, 0x80, - 0x04, 0xc0, 0x98, 0x13, 0x02, 0x60, - 0xd0, 0x1a, 0x03, 0x40, 0x68, 0x00, - 0x82, 0x50, 0x4a, 0x09, 0x41, 0x20, - 0x21, 0x24, 0x24, 0x84, 0x90, 0x90, - 0x0c, 0x21, 0x84, 0x30, 0x86, 0x10, - 0x4a, 0x09, 0x41, 0x28, 0x25, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0x89, 0x50, - 0x46, 0x48, 0xc9, 0x19, 0x23, 0x20, - 0x33, 0x26, 0x64, 0xcc, 0x99, 0x90, - 0x99, 0x13, 0x22, 0x64, 0x4c, 0x80, - 0x05, 0x80, 0xb0, 0x16, 0x02, 0xc0, - 0x80, 0xb0, 0x16, 0x02, 0xc0, 0x50, - 0x84, 0x50, 0x8a, 0x11, 0x42, 0x20, - 0x40, 0x68, 0x0d, 0x01, 0xa0, 0x30, - 0x0a, 0x81, 0x50, 0x2a, 0x05, 0x40, - 0x68, 0x0d, 0x01, 0xa0, 0x34, 0x00, - 0x10, 0x22, 0x04, 0x40, 0x88, 0x10, - 0x30, 0x46, 0x08, 0xc1, 0x18, 0x20, - 0xb5, 0x1c, 0x1c, 0x21, 0xac, 0xa0 - }; - - const WebRtc_UWord8 mask44_5[30] = - { - 0xc6, 0xd8, 0xdb, 0x1b, 0x63, 0x60, - 0x63, 0x6c, 0x6d, 0x8d, 0xb1, 0xb0, - 0x1d, 0xa3, 0xb4, 0x76, 0x8e, 0xd0, - 0xad, 0x55, 0xaa, 0xb5, 0x56, 0xa0, - 0xb2, 0xb6, 0x56, 0xca, 0xd9, 0x50 - }; - - const WebRtc_UWord8 mask44_6[36] = - { - 0x64, 0x4c, 0x89, 0x91, 0x32, 0x20, - 0x51, 0x4a, 0x29, 0x45, 0x28, 0xa0, - 0xa8, 0x35, 0x06, 0xa0, 0xd4, 0x10, - 0xc4, 0xa0, 0x97, 0x12, 0x82, 0x50, - 0x03, 0x60, 0x6c, 0x0d, 0x81, 0xb0, - 0x90, 0xd2, 0x1a, 0x43, 0x48, 0x60 - }; - - const WebRtc_UWord8 mask44_7[42] = - { - 0xc6, 0x48, 0xcb, 0x19, 0x23, 0x20, - 0x13, 0x26, 0x64, 0x4c, 0x99, 0x90, - 0x8d, 0x13, 0xa2, 0x34, 0x4e, 0x80, - 0x8b, 0x41, 0x6a, 0x2d, 0x05, 0xa0, - 0x52, 0xaa, 0x15, 0x4a, 0xa8, 0x50, - 0xa2, 0xd4, 0x1a, 0x8b, 0x50, 0x60, - 0x61, 0xa8, 0x2d, 0x86, 0xa0, 0xb0 - }; - - const WebRtc_UWord8 mask44_8[48] = - { - 0x28, 0x85, 0x38, 0xa2, 0x14, 0xe0, - 0x21, 0xf4, 0x04, 0x87, 0xd0, 0x10, - 0xe9, 0x1d, 0x03, 0xa4, 0x74, 0x00, - 0x17, 0x02, 0xe0, 0x5c, 0x0b, 0x80, - 0x83, 0xa0, 0x56, 0x0e, 0x81, 0x50, - 0x46, 0x18, 0xe9, 0x18, 0x63, 0xa0, - 0x50, 0x6a, 0x0d, 0x41, 0xa8, 0x30, - 0x1c, 0x23, 0x84, 0x70, 0x8e, 0x10 - }; - - const WebRtc_UWord8 mask44_9[54] = - { - 0x44, 0x48, 0xc9, 0x11, 0x23, 0x20, - 0x28, 0x2d, 0x0c, 0xa0, 0xb4, 0x30, - 0x25, 0x14, 0xa0, 0x94, 0x52, 0x80, - 0x59, 0x0a, 0x21, 0x64, 0x28, 0x80, - 0x03, 0xa0, 0x34, 0x0e, 0x80, 0xd0, - 0xc0, 0xd0, 0x1b, 0x03, 0x40, 0x60, - 0xa2, 0x30, 0x46, 0x88, 0xc1, 0x10, - 0x14, 0x82, 0xd0, 0x52, 0x0b, 0x40, - 0x9a, 0x03, 0x82, 0x68, 0x0e, 0x00 - }; - - const WebRtc_UWord8 mask45_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8 - }; - - const WebRtc_UWord8 mask45_10[60] = - { - 0xc0, 0x38, 0x89, 0x91, 0x28, 0xa0, - 0x30, 0x0e, 0x29, 0x45, 0x22, 0x88, - 0xe8, 0x07, 0x02, 0xa4, 0x40, 0x68, - 0x85, 0x08, 0xa8, 0x13, 0x12, 0x10, - 0xd0, 0x92, 0x13, 0x40, 0x05, 0x10, - 0x86, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x4a, 0x68, 0x0c, 0x84, 0xdc, 0x00, - 0x01, 0xa0, 0x74, 0x30, 0x84, 0x88, - 0x4c, 0x81, 0x91, 0x28, 0x2b, 0x00, - 0x62, 0x24, 0x04, 0x4a, 0xd1, 0x40 - }; - - const WebRtc_UWord8 mask45_11[66] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10 - }; - - const WebRtc_UWord8 mask45_12[72] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x86, 0x90, 0xd2, 0x1a, 0x29, 0xb0, - 0x23, 0x24, 0x64, 0x8c, 0xb2, 0x10, - 0x16, 0x22, 0xc4, 0x58, 0x86, 0x60, - 0x4c, 0x29, 0x85, 0x30, 0xc1, 0x50, - 0x41, 0xc8, 0x39, 0x07, 0x04, 0x98, - 0xf4, 0x18, 0x9c, 0x65, 0x5b, 0x90 - }; - - const WebRtc_UWord8 mask45_13[78] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30 - }; - - const WebRtc_UWord8 mask45_14[84] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0xc6, 0xca, 0xea, 0x70, 0xfe, 0xc8 - }; - - const WebRtc_UWord8 mask45_15[90] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80 - }; - - const WebRtc_UWord8 mask45_16[96] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x86, 0xc1, 0x47, 0xeb, 0x67, 0xd0 - }; - - const WebRtc_UWord8 mask45_17[102] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70 - }; - - const WebRtc_UWord8 mask45_18[108] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0xe4, 0xd4, 0x6e, 0x08, 0xc9, 0x58 - }; - - const WebRtc_UWord8 mask45_19[114] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40 - }; - - const WebRtc_UWord8 mask45_2[12] = - { - 0xec, 0xdd, 0x9b, 0xb3, 0x76, 0x60, - 0x9b, 0xb3, 0x76, 0x6e, 0xc9, 0xd8 - }; - - const WebRtc_UWord8 mask45_20[120] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x3b, 0x48, 0xc7, 0x6d, 0x29, 0xe8 - }; - - const WebRtc_UWord8 mask45_21[126] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10 - }; - - const WebRtc_UWord8 mask45_22[132] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x9e, 0xce, 0x8b, 0xaa, 0x34, 0x68 - }; - - const WebRtc_UWord8 mask45_23[138] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x86, 0x90, 0xd2, 0x1a, 0x29, 0xb0, - 0x23, 0x24, 0x64, 0x8c, 0xb2, 0x10, - 0x16, 0x22, 0xc4, 0x58, 0x86, 0x60, - 0x4c, 0x29, 0x85, 0x30, 0xc1, 0x50, - 0x41, 0xc8, 0x39, 0x07, 0x04, 0x98, - 0xf4, 0x18, 0x9c, 0x65, 0x5b, 0x90 - }; - - const WebRtc_UWord8 mask45_24[144] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x86, 0x90, 0xd2, 0x1a, 0x29, 0xb0, - 0x23, 0x24, 0x64, 0x8c, 0xb2, 0x10, - 0x16, 0x22, 0xc4, 0x58, 0x86, 0x60, - 0x4c, 0x29, 0x85, 0x30, 0xc1, 0x50, - 0x41, 0xc8, 0x39, 0x07, 0x04, 0x98, - 0xf4, 0x18, 0x9c, 0x65, 0x5b, 0x90, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x95, 0x91, 0xad, 0xd9, 0x86, 0x98 - }; - - const WebRtc_UWord8 mask45_25[150] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x86, 0x90, 0xd2, 0x1a, 0x29, 0xb0, - 0x23, 0x24, 0x64, 0x8c, 0xb2, 0x10, - 0x16, 0x22, 0xc4, 0x58, 0x86, 0x60, - 0x4c, 0x29, 0x85, 0x30, 0xc1, 0x50, - 0x41, 0xc8, 0x39, 0x07, 0x04, 0x98, - 0xf4, 0x18, 0x9c, 0x65, 0x5b, 0x90, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30 - }; - - const WebRtc_UWord8 mask45_26[156] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x86, 0x90, 0xd2, 0x1a, 0x29, 0xb0, - 0x23, 0x24, 0x64, 0x8c, 0xb2, 0x10, - 0x16, 0x22, 0xc4, 0x58, 0x86, 0x60, - 0x4c, 0x29, 0x85, 0x30, 0xc1, 0x50, - 0x41, 0xc8, 0x39, 0x07, 0x04, 0x98, - 0xf4, 0x18, 0x9c, 0x65, 0x5b, 0x90, - 0xb0, 0xfd, 0xb2, 0xf3, 0x8a, 0xc0 - }; - - const WebRtc_UWord8 mask45_27[162] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0xc6, 0xca, 0xea, 0x70, 0xfe, 0xc8 - }; - - const WebRtc_UWord8 mask45_28[168] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0xc6, 0xca, 0xea, 0x70, 0xfe, 0xc8, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x44, 0x46, 0x28, 0xfb, 0x66, 0x80 - }; - - const WebRtc_UWord8 mask45_29[174] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0xc6, 0xca, 0xea, 0x70, 0xfe, 0xc8, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80 - }; - - const WebRtc_UWord8 mask45_3[18] = - { - 0xca, 0xd9, 0x5b, 0x2b, 0x4d, 0x90, - 0xf1, 0x5e, 0x2b, 0xc5, 0x24, 0xe8, - 0xb6, 0x35, 0xc5, 0xd8, 0x9f, 0x40 - }; - - const WebRtc_UWord8 mask45_30[180] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0x0c, 0xa1, 0x94, 0x32, 0x90, 0xc0, - 0xa1, 0x34, 0x26, 0x84, 0x89, 0x18, - 0x12, 0xa2, 0x54, 0x4a, 0x84, 0x70, - 0x8a, 0x51, 0x4a, 0x29, 0x17, 0x00, - 0xc6, 0xca, 0xea, 0x70, 0xfe, 0xc8, - 0x1c, 0xc9, 0x43, 0x25, 0xa7, 0x00 - }; - - const WebRtc_UWord8 mask45_31[186] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x86, 0xc1, 0x47, 0xeb, 0x67, 0xd0 - }; - - const WebRtc_UWord8 mask45_32[192] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x86, 0xc1, 0x47, 0xeb, 0x67, 0xd0, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x40, 0x7e, 0xc1, 0x30, 0x29, 0x50 - }; - - const WebRtc_UWord8 mask45_33[198] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x86, 0xc1, 0x47, 0xeb, 0x67, 0xd0, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70 - }; - - const WebRtc_UWord8 mask45_34[204] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x91, 0x92, 0x32, 0x46, 0x48, 0x48, - 0xa4, 0x34, 0x86, 0x90, 0x81, 0x28, - 0x50, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0x84, 0xd0, 0x9a, 0x13, 0x16, 0x00, - 0x09, 0x61, 0x2c, 0x25, 0xc4, 0x30, - 0x86, 0xc1, 0x47, 0xeb, 0x67, 0xd0, - 0x1f, 0x78, 0x45, 0x5e, 0x46, 0x50 - }; - - const WebRtc_UWord8 mask45_35[210] = - { - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0xe4, 0xd4, 0x6e, 0x08, 0xc9, 0x58 - }; - - const WebRtc_UWord8 mask45_36[216] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0xe4, 0xd4, 0x6e, 0x08, 0xc9, 0x58, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0xd0, 0x1a, 0xf0, 0x14, 0xf0, 0xe8 - }; - - const WebRtc_UWord8 mask45_37[222] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0xe4, 0xd4, 0x6e, 0x08, 0xc9, 0x58, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40 - }; - - const WebRtc_UWord8 mask45_38[228] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x0c, 0x81, 0x90, 0x32, 0x10, 0x30, - 0x80, 0x70, 0x0e, 0x01, 0xc0, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x21, 0x20, - 0x05, 0x40, 0xa8, 0x15, 0x00, 0xc8, - 0x43, 0x08, 0x61, 0x0c, 0x0a, 0x08, - 0x1a, 0x03, 0x40, 0x68, 0x05, 0x40, - 0x60, 0x2c, 0x05, 0x80, 0x9c, 0x00, - 0x14, 0x22, 0x84, 0x50, 0xe2, 0x80, - 0xe4, 0xd4, 0x6e, 0x08, 0xc9, 0x58, - 0x04, 0x67, 0x1b, 0xba, 0x1d, 0xa0 - }; - - const WebRtc_UWord8 mask45_39[234] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x3b, 0x48, 0xc7, 0x6d, 0x29, 0xe8 - }; - - const WebRtc_UWord8 mask45_4[24] = - { - 0xc4, 0xd8, 0x9b, 0x13, 0x45, 0x90, - 0x31, 0x66, 0x2c, 0xc5, 0x8a, 0x58, - 0x4b, 0x29, 0x65, 0x2c, 0x91, 0x68, - 0x2c, 0xa5, 0x94, 0xb2, 0xa2, 0xa8 - }; - - const WebRtc_UWord8 mask45_40[240] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x3b, 0x48, 0xc7, 0x6d, 0x29, 0xe8, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0xd9, 0x40, 0x46, 0xe6, 0x4f, 0xd8 - }; - - const WebRtc_UWord8 mask45_41[246] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x3b, 0x48, 0xc7, 0x6d, 0x29, 0xe8, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10 - }; - - const WebRtc_UWord8 mask45_42[252] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x62, 0x6c, 0x4d, 0x89, 0xf2, 0x10, - 0x8c, 0x11, 0x82, 0x30, 0x12, 0x20, - 0x01, 0x60, 0x2c, 0x05, 0xd0, 0x88, - 0x07, 0x80, 0xf0, 0x1e, 0x0c, 0x18, - 0xa0, 0x94, 0x12, 0x82, 0x01, 0xc8, - 0x18, 0xa3, 0x14, 0x62, 0xc5, 0x08, - 0x91, 0x12, 0x22, 0x44, 0x02, 0x48, - 0x78, 0x0f, 0x01, 0xe0, 0x00, 0x70, - 0x3b, 0x48, 0xc7, 0x6d, 0x29, 0xe8, - 0xac, 0xcc, 0x04, 0x41, 0x97, 0x30 - }; - - const WebRtc_UWord8 mask45_43[258] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x9e, 0xce, 0x8b, 0xaa, 0x34, 0x68 - }; - - const WebRtc_UWord8 mask45_44[264] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x9e, 0xce, 0x8b, 0xaa, 0x34, 0x68, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0xf8, 0x40, 0xe3, 0x2e, 0x16, 0x00 - }; - - const WebRtc_UWord8 mask45_45[270] = - { - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x22, 0x88, - 0xa9, 0x15, 0x22, 0xa4, 0x40, 0x68, - 0x04, 0xc0, 0x98, 0x13, 0x12, 0x10, - 0xd0, 0x1a, 0x03, 0x40, 0x05, 0x10, - 0x82, 0x50, 0x4a, 0x09, 0x00, 0x70, - 0x21, 0x24, 0x24, 0x84, 0xdc, 0x00, - 0x0c, 0x21, 0x84, 0x30, 0x84, 0x88, - 0x4a, 0x09, 0x41, 0x28, 0x2b, 0x00, - 0x12, 0xa2, 0x54, 0x4a, 0xd1, 0x40, - 0x9e, 0xce, 0x8b, 0xaa, 0x34, 0x68, - 0x46, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x33, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x99, 0x13, 0x22, 0x64, 0x08, 0x48, - 0x05, 0x80, 0xb0, 0x16, 0x00, 0x38, - 0x80, 0xb0, 0x16, 0x02, 0x86, 0x08, - 0x84, 0x50, 0x8a, 0x11, 0x20, 0x60, - 0x40, 0x68, 0x0d, 0x01, 0xb5, 0x00, - 0x0a, 0x81, 0x50, 0x2a, 0x43, 0x00, - 0x68, 0x0d, 0x01, 0xa0, 0x12, 0x40, - 0x10, 0x22, 0x04, 0x40, 0xc4, 0x80, - 0x30, 0x46, 0x08, 0xc1, 0x60, 0x10, - 0x64, 0x4c, 0x89, 0x19, 0x08, 0x30, - 0x51, 0x4a, 0x28, 0xcc, 0x81, 0x18, - 0xa9, 0x15, 0x22, 0x64, 0x20, 0x28, - 0x04, 0xc0, 0x98, 0x16, 0x10, 0xc0, - 0xd0, 0x1a, 0x02, 0x02, 0xc0, 0x88, - 0x82, 0x50, 0x4a, 0x11, 0x0a, 0x40, - 0x21, 0x24, 0x25, 0x01, 0xcc, 0x00, - 0x0c, 0x21, 0x84, 0x2a, 0x04, 0x48, - 0x4a, 0x09, 0x41, 0xa0, 0x31, 0x00, - 0x12, 0xa2, 0x54, 0x40, 0x92, 0x10, - 0x9e, 0xce, 0x88, 0xc1, 0x45, 0x00, - 0xfb, 0x97, 0x5d, 0x7d, 0x42, 0x20 - }; - - const WebRtc_UWord8 mask45_5[30] = - { - 0xc6, 0xd8, 0xdb, 0x1b, 0x29, 0xb0, - 0x63, 0x6c, 0x6d, 0x8d, 0xb2, 0x58, - 0x1d, 0xa3, 0xb4, 0x76, 0x87, 0x70, - 0xad, 0x55, 0xaa, 0xb5, 0x54, 0xe0, - 0xb2, 0xb6, 0x56, 0xca, 0xdc, 0x18 - }; - - const WebRtc_UWord8 mask45_6[36] = - { - 0x64, 0x4c, 0x89, 0x91, 0x28, 0xa0, - 0x51, 0x4a, 0x29, 0x45, 0x62, 0x88, - 0xa8, 0x35, 0x04, 0x32, 0x90, 0xc0, - 0xc4, 0xa0, 0x96, 0x84, 0x89, 0x18, - 0x03, 0x60, 0x6c, 0x4a, 0x84, 0x70, - 0x90, 0xd2, 0x1a, 0x29, 0x17, 0x00 - }; - - const WebRtc_UWord8 mask45_7[42] = - { - 0xc6, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x13, 0x26, 0x64, 0xcc, 0x90, 0x98, - 0x8d, 0x13, 0xa2, 0x46, 0x48, 0x48, - 0x8b, 0x41, 0x6a, 0x90, 0x81, 0x28, - 0x52, 0xaa, 0x15, 0x42, 0x83, 0x50, - 0xa2, 0xd4, 0x1a, 0x13, 0x16, 0x00, - 0x61, 0xa8, 0x2c, 0x25, 0xc4, 0x30 - }; - - const WebRtc_UWord8 mask45_8[48] = - { - 0x28, 0x85, 0x38, 0x32, 0x10, 0x30, - 0x21, 0xf4, 0x06, 0x01, 0xc0, 0x18, - 0xe9, 0x1d, 0x02, 0x82, 0x21, 0x20, - 0x17, 0x02, 0xe0, 0x15, 0x00, 0xc8, - 0x83, 0xa0, 0x55, 0x0c, 0x0a, 0x08, - 0x46, 0x18, 0xe8, 0x68, 0x05, 0x40, - 0x50, 0x6a, 0x0d, 0x80, 0x9c, 0x00, - 0x1c, 0x23, 0x84, 0x50, 0xe2, 0x80 - }; - - const WebRtc_UWord8 mask45_9[54] = - { - 0x44, 0x48, 0xc9, 0x19, 0x29, 0xb0, - 0x28, 0x2d, 0x0d, 0x89, 0xf2, 0x10, - 0x25, 0x14, 0xa2, 0x30, 0x12, 0x20, - 0x59, 0x0a, 0x20, 0x05, 0xd0, 0x88, - 0x03, 0xa0, 0x34, 0x1e, 0x0c, 0x18, - 0xc0, 0xd0, 0x1a, 0x82, 0x01, 0xc8, - 0xa2, 0x30, 0x44, 0x62, 0xc5, 0x08, - 0x14, 0x82, 0xd2, 0x44, 0x02, 0x48, - 0x9a, 0x03, 0x81, 0xe0, 0x00, 0x70 - }; - - const WebRtc_UWord8 mask46_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc - }; - - const WebRtc_UWord8 mask46_10[60] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0 - }; - - const WebRtc_UWord8 mask46_11[66] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08 - }; - - const WebRtc_UWord8 mask46_12[72] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x86, 0x8a, 0x6d, 0x0d, 0x14, 0xd8, - 0x23, 0x2c, 0x84, 0x46, 0x59, 0x08, - 0x16, 0x21, 0x98, 0x2c, 0x43, 0x30, - 0x4c, 0x30, 0x54, 0x98, 0x60, 0xa8, - 0x41, 0xc1, 0x26, 0x83, 0x82, 0x4c, - 0x19, 0x56, 0xe4, 0x32, 0xad, 0xc8 - }; - - const WebRtc_UWord8 mask46_13[78] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18 - }; - - const WebRtc_UWord8 mask46_14[84] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x9c, 0x3f, 0xb3, 0x38, 0x7f, 0x64 - }; - - const WebRtc_UWord8 mask46_15[90] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40 - }; - - const WebRtc_UWord8 mask46_16[96] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0xd9, 0xf5, 0xf5, 0xb3, 0xe8 - }; - - const WebRtc_UWord8 mask46_17[102] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38 - }; - - const WebRtc_UWord8 mask46_18[108] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x82, 0x32, 0x57, 0x04, 0x64, 0xac - }; - - const WebRtc_UWord8 mask46_19[114] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0 - }; - - const WebRtc_UWord8 mask46_2[12] = - { - 0xec, 0xdd, 0x99, 0xd9, 0xbb, 0x30, - 0x9b, 0xb2, 0x77, 0x37, 0x64, 0xec - }; - - const WebRtc_UWord8 mask46_20[120] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0xdb, 0x4a, 0x7b, 0xb6, 0x94, 0xf4 - }; - - const WebRtc_UWord8 mask46_21[126] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08 - }; - - const WebRtc_UWord8 mask46_22[132] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0xea, 0x8d, 0x1b, 0xd5, 0x1a, 0x34 - }; - - const WebRtc_UWord8 mask46_23[138] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x46, 0x42, 0x0c, 0x8c, 0x84, 0x18, - 0x33, 0x20, 0x46, 0x66, 0x40, 0x8c, - 0x99, 0x08, 0x0b, 0x32, 0x10, 0x14, - 0x05, 0x84, 0x30, 0x0b, 0x08, 0x60, - 0x80, 0xb0, 0x23, 0x01, 0x60, 0x44, - 0x84, 0x42, 0x91, 0x08, 0x85, 0x20, - 0x40, 0x73, 0x00, 0x80, 0xe6, 0x00, - 0x0a, 0x81, 0x12, 0x15, 0x02, 0x24, - 0x68, 0x0c, 0x40, 0xd0, 0x18, 0x80, - 0x10, 0x24, 0x84, 0x20, 0x49, 0x08, - 0x30, 0x51, 0x40, 0x60, 0xa2, 0x80, - 0x5f, 0x50, 0x88, 0xbe, 0xa1, 0x10 - }; - - const WebRtc_UWord8 mask46_24[144] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x86, 0x8a, 0x6d, 0x0d, 0x14, 0xd8, - 0x23, 0x2c, 0x84, 0x46, 0x59, 0x08, - 0x16, 0x21, 0x98, 0x2c, 0x43, 0x30, - 0x4c, 0x30, 0x54, 0x98, 0x60, 0xa8, - 0x41, 0xc1, 0x26, 0x83, 0x82, 0x4c, - 0x19, 0x56, 0xe4, 0x32, 0xad, 0xc8, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x18, 0x8b, 0x03, 0xb4, 0x3b, 0x10 - }; - - const WebRtc_UWord8 mask46_25[150] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x86, 0x8a, 0x6d, 0x0d, 0x14, 0xd8, - 0x23, 0x2c, 0x84, 0x46, 0x59, 0x08, - 0x16, 0x21, 0x98, 0x2c, 0x43, 0x30, - 0x4c, 0x30, 0x54, 0x98, 0x60, 0xa8, - 0x41, 0xc1, 0x26, 0x83, 0x82, 0x4c, - 0x19, 0x56, 0xe4, 0x32, 0xad, 0xc8, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18 - }; - - const WebRtc_UWord8 mask46_26[156] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x86, 0x8a, 0x6d, 0x0d, 0x14, 0xd8, - 0x23, 0x2c, 0x84, 0x46, 0x59, 0x08, - 0x16, 0x21, 0x98, 0x2c, 0x43, 0x30, - 0x4c, 0x30, 0x54, 0x98, 0x60, 0xa8, - 0x41, 0xc1, 0x26, 0x83, 0x82, 0x4c, - 0x19, 0x56, 0xe4, 0x32, 0xad, 0xc8, - 0x2d, 0x6d, 0xd2, 0x57, 0xd6, 0x2c - }; - - const WebRtc_UWord8 mask46_27[162] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x9c, 0x3f, 0xb3, 0x38, 0x7f, 0x64 - }; - - const WebRtc_UWord8 mask46_28[168] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x9c, 0x3f, 0xb3, 0x38, 0x7f, 0x64, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0x52, 0xf9, 0x72, 0xd9, 0x68 - }; - - const WebRtc_UWord8 mask46_29[174] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x9c, 0x3f, 0xb3, 0x38, 0x7f, 0x64, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40 - }; - - const WebRtc_UWord8 mask46_3[18] = - { - 0xca, 0xd3, 0x65, 0x95, 0xa6, 0xc8, - 0xf1, 0x49, 0x3b, 0xe2, 0x92, 0x74, - 0x76, 0x27, 0xd0, 0xec, 0x4f, 0xa0 - }; - - const WebRtc_UWord8 mask46_30[180] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80, - 0x9c, 0x3f, 0xb3, 0x38, 0x7f, 0x64, - 0x99, 0xf6, 0x0a, 0xdd, 0x16, 0xb0 - }; - - const WebRtc_UWord8 mask46_31[186] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0xd9, 0xf5, 0xf5, 0xb3, 0xe8 - }; - - const WebRtc_UWord8 mask46_32[192] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0xd9, 0xf5, 0xf5, 0xb3, 0xe8, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x69, 0xcd, 0xeb, 0x51, 0xc9, 0xa8 - }; - - const WebRtc_UWord8 mask46_33[198] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0xd9, 0xf5, 0xf5, 0xb3, 0xe8, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38 - }; - - const WebRtc_UWord8 mask46_34[204] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18, - 0xfa, 0xd9, 0xf5, 0xf5, 0xb3, 0xe8, - 0x60, 0xf0, 0x13, 0xf0, 0x4d, 0xe0 - }; - - const WebRtc_UWord8 mask46_35[210] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x82, 0x32, 0x57, 0x04, 0x64, 0xac - }; - - const WebRtc_UWord8 mask46_36[216] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x82, 0x32, 0x57, 0x04, 0x64, 0xac, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x6c, 0x3a, 0x45, 0x70, 0xd7, 0x00 - }; - - const WebRtc_UWord8 mask46_37[222] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x82, 0x32, 0x57, 0x04, 0x64, 0xac, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0 - }; - - const WebRtc_UWord8 mask46_38[228] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40, - 0x82, 0x32, 0x57, 0x04, 0x64, 0xac, - 0x72, 0x2b, 0xa5, 0xd4, 0xb9, 0x30 - }; - - const WebRtc_UWord8 mask46_39[234] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0xdb, 0x4a, 0x7b, 0xb6, 0x94, 0xf4 - }; - - const WebRtc_UWord8 mask46_4[24] = - { - 0xc4, 0xd1, 0x65, 0x89, 0xa2, 0xc8, - 0x31, 0x62, 0x96, 0x62, 0xc5, 0x2c, - 0x4b, 0x24, 0x5a, 0x96, 0x48, 0xb4, - 0x2c, 0xa8, 0xaa, 0x59, 0x51, 0x54 - }; - - const WebRtc_UWord8 mask46_40[240] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0xdb, 0x4a, 0x7b, 0xb6, 0x94, 0xf4, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x7c, 0xc8, 0x93, 0x63, 0x3c, 0x80 - }; - - const WebRtc_UWord8 mask46_41[246] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0xdb, 0x4a, 0x7b, 0xb6, 0x94, 0xf4, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08 - }; - - const WebRtc_UWord8 mask46_42[252] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38, - 0xdb, 0x4a, 0x7b, 0xb6, 0x94, 0xf4, - 0xfc, 0x6e, 0x89, 0x54, 0x4f, 0x00 - }; - - const WebRtc_UWord8 mask46_43[258] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0xea, 0x8d, 0x1b, 0xd5, 0x1a, 0x34 - }; - - const WebRtc_UWord8 mask46_44[264] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0xea, 0x8d, 0x1b, 0xd5, 0x1a, 0x34, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x1a, 0x8a, 0x00, 0x1c, 0x89, 0x54 - }; - - const WebRtc_UWord8 mask46_45[270] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0xea, 0x8d, 0x1b, 0xd5, 0x1a, 0x34, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x46, 0x42, 0x0c, 0x8c, 0x84, 0x18, - 0x33, 0x20, 0x46, 0x66, 0x40, 0x8c, - 0x99, 0x08, 0x0b, 0x32, 0x10, 0x14, - 0x05, 0x84, 0x30, 0x0b, 0x08, 0x60, - 0x80, 0xb0, 0x23, 0x01, 0x60, 0x44, - 0x84, 0x42, 0x91, 0x08, 0x85, 0x20, - 0x40, 0x73, 0x00, 0x80, 0xe6, 0x00, - 0x0a, 0x81, 0x12, 0x15, 0x02, 0x24, - 0x68, 0x0c, 0x40, 0xd0, 0x18, 0x80, - 0x10, 0x24, 0x84, 0x20, 0x49, 0x08, - 0x30, 0x51, 0x40, 0x60, 0xa2, 0x80, - 0x5f, 0x50, 0x88, 0xbe, 0xa1, 0x10 - }; - - const WebRtc_UWord8 mask46_46[276] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x46, 0x42, 0x0c, 0x8c, 0x84, 0x18, - 0x33, 0x20, 0x46, 0x66, 0x40, 0x8c, - 0x99, 0x08, 0x0b, 0x32, 0x10, 0x14, - 0x05, 0x84, 0x30, 0x0b, 0x08, 0x60, - 0x80, 0xb0, 0x23, 0x01, 0x60, 0x44, - 0x84, 0x42, 0x91, 0x08, 0x85, 0x20, - 0x40, 0x73, 0x00, 0x80, 0xe6, 0x00, - 0x0a, 0x81, 0x12, 0x15, 0x02, 0x24, - 0x68, 0x0c, 0x40, 0xd0, 0x18, 0x80, - 0x10, 0x24, 0x84, 0x20, 0x49, 0x08, - 0x30, 0x51, 0x40, 0x60, 0xa2, 0x80, - 0x5f, 0x50, 0x88, 0xbe, 0xa1, 0x10, - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x99, 0x02, 0x13, 0x32, 0x04, 0x24, - 0x05, 0x80, 0x0e, 0x0b, 0x00, 0x1c, - 0x80, 0xa1, 0x83, 0x01, 0x43, 0x04, - 0x84, 0x48, 0x19, 0x08, 0x90, 0x30, - 0x40, 0x6d, 0x40, 0x80, 0xda, 0x80, - 0x0a, 0x90, 0xc0, 0x15, 0x21, 0x80, - 0x68, 0x04, 0x90, 0xd0, 0x09, 0x20, - 0x10, 0x31, 0x20, 0x20, 0x62, 0x40, - 0x30, 0x58, 0x04, 0x60, 0xb0, 0x08, - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x48, 0xa2, 0xa2, 0x91, 0x44, - 0xa9, 0x10, 0x1b, 0x52, 0x20, 0x34, - 0x04, 0xc4, 0x84, 0x09, 0x89, 0x08, - 0xd0, 0x01, 0x45, 0xa0, 0x02, 0x88, - 0x82, 0x40, 0x1d, 0x04, 0x80, 0x38, - 0x21, 0x37, 0x00, 0x42, 0x6e, 0x00, - 0x0c, 0x21, 0x22, 0x18, 0x42, 0x44, - 0x4a, 0x0a, 0xc0, 0x94, 0x15, 0x80, - 0x12, 0xb4, 0x50, 0x25, 0x68, 0xa0, - 0xea, 0x8d, 0x1b, 0xd5, 0x1a, 0x34, - 0xd5, 0xdf, 0x59, 0xb9, 0xba, 0x10 - }; - - const WebRtc_UWord8 mask46_5[30] = - { - 0xc6, 0xca, 0x6d, 0x8d, 0x94, 0xd8, - 0x63, 0x6c, 0x96, 0xc6, 0xd9, 0x2c, - 0x1d, 0xa1, 0xdc, 0x3b, 0x43, 0xb8, - 0xad, 0x55, 0x39, 0x5a, 0xaa, 0x70, - 0xb2, 0xb7, 0x07, 0x65, 0x6e, 0x0c - }; - - const WebRtc_UWord8 mask46_6[36] = - { - 0x64, 0x4a, 0x28, 0xc8, 0x94, 0x50, - 0x51, 0x58, 0xa2, 0xa2, 0xb1, 0x44, - 0x0c, 0xa4, 0x30, 0x19, 0x48, 0x60, - 0xa1, 0x22, 0x47, 0x42, 0x44, 0x8c, - 0x12, 0xa1, 0x1c, 0x25, 0x42, 0x38, - 0x8a, 0x45, 0xc1, 0x14, 0x8b, 0x80 - }; - - const WebRtc_UWord8 mask46_7[42] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x33, 0x24, 0x26, 0x66, 0x48, 0x4c, - 0x91, 0x92, 0x13, 0x23, 0x24, 0x24, - 0xa4, 0x20, 0x4b, 0x48, 0x40, 0x94, - 0x50, 0xa0, 0xd4, 0xa1, 0x41, 0xa8, - 0x84, 0xc5, 0x81, 0x09, 0x8b, 0x00, - 0x09, 0x71, 0x0c, 0x12, 0xe2, 0x18 - }; - - const WebRtc_UWord8 mask46_8[48] = - { - 0x0c, 0x84, 0x0c, 0x19, 0x08, 0x18, - 0x80, 0x70, 0x07, 0x00, 0xe0, 0x0c, - 0xa0, 0x88, 0x49, 0x41, 0x10, 0x90, - 0x05, 0x40, 0x32, 0x0a, 0x80, 0x64, - 0x43, 0x02, 0x82, 0x86, 0x05, 0x04, - 0x1a, 0x01, 0x50, 0x34, 0x02, 0xa0, - 0x60, 0x27, 0x00, 0xc0, 0x4e, 0x00, - 0x14, 0x38, 0xa0, 0x28, 0x71, 0x40 - }; - - const WebRtc_UWord8 mask46_9[54] = - { - 0x46, 0x4a, 0x6c, 0x8c, 0x94, 0xd8, - 0x62, 0x7c, 0x84, 0xc4, 0xf9, 0x08, - 0x8c, 0x04, 0x89, 0x18, 0x09, 0x10, - 0x01, 0x74, 0x22, 0x02, 0xe8, 0x44, - 0x07, 0x83, 0x06, 0x0f, 0x06, 0x0c, - 0xa0, 0x80, 0x73, 0x41, 0x00, 0xe4, - 0x18, 0xb1, 0x42, 0x31, 0x62, 0x84, - 0x91, 0x00, 0x93, 0x22, 0x01, 0x24, - 0x78, 0x00, 0x1c, 0xf0, 0x00, 0x38 - }; - - const WebRtc_UWord8 mask47_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe - }; - - const WebRtc_UWord8 mask47_10[60] = - { - 0x64, 0x4a, 0x28, 0x22, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x68, 0xa6, - 0xa9, 0x10, 0x1a, 0x00, 0x90, 0x0a, - 0x04, 0xc4, 0x84, 0x21, 0x06, 0x12, - 0xd0, 0x01, 0x44, 0x94, 0x29, 0x42, - 0x82, 0x40, 0x1c, 0x81, 0x48, 0x14, - 0x21, 0x37, 0x01, 0x40, 0xd4, 0x04, - 0x0c, 0x21, 0x23, 0x11, 0x01, 0x18, - 0x4a, 0x0a, 0xc1, 0x0c, 0x10, 0xc0, - 0x12, 0xb4, 0x50, 0xa8, 0x1a, 0x80 - }; - - const WebRtc_UWord8 mask47_11[66] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x68, - 0x33, 0x24, 0x27, 0x40, 0x64, 0x22, - 0x99, 0x02, 0x12, 0x2a, 0x22, 0x82, - 0x05, 0x80, 0x0e, 0x06, 0xa0, 0x2a, - 0x80, 0xa1, 0x83, 0x19, 0x11, 0x90, - 0x84, 0x48, 0x18, 0x51, 0x05, 0x10, - 0x40, 0x6d, 0x40, 0x10, 0x91, 0x08, - 0x0a, 0x90, 0xc1, 0x32, 0x03, 0x20, - 0x68, 0x04, 0x90, 0x45, 0x24, 0x52, - 0x10, 0x31, 0x20, 0x8c, 0x08, 0xc0, - 0x30, 0x58, 0x05, 0x18, 0x58, 0x04 - }; - - const WebRtc_UWord8 mask47_12[72] = - { - 0x64, 0x4a, 0x28, 0x20, 0xc2, 0x0c, - 0x51, 0x58, 0xa2, 0x04, 0x60, 0x46, - 0x0c, 0xa4, 0x30, 0x80, 0xa8, 0x0a, - 0xa1, 0x22, 0x46, 0x43, 0x04, 0x30, - 0x12, 0xa1, 0x1d, 0x02, 0x30, 0x22, - 0x8a, 0x45, 0xc0, 0x29, 0x02, 0x90, - 0x86, 0x8a, 0x6d, 0x30, 0x13, 0x00, - 0x23, 0x2c, 0x84, 0x11, 0x21, 0x12, - 0x16, 0x21, 0x98, 0xc4, 0x0c, 0x40, - 0x4c, 0x30, 0x54, 0x48, 0x44, 0x84, - 0x41, 0xc1, 0x27, 0x14, 0x11, 0x40, - 0x19, 0x56, 0xe5, 0x08, 0x90, 0x88 - }; - - const WebRtc_UWord8 mask47_13[78] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c - }; - - const WebRtc_UWord8 mask47_14[84] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x9c, 0x3f, 0xb3, 0xe5, 0xad, 0x1c - }; - - const WebRtc_UWord8 mask47_15[90] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0 - }; - - const WebRtc_UWord8 mask47_16[96] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0xfa, 0xd9, 0xf5, 0xfe, 0xdc, 0x14 - }; - - const WebRtc_UWord8 mask47_17[102] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c - }; - - const WebRtc_UWord8 mask47_18[108] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x82, 0x32, 0x56, 0x68, 0xa1, 0x5c - }; - - const WebRtc_UWord8 mask47_19[114] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50 - }; - - const WebRtc_UWord8 mask47_2[12] = - { - 0xec, 0xdd, 0x99, 0xd9, 0x9d, 0x98, - 0x9b, 0xb2, 0x77, 0x27, 0x72, 0x76 - }; - - const WebRtc_UWord8 mask47_20[120] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0xdb, 0x4a, 0x7b, 0x31, 0x45, 0x2a - }; - - const WebRtc_UWord8 mask47_21[126] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04 - }; - - const WebRtc_UWord8 mask47_22[132] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xea, 0x8d, 0x1a, 0x35, 0x55, 0xdc - }; - - const WebRtc_UWord8 mask47_23[138] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x46, 0x42, 0x0c, 0x20, 0xc2, 0x0c, - 0x33, 0x20, 0x46, 0x04, 0x60, 0x46, - 0x99, 0x08, 0x0a, 0x80, 0xa8, 0x0a, - 0x05, 0x84, 0x30, 0x43, 0x04, 0x30, - 0x80, 0xb0, 0x23, 0x02, 0x30, 0x22, - 0x84, 0x42, 0x90, 0x29, 0x02, 0x90, - 0x40, 0x73, 0x01, 0x30, 0x13, 0x00, - 0x0a, 0x81, 0x12, 0x11, 0x21, 0x12, - 0x68, 0x0c, 0x40, 0xc4, 0x0c, 0x40, - 0x10, 0x24, 0x84, 0x48, 0x44, 0x84, - 0x30, 0x51, 0x41, 0x14, 0x11, 0x40, - 0x5f, 0x50, 0x89, 0x08, 0x90, 0x88 - }; - - const WebRtc_UWord8 mask47_24[144] = - { - 0x64, 0x4a, 0x28, 0x20, 0xc2, 0x0c, - 0x51, 0x58, 0xa2, 0x04, 0x60, 0x46, - 0x0c, 0xa4, 0x30, 0x80, 0xa8, 0x0a, - 0xa1, 0x22, 0x46, 0x43, 0x04, 0x30, - 0x12, 0xa1, 0x1d, 0x02, 0x30, 0x22, - 0x8a, 0x45, 0xc0, 0x29, 0x02, 0x90, - 0x86, 0x8a, 0x6d, 0x30, 0x13, 0x00, - 0x23, 0x2c, 0x84, 0x11, 0x21, 0x12, - 0x16, 0x21, 0x98, 0xc4, 0x0c, 0x40, - 0x4c, 0x30, 0x54, 0x48, 0x44, 0x84, - 0x41, 0xc1, 0x27, 0x14, 0x11, 0x40, - 0x19, 0x56, 0xe5, 0x08, 0x90, 0x88, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x68, - 0x33, 0x24, 0x27, 0x40, 0x64, 0x22, - 0x99, 0x02, 0x12, 0x2a, 0x22, 0x82, - 0x05, 0x80, 0x0e, 0x06, 0xa0, 0x2a, - 0x80, 0xa1, 0x83, 0x19, 0x11, 0x90, - 0x84, 0x48, 0x18, 0x51, 0x05, 0x10, - 0x40, 0x6d, 0x40, 0x10, 0x91, 0x08, - 0x0a, 0x90, 0xc1, 0x32, 0x03, 0x20, - 0x68, 0x04, 0x90, 0x45, 0x24, 0x52, - 0x10, 0x31, 0x20, 0x8c, 0x08, 0xc0, - 0x30, 0x58, 0x05, 0x18, 0x58, 0x04, - 0x27, 0x41, 0x35, 0x57, 0x7e, 0x6a - }; - - const WebRtc_UWord8 mask47_25[150] = - { - 0x64, 0x4a, 0x28, 0x20, 0xc2, 0x0c, - 0x51, 0x58, 0xa2, 0x04, 0x60, 0x46, - 0x0c, 0xa4, 0x30, 0x80, 0xa8, 0x0a, - 0xa1, 0x22, 0x46, 0x43, 0x04, 0x30, - 0x12, 0xa1, 0x1d, 0x02, 0x30, 0x22, - 0x8a, 0x45, 0xc0, 0x29, 0x02, 0x90, - 0x86, 0x8a, 0x6d, 0x30, 0x13, 0x00, - 0x23, 0x2c, 0x84, 0x11, 0x21, 0x12, - 0x16, 0x21, 0x98, 0xc4, 0x0c, 0x40, - 0x4c, 0x30, 0x54, 0x48, 0x44, 0x84, - 0x41, 0xc1, 0x27, 0x14, 0x11, 0x40, - 0x19, 0x56, 0xe5, 0x08, 0x90, 0x88, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c - }; - - const WebRtc_UWord8 mask47_26[156] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0x20, 0xc2, 0x0c, - 0x51, 0x58, 0xa2, 0x04, 0x60, 0x46, - 0x0c, 0xa4, 0x30, 0x80, 0xa8, 0x0a, - 0xa1, 0x22, 0x46, 0x43, 0x04, 0x30, - 0x12, 0xa1, 0x1d, 0x02, 0x30, 0x22, - 0x8a, 0x45, 0xc0, 0x29, 0x02, 0x90, - 0x86, 0x8a, 0x6d, 0x30, 0x13, 0x00, - 0x23, 0x2c, 0x84, 0x11, 0x21, 0x12, - 0x16, 0x21, 0x98, 0xc4, 0x0c, 0x40, - 0x4c, 0x30, 0x54, 0x48, 0x44, 0x84, - 0x41, 0xc1, 0x27, 0x14, 0x11, 0x40, - 0x19, 0x56, 0xe5, 0x08, 0x90, 0x88, - 0x6c, 0xea, 0xc4, 0x42, 0x20, 0x9e - }; - - const WebRtc_UWord8 mask47_27[162] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x9c, 0x3f, 0xb3, 0xe5, 0xad, 0x1c - }; - - const WebRtc_UWord8 mask47_28[168] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x9c, 0x3f, 0xb3, 0xe5, 0xad, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x86, 0x1e, 0xa6, 0xaf, 0x3d, 0x04 - }; - - const WebRtc_UWord8 mask47_29[174] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x9c, 0x3f, 0xb3, 0xe5, 0xad, 0x1c, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0 - }; - - const WebRtc_UWord8 mask47_3[18] = - { - 0xca, 0xd3, 0x65, 0x36, 0x53, 0x64, - 0xf1, 0x49, 0x3a, 0x93, 0xa9, 0x3a, - 0x76, 0x27, 0xd0, 0x7d, 0x07, 0xd0 - }; - - const WebRtc_UWord8 mask47_30[180] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x58, 0xa3, 0x8a, 0x38, 0xa2, - 0x0c, 0xa4, 0x30, 0x43, 0x04, 0x30, - 0xa1, 0x22, 0x46, 0x24, 0x62, 0x46, - 0x12, 0xa1, 0x1c, 0x11, 0xc1, 0x1c, - 0x8a, 0x45, 0xc0, 0x5c, 0x05, 0xc0, - 0x9c, 0x3f, 0xb3, 0xe5, 0xad, 0x1c, - 0x97, 0x43, 0x63, 0xc6, 0x09, 0x9c - }; - - const WebRtc_UWord8 mask47_31[186] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0xfa, 0xd9, 0xf5, 0xfe, 0xdc, 0x14 - }; - - const WebRtc_UWord8 mask47_32[192] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0xfa, 0xd9, 0xf5, 0xfe, 0xdc, 0x14, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0xe5, 0x50, 0x45, 0x63, 0xc2, 0xf4 - }; - - const WebRtc_UWord8 mask47_33[198] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0xfa, 0xd9, 0xf5, 0xfe, 0xdc, 0x14, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c - }; - - const WebRtc_UWord8 mask47_34[204] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x91, 0x92, 0x13, 0x21, 0x32, 0x12, - 0xa4, 0x20, 0x4a, 0x04, 0xa0, 0x4a, - 0x50, 0xa0, 0xd4, 0x0d, 0x40, 0xd4, - 0x84, 0xc5, 0x80, 0x58, 0x05, 0x80, - 0x09, 0x71, 0x0d, 0x10, 0xd1, 0x0c, - 0xfa, 0xd9, 0xf5, 0xfe, 0xdc, 0x14, - 0xef, 0xbb, 0xa6, 0x23, 0x5c, 0xbe - }; - - const WebRtc_UWord8 mask47_35[210] = - { - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x82, 0x32, 0x56, 0x68, 0xa1, 0x5c - }; - - const WebRtc_UWord8 mask47_36[216] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x82, 0x32, 0x56, 0x68, 0xa1, 0x5c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0e, 0xd7, 0x38, 0x20, 0x87, 0x66 - }; - - const WebRtc_UWord8 mask47_37[222] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x82, 0x32, 0x56, 0x68, 0xa1, 0x5c, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50 - }; - - const WebRtc_UWord8 mask47_38[228] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x0c, 0x84, 0x0c, 0x40, 0xc4, 0x0c, - 0x80, 0x70, 0x07, 0x00, 0x70, 0x06, - 0xa0, 0x88, 0x48, 0x84, 0x88, 0x48, - 0x05, 0x40, 0x32, 0x03, 0x20, 0x32, - 0x43, 0x02, 0x82, 0x28, 0x22, 0x82, - 0x1a, 0x01, 0x50, 0x15, 0x01, 0x50, - 0x60, 0x27, 0x00, 0x70, 0x07, 0x00, - 0x14, 0x38, 0xa1, 0x8a, 0x18, 0xa0, - 0x82, 0x32, 0x56, 0x68, 0xa1, 0x5c, - 0x7b, 0x47, 0xa5, 0xde, 0x9a, 0xd4 - }; - - const WebRtc_UWord8 mask47_39[234] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0xdb, 0x4a, 0x7b, 0x31, 0x45, 0x2a - }; - - const WebRtc_UWord8 mask47_4[24] = - { - 0xc4, 0xd1, 0x65, 0x16, 0x51, 0x64, - 0x31, 0x62, 0x96, 0x29, 0x62, 0x96, - 0x4b, 0x24, 0x5a, 0x45, 0xa4, 0x5a, - 0x2c, 0xa8, 0xaa, 0x8a, 0xa8, 0xaa - }; - - const WebRtc_UWord8 mask47_40[240] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0xdb, 0x4a, 0x7b, 0x31, 0x45, 0x2a, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xc4, 0xae, 0x5e, 0x33, 0xf5, 0x1a - }; - - const WebRtc_UWord8 mask47_41[246] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0xdb, 0x4a, 0x7b, 0x31, 0x45, 0x2a, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04 - }; - - const WebRtc_UWord8 mask47_42[252] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x85, 0xc8, 0x5c, 0x84, - 0x8c, 0x04, 0x88, 0x48, 0x84, 0x88, - 0x01, 0x74, 0x23, 0x42, 0x34, 0x22, - 0x07, 0x83, 0x06, 0x30, 0x63, 0x06, - 0xa0, 0x80, 0x72, 0x07, 0x20, 0x72, - 0x18, 0xb1, 0x43, 0x14, 0x31, 0x42, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x01, 0xc0, 0x1c, - 0xdb, 0x4a, 0x7b, 0x31, 0x45, 0x2a, - 0x3c, 0xb0, 0x36, 0x3b, 0x14, 0xa2 - }; - - const WebRtc_UWord8 mask47_43[258] = - { - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xea, 0x8d, 0x1a, 0x35, 0x55, 0xdc - }; - - const WebRtc_UWord8 mask47_44[264] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xea, 0x8d, 0x1a, 0x35, 0x55, 0xdc, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0xd4, 0x8a, 0xd4, 0xd3, 0x3f, 0xe6 - }; - - const WebRtc_UWord8 mask47_45[270] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xea, 0x8d, 0x1a, 0x35, 0x55, 0xdc, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x46, 0x42, 0x0c, 0x20, 0xc2, 0x0c, - 0x33, 0x20, 0x46, 0x04, 0x60, 0x46, - 0x99, 0x08, 0x0a, 0x80, 0xa8, 0x0a, - 0x05, 0x84, 0x30, 0x43, 0x04, 0x30, - 0x80, 0xb0, 0x23, 0x02, 0x30, 0x22, - 0x84, 0x42, 0x90, 0x29, 0x02, 0x90, - 0x40, 0x73, 0x01, 0x30, 0x13, 0x00, - 0x0a, 0x81, 0x12, 0x11, 0x21, 0x12, - 0x68, 0x0c, 0x40, 0xc4, 0x0c, 0x40, - 0x10, 0x24, 0x84, 0x48, 0x44, 0x84, - 0x30, 0x51, 0x41, 0x14, 0x11, 0x40, - 0x5f, 0x50, 0x89, 0x08, 0x90, 0x88 - }; - - const WebRtc_UWord8 mask47_46[276] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x46, 0x42, 0x0c, 0x20, 0xc2, 0x0c, - 0x33, 0x20, 0x46, 0x04, 0x60, 0x46, - 0x99, 0x08, 0x0a, 0x80, 0xa8, 0x0a, - 0x05, 0x84, 0x30, 0x43, 0x04, 0x30, - 0x80, 0xb0, 0x23, 0x02, 0x30, 0x22, - 0x84, 0x42, 0x90, 0x29, 0x02, 0x90, - 0x40, 0x73, 0x01, 0x30, 0x13, 0x00, - 0x0a, 0x81, 0x12, 0x11, 0x21, 0x12, - 0x68, 0x0c, 0x40, 0xc4, 0x0c, 0x40, - 0x10, 0x24, 0x84, 0x48, 0x44, 0x84, - 0x30, 0x51, 0x41, 0x14, 0x11, 0x40, - 0x5f, 0x50, 0x89, 0x08, 0x90, 0x88, - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x64, 0x4a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x48, 0xa2, 0x8a, 0x28, 0xa2, - 0xa9, 0x10, 0x1b, 0x01, 0xb0, 0x1a, - 0x04, 0xc4, 0x84, 0x48, 0x44, 0x84, - 0xd0, 0x01, 0x44, 0x14, 0x41, 0x44, - 0x82, 0x40, 0x1c, 0x01, 0xc0, 0x1c, - 0x21, 0x37, 0x01, 0x70, 0x17, 0x00, - 0x0c, 0x21, 0x22, 0x12, 0x21, 0x22, - 0x4a, 0x0a, 0xc0, 0xac, 0x0a, 0xc0, - 0x12, 0xb4, 0x51, 0x45, 0x14, 0x50, - 0xea, 0x8d, 0x1a, 0x35, 0x55, 0xdc, - 0x37, 0x9d, 0xcf, 0xe0, 0xe4, 0x20 - }; - - const WebRtc_UWord8 mask47_47[282] = - { - 0x46, 0x4a, 0x6c, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x42, 0x64, 0x26, - 0x99, 0x02, 0x12, 0x21, 0x22, 0x12, - 0x05, 0x80, 0x0e, 0x00, 0xe0, 0x0e, - 0x80, 0xa1, 0x82, 0x18, 0x21, 0x82, - 0x84, 0x48, 0x18, 0x81, 0x88, 0x18, - 0x40, 0x6d, 0x40, 0xd4, 0x0d, 0x40, - 0x0a, 0x90, 0xc1, 0x0c, 0x10, 0xc0, - 0x68, 0x04, 0x90, 0x49, 0x04, 0x90, - 0x10, 0x31, 0x21, 0x12, 0x11, 0x20, - 0x30, 0x58, 0x05, 0x80, 0x58, 0x04, - 0x46, 0x42, 0x0c, 0x20, 0xc2, 0x0c, - 0x33, 0x20, 0x46, 0x04, 0x60, 0x46, - 0x99, 0x08, 0x0a, 0x80, 0xa8, 0x0a, - 0x05, 0x84, 0x30, 0x43, 0x04, 0x30, - 0x80, 0xb0, 0x23, 0x02, 0x30, 0x22, - 0x84, 0x42, 0x90, 0x29, 0x02, 0x90, - 0x40, 0x73, 0x01, 0x30, 0x13, 0x00, - 0x0a, 0x81, 0x12, 0x11, 0x21, 0x12, - 0x68, 0x0c, 0x40, 0xc4, 0x0c, 0x40, - 0x10, 0x24, 0x84, 0x48, 0x44, 0x84, - 0x30, 0x51, 0x41, 0x14, 0x11, 0x40, - 0x5f, 0x50, 0x89, 0x08, 0x90, 0x88, - 0x46, 0x4a, 0x6c, 0x20, 0xc2, 0x0c, - 0x33, 0x24, 0x26, 0x04, 0x60, 0x46, - 0x99, 0x02, 0x12, 0x80, 0xa8, 0x0a, - 0x05, 0x80, 0x0e, 0x43, 0x04, 0x30, - 0x80, 0xa1, 0x83, 0x02, 0x30, 0x22, - 0x84, 0x48, 0x18, 0x29, 0x02, 0x90, - 0x40, 0x6d, 0x41, 0x30, 0x13, 0x00, - 0x0a, 0x90, 0xc0, 0x11, 0x21, 0x12, - 0x68, 0x04, 0x90, 0xc4, 0x0c, 0x40, - 0x10, 0x31, 0x20, 0x48, 0x44, 0x84, - 0x30, 0x58, 0x05, 0x14, 0x11, 0x40, - 0x46, 0x42, 0x0d, 0x08, 0x90, 0x88, - 0x33, 0x20, 0x46, 0xa6, 0xca, 0x6c, - 0x99, 0x08, 0x0a, 0x42, 0x64, 0x26, - 0x05, 0x84, 0x30, 0x21, 0x22, 0x12, - 0x80, 0xb0, 0x22, 0x00, 0xe0, 0x0e, - 0x84, 0x42, 0x90, 0x18, 0x21, 0x82, - 0x40, 0x73, 0x00, 0x81, 0x88, 0x18, - 0x0a, 0x81, 0x12, 0xd4, 0x0d, 0x40, - 0x68, 0x0c, 0x41, 0x0c, 0x10, 0xc0, - 0x10, 0x24, 0x84, 0x49, 0x04, 0x90, - 0x30, 0x51, 0x41, 0x12, 0x11, 0x20, - 0x5f, 0x50, 0x89, 0x80, 0x58, 0x04, - 0x1f, 0x2f, 0x63, 0x10, 0x64, 0xb2 - }; - - const WebRtc_UWord8 mask47_5[30] = - { - 0xc6, 0xca, 0x6c, 0xa6, 0xca, 0x6c, - 0x63, 0x6c, 0x96, 0xc9, 0x6c, 0x96, - 0x1d, 0xa1, 0xdc, 0x1d, 0xc1, 0xdc, - 0xad, 0x55, 0x39, 0x53, 0x95, 0x38, - 0xb2, 0xb7, 0x07, 0x70, 0x77, 0x06 - }; - - const WebRtc_UWord8 mask47_6[36] = - { - 0x64, 0x4a, 0x29, 0xa2, 0x9a, 0x28, - 0x51, 0x58, 0xa2, 0x8a, 0x68, 0xa6, - 0x0c, 0xa4, 0x30, 0x45, 0xa4, 0x5a, - 0xa1, 0x22, 0x46, 0x2d, 0x82, 0xd8, - 0x12, 0xa1, 0x1c, 0x17, 0x41, 0x74, - 0x8a, 0x45, 0xc1, 0xd1, 0x1d, 0x10 - }; - - const WebRtc_UWord8 mask47_7[42] = - { - 0x46, 0x4a, 0x6d, 0xa6, 0xca, 0x6c, - 0x33, 0x24, 0x26, 0x4a, 0x64, 0xa6, - 0x91, 0x92, 0x12, 0x61, 0xa6, 0x0a, - 0xa4, 0x20, 0x4a, 0x0c, 0x90, 0xd8, - 0x50, 0xa0, 0xd5, 0x81, 0x70, 0x36, - 0x84, 0xc5, 0x80, 0x55, 0x45, 0x54, - 0x09, 0x71, 0x0d, 0x50, 0x9d, 0x08 - }; - - const WebRtc_UWord8 mask47_8[48] = - { - 0x0c, 0x84, 0x0d, 0x02, 0xc0, 0x2c, - 0x80, 0x70, 0x06, 0x80, 0x78, 0x06, - 0xa0, 0x88, 0x48, 0x21, 0x22, 0x12, - 0x05, 0x40, 0x32, 0x0c, 0xa0, 0xca, - 0x43, 0x02, 0x82, 0x40, 0x95, 0x08, - 0x1a, 0x01, 0x51, 0x15, 0x41, 0x54, - 0x60, 0x27, 0x00, 0x66, 0x06, 0x60, - 0x14, 0x38, 0xa0, 0x99, 0x09, 0x90 - }; - - const WebRtc_UWord8 mask47_9[54] = - { - 0x46, 0x4a, 0x6d, 0xa6, 0xca, 0x6c, - 0x62, 0x7c, 0x84, 0xc8, 0x4c, 0x84, - 0x8c, 0x04, 0x88, 0x30, 0x83, 0x88, - 0x01, 0x74, 0x23, 0x40, 0x94, 0x08, - 0x07, 0x83, 0x07, 0x02, 0x70, 0x26, - 0xa0, 0x80, 0x72, 0x45, 0x44, 0x54, - 0x18, 0xb1, 0x42, 0x10, 0xe1, 0x0e, - 0x91, 0x00, 0x92, 0x09, 0x20, 0x92, - 0x78, 0x00, 0x1c, 0x03, 0x80, 0x38 - }; - - const WebRtc_UWord8 mask48_1[6] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - - const WebRtc_UWord8 mask48_10[60] = - { - 0x11, 0x45, 0x14, 0x11, 0x45, 0x14, - 0x45, 0x34, 0x53, 0x45, 0x34, 0x53, - 0x00, 0x48, 0x05, 0x00, 0x48, 0x05, - 0x10, 0x83, 0x09, 0x10, 0x83, 0x09, - 0x4a, 0x14, 0xa1, 0x4a, 0x14, 0xa1, - 0x40, 0xa4, 0x0a, 0x40, 0xa4, 0x0a, - 0xa0, 0x6a, 0x02, 0xa0, 0x6a, 0x02, - 0x88, 0x80, 0x8c, 0x88, 0x80, 0x8c, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x54, 0x0d, 0x40, 0x54, 0x0d, 0x40 - }; - - const WebRtc_UWord8 mask48_11[66] = - { - 0x53, 0x65, 0x34, 0x53, 0x65, 0x34, - 0xa0, 0x32, 0x11, 0xa0, 0x32, 0x11, - 0x15, 0x11, 0x41, 0x15, 0x11, 0x41, - 0x03, 0x50, 0x15, 0x03, 0x50, 0x15, - 0x8c, 0x88, 0xc8, 0x8c, 0x88, 0xc8, - 0x28, 0x82, 0x88, 0x28, 0x82, 0x88, - 0x08, 0x48, 0x84, 0x08, 0x48, 0x84, - 0x99, 0x01, 0x90, 0x99, 0x01, 0x90, - 0x22, 0x92, 0x29, 0x22, 0x92, 0x29, - 0x46, 0x04, 0x60, 0x46, 0x04, 0x60, - 0x8c, 0x2c, 0x02, 0x8c, 0x2c, 0x02 - }; - - const WebRtc_UWord8 mask48_12[72] = - { - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44 - }; - - const WebRtc_UWord8 mask48_13[78] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86 - }; - - const WebRtc_UWord8 mask48_14[84] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0xf2, 0xd6, 0x8e, 0xf2, 0xd6, 0x8e - }; - - const WebRtc_UWord8 mask48_15[90] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50 - }; - - const WebRtc_UWord8 mask48_16[96] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0xff, 0x6e, 0x0a, 0xff, 0x6e, 0x0a - }; - - const WebRtc_UWord8 mask48_17[102] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e - }; - - const WebRtc_UWord8 mask48_18[108] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x34, 0x50, 0xae, 0x34, 0x50, 0xae - }; - - const WebRtc_UWord8 mask48_19[114] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28 - }; - - const WebRtc_UWord8 mask48_2[12] = - { - 0xec, 0xce, 0xcc, 0xec, 0xce, 0xcc, - 0x93, 0xb9, 0x3b, 0x93, 0xb9, 0x3b - }; - - const WebRtc_UWord8 mask48_20[120] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x98, 0xa2, 0x95, 0x98, 0xa2, 0x95 - }; - - const WebRtc_UWord8 mask48_21[126] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02 - }; - - const WebRtc_UWord8 mask48_22[132] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x1a, 0xaa, 0xee, 0x1a, 0xaa, 0xee - }; - - const WebRtc_UWord8 mask48_23[138] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44 - }; - - const WebRtc_UWord8 mask48_24[144] = - { - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x88, 0x32, 0x59, 0x88, 0x32, 0x59 - }; - - const WebRtc_UWord8 mask48_25[150] = - { - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86 - }; - - const WebRtc_UWord8 mask48_26[156] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x3e, 0x20, 0x79, 0xe5, 0x55, 0x70 - }; - - const WebRtc_UWord8 mask48_27[162] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0xf2, 0xd6, 0x8e, 0xf2, 0xd6, 0x8e - }; - - const WebRtc_UWord8 mask48_28[168] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0xf2, 0xd6, 0x8e, 0xf2, 0xd6, 0x8e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x32, 0xe3, 0xc0, 0x4a, 0xf2, 0x2a - }; - - const WebRtc_UWord8 mask48_29[174] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0xf2, 0xd6, 0x8e, 0xf2, 0xd6, 0x8e, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50 - }; - - const WebRtc_UWord8 mask48_3[18] = - { - 0x9b, 0x29, 0xb2, 0x9b, 0x29, 0xb2, - 0x49, 0xd4, 0x9d, 0x49, 0xd4, 0x9d, - 0x3e, 0x83, 0xe8, 0x3e, 0x83, 0xe8 - }; - - const WebRtc_UWord8 mask48_30[180] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0xc5, 0x1c, 0x51, 0xc5, 0x1c, 0x51, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x12, 0x31, 0x23, 0x12, 0x31, 0x23, - 0x08, 0xe0, 0x8e, 0x08, 0xe0, 0x8e, - 0x2e, 0x02, 0xe0, 0x2e, 0x02, 0xe0, - 0xf2, 0xd6, 0x8e, 0xf2, 0xd6, 0x8e, - 0x66, 0xf3, 0x9a, 0xdd, 0x68, 0x93 - }; - - const WebRtc_UWord8 mask48_31[186] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0xff, 0x6e, 0x0a, 0xff, 0x6e, 0x0a - }; - - const WebRtc_UWord8 mask48_32[192] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0xff, 0x6e, 0x0a, 0xff, 0x6e, 0x0a, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0xd5, 0x4a, 0x4f, 0x48, 0xb5, 0x31 - }; - - const WebRtc_UWord8 mask48_33[198] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0xff, 0x6e, 0x0a, 0xff, 0x6e, 0x0a, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e - }; - - const WebRtc_UWord8 mask48_34[204] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x90, 0x99, 0x09, 0x90, 0x99, 0x09, - 0x02, 0x50, 0x25, 0x02, 0x50, 0x25, - 0x06, 0xa0, 0x6a, 0x06, 0xa0, 0x6a, - 0x2c, 0x02, 0xc0, 0x2c, 0x02, 0xc0, - 0x88, 0x68, 0x86, 0x88, 0x68, 0x86, - 0xff, 0x6e, 0x0a, 0xff, 0x6e, 0x0a, - 0x40, 0x72, 0x4c, 0xe8, 0xf2, 0x42 - }; - - const WebRtc_UWord8 mask48_35[210] = - { - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x34, 0x50, 0xae, 0x34, 0x50, 0xae - }; - - const WebRtc_UWord8 mask48_36[216] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x34, 0x50, 0xae, 0x34, 0x50, 0xae, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x71, 0xba, 0x8b, 0xf3, 0xfa, 0x9d - }; - - const WebRtc_UWord8 mask48_37[222] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x34, 0x50, 0xae, 0x34, 0x50, 0xae, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28 - }; - - const WebRtc_UWord8 mask48_38[228] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x20, 0x62, 0x06, 0x20, 0x62, 0x06, - 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, - 0x42, 0x44, 0x24, 0x42, 0x44, 0x24, - 0x01, 0x90, 0x19, 0x01, 0x90, 0x19, - 0x14, 0x11, 0x41, 0x14, 0x11, 0x41, - 0x0a, 0x80, 0xa8, 0x0a, 0x80, 0xa8, - 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, - 0xc5, 0x0c, 0x50, 0xc5, 0x0c, 0x50, - 0x34, 0x50, 0xae, 0x34, 0x50, 0xae, - 0x2a, 0x7a, 0xf6, 0x8c, 0xde, 0x51 - }; - - const WebRtc_UWord8 mask48_39[234] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x98, 0xa2, 0x95, 0x98, 0xa2, 0x95 - }; - - const WebRtc_UWord8 mask48_4[24] = - { - 0x8b, 0x28, 0xb2, 0x8b, 0x28, 0xb2, - 0x14, 0xb1, 0x4b, 0x14, 0xb1, 0x4b, - 0x22, 0xd2, 0x2d, 0x22, 0xd2, 0x2d, - 0x45, 0x54, 0x55, 0x45, 0x54, 0x55 - }; - - const WebRtc_UWord8 mask48_40[240] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x98, 0xa2, 0x95, 0x98, 0xa2, 0x95, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x20, 0x5f, 0x68, 0xd5, 0xa2, 0x1b - }; - - const WebRtc_UWord8 mask48_41[246] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x98, 0xa2, 0x95, 0x98, 0xa2, 0x95, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02 - }; - - const WebRtc_UWord8 mask48_42[252] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0xe4, 0x2e, 0x42, 0xe4, 0x2e, 0x42, - 0x24, 0x42, 0x44, 0x24, 0x42, 0x44, - 0xa1, 0x1a, 0x11, 0xa1, 0x1a, 0x11, - 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, - 0x03, 0x90, 0x39, 0x03, 0x90, 0x39, - 0x8a, 0x18, 0xa1, 0x8a, 0x18, 0xa1, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0x98, 0xa2, 0x95, 0x98, 0xa2, 0x95, - 0x66, 0xcf, 0xa3, 0x47, 0x69, 0x00 - }; - - const WebRtc_UWord8 mask48_43[258] = - { - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x1a, 0xaa, 0xee, 0x1a, 0xaa, 0xee - }; - - const WebRtc_UWord8 mask48_44[264] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x1a, 0xaa, 0xee, 0x1a, 0xaa, 0xee, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0xc6, 0x40, 0x1f, 0x57, 0xc6, 0xe6 - }; - - const WebRtc_UWord8 mask48_45[270] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x1a, 0xaa, 0xee, 0x1a, 0xaa, 0xee, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44 - }; - - const WebRtc_UWord8 mask48_46[276] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x51, 0x45, 0x14, 0x51, 0x45, 0x14, - 0x45, 0x14, 0x51, 0x45, 0x14, 0x51, - 0x80, 0xd8, 0x0d, 0x80, 0xd8, 0x0d, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x0a, 0x20, 0xa2, 0x0a, 0x20, 0xa2, - 0x00, 0xe0, 0x0e, 0x00, 0xe0, 0x0e, - 0xb8, 0x0b, 0x80, 0xb8, 0x0b, 0x80, - 0x09, 0x10, 0x91, 0x09, 0x10, 0x91, - 0x56, 0x05, 0x60, 0x56, 0x05, 0x60, - 0xa2, 0x8a, 0x28, 0xa2, 0x8a, 0x28, - 0x1a, 0xaa, 0xee, 0x1a, 0xaa, 0xee, - 0x10, 0xf9, 0xab, 0x12, 0x14, 0xef - }; - - const WebRtc_UWord8 mask48_47[282] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x88, 0x32, 0x59, 0x88, 0x32, 0x59 - }; - - const WebRtc_UWord8 mask48_48[288] = - { - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x88, 0x32, 0x59, 0x88, 0x32, 0x59, - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x21, 0x32, 0x13, 0x21, 0x32, 0x13, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x00, 0x70, 0x07, 0x00, 0x70, 0x07, - 0x0c, 0x10, 0xc1, 0x0c, 0x10, 0xc1, - 0x40, 0xc4, 0x0c, 0x40, 0xc4, 0x0c, - 0x6a, 0x06, 0xa0, 0x6a, 0x06, 0xa0, - 0x86, 0x08, 0x60, 0x86, 0x08, 0x60, - 0x24, 0x82, 0x48, 0x24, 0x82, 0x48, - 0x89, 0x08, 0x90, 0x89, 0x08, 0x90, - 0xc0, 0x2c, 0x02, 0xc0, 0x2c, 0x02, - 0x10, 0x61, 0x06, 0x10, 0x61, 0x06, - 0x02, 0x30, 0x23, 0x02, 0x30, 0x23, - 0x40, 0x54, 0x05, 0x40, 0x54, 0x05, - 0x21, 0x82, 0x18, 0x21, 0x82, 0x18, - 0x81, 0x18, 0x11, 0x81, 0x18, 0x11, - 0x14, 0x81, 0x48, 0x14, 0x81, 0x48, - 0x98, 0x09, 0x80, 0x98, 0x09, 0x80, - 0x08, 0x90, 0x89, 0x08, 0x90, 0x89, - 0x62, 0x06, 0x20, 0x62, 0x06, 0x20, - 0x24, 0x22, 0x42, 0x24, 0x22, 0x42, - 0x8a, 0x08, 0xa0, 0x8a, 0x08, 0xa0, - 0x84, 0x48, 0x44, 0x84, 0x48, 0x44, - 0xff, 0x9b, 0xdf, 0xec, 0xae, 0x0e - }; - - const WebRtc_UWord8 mask48_5[30] = - { - 0x53, 0x65, 0x36, 0x53, 0x65, 0x36, - 0x64, 0xb6, 0x4b, 0x64, 0xb6, 0x4b, - 0x0e, 0xe0, 0xee, 0x0e, 0xe0, 0xee, - 0xa9, 0xca, 0x9c, 0xa9, 0xca, 0x9c, - 0xb8, 0x3b, 0x83, 0xb8, 0x3b, 0x83 - }; - - const WebRtc_UWord8 mask48_6[36] = - { - 0xd1, 0x4d, 0x14, 0xd1, 0x4d, 0x14, - 0x45, 0x34, 0x53, 0x45, 0x34, 0x53, - 0x22, 0xd2, 0x2d, 0x22, 0xd2, 0x2d, - 0x16, 0xc1, 0x6c, 0x16, 0xc1, 0x6c, - 0x0b, 0xa0, 0xba, 0x0b, 0xa0, 0xba, - 0xe8, 0x8e, 0x88, 0xe8, 0x8e, 0x88 - }; - - const WebRtc_UWord8 mask48_7[42] = - { - 0xd3, 0x65, 0x36, 0xd3, 0x65, 0x36, - 0x25, 0x32, 0x53, 0x25, 0x32, 0x53, - 0x30, 0xd3, 0x05, 0x30, 0xd3, 0x05, - 0x06, 0x48, 0x6c, 0x06, 0x48, 0x6c, - 0xc0, 0xb8, 0x1b, 0xc0, 0xb8, 0x1b, - 0x2a, 0xa2, 0xaa, 0x2a, 0xa2, 0xaa, - 0xa8, 0x4e, 0x84, 0xa8, 0x4e, 0x84 - }; - - const WebRtc_UWord8 mask48_8[48] = - { - 0x81, 0x60, 0x16, 0x81, 0x60, 0x16, - 0x40, 0x3c, 0x03, 0x40, 0x3c, 0x03, - 0x10, 0x91, 0x09, 0x10, 0x91, 0x09, - 0x06, 0x50, 0x65, 0x06, 0x50, 0x65, - 0x20, 0x4a, 0x84, 0x20, 0x4a, 0x84, - 0x8a, 0xa0, 0xaa, 0x8a, 0xa0, 0xaa, - 0x33, 0x03, 0x30, 0x33, 0x03, 0x30, - 0x4c, 0x84, 0xc8, 0x4c, 0x84, 0xc8 - }; - - const WebRtc_UWord8 mask48_9[54] = - { - 0xd3, 0x65, 0x36, 0xd3, 0x65, 0x36, - 0x64, 0x26, 0x42, 0x64, 0x26, 0x42, - 0x18, 0x41, 0xc4, 0x18, 0x41, 0xc4, - 0xa0, 0x4a, 0x04, 0xa0, 0x4a, 0x04, - 0x81, 0x38, 0x13, 0x81, 0x38, 0x13, - 0x22, 0xa2, 0x2a, 0x22, 0xa2, 0x2a, - 0x08, 0x70, 0x87, 0x08, 0x70, 0x87, - 0x04, 0x90, 0x49, 0x04, 0x90, 0x49, - 0x01, 0xc0, 0x1c, 0x01, 0xc0, 0x1c - }; - - const WebRtc_UWord8 mask4_1[2] = - { - 0xf0, 0x00 - }; - - const WebRtc_UWord8 mask4_2[4] = - { - 0xe0, 0x00, - 0xd0, 0x00 - }; - - const WebRtc_UWord8 mask4_3[6] = - { - 0xc0, 0x00, - 0xb0, 0x00, - 0x60, 0x00 - }; - - const WebRtc_UWord8 mask4_4[8] = - { - 0xc0, 0x00, - 0xa0, 0x00, - 0x30, 0x00, - 0x50, 0x00 - }; - - const WebRtc_UWord8 mask5_1[2] = - { - 0xf8, 0x00 - }; - - const WebRtc_UWord8 mask5_2[4] = - { - 0xe0, 0x00, - 0x98, 0x00 - }; - - const WebRtc_UWord8 mask5_3[6] = - { - 0xe0, 0x00, - 0x90, 0x00, - 0x58, 0x00 - }; - - const WebRtc_UWord8 mask5_4[8] = - { - 0xe0, 0x00, - 0x98, 0x00, - 0x50, 0x00, - 0x28, 0x00 - }; - - const WebRtc_UWord8 mask5_5[10] = - { - 0xc0, 0x00, - 0x30, 0x00, - 0x18, 0x00, - 0xa0, 0x00, - 0x48, 0x00 - }; - - const WebRtc_UWord8 mask6_1[2] = - { - 0xfc, 0x00 - }; - - const WebRtc_UWord8 mask6_2[4] = - { - 0xe8, 0x00, - 0x9c, 0x00 - }; - - const WebRtc_UWord8 mask6_3[6] = - { - 0x98, 0x00, - 0x4c, 0x00, - 0x34, 0x00 - }; - - const WebRtc_UWord8 mask6_4[8] = - { - 0xa8, 0x00, - 0x58, 0x00, - 0x64, 0x00, - 0x94, 0x00 - }; - - const WebRtc_UWord8 mask6_5[10] = - { - 0x58, 0x00, - 0x34, 0x00, - 0x2c, 0x00, - 0x90, 0x00, - 0xc4, 0x00 - }; - - const WebRtc_UWord8 mask6_6[12] = - { - 0x98, 0x00, - 0x64, 0x00, - 0x54, 0x00, - 0x1c, 0x00, - 0xa8, 0x00, - 0xe0, 0x00 - }; - - const WebRtc_UWord8 mask7_1[2] = - { - 0xfe, 0x00 - }; - - const WebRtc_UWord8 mask7_2[4] = - { - 0xcc, 0x00, - 0x3a, 0x00 - }; - - const WebRtc_UWord8 mask7_3[6] = - { - 0xcc, 0x00, - 0x96, 0x00, - 0x74, 0x00 - }; - - const WebRtc_UWord8 mask7_4[8] = - { - 0x8c, 0x00, - 0x38, 0x00, - 0x26, 0x00, - 0x4a, 0x00 - }; - - const WebRtc_UWord8 mask7_5[10] = - { - 0xac, 0x00, - 0x34, 0x00, - 0x1a, 0x00, - 0xc4, 0x00, - 0x62, 0x00 - }; - - const WebRtc_UWord8 mask7_6[12] = - { - 0x26, 0x00, - 0xb0, 0x00, - 0x1c, 0x00, - 0x52, 0x00, - 0x0a, 0x00, - 0xc8, 0x00 - }; - - const WebRtc_UWord8 mask7_7[14] = - { - 0x6a, 0x00, - 0x94, 0x00, - 0x1a, 0x00, - 0xc4, 0x00, - 0x0c, 0x00, - 0xc2, 0x00, - 0x34, 0x00 - }; - - const WebRtc_UWord8 mask8_1[2] = - { - 0xff, 0x00 - }; - - const WebRtc_UWord8 mask8_2[4] = - { - 0xce, 0x00, - 0xb9, 0x00 - }; - - const WebRtc_UWord8 mask8_3[6] = - { - 0x8d, 0x00, - 0x96, 0x00, - 0x78, 0x00 - }; - - const WebRtc_UWord8 mask8_4[8] = - { - 0x8e, 0x00, - 0x6a, 0x00, - 0x36, 0x00, - 0xd1, 0x00 - }; - - const WebRtc_UWord8 mask8_5[10] = - { - 0x55, 0x00, - 0xaa, 0x00, - 0xa5, 0x00, - 0x62, 0x00, - 0x3c, 0x00 - }; - - const WebRtc_UWord8 mask8_6[12] = - { - 0x51, 0x00, - 0xa2, 0x00, - 0x95, 0x00, - 0x4a, 0x00, - 0x30, 0x00, - 0x2c, 0x00 - }; - - const WebRtc_UWord8 mask8_7[14] = - { - 0x15, 0x00, - 0x8a, 0x00, - 0x25, 0x00, - 0x62, 0x00, - 0x58, 0x00, - 0x0e, 0x00, - 0x83, 0x00 - }; - - const WebRtc_UWord8 mask8_8[16] = - { - 0x25, 0x00, - 0x8a, 0x00, - 0x91, 0x00, - 0x68, 0x00, - 0x32, 0x00, - 0x43, 0x00, - 0xc4, 0x00, - 0x1c, 0x00 - }; - - const WebRtc_UWord8 mask9_1[2] = - { - 0xff, 0x80 - }; - - const WebRtc_UWord8 mask9_2[4] = - { - 0xce, 0x00, - 0x39, 0x80 - }; - - const WebRtc_UWord8 mask9_3[6] = - { - 0xac, 0x00, - 0x47, 0x00, - 0x32, 0x80 - }; - - const WebRtc_UWord8 mask9_4[8] = - { - 0xcc, 0x00, - 0x2b, 0x00, - 0x32, 0x80, - 0x25, 0x80 - }; - - const WebRtc_UWord8 mask9_5[10] = - { - 0x8c, 0x80, - 0x27, 0x00, - 0x32, 0x80, - 0x61, 0x80, - 0x5c, 0x00 - }; - - const WebRtc_UWord8 mask9_6[12] = - { - 0x84, 0x80, - 0x27, 0x00, - 0x51, 0x80, - 0x1a, 0x00, - 0x68, 0x00, - 0x89, 0x00 - }; - - const WebRtc_UWord8 mask9_7[14] = - { - 0x8c, 0x00, - 0x47, 0x00, - 0x81, 0x80, - 0x12, 0x80, - 0x58, 0x00, - 0x28, 0x80, - 0x34, 0x00 - }; - - const WebRtc_UWord8 mask9_8[16] = - { - 0x2c, 0x00, - 0x91, 0x00, - 0xc0, 0x80, - 0x06, 0x80, - 0xc8, 0x00, - 0x45, 0x00, - 0x30, 0x80, - 0xa2, 0x00 - }; - - const WebRtc_UWord8 mask9_9[18] = - { - 0x4c, 0x00, - 0x66, 0x00, - 0x91, 0x00, - 0x42, 0x80, - 0xa4, 0x00, - 0x13, 0x00, - 0x30, 0x80, - 0x88, 0x80, - 0x09, 0x00 - }; - - const WebRtc_UWord8* packetMask1[1] = - { - mask1_1 - }; - - const WebRtc_UWord8* packetMask2[2] = - { - mask2_1, - mask2_2 - }; - - const WebRtc_UWord8* packetMask3[3] = - { - mask3_1, - mask3_2, - mask3_3 - }; - - const WebRtc_UWord8* packetMask4[4] = - { - mask4_1, - mask4_2, - mask4_3, - mask4_4 - }; - - const WebRtc_UWord8* packetMask5[5] = - { - mask5_1, - mask5_2, - mask5_3, - mask5_4, - mask5_5 - }; - - const WebRtc_UWord8* packetMask6[6] = - { - mask6_1, - mask6_2, - mask6_3, - mask6_4, - mask6_5, - mask6_6 - }; - - const WebRtc_UWord8* packetMask7[7] = - { - mask7_1, - mask7_2, - mask7_3, - mask7_4, - mask7_5, - mask7_6, - mask7_7 - }; - - const WebRtc_UWord8* packetMask8[8] = - { - mask8_1, - mask8_2, - mask8_3, - mask8_4, - mask8_5, - mask8_6, - mask8_7, - mask8_8 - }; - - const WebRtc_UWord8* packetMask9[9] = - { - mask9_1, - mask9_2, - mask9_3, - mask9_4, - mask9_5, - mask9_6, - mask9_7, - mask9_8, - mask9_9 - }; - - const WebRtc_UWord8* packetMask10[10] = - { - mask10_1, - mask10_2, - mask10_3, - mask10_4, - mask10_5, - mask10_6, - mask10_7, - mask10_8, - mask10_9, - mask10_10 - }; - - const WebRtc_UWord8* packetMask11[11] = - { - mask11_1, - mask11_2, - mask11_3, - mask11_4, - mask11_5, - mask11_6, - mask11_7, - mask11_8, - mask11_9, - mask11_10, - mask11_11 - }; - - const WebRtc_UWord8* packetMask12[12] = - { - mask12_1, - mask12_2, - mask12_3, - mask12_4, - mask12_5, - mask12_6, - mask12_7, - mask12_8, - mask12_9, - mask12_10, - mask12_11, - mask12_12 - }; - - const WebRtc_UWord8* packetMask13[13] = - { - mask13_1, - mask13_2, - mask13_3, - mask13_4, - mask13_5, - mask13_6, - mask13_7, - mask13_8, - mask13_9, - mask13_10, - mask13_11, - mask13_12, - mask13_13 - }; - - const WebRtc_UWord8* packetMask14[14] = - { - mask14_1, - mask14_2, - mask14_3, - mask14_4, - mask14_5, - mask14_6, - mask14_7, - mask14_8, - mask14_9, - mask14_10, - mask14_11, - mask14_12, - mask14_13, - mask14_14 - }; - - const WebRtc_UWord8* packetMask15[15] = - { - mask15_1, - mask15_2, - mask15_3, - mask15_4, - mask15_5, - mask15_6, - mask15_7, - mask15_8, - mask15_9, - mask15_10, - mask15_11, - mask15_12, - mask15_13, - mask15_14, - mask15_15 - }; - - const WebRtc_UWord8* packetMask16[16] = - { - mask16_1, - mask16_2, - mask16_3, - mask16_4, - mask16_5, - mask16_6, - mask16_7, - mask16_8, - mask16_9, - mask16_10, - mask16_11, - mask16_12, - mask16_13, - mask16_14, - mask16_15, - mask16_16 - }; - - const WebRtc_UWord8* packetMask17[17] = - { - mask17_1, - mask17_2, - mask17_3, - mask17_4, - mask17_5, - mask17_6, - mask17_7, - mask17_8, - mask17_9, - mask17_10, - mask17_11, - mask17_12, - mask17_13, - mask17_14, - mask17_15, - mask17_16, - mask17_17 - }; - - const WebRtc_UWord8* packetMask18[18] = - { - mask18_1, - mask18_2, - mask18_3, - mask18_4, - mask18_5, - mask18_6, - mask18_7, - mask18_8, - mask18_9, - mask18_10, - mask18_11, - mask18_12, - mask18_13, - mask18_14, - mask18_15, - mask18_16, - mask18_17, - mask18_18 - }; - - const WebRtc_UWord8* packetMask19[19] = - { - mask19_1, - mask19_2, - mask19_3, - mask19_4, - mask19_5, - mask19_6, - mask19_7, - mask19_8, - mask19_9, - mask19_10, - mask19_11, - mask19_12, - mask19_13, - mask19_14, - mask19_15, - mask19_16, - mask19_17, - mask19_18, - mask19_19 - }; - - const WebRtc_UWord8* packetMask20[20] = - { - mask20_1, - mask20_2, - mask20_3, - mask20_4, - mask20_5, - mask20_6, - mask20_7, - mask20_8, - mask20_9, - mask20_10, - mask20_11, - mask20_12, - mask20_13, - mask20_14, - mask20_15, - mask20_16, - mask20_17, - mask20_18, - mask20_19, - mask20_20 - }; - - const WebRtc_UWord8* packetMask21[21] = - { - mask21_1, - mask21_2, - mask21_3, - mask21_4, - mask21_5, - mask21_6, - mask21_7, - mask21_8, - mask21_9, - mask21_10, - mask21_11, - mask21_12, - mask21_13, - mask21_14, - mask21_15, - mask21_16, - mask21_17, - mask21_18, - mask21_19, - mask21_20, - mask21_21 - }; - - const WebRtc_UWord8* packetMask22[22] = - { - mask22_1, - mask22_2, - mask22_3, - mask22_4, - mask22_5, - mask22_6, - mask22_7, - mask22_8, - mask22_9, - mask22_10, - mask22_11, - mask22_12, - mask22_13, - mask22_14, - mask22_15, - mask22_16, - mask22_17, - mask22_18, - mask22_19, - mask22_20, - mask22_21, - mask22_22 - }; - - const WebRtc_UWord8* packetMask23[23] = - { - mask23_1, - mask23_2, - mask23_3, - mask23_4, - mask23_5, - mask23_6, - mask23_7, - mask23_8, - mask23_9, - mask23_10, - mask23_11, - mask23_12, - mask23_13, - mask23_14, - mask23_15, - mask23_16, - mask23_17, - mask23_18, - mask23_19, - mask23_20, - mask23_21, - mask23_22, - mask23_23 - }; - - const WebRtc_UWord8* packetMask24[24] = - { - mask24_1, - mask24_2, - mask24_3, - mask24_4, - mask24_5, - mask24_6, - mask24_7, - mask24_8, - mask24_9, - mask24_10, - mask24_11, - mask24_12, - mask24_13, - mask24_14, - mask24_15, - mask24_16, - mask24_17, - mask24_18, - mask24_19, - mask24_20, - mask24_21, - mask24_22, - mask24_23, - mask24_24 - }; - - const WebRtc_UWord8* packetMask25[25] = - { - mask25_1, - mask25_2, - mask25_3, - mask25_4, - mask25_5, - mask25_6, - mask25_7, - mask25_8, - mask25_9, - mask25_10, - mask25_11, - mask25_12, - mask25_13, - mask25_14, - mask25_15, - mask25_16, - mask25_17, - mask25_18, - mask25_19, - mask25_20, - mask25_21, - mask25_22, - mask25_23, - mask25_24, - mask25_25 - }; - - const WebRtc_UWord8* packetMask26[26] = - { - mask26_1, - mask26_2, - mask26_3, - mask26_4, - mask26_5, - mask26_6, - mask26_7, - mask26_8, - mask26_9, - mask26_10, - mask26_11, - mask26_12, - mask26_13, - mask26_14, - mask26_15, - mask26_16, - mask26_17, - mask26_18, - mask26_19, - mask26_20, - mask26_21, - mask26_22, - mask26_23, - mask26_24, - mask26_25, - mask26_26 - }; - - const WebRtc_UWord8* packetMask27[27] = - { - mask27_1, - mask27_2, - mask27_3, - mask27_4, - mask27_5, - mask27_6, - mask27_7, - mask27_8, - mask27_9, - mask27_10, - mask27_11, - mask27_12, - mask27_13, - mask27_14, - mask27_15, - mask27_16, - mask27_17, - mask27_18, - mask27_19, - mask27_20, - mask27_21, - mask27_22, - mask27_23, - mask27_24, - mask27_25, - mask27_26, - mask27_27 - }; - - const WebRtc_UWord8* packetMask28[28] = - { - mask28_1, - mask28_2, - mask28_3, - mask28_4, - mask28_5, - mask28_6, - mask28_7, - mask28_8, - mask28_9, - mask28_10, - mask28_11, - mask28_12, - mask28_13, - mask28_14, - mask28_15, - mask28_16, - mask28_17, - mask28_18, - mask28_19, - mask28_20, - mask28_21, - mask28_22, - mask28_23, - mask28_24, - mask28_25, - mask28_26, - mask28_27, - mask28_28 - }; - - const WebRtc_UWord8* packetMask29[29] = - { - mask29_1, - mask29_2, - mask29_3, - mask29_4, - mask29_5, - mask29_6, - mask29_7, - mask29_8, - mask29_9, - mask29_10, - mask29_11, - mask29_12, - mask29_13, - mask29_14, - mask29_15, - mask29_16, - mask29_17, - mask29_18, - mask29_19, - mask29_20, - mask29_21, - mask29_22, - mask29_23, - mask29_24, - mask29_25, - mask29_26, - mask29_27, - mask29_28, - mask29_29 - }; - - const WebRtc_UWord8* packetMask30[30] = - { - mask30_1, - mask30_2, - mask30_3, - mask30_4, - mask30_5, - mask30_6, - mask30_7, - mask30_8, - mask30_9, - mask30_10, - mask30_11, - mask30_12, - mask30_13, - mask30_14, - mask30_15, - mask30_16, - mask30_17, - mask30_18, - mask30_19, - mask30_20, - mask30_21, - mask30_22, - mask30_23, - mask30_24, - mask30_25, - mask30_26, - mask30_27, - mask30_28, - mask30_29, - mask30_30 - }; - - const WebRtc_UWord8* packetMask31[31] = - { - mask31_1, - mask31_2, - mask31_3, - mask31_4, - mask31_5, - mask31_6, - mask31_7, - mask31_8, - mask31_9, - mask31_10, - mask31_11, - mask31_12, - mask31_13, - mask31_14, - mask31_15, - mask31_16, - mask31_17, - mask31_18, - mask31_19, - mask31_20, - mask31_21, - mask31_22, - mask31_23, - mask31_24, - mask31_25, - mask31_26, - mask31_27, - mask31_28, - mask31_29, - mask31_30, - mask31_31 - }; - - const WebRtc_UWord8* packetMask32[32] = - { - mask32_1, - mask32_2, - mask32_3, - mask32_4, - mask32_5, - mask32_6, - mask32_7, - mask32_8, - mask32_9, - mask32_10, - mask32_11, - mask32_12, - mask32_13, - mask32_14, - mask32_15, - mask32_16, - mask32_17, - mask32_18, - mask32_19, - mask32_20, - mask32_21, - mask32_22, - mask32_23, - mask32_24, - mask32_25, - mask32_26, - mask32_27, - mask32_28, - mask32_29, - mask32_30, - mask32_31, - mask32_32 - }; - - const WebRtc_UWord8* packetMask33[33] = - { - mask33_1, - mask33_2, - mask33_3, - mask33_4, - mask33_5, - mask33_6, - mask33_7, - mask33_8, - mask33_9, - mask33_10, - mask33_11, - mask33_12, - mask33_13, - mask33_14, - mask33_15, - mask33_16, - mask33_17, - mask33_18, - mask33_19, - mask33_20, - mask33_21, - mask33_22, - mask33_23, - mask33_24, - mask33_25, - mask33_26, - mask33_27, - mask33_28, - mask33_29, - mask33_30, - mask33_31, - mask33_32, - mask33_33 - }; - - const WebRtc_UWord8* packetMask34[34] = - { - mask34_1, - mask34_2, - mask34_3, - mask34_4, - mask34_5, - mask34_6, - mask34_7, - mask34_8, - mask34_9, - mask34_10, - mask34_11, - mask34_12, - mask34_13, - mask34_14, - mask34_15, - mask34_16, - mask34_17, - mask34_18, - mask34_19, - mask34_20, - mask34_21, - mask34_22, - mask34_23, - mask34_24, - mask34_25, - mask34_26, - mask34_27, - mask34_28, - mask34_29, - mask34_30, - mask34_31, - mask34_32, - mask34_33, - mask34_34 - }; - - const WebRtc_UWord8* packetMask35[35] = - { - mask35_1, - mask35_2, - mask35_3, - mask35_4, - mask35_5, - mask35_6, - mask35_7, - mask35_8, - mask35_9, - mask35_10, - mask35_11, - mask35_12, - mask35_13, - mask35_14, - mask35_15, - mask35_16, - mask35_17, - mask35_18, - mask35_19, - mask35_20, - mask35_21, - mask35_22, - mask35_23, - mask35_24, - mask35_25, - mask35_26, - mask35_27, - mask35_28, - mask35_29, - mask35_30, - mask35_31, - mask35_32, - mask35_33, - mask35_34, - mask35_35 - }; - - const WebRtc_UWord8* packetMask36[36] = - { - mask36_1, - mask36_2, - mask36_3, - mask36_4, - mask36_5, - mask36_6, - mask36_7, - mask36_8, - mask36_9, - mask36_10, - mask36_11, - mask36_12, - mask36_13, - mask36_14, - mask36_15, - mask36_16, - mask36_17, - mask36_18, - mask36_19, - mask36_20, - mask36_21, - mask36_22, - mask36_23, - mask36_24, - mask36_25, - mask36_26, - mask36_27, - mask36_28, - mask36_29, - mask36_30, - mask36_31, - mask36_32, - mask36_33, - mask36_34, - mask36_35, - mask36_36 - }; - - const WebRtc_UWord8* packetMask37[37] = - { - mask37_1, - mask37_2, - mask37_3, - mask37_4, - mask37_5, - mask37_6, - mask37_7, - mask37_8, - mask37_9, - mask37_10, - mask37_11, - mask37_12, - mask37_13, - mask37_14, - mask37_15, - mask37_16, - mask37_17, - mask37_18, - mask37_19, - mask37_20, - mask37_21, - mask37_22, - mask37_23, - mask37_24, - mask37_25, - mask37_26, - mask37_27, - mask37_28, - mask37_29, - mask37_30, - mask37_31, - mask37_32, - mask37_33, - mask37_34, - mask37_35, - mask37_36, - mask37_37 - }; - - const WebRtc_UWord8* packetMask38[38] = - { - mask38_1, - mask38_2, - mask38_3, - mask38_4, - mask38_5, - mask38_6, - mask38_7, - mask38_8, - mask38_9, - mask38_10, - mask38_11, - mask38_12, - mask38_13, - mask38_14, - mask38_15, - mask38_16, - mask38_17, - mask38_18, - mask38_19, - mask38_20, - mask38_21, - mask38_22, - mask38_23, - mask38_24, - mask38_25, - mask38_26, - mask38_27, - mask38_28, - mask38_29, - mask38_30, - mask38_31, - mask38_32, - mask38_33, - mask38_34, - mask38_35, - mask38_36, - mask38_37, - mask38_38 - }; - - const WebRtc_UWord8* packetMask39[39] = - { - mask39_1, - mask39_2, - mask39_3, - mask39_4, - mask39_5, - mask39_6, - mask39_7, - mask39_8, - mask39_9, - mask39_10, - mask39_11, - mask39_12, - mask39_13, - mask39_14, - mask39_15, - mask39_16, - mask39_17, - mask39_18, - mask39_19, - mask39_20, - mask39_21, - mask39_22, - mask39_23, - mask39_24, - mask39_25, - mask39_26, - mask39_27, - mask39_28, - mask39_29, - mask39_30, - mask39_31, - mask39_32, - mask39_33, - mask39_34, - mask39_35, - mask39_36, - mask39_37, - mask39_38, - mask39_39 - }; - - const WebRtc_UWord8* packetMask40[40] = - { - mask40_1, - mask40_2, - mask40_3, - mask40_4, - mask40_5, - mask40_6, - mask40_7, - mask40_8, - mask40_9, - mask40_10, - mask40_11, - mask40_12, - mask40_13, - mask40_14, - mask40_15, - mask40_16, - mask40_17, - mask40_18, - mask40_19, - mask40_20, - mask40_21, - mask40_22, - mask40_23, - mask40_24, - mask40_25, - mask40_26, - mask40_27, - mask40_28, - mask40_29, - mask40_30, - mask40_31, - mask40_32, - mask40_33, - mask40_34, - mask40_35, - mask40_36, - mask40_37, - mask40_38, - mask40_39, - mask40_40 - }; - - const WebRtc_UWord8* packetMask41[41] = - { - mask41_1, - mask41_2, - mask41_3, - mask41_4, - mask41_5, - mask41_6, - mask41_7, - mask41_8, - mask41_9, - mask41_10, - mask41_11, - mask41_12, - mask41_13, - mask41_14, - mask41_15, - mask41_16, - mask41_17, - mask41_18, - mask41_19, - mask41_20, - mask41_21, - mask41_22, - mask41_23, - mask41_24, - mask41_25, - mask41_26, - mask41_27, - mask41_28, - mask41_29, - mask41_30, - mask41_31, - mask41_32, - mask41_33, - mask41_34, - mask41_35, - mask41_36, - mask41_37, - mask41_38, - mask41_39, - mask41_40, - mask41_41 - }; - - const WebRtc_UWord8* packetMask42[42] = - { - mask42_1, - mask42_2, - mask42_3, - mask42_4, - mask42_5, - mask42_6, - mask42_7, - mask42_8, - mask42_9, - mask42_10, - mask42_11, - mask42_12, - mask42_13, - mask42_14, - mask42_15, - mask42_16, - mask42_17, - mask42_18, - mask42_19, - mask42_20, - mask42_21, - mask42_22, - mask42_23, - mask42_24, - mask42_25, - mask42_26, - mask42_27, - mask42_28, - mask42_29, - mask42_30, - mask42_31, - mask42_32, - mask42_33, - mask42_34, - mask42_35, - mask42_36, - mask42_37, - mask42_38, - mask42_39, - mask42_40, - mask42_41, - mask42_42 - }; - - const WebRtc_UWord8* packetMask43[43] = - { - mask43_1, - mask43_2, - mask43_3, - mask43_4, - mask43_5, - mask43_6, - mask43_7, - mask43_8, - mask43_9, - mask43_10, - mask43_11, - mask43_12, - mask43_13, - mask43_14, - mask43_15, - mask43_16, - mask43_17, - mask43_18, - mask43_19, - mask43_20, - mask43_21, - mask43_22, - mask43_23, - mask43_24, - mask43_25, - mask43_26, - mask43_27, - mask43_28, - mask43_29, - mask43_30, - mask43_31, - mask43_32, - mask43_33, - mask43_34, - mask43_35, - mask43_36, - mask43_37, - mask43_38, - mask43_39, - mask43_40, - mask43_41, - mask43_42, - mask43_43 - }; - - const WebRtc_UWord8* packetMask44[44] = - { - mask44_1, - mask44_2, - mask44_3, - mask44_4, - mask44_5, - mask44_6, - mask44_7, - mask44_8, - mask44_9, - mask44_10, - mask44_11, - mask44_12, - mask44_13, - mask44_14, - mask44_15, - mask44_16, - mask44_17, - mask44_18, - mask44_19, - mask44_20, - mask44_21, - mask44_22, - mask44_23, - mask44_24, - mask44_25, - mask44_26, - mask44_27, - mask44_28, - mask44_29, - mask44_30, - mask44_31, - mask44_32, - mask44_33, - mask44_34, - mask44_35, - mask44_36, - mask44_37, - mask44_38, - mask44_39, - mask44_40, - mask44_41, - mask44_42, - mask44_43, - mask44_44 - }; - - const WebRtc_UWord8* packetMask45[45] = - { - mask45_1, - mask45_2, - mask45_3, - mask45_4, - mask45_5, - mask45_6, - mask45_7, - mask45_8, - mask45_9, - mask45_10, - mask45_11, - mask45_12, - mask45_13, - mask45_14, - mask45_15, - mask45_16, - mask45_17, - mask45_18, - mask45_19, - mask45_20, - mask45_21, - mask45_22, - mask45_23, - mask45_24, - mask45_25, - mask45_26, - mask45_27, - mask45_28, - mask45_29, - mask45_30, - mask45_31, - mask45_32, - mask45_33, - mask45_34, - mask45_35, - mask45_36, - mask45_37, - mask45_38, - mask45_39, - mask45_40, - mask45_41, - mask45_42, - mask45_43, - mask45_44, - mask45_45 - }; - - const WebRtc_UWord8* packetMask46[46] = - { - mask46_1, - mask46_2, - mask46_3, - mask46_4, - mask46_5, - mask46_6, - mask46_7, - mask46_8, - mask46_9, - mask46_10, - mask46_11, - mask46_12, - mask46_13, - mask46_14, - mask46_15, - mask46_16, - mask46_17, - mask46_18, - mask46_19, - mask46_20, - mask46_21, - mask46_22, - mask46_23, - mask46_24, - mask46_25, - mask46_26, - mask46_27, - mask46_28, - mask46_29, - mask46_30, - mask46_31, - mask46_32, - mask46_33, - mask46_34, - mask46_35, - mask46_36, - mask46_37, - mask46_38, - mask46_39, - mask46_40, - mask46_41, - mask46_42, - mask46_43, - mask46_44, - mask46_45, - mask46_46 - }; - - const WebRtc_UWord8* packetMask47[47] = - { - mask47_1, - mask47_2, - mask47_3, - mask47_4, - mask47_5, - mask47_6, - mask47_7, - mask47_8, - mask47_9, - mask47_10, - mask47_11, - mask47_12, - mask47_13, - mask47_14, - mask47_15, - mask47_16, - mask47_17, - mask47_18, - mask47_19, - mask47_20, - mask47_21, - mask47_22, - mask47_23, - mask47_24, - mask47_25, - mask47_26, - mask47_27, - mask47_28, - mask47_29, - mask47_30, - mask47_31, - mask47_32, - mask47_33, - mask47_34, - mask47_35, - mask47_36, - mask47_37, - mask47_38, - mask47_39, - mask47_40, - mask47_41, - mask47_42, - mask47_43, - mask47_44, - mask47_45, - mask47_46, - mask47_47 - }; - - const WebRtc_UWord8* packetMask48[48] = - { - mask48_1, - mask48_2, - mask48_3, - mask48_4, - mask48_5, - mask48_6, - mask48_7, - mask48_8, - mask48_9, - mask48_10, - mask48_11, - mask48_12, - mask48_13, - mask48_14, - mask48_15, - mask48_16, - mask48_17, - mask48_18, - mask48_19, - mask48_20, - mask48_21, - mask48_22, - mask48_23, - mask48_24, - mask48_25, - mask48_26, - mask48_27, - mask48_28, - mask48_29, - mask48_30, - mask48_31, - mask48_32, - mask48_33, - mask48_34, - mask48_35, - mask48_36, - mask48_37, - mask48_38, - mask48_39, - mask48_40, - mask48_41, - mask48_42, - mask48_43, - mask48_44, - mask48_45, - mask48_46, - mask48_47, - mask48_48 - }; - - const WebRtc_UWord8** packetMaskTbl[48] = - { - packetMask1, - packetMask2, - packetMask3, - packetMask4, - packetMask5, - packetMask6, - packetMask7, - packetMask8, - packetMask9, - packetMask10, - packetMask11, - packetMask12, - packetMask13, - packetMask14, - packetMask15, - packetMask16, - packetMask17, - packetMask18, - packetMask19, - packetMask20, - packetMask21, - packetMask22, - packetMask23, - packetMask24, - packetMask25, - packetMask26, - packetMask27, - packetMask28, - packetMask29, - packetMask30, - packetMask31, - packetMask32, - packetMask33, - packetMask34, - packetMask35, - packetMask36, - packetMask37, - packetMask38, - packetMask39, - packetMask40, - packetMask41, - packetMask42, - packetMask43, - packetMask44, - packetMask45, - packetMask46, - packetMask47, - packetMask48 - }; -} // Anonymous namespace -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_PRIVATE_TABLES_H_ diff --git a/modules/rtp_rtcp/source/forward_error_correction.cc b/modules/rtp_rtcp/source/forward_error_correction.cc deleted file mode 100644 index 4cef2eda9..000000000 --- a/modules/rtp_rtcp/source/forward_error_correction.cc +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "forward_error_correction.h" -#include "fec_private_tables.h" -#include "rtp_utility.h" - -#include "trace.h" -#include -#include - -#include "forward_error_correction_internal.h" - -namespace webrtc { - -// Minimum RTP header size in bytes. -const WebRtc_UWord8 kRtpHeaderSize = 12; - -// FEC header size in bytes. -const WebRtc_UWord8 kFecHeaderSize = 10; - -// ULP header size in bytes (L bit is set). -const WebRtc_UWord8 kUlpHeaderSizeLBitSet = (2 + kMaskSizeLBitSet); - -// ULP header size in bytes (L bit is cleared). -const WebRtc_UWord8 kUlpHeaderSizeLBitClear = (2 + kMaskSizeLBitClear); - -// Transport header size in bytes. Assume UDP/IPv4 as a reasonable minimum. -const WebRtc_UWord8 kTransportOverhead = 28; - -// -// Used for internal storage of FEC packets in a list. -// -struct FecPacket -{ - ListWrapper protectedPktList; /**> List containing #ProtectedPacket types.*/ - WebRtc_UWord16 seqNum; /**> Sequence number. */ - WebRtc_UWord32 ssrc; /**> SSRC of the current frame. */ - ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */ -}; - -// -// Used to link media packets to their protecting FEC packets. -// -struct ProtectedPacket -{ - WebRtc_UWord16 seqNum; /**> Sequence number. */ - ForwardErrorCorrection::Packet* pkt; /**> Pointer to the packet storage. */ -}; - -ForwardErrorCorrection::ForwardErrorCorrection(const WebRtc_Word32 id) : - _id(id), - _generatedFecPackets(NULL), - _fecPacketList(), - _seqNumBase(0), - _lastMediaPacketReceived(false), - _fecPacketReceived(false) -{ -} - -ForwardErrorCorrection::~ForwardErrorCorrection() -{ - if (_generatedFecPackets != NULL) - { - delete [] _generatedFecPackets; - } -} - -// Input packet -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | RTP Header (12 octets) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | RTP Payload | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -// Output packet -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Header (10 octets) | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Level 0 Header | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | FEC Level 0 Payload | -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -WebRtc_Word32 -ForwardErrorCorrection::GenerateFEC(const ListWrapper& mediaPacketList, - WebRtc_UWord8 protectionFactor, - WebRtc_UWord32 numImportantPackets, - ListWrapper& fecPacketList) -{ - if (mediaPacketList.Empty()) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet list is empty", __FUNCTION__); - return -1; - } - - if (!fecPacketList.Empty()) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s FEC packet list is not empty", __FUNCTION__); - return -1; - } - - const WebRtc_UWord16 numMediaPackets = mediaPacketList.GetSize(); - const WebRtc_UWord8 lBit = numMediaPackets > 16 ? 1 : 0; - const WebRtc_UWord16 numMaskBytes = - (lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear; - const WebRtc_UWord16 ulpHeaderSize = - (lBit == 1)? kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; - const WebRtc_UWord16 fecRtpOffset = - kFecHeaderSize + ulpHeaderSize - kRtpHeaderSize; - - if (numMediaPackets > kMaxMediaPackets) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s can only protect %d media packets per frame; %d requested", - __FUNCTION__, kMaxMediaPackets, numMediaPackets); - return -1; - } - - // Error checking on the number of important packets. - // Can't have more important packets than media packets. - if (numImportantPackets > numMediaPackets) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Number of Important packet greater than number of Media Packets %d %d", - numImportantPackets, numMediaPackets); - return -1; - } - if (numImportantPackets < 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Number of Important packets less than zero %d %d", - numImportantPackets, numMediaPackets); - return -1; - } - - // Do some error checking on the media packets. - Packet* mediaPacket; - ListItem* mediaListItem = mediaPacketList.First(); - while (mediaListItem != NULL) - { - mediaPacket = static_cast(mediaListItem->GetItem()); - - if (mediaPacket->length < kRtpHeaderSize) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet (%d bytes) is smaller than RTP header", - __FUNCTION__, mediaPacket->length); - return -1; - } - - // Ensure our FEC packets will fit in a typical MTU. - if (mediaPacket->length + PacketOverhead() + kTransportOverhead > - IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s media packet (%d bytes) with overhead is larger than MTU (%d bytes)", - __FUNCTION__, mediaPacket->length, IP_PACKET_SIZE); - return -1; - } - - mediaListItem = mediaPacketList.Next(mediaListItem); - } - - // Result in Q0 with an unsigned round. - WebRtc_UWord32 numFecPackets = (numMediaPackets * protectionFactor + - (1 << 7)) >> 8; - if (numFecPackets == 0) - { - return 0; - } - assert(numFecPackets <= numMediaPackets); - - // -- Initialize FEC list -- - if (_generatedFecPackets != NULL) - { - delete [] _generatedFecPackets; - _generatedFecPackets = NULL; - } - - _generatedFecPackets = new Packet[numFecPackets]; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - memset(_generatedFecPackets[i].data, 0, IP_PACKET_SIZE); - _generatedFecPackets[i].length = 0; // Use this as a marker for untouched - // packets. - fecPacketList.PushBack(&_generatedFecPackets[i]); - } - - // -- Generate packet masks -- - WebRtc_UWord8* packetMask = new WebRtc_UWord8[numFecPackets * numMaskBytes]; - memset(packetMask, 0, numFecPackets * numMaskBytes); - internal::GeneratePacketMasks(numMediaPackets, numFecPackets, - numImportantPackets, packetMask); - - // -- Generate FEC bit strings -- - WebRtc_UWord8 mediaPayloadLength[2]; - mediaListItem = mediaPacketList.First(); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - mediaListItem = mediaPacketList.First(); - WebRtc_UWord32 pktMaskIdx = i * numMaskBytes; - WebRtc_UWord32 mediaPktIdx = 0; - WebRtc_UWord16 fecPacketLength = 0; - while (mediaListItem != NULL) - { - // Each FEC packet has a multiple byte mask. - if (packetMask[pktMaskIdx] & (1 << (7 - mediaPktIdx))) - { - mediaPacket = static_cast(mediaListItem->GetItem()); - - // Assign network-ordered media payload length. - ModuleRTPUtility::AssignUWord16ToBuffer(mediaPayloadLength, - mediaPacket->length - kRtpHeaderSize); - fecPacketLength = mediaPacket->length + fecRtpOffset; - // On the first protected packet, we don't need to XOR. - if (_generatedFecPackets[i].length == 0) - { - // Copy the first 2 bytes of the RTP header. - memcpy(_generatedFecPackets[i].data, mediaPacket->data, 2); - // Copy the 5th to 8th bytes of the RTP header. - memcpy(&_generatedFecPackets[i].data[4], &mediaPacket->data[4], 4); - // Copy network-ordered payload size. - memcpy(&_generatedFecPackets[i].data[8], mediaPayloadLength, 2); - - // Copy RTP payload, leaving room for the ULP header. - memcpy(&_generatedFecPackets[i].data[kFecHeaderSize + ulpHeaderSize], - &mediaPacket->data[kRtpHeaderSize], - mediaPacket->length - kRtpHeaderSize); - } - else - { - // XOR with the first 2 bytes of the RTP header. - _generatedFecPackets[i].data[0] ^= mediaPacket->data[0]; - _generatedFecPackets[i].data[1] ^= mediaPacket->data[1]; - - // XOR with the 5th to 8th bytes of the RTP header. - for (WebRtc_UWord32 j = 4; j < 8; j++) - { - _generatedFecPackets[i].data[j] ^= mediaPacket->data[j]; - } - - // XOR with the network-ordered payload size. - _generatedFecPackets[i].data[8] ^= mediaPayloadLength[0]; - _generatedFecPackets[i].data[9] ^= mediaPayloadLength[1]; - - // XOR with RTP payload, leaving room for the ULP header. - for (WebRtc_Word32 j = kFecHeaderSize + ulpHeaderSize; - j < fecPacketLength; j++) - { - _generatedFecPackets[i].data[j] ^= - mediaPacket->data[j - fecRtpOffset]; - } - } - - if (fecPacketLength > _generatedFecPackets[i].length) - { - _generatedFecPackets[i].length = fecPacketLength; - } - } - - mediaListItem = mediaPacketList.Next(mediaListItem); - mediaPktIdx++; - if (mediaPktIdx == 8) - { - // Switch to the next mask byte. - mediaPktIdx = 0; - pktMaskIdx++; - } - } - - if (_generatedFecPackets[i].length == 0) - { - //Note: This shouldn't happen: means packet mask is wrong or poorly designed - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "Packet mask has row of zeros %d %d %d ", - numMediaPackets, numImportantPackets, numFecPackets); - delete packetMask; - return -1; - - } - } - - // -- Generate FEC and ULP headers -- - // - // FEC Header, 10 bytes - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |E|L|P|X| CC |M| PT recovery | SN base | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | TS recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | length recovery | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - // ULP Header, 4 bytes (for L = 0) - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | Protection Length | mask | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | mask cont. (present only when L = 1) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - mediaListItem = mediaPacketList.First(); - mediaPacket = static_cast(mediaListItem->GetItem()); - assert(mediaPacket != NULL); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - // -- FEC header -- - _generatedFecPackets[i].data[0] &= 0x7f; // Set E to zero. - if (lBit == 0) - { - _generatedFecPackets[i].data[0] &= 0xbf; // Clear the L bit. - } - else - { - _generatedFecPackets[i].data[0] |= 0x40; // Set the L bit. - } - - // Two byte sequence number from first RTP packet to SN base. - // We use the same sequence number base for every FEC packet, - // but that's not required in general. - memcpy(&_generatedFecPackets[i].data[2], &mediaPacket->data[2], 2); - - // -- ULP header -- - // Copy the payload size to the protection length field. - // (We protect the entire packet.) - ModuleRTPUtility::AssignUWord16ToBuffer(&_generatedFecPackets[i].data[10], - _generatedFecPackets[i].length - kFecHeaderSize - ulpHeaderSize); - - // Copy the packet mask. - memcpy(&_generatedFecPackets[i].data[12], &packetMask[i * numMaskBytes], - numMaskBytes); - } - delete packetMask; - return 0; -} - -WebRtc_Word32 -ForwardErrorCorrection::DecodeFEC(ListWrapper& receivedPacketList, - ListWrapper& recoveredPacketList, - const WebRtc_UWord16 lastFECSeqNum, - bool& frameComplete) -{ - // TODO: can we check for multiple ULP headers, and return an error? - - // Allow an empty received packet list when complete is true as a teardown indicator. - if (receivedPacketList.Empty() && !frameComplete) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, - "%s received packet list is empty, but we're not tearing down here", - __FUNCTION__); - return -1; - } - - ListItem* packetListItem = NULL; - ListItem* protectedPacketListItem = NULL; - FecPacket* fecPacket = NULL; - RecoveredPacket* recPacket = NULL; - if (frameComplete) - { - // We have a new frame. - _seqNumBase = 0; - _lastMediaPacketReceived = false; - _fecPacketReceived = false; - - // Free the memory for any existing recovered packets, if the user hasn't. - if (!recoveredPacketList.Empty()) - { - packetListItem = recoveredPacketList.First(); - while (packetListItem != NULL) - { - recPacket = static_cast(packetListItem->GetItem()); - delete recPacket->pkt; - delete recPacket; - recPacket = NULL; - packetListItem = recoveredPacketList.Next(packetListItem); - recoveredPacketList.PopFront(); - } - assert(recoveredPacketList.Empty()); - } - - // Free the FEC packet list. - packetListItem = _fecPacketList.First(); - while (packetListItem != NULL) - { - fecPacket = static_cast(packetListItem->GetItem()); - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - delete static_cast(protectedPacketListItem->GetItem()); - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - fecPacket->protectedPktList.PopFront(); - } - assert(fecPacket->protectedPktList.Empty()); - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - packetListItem = _fecPacketList.Next(packetListItem); - _fecPacketList.PopFront(); - } - assert(_fecPacketList.Empty()); - } - - // -- Insert packets into FEC or recovered list -- - ReceivedPacket* rxPacket = NULL; - RecoveredPacket* recPacketToInsert = NULL; - ProtectedPacket* protectedPacket = NULL; - ListItem* recPacketListItem = NULL; - ListItem* fecPacketListItem = NULL; - packetListItem = receivedPacketList.First(); - while (packetListItem != NULL) - { - rxPacket = static_cast(packetListItem->GetItem()); - - if (!rxPacket->isFec) // Media packet - { - // Insert into recovered packet list. This is essentially an insertion - // sort, with duplicate removal. We search from the back of the list as we - // expect packets to arrive in order. - if (rxPacket->lastMediaPktInFrame) - { - if (_lastMediaPacketReceived) - { - // We already received the last packet. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "%s last media packet marked more than once per frame", - __FUNCTION__); - } - _lastMediaPacketReceived = true; - } - - bool duplicatePacket = false; - recPacketListItem = recoveredPacketList.Last(); - ListItem* nextItem = NULL; - while (recPacketListItem != NULL) - { - recPacket = static_cast(recPacketListItem->GetItem()); - if (rxPacket->seqNum == recPacket->seqNum) - { - // Duplicate packet, no need to add to list. - duplicatePacket = true; - break; - } - else if ((rxPacket->seqNum < recPacket->seqNum || - rxPacket->seqNum > recPacket->seqNum + 48) && // Wrap guard. - rxPacket->seqNum > recPacket->seqNum - 48) // - { - nextItem = recPacketListItem; - recPacketListItem = recoveredPacketList.Previous(recPacketListItem); - } - else - { - // Found the correct position. - break; - } - } - - if (duplicatePacket) - { - // Delete duplicate media packet data. - delete rxPacket->pkt; - }else - { - recPacketToInsert = new RecoveredPacket; - recPacketToInsert->wasRecovered = false; - recPacketToInsert->seqNum = rxPacket->seqNum; - recPacketToInsert->pkt = rxPacket->pkt; - recPacketToInsert->pkt->length = rxPacket->pkt->length; - - if (nextItem == NULL) - { - // Insert at the back. - recoveredPacketList.PushBack(recPacketToInsert); - }else - { - // Insert in sorted position. - recoveredPacketList.InsertBefore(nextItem, new ListItem(recPacketToInsert)); - } - } - }else // FEC packet - { - _fecPacketReceived = true; - WebRtc_UWord8 packetMask; - - // Check for duplicate. - bool duplicatePacket = false; - fecPacketListItem = _fecPacketList.First(); - while (fecPacketListItem != NULL) - { - fecPacket = static_cast(fecPacketListItem->GetItem()); - if (rxPacket->seqNum == fecPacket->seqNum) - { - duplicatePacket = true; - break; - } - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - if (duplicatePacket) - { - // Delete duplicate FEC packet data. - delete rxPacket->pkt; - rxPacket->pkt = NULL; - - }else - { - fecPacket = new FecPacket; - fecPacket->pkt = rxPacket->pkt; - fecPacket->seqNum = rxPacket->seqNum; - fecPacket->ssrc = rxPacket->ssrc; - - // We store this for determining frame completion later. - _seqNumBase = ModuleRTPUtility::BufferToUWord16(&fecPacket->pkt->data[2]); - const WebRtc_UWord16 maskSizeBytes = (fecPacket->pkt->data[0] & 0x40) ? - kMaskSizeLBitSet : kMaskSizeLBitClear; // L bit set? - - for (WebRtc_UWord16 byteIdx = 0; byteIdx < maskSizeBytes; byteIdx++) - { - packetMask = fecPacket->pkt->data[12 + byteIdx]; - for (WebRtc_UWord16 bitIdx = 0; bitIdx < 8; bitIdx++) - { - if (packetMask & (1 << (7 - bitIdx))) - { - protectedPacket = new ProtectedPacket; - fecPacket->protectedPktList.PushBack(protectedPacket); - // This wraps naturally with the sequence number. - protectedPacket->seqNum = static_cast - (_seqNumBase + (byteIdx << 3) + bitIdx); - protectedPacket->pkt = NULL; - } - } - } - - if (fecPacket->protectedPktList.Empty()) - { - // All-zero packet mask; we can discard this FEC packet. - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - } - else - { - _fecPacketList.PushBack(fecPacket); - } - } - } - - packetListItem = receivedPacketList.Next(packetListItem); - - // Delete the received packet "wrapper", but not the packet data. - delete rxPacket; - rxPacket = NULL; - receivedPacketList.PopFront(); - } - assert(receivedPacketList.Empty()); - - // -- Attempt to recover packets -- - WebRtc_UWord16 protectedPacketsFound; - WebRtc_UWord8 mediaPayloadLength[2]; - WebRtc_UWord8 protectionLength[2]; - fecPacketListItem = _fecPacketList.First(); - ListItem* fecPacketListItemToDiscard = NULL; - while (fecPacketListItem != NULL) - { - // Search for each FEC packet's protected media packets. - fecPacket = static_cast(fecPacketListItem->GetItem()); - protectedPacketListItem = fecPacket->protectedPktList.First(); - recPacketListItem = recoveredPacketList.First(); - protectedPacketsFound = 0; - fecPacketListItemToDiscard = NULL; - while (protectedPacketListItem != NULL) - { - protectedPacket = - static_cast(protectedPacketListItem->GetItem()); - - if (protectedPacket->pkt != NULL) - { - // We already have the required packet. - protectedPacketsFound++; - } - else - { - // Search for the required packet. - while (recPacketListItem != NULL) - { - recPacket = - static_cast(recPacketListItem->GetItem()); - recPacketListItem = recoveredPacketList.Next(recPacketListItem); - if (protectedPacket->seqNum == recPacket->seqNum) - { - protectedPacket->pkt = recPacket->pkt; - protectedPacketsFound++; - break; - } - } - - // Since the recovered packet list is already sorted, we don't need to - // restart at the beginning of the list unless the previous protected - // packet wasn't found. - if (protectedPacket->pkt == NULL) - { - recPacketListItem = recoveredPacketList.First(); - } - } - - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - } - - if (protectedPacketsFound == fecPacket->protectedPktList.GetSize() - 1) - { - // Recovery possible. - WebRtc_UWord8 lengthRecovery[2]; - const WebRtc_UWord16 ulpHeaderSize = fecPacket->pkt->data[0] & 0x40 ? - kUlpHeaderSizeLBitSet : kUlpHeaderSizeLBitClear; // L bit set? - - RecoveredPacket* recPacketToInsert = new RecoveredPacket; - recPacketToInsert->wasRecovered = true; - recPacketToInsert->pkt = new Packet; - memset(recPacketToInsert->pkt->data, 0, IP_PACKET_SIZE); - - // Copy the protection length from the ULP header. - memcpy(&protectionLength, &fecPacket->pkt->data[10], 2); - - // Copy the first 2 bytes of the FEC header. - memcpy(recPacketToInsert->pkt->data, fecPacket->pkt->data, 2); - - // Copy the 5th to 8th bytes of the FEC header. - memcpy(&recPacketToInsert->pkt->data[4], &fecPacket->pkt->data[4], 4); - - // Set the SSRC field. - ModuleRTPUtility::AssignUWord32ToBuffer(&recPacketToInsert->pkt->data[8], - fecPacket->ssrc); - - // Copy the length recovery field. - memcpy(&lengthRecovery, &fecPacket->pkt->data[8], 2); - - // Copy FEC payload, skipping the ULP header. - memcpy(&recPacketToInsert->pkt->data[kRtpHeaderSize], - &fecPacket->pkt->data[kFecHeaderSize + ulpHeaderSize], - ModuleRTPUtility::BufferToUWord16(protectionLength)); - - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - protectedPacket = - static_cast(protectedPacketListItem->GetItem()); - - if (protectedPacket->pkt == NULL) - { - // This is the packet we're recovering. - recPacketToInsert->seqNum = protectedPacket->seqNum; - } - else - { - // XOR with the first 2 bytes of the RTP header. - for (WebRtc_UWord32 i = 0; i < 2; i++) - { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - - // XOR with the 5th to 8th bytes of the RTP header. - for (WebRtc_UWord32 i = 4; i < 8; i++) - { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - - // XOR with the network-ordered payload size. - ModuleRTPUtility::AssignUWord16ToBuffer(mediaPayloadLength, - protectedPacket->pkt->length - kRtpHeaderSize); - lengthRecovery[0] ^= mediaPayloadLength[0]; - lengthRecovery[1] ^= mediaPayloadLength[1]; - - // XOR with RTP payload. - // TODO: Are we doing more XORs than required here? - for (WebRtc_Word32 i = kRtpHeaderSize; i < protectedPacket->pkt->length; - i++) - { - recPacketToInsert->pkt->data[i] ^= protectedPacket->pkt->data[i]; - } - } - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - } - - // Set the RTP version to 2. - recPacketToInsert->pkt->data[0] |= 0x80; // Set the 1st bit. - recPacketToInsert->pkt->data[0] &= 0xbf; // Clear the 2nd bit. - - // Assume a recovered marker bit indicates the last media packet in a frame. - if (recPacketToInsert->pkt->data[1] & 0x80) - { - if (_lastMediaPacketReceived) - { - // Multiple marker bits are illegal. - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "%s recovered media packet contains a marker bit, but the last " - "media packet in this frame has already been marked", - __FUNCTION__); - } - _lastMediaPacketReceived = true; - } - - // Set the SN field. - ModuleRTPUtility::AssignUWord16ToBuffer(&recPacketToInsert->pkt->data[2], - recPacketToInsert->seqNum); - - // Recover the packet length. - recPacketToInsert->pkt->length = - ModuleRTPUtility::BufferToUWord16(lengthRecovery) + kRtpHeaderSize; - - // Insert into recovered list in correct position. - recPacketListItem = recoveredPacketList.Last(); - ListItem* nextItem = NULL; - while (recPacketListItem != NULL) - { - recPacket = static_cast(recPacketListItem->GetItem()); - if ((recPacketToInsert->seqNum < recPacket->seqNum || - recPacketToInsert->seqNum > recPacket->seqNum + 48) && // Wrap guard. - recPacketToInsert->seqNum > recPacket->seqNum - 48) // - { - nextItem = recPacketListItem; - recPacketListItem = recoveredPacketList.Previous(recPacketListItem); - } - else - { - // Found the correct position. - break; - } - } - - if (nextItem == NULL) - { - // Insert at the back. - recoveredPacketList.PushBack(recPacketToInsert); - } - else - { - // Insert in sorted position. - recoveredPacketList.InsertBefore(nextItem, - new ListItem(recPacketToInsert)); - } - - protectedPacketsFound++; - assert(protectedPacketsFound == fecPacket->protectedPktList.GetSize()); - fecPacketListItemToDiscard = fecPacketListItem; - } - - if (fecPacketListItemToDiscard != NULL) - { - // A packet has been recovered. We need to check the FEC list again, as this - // may allow additional packets to be recovered. - fecPacketListItem = _fecPacketList.First(); - if (fecPacketListItem == fecPacketListItemToDiscard) - { - // If we're deleting the first item, we need to get the next first. - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - } - else - { - // Store this in case a discard is required. - fecPacketListItemToDiscard = fecPacketListItem; - fecPacketListItem = _fecPacketList.Next(fecPacketListItem); - } - - if (protectedPacketsFound == fecPacket->protectedPktList.GetSize()) - { - // Either all protected packets arrived or have been recovered. - // We can discard this FEC packet. - protectedPacketListItem = fecPacket->protectedPktList.First(); - while (protectedPacketListItem != NULL) - { - delete static_cast(protectedPacketListItem->GetItem()); - protectedPacketListItem = - fecPacket->protectedPktList.Next(protectedPacketListItem); - fecPacket->protectedPktList.PopFront(); - } - assert(fecPacket->protectedPktList.Empty()); - delete fecPacket->pkt; - delete fecPacket; - fecPacket = NULL; - assert(fecPacketListItemToDiscard != NULL); - _fecPacketList.Erase(fecPacketListItemToDiscard); - } - } - - // Check if we have a complete frame. - frameComplete = false; - - if (_lastMediaPacketReceived) - { - if(!_fecPacketReceived) - { - // best estimate we have if we have not received a FEC packet - _seqNumBase = lastFECSeqNum + 1; - } - // With this we assume the user is attempting to decode a FEC stream. - WebRtc_UWord16 seqNumIdx = 0; - frameComplete = true; - recPacketListItem = recoveredPacketList.First(); - while (recPacketListItem != NULL && frameComplete == true) - { - recPacket = static_cast(recPacketListItem->GetItem()); - recPacketListItem = recoveredPacketList.Next(recPacketListItem); - if (recPacket->seqNum != - static_cast(_seqNumBase + seqNumIdx)) // Wraps naturally. - { - frameComplete = false; - } - seqNumIdx++; - } - } - - return 0; -} - -WebRtc_UWord16 -ForwardErrorCorrection::PacketOverhead() -{ - return kFecHeaderSize + kUlpHeaderSizeLBitSet; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/forward_error_correction.h b/modules/rtp_rtcp/source/forward_error_correction.h deleted file mode 100644 index 275643b4a..000000000 --- a/modules/rtp_rtcp/source/forward_error_correction.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ - -#include "typedefs.h" -#include "rtp_rtcp_defines.h" - -#include "list_wrapper.h" - -namespace webrtc { -/** - * Performs codec-independent forward error correction. - */ -class ForwardErrorCorrection -{ -public: - - // Maximum number of media packets we can protect - static const int kMaxMediaPackets = 48; - - /** - * The ListWrapper parameters of #GenerateFEC() should reference structs of this type. - */ - struct Packet - { - WebRtc_UWord16 length; /**> Length of packet in bytes. */ - WebRtc_UWord8 data[IP_PACKET_SIZE]; /**> Packet data. */ - }; - - /** - * The received list parameter of #DecodeFEC() must reference structs of this type. - * The lastMediaPktInFrame is not required to be used for correct recovery, but will - * reduce delay by allowing #DecodeFEC() to pre-emptively determine frame completion. - * If set, we assume a FEC stream, and the following assumptions must hold:\n - * - * 1. The media packets in a frame have contiguous sequence numbers, i.e. the frame's - * FEC packets have sequence numbers either lower than the first media packet or - * higher than the last media packet.\n - * 2. All FEC packets have a sequence number base equal to the first media packet in - * the corresponding frame.\n - * - * The ssrc member is needed to ensure we can restore the SSRC field of recovered - * packets. In most situations this could be retrieved from other media packets, but - * in the case of an FEC packet protecting a single missing media packet, we have no - * other means of obtaining it. - */ - struct ReceivedPacket - { - WebRtc_UWord16 seqNum; /**> Sequence number of packet. */ - WebRtc_UWord32 ssrc; /**> SSRC of the current frame. Must be set for FEC - packets, but not required for media packets. */ - bool isFec; /**> Set to true if this is an FEC packet and false - otherwise. */ - bool lastMediaPktInFrame; /**> Set to true to mark the last media packet in the - frame and false otherwise. */ - Packet* pkt; /**> Pointer to the packet storage. */ - }; - - /** - * The recovered list parameter of #DecodeFEC() will reference structs of this type. - */ - struct RecoveredPacket - { - bool wasRecovered; /**> Will be true if this packet was recovered by the FEC. - Otherwise it was a media packet passed in through the - received packet list. */ - WebRtc_UWord16 seqNum; /**> Sequence number of the packet. This is mostly for - implementation convenience but could be utilized by the - user if so desired. */ - Packet* pkt; /**> Pointer to the packet storage. */ - }; - - /** - * Constructor. - * - * \param[in] id Module ID - */ - ForwardErrorCorrection(const WebRtc_Word32 id); - - /** - * Destructor. Before freeing an instance of the class, #DecodeFEC() must be called - * in a particular fashion to free outstanding memory. Refer to #DecodeFEC(). - */ - virtual ~ForwardErrorCorrection(); - - /** - * Generates a list of FEC packets from supplied media packets. - * - * \param[in] mediaPacketList List of media packets to protect, of type #Packet. - * All packets must belong to the same frame and the - * list must not be empty. - * \param[in] protectionFactor FEC protection overhead in the [0, 255] domain. To - * obtain 100% overhead, or an equal number of FEC - * packets as media packets, use 255. - * \param[in] numImportantPackets The number of "important" packets in the frame. - * These packets may receive greater protection than - * the remaining packets. The important packets must - * be located at the start of the media packet list. - * For codecs with data partitioning, the important - * packets may correspond to first partition packets. - * \param[out] fecPacketList List of FEC packets, of type #Packet. Must be empty - * on entry. The memory available through the list - * will be valid until the next call to GenerateFEC(). - * - * \return 0 on success, -1 on failure. - */ - WebRtc_Word32 GenerateFEC(const ListWrapper& mediaPacketList, - WebRtc_UWord8 protectionFactor, - WebRtc_UWord32 numImportantPackets, - ListWrapper& fecPacketList); - - /** - * Decodes a list of media and FEC packets. It will parse the input received packet - * list, storing FEC packets internally and inserting media packets to the output - * recovered packet list. The recovered list will be sorted by ascending sequence - * number and have duplicates removed. The function should be called as new packets - * arrive, with the recovered list being progressively assembled with each call. - * The received packet list will be empty at output.\n - * - * The user will allocate packets submitted through the received list. The function - * will handle allocation of recovered packets and optionally deleting of all packet - * memory. The user may delete the recovered list packets, in which case they must - * remove deleted packets from the recovered list.\n - * - * Before deleting an instance of the class, call the function with an empty received - * packet list and the completion parameter set to true. This will free any - * outstanding memory. - * - * \param[in] receivedPacketList List of new received packets, of type - * #ReceivedPacket, beloning to a single frame. At - * output the list will be empty, with packets - * either stored internally, or accessible through - * the recovered list. - * \param[out] recoveredPacketList List of recovered media packets, of type - * #RecoveredPacket, belonging to a single frame. - * The memory available through the list will be - * valid until the next call to DecodeFEC() in which - * the completion parameter is set to true. - * \param[in] lastFECSeqNum Estimated last seqNumber before this frame - * \param[in,out] frameComplete Set to true on input to indicate the start of a - * new frame. On output, this will be set to true if - * all media packets in the frame have been - * recovered. Note that the frame may be complete - * without this parameter having been set, as it may - * not always be possible to determine frame - * completion. - * - * \return 0 on success, -1 on failure. - */ - WebRtc_Word32 DecodeFEC(ListWrapper& receivedPacketList, - ListWrapper& recoveredPacketList, - const WebRtc_UWord16 lastFECSeqNum, - bool& frameComplete); - /** - * Gets the size in bytes of the FEC/ULP headers, which must be accounted for as - * packet overhead. - * \return Packet overhead in bytes. - */ - static WebRtc_UWord16 PacketOverhead(); - -private: - WebRtc_Word32 _id; - Packet* _generatedFecPackets; - ListWrapper _fecPacketList; - WebRtc_UWord16 _seqNumBase; - bool _lastMediaPacketReceived; - bool _fecPacketReceived; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FORWARD_ERROR_CORRECTION_H_ diff --git a/modules/rtp_rtcp/source/forward_error_correction_internal.cc b/modules/rtp_rtcp/source/forward_error_correction_internal.cc deleted file mode 100644 index 1d2e6a29e..000000000 --- a/modules/rtp_rtcp/source/forward_error_correction_internal.cc +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "forward_error_correction_internal.h" -#include "fec_private_tables.h" - -#include -#include - -namespace { - -// This parameter enables/disables unequal protection (UEP) across packets. -// This is not to be confused with protection within packets (referred to as ULP). -// One use case of UEP across packets is for codecs with data partitioning, -// e.g., VP8, H264 XP profile, where important packets would be first partition. -// TODO (marpan): Pass this parameter from MediaOpt (VCM). -const bool kUseUnequalProtection = true; - -// Allow for two different modes of protection for residual packets. -// The residual packets are the remaining packets beyond the important ones. -enum ResidualProtectionMode -{ - kModeNoOverlap, - kModeOverlap, -}; - - -/** - * Fits an input mask (subMask) to an output mask. - * The mask is a matrix where the rows are the FEC packets, - * and the columns are the source packets the FEC is applied to. - * Each row of the mask is represented by a number of mask bytes. - * - * \param[in] numMaskBytes The number of mask bytes of output mask. - * \param[in] numSubMaskBytes The number of mask bytes of input mask. - * \param[in] numRows The number of rows of the input mask. - * \param[in] subMask A pointer to hold the input mask, of size - * [0, numRows * numSubMaskBytes] - * \param[out] packetMask A pointer to hold the output mask, of size - * [0, x * numMaskBytes], where x >= numRows. - */ -void FitSubMask(const WebRtc_UWord16 numMaskBytes, - const WebRtc_UWord16 numSubMaskBytes, - const WebRtc_UWord16 numRows, - const WebRtc_UWord8* subMask, - WebRtc_UWord8* packetMask) -{ - if (numMaskBytes == numSubMaskBytes) - { - - memcpy(packetMask,subMask, - numRows * numSubMaskBytes); - } - else - { - for (WebRtc_UWord32 i = 0; i < numRows; i++) - { - WebRtc_UWord32 pktMaskIdx = i * numMaskBytes; - WebRtc_UWord32 pktMaskIdx2 = i * numSubMaskBytes; - for (WebRtc_UWord32 j = 0; j < numSubMaskBytes; j++) - { - packetMask[pktMaskIdx] = subMask[pktMaskIdx2]; - pktMaskIdx++; - pktMaskIdx2++; - } - } - } -} - -/** - * Shifts a mask by number of columns (bits), and fits it to an output mask. - * The mask is a matrix where the rows are the FEC packets, - * and the columns are the source packets the FEC is applied to. - * Each row of the mask is represented by a number of mask bytes. - * - * \param[in] numMaskBytes The number of mask bytes of output mask. - * \param[in] numSubMaskBytes The number of mask bytes of input mask. - * \param[in] numColumnShift The number columns to be shifted, and - * the starting row for the output mask. - * \param[in] endRow The ending row for the output mask. - * \param[in] subMask A pointer to hold the input mask, of size - * [0, (endRowFEC - startRowFec) * numSubMaskBytes] - * \param[out] packetMask A pointer to hold the output mask, of size - * [0, x * numMaskBytes], where x >= endRowFEC. - */ -// TODO (marpan): This function is doing three things at the same time: -// shift within a byte, byte shift and resizing. -// Split up into subroutines. -void ShiftFitSubMask(const WebRtc_UWord16 numMaskBytes, - const WebRtc_UWord16 resMaskBytes, - const WebRtc_UWord16 numColumnShift, - const WebRtc_UWord16 endRow, - const WebRtc_UWord8* subMask, - WebRtc_UWord8* packetMask) -{ - - // Number of bit shifts within a byte - const WebRtc_UWord8 numBitShifts = (numColumnShift % 8); - const WebRtc_UWord8 numByteShifts = numColumnShift >> 3; - - // Modify new mask with sub-mask21. - - // Loop over the remaining FEC packets. - for (WebRtc_UWord32 i = numColumnShift; i < endRow; i++) - { - // Byte index of new mask, for row i and column resMaskBytes, - // offset by the number of bytes shifts - WebRtc_UWord32 pktMaskIdx = i * numMaskBytes + resMaskBytes - 1 - + numByteShifts; - // Byte index of subMask, for row i and column resMaskBytes - WebRtc_UWord32 pktMaskIdx2 = - (i - numColumnShift) * resMaskBytes + resMaskBytes - 1; - - WebRtc_UWord8 shiftRightCurrByte = 0; - WebRtc_UWord8 shiftLeftPrevByte = 0; - WebRtc_UWord8 combNewByte = 0; - - // Handle case of numMaskBytes > resMaskBytes: - // For a given row, copy the rightmost "numBitShifts" bits - // of the last byte of subMask into output mask. - if (numMaskBytes > resMaskBytes) - { - shiftLeftPrevByte = - (subMask[pktMaskIdx2] << (8 - numBitShifts)); - packetMask[pktMaskIdx + 1] = shiftLeftPrevByte; - } - - // For each row i (FEC packet), shift the bit-mask of the subMask. - // Each row of the mask contains "resMaskBytes" of bytes. - // We start from the last byte of the subMask and move to first one. - for (WebRtc_Word32 j = resMaskBytes - 1; j > 0; j--) - { - // Shift current byte of sub21 to the right by "numBitShifts". - shiftRightCurrByte = - subMask[pktMaskIdx2] >> numBitShifts; - - // Fill in shifted bits with bits from the previous (left) byte: - // First shift the previous byte to the left by "8-numBitShifts". - shiftLeftPrevByte = - (subMask[pktMaskIdx2 - 1] << (8 - numBitShifts)); - - // Then combine both shifted bytes into new mask byte. - combNewByte = shiftRightCurrByte | shiftLeftPrevByte; - - // Assign to new mask. - packetMask[pktMaskIdx] = combNewByte; - pktMaskIdx--; - pktMaskIdx2--; - } - // For the first byte in the row (j=0 case). - shiftRightCurrByte = subMask[pktMaskIdx2] >> numBitShifts; - packetMask[pktMaskIdx] = shiftRightCurrByte; - - } -} - -} //namespace - -namespace webrtc { -namespace internal { - -// Residual protection for remaining packets -void ResidualPacketProtection(const WebRtc_UWord16 numMediaPackets, - const WebRtc_UWord16 numFecPackets, - const WebRtc_UWord16 numImpPackets, - const WebRtc_UWord16 numMaskBytes, - const ResidualProtectionMode mode, - WebRtc_UWord8* packetMask) -{ - if (mode == kModeNoOverlap) - { - // subMask21 - - const WebRtc_UWord8 lBit = - (numMediaPackets - numImpPackets) > 16 ? 1 : 0; - - const WebRtc_UWord16 resMaskBytes = - (lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear; - - const WebRtc_UWord8* packetMaskSub21 = - packetMaskTbl[numMediaPackets - numImpPackets - 1] - [numFecPackets - numImpPackets - 1]; - - ShiftFitSubMask(numMaskBytes, resMaskBytes, - numImpPackets, numFecPackets, - packetMaskSub21, packetMask); - } - else if (mode == kModeOverlap) - { - // subMask22 - - const WebRtc_UWord16 numFecForResidual = - numFecPackets - numImpPackets; - - const WebRtc_UWord8* packetMaskSub22 = - packetMaskTbl[numMediaPackets - 1] - [numFecForResidual - 1]; - - FitSubMask(numMaskBytes, numMaskBytes, - numFecForResidual, - packetMaskSub22, - &packetMask[numImpPackets * numMaskBytes]); - } - else - { - assert(false); - } - -} - -// Higher protection for numImpPackets -void ImportantPacketProtection(const WebRtc_UWord16 numFecPackets, - const WebRtc_UWord16 numImpPackets, - const WebRtc_UWord16 numMaskBytes, - WebRtc_UWord8* packetMask) -{ - const WebRtc_UWord8 lBit = numImpPackets > 16 ? 1 : 0; - const WebRtc_UWord16 numImpMaskBytes = - (lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear; - - WebRtc_UWord32 numFecForImpPackets = numImpPackets; - if (numFecPackets < numImpPackets) - { - numFecForImpPackets = numFecPackets; - } - - // Get subMask1 from table - const WebRtc_UWord8* packetMaskSub1 = - packetMaskTbl[numImpPackets - 1][numFecForImpPackets - 1]; - - FitSubMask(numMaskBytes, numImpMaskBytes, - numFecForImpPackets, - packetMaskSub1, - packetMask); - -} - -// Modification for UEP: reuse the tables (designed for equal protection). -// First version is to build mask from two sub-masks. -// Longer-term, may add another set of tables for UEP cases for more -// flexibility in protection between important and residual packets. - -// UEP scheme: -// First subMask is for higher protection for important packets. -// Other subMask is the residual protection for remaining packets. - -// Mask is characterized as (#packets_to_protect, #fec_for_protection). -// Protection defined as: (#fec_for_protection / #packets_to_protect). - -// So if k = numMediaPackets, n=total#packets, (n-k)=numFecPackets, -// and m=numImpPackets, then we will have the following: - -// For important packets: -// subMask1 = (m, t): protection = m/(t), where t=min(m,n-k). - -// For the residual protection, we currently have two options: - -// Mode 0: subMask21 = (k-m,n-k-m): protection = (n-k-m)/(k-m): -// no protection overlap between the two partitions. - -// Mode 1: subMask22 = (k, n-k-m), with protection (n-k-m)/(k): -// some protection overlap between the two partitions. - -void UnequalProtectionMask(const WebRtc_UWord16 numMediaPackets, - const WebRtc_UWord16 numFecPackets, - const WebRtc_UWord16 numImpPackets, - const WebRtc_UWord16 numMaskBytes, - const ResidualProtectionMode mode, - WebRtc_UWord8* packetMask) -{ - - // - // Generate subMask1: higher protection for numImpPackets: - // - ImportantPacketProtection(numFecPackets, numImpPackets, - numMaskBytes, packetMask); - - // - // Generate subMask2: left-over protection (for remaining partition data), - // if we still have some some FEC packets - // - if (numFecPackets > numImpPackets) - { - - ResidualPacketProtection(numMediaPackets,numFecPackets, - numImpPackets, numMaskBytes, - mode, packetMask); - } - -} - -void GeneratePacketMasks(const WebRtc_UWord32 numMediaPackets, - const WebRtc_UWord32 numFecPackets, - const WebRtc_UWord32 numImpPackets, - WebRtc_UWord8* packetMask) -{ - assert(numMediaPackets <= sizeof(packetMaskTbl)/sizeof(*packetMaskTbl) && - numMediaPackets > 0); - assert(numFecPackets <= numMediaPackets && numFecPackets > 0); - assert(numImpPackets <= numMediaPackets && numImpPackets >= 0); - - WebRtc_UWord8 lBit = numMediaPackets > 16 ? 1 : 0; - const WebRtc_UWord16 numMaskBytes = - (lBit == 1)? kMaskSizeLBitSet : kMaskSizeLBitClear; - - // Default: use overlap mode for residual protection. - const ResidualProtectionMode kResidualProtectionMode = kModeOverlap; - - // Force equal-protection for these cases. - // Equal protection is also used for: (numImpPackets == 1 && numFecPackets == 1). - // UEP=off would generally be more efficient than the UEP=on for this case. - // TODO (marpan): check/test this condition. - if (!kUseUnequalProtection || numImpPackets == 0 || - (numImpPackets == 1 && numFecPackets == 1)) - { - // Retrieve corresponding mask table directly: for equal-protection case. - // Mask = (k,n-k), with protection factor = (n-k)/k, - // where k = numMediaPackets, n=total#packets, (n-k)=numFecPackets. - memcpy(packetMask, packetMaskTbl[numMediaPackets - 1][numFecPackets - 1], - numFecPackets * numMaskBytes); - } - else //UEP case - { - UnequalProtectionMask(numMediaPackets, numFecPackets, numImpPackets, - numMaskBytes, kResidualProtectionMode, - packetMask); - - } // End of UEP modification - -} //End of GetPacketMasks - -} // namespace internal -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/forward_error_correction_internal.h b/modules/rtp_rtcp/source/forward_error_correction_internal.h deleted file mode 100644 index 6cda8c111..000000000 --- a/modules/rtp_rtcp/source/forward_error_correction_internal.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "typedefs.h" - -namespace webrtc { - -// Packet mask size in bytes (L bit is set). -const WebRtc_UWord8 kMaskSizeLBitSet = 6; -// Packet mask size in bytes (L bit is cleared). -const WebRtc_UWord8 kMaskSizeLBitClear = 2; - -namespace internal { - - /** - * Returns an array of packet masks. The mask of a single FEC packet - * corresponds to a number of mask bytes. The mask indicates which - * media packets should be protected by the FEC packet. - - * \param[in] numMediaPackets The number of media packets to protect. - * [1, maxMediaPackets]. - * \param[in] numFecPackets The number of FEC packets which will be generated. - * [1, numMediaPackets]. - * \param[in] numImpPackets The number of important packets. - * [0, numMediaPackets]. - * numImpPackets = 0 is the equal protection scenario. - * \param[out] packetMask A pointer to hold the packet mask array, of size - * numFecPackets * "number of mask bytes". - */ -void GeneratePacketMasks(const WebRtc_UWord32 numMediaPackets, - const WebRtc_UWord32 numFecPackets, - const WebRtc_UWord32 numImpPackets, - WebRtc_UWord8* packetMask); - - -} // namespace internal -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/h263_information.cc b/modules/rtp_rtcp/source/h263_information.cc deleted file mode 100644 index 75030679d..000000000 --- a/modules/rtp_rtcp/source/h263_information.cc +++ /dev/null @@ -1,1472 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include "h263_information.h" - -namespace webrtc { -// MCBPC --- -static const WebRtc_Word32 MCBPC_I_MAX_CODE_LENGTH = 2; -static const WebRtc_Word32 MCBPC_I_TABLE_LENGTH = 9; -static const WebRtc_UWord8 MCBPC_I_CODE[9][2] = {{0x80,0x00}, //1 - {0x20,0x00}, //001 - {0x40,0x00}, //010 - {0x60,0x00}, //011 - {0x10,0x00}, //0001 - {0x04,0x00}, //0000 01 - {0x08,0x00}, //0000 10 - {0x0C,0x00}, //0000 11 - {0x00,0x80}}; //0000 0000 1 - -static const WebRtc_UWord8 MCBPC_I_MASK[9][2] = {{0x80,0x00}, //1 - {0xE0,0x00}, //001 - {0xE0,0x00}, //010 - {0xE0,0x00}, //011 - {0xF0,0x00}, //0001 - {0xFC,0x00}, //0000 01 - {0xFC,0x00}, //0000 10 - {0xFC,0x00}, //0000 11 - {0xFF,0x80}}; //0000 0000 1 - -static const WebRtc_Word32 MCBPC_I_MBTYPE[9] = {3,3,3,3,4,4,4,4,6}; -static const WebRtc_Word32 MCBPC_I_SIZE[9] = {1,3,3,3,4,6,6,6,9}; -static const char MCBPC_I_CBPC[9][2] = {{0,0},{0,1},{1,0},{1,1},{0,0},{0,1},{1,0},{1,1},{0,0}}; - -static const WebRtc_Word32 MCBPC_P_MAX_CODE_LENGTH = 2; -static const WebRtc_Word32 MCBPC_P_TABLE_LENGTH = 25; -static const WebRtc_UWord8 MCBPC_P_CODE[25][2] = {{0x80,0x00}, //1 - {0x30,0x00}, //0011 - {0x20,0x00}, //0010 - {0x14,0x00}, //0001 01 - {0x60,0x00}, //011 - {0x0E,0x00}, //0000 111 - {0x0C,0x00}, //0000 110 - {0x02,0x80}, //0000 0010 1 - {0x40,0x00}, //010 - {0x0A,0x00}, //0000 101 - {0x08,0x00}, //0000 100 - {0x05,0x00}, //0000 0101 - {0x18,0x00}, //0001 1 - {0x04,0x00}, //0000 0100 - {0x03,0x00}, //0000 0011 - {0x06,0x00}, //0000 011 - {0x10,0x00}, //0001 00 - {0x02,0x00}, //0000 0010 0 - {0x01,0x80}, //0000 0001 1 - {0x01,0x00}, //0000 0001 0 - {0x00,0x80}, //0000 0000 1 - {0x00,0x40}, //0000 0000 010 - {0x00,0x60}, //0000 0000 0110 0 - {0x00,0x70}, //0000 0000 0111 0 - {0x00,0x78}}; //0000 0000 0111 1 - -static const WebRtc_UWord8 MCBPC_P_MASK[25][2] = {{0x80,0x00}, //1 - {0xF0,0x00}, //0011 - {0xF0,0x00}, //0010 - {0xFC,0x00}, //0001 01 - {0xE0,0x00}, //011 - {0xFE,0x00}, //0000 111 - {0xFE,0x00}, //0000 110 - {0xFF,0x80}, //0000 0010 1 - {0xE0,0x00}, //010 - {0xFE,0x00}, //0000 101 - {0xFE,0x00}, //0000 100 - {0xFF,0x00}, //0000 0101 - {0xF8,0x00}, //0001 1 - {0xFF,0x00}, //0000 0100 - {0xFF,0x00}, //0000 0011 - {0xFE,0x00}, //0000 011 - {0xFC,0x00}, //0001 00 - {0xFF,0x80}, //0000 0010 0 - {0xFF,0x80}, //0000 0001 1 - {0xFF,0x80}, //0000 0001 0 - {0xFF,0x80}, //0000 0000 1 - {0xFF,0xE0}, //0000 0000 010 - {0xFF,0xF8}, //0000 0000 0110 0 - {0xFF,0xF8}, //0000 0000 0111 0 - {0xFF,0xF8}}; //0000 0000 0111 1 - -static const WebRtc_Word32 MCBPC_P_MBTYPE[25] = {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,6, 5, 5, 5, 5}; -static const WebRtc_Word32 MCBPC_P_SIZE[25] = {1,4,4,6,3,7,7,9,3,7,7,8,5,8,8,7,6,9,9,9,9,11,13,13,13}; -static const char MCBPC_P_CBPC[25][2] = {{0,0},{0,1},{1,0},{1,1}, - {0,0},{0,1},{1,0},{1,1}, - {0,0},{0,1},{1,0},{1,1}, - {0,0},{0,1},{1,0},{1,1}, - {0,0},{0,1},{1,0},{1,1}, - {0,0}, - {0,0},{0,1},{1,0},{1,1}}; - -// CBPY --- -static const WebRtc_Word32 CBPY_MAX_CODE_LENGTH = 1; -static const WebRtc_Word32 CBPY_TABLE_LENGTH = 16; -static const WebRtc_UWord8 CBPY_CODE[16][1] = {{0x30}, //0011 - {0x28}, //0010 1 - {0x20}, //0010 0 - {0x90}, //1001 - {0x18}, //0001 1 - {0x70}, //0111 - {0x08}, //0000 10 - {0xB0}, //1011 - {0x10}, //0001 0 - {0x0C}, //0000 11 - {0x50}, //0101 - {0xA0}, //1010 - {0x40}, //0100 - {0x80}, //1000 - {0x60}, //0110 - {0xC0}}; //11 - -static const WebRtc_UWord8 CBPY_MASK[16][1] = {{0xF0}, //0011 - {0xF8}, //0010 1 - {0xF8}, //0010 0 - {0xF0}, //1001 - {0xF8}, //0001 1 - {0xF0}, //0111 - {0xFC}, //0000 10 - {0xF0}, //1011 - {0xF8}, //0001 0 - {0xFC}, //0000 11 - {0xF0}, //0101 - {0xF0}, //1010 - {0xF0}, //0100 - {0xF0}, //1000 - {0xF0}, //0110 - {0xC0}}; //11 - -static const WebRtc_Word32 CBPY_SIZE[16] = {4,5,5,4,5,4,6,4,5,6,4,4,4,4,4,2}; -static const char CBPY_CBPY[16][4] = {{0,0,0,0},{0,0,0,1},{0,0,1,0},{0,0,1,1}, - {0,1,0,0},{0,1,0,1},{0,1,1,0},{0,1,1,1}, - {1,0,0,0},{1,0,0,1},{1,0,1,0},{1,0,1,1}, - {1,1,0,0},{1,1,0,1},{1,1,1,0},{1,1,1,1}}; - -// MVD --- -static const WebRtc_Word32 MVD_MAX_CODE_LENGTH = 2; -static const WebRtc_Word32 MVD_TABLE_LENGTH = 64; -static const WebRtc_UWord8 MVD_CODE[64][2] = {{0x00,0x28}, //0000 0000 0010 1 - {0x00,0x38}, //0000 0000 0011 1 - {0x00,0x50}, //0000 0000 0101 - {0x00,0x70}, //0000 0000 0111 - {0x00,0x90}, //0000 0000 1001 - {0x00,0xB0}, //0000 0000 1011 - {0x00,0xD0}, //0000 0000 1101 - {0x00,0xF0}, //0000 0000 1111 - {0x01,0x20}, //0000 0001 001 - {0x01,0x60}, //0000 0001 011 - {0x01,0xA0}, //0000 0001 101 - {0x01,0xE0}, //0000 0001 111 - {0x02,0x20}, //0000 0010 001 - {0x02,0x60}, //0000 0010 011 - {0x02,0xA0}, //0000 0010 101 - {0x02,0xE0}, //0000 0010 111 - {0x03,0x20}, //0000 0011 001 - {0x03,0x60}, //0000 0011 011 - {0x03,0xA0}, //0000 0011 101 - {0x03,0xE0}, //0000 0011 111 - {0x04,0x20}, //0000 0100 001 - {0x04,0x60}, //0000 0100 011 - {0x04,0xC0}, //0000 0100 11 - {0x05,0x40}, //0000 0101 01 - {0x05,0xC0}, //0000 0101 11 - {0x07,0x00}, //0000 0111 - {0x09,0x00}, //0000 1001 - {0x0B,0x00}, //0000 1011 - {0x0E,0x00}, //0000 111 - {0x18,0x00}, //0001 1 - {0x30,0x00}, //0011 - {0x60,0x00}, //011 - {0x80,0x00}, //1 - {0x40,0x00}, //010 - {0x20,0x00}, //0010 - {0x10,0x00}, //0001 0 - {0x0C,0x00}, //0000 110 - {0x0A,0x00}, //0000 1010 - {0x08,0x00}, //0000 1000 - {0x06,0x00}, //0000 0110 - {0x05,0x80}, //0000 0101 10 - {0x05,0x00}, //0000 0101 00 - {0x04,0x80}, //0000 0100 10 - {0x04,0x40}, //0000 0100 010 - {0x04,0x00}, //0000 0100 000 - {0x03,0xC0}, //0000 0011 110 - {0x03,0x80}, //0000 0011 100 - {0x03,0x40}, //0000 0011 010 - {0x03,0x00}, //0000 0011 000 - {0x02,0xC0}, //0000 0010 110 - {0x02,0x80}, //0000 0010 100 - {0x02,0x40}, //0000 0010 010 - {0x02,0x00}, //0000 0010 000 - {0x01,0xC0}, //0000 0001 110 - {0x01,0x80}, //0000 0001 100 - {0x01,0x40}, //0000 0001 010 - {0x01,0x00}, //0000 0001 000 - {0x00,0xE0}, //0000 0000 1110 - {0x00,0xC0}, //0000 0000 1100 - {0x00,0xA0}, //0000 0000 1010 - {0x00,0x80}, //0000 0000 1000 - {0x00,0x60}, //0000 0000 0110 - {0x00,0x40}, //0000 0000 0100 - {0x00,0x30}}; //0000 0000 0011 0 - -static const WebRtc_UWord8 MVD_MASK[64][2] = {{0xFF,0xF8}, //0000 0000 0010 1 - {0xFF,0xF8}, //0000 0000 0011 1 - {0xFF,0xF0}, //0000 0000 0101 - {0xFF,0xF0}, //0000 0000 0111 - {0xFF,0xF0}, //0000 0000 1001 - {0xFF,0xF0}, //0000 0000 1011 - {0xFF,0xF0}, //0000 0000 1101 - {0xFF,0xF0}, //0000 0000 1111 - {0xFF,0xE0}, //0000 0001 001 - {0xFF,0xE0}, //0000 0001 011 - {0xFF,0xE0}, //0000 0001 101 - {0xFF,0xE0}, //0000 0001 111 - {0xFF,0xE0}, //0000 0010 001 - {0xFF,0xE0}, //0000 0010 011 - {0xFF,0xE0}, //0000 0010 101 - {0xFF,0xE0}, //0000 0010 111 - {0xFF,0xE0}, //0000 0011 001 - {0xFF,0xE0}, //0000 0011 011 - {0xFF,0xE0}, //0000 0011 101 - {0xFF,0xE0}, //0000 0011 111 - {0xFF,0xE0}, //0000 0100 001 - {0xFF,0xE0}, //0000 0100 011 - {0xFF,0xC0}, //0000 0100 11 - {0xFF,0xC0}, //0000 0101 01 - {0xFF,0xC0}, //0000 0101 11 - {0xFF,0x00}, //0000 0111 - {0xFF,0x00}, //0000 1001 - {0xFF,0x00}, //0000 1011 - {0xFE,0x00}, //0000 111 - {0xF8,0x00}, //0001 1 - {0xF0,0x00}, //0011 - {0xE0,0x00}, //011 - {0x80,0x00}, //1 - {0xE0,0x00}, //010 - {0xF0,0x00}, //0010 - {0xF8,0x00}, //0001 0 - {0xFE,0x00}, //0000 110 - {0xFF,0x00}, //0000 1010 - {0xFF,0x00}, //0000 1000 - {0xFF,0x00}, //0000 0110 - {0xFF,0xC0}, //0000 0101 10 - {0xFF,0xC0}, //0000 0101 00 - {0xFF,0xC0}, //0000 0100 10 - {0xFF,0xE0}, //0000 0100 010 - {0xFF,0xE0}, //0000 0100 000 - {0xFF,0xE0}, //0000 0011 110 - {0xFF,0xE0}, //0000 0011 100 - {0xFF,0xE0}, //0000 0011 010 - {0xFF,0xE0}, //0000 0011 000 - {0xFF,0xE0}, //0000 0010 110 - {0xFF,0xE0}, //0000 0010 100 - {0xFF,0xE0}, //0000 0010 010 - {0xFF,0xE0}, //0000 0010 000 - {0xFF,0xE0}, //0000 0001 110 - {0xFF,0xE0}, //0000 0001 100 - {0xFF,0xE0}, //0000 0001 010 - {0xFF,0xE0}, //0000 0001 000 - {0xFF,0xF0}, //0000 0000 1110 - {0xFF,0xF0}, //0000 0000 1100 - {0xFF,0xF0}, //0000 0000 1010 - {0xFF,0xF0}, //0000 0000 1000 - {0xFF,0xF0}, //0000 0000 0110 - {0xFF,0xF0}, //0000 0000 0100 - {0xFF,0xF8}}; //0000 0000 0011 0 - -static const WebRtc_Word32 MVD_SIZE[64] = {13,13,12,12,12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11, - 11,11,10,10,10, 8, 8, 8, 7, 5, 4, 3, 1, 3, 4, 5, 7, 8, 8, 8, - 10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12, - 12,12,12,13}; - -// TCOEF --- -static const WebRtc_Word32 TCOEF_MAX_CODE_LENGTH = 2; -static const WebRtc_Word32 TCOEF_TABLE_LENGTH = 103; -static const WebRtc_UWord8 TCOEF_CODE[103][2] = {{0x80,0x00}, //10s - {0xF0,0x00}, //1111s - {0x54,0x00}, //0101 01s - {0x2E,0x00}, //0010 111s - {0x1F,0x00}, //0001 1111s - {0x12,0x80}, //0001 0010 1s - {0x12,0x00}, //0001 0010 0s - {0x08,0x40}, //0000 1000 01s - {0x08,0x00}, //0000 1000 00s - {0x00,0xE0}, //0000 0000 111s - {0x00,0xC0}, //0000 0000 110s - {0x04,0x00}, //0000 0100 000s - {0xC0,0x00}, //110s - {0x50,0x00}, //0101 00s - {0x1E,0x00}, //0001 1110s - {0x03,0xC0}, //0000 0011 11s - {0x04,0x20}, //0000 0100 001s - {0x05,0x00}, //0000 0101 0000s - {0xE0,0x00}, //1110s - {0x1D,0x00}, //0001 1101s - {0x03,0x80}, //0000 0011 10s - {0x05,0x10}, //0000 0101 0001s - {0x68,0x00}, //0110 1s - {0x11,0x80}, //0001 0001 1s - {0x03,0x40}, //0000 0011 01s - {0x60,0x00}, //0110 0s - {0x11,0x00}, //0001 0001 0s - {0x05,0x20}, //0000 0101 0010s - {0x58,0x00}, //0101 1s - {0x03,0x00}, //0000 0011 00s - {0x05,0x30}, //0000 0101 0011s - {0x4C,0x00}, //0100 11s - {0x02,0xC0}, //0000 0010 11s - {0x05,0x40}, //0000 0101 0100s - {0x48,0x00}, //0100 10s - {0x02,0x80}, //0000 0010 10s - {0x44,0x00}, //0100 01s - {0x02,0x40}, //0000 0010 01s - {0x40,0x00}, //0100 00s - {0x02,0x00}, //0000 0010 00s - {0x2C,0x00}, //0010 110s - {0x05,0x50}, //0000 0101 0101s - {0x2A,0x00}, //0010 101s - {0x28,0x00}, //0010 100s - {0x1C,0x00}, //0001 1100s - {0x1B,0x00}, //0001 1011s - {0x10,0x80}, //0001 0000 1s - {0x10,0x00}, //0001 0000 0s - {0x0F,0x80}, //0000 1111 1s - {0x0F,0x00}, //0000 1111 0s - {0x0E,0x80}, //0000 1110 1s - {0x0E,0x00}, //0000 1110 0s - {0x0D,0x80}, //0000 1101 1s - {0x0D,0x00}, //0000 1101 0s - {0x04,0x40}, //0000 0100 010s - {0x04,0x60}, //0000 0100 011s - {0x05,0x60}, //0000 0101 0110s - {0x05,0x70}, //0000 0101 0111s - {0x70,0x00}, //0111s //last=1 --------------------- - {0x0C,0x80}, //0000 1100 1s - {0x00,0xA0}, //0000 0000 101s - {0x3C,0x00}, //0011 11s - {0x00,0x80}, //0000 0000 100s - {0x38,0x00}, //0011 10s - {0x34,0x00}, //0011 01s - {0x30,0x00}, //0011 00s - {0x26,0x00}, //0010 011s - {0x24,0x00}, //0010 010s - {0x22,0x00}, //0010 001s - {0x20,0x00}, //0010 000s - {0x1A,0x00}, //0001 1010s - {0x19,0x00}, //0001 1001s - {0x18,0x00}, //0001 1000s - {0x17,0x00}, //0001 0111s - {0x16,0x00}, //0001 0110s - {0x15,0x00}, //0001 0101s - {0x14,0x00}, //0001 0100s - {0x13,0x00}, //0001 0011s - {0x0C,0x00}, //0000 1100 0s - {0x0B,0x80}, //0000 1011 1s - {0x0B,0x00}, //0000 1011 0s - {0x0A,0x80}, //0000 1010 1s - {0x0A,0x00}, //0000 1010 0s - {0x09,0x80}, //0000 1001 1s - {0x09,0x00}, //0000 1001 0s - {0x08,0x80}, //0000 1000 1s - {0x01,0xC0}, //0000 0001 11s - {0x01,0x80}, //0000 0001 10s - {0x01,0x40}, //0000 0001 01s - {0x01,0x00}, //0000 0001 00s - {0x04,0x80}, //0000 0100 100s - {0x04,0xA0}, //0000 0100 101s - {0x04,0xC0}, //0000 0100 110s - {0x04,0xE0}, //0000 0100 111s - {0x05,0x80}, //0000 0101 1000s - {0x05,0x90}, //0000 0101 1001s - {0x05,0xA0}, //0000 0101 1010s - {0x05,0xB0}, //0000 0101 1011s - {0x05,0xC0}, //0000 0101 1100s - {0x05,0xD0}, //0000 0101 1101s - {0x05,0xE0}, //0000 0101 1110s - {0x05,0xF0}, //0000 0101 1111s - {0x06,0x00}}; //0000 011 (escape) - -static const WebRtc_UWord8 TCOEF_MASK[103][2] = {{0xC0,0x00}, //10s - {0xF0,0x00}, //1111s - {0xFC,0x00}, //0101 01s - {0xFE,0x00}, //0010 111s - {0xFF,0x00}, //0001 1111s - {0xFF,0x80}, //0001 0010 1s - {0xFF,0x80}, //0001 0010 0s - {0xFF,0xC0}, //0000 1000 01s - {0xFF,0xC0}, //0000 1000 00s - {0xFF,0xE0}, //0000 0000 111s - {0xFF,0xE0}, //0000 0000 110s - {0xFF,0xE0}, //0000 0100 000s - {0xE0,0x00}, //110s - {0xFC,0x00}, //0101 00s - {0xFF,0x00}, //0001 1110s - {0xFF,0xC0}, //0000 0011 11s - {0xFF,0xE0}, //0000 0100 001s - {0xFF,0xF0}, //0000 0101 0000s - {0xF0,0x00}, //1110s - {0xFF,0x00}, //0001 1101s - {0xFF,0xC0}, //0000 0011 10s - {0xFF,0xF0}, //0000 0101 0001s - {0xF8,0x00}, //0110 1s - {0xFF,0x80}, //0001 0001 1s - {0xFF,0xC0}, //0000 0011 01s - {0xF8,0x00}, //0110 0s - {0xFF,0x80}, //0001 0001 0s - {0xFF,0xF0}, //0000 0101 0010s - {0xF8,0x00}, //0101 1s - {0xFF,0xC0}, //0000 0011 00s - {0xFF,0xF0}, //0000 0101 0011s - {0xFC,0x00}, //0100 11s - {0xFF,0xC0}, //0000 0010 11s - {0xFF,0xF0}, //0000 0101 0100s - {0xFC,0x00}, //0100 10s - {0xFF,0xC0}, //0000 0010 10s - {0xFC,0x00}, //0100 01s - {0xFF,0xC0}, //0000 0010 01s - {0xFC,0x00}, //0100 00s - {0xFF,0xC0}, //0000 0010 00s - {0xFE,0x00}, //0010 110s - {0xFF,0xF0}, //0000 0101 0101s - {0xFE,0x00}, //0010 101s - {0xFE,0x00}, //0010 100s - {0xFF,0x00}, //0001 1100s - {0xFF,0x00}, //0001 1011s - {0xFF,0x80}, //0001 0000 1s - {0xFF,0x80}, //0001 0000 0s - {0xFF,0x80}, //0000 1111 1s - {0xFF,0x80}, //0000 1111 0s - {0xFF,0x80}, //0000 1110 1s - {0xFF,0x80}, //0000 1110 0s - {0xFF,0x80}, //0000 1101 1s - {0xFF,0x80}, //0000 1101 0s - {0xFF,0xE0}, //0000 0100 010s - {0xFF,0xE0}, //0000 0100 011s - {0xFF,0xF0}, //0000 0101 0110s - {0xFF,0xF0}, //0000 0101 0111s - {0xF0,0x00}, //0111s //last=1 ---- - {0xFF,0x80}, //0000 1100 1s - {0xFF,0xE0}, //0000 0000 101s - {0xFC,0x00}, //0011 11s - {0xFF,0xE0}, //0000 0000 100s - {0xFC,0x00}, //0011 10s - {0xFC,0x00}, //0011 01s - {0xFC,0x00}, //0011 00s - {0xFE,0x00}, //0010 011s - {0xFE,0x00}, //0010 010s - {0xFE,0x00}, //0010 001s - {0xFE,0x00}, //0010 000s - {0xFF,0x00}, //0001 1010s - {0xFF,0x00}, //0001 1001s - {0xFF,0x00}, //0001 1000s - {0xFF,0x00}, //0001 0111s - {0xFF,0x00}, //0001 0110s - {0xFF,0x00}, //0001 0101s - {0xFF,0x00}, //0001 0100s - {0xFF,0x00}, //0001 0011s - {0xFF,0x80}, //0000 1100 0s - {0xFF,0x80}, //0000 1011 1s - {0xFF,0x80}, //0000 1011 0s - {0xFF,0x80}, //0000 1010 1s - {0xFF,0x80}, //0000 1010 0s - {0xFF,0x80}, //0000 1001 1s - {0xFF,0x80}, //0000 1001 0s - {0xFF,0x80}, //0000 1000 1s - {0xFF,0xC0}, //0000 0001 11s - {0xFF,0xC0}, //0000 0001 10s - {0xFF,0xC0}, //0000 0001 01s - {0xFF,0xC0}, //0000 0001 00s - {0xFF,0xE0}, //0000 0100 100s - {0xFF,0xE0}, //0000 0100 101s - {0xFF,0xE0}, //0000 0100 110s - {0xFF,0xE0}, //0000 0100 111s - {0xFF,0xF0}, //0000 0101 1000s - {0xFF,0xF0}, //0000 0101 1001s - {0xFF,0xF0}, //0000 0101 1010s - {0xFF,0xF0}, //0000 0101 1011s - {0xFF,0xF0}, //0000 0101 1100s - {0xFF,0xF0}, //0000 0101 1101s - {0xFF,0xF0}, //0000 0101 1110s - {0xFF,0xF0}, //0000 0101 1111s - {0xFE,0x00}}; //0000 011 (escape) - -static const WebRtc_Word32 TCOEF_SIZE[103] = { 3, 5, 7, 8, 9,10,10,11,11,12,12,12, 4, 7, 9,11,12,13, 5, 9, - 11,13, 6,10,11, 6,10,13, 6,11,13, 7,11,13, 7,11, 7,11, 7,11, - 8,13, 8, 8, 9, 9,10,10,10,10,10,10,10,10,12,12,13,13, 5,10, - 12, 7,12, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,10,10, - 10,10,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,13,13, - 13,13, 8}; - -static const char TCOEF_LAST[103] = {0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,1, - 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1, 1,1,0}; - -// -------------------------------------- -// -------------------------------------- -// -------------------------------------- - -WebRtc_Word32 -H263Info::CalculateMBOffset(const WebRtc_UWord8 numOfGOB) const -{ - // calculate the number of MBs before this GOB - - // sanity - if( numOfGOBs < numOfGOB) - { - assert(false); - return -1; - } - WebRtc_Word32 numMBs = 0; - for(WebRtc_UWord8 GOB = 0; GOB< numOfGOB; GOB++) - { - numMBs += ptrNumOfMBs[GOB]; - } - return numMBs; -} - - -H263Information::H263Information() -{ -} - -H263Information::~H263Information() -{ - _infoMB.bufferSize = 0; - if (_infoMB.ptrBuffer) - { - delete [] _infoMB.ptrBuffer; - delete [] _infoMB.ptrBufferHMV; - delete [] _infoMB.ptrBufferVMV; - _infoMB.ptrBuffer = 0; - _infoMB.ptrBufferHMV = 0; - _infoMB.ptrBufferVMV = 0; - } -} - -void -H263Information::Reset() -{ - _info.uiH263PTypeFmt = 0; - _info.codecBits = 0; - _info.pQuant = 0; - _info.numOfGOBs = 0; - _info.cpmBit = 0; - _info.fType = 0; - - memset(_info.ptrGOBbuffer, 0, sizeof(_info.ptrGOBbuffer)); - memset(_info.ptrGOBbufferSBit, 0, sizeof(_info.ptrGOBbufferSBit)); - memset(_info.ptrGQuant, 0, sizeof(_info.ptrGQuant)); - memset(_info.ptrNumOfMBs, 0, sizeof(_info.ptrNumOfMBs)); - memset(_info.ptrGroupNum, 0, sizeof(_info.ptrGroupNum)); - - if (_infoMB.ptrBuffer) - { - memset(_infoMB.ptrBuffer, 0, sizeof(WebRtc_UWord32) * _infoMB.bufferSize); - memset(_infoMB.ptrBufferHMV, 0, sizeof(WebRtc_UWord8) * _infoMB.bufferSize); - memset(_infoMB.ptrBufferVMV, 0, sizeof(WebRtc_UWord8) * _infoMB.bufferSize); - } -} - -/******************************************************************************* - * WebRtc_Word32 GetInfo(const WebRtc_UWord8* ptrEncodedBuffer, - * const WebRtc_UWord32 length, - * const H263Info*& ptrInfo); - * - * Gets information from an encoded stream. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Output: - * - ptrInfo : Pointer to struct with H263 info. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H263Information::GetInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length, - const H263Info*& ptrInfo) -{ - if (!ptrEncodedBuffer || length < 8) - { - return -1; - } - - if (!HasInfo(length)) - { - if (-1 == FindInfo(ptrEncodedBuffer, length)) - { - Reset(); - return -1; - } - } - - ptrInfo = &_info; - return 0; -} - -RtpVideoCodecTypes -H263Information::Type() -{ - return kRtpH263Video; -} - - -/******************************************************************************* - * WebRtc_Word32 GetMBInfo(const WebRtc_UWord8* ptrEncodedBuffer, - * const WebRtc_UWord32 length, - * WebRtc_Word32 numOfGOB, - * const H263MBInfo*& ptrInfoMB); - * - * Gets macroblock positions for a GOB. - * Also, the horizontal and vertical motion vector for each MB are returned. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - numOfGOB : Group number of current GOB. - * - * Output: - * - infoMB : Pointer to struct with MB positions in bits for a GOB. - * Horizontal and vertical motion vector for each MB. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ -WebRtc_Word32 -H263Information::GetMBInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length, - const WebRtc_UWord8 numOfGOB, - const H263MBInfo*& ptrInfoMB) -{ - if (!ptrEncodedBuffer || numOfGOB > _info.numOfGOBs - 1) - { - return -1; - } - if (-1 == VerifyAndAllocateMB()) - { - return -1; - } - if (length != _info.ptrGOBbuffer[_info.numOfGOBs]) - { - return -1; - } - if (!HasMBInfo(numOfGOB)) - { - if (-1 == FindMBs(ptrEncodedBuffer, numOfGOB, length)) - { - Reset(); - return -1; - } - } - ptrInfoMB = &_infoMB; - return 0; -} - -bool -H263Information::HasInfo(const WebRtc_UWord32 length) -{ - if (!_info.ptrGOBbuffer) - { - return false; - } - if (_info.ptrGOBbuffer[0] == 0) - { - return false; - } - - // has info, make sure current length matches info length - if (length != _info.ptrGOBbuffer[_info.numOfGOBs - 1]) - { - Reset(); - return false; - } - return true; -} - -WebRtc_Word32 -H263Information::FindInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length) -{ - _ptrData = ptrEncodedBuffer; - - if (!PictureStartCode()) - { - return -1; - } - - // Gets picture size - if (-1 == FindPTypeFMT()) - { - return -1; - } - - FindFType(); - FindCodecBits(); - FindPQUANT(); - FindCPMbit(); - - if (-1 == FindGOBs(length)) - { - return -1; - } - if (-1 == SetNumOfMBs()) - { - return -1; - } - return 0; -} - -WebRtc_Word32 -H263Information::SetNumOfMBs() -{ - // Source format: 000 - forbidden - // 001 - SQCIF 1 GOB - one macroblock row - // 010 - QCIF 1 GOB - one macroblock row - // 011 - CIF 1 GOB - one macroblock row - // 100 - 4CIF 1 GOB - two macroblock rows - // 101 - 16CIF 1 GOB - four macroblock rows - // 110 - reserved - // 111 - extended PTYPE - - WebRtc_UWord16 numOfMBsPerGOB = 0; - - switch (_info.uiH263PTypeFmt) - { - case 1: // SQCIF - numOfMBsPerGOB = 8; - _info.totalNumOfMBs = 8 * 6; //128x96 - break; - case 2: // QCIF - numOfMBsPerGOB = 11; - _info.totalNumOfMBs = 11 * 9; - break; - case 3: // CIF - numOfMBsPerGOB = 22; - _info.totalNumOfMBs = 22 * 18; - break; - case 4: // 4CIF - numOfMBsPerGOB = 88; - _info.totalNumOfMBs = 88 * 18; - break; - case 5: // 16CIF - numOfMBsPerGOB = 352; - _info.totalNumOfMBs = 352 * 18; - break; - default: - return -1; - } - - // loop through all GOBs - WebRtc_UWord16 numberOfMBs = 0; - for(WebRtc_UWord8 GOB = 0; GOB < (_info.numOfGOBs -1); GOB++) - { - _info.ptrNumOfMBs[GOB] = numOfMBsPerGOB * (_info.ptrGroupNum[GOB+1] - _info.ptrGroupNum[GOB]); - numberOfMBs += _info.ptrNumOfMBs[GOB]; - } - _info.ptrNumOfMBs[_info.numOfGOBs -1] = _info.totalNumOfMBs - numberOfMBs; - return 0; -} - -bool -H263Information::PictureStartCode() -{ - // Picture start code (PSC) (22 bits) - // ----0--------1-------2---- - // |00000000|00000000|100000xx| - // -------------------------- - - if ( _ptrData[0] == 0 && - _ptrData[1] == 0 && - (_ptrData[2] & 0x80) && - (_ptrData[2] & 0x7C) == 0) - { - return true; - } - return false; -} - -WebRtc_Word32 -H263Information::FindPTypeFMT() -{ - // Type Information (PTYPE) (Variable Length)(8/13 bits) - // ----3--------4--------5--- - // |xxxxxxPP|PPPPPPpp|pppxxxxx| - // -------------------------- - - // Source format (bits 6-8) - _info.uiH263PTypeFmt = (_ptrData[4] >> 2) & 0x07; - return 0; -} - -void -H263Information::FindFType() -{ - // Type Information (PTYPE) (13 bits, source format != 111, not extended PTYPE) - // ----3--------4--------5--- - // |xxxxxxPP|PPPPPPPP|PPPxxxxx| - // -------------------------- - - // Picture coding type (bit 9) - _info.fType = (_ptrData[4] & 0x02) >> 1; // 0 = I-frame, 1 = P-frame -} - -void -H263Information::FindCodecBits() -{ - // Type Information (PTYPE) (13 bits) - // ----3--------4--------5--- - // |xxxxxxPP|PPPPPPPP|PPPxxxxx| - // -------------------------- - - // Bits 9-12 // bit 10: Unrestricted Motion Vector mode (annex D) 0-off, 1-on - _info.codecBits = (_ptrData[4] & 0x03) << 2; // bit 11: Syntax-based Arithmetic Coding mode (annex E) - _info.codecBits += (_ptrData[5] & 0xC0) >> 6; // bit 12: Advanced Prediction mode (annex F) -} - -void -H263Information::FindPQUANT() -{ - // Quantizer Information (PQUANT) (5 bits) - // ----5--- - // |xxxQQQQQ| - // -------- - - _info.pQuant = _ptrData[5] & 0x1F; -} - -void -H263Information::FindCPMbit() -{ - // Continuous presence multipoWebRtc_Word32 and Video multiplex (CPM) (1 bit) - // ----6--- - // |Cxxxxxxx| - // -------- - - _info.cpmBit = IsBitOne(48); // 0-off, 1-on -} - -WebRtc_Word32 -H263Information::FindGOBs(const WebRtc_UWord32 length) -{ - // Group of block layer (GOB). - // GOB header followed by data for (one or more rows) of macroblocks. - - // Stuffing (GSTUF) (Variable length) consisting of less than 8 zero-bits. May be - // inserted by encoders so that the start of the GBSC is byte aligned. - // Group of block start code (GBSC) (17 bits). May be byte aligned. - // Group Number (GN) (5 bits). Group numbers 1-17 used for standard picture formats. - // 0 used in PSC. - // -------------------------------- - // |GSTUF|00000000|00000000|1GGGGGxx| - // -------------------------------- - - WebRtc_UWord8 numOfGOB = 0; - WebRtc_UWord8 groupNum = 0; - WebRtc_UWord8 sBit = 0; - - _info.ptrGroupNum[numOfGOB] = 0; - _info.ptrGOBbuffer[numOfGOB] = 0; - _info.ptrGOBbufferSBit[numOfGOB] = 0; - numOfGOB++; - - for (WebRtc_UWord32 i = 3; (i < length - 2); i++) - { - if (_ptrData[i] == 0) - { - if (_ptrData[i + 1] == 0) - { - if (_ptrData[i + 2] & 0x80) - { - // GBSC byte aligned - groupNum = (_ptrData[i + 2] >> 2) & 0x1f; - _info.ptrGroupNum[numOfGOB] = groupNum; - _info.ptrGOBbuffer[numOfGOB] = i; - _info.ptrGOBbufferSBit[numOfGOB] = 0; - numOfGOB++; - } - }else - { - // check for non byte aligned GBSC - if ((_ptrData[i - 1] & 0x7F) == 0 && (_ptrData[i + 1] & 0xC0) == 0x40) - { - // |x0000000|00000000|01GGGGGx| - sBit = 1; - groupNum = (_ptrData[i + 1] >> 1) & 0x1f; - } - else if ((_ptrData[i - 1] & 0x3F) == 0 && (_ptrData[i + 1] & 0xE0) == 0x20) - { - // |xx000000|00000000|001GGGGG| - sBit = 2; - groupNum = (_ptrData[i + 1]) & 0x1f; - } - else if ((_ptrData[i - 1] & 0x1F) == 0 && (_ptrData[i + 1] & 0xF0) == 0x10) - { - // |xxx00000|00000000|0001GGGG|G - sBit = 3; - groupNum = ((_ptrData[i + 1] & 0x0F) << 1) | ((_ptrData[i + 2] >> 7) & 0x01); - } - else if ((_ptrData[i - 1] & 0x0F) == 0 && (_ptrData[i + 1] & 0xF8) == 0x08) - { - // |xxxx0000|00000000|00001GGG|GG - sBit = 4; - groupNum = ((_ptrData[i + 1] & 0x07) << 2) | ((_ptrData[i + 2] >> 6) & 0x03); - } - else if ((_ptrData[i - 1] & 0x07) == 0 && (_ptrData[i + 1] & 0xFC) == 0x04) - { - // |xxxxx000|00000000|000001GG|GGG - sBit = 5; - groupNum = ((_ptrData[i + 1] & 0x03) << 3) | ((_ptrData[i + 2] >> 5) & 0x07); - } - else if ((_ptrData[i - 1] & 0x03) == 0 && (_ptrData[i + 1] & 0xFE) == 0x02) - { - // |xxxxxx00|00000000|0000001G|GGGG - sBit = 6; - groupNum = ((_ptrData[i + 1] & 0x01) << 4) | ((_ptrData[i + 2] >> 4) & 0x0F); - } - else if ((_ptrData[i - 1] & 0x01) == 0 && _ptrData[i + 1] == 0x01) - { - // |xxxxxxx0|00000000|00000001|GGGGG - sBit = 7; - groupNum = (_ptrData[i + 2] >> 3) & 0x1f; - } - else - { - sBit = 0; - groupNum = 0; - } - if (sBit) - { - _info.ptrGroupNum[numOfGOB] = groupNum; - _info.ptrGOBbuffer[numOfGOB] = i - 1; - _info.ptrGOBbufferSBit[numOfGOB] = sBit; - numOfGOB++; - } - } - if(numOfGOB >= MAX_NUMBER_OF_H263_GOB) - { - return -1; - } - } - } - _info.numOfGOBs = numOfGOB; - _info.ptrGOBbuffer[numOfGOB] = length; - _info.ptrGOBbufferSBit[numOfGOB] = 0; - return 0; -} - -WebRtc_Word32 -H263Information::VerifyAndAllocateMB() -{ - WebRtc_UWord32 minimumSize = _info.totalNumOfMBs; - if (minimumSize == 0) - { - return -1; - } - - if (minimumSize > _infoMB.bufferSize) - { - // make sure that our buffer is big enough - if (_infoMB.ptrBuffer) - { - delete [] _infoMB.ptrBuffer; - delete [] _infoMB.ptrBufferHMV; - delete [] _infoMB.ptrBufferVMV; - } - _infoMB.ptrBuffer = new WebRtc_UWord32[minimumSize]; - _infoMB.ptrBufferHMV = new WebRtc_UWord8[minimumSize]; - _infoMB.ptrBufferVMV = new WebRtc_UWord8[minimumSize]; - _infoMB.bufferSize = minimumSize; - - // reset memory - memset(_infoMB.ptrBuffer, 0, sizeof(WebRtc_UWord32) * _infoMB.bufferSize); - memset(_infoMB.ptrBufferHMV, 0, sizeof(WebRtc_UWord8) * _infoMB.bufferSize); - memset(_infoMB.ptrBufferVMV, 0, sizeof(WebRtc_UWord8) * _infoMB.bufferSize); - } - return 0; -} - -bool -H263Information::HasMBInfo(const WebRtc_UWord8 numOfGOB) -{ - if (!_infoMB.ptrBuffer) - { - return false; - } - - WebRtc_Word32 offset = _info.CalculateMBOffset(numOfGOB); - - if (_infoMB.ptrBuffer[offset] == 0) - { - return false; - } - return true; -} - -WebRtc_Word32 -H263Information::FindMBs(const WebRtc_UWord8 *ptrEncodedBuffer, - const WebRtc_UWord8 numOfGOB, - const WebRtc_UWord32 length) -{ - _bitCnt = 0; - WebRtc_Word32 bitCntOffset = 0; - _ptrData = ptrEncodedBuffer; - WebRtc_UWord32 payloadBytesToSend = length; - - if (numOfGOB > 0) - { - // Point at current GOB - _ptrData += _info.ptrGOBbuffer[numOfGOB ]; - payloadBytesToSend -= _info.ptrGOBbuffer[numOfGOB]; - // Start bits to ignore - _bitCnt += _info.ptrGOBbufferSBit[numOfGOB]; - // Byte with start bits has already been sent, start counting from next byte - if (_info.ptrGOBbufferSBit[numOfGOB]) - { - bitCntOffset = 8; - } - } - - WebRtc_Word32 offset = _info.CalculateMBOffset(numOfGOB); - WebRtc_UWord32 *sizeOfMBs = &_infoMB.ptrBuffer[offset]; - WebRtc_UWord8 *hmv1 = &_infoMB.ptrBufferHMV[offset]; - WebRtc_UWord8 *vmv1 = &_infoMB.ptrBufferVMV[offset]; - - // Header data - if (numOfGOB == 0) - { - // Picture layer - // ----------------------- - // | Picture header | GOBs | // For GOB number 0, empty GOB header - // ----------------------- - - _bitCnt = 49; - if (_info.cpmBit) - { - _bitCnt += 2; - } - - WebRtc_Word32 peiBit = IsBitOne(_bitCnt); - _bitCnt++; - if (peiBit) - { - _bitCnt += 8; - peiBit = IsBitOne(_bitCnt); - _bitCnt++; - if (peiBit) - { - _bitCnt += 9; - } - } - } - else if (numOfGOB < _info.numOfGOBs) - { - // Group of block layer (GOB). - // Group of block start code (GBSC) (17 bits). - // Group Number (GN) (5 bits). - // GOB Sub-Stream Indicator (GSBI) (2 bits). Only present if CPM = 1. - // GOB Frame ID (GFID) (2 bits). - // Quantizer Information (GQUANT) (5 bits). - // ---------------------------------------------------- - // | GBSC | GN | GSBI | GFID | GQUANT | Macroblock data | - // ---------------------------------------------------- - - _bitCnt += 24; - if (_info.cpmBit) - { - _bitCnt += 2; - } - - FindGQUANT(numOfGOB); - - _bitCnt += 5; - } - else - { - return -1; - } - - // Start of macroblock data - // ----------------------- - // | MB header | blockdata | - // ----------------------- - for (WebRtc_Word32 j = 0; j < _info.ptrNumOfMBs[numOfGOB]; j++) - { - // MB header - // ------------------------------------------------ - // | COD(inter) | MCBPC | CBPY | DQUANT |MVD(inter) | - // ------------------------------------------------ - WebRtc_Word32 codBit = 0; - if (_info.fType) - { - codBit = IsBitOne(_bitCnt); - _bitCnt++; - } - - if (codBit == 0) // if codBit == 1, no further info transmitted for this MB - { - WebRtc_Word32 mbType = 0; - char cbp[6]; - - // Get MB type & coded block pattern for chrominance (MCBPC) - WebRtc_Word32 size = FindMCBPC(mbType, cbp); - - _bitCnt += size; - - if (size == -1) - { - return -1; - } - - // Get coded block pattern for luminance (CBPY) - size = FindCBPY(mbType, cbp); - - _bitCnt += size; - - if (size == -1) - { - return -1; - } - - // Quantizer information (DQUANT), change in QUANT - if (mbType == 1 || mbType == 4) - { - _bitCnt += 2; - } - - // Get motion vector data (MVD) (inter-frames) - if (_info.fType && (mbType < 3 || mbType == 5)) - { - // Horizontal and vertical component - for (WebRtc_Word32 k = 0; k < 2; k++) - { - size = FindMVD(j, k, hmv1, vmv1); - - _bitCnt += size; - - if (size == -1) - { - return -1; - } - } - } - - // Block data. 1MB = 6 blocks (4 luminance blocks + 2 color blocks) - // ----------------- - // | intraDC | TCOEF | - // ----------------- - WebRtc_Word32 numOfBlocks = 6; - - WebRtc_Word32 WebRtc_Word32raDC = 0; - if (mbType == 3 || mbType == 4) - { - WebRtc_Word32raDC = 8; - } - - for (WebRtc_Word32 i = 0; i < numOfBlocks; i++) - { - // Get WebRtc_Word32raDC coefficient - _bitCnt += WebRtc_Word32raDC; - - // Get non-WebRtc_Word32RA dc coefficients - if (cbp[i]) // presence indicated by CBPY and CBPC - { - WebRtc_Word32 last = 0; - while (last == 0) - { - // Get transform coefficient (TCOEF) - size = FindTCOEF(last); - - _bitCnt += size; - - if (size == -1) - { - return -1; - } - } - } - } - // end of MB - sizeOfMBs[j] = _bitCnt - bitCntOffset; - } - else - { - // end of MB - sizeOfMBs[j] = _bitCnt - bitCntOffset; - } - } - // end of MBs - WebRtc_Word32 nextByte = _bitCnt >> 3; - WebRtc_Word32 nextByteRem = _bitCnt % 8; - if (nextByteRem) - { - nextByte++; - } - - // Check for next GOB/end of picture - if (numOfGOB < (_info.numOfGOBs - 1)) - { - // Test with GSTUF, GBSC (0000 0000 0000 0000 1) - if (_ptrData[nextByte] == 0 && - _ptrData[nextByte + 1] == 0 && - _ptrData[nextByte + 2] & 0x80) - { - sizeOfMBs[_info.ptrNumOfMBs[numOfGOB] - 1] = (nextByte << 3) - bitCntOffset; - return 1; // ok, next GOB - } - // ...without GSTUF - if (IsGBSC()) - { - return 1; // ok, next GOB - } - } - else if (numOfGOB == (_info.numOfGOBs - 1)) - { - // Picture end code may be included, (PSTUF (optional), EOS (22bits), ESTUF) - if (((WebRtc_UWord32) nextByte == payloadBytesToSend) || - ((WebRtc_UWord32)(nextByte + 2) == payloadBytesToSend) || - ((WebRtc_UWord32)(nextByte + 3) == payloadBytesToSend)) - { - sizeOfMBs[_info.ptrNumOfMBs[numOfGOB] - 1] = (payloadBytesToSend << 3) - bitCntOffset; - return 1; // ok, end of picture layer - } - } - else - { - return -1; - } - - return -1; -} - -WebRtc_Word32 -H263Information::FindMCBPC(WebRtc_Word32 &mbType, char *cbp) -{ - if (_info.fType == 0) // intra frame - { - ByteAlignData(MCBPC_I_MAX_CODE_LENGTH); - - WebRtc_Word32 i; - for (i = 0; i < MCBPC_I_TABLE_LENGTH - 1; i++) - { - if ((_dataShifted[0] & MCBPC_I_MASK[i][0]) == MCBPC_I_CODE[i][0]) - { - cbp[4] = MCBPC_I_CBPC[i][0]; - cbp[5] = MCBPC_I_CBPC[i][1]; - mbType = MCBPC_I_MBTYPE[i]; - - return MCBPC_I_SIZE[i]; - } - } - // last row - i = MCBPC_I_TABLE_LENGTH - 1; - if ((_dataShifted[0] & MCBPC_I_MASK[i][0]) == MCBPC_I_CODE[i][0] && - (_dataShifted[1] & MCBPC_I_MASK[i][1]) == MCBPC_I_CODE[i][1]) - { - cbp[4] = MCBPC_I_CBPC[i][0]; - cbp[5] = MCBPC_I_CBPC[i][1]; - mbType = MCBPC_I_MBTYPE[i]; - - return MCBPC_I_SIZE[i]; - } - - return -1; - } - else // inter frame - { - ByteAlignData(MCBPC_P_MAX_CODE_LENGTH); - - for (WebRtc_Word32 i = 0; i < MCBPC_P_TABLE_LENGTH; i++) - { - if ((_dataShifted[0] & MCBPC_P_MASK[i][0]) == MCBPC_P_CODE[i][0] && - (_dataShifted[1] & MCBPC_P_MASK[i][1]) == MCBPC_P_CODE[i][1]) - { - cbp[4] = MCBPC_P_CBPC[i][0]; - cbp[5] = MCBPC_P_CBPC[i][1]; - mbType = MCBPC_P_MBTYPE[i]; - - return MCBPC_P_SIZE[i]; - } - } - } - - return -1; -} - -WebRtc_Word32 -H263Information::FindCBPY(WebRtc_Word32 mbType, char *cbp) -{ - ByteAlignData(CBPY_MAX_CODE_LENGTH); - - for (WebRtc_Word32 i = 0; i < CBPY_TABLE_LENGTH; i++) - { - if ((_dataShifted[0] & CBPY_MASK[i][0]) == CBPY_CODE[i][0]) - { - cbp[0] = CBPY_CBPY[i][0]; - cbp[1] = CBPY_CBPY[i][1]; - cbp[2] = CBPY_CBPY[i][2]; - cbp[3] = CBPY_CBPY[i][3]; - - if (mbType == 0 || mbType == 1) - { - for (WebRtc_Word32 k = 0; k < 4; k++) - { - if (cbp[k]) - { - cbp[k] = 0; - } - else - { - cbp[k] = 1; - } - } - } - return CBPY_SIZE[i]; - } - } - - return -1; -} - -WebRtc_Word32 -H263Information::FindMVD(WebRtc_Word32 numOfMB, WebRtc_Word32 verORhor, WebRtc_UWord8 *hmv1, WebRtc_UWord8 *vmv1) -{ - ByteAlignData(MVD_MAX_CODE_LENGTH); - - for (WebRtc_Word32 i = 0; i < MVD_TABLE_LENGTH; i++) - { - if ((_dataShifted[0] & MVD_MASK[i][0]) == MVD_CODE[i][0] && - (_dataShifted[1] & MVD_MASK[i][1]) == MVD_CODE[i][1]) - { - // Store horizontal/vertical motion vector predictor (2's complement number) - WebRtc_Word32 index = i - 32; - if (index < 0) - { - index = ~ (-index-1); - } - - if (verORhor == 0) - { - hmv1[numOfMB] = static_cast(index); - - } else - { - vmv1[numOfMB] = static_cast(index); - } - - return MVD_SIZE[i]; - } - } - return -1; -} - -WebRtc_Word32 -H263Information::FindTCOEF(WebRtc_Word32 &last) -{ - ByteAlignData(TCOEF_MAX_CODE_LENGTH); - - for (WebRtc_Word32 i = 0; i < TCOEF_TABLE_LENGTH; i++) - { - if ((_dataShifted[0] & TCOEF_MASK[i][0]) == TCOEF_CODE[i][0] && - (_dataShifted[1] & TCOEF_MASK[i][1]) == TCOEF_CODE[i][1]) - { - last = TCOEF_LAST[i]; - if (i == (TCOEF_TABLE_LENGTH - 1)) - { - if (IsBitOne(_bitCnt + 7)) - { - last = 1; - } - return 22; - } - return TCOEF_SIZE[i]; - } - } - return -1; -} - -bool -H263Information::IsGBSC() -{ - // Group of block layer (GOB). - // Stuffing (GSTUF) (Variable length) consisting of less than 8 zero-bits. May be - // inserted by encoders so that the start of the GBSC is byte aligned. - // Group of block start code (GBSC) (17 bits). May be byte aligned. - // Group Number (GN) (5 bits). Group numbers 1-17 used for standard picture formats. - // -------------------------------- - // |GSTUF|00000000|00000000|1GGGGGxx| - // -------------------------------- - - ByteAlignData(3); - - if (_dataShifted[0] == 0 && _dataShifted[1] == 0 && (_dataShifted[2] & 0x80) == 0x80) - { - return true; - } - - return false; -} - -void -H263Information::FindGQUANT(WebRtc_Word32 numOfGOB) -{ - ByteAlignData(1); - - _info.ptrGQuant[numOfGOB] = (_dataShifted[0] >> 3) & 0x1F; -} - -WebRtc_UWord8 -H263Information::IsBitOne(const WebRtc_Word32 bitCnt) const -{ - WebRtc_Word32 curByte = bitCnt >> 3; - WebRtc_Word32 bit = bitCnt % 8; - - switch(bit) - { - case 0: - return _ptrData[curByte] & 0x80; - case 1: - return _ptrData[curByte] & 0x40; - case 2: - return _ptrData[curByte] & 0x20; - case 3: - return _ptrData[curByte] & 0x10; - case 4: - return _ptrData[curByte] & 0x08; - case 5: - return _ptrData[curByte] & 0x04; - case 6: - return _ptrData[curByte] & 0x02; - case 7: - return _ptrData[curByte] & 0x01; - default: - return 0; - } -} - -void -H263Information::ByteAlignData(WebRtc_Word32 numOfBytes) -{ - WebRtc_Word32 sByte = _bitCnt >> 3; - WebRtc_Word32 sBit = _bitCnt % 8; - - // Shift to byte align - WebRtc_Word32 i = 0; - do - { - _dataShifted[i] = _ptrData[sByte] << sBit; - _dataShifted[i++] += _ptrData[++sByte] >> (8 - sBit); - } while (i < numOfBytes); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/h263_information.h b/modules/rtp_rtcp/source/h263_information.h deleted file mode 100644 index 783978763..000000000 --- a/modules/rtp_rtcp/source/h263_information.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_H263_INFORMATION_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_H263_INFORMATION_H_ - -#include "typedefs.h" - -#include "video_codec_information.h" - -#define MAX_NUMBER_OF_H263_GOB 32 // 5 bits - -namespace webrtc { -class H263Info -{ -public: - H263Info() - : - uiH263PTypeFmt(0), - codecBits(0), - pQuant(0), - numOfGOBs(0), - totalNumOfMBs(0), - cpmBit(0), - fType(0) - { - memset(ptrGOBbuffer, 0, sizeof(ptrGOBbuffer)); - memset(ptrGOBbufferSBit, 0, sizeof(ptrGOBbufferSBit)); - memset(ptrGQuant, 0, sizeof(ptrGQuant)); - memset(ptrNumOfMBs, 0, sizeof(ptrNumOfMBs)); - memset(ptrGroupNum, 0, sizeof(ptrGroupNum)); - } - - WebRtc_Word32 CalculateMBOffset(const WebRtc_UWord8 numOfGOB) const; - - WebRtc_UWord8 uiH263PTypeFmt; // Defines frame size - WebRtc_UWord8 codecBits; - WebRtc_UWord8 pQuant; - WebRtc_UWord8 numOfGOBs; // Total number of GOBs of current frame - - WebRtc_UWord16 totalNumOfMBs; - WebRtc_UWord8 cpmBit; - WebRtc_UWord8 fType; // 0 - intra frame, 1 - inter frame - - WebRtc_UWord16 ptrNumOfMBs[MAX_NUMBER_OF_H263_GOB]; // Total number of MBs of current GOB - WebRtc_UWord32 ptrGOBbuffer[MAX_NUMBER_OF_H263_GOB]; // GOB buffer (start byte of GOBs) - WebRtc_UWord8 ptrGroupNum[MAX_NUMBER_OF_H263_GOB]; - WebRtc_UWord8 ptrGOBbufferSBit[MAX_NUMBER_OF_H263_GOB]; // sBit buffer (number of start bits to ignore for corresponding GOB) - WebRtc_UWord8 ptrGQuant[MAX_NUMBER_OF_H263_GOB]; // quantizer information for GOBs -}; - -struct H263MBInfo -{ - H263MBInfo() - : - bufferSize(0), - ptrBuffer(0), - ptrBufferHMV(0), - ptrBufferVMV(0) - { - } - - WebRtc_UWord32 bufferSize; // Size of MB buffer - WebRtc_UWord32* ptrBuffer; // MB buffer - WebRtc_UWord8* ptrBufferHMV; // Horizontal motion vector for corresponding MB - WebRtc_UWord8* ptrBufferVMV; // Vertical motion vector for corresponding MB -}; - -class H263Information : public VideoCodecInformation -{ -public: - H263Information(); - ~H263Information(); - - /******************************************************************************* - * void Reset(); - * - * Resets the members to zero. - * - */ - virtual void Reset(); - - virtual RtpVideoCodecTypes Type(); - - /******************************************************************************* - * WebRtc_Word32 GetInfo(WebRtc_UWord8* ptrEncodedBuffer, - * WebRtc_UWord32 length, - * const H263Info*& ptrInfo); - * - * Gets information from an encoded stream. - * - * Input: - * - ptrEncodedBuffer : PoWebRtc_Word32er to encoded stream. - * - length : Length in bytes of encoded stream. - * - * Output: - * - ptrInfo : PoWebRtc_Word32er to struct with H263 info. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ - virtual WebRtc_Word32 GetInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length, - const H263Info*& ptrInfo); - - /******************************************************************************* - * WebRtc_Word32 GetMBInfo(const WebRtc_UWord8* ptrEncodedBuffer, - * WebRtc_UWord32 length, - * WebRtc_Word32 numOfGOB, - * const H263MBInfo*& ptrInfoMB); - * - * Gets macroblock positions for a GOB. - * Also, the horizontal and vertical motion vector for each MB are returned. - * - * Input: - * - ptrEncodedBuffer : Pointer to encoded stream. - * - length : Length in bytes of encoded stream. - * - numOfGOB : Group number of current GOB. - * - * Output: - * - ptrInfoMB : Pointer to struct with MB positions in bits for a GOB. - * Horizontal and vertical motion vector for each MB. - * - * Return value: - * - 0 : ok - * - (-1) : Error - */ - WebRtc_Word32 GetMBInfo(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord32 length, - const WebRtc_UWord8 numOfGOB, - const H263MBInfo*& ptrInfoMB); - -protected: - bool HasInfo(const WebRtc_UWord32 length); - WebRtc_Word32 FindInfo(const WebRtc_UWord8* ptrEncodedBuffer, const WebRtc_UWord32 length); - - bool PictureStartCode(); - WebRtc_Word32 FindPTypeFMT(); - void FindFType(); - void FindCodecBits(); - void FindPQUANT(); - void FindCPMbit(); - WebRtc_Word32 SetNumOfMBs(); - - WebRtc_Word32 FindGOBs(const WebRtc_UWord32 length); - - // MB info - WebRtc_Word32 VerifyAndAllocateMB(); - bool HasMBInfo(const WebRtc_UWord8 numOfGOB); - WebRtc_Word32 FindMBs(const WebRtc_UWord8* ptrEncodedBuffer, - const WebRtc_UWord8 numOfGOB, - const WebRtc_UWord32 length); - - void FindGQUANT(WebRtc_Word32 numOfGOB); - WebRtc_Word32 FindMCBPC(WebRtc_Word32 &mbType, char *cbp); - WebRtc_Word32 FindCBPY(WebRtc_Word32 mbType, char *cbp); - WebRtc_Word32 FindMVD(WebRtc_Word32 numOfMB, WebRtc_Word32 verORhor, WebRtc_UWord8 *hmv1, WebRtc_UWord8 *vmv1); - WebRtc_Word32 FindTCOEF(WebRtc_Word32 &last); - bool IsGBSC(); - WebRtc_UWord8 IsBitOne(const WebRtc_Word32 bitCnt) const; - void ByteAlignData(WebRtc_Word32 numOfBytes); - void OutputBits(WebRtc_Word32 length); - -private: - WebRtc_Word32 _bitCnt; - const WebRtc_UWord8* _ptrData; - WebRtc_UWord8 _dataShifted[5]; - - H263Info _info; - H263MBInfo _infoMB; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_H263_INFORMATION_H_ diff --git a/modules/rtp_rtcp/source/overuse_detector.cc b/modules/rtp_rtcp/source/overuse_detector.cc deleted file mode 100644 index c9f042147..000000000 --- a/modules/rtp_rtcp/source/overuse_detector.cc +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tick_util.h" -#include "trace.h" -#include "overuse_detector.h" -#include "remote_rate_control.h" -#include -#include //abs - -#ifdef MATLAB -extern MatlabEngine eng; // global variable defined elsewhere -#endif - -#define INIT_CAPACITY_SLOPE 8.0/512.0 -#define DETECTOR_THRESHOLD 25.0 -#define OVER_USING_TIME_THRESHOLD 100 -#define MIN_FRAME_PERIOD_HISTORY_LEN 60 - -namespace webrtc { -OverUseDetector::OverUseDetector() -: -_firstPacket(true), -_currentFrame(), -_prevFrame(), -_numOfDeltas(0), -_slope(INIT_CAPACITY_SLOPE), -_offset(0), -_E(), -_processNoise(), -_avgNoise(0.0), -_varNoise(500), -_threshold(DETECTOR_THRESHOLD), -_tsDeltaHist(), -_prevOffset(0.0), -_timeOverUsing(-1), -_overUseCounter(0), -_hypothesis(kBwNormal) -#ifdef DEBUG_FILE -,_debugFile(NULL) -#endif -#ifdef MATLAB -,_plot1(NULL), -_plot2(NULL), -_plot3(NULL), -_plot4(NULL) -#endif -{ - _E[0][0] = 100; - _E[1][1] = 1e-1; - _E[0][1] = _E[1][0] = 0; - _processNoise[0] = 1e-10; - _processNoise[1] = 1e-2; -#ifdef DEBUG_FILE - _debugFile = fopen("detectorData.txt", "w"); - if (_debugFile) - fprintf(_debugFile, "data = [\n"); -#endif -} - -OverUseDetector::~OverUseDetector() -{ -#ifdef DEBUG_FILE - if (_debugFile) { - fprintf(_debugFile, "];\n"); - fclose(_debugFile); - } -#endif -#ifdef MATLAB - if (_plot1) - { - eng.DeletePlot(_plot1); - _plot1 = NULL; - } - if (_plot2) - { - eng.DeletePlot(_plot2); - _plot2 = NULL; - } - if (_plot3) - { - eng.DeletePlot(_plot3); - _plot3 = NULL; - } - if (_plot4) - { - eng.DeletePlot(_plot4); - _plot4 = NULL; - } -#endif - while (!_tsDeltaHist.Empty()) - { - ListItem* item = _tsDeltaHist.First(); - delete static_cast(item->GetItem()); - _tsDeltaHist.Erase(item); - } -} - -void OverUseDetector::Reset() -{ - _firstPacket = true; - _currentFrame._size = 0; - _currentFrame._completeTimeMs = -1; - _currentFrame._timestamp = -1; - _prevFrame._size = 0; - _prevFrame._completeTimeMs = -1; - _prevFrame._timestamp = -1; - _numOfDeltas = 0; - _slope = INIT_CAPACITY_SLOPE; - _offset = 0; - _E[0][0] = 100; - _E[1][1] = 1e-1; - _E[0][1] = _E[1][0] = 0; - _processNoise[0] = 1e-10; - _processNoise[1] = 1e-2; - _avgNoise = 0.0; - _varNoise = 500; - _threshold = DETECTOR_THRESHOLD; - _prevOffset = 0.0; - _timeOverUsing = -1; - _overUseCounter = 0; - _hypothesis = kBwNormal; - while (!_tsDeltaHist.Empty()) - { - ListItem* item = _tsDeltaHist.First(); - delete static_cast(item->GetItem()); - _tsDeltaHist.Erase(item); - } -} - -bool OverUseDetector::Update(const WebRtcRTPHeader& rtpHeader, const WebRtc_UWord16 packetSize) -{ -#ifdef MATLAB - // Create plots - const WebRtc_Word64 startTimeMs = TickTime::MillisecondTimestamp(); - if (_plot1 == NULL) - { - _plot1 = eng.NewPlot(new MatlabPlot()); - _plot1->AddLine(1000, "b.", "scatter"); - } - if (_plot2 == NULL) - { - _plot2 = eng.NewPlot(new MatlabPlot()); - _plot2->AddTimeLine(30, "b", "offset", startTimeMs); - _plot2->AddTimeLine(30, "r--", "limitPos", startTimeMs); - _plot2->AddTimeLine(30, "k.", "trigger", startTimeMs); - _plot2->AddTimeLine(30, "ko", "detection", startTimeMs); - //_plot2->AddTimeLine(30, "g", "slowMean", startTimeMs); - } - if (_plot3 == NULL) - { - _plot3 = eng.NewPlot(new MatlabPlot()); - _plot3->AddTimeLine(30, "b", "noiseVar", startTimeMs); - } - if (_plot4 == NULL) - { - _plot4 = eng.NewPlot(new MatlabPlot()); - //_plot4->AddTimeLine(60, "b", "p11", startTimeMs); - //_plot4->AddTimeLine(60, "r", "p12", startTimeMs); - _plot4->AddTimeLine(60, "g", "p22", startTimeMs); - //_plot4->AddTimeLine(60, "g--", "p22_hat", startTimeMs); - //_plot4->AddTimeLine(30, "b.-", "deltaFs", startTimeMs); - } - -#endif - - bool wrapped = false; - bool completeFrame = false; - const WebRtc_Word64 nowMs = TickTime::MillisecondTimestamp(); - if (_currentFrame._timestamp == -1) - { - _currentFrame._timestamp = rtpHeader.header.timestamp; - } - else if (OldTimestamp(rtpHeader.header.timestamp, static_cast(_currentFrame._timestamp), wrapped)) - { - // Don't update with old data - return completeFrame; - } - else if (rtpHeader.header.timestamp != _currentFrame._timestamp) - { - // First packet of a later frame, the previous frame sample is ready - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "Frame complete at %I64i", _currentFrame._completeTimeMs); - if (_prevFrame._completeTimeMs >= 0) // This is our second frame - { - WebRtc_Word64 tDelta = 0; - double tsDelta = 0; - // Check for wrap - OldTimestamp(static_cast(_prevFrame._timestamp), static_cast(_currentFrame._timestamp), wrapped); - CompensatedTimeDelta(_currentFrame, _prevFrame, tDelta, tsDelta, wrapped); - UpdateKalman(tDelta, tsDelta, _currentFrame._size, _prevFrame._size); - } - // The new timestamp is now the current frame, - // and the old timestamp becomes the previous frame. - _prevFrame = _currentFrame; - _currentFrame._timestamp = rtpHeader.header.timestamp; - _currentFrame._size = 0; - _currentFrame._completeTimeMs = -1; - completeFrame = true; - } - // Accumulate the frame size - _currentFrame._size += packetSize; - _currentFrame._completeTimeMs = nowMs; - return completeFrame; -} - -BandwidthUsage OverUseDetector::State() const -{ -#ifdef _DEBUG - char logStr[256]; - static BandwidthUsage oldState = kBwNormal; - if (_hypothesis != oldState) - { - switch(_hypothesis) - { - case kBwOverusing: - { -#ifdef _WIN32 - _snprintf(logStr,256, "State: OVER-USING\n"); -#else - snprintf(logStr,256, "State: OVER-USING\n"); -#endif - break; - } - case kBwUnderUsing: - { -#ifdef _WIN32 - _snprintf(logStr,256, "State: UNDER-USING\n"); -#else - snprintf(logStr,256, "State: UNDER-USING\n"); -#endif - break; - } - case kBwNormal: - { -#ifdef _WIN32 - _snprintf(logStr,256, "State: NORMAL\n"); -#else - snprintf(logStr,256, "State: NORMAL\n"); -#endif - break; - } - } -#ifdef _WIN32 - OutputDebugStringA(logStr); -#else - //TODO -#endif - oldState = _hypothesis; - } -#endif - return _hypothesis; -} - -double OverUseDetector::NoiseVar() const -{ - return _varNoise; -} - -void OverUseDetector::SetRateControlRegion(RateControlRegion region) -{ - switch (region) - { - case kRcMaxUnknown: - { - _threshold = DETECTOR_THRESHOLD; - break; - } - case kRcAboveMax: - case kRcNearMax: - { - _threshold = DETECTOR_THRESHOLD / 2; - break; - } - } -} - -void OverUseDetector::CompensatedTimeDelta(const FrameSample& currentFrame, const FrameSample& prevFrame, WebRtc_Word64& tDelta, - double& tsDelta, bool wrapped) -{ - _numOfDeltas++; - if (_numOfDeltas > 1000) - { - _numOfDeltas = 1000; - } - // Add wrap-around compensation - WebRtc_Word64 wrapCompensation = 0; - if (wrapped) - { - wrapCompensation = static_cast(1)<<32; - } - tsDelta = (currentFrame._timestamp + wrapCompensation - prevFrame._timestamp) / 90.0; - tDelta = currentFrame._completeTimeMs - prevFrame._completeTimeMs; - assert(tsDelta > 0); -} - -double OverUseDetector::CurrentDrift() -{ - return 1.0; -} - -void OverUseDetector::UpdateKalman(WebRtc_Word64 tDelta, double tsDelta, WebRtc_UWord32 frameSize, WebRtc_UWord32 prevFrameSize) -{ - const double minFramePeriod = UpdateMinFramePeriod(tsDelta); - const double drift = CurrentDrift(); - // Compensate for drift - const double tTsDelta = tDelta - tsDelta / drift; - double fsDelta = static_cast(frameSize) - prevFrameSize; - -#ifdef DEBUG_FILE - if (_debugFile) { - fprintf(_debugFile, "%I64i %f %I64i %lu;\n", tDelta, tsDelta, static_cast(fsDelta), frameSize); - fflush(_debugFile); - } -#endif - - // Update the Kalman filter - const double scaleFactor = minFramePeriod / (1000.0 / 30.0); - _E[0][0] += _processNoise[0] * scaleFactor; - _E[1][1] += _processNoise[1] * scaleFactor; - - if ((_hypothesis == kBwOverusing && _offset < _prevOffset) || - (_hypothesis == kBwUnderUsing && _offset > _prevOffset)) - { - _E[1][1] += 10 * _processNoise[1] * scaleFactor; - } - - const double h[2] = {fsDelta, 1.0}; - const double Eh[2] = {_E[0][0]*h[0] + _E[0][1]*h[1], - _E[1][0]*h[0] + _E[1][1]*h[1]}; - - const double residual = tTsDelta - _slope*h[0] - _offset; - - const bool stableState = (BWE_MIN(_numOfDeltas, 60) * abs(_offset) < _threshold); - // We try to filter out very late frames. For instance periodic key - // frames doesn't fit the Gaussian model well. - if (abs(residual) < 3 * sqrt(_varNoise)) - { - UpdateNoiseEstimate(residual, minFramePeriod, stableState); - } - else - { - UpdateNoiseEstimate(3 * sqrt(_varNoise), minFramePeriod, stableState); - } - - const double denom = _varNoise + h[0]*Eh[0] + h[1]*Eh[1]; - - const double K[2] = {Eh[0] / denom, - Eh[1] / denom}; - - const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]}, - {-K[1]*h[0], 1.0 - K[1]*h[1]}}; - const double e00 = _E[0][0]; - const double e01 = _E[0][1]; - - // Update state - _E[0][0] = e00 * IKh[0][0] + _E[1][0] * IKh[0][1]; - _E[0][1] = e01 * IKh[0][0] + _E[1][1] * IKh[0][1]; - _E[1][0] = e00 * IKh[1][0] + _E[1][0] * IKh[1][1]; - _E[1][1] = e01 * IKh[1][0] + _E[1][1] * IKh[1][1]; - - // Covariance matrix, must be positive semi-definite - assert(_E[0][0] + _E[1][1] >= 0 && - _E[0][0] * _E[1][1] - _E[0][1] * _E[1][0] >= 0 && - _E[0][0] >= 0); - -#ifdef MATLAB - //_plot4->Append("p11",_E[0][0]); - //_plot4->Append("p12",_E[0][1]); - _plot4->Append("p22",_E[1][1]); - //_plot4->Append("p22_hat", 0.5*(_processNoise[1] + - // sqrt(_processNoise[1]*(_processNoise[1] + 4*_varNoise)))); - //_plot4->Append("deltaFs", fsDelta); - _plot4->Plot(); -#endif - _slope = _slope + K[0] * residual; - _prevOffset = _offset; - _offset = _offset + K[1] * residual; - - Detect(tsDelta); - -#ifdef MATLAB - _plot1->Append("scatter", static_cast(_currentFrame._size) - _prevFrame._size, - static_cast(tDelta-tsDelta)); - _plot1->MakeTrend("scatter", "slope", _slope, _offset, "k-"); - _plot1->MakeTrend("scatter", "thresholdPos", _slope, _offset + 2 * sqrt(_varNoise), "r-"); - _plot1->MakeTrend("scatter", "thresholdNeg", _slope, _offset - 2 * sqrt(_varNoise), "r-"); - _plot1->Plot(); - - _plot2->Append("offset", _offset); - _plot2->Append("limitPos", _threshold/BWE_MIN(_numOfDeltas, 60)); - _plot2->Plot(); - - _plot3->Append("noiseVar", _varNoise); - _plot3->Plot(); -#endif -} - -double OverUseDetector::UpdateMinFramePeriod(double tsDelta) -{ - double minFramePeriod = tsDelta; - if (_tsDeltaHist.GetSize() >= MIN_FRAME_PERIOD_HISTORY_LEN) - { - ListItem* firstItem = _tsDeltaHist.First(); - delete static_cast(firstItem->GetItem()); - _tsDeltaHist.Erase(firstItem); - } - for (ListItem* item = _tsDeltaHist.First(); - item != NULL; - item = _tsDeltaHist.Next(item)) - { - const double* histDelta = static_cast(item->GetItem()); - minFramePeriod = BWE_MIN(*histDelta, minFramePeriod); - } - _tsDeltaHist.PushBack(new double(tsDelta)); - return minFramePeriod; -} - -void OverUseDetector::UpdateNoiseEstimate(double residual, double tsDelta, bool stableState) -{ - if (!stableState) - { - return; - } - // Faster filter during startup to faster adapt to the jitter level of the network - // alpha is tuned for 30 frames per second, but - double alpha = 0.01; - if (_numOfDeltas > 10*30) - { - alpha = 0.002; - } - // Only update the noise estimate if we're not over-using - // beta is a function of alpha and the time delta since - // the previous update. - const double beta = pow(1 - alpha, tsDelta * 30.0 / 1000.0); - _avgNoise = beta * _avgNoise + (1 - beta) * residual; - _varNoise = beta * _varNoise + (1 - beta) * (_avgNoise - residual) * (_avgNoise - residual); - if (_varNoise < 1e-7) - { - _varNoise = 1e-7; - } -} - -BandwidthUsage OverUseDetector::Detect(double tsDelta) -{ - if (_numOfDeltas < 2) - { - return kBwNormal; - } - const double T = BWE_MIN(_numOfDeltas, 60) * _offset; - if (abs(T) > _threshold) - { - if (_offset > 0) - { - if (_timeOverUsing == -1) - { - // Initialize the timer. Assume that we've been - // over-using half of the time since the previous - // sample. - _timeOverUsing = tsDelta / 2; - } - else - { - // Increment timer - _timeOverUsing += tsDelta; - } - _overUseCounter++; - if (_timeOverUsing > OVER_USING_TIME_THRESHOLD && _overUseCounter > 1) - { - if (_offset >= _prevOffset) - { -#ifdef _DEBUG - if (_hypothesis != kBwOverusing) - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwOverusing"); -#endif - _timeOverUsing = 0; - _overUseCounter = 0; - _hypothesis = kBwOverusing; -#ifdef MATLAB - _plot2->Append("detection",_offset); // plot it later -#endif - } - } -#ifdef MATLAB - _plot2->Append("trigger",_offset); // plot it later -#endif - } - else - { -#ifdef _DEBUG - if (_hypothesis != kBwUnderUsing) - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwUnderUsing"); -#endif - _timeOverUsing = -1; - _overUseCounter = 0; - _hypothesis = kBwUnderUsing; - } - } - else - { -#ifdef _DEBUG - if (_hypothesis != kBwNormal) - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: kBwNormal"); -#endif - _timeOverUsing = -1; - _overUseCounter = 0; - _hypothesis = kBwNormal; - } - return _hypothesis; -} - -bool OverUseDetector::OldTimestamp(WebRtc_UWord32 newTimestamp, WebRtc_UWord32 existingTimestamp, bool& wrapped) -{ - wrapped = (newTimestamp < 0x0000ffff && existingTimestamp > 0xffff0000) || - (newTimestamp > 0xffff0000 && existingTimestamp < 0x0000ffff); - if (existingTimestamp > newTimestamp && !wrapped) - { - return true; - } - else if (existingTimestamp <= newTimestamp && !wrapped) - { - return false; - } - else if (existingTimestamp < newTimestamp && wrapped) - { - return true; - } - else - { - return false; - } -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/overuse_detector.h b/modules/rtp_rtcp/source/overuse_detector.h deleted file mode 100644 index adc861f2b..000000000 --- a/modules/rtp_rtcp/source/overuse_detector.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ - -#include "bwe_defines.h" -#include "module_common_types.h" -#include "typedefs.h" -#include "list_wrapper.h" -#include - -//#define DEBUG_FILE - -#ifdef MATLAB -#include "../test/BWEStandAlone/MatlabPlot.h" -#endif - -namespace webrtc { -enum RateControlRegion; - -struct FrameSample -{ - FrameSample() : _size(0), _completeTimeMs(-1), _timestamp(-1) {} - - WebRtc_UWord32 _size; - WebRtc_Word64 _completeTimeMs; - WebRtc_Word64 _timestamp; -}; - -class OverUseDetector -{ -public: - OverUseDetector(); - ~OverUseDetector(); - bool Update(const WebRtcRTPHeader& rtpHeader, const WebRtc_UWord16 packetSize); - BandwidthUsage State() const; - void Reset(); - double NoiseVar() const; - void SetRateControlRegion(RateControlRegion region); - -private: - static bool OldTimestamp(WebRtc_UWord32 newTimestamp, WebRtc_UWord32 existingTimestamp, bool& wrapped); - void CompensatedTimeDelta(const FrameSample& currentFrame, const FrameSample& prevFrame, WebRtc_Word64& tDelta, - double& tsDelta, bool wrapped); - void UpdateKalman(WebRtc_Word64 tDelta, double tsDelta, WebRtc_UWord32 frameSize, WebRtc_UWord32 prevFrameSize); - double UpdateMinFramePeriod(double tsDelta); - void UpdateNoiseEstimate(double residual, double tsDelta, bool stableState); - BandwidthUsage Detect(double tsDelta); - double CurrentDrift(); - - bool _firstPacket; - FrameSample _currentFrame; - FrameSample _prevFrame; - - WebRtc_UWord16 _numOfDeltas; - double _slope; - double _offset; - double _E[2][2]; - double _processNoise[2]; - double _avgNoise; - double _varNoise; - double _varFsDelta; - double _threshold; - ListWrapper _tsDeltaHist; - - double _prevOffset; - double _timeOverUsing; - WebRtc_UWord16 _overUseCounter; - BandwidthUsage _hypothesis; - -#ifdef DEBUG_FILE - FILE* _debugFile; -#endif -#ifdef MATLAB - MatlabPlot *_plot1; - MatlabPlot *_plot2; - MatlabPlot *_plot3; - MatlabPlot *_plot4; -#endif -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ diff --git a/modules/rtp_rtcp/source/receiver_fec.cc b/modules/rtp_rtcp/source/receiver_fec.cc deleted file mode 100644 index 227827824..000000000 --- a/modules/rtp_rtcp/source/receiver_fec.cc +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "receiver_fec.h" -#include "rtp_receiver_video.h" -#include "forward_error_correction.h" -#include "rtp_utility.h" - -// RFC 5109 -namespace webrtc { -ReceiverFEC::ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner) : - _owner(owner), - _fec(new ForwardErrorCorrection(id)), - _payloadTypeFEC(-1), - _lastFECSeqNum(0), - _frameComplete(true) -{ -} - -ReceiverFEC::~ReceiverFEC() -{ - // Clean up DecodeFEC() - while (_receivedPacketList.First() != NULL) - { - ForwardErrorCorrection::ReceivedPacket* receivedPacket = - static_cast( - _receivedPacketList.First()->GetItem()); - delete receivedPacket->pkt; - delete receivedPacket; - receivedPacket = NULL; - _receivedPacketList.PopFront(); - } - assert(_receivedPacketList.Empty()); - - if (_fec != NULL) - { - bool frameComplete = true; - _fec->DecodeFEC(_receivedPacketList, _recoveredPacketList,_lastFECSeqNum, frameComplete); - delete _fec; - } -} - -void -ReceiverFEC::SetPayloadTypeFEC(const WebRtc_Word8 payloadType) -{ - _payloadTypeFEC = payloadType; -} - -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |F| block PT | timestamp offset | block length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -RFC 2198 RTP Payload for Redundant Audio Data September 1997 - - The bits in the header are specified as follows: - - F: 1 bit First bit in header indicates whether another header block - follows. If 1 further header blocks follow, if 0 this is the - last header block. - If 0 there is only 1 byte RED header - - block PT: 7 bits RTP payload type for this block. - - timestamp offset: 14 bits Unsigned offset of timestamp of this block - relative to timestamp given in RTP header. The use of an unsigned - offset implies that redundant data must be sent after the primary - data, and is hence a time to be subtracted from the current - timestamp to determine the timestamp of the data for which this - block is the redundancy. - - block length: 10 bits Length in bytes of the corresponding data - block excluding header. -*/ - -WebRtc_Word32 -ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 payloadDataLength, - bool& FECpacket ) -{ - if (_payloadTypeFEC == -1) - { - return -1; - } - - WebRtc_UWord8 REDHeaderLength = 1; - - // Add to list without RED header, aka a virtual RTP packet - // we remove the RED header - - ForwardErrorCorrection::ReceivedPacket* receivedPacket = new ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = new ForwardErrorCorrection::Packet; - - // get payload type from RED header - WebRtc_UWord8 payloadType = incomingRtpPacket[rtpHeader->header.headerLength] & 0x7f; - - // use the payloadType to decide if it's FEC or coded data - if(_payloadTypeFEC == payloadType) - { - receivedPacket->isFec = true; - FECpacket = true; - } else - { - receivedPacket->isFec = false; - FECpacket = false; - } - receivedPacket->lastMediaPktInFrame = rtpHeader->header.markerBit; - receivedPacket->seqNum = rtpHeader->header.sequenceNumber; - - WebRtc_UWord16 blockLength = 0; - if(incomingRtpPacket[rtpHeader->header.headerLength] & 0x80) - { - // f bit set in RED header - REDHeaderLength = 4; - WebRtc_UWord16 timestampOffset = (incomingRtpPacket[rtpHeader->header.headerLength + 1]) << 8; - timestampOffset += incomingRtpPacket[rtpHeader->header.headerLength+2]; - timestampOffset = timestampOffset >> 2; - if(timestampOffset != 0) - { - // sanity timestampOffset must be 0 - assert(false); - return -1; - } - blockLength = (0x03 & incomingRtpPacket[rtpHeader->header.headerLength + 2]) << 8; - blockLength += (incomingRtpPacket[rtpHeader->header.headerLength + 3]); - - // check next RED header - if(incomingRtpPacket[rtpHeader->header.headerLength+4] & 0x80) - { - // more than 2 blocks in packet not supported - assert(false); - return -1; - } - if(blockLength > payloadDataLength - REDHeaderLength) - { - // block length longer than packet - assert(false); - return -1; - } - } - - ForwardErrorCorrection::ReceivedPacket* secondReceivedPacket = NULL; - if(blockLength > 0) - { - // handle block length, split into 2 packets - REDHeaderLength = 5; - - // copy the RTP header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket, - rtpHeader->header.headerLength); - - // replace the RED payload type - receivedPacket->pkt->data[1] &= 0x80; // reset the payload - receivedPacket->pkt->data[1] += payloadType; // set the media payload type - - // copy the payload data - memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - blockLength); - - receivedPacket->pkt->length = blockLength; - - secondReceivedPacket = new ForwardErrorCorrection::ReceivedPacket; - secondReceivedPacket->pkt = new ForwardErrorCorrection::Packet; - - secondReceivedPacket->isFec = true; - secondReceivedPacket->lastMediaPktInFrame = false; - secondReceivedPacket->seqNum = rtpHeader->header.sequenceNumber; - - // copy the FEC payload data - memcpy(secondReceivedPacket->pkt->data, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength + - blockLength, payloadDataLength - REDHeaderLength - blockLength); - - secondReceivedPacket->pkt->length = payloadDataLength - REDHeaderLength - - blockLength; - - } else if(receivedPacket->isFec) - { - // everything behind the RED header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - payloadDataLength - REDHeaderLength); - receivedPacket->pkt->length = payloadDataLength - REDHeaderLength; - receivedPacket->ssrc = ModuleRTPUtility::BufferToUWord32(&incomingRtpPacket[8]); - - }else - { - // copy the RTP header - memcpy(receivedPacket->pkt->data, - incomingRtpPacket, - rtpHeader->header.headerLength); - - // replace the RED payload type - receivedPacket->pkt->data[1] &= 0x80; // reset the payload - receivedPacket->pkt->data[1] += payloadType; // set the media payload type - - // copy the media payload data - memcpy(receivedPacket->pkt->data + rtpHeader->header.headerLength, - incomingRtpPacket + rtpHeader->header.headerLength + REDHeaderLength, - payloadDataLength - REDHeaderLength); - - receivedPacket->pkt->length = rtpHeader->header.headerLength + - payloadDataLength - REDHeaderLength; - } - - if(receivedPacket->isFec) - { - AddReceivedFECInfo(rtpHeader, NULL, FECpacket); - } - - if(receivedPacket->pkt->length == 0) - { - delete receivedPacket->pkt; - delete receivedPacket; - return 0; - } - _receivedPacketList.PushBack(receivedPacket); - if (secondReceivedPacket) - { - _receivedPacketList.PushBack(secondReceivedPacket); - } - return 0; -} - -void -ReceiverFEC::AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - bool& FECpacket) -{ - // store the highest FEC seq num received - if(_lastFECSeqNum >= rtpHeader->header.sequenceNumber) - { - if(_lastFECSeqNum > 0xff00 && rtpHeader->header.sequenceNumber < 0x0ff ) //detect wrap around - { - // wrap - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } else - { - // old seqNum - } - }else - { - // check for a wrap - if(rtpHeader->header.sequenceNumber > 0xff00 && _lastFECSeqNum < 0x0ff ) //detect wrap around - { - // old seqNum - }else - { - _lastFECSeqNum = rtpHeader->header.sequenceNumber; - } - } - - if(incomingRtpPacket) - { - // get payload type from RED header - WebRtc_UWord8 payloadType = incomingRtpPacket[rtpHeader->header.headerLength] & 0x7f; - - // use the payloadType to decide if it's FEC or coded data - if(_payloadTypeFEC == payloadType) - { - FECpacket = true; - } else - { - FECpacket = false; - } - } -} - -WebRtc_Word32 -ReceiverFEC::ProcessReceivedFEC(const bool forceFrameDecode) -{ - if (!_receivedPacketList.Empty()) - { - if (_fec->DecodeFEC(_receivedPacketList, - _recoveredPacketList, - _lastFECSeqNum, - _frameComplete) != 0) - { - return -1; - } - assert(_receivedPacketList.Empty()); - } - if (forceFrameDecode) - { - _frameComplete = true; - } - if (_frameComplete) - { - while (_recoveredPacketList.First() != NULL) - { - ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - static_cast(_recoveredPacketList.First()->GetItem()); - - WebRtcRTPHeader rtpHeader; - memset(&rtpHeader, 0, sizeof(rtpHeader)); - - ModuleRTPUtility::RTPHeaderParser rtpHeaderParser(recoveredPacket->pkt->data, - recoveredPacket->pkt->length); - - if (!rtpHeaderParser.Parse(rtpHeader)) - { - return -1; - } - if (_owner->ReceiveRecoveredPacketCallback(&rtpHeader, - &recoveredPacket->pkt->data[rtpHeader.header.headerLength], - recoveredPacket->pkt->length - rtpHeader.header.headerLength) != 0) - { - return -1; - } - - delete recoveredPacket->pkt; - delete recoveredPacket; - recoveredPacket = NULL; - _recoveredPacketList.PopFront(); - } - assert(_recoveredPacketList.Empty()); - } - - return 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/receiver_fec.h b/modules/rtp_rtcp/source/receiver_fec.h deleted file mode 100644 index e680cfee4..000000000 --- a/modules/rtp_rtcp/source/receiver_fec.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ - -#include "rtp_rtcp_defines.h" - -#include "typedefs.h" -#include "list_wrapper.h" - -namespace webrtc { -class ForwardErrorCorrection; -class RTPReceiverVideo; - -class ReceiverFEC -{ -public: - ReceiverFEC(const WebRtc_Word32 id, RTPReceiverVideo* owner); - virtual ~ReceiverFEC(); - - WebRtc_Word32 AddReceivedFECPacket(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 payloadDataLength, - bool& FECpacket); - - void AddReceivedFECInfo(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - bool& FECpacket); - - WebRtc_Word32 ProcessReceivedFEC(const bool forceFrameDecode); - - void SetPayloadTypeFEC(const WebRtc_Word8 payloadType); - -private: - RTPReceiverVideo* _owner; - ForwardErrorCorrection* _fec; - ListWrapper _receivedPacketList; - ListWrapper _recoveredPacketList; - WebRtc_Word8 _payloadTypeFEC; - WebRtc_UWord16 _lastFECSeqNum; - bool _frameComplete; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_ diff --git a/modules/rtp_rtcp/source/remote_rate_control.cc b/modules/rtp_rtcp/source/remote_rate_control.cc deleted file mode 100644 index 3c72fd73f..000000000 --- a/modules/rtp_rtcp/source/remote_rate_control.cc +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "remote_rate_control.h" -#include "tick_util.h" -#include "trace.h" -#include -#include - -#ifdef MATLAB -extern MatlabEngine eng; // global variable defined elsewhere -#endif - -namespace webrtc { -RemoteRateControl::RemoteRateControl() -: -_minConfiguredBitRate(30000), -_maxConfiguredBitRate(30000000), -_currentBitRate(_maxConfiguredBitRate), -_maxHoldRate(0), -_avgMaxBitRate(-1.0f), -_varMaxBitRate(0.4f), -_rcState(kRcHold), -_cameFromState(kRcDecrease), -_rcRegion(kRcMaxUnknown), -_lastBitRateChange(-1), -_currentInput(kBwNormal, 0, 1.0), -_updated(false), -_timeFirstIncomingEstimate(-1), -_initializedBitRate(false), -_avgChangePeriod(1000.0f), -_lastChangeMs(-1), -_beta(0.9f) -#ifdef DEBUG_DELAY_SAMPLES -,_delayFile(NULL) -#endif -#ifdef MATLAB -,_plot1(NULL), -_plot2(NULL) -#endif -{ -#ifdef DEBUG_DELAY_SAMPLES - _delayFile = fopen("delaySamples.m", "w"); - fprintf(_delayFile, "delays=[\n"); -#endif -} - -RemoteRateControl::~RemoteRateControl() -{ -#ifdef DEBUG_DELAY_SAMPLES - fprintf(_delayFile, "];"); - fflush(_delayFile); - fclose(_delayFile); -#endif -#ifdef MATLAB - eng.DeletePlot(_plot1); - eng.DeletePlot(_plot2); -#endif -} - -void RemoteRateControl::Reset() -{ - _minConfiguredBitRate = 30000; - _maxConfiguredBitRate = 30000000; - _currentBitRate = _maxConfiguredBitRate; - _maxHoldRate = 0; - _avgMaxBitRate = -1.0f; - _varMaxBitRate = 0.4f; - _rcState = kRcHold; - _cameFromState = kRcHold; - _rcRegion = kRcMaxUnknown; - _lastBitRateChange = -1; - _avgChangePeriod = 1000.0f; - _lastChangeMs = -1; - _beta = 0.9f; - _currentInput._bwState = kBwNormal; - _currentInput._incomingBitRate = 0; - _currentInput._noiseVar = 1.0; - _updated = false; - _timeFirstIncomingEstimate = -1; - _initializedBitRate = false; -} - -WebRtc_Word32 RemoteRateControl::SetConfiguredBitRates(WebRtc_UWord32 minBitRateBps, WebRtc_UWord32 maxBitRateBps) -{ - if (minBitRateBps > maxBitRateBps) - { - return -1; - } - _minConfiguredBitRate = minBitRateBps; - _maxConfiguredBitRate = maxBitRateBps; - _currentBitRate = BWE_MIN(BWE_MAX(minBitRateBps, _currentBitRate), maxBitRateBps); - return 0; -} - -WebRtc_UWord32 RemoteRateControl::TargetBitRate(WebRtc_UWord32 RTT) -{ - _currentBitRate = ChangeBitRate(_currentBitRate, _currentInput._incomingBitRate, - _currentInput._noiseVar, RTT); - return _currentBitRate; -} - -RateControlRegion RemoteRateControl::Update(const RateControlInput& input, bool& firstOverUse) -{ -#ifdef MATLAB - // Create plots - if (_plot1 == NULL) - { - _plot1 = eng.NewPlot(new MatlabPlot()); - - _plot1->AddTimeLine(30, "b", "current"); - _plot1->AddTimeLine(30, "r-", "avgMax"); - _plot1->AddTimeLine(30, "r--", "pStdMax"); - _plot1->AddTimeLine(30, "r--", "nStdMax"); - _plot1->AddTimeLine(30, "r+", "max"); - _plot1->AddTimeLine(30, "g", "incoming"); - _plot1->AddTimeLine(30, "b+", "recovery"); - } - if (_plot2 == NULL) - { - _plot2 = eng.NewPlot(new MatlabPlot()); - - _plot2->AddTimeLine(30, "b", "alpha"); - } -#endif - - firstOverUse = (_currentInput._bwState != kBwOverusing && - input._bwState == kBwOverusing); - - // Set the initial bit rate value to what we're receiving the first second - if (!_initializedBitRate) - { - if (_timeFirstIncomingEstimate < 0) - { - if (input._incomingBitRate > 0) - { - _timeFirstIncomingEstimate = TickTime::MillisecondTimestamp(); - } - } - else if (TickTime::MillisecondTimestamp() - _timeFirstIncomingEstimate > 1000 && - input._incomingBitRate > 0) - { - _currentBitRate = input._incomingBitRate; - _initializedBitRate = true; - } - } - - if (_updated && _currentInput._bwState == kBwOverusing) - { - // Only update delay factor and incoming bit rate. We always want to react on an over-use. - _currentInput._noiseVar = input._noiseVar; - _currentInput._incomingBitRate = input._incomingBitRate; - return _rcRegion; - } - _updated = true; - _currentInput = input; - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: Incoming rate = %u kbps", input._incomingBitRate/1000); - return _rcRegion; -} - -WebRtc_UWord32 RemoteRateControl::ChangeBitRate(WebRtc_UWord32 currentBitRate, - WebRtc_UWord32 incomingBitRate, double noiseVar, WebRtc_UWord32 RTT) -{ - const WebRtc_Word64 now = TickTime::MillisecondTimestamp(); - if (!_updated) - { - return _currentBitRate; - } - _updated = false; - UpdateChangePeriod(now); - ChangeState(_currentInput, now); - // calculated here because it's used in multiple places - const float incomingBitRateKbps = incomingBitRate / 1000.0f; - // Calculate the max bit rate std dev given the normalized - // variance and the current incoming bit rate. - const float stdMaxBitRate = sqrt(_varMaxBitRate * _avgMaxBitRate); - bool recovery = false; - switch (_rcState) - { - case kRcHold: - { - _maxHoldRate = BWE_MAX(_maxHoldRate, incomingBitRate); - break; - } - case kRcIncrease: - { - if (_avgMaxBitRate >= 0) - { - if (incomingBitRateKbps > _avgMaxBitRate + 3 * stdMaxBitRate) - { - ChangeRegion(kRcMaxUnknown); - _avgMaxBitRate = -1.0; - } - else if (incomingBitRateKbps > _avgMaxBitRate + 2.5 * stdMaxBitRate) - { - ChangeRegion(kRcAboveMax); - } - } -#ifdef _DEBUG - char logStr[256]; -#ifdef _WIN32 - _snprintf(logStr,256, "Response time: %f + %i + 10*33\n", _avgChangePeriod, RTT); - OutputDebugStringA(logStr); -#else - snprintf(logStr,256, "Response time: %f + %i + 10*33\n", _avgChangePeriod, RTT); - //TODO -#endif -#endif - const WebRtc_UWord32 responseTime = static_cast(_avgChangePeriod + 0.5f) + RTT + 300; - double alpha = RateIncreaseFactor(now, _lastBitRateChange, responseTime, noiseVar); - - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: _avgChangePeriod = %f ms; RTT = %u ms", _avgChangePeriod, RTT); - - currentBitRate = static_cast(currentBitRate * alpha) + 1000; - if (_maxHoldRate > 0 && _beta * _maxHoldRate > currentBitRate) - { - currentBitRate = static_cast(_beta * _maxHoldRate); - _avgMaxBitRate = _beta * _maxHoldRate / 1000.0f; - ChangeRegion(kRcNearMax); - recovery = true; -#ifdef MATLAB - _plot1->Append("recovery", _maxHoldRate/1000); -#endif - } - _maxHoldRate = 0; - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: Increase rate to currentBitRate = %u kbps", currentBitRate/1000); -#ifdef _DEBUG - //char logStr[256]; -#ifdef _WIN32 - _snprintf(logStr,256, "New bitRate: %lu\n", currentBitRate / 1000); - OutputDebugStringA(logStr); -#else - snprintf(logStr,256, "New bitRate: %lu\n", currentBitRate / 1000); - //TODO -#endif -#endif - _lastBitRateChange = now; - break; - } - case kRcDecrease: - { - if (incomingBitRate < _minConfiguredBitRate) - { - currentBitRate = _minConfiguredBitRate; - } - else - { - // Set bit rate to something slightly lower than max - // to get rid of any self-induced delay. - currentBitRate = static_cast(_beta * incomingBitRate + 0.5); - if (currentBitRate > _currentBitRate) - { - // Avoid increasing the rate when over-using. - if (_rcRegion != kRcMaxUnknown) - { - currentBitRate = static_cast(_beta * _avgMaxBitRate * 1000 + 0.5f); - } - currentBitRate = BWE_MIN(currentBitRate, _currentBitRate); - } - ChangeRegion(kRcNearMax); - - if (incomingBitRateKbps < _avgMaxBitRate - 3 * stdMaxBitRate) - { - _avgMaxBitRate = -1.0f; - } - - UpdateMaxBitRateEstimate(incomingBitRateKbps); - -#ifdef MATLAB - _plot1->Append("max", incomingBitRateKbps); -#endif - - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: Decrease rate to currentBitRate = %u kbps", currentBitRate/1000); - } - // Stay on hold until the pipes are cleared. - ChangeState(kRcHold); - _lastBitRateChange = now; - break; - } - } - if (!recovery && (incomingBitRate > 100000 || currentBitRate > 150000) && - currentBitRate > 1.5 * incomingBitRate) - { - // Allow changing the bit rate if we are operating at very low rates - // Don't change the bit rate if the send side is too far off - currentBitRate = _currentBitRate; - _lastBitRateChange = now; - } -#ifdef MATLAB - if (_avgMaxBitRate >= 0.0f) - { - _plot1->Append("avgMax", _avgMaxBitRate); - _plot1->Append("pStdMax", _avgMaxBitRate + 3*stdMaxBitRate); - _plot1->Append("nStdMax", _avgMaxBitRate - 3*stdMaxBitRate); - } - _plot1->Append("incoming", incomingBitRate/1000); - _plot1->Append("current", currentBitRate/1000); - _plot1->Plot(); -#endif - return currentBitRate; -} - -double RemoteRateControl::RateIncreaseFactor(WebRtc_Word64 nowMs, WebRtc_Word64 lastMs, WebRtc_UWord32 reactionTimeMs, double noiseVar) const -{ - // alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2)))) - // Parameters - const double B = 0.0407; - const double b = 0.0025; - const double c1 = -6700.0 / (33 * 33); - const double c2 = 800.0; - const double d = 0.85; - - double alpha = 1.001 + B / (1 + exp( b * (d * reactionTimeMs - (c1 * noiseVar + c2)))); - - if (alpha < 1.001) - { - alpha = 1.001; - } - else if (alpha > 1.3) - { - alpha = 1.3; - } - - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, - "BWE: alpha = %f", alpha); -#ifdef MATLAB - _plot2->Append("alpha", alpha); - _plot2->Plot(); -#endif - - if (lastMs > -1) - { - alpha = pow(alpha, (nowMs - lastMs) / 1000.0); - } - - if (_rcRegion == kRcNearMax) - { - // We're close to our previous maximum. Try to stabilize the - // bit rate in this region, by increasing in smaller steps. - alpha = alpha - (alpha - 1.0) / 2.0; - } - else if (_rcRegion == kRcMaxUnknown) - { - alpha = alpha + (alpha - 1.0) * 2.0; - } - - return alpha; -} - -void RemoteRateControl::UpdateChangePeriod(WebRtc_Word64 nowMs) -{ - WebRtc_Word64 changePeriod = 0; - if (_lastChangeMs > -1) - { - changePeriod = nowMs - _lastChangeMs; - } - _lastChangeMs = nowMs; - _avgChangePeriod = 0.9f * _avgChangePeriod + 0.1f * changePeriod; -} - -void RemoteRateControl::UpdateMaxBitRateEstimate(float incomingBitRateKbps) -{ - const float alpha = 0.05f; - if (_avgMaxBitRate == -1.0f) - { - _avgMaxBitRate = incomingBitRateKbps; - } - else - { - _avgMaxBitRate = (1 - alpha) * _avgMaxBitRate + - alpha * incomingBitRateKbps; - } - // Estimate the max bit rate variance and normalize the variance - // with the average max bit rate. - const float norm = BWE_MAX(_avgMaxBitRate, 1.0f); - _varMaxBitRate = (1 - alpha) * _varMaxBitRate + - alpha * (_avgMaxBitRate - incomingBitRateKbps) * - (_avgMaxBitRate - incomingBitRateKbps) / - norm; - // 0.4 ~= 14 kbit/s at 500 kbit/s - if (_varMaxBitRate < 0.4f) - { - _varMaxBitRate = 0.4f; - } - // 2.5f ~= 35 kbit/s at 500 kbit/s - if (_varMaxBitRate > 2.5f) - { - _varMaxBitRate = 2.5f; - } -} - -void RemoteRateControl::ChangeState(const RateControlInput& input, WebRtc_Word64 nowMs) -{ - switch (_currentInput._bwState) - { - case kBwNormal: - { - if (_rcState == kRcHold) - { - _lastBitRateChange = nowMs; - ChangeState(kRcIncrease); - } - break; - } - case kBwOverusing: - { - if (_rcState != kRcDecrease) - { - ChangeState(kRcDecrease); - } - break; - } - case kBwUnderUsing: - { - ChangeState(kRcHold); - break; - } - } -} - -void RemoteRateControl::ChangeRegion(RateControlRegion region) -{ - _rcRegion = region; - switch (_rcRegion) - { - case kRcAboveMax: - case kRcMaxUnknown: - { - _beta = 0.9f; - break; - } - case kRcNearMax: - { - _beta = 0.95f; - break; - } - } -} - -void RemoteRateControl::ChangeState(RateControlState newState) -{ - _cameFromState = _rcState; - _rcState = newState; -#ifdef _DEBUG - char logStr[256]; - char state1[15]; - char state2[15]; - char state3[15]; - StateStr(_cameFromState, state1); - StateStr(_rcState, state2); - StateStr(_currentInput._bwState, state3); -#ifdef _WIN32 - _snprintf(logStr,256, "\t%s => %s due to %s\n", state1, state2, state3); - OutputDebugStringA(logStr); -#else - snprintf(logStr,256, "\t%s => %s due to %s\n", state1, state2, state3); - //TODO -#endif -#endif -} - -#ifdef _DEBUG -void RemoteRateControl::StateStr(RateControlState state, char* str) -{ - switch (state) - { - case kRcDecrease: - strncpy(str, "DECREASE", 8); - str[8] = 0; - break; - case kRcHold: - strncpy(str, "HOLD", 4); - str[4] = 0; - break; - case kRcIncrease: - strncpy(str, "INCREASE", 8); - str[8] = 0; - break; - } -} - -void RemoteRateControl::StateStr(BandwidthUsage state, char* str) -{ - switch (state) - { - case kBwNormal: - strncpy(str, "NORMAL", 6); - str[6] = 0; - break; - case kBwOverusing: - strncpy(str, "OVER USING", 10); - str[10] = 0; - break; - case kBwUnderUsing: - strncpy(str, "UNDER USING", 11); - str[11] = 0; - break; - } -} -#endif - -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/remote_rate_control.h b/modules/rtp_rtcp/source/remote_rate_control.h deleted file mode 100644 index f3bb44ecf..000000000 --- a/modules/rtp_rtcp/source/remote_rate_control.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ - -#include "bwe_defines.h" -#include "typedefs.h" -#include "list_wrapper.h" -#include - -#ifdef MATLAB -#include "../test/BWEStandAlone/MatlabPlot.h" -#endif - -namespace webrtc { -class RemoteRateControl -{ -public: - RemoteRateControl(); - ~RemoteRateControl(); - WebRtc_Word32 SetConfiguredBitRates(WebRtc_UWord32 minBitRate, WebRtc_UWord32 maxBitRate); - WebRtc_UWord32 TargetBitRate(WebRtc_UWord32 RTT); - RateControlRegion Update(const RateControlInput& input, bool& firstOverUse); - void Reset(); - -private: - WebRtc_UWord32 ChangeBitRate(WebRtc_UWord32 currentBitRate, - WebRtc_UWord32 incomingBitRate, double delayFactor, WebRtc_UWord32 RTT); - double RateIncreaseFactor(WebRtc_Word64 nowMs, WebRtc_Word64 lastMs, WebRtc_UWord32 reactionTimeMs, double noiseVar) const; - void UpdateChangePeriod(WebRtc_Word64 nowMs); - void UpdateMaxBitRateEstimate(float incomingBitRateKbps); - void ChangeState(const RateControlInput& input, WebRtc_Word64 nowMs); - void ChangeState(RateControlState newState); - void ChangeRegion(RateControlRegion region); -#ifdef _DEBUG - static void StateStr(RateControlState state, char* str); - static void StateStr(BandwidthUsage state, char* str); -#endif - - WebRtc_UWord32 _minConfiguredBitRate; - WebRtc_UWord32 _maxConfiguredBitRate; - WebRtc_UWord32 _currentBitRate; - WebRtc_UWord32 _maxHoldRate; - float _avgMaxBitRate; - float _varMaxBitRate; - RateControlState _rcState; - RateControlState _cameFromState; - RateControlRegion _rcRegion; - WebRtc_Word64 _lastBitRateChange; - RateControlInput _currentInput; - bool _updated; - WebRtc_Word64 _timeFirstIncomingEstimate; - bool _initializedBitRate; - - float _avgChangePeriod; - WebRtc_Word64 _lastChangeMs; - float _beta; -#ifdef DEBUG_DELAY_SAMPLES - FILE* _delayFile; -#endif -#ifdef MATLAB - MatlabPlot *_plot1; - MatlabPlot *_plot2; -#endif -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc deleted file mode 100644 index 6bad528b6..000000000 --- a/modules/rtp_rtcp/source/rtcp_receiver.cc +++ /dev/null @@ -1,1496 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtcp_receiver.h" -#include "rtcp_utility.h" - -#include //memset -#include //assert - -#include "trace.h" -#include "critical_section_wrapper.h" - -namespace -{ - const float FRAC = 4.294967296E9; -} - -namespace webrtc { -using namespace RTCPUtility; -using namespace RTCPHelp; - -RTCPReceiver::RTCPReceiver(const WebRtc_Word32 id, ModuleRtpRtcpPrivate& callback) : - _id(id), - _method(kRtcpOff), - _lastReceived(0), - _cbRtcpPrivate(callback), - _criticalSectionFeedbacks(*CriticalSectionWrapper::CreateCriticalSection()), - _cbRtcpFeedback(NULL), - _cbVideoFeedback(NULL), - _criticalSectionRTCPReceiver(*CriticalSectionWrapper::CreateCriticalSection()), - _SSRC(0), - _remoteSSRC(0), - _remoteSenderInfo(), - _lastReceivedSRNTPsecs(0), - _lastReceivedSRNTPfrac(0), - _receivedInfoMap(), - _packetTimeOutMS(0) -{ - memset(&_remoteSenderInfo, 0, sizeof(_remoteSenderInfo)); - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); -} - -RTCPReceiver::~RTCPReceiver() -{ - delete &_criticalSectionRTCPReceiver; - delete &_criticalSectionFeedbacks; - - bool loop = true; - do - { - MapItem* item = _receivedReportBlockMap.First(); - if(item) - { - // delete - RTCPReportBlockInformation* block= ((RTCPReportBlockInformation*)item->GetItem()); - delete block; - - // remove from map and delete Item - _receivedReportBlockMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - loop = true; - do - { - MapItem* item = _receivedInfoMap.First(); - if(item) - { - // delete - RTCPReceiveInformation* block= ((RTCPReceiveInformation*)item->GetItem()); - delete block; - - // remove from map and delete Item - _receivedInfoMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - loop = true; - do - { - MapItem* item = _receivedCnameMap.First(); - if(item) - { - // delete - RTCPCnameInformation* block= ((RTCPCnameInformation*)item->GetItem()); - delete block; - - // remove from map and delete Item - _receivedCnameMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); -} - -void -RTCPReceiver::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -RTCPMethod -RTCPReceiver::Status() const -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - return _method; -} - -WebRtc_Word32 -RTCPReceiver::SetRTCPStatus(const RTCPMethod method) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - _method = method; - return 0; -} - -WebRtc_UWord32 -RTCPReceiver::LastReceived() -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - return _lastReceived; -} - -WebRtc_Word32 -RTCPReceiver::SetRemoteSSRC( const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - // new SSRC reset old reports - memset(&_remoteSenderInfo, 0, sizeof(_remoteSenderInfo)); - _lastReceivedSRNTPsecs = 0; - _lastReceivedSRNTPfrac = 0; - - _remoteSSRC = ssrc; - return 0; -} - -WebRtc_Word32 -RTCPReceiver::RegisterIncomingRTCPCallback(RtcpFeedback* incomingMessagesCallback) -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - _cbRtcpFeedback = incomingMessagesCallback; - return 0; -} - -WebRtc_Word32 -RTCPReceiver::RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback) -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - _cbVideoFeedback = incomingMessagesCallback; - return 0; -} - -void -RTCPReceiver::SetSSRC( const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - _SSRC = ssrc; -} - -WebRtc_Word32 -RTCPReceiver::ResetRTT(const WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - RTCPReportBlockInformation* reportBlock = GetReportBlockInformation(remoteSSRC); - if(reportBlock == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tfailed to GetReportBlockInformation(%d)", remoteSSRC); - return -1; - } - reportBlock->RTT = 0; - reportBlock->avgRTT = 0; - reportBlock->minRTT = 0; - reportBlock->maxRTT = 0; - - return 0; -} - -WebRtc_Word32 -RTCPReceiver::RTT(const WebRtc_UWord32 remoteSSRC, - WebRtc_UWord16* RTT, - WebRtc_UWord16* avgRTT, - WebRtc_UWord16* minRTT, - WebRtc_UWord16* maxRTT) const - -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - RTCPReportBlockInformation* reportBlock = GetReportBlockInformation(remoteSSRC); - if(reportBlock == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tfailed to GetReportBlockInformation(%d)", remoteSSRC); - return -1; - } - if(RTT) - { - *RTT = reportBlock->RTT; - } - if(avgRTT) - { - *avgRTT = reportBlock->avgRTT; - } - if(minRTT) - { - *minRTT = reportBlock->minRTT; - } - if(maxRTT) - { - *maxRTT = reportBlock->maxRTT; - } - return 0; -} - -void -RTCPReceiver::UpdateLipSync(const WebRtc_Word32 audioVideoOffset) const -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - if(_cbRtcpFeedback) - { - _cbRtcpFeedback->OnLipSyncUpdate(_id,audioVideoOffset); - } -}; - -WebRtc_Word32 -RTCPReceiver::NTP(WebRtc_UWord32 *ReceivedNTPsecs, - WebRtc_UWord32 *ReceivedNTPfrac, - WebRtc_UWord32 *RTCPArrivalTimeSecs, - WebRtc_UWord32 *RTCPArrivalTimeFrac) const -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - if(ReceivedNTPsecs) - { - *ReceivedNTPsecs = _remoteSenderInfo.NTPseconds; // NTP from incoming SendReport - } - if(ReceivedNTPfrac) - { - *ReceivedNTPfrac = _remoteSenderInfo.NTPfraction; - } - if(RTCPArrivalTimeFrac) - { - *RTCPArrivalTimeFrac = _lastReceivedSRNTPfrac; // local NTP time when we received a RTCP packet with a send block - } - if(RTCPArrivalTimeSecs) - { - *RTCPArrivalTimeSecs = _lastReceivedSRNTPsecs; - } - return 0; -} - -WebRtc_Word32 -RTCPReceiver::SenderInfoReceived(RTCPSenderInfo* senderInfo) const -{ - if(senderInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - if(_lastReceivedSRNTPsecs == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s No received SR", __FUNCTION__); - return -1; - } - memcpy(senderInfo, &(_remoteSenderInfo), sizeof(RTCPSenderInfo)); - return 0; -} - -// statistics -// we can get multiple receive reports when we receive the report from a CE -WebRtc_Word32 -RTCPReceiver::StatisticsReceived(const WebRtc_UWord32 remoteSSRC, - RTCPReportBlock* receiveBlock) const -{ - if(receiveBlock == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - RTCPReportBlockInformation* reportBlockInfo = GetReportBlockInformation(remoteSSRC); - if(reportBlockInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tfailed to GetReportBlockInformation(%d)", remoteSSRC); - return -1; - } - memcpy(receiveBlock, &(reportBlockInfo->remoteReceiveBlock), sizeof(RTCPReportBlock)); - return 0; -} - -WebRtc_Word32 -RTCPReceiver::IncomingRTCPPacket(RTCPPacketInformation& rtcpPacketInformation, - RTCPUtility::RTCPParserV2* rtcpParser) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - _lastReceived = ModuleRTPUtility::GetTimeInMS(); - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser->Begin(); - while (pktType != RTCPUtility::kRtcpNotValidCode) - { - // Each "case" is responsible for iterate the parser to the - // next top level packet. - switch (pktType) - { - case RTCPUtility::kRtcpSrCode: - case RTCPUtility::kRtcpRrCode: - HandleSenderReceiverReport(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpSdesCode: - HandleSDES(*rtcpParser); - break; - case RTCPUtility::kRtcpXrVoipMetricCode: - HandleXRVOIPMetric(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpByeCode: - HandleBYE(*rtcpParser); - break; - case RTCPUtility::kRtcpRtpfbNackCode: - HandleNACK(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpRtpfbTmmbrCode: - HandleTMMBR(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpRtpfbTmmbnCode: - HandleTMMBN(*rtcpParser); - break; - case RTCPUtility::kRtcpRtpfbSrReqCode: - HandleSR_REQ(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpPsfbPliCode: - HandlePLI(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpPsfbSliCode: - HandleSLI(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpPsfbRpsiCode: - HandleRPSI(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpPsfbFirCode: - HandleFIR(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpAppCode: - // generic application messages - HandleAPP(*rtcpParser, rtcpPacketInformation); - break; - case RTCPUtility::kRtcpAppItemCode: - // generic application messages - HandleAPPItem(*rtcpParser, rtcpPacketInformation); - break; - default: - rtcpParser->Iterate(); - break; - } - pktType = rtcpParser->PacketType(); - } - return 0; -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSenderReceiverReport(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - RTCPUtility::RTCPPacketTypes rtcpPacketType = rtcpParser.PacketType(); - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - assert((rtcpPacketType == RTCPUtility::kRtcpRrCode) || (rtcpPacketType == RTCPUtility::kRtcpSrCode)); - - // SR.SenderSSRC - // The synchronization source identifier for the originator of this SR packet - - // rtcpPacket.RR.SenderSSRC - // The source of the packet sender, same as of SR? or is this a CE? - - const WebRtc_UWord32 remoteSSRC = (rtcpPacketType == RTCPUtility::kRtcpRrCode) ? rtcpPacket.RR.SenderSSRC:rtcpPacket.SR.SenderSSRC; - const WebRtc_UWord8 numberOfReportBlocks = (rtcpPacketType == RTCPUtility::kRtcpRrCode) ? rtcpPacket.RR.NumberOfReportBlocks:rtcpPacket.SR.NumberOfReportBlocks; - - rtcpPacketInformation.remoteSSRC = remoteSSRC; - - RTCPReceiveInformation* ptrReceiveInfo = CreateReceiveInformation(remoteSSRC); - if (!ptrReceiveInfo) - { - rtcpParser.Iterate(); - return; - } - - if (rtcpPacketType == RTCPUtility::kRtcpSrCode) - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, - "Received SR(%d). SSRC:0x%x, from SSRC:0x%x, to us %d.", _id, _SSRC, remoteSSRC, (_remoteSSRC == remoteSSRC)?1:0); - - if (_remoteSSRC == remoteSSRC) // have I received RTP packets from this party - { - // only signal that we have received a SR when we accept one - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpSr; - - // We will only store the send report from one source, but - // we will store all the receive block - - // Save the NTP time of this report - _remoteSenderInfo.NTPseconds = rtcpPacket.SR.NTPMostSignificant; - _remoteSenderInfo.NTPfraction = rtcpPacket.SR.NTPLeastSignificant; - _remoteSenderInfo.RTPtimeStamp = rtcpPacket.SR.RTPTimestamp; - _remoteSenderInfo.sendPacketCount = rtcpPacket.SR.SenderPacketCount; - _remoteSenderInfo.sendOctetCount = rtcpPacket.SR.SenderOctetCount; - - ModuleRTPUtility::CurrentNTP(_lastReceivedSRNTPsecs, _lastReceivedSRNTPfrac); - } - else - { - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpRr; - } - } else - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, - "Received RR(%d). SSRC:0x%x, from SSRC:0x%x", _id, _SSRC, remoteSSRC); - - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpRr; - } - UpdateReceiveInformation(*ptrReceiveInfo); - - rtcpPacketType = rtcpParser.Iterate(); - - while (rtcpPacketType == RTCPUtility::kRtcpReportBlockItemCode) - { - HandleReportBlock(rtcpPacket, rtcpPacketInformation, remoteSSRC, numberOfReportBlocks); - rtcpPacketType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleReportBlock(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPPacketInformation& rtcpPacketInformation, - const WebRtc_UWord32 remoteSSRC, - const WebRtc_UWord8 numberOfReportBlocks) -{ - // this will be called once per report block in the RTCP packet - // we store all incoming reports - // each packet has max 31 RR blocks - // - // we can calc RTT if we send a send report and get a report block back - - /* - rtcpPacket.ReportBlockItem.SSRC - The SSRC identifier of the source to which the information in this - reception report block pertains. - */ - - // if we receive a RTCP packet with multiple reportBlocks only store the ones to us - if( _SSRC && - numberOfReportBlocks > 1) - { - // we have more than one reportBlock in the RTCP packet - if(rtcpPacket.ReportBlockItem.SSRC != _SSRC) - { - // this block is not for us ignore it - return; - } - } - - _criticalSectionRTCPReceiver.Leave(); - // to avoid problem with accuireing _criticalSectionRTCPSender while holding _criticalSectionRTCPReceiver - - WebRtc_UWord32 sendTimeMS = _cbRtcpPrivate.SendTimeOfSendReport(rtcpPacket.ReportBlockItem.LastSR); - - _criticalSectionRTCPReceiver.Enter(); - - // ReportBlockItem.SSRC is who it's to - // we store all incoming reports, used in conference relay - - RTCPReportBlockInformation* reportBlock = CreateReportBlockInformation(remoteSSRC); - if(reportBlock == NULL) - { - return; - } - - reportBlock->remoteReceiveBlock.fractionLost = rtcpPacket.ReportBlockItem.FractionLost; - reportBlock->remoteReceiveBlock.cumulativeLost = rtcpPacket.ReportBlockItem.CumulativeNumOfPacketsLost; - reportBlock->remoteReceiveBlock.extendedHighSeqNum= rtcpPacket.ReportBlockItem.ExtendedHighestSequenceNumber; - reportBlock->remoteReceiveBlock.jitter = rtcpPacket.ReportBlockItem.Jitter; - reportBlock->remoteReceiveBlock.delaySinceLastSR = rtcpPacket.ReportBlockItem.DelayLastSR; - reportBlock->remoteReceiveBlock.lastSR = rtcpPacket.ReportBlockItem.LastSR; - - if(rtcpPacket.ReportBlockItem.Jitter > reportBlock->remoteMaxJitter) - { - reportBlock->remoteMaxJitter = rtcpPacket.ReportBlockItem.Jitter; - } - - WebRtc_UWord32 delaySinceLastSendReport = rtcpPacket.ReportBlockItem.DelayLastSR; - - // do we have a local SSRC - // keep track of our relayed SSRC too - if(_SSRC) - { - // we filter rtcpPacket.ReportBlockItem.SSRC to our SSRC - // hence only reports to us - if( rtcpPacket.ReportBlockItem.SSRC == _SSRC) - { - // local NTP time when we received this - WebRtc_UWord32 lastReceivedRRNTPsecs = 0; - WebRtc_UWord32 lastReceivedRRNTPfrac = 0; - - ModuleRTPUtility::CurrentNTP(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac); - - // time when we received this in MS - WebRtc_UWord32 receiveTimeMS = ModuleRTPUtility::ConvertNTPTimeToMS(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac); - - // Estimate RTT - WebRtc_UWord32 d =(delaySinceLastSendReport&0x0000ffff)*1000; - d /= 65536; - d+=((delaySinceLastSendReport&0xffff0000)>>16)*1000; - - WebRtc_Word32 RTT = 0; - - if(sendTimeMS > 0) - { - RTT = receiveTimeMS - d - sendTimeMS; - if( RTT <= 0) - { - RTT = 1; - } - if (RTT > reportBlock->maxRTT) - { - // store max RTT - reportBlock->maxRTT = (WebRtc_UWord16)RTT; - } - if(reportBlock->minRTT == 0) - { - // first RTT - reportBlock->minRTT = (WebRtc_UWord16)RTT; - }else if (RTT < reportBlock->minRTT) - { - // Store min RTT - reportBlock->minRTT = (WebRtc_UWord16)RTT; - } - // store last RTT - reportBlock->RTT = (WebRtc_UWord16)RTT; - - // store average RTT - if(reportBlock->numAverageCalcs != 0) - { - float ac = static_cast(reportBlock->numAverageCalcs); - float newAverage = ((ac / (ac + 1)) * reportBlock->avgRTT) + ((1 / (ac + 1)) * RTT); - reportBlock->avgRTT = static_cast(newAverage + 0.5f); - }else - { - // first RTT - reportBlock->avgRTT = (WebRtc_UWord16)RTT; - } - reportBlock->numAverageCalcs++; - } - - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, - " -> Received report block(%d), from SSRC:0x%x, RTT:%d, loss:%d", _id, remoteSSRC, RTT, rtcpPacket.ReportBlockItem.FractionLost); - - // rtcpPacketInformation - rtcpPacketInformation.AddReportInfo(reportBlock->remoteReceiveBlock.fractionLost, - (WebRtc_UWord16)RTT, - reportBlock->remoteReceiveBlock.extendedHighSeqNum, - reportBlock->remoteReceiveBlock.jitter); - } - } -} - -RTCPReportBlockInformation* -RTCPReceiver::CreateReportBlockInformation(WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - RTCPReportBlockInformation* ptrReportBlockInfo = NULL; - MapItem* ptrReportBlockInfoItem = _receivedReportBlockMap.Find(remoteSSRC); - if (ptrReportBlockInfoItem == NULL) - { - ptrReportBlockInfo = new RTCPReportBlockInformation; - _receivedReportBlockMap.Insert(remoteSSRC, ptrReportBlockInfo); - } else - { - ptrReportBlockInfo = static_cast(ptrReportBlockInfoItem->GetItem()); - } - return ptrReportBlockInfo; - -} - -RTCPReportBlockInformation* -RTCPReceiver::GetReportBlockInformation(WebRtc_UWord32 remoteSSRC) const -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* ptrReportBlockInfoItem = _receivedReportBlockMap.Find(remoteSSRC); - if (ptrReportBlockInfoItem == NULL) - { - return NULL; - } - return static_cast(ptrReportBlockInfoItem->GetItem()); -} - -RTCPCnameInformation* -RTCPReceiver::CreateCnameInformation(WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - RTCPCnameInformation* ptrCnameInfo = NULL; - MapItem* ptrCnameInfoItem = _receivedCnameMap.Find(remoteSSRC); - if (ptrCnameInfoItem == NULL) - { - ptrCnameInfo = new RTCPCnameInformation; - _receivedCnameMap.Insert(remoteSSRC, ptrCnameInfo); - } else - { - ptrCnameInfo = static_cast(ptrCnameInfoItem->GetItem()); - } - return ptrCnameInfo; -} - -RTCPCnameInformation* -RTCPReceiver::GetCnameInformation(WebRtc_UWord32 remoteSSRC) const -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* ptrCnameInfoItem = _receivedCnameMap.Find(remoteSSRC); - if (ptrCnameInfoItem == NULL) - { - return NULL; - } - return static_cast(ptrCnameInfoItem->GetItem()); -} - -RTCPReceiveInformation* -RTCPReceiver::CreateReceiveInformation(WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - RTCPReceiveInformation* ptrReceiveInfo = NULL; - MapItem* ptrReceiveInfoItem = _receivedInfoMap.Find(remoteSSRC); - if (ptrReceiveInfoItem == NULL) - { - ptrReceiveInfo = new RTCPReceiveInformation; - _receivedInfoMap.Insert(remoteSSRC, ptrReceiveInfo); - } else - { - ptrReceiveInfo = static_cast(ptrReceiveInfoItem->GetItem()); - } - return ptrReceiveInfo; -} - -RTCPReceiveInformation* -RTCPReceiver::GetReceiveInformation(WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* ptrReceiveInfoItem = _receivedInfoMap.Find(remoteSSRC); - if (ptrReceiveInfoItem == NULL) - { - return NULL; - } - return static_cast(ptrReceiveInfoItem->GetItem()); -} - -void -RTCPReceiver::UpdateReceiveInformation( RTCPReceiveInformation& receiveInformation) -{ - // Update that this remote is alive - receiveInformation.lastTimeReceived = ModuleRTPUtility::GetTimeInMS(); -} - -bool -RTCPReceiver::UpdateRTCPReceiveInformationTimers() -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - bool updateBoundingSet = false; - WebRtc_UWord32 timeNow = ModuleRTPUtility::GetTimeInMS(); - MapItem* receiveInfoItem=_receivedInfoMap.First(); - - while(receiveInfoItem) - { - RTCPReceiveInformation* receiveInfo = (RTCPReceiveInformation*)receiveInfoItem->GetItem(); - if(receiveInfo == NULL) - { - return updateBoundingSet; - } - // time since last received rtcp packet - // when we dont have a lastTimeReceived and the object is marked readyForDelete - // it's removed from the map - if( receiveInfo->lastTimeReceived) - { - if((timeNow - receiveInfo->lastTimeReceived) > 5*RTCP_INTERVAL_AUDIO_MS) // use audio define since we don't know what interval the remote peer is using - { - // no rtcp packet for the last five regular intervals, reset limitations - receiveInfo->TmmbrSet.lengthOfSet = 0; - receiveInfo->lastTimeReceived = 0; // prevent that we call this over and over again - updateBoundingSet = true; // send new TMMBN to all channels using the default codec - } - receiveInfoItem = _receivedInfoMap.Next(receiveInfoItem); - }else - { - if(receiveInfo->readyForDelete) - { - // store our current receiveInfoItem - MapItem* receiveInfoItemToBeErased = receiveInfoItem; - - // iterate - receiveInfoItem = _receivedInfoMap.Next(receiveInfoItem); - - // delete current - delete receiveInfo; - _receivedInfoMap.Erase(receiveInfoItemToBeErased); - }else - { - receiveInfoItem = _receivedInfoMap.Next(receiveInfoItem); - } - } - - } - return updateBoundingSet; -} - -WebRtc_Word32 -RTCPReceiver::BoundingSet(bool &tmmbrOwner, TMMBRSet*& boundingSetRec) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* receiveInfoItem=_receivedInfoMap.Find(_remoteSSRC); - if(receiveInfoItem ) - { - RTCPReceiveInformation* receiveInfo = (RTCPReceiveInformation*)receiveInfoItem->GetItem(); - if(receiveInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s failed to get RTCPReceiveInformation", __FUNCTION__); - return -1; - } - if(receiveInfo->TmmbnBoundingSet.lengthOfSet > 0) - { - boundingSetRec->VerifyAndAllocateSet(receiveInfo->TmmbnBoundingSet.lengthOfSet + 1); - for(WebRtc_UWord32 i=0; i< receiveInfo->TmmbnBoundingSet.lengthOfSet; i++) - { - if(receiveInfo->TmmbnBoundingSet.ptrSsrcSet[i] == _SSRC) - { - // owner of bounding set - tmmbrOwner = true; - } - boundingSetRec->ptrTmmbrSet[i] = receiveInfo->TmmbnBoundingSet.ptrTmmbrSet[i]; - boundingSetRec->ptrPacketOHSet[i] = receiveInfo->TmmbnBoundingSet.ptrPacketOHSet[i]; - boundingSetRec->ptrSsrcSet[i] = receiveInfo->TmmbnBoundingSet.ptrSsrcSet[i]; - } - return receiveInfo->TmmbnBoundingSet.lengthOfSet; - } - } - return -1; -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSDES(RTCPUtility::RTCPParserV2& rtcpParser) -{ - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpSdesChunkCode) - { - HandleSDESChunk(rtcpParser); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSDESChunk(RTCPUtility::RTCPParserV2& rtcpParser) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPCnameInformation* cnameInfo = CreateCnameInformation(rtcpPacket.CName.SenderSSRC); - if (cnameInfo) - { - memcpy(cnameInfo->name, rtcpPacket.CName.CName, rtcpPacket.CName.CNameLength); - cnameInfo->length = rtcpPacket.CName.CNameLength; - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleNACK(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.NACK.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - if (_SSRC != rtcpPacket.NACK.MediaSSRC) - { - // Not to us. - rtcpParser.Iterate(); - return; - } - - rtcpPacketInformation.ResetNACKPacketIdArray(); - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpRtpfbNackItemCode) - { - HandleNACKItem(rtcpPacket, rtcpPacketInformation); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleNACKItem(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPPacketInformation& rtcpPacketInformation) -{ - rtcpPacketInformation.AddNACKPacket(rtcpPacket.NACKItem.PacketID); - - WebRtc_UWord16 bitMask = rtcpPacket.NACKItem.BitMask; - if(bitMask) - { - for(int i=1; i <= 16; ++i) - { - if(bitMask & 0x01) - { - rtcpPacketInformation.AddNACKPacket(rtcpPacket.NACKItem.PacketID + i); - } - bitMask = bitMask >>1; - } - } - - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpNack; -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleBYE(RTCPUtility::RTCPParserV2& rtcpParser) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - // clear our lists - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* ptrReportBlockInfoItem = _receivedReportBlockMap.Find(rtcpPacket.BYE.SenderSSRC); - if (ptrReportBlockInfoItem != NULL) - { - delete static_cast(ptrReportBlockInfoItem->GetItem()); - _receivedReportBlockMap.Erase(ptrReportBlockInfoItem); - } - // we can't delete it due to TMMBR - MapItem* ptrReceiveInfoItem = _receivedInfoMap.Find(rtcpPacket.BYE.SenderSSRC); - if (ptrReceiveInfoItem != NULL) - { - static_cast(ptrReceiveInfoItem->GetItem())->readyForDelete = true; - } - - MapItem* ptrCnameInfoItem = _receivedCnameMap.Find(rtcpPacket.BYE.SenderSSRC); - if (ptrCnameInfoItem != NULL) - { - delete static_cast(ptrCnameInfoItem->GetItem()); - _receivedCnameMap.Erase(ptrCnameInfoItem); - } - rtcpParser.Iterate(); -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleXRVOIPMetric(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - if(rtcpPacket.XRVOIPMetricItem.SSRC == _SSRC) - { - // Store VoIP metrics block if it's about me - // from OriginatorSSRC do we filter it? - // rtcpPacket.XR.OriginatorSSRC; - - RTCPVoIPMetric receivedVoIPMetrics; - receivedVoIPMetrics.burstDensity = rtcpPacket.XRVOIPMetricItem.burstDensity; - receivedVoIPMetrics.burstDuration = rtcpPacket.XRVOIPMetricItem.burstDuration; - receivedVoIPMetrics.discardRate = rtcpPacket.XRVOIPMetricItem.discardRate; - receivedVoIPMetrics.endSystemDelay = rtcpPacket.XRVOIPMetricItem.endSystemDelay; - receivedVoIPMetrics.extRfactor = rtcpPacket.XRVOIPMetricItem.extRfactor; - receivedVoIPMetrics.gapDensity = rtcpPacket.XRVOIPMetricItem.gapDensity; - receivedVoIPMetrics.gapDuration = rtcpPacket.XRVOIPMetricItem.gapDuration; - receivedVoIPMetrics.Gmin = rtcpPacket.XRVOIPMetricItem.Gmin; - receivedVoIPMetrics.JBabsMax = rtcpPacket.XRVOIPMetricItem.JBabsMax; - receivedVoIPMetrics.JBmax = rtcpPacket.XRVOIPMetricItem.JBmax; - receivedVoIPMetrics.JBnominal = rtcpPacket.XRVOIPMetricItem.JBnominal; - receivedVoIPMetrics.lossRate = rtcpPacket.XRVOIPMetricItem.lossRate; - receivedVoIPMetrics.MOSCQ = rtcpPacket.XRVOIPMetricItem.MOSCQ; - receivedVoIPMetrics.MOSLQ = rtcpPacket.XRVOIPMetricItem.MOSLQ; - receivedVoIPMetrics.noiseLevel = rtcpPacket.XRVOIPMetricItem.noiseLevel; - receivedVoIPMetrics.RERL = rtcpPacket.XRVOIPMetricItem.RERL; - receivedVoIPMetrics.Rfactor = rtcpPacket.XRVOIPMetricItem.Rfactor; - receivedVoIPMetrics.roundTripDelay = rtcpPacket.XRVOIPMetricItem.roundTripDelay; - receivedVoIPMetrics.RXconfig = rtcpPacket.XRVOIPMetricItem.RXconfig; - receivedVoIPMetrics.signalLevel = rtcpPacket.XRVOIPMetricItem.signalLevel; - - rtcpPacketInformation.AddVoIPMetric(&receivedVoIPMetrics); - - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpXrVoipMetric; // received signal - } - rtcpParser.Iterate(); -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandlePLI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.PLI.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - if (_SSRC != rtcpPacket.PLI.MediaSSRC) - { - // Not to us. - rtcpParser.Iterate(); - return; - } - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpPli; // received signal that we need to send a new key frame - rtcpParser.Iterate(); -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleTMMBR(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - WebRtc_UWord32 senderSSRC = rtcpPacket.TMMBR.SenderSSRC; - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(senderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - if(rtcpPacket.TMMBR.MediaSSRC) - { - // rtcpPacket.TMMBR.MediaSSRC SHOULD be 0 if same as SenderSSRC - // in relay mode this is a valid number - senderSSRC = rtcpPacket.TMMBR.MediaSSRC; - } - - // Use packet length to calc max number of TMMBR blocks - // each TMMBR block is 8 bytes - ptrdiff_t maxNumOfTMMBRBlocks = rtcpParser.LengthLeft() / 8; - - // sanity - if(maxNumOfTMMBRBlocks > 200) // we can't have more than what's in one packet - { - assert(false); - rtcpParser.Iterate(); - return; - } - ptrReceiveInfo->VerifyAndAllocateTMMBRSet((WebRtc_UWord32)maxNumOfTMMBRBlocks); - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpRtpfbTmmbrItemCode) - { - HandleTMMBRItem(*ptrReceiveInfo, rtcpPacket, rtcpPacketInformation, senderSSRC); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleTMMBRItem(RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPPacketInformation& rtcpPacketInformation, - const WebRtc_UWord32 senderSSRC) -{ - if (_SSRC == rtcpPacket.TMMBRItem.SSRC && - rtcpPacket.TMMBRItem.MaxTotalMediaBitRate > 0) - { - receiveInfo.InsertTMMBRItem(senderSSRC, rtcpPacket.TMMBRItem); - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpTmmbr; - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleTMMBN(RTCPUtility::RTCPParserV2& rtcpParser) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.TMMBN.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - // Use packet length to calc max number of TMMBN blocks - // each TMMBN block is 8 bytes - ptrdiff_t maxNumOfTMMBNBlocks = rtcpParser.LengthLeft() / 8; - - // sanity - if(maxNumOfTMMBNBlocks > 200) // we cant have more than what's in one packet - { - assert(false); - rtcpParser.Iterate(); - return; - } - - ptrReceiveInfo->VerifyAndAllocateBoundingSet((WebRtc_UWord32)maxNumOfTMMBNBlocks); - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpRtpfbTmmbnItemCode) - { - HandleTMMBNItem(*ptrReceiveInfo, rtcpPacket); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSR_REQ(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpSrReq; - rtcpParser.Iterate(); -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleTMMBNItem(RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket) -{ - const unsigned int idx = receiveInfo.TmmbnBoundingSet.lengthOfSet; - - receiveInfo.TmmbnBoundingSet.ptrTmmbrSet[idx] = rtcpPacket.TMMBNItem.MaxTotalMediaBitRate; - receiveInfo.TmmbnBoundingSet.ptrPacketOHSet[idx] = rtcpPacket.TMMBNItem.MeasuredOverhead; - receiveInfo.TmmbnBoundingSet.ptrSsrcSet[idx] = rtcpPacket.TMMBNItem.SSRC; - - ++receiveInfo.TmmbnBoundingSet.lengthOfSet; -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSLI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.SLI.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpPsfbSliItemCode) - { - HandleSLIItem(rtcpPacket, rtcpPacketInformation); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleSLIItem(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPPacketInformation& rtcpPacketInformation) -{ - // in theory there could be multiple slices lost - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpSli; // received signal that we need to refresh a slice - rtcpPacketInformation.sliPictureId = rtcpPacket.SLIItem.PictureId; -} - -void -RTCPReceiver::HandleRPSI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.RPSI.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - if(pktType == RTCPUtility::kRtcpPsfbRpsiCode) - { - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpRpsi; // received signal that we have a confirmed reference picture - if(rtcpPacket.RPSI.NumberOfValidBits%8 != 0) - { - // to us unknown - // continue - rtcpParser.Iterate(); - return; - } - rtcpPacketInformation.rpsiPictureId = 0; - - // convert NativeBitString to rpsiPictureId - WebRtc_UWord8 numberOfBytes = rtcpPacket.RPSI.NumberOfValidBits /8; - for(WebRtc_UWord8 n = 0; n < (numberOfBytes-1); n++) - { - rtcpPacketInformation.rpsiPictureId += (rtcpPacket.RPSI.NativeBitString[n] & 0x7f); - rtcpPacketInformation.rpsiPictureId <<= 7; // prepare next - } - rtcpPacketInformation.rpsiPictureId += (rtcpPacket.RPSI.NativeBitString[numberOfBytes-1] & 0x7f); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleFIR(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - RTCPReceiveInformation* ptrReceiveInfo = GetReceiveInformation(rtcpPacket.FIR.SenderSSRC); - if (ptrReceiveInfo == NULL) - { - // This remote SSRC must be saved before. - rtcpParser.Iterate(); - return; - } - - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Iterate(); - while (pktType == RTCPUtility::kRtcpPsfbFirItemCode) - { - HandleFIRItem(*ptrReceiveInfo, rtcpPacket, rtcpPacketInformation); - pktType = rtcpParser.Iterate(); - } -} - -// no need for critsect we have _criticalSectionRTCPReceiver -void -RTCPReceiver::HandleFIRItem(RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPPacketInformation& rtcpPacketInformation) -{ - if (_SSRC == rtcpPacket.FIRItem.SSRC) // is it our sender that is requested to generate a new keyframe - { - // rtcpPacket.FIR.MediaSSRC SHOULD be 0 but we ignore to check it - // we don't know who this originate from - - // check if we have reported this FIRSequenceNumber before - if (rtcpPacket.FIRItem.CommandSequenceNumber != receiveInfo.lastFIRSequenceNumber) - { - // - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - - // extra sanity don't go crazy with the callbacks - if( (now - receiveInfo.lastFIRRequest) > RTCP_MIN_FRAME_LENGTH_MS) - { - receiveInfo.lastFIRRequest = now; - receiveInfo.lastFIRSequenceNumber = rtcpPacket.FIRItem.CommandSequenceNumber; - - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpFir; // received signal that we need to send a new key frame - } - } - } -} - -void -RTCPReceiver::HandleAPP(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpApp; - rtcpPacketInformation.applicationSubType = rtcpPacket.APP.SubType; - rtcpPacketInformation.applicationName = rtcpPacket.APP.Name; - - rtcpParser.Iterate(); -} - -void -RTCPReceiver::HandleAPPItem(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPPacketInformation& rtcpPacketInformation) -{ - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - - rtcpPacketInformation.AddApplicationData(rtcpPacket.APP.Data, rtcpPacket.APP.Size); - - rtcpParser.Iterate(); -} - -void -RTCPReceiver::OnReceivedIntraFrameRequest(const WebRtc_UWord8 message) const -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - - if(_cbVideoFeedback) - { - _cbVideoFeedback->OnReceivedIntraFrameRequest(_id, message); - } -} - -void -RTCPReceiver::OnReceivedSliceLossIndication(const WebRtc_UWord8 pitureID) const -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - - if(_cbRtcpFeedback) - { - _cbRtcpFeedback->OnSLIReceived(_id, pitureID); - } -} - -void -RTCPReceiver::OnReceivedReferencePictureSelectionIndication(const WebRtc_UWord64 pitureID) const -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - - if(_cbRtcpFeedback) - { - _cbRtcpFeedback->OnRPSIReceived(_id, pitureID); - } -} - -// Holding no Critical section -void -RTCPReceiver::TriggerCallbacksFromRTCPPacket(RTCPPacketInformation& rtcpPacketInformation) -{ - // callback if SR or RR - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpSr || - rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpRr) - { - if(rtcpPacketInformation.reportBlock) - { - _cbRtcpPrivate.OnPacketLossStatisticsUpdate(rtcpPacketInformation.fractionLost, - rtcpPacketInformation.roundTripTime, - rtcpPacketInformation.lastReceivedExtendedHighSeqNum, - rtcpPacketInformation.jitter); - } - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpSr) - { - _cbRtcpPrivate.OnReceivedNTP(); - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpSrReq) - { - _cbRtcpPrivate.OnRequestSendReport(); - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpNack) - { - if (rtcpPacketInformation.nackSequenceNumbersLength > 0) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, "SIG [RTCP] Incoming NACK to id:%d", _id); - _cbRtcpPrivate.OnReceivedNACK(rtcpPacketInformation.nackSequenceNumbersLength, - rtcpPacketInformation.nackSequenceNumbers); - } - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpTmmbr) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, "SIG [RTCP] Incoming TMMBR to id:%d", _id); - - // might trigger a OnReceivedBandwidthEstimateUpdate - _cbRtcpPrivate.OnReceivedTMMBR(); - } - if ((rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpPli) || - (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpFir)) - { - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpPli) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, "SIG [RTCP] Incoming PLI to id:%d", _id); - }else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceRtpRtcp, _id, "SIG [RTCP] Incoming FIR to id:%d", _id); - } - // we need use a bounce it up to handle default channel - _cbRtcpPrivate.OnReceivedIntraFrameRequest(0); - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpSli) - { - // we need use a bounce it up to handle default channel - _cbRtcpPrivate.OnReceivedSliceLossIndication(rtcpPacketInformation.sliPictureId); - } - if (rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpRpsi) - { - // we need use a bounce it up to handle default channel - _cbRtcpPrivate.OnReceivedReferencePictureSelectionIndication(rtcpPacketInformation.rpsiPictureId); - } - { - CriticalSectionScoped lock(_criticalSectionFeedbacks); - - // we need a feedback that we have received a report block(s) so that we can generate a new packet - // in a conference relay scenario, one received report can generate several RTCP packets, based - // on number relayed/mixed - // a send report block should go out to all receivers - if(_cbRtcpFeedback) - { - if(rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpSr) - { - _cbRtcpFeedback->OnSendReportReceived(_id, rtcpPacketInformation.remoteSSRC); - } else - { - _cbRtcpFeedback->OnReceiveReportReceived(_id, rtcpPacketInformation.remoteSSRC); - } - if(rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpXrVoipMetric) - { - WebRtc_Word8 VoIPmetricBuffer[7*4]; - VoIPmetricBuffer[0] = rtcpPacketInformation.VoIPMetric->lossRate; - VoIPmetricBuffer[1] = rtcpPacketInformation.VoIPMetric->discardRate; - VoIPmetricBuffer[2] = rtcpPacketInformation.VoIPMetric->burstDensity; - VoIPmetricBuffer[3] = rtcpPacketInformation.VoIPMetric->gapDensity; - - VoIPmetricBuffer[4] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->burstDuration >> 8); - VoIPmetricBuffer[5] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->burstDuration); - VoIPmetricBuffer[6] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->gapDuration >> 8); - VoIPmetricBuffer[7] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->gapDuration); - - VoIPmetricBuffer[8] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->roundTripDelay >> 8); - VoIPmetricBuffer[9] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->roundTripDelay); - VoIPmetricBuffer[10] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->endSystemDelay >> 8); - VoIPmetricBuffer[11] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->endSystemDelay); - - VoIPmetricBuffer[12] = rtcpPacketInformation.VoIPMetric->signalLevel; - VoIPmetricBuffer[13] = rtcpPacketInformation.VoIPMetric->noiseLevel; - VoIPmetricBuffer[14] = rtcpPacketInformation.VoIPMetric->RERL; - VoIPmetricBuffer[15] = rtcpPacketInformation.VoIPMetric->Gmin; - - VoIPmetricBuffer[16] = rtcpPacketInformation.VoIPMetric->Rfactor; - VoIPmetricBuffer[17] = rtcpPacketInformation.VoIPMetric->extRfactor; - VoIPmetricBuffer[18] = rtcpPacketInformation.VoIPMetric->MOSLQ; - VoIPmetricBuffer[19] = rtcpPacketInformation.VoIPMetric->MOSCQ; - - VoIPmetricBuffer[20] = rtcpPacketInformation.VoIPMetric->RXconfig; - VoIPmetricBuffer[21] = 0; // reserved - VoIPmetricBuffer[22] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBnominal >> 8); - VoIPmetricBuffer[23] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBnominal); - - VoIPmetricBuffer[24] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBmax >> 8); - VoIPmetricBuffer[25] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBmax); - VoIPmetricBuffer[26] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBabsMax >> 8); - VoIPmetricBuffer[27] = (WebRtc_UWord8)(rtcpPacketInformation.VoIPMetric->JBabsMax); - - _cbRtcpFeedback->OnXRVoIPMetricReceived(_id, rtcpPacketInformation.VoIPMetric, VoIPmetricBuffer); - } - if(rtcpPacketInformation.rtcpPacketTypeFlags & kRtcpApp) - { - _cbRtcpFeedback->OnApplicationDataReceived(_id, - rtcpPacketInformation.applicationSubType, - rtcpPacketInformation.applicationName, - rtcpPacketInformation.applicationLength, - rtcpPacketInformation.applicationData); - } - } - } -} - -void -RTCPReceiver::UpdateBandwidthEstimate(const WebRtc_UWord16 bwEstimateKbit) -{ - CriticalSectionScoped lock(_criticalSectionFeedbacks); - - if(_cbRtcpFeedback) - { - _cbRtcpFeedback->OnTMMBRReceived(_id, bwEstimateKbit); - } - -} - -WebRtc_Word32 -RTCPReceiver::CNAME(const WebRtc_UWord32 remoteSSRC, - WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const -{ - if(cName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - RTCPCnameInformation* cnameInfo = GetCnameInformation(remoteSSRC); - if(cnameInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tfailed to GetCnameInformation(%d)", remoteSSRC); - return -1; - } - memcpy(cName, cnameInfo->name, cnameInfo->length); - cName[cnameInfo->length] = 0; - return 0; -} - -// no callbacks allowed inside this function -WebRtc_Word32 -RTCPReceiver::TMMBRReceived(const WebRtc_UWord32 size, - const WebRtc_UWord32 accNumCandidates, - TMMBRSet* candidateSet) const -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - - MapItem* receiveInfoItem=_receivedInfoMap.First(); - if(receiveInfoItem == NULL) - { - return -1; - } - WebRtc_UWord32 num = accNumCandidates; - if(candidateSet) - { - while( num < size && receiveInfoItem) - { - RTCPReceiveInformation* receiveInfo = (RTCPReceiveInformation*)receiveInfoItem->GetItem(); - if(receiveInfo == NULL) - { - return 0; - } - for (WebRtc_UWord32 i = 0; (num < size) && (i < receiveInfo->TmmbrSet.lengthOfSet); i++) - { - if(receiveInfo->GetTMMBRSet(i, num, candidateSet) == 0) - { - num++; - } - } - receiveInfoItem = _receivedInfoMap.Next(receiveInfoItem); - } - } else - { - while(receiveInfoItem) - { - RTCPReceiveInformation* receiveInfo = (RTCPReceiveInformation*)receiveInfoItem->GetItem(); - if(receiveInfo == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s failed to get RTCPReceiveInformation", __FUNCTION__); - return -1; - } - num += receiveInfo->TmmbrSet.lengthOfSet; - - receiveInfoItem = _receivedInfoMap.Next(receiveInfoItem); - } - } - return num; -} - -WebRtc_Word32 -RTCPReceiver::SetPacketTimeout(const WebRtc_UWord32 timeoutMS) -{ - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - _packetTimeOutMS = timeoutMS; - return 0; -} - -void RTCPReceiver::PacketTimeout() -{ - if(_packetTimeOutMS == 0) - { - // not configured - return; - } - - bool packetTimeOut = false; - { - CriticalSectionScoped lock(_criticalSectionRTCPReceiver); - if(_lastReceived == 0) - { - // not active - return; - } - - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - - if(now - _lastReceived > _packetTimeOutMS) - { - packetTimeOut = true; - _lastReceived = 0; // only one callback - } - } - CriticalSectionScoped lock(_criticalSectionFeedbacks); - if(packetTimeOut && _cbRtcpFeedback) - { - _cbRtcpFeedback->OnRTCPPacketTimeout(_id); - } -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h deleted file mode 100644 index 2486f8594..000000000 --- a/modules/rtp_rtcp/source/rtcp_receiver.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_ - -#include "typedefs.h" -#include "map_wrapper.h" -#include "rtp_utility.h" -#include "rtcp_utility.h" -#include "rtp_rtcp_defines.h" -#include "rtp_rtcp_private.h" -#include "rtcp_receiver_help.h" - -namespace webrtc { -class RTCPReceiver -{ -public: - RTCPReceiver(const WebRtc_Word32 id, ModuleRtpRtcpPrivate& callback); - virtual ~RTCPReceiver(); - - void ChangeUniqueId(const WebRtc_Word32 id); - - RTCPMethod Status() const; - WebRtc_Word32 SetRTCPStatus(const RTCPMethod method); - - WebRtc_UWord32 LastReceived(); - - void SetSSRC( const WebRtc_UWord32 ssrc); - void SetRelaySSRC( const WebRtc_UWord32 ssrc); - WebRtc_Word32 SetRemoteSSRC( const WebRtc_UWord32 ssrc); - - WebRtc_UWord32 RelaySSRC() const; - - WebRtc_Word32 RegisterIncomingRTCPCallback(RtcpFeedback* incomingMessagesCallback); - - WebRtc_Word32 RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback); - - WebRtc_Word32 IncomingRTCPPacket(RTCPHelp::RTCPPacketInformation& rtcpPacketInformation, - RTCPUtility::RTCPParserV2 *rtcpParser); - - void TriggerCallbacksFromRTCPPacket(RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - // get received cname - WebRtc_Word32 CNAME(const WebRtc_UWord32 remoteSSRC, - WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const; - - // get received NTP - WebRtc_Word32 NTP(WebRtc_UWord32 *ReceivedNTPsecs, - WebRtc_UWord32 *ReceivedNTPfrac, - WebRtc_UWord32 *RTCPArrivalTimeSecs, - WebRtc_UWord32 *RTCPArrivalTimeFrac) const; - - // get rtt - WebRtc_Word32 RTT(const WebRtc_UWord32 remoteSSRC, - WebRtc_UWord16* RTT, - WebRtc_UWord16* avgRTT, - WebRtc_UWord16* minRTT, - WebRtc_UWord16* maxRTT) const; - - WebRtc_Word32 ResetRTT(const WebRtc_UWord32 remoteSSRC); - - void UpdateLipSync(const WebRtc_Word32 audioVideoOffset) const; - - WebRtc_Word32 SenderInfoReceived(RTCPSenderInfo* senderInfo) const; - - void OnReceivedIntraFrameRequest(const WebRtc_UWord8 message) const; - void OnReceivedSliceLossIndication(const WebRtc_UWord8 pitureID) const; - void OnReceivedReferencePictureSelectionIndication(const WebRtc_UWord64 pitureID) const; - - // get statistics - WebRtc_Word32 StatisticsReceived(const WebRtc_UWord32 remoteSSRC, - RTCPReportBlock* receiveBlock) const; - // Get TMMBR - WebRtc_Word32 TMMBRReceived(const WebRtc_UWord32 size, - const WebRtc_UWord32 accNumCandidates, - TMMBRSet* candidateSet) const; - - bool UpdateRTCPReceiveInformationTimers(); - - void UpdateBandwidthEstimate(const WebRtc_UWord16 bwEstimateKbit); - - WebRtc_Word32 BoundingSet(bool &tmmbrOwner, - TMMBRSet*& boundingSetRec); - - WebRtc_Word32 SetPacketTimeout(const WebRtc_UWord32 timeoutMS); - void PacketTimeout(); - -protected: - RTCPHelp::RTCPReportBlockInformation* CreateReportBlockInformation(const WebRtc_UWord32 remoteSSRC); - RTCPHelp::RTCPReportBlockInformation* GetReportBlockInformation(const WebRtc_UWord32 remoteSSRC) const; - - RTCPUtility::RTCPCnameInformation* CreateCnameInformation(const WebRtc_UWord32 remoteSSRC); - RTCPUtility::RTCPCnameInformation* GetCnameInformation(const WebRtc_UWord32 remoteSSRC) const; - - RTCPHelp::RTCPReceiveInformation* CreateReceiveInformation(const WebRtc_UWord32 remoteSSRC); - RTCPHelp::RTCPReceiveInformation* GetReceiveInformation(const WebRtc_UWord32 remoteSSRC); - - void UpdateReceiveInformation( RTCPHelp::RTCPReceiveInformation& receiveInformation); - - void HandleSenderReceiverReport(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleReportBlock(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation, - const WebRtc_UWord32 remoteSSRC, - const WebRtc_UWord8 numberOfReportBlocks); - - void HandleSDES(RTCPUtility::RTCPParserV2& rtcpParser); - - void HandleSDESChunk(RTCPUtility::RTCPParserV2& rtcpParser); - - void HandleXRVOIPMetric(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleNACK(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleNACKItem(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleBYE(RTCPUtility::RTCPParserV2& rtcpParser); - - void HandlePLI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleSLI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleSLIItem(const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleRPSI(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleTMMBR(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleTMMBRItem(RTCPHelp::RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation, - const WebRtc_UWord32 senderSSRC); - - void HandleTMMBN(RTCPUtility::RTCPParserV2& rtcpParser); - - void HandleSR_REQ(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleTMMBNItem(RTCPHelp::RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket); - - void HandleFIR(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleFIRItem(RTCPHelp::RTCPReceiveInformation& receiveInfo, - const RTCPUtility::RTCPPacket& rtcpPacket, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleAPP(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - - void HandleAPPItem(RTCPUtility::RTCPParserV2& rtcpParser, - RTCPHelp::RTCPPacketInformation& rtcpPacketInformation); - -private: - WebRtc_Word32 _id; - RTCPMethod _method; - WebRtc_UWord32 _lastReceived; - ModuleRtpRtcpPrivate& _cbRtcpPrivate; - - CriticalSectionWrapper& _criticalSectionFeedbacks; - RtcpFeedback* _cbRtcpFeedback; - RtpVideoFeedback* _cbVideoFeedback; - - CriticalSectionWrapper& _criticalSectionRTCPReceiver; - WebRtc_UWord32 _SSRC; - WebRtc_UWord32 _remoteSSRC; - - // Received send report - RTCPSenderInfo _remoteSenderInfo; - WebRtc_UWord32 _lastReceivedSRNTPsecs; // when did we receive the last send report - WebRtc_UWord32 _lastReceivedSRNTPfrac; - - // Received report block - MapWrapper _receivedReportBlockMap; // pair SSRC to report block - MapWrapper _receivedInfoMap; // pair SSRC of sender to might not be a SSRC that have any data (i.e. a conference) - MapWrapper _receivedCnameMap; // pair SSRC to Cname - - // timeout - WebRtc_UWord32 _packetTimeOutMS; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_H_ diff --git a/modules/rtp_rtcp/source/rtcp_receiver_help.cc b/modules/rtp_rtcp/source/rtcp_receiver_help.cc deleted file mode 100644 index 359851efa..000000000 --- a/modules/rtp_rtcp/source/rtcp_receiver_help.cc +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtcp_receiver_help.h" -#include "rtp_utility.h" - -#include //memset -#include //assert - -namespace webrtc { -using namespace RTCPHelp; - -RTCPPacketInformation::RTCPPacketInformation() : - rtcpPacketTypeFlags(0), - nackSequenceNumbers(0), - nackSequenceNumbersLength(0), - applicationSubType(0), - applicationName(0), - applicationData(), - applicationLength(0), - reportBlock(false), - fractionLost(0), - roundTripTime(0), - lastReceivedExtendedHighSeqNum(0), - jitter(0), - sliPictureId(0), - rpsiPictureId(0), - VoIPMetric(NULL) -{ -} - -RTCPPacketInformation::~RTCPPacketInformation() -{ - delete [] nackSequenceNumbers; - delete [] applicationData; - delete VoIPMetric; -} - -void -RTCPPacketInformation::AddVoIPMetric(const RTCPVoIPMetric* metric) -{ - VoIPMetric = new RTCPVoIPMetric(); - memcpy(VoIPMetric, metric, sizeof(RTCPVoIPMetric)); -} - -void -RTCPPacketInformation::AddApplicationData(const WebRtc_UWord8* data, const WebRtc_UWord16 size) -{ - WebRtc_UWord8* oldData = applicationData; - WebRtc_UWord16 oldLength = applicationLength; - - applicationLength += size; - applicationData = new WebRtc_UWord8[applicationLength]; - - if(oldData) - { - memcpy(applicationData, oldData, oldLength); - memcpy(applicationData+oldLength, data, size); - delete [] oldData; - } else - { - memcpy(applicationData, data, size); - } -} - -void -RTCPPacketInformation::ResetNACKPacketIdArray() -{ - if(NULL == nackSequenceNumbers) - { - nackSequenceNumbers = new WebRtc_UWord16[NACK_PACKETS_MAX_SIZE]; - } - nackSequenceNumbersLength = 0; -} - -void -RTCPPacketInformation::AddNACKPacket(const WebRtc_UWord16 packetID) -{ - assert(nackSequenceNumbers); - - WebRtc_UWord16& idx = nackSequenceNumbersLength; - if (idx < NACK_PACKETS_MAX_SIZE) - { - nackSequenceNumbers[idx++] = packetID; - } -} - -void -RTCPPacketInformation::AddReportInfo(const WebRtc_UWord8 fraction, - const WebRtc_UWord16 rtt, - const WebRtc_UWord32 extendedHighSeqNum, - const WebRtc_UWord32 j) -{ - reportBlock = true; - fractionLost = fraction; - roundTripTime = rtt; - jitter = j; - lastReceivedExtendedHighSeqNum = extendedHighSeqNum; -} - -RTCPReportBlockInformation::RTCPReportBlockInformation(): - remoteReceiveBlock(), - remoteMaxJitter(0), - RTT(0), - minRTT(0), - maxRTT(0), - avgRTT(0), - numAverageCalcs(0) -{ - memset(&remoteReceiveBlock,0,sizeof(remoteReceiveBlock)); -} - -RTCPReportBlockInformation::~RTCPReportBlockInformation() -{ -} - -RTCPReceiveInformation::RTCPReceiveInformation() : - - lastTimeReceived(0), - lastFIRSequenceNumber(-1), - lastFIRRequest(0), - readyForDelete(false), - _tmmbrSetTimeouts(NULL) -{ -} - -RTCPReceiveInformation::~RTCPReceiveInformation() -{ - if(_tmmbrSetTimeouts) - { - delete [] _tmmbrSetTimeouts; - } -} - -// don't use TmmbrSet.VerifyAndAllocate this version keeps the data -void -RTCPReceiveInformation::VerifyAndAllocateTMMBRSet(const WebRtc_UWord32 minimumSize) -{ - if(minimumSize > TmmbrSet.sizeOfSet) - { - // make sure that our buffers are big enough - WebRtc_UWord32* ptrTmmbrSet = new WebRtc_UWord32[minimumSize]; - WebRtc_UWord32* ptrTmmbrPacketOHSet = new WebRtc_UWord32[minimumSize]; - WebRtc_UWord32* ptrTmmbrSsrcSet = new WebRtc_UWord32[minimumSize]; - WebRtc_UWord32* tmmbrSetTimeouts = new WebRtc_UWord32[minimumSize]; - - if(TmmbrSet.lengthOfSet > 0) - { - // copy old values - memcpy(ptrTmmbrSet, TmmbrSet.ptrTmmbrSet, sizeof(WebRtc_UWord32) * TmmbrSet.lengthOfSet); - memcpy(ptrTmmbrPacketOHSet, TmmbrSet.ptrPacketOHSet, sizeof(WebRtc_UWord32) * TmmbrSet.lengthOfSet); - memcpy(ptrTmmbrSsrcSet, TmmbrSet.ptrSsrcSet, sizeof(WebRtc_UWord32) * TmmbrSet.lengthOfSet); - memcpy(tmmbrSetTimeouts, _tmmbrSetTimeouts, sizeof(WebRtc_UWord32) * TmmbrSet.lengthOfSet); - } - if(TmmbrSet.ptrTmmbrSet) - { - delete [] TmmbrSet.ptrTmmbrSet; - delete [] TmmbrSet.ptrPacketOHSet; - delete [] TmmbrSet.ptrSsrcSet; - } - if(_tmmbrSetTimeouts) - { - delete [] _tmmbrSetTimeouts; - } - TmmbrSet.ptrTmmbrSet = ptrTmmbrSet; - TmmbrSet.ptrPacketOHSet = ptrTmmbrPacketOHSet; - TmmbrSet.ptrSsrcSet = ptrTmmbrSsrcSet; - TmmbrSet.sizeOfSet = minimumSize; - _tmmbrSetTimeouts = tmmbrSetTimeouts; - } -} - -void -RTCPReceiveInformation::InsertTMMBRItem(const WebRtc_UWord32 senderSSRC, - const RTCPUtility::RTCPPacketRTPFBTMMBRItem& TMMBRItem) -{ - // serach to see if we have it in our list - for(WebRtc_UWord32 i = 0; i < TmmbrSet.lengthOfSet; i++) - { - if(TmmbrSet.ptrSsrcSet[i] == senderSSRC) - { - // we already have this SSRC in our list - // update it - TmmbrSet.ptrPacketOHSet[i] = TMMBRItem.MeasuredOverhead; - TmmbrSet.ptrTmmbrSet[i] = TMMBRItem.MaxTotalMediaBitRate; - _tmmbrSetTimeouts[i] = ModuleRTPUtility::GetTimeInMS(); - return; - } - } - VerifyAndAllocateTMMBRSet(TmmbrSet.lengthOfSet+1); - - const WebRtc_UWord32 idx = TmmbrSet.lengthOfSet; - TmmbrSet.ptrPacketOHSet[idx] = TMMBRItem.MeasuredOverhead; - TmmbrSet.ptrTmmbrSet[idx] = TMMBRItem.MaxTotalMediaBitRate; - TmmbrSet.ptrSsrcSet[idx] = senderSSRC; - _tmmbrSetTimeouts[idx] = ModuleRTPUtility::GetTimeInMS(); - TmmbrSet.lengthOfSet++; -} - -WebRtc_Word32 -RTCPReceiveInformation::GetTMMBRSet(const WebRtc_UWord32 sourceIdx, - const WebRtc_UWord32 targetIdx, - TMMBRSet* candidateSet) -{ - if(sourceIdx >= TmmbrSet.lengthOfSet) - { - return -1; - } - if(targetIdx >= candidateSet->sizeOfSet) - { - return -1; - } - WebRtc_UWord32 timeNow = ModuleRTPUtility::GetTimeInMS(); - - // use audio define since we don't know what interval the remote peer is using - if(timeNow - _tmmbrSetTimeouts[sourceIdx] > 5*RTCP_INTERVAL_AUDIO_MS) - { - // value timed out - const WebRtc_UWord32 move = TmmbrSet.lengthOfSet - (sourceIdx + 1); - if(move > 0) - { - memmove(&(TmmbrSet.ptrTmmbrSet[sourceIdx]), &(TmmbrSet.ptrTmmbrSet[sourceIdx+1]), move* sizeof(WebRtc_UWord32)); - memmove(&(TmmbrSet.ptrPacketOHSet[sourceIdx]),&(TmmbrSet.ptrPacketOHSet[sourceIdx+1]), move* sizeof(WebRtc_UWord32)); - memmove(&(TmmbrSet.ptrSsrcSet[sourceIdx]),&(TmmbrSet.ptrSsrcSet[sourceIdx+1]), move* sizeof(WebRtc_UWord32)); - memmove(&(_tmmbrSetTimeouts[sourceIdx]),&(_tmmbrSetTimeouts[sourceIdx+1]), move* sizeof(WebRtc_UWord32)); - } - TmmbrSet.lengthOfSet--; - return -1; - } - - candidateSet->ptrTmmbrSet[targetIdx] = TmmbrSet.ptrTmmbrSet[sourceIdx]; - candidateSet->ptrPacketOHSet[targetIdx] = TmmbrSet.ptrPacketOHSet[sourceIdx]; - candidateSet->ptrSsrcSet[targetIdx] = TmmbrSet.ptrSsrcSet[sourceIdx]; - return 0; -} - -void RTCPReceiveInformation::VerifyAndAllocateBoundingSet(const WebRtc_UWord32 minimumSize) -{ - TmmbnBoundingSet.VerifyAndAllocateSet(minimumSize); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_receiver_help.h b/modules/rtp_rtcp/source/rtcp_receiver_help.h deleted file mode 100644 index 26caaa92f..000000000 --- a/modules/rtp_rtcp/source/rtcp_receiver_help.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_HELP_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_HELP_H_ - -#include "typedefs.h" - -#include "rtp_rtcp_defines.h" // RTCPReportBlock -#include "rtp_rtcp_config.h" // RTCP_MAX_REPORT_BLOCKS -#include "rtcp_utility.h" -#include "tmmbr_help.h" - -namespace webrtc { -namespace RTCPHelp -{ - -class RTCPPacketInformation -{ -public: - RTCPPacketInformation(); - ~RTCPPacketInformation(); - - void AddVoIPMetric(const RTCPVoIPMetric* metric); - - void AddApplicationData(const WebRtc_UWord8* data, const WebRtc_UWord16 size); - - void AddNACKPacket(const WebRtc_UWord16 packetID); - void ResetNACKPacketIdArray(); - - void AddReportInfo(const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 rtt, - const WebRtc_UWord32 extendedHighSeqNum, - const WebRtc_UWord32 jitter); - - WebRtc_UWord32 rtcpPacketTypeFlags; // RTCPPacketTypeFlags bit field - WebRtc_UWord32 remoteSSRC; - - WebRtc_UWord16* nackSequenceNumbers; - WebRtc_UWord16 nackSequenceNumbersLength; - - WebRtc_UWord8 applicationSubType; - WebRtc_UWord32 applicationName; - WebRtc_UWord8* applicationData; - WebRtc_UWord16 applicationLength; - - bool reportBlock; - WebRtc_UWord8 fractionLost; - WebRtc_UWord16 roundTripTime; - WebRtc_UWord32 lastReceivedExtendedHighSeqNum; - WebRtc_UWord32 jitter; - - WebRtc_UWord8 sliPictureId; - WebRtc_UWord64 rpsiPictureId; - - RTCPVoIPMetric* VoIPMetric; -}; - - -class RTCPReportBlockInformation -{ -public: - RTCPReportBlockInformation(); - ~RTCPReportBlockInformation(); - - // Statistics - RTCPReportBlock remoteReceiveBlock; - WebRtc_UWord32 remoteMaxJitter; - - // RTT - WebRtc_UWord16 RTT; - WebRtc_UWord16 minRTT; - WebRtc_UWord16 maxRTT; - WebRtc_UWord16 avgRTT; - WebRtc_UWord32 numAverageCalcs; -}; - -class RTCPReceiveInformation -{ -public: - RTCPReceiveInformation(); - ~RTCPReceiveInformation(); - - void VerifyAndAllocateBoundingSet(const WebRtc_UWord32 minimumSize); - void VerifyAndAllocateTMMBRSet(const WebRtc_UWord32 minimumSize); - - void InsertTMMBRItem(const WebRtc_UWord32 senderSSRC, - const RTCPUtility::RTCPPacketRTPFBTMMBRItem& TMMBRItem); - - // get - WebRtc_Word32 GetTMMBRSet(const WebRtc_UWord32 sourceIdx, - const WebRtc_UWord32 targetIdx, - TMMBRSet* candidateSet); - - WebRtc_UWord32 lastTimeReceived; - - // FIR - WebRtc_Word32 lastFIRSequenceNumber; - WebRtc_UWord32 lastFIRRequest; - - // TMMBN - TMMBRSet TmmbnBoundingSet; - - // TMMBR - TMMBRSet TmmbrSet; - - bool readyForDelete; -private: - WebRtc_UWord32* _tmmbrSetTimeouts; -}; - -} // end namespace RTCPHelp -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_RECEIVER_HELP_H_ diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc deleted file mode 100644 index 89af2879b..000000000 --- a/modules/rtp_rtcp/source/rtcp_sender.cc +++ /dev/null @@ -1,2002 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtcp_sender.h" -#include "rtcp_utility.h" - -#include // memcpy -#include // assert -#include // rand - -#include "trace.h" -#include "tick_util.h" -#include "common_types.h" -#include "critical_section_wrapper.h" - -namespace webrtc { -RTCPSender::RTCPSender(const WebRtc_Word32 id, - const bool audio, - ModuleRtpRtcpPrivate& callback) : - _id(id), - _audio(audio), - _method(kRtcpOff), - _cbRtcpPrivate(callback), - _criticalSectionTransport(*CriticalSectionWrapper::CreateCriticalSection()), - _cbTransport(NULL), - - _criticalSectionRTCPSender(*CriticalSectionWrapper::CreateCriticalSection()), - _usingNack(false), - _sending(false), - _sendTMMBN(false), - _TMMBR(false), - _nextTimeToSendRTCP(0), - _SSRC(0), - _remoteSSRC(0), - _CNAME(), - _reportBlocks(), - _csrcCNAMEs(), - - _cameraDelayMS(0), - - _lastSendReport(), - _lastRTCPTime(), - - _CSRCs(0), - _CSRC(), - _includeCSRCs(true), - - _sequenceNumberFIR(0), - _lastTimeFIR(0), - - _tmmbrHelp(audio), - _tmmbr_Send(0), - _packetOH_Send(0), - _remoteRateControl(), - - _appSend(false), - _appSubType(0), - _appName(), - _appData(NULL), - _appLength(0), - _xrSendVoIPMetric(false), - _xrVoIPMetric() -{ - memset(_CNAME, 0, sizeof(_CNAME)); - memset(_lastSendReport, 0, sizeof(_lastSendReport)); - memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime)); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); -} - -RTCPSender::~RTCPSender() -{ - if(_appData) - { - delete [] _appData; - } - - MapItem* item = _reportBlocks.First(); - while(item) - { - RTCPReportBlock* ptr = (RTCPReportBlock*)(item->GetItem()); - if(ptr) - { - delete ptr; - } - _reportBlocks.Erase(item); - item = _reportBlocks.First(); - } - item = _csrcCNAMEs.First(); - while(item) - { - RTCPUtility::RTCPCnameInformation* ptr = (RTCPUtility::RTCPCnameInformation*)(item->GetItem()); - if(ptr) - { - delete ptr; - } - _csrcCNAMEs.Erase(item); - item = _csrcCNAMEs.First(); - } - delete &_criticalSectionTransport; - delete &_criticalSectionRTCPSender; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); -} - -WebRtc_Word32 -RTCPSender::Init() -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - _method = kRtcpOff; - _cbTransport = NULL; - _usingNack = false; - _sending = false; - _sendTMMBN = false; - _TMMBR = false; - _SSRC = 0; - _remoteSSRC = 0; - _cameraDelayMS = 0; - _sequenceNumberFIR = 0; - _tmmbr_Send = 0; - _packetOH_Send = 0; - _remoteRateControl.Reset(); - _nextTimeToSendRTCP = 0; - _CSRCs = 0; - _appSend = false; - _appSubType = 0; - - if(_appData) - { - delete [] _appData; - _appData = NULL; - } - _appLength = 0; - - _xrSendVoIPMetric = false; - - memset(&_xrVoIPMetric, 0, sizeof(_xrVoIPMetric)); - memset(_CNAME, 0, sizeof(_CNAME)); - memset(_lastSendReport, 0, sizeof(_lastSendReport)); - memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime)); - return 0; -} - -void -RTCPSender::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -WebRtc_Word32 -RTCPSender::RegisterSendTransport(Transport* outgoingTransport) -{ - CriticalSectionScoped lock(_criticalSectionTransport); - _cbTransport = outgoingTransport; - return 0; -} - -RTCPMethod -RTCPSender::Status() const -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - return _method; -} - -WebRtc_Word32 -RTCPSender::SetRTCPStatus(const RTCPMethod method) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - if(method != kRtcpOff) - { - if(_audio) - { - _nextTimeToSendRTCP = ModuleRTPUtility::GetTimeInMS() + (RTCP_INTERVAL_AUDIO_MS/2); - } else - { - _nextTimeToSendRTCP = ModuleRTPUtility::GetTimeInMS() + (RTCP_INTERVAL_VIDEO_MS/2); - } - } - _method = method; - return 0; -} - -bool -RTCPSender::Sending() const -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - return _sending; -} - -WebRtc_Word32 -RTCPSender::SetSendingStatus(const bool sending) -{ - bool sendRTCPBye = false; - { - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(_method != kRtcpOff) - { - if(sending == false && _sending == true) - { - // Trigger RTCP bye - sendRTCPBye = true; - } - } - _sending = sending; - } - if(sendRTCPBye) - { - return SendRTCP(kRtcpBye); - } - return 0; -} - -bool -RTCPSender::TMMBR() const -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - return _TMMBR; -} - -WebRtc_Word32 -RTCPSender::SetTMMBRStatus(const bool enable) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - _TMMBR = enable; - return 0; -} - -void -RTCPSender::SetSSRC( const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(_SSRC != 0) - { - // not first SetSSRC, probably due to a collision - // schedule a new RTCP report - _nextTimeToSendRTCP = ModuleRTPUtility::GetTimeInMS() + 100; // make sure that we send a RTP packet - } - _SSRC = ssrc; -} - -WebRtc_Word32 -RTCPSender::SetRemoteSSRC( const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - _remoteSSRC = ssrc; - _remoteRateControl.Reset(); - return 0; -} - -WebRtc_Word32 -RTCPSender::SetCameraDelay(const WebRtc_Word32 delayMS) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - if(delayMS > 1000 || delayMS < -1000) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, delay can't be larger than 1 sec", __FUNCTION__); - return -1; - } - _cameraDelayMS = delayMS; - return 0; -} - -WebRtc_Word32 -RTCPSender::CNAME(WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - if(cName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPSender); - memcpy(cName, _CNAME, RTCP_CNAME_SIZE); - return 0; -} - -WebRtc_Word32 -RTCPSender::SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - if(cName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - WebRtc_Word32 length = (WebRtc_Word32)strlen(cName); - if(length > RTCP_CNAME_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, too long cName", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - memcpy(_CNAME, cName, length+1); - return 0; -} - -WebRtc_Word32 -RTCPSender::AddMixedCNAME(const WebRtc_UWord32 SSRC, - const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - if(cName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - WebRtc_Word32 length = (WebRtc_Word32)strlen(cName); - if(length > RTCP_CNAME_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument, too long cName", __FUNCTION__); - return -1; - } - - CriticalSectionScoped lock(_criticalSectionRTCPSender); - if(_csrcCNAMEs.Size() == kRtpCsrcSize) - { - return -1; - } - RTCPUtility::RTCPCnameInformation* ptr= new RTCPUtility::RTCPCnameInformation(); - - memcpy(ptr->name, cName, length+1); - ptr->length = (WebRtc_UWord8)length; - _csrcCNAMEs.Insert(SSRC, ptr); - return 0; -} - -WebRtc_Word32 -RTCPSender::RemoveMixedCNAME(const WebRtc_UWord32 SSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - MapItem* item= _csrcCNAMEs.Find(SSRC); - if(item) - { - RTCPUtility::RTCPCnameInformation* ptr= (RTCPUtility::RTCPCnameInformation*)(item->GetItem()); - if(ptr) - { - delete ptr; - } - _csrcCNAMEs.Erase(item); - return 0; - } - return -1; -} - -bool -RTCPSender::TimeToSendRTCPReport(const bool sendKeyframeBeforeRTP) const -{ -/* - For audio we use a fix 5 sec interval - - For video we use 1 sec interval fo a BW smaller than 360 kbit/s, - technicaly we break the max 5% RTCP BW for video below 10 kbit/s but that should be extreamly rare - - -From RFC 3550 - - MAX RTCP BW is 5% if the session BW - A send report is approximately 65 bytes inc CNAME - A report report is approximately 28 bytes - - The RECOMMENDED value for the reduced minimum in seconds is 360 - divided by the session bandwidth in kilobits/second. This minimum - is smaller than 5 seconds for bandwidths greater than 72 kb/s. - - If the participant has not yet sent an RTCP packet (the variable - initial is true), the constant Tmin is set to 2.5 seconds, else it - is set to 5 seconds. - - The interval between RTCP packets is varied randomly over the - range [0.5,1.5] times the calculated interval to avoid unintended - synchronization of all participants - - if we send - If the participant is a sender (we_sent true), the constant C is - set to the average RTCP packet size (avg_rtcp_size) divided by 25% - of the RTCP bandwidth (rtcp_bw), and the constant n is set to the - number of senders. - - if we receive only - If we_sent is not true, the constant C is set - to the average RTCP packet size divided by 75% of the RTCP - bandwidth. The constant n is set to the number of receivers - (members - senders). If the number of senders is greater than - 25%, senders and receivers are treated together. - - reconsideration NOT required for peer-to-peer - "timer reconsideration" is - employed. This algorithm implements a simple back-off mechanism - which causes users to hold back RTCP packet transmission if the - group sizes are increasing. - - n = number of members - C = avg_size/(rtcpBW/4) - - 3. The deterministic calculated interval Td is set to max(Tmin, n*C). - - 4. The calculated interval T is set to a number uniformly distributed - between 0.5 and 1.5 times the deterministic calculated interval. - - 5. The resulting value of T is divided by e-3/2=1.21828 to compensate - for the fact that the timer reconsideration algorithm converges to - a value of the RTCP bandwidth below the intended average -*/ - - if(_method == kRtcpOff) - { - return false; - } - - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(!_audio && sendKeyframeBeforeRTP) - { - // for video key-frames we want to send the RTCP before the large key-frame - // if we have a 100 ms margin - now += RTCP_SEND_BEFORE_KEY_FRAME_MS; - } - - if(now > _nextTimeToSendRTCP) - { - return true; - - } else if(now < 0x0000ffff && _nextTimeToSendRTCP > 0xffff0000) // 65 sec margin - { - // wrap - return true; - } - return false; -} - -WebRtc_UWord32 -RTCPSender::LastSendReport( WebRtc_UWord32& lastRTCPTime) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - lastRTCPTime = _lastRTCPTime[0]; - return _lastSendReport[0]; -} - -WebRtc_UWord32 -RTCPSender::SendTimeOfSendReport(const WebRtc_UWord32 sendReport) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - // This is only saved when we are the sender - if((_lastSendReport[0] == 0) || (sendReport == 0)) - { - return 0; // will be ignored - } else - { - for(int i = 0; i < RTCP_NUMBER_OF_SR; ++i) - { - if( _lastSendReport[i] == sendReport) - { - return _lastRTCPTime[i]; - } - } - } - return 0; -} - -WebRtc_Word32 -RTCPSender::AddReportBlock(const WebRtc_UWord32 SSRC, - const RTCPReportBlock* reportBlock) -{ - if(reportBlock == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(_reportBlocks.Size() >= RTCP_MAX_REPORT_BLOCKS) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - RTCPReportBlock* copyReportBlock = new RTCPReportBlock(); - memcpy(copyReportBlock, reportBlock, sizeof(RTCPReportBlock)); - _reportBlocks.Insert(SSRC, copyReportBlock); - return 0; -} - -WebRtc_Word32 -RTCPSender::RemoveReportBlock(const WebRtc_UWord32 SSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - MapItem* item= _reportBlocks.Find(SSRC); - if(item) - { - RTCPReportBlock* ptr= (RTCPReportBlock*)(item->GetItem()); - if(ptr) - { - delete ptr; - } - _reportBlocks.Erase(item); - return 0; - } - return -1; -} - -WebRtc_Word32 -RTCPSender::BuildSR(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac, - const RTCPReportBlock* received) -{ - // sanity - if(pos + 52 >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -2; - } - WebRtc_UWord32 RTPtime; - WebRtc_UWord32 BackTimedNTPsec; - WebRtc_UWord32 BackTimedNTPfrac; - - WebRtc_UWord32 posNumberOfReportBlocks = pos; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80; - - // Sender report - rtcpbuffer[pos++]=(WebRtc_UWord8)200; - - for(int i = (RTCP_NUMBER_OF_SR-2); i >= 0; i--) - { - // shift old - _lastSendReport[i+1] = _lastSendReport[i]; - _lastRTCPTime[i+1] =_lastRTCPTime[i]; - } - - _lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac); // before video cam compensation - - if(_cameraDelayMS >= 0) - { - // fraction of a second as an unsigned word32 4.294 967 296E9 - WebRtc_UWord32 cameraDelayFixFrac = (WebRtc_UWord32)_cameraDelayMS* 4294967; // note camera delay can't be larger than +/-1000ms - if(NTPfrac > cameraDelayFixFrac) - { - // no problem just reduce the fraction part - BackTimedNTPfrac = NTPfrac - cameraDelayFixFrac; - BackTimedNTPsec = NTPsec; - } else - { - // we need to reduce the sec and add that sec to the frac - BackTimedNTPsec = NTPsec - 1; - BackTimedNTPfrac = 0xffffffff - (cameraDelayFixFrac - NTPfrac); - } - } else - { - // fraction of a second as an unsigned word32 4.294 967 296E9 - WebRtc_UWord32 cameraDelayFixFrac = (WebRtc_UWord32)(-_cameraDelayMS)* 4294967; // note camera delay can't be larger than +/-1000ms - if(NTPfrac > 0xffffffff - cameraDelayFixFrac) - { - // we need to add the sec and add that sec to the frac - BackTimedNTPsec = NTPsec + 1; - BackTimedNTPfrac = cameraDelayFixFrac + NTPfrac; // this will wrap but that is ok - } else - { - // no problem just add the fraction part - BackTimedNTPsec = NTPsec; - BackTimedNTPfrac = NTPfrac + cameraDelayFixFrac; - } - } - _lastSendReport[0] = (BackTimedNTPsec <<16) + (BackTimedNTPfrac >> 16); - - // RTP timestamp - // This should have a ramdom start value added - // RTP is counted from NTP not the acctual RTP - // This reflects the perfect RTP time - // we solve this by initiating RTP to our NTP :) - - WebRtc_UWord32 freqHz = 90000; // For video - if(_audio) - { - freqHz = _cbRtcpPrivate.CurrentSendFrequencyHz(); - RTPtime = ModuleRTPUtility::CurrentRTP(freqHz); - } - else // video - { - // used to be (WebRtc_UWord32)(((float)BackTimedNTPfrac/(float)FRAC)* 90000) - WebRtc_UWord32 tmp = 9*(BackTimedNTPfrac/429496); - RTPtime = BackTimedNTPsec*freqHz + tmp; - } - - - - - // Add sender data - // Save for our length field - pos++; - pos++; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - // NTP - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, BackTimedNTPsec); - pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, BackTimedNTPfrac); - pos += 4; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, RTPtime); - pos += 4; - - //sender's packet count - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _cbRtcpPrivate.PacketCountSent()); - pos += 4; - - //sender's octet count - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _cbRtcpPrivate.ByteCountSent()); - pos += 4; - - WebRtc_UWord8 numberOfReportBlocks = 0; - WebRtc_Word32 retVal = AddReportBlocks(rtcpbuffer, pos, numberOfReportBlocks, received, NTPsec, NTPfrac); - if(retVal < 0) - { - // - return retVal ; - } - rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks; - - WebRtc_UWord16 len = WebRtc_UWord16((pos/4) -1); - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len); - return 0; -} - - -WebRtc_Word32 -RTCPSender::BuildSDEC(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - WebRtc_UWord32 lengthCname =(WebRtc_UWord32)strlen((char*)_CNAME); - - // sanity max is 255 - if(lengthCname > RTCP_CNAME_SIZE) - { - lengthCname = RTCP_CNAME_SIZE; - } - // sanity - if(pos + 12+ lengthCname >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -2; - } - // SDEC Source Description - - // We always need to add SDES CNAME - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + 1 + _csrcCNAMEs.Size(); // source counts - rtcpbuffer[pos++]=(WebRtc_UWord8)202; - - // handle SDES length later on - WebRtc_UWord32 SDESLengthPos = pos; - pos++; - pos++; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // CNAME = 1 - rtcpbuffer[pos++]=(WebRtc_UWord8)1; - - // - rtcpbuffer[pos++]=(WebRtc_UWord8)lengthCname; - - WebRtc_UWord16 SDESLength = 10; - - memcpy(&rtcpbuffer[pos],_CNAME,lengthCname); - pos += lengthCname; - SDESLength += (WebRtc_UWord16)lengthCname; - - WebRtc_UWord16 padding =0; - - // We must have a zero field even if we have an even multiple of 4 bytes - if((pos % 4) ==0) - { - padding++; - rtcpbuffer[pos++]=0; - } - while((pos % 4) !=0) - { - padding++; - rtcpbuffer[pos++]=0; - } - SDESLength += padding; - - MapItem* item = _csrcCNAMEs.First(); - - for(int i = 0; item && i < _csrcCNAMEs.Size(); i++) - { - RTCPUtility::RTCPCnameInformation* cname = (RTCPUtility::RTCPCnameInformation*)(item->GetItem()); - WebRtc_UWord32 SSRC = item->GetUnsignedId(); - - // Add SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, SSRC); - pos += 4; - - // CNAME = 1 - rtcpbuffer[pos++]=(WebRtc_UWord8)1; - - rtcpbuffer[pos++]= cname->length; - - SDESLength += 6; - - memcpy(&rtcpbuffer[pos],cname->name, cname->length); - pos += cname->length; - SDESLength += cname->length; - - WebRtc_UWord16 padding =0; - - // We must have a zero field even if we have an even multiple of 4 bytes - if((pos % 4) ==0) - { - padding++; - rtcpbuffer[pos++]=0; - } - while((pos % 4) !=0) - { - padding++; - rtcpbuffer[pos++]=0; - } - SDESLength += padding; - - item = _csrcCNAMEs.Next(item); - } - WebRtc_UWord16 length = SDESLength; - length= (length/4) - 1; // in 32-bit words minus one and we dont count the header - - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+SDESLengthPos, length); - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildRR(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac, - const RTCPReportBlock* received) -{ - // sanity one block - if(pos + 32 >= IP_PACKET_SIZE) - { - return -2; - } - WebRtc_UWord32 posNumberOfReportBlocks = pos; - - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80; - rtcpbuffer[pos++]=(WebRtc_UWord8)201; - - // Save for our length field - pos++; - pos++; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - WebRtc_UWord8 numberOfReportBlocks = 0; - WebRtc_Word32 retVal = AddReportBlocks(rtcpbuffer, pos, numberOfReportBlocks, received, NTPsec, NTPfrac); - if(retVal < 0) - { - return retVal; - } - rtcpbuffer[posNumberOfReportBlocks] += numberOfReportBlocks; - - WebRtc_UWord16 len = WebRtc_UWord16((pos)/4 -1); - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+2, len); - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildPLI(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - // sanity - if(pos + 12 >= IP_PACKET_SIZE) - { - return -2; - } - // add picture loss indicator - WebRtc_UWord8 FMT = 1; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)206; - - //Used fixed length of 2 - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)(2); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildFIR(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, const WebRtc_UWord32 RTT) -{ - bool firRepeat = false; - WebRtc_UWord32 diff = ModuleRTPUtility::GetTimeInMS() - _lastTimeFIR; - if(diff < RTT + 3) // 3 is processing jitter - { - // we have recently sent a FIR - // don't send another - return 0; - - } else - { - if(diff < (RTT*2 + RTCP_MIN_FRAME_LENGTH_MS)) - { - // send a FIR_REPEAT instead of a FIR - firRepeat = true; - } - } - _lastTimeFIR = ModuleRTPUtility::GetTimeInMS(); - if(!firRepeat) - { - _sequenceNumberFIR++; // do not increase if repetition - } - - // sanity - if(pos + 20 >= IP_PACKET_SIZE) - { - return -2; - } - - // add full intra request indicator - WebRtc_UWord8 FMT = 4; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)206; - - //Length of 4 - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)(4); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // RFC 5104 4.3.1.2. Semantics - - // SSRC of media source - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - - // Additional Feedback Control Information (FCI) - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - rtcpbuffer[pos++]=(WebRtc_UWord8)(_sequenceNumberFIR); - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - return 0; -} - -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | First | Number | PictureID | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -WebRtc_Word32 -RTCPSender::BuildSLI(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, const WebRtc_UWord8 pictureID) -{ - // sanity - if(pos + 16 >= IP_PACKET_SIZE) - { - return -2; - } - // add slice loss indicator - WebRtc_UWord8 FMT = 2; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)206; - - //Used fixed length of 3 - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)(3); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - // Add first, number & picture ID 6 bits - // first = 0, 13 - bits - // number = 0x1fff, 13 - bits only ones for now - WebRtc_UWord32 sliField = (0x1fff << 6)+ (0x3f & pictureID); - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, sliField); - pos += 4; - return 0; -} - -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PB |0| Payload Type| Native RPSI bit string | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined per codec ... | Padding (0) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -/* -* Note: not generic made for VP8 -*/ -WebRtc_Word32 -RTCPSender::BuildRPSI(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord64 pictureID, - const WebRtc_UWord8 payloadType) -{ - // sanity - if(pos + 24 >= IP_PACKET_SIZE) - { - return -2; - } - // add Reference Picture Selection Indication - WebRtc_UWord8 FMT = 3; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)206; - - // calc length - WebRtc_UWord32 bitsRequired = 7; - WebRtc_UWord8 bytesRequired = 1; - while((pictureID>>bitsRequired) > 0) - { - bitsRequired += 7; - bytesRequired++; - } - - WebRtc_UWord8 size = 3; - if(bytesRequired > 6) - { - size = 5; - } else if(bytesRequired > 2) - { - size = 4; - } - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=size; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - // calc padding length - WebRtc_UWord8 paddingBytes = 4-((2+bytesRequired)%4); - if(paddingBytes == 4) - { - paddingBytes = 0; - } - // add padding length in bits - rtcpbuffer[pos] = paddingBytes*8; // padding can be 0, 8, 16 or 24 - pos++; - - // add payload type - rtcpbuffer[pos] = payloadType; - pos++; - - // add picture ID - for(int i = bytesRequired-1; i > 0; i--) - { - rtcpbuffer[pos] = 0x80 | WebRtc_UWord8(pictureID >> (i*7)); - pos++; - } - // add last byte of picture ID - rtcpbuffer[pos] = WebRtc_UWord8(pictureID & 0x7f); - pos++; - - // add padding - for(int j = 0; j send TMMBR - // If not an owner but the TMMBR would enter the TMMBN -> send TMMBR - - // About to send TMMBR, first run remote rate control - // to get a target bit rate. - _tmmbr_Send = _remoteRateControl.TargetBitRate(RTT) / 1000; - - // get current bounding set from RTCP receiver - bool tmmbrOwner = false; - TMMBRSet* candidateSet = _tmmbrHelp.CandidateSet(); // store in candidateSet, allocates one extra slot - - // holding _criticalSectionRTCPSender while calling RTCPreceiver which will accuire _criticalSectionRTCPReceiver - // is a potental deadlock but since RTCPreceiver is not doing the revese we should be fine - WebRtc_Word32 lengthOfBoundingSet = _cbRtcpPrivate.BoundingSet(tmmbrOwner, candidateSet); - - if(lengthOfBoundingSet > 0) - { - for (WebRtc_Word32 i = 0; i < lengthOfBoundingSet; i++) - { - if( candidateSet->ptrTmmbrSet[i] == _tmmbr_Send && - candidateSet->ptrPacketOHSet[i] == _packetOH_Send) - { - // do not send the same tuple - return 0; - } - } - if(!tmmbrOwner) - { - // use received bounding set as candidate set - // add current tuple - candidateSet->ptrTmmbrSet[lengthOfBoundingSet] = _tmmbr_Send; - candidateSet->ptrPacketOHSet[lengthOfBoundingSet] = _packetOH_Send; - candidateSet->ptrSsrcSet[lengthOfBoundingSet] = _SSRC; - int numCandidates = lengthOfBoundingSet+ 1; - - // find bounding set - TMMBRSet* boundingSet = NULL; - int numBoundingSet = _tmmbrHelp.FindTMMBRBoundingSet(boundingSet); - if(numBoundingSet > 0 || numBoundingSet <= numCandidates) - { - tmmbrOwner = _tmmbrHelp.IsOwner(_SSRC, numBoundingSet); - } - if(!tmmbrOwner) - { - // did not enter bounding set, no meaning to send this request - return 0; - } - } - } - - if(_tmmbr_Send) - { - // sanity - if(pos + 20 >= IP_PACKET_SIZE) - { - return -2; - } - // add TMMBR indicator - WebRtc_UWord8 FMT = 3; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)205; - - //Length of 4 - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)(4); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // RFC 5104 4.2.1.2. Semantics - - // SSRC of media source - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - - // Additional Feedback Control Information (FCI) - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - WebRtc_UWord32 bitRate = _tmmbr_Send*1000; - WebRtc_UWord32 mmbrExp = 0; - for(WebRtc_UWord32 i=0;i<64;i++) - { - if(bitRate <= ((WebRtc_UWord32)131071 << i)) - { - mmbrExp = i; - break; - } - } - WebRtc_UWord32 mmbrMantissa = (bitRate >> mmbrExp); - - rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03)); - rtcpbuffer[pos++]=(WebRtc_UWord8)(mmbrMantissa >> 7); - rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrMantissa << 1) + ((_packetOH_Send >> 8)& 0x01)); - rtcpbuffer[pos++]=(WebRtc_UWord8)(_packetOH_Send); - } - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildTMMBN(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - TMMBRSet* boundingSet = _tmmbrHelp.BoundingSetToSend(); - if(boundingSet == NULL) - { - return -1; - } - // sanity - if(pos + 12 + boundingSet->lengthOfSet*8 >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -2; - } - WebRtc_UWord8 FMT = 4; - // add TMMBN indicator - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)205; - - //Add length later - int posLength = pos; - pos++; - pos++; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // RFC 5104 4.2.2.2. Semantics - - // SSRC of media source - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - - // Additional Feedback Control Information (FCI) - int numBoundingSet = 0; - for(WebRtc_UWord32 n=0; n< boundingSet->lengthOfSet; n++) - { - if (boundingSet->ptrTmmbrSet[n] > 0) - { - WebRtc_UWord32 tmmbrSSRC = boundingSet->ptrSsrcSet[n]; - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, tmmbrSSRC); - pos += 4; - - WebRtc_UWord32 bitRate = boundingSet->ptrTmmbrSet[n] * 1000; - WebRtc_UWord32 mmbrExp = 0; - for(int i=0; i<64; i++) - { - if(bitRate <= ((WebRtc_UWord32)131071 << i)) - { - mmbrExp = i; - break; - } - } - WebRtc_UWord32 mmbrMantissa = (bitRate >> mmbrExp); - WebRtc_UWord32 measuredOH = boundingSet->ptrPacketOHSet[n]; - - rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrExp << 2) + ((mmbrMantissa >> 15) & 0x03)); - rtcpbuffer[pos++]=(WebRtc_UWord8)(mmbrMantissa >> 7); - rtcpbuffer[pos++]=(WebRtc_UWord8)((mmbrMantissa << 1) + ((measuredOH >> 8)& 0x01)); - rtcpbuffer[pos++]=(WebRtc_UWord8)(measuredOH); - numBoundingSet++; - } - } - WebRtc_UWord16 length= (WebRtc_UWord16)(2+2*numBoundingSet); - rtcpbuffer[posLength++]=(WebRtc_UWord8)(length>>8); - rtcpbuffer[posLength]=(WebRtc_UWord8)(length); - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildAPP(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - // sanity - if(_appData == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); - return -1; - } - if(pos + 12 + _appLength >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -2; - } - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + _appSubType; - - // Add APP ID - rtcpbuffer[pos++]=(WebRtc_UWord8)204; - - WebRtc_UWord16 length = (_appLength>>2) + 2; // include SSRC and name - rtcpbuffer[pos++]=(WebRtc_UWord8)(length>>8); - rtcpbuffer[pos++]=(WebRtc_UWord8)(length); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add our application name - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _appName); - pos += 4; - - // Add the data - memcpy(rtcpbuffer +pos, _appData,_appLength); - pos += _appLength; - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildNACK(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_Word32 nackSize, - const WebRtc_UWord16* nackList) -{ - // sanity - if(pos + 16 >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -2; - } - - // int size, WebRtc_UWord16* nackList - // add nack list - WebRtc_UWord8 FMT = 1; - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + FMT; - rtcpbuffer[pos++]=(WebRtc_UWord8)205; - - rtcpbuffer[pos++]=(WebRtc_UWord8) 0; - int nackSizePos = pos; - rtcpbuffer[pos++]=(WebRtc_UWord8)(3); //setting it to one kNACK signal as default - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - // add the list - int i = 0; - int numOfNackFields = 0; - while(nackSize > i && numOfNackFields < 253) - { - WebRtc_UWord16 nack = nackList[i]; - // put dow our sequence number - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+pos, nack); - pos += 2; - - i++; - numOfNackFields++; - if(nackSize > i) - { - bool moreThan16Away = (WebRtc_UWord16(nack+16) < nackList[i])?true: false; - if(!moreThan16Away) - { - // check for a wrap - if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff) - { - // wrap - moreThan16Away = true; - } - } - if(moreThan16Away) - { - // next is more than 16 away - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - } else - { - // build our bitmask - WebRtc_UWord16 bitmask = 0; - - bool within16Away = (WebRtc_UWord16(nack+16) > nackList[i])?true: false; - if(within16Away) - { - // check for a wrap - if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff) - { - // wrap - within16Away = false; - } - } - - while( nackSize > i && within16Away) - { - WebRtc_Word16 shift = (nackList[i]-nack)-1; - assert(!(shift > 15) && !(shift < 0)); - - bitmask += (1<< shift); - i++; - if(nackSize > i) - { - within16Away = (WebRtc_UWord16(nack+16) > nackList[i])?true: false; - if(within16Away) - { - // check for a wrap - if(WebRtc_UWord16(nack+16) > 0xff00 && nackList[i] < 0x0fff) - { - // wrap - within16Away = false; - } - } - } - } - ModuleRTPUtility::AssignUWord16ToBuffer(rtcpbuffer+pos, bitmask); - pos += 2; - } - // sanity do we have room from one more 4 byte block? - if(pos + 4 >= IP_PACKET_SIZE) - { - return -2; - } - } else - { - // no more in the list - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - } - } - rtcpbuffer[nackSizePos]=(WebRtc_UWord8)(2+numOfNackFields); - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildBYE(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - // sanity - if(pos + 8 >= IP_PACKET_SIZE) - { - return -2; - } - if(_includeCSRCs) - { - // Add a bye packet - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + 1 + _CSRCs; // number of SSRC+CSRCs - rtcpbuffer[pos++]=(WebRtc_UWord8)203; - - // length - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)(1 + _CSRCs); - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // add CSRCs - for(int i = 0; i < _CSRCs; i++) - { - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _CSRC[i]); - pos += 4; - } - } else - { - // Add a bye packet - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80 + 1; // number of SSRC+CSRCs - rtcpbuffer[pos++]=(WebRtc_UWord8)203; - - // length - rtcpbuffer[pos++]=(WebRtc_UWord8)0; - rtcpbuffer[pos++]=(WebRtc_UWord8)1; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - } - return 0; -} - -WebRtc_Word32 -RTCPSender::BuildVoIPMetric(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos) -{ - // sanity - if(pos + 44 >= IP_PACKET_SIZE) - { - return -2; - } - - // Add XR header - rtcpbuffer[pos++]=(WebRtc_UWord8)0x80; - rtcpbuffer[pos++]=(WebRtc_UWord8)207; - - WebRtc_UWord32 XRLengthPos = pos; - - // handle length later on - pos++; - pos++; - - // Add our own SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _SSRC); - pos += 4; - - // Add a VoIP metrics block - rtcpbuffer[pos++]=7; - rtcpbuffer[pos++]=0; - rtcpbuffer[pos++]=0; - rtcpbuffer[pos++]=8; - - // Add the remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - rtcpbuffer[pos++] = _xrVoIPMetric.lossRate; - rtcpbuffer[pos++] = _xrVoIPMetric.discardRate; - rtcpbuffer[pos++] = _xrVoIPMetric.burstDensity; - rtcpbuffer[pos++] = _xrVoIPMetric.gapDensity; - - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.burstDuration >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.burstDuration); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.gapDuration >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.gapDuration); - - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.roundTripDelay >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.roundTripDelay); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.endSystemDelay >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.endSystemDelay); - - rtcpbuffer[pos++] = _xrVoIPMetric.signalLevel; - rtcpbuffer[pos++] = _xrVoIPMetric.noiseLevel; - rtcpbuffer[pos++] = _xrVoIPMetric.RERL; - rtcpbuffer[pos++] = _xrVoIPMetric.Gmin; - - rtcpbuffer[pos++] = _xrVoIPMetric.Rfactor; - rtcpbuffer[pos++] = _xrVoIPMetric.extRfactor; - rtcpbuffer[pos++] = _xrVoIPMetric.MOSLQ; - rtcpbuffer[pos++] = _xrVoIPMetric.MOSCQ; - - rtcpbuffer[pos++] = _xrVoIPMetric.RXconfig; - rtcpbuffer[pos++] = 0; // reserved - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBnominal >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBnominal); - - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBmax >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBmax); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBabsMax >> 8); - rtcpbuffer[pos++] = (WebRtc_UWord8)(_xrVoIPMetric.JBabsMax); - - rtcpbuffer[XRLengthPos]=(WebRtc_UWord8)(0); - rtcpbuffer[XRLengthPos+1]=(WebRtc_UWord8)(10); - return 0; -} - -WebRtc_Word32 -RTCPSender::SendRTCP(const WebRtc_UWord32 packetTypeFlags, - const WebRtc_Word32 nackSize, // NACK - const WebRtc_UWord16* nackList, // NACK - const WebRtc_UWord32 RTT, // FIR - const WebRtc_UWord64 pictureID) // SLI & RPSI -{ - WebRtc_UWord32 rtcpPacketTypeFlags = packetTypeFlags; - WebRtc_UWord32 pos = 0; - WebRtc_UWord8 rtcpbuffer[IP_PACKET_SIZE]; - - if(_method == kRtcpOff) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); - return -1; - } - - do // only to be able to use break :) (and the critsect must be inside its own scope) - { - // collect the received information - RTCPReportBlock received; - bool hasReceived = false; - WebRtc_UWord32 NTPsec = 0; - WebRtc_UWord32 NTPfrac = 0; - - if( _method == kRtcpCompound || - rtcpPacketTypeFlags & kRtcpReport || - rtcpPacketTypeFlags & kRtcpSr || - rtcpPacketTypeFlags & kRtcpRr) - { - // get statistics from our RTPreceiver outside critsect - if(_cbRtcpPrivate.ReportBlockStatistics(&received.fractionLost, - &received.cumulativeLost, - &received.extendedHighSeqNum, - &received.jitter) == 0) - { - hasReceived = true; - - WebRtc_UWord32 lastReceivedRRNTPsecs = 0; - WebRtc_UWord32 lastReceivedRRNTPfrac = 0; - WebRtc_UWord32 remoteSR = 0; - - // ok even if we have not received a SR, we will send 0 in that case - _cbRtcpPrivate.LastReceivedNTP(lastReceivedRRNTPsecs, lastReceivedRRNTPfrac, remoteSR); - - // get our NTP as late as possible to avoid a race - ModuleRTPUtility::CurrentNTP(NTPsec, NTPfrac); - - // Delay since last received report - WebRtc_UWord32 delaySinceLastReceivedSR = 0; - if((lastReceivedRRNTPsecs !=0) || (lastReceivedRRNTPfrac !=0)) - { - // get the 16 lowest bits of seconds and the 16 higest bits of fractions - WebRtc_UWord32 now=NTPsec&0x0000FFFF; - now <<=16; - now += (NTPfrac&0xffff0000)>>16; - - WebRtc_UWord32 receiveTime = lastReceivedRRNTPsecs&0x0000FFFF; - receiveTime <<=16; - receiveTime += (lastReceivedRRNTPfrac&0xffff0000)>>16; - - delaySinceLastReceivedSR = now-receiveTime; - } - received.delaySinceLastSR = delaySinceLastReceivedSR; - received.lastSR = remoteSR; - } else - { - // we need to send our NTP even if we dont have received any reports - ModuleRTPUtility::CurrentNTP(NTPsec, NTPfrac); - } - } - - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(_TMMBR ) // attach TMMBR to send and receive reports - { - rtcpPacketTypeFlags |= kRtcpTmmbr; - } - if(_appSend) - { - rtcpPacketTypeFlags |= kRtcpApp; - _appSend = false; - } - if(_xrSendVoIPMetric) - { - rtcpPacketTypeFlags |= kRtcpXrVoipMetric; - _xrSendVoIPMetric = false; - } - if(_sendTMMBN) // set when having received a TMMBR - { - rtcpPacketTypeFlags |= kRtcpTmmbn; - _sendTMMBN = false; - } - - if(_method == kRtcpCompound) - { - if(_sending) - { - rtcpPacketTypeFlags |= kRtcpSr; - } else - { - rtcpPacketTypeFlags |= kRtcpRr; - } - } else if(_method == kRtcpNonCompound) - { - if(rtcpPacketTypeFlags & kRtcpReport) - { - if(_sending) - { - rtcpPacketTypeFlags |= kRtcpSr; - } else - { - rtcpPacketTypeFlags |= kRtcpRr; - } - } - } - if( rtcpPacketTypeFlags & kRtcpRr || - rtcpPacketTypeFlags & kRtcpSr) - { - // generate next time to send a RTCP report - // seeded from RTP constructor - WebRtc_Word32 random = rand() % 1000; - WebRtc_Word32 timeToNext = RTCP_INTERVAL_AUDIO_MS; - - if(_audio) - { - timeToNext = (RTCP_INTERVAL_AUDIO_MS/2) + (RTCP_INTERVAL_AUDIO_MS*random/1000); - }else - { - WebRtc_UWord32 minIntervalMs = RTCP_INTERVAL_AUDIO_MS; - if(_sending) - { - // calc bw for video 360/sendBW in kbit/s - WebRtc_Word32 sendBitrateKbit = _cbRtcpPrivate.BitrateSent()/1000; - if(sendBitrateKbit != 0) - { - minIntervalMs = 360000/sendBitrateKbit; - } - } - if(minIntervalMs > RTCP_INTERVAL_VIDEO_MS) - { - minIntervalMs = RTCP_INTERVAL_VIDEO_MS; - } - timeToNext = (minIntervalMs/2) + (minIntervalMs*random/1000); - } - _nextTimeToSendRTCP = ModuleRTPUtility::GetTimeInMS() + timeToNext; - } - - // if the data does not fitt in the packet we fill it as much as possible - WebRtc_Word32 buildVal = 0; - - if(rtcpPacketTypeFlags & kRtcpSr) - { - if(hasReceived) - { - buildVal = BuildSR(rtcpbuffer, pos, NTPsec, NTPfrac, &received); - } else - { - buildVal = BuildSR(rtcpbuffer, pos, NTPsec, NTPfrac); - } - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - buildVal = BuildSDEC(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - - }else if(rtcpPacketTypeFlags & kRtcpRr) - { - if(hasReceived) - { - buildVal = BuildRR(rtcpbuffer, pos, NTPsec, NTPfrac,&received); - }else - { - buildVal = BuildRR(rtcpbuffer, pos, NTPsec, NTPfrac); - } - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - // only of set - if(_CNAME[0] != 0) - { - buildVal = BuildSDEC(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - } - } - } - if(rtcpPacketTypeFlags & kRtcpPli) - { - buildVal = BuildPLI(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpFir) - { - buildVal = BuildFIR(rtcpbuffer, pos, RTT); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpSli) - { - buildVal = BuildSLI(rtcpbuffer, pos, (WebRtc_UWord8)pictureID); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpRpsi) - { - const WebRtc_Word8 payloadType = _cbRtcpPrivate.SendPayloadType(); - if(payloadType == -1) - { - return -1; - } - buildVal = BuildRPSI(rtcpbuffer, pos, pictureID, (WebRtc_UWord8)payloadType); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpBye) - { - buildVal = BuildBYE(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpApp) - { - buildVal = BuildAPP(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpTmmbr) - { - buildVal = BuildTMMBR(rtcpbuffer, pos, RTT); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpTmmbn) - { - buildVal = BuildTMMBN(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpNack) - { - buildVal = BuildNACK(rtcpbuffer, pos, nackSize, nackList); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - if(rtcpPacketTypeFlags & kRtcpXrVoipMetric) - { - buildVal = BuildVoIPMetric(rtcpbuffer, pos); - if(buildVal == -1) - { - return -1; // error - - }else if(buildVal == -2) - { - break; // out of buffer - } - } - }while (false); - - return SendToNetwork(rtcpbuffer, (WebRtc_UWord16)pos); -} - -WebRtc_Word32 -RTCPSender::SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 length) -{ - CriticalSectionScoped lock(_criticalSectionTransport); - if(_cbTransport) - { - if(_cbTransport->SendRTCPPacket(_id, dataBuffer, length) > 0) - { - return 0; - } - } - return -1; -} - -WebRtc_Word32 -RTCPSender::SetCSRCStatus(const bool include) -{ - _includeCSRCs = include; - return 0; -} - -WebRtc_Word32 -RTCPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength) -{ - if(arrLength > kRtpCsrcSize) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - assert(false); - return -1; - } - - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - for(int i = 0; i < arrLength;i++) - { - _CSRC[i] = arrOfCSRC[i]; - } - _CSRCs = arrLength; - return 0; -} - -WebRtc_Word32 -RTCPSender::SetApplicationSpecificData(const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord8* data, - const WebRtc_UWord16 length) -{ - if(length %4 != 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if(_appData) - { - delete [] _appData; - } - - _appSend = true; - _appSubType = subType; - _appName = name; - _appData = new WebRtc_UWord8[length]; - _appLength = length; - memcpy(_appData, data, length); - return 0; -} - -WebRtc_Word32 -RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - memcpy(&_xrVoIPMetric, VoIPMetric, sizeof(RTCPVoIPMetric)); - - _xrSendVoIPMetric = true; - return 0; -} - -// called under critsect _criticalSectionRTCPSender -WebRtc_Word32 -RTCPSender::AddReportBlocks(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - WebRtc_UWord8& numberOfReportBlocks, - const RTCPReportBlock* received, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac) -{ - // sanity one block - if(pos + 24 >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - numberOfReportBlocks = _reportBlocks.Size(); - if(received) - { - // add our multiple RR to numberOfReportBlocks - numberOfReportBlocks++; - } - - if(received) - { - // answer to the one that sends to me - _lastRTCPTime[0] = ModuleRTPUtility::ConvertNTPTimeToMS(NTPsec, NTPfrac); - - // Remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, _remoteSSRC); - pos += 4; - - // fraction lost - rtcpbuffer[pos++]=received->fractionLost; - - // cumulative loss - ModuleRTPUtility::AssignUWord24ToBuffer(rtcpbuffer+pos, received->cumulativeLost); - pos += 3; - - // extended highest seq_no, contain the highest sequence number received - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->extendedHighSeqNum); - pos += 4; - - //Jitter - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->jitter); - pos += 4; - - // Last SR timestamp, our NTP time when we received the last report - // This is the value that we read from the send report packet not when we received it... - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->lastSR); - pos += 4; - - // Delay since last received report,time since we received the report - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, received->delaySinceLastSR); - pos += 4; - } - - if(pos + _reportBlocks.Size()*24 >= IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - MapItem* item = _reportBlocks.First(); - for(int i = 0; i < _reportBlocks.Size() && item; i++) - { - // we can have multiple report block in a conference - WebRtc_UWord32 remoteSSRC = item->GetId(); - RTCPReportBlock* reportBlock = (RTCPReportBlock*)item->GetItem(); - if(reportBlock) - { - // Remote SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, remoteSSRC); - pos += 4; - - // fraction lost - rtcpbuffer[pos++]=(WebRtc_UWord8)(reportBlock->fractionLost); - - // cumulative loss - ModuleRTPUtility::AssignUWord24ToBuffer(rtcpbuffer+pos, reportBlock->cumulativeLost); - pos += 3; - - // extended highest seq_no, contain the highest sequence number received - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, reportBlock->extendedHighSeqNum); - pos += 4; - - //Jitter - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, reportBlock->jitter); - pos += 4; - - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, reportBlock->lastSR); - pos += 4; - - ModuleRTPUtility::AssignUWord32ToBuffer(rtcpbuffer+pos, reportBlock->delaySinceLastSR); - pos += 4; - } - item = _reportBlocks.Next(item); - } - return pos; -} - -// no callbacks allowed inside this function -WebRtc_Word32 -RTCPSender::SetTMMBN(const TMMBRSet* boundingSet, - const WebRtc_UWord32 maxBitrateKbit) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - - if (0 == _tmmbrHelp.SetTMMBRBoundingSetToSend(boundingSet, maxBitrateKbit)) - { - _sendTMMBN = true; - return 0; - } - return -1; -} - -WebRtc_Word32 -RTCPSender::RequestTMMBR(WebRtc_UWord32 estimatedBW, WebRtc_UWord32 packetOH) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - if(_TMMBR) - { - _tmmbr_Send = estimatedBW; - _packetOH_Send = packetOH; - - return 0; - } - return -1; -} - -RateControlRegion -RTCPSender::UpdateOverUseState(const RateControlInput& rateControlInput, bool& firstOverUse) -{ - CriticalSectionScoped lock(_criticalSectionRTCPSender); - return _remoteRateControl.Update(rateControlInput, firstOverUse); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h deleted file mode 100644 index c920a1137..000000000 --- a/modules/rtp_rtcp/source/rtcp_sender.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_SENDER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_SENDER_H_ - -#include "typedefs.h" -#include "rtp_utility.h" -#include "map_wrapper.h" -#include "rtp_rtcp_defines.h" -#include "rtp_rtcp_private.h" -#include "remote_rate_control.h" - -namespace webrtc { -class RTCPSender -{ -public: - RTCPSender(const WebRtc_Word32 id, const bool audio, ModuleRtpRtcpPrivate& callback); - virtual ~RTCPSender(); - - void ChangeUniqueId(const WebRtc_Word32 id); - - WebRtc_Word32 Init(); - - WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport); - - RTCPMethod Status() const; - WebRtc_Word32 SetRTCPStatus(const RTCPMethod method); - - bool Sending() const; - WebRtc_Word32 SetSendingStatus(const bool enabled); // combine the functions - - WebRtc_Word32 SetNackStatus(const bool enable); - - void SetSSRC( const WebRtc_UWord32 ssrc); - - WebRtc_Word32 SetRemoteSSRC( const WebRtc_UWord32 ssrc); - - WebRtc_Word32 SetCameraDelay(const WebRtc_Word32 delayMS); - - WebRtc_Word32 CNAME(WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - WebRtc_Word32 SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - - WebRtc_Word32 AddMixedCNAME(const WebRtc_UWord32 SSRC, - const WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - - WebRtc_Word32 RemoveMixedCNAME(const WebRtc_UWord32 SSRC); - - WebRtc_UWord32 SendTimeOfSendReport(const WebRtc_UWord32 sendReport); - - bool TimeToSendRTCPReport(const bool sendKeyframeBeforeRTP = false) const; - - WebRtc_UWord32 LastSendReport(WebRtc_UWord32& lastRTCPTime); - - WebRtc_Word32 SendRTCP(const WebRtc_UWord32 rtcpPacketTypeFlags, - const WebRtc_Word32 nackSize = 0, - const WebRtc_UWord16* nackList = 0, - const WebRtc_UWord32 RTT = 0, - const WebRtc_UWord64 pictureID = 0); - - WebRtc_Word32 AddReportBlock(const WebRtc_UWord32 SSRC, - const RTCPReportBlock* receiveBlock); - - WebRtc_Word32 RemoveReportBlock(const WebRtc_UWord32 SSRC); - - /* - * TMMBR - */ - bool TMMBR() const; - - WebRtc_Word32 SetTMMBRStatus(const bool enable); - - WebRtc_Word32 SetTMMBN(const TMMBRSet* boundingSet, - const WebRtc_UWord32 maxBitrateKbit); - - WebRtc_Word32 RequestTMMBR(const WebRtc_UWord32 estimatedBW, - const WebRtc_UWord32 packetOH); - - /* - * - */ - - WebRtc_Word32 SetApplicationSpecificData(const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord8* data, - const WebRtc_UWord16 length); - - WebRtc_Word32 SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric); - - WebRtc_Word32 SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength); - - WebRtc_Word32 SetCSRCStatus(const bool include); - - /* - * New bandwidth estimation - */ - - RateControlRegion UpdateOverUseState(const RateControlInput& rateControlInput, bool& firstOverUse); - -private: - WebRtc_Word32 SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 length); - - void UpdatePacketRate(); - - WebRtc_Word32 AddReportBlocks(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - WebRtc_UWord8& numberOfReportBlocks, - const RTCPReportBlock* received, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac); - - WebRtc_Word32 BuildSR(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac, - const RTCPReportBlock* received = NULL); - - WebRtc_Word32 BuildRR(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord32 NTPsec, - const WebRtc_UWord32 NTPfrac, - const RTCPReportBlock* received = NULL); - - WebRtc_Word32 BuildSDEC(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildPLI(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildTMMBR(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos, WebRtc_UWord32 RTT); - WebRtc_Word32 BuildTMMBN(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildAPP(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildVoIPMetric(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildBYE(WebRtc_UWord8* rtcpbuffer, WebRtc_UWord32& pos); - WebRtc_Word32 BuildFIR(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord32 RTT); - WebRtc_Word32 BuildSLI(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord8 pictureID); - WebRtc_Word32 BuildRPSI(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_UWord64 pictureID, - const WebRtc_UWord8 payloadType); - - WebRtc_Word32 BuildNACK(WebRtc_UWord8* rtcpbuffer, - WebRtc_UWord32& pos, - const WebRtc_Word32 nackSize, - const WebRtc_UWord16* nackList); - -private: - WebRtc_Word32 _id; - const bool _audio; - RTCPMethod _method; - - ModuleRtpRtcpPrivate& _cbRtcpPrivate; - - CriticalSectionWrapper& _criticalSectionTransport; - Transport* _cbTransport; - - CriticalSectionWrapper& _criticalSectionRTCPSender; - bool _usingNack; - bool _sending; - bool _sendTMMBN; - bool _TMMBR; - - WebRtc_UWord32 _nextTimeToSendRTCP; - - WebRtc_UWord32 _SSRC; - WebRtc_UWord32 _remoteSSRC; // SSRC that we receive on our RTP channel - WebRtc_UWord8 _CNAME[RTCP_CNAME_SIZE]; - - MapWrapper _reportBlocks; // map of SSRC to RTCPReportBlock - MapWrapper _csrcCNAMEs; // map of SSRC to Cnames - - WebRtc_Word32 _cameraDelayMS; - - // Sent - WebRtc_UWord32 _lastSendReport[RTCP_NUMBER_OF_SR]; // allow packet loss and RTT above 1 sec - WebRtc_UWord32 _lastRTCPTime[RTCP_NUMBER_OF_SR]; - - // send CSRCs - WebRtc_UWord8 _CSRCs; - WebRtc_UWord32 _CSRC[kRtpCsrcSize]; - bool _includeCSRCs; - - // Full intra request - WebRtc_UWord8 _sequenceNumberFIR; - WebRtc_UWord32 _lastTimeFIR; - - // TMMBR - TMMBRHelp _tmmbrHelp; - WebRtc_UWord32 _tmmbr_Send; - WebRtc_UWord32 _packetOH_Send; - RemoteRateControl _remoteRateControl; - - // APP - bool _appSend; - WebRtc_UWord8 _appSubType; - WebRtc_UWord32 _appName; - WebRtc_UWord8* _appData; - WebRtc_UWord16 _appLength; - - // XR VoIP metric - bool _xrSendVoIPMetric; - RTCPVoIPMetric _xrVoIPMetric; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_SENDER_H_ diff --git a/modules/rtp_rtcp/source/rtcp_utility.cc b/modules/rtp_rtcp/source/rtcp_utility.cc deleted file mode 100644 index f1e7b8530..000000000 --- a/modules/rtp_rtcp/source/rtcp_utility.cc +++ /dev/null @@ -1,1350 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtcp_utility.h" - -#include // memcpy -#include // ceil -#include - - -namespace webrtc { -RTCPUtility::RTCPCnameInformation::RTCPCnameInformation() : - length(0) -{ - memset(name, 0, sizeof(name)); -} - -RTCPUtility::RTCPCnameInformation::~RTCPCnameInformation() -{ -} - -/////////// -// RTCPParserV2 : currently read only - -RTCPUtility::RTCPParserV2::RTCPParserV2( const WebRtc_UWord8* rtcpData, - size_t rtcpDataLength, - bool rtcpReducedSizeEnable) - : - _ptrRTCPDataBegin(rtcpData), - _RTCPReducedSizeEnable(rtcpReducedSizeEnable), - _ptrRTCPDataEnd(rtcpData + rtcpDataLength), - - _validPacket(false), - _ptrRTCPData(rtcpData), - - _state(State_TopLevel), - _numberOfBlocks(0), - - _packetType(kRtcpNotValidCode) -{ - Validate(); -} - -RTCPUtility::RTCPParserV2::~RTCPParserV2() -{ -} - -ptrdiff_t -RTCPUtility::RTCPParserV2::LengthLeft() const -{ - return (_ptrRTCPDataEnd- _ptrRTCPData); -} - -RTCPUtility::RTCPPacketTypes -RTCPUtility::RTCPParserV2::PacketType() const -{ - return _packetType; -} - -const RTCPUtility::RTCPPacket& -RTCPUtility::RTCPParserV2::Packet() const -{ - return _packet; -} - -RTCPUtility::RTCPPacketTypes -RTCPUtility::RTCPParserV2::Begin() -{ - _ptrRTCPData = _ptrRTCPDataBegin; - - return Iterate(); -} - -RTCPUtility::RTCPPacketTypes -RTCPUtility::RTCPParserV2::Iterate() -{ - // Reset packet type - _packetType = kRtcpNotValidCode; - - if (IsValid()) - { - switch (_state) - { - case State_TopLevel: - IterateTopLevel(); - break; - case State_ReportBlockItem: - IterateReportBlockItem(); - break; - case State_SDESChunk: - IterateSDESChunk(); - break; - case State_BYEItem: - IterateBYEItem(); - break; - case State_RTPFB_NACKItem: - IterateNACKItem(); - break; - case State_RTPFB_TMMBRItem: - IterateTMMBRItem(); - break; - case State_RTPFB_TMMBNItem: - IterateTMMBNItem(); - break; - case State_PSFB_SLIItem: - IterateSLIItem(); - break; - case State_PSFB_RPSIItem: - IterateRPSIItem(); - break; - case State_PSFB_FIRItem: - IterateFIRItem(); - break; - case State_AppItem: - IterateAppItem(); - break; - default: - assert(false); // Invalid state! - break; - } - } - return _packetType; -} - -void -RTCPUtility::RTCPParserV2::IterateTopLevel() -{ - for (;;) - { - RTCPCommonHeader header; - - const bool success = RTCPParseCommonHeader(_ptrRTCPData, - _ptrRTCPDataEnd, - header); - - if (!success) - { - return; - } - _ptrRTCPBlockEnd = _ptrRTCPData + header.LengthInOctets; - if (_ptrRTCPBlockEnd > _ptrRTCPDataEnd) - { - // Bad block! - return; - } - - switch (header.PT) - { - case PT_SR: - { - // number of Report blocks - _numberOfBlocks = header.IC; - ParseSR(); - return; - } - case PT_RR: - { - // number of Report blocks - _numberOfBlocks = header.IC; - ParseRR(); - return; - } - case PT_SDES: - { - // number of SDES blocks - _numberOfBlocks = header.IC; - const bool ok = ParseSDES(); - if (!ok) - { - // Nothing supported found, continue to next block! - break; - } - return; - } - case PT_BYE: - { - _numberOfBlocks = header.IC; - const bool ok = ParseBYE(); - if (!ok) - { - // Nothing supported found, continue to next block! - break; - } - return; - } - case PT_RTPFB: // Fall through! - case PT_PSFB: - { - const bool ok = ParseFBCommon(header); - if (!ok) - { - // Nothing supported found, continue to next block! - break; - } - - return; - } - case PT_APP: - { - const bool ok = ParseAPP(header); - if (!ok) - { - // Nothing supported found, continue to next block! - break; - } - return; - } - case PT_XR: - { - const bool ok = ParseXR(); - if (!ok) - { - // Nothing supported found, continue to next block! - break; - } - return; - } - default: - // Not supported! Skip! - EndCurrentBlock(); - break; - } - } -} - -void -RTCPUtility::RTCPParserV2::IterateReportBlockItem() -{ - const bool success = ParseReportBlockItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateSDESChunk() -{ - const bool success = ParseSDESChunk(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateBYEItem() -{ - const bool success = ParseBYEItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateNACKItem() -{ - const bool success = ParseNACKItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateTMMBRItem() -{ - const bool success = ParseTMMBRItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateTMMBNItem() -{ - const bool success = ParseTMMBNItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateSLIItem() -{ - const bool success = ParseSLIItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateRPSIItem() -{ - const bool success = ParseRPSIItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateFIRItem() -{ - const bool success = ParseFIRItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::IterateAppItem() -{ - const bool success = ParseAPPItem(); - if (!success) - { - Iterate(); - } -} - -void -RTCPUtility::RTCPParserV2::Validate() -{ - if (_ptrRTCPData == NULL) - { - return; // NOT VALID - } - - RTCPCommonHeader header; - const bool success = RTCPParseCommonHeader(_ptrRTCPDataBegin, - _ptrRTCPDataEnd, - header); - - if (!success) - { - return; // NOT VALID! - } - - // * if (!reducedSize) : first packet must be RR or SR. - // - // * The padding bit (P) should be zero for the first packet of a - // compound RTCP packet because padding should only be applied, - // if it is needed, to the last packet. (NOT CHECKED!) - // - // * The length fields of the individual RTCP packets must add up - // to the overall length of the compound RTCP packet as - // received. This is a fairly strong check. (NOT CHECKED!) - - if (!_RTCPReducedSizeEnable) - { - if ((header.PT != PT_SR) && (header.PT != PT_RR)) - { - return; // NOT VALID - } - } - - _validPacket = true; -} - -bool -RTCPUtility::RTCPParserV2::IsValid() const -{ - return _validPacket; -} - -void -RTCPUtility::RTCPParserV2::EndCurrentBlock() -{ - _ptrRTCPData = _ptrRTCPBlockEnd; -} - -bool -RTCPUtility::RTCPParseCommonHeader( const WebRtc_UWord8* ptrDataBegin, - const WebRtc_UWord8* ptrDataEnd, - RTCPCommonHeader& parsedHeader) -{ - if (!ptrDataBegin || !ptrDataEnd) - { - return false; - } - - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |V=2|P| IC | PT | length | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // - // Common header for all RTCP packets, 4 octets. - - if ((ptrDataEnd - ptrDataBegin) < 4) - { - return false; - } - - parsedHeader.V = ptrDataBegin[0] >> 6; - parsedHeader.P = ((ptrDataBegin[0] & 0x20) == 0) ? false : true; - parsedHeader.IC = ptrDataBegin[0] & 0x1f; - parsedHeader.PT = ptrDataBegin[1]; - - parsedHeader.LengthInOctets = (ptrDataBegin[2] << 8) + ptrDataBegin[3] + 1; - parsedHeader.LengthInOctets *= 4; - - if(parsedHeader.LengthInOctets == 0) - { - return false; - } - // Check if RTP version field == 2 - if (parsedHeader.V != 2) - { - return false; - } - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseRR() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - return false; - } - - - _ptrRTCPData += 4; // Skip header - - _packetType = kRtcpRrCode; - - _packet.RR.SenderSSRC = *_ptrRTCPData++ << 24; - _packet.RR.SenderSSRC += *_ptrRTCPData++ << 16; - _packet.RR.SenderSSRC += *_ptrRTCPData++ << 8; - _packet.RR.SenderSSRC += *_ptrRTCPData++; - - _packet.RR.NumberOfReportBlocks = _numberOfBlocks; - - // State transition - _state = State_ReportBlockItem; - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseSR() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 28) - { - EndCurrentBlock(); - return false; - } - - _ptrRTCPData += 4; // Skip header - - _packetType = kRtcpSrCode; - - _packet.SR.SenderSSRC = *_ptrRTCPData++ << 24; - _packet.SR.SenderSSRC += *_ptrRTCPData++ << 16; - _packet.SR.SenderSSRC += *_ptrRTCPData++ << 8; - _packet.SR.SenderSSRC += *_ptrRTCPData++; - - _packet.SR.NTPMostSignificant = *_ptrRTCPData++ << 24; - _packet.SR.NTPMostSignificant += *_ptrRTCPData++ << 16; - _packet.SR.NTPMostSignificant += *_ptrRTCPData++ << 8; - _packet.SR.NTPMostSignificant += *_ptrRTCPData++; - - _packet.SR.NTPLeastSignificant = *_ptrRTCPData++ << 24; - _packet.SR.NTPLeastSignificant += *_ptrRTCPData++ << 16; - _packet.SR.NTPLeastSignificant += *_ptrRTCPData++ << 8; - _packet.SR.NTPLeastSignificant += *_ptrRTCPData++; - - _packet.SR.RTPTimestamp = *_ptrRTCPData++ << 24; - _packet.SR.RTPTimestamp += *_ptrRTCPData++ << 16; - _packet.SR.RTPTimestamp += *_ptrRTCPData++ << 8; - _packet.SR.RTPTimestamp += *_ptrRTCPData++; - - _packet.SR.SenderPacketCount = *_ptrRTCPData++ << 24; - _packet.SR.SenderPacketCount += *_ptrRTCPData++ << 16; - _packet.SR.SenderPacketCount += *_ptrRTCPData++ << 8; - _packet.SR.SenderPacketCount += *_ptrRTCPData++; - - _packet.SR.SenderOctetCount = *_ptrRTCPData++ << 24; - _packet.SR.SenderOctetCount += *_ptrRTCPData++ << 16; - _packet.SR.SenderOctetCount += *_ptrRTCPData++ << 8; - _packet.SR.SenderOctetCount += *_ptrRTCPData++; - - _packet.SR.NumberOfReportBlocks = _numberOfBlocks; - - // State transition - if(_numberOfBlocks != 0) - { - _state = State_ReportBlockItem; - }else - { - // don't go to state report block item if 0 report blocks - _state = State_TopLevel; - EndCurrentBlock(); - } - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseReportBlockItem() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 24 || _numberOfBlocks <= 0) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - _packet.ReportBlockItem.SSRC = *_ptrRTCPData++ << 24; - _packet.ReportBlockItem.SSRC += *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.SSRC += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.SSRC += *_ptrRTCPData++; - - _packet.ReportBlockItem.FractionLost = *_ptrRTCPData++; - - _packet.ReportBlockItem.CumulativeNumOfPacketsLost = *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.CumulativeNumOfPacketsLost += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.CumulativeNumOfPacketsLost += *_ptrRTCPData++; - - _packet.ReportBlockItem.ExtendedHighestSequenceNumber = *_ptrRTCPData++ << 24; - _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++; - - _packet.ReportBlockItem.Jitter = *_ptrRTCPData++ << 24; - _packet.ReportBlockItem.Jitter += *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.Jitter += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.Jitter += *_ptrRTCPData++; - - _packet.ReportBlockItem.LastSR = *_ptrRTCPData++ << 24; - _packet.ReportBlockItem.LastSR += *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.LastSR += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.LastSR += *_ptrRTCPData++; - - _packet.ReportBlockItem.DelayLastSR = *_ptrRTCPData++ << 24; - _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++ << 16; - _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++ << 8; - _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++; - - _numberOfBlocks--; - _packetType = kRtcpReportBlockItemCode; - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseSDES() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - _ptrRTCPData += 4; // Skip header - - _state = State_SDESChunk; - _packetType = kRtcpSdesCode; - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseSDESChunk() -{ - if(_numberOfBlocks <= 0) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - _numberOfBlocks--; - - // Find CName item in a SDES chunk. - while (_ptrRTCPData < _ptrRTCPBlockEnd) - { - const ptrdiff_t dataLen = _ptrRTCPBlockEnd - _ptrRTCPData; - if (dataLen < 4) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - WebRtc_UWord32 SSRC = *_ptrRTCPData++ << 24; - SSRC += *_ptrRTCPData++ << 16; - SSRC += *_ptrRTCPData++ << 8; - SSRC += *_ptrRTCPData++; - - const bool foundCname = ParseSDESItem(); - if (foundCname) - { - _packet.CName.SenderSSRC = SSRC; // Add SSRC - return true; - } - } - _state = State_TopLevel; - - EndCurrentBlock(); - return false; -} - -bool -RTCPUtility::RTCPParserV2::ParseSDESItem() -{ - // Find CName - // Only the CNAME item is mandatory. RFC 3550 page 46 - bool foundCName = false; - - size_t itemOctetsRead = 0; - while (_ptrRTCPData < _ptrRTCPBlockEnd) - { - const WebRtc_UWord8 tag = *_ptrRTCPData++; - ++itemOctetsRead; - - if (tag == 0) - { - // End tag! 4 oct aligned - while ((itemOctetsRead++ % 4) != 0) - { - ++_ptrRTCPData; - } - return foundCName; - } - - if (_ptrRTCPData < _ptrRTCPBlockEnd) - { - const WebRtc_UWord8 len = *_ptrRTCPData++; - ++itemOctetsRead; - - if (tag == 1) - { - // CNAME - - // Sanity - if ((_ptrRTCPData + len) >= _ptrRTCPBlockEnd) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - for (unsigned int i = 0; i < len; ++i) - { - const WebRtc_UWord8 c = _ptrRTCPData[i]; - if ((c < ' ') || (c > '{') || (c == '%') || (c == '\\')) - { - // Illegal char - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packet.CName.CName[i] = c; - } - - _packetType = kRtcpSdesChunkCode; - _packet.CName.CNameLength = len; - - foundCName = true; - } - - _ptrRTCPData += len; - itemOctetsRead += len; - } - } - - // No end tag found! - _state = State_TopLevel; - - EndCurrentBlock(); - return false; -} - -bool -RTCPUtility::RTCPParserV2::ParseBYE() -{ - _ptrRTCPData += 4; // Skip header - - _state = State_BYEItem; - - return ParseBYEItem(); -} - -bool -RTCPUtility::RTCPParserV2::ParseBYEItem() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - if (length < 4 || _numberOfBlocks == 0) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpByeCode; - - _packet.BYE.SenderSSRC = *_ptrRTCPData++ << 24; - _packet.BYE.SenderSSRC += *_ptrRTCPData++ << 16; - _packet.BYE.SenderSSRC += *_ptrRTCPData++ << 8; - _packet.BYE.SenderSSRC += *_ptrRTCPData++; - - // we can have several CSRCs attached - - // sanity - if(length >= 4*_numberOfBlocks) - { - _ptrRTCPData += (_numberOfBlocks -1)*4; - } - _numberOfBlocks = 0; - - return true; -} -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P|reserved | PT=XR=207 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - : report blocks : - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -bool RTCPUtility::RTCPParserV2::ParseXR() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - EndCurrentBlock(); - return false; - } - - _ptrRTCPData += 4; // Skip header - - _packet.XR.OriginatorSSRC = *_ptrRTCPData++ << 24; - _packet.XR.OriginatorSSRC += *_ptrRTCPData++ << 16; - _packet.XR.OriginatorSSRC += *_ptrRTCPData++ << 8; - _packet.XR.OriginatorSSRC += *_ptrRTCPData++; - - return ParseXRItem(); -} -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | BT | type-specific | block length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - : type-specific block contents : - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -bool -RTCPUtility::RTCPParserV2::ParseXRItem() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 4) // - { - EndCurrentBlock(); - return false; - } - - WebRtc_UWord8 blockType = *_ptrRTCPData++; - WebRtc_UWord8 typeSpecific = *_ptrRTCPData++; - - WebRtc_UWord16 blockLength = *_ptrRTCPData++ << 8; - blockLength = *_ptrRTCPData++; - - if(blockType == 7 && typeSpecific == 0) - { - if(blockLength != 8) - { - EndCurrentBlock(); - return false; - } - return ParseXRVOIPMetricItem(); - }else - { - EndCurrentBlock(); - return false; - } -} -/* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | BT=7 | reserved | block length = 8 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of source | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | loss rate | discard rate | burst density | gap density | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | burst duration | gap duration | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | round trip delay | end system delay | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | signal level | noise level | RERL | Gmin | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | R factor | ext. R factor | MOS-LQ | MOS-CQ | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | RX config | reserved | JB nominal | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | JB maximum | JB abs max | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ -bool -RTCPUtility::RTCPParserV2::ParseXRVOIPMetricItem() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 28) - { - EndCurrentBlock(); - return false; - } - _packetType = kRtcpXrVoipMetricCode; - - _packet.XRVOIPMetricItem.SSRC = *_ptrRTCPData++ << 24; - _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++ << 16; - _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.lossRate = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.discardRate = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.burstDensity = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.gapDensity = *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.burstDuration = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.burstDuration += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.gapDuration = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.gapDuration += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.roundTripDelay = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.roundTripDelay += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.endSystemDelay = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.endSystemDelay += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.signalLevel = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.noiseLevel = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.RERL = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.Gmin = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.Rfactor = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.extRfactor = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.MOSLQ = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.MOSCQ = *_ptrRTCPData++; - _packet.XRVOIPMetricItem.RXconfig = *_ptrRTCPData++; - _ptrRTCPData++; // skip reserved - - _packet.XRVOIPMetricItem.JBnominal = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.JBnominal += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.JBmax = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.JBmax += *_ptrRTCPData++; - - _packet.XRVOIPMetricItem.JBabsMax = *_ptrRTCPData++ << 8; - _packet.XRVOIPMetricItem.JBabsMax += *_ptrRTCPData++; - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseFBCommon(const RTCPCommonHeader& header) -{ - assert((header.PT == PT_RTPFB) || (header.PT == PT_PSFB)); // Parser logic check - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 12) // 4 * 3, RFC4585 section 6.1 - { - EndCurrentBlock(); - return false; - } - - _ptrRTCPData += 4; // Skip RTCP header - - WebRtc_UWord32 senderSSRC = *_ptrRTCPData++ << 24; - senderSSRC += *_ptrRTCPData++ << 16; - senderSSRC += *_ptrRTCPData++ << 8; - senderSSRC += *_ptrRTCPData++; - - WebRtc_UWord32 mediaSSRC = *_ptrRTCPData++ << 24; - mediaSSRC += *_ptrRTCPData++ << 16; - mediaSSRC += *_ptrRTCPData++ << 8; - mediaSSRC += *_ptrRTCPData++; - - if (header.PT == PT_RTPFB) - { - // Transport layer feedback - - switch (header.IC) - { - case 1: - { - // NACK - _packetType = kRtcpRtpfbNackCode; - _packet.NACK.SenderSSRC = senderSSRC; - _packet.NACK.MediaSSRC = mediaSSRC; - - _state = State_RTPFB_NACKItem; - - return true; - } - case 2: - { - // used to be ACK is this code point, which is removed - // conficts with http://tools.ietf.org/html/draft-levin-avt-rtcp-burst-00 - break; - } - case 3: - { - // TMMBR - _packetType = kRtcpRtpfbTmmbrCode; - _packet.TMMBR.SenderSSRC = senderSSRC; - _packet.TMMBR.MediaSSRC = mediaSSRC; - - _state = State_RTPFB_TMMBRItem; - - return true; - } - case 4: - { - // TMMBN - _packetType = kRtcpRtpfbTmmbnCode; - _packet.TMMBN.SenderSSRC = senderSSRC; - _packet.TMMBN.MediaSSRC = mediaSSRC; - - _state = State_RTPFB_TMMBNItem; - - return true; - } - case 5: - { - // RTCP-SR-REQ Rapid Synchronisation of RTP Flows - // draft-perkins-avt-rapid-rtp-sync-03.txt - // trigger a new RTCP SR - _packetType = kRtcpRtpfbSrReqCode; - - // Note: No state transition, SR REQ is empty! - return true; - } - default: - break; - } - EndCurrentBlock(); - return false; - } - else if (header.PT == PT_PSFB) - { - // Payload specific feedback - switch (header.IC) - { - case 1: - // PLI - _packetType = kRtcpPsfbPliCode; - _packet.PLI.SenderSSRC = senderSSRC; - _packet.PLI.MediaSSRC = mediaSSRC; - - // Note: No state transition, PLI FCI is empty! - return true; - case 2: - // SLI - _packetType = kRtcpPsfbSliCode; - _packet.SLI.SenderSSRC = senderSSRC; - _packet.SLI.MediaSSRC = mediaSSRC; - - _state = State_PSFB_SLIItem; - - return true; - case 3: - _packetType = kRtcpPsfbRpsiCode; - _packet.RPSI.SenderSSRC = senderSSRC; - _packet.RPSI.MediaSSRC = mediaSSRC; - - _state = State_PSFB_RPSIItem; - return true; - case 4: - // FIR - _packetType = kRtcpPsfbFirCode; - _packet.FIR.SenderSSRC = senderSSRC; - _packet.FIR.MediaSSRC = mediaSSRC; - - _state = State_PSFB_FIRItem; - return true; - default: - break; - } - - EndCurrentBlock(); - return false; - } - else - { - assert(false); - - EndCurrentBlock(); - return false; - } -} - -bool -RTCPUtility::RTCPParserV2::ParseRPSIItem() -{ - // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI) - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PB |0| Payload Type| Native RPSI bit string | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined per codec ... | Padding (0) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 4) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - if(length > 2+RTCP_RPSI_DATA_SIZE) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpPsfbRpsiCode; - - WebRtc_UWord8 paddingBits = *_ptrRTCPData++; - _packet.RPSI.PayloadType = *_ptrRTCPData++; - - memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length-2); - - _packet.RPSI.NumberOfValidBits = WebRtc_UWord16(length-2)*8 - paddingBits; - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseNACKItem() -{ - // RFC 4585 6.2.1. Generic NACK - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 4) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpRtpfbNackItemCode; - - _packet.NACKItem.PacketID = *_ptrRTCPData++ << 8; - _packet.NACKItem.PacketID += *_ptrRTCPData++; - - _packet.NACKItem.BitMask = *_ptrRTCPData++ << 8; - _packet.NACKItem.BitMask += *_ptrRTCPData++; - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseTMMBRItem() -{ - // RFC 5104 4.2.1. Temporary Maximum Media Stream Bit Rate Request (TMMBR) - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpRtpfbTmmbrItemCode; - - _packet.TMMBRItem.SSRC = *_ptrRTCPData++ << 24; - _packet.TMMBRItem.SSRC += *_ptrRTCPData++ << 16; - _packet.TMMBRItem.SSRC += *_ptrRTCPData++ << 8; - _packet.TMMBRItem.SSRC += *_ptrRTCPData++; - - WebRtc_UWord8 mxtbrExp = (_ptrRTCPData[0] >> 2) & 0x3F; - - WebRtc_UWord32 mxtbrMantissa = (_ptrRTCPData[0] & 0x03) << 15; - mxtbrMantissa += (_ptrRTCPData[1] << 7); - mxtbrMantissa += (_ptrRTCPData[2] >> 1) & 0x7F; - - WebRtc_UWord32 measuredOH = (_ptrRTCPData[2] & 0x01) << 8; - measuredOH += _ptrRTCPData[3]; - - _ptrRTCPData += 4; // Fwd read data - - _packet.TMMBRItem.MaxTotalMediaBitRate = ((mxtbrMantissa << mxtbrExp) / 1000); - _packet.TMMBRItem.MeasuredOverhead = measuredOH; - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseTMMBNItem() -{ - // RFC 5104 4.2.2. Temporary Maximum Media Stream Bit Rate Notification (TMMBN) - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpRtpfbTmmbnItemCode; - - _packet.TMMBNItem.SSRC = *_ptrRTCPData++ << 24; - _packet.TMMBNItem.SSRC += *_ptrRTCPData++ << 16; - _packet.TMMBNItem.SSRC += *_ptrRTCPData++ << 8; - _packet.TMMBNItem.SSRC += *_ptrRTCPData++; - - WebRtc_UWord8 mxtbrExp = (_ptrRTCPData[0] >> 2) & 0x3F; - - WebRtc_UWord32 mxtbrMantissa = (_ptrRTCPData[0] & 0x03) << 15; - mxtbrMantissa += (_ptrRTCPData[1] << 7); - mxtbrMantissa += (_ptrRTCPData[2] >> 1) & 0x7F; - - WebRtc_UWord32 measuredOH = (_ptrRTCPData[2] & 0x01) << 8; - measuredOH += _ptrRTCPData[3]; - - _ptrRTCPData += 4; // Fwd read data - - _packet.TMMBNItem.MaxTotalMediaBitRate = ((mxtbrMantissa << mxtbrExp) / 1000); - _packet.TMMBNItem.MeasuredOverhead = measuredOH; - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseSLIItem() -{ - // RFC 5104 6.3.2. Slice Loss Indication (SLI) - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | First | Number | PictureID | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 4) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - _packetType = kRtcpPsfbSliItemCode; - - WebRtc_UWord32 buffer; - buffer = *_ptrRTCPData++ << 24; - buffer += *_ptrRTCPData++ << 16; - buffer += *_ptrRTCPData++ << 8; - buffer += *_ptrRTCPData++; - - _packet.SLIItem.FirstMB = WebRtc_UWord16((buffer>>19) & 0x1fff); - _packet.SLIItem.NumberOfMB = WebRtc_UWord16((buffer>>6) & 0x1fff); - _packet.SLIItem.PictureId = WebRtc_UWord8(buffer & 0x3f); - - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseFIRItem() -{ - // RFC 5104 4.3.1. Full Intra Request (FIR) - - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 8) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - - _packetType = kRtcpPsfbFirItemCode; - - _packet.FIRItem.SSRC = *_ptrRTCPData++ << 24; - _packet.FIRItem.SSRC += *_ptrRTCPData++ << 16; - _packet.FIRItem.SSRC += *_ptrRTCPData++ << 8; - _packet.FIRItem.SSRC += *_ptrRTCPData++; - - _packet.FIRItem.CommandSequenceNumber = *_ptrRTCPData++; - _ptrRTCPData += 3; // Skip "Reserved" bytes. - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseAPP( const RTCPCommonHeader& header) -{ - ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - - if (length < 12) // 4 * 3, RFC 3550 6.7 APP: Application-Defined RTCP Packet - { - EndCurrentBlock(); - return false; - } - - _ptrRTCPData += 4; // Skip RTCP header - - WebRtc_UWord32 senderSSRC = *_ptrRTCPData++ << 24; - senderSSRC += *_ptrRTCPData++ << 16; - senderSSRC += *_ptrRTCPData++ << 8; - senderSSRC += *_ptrRTCPData++; - - WebRtc_UWord32 name = *_ptrRTCPData++ << 24; - name += *_ptrRTCPData++ << 16; - name += *_ptrRTCPData++ << 8; - name += *_ptrRTCPData++; - - length = _ptrRTCPBlockEnd - _ptrRTCPData; - - _packetType = kRtcpAppCode; - - _packet.APP.SubType = header.IC; - _packet.APP.Name = name; - - _state = State_AppItem; - return true; -} - -bool -RTCPUtility::RTCPParserV2::ParseAPPItem() -{ - const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - if (length < 4) - { - _state = State_TopLevel; - - EndCurrentBlock(); - return false; - } - _packetType = kRtcpAppItemCode; - - if(length > kRtcpAppCode_DATA_SIZE) - { - memcpy(_packet.APP.Data, _ptrRTCPData, kRtcpAppCode_DATA_SIZE); - _packet.APP.Size = kRtcpAppCode_DATA_SIZE; - _ptrRTCPData += kRtcpAppCode_DATA_SIZE; - }else - { - memcpy(_packet.APP.Data, _ptrRTCPData, length); - _packet.APP.Size = (WebRtc_UWord16)length; - _ptrRTCPData += length; - } - return true; -} - -RTCPUtility::RTCPPacketIterator::RTCPPacketIterator( - WebRtc_UWord8* rtcpData, - size_t rtcpDataLength) - : - _ptrBegin(rtcpData), - _ptrEnd(rtcpData + rtcpDataLength) -{ -} - -RTCPUtility::RTCPPacketIterator::~RTCPPacketIterator() -{ -} - -const RTCPUtility::RTCPCommonHeader* -RTCPUtility::RTCPPacketIterator::Begin() -{ - _ptrBlock = _ptrBegin; - - return Iterate(); -} - -const RTCPUtility::RTCPCommonHeader* -RTCPUtility::RTCPPacketIterator::Iterate() -{ - const bool success = RTCPParseCommonHeader(_ptrBlock, _ptrEnd, _header); - if (!success) - { - _ptrBlock = NULL; - return NULL; - } - _ptrBlock += _header.LengthInOctets; - - if (_ptrBlock > _ptrEnd) - { - _ptrBlock = NULL; - return NULL; - } - - return &_header; -} - -const RTCPUtility::RTCPCommonHeader* -RTCPUtility::RTCPPacketIterator::Current() -{ - if (!_ptrBlock) - { - return NULL; - } - - return &_header; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_utility.h b/modules/rtp_rtcp/source/rtcp_utility.h deleted file mode 100644 index decb606ee..000000000 --- a/modules/rtp_rtcp/source/rtcp_utility.h +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_UTILITY_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_UTILITY_H_ - -#include // size_t, ptrdiff_t - -#include "typedefs.h" -#include "rtp_rtcp_config.h" -#include "rtp_rtcp_defines.h" - -namespace webrtc { -namespace RTCPUtility { - // CNAME - class RTCPCnameInformation - { - public: - RTCPCnameInformation(); - ~RTCPCnameInformation(); - - WebRtc_UWord8 name[RTCP_CNAME_SIZE]; - WebRtc_UWord8 length; - }; - - struct RTCPPacketRR - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord8 NumberOfReportBlocks; - }; - struct RTCPPacketSR - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord8 NumberOfReportBlocks; - - // sender info - WebRtc_UWord32 NTPMostSignificant; - WebRtc_UWord32 NTPLeastSignificant; - WebRtc_UWord32 RTPTimestamp; - WebRtc_UWord32 SenderPacketCount; - WebRtc_UWord32 SenderOctetCount; - }; - struct RTCPPacketReportBlockItem - { - // report block - WebRtc_UWord32 SSRC; - WebRtc_UWord8 FractionLost; - WebRtc_UWord32 CumulativeNumOfPacketsLost; - WebRtc_UWord32 ExtendedHighestSequenceNumber; - WebRtc_UWord32 Jitter; - WebRtc_UWord32 LastSR; - WebRtc_UWord32 DelayLastSR; - }; - - struct RTCPPacketSDESCName - { - // RFC3550 - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord8 CName[RTCP_CNAME_SIZE]; - WebRtc_UWord8 CNameLength; - }; - - struct RTCPPacketBYE - { - WebRtc_UWord32 SenderSSRC; - }; - struct RTCPPacketXR - { - // RFC 3611 - WebRtc_UWord32 OriginatorSSRC; - }; - struct RTCPPacketXRVOIPMetricItem - { - // RFC 3611 4.7 - WebRtc_UWord32 SSRC; - WebRtc_UWord8 lossRate; - WebRtc_UWord8 discardRate; - WebRtc_UWord8 burstDensity; - WebRtc_UWord8 gapDensity; - WebRtc_UWord16 burstDuration; - WebRtc_UWord16 gapDuration; - WebRtc_UWord16 roundTripDelay; - WebRtc_UWord16 endSystemDelay; - WebRtc_UWord8 signalLevel; - WebRtc_UWord8 noiseLevel; - WebRtc_UWord8 RERL; - WebRtc_UWord8 Gmin; - WebRtc_UWord8 Rfactor; - WebRtc_UWord8 extRfactor; - WebRtc_UWord8 MOSLQ; - WebRtc_UWord8 MOSCQ; - WebRtc_UWord8 RXconfig; - WebRtc_UWord16 JBnominal; - WebRtc_UWord16 JBmax; - WebRtc_UWord16 JBabsMax; - }; - - struct RTCPPacketRTPFBNACK - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; - }; - struct RTCPPacketRTPFBNACKItem - { - // RFC4585 - WebRtc_UWord16 PacketID; - WebRtc_UWord16 BitMask; - }; - - struct RTCPPacketRTPFBTMMBR - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; // zero! - }; - struct RTCPPacketRTPFBTMMBRItem - { - // RFC5104 - WebRtc_UWord32 SSRC; - WebRtc_UWord32 MaxTotalMediaBitRate; // In Kbit/s - WebRtc_UWord32 MeasuredOverhead; - }; - - struct RTCPPacketRTPFBTMMBN - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; // zero! - }; - struct RTCPPacketRTPFBTMMBNItem - { - // RFC5104 - WebRtc_UWord32 SSRC; // "Owner" - WebRtc_UWord32 MaxTotalMediaBitRate; - WebRtc_UWord32 MeasuredOverhead; - }; - - struct RTCPPacketPSFBFIR - { - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; // zero! - }; - struct RTCPPacketPSFBFIRItem - { - // RFC5104 - WebRtc_UWord32 SSRC; - WebRtc_UWord8 CommandSequenceNumber; - }; - - struct RTCPPacketPSFBPLI - { - // RFC4585 - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; - }; - - struct RTCPPacketPSFBSLI - { - // RFC4585 - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; - }; - struct RTCPPacketPSFBSLIItem - { - // RFC4585 - WebRtc_UWord16 FirstMB; - WebRtc_UWord16 NumberOfMB; - WebRtc_UWord8 PictureId; - }; - struct RTCPPacketPSFBRPSI - { - // RFC4585 - WebRtc_UWord32 SenderSSRC; - WebRtc_UWord32 MediaSSRC; - WebRtc_UWord8 PayloadType; - WebRtc_UWord16 NumberOfValidBits; - WebRtc_UWord8 NativeBitString[RTCP_RPSI_DATA_SIZE]; - }; - - // generic name APP - struct RTCPPacketAPP - { - WebRtc_UWord8 SubType; - WebRtc_UWord32 Name; - WebRtc_UWord8 Data[kRtcpAppCode_DATA_SIZE]; - WebRtc_UWord16 Size; - }; - - union RTCPPacket - { - RTCPPacketRR RR; - RTCPPacketSR SR; - RTCPPacketReportBlockItem ReportBlockItem; - - RTCPPacketSDESCName CName; - RTCPPacketBYE BYE; - - RTCPPacketRTPFBNACK NACK; - RTCPPacketRTPFBNACKItem NACKItem; - - RTCPPacketPSFBPLI PLI; - RTCPPacketPSFBSLI SLI; - RTCPPacketPSFBSLIItem SLIItem; - RTCPPacketPSFBRPSI RPSI; - - RTCPPacketRTPFBTMMBR TMMBR; - RTCPPacketRTPFBTMMBRItem TMMBRItem; - RTCPPacketRTPFBTMMBN TMMBN; - RTCPPacketRTPFBTMMBNItem TMMBNItem; - RTCPPacketPSFBFIR FIR; - RTCPPacketPSFBFIRItem FIRItem; - - RTCPPacketXR XR; - RTCPPacketXRVOIPMetricItem XRVOIPMetricItem; - - RTCPPacketAPP APP; - }; - - enum RTCPPacketTypes - { - kRtcpNotValidCode, - - // RFC3550 - kRtcpRrCode, - kRtcpSrCode, - kRtcpReportBlockItemCode, - - kRtcpSdesCode, - kRtcpSdesChunkCode, - kRtcpByeCode, - - // RFC4585 - kRtcpRtpfbNackCode, - kRtcpRtpfbNackItemCode, - - kRtcpPsfbPliCode, - kRtcpPsfbRpsiCode, - kRtcpPsfbSliCode, - kRtcpPsfbSliItemCode, - - // RFC5104 - kRtcpRtpfbTmmbrCode, - kRtcpRtpfbTmmbrItemCode, - kRtcpRtpfbTmmbnCode, - kRtcpRtpfbTmmbnItemCode, - kRtcpPsfbFirCode, - kRtcpPsfbFirItemCode, - - // draft-perkins-avt-rapid-rtp-sync - kRtcpRtpfbSrReqCode, - - // RFC 3611 - kRtcpXrVoipMetricCode, - - kRtcpAppCode, - kRtcpAppItemCode, - }; - - struct RTCPRawPacket - { - const WebRtc_UWord8* _ptrPacketBegin; - const WebRtc_UWord8* _ptrPacketEnd; - }; - - struct RTCPModRawPacket - { - WebRtc_UWord8* _ptrPacketBegin; - WebRtc_UWord8* _ptrPacketEnd; - }; - - struct RTCPCommonHeader - { - WebRtc_UWord8 V; // Version - bool P; // Padding - WebRtc_UWord8 IC; // Item count/subtype - WebRtc_UWord8 PT; // Packet Type - WebRtc_UWord16 LengthInOctets; - }; - - enum RTCPPT - { - PT_SR = 200, - PT_RR = 201, - PT_SDES = 202, - PT_BYE = 203, - PT_APP = 204, - PT_RTPFB = 205, - PT_PSFB = 206, - PT_XR = 207 - }; - - bool RTCPParseCommonHeader( const WebRtc_UWord8* ptrDataBegin, - const WebRtc_UWord8* ptrDataEnd, - RTCPCommonHeader& parsedHeader); - - class RTCPParserV2 - { - public: - RTCPParserV2(const WebRtc_UWord8* rtcpData, - size_t rtcpDataLength, - bool rtcpReducedSizeEnable); // Set to true, to allow non-compound RTCP! - ~RTCPParserV2(); - - RTCPPacketTypes PacketType() const; - const RTCPPacket& Packet() const; - const RTCPRawPacket& RawPacket() const; - ptrdiff_t LengthLeft() const; - - bool IsValid() const; - - RTCPPacketTypes Begin(); - RTCPPacketTypes Iterate(); - - private: - enum ParseState - { - State_TopLevel, // Top level packet - State_ReportBlockItem, // SR/RR report block - State_SDESChunk, // SDES chunk - State_BYEItem, // BYE item - State_RTPFB_NACKItem, // NACK FCI item - State_RTPFB_TMMBRItem, // TMMBR FCI item - State_RTPFB_TMMBNItem, // TMMBN FCI item - State_PSFB_SLIItem, // SLI FCI item - State_PSFB_RPSIItem, // RPSI FCI item - State_PSFB_FIRItem, // FIR FCI item - State_XRItem, - State_AppItem - }; - - private: - void IterateTopLevel(); - void IterateReportBlockItem(); - void IterateSDESChunk(); - void IterateBYEItem(); - void IterateNACKItem(); - void IterateTMMBRItem(); - void IterateTMMBNItem(); - void IterateSLIItem(); - void IterateRPSIItem(); - void IterateFIRItem(); - void IterateAppItem(); - - void Validate(); - void EndCurrentBlock(); - - bool ParseRR(); - bool ParseSR(); - bool ParseReportBlockItem(); - - bool ParseSDES(); - bool ParseSDESChunk(); - bool ParseSDESItem(); - - bool ParseBYE(); - bool ParseBYEItem(); - - bool ParseXR(); - bool ParseXRItem(); - bool ParseXRVOIPMetricItem(); - - bool ParseFBCommon(const RTCPCommonHeader& header); - bool ParseNACKItem(); - bool ParseTMMBRItem(); - bool ParseTMMBNItem(); - bool ParseSLIItem(); - bool ParseRPSIItem(); - bool ParseFIRItem(); - - bool ParseAPP(const RTCPCommonHeader& header); - bool ParseAPPItem(); - - private: - const WebRtc_UWord8* const _ptrRTCPDataBegin; - const bool _RTCPReducedSizeEnable; - const WebRtc_UWord8* const _ptrRTCPDataEnd; - - bool _validPacket; - const WebRtc_UWord8* _ptrRTCPData; - const WebRtc_UWord8* _ptrRTCPBlockEnd; - - ParseState _state; - WebRtc_UWord8 _numberOfBlocks; - - RTCPPacketTypes _packetType; - RTCPPacket _packet; - }; - - class RTCPPacketIterator - { - public: - RTCPPacketIterator(WebRtc_UWord8* rtcpData, - size_t rtcpDataLength); - ~RTCPPacketIterator(); - - const RTCPCommonHeader* Begin(); - const RTCPCommonHeader* Iterate(); - const RTCPCommonHeader* Current(); - - private: - WebRtc_UWord8* const _ptrBegin; - WebRtc_UWord8* const _ptrEnd; - - WebRtc_UWord8* _ptrBlock; - - RTCPCommonHeader _header; - }; -} // RTCPUtility -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_UTILITY_H_ diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.cc b/modules/rtp_rtcp/source/rtp_format_vp8.cc deleted file mode 100644 index c0ed4927c..000000000 --- a/modules/rtp_rtcp/source/rtp_format_vp8.cc +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_format_vp8.h" - -#include // assert -#include // memcpy - -namespace webrtc { - -// Define how the VP8PacketizerModes are implemented. -// Modes are: kStrict, kAggregate, kSloppy. -const RtpFormatVp8::AggregationMode RtpFormatVp8::aggr_modes_[kNumModes] = - { kAggrNone, kAggrPartitions, kAggrFragments }; -const bool RtpFormatVp8::balance_modes_[kNumModes] = - { true, false, false }; -const bool RtpFormatVp8::separate_first_modes_[kNumModes] = - { true, false, false }; - -RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size, - const RTPVideoHeaderVP8& hdr_info, - const RTPFragmentationHeader& fragmentation, - VP8PacketizerMode mode) - : payload_data_(payload_data), - payload_size_(static_cast(payload_size)), - payload_bytes_sent_(0), - part_ix_(0), - beginning_(true), - first_fragment_(true), - vp8_header_bytes_(1), - aggr_mode_(aggr_modes_[mode]), - balance_(balance_modes_[mode]), - separate_first_(separate_first_modes_[mode]), - hdr_info_(hdr_info) -{ - part_info_ = fragmentation; -} - -RtpFormatVp8::RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size, - const RTPVideoHeaderVP8& hdr_info) - : payload_data_(payload_data), - payload_size_(static_cast(payload_size)), - part_info_(), - payload_bytes_sent_(0), - part_ix_(0), - beginning_(true), - first_fragment_(true), - vp8_header_bytes_(1), - aggr_mode_(aggr_modes_[kSloppy]), - balance_(balance_modes_[kSloppy]), - separate_first_(separate_first_modes_[kSloppy]), - hdr_info_(hdr_info) -{ - part_info_.VerifyAndAllocateFragmentationHeader(1); - part_info_.fragmentationLength[0] = payload_size; - part_info_.fragmentationOffset[0] = 0; -} - -int RtpFormatVp8::CalcNextSize(int max_payload_len, int remaining_bytes, - bool split_payload) const -{ - if (max_payload_len == 0 || remaining_bytes == 0) - { - return 0; - } - if (!split_payload) - { - return max_payload_len >= remaining_bytes ? remaining_bytes : 0; - } - - if (balance_) - { - // Balance payload sizes to produce (almost) equal size - // fragments. - // Number of fragments for remaining_bytes: - int num_frags = remaining_bytes / max_payload_len + 1; - // Number of bytes in this fragment: - return static_cast(static_cast(remaining_bytes) - / num_frags + 0.5); - } - else - { - return max_payload_len >= remaining_bytes ? remaining_bytes - : max_payload_len; - } -} - -int RtpFormatVp8::NextPacket(int max_payload_len, WebRtc_UWord8* buffer, - int* bytes_to_send, bool* last_packet) -{ - const int num_partitions = part_info_.fragmentationVectorSize; - int send_bytes = 0; // How much data to send in this packet. - bool split_payload = true; // Splitting of partitions is initially allowed. - int remaining_in_partition = part_info_.fragmentationOffset[part_ix_] - - payload_bytes_sent_ + part_info_.fragmentationLength[part_ix_] + - FirstHeaderExtraLength(); // Add header extra length to payload length. - int rem_payload_len = max_payload_len - vp8_header_bytes_; - - while (int next_size = CalcNextSize(rem_payload_len, remaining_in_partition, - split_payload)) - { - send_bytes += next_size; - rem_payload_len -= next_size; - remaining_in_partition -= next_size; - - if (remaining_in_partition == 0 && !(beginning_ && separate_first_)) - { - // Advance to next partition? - // Check that there are more partitions; verify that we are either - // allowed to aggregate fragments, or that we are allowed to - // aggregate intact partitions and that we started this packet - // with an intact partition (indicated by first_fragment_ == true). - if (part_ix_ + 1 < num_partitions && - ((aggr_mode_ == kAggrFragments) || - (aggr_mode_ == kAggrPartitions && first_fragment_))) - { - remaining_in_partition - = part_info_.fragmentationLength[++part_ix_]; - // Disallow splitting unless kAggrFragments. In kAggrPartitions, - // we can only aggregate intact partitions. - split_payload = (aggr_mode_ == kAggrFragments); - } - } - else if (balance_ && remaining_in_partition > 0) - { - break; - } - } - if (remaining_in_partition == 0) - { - ++part_ix_; // Advance to next partition. - } - - send_bytes -= FirstHeaderExtraLength(); // Remove the extra length again. - assert(send_bytes > 0); - const bool end_of_fragment = (remaining_in_partition == 0); - // Write the payload header and the payload to buffer. - *bytes_to_send = WriteHeaderAndPayload(send_bytes, end_of_fragment, buffer, - max_payload_len); - if (*bytes_to_send < 0) - { - return -1; - } - - *last_packet = (payload_bytes_sent_ >= payload_size_); - assert(!*last_packet || (payload_bytes_sent_ == payload_size_)); - return 0; -} - -int RtpFormatVp8::WriteHeaderAndPayload(int payload_bytes, - bool end_of_fragment, - WebRtc_UWord8* buffer, - int buffer_length) -{ - // Write the VP8 payload header. - // 0 1 2 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | RSV |I|N|FI |B| PictureID (1 or 2 octets) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - if (payload_bytes < 0) - { - return -1; - } - if (payload_bytes_sent_ + payload_bytes > payload_size_) - { - return -1; - } - - buffer[0] = 0; - if (hdr_info_.nonReference) buffer[0] |= (0x01 << 3); // N - if (!first_fragment_) buffer[0] |= (0x01 << 2); // FI - if (!end_of_fragment) buffer[0] |= (0x01 << 1); // FI - if (beginning_) buffer[0] |= 0x01; // B - - int pic_id_len = WritePictureID(&buffer[vp8_header_bytes_], - buffer_length - vp8_header_bytes_); - if (pic_id_len < 0) return pic_id_len; // error - if (pic_id_len > 0) buffer[0] |= (0x01 << 4); // I - - if (vp8_header_bytes_ + pic_id_len + payload_bytes > buffer_length) - { - return -1; - } - memcpy(&buffer[vp8_header_bytes_ + pic_id_len], - &payload_data_[payload_bytes_sent_], payload_bytes); - - beginning_ = false; // next packet cannot be first packet in frame - // next packet starts new fragment if this ended one - first_fragment_ = end_of_fragment; - payload_bytes_sent_ += payload_bytes; - - // Return total length of written data. - return payload_bytes + vp8_header_bytes_ + pic_id_len; -} - -int RtpFormatVp8::WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const -{ - const WebRtc_UWord16 pic_id = - static_cast (hdr_info_.pictureId); - int picture_id_len = PictureIdLength(); - if (picture_id_len > buffer_length) return -1; // error - if (picture_id_len == 2) - { - buffer[0] = 0x80 | ((pic_id >> 8) & 0x7F); - buffer[1] = pic_id & 0xFF; - } - else if (picture_id_len == 1) - { - buffer[0] = pic_id & 0x7F; - } - return picture_id_len; -} - -int RtpFormatVp8::FirstHeaderExtraLength() const -{ - if (!beginning_) - { - return 0; - } - int length = 0; - - length += PictureIdLength(); - - return length; -} - -int RtpFormatVp8::PictureIdLength() const -{ - if (!beginning_ || hdr_info_.pictureId == kNoPictureId) - { - return 0; - } - if (hdr_info_.pictureId <= 0x7F) - { - return 1; - } - else - { - return 2; - } -} - -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_format_vp8.h b/modules/rtp_rtcp/source/rtp_format_vp8.h deleted file mode 100644 index bad3abe20..000000000 --- a/modules/rtp_rtcp/source/rtp_format_vp8.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * This file contains the declaration of the VP8 packetizer class. - * A packetizer object is created for each encoded video frame. The - * constructor is called with the payload data and size, - * together with the fragmentation information and a packetizer mode - * of choice. Alternatively, if no fragmentation info is available, the - * second constructor can be used with only payload data and size; in that - * case the mode kSloppy is used. - * - * After creating the packetizer, the method NextPacket is called - * repeatedly to get all packets for the frame. The method returns - * false as long as there are more packets left to fetch. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_ -#define WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_ - -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc -{ - -enum VP8PacketizerMode -{ - kStrict = 0, // split partitions if too large; never aggregate, balance size - kAggregate, // split partitions if too large; aggregate whole partitions - kSloppy, // split entire payload without considering partition limits - kNumModes, -}; - -// Packetizer for VP8. -class RtpFormatVp8 -{ -public: - // Initialize with payload from encoder and fragmentation info. - // The payload_data must be exactly one encoded VP8 frame. - RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size, - const RTPVideoHeaderVP8& hdr_info, - const RTPFragmentationHeader& fragmentation, - VP8PacketizerMode mode); - - // Initialize without fragmentation info. Mode kSloppy will be used. - // The payload_data must be exactly one encoded VP8 frame. - RtpFormatVp8(const WebRtc_UWord8* payload_data, - WebRtc_UWord32 payload_size, - const RTPVideoHeaderVP8& hdr_info); - - // Get the next payload with VP8 payload header. - // max_payload_len limits the sum length of payload and VP8 payload header. - // buffer is a pointer to where the output will be written. - // bytes_to_send is an output variable that will contain number of bytes - // written to buffer. Parameter last_packet is true for the last packet of - // the frame, false otherwise (i.e., call the function again to get the - // next packet). Returns negative on error, zero otherwise. - int NextPacket(int max_payload_len, WebRtc_UWord8* buffer, - int* bytes_to_send, bool* last_packet); - -private: - enum AggregationMode - { - kAggrNone = 0, // no aggregation - kAggrPartitions, // aggregate intact partitions - kAggrFragments // aggregate intact and fragmented partitions - }; - - static const AggregationMode aggr_modes_[kNumModes]; - static const bool balance_modes_[kNumModes]; - static const bool separate_first_modes_[kNumModes]; - - // Calculate size of next chunk to send. Returns 0 if none can be sent. - int CalcNextSize(int max_payload_len, int remaining_bytes, - bool split_payload) const; - - // Write the payload header and copy the payload to the buffer. - // Will copy send_bytes bytes from the current position on the payload data. - // last_fragment indicates that this packet ends with the last byte of a - // partition. - int WriteHeaderAndPayload(int send_bytes, bool end_of_fragment, - WebRtc_UWord8* buffer, int buffer_length); - - // Write the PictureID from codec_specific_info_ to buffer. One or two - // bytes are written, depending on magnitude of PictureID. The function - // returns the number of bytes written. - int WritePictureID(WebRtc_UWord8* buffer, int buffer_length) const; - - // Calculate and return length (octets) of the variable header fields in - // the next header (i.e., header length in addition to vp8_header_bytes_). - int FirstHeaderExtraLength() const; - - // Calculate and return length (octets) of PictureID field in the next - // header. Can be 0, 1, or 2. - int PictureIdLength() const; - - const WebRtc_UWord8* payload_data_; - const int payload_size_; - RTPFragmentationHeader part_info_; - int payload_bytes_sent_; - int part_ix_; - bool beginning_; // first partition in this frame - bool first_fragment_; // first fragment of a partition - const int vp8_header_bytes_; // length of VP8 payload header's fixed part - AggregationMode aggr_mode_; - bool balance_; - bool separate_first_; - const RTPVideoHeaderVP8 hdr_info_; -}; - -} - -#endif /* WEBRTC_MODULES_RTP_RTCP_RTP_FORMAT_VP8_H_ */ diff --git a/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc b/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc deleted file mode 100644 index 0c9ad609c..000000000 --- a/modules/rtp_rtcp/source/rtp_format_vp8_unittest.cc +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -/* - * This file includes unit tests for the VP8 packetizer. - */ - -#include - -#include "typedefs.h" -#include "rtp_format_vp8.h" - -namespace { - -using webrtc::RTPFragmentationHeader; -using webrtc::RtpFormatVp8; -using webrtc::RTPVideoHeaderVP8; - -const WebRtc_UWord32 kPayloadSize = 30; - -class RtpFormatVp8Test : public ::testing::Test { - protected: - RtpFormatVp8Test() {}; - virtual void SetUp(); - virtual void TearDown(); - void CheckHeader(bool first_in_frame, bool frag_start, bool frag_end); - void CheckPayload(int payload_end); - void CheckLast(bool last) const; - void CheckPacket(int send_bytes, int expect_bytes, bool last, - bool first_in_frame, bool frag_start, bool frag_end); - WebRtc_UWord8 payload_data_[kPayloadSize]; - WebRtc_UWord8 buffer_[kPayloadSize]; - WebRtc_UWord8 *data_ptr_; - RTPFragmentationHeader* fragmentation_; - RTPVideoHeaderVP8 hdr_info_; - int payload_start_; -}; - -void RtpFormatVp8Test::SetUp() { - for (int i = 0; i < kPayloadSize; i++) - { - payload_data_[i] = i / 10; // integer division - } - data_ptr_ = payload_data_; - - fragmentation_ = new RTPFragmentationHeader; - fragmentation_->VerifyAndAllocateFragmentationHeader(3); - fragmentation_->fragmentationLength[0] = 10; - fragmentation_->fragmentationLength[1] = 10; - fragmentation_->fragmentationLength[2] = 10; - fragmentation_->fragmentationOffset[0] = 0; - fragmentation_->fragmentationOffset[1] = 10; - fragmentation_->fragmentationOffset[2] = 20; - - hdr_info_.pictureId = 0; - hdr_info_.nonReference = false; -} - -void RtpFormatVp8Test::TearDown() { - delete fragmentation_; -} - -#define EXPECT_BIT_EQ(x,n,a) EXPECT_EQ((((x)>>n)&0x1), a) - -#define EXPECT_RSV_ZERO(x) EXPECT_EQ(((x)&0xE0), 0) - -//#define EXPECT_BIT_I_EQ(x,a) EXPECT_EQ((((x)&0x10) > 0), (a > 0)) -#define EXPECT_BIT_I_EQ(x,a) EXPECT_BIT_EQ(x, 4, a) - -#define EXPECT_BIT_N_EQ(x,a) EXPECT_EQ((((x)&0x08) > 0), (a > 0)) - -#define EXPECT_FI_EQ(x,a) EXPECT_EQ((((x)&0x06) >> 1), a) - -#define EXPECT_BIT_B_EQ(x,a) EXPECT_EQ((((x)&0x01) > 0), (a > 0)) - -void RtpFormatVp8Test::CheckHeader(bool first_in_frame, bool frag_start, - bool frag_end) -{ - payload_start_ = 1; - EXPECT_RSV_ZERO(buffer_[0]); - if (first_in_frame & hdr_info_.pictureId != webrtc::kNoPictureId) - { - EXPECT_BIT_I_EQ(buffer_[0], 1); - if (hdr_info_.pictureId > 0x7F) - { - EXPECT_BIT_EQ(buffer_[1], 7, 1); - EXPECT_EQ(buffer_[1] & 0x7F, - (hdr_info_.pictureId >> 8) & 0x7F); - EXPECT_EQ(buffer_[2], hdr_info_.pictureId & 0xFF); - payload_start_ += 2; - } - else - { - EXPECT_BIT_EQ(buffer_[1], 7, 0); - EXPECT_EQ(buffer_[1] & 0x7F, - (hdr_info_.pictureId) & 0x7F); - payload_start_ += 1; - } - } - EXPECT_BIT_N_EQ(buffer_[0], 0); - WebRtc_UWord8 fi = 0x03; - if (frag_start) fi = fi & 0x01; - if (frag_end) fi = fi & 0x02; - EXPECT_FI_EQ(buffer_[0], fi); - if (first_in_frame) EXPECT_BIT_B_EQ(buffer_[0], 1); -} - -void RtpFormatVp8Test::CheckPayload(int payload_end) -{ - for (int i = payload_start_; i < payload_end; i++, data_ptr_++) - EXPECT_EQ(buffer_[i], *data_ptr_); -} - -void RtpFormatVp8Test::CheckLast(bool last) const -{ - EXPECT_EQ(last, data_ptr_ == payload_data_ + kPayloadSize); -} - -void RtpFormatVp8Test::CheckPacket(int send_bytes, int expect_bytes, bool last, - bool first_in_frame, bool frag_start, bool frag_end) -{ - EXPECT_EQ(send_bytes, expect_bytes); - CheckHeader(first_in_frame, frag_start, frag_end); - CheckPayload(send_bytes); - CheckLast(last); -} - -TEST_F(RtpFormatVp8Test, TestStrictMode) -{ - int send_bytes = 0; - bool last; - bool first_in_frame = true; - - hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, - hdr_info_, *fragmentation_, webrtc::kStrict); - - // get first packet, expect balanced size = same as second packet - EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 7, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ false); - first_in_frame = false; - - // get second packet - EXPECT_EQ(0, packetizer.NextPacket(8, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 7, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ true); - - // Second partition - // Get first (and only) packet - EXPECT_EQ(0, packetizer.NextPacket(20, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 11, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ true); - - // Third partition - // Get first packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 4, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ false); - - // Get second packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 3, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // Get third packet (of four) - EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 4, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // Get fourth and last packet - EXPECT_EQ(0, packetizer.NextPacket(4, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 3, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ true); - -} - -TEST_F(RtpFormatVp8Test, TestAggregateMode) -{ - int send_bytes = 0; - bool last; - bool first_in_frame = true; - - hdr_info_.pictureId = 20; // <= 0x7F should produce 1-byte PictureID - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, - hdr_info_, *fragmentation_, webrtc::kAggregate); - - // get first packet - // first half of first partition - EXPECT_EQ(0, packetizer.NextPacket(6, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 6, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ false); - first_in_frame = false; - - // get second packet - // second half of first partition - EXPECT_EQ(0, packetizer.NextPacket(10, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 7, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ true); - - // get third packet - // last two partitions aggregated - EXPECT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 21, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ true); - -} - -TEST_F(RtpFormatVp8Test, TestSloppyMode) -{ - int send_bytes = 0; - bool last; - bool first_in_frame = true; - - hdr_info_.pictureId = webrtc::kNoPictureId; // no PictureID - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, - hdr_info_, *fragmentation_, webrtc::kSloppy); - - // get first packet - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ false); - first_in_frame = false; - - // get second packet - // fragments of first and second partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // get third packet - // fragments of second and third partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // get fourth packet - // second half of last partition - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 7, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ true); - -} - -// Verify that sloppy mode is forced if fragmentation info is missing. -TEST_F(RtpFormatVp8Test, TestSloppyModeFallback) -{ - int send_bytes = 0; - bool last; - bool first_in_frame = true; - - hdr_info_.pictureId = 200; // > 0x7F should produce 2-byte PictureID - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, - hdr_info_); - - // get first packet - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ true, - /* frag_end */ false); - first_in_frame = false; - - // get second packet - // fragments of first and second partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // get third packet - // fragments of second and third partitions - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ false); - - // get fourth packet - // second half of last partition - EXPECT_EQ(0, packetizer.NextPacket(9, buffer_, &send_bytes, &last)); - CheckPacket(send_bytes, 9, last, - first_in_frame, - /* frag_start */ false, - /* frag_end */ true); - -} - -// Verify that non-reference bit is set. -TEST_F(RtpFormatVp8Test, TestNonReferenceBit) { - int send_bytes = 0; - bool last; - bool first_in_frame = true; - - hdr_info_.nonReference = true; - RtpFormatVp8 packetizer = RtpFormatVp8(payload_data_, kPayloadSize, - hdr_info_); - - // get first packet - ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); - ASSERT_FALSE(last); - EXPECT_BIT_N_EQ(buffer_[0], 1); - - // get second packet - ASSERT_EQ(0, packetizer.NextPacket(25, buffer_, &send_bytes, &last)); - ASSERT_TRUE(last); - EXPECT_BIT_N_EQ(buffer_[0], 1); -} - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} - -} // namespace diff --git a/modules/rtp_rtcp/source/rtp_receiver.cc b/modules/rtp_rtcp/source/rtp_receiver.cc deleted file mode 100644 index 172a91ae2..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver.cc +++ /dev/null @@ -1,1668 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "rtp_receiver.h" - -#include "rtp_rtcp_defines.h" -#include "critical_section_wrapper.h" - -#include -#include //memcpy -#include // floor -#include // abs - -namespace webrtc { -RTPReceiver::RTPReceiver(const WebRtc_Word32 id, - const bool audio, - ModuleRtpRtcpPrivate& callback) : - RTPReceiverAudio(id), - RTPReceiverVideo(id, callback), - _id(id), - _audio(audio), - _criticalSectionCbs(*CriticalSectionWrapper::CreateCriticalSection()), - _cbPrivateFeedback(callback), - _cbRtpFeedback(NULL), - _cbRtpData(NULL), - - _criticalSectionRTPReceiver(*CriticalSectionWrapper::CreateCriticalSection()), - _lastReceiveTime(0), - _lastReceivedPayloadLength(0), - _lastReceivedPayloadType(-1), - _lastReceivedMediaPayloadType(-1), - _lastReceivedAudioSpecific(), - _lastReceivedVideoSpecific(), - - _packetTimeOutMS(0), - - _redPayloadType(-1), - _payloadTypeMap(), - _SSRC(0), - _numCSRCs(0), - _currentRemoteCSRC(), - _numEnergy(0), - _currentRemoteEnergy(), - _useSSRCFilter(false), - _SSRCFilter(0), - - _jitterQ4(0), - _jitterMaxQ4(0), - _cumulativeLoss(0), - _localTimeLastReceivedTimestamp(0), - _lastReceivedTimestamp(0), - _lastReceivedSequenceNumber(0), - - _receivedSeqFirst(0), - _receivedSeqMax(0), - _receivedSeqWraps(0), - - _receivedPacketOH(12), // RTP header - _receivedByteCount(0), - _receivedOldPacketCount(0), - _receivedInorderPacketCount(0), - - _lastReportInorderPackets(0), - _lastReportOldPackets(0), - _lastReportSeqMax(0), - _lastReportFractionLost(0), - _lastReportCumulativeLost(0), - _lastReportExtendedHighSeqNum(0), - _lastReportJitter(0), - - _nackMethod(kNackOff) -{ - memset(_currentRemoteCSRC, 0, sizeof(_currentRemoteCSRC)); - memset(_currentRemoteEnergy, 0, sizeof(_currentRemoteEnergy)); - memset(&_lastReceivedAudioSpecific, 0, sizeof(_lastReceivedAudioSpecific)); - - _lastReceivedAudioSpecific.channels = 1; - _lastReceivedVideoSpecific.maxRate = 0; - _lastReceivedVideoSpecific.videoCodecType = kRtpNoVideo; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); -} - -RTPReceiver::~RTPReceiver() -{ - if(_cbRtpFeedback) - { - for(int i = 0; i < _numCSRCs; i++) - { - _cbRtpFeedback->OnIncomingCSRCChanged(_id,_currentRemoteCSRC[i], false); - } - } - delete &_criticalSectionCbs; - delete &_criticalSectionRTPReceiver; - - // empty map - bool loop = true; - do - { - MapItem* item = _payloadTypeMap.First(); - if(item) - { - // delete - ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); - delete payload; - - // remove from map and delete Item - _payloadTypeMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); -} - -WebRtc_Word32 -RTPReceiver::Init() -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - _lastReceiveTime = 0; - _lastReceivedPayloadLength = 0; - _packetTimeOutMS = 0; - _lastReceivedPayloadType = -1; - _lastReceivedMediaPayloadType = -1; - _redPayloadType = -1; - - memset(&_lastReceivedAudioSpecific, 0, sizeof(_lastReceivedAudioSpecific)); - _lastReceivedAudioSpecific.channels = 1; - - _lastReceivedVideoSpecific.videoCodecType = kRtpNoVideo; - _lastReceivedVideoSpecific.maxRate = 0; - _SSRC = 0; - _numCSRCs = 0; - _numEnergy = 0; - _jitterQ4 = 0; - _jitterMaxQ4 = 0; - _cumulativeLoss = 0; - _useSSRCFilter = false; - _SSRCFilter = 0; - - _localTimeLastReceivedTimestamp = 0; - _lastReceivedTimestamp = 0; - _lastReceivedSequenceNumber = 0; - - _receivedSeqFirst = 0; - _receivedSeqMax = 0; - _receivedSeqWraps = 0; - - _receivedPacketOH = 12; // RTP header - _receivedByteCount = 0; - _receivedOldPacketCount = 0; - _receivedInorderPacketCount = 0; - - _lastReportInorderPackets = 0; - _lastReportOldPackets = 0; - _lastReportSeqMax = 0; - _lastReportFractionLost = 0; - _lastReportCumulativeLost = 0; - _lastReportExtendedHighSeqNum = 0; - _lastReportJitter = 0; - - // clear db - bool loop = true; - do - { - MapItem* item = _payloadTypeMap.First(); - if(item) - { - ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); - delete payload; - // remove from map - _payloadTypeMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - Bitrate::Init(); - RTPReceiverAudio::Init(); - return RTPReceiverVideo::Init(); -} - -void -RTPReceiver::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - RTPReceiverAudio::ChangeUniqueId(id); - RTPReceiverVideo::ChangeUniqueId(id); -} - -RtpVideoCodecTypes -RTPReceiver::VideoCodecType() const -{ - return _lastReceivedVideoSpecific.videoCodecType; -} - -WebRtc_UWord32 -RTPReceiver::MaxConfiguredBitrate() const -{ - return _lastReceivedVideoSpecific.maxRate; -} - -bool -RTPReceiver::REDPayloadType(const WebRtc_Word8 payloadType) const -{ - return (_redPayloadType == payloadType)?true:false; -} - -WebRtc_Word8 -RTPReceiver::REDPayloadType() const -{ - return _redPayloadType; -} - - // configure a timeout value -WebRtc_Word32 -RTPReceiver::SetPacketTimeout(const WebRtc_UWord32 timeoutMS) -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - _packetTimeOutMS = timeoutMS; - return 0; -} - -void RTPReceiver::PacketTimeout() -{ - if(_packetTimeOutMS == 0) - { - // not configured - return; - } - - bool packetTimeOut = false; - { - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - if(_lastReceiveTime == 0) - { - // not active - return; - } - - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - - if(now - _lastReceiveTime > _packetTimeOutMS) - { - packetTimeOut = true; - _lastReceiveTime = 0; // only one callback - _lastReceivedPayloadType = -1; // makes RemotePayload return -1, which we want - _lastReceivedMediaPayloadType = -1; - } - } - CriticalSectionScoped lock(_criticalSectionCbs); - if(packetTimeOut && _cbRtpFeedback) - { - _cbRtpFeedback->OnPacketTimeout(_id); - } -} - -void -RTPReceiver::ProcessDeadOrAlive(const bool RTCPalive, const WebRtc_UWord32 now) -{ - if(_cbRtpFeedback == NULL) - { - // no callback - return; - } - RTPAliveType alive = kRtpDead; - - if(_lastReceiveTime + 1000 > now) - { - // always alive if we have received a RTP packet the last sec - alive = kRtpAlive; - - } else - { - if(RTCPalive) - { - if(_audio) - { - // alive depends on CNG - // if last received size < 10 likely CNG - if(_lastReceivedPayloadLength < 10) // our CNG is 9 bytes - { - // potential CNG - // receiver need to check kRtpNoRtp against NetEq speechType kOutputPLCtoCNG - alive = kRtpNoRtp; - } else - { - // dead - } - } else - { - // dead for video - } - }else - { - // no RTP packet for 1 sec and no RTCP - // dead - } - } - - - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpFeedback) - { - _cbRtpFeedback->OnPeriodicDeadOrAlive(_id, alive); - } -} - -WebRtc_UWord16 -RTPReceiver::PacketOHReceived() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _receivedPacketOH; -} - -WebRtc_UWord32 -RTPReceiver::PacketCountReceived() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _receivedInorderPacketCount; -} - -WebRtc_UWord32 -RTPReceiver::ByteCountReceived() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _receivedByteCount; -} - -WebRtc_Word32 -RTPReceiver::RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback) -{ - CriticalSectionScoped lock(_criticalSectionCbs); - _cbRtpFeedback = incomingMessagesCallback; - return 0; -} - -WebRtc_Word32 -RTPReceiver::RegisterIncomingDataCallback(RtpData* incomingDataCallback) -{ - CriticalSectionScoped lock(_criticalSectionCbs); - _cbRtpData = incomingDataCallback; - return 0; -} - -WebRtc_Word32 -RTPReceiver::RegisterReceivePayload( const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) -{ - if(payloadName == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - // sanity - switch(payloadType) - { - // reserved payload types to avoid RTCP conflicts when marker bit is set - case 64: // 192 Full INTRA-frame request - case 72: // 200 Sender report - case 73: // 201 Receiver report - case 74: // 202 Source description - case 75: // 203 Goodbye - case 76: // 204 Application-defined - case 77: // 205 Transport layer FB message - case 78: // 206 Payload-specific FB message - case 79: // 207 Extended report - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid payloadtype:%d", __FUNCTION__, payloadType); - return -1; - default: - break; - } - WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); - - MapItem* item = NULL; - item = _payloadTypeMap.Find(payloadType); - if( NULL != item) - { - // we already use this payload type - - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - assert(payload); - - WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); - - // check if it's the same as we already have - // if same ignore sending an error - if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) - { - if(_audio && - payload->audio && - payload->typeSpecific.Audio.frequency == frequency && - payload->typeSpecific.Audio.channels == channels && - (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) - { - payload->typeSpecific.Audio.rate = rate; // Ensure that we update the rate if new or old is zero - return 0; - } - if(!_audio && !payload->audio) - { - // update maxBitrate for video - payload->typeSpecific.Video.maxRate = rate; - return 0; - } - } - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument payloadType:%d already registered", __FUNCTION__, payloadType); - return -1; - } - - if(_audio) - { - // remove existing item, hence search for the name - // only for audio, for video we allow a codecs to use multiple pltypes - item = _payloadTypeMap.First(); - while(item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); - - if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) - { - // we found the payload name in the list - // if audio check frequency and rate - if( payload->audio) - { - if(payload->typeSpecific.Audio.frequency == frequency && - (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) - { - // remove old setting - delete payload; - _payloadTypeMap.Erase(item); - break; - } - } else if(ModuleRTPUtility::StringCompare(payloadName,"red",3)) - { - delete payload; - _payloadTypeMap.Erase(item); - break; - } - } - item = _payloadTypeMap.Next(item); - } - } - - ModuleRTPUtility::Payload* payload = NULL; - - // save the RED payload type - // used in both audio and video - if(ModuleRTPUtility::StringCompare(payloadName,"red",3)) - { - _redPayloadType = payloadType; - payload = new ModuleRTPUtility::Payload; - payload->audio = false; - memcpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE); - } else - { - if(_audio) - { - payload = RegisterReceiveAudioPayload(payloadName, payloadType, frequency, channels, rate); - } else - { - payload = RegisterReceiveVideoPayload(payloadName, payloadType, rate); - } - if(payload == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s filed to register payload", __FUNCTION__); - return -1; - } - } - _payloadTypeMap.Insert(payloadType, payload); - - // Successful set of payload type, clear the value of last receivedPT, since it might mean something else - _lastReceivedPayloadType = -1; - _lastReceivedMediaPayloadType = -1; - return 0; -} - -WebRtc_Word32 -RTPReceiver::DeRegisterReceivePayload(const WebRtc_Word8 payloadType) -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - MapItem* item = _payloadTypeMap.Find(payloadType); - if( NULL != item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - delete payload; - - _payloadTypeMap.Erase(item); - return 0; - } - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s failed to find payloadType:%d", __FUNCTION__, payloadType); - return -1; -} - -WebRtc_Word32 -RTPReceiver::ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - WebRtc_Word8* payloadType, - const WebRtc_UWord32 rate) const -{ - if(payloadType == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); - - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - MapItem* item = _payloadTypeMap.First(); - while( NULL != item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - assert(payload); - - WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); - if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) - { - // name match - if(payload->audio) - { - if (rate == 0) - { - // [default] audio, check freq and channels - if( payload->typeSpecific.Audio.frequency == frequency && - payload->typeSpecific.Audio.channels == channels) - { - *payloadType = item->GetId(); - return 0; - } - } - else - { - // audio, check freq, channels and rate - if( payload->typeSpecific.Audio.frequency == frequency && - payload->typeSpecific.Audio.channels == channels && - payload->typeSpecific.Audio.rate == rate) // extra rate condition added - { - *payloadType = item->GetId(); - return 0; - } - } - } else - { - // video - *payloadType = item->GetId(); - return 0; - } - } - item = _payloadTypeMap.Next(item); - } - return -1; -} - -WebRtc_Word32 -RTPReceiver::ReceivePayload(const WebRtc_Word8 payloadType, - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels, - WebRtc_UWord32* rate) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - MapItem* item = _payloadTypeMap.Find(payloadType); - if( NULL == item) - { - return -1; - } - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - assert(payload); - - if(frequency) - { - if(payload->audio) - { - *frequency = payload->typeSpecific.Audio.frequency; - } else - { - *frequency = 90000; - } - } - if(channels) - { - if(payload->audio) - { - *channels = payload->typeSpecific.Audio.channels; - } else - { - *channels = 1; - } - } - if (rate) - { - if(payload->audio) - { - *rate = payload->typeSpecific.Audio.rate; - } else - { - assert(false); - *rate = 0; - } - } - if(payloadName) - { - memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); - } - return 0; -} - -WebRtc_Word32 -RTPReceiver::RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_Word8* payloadType, - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels) const -{ - if(_lastReceivedPayloadType == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); - return -1; - } - memset(payloadName, 0, RTP_PAYLOAD_NAME_SIZE); - - MapItem* item = _payloadTypeMap.Find(_lastReceivedPayloadType); - if( NULL != item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - if(payload) - { - memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); - - if(payloadType ) - { - *payloadType = _lastReceivedPayloadType; - } - if(frequency) - { - if(payload->audio) - { - *frequency = payload->typeSpecific.Audio.frequency; - } else - { - *frequency = 90000; - } - } - if(channels) - { - if(payload->audio) - { - *channels = payload->typeSpecific.Audio.channels; - } else - { - *channels = 1; - } - } - return 0; - } - } - return -1; -} - -NACKMethod -RTPReceiver::NACK() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _nackMethod; -} - - // Turn negative acknowledgement requests on/off -WebRtc_Word32 -RTPReceiver::SetNACKStatus(const NACKMethod method) -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - _nackMethod = method; - return 0; -} - -WebRtc_UWord32 -RTPReceiver::SSRC() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _SSRC; -} - - // Get remote CSRC -WebRtc_Word32 -RTPReceiver::CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - assert(_numCSRCs <= kRtpCsrcSize); - - if(_numCSRCs >0) - { - memcpy(arrOfCSRC, _currentRemoteCSRC, sizeof(WebRtc_UWord32)*_numCSRCs); - } - return _numCSRCs; -} - -WebRtc_Word32 -RTPReceiver::Energy( WebRtc_UWord8 arrOfEnergy[kRtpCsrcSize]) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - assert(_numEnergy <= kRtpCsrcSize); - - if(_numEnergy >0) - { - memcpy(arrOfEnergy, _currentRemoteEnergy, sizeof(WebRtc_UWord8)*_numCSRCs); - } - return _numEnergy; -} - -WebRtc_Word32 -RTPReceiver::IncomingRTPPacket(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 incomingRtpPacketLength) -{ - // rtpHeader now contains the parsed RTP header. - // Adjust packet length w r t RTP padding. - WebRtc_Word32 length = incomingRtpPacketLength - rtpHeader->header.paddingLength; - - // length sanity - if((length - rtpHeader->header.headerLength) < 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - if(_useSSRCFilter) - { - if(rtpHeader->header.ssrc != _SSRCFilter) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s drop packet due to SSRC filter", __FUNCTION__); - return -1; - } - } - if(_lastReceiveTime == 0) - { - // trigger only once - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpFeedback) - { - if(length - rtpHeader->header.headerLength == 0) - { - // keepalive packet - _cbRtpFeedback->OnReceivedPacket(_id, kPacketKeepAlive); - }else - { - _cbRtpFeedback->OnReceivedPacket(_id, kPacketRtp); - } - } - } - if(length - rtpHeader->header.headerLength == 0) - { - // ok keepalive packet - return 0; - } - - WebRtc_Word8 firstPayloadByte = 0; - if(length > 0) - { - firstPayloadByte = incomingRtpPacket[rtpHeader->header.headerLength]; - } - - // trigger our callbacks - CheckSSRCChanged(rtpHeader); - - bool isRED = false; - ModuleRTPUtility::VideoPayload videoSpecific; - videoSpecific.maxRate = 0; - videoSpecific.videoCodecType = kRtpNoVideo; - - ModuleRTPUtility::AudioPayload audioSpecific; - audioSpecific.bitsPerSample = 0; - audioSpecific.channels = 0; - audioSpecific.frequency = 0; - - if(CheckPayloadChanged(rtpHeader, firstPayloadByte, isRED, audioSpecific, videoSpecific) == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s received invalid payloadtype", __FUNCTION__); - return -1; - } - CheckCSRC(rtpHeader); - - WebRtc_Word32 retVal = 0; - const WebRtc_UWord8* payloadData = incomingRtpPacket + rtpHeader->header.headerLength; - const WebRtc_UWord16 payloadDataLength = (WebRtc_UWord16)(length - rtpHeader->header.headerLength); - - if(_audio) - { - retVal = ParseAudioCodecSpecific(rtpHeader, - payloadData, - payloadDataLength, - audioSpecific, - isRED); - }else - { - retVal = ParseVideoCodecSpecific(rtpHeader, - payloadData, - payloadDataLength, - videoSpecific.videoCodecType, - isRED, - incomingRtpPacket, - incomingRtpPacketLength); - } - if(retVal != -1) - { - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - // this compare to _receivedSeqMax - // we store the last received after we have done the callback - const bool oldPacket = RetransmitOfOldPacket(rtpHeader->header.sequenceNumber, - rtpHeader->header.timestamp); - - // this updates _receivedSeqMax and other members - UpdateStatistics(rtpHeader, payloadDataLength, oldPacket); - - _lastReceiveTime = ModuleRTPUtility::GetTimeInMS(); // need to be updated after RetransmitOfOldPacket & RetransmitOfOldPacketUpdateStatistics - _lastReceivedPayloadLength = payloadDataLength; - - if(retVal >= 0 && !oldPacket) - { - if(_lastReceivedTimestamp != rtpHeader->header.timestamp) - { - _lastReceivedTimestamp = rtpHeader->header.timestamp; - } - _lastReceivedSequenceNumber = rtpHeader->header.sequenceNumber; - } - } - return retVal; -} - -// must not have critsect when called -WebRtc_Word32 -RTPReceiver::CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader) -{ - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpData) - { - return _cbRtpData->OnReceivedPayloadData(payloadData, payloadSize, rtpHeader); - } - return -1; -} - -// we already have the _criticalSectionRTPReceiver critsect when we call this -void -RTPReceiver::UpdateStatistics(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord16 bytes, - const bool oldPacket) -{ - WebRtc_UWord32 freq = 90000; - if(_audio) - { - freq = AudioFrequency(); - } - - Bitrate::Update(bytes); - - _receivedByteCount += bytes; - - if (_receivedSeqMax == 0 && _receivedSeqWraps == 0) - { - // First received report - _receivedSeqFirst = rtpHeader->header.sequenceNumber; - _receivedSeqMax = rtpHeader->header.sequenceNumber; - _receivedInorderPacketCount = 1; - _localTimeLastReceivedTimestamp = ModuleRTPUtility::CurrentRTP(freq); //time in samples - return; - } - - // count only the new packets received - if(InOrderPacket(rtpHeader->header.sequenceNumber)) - { - const WebRtc_UWord32 RTPtime = ModuleRTPUtility::CurrentRTP(freq); //time in samples - _receivedInorderPacketCount++; - - // wrong if we use RetransmitOfOldPacket - WebRtc_Word32 seqDiff = rtpHeader->header.sequenceNumber - _receivedSeqMax; - if (seqDiff < 0) - { - // Wrap around detected - _receivedSeqWraps++; - } - // new max - _receivedSeqMax = rtpHeader->header.sequenceNumber; - - if (rtpHeader->header.timestamp != _lastReceivedTimestamp && - _receivedInorderPacketCount > 1) - { - WebRtc_Word32 timeDiffSamples = (RTPtime - _localTimeLastReceivedTimestamp) - - (rtpHeader->header.timestamp - _lastReceivedTimestamp); - - timeDiffSamples = abs(timeDiffSamples); - - // libJingle sometimes deliver crazy jumps in TS for the same stream - // If this happen don't update jitter value - if(timeDiffSamples < 450000) // Use 5 secs video frequency as border - { - // note we calculate in Q4 to avoid using float - WebRtc_Word32 jitterDiffQ4 = (timeDiffSamples << 4) - _jitterQ4; - _jitterQ4 += ((jitterDiffQ4 + 8) >> 4); - } - } - _localTimeLastReceivedTimestamp = RTPtime; - } else - { - if(oldPacket) - { - _receivedOldPacketCount++; - }else - { - _receivedInorderPacketCount++; - } - } - - WebRtc_UWord16 packetOH = rtpHeader->header.headerLength + rtpHeader->header.paddingLength; - - // our measured overhead - // filter from RFC 5104 4.2.1.2 - // avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH, - _receivedPacketOH = (15*_receivedPacketOH + packetOH) >> 4; -} - -// we already have the _criticalSectionRTPReceiver critsect when we call this -bool -RTPReceiver::RetransmitOfOldPacket(const WebRtc_UWord16 sequenceNumber, - const WebRtc_UWord32 rtpTimeStamp) const -{ - if(InOrderPacket(sequenceNumber)) - { - return false; - } - WebRtc_UWord32 timeDiffMS = ModuleRTPUtility::GetTimeInMS() - _lastReceiveTime; // last time we received a packet - WebRtc_Word32 rtpTimeStampDiffMS = ((WebRtc_Word32)(rtpTimeStamp - _lastReceivedTimestamp))/90; // diff in time stamp since last received in order - - WebRtc_UWord16 minRTT = 0; - _cbPrivateFeedback.RTT(_SSRC,NULL,NULL,&minRTT, NULL); - if(minRTT == 0) - { - // no update - // assume loss - return true; - } - WebRtc_UWord16 timeWindow = (minRTT/3)+1; - if((WebRtc_Word32)timeDiffMS > rtpTimeStampDiffMS + timeWindow) - { - return true; - } - return false; -} - -bool -RTPReceiver::InOrderPacket(const WebRtc_UWord16 sequenceNumber) const -{ - if(_receivedSeqMax >= sequenceNumber) - { - if(!(_receivedSeqMax > 0xff00 && sequenceNumber < 0x0ff ))//detect wrap around - { - if(_receivedSeqMax - NACK_PACKETS_MAX_SIZE > sequenceNumber) - { - // we have a restart of the remote side - }else - { - // we received a retransmit of a packet we already have - return false; - } - } - }else - { - // check for a wrap - if(sequenceNumber > 0xff00 && _receivedSeqMax < 0x0ff )//detect wrap around - { - if(_receivedSeqMax - NACK_PACKETS_MAX_SIZE > sequenceNumber) - { - // we have a restart of the remote side - }else - { - // we received a retransmit of a packet we already have - return false; - } - } - } - return true; -} - -WebRtc_UWord16 -RTPReceiver::SequenceNumber() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _lastReceivedSequenceNumber; -} - -WebRtc_UWord32 -RTPReceiver::TimeStamp() const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - return _lastReceivedTimestamp; -} - -WebRtc_UWord32 -RTPReceiver::PayloadTypeToPayload(const WebRtc_UWord8 payloadType, - ModuleRTPUtility::Payload*& payload) const -{ - MapItem* item = _payloadTypeMap.Find(payloadType); - - // check that this is a registered payload type - if(item == NULL) - { - return -1; - } - - payload = (ModuleRTPUtility::Payload*)item->GetItem(); - if(payload == NULL) - { - return -1; - } - - return 0; -} - - -// timeStamp of the last incoming packet that is the first packet of its frame -WebRtc_Word32 -RTPReceiver::EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - WebRtc_UWord32 freq = 90000; - if(_audio) - { - freq = AudioFrequency(); - } - if(_localTimeLastReceivedTimestamp == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); - return -1; - } - //time in samples - WebRtc_UWord32 diff = ModuleRTPUtility::CurrentRTP(freq) - _localTimeLastReceivedTimestamp; - - timestamp = _lastReceivedTimestamp + diff; - return 0; -} - - // get the currently configured SSRC filter -WebRtc_Word32 -RTPReceiver::SSRCFilter(WebRtc_UWord32& allowedSSRC) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - if(_useSSRCFilter) - { - allowedSSRC = _SSRCFilter; - return 0; - } - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); - return -1; -} - - // set a SSRC to be used as a filter for incoming RTP streams -WebRtc_Word32 -RTPReceiver::SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC) -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - _useSSRCFilter = enable; - if(enable) - { - _SSRCFilter = allowedSSRC; - } else - { - _SSRCFilter = 0; - } - return 0; -} - -// no criticalsection when called -void -RTPReceiver::CheckSSRCChanged(const WebRtcRTPHeader* rtpHeader) -{ - bool newSSRC = false; - bool reInitializeDecoder = false; - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE]; - WebRtc_UWord32 frequency = 90000; // default video freq - WebRtc_UWord8 channels = 1; - WebRtc_UWord32 rate = 0; - memset(payloadName, 0, sizeof(payloadName)); - - { - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - if (_SSRC != rtpHeader->header.ssrc || (_lastReceivedPayloadType == -1 && _SSRC == 0)) // we need the _payloadType to make the call if the remote SSRC is 0 - { - newSSRC = true; - - // reset last report - ResetStatistics(); - RTPReceiverVideo::ResetOverUseDetector(); - - _lastReceivedTimestamp = 0; - _lastReceivedSequenceNumber = 0; - - if (_SSRC) // do we have a SSRC? then the stream is restarted - { - // if we have the same codec? reinit decoder - if (rtpHeader->header.payloadType == _lastReceivedPayloadType) - { - reInitializeDecoder = true; - - MapItem* item = _payloadTypeMap.Find(rtpHeader->header.payloadType); - if( NULL != item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - if(payload) - { - memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); - if(payload->audio) - { - frequency = payload->typeSpecific.Audio.frequency; - channels = payload->typeSpecific.Audio.channels; - rate = payload->typeSpecific.Audio.rate; - } else - { - frequency = 90000; - } - } - } - } - } - _SSRC = rtpHeader->header.ssrc; - } - } - if(newSSRC) - { - // we need to get this to our RTCP sender and receiver - // need to do this outside critical section - _cbPrivateFeedback.SetRemoteSSRC(rtpHeader->header.ssrc); - } - - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpFeedback) - { - if(newSSRC) - { - _cbRtpFeedback->OnIncomingSSRCChanged(_id, rtpHeader->header.ssrc); - } - if(reInitializeDecoder) - { - if(-1 == _cbRtpFeedback->OnInitializeDecoder(_id, rtpHeader->header.payloadType, payloadName, frequency, channels, rate)) // new stream same codec - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create decoder for payload type:%d", rtpHeader->header.payloadType); - } - } - } -} - -// no criticalsection when called -WebRtc_Word32 -RTPReceiver::CheckPayloadChanged(const WebRtcRTPHeader* rtpHeader, - const WebRtc_Word8 firstPayloadByte, - bool& isRED, - ModuleRTPUtility::AudioPayload& audioSpecificPayload, - ModuleRTPUtility::VideoPayload& videoSpecificPayload) -{ - bool reInitializeDecoder = false; - - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE]; - WebRtc_Word8 payloadType = rtpHeader->header.payloadType; - - { - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - if (payloadType != _lastReceivedPayloadType) - { - if (REDPayloadType(payloadType)) - { - // get the real codec payload type - payloadType = firstPayloadByte & 0x7f; - isRED = true; - - //when we receive RED we need to check the real payload type - if (payloadType == _lastReceivedPayloadType) - { - if(_audio) - { - memcpy(&audioSpecificPayload, &_lastReceivedAudioSpecific, sizeof(_lastReceivedAudioSpecific)); - } else - { - memcpy(&videoSpecificPayload, &_lastReceivedVideoSpecific, sizeof(_lastReceivedVideoSpecific)); - } - return 0; - } - } - if(_audio) - { - if(TelephoneEventPayloadType(payloadType)) - { - // don't do callbacks for DTMF packets - isRED = false; - return 0; - } - - // frequency is updated for CNG - if(CNGPayloadType(payloadType, audioSpecificPayload.frequency)) - { - // don't do callbacks for DTMF packets - isRED = false; - return 0; - } - } - MapItem* item = _payloadTypeMap.Find(payloadType); - - // check that this is a registered payload type - if(item == NULL) - { - return -1; - } - memset(payloadName, 0, sizeof(payloadName)); - - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - if(payload == NULL) - { - return -1; - } - - memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); - _lastReceivedPayloadType = payloadType; - - reInitializeDecoder = true; - - if(payload->audio) - { - memcpy(&_lastReceivedAudioSpecific, &(payload->typeSpecific.Audio), sizeof(_lastReceivedAudioSpecific)); - memcpy(&audioSpecificPayload, &(payload->typeSpecific.Audio), sizeof(_lastReceivedAudioSpecific)); - }else - { - memcpy(&_lastReceivedVideoSpecific, &(payload->typeSpecific.Video), sizeof(_lastReceivedVideoSpecific)); - memcpy(&videoSpecificPayload, &(payload->typeSpecific.Video), sizeof(_lastReceivedVideoSpecific)); - - if (_lastReceivedVideoSpecific.videoCodecType == kRtpFecVideo) - { - // Only reset the decoder on media packets. - reInitializeDecoder = false; - } - else - { - if (_lastReceivedMediaPayloadType == _lastReceivedPayloadType) - { - // Only reset the decoder if the media codec type has changed. - reInitializeDecoder = false; - } - _lastReceivedMediaPayloadType = _lastReceivedPayloadType; - } - } - if(reInitializeDecoder) - { - // reset statistics - ResetStatistics(); - } - }else - { - if(_audio) - { - memcpy(&audioSpecificPayload, &_lastReceivedAudioSpecific, sizeof(_lastReceivedAudioSpecific)); - } else - { - memcpy(&videoSpecificPayload, &_lastReceivedVideoSpecific, sizeof(_lastReceivedVideoSpecific)); - } - isRED = false; - } - } // end critsect - if(reInitializeDecoder) - { - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpFeedback) - { - // create new decoder instance - if(_audio) - { - if (-1 == _cbRtpFeedback->OnInitializeDecoder(_id, payloadType, payloadName, audioSpecificPayload.frequency, audioSpecificPayload.channels, audioSpecificPayload.rate)) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create audio decoder for payload type:%d", payloadType); - return -1; // Wrong payload type - } - } else - { - if (-1 == _cbRtpFeedback->OnInitializeDecoder(_id, payloadType, payloadName, 90000, 1, 0)) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create video decoder for payload type:%d", payloadType); - return -1; // Wrong payload type - } - } - } - } - return 0; -} - -// no criticalsection when called -void -RTPReceiver::CheckCSRC(const WebRtcRTPHeader* rtpHeader) -{ - bool checkChanged = false; - WebRtc_Word32 numCSRCsDiff = 0; - WebRtc_UWord32 oldRemoteCSRC[kRtpCsrcSize]; - WebRtc_UWord8 oldNumCSRCs = 0; - - { - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - if(TelephoneEventPayloadType(rtpHeader->header.payloadType)) - { - // don't do this for DTMF packets - return ; - } - - _numEnergy = rtpHeader->type.Audio.numEnergy; - if(rtpHeader->type.Audio.numEnergy > 0 && rtpHeader->type.Audio.numEnergy <= kRtpCsrcSize) - { - memcpy(_currentRemoteEnergy, rtpHeader->type.Audio.arrOfEnergy, rtpHeader->type.Audio.numEnergy); - } - - oldNumCSRCs = _numCSRCs; - - const WebRtc_UWord8 numCSRCs = rtpHeader->header.numCSRCs; - - if(((numCSRCs > 0) && (numCSRCs <= kRtpCsrcSize)) || oldNumCSRCs) - { - if(oldNumCSRCs > 0) - { - // make a copy of old - memcpy(oldRemoteCSRC, _currentRemoteCSRC, _numCSRCs * sizeof(WebRtc_UWord32)); - } - if(numCSRCs > 0) - { - // copy new - memcpy(_currentRemoteCSRC, rtpHeader->header.arrOfCSRCs, numCSRCs * sizeof(WebRtc_UWord32)); - } - numCSRCsDiff = numCSRCs - oldNumCSRCs; - _numCSRCs = numCSRCs; //update stored CSRCs - checkChanged = true; - - }else - { - if(_numCSRCs != 0) - { - checkChanged = true; - numCSRCsDiff = numCSRCs - oldNumCSRCs; - } - _numCSRCs = 0; - } - } - - if(checkChanged ) - { - CriticalSectionScoped lock(_criticalSectionCbs); - if(_cbRtpFeedback) - { - bool haveCalledCallback = false; - // search for new CSRC in old array - for (WebRtc_UWord8 i = 0; i < rtpHeader->header.numCSRCs; ++i) - { - const WebRtc_UWord32 csrc = rtpHeader->header.arrOfCSRCs[i]; - - bool foundMatch = false; - for (WebRtc_UWord8 j = 0; j < oldNumCSRCs; ++j) - { - if (csrc == oldRemoteCSRC[j]) // old list - { - foundMatch = true; - break; - } - } - if (!foundMatch && csrc) - { - // didn't find it - // report it as new - haveCalledCallback = true; - _cbRtpFeedback->OnIncomingCSRCChanged(_id, csrc, true); - } - } - - // search for old CSRC in new array - for (WebRtc_UWord8 i = 0; i < oldNumCSRCs; ++i) - { - const WebRtc_UWord32 csrc = oldRemoteCSRC[i]; - - bool foundMatch = false; - for (WebRtc_UWord8 j = 0; j < rtpHeader->header.numCSRCs; ++j) - { - if (csrc == rtpHeader->header.arrOfCSRCs[j]) - { - foundMatch = true; - break; - } - } - if (!foundMatch && csrc) - { - // did not find it - // report as removed - haveCalledCallback = true; - _cbRtpFeedback->OnIncomingCSRCChanged(_id, csrc, false); - } - } - if(!haveCalledCallback) - { - // Layout change for less mixed streams than slots in the layout - // won't trigger a callback above. - if (numCSRCsDiff > 0) - { - _cbRtpFeedback->OnIncomingCSRCChanged(_id, 0, true); - } - else if (numCSRCsDiff < 0) - { - _cbRtpFeedback->OnIncomingCSRCChanged(_id, 0, false); - } - } - } - } -} - -WebRtc_Word32 -RTPReceiver::ResetStatistics() -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - _lastReportInorderPackets = 0; - _lastReportOldPackets = 0; - _lastReportSeqMax = 0; - _lastReportFractionLost = 0; - _lastReportCumulativeLost = 0; - _lastReportExtendedHighSeqNum = 0; - _lastReportJitter = 0; - _jitterQ4 = 0; - _jitterMaxQ4 = 0; - _cumulativeLoss = 0; - _receivedSeqWraps = 0; - _receivedSeqMax = 0; - _receivedSeqFirst = 0; - _receivedByteCount = 0; - _receivedOldPacketCount = 0; - _receivedInorderPacketCount = 0; - return 0; -} - -WebRtc_Word32 -RTPReceiver::ResetDataCounters() -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - _receivedByteCount = 0; - _receivedOldPacketCount = 0; - _receivedInorderPacketCount = 0; - _lastReportInorderPackets = 0; - - return 0; -} - -WebRtc_Word32 -RTPReceiver::Statistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, - WebRtc_UWord32 *max_jitter, - bool reset) const -{ - WebRtc_Word32 missing; - return Statistics(fraction_lost, - cum_lost, - ext_max, - jitter, - max_jitter, - &missing, - reset); -} - -WebRtc_Word32 -RTPReceiver::Statistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, - WebRtc_UWord32 *max_jitter, - WebRtc_Word32 *missing, - bool reset) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - if (missing == NULL) - { - return -1; - } - if(_receivedSeqFirst == 0 && _receivedByteCount == 0) - { - // we have not received anything - // -1 required by RTCP sender - return -1; - } - if(!reset) - { - if(_lastReportInorderPackets == 0) - { - // no report - return -1; - } - // just get last report - if(fraction_lost) - { - *fraction_lost = _lastReportFractionLost; - } - if(cum_lost) - { - *cum_lost = _lastReportCumulativeLost; // 24 bits valid - } - if(ext_max) - { - *ext_max = _lastReportExtendedHighSeqNum; - } - if(jitter) - { - *jitter =_lastReportJitter; - } - if(max_jitter) - { - // note that the internal jitter value is in Q4 - // and needs to be scaled by 1/16 - *max_jitter = (_jitterMaxQ4 >> 4); - } - return 0; - } - - if (_lastReportInorderPackets == 0) - { - // First time we send a report - _lastReportSeqMax = _receivedSeqFirst-1; - } - /* - * calc fraction lost - */ - WebRtc_UWord16 expSinceLast = (_receivedSeqMax - _lastReportSeqMax); - - if(_lastReportSeqMax > _receivedSeqMax) - { - // can we assume that the seqNum can't go decrease over a full RTCP period ? - expSinceLast = 0; - } - - // number of received RTP packets since last report, counts all packets but not re-transmissions - WebRtc_UWord32 recSinceLast = _receivedInorderPacketCount - _lastReportInorderPackets; - - if(_nackMethod == kNackOff) - { - // this is needed for re-ordered packets - WebRtc_UWord32 oldPackets = _receivedOldPacketCount - _lastReportOldPackets; - recSinceLast += oldPackets; - }else - { - // with NACK we don't know the expected retransmitions during the last second - // we know how many "old" packets we have received we just count the numer of - // old received to estimate the loss but it still does not guarantee an exact number - // since we run this based on time triggered by sending of a RTP packet this - // should have a minimum effect - - // with NACK we don't count old packets as received since they are re-transmitted - // we use RTT to decide if a packet is re-ordered or re-transmitted - } - - *missing = 0; - if(expSinceLast > recSinceLast) - { - *missing = (expSinceLast - recSinceLast); - } - WebRtc_UWord8 fractionLost = 0; - if(expSinceLast) - { - // scale 0 to 255, where 255 is 100% loss - fractionLost = (WebRtc_UWord8) ((255 * (*missing)) / expSinceLast); - } - if(fraction_lost) - { - *fraction_lost = fractionLost; - } - // we need a counter for cumulative loss too - _cumulativeLoss += *missing; - - if(_jitterQ4 > _jitterMaxQ4) - { - _jitterMaxQ4 = _jitterQ4; - } - if(cum_lost) - { - *cum_lost = _cumulativeLoss; - } - if(ext_max) - { - *ext_max = (_receivedSeqWraps<<16) + _receivedSeqMax; - } - if(jitter) - { - // note that the internal jitter value is in Q4 - // and needs to be scaled by 1/16 - *jitter = (_jitterQ4 >> 4); - } - if(max_jitter) - { - // note that the internal jitter value is in Q4 - // and needs to be scaled by 1/16 - *max_jitter = (_jitterMaxQ4 >> 4); - } - if(reset) - { - // store this report - _lastReportFractionLost = fractionLost; - _lastReportCumulativeLost = _cumulativeLoss; // 24 bits valid - _lastReportExtendedHighSeqNum = (_receivedSeqWraps<<16) + _receivedSeqMax; - _lastReportJitter = (_jitterQ4 >> 4); - - // only for report blocks in RTCP SR and RR - _lastReportInorderPackets = _receivedInorderPacketCount; - _lastReportOldPackets = _receivedOldPacketCount; - _lastReportSeqMax = _receivedSeqMax; - } - return 0; -} - -WebRtc_Word32 -RTPReceiver::DataCounters(WebRtc_UWord32 *bytesReceived, - WebRtc_UWord32 *packetsReceived) const -{ - CriticalSectionScoped lock(_criticalSectionRTPReceiver); - - if(bytesReceived) - { - *bytesReceived = _receivedByteCount; - } - if(packetsReceived) - { - *packetsReceived = _receivedOldPacketCount + _receivedInorderPacketCount; - } - return 0; -} - -void -RTPReceiver::ProcessBitrate() -{ - CriticalSectionScoped cs(_criticalSectionRTPReceiver); - - Bitrate::Process(); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_receiver.h b/modules/rtp_rtcp/source/rtp_receiver.h deleted file mode 100644 index 3c87aa977..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_H_ - -#include "typedefs.h" -#include "rtp_utility.h" - -#include "rtp_rtcp.h" -#include "rtp_receiver_audio.h" -#include "rtp_receiver_video.h" -#include "rtcp_receiver_help.h" -#include "Bitrate.h" - -namespace webrtc { -class RtpRtcpFeedback; -class Trace; - -class RTPReceiver : public RTPReceiverAudio, public RTPReceiverVideo, public Bitrate -{ -public: - RTPReceiver(const WebRtc_Word32 id, - const bool audio, - ModuleRtpRtcpPrivate& callback); - - virtual ~RTPReceiver(); - - virtual void ChangeUniqueId(const WebRtc_Word32 id); - - WebRtc_Word32 Init(); - - RtpVideoCodecTypes VideoCodecType() const; - WebRtc_UWord32 MaxConfiguredBitrate() const; - - WebRtc_Word32 SetPacketTimeout(const WebRtc_UWord32 timeoutMS); - void PacketTimeout(); - - void ProcessDeadOrAlive(const bool RTCPalive, const WebRtc_UWord32 now); - - void ProcessBitrate(); - - WebRtc_Word32 RegisterIncomingDataCallback(RtpData* incomingDataCallback); - WebRtc_Word32 RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback); - - WebRtc_Word32 RegisterReceivePayload( const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate); - - WebRtc_Word32 DeRegisterReceivePayload(const WebRtc_Word8 payloadType); - - WebRtc_Word32 ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - WebRtc_Word8* payloadType, - const WebRtc_UWord32 rate) const; - - WebRtc_Word32 ReceivePayload(const WebRtc_Word8 payloadType, - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels, - WebRtc_UWord32* rate) const; - - WebRtc_Word32 RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_Word8* payloadType, - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels) const; - - WebRtc_Word32 IncomingRTPPacket(WebRtcRTPHeader* rtpheader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 incomingRtpPacketLengt); - - NACKMethod NACK() const ; - - // Turn negative acknowledgement requests on/off - WebRtc_Word32 SetNACKStatus(const NACKMethod method); - - - // last received - virtual WebRtc_UWord32 TimeStamp() const; - virtual WebRtc_UWord16 SequenceNumber() const; - - WebRtc_Word32 EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const; - - WebRtc_UWord32 SSRC() const; - - WebRtc_Word32 CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const; - - WebRtc_Word32 Energy( WebRtc_UWord8 arrOfEnergy[kRtpCsrcSize]) const; - - // get the currently configured SSRC filter - WebRtc_Word32 SSRCFilter(WebRtc_UWord32& allowedSSRC) const; - - // set a SSRC to be used as a filter for incoming RTP streams - WebRtc_Word32 SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC); - - WebRtc_Word32 Statistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, // will be moved from JB - WebRtc_UWord32 *max_jitter, - bool reset = false) const; - - WebRtc_Word32 Statistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, // will be moved from JB - WebRtc_UWord32 *max_jitter, - WebRtc_Word32 *missing, - bool reset = false) const; - - WebRtc_Word32 DataCounters(WebRtc_UWord32 *bytesReceived, - WebRtc_UWord32 *packetsReceived) const; - - WebRtc_Word32 ResetStatistics(); - - WebRtc_Word32 ResetDataCounters(); - - WebRtc_UWord16 PacketOHReceived() const; - - WebRtc_UWord32 PacketCountReceived() const; - - WebRtc_UWord32 ByteCountReceived() const; - - virtual WebRtc_UWord32 PayloadTypeToPayload(const WebRtc_UWord8 payloadType, - ModuleRTPUtility::Payload*& payload) const; - -protected: - virtual WebRtc_Word32 CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader); - - virtual bool RetransmitOfOldPacket(const WebRtc_UWord16 sequenceNumber, - const WebRtc_UWord32 rtpTimeStamp) const; - - - void UpdateStatistics(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord16 bytes, - const bool oldPacket); - - virtual WebRtc_Word8 REDPayloadType() const; - -private: - // Is RED configured with payload type payloadType - bool REDPayloadType(const WebRtc_Word8 payloadType) const; - - bool InOrderPacket(const WebRtc_UWord16 sequenceNumber) const; - - void CheckSSRCChanged(const WebRtcRTPHeader* rtpHeader); - void CheckCSRC(const WebRtcRTPHeader* rtpHeader); - WebRtc_Word32 CheckPayloadChanged(const WebRtcRTPHeader* rtpHeader, - const WebRtc_Word8 firstPayloadByte, - bool& isRED, - ModuleRTPUtility::AudioPayload& audioSpecific, - ModuleRTPUtility::VideoPayload& videoSpecific); - - void UpdateNACKBitRate(WebRtc_Word32 bytes, WebRtc_UWord32 now); - bool ProcessNACKBitRate(WebRtc_UWord32 now); - -private: - WebRtc_Word32 _id; - const bool _audio; - - CriticalSectionWrapper& _criticalSectionCbs; - ModuleRtpRtcpPrivate& _cbPrivateFeedback; - RtpFeedback* _cbRtpFeedback; - RtpData* _cbRtpData; - - CriticalSectionWrapper& _criticalSectionRTPReceiver; - mutable WebRtc_UWord32 _lastReceiveTime; - WebRtc_UWord16 _lastReceivedPayloadLength; - WebRtc_Word8 _lastReceivedPayloadType; - WebRtc_Word8 _lastReceivedMediaPayloadType; - - ModuleRTPUtility::AudioPayload _lastReceivedAudioSpecific; - ModuleRTPUtility::VideoPayload _lastReceivedVideoSpecific; - - WebRtc_UWord32 _packetTimeOutMS; - - WebRtc_Word8 _redPayloadType; - - // - MapWrapper _payloadTypeMap; - - // SSRCs - WebRtc_UWord32 _SSRC; - WebRtc_UWord8 _numCSRCs; - WebRtc_UWord32 _currentRemoteCSRC[kRtpCsrcSize]; - WebRtc_UWord8 _numEnergy; - WebRtc_UWord8 _currentRemoteEnergy[kRtpCsrcSize]; - - bool _useSSRCFilter; - WebRtc_UWord32 _SSRCFilter; - - // stats on received RTP packets - WebRtc_UWord32 _jitterQ4; - mutable WebRtc_UWord32 _jitterMaxQ4; - mutable WebRtc_UWord32 _cumulativeLoss; - - WebRtc_UWord32 _localTimeLastReceivedTimestamp; - WebRtc_UWord32 _lastReceivedTimestamp; - WebRtc_UWord16 _lastReceivedSequenceNumber; - WebRtc_UWord16 _receivedSeqFirst; - WebRtc_UWord16 _receivedSeqMax; - WebRtc_UWord16 _receivedSeqWraps; - - // current counter values - WebRtc_UWord16 _receivedPacketOH; - WebRtc_UWord32 _receivedByteCount; - WebRtc_UWord32 _receivedOldPacketCount; - WebRtc_UWord32 _receivedInorderPacketCount; - - // counter values when we sent the last report - mutable WebRtc_UWord32 _lastReportInorderPackets; - mutable WebRtc_UWord32 _lastReportOldPackets; - mutable WebRtc_UWord16 _lastReportSeqMax; - mutable WebRtc_UWord8 _lastReportFractionLost; - mutable WebRtc_UWord32 _lastReportCumulativeLost; // 24 bits valid - mutable WebRtc_UWord32 _lastReportExtendedHighSeqNum; - mutable WebRtc_UWord32 _lastReportJitter; - - // NACK - NACKMethod _nackMethod; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_H_ diff --git a/modules/rtp_rtcp/source/rtp_receiver_audio.cc b/modules/rtp_rtcp/source/rtp_receiver_audio.cc deleted file mode 100644 index 2458725ca..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver_audio.cc +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_receiver_audio.h" - -#include //assert -#include // memcpy() -#include // pow() - -#include "critical_section_wrapper.h" - -namespace webrtc { -RTPReceiverAudio::RTPReceiverAudio(const WebRtc_Word32 id): - _id(id), - _lastReceivedFrequency(8000), - _telephoneEvent(false), - _telephoneEventForwardToDecoder(false), - _telephoneEventDetectEndOfTone(false), - _telephoneEventPayloadType(-1), - _telephoneEventReported(), - _cngNBPayloadType(-1), - _cngWBPayloadType(-1), - _cngSWBPayloadType(-1), - _cngPayloadType(-1), - _G722PayloadType(-1), - _lastReceivedG722(false), - _criticalSectionFeedback(*CriticalSectionWrapper::CreateCriticalSection()), - _cbAudioFeedback(NULL) -{ -} - -RTPReceiverAudio::~RTPReceiverAudio() -{ - delete &_criticalSectionFeedback; -} - -WebRtc_Word32 -RTPReceiverAudio::Init() -{ - _lastReceivedFrequency = 8000; - _telephoneEvent = false; - _telephoneEventForwardToDecoder = false; - _telephoneEventDetectEndOfTone = false; - _telephoneEventPayloadType = -1; - - while(_telephoneEventReported.Size() > 0) - { - _telephoneEventReported.Erase(_telephoneEventReported.First()); - } - _cngNBPayloadType = -1; - _cngWBPayloadType = -1; - _cngSWBPayloadType = -1; - _cngPayloadType = -1; - _G722PayloadType = -1; - _lastReceivedG722 = false; - return 0; -} - -void -RTPReceiverAudio::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -WebRtc_Word32 -RTPReceiverAudio::RegisterIncomingAudioCallback(RtpAudioFeedback* incomingMessagesCallback) -{ - CriticalSectionScoped lock(_criticalSectionFeedback); - _cbAudioFeedback = incomingMessagesCallback; - return 0; -} - -WebRtc_UWord32 -RTPReceiverAudio::AudioFrequency() const -{ - if(_lastReceivedG722) - { - return 8000; - } - return _lastReceivedFrequency; -} - -// Outband TelephoneEvent(DTMF) detection -WebRtc_Word32 -RTPReceiverAudio::SetTelephoneEventStatus(const bool enable, - const bool forwardToDecoder, - const bool detectEndOfTone) -{ - _telephoneEvent= enable; - _telephoneEventDetectEndOfTone = detectEndOfTone; - _telephoneEventForwardToDecoder = forwardToDecoder; - return 0; -} - - // Is outband TelephoneEvent(DTMF) turned on/off? -bool -RTPReceiverAudio::TelephoneEvent() const -{ - return _telephoneEvent; -} - -// Is forwarding of outband telephone events turned on/off? -bool -RTPReceiverAudio::TelephoneEventForwardToDecoder() const -{ - return _telephoneEventForwardToDecoder; -} - -bool -RTPReceiverAudio::TelephoneEventPayloadType(const WebRtc_Word8 payloadType) const -{ - return (_telephoneEventPayloadType == payloadType)?true:false; -} - -bool -RTPReceiverAudio::CNGPayloadType(const WebRtc_Word8 payloadType, - WebRtc_UWord32& frequency) -{ - // we can have three CNG on 8000Hz, 16000Hz and 32000Hz - if(_cngNBPayloadType == payloadType) - { - frequency = 8000; - if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngNBPayloadType)) - { - ResetStatistics(); - } - _cngPayloadType = _cngNBPayloadType; - return true; - } else if(_cngWBPayloadType == payloadType) - { - // if last received codec is G.722 we must use frequency 8000 - if(_lastReceivedG722) - { - frequency = 8000; - } else - { - frequency = 16000; - } - if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngWBPayloadType)) - { - ResetStatistics(); - } - _cngPayloadType = _cngWBPayloadType; - return true; - }else if(_cngSWBPayloadType == payloadType) - { - frequency = 32000; - if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngSWBPayloadType)) - { - ResetStatistics(); - } - _cngPayloadType = _cngSWBPayloadType; - return true; - }else - { - // not CNG - if(_G722PayloadType == payloadType) - { - _lastReceivedG722 = true; - }else - { - _lastReceivedG722 = false; - } - } - return false; -} - -/* - Sample based or frame based codecs based on RFC 3551 - - NOTE! There is one error in the RFC, stating G.722 uses 8 bits/samples. - The correct rate is 4 bits/sample. - - name of sampling default - encoding sample/frame bits/sample rate ms/frame ms/packet - - Sample based audio codecs - DVI4 sample 4 var. 20 - G722 sample 4 16,000 20 - G726-40 sample 5 8,000 20 - G726-32 sample 4 8,000 20 - G726-24 sample 3 8,000 20 - G726-16 sample 2 8,000 20 - L8 sample 8 var. 20 - L16 sample 16 var. 20 - PCMA sample 8 var. 20 - PCMU sample 8 var. 20 - - Frame based audio codecs - G723 frame N/A 8,000 30 30 - G728 frame N/A 8,000 2.5 20 - G729 frame N/A 8,000 10 20 - G729D frame N/A 8,000 10 20 - G729E frame N/A 8,000 10 20 - GSM frame N/A 8,000 20 20 - GSM-EFR frame N/A 8,000 20 20 - LPC frame N/A 8,000 20 20 - MPA frame N/A var. var. - - G7221 frame N/A -*/ - -ModuleRTPUtility::Payload* -RTPReceiverAudio::RegisterReceiveAudioPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) -{ - WebRtc_Word32 length = (WebRtc_Word32)strlen(payloadName); - if(length > RTP_PAYLOAD_NAME_SIZE) - { - assert(false); - return NULL; - } - - if (ModuleRTPUtility::StringCompare(payloadName,"telephone-event",15)) - { - _telephoneEventPayloadType = payloadType; - } - if (ModuleRTPUtility::StringCompare(payloadName,"cn",2)) - { - // we can have three CNG on 8000Hz, 16000Hz and 32000Hz - if(frequency == 8000) - { - _cngNBPayloadType = payloadType; - - } else if(frequency == 16000) - { - _cngWBPayloadType = payloadType; - - } else if(frequency == 32000) - { - _cngSWBPayloadType = payloadType; - }else - { - assert(false); - return NULL; - } - } - WebRtc_UWord8 bitsPerSample = 0; // zero implies frame based - if (ModuleRTPUtility::StringCompare(payloadName,"DVI4",4)) - { - bitsPerSample = 4; - } else if(ModuleRTPUtility::StringCompare(payloadName,"G722",4)) - { - if(ModuleRTPUtility::StringCompare(payloadName,"G7221",5)) - { - // frame based - } else - { - _G722PayloadType = payloadType; - bitsPerSample = 4; - } - } else if(ModuleRTPUtility::StringCompare(payloadName,"G726-40",7)) - { - bitsPerSample = 5; - } else if(ModuleRTPUtility::StringCompare(payloadName,"G726-32",7)) - { - bitsPerSample = 4; - } else if(ModuleRTPUtility::StringCompare(payloadName,"G726-24",7)) - { - bitsPerSample = 3; - } else if(ModuleRTPUtility::StringCompare(payloadName,"G726-16",7)) - { - bitsPerSample = 2; - } else if(ModuleRTPUtility::StringCompare(payloadName,"L8",2)) - { - bitsPerSample = 8; - } else if(ModuleRTPUtility::StringCompare(payloadName,"L16",3)) - { - bitsPerSample = 16; - } else if(ModuleRTPUtility::StringCompare(payloadName,"PCMU",4)) - { - bitsPerSample = 8; - } else if(ModuleRTPUtility::StringCompare(payloadName,"PCMA",4)) - { - bitsPerSample = 8; - } - - ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload; - memcpy(payload->name, payloadName, length+1); - payload->typeSpecific.Audio.frequency = frequency; - payload->typeSpecific.Audio.channels = channels; - payload->typeSpecific.Audio.bitsPerSample = bitsPerSample; - payload->typeSpecific.Audio.rate = rate; - payload->audio = true; - return payload; -} - -// we are not allowed to have any critsects when calling CallbackOfReceivedPayloadData -WebRtc_Word32 -RTPReceiverAudio::ParseAudioCodecSpecific(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadLength, - const ModuleRTPUtility::AudioPayload& audioSpecific, - const bool isRED) -{ - WebRtc_UWord8 newEvents[MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS]; - WebRtc_UWord8 removedEvents[MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS]; - WebRtc_UWord8 numberOfNewEvents = 0; - WebRtc_UWord8 numberOfRemovedEvents = 0; - bool telephoneEventPacket = TelephoneEventPayloadType(rtpHeader->header.payloadType); - - if(payloadLength == 0) - { - return 0; - } - - { - CriticalSectionScoped lock(_criticalSectionFeedback); - - if(telephoneEventPacket) - { - // RFC 4733 2.3 - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | event |E|R| volume | duration | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - if(payloadLength % 4 != 0) - { - return -1; - } - WebRtc_UWord8 numberOfEvents = payloadLength / 4; - - // sanity - if(numberOfEvents >= MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS) - { - numberOfEvents = MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS; - } - for (int n = 0; n < numberOfEvents; n++) - { - bool end = (payloadData[(4*n)+1] & 0x80)? true:false; - - if(_telephoneEventReported.Find(payloadData[4*n]) != NULL) - { - // we have already seen this event - if(end) - { - removedEvents[numberOfRemovedEvents]= payloadData[4*n]; - numberOfRemovedEvents++; - _telephoneEventReported.Erase(payloadData[4*n]); - } - }else - { - if(end) - { - // don't add if it's a end of a tone - }else - { - newEvents[numberOfNewEvents] = payloadData[4*n]; - numberOfNewEvents++; - _telephoneEventReported.Insert(payloadData[4*n],NULL); - } - } - } - - // RFC 4733 2.5.1.3 & 2.5.2.3 Long-Duration Events - // should not be a problem since we don't care about the duration - - // RFC 4733 See 2.5.1.5. & 2.5.2.4. Multiple Events in a Packet - } - - if(_telephoneEvent && _cbAudioFeedback) - { - for (int n = 0; n < numberOfNewEvents; n++) - { - _cbAudioFeedback->OnReceivedTelephoneEvent(_id, newEvents[n], false); - } - if(_telephoneEventDetectEndOfTone) - { - for (int n = 0; n < numberOfRemovedEvents; n++) - { - _cbAudioFeedback->OnReceivedTelephoneEvent(_id, removedEvents[n], true); - } - } - } - } - if(! telephoneEventPacket ) - { - _lastReceivedFrequency = audioSpecific.frequency; - } - - // Check if this is a CNG packet, receiver might want to know - WebRtc_UWord32 dummy; - if(CNGPayloadType(rtpHeader->header.payloadType, dummy)) - { - rtpHeader->type.Audio.isCNG=true; - rtpHeader->frameType = kAudioFrameCN; - }else - { - rtpHeader->frameType = kAudioFrameSpeech; - rtpHeader->type.Audio.isCNG=false; - } - - // check if it's a DTMF event, hence something we can playout - if(telephoneEventPacket) - { - if(!_telephoneEventForwardToDecoder) - { - // don't forward event to decoder - return 0; - } - MapItem* first = _telephoneEventReported.First(); - if(first && first->GetId() > 15) - { - // don't forward non DTMF events - return 0; - } - } - if(isRED && !(payloadData[0] & 0x80)) - { - // we recive only one frame packed in a RED packet remove the RED wrapper - rtpHeader->header.payloadType = payloadData[0]; - - // only one frame in the RED strip the one byte to help NetEq - return CallbackOfReceivedPayloadData(payloadData+1, - payloadLength-1, - rtpHeader); - } - if(audioSpecific.channels > 1) - { - WebRtc_Word32 retVal = 0; - WebRtc_UWord16 channelLength = payloadLength/audioSpecific.channels; - - if(audioSpecific.bitsPerSample > 0) - { - // sanity - assert((payloadLength*8)%audioSpecific.bitsPerSample == 0); - - // sample based codec - - // build matrix - WebRtc_UWord8 matrix[IP_PACKET_SIZE]; - WebRtc_UWord32 offsetBytes = 0; - WebRtc_UWord32 offsetBytesInsert = 0; - // initialize matrix to 0 - memset(matrix, 0, audioSpecific.channels*channelLength); - - switch(audioSpecific.bitsPerSample) - { - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - { - WebRtc_UWord32 offsetSamples = 0; - WebRtc_UWord32 offsetSamplesInsert = 0; - WebRtc_UWord16 bitMask = (WebRtc_UWord16)ModuleRTPUtility::pow2(audioSpecific.bitsPerSample)-1; - WebRtc_UWord16 samplesPerChannel =payloadLength*8/audioSpecific.bitsPerSample/audioSpecific.channels; - - for(WebRtc_UWord32 i = 0; i < samplesPerChannel; i++) - { - WebRtc_UWord8 insertShift = (WebRtc_UWord8)((offsetSamplesInsert+audioSpecific.bitsPerSample)%16); - insertShift = 16 - insertShift; // inverse the calculation - - for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++) - { - // get sample - WebRtc_UWord16 s = payloadData[offsetBytes] << 8; - - // check that we don't read outside the memory - if(offsetBytes < (WebRtc_UWord32)payloadLength -2) - { - s += payloadData[offsetBytes+1]; - } - - WebRtc_UWord8 readShift = (WebRtc_UWord8)((offsetSamples+audioSpecific.bitsPerSample)%16); - readShift = 16 - readShift; // inverse the calculation - s >>= readShift; - s &= bitMask; - - // prepare for reading next sample - offsetSamples += audioSpecific.bitsPerSample; - if(readShift <= audioSpecific.bitsPerSample) - { - // next does not fitt - // or fitt exactly - offsetSamples -= 8; - offsetBytes++; - } - - // insert sample into matrix - WebRtc_UWord32 columOffset = j*channelLength; - - WebRtc_UWord16 insert = s << insertShift; -#if defined(WEBRTC_LITTLE_ENDIAN) - matrix[columOffset+offsetBytesInsert] |= static_cast(insert>>8); - matrix[columOffset+offsetBytesInsert+1] |= static_cast(insert); -#else - WebRtc_UWord16* matrixU16 = (WebRtc_UWord16*)&(matrix[columOffset+offsetBytesInsert]); - matrixU16[0] |= (s << insertShift); -#endif - } - // prepare for writing next sample - offsetSamplesInsert += audioSpecific.bitsPerSample; - if(insertShift <= audioSpecific.bitsPerSample) - { - // next does not fitt - // or fitt exactly - offsetSamplesInsert -= 8; - offsetBytesInsert++; - } - } - } - break; - case 8: - { - WebRtc_UWord32 sample = 0; - for(WebRtc_UWord32 i = 0; i < channelLength; i++) - { - for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++) - { - WebRtc_UWord32 columOffset = j*channelLength; - matrix[columOffset + i] = payloadData[sample++]; - } - } - } - break; - case 16: - { - WebRtc_UWord32 sample = 0; - for(WebRtc_UWord32 i = 0; i < channelLength; i +=2) - { - for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++) - { - WebRtc_UWord32 columOffset = j*channelLength; - matrix[columOffset + i] = payloadData[sample++]; - matrix[columOffset + i + 1] = payloadData[sample++]; - } - } - } - break; - default: - assert(false); - return -1; - } - // we support 16 bits sample - // callback for all channels - for(int channel = 0; channel < audioSpecific.channels && retVal == 0; channel++) - { - // one callback per channel - rtpHeader->type.Audio.channel = channel+1; - - if(channel == 0) - { - // include the original packet only in the first callback - retVal = CallbackOfReceivedPayloadData(&matrix[channel*channelLength], - channelLength, - rtpHeader); - } else - { - retVal = CallbackOfReceivedPayloadData(&matrix[channel*channelLength], - channelLength, - rtpHeader); - } - } - } else - { - for(int channel = 1; channel <= audioSpecific.channels && retVal == 0; channel++) - { - // one callback per channel - rtpHeader->type.Audio.channel = channel; - - if(channel == 1) - { - // include the original packet only in the first callback - retVal = CallbackOfReceivedPayloadData(payloadData, - channelLength, - rtpHeader); - } else - { - retVal = CallbackOfReceivedPayloadData(payloadData, - channelLength, - rtpHeader); - } - payloadData += channelLength; - } - } - return retVal; - }else - { - rtpHeader->type.Audio.channel = 1; - return CallbackOfReceivedPayloadData(payloadData, - payloadLength, - rtpHeader); - } -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_receiver_audio.h b/modules/rtp_rtcp/source/rtp_receiver_audio.h deleted file mode 100644 index 6fe9bf543..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver_audio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_ - -#include "rtp_rtcp_defines.h" -#include "rtp_utility.h" - -#include "typedefs.h" -#include "map_wrapper.h" - -namespace webrtc { -class CriticalSectionWrapper; - -class RTPReceiverAudio -{ -public: - RTPReceiverAudio(const WebRtc_Word32 id); - virtual ~RTPReceiverAudio(); - - virtual void ChangeUniqueId(const WebRtc_Word32 id); - - WebRtc_Word32 Init(); - - WebRtc_Word32 RegisterIncomingAudioCallback(RtpAudioFeedback* incomingMessagesCallback); - - ModuleRTPUtility::Payload* RegisterReceiveAudioPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate); - - WebRtc_UWord32 AudioFrequency() const; - - // Outband TelephoneEvent (DTMF) detection - WebRtc_Word32 SetTelephoneEventStatus(const bool enable, - const bool forwardToDecoder, - const bool detectEndOfTone); - - // Is outband DTMF(AVT) turned on/off? - bool TelephoneEvent() const ; - - // Is forwarding of outband telephone events turned on/off? - bool TelephoneEventForwardToDecoder() const ; - - // Is TelephoneEvent configured with payload type payloadType - bool TelephoneEventPayloadType(const WebRtc_Word8 payloadType) const; - - // Is CNG configured with payload type payloadType - bool CNGPayloadType(const WebRtc_Word8 payloadType, WebRtc_UWord32& frequency); - - WebRtc_Word32 ParseAudioCodecSpecific(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadLength, - const ModuleRTPUtility::AudioPayload& audioSpecific, - const bool isRED); - - virtual WebRtc_Word32 ResetStatistics() = 0; - -protected: - virtual WebRtc_Word32 CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader) = 0; -private: - WebRtc_Word32 _id; - - WebRtc_UWord32 _lastReceivedFrequency; - - bool _telephoneEvent; - bool _telephoneEventForwardToDecoder; - bool _telephoneEventDetectEndOfTone; - WebRtc_Word8 _telephoneEventPayloadType; - MapWrapper _telephoneEventReported; - - WebRtc_Word8 _cngNBPayloadType; - WebRtc_Word8 _cngWBPayloadType; - WebRtc_Word8 _cngSWBPayloadType; - WebRtc_Word8 _cngPayloadType; - - // G722 is special since it use the wrong number of RTP samples in timestamp VS. number of samples in the frame - WebRtc_Word8 _G722PayloadType; - bool _lastReceivedG722; - - CriticalSectionWrapper& _criticalSectionFeedback; - RtpAudioFeedback* _cbAudioFeedback; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_AUDIO_H_ diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.cc b/modules/rtp_rtcp/source/rtp_receiver_video.cc deleted file mode 100644 index 894e5deac..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver_video.cc +++ /dev/null @@ -1,680 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include //assert -#include // memcpy() -#include - -#include "rtp_receiver_video.h" - -#include "trace.h" -#include "critical_section_wrapper.h" -#include "tick_util.h" - -#include "receiver_fec.h" - -namespace webrtc { -WebRtc_UWord32 BitRateBPS(WebRtc_UWord16 x ) -{ - return (x & 0x3fff) * WebRtc_UWord32(pow(10.0f,(2 + (x >> 14)))); -} - -RTPReceiverVideo::RTPReceiverVideo(const WebRtc_Word32 id, - ModuleRtpRtcpPrivate& callback): - _id(id), - _criticalSectionFeedback(*CriticalSectionWrapper::CreateCriticalSection()), - _cbVideoFeedback(NULL), - _cbPrivateFeedback(callback), - _criticalSectionReceiverVideo(*CriticalSectionWrapper::CreateCriticalSection()), - - _completeFrame(false), - _receiveFEC(NULL), - _packetStartTimeMs(0), - _receivedBW(), - _estimatedBW(0), - _currentFecFrameDecoded(false), - _h263InverseLogic(false), - _overUseDetector(), - _videoBitRate(), - _lastBitRateChange(0), - _packetOverHead(28) -{ - memset(_receivedBW, 0,sizeof(_receivedBW)); -} - -RTPReceiverVideo::~RTPReceiverVideo() -{ - delete &_criticalSectionFeedback; - delete &_criticalSectionReceiverVideo; - delete _receiveFEC; -} - -WebRtc_Word32 -RTPReceiverVideo::Init() -{ - _completeFrame = false; - _packetStartTimeMs = 0; - _estimatedBW = 0; - _currentFecFrameDecoded = false; - _packetOverHead = 28; - for (int i = 0; i < BW_HISTORY_SIZE; i++) - { - _receivedBW[i] = 0; - } - ResetOverUseDetector(); - return 0; -} - -void -RTPReceiverVideo::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -WebRtc_Word32 -RTPReceiverVideo::RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback) -{ - CriticalSectionScoped lock(_criticalSectionFeedback); - _cbVideoFeedback = incomingMessagesCallback; - return 0; -} - -void -RTPReceiverVideo::UpdateBandwidthManagement(const WebRtc_UWord32 minBitrateBps, - const WebRtc_UWord32 maxBitrateBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) -{ - CriticalSectionScoped lock(_criticalSectionFeedback); - if(_cbVideoFeedback) - { - _cbVideoFeedback->OnNetworkChanged(_id, minBitrateBps, maxBitrateBps, fractionLost, roundTripTimeMs, bwEstimateKbitMin, bwEstimateKbitMax); - } -} - -ModuleRTPUtility::Payload* -RTPReceiverVideo::RegisterReceiveVideoPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxRate) -{ - RtpVideoCodecTypes videoType = kRtpNoVideo; - if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) - { - videoType = kRtpVp8Video; - - } else if ((ModuleRTPUtility::StringCompare(payloadName, "H263-1998", 9)) || - (ModuleRTPUtility::StringCompare(payloadName, "H263-2000", 9))) - { - videoType = kRtpH2631998Video; - - } else if (ModuleRTPUtility::StringCompare(payloadName, "H263", 4)) - { - videoType = kRtpH263Video; - - } else if (ModuleRTPUtility::StringCompare(payloadName, "MP4V-ES", 7)) - { - videoType = kRtpMpeg4Video; - - } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) - { - videoType = kRtpNoVideo; - - } else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) - { - // store this - if(_receiveFEC == NULL) - { - _receiveFEC = new ReceiverFEC(_id, this); - } - _receiveFEC->SetPayloadTypeFEC(payloadType); - videoType = kRtpFecVideo; - }else - { - return NULL; - } - - ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload; - strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE); - payload->typeSpecific.Video.videoCodecType = videoType; - payload->typeSpecific.Video.maxRate = maxRate; - payload->audio = false; - return payload; -} - -void RTPReceiverVideo::ResetOverUseDetector() -{ - _overUseDetector.Reset(); - _videoBitRate.Init(); - _lastBitRateChange = 0; -} - -// called under _criticalSectionReceiverVideo -WebRtc_UWord16 -RTPReceiverVideo::EstimateBandwidth(const WebRtc_UWord16 bandwidth) -{ - // received fragments - // estimate BW - - WebRtc_UWord16 bwSort[BW_HISTORY_SIZE]; - for(int i = 0; i < BW_HISTORY_SIZE-1; i++) - { - _receivedBW[i] = _receivedBW[i+1]; - bwSort[i] = _receivedBW[i+1]; - } - _receivedBW[BW_HISTORY_SIZE-1] = bandwidth; - bwSort[BW_HISTORY_SIZE-1] = bandwidth; - - WebRtc_UWord16 temp; - for (int i = BW_HISTORY_SIZE-1; i >= 0; i--) - { - for (int j = 1; j <= i; j++) - { - if (bwSort[j-1] > bwSort[j]) - { - temp = bwSort[j-1]; - bwSort[j-1] = bwSort[j]; - bwSort[j] = temp; - } - } - } - int zeroCount = 0; - for (; zeroCount < BW_HISTORY_SIZE; zeroCount++) - { - if (bwSort[zeroCount]!= 0) - { - break; - } - } - WebRtc_UWord32 indexMedian = (BW_HISTORY_SIZE -1) - (BW_HISTORY_SIZE-zeroCount)/2; - WebRtc_UWord16 bandwidthMedian = bwSort[indexMedian]; - - if (bandwidthMedian > 0) - { - if (_estimatedBW == bandwidth) - { - // don't trigger a callback - bandwidthMedian = 0; - } else - { - _estimatedBW = bandwidthMedian; - } - } else - { - // can't be negative - bandwidthMedian = 0; - } - - return bandwidthMedian; -} - -// we have no critext when calling this -// we are not allowed to have any critsects when calling CallbackOfReceivedPayloadData -WebRtc_Word32 -RTPReceiverVideo::ParseVideoCodecSpecific(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength, - const RtpVideoCodecTypes videoType, - const bool isRED, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 incomingRtpPacketSize) -{ - WebRtc_Word32 retVal = 0; - - _criticalSectionReceiverVideo.Enter(); - - _videoBitRate.Update(payloadDataLength, TickTime::MillisecondTimestamp()); - - // Add headers, ideally we would like to include for instance - // Ethernet header here as well. - const WebRtc_UWord16 packetSize = payloadDataLength + _packetOverHead + - rtpHeader->header.headerLength + rtpHeader->header.paddingLength; - _overUseDetector.Update(*rtpHeader, packetSize); - - if (isRED) - { - if(_receiveFEC == NULL) - { - _criticalSectionReceiverVideo.Leave(); - return -1; - } - if (rtpHeader->header.timestamp != TimeStamp()) - { - // We have a new frame. Force a decode with the existing packets. - retVal = _receiveFEC->ProcessReceivedFEC(true); - _currentFecFrameDecoded = false; - } - - bool FECpacket = false; - if(retVal != -1) - { - if (!_currentFecFrameDecoded) - { - retVal = _receiveFEC->AddReceivedFECPacket(rtpHeader, incomingRtpPacket, payloadDataLength, FECpacket); - - if (retVal != -1 && (FECpacket || rtpHeader->header.markerBit)) - { - // Only attempt a decode after receiving the last media packet. - retVal = _receiveFEC->ProcessReceivedFEC(false); - } - }else - { - _receiveFEC->AddReceivedFECInfo(rtpHeader,incomingRtpPacket, FECpacket); - } - } - _criticalSectionReceiverVideo.Leave(); - - if(retVal == 0 && FECpacket ) - { - // callback with the received FEC packet, the normal packets are deliverd after parsing - // this contain the original RTP packet header but with empty payload and data length - rtpHeader->frameType = kFrameEmpty; - WebRtc_Word32 retVal = SetCodecType(videoType, rtpHeader); //we need this for the routing - if(retVal != 0) - { - return retVal; - } - retVal =CallbackOfReceivedPayloadData(NULL, - 0, - rtpHeader); - } - }else - { - // will leave the _criticalSectionReceiverVideo critsect - retVal = ParseVideoCodecSpecificSwitch(rtpHeader, - payloadData, - payloadDataLength, - videoType); - } - - // Update the remote rate control object and update the overuse - // detector with the current rate control region. - _criticalSectionReceiverVideo.Enter(); - const RateControlInput input(_overUseDetector.State(), - _videoBitRate.BitRate( - TickTime::MillisecondTimestamp()), - _overUseDetector.NoiseVar()); - _criticalSectionReceiverVideo.Leave(); - - // Call the callback outside critical section - const RateControlRegion region = _cbPrivateFeedback.OnOverUseStateUpdate(input); - - _criticalSectionReceiverVideo.Enter(); - _overUseDetector.SetRateControlRegion(region); - _criticalSectionReceiverVideo.Leave(); - - return retVal; -} - -WebRtc_Word32 -RTPReceiverVideo::BuildRTPheader(const WebRtcRTPHeader* rtpHeader, - WebRtc_UWord8* dataBuffer) const -{ - dataBuffer[0] = static_cast(0x80); // version 2 - dataBuffer[1] = static_cast(rtpHeader->header.payloadType); - if (rtpHeader->header.markerBit) - { - dataBuffer[1] |= kRtpMarkerBitMask; // MarkerBit is 1 - } - - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, rtpHeader->header.sequenceNumber); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, rtpHeader->header.timestamp); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, rtpHeader->header.ssrc); - - WebRtc_Word32 rtpHeaderLength = 12; - - // Add the CSRCs if any - if (rtpHeader->header.numCSRCs > 0) - { - if(rtpHeader->header.numCSRCs > 16) - { - // error - assert(false); - } - WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength]; - for (WebRtc_UWord32 i = 0; i < rtpHeader->header.numCSRCs; ++i) - { - ModuleRTPUtility::AssignUWord32ToBuffer(ptr, rtpHeader->header.arrOfCSRCs[i]); - ptr +=4; - } - dataBuffer[0] = (dataBuffer[0]&0xf0) | rtpHeader->header.numCSRCs; - - // Update length of header - rtpHeaderLength += sizeof(WebRtc_UWord32)*rtpHeader->header.numCSRCs; - } - return rtpHeaderLength; -} - -WebRtc_Word32 -RTPReceiverVideo::ReceiveRecoveredPacketCallback(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - _criticalSectionReceiverVideo.Enter(); - - _currentFecFrameDecoded = true; - - ModuleRTPUtility::Payload* payload = NULL; - if (PayloadTypeToPayload(rtpHeader->header.payloadType, payload) != 0) - { - return -1; - } - // here we can re-create the original lost packet so that we can use it for the relay - // we need to re-create the RED header too - WebRtc_UWord8 recoveredPacket[IP_PACKET_SIZE]; - WebRtc_UWord16 rtpHeaderLength = (WebRtc_UWord16)BuildRTPheader(rtpHeader, recoveredPacket); - - const WebRtc_UWord8 REDForFECHeaderLength = 1; - - // replace pltype - recoveredPacket[1] &= 0x80; // reset - recoveredPacket[1] += REDPayloadType(); // replace with RED payload type - - // add RED header - recoveredPacket[rtpHeaderLength] = rtpHeader->header.payloadType; // f-bit always 0 - - memcpy(recoveredPacket + rtpHeaderLength + REDForFECHeaderLength, payloadData, payloadDataLength); - - return ParseVideoCodecSpecificSwitch(rtpHeader, - payloadData, - payloadDataLength, - payload->typeSpecific.Video.videoCodecType); -} - -WebRtc_Word32 -RTPReceiverVideo::SetCodecType(const RtpVideoCodecTypes videoType, - WebRtcRTPHeader* rtpHeader) const -{ - switch (videoType) - { - case kRtpNoVideo: - rtpHeader->type.Video.codec = kRTPVideoGeneric; - break; - case kRtpVp8Video: - rtpHeader->type.Video.codec = kRTPVideoVP8; - break; - case kRtpH263Video: - rtpHeader->type.Video.codec = kRTPVideoH263; - break; - case kRtpH2631998Video: - rtpHeader->type.Video.codec = kRTPVideoH263; - break; - case kRtpMpeg4Video: - rtpHeader->type.Video.codec = kRTPVideoMPEG4; - break; - case kRtpFecVideo: - rtpHeader->type.Video.codec = kRTPVideoFEC; - break; - default: - assert(((void)"ParseCodecSpecific videoType can not be unknown here!", false)); - return -1; - } - return 0; -} - - -WebRtc_Word32 -RTPReceiverVideo::ParseVideoCodecSpecificSwitch(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength, - const RtpVideoCodecTypes videoType) -{ - WebRtc_Word32 retVal = SetCodecType(videoType, rtpHeader); - if(retVal != 0) - { - return retVal; - } - - // all receive functions release _criticalSectionReceiverVideo before returning - switch (videoType) - { - case kRtpNoVideo: - retVal = ReceiveGenericCodec(rtpHeader, payloadData, payloadDataLength); - break; - case kRtpVp8Video: - retVal = ReceiveVp8Codec(rtpHeader, payloadData, payloadDataLength); - break; - case kRtpH263Video: - retVal = ReceiveH263Codec(rtpHeader, payloadData, payloadDataLength); - break; - case kRtpH2631998Video: - retVal = ReceiveH2631998Codec(rtpHeader,payloadData, payloadDataLength); - break; - case kRtpMpeg4Video: - retVal = ReceiveMPEG4Codec(rtpHeader,payloadData, payloadDataLength); - break; - default: - _criticalSectionReceiverVideo.Leave(); - assert(((void)"ParseCodecSpecific videoType can not be unknown here!", false)); - return -1; - } - return retVal; -} - -WebRtc_Word32 -RTPReceiverVideo::ReceiveH263Codec(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH263Video, - payloadData, - payloadDataLength); - ModuleRTPUtility::RTPPayload parsedPacket; - const bool success = rtpPayloadParser.Parse(parsedPacket); - - // from here down we only work on local data - _criticalSectionReceiverVideo.Leave(); - - if (!success) - { - return -1; - } - if (IP_PACKET_SIZE < parsedPacket.info.H263.dataLength + parsedPacket.info.H263.insert2byteStartCode? 2:0) - { - return -1; - } - return ReceiveH263CodecCommon(parsedPacket, rtpHeader); -} - -WebRtc_Word32 -RTPReceiverVideo::ReceiveH2631998Codec(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpH2631998Video, - payloadData, - payloadDataLength); - - ModuleRTPUtility::RTPPayload parsedPacket; - const bool success = rtpPayloadParser.Parse(parsedPacket); - if (!success) - { - _criticalSectionReceiverVideo.Leave(); - return -1; - } - if (IP_PACKET_SIZE < parsedPacket.info.H263.dataLength + parsedPacket.info.H263.insert2byteStartCode? 2:0) - { - _criticalSectionReceiverVideo.Leave(); - return -1; - } - // from here down we only work on local data - _criticalSectionReceiverVideo.Leave(); - - return ReceiveH263CodecCommon(parsedPacket, rtpHeader); -} - -WebRtc_Word32 -RTPReceiverVideo::ReceiveH263CodecCommon(ModuleRTPUtility::RTPPayload& parsedPacket, - WebRtcRTPHeader* rtpHeader) -{ - rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta; - if (_h263InverseLogic) // Microsoft H263 bug - { - if (rtpHeader->frameType == kVideoFrameKey) - rtpHeader->frameType = kVideoFrameDelta; - else - rtpHeader->frameType = kVideoFrameKey; - } - rtpHeader->type.Video.isFirstPacket = parsedPacket.info.H263.hasPictureStartCode; - - // if p == 0 - // it's a follow-on packet, hence it's not independently decodable - rtpHeader->type.Video.codecHeader.H263.independentlyDecodable = parsedPacket.info.H263.hasPbit; - - if (parsedPacket.info.H263.hasPictureStartCode) - { - rtpHeader->type.Video.width = parsedPacket.info.H263.frameWidth; - rtpHeader->type.Video.height = parsedPacket.info.H263.frameHeight; - } else - { - rtpHeader->type.Video.width = 0; - rtpHeader->type.Video.height = 0; - } - rtpHeader->type.Video.codecHeader.H263.bits = (parsedPacket.info.H263.startBits > 0)?true:false; - - // copy to a local buffer - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - WebRtc_UWord16 dataLength = 0; - - // we need to copy since we modify the first byte - if(parsedPacket.info.H263.insert2byteStartCode) - { - dataBuffer[0] = 0; - dataBuffer[1] = 0; - memcpy(dataBuffer+2, parsedPacket.info.H263.data, parsedPacket.info.H263.dataLength); - dataLength = 2 + parsedPacket.info.H263.dataLength; - } else - { - memcpy(dataBuffer, parsedPacket.info.H263.data, parsedPacket.info.H263.dataLength); - dataLength = parsedPacket.info.H263.dataLength; - } - - if(parsedPacket.info.H263.dataLength > 0) - { - if(parsedPacket.info.H263.startBits > 0) - { - // make sure that the ignored start bits are zero - dataBuffer[0] &= (0xff >> parsedPacket.info.H263.startBits); - } - if(parsedPacket.info.H263.endBits > 0) - { - // make sure that the ignored end bits are zero - dataBuffer[parsedPacket.info.H263.dataLength -1] &= ((0xff << parsedPacket.info.H263.endBits) & 0xff); - } - } - - return CallbackOfReceivedPayloadData(dataBuffer, dataLength, rtpHeader); -} - -WebRtc_Word32 -RTPReceiverVideo::ReceiveMPEG4Codec(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpMpeg4Video, - payloadData, - payloadDataLength); - - ModuleRTPUtility::RTPPayload parsedPacket; - const bool success = rtpPayloadParser.Parse(parsedPacket); - if (!success) - { - _criticalSectionReceiverVideo.Leave(); - return -1; - } - // from here down we only work on local data - _criticalSectionReceiverVideo.Leave(); - - rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta; - rtpHeader->type.Video.isFirstPacket = parsedPacket.info.MPEG4.isFirstPacket; - - if(CallbackOfReceivedPayloadData(parsedPacket.info.MPEG4.data, - parsedPacket.info.MPEG4.dataLength, - rtpHeader) != 0) - { - return -1; - } - return 0; - } - -WebRtc_Word32 -RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - ModuleRTPUtility::RTPPayloadParser rtpPayloadParser(kRtpVp8Video, - payloadData, - payloadDataLength); - - ModuleRTPUtility::RTPPayload parsedPacket; - const bool success = rtpPayloadParser.Parse(parsedPacket); - - // from here down we only work on local data - _criticalSectionReceiverVideo.Leave(); - - if (!success) - { - return -1; - } - if (parsedPacket.info.VP8.dataLength == 0) - { - // we have an "empty" VP8 packet, it's ok, could be one way video - return 0; - } - rtpHeader->frameType = (parsedPacket.frameType == ModuleRTPUtility::kIFrame) ? kVideoFrameKey : kVideoFrameDelta; - - rtpHeader->type.Video.codecHeader.VP8.startBit = parsedPacket.info.VP8.startFragment; // Start of partition - rtpHeader->type.Video.codecHeader.VP8.stopBit= parsedPacket.info.VP8.stopFragment; // Stop of partition - - rtpHeader->type.Video.isFirstPacket = parsedPacket.info.VP8.beginningOfFrame; - - if(CallbackOfReceivedPayloadData(parsedPacket.info.VP8.data, - parsedPacket.info.VP8.dataLength, - rtpHeader) != 0) - { - return -1; - } - return 0; -} - - -WebRtc_Word32 -RTPReceiverVideo::ReceiveGenericCodec(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) -{ - rtpHeader->frameType = kVideoFrameKey; - - if(((SequenceNumber() + 1) == rtpHeader->header.sequenceNumber) && - (TimeStamp() != rtpHeader->header.timestamp)) - { - rtpHeader->type.Video.isFirstPacket = true; - } - _criticalSectionReceiverVideo.Leave(); - - if(CallbackOfReceivedPayloadData(payloadData, payloadDataLength, rtpHeader) != 0) - { - return -1; - } - return 0; -} - -WebRtc_Word32 RTPReceiverVideo::SetH263InverseLogic(const bool enable) -{ - _h263InverseLogic = enable; - return 0; -} - -void RTPReceiverVideo::SetPacketOverHead(WebRtc_UWord16 packetOverHead) -{ - _packetOverHead = packetOverHead; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.h b/modules/rtp_rtcp/source/rtp_receiver_video.h deleted file mode 100644 index 8d36490fe..000000000 --- a/modules/rtp_rtcp/source/rtp_receiver_video.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_ - -#include "rtp_rtcp_defines.h" -#include "rtp_rtcp_private.h" -#include "rtp_utility.h" - -#include "typedefs.h" -#include "map_wrapper.h" -#include "list_wrapper.h" - -#include "overuse_detector.h" -#include "remote_rate_control.h" -#include "Bitrate.h" - -namespace webrtc { -class ReceiverFEC; - -class RTPReceiverVideo -{ -public: - RTPReceiverVideo(const WebRtc_Word32 id, - ModuleRtpRtcpPrivate& callback); - - virtual ~RTPReceiverVideo(); - - virtual void ChangeUniqueId(const WebRtc_Word32 id); - - WebRtc_Word32 Init(); - - WebRtc_Word32 RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback); - - void UpdateBandwidthManagement(const WebRtc_UWord32 minBitrateBps, - const WebRtc_UWord32 maxBitrateBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax); - - ModuleRTPUtility::Payload* RegisterReceiveVideoPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxRate); - - WebRtc_Word32 ParseVideoCodecSpecific(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength, - const RtpVideoCodecTypes videoType, - const bool isRED, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 incomingRtpPacketSize); - - WebRtc_Word32 SetH263InverseLogic(const bool enable); - - WebRtc_Word32 ReceiveRecoveredPacketCallback(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - void SetPacketOverHead(WebRtc_UWord16 packetOverHead); - -protected: - void ResetOverUseDetector(); - - WebRtc_UWord16 EstimateBandwidth( const WebRtc_UWord16 bufferLength); - - virtual WebRtc_Word32 CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader) = 0; - - virtual WebRtc_UWord32 TimeStamp() const = 0; - virtual WebRtc_UWord16 SequenceNumber() const = 0; - - virtual WebRtc_UWord32 PayloadTypeToPayload(const WebRtc_UWord8 payloadType, - ModuleRTPUtility::Payload*& payload) const = 0; - - virtual bool RetransmitOfOldPacket(const WebRtc_UWord16 sequenceNumber, - const WebRtc_UWord32 rtpTimeStamp) const = 0; - - virtual WebRtc_Word8 REDPayloadType() const = 0; - - WebRtc_Word32 SetCodecType(const RtpVideoCodecTypes videoType, - WebRtcRTPHeader* rtpHeader) const; - - WebRtc_Word32 ParseVideoCodecSpecificSwitch(WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength, - const RtpVideoCodecTypes videoType); - - WebRtc_Word32 ReceiveGenericCodec(WebRtcRTPHeader *rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - WebRtc_Word32 ReceiveH263Codec(WebRtcRTPHeader *rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - WebRtc_Word32 ReceiveH2631998Codec(WebRtcRTPHeader *rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - WebRtc_Word32 ReceiveH263CodecCommon(ModuleRTPUtility::RTPPayload& parsedPacket, - WebRtcRTPHeader* rtpHeader); - - WebRtc_Word32 ReceiveMPEG4Codec(WebRtcRTPHeader *rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - WebRtc_Word32 ReceiveVp8Codec(WebRtcRTPHeader *rtpHeader, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); - - WebRtc_Word32 BuildRTPheader(const WebRtcRTPHeader* rtpHeader, - WebRtc_UWord8* dataBuffer) const; - -private: - WebRtc_Word32 _id; - - CriticalSectionWrapper& _criticalSectionFeedback; - RtpVideoFeedback* _cbVideoFeedback; - - ModuleRtpRtcpPrivate& _cbPrivateFeedback; - - CriticalSectionWrapper& _criticalSectionReceiverVideo; - - // bandwidth - bool _completeFrame; - WebRtc_UWord32 _packetStartTimeMs; - WebRtc_UWord16 _receivedBW[BW_HISTORY_SIZE]; - WebRtc_UWord16 _estimatedBW; - - // FEC - bool _currentFecFrameDecoded; - ReceiverFEC* _receiveFEC; - - // H263 - bool _h263InverseLogic; - - // BWE - OverUseDetector _overUseDetector; - BitRateStats _videoBitRate; - WebRtc_Word64 _lastBitRateChange; - WebRtc_UWord16 _packetOverHead; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_VIDEO_H_ diff --git a/modules/rtp_rtcp/source/rtp_rtcp.gyp b/modules/rtp_rtcp/source/rtp_rtcp.gyp deleted file mode 100644 index fdd8ee8ab..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp.gyp +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'rtp_rtcp', - 'type': '<(library)', - 'dependencies': [ - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../interface', - ], - }, - 'sources': [ - # Common - '../interface/rtp_rtcp.h', - '../interface/rtp_rtcp_defines.h', - 'bitrate.cc', - 'Bitrate.h', - 'rtp_rtcp_config.h', - 'rtp_rtcp_impl.cc', - 'rtp_rtcp_impl.h', - 'rtp_rtcp_private.h', - 'rtcp_receiver.cc', - 'rtcp_receiver.h', - 'rtcp_receiver_help.cc', - 'rtcp_receiver_help.h', - 'rtcp_sender.cc', - 'rtcp_sender.h', - 'rtcp_utility.cc', - 'rtcp_utility.h', - 'rtp_receiver.cc', - 'rtp_receiver.h', - 'rtp_sender.cc', - 'rtp_sender.h', - 'rtp_utility.cc', - 'rtp_utility.h', - 'ssrc_database.cc', - 'ssrc_database.h', - 'tmmbr_help.cc', - 'tmmbr_help.h', - # Audio Files - 'dtmf_queue.cc', - 'dtmf_queue.h', - 'rtp_receiver_audio.cc', - 'rtp_receiver_audio.h', - 'rtp_sender_audio.cc', - 'rtp_sender_audio.h', - # Video Files - 'bandwidth_management.cc', - 'bandwidth_management.h', - 'bwe_defines.h', - 'fec_private_tables.h', - 'forward_error_correction.cc', - 'forward_error_correction.h', - 'forward_error_correction_internal.cc', - 'forward_error_correction_internal.h', - 'overuse_detector.cc', - 'overuse_detector.h', - 'h263_information.cc', - 'h263_information.h', - 'remote_rate_control.cc', - 'remote_rate_control.h', - 'rtp_receiver_video.cc', - 'rtp_receiver_video.h', - 'rtp_sender_video.cc', - 'rtp_sender_video.h', - 'receiver_fec.cc', - 'receiver_fec.h', - 'video_codec_information.h', - 'rtp_format_vp8.cc', - 'rtp_format_vp8.h', - ], # source - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/rtp_rtcp/source/rtp_rtcp_config.h b/modules/rtp_rtcp/source/rtp_rtcp_config.h deleted file mode 100644 index 3afb78d37..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp_config.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_CONFIG_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_CONFIG_H_ - -// Configuration file for RTP utilities (RTPSender, RTPReceiver ...) -namespace webrtc { -enum { kRtpRtcpMaxIdleTimeProcess = 10 }; - -enum { NACK_PACKETS_MAX_SIZE = 256 }; // in packets -enum { NACK_BYTECOUNT_SIZE = 60}; // size of our NACK history - -enum { RTCP_INTERVAL_VIDEO_MS = 1000 }; -enum { RTCP_INTERVAL_AUDIO_MS = 5000 }; -enum { RTCP_SEND_BEFORE_KEY_FRAME_MS= 100 }; -enum { RTCP_MAX_REPORT_BLOCKS = 31}; // RFC 3550 page 37 -enum { RTCP_MIN_FRAME_LENGTH_MS = 17}; -enum { kRtcpAppCode_DATA_SIZE = 32*4}; // multiple of 4, this is not a limitation of the size -enum { RTCP_RPSI_DATA_SIZE = 30}; -enum { RTCP_NUMBER_OF_SR = 60 }; - -enum { MAX_NUMBER_OF_TEMPORAL_ID = 8 }; // RFC -enum { MAX_NUMBER_OF_DEPENDENCY_QUALITY_ID = 128 };// RFC - -enum { BW_HISTORY_SIZE = 35}; - -#define MIN_AUDIO_BW_MANAGEMENT_BITRATE 6 -#define MIN_VIDEO_BW_MANAGEMENT_BITRATE 30 - -enum { DTMF_OUTBAND_MAX = 20}; - -enum { RTP_MAX_BURST_SLEEP_TIME = 500 }; -enum { RTP_AUDIO_LEVEL_UNIQUE_ID = 0xbede }; -enum { RTP_MAX_PACKETS_PER_FRAME= 512 }; // must be multiple of 32 -} // namespace webrtc - - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_CONFIG_H_ diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc deleted file mode 100644 index ae55a0515..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ /dev/null @@ -1,2596 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "common_types.h" -#include "rtp_rtcp_impl.h" -#include "trace.h" - -#ifdef MATLAB -#include "../test/BWEStandAlone/MatlabPlot.h" -extern MatlabEngine eng; // global variable defined elsewhere -#endif - -#include //memcpy -#include //assert - -// local for this file -namespace -{ - const float FracMS = 4.294967296E6f; -} - -#ifdef _WIN32 - // disable warning C4355: 'this' : used in base member initializer list - #pragma warning(disable : 4355) -#endif - -namespace webrtc { -using namespace RTCPUtility; - -RtpRtcp* -RtpRtcp::CreateRtpRtcp(const WebRtc_Word32 id, - const bool audio) -{ - if(audio) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id, "CreateRtpRtcp(audio)"); - } else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, id, "CreateRtpRtcp(video)"); - } - return new ModuleRtpRtcpImpl(id, audio); -} - -void RtpRtcp::DestroyRtpRtcp(RtpRtcp* module) -{ - if(module) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, static_cast(module)->Id(), "DestroyRtpRtcp()"); - delete static_cast(module); - } -} - -ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const WebRtc_Word32 id, - const bool audio): - TMMBRHelp(audio), - _id(id), - _audio(audio), - _collisionDetected(false), - _lastProcessTime(ModuleRTPUtility::GetTimeInMS()), - - _packetOverHead(28), // IPV4 UDP - _criticalSectionModulePtrs(*CriticalSectionWrapper::CreateCriticalSection()), - _criticalSectionModulePtrsFeedback(*CriticalSectionWrapper::CreateCriticalSection()), - _defaultModule(NULL), - _audioModule(NULL), - _videoModule(NULL), - _childModules(), - _deadOrAliveActive(false), - _deadOrAliveTimeoutMS(0), - _deadOrAliveLastTimer(0), - _rtpReceiver(id, audio, *this), - _rtcpReceiver(id,*this), - _bandwidthManagement(id), - _receivedNTPsecsAudio(0), - _receivedNTPfracAudio(0), - _RTCPArrivalTimeSecsAudio(0), - _RTCPArrivalTimeFracAudio(0), - _rtpSender(id, audio), - _rtcpSender(id, audio, *this), - _nackMethod(kNackOff), - _nackLastTimeSent(0), - _nackLastSeqNumberSent(0), - _keyFrameReqMethod(kKeyFrameReqFirRtp), - _lastChildBitrateUpdate(0) -#ifdef MATLAB - ,_plot1(NULL) -#endif -{ - // make sure that RTCP objects are aware of our SSRC - WebRtc_UWord32 SSRC = _rtpSender.SSRC(); - _rtcpSender.SetSSRC(SSRC); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); -} - -ModuleRtpRtcpImpl::~ModuleRtpRtcpImpl() -{ - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); - - // make sure to unregister this module from other modules - - const bool defaultInstance(_childModules.Empty()?false:true); - - if(defaultInstance) - { - // deregister for the default module - // will go in to the child modules and remove it self - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - _childModules.Erase(item); - if(module) - { - module->DeRegisterDefaultModule(); - } - item = _childModules.First(); - } - } else - { - // deregister for the child modules - // will go in to the default and remove it self - DeRegisterDefaultModule(); - } - - if(_audio) - { - DeRegisterVideoModule(); - } else - { - DeRegisterSyncModule(); - } - -#ifdef MATLAB - if (_plot1) - { - eng.DeletePlot(_plot1); - _plot1 = NULL; - } -#endif - - delete &_criticalSectionModulePtrs; - delete &_criticalSectionModulePtrsFeedback; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "Version(bufferLength:%d)", remainingBufferInBytes); - return GetVersion(version, remainingBufferInBytes, position); -} - -WebRtc_Word32 -RtpRtcp::GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - if(version == NULL) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, "Invalid in argument to Version()"); - return -1; - } - WebRtc_Word8 ourVersion[] = "Module RTP RTCP 1.3.0"; - WebRtc_UWord32 ourLength = (WebRtc_UWord32)strlen(ourVersion); - if(remainingBufferInBytes < ourLength +1) - { - return -1; - } - memcpy(version, ourVersion, ourLength); - version[ourLength] = '\0'; // null terminaion - remainingBufferInBytes -= (ourLength + 1); - position += (ourLength + 1); - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ChangeUniqueId(new id:%d)", id); - - _id = id; - - _rtpReceiver.ChangeUniqueId(id); - _rtcpReceiver.ChangeUniqueId(id); - _rtpSender.ChangeUniqueId(id); - _rtcpSender.ChangeUniqueId(id); - return 0; -} - -// default encoder that we need to multiplex out -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterDefaultModule(RtpRtcp* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterDefaultModule(module:0x%x)", module); - - if(module == NULL) - { - return -1; - } - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - if(_defaultModule) - { - _defaultModule->DeRegisterChildModule(this); - } - _defaultModule = (ModuleRtpRtcpPrivate*)module; - _defaultModule->RegisterChildModule(this); - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::DeRegisterDefaultModule() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterDefaultModule()"); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - _defaultModule->DeRegisterChildModule(this); - _defaultModule = NULL; - } - return 0; -} - -bool ModuleRtpRtcpImpl::DefaultModuleRegistered() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DefaultModuleRegistered()"); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - return true; - } - else - { - return false; - } -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::NumberChildModules() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "NumberChildModules"); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - CriticalSectionScoped doubleLock(_criticalSectionModulePtrsFeedback); - // we use two locks for protecting _childModules one (_criticalSectionModulePtrsFeedback) for incoming - // messages (BitrateSent and UpdateTMMBR) and _criticalSectionModulePtrs for all outgoing messages sending packets etc - - return _childModules.GetSize(); -} - -void -ModuleRtpRtcpImpl::RegisterChildModule(RtpRtcp* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterChildModule(module:0x%x)", module); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - CriticalSectionScoped doubleLock(_criticalSectionModulePtrsFeedback); - // we use two locks for protecting _childModules one (_criticalSectionModulePtrsFeedback) for incoming - // messages (BitrateSent and UpdateTMMBR) and _criticalSectionModulePtrs for all outgoing messages sending packets etc - - _childModules.PushFront(module); -} - -void -ModuleRtpRtcpImpl::DeRegisterChildModule(RtpRtcp* removeModule) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterChildModule(module:0x%x)", removeModule); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - CriticalSectionScoped doubleLock(_criticalSectionModulePtrsFeedback); - - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if(module == removeModule) - { - _childModules.Erase(item); - return; - } - item = _childModules.Next(item); - } -} - -// Lip-sync between voice-video engine, -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterSyncModule(RtpRtcp* audioModule) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterSyncModule(module:0x%x)", audioModule); - - if(audioModule == NULL) - { - return -1; - } - if(_audio) - { - return -1; - } - CriticalSectionScoped lock(_criticalSectionModulePtrs); - _audioModule = (ModuleRtpRtcpPrivate*)audioModule; - return _audioModule->RegisterVideoModule(this); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::DeRegisterSyncModule() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterSyncModule()"); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_audioModule) - { - ModuleRtpRtcpPrivate* audioModule=_audioModule; - _audioModule = NULL; - _receivedNTPsecsAudio = 0; - _receivedNTPfracAudio = 0; - _RTCPArrivalTimeSecsAudio = 0; - _RTCPArrivalTimeFracAudio = 0; - audioModule->DeRegisterVideoModule(); - } - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterVideoModule(RtpRtcp* videoModule) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterVideoModule(module:0x%x)", videoModule); - - if(videoModule == NULL) - { - return -1; - } - if(!_audio) - { - return -1; - } - CriticalSectionScoped lock(_criticalSectionModulePtrs); - _videoModule = (ModuleRtpRtcpPrivate*)videoModule; - return 0; -} - -void -ModuleRtpRtcpImpl::DeRegisterVideoModule() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterVideoModule()"); - - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_videoModule) - { - ModuleRtpRtcpPrivate* videoModule=_videoModule; - _videoModule=NULL; - videoModule->DeRegisterSyncModule(); - } -} - -// returns the number of milliseconds until the module want a worker thread to call Process -WebRtc_Word32 -ModuleRtpRtcpImpl::TimeUntilNextProcess() -{ - const WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - return kRtpRtcpMaxIdleTimeProcess - (now -_lastProcessTime); -} - -// Process any pending tasks such as timeouts -// non time critical events -WebRtc_Word32 -ModuleRtpRtcpImpl::Process() -{ - _lastProcessTime = ModuleRTPUtility::GetTimeInMS(); - - _rtpReceiver.PacketTimeout(); - _rtcpReceiver.PacketTimeout(); - - _rtpSender.ProcessBitrate(); - _rtpReceiver.ProcessBitrate(); - - ProcessDeadOrAliveTimer(); - - if(_rtcpSender.TimeToSendRTCPReport()) - { - WebRtc_UWord16 RTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL,NULL,NULL); - _rtcpSender.SendRTCP(kRtcpReport, 0, 0, RTT); - } - if(_rtpSender.RTPKeepalive()) - { - // check time to send RTP keep alive - if( _rtpSender.TimeToSendRTPKeepalive()) - { - _rtpSender.SendRTPKeepalivePacket(); - } - } - if(UpdateRTCPReceiveInformationTimers()) - { - // a receiver has timed out - UpdateTMMBR(); - } - return 0; -} - - /** - * Receiver - */ - -WebRtc_Word32 -ModuleRtpRtcpImpl::InitReceiver() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "InitReceiver()"); - - _packetOverHead = 28; // default is IPV4 UDP - _receivedNTPsecsAudio = 0; - _receivedNTPfracAudio = 0; - _RTCPArrivalTimeSecsAudio = 0; - _RTCPArrivalTimeFracAudio = 0; - - WebRtc_Word32 ret = _rtpReceiver.Init(); - if (ret < 0) - { - return ret; - } - _rtpReceiver.SetPacketOverHead(_packetOverHead); - return ret; -} - -void -ModuleRtpRtcpImpl::ProcessDeadOrAliveTimer() -{ - if(_deadOrAliveActive) - { - const WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - if(now > _deadOrAliveTimeoutMS +_deadOrAliveLastTimer) - { - _deadOrAliveLastTimer += _deadOrAliveTimeoutMS; - - bool RTCPalive = false; // RTCP is alive if we have received a report the last 12 seconds - if(_rtcpReceiver.LastReceived() + 12000 > now) - { - RTCPalive = true; - } - _rtpReceiver.ProcessDeadOrAlive(RTCPalive, now); - } - } -} - - // Set periodic dead or alive notification -WebRtc_Word32 -ModuleRtpRtcpImpl::SetPeriodicDeadOrAliveStatus(const bool enable, - const WebRtc_UWord8 sampleTimeSeconds) -{ - if(enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetPeriodicDeadOrAliveStatus(enable, %d)", sampleTimeSeconds); - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetPeriodicDeadOrAliveStatus(disable)"); - } - - if(sampleTimeSeconds == 0) - { - return -1; - } - _deadOrAliveActive = enable; - _deadOrAliveTimeoutMS = sampleTimeSeconds*1000; - _deadOrAliveLastTimer = ModuleRTPUtility::GetTimeInMS(); // trigger the first after one period - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::PeriodicDeadOrAliveStatus(bool &enable, - WebRtc_UWord8 &sampleTimeSeconds) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "PeriodicDeadOrAliveStatus()"); - - enable = _deadOrAliveActive; - sampleTimeSeconds = (WebRtc_UWord8)(_deadOrAliveTimeoutMS/1000); - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetPacketTimeout(const WebRtc_UWord32 RTPtimeoutMS, - const WebRtc_UWord32 RTCPtimeoutMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetPacketTimeout(%u,%u)",RTPtimeoutMS, RTCPtimeoutMS); - - if(_rtpReceiver.SetPacketTimeout(RTPtimeoutMS) == 0) - { - return _rtcpReceiver.SetPacketTimeout(RTCPtimeoutMS); - } - return -1; -} - -// set codec name and payload type -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterReceivePayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterReceivePayload()"); - - return _rtpReceiver.RegisterReceivePayload(payloadName, payloadType, frequency, channels, rate); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::DeRegisterReceivePayload(const WebRtc_Word8 payloadType) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterReceivePayload(%d)", payloadType); - - return _rtpReceiver.DeRegisterReceivePayload(payloadType); -} - - // get configured payload type -WebRtc_Word32 -ModuleRtpRtcpImpl::ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - WebRtc_Word8* payloadType, - const WebRtc_UWord32 rate) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ReceivePayloadType()"); - - return _rtpReceiver.ReceivePayloadType(payloadName, frequency, channels, payloadType, rate); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::ReceivePayload(const WebRtc_Word8 payloadType, - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels, - WebRtc_UWord32* rate) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ReceivePayload()"); - - return _rtpReceiver.ReceivePayload(payloadType, payloadName, frequency, channels, rate); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_Word8* payloadType, - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemotePayload()"); - - return _rtpReceiver.RemotePayload(payloadName, payloadType, frequency, channels); -} - - // get the currently configured SSRC filter -WebRtc_Word32 -ModuleRtpRtcpImpl::SSRCFilter(WebRtc_UWord32& allowedSSRC) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SSRCFilter()"); - - return _rtpReceiver.SSRCFilter(allowedSSRC); -} - - // set a SSRC to be used as a filter for incoming RTP streams -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC) -{ - if(enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSSRCFilter(enable, 0x%x)", allowedSSRC); - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSSRCFilter(disable)"); - } - - return _rtpReceiver.SetSSRCFilter(enable, allowedSSRC); -} - -// Get last received remote timestamp -WebRtc_UWord32 -ModuleRtpRtcpImpl::RemoteTimestamp() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteTimestamp()"); - - return _rtpReceiver.TimeStamp(); -} - -// Get the current estimated remote timestamp -WebRtc_Word32 -ModuleRtpRtcpImpl::EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "EstimatedRemoteTimeStamp()"); - - return _rtpReceiver.EstimatedRemoteTimeStamp(timestamp); -} - -// Get incoming SSRC -WebRtc_UWord32 -ModuleRtpRtcpImpl::RemoteSSRC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteSSRC()"); - - return _rtpReceiver.SSRC(); -} - -// Get remote CSRC -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoteCSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteCSRCs()"); - - return _rtpReceiver.CSRCs(arrOfCSRC); -} - -// called by the network module when we receive a packet -WebRtc_Word32 -ModuleRtpRtcpImpl::IncomingPacket(const WebRtc_UWord8* incomingPacket, - const WebRtc_UWord16 incomingPacketLength) -{ - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "IncomingPacket(packetLength:%u)", incomingPacketLength); - - // minimum RTP is 12 bytes - // minimum RTCP is 8 bytes (RTCP BYE) - if(incomingPacketLength < 8 || incomingPacket == NULL) - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, "IncomingPacket invalid buffer or length"); - return -1; - } - // check RTP version - const WebRtc_UWord8 version = incomingPacket[0] >> 6 ; - if(version != 2) - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, "IncomingPacket invalid RTP version"); - return -1; - } - - ModuleRTPUtility::RTPHeaderParser rtpParser(incomingPacket, incomingPacketLength); - - if(rtpParser.RTCP()) - { - RTCPUtility::RTCPParserV2 rtcpParser(incomingPacket, - incomingPacketLength, - true); // Allow receive of non-compound RTCP packets. - - const bool validRTCPHeader = rtcpParser.IsValid(); - if(!validRTCPHeader) - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, "IncomingPacket invalid RTCP packet"); - return -1; - } - RTCPHelp::RTCPPacketInformation rtcpPacketInformation; - WebRtc_Word32 retVal = _rtcpReceiver.IncomingRTCPPacket(rtcpPacketInformation, - &rtcpParser); - if(retVal == 0) - { - _rtcpReceiver.TriggerCallbacksFromRTCPPacket(rtcpPacketInformation); - } - return retVal; - - } else - { - WebRtcRTPHeader rtpHeader; - memset(&rtpHeader, 0, sizeof(rtpHeader)); - - const bool validRTPHeader = rtpParser.Parse(rtpHeader); - if(!validRTPHeader) - { - WEBRTC_TRACE(kTraceDebug, kTraceRtpRtcp, _id, "IncomingPacket invalid RTP header"); - return -1; - } - return _rtpReceiver.IncomingRTPPacket(&rtpHeader, - incomingPacket, - incomingPacketLength); - } -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::IncomingAudioNTP(const WebRtc_UWord32 audioReceivedNTPsecs, - const WebRtc_UWord32 audioReceivedNTPfrac, - const WebRtc_UWord32 audioRTCPArrivalTimeSecs, - const WebRtc_UWord32 audioRTCPArrivalTimeFrac) -{ - _receivedNTPsecsAudio = audioReceivedNTPsecs; - _receivedNTPfracAudio = audioReceivedNTPfrac; - _RTCPArrivalTimeSecsAudio = audioRTCPArrivalTimeSecs; - _RTCPArrivalTimeFracAudio = audioRTCPArrivalTimeFrac; - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterIncomingDataCallback(RtpData* incomingDataCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterIncomingDataCallback(incomingDataCallback:0x%x)", incomingDataCallback); - - return _rtpReceiver.RegisterIncomingDataCallback(incomingDataCallback); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterIncomingRTPCallback(incomingMessagesCallback:0x%x)",incomingMessagesCallback); - - return _rtpReceiver.RegisterIncomingRTPCallback(incomingMessagesCallback); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterIncomingRTCPCallback(RtcpFeedback* incomingMessagesCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterIncomingRTCPCallback(incomingMessagesCallback:0x%x)",incomingMessagesCallback); - - return _rtcpReceiver.RegisterIncomingRTCPCallback(incomingMessagesCallback); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterIncomingVideoCallback(incomingMessagesCallback:0x%x)",incomingMessagesCallback); - - if(_rtcpReceiver.RegisterIncomingVideoCallback(incomingMessagesCallback) == 0) - { - return _rtpReceiver.RegisterIncomingVideoCallback(incomingMessagesCallback); - } - return -1; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterAudioCallback(RtpAudioFeedback* messagesCallback) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterAudioCallback(messagesCallback:0x%x)",messagesCallback); - - if(_rtpSender.RegisterAudioCallback(messagesCallback) == 0) - { - return _rtpReceiver.RegisterIncomingAudioCallback(messagesCallback); - } - return -1; -} - - /** - * Sender - */ - -WebRtc_Word32 -ModuleRtpRtcpImpl::InitSender() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "InitSender()"); - - _collisionDetected = false; - - // if we are already receiving inform our sender to avoid collision - if(_rtpSender.Init(_rtpReceiver.SSRC()) != 0) - { - return -1; - } - WebRtc_Word32 retVal = _rtcpSender.Init(); - - // make sure that RTCP objects are aware of our SSRC (it could have changed due to collision) - WebRtc_UWord32 SSRC = _rtpSender.SSRC(); - _rtcpReceiver.SetSSRC(SSRC); - _rtcpSender.SetSSRC(SSRC); - return retVal; -} - -bool -ModuleRtpRtcpImpl::RTPKeepalive() const -{ - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "RTPKeepalive()"); - - return _rtpSender.RTPKeepalive(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RTPKeepaliveStatus(bool* enable, - WebRtc_Word8* unknownPayloadType, - WebRtc_UWord16* deltaTransmitTimeMS) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RTPKeepaliveStatus()"); - - return _rtpSender.RTPKeepaliveStatus(enable, unknownPayloadType, deltaTransmitTimeMS); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetRTPKeepaliveStatus(bool enable, WebRtc_Word8 unknownPayloadType, WebRtc_UWord16 deltaTransmitTimeMS) -{ - if (enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetRTPKeepaliveStatus(enable, payloadType:%d deltaTransmitTimeMS:%u)",unknownPayloadType,deltaTransmitTimeMS); - - // check the transmit keepalive delta time [1,60] - if (deltaTransmitTimeMS < 1000 || deltaTransmitTimeMS > 60000) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tinvalid deltaTransmitTimeSeconds (%d)", deltaTransmitTimeMS); - return (-1); - } - - // check the payload time [0,127] - if (unknownPayloadType < 0 ) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tinvalid unknownPayloadType (%d)", unknownPayloadType); - return (-1); - } - - // enable RTP keepalive mechanism - return _rtpSender.EnableRTPKeepalive(unknownPayloadType, deltaTransmitTimeMS); - - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetRTPKeepaliveStatus(disable)"); - return _rtpSender.DisableRTPKeepalive(); - } -} - -// set codec name and payload type -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterSendPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterSendPayload(payloadName:%s payloadType:%d frequency:%u)", payloadName, payloadType, frequency); - - return _rtpSender.RegisterPayload(payloadName, payloadType, frequency, channels, rate); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::DeRegisterSendPayload(const WebRtc_Word8 payloadType) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "DeRegisterSendPayload(%d)", payloadType); - - return _rtpSender.DeRegisterSendPayload(payloadType); -} - -WebRtc_Word8 -ModuleRtpRtcpImpl::SendPayloadType() const -{ - return _rtpSender.SendPayloadType(); -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::StartTimestamp() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "StartTimestamp()"); - - return _rtpSender.StartTimestamp(); -} - - // configure start timestamp, default is a random number -WebRtc_Word32 -ModuleRtpRtcpImpl::SetStartTimestamp(const WebRtc_UWord32 timestamp) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetStartTimestamp(%d)", timestamp); - - return _rtpSender.SetStartTimestamp(timestamp, true); -} - -WebRtc_UWord16 -ModuleRtpRtcpImpl::SequenceNumber() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SequenceNumber()"); - - return _rtpSender.SequenceNumber(); -} - - // Set SequenceNumber, default is a random number -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSequenceNumber(const WebRtc_UWord16 seqNum) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSequenceNumber(%d)",seqNum); - - return _rtpSender.SetSequenceNumber(seqNum); -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::SSRC() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SSRC()"); - - return _rtpSender.SSRC(); -} - - // configure SSRC, default is a random number -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSSRC(const WebRtc_UWord32 ssrc) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSSRC(%d)", ssrc); - - if(_rtpSender.SetSSRC(ssrc) == 0) - { - _rtcpReceiver.SetSSRC(ssrc); - _rtcpSender.SetSSRC(ssrc); - return 0; - } - return -1; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetCSRCStatus(const bool include) -{ - _rtcpSender.SetCSRCStatus(include); - return _rtpSender.SetCSRCStatus(include); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "CSRCs()"); - - return _rtpSender.CSRCs(arrOfCSRC); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetCSRCs(arrLength:%d)", arrLength); - - const bool defaultInstance(_childModules.Empty()?false:true); - - if(defaultInstance) - { - // for default we need to update all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if(module) - { - module->SetCSRCs(arrOfCSRC, arrLength); - } - item = _childModules.Next(item); - } - return 0; - - } else - { - for(int i = 0;i < arrLength;i++) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "\tidx:%d CSRC:%u", i, arrOfCSRC[i]); - } - _rtcpSender.SetCSRCs(arrOfCSRC, arrLength); - return _rtpSender.SetCSRCs(arrOfCSRC, arrLength); - } -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::PacketCountSent() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "PacketCountSent()"); - - return _rtpSender.Packets(); -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::ByteCountSent() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ByteCountSent()"); - - return _rtpSender.Bytes(); -} - -int ModuleRtpRtcpImpl::CurrentSendFrequencyHz() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "CurrentSendFrequencyHz()"); - - return _rtpSender.SendPayloadFrequency(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSendingStatus(const bool sending) -{ - if(sending) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendingStatus(sending)"); - }else - { - if(_rtpSender.RTPKeepalive()) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Can't SetSendingStatus(stopped) when RTP Keepalive is active"); - return -1; - } - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendingStatus(stopped)"); - } - if(_rtcpSender.Sending() != sending) - { - // sends RTCP BYE when going from true to false - WebRtc_Word32 retVal = _rtcpSender.SetSendingStatus(sending); - - _collisionDetected = false; - - // generate a new timeStamp if true and not configured via API - // generate a new SSRC for the next "call" if false - _rtpSender.SetSendingStatus(sending); - - // make sure that RTCP objects are aware of our SSRC (it could have changed due to collision) - WebRtc_UWord32 SSRC = _rtpSender.SSRC(); - _rtcpReceiver.SetSSRC(SSRC); - _rtcpSender.SetSSRC(SSRC); - return retVal; - } - return 0; -} - -bool -ModuleRtpRtcpImpl::Sending() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "Sending()"); - - return _rtcpSender.Sending(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSendingMediaStatus(const bool sending) -{ - if(sending) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendingMediaStatus(sending)"); - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendingMediaStatus(stopped)"); - } - _rtpSender.SetSendingMediaStatus(sending); - return 0; -} - -bool -ModuleRtpRtcpImpl::SendingMedia() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "Sending()"); - - const bool haveChildModules(_childModules.Empty()?false:true); - - if(!haveChildModules) - { - return _rtpSender.SendingMedia(); - } - else - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - ListItem* item = _childModules.First(); - if(item) - { - RTPSender& rtpSender = static_cast(item->GetItem())->_rtpSender; - if (rtpSender.SendingMedia()) - { - return true; - } - item = _childModules.Next(item); - } - } - return false; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RegisterSendTransport(Transport* outgoingTransport) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RegisterSendTransport(0x%x)", outgoingTransport); - - if(_rtpSender.RegisterSendTransport(outgoingTransport) == 0) - { - return _rtcpSender.RegisterSendTransport(outgoingTransport); - } - return -1; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SendOutgoingData(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, - "SendOutgoingData(frameType:%d payloadType:%d timeStamp:%u payloadSize:%u)", - frameType, payloadType, timeStamp, payloadSize); - - if(_rtcpSender.TimeToSendRTCPReport(kVideoFrameKey == frameType)) - { - WebRtc_UWord16 RTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL,NULL,NULL); - _rtcpSender.SendRTCP(kRtcpReport, 0, 0, RTT); - } - - const bool haveChildModules(_childModules.Empty()?false:true); - - WebRtc_Word32 retVal = -1; - if(!haveChildModules) - { - retVal = _rtpSender.SendOutgoingData(frameType, - payloadType, - timeStamp, - payloadData, - payloadSize, - fragmentation, - NULL, - rtpTypeHdr); - } else - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - VideoCodecInformation* codecInfo = NULL; - - ListItem* item = _childModules.First(); - if(item) - { - RTPSender& rtpSender = static_cast(item->GetItem())->_rtpSender; - retVal = rtpSender.SendOutgoingData(frameType, - payloadType, - timeStamp, - payloadData, - payloadSize, - fragmentation, - NULL, - rtpTypeHdr); - - item = _childModules.Next(item); - } - - // send to all remaining "child" modules - while(item) - { - RTPSender& rtpSender = static_cast(item->GetItem())->_rtpSender; - retVal = rtpSender.SendOutgoingData(frameType, - payloadType, - timeStamp, - payloadData, - payloadSize, - fragmentation, - codecInfo, - rtpTypeHdr); - - item = _childModules.Next(item); - } - } - return retVal; -} - -WebRtc_UWord16 -ModuleRtpRtcpImpl::MaxPayloadLength() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "MaxPayloadLength()"); - - return _rtpSender.MaxPayloadLength(); -} - -WebRtc_UWord16 -ModuleRtpRtcpImpl::MaxDataPayloadLength() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "MaxDataPayloadLength()"); - - WebRtc_UWord16 minDataPayloadLength = IP_PACKET_SIZE-28; // Assuming IP/UDP - - const bool defaultInstance(_childModules.Empty() ? false : true); - if (defaultInstance) - { - // for default we need to update all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - ListItem* item = _childModules.First(); - while(item) - { - RtpRtcp* module = static_cast(item->GetItem()); - if (module) - { - WebRtc_UWord16 dataPayloadLength = module->MaxDataPayloadLength(); - if (dataPayloadLength < minDataPayloadLength) - { - minDataPayloadLength = dataPayloadLength; - } - } - item = _childModules.Next(item); - } - } - - WebRtc_UWord16 dataPayloadLength = _rtpSender.MaxDataPayloadLength(); - if (dataPayloadLength < minDataPayloadLength) - { - minDataPayloadLength = dataPayloadLength; - } - - return minDataPayloadLength; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetTransportOverhead(const bool TCP, - const bool IPV6, - const WebRtc_UWord8 authenticationOverhead) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetTransportOverhead(TCP:%d, IPV6:%d authenticationOverhead:%u)",TCP,IPV6,authenticationOverhead); - - WebRtc_UWord16 packetOverHead = 0; - if(IPV6) - { - packetOverHead = 40; - } else - { - packetOverHead = 20; - } - if(TCP) - { - // TCP - packetOverHead += 20; - } else - { - // UDP - packetOverHead += 8; - } - packetOverHead += authenticationOverhead; - - if(packetOverHead == _packetOverHead) - { - // ok same as before - return 0; - } - // calc diff - WebRtc_Word16 packetOverHeadDiff = packetOverHead - _packetOverHead; - - // store new - _packetOverHead = packetOverHead; - - _rtpReceiver.SetPacketOverHead(_packetOverHead); - WebRtc_UWord16 length = _rtpSender.MaxPayloadLength() - packetOverHeadDiff; - return _rtpSender.SetMaxPayloadLength(length, _packetOverHead); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetMaxTransferUnit(const WebRtc_UWord16 MTU) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetMaxTransferUnit(%u)",MTU); - - if(MTU > IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Invalid in argument to SetMaxTransferUnit(%u)",MTU); - return -1; - } - return _rtpSender.SetMaxPayloadLength(MTU - _packetOverHead, _packetOverHead); -} - - /* - * RTCP - */ -RTCPMethod -ModuleRtpRtcpImpl::RTCP() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RTCP()"); - - if(_rtcpSender.Status() != kRtcpOff) - { - return _rtcpReceiver.Status(); - } - return kRtcpOff; -} - - // configure RTCP status i.e on/off -WebRtc_Word32 -ModuleRtpRtcpImpl::SetRTCPStatus(const RTCPMethod method) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetRTCPStatus(%d)",method); - - if(_rtcpSender.SetRTCPStatus(method) == 0) - { - return _rtcpReceiver.SetRTCPStatus(method); - } - return -1; -} -// only for internal test -WebRtc_UWord32 -ModuleRtpRtcpImpl::LastSendReport(WebRtc_UWord32& lastRTCPTime) -{ - return _rtcpSender.LastSendReport(lastRTCPTime); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetCNAME(%s)", cName); - - return _rtcpSender.SetCNAME(cName); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::CNAME(WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "CNAME()"); - - return _rtcpSender.CNAME(cName); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::AddMixedCNAME(const WebRtc_UWord32 SSRC, - const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "AddMixedCNAME(SSRC:%u)", SSRC); - - return _rtcpSender.AddMixedCNAME(SSRC, cName); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoveMixedCNAME(const WebRtc_UWord32 SSRC) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoveMixedCNAME(SSRC:%u)", SSRC); - - return _rtcpSender.RemoveMixedCNAME(SSRC); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoteCNAME(const WebRtc_UWord32 remoteSSRC, - WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteCNAME(SSRC:%u)", remoteSSRC); - - return _rtcpReceiver.CNAME(remoteSSRC, cName); -} - -WebRtc_UWord16 ModuleRtpRtcpImpl::RemoteSequenceNumber() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteSequenceNumber()"); - - return _rtpReceiver.SequenceNumber(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoteNTP(WebRtc_UWord32 *receivedNTPsecs, - WebRtc_UWord32 *receivedNTPfrac, - WebRtc_UWord32 *RTCPArrivalTimeSecs, - WebRtc_UWord32 *RTCPArrivalTimeFrac) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteNTP()"); - - return _rtcpReceiver.NTP(receivedNTPsecs, - receivedNTPfrac, - RTCPArrivalTimeSecs, - RTCPArrivalTimeFrac); -} - -// Get RoundTripTime -WebRtc_Word32 -ModuleRtpRtcpImpl::RTT(const WebRtc_UWord32 remoteSSRC, - WebRtc_UWord16* RTT, - WebRtc_UWord16* avgRTT, - WebRtc_UWord16* minRTT, - WebRtc_UWord16* maxRTT) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RTT()"); - - return _rtcpReceiver.RTT(remoteSSRC, RTT, avgRTT, minRTT, maxRTT); -} - - // Reset RoundTripTime statistics -WebRtc_Word32 -ModuleRtpRtcpImpl::ResetRTT(const WebRtc_UWord32 remoteSSRC) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ResetRTT(SSRC:%u)", remoteSSRC); - - return _rtcpReceiver.ResetRTT(remoteSSRC); -} - - // Reset RTP statistics -WebRtc_Word32 -ModuleRtpRtcpImpl::ResetStatisticsRTP() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ResetStatisticsRTP()"); - - return _rtpReceiver.ResetStatistics(); -} - - // Reset RTP data counters for the receiving side -WebRtc_Word32 -ModuleRtpRtcpImpl::ResetReceiveDataCountersRTP() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ResetReceiveDataCountersRTP()"); - - return _rtpReceiver.ResetDataCounters(); -} - - // Reset RTP data counters for the sending side -WebRtc_Word32 -ModuleRtpRtcpImpl::ResetSendDataCountersRTP() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ResetSendDataCountersRTP()"); - - return _rtpSender.ResetDataCounters(); -} - - // Force a send of an RTCP packet - // normal SR and RR are triggered via the process function -WebRtc_Word32 -ModuleRtpRtcpImpl::SendRTCP(WebRtc_UWord32 rtcpPacketType) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendRTCP(0x%x)", rtcpPacketType); - - return _rtcpSender.SendRTCP(rtcpPacketType); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetRTCPApplicationSpecificData(const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord8* data, - const WebRtc_UWord16 length) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetRTCPApplicationSpecificData(subType:%d name:0x%x)", subType, name); - - return _rtcpSender.SetApplicationSpecificData(subType, name, data, length); -} - - /* - * (XR) VOIP metric - */ -WebRtc_Word32 -ModuleRtpRtcpImpl::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetRTCPVoIPMetrics()"); - - return _rtcpSender.SetRTCPVoIPMetrics(VoIPMetric); -} - - // our localy created statistics of the received RTP stream -WebRtc_Word32 -ModuleRtpRtcpImpl::StatisticsRTP(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, - WebRtc_UWord32 *max_jitter) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "StatisticsRTP()"); - - WebRtc_Word32 retVal =_rtpReceiver.Statistics(fraction_lost,cum_lost,ext_max,jitter, max_jitter,(_rtcpSender.Status() == kRtcpOff)); - if(retVal == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "StatisticsRTP() no statisitics availble"); - } - return retVal; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::DataCountersRTP(WebRtc_UWord32 *bytesSent, - WebRtc_UWord32 *packetsSent, - WebRtc_UWord32 *bytesReceived, - WebRtc_UWord32 *packetsReceived) const -{ - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "DataCountersRTP()"); - - if(bytesSent) - { - *bytesSent = _rtpSender.Bytes(); - } - if(packetsSent) - { - *packetsSent= _rtpSender.Packets(); - } - return _rtpReceiver.DataCounters(bytesReceived, packetsReceived); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::ReportBlockStatistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "ReportBlockStatistics()"); - WebRtc_Word32 missing = 0; - WebRtc_Word32 ret = _rtpReceiver.Statistics(fraction_lost,cum_lost,ext_max,jitter, NULL, &missing, true); - -#ifdef MATLAB - if (_plot1 == NULL) - { - _plot1 = eng.NewPlot(new MatlabPlot()); - _plot1->AddTimeLine(30, "b", "lost", TickTime::MillisecondTimestamp()); - } - _plot1->Append("lost", missing); - _plot1->Plot(); -#endif - - return ret; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoteRTCPStat( RTCPSenderInfo* senderInfo) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteRTCPStat()"); - - return _rtcpReceiver.SenderInfoReceived(senderInfo); -} - - // received RTCP report -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoteRTCPStat(const WebRtc_UWord32 remoteSSRC, - RTCPReportBlock* receiveBlock) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoteRTCPStat()"); - - return _rtcpReceiver.StatisticsReceived(remoteSSRC, receiveBlock); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::AddRTCPReportBlock(const WebRtc_UWord32 SSRC, - const RTCPReportBlock* reportBlock) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "AddRTCPReportBlock()"); - - return _rtcpSender.AddReportBlock(SSRC, reportBlock); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RemoveRTCPReportBlock(const WebRtc_UWord32 SSRC) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RemoveRTCPReportBlock()"); - - return _rtcpSender.RemoveReportBlock(SSRC); -} - - /* - * (TMMBR) Temporary Max Media Bit Rate - */ -bool -ModuleRtpRtcpImpl::TMMBR() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "TMMBR()"); - - return _rtcpSender.TMMBR(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetTMMBRStatus(const bool enable) -{ - if(enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetTMMBRStatus(enable)"); - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetTMMBRStatus(disable)"); - } - - return _rtcpSender.SetTMMBRStatus(enable); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::TMMBRReceived(const WebRtc_UWord32 size, - const WebRtc_UWord32 accNumCandidates, - TMMBRSet* candidateSet) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "TMMBRReceived()"); - - return _rtcpReceiver.TMMBRReceived(size, accNumCandidates, candidateSet); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetTMMBN(const TMMBRSet* boundingSet, - const WebRtc_UWord32 maxBitrateKbit) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetTMMBN()"); - - return _rtcpSender.SetTMMBN(boundingSet, maxBitrateKbit); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RequestTMMBR(const WebRtc_UWord32 estimatedBW, - const WebRtc_UWord32 packetOH) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RequestTMMBR()"); - - return _rtcpSender.RequestTMMBR(estimatedBW, packetOH); -} - - /* - * (NACK) Negative acknowledgement - */ - - // Is Negative acknowledgement requests on/off? -NACKMethod -ModuleRtpRtcpImpl::NACK() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "NACK()"); - - NACKMethod childMethod = kNackOff; - const bool defaultInstance(_childModules.Empty() ? false : true); - if (defaultInstance) - { - // for default we need to check all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - ListItem* item = _childModules.First(); - while(item) - { - RtpRtcp* module = static_cast(item->GetItem()); - if (module) - { - NACKMethod nackMethod = module->NACK(); - if (nackMethod != kNackOff) - { - childMethod = nackMethod; - break; - } - } - item = _childModules.Next(item); - } - } - - NACKMethod method = _nackMethod; - if (childMethod != kNackOff) - { - method = childMethod; - } - return method; -} - - // Turn negative acknowledgement requests on/off -WebRtc_Word32 -ModuleRtpRtcpImpl::SetNACKStatus(NACKMethod method) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetNACKStatus(%u)",method); - - _nackMethod = method; - _rtpReceiver.SetNACKStatus(method); - return 0; -} - - // Send a Negative acknowledgement packet -WebRtc_Word32 -ModuleRtpRtcpImpl::SendNACK(const WebRtc_UWord16* nackList, - const WebRtc_UWord16 size) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendNACK(size:%u)", size); - - if(size > NACK_PACKETS_MAX_SIZE) - { - RequestKeyFrame(kVideoFrameKey); - return -1; - } - WebRtc_UWord16 avgRTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(),NULL, &avgRTT, NULL, NULL); - - WebRtc_UWord32 waitTime = 5 +((avgRTT*3)>>1); // 5 + RTT*1.5 - if(waitTime==5) - { - waitTime = 100; //During startup we don't have an RTT - } - const WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - const WebRtc_UWord32 timeLimit = now - waitTime; - - if(_nackLastTimeSent < timeLimit) - { - // send list - } else - { - // only send if extended list - if(_nackLastSeqNumberSent == nackList[size-1]) - { - // last seq num is the same don't send list - return 0; - }else - { - // send list - } - } - _nackLastTimeSent = now; - _nackLastSeqNumberSent = nackList[size-1]; - - switch(_nackMethod) - { - case kNackRtcp: - return _rtcpSender.SendRTCP(kRtcpNack, size, nackList); - case kNackOff: - return -1; - default: - assert(false); - }; - return -1; -} - - // Store the sent packets, needed to answer to a Negative acknowledgement requests -WebRtc_Word32 -ModuleRtpRtcpImpl::SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore) -{ - if(enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetStorePacketsStatus(enable, numberToStore:%d)", numberToStore); - }else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetStorePacketsStatus(disable)"); - } - - return _rtpSender.SetStorePacketsStatus(enable, numberToStore); -} - - /* - * Audio - */ - - // Outband TelephoneEvent detection -WebRtc_Word32 ModuleRtpRtcpImpl::SetTelephoneEventStatus(const bool enable, - const bool forwardToDecoder, - const bool detectEndOfTone) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetTelephoneEventStatus(enable:%d forwardToDecoder:%d detectEndOfTone:%d)", enable, forwardToDecoder, detectEndOfTone); - - return _rtpReceiver.SetTelephoneEventStatus(enable, forwardToDecoder, detectEndOfTone); -} - - // Is outband TelephoneEvent turned on/off? -bool ModuleRtpRtcpImpl::TelephoneEvent() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "TelephoneEvent()"); - - return _rtpReceiver.TelephoneEvent(); -} - - // Is forwarding of outband telephone events turned on/off? -bool ModuleRtpRtcpImpl::TelephoneEventForwardToDecoder() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "TelephoneEventForwardToDecoder()"); - - return _rtpReceiver.TelephoneEventForwardToDecoder(); -} - - // Send a TelephoneEvent tone using RFC 2833 (4733) -WebRtc_Word32 -ModuleRtpRtcpImpl::SendTelephoneEventOutband(const WebRtc_UWord8 key, - const WebRtc_UWord16 timeMs, - const WebRtc_UWord8 level) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendTelephoneEventOutband(key:%u, timeMs:%u, level:%u)", key, timeMs, level); - - return _rtpSender.SendTelephoneEvent(key, timeMs, level); -} - -bool -ModuleRtpRtcpImpl::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendTelephoneEventActive()"); - - return _rtpSender.SendTelephoneEventActive(telephoneEvent); -} - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) -WebRtc_Word32 -ModuleRtpRtcpImpl::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetAudioPacketSize(%u)", packetSizeSamples); - - return _rtpSender.SetAudioPacketSize(packetSizeSamples); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetRTPAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, - "SetRTPAudioLevelIndicationStatus(enable=%d, ID=%u)", enable, ID); - return _rtpSender.SetAudioLevelIndicationStatus(enable, ID); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::GetRTPAudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "GetRTPAudioLevelIndicationStatus()"); - return _rtpSender.AudioLevelIndicationStatus(enable, ID); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetAudioLevel(const WebRtc_UWord8 level_dBov) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetAudioLevel(level_dBov:%u)", level_dBov); - return _rtpSender.SetAudioLevel(level_dBov); -} - - // Set payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSendREDPayloadType(const WebRtc_Word8 payloadType) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendREDPayloadType(%d)", payloadType); - - return _rtpSender.SetRED(payloadType); -} - - // Get payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -ModuleRtpRtcpImpl::SendREDPayloadType(WebRtc_Word8& payloadType) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendREDPayloadType()"); - - return _rtpSender.RED(payloadType); -} - - - /* - * Video - */ -RtpVideoCodecTypes -ModuleRtpRtcpImpl::ReceivedVideoCodec() const -{ - return _rtpReceiver.VideoCodecType(); -} - -RtpVideoCodecTypes -ModuleRtpRtcpImpl::SendVideoCodec() const -{ - return _rtpSender.VideoCodecType(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetSendBitrate(const WebRtc_UWord32 startBitrate, - const WebRtc_UWord16 minBitrateKbit, - const WebRtc_UWord16 maxBitrateKbit) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetSendBitrate start:%ubit/s min:%uKbit/s max:%uKbit/s", startBitrate, minBitrateKbit, maxBitrateKbit); - - const bool defaultInstance(_childModules.Empty()?false:true); - - if(defaultInstance) - { - // for default we need to update all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if(module) - { - module->SetSendBitrate(startBitrate, minBitrateKbit, maxBitrateKbit); - } - item = _childModules.Next(item); - } - } - _rtpSender.SetTargetSendBitrate(startBitrate); - - return _bandwidthManagement.SetSendBitrate(startBitrate, minBitrateKbit, maxBitrateKbit); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetKeyFrameRequestMethod(const KeyFrameRequestMethod method) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetKeyFrameRequestMethod(method:%u)",method); - - _keyFrameReqMethod = method; - return 0; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::RequestKeyFrame(const FrameType frameType) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "RequestKeyFrame(frameType:%d)",frameType); - - switch(_keyFrameReqMethod) - { - case kKeyFrameReqFirRtp: - return _rtpSender.SendRTPIntraRequest(); - - case kKeyFrameReqPliRtcp: - return _rtcpSender.SendRTCP(kRtcpPli); - - case kKeyFrameReqFirRtcp: - { - // conference scenario - WebRtc_UWord16 RTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL,NULL,NULL); - return _rtcpSender.SendRTCP(kRtcpFir, 0,NULL, RTT); - } - default: - assert(false); - return -1; - } -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SendRTCPSliceLossIndication(const WebRtc_UWord8 pictureID) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SendRTCPSliceLossIndication (pictureID:%d)", pictureID); - return _rtcpSender.SendRTCP(kRtcpSli, 0,0,0, pictureID); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetCameraDelay(const WebRtc_Word32 delayMS) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetCameraDelay(%d)",delayMS); - const bool defaultInstance(_childModules.Empty()?false:true); - - if(defaultInstance) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if(module) - { - module->SetCameraDelay(delayMS); - } - item = _childModules.Next(item); - } - return 0; - } else - { - return _rtcpSender.SetCameraDelay(delayMS); - } -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC) -{ - if(enable) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetGenericFECStatus(enable, %u)", payloadTypeRED); - } else - { - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetGenericFECStatus(disable)"); - } - return _rtpSender.SetGenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::GenericFECStatus(bool& enable, WebRtc_UWord8& payloadTypeRED, WebRtc_UWord8& payloadTypeFEC) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "GenericFECStatus()"); - - bool childEnabled = false; - const bool defaultInstance(_childModules.Empty() ? false : true); - if (defaultInstance) - { - // for default we need to check all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - ListItem* item = _childModules.First(); - while(item) - { - RtpRtcp* module = static_cast(item->GetItem()); - if (module) - { - bool enabled = false; - WebRtc_UWord8 dummyPTypeRED = 0; - WebRtc_UWord8 dummyPTypeFEC = 0; - if (module->GenericFECStatus(enabled, dummyPTypeRED, dummyPTypeFEC) == 0 && enabled) - { - childEnabled = true; - break; - } - } - item = _childModules.Next(item); - } - } - - WebRtc_Word32 retVal = _rtpSender.GenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); - if (childEnabled) - { - // returns true if enabled for any child module - enable = childEnabled; - } - return retVal; -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetFECCodeRate(%u, %u)", keyFrameCodeRate, deltaFrameCodeRate); - - const bool defaultInstance(_childModules.Empty()?false:true); - if (defaultInstance) - { - // for default we need to update all child modules too - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - ListItem* item = _childModules.First(); - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if (module) - { - module->SetFECCodeRate(keyFrameCodeRate, deltaFrameCodeRate); - } - item = _childModules.Next(item); - } - return 0; - - } else - { - return _rtpSender.SetFECCodeRate(keyFrameCodeRate, deltaFrameCodeRate); - } -} - - /* - * Implementation of ModuleRtpRtcpPrivate - */ -void -ModuleRtpRtcpImpl::SetRemoteSSRC(const WebRtc_UWord32 SSRC) -{ - // inform about the incoming SSRC - _rtcpSender.SetRemoteSSRC(SSRC); - _rtcpReceiver.SetRemoteSSRC(SSRC); - - // check for a SSRC collision - if(_rtpSender.SSRC() == SSRC && !_collisionDetected ) // loopback - { - // if we detect a collision change the SSRC but only once - _collisionDetected = true; - WebRtc_UWord32 newSSRC =_rtpSender.GenerateNewSSRC(); - if(newSSRC == 0) - { - // configured via API ignore - return; - } - if(kRtcpOff != _rtcpSender.Status()) - { - // send RTCP bye on the current SSRC - _rtcpSender.SendRTCP(kRtcpBye); - } - // change local SSRC - - // inform all objects about the new SSRC - _rtcpSender.SetSSRC(newSSRC); - _rtcpReceiver.SetSSRC(newSSRC); - } -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::BitrateReceivedNow() const -{ - return _rtpReceiver.BitrateNow(); -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::BitrateSent() const -{ - const bool defaultInstance(_childModules.Empty()?false:true); - - if(defaultInstance) - { - // for default we need to update the send bitrate - CriticalSectionScoped lock(_criticalSectionModulePtrsFeedback); - - ListItem* item = _childModules.First(); - WebRtc_UWord32 bitrate = 0; - while (item) - { - RtpRtcp* module = (RtpRtcp*)item->GetItem(); - if(module) - { - bitrate = (module->BitrateSent() > bitrate) ?module->BitrateSent():bitrate; - } - item = _childModules.Next(item); - } - return bitrate; - } else - { - return _rtpSender.BitrateLast(); - } -} - -// for lip sync -void -ModuleRtpRtcpImpl::OnReceivedNTP() -{ - // don't do anything if we are the audio module - // video module is responsible for sync - - if(!_audio) - { - WebRtc_Word32 diff = 0; - WebRtc_UWord32 receivedNTPsecs = 0; - WebRtc_UWord32 receivedNTPfrac= 0; - WebRtc_UWord32 RTCPArrivalTimeSecs= 0; - WebRtc_UWord32 RTCPArrivalTimeFrac= 0; - - if(0 == _rtcpReceiver.NTP(&receivedNTPsecs, &receivedNTPfrac, &RTCPArrivalTimeSecs, &RTCPArrivalTimeFrac)) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - if(_audioModule) - { - if(0 != _audioModule->RemoteNTP(&_receivedNTPsecsAudio, - &_receivedNTPfracAudio, - &_RTCPArrivalTimeSecsAudio, - &_RTCPArrivalTimeFracAudio)) - { - // failed ot get audio NTP - return; - } - } - if(_receivedNTPfracAudio != 0) - { - // ReceivedNTPxxx is NTP at sender side when sent. - // RTCPArrivalTimexxx is NTP at receiver side when received. - // can't use ConvertNTPTimeToMS since calculation can be negative - - WebRtc_Word32 NTPdiff = (WebRtc_Word32)((_receivedNTPsecsAudio - receivedNTPsecs)*1000); // ms - NTPdiff += (WebRtc_Word32)(_receivedNTPfracAudio/FracMS - receivedNTPfrac/FracMS); // ms - - WebRtc_Word32 RTCPdiff = (WebRtc_Word32)((_RTCPArrivalTimeSecsAudio - RTCPArrivalTimeSecs)*1000); // ms - RTCPdiff += (WebRtc_Word32)((_RTCPArrivalTimeFracAudio/FracMS - RTCPArrivalTimeFrac/FracMS)); // ms - - diff = NTPdiff - RTCPdiff; - // if diff is + video is behind - if(diff < -1000 || diff > 1000) - { - // unresonable ignore value. - diff = 0; - return; - } - } - } - // export via callback - // after release of critsect - _rtcpReceiver.UpdateLipSync(diff); - } -} - -// our local BW estimate is updated -void -ModuleRtpRtcpImpl::OnBandwidthEstimateUpdate(WebRtc_UWord16 bandWidthKbit) -{ - WebRtc_UWord32 maxBitrateKbit = _rtpReceiver.MaxConfiguredBitrate()/1000; - if(maxBitrateKbit) - { - // the app has set a max bitrate - if(maxBitrateKbit < bandWidthKbit) - { - // cap TMMBR at max configured bitrate - bandWidthKbit = (WebRtc_UWord16)maxBitrateKbit; - } - } - if(_rtcpSender.TMMBR()) - { - /* Maximum total media bit rate: - The upper limit on total media bit rate for a given media - stream at a particular receiver and for its selected protocol - layer. Note that this value cannot be measured on the - received media stream. Instead, it needs to be calculated or - determined through other means, such as quality of service - (QoS) negotiations or local resource limitations. Also note - that this value is an average (on a timescale that is - reasonable for the application) and that it may be different - from the instantaneous bit rate seen by packets in the media - stream. - */ - /* Overhead: - All protocol header information required to convey a packet - with media data from sender to receiver, from the application - layer down to a pre-defined protocol level (for example, down - to, and including, the IP header). Overhead may include, for - example, IP, UDP, and RTP headers, any layer 2 headers, any - Contributing Sources (CSRCs), RTP padding, and RTP header - extensions. Overhead excludes any RTP payload headers and the - payload itself. - */ - WebRtc_UWord16 RTPpacketOH = _rtpReceiver.PacketOHReceived(); - - // call RequestTMMBR when our localy created estimate changes - _rtcpSender.RequestTMMBR(bandWidthKbit, 0/*RTPpacketOH + _packetOverHead*/); - } -} - -RateControlRegion -ModuleRtpRtcpImpl::OnOverUseStateUpdate(const RateControlInput& rateControlInput) -{ - bool firstOverUse = false; - const RateControlRegion region = _rtcpSender.UpdateOverUseState(rateControlInput, firstOverUse); - if (firstOverUse && _rtcpSender.Status() == kRtcpNonCompound) - { - // Send TMMBR immediately - WebRtc_UWord16 RTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL,NULL,NULL); - _rtcpSender.SendRTCP(kRtcpTmmbr, 0, 0, RTT); - } - return region; -} - -// bad state of RTP receiver request a keyframe -void -ModuleRtpRtcpImpl::OnRequestIntraFrame(const FrameType frameType) -{ - RequestKeyFrame(frameType); -} - -void -ModuleRtpRtcpImpl::OnReceivedIntraFrameRequest(const WebRtc_UWord8 message) -{ - if(_defaultModule) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - // if we use a default module pass this info to the default module - _defaultModule->OnReceivedIntraFrameRequest(message); - return; - } - } - _rtcpReceiver.OnReceivedIntraFrameRequest(message); -} - -// received a request for a new SLI -void -ModuleRtpRtcpImpl::OnReceivedSliceLossIndication(const WebRtc_UWord8 pictureID) -{ - if(_defaultModule) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - // if we use a default module pass this info to the default module - _defaultModule->OnReceivedSliceLossIndication(pictureID); - return; - } - } - _rtcpReceiver.OnReceivedSliceLossIndication(pictureID); -} - -// received a new refereence frame -void -ModuleRtpRtcpImpl::OnReceivedReferencePictureSelectionIndication(const WebRtc_UWord64 pictureID) -{ - if(_defaultModule) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - // if we use a default module pass this info to the default module - _defaultModule->OnReceivedReferencePictureSelectionIndication(pictureID); - return; - } - } - _rtcpReceiver.OnReceivedReferencePictureSelectionIndication(pictureID); -} - -void -ModuleRtpRtcpImpl::OnReceivedBandwidthEstimateUpdate(const WebRtc_UWord16 bwEstimateMinKbit, - const WebRtc_UWord16 bwEstimateMaxKbit) -{ - if(_defaultModule) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) - { - // if we use a default module pass this info to the default module - _defaultModule->OnReceivedBandwidthEstimateUpdate(bwEstimateMinKbit, bwEstimateMaxKbit); - return; - } - } - if(_audio) - { - _rtcpReceiver.UpdateBandwidthEstimate(bwEstimateMinKbit); - }else - { - WebRtc_UWord32 newBitrate = 0; - WebRtc_UWord8 fractionLost = 0; - WebRtc_UWord16 roundTripTime = 0; - if(_bandwidthManagement.UpdateBandwidthEstimate(bwEstimateMinKbit, bwEstimateMaxKbit, newBitrate, fractionLost,roundTripTime) == 0) - { - // video callback - _rtpReceiver.UpdateBandwidthManagement(newBitrate, newBitrate, fractionLost, roundTripTime, bwEstimateMinKbit, bwEstimateMaxKbit); - const bool defaultInstance = !_childModules.Empty(); - if((newBitrate > 0) && !defaultInstance) - { - // update bitrate - _rtpSender.SetTargetSendBitrate(newBitrate); - } - } - } -} - -// bw estimation -void -ModuleRtpRtcpImpl::OnPacketLossStatisticsUpdate(const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTime, - const WebRtc_UWord32 lastReceivedExtendedHighSeqNum, - const WebRtc_UWord32 jitter) -{ - WebRtc_UWord32 newBitrate = 0; - WebRtc_UWord8 filteredFractionLost = fractionLost; - WebRtc_UWord16 filteredRoundTripTime = roundTripTime; - WebRtc_UWord16 bwEstimateKbitMin = 0; - WebRtc_UWord16 bwEstimateKbitMax = 0; - - const bool defaultInstance(_childModules.Empty()?false:true); - { - if(_bandwidthManagement.UpdatePacketLoss(lastReceivedExtendedHighSeqNum, - defaultInstance, - fractionLost, - roundTripTime, - newBitrate, - bwEstimateKbitMin, - bwEstimateKbitMax) != 0) - { - // ignore this update - newBitrate = 0; - } - } - if(newBitrate != 0 && - !defaultInstance) - { - // We need to do update RTP sender before calling default module in - // case we'll strip any layers. - _rtpSender.SetTargetSendBitrate(newBitrate); - - if(_defaultModule) - { - // if we have a default module update it - CriticalSectionScoped lock(_criticalSectionModulePtrs); - if(_defaultModule) // we need to check again inside the critsect - { - // if we use a default module pass this info to the default module - _defaultModule->OnPacketLossStatisticsUpdate(filteredFractionLost, - filteredRoundTripTime, - lastReceivedExtendedHighSeqNum, - jitter); - } - return; - } - // video callback - _rtpReceiver.UpdateBandwidthManagement(newBitrate, newBitrate, filteredFractionLost, filteredRoundTripTime, bwEstimateKbitMin, bwEstimateKbitMax); - } - else if (defaultInstance) - { - // Check if it's time to update bitrate - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - if((now - _lastChildBitrateUpdate) > (3*RTCP_INTERVAL_VIDEO_MS/2)) - { - WebRtc_UWord32 minBitrateBps = 0xffffffff; - WebRtc_UWord32 maxBitrateBps = 0; - { - // Time to update bitrate estimate, - // get min and max for the sending channels - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - ListItem* item = _childModules.First(); - while(item) - { - // Get child RTP sender and ask for bitrate estimate - ModuleRtpRtcpPrivate* childModule = (ModuleRtpRtcpPrivate*)item->GetItem(); - if (childModule->Sending()) - { - RTPSender& childRtpSender = static_cast(item->GetItem())->_rtpSender; - WebRtc_UWord32 childEstimateBps = 1000*childRtpSender.TargetSendBitrateKbit(); - if (childEstimateBps < minBitrateBps) - { - minBitrateBps = childEstimateBps; - } - if (childEstimateBps > maxBitrateBps) - { - maxBitrateBps = childEstimateBps; - } - } - item = _childModules.Next(item); - } - } - // Limit the bitrate with TMMBR. - if(bwEstimateKbitMin && bwEstimateKbitMin 0) - { - // video callback - _rtpReceiver.UpdateBandwidthManagement(minBitrateBps, maxBitrateBps, filteredFractionLost, filteredRoundTripTime, bwEstimateKbitMin, bwEstimateKbitMax); - } - _lastChildBitrateUpdate = now; - } - - } -} - -void -ModuleRtpRtcpImpl::OnRequestSendReport() -{ - _rtcpSender.SendRTCP(kRtcpSr); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SendRTCPReferencePictureSelection(const WebRtc_UWord64 pictureID) -{ - return _rtcpSender.SendRTCP(kRtcpRpsi, 0,0,0, pictureID); -} - -WebRtc_UWord32 -ModuleRtpRtcpImpl::SendTimeOfSendReport(const WebRtc_UWord32 sendReport) -{ - return _rtcpSender.SendTimeOfSendReport(sendReport); -} - -void -ModuleRtpRtcpImpl::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, - const WebRtc_UWord16* nackSequenceNumbers) -{ - if(!_rtpSender.StorePackets() || nackSequenceNumbers == NULL || nackSequenceNumbersLength == 0) - { - return; - } - WebRtc_UWord16 avgRTT = 0; - _rtcpReceiver.RTT(_rtpReceiver.SSRC(), NULL, &avgRTT ,NULL,NULL); - _rtpSender.OnReceivedNACK(nackSequenceNumbersLength, nackSequenceNumbers, avgRTT); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::LastReceivedNTP(WebRtc_UWord32& RTCPArrivalTimeSecs, // when we received the last report - WebRtc_UWord32& RTCPArrivalTimeFrac, - WebRtc_UWord32& remoteSR) // NTP inside the last received (mid 16 bits from sec and frac) -{ - WebRtc_UWord32 NTPsecs = 0; - WebRtc_UWord32 NTPfrac = 0; - - if(-1 == _rtcpReceiver.NTP(&NTPsecs, &NTPfrac, &RTCPArrivalTimeSecs, &RTCPArrivalTimeFrac)) - { - return -1; - } - remoteSR = ((NTPsecs & 0x0000ffff) << 16) + ((NTPfrac & 0xffff0000) >> 16); - return 0; -} - -void -ModuleRtpRtcpImpl::OnReceivedTMMBR() -{ - // we received a TMMBR in a RTCP packet - // answer with a TMMBN - UpdateTMMBR(); -} - -bool -ModuleRtpRtcpImpl::UpdateRTCPReceiveInformationTimers() -{ - // if this returns true this channel has timed out - // periodically check if this is true and if so call UpdateTMMBR - return _rtcpReceiver.UpdateRTCPReceiveInformationTimers(); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::UpdateTMMBR() -{ - WebRtc_Word32 numBoundingSet = 0; - WebRtc_Word32 newBitrates = 0; - WebRtc_UWord32 minBitrateKbit = 0; - WebRtc_UWord32 maxBitrateKbit = 0; - - if(_defaultModule) - { - CriticalSectionScoped lock(_criticalSectionModulePtrs); - - // no callbacks allowed inside here - if(_defaultModule) - { - // let the default module do the update - return _defaultModule->UpdateTMMBR(); - } - } - - WebRtc_UWord32 accNumCandidates = 0; - - // Find candidate set - if(!_childModules.Empty()) - { - CriticalSectionScoped lock(_criticalSectionModulePtrsFeedback); - - // this module is the default module - // loop over all modules using the default codec - WebRtc_UWord32 size = 0; - ListItem* item = _childModules.First(); - while(item) - { - ModuleRtpRtcpPrivate* module = (ModuleRtpRtcpPrivate*)item->GetItem(); - WebRtc_Word32 tmpSize = module->TMMBRReceived(0,0, NULL); - if(tmpSize > 0) - { - size += tmpSize; - } - item = _childModules.Next(item); - } - TMMBRSet* candidateSet = VerifyAndAllocateCandidateSet(size); - if(candidateSet == NULL) - { - return -1; - } - - item = _childModules.First(); - while(item) - { - ModuleRtpRtcpPrivate* module = (ModuleRtpRtcpPrivate*)item->GetItem(); - if(size > accNumCandidates && module) - { - WebRtc_Word32 accSize = module->TMMBRReceived(size, accNumCandidates, candidateSet); - if (accSize > 0) - { - accNumCandidates = accSize; - } - } - item = _childModules.Next(item); - } - } else - { - // this module don't use the default module and is not the default module - WebRtc_Word32 size = _rtcpReceiver.TMMBRReceived(0,0,NULL); - if(size > 0) - { - TMMBRSet* candidateSet = VerifyAndAllocateCandidateSet(size); - // get candidate set from receiver - accNumCandidates = _rtcpReceiver.TMMBRReceived(size, accNumCandidates, candidateSet); - } - else - { - // candidate set empty - VerifyAndAllocateCandidateSet(0); // resets candidate set - } - } - - // Find bounding set - TMMBRSet* boundingSet = NULL; - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - if(numBoundingSet == -1) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Failed to find TMMBR bounding set."); - return -1; - } - - // Set bounding set - // Inform remote clients about the new bandwidth - if(_childModules.Empty()) - { - // inform the remote client - _rtcpSender.SetTMMBN(boundingSet, _rtpSender.MaxConfiguredBitrateVideo()/1000); // might trigger a TMMBN - } else - { - // inform child modules using the default codec - CriticalSectionScoped lock(_criticalSectionModulePtrsFeedback); - ListItem* item = _childModules.First(); - while(item) - { - ModuleRtpRtcpPrivate* module = (ModuleRtpRtcpPrivate*)item->GetItem(); - if( module) - { - module->SetTMMBN(boundingSet, _rtpSender.MaxConfiguredBitrateVideo()/1000); - } - item = _childModules.Next(item); - } - } - - if(numBoundingSet == 0) - { - // owner of max bitrate request has timed out - // empty bounding set has been sent - return 0; - } - - // Get net bitrate from bounding set depending on sent packet rate - newBitrates = CalcMinMaxBitRate(_rtpSender.PacketRate(), - (WebRtc_UWord32)numBoundingSet, - minBitrateKbit, - maxBitrateKbit); - - // no critsect when calling out to "unknown" code - if(newBitrates == 0) // we have new bitrates - { - // Set new max bitrate - // we have a new bandwidth estimate on this channel - OnReceivedBandwidthEstimateUpdate((WebRtc_UWord16)minBitrateKbit, (WebRtc_UWord16)maxBitrateKbit); - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Set TMMBR request min:%d kbps max:%d kbps, channel: %d", minBitrateKbit, maxBitrateKbit, _id); - } - return 0; -} - -// called from RTCPsender -WebRtc_Word32 -ModuleRtpRtcpImpl::BoundingSet(bool &tmmbrOwner, - TMMBRSet*& boundingSet) -{ - return _rtcpReceiver.BoundingSet(tmmbrOwner, - boundingSet); -} - -WebRtc_Word32 -ModuleRtpRtcpImpl::SetH263InverseLogic(const bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceRtpRtcp, _id, "SetH263InverseLogic(%s)", enable ? "true":"false"); - return _rtpReceiver.SetH263InverseLogic(enable); -} - -void -ModuleRtpRtcpImpl::SendKeyFrame() -{ - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "SendKeyFrame()"); - OnReceivedIntraFrameRequest(0); - return; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h deleted file mode 100644 index 3269db227..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_IMPL_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_IMPL_H_ - -#include "rtp_rtcp.h" -#include "rtp_rtcp_private.h" - -#include "rtp_sender.h" -#include "rtp_receiver.h" -#include "rtcp_receiver.h" -#include "rtcp_sender.h" -#include "bandwidth_management.h" - -#include "list_wrapper.h" - -#ifdef MATLAB -class MatlabPlot; -#endif - -namespace webrtc { - -class ModuleRtpRtcpImpl : public ModuleRtpRtcpPrivate, private TMMBRHelp -{ -public: - ModuleRtpRtcpImpl(const WebRtc_Word32 id, - const bool audio); - - virtual ~ModuleRtpRtcpImpl(); - - // get Module ID - WebRtc_Word32 Id() {return _id;} - - // Get Module version - WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - // De-muxing functionality for - virtual WebRtc_Word32 RegisterDefaultModule(RtpRtcp* module); - virtual WebRtc_Word32 DeRegisterDefaultModule(); - virtual bool DefaultModuleRegistered(); - - virtual WebRtc_UWord32 NumberChildModules(); - - // Lip-sync between voice-video - virtual WebRtc_Word32 RegisterSyncModule(RtpRtcp* module); - virtual WebRtc_Word32 DeRegisterSyncModule(); - - virtual WebRtc_Word32 RegisterVideoModule(RtpRtcp* videoModule); - virtual void DeRegisterVideoModule(); - - // returns the number of milliseconds until the module want a worker thread to call Process - virtual WebRtc_Word32 TimeUntilNextProcess(); - - // Process any pending tasks such as timeouts - virtual WebRtc_Word32 Process(); - - /** - * Receiver - */ - virtual WebRtc_Word32 InitReceiver(); - - // configure a timeout value - virtual WebRtc_Word32 SetPacketTimeout(const WebRtc_UWord32 RTPtimeoutMS, - const WebRtc_UWord32 RTCPtimeoutMS); - - // Set periodic dead or alive notification - virtual WebRtc_Word32 SetPeriodicDeadOrAliveStatus(const bool enable, - const WebRtc_UWord8 sampleTimeSeconds); - - // Get periodic dead or alive notification status - virtual WebRtc_Word32 PeriodicDeadOrAliveStatus(bool &enable, - WebRtc_UWord8 &sampleTimeSeconds); - - // set codec name and payload type - virtual WebRtc_Word32 RegisterReceivePayload( const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate); - - virtual WebRtc_Word32 DeRegisterReceivePayload(const WebRtc_Word8 payloadType); - - // get configured payload type - virtual WebRtc_Word32 ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - WebRtc_Word8* payloadType, - const WebRtc_UWord32 rate = 0) const; - - // get configured payload - virtual WebRtc_Word32 ReceivePayload(const WebRtc_Word8 payloadType, - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels, - WebRtc_UWord32* rate = NULL) const; - - virtual WebRtc_Word32 RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - WebRtc_Word8* payloadType, - WebRtc_UWord32* frequency, - WebRtc_UWord8* channels) const; - - // get the currently configured SSRC filter - virtual WebRtc_Word32 SSRCFilter(WebRtc_UWord32& allowedSSRC) const; - - // set a SSRC to be used as a filter for incoming RTP streams - virtual WebRtc_Word32 SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC); - - // Get last received remote timestamp - virtual WebRtc_UWord32 RemoteTimestamp() const; - - // Get the current estimated remote timestamp - virtual WebRtc_Word32 EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const; - - // Get incoming SSRC - virtual WebRtc_UWord32 RemoteSSRC() const; - - // Get remote CSRC - virtual WebRtc_Word32 RemoteCSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const ; - - // called by the network module when we receive a packet - virtual WebRtc_Word32 IncomingPacket( const WebRtc_UWord8* incomingPacket, - const WebRtc_UWord16 packetLength); - - virtual WebRtc_Word32 IncomingAudioNTP(const WebRtc_UWord32 audioReceivedNTPsecs, - const WebRtc_UWord32 audioReceivedNTPfrac, - const WebRtc_UWord32 audioRTCPArrivalTimeSecs, - const WebRtc_UWord32 audioRTCPArrivalTimeFrac); - - // Used by the module to deliver the incoming data to the codec module - virtual WebRtc_Word32 RegisterIncomingDataCallback(RtpData* incomingDataCallback); - - // Used by the module to deliver messages to the codec module/appliation - virtual WebRtc_Word32 RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback); - - virtual WebRtc_Word32 RegisterIncomingRTCPCallback(RtcpFeedback* incomingMessagesCallback); - - virtual WebRtc_Word32 RegisterIncomingVideoCallback(RtpVideoFeedback* incomingMessagesCallback); - - virtual WebRtc_Word32 RegisterAudioCallback(RtpAudioFeedback* messagesCallback); - - /** - * Sender - */ - virtual WebRtc_Word32 InitSender(); - - virtual WebRtc_Word32 SetRTPKeepaliveStatus(const bool enable, - const WebRtc_Word8 unknownPayloadType, - const WebRtc_UWord16 deltaTransmitTimeMS); - - virtual WebRtc_Word32 RTPKeepaliveStatus(bool* enable, - WebRtc_Word8* unknownPayloadType, - WebRtc_UWord16* deltaTransmitTimeMS) const; - - virtual bool RTPKeepalive() const; - - // set codec name and payload type - virtual WebRtc_Word32 RegisterSendPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate); - - virtual WebRtc_Word32 DeRegisterSendPayload(const WebRtc_Word8 payloadType); - - virtual WebRtc_Word8 SendPayloadType() const; - - // get start timestamp - virtual WebRtc_UWord32 StartTimestamp() const; - - // configure start timestamp, default is a random number - virtual WebRtc_Word32 SetStartTimestamp(const WebRtc_UWord32 timestamp); - - // Get SequenceNumber - virtual WebRtc_UWord16 SequenceNumber() const; - - // Set SequenceNumber, default is a random number - virtual WebRtc_Word32 SetSequenceNumber(const WebRtc_UWord16 seq); - - // Get SSRC - virtual WebRtc_UWord32 SSRC() const; - - // configure SSRC, default is a random number - virtual WebRtc_Word32 SetSSRC(const WebRtc_UWord32 ssrc); - - // Get CSRC - virtual WebRtc_Word32 CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const ; - - // Set CSRC - virtual WebRtc_Word32 SetCSRCs( const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength); - - virtual WebRtc_Word32 SetCSRCStatus(const bool include); - - virtual WebRtc_UWord32 PacketCountSent() const; - - virtual int CurrentSendFrequencyHz() const; - - virtual WebRtc_UWord32 ByteCountSent() const; - - // sends kRtcpByeCode when going from true to false - virtual WebRtc_Word32 SetSendingStatus(const bool sending); - - // get send status - virtual bool Sending() const; - - // Drops or relays media packets - virtual WebRtc_Word32 SetSendingMediaStatus(const bool sending); - - // Send media status - virtual bool SendingMedia() const; - - // Used by the module to send RTP and RTCP packet to the network module - virtual WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport); - - // Used by the codec module to deliver a video or audio frame for packetization - virtual WebRtc_Word32 - SendOutgoingData(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation = NULL, - const RTPVideoTypeHeader* rtpTypeHdr = NULL); - - /* - * RTCP - */ - - // Get RTCP status - virtual RTCPMethod RTCP() const; - - // configure RTCP status i.e on/off - virtual WebRtc_Word32 SetRTCPStatus(const RTCPMethod method); - - // Set RTCP CName - virtual WebRtc_Word32 SetCNAME(const WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - - // Get RTCP CName - virtual WebRtc_Word32 CNAME(WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - - // Get remote CName - virtual WebRtc_Word32 RemoteCNAME(const WebRtc_UWord32 remoteSSRC, - WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const; - - // Get remote NTP - virtual WebRtc_Word32 RemoteNTP(WebRtc_UWord32 *ReceivedNTPsecs, - WebRtc_UWord32 *ReceivedNTPfrac, - WebRtc_UWord32 *RTCPArrivalTimeSecs, - WebRtc_UWord32 *RTCPArrivalTimeFrac) const ; - - virtual WebRtc_Word32 AddMixedCNAME(const WebRtc_UWord32 SSRC, - const WebRtc_Word8 cName[RTCP_CNAME_SIZE]); - - virtual WebRtc_Word32 RemoveMixedCNAME(const WebRtc_UWord32 SSRC); - - // Get RoundTripTime - virtual WebRtc_Word32 RTT(const WebRtc_UWord32 remoteSSRC, - WebRtc_UWord16* RTT, - WebRtc_UWord16* avgRTT, - WebRtc_UWord16* minRTT, - WebRtc_UWord16* maxRTT) const; - - // Reset RoundTripTime statistics - virtual WebRtc_Word32 ResetRTT(const WebRtc_UWord32 remoteSSRC); - - // Force a send of an RTCP packet - // normal SR and RR are triggered via the process function - virtual WebRtc_Word32 SendRTCP(WebRtc_UWord32 rtcpPacketType = kRtcpReport); - - // statistics of our localy created statistics of the received RTP stream - virtual WebRtc_Word32 StatisticsRTP(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter, - WebRtc_UWord32 *max_jitter = NULL) const; - - // Reset RTP statistics - virtual WebRtc_Word32 ResetStatisticsRTP(); - - virtual WebRtc_Word32 ResetReceiveDataCountersRTP(); - - virtual WebRtc_Word32 ResetSendDataCountersRTP(); - - // statistics of the amount of data sent and received - virtual WebRtc_Word32 DataCountersRTP(WebRtc_UWord32 *bytesSent, - WebRtc_UWord32 *packetsSent, - WebRtc_UWord32 *bytesReceived, - WebRtc_UWord32 *packetsReceived) const; - - virtual WebRtc_Word32 ReportBlockStatistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter); - - // Get received RTCP report, sender info - virtual WebRtc_Word32 RemoteRTCPStat( RTCPSenderInfo* senderInfo); - - // Get received RTCP report, report block - virtual WebRtc_Word32 RemoteRTCPStat( const WebRtc_UWord32 remoteSSRC, - RTCPReportBlock* receiveBlock); - - // Set received RTCP report block - virtual WebRtc_Word32 AddRTCPReportBlock(const WebRtc_UWord32 SSRC, - const RTCPReportBlock* receiveBlock); - - virtual WebRtc_Word32 RemoveRTCPReportBlock(const WebRtc_UWord32 SSRC); - - /* - * (TMMBR) Temporary Max Media Bit Rate - */ - virtual bool TMMBR() const ; - - virtual WebRtc_Word32 SetTMMBRStatus(const bool enable); - - virtual WebRtc_Word32 TMMBRReceived(const WebRtc_UWord32 size, - const WebRtc_UWord32 accNumCandidates, - TMMBRSet* candidateSet) const; - - virtual WebRtc_Word32 SetTMMBN(const TMMBRSet* boundingSet, - const WebRtc_UWord32 maxBitrateKbit); - - virtual WebRtc_Word32 RequestTMMBR(const WebRtc_UWord32 estimatedBW, - const WebRtc_UWord32 packetOH); - - virtual WebRtc_UWord16 MaxPayloadLength() const; - - virtual WebRtc_UWord16 MaxDataPayloadLength() const; - - virtual WebRtc_Word32 SetMaxTransferUnit(const WebRtc_UWord16 size); - - virtual WebRtc_Word32 SetTransportOverhead(const bool TCP, - const bool IPV6, - const WebRtc_UWord8 authenticationOverhead = 0); - - /* - * (NACK) Negative acknowledgement - */ - - // Is Negative acknowledgement requests on/off? - virtual NACKMethod NACK() const ; - - // Turn negative acknowledgement requests on/off - virtual WebRtc_Word32 SetNACKStatus(const NACKMethod method); - - // Send a Negative acknowledgement packet - virtual WebRtc_Word32 SendNACK(const WebRtc_UWord16* nackList, - const WebRtc_UWord16 size); - - // Store the sent packets, needed to answer to a Negative acknowledgement requests - virtual WebRtc_Word32 SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore = 200); - - /* - * (APP) Application specific data - */ - virtual WebRtc_Word32 SetRTCPApplicationSpecificData(const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord8* data, - const WebRtc_UWord16 length); - /* - * (XR) VOIP metric - */ - virtual WebRtc_Word32 SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric); - - /* - * Audio - */ - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) - virtual WebRtc_Word32 SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples); - - // Outband DTMF detection - virtual WebRtc_Word32 SetTelephoneEventStatus(const bool enable, - const bool forwardToDecoder, - const bool detectEndOfTone = false); - - // Is outband DTMF turned on/off? - virtual bool TelephoneEvent() const; - - // Is forwarding of outband telephone events turned on/off? - virtual bool TelephoneEventForwardToDecoder() const; - - virtual bool SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const; - - // Send a TelephoneEvent tone using RFC 2833 (4733) - virtual WebRtc_Word32 SendTelephoneEventOutband(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level); - - // Set payload type for Redundant Audio Data RFC 2198 - virtual WebRtc_Word32 SetSendREDPayloadType(const WebRtc_Word8 payloadType); - - // Get payload type for Redundant Audio Data RFC 2198 - virtual WebRtc_Word32 SendREDPayloadType(WebRtc_Word8& payloadType) const; - - // Set status and ID for header-extension-for-audio-level-indication. - virtual WebRtc_Word32 SetRTPAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID); - - // Get status and ID for header-extension-for-audio-level-indication. - virtual WebRtc_Word32 GetRTPAudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const; - - // Store the audio level in dBov for header-extension-for-audio-level-indication. - virtual WebRtc_Word32 SetAudioLevel(const WebRtc_UWord8 level_dBov); - - /* - * Video - */ - virtual RtpVideoCodecTypes ReceivedVideoCodec() const; - - virtual RtpVideoCodecTypes SendVideoCodec() const; - - virtual WebRtc_Word32 SendRTCPSliceLossIndication(const WebRtc_UWord8 pictureID); - - // Set method for requestion a new key frame - virtual WebRtc_Word32 SetKeyFrameRequestMethod(const KeyFrameRequestMethod method); - - // send a request for a keyframe - virtual WebRtc_Word32 RequestKeyFrame(const FrameType frameType); - - virtual WebRtc_Word32 SetCameraDelay(const WebRtc_Word32 delayMS); - - virtual WebRtc_Word32 SetSendBitrate(const WebRtc_UWord32 startBitrate, - const WebRtc_UWord16 minBitrateKbit, - const WebRtc_UWord16 maxBitrateKbit); - - virtual WebRtc_Word32 SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC); - - virtual WebRtc_Word32 GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC); - - - virtual WebRtc_Word32 SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate); - - virtual WebRtc_Word32 SetH263InverseLogic(const bool enable); - - // only for internal testing - WebRtc_UWord32 LastSendReport(WebRtc_UWord32& lastRTCPTime); - -protected: - virtual void RegisterChildModule(RtpRtcp* module); - - virtual void DeRegisterChildModule(RtpRtcp* module); - - bool UpdateRTCPReceiveInformationTimers(); - - void ProcessDeadOrAliveTimer(); - - /* - * Implementation of ModuleRtpRtcpPrivate - */ - virtual void SetRemoteSSRC(const WebRtc_UWord32 SSRC); - - virtual void OnReceivedNTP() ; - - // bw estimation - virtual void OnPacketLossStatisticsUpdate(const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTime, - const WebRtc_UWord32 lastReceivedExtendedHighSeqNum, - const WebRtc_UWord32 jitter); - - // bw estimation - virtual void OnReceivedTMMBR(); - - virtual void OnBandwidthEstimateUpdate(WebRtc_UWord16 bandWidthKbit); - - virtual void OnReceivedBandwidthEstimateUpdate(const WebRtc_UWord16 bwEstimateMinKbit, - const WebRtc_UWord16 bwEstimateMaxKbit); - - virtual RateControlRegion OnOverUseStateUpdate(const RateControlInput& rateControlInput); - - // bad state of RTP receiver request a keyframe - virtual void OnRequestIntraFrame(const FrameType frameType); - - // good state of RTP receiver inform sender - virtual WebRtc_Word32 SendRTCPReferencePictureSelection(const WebRtc_UWord64 pictureID); - - virtual void OnReceivedIntraFrameRequest(const WebRtc_UWord8 message); - - // received a request for a new SLI - virtual void OnReceivedSliceLossIndication(const WebRtc_UWord8 pictureID); - - // received a new refereence frame - virtual void OnReceivedReferencePictureSelectionIndication(const WebRtc_UWord64 pitureID); - - virtual void OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, - const WebRtc_UWord16* nackSequenceNumbers); - - virtual void OnRequestSendReport(); - - virtual WebRtc_UWord32 SendTimeOfSendReport(const WebRtc_UWord32 sendReport); - - virtual WebRtc_Word32 LastReceivedNTP(WebRtc_UWord32& NTPsecs, // when we received the last report - WebRtc_UWord32& NTPfrac, - WebRtc_UWord32& remoteSR); // NTP inside the last received (mid 16 bits from sec and frac) - - virtual WebRtc_UWord32 BitrateSent() const; - - virtual WebRtc_UWord32 BitrateReceivedNow() const; - - // Get remote SequenceNumber - virtual WebRtc_UWord16 RemoteSequenceNumber() const; - - virtual WebRtc_Word32 UpdateTMMBR(); - - virtual WebRtc_Word32 BoundingSet(bool &tmmbrOwner, - TMMBRSet*& boundingSetRec); - -private: - void SendKeyFrame(); - - WebRtc_Word32 _id; - const bool _audio; - bool _collisionDetected; - WebRtc_UWord32 _lastProcessTime; - WebRtc_UWord16 _packetOverHead; - - CriticalSectionWrapper& _criticalSectionModulePtrs; - CriticalSectionWrapper& _criticalSectionModulePtrsFeedback; - ModuleRtpRtcpPrivate* _defaultModule; - ModuleRtpRtcpPrivate* _audioModule; - ModuleRtpRtcpPrivate* _videoModule; - ListWrapper _childModules; - - // Dead or alive - bool _deadOrAliveActive; - WebRtc_UWord32 _deadOrAliveTimeoutMS; - WebRtc_UWord32 _deadOrAliveLastTimer; - - // receive side - RTPReceiver _rtpReceiver; - RTCPReceiver _rtcpReceiver; - BandwidthManagement _bandwidthManagement; - - WebRtc_UWord32 _receivedNTPsecsAudio; - WebRtc_UWord32 _receivedNTPfracAudio; - WebRtc_UWord32 _RTCPArrivalTimeSecsAudio; - WebRtc_UWord32 _RTCPArrivalTimeFracAudio; - - // send side - RTPSender _rtpSender; - RTCPSender _rtcpSender; - NACKMethod _nackMethod; - WebRtc_UWord32 _nackLastTimeSent; - WebRtc_UWord16 _nackLastSeqNumberSent; - - KeyFrameRequestMethod _keyFrameReqMethod; - - WebRtc_UWord32 _lastChildBitrateUpdate; - -#ifdef MATLAB - MatlabPlot* _plot1; -#endif -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_IMPL_H_ diff --git a/modules/rtp_rtcp/source/rtp_rtcp_private.h b/modules/rtp_rtcp/source/rtp_rtcp_private.h deleted file mode 100644 index b09bcb834..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp_private.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_PRIVATE_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_PRIVATE_H_ - -#include "bwe_defines.h" -#include "rtp_rtcp.h" -#include "tmmbr_help.h" -#include "rtp_utility.h" - -namespace webrtc { -class ModuleRtpRtcpPrivate : public RtpRtcp -{ -public: - virtual void RegisterChildModule(RtpRtcp* module) = 0; - virtual void DeRegisterChildModule(RtpRtcp* module) = 0; - - virtual WebRtc_Word32 RegisterVideoModule(RtpRtcp* videoModule) = 0; - virtual void DeRegisterVideoModule() = 0; - - virtual void SetRemoteSSRC(const WebRtc_UWord32 SSRC) = 0; - - virtual WebRtc_Word8 SendPayloadType() const = 0; - - virtual RtpVideoCodecTypes ReceivedVideoCodec() const = 0; - - virtual RtpVideoCodecTypes SendVideoCodec() const = 0; - - // lipsync - virtual void OnReceivedNTP() = 0; - - // bw estimation - virtual void OnPacketLossStatisticsUpdate(const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTime, - const WebRtc_UWord32 lastReceivedExtendedHighSeqNum, - const WebRtc_UWord32 jitter) = 0; - - // bw estimation - virtual void OnReceivedTMMBR() = 0; - - // bw estimation - virtual void OnReceivedBandwidthEstimateUpdate( const WebRtc_UWord16 bwEstimateMinKbit, - const WebRtc_UWord16 bwEstimateMaxKbit ) = 0; - - // - virtual RateControlRegion OnOverUseStateUpdate(const RateControlInput& rateControlInput) = 0; - - // received a request for a new key frame - virtual void OnReceivedIntraFrameRequest(const WebRtc_UWord8 message) = 0; - - // received a request for a new SLI - virtual void OnReceivedSliceLossIndication(const WebRtc_UWord8 pictureID) = 0; - - // received a new refereence frame - virtual void OnReceivedReferencePictureSelectionIndication(const WebRtc_UWord64 pitureID) = 0; - - // request for a RTCP send report - virtual void OnRequestSendReport() = 0; - - // Get remote SequenceNumber - virtual WebRtc_UWord16 RemoteSequenceNumber() const = 0; - - virtual WebRtc_UWord32 PacketCountSent() const = 0; - - virtual int CurrentSendFrequencyHz() const = 0; - - virtual WebRtc_UWord32 ByteCountSent() const = 0; - - virtual WebRtc_UWord32 BitrateReceivedNow() const = 0; - - virtual WebRtc_UWord32 SendTimeOfSendReport(const WebRtc_UWord32 sendReport) = 0; - - virtual WebRtc_Word32 LastReceivedNTP(WebRtc_UWord32& NTPsecs, // when we received the last report - WebRtc_UWord32& NTPfrac, - WebRtc_UWord32& remoteSR) = 0; // NTP inside the last received (mid 16 bits from sec and frac) - - virtual WebRtc_Word32 ReportBlockStatistics(WebRtc_UWord8 *fraction_lost, - WebRtc_UWord32 *cum_lost, - WebRtc_UWord32 *ext_max, - WebRtc_UWord32 *jitter) = 0; - - // bad state of RTP receiver request a keyframe - virtual void OnRequestIntraFrame( const FrameType frameType) = 0; - - /* - * NACK - */ - virtual void OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, - const WebRtc_UWord16* nackSequenceNumbers) = 0; - - /* - * TMMBR - */ - virtual WebRtc_Word32 UpdateTMMBR() = 0; - - virtual WebRtc_Word32 SetTMMBN(const TMMBRSet* boundingSet, - const WebRtc_UWord32 maxBitrateKbit) = 0; - - virtual WebRtc_Word32 BoundingSet(bool &tmmbrOwner, - TMMBRSet*& boundingSetRec)= 0; - - virtual WebRtc_Word32 TMMBRReceived(const WebRtc_UWord32 size, - const WebRtc_UWord32 accNumCandidates, - TMMBRSet* candidateSet) const = 0; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RTCP_PRIVATE_H_ diff --git a/modules/rtp_rtcp/source/rtp_rtcp_tests.gyp b/modules/rtp_rtcp/source/rtp_rtcp_tests.gyp deleted file mode 100644 index cfa0258f5..000000000 --- a/modules/rtp_rtcp/source/rtp_rtcp_tests.gyp +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'rtp_format_vp8_unittest', - 'type': 'executable', - 'dependencies': [ - 'rtp_rtcp.gyp:rtp_rtcp', - '../../../../testing/gtest.gyp:gtest', - '../../../../testing/gtest.gyp:gtest_main', - ], - 'include_dirs': [ - '.', - ], - 'sources': [ - 'rtp_format_vp8_unittest.h', - 'rtp_format_vp8_unittest.cc', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc deleted file mode 100644 index 74767816d..000000000 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ /dev/null @@ -1,1549 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include // srand - -#include "rtp_sender.h" - -#include "critical_section_wrapper.h" -#include "trace.h" -#include "tick_util.h" - -#include "rtp_sender_audio.h" -#include "rtp_sender_video.h" - -namespace webrtc { -RTPSender::RTPSender(const WebRtc_Word32 id, const bool audio) : - _id(id), - _audioConfigured(audio), - _audio(NULL), - _video(NULL), - _sendCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _transportCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - - _transport(NULL), - - _sendingMedia(true), // Default to sending media - - _maxPayloadLength(IP_PACKET_SIZE-28), // default is IP/UDP - _targetSendBitrate(0), - _packetOverHead(28), - - _payloadType(-1), - _payloadTypeMap(), - - _keepAliveIsActive(false), - _keepAlivePayloadType(-1), - _keepAliveLastSent(0), - _keepAliveDeltaTimeSend(0), - - _storeSentPackets(false), - _storeSentPacketsNumber(0), - _prevSentPacketsCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _prevSentPacketsIndex(0), - _ptrPrevSentPackets(NULL), - _prevSentPacketsSeqNum(NULL), - _prevSentPacketsLength(NULL), - _prevSentPacketsResendTime(NULL), - - // NACK - _nackByteCountTimes(), - _nackByteCount(), - - // statistics - _packetsSent(0), - _payloadBytesSent(0), - - // RTP variables - _startTimeStampForced(false), - _startTimeStamp(0), - _ssrcDB(*SSRCDatabase::GetSSRCDatabase()), - _remoteSSRC(0), - _sequenceNumberForced(false), - _sequenceNumber(0), - _ssrcForced(false), - _ssrc(0), - _timeStamp(0), - _CSRCs(0), - _CSRC(), - _includeCSRCs(true) -{ - memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes)); - memset(_nackByteCount, 0, sizeof(_nackByteCount)); - - memset(_CSRC, 0, sizeof(_CSRC)); - - // we need to seed the random generator, otherwise we get 26500 each time, hardly a random value :) - srand( (WebRtc_UWord32)ModuleRTPUtility::GetTimeInMS() ); - - _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 - - if(audio) - { - _audio = new RTPSenderAudio(id, this); - } else - { - _video = new RTPSenderVideo(id, this); // - } - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); -} - -RTPSender::~RTPSender() -{ - if(_remoteSSRC != 0) - { - _ssrcDB.ReturnSSRC(_remoteSSRC); - } - _ssrcDB.ReturnSSRC(_ssrc); - - SSRCDatabase::ReturnSSRCDatabase(); - delete &_prevSentPacketsCritsect; - delete &_sendCritsect; - delete &_transportCritsect; - - // empty map - bool loop = true; - do - { - MapItem* item = _payloadTypeMap.First(); - if(item) - { - // delete - ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); - delete payload; - - // remove from map and delete Item - _payloadTypeMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++) - { - if(_ptrPrevSentPackets[i]) - { - delete [] _ptrPrevSentPackets[i]; - _ptrPrevSentPackets[i] = 0; - } - } - delete [] _ptrPrevSentPackets; - delete [] _prevSentPacketsSeqNum; - delete [] _prevSentPacketsLength; - delete [] _prevSentPacketsResendTime; - - delete _audio; - delete _video; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); -} - -WebRtc_Word32 -RTPSender::Init(const WebRtc_UWord32 remoteSSRC) -{ - CriticalSectionScoped cs(_sendCritsect); - - // reset to default generation - _ssrcForced = false; - _startTimeStampForced = false; - - // register a remote SSRC if we have it to avoid collisions - if(remoteSSRC != 0) - { - if(_ssrc == remoteSSRC) - { - // collision detected - _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 - } - _remoteSSRC = remoteSSRC; - _ssrcDB.RegisterSSRC(remoteSSRC); - } - _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); - _packetsSent = 0; - _payloadBytesSent = 0; - _packetOverHead = 28; - - _keepAlivePayloadType = -1; - - bool loop = true; - do - { - MapItem* item = _payloadTypeMap.First(); - if(item) - { - ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); - delete payload; - _payloadTypeMap.Erase(item); - } else - { - loop = false; - } - } while (loop); - - memset(_CSRC, 0, sizeof(_CSRC)); - - memset(_nackByteCount, 0, sizeof(_nackByteCount)); - memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes)); - - SetStorePacketsStatus(false, 0); - - Bitrate::Init(); - - if(_audioConfigured) - { - _audio->Init(); - } else - { - _video->Init(); - } - return(0); -} - -void -RTPSender::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - if(_audioConfigured) - { - _audio->ChangeUniqueId(id); - } else - { - _video->ChangeUniqueId(id); - } -} - -WebRtc_Word32 -RTPSender::SetTargetSendBitrate(const WebRtc_UWord32 bits) -{ - _targetSendBitrate = (WebRtc_UWord16)(bits/1000); - return 0; -} - -WebRtc_UWord16 -RTPSender::TargetSendBitrateKbit() const -{ - return _targetSendBitrate; -} -WebRtc_UWord16 -RTPSender::ActualSendBitrateKbit() const -{ - return (WebRtc_UWord16) (Bitrate::BitrateNow()/1000); -} - -//can be called multiple times -WebRtc_Word32 -RTPSender::RegisterPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadNumber, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) -{ - if (!payloadName) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - CriticalSectionScoped cs(_sendCritsect); - - if(payloadNumber == _keepAlivePayloadType) - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "invalid state", __FUNCTION__); - return -1; - } - - MapItem* item = _payloadTypeMap.Find(payloadNumber); - if( NULL != item) - { - // we already use this payload type - - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - assert(payload); - - // check if it's the same as we already have - WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); - WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); - if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, nameLength)) - { - if(_audioConfigured && payload->audio && - payload->typeSpecific.Audio.frequency == frequency && - (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) - { - payload->typeSpecific.Audio.rate = rate; // Ensure that we update the rate if new or old is zero - return 0; - } - if(!_audioConfigured && !payload->audio) - { - return 0; - } - } - return -1; - } - - WebRtc_Word32 retVal = -1; - ModuleRTPUtility::Payload* payload = NULL; - - if(_audioConfigured) - { - retVal = _audio->RegisterAudioPayload(payloadName, payloadNumber, frequency, channels, rate, payload); - } else - { - retVal = _video->RegisterVideoPayload(payloadName, payloadNumber, rate, payload); - } - if(payload) - { - _payloadTypeMap.Insert(payloadNumber, payload); - } - return retVal; -} - -WebRtc_Word32 -RTPSender::DeRegisterSendPayload(const WebRtc_Word8 payloadType) -{ - CriticalSectionScoped lock(_sendCritsect); - - MapItem* item = _payloadTypeMap.Find(payloadType); - if( NULL != item) - { - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - delete payload; - - _payloadTypeMap.Erase(item); - return 0; - } - return -1; -} - - -WebRtc_Word8 RTPSender::SendPayloadType() const -{ - return _payloadType; -} - - -int RTPSender::SendPayloadFrequency() const -{ - return _audio->AudioFrequency(); -} - - -// See http://www.ietf.org/internet-drafts/draft-ietf-avt-app-rtp-keepalive-04.txt -// for details about this method. Only Section 4.6 is implemented so far. -bool -RTPSender::RTPKeepalive() const -{ - return _keepAliveIsActive; -} - -WebRtc_Word32 -RTPSender::RTPKeepaliveStatus(bool* enable, - WebRtc_Word8* unknownPayloadType, - WebRtc_UWord16* deltaTransmitTimeMS) const -{ - CriticalSectionScoped cs(_sendCritsect); - - if(enable) - { - *enable = _keepAliveIsActive; - } - if(unknownPayloadType) - { - *unknownPayloadType = _keepAlivePayloadType; - } - if(deltaTransmitTimeMS) - { - *deltaTransmitTimeMS =_keepAliveDeltaTimeSend; - } - return 0; -} - -WebRtc_Word32 -RTPSender::EnableRTPKeepalive( const WebRtc_Word8 unknownPayloadType, - const WebRtc_UWord16 deltaTransmitTimeMS) -{ - CriticalSectionScoped cs(_sendCritsect); - - if( NULL != _payloadTypeMap.Find(unknownPayloadType)) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - - _keepAliveIsActive = true; - _keepAlivePayloadType = unknownPayloadType; - _keepAliveLastSent = ModuleRTPUtility::GetTimeInMS(); - _keepAliveDeltaTimeSend = deltaTransmitTimeMS; - return 0; -} - -WebRtc_Word32 -RTPSender::DisableRTPKeepalive() -{ - _keepAliveIsActive = false; - return 0; -} - -bool -RTPSender::TimeToSendRTPKeepalive() const -{ - CriticalSectionScoped cs(_sendCritsect); - - bool timeToSend(false); - - WebRtc_UWord32 dT = ModuleRTPUtility::GetTimeInMS() - _keepAliveLastSent; - if (dT > _keepAliveDeltaTimeSend) - { - timeToSend = true; - } - return timeToSend; -} - -// ---------------------------------------------------------------------------- -// From the RFC draft: -// -// 4.6. RTP Packet with Unknown Payload Type -// -// The application sends an RTP packet of 0 length with a dynamic -// payload type that has not been negotiated by the peers (e.g. not -// negotiated within the SDP offer/answer, and thus not mapped to any -// media format). -// -// The sequence number is incremented by one for each packet, as it is -// sent within the same RTP session as the actual media. The timestamp -// contains the same value a media packet would have at this time. The -// marker bit is not significant for the keepalive packets and is thus -// set to zero. -// -// Normally the peer will ignore this packet, as RTP [RFC3550] states -// that "a receiver MUST ignore packets with payload types that it does -// not understand". -// -// Cons: -// o [RFC4566] and [RFC3264] mandate not to send media with inactive -// and recvonly attributes, however this is mitigated as no real -// media is sent with this mechanism. -// -// Recommendation: -// o This method should be used for RTP keepalive. -// -// 7. Timing and Transport Considerations -// -// An application supporting this specification must transmit keepalive -// packets every Tr seconds during the whole duration of the media -// session. Tr SHOULD be configurable, and otherwise MUST default to 15 -// seconds. -// -// Keepalives packets within a particular RTP session MUST use the tuple -// (source IP address, source TCP/UDP ports, target IP address, target -// TCP/UDP Port) of the regular RTP packets. -// -// The agent SHOULD only send RTP keepalive when it does not send -// regular RTP packets. -// -// http://www.ietf.org/internet-drafts/draft-ietf-avt-app-rtp-keepalive-04.txt -// ---------------------------------------------------------------------------- - -WebRtc_Word32 -RTPSender::SendRTPKeepalivePacket() -{ - // RFC summary: - // - // - Send an RTP packet of 0 length; - // - dynamic payload type has not been negotiated (not mapped to any media); - // - sequence number is incremented by one for each packet; - // - timestamp contains the same value a media packet would have at this time; - // - marker bit is set to zero. - - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - WebRtc_UWord16 rtpHeaderLength = 12; - { - CriticalSectionScoped cs(_sendCritsect); - - WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - WebRtc_UWord32 dT = now -_keepAliveLastSent; // delta time in MS - - WebRtc_UWord32 freqKHz = 90; // video - if(_audioConfigured) - { - freqKHz = _audio->AudioFrequency()/1000; - } - WebRtc_UWord32 dSamples = dT*freqKHz; - - // set timestamp - _timeStamp += dSamples; - _keepAliveLastSent = now; - - rtpHeaderLength = RTPHeaderLength(); - - // correct seq num, time stamp and payloadtype - BuildRTPheader(dataBuffer, _keepAlivePayloadType, false, 0, false); - } - - return SendToNetwork(dataBuffer, 0, rtpHeaderLength); -} - -WebRtc_Word32 -RTPSender::SetMaxPayloadLength(const WebRtc_UWord16 maxPayloadLength, const WebRtc_UWord16 packetOverHead) -{ - // sanity check - if(maxPayloadLength < 100 || maxPayloadLength > IP_PACKET_SIZE) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); - return -1; - } - if(maxPayloadLength > _maxPayloadLength) - { - CriticalSectionScoped lock(_prevSentPacketsCritsect); - if(_storeSentPackets) - { - // we need to free the memmory allocated for storing sent packets - // will be allocated in SendToNetwork - for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++) - { - if(_ptrPrevSentPackets[i]) - { - delete [] _ptrPrevSentPackets[i]; - _ptrPrevSentPackets[i] = NULL; - } - } - } - } - - CriticalSectionScoped cs(_sendCritsect); - _maxPayloadLength = maxPayloadLength; - _packetOverHead = packetOverHead; - - WEBRTC_TRACE(kTraceInfo, kTraceRtpRtcp, _id, "SetMaxPayloadLength to %d.", maxPayloadLength); - return 0; -} - -WebRtc_UWord16 -RTPSender::MaxDataPayloadLength() const -{ - if(_audioConfigured) - { - return _maxPayloadLength - RTPHeaderLength(); - } else - { - return _maxPayloadLength - RTPHeaderLength() - _video->FECPacketOverhead(); // Include the FEC/ULP/RED overhead. - } -} - -WebRtc_UWord16 -RTPSender::MaxPayloadLength() const -{ - return _maxPayloadLength; -} - -WebRtc_UWord16 -RTPSender::PacketOverHead() const -{ - return _packetOverHead; -} - -WebRtc_Word32 -RTPSender::CheckPayloadType(const WebRtc_Word8 payloadType, - RtpVideoCodecTypes& videoType) -{ - CriticalSectionScoped cs(_sendCritsect); - - if(payloadType < 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tinvalid payloadType (%d)", payloadType); - return -1; - } - - if(_audioConfigured) - { - WebRtc_Word8 redPlType = -1; - if(_audio->RED(redPlType) == 0) - { - // we have configured RED - if(redPlType == payloadType) - { - // and it's a match - return 0; - } - } - } - - if(_payloadType != payloadType) - { - MapItem* item = _payloadTypeMap.Find(payloadType); - if( NULL == item) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tpayloadType:%d not registered", payloadType); - return -1; - } - _payloadType = payloadType; - ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); - if(payload) - { - if(payload->audio) - { - if(_audioConfigured) - { - // Extract payload frequency - int payloadFreqHz; - if(ModuleRTPUtility::StringCompare(payload->name,"g722",4)&& - (payload->name[4] == 0)) //Check that strings end there, g722.1... - { - // Special case for G.722, bug in spec - payloadFreqHz=8000; - } - else - { - payloadFreqHz=payload->typeSpecific.Audio.frequency; - } - - //we don't do anything if it's CN - if((_audio->AudioFrequency() != payloadFreqHz)&& - (!ModuleRTPUtility::StringCompare(payload->name,"cn",2))) - { - _audio->SetAudioFrequency(payloadFreqHz); - // We need to correct the timestamp again, - // since this might happen after we've set it - WebRtc_UWord32 RTPtime = - ModuleRTPUtility::CurrentRTP(payloadFreqHz); - SetStartTimestamp(RTPtime); - // will be ignored if it's already configured via API - } - } - }else - { - if(!_audioConfigured) - { - _video->SetVideoCodecType(payload->typeSpecific.Video.videoCodecType); - videoType = payload->typeSpecific.Video.videoCodecType; - _video->SetMaxConfiguredBitrateVideo(payload->typeSpecific.Video.maxRate); - } - } - } - } else - { - if(!_audioConfigured) - { - videoType = _video->VideoCodecType(); - } - } - return 0; -} - -WebRtc_Word32 -RTPSender::SendOutgoingData(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - { - // Drop this packet if we're not sending media packets - CriticalSectionScoped cs(_sendCritsect); - if (!_sendingMedia) - { - return 0; - } - } - RtpVideoCodecTypes videoType; - if(CheckPayloadType(payloadType, videoType) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument failed to find payloadType:%d", __FUNCTION__, payloadType); - return -1; - } - // update keepalive so that we don't trigger keepalive messages while sending data - _keepAliveLastSent = ModuleRTPUtility::GetTimeInMS(); - - if(_audioConfigured) - { - // assert video frameTypes - assert(frameType == kAudioFrameSpeech || - frameType == kAudioFrameCN || - frameType == kFrameEmpty); - - return _audio->SendAudio(frameType, payloadType, captureTimeStamp, payloadData, payloadSize,fragmentation); - } else - { - // assert audio frameTypes - assert(frameType == kVideoFrameKey || - frameType == kVideoFrameDelta || - frameType == kVideoFrameGolden || - frameType == kVideoFrameAltRef); - - return _video->SendVideo(videoType, - frameType, - payloadType, - captureTimeStamp, - payloadData, - payloadSize, - fragmentation, - codecInfo, - rtpTypeHdr); - } -} - -WebRtc_Word32 -RTPSender::SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore) -{ - CriticalSectionScoped lock(_prevSentPacketsCritsect); - - if(enable) - { - if(_storeSentPackets) - { - // already enabled - return -1; - } - if(numberToStore > 0) - { - _storeSentPackets = enable; - _storeSentPacketsNumber = numberToStore; - - _ptrPrevSentPackets = new WebRtc_Word8*[numberToStore], - _prevSentPacketsSeqNum = new WebRtc_UWord16[numberToStore]; - _prevSentPacketsLength = new WebRtc_UWord16[numberToStore]; - _prevSentPacketsResendTime = new WebRtc_UWord32[numberToStore]; - - memset(_ptrPrevSentPackets,0, sizeof(WebRtc_Word8*)*numberToStore); - memset(_prevSentPacketsSeqNum,0, sizeof(WebRtc_UWord16)*numberToStore); - memset(_prevSentPacketsLength,0, sizeof(WebRtc_UWord16)*numberToStore); - memset(_prevSentPacketsResendTime,0,sizeof(WebRtc_UWord32)*numberToStore); - } else - { - // storing 0 packets does not make sence - return -1; - } - } else - { - _storeSentPackets = enable; - if(_storeSentPacketsNumber > 0) - { - for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++) - { - if(_ptrPrevSentPackets[i]) - { - delete [] _ptrPrevSentPackets[i]; - _ptrPrevSentPackets[i] = 0; - } - } - delete [] _ptrPrevSentPackets; - delete [] _prevSentPacketsSeqNum; - delete [] _prevSentPacketsLength; - delete [] _prevSentPacketsResendTime; - - _ptrPrevSentPackets = NULL; - _prevSentPacketsSeqNum = NULL; - _prevSentPacketsLength = NULL; - _prevSentPacketsResendTime = NULL; - - _storeSentPacketsNumber = 0; - } - } - return 0; -} - -bool -RTPSender::StorePackets() const -{ - return _storeSentPackets; -} - -WebRtc_Word32 -RTPSender::ReSendToNetwork(WebRtc_UWord16 packetID, - WebRtc_UWord32 minResendTime) -{ -#ifdef DEBUG_RTP_SEQUENCE_NUMBER - char str[256]; - sprintf(str,"Re-Send sequenceNumber %d\n", packetID) ; - OutputDebugString(str); -#endif - - WebRtc_Word32 i = -1; - WebRtc_Word32 length = 0; - WebRtc_Word32 index =0; - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - - { - CriticalSectionScoped lock(_prevSentPacketsCritsect); - - if(_storeSentPackets) - { - WebRtc_UWord16 seqNum = 0; - if(_prevSentPacketsIndex) - { - seqNum = _prevSentPacketsSeqNum[_prevSentPacketsIndex-1]; - }else - { - seqNum = _prevSentPacketsSeqNum[_storeSentPacketsNumber-1]; - } - index = (_prevSentPacketsIndex-1) - (seqNum - packetID); - if (index >= 0 && index < _storeSentPacketsNumber) - { - seqNum = _prevSentPacketsSeqNum[index]; - } - if(seqNum != packetID) - { - //we did not found a match, search all - for (WebRtc_Word32 m = 0; m < _storeSentPacketsNumber ;m++) - { - if(_prevSentPacketsSeqNum[m] == packetID) - { - index = m; - seqNum = _prevSentPacketsSeqNum[index]; - break; - } - } - } - if(seqNum == packetID) - { - WebRtc_UWord32 timeNow= ModuleRTPUtility::GetTimeInMS(); - if(minResendTime>0 && (timeNow-_prevSentPacketsResendTime[index] _maxPayloadLength || _ptrPrevSentPackets[index] == 0) - { - return -1; - } - } else - { - return -1; - } - } - if(length ==0) - { - return -1; - } - - // copy to local buffer for callback - memcpy(dataBuffer, _ptrPrevSentPackets[index], length); - } - { - CriticalSectionScoped lock(_transportCritsect); - if(_transport) - { - i = _transport->SendPacket(_id, dataBuffer, length); - } - } - if(i > 0) - { - CriticalSectionScoped cs(_sendCritsect); - - Bitrate::Update(i); - - _packetsSent++; - - // we on purpose don't add to _payloadBytesSent since this is a re-transmit and not new payload data - } - if(_storeSentPackets && i > 0) - { - CriticalSectionScoped lock(_prevSentPacketsCritsect); - - if(_prevSentPacketsSeqNum[index] == packetID) // Make sure the packet is still in the array - { - _prevSentPacketsResendTime[index]= ModuleRTPUtility::GetTimeInMS(); // Store the time when the frame was last resent. - } - return i; //bytes sent over network - } - return -1; -} - -void -RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, - const WebRtc_UWord16* nackSequenceNumbers, - const WebRtc_UWord16 avgRTT) -{ - const WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); - WebRtc_UWord32 bytesReSent = 0; - - // Enough bandwith to send NACK? - if(ProcessNACKBitRate(now)) - { - for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i) - { - const WebRtc_Word32 bytesSent = ReSendToNetwork(nackSequenceNumbers[i], - 5+avgRTT); - if (bytesSent > 0) - { - bytesReSent += bytesSent; - - } else if(bytesSent==0) - { - continue; // The packet has previously been resent. Try resending next packet in the list. - - } else if(bytesSent<0) // Failed to send one Sequence number. Give up the rest in this nack. - { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Failed resending RTP packet %d, Discard rest of NACK RTP packets", nackSequenceNumbers[i]); - break; - } - // delay bandwidth estimate (RTT * BW) - if(TargetSendBitrateKbit() != 0 && avgRTT) - { - if(bytesReSent > (WebRtc_UWord32)(TargetSendBitrateKbit() * avgRTT)>>3 ) // kbits/s * ms= bits/8 = bytes - { - break; // ignore the rest of the packets in the list - } - } - } - if (bytesReSent > 0) - { - UpdateNACKBitRate(bytesReSent,now); // Update the nack bit rate - } - }else - { - WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "NACK bitrate reached. Skipp sending NACK response. Target %d",TargetSendBitrateKbit()); - } -} - -/** -* @return true if the nack bitrate is lower than the requested max bitrate -*/ -bool -RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now) -{ - WebRtc_UWord32 num = 0; - WebRtc_Word32 byteCount = 0; - const WebRtc_UWord32 avgIntervall=1000; - - CriticalSectionScoped cs(_sendCritsect); - - if(_targetSendBitrate == 0) - { - return true; - } - - for(num = 0; num < NACK_BYTECOUNT_SIZE; num++) - { - if((now - _nackByteCountTimes[num]) > avgIntervall) // don't use data older than 1sec - { - break; - } else - { - byteCount += _nackByteCount[num]; - } - } - WebRtc_Word32 timeIntervall=avgIntervall; - if (num == NACK_BYTECOUNT_SIZE ) // More than NACK_BYTECOUNT_SIZE nack messages has been received during the last msgIntervall - { - timeIntervall= now - _nackByteCountTimes[num-1]; - if(timeIntervall <0) - { - timeIntervall=avgIntervall; - } - } - return (byteCount*8)<(_targetSendBitrate*timeIntervall); -} - -void -RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes, - const WebRtc_UWord32 now) -{ - CriticalSectionScoped cs(_sendCritsect); - - // save bitrate statistics - if(bytes > 0) - { - if(now == 0) - { - // add padding length - _nackByteCount[0] += bytes; - } else - { - if(_nackByteCountTimes[0] == 0) - { - // first no shift - } else - { - // shift - for(int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--) - { - _nackByteCount[i+1] = _nackByteCount[i]; - _nackByteCountTimes[i+1] = _nackByteCountTimes[i]; - } - } - _nackByteCount[0] = bytes; - _nackByteCountTimes[0] = now; - } - } -} - -WebRtc_Word32 -RTPSender::SendToNetwork(const WebRtc_UWord8* buffer, - const WebRtc_UWord16 length, - const WebRtc_UWord16 rtpLength, - const bool dontStore) -{ - WebRtc_Word32 retVal = -1; - // sanity - if(length + rtpLength > _maxPayloadLength) - { - return -1; - } - - if(!dontStore) - { - // Store my packets - // Used for NACK - CriticalSectionScoped lock(_prevSentPacketsCritsect); - if(_storeSentPackets && length > 0) - { - if(_ptrPrevSentPackets[0] == NULL) - { - for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++) - { - _ptrPrevSentPackets[i] = new char[_maxPayloadLength]; - memset(_ptrPrevSentPackets[i],0, _maxPayloadLength); - } - } - - const WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3]; - - memcpy(_ptrPrevSentPackets[_prevSentPacketsIndex], buffer, length + rtpLength); - _prevSentPacketsSeqNum[_prevSentPacketsIndex] = sequenceNumber; - _prevSentPacketsLength[_prevSentPacketsIndex]= length + rtpLength; - _prevSentPacketsResendTime[_prevSentPacketsIndex]=0; // Packet has not been re-sent. - _prevSentPacketsIndex++; - if(_prevSentPacketsIndex >= _storeSentPacketsNumber) - { - _prevSentPacketsIndex = 0; - } - } - } - // Send packet - { - CriticalSectionScoped cs(_transportCritsect); - if(_transport) - { - retVal = _transport->SendPacket(_id, buffer, length + rtpLength); - } - } - // success? - if(retVal > 0) - { - CriticalSectionScoped cs(_sendCritsect); - - Bitrate::Update(retVal); - - _packetsSent++; - - if(retVal > rtpLength) - { - _payloadBytesSent += retVal-rtpLength; - } - return 0; - } - return -1; -} - -void -RTPSender::ProcessBitrate() -{ - CriticalSectionScoped cs(_sendCritsect); - - Bitrate::Process(); -} - -WebRtc_UWord16 -RTPSender::RTPHeaderLength() const -{ - WebRtc_UWord16 rtpHeaderLength = 12; - - if(_includeCSRCs) - { - rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs; - } - return rtpHeaderLength; -} - -WebRtc_UWord16 -RTPSender::IncrementSequenceNumber() -{ - CriticalSectionScoped cs(_sendCritsect); - return _sequenceNumber++; -} - -WebRtc_Word32 -RTPSender::ResetDataCounters() -{ - _packetsSent = 0; - _payloadBytesSent = 0; - - return 0; -} - -// number of sent RTP packets -// dont use critsect to avoid potental deadlock -WebRtc_UWord32 -RTPSender::Packets() const -{ - return _packetsSent; -} - -// number of sent RTP bytes -// dont use critsect to avoid potental deadlock -WebRtc_UWord32 -RTPSender::Bytes() const -{ - return _payloadBytesSent; -} - -WebRtc_Word32 -RTPSender::BuildRTPheader(WebRtc_UWord8* dataBuffer, - const WebRtc_Word8 payloadType, - const bool markerBit, - const WebRtc_UWord32 captureTimeStamp, - const bool timeStampProvided, - const bool incSequenceNumber) -{ - assert(payloadType>=0); - - CriticalSectionScoped cs(_sendCritsect); - - dataBuffer[0] = static_cast(0x80); // version 2 - dataBuffer[1] = static_cast(payloadType); - if (markerBit) - { - dataBuffer[1] |= kRtpMarkerBitMask; // MarkerBit is set - } - - if(timeStampProvided) - { - _timeStamp = _startTimeStamp + captureTimeStamp; - } else - { - // make a unique time stamp - // used for inband signaling - // we can't inc by the actual time, since then we increase the risk of back timing - _timeStamp++; - } - - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _sequenceNumber); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, _timeStamp); - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, _ssrc); - - WebRtc_Word32 rtpHeaderLength = 12; - - // Add the CSRCs if any - if (_includeCSRCs && _CSRCs > 0) - { - if(_CSRCs > kRtpCsrcSize) - { - // error - assert(false); - return -1; - } - WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength]; - for (WebRtc_UWord32 i = 0; i < _CSRCs; ++i) - { - ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _CSRC[i]); - ptr +=4; - } - dataBuffer[0] = (dataBuffer[0]&0xf0) | _CSRCs; - - // Update length of header - rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs; - } - { - _sequenceNumber++; // prepare for next packet - } - - return rtpHeaderLength; -} - -WebRtc_Word32 -RTPSender::RegisterSendTransport(Transport* transport) -{ - CriticalSectionScoped cs(_transportCritsect); - _transport = transport; - return 0; -} - -void -RTPSender::SetSendingStatus(const bool enabled) -{ - if(enabled) - { - WebRtc_UWord32 freq; - if(_audioConfigured) - { - WebRtc_UWord32 frequency = _audio->AudioFrequency(); - - // sanity - switch(frequency) - { - case 8000: - case 12000: - case 16000: - case 24000: - case 32000: - break; - default: - assert(false); - return; - } - freq = frequency; - } else - { - freq = 90000; // 90 KHz for all video - } - WebRtc_UWord32 RTPtime = ModuleRTPUtility::CurrentRTP(freq); - - SetStartTimestamp(RTPtime); // will be ignored if it's already configured via API - - } else - { - if(!_ssrcForced) - { - // generate a new SSRC - _ssrcDB.ReturnSSRC(_ssrc); - _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 - - } - if(!_sequenceNumberForced && !_ssrcForced) // don't initialize seq number if SSRC passed externally - { - // generate a new sequence number - _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); - } - } -} - -void -RTPSender::SetSendingMediaStatus(const bool enabled) -{ - CriticalSectionScoped cs(_sendCritsect); - _sendingMedia = enabled; -} - -bool -RTPSender::SendingMedia() const -{ - CriticalSectionScoped cs(_sendCritsect); - return _sendingMedia; -} - -WebRtc_UWord32 -RTPSender::Timestamp() const -{ - CriticalSectionScoped cs(_sendCritsect); - return _timeStamp; -} - - -WebRtc_Word32 -RTPSender::SetStartTimestamp( const WebRtc_UWord32 timestamp, const bool force) -{ - CriticalSectionScoped cs(_sendCritsect); - if(force) - { - _startTimeStampForced = force; - _startTimeStamp = timestamp; - } else - { - if(!_startTimeStampForced) - { - _startTimeStamp = timestamp; - } - } - return 0; -} - -WebRtc_UWord32 -RTPSender::StartTimestamp() const -{ - CriticalSectionScoped cs(_sendCritsect); - return _startTimeStamp; -} - -WebRtc_UWord32 -RTPSender::GenerateNewSSRC() -{ - // if configured via API, return 0 - CriticalSectionScoped cs(_sendCritsect); - - if(_ssrcForced) - { - return 0; - } - _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 - return _ssrc; -} - -WebRtc_Word32 -RTPSender::SetSSRC(WebRtc_UWord32 ssrc) -{ - // this is configured via the API - CriticalSectionScoped cs(_sendCritsect); - - if (_ssrc == ssrc && _ssrcForced) - { - return 0; // since it's same ssrc, don't reset anything - } - - _ssrcForced = true; - - _ssrcDB.ReturnSSRC(_ssrc); - _ssrcDB.RegisterSSRC(ssrc); - _ssrc = ssrc; - - if(!_sequenceNumberForced) - { - _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); - } - return 0; -} - -WebRtc_UWord32 -RTPSender::SSRC() const -{ - CriticalSectionScoped cs(_sendCritsect); - return _ssrc; -} - -WebRtc_Word32 -RTPSender::SetCSRCStatus(const bool include) -{ - _includeCSRCs = include; - return 0; -} - -WebRtc_Word32 -RTPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength) -{ - if(arrLength > kRtpCsrcSize) - { - assert(false); - return -1; - } - - CriticalSectionScoped cs(_sendCritsect); - - for(int i = 0; i < arrLength;i++) - { - _CSRC[i] = arrOfCSRC[i]; - } - _CSRCs = arrLength; - return 0; -} - -WebRtc_Word32 -RTPSender::CSRCs(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const -{ - CriticalSectionScoped cs(_sendCritsect); - - if(arrOfCSRC == NULL) - { - assert(false); - return -1; - } - for(int i = 0; i < _CSRCs && i < kRtpCsrcSize;i++) - { - arrOfCSRC[i] = _CSRC[i]; - } - return _CSRCs; -} - -WebRtc_Word32 -RTPSender::SetSequenceNumber(WebRtc_UWord16 seq) -{ - CriticalSectionScoped cs(_sendCritsect); - _sequenceNumberForced = true; - _sequenceNumber = seq; - return 0; -} - -WebRtc_UWord16 -RTPSender::SequenceNumber() const -{ - CriticalSectionScoped cs(_sendCritsect); - return _sequenceNumber; -} - - - /* - * Audio - */ -WebRtc_Word32 -RTPSender::RegisterAudioCallback(RtpAudioFeedback* messagesCallback) -{ - if(!_audioConfigured) - { - return -1; - } - return _audio->RegisterAudioCallback(messagesCallback); -} - - // Send a DTMF tone, RFC 2833 (4733) -WebRtc_Word32 -RTPSender::SendTelephoneEvent(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level) -{ - if(!_audioConfigured) - { - return -1; - } - return _audio->SendTelephoneEvent(key, time_ms, level); -} - -bool -RTPSender::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const -{ - if(!_audioConfigured) - { - return false; - } - return _audio->SendTelephoneEventActive(telephoneEvent); -} - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) -WebRtc_Word32 -RTPSender::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples) -{ - if(!_audioConfigured) - { - return -1; - } - return _audio->SetAudioPacketSize(packetSizeSamples); -} - -WebRtc_Word32 -RTPSender::SetAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID) -{ - if(!_audioConfigured) - { - return -1; - } - return _audio->SetAudioLevelIndicationStatus(enable, ID); -} - -WebRtc_Word32 -RTPSender::AudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const -{ - return _audio->AudioLevelIndicationStatus(enable, ID); -} - -WebRtc_Word32 -RTPSender::SetAudioLevel(const WebRtc_UWord8 level_dBov) -{ - return _audio->SetAudioLevel(level_dBov); -} - - // Set payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -RTPSender::SetRED(const WebRtc_Word8 payloadType) -{ - if(!_audioConfigured) - { - return -1; - } - return _audio->SetRED(payloadType); -} - - // Get payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -RTPSender::RED(WebRtc_Word8& payloadType) const -{ - if(!_audioConfigured) - { - return NULL; - } - return _audio->RED(payloadType); -} - - /* - * Video - */ -VideoCodecInformation* -RTPSender::CodecInformationVideo() -{ - if(_audioConfigured) - { - return NULL; - } - return _video->CodecInformationVideo(); -} - -RtpVideoCodecTypes -RTPSender::VideoCodecType() const -{ - if(_audioConfigured) - { - return kRtpNoVideo; - } - return _video->VideoCodecType(); -} - -WebRtc_UWord32 -RTPSender::MaxConfiguredBitrateVideo() const -{ - if(_audioConfigured) - { - return 0; - } - return _video->MaxConfiguredBitrateVideo(); -} - -WebRtc_Word32 -RTPSender::SendRTPIntraRequest() -{ - if(_audioConfigured) - { - return -1; - } - return _video->SendRTPIntraRequest(); -} - -// FEC -WebRtc_Word32 -RTPSender::SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC) -{ - if(_audioConfigured) - { - return -1; - } - return _video->SetGenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); -} - -WebRtc_Word32 -RTPSender::GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC) const -{ - if(_audioConfigured) - { - return -1; - } - return _video->GenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); -} - -WebRtc_Word32 -RTPSender::SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate) -{ - if(_audioConfigured) - { - return -1; - } - return _video->SetFECCodeRate(keyFrameCodeRate, deltaFrameCodeRate); -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h deleted file mode 100644 index 0e80cb57b..000000000 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_ - -#include "rtp_rtcp_config.h" // misc. defines (e.g. MAX_PACKET_LENGTH) -#include "common_types.h" // Encryption -#include "ssrc_database.h" -#include "list_wrapper.h" -#include "map_wrapper.h" -#include "Bitrate.h" -#include "video_codec_information.h" - -#include -#include - -#define MAX_INIT_RTP_SEQ_NUMBER 32767 // 2^15 -1 - -namespace webrtc { -class CriticalSectionWrapper; -class RTPSenderAudio; -class RTPSenderVideo; - -class RTPSenderInterface -{ -public: - RTPSenderInterface() {} - virtual ~RTPSenderInterface() {} - - virtual WebRtc_UWord32 SSRC() const = 0; - virtual WebRtc_UWord32 Timestamp() const = 0; - - virtual WebRtc_Word32 BuildRTPheader(WebRtc_UWord8* dataBuffer, - const WebRtc_Word8 payloadType, - const bool markerBit, - const WebRtc_UWord32 captureTimeStamp, - const bool timeStampProvided = true, - const bool incSequenceNumber = true) = 0; - - virtual WebRtc_UWord16 RTPHeaderLength() const = 0; - virtual WebRtc_UWord16 IncrementSequenceNumber() = 0; - virtual WebRtc_UWord16 SequenceNumber() const = 0; - virtual WebRtc_UWord16 MaxPayloadLength() const = 0; - virtual WebRtc_UWord16 PacketOverHead() const = 0; - virtual WebRtc_UWord16 TargetSendBitrateKbit() const = 0; - virtual WebRtc_UWord16 ActualSendBitrateKbit() const = 0; - - virtual WebRtc_Word32 SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength, - const bool dontStore = false) = 0; -}; - -class RTPSender : public Bitrate, public RTPSenderInterface -{ -public: - RTPSender(const WebRtc_Word32 id, const bool audio); - virtual ~RTPSender(); - - WebRtc_Word32 Init(const WebRtc_UWord32 remoteSSRC); - void ChangeUniqueId(const WebRtc_Word32 id); - - void ProcessBitrate(); - - WebRtc_UWord16 TargetSendBitrateKbit() const; - WebRtc_UWord16 ActualSendBitrateKbit() const; - - WebRtc_Word32 SetTargetSendBitrate(const WebRtc_UWord32 bits); - - WebRtc_UWord16 MaxDataPayloadLength() const; // with RTP and FEC headers - - // callback - WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport); - - WebRtc_Word32 RegisterPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate); - - WebRtc_Word32 DeRegisterSendPayload(const WebRtc_Word8 payloadType); - - WebRtc_Word8 SendPayloadType() const; - - int SendPayloadFrequency() const; - - void SetSendingStatus(const bool enabled); - - void SetSendingMediaStatus(const bool enabled); - bool SendingMedia() const; - - // number of sent RTP packets - WebRtc_UWord32 Packets() const; - - // number of sent RTP bytes - WebRtc_UWord32 Bytes() const; - - WebRtc_Word32 ResetDataCounters(); - - WebRtc_UWord32 StartTimestamp() const; - WebRtc_Word32 SetStartTimestamp( const WebRtc_UWord32 timestamp, const bool force = false); - - WebRtc_UWord32 GenerateNewSSRC(); - WebRtc_Word32 SetSSRC( const WebRtc_UWord32 ssrc); - - WebRtc_UWord16 SequenceNumber() const; - WebRtc_Word32 SetSequenceNumber( WebRtc_UWord16 seq); - - WebRtc_Word32 CSRCs(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const; - - WebRtc_Word32 SetCSRCStatus(const bool include); - - WebRtc_Word32 SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], - const WebRtc_UWord8 arrLength); - - WebRtc_Word32 SetMaxPayloadLength(const WebRtc_UWord16 length, - const WebRtc_UWord16 packetOverHead); - - WebRtc_Word32 - SendOutgoingData(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo = NULL, - const RTPVideoTypeHeader* rtpTypeHdr = NULL); - - /* - * NACK - */ - void OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, - const WebRtc_UWord16* nackSequenceNumbers, - const WebRtc_UWord16 avgRTT); - - WebRtc_Word32 SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore); - - bool StorePackets() const; - - WebRtc_Word32 ReSendToNetwork(WebRtc_UWord16 packetID, - WebRtc_UWord32 minResendTime=0); - - bool ProcessNACKBitRate(const WebRtc_UWord32 now); - - void UpdateNACKBitRate( const WebRtc_UWord32 bytes, - const WebRtc_UWord32 now); - - /* - * Keep alive - */ - WebRtc_Word32 EnableRTPKeepalive( const WebRtc_Word8 unknownPayloadType, - const WebRtc_UWord16 deltaTransmitTimeMS); - - WebRtc_Word32 RTPKeepaliveStatus(bool* enable, - WebRtc_Word8* unknownPayloadType, - WebRtc_UWord16* deltaTransmitTimeMS) const; - - WebRtc_Word32 DisableRTPKeepalive(); - - bool RTPKeepalive() const; - - bool TimeToSendRTPKeepalive() const; - - WebRtc_Word32 SendRTPKeepalivePacket(); - - /* - * Functions wrapping RTPSenderInterface - */ - virtual WebRtc_Word32 BuildRTPheader(WebRtc_UWord8* dataBuffer, - const WebRtc_Word8 payloadType, - const bool markerBit, - const WebRtc_UWord32 captureTimeStamp, - const bool timeStampProvided = true, - const bool incSequenceNumber = true); - - virtual WebRtc_UWord16 RTPHeaderLength() const ; - virtual WebRtc_UWord16 IncrementSequenceNumber(); - virtual WebRtc_UWord16 MaxPayloadLength() const; - virtual WebRtc_UWord16 PacketOverHead() const; - - // current timestamp - virtual WebRtc_UWord32 Timestamp() const; - virtual WebRtc_UWord32 SSRC() const; - - virtual WebRtc_Word32 SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength, - const bool dontStore = false); - - /* - * Audio - */ - WebRtc_Word32 RegisterAudioCallback(RtpAudioFeedback* messagesCallback); - - // Send a DTMF tone using RFC 2833 (4733) - WebRtc_Word32 SendTelephoneEvent(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level); - - bool SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const; - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) - WebRtc_Word32 SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples); - - // Set status and ID for header-extension-for-audio-level-indication. - WebRtc_Word32 SetAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID); - - // Get status and ID for header-extension-for-audio-level-indication. - WebRtc_Word32 AudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const; - - // Store the audio level in dBov for header-extension-for-audio-level-indication. - WebRtc_Word32 SetAudioLevel(const WebRtc_UWord8 level_dBov); - - // Set payload type for Redundant Audio Data RFC 2198 - WebRtc_Word32 SetRED(const WebRtc_Word8 payloadType); - - // Get payload type for Redundant Audio Data RFC 2198 - WebRtc_Word32 RED(WebRtc_Word8& payloadType) const; - - /* - * Video - */ - VideoCodecInformation* CodecInformationVideo(); - - RtpVideoCodecTypes VideoCodecType() const; - - WebRtc_UWord32 MaxConfiguredBitrateVideo() const; - - WebRtc_Word32 SendRTPIntraRequest(); - - // FEC - WebRtc_Word32 SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC); - - WebRtc_Word32 GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC) const; - - WebRtc_Word32 SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate); - -protected: - WebRtc_Word32 CheckPayloadType(const WebRtc_Word8 payloadType, RtpVideoCodecTypes& videoType); - -private: - WebRtc_Word32 _id; - const bool _audioConfigured; - RTPSenderAudio* _audio; - RTPSenderVideo* _video; - - CriticalSectionWrapper& _sendCritsect; - - CriticalSectionWrapper& _transportCritsect; - Transport* _transport; - - bool _sendingMedia; - - WebRtc_UWord16 _maxPayloadLength; - WebRtc_UWord16 _targetSendBitrate; - WebRtc_UWord16 _packetOverHead; - - WebRtc_Word8 _payloadType; - MapWrapper _payloadTypeMap; - - bool _keepAliveIsActive; - WebRtc_Word8 _keepAlivePayloadType; - WebRtc_UWord32 _keepAliveLastSent; - WebRtc_UWord16 _keepAliveDeltaTimeSend; - - bool _storeSentPackets; - WebRtc_UWord16 _storeSentPacketsNumber; - CriticalSectionWrapper& _prevSentPacketsCritsect; - WebRtc_Word32 _prevSentPacketsIndex; - WebRtc_Word8** _ptrPrevSentPackets; - WebRtc_UWord16* _prevSentPacketsSeqNum; - WebRtc_UWord16* _prevSentPacketsLength; - WebRtc_UWord32* _prevSentPacketsResendTime; - - // NACK - WebRtc_UWord32 _nackByteCountTimes[NACK_BYTECOUNT_SIZE]; - WebRtc_Word32 _nackByteCount[NACK_BYTECOUNT_SIZE]; - - // statistics - WebRtc_UWord32 _packetsSent; - WebRtc_UWord32 _payloadBytesSent; - - // RTP variables - bool _startTimeStampForced; - WebRtc_UWord32 _startTimeStamp; - SSRCDatabase& _ssrcDB; - WebRtc_UWord32 _remoteSSRC; - bool _sequenceNumberForced; - WebRtc_UWord16 _sequenceNumber; - bool _ssrcForced; - WebRtc_UWord32 _ssrc; - WebRtc_UWord32 _timeStamp; - WebRtc_UWord8 _CSRCs; - WebRtc_UWord32 _CSRC[kRtpCsrcSize]; - bool _includeCSRCs; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_ diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.cc b/modules/rtp_rtcp/source/rtp_sender_audio.cc deleted file mode 100644 index 768b45a36..000000000 --- a/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_sender_audio.h" - -#include //memcpy -#include //assert - -namespace webrtc { -RTPSenderAudio::RTPSenderAudio(const WebRtc_Word32 id, RTPSenderInterface* rtpSender) : - _id(id), - _rtpSender(rtpSender), - _audioFeedbackCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _audioFeedback(NULL), - _sendAudioCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _frequency(8000), - _packetSizeSamples(160), - _dtmfEventIsOn(false), - _dtmfEventFirstPacketSent(false), - _dtmfPayloadType(-1), - _dtmfTimestamp(0), - _dtmfKey(0), - _dtmfLengthSamples(0), - _dtmfLevel(0), - _dtmfTimeLastSent(0), - _dtmfTimestampLastSent(0), - _REDPayloadType(-1), - _inbandVADactive(false), - _cngNBPayloadType(-1), - _cngWBPayloadType(-1), - _cngSWBPayloadType(-1), - _lastPayloadType(-1), - _includeAudioLevelIndication(false), // @TODO - reset at Init()? - _audioLevelIndicationID(0) -{ -}; - -RTPSenderAudio::~RTPSenderAudio() -{ - delete &_sendAudioCritsect; - delete &_audioFeedbackCritsect; -} - -WebRtc_Word32 -RTPSenderAudio::Init() -{ - CriticalSectionScoped cs(_sendAudioCritsect); - - _dtmfPayloadType = -1; - _inbandVADactive = false; - _cngNBPayloadType = -1; - _cngWBPayloadType = -1; - _cngSWBPayloadType = -1; - _lastPayloadType = -1; - _REDPayloadType = -1; - _dtmfTimeLastSent = 0; - _dtmfTimestampLastSent = 0; - ResetDTMF(); - return 0; -} - -void -RTPSenderAudio::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -WebRtc_Word32 -RTPSenderAudio::RegisterAudioCallback(RtpAudioFeedback* messagesCallback) -{ - CriticalSectionScoped cs(_audioFeedbackCritsect); - _audioFeedback = messagesCallback; - return 0; -} - -void -RTPSenderAudio::SetAudioFrequency(const WebRtc_UWord32 f) -{ - CriticalSectionScoped cs(_sendAudioCritsect); - _frequency = f; -} - -WebRtc_UWord32 -RTPSenderAudio::AudioFrequency() const -{ - CriticalSectionScoped cs(_sendAudioCritsect); - return _frequency; -} - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) -WebRtc_Word32 -RTPSenderAudio::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples) -{ - CriticalSectionScoped cs(_sendAudioCritsect); - - _packetSizeSamples = packetSizeSamples; - return 0; -} - -WebRtc_Word32 -RTPSenderAudio::RegisterAudioPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate, - ModuleRTPUtility::Payload*& payload) -{ - WebRtc_Word32 length = (WebRtc_Word32)strlen(payloadName); - if(length > RTP_PAYLOAD_NAME_SIZE) - { - return -1; - } - - CriticalSectionScoped cs(_sendAudioCritsect); - - if (ModuleRTPUtility::StringCompare(payloadName,"cn",2)) - { - // we can have multiple CNG payload types - if(frequency == 8000) - { - _cngNBPayloadType = payloadType; - - } else if(frequency == 16000) - { - _cngWBPayloadType = payloadType; - - } else if(frequency == 32000) - { - _cngSWBPayloadType = payloadType; - }else - { - return -1; - } - } - if (ModuleRTPUtility::StringCompare(payloadName,"telephone-event",15)) - { - // Don't add it to the list - // we dont want to allow send with a DTMF payloadtype - _dtmfPayloadType = payloadType; - return 0; - // The default timestamp rate is 8000 Hz, but other rates may be defined. - } - payload = new ModuleRTPUtility::Payload; - payload->typeSpecific.Audio.frequency = frequency; - payload->typeSpecific.Audio.channels = channels; - payload->typeSpecific.Audio.rate = rate; - payload->audio = true; - memcpy(payload->name, payloadName, length+1); - return 0; -} - -bool -RTPSenderAudio::MarkerBit(const FrameType frameType, - const WebRtc_Word8 payloadType) -{ - CriticalSectionScoped cs(_sendAudioCritsect); - - // for audio true for first packet in a speech burst - bool markerBit = false; - if(_lastPayloadType != payloadType) - { - if(_cngNBPayloadType != -1) - { - // we have configured NB CNG - if(_cngNBPayloadType == payloadType) - { - // only set a marker bit when we change payload type to a non CNG - return false; - } - } - if(_cngWBPayloadType != -1) - { - // we have configured WB CNG - if(_cngWBPayloadType == payloadType) - { - // only set a marker bit when we change payload type to a non CNG - return false; - } - } - if(_cngSWBPayloadType != -1) - { - // we have configured SWB CNG - if(_cngSWBPayloadType == payloadType) - { - // only set a marker bit when we change payload type to a non CNG - return false; - } - } - // payloadType differ - if(_lastPayloadType == -1) - { - if(frameType != kAudioFrameCN) - { - // first packet and NOT CNG - return true; - - }else - { - // first packet and CNG - _inbandVADactive = true; - return false; - } - } - // not first packet AND - // not CNG AND - // payloadType changed - - // set a marker bit when we change payload type - markerBit = true; - } - - // For G.723 G.729, AMR etc we can have inband VAD - if(frameType == kAudioFrameCN) - { - _inbandVADactive = true; - - } else if(_inbandVADactive) - { - _inbandVADactive = false; - markerBit = true; - } - return markerBit; -} - -bool -RTPSenderAudio::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const -{ - if(_dtmfEventIsOn) - { - telephoneEvent = _dtmfKey; - return true; - } - WebRtc_UWord32 delaySinceLastDTMF = (ModuleRTPUtility::GetTimeInMS() - _dtmfTimeLastSent); - if(delaySinceLastDTMF < 100) - { - telephoneEvent = _dtmfKey; - return true; - } - telephoneEvent = -1; - return false; -} - -WebRtc_Word32 -RTPSenderAudio::SendAudio(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 dataSize, - const RTPFragmentationHeader* fragmentation) -{ - WebRtc_UWord16 payloadSize = (WebRtc_UWord16)dataSize; - WebRtc_UWord16 maxPayloadLength = _rtpSender->MaxPayloadLength(); - bool dtmfToneStarted = false; - WebRtc_UWord16 dtmfLengthMS = 0; - WebRtc_UWord8 key = 0; - - // Check if we have pending DTMFs to send - if ( !_dtmfEventIsOn && PendingDTMF()) - { - CriticalSectionScoped cs(_sendAudioCritsect); - - WebRtc_UWord32 delaySinceLastDTMF = (ModuleRTPUtility::GetTimeInMS() - _dtmfTimeLastSent); - - if(delaySinceLastDTMF > 100) - { - // New tone to play - _dtmfTimestamp = captureTimeStamp; - if (NextDTMF(&key, &dtmfLengthMS, &_dtmfLevel) >= 0) - { - _dtmfEventFirstPacketSent = false; - _dtmfKey = key; - _dtmfLengthSamples = (_frequency/1000)*dtmfLengthMS; - dtmfToneStarted = true; - _dtmfEventIsOn = true; - } - } - } - if(dtmfToneStarted) - { - CriticalSectionScoped cs(_audioFeedbackCritsect); - if(_audioFeedback) - { - _audioFeedback->OnPlayTelephoneEvent(_id, key, dtmfLengthMS, _dtmfLevel); - } - } - - // A source MAY send events and coded audio packets for the same time - // but we don't support it - { - _sendAudioCritsect.Enter(); - - if (_dtmfEventIsOn) - { - if(frameType == kFrameEmpty) - { - // kFrameEmpty is used to drive the DTMF when in CN mode - // it can be triggered more frequently than we want to send the DTMF packets - if(_packetSizeSamples > (captureTimeStamp - _dtmfTimestampLastSent) ) - { - // not time to send yet - _sendAudioCritsect.Leave(); - return 0; - } - } - _dtmfTimestampLastSent = captureTimeStamp; - - WebRtc_UWord32 dtmfDurationSamples = (captureTimeStamp - _dtmfTimestamp); - - bool ended = false; - bool send = true; - - if(_dtmfLengthSamples > dtmfDurationSamples) - { - if (dtmfDurationSamples > 0) // Skip send packet at start, since we shouldn't use duration 0 - { - } else - { - send = false; - } - }else - { - ended = true; - _dtmfEventIsOn = false; - _dtmfTimeLastSent = ModuleRTPUtility::GetTimeInMS(); - } - // don't hold the critsect while calling SendTelephoneEventPacket - _sendAudioCritsect.Leave(); - if(send) - { - if(dtmfDurationSamples > 0xffff) - { - // RFC 4733 2.5.2.3 Long-Duration Events - SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)0xffff, false); - - // set new timestap for this segment - _dtmfTimestamp = captureTimeStamp; - dtmfDurationSamples -= 0xffff; - _dtmfLengthSamples -= 0xffff; - - return SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)dtmfDurationSamples, false); - } else - { - // set markerBit on the first packet in the burst - WebRtc_Word32 retVal = SendTelephoneEventPacket(ended, _dtmfTimestamp, (WebRtc_UWord16)dtmfDurationSamples, !_dtmfEventFirstPacketSent); - _dtmfEventFirstPacketSent = true; - return retVal; - } - } - return(0); - } - _sendAudioCritsect.Leave(); - } - if(payloadSize == 0 || payloadData == NULL) - { - if(frameType == kFrameEmpty) - { - // we don't send empty audio RTP packets - // no error since we use it to drive DTMF when we use VAD - return 0; - }else - { - return -1; - } - } - - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - bool markerBit = MarkerBit(frameType, payloadType); - - WebRtc_Word32 rtpHeaderLength = 0; - WebRtc_UWord16 timestampOffset = 0; - - if( _REDPayloadType >= 0 && - fragmentation && - fragmentation->fragmentationVectorSize > 1 && - !markerBit) - { - // have we configured RED? use its payload type - // we need to get the current timestamp to calc the diff - WebRtc_UWord32 oldTimeStamp = _rtpSender->Timestamp(); - rtpHeaderLength = _rtpSender->BuildRTPheader(dataBuffer, _REDPayloadType, markerBit, captureTimeStamp); - timestampOffset = WebRtc_UWord16(_rtpSender->Timestamp() - oldTimeStamp); - } else - { - rtpHeaderLength= _rtpSender->BuildRTPheader(dataBuffer, payloadType, markerBit, captureTimeStamp); - } - if(rtpHeaderLength == -1) - { - return -1; - } - { - CriticalSectionScoped cs(_sendAudioCritsect); - - if (_includeAudioLevelIndication) - { - dataBuffer[0] |= 0x10; // set eXtension bit - - // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ - - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0xBE | 0xDE | length=1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ID | len=0 |V| level | 0x00 | 0x00 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - // add the extension - - // add our ID (0xBEDE) - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, RTP_AUDIO_LEVEL_UNIQUE_ID); - rtpHeaderLength += 2; - - // add the length (length=1) in number of word32 - const WebRtc_UWord8 length = 1; - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, length); - rtpHeaderLength += 2; - - // add ID (defined by the user) and len(=0) byte - const WebRtc_UWord8 id = _audioLevelIndicationID; - const WebRtc_UWord8 len = 0; - dataBuffer[rtpHeaderLength++] = (id << 4) + len; - - // add voice-activity flag (V) bit and the audio level (in dBov) - const WebRtc_UWord8 V = (frameType == kAudioFrameSpeech); - WebRtc_UWord8 level = _audioLevel_dBov; - dataBuffer[rtpHeaderLength++] = (V << 7) + level; - - // add two bytes zero padding - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, 0); - rtpHeaderLength += 2; - } - - if(maxPayloadLength < rtpHeaderLength + payloadSize ) - { - // too large payload buffer - return -1; - } - - if( _REDPayloadType >= 0 && // have we configured RED? - fragmentation && - fragmentation->fragmentationVectorSize > 1 && - !markerBit) - { - if(fragmentation == NULL) - { - // this can't happen any more but save the code incase we want to use it later again - // we don't send this type of packet due to old NetEq issue - dataBuffer[rtpHeaderLength++] = (WebRtc_UWord8)payloadType; - memcpy(dataBuffer+rtpHeaderLength, payloadData, payloadSize); - }else - { - if( fragmentation->fragmentationVectorSize > 1 && - !markerBit && // markerBit == first packet - timestampOffset <= 0x3fff) // silence for too long send only new data - { - if(fragmentation->fragmentationVectorSize != 2) - { - // we only support 2 codecs when using RED - return -1; - } - // only 0x80 if we have multiple blocks - dataBuffer[rtpHeaderLength++] = 0x80 + fragmentation->fragmentationPlType[1]; - WebRtc_UWord32 blockLength = fragmentation->fragmentationLength[1]; - - // sanity blockLength - if(blockLength > 0x3ff) // block length 10 bits 1023 bytes - { - return -1; - } - WebRtc_UWord32 REDheader = (timestampOffset << 10) + blockLength; - ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer+rtpHeaderLength, REDheader); - rtpHeaderLength += 3; - - dataBuffer[rtpHeaderLength++] = fragmentation->fragmentationPlType[0]; - // copy the RED data - memcpy(dataBuffer+rtpHeaderLength, - payloadData + fragmentation->fragmentationOffset[1], - fragmentation->fragmentationLength[1]); - - // copy the normal data - memcpy( dataBuffer+rtpHeaderLength + fragmentation->fragmentationLength[1], - payloadData + fragmentation->fragmentationOffset[0], - fragmentation->fragmentationLength[0]); - - payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0] + fragmentation->fragmentationLength[1]); - - } else - { - dataBuffer[rtpHeaderLength++] = (WebRtc_UWord8)payloadType; - - memcpy( dataBuffer+rtpHeaderLength, - payloadData + fragmentation->fragmentationOffset[0], - fragmentation->fragmentationLength[0]); - - payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]); - } - } - }else - { - if( fragmentation && - fragmentation->fragmentationVectorSize > 0) - { - // use the fragment info if we have one - memcpy( dataBuffer+rtpHeaderLength, - payloadData + fragmentation->fragmentationOffset[0], - fragmentation->fragmentationLength[0]); - - payloadSize = WebRtc_UWord16(fragmentation->fragmentationLength[0]); - - }else - { - memcpy(dataBuffer+rtpHeaderLength, payloadData, payloadSize); - } - } - _lastPayloadType = payloadType; - - } // end critical section - - return _rtpSender->SendToNetwork(dataBuffer, payloadSize, (WebRtc_UWord16)rtpHeaderLength); -} - - -WebRtc_Word32 -RTPSenderAudio::SetAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID) -{ - if(ID < 1 || ID > 14) - { - return -1; - } - CriticalSectionScoped cs(_sendAudioCritsect); - - _includeAudioLevelIndication = enable; - _audioLevelIndicationID = ID; - - return 0; -} - -WebRtc_Word32 -RTPSenderAudio::AudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const -{ - CriticalSectionScoped cs(_sendAudioCritsect); - enable = _includeAudioLevelIndication; - ID = _audioLevelIndicationID; - return 0; -} - - // Audio level magnitude and voice activity flag are set for each RTP packet -WebRtc_Word32 -RTPSenderAudio::SetAudioLevel(const WebRtc_UWord8 level_dBov) -{ - if (level_dBov > 127) - { - return -1; - } - CriticalSectionScoped cs(_sendAudioCritsect); - _audioLevel_dBov = level_dBov; - return 0; -} - - // Set payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -RTPSenderAudio::SetRED(const WebRtc_Word8 payloadType) -{ - if(payloadType < -1 ) - { - return -1; - } - _REDPayloadType = payloadType; - return 0; -} - - // Get payload type for Redundant Audio Data RFC 2198 -WebRtc_Word32 -RTPSenderAudio::RED(WebRtc_Word8& payloadType) const -{ - if(_REDPayloadType == -1) - { - // not configured - return -1; - } - payloadType = _REDPayloadType; - return 0; -} - -// Send a TelephoneEvent tone using RFC 2833 (4733) -WebRtc_Word32 -RTPSenderAudio::SendTelephoneEvent(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level) -{ - // DTMF is protected by its own critsect - if(_dtmfPayloadType < 0) - { - // TelephoneEvent payloadtype not configured - return -1; - } - return AddDTMF(key, time_ms, level); -} - -WebRtc_Word32 -RTPSenderAudio::SendTelephoneEventPacket(const bool ended, - const WebRtc_UWord32 dtmfTimeStamp, - const WebRtc_UWord16 duration, - const bool markerBit) -{ - WebRtc_UWord8 dtmfbuffer[IP_PACKET_SIZE]; - WebRtc_UWord8 sendCount = 1; - WebRtc_Word32 retVal = 0; - - if(ended) - { - // resend last packet in an event 3 times - sendCount = 3; - } - do - { - _sendAudioCritsect.Enter(); - - //Send DTMF data - _rtpSender->BuildRTPheader(dtmfbuffer, _dtmfPayloadType, markerBit, dtmfTimeStamp); - - // reset CSRC and X bit - dtmfbuffer[0] &= 0xe0; - - //Create DTMF data - /* From RFC 2833: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | event |E|R| volume | duration | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - // R bit always cleared - WebRtc_UWord8 R = 0x00; - WebRtc_UWord8 volume = _dtmfLevel; - - // First packet un-ended - WebRtc_UWord8 E = 0x00; - - if(ended) - { - E = 0x80; - } - - // First byte is Event number, equals key number - dtmfbuffer[12] = _dtmfKey; - dtmfbuffer[13] = E|R|volume; - ModuleRTPUtility::AssignUWord16ToBuffer(dtmfbuffer+14, duration); - - _sendAudioCritsect.Leave(); - retVal = _rtpSender->SendToNetwork(dtmfbuffer, 4, 12); - sendCount--; - - }while (sendCount > 0 && retVal == 0); - - return retVal; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.h b/modules/rtp_rtcp/source/rtp_sender_audio.h deleted file mode 100644 index 592a17388..000000000 --- a/modules/rtp_rtcp/source/rtp_sender_audio.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_AUDIO_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_AUDIO_H_ - -#include "rtp_rtcp_config.h" // misc. defines (e.g. MAX_PACKET_LENGTH) -#include "common_types.h" // Transport -#include "map_wrapper.h" -#include "typedefs.h" - -#include "dtmf_queue.h" -#include "rtp_utility.h" - -#include "rtp_sender.h" - -namespace webrtc { -class RTPSenderAudio: public DTMFqueue -{ -public: - RTPSenderAudio(const WebRtc_Word32 id, RTPSenderInterface* rtpSender); - virtual ~RTPSenderAudio(); - - void ChangeUniqueId(const WebRtc_Word32 id); - - WebRtc_Word32 Init(); - - WebRtc_Word32 RegisterAudioPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate, - ModuleRTPUtility::Payload*& payload); - - WebRtc_Word32 SendAudio(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation); - - // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) - WebRtc_Word32 SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples); - - // Set status and ID for header-extension-for-audio-level-indication. - // Valid ID range is [1,14]. - WebRtc_Word32 SetAudioLevelIndicationStatus(const bool enable, - const WebRtc_UWord8 ID); - - // Get status and ID for header-extension-for-audio-level-indication. - WebRtc_Word32 AudioLevelIndicationStatus(bool& enable, - WebRtc_UWord8& ID) const; - - // Store the audio level in dBov for header-extension-for-audio-level-indication. - // Valid range is [0,100]. Actual value is negative. - WebRtc_Word32 SetAudioLevel(const WebRtc_UWord8 level_dBov); - - // Send a DTMF tone using RFC 2833 (4733) - WebRtc_Word32 SendTelephoneEvent(const WebRtc_UWord8 key, - const WebRtc_UWord16 time_ms, - const WebRtc_UWord8 level); - - bool SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const; - - void SetAudioFrequency(const WebRtc_UWord32 f); - - WebRtc_UWord32 AudioFrequency() const; - - // Set payload type for Redundant Audio Data RFC 2198 - WebRtc_Word32 SetRED(const WebRtc_Word8 payloadType); - - // Get payload type for Redundant Audio Data RFC 2198 - WebRtc_Word32 RED(WebRtc_Word8& payloadType) const; - - WebRtc_Word32 RegisterAudioCallback(RtpAudioFeedback* messagesCallback); - -protected: - WebRtc_Word32 SendTelephoneEventPacket(const bool ended, - const WebRtc_UWord32 dtmfTimeStamp, - const WebRtc_UWord16 duration, - const bool markerBit); // set on first packet in talk burst - - bool MarkerBit(const FrameType frameType, - const WebRtc_Word8 payloadType); - -private: - WebRtc_Word32 _id; - RTPSenderInterface* _rtpSender; - CriticalSectionWrapper& _audioFeedbackCritsect; - RtpAudioFeedback* _audioFeedback; - - CriticalSectionWrapper& _sendAudioCritsect; - - WebRtc_UWord32 _frequency; - WebRtc_UWord16 _packetSizeSamples; - - // DTMF - bool _dtmfEventIsOn; - bool _dtmfEventFirstPacketSent; - WebRtc_Word8 _dtmfPayloadType; - WebRtc_UWord32 _dtmfTimestamp; - WebRtc_UWord8 _dtmfKey; - WebRtc_UWord32 _dtmfLengthSamples; - WebRtc_UWord8 _dtmfLevel; - WebRtc_UWord32 _dtmfTimeLastSent; - WebRtc_UWord32 _dtmfTimestampLastSent; - - WebRtc_Word8 _REDPayloadType; - - // VAD detection, used for markerbit - bool _inbandVADactive; - WebRtc_Word8 _cngNBPayloadType; - WebRtc_Word8 _cngWBPayloadType; - WebRtc_Word8 _cngSWBPayloadType; - WebRtc_Word8 _lastPayloadType; - - // Audio level indication (https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/) - bool _includeAudioLevelIndication; - WebRtc_UWord8 _audioLevelIndicationID; - WebRtc_UWord8 _audioLevel_dBov; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_AUDIO_H_ diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc deleted file mode 100644 index 8aa4382b9..000000000 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ /dev/null @@ -1,1248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_sender_video.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -#include "rtp_utility.h" - -#include // memcpy -#include // assert -#include // srand - -#include "h263_information.h" -#include "rtp_format_vp8.h" - -namespace webrtc { -enum { REDForFECHeaderLength = 1 }; - -RTPSenderVideo::RTPSenderVideo(const WebRtc_Word32 id, - RTPSenderInterface* rtpSender) : - _id(id), - _rtpSender(*rtpSender), - _sendVideoCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - - _videoType(kRtpNoVideo), - _videoCodecInformation(NULL), - _maxBitrate(0), - - // Generic FEC - _fec(id), - _fecEnabled(false), - _payloadTypeRED(-1), - _payloadTypeFEC(-1), - _codeRateKey(0), - _codeRateDelta(0), - _fecProtectionFactor(0), - _numberFirstPartition(0), - - // H263 - _savedByte(0), - _eBit(0) -{ -} - -RTPSenderVideo::~RTPSenderVideo() -{ - if(_videoCodecInformation) - { - delete _videoCodecInformation; - } - delete &_sendVideoCritsect; -} - -WebRtc_Word32 -RTPSenderVideo::Init() -{ - CriticalSectionScoped cs(_sendVideoCritsect); - - _fecEnabled = false; - _payloadTypeRED = -1; - _payloadTypeFEC = -1; - _codeRateKey = 0; - _codeRateDelta = 0; - _fecProtectionFactor = 0; - _numberFirstPartition = 0; - return 0; -} - -void -RTPSenderVideo::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; -} - -void -RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType) -{ - CriticalSectionScoped cs(_sendVideoCritsect); - _videoType = videoType; -} - -RtpVideoCodecTypes -RTPSenderVideo::VideoCodecType() const -{ - return _videoType; -} - -WebRtc_Word32 -RTPSenderVideo::RegisterVideoPayload( - const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxBitRate, - ModuleRTPUtility::Payload*& payload) -{ - CriticalSectionScoped cs(_sendVideoCritsect); - - RtpVideoCodecTypes videoType = kRtpNoVideo; - if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3)) - { - videoType = kRtpVp8Video; - } - else if ((ModuleRTPUtility::StringCompare(payloadName, "H263-1998", 9)) || - (ModuleRTPUtility::StringCompare(payloadName, "H263-2000", 9))) - { - videoType = kRtpH2631998Video; - } - else if (ModuleRTPUtility::StringCompare(payloadName, "H263", 4)) - { - videoType = kRtpH263Video; - } - else if (ModuleRTPUtility::StringCompare(payloadName, "MP4V-ES", 7)) - { - videoType = kRtpMpeg4Video; - } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) - { - videoType = kRtpNoVideo; - }else - { - videoType = kRtpNoVideo; - return -1; - } - payload = new ModuleRTPUtility::Payload; - strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE); - payload->typeSpecific.Video.videoCodecType = videoType; - payload->typeSpecific.Video.maxRate = maxBitRate; - payload->audio = false; - - return 0; -} - -struct RtpPacket -{ - WebRtc_UWord16 rtpHeaderLength; - ForwardErrorCorrection::Packet* pkt; -}; - -WebRtc_Word32 -RTPSenderVideo::SendVideoPacket(const FrameType frameType, - const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength) -{ - if(_fecEnabled) - { - WebRtc_Word32 retVal = 0; - - const bool markerBit = (dataBuffer[1] & kRtpMarkerBitMask)?true:false; - RtpPacket* ptrGenericFEC = new RtpPacket; - ptrGenericFEC->pkt = new ForwardErrorCorrection::Packet; - ptrGenericFEC->pkt->length = payloadLength + rtpHeaderLength; - ptrGenericFEC->rtpHeaderLength = rtpHeaderLength; - memcpy(ptrGenericFEC->pkt->data, dataBuffer, - ptrGenericFEC->pkt->length); - - // Add packet to FEC list - _rtpPacketListFec.PushBack(ptrGenericFEC); - // FEC can only protect up to kMaxMediaPackets packets - if (_mediaPacketListFec.GetSize() < - ForwardErrorCorrection::kMaxMediaPackets) - { - _mediaPacketListFec.PushBack(ptrGenericFEC->pkt); - } - - // Last packet in frame - if (markerBit) - { - // Interface for FEC - ListWrapper fecPacketList; - - // Retain the RTP header of the last media packet to construct FEC - // packet RTP headers. - ForwardErrorCorrection::Packet lastMediaRtpHeader; - memcpy(lastMediaRtpHeader.data, - ptrGenericFEC->pkt->data, - ptrGenericFEC->rtpHeaderLength); - - lastMediaRtpHeader.length = ptrGenericFEC->rtpHeaderLength; - // Replace payload and clear marker bit. - lastMediaRtpHeader.data[1] = _payloadTypeRED; - - // Number of first partition packets cannot exceed kMaxMediaPackets - if (_numberFirstPartition > - ForwardErrorCorrection::kMaxMediaPackets) - { - _numberFirstPartition = - ForwardErrorCorrection::kMaxMediaPackets; - } - - retVal = _fec.GenerateFEC(_mediaPacketListFec, _fecProtectionFactor, - _numberFirstPartition, fecPacketList); - while(!_rtpPacketListFec.Empty()) - { - WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE]; - memset(newDataBuffer, 0, sizeof(newDataBuffer)); - - ListItem* item = _rtpPacketListFec.First(); - RtpPacket* packetToSend = - static_cast(item->GetItem()); - - // Copy RTP header - memcpy(newDataBuffer, packetToSend->pkt->data, - packetToSend->rtpHeaderLength); - - // Get codec pltype - WebRtc_UWord8 payloadType = newDataBuffer[1] & 0x7f; - - // Replace pltype - newDataBuffer[1] &= 0x80; // reset - newDataBuffer[1] += _payloadTypeRED; // replace - - // Add RED header - // f-bit always 0 - newDataBuffer[packetToSend->rtpHeaderLength] = payloadType; - - // Copy payload data - memcpy(newDataBuffer + packetToSend->rtpHeaderLength + - REDForFECHeaderLength, - packetToSend->pkt->data + packetToSend->rtpHeaderLength, - packetToSend->pkt->length - - packetToSend->rtpHeaderLength); - - _rtpPacketListFec.PopFront(); - _mediaPacketListFec.PopFront(); - - // Send normal packet with RED header - retVal |= _rtpSender.SendToNetwork( - newDataBuffer, - packetToSend->pkt->length - - packetToSend->rtpHeaderLength + - REDForFECHeaderLength, - packetToSend->rtpHeaderLength); - - delete packetToSend->pkt; - delete packetToSend; - packetToSend = NULL; - } - assert(_mediaPacketListFec.Empty()); - assert(_rtpPacketListFec.Empty()); - - while(!fecPacketList.Empty()) - { - WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE]; - - ListItem* item = fecPacketList.First(); - - // Build FEC packets - ForwardErrorCorrection::Packet* packetToSend = - static_cast - (item->GetItem()); - - // The returned FEC packets have no RTP headers. - // Copy the last media packet's modified RTP header. - memcpy(newDataBuffer, lastMediaRtpHeader.data, - lastMediaRtpHeader.length); - - // Add sequence number - ModuleRTPUtility::AssignUWord16ToBuffer( - &newDataBuffer[2], _rtpSender.IncrementSequenceNumber()); - - // Add RED header - // f-bit always 0 - newDataBuffer[lastMediaRtpHeader.length] = _payloadTypeFEC; - - // Copy payload data - memcpy(newDataBuffer + lastMediaRtpHeader.length + - REDForFECHeaderLength, - packetToSend->data, - packetToSend->length); - - fecPacketList.PopFront(); - - // Invalid FEC packet - assert(packetToSend->length != 0); - - // No marker bit on FEC packets, last media packet have the - // marker send FEC packet with RED header - retVal |= _rtpSender.SendToNetwork( - newDataBuffer, - packetToSend->length + REDForFECHeaderLength, - lastMediaRtpHeader.length); - } - } - return retVal; - } - return _rtpSender.SendToNetwork(dataBuffer, - payloadLength, - rtpHeaderLength); -} - -WebRtc_Word32 -RTPSenderVideo::SendRTPIntraRequest() -{ - // RFC 2032 - // 5.2.1. Full intra-frame Request (FIR) packet - - WebRtc_UWord16 length = 8; - WebRtc_UWord8 data[8]; - data[0] = 0x80; - data[1] = 192; - data[2] = 0; - data[3] = 1; // length - - ModuleRTPUtility::AssignUWord32ToBuffer(data+4, _rtpSender.SSRC()); - - return _rtpSender.SendToNetwork(data, 0, length); -} - -WebRtc_Word32 -RTPSenderVideo::SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC) -{ - _fecEnabled = enable; - _payloadTypeRED = payloadTypeRED; - _payloadTypeFEC = payloadTypeFEC; - _codeRateKey = 0; - _codeRateDelta = 0; - - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC) const -{ - enable = _fecEnabled; - payloadTypeRED = _payloadTypeRED; - payloadTypeFEC = _payloadTypeFEC; - return 0; -} - -WebRtc_UWord16 -RTPSenderVideo::FECPacketOverhead() const -{ - if (_fecEnabled) - { - return ForwardErrorCorrection::PacketOverhead() + - REDForFECHeaderLength; - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate) -{ - _codeRateKey = keyFrameCodeRate; - _codeRateDelta = deltaFrameCodeRate; - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType, - const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - if( payloadSize == 0) - { - return -1; - } - - if (frameType == kVideoFrameKey) - { - _fecProtectionFactor = _codeRateKey; - } else - { - _fecProtectionFactor = _codeRateDelta; - } - - // Default setting for number of first partition packets: - // Will be extracted in SendVP8 for VP8 codec; other codecs use 0 - _numberFirstPartition = 0; - - WebRtc_Word32 retVal = -1; - switch(videoType) - { - case kRtpNoVideo: - retVal = SendGeneric(payloadType,captureTimeStamp, payloadData, - payloadSize); - break; - case kRtpH263Video: - retVal = SendH263(frameType,payloadType, captureTimeStamp, payloadData, - payloadSize, codecInfo); - break; - case kRtpH2631998Video: //RFC 4629 - retVal = SendH2631998(frameType,payloadType, captureTimeStamp, - payloadData, payloadSize, codecInfo); - break; - case kRtpMpeg4Video: // RFC 3016 - retVal = SendMPEG4(frameType,payloadType, captureTimeStamp, - payloadData, payloadSize); - break; - case kRtpVp8Video: - retVal = SendVP8(frameType, payloadType, captureTimeStamp, - payloadData, payloadSize, fragmentation, rtpTypeHdr); - break; - default: - assert(false); - break; - } - if(retVal <= 0) - { - return retVal; - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendGeneric(const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize) -{ - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_UWord32 bytesSent = 0; - WebRtc_Word32 payloadBytesToSend = payloadSize; - - const WebRtc_UWord8* data = payloadData; - WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - WebRtc_UWord16 maxLength = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength; - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - - // Fragment packet into packets of max MaxPayloadLength bytes payload. - while (payloadBytesToSend > 0) - { - if (payloadBytesToSend > maxLength) - { - payloadBytesInPacket = maxLength; - payloadBytesToSend -= payloadBytesInPacket; - // MarkerBit is 0 - if(_rtpSender.BuildRTPheader(dataBuffer, - payloadType, - false, - captureTimeStamp) != rtpHeaderLength) - { - return -1; - } - } - else - { - payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend; - payloadBytesToSend = 0; - // MarkerBit is 1 - if(_rtpSender.BuildRTPheader(dataBuffer, payloadType, true, - captureTimeStamp) != rtpHeaderLength) - { - return -1; - } - } - - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength], &data[bytesSent], - payloadBytesInPacket); - bytesSent += payloadBytesInPacket; - - if(-1 == SendVideoPacket(kVideoFrameKey, - dataBuffer, - payloadBytesInPacket, - rtpHeaderLength)) - { - return -1; - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendPadData(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord32 bytes) -{ - const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - WebRtc_UWord32 maxLength = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength; - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - - if(bytesheader.payloadType, - false, 0, false, false); - - // Version 0 to be compatible with old ViE - dataBuffer[0] &= !0x80; - - // Set relay SSRC - ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, - rtpHeader->header.ssrc); - // Start at 12 - WebRtc_Word32* data = (WebRtc_Word32*)&(dataBuffer[12]); - - // Build data buffer - for(WebRtc_UWord32 j = 0; j < ((maxLength>>2)-4) && j < (bytes>>4); j++) - { - data[j] = rand(); - } - } - // Min - WebRtc_UWord16 length = (WebRtc_UWord16)(bytes 0) - { - WebRtc_UWord16 payloadBytes = 0; - WebRtc_Word32 dataOffset = rtpHeaderLength; - - do - { - WebRtc_Word32 size = 0; - bool markerBit = false; - if(payloadBytesToSend > maxLength) - { - size = FindMPEG4NALU(data, maxLength); - }else - { - // Last in frame - markerBit = true; - size = payloadBytesToSend; - } - if(size <= 0) - { - return -1; - } - if(size > maxLength) - { - // We need to fragment NALU - return -1; - } - - if(payloadBytes == 0) - { - // Build RTP header - if(_rtpSender.BuildRTPheader( - dataBuffer, - payloadType, - markerBit, - captureTimeStamp) != rtpHeaderLength) - { - return -1; - } - } - - if( size + payloadBytes <= maxLength) - { - // Put payload in packet - memcpy(&dataBuffer[dataOffset], data, size); - dataOffset += size; //advance frame ptr - data += size; //advance packet ptr - payloadBytes += (WebRtc_UWord16)size; - payloadBytesToSend -= size; - } else - { - // Send packet - break; - } - }while(payloadBytesToSend); - - if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytes, - rtpHeaderLength)) - { - return -1; - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::FindMPEG4NALU(const WebRtc_UWord8* inData, - WebRtc_Word32 maxLength) -{ - WebRtc_Word32 size = 0; - for (WebRtc_Word32 i = maxLength; i > 4; i-=2) // Find NAL - { - // Scan down - if (inData[i] == 0) - { - if (inData[i-1] == 0) - { - // i point at the last zero - size = i-1; - }else if(inData[i+1] == 0) - { - size = i; - } - if(size > 0) - { - return size; - } - } - } - return 0; -} - -VideoCodecInformation* -RTPSenderVideo::CodecInformationVideo() -{ - return _videoCodecInformation; -} - -void -RTPSenderVideo::SetMaxConfiguredBitrateVideo(const WebRtc_UWord32 maxBitrate) -{ - _maxBitrate = maxBitrate; -} - -WebRtc_UWord32 -RTPSenderVideo::MaxConfiguredBitrateVideo() const -{ - return _maxBitrate; -} - -WebRtc_Word32 -RTPSenderVideo::SendH263(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - VideoCodecInformation* codecInfo) -{ - bool modeA = true; - WebRtc_UWord16 h263HeaderLength = 4; - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_Word32 payloadBytesToSend = payloadSize; - WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - // -2: one byte is possible old ebit -> sBit, - // one byte is new ebit if next GOB header is not byte aligned - // (eventual sBit, eBit) - WebRtc_UWord16 maxPayloadLengthH263 = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength - h263HeaderLength - 2; - - // Fragment packet into packets of max MaxPayloadLength bytes payload. - WebRtc_UWord8 numOfGOB = 0; - WebRtc_UWord16 prevOK = 0; - WebRtc_UWord32 payloadBytesSent = 0; - WebRtc_UWord8 sbit = 0; - _eBit = 0; - - H263Information* h263Information = NULL; - if(codecInfo) - { - // Another channel have already parsed this data - h263Information = static_cast(codecInfo); - - } else - { - if(_videoCodecInformation) - { - if(_videoCodecInformation->Type() != kRtpH263Video) - { - // Wrong codec - delete _videoCodecInformation; - _videoCodecInformation = new H263Information(); - } else - { - _videoCodecInformation->Reset(); - } - } else - { - _videoCodecInformation = new H263Information(); - } - h263Information = static_cast(_videoCodecInformation); - } - - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - const WebRtc_UWord8* data = payloadData; - const H263Info* ptrH263Info = NULL; - - if (h263Information->GetInfo(payloadData,payloadSize, ptrH263Info) == -1) - { - return -1; - } - - while (payloadBytesToSend > 0) - { - prevOK = 0; - modeA = true; - - if (payloadBytesToSend > maxPayloadLengthH263) - { - // Fragment packet at GOB boundary - for (; numOfGOB < ptrH263Info->numOfGOBs; numOfGOB++) - { - // Fit one or more GOBs into packet - if (WebRtc_Word32(ptrH263Info->ptrGOBbuffer[numOfGOB+1] - - payloadBytesSent) < maxPayloadLengthH263) - { - prevOK = static_cast( - ptrH263Info->ptrGOBbuffer[numOfGOB+1] - - payloadBytesSent); - }else - { - break; - } - } - if (!prevOK) - { - // GOB larger than max MaxPayloadLength bytes => Mode B required - // Fragment stream at MB boundaries - modeA = false; - - // Get MB positions within GOB - const H263MBInfo* ptrInfoMB = NULL; - if (-1 == h263Information->GetMBInfo(payloadData, payloadSize, - numOfGOB, ptrInfoMB)) - { - return -1; - } - WebRtc_Word32 offset = ptrH263Info-> - CalculateMBOffset(numOfGOB); - if(offset < 0) - { - return -1; - } - // Send packets fragmented at MB boundaries - if (-1 == SendH263MBs(frameType, payloadType, captureTimeStamp, - dataBuffer, data, rtpHeaderLength, - numOfGOB, *ptrH263Info,*ptrInfoMB, offset)) - { - return -1; - } - offset = ptrH263Info->CalculateMBOffset(numOfGOB+1); - if(offset < 0) - { - return -1; - } - WebRtc_Word32 numBytes = ptrInfoMB->ptrBuffer[offset-1] / 8; - WebRtc_Word32 numBytesRem = ptrInfoMB->ptrBuffer[offset-1] % 8; - if (numBytesRem) - { - // In case our GOB is not byte alligned - numBytes++; - } - payloadBytesToSend -= numBytes; - data += numBytes; - payloadBytesSent += numBytes; - numOfGOB++; - } - } - if (modeA) - { - h263HeaderLength = 4; - WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - // H.263 payload header (4 bytes) - // First bit 0 == mode A, (00 000 000) - dataBuffer[rtpHeaderLength] = 0; - dataBuffer[rtpHeaderLength+1] = ptrH263Info->uiH263PTypeFmt << 5; - // Last bit 0 - dataBuffer[rtpHeaderLength + 1] += ptrH263Info->codecBits << 1; - // First 3 bits 0 - dataBuffer[rtpHeaderLength + 2] = 0; - // No pb frame - dataBuffer[rtpHeaderLength + 3] = 0; - - // Last packet eBit -> current packet sBit - sbit = (8 - _eBit) % 8; - - if (payloadBytesToSend > maxPayloadLengthH263) - { - if (numOfGOB > 0) - { - // Check if GOB header is byte aligned - if(ptrH263Info->ptrGOBbufferSBit) - { - _eBit = (8 - - ptrH263Info->ptrGOBbufferSBit[numOfGOB - 1]) % 8; - } else - { - _eBit = 0; - } - } - if (_eBit) - { - // Next GOB header is not byte aligned, - // include this byte in packet - // Send the byte with eBits - prevOK++; - } - } - - if (payloadBytesToSend > maxPayloadLengthH263) - { - payloadBytesInPacket = prevOK; - payloadBytesToSend -= payloadBytesInPacket; - _rtpSender.BuildRTPheader(dataBuffer, payloadType, - false, captureTimeStamp); - - } else - { - payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend; - payloadBytesToSend = 0; - _rtpSender.BuildRTPheader(dataBuffer, payloadType, - true, captureTimeStamp); - _eBit = 0; - } - - if (sbit) - { - // Add last sent byte and put payload in packet - // Set sBit - dataBuffer[rtpHeaderLength] = dataBuffer[rtpHeaderLength] | - ((sbit & 0x7) << 3); - memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], - &_savedByte, 1); - memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength + 1], - data, payloadBytesInPacket); - h263HeaderLength++; - - }else - { - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], data, - payloadBytesInPacket); - } - if (_eBit) - { - // Save last byte to paste in next packet - // Set eBit - dataBuffer[rtpHeaderLength] |= (_eBit & 0x7); - _savedByte = dataBuffer[payloadBytesInPacket + - h263HeaderLength + rtpHeaderLength-1]; - } - if (-1 == SendVideoPacket(frameType, - dataBuffer, - payloadBytesInPacket + h263HeaderLength, - rtpHeaderLength)) - { - return -1; - } - payloadBytesSent += payloadBytesInPacket; - data += payloadBytesInPacket; - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendH2631998(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - VideoCodecInformation* codecInfo) -{ - const WebRtc_UWord16 h2631998HeaderLength = 2; - // No extra header included - const WebRtc_UWord8 pLen = 0; - const WebRtc_UWord8 peBit = 0; - bool fragment = false; - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_Word32 payloadBytesToSend = payloadSize; - WebRtc_UWord16 numPayloadBytesToSend = 0; - WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - - // P is not set in all packets, - // only packets that has a PictureStart or a GOB header - WebRtc_UWord8 p = 2; - - H263Information* h263Information = NULL; - if(codecInfo) - { - // Another channel have already parsed this data - h263Information = static_cast(codecInfo); - - } else - { - if(_videoCodecInformation) - { - if(_videoCodecInformation->Type() != kRtpH263Video) - { - // Wrong codec - delete _videoCodecInformation; - _videoCodecInformation = new H263Information(); - } else - { - _videoCodecInformation->Reset(); - } - } else - { - _videoCodecInformation = new H263Information(); - } - h263Information = static_cast(_videoCodecInformation); - } - const H263Info* ptrH263Info = NULL; - if (h263Information->GetInfo(payloadData,payloadSize, ptrH263Info) == -1) - { - return -1; - } - - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - const WebRtc_UWord16 maxPayloadLengthH2631998 = - _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - - h2631998HeaderLength; - const WebRtc_UWord8* data = payloadData; - WebRtc_UWord8 numOfGOB = 0; - WebRtc_UWord32 payloadBytesSent = 0; - - while(payloadBytesToSend > 0) - { - WebRtc_Word32 prevOK = 0; - - // Fragment packets at GOB boundaries - for (; numOfGOB < ptrH263Info->numOfGOBs; numOfGOB++) - { - // Fit one or more GOBs into packet - if (static_cast( - ptrH263Info->ptrGOBbuffer[numOfGOB+1] - - payloadBytesSent) <= - (maxPayloadLengthH2631998 + p)) - { - prevOK = static_cast( - ptrH263Info->ptrGOBbuffer[numOfGOB+1] - - payloadBytesSent); - if(fragment) - { - // This is a fragment, send it - break; - } - }else - { - break; - } - } - if(!prevOK) - { - // GOB larger than MaxPayloadLength bytes - fragment = true; - numPayloadBytesToSend = maxPayloadLengthH2631998; - } else - { - fragment = false; - numPayloadBytesToSend = WebRtc_UWord16(prevOK - p); - } - dataBuffer[rtpHeaderLength] = (p << 1) + ((pLen >> 5) & 0x01); - dataBuffer[rtpHeaderLength+1] = ((pLen & 0x1F) << 3) + peBit; - - if(p == 2) - { - // Increment data ptr - // (do not send first two bytes of picture or GOB start code) - data += 2; - payloadBytesToSend -= 2; - } - - if(payloadBytesToSend > maxPayloadLengthH2631998) - { - payloadBytesInPacket = numPayloadBytesToSend; - payloadBytesToSend -= payloadBytesInPacket; - - _rtpSender.BuildRTPheader(dataBuffer, payloadType, - false, captureTimeStamp); - }else - { - payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend; - payloadBytesToSend = 0; - // MarkerBit is 1 - _rtpSender.BuildRTPheader(dataBuffer, payloadType, - true, captureTimeStamp); - } - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength + h2631998HeaderLength], - data, payloadBytesInPacket); - - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + - h2631998HeaderLength, rtpHeaderLength)) - { - return -1; - } - data += payloadBytesInPacket; - payloadBytesSent += payloadBytesInPacket + p; - if(fragment) - { - p = 0; - }else - { - p = 2; - } - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendH263MBs(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_UWord8* dataBuffer, - const WebRtc_UWord8 *data, - const WebRtc_UWord16 rtpHeaderLength, - const WebRtc_UWord8 numOfGOB, - const H263Info& info, - const H263MBInfo& infoMB, - const WebRtc_Word32 offset) -{ - // Mode B - WebRtc_UWord32 *sizeOfMBs = &infoMB.ptrBuffer[offset]; - WebRtc_UWord8 *hmv1 = &infoMB.ptrBufferHMV[offset]; - WebRtc_UWord8 *vmv1 = &infoMB.ptrBufferVMV[offset]; - - WebRtc_UWord16 h263HeaderLength = 8; - WebRtc_UWord16 payloadBytesInPacket = 0; - WebRtc_Word32 payloadBytesToSend = - sizeOfMBs[info.ptrNumOfMBs[numOfGOB]-1] / 8; - WebRtc_UWord8 eBitLastByte = (WebRtc_UWord8)((8 - - (sizeOfMBs[info.ptrNumOfMBs[numOfGOB]-1] % 8)) % 8); - WebRtc_Word32 sBit = 0; - WebRtc_Word32 firstMB = 0; - WebRtc_UWord32 bitsRem = 0; - WebRtc_UWord32 payloadBytesSent = 0; - WebRtc_Word32 numOfMB = 0; - WebRtc_Word32 prevOK = 0; - - // (Eventual sBit, eBit) - WebRtc_UWord16 maxPayloadLengthH263MB = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength - h263HeaderLength - 2; - if (eBitLastByte) - { - payloadBytesToSend++; - } - - // Fragment packet into packets of max MaxPayloadLength bytes payload. - while (payloadBytesToSend > 0) - { - prevOK = 0; - firstMB = numOfMB; - if (payloadBytesToSend > maxPayloadLengthH263MB) - { - // Fragment packet at MB boundary - for (; numOfMB < info.ptrNumOfMBs[numOfGOB]; numOfMB++) - { - // Fit one or more MBs into packet - if (WebRtc_Word32(sizeOfMBs[numOfMB] / 8 - payloadBytesSent) < - maxPayloadLengthH263MB) - { - prevOK = sizeOfMBs[numOfMB] / 8 - payloadBytesSent; - bitsRem = sizeOfMBs[numOfMB] % 8; - if (bitsRem) - { - prevOK++; - } - }else - { - break; - } - } - - if (!prevOK) - { - // MB does not fit in packet - return -1; - } - } - - - // H.263 payload header (8 bytes) - h263HeaderLength = 8; - // First bit 1 == mode B, 10 000 000 - dataBuffer[rtpHeaderLength] = (WebRtc_UWord8)0x80; - // Source format - dataBuffer[rtpHeaderLength + 1] = (info.uiH263PTypeFmt) << 5; - if (numOfGOB == 0) - { - // Quantization value for first MB in packet - dataBuffer[rtpHeaderLength + 1] += info.pQuant; - } - if (numOfGOB > 0 && firstMB > 0) - { - // Quantization value for first MB in packet - // (0 if packet begins w/ a GOB header) - dataBuffer[rtpHeaderLength + 1] += info.ptrGQuant[numOfGOB]; - } - // GOB # - dataBuffer[rtpHeaderLength + 2] = numOfGOB << 3; - // First MB in the packet - dataBuffer[rtpHeaderLength + 2] += (WebRtc_UWord8)((firstMB >> 6)& 0x7); - dataBuffer[rtpHeaderLength + 3] = (WebRtc_UWord8)(firstMB << 2); - dataBuffer[rtpHeaderLength + 4] = (info.codecBits) << 4; - // Horizontal motion vector - dataBuffer[rtpHeaderLength + 4] += (hmv1[firstMB] & 0x7F) >> 3; - dataBuffer[rtpHeaderLength + 5] = hmv1[firstMB] << 5; - // Vertical motion vector - dataBuffer[rtpHeaderLength + 5] += (vmv1[firstMB] & 0x7F) >> 2; - dataBuffer[rtpHeaderLength + 6] = vmv1[firstMB] << 6; - dataBuffer[rtpHeaderLength + 7] = 0; - - sBit = (8 - _eBit) % 8; - - if (payloadBytesToSend > maxPayloadLengthH263MB) - { - payloadBytesInPacket = (WebRtc_UWord16)prevOK; - payloadBytesToSend -= payloadBytesInPacket; - - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false, - captureTimeStamp); - - _eBit = (WebRtc_UWord8)((8 - bitsRem) % 8); - } - else - { - payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend; - payloadBytesToSend = 0; - - if (numOfGOB == (info.numOfGOBs - 1)) - { - _rtpSender.BuildRTPheader(dataBuffer, payloadType, true, - captureTimeStamp); - _eBit = 0; - } - else - { - _rtpSender.BuildRTPheader(dataBuffer, payloadType, false, - captureTimeStamp); - _eBit = eBitLastByte; - } - } - - - if (sBit) - { - // Add last sent byte and put payload in packet - dataBuffer[rtpHeaderLength] |= ((sBit & 0x7) << 3); - dataBuffer[rtpHeaderLength + h263HeaderLength] = _savedByte; - memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength + 1], - data, payloadBytesInPacket); - h263HeaderLength++; - } else - { - // Put payload in packet - memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], - data, payloadBytesInPacket); - } - if (_eBit) - { - // Save last byte to paste in next packet - dataBuffer[rtpHeaderLength] |= (_eBit & 0x7); - _savedByte = dataBuffer[rtpHeaderLength + - h263HeaderLength + - payloadBytesInPacket - 1]; - } - if (-1 == SendVideoPacket(frameType, - dataBuffer, - payloadBytesInPacket + h263HeaderLength, - rtpHeaderLength)) - { - return -1; - } - - data += payloadBytesInPacket; - payloadBytesSent += payloadBytesInPacket; - } - return 0; -} - -WebRtc_Word32 -RTPSenderVideo::SendVP8(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength(); - WebRtc_UWord16 vp8HeaderLength = 1; - int payloadBytesInPacket = 0; - WebRtc_Word32 bytesSent = 0; - - WebRtc_Word32 payloadBytesToSend = payloadSize; - const WebRtc_UWord8* data = payloadData; - - WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; - WebRtc_UWord16 maxPayloadLengthVP8 = _rtpSender.MaxPayloadLength() - - FECPacketOverhead() - rtpHeaderLength; - - assert(rtpTypeHdr); - RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, - *fragmentation, kStrict); - - bool last = false; - while (!last) - { - // Write VP8 Payload Descriptor and VP8 payload. - if (packetizer.NextPacket(maxPayloadLengthVP8, - &dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last) < 0) - { - return -1; - } - - // Write RTP header. - // Set marker bit true if this is the last packet in frame. - _rtpSender.BuildRTPheader(dataBuffer, payloadType, last, - captureTimeStamp); - - //TODO (marpan): Set the _numberFirstPartition - - if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, - rtpHeaderLength)) - { - return -1; - } - } - return 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h deleted file mode 100644 index 078ec833e..000000000 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ - -#include "typedefs.h" -#include "common_types.h" // Transport -#include "rtp_rtcp_config.h" - -#include "rtp_rtcp_defines.h" -#include "rtp_utility.h" -#include "list_wrapper.h" - -#include "video_codec_information.h" -#include "h263_information.h" -#include "forward_error_correction.h" -#include "Bitrate.h" - -#include "rtp_sender.h" - -namespace webrtc { -class CriticalSectionWrapper; -class RTPSenderVideo -{ -public: - RTPSenderVideo(const WebRtc_Word32 id, RTPSenderInterface* rtpSender); - virtual ~RTPSenderVideo(); - - WebRtc_Word32 Init(); - - virtual void ChangeUniqueId(const WebRtc_Word32 id); - - virtual RtpVideoCodecTypes VideoCodecType() const; - - WebRtc_UWord16 FECPacketOverhead() const; - - WebRtc_Word32 RegisterVideoPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 maxBitRate, - ModuleRTPUtility::Payload*& payload); - - WebRtc_Word32 SendVideo(const RtpVideoCodecTypes videoType, - const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - VideoCodecInformation* codecInfo, - const RTPVideoTypeHeader* rtpTypeHdr); - - WebRtc_Word32 SendRTPIntraRequest(); - - void SetVideoCodecType(RtpVideoCodecTypes type); - - VideoCodecInformation* CodecInformationVideo(); - - void SetMaxConfiguredBitrateVideo(const WebRtc_UWord32 maxBitrate); - - WebRtc_UWord32 MaxConfiguredBitrateVideo() const; - - WebRtc_Word32 SendPadData(const WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord32 bytes); - - // FEC - WebRtc_Word32 SetGenericFECStatus(const bool enable, - const WebRtc_UWord8 payloadTypeRED, - const WebRtc_UWord8 payloadTypeFEC); - - WebRtc_Word32 GenericFECStatus(bool& enable, - WebRtc_UWord8& payloadTypeRED, - WebRtc_UWord8& payloadTypeFEC) const; - - WebRtc_Word32 SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, - const WebRtc_UWord8 deltaFrameCodeRate); - -protected: - virtual WebRtc_Word32 SendVideoPacket(const FrameType frameType, - const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength); - -private: - WebRtc_Word32 SendGeneric(const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize); - - WebRtc_Word32 SendH263(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - VideoCodecInformation* codecInfo); - - WebRtc_Word32 SendH2631998(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - VideoCodecInformation* codecInfo); - - WebRtc_Word32 SendMPEG4(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize); - - WebRtc_Word32 SendVP8(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader* fragmentation, - const RTPVideoTypeHeader* rtpTypeHdr); - - // MPEG 4 - WebRtc_Word32 FindMPEG4NALU(const WebRtc_UWord8* inData ,WebRtc_Word32 MaxPayloadLength); - - // H263 - WebRtc_Word32 SendH263MBs(const FrameType frameType, - const WebRtc_Word8 payloadType, - const WebRtc_UWord32 captureTimeStamp, - WebRtc_UWord8* dataBuffer, - const WebRtc_UWord8 *data, - const WebRtc_UWord16 rtpHeaderLength, - const WebRtc_UWord8 numOfGOB, - const H263Info& info, - const H263MBInfo& infoMB, - const WebRtc_Word32 offset); - -private: - WebRtc_Word32 _id; - RTPSenderInterface& _rtpSender; - - CriticalSectionWrapper& _sendVideoCritsect; - RtpVideoCodecTypes _videoType; - VideoCodecInformation* _videoCodecInformation; - WebRtc_UWord32 _maxBitrate; - - // FEC - ForwardErrorCorrection _fec; - bool _fecEnabled; - WebRtc_Word8 _payloadTypeRED; - WebRtc_Word8 _payloadTypeFEC; - WebRtc_UWord8 _codeRateKey; - WebRtc_UWord8 _codeRateDelta; - WebRtc_UWord8 _fecProtectionFactor; - WebRtc_UWord32 _numberFirstPartition; - ListWrapper _mediaPacketListFec; - ListWrapper _rtpPacketListFec; - - // H263 - WebRtc_UWord8 _savedByte; - WebRtc_UWord8 _eBit; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_H_ diff --git a/modules/rtp_rtcp/source/rtp_utility.cc b/modules/rtp_rtcp/source/rtp_utility.cc deleted file mode 100644 index 3fbb3ffbd..000000000 --- a/modules/rtp_rtcp/source/rtp_utility.cc +++ /dev/null @@ -1,995 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_utility.h" - -#include // memcpy -#include // ceil -#include - -#if defined(_WIN32) - #include // FILETIME - #include // timeval - #include //timeGetTime -#elif ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC)) - #include // gettimeofday - #include -#endif - -#if (defined(_DEBUG) && defined(_WIN32) && (_MSC_VER >= 1400)) -#include -#define DEBUG_PRINT(...) \ -{ \ - char msg[256]; \ - sprintf(msg, __VA_ARGS__); \ - OutputDebugString(msg); \ -} -#else -// special fix for visual 2003 -#define DEBUG_PRINT(exp) ((void)0) -#endif // defined(_DEBUG) && defined(_WIN32) - -namespace -{ - const float FRAC = 4.294967296E9; -} - -namespace webrtc { -// PLATFORM SPECIFIC [BEGIN] -#if defined(_WIN32) - #include - - namespace ModuleRTPUtility - { - WebRtc_UWord32 GetTimeInMS() - { - return timeGetTime(); - } - bool StringCompare(const WebRtc_Word8* str1 , const WebRtc_Word8* str2, const WebRtc_UWord32 length) - { - return (_strnicmp(str1, str2, length) == 0)?true: false; - } - - class HelpTimer - { - public: - struct reference_point - { - FILETIME file_time; - LARGE_INTEGER counterMS; - }; - - HelpTimer() - { - // set timer accuracy to 1 ms - timeBeginPeriod(1); - - _timeInMs = 0; - _numWrapTimeInMs = 0; - synchronize(); - }; - - virtual ~HelpTimer() - { - timeEndPeriod(1); - }; - - void get_time(FILETIME& current_time) - { - // we can't use query performance counter due to speed stepping - DWORD t = timeGetTime(); - // NOTE: we have a miss match in sign between _timeInMs(LONG) and t(DWORD) however we only use it here without +- etc - volatile LONG* timeInMsPtr = &_timeInMs; - DWORD old = InterlockedExchange(timeInMsPtr, t); // make sure that we only inc wrapper once - if(old > t) - { - // wrap - _numWrapTimeInMs++; - } - LARGE_INTEGER elapsedMS; - elapsedMS.HighPart = _numWrapTimeInMs; - elapsedMS.LowPart = t; - - elapsedMS.QuadPart = elapsedMS.QuadPart - _ref_point.counterMS.QuadPart; - - // - // Translate to 100-nanoseconds intervals (FILETIME resolution) and add to - // reference FILETIME to get current FILETIME. - // - ULARGE_INTEGER filetime_ref_as_ul; - - filetime_ref_as_ul.HighPart = _ref_point.file_time.dwHighDateTime; - filetime_ref_as_ul.LowPart = _ref_point.file_time.dwLowDateTime; - filetime_ref_as_ul.QuadPart += (ULONGLONG)((elapsedMS.QuadPart)*1000*10); - // - // Copy to result - // - current_time.dwHighDateTime = filetime_ref_as_ul.HighPart; - current_time.dwLowDateTime = filetime_ref_as_ul.LowPart; - }; - - private: - void synchronize() - { - FILETIME ft0 = { 0, 0 }, - ft1 = { 0, 0 }; - // - // Spin waiting for a change in system time. Get the matching - // performance counter value for that time. - // - ::GetSystemTimeAsFileTime(&ft0); - do - { - ::GetSystemTimeAsFileTime(&ft1); - _ref_point.counterMS.QuadPart = ::timeGetTime(); - ::Sleep(0); - } - while ((ft0.dwHighDateTime == ft1.dwHighDateTime) && - (ft0.dwLowDateTime == ft1.dwLowDateTime)); - - _ref_point.file_time = ft1; - } - volatile LONG _timeInMs; // this needs to be long due to Windows, not an issue due to its usage - volatile WebRtc_UWord32 _numWrapTimeInMs; - reference_point _ref_point; - }; - - static HelpTimer helpTimer; - - }; // end namespace - -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - #include // gettimeofday - #include // nanosleep, gettimeofday - - WebRtc_UWord32 - ModuleRTPUtility::GetTimeInMS() - { - struct timeval tv; - struct timezone tz; - WebRtc_UWord32 val; - - gettimeofday(&tv, &tz); - val = (WebRtc_UWord32)(tv.tv_sec*1000 + tv.tv_usec/1000); - return val; - } - bool ModuleRTPUtility::StringCompare(const WebRtc_Word8* str1 , const WebRtc_Word8* str2, const WebRtc_UWord32 length) - { - return (strncasecmp(str1, str2, length) == 0)?true: false; - } -#endif // PLATFORM SPECIFIC [END] - -#if !defined(WEBRTC_LITTLE_ENDIAN) && !defined(WEBRTC_BIG_ENDIAN) - #error Either WEBRTC_LITTLE_ENDIAN or WEBRTC_BIG_ENDIAN must be defined -#endif - -/* for RTP/RTCP - All integer fields are carried in network byte order, that is, most - significant byte (octet) first. AKA big-endian. -*/ -void -ModuleRTPUtility::AssignUWord32ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord32 value) -{ -#if defined(WEBRTC_LITTLE_ENDIAN) - dataBuffer[0] = static_cast(value>>24); - dataBuffer[1] = static_cast(value>>16); - dataBuffer[2] = static_cast(value>>8); - dataBuffer[3] = static_cast(value); -#else - WebRtc_UWord32* ptr = reinterpret_cast(dataBuffer); - ptr[0] = value; -#endif -} - -void -ModuleRTPUtility::AssignUWord24ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord32 value) -{ -#if defined(WEBRTC_LITTLE_ENDIAN) - dataBuffer[0] = static_cast(value>>16); - dataBuffer[1] = static_cast(value>>8); - dataBuffer[2] = static_cast(value); -#else - dataBuffer[0] = static_cast(value); - dataBuffer[1] = static_cast(value>>8); - dataBuffer[2] = static_cast(value>>16); -#endif -} - -void -ModuleRTPUtility::AssignUWord16ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord16 value) -{ -#if defined(WEBRTC_LITTLE_ENDIAN) - dataBuffer[0] = static_cast(value>>8); - dataBuffer[1] = static_cast(value); -#else - WebRtc_UWord16* ptr = reinterpret_cast(dataBuffer); - ptr[0] = value; -#endif -} - -WebRtc_UWord16 -ModuleRTPUtility::BufferToUWord16(const WebRtc_UWord8* dataBuffer) -{ -#if defined(WEBRTC_LITTLE_ENDIAN) - return (dataBuffer[0] << 8) + dataBuffer[1]; -#else - return *reinterpret_cast(dataBuffer); -#endif -} - -WebRtc_UWord32 -ModuleRTPUtility::BufferToUWord24(const WebRtc_UWord8* dataBuffer) -{ - return (dataBuffer[0] << 16) + (dataBuffer[1] << 8) + dataBuffer[2]; -} - -WebRtc_UWord32 -ModuleRTPUtility::BufferToUWord32(const WebRtc_UWord8* dataBuffer) -{ -#if defined(WEBRTC_LITTLE_ENDIAN) - return (dataBuffer[0] << 24) + (dataBuffer[1] << 16) + (dataBuffer[2] << 8) + - dataBuffer[3]; -#else - return *reinterpret_cast(dataBuffer); -#endif -} - -WebRtc_UWord32 -ModuleRTPUtility::pow2(WebRtc_UWord8 exp) -{ - return 1 << exp; -} - -WebRtc_Word32 -ModuleRTPUtility::CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) -{ - /* - * Use the system time (roughly synchronised to the tick, and - * extrapolated using the system performance counter. - */ - const WebRtc_UWord32 JAN_1970 = 2208988800UL; // NTP seconds - -#if defined(_WIN32) - const WebRtc_UWord64 FILETIME_1970 = 0x019db1ded53e8000; - - FILETIME StartTime; - WebRtc_UWord64 Time; - struct timeval tv; - - // we can't use query performance counter since they can change depending on speed steping - helpTimer.get_time(StartTime); - - Time = (((WebRtc_UWord64) StartTime.dwHighDateTime) << 32) + - (WebRtc_UWord64) StartTime.dwLowDateTime; - - // Convert the hecto-nano second time to tv format - Time -= FILETIME_1970; - - tv.tv_sec = (WebRtc_UWord32) ( Time / (WebRtc_UWord64)10000000); - tv.tv_usec = (WebRtc_UWord32) (( Time % (WebRtc_UWord64)10000000) / 10); - - double dtemp; - - secs = tv.tv_sec + JAN_1970; - dtemp = tv.tv_usec / 1e6; - - if (dtemp >= 1) - { - dtemp -= 1; - secs++; - } else if (dtemp < -1) - { - dtemp += 1; - secs--; - } - dtemp *= FRAC; - frac = (WebRtc_UWord32)dtemp; - return 0; -#endif - -#if ((defined WEBRTC_LINUX) || (defined WEBRTC_MAC)) - - double dtemp; - struct timeval tv; - struct timezone tz; - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; - gettimeofday(&tv,&tz); - - secs = tv.tv_sec + JAN_1970; - dtemp = tv.tv_usec / 1e6; - if (dtemp >= 1) - { - dtemp -= 1; - secs++; - } else if (dtemp < -1) - { - dtemp += 1; - secs--; - } - dtemp *= FRAC; - frac = (WebRtc_UWord32)dtemp; - - return(0); -#endif -} - -WebRtc_UWord32 -ModuleRTPUtility::ConvertNTPTimeToMS(WebRtc_UWord32 NTPsec, WebRtc_UWord32 NTPfrac) -{ - int freq = 1000; - float ftemp = (float)NTPfrac/(float)FRAC; - WebRtc_UWord32 tmp = (WebRtc_UWord32)(ftemp * freq); - WebRtc_UWord32 MStime= NTPsec*freq + tmp; - return MStime; -} - -WebRtc_UWord32 -ModuleRTPUtility::CurrentRTP(WebRtc_UWord32 freq) -{ - WebRtc_UWord32 NTPsec = 0; - WebRtc_UWord32 NTPfrac = 0; - CurrentNTP( NTPsec, NTPfrac); - float ftemp = (float)NTPfrac/(float)FRAC; - WebRtc_UWord32 tmp = (WebRtc_UWord32)(ftemp * freq); - return NTPsec*freq + tmp; -} - -void -ModuleRTPUtility::RTPPayload::SetType(RtpVideoCodecTypes videoType) -{ - type = videoType; - - switch (type) - { - case kRtpNoVideo: - break; - case kRtpH263Video: - case kRtpH2631998Video: - { - info.H263.hasPictureStartCode = false; - info.H263.insert2byteStartCode = false; - info.H263.hasPbit = false; - info.H263.frameWidth = 0; - info.H263.frameHeight = 0; - info.H263.startBits = 0; - info.H263.endBits = 0; - info.H263.data = 0; - info.H263.dataLength = 0; - break; - } - case kRtpMpeg4Video: - { - info.MPEG4.isFirstPacket = false; - info.MPEG4.data = 0; - info.MPEG4.dataLength = 0; - break; - } - case kRtpVp8Video: - { - info.VP8.beginningOfFrame = false; - info.VP8.nonReferenceFrame = false; - info.VP8.hasPictureID = false; - info.VP8.fragments = false; - info.VP8.startFragment = false; - info.VP8.stopFragment = false; - break; - } - default: - break; - } -} - -ModuleRTPUtility::RTPHeaderParser::RTPHeaderParser(const WebRtc_UWord8* rtpData, - const WebRtc_UWord32 rtpDataLength): - _ptrRTPDataBegin(rtpData), - _ptrRTPDataEnd(rtpData ? (rtpData + rtpDataLength) : NULL) -{ -} - -ModuleRTPUtility::RTPHeaderParser::~RTPHeaderParser() -{ -} - -bool -ModuleRTPUtility::RTPHeaderParser::RTCP() const -{ - // 72 to 76 is reserved for RTP - // 77 to 79 is not reserver but they are not assigned we will block them - // for RTCP 200 SR == marker bit + 72 - // for RTCP 204 APP == marker bit + 76 - /* - * RTCP - * - * FIR full INTRA-frame request 192 [RFC2032] supported - * NACK negative acknowledgement 193 [RFC2032] - * IJ Extended inter-arrival jitter report 195 [RFC-ietf-avt-rtp-toffset-07.txt] http://tools.ietf.org/html/draft-ietf-avt-rtp-toffset-07 - * SR sender report 200 [RFC3551] supported - * RR receiver report 201 [RFC3551] supported - * SDES source description 202 [RFC3551] supported - * BYE goodbye 203 [RFC3551] supported - * APP application-defined 204 [RFC3551] ignored - * RTPFB Transport layer FB message 205 [RFC4585] supported - * PSFB Payload-specific FB message 206 [RFC4585] supported - * XR extended report 207 [RFC3611] supported - */ - - /* 205 RFC 5104 - * FMT 1 NACK supported - * FMT 2 reserved - * FMT 3 TMMBR supported - * FMT 4 TMMBN supported - */ - - /* 206 RFC 5104 - * FMT 1: Picture Loss Indication (PLI) supported - * FMT 2: Slice Lost Indication (SLI) - * FMT 3: Reference Picture Selection Indication (RPSI) - * FMT 4: Full Intra Request (FIR) Command supported - * FMT 5: Temporal-Spatial Trade-off Request (TSTR) - * FMT 6: Temporal-Spatial Trade-off Notification (TSTN) - * FMT 7: Video Back Channel Message (VBCM) - * FMT 15: Application layer FB message - */ - - const WebRtc_UWord8 payloadType = _ptrRTPDataBegin[1]; - - bool RTCP = false; - - // check if this is a RTCP packet - switch(payloadType) - { - case 192: - RTCP = true; - break; - case 193: - case 195: - // not supported - // pass through and check for a potential RTP packet - break; - case 200: - case 201: - case 202: - case 203: - case 204: - case 205: - case 206: - case 207: - RTCP = true; - break; - } - return RTCP; -} - -bool -ModuleRTPUtility::RTPHeaderParser::Parse(WebRtcRTPHeader& parsedPacket) const -{ - const ptrdiff_t length = _ptrRTPDataEnd - _ptrRTPDataBegin; - - if (length < 12) - { - return false; - } - - const WebRtc_UWord8 V = _ptrRTPDataBegin[0] >> 6 ; // Version - const bool P = ((_ptrRTPDataBegin[0] & 0x20) == 0) ? false : true; // Padding - const bool X = ((_ptrRTPDataBegin[0] & 0x10) == 0) ? false : true; // eXtension - const WebRtc_UWord8 CC = _ptrRTPDataBegin[0] & 0x0f; - const bool M = ((_ptrRTPDataBegin[1] & 0x80) == 0) ? false : true; - - const WebRtc_UWord8 PT = _ptrRTPDataBegin[1] & 0x7f; - - const WebRtc_UWord16 sequenceNumber = (_ptrRTPDataBegin[2] << 8) + _ptrRTPDataBegin[3]; - - const WebRtc_UWord8* ptr = &_ptrRTPDataBegin[4]; - - WebRtc_UWord32 RTPTimestamp = *ptr++ << 24; - RTPTimestamp += *ptr++ << 16; - RTPTimestamp += *ptr++ << 8; - RTPTimestamp += *ptr++; - - WebRtc_UWord32 SSRC = *ptr++ << 24; - SSRC += *ptr++ << 16; - SSRC += *ptr++ << 8; - SSRC += *ptr++; - - if (V != 2) - { - return false; - } - - const WebRtc_UWord8 CSRCocts = CC * 4; - - if ((ptr + CSRCocts) > _ptrRTPDataEnd) - { - return false; - } - - parsedPacket.header.markerBit = M; - parsedPacket.header.payloadType = PT; - parsedPacket.header.sequenceNumber = sequenceNumber; - parsedPacket.header.timestamp = RTPTimestamp; - parsedPacket.header.ssrc = SSRC; - parsedPacket.header.numCSRCs = CC; - parsedPacket.header.paddingLength = P ? *(_ptrRTPDataEnd - 1) : 0; - - for (unsigned int i = 0; i < CC; ++i) - { - WebRtc_UWord32 CSRC = *ptr++ << 24; - CSRC += *ptr++ << 16; - CSRC += *ptr++ << 8; - CSRC += *ptr++; - parsedPacket.header.arrOfCSRCs[i] = CSRC; - } - parsedPacket.type.Audio.numEnergy = parsedPacket.header.numCSRCs; - - parsedPacket.header.headerLength = 12 + CSRCocts; - if (X) - { - const ptrdiff_t remain = _ptrRTPDataEnd - ptr; - if (remain < 4) - { - return false; - } - - parsedPacket.header.headerLength += 4; - - WebRtc_UWord16 definedByProfile = *ptr++ << 8; - definedByProfile += *ptr++; - - WebRtc_UWord16 XLen = *ptr++ << 8; - XLen += *ptr++; // in 32 bit words - XLen *= 4; // in octs - - if (remain < (4 + XLen)) - { - return false; - } - if(definedByProfile == RTP_AUDIO_LEVEL_UNIQUE_ID && XLen == 4) - { - // --- Only used for debugging --- - - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0xBE | 0xDE | length=1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ID | len=0 |V| level | 0x00 | 0x00 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - // Parse out the fields but only use it for debugging for now. - const WebRtc_UWord8 ID = (*ptr & 0xf0) >> 4; - const WebRtc_UWord8 len = (*ptr & 0x0f); - ptr++; - const WebRtc_UWord8 V = (*ptr & 0x80) >> 7; - const WebRtc_UWord8 level = (*ptr & 0x7f); - // DEBUG_PRINT("RTP_AUDIO_LEVEL_UNIQUE_ID: ID=%u, len=%u, V=%u, level=%u", ID, len, V, level); - } - parsedPacket.header.headerLength += XLen; - } - - return true; -} - -// RTP payload parser -ModuleRTPUtility::RTPPayloadParser::RTPPayloadParser(const RtpVideoCodecTypes videoType, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength) - : - _dataPtr(payloadData), - _dataLength(payloadDataLength), - _videoType(videoType) -{ -} - -ModuleRTPUtility::RTPPayloadParser::~RTPPayloadParser() -{ -} - -bool -ModuleRTPUtility::RTPPayloadParser::Parse( RTPPayload& parsedPacket) const -{ - parsedPacket.SetType(_videoType); - - switch (_videoType) - { - case kRtpNoVideo: - return ParseGeneric(parsedPacket); - case kRtpH263Video: - return ParseH263(parsedPacket); - case kRtpH2631998Video: - return ParseH2631998(parsedPacket); - case kRtpMpeg4Video: - return ParseMPEG4(parsedPacket); - case kRtpVp8Video: - return ParseVP8(parsedPacket); - default: - return false; - } -} - -bool -ModuleRTPUtility::RTPPayloadParser::ParseGeneric(RTPPayload& /*parsedPacket*/) const -{ - return false; -} - -bool -ModuleRTPUtility::RTPPayloadParser::ParseH263(RTPPayload& parsedPacket) const -{ - if(_dataLength <= 2) - { - // data length sanity check. - return false; - } - - const WebRtc_UWord8 header1 = _dataPtr[0]; - const WebRtc_UWord8 header2 = _dataPtr[1]; - bool modeA = false; - bool modeB = false; - bool modeC = false; - - parsedPacket.frameType = ((header2 & 0x10) == 0) ? kIFrame : kPFrame; - - unsigned int h263HeaderLength = 0; - if ((header1 & 0x80) == 0) - { - // Mode A - modeA = true; - h263HeaderLength = 4; - } - else - { - // In Mode B and Mode C, I bit is in 5th byte of header - const WebRtc_UWord8 header5 = _dataPtr[4]; - parsedPacket.frameType = ((header5 & 0x80) == 0) ? kIFrame : kPFrame; - - if((header1 & 0x40) == 0) - { - // IMPROVEMENT use the information in the H263 header? GQuant of the first MB - modeB = true; - h263HeaderLength = 8; - }else - { - modeC = true; - h263HeaderLength = 12; - } - } - - if (_dataLength < h263HeaderLength) - { - // Received empty H263 packet - return false; - } - - // Get SBIT and EBIT - WebRtc_UWord8 sbit = 0; - WebRtc_UWord8 ebit = 0; - const WebRtc_UWord8 seBit = header1 & 0x3f; - if (seBit) - { - // We got SBIT or EBIT - sbit = (seBit >> 3) & 0x07; - ebit = seBit & 0x07; - } - - const bool isH263PictureStartCode = H263PictureStartCode(_dataPtr + h263HeaderLength); - if (isH263PictureStartCode) - { - // parse out real size and inform the decoder - WebRtc_UWord16 width = 0; - WebRtc_UWord16 height = 0; - - GetH263FrameSize(_dataPtr + h263HeaderLength, width, height); - - parsedPacket.info.H263.hasPictureStartCode = true; - parsedPacket.info.H263.frameWidth = width; - parsedPacket.info.H263.frameHeight = height; - } - parsedPacket.info.H263.startBits = sbit; - parsedPacket.info.H263.endBits = ebit; - parsedPacket.info.H263.data = _dataPtr + h263HeaderLength; - parsedPacket.info.H263.dataLength = _dataLength - h263HeaderLength; - parsedPacket.info.H263.insert2byteStartCode = false; // not used in this mode - parsedPacket.info.H263.hasPbit = true; // not used in this mode - return true; -} - -bool -ModuleRTPUtility::RTPPayloadParser::ParseH2631998( RTPPayload& parsedPacket) const -{ - unsigned int h2631998HeaderLength = 2; - if(_dataLength <= h2631998HeaderLength) - { - // Received empty H263 (1998) packet - return false; - } - - const WebRtc_UWord8 header1 = _dataPtr[0]; - const WebRtc_UWord8 header2 = _dataPtr[1]; - - parsedPacket.frameType = kPFrame; - - WebRtc_UWord8 p = (header1 >> 2) & 0x01; // picture start or a picture segment - WebRtc_UWord8 vrc = header1 & 0x02; // Video Redundancy Coding (VRC) - WebRtc_UWord8 pLen = ((header1 & 0x01) << 5) + ((header2 >> 3) & 0x1f); // Length, in bytes, of the extra picture header - //WebRtc_UWord8 peBit = (header2 & 0x07); // number of bits that shall be ignored in the last byte of the extra picture header - - if (vrc) - { - return false; - } - if (pLen > 0) - { - h2631998HeaderLength += pLen; - //get extra header - - // IMPROVEMENT we don't use the redundant picture header - } - - if (_dataLength <= h2631998HeaderLength) - { - // Received empty H263 (1998) packet - return false; - } - // if p == 0 - // it's a follow-on packet, hence it's not independently decodable - - const bool isH263PictureStartCode = H263PictureStartCode(_dataPtr + h2631998HeaderLength, (p>0)?true:false); - if (isH263PictureStartCode) - { - // parse out real size and inform the decoder - WebRtc_UWord16 width = 0; - WebRtc_UWord16 height = 0; - - if(p) - { - parsedPacket.frameType = GetH263FrameType(_dataPtr + h2631998HeaderLength - 2); - GetH263FrameSize(_dataPtr + h2631998HeaderLength - 2, width, height); - } - - parsedPacket.info.H263.hasPictureStartCode = true; - parsedPacket.info.H263.frameWidth = width; - parsedPacket.info.H263.frameHeight = height; - - } - parsedPacket.info.H263.hasPbit = (p>0)?true:false; - parsedPacket.info.H263.insert2byteStartCode = (p>0)?true:false; - parsedPacket.info.H263.data = _dataPtr + h2631998HeaderLength; - parsedPacket.info.H263.dataLength = _dataLength - h2631998HeaderLength; - return true; -} - -bool -ModuleRTPUtility::RTPPayloadParser::ParseMPEG4( - RTPPayload& parsedPacket) const -{ - if (_dataLength <= 5) - { - // Received empty MPEG4 packet - return false; - } - - parsedPacket.frameType = kPFrame; - - if (_dataPtr[0] == 0 && _dataPtr[1] == 0 && _dataPtr[2] == 1) - { - parsedPacket.info.MPEG4.isFirstPacket = true; - if (!(_dataPtr[4] & 0x40)) - { - parsedPacket.frameType = kIFrame; - } - } - - parsedPacket.info.MPEG4.data = _dataPtr; - parsedPacket.info.MPEG4.dataLength = _dataLength; - - return true; -} - -bool -ModuleRTPUtility::RTPPayloadParser::ParseVP8(RTPPayload& parsedPacket) const -{ - parsedPacket.info.VP8.hasPictureID = (_dataPtr[0] & 0x10)?true:false; - parsedPacket.info.VP8.nonReferenceFrame = (_dataPtr[0] & 0x08)?true:false; - parsedPacket.info.VP8.fragments = (_dataPtr[0] & 0x06)?true:false; - parsedPacket.info.VP8.beginningOfFrame = (_dataPtr[0] & 0x01)?true:false; - - if(parsedPacket.info.VP8.fragments) - { - WebRtc_UWord8 fragments = (_dataPtr[0] >> 1) & 0x03; - if( fragments == 1) - { - parsedPacket.info.VP8.startFragment = true; - parsedPacket.info.VP8.stopFragment = false; - } else if(fragments == 3) - { - parsedPacket.info.VP8.startFragment = false; - parsedPacket.info.VP8.stopFragment = true; - } else - { - parsedPacket.info.VP8.startFragment = false; - parsedPacket.info.VP8.stopFragment = false; - } - } else - { - parsedPacket.info.VP8.startFragment = true; - parsedPacket.info.VP8.stopFragment = true; - } - if(parsedPacket.info.VP8.hasPictureID) - { - WebRtc_UWord8 numBytesPictureId = 1; - while(_dataPtr[numBytesPictureId] & 0x80) - { - numBytesPictureId++; - } - - parsedPacket.frameType = (_dataPtr[1+numBytesPictureId] & 0x01) ? kPFrame : kIFrame; // first bit after picture id - - if(!parsedPacket.info.VP8.startFragment) - { - // if not start fragment parse away all picture IDs - parsedPacket.info.VP8.hasPictureID = false; - parsedPacket.info.VP8.data = _dataPtr+numBytesPictureId; - parsedPacket.info.VP8.dataLength = _dataLength-numBytesPictureId; - return true; - } - } else - { - parsedPacket.frameType = (_dataPtr[1] & 0x01) ? kPFrame : kIFrame; // first bit after picture id - } - parsedPacket.info.VP8.data = _dataPtr+1; - parsedPacket.info.VP8.dataLength = _dataLength-1; - - return true; -} - -bool -ModuleRTPUtility::RTPPayloadParser::H263PictureStartCode(const WebRtc_UWord8* data, const bool skipFirst2bytes) const -{ - // data is at least 3 bytes! - - if(skipFirst2bytes) - { - const WebRtc_UWord8 h3 = *(data); - if((h3 & 0x7C) == 0 && (h3 & 0x80)) - { - return true; - } - }else - { - // first part of the frame - const WebRtc_UWord8 h1 = *(data); - const WebRtc_UWord8 h2 = *(data+1); - const WebRtc_UWord8 h3 = *(data+2); - if(h1 == 0 && h2 == 0 && (h3 & 0x7C) == 0 && (h3 & 0x80)) - { - return true; - } - } - - return false; -} - -void -ModuleRTPUtility::RTPPayloadParser::GetH263FrameSize(const WebRtc_UWord8* inputVideoBuffer, - WebRtc_UWord16& width, - WebRtc_UWord16& height) const -{ - WebRtc_UWord8 uiH263PTypeFmt = (inputVideoBuffer[4] >> 2) & 0x07; - if (uiH263PTypeFmt == 7) //extended PTYPE (for QQVGA, QVGA, VGA) - { - const WebRtc_UWord8 uiH263PlusPTypeUFEP = ((inputVideoBuffer[4] & 0x03) << 1) + ((inputVideoBuffer[5] >> 7) & 0x01); - if (uiH263PlusPTypeUFEP == 1) //optional part included - { - WebRtc_UWord8 uiH263PlusPTypeFmt = (inputVideoBuffer[5] >> 4) & 0x07; - if(uiH263PlusPTypeFmt == 6) //custom picture format - { - const WebRtc_UWord16 uiH263PlusPTypeCPFmt_PWI = ((inputVideoBuffer[9] & 0x7F) << 2) + ((inputVideoBuffer[10] >> 6) & 0x03); - const WebRtc_UWord16 uiH263PlusPTypeCPFmt_PHI = ((inputVideoBuffer[10] & 0x1F) << 4) + ((inputVideoBuffer[11] >> 4) & 0x0F); - width = (uiH263PlusPTypeCPFmt_PWI + 1)*4; - width = uiH263PlusPTypeCPFmt_PHI*4; - } - else - { - switch (uiH263PlusPTypeFmt) - { - case 1: // SQCIF - width = 128; - height = 96; - break; - case 2: // QCIF - width = 176; - height = 144; - break; - case 3: // CIF - width = 352; - height = 288; - break; - case 4: // 4CIF - width = 704; - height = 576; - break; - case 5: // 16CIF - width = 1408; - height = 1152; - break; - default: - assert(false); - break; - } - } - } - } - else - { - switch (uiH263PTypeFmt) - { - case 1: // SQCIF - width = 128; - height = 96; - break; - case 2: // QCIF - width = 176; - height = 144; - break; - case 3: // CIF - width = 352; - height = 288; - break; - case 4: // 4CIF - width = 704; - height = 576; - break; - case 5: // 16CIF - width = 1408; - height = 1152; - break; - default: - assert(false); - break; - } - } -} - -ModuleRTPUtility::FrameTypes -ModuleRTPUtility::RTPPayloadParser::GetH263FrameType( - const WebRtc_UWord8* inputVideoBuffer) const -{ - FrameTypes frameType = kPFrame; - const WebRtc_UWord8 uiH263PTypeFmt = (inputVideoBuffer[4] >> 2) & 0x07; - WebRtc_UWord8 pType = 1; - if (uiH263PTypeFmt != 7) - { - pType = (inputVideoBuffer[4] >> 1) & 0x01; - } - else - { - const WebRtc_UWord8 uiH263PlusPTypeUFEP = ((inputVideoBuffer[4] & 0x03) << 1) + ((inputVideoBuffer[5] >> 7) & 0x01); - if (uiH263PlusPTypeUFEP == 1) - { - pType = ((inputVideoBuffer[7] >> 2) & 0x07); - } - else if (uiH263PlusPTypeUFEP == 0) - { - pType = ((inputVideoBuffer[5] >> 4) & 0x07); - } - } - - if (pType == 0) - { - frameType = kIFrame; - } - - return frameType; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_utility.h b/modules/rtp_rtcp/source/rtp_utility.h deleted file mode 100644 index e44bf846f..000000000 --- a/modules/rtp_rtcp/source/rtp_utility.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_UTILITY_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_UTILITY_H_ - -#include // size_t, ptrdiff_t - -#include "typedefs.h" -#include "rtp_rtcp_config.h" -#include "rtp_rtcp_defines.h" - -namespace webrtc { -enum RtpVideoCodecTypes -{ - kRtpNoVideo = 0, - kRtpH263Video = 1, - kRtpH2631998Video = 2, - kRtpMpeg4Video = 5, - kRtpFecVideo = 10, - kRtpVp8Video = 11 -}; - -const WebRtc_UWord8 kRtpMarkerBitMask = 0x80; - -namespace ModuleRTPUtility -{ - struct AudioPayload - { - WebRtc_UWord32 frequency; - WebRtc_UWord8 channels; - WebRtc_UWord8 bitsPerSample; - WebRtc_UWord32 rate; - }; - struct VideoPayload - { - RtpVideoCodecTypes videoCodecType; - WebRtc_UWord32 maxRate; - }; - union PayloadUnion - { - AudioPayload Audio; - VideoPayload Video; - }; - struct Payload - { - WebRtc_Word8 name[RTP_PAYLOAD_NAME_SIZE]; - bool audio; - PayloadUnion typeSpecific; - }; - - WebRtc_Word32 CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) ; - - WebRtc_UWord32 CurrentRTP(WebRtc_UWord32 freq); - - WebRtc_UWord32 pow2(WebRtc_UWord8 exp); - - WebRtc_UWord32 GetTimeInMS(); - - WebRtc_UWord32 ConvertNTPTimeToMS(WebRtc_UWord32 NTPsec, WebRtc_UWord32 NTPfrac); - - bool StringCompare(const WebRtc_Word8* str1 , const WebRtc_Word8* str2, const WebRtc_UWord32 length); - - void AssignUWord32ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord32 value); - void AssignUWord24ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord32 value); - void AssignUWord16ToBuffer(WebRtc_UWord8* dataBuffer, WebRtc_UWord16 value); - - /** - * Converts a network-ordered two-byte input buffer to a host-ordered value. - * \param[in] dataBuffer Network-ordered two-byte buffer to convert. - * \return Host-ordered value. - */ - WebRtc_UWord16 BufferToUWord16(const WebRtc_UWord8* dataBuffer); - - /** - * Converts a network-ordered three-byte input buffer to a host-ordered value. - * \param[in] dataBuffer Network-ordered three-byte buffer to convert. - * \return Host-ordered value. - */ - WebRtc_UWord32 BufferToUWord24(const WebRtc_UWord8* dataBuffer); - - /** - * Converts a network-ordered four-byte input buffer to a host-ordered value. - * \param[in] dataBuffer Network-ordered four-byte buffer to convert. - * \return Host-ordered value. - */ - WebRtc_UWord32 BufferToUWord32(const WebRtc_UWord8* dataBuffer); - - class RTPHeaderParser - { - public: - RTPHeaderParser(const WebRtc_UWord8* rtpData, - const WebRtc_UWord32 rtpDataLength); - ~RTPHeaderParser(); - - bool RTCP( ) const; - bool Parse( WebRtcRTPHeader& parsedPacket) const; - - private: - const WebRtc_UWord8* const _ptrRTPDataBegin; - const WebRtc_UWord8* const _ptrRTPDataEnd; - }; - - enum FrameTypes - { - kIFrame, // key frame - kPFrame // Delta frame - }; - - struct RTPPayloadH263 - { - // H.263 and H.263+ - bool hasPictureStartCode; - bool insert2byteStartCode; - bool hasPbit; - WebRtc_UWord16 frameWidth; - WebRtc_UWord16 frameHeight; - - WebRtc_UWord8 endBits; // ignore last end bits - WebRtc_UWord8 startBits; // ignore first bits - - const WebRtc_UWord8* data; - WebRtc_UWord16 dataLength; - }; - - struct RTPPayloadMPEG4 - { - // MPEG4 - bool isFirstPacket; - const WebRtc_UWord8* data; - WebRtc_UWord16 dataLength; - }; - struct RTPPayloadVP8 - { - bool beginningOfFrame; - bool nonReferenceFrame; - bool hasPictureID; - bool fragments; - bool startFragment; - bool stopFragment; - - const WebRtc_UWord8* data; - WebRtc_UWord16 dataLength; - }; - - union RTPPayloadUnion - { - RTPPayloadH263 H263; - RTPPayloadMPEG4 MPEG4; - RTPPayloadVP8 VP8; - }; - - struct RTPPayload - { - void SetType(RtpVideoCodecTypes videoType); - - RtpVideoCodecTypes type; - FrameTypes frameType; - RTPPayloadUnion info; - }; - - // RTP payload parser - class RTPPayloadParser - { - public: - RTPPayloadParser(const RtpVideoCodecTypes payloadType, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadDataLength); // Length w/o padding. - - ~RTPPayloadParser(); - - bool Parse(RTPPayload& parsedPacket) const; - - private: - bool ParseGeneric(RTPPayload& parsedPacket) const; - - bool ParseH263(RTPPayload& parsedPacket) const; - bool ParseH2631998(RTPPayload& parsedPacket) const; - - bool ParseMPEG4(RTPPayload& parsedPacket) const; - - bool ParseVP8(RTPPayload& parsedPacket) const; - - // H.263 - bool H263PictureStartCode(const WebRtc_UWord8* data, - const bool skipFirst2bytes = false) const; - - void GetH263FrameSize(const WebRtc_UWord8* inputVideoBuffer, - WebRtc_UWord16& width, - WebRtc_UWord16& height) const; - - FrameTypes GetH263FrameType(const WebRtc_UWord8* inputVideoBuffer) const; - - private: - const WebRtc_UWord8* _dataPtr; - const WebRtc_UWord16 _dataLength; - const RtpVideoCodecTypes _videoType; - }; -} -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_UTILITY_H_ diff --git a/modules/rtp_rtcp/source/ssrc_database.cc b/modules/rtp_rtcp/source/ssrc_database.cc deleted file mode 100644 index e69e5d869..000000000 --- a/modules/rtp_rtcp/source/ssrc_database.cc +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ssrc_database.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -#include -#include - -#ifdef _WIN32 - #include - #include //timeGetTime - - #pragma warning(disable:4311) - #pragma warning(disable:4312) - - // Platform SDK fixes when building with /Wp64 for a 32 bits target. - #if !defined(_WIN64) && defined(_Wp64) - #ifdef InterlockedExchangePointer - #undef InterlockedExchangePointer - // The problem is that the macro provided for InterlockedExchangePointer() is - // doing a (LONG) C-style cast that triggers invariably the warning C4312 when - // building on 32 bits. - inline void* InterlockedExchangePointer(void* volatile* target, void* value) - { - return reinterpret_cast(static_cast(InterlockedExchange( - reinterpret_cast(target), - static_cast(reinterpret_cast(value))))); - } - #endif // #ifdef InterlockedExchangePointer - #endif //!defined(_WIN64) && defined(_Wp64) - -#else - #include - #include - #include - #include - #ifndef WEBRTC_NO_AUTO_PTR // are we allowed to use auto_ptrs? - #include // definition of auto_ptr - #endif -#endif - -namespace webrtc { -// Construct On First Use idiom. Avoids "static initialization order fiasco" (JFGI). -SSRCDatabase*& -SSRCDatabase::StaticInstance(SsrcDatabaseCount inc) -{ - static volatile long theSSRCDatabaseCount = 0; // this needs to be long due to Windows, not an issue due to its usage - static SSRCDatabase* theSSRCDatabase = NULL; - - SsrcDatabaseCreate state = kSsrcDbExist; - -#ifndef _WIN32 -#ifdef WEBRTC_NO_AUTO_PTR - // since we only have InterlockedExchange on windows and no auto_ptrs, this will result in a memory leak but we accept it for now - static CriticalSectionWrapper* crtiSect(CriticalSectionWrapper::CreateCriticalSection()); - CriticalSectionScoped lock(*crtiSect); -#else - static std::auto_ptr crtiSect = std::auto_ptr(CriticalSectionWrapper::CreateCriticalSection()); - CriticalSectionScoped lock(*crtiSect); -#endif - - if(inc == kSsrcDbInc) - { - theSSRCDatabaseCount++; - if(theSSRCDatabaseCount == 1) - { - state = kSsrcDbCreate; - } - } else - { - theSSRCDatabaseCount--; - if(theSSRCDatabaseCount == 0) - { - state = kSsrcDbDestroy; - } - } - if(state == kSsrcDbCreate) - { - theSSRCDatabase = new SSRCDatabase(); - - }else if(state == kSsrcDbDestroy) - { - SSRCDatabase* oldValue = theSSRCDatabase; - theSSRCDatabase = NULL; - if(oldValue) - { - delete oldValue; - } - return theSSRCDatabase; - } -#else - // Windows - if(inc == kSsrcDbInc) - { - if(1 == InterlockedIncrement(&theSSRCDatabaseCount)) - { - state = kSsrcDbCreate; - } - } else - { - int newValue = InterlockedDecrement(&theSSRCDatabaseCount); - if(newValue == 0) - { - state = kSsrcDbDestroy; - } - } - if(state == kSsrcDbCreate) - { - SSRCDatabase* newValue = new SSRCDatabase(); - SSRCDatabase* oldValue = (SSRCDatabase*)InterlockedExchangePointer(reinterpret_cast(&theSSRCDatabase), newValue); - assert(oldValue == NULL); - - }else if(state == kSsrcDbDestroy) - { - SSRCDatabase* oldValue = (SSRCDatabase*)InterlockedExchangePointer(reinterpret_cast(&theSSRCDatabase), NULL); - if(oldValue) - { - delete oldValue; - } - return theSSRCDatabase; - } -#endif - assert(theSSRCDatabase); - return theSSRCDatabase; -} - -SSRCDatabase* -SSRCDatabase::GetSSRCDatabase() -{ - return StaticInstance(kSsrcDbInc); -} - -void -SSRCDatabase::ReturnSSRCDatabase() -{ - StaticInstance(kSsrcDbDec); -} - -WebRtc_UWord32 -SSRCDatabase::CreateSSRC() -{ - CriticalSectionScoped lock(*_critSect); - - WebRtc_UWord32 ssrc = GenerateRandom(); - -#ifndef WEBRTC_NO_STL - - while(_ssrcMap.find(ssrc) != _ssrcMap.end()) - { - ssrc = GenerateRandom(); - } - _ssrcMap[ssrc] = 0; - -#else - if(_sizeOfSSRC <= _numberOfSSRC) - { - // allocate more space - const int newSize = _sizeOfSSRC + 10; - WebRtc_UWord32* tempSSRCVector = new WebRtc_UWord32[newSize]; - memcpy(tempSSRCVector, _ssrcVector, _sizeOfSSRC*sizeof(WebRtc_UWord32)); - delete [] _ssrcVector; - - _ssrcVector = tempSSRCVector; - _sizeOfSSRC = newSize; - } - - // check if in DB - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - i = 0; // start over with a new ssrc - ssrc = GenerateRandom(); - } - - } - // add to database - _ssrcVector[_numberOfSSRC] = ssrc; - _numberOfSSRC++; - } -#endif - return ssrc; -} - -WebRtc_Word32 -SSRCDatabase::RegisterSSRC(const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(*_critSect); - -#ifndef WEBRTC_NO_STL - - _ssrcMap[ssrc] = 0; - -#else - if(_sizeOfSSRC <= _numberOfSSRC) - { - // allocate more space - const int newSize = _sizeOfSSRC + 10; - WebRtc_UWord32* tempSSRCVector = new WebRtc_UWord32[newSize]; - memcpy(tempSSRCVector, _ssrcVector, _sizeOfSSRC*sizeof(WebRtc_UWord32)); - delete [] _ssrcVector; - - _ssrcVector = tempSSRCVector; - _sizeOfSSRC = newSize; - } - // check if in DB - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - return -1; - } - } - // add to database - _ssrcVector[_numberOfSSRC] = ssrc; - _numberOfSSRC++; - } -#endif - return 0; -} - -WebRtc_Word32 -SSRCDatabase::ReturnSSRC(const WebRtc_UWord32 ssrc) -{ - CriticalSectionScoped lock(*_critSect); - -#ifndef WEBRTC_NO_STL - _ssrcMap.erase(ssrc); - -#else - if(_ssrcVector) - { - for (int i=0; i<_numberOfSSRC; i++) - { - if (_ssrcVector[i] == ssrc) - { - // we have a match - // remove from database - _ssrcVector[i] = _ssrcVector[_numberOfSSRC-1]; - _numberOfSSRC--; - break; - } - } - } -#endif - return 0; -} - -SSRCDatabase::SSRCDatabase() -{ - // we need to seed the random generator, otherwise we get 26500 each time, hardly a random value :) -#ifdef _WIN32 - srand(timeGetTime()); -#else - struct timeval tv; - struct timezone tz; - gettimeofday(&tv, &tz); - srand(tv.tv_usec); -#endif - -#ifdef WEBRTC_NO_STL - _sizeOfSSRC = 10; - _numberOfSSRC = 0; - _ssrcVector = new WebRtc_UWord32[10]; -#endif - _critSect = CriticalSectionWrapper::CreateCriticalSection(); - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, -1, "%s created", __FUNCTION__); -} - -SSRCDatabase::~SSRCDatabase() -{ -#ifdef WEBRTC_NO_STL - delete [] _ssrcVector; -#else - _ssrcMap.clear(); -#endif - delete _critSect; - - WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, -1, "%s deleted", __FUNCTION__); -} - -WebRtc_UWord32 SSRCDatabase::GenerateRandom() -{ - WebRtc_UWord32 ssrc = 0; - do - { - ssrc = rand(); - ssrc = ssrc <<16; - ssrc += rand(); - - } while (ssrc == 0 || ssrc == 0xffffffff); - - return ssrc; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/ssrc_database.h b/modules/rtp_rtcp/source/ssrc_database.h deleted file mode 100644 index 73ad21845..000000000 --- a/modules/rtp_rtcp/source/ssrc_database.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_SSRC_DATABASE_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_SSRC_DATABASE_H_ - -#include "typedefs.h" - -#ifndef WEBRTC_NO_STL - #include -#endif - -namespace webrtc { -class CriticalSectionWrapper; - -class SSRCDatabase -{ - enum SsrcDatabaseCount - { - kSsrcDbDec = 0, - kSsrcDbInc = 1 - }; - - enum SsrcDatabaseCreate - { - kSsrcDbExist = 0, - kSsrcDbCreate = 1, - kSsrcDbDestroy = 2 - }; - -public: - static SSRCDatabase* GetSSRCDatabase(); - static void ReturnSSRCDatabase(); - - WebRtc_UWord32 CreateSSRC(); - WebRtc_Word32 RegisterSSRC(const WebRtc_UWord32 ssrc); - WebRtc_Word32 ReturnSSRC(const WebRtc_UWord32 ssrc); - -private: - static SSRCDatabase*& StaticInstance(SsrcDatabaseCount inc); - -private: - SSRCDatabase(); - virtual ~SSRCDatabase(); - - WebRtc_UWord32 GenerateRandom(); - -#ifdef WEBRTC_NO_STL - int _numberOfSSRC; - int _sizeOfSSRC; - - WebRtc_UWord32* _ssrcVector; -#else - std::map _ssrcMap; -#endif - - CriticalSectionWrapper* _critSect; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_SSRC_DATABASE_H_ diff --git a/modules/rtp_rtcp/source/tmmbr_help.cc b/modules/rtp_rtcp/source/tmmbr_help.cc deleted file mode 100644 index 43537f831..000000000 --- a/modules/rtp_rtcp/source/tmmbr_help.cc +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tmmbr_help.h" - -#include "rtp_rtcp_config.h" - -namespace webrtc { -TMMBRSet::TMMBRSet() : - ptrTmmbrSet(0), - ptrPacketOHSet(0), - ptrSsrcSet(0), - sizeOfSet(0), - lengthOfSet(0) -{ -} - -TMMBRSet::~TMMBRSet() -{ - delete [] ptrTmmbrSet; - delete [] ptrPacketOHSet; - delete [] ptrSsrcSet; - ptrTmmbrSet = 0; - ptrPacketOHSet = 0; - ptrSsrcSet = 0; - sizeOfSet = 0; - lengthOfSet = 0; -} - -void -TMMBRSet::VerifyAndAllocateSet(WebRtc_UWord32 minimumSize) -{ - if(minimumSize > sizeOfSet) - { - // make sure that our buffers are big enough - if(ptrTmmbrSet) - { - delete [] ptrTmmbrSet; - delete [] ptrPacketOHSet; - delete [] ptrSsrcSet; - } - ptrTmmbrSet = new WebRtc_UWord32[minimumSize]; - ptrPacketOHSet = new WebRtc_UWord32[minimumSize]; - ptrSsrcSet = new WebRtc_UWord32[minimumSize]; - sizeOfSet = minimumSize; - } - // reset memory - for(WebRtc_UWord32 i = 0; i < sizeOfSet; i++) - { - ptrTmmbrSet[i] = 0; - ptrPacketOHSet[i] = 0; - ptrSsrcSet[i] = 0; - } - lengthOfSet = 0; -} - -TMMBRHelp::TMMBRHelp(const bool audio) : - _criticalSection(*CriticalSectionWrapper::CreateCriticalSection()), - _audio(audio), - _candidateSet(), - _boundingSet(), - _boundingSetToSend(), - _ptrIntersectionBoundingSet(NULL), - _ptrMaxPRBoundingSet(NULL) -{ -} - -TMMBRHelp::~TMMBRHelp() -{ - delete [] _ptrIntersectionBoundingSet; - delete [] _ptrMaxPRBoundingSet; - _ptrIntersectionBoundingSet = 0; - _ptrMaxPRBoundingSet = 0; - delete &_criticalSection; -} - -TMMBRSet* -TMMBRHelp::VerifyAndAllocateBoundingSet(WebRtc_UWord32 minimumSize) -{ - CriticalSectionScoped lock(_criticalSection); - - if(minimumSize > _boundingSet.sizeOfSet) - { - // make sure that our buffers are big enough - if(_ptrIntersectionBoundingSet) - { - delete [] _ptrIntersectionBoundingSet; - delete [] _ptrMaxPRBoundingSet; - } - _ptrIntersectionBoundingSet = new float[minimumSize]; - _ptrMaxPRBoundingSet = new float[minimumSize]; - } - _boundingSet.VerifyAndAllocateSet(minimumSize); - return &_boundingSet; -} - -TMMBRSet* -TMMBRHelp::BoundingSet() -{ - return &_boundingSet; -} - -WebRtc_Word32 -TMMBRHelp::SetTMMBRBoundingSetToSend(const TMMBRSet* boundingSetToSend, - const WebRtc_UWord32 maxBitrateKbit) -{ - CriticalSectionScoped lock(_criticalSection); - - if (boundingSetToSend == NULL) - { - _boundingSetToSend.lengthOfSet = 0; - return 0; - } - - VerifyAndAllocateBoundingSetToSend(boundingSetToSend->lengthOfSet); - - for (WebRtc_UWord32 i = 0; i < boundingSetToSend->lengthOfSet; i++) - { - // cap at our configured max bitrate - WebRtc_UWord32 bitrate = boundingSetToSend->ptrTmmbrSet[i]; - if(maxBitrateKbit) - { - // do we have a configured max bitrate? - if(bitrate > maxBitrateKbit) - { - bitrate = maxBitrateKbit; - } - } - - _boundingSetToSend.ptrTmmbrSet[i] = bitrate; - _boundingSetToSend.ptrPacketOHSet[i] = boundingSetToSend->ptrPacketOHSet[i]; - _boundingSetToSend.ptrSsrcSet[i] = boundingSetToSend->ptrSsrcSet[i]; - } - _boundingSetToSend.lengthOfSet = boundingSetToSend->lengthOfSet; - return 0; -} - -WebRtc_Word32 -TMMBRHelp::VerifyAndAllocateBoundingSetToSend(WebRtc_UWord32 minimumSize) -{ - CriticalSectionScoped lock(_criticalSection); - - _boundingSetToSend.VerifyAndAllocateSet(minimumSize); - return 0; -} - -TMMBRSet* -TMMBRHelp::VerifyAndAllocateCandidateSet(WebRtc_UWord32 minimumSize) -{ - CriticalSectionScoped lock(_criticalSection); - - _candidateSet.VerifyAndAllocateSet(minimumSize); - return &_candidateSet; -} - -TMMBRSet* -TMMBRHelp::CandidateSet() -{ - return &_candidateSet; -} - -TMMBRSet* -TMMBRHelp::BoundingSetToSend() -{ - return &_boundingSetToSend; -} - -WebRtc_Word32 -TMMBRHelp::FindTMMBRBoundingSet(TMMBRSet*& boundingSet) -{ - CriticalSectionScoped lock(_criticalSection); - - // Work on local variable, will be modified - TMMBRSet candidateSet; - candidateSet.VerifyAndAllocateSet(_candidateSet.sizeOfSet); - - // Number of set candidates - WebRtc_Word32 numSetCandidates = 0; - for (WebRtc_UWord32 i = 0; i < _candidateSet.sizeOfSet; i++) - { - if(_candidateSet.ptrTmmbrSet[i]) - { - numSetCandidates++; - candidateSet.ptrTmmbrSet[i] = _candidateSet.ptrTmmbrSet[i]; - candidateSet.ptrPacketOHSet[i] = _candidateSet.ptrPacketOHSet[i]; - candidateSet.ptrSsrcSet[i] = _candidateSet.ptrSsrcSet[i]; - } - else - { - // make sure this is zero if tmmbr = 0 - _candidateSet.ptrPacketOHSet[i] = 0; - } - } - candidateSet.lengthOfSet = numSetCandidates; - - // Find bounding set - WebRtc_UWord32 numBoundingSet = 0; - if (numSetCandidates > 0) - { - numBoundingSet = FindTMMBRBoundingSet(numSetCandidates, candidateSet); - if(numBoundingSet < 1 || (numBoundingSet > _candidateSet.sizeOfSet)) - { - return -1; - } - boundingSet = &_boundingSet; - } - return numBoundingSet; -} - - -WebRtc_Word32 -TMMBRHelp::FindTMMBRBoundingSet(WebRtc_Word32 numCandidates, TMMBRSet& candidateSet) -{ - CriticalSectionScoped lock(_criticalSection); - - WebRtc_UWord32 numBoundingSet = 0; - VerifyAndAllocateBoundingSet(candidateSet.sizeOfSet); - - if (numCandidates == 1) - { - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if(candidateSet.ptrTmmbrSet[i] > 0) - { - _boundingSet.ptrTmmbrSet[numBoundingSet] = candidateSet.ptrTmmbrSet[i]; - _boundingSet.ptrPacketOHSet[numBoundingSet] = candidateSet.ptrPacketOHSet[i]; - _boundingSet.ptrSsrcSet[numBoundingSet] = candidateSet.ptrSsrcSet[i]; - numBoundingSet++; - } - } - if (numBoundingSet != 1) - { - numBoundingSet = -1; - } - } else - { - // 1. Sort by increasing packetOH - WebRtc_UWord32 temp; - for (int i = candidateSet.sizeOfSet - 1; i >= 0; i--) - { - for (int j = 1; j <= i; j++) - { - if (candidateSet.ptrPacketOHSet[j-1] > candidateSet.ptrPacketOHSet[j]) - { - temp = candidateSet.ptrPacketOHSet[j-1]; - candidateSet.ptrPacketOHSet[j-1] = candidateSet.ptrPacketOHSet[j]; - candidateSet.ptrPacketOHSet[j] = temp; - temp = candidateSet.ptrTmmbrSet[j-1]; - candidateSet.ptrTmmbrSet[j-1] = candidateSet.ptrTmmbrSet[j]; - candidateSet.ptrTmmbrSet[j] = temp; - temp = candidateSet.ptrSsrcSet[j-1]; - candidateSet.ptrSsrcSet[j-1] = candidateSet.ptrSsrcSet[j]; - candidateSet.ptrSsrcSet[j] = temp; - } - } - } - // 2. For tuples with same OH, keep the one w/ the lowest bitrate - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if (candidateSet.ptrTmmbrSet[i] > 0) - { - // get min bitrate for packets w/ same OH - WebRtc_UWord32 currentPacketOH = candidateSet.ptrPacketOHSet[i]; - WebRtc_UWord32 currentMinTMMBR = candidateSet.ptrTmmbrSet[i]; - WebRtc_UWord32 currentMinIndexTMMBR = i; - for (WebRtc_UWord32 j = i+1; j < candidateSet.sizeOfSet; j++) - { - if(candidateSet.ptrPacketOHSet[j] == currentPacketOH) - { - if(candidateSet.ptrTmmbrSet[j] < currentMinTMMBR) - { - currentMinTMMBR = candidateSet.ptrTmmbrSet[j]; - currentMinIndexTMMBR = j; - } - } - } - // keep lowest bitrate - for (WebRtc_UWord32 j = 0; j < candidateSet.sizeOfSet; j++) - { - if(candidateSet.ptrPacketOHSet[j] == currentPacketOH && j != currentMinIndexTMMBR) - { - candidateSet.ptrTmmbrSet[j] = 0; - candidateSet.ptrPacketOHSet[j] = 0; - candidateSet.ptrSsrcSet[j] = 0; - numCandidates--; - } - } - } - } - // 3. Select and remove tuple w/ lowest tmmbr. (If more than 1, choose the one w/ highest OH). - WebRtc_UWord32 minTMMBR = 0; - WebRtc_UWord32 minIndexTMMBR = 0; - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if (candidateSet.ptrTmmbrSet[i] > 0) - { - minTMMBR = candidateSet.ptrTmmbrSet[i]; - minIndexTMMBR = i; - break; - } - } - - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if (candidateSet.ptrTmmbrSet[i] > 0 && candidateSet.ptrTmmbrSet[i] <= minTMMBR) - { - // get min bitrate - minTMMBR = candidateSet.ptrTmmbrSet[i]; - minIndexTMMBR = i; - } - } - // first member of selected list - _boundingSet.ptrTmmbrSet[numBoundingSet] = candidateSet.ptrTmmbrSet[minIndexTMMBR]; - _boundingSet.ptrPacketOHSet[numBoundingSet] = candidateSet.ptrPacketOHSet[minIndexTMMBR]; - _boundingSet.ptrSsrcSet[numBoundingSet] = candidateSet.ptrSsrcSet[minIndexTMMBR]; - // set intersection value - _ptrIntersectionBoundingSet[numBoundingSet] = 0; - // calculate its maximum packet rate (where its line crosses x-axis) - _ptrMaxPRBoundingSet[numBoundingSet] = _boundingSet.ptrTmmbrSet[numBoundingSet]*1000 / float(8*_boundingSet.ptrPacketOHSet[numBoundingSet]); - numBoundingSet++; - // remove from candidate list - candidateSet.ptrTmmbrSet[minIndexTMMBR] = 0; - candidateSet.ptrPacketOHSet[minIndexTMMBR] = 0; - candidateSet.ptrSsrcSet[minIndexTMMBR] = 0; - numCandidates--; - - // 4. Discard from candidate list all tuple w/ lower OH (next tuple must be steeper) - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if(candidateSet.ptrTmmbrSet[i] > 0 && candidateSet.ptrPacketOHSet[i] < _boundingSet.ptrPacketOHSet[0]) - { - candidateSet.ptrTmmbrSet[i] = 0; - candidateSet.ptrPacketOHSet[i] = 0; - candidateSet.ptrSsrcSet[i] = 0; - numCandidates--; - } - } - - if (numCandidates == 0) - { - _boundingSet.lengthOfSet = numBoundingSet; - return numBoundingSet; - } - - bool getNewCandidate = true; - int curCandidateTMMBR = 0; - int curCandidateIndex = 0; - int curCandidatePacketOH = 0; - int curCandidateSSRC = 0; - do - { - if (getNewCandidate) - { - // 5. Remove first remaining tuple from candidate list - for (WebRtc_UWord32 i = 0; i < candidateSet.sizeOfSet; i++) - { - if (candidateSet.ptrTmmbrSet[i] > 0) - { - curCandidateTMMBR = candidateSet.ptrTmmbrSet[i]; - curCandidatePacketOH = candidateSet.ptrPacketOHSet[i]; - curCandidateSSRC = candidateSet.ptrSsrcSet[i]; - curCandidateIndex = i; - candidateSet.ptrTmmbrSet[curCandidateIndex] = 0; - candidateSet.ptrPacketOHSet[curCandidateIndex] = 0; - candidateSet.ptrSsrcSet[curCandidateIndex] = 0; - break; - } - } - } - - // 6. Calculate packet rate and intersection of the current line with line of last tuple in selected list - float packetRate = float(curCandidateTMMBR - _boundingSet.ptrTmmbrSet[numBoundingSet-1])*1000 / (8*(curCandidatePacketOH - _boundingSet.ptrPacketOHSet[numBoundingSet-1])); - - // 7. If the packet rate is equal or lower than intersection of last tuple in selected list, - // remove last tuple in selected list & go back to step 6 - if(packetRate <= _ptrIntersectionBoundingSet[numBoundingSet-1]) - { - // remove last tuple and goto step 6 - numBoundingSet--; - _boundingSet.ptrTmmbrSet[numBoundingSet] = 0; - _boundingSet.ptrPacketOHSet[numBoundingSet] = 0; - _boundingSet.ptrSsrcSet[numBoundingSet] = 0; - _ptrIntersectionBoundingSet[numBoundingSet] = 0; - _ptrMaxPRBoundingSet[numBoundingSet] = 0; - getNewCandidate = false; - } else - { - // 8. If packet rate is lower than maximum packet rate of last tuple in selected list, add current tuple to selected list - if (packetRate < _ptrMaxPRBoundingSet[numBoundingSet-1]) - { - _boundingSet.ptrTmmbrSet[numBoundingSet] = curCandidateTMMBR; - _boundingSet.ptrPacketOHSet[numBoundingSet] = curCandidatePacketOH; - _boundingSet.ptrSsrcSet[numBoundingSet] = curCandidateSSRC; - _ptrIntersectionBoundingSet[numBoundingSet] = packetRate; - _ptrMaxPRBoundingSet[numBoundingSet] = _boundingSet.ptrTmmbrSet[numBoundingSet]*1000 / float(8*_boundingSet.ptrPacketOHSet[numBoundingSet]); - numBoundingSet++; - } - numCandidates--; - getNewCandidate = true; - } - - // 9. Go back to step 5 if any tuple remains in candidate list - } while (numCandidates > 0); - } - _boundingSet.lengthOfSet = numBoundingSet; - return numBoundingSet; -} - -bool -TMMBRHelp::IsOwner(const WebRtc_UWord32 ssrc, - const WebRtc_UWord32 length) const -{ - CriticalSectionScoped lock(_criticalSection); - - if (length == 0) - { - // empty bounding set - return false; - } - - for(WebRtc_UWord32 i = 0; (i < length) && (i < _boundingSet.sizeOfSet); ++i) - { - if(_boundingSet.ptrSsrcSet[i] == ssrc) - { - return true; - } - } - return false; -} - -WebRtc_Word32 -TMMBRHelp::CalcMinMaxBitRate(const WebRtc_UWord32 totalPacketRate, - const WebRtc_UWord32 lengthOfBoundingSet, - WebRtc_UWord32& minBitrateKbit, - WebRtc_UWord32& maxBitrateKbit) const -{ - CriticalSectionScoped lock(_criticalSection); - - if (lengthOfBoundingSet <= 0 || _candidateSet.sizeOfSet == 0) - { - // empty bounding set - return -1; - } - - minBitrateKbit = 0xFFFFFFFF; - maxBitrateKbit = 0; - - for (WebRtc_UWord32 i = 0; i < _candidateSet.sizeOfSet; ++i) - { - if (_candidateSet.ptrTmmbrSet[i]) - { - WebRtc_Word32 curNetBitRate = static_cast((_candidateSet.ptrTmmbrSet[i]*1000.0 - - (totalPacketRate * (_candidateSet.ptrPacketOHSet[i] << 3)))/1000 + 0.5); - - if (curNetBitRate < 0) - { - // could be negative for a large packet rate - if(_audio) - { - curNetBitRate = MIN_AUDIO_BW_MANAGEMENT_BITRATE; - }else - { - curNetBitRate = MIN_VIDEO_BW_MANAGEMENT_BITRATE; - } - } - minBitrateKbit = (WebRtc_UWord32(curNetBitRate) < minBitrateKbit) ? curNetBitRate : minBitrateKbit; - } - } - maxBitrateKbit = minBitrateKbit; - - if (maxBitrateKbit == 0 || maxBitrateKbit < minBitrateKbit) - { - return -1; - } - - if(_audio) - { - if (minBitrateKbit < MIN_AUDIO_BW_MANAGEMENT_BITRATE) - { - minBitrateKbit = MIN_AUDIO_BW_MANAGEMENT_BITRATE; - } - if (maxBitrateKbit < MIN_AUDIO_BW_MANAGEMENT_BITRATE) - { - maxBitrateKbit = MIN_AUDIO_BW_MANAGEMENT_BITRATE; - } - }else - { - if (minBitrateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) - { - minBitrateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; - } - if (maxBitrateKbit < MIN_VIDEO_BW_MANAGEMENT_BITRATE) - { - maxBitrateKbit = MIN_VIDEO_BW_MANAGEMENT_BITRATE; - } - } - - return 0; -} -} // namespace webrtc diff --git a/modules/rtp_rtcp/source/tmmbr_help.h b/modules/rtp_rtcp/source/tmmbr_help.h deleted file mode 100644 index 0575f1d00..000000000 --- a/modules/rtp_rtcp/source/tmmbr_help.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_TMMBR_HELP_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_TMMBR_HELP_H_ - -#include "typedefs.h" - -#include "critical_section_wrapper.h" - -#ifndef NULL - #define NULL 0 -#endif - -namespace webrtc { -class TMMBRSet -{ -public: - TMMBRSet(); - ~TMMBRSet(); - - void VerifyAndAllocateSet(WebRtc_UWord32 minimumSize); - - WebRtc_UWord32* ptrTmmbrSet; - WebRtc_UWord32* ptrPacketOHSet; - WebRtc_UWord32* ptrSsrcSet; - WebRtc_UWord32 sizeOfSet; - WebRtc_UWord32 lengthOfSet; -}; - -class TMMBRHelp -{ -public: - TMMBRHelp(const bool audio); - virtual ~TMMBRHelp(); - - TMMBRSet* BoundingSet(); // used for debuging - TMMBRSet* CandidateSet(); - TMMBRSet* BoundingSetToSend(); - - TMMBRSet* VerifyAndAllocateCandidateSet(const WebRtc_UWord32 minimumSize); - WebRtc_Word32 FindTMMBRBoundingSet(TMMBRSet*& boundingSet); - WebRtc_Word32 SetTMMBRBoundingSetToSend(const TMMBRSet* boundingSetToSend, - const WebRtc_UWord32 maxBitrateKbit); - - bool IsOwner(const WebRtc_UWord32 ssrc, - const WebRtc_UWord32 length) const; - - WebRtc_Word32 CalcMinMaxBitRate(const WebRtc_UWord32 totalPacketRate, - const WebRtc_UWord32 lengthOfBoundingSet, - WebRtc_UWord32& minBitrateKbit, - WebRtc_UWord32& maxBitrateKbit) const; - -protected: - TMMBRSet* VerifyAndAllocateBoundingSet(WebRtc_UWord32 minimumSize); - WebRtc_Word32 VerifyAndAllocateBoundingSetToSend(WebRtc_UWord32 minimumSize); - - WebRtc_Word32 FindTMMBRBoundingSet(WebRtc_Word32 numCandidates, TMMBRSet& candidateSet); - -private: - CriticalSectionWrapper& _criticalSection; - const bool _audio; - TMMBRSet _candidateSet; - TMMBRSet _boundingSet; - TMMBRSet _boundingSetToSend; - - float* _ptrIntersectionBoundingSet; - float* _ptrMaxPRBoundingSet; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_TMMBR_HELP_H_ diff --git a/modules/rtp_rtcp/source/video_codec_information.h b/modules/rtp_rtcp/source/video_codec_information.h deleted file mode 100644 index 4364f0b74..000000000 --- a/modules/rtp_rtcp/source/video_codec_information.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_VIDEO_CODEC_INFORMATION_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_VIDEO_CODEC_INFORMATION_H_ - -#include "rtp_rtcp_config.h" -#include "rtp_utility.h" - -namespace webrtc { -class VideoCodecInformation -{ -public: - virtual void Reset() = 0; - - virtual RtpVideoCodecTypes Type() = 0; - virtual ~VideoCodecInformation(){}; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_VIDEO_CODEC_INFORMATION_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.cpp b/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.cpp deleted file mode 100644 index 1a55e4e65..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "BWEConvergenceTest.h" - -#include -#include - -#include "TestSenderReceiver.h" -#include "TestLoadGenerator.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "tick_util.h" - - -BWEConvergenceTestUp::BWEConvergenceTestUp(std::string testName, int startRateKbps, int availBWkbps) -: -_availBWkbps(availBWkbps), -BWEOneWayTest(testName, startRateKbps) -{ -} - - -BWEConvergenceTestUp::~BWEConvergenceTestUp() -{ - if (_gen) - { - delete _gen; - _gen = NULL; - } -} - - -int BWEConvergenceTestUp::Init(std::string ip, WebRtc_UWord16 port) -{ - // create the load generator object - const int rtpSampleRate = 90000; - const int frameRate = 30; - const double spreadFactor = 0.2; - - if (_master) - { - _gen = new CBRFixFRGenerator(_sendrec, _startRateKbps, rtpSampleRate, frameRate, spreadFactor); - if (!_gen) - { - return (-1); - } - } - - return BWEOneWayTest::Init(ip, port); -} - - -bool BWEConvergenceTestUp::StoppingCriterionMaster() -{ - return ((_sendrec->BitrateSent() / 1000.0) > (0.9 * _availBWkbps)); -} - - diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.h b/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.h deleted file mode 100644 index b830d14bd..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWEConvergenceTest.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWECONVERGENCETEST_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWECONVERGENCETEST_H_ - -#include - -#include "BWETestBase.h" - -#include "typedefs.h" - -#include "TestSenderReceiver.h" - -class BWEConvergenceTestUp : public BWEOneWayTest -{ -public: - BWEConvergenceTestUp(std::string testName, int startRateKbps, int availBWkbps); - virtual ~BWEConvergenceTestUp(); - - virtual int Init(std::string ip, WebRtc_UWord16 port); - -protected: - virtual bool StoppingCriterionMaster(); - -private: - int _availBWkbps; -}; - - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWECONVERGENCETEST_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.cpp b/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.cpp deleted file mode 100644 index 1fd19fef0..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include "BWEStabilityTest.h" -#include "TestLoadGenerator.h" -#include "tick_util.h" -#include "critical_section_wrapper.h" - - -BWEStabilityTest::BWEStabilityTest(std::string testName, int rateKbps, int testDurationSeconds) -: -_testDurationSeconds(testDurationSeconds), -BWEOneWayTest(testName, rateKbps) -{ -} - - -BWEStabilityTest::~BWEStabilityTest() -{ - if (_gen) - { - delete _gen; - _gen = NULL; - } -} - - -int BWEStabilityTest::Init(std::string ip, WebRtc_UWord16 port) -{ - // create the load generator object - const int rtpSampleRate = 90000; - const int frameRate = 30; - const double spreadFactor = 0.2; - const double keyToDeltaRatio = 7; - const int keyFramePeriod = 300; - - if (_master) - { - _gen = new CBRFixFRGenerator(_sendrec, _startRateKbps, rtpSampleRate, frameRate, spreadFactor); - //_gen = new PeriodicKeyFixFRGenerator(_sendrec, _startRateKbps, rtpSampleRate, frameRate, - // spreadFactor, keyToDeltaRatio, keyFramePeriod); - if (!_gen) - { - return (-1); - } - - } - - return BWEOneWayTest::Init(ip, port); -} - - -void BWEStabilityTest::Report(std::fstream &log) -{ - // cannot report on a running test - if(_running) return; - - BWETest::Report(log); - - CriticalSectionScoped cs(_statCritSect); - - log << "Bitrate statistics\n"; - log << "\tAverage = " << _rateVecKbps.Mean() << " kbps\n"; - log << "\tMin = " << _rateVecKbps.Min() << " kbps\n"; - log << "\tMax = " << _rateVecKbps.Max() << " kbps\n"; - log << "\tStd = " << _rateVecKbps.Std() << " kbps\n"; - -} - - -bool BWEStabilityTest::StoppingCriterionMaster() -{ - return (TickTime::MillisecondTimestamp() - _startTimeMs >= _testDurationSeconds * 1000); -} diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.h b/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.h deleted file mode 100644 index 8f213b1cf..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWEStabilityTest.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWESTABILITYTEST_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWESTABILITYTEST_H_ - -#include - -#include "BWETestBase.h" - -#include "typedefs.h" - -#include "TestSenderReceiver.h" - -class BWEStabilityTest : public BWEOneWayTest -{ -public: - BWEStabilityTest(std::string testName, int rateKbps, int testDurationSeconds); - virtual ~BWEStabilityTest(); - - virtual int Init(std::string ip, WebRtc_UWord16 port); - virtual void Report(std::fstream &log); - -protected: - virtual bool StoppingCriterionMaster(); - -private: - int _testDurationSeconds; -}; - - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWESTABILITYTEST_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc b/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc deleted file mode 100644 index 471ea5fde..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWEStandAlone.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// BWEStandAlone.cpp : Defines the entry point for the console application. -// - -#include -#include - -#include "event_wrapper.h" -#include "udp_transport.h" -#include "rtp_rtcp.h" -#include "trace.h" - -#include "TestSenderReceiver.h" -#include "TestLoadGenerator.h" - -#include "MatlabPlot.h" - -//#include "vld.h" - -class myTransportCB: public UdpTransportData -{ -public: - myTransportCB (RtpRtcp *rtpMod) : _rtpMod(rtpMod) {}; -protected: - // Inherited from UdpTransportData - virtual void IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort); - - virtual void IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort); - -private: - RtpRtcp *_rtpMod; -}; - -void myTransportCB::IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) -{ - printf("Receiving RTP from IP %s, port %u\n", fromIP, fromPort); - _rtpMod->IncomingPacket((WebRtc_UWord8 *) incomingRtpPacket, static_cast(rtpPacketLength)); -} - -void myTransportCB::IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) -{ - printf("Receiving RTCP from IP %s, port %u\n", fromIP, fromPort); - _rtpMod->IncomingPacket((WebRtc_UWord8 *) incomingRtcpPacket, static_cast(rtcpPacketLength)); -} - - -int main(int argc, char* argv[]) -{ - bool isSender = false; - bool isReceiver = false; - WebRtc_UWord16 port; - std::string ip; - TestSenderReceiver *sendrec = new TestSenderReceiver(); - TestLoadGenerator *gen; - - if (argc == 2) - { - // receiver only - isReceiver = true; - - // read port - port = atoi(argv[1]); - } - else if (argc == 3) - { - // sender and receiver - isSender = true; - isReceiver = true; - - // read IP - ip = argv[1]; - - // read port - port = atoi(argv[2]); - } - - Trace::CreateTrace(); - Trace::SetTraceFile("BWEStandAloneTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - sendrec->InitReceiver(port); - - sendrec->Start(); - - if (isSender) - { - const WebRtc_UWord32 startRateKbps = 1000; - //gen = new CBRGenerator(sendrec, 1000, 500); - gen = new CBRFixFRGenerator(sendrec, startRateKbps, 90000, 30, 0.2); - //gen = new PeriodicKeyFixFRGenerator(sendrec, startRateKbps, 90000, 30, 0.2, 7, 300); - //const WebRtc_UWord16 numFrameRates = 5; - //const WebRtc_UWord8 frameRates[numFrameRates] = {30, 15, 20, 23, 25}; - //gen = new CBRVarFRGenerator(sendrec, 1000, frameRates, numFrameRates, 90000, 4.0, 0.1, 0.2); - //gen = new CBRFrameDropGenerator(sendrec, startRateKbps, 90000, 0.2); - sendrec->SetLoadGenerator(gen); - sendrec->InitSender(startRateKbps, ip.c_str(), port); - gen->Start(); - } - - while (1) - { - } - - if (isSender) - { - gen->Stop(); - delete gen; - } - - delete sendrec; - - //WebRtc_UWord8 numberOfSocketThreads = 1; - //UdpTransport* transport = UdpTransport::Create(0, numberOfSocketThreads); - - //RtpRtcp* rtp = RtpRtcp::CreateRtpRtcp(1, false); - //if (rtp->InitSender() != 0) - //{ - // exit(1); - //} - //if (rtp->RegisterSendTransport(transport) != 0) - //{ - // exit(1); - //} - -// transport->InitializeSendSockets("192.168.200.39", 8000); - //transport->InitializeSendSockets("127.0.0.1", 10000); - //transport->InitializeSourcePorts(8000); - - - return(0); - // myTransportCB *tp = new myTransportCB(rtp); - // transport->InitializeReceiveSockets(tp, 10000, "0.0.0.0"); - // transport->StartReceiving(500); - - // WebRtc_Word8 data[100]; - // for (int i = 0; i < 100; data[i] = i++); - - // for (int i = 0; i < 100; i++) - // { - // transport->SendRaw(data, 100, false); - // } - - - - // WebRtc_Word32 totTime = 0; - // while (totTime < 10000) - // { - // transport->Process(); - // WebRtc_Word32 wTime = transport->TimeUntilNextProcess(); - // totTime += wTime; - // Sleep(wTime); - // } - - - //if (transport) - //{ - // // Destroy the Socket Transport module - // transport->StopReceiving(); - // transport->InitializeReceiveSockets(NULL,0);// deregister callback - // UdpTransport::Destroy(transport); - // transport = NULL; - // } - - // if (tp) - // { - // delete tp; - // tp = NULL; - // } - - // if (rtp) - // { - // RtpRtcp::DestroyRtpRtcp(rtp); - // rtp = NULL; - // } - - - //return 0; -} - diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.cpp b/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.cpp deleted file mode 100644 index edc26c873..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "BWETestBase.h" - -#include // sort -#include -#include -#include -#include - -#include "TestSenderReceiver.h" -#include "TestLoadGenerator.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "tick_util.h" -#include "critical_section_wrapper.h" - - -double StatVec::Mean() -{ - double sum = 0; - - // sanity - if (size() <= 0) return (0); - - std::vector::iterator it; - for (it = begin(); it < end(); ++it) - { - sum += (*it); - } - - return (sum / size()); -} - -double StatVec::Variance() -{ - double sumSqaure = 0; - double sum = 0; - - std::vector::iterator it; - for (it = begin(); it < end(); ++it) - { - sum += (*it); - sumSqaure += (*it) * (*it); - } - - // Normalizes by N-1. This produces the best unbiased estimate of the - // variance if X is a sample from a normal distribution. - int M = static_cast (size() - 1); - - if (M > 0) - { - double var = (sumSqaure / M) - (sum / (M+1)) * (sum / M); - assert(var >= 0); - return (var); - } - else - { - return (0); - } -} - -double StatVec::Std() -{ - return (sqrt(Variance())); -} - -double StatVec::Max() -{ - // sanity - if (size() <= 0) return (0); - - std::vector::iterator it = begin(); - double maxVal = (*it); - ++it; - - for (; it < end(); ++it) - { - if ((*it) > maxVal) maxVal = (*it); - } - - return (maxVal); -} - -double StatVec::Min() -{ - // sanity - if (size() <= 0) return (0); - - std::vector::iterator it = begin(); - double minVal = (*it); - ++it; - - for (; it < end(); ++it) - { - if ((*it) < minVal) minVal = (*it); - } - - return (minVal); -} - -double StatVec::Median() -{ - double median; - - // sanity - if (size() <= 0) return (0); - - // sort the vector - sort(begin(), end()); - - if ((size() % 2) == 0) - { - // even size; use average of two center elements - median = (at(size()/2 - 1) + at(size()/2)) / 2.0; - } - else - { - // odd size; take center element - median = at(size()/2); - } - - return (median); -} - -double StatVec::Percentile(double p) -{ - // sanity - if (size() <= 0) return (0); - - // sort the vector - sort(begin(), end()); - - int rank = static_cast (((size() - 1) * p) / 100 + 0.5); // between 1 and size() - rank -= 1; // between 0 and size()-1 - - assert(rank >= 0); - assert(rank < static_cast(size())); - - return (at(rank)); -} - -void StatVec::Export(std::fstream &file, bool colVec /*= false*/) -{ - // sanity - if (size() <= 0) return; - - std::string separator; - if (colVec) separator = "\n"; - else separator = ", "; - - std::vector::iterator it = begin(); - file << (*it); - ++it; - - for (; it < end(); ++it) - { - file << separator << (*it); - } - - file << std::endl; -} - - -bool BWETestProcThreadFunction(void *obj) -{ - if (obj == NULL) - { - return false; - } - BWETest *theObj = static_cast(obj); - - theObj->ProcLoop(); - - theObj->Stop(); - - return(true); -} - - -BWETest::BWETest(std::string testName, int startRateKbps): -_testName(testName), -_startRateKbps(startRateKbps), -_master(false), -_sendrec(NULL), -_gen(NULL), -_initialized(false), -_started(false), -_running(false), -_eventPtr(NULL), -_procThread(NULL), -_startTimeMs(-1), -_stopTimeMs(-1), -_statCritSect(*CriticalSectionWrapper::CreateCriticalSection()) -{ - _sendrec = new TestSenderReceiver(); -} - - -BWETest::~BWETest() -{ - if (_running) - { - Stop(); - } - - _statCritSect.Enter(); - delete &_statCritSect; - - if (_sendrec) - { - delete _sendrec; - _sendrec = NULL; - } -} - - -bool BWETest::SetMaster(bool isMaster /*= true*/) -{ - if (!_initialized) - { - // Can only set status before initializing. - _master = isMaster; - } - - return (_master); -} - - -int BWETest::Init(std::string ip, WebRtc_UWord16 port) -{ - if (_initialized) - { - // cannot init twice - return (-1); - } - - if (!_sendrec) - { - throw "SenderReceiver must be created"; - exit(1); - } - - if (_started) - { - // cannot init after start - return (-1); - } - - // initialize receiver port (for feedback) - _sendrec->InitReceiver(port); - - // initialize sender - _sendrec->SetLoadGenerator(_gen); - _sendrec->InitSender(_startRateKbps, ip.c_str(), port); - //_gen->Start(); - - _sendrec->SetCallback(this); - - _initialized = true; - - return 0; -} - - -bool BWETest::Start() -{ - if (!_initialized) - { - // must init first - return (false); - } - if (_started) - { - // already started, do nothing - return (true); - } - - if (_sendrec->Start() != 0) - { - // failed - return (false); - } - - if (_gen) - { - if (_gen->Start() != 0) - { - // failed - return (false); - } - } - - _eventPtr = EventWrapper::Create(); - - _startTimeMs = TickTime::MillisecondTimestamp(); - _started = true; - _running = true; - - return (true); -} - - -bool BWETest::Stop() -{ - if (_procThread) - { - _stopTimeMs = TickTime::MillisecondTimestamp(); - _procThread->SetNotAlive(); - _running = false; - _eventPtr->Set(); - - while (!_procThread->Stop()) - { - ; - } - - delete _procThread; - _procThread = NULL; - - } - - if (_eventPtr) - { - delete _eventPtr; - _eventPtr = NULL; - } - - _procThread = NULL; - - if(_gen) - { - _gen->Stop(); - } - - return(true); -} - - -bool BWETest::ProcLoop(void) -{ - bool receiving = false; - - // no critSect - while (_running) - { - - // check stopping criterions - if (_master && StoppingCriterionMaster()) - { - printf("StoppingCriterionMaster()\n"); - _stopTimeMs = TickTime::MillisecondTimestamp(); - _running = false; - } - else if (!_master && StoppingCriterionSlave()) - { - printf("StoppingCriterionSlave()\n"); - _running = false; - } - - // wait - _eventPtr->Wait(1000); // 1000 ms - - } - - return true; -} - - -void BWETest::Report(std::fstream &log) -{ - // cannot report on a running test - if(_running) return; - - CriticalSectionScoped cs(_statCritSect); - - log << "\n\n*** Test name = " << _testName << "\n"; - log << "Execution time = " << static_cast(_stopTimeMs - _startTimeMs) / 1000 << " s\n"; - log << "\n"; - log << "RTT statistics\n"; - log << "\tMin = " << _rttVecMs.Min() << " ms\n"; - log << "\tMax = " << _rttVecMs.Max() << " ms\n"; - log << "\n"; - log << "Loss statistics\n"; - log << "\tAverage = " << _lossVec.Mean() << "%\n"; - log << "\tMax = " << _lossVec.Max() << "%\n"; - - log << "\n" << "Rates" << "\n"; - _rateVecKbps.Export(log); - - log << "\n" << "RTT" << "\n"; - _rttVecMs.Export(log); - -} - - -// SenderReceiver callback -void BWETest::OnOnNetworkChanged(const WebRtc_UWord32 bitrateTargetBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord32 jitterMS, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) -{ - CriticalSectionScoped cs(_statCritSect); - - // bitrate statistics - WebRtc_Word32 newBitrateKbps = bitrateTargetBps/1000; - - _rateVecKbps.push_back(newBitrateKbps); - _rttVecMs.push_back(roundTripTimeMs); - _lossVec.push_back(static_cast(fractionLost) / 255.0); -} - - -int BWEOneWayTest::Init(std::string ip, WebRtc_UWord16 port) -{ - - if (!_master) - { - // Use timeout stopping criterion by default for receiver - UseRecvTimeout(); - } - - return (BWETest::Init(ip, port)); - -} - - -bool BWEOneWayTest::Start() -{ - bool ret = BWETest::Start(); - - if (!_master) - { - // send one dummy RTP packet to enable RTT measurements - const WebRtc_UWord8 dummy = 0; - //_gen->sendPayload(TickTime::MillisecondTimestamp(), &dummy, 0); - _sendrec->SendOutgoingData( - static_cast(TickTime::MillisecondTimestamp()*90), - &dummy, 1, webrtc::kVideoFrameDelta); - } - - return ret; -} diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.h b/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.h deleted file mode 100644 index 2dccdb0cc..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWETestBase.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETESTBASE_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETESTBASE_H_ - -#include -#include - -#include "typedefs.h" - -#include "TestSenderReceiver.h" - - -class StatVec : public std::vector -{ -public: - double Mean(); - double Variance(); - double Std(); - double Max(); - double Min(); - double Median(); - double Percentile(double p); // 0 <= p <= 100% - void Export(std::fstream &file, bool colVec = false); -}; - - -class BWETest : public SendRecCB -{ -public: - BWETest(std::string testName, int startRateKbps); - virtual ~BWETest(); - - bool SetMaster(bool isMaster = true); - void UseRecvTimeout() { _sendrec->SetPacketTimeout(1000); }; - virtual int Init(std::string ip, WebRtc_UWord16 port); - virtual bool Start(); - virtual bool Stop(); - bool ProcLoop(void); - virtual void Report(std::fstream &log); - std::string TestName() { return (_testName); }; - - // SenderReceiver callback - virtual void OnOnNetworkChanged(const WebRtc_UWord32 bitrateTargetBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord32 jitterMS, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax); - - -protected: - virtual bool StoppingCriterionMaster() = 0; - virtual bool StoppingCriterionSlave() { return (_sendrec->timeOutTriggered()); }; - - TestSenderReceiver * _sendrec; - TestLoadGenerator * _gen; - - std::string _testName; - int _startRateKbps; - bool _master; - bool _initialized; - bool _started; - bool _running; - EventWrapper *_eventPtr; - ThreadWrapper* _procThread; - WebRtc_Word64 _startTimeMs; - WebRtc_Word64 _stopTimeMs; - - // Statistics, protected by separate CritSect - CriticalSectionWrapper& _statCritSect; - StatVec _rateVecKbps; - StatVec _rttVecMs; - StatVec _lossVec; -}; - - -class BWEOneWayTest : public BWETest -{ -public: - BWEOneWayTest(std::string testName, int startRateKbps) : - BWETest(testName, startRateKbps) {}; - - virtual int Init(std::string ip, WebRtc_UWord16 port); - virtual bool Start(); - -protected: - virtual bool StoppingCriterionSlave() {return ( _sendrec->timeOutTriggered()); }; - -private: - -}; - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETESTBASE_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWETester.cpp b/modules/rtp_rtcp/test/BWEStandAlone/BWETester.cpp deleted file mode 100644 index f1d79feec..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWETester.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// BWETester.cpp : Defines the entry point for the console application. -// - -#include -#include -#include -#include - -#include "event_wrapper.h" -#include "trace.h" - -#include "BWEStabilityTest.h" -#include "BWEConvergenceTest.h" -#include "BWETwoWayLimitFinding.h" - -#include "MatlabPlot.h" - -//#include "vld.h" - -#ifdef MATLAB -MatlabEngine eng; -#endif - - -class testContainer -{ -public: - testContainer(BWETest *test, bool waitForKeyStroke, int delayStartSec, - std::string instruction) : - _test(test), - _waitMaster(waitForKeyStroke), - _waitSlave(waitForKeyStroke), - _delayMaster(delayStartSec), - _delaySlave(delayStartSec), - _instr(instruction) {}; - - testContainer(BWETest *test, - bool waitForKeyStrokeMaster, - bool waitForKeyStrokeSlave, - int delayStartSecMaster, - int delayStartSecSlave, - std::string instruction) : - _test(test), - _waitMaster(waitForKeyStrokeMaster), - _waitSlave(waitForKeyStrokeSlave), - _delayMaster(delayStartSecMaster), - _delaySlave(delayStartSecSlave), - _instr(instruction) {}; - - ~testContainer() { if(_test) delete _test; _test = NULL; }; - - BWETest *_test; - bool _waitMaster; - bool _waitSlave; - int _delayMaster; - int _delaySlave; - std::string _instr; -}; - - -// This is were the test cases are created. -// Syntax: -// tests->push_back(new testContainer( -// new _BWETestConstructor_, // constructor for the test case -// _wait_, // wait for user key press before start -// _delay_, // delay test start (after a key press if enabled) -// "Intruction to user.")); // message to show in console before starting -// -// Or: -// tests->push_back(new testContainer( -// new _BWETestConstructor_, // constructor for the test case -// _waitMaster_, // master will wait for user key press before start -// _waitSlave_, // slave will wait for user key press before start -// _delayMaster_, // delay master test start (after a key press if enabled) -// _delaySlave_, // delay slave test start (after a key press if enabled) -// "Intruction to user.")); // message to show in console before starting -// -// Valid test cases are: -// BWEConvergenceTestUp -// BWEStabilityTest -// BWETwoWayLimitFinding - - -void PopulateTests(std::vector* tests, bool isMaster) -{ - tests->push_back(new testContainer( - new BWEStabilityTest("Stability", 400, 5*60), - true, true, - 0, 0, - "Set bandwidth limit to 512 kbps")); - - tests->push_back(new testContainer( - new BWEStabilityTest("Stability", 4000, 5*60), - true, true, - 0, 0, - "Set bandwidth limit to 5120 kbps")); - - tests->push_back(new testContainer( - new BWEStabilityTest("Stability", 400, 5*60), - true, true, - 0, 0, - "Set bandwidth limit to 512 kbps and a normal distributed delay\ - with mean 100 ms and std dev 15 ms")); - - tests->push_back(new testContainer( - new BWEConvergenceTestUp("Convergence 256->512", 256, 512), - true, - 0, - "Set bandwith limit to 512 kbps")); - - tests->push_back(new testContainer( - new BWEConvergenceTestUp("Convergence 1024->5120", 1024, 5120), - true, - 0, - "Set bandwith limit to 5120 kbps")); - - tests->push_back(new testContainer( - new BWETwoWayLimitFinding("Asymmetric limit finding {1024, 2048} kbps", - 500, 1024, - 500, 2048, - isMaster), - true, - 0, - "Set bandwith limit to {1024, 2048} kbps asymmetric")); - - tests->push_back(new testContainer( - new BWETwoWayLimitFinding("Symmetric limit finding {1024, 1024} kbps", - 500, 1024, - 500, 1024, - isMaster), - true, - 0, - "Set bandwith limit to 1024 kbps symmetric")); -} - - -int main(int argc, char* argv[]) -{ - - bool isMaster = false; - WebRtc_UWord16 port; - std::string ip; - std::fstream log; - log.open("TestLog.txt", std::fstream::out | std::fstream::app); - - log << "\n\nBWE TESTER\n"; - - time_t t = time(0); // get time now - struct tm * now = localtime( & t ); - log << (now->tm_year + 1900) << '-' - << (now->tm_mon + 1) << '-' - << now->tm_mday << " " - << now->tm_hour << ":" << now->tm_min - << "\n"; - - if (argc == 4) - { - // read IP - ip = argv[1]; - - // read port - port = atoi(argv[2]); - - // read master/slave - isMaster = (atoi(argv[3]) != 0); - - std::cout << "Destination: " << ip << "\n"; - log << "Destination: " << ip << "\n"; - std::cout << "Port: " << port << "\n"; - log << "Port: " << port << "\n"; - if (isMaster) - { - std::cout << "Master\n"; - log << "Master\n"; - } - else - { - std::cout << "Slave\n"; - log << "Slave\n"; - } - - } - else - { - printf("Usage\nBWETester dstIP port master\n"); - exit(1); - } - - std::vector tests; - PopulateTests(&tests, isMaster); - - int testIndex = 0; - EventWrapper* event = EventWrapper::Create(); - std::vector::iterator it; - for (it=tests.begin() ; it < tests.end(); it++) - { - ++testIndex; - - BWETest *theTest = (*it)->_test; - - if (theTest) - { - std::cout << "\nTest " << testIndex << ": " << theTest->TestName() << "\n"; - } - - // Print instructions - std::cout << "--> " << (*it)->_instr << std::endl; - - if ((isMaster && (*it)->_waitMaster) - || (!isMaster && (*it)->_waitSlave)) - { - // Wait for a key press - std::cout << "Press enter to start test\n"; - getc(stdin); - } - - if (isMaster) - { - if ((*it)->_delayMaster > 0) - { - // Wait - std::cout << "Test starting in " - << (*it)->_delayMaster - << " seconds" << std::endl; - event->Wait((*it)->_delayMaster * 1000); - } - } - else - { - if ((*it)->_delaySlave > 0) - { - // Wait - std::cout << "Test starting in " - << (*it)->_delaySlave - << " seconds" << std::endl; - event->Wait((*it)->_delaySlave * 1000); - } - } - - // Start execution - if (theTest) - { - theTest->SetMaster(isMaster); - if (theTest->Init(ip, port) != 0) - { - throw "Error initializing sender"; - exit (1); - } - - theTest->Start(); - theTest->ProcLoop(); - theTest->Stop(); - theTest->Report(log); - log << std::flush; - } - - delete (*it); // deletes the test too - } - delete event; - event = NULL; - - log.close(); - return (0); -} diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.cpp b/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.cpp deleted file mode 100644 index 043c7b083..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "BWETwoWayLimitFinding.h" -#include "TestLoadGenerator.h" - - -BWETwoWayLimitFinding::BWETwoWayLimitFinding( - std::string testName, - int masterStartRateKbps, int masterAvailBWkbps, - int slaveStartRateKbps, int slaveAvailBWkbps, - bool isMaster /*= false*/) - : -BWETest(testName, (isMaster ? masterStartRateKbps : slaveStartRateKbps)), -_availBWkbps(isMaster ? masterAvailBWkbps : slaveAvailBWkbps), -_incomingAvailBWkbps(isMaster ? slaveAvailBWkbps : masterAvailBWkbps), -_forwLimitReached(false), -_revLimitReached(false) -{ - _master = isMaster; -} - - -BWETwoWayLimitFinding::~BWETwoWayLimitFinding() -{ - if (_gen) - { - delete _gen; - _gen = NULL; - } -} - - -int BWETwoWayLimitFinding::Init(std::string ip, WebRtc_UWord16 port) -{ - // create the load generator object - const int rtpSampleRate = 90000; - const int frameRate = 30; - const double spreadFactor = 0.2; - - _gen = new CBRFixFRGenerator(_sendrec, _startRateKbps, rtpSampleRate, frameRate, spreadFactor); - if (!_gen) - { - return (-1); - } - - if (!_master) UseRecvTimeout(); // slave shuts down when incoming stream dies - - return BWETest::Init(ip, port); -} - - -bool BWETwoWayLimitFinding::StoppingCriterionMaster() -{ - if ((_sendrec->BitrateSent() / 1000.0) > (0.95 * _availBWkbps)) - { - _forwLimitReached = true; - } - - WebRtc_Word32 revRateKbps = _sendrec->ReceiveBitrateKbps(); - if (revRateKbps > (0.95 * _incomingAvailBWkbps)) - { - _revLimitReached = true; - } - - return (_forwLimitReached && _revLimitReached); -} - diff --git a/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.h b/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.h deleted file mode 100644 index fc790e564..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/BWETwoWayLimitFinding.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETWOWAYLIMITFINDING_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETWOWAYLIMITFINDING_H_ - -#include "BWETestBase.h" - -class BWETwoWayLimitFinding : public BWETest -{ -public: - BWETwoWayLimitFinding(std::string testName, - int masterStartRateKbps, int masterAvailBWkbps, - int slaveStartRateKbps, int slaveAvailBWkbps, - bool isMaster = false); - - virtual ~BWETwoWayLimitFinding(); - - virtual int Init(std::string ip, WebRtc_UWord16 port); - -protected: - virtual bool StoppingCriterionMaster(); - //virtual bool StoppingCriterionSlave(); - -private: - int _availBWkbps; - int _incomingAvailBWkbps; - bool _forwLimitReached; - bool _revLimitReached; - -}; - - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_BWETWOWAYLIMITFINDING_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.cc b/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.cc deleted file mode 100644 index e708b0f80..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.cc +++ /dev/null @@ -1,1071 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "MatlabPlot.h" -#ifdef MATLAB -#include "engine.h" -#endif -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "critical_section_wrapper.h" -#include "tick_util.h" - -#include -#include -#include -#include - -using namespace webrtc; - -#ifdef MATLAB -MatlabEngine eng; - -MatlabLine::MatlabLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/) -: -_xArray(NULL), -_yArray(NULL), -_maxLen(maxLen), -_plotAttribute(), -_name() -{ - if (_maxLen > 0) - { - _xArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL); - _yArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL); - } - - if (plotAttrib) - { - _plotAttribute = plotAttrib; - } - - if (name) - { - _name = name; - } -} - -MatlabLine::~MatlabLine() -{ - if (_xArray != NULL) - { - mxDestroyArray(_xArray); - } - if (_yArray != NULL) - { - mxDestroyArray(_yArray); - } -} - -void MatlabLine::Append(double x, double y) -{ - if (_maxLen > 0 && _xData.size() > static_cast(_maxLen)) - { - _xData.resize(_maxLen); - _yData.resize(_maxLen); - } - - _xData.push_front(x); - _yData.push_front(y); -} - - -// append y-data with running integer index as x-data -void MatlabLine::Append(double y) -{ - if (_xData.empty()) - { - // first element is index 0 - Append(0, y); - } - else - { - // take last x-value and increment - double temp = _xData.back(); // last x-value - Append(temp + 1, y); - } -} - - -void MatlabLine::SetMaxLen(int maxLen) -{ - if (maxLen <= 0) - { - // means no maxLen - _maxLen = -1; - } - else - { - _maxLen = maxLen; - - if (_xArray != NULL) - { - mxDestroyArray(_xArray); - mxDestroyArray(_yArray); - } - _xArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL); - _yArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL); - - maxLen = ((unsigned int)maxLen <= _xData.size()) ? maxLen : (int)_xData.size(); - _xData.resize(maxLen); - _yData.resize(maxLen); - - //// reserve the right amount of memory - //_xData.reserve(_maxLen); - //_yData.reserve(_maxLen); - } -} - -void MatlabLine::SetAttribute(char *plotAttrib) -{ - _plotAttribute = plotAttrib; -} - -void MatlabLine::SetName(char *name) -{ - _name = name; -} - -void MatlabLine::GetPlotData(mxArray** xData, mxArray** yData) -{ - // Make sure we have enough Matlab allocated memory. - // Assuming both arrays (x and y) are of the same size. - if (_xData.empty()) - { - return; // No data - } - unsigned int size = 0; - if (_xArray != NULL) - { - size = (unsigned int)mxGetNumberOfElements(_xArray); - } - if (size < _xData.size()) - { - if (_xArray != NULL) - { - mxDestroyArray(_xArray); - mxDestroyArray(_yArray); - } - _xArray = mxCreateDoubleMatrix(1, _xData.size(), mxREAL); - _yArray = mxCreateDoubleMatrix(1, _yData.size(), mxREAL); - } - - if (!_xData.empty()) - { - double* x = mxGetPr(_xArray); - - std::list::iterator it = _xData.begin(); - - for (int i = 0; it != _xData.end(); it++, i++) - { - x[i] = *it; - } - } - - if (!_yData.empty()) - { - double* y = mxGetPr(_yArray); - - std::list::iterator it = _yData.begin(); - - for (int i = 0; it != _yData.end(); it++, i++) - { - y[i] = *it; - } - } - *xData = _xArray; - *yData = _yArray; -} - -std::string MatlabLine::GetXName() -{ - std::ostringstream xString; - xString << "x_" << _name; - return xString.str(); -} - -std::string MatlabLine::GetYName() -{ - std::ostringstream yString; - yString << "y_" << _name; - return yString.str(); -} - -std::string MatlabLine::GetPlotString() -{ - - std::ostringstream s; - - if (_xData.size() == 0) - { - s << "[0 1], [0 1]"; // To get an empty plot - } - else - { - s << GetXName() << "(1:" << _xData.size() << "),"; - s << GetYName() << "(1:" << _yData.size() << ")"; - } - - s << ", '"; - s << _plotAttribute; - s << "'"; - - return s.str(); -} - -std::string MatlabLine::GetRefreshString() -{ - std::ostringstream s; - - if (_xData.size() > 0) - { - s << "set(h,'xdata',"<< GetXName() <<"(1:" << _xData.size() << "),'ydata',"<< GetYName() << "(1:" << _yData.size() << "));"; - } - else - { - s << "set(h,'xdata',[NaN],'ydata',[NaN]);"; - } - return s.str(); -} - -std::string MatlabLine::GetLegendString() -{ - return ("'" + _name + "'"); -} - -bool MatlabLine::hasLegend() -{ - return (!_name.empty()); -} - - -// remove data points, but keep attributes -void MatlabLine::Reset() -{ - _xData.clear(); - _yData.clear(); -} - - -void MatlabLine::UpdateTrendLine(MatlabLine * sourceData, double slope, double offset) -{ - Reset(); // reset data, not attributes and name - - double thexMin = sourceData->xMin(); - double thexMax = sourceData->xMax(); - Append(thexMin, thexMin * slope + offset); - Append(thexMax, thexMax * slope + offset); -} - -double MatlabLine::xMin() -{ - if (!_xData.empty()) - { - std::list::iterator theStart = _xData.begin(); - std::list::iterator theEnd = _xData.end(); - return(*min_element(theStart, theEnd)); - } - return (0.0); -} - -double MatlabLine::xMax() -{ - if (!_xData.empty()) - { - std::list::iterator theStart = _xData.begin(); - std::list::iterator theEnd = _xData.end(); - return(*max_element(theStart, theEnd)); - } - return (0.0); -} - -double MatlabLine::yMin() -{ - if (!_yData.empty()) - { - std::list::iterator theStart = _yData.begin(); - std::list::iterator theEnd = _yData.end(); - return(*min_element(theStart, theEnd)); - } - return (0.0); -} - -double MatlabLine::yMax() -{ - if (!_yData.empty()) - { - std::list::iterator theStart = _yData.begin(); - std::list::iterator theEnd = _yData.end(); - return(*max_element(theStart, theEnd)); - } - return (0.0); -} - - - -MatlabTimeLine::MatlabTimeLine(int horizonSeconds /*= -1*/, const char *plotAttrib /*= NULL*/, - const char *name /*= NULL*/, - WebRtc_Word64 refTimeMs /* = -1*/) - : -_timeHorizon(horizonSeconds), -MatlabLine(-1, plotAttrib, name) // infinite number of elements -{ - if (refTimeMs < 0) - _refTimeMs = TickTime::MillisecondTimestamp(); - else - _refTimeMs = refTimeMs; -} - -void MatlabTimeLine::Append(double y) -{ - MatlabLine::Append(static_cast(TickTime::MillisecondTimestamp() - _refTimeMs) / 1000.0, y); - - PurgeOldData(); -} - - -void MatlabTimeLine::PurgeOldData() -{ - if (_timeHorizon > 0) - { - // remove old data - double historyLimit = static_cast(TickTime::MillisecondTimestamp() - _refTimeMs) / 1000.0 - - _timeHorizon; // remove data points older than this - - std::list::reverse_iterator ritx = _xData.rbegin(); - WebRtc_UWord32 removeCount = 0; - while (ritx != _xData.rend()) - { - if (*ritx >= historyLimit) - { - break; - } - ritx++; - removeCount++; - } - if (removeCount == 0) - { - return; - } - - // remove the range [begin, it). - //if (removeCount > 10) - //{ - // printf("Removing %lu elements\n", removeCount); - //} - _xData.resize(_xData.size() - removeCount); - _yData.resize(_yData.size() - removeCount); - } -} - - -WebRtc_Word64 MatlabTimeLine::GetRefTime() -{ - return(_refTimeMs); -} - - - - -MatlabPlot::MatlabPlot() -: -_figHandle(-1), -_smartAxis(false), -_critSect(CriticalSectionWrapper::CreateCriticalSection()), -_timeToPlot(false), -_plotting(false), -_enabled(true), -_firstPlot(true), -_legendEnabled(true), -_donePlottingEvent(EventWrapper::Create()) -{ - CriticalSectionScoped cs(*_critSect); - - _xlim[0] = 0; - _xlim[1] = 0; - _ylim[0] = 0; - _ylim[1] = 0; - -#ifdef PLOT_TESTING - _plotStartTime = -1; - _plotDelay = 0; -#endif - -} - - -MatlabPlot::~MatlabPlot() -{ - _critSect->Enter(); - - // delete all line objects - while (!_line.empty()) - { - delete *(_line.end() - 1); - _line.pop_back(); - } - - delete _critSect; - delete _donePlottingEvent; -} - - -int MatlabPlot::AddLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/) -{ - CriticalSectionScoped cs(*_critSect); - if (!_enabled) - { - return -1; - } - - MatlabLine *newLine = new MatlabLine(maxLen, plotAttrib, name); - _line.push_back(newLine); - - return (static_cast(_line.size() - 1)); // index of newly inserted line -} - - -int MatlabPlot::AddTimeLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/, - WebRtc_Word64 refTimeMs /*= -1*/) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return -1; - } - - MatlabTimeLine *newLine = new MatlabTimeLine(maxLen, plotAttrib, name, refTimeMs); - _line.push_back(newLine); - - return (static_cast(_line.size() - 1)); // index of newly inserted line -} - - -int MatlabPlot::GetLineIx(const char *name) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return -1; - } - - // search the list for a matching line name - std::vector::iterator it = _line.begin(); - bool matchFound = false; - int lineIx = 0; - - for (; it != _line.end(); it++, lineIx++) - { - if ((*it)->_name == name) - { - matchFound = true; - break; - } - } - - if (matchFound) - { - return (lineIx); - } - else - { - return (-1); - } -} - - -void MatlabPlot::Append(int lineIndex, double x, double y) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return; - } - - // sanity for index - if (lineIndex < 0 || lineIndex >= static_cast(_line.size())) - { - throw "Line index out of range"; - exit(1); - } - - return (_line[lineIndex]->Append(x, y)); -} - - -void MatlabPlot::Append(int lineIndex, double y) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return; - } - - // sanity for index - if (lineIndex < 0 || lineIndex >= static_cast(_line.size())) - { - throw "Line index out of range"; - exit(1); - } - - return (_line[lineIndex]->Append(y)); -} - - -int MatlabPlot::Append(const char *name, double x, double y) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return -1; - } - - // search the list for a matching line name - int lineIx = GetLineIx(name); - - if (lineIx < 0) //(!matchFound) - { - // no match; append new line - lineIx = AddLine(-1, NULL, name); - } - - // append data to line - Append(lineIx, x, y); - return (lineIx); -} - -int MatlabPlot::Append(const char *name, double y) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return -1; - } - - // search the list for a matching line name - int lineIx = GetLineIx(name); - - if (lineIx < 0) //(!matchFound) - { - // no match; append new line - lineIx = AddLine(-1, NULL, name); - } - - // append data to line - Append(lineIx, y); - return (lineIx); -} - -int MatlabPlot::Length(char *name) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return -1; - } - - int ix = GetLineIx(name); - if (ix >= 0) - { - return (static_cast(_line[ix]->_xData.size())); - } - else - { - return (-1); - } -} - - -void MatlabPlot::SetPlotAttribute(char *name, char *plotAttrib) -{ - CriticalSectionScoped cs(*_critSect); - - if (!_enabled) - { - return; - } - - int lineIx = GetLineIx(name); - - if (lineIx >= 0) - { - _line[lineIx]->SetAttribute(plotAttrib); - } -} - -// Must be called under critical section _critSect -void MatlabPlot::UpdateData(Engine* ep) -{ - if (!_enabled) - { - return; - } - - for (std::vector::iterator it = _line.begin(); it != _line.end(); it++) - { - mxArray* xData = NULL; - mxArray* yData = NULL; - (*it)->GetPlotData(&xData, &yData); - if (xData != NULL) - { - std::string xName = (*it)->GetXName(); - std::string yName = (*it)->GetYName(); - _critSect->Leave(); -#ifdef MATLAB6 - mxSetName(xData, xName.c_str()); - mxSetName(yData, yName.c_str()); - engPutArray(ep, xData); - engPutArray(ep, yData); -#else - int ret = engPutVariable(ep, xName.c_str(), xData); - assert(ret == 0); - ret = engPutVariable(ep, yName.c_str(), yData); - assert(ret == 0); -#endif - _critSect->Enter(); - } - } -} - -bool MatlabPlot::GetPlotCmd(std::ostringstream & cmd, Engine* ep) -{ - _critSect->Enter(); - - if (!DataAvailable()) - { - return false; - } - - if (_firstPlot) - { - GetPlotCmd(cmd); - _firstPlot = false; - } - else - { - GetRefreshCmd(cmd); - } - - UpdateData(ep); - - _critSect->Leave(); - - return true; -} - -// Call inside critsect -void MatlabPlot::GetPlotCmd(std::ostringstream & cmd) -{ - // we have something to plot - // empty the stream - cmd.str(""); // (this seems to be the only way) - - cmd << "figure; h" << _figHandle << "= plot("; - - // first line - std::vector::iterator it = _line.begin(); - cmd << (*it)->GetPlotString(); - - it++; - - // remaining lines - for (; it != _line.end(); it++) - { - cmd << ", "; - cmd << (*it)->GetPlotString(); - } - - cmd << "); "; - - if (_legendEnabled) - { - GetLegendCmd(cmd); - } - - if (_smartAxis) - { - double xMin = _xlim[0]; - double xMax = _xlim[1]; - double yMax = _ylim[1]; - for (std::vector::iterator it = _line.begin(); it != _line.end(); it++) - { - xMax = std::max(xMax, (*it)->xMax()); - xMin = std::min(xMin, (*it)->xMin()); - - yMax = std::max(yMax, (*it)->yMax()); - yMax = std::max(yMax, fabs((*it)->yMin())); - } - _xlim[0] = xMin; - _xlim[1] = xMax; - _ylim[0] = -yMax; - _ylim[1] = yMax; - - cmd << "axis([" << _xlim[0] << ", " << _xlim[1] << ", " << _ylim[0] << ", " << _ylim[1] << "]);"; - } - - int i=1; - for (it = _line.begin(); it != _line.end(); i++, it++) - { - cmd << "set(h" << _figHandle << "(" << i << "), 'Tag', " << (*it)->GetLegendString() << ");"; - } -} - -// Call inside critsect -void MatlabPlot::GetRefreshCmd(std::ostringstream & cmd) -{ - cmd.str(""); // (this seems to be the only way) - std::vector::iterator it = _line.begin(); - for (it = _line.begin(); it != _line.end(); it++) - { - cmd << "h = findobj(0, 'Tag', " << (*it)->GetLegendString() << ");"; - cmd << (*it)->GetRefreshString(); - } - //if (_legendEnabled) - //{ - // GetLegendCmd(cmd); - //} -} - -void MatlabPlot::GetLegendCmd(std::ostringstream & cmd) -{ - std::vector::iterator it = _line.begin(); - bool anyLegend = false; - for (; it != _line.end(); it++) - { - anyLegend = anyLegend || (*it)->hasLegend(); - } - if (anyLegend) - { - // create the legend - - cmd << "legend(h" << _figHandle << ",{"; - - - // iterate lines - int i = 0; - for (std::vector::iterator it = _line.begin(); it != _line.end(); it++) - { - if (i > 0) - { - cmd << ", "; - } - cmd << (*it)->GetLegendString(); - i++; - } - - cmd << "}, 2); "; // place legend in upper-left corner - } -} - -// Call inside critsect -bool MatlabPlot::DataAvailable() -{ - if (!_enabled) - { - return false; - } - - for (std::vector::iterator it = _line.begin(); it != _line.end(); it++) - { - (*it)->PurgeOldData(); - } - - return true; -} - -void MatlabPlot::Plot() -{ - CriticalSectionScoped cs(*_critSect); - - _timeToPlot = true; - -#ifdef PLOT_TESTING - _plotStartTime = TickTime::MillisecondTimestamp(); -#endif -} - - -void MatlabPlot::Reset() -{ - CriticalSectionScoped cs(*_critSect); - - _enabled = true; - - for (std::vector::iterator it = _line.begin(); it != _line.end(); it++) - { - (*it)->Reset(); - } - -} - -void MatlabPlot::SetFigHandle(int handle) -{ - CriticalSectionScoped cs(*_critSect); - - if (handle > 0) - _figHandle = handle; -} - -bool -MatlabPlot::TimeToPlot() -{ - CriticalSectionScoped cs(*_critSect); - return _enabled && _timeToPlot; -} - -void -MatlabPlot::Plotting() -{ - CriticalSectionScoped cs(*_critSect); - _plotting = true; -} - -void -MatlabPlot::DonePlotting() -{ - CriticalSectionScoped cs(*_critSect); - _timeToPlot = false; - _plotting = false; - _donePlottingEvent->Set(); -} - -void -MatlabPlot::DisablePlot() -{ - _critSect->Enter(); - while (_plotting) - { - _critSect->Leave(); - _donePlottingEvent->Wait(WEBRTC_EVENT_INFINITE); - _critSect->Enter(); - } - _enabled = false; -} - -int MatlabPlot::MakeTrend(const char *sourceName, const char *trendName, double slope, double offset, const char *plotAttrib) -{ - CriticalSectionScoped cs(*_critSect); - - int sourceIx; - int trendIx; - - sourceIx = GetLineIx(sourceName); - if (sourceIx < 0) - { - // could not find source - return (-1); - } - - trendIx = GetLineIx(trendName); - if (trendIx < 0) - { - // no trend found; add new line - trendIx = AddLine(2 /*maxLen*/, plotAttrib, trendName); - } - - _line[trendIx]->UpdateTrendLine(_line[sourceIx], slope, offset); - - return (trendIx); - -} - - -MatlabEngine::MatlabEngine() -: -_critSect(CriticalSectionWrapper::CreateCriticalSection()), -_eventPtr(NULL), -_plotThread(NULL), -_running(false), -_numPlots(0) -{ - _eventPtr = EventWrapper::Create(); - - _plotThread = ThreadWrapper::CreateThread(MatlabEngine::PlotThread, this, kLowPriority, "MatlabPlot"); - - if (_plotThread == NULL) - { - throw "Unable to start MatlabEngine thread"; - exit(1); - } - - _running = true; - - unsigned int tid; - _plotThread->Start(tid); - -} - -MatlabEngine::~MatlabEngine() -{ - _critSect->Enter(); - - if (_plotThread) - { - _plotThread->SetNotAlive(); - _running = false; - _eventPtr->Set(); - - while (!_plotThread->Stop()) - { - ; - } - - delete _plotThread; - } - - _plots.clear(); - - _plotThread = NULL; - - delete _eventPtr; - _eventPtr = NULL; - - _critSect->Leave(); - delete _critSect; - -} - -MatlabPlot * MatlabEngine::NewPlot(MatlabPlot *newPlot) -{ - CriticalSectionScoped cs(*_critSect); - - //MatlabPlot *newPlot = new MatlabPlot(); - - if (newPlot) - { - newPlot->SetFigHandle(++_numPlots); // first plot is number 1 - _plots.push_back(newPlot); - } - - return (newPlot); - -} - - -void MatlabEngine::DeletePlot(MatlabPlot *plot) -{ - CriticalSectionScoped cs(*_critSect); - - if (plot == NULL) - { - return; - } - - std::vector::iterator it; - for (it = _plots.begin(); it < _plots.end(); it++) - { - if (plot == *it) - { - break; - } - } - - assert (plot == *it); - - (*it)->DisablePlot(); - - _plots.erase(it); - --_numPlots; - - delete plot; -} - - -bool MatlabEngine::PlotThread(void *obj) -{ - if (!obj) - { - return (false); - } - - MatlabEngine *eng = (MatlabEngine *) obj; - - Engine *ep = engOpen(NULL); - if (!ep) - { - throw "Cannot open Matlab engine"; - return (false); - } - - engSetVisible(ep, true); - engEvalString(ep, "close all;"); - - while (eng->_running) - { - eng->_critSect->Enter(); - - // iterate through all plots - for (unsigned int ix = 0; ix < eng->_plots.size(); ix++) - { - MatlabPlot *plot = eng->_plots[ix]; - if (plot->TimeToPlot()) - { - plot->Plotting(); - eng->_critSect->Leave(); - std::ostringstream cmd; - - if (engEvalString(ep, cmd.str().c_str())) - { - // engine dead - return (false); - } - - // empty the stream - cmd.str(""); // (this seems to be the only way) - if (plot->GetPlotCmd(cmd, ep)) - { - // things to plot, we have already accessed what we need in the plot - plot->DonePlotting(); - - WebRtc_Word64 start = TickTime::MillisecondTimestamp(); - // plot it - int ret = engEvalString(ep, cmd.str().c_str()); - printf("time=%I64i\n", TickTime::MillisecondTimestamp() - start); - if (ret) - { - // engine dead - return (false); - } - -#ifdef PLOT_TESTING - if(plot->_plotStartTime >= 0) - { - plot->_plotDelay = TickTime::MillisecondTimestamp() - plot->_plotStartTime; - plot->_plotStartTime = -1; - } -#endif - } - eng->_critSect->Enter(); - } - } - - eng->_critSect->Leave(); - // wait a while - eng->_eventPtr->Wait(66); // 33 ms - } - - if (ep) - { - engClose(ep); - ep = NULL; - } - - return (true); - -} - -#endif // MATLAB diff --git a/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h b/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h deleted file mode 100644 index 08c700665..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/MatlabPlot.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_MATLABPLOT_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_MATLABPLOT_H_ - -#include -#include -#include - -#include "typedefs.h" - -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -} - -//#define PLOT_TESTING - -#ifdef MATLAB - -typedef struct engine Engine; -typedef struct mxArray_tag mxArray; - -class MatlabLine -{ - friend class MatlabPlot; - -public: - MatlabLine(int maxLen = -1, const char *plotAttrib = NULL, const char *name = NULL); - ~MatlabLine(); - virtual void Append(double x, double y); - virtual void Append(double y); - void SetMaxLen(int maxLen); - void SetAttribute(char *plotAttrib); - void SetName(char *name); - void Reset(); - virtual void PurgeOldData() {}; - - void UpdateTrendLine(MatlabLine * sourceData, double slope, double offset); - - double xMin(); - double xMax(); - double yMin(); - double yMax(); - -protected: - void GetPlotData(mxArray** xData, mxArray** yData); - std::string GetXName(); - std::string GetYName(); - std::string GetPlotString(); - std::string GetRefreshString(); - std::string GetLegendString(); - bool hasLegend(); - std::list _xData; - std::list _yData; - mxArray* _xArray; - mxArray* _yArray; - int _maxLen; - std::string _plotAttribute; - std::string _name; -}; - - -class MatlabTimeLine : public MatlabLine -{ -public: - MatlabTimeLine(int horizonSeconds = -1, const char *plotAttrib = NULL, const char *name = NULL, - WebRtc_Word64 refTimeMs = -1); - ~MatlabTimeLine() {}; - void Append(double y); - void PurgeOldData(); - WebRtc_Word64 GetRefTime(); - -private: - WebRtc_Word64 _refTimeMs; - int _timeHorizon; -}; - - -class MatlabPlot -{ - friend class MatlabEngine; - -public: - MatlabPlot(); - ~MatlabPlot(); - - int AddLine(int maxLen = -1, const char *plotAttrib = NULL, const char *name = NULL); - int AddTimeLine(int maxLen = -1, const char *plotAttrib = NULL, const char *name = NULL, - WebRtc_Word64 refTimeMs = -1); - int GetLineIx(const char *name); - void Append(int lineIndex, double x, double y); - void Append(int lineIndex, double y); - int Append(const char *name, double x, double y); - int Append(const char *name, double y); - int Length(char *name); - void SetPlotAttribute(char *name, char *plotAttrib); - void Plot(); - void Reset(); - void SmartAxis(bool status = true) { _smartAxis = status; }; - void SetFigHandle(int handle); - void EnableLegend(bool enable) { _legendEnabled = enable; }; - - bool TimeToPlot(); - void Plotting(); - void DonePlotting(); - void DisablePlot(); - - int MakeTrend(const char *sourceName, const char *trendName, double slope, double offset, const char *plotAttrib = NULL); - -#ifdef PLOT_TESTING - WebRtc_Word64 _plotStartTime; - WebRtc_Word64 _plotDelay; -#endif - -private: - void UpdateData(Engine* ep); - bool GetPlotCmd(std::ostringstream & cmd, Engine* ep); - void GetPlotCmd(std::ostringstream & cmd); // call inside crit sect - void GetRefreshCmd(std::ostringstream & cmd); // call inside crit sect - void GetLegendCmd(std::ostringstream & cmd); - bool DataAvailable(); - - std::vector _line; - int _figHandle; - bool _smartAxis; - double _xlim[2]; - double _ylim[2]; - webrtc::CriticalSectionWrapper *_critSect; - bool _timeToPlot; - bool _plotting; - bool _enabled; - bool _firstPlot; - bool _legendEnabled; - webrtc::EventWrapper* _donePlottingEvent; -}; - - -class MatlabEngine -{ -public: - MatlabEngine(); - ~MatlabEngine(); - - MatlabPlot * NewPlot(MatlabPlot *newPlot); - void DeletePlot(MatlabPlot *plot); - -private: - static bool PlotThread(void *obj); - - std::vector _plots; - webrtc::CriticalSectionWrapper *_critSect; - webrtc::EventWrapper *_eventPtr; - webrtc::ThreadWrapper* _plotThread; - bool _running; - int _numPlots; -}; - -#endif //MATLAB - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_MATLABPLOT_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.cc b/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.cc deleted file mode 100644 index 20ffe609a..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.cc +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include // for max function -#include - -#include "TestLoadGenerator.h" -#include "TestSenderReceiver.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "critical_section_wrapper.h" -#include "tick_util.h" - - -bool SenderThreadFunction(void *obj) -{ - if (obj == NULL) - { - return false; - } - TestLoadGenerator *_genObj = static_cast(obj); - - return _genObj->GeneratorLoop(); -} - - -TestLoadGenerator::TestLoadGenerator(TestSenderReceiver *sender, WebRtc_Word32 rtpSampleRate) -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_eventPtr(NULL), -_genThread(NULL), -_bitrateKbps(0), -_sender(sender), -_running(false), -_rtpSampleRate(rtpSampleRate) -{ -} - -TestLoadGenerator::~TestLoadGenerator () -{ - if (_running) - { - Stop(); - } - - delete &_critSect; -} - -WebRtc_Word32 TestLoadGenerator::SetBitrate (WebRtc_Word32 newBitrateKbps) -{ - CriticalSectionScoped cs(_critSect); - - if (newBitrateKbps < 0) - { - return -1; - } - - _bitrateKbps = newBitrateKbps; - - printf("New bitrate = %i kbps\n", _bitrateKbps); - - return _bitrateKbps; -} - - -WebRtc_Word32 TestLoadGenerator::Start (const char *threadName) -{ - CriticalSectionScoped cs(_critSect); - - _eventPtr = EventWrapper::Create(); - - _genThread = ThreadWrapper::CreateThread(SenderThreadFunction, this, kRealtimePriority, threadName); - if (_genThread == NULL) - { - throw "Unable to start generator thread"; - exit(1); - } - - _running = true; - - unsigned int tid; - _genThread->Start(tid); - - return 0; -} - - -WebRtc_Word32 TestLoadGenerator::Stop () -{ - _critSect.Enter(); - - if (_genThread) - { - _genThread->SetNotAlive(); - _running = false; - _eventPtr->Set(); - - while (!_genThread->Stop()) - { - _critSect.Leave(); - _critSect.Enter(); - } - - delete _genThread; - _genThread = NULL; - - delete _eventPtr; - _eventPtr = NULL; - } - - _genThread = NULL; - _critSect.Leave(); - return (0); -} - - -int TestLoadGenerator::generatePayload () -{ - return(generatePayload( static_cast( TickTime::MillisecondTimestamp() * _rtpSampleRate / 1000 ))); -} - - -int TestLoadGenerator::sendPayload (const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::FrameType frameType /*= webrtc::kVideoFrameDelta*/) -{ - - return (_sender->SendOutgoingData(timeStamp, payloadData, payloadSize, frameType)); -} - - -CBRGenerator::CBRGenerator (TestSenderReceiver *sender, WebRtc_Word32 payloadSizeBytes, WebRtc_Word32 bitrateKbps, WebRtc_Word32 rtpSampleRate) -: -//_eventPtr(NULL), -_payloadSizeBytes(payloadSizeBytes), -_payload(new WebRtc_UWord8[payloadSizeBytes]), -TestLoadGenerator(sender, rtpSampleRate) -{ - SetBitrate (bitrateKbps); -} - -CBRGenerator::~CBRGenerator () -{ - if (_running) - { - Stop(); - } - - if (_payload) - { - delete [] _payload; - } - -} - -bool CBRGenerator::GeneratorLoop () -{ - double periodMs; - WebRtc_Word64 nextSendTime = TickTime::MillisecondTimestamp(); - - - // no critSect - while (_running) - { - // send data (critSect inside) - generatePayload( static_cast(nextSendTime * _rtpSampleRate / 1000) ); - - // calculate wait time - periodMs = 8.0 * _payloadSizeBytes / ( _bitrateKbps ); - - nextSendTime = static_cast(nextSendTime + periodMs); - - WebRtc_Word32 waitTime = static_cast(nextSendTime - TickTime::MillisecondTimestamp()); - if (waitTime < 0) - { - waitTime = 0; - } - // wait - _eventPtr->Wait(static_cast(waitTime)); - } - - return true; -} - -int CBRGenerator::generatePayload ( WebRtc_UWord32 timestamp ) -{ - CriticalSectionScoped cs(_critSect); - - //WebRtc_UWord8 *payload = new WebRtc_UWord8[_payloadSizeBytes]; - - int ret = sendPayload(timestamp, _payload, _payloadSizeBytes); - - //delete [] payload; - return ret; -} - - - - -///////////////////// - -CBRFixFRGenerator::CBRFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, - WebRtc_Word32 rtpSampleRate, WebRtc_Word32 frameRateFps /*= 30*/, - double spread /*= 0.0*/) -: -//_eventPtr(NULL), -_payloadSizeBytes(0), -_payload(NULL), -_payloadAllocLen(0), -_frameRateFps(frameRateFps), -_spreadFactor(spread), -TestLoadGenerator(sender, rtpSampleRate) -{ - SetBitrate (bitrateKbps); -} - -CBRFixFRGenerator::~CBRFixFRGenerator () -{ - if (_running) - { - Stop(); - } - - if (_payload) - { - delete [] _payload; - _payloadAllocLen = 0; - } - -} - -bool CBRFixFRGenerator::GeneratorLoop () -{ - double periodMs; - WebRtc_Word64 nextSendTime = TickTime::MillisecondTimestamp(); - - _critSect.Enter(); - - if (_frameRateFps <= 0) - { - return false; - } - - _critSect.Leave(); - - // no critSect - while (_running) - { - _critSect.Enter(); - - // calculate payload size - _payloadSizeBytes = nextPayloadSize(); - - if (_payloadSizeBytes > 0) - { - - if (_payloadAllocLen < _payloadSizeBytes * (1 + _spreadFactor)) - { - // re-allocate _payload - if (_payload) - { - delete [] _payload; - _payload = NULL; - } - - _payloadAllocLen = static_cast((_payloadSizeBytes * (1 + _spreadFactor) * 3) / 2 + .5); // 50% extra to avoid frequent re-alloc - _payload = new WebRtc_UWord8[_payloadAllocLen]; - } - - - // send data (critSect inside) - generatePayload( static_cast(nextSendTime * _rtpSampleRate / 1000) ); - } - - _critSect.Leave(); - - // calculate wait time - periodMs = 1000.0 / _frameRateFps; - nextSendTime = static_cast(nextSendTime + periodMs + 0.5); - - WebRtc_Word32 waitTime = static_cast(nextSendTime - TickTime::MillisecondTimestamp()); - if (waitTime < 0) - { - waitTime = 0; - } - // wait - _eventPtr->Wait(waitTime); - } - - return true; -} - -WebRtc_Word32 CBRFixFRGenerator::nextPayloadSize() -{ - const double periodMs = 1000.0 / _frameRateFps; - return static_cast(_bitrateKbps * periodMs / 8 + 0.5); -} - -int CBRFixFRGenerator::generatePayload ( WebRtc_UWord32 timestamp ) -{ - CriticalSectionScoped cs(_critSect); - - double factor = ((double) rand() - RAND_MAX/2) / RAND_MAX; // [-0.5; 0.5] - factor = 1 + 2 * _spreadFactor * factor; // [1 - _spreadFactor ; 1 + _spreadFactor] - - WebRtc_Word32 thisPayloadBytes = static_cast(_payloadSizeBytes * factor); - // sanity - if (thisPayloadBytes > _payloadAllocLen) - { - thisPayloadBytes = _payloadAllocLen; - } - - int ret = sendPayload(timestamp, _payload, thisPayloadBytes); - return ret; -} - - -///////////////////// - -PeriodicKeyFixFRGenerator::PeriodicKeyFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, - WebRtc_Word32 rtpSampleRate, WebRtc_Word32 frameRateFps /*= 30*/, - double spread /*= 0.0*/, double keyFactor /*= 4.0*/, WebRtc_UWord32 keyPeriod /*= 300*/) -: -_keyFactor(keyFactor), -_keyPeriod(keyPeriod), -_frameCount(0), -CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, frameRateFps, spread) -{ -} - -WebRtc_Word32 PeriodicKeyFixFRGenerator::nextPayloadSize() -{ - // calculate payload size for a delta frame - WebRtc_Word32 payloadSizeBytes = static_cast(1000 * _bitrateKbps / (8.0 * _frameRateFps * (1.0 + (_keyFactor - 1.0) / _keyPeriod)) + 0.5); - - if (_frameCount % _keyPeriod == 0) - { - // this is a key frame, scale the payload size - payloadSizeBytes = static_cast(_keyFactor * _payloadSizeBytes + 0.5); - } - _frameCount++; - - return payloadSizeBytes; -} - -//////////////////// - -CBRVarFRGenerator::CBRVarFRGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, const WebRtc_UWord8* frameRates, - WebRtc_UWord16 numFrameRates, WebRtc_Word32 rtpSampleRate, double avgFrPeriodMs, - double frSpreadFactor, double spreadFactor) -: -_avgFrPeriodMs(avgFrPeriodMs), -_frSpreadFactor(frSpreadFactor), -_frameRates(NULL), -_numFrameRates(numFrameRates), -_frChangeTimeMs(TickTime::MillisecondTimestamp() + _avgFrPeriodMs), -CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, frameRates[0], spreadFactor) -{ - _frameRates = new WebRtc_UWord8[_numFrameRates]; - memcpy(_frameRates, frameRates, _numFrameRates); -} - -CBRVarFRGenerator::~CBRVarFRGenerator() -{ - delete [] _frameRates; -} - -void CBRVarFRGenerator::ChangeFrameRate() -{ - const WebRtc_Word64 nowMs = TickTime::MillisecondTimestamp(); - if (nowMs < _frChangeTimeMs) - { - return; - } - // Time to change frame rate - WebRtc_UWord16 frIndex = static_cast(static_cast(rand()) / RAND_MAX - * (_numFrameRates - 1) + 0.5) ; - assert(frIndex < _numFrameRates); - _frameRateFps = _frameRates[frIndex]; - // Update the next frame rate change time - double factor = ((double) rand() - RAND_MAX/2) / RAND_MAX; // [-0.5; 0.5] - factor = 1 + 2 * _frSpreadFactor * factor; // [1 - _frSpreadFactor ; 1 + _frSpreadFactor] - _frChangeTimeMs = nowMs + static_cast(1000.0 * factor * - _avgFrPeriodMs + 0.5); - - printf("New frame rate: %d\n", _frameRateFps); -} - -WebRtc_Word32 CBRVarFRGenerator::nextPayloadSize() -{ - ChangeFrameRate(); - return CBRFixFRGenerator::nextPayloadSize(); -} - -//////////////////// - -CBRFrameDropGenerator::CBRFrameDropGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, - WebRtc_Word32 rtpSampleRate, double spreadFactor) -: -_accBits(0), -CBRFixFRGenerator(sender, bitrateKbps, rtpSampleRate, 30, spreadFactor) -{ -} - -CBRFrameDropGenerator::~CBRFrameDropGenerator() -{ -} - -WebRtc_Word32 CBRFrameDropGenerator::nextPayloadSize() -{ - _accBits -= 1000 * _bitrateKbps / _frameRateFps; - if (_accBits < 0) - { - _accBits = 0; - } - if (_accBits > 0.3 * _bitrateKbps * 1000) - { - //printf("drop\n"); - return 0; - } - else - { - //printf("keep\n"); - const double periodMs = 1000.0 / _frameRateFps; - WebRtc_Word32 frameSize = static_cast(_bitrateKbps * periodMs / 8 + 0.5); - frameSize = std::max(frameSize, static_cast(300 * periodMs / 8 + 0.5)); - _accBits += frameSize * 8; - return frameSize; - } -} diff --git a/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.h b/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.h deleted file mode 100644 index 4ab75192e..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/TestLoadGenerator.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTLOADGENERATOR_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTLOADGENERATOR_H_ - -#include - -#include "typedefs.h" -#include "module_common_types.h" - -class TestSenderReceiver; -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -} - -class TestLoadGenerator -{ -public: - TestLoadGenerator (TestSenderReceiver *sender, WebRtc_Word32 rtpSampleRate = 90000); - virtual ~TestLoadGenerator (); - - WebRtc_Word32 SetBitrate (WebRtc_Word32 newBitrateKbps); - virtual WebRtc_Word32 Start (const char *threadName = NULL); - virtual WebRtc_Word32 Stop (); - virtual bool GeneratorLoop () = 0; - -protected: - virtual int generatePayload ( WebRtc_UWord32 timestamp ) = 0; - int generatePayload (); - int sendPayload (const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::FrameType frameType = webrtc::kVideoFrameDelta); - - webrtc::CriticalSectionWrapper& _critSect; - webrtc::EventWrapper *_eventPtr; - webrtc::ThreadWrapper* _genThread; - WebRtc_Word32 _bitrateKbps; - TestSenderReceiver *_sender; - bool _running; - WebRtc_Word32 _rtpSampleRate; -}; - - -class CBRGenerator : public TestLoadGenerator -{ -public: - CBRGenerator (TestSenderReceiver *sender, WebRtc_Word32 payloadSizeBytes, WebRtc_Word32 bitrateKbps, WebRtc_Word32 rtpSampleRate = 90000); - virtual ~CBRGenerator (); - - virtual WebRtc_Word32 Start () {return (TestLoadGenerator::Start("CBRGenerator"));}; - - virtual bool GeneratorLoop (); - -protected: - virtual int generatePayload ( WebRtc_UWord32 timestamp ); - - WebRtc_Word32 _payloadSizeBytes; - WebRtc_UWord8 *_payload; -}; - - -class CBRFixFRGenerator : public TestLoadGenerator // constant bitrate and fixed frame rate -{ -public: - CBRFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, WebRtc_Word32 rtpSampleRate = 90000, - WebRtc_Word32 frameRateFps = 30, double spread = 0.0); - virtual ~CBRFixFRGenerator (); - - virtual WebRtc_Word32 Start () {return (TestLoadGenerator::Start("CBRFixFRGenerator"));}; - - virtual bool GeneratorLoop (); - -protected: - virtual WebRtc_Word32 nextPayloadSize (); - virtual int generatePayload ( WebRtc_UWord32 timestamp ); - - WebRtc_Word32 _payloadSizeBytes; - WebRtc_UWord8 *_payload; - WebRtc_Word32 _payloadAllocLen; - WebRtc_Word32 _frameRateFps; - double _spreadFactor; -}; - -class PeriodicKeyFixFRGenerator : public CBRFixFRGenerator // constant bitrate and fixed frame rate with periodically large frames -{ -public: - PeriodicKeyFixFRGenerator (TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, WebRtc_Word32 rtpSampleRate = 90000, - WebRtc_Word32 frameRateFps = 30, double spread = 0.0, double keyFactor = 4.0, WebRtc_UWord32 keyPeriod = 300); - virtual ~PeriodicKeyFixFRGenerator () {} - -protected: - virtual WebRtc_Word32 nextPayloadSize (); - - double _keyFactor; - WebRtc_UWord32 _keyPeriod; - WebRtc_UWord32 _frameCount; -}; - -// Probably better to inherit CBRFixFRGenerator from CBRVarFRGenerator, but since -// the fix FR version already existed this was easier. -class CBRVarFRGenerator : public CBRFixFRGenerator // constant bitrate and variable frame rate -{ -public: - CBRVarFRGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, const WebRtc_UWord8* frameRates, - WebRtc_UWord16 numFrameRates, WebRtc_Word32 rtpSampleRate = 90000, double avgFrPeriodMs = 5.0, - double frSpreadFactor = 0.05, double spreadFactor = 0.0); - - ~CBRVarFRGenerator(); - -protected: - virtual void ChangeFrameRate(); - virtual WebRtc_Word32 nextPayloadSize (); - - double _avgFrPeriodMs; - double _frSpreadFactor; - WebRtc_UWord8* _frameRates; - WebRtc_UWord16 _numFrameRates; - WebRtc_Word64 _frChangeTimeMs; -}; - -class CBRFrameDropGenerator : public CBRFixFRGenerator // constant bitrate and variable frame rate -{ -public: - CBRFrameDropGenerator(TestSenderReceiver *sender, WebRtc_Word32 bitrateKbps, - WebRtc_Word32 rtpSampleRate = 90000, double spreadFactor = 0.0); - - ~CBRFrameDropGenerator(); - -protected: - virtual WebRtc_Word32 nextPayloadSize(); - - double _accBits; -}; - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTLOADGENERATOR_H_ diff --git a/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc b/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc deleted file mode 100644 index 2e8270a33..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.cc +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include - -#include "rtp_rtcp.h" -#include "udp_transport.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "tick_util.h" -#include "critical_section_wrapper.h" -#include "TestSenderReceiver.h" -#include "TestLoadGenerator.h" -#include - -#define NR_OF_SOCKET_BUFFERS 500 - - -bool ProcThreadFunction(void *obj) -{ - if (obj == NULL) - { - return false; - } - TestSenderReceiver *theObj = static_cast(obj); - - return theObj->ProcLoop(); -} - - -TestSenderReceiver::TestSenderReceiver (void) -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_eventPtr(NULL), -_procThread(NULL), -_running(false), -_payloadType(0), -_loadGenerator(NULL), -_isSender(false), -_isReceiver(false), -_timeOut(false), -_sendRecCB(NULL), -_lastBytesReceived(0), -_lastTime(-1) -{ - // RTP/RTCP module - _rtp = RtpRtcp::CreateRtpRtcp(0, false); - if (!_rtp) - { - throw "Could not create RTP/RTCP module"; - exit(1); - } - - if (_rtp->InitReceiver() != 0) - { - throw "_rtp->InitReceiver()"; - exit(1); - } - - if (_rtp->InitSender() != 0) - { - throw "_rtp->InitSender()"; - exit(1); - } - - // SocketTransport module - WebRtc_UWord8 numberOfThreads = 1; - _transport = UdpTransport::Create(0, numberOfThreads); - if (!_transport) - { - throw "Could not create transport module"; - exit(1); - } -} - -TestSenderReceiver::~TestSenderReceiver (void) -{ - - Stop(); // N.B. without critSect - - _critSect.Enter(); - - if (_rtp) - { - RtpRtcp::DestroyRtpRtcp(_rtp); - _rtp = NULL; - } - - if (_transport) - { - UdpTransport::Destroy(_transport); - _transport = NULL; - } - - delete &_critSect; - -} - - -WebRtc_Word32 TestSenderReceiver::InitReceiver (const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort, - const WebRtc_Word8 payloadType /*= 127*/) -{ - CriticalSectionScoped cs(_critSect); - - // init transport - if (_transport->InitializeReceiveSockets(this, rtpPort/*, 0, NULL, 0, true*/) != 0) - { - throw "_transport->InitializeReceiveSockets"; - exit(1); - } - - if (_rtp->RegisterIncomingRTPCallback(this) != 0) - { - throw "_rtp->RegisterIncomingRTPCallback"; - exit(1); - } - - if (_rtp->RegisterIncomingDataCallback(this) != 0) - { - throw "_rtp->RegisterIncomingRTPCallback"; - exit(1); - } - - if (_rtp->SetRTCPStatus(kRtcpNonCompound) != 0) - { - throw "_rtp->SetRTCPStatus"; - exit(1); - } - - if (_rtp->SetTMMBRStatus(true) != 0) - { - throw "_rtp->SetTMMBRStatus"; - exit(1); - } - - if (_rtp->RegisterReceivePayload("I420", payloadType, 90000) != 0) - { - throw "_rtp->RegisterReceivePayload"; - exit(1); - } - - _isReceiver = true; - - return (0); -} - - -WebRtc_Word32 TestSenderReceiver::Start() -{ - CriticalSectionScoped cs(_critSect); - - _eventPtr = EventWrapper::Create(); - - if (_rtp->SetSendingStatus(true) != 0) - { - throw "_rtp->SetSendingStatus"; - exit(1); - } - - _procThread = ThreadWrapper::CreateThread(ProcThreadFunction, this, kRealtimePriority, "TestSenderReceiver"); - if (_procThread == NULL) - { - throw "Unable to create process thread"; - exit(1); - } - - _running = true; - - if (_isReceiver) - { - if (_transport->StartReceiving(NR_OF_SOCKET_BUFFERS) != 0) - { - throw "_transport->StartReceiving"; - exit(1); - } - } - - unsigned int tid; - _procThread->Start(tid); - - return 0; - -} - - -WebRtc_Word32 TestSenderReceiver::Stop () -{ - CriticalSectionScoped cs(_critSect); - - _transport->StopReceiving(); - - if (_procThread) - { - _procThread->SetNotAlive(); - _running = false; - _eventPtr->Set(); - - while (!_procThread->Stop()) - { - ; - } - - delete _eventPtr; - - delete _procThread; - } - - _procThread = NULL; - - return (0); -} - - -bool TestSenderReceiver::ProcLoop(void) -{ - - // process RTP/RTCP module - _rtp->Process(); - - // process SocketTransport module - _transport->Process(); - - // no critSect - while (_running) - { - // ask RTP/RTCP module for wait time - WebRtc_Word32 rtpWait = _rtp->TimeUntilNextProcess(); - - // ask SocketTransport module for wait time - WebRtc_Word32 tpWait = _transport->TimeUntilNextProcess(); - - WebRtc_Word32 minWait = (rtpWait < tpWait) ? rtpWait: tpWait; - minWait = (minWait > 0) ? minWait : 0; - // wait - _eventPtr->Wait(minWait); - - // process RTP/RTCP module - _rtp->Process(); - - // process SocketTransport module - _transport->Process(); - - } - - return true; -} - - -WebRtc_Word32 TestSenderReceiver::ReceiveBitrateKbps () -{ - WebRtc_UWord32 bytesSent; - WebRtc_UWord32 packetsSent; - WebRtc_UWord32 bytesReceived; - WebRtc_UWord32 packetsReceived; - - if (_rtp->DataCountersRTP(&bytesSent, &packetsSent, &bytesReceived, &packetsReceived) == 0) - { - WebRtc_Word64 now = TickTime::MillisecondTimestamp(); - WebRtc_Word32 kbps = 0; - if (now > _lastTime) - { - if (_lastTime > 0) - { - // 8 * bytes / ms = kbps - kbps = static_cast( - (8 * (bytesReceived - _lastBytesReceived)) / (now - _lastTime)); - } - _lastTime = now; - _lastBytesReceived = bytesReceived; - } - return (kbps); - } - - return (-1); -} - - -WebRtc_Word32 TestSenderReceiver::SetPacketTimeout(const WebRtc_UWord32 timeoutMS) -{ - return (_rtp->SetPacketTimeout(timeoutMS, 0 /* RTCP timeout */)); -} - - -void TestSenderReceiver::OnPacketTimeout(const WebRtc_Word32 id) -{ - CriticalSectionScoped lock(_critSect); - - _timeOut = true; -} - - -void TestSenderReceiver::OnReceivedPacket(const WebRtc_Word32 id, - const RtpRtcpPacketType packetType) -{ - // do nothing - //printf("OnReceivedPacket\n"); - -} - -WebRtc_Word32 TestSenderReceiver::OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader) -{ - //printf("OnReceivedPayloadData\n"); - return (0); -} - - -void TestSenderReceiver::IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) -{ - _rtp->IncomingPacket((WebRtc_UWord8 *) incomingRtpPacket, static_cast(rtpPacketLength)); -} - - - -void TestSenderReceiver::IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) -{ - _rtp->IncomingPacket((WebRtc_UWord8 *) incomingRtcpPacket, static_cast(rtcpPacketLength)); -} - - - - - -/////////////////// - - -WebRtc_Word32 TestSenderReceiver::InitSender (const WebRtc_UWord32 startBitrateKbps, - const WebRtc_Word8* ipAddr, - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort /*= 0*/, - const WebRtc_Word8 payloadType /*= 127*/) -{ - CriticalSectionScoped cs(_critSect); - - _payloadType = payloadType; - - // check load generator valid - if (_loadGenerator) - { - _loadGenerator->SetBitrate(startBitrateKbps); - } - - if (_rtp->RegisterSendTransport(_transport) != 0) - { - throw "_rtp->RegisterSendTransport"; - exit(1); - } - if (_rtp->RegisterSendPayload("I420", _payloadType, 90000) != 0) - { - throw "_rtp->RegisterSendPayload"; - exit(1); - } - - if (_rtp->RegisterIncomingVideoCallback(this) != 0) - { - throw "_rtp->RegisterIncomingVideoCallback"; - exit(1); - } - - if (_rtp->SetRTCPStatus(kRtcpNonCompound) != 0) - { - throw "_rtp->SetRTCPStatus"; - exit(1); - } - - if (_rtp->SetSendBitrate(startBitrateKbps*1000, 0, MAX_BITRATE_KBPS) != 0) - { - throw "_rtp->SetSendBitrate"; - exit(1); - } - - - // SocketTransport - if (_transport->InitializeSendSockets(ipAddr, rtpPort, rtcpPort)) - { - throw "_transport->InitializeSendSockets"; - exit(1); - } - - _isSender = true; - - return (0); -} - - - -WebRtc_Word32 -TestSenderReceiver::SendOutgoingData(const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::FrameType frameType /*= webrtc::kVideoFrameDelta*/) -{ - return (_rtp->SendOutgoingData(frameType, _payloadType, timeStamp, payloadData, payloadSize)); -} - - -WebRtc_Word32 TestSenderReceiver::SetLoadGenerator(TestLoadGenerator *generator) -{ - CriticalSectionScoped cs(_critSect); - - _loadGenerator = generator; - return(0); - -} - -void TestSenderReceiver::OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord32 minBitrateBps, - const WebRtc_UWord32 maxBitrateBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) -{ - if (_loadGenerator) - { - _loadGenerator->SetBitrate(maxBitrateBps/1000); - } - - if (_sendRecCB) - { - _sendRecCB->OnOnNetworkChanged(maxBitrateBps, - fractionLost, - roundTripTimeMs, - bwEstimateKbitMin, - bwEstimateKbitMax); - } -} - - diff --git a/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h b/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h deleted file mode 100644 index cc9ccdd9c..000000000 --- a/modules/rtp_rtcp/test/BWEStandAlone/TestSenderReceiver.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTSENDERRECEIVER_H_ -#define WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTSENDERRECEIVER_H_ - -#include "typedefs.h" -#include "rtp_rtcp.h" -#include "rtp_rtcp_defines.h" -#include "udp_transport.h" - -class TestLoadGenerator; -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -} - -using namespace webrtc; - -#define MAX_BITRATE_KBPS 50000 - - -class SendRecCB -{ -public: - virtual void OnOnNetworkChanged(const WebRtc_UWord32 bitrateTarget, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) = 0; - - virtual ~SendRecCB() {}; -}; - - -class TestSenderReceiver : public RtpFeedback, public RtpData, public UdpTransportData, public RtpVideoFeedback -{ - -public: - TestSenderReceiver (void); - - ~TestSenderReceiver (void); - - void SetCallback (SendRecCB *cb) { _sendRecCB = cb; }; - - WebRtc_Word32 Start(); - - WebRtc_Word32 Stop(); - - bool ProcLoop(); - - ///////////////////////////////////////////// - // Receiver methods - - WebRtc_Word32 InitReceiver (const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0, - const WebRtc_Word8 payloadType = 127); - - WebRtc_Word32 ReceiveBitrateKbps (); - - WebRtc_Word32 SetPacketTimeout(const WebRtc_UWord32 timeoutMS); - - bool timeOutTriggered () { return (_timeOut); }; - - // Inherited from RtpFeedback - virtual WebRtc_Word32 OnInitializeDecoder(const WebRtc_Word32 id, - const WebRtc_Word8 payloadType, - const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) { return(0);}; - - virtual void OnPacketTimeout(const WebRtc_Word32 id); - - virtual void OnReceivedPacket(const WebRtc_Word32 id, - const RtpRtcpPacketType packetType); - - virtual void OnPeriodicDeadOrAlive(const WebRtc_Word32 id, - const RTPAliveType alive) {}; - - virtual void OnIncomingSSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 SSRC) {}; - - virtual void OnIncomingCSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 CSRC, - const bool added) {}; - - - // Inherited from RtpData - - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader); - - - // Inherited from UdpTransportData - virtual void IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort); - - virtual void IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort); - - - - ///////////////////////////////// - // Sender methods - - WebRtc_Word32 InitSender (const WebRtc_UWord32 startBitrateKbps, - const WebRtc_Word8* ipAddr, - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0, - const WebRtc_Word8 payloadType = 127); - - WebRtc_Word32 SendOutgoingData(const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::FrameType frameType = webrtc::kVideoFrameDelta); - - WebRtc_Word32 SetLoadGenerator(TestLoadGenerator *generator); - - WebRtc_UWord32 BitrateSent() { return (_rtp->BitrateSent()); }; - - - // Inherited from RtpVideoFeedback - virtual void OnReceivedIntraFrameRequest(const WebRtc_Word32 id, - const WebRtc_UWord8 message = 0) {}; - - virtual void OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord32 minBitrateBps, - const WebRtc_UWord32 maxBitrateBps, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax); - -private: - RtpRtcp* _rtp; - UdpTransport* _transport; - webrtc::CriticalSectionWrapper& _critSect; - webrtc::EventWrapper *_eventPtr; - webrtc::ThreadWrapper* _procThread; - bool _running; - WebRtc_Word8 _payloadType; - TestLoadGenerator* _loadGenerator; - bool _isSender; - bool _isReceiver; - bool _timeOut; - SendRecCB * _sendRecCB; - WebRtc_UWord32 _lastBytesReceived; - WebRtc_Word64 _lastTime; - -}; - -#endif // WEBRTC_MODULES_RTP_RTCP_TEST_BWESTANDALONE_TESTSENDERRECEIVER_H_ diff --git a/modules/rtp_rtcp/test/bitstreamTest/bitstreamTest.cpp b/modules/rtp_rtcp/test/bitstreamTest/bitstreamTest.cpp deleted file mode 100644 index 38b6e1508..000000000 --- a/modules/rtp_rtcp/test/bitstreamTest/bitstreamTest.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "../../source/BitstreamBuilder.h" -#include "../../source/BitstreamParser.h" - -#include -#include -#include -#include -#include - -WebRtc_UWord32 BitRateBPS(WebRtc_UWord16 x ) -{ - return (x & 0x3fff) * WebRtc_UWord32(pow(10.0f,(2 + (x >> 14)))); -} - -WebRtc_UWord16 BitRateBPSInv(WebRtc_UWord32 x ) -{ - // 16383 0x3fff - // 1 638 300 exp 0 - // 16 383 000 exp 1 - // 163 830 000 exp 2 - // 1 638 300 000 exp 3 - const float exp = log10(float(x>>14)) - 2; - if(exp < 0.0) - { - return WebRtc_UWord16(x /100); - }else if(exp < 1.0) - { - return 0x4000 + WebRtc_UWord16(x /1000); - }else if(exp < 2.0) - { - return 0x8000 + WebRtc_UWord16(x /10000); - }else if(exp < 3.0) - { - return 0xC000 + WebRtc_UWord16(x /100000); - } else - { - assert(false); - return 0; - } -} - - -int _tmain(int argc, _TCHAR* argv[]) -{ - WebRtc_UWord8 dataBuffer[128]; - BitstreamBuilder builder(dataBuffer, sizeof(dataBuffer)); - - // test 1 to 4 bits - builder.Add1Bit(1); - builder.Add1Bit(0); - builder.Add1Bit(1); - - builder.Add2Bits(1); - builder.Add2Bits(2); - builder.Add2Bits(3); - - builder.Add3Bits(1); - builder.Add3Bits(3); - builder.Add3Bits(7); - - builder.Add4Bits(1); - builder.Add4Bits(5); - builder.Add4Bits(15); - - assert(4 == builder.Length()); - - BitstreamParser parser(dataBuffer, sizeof(dataBuffer)); - - assert(1 == parser.Get1Bit()); - assert(0 == parser.Get1Bit()); - assert(1 == parser.Get1Bit()); - - assert(1 == parser.Get2Bits()); - assert(2 == parser.Get2Bits()); - assert(3 == parser.Get2Bits()); - - assert(1 == parser.Get3Bits()); - assert(3 == parser.Get3Bits()); - assert(7 == parser.Get3Bits()); - - assert(1 == parser.Get4Bits()); - assert(5 == parser.Get4Bits()); - assert(15 == parser.Get4Bits()); - - printf("Test of 1 to 4 bits done\n"); - - // test 5 to 7 bits - builder.Add5Bits(1); - builder.Add5Bits(15); - builder.Add5Bits(30); - - builder.Add6Bits(1); - builder.Add6Bits(30); - builder.Add6Bits(60); - - builder.Add7Bits(1); - builder.Add7Bits(60); - builder.Add7Bits(120); - - assert(1 == parser.Get5Bits()); - assert(15 == parser.Get5Bits()); - assert(30 == parser.Get5Bits()); - - assert(1 == parser.Get6Bits()); - assert(30 == parser.Get6Bits()); - assert(60 == parser.Get6Bits()); - - assert(1 == parser.Get7Bits()); - assert(60 == parser.Get7Bits()); - assert(120 == parser.Get7Bits()); - - printf("Test of 5 to 7 bits done\n"); - - builder.Add8Bits(1); - builder.Add1Bit(1); - builder.Add8Bits(255); - builder.Add1Bit(0); - builder.Add8Bits(127); - builder.Add1Bit(1); - builder.Add8Bits(60); - builder.Add1Bit(0); - builder.Add8Bits(30); - builder.Add1Bit(1); - builder.Add8Bits(120); - builder.Add1Bit(0); - builder.Add8Bits(160); - builder.Add1Bit(1); - builder.Add8Bits(180); - - assert(1 == parser.Get8Bits()); - assert(1 == parser.Get1Bit()); - assert(255 == parser.Get8Bits()); - assert(0 == parser.Get1Bit()); - assert(127 == parser.Get8Bits()); - assert(1 == parser.Get1Bit()); - assert(60 == parser.Get8Bits()); - assert(0 == parser.Get1Bit()); - assert(30 == parser.Get8Bits()); - assert(1 == parser.Get1Bit()); - assert(120 == parser.Get8Bits()); - assert(0 == parser.Get1Bit()); - assert(160 == parser.Get8Bits()); - assert(1 == parser.Get1Bit()); - assert(180 == parser.Get8Bits()); - - printf("Test of 8 bits done\n"); - - builder.Add16Bits(1); - builder.Add1Bit(1); - builder.Add16Bits(255); - builder.Add1Bit(0); - builder.Add16Bits(12756); - builder.Add1Bit(1); - builder.Add16Bits(60); - builder.Add1Bit(0); - builder.Add16Bits(30); - builder.Add1Bit(1); - builder.Add16Bits(30120); - builder.Add1Bit(0); - builder.Add16Bits(160); - builder.Add1Bit(1); - builder.Add16Bits(180); - - assert(1 == parser.Get16Bits()); - assert(1 == parser.Get1Bit()); - assert(255 == parser.Get16Bits()); - assert(0 == parser.Get1Bit()); - assert(12756 == parser.Get16Bits()); - assert(1 == parser.Get1Bit()); - assert(60 == parser.Get16Bits()); - assert(0 == parser.Get1Bit()); - assert(30 == parser.Get16Bits()); - assert(1 == parser.Get1Bit()); - assert(30120 == parser.Get16Bits()); - assert(0 == parser.Get1Bit()); - assert(160 == parser.Get16Bits()); - assert(1 == parser.Get1Bit()); - assert(180 == parser.Get16Bits()); - - printf("Test of 16 bits done\n"); - - builder.Add24Bits(1); - builder.Add1Bit(1); - builder.Add24Bits(255); - builder.Add1Bit(0); - builder.Add24Bits(12756); - builder.Add1Bit(1); - builder.Add24Bits(60); - builder.Add1Bit(0); - builder.Add24Bits(303333); - builder.Add1Bit(1); - builder.Add24Bits(30120); - builder.Add1Bit(0); - builder.Add24Bits(160); - builder.Add1Bit(1); - builder.Add24Bits(8018018); - - assert(1 == parser.Get24Bits()); - assert(1 == parser.Get1Bit()); - assert(255 == parser.Get24Bits()); - assert(0 == parser.Get1Bit()); - assert(12756 == parser.Get24Bits()); - assert(1 == parser.Get1Bit()); - assert(60 == parser.Get24Bits()); - assert(0 == parser.Get1Bit()); - assert(303333 == parser.Get24Bits()); - assert(1 == parser.Get1Bit()); - assert(30120 == parser.Get24Bits()); - assert(0 == parser.Get1Bit()); - assert(160 == parser.Get24Bits()); - assert(1 == parser.Get1Bit()); - assert(8018018 == parser.Get24Bits()); - - printf("Test of 24 bits done\n"); - - builder.Add32Bits(1); - builder.Add1Bit(1); - builder.Add32Bits(255); - builder.Add1Bit(0); - builder.Add32Bits(12756); - builder.Add1Bit(1); - builder.Add32Bits(60); - builder.Add1Bit(0); - builder.Add32Bits(303333); - builder.Add1Bit(1); - builder.Add32Bits(3012000012); - builder.Add1Bit(0); - builder.Add32Bits(1601601601); - builder.Add1Bit(1); - builder.Add32Bits(8018018); - - assert(1 == parser.Get32Bits()); - assert(1 == parser.Get1Bit()); - assert(255 == parser.Get32Bits()); - assert(0 == parser.Get1Bit()); - assert(12756 == parser.Get32Bits()); - assert(1 == parser.Get1Bit()); - assert(60 == parser.Get32Bits()); - assert(0 == parser.Get1Bit()); - assert(303333 == parser.Get32Bits()); - assert(1 == parser.Get1Bit()); - assert(3012000012 == parser.Get32Bits()); - assert(0 == parser.Get1Bit()); - assert(1601601601 == parser.Get32Bits()); - assert(1 == parser.Get1Bit()); - assert(8018018 == parser.Get32Bits()); - - printf("Test of 32 bits done\n"); - - builder.AddUE(1); - builder.AddUE(4); - builder.AddUE(9809706); - builder.AddUE(2); - builder.AddUE(15); - builder.AddUE(16998); - - assert( 106 == builder.Length()); - - assert(1 == parser.GetUE()); - assert(4 == parser.GetUE()); - assert(9809706 == parser.GetUE()); - assert(2 == parser.GetUE()); - assert(15 == parser.GetUE()); - assert(16998 == parser.GetUE()); - - printf("Test UE bits done\n"); - - BitstreamBuilder builderScalabilityInfo(dataBuffer, sizeof(dataBuffer)); - BitstreamParser parserScalabilityInfo(dataBuffer, sizeof(dataBuffer)); - - const WebRtc_UWord8 numberOfLayers = 4; - const WebRtc_UWord8 layerId[numberOfLayers] = {0,1,2,3}; - const WebRtc_UWord8 priorityId[numberOfLayers] = {0,1,2,3}; - const WebRtc_UWord8 discardableId[numberOfLayers] = {0,1,1,1}; - - const WebRtc_UWord8 dependencyId[numberOfLayers]= {0,1,1,1}; - const WebRtc_UWord8 qualityId[numberOfLayers]= {0,0,0,1}; - const WebRtc_UWord8 temporalId[numberOfLayers]= {0,0,1,1}; - - const WebRtc_UWord16 avgBitrate[numberOfLayers]= {BitRateBPSInv(100000), - BitRateBPSInv(200000), - BitRateBPSInv(400000), - BitRateBPSInv(800000)}; - - // todo which one is the sum? - const WebRtc_UWord16 maxBitrateLayer[numberOfLayers]= {BitRateBPSInv(150000), - BitRateBPSInv(300000), - BitRateBPSInv(500000), - BitRateBPSInv(900000)}; - - const WebRtc_UWord16 maxBitrateLayerRepresentation[numberOfLayers] = {BitRateBPSInv(150000), - BitRateBPSInv(450000), - BitRateBPSInv(950000), - BitRateBPSInv(1850000)}; - - assert( 16300 == BitRateBPS(BitRateBPSInv(16383))); - assert( 163800 == BitRateBPS(BitRateBPSInv(163830))); - assert( 1638300 == BitRateBPS(BitRateBPSInv(1638300))); - assert( 1638000 == BitRateBPS(BitRateBPSInv(1638400))); - - assert( 18500 == BitRateBPS(BitRateBPSInv(18500))); - assert( 185000 == BitRateBPS(BitRateBPSInv(185000))); - assert( 1850000 == BitRateBPS(BitRateBPSInv(1850000))); - assert( 18500000 == BitRateBPS(BitRateBPSInv(18500000))); - assert( 185000000 == BitRateBPS(BitRateBPSInv(185000000))); - - const WebRtc_UWord16 maxBitrareCalcWindow[numberOfLayers] = {200, 200,200,200};// in 1/100 of second - - builderScalabilityInfo.Add1Bit(0); // temporal_id_nesting_flag - builderScalabilityInfo.Add1Bit(0); // priority_layer_info_present_flag - builderScalabilityInfo.Add1Bit(0); // priority_id_setting_flag - - builderScalabilityInfo.AddUE(numberOfLayers-1); - - for(int i = 0; i<= numberOfLayers-1; i++) - { - builderScalabilityInfo.AddUE(layerId[i]); - builderScalabilityInfo.Add6Bits(priorityId[i]); - builderScalabilityInfo.Add1Bit(discardableId[i]); - builderScalabilityInfo.Add3Bits(dependencyId[i]); - builderScalabilityInfo.Add4Bits(qualityId[i]); - builderScalabilityInfo.Add3Bits(temporalId[i]); - - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - - builderScalabilityInfo.Add1Bit(1); // bitrate_info_present_flag - - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - builderScalabilityInfo.Add1Bit(0); - - builderScalabilityInfo.Add16Bits(avgBitrate[i]); - builderScalabilityInfo.Add16Bits(maxBitrateLayer[i]); - builderScalabilityInfo.Add16Bits(maxBitrateLayerRepresentation[i]); - builderScalabilityInfo.Add16Bits(maxBitrareCalcWindow[i]); - - builderScalabilityInfo.AddUE(0); // layer_dependency_info_src_layer_id_delta - builderScalabilityInfo.AddUE(0); // parameter_sets_info_src_layer_id_delta - } - - printf("Test builderScalabilityInfo done\n"); - - // Scalability Info parser - parserScalabilityInfo.Get1Bit(); // not used in futher parsing - const WebRtc_UWord8 priority_layer_info_present = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 priority_id_setting_flag = parserScalabilityInfo.Get1Bit(); - - WebRtc_UWord32 numberOfLayersMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 j = 0; j<= numberOfLayersMinusOne; j++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.Get6Bits(); - parserScalabilityInfo.Get1Bit(); - parserScalabilityInfo.Get3Bits(); - parserScalabilityInfo.Get4Bits(); - parserScalabilityInfo.Get3Bits(); - - const WebRtc_UWord8 sub_pic_layer_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 sub_region_layer_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 iroi_division_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 profile_level_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 bitrate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 frm_rate_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 frm_size_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 layer_dependency_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 parameter_sets_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 bitstream_restriction_info_present_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 exact_inter_layer_pred_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(sub_pic_layer_flag || iroi_division_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - } - const WebRtc_UWord8 layer_conversion_flag = parserScalabilityInfo.Get1Bit(); - const WebRtc_UWord8 layer_output_flag = parserScalabilityInfo.Get1Bit(); // not used in futher parsing - - if(profile_level_info_present_flag) - { - parserScalabilityInfo.Get24Bits(); - } - if(bitrate_info_present_flag) - { - // this is what we want - assert(avgBitrate[j] == parserScalabilityInfo.Get16Bits()); - assert(maxBitrateLayer[j] == parserScalabilityInfo.Get16Bits()); - assert(maxBitrateLayerRepresentation[j] == parserScalabilityInfo.Get16Bits()); - assert(maxBitrareCalcWindow[j] == parserScalabilityInfo.Get16Bits()); - }else - { - assert(false); - } - if(frm_rate_info_present_flag) - { - parserScalabilityInfo.Get2Bits(); - parserScalabilityInfo.Get16Bits(); - } - if(frm_size_info_present_flag || iroi_division_info_present_flag) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - if(sub_region_layer_flag) - { - parserScalabilityInfo.GetUE(); - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - if(sub_pic_layer_flag) - { - parserScalabilityInfo.GetUE(); - } - if(iroi_division_info_present_flag) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - }else - { - const WebRtc_UWord32 numRoisMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= numRoisMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - } - } - if(layer_dependency_info_present_flag) - { - const WebRtc_UWord32 numDirectlyDependentLayers = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k < numDirectlyDependentLayers; k++) - { - parserScalabilityInfo.GetUE(); - } - } else - { - parserScalabilityInfo.GetUE(); - } - if(parameter_sets_info_present_flag) - { - const WebRtc_UWord32 numSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= numSeqParameterSetMinusOne; k++) - { - parserScalabilityInfo.GetUE(); - } - const WebRtc_UWord32 numSubsetSeqParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 l = 0; l <= numSubsetSeqParameterSetMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - } - const WebRtc_UWord32 numPicParameterSetMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 m = 0; m <= numPicParameterSetMinusOne; m++) - { - parserScalabilityInfo.GetUE(); - } - }else - { - parserScalabilityInfo.GetUE(); - } - if(bitstream_restriction_info_present_flag) - { - parserScalabilityInfo.Get1Bit(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.GetUE(); - } - if(layer_conversion_flag) - { - parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <2;k++) - { - if(parserScalabilityInfo.Get1Bit()) - { - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - } - if(priority_layer_info_present) - { - const WebRtc_UWord32 prNumDidMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 k = 0; k <= prNumDidMinusOne;k++) - { - parserScalabilityInfo.Get3Bits(); - const WebRtc_UWord32 prNumMinusOne = parserScalabilityInfo.GetUE(); - for(WebRtc_UWord32 l = 0; l <= prNumMinusOne; l++) - { - parserScalabilityInfo.GetUE(); - parserScalabilityInfo.Get24Bits(); - parserScalabilityInfo.Get16Bits(); - parserScalabilityInfo.Get16Bits(); - } - } - } - if(priority_id_setting_flag) - { - WebRtc_UWord8 priorityIdSettingUri; - WebRtc_UWord32 priorityIdSettingUriIdx = 0; - do - { - priorityIdSettingUri = parserScalabilityInfo.Get8Bits(); - } while (priorityIdSettingUri != 0); - } - printf("Test parserScalabilityInfo done\n"); - - printf("\nAPI test of parser for ScalabilityInfo done\n"); - - ::Sleep(5000); -} diff --git a/modules/rtp_rtcp/test/bwe_standalone.gyp b/modules/rtp_rtcp/test/bwe_standalone.gyp deleted file mode 100644 index dc4d741f5..000000000 --- a/modules/rtp_rtcp/test/bwe_standalone.gyp +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'bwe_standalone', - 'type': 'executable', - 'dependencies': [ - 'matlab_plotting', - '../source/rtp_rtcp.gyp:rtp_rtcp', - '../../udp_transport/source/udp_transport.gyp:udp_transport', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../interface', - ], - 'sources': [ - 'BWEStandAlone/BWEStandAlone.cc', - 'BWEStandAlone/TestLoadGenerator.cc', - 'BWEStandAlone/TestLoadGenerator.h', - 'BWEStandAlone/TestSenderReceiver.cc', - 'BWEStandAlone/TestSenderReceiver.h', - ], # source - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }, - ], - ], - - 'include_dirs': [ - ], - 'link_settings': { - }, - }, - - { - 'target_name': 'matlab_plotting', - 'type': '<(library)', - 'dependencies': [ - 'matlab_plotting_include', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '/opt/matlab2010a/extern/include', - ], - # 'direct_dependent_settings': { - # 'defines': [ - # 'MATLAB', - # ], - # 'include_dirs': [ - # 'BWEStandAlone', - # ], - # }, - 'export_dependent_settings': [ - 'matlab_plotting_include', - ], - 'sources': [ - 'BWEStandAlone/MatlabPlot.cc', - 'BWEStandAlone/MatlabPlot.h', - ], - 'link_settings': { - 'ldflags' : [ - '-L/opt/matlab2010a/bin/glnxa64', - '-leng', - '-lmx', - '-Wl,-rpath,/opt/matlab2010a/bin/glnxa64', - ], - }, - 'defines': [ - 'MATLAB', - ], - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }, - ], - ], - }, - - { - 'target_name': 'matlab_plotting_include', - 'type': 'none', - 'direct_dependent_settings': { - 'defines': [ -# 'MATLAB', - ], - 'include_dirs': [ - 'BWEStandAlone', - ], - }, - }, - - { - 'target_name': 'matlab_plotting_test', - 'type': 'executable', - 'dependencies': [ - 'matlab_plotting', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - ], - 'sources': [ - 'BWEStandAlone/matlab_plotting_test.cc', - ], # source - 'conditions': [ - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', # enable exceptions - ], - }, - ], - ], - - 'include_dirs': [ - ], - 'link_settings': { - }, - }, - - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/rtp_rtcp/test/testAPI/H263_CIF_IFRAME.bin b/modules/rtp_rtcp/test/testAPI/H263_CIF_IFRAME.bin deleted file mode 100644 index 00e3f8058ee68190046d8cbdbe6461995dc0a39a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14849 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�F3-A(vdY>M%wLVhqMPcB?%^VF zkf{QdZQ97nHIiQ$SNqBZpJxJnfdIkQ5 z3j`ZYC+UFVyz{A!!uAl>i#W-tX9Z9TjZ0%na*4j2Kn2ykEbBdMwGY8O7sIx~t(##}@qx-xA5UzU1XZ~n_PnQ8p3-E#Sfh%& zoi-#o~-y9}9j*JOmzIuCoxLgNudH%#-*B(PG z&agpqF$?@XIpNQR&Zgn#NEGWM3)M8B{Y_YqK_2H#u#> zUAmmiK_*~y;PI9=OcA)0^J{M>4^1ikiW`GZg^5jtue5gHtAKU4+=@v7!JUsfX&M=T4Lmb9wQHy`%TA~5K2NO){?Ab*PyXIqo0@oK^O8nlGKZ%;I}K1KekCvylEM}>Q~~Wg|U+VHOG0? z-gaZBPw`Fie>*F5xAydnLje?y;pmOS+2z(s*g6Ng)%Djes~t;H=o}u`xH&B5!Dhn* z-=qVBe6@Hwm0bNV1+&$%d_$N>lK+<&gWZBn;K zzJW94-rofIj06l)CSde|<~)w>G9AVUWEqlGlNbNSalA3 z{p?fW+pMm*JS0c|ot(|)+Q`bt?U}`Qy4dBv z^atL=7dvdZ9|>?R9u~U|uth>Z9f~oz0(yV;vTauJ2hJdoYk~nIpKEflsnbqVX^YE0 zutX3$gumhJwt7ISqEC0(nLY|t$$jm>pIDeV*aOQWul1vV?+RhhzG_lCyl>WNCNvR z)Qo=)6sfi}vdDj`sd-^Oz*QGm@fy!VX6gb)TVFN;i)}V(XB2%--+grK&~%?y%x-VL zQ3}E0_7~eU^&)ss%73T*F&|HB51t%iV}Y(o2gP%@g1#NBUszN2NMNZ_w>??$S2BKp z7-Abt&JN$jCo|2*{VEBmFh~Oy6z29J5M6AuUNN@U_AQtkZGGTN^#RxP9<$xoiou@+ zsSJ}Lg@f)TV2GtA?(6BslZO~oE$UM*SJUUbw#)W38^bTM=mbUSNE$1;hZgA2+#hO^ z_)`6TXj@>QyTy-NS$oc0Kg`Y>7AI|?f~IPuSbSbVpUaltjtcVWpJMu@0+P=5R6*Q9 zG02=!BS)Hi3inX5@wa0(`#td@{Y%G{yMe64_q74<#1#(cr5JZpG-% zt^BNyUw+KFp@taVa8-=xRJyD44R_P0_H`?i`t90iHm}qlO;sYS#cz%m8#m`7rx{1F z|GbCd#)$5po<`~3z~eppJwpH>_uyM5qX~v$F%60RWTpQdRZ9_X$I?y6&B*U^>Pl;O zuA1uCYnyXo-VVeNY2vnxty+FM4?~pZ6iD;%xte#;m`E0dXapHyI4%ABK62^mtsRf$ zfBIds{Y!V66&{X`f9Z%}khj|&P6!_B1E%EiXq)h714-kY9BGoQ zX#UNeKEXI>XpB(#V`8@gTMibC*NM<#>gflMc?vATYrT_q5KrxoT(v*C7`U@+ZZzEB z`=>_E>4gO4J_?-_Kn;TGEsQ*U5%PPF=Ly+wErvRjWp~d7_0EEYWC`@7fv(KGC-a4e z?@=FV^JH*+qr&%|YMu0;;LJ{x3gt=_3)UCJSFd?qfVsPczW-`&pJD&jRA zfdw>5i~7bD4i$Sp^)u~!@gn-LUjR;8SdZ9~cb0o)ZEH(5%3#uZp<#d=pm`{I+r74>CyW$mL#>rT`$Y7 zFV>%MHC%e%?(#>g_Gjxq`>)x#bidbSJVLjP^dBiVtiuLee;u>)PEV9K0oz5zjamRO_6iUMn-|xZHceaS%hcVtNSF!lfH%@ z3JoYy`*iQ>0{kJR`L~_zlAiyKgN4%{tb=c+!!xbv9vF#aeey zY;+b#!1X3%ZAbitj9!nxhon^L4oyF|oh1Jx@phGQkN7fu`*AMaQy(xv^A! z=dCeOok-cV7QQRC{`}(J{aWX6+@-pjC-3NbXPMK<-U06FLXWjR-@N$eLA=RmoUAnr zYD5hgqISU;!JrY``7aNh`yX6?ofh*3-rQ{+G?>X18WLUNT%Jqhij)l=9CW-hH4+`) zU-GoT#-~?Wb$a^m{AcM%d&lzWgazY%DFV)3=daj+(vyH1b%U`hN`hqwws0cg7VpLs zY z8zXZdvd2FqvtJ-1g7(CNRD^Uc2`#)$f=Ct>uGyUZJ>k6j^YK~XB|`qh1ek|!RfnNC zrP!bm}U;7+6!66C2QRX*9=Om&tJ(-HaHkjxb1%?U`pggG8Y(c|7BKIIrH(k%6L-K z=bz(t;SuEUKSS*UraxIFL3rk zmZ}MmO7PG273XHzdFk;aq&T3aq=EN{0_3_*&IUr!bln~$^@*!5Zg7cvQyn~Na*iK* zXjPyYc+PNj`ZdbP;#3uzelmPwq}V3be#`yw)?!P@fmph)fFt9EHnJz~A^D4D2ZYRZ z788bK1?wv-#tlY>L+L9vS5}{cex7b;jww^5taTr5O?D<%V#QSx$7j$1G^G`}f5ym&q=neD~!=1;kmr^FaNMQgF3QbCI`-#o&m-rlPuRBsOLdz&d% zEp)N3P@F@gIh-qqz^Nwh90WCf&B1&6n+Z8OpWl7)sl!mgC0m`pqR!@Ho=%Ub@TgF2 zX*B~@`60LS>jg4M{v*3_LjnW$b;pY3$!@f*E4-N8Tp*5;vzW?3)`3EsU zmHsEacXK8E2J~Pw|MC*nOb2mQPC@QgahpoZq~5y6GlqwlF#W=;g%EhgT6(eU-F#F{ zwR|E*w7b>0{XQHj4mC>r7DLDAv$E&vZ1e7>y)~`&bm0By-yD_L?_jsipK<+uyUh1C zXWxw9vrK>Vtyg1`!2%=Q7yiT5SxNiu_jhf^{JlGL8FNb#OArp7w4GZY`S;Lt5>_En zp<8Ns#omA6PTK6Jblv)Gk_KKyr|Rz;(k}tpS&x65!#I^|@W`;dyvWD1Yxnh*=$5(I z{)?eg;eA%^W%Zu>rr^D%Iq0(bzq&O4v+NWJ+4KCeMu+66KZb!NzrhO}+zrIDgEX5u zCaXIq>c6qalW4X^Y0#)1LM5&iU44jBETQbIm<4pup)GYFe{10Hog`2f>0m%(vsntC!agUnn5nasGX^ci;^E;ayq!QsR^N%sU7NxkJpn9U~8K zd)O@?${7^g1f(URj$5F!*4xtjW!)oGv2NwUOwpM|?!G$8*A3Whb56d0aHd0Bjwmm6 zHSk_oB^vq?RA&zbsXzXJ#XH|aS|!``qw`(P9%i`0z2#W(>4DcdKh(HO8^_Z3#zfDZ zmbce>8X ze6!mX-^_QV-`^}MgfLJl4hq%>Q4L}6@9~l77hO6QX2Jmnqi|Fse1l-YiBX$PV>pM- zj_x2(lviFXN!>PXe-4A ztN7#4ay({6o>S^7w=H+f8&Vn_8}T3<2GKq`cqyt>?gi<9!n3~MNKWREsVTuSOG_m^Y7OY5ZyA2nu)HaWAeoVGj}a#UB=IA>E;KPUClOF<^? z!$Y5-<8Wpc$}K9}Ww*7GsnWeknhH>vy{gylxW`0|skp!M)~{56fI1X}omb5G9fr=< z+XVqD->K6x4hxf&ypfAei|x&i+6srX&pevUJ86)z`zA7O>N({vsyoXm&)GM3R_0M- z_F50ItPPjvu!~;fo8VX+RA-?H$kS+3S-vRtiyIFKbw4B+ zZyQvy1U=(-2Wdlj7mnS3Cte*AvbhaHedJJZEjsX%_q+z)`3%YDa^TeaJ@;?so{jvR z5gnSZ>_4*Y$@5*C5Vwb_%c4#A&D*J)mD;=E3?6t@PJ7>kikiy^8MamzZXA2~P4ogMo*_=$i9tf}nIsp@m zHNc2?Z5SLkWXg~b-p(yE_xZ?DRj;(N_nX`N`z>9FYYEeH`8CttgfEZLnUz%6oe=a@jHQGBr+P}9pqo#a(K{O>UgdSE*(C*P+kvkO}I zh3+p{aPgjEDqulobdnV4&L~4Mre~*0H@xFd=sW%gESeE6*e<{b3?{hPe6HcpS})eO zz5Ro96{!*uECxg4jbI8=Ht)i01hC{cQpxNTOhJH0Iu0CJ;BGHEaIHW3|6{u(W*8FS zIO)Mhe={{hz=B9Ff|!!L8);~mAnv*+_j-RVW4B=V4Mb|-D|uC!xgmf8h_PEokZ=HU zT=9!FnCM^j<-260W5W5X!Dzmftm;l3*j*>n{Ngh)_L#pU>{GJrmj2N$ggk{Pt1>IV zu@GIMQ^%nowfji3U778ITS-XxgJnT0(1i)&Z1_=e>`FL~79?dFP~C&_Lkq4g$)H1H z#W74&GklQ>wtM)F;ox6HHcFaR}Wi(DP1cPu;^IB85^2;E@NLcn?CH4x`E2nWZ)haJv zS-pFz!NkQ+l5#u9;#T_1Om0a4F%Su424m+!L2AYXp_8}0wzB&rAo%k~2Dw>q)R_gJS{k;7+-!LGZFcLKoKDrwTH{;5w zSc4c?!7j?v{H#V@1QvDZXLhMJKUvR;_Hdwj{fKc*2g8DxW_Ll~AfOxPeoJo|ItqAGr;K=gO(_EbX%JF?@GJ(z6RIm-HnC zKLgZ3*9>J&5_JWgaH@m@0gGQ1kZT%F5CQ2jeydYgPP5r?0UWi{ezD>#cp0iePHqUK z1#QFdNbqih#_zL)Y73ZTUEk-##K#MkrLa2>WqE>YVMLia3LFAOrm6y)f;)#BG5+=~ zj1=p(;8gLhJ(&oAeG>xuqle&PaY@3KhWu{nL{xN~9XFax&hOuqORvSHw%ZV_#uQm9gh&b zTofbuy_Z+7U(cpA#h(Wl3C0WutkEP%VMH3Q!T6IK{*72L9AG7XDqb9MIS3x31d%Sa z?@%Tgn@_7{w6yWmm}uCI+7ZVA>R`b8mw}HVdYCXjB1+1Ty)?hs*$CNBxg3NKY5fQ) z+aKUK;7;I$plw>P3uaR0Epv<@dt_DbG#M{NO8zTLF7}--Jn0G?EP@x)JqN?N*g#-i z4?99Q?t6~@9hV%kc<2vqItpz&%zW?P)ti8aDut;;R5`T zI~AP6+BcRvFD9Uo(kOxZMSb>{`S<-0(`jfbo#vXvi2fdkeK z)#+p3AkGXWeM}<@0&#DYehiF$+~DY)6feTj9d0Y4;^e=yvCpthJKNBDAC(Vx#pu5o6`RE>gk=cLn8Rik(Gn28!MN|f)&t*3 zlM2Qw;doa2SSG&sdMF<5YtG;t(#aWl#g8}x-e;t*kb&x6_OGT-vR^3tx$euSO?JvU zJY%(JMEMBV;`n-iA(sN5!&Javen-$4Pqjcr&11z(7b>wXxwAYBC@FDc-(W-0L4o-$ z1{d*GgAe-TP(R>de-!~A3t0z);9)@rz~q58(&FG@K}&{@uqp-GNEw`d!;IQET&@-k z1Jg&V{`LV=$*NP@O3Ssj#tiClil1ZO-_hW`Ms<@&@qkVXhVW1;Z&*smFyzo!OmihV>;lMo^N7*J90$%=_;;D{w6^n3gY z7etX5WoL~=U4?5)S;U^Z@K1_TH29wmeh+KX`dp0|3-3>LIZ64 zPH?w0ohU=Oqnh+OFf_>Qip7EN)jY6*B_IUCZ{uN{PTFVw1;fHY1Up;MCWOkoAd8CX z)M@_$Z*8pOU;qG}a8t7J@o@BbT&@AY%j)z0YmGu7{4X9)`zY+sHKFepfs_4|$agCz zu$`hnU<83rRi@h>v+sfch$Jbg&i}xN$sc}=W<5x}ym+bs!s74!se>2oR>koX{0K`P z9g&`b9kTd6gm3UOsB+MlJ?nM7M6N^6yCX_qF(0w;Di@B0$Oa1qukb4cp_?So3{Ho^ zsq)Iv$3dx71}p#DXUOT89#Irn{{kNnJp#lKDoEQu{U0ilW;U&`#s_@Lv6J*AhZ%>v zaf8kZaGBjqNI=^vG>(7&P;rAqls*Oq;W8X{DU9nv=>CC{2<;Pp!1Fr)6u0lHggZUG z`m~{zToYPEnE3NUA&OyY_2FCIWCsg|rq1DdN@fli1wDIY=5TDq`3TZLN;=34NE$3b zqX1{W$WBEp!df#zQCe|gRWw*IB{A3#8R=qCa@-e$$bR%j8NwroHSnra(WFMF4FBL< zb1jD_nqXRN$T?ushl14xxSk;wt-x}?mfI7sJ7AI>%1GcAKVJYnQpoh(C?SDq{%ys5 zj{kvJ;dp;`NFsEf7IXX?M*5lbLqTB80L@Gmp=~=4ju3@LQ}&RB?JWr4 z92>(aA9&cMB)e91?hcwov}l<8teo46w3Oo5c8=#OL~0A{v);_fobglYS0YeW+Iu5< z@bw`YYFI`U=UGG<_Zb1f_h7*KtGb{_*u}dkC6F*b0S_}vy>p;*MHgSc=6n9h#V{|< zuuE4!(z2(2!3W@j1M0Z`gAfK?Gf6K&f%z`8qyN}jq*?&LgWBEtq(?LXfeZT|E}&H3 z{1SQ*VGWWp+F@vXa6bV+`{S@+LOqqG1NqVm^#WIcga;qQmIipBz=e*$*>|H0jWEH1 z3-~a<3Wk61OXtrf>@_rpWfasx<3ac|N>&UnAwVun*@ zV_QUi*j#G5@+2ZSA2c2xo6`M}@RJT@ zC1tovC8U4<;VzX!1^`4DFmHno`9>02^blYKL4yYPF#nWc|NgGf8AEaP90w!_%nO!M z5S;B9CQwWn4N;*u6Q$&fo{&*ky9wz>v3p`iQ)-}7gMs+}{+L$fCJPaCYrrfs51GiF z7-uk{Al7?JnfZDbQUS%NIX}SSv<9*#uVc1#$L(Rcg*zyJWbVocXa46U|K zA3>F$Vn@yeQ|c0Yf_FbUqX3cj=oG@?!}`7rJ4E9!`S9?#?DG391|SckNzD6W-;F4` z4lncn_QFqM4Joxh(oN|NE`ezhpMU>N@eyTs)MP@L_z8#tUJ|3OPYZYz zBIwTXI82KMC-x8{7!(C)v@l=9q(~tGco7#Ak&KWWN9i5K)H{jA)Zc`LkieW`o+B`P z;B5swWLley!rr1F3&Z8k%k`f^2|?o2w;eaw&I6JC{{DrU2PgTxvPr36C8DM;8T zRK5}+7zff{fm)WnjZQUe2OJ7}fBp|>(s!*7LCOMu{|}?z)-?SFd=%9U6VYu0KSu?V zs!PDk`WL_dt?(}oqENS$%AC~#iX!t#=Ty)yQy86{vSd?Kb5?Mb3AWGQyeFMe+kqmMzg_ezW~pu)>IrIC-?XO z0L5xQQ@lrmgPjZ+`B#CPWN1Ngpj&HfoXi};Fp%O0htYII@)OkA^_@w@sMeD_oNE96 zVOh?23Xr7FG)UMo07rxBbz_wSwBtFw#=fhdW}loLft?DU-^@__B0zZ^)Xzk0;w?gP z1B2+Z;e{3~GoI)cA)>%6G=G0K_JK_Kk#MYN6NK577zRd@*heqcN2y_{E;6m}@8=U^ VGhr?e1Y&G!GHHJ diff --git a/modules/rtp_rtcp/test/testAPI/H263_QCIF_IFRAME.bin b/modules/rtp_rtcp/test/testAPI/H263_QCIF_IFRAME.bin deleted file mode 100644 index 0fa144c9c1702a825a2bc7f874d9d12e8ac38bf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2020 zcmVK%@(JS*I0|8zZuk_=e*fpEj!zV+fTnRem|ID-S~KLW@FaWU_CNl0@B2h>Yn zz~q5D0|V+Lfsj=}WGm{?%%z!m1_#(l1vz9EJp%*oB!dh2Mo=f;)TX9Ag9Gp-gA4c> zzJmkuKL!`^Sk?sp`joVX;6pD47x7pTd*C6Lf}L0qd-?bkqMtxM^as^}pP_ron!Df6 z!1J&PHvpGo?nnRs=xC+JL*Fk3t*t-r_AmqLfX{%7isH3Er!EF3_rlgkFTp>607M_P zy~XWMaC}4Tj?Zq-dx(9T{1f~nJM`gU;o@OmrxO(y8wme6n3%Y@FZsj5#Y4dV&I$S! z1_%CcPS39}Kl5;GbZ&wFn==zrED!hq1Yz*h_ZPN0J{|gR53f(~@6&;WgouWIoJ?e7 zge&~xq$H%^-{&74CnW>_IM}%v5C8eZLdL}Zz|IBs76twWZw^n6@9kpt~wp{Z^N)KGlB!4U}nDozywf>j3&Dl^fW(1{;emk+RtzuDF+gIqfN()o!NCQ7|6CX_A-@I} z=op|eTkHk^G#GEcpbV7(!30qU1QGP~4|fIxG#GEiV5$c+3D6i2?*j|?UJ9UdK$FLg zO;P3C`2*Ymfeus(arhB}#+VEMX>#S@yxV+I5?d_-AU&W_Dq602C(&BgM5C927 zrgFbL>IxiSv{de1Y8D~Sv~>mt+w2qz9l?S0ABV!onCLh9J%a=0KPP_%2h>XgC1Tkb zI4A!83=gpU4Aegsyg?+t1bPI1Es%69{LD`%#tHpyfT1q~Gzk1!;s@1%nuYLSd@_Ot z2jnvUcmHow@^;;P2rxe275)qls-VI1|1E6~z`*({3?G4k^HdG!jaLA@+!%Mhhjufe0?>F~7rLmE|9hKyyH#*EOL4 z7*Im+Vt#?n1Da9+z`}zo1}EZ!ngucp2tjh!QKT(kSvCv^IFzw(`pWHMGjA9eLauCj z0f54T{1~5qizY{)fD9-{!HM@)ES&`ak)ky%9PnK_3IQvlssuqj+%JV(upxk8Frfg& ze1>v>!2E;&VM06%PuOQR3lGIPj}0CX{RplY00MC3UzM7UepVwsLNV?0kd%^^hku;J+}!jZ`gy5%3?JEYGO8ak zYIc79d0BP0gRo-2p92lRz8R6c1=McyG~e%{p#@e02l)Vkw2USk z61*d&qnM1E6c#U%ttFsbKExAaZfF&r<^f@8QrW;4xo|GL5wTX#H3)KI)X{ z_9ew>n8cxj#v$UuxL|02T={hsG6l2|bedJDAp$THB&-Gp*-#lQpwj=Jt_BCyRtVs# zN($pnsDqXdLxV&9^~r-;0~iMg3Pc-F5?bJ3eM^`4(y$mGLgoH6bqeAAa;m5@%McMiSS^2 zi-A&t0~`Du63hbws1GDMnFUi%C3ZUc+`qihEk`x>ft zVDZlo|65=B0lFA8Exz*(2Be};{`6# zQQ~J -#include - -#include -#include -#include -#include - -#include "rtp_rtcp.h" -#include "common_types.h" -#include "process_thread.h" -#include "trace.h" - -#include "../source/ModuleRtpRtcpImpl.h" - -#define TEST_AUDIO -#define TEST_VIDEO - -WebRtc_UWord8 _payloadDataFile[65000]; -WebRtc_UWord16 _payloadDataFileLength; -#define VIDEO_NACK_LIST_SIZE 30 - -class LoopBackTransport : public webrtc::Transport -{ -public: - LoopBackTransport(RtpRtcp* rtpRtcpModule) : - _rtpRtcpModule(rtpRtcpModule) - { - _sendCount = 0; - } - virtual int SendPacket(int channel, const void *data, int len) - { - _sendCount++; - if(_sendCount > 500 && _sendCount <= 510) - { - // drop 10 packets - printf("\tDrop packet\n"); - return len; - } - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; - } - WebRtc_UWord32 _sendCount; - RtpRtcp* _rtpRtcpModule; -}; - -class DataRelayReceiverVideo : public RtpData -{ -public: - DataRelayReceiverVideo(RtpRtcp* rtpRtcpModule) : _rtpRtcpModule(rtpRtcpModule) - {} - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* rtpPacket, - const WebRtc_UWord16 rtpPacketSize) - { - if(rtpPacketSize == 0) - { - // we relay only one packet once, but this function in called for each NALU - return 0; - } - if(_rtpRtcpModule->SendRTPPacket(rtpHeader, rtpPacket, rtpPacketSize) == 0) - { - return 0; - } - return -1; - } - RtpRtcp* _rtpRtcpModule; -}; - - -class LoopBackTransportVideo : public webrtc::Transport -{ -public: - LoopBackTransportVideo(RtpRtcp* rtpRtcpModule) : - _count(0), - _packetLoss(0), - _rtpRtcpModule(rtpRtcpModule) - { - } - virtual int SendPacket(int channel, const void *data, int len) - { - if(static_cast(data)[0] == 0) - { -// printf("\t\tReceived pad data length:%d\n", len); - return len; - } - _count++; - if(_packetLoss > 0) - { - if(_count%_packetLoss == 0) - { -// printf("Drop video packet: %u\n", static_cast(data)[3]); - return len; - } -// printf("video packet: %u\n", static_cast(data)[3]); - } else - { -// printf("video packet: %u\n", static_cast(data)[3]); - } - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; - } - WebRtc_UWord32 _packetLoss; - WebRtc_UWord32 _count; - WebRtc_UWord32 _time; - RtpRtcp* _rtpRtcpModule; -}; - - -class DataReceiver : public RtpData -{ -public: - DataReceiver(RtpRtcp* rtpRtcpModule) : - _rtpRtcpModule(rtpRtcpModule) - { - } - - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* rtpPacket, - const WebRtc_UWord16 rtpPacketSize) - { -// printf("\tReceived packet:%d payload type:%d length:%d\n", rtpHeader->header.sequenceNumber, rtpHeader->header.payloadType, payloadSize); - - if(rtpHeader->header.payloadType == 98 || - rtpHeader->header.payloadType == 99) - { - if(strncmp("test", (const char*)payloadData, 4) == 0) - { - return 0; - } - assert(false); - return -1; - } - if(rtpHeader->header.payloadType == 100 || - rtpHeader->header.payloadType == 101 || - rtpHeader->header.payloadType == 102) - { - if(rtpHeader->type.Audio.channel == 1) - { - if(payloadData[0] == 0xff) - { - return 0; - } - }else if(rtpHeader->type.Audio.channel == 2) - { - if(payloadData[0] == 0x0) - { - return 0; - } - }else if(rtpHeader->type.Audio.channel == 3) - { - if(payloadData[0] == 0xaa) - { - return 0; - } - } - assert(false); - return -1; - } - if(payloadSize == 10) - { - if(strncmp("testEnergy", (const char*)payloadData, 10) == 0) - { - if(rtpHeader->type.Audio.numEnergy == 2) - { - if( rtpHeader->type.Audio.arrOfEnergy[0] == 7 && - rtpHeader->type.Audio.arrOfEnergy[1] == 9) - { - return 0; - } - } - assert(false); - return -1; - } - } - return 0; - } - - RtpRtcp* _rtpRtcpModule; -}; - -class DataReceiverVideo : public RtpData -{ -public: - DataReceiverVideo() : - _packetLoss(false), - _curLength(0) - { - } - void CheckRecivedFrame(bool nack) - { - printf("\t\tCheckRecivedFrame\n"); - { - assert(_curLength == _payloadDataFileLength); - _curLength = 0; - if(!nack) - { - for (int i = 0; i < _payloadDataFileLength; i++) - { - assert(_receiveBuffer[i] == _payloadDataFile[i]); - } - } - } - } - - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* rtpPacket, - const WebRtc_UWord16 rtpPacketSize) - { - if(rtpHeader->frameType == webrtc::kFrameEmpty && payloadSize == 0) - { - return 0; - } - // store received payload data - int sByte = 0; - if (rtpHeader->type.Video.codec == VideoH263 && rtpHeader->type.Video.codecHeader.H263.bits) - { - // "or" the first bits - assert(_curLength > 0); - _receiveBuffer[_curLength - 1] |= payloadData[0]; - sByte = 1; - } - memcpy(&_receiveBuffer[_curLength], &payloadData[sByte], payloadSize - sByte); - _curLength += payloadSize - sByte; - - if(!_packetLoss) - { - if (rtpHeader->header.markerBit && payloadSize) - { - // last packet, compare send and received data stream - CheckRecivedFrame(false); - } - } else - { - for(int i = 0; i < VIDEO_NACK_LIST_SIZE; i++) - { - if(_nackList[i] == rtpHeader->header.sequenceNumber) - { - _nackList[i] = -1; - break; - } - } - } - return 0; - } - - bool _packetLoss; - WebRtc_Word32 _nackList[VIDEO_NACK_LIST_SIZE]; - WebRtc_UWord8 _receiveBuffer[100000]; - WebRtc_UWord32 _curLength; -}; - -class VideoFeedback : public RtpVideoFeedback -{ - virtual void OnReceivedIntraFrameRequest(const WebRtc_Word32 id, - const WebRtc_UWord8 message) - { - printf("\tReceived video IntraFrameRequest message:%d \n", message); - }; - - virtual void OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord32 bitrateTarget, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord32 jitterMS, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) - { - static int count = 0; - count++; - const WebRtc_UWord32 bitrateTargetKbit = bitrateTarget/1000; - - // todo jitter is not valid due to send rate - if(count == 1) - { - assert(3667 >= bwEstimateKbitMax); - assert(fractionLost >= 80 && fractionLost < 150); - assert(300 == bitrateTargetKbit); // no inc due to no fraction loss - - } else if(count == 2) - { - assert(1517 == bwEstimateKbitMax); - assert(0 == fractionLost); - assert(300 == bitrateTargetKbit); // no inc due to no actual bitrate - } else if(count == 3) - { - assert(1517 == bwEstimateKbitMax); - assert(0 == fractionLost); - assert(220 == bitrateTargetKbit); - } else if(count == 4) - { - assert(0 == fractionLost); - assert(243 == bitrateTargetKbit); - } else - { - assert(10 == jitterMS); - assert(4 == fractionLost); - } - - printf("\tReceived video OnNetworkChanged bitrateTargetKbit:%d RTT:%d Loss:%d\n", bitrateTargetKbit, roundTripTimeMs, fractionLost); - }; -}; - -class AudioFeedback : public RtpAudioFeedback -{ - virtual void OnReceivedTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const bool end) - { - static WebRtc_UWord8 expectedEvent = 0; - - if(end) - { - WebRtc_UWord8 oldEvent = expectedEvent-1; - if(expectedEvent == 32) - { - oldEvent = 15; - } -#if 0 // test of multiple events - else if(expectedEvent == 34) - { - oldEvent = 32; - expectedEvent = 33; - }else if(expectedEvent == 33) - { - oldEvent = 33; - expectedEvent = 34; - } -#endif - assert(oldEvent == event); - }else - { - assert(expectedEvent == event); - expectedEvent++; - } - if(expectedEvent == 16) - { - expectedEvent = 32; - } - - if(end) - { - printf("\tReceived End of DTMF event:%d with id:%d\n", event, id); - }else - { - printf("\tReceived Start of DTMF event:%d with id:%d\n", event, id); - } - } - virtual void OnPlayTelephoneEvent(const WebRtc_Word32 id, - const WebRtc_UWord8 event, - const WebRtc_UWord16 lengthMs, - const WebRtc_UWord8 volume) - { - printf("\tPlayout DTMF event:%d time:%d ms volume:%d with id:%d\n", event, lengthMs,volume, id); - }; -}; - -class RtcpFeedback : public RtcpFeedback -{ -public: - RtcpFeedback() - { - _rtpRtcpModule = NULL; - _rtpRtcpModuleRelay = NULL; - }; - virtual void OnRTCPPacketTimeout(const WebRtc_Word32 id) - { - printf("\tReceived OnPacketTimeout for RTCP id:%d\n", id); - } - - // if audioVideoOffset > 0 video is behind audio - virtual void OnLipSyncUpdate(const WebRtc_Word32 id, - const WebRtc_Word32 audioVideoOffset) - { -// printf("\tReceived OnLipSyncUpdate:%d with id:%d\n", audioVideoOffset, id); - }; - virtual void OnTMMBRReceived(const WebRtc_Word32 id, - const WebRtc_UWord16 bwEstimateKbit) - { - printf("\tReceived OnTMMBRReceived:%d with id:%d\n", bwEstimateKbit, id); - }; - - virtual void OnXRVoIPMetricReceived(const WebRtc_Word32 id, - const RTCPVoIPMetric* metric, - const WebRtc_Word8 VoIPmetricBuffer[28]) - { - printf("\tOnXRVoIPMetricReceived:%d with id:%d\n", metric->burstDensity, id); - }; - virtual void OnSLIReceived(const WebRtc_Word32 id, - const WebRtc_UWord8 pictureId) - { - printf("\tReceived OnSLIReceived:%d with id:%d\n", pictureId, id); - assert(pictureId == 28); - }; - - virtual void OnRPSIReceived(const WebRtc_Word32 id, - const WebRtc_UWord64 pictureId) - { - printf("\tReceived OnRPSIReceived:%d with id:%d\n", pictureId, id); - assert(pictureId == 12345678); - }; - - virtual void OnApplicationDataReceived(const WebRtc_Word32 id, - const WebRtc_UWord8 subType, - const WebRtc_UWord32 name, - const WebRtc_UWord16 length, - const WebRtc_UWord8* data) - { - WebRtc_Word8 printName[5]; - printName[0] = (WebRtc_Word8)(name >> 24); - printName[1] = (WebRtc_Word8)(name >> 16); - printName[2] = (WebRtc_Word8)(name >> 8); - printName[3] = (WebRtc_Word8)name; - printName[4] = 0; - - WebRtc_Word8* printData = new WebRtc_Word8[length+1]; - memcpy(printData, data, length); - printData[length] = 0; - - printf("\tOnApplicationDataReceived subtype:%d name:%s data:%s with id:%d\n", subType, printName, printData, id); - - assert(strncmp("test",printName, 5) == 0); - delete [] printData; - }; - - virtual void OnSendReportReceived(const WebRtc_Word32 id, - const WebRtc_UWord32 senderSSRC, - const WebRtc_UWord8* incomingPacket, - const WebRtc_UWord16 packetLength) - { - printf("\tOnSendReportReceived RTCP id:%d\n", id); - - if(_rtpRtcpModule) - { - RTCPSenderInfo senderInfo; - assert(_rtpRtcpModule->RemoteRTCPStat(&senderInfo) == 0); - senderInfo.sendOctetCount; - senderInfo.sendPacketCount; - } - if(_rtpRtcpModuleRelay) - { - // relay packet - _rtpRtcpModuleRelay->SendRTCPPacket(incomingPacket, packetLength); - } - }; - - // for relay conferencing - virtual void OnReceiveReportReceived(const WebRtc_Word32 id, - const WebRtc_UWord32 senderSSRC, - const WebRtc_UWord8* incomingPacket, - const WebRtc_UWord16 packetLength) - { - WebRtc_UWord16 RTT = 0; - WebRtc_UWord32 remoteSSRC; - switch(id) - { - case 123: - remoteSSRC = 124; - break; - case 124: - remoteSSRC = 123; - break; - case 125: - remoteSSRC = 126; - break; - case 126: - remoteSSRC = 125; - break; - default: - assert(false); - } - - _rtpRtcpModule->RTT(remoteSSRC, &RTT,NULL,NULL,NULL); - - printf("\tOnReceiveReportReceived RTT:%d RTCP id:%d\n", RTT, id); - if(_rtpRtcpModuleRelay) - { - // relay packet - _rtpRtcpModuleRelay->SendRTCPPacket(incomingPacket, packetLength); - } - }; - RtpRtcp* _rtpRtcpModule; - RtpRtcp* _rtpRtcpModuleRelay; -}; - -class RTPCallback : public RtpFeedback -{ -public: - virtual WebRtc_Word32 OnInitializeDecoder(const WebRtc_Word32 id, - const WebRtc_Word8 payloadType, - const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], - const WebRtc_UWord32 frequency, - const WebRtc_UWord8 channels, - const WebRtc_UWord32 rate) - { - if(payloadType == 96) - { - assert(rate == 64000); - } - printf("\tReceived OnInitializeDecoder \n\t\tpayloadName:%s \n\t\tpayloadType:%d \n\t\tfrequency:%d \n\t\tchannels:%d \n\t\trate:%d \n\t\twith id:%d\n", payloadName,payloadType,frequency, channels, rate, id); - return 0; - } - - virtual void OnPacketTimeout(const WebRtc_Word32 id) - { - printf("\tReceived OnPacketTimeout\n"); - } - - virtual void OnReceivedPacket(const WebRtc_Word32 id, - const RtpRtcpPacketType packetType) - { - printf("\tReceived OnReceivedPacket\n"); - } - - virtual void OnPeriodicDeadOrAlive(const WebRtc_Word32 id, - const RTPAliveType alive) - { - printf("\tReceived OnPeriodicDeadOrAlive\n"); - } - - virtual void OnIncomingSSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 SSRC) - { - printf("\tReceived OnIncomingSSRCChanged\n"); - } - - virtual void OnIncomingCSRCChanged( const WebRtc_Word32 id, - const WebRtc_UWord32 CSRC, - const bool added) - { - printf("\tReceived OnIncomingCSRCChanged\n"); - } -}; - -// todo look at VE 3.0 test app -int _tmain(int argc, _TCHAR* argv[]) -{ -// _crtBreakAlloc = 17967; - - WebRtc_Word8 fileName[1024] = "testTrace.txt"; - Trace::CreateTrace(); - Trace::SetTraceFile(fileName); - memcpy(fileName, "testTraceDebug.txt", 19); - Trace::SetEncryptedTraceFile(fileName); - Trace::SetLevelFilter(webrtc::kTraceAll); - - int myId = 123; - ProcessThread* processThread = ProcessThread::CreateProcessThread(); - processThread->Start(); - -#ifdef TEST_AUDIO - // test all APIs in RTP/RTCP module - RtpRtcp* rtpRtcpModule1 = RtpRtcp::CreateRtpRtcp(myId, - true); // audio - - RtpRtcp* rtpRtcpModule2 = RtpRtcp::CreateRtpRtcp(myId+1, - true); // audio - - processThread->RegisterModule(rtpRtcpModule1); - processThread->RegisterModule(rtpRtcpModule2); - - printf("Welcome to API test of RTP/RTCP module\n"); - - WebRtc_Word8 version[256]; - WebRtc_UWord32 remainingBufferInBytes = 256; - WebRtc_UWord32 position = 0; - assert( 0 == rtpRtcpModule1->Version(version, remainingBufferInBytes, position)); - assert(-1 == rtpRtcpModule1->Version(NULL, remainingBufferInBytes, position)); - printf("\nVersion\n\t%s\n\n", version); - - assert( 0 == rtpRtcpModule1->InitReceiver()); - assert( 0 == rtpRtcpModule1->InitSender()); - - assert( 0 == rtpRtcpModule2->InitReceiver()); - assert( 0 == rtpRtcpModule2->InitSender()); - - printf("\tInitialization done\n"); - - assert(-1 == rtpRtcpModule1->SetMaxTransferUnit(10)); - assert(-1 == rtpRtcpModule1->SetMaxTransferUnit(IP_PACKET_SIZE + 1)); - assert( 0 == rtpRtcpModule1->SetMaxTransferUnit(1234)); - assert(1234-20-8 == rtpRtcpModule1->MaxPayloadLength()); - - assert( 0 == rtpRtcpModule1->SetTransportOverhead(true, true, 12)); - assert(1234 - 20- 20 -20 - 12 == rtpRtcpModule1->MaxPayloadLength()); - - assert( 0 == rtpRtcpModule1->SetTransportOverhead(false, false, 0)); - assert(1234 - 20 - 8== rtpRtcpModule1->MaxPayloadLength()); - - assert( 0 == rtpRtcpModule1->SetSequenceNumber(2345)); - assert(2345 == rtpRtcpModule1->SequenceNumber()); - - assert( 0 == rtpRtcpModule1->SetSSRC(3456)); - assert(3456 == rtpRtcpModule1->SSRC()); - - assert( 0 == rtpRtcpModule1->SetStartTimestamp(4567)); - assert(4567 == rtpRtcpModule1->StartTimestamp()); - - assert(0 == rtpRtcpModule1->SetAudioEnergy(NULL,0)); - - WebRtc_UWord32 arrOfCSRC[webrtc::kRtpCsrcSize] = {1234,2345}; - WebRtc_UWord32 testOfCSRC[webrtc::kRtpCsrcSize] = {0,0,0}; - assert( 0 == rtpRtcpModule1->SetCSRCs(arrOfCSRC,2)); - assert( 2 == rtpRtcpModule1->CSRCs(testOfCSRC)); - assert(arrOfCSRC[0] == testOfCSRC[0]); - assert(arrOfCSRC[1] == testOfCSRC[1]); - - assert( kRtcpOff == rtpRtcpModule1->RTCP()); - assert(0 == rtpRtcpModule1->SetRTCPStatus(kRtcpCompound)); - assert( kRtcpCompound == rtpRtcpModule1->RTCP()); - - assert( kRtcpOff == rtpRtcpModule2->RTCP()); - assert(0 == rtpRtcpModule2->SetRTCPStatus(kRtcpCompound)); - assert( kRtcpCompound == rtpRtcpModule2->RTCP()); - - assert( 0 == rtpRtcpModule1->SetCNAME("john.doe@test.test")); - assert( 0 == rtpRtcpModule2->SetCNAME("jane.doe@test.test")); - assert(-1 == rtpRtcpModule1->SetCNAME(NULL)); - WebRtc_Word8 cName[RTCP_CNAME_SIZE]; - assert(0 == rtpRtcpModule1->CNAME(cName)); - assert(0 == strncmp(cName, "john.doe@test.test", RTCP_CNAME_SIZE)); - assert(-1 == rtpRtcpModule1->CNAME(NULL)); - - assert( false == rtpRtcpModule1->TMMBR()); - assert(0 == rtpRtcpModule1->SetTMMBRStatus(true)); - assert( true == rtpRtcpModule1->TMMBR()); - assert(0 == rtpRtcpModule1->SetTMMBRStatus(false)); - assert( false == rtpRtcpModule1->TMMBR()); - - assert( kNackOff == rtpRtcpModule1->NACK()); - assert(0 == rtpRtcpModule1->SetNACKStatus(kNackRtcp)); - assert( kNackRtcp == rtpRtcpModule1->NACK()); - - assert( false == rtpRtcpModule1->Sending()); - assert(0 == rtpRtcpModule1->SetSendingStatus(true)); - assert( true == rtpRtcpModule1->Sending()); - assert(0 == rtpRtcpModule2->SetSendingStatus(true)); - - // audio specific - assert( false == rtpRtcpModule1->TelephoneEvent()); - assert(0 == rtpRtcpModule2->SetTelephoneEventStatus(true, true, true)); // to test detection at the end of a DTMF tone - assert( true == rtpRtcpModule2->TelephoneEvent()); - - printf("Basic set/get test done\n"); - - // test setup - DataReceiver* myDataReceiver1 = new DataReceiver(rtpRtcpModule1); - assert(0 == rtpRtcpModule1->RegisterIncomingDataCallback(myDataReceiver1)); - - DataReceiver* myDataReceiver2 = new DataReceiver(rtpRtcpModule2); - assert(0 == rtpRtcpModule2->RegisterIncomingDataCallback(myDataReceiver2)); - - LoopBackTransport* myLoopBackTransport1 = new LoopBackTransport(rtpRtcpModule2); - assert(0 == rtpRtcpModule1->RegisterSendTransport(myLoopBackTransport1)); - - LoopBackTransport* myLoopBackTransport2 = new LoopBackTransport(rtpRtcpModule1); - assert(0 == rtpRtcpModule2->RegisterSendTransport(myLoopBackTransport2)); - - RTPCallback* myRTPCallback = new RTPCallback(); - assert(0 == rtpRtcpModule2->RegisterIncomingRTPCallback(myRTPCallback)); - - RtcpFeedback* myRTCPFeedback1 = new RtcpFeedback(); - RtcpFeedback* myRTCPFeedback2 = new RtcpFeedback(); - myRTCPFeedback1->_rtpRtcpModule = rtpRtcpModule1; - myRTCPFeedback2->_rtpRtcpModule = rtpRtcpModule2; - assert(0 == rtpRtcpModule1->RegisterIncomingRTCPCallback(myRTCPFeedback1)); - assert(0 == rtpRtcpModule2->RegisterIncomingRTCPCallback(myRTCPFeedback2)); - - assert(0 == rtpRtcpModule1->SetSendingStatus(true)); - - // start basic RTP test - // send an empty RTP packet, should fail since we have not registerd the payload type - assert(-1 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, 0, NULL, 0)); - - WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE] = "PCMU"; - - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 96, 8000)); - assert(0 == rtpRtcpModule1->RegisterReceivePayload(payloadName, 96, 8000)); - assert(0 == rtpRtcpModule2->RegisterSendPayload( payloadName, 96, 8000)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 96, 8000, 1, 64000)); - - WebRtc_Word8 testPayloadName[RTP_PAYLOAD_NAME_SIZE]; - WebRtc_UWord32 testFrequency = 0; - WebRtc_Word8 testPayloadType= 0; - WebRtc_UWord8 testChannels= 0; - - assert(0 == rtpRtcpModule1->ReceivePayload( 96,testPayloadName, &testFrequency, &testChannels)); - assert(0 == strncmp(testPayloadName, payloadName, 4)); - assert(1 == testChannels); - - assert(0 == rtpRtcpModule1->ReceivePayloadType( payloadName,8000,1,&testPayloadType)); - assert(testPayloadType == 96); - - // energy test - const WebRtc_UWord8 energy[3] = {7,9,3}; - assert(-1 == rtpRtcpModule1->SetAudioEnergy(energy,3)); //should fails since we only have 2 CSRCs - assert(0 == rtpRtcpModule1->SetAudioEnergy(energy,2)); - - // send RTP packet with the data "testtest" - const WebRtc_UWord8 test[9] = "testtest"; - const WebRtc_UWord8 testEnergy[11] = "testEnergy"; - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 0, testEnergy, 10)); - assert(0 == rtpRtcpModule2->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 0, test, 8)); -// assert(-1 == rtpRtcpModule->SendOutgoingData(96, 0, NULL, 4)); - - assert(3456 == rtpRtcpModule2->RemoteSSRC()); - assert(4567 == rtpRtcpModule2->RemoteTimestamp()); - - assert(0 == rtpRtcpModule1->SetStorePacketsStatus(true, 100)); - - assert(-1 == rtpRtcpModule1->SetTFRCStatus(true)); - assert(0 == rtpRtcpModule1->SetAudioEnergy(NULL,0)); - assert(0 == rtpRtcpModule1->SetTFRCStatus(true)); - - memcpy(payloadName, "RED",4); - // Test RED - assert(0 == rtpRtcpModule1->SetSendREDPayloadType(127)); - WebRtc_Word8 red = 0; - assert(0 == rtpRtcpModule1->SendREDPayloadType(red)); - assert(127 == red); - assert(0 == rtpRtcpModule1->RegisterReceivePayload( payloadName, 127)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 127)); - - { - RTPFragmentationHeader fragmentation; - fragmentation.fragmentationVectorSize = 2; - fragmentation.fragmentationLength = new WebRtc_UWord32[2]; - fragmentation.fragmentationLength[0] = 4; - fragmentation.fragmentationLength[1] = 4; - fragmentation.fragmentationOffset = new WebRtc_UWord32[2]; - fragmentation.fragmentationOffset[0] = 0; - fragmentation.fragmentationOffset[1] = 4; - fragmentation.fragmentationTimeDiff = new WebRtc_UWord16[2]; - fragmentation.fragmentationTimeDiff[0] = 0; - fragmentation.fragmentationTimeDiff[1] = 0; - fragmentation.fragmentationPlType = new WebRtc_UWord8[2]; - fragmentation.fragmentationPlType[0] = 96; - fragmentation.fragmentationPlType[1] = 96; - - // send a RTP packet - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 160, test,8, &fragmentation)); - } - assert(0 == rtpRtcpModule1->SetSendREDPayloadType(-1)); - assert(-1 == rtpRtcpModule1->SendREDPayloadType(red)); - - assert(0 == rtpRtcpModule1->SetStorePacketsStatus(false)); - - assert(0 == rtpRtcpModule1->SetTFRCStatus(false)); - - printf("Basic RTP test done\n"); - - // todo CNG - - AudioFeedback* audioFeedback = new AudioFeedback(); - assert(0 == rtpRtcpModule2->RegisterAudioCallback(audioFeedback)); - - // prepare for DTMF - memcpy(payloadName, "telephone-event",16); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 97, 8000)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 97)); - - // prepare for 3 channel audio 8 bits per sample - memcpy(payloadName, "PCMA",5); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 98, 8000, 3)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 98,8000, 3)); - - // prepare for 3 channel audio 16 bits per sample - memcpy(payloadName, "L16",4); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 99, 8000, 3)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 99, 8000, 3)); - - // prepare for 3 channel audio 5 bits per sample - memcpy(payloadName, "G726-40",8); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 100, 8000, 3)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 100, 8000, 3)); - - // prepare for 3 channel audio 3 bits per sample - memcpy(payloadName, "G726-24",8); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 101, 8000, 3)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 101, 8000, 3)); - - // prepare for 3 channel audio 2 bits per sample - memcpy(payloadName, "G726-16",8); - assert(0 == rtpRtcpModule1->RegisterSendPayload( payloadName, 102, 8000, 3)); - assert(0 == rtpRtcpModule2->RegisterReceivePayload( payloadName, 102, 8000, 3)); - - // Start DTMF test - - // Send a DTMF tone using RFC 2833 (4733) - for(int i = 0; i < 16; i++) - { - printf("\tSending tone: %d\n", i); - assert(0 == rtpRtcpModule1->SendTelephoneEventOutband(i, 160, 10)); - } - - // send RTP packets for 16 tones a 160 ms + 100ms pause between = 2560ms + 1600ms = 4160ms - int j = 2; - for(;j <= 250;j++) - { - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 160*j, test,8)); - Sleep(20); - } - printf("Basic DTMF test done\n"); - - assert(0 == rtpRtcpModule1->SendTelephoneEventOutband(32, 9000, 10)); - - for(;j <= 740;j++) - { - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 160*j, test,8)); - Sleep(20); - } - - printf("Start Stereo test\n"); - // test sample based multi channel codec, 3 channels 8 bits - WebRtc_UWord8 test3channels[15] = "ttteeesssttt"; - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,98, 160*j, test3channels,12)); - Sleep(20); - j++; - - // test sample based multi channel codec, 3 channels 16 bits - const WebRtc_UWord8 test3channels16[13] = "teteteststst"; - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,99, 160*j, test3channels16,12)); - Sleep(20); - j++; - - // test sample based multi channel codec, 3 channels 5 bits - test3channels[0] = 0xf8; // 5 ones 3 zeros - test3channels[1] = 0x2b; // 2 zeros 5 10 1 one - test3channels[2] = 0xf0; // 4 ones 4 zeros - test3channels[3] = 0x2b; // 1 zero 5 01 2 ones - test3channels[4] = 0xe0; // 3 ones 5 zeros - test3channels[5] = 0x0; - test3channels[6] = 0x0; - test3channels[7] = 0x0; - test3channels[8] = 0x0; - test3channels[9] = 0x0; - test3channels[10] = 0x0; - test3channels[11] = 0x0; - test3channels[12] = 0x0; - test3channels[13] = 0x0; - test3channels[14] = 0x0; - - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,100, 160*j, test3channels,15)); - Sleep(20); - j++; - - // test sample based multi channel codec, 3 channels 3 bits - test3channels[0] = 0xe2; // 3 ones 3 zeros 2 10 - test3channels[1] = 0xf0; // 1 1 3 ones 3 zeros 1 0 - test3channels[2] = 0xb8; // 2 10 3 ones 3 zeros - test3channels[3] = 0xa0; // 3 101 5 zeros - test3channels[4] = 0x0; - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,101, 160*j, test3channels,15)); - Sleep(20); - j++; - - // test sample based multi channel codec, 3 channels 2 bits - test3channels[0] = 0xcb; // 2 ones 2 zeros 2 10 2 ones - test3channels[1] = 0x2c; // 2 zeros 2 10 2 ones 2 zeros - test3channels[2] = 0xb2; // 2 10 2 ones 2 zeros 2 10 - test3channels[3] = 0xcb; // 2 ones 2 zeros 2 10 2 ones - test3channels[4] = 0x2c; // 2 zeros 2 10 2 ones 2 zeros - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,102, 160*j, test3channels,15)); - Sleep(20); - j++; - - for(;j <= 750;j++) - { - assert(0 == rtpRtcpModule1->SendOutgoingData(webrtc::kAudioFrameSpeech,96, 160*j, test,8)); - Sleep(20); - } - - printf("Long tone DTMF test done\n"); - - // start basic RTCP test - assert(0 == rtpRtcpModule1->SendRTCPReferencePictureSelection(12345678)); - - assert(0 == rtpRtcpModule1->SendRTCPSliceLossIndication(156)); - - testOfCSRC[0] = 0; - testOfCSRC[1] = 0; - assert( 2 == rtpRtcpModule2->RemoteCSRCs(testOfCSRC)); - assert(arrOfCSRC[0] == testOfCSRC[0]); - assert(arrOfCSRC[1] == testOfCSRC[1]); - - // set cname of mixed - assert( 0 == rtpRtcpModule1->AddMixedCNAME(arrOfCSRC[0], "john@192.168.0.1")); - assert( 0 == rtpRtcpModule1->AddMixedCNAME(arrOfCSRC[1], "jane@192.168.0.2")); - assert(-1 == rtpRtcpModule1->AddMixedCNAME(arrOfCSRC[0], NULL)); - - assert(-1 == rtpRtcpModule1->RemoveMixedCNAME(arrOfCSRC[0] + 1)); // not added - assert( 0 == rtpRtcpModule1->RemoveMixedCNAME(arrOfCSRC[1])); - assert( 0 == rtpRtcpModule1->AddMixedCNAME(arrOfCSRC[1], "jane@192.168.0.2")); - - RTCPReportBlock reportBlock; - reportBlock.cumulativeLost = 1; - reportBlock.delaySinceLastSR = 2; - reportBlock.extendedHighSeqNum= 3; - reportBlock.fractionLost= 4; - reportBlock.jitter= 5; - reportBlock.lastSR= 6; - - // set report blocks - assert(-1 == rtpRtcpModule1->AddRTCPReportBlock(arrOfCSRC[0], NULL)); - assert( 0 == rtpRtcpModule1->AddRTCPReportBlock(arrOfCSRC[0], &reportBlock)); - - reportBlock.lastSR= 7; - assert(0 == rtpRtcpModule1->AddRTCPReportBlock(arrOfCSRC[1], &reportBlock)); - - WebRtc_UWord32 name = 't'<<24; - name += 'e'<<16; - name += 's'<<8; - name += 't'; - assert(0 == rtpRtcpModule1->SetRTCPApplicationSpecificData(3,name,(const WebRtc_UWord8 *)"test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test ",300)); - - // send RTCP packet, triggered by timer - Sleep(8000); - - WebRtc_UWord32 receivedNTPsecs = 0; - WebRtc_UWord32 receivedNTPfrac = 0; - WebRtc_UWord32 RTCPArrivalTimeSecs = 0; - WebRtc_UWord32 RTCPArrivalTimeFrac = 0; - - assert(0 == rtpRtcpModule2->RemoteNTP(&receivedNTPsecs, - &receivedNTPfrac, - &RTCPArrivalTimeSecs, - &RTCPArrivalTimeFrac)); - - assert(-1 == rtpRtcpModule2->RemoteCNAME(rtpRtcpModule2->RemoteSSRC() + 1, cName)); // not received - assert(-1 == rtpRtcpModule2->RemoteCNAME(rtpRtcpModule2->RemoteSSRC(), NULL)); - - // check multiple CNAME - assert(0 == rtpRtcpModule2->RemoteCNAME(rtpRtcpModule2->RemoteSSRC(), cName)); - assert(0 == strncmp(cName, "john.doe@test.test", RTCP_CNAME_SIZE)); - - assert(0 == rtpRtcpModule2->RemoteCNAME(arrOfCSRC[0], cName)); - assert(0 == strncmp(cName, "john@192.168.0.1", RTCP_CNAME_SIZE)); - - assert(0 == rtpRtcpModule2->RemoteCNAME(arrOfCSRC[1], cName)); - assert(0 == strncmp(cName, "jane@192.168.0.2", RTCP_CNAME_SIZE)); - - // get all report blocks - RTCPReportBlock reportBlockReceived; - - assert(-1 == rtpRtcpModule1->RemoteRTCPStat(rtpRtcpModule1->RemoteSSRC() + 1, &reportBlockReceived)); // not received - assert(-1 == rtpRtcpModule1->RemoteRTCPStat(rtpRtcpModule1->RemoteSSRC(), NULL)); - assert(0 == rtpRtcpModule1->RemoteRTCPStat(rtpRtcpModule1->RemoteSSRC(), &reportBlockReceived)); - float secSinceLastReport = (float)reportBlockReceived.delaySinceLastSR/65536.0f; - assert( secSinceLastReport > 0.0f && secSinceLastReport < 7.5f); // audio RTCP max 7.5 sec - // startSeqNum + number of sent + number of extra due to DTMF - assert(2345+750+2+16 == reportBlockReceived.extendedHighSeqNum); - assert(0 == reportBlockReceived.fractionLost); - // we have dropped 10 packets but since we change codec it's reset - assert(0 == reportBlockReceived.cumulativeLost); - - WebRtc_UWord8 fraction_lost = 0; // scale 0 to 255 - WebRtc_UWord32 cum_lost = 0; // number of lost packets - WebRtc_UWord32 ext_max = 0; // highest sequence number received - WebRtc_UWord32 jitter = 0; - WebRtc_UWord32 max_jitter = 0; - assert(0 == rtpRtcpModule2->StatisticsRTP(&fraction_lost, &cum_lost, &ext_max, &jitter, &max_jitter)); - assert(0 == fraction_lost); - assert(0 == cum_lost); - assert(2345+750+16+2 == ext_max); - assert(reportBlockReceived.jitter == jitter); - - WebRtc_UWord16 RTT; - WebRtc_UWord16 avgRTT; - WebRtc_UWord16 minRTT; - WebRtc_UWord16 maxRTT; - - // Get RoundTripTime - assert(0 == rtpRtcpModule1->RTT(rtpRtcpModule1->RemoteSSRC(),&RTT, &avgRTT, &minRTT, &maxRTT)); - assert(RTT < 10); - assert(avgRTT < 10); - assert(minRTT < 10); - assert(minRTT > 0); - assert(maxRTT < 10); - -/* since we filter out this in the receiver we can't get it - - assert(0 == rtpRtcpModule2->RemoteRTCPStat(arrOfCSRC[0], &reportBlockReceived)); - assert(reportBlock.cumulativeLost == reportBlockReceived.cumulativeLost); - assert(reportBlock.delaySinceLastSR == reportBlockReceived.delaySinceLastSR); - assert(reportBlock.extendedHighSeqNum == reportBlockReceived.extendedHighSeqNum); - assert(reportBlock.fractionLost == reportBlockReceived.fractionLost); - assert(reportBlock.jitter == reportBlockReceived.jitter); - assert(6 == reportBlockReceived.lastSR); - - assert(0 == rtpRtcpModule2->RemoteRTCPStat(arrOfCSRC[1], &reportBlockReceived)); - assert(reportBlock.cumulativeLost == reportBlockReceived.cumulativeLost); - assert(reportBlock.delaySinceLastSR == reportBlockReceived.delaySinceLastSR); - assert(reportBlock.extendedHighSeqNum == reportBlockReceived.extendedHighSeqNum); - assert(reportBlock.fractionLost == reportBlockReceived.fractionLost); - assert(reportBlock.jitter == reportBlockReceived.jitter); - assert(reportBlock.lastSR == reportBlockReceived.lastSR); -*/ - // set report blocks - assert(0 == rtpRtcpModule1->AddRTCPReportBlock(arrOfCSRC[0], &reportBlock)); - - // test receive report - assert(0 == rtpRtcpModule1->SetSendingStatus(false)); - - // test that BYE clears the CNAME - assert(-1 == rtpRtcpModule2->RemoteCNAME(rtpRtcpModule2->RemoteSSRC(), cName)); - - // send RTCP packet, triggered by timer - Sleep(5000); - printf("\tBasic RTCP test done\n"); - - processThread->DeRegisterModule(rtpRtcpModule1); - processThread->DeRegisterModule(rtpRtcpModule2); - - RtpRtcp::DestroyRtpRtcp(rtpRtcpModule1); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModule2); - -#endif // TEST_AUDIO - -#ifdef TEST_VIDEO - - // Test video - RtpRtcp* rtpRtcpModuleVideo = RtpRtcp::CreateRtpRtcp(myId, - false); // video - - assert( 0 == rtpRtcpModuleVideo->InitReceiver()); - assert( 0 == rtpRtcpModuleVideo->InitSender()); - - LoopBackTransportVideo* myLoopBackTransportVideo = new LoopBackTransportVideo(rtpRtcpModuleVideo); - assert(0 == rtpRtcpModuleVideo->RegisterSendTransport(myLoopBackTransportVideo)); - - DataReceiverVideo* myDataReceiverVideo = new DataReceiverVideo(); - assert(0 == rtpRtcpModuleVideo->RegisterIncomingDataCallback(myDataReceiverVideo)); - - VideoFeedback* myVideoFeedback = new VideoFeedback(); - assert(0 == rtpRtcpModuleVideo->RegisterIncomingVideoCallback(myVideoFeedback)); - - printf("Start video test\n"); - WebRtc_UWord32 timestamp = 3000; - WebRtc_Word8 payloadNameVideo[RTP_PAYLOAD_NAME_SIZE] = "I420"; - - assert(0 == rtpRtcpModuleVideo->RegisterSendPayload(payloadNameVideo, 123)); - assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload(payloadNameVideo, 123)); - - _payloadDataFileLength = (WebRtc_UWord16)sizeof(_payloadDataFile); - - for(int n = 0; n< _payloadDataFileLength; n++) - { - _payloadDataFile[n] = n%10; - } - - printf("\tSending I420 frame. Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,123, timestamp, _payloadDataFile, _payloadDataFileLength)); - - memcpy(payloadNameVideo, "MP4V-ES", 8); - assert(0 == rtpRtcpModuleVideo->RegisterSendPayload(payloadNameVideo, 122)); - assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload(payloadNameVideo, 122)); - - // fake a MPEG-4 coded stream - for (int m = 500; m< _payloadDataFileLength; m+= 500) - { - // start codes - _payloadDataFile[m] = 0; - _payloadDataFile[m+1] = 0; - } - printf("\tSending MPEG-4 frame. Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,122, timestamp, _payloadDataFile, _payloadDataFileLength)); - - memcpy(payloadNameVideo, "H263-1998", 10); - assert(0 == rtpRtcpModuleVideo->RegisterSendPayload(payloadNameVideo, 124)); - assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload(payloadNameVideo, 124)); - - // Test send H.263 frame - FILE* openFile = fopen("H263_CIF_IFRAME.bin", "rb"); - assert(openFile != NULL); - fseek(openFile, 0, SEEK_END); - _payloadDataFileLength = (WebRtc_Word16)(ftell(openFile)); - rewind(openFile); - assert(_payloadDataFileLength > 0); - fread(_payloadDataFile, 1, _payloadDataFileLength, openFile); - fclose(openFile); - - // send frame (1998/2000) - printf("\tSending H263(1998) frame. Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,124, timestamp, _payloadDataFile, _payloadDataFileLength)); - - memcpy(payloadNameVideo, "H263",5); - assert(0 == rtpRtcpModuleVideo->RegisterSendPayload(payloadNameVideo, 34)); - assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload(payloadNameVideo, 34)); - - timestamp += 3000; - - // send frame - printf("\tSending H263 frame. Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - timestamp += 3000; - - // lower MTU -> mode B - printf("\tSending H263 frame (MTU 300). Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SetMaxTransferUnit(300)); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - - timestamp += 3000; - // get frame w/ non-byte aligned GOB headers - openFile = fopen("H263_QCIF_IFRAME.bin", "rb"); - assert(openFile != NULL); - fseek(openFile, 0, SEEK_END); - _payloadDataFileLength = (WebRtc_Word16)(ftell(openFile)); - rewind(openFile); - assert(_payloadDataFileLength > 0); - fread(_payloadDataFile, 1, _payloadDataFileLength, openFile); - fclose(openFile); - - // send frame - printf("\tSending H263 frame (MTU 1500). Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SetMaxTransferUnit(1500)); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameKey,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - timestamp += 3000; - - // lower MTU -> mode B - printf("\tSending H263 frame (MTU 300). Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SetMaxTransferUnit(300)); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameKey,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - timestamp += 3000; - - openFile = fopen("H263_CIF_PFRAME.bin", "rb"); - assert(openFile != NULL); - fseek(openFile, 0, SEEK_END); - _payloadDataFileLength = (WebRtc_Word16)(ftell(openFile)); - rewind(openFile); - assert(_payloadDataFileLength > 0); - fread(_payloadDataFile, 1, _payloadDataFileLength, openFile); - fclose(openFile); - - // test H.263 without all GOBs - assert(0 == rtpRtcpModuleVideo->SetMaxTransferUnit(1500)); - printf("\tSending H263 frame without all GOBs (MTU 1500). Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - timestamp += 3000; - - // test H.263 without all GOBs small MTU - assert(0 == rtpRtcpModuleVideo->SetMaxTransferUnit(500)); - printf("\tSending H263 frame without all GOBs (MTU 500). Length: %d\n", _payloadDataFileLength); - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameDelta,34, timestamp, _payloadDataFile, _payloadDataFileLength)); - - // test PLI with relay - assert(0 == rtpRtcpModuleVideo->RegisterIncomingVideoCallback(myVideoFeedback)); - assert(0 == rtpRtcpModuleVideoReceiver->SetKeyFrameRequestMethod(kKeyFrameReqPliRtcp)); - assert(0 == rtpRtcpModuleVideoReceiver->RequestKeyFrame()); - - - processThread->DeRegisterModule(rtpRtcpModuleVideo); - processThread->DeRegisterModule(rtpRtcpModuleVideoReceiver); - processThread->Stop(); - ProcessThread::DestroyProcessThread(processThread); - - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideoReceiver); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideoRelay); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideoRelay2); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo); -#endif // TEST_VIDEO - - printf("\nAPI test of RTP/RTCP module done\n"); - -#ifdef TEST_AUDIO - delete myLoopBackTransport1; - delete myLoopBackTransport2; - delete myDataReceiver1; - delete myDataReceiver2; - delete myRTCPFeedback1; - delete myRTCPFeedback2; - delete audioFeedback; - delete myRTPCallback; -#endif -#ifdef TEST_VIDEO - delete myLoopBackTransportVideo; - delete myVideoFeedback; - delete myDataReceiverVideo; - delete myRelayDataReceiver; - delete myRelaySender; - delete myRelayReceiver; - - delete myRelaySender2; - delete myRelayReceiver2; - delete myRelayDataReceiver2; - delete myDataReceive2; - - delete myRTCPFeedbackVideo; - delete myRTCPFeedbackRealy; - delete myRTCPFeedbackReceiver; - delete myRTCPFeedbackRealy2; - -#endif // TEST_VIDEO - - ::Sleep(5000); - Trace::ReturnTrace(); - return 0; -} - diff --git a/modules/rtp_rtcp/test/testFec/test_fec.cc b/modules/rtp_rtcp/test/testFec/test_fec.cc deleted file mode 100644 index 111682950..000000000 --- a/modules/rtp_rtcp/test/testFec/test_fec.cc +++ /dev/null @@ -1,569 +0,0 @@ -/** - * Test application for core FEC algorithm. Calls encoding and decoding functions in - * ForwardErrorCorrection directly. - * - */ - -#include -#include -#include -#include -#include - -#include "forward_error_correction.h" -#include "forward_error_correction_internal.h" - -#include "list_wrapper.h" -#include "rtp_utility.h" - -#define VERBOSE_OUTPUT - -void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList, - WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate); - -int main() -{ - enum { kMaxNumberMediaPackets = 48 }; - enum { kMaxNumberFecPackets = 48 }; - - // Use same values as set in forward_correction.cc - const WebRtc_UWord8 rtpHeaderSize = 12; - const bool kUEP = true; - const WebRtc_UWord32 kForceFecThr = 1; - - WebRtc_UWord32 id = 0; - webrtc::ForwardErrorCorrection fec(id); - - // FOR UEP test - WebRtc_UWord32 numImpPackets = 0; - - webrtc::ListWrapper mediaPacketList; - webrtc::ListWrapper fecPacketList; - webrtc::ListWrapper toDecodeList; - webrtc::ListWrapper receivedPacketList; - webrtc::ListWrapper recoveredPacketList; - webrtc::ListWrapper fecMaskList; - webrtc::ForwardErrorCorrection::Packet* mediaPacket; - const float lossRate[] = {0, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 0.9f}; - const WebRtc_UWord32 lossRateSize = sizeof(lossRate)/sizeof(*lossRate); - const float reorderRate = 0.1f; - const float duplicateRate = 0.1f; - - WebRtc_UWord8 mediaLossMask[kMaxNumberMediaPackets]; - WebRtc_UWord8 fecLossMask[kMaxNumberFecPackets]; - WebRtc_UWord8 fecPacketMasks[kMaxNumberFecPackets][kMaxNumberMediaPackets]; - - // Seed the random number generator, storing the seed to file in order to reproduce - // past results. - const unsigned int randomSeed = static_cast(time(NULL)); - srand(randomSeed); - FILE* randomSeedFile = fopen("randomSeedLog.txt", "a"); - fprintf(randomSeedFile, "%u\n", randomSeed); - fclose(randomSeedFile); - randomSeedFile = NULL; - - WebRtc_UWord16 seqNum = static_cast(rand()); - WebRtc_UWord32 timeStamp = static_cast(rand()); - const WebRtc_UWord32 ssrc = static_cast(rand()); - - for (WebRtc_UWord32 lossRateIdx = 0; lossRateIdx < lossRateSize; lossRateIdx++) - { - printf("Loss rate: %.2f\n", lossRate[lossRateIdx]); - for (WebRtc_UWord32 numMediaPackets = 1; numMediaPackets <= kMaxNumberMediaPackets; - numMediaPackets++) - { - - for (WebRtc_UWord32 numFecPackets = 1; numFecPackets <= numMediaPackets && - numFecPackets <= kMaxNumberFecPackets; numFecPackets++) - { - - // loop over all possible numImpPackets - for (WebRtc_UWord32 numImpPackets = 0; numImpPackets <= numMediaPackets && - numImpPackets <= kMaxNumberMediaPackets; numImpPackets++) - { - - WebRtc_UWord8 protectionFactor = static_cast - (numFecPackets * 255 / numMediaPackets); - - - WebRtc_UWord32 maskBytesPerFecPacket = 2; - if (numMediaPackets > 16) - { - maskBytesPerFecPacket = 6; - } - - // Transfer packet masks from bit-mask to byte-mask. - WebRtc_UWord8 packetMask[numFecPackets * maskBytesPerFecPacket]; - memset(packetMask, 0, numFecPackets * maskBytesPerFecPacket); - - webrtc::internal::GeneratePacketMasks(numMediaPackets,numFecPackets, - numImpPackets, packetMask); - -#ifdef VERBOSE_OUTPUT - printf("%u media packets, %u FEC packets, %u numImpPackets, " - "loss rate = %.2f \n", - numMediaPackets, numFecPackets, numImpPackets, lossRate[lossRateIdx]); - printf("Packet mask matrix \n"); -#endif - - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - for (WebRtc_UWord32 j = 0; j < numMediaPackets; j++) - { - const WebRtc_UWord8 byteMask = - packetMask[i * maskBytesPerFecPacket + j / 8]; - const WebRtc_UWord32 bitPosition = (7 - j % 8); - fecPacketMasks[i][j] = - (byteMask & (1 << bitPosition)) >> bitPosition; -#ifdef VERBOSE_OUTPUT - printf("%u ", fecPacketMasks[i][j]); -#endif - } -#ifdef VERBOSE_OUTPUT - printf("\n"); -#endif - } -#ifdef VERBOSE_OUTPUT - printf("\n"); -#endif - // Check for all zero rows or columns: indicates incorrect mask - WebRtc_UWord32 rowLimit = numMediaPackets; - if (numFecPackets <= numImpPackets && kUEP == true) - { - rowLimit = numImpPackets; - } - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - WebRtc_UWord32 rowSum = 0; - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) - { - rowSum += fecPacketMasks[i][j]; - } - if (rowSum == 0) - { - printf("ERROR: row is all zero %d \n",i); - return -1; - } - } - for (WebRtc_UWord32 j = 0; j < rowLimit; j++) - { - WebRtc_UWord32 columnSum = 0; - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - columnSum += fecPacketMasks[i][j]; - } - if (columnSum == 0) - { - printf("ERROR: column is all zero %d \n",j); - return -1; - } - } - - // Construct media packets. - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - mediaPacket = new webrtc::ForwardErrorCorrection::Packet; - mediaPacketList.PushBack(mediaPacket); - mediaPacket->length = - static_cast((static_cast(rand()) / - RAND_MAX) * (IP_PACKET_SIZE - 12 - 28 - - webrtc::ForwardErrorCorrection::PacketOverhead())); - if (mediaPacket->length < 12) - { - mediaPacket->length = 12; - } - // Set the RTP version to 2. - mediaPacket->data[0] |= 0x80; // Set the 1st bit. - mediaPacket->data[0] &= 0xbf; // Clear the 2nd bit. - - mediaPacket->data[1] &= 0x7f; // Clear marker bit. - webrtc::ModuleRTPUtility::AssignUWord16ToBuffer(&mediaPacket->data[2], - seqNum); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[4], - timeStamp); - webrtc::ModuleRTPUtility::AssignUWord32ToBuffer(&mediaPacket->data[8], - ssrc); - - for (WebRtc_Word32 j = 12; j < mediaPacket->length; j++) - { - mediaPacket->data[j] = - static_cast((static_cast(rand()) / - RAND_MAX) * 255); - } - seqNum++; - } - mediaPacket->data[1] |= 0x80; // Set the marker bit of the last packet. - - if (fec.GenerateFEC(mediaPacketList, protectionFactor, numImpPackets, - fecPacketList) != 0) - { - printf("Error: GenerateFEC() failed\n"); - return -1; - } - - if (fecPacketList.GetSize() != numFecPackets) - { - printf("Error: we requested %u FEC packets, " - "but GenerateFEC() produced %u\n", - numFecPackets, fecPacketList.GetSize()); - return -1; - } - memset(mediaLossMask, 0, sizeof(mediaLossMask)); - webrtc::ListItem* mediaPacketListItem = mediaPacketList.First(); - webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket; - WebRtc_UWord32 mediaPacketIdx = 0; - - while (mediaPacketListItem != NULL) - { - mediaPacket = static_cast - (mediaPacketListItem->GetItem()); - const float lossRandomVariable = (static_cast(rand()) / - (RAND_MAX)); - - if (lossRandomVariable >= lossRate[lossRateIdx]) - { - mediaLossMask[mediaPacketIdx] = 1; - receivedPacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new webrtc::ForwardErrorCorrection::Packet; - receivedPacketList.PushBack(receivedPacket); - - receivedPacket->pkt->length = mediaPacket->length; - memcpy(receivedPacket->pkt->data, mediaPacket->data, - mediaPacket->length); - receivedPacket->seqNum = - webrtc::ModuleRTPUtility::BufferToUWord16(&mediaPacket->data[2]); - receivedPacket->isFec = false; - receivedPacket->lastMediaPktInFrame = - mediaPacket->data[1] & 0x80; - } - mediaPacketIdx++; - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - } - memset(fecLossMask, 0, sizeof(fecLossMask)); - webrtc::ListItem* fecPacketListItem = fecPacketList.First(); - webrtc::ForwardErrorCorrection::Packet* fecPacket; - WebRtc_UWord32 fecPacketIdx = 0; - while (fecPacketListItem != NULL) - { - fecPacket = static_cast - (fecPacketListItem->GetItem()); - const float lossRandomVariable = - (static_cast(rand()) / (RAND_MAX)); - if (lossRandomVariable >= lossRate[lossRateIdx]) - { - fecLossMask[fecPacketIdx] = 1; - receivedPacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - receivedPacket->pkt = - new webrtc::ForwardErrorCorrection::Packet; - receivedPacketList.PushBack(receivedPacket); - - receivedPacket->pkt->length = fecPacket->length; - memcpy(receivedPacket->pkt->data, fecPacket->data, - fecPacket->length); - - receivedPacket->seqNum = seqNum; - receivedPacket->isFec = true; - receivedPacket->lastMediaPktInFrame = false; - receivedPacket->ssrc = ssrc; - - fecMaskList.PushBack(fecPacketMasks[fecPacketIdx]); - } - fecPacketIdx++; - seqNum++; - fecPacketListItem = fecPacketList.Next(fecPacketListItem); - } - -#ifdef VERBOSE_OUTPUT - printf("Media loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - printf("%u ", mediaLossMask[i]); - } - printf("\n\n"); - - printf("FEC loss mask:\n"); - for (WebRtc_UWord32 i = 0; i < numFecPackets; i++) - { - printf("%u ", fecLossMask[i]); - } - printf("\n\n"); -#endif - - webrtc::ListItem* listItem = fecMaskList.First(); - WebRtc_UWord8* fecMask; - while (listItem != NULL) - { - fecMask = static_cast(listItem->GetItem()); - WebRtc_UWord32 hammingDist = 0; - WebRtc_UWord32 recoveryPosition = 0; - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - if (mediaLossMask[i] == 0 && fecMask[i] == 1) - { - recoveryPosition = i; - hammingDist++; - } - } - webrtc::ListItem* itemToDelete = listItem; - listItem = fecMaskList.Next(listItem); - - if (hammingDist == 1) - { - // Recovery possible. Restart search. - mediaLossMask[recoveryPosition] = 1; - listItem = fecMaskList.First(); - } - else if (hammingDist == 0) - { - // FEC packet cannot provide further recovery. - fecMaskList.Erase(itemToDelete); - } - } -#ifdef VERBOSE_OUTPUT - printf("Recovery mask:\n"); - for (WebRtc_UWord32 i = 0; i < numMediaPackets; i++) - { - printf("%u ", mediaLossMask[i]); - } - printf("\n\n"); -#endif - bool complete = true; // Marks start of new frame. - bool fecPacketReceived = false; // For error-checking frame completion. - while (!receivedPacketList.Empty()) - { - WebRtc_UWord32 numPacketsToDecode = static_cast - ((static_cast(rand()) / RAND_MAX) * - receivedPacketList.GetSize() + 0.5); - if (numPacketsToDecode < 1) - { - numPacketsToDecode = 1; - } - - ReceivePackets(toDecodeList, receivedPacketList, numPacketsToDecode, - reorderRate, duplicateRate); - - if (fecPacketReceived == false) - { - listItem = toDecodeList.First(); - while (listItem != NULL) - { - receivedPacket = - static_cast(listItem->GetItem()); - if (receivedPacket->isFec) - { - fecPacketReceived = true; - } - listItem = toDecodeList.Next(listItem); - } - } - - if (fec.DecodeFEC(toDecodeList, recoveredPacketList, seqNum, - complete) != 0) - { - printf("Error: DecodeFEC() failed\n"); - return -1; - } - - if (!toDecodeList.Empty()) - { - printf("Error: received packet list is not empty\n"); - return -1; - } - - if (recoveredPacketList.GetSize() == numMediaPackets && - fecPacketReceived == true) - { - if (complete == true) - { -#ifdef VERBOSE_OUTPUT - printf("Full frame recovery correctly marked\n\n"); -#endif - break; - } - else - { - printf("Error: " - "it should be possible to verify full frame recovery," - " but complete parameter was set to false\n"); - return -1; - } - } - else - { - if (complete == true) - { - printf("Error: " - "it should not be possible to verify full frame recovery," - " but complete parameter was set to true\n"); - return -1; - } - } - } - - mediaPacketListItem = mediaPacketList.First(); - mediaPacketIdx = 0; - while (mediaPacketListItem != NULL) - { - if (mediaLossMask[mediaPacketIdx] == 1) - { - // Should have recovered this packet. - webrtc::ListItem* recoveredPacketListItem = - recoveredPacketList.First(); - mediaPacket = - static_cast - (mediaPacketListItem->GetItem()); - - if (recoveredPacketListItem == NULL) - { - printf("Error: insufficient number of recovered packets.\n"); - return -1; - } - - webrtc::ForwardErrorCorrection::RecoveredPacket* recoveredPacket = - static_cast - (recoveredPacketListItem->GetItem()); - - if (recoveredPacket->pkt->length != mediaPacket->length) - { - printf("Error: recovered packet length not identical to " - "original media packet\n"); - return -1; - } - if (memcmp(recoveredPacket->pkt->data, mediaPacket->data, - mediaPacket->length) != 0) - { - printf("Error: recovered packet payload not identical to " - "original media packet\n"); - return -1; - } - delete recoveredPacket->pkt; - delete recoveredPacket; - recoveredPacket = NULL; - recoveredPacketList.PopFront(); - } - mediaPacketIdx++; - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - } - - if (!recoveredPacketList.Empty()) - { - printf("Error: excessive number of recovered packets.\n"); - return -1; - } - // -- Teardown -- - mediaPacketListItem = mediaPacketList.First(); - while (mediaPacketListItem != NULL) - { - delete static_cast - (mediaPacketListItem->GetItem()); - mediaPacketListItem = mediaPacketList.Next(mediaPacketListItem); - mediaPacketList.PopFront(); - } - assert(mediaPacketList.Empty()); - - fecPacketListItem = fecPacketList.First(); - while (fecPacketListItem != NULL) - { - fecPacketListItem = fecPacketList.Next(fecPacketListItem); - fecPacketList.PopFront(); - } - - // Delete received packets we didn't pass to DecodeFEC(), due to early - // frame completion. - listItem = receivedPacketList.First(); - while (listItem != NULL) - { - receivedPacket = - static_cast - (listItem->GetItem()); - delete receivedPacket->pkt; - delete receivedPacket; - receivedPacket = NULL; - listItem = receivedPacketList.Next(listItem); - receivedPacketList.PopFront(); - } - assert(receivedPacketList.Empty()); - - while (fecMaskList.First() != NULL) - { - fecMaskList.PopFront(); - } - timeStamp += 90000 / 30; - - } //loop over numImpPackets - } //loop over FecPackets - } //loop over numMediaPackets - } // loop over loss rates - - // Have DecodeFEC free allocated memory. - bool complete = true; - fec.DecodeFEC(receivedPacketList, recoveredPacketList, seqNum, complete); - if (!recoveredPacketList.Empty()) - { - printf("Error: recovered packet list is not empty\n"); - return -1; - } - - printf("\nAll tests passed successfully\n"); - - return 0; -} - - - -void ReceivePackets(webrtc::ListWrapper& toDecodeList, webrtc::ListWrapper& receivedPacketList, - WebRtc_UWord32 numPacketsToDecode, float reorderRate, float duplicateRate) -{ - assert(toDecodeList.Empty()); - assert(numPacketsToDecode <= receivedPacketList.GetSize()); - - webrtc::ListItem* listItem = receivedPacketList.First(); - for (WebRtc_UWord32 i = 0; i < numPacketsToDecode; i++) - { - // Reorder packets. - float randomVariable = static_cast(rand()) / RAND_MAX; - while (randomVariable < reorderRate) - { - webrtc::ListItem* nextItem = receivedPacketList.Next(listItem); - if (nextItem == NULL) - { - break; - } - else - { - listItem = nextItem; - } - randomVariable = static_cast(rand()) / RAND_MAX; - } - - assert(listItem != NULL); - webrtc::ForwardErrorCorrection::ReceivedPacket* receivedPacket = - static_cast(listItem->GetItem()); - toDecodeList.PushBack(receivedPacket); - - // Duplicate packets. - randomVariable = static_cast(rand()) / RAND_MAX; - while (randomVariable < duplicateRate) - { - webrtc::ForwardErrorCorrection::ReceivedPacket* duplicatePacket = - new webrtc::ForwardErrorCorrection::ReceivedPacket; - memcpy(duplicatePacket, receivedPacket, - sizeof(webrtc::ForwardErrorCorrection::ReceivedPacket)); - - duplicatePacket->pkt = new webrtc::ForwardErrorCorrection::Packet; - memcpy(duplicatePacket->pkt->data, receivedPacket->pkt->data, - receivedPacket->pkt->length); - duplicatePacket->pkt->length = receivedPacket->pkt->length; - - toDecodeList.PushBack(duplicatePacket); - randomVariable = static_cast(rand()) / RAND_MAX; - } - - receivedPacketList.Erase(listItem); - listItem = receivedPacketList.First(); - } -} diff --git a/modules/rtp_rtcp/test/testFec/test_fec.gyp b/modules/rtp_rtcp/test/testFec/test_fec.gyp deleted file mode 100644 index c5830a800..000000000 --- a/modules/rtp_rtcp/test/testFec/test_fec.gyp +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'test_fec', - 'type': 'executable', - 'dependencies': [ - '../../source/rtp_rtcp.gyp:rtp_rtcp', - ], - - 'include_dirs': [ - '../../source', - '../../../../system_wrappers/interface', - ], - - 'sources': [ - 'test_fec.cc', - ], - - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: \ No newline at end of file diff --git a/modules/rtp_rtcp/test/testH263Parser/H263Foreman_CIF_Iframe.bin b/modules/rtp_rtcp/test/testH263Parser/H263Foreman_CIF_Iframe.bin deleted file mode 100644 index 00e3f8058ee68190046d8cbdbe6461995dc0a39a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14849 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�F3-A(vdY>M%wLVhqMPcB?%^VF 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 M%bzk1xhjSao;lBoA^-pY diff --git a/modules/rtp_rtcp/test/testH263Parser/testH263Parser.cpp b/modules/rtp_rtcp/test/testH263Parser/testH263Parser.cpp deleted file mode 100644 index e4c36d1c2..000000000 --- a/modules/rtp_rtcp/test/testH263Parser/testH263Parser.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include - -#include "h263_information.h" - -#define TEST_STR "Test H263 parser." -#define TEST_PASSED() std::cerr << TEST_STR << " : [OK]" << std::endl -#define PRINT_LINE std::cout << "------------------------------------------" << std::endl; - - -void PrintInfo(const H263Info* ptr) -{ - std::cout << "info = GOBs: {"; - for (int i = 0; i < ptr->numOfGOBs; i++) - { - std::cout << ptr->ptrGOBbuffer[i] << " "; - } - std::cout << "}" << std::endl; - std::cout << " sBit: {"; - for (int i = 0; i < ptr->numOfGOBs; i++) - { - std::cout << (int)ptr->ptrGOBbufferSBit[i] << " "; - } - std::cout << "}" << std::endl; - - std::cout << "uiH263PTypeFmt: " << (int)ptr->uiH263PTypeFmt << std::endl; - std::cout << " codecBits: " << (int)ptr->codecBits << std::endl; - std::cout << " pQuant: " << (int)ptr->pQuant << std::endl; - std::cout << " fType: " << (int)ptr->fType << std::endl; - std::cout << " cpmBit: " << (int)ptr->cpmBit << std::endl; - std::cout << " numOfGOBs: " << (int)ptr->numOfGOBs << std::endl; - std::cout << " numOfMBs: " << (int)ptr->totalNumOfMBs << std::endl; - std::cout << " " << std::endl; -}; - -void PrintMBInfo( - const H263Info* ptr, - const H263MBInfo* ptrMB) -{ - std::cout << " gQuant: {"; - for (int i = 0; i < ptr->numOfGOBs; i++) - { - std::cout << (int)ptr->ptrGQuant[i] << " "; - } - std::cout << "}" << std::endl; - std::cout << " " << std::endl; - - std::cout << "MBs:{"; - int k = 0; - for (int i = 0; i < ptr->numOfGOBs; i++) - { - std::cout << " {"; - for (int j = 0; j < ptr->ptrNumOfMBs[i]; j++) - { - std::cout << ptrMB->ptrBuffer[k++] << " "; - } - std::cout << "}" << std::endl; - } - std::cout << "}" << std::endl; - PRINT_LINE; -}; - -void ValidateResults( - const H263Info* ptr, - const H263Info* ptrRef) -{ - assert(ptr->uiH263PTypeFmt == ptrRef->uiH263PTypeFmt); - assert(ptr->codecBits == ptrRef->codecBits); - assert(ptr->pQuant == ptrRef->pQuant); - assert(ptr->fType == ptrRef->fType); - assert(ptr->numOfGOBs == ptrRef->numOfGOBs); - assert(ptr->totalNumOfMBs == ptrRef->totalNumOfMBs); - for (int i = 0; i < ptr->numOfGOBs; i++) - { - assert(ptr->ptrGOBbuffer[i] == ptrRef->ptrGOBbuffer[i]); - assert(ptr->ptrGOBbufferSBit[i] == ptrRef->ptrGOBbufferSBit[i]); - } - PrintInfo(ptr); -} - -void ValidateMBResults( - const H263Info* ptr, - const H263MBInfo* ptrMB, - const H263Info* ptrRef, - bool printRes = true) -{ - int offset = 0; - int numBytes = 0; - for (int i = 0; i < ptr->numOfGOBs; i++) - { - offset = ptr->CalculateMBOffset(i+1); - numBytes += ptrMB->ptrBuffer[offset - 1] / 8; - int numBytesRem = ptrMB->ptrBuffer[offset - 1] % 8; - if (numBytesRem) - { - numBytes++; - } - assert(ptr->ptrGQuant[i] == ptrRef->ptrGQuant[i]); - } - assert(ptr->ptrGOBbuffer[ptr->numOfGOBs] == numBytes); - assert(unsigned int( ptr->totalNumOfMBs) <= ptrMB->bufferSize); - if (printRes) - { - PrintMBInfo(ptr, ptrMB); - } -} - - -int _tmain(int argc, _TCHAR* argv[]) -{ - - std::string str; - std::cout << "--------------------------------" << std::endl; - std::cout << "------- Test H.263 Parser ------" << std::endl; - std::cout << "--------------------------------" << std::endl; - std::cout << " " << std::endl; - - // ----------------------------------------------------- - // Input data - H.263 encoded stream SQCIF (P-frame) - // ----------------------------------------------------- - const int lengthRefSQCIF = 77; - const unsigned char encodedStreamSQCIF[lengthRefSQCIF] = { - 0x00, 0x00, 0x81, 0xf6, 0x06, 0x04, 0x3f, 0xb7, 0xbc, 0x00, 0x00, 0x86, 0x23, - 0x5b, 0xdb, 0xdf, 0xb1, 0x93, 0xdb, 0xde, 0xd6, 0xf0, 0x00, 0x00, 0x8a, 0x27, - 0xdb, 0xcf, 0xad, 0xbe, 0x00, 0x00, 0x8e, 0x27, 0xed, 0xef, 0x80, 0x00, 0x00, - 0x92, 0x27, 0x6f, 0x7f, 0x80, 0x00, 0x00, 0x96, 0x20, 0xfc, 0xe2, 0xdb, 0xfe, - 0xb7, 0x7d, 0xea, 0x5f, 0xf8, 0xab, 0xd2, 0xff, 0xf6, 0xc9, 0xe5, 0x5e, 0x97, - 0xf7, 0xff, 0xad, 0x4f, 0x49, 0x3b, 0xff, 0xd6, 0xa6, 0x75, 0x82, 0x60}; - - // Expected results - H263Info infoRefSQCIF; - infoRefSQCIF.uiH263PTypeFmt = 1; - infoRefSQCIF.codecBits = 8; - infoRefSQCIF.pQuant = 4; - infoRefSQCIF.fType = 1; - infoRefSQCIF.cpmBit = 0; - infoRefSQCIF.numOfGOBs = 6; - infoRefSQCIF.totalNumOfMBs = 8*6; - - infoRefSQCIF.ptrGOBbuffer[0] = 0; infoRefSQCIF.ptrGOBbufferSBit[0] = 0; infoRefSQCIF.ptrGQuant[0] = 0; - infoRefSQCIF.ptrGOBbuffer[1] = 9; infoRefSQCIF.ptrGOBbufferSBit[1] = 0; infoRefSQCIF.ptrGQuant[1] = 4; - infoRefSQCIF.ptrGOBbuffer[2] = 22; infoRefSQCIF.ptrGOBbufferSBit[2] = 0; infoRefSQCIF.ptrGQuant[2] = 4; - infoRefSQCIF.ptrGOBbuffer[3] = 30; infoRefSQCIF.ptrGOBbufferSBit[3] = 0; infoRefSQCIF.ptrGQuant[3] = 4; - infoRefSQCIF.ptrGOBbuffer[4] = 37; infoRefSQCIF.ptrGOBbufferSBit[4] = 0; infoRefSQCIF.ptrGQuant[4] = 4; - infoRefSQCIF.ptrGOBbuffer[5] = 44; infoRefSQCIF.ptrGOBbufferSBit[5] = 0; infoRefSQCIF.ptrGQuant[5] = 4; - - // ---------------------------------------------------- - // Input data - H.263 encoded stream QCIF (P-frame) - // ---------------------------------------------------- - const int lengthRefQCIF = 123; - const unsigned char encodedStreamQCIF[lengthRefQCIF] = { - 0x00, 0x00, 0x81, 0x02, 0x0a, 0x04, 0x3f, 0xf8, 0x00, 0x00, 0x86, 0x27, 0x8b, - 0xc6, 0x9f, 0x17, 0x9c, 0x00, 0x00, 0x8a, 0x20, 0xbc, 0x22, 0xf8, 0x5f, 0x46, - 0x03, 0xc1, 0x77, 0x15, 0xe0, 0xb8, 0x38, 0x3f, 0x05, 0xa0, 0xbf, 0x8f, 0x00, - 0x00, 0x8e, 0x27, 0xfc, 0x5e, 0x5a, 0x33, 0x80, 0x00, 0x00, 0x92, 0x25, 0x8c, - 0x1e, 0xbf, 0xfc, 0x7e, 0x35, 0xfc, 0x00, 0x00, 0x96, 0x27, 0xff, 0x00, 0x00, - 0x9a, 0x20, 0xdb, 0x34, 0xef, 0xfc, 0x00, 0x00, 0x9e, 0x20, 0xaf, 0x17, 0x0d, - 0x3e, 0xde, 0x0f, 0x8f, 0xff, 0x80, 0x00, 0x00, 0xa2, 0x22, 0xbb, 0x27, 0x81, - 0xeb, 0xff, 0x5b, 0x07, 0xab, 0xff, 0xad, 0x9e, 0xd8, 0xc9, 0x6b, 0x75, 0x54, - 0xbf, 0xbe, 0x8a, 0xbd, 0xf2, 0xfb, 0xfb, 0x3d, 0x3d, 0x25, 0xb7, 0xf7, 0xfc, - 0x92, 0x4c, 0xdb, 0x6d, 0x69, 0xc0}; - - // Expected results - H263Info infoRefQCIF; - infoRefQCIF.uiH263PTypeFmt = 2; - infoRefQCIF.codecBits = 8; - infoRefQCIF.pQuant = 4; - infoRefQCIF.fType = 1; - infoRefQCIF.cpmBit = 0; - infoRefQCIF.numOfGOBs = 9; - infoRefQCIF.totalNumOfMBs = 11*9; - - infoRefQCIF.ptrGOBbuffer[0] = 0; infoRefQCIF.ptrGOBbufferSBit[0] = 0; infoRefQCIF.ptrGQuant[0] = 0; - infoRefQCIF.ptrGOBbuffer[1] = 8; infoRefQCIF.ptrGOBbufferSBit[1] = 0; infoRefQCIF.ptrGQuant[1] = 4; - infoRefQCIF.ptrGOBbuffer[2] = 17; infoRefQCIF.ptrGOBbufferSBit[2] = 0; infoRefQCIF.ptrGQuant[2] = 4; - infoRefQCIF.ptrGOBbuffer[3] = 38; infoRefQCIF.ptrGOBbufferSBit[3] = 0; infoRefQCIF.ptrGQuant[3] = 4; - infoRefQCIF.ptrGOBbuffer[4] = 47; infoRefQCIF.ptrGOBbufferSBit[4] = 0; infoRefQCIF.ptrGQuant[4] = 4; - infoRefQCIF.ptrGOBbuffer[5] = 58; infoRefQCIF.ptrGOBbufferSBit[5] = 0; infoRefQCIF.ptrGQuant[5] = 4; - infoRefQCIF.ptrGOBbuffer[6] = 63; infoRefQCIF.ptrGOBbufferSBit[6] = 0; infoRefQCIF.ptrGQuant[6] = 4; - infoRefQCIF.ptrGOBbuffer[7] = 71; infoRefQCIF.ptrGOBbufferSBit[7] = 0; infoRefQCIF.ptrGQuant[7] = 4; - infoRefQCIF.ptrGOBbuffer[8] = 84; infoRefQCIF.ptrGOBbufferSBit[8] = 0; infoRefQCIF.ptrGQuant[8] = 4; - - // --------------------------------------------------- - // Input data - H.263 encoded stream CIF (P-frame) - // --------------------------------------------------- - const int lengthRefCIF = 212; - const unsigned char encodedStreamCIF[lengthRefCIF] = { - 0x00, 0x00, 0x82, 0x9a, 0x0e, 0x04, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x86, 0x27, - 0xff, 0xff, 0xe0, 0x00, 0x00, 0x8a, 0x27, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x8e, - 0x27, 0xff, 0xff, 0x6b, 0x09, 0x70, 0x00, 0x00, 0x92, 0x27, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x96, 0x27, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x9a, 0x27, 0x6f, 0x7f, - 0xff, 0xfe, 0x00, 0x00, 0x9e, 0x27, 0xff, 0xfe, 0xc6, 0x31, 0xe0, 0x00, 0x00, - 0xa2, 0x27, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xa6, 0x27, 0xf6, 0xb7, 0xfe, 0xa6, - 0x14, 0x95, 0xb4, 0xc6, 0x41, 0x6b, 0x3a, 0x2e, 0x8d, 0x42, 0xef, 0xc0, 0x00, - 0x00, 0xaa, 0x27, 0xff, 0xb1, 0x95, 0x05, 0x0c, 0xe3, 0x4a, 0x17, 0xff, 0x80, - 0x00, 0x00, 0xae, 0x27, 0xff, 0xf6, 0xf7, 0xfe, 0x00, 0x00, 0xb2, 0x27, 0xff, - 0x8b, 0xdf, 0xff, 0x00, 0x00, 0xb6, 0x27, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xba, - 0x26, 0x2f, 0x7f, 0xff, 0xfb, 0x58, 0x5b, 0x80, 0x00, 0x00, 0xbe, 0x20, 0xbc, - 0xe4, 0x5e, 0x6f, 0xff, 0xfe, 0xce, 0xf1, 0x94, 0x00, 0x00, 0xc2, 0x23, 0x18, - 0x4b, 0x17, 0x87, 0x0f, 0xff, 0xb3, 0xb6, 0x09, 0x63, 0x46, 0x73, 0x40, 0xb2, - 0x5f, 0x0a, 0xc6, 0xbe, 0xd7, 0x7a, 0x61, 0xbc, 0x68, 0xde, 0xf0, 0x00, 0x00, - 0xc6, 0x26, 0x2f, 0x0a, 0xff, 0xff, 0x17, 0x9a, 0x17, 0x8c, 0xb1, 0x37, 0x67, - 0xaf, 0xec, 0xf7, 0xa5, 0xbf, 0xb6, 0x49, 0x27, 0x6d, 0xb7, 0x92, 0x49, 0x1b, - 0xb4, 0x9a, 0xe0, 0x62}; - - // Expected results - H263Info infoRefCIF; - infoRefCIF.uiH263PTypeFmt = 3; - infoRefCIF.codecBits = 8; - infoRefCIF.pQuant = 4; - infoRefCIF.fType = 1; - infoRefCIF.cpmBit = 0; - infoRefCIF.numOfGOBs = 18; - infoRefCIF.totalNumOfMBs = 22*18; - - infoRefCIF.ptrGOBbuffer[0] = 0; infoRefCIF.ptrGOBbufferSBit[0] = 0; infoRefCIF.ptrGQuant[0] = 0; - infoRefCIF.ptrGOBbuffer[1] = 9; infoRefCIF.ptrGOBbufferSBit[1] = 0; infoRefCIF.ptrGQuant[1] = 4; - infoRefCIF.ptrGOBbuffer[2] = 16; infoRefCIF.ptrGOBbufferSBit[2] = 0; infoRefCIF.ptrGQuant[2] = 4; - infoRefCIF.ptrGOBbuffer[3] = 23; infoRefCIF.ptrGOBbufferSBit[3] = 0; infoRefCIF.ptrGQuant[3] = 4; - infoRefCIF.ptrGOBbuffer[4] = 32; infoRefCIF.ptrGOBbufferSBit[4] = 0; infoRefCIF.ptrGQuant[4] = 4; - infoRefCIF.ptrGOBbuffer[5] = 39; infoRefCIF.ptrGOBbufferSBit[5] = 0; infoRefCIF.ptrGQuant[5] = 4; - infoRefCIF.ptrGOBbuffer[6] = 46; infoRefCIF.ptrGOBbufferSBit[6] = 0; infoRefCIF.ptrGQuant[6] = 4; - infoRefCIF.ptrGOBbuffer[7] = 54; infoRefCIF.ptrGOBbufferSBit[7] = 0; infoRefCIF.ptrGQuant[7] = 4; - infoRefCIF.ptrGOBbuffer[8] = 63; infoRefCIF.ptrGOBbufferSBit[8] = 0; infoRefCIF.ptrGQuant[8] = 4; - infoRefCIF.ptrGOBbuffer[9] = 70; infoRefCIF.ptrGOBbufferSBit[9] = 0; infoRefCIF.ptrGQuant[9] = 4; - infoRefCIF.ptrGOBbuffer[10] = 90; infoRefCIF.ptrGOBbufferSBit[10] = 0; infoRefCIF.ptrGQuant[10] = 4; - infoRefCIF.ptrGOBbuffer[11] = 104; infoRefCIF.ptrGOBbufferSBit[11] = 0; infoRefCIF.ptrGQuant[11] = 4; - infoRefCIF.ptrGOBbuffer[12] = 112; infoRefCIF.ptrGOBbufferSBit[12] = 0; infoRefCIF.ptrGQuant[12] = 4; - infoRefCIF.ptrGOBbuffer[13] = 120; infoRefCIF.ptrGOBbufferSBit[13] = 0; infoRefCIF.ptrGQuant[13] = 4; - infoRefCIF.ptrGOBbuffer[14] = 127; infoRefCIF.ptrGOBbufferSBit[14] = 0; infoRefCIF.ptrGQuant[14] = 4; - infoRefCIF.ptrGOBbuffer[15] = 138; infoRefCIF.ptrGOBbufferSBit[15] = 0; infoRefCIF.ptrGQuant[15] = 4; - infoRefCIF.ptrGOBbuffer[16] = 151; infoRefCIF.ptrGOBbufferSBit[16] = 0; infoRefCIF.ptrGQuant[16] = 4; - infoRefCIF.ptrGOBbuffer[17] = 180; infoRefCIF.ptrGOBbufferSBit[17] = 0; infoRefCIF.ptrGQuant[17] = 4; - - // ----------------------------------------------------------------------- - // Input data - H.263 encoded stream QCIF (I-frame). Non byte aligned GOBs - // ----------------------------------------------------------------------- - const int lengthRefQCIF_N = 2020; - const unsigned char encodedStreamQCIF_N[lengthRefQCIF_N] = { - 0x00,0x00,0x80,0x06,0x08,0x07,0x34,0xe4,0xf7,0x66,0x75,0x12,0x9b,0x64,0x83,0xe9,0x4c,0xc7,0x3c,0x77,0x83,0xcf,0x67,0x96, - 0xe0,0x06,0x69,0x95,0x70,0x60,0x43,0x53,0x96,0x8a,0xa2,0x9e,0x96,0xf8,0x79,0xf0,0xf2,0xb8,0x30,0x21,0xb0,0x0c,0xc0,0x22, - 0x0e,0x70,0x12,0xb0,0x30,0x21,0x9e,0x03,0x3e,0x02,0x22,0xa8,0x83,0xcf,0x7d,0xe8,0xf3,0x85,0x8d,0x01,0x47,0x29,0x03,0x02, - 0x16,0x5c,0x06,0x7c,0x84,0x56,0x9c,0x0c,0x08,0x49,0x70,0x29,0xe1,0x68,0x39,0xbf,0xd0,0xef,0x6f,0x6e,0x8e,0x64,0x72,0x70, - 0x30,0x21,0x03,0xe0,0x53,0x82,0xab,0xe2,0xd4,0xa8,0x3e,0x12,0x80,0xc8,0x2a,0x9f,0x07,0x23,0xdf,0x4f,0xaa,0x5b,0x72,0xaa, - 0x22,0x81,0x21,0x7a,0x80,0x54,0x85,0x82,0x50,0x17,0x40,0x2a,0x7f,0x3c,0xfe,0xa5,0x49,0x39,0x08,0x18,0x20,0x65,0x95,0x8c, - 0x8f,0x67,0xc7,0xc0,0x63,0xe1,0x1b,0xf1,0xef,0xfd,0x25,0x13,0x24,0x9c,0xa9,0x1f,0x02,0x9b,0xe1,0x19,0xae,0x97,0x02,0x9a, - 0x84,0x6f,0xbf,0xfd,0x14,0xf2,0x09,0xe3,0x10,0x38,0x0a,0x61,0x99,0x6e,0xf8,0x14,0xce,0x7e,0xff,0xe7,0xa7,0x8e,0x4f,0x14, - 0xa9,0x80,0xa6,0x71,0x3a,0x98,0x06,0x1c,0xfc,0xff,0xcd,0x8f,0x18,0x9e,0x24,0x52,0x05,0x9c,0x42,0xa5,0x33,0x9f,0xdf,0x80, - 0x00,0x21,0x0e,0xf6,0x61,0xe2,0x7a,0x35,0x64,0x8e,0x61,0xa2,0x58,0x2a,0x0b,0x45,0x4c,0xe0,0xa1,0xa0,0xa6,0x83,0x58,0xf0, - 0x08,0x77,0x3e,0xb5,0xc6,0x05,0x6b,0x6e,0x69,0x06,0x07,0xe6,0x82,0x9b,0xc8,0x7e,0x2d,0x6d,0x06,0x07,0xe3,0xe0,0xa6,0x2e, - 0x05,0x58,0x39,0xbf,0xd1,0xc7,0x9e,0x15,0x29,0x39,0x31,0xbc,0x18,0x1f,0x62,0xf5,0x95,0x20,0xa0,0xe6,0xff,0x38,0x83,0x03, - 0xea,0x3f,0x02,0xc8,0x05,0x71,0x31,0xef,0x79,0x49,0x48,0x73,0x06,0x07,0xd4,0x4b,0x5f,0xc0,0xe4,0x81,0x3b,0x03,0x03,0xea, - 0x24,0x81,0x90,0x55,0x41,0x64,0x2b,0xea,0xd1,0xcc,0xa5,0x99,0x79,0x06,0x07,0xd8,0x49,0x05,0x39,0x64,0x16,0x3d,0x03,0x03, - 0xee,0x24,0x83,0x0b,0xf9,0x46,0x50,0x27,0xdf,0xd4,0xa6,0xa6,0x3d,0x83,0x03,0xf0,0x25,0x83,0x0b,0xf8,0x19,0xbe,0x83,0x03, - 0xf2,0x3f,0x06,0x17,0xf1,0x58,0xd6,0x04,0xff,0xfa,0x94,0xb4,0x87,0xe0,0x43,0x2f,0x06,0x17,0xf1,0x58,0x11,0x7b,0xe0,0x21, - 0x97,0x82,0x9d,0x58,0x11,0x7b,0xf9,0xf8,0x15,0xa2,0x9f,0x40,0x3e,0xf4,0x07,0xd5,0x81,0x9f,0xa1,0x7b,0xca,0x9a,0xbb,0xdf, - 0xcf,0xc0,0xf3,0xb0,0x09,0x37,0x00,0x97,0x63,0xee,0x47,0xff,0xff,0xe8,0x68,0xa5,0xc6,0x43,0xdf,0x2f,0x06,0xad,0xad,0x3f, - 0xef,0xf6,0x30,0x03,0xea,0x80,0xcf,0x80,0x8b,0x8a,0xe2,0xb5,0x40,0xa7,0x2e,0x06,0x27,0xf7,0xc2,0xd6,0x47,0x2f,0xc1,0x3f, - 0x80,0x00,0x44,0x1f,0xb5,0xbd,0xc5,0xed,0x4f,0x70,0x7c,0x43,0xec,0x8e,0xcf,0x6e,0xcf,0x7b,0x88,0x7d,0x9b,0xfc,0x13,0xfc, - 0x24,0x3b,0xf4,0xe1,0x61,0xe1,0xe2,0x61,0x5f,0xa7,0x13,0x15,0x17,0x1b,0x08,0xff,0x38,0x98,0x98,0xb8,0xb8,0x2f,0xf9,0xc3, - 0xc2,0xc5,0x43,0xc0,0xff,0xce,0x09,0xfa,0x16,0x06,0x07,0xfe,0x6f,0x4e,0xcf,0xaf,0x30,0x3f,0xf3,0x70,0x6c,0x74,0x6e,0x81, - 0xff,0x9b,0x33,0x13,0x53,0x2c,0x0f,0xf8,0x00,0x04,0x61,0xf0,0xd4,0xf7,0x17,0xb6,0x3a,0x3e,0x1d,0xfa,0x6f,0x0f,0xaf,0x4f, - 0xf0,0xef,0xd3,0x81,0x85,0x84,0x88,0x86,0x7e,0x9c,0x4c,0x64,0x64,0x84,0x2b,0xfc,0xe3,0xa4,0x24,0xa4,0xe0,0xdf,0xe7,0x1f, - 0x1d,0x27,0x25,0x03,0xff,0x38,0xd8,0xb9,0x19,0x0f,0xff,0xf9,0xc4,0x42,0xc6,0xc4,0xff,0xc0,0xce,0x05,0xf6,0x16,0x05,0xfe, - 0x06,0x6f,0x0e,0x4f,0x8e,0xef,0xf0,0x59,0x6d,0x68,0x72,0x3e,0xd9,0x1f,0xff,0x02,0x00,0x01,0x20,0x7c,0x3a,0x29,0x4c,0xf7, - 0xa5,0x34,0x0c,0x3d,0xb5,0xae,0x3a,0x1d,0xf5,0x6f,0xc3,0xb0,0x30,0x33,0x82,0x03,0xa0,0x60,0x66,0xbf,0x01,0xc0,0x04,0x5b, - 0x07,0x04,0x16,0x10,0xf3,0xf2,0xe1,0x94,0x17,0x81,0x82,0xd1,0x54,0x40,0xf0,0x7e,0xb4,0x2d,0x16,0x2d,0x83,0x82,0x0a,0xb8, - 0x0e,0x08,0x2c,0x11,0xe7,0xf5,0xc5,0x02,0x10,0x96,0xb7,0x8b,0x45,0x91,0x60,0xc0,0x83,0x89,0x60,0xa8,0x07,0x2b,0xf9,0x70, - 0x1c,0x10,0x65,0xb0,0x70,0x41,0xa0,0x8f,0x3f,0xae,0x28,0x18,0x10,0x91,0xf8,0x19,0x54,0x81,0xf1,0x20,0xc0,0x84,0x8f,0xc0, - 0xcf,0x90,0x3d,0x6c,0x1c,0x10,0x55,0xa0,0x70,0x41,0x7f,0xcf,0x7f,0xf8,0x90,0x60,0x43,0x0b,0xc1,0x85,0xfb,0x80,0xc4,0x81, - 0x8a,0xe1,0x81,0x81,0x0c,0x56,0x0c,0x2f,0xd0,0x31,0x40,0x62,0xb5,0x80,0x70,0x41,0x55,0xc1,0xc1,0x05,0x7e,0xff,0x5c,0x18, - 0x30,0x21,0xbf,0x06,0x17,0xe8,0x18,0xa0,0x31,0x5b,0xec,0x06,0x00,0x34,0x18,0x6f,0xbf,0xa0,0x0c,0x95,0x01,0xc1,0x04,0x51, - 0x07,0x04,0x11,0xf4,0xf4,0x0f,0x77,0x06,0x04,0x34,0x18,0x6f,0xc5,0x60,0xaa,0x07,0x34,0x09,0xd0,0x18,0x10,0xef,0x03,0x0b, - 0xf8,0x5e,0x0a,0xa0,0x73,0x40,0x93,0xc7,0x8e,0x4d,0x51,0xe5,0xdc,0xf9,0x03,0xdc,0x01,0x81,0x0e,0x54,0x0a,0x71,0xf8,0x11, - 0x82,0xc6,0x98,0x0c,0x00,0x69,0x72,0xe5,0xe0,0xe4,0xff,0x25,0x67,0x80,0xcf,0x90,0xb9,0x1f,0x15,0x01,0x9f,0x21,0x73,0xe4, - 0x17,0x63,0x06,0x04,0x34,0x7c,0x05,0xc0,0x8b,0xd7,0x41,0x81,0x0c,0x12,0x00,0xc0,0x2a,0xfe,0x2d,0x42,0x03,0x80,0x66,0x21, - 0x71,0xf0,0x1c,0x03,0x30,0x6a,0xfb,0x05,0xd5,0xc1,0x81,0x0c,0x12,0x01,0x4c,0x0a,0xbf,0x8b,0x54,0xa0,0x06,0x09,0x00,0xa6, - 0x05,0x5f,0xc5,0xa7,0x40,0xa2,0x03,0x2e,0x38,0xd2,0xe2,0x77,0xe8,0x10,0x00,0x09,0x43,0xa6,0x72,0xbf,0x3c,0xea,0x0a,0x1c, - 0x60,0xb4,0x54,0xee,0x5e,0x6a,0x16,0x21,0xcf,0xb4,0x75,0x06,0x07,0xdb,0xec,0x14,0x0b,0x1d,0xc1,0x81,0xf4,0x1f,0x87,0xc2, - 0xc8,0x98,0xe8,0x37,0xfa,0x3d,0x83,0x03,0xe6,0x3f,0x27,0x7f,0x06,0x07,0xd4,0x4b,0x03,0x25,0x62,0xd9,0x19,0x38,0x27,0xfe, - 0xfe,0x0c,0x0f,0xb0,0xfc,0x0c,0xd4,0x3f,0x16,0xbc,0x41,0x24,0xbf,0x04,0x7a,0x04,0x7e,0x2d,0x90,0x74,0x2c,0xfc,0xcc,0x4f, - 0x28,0xc6,0x09,0xfd,0x6e,0x80,0xa1,0x2f,0x03,0x34,0x08,0xfc,0x5a,0xe2,0x07,0xd5,0x81,0x9a,0x85,0xf0,0x60,0x7c,0x32,0x82, - 0x06,0x07,0xe4,0x32,0xff,0x77,0xff,0x6f,0x53,0xf2,0x76,0xdd,0x7c,0x08,0x30,0x3e,0xe0,0x15,0xfe,0x0c,0x0f,0xaa,0xa0,0xc1, - 0xf3,0xff,0x2d,0x6d,0x0f,0xc0,0xc0,0xfa,0x2a,0x0c,0x1f,0x81,0x81,0xf3,0x54,0x20,0xb9,0xee,0x07,0x4c,0x86,0x18,0xdf,0x01, - 0x81,0xf3,0xf0,0x9d,0xf0,0x18,0x1f,0x30,0xc9,0xe6,0x0b,0x2c,0x0b,0xaf,0x40,0xc0,0xf9,0x86,0x4f,0x00,0xc0,0xf9,0xc2,0x67, - 0x98,0x3c,0xac,0xaa,0xba,0x83,0x03,0xe5,0x16,0x01,0xce,0x20,0xe0,0xf9,0xbd,0x41,0xed,0x45,0x2c,0xc3,0x68,0x38,0x3e,0x6d, - 0x00,0xe0,0xfa,0x3d,0xc1,0x00,0x00,0x4c,0x1d,0xab,0xda,0xb3,0x51,0x2f,0xaa,0xcd,0x45,0xc5,0x39,0xf1,0xf3,0x8c,0x90,0x8e, - 0x92,0x86,0x7f,0x9c,0xa4,0xbc,0xb4,0xcc,0x1f,0xfa,0xb9,0x48,0xf1,0xd0,0x8b,0xb8,0x9b,0x94,0xb6,0x51,0x84,0xbb,0xa6,0x2c, - 0x52,0xaf,0xde,0xad,0x58,0x2b,0x82,0x32,0xfe,0xb9,0x50,0x60,0x81,0xc1,0x86,0xfb,0x06,0x27,0xf5,0x50,0x39,0xa0,0x93,0x20, - 0xc1,0x04,0x01,0x81,0xf8,0x31,0x3f,0x7e,0x07,0x34,0x12,0x4a,0x84,0x21,0xf6,0x88,0xa0,0x46,0x0b,0x25,0xc1,0x81,0x08,0x2e, - 0xe8,0x31,0xbf,0xc3,0x60,0x95,0xe5,0x1f,0x90,0x40,0x73,0x40,0xa0,0xd7,0x35,0xa1,0x00,0x18,0x50,0x42,0xf0,0x62,0x7e,0x81, - 0xce,0x03,0x9a,0x52,0x01,0xc0,0xc2,0x83,0x2b,0x06,0x27,0xe2,0x83,0x9a,0x05,0x32,0x0c,0x08,0x41,0x72,0xd7,0x51,0xa4,0x2d, - 0x60,0x59,0x36,0x0c,0x08,0x38,0x94,0xb1,0x6f,0xfa,0xca,0xed,0x62,0x33,0x6f,0x18,0x18,0x42,0xae,0x6c,0x7a,0x01,0x80,0xc2, - 0x83,0xfc,0x18,0x9f,0x7f,0x8b,0x26,0x47,0xa0,0x80,0x0c,0x28,0x47,0xc1,0x89,0xf7,0x56,0x2c,0x9d,0x05,0x00,0x91,0xa2,0x35, - 0x2d,0x1c,0xf0,0x5d,0x3a,0x0a,0x01,0x2b,0xa3,0xaa,0x04,0x41,0x3d,0xdc,0x2f,0x85,0x5c,0xb0,0x21,0x80,0x60,0x30,0xa1,0x00, - 0xc5,0x7c,0x86,0x72,0x80,0xc0,0xfc,0x84,0x00,0x61,0x42,0x3c,0x0c,0x4f,0xd8,0x67,0x36,0x0c,0x08,0x38,0xfb,0x04,0x60,0x55, - 0x83,0x45,0xfe,0x6a,0x5c,0x14,0x02,0x50,0x29,0x86,0x6e,0xfc,0x5c,0x5b,0x0c,0x79,0x10,0x60,0x7e,0x02,0x10,0x30,0xa1,0x05, - 0xc0,0x45,0xf1,0xa0,0xc0,0xfb,0x89,0x00,0xc2,0x84,0x03,0x80,0x0d,0x25,0x04,0xa1,0xf0,0x29,0x86,0x31,0xc0,0x84,0x3e,0x05, - 0x34,0x06,0xb0,0x04,0xd3,0xb4,0x36,0x85,0xcb,0x88,0x06,0x07,0xdc,0x4a,0x06,0x14,0x20,0x21,0x20,0x15,0xc2,0x03,0x03,0xec, - 0x3e,0x06,0x14,0x20,0x20,0x01,0x01,0x5c,0x40,0x1e,0x1f,0x02,0x9b,0xc8,0x01,0x7d,0x07,0x15,0x0f,0x81,0x4d,0xe0,0x20,0x0b, - 0x37,0x74,0x25,0xa0,0xb2,0x86,0x5b,0xf8,0x30,0x3e,0xc5,0xc0,0xc2,0x83,0x84,0x00,0x62,0x40,0xc5,0x6f,0x10,0x03,0x7c,0x0c, - 0x28,0x28,0x91,0x01,0xc8,0x04,0x7e,0x50,0x5c,0x0a,0x65,0x48,0x1c,0xeb,0x15,0x17,0x01,0x8f,0x0d,0x1e,0x11,0xfd,0x08,0xae, - 0x18,0x00,0x02,0x70,0xe5,0x5f,0x95,0x9a,0x8e,0x7e,0x56,0x23,0x3e,0x42,0x31,0xed,0xf3,0x90,0x94,0x92,0x96,0x87,0x7f,0x9c, - 0xc4,0xdc,0xdc,0xf4,0x1f,0xfa,0x79,0xa9,0x78,0x0c,0x1f,0xd9,0x72,0x32,0xaa,0x1f,0x32,0x6a,0x76,0x7e,0xfe,0x79,0x59,0x75, - 0xb7,0x83,0xb0,0x62,0xc0,0x9f,0x03,0x0d,0xc0,0xbe,0x19,0x91,0xbc,0x05,0xd4,0x6e,0xf3,0xe3,0x90,0x46,0x25,0x9a,0x06,0x07, - 0xe0,0x7c,0x06,0x4b,0x28,0x34,0xdf,0xef,0xa2,0xa1,0x05,0x56,0x03,0x07,0xf9,0x00,0x82,0xb4,0x8c,0x26,0x1c,0x12,0xbc,0x23, - 0xa5,0xa3,0x98,0x8c,0x9a,0x14,0x16,0x2f,0x92,0xad,0x25,0xa0,0x5c,0x3e,0xc4,0x13,0x63,0x6f,0x97,0x05,0x28,0xdb,0x8d,0x08, - 0x48,0xcf,0x53,0x03,0x03,0xef,0xe1,0xd0,0x28,0xe0,0x31,0x5f,0x8b,0x32,0x8d,0xb4,0xfd,0x68,0xfd,0x58,0x30,0x3e,0xea,0x94, - 0xe8,0xf6,0x25,0xc5,0x69,0x98,0xc4,0xa1,0x83,0xc6,0x21,0xe2,0xc2,0xb8,0x60,0x68,0x80,0x5c,0xf9,0x75,0x15,0x32,0x05,0xb4, - 0x11,0x74,0x9a,0x55,0xa8,0x21,0x02,0x30,0x13,0x24,0xac,0x06,0x07,0xd9,0x50,0x19,0x2c,0xa0,0xd2,0xff,0x9f,0xae,0x06,0x07, - 0xd5,0x56,0x08,0xe0,0xaa,0x4a,0x0a,0xe3,0x4e,0xa8,0x83,0x96,0x0f,0x43,0x83,0x43,0xfe,0xf5,0xc9,0x83,0x5a,0x03,0x18,0x07, - 0x08,0x0a,0x44,0x1b,0x50,0x12,0x5a,0xe0,0x60,0x7d,0x4b,0x97,0xf8,0xd2,0xb0,0x18,0x1f,0x42,0xe5,0xfe,0x34,0x75,0x0a,0xe2, - 0x3c,0x80,0x3e,0x7f,0xf1,0x80,0xd9,0x80,0x9d,0xac,0x06,0x07,0xd1,0x50,0x17,0x07,0xcf,0xfe,0xa4,0x18,0x1f,0x45,0x40,0x5c, - 0x23,0x76,0x88,0xdc,0x3c,0x18,0x70,0x52,0x20,0x9a,0xa7,0x06,0x07,0xcd,0x52,0x70,0x8e,0x90,0x18,0x1f,0x32,0xe0,0x2e,0x58, - 0x39,0x20,0x76,0x88,0x3b,0xf1,0xf7,0x7d,0x04,0x89,0xf0,0x60,0x7c,0x8b,0x81,0x52,0x82,0x03,0x1b,0xfc,0x1d,0x12,0xcc,0x03, - 0x03,0xe3,0xe5,0x83,0x37,0x88,0x70,0xe0,0x56,0x00,0x01,0x40,0x73,0x4f,0xbf,0x11,0x9f,0x22,0xfe,0xc5,0x22,0x8f,0x20,0x8d, - 0x7b,0x7e,0x65,0x26,0x64,0x78,0x4d,0x2d,0x6c,0x26,0x87,0x7f,0xf4,0x0f,0xa7,0x7c,0xe5,0xb1,0x68,0x5a,0x2d,0xa2,0xdf,0x01, - 0x82,0xd7,0xc1,0x41,0x7a,0xf7,0xdd,0x7f,0xad,0xae,0x2e,0xa3,0x57,0x7c,0x06,0x23,0x40,0x5f,0x89,0x9b,0x46,0x43,0x6b,0xfc, - 0xff,0x32,0x44,0x9a,0x53,0x5e,0xfb,0x0b,0xf2,0x6e,0x03,0x8b,0xfd,0xa3,0x6c,0x07,0x17,0xfb,0x1a,0xaa,0x76,0x60,0xf1,0xcf, - 0x10,0xff,0x5b,0x60,0x38,0xbf,0xdb,0x3b,0x17,0x18,0xe7,0x8f,0x1d,0xa4,0x3c,0x0c,0x9d,0x22,0x3e,0xd7,0x00,0xc9,0x63,0xf7, - 0x38,0xb8,0xc7,0x4c,0x78,0xed,0x31,0xe3,0x9d,0x22,0x3e,0xe7,0x17,0x19,0x6d,0xec,0x27,0xd0,0x8a,0xbc,0x3b,0xe3,0x05,0x2e, - 0xd2,0x51,0xe2,0x67,0x58,0x8f,0xb5,0xb0,0x97,0x63,0x61,0x2e,0x82,0x88,0x4d,0x9a,0xa3,0xc4,0xce,0x91,0x3f,0x57,0x61,0x2e, - 0x96,0xc2,0x5c,0x95,0x10,0x9b,0x13,0x21,0x33,0xa4,0x4f,0xcd,0x9f,0x84,0xd8,0x92,0xc5,0x86,0x77,0xd2,0x3a,0xc7,0xc2,0x32, - 0x38,0x2f,0x73,0x89}; - - // Expected results - H263Info infoRefQCIF_N; - infoRefQCIF_N.uiH263PTypeFmt = 2; - infoRefQCIF_N.codecBits = 0; - infoRefQCIF_N.pQuant = 7; - infoRefQCIF_N.fType = 0; - infoRefQCIF_N.cpmBit = 0; - infoRefQCIF_N.numOfGOBs = 9; - infoRefQCIF_N.totalNumOfMBs = 11*9; - - infoRefQCIF_N.ptrGOBbuffer[0] = 0; infoRefQCIF_N.ptrGOBbufferSBit[0] = 0; infoRefQCIF_N.ptrGQuant[0] = 0; - infoRefQCIF_N.ptrGOBbuffer[1] = 215; infoRefQCIF_N.ptrGOBbufferSBit[1] = 2; infoRefQCIF_N.ptrGQuant[1] = 7; - infoRefQCIF_N.ptrGOBbuffer[2] = 456; infoRefQCIF_N.ptrGOBbufferSBit[2] = 1; infoRefQCIF_N.ptrGQuant[2] = 7; - infoRefQCIF_N.ptrGOBbuffer[3] = 535; infoRefQCIF_N.ptrGOBbufferSBit[3] = 5; infoRefQCIF_N.ptrGQuant[3] = 7; - infoRefQCIF_N.ptrGOBbuffer[4] = 615; infoRefQCIF_N.ptrGOBbufferSBit[4] = 7; infoRefQCIF_N.ptrGQuant[4] = 7; - infoRefQCIF_N.ptrGOBbuffer[5] = 925; infoRefQCIF_N.ptrGOBbufferSBit[5] = 4; infoRefQCIF_N.ptrGQuant[5] = 7; - infoRefQCIF_N.ptrGOBbuffer[6] = 1133; infoRefQCIF_N.ptrGOBbufferSBit[6] = 1; infoRefQCIF_N.ptrGQuant[6] = 7; - infoRefQCIF_N.ptrGOBbuffer[7] = 1512; infoRefQCIF_N.ptrGOBbufferSBit[7] = 6; infoRefQCIF_N.ptrGQuant[7] = 7; - infoRefQCIF_N.ptrGOBbuffer[8] = 1832; infoRefQCIF_N.ptrGOBbufferSBit[8] = 7; infoRefQCIF_N.ptrGQuant[8] = 7; - - // -------------------------------------------------- - // Input data - H.263 encoded stream CIF (I-frame) - // -------------------------------------------------- - - FILE* openFile = fopen("H263Foreman_CIF_Iframe.bin", "rb"); - - fseek(openFile, 0, SEEK_END); - int lengthRefCIF_I = ftell(openFile); - fseek(openFile, 0, SEEK_SET); - - unsigned char* encodedStreamCIF_I = new unsigned char[lengthRefCIF_I]; - fread(encodedStreamCIF_I, 1, lengthRefCIF_I, openFile); - fclose(openFile); - - // Expected results - H263Info infoRefCIF_I; - infoRefCIF_I.uiH263PTypeFmt = 3; - infoRefCIF_I.codecBits = 0; - infoRefCIF_I.pQuant = 5; - infoRefCIF_I.fType = 0; - infoRefCIF_I.cpmBit = 0; - infoRefCIF_I.numOfGOBs = 18; - infoRefCIF_I.totalNumOfMBs = 22*18; - - infoRefCIF_I.ptrGOBbuffer[0] = 0; infoRefCIF_I.ptrGOBbufferSBit[0] = 0; infoRefCIF_I.ptrGQuant[0] = 0; - infoRefCIF_I.ptrGOBbuffer[1] = 1607; infoRefCIF_I.ptrGOBbufferSBit[1] = 0; infoRefCIF_I.ptrGQuant[1] = 5; - infoRefCIF_I.ptrGOBbuffer[2] = 2759; infoRefCIF_I.ptrGOBbufferSBit[2] = 0; infoRefCIF_I.ptrGQuant[2] = 5; - infoRefCIF_I.ptrGOBbuffer[3] = 3699; infoRefCIF_I.ptrGOBbufferSBit[3] = 0; infoRefCIF_I.ptrGQuant[3] = 5; - infoRefCIF_I.ptrGOBbuffer[4] = 4506; infoRefCIF_I.ptrGOBbufferSBit[4] = 0; infoRefCIF_I.ptrGQuant[4] = 5; - infoRefCIF_I.ptrGOBbuffer[5] = 5260; infoRefCIF_I.ptrGOBbufferSBit[5] = 0; infoRefCIF_I.ptrGQuant[5] = 5; - infoRefCIF_I.ptrGOBbuffer[6] = 6254; infoRefCIF_I.ptrGOBbufferSBit[6] = 0; infoRefCIF_I.ptrGQuant[6] = 5; - infoRefCIF_I.ptrGOBbuffer[7] = 7117; infoRefCIF_I.ptrGOBbufferSBit[7] = 0; infoRefCIF_I.ptrGQuant[7] = 5; - infoRefCIF_I.ptrGOBbuffer[8] = 7804; infoRefCIF_I.ptrGOBbufferSBit[8] = 0; infoRefCIF_I.ptrGQuant[8] = 5; - infoRefCIF_I.ptrGOBbuffer[9] = 8600; infoRefCIF_I.ptrGOBbufferSBit[9] = 0; infoRefCIF_I.ptrGQuant[9] = 5; - infoRefCIF_I.ptrGOBbuffer[10] = 9419; infoRefCIF_I.ptrGOBbufferSBit[10] = 0; infoRefCIF_I.ptrGQuant[10] = 5; - infoRefCIF_I.ptrGOBbuffer[11] = 10126; infoRefCIF_I.ptrGOBbufferSBit[11] = 0; infoRefCIF_I.ptrGQuant[11] = 5; - infoRefCIF_I.ptrGOBbuffer[12] = 10723; infoRefCIF_I.ptrGOBbufferSBit[12] = 0; infoRefCIF_I.ptrGQuant[12] = 5; - infoRefCIF_I.ptrGOBbuffer[13] = 11280; infoRefCIF_I.ptrGOBbufferSBit[13] = 0; infoRefCIF_I.ptrGQuant[13] = 5; - infoRefCIF_I.ptrGOBbuffer[14] = 11910; infoRefCIF_I.ptrGOBbufferSBit[14] = 0; infoRefCIF_I.ptrGQuant[14] = 5; - infoRefCIF_I.ptrGOBbuffer[15] = 12430; infoRefCIF_I.ptrGOBbufferSBit[15] = 0; infoRefCIF_I.ptrGQuant[15] = 5; - infoRefCIF_I.ptrGOBbuffer[16] = 12925; infoRefCIF_I.ptrGOBbufferSBit[16] = 0; infoRefCIF_I.ptrGQuant[16] = 5; - infoRefCIF_I.ptrGOBbuffer[17] = 13506; infoRefCIF_I.ptrGOBbufferSBit[17] = 0; infoRefCIF_I.ptrGQuant[17] = 5; - - // -------------------------------------------------- - // Input data - H.263 encoded stream CIF (P-frame) - // -------------------------------------------------- - - openFile = fopen("H263Foreman_CIF_Pframe.bin", "rb"); - - fseek(openFile, 0, SEEK_END); - int lengthRefCIF_P = ftell(openFile); - fseek(openFile, 0, SEEK_SET); - - unsigned char* encodedStreamCIF_P = new unsigned char[lengthRefCIF_P]; - fread(encodedStreamCIF_P, 1, lengthRefCIF_P, openFile); - fclose(openFile); - - // Expected results - H263Info infoRefCIF_P; - infoRefCIF_P.uiH263PTypeFmt = 3; - infoRefCIF_P.codecBits = 8; - infoRefCIF_P.pQuant = 4; - infoRefCIF_P.fType = 1; - infoRefCIF_P.cpmBit = 0; - infoRefCIF_P.numOfGOBs = 18; - infoRefCIF_P.totalNumOfMBs = 22*18; - - infoRefCIF_P.ptrGOBbuffer[0] = 0; infoRefCIF_P.ptrGOBbufferSBit[0] = 0; infoRefCIF_P.ptrGQuant[0] = 0; - infoRefCIF_P.ptrGOBbuffer[1] = 252; infoRefCIF_P.ptrGOBbufferSBit[1] = 0; infoRefCIF_P.ptrGQuant[1] = 5; - infoRefCIF_P.ptrGOBbuffer[2] = 482; infoRefCIF_P.ptrGOBbufferSBit[2] = 0; infoRefCIF_P.ptrGQuant[2] = 6; - infoRefCIF_P.ptrGOBbuffer[3] = 581; infoRefCIF_P.ptrGOBbufferSBit[3] = 0; infoRefCIF_P.ptrGQuant[3] = 6; - infoRefCIF_P.ptrGOBbuffer[4] = 676; infoRefCIF_P.ptrGOBbufferSBit[4] = 0; infoRefCIF_P.ptrGQuant[4] = 7; - infoRefCIF_P.ptrGOBbuffer[5] = 756; infoRefCIF_P.ptrGOBbufferSBit[5] = 0; infoRefCIF_P.ptrGQuant[5] = 7; - infoRefCIF_P.ptrGOBbuffer[6] = 855; infoRefCIF_P.ptrGOBbufferSBit[6] = 0; infoRefCIF_P.ptrGQuant[6] = 8; - infoRefCIF_P.ptrGOBbuffer[7] = 949; infoRefCIF_P.ptrGOBbufferSBit[7] = 0; infoRefCIF_P.ptrGQuant[7] = 9; - infoRefCIF_P.ptrGOBbuffer[8] = 1004; infoRefCIF_P.ptrGOBbufferSBit[8] = 0; infoRefCIF_P.ptrGQuant[8] = 10; - infoRefCIF_P.ptrGOBbuffer[9] = 1062; infoRefCIF_P.ptrGOBbufferSBit[9] = 0; infoRefCIF_P.ptrGQuant[9] = 11; - infoRefCIF_P.ptrGOBbuffer[10] = 1115; infoRefCIF_P.ptrGOBbufferSBit[10] = 0; infoRefCIF_P.ptrGQuant[10] = 11; - infoRefCIF_P.ptrGOBbuffer[11] = 1152; infoRefCIF_P.ptrGOBbufferSBit[11] = 0; infoRefCIF_P.ptrGQuant[11] = 13; - infoRefCIF_P.ptrGOBbuffer[12] = 1183; infoRefCIF_P.ptrGOBbufferSBit[12] = 0; infoRefCIF_P.ptrGQuant[12] = 14; - infoRefCIF_P.ptrGOBbuffer[13] = 1214; infoRefCIF_P.ptrGOBbufferSBit[13] = 0; infoRefCIF_P.ptrGQuant[13] = 15; - infoRefCIF_P.ptrGOBbuffer[14] = 1257; infoRefCIF_P.ptrGOBbufferSBit[14] = 0; infoRefCIF_P.ptrGQuant[14] = 16; - infoRefCIF_P.ptrGOBbuffer[15] = 1286; infoRefCIF_P.ptrGOBbufferSBit[15] = 0; infoRefCIF_P.ptrGQuant[15] = 16; - infoRefCIF_P.ptrGOBbuffer[16] = 1321; infoRefCIF_P.ptrGOBbufferSBit[16] = 0; infoRefCIF_P.ptrGQuant[16] = 16; - infoRefCIF_P.ptrGOBbuffer[17] = 1352; infoRefCIF_P.ptrGOBbufferSBit[17] = 0; infoRefCIF_P.ptrGQuant[17] = 14; - - //--------------------------------------------------------------- - //--------------------------------------------------------------- - //--------------------------------------------------------------- - // Start test - const H263Info* ptrInfoSQCIF = NULL; - const H263MBInfo* ptrMBInfoSQCIF = NULL; - const H263Info* ptrInfoQCIF = NULL; - const H263MBInfo* ptrMBInfoQCIF = NULL; - const H263Info* ptrInfoCIF = NULL; - const H263MBInfo* ptrMBInfoCIF = NULL; - const H263Info* ptrInfoQCIF_N = NULL; - const H263MBInfo* ptrMBInfoQCIF_N = NULL; - const H263Info* ptrInfoCIF_I = NULL; - const H263MBInfo* ptrMBInfoCIF_I = NULL; - const H263Info* ptrInfoCIF_P = NULL; - const H263MBInfo* ptrMBInfoCIF_P = NULL; - H263Information h263Information; - - // Input buffer - const int length = 3000; - unsigned char* encodedBuffer = new unsigned char[lengthRefCIF_P]; - - // Test invalid inputs - assert(-1 == h263Information.GetInfo(NULL, length, ptrInfoSQCIF)); - assert(-1 == h263Information.GetInfo(encodedBuffer, 0, ptrInfoSQCIF)); - assert(-1 == h263Information.GetInfo(encodedBuffer, length, ptrInfoSQCIF)); // invalid H.263 stream -// assert(-1 == h263Information.GetInfo(encodedStreamSQCIF, lengthRefSQCIF/2, ptrInfoSQCIF)); // invalid H.263 stream - - assert(-1 == h263Information.GetMBInfo(NULL, length, 0, ptrMBInfoSQCIF)); - assert(-1 == h263Information.GetMBInfo(encodedBuffer, 0, 0, ptrMBInfoSQCIF)); - assert(-1 == h263Information.GetMBInfo(encodedBuffer, length, 0, ptrMBInfoSQCIF)); - assert(-1 == h263Information.GetMBInfo(encodedBuffer, length, -1, ptrMBInfoSQCIF)); // incorrect group number - assert(-1 == h263Information.GetMBInfo(encodedBuffer, length, 8, ptrMBInfoSQCIF)); // incorrect group number - - // ---------------------------------------------- - // Get info from encoded H.263 stream - SQCIF - // ---------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamSQCIF, lengthRefSQCIF, ptrInfoSQCIF)); - ValidateResults(ptrInfoSQCIF, &infoRefSQCIF); - - // Get MB info - for (int i = 0; i < ptrInfoSQCIF->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamSQCIF, lengthRefSQCIF, i, ptrMBInfoSQCIF)); - } - ValidateMBResults(ptrInfoSQCIF, ptrMBInfoSQCIF, &infoRefSQCIF); - - // --------------------------------------------- - // Get info from encoded H.263 stream - QCIF - // --------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamQCIF, lengthRefQCIF, ptrInfoQCIF)); - ValidateResults(ptrInfoQCIF, &infoRefQCIF); - - // Get MB info - for (int i = 0; i < ptrInfoQCIF->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamQCIF, lengthRefQCIF, i, ptrMBInfoQCIF)); - } - ValidateMBResults(ptrInfoQCIF, ptrMBInfoQCIF, &infoRefQCIF); - - // -------------------------------------------- - // Get info from encoded H.263 stream - CIF - // -------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamCIF, lengthRefCIF, ptrInfoCIF)); - ValidateResults(ptrInfoCIF, &infoRefCIF); - - // Get MB info - for (int i = 0; i < ptrInfoCIF->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamCIF, lengthRefCIF, i, ptrMBInfoCIF)); - } - ValidateMBResults(ptrInfoCIF, ptrMBInfoCIF, &infoRefCIF); - - // ---------------------------------------------------------------------- - // Get info from encoded H.263 stream - QCIF - non byte aligned GOBs - // ---------------------------------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamQCIF_N, lengthRefQCIF_N, ptrInfoQCIF_N)); - ValidateResults(ptrInfoQCIF_N, &infoRefQCIF_N); - - // Get MB info - for (int i = 0; i < ptrInfoQCIF_N->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamQCIF_N, lengthRefQCIF_N, i, ptrMBInfoQCIF_N)); - } - ValidateMBResults(ptrInfoQCIF_N, ptrMBInfoQCIF_N, &infoRefQCIF_N); - - // ------------------------------------------- - // Get info from encoded H.263 stream - CIF - // ------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamCIF_I, lengthRefCIF_I, ptrInfoCIF_I)); - ValidateResults(ptrInfoCIF_I, &infoRefCIF_I); - - // Get MB info - unsigned int start = timeGetTime(); - for (int i = 0; i < ptrInfoCIF_I->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamCIF_I, lengthRefCIF_I, i, ptrMBInfoCIF_I)); - } - unsigned int endMB = timeGetTime(); - ValidateMBResults(ptrInfoCIF_I, ptrMBInfoCIF_I, &infoRefCIF_I, false); - - std::cout << "I-frame, length: " << lengthRefCIF_I << " bytes. Time: " << endMB - start << " ms." << std::endl; - PRINT_LINE; - - // ------------------------------------------- - // Get info from encoded H.263 stream - CIF - // ------------------------------------------- - h263Information.Reset(); - assert(0 == h263Information.GetInfo(encodedStreamCIF_P, lengthRefCIF_P, ptrInfoCIF_P)); - ValidateResults(ptrInfoCIF_P, &infoRefCIF_P); - - // Get MB info - start = timeGetTime(); - for (int i = 0; i < ptrInfoCIF_P->numOfGOBs; i++) - { - assert(0 == h263Information.GetMBInfo(encodedStreamCIF_P, lengthRefCIF_P, i, ptrMBInfoCIF_P)); - } - endMB = timeGetTime(); - ValidateMBResults(ptrInfoCIF_P, ptrMBInfoCIF_P, &infoRefCIF_P, false); - - std::cout << "P-frame, length: " << lengthRefCIF_P << " bytes. Time: " << endMB - start << " ms." << std::endl; - PRINT_LINE; - - delete [] encodedStreamCIF_I; - delete [] encodedStreamCIF_P; - delete [] encodedBuffer; - - TEST_PASSED(); - ::Sleep(5000); - return 0; -} - diff --git a/modules/rtp_rtcp/test/testRateControl/testRateControl.cpp b/modules/rtp_rtcp/test/testRateControl/testRateControl.cpp deleted file mode 100644 index c2825577c..000000000 --- a/modules/rtp_rtcp/test/testRateControl/testRateControl.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include - -#include "rtp_rtcp.h" -#include "common_types.h" -#include "RateControlDetector.h" -/*#include "rtcp_utility.h" -#include "tmmbr_help.h"*/ - -#define TEST_STR "Test RateControl." -#define TEST_PASSED() std::cerr << TEST_STR << " : [OK]" << std::endl -#define PRINT_LINE std::cout << "------------------------------------------" << std::endl; - - -const int maxFileLen = 200; -WebRtc_UWord8* dataFile[maxFileLen]; - - -struct InputSet -{ - WebRtc_UWord32 TMMBR; - WebRtc_UWord32 packetOH; - WebRtc_UWord32 SSRC; -}; - -const InputSet set0 = {220, 80, 11111}; // bitRate, packetOH, ssrc -const InputSet set1 = {180, 90, 22222}; -const InputSet set2 = {100, 210, 33333}; -const InputSet set3 = { 35, 40, 44444}; -const InputSet set4 = { 40, 60, 55555}; -const InputSet set4_1 = {100, 60, 55555}; -const InputSet set4_2 = { 10, 60, 55555}; -const InputSet set5 = {200, 40, 66666}; -const InputSet set00 = { 0, 40, 66666}; - - - - -WebRtc_Word32 GetFile(char* fileName) -{ - if (!fileName[0]) - { - return 0; - } - - FILE* openFile = fopen(fileName, "rb"); - assert(openFile != NULL); - fseek(openFile, 0, SEEK_END); - int len = (WebRtc_Word16)(ftell(openFile)); - rewind(openFile); - assert(len > 0 && len < maxFileLen); - fread(dataFile, 1, len, openFile); - fclose(openFile); - return len; -}; - - -class LoopBackTransport2 : public webrtc::Transport -{ -public: - LoopBackTransport2(RtpRtcp* rtpRtcpModule) : - _rtpRtcpModule(rtpRtcpModule), - _cnt(0) - { - } - virtual int SendPacket(int channel, const void *data, int len) - { - return _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len); - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - char fileName[256] = {0}; - - - // Get stored rtcp packet w/ TMMBR - len = GetFile(fileName); - if (len == 0) - { - return 0; - } - - // Send in bitrate request - return _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len); - } - RtpRtcp* _rtpRtcpModule; - WebRtc_UWord32 _cnt; -}; - - -class LoopBackTransportVideo : public webrtc::Transport -{ -public: - LoopBackTransportVideo(RtpRtcp* rtpRtcpModule) : - _rtpRtcpModule(rtpRtcpModule), - _cnt(0) - { - } - virtual int SendPacket(int channel, const void *data, int len) - { - return _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len); - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - char fileName[256] = {0}; - - strcpy(fileName, "RTCPPacketTMMBR0.bin"); - - ++_cnt; - - // Get stored rtcp packet w/ TMMBR - len = GetFile(fileName); - if (len == 0) - { - return 0; - } - - // Send in bitrate request*/ - return _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len); - } - - RtpRtcp* _rtpRtcpModule; - WebRtc_UWord32 _cnt; -}; - -class TestRateControl : private RateControlDetector -{ -public: - TestRateControl():RateControlDetector(0) - { - } - ~TestRateControl() - { - } - void Start() - { - //Test perfect conditions - // But only one packet per frame - SetLastUsedBitRate(500); - WebRtc_UWord32 rtpTs=1234*90; - WebRtc_UWord32 framePeriod=33; // In Ms - WebRtc_UWord32 rtpDelta=framePeriod*90; - WebRtc_UWord32 netWorkDelay=10; - WebRtc_UWord32 arrivalTime=rtpTs/90+netWorkDelay; - WebRtc_UWord32 newBitRate=0; - for(WebRtc_UWord32 k=0;k<10;k++) - { - // Receive 10 packets - for(WebRtc_UWord32 i=0;i<10;i++) - { - NotifyNewArrivedPacket(rtpTs,arrivalTime); - rtpTs+=rtpDelta; - arrivalTime=rtpTs/90+netWorkDelay; - } - newBitRate=RateControl(2*netWorkDelay); - SetLastUsedBitRate(newBitRate); - Sleep(10*framePeriod); - std::cout << "RTCP Packet " << k << " new bitrate " << newBitRate << std::endl; - } - Reset(); - - - //Test increasing RTT - std::cout << "Test increasing RTT - No Receive timing changes" << std::endl; - SetLastUsedBitRate(500); - - for(WebRtc_UWord32 k=0;k<10;k++) - { - // Receive 10 packets - for(WebRtc_UWord32 i=0;i<10;i++) - { - NotifyNewArrivedPacket(rtpTs,arrivalTime); - rtpTs+=rtpDelta; - arrivalTime=rtpTs/90+netWorkDelay; - } - WebRtc_UWord32 rtt=2*netWorkDelay+k*20; - newBitRate=RateControl(rtt); - Sleep(10*framePeriod); - SetLastUsedBitRate(newBitRate); - std::cout << "RTCP Packet " << k << " RTT "<< rtt << " new bitrate " << newBitRate << std::endl; - - } - - Reset(); - - - //Test increasing RTT - std::cout << "Test increasing RTT - Changed receive timing" << std::endl; - SetLastUsedBitRate(500); - - for(WebRtc_UWord32 k=0;k<10;k++) - { - // Receive 10 packets - for(WebRtc_UWord32 i=0;i<10;i++) - { - NotifyNewArrivedPacket(rtpTs,arrivalTime); - rtpTs+=rtpDelta; - arrivalTime=rtpTs/90+netWorkDelay+i+(k*20); - } - WebRtc_UWord32 rtt=2*netWorkDelay+k*20; - newBitRate=RateControl(rtt); - Sleep(10*framePeriod); - SetLastUsedBitRate(newBitRate); - std::cout << "RTCP Packet " << k << " RTT "<< rtt << " new bitrate " << newBitRate << std::endl; - - } - - - - }; -}; - -class NULLDataZink: public RtpData -{ - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader) - { - return 0; - }; -}; - - -int _tmain(int argc, _TCHAR* argv[]) -{ - - std::string str; - std::cout << "------------------------" << std::endl; - std::cout << "---Test RateControl ----" << std::endl; - std::cout << "------------------------" << std::endl; - std::cout << " " << std::endl; - - // -------------------- - // Test TMMBRHelp class - - // -------------------- - TestRateControl test; - test.Start(); - - printf("RateControl-class test done.\n"); - - // ------------------------ - // Test RateControl single module - // ------------------------ - RtpRtcp* rtpRtcpModuleVideo = RtpRtcp::CreateRtpRtcp(0, false); - - LoopBackTransportVideo* myLoopBackTransportVideo = new LoopBackTransportVideo(rtpRtcpModuleVideo); - assert(0 == rtpRtcpModuleVideo->RegisterSendTransport(myLoopBackTransportVideo)); - printf("Multi module test done.\n"); - - - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo); - delete myLoopBackTransportVideo; - - TEST_PASSED(); - ::Sleep(5000); - - return 0; -} - diff --git a/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR0.bin b/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR0.bin deleted file mode 100644 index 19df13c4d7462fcf251096e07a63dba8abd82b86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 bcmZoT$-v0Kpq<`)mVpICGe7}*bV2|CQx*i( diff --git a/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR1.bin b/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR1.bin deleted file mode 100644 index b7b7c941ed69dca615a5217d33d62702b373cf10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 bcmZoT$-v0K5O%KlECUOOW`F{={R&Y4XMzSM diff --git a/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR2.bin b/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR2.bin deleted file mode 100644 index 257835c616d22cf64e5154a6c3ecf8fca856f06b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 bcmZoT$-v0K&}7V_Af(04?Yib4C diff --git a/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4.bin b/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4.bin deleted file mode 100644 index 28cd99ce9f178a008b735af90d0b7dbf6388af61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 bcmZoT$-v0KaFe diff --git a/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4_1.bin b/modules/rtp_rtcp/test/testTMMBR/RTCPPacketTMMBR4_1.bin deleted file mode 100644 index 5080b88ab9f295cc3e928448b76a36f1f1c39e0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 bcmZoT$-v0KaFe -#include -#include -#include - -#include "rtp_rtcp.h" -#include "common_types.h" -#include "rtcp_utility.h" -#include "tmmbr_help.h" - -#define TEST_STR "Test TMMBR." -#define TEST_PASSED() std::cerr << TEST_STR << " : [OK]" << std::endl -#define PRINT_LINE std::cout << "------------------------------------------" << std::endl; - - -const int maxFileLen = 200; -WebRtc_UWord8* dataFile[maxFileLen]; - - -struct InputSet -{ - WebRtc_UWord32 TMMBR; - WebRtc_UWord32 packetOH; - WebRtc_UWord32 SSRC; -}; - -const InputSet set0 = {220, 80, 11111}; // bitRate, packetOH, ssrc -const InputSet set1 = {180, 90, 22222}; -const InputSet set2 = {100, 210, 33333}; -const InputSet set3 = { 35, 40, 44444}; -const InputSet set4 = { 40, 60, 55555}; -const InputSet set4_1 = {100, 60, 55555}; -const InputSet set4_2 = { 10, 60, 55555}; -const InputSet set5 = {200, 40, 66666}; -const InputSet set00 = { 0, 40, 66666}; - -const int maxBitrate = 230; // if this is lower than max in the list above test should fail - -void Verify(TMMBRSet* boundingSet, int index, InputSet set) -{ - assert(boundingSet->ptrTmmbrSet[index] == set.TMMBR); - assert(boundingSet->ptrPacketOHSet[index] == set.packetOH); - assert(boundingSet->ptrSsrcSet[index] == set.SSRC); -}; - -int ParseRTCPPacket(const void *data, int len, TMMBRSet*& boundingSet) -{ - int numItems = -1; - RTCPUtility::RTCPParserV2 rtcpParser((const WebRtc_UWord8*)data, len, true); - RTCPUtility::RTCPPacketTypes pktType = rtcpParser.Begin(); - while (pktType != RTCPUtility::kRtcpNotValidCode) - { - const RTCPUtility::RTCPPacket& rtcpPacket = rtcpParser.Packet(); - if (pktType == RTCPUtility::kRtcpRtpfbTmmbnCode) - { - assert(0 == rtcpPacket.TMMBN.SenderSSRC); - assert(0 == rtcpPacket.TMMBN.MediaSSRC); - numItems = 0; - } - if (pktType == RTCPUtility::kRtcpRtpfbTmmbnItemCode) - { - boundingSet->ptrTmmbrSet[numItems] = rtcpPacket.TMMBNItem.MaxTotalMediaBitRate; - boundingSet->ptrPacketOHSet[numItems] = rtcpPacket.TMMBNItem.MeasuredOverhead; - boundingSet->ptrSsrcSet[numItems] = rtcpPacket.TMMBNItem.SSRC; - ++numItems; - } - pktType = rtcpParser.Iterate(); - } - return numItems; -}; - -WebRtc_Word32 GetFile(char* fileName) -{ - if (!fileName[0]) - { - return 0; - } - - FILE* openFile = fopen(fileName, "rb"); - assert(openFile != NULL); - fseek(openFile, 0, SEEK_END); - int len = (WebRtc_Word16)(ftell(openFile)); - rewind(openFile); - assert(len > 0 && len < maxFileLen); - fread(dataFile, 1, len, openFile); - fclose(openFile); - return len; -}; - - -class LoopBackTransport2 : public webrtc::Transport, private TMMBRHelp -{ -public: - LoopBackTransport2(RtpRtcp* rtpRtcpModule) : - TMMBRHelp(false), - _rtpRtcpModule(rtpRtcpModule), - _cnt(0) - { - } - virtual int SendPacket(int channel, const void *data, int len) - { - if( 0 == _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len)) - { - return len; - } - return -1; - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - char fileName[256] = {0}; - TMMBRSet* boundingSet = BoundingSet(); - boundingSet->VerifyAndAllocateSet(3); - - if (_cnt == 0) - { - // TMMBN {} - // TMMBN {} - // TMMBN {} - // TMMBN {2,4,0} -> {4,2} - assert(2 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set4); - Verify(boundingSet, 1, set2); - - strcpy(fileName, "RTCPPacketTMMBR3.bin"); - } - - ++_cnt; - - // Get stored rtcp packet w/ TMMBR - len = GetFile(fileName); - if (len == 0) - { - return 1; - } - - // Send in bitrate request - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len) == 0) - { - return len; - } - return -1; - } - RtpRtcp* _rtpRtcpModule; - WebRtc_UWord32 _cnt; -}; - - -class LoopBackTransportVideo : public webrtc::Transport, private TMMBRHelp -{ -public: - LoopBackTransportVideo(RtpRtcp* rtpRtcpModule) : - TMMBRHelp(false), - _rtpRtcpModule(rtpRtcpModule), - _cnt(0) - { - } - virtual int SendPacket(int channel, const void *data, int len) - { - if(_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len)== 0) - { - return len; - } - return -1; - } - virtual int SendRTCPPacket(int channel, const void *data, int len) - { - char fileName[256] = {0}; - TMMBRSet* boundingSet = BoundingSet(); - boundingSet->VerifyAndAllocateSet(3); - - if (_cnt == 0) - { - strcpy(fileName, "RTCPPacketTMMBR0.bin"); - } - else if (_cnt == 1) - { - // TMMBN {0} -> {0} - assert(1 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set0); - - strcpy(fileName, "RTCPPacketTMMBR1.bin"); - } - else if (_cnt == 2) - { - // TMMBN {0,1} -> {1} - assert(1 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set1); - - strcpy(fileName, "RTCPPacketTMMBR2.bin"); - } - else if (_cnt == 3) - { - // TMMBN {0,1,2} -> {2} - assert(1 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set2); - - strcpy(fileName, "RTCPPacketTMMBR3.bin"); - } - else if (_cnt == 4) - { - // TMMBN {0,1,2,3} -> {3,2} - assert(2 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set2); - - strcpy(fileName, "RTCPPacketTMMBR4.bin"); - } - else if (_cnt == 5) - { - // TMMBN {0,1,2,3,4} -> {3,4,2} - assert(3 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - strcpy(fileName, "RTCPPacketTMMBR5.bin"); - } - else if (_cnt == 6) - { - // TMMBN {0,1,2,3,4,5} -> {3,4,2} - assert(3 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - strcpy(fileName, "RTCPPacketTMMBR4_2.bin"); - } - else if (_cnt == 7) - { - // TMMBN {0,1,2,3,4_2,5} -> {4_2} - assert(1 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set4_2); - - ++_cnt; - ::Sleep(5*RTCP_INTERVAL_AUDIO_MS + 1000); // time out receiver - _rtpRtcpModule->Process(); // SendRTCP() (_cnt == 8) - // a receiver has timed out -> UpdateTMMBR() - } - else if (_cnt == 8) - { - // No TMMBN in this packet - assert(-1 == ParseRTCPPacket(data, len, boundingSet)); - } - else if (_cnt == 10) - { - // TMMBN {} -> {}, empty set - assert(0 == ParseRTCPPacket(data, len, boundingSet)); - - strcpy(fileName, "RTCPPacketTMMBR2.bin"); - } - else if (_cnt == 11) - { - // TMMBN {2} -> {2} - assert(1 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set2); - } - else if (_cnt == 12) // ----- multi module ------------- - { - // No TMMBN in this packet - assert(-1 == ParseRTCPPacket(data, len, boundingSet)); - - strcpy(fileName, "RTCPPacketTMMBR4.bin"); - } - else if (_cnt == 13) - { - // TMMBN {} - // TMMBN {} - // TMMBN {} - // TMMBN {2,4} -> {4,2} - assert(2 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set4); - Verify(boundingSet, 1, set2); - - strcpy(fileName, "RTCPPacketTMMBR0.bin"); - } - else if (_cnt == 14) - { - // TMMBN {} - // TMMBN {3} - // TMMBN {} - // TMMBN {2,4,0} -> {3,4,2} - assert(3 == ParseRTCPPacket(data, len, boundingSet)); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - strcpy(fileName, "RTCPPacketTMMBR1.bin"); - } - //else if (_cnt == 15) - //{ - // // TMMBN {} - // // TMMBN {} - // // TMMBN {} - // // TMMBN {2,4,0,1} -> {4,2} - // //assert(2 == ParseRTCPPacket(data, len, boundingSet)); - // //Verify(boundingSet, 0, set4); - // //Verify(boundingSet, 1, set2); - //} - //else if (_cnt == 15) - //{ - // // No TMMBN in this packet - // assert(-1 == ParseRTCPPacket(data, len, boundingSet)); - //} - else if (_cnt == 15) - { - // TMMBN {} - // TMMBN {} - // TMMBN {} - // TMMBN {} -> {}, empty set - assert(0 == ParseRTCPPacket(data, len, boundingSet)); - } - - ++_cnt; - - // Get stored rtcp packet w/ TMMBR - len = GetFile(fileName); - if (len == 0) - { - return 1; - } - - // Send in bitrate request - if( 0 == _rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)dataFile, len)) - { - return len; - } - return -1; - } - - RtpRtcp* _rtpRtcpModule; - WebRtc_UWord32 _cnt; -}; - -class TestTMMBR : private TMMBRHelp -{ -public: - TestTMMBR() : TMMBRHelp(false) {}; - - void Add(TMMBRSet* candidateSet, int index, InputSet set) - { - candidateSet->ptrTmmbrSet[index] = set.TMMBR; - candidateSet->ptrPacketOHSet[index] = set.packetOH; - candidateSet->ptrSsrcSet[index] = set.SSRC; - }; - - void Start() - { - // Get sets - TMMBRSet* candidateSet = CandidateSet(); - assert(0 == candidateSet->sizeOfSet); - TMMBRSet* boundingSet = BoundingSet(); - assert(0 == boundingSet->sizeOfSet); - TMMBRSet* boundingSetToSend = BoundingSetToSend(); - assert(0 == boundingSetToSend->sizeOfSet); - - WebRtc_Word32 numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(0 == numBoundingSet); // should be empty - - assert( 0 == SetTMMBRBoundingSetToSend(NULL,0)); // ok to send empty set - assert( 0 == SetTMMBRBoundingSetToSend(boundingSet,0)); // ok to send empty set - - WebRtc_UWord32 minBitrateKbit = 0; - WebRtc_UWord32 maxBitrateKbit = 0; - assert(-1 == CalcMinMaxBitRate(0, 0, 1, false, minBitrateKbit, maxBitrateKbit)); // no bounding set - - // --------------------------------- - // Test candidate set {0} -> {0} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(1); - assert(1 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(1 == numBoundingSet); - Verify(boundingSet, 0, set0); - - // Is owner of set - assert(!IsOwner(set0.SSRC, 0)); // incorrect length - assert(!IsOwner(set1.SSRC, 100)); // incorrect length - - assert( IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set0); - - // Get net bitrate depending on packet rate - assert( 0 == CalcMinMaxBitRate(0, numBoundingSet, false,0, minBitrateKbit, maxBitrateKbit)); - assert(set0.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - assert(0 == CalcMinMaxBitRate(0, 100, false,0, minBitrateKbit, maxBitrateKbit)); // incorrect length - assert(set0.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {0,1} -> {1} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(2); - assert(2 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - Add(candidateSet, 1, set1); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(1 == numBoundingSet); - Verify(boundingSet, 0, set1); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert( IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set1); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set1.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {0,1,2} -> {2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(3); - assert(3 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - Add(candidateSet, 1, set1); - Add(candidateSet, 2, set2); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(1 == numBoundingSet); - Verify(boundingSet, 0, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set2.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {0,1,2,3} -> {3,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(4); - assert(4 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - Add(candidateSet, 1, set1); - Add(candidateSet, 2, set2); - Add(candidateSet, 3, set3); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {0,1,2,3,4} -> {3,4,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(5); - assert(5 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - Add(candidateSet, 1, set1); - Add(candidateSet, 2, set2); - Add(candidateSet, 3, set3); - Add(candidateSet, 4, set4); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(3 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set4); - Verify(boundingSetToSend, 2, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {0,1,2,3,4,5} -> {3,4,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(6); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set0); - Add(candidateSet, 1, set1); - Add(candidateSet, 2, set2); - Add(candidateSet, 3, set3); - Add(candidateSet, 4, set4); - Add(candidateSet, 5, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(3 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set4); - Verify(boundingSetToSend, 2, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set0.TMMBR == maxBitrateKbit); - - - // --------------------------------- - // Test candidate set {1,2,3,4,5} -> {3,4,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(5); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set2); - Add(candidateSet, 2, set3); - Add(candidateSet, 3, set4); - Add(candidateSet, 4, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(3 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - Verify(boundingSet, 2, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - assert(boundingSetToSend->sizeOfSet == numBoundingSet); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set4); - Verify(boundingSetToSend, 2, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - - // --------------------------------- - // Test candidate set {1,3,4,5} -> {3,4} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(4); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set3); - Add(candidateSet, 2, set4); - Add(candidateSet, 3, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set4); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set4); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet,true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {1,2,4,5} -> {4,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(4); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set2); - Add(candidateSet, 2, set4); - Add(candidateSet, 3, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set4); - Verify(boundingSet, 1, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert(!IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set4); - Verify(boundingSetToSend, 1, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set4.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {1,2,3,5} -> {3,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(4); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set2); - Add(candidateSet, 2, set3); - Add(candidateSet, 3, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert(!IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {1,2,3,4_1,5} -> {3,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(5); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set2); - Add(candidateSet, 2, set3); - Add(candidateSet, 3, set4_1); - Add(candidateSet, 4, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set3); - Verify(boundingSet, 1, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert( IsOwner(set3.SSRC, numBoundingSet)); - assert(!IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set3); - Verify(boundingSetToSend, 1, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set3.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {1,2,3,4_2,5} -> {4_2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(5); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set1); - Add(candidateSet, 1, set2); - Add(candidateSet, 2, set3); - Add(candidateSet, 3, set4_2); - Add(candidateSet, 4, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(1 == numBoundingSet); - Verify(boundingSet, 0, set4_2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - assert(!IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set4_2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0, numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(MIN_VIDEO_BW_MANAGEMENT_BITRATE == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {} -> {} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(0); - assert(6 == candidateSet->sizeOfSet); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(0 == numBoundingSet); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - assert(!IsOwner(set3.SSRC, numBoundingSet)); - assert(!IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - - // Get net bitrate depending on packet rate - assert(-1 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - - // --------------------------------- - // Test candidate set {x0,5} -> {5} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(2); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set00); - Add(candidateSet, 1, set5); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(1 == numBoundingSet); - Verify(boundingSet, 0, set5); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert(!IsOwner(set2.SSRC, numBoundingSet)); - assert(!IsOwner(set3.SSRC, numBoundingSet)); - assert(!IsOwner(set4.SSRC, numBoundingSet)); - assert( IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set5); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set5.TMMBR == minBitrateKbit); - assert(set5.TMMBR == maxBitrateKbit); - - // --------------------------------- - // Test candidate set {x0,4,2} -> {4,2} - // --------------------------------- - candidateSet = VerifyAndAllocateCandidateSet(3); - assert(6 == candidateSet->sizeOfSet); - Add(candidateSet, 0, set00); - Add(candidateSet, 1, set4); - Add(candidateSet, 2, set2); - - // Find bounding set - numBoundingSet = FindTMMBRBoundingSet(boundingSet); - assert(2 == numBoundingSet); - Verify(boundingSet, 0, set4); - Verify(boundingSet, 1, set2); - - // Is owner of set - assert(!IsOwner(set0.SSRC, numBoundingSet)); - assert(!IsOwner(set1.SSRC, numBoundingSet)); - assert( IsOwner(set2.SSRC, numBoundingSet)); - assert(!IsOwner(set3.SSRC, numBoundingSet)); - assert( IsOwner(set4.SSRC, numBoundingSet)); - assert(!IsOwner(set5.SSRC, numBoundingSet)); - - // Set boundingSet to send - assert(0 == SetTMMBRBoundingSetToSend(boundingSet, maxBitrate)); - - // Get boundingSet to send - boundingSetToSend = BoundingSetToSend(); - Verify(boundingSetToSend, 0, set4); - Verify(boundingSetToSend, 1, set2); - - // Get net bitrate depending on packet rate - assert(0 == CalcMinMaxBitRate(0,numBoundingSet, true,0, minBitrateKbit, maxBitrateKbit)); - assert(set4.TMMBR == minBitrateKbit); - assert(set2.TMMBR == maxBitrateKbit); - }; -}; - -class NULLDataZink: public RtpData -{ - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader, - const WebRtc_UWord8* incomingRtpPacket, - const WebRtc_UWord16 incomingRtpPacketLengt) - { - return 0; - }; -}; - - -int _tmain(int argc, _TCHAR* argv[]) -{ - - std::string str; - std::cout << "------------------------" << std::endl; - std::cout << "------ Test TMMBR ------" << std::endl; - std::cout << "------------------------" << std::endl; - std::cout << " " << std::endl; - - // -------------------- - // Test TMMBRHelp class - // -------------------- - TestTMMBR test; - test.Start(); - - printf("TMMBRHelp-class test done.\n"); - - // ------------------------ - // Test TMMBR single module - // ------------------------ - RtpRtcp* rtpRtcpModuleVideo = RtpRtcp::CreateRtpRtcp(0, false); - - LoopBackTransportVideo* myLoopBackTransportVideo = new LoopBackTransportVideo(rtpRtcpModuleVideo); - assert(0 == rtpRtcpModuleVideo->RegisterSendTransport(myLoopBackTransportVideo)); - - assert(false == rtpRtcpModuleVideo->TMMBR()); - rtpRtcpModuleVideo->SetTMMBRStatus(true); - assert(true == rtpRtcpModuleVideo->TMMBR()); - - assert(0 == rtpRtcpModuleVideo->RegisterSendPayload( "I420", 96)); - assert(0 == rtpRtcpModuleVideo->RegisterReceivePayload( "I420", 96)); - - // send a RTP packet with SSRC 11111 to get 11111 as the received SSRC - assert(0 == rtpRtcpModuleVideo->SetSSRC(11111)); - const WebRtc_UWord8 testStream[9] = "testtest"; - assert(0 == rtpRtcpModuleVideo->RegisterIncomingDataCallback(new NULLDataZink())); // needed to avoid error from parsing the incoming stream - assert(0 == rtpRtcpModuleVideo->SendOutgoingData(webrtc::kVideoFrameKey,96, 0, testStream, 8)); - - // set the SSRC to 0 - assert(0 == rtpRtcpModuleVideo->SetSSRC(0)); - - // - assert(0 == rtpRtcpModuleVideo->SetRTCPStatus(kRtcpCompound)); - - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {0} // should this make us remember a TMMBR? - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {1}, verify TMMBN {0} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {2}, verify TMMBN {1} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {3}, verify TMMBN {2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {4}, verify TMMBN {3,2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {5}, verify TMMBN {3,4,2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {4_2}, verify TMMBN {3,4,2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> time out receivers, verify TMMBN {4_2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> verify TMMBN {2} - - printf("Single module test done.\n"); - - // ------------------------ - // Test TMMBR multi module - // ------------------------ - RtpRtcp* rtpRtcpModuleVideoDef = RtpRtcp::CreateRtpRtcp(10, false); - assert(0 == rtpRtcpModuleVideo->RegisterDefaultModule(rtpRtcpModuleVideoDef)); - - RtpRtcp* rtpRtcpModuleVideo1 = RtpRtcp::CreateRtpRtcp(1, false); - assert(0 == rtpRtcpModuleVideo1->RegisterDefaultModule(rtpRtcpModuleVideoDef)); - - RtpRtcp* rtpRtcpModuleVideo2 = RtpRtcp::CreateRtpRtcp(2, false); - assert(0 == rtpRtcpModuleVideo2->RegisterDefaultModule(rtpRtcpModuleVideoDef)); - - RtpRtcp* rtpRtcpModuleVideo3 = RtpRtcp::CreateRtpRtcp(3, false); - assert(0 == rtpRtcpModuleVideo3->RegisterDefaultModule(rtpRtcpModuleVideoDef)); - - LoopBackTransport2* myLoopBackTransport2 = new LoopBackTransport2(rtpRtcpModuleVideo2); - assert(0 == rtpRtcpModuleVideo2->RegisterSendTransport(myLoopBackTransport2)); - - assert(0 == rtpRtcpModuleVideo2->SetRTCPStatus(kRtcpCompound)); - - // set the SSRC to 0 - assert(0 == rtpRtcpModuleVideo2->SetSSRC(0)); - - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {4}, verify no TMMBN in this packet - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {0}, verify TMMBN {4,2} - assert(0 == rtpRtcpModuleVideo2->SendRTCP()); // -> incoming TMMBR {3}, verify TMMBN {4,2} - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // -> incoming TMMBR {1}, verify TMMBN {3,4,2} - ::Sleep(5*RTCP_INTERVAL_AUDIO_MS + 1000); - rtpRtcpModuleVideo2->Process(); // time out receiver2 -> UpdateTMMBR() - assert(0 == rtpRtcpModuleVideo->SendRTCP()); // verify TMMBN {} - - printf("Multi module test done.\n"); - - - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo1); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo2); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideo3); - RtpRtcp::DestroyRtpRtcp(rtpRtcpModuleVideoDef); - - TEST_PASSED(); - ::Sleep(5000); - - return 0; -} - diff --git a/modules/rtp_rtcp/test/test_bwe/test_bwe.gyp b/modules/rtp_rtcp/test/test_bwe/test_bwe.gyp deleted file mode 100644 index cce7f42d7..000000000 --- a/modules/rtp_rtcp/test/test_bwe/test_bwe.gyp +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'test_bwe', - 'type': 'executable', - 'dependencies': [ - '../../source/rtp_rtcp.gyp:rtp_rtcp', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../../../testing/gtest.gyp:gtest', - '../../../../../testing/gtest.gyp:gtest_main', - ], - 'include_dirs': [ - '../../source', - ], - 'sources': [ - 'unit_test.cc', - '../../source/bitrate.cc', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/rtp_rtcp/test/test_bwe/unit_test.cc b/modules/rtp_rtcp/test/test_bwe/unit_test.cc deleted file mode 100644 index 73f64f1e4..000000000 --- a/modules/rtp_rtcp/test/test_bwe/unit_test.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -/* - * This file includes unit tests for the bandwidth estimation and management - */ - -#include - -#include "typedefs.h" -#include "Bitrate.h" - -namespace { - -using webrtc::BitRateStats; - -class BitRateStatsTest : public ::testing::Test -{ -protected: - BitRateStatsTest() {}; - BitRateStats bitRate; -}; - -TEST_F(BitRateStatsTest, TestStrictMode) -{ - WebRtc_Word64 nowMs = 0; - // Should be initialized to 0. - EXPECT_EQ(0, bitRate.BitRate(nowMs)); - bitRate.Update(1500, nowMs); - // Expecting 12 kbps given a 1000 window with one 1500 bytes packet. - EXPECT_EQ(12000, bitRate.BitRate(nowMs)); - bitRate.Init(); - // Expecting 0 after init. - EXPECT_EQ(0, bitRate.BitRate(nowMs)); - for (int i = 0; i < 100000; ++i) - { - if (nowMs % 10 == 0) - bitRate.Update(1500, nowMs); - // Approximately 1200 kbps expected. Not exact since when packets - // are removed we will jump 10 ms to the next packet. - if (nowMs > 0 && nowMs % 2000 == 0) - EXPECT_NEAR(1200000, bitRate.BitRate(nowMs), 6000); - nowMs += 1; - } - nowMs += 2000; - // The window is 2 seconds. If nothing has been received for that time - // the estimate should be 0. - EXPECT_EQ(0, bitRate.BitRate(nowMs)); -} - -} diff --git a/modules/udp_transport/OWNERS b/modules/udp_transport/OWNERS deleted file mode 100644 index ded5e8bd3..000000000 --- a/modules/udp_transport/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -hellner@google.com -pwestin@google.com -grunell@google.com -mallinath@google.com -tomasl@google.com \ No newline at end of file diff --git a/modules/udp_transport/interface/udp_transport.h b/modules/udp_transport/interface/udp_transport.h deleted file mode 100644 index bcc0cf6e3..000000000 --- a/modules/udp_transport/interface/udp_transport.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_INTERFACE_UDP_TRANSPORT_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_INTERFACE_UDP_TRANSPORT_H_ - -#include "common_types.h" -#include "module.h" -#include "typedefs.h" - -#define SS_MAXSIZE 128 -#define SS_ALIGNSIZE (sizeof (WebRtc_UWord64)) -#define SS_PAD1SIZE (SS_ALIGNSIZE - sizeof(WebRtc_Word16)) -#define SS_PAD2SIZE (SS_MAXSIZE - (sizeof(WebRtc_Word16) + SS_PAD1SIZE +\ - SS_ALIGNSIZE)) - -// BSD requires use of HAVE_STRUCT_SOCKADDR_SA_LEN -namespace webrtc { -struct SocketAddressIn -{ - // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - WebRtc_Word8 sin_length; - WebRtc_Word8 sin_family; -#else - WebRtc_Word16 sin_family; -#endif - WebRtc_UWord16 sin_port; - WebRtc_UWord32 sin_addr; - WebRtc_Word8 sin_zero[8]; -}; - -struct Version6InAddress -{ - union - { - WebRtc_UWord8 _s6_u8[16]; - WebRtc_UWord32 _s6_u32[4]; - WebRtc_UWord64 _s6_u64[2]; - } Version6AddressUnion; -}; - -struct SocketAddressInVersion6 -{ - // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - WebRtc_Word8 sin_length; - WebRtc_Word8 sin_family; -#else - WebRtc_Word16 sin_family; -#endif - // Transport layer port number. - WebRtc_UWord16 sin6_port; - // IPv6 traffic class and flow info or ip4 address. - WebRtc_UWord32 sin6_flowinfo; - // IPv6 address - struct Version6InAddress sin6_addr; - // Set of interfaces for a scope. - WebRtc_UWord32 sin6_scope_id; -}; - -struct SocketAddressStorage -{ - // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - WebRtc_Word8 sin_length; - WebRtc_Word8 sin_family; -#else - WebRtc_Word16 sin_family; -#endif - WebRtc_Word8 __ss_pad1[SS_PAD1SIZE]; - WebRtc_UWord64 __ss_align; - WebRtc_Word8 __ss_pad2[SS_PAD2SIZE]; -}; - -struct SocketAddress -{ - union - { - struct SocketAddressIn _sockaddr_in; - struct SocketAddressInVersion6 _sockaddr_in6; - struct SocketAddressStorage _sockaddr_storage; - }; -}; - -// Callback class that receives packets from UdpTransport. -class UdpTransportData -{ -public: - virtual ~UdpTransportData() {}; - - virtual void IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) = 0; - - virtual void IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) = 0; -}; - - -class UdpTransport : public Module, public Transport -{ -public: - enum - { - kIpAddressVersion6Length = 64, - kIpAddressVersion4Length = 16 - }; - enum ErrorCode - { - kNoSocketError = 0, - kFailedToBindPort = 1, - kIpAddressInvalid = 2, - kAddressInvalid = 3, - kSocketInvalid = 4, - kPortInvalid = 5, - kTosInvalid = 6, - kMulticastAddressInvalid = 7, - kQosError = 8, - kSocketAlreadyInitialized = 9, - kIpVersion6Error = 10, - FILTER_ERROR = 11, - kStartReceiveError = 12, - kStopReceiveError = 13, - kCannotFindLocalIp = 14, - kTosError = 16, - kNotInitialized = 17, - kPcpError = 18 - }; - - // Factory method. Constructor disabled. - static UdpTransport* Create(const WebRtc_Word32 id, - WebRtc_UWord8& numSocketThreads); - static void Destroy(UdpTransport* module); - - // Prepares the class for sending RTP packets to ipAddr:rtpPort and RTCP - // packets to ipAddr:rtpPort+1 if rtcpPort is zero. Otherwise to - // ipAddr:rtcpPort. - virtual WebRtc_Word32 InitializeSendSockets( - const WebRtc_Word8* ipAddr, - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0) = 0; - - // Register packetCallback for receiving incoming packets. Set the local - // RTP port to rtpPort. Bind local IP address to ipAddr. If ipAddr is NULL - // bind to local IP ANY. Set the local rtcp port to rtcpPort or rtpPort + 1 - // if rtcpPort is 0. - virtual WebRtc_Word32 InitializeReceiveSockets( - UdpTransportData* const packetCallback, - const WebRtc_UWord16 rtpPort, - const WebRtc_Word8* ipAddr = NULL, - const WebRtc_Word8* multicastIpAddr = NULL, - const WebRtc_UWord16 rtcpPort = 0) = 0; - - // Set local RTP port to rtpPort and RTCP port to rtcpPort or rtpPort + 1 if - // rtcpPort is 0. These ports will be used for sending instead of the local - // ports set by InitializeReceiveSockets(..). - virtual WebRtc_Word32 InitializeSourcePorts( - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0) = 0; - - // Retrieve local ports used for sending if other than the ports specified - // by InitializeReceiveSockets(..). rtpPort is set to the RTP port. - // rtcpPort is set to the RTCP port. - virtual WebRtc_Word32 SourcePorts(WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const = 0; - - // Set ipAddr to the IP address that is currently being listened on. rtpPort - // to the RTP port listened to. rtcpPort to the RTCP port listened on. - // multicastIpAddr to the multicast IP address group joined (the address - // is NULL terminated). - virtual WebRtc_Word32 ReceiveSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort, - WebRtc_Word8 multicastIpAddr[kIpAddressVersion6Length]) const = 0; - - // Set ipAddr to the IP address being sent from. rtpPort to the local RTP - // port used for sending and rtcpPort to the local RTCP port used for - // sending. - virtual WebRtc_Word32 SendSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const = 0; - - // Put the IP address, RTP port and RTCP port from the last received packet - // into ipAddr, rtpPort and rtcpPort respectively. - virtual WebRtc_Word32 RemoteSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const = 0; - - // Enable/disable quality of service if QoS is true or false respectively. - // Set the type of service to serviceType, max bitrate in kbit/s to - // maxBitrate and override DSCP if overrideDSCP is not 0. - // Note: Must be called both InitializeSendSockets() and - // InitializeReceiveSockets() has been called. - virtual WebRtc_Word32 SetQoS(const bool QoS, - const WebRtc_Word32 serviceType, - const WebRtc_UWord32 maxBitrate = 0, - const WebRtc_Word32 overrideDSCP = 0, - const bool audio = false) = 0; - - // Set QoS to true if quality of service has been turned on. If QoS is true, - // also set serviceType to type of service and overrideDSCP to override - // DSCP. - virtual WebRtc_Word32 QoS(bool& QoS, - WebRtc_Word32& serviceType, - WebRtc_Word32& overrideDSCP) const = 0; - - // Set type of service. - virtual WebRtc_Word32 SetToS(const WebRtc_Word32 DSCP, - const bool useSetSockOpt = false) = 0; - - // Get type of service configuration. - virtual WebRtc_Word32 ToS(WebRtc_Word32& DSCP, - bool& useSetSockOpt) const = 0; - - // Set Priority Code Point (IEEE 802.1Q) - // Note: for Linux this function will set the priority for the socket, - // which then can be mapped to a PCP value with vconfig. - virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 PCP) = 0; - - // Get Priority Code Point - virtual WebRtc_Word32 PCP(WebRtc_Word32& PCP) const = 0; - - // Enable IPv6. - // Note: this API must be called before any call to - // InitializeReceiveSockets() or InitializeSendSockets(). It is not - // possible to go back to IPv4 (default) after this call. - virtual WebRtc_Word32 EnableIpV6() = 0; - - // Return true if IPv6 has been enabled. - virtual bool IpV6Enabled() const = 0; - - // Only allow packets received from filterIPAddress to be processed. - // Note: must be called after EnableIPv6(), if IPv6 is used. - virtual WebRtc_Word32 SetFilterIP( - const WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]) = 0; - - // Write the filter IP address (if any) to filterIPAddress. - virtual WebRtc_Word32 FilterIP( - WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]) const = 0; - - // Only allow RTP packets from rtpFilterPort and RTCP packets from - // rtcpFilterPort be processed. - // Note: must be called after EnableIPv6(), if IPv6 is used. - virtual WebRtc_Word32 SetFilterPorts( - const WebRtc_UWord16 rtpFilterPort, - const WebRtc_UWord16 rtcpFilterPort) = 0; - - // Set rtpFilterPort to the filter RTP port and rtcpFilterPort to the - // filter RTCP port (if filtering based on port is enabled). - virtual WebRtc_Word32 FilterPorts(WebRtc_UWord16& rtpFilterPort, - WebRtc_UWord16& rtcpFilterPort) const = 0; - - // Set the number of buffers that the socket implementation may use for - // receiving packets to numberOfSocketBuffers. I.e. the number of packets - // that can be received in parallell. - // Note: this API only has effect on Windows. - virtual WebRtc_Word32 StartReceiving( - const WebRtc_UWord32 numberOfSocketBuffers) = 0; - - // Stop receive incoming packets. - virtual WebRtc_Word32 StopReceiving() = 0; - - // Return true incoming packets are received. - virtual bool Receiving() const = 0; - - // Return true if send sockets have been initialized. - virtual bool SendSocketsInitialized() const = 0; - - // Return true if local ports for sending has been set. - virtual bool SourcePortsInitialized() const = 0; - - // Return true if receive sockets have been initialized. - virtual bool ReceiveSocketsInitialized() const = 0; - - // Send data with size length to ip:portnr. The same port as the set - // with InitializeSendSockets(..) is used if portnr is 0. The same IP - // address as set with InitializeSendSockets(..) is used if ip is NULL. - // If isRTCP is true the port used will be the RTCP port. - virtual WebRtc_Word32 SendRaw(const WebRtc_Word8* data, - WebRtc_UWord32 length, - WebRtc_Word32 isRTCP, - WebRtc_UWord16 portnr = 0, - const WebRtc_Word8* ip = NULL) = 0; - - // Send RTP data with size length to the address specified by to. - virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const SocketAddress& to) = 0; - - - // Send RTCP data with size length to the address specified by to. - virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const SocketAddress& to) = 0; - - // Send RTP data with size length to ip:rtpPort where ip is the ip set by - // the InitializeSendSockets(..) call. - virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - WebRtc_UWord16 rtpPort) = 0; - - - // Send RTCP data with size length to ip:rtcpPort where ip is the ip set by - // the InitializeSendSockets(..) call. - virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - WebRtc_UWord16 rtcpPort) = 0; - - // Set the IP address to which packets are sent to ipaddr. - virtual WebRtc_Word32 SetSendIP( - const WebRtc_Word8 ipaddr[kIpAddressVersion6Length]) = 0; - - // Set the send RTP and RTCP port to rtpPort and rtcpPort respectively. - virtual WebRtc_Word32 SetSendPorts(const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0) = 0; - - // Retreive the last registered error code. - virtual ErrorCode LastError() const = 0; - - // Put the local IPv4 address in localIP. - // Note: this API is for IPv4 only. - static WebRtc_Word32 LocalHostAddress(WebRtc_UWord32& localIP); - - // Put the local IP6 address in localIP. - // Note: this API is for IPv6 only. - static WebRtc_Word32 LocalHostAddressIPV6(WebRtc_UWord8 localIP[16]); - - // Return a copy of hostOrder (host order) in network order. - static WebRtc_UWord16 Htons(WebRtc_UWord16 hostOrder); - - // Return a copy of hostOrder (host order) in network order. - static WebRtc_UWord32 Htonl(WebRtc_UWord32 hostOrder); - - // Return IPv4 address in ip as 32 bit integer. - static WebRtc_UWord32 InetAddrIPV4(const WebRtc_Word8* ip); - - // Convert the character string src into a network address structure in - // the af address family and put it in dst. - // Note: same functionality as inet_pton(..) - static WebRtc_Word32 InetPresentationToNumeric(WebRtc_Word32 af, - const WebRtc_Word8* src, - void* dst); - - // Set ip and sourcePort according to address. As input parameter ipSize - // is the length of ip. As output parameter it's the number of characters - // written to ip (not counting the '\0' character). - // Note: this API is only implemented on Windows and Linux. - static WebRtc_Word32 IPAddress(const SocketAddress& address, - WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort); - - // Set ip and sourcePort according to address. As input parameter ipSize - // is the length of ip. As output parameter it's the number of characters - // written to ip (not counting the '\0' character). - // Note: this API is only implemented on Windows and Linux. - // Additional note: this API caches the address of the last call to it. If - // address is likley to be the same for multiple calls it may be beneficial - // to call this API instead of IPAddress(). - virtual WebRtc_Word32 IPAddressCached(const SocketAddress& address, - WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort) = 0; - - // Return true if ipaddr is a valid IP address. - // If ipV6 is false ipaddr is interpreted as an IPv4 address otherwise it - // is interptreted as IPv6. - static bool IsIpAddressValid(const WebRtc_Word8* ipaddr, const bool ipV6); -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_INTERFACE_UDP_TRANSPORT_H_ diff --git a/modules/udp_transport/source/Android.mk b/modules/udp_transport/source/Android.mk deleted file mode 100644 index b1156685f..000000000 --- a/modules/udp_transport/source/Android.mk +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_udp_transport -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := udp_transport_impl.cc \ - udp_socket_wrapper.cc \ - udp_socket_manager_wrapper.cc \ - udp_socket_manager_linux.cc \ - udp_socket_linux.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/udp_transport/source/traffic_control_windows.cc b/modules/udp_transport/source/traffic_control_windows.cc deleted file mode 100644 index 09038c06f..000000000 --- a/modules/udp_transport/source/traffic_control_windows.cc +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "traffic_control_windows.h" - -#include - -#include "trace.h" - -namespace webrtc { -TrafficControlWindows* TrafficControlWindows::instance = NULL; -WebRtc_UWord32 TrafficControlWindows::refCounter = 0; - -TrafficControlWindows::TrafficControlWindows(const WebRtc_Word32 id) : _id(id) -{ -} - -TrafficControlWindows* TrafficControlWindows::GetInstance( - const WebRtc_Word32 id) -{ - if(instance != NULL) - { - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - id, - "TrafficControlWindows - Returning already created object"); - refCounter++; - return instance; - } - - WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, - "TrafficControlWindows - Creating new object"); - instance = new TrafficControlWindows(id); - if(instance == NULL) - { - WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, - "TrafficControlWindows - Error allocating memory"); - return NULL; - } - - instance->tcRegister = NULL; - instance->tcDeregister = NULL; - - instance->tcEnumerate = NULL; - instance->tcOpenInterface = NULL; - instance->tcCloseInterface = NULL; - - instance->tcAddFlow = NULL; - instance->tcDeleteFlow = NULL; - - instance->tcAddFilter = NULL; - instance->tcDeleteFilter = NULL; - - HMODULE trafficLib = LoadLibrary(TEXT("traffic.dll")); - if(trafficLib == NULL) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceTransport, - id, - "TrafficControlWindows - No QOS support, LoadLibrary returned NULL,\ - last error: %d\n", - GetLastError()); - delete instance; - instance = NULL; - return NULL; - } - - instance->tcRegister = (registerFn)GetProcAddress(trafficLib, - "TcRegisterClient"); - instance->tcDeregister = (deregisterFn)GetProcAddress(trafficLib, - "TcDeregisterClient"); - instance->tcEnumerate = (enumerateFn)GetProcAddress( - trafficLib, - "TcEnumerateInterfaces"); - instance->tcOpenInterface = (openInterfaceFn)GetProcAddress( - trafficLib, - "TcOpenInterfaceW"); - instance->tcCloseInterface = (closeInterfaceFn)GetProcAddress( - trafficLib, - "TcCloseInterface"); - instance->tcAddFlow = (flowAddFn)GetProcAddress(trafficLib, - "TcAddFlow"); - instance->tcDeleteFlow = (flowDeleteFn)GetProcAddress(trafficLib, - "TcDeleteFlow"); - - instance->tcAddFilter = (filterAddFn)GetProcAddress(trafficLib, - "TcAddFilter"); - instance->tcDeleteFilter = (filterDeleteFn)GetProcAddress(trafficLib, - "TcDeleteFilter"); - - if(instance->tcRegister == NULL || - instance->tcDeregister == NULL || - instance->tcEnumerate == NULL || - instance->tcOpenInterface == NULL || - instance->tcCloseInterface == NULL || - instance->tcAddFlow == NULL || - instance->tcAddFilter == NULL || - instance->tcDeleteFlow == NULL || - instance->tcDeleteFilter == NULL) - { - delete instance; - instance = NULL; - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - id, - "TrafficControlWindows - Could not find function pointer for\ - traffic control functions"); - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - id, - "Tcregister : %x, tcDeregister: %x, tcEnumerate: %x,\ - tcOpenInterface: %x, tcCloseInterface: %x, tcAddFlow: %x, tcAddFilter: %x,\ - tcDeleteFlow: %x, tcDeleteFilter: %x", - instance->tcRegister, - instance->tcDeregister, - instance->tcEnumerate, - instance->tcOpenInterface, - instance->tcCloseInterface, - instance->tcAddFlow, - instance->tcAddFilter, - instance->tcDeleteFlow, - instance->tcDeleteFilter ); - return NULL; - } - refCounter++; - return instance; -} - -void TrafficControlWindows::Release(TrafficControlWindows* gtc) -{ - if (0 == refCounter) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, -1, - "TrafficControlWindows - Cannot release, refCounter is 0"); - return; - } - if (NULL == gtc) - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, -1, - "TrafficControlWindows - Not releasing, gtc is NULL"); - return; - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, gtc->_id, - "TrafficControlWindows - Releasing object"); - refCounter--; - if ((0 == refCounter) && instance) - { - WEBRTC_TRACE(kTraceMemory, kTraceTransport, gtc->_id, - "TrafficControlWindows - Deleting object"); - delete instance; - instance = NULL; - } -} -WebRtc_Word32 TrafficControlWindows::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -ULONG TrafficControlWindows::TcRegisterClient( - ULONG TciVersion, - HANDLE ClRegCtx, - PTCI_CLIENT_FUNC_LIST ClientHandlerList, - PHANDLE pClientHandle) -{ - assert(tcRegister != NULL); - - return tcRegister(TciVersion, ClRegCtx, ClientHandlerList, pClientHandle); -} - -ULONG TrafficControlWindows::TcDeregisterClient(HANDLE clientHandle) -{ - assert(tcDeregister != NULL); - - return tcDeregister(clientHandle); -} - - -ULONG TrafficControlWindows::TcEnumerateInterfaces( - HANDLE ClientHandle, - PULONG pBufferSize, - PTC_IFC_DESCRIPTOR interfaceBuffer) -{ - assert(tcEnumerate != NULL); - - return tcEnumerate(ClientHandle, pBufferSize, interfaceBuffer); -} - - -ULONG TrafficControlWindows::TcOpenInterfaceW(LPWSTR pInterfaceName, - HANDLE ClientHandle, - HANDLE ClIfcCtx, - PHANDLE pIfcHandle) -{ - assert(tcOpenInterface != NULL); - - return tcOpenInterface(pInterfaceName, ClientHandle, ClIfcCtx, pIfcHandle); - -} - -ULONG TrafficControlWindows::TcCloseInterface(HANDLE IfcHandle) -{ - assert(tcCloseInterface != NULL); - - return tcCloseInterface(IfcHandle); -} - -ULONG TrafficControlWindows::TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx, - ULONG Flags, PTC_GEN_FLOW pGenericFlow, - PHANDLE pFlowHandle) -{ - assert(tcAddFlow != NULL); - return tcAddFlow(IfcHandle, ClFlowCtx, Flags, pGenericFlow, pFlowHandle); -} - -ULONG TrafficControlWindows::TcAddFilter(HANDLE FlowHandle, - PTC_GEN_FILTER pGenericFilter, - PHANDLE pFilterHandle) -{ - assert(tcAddFilter != NULL); - return tcAddFilter(FlowHandle, pGenericFilter, pFilterHandle); -} - -ULONG TrafficControlWindows::TcDeleteFlow(HANDLE FlowHandle) -{ - assert(tcDeleteFlow != NULL); - return tcDeleteFlow(FlowHandle); - -} - -ULONG TrafficControlWindows::TcDeleteFilter(HANDLE FilterHandle) -{ - assert(tcDeleteFilter != NULL); - return tcDeleteFilter(FilterHandle); -} - -void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event, - HANDLE SubCode, ULONG BufSize, PVOID Buffer) -{ -} -} // namespace webrtc diff --git a/modules/udp_transport/source/traffic_control_windows.h b/modules/udp_transport/source/traffic_control_windows.h deleted file mode 100644 index cfa52ce18..000000000 --- a/modules/udp_transport/source/traffic_control_windows.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_TRAFFIC_CONTROL_WINDOWS_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_TRAFFIC_CONTROL_WINDOWS_H_ - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -// Disable deprication warning from traffic.h -#pragma warning(disable : 4995) - -#include -#include -#include -#include - -#include "trace.h" - -namespace webrtc { -void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event, - HANDLE SubCode, ULONG BufSize, PVOID Buffer); - - -typedef ULONG (WINAPI *registerFn)(ULONG, HANDLE, PTCI_CLIENT_FUNC_LIST, - PHANDLE); -typedef ULONG (WINAPI *deregisterFn)(HANDLE); -typedef ULONG (WINAPI *enumerateFn)(HANDLE, PULONG, PTC_IFC_DESCRIPTOR); -typedef ULONG (WINAPI *openInterfaceFn)(LPWSTR, HANDLE, HANDLE, PHANDLE); -typedef ULONG (WINAPI *closeInterfaceFn)(HANDLE); -typedef ULONG (WINAPI *flowAddFn)(HANDLE, HANDLE, ULONG, PTC_GEN_FLOW, PHANDLE); -typedef ULONG (WINAPI *filterAddFn)(HANDLE, PTC_GEN_FILTER, PHANDLE); -typedef ULONG (WINAPI *flowDeleteFn)(HANDLE); -typedef ULONG (WINAPI *filterDeleteFn)(HANDLE); - -class TrafficControlWindows -{ - public: - // Factory method. Constructor disabled. - static TrafficControlWindows* GetInstance(const WebRtc_Word32 id); - static void Release(TrafficControlWindows* gtc); - - WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - ULONG TcRegisterClient(ULONG TciVersion, HANDLE ClRegCtx, - PTCI_CLIENT_FUNC_LIST ClientHandlerList, - PHANDLE pClientHandle); - - ULONG TcDeregisterClient(HANDLE clientHandle); - - ULONG TcEnumerateInterfaces(HANDLE ClientHandle, PULONG pBufferSize, - PTC_IFC_DESCRIPTOR interfaceBuffer); - - ULONG TcOpenInterfaceW(LPWSTR pInterfaceName, HANDLE ClientHandle, - HANDLE ClIfcCtx, PHANDLE pIfcHandle); - - ULONG TcCloseInterface(HANDLE IfcHandle); - - ULONG TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx, ULONG Flags, - PTC_GEN_FLOW pGenericFlow, PHANDLE pFlowHandle); - - ULONG TcAddFilter(HANDLE FlowHandle, PTC_GEN_FILTER pGenericFilter, - PHANDLE pFilterHandle); - - ULONG TcDeleteFlow(HANDLE FlowHandle); - ULONG TcDeleteFilter(HANDLE FilterHandle); -private: - TrafficControlWindows(const WebRtc_Word32 id); - WebRtc_Word32 _id; - TCI_CLIENT_FUNC_LIST QoSFunctions; - - static TrafficControlWindows* instance; - - registerFn tcRegister; - deregisterFn tcDeregister; - - enumerateFn tcEnumerate; - openInterfaceFn tcOpenInterface; - closeInterfaceFn tcCloseInterface; - - flowAddFn tcAddFlow; - flowDeleteFn tcDeleteFlow; - - filterAddFn tcAddFilter; - filterDeleteFn tcDeleteFilter; - - static WebRtc_UWord32 refCounter; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_TRAFFIC_CONTROL_WINDOWS_H_ diff --git a/modules/udp_transport/source/udp_socket2_manager_windows.cc b/modules/udp_transport/source/udp_socket2_manager_windows.cc deleted file mode 100644 index af9eac1bc..000000000 --- a/modules/udp_transport/source/udp_socket2_manager_windows.cc +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket2_manager_windows.h" - -#include -#include - -#include "aligned_malloc.h" -#include "udp_socket2_windows.h" - -namespace webrtc { -WebRtc_UWord32 UdpSocket2ManagerWindows::_numOfActiveManagers = 0; -bool UdpSocket2ManagerWindows::_wsaInit = false; - -UdpSocket2ManagerWindows::UdpSocket2ManagerWindows( - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) - : UdpSocketManager(id, numOfWorkThreads), - _id(id), - _stoped(false), - _init(false), - _pCrit(NULL), - _ioCompletionHandle(NULL), - _numActiveSockets(0), - _numOfWorkThreads(numOfWorkThreads) -{ - _managerNumber = _numOfActiveManagers++; - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2ManagerWindows(%d)::UdpSocket2ManagerWindows()", - _managerNumber); - - if(_numOfActiveManagers == 1) - { - WORD wVersionRequested = MAKEWORD(2, 2); - WSADATA wsaData; - _wsaInit = WSAStartup(wVersionRequested, &wsaData) == 0; - // TODO (hellner): seems safer to use RAII for this. E.g. what happens - // if a UdpSocket2ManagerWindows() created and destroyed - // without being initialized. - } - _event = EventWrapper::Create(); -} - -UdpSocket2ManagerWindows::~UdpSocket2ManagerWindows() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2ManagerWindows(%d)::~UdpSocket2ManagerWindows()", - _managerNumber); - - if(_init) - { - _pCrit->Enter(); - if(_numActiveSockets) - { - _pCrit->Leave(); - _event->Wait(INFINITE); - } - else - { - _pCrit->Leave(); - } - StopWorkerThreads(); - - // All threads are stopped. Safe to delete them. - ListItem* pItem = NULL; - UdpSocket2WorkerWindows* pWorker; - while((pItem = _workerThreadsList.First()) != NULL) - { - pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); - delete pWorker; - _workerThreadsList.PopFront(); - } - - _ioContextPool.Free(); - - _numOfActiveManagers--; - if(_ioCompletionHandle) - { - CloseHandle(_ioCompletionHandle); - } - if (_numOfActiveManagers == 0) - { - if(_wsaInit) - { - WSACleanup(); - } - } - if(_pCrit) - { - delete _pCrit; - } - } - if(_event) - { - delete _event; - } -} - -WebRtc_Word32 UdpSocket2ManagerWindows::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -bool UdpSocket2ManagerWindows::Start() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2ManagerWindows(%d)::Start()",_managerNumber); - if(!_init) - { - Init(); - } - - if(!_init) - { - return false; - } - _pCrit->Enter(); - // Start worker threads. - _stoped = false; - WebRtc_Word32 i = 0; - WebRtc_Word32 error = 0; - ListItem* pItem = _workerThreadsList.First(); - UdpSocket2WorkerWindows* pWorker; - while(pItem != NULL && !error) - { - pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); - if(!pWorker->Start()) - error = 1; - pItem = _workerThreadsList.Next(pItem); - } - if(error) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::Start() error starting worker\ - threads", - _managerNumber); - _pCrit->Leave(); - return false; - } - _pCrit->Leave(); - return true; -} - -WebRtc_Word32 UdpSocket2ManagerWindows::Init() -{ - if(!_init) - { - _pCrit = CriticalSectionWrapper::CreateCriticalSection(); - if(_pCrit == NULL) - { - return -1; - } - _pCrit->Enter(); - - _ioCompletionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, - 0, 0); - if(_ioCompletionHandle == NULL) - { - WebRtc_Word32 error = GetLastError(); - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::Init()\ - _ioCompletioHandle == NULL: error:%d", - _managerNumber,error); - _pCrit->Leave(); - return -1; - } - - // Create worker threads. - WebRtc_UWord32 i = 0; - WebRtc_Word32 error = 0; - while(i < _numOfWorkThreads && !error) - { - UdpSocket2WorkerWindows* pWorker = - new UdpSocket2WorkerWindows(_ioCompletionHandle); - if(pWorker == NULL) - { - error = 1; - break; - } - if(pWorker->Init()) - { - error = 1; - delete pWorker; - break; - } - _workerThreadsList.PushFront(pWorker); - i++; - } - if(error) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::Init() error creating work\ - threads", - _managerNumber); - // Delete worker threads. - ListItem* pItem = NULL; - UdpSocket2WorkerWindows* pWorker; - while((pItem = _workerThreadsList.First()) != NULL) - { - pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); - delete pWorker; - _workerThreadsList.PopFront(); - } - _pCrit->Leave(); - return -1; - } - if(_ioContextPool.Init()) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::Init() error initiating\ - _ioContextPool", - _managerNumber); - _pCrit->Leave(); - return -1; - } - _init = true; - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows::Init() %d number of work threads\ - created and init", - _numOfWorkThreads); - _pCrit->Leave(); - } - return 0; -} - -bool UdpSocket2ManagerWindows::Stop() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2ManagerWindows(%d)::Stop()",_managerNumber); - - if(!_init) - { - return false; - } - _pCrit->Enter(); - _stoped = true; - if(_numActiveSockets) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::Stop() there is still active\ - sockets", - _managerNumber); - _pCrit->Leave(); - return false; - } - // No active sockets. Stop all worker threads. - bool result = StopWorkerThreads(); - _pCrit->Leave(); - return result; -} -bool UdpSocket2ManagerWindows::StopWorkerThreads() -{ - WebRtc_Word32 error = 0; - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() Worker\ - threadsStoped, numActicve Sockets=%d", - _managerNumber, - _numActiveSockets); - UdpSocket2WorkerWindows* pWorker; - ListItem* pItem = _workerThreadsList.First(); - - // Set worker threads to not alive so that they will stop calling - // UdpSocket2WorkerWindows::Run(). - while(pItem != NULL) - { - pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); - pWorker->SetNotAlive(); - pItem = _workerThreadsList.Next(pItem); - } - // Release all threads waiting for GetQueuedCompletionStatus(..). - if(_ioCompletionHandle) - { - WebRtc_UWord32 i = 0; - for(i = 0; i < _workerThreadsList.GetSize(); i++) - { - PostQueuedCompletionStatus(_ioCompletionHandle, 0 ,0 , NULL); - } - } - pItem = _workerThreadsList.First(); - - while(pItem != NULL) - { - pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); - if(pWorker->Stop() == false) - { - error = -1; - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, - "failed to stop worker thread"); - } - pItem = _workerThreadsList.Next(pItem); - } - - if(error) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() error stopping\ - worker threads", - _managerNumber); - return false; - } - return true; -} - -bool UdpSocket2ManagerWindows::AddSocketPrv(UdpSocket2Windows* s) -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2ManagerWindows(%d)::AddSocketPrv()",_managerNumber); - if(!_init) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::AddSocketPrv() manager not\ - initialized", - _managerNumber); - return false; - } - _pCrit->Enter(); - if(s == NULL) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket == NULL", - _managerNumber); - _pCrit->Leave(); - return false; - } - if(s->GetFd() == NULL || s->GetFd() == INVALID_SOCKET) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket->GetFd() ==\ - %d", - _managerNumber, - (WebRtc_Word32)s->GetFd()); - _pCrit->Leave(); - return false; - - } - _ioCompletionHandle = CreateIoCompletionPort((HANDLE)s->GetFd(), - _ioCompletionHandle, - (ULONG_PTR)(s), 0); - if(_ioCompletionHandle == NULL) - { - WebRtc_Word32 error = GetLastError(); - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::AddSocketPrv() Error adding to IO\ - completion: %d", - _managerNumber, - error); - _pCrit->Leave(); - return false; - } - _numActiveSockets++; - _pCrit->Leave(); - return true; -} -bool UdpSocket2ManagerWindows::RemoveSocketPrv(UdpSocket2Windows* s) -{ - if(!_init) - { - return false; - } - _pCrit->Enter(); - _numActiveSockets--; - if(_numActiveSockets == 0) - { - _event->Set(); - } - _pCrit->Leave(); - return true; -} - -PerIoContext* UdpSocket2ManagerWindows::PopIoContext() -{ - if(!_init) - { - return NULL; - } - - PerIoContext* pIoC = NULL; - if(!_stoped) - { - pIoC = _ioContextPool.PopIoContext(); - }else - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2ManagerWindows(%d)::PopIoContext() Manager Not started", - _managerNumber); - } - return pIoC; -} - -WebRtc_Word32 UdpSocket2ManagerWindows::PushIoContext(PerIoContext* pIoContext) -{ - return _ioContextPool.PushIoContext(pIoContext); -} - -IoContextPool::IoContextPool() - : _pListHead(NULL), - _init(false), - _size(0), - _inUse(0) -{ -} - -IoContextPool::~IoContextPool() -{ - Free(); - assert(_size.Value() == 0); - AlignedFree(_pListHead); -} - -WebRtc_Word32 IoContextPool::Init(WebRtc_UWord32 /*increaseSize*/) -{ - if(_init) - { - return 0; - } - - _pListHead = (PSLIST_HEADER)AlignedMalloc(sizeof(SLIST_HEADER), - MEMORY_ALLOCATION_ALIGNMENT); - if(_pListHead == NULL) - { - return -1; - } - InitializeSListHead(_pListHead); - _init = true; - return 0; -} - -PerIoContext* IoContextPool::PopIoContext() -{ - if(!_init) - { - return NULL; - } - - PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); - if(pListEntry == NULL) - { - IoContextPoolItem* item = (IoContextPoolItem*) - AlignedMalloc( - sizeof(IoContextPoolItem), - MEMORY_ALLOCATION_ALIGNMENT); - if(item == NULL) - { - return NULL; - } - memset(&item->payload.ioContext,0,sizeof(PerIoContext)); - item->payload.base = item; - pListEntry = &(item->itemEntry); - ++_size; - } - ++_inUse; - return &((IoContextPoolItem*)pListEntry)->payload.ioContext; -} - -WebRtc_Word32 IoContextPool::PushIoContext(PerIoContext* pIoContext) -{ - // TODO (hellner): Overlapped IO should be completed at this point. Perhaps - // add an assert? - const bool overlappedIOCompleted = HasOverlappedIoCompleted( - (LPOVERLAPPED)pIoContext); - - IoContextPoolItem* item = ((IoContextPoolItemPayload*)pIoContext)->base; - - const WebRtc_Word32 usedItems = --_inUse; - const WebRtc_Word32 totalItems = _size.Value(); - const WebRtc_Word32 freeItems = totalItems - usedItems; - if(freeItems < 0) - { - assert(false); - AlignedFree(item); - return -1; - } - if((freeItems >= totalItems>>1) && - overlappedIOCompleted) - { - AlignedFree(item); - --_size; - return 0; - } - InterlockedPushEntrySList(_pListHead, &(item->itemEntry)); - return 0; -} - -WebRtc_Word32 IoContextPool::Free() -{ - if(!_init) - { - return 0; - } - - WebRtc_Word32 itemsFreed = 0; - PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); - while(pListEntry != NULL) - { - IoContextPoolItem* item = ((IoContextPoolItem*)pListEntry); - AlignedFree(item); - --_size; - itemsFreed++; - pListEntry = InterlockedPopEntrySList(_pListHead); - } - return itemsFreed; -} - -WebRtc_Word32 UdpSocket2WorkerWindows::_numOfWorkers = 0; - -UdpSocket2WorkerWindows::UdpSocket2WorkerWindows(HANDLE ioCompletionHandle) - : _ioCompletionHandle(ioCompletionHandle), - _pThread(NULL), - _init(false) -{ - _workerNumber = _numOfWorkers++; - WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, - "UdpSocket2WorkerWindows created"); -} - -UdpSocket2WorkerWindows::~UdpSocket2WorkerWindows() -{ - if(_pThread) - { - delete _pThread; - } - WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, - "UdpSocket2WorkerWindows deleted"); -} - -bool UdpSocket2WorkerWindows::Start() -{ - unsigned int id = 0; - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, - "Start UdpSocket2WorkerWindows"); - return _pThread->Start(id); -} - -bool UdpSocket2WorkerWindows::Stop() -{ - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, - "Stop UdpSocket2WorkerWindows"); - return _pThread->Stop(); -} - -void UdpSocket2WorkerWindows::SetNotAlive() -{ - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, - "SetNotAlive UdpSocket2WorkerWindows"); - _pThread->SetNotAlive(); -} - -WebRtc_Word32 UdpSocket2WorkerWindows::Init() -{ - if(!_init) - { - const WebRtc_Word8* threadName = "UdpSocket2ManagerWindows_thread"; - _pThread = ThreadWrapper::CreateThread(Run, this, kRealtimePriority, - threadName); - if(_pThread == NULL) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - -1, - "UdpSocket2WorkerWindows(%d)::Init(), error creating thread!", - _workerNumber); - return -1; - } - _init = true; - } - return 0; -} - -bool UdpSocket2WorkerWindows::Run(ThreadObj obj) -{ - UdpSocket2WorkerWindows* pWorker = - static_cast(obj); - return pWorker->Process(); -} - -// Process should always return true. Stopping the worker threads is done in -// the UdpSocket2ManagerWindows::StopWorkerThreads() function. -bool UdpSocket2WorkerWindows::Process() -{ - WebRtc_Word32 success = 0; - DWORD ioSize = 0; - UdpSocket2Windows* pSocket = NULL; - PerIoContext* pIOContext = 0; - OVERLAPPED* pOverlapped = 0; - success = GetQueuedCompletionStatus(_ioCompletionHandle, - &ioSize, - (ULONG_PTR*)&pSocket, &pOverlapped, 200); - - WebRtc_UWord32 error = 0; - if(!success) - { - error = GetLastError(); - if(error == WAIT_TIMEOUT) - { - return true; - } - // This may happen if e.g. PostQueuedCompletionStatus() has been called. - // The IO context still needs to be reclaimed or re-used which is done - // in UdpSocket2Windows::IOCompleted(..). - } - if(pSocket == NULL) - { - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - -1, - "UdpSocket2WorkerWindows(%d)::Process(), pSocket == 0, end thread", - _workerNumber); - return true; - } - pIOContext = (PerIoContext*)pOverlapped; - pSocket->IOCompleted(pIOContext,ioSize,error); - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket2_manager_windows.h b/modules/udp_transport/source/udp_socket2_manager_windows.h deleted file mode 100644 index 3e10aad2c..000000000 --- a/modules/udp_transport/source/udp_socket2_manager_windows.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_MANAGER_WINDOWS_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_MANAGER_WINDOWS_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -#include "atomic32_wrapper.h" -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "list_wrapper.h" -#include "thread_wrapper.h" -#include "udp_socket2_windows.h" -#include "udp_socket_manager_wrapper.h" - -#define MAX_IO_BUFF_SIZE 1600 - -namespace webrtc { -enum IO_OPERATION { - OP_READ, - OP_WRITE -}; - -class UdpSocket2Windows; - -// Struct used for all socket I/O operations. -struct PerIoContext { - WSAOVERLAPPED overlapped; - char buffer[MAX_IO_BUFF_SIZE]; - WSABUF wsabuf; - int nTotalBytes; - int nSentBytes; - int bytes; - IO_OPERATION ioOperation; - SocketAddress from; - int fromLen; - // Should be set to true if the I/O context was passed to the system by - // a thread not controlled by the socket implementation. - bool ioInitiatedByThreadWrapper; - // TODO (hellner): Not used. Delete it. - PerIoContext* pNextFree; -}; - -struct IoContextPoolItem; -struct IoContextPoolItemPayload -{ - PerIoContext ioContext; - IoContextPoolItem* base; -}; - -struct IoContextPoolItem -{ - // Atomic single linked list entry header. - SLIST_ENTRY itemEntry; - // Atomic single linked list payload - IoContextPoolItemPayload payload; -}; - -class IoContextPool -{ -public: - IoContextPool(); - virtual ~IoContextPool(); - virtual WebRtc_Word32 Init(WebRtc_UWord32 increaseSize = 128); - // Re-use an old unused IO context or create a new one. - virtual PerIoContext* PopIoContext(); - virtual WebRtc_Word32 PushIoContext(PerIoContext* pIoContext); - virtual inline WebRtc_Word32 GetSize(WebRtc_UWord32* inUse = 0) - {return _size.Value();} - virtual WebRtc_Word32 Free(); -private: - // Sample code for use of msfts single linked atomic list can be found here: - // http://msdn.microsoft.com/en-us/library/ms686962(VS.85).aspx - - // Atomic single linked list head. - PSLIST_HEADER _pListHead; - - bool _init; - Atomic32Wrapper _size; - Atomic32Wrapper _inUse; -}; - - -class UdpSocket2ManagerWindows : public UdpSocketManager -{ -public: - UdpSocket2ManagerWindows(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); - virtual ~UdpSocket2ManagerWindows(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool Start(); - virtual bool Stop(); - - virtual inline bool AddSocket(UdpSocketWrapper* s) - {if(s) return AddSocketPrv(reinterpret_cast(s)); - return false;} - virtual bool RemoveSocket(UdpSocketWrapper* s) - {if(s) return RemoveSocketPrv(reinterpret_cast(s)); - return false;} - - PerIoContext* PopIoContext(void); - WebRtc_Word32 PushIoContext(PerIoContext* pIoContext); - -private: - bool StopWorkerThreads(); - WebRtc_Word32 Init(); - bool AddSocketPrv(UdpSocket2Windows* s); - bool RemoveSocketPrv(UdpSocket2Windows* s); - - static WebRtc_UWord32 _numOfActiveManagers; - static bool _wsaInit; - - WebRtc_Word32 _id; - CriticalSectionWrapper* _pCrit; - WebRtc_Word32 _managerNumber; - volatile bool _stoped; - bool _init; - WebRtc_Word32 _numActiveSockets; - ListWrapper _workerThreadsList; - WebRtc_UWord32 _numOfWorkThreads; - EventWrapper* _event; - - HANDLE _ioCompletionHandle; - IoContextPool _ioContextPool; -}; - -class UdpSocket2WorkerWindows -{ -public: - UdpSocket2WorkerWindows(HANDLE ioCompletionHandle); - virtual ~UdpSocket2WorkerWindows(); - - virtual bool Start(); - virtual bool Stop(); - virtual WebRtc_Word32 Init(); - virtual void SetNotAlive(); -protected: - static bool Run(ThreadObj obj); - bool Process(); -private: - HANDLE _ioCompletionHandle; - ThreadWrapper*_pThread; - static WebRtc_Word32 _numOfWorkers; - WebRtc_Word32 _workerNumber; - volatile bool _stop; - bool _init; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_MANAGER_WINDOWS_H_ diff --git a/modules/udp_transport/source/udp_socket2_windows.cc b/modules/udp_transport/source/udp_socket2_windows.cc deleted file mode 100644 index c3c8d82b3..000000000 --- a/modules/udp_transport/source/udp_socket2_windows.cc +++ /dev/null @@ -1,1411 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket2_windows.h" - -#include -#include -#include - -#include "traffic_control_windows.h" -#include "udp_socket2_manager_windows.h" - -#pragma warning(disable : 4311) - -namespace webrtc { -typedef struct _QOS_DESTADDR -{ - QOS_OBJECT_HDR ObjectHdr; - const struct sockaddr* SocketAddress; - ULONG SocketAddressLength; -} QOS_DESTADDR, *LPQOS_DESTADDR; - -typedef const QOS_DESTADDR* LPCQOS_DESTADDR; - -// TODO (patrikw): seems to be defined in ws2ipdef.h as 3. How come it's -// redefined here (as a different value)? -#define IP_TOS 8 - -#define QOS_GENERAL_ID_BASE 2000 -#define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE) - -UdpSocket2Windows::UdpSocket2Windows(const WebRtc_Word32 id, - UdpSocketManager* mgr, bool ipV6Enable, - bool disableGQOS) - : _id(id), - _qos(true), - _iProtocol(0), - _outstandingCalls(0), - _outstandingCallComplete(0), - _terminate(false), - _addedToMgr(false), - _safeTodelete(false), - _outstandingCallsDisabled(0), - _clientHandle(NULL), - _flowHandle(NULL), - _filterHandle(NULL), - _flow(NULL), - _gtc(NULL), - _pcp(-2), - _receiveBuffers(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, - "UdpSocket2Windows::UdpSocket2Windows()"); - - _wantsIncoming = false; - _mgr = static_cast(mgr); - - _obj = NULL; - _incomingCb = NULL; - _socket = INVALID_SOCKET; - _pCrit = CriticalSectionWrapper::CreateCriticalSection(); - _ptrCbRWLock = RWLockWrapper::CreateRWLock(); - _ptrDestRWLock = RWLockWrapper::CreateRWLock(); - _ptrSocketRWLock = RWLockWrapper::CreateRWLock(); - _ptrDeleteCrit = CriticalSectionWrapper::CreateCriticalSection(); - _ptrDeleteCond = ConditionVariableWrapper::CreateConditionVariable(); - - // Check if QoS is supported. - BOOL bProtocolFound = FALSE; - WSAPROTOCOL_INFO *lpProtocolBuf = NULL; - WSAPROTOCOL_INFO pProtocolInfo; - - if(!disableGQOS) - { - DWORD dwBufLen = 0; - // Set dwBufLen to the size needed to retreive all the requested - // information from WSAEnumProtocols. - WebRtc_Word32 nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); - lpProtocolBuf = (WSAPROTOCOL_INFO*)malloc(dwBufLen); - nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); - - if (ipV6Enable) - { - _iProtocol=AF_INET6; - } else { - _iProtocol=AF_INET; - } - - for (WebRtc_Word32 i=0; iTcDeleteFilter(_filterHandle); - } - if(_flowHandle) - { - _gtc->TcDeleteFlow(_flowHandle); - } - TrafficControlWindows::Release( _gtc); - } -} - -WebRtc_Word32 UdpSocket2Windows::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - if (_gtc) - { - _gtc->ChangeUniqueId(id); - } - return 0; -} - -bool UdpSocket2Windows::ValidHandle() -{ - return GetFd() != INVALID_SOCKET; -} - -bool UdpSocket2Windows::SetCallback(CallbackObj obj, IncomingSocketCallback cb) -{ - _ptrCbRWLock->AcquireLockExclusive(); - _obj = obj; - _incomingCb = cb; - _ptrCbRWLock->ReleaseLockExclusive(); - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SetCallback ",(WebRtc_Word32)this); - if(_addedToMgr) - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SetCallback alreadey added", - (WebRtc_Word32) this); - return false; - - } - if (_mgr->AddSocket(this)) - { - WEBRTC_TRACE( - kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SetCallback socket added to manager", - (WebRtc_Word32)this); - _addedToMgr = true; - return true; - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SetCallback error adding me to mgr", - (WebRtc_Word32) this); - return false; -} - -bool UdpSocket2Windows::SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, - WebRtc_Word32 optlen) -{ - bool returnValue = true; - if(!AquireSocket()) - { - return false; - } - if(0 != setsockopt(_socket, level, optname, optval, optlen )) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::SetSockopt(), WSAerror:%d", - WSAGetLastError()); - returnValue = false; - } - ReleaseSocket(); - return returnValue; -} - -bool UdpSocket2Windows::StartReceiving(WebRtc_UWord32 receiveBuffers) -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows(%d)::StartReceiving(%d)", - (WebRtc_Word32)this, receiveBuffers); - - _wantsIncoming = true; - - WebRtc_Word32 numberOfReceiveBuffersToCreate = - receiveBuffers - _receiveBuffers.Value(); - numberOfReceiveBuffersToCreate = (numberOfReceiveBuffersToCreate < 0) ? - 0 : numberOfReceiveBuffersToCreate; - - WebRtc_Word32 error = 0; - for(WebRtc_Word32 i = 0; - i < numberOfReceiveBuffersToCreate; - i++) - { - if(PostRecv()) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::StartReceiving() i=%d", i); - error = -1; - break; - } - ++_receiveBuffers; - } - if(error == -1) - { - return false; - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Socket receiving using:%d number of buffers", - _receiveBuffers.Value()); - return true; -} - -bool UdpSocket2Windows::StopReceiving() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocket2Windows::StopReceiving()"); - _wantsIncoming = false; - return true; -} - -bool UdpSocket2Windows::Bind(const SocketAddress& name) -{ - const struct sockaddr* addr = - reinterpret_cast(&name); - bool returnValue = true; - if(!AquireSocket()) - { - return false; - } - if (0 != bind(_socket, addr, sizeof(SocketAddress))) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::Bind() WSAerror: %d", - WSAGetLastError()); - returnValue = false; - } - ReleaseSocket(); - return true; -} - -WebRtc_Word32 UdpSocket2Windows::SendTo(const WebRtc_Word8* buf, - WebRtc_Word32 len, - const SocketAddress& to) -{ - WebRtc_Word32 retVal = 0; - WebRtc_Word32 error = 0; - if(len < 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SendTo(), len= %d < 0", - (WebRtc_Word32)this, len); - return -1; - } - - PerIoContext* pIoContext = _mgr->PopIoContext(); - if(pIoContext == 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows(%d)::SendTo(), pIoContext==0", - (WebRtc_Word32) this); - return -1; - } - // sizeof(pIoContext->buffer) is smaller than the highest number that - // can be represented by a WebRtc_Word32. - if(len >= (WebRtc_Word32) sizeof(pIoContext->buffer)) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows(%d)::SendTo(), len= %d > buffer_size = %d", - (WebRtc_Word32) this, - len,sizeof(pIoContext->buffer)); - len = sizeof(pIoContext->buffer); - } - - memcpy(pIoContext->buffer,buf,len); - pIoContext->wsabuf.buf = pIoContext->buffer; - pIoContext->wsabuf.len = len; - pIoContext->fromLen=sizeof(SocketAddress); - pIoContext->ioOperation = OP_WRITE; - pIoContext->nTotalBytes = len; - pIoContext->nSentBytes=0; - - DWORD numOfbytesSent = 0; - const struct sockaddr* addr = reinterpret_cast(&to); - - if(!AquireSocket()) - { - _mgr->PushIoContext(pIoContext); - return -1; - } - // Assume that the WSASendTo call will be successfull to make sure that - // _outstandingCalls is positive. Roll back if WSASendTo failed. - if(!NewOutstandingCall()) - { - _mgr->PushIoContext(pIoContext); - ReleaseSocket(); - return -1; - } - retVal = WSASendTo(_socket, &pIoContext->wsabuf, 1, &numOfbytesSent, - 0, addr, sizeof(SocketAddress), - &(pIoContext->overlapped), 0); - ReleaseSocket(); - - if( retVal == SOCKET_ERROR ) - { - error = WSAGetLastError(); - if(error != ERROR_IO_PENDING) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::SendTo() WSAerror: %d",error); - } - } - if(retVal == 0 || (retVal == SOCKET_ERROR && error == ERROR_IO_PENDING)) - { - return len; - } - if((error = _mgr->PushIoContext(pIoContext))) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows(%d)::SendTo(), error:%d pushing ioContext", - (WebRtc_Word32)this, error); - } - - // Roll back. - OutstandingCallCompleted(); - return -1; -} - -void UdpSocket2Windows::IOCompleted(PerIoContext* pIOContext, - WebRtc_UWord32 ioSize, WebRtc_UWord32 error) -{ - if(pIOContext == NULL || error == ERROR_OPERATION_ABORTED) - { - if ((pIOContext != NULL) && - !pIOContext->ioInitiatedByThreadWrapper && - (error == ERROR_OPERATION_ABORTED) && - (pIOContext->ioOperation == OP_READ) && - _outstandingCallsDisabled.Value() == 1) - { - // !pIOContext->initiatedIOByThreadWrapper indicate that the I/O - // was not initiaded by a ThreadWrapper thread. - // This may happen if the thread that initiated receiving (e.g. - // by calling StartListen())) is deleted before any packets have - // been received. - // In this case there is no packet in the PerIoContext. Re-use it - // to post a new PostRecv(..). - // Note 1: the PerIoContext will henceforth be posted by a thread - // that is controlled by the socket implementation. - // Note 2: This is more likely to happen to RTCP packets as - // they are less frequent than RTP packets. - // Note 3: _outstandingCallsDisabled being false (= 1) indicates - // that the socket isn't being shut down. - // Note 4: This should only happen buffers set to recevie packets - // (OP_READ). - if (_outstandingCallsDisabled.Value() != 1) - { - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - _id, - "UdpSocket2Windows::IOCompleted(pIOContext=%p,\ - ioSize=%.lu, error=%.lu) Received operation aborted but continuing since\ - pIOContext->ioInitiatedByThreadWrapper == false", - pIOContext, - ioSize, - error); - WebRtc_Word32 ioOp = pIOContext ? - (WebRtc_Word32)pIOContext->ioOperation : -1; - WebRtc_Word32 ioInit = pIOContext ? - (WebRtc_Word32)pIOContext->ioInitiatedByThreadWrapper : -1; - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - _id, - "pIOContext->ioOperation=%d,\ - pIOContext->ioInitiatedByThreadWrapper=%d, _outstandingCallsDisabled=%d,\ - _incomingCb=%p, this=%p", - ioOp, - ioInit, - (WebRtc_Word32)_outstandingCallsDisabled.Value(), - _incomingCb, - this); - } - } else { - if(pIOContext == NULL) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows::IOCompleted(%d,%d,%d), %d", - (WebRtc_Word32)pIOContext, - ioSize, - error, - pIOContext ? (WebRtc_Word32)pIOContext->ioOperation : -1); - } else { - WEBRTC_TRACE( - kTraceDebug, - kTraceTransport, - _id, - "UdpSocket2Windows::IOCompleted() Operation aborted"); - } - if(pIOContext) - { - WebRtc_Word32 remainingReceiveBuffers = --_receiveBuffers; - if(remainingReceiveBuffers < 0) - { - assert(false); - } - WebRtc_Word32 err = 0; - if((err = _mgr->PushIoContext(pIOContext))) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows::IOCompleted(), err = %d, when\ - pushing ioContext after error", - err); - } - } - OutstandingCallCompleted(); - return; - } - } // if (pIOContext == NULL || error == ERROR_OPERATION_ABORTED) - - if(pIOContext->ioOperation == OP_WRITE) - { - _mgr->PushIoContext(pIOContext); - } - else if(pIOContext->ioOperation == OP_READ) - { - if(!error && ioSize != 0) - { - _ptrCbRWLock->AcquireLockShared(); - if(_wantsIncoming && _incomingCb) - { - _incomingCb(_obj,pIOContext->wsabuf.buf, ioSize, - &pIOContext->from); - } - _ptrCbRWLock->ReleaseLockShared(); - } - WebRtc_Word32 err = PostRecv(pIOContext); - if(err == 0) - { - // The PerIoContext was posted by a thread controlled by the socket - // implementation. - pIOContext->ioInitiatedByThreadWrapper = true; - } - OutstandingCallCompleted(); - return; - } else { - // Unknown operation. Should not happen. Return pIOContext to avoid - // memory leak. - assert(false); - _mgr->PushIoContext(pIOContext); - } - OutstandingCallCompleted(); - // Don't touch any members after OutstandingCallCompleted() since the socket - // may be deleted at this point. -} - -WebRtc_Word32 UdpSocket2Windows::PostRecv() -{ - PerIoContext* pIoContext=_mgr->PopIoContext(); - if(pIoContext == 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows(%d)::PostRecv(), pIoContext == 0", - (WebRtc_Word32)this); - return -1; - } - // This function may have been called by thread not controlled by the socket - // implementation. - pIoContext->ioInitiatedByThreadWrapper = false; - return PostRecv(pIoContext); -} - -WebRtc_Word32 UdpSocket2Windows::PostRecv(PerIoContext* pIoContext) -{ - if(pIoContext==0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows(%d)::PostRecv(?), pIoContext==0", - (WebRtc_Word32)this); - return -1; - } - - DWORD numOfRecivedBytes = 0; - DWORD flags = 0; - pIoContext->wsabuf.buf = pIoContext->buffer; - pIoContext->wsabuf.len = sizeof(pIoContext->buffer); - pIoContext->fromLen = sizeof(SocketAddress); - pIoContext->ioOperation = OP_READ; - WebRtc_Word32 rxError = 0; - WebRtc_Word32 nRet = 0; - WebRtc_Word32 postingSucessfull = false; - - if(!AquireSocket()) - { - _mgr->PushIoContext(pIoContext); - return -1; - } - - // Assume that the WSARecvFrom() call will be successfull to make sure that - // _outstandingCalls is positive. Roll back if WSARecvFrom() failed. - if(!NewOutstandingCall()) - { - _mgr->PushIoContext(pIoContext); - ReleaseSocket(); - return -1; - } - for(WebRtc_Word32 tries = 0; tries < 10; tries++) - { - nRet = WSARecvFrom( - _socket, - &(pIoContext->wsabuf), - 1, - &numOfRecivedBytes, - &flags, - reinterpret_cast(&(pIoContext->from)), - &(pIoContext->fromLen), - &(pIoContext->overlapped), - 0); - - if( nRet == SOCKET_ERROR) - { - rxError = WSAGetLastError(); - if(rxError != ERROR_IO_PENDING) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows(%d)::PostRecv(?), WSAerror:%d when\ - posting new recieve,trie:%d", - (WebRtc_Word32)this, - rxError, - tries); - // Tell the OS that this is a good place to context switch if - // it wants to. - Sleep(0); - } - } - if((rxError == ERROR_IO_PENDING) || (nRet == 0)) - { - postingSucessfull = true; - break; - } - } - ReleaseSocket(); - - if(postingSucessfull) - { - return 0; - } - WebRtc_Word32 remainingReceiveBuffers = --_receiveBuffers; - if(remainingReceiveBuffers < 0) - { - assert(false); - } - WebRtc_Word32 error = 0; - if((error = _mgr->PushIoContext(pIoContext))) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocket2Windows(%d)::PostRecv(?), error:%d when PushIoContext", - (WebRtc_Word32)this, - error); - } - // Roll back. - OutstandingCallCompleted(); - return -1; -} - -void UdpSocket2Windows::CloseBlocking() -{ - LINGER lingerStruct; - - lingerStruct.l_onoff = 1; - lingerStruct.l_linger = 0; - if(AquireSocket()) - { - setsockopt(_socket, SOL_SOCKET, SO_LINGER, - (WebRtc_Word8 *)&lingerStruct, sizeof(lingerStruct)); - ReleaseSocket(); - } - - _wantsIncoming = false; - // Reclaims the socket and prevents it from being used again. - InvalidateSocket(); - DisableNewOutstandingCalls(); - WaitForOutstandingCalls(); - delete this; -} - -bool UdpSocket2Windows::SetQos(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, - const SocketAddress &stRemName, - WebRtc_Word32 overrideDSCP) -{ - if(_qos == false) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::SetQos(), socket not capable of QOS"); - return false; - } - if(overrideDSCP != 0) - { - FLOWSPEC f; - WebRtc_Word32 err = CreateFlowSpec(serviceType, tokenRate, bucketSize, - peekBandwith, minPolicedSize, - maxSduSize, &f); - if(err == -1) - { - return false; - } - - SocketAddress socketName; - struct sockaddr_in* name = - reinterpret_cast(&socketName); - int nameLength = sizeof(SocketAddress); - if(AquireSocket()) - { - getsockname(_socket, (struct sockaddr*)name, &nameLength); - ReleaseSocket(); - } - - if(serviceType == 0) - { - // Disable TOS byte setting. - return SetTrafficControl(0, -1, name, &f, &f) == 0; - } - return SetTrafficControl(overrideDSCP, -1, name, &f, &f) == 0; - } - - QOS Qos; - WebRtc_Word32 result ; - DWORD BytesRet; - QOS_DESTADDR QosDestaddr; - - memset (&Qos, QOS_NOT_SPECIFIED, sizeof(QOS)); - - Qos.SendingFlowspec.ServiceType = serviceType; - Qos.SendingFlowspec.TokenRate = tokenRate; - Qos.SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - - // Only ServiceType is needed for receiving. - Qos.ReceivingFlowspec.ServiceType = serviceType; - Qos.ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - - Qos.ProviderSpecific.len = 0; - - Qos.ProviderSpecific.buf = NULL; - - ZeroMemory((WebRtc_Word8 *)&QosDestaddr, sizeof(QosDestaddr)); - - OSVERSIONINFOEX osvie; - osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - GetVersionEx((LPOSVERSIONINFO)&osvie); - -// Operating system Version number dwMajorVersion dwMinorVersion -// Windows 7 6.1 6 1 -// Windows Server 2008 R2 6.1 6 1 -// Windows Server 2008 6.0 6 0 -// Windows Vista 6.0 6 0 -// Windows Server 2003 R2 5.2 5 2 -// Windows Server 2003 5.2 5 2 -// Windows XP 5.1 5 1 -// Windows 2000 5.0 5 0 - - // SERVICE_NO_QOS_SIGNALING and QOS_DESTADDR should not be used if version - // is 6.0 or greater. - if(osvie.dwMajorVersion >= 6) - { - Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.ServiceType = serviceType; - - } else { - Qos.SendingFlowspec.MinimumPolicedSize = - QOS_NOT_SPECIFIED | SERVICE_NO_QOS_SIGNALING; - Qos.ReceivingFlowspec.ServiceType = - serviceType | SERVICE_NO_QOS_SIGNALING; - - QosDestaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR; - QosDestaddr.ObjectHdr.ObjectLength = sizeof(QosDestaddr); - QosDestaddr.SocketAddress = (SOCKADDR *)&stRemName; - if (AF_INET6 == _iProtocol) - { - QosDestaddr.SocketAddressLength = sizeof(SocketAddressInVersion6); - } else { - QosDestaddr.SocketAddressLength = sizeof(SocketAddressIn); - } - - Qos.ProviderSpecific.len = QosDestaddr.ObjectHdr.ObjectLength; - Qos.ProviderSpecific.buf = (WebRtc_Word8*)&QosDestaddr; - } - - if(AquireSocket()) - { - // To set QoS with SIO_SET_QOS the socket must be locally bound first - // or the call will fail with error code 10022. - result = WSAIoctl(GetFd(), SIO_SET_QOS, &Qos, sizeof(QOS), NULL, 0, - &BytesRet, NULL,NULL); - ReleaseSocket(); - } - if (result == SOCKET_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::SetQos() WSAerror : %d", - WSAGetLastError()); - return false; - } - return true; -} - -WebRtc_Word32 UdpSocket2Windows::SetTOS(WebRtc_Word32 serviceType) -{ - SocketAddress socketName; - - struct sockaddr_in* name = - reinterpret_cast(&socketName); - int nameLength = sizeof(SocketAddress); - if(AquireSocket()) - { - getsockname(_socket, (struct sockaddr*)name, &nameLength); - ReleaseSocket(); - } - - WebRtc_Word32 res = SetTrafficControl(serviceType, -1, name); - if (res == -1) - { - OSVERSIONINFO OsVersion; - OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx (&OsVersion); - - if ((OsVersion.dwMajorVersion == 4)) // NT 4.0 - { - if(SetSockopt(IPPROTO_IP,IP_TOS , - (WebRtc_Word8*)&serviceType, 4) != 0) - { - return -1; - } - } - } - return res; -} - -WebRtc_Word32 UdpSocket2Windows::SetPCP(WebRtc_Word32 pcp) -{ - SocketAddress socketName; - struct sockaddr_in* name = - reinterpret_cast(&socketName); - int nameLength = sizeof(SocketAddress); - if(AquireSocket()) - { - getsockname(_socket, (struct sockaddr*)name, &nameLength); - ReleaseSocket(); - } - return SetTrafficControl(-1, pcp, name); -} - -WebRtc_Word32 UdpSocket2Windows::SetTrafficControl( - WebRtc_Word32 dscp, - WebRtc_Word32 pcp, - const struct sockaddr_in* name, - FLOWSPEC* send, FLOWSPEC* recv) -{ - if (pcp == _pcp) - { - // No change. - pcp = -1; - } - if ((-1 == pcp) && (-1 == dscp)) - { - return 0; - } - if (!_gtc) - { - _gtc = TrafficControlWindows::GetInstance(_id); - } - if (!_gtc) - { - return -1; - } - if(_filterHandle) - { - _gtc->TcDeleteFilter(_filterHandle); - _filterHandle = NULL; - } - if(_flowHandle) - { - _gtc->TcDeleteFlow(_flowHandle); - _flowHandle = NULL; - } - if(_clientHandle) - { - _gtc->TcDeregisterClient(_clientHandle); - _clientHandle = NULL; - } - if ((0 == dscp) && (-2 == _pcp) && (-1 == pcp)) - { - // TODO (pwestin): why is this not done before deleting old filter and - // flow? This scenario should probably be documented in - // the function declaration. - return 0; - } - - TCI_CLIENT_FUNC_LIST QoSFunctions; - QoSFunctions.ClAddFlowCompleteHandler = NULL; - QoSFunctions.ClDeleteFlowCompleteHandler = NULL; - QoSFunctions.ClModifyFlowCompleteHandler = NULL; - QoSFunctions.ClNotifyHandler = (TCI_NOTIFY_HANDLER)MyClNotifyHandler; - // Register the client with Traffic control interface. - HANDLE ClientHandle; - ULONG result = _gtc->TcRegisterClient(CURRENT_TCI_VERSION, NULL, - &QoSFunctions,&ClientHandle); - if(result != NO_ERROR) - { - // This is likely caused by the application not being run as - // administrator. - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "TcRegisterClient returned %d", result); - return result; - } - - // Find traffic control-enabled network interfaces that matches this - // socket's IP address. - ULONG BufferSize = 0; - result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL); - - if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER) - { - _gtc->TcDeregisterClient(ClientHandle); - return result; - } - - if(result != ERROR_INSUFFICIENT_BUFFER) - { - // Empty buffer contains all control-enabled network interfaces. I.e. - // QoS is not enabled. - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS faild since QOS is not installed on the interface"); - - _gtc->TcDeregisterClient(ClientHandle); - return -1; - } - - PTC_IFC_DESCRIPTOR pInterfaceBuffer = - (PTC_IFC_DESCRIPTOR)malloc(BufferSize); - if(pInterfaceBuffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Out ot memory failure"); - _gtc->TcDeregisterClient(ClientHandle); - return ERROR_NOT_ENOUGH_MEMORY; - } - - result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, - pInterfaceBuffer); - - if(result != NO_ERROR) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "Critical: error enumerating interfaces when passing in correct\ - buffer size: %d", result); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - PTC_IFC_DESCRIPTOR oneinterface; - HANDLE ifcHandle, iFilterHandle, iflowHandle; - bool addrFound = false; - ULONG filterSourceAddress = ULONG_MAX; - - // Find the interface corresponding to the local address. - for(oneinterface = pInterfaceBuffer; - oneinterface != (PTC_IFC_DESCRIPTOR) - (((WebRtc_Word8*)pInterfaceBuffer) + BufferSize); - oneinterface = (PTC_IFC_DESCRIPTOR) - ((WebRtc_Word8 *)oneinterface + oneinterface->Length)) - { - - WebRtc_Word8 interfaceName[500]; - WideCharToMultiByte(CP_ACP, 0, oneinterface->pInterfaceName, -1, - interfaceName, sizeof(interfaceName), 0, 0 ); - - PNETWORK_ADDRESS_LIST addresses = - &(oneinterface->AddressListDesc.AddressList); - for(LONG i = 0; i < addresses->AddressCount ; i++) - { - // Only look at TCP/IP addresses. - if(addresses->Address[i].AddressType != NDIS_PROTOCOL_ID_TCP_IP) - { - continue; - } - - NETWORK_ADDRESS_IP* pIpAddr = - (NETWORK_ADDRESS_IP*)&(addresses->Address[i].Address); - struct in_addr in; - in.S_un.S_addr = pIpAddr->in_addr; - if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr) - { - filterSourceAddress = pIpAddr->in_addr; - addrFound = true; - } - } - if(!addrFound) - { - continue; - } else - { - break; - } - } - if(!addrFound) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS faild since address is not found"); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return -1; - } - result = _gtc->TcOpenInterfaceW(oneinterface->pInterfaceName, ClientHandle, - NULL, &ifcHandle); - if(result != NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Error opening interface: %d", result); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - // Create flow if one doesn't exist. - if (!_flow) - { - bool addPCP = ((pcp >= 0) || ((-1 == pcp) && (_pcp >= 0))); - int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) + - (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); - _flow = (PTC_GEN_FLOW)malloc(allocSize); - - _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; - _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.TokenRate = QOS_NOT_SPECIFIED; - - _flow->ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; - _flow->ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; - - QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; - dsClass->DSField = 0; - dsClass->ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS; - dsClass->ObjectHdr.ObjectLength = sizeof(QOS_DS_CLASS); - - if (addPCP) - { - QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); - trafficClass->TrafficClass = 0; - trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS; - trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS); - } - - _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) + - (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); - } else if (-1 != pcp) { - // Reallocate memory since pcp has changed. - PTC_GEN_FLOW oldFlow = _flow; - bool addPCP = (pcp >= 0); - int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) + - (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); - _flow = (PTC_GEN_FLOW)malloc(allocSize); - - // Copy old flow. - _flow->ReceivingFlowspec = oldFlow->ReceivingFlowspec; - _flow->SendingFlowspec = oldFlow->SendingFlowspec; - // The DS info is always the first object. - QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; - QOS_DS_CLASS* oldDsClass = (QOS_DS_CLASS*)oldFlow->TcObjects; - dsClass->DSField = oldDsClass->DSField; - dsClass->ObjectHdr.ObjectType = oldDsClass->ObjectHdr.ObjectType; - dsClass->ObjectHdr.ObjectLength = oldDsClass->ObjectHdr.ObjectLength; - - if (addPCP) - { - QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); - trafficClass->TrafficClass = 0; - trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS; - trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS); - } - - _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) + - (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); - free(oldFlow); - } - - // Setup send and receive flow and DS object. - if (dscp >= 0) - { - if (!send || (0 == dscp)) - { - _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - _flow->SendingFlowspec.PeakBandwidth = - (0 == dscp ? QOS_NOT_SPECIFIED : POSITIVE_INFINITY_RATE); - _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; - _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - // 128000 * 10 is 10mbit/s. - _flow->SendingFlowspec.TokenRate = - (0 == dscp ? QOS_NOT_SPECIFIED : 128000 * 10); - } - else - { - _flow->SendingFlowspec.DelayVariation = send->DelayVariation; - _flow->SendingFlowspec.Latency = send->Latency; - _flow->SendingFlowspec.MaxSduSize = send->MaxSduSize; - _flow->SendingFlowspec.MinimumPolicedSize = - send->MinimumPolicedSize; - _flow->SendingFlowspec.PeakBandwidth = send->PeakBandwidth; - _flow->SendingFlowspec.PeakBandwidth = POSITIVE_INFINITY_RATE; - _flow->SendingFlowspec.ServiceType = send->ServiceType; - _flow->SendingFlowspec.TokenBucketSize = send->TokenBucketSize; - _flow->SendingFlowspec.TokenRate = send->TokenRate; - } - - if (!recv || (0 == dscp)) - { - _flow->ReceivingFlowspec.DelayVariation = - _flow->SendingFlowspec.DelayVariation; - _flow->ReceivingFlowspec.Latency = _flow->SendingFlowspec.Latency; - _flow->ReceivingFlowspec.MaxSduSize = - _flow->SendingFlowspec.MaxSduSize; - _flow->ReceivingFlowspec.MinimumPolicedSize = - _flow->SendingFlowspec.MinimumPolicedSize; - _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - _flow->ReceivingFlowspec.ServiceType = - 0 == dscp ? SERVICETYPE_BESTEFFORT : SERVICETYPE_CONTROLLEDLOAD; - _flow->ReceivingFlowspec.TokenBucketSize = - _flow->SendingFlowspec.TokenBucketSize; - _flow->ReceivingFlowspec.TokenRate = - _flow->SendingFlowspec.TokenRate; - } else { - _flow->ReceivingFlowspec.DelayVariation = recv->DelayVariation; - _flow->ReceivingFlowspec.Latency = recv->Latency; - _flow->ReceivingFlowspec.MaxSduSize = recv->MaxSduSize; - _flow->ReceivingFlowspec.MinimumPolicedSize = - recv->MinimumPolicedSize; - _flow->ReceivingFlowspec.PeakBandwidth = recv->PeakBandwidth; - _flow->ReceivingFlowspec.ServiceType = recv->ServiceType; - _flow->ReceivingFlowspec.TokenBucketSize = recv->TokenBucketSize; - _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; - } - - // Setup DS (for DSCP value). - // DS is always the first object. - QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; - dsClass->DSField = dscp; - } - - // Setup PCP (802.1p priority in 802.1Q/VLAN tagging) - if (pcp >= 0) - { - // DS is always first object. - QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; - QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); - trafficClass->TrafficClass = pcp; - } - - result = _gtc->TcAddFlow(ifcHandle, NULL, 0, _flow, &iflowHandle); - if(result != NO_ERROR) - { - _gtc->TcCloseInterface(ifcHandle); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return -1; - } - - IP_PATTERN filterPattern, mask; - - ZeroMemory((WebRtc_Word8*)&filterPattern, sizeof(IP_PATTERN)); - ZeroMemory((WebRtc_Word8*)&mask, sizeof(IP_PATTERN)); - - filterPattern.ProtocolId = IPPROTO_UDP; - // "name" fields already in network order. - filterPattern.S_un.S_un_ports.s_srcport = name->sin_port; - filterPattern.SrcAddr = filterSourceAddress; - - // Unsigned max of a type corresponds to a bitmask with all bits set to 1. - // I.e. the filter should allow all ProtocolIds, any source port and any - // IP address - mask.ProtocolId = UCHAR_MAX; - mask.S_un.S_un_ports.s_srcport = USHRT_MAX; - mask.SrcAddr = ULONG_MAX; - - TC_GEN_FILTER filter; - - filter.AddressType = NDIS_PROTOCOL_ID_TCP_IP; - filter.Mask = (LPVOID)&mask; - filter.Pattern = (LPVOID)&filterPattern; - filter.PatternSize = sizeof(IP_PATTERN); - - result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle); - if(result != NO_ERROR) - { - _gtc->TcDeleteFlow(iflowHandle); - _gtc->TcCloseInterface(ifcHandle); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - _flowHandle = iflowHandle; - _filterHandle = iFilterHandle; - _clientHandle = ClientHandle; - if (-1 != pcp) - { - _pcp = pcp; - } - - _gtc->TcCloseInterface(ifcHandle); - free(pInterfaceBuffer); - - return 0; -} - -WebRtc_Word32 UdpSocket2Windows::CreateFlowSpec(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, - FLOWSPEC* f) -{ - if (!f) - { - return -1; - } - - f->ServiceType = serviceType; - f->TokenRate = tokenRate; - f->TokenBucketSize = QOS_NOT_SPECIFIED; - f->PeakBandwidth = QOS_NOT_SPECIFIED; - f->DelayVariation = QOS_NOT_SPECIFIED; - f->Latency = QOS_NOT_SPECIFIED; - f->MaxSduSize = QOS_NOT_SPECIFIED; - f->MinimumPolicedSize = QOS_NOT_SPECIFIED; - return 0; -} - -bool UdpSocket2Windows::NewOutstandingCall() -{ - assert(_outstandingCallsDisabled.Value() == 0); - - const WebRtc_Word32 outstandingCalls = ++_outstandingCalls; - return true; -} - -void UdpSocket2Windows::OutstandingCallCompleted() -{ - _ptrDestRWLock->AcquireLockShared(); - ++_outstandingCallComplete; - if((--_outstandingCalls == 0) && (_outstandingCallsDisabled.Value() == 1)) - { - // When there are no outstanding calls and new outstandning calls are - // disabled it is time to terminate. - _terminate = true; - } - _ptrDestRWLock->ReleaseLockShared(); - - if((--_outstandingCallComplete == 0) && - (_terminate)) - { - // Only one thread will enter here. The thread with the last outstanding - // call. - CriticalSectionScoped cs(*_ptrDeleteCrit); - _safeTodelete = true; - _ptrDeleteCond->Wake(); - } -} - -void UdpSocket2Windows::DisableNewOutstandingCalls() -{ - _ptrDestRWLock->AcquireLockExclusive(); - if(_outstandingCallsDisabled.Value() == 1) - { - // Outstandning calls are already disabled. - _ptrDestRWLock->ReleaseLockExclusive(); - return; - } - _outstandingCallsDisabled = 1; - const bool noOutstandingCalls = (_outstandingCalls.Value() == 0); - _ptrDestRWLock->ReleaseLockExclusive(); - - RemoveSocketFromManager(); - - if(noOutstandingCalls) - { - CriticalSectionScoped cs(*_ptrDeleteCrit); - _safeTodelete = true; - _ptrDeleteCond->Wake(); - } -} - -void UdpSocket2Windows::WaitForOutstandingCalls() -{ - CriticalSectionScoped cs(*_ptrDeleteCrit); - while(!_safeTodelete) - { - _ptrDeleteCond->SleepCS(*_ptrDeleteCrit); - } -} - -void UdpSocket2Windows::RemoveSocketFromManager() -{ - // New outstanding calls should be disabled at this point. - assert(_outstandingCallsDisabled.Value() != 0); - - if(_addedToMgr) - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "calling UdpSocketManager::RemoveSocket()"); - if(_mgr->RemoveSocket(this)) - { - _addedToMgr=false; - } - } -} - -bool UdpSocket2Windows::AquireSocket() -{ - _ptrSocketRWLock->AcquireLockShared(); - const bool returnValue = _socket != INVALID_SOCKET; - if(!returnValue) - { - _ptrSocketRWLock->ReleaseLockShared(); - } - return returnValue; -} - -void UdpSocket2Windows::ReleaseSocket() -{ - _ptrSocketRWLock->ReleaseLockShared(); -} - -bool UdpSocket2Windows::InvalidateSocket() -{ - _ptrSocketRWLock->AcquireLockExclusive(); - if(_socket == INVALID_SOCKET) - { - _ptrSocketRWLock->ReleaseLockExclusive(); - return true; - } - // Give the socket back to the system. All socket calls will fail from now - // on. - if(closesocket(_socket) == SOCKET_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows(%d)::InvalidateSocket() WSAerror: %d", - (WebRtc_Word32)this, WSAGetLastError()); - } - _socket = INVALID_SOCKET; - _ptrSocketRWLock->ReleaseLockExclusive(); - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket2_windows.h b/modules/udp_transport/source/udp_socket2_windows.h deleted file mode 100644 index c0d68bb8e..000000000 --- a/modules/udp_transport/source/udp_socket2_windows.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_WINDOWS_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_WINDOWS_H_ - -// Disable deprication warning from traffic.h -#pragma warning(disable : 4995) - -// Don't change include order for these header files. -#include -#include -#include - -#include "atomic32_wrapper.h" -#include "condition_variable_wrapper.h" -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "list_wrapper.h" -#include "rw_lock_wrapper.h" -#include "trace.h" -#include "udp_socket_wrapper.h" -#include "udp_socket2_manager_windows.h" - -namespace webrtc { -class UdpSocket2ManagerWindows; -class TrafficControlWindows; -struct PerIoContext; - -class UdpSocket2Windows : public UdpSocketWrapper -{ -public: - UdpSocket2Windows(const WebRtc_Word32 id, UdpSocketManager* mgr, - bool ipV6Enable = false, bool disableGQOS = false); - virtual ~UdpSocket2Windows(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool ValidHandle(); - - virtual bool SetCallback(CallbackObj, IncomingSocketCallback); - - virtual bool Bind(const SocketAddress& name); - virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, WebRtc_Word32 optlen); - - virtual bool StartReceiving(const WebRtc_UWord32 receiveBuffers); - virtual inline bool StartReceiving() {return StartReceiving(8);} - virtual bool StopReceiving(); - - virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, - const SocketAddress& to); - - virtual void CloseBlocking(); - - virtual SOCKET GetFd() { return _socket;} - virtual bool SetQos(WebRtc_Word32 serviceType, WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, WebRtc_Word32 maxSduSize, - const SocketAddress &stRemName, - WebRtc_Word32 overrideDSCP = 0); - - virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType); - virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 pcp); - - virtual WebRtc_UWord32 ReceiveBuffers(){return _receiveBuffers.Value();} - -protected: - void IOCompleted(PerIoContext* pIOContext, WebRtc_UWord32 ioSize, - WebRtc_UWord32 error); - - WebRtc_Word32 PostRecv(); - // Use pIoContext to post a new WSARecvFrom(..). - WebRtc_Word32 PostRecv(PerIoContext* pIoContext); - -private: - friend class UdpSocket2WorkerWindows; - - // Set traffic control (TC) flow adding it the interface that matches this - // sockets address. - // A filter is created and added to the flow. - // The flow consists of: - // (1) QoS send and receive information (flow specifications). - // (2) A DS object (for specifying exact DSCP value). - // (3) Possibly a traffic object (for specifying exact 802.1p priority (PCP) - // value). - // - // dscp values: - // -1 don't change the current dscp value. - // 0 don't add any flow to TC, unless pcp is specified. - // 1-63 Add a flow to TC with the specified dscp value. - // pcp values: - // -2 Don't add pcp info to the flow, (3) will not be added. - // -1 Don't change the current value. - // 0-7 Add pcp info to the flow with the specified value, - // (3) will be added. - // - // If both dscp and pcp are -1 no flow will be created or added to TC. - // If dscp is 0 and pcp is 0-7 (1), (2) and (3) will be created. - // Note: input parameter values are assumed to be in valid range, checks - // must be done by caller. - WebRtc_Word32 SetTrafficControl(WebRtc_Word32 dscp, WebRtc_Word32 pcp, - const struct sockaddr_in* name, - FLOWSPEC* send = NULL, - FLOWSPEC* recv = NULL); - WebRtc_Word32 CreateFlowSpec(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, FLOWSPEC *f); - - WebRtc_Word32 _id; - RWLockWrapper* _ptrCbRWLock; - IncomingSocketCallback _incomingCb; - CallbackObj _obj; - bool _qos; - - SocketAddress _remoteAddr; - SOCKET _socket; - WebRtc_Word32 _iProtocol; - UdpSocket2ManagerWindows* _mgr; - - CriticalSectionWrapper* _pCrit; - Atomic32Wrapper _outstandingCalls; - Atomic32Wrapper _outstandingCallComplete; - volatile bool _terminate; - volatile bool _addedToMgr; - - CriticalSectionWrapper* _ptrDeleteCrit; - ConditionVariableWrapper* _ptrDeleteCond; - bool _safeTodelete; - - RWLockWrapper* _ptrDestRWLock; - Atomic32Wrapper _outstandingCallsDisabled; // 0 = false, 1 = true - bool NewOutstandingCall(); - void OutstandingCallCompleted(); - void DisableNewOutstandingCalls(); - void WaitForOutstandingCalls(); - - void RemoveSocketFromManager(); - - // RWLockWrapper is used as a reference counter for the socket. Write lock - // is used for creating and deleting socket. Read lock is used for - // accessing the socket. - RWLockWrapper* _ptrSocketRWLock; - bool AquireSocket(); - void ReleaseSocket(); - bool InvalidateSocket(); - - // Traffic control handles and structure pointers. - HANDLE _clientHandle; - HANDLE _flowHandle; - HANDLE _filterHandle; - PTC_GEN_FLOW _flow; - // TrafficControlWindows implements TOS and PCP. - TrafficControlWindows* _gtc; - // Holds the current pcp value. Can be -2 or 0 - 7. - int _pcp; - - Atomic32Wrapper _receiveBuffers; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET2_WINDOWS_H_ diff --git a/modules/udp_transport/source/udp_socket_linux.cc b/modules/udp_transport/source/udp_socket_linux.cc deleted file mode 100644 index a926d04b2..000000000 --- a/modules/udp_transport/source/udp_socket_linux.cc +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "trace.h" -#include "udp_socket_manager_wrapper.h" -#include "udp_socket_wrapper.h" - -namespace webrtc { -UdpSocketLinux::UdpSocketLinux(const WebRtc_Word32 id, UdpSocketManager* mgr, - bool ipV6Enable) -{ - WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, - "UdpSocketLinux::UdpSocketLinux()"); - - _wantsIncoming = false; - _error = 0; - _mgr = mgr; - - _id = id; - _obj = NULL; - _incomingCb = NULL; - _readyForDeletionCond = ConditionVariableWrapper::CreateConditionVariable(); - _closeBlockingCompletedCond = - ConditionVariableWrapper::CreateConditionVariable(); - _cs = CriticalSectionWrapper::CreateCriticalSection(); - _readyForDeletion = false; - _closeBlockingActive = false; - _closeBlockingCompleted= false; - if(ipV6Enable) - { - _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - } - else { - _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - } - - // Non-blocking mode - int iMode = 1; - ioctl(_socket, FIONBIO, &iMode); - // Enable close on fork for file descriptor so that it will not block until - // forked process terminates. - fcntl(_socket,F_SETFD,FD_CLOEXEC); -} - -UdpSocketLinux::~UdpSocketLinux() -{ - if(_socket != INVALID_SOCKET) - { - close(_socket); - _socket = INVALID_SOCKET; - } - if(_readyForDeletionCond) - { - delete _readyForDeletionCond; - } - - if(_closeBlockingCompletedCond) - { - delete _closeBlockingCompletedCond; - } - - if(_cs) - { - delete _cs; - } -} - -WebRtc_Word32 UdpSocketLinux::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -bool UdpSocketLinux::SetCallback(CallbackObj obj, IncomingSocketCallback cb) -{ - _obj = obj; - _incomingCb = cb; - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketLinux(%p)::SetCallback", this); - - if (_mgr->AddSocket(this)) - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketLinux(%p)::SetCallback socket added to manager", - this); - return true; // socket is now ready for action - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketLinux(%p)::SetCallback error adding me to mgr", - this); - return false; -} - -bool UdpSocketLinux::SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, WebRtc_Word32 optlen) -{ - if(0 == setsockopt(_socket, level, optname, optval, optlen )) - { - return true; - } - - _error = errno; - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketLinux::SetSockopt(), error:%d", _error); - return false; -} - -WebRtc_Word32 UdpSocketLinux::SetTOS(WebRtc_Word32 serviceType) -{ - if (SetSockopt(IPPROTO_IP, IP_TOS ,(WebRtc_Word8*)&serviceType ,4) != 0) - { - return -1; - } - return 0; -} - -bool UdpSocketLinux::Bind(const SocketAddress& name) -{ - int size = sizeof(sockaddr); - if (0 == bind(_socket, reinterpret_cast(&name),size)) - { - return true; - } - _error = errno; - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketLinux::Bind() error: %d",_error); - return false; -} - -WebRtc_Word32 UdpSocketLinux::SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, - const SocketAddress& to) -{ - int size = sizeof(sockaddr); - int retVal = sendto(_socket,buf, len, 0, - reinterpret_cast(&to), size); - if(retVal == SOCKET_ERROR) - { - _error = errno; - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocketLinux::SendTo() error: %d", _error); - } - - return retVal; -} - -bool UdpSocketLinux::ValidHandle() -{ - return _socket != INVALID_SOCKET; -} - -void UdpSocketLinux::HasIncoming() -{ - char buf[2048]; - int retval; - SocketAddress from; -#if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC) - sockaddr sockaddrfrom; - memset(&from, 0, sizeof(from)); - memset(&sockaddrfrom, 0, sizeof(sockaddrfrom)); - socklen_t fromlen = sizeof(sockaddrfrom); -#else - memset(&from, 0, sizeof(from)); - socklen_t fromlen = sizeof(from); -#endif - -#if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_MAC) - retval = recvfrom(_socket,buf, sizeof(buf), 0, - reinterpret_cast(&sockaddrfrom), &fromlen); - memcpy(&from, &sockaddrfrom, fromlen); - from._sockaddr_storage.sin_family = sockaddrfrom.sa_family; -#else - retval = recvfrom(_socket,buf, sizeof(buf), 0, - reinterpret_cast(&from), &fromlen); -#endif - - switch(retval) - { - case 0: - // The peer has performed an orderly shutdown. - break; - case SOCKET_ERROR: - break; - default: - if(_wantsIncoming && _incomingCb) - { - _incomingCb(_obj,buf, retval, &from); - } - break; - } -} - -void UdpSocketLinux::CloseBlocking() -{ - _cs->Enter(); - _closeBlockingActive = true; - if(!CleanUp()) - { - _closeBlockingActive = false; - _cs->Leave(); - return; - } - - while(!_readyForDeletion) - { - _readyForDeletionCond->SleepCS(*_cs); - } - _closeBlockingCompleted = true; - _closeBlockingCompletedCond->Wake(); - _cs->Leave(); -} - -void UdpSocketLinux::ReadyForDeletion() -{ - _cs->Enter(); - if(!_closeBlockingActive) - { - _cs->Leave(); - return; - } - close(_socket); - _socket = INVALID_SOCKET; - _readyForDeletion = true; - _readyForDeletionCond->Wake(); - while(!_closeBlockingCompleted) - { - _closeBlockingCompletedCond->SleepCS(*_cs); - } - _cs->Leave(); -} - -bool UdpSocketLinux::CleanUp() -{ - _wantsIncoming = false; - - if (_socket == INVALID_SOCKET) - { - return false; - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "calling UdpSocketManager::RemoveSocket()..."); - _mgr->RemoveSocket(this); - // After this, the socket should may be or will be as deleted. Return - // immediately. - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_linux.h b/modules/udp_transport/source/udp_socket_linux.h deleted file mode 100644 index eeb72d7b7..000000000 --- a/modules/udp_transport/source/udp_socket_linux.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_LINUX_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_LINUX_H_ - -#include -#include -#include -#include - -#include "condition_variable_wrapper.h" -#include "critical_section_wrapper.h" -#include "udp_socket_wrapper.h" - -#define SOCKET_ERROR -1 - -namespace webrtc { -class UdpSocketLinux : public UdpSocketWrapper -{ -public: - UdpSocketLinux(const WebRtc_Word32 id, UdpSocketManager* mgr, - bool ipV6Enable = false); - - virtual ~UdpSocketLinux(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb); - - virtual bool Bind(const SocketAddress& name); - - virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, WebRtc_Word32 optlen); - - virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType); - - virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, - const SocketAddress& to); - - // Deletes socket in addition to closing it. - // TODO (hellner): make destructor protected. - virtual void CloseBlocking(); - - virtual SOCKET GetFd() {return _socket;} - virtual WebRtc_Word32 GetError() {return _error;} - - virtual bool ValidHandle(); - - virtual bool SetQos(WebRtc_Word32 /*serviceType*/, - WebRtc_Word32 /*tokenRate*/, - WebRtc_Word32 /*bucketSize*/, - WebRtc_Word32 /*peekBandwith*/, - WebRtc_Word32 /*minPolicedSize*/, - WebRtc_Word32 /*maxSduSize*/, - const SocketAddress& /*stRemName*/, - WebRtc_Word32 /*overrideDSCP*/) {return false;} - - bool CleanUp(); - void HasIncoming(); - bool WantsIncoming() {return _wantsIncoming;} - void ReadyForDeletion(); -private: - friend class UdpSocketManagerLinux; - - WebRtc_Word32 _id; - IncomingSocketCallback _incomingCb; - CallbackObj _obj; - WebRtc_Word32 _error; - - SOCKET _socket; - UdpSocketManager* _mgr; - ConditionVariableWrapper* _closeBlockingCompletedCond; - ConditionVariableWrapper* _readyForDeletionCond; - - bool _closeBlockingActive; - bool _closeBlockingCompleted; - bool _readyForDeletion; - - CriticalSectionWrapper* _cs; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_LINUX_H_ diff --git a/modules/udp_transport/source/udp_socket_manager_linux.cc b/modules/udp_transport/source/udp_socket_manager_linux.cc deleted file mode 100644 index ba8f86c7b..000000000 --- a/modules/udp_transport/source/udp_socket_manager_linux.cc +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_manager_linux.h" - -#include -#include -#include -#include -#include - -#include "trace.h" -#include "udp_socket_linux.h" - -namespace webrtc { -UdpSocketManagerLinux::UdpSocketManagerLinux(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) - : UdpSocketManager(id, numOfWorkThreads), - _id(id), - _critSect(CriticalSectionWrapper::CreateCriticalSection()), - _numberOfSocketMgr(numOfWorkThreads), - _incSocketMgrNextTime(0), - _nextSocketMgrToAssign(0), - _socketMgr() -{ - if(MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX < _numberOfSocketMgr) - { - _numberOfSocketMgr = MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX; - } - for(int i = 0;i < _numberOfSocketMgr; i++) - { - _socketMgr[i] = new UdpSocketManagerLinuxImpl(); - } - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::UdpSocketManagerLinux()", - _numberOfSocketMgr); -} - -UdpSocketManagerLinux::~UdpSocketManagerLinux() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::UdpSocketManagerLinux()", - _numberOfSocketMgr); - - for(int i = 0;i < _numberOfSocketMgr; i++) - { - delete _socketMgr[i]; - } - delete _critSect; -} - -WebRtc_Word32 UdpSocketManagerLinux::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -bool UdpSocketManagerLinux::Start() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::Start()", - _numberOfSocketMgr); - - _critSect->Enter(); - bool retVal = true; - for(int i = 0;i < _numberOfSocketMgr && retVal; i++) - { - retVal = _socketMgr[i]->Start(); - } - if(!retVal) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocketManagerLinux(%d)::Start() error starting socket managers", - _numberOfSocketMgr); - } - _critSect->Leave(); - return retVal; -} - -bool UdpSocketManagerLinux::Stop() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::Stop()",_numberOfSocketMgr); - - _critSect->Enter(); - bool retVal = true; - for(int i = 0;i < _numberOfSocketMgr && retVal; i++) - { - retVal = _socketMgr[i]->Stop(); - } - if(!retVal) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocketManagerLinux(%d)::Stop() there are still active socket\ - managers", - _numberOfSocketMgr); - } - _critSect->Leave(); - return retVal; -} - -bool UdpSocketManagerLinux::AddSocket(UdpSocketWrapper* s) -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::AddSocket()",_numberOfSocketMgr); - - _critSect->Enter(); - bool retVal = _socketMgr[_nextSocketMgrToAssign]->AddSocket(s); - if(!retVal) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocketManagerLinux(%d)::AddSocket() failed to add socket to\ - manager", - _numberOfSocketMgr); - } - - // Distribute sockets on UdpSocketManagerLinuxImpls in a round-robin - // fashion. - if(_incSocketMgrNextTime == 0) - { - _incSocketMgrNextTime++; - } else { - _incSocketMgrNextTime = 0; - _nextSocketMgrToAssign++; - if(_nextSocketMgrToAssign >= _numberOfSocketMgr) - { - _nextSocketMgrToAssign = 0; - } - } - _critSect->Leave(); - return retVal; -} - -bool UdpSocketManagerLinux::RemoveSocket(UdpSocketWrapper* s) -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketManagerLinux(%d)::RemoveSocket()", - _numberOfSocketMgr); - - _critSect->Enter(); - bool retVal = false; - for(int i = 0;i < _numberOfSocketMgr && (retVal == false); i++) - { - retVal = _socketMgr[i]->RemoveSocket(s); - } - if(!retVal) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpSocketManagerLinux(%d)::RemoveSocket() failed to remove socket\ - from manager", - _numberOfSocketMgr); - } - _critSect->Leave(); - return retVal; -} - - -UdpSocketManagerLinuxImpl::UdpSocketManagerLinuxImpl() -{ - _critSectList = CriticalSectionWrapper::CreateCriticalSection(); - _thread = ThreadWrapper::CreateThread(UdpSocketManagerLinuxImpl::Run, this, - kRealtimePriority, - "UdpSocketManagerLinuxImplThread"); - FD_ZERO(&_readFds); - WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, - "UdpSocketManagerLinux created"); -} - -UdpSocketManagerLinuxImpl::~UdpSocketManagerLinuxImpl() -{ - if(_thread != NULL) - { - delete _thread; - } - - if (_critSectList != NULL) - { - UpdateSocketMap(); - - _critSectList->Enter(); - - MapItem* item = _socketMap.First(); - while(item) - { - UdpSocketLinux* s = static_cast(item->GetItem()); - _socketMap.Erase(item); - item = _socketMap.First(); - delete s; - } - _critSectList->Leave(); - - delete _critSectList; - } - - WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, - "UdpSocketManagerLinux deleted"); -} - -bool UdpSocketManagerLinuxImpl::Start() -{ - unsigned int id = 0; - if (_thread == NULL) - { - return false; - } - - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, - "Start UdpSocketManagerLinux"); - return _thread->Start(id); -} - -bool UdpSocketManagerLinuxImpl::Stop() -{ - if (_thread == NULL) - { - return true; - } - - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, - "Stop UdpSocketManagerLinux"); - return _thread->Stop(); -} - -bool UdpSocketManagerLinuxImpl::Process() -{ - bool doSelect = false; - // Timeout = 1 second. - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 10000; - MapItem* it; - - FD_ZERO(&_readFds); - - UpdateSocketMap(); - - unsigned int maxFd = 0; - for (it = _socketMap.First(); it != NULL; it=_socketMap.Next(it)) - { - UdpSocketLinux* s = static_cast(it->GetItem()); - doSelect = true; - maxFd = maxFd > it->GetUnsignedId() ? maxFd : it->GetUnsignedId(); - FD_SET(it->GetUnsignedId(), &_readFds); - - maxFd = maxFd > it->GetUnsignedId() ? maxFd : it->GetUnsignedId(); - doSelect = true; - } - - int num = 0; - if (doSelect) - { - num = select(maxFd+1, &_readFds, NULL, NULL, &timeout); - - if (num == SOCKET_ERROR) - { - // Timeout = 10 ms. - timespec t; - t.tv_sec = 0; - t.tv_nsec = 10000*1000; - nanosleep(&t, NULL); - return true; - } - }else - { - // Timeout = 10 ms. - timespec t; - t.tv_sec = 0; - t.tv_nsec = 10000*1000; - nanosleep(&t, NULL); - return true; - } - - for (it = _socketMap.First(); it != NULL && num > 0; - it = _socketMap.Next(it)) - { - UdpSocketLinux* s = static_cast(it->GetItem()); - if (FD_ISSET(it->GetUnsignedId(), &_readFds)) - { - s->HasIncoming(); - num--; - } - } - return true; -} - -bool UdpSocketManagerLinuxImpl::Run(ThreadObj obj) -{ - UdpSocketManagerLinuxImpl* mgr = - static_cast(obj); - return mgr->Process(); -} - -bool UdpSocketManagerLinuxImpl::AddSocket(UdpSocketWrapper* s) -{ - UdpSocketLinux* sl = static_cast(s); - if(sl->GetFd() == INVALID_SOCKET || !(sl->GetFd() < FD_SETSIZE)) - { - return false; - } - _critSectList->Enter(); - _addList.PushBack(s); - _critSectList->Leave(); - return true; -} - -bool UdpSocketManagerLinuxImpl::RemoveSocket(UdpSocketWrapper* s) -{ - // Put in remove list if this is the correct UdpSocketManagerLinuxImpl. - _critSectList->Enter(); - - // If the socket is in the add list it's safe to remove and delete it. - ListItem* addListItem = _addList.First(); - while(addListItem) - { - UdpSocketLinux* addSocket = (UdpSocketLinux*)addListItem->GetItem(); - unsigned int addFD = addSocket->GetFd(); - unsigned int removeFD = static_cast(s)->GetFd(); - if(removeFD == addFD) - { - _removeList.PushBack(removeFD); - _critSectList->Leave(); - return true; - } - addListItem = _addList.Next(addListItem); - } - - // Checking the socket map is safe since all Erase and Insert calls to this - // map are also protected by _critSectList. - if(_socketMap.Find(static_cast(s)->GetFd()) != NULL) - { - _removeList.PushBack(static_cast(s)->GetFd()); - _critSectList->Leave(); - return true; - } - _critSectList->Leave(); - return false; -} - -void UdpSocketManagerLinuxImpl::UpdateSocketMap() -{ - // Remove items in remove list. - _critSectList->Enter(); - while(!_removeList.Empty()) - { - UdpSocketLinux* deleteSocket = NULL; - unsigned int removeFD = _removeList.First()->GetUnsignedItem(); - - // If the socket is in the add list it hasn't been added to the socket - // map yet. Just remove the socket from the add list. - ListItem* addListItem = _addList.First(); - while(addListItem) - { - UdpSocketLinux* addSocket = (UdpSocketLinux*)addListItem->GetItem(); - unsigned int addFD = addSocket->GetFd(); - if(removeFD == addFD) - { - deleteSocket = addSocket; - _addList.Erase(addListItem); - break; - } - addListItem = _addList.Next(addListItem); - } - - // Find and remove socket from _socketMap. - MapItem* it = _socketMap.Find(removeFD); - if(it != NULL) - { - UdpSocketLinux* socket = - static_cast(it->GetItem()); - if(socket) - { - deleteSocket = socket; - } - _socketMap.Erase(it); - } - if(deleteSocket) - { - deleteSocket->ReadyForDeletion(); - delete deleteSocket; - } - _removeList.PopFront(); - } - - // Add sockets from add list. - while(!_addList.Empty()) - { - UdpSocketLinux* s = - static_cast(_addList.First()->GetItem()); - if(s) - { - _socketMap.Insert(s->GetFd(), s); - } - _addList.PopFront(); - } - _critSectList->Leave(); -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_manager_linux.h b/modules/udp_transport/source/udp_socket_manager_linux.h deleted file mode 100644 index 8cf6f5047..000000000 --- a/modules/udp_transport/source/udp_socket_manager_linux.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_LINUX_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_LINUX_H_ - -#include -#include - -#include "critical_section_wrapper.h" -#include "list_wrapper.h" -#include "map_wrapper.h" -#include "thread_wrapper.h" -#include "udp_socket_manager_wrapper.h" -#include "udp_socket_wrapper.h" - -#define MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX 8 - -namespace webrtc { -class UdpSocketManagerLinuxImpl; - -class UdpSocketManagerLinux : public UdpSocketManager -{ -public: - UdpSocketManagerLinux(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); - virtual ~UdpSocketManagerLinux(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool Start(); - virtual bool Stop(); - - virtual bool AddSocket(UdpSocketWrapper* s); - virtual bool RemoveSocket(UdpSocketWrapper* s); -private: - WebRtc_Word32 _id; - CriticalSectionWrapper* _critSect; - WebRtc_UWord8 _numberOfSocketMgr; - WebRtc_UWord8 _incSocketMgrNextTime; - WebRtc_UWord8 _nextSocketMgrToAssign; - UdpSocketManagerLinuxImpl* _socketMgr[MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX]; -}; - -class UdpSocketManagerLinuxImpl -{ -public: - UdpSocketManagerLinuxImpl(); - virtual ~UdpSocketManagerLinuxImpl(); - - virtual bool Start(); - virtual bool Stop(); - - virtual bool AddSocket(UdpSocketWrapper* s); - virtual bool RemoveSocket(UdpSocketWrapper* s); - -protected: - static bool Run(ThreadObj obj); - bool Process(); - void UpdateSocketMap(); - -private: - ThreadWrapper* _thread; - CriticalSectionWrapper* _critSectList; - - fd_set _readFds; - - MapWrapper _socketMap; - ListWrapper _addList; - ListWrapper _removeList; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_LINUX_H_ diff --git a/modules/udp_transport/source/udp_socket_manager_windows.cc b/modules/udp_transport/source/udp_socket_manager_windows.cc deleted file mode 100644 index 970b0e2dd..000000000 --- a/modules/udp_transport/source/udp_socket_manager_windows.cc +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_manager_windows.h" -#include "udp_socket_windows.h" - -namespace webrtc { -WebRtc_UWord32 UdpSocketManagerWindows::_numOfActiveManagers = 0; - -UdpSocketManagerWindows::UdpSocketManagerWindows( - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) - : _id(id), - UdpSocketManager(id, numOfWorkThreads) -{ - const WebRtc_Word8* threadName = "UdpSocketManagerWindows_Thread"; - _critSectList = CriticalSectionWrapper::CreateCriticalSection(); - _thread = ThreadWrapper::CreateThread(UdpSocketManagerWindows::Run, - this, kRealtimePriority, threadName); - FD_ZERO(&_readFds); - FD_ZERO(&_writeFds); - FD_ZERO(&_exceptFds); - _numOfActiveManagers++; -} - -UdpSocketManagerWindows::~UdpSocketManagerWindows() -{ - if(_thread != NULL) - { - delete _thread; - } - - if (_critSectList != NULL) - { - _critSectList->Enter(); - - while(!_socketMap.empty()) - { - std::map::iterator it = - _socketMap.begin(); - UdpSocketWindows* s = static_cast(it->second); - _socketMap.erase(it); - delete s; - } - _removeList.erase(_removeList.begin(), _removeList.end()); - - while(!_addList.empty()) - { - std::list::iterator it = _addList.begin(); - UdpSocketWindows* s = static_cast(*it); - _addList.erase(it); - delete s; - } - _critSectList->Leave(); - - delete _critSectList; - } - - _numOfActiveManagers--; - - if (_numOfActiveManagers == 0) - WSACleanup(); -} - -WebRtc_Word32 UdpSocketManagerWindows::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -bool UdpSocketManagerWindows::Start() -{ - unsigned int id; - if (_thread == NULL) - return false; - - return _thread->Start(id); -} - -bool UdpSocketManagerWindows::Stop() -{ - if (_thread == NULL) - return true; - - return _thread->Stop(); -} - -bool UdpSocketManagerWindows::Process() -{ - bool doSelect = false; - // Timeout = 1 second. - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 10000; - - FD_ZERO(&_readFds); - FD_ZERO(&_writeFds); - FD_ZERO(&_exceptFds); - - _critSectList->Enter(); - // Remove sockets that have been registered for removal. - while(!_removeList.empty()) - { - SOCKET id = *_removeList.begin(); - std::map::iterator it = _socketMap.find(id); - if(it != _socketMap.end()) - { - UdpSocketWindows* s = static_cast(it->second); - _socketMap.erase(it); - _removeList.pop_front(); - delete s; - } - } - - // Add sockets that have been registered for being added. - while (!_addList.empty()) - { - UdpSocketWindows* s = *_addList.begin(); - if(s) - { - _socketMap[s->GetFd()] = s; - } - _addList.pop_front(); - } - _critSectList->Leave(); - - std::map::iterator it = _socketMap.begin(); - while(it != _socketMap.end()) - { - UdpSocketWindows* s = it->second; - if (s->WantsIncoming()) - { - doSelect = true; - FD_SET(it->first, &_readFds); - } - if(!s->IsWritable()) - { - FD_SET(it->first, &_writeFds); - doSelect = true; - } - it++; - } - - WebRtc_Word32 num = 0; - if (doSelect) - { - num = select(0, &_readFds, &_writeFds, &_exceptFds, &timeout); - if (num == SOCKET_ERROR) - { - Sleep(10); - return true; - } - }else - { - Sleep(10); - return true; - } - - it = _socketMap.begin(); - while (it != _socketMap.end() && num > 0) - { - if (FD_ISSET(it->first, &_readFds)) - { - static_cast(it->second)->HasIncoming(); - num--; - } - if (FD_ISSET(it->first, &_writeFds)) - { - // Socket available for writing. - static_cast(it->second)->SetWritable(); - num--; - } - } - return true; -} - -bool UdpSocketManagerWindows::Run(ThreadObj obj) -{ - UdpSocketManagerWindows* mgr = static_cast(obj); - return mgr->Process(); -}; - -bool UdpSocketManagerWindows::AddSocket(UdpSocketWrapper* s) -{ - UdpSocketWindows* winSock = static_cast(s); - - _critSectList->Enter(); - std::map::iterator it = - _socketMap.find(winSock->GetFd()); - if (it != _socketMap.end()) - { - if (!_removeList.empty()) - { - // File descriptors are re-used so it's possible that a socket has - // been added with the same file descriptor as a socket that is to - // be removed. I.e. the socket that is to be removed is no longer - // in use, delete it. - // TODO (hellner): removing items from _socketMap may cause race - // condition. Fix this. - std::list::iterator removeIt = _removeList.begin(); - while(removeIt != _removeList.end()) - { - if (*removeIt == winSock->GetFd()) - { - it = _socketMap.find(*removeIt); - UdpSocketWindows* delete_socket = it->second; - _socketMap.erase(it); - _removeList.erase(removeIt); - delete delete_socket; - _addList.push_back(winSock); - _critSectList->Leave(); - return true; - } - removeIt++; - } - } - _critSectList->Leave(); - return false; - } - - _addList.push_back(winSock); - _critSectList->Leave(); - return true; -} - -bool UdpSocketManagerWindows::RemoveSocket(UdpSocketWrapper* s) -{ - UdpSocketWindows* winSock = static_cast(s); - - _critSectList->Enter(); - // If socket is in the add list its safe to just remove it from the list. - if (!_addList.empty()) - { - std::list::iterator it = _addList.begin(); - while(it != _addList.end()) - { - UdpSocketWindows* tempSocket = (*it); - if (tempSocket->GetFd() == winSock->GetFd()) - { - _addList.erase(it); - delete winSock; - _critSectList->Leave(); - return true; - } - it++; - } - } - - // If the socket is not even added to the UdpSocketManagerWindows it's - // safe to delete the socket. - std::map::iterator findIt = - _socketMap.find(winSock->GetFd()); - if (findIt == _socketMap.end()) - { - delete winSock; - _critSectList->Leave(); - return false; - } - - _removeList.push_back(winSock->GetFd()); - _critSectList->Leave(); - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_manager_windows.h b/modules/udp_transport/source/udp_socket_manager_windows.h deleted file mode 100644 index 143cb5e54..000000000 --- a/modules/udp_transport/source/udp_socket_manager_windows.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WINDOWS_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WINDOWS_H_ - -#define FD_SETSIZE 1024 - -#include -#include -#include - -// Don't change the include order. -// TODO (hellner): all header files should be compilable separately. Fix this. -#include "udp_socket_manager_wrapper.h" -#include "thread_wrapper.h" -#include "critical_section_wrapper.h" - -namespace webrtc { -class UdpSocketWindows; - -class UdpSocketManagerWindows : public UdpSocketManager -{ -public: - UdpSocketManagerWindows(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); - virtual ~UdpSocketManagerWindows(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool Start(); - virtual bool Stop(); - - virtual bool AddSocket(UdpSocketWrapper* s); - virtual bool RemoveSocket(UdpSocketWrapper* s); - -protected: - static bool Run(ThreadObj obj); - bool Process(); - -private: - WebRtc_Word32 _id; - ThreadWrapper* _thread; - - fd_set _readFds; - fd_set _writeFds; - fd_set _exceptFds; - - CriticalSectionWrapper* _critSectList; - - std::map _socketMap; - std::list _addList; - std::list _removeList; - - static WebRtc_UWord32 _numOfActiveManagers; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WINDOWS_H_ diff --git a/modules/udp_transport/source/udp_socket_manager_wrapper.cc b/modules/udp_transport/source/udp_socket_manager_wrapper.cc deleted file mode 100644 index f42bca775..000000000 --- a/modules/udp_transport/source/udp_socket_manager_wrapper.cc +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_manager_wrapper.h" - -#include - -#ifdef _WIN32 -#include "fix_interlocked_exchange_pointer_windows.h" -#include "udp_socket_manager_windows.h" -#include "udp_socket2_manager_windows.h" -#else -#include "udp_socket_manager_linux.h" -#endif - -#ifndef _WIN32 -#ifndef WEBRTC_NO_AUTO_PTR -#include -#endif -#endif - -namespace webrtc { -UdpSocketManager* UdpSocketManager::CreateSocketManager( - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) -{ -#if defined(_WIN32) - #if (defined(USE_WINSOCK2)) - return static_cast( - new UdpSocket2ManagerWindows(id, numOfWorkThreads)); - #else - numOfWorkThreads = 1; - return static_cast( - new UdpSocketManagerWindows(id, numOfWorkThreads)); - #endif -#else - return new UdpSocketManagerLinux(id, numOfWorkThreads); -#endif -} - -// TODO (hellner): more or less the same code is used in trace_impl.cc. -// Should be possible to avoid duplication here. -// Construct On First Use idiom. Avoids "static initialization order fiasco". -UdpSocketManager* UdpSocketManager::StaticInstance( - const UdpSocketManagerCount inc, - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) -{ - // TODO (hellner): use atomic wrapper instead. - static volatile long theUdpSocketManagerCount = 0; - static UdpSocketManager* volatile theUdpSocketManager = NULL; - - UdpSocketManagerState state = kUdpSocketManagerExist; - -#ifndef _WIN32 - -#ifdef WEBRTC_NO_AUTO_PTR - // TODO (pwestin): crtiSect is never reclaimed. Fix memory leak. - static CriticalSectionWrapper* crtiSect( - CriticalSectionWrapper::CreateCriticalSection()); -#else - static std::auto_ptr crtiSect = - std::auto_ptr( - CriticalSectionWrapper::CreateCriticalSection()); -#endif - CriticalSectionScoped lock(*crtiSect); - - if(inc == kUdpSocketManagerInc) - { - theUdpSocketManagerCount++; - if(theUdpSocketManagerCount == 1) - { - state = kUdpSocketManagerCreate; - } - } else - { - theUdpSocketManagerCount--; - if(theUdpSocketManagerCount == 0) - { - state = kUdpSocketManagerDestroy; - } - } - if(state == kUdpSocketManagerCreate) - { - theUdpSocketManager = - UdpSocketManager::CreateSocketManager(id, numOfWorkThreads); - theUdpSocketManager->Start(); - assert(theUdpSocketManager); - return theUdpSocketManager; - - }else if(state == kUdpSocketManagerDestroy) - { - UdpSocketManager* oldValue = theUdpSocketManager; - theUdpSocketManager = NULL; - if(oldValue) - { - if(oldValue->Stop()) - { - delete static_cast(oldValue); - } - } - return NULL; - } else - { - if(theUdpSocketManager) - { - numOfWorkThreads = theUdpSocketManager->WorkThreads(); - } - } -#else // _WIN32 - if(inc == kUdpSocketManagerInc) - { - if(theUdpSocketManagerCount == 0) - { - state = kUdpSocketManagerCreate; - }else { - if(1 == InterlockedIncrement(&theUdpSocketManagerCount)) - { - // The instance has been destroyed by some other thread. - // Rollback. - InterlockedDecrement(&theUdpSocketManagerCount); - state = kUdpSocketManagerCreate; - } - } - } else - { - WebRtc_Word32 newValue = InterlockedDecrement( - &theUdpSocketManagerCount); - if(newValue == 0) - { - state = kUdpSocketManagerDestroy; - } - } - if(state == kUdpSocketManagerCreate) - { - // Create instance and let whichever thread finishes first assign its - // local copy to the global instance. All other threads reclaim their - // local copy. - UdpSocketManager* newSocketMgr= - UdpSocketManager::CreateSocketManager(id, numOfWorkThreads); - if(1 == InterlockedIncrement(&theUdpSocketManagerCount)) - { - UdpSocketManager* oldValue = (UdpSocketManager*) - InterlockedExchangePointer( - reinterpret_cast(&theUdpSocketManager), - newSocketMgr); - newSocketMgr->Start(); - assert(oldValue == NULL); - assert(theUdpSocketManager); - return newSocketMgr; - - } - InterlockedDecrement(&theUdpSocketManagerCount); - if(newSocketMgr) - { - delete static_cast(newSocketMgr); - } - return NULL; - } else if(state == kUdpSocketManagerDestroy) - { - UdpSocketManager* oldValue = (UdpSocketManager*) - InterlockedExchangePointer( - reinterpret_cast(&theUdpSocketManager), - NULL); - if(oldValue) - { - if(oldValue->Stop()) - { - delete static_cast(oldValue); - } - } - return NULL; - } else - { - if(theUdpSocketManager) - { - numOfWorkThreads = theUdpSocketManager->WorkThreads(); - } - } -#endif // #ifndef _WIN32 - return theUdpSocketManager; -} - -UdpSocketManager* UdpSocketManager::Create(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads) -{ - return UdpSocketManager::StaticInstance(kUdpSocketManagerInc, id, - numOfWorkThreads); -} - -void UdpSocketManager::Return() -{ - WebRtc_UWord8 numOfWorkThreads = 0; - UdpSocketManager::StaticInstance(kUdpSocketManagerDec, -1, - numOfWorkThreads); -} - -UdpSocketManager::UdpSocketManager(const WebRtc_Word32 /*id*/, - WebRtc_UWord8& numOfWorkThreads) - : _numOfWorkThreads(numOfWorkThreads) -{ -} - -WebRtc_UWord8 UdpSocketManager::WorkThreads() const -{ - return _numOfWorkThreads; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_manager_wrapper.h b/modules/udp_transport/source/udp_socket_manager_wrapper.h deleted file mode 100644 index 31828a137..000000000 --- a/modules/udp_transport/source/udp_socket_manager_wrapper.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WRAPPER_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WRAPPER_H_ - -#include "typedefs.h" - -namespace webrtc { -class UdpSocketWrapper; - -enum UdpSocketManagerCount -{ - kUdpSocketManagerDec = 0, - kUdpSocketManagerInc = 1 -}; - -enum UdpSocketManagerState -{ - kUdpSocketManagerExist = 0, - kUdpSocketManagerCreate = 1, - kUdpSocketManagerDestroy = 2 -}; - -class UdpSocketManager -{ -public: - static UdpSocketManager* Create(const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); - static void Return(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - // Start listening to sockets that have been registered via the - // AddSocket(..) API. - virtual bool Start() = 0; - // Stop listening to sockets. - virtual bool Stop() = 0; - - virtual WebRtc_UWord8 WorkThreads() const; - - // Register a socket with the socket manager. - virtual bool AddSocket(UdpSocketWrapper* s) = 0; - // Unregister a socket from the manager. - virtual bool RemoveSocket(UdpSocketWrapper* s) = 0; - -protected: - UdpSocketManager(const WebRtc_Word32 /*id*/, - WebRtc_UWord8& numOfWorkThreads); - - virtual ~UdpSocketManager() {} - -private: - const WebRtc_UWord8 _numOfWorkThreads; - - // Factory method. - static UdpSocketManager* CreateSocketManager( - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); - - static UdpSocketManager* StaticInstance(const UdpSocketManagerCount inc, - const WebRtc_Word32 id, - WebRtc_UWord8& numOfWorkThreads); -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_MANAGER_WRAPPER_H_ diff --git a/modules/udp_transport/source/udp_socket_windows.cc b/modules/udp_transport/source/udp_socket_windows.cc deleted file mode 100644 index 05381e1d0..000000000 --- a/modules/udp_transport/source/udp_socket_windows.cc +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_windows.h" - -// Disable deprication warning from traffic.h -#pragma warning(disable : 4995) - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include -#include -#include -#include -#include -// Don't change include order for these header files. -#include -#include -#include - -#include "traffic_control_windows.h" -#include "udp_socket_manager_wrapper.h" - -namespace webrtc { -typedef struct _QOS_DESTADDR -{ - QOS_OBJECT_HDR ObjectHdr; - const struct sockaddr* SocketAddress; - ULONG SocketAddressLength; -} QOS_DESTADDR, *LPQOS_DESTADDR; - -typedef const QOS_DESTADDR* LPCQOS_DESTADDR; - -#define QOS_GENERAL_ID_BASE 2000 -#define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE) - -#define MAX_PACKET_SIZE 2048 - -class UDPPacket -{ -public: - UDPPacket() - { - _length = 0; - } - WebRtc_Word32 Set(const WebRtc_Word8* buf, WebRtc_Word32 length) - { - if(length > MAX_PACKET_SIZE) - return 0; - - _length = length; - memcpy(_buffer,buf,length); - return length; - } - WebRtc_Word32 Set(const WebRtc_Word8* buf, WebRtc_Word32 length, - const SocketAddress* addr) - { - if(length > MAX_PACKET_SIZE) - { - return 0; - } - - _length = length; - memcpy(&_remoteAddr,addr,sizeof(SocketAddress)); - memcpy(_buffer,buf,length); - return length; - } - - SocketAddress _remoteAddr; - WebRtc_Word8 _buffer[MAX_PACKET_SIZE]; - WebRtc_Word32 _length; -}; - -UdpSocketWindows::UdpSocketWindows(const WebRtc_Word32 id, - UdpSocketManager* mgr, bool ipV6Enable) - : _id(id), - _qos(true) -{ - _wantsIncoming = false; - _error = 0; - _mgr = mgr; - _addedToMgr = false; - - _obj = NULL; - _incomingCb = NULL; - _socket = INVALID_SOCKET; - _terminate=false; - - _clientHandle = INVALID_HANDLE_VALUE; - _flowHandle = INVALID_HANDLE_VALUE; - _filterHandle = INVALID_HANDLE_VALUE; - - WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, - "UdpSocketWindows::UdpSocketWindows()"); - - _gtc = NULL; - - // Check if QoS is supported. - WSAPROTOCOL_INFO pProtocolInfo; - DWORD dwBufLen = 0; - BOOL bProtocolFound = FALSE; - WSAPROTOCOL_INFO* lpProtocolBuf = NULL; - - // Set dwBufLen to the size needed to retreive all the requested information - // from WSAEnumProtocols. - WebRtc_Word32 nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); - lpProtocolBuf = (WSAPROTOCOL_INFO*)malloc(dwBufLen); - nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); - - WebRtc_Word32 iProtocol; - if (ipV6Enable) - { - iProtocol = AF_INET6; - } else { - iProtocol = AF_INET; - } - - for (WebRtc_Word32 i = 0; i < nRet; i++) - { - if (iProtocol == lpProtocolBuf[i].iAddressFamily && IPPROTO_UDP == - lpProtocolBuf[i].iProtocol) - { - if ((XP1_QOS_SUPPORTED == - (XP1_QOS_SUPPORTED & lpProtocolBuf[i].dwServiceFlags1))) - { - pProtocolInfo = lpProtocolBuf[i]; - bProtocolFound = TRUE; - break; - } - } - } - - if(!bProtocolFound) - { - _socket = INVALID_SOCKET; - _qos = false; - free(lpProtocolBuf); - _error = SOCKET_ERROR_NO_QOS; - }else { - _socket = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO,&pProtocolInfo, 0, - WSA_FLAG_OVERLAPPED); - free(lpProtocolBuf); - if (_socket != INVALID_SOCKET) - { - return; - }else - { - _qos = false; - _error = SOCKET_ERROR_NO_QOS; - } - } - // QoS not supported. - if(ipV6Enable) - { - _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - }else - { - _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - } - // Non-blocking mode. - WebRtc_Word32 iMode = 1; - ioctlsocket(_socket, FIONBIO, (u_long FAR*) &iMode); -} - -UdpSocketWindows::~UdpSocketWindows() -{ - WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, - "UdpSocketWindows::~UdpSocketWindows()"); - if (_gtc) - { - TrafficControlWindows::Release(_gtc); - } -} - -WebRtc_Word32 UdpSocketWindows::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - if (_gtc) - { - _gtc->ChangeUniqueId(id); - } - return 0; -} - -bool UdpSocketWindows::ValidHandle() -{ - return GetFd() != INVALID_SOCKET; -} - -bool UdpSocketWindows::SetCallback(CallbackObj obj, IncomingSocketCallback cb) -{ - _obj = obj; - _incomingCb = cb; - - if (_mgr->AddSocket(this)) - { - _addedToMgr = true; - return true; - } - return false; -} - -bool UdpSocketWindows::SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, - WebRtc_Word32 optlen) -{ - if(0 == setsockopt(_socket, level, optname, optval, optlen)) - { - return true; - } - _error = WSAGetLastError(); - return false; -} - -bool UdpSocketWindows::Bind(const SocketAddress& name) -{ - const struct sockaddr* socketName = - reinterpret_cast(&name); - - if (0 == bind(_socket, socketName, sizeof(SocketAddress))) - { - _localAddr = name; - return true; - } - _error = WSAGetLastError(); - return false; -} - -WebRtc_Word32 UdpSocketWindows::SendTo(const WebRtc_Word8* buf, - WebRtc_Word32 len, - const SocketAddress& to) -{ - // Don't try to send this packet if there are older packets queued up. - if(!_notSentPackets.Empty()) - { - UDPPacket* packet = new UDPPacket(); - packet->Set(buf, len, &to); - if(!_notSentPackets.Empty()) - { - _notSentPackets.PushBack(packet); - return len; - }else { - // No old packets queued up. Free to try to send. - delete packet; - } - } - - WebRtc_Word32 retVal; - retVal = sendto(_socket, buf, len, 0, - reinterpret_cast(&to), - sizeof(SocketAddress)); - - if(retVal == SOCKET_ERROR) - { - _error = WSAGetLastError(); - if (_error == WSAEWOULDBLOCK) - { - UDPPacket* packet = new UDPPacket(); - packet->Set(buf,len, &to); - _notSentPackets.PushBack(packet); - return len; - } - } - return retVal; -} - -void UdpSocketWindows::HasIncoming() -{ - WebRtc_Word8 buf[MAX_PACKET_SIZE]; - SocketAddress from; - int fromlen = sizeof(from); - WebRtc_Word32 retval = recvfrom(_socket,buf, sizeof(buf), 0, - reinterpret_cast(&from), - &fromlen); - - switch(retval) - { - case 0: - // The connection has been gracefully closed. - break; - case SOCKET_ERROR: - _error = WSAGetLastError(); - break; - default: - if(_wantsIncoming && _incomingCb) - _incomingCb(_obj,buf, retval, &from); - break; - } -} - -void UdpSocketWindows::CleanUp() -{ - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "UdpSocketWindows::CleanUp()"); - _wantsIncoming = false; - - if(_clientHandle != INVALID_HANDLE_VALUE) - { - assert(_filterHandle != INVALID_HANDLE_VALUE); - assert(_flowHandle != INVALID_HANDLE_VALUE); - - if (_gtc) - { - _gtc->TcDeleteFilter(_filterHandle); - _gtc->TcDeleteFlow(_flowHandle); - _gtc->TcDeregisterClient(_clientHandle); - } - - _clientHandle = INVALID_HANDLE_VALUE; - _filterHandle = INVALID_HANDLE_VALUE; - _flowHandle = INVALID_HANDLE_VALUE; - } - - while(!_notSentPackets.Empty()) - { - UDPPacket* packet = (UDPPacket*)_notSentPackets.First()->GetItem(); - if(!packet) - { - break; - } - delete packet; - _notSentPackets.PopFront(); - } - - if (_socket != INVALID_SOCKET) - { - if (closesocket(_socket) == SOCKET_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "closesocket() => error = %d", WSAGetLastError()); - } - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "WinSock::closesocket() done"); - - if(_addedToMgr) - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "calling UdpSocketManager::RemoveSocket()"); - _mgr->RemoveSocket(this); - } - } -} - -void UdpSocketWindows::SetWritable() -{ - // Try to send packets that have been queued up. - while(!_notSentPackets.Empty()) - { - UDPPacket* packet = (UDPPacket*)_notSentPackets.First()->GetItem(); - if(!packet) - { - break; - } - if(sendto( - _socket,packet->_buffer, - packet->_length, - 0, - reinterpret_cast( - &(packet->_remoteAddr)), - sizeof(SocketAddress)) == SOCKET_ERROR) - { - _error = WSAGetLastError(); - if (_error == WSAEWOULDBLOCK) - { - return; - } - } else { - delete packet; - _notSentPackets.PopFront(); - } - } -} - -bool UdpSocketWindows::SetQos(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, - const SocketAddress &stRemName, - WebRtc_Word32 overrideDSCP) -{ - if(_qos == false) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "UdpSocket2Windows::SetQos(), socket not capable of QOS"); - return false; - } - QOS Qos; - WebRtc_Word32 result; - DWORD BytesRet; - - if(overrideDSCP != 0) - { - FLOWSPEC f; - WebRtc_Word32 err = CreateFlowSpec(serviceType, tokenRate, bucketSize, - peekBandwith, minPolicedSize, - maxSduSize, &f); - if(err == -1) - { - return false; - } - return SetTOSByte(overrideDSCP, &f, &f) == 0; - } - memset(&Qos, QOS_NOT_SPECIFIED, sizeof(QOS)); - - Qos.SendingFlowspec.ServiceType = serviceType; - Qos.SendingFlowspec.TokenRate = tokenRate; - Qos.SendingFlowspec.TokenBucketSize = bucketSize; - Qos.SendingFlowspec.PeakBandwidth = peekBandwith; - Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED; - Qos.SendingFlowspec.MinimumPolicedSize = minPolicedSize; - Qos.SendingFlowspec.MaxSduSize = maxSduSize; - - // Only ServiceType is needed for receiving. - Qos.ReceivingFlowspec.ServiceType = serviceType; - Qos.ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; - Qos.ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; - - Qos.ProviderSpecific.len = 0; - Qos.ProviderSpecific.buf = NULL; - WebRtc_Word8* p = (WebRtc_Word8*)malloc(sizeof(QOS_DESTADDR) + - sizeof(QOS_DS_CLASS)); - - QOS_DESTADDR* QosDestaddr = (QOS_DESTADDR*)p; - ZeroMemory((WebRtc_Word8 *)QosDestaddr, sizeof(QOS_DESTADDR)); - QosDestaddr->ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR; - QosDestaddr->ObjectHdr.ObjectLength = sizeof(QOS_DESTADDR); - QosDestaddr->SocketAddress = (SOCKADDR*)&stRemName; - QosDestaddr->SocketAddressLength = sizeof(SocketAddress); - Qos.ProviderSpecific.len = QosDestaddr->ObjectHdr.ObjectLength; - Qos.ProviderSpecific.buf = (WebRtc_Word8*)p; - - // Socket must be bound for this call to be successfull. If socket is not - // bound WSAGetLastError() will return 10022. - result = WSAIoctl(GetFd(),SIO_SET_QOS, &Qos,sizeof(QOS),NULL, 0, &BytesRet, - NULL,NULL); - if (result == SOCKET_ERROR) - { - _error = WSAGetLastError(); - free(p); - return false; - } - free(p); - return true; -} - -WebRtc_Word32 UdpSocketWindows::SetTOS(WebRtc_Word32 serviceType) -{ - WebRtc_Word32 res = SetTOSByte(serviceType, NULL, NULL); - - if (res == -1) - { - OSVERSIONINFO OsVersion; - OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx (&OsVersion); - - if ((OsVersion.dwMajorVersion == 4)) - { - return -1; - } - } - return res; -} - -WebRtc_Word32 UdpSocketWindows::CreateFlowSpec(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, - FLOWSPEC *f) -{ - if(!f) - { - return -1; - } - - f->ServiceType = serviceType; - f->TokenRate = tokenRate; - f->TokenBucketSize = bucketSize; - f->PeakBandwidth = peekBandwith; - f->DelayVariation = QOS_NOT_SPECIFIED; - f->Latency = QOS_NOT_SPECIFIED; - f->MinimumPolicedSize = minPolicedSize; - f->MaxSduSize = maxSduSize; - return 0; -} - -WebRtc_Word32 UdpSocketWindows::SetTOSByte(WebRtc_Word32 serviceType, - FLOWSPEC* send, FLOWSPEC* recv) -{ - if(_socket == INVALID_SOCKET) - { - return -1; - } - if (!_gtc) - { - _gtc = TrafficControlWindows::GetInstance(_id); - } - if (!_gtc) - { - return -1; - } - - TCI_CLIENT_FUNC_LIST QoSFunctions; - QoSFunctions.ClAddFlowCompleteHandler = NULL; - QoSFunctions.ClDeleteFlowCompleteHandler = NULL; - QoSFunctions.ClModifyFlowCompleteHandler = NULL; - QoSFunctions.ClNotifyHandler = (TCI_NOTIFY_HANDLER)MyClNotifyHandler; - // Register the client with Traffic control interface. - HANDLE ClientHandle; - ULONG result = _gtc->TcRegisterClient(CURRENT_TCI_VERSION, NULL, - &QoSFunctions,&ClientHandle); - if(result != NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "TcRegisterClient returned %d", result); - return result; - } - - // Find traffic control-enabled network interfaces. - ULONG BufferSize = 0; - result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL); - - if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Error enumerating interfaces, %d", result); - _gtc->TcDeregisterClient(ClientHandle); - return result; - } - - if(result != ERROR_INSUFFICIENT_BUFFER) - { - // Empty buffer contains all control-enabled network interfaces. I.e. - // ToS is not enabled. - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "Error enumerating interfaces: passed in 0 and received\ - NO_ERROR when expecting INSUFFICIENT_BUFFER, %d"); - _gtc->TcDeregisterClient(ClientHandle); - return -1; - } - - PTC_IFC_DESCRIPTOR pInterfaceBuffer = - (PTC_IFC_DESCRIPTOR)malloc(BufferSize); - if(pInterfaceBuffer == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Out ot memory failure"); - _gtc->TcDeregisterClient(ClientHandle); - return ERROR_NOT_ENOUGH_MEMORY; - } - - result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, - pInterfaceBuffer); - - if(result != NO_ERROR) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "Critical: error enumerating interfaces when passing in correct\ - buffer size: %d", result); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - - PTC_IFC_DESCRIPTOR oneinterface; - HANDLE ifcHandle, iFilterHandle, iflowHandle; - bool addrFound = false; - ULONG filterSourceAddress = ULONG_MAX; - - const struct sockaddr_in* name; - name = reinterpret_cast(&_localAddr); - - // Find the interface corresponding to the local address. - for(oneinterface = pInterfaceBuffer; - oneinterface != (PTC_IFC_DESCRIPTOR) - (((WebRtc_Word8*)pInterfaceBuffer) + BufferSize); - oneinterface = (PTC_IFC_DESCRIPTOR) - ((WebRtc_Word8*)oneinterface + oneinterface->Length)) - { - - WebRtc_Word8 interfaceName[500]; - WideCharToMultiByte(CP_ACP, 0, oneinterface->pInterfaceName, -1, - interfaceName, sizeof(interfaceName), 0, 0); - - PNETWORK_ADDRESS_LIST addresses = - &(oneinterface->AddressListDesc.AddressList); - for(LONG i = 0; i < addresses->AddressCount; i++) - { - // Only look at TCP/IP addresses. - if(addresses->Address[i].AddressType != NDIS_PROTOCOL_ID_TCP_IP) - { - continue; - } - - NETWORK_ADDRESS_IP* pIpAddr = - (NETWORK_ADDRESS_IP*)&(addresses->Address[i].Address); - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Examining Interface %s", interfaceName); - if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr) - { - filterSourceAddress = pIpAddr->in_addr; - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Found ip addr: %s", inet_ntoa(name->sin_addr)); - addrFound = true; - } - } - if(!addrFound) - { - continue; - } - else - { - break; - } - } - - if(!addrFound) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "IP Address not found"); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return -1; - } - - - result = _gtc->TcOpenInterfaceW(oneinterface->pInterfaceName, ClientHandle, - NULL, &ifcHandle); - - if(result != NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Error opening interface: %d", result); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - FLOWSPEC defaultSend, defaultRecv; - if(send == NULL) - { - defaultSend.DelayVariation = QOS_NOT_SPECIFIED; - defaultSend.Latency = QOS_NOT_SPECIFIED; - defaultSend.MaxSduSize = QOS_NOT_SPECIFIED; - defaultSend.MinimumPolicedSize = QOS_NOT_SPECIFIED; - defaultSend.PeakBandwidth = QOS_NOT_SPECIFIED; - defaultSend.ServiceType = SERVICETYPE_BESTEFFORT; - defaultSend.TokenBucketSize = QOS_NOT_SPECIFIED; - defaultSend.TokenRate = 10000; - } - else - { - defaultSend = *send; - } - if(recv == NULL) - { - defaultRecv = defaultSend; - defaultRecv.ServiceType = SERVICETYPE_CONTROLLEDLOAD; - } - else - { - defaultRecv = *recv; - } - - - PTC_GEN_FLOW flow = - (PTC_GEN_FLOW)malloc(sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS)); - - flow->ReceivingFlowspec = defaultRecv; - flow->SendingFlowspec = defaultSend; - - QOS_DS_CLASS dsClass; - - ZeroMemory((WebRtc_Word8*)&dsClass, sizeof(QOS_DS_CLASS)); - - dsClass.DSField = serviceType; - - dsClass.ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS; - dsClass.ObjectHdr.ObjectLength = sizeof(dsClass); - - memcpy(flow->TcObjects, (void*)&dsClass, sizeof(QOS_DS_CLASS)); - flow->TcObjectsLength = sizeof(dsClass); - - result = _gtc->TcAddFlow(ifcHandle, NULL, 0, flow, &iflowHandle); - if(result != NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Error adding flow: %d", result); - _gtc->TcCloseInterface(ifcHandle); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return -1; - } - - free(flow); - IP_PATTERN filterPattern, mask; - - ZeroMemory((WebRtc_Word8*)&filterPattern, sizeof(IP_PATTERN)); - ZeroMemory((WebRtc_Word8*)&mask, sizeof(IP_PATTERN)); - - filterPattern.ProtocolId = IPPROTO_UDP; - // "name" fields are in network order. - filterPattern.S_un.S_un_ports.s_srcport = name->sin_port; - filterPattern.SrcAddr = filterSourceAddress; - - // Unsigned max of a type corresponds to a bitmask with all bits set to 1. - // I.e. the filter should allow all ProtocolIds, any source port and any - // IP address. - mask.ProtocolId = UCHAR_MAX; - mask.S_un.S_un_ports.s_srcport = USHRT_MAX; - mask.SrcAddr = ULONG_MAX; - - TC_GEN_FILTER filter; - filter.AddressType = NDIS_PROTOCOL_ID_TCP_IP; - filter.Mask = (LPVOID)&mask; - filter.Pattern = (LPVOID)&filterPattern; - filter.PatternSize = sizeof(IP_PATTERN); - if(_filterHandle != INVALID_HANDLE_VALUE) - { - _gtc->TcDeleteFilter(_filterHandle); - } - - result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle); - if(result != NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Error adding filter: %d", result); - _gtc->TcDeleteFlow(iflowHandle); - _gtc->TcCloseInterface(ifcHandle); - _gtc->TcDeregisterClient(ClientHandle); - free(pInterfaceBuffer); - return result; - } - - _flowHandle = iflowHandle; - _filterHandle = iFilterHandle; - _clientHandle = ClientHandle; - - _gtc->TcCloseInterface(ifcHandle); - free(pInterfaceBuffer); - - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Successfully created flow and filter."); - return 0; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_windows.h b/modules/udp_transport/source/udp_socket_windows.h deleted file mode 100644 index 0bbd20deb..000000000 --- a/modules/udp_transport/source/udp_socket_windows.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WINDOWS_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WINDOWS_H_ - -#include - -#include "list_wrapper.h" -#include "udp_socket_manager_wrapper.h" -#include "udp_socket_wrapper.h" - -namespace webrtc { -class TrafficControlWindows; - -class UdpSocketWindows : public UdpSocketWrapper -{ -public: - UdpSocketWindows(const WebRtc_Word32 id, UdpSocketManager* mgr, - bool ipV6Enable = false); - virtual ~UdpSocketWindows(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual bool ValidHandle(); - - virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb); - - virtual bool Bind(const SocketAddress& name); - - virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, WebRtc_Word32 optlen); - - virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, - const SocketAddress& to); - - virtual SOCKET GetFd() {return _socket;} - virtual WebRtc_Word32 GetError() {return _error;} - - virtual bool SetQos(WebRtc_Word32 serviceType, WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, WebRtc_Word32 maxSduSize, - const SocketAddress& stRemName, - WebRtc_Word32 overrideDSCP = 0); - - virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType); - -protected: - void CleanUp(); - void HasIncoming(); - // Socket is free to process pending packets. - void SetWritable(); - bool IsWritable() {return _notSentPackets.Empty();} - bool WantsIncoming() {return _wantsIncoming;} - -private: - friend class UdpSocketManagerWindows; - - WebRtc_Word32 CreateFlowSpec(WebRtc_Word32 serviceType, - WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, - WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, - WebRtc_Word32 maxSduSize, FLOWSPEC* f); - - WebRtc_Word32 SetTOSByte(WebRtc_Word32 serviceType, FLOWSPEC* send, - FLOWSPEC* recv); - - WebRtc_Word32 _id; - IncomingSocketCallback _incomingCb; - - CallbackObj _obj; - bool _qos; - WebRtc_Word32 _error; - - volatile bool _addedToMgr; - - SocketAddress _remoteAddr; - SocketAddress _localAddr; - SOCKET _socket; - UdpSocketManager* _mgr; - - ListWrapper _notSentPackets; - bool _terminate; - - // QoS handles. - HANDLE _clientHandle; - HANDLE _flowHandle; - HANDLE _filterHandle; - - // TOS implementation. - TrafficControlWindows* _gtc; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WINDOWS_H_ diff --git a/modules/udp_transport/source/udp_socket_wrapper.cc b/modules/udp_transport/source/udp_socket_wrapper.cc deleted file mode 100644 index efe922a85..000000000 --- a/modules/udp_transport/source/udp_socket_wrapper.cc +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_socket_wrapper.h" - -#include -#include - -#include "event_wrapper.h" -#include "trace.h" -#include "udp_socket_manager_wrapper.h" - -#if defined(_WIN32) - #include "udp_socket_windows.h" - #include "udp_socket2_windows.h" -#else - #include "udp_socket_linux.h" -#endif - - -namespace webrtc { -bool UdpSocketWrapper::_initiated = false; - -// Temporary Android hack. The value 1024 is taken from -// /build/platforms/android-1.5/arch-arm/usr/include/linux/posix_types.h -// TODO (tomasl): can we remove this now? -#ifndef FD_SETSIZE -#define FD_SETSIZE 1024 -#endif - -UdpSocketWrapper::UdpSocketWrapper() : _deleteEvent(NULL) -{ -} - -UdpSocketWrapper::~UdpSocketWrapper() -{ - if(_deleteEvent) - { - _deleteEvent->Set(); - _deleteEvent = NULL; - } -} - -void UdpSocketWrapper::SetEventToNull() -{ - if (_deleteEvent) - { - _deleteEvent = NULL; - } -} - -#ifdef USE_WINSOCK2 -UdpSocketWrapper* UdpSocketWrapper::CreateSocket(const WebRtc_Word32 id, - UdpSocketManager* mgr, - CallbackObj obj, - IncomingSocketCallback cb, - bool ipV6Enable, - bool disableGQOS) -#else -UdpSocketWrapper* UdpSocketWrapper::CreateSocket(const WebRtc_Word32 id, - UdpSocketManager* mgr, - CallbackObj obj, - IncomingSocketCallback cb, - bool ipV6Enable, - bool /*disableGQOS*/) -#endif - -{ - WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, - "UdpSocketWrapper::CreateSocket"); - - UdpSocketWrapper* s = 0; - -#ifdef _WIN32 - if (!_initiated) - { - WSADATA wsaData; - WORD wVersionRequested = MAKEWORD( 2, 2 ); - WebRtc_Word32 err = WSAStartup( wVersionRequested, &wsaData); - if (err != 0) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - id, - "UdpSocketWrapper::CreateSocket failed to initialize sockets\ - WSAStartup error:%d", - err); - return NULL; - } - - _initiated = true; - } - -#ifdef USE_WINSOCK2 - s = new UdpSocket2Windows(id, mgr, ipV6Enable, disableGQOS); -#else - #pragma message("Error: No non-Winsock2 implementation for WinCE") - s = new UdpSocketWindows(id, mgr, ipV6Enable); -#endif - -#else - if (!_initiated) - { - _initiated = true; - } - s = new UdpSocketLinux(id, mgr, ipV6Enable); - if (s) - { - UdpSocketLinux* sl = static_cast(s); - if (sl->GetFd() != INVALID_SOCKET && sl->GetFd() < FD_SETSIZE) - { - // ok - } else - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - id, - "UdpSocketWrapper::CreateSocket failed to initialize socket"); - delete s; - s = NULL; - } - } -#endif - if (s) - { - s->_deleteEvent = NULL; - if (!s->SetCallback(obj, cb)) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - id, - "UdpSocketWrapper::CreateSocket failed to ser callback"); - return(NULL); - } - } - return s; -} - -bool UdpSocketWrapper::StartReceiving() -{ - _wantsIncoming = true; - return true; -} - -bool UdpSocketWrapper::StopReceiving() -{ - _wantsIncoming = false; - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_socket_wrapper.h b/modules/udp_transport/source/udp_socket_wrapper.h deleted file mode 100644 index 9a3b585d6..000000000 --- a/modules/udp_transport/source/udp_socket_wrapper.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WRAPPER_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WRAPPER_H_ - -#include "udp_transport.h" - -namespace webrtc { -class EventWrapper; -class UdpSocketManager; - -#define SOCKET_ERROR_NO_QOS -1000 - -#ifndef _WIN32 -typedef int SOCKET; -#endif - -#ifndef INVALID_SOCKET -#define INVALID_SOCKET (SOCKET)(~0) - -#ifndef AF_INET -#define AF_INET 2 -#endif - -#endif - -typedef void* CallbackObj; -typedef void(*IncomingSocketCallback)(CallbackObj obj, const WebRtc_Word8* buf, - WebRtc_Word32 len, - const SocketAddress* from); - -class UdpSocketWrapper -{ -public: - virtual ~UdpSocketWrapper(); - static UdpSocketWrapper* CreateSocket(const WebRtc_Word32 id, - UdpSocketManager* mgr, - CallbackObj obj, - IncomingSocketCallback cb, - bool ipV6Enable = false, - bool disableGQOS = false); - - // Set the unique identifier of this class to id. - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - // Register cb for receiving callbacks when there are incoming packets. - // Register obj so that it will be passed in calls to cb. - virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb) = 0; - - // Socket to local address specified by name. - virtual bool Bind(const SocketAddress& name) = 0; - - // Start receiving UDP data. - virtual bool StartReceiving(); - virtual inline bool StartReceiving(const WebRtc_UWord32 /*receiveBuffers*/) - {return StartReceiving();} - // Stop receiving UDP data. - virtual bool StopReceiving(); - - virtual bool ValidHandle() = 0; - - // Set socket options. - virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, - const WebRtc_Word8* optval, - WebRtc_Word32 optlen) = 0; - - // Set TOS for outgoing packets. - virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType) = 0; - - // Set 802.1Q PCP field (802.1p) for outgoing VLAN traffic. - virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 /*pcp*/) {return -1;} - - // Send buf of length len to the address specified by to. - virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, - const SocketAddress& to) = 0; - - virtual void SetEventToNull(); - - // Close socket and don't return until completed. - virtual void CloseBlocking() {} - - // tokenRate is in bit/s. peakBandwidt is in byte/s - virtual bool SetQos(WebRtc_Word32 serviceType, WebRtc_Word32 tokenRate, - WebRtc_Word32 bucketSize, WebRtc_Word32 peekBandwith, - WebRtc_Word32 minPolicedSize, WebRtc_Word32 maxSduSize, - const SocketAddress &stRemName, - WebRtc_Word32 overrideDSCP = 0) = 0; - - virtual WebRtc_UWord32 ReceiveBuffers() {return 0;}; - -protected: - UdpSocketWrapper(); - - bool _wantsIncoming; - EventWrapper* _deleteEvent; - -private: - static bool _initiated; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_SOCKET_WRAPPER_H_ diff --git a/modules/udp_transport/source/udp_transport.gyp b/modules/udp_transport/source/udp_transport.gyp deleted file mode 100644 index e193cad8f..000000000 --- a/modules/udp_transport/source/udp_transport.gyp +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'udp_transport', - 'type': '<(library)', - 'dependencies': [ - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../interface', - ], - }, - 'sources': [ - # PLATFORM INDEPENDENT SOURCE FILES - '../interface/udp_transport.h', - 'udp_transport_impl.cc', - 'udp_socket_wrapper.cc', - 'udp_socket_manager_wrapper.cc', - 'udp_transport_impl.h', - 'udp_socket_wrapper.h', - 'udp_socket_manager_wrapper.h', - # PLATFORM SPECIFIC SOURCE FILES - Will be filtered below - # Linux/Mac - 'udp_socket_linux.cc', - 'udp_socket_linux.h', - 'udp_socket_manager_linux.cc', - 'udp_socket_manager_linux.h', - # Windows - 'udp_socket_manager_windows.cc', - 'udp_socket_manager_windows.h', - 'udp_socket2_manager_windows.cc', - 'udp_socket2_manager_windows.h', - 'udp_socket_windows.cc', - 'udp_socket_windows.h', - 'udp_socket2_windows.cc', - 'udp_socket2_windows.h', - 'traffic_control_windows.cc', - 'traffic_control_windows.h', - ], # source - 'conditions': [ - # DEFINE PLATFORM SPECIFIC SOURCE FILES - ['OS!="linux" and OS!="mac"', { - 'sources!': [ - 'udp_socket_linux.cc', - 'udp_socket_linux.h', - 'udp_socket_manager_linux.cc', - 'udp_socket_manager_linux.h', - ], - }], - ['OS!="win"', { - 'sources!': [ - 'udp_socket_manager_windows.cc', - 'udp_socket_manager_windows.h', - 'udp_socket2_manager_windows.cc', - 'udp_socket2_manager_windows.h', - 'udp_socket_windows.cc', - 'udp_socket_windows.h', - 'udp_socket2_windows.cc', - 'udp_socket2_windows.h', - 'traffic_control_windows.cc', - 'traffic_control_windows.h', - ], - }], - ['OS=="linux"', { - 'cflags': [ - '-fno-strict-aliasing', - ], - }], - ['OS=="mac"', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': '-fno-strict-aliasing', - }, - }], - ['OS=="win"', { - 'defines': [ - 'USE_WINSOCK2', - ], - }], - ] # conditions - }, - ], # targets -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/udp_transport/source/udp_transport_impl.cc b/modules/udp_transport/source/udp_transport_impl.cc deleted file mode 100644 index 18571a42e..000000000 --- a/modules/udp_transport/source/udp_transport_impl.cc +++ /dev/null @@ -1,3097 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "udp_transport_impl.h" - -#include -#include -#include -#include - -#if defined(_WIN32) - #include - #include - // Disable warning for default initialized arrays on VS2005 - #pragma warning(disable:4351) -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#ifndef MAC_IPHONE - #include -#endif -#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - -#if defined(WEBRTC_MAC) -#include -#include -#endif -#if defined(WEBRTC_LINUX) -#include -#include -#endif - -#include "common_types.h" -#include "critical_section_wrapper.h" -#include "rw_lock_wrapper.h" -#include "trace.h" -#include "typedefs.h" -#include "udp_socket_manager_wrapper.h" - -#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) -#define GetLastError() errno - -#define IFRSIZE ((int)(size * sizeof (struct ifreq))) - -#define NLMSG_OK_NO_WARNING(nlh,len) \ - ((len) >= (int)sizeof(struct nlmsghdr) && \ - (int)(nlh)->nlmsg_len >= (int)sizeof(struct nlmsghdr) && \ - (int)(nlh)->nlmsg_len <= (len)) - -WebRtc_UWord32 timeGetTime() -{ - struct timeval tv; - struct timezone tz; - WebRtc_UWord32 val; - gettimeofday(&tv, &tz); - val= tv.tv_sec*1000+ tv.tv_usec/1000; - return(val); -} -#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - -namespace webrtc { -UdpTransport* UdpTransport::Create(const WebRtc_Word32 id, - WebRtc_UWord8& numSocketThreads) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, id, - "Create(numSocketThreads:%d)",numSocketThreads); - return new UdpTransportImpl(id, numSocketThreads); -} - -void UdpTransport::Destroy(UdpTransport* module) -{ - if(module) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, - static_cast(module)->Id(), "Destroy"); - delete module; - } -} - -UdpTransportImpl::UdpTransportImpl(const WebRtc_Word32 id, - WebRtc_UWord8& numSocketThreads) - : _id(id), - _crit(CriticalSectionWrapper::CreateCriticalSection()), - _critFilter(CriticalSectionWrapper::CreateCriticalSection()), - _critPacketCallback(CriticalSectionWrapper::CreateCriticalSection()), - _mgr(UdpSocketManager::Create(id, numSocketThreads)), - _lastError(kNoSocketError), - _destPort(0), - _destPortRTCP(0), - _localPort(0), - _localPortRTCP(0), - _srcPort(0), - _srcPortRTCP(0), - _fromPort(0), - _fromPortRTCP(0), - _fromIP(), - _destIP(), - _localIP(), - _localMulticastIP(), - _ptrRtpSocket(NULL), - _ptrRtcpSocket(NULL), - _ptrSendRtpSocket(NULL), - _ptrSendRtcpSocket(NULL), - _remoteRTPAddr(), - _remoteRTCPAddr(), - _localRTPAddr(), - _localRTCPAddr(), - _tos(0), - _receiving(false), - _useSetSockOpt(false), - _qos(false), - _pcp(0), - _IpV6EnabledRead(false), - _ipV6Enabled(false), - _serviceType(0), - _overrideDSCP(0), - _maxBitrate(0), - _cachLock(RWLockWrapper::CreateRWLock()), - _previousAddress(), - _previousIP(), - _previousIPSize(0), - _previousSourcePort(0), - _filterIPAddress(), - _rtpFilterPort(0), - _rtcpFilterPort(0), - _packetCallback(0) -{ - memset(&_remoteRTPAddr, 0, sizeof(_remoteRTPAddr)); - memset(&_remoteRTCPAddr, 0, sizeof(_remoteRTCPAddr)); - memset(&_localRTPAddr, 0, sizeof(_localRTPAddr)); - memset(&_localRTCPAddr, 0, sizeof(_localRTCPAddr)); - - memset(_fromIP, 0, sizeof(_fromIP)); - memset(_destIP, 0, sizeof(_destIP)); - memset(_localIP, 0, sizeof(_localIP)); - memset(_localMulticastIP, 0, sizeof(_localMulticastIP)); - - memset(&_filterIPAddress, 0, sizeof(_filterIPAddress)); - if(_mgr == NULL) - { - _mgr = UdpSocketManager::Create(id, numSocketThreads); - } - - WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, "%s created", __FUNCTION__); -} - -UdpTransportImpl::~UdpTransportImpl() -{ - CloseSendSockets(); - CloseReceiveSockets(); - delete _crit; - delete _critFilter; - delete _critPacketCallback; - delete _cachLock; - - UdpSocketManager::Return(); - WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "%s deleted", - __FUNCTION__); -} - -WebRtc_Word32 UdpTransportImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, - "ChangeUniqueId(new id:%d)", id); - - CriticalSectionScoped cs(*_crit); - _id = id; - if(_mgr) - { - _mgr->ChangeUniqueId(id); - } - if(_ptrRtpSocket) - { - _ptrRtpSocket->ChangeUniqueId(id); - } - if(_ptrRtcpSocket) - { - _ptrRtcpSocket->ChangeUniqueId(id); - } - if(_ptrSendRtpSocket) - { - _ptrSendRtpSocket->ChangeUniqueId(id); - } - if(_ptrSendRtcpSocket) - { - _ptrSendRtcpSocket->ChangeUniqueId(id); - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(version == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Version pointer is NULL"); - return -1; - } - WebRtc_Word8 ourVersion[256] = "UdpTransport 1.1.0"; - WebRtc_Word32 ourLength = (WebRtc_Word32)strlen(ourVersion); - if((WebRtc_Word32)remainingBufferInBytes < ourLength +1) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Version buffer not long enough"); - return -1; - } - memcpy(version, ourVersion, ourLength); - version[ourLength] = 0; - position += ourLength; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::TimeUntilNextProcess() -{ - return 100; -} - -WebRtc_Word32 UdpTransportImpl::Process() -{ - return 0; -} - -UdpTransport::ErrorCode UdpTransportImpl::LastError() const -{ - return _lastError; -} - -bool SameAddress(const SocketAddress& address1, const SocketAddress& address2) -{ - return (memcmp(&address1,&address2,sizeof(address1)) == 0); -} - -void UdpTransportImpl::GetCachedAddress(WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort) -{ - const WebRtc_UWord32 originalIPSize = ipSize; - // If the incoming string is too small, fill it as much as there is room - // for. Make sure that there is room for the '\0' character. - ipSize = (ipSize - 1 < _previousIPSize) ? ipSize - 1 : _previousIPSize; - memcpy(ip,_previousIP,sizeof(WebRtc_Word8)*(ipSize + 1)); - ip[originalIPSize - 1] = '\0'; - sourcePort = _previousSourcePort; -} - -WebRtc_Word32 UdpTransportImpl::IPAddressCached(const SocketAddress& address, - WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort) -{ - { - ReadLockScoped rl(*_cachLock); - // Check if the old address can be re-used (is the same). - if(SameAddress(address,_previousAddress)) - { - GetCachedAddress(ip,ipSize,sourcePort); - return 0; - } - } - // Get the new address and store it. - WriteLockScoped wl(*_cachLock); - ipSize = kIpAddressVersion6Length; - if(IPAddress(address,_previousIP,ipSize,_previousSourcePort) != 0) - { - return -1; - } - _previousIPSize = ipSize; - memcpy(&_previousAddress, &address, sizeof(address)); - // Address has been cached at this point. - GetCachedAddress(ip,ipSize,sourcePort); - return 0; -} - -WebRtc_Word32 UdpTransportImpl::InitializeReceiveSockets( - UdpTransportData* const packetCallback, - const WebRtc_UWord16 portnr, - const WebRtc_Word8* ip, - const WebRtc_Word8* multicastIpAddr, - const WebRtc_UWord16 rtcpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - { - CriticalSectionScoped cs(*_critPacketCallback); - _packetCallback = packetCallback; - - if(packetCallback == NULL) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, - "Closing down receive sockets"); - return 0; - } - } - - CriticalSectionScoped cs(*_crit); - CloseReceiveSockets(); - - if(portnr == 0) - { - // TODO (hellner): why not just fail here? - if(_destPort == 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets port 0 not allowed"); - _lastError = kPortInvalid; - return -1; - } - _localPort = _destPort; - } else { - _localPort = portnr; - } - if(rtcpPort) - { - _localPortRTCP = rtcpPort; - }else { - _localPortRTCP = _localPort + 1; - WEBRTC_TRACE( - kTraceStateInfo, - kTraceTransport, - _id, - "InitializeReceiveSockets RTCP port not configured using RTP\ - port+1=%d", - _localPortRTCP); - } - - if(ip) - { - if(IsIpAddressValid(ip,IpV6Enabled())) - { - strncpy(_localIP, ip,kIpAddressVersion6Length); - } else - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets invalid IP address"); - _lastError = kIpAddressInvalid; - return -1; - } - }else - { - // Don't bind to a specific IP address. - if(! IpV6Enabled()) - { - strncpy(_localIP, "0.0.0.0",16); - } else - { - strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", - kIpAddressVersion6Length); - } - } - if(multicastIpAddr && !IpV6Enabled()) - { - if(IsIpAddressValid(multicastIpAddr,IpV6Enabled())) - { - strncpy(_localMulticastIP, multicastIpAddr, - kIpAddressVersion6Length); - } else - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets invalid IP address"); - _lastError = kIpAddressInvalid; - return -1; - } - } - if(_mgr == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets no socket manager"); - return -1; - } - - _useSetSockOpt=false; - _tos=0; - _pcp=0; - - _ptrRtpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, this, - IncomingRTPCallback, - IpV6Enabled()); - - _ptrRtcpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, this, - IncomingRTCPCallback, - IpV6Enabled()); - - ErrorCode retVal = BindLocalRTPSocket(); - if(retVal != kNoSocketError) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets faild to bind RTP socket"); - _lastError = retVal; - CloseReceiveSockets(); - return -1; - } - retVal = BindLocalRTCPSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeReceiveSockets faild to bind RTCP socket"); - CloseReceiveSockets(); - return -1; - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::ReceiveSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort, - WebRtc_Word8 multicastIpAddr[kIpAddressVersion6Length]) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - rtpPort = _localPort; - rtcpPort = _localPortRTCP; - if (ipAddr) - { - strncpy(ipAddr, _localIP, IpV6Enabled() ? - UdpTransport::kIpAddressVersion6Length : - UdpTransport::kIpAddressVersion4Length); - } - if (multicastIpAddr) - { - strncpy(multicastIpAddr, _localMulticastIP, IpV6Enabled() ? - UdpTransport::kIpAddressVersion6Length : - UdpTransport::kIpAddressVersion4Length); - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SendSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - rtpPort = _destPort; - rtcpPort = _destPortRTCP; - strncpy(ipAddr, _destIP, IpV6Enabled() ? - UdpTransport::kIpAddressVersion6Length : - UdpTransport::kIpAddressVersion4Length); - return 0; -} - -WebRtc_Word32 UdpTransportImpl::RemoteSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - rtpPort = _fromPort; - rtcpPort = _fromPortRTCP; - if(ipAddr) - { - strncpy(ipAddr, _fromIP, IpV6Enabled() ? - kIpAddressVersion6Length : - kIpAddressVersion4Length); - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::FilterPorts( - WebRtc_UWord16& rtpFilterPort, - WebRtc_UWord16& rtcpFilterPort) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_critFilter); - rtpFilterPort = _rtpFilterPort; - rtcpFilterPort = _rtcpFilterPort; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SetQoS(bool QoS, WebRtc_Word32 serviceType, - WebRtc_UWord32 maxBitrate, - WebRtc_Word32 overrideDSCP, bool audio) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(QoS) - { - return EnableQoS(serviceType, audio, maxBitrate, overrideDSCP); - }else - { - return DisableQoS(); - } -} - -WebRtc_Word32 UdpTransportImpl::EnableQoS(WebRtc_Word32 serviceType, - bool audio, WebRtc_UWord32 maxBitrate, - WebRtc_Word32 overrideDSCP) -{ - if (_ipV6Enabled) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but will be ignored since IPv6 is enabled"); - _lastError = kQosError; - return -1; - } - if (_tos) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "TOS already enabled, can't use TOS and QoS at the same time"); - _lastError = kQosError; - return -1; - } - if (_pcp) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "PCP already enabled, can't use PCP and QoS at the same time"); - _lastError = kQosError; - return -1; - } - if(_destPort == 0) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but not started since we have not yet configured\ - the send destination"); - return -1; - } - if(_qos) - { - if(_overrideDSCP == 0 && overrideDSCP != 0) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is already enabled and overrideDSCP differs, not allowed"); - return -1; - } - } - CriticalSectionScoped cs(*_crit); - - UdpSocketWrapper* rtpSock = _ptrSendRtpSocket ? - _ptrSendRtpSocket : - _ptrRtpSocket; - if (!rtpSock || !rtpSock->ValidHandle()) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but not started since we have not yet created the\ - RTP socket"); - return -1; - } - UdpSocketWrapper* rtcpSock = _ptrSendRtcpSocket ? - _ptrSendRtcpSocket : - _ptrRtcpSocket; - if (!rtcpSock || !rtcpSock->ValidHandle()) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but not started since we have not yet created the\ - RTCP socket"); - return -1; - } - - // Minimum packet size in bytes for which the requested quality of service - // will be provided. The smallest RTP header is 12 byte. - const WebRtc_Word32 min_policed_size = 12; - // Max SDU, maximum packet size permitted or used in the traffic flow, in - // bytes. - const WebRtc_Word32 max_sdu_size = 1500; - - // Enable QoS for RTP sockets. - if(maxBitrate) - { - // Note: 1 kbit is 125 bytes. - // Token Rate is typically set to the average bit rate from peak to - // peak. - // Bucket size is normally set to the largest average frame size. - if(audio) - { - WEBRTC_TRACE(kTraceStateInfo, - kTraceTransport, - _id, - "Enable QOS for audio with max bitrate:%d", - maxBitrate); - - const WebRtc_Word32 token_rate = maxBitrate*125; - // The largest audio packets are 60ms frames. This is a fraction - // more than 16 packets/second. These 16 frames are sent, at max, - // at a bitrate of maxBitrate*125 -> 1 frame is maxBitrate*125/16 ~ - // maxBitrate * 8. - const WebRtc_Word32 bucket_size = maxBitrate * 8; - const WebRtc_Word32 peek_bandwith = maxBitrate * 125; - if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, - max_sdu_size, _remoteRTPAddr, overrideDSCP)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS failed on the RTP socket"); - _lastError = kQosError; - return -1; - } - }else - { - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, - "Enable QOS for video with max bitrate:%d", - maxBitrate); - - // Allow for a token rate that is twice that of the maximum bitrate - // (in bytes). - const WebRtc_Word32 token_rate = maxBitrate*250; - // largest average frame size (key frame size). Assuming that a - // keyframe is 25% of the bitrate during the second its sent - // Assume that a key frame is 25% of the bitrate the second that it - // is sent. The largest frame size is then maxBitrate* 125 * 0.25 ~ - // 31. - const WebRtc_Word32 bucket_size = maxBitrate*31; - const WebRtc_Word32 peek_bandwith = maxBitrate*125; - if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, max_sdu_size, - _remoteRTPAddr, overrideDSCP)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS failed on the RTP socket"); - _lastError = kQosError; - return -1; - } - } - } else if(audio) - { - // No max bitrate set. Audio. - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, - "Enable QOS for audio with default max bitrate"); - - // Let max bitrate be 240kbit/s. - const WebRtc_Word32 token_rate = 30000; - const WebRtc_Word32 bucket_size = 2000; - const WebRtc_Word32 peek_bandwith = 30000; - if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, max_sdu_size, - _remoteRTPAddr, overrideDSCP)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS failed on the RTP socket"); - _lastError = kQosError; - return -1; - } - }else - { - // No max bitrate set. Video. - WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, - "Enable QOS for video with default max bitrate"); - - // Let max bitrate be 10mbit/s. - const WebRtc_Word32 token_rate = 128000*10; - const WebRtc_Word32 bucket_size = 32000; - const WebRtc_Word32 peek_bandwith = 256000; - if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, max_sdu_size, - _remoteRTPAddr, overrideDSCP)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "QOS failed on the RTP socket"); - _lastError = kQosError; - return -1; - } - } - - // Enable QoS for RTCP sockets. - // TODO (hellner): shouldn't RTCP be based on 5% of the maximum bandwidth? - if(audio) - { - const WebRtc_Word32 token_rate = 200; - const WebRtc_Word32 bucket_size = 200; - const WebRtc_Word32 peek_bandwith = 400; - if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, max_sdu_size, - _remoteRTCPAddr, overrideDSCP)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "QOS failed on the RTCP socket"); - _lastError = kQosError; - } - }else - { - const WebRtc_Word32 token_rate = 5000; - const WebRtc_Word32 bucket_size = 100; - const WebRtc_Word32 peek_bandwith = 10000; - if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size, - peek_bandwith, min_policed_size, max_sdu_size, - _remoteRTCPAddr, _overrideDSCP)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "QOS failed on the RTCP socket"); - _lastError = kQosError; - } - } - _qos = true; - _serviceType = serviceType; - _maxBitrate = maxBitrate; - _overrideDSCP = overrideDSCP; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::DisableQoS() -{ - if(_qos == false) - { - return 0; - } - CriticalSectionScoped cs(*_crit); - - UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ? - _ptrSendRtpSocket : _ptrRtpSocket); - if (!rtpSock || !rtpSock->ValidHandle()) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but not started since we have not yet created the\ - RTP socket"); - return -1; - } - UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ? - _ptrSendRtcpSocket : _ptrRtcpSocket); - if (!rtcpSock || !rtcpSock->ValidHandle()) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "QOS is enabled but not started since we have not yet created the\ - RTCP socket"); - return -1; - } - - const WebRtc_Word32 service_type = 0; // = SERVICETYPE_NOTRAFFIC - const WebRtc_Word32 not_specified = -1; - if (!rtpSock->SetQos(service_type, not_specified, not_specified, - not_specified, not_specified, not_specified, - _remoteRTPAddr, _overrideDSCP)) - { - _lastError = kQosError; - return -1; - } - if (!rtcpSock->SetQos(service_type, not_specified, not_specified, - not_specified, not_specified, not_specified, - _remoteRTCPAddr,_overrideDSCP)) - { - _lastError = kQosError; - } - _qos = false; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::QoS(bool& QoS, WebRtc_Word32& serviceType, - WebRtc_Word32& overrideDSCP) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - QoS = _qos; - serviceType = _serviceType; - overrideDSCP = _overrideDSCP; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SetToS(WebRtc_Word32 DSCP, bool useSetSockOpt) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if (_qos) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled"); - _lastError = kQosError; - return -1; - } - if (DSCP < 0 || DSCP > 63) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid DSCP"); - _lastError = kTosInvalid; - return -1; - } - if(_tos) - { - if(useSetSockOpt != _useSetSockOpt) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "Can't switch SetSockOpt method without disabling TOS first"); - _lastError = kTosInvalid; - return -1; - } - } - CriticalSectionScoped cs(*_crit); - UdpSocketWrapper* rtpSock = NULL; - UdpSocketWrapper* rtcpSock = NULL; - if(_ptrSendRtpSocket) - { - rtpSock = _ptrSendRtpSocket; - }else - { - rtpSock = _ptrRtpSocket; - } - if (rtpSock == NULL) - { - _lastError = kSocketInvalid; - return -1; - } - if(!rtpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - if(_ptrSendRtcpSocket) - { - rtcpSock = _ptrSendRtcpSocket; - }else - { - rtcpSock = _ptrRtcpSocket; - } - if (rtcpSock == NULL) - { - _lastError = kSocketInvalid; - return -1; - } - if(!rtcpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - - if (useSetSockOpt) - { -#ifdef _WIN32 - OSVERSIONINFO OsVersion; - OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&OsVersion); - // Disable QoS before setting ToS on Windows XP. This is done by closing - // and re-opening the sockets. - // TODO (hellner): why not just fail here and force the user to - // re-initialize sockets? Doing this may trick the user - // into thinking that the sockets are in a state which - // they aren't. - if (OsVersion.dwMajorVersion == 5 && - OsVersion.dwMinorVersion == 1) - { - if(!_useSetSockOpt) - { - if(_ptrSendRtpSocket) - { - CloseSendSockets(); - _ptrSendRtpSocket = - UdpSocketWrapper::CreateSocket(_id, _mgr, NULL, - NULL, IpV6Enabled(), - true); - _ptrSendRtcpSocket = - UdpSocketWrapper::CreateSocket(_id, _mgr, NULL, - NULL, IpV6Enabled(), - true); - rtpSock=_ptrSendRtpSocket; - rtcpSock=_ptrSendRtcpSocket; - ErrorCode retVal = BindRTPSendSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - retVal = BindRTCPSendSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - } - else - { - bool receiving=_receiving; - WebRtc_UWord32 noOfReceiveBuffers = 0; - if(receiving) - { - noOfReceiveBuffers=_ptrRtpSocket->ReceiveBuffers(); - if(StopReceiving()!=0) - { - return -1; - } - } - CloseReceiveSockets(); - _ptrRtpSocket = UdpSocketWrapper::CreateSocket( - _id, _mgr, this, IncomingRTPCallback, - IpV6Enabled(), true); - _ptrRtcpSocket = UdpSocketWrapper::CreateSocket( - _id, _mgr, this, IncomingRTCPCallback, - IpV6Enabled(),true); - rtpSock=_ptrRtpSocket; - rtcpSock=_ptrRtcpSocket; - ErrorCode retVal = BindLocalRTPSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - retVal = BindLocalRTCPSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - if(receiving) - { - if(StartReceiving(noOfReceiveBuffers) != - kNoSocketError) - { - return -1; - } - } - } - } - } -#endif // #ifdef _WIN32 - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Setting TOS using SetSockopt"); - WebRtc_Word32 TOSShifted = DSCP << 2; - if (!rtpSock->SetSockopt(IPPROTO_IP, IP_TOS, - (WebRtc_Word8*) &TOSShifted, 4)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not SetSockopt tos value on RTP socket"); - _lastError = kTosInvalid; - return -1; - } - if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_TOS, - (WebRtc_Word8*) &TOSShifted, 4)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not sSetSockopt tos value on RTCP socket"); - _lastError = kTosInvalid; - return -1; - } - } else - { - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, - "Setting TOS NOT using SetSockopt"); - if (rtpSock->SetTOS(DSCP) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not set tos value on RTP socket"); - _lastError = kTosError; - return -1; - } - if (rtcpSock->SetTOS(DSCP) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not set tos value on RTCP socket"); - _lastError = kTosError; - return -1; - } - } - _useSetSockOpt = useSetSockOpt; - _tos = DSCP; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::ToS(WebRtc_Word32& DSCP, - bool& useSetSockOpt) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - DSCP = _tos; - useSetSockOpt = _useSetSockOpt; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SetPCP(WebRtc_Word32 PCP) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - if (_qos) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled"); - _lastError = kQosError; - return -1; - } - if ((PCP < 0) || (PCP > 7)) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid PCP"); - _lastError = kPcpError; - return -1; - } - - CriticalSectionScoped cs(*_crit); - UdpSocketWrapper* rtpSock = NULL; - UdpSocketWrapper* rtcpSock = NULL; - if(_ptrSendRtpSocket) - { - rtpSock = _ptrSendRtpSocket; - }else - { - rtpSock = _ptrRtpSocket; - } - if (rtpSock == NULL) - { - _lastError = kSocketInvalid; - return -1; - } - if(!rtpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - if(_ptrSendRtcpSocket) - { - rtcpSock = _ptrSendRtcpSocket; - }else - { - rtcpSock = _ptrRtcpSocket; - } - if (rtcpSock == NULL) - { - _lastError = kSocketInvalid; - return -1; - } - if(!rtcpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - -#if defined(_WIN32) - if (rtpSock->SetPCP(PCP) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not set PCP value on RTP socket"); - _lastError = kPcpError; - return -1; - } - if (rtcpSock->SetPCP(PCP) != 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not set PCP value on RTCP socket"); - _lastError = kPcpError; - return -1; - } - -#elif defined(WEBRTC_LINUX) - if (!rtpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (WebRtc_Word8*) &PCP, - sizeof(PCP))) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not SetSockopt PCP value on RTP socket"); - _lastError = kPcpError; - return -1; - } - if (!rtcpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (WebRtc_Word8*) &PCP, - sizeof(PCP))) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Could not SetSockopt PCP value on RTCP socket"); - _lastError = kPcpError; - return -1; - } -#else - // Not supported on other platforms (WEBRTC_MAC) - _lastError = kPcpError; - return -1; -#endif - _pcp = PCP; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::PCP(WebRtc_Word32& PCP) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - PCP = _pcp; - return 0; -} - -bool UdpTransportImpl::SetSockOptUsed() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - return _useSetSockOpt; -} - -WebRtc_Word32 UdpTransportImpl::EnableIpV6() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - if(_IpV6EnabledRead) - { - if(_ipV6Enabled) - { - return 0; - }else { - _lastError = kIpVersion6Error; - return -1; - } - } - _ipV6Enabled=true; - return 0; -} - -WebRtc_Word32 UdpTransportImpl::FilterIP( - WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - if(filterIPAddress == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "FilterIP: Invalid argument"); - return -1; - } - if(_filterIPAddress._sockaddr_storage.sin_family == 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "No Filter configured"); - return -1; - } - CriticalSectionScoped cs(*_critFilter); - WebRtc_UWord32 ipSize = kIpAddressVersion6Length; - WebRtc_UWord16 sourcePort; - return IPAddress(_filterIPAddress, filterIPAddress, ipSize, sourcePort); -} - -WebRtc_Word32 UdpTransportImpl::SetFilterIP( - const WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(filterIPAddress == NULL) - { - memset(&_filterIPAddress, 0, sizeof(_filterIPAddress)); - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP reset"); - return 0; - } - CriticalSectionScoped cs(*_critFilter); - if (_ipV6Enabled) - { - _filterIPAddress._sockaddr_storage.sin_family = AF_INET6; - - if (InetPresentationToNumeric( - AF_INET6, - filterIPAddress, - &_filterIPAddress._sockaddr_in6.sin6_addr) < 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Failed to set\ - filter IP for IPv6"); - _lastError = FILTER_ERROR; - return -1; - } - } - else - { - _filterIPAddress._sockaddr_storage.sin_family = AF_INET; - - if(InetPresentationToNumeric( - AF_INET, - filterIPAddress, - &_filterIPAddress._sockaddr_in.sin_addr) < 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to set filter IP for IPv4"); - _lastError = FILTER_ERROR; - return -1; - } - } - WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP set"); - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SetFilterPorts(WebRtc_UWord16 rtpFilterPort, - WebRtc_UWord16 rtcpFilterPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_critFilter); - _rtpFilterPort = rtpFilterPort; - _rtcpFilterPort = rtcpFilterPort; - return 0; -} - -bool UdpTransportImpl::SendSocketsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - if(_ptrSendRtpSocket) - { - return true; - } - if(_destPort !=0) - { - return true; - } - return false; -} - -bool UdpTransportImpl::ReceiveSocketsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(_ptrRtpSocket) - { - return true; - } - return false; -} - -bool UdpTransportImpl::SourcePortsInitialized() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(_ptrSendRtpSocket) - { - return true; - } - return false; -} - -bool UdpTransportImpl::IpV6Enabled() const -{ - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__); - _IpV6EnabledRead=true; - return _ipV6Enabled; -} - -void UdpTransportImpl::BuildRemoteRTPAddr() -{ - if(_ipV6Enabled) - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _remoteRTPAddr.sin_length = 0; - _remoteRTPAddr.sin_family = PF_INET6; -#else - _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET6; -#endif - - _remoteRTPAddr._sockaddr_in6.sin6_flowinfo=0; - _remoteRTPAddr._sockaddr_in6.sin6_scope_id=0; - _remoteRTPAddr._sockaddr_in6.sin6_port = Htons(_destPort); - InetPresentationToNumeric(AF_INET6,_destIP, - &_remoteRTPAddr._sockaddr_in6.sin6_addr); - } else - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _remoteRTPAddr.sin_length = 0; - _remoteRTPAddr.sin_family = PF_INET; -#else - _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET; -#endif - _remoteRTPAddr._sockaddr_in.sin_port = Htons(_destPort); - _remoteRTPAddr._sockaddr_in.sin_addr = InetAddrIPV4(_destIP); - } -} - -void UdpTransportImpl::BuildRemoteRTCPAddr() -{ - if(_ipV6Enabled) - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _remoteRTCPAddr.sin_length = 0; - _remoteRTCPAddr.sin_family = PF_INET6; -#else - _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET6; -#endif - - _remoteRTCPAddr._sockaddr_in6.sin6_flowinfo=0; - _remoteRTCPAddr._sockaddr_in6.sin6_scope_id=0; - _remoteRTCPAddr._sockaddr_in6.sin6_port = Htons(_destPortRTCP); - InetPresentationToNumeric(AF_INET6,_destIP, - &_remoteRTCPAddr._sockaddr_in6.sin6_addr); - - } else - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _remoteRTCPAddr.sin_length = 0; - _remoteRTCPAddr.sin_family = PF_INET; -#else - _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET; -#endif - _remoteRTCPAddr._sockaddr_in.sin_port = Htons(_destPortRTCP); - _remoteRTCPAddr._sockaddr_in.sin_addr= InetAddrIPV4(_destIP); - } -} - -UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTPSendSocket() -{ - if(!_ptrSendRtpSocket) - { - return kSocketInvalid; - } - if(!_ptrSendRtpSocket->ValidHandle()) - { - return kIpAddressInvalid; - } - if(_ipV6Enabled) - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _localRTPAddr.sin_length = 0; - _localRTPAddr.sin_family = PF_INET6; -#else - _localRTPAddr._sockaddr_storage.sin_family = PF_INET6; -#endif - _localRTPAddr._sockaddr_in6.sin6_flowinfo=0; - _localRTPAddr._sockaddr_in6.sin6_scope_id=0; - _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] = - 0; // = INADDR_ANY - _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] = - 0; - _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] = - 0; - _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] = - 0; - _localRTPAddr._sockaddr_in6.sin6_port = Htons(_srcPort); - if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _srcPort); - return kFailedToBindPort; - } - } else { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _localRTPAddr.sin_length = 0; - _localRTPAddr.sin_family = PF_INET; -#else - _localRTPAddr._sockaddr_storage.sin_family = PF_INET; -#endif - _localRTPAddr._sockaddr_in.sin_addr = 0; - _localRTPAddr._sockaddr_in.sin_port = Htons(_srcPort); - if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _srcPort); - return kFailedToBindPort; - } - } - return kNoSocketError; -} - -UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTCPSendSocket() -{ - if(!_ptrSendRtcpSocket) - { - return kSocketInvalid; - } - - if(_ipV6Enabled) - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _localRTCPAddr.sin_length = 0; - _localRTCPAddr.sin_family = PF_INET6; -#else - _localRTCPAddr._sockaddr_storage.sin_family = PF_INET6; -#endif - _localRTCPAddr._sockaddr_in6.sin6_flowinfo=0; - _localRTCPAddr._sockaddr_in6.sin6_scope_id=0; - _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] = - 0; // = INADDR_ANY - _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] = - 0; - _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] = - 0; - _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] = - 0; - _localRTCPAddr._sockaddr_in6.sin6_port = Htons(_srcPortRTCP); - if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _srcPortRTCP); - return kFailedToBindPort; - } - } else { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - _localRTCPAddr.sin_length = 0; - _localRTCPAddr.sin_family = PF_INET; -#else - _localRTCPAddr._sockaddr_storage.sin_family = PF_INET; -#endif - _localRTCPAddr._sockaddr_in.sin_addr= 0; - _localRTCPAddr._sockaddr_in.sin_port = Htons(_srcPortRTCP); - if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _srcPortRTCP); - return kFailedToBindPort; - } - } - return kNoSocketError; -} - -UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTPSocket() -{ - if(!_ptrRtpSocket) - { - return kSocketInvalid; - } - if(!IpV6Enabled()) - { - SocketAddress recAddr; - memset(&recAddr, 0, sizeof(SocketAddress)); - recAddr._sockaddr_storage.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - recAddr.sin_length = 0; - recAddr.sin_family = PF_INET; -#else - recAddr._sockaddr_storage.sin_family = PF_INET; -#endif - recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP); - recAddr._sockaddr_in.sin_port = Htons(_localPort); - - if (!_ptrRtpSocket->Bind(recAddr)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _localPort); - return kFailedToBindPort; - } - } - else - { - SocketAddress stLclName; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - stLclName.sin_lenght = 0; - stLclName.sin_family = PF_INET6; -#else - stLclName._sockaddr_storage.sin_family = PF_INET6; -#endif - InetPresentationToNumeric(AF_INET6,_localIP, - &stLclName._sockaddr_in6.sin6_addr); - stLclName._sockaddr_in6.sin6_port = Htons(_localPort); - stLclName._sockaddr_in6.sin6_flowinfo = 0; - stLclName._sockaddr_in6.sin6_scope_id = 0; - - if (!_ptrRtpSocket->Bind(stLclName)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _localPort); - return kFailedToBindPort; - } - } - - if(_localMulticastIP[0] != 0) - { - // Join the multicast group from which to receive datagrams. - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP); - mreq.imr_interface.s_addr = INADDR_ANY; - - if (!_ptrRtpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP, - (WebRtc_Word8*)&mreq,sizeof (mreq))) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "setsockopt() for multicast failed, not closing socket"); - }else - { - WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id, - "multicast group successfully joined"); - } - } - return kNoSocketError; -} - -UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTCPSocket() -{ - if(!_ptrRtcpSocket) - { - return kSocketInvalid; - } - if(! IpV6Enabled()) - { - SocketAddress recAddr; - memset(&recAddr, 0, sizeof(SocketAddress)); -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - recAddr.sin_length = 0; - recAddr.sin_family = AF_INET; -#else - recAddr._sockaddr_storage.sin_family = AF_INET; -#endif - recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP); - recAddr._sockaddr_in.sin_port = Htons(_localPortRTCP); - - if (!_ptrRtcpSocket->Bind(recAddr)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _localPortRTCP); - return kFailedToBindPort; - } - } - else - { - SocketAddress stLclName; -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - stLclName.sin_length = 0; - stLclName.sin_family = PF_INET6; -#else - stLclName._sockaddr_storage.sin_family = PF_INET6; -#endif - stLclName._sockaddr_in6.sin6_flowinfo = 0; - stLclName._sockaddr_in6.sin6_scope_id = 0; - stLclName._sockaddr_in6.sin6_port = Htons(_localPortRTCP); - - InetPresentationToNumeric(AF_INET6,_localIP, - &stLclName._sockaddr_in6.sin6_addr); - if (!_ptrRtcpSocket->Bind(stLclName)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, - "Failed to bind to port:%d ", _localPortRTCP); - return kFailedToBindPort; - } - } - if(_localMulticastIP[0] != 0) - { - // Join the multicast group from which to receive datagrams. - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP); - mreq.imr_interface.s_addr = INADDR_ANY; - - if (!_ptrRtcpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP, - (WebRtc_Word8*)&mreq,sizeof (mreq))) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "setsockopt() for multicast failed, not closing socket"); - }else - { - WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id, - "multicast group successfully joined"); - } - } - return kNoSocketError; -} - -WebRtc_Word32 UdpTransportImpl::InitializeSourcePorts(WebRtc_UWord16 rtpPort, - WebRtc_UWord16 rtcpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - if(rtpPort == 0) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "InitializeSourcePorts port 0 not allowed"); - _lastError = kPortInvalid; - return -1; - } - - CriticalSectionScoped cs(*_crit); - - CloseSendSockets(); - - if(_mgr == NULL) - { - return -1; - } - - _srcPort = rtpPort; - if(rtcpPort == 0) - { - _srcPortRTCP = rtpPort+1; - } else - { - _srcPortRTCP = rtcpPort; - } - _useSetSockOpt =false; - _tos=0; - _pcp=0; - - _ptrSendRtpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, NULL, NULL, - IpV6Enabled()); - _ptrSendRtcpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, NULL, NULL, - IpV6Enabled()); - - ErrorCode retVal = BindRTPSendSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - retVal = BindRTCPSendSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - return -1; - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SourcePorts(WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const -{ - CriticalSectionScoped cs(*_crit); - - rtpPort = (_srcPort != 0) ? _srcPort : _localPort; - rtcpPort = (_srcPortRTCP != 0) ? _srcPortRTCP : _localPortRTCP; - return 0; -} - - -#ifdef _WIN32 -WebRtc_Word32 UdpTransportImpl::StartReceiving( - WebRtc_UWord32 numberOfSocketBuffers) -#else -WebRtc_Word32 UdpTransportImpl::StartReceiving( - WebRtc_UWord32 /*numberOfSocketBuffers*/) -#endif -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - if(_receiving) - { - return 0; - } - if(_ptrRtpSocket) - { -#ifdef _WIN32 - if(!_ptrRtpSocket->StartReceiving(numberOfSocketBuffers)) -#else - if(!_ptrRtpSocket->StartReceiving()) -#endif - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to start receive on RTP socket"); - _lastError = kStartReceiveError; - return -1; - } - } - if(_ptrRtcpSocket) - { - if(!_ptrRtcpSocket->StartReceiving()) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to start receive on RTCP socket"); - _lastError = kStartReceiveError; - return -1; - } - } - if( _ptrRtpSocket == NULL && - _ptrRtcpSocket == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to StartReceiving, no socket initialized"); - _lastError = kStartReceiveError; - return -1; - } - _receiving = true; - return 0; -} - -bool UdpTransportImpl::Receiving() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - return _receiving; -} - -WebRtc_Word32 UdpTransportImpl::StopReceiving() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - CriticalSectionScoped cs(*_crit); - - _receiving = false; - - if (_ptrRtpSocket) - { - if (!_ptrRtpSocket->StopReceiving()) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to stop receiving on RTP socket"); - _lastError = kStopReceiveError; - return -1; - } - } - if (_ptrRtcpSocket) - { - if (!_ptrRtcpSocket->StopReceiving()) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "Failed to stop receiving on RTCP socket"); - _lastError = kStopReceiveError; - return -1; - } - } - return 0; -} - -WebRtc_Word32 UdpTransportImpl::InitializeSendSockets( - const WebRtc_Word8* ipaddr, - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - { - CriticalSectionScoped cs(*_crit); - _destPort = rtpPort; - if(rtcpPort == 0) - { - _destPortRTCP = _destPort+1; - } else - { - _destPortRTCP = rtcpPort; - } - - if(ipaddr == NULL) - { - if (!IsIpAddressValid(_destIP, IpV6Enabled())) - { - _destPort = 0; - _destPortRTCP = 0; - _lastError = kIpAddressInvalid; - return -1; - } - } else - { - if (IsIpAddressValid(ipaddr, IpV6Enabled())) - { - strncpy( - _destIP, - ipaddr, - IpV6Enabled() ? kIpAddressVersion6Length : - kIpAddressVersion4Length); - } else { - _destPort = 0; - _destPortRTCP = 0; - _lastError = kIpAddressInvalid; - return -1; - } - } - BuildRemoteRTPAddr(); - BuildRemoteRTCPAddr(); - } - - if (_ipV6Enabled) - { - if (_qos) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceTransport, - _id, - "QOS is enabled but will be ignored since IPv6 is enabled"); - } - }else - { - // TODO (grunell): Multicast support is experimantal. - - // Put the first digit of the remote address in val. - WebRtc_Word32 val = ntohl(_remoteRTPAddr._sockaddr_in.sin_addr)>> 24; - - if((val > 223) && (val < 240)) - { - // Multicast address. - CriticalSectionScoped cs(*_crit); - - UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ? - _ptrSendRtpSocket : _ptrRtpSocket); - if (!rtpSock || !rtpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ? - _ptrSendRtcpSocket : _ptrRtcpSocket); - if (!rtcpSock || !rtcpSock->ValidHandle()) - { - _lastError = kSocketInvalid; - return -1; - } - - // Set Time To Live to same region - WebRtc_Word32 iOptVal = 64; - if (!rtpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL, - (WebRtc_Word8*)&iOptVal, - sizeof (WebRtc_Word32))) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "setsockopt for multicast error on RTP socket"); - _ptrRtpSocket->CloseBlocking(); - _ptrRtpSocket = NULL; - _lastError = kMulticastAddressInvalid; - return -1; - } - if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL, - (WebRtc_Word8*)&iOptVal, - sizeof (WebRtc_Word32))) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "setsockopt for multicast error on RTCP socket"); - _ptrRtpSocket->CloseBlocking(); - _ptrRtpSocket = NULL; - _lastError = kMulticastAddressInvalid; - return -1; - } - } - } - return 0; -} - -void UdpTransportImpl::BuildSockaddrIn(WebRtc_UWord16 portnr, - const WebRtc_Word8* ip, - SocketAddress& remoteAddr) const -{ - if(_ipV6Enabled) - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - remoteAddr.sin_length = 0; - remoteAddr.sin_family = PF_INET6; -#else - remoteAddr._sockaddr_storage.sin_family = PF_INET6; -#endif - remoteAddr._sockaddr_in6.sin6_port = Htons(portnr); - InetPresentationToNumeric(AF_INET6, ip, - &remoteAddr._sockaddr_in6.sin6_addr); - remoteAddr._sockaddr_in6.sin6_flowinfo=0; - remoteAddr._sockaddr_in6.sin6_scope_id=0; - } else - { -#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN - remoteAddr.sin_length = 0; - remoteAddr.sin_family = PF_INET; -#else - remoteAddr._sockaddr_storage.sin_family = PF_INET; -#endif - remoteAddr._sockaddr_in.sin_port = Htons(portnr); - remoteAddr._sockaddr_in.sin_addr= InetAddrIPV4( - const_cast(ip)); - } -} - -WebRtc_Word32 UdpTransportImpl::SendRaw(const WebRtc_Word8 *data, - WebRtc_UWord32 length, - WebRtc_Word32 isRTCP, - WebRtc_UWord16 portnr, - const WebRtc_Word8 *ip) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - if(isRTCP) - { - UdpSocketWrapper* rtcpSock = NULL; - if(_ptrSendRtcpSocket) - { - rtcpSock = _ptrSendRtcpSocket; - } else if(_ptrRtcpSocket) - { - rtcpSock = _ptrRtcpSocket; - } else - { - return -1; - } - if(portnr == 0 && ip == NULL) - { - return rtcpSock->SendTo(data,length,_remoteRTCPAddr); - - } else if(portnr != 0 && ip != NULL) - { - SocketAddress remoteAddr; - BuildSockaddrIn(portnr, ip, remoteAddr); - return rtcpSock->SendTo(data,length,remoteAddr); - } else if(ip != NULL) - { - SocketAddress remoteAddr; - BuildSockaddrIn(_destPortRTCP, ip, remoteAddr); - return rtcpSock->SendTo(data,length,remoteAddr); - } else - { - SocketAddress remoteAddr; - BuildSockaddrIn(portnr, _destIP, remoteAddr); - return rtcpSock->SendTo(data,length,remoteAddr); - } - } else { - UdpSocketWrapper* rtpSock = NULL; - if(_ptrSendRtpSocket) - { - rtpSock = _ptrSendRtpSocket; - - } else if(_ptrRtpSocket) - { - rtpSock = _ptrRtpSocket; - } else - { - return -1; - } - if(portnr == 0 && ip == NULL) - { - return rtpSock->SendTo(data,length,_remoteRTPAddr); - - } else if(portnr != 0 && ip != NULL) - { - SocketAddress remoteAddr; - BuildSockaddrIn(portnr, ip, remoteAddr); - return rtpSock->SendTo(data,length,remoteAddr); - } else if(ip != NULL) - { - SocketAddress remoteAddr; - BuildSockaddrIn(_destPort, ip, remoteAddr); - return rtpSock->SendTo(data,length,remoteAddr); - } else - { - SocketAddress remoteAddr; - BuildSockaddrIn(portnr, _destIP, remoteAddr); - return rtpSock->SendTo(data,length,remoteAddr); - } - } -} - -WebRtc_Word32 UdpTransportImpl::SendRTPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const SocketAddress& to) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - if(_ptrSendRtpSocket) - { - return _ptrSendRtpSocket->SendTo(data,length,to); - - } else if(_ptrRtpSocket) - { - return _ptrRtpSocket->SendTo(data,length,to); - } - return -1; -} - -WebRtc_Word32 UdpTransportImpl::SendRTCPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const SocketAddress& to) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - CriticalSectionScoped cs(*_crit); - - if(_ptrSendRtcpSocket) - { - return _ptrSendRtcpSocket->SendTo(data,length,to); - - } else if(_ptrRtcpSocket) - { - return _ptrRtcpSocket->SendTo(data,length,to); - } - return -1; -} - -WebRtc_Word32 UdpTransportImpl::SendRTPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const WebRtc_UWord16 rtpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - CriticalSectionScoped cs(*_crit); - // Use the current SocketAdress but update it with rtpPort. - SocketAddress to; - memcpy(&to, &_remoteRTPAddr, sizeof(SocketAddress)); - - if(_ipV6Enabled) - { - to._sockaddr_in6.sin6_port = Htons(rtpPort); - } else - { - to._sockaddr_in.sin_port = Htons(rtpPort); - } - - if(_ptrSendRtpSocket) - { - return _ptrSendRtpSocket->SendTo(data,length,to); - - } else if(_ptrRtpSocket) - { - return _ptrRtpSocket->SendTo(data,length,to); - } - return -1; -} - -WebRtc_Word32 UdpTransportImpl::SendRTCPPacketTo(const WebRtc_Word8* data, - WebRtc_UWord32 length, - const WebRtc_UWord16 rtcpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - - // Use the current SocketAdress but update it with rtcpPort. - SocketAddress to; - memcpy(&to, &_remoteRTCPAddr, sizeof(SocketAddress)); - - if(_ipV6Enabled) - { - to._sockaddr_in6.sin6_port = Htons(rtcpPort); - } else - { - to._sockaddr_in.sin_port = Htons(rtcpPort); - } - - if(_ptrSendRtcpSocket) - { - return _ptrSendRtcpSocket->SendTo(data,length,to); - - } else if(_ptrRtcpSocket) - { - return _ptrRtcpSocket->SendTo(data,length,to); - } - return -1; -} - -int UdpTransportImpl::SendPacket(int /*channel*/, const void* data, int length) -{ - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__); - - CriticalSectionScoped cs(*_crit); - - if(_destIP[0] == 0) - { - return -1; - } - if(_destPort == 0) - { - return -1; - } - - // Create socket if it hasn't been set up already. - // TODO (hellner): why not fail here instead. Sockets not being initialized - // indicates that there is a problem somewhere. - if( _ptrSendRtpSocket == NULL && - _ptrRtpSocket == NULL) - { - WEBRTC_TRACE( - kTraceStateInfo, - kTraceTransport, - _id, - "Creating RTP socket since no receive or source socket is\ - configured"); - - _ptrRtpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, this, - IncomingRTPCallback, - IpV6Enabled()); - - // Don't bind to a specific IP address. - if(! IpV6Enabled()) - { - strncpy(_localIP, "0.0.0.0",16); - } else - { - strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", - kIpAddressVersion6Length); - } - _localPort = _destPort; - - ErrorCode retVal = BindLocalRTPSocket(); - if(retVal != kNoSocketError) - { - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "SendPacket() failed to bind RTP socket"); - _lastError = retVal; - CloseReceiveSockets(); - return -1; - } - } - - if(_ptrSendRtpSocket) - { - return _ptrSendRtpSocket->SendTo((const WebRtc_Word8*)data, length, - _remoteRTPAddr); - - } else if(_ptrRtpSocket) - { - return _ptrRtpSocket->SendTo((const WebRtc_Word8*)data, length, - _remoteRTPAddr); - } - return -1; -} - -int UdpTransportImpl::SendRTCPPacket(int /*channel*/, const void* data, - int length) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - - CriticalSectionScoped cs(*_crit); - if(_destIP[0] == 0) - { - return -1; - } - if(_destPortRTCP == 0) - { - return -1; - } - - // Create socket if it hasn't been set up already. - // TODO (hellner): why not fail here instead. Sockets not being initialized - // indicates that there is a problem somewhere. - if( _ptrSendRtcpSocket == NULL && - _ptrRtcpSocket == NULL) - { - WEBRTC_TRACE( - kTraceStateInfo, - kTraceTransport, - _id, - "Creating RTCP socket since no receive or source socket is\ - configured"); - - _ptrRtcpSocket = UdpSocketWrapper::CreateSocket(_id, _mgr, this, - IncomingRTCPCallback, - IpV6Enabled()); - - // Don't bind to a specific IP address. - if(! IpV6Enabled()) - { - strncpy(_localIP, "0.0.0.0",16); - } else - { - strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", - kIpAddressVersion6Length); - } - _localPortRTCP = _destPortRTCP; - - ErrorCode retVal = BindLocalRTCPSocket(); - if(retVal != kNoSocketError) - { - _lastError = retVal; - WEBRTC_TRACE(kTraceError, kTraceTransport, _id, - "SendRTCPPacket() failed to bind RTCP socket"); - CloseReceiveSockets(); - return -1; - } - } - - if(_ptrSendRtcpSocket) - { - return _ptrSendRtcpSocket->SendTo((const WebRtc_Word8*)data, length, - _remoteRTCPAddr); - } else if(_ptrRtcpSocket) - { - return _ptrRtcpSocket->SendTo((const WebRtc_Word8*)data, length, - _remoteRTCPAddr); - } - return -1; -} - -WebRtc_Word32 UdpTransportImpl::SetSendIP(const WebRtc_Word8* ipaddr) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - if(!IsIpAddressValid(ipaddr,IpV6Enabled())) - { - return kIpAddressInvalid; - } - CriticalSectionScoped cs(*_crit); - strncpy(_destIP, ipaddr,kIpAddressVersion6Length); - BuildRemoteRTPAddr(); - BuildRemoteRTCPAddr(); - return 0; -} - -WebRtc_Word32 UdpTransportImpl::SetSendPorts(WebRtc_UWord16 rtpPort, - WebRtc_UWord16 rtcpPort) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, _id, "%s", __FUNCTION__); - CriticalSectionScoped cs(*_crit); - _destPort = rtpPort; - if(rtcpPort == 0) - { - _destPortRTCP = _destPort+1; - } else - { - _destPortRTCP = rtcpPort; - } - BuildRemoteRTPAddr(); - BuildRemoteRTCPAddr(); - return 0; -} - -void UdpTransportImpl::IncomingRTPCallback(CallbackObj obj, - const WebRtc_Word8* rtpPacket, - WebRtc_Word32 rtpPacketLength, - const SocketAddress* from) -{ - if (rtpPacket && rtpPacketLength > 0) - { - UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj; - socketTransport->IncomingRTPFunction(rtpPacket, rtpPacketLength, from); - } -} - -void UdpTransportImpl::IncomingRTCPCallback(CallbackObj obj, - const WebRtc_Word8* rtcpPacket, - WebRtc_Word32 rtcpPacketLength, - const SocketAddress* from) -{ - if (rtcpPacket && rtcpPacketLength > 0) - { - UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj; - socketTransport->IncomingRTCPFunction(rtcpPacket, rtcpPacketLength, - from); - } -} - -void UdpTransportImpl::IncomingRTPFunction(const WebRtc_Word8* rtpPacket, - WebRtc_Word32 rtpPacketLength, - const SocketAddress* fromSocket) -{ - WebRtc_Word8 ipAddress[kIpAddressVersion6Length]; - WebRtc_UWord32 ipAddressLength = kIpAddressVersion6Length; - WebRtc_UWord16 portNr = 0; - - { - CriticalSectionScoped cs(*_critFilter); - if (FilterIPAddress(fromSocket) == false) - { - // Packet should be filtered out. Drop it. - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, - "Incoming RTP packet blocked by IP filter"); - return; - } - - if (IPAddressCached(*fromSocket, ipAddress, ipAddressLength, portNr) < - 0) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpTransportImpl::IncomingRTPFunction - Cannot get sender\ - information"); - }else - { - strncpy(_fromIP, ipAddress, kIpAddressVersion6Length); - } - - // Filter based on port. - if (_rtpFilterPort != 0 && - _rtpFilterPort != portNr) - { - // Drop packet. - memset(_fromIP, 0, sizeof(_fromIP)); - WEBRTC_TRACE( - kTraceStream, - kTraceTransport, - _id, - "Incoming RTP packet blocked by filter incoming from port:%d\ - allowed port:%d", - portNr, - _rtpFilterPort); - return; - } - _fromPort = portNr; - } - - CriticalSectionScoped cs(*_critPacketCallback); - if (_packetCallback) - { - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, - "Incoming RTP packet from ip:%s port:%d", ipAddress, portNr); - _packetCallback->IncomingRTPPacket(rtpPacket, rtpPacketLength, - ipAddress, portNr); - } -} - -void UdpTransportImpl::IncomingRTCPFunction(const WebRtc_Word8* rtcpPacket, - WebRtc_Word32 rtcpPacketLength, - const SocketAddress* fromSocket) -{ - WebRtc_Word8 ipAddress[kIpAddressVersion6Length]; - WebRtc_UWord32 ipAddressLength = kIpAddressVersion6Length; - WebRtc_UWord16 portNr = 0; - - { - CriticalSectionScoped cs(*_critFilter); - if (FilterIPAddress(fromSocket) == false) - { - // Packet should be filtered out. Drop it. - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, - "Incoming RTCP packet blocked by IP filter"); - return; - } - if (IPAddress(*fromSocket, ipAddress, ipAddressLength, portNr) < 0) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpTransportImpl::IncomingRTCPFunction - Cannot get sender\ - information"); - }else { - strncpy(_fromIP, ipAddress, kIpAddressVersion6Length); - } - - // Filter based on port. - if (_rtcpFilterPort != 0 && - _rtcpFilterPort != portNr) - { - // Drop packet. - WEBRTC_TRACE( - kTraceStream, - kTraceTransport, - _id, - "Incoming RTCP packet blocked by filter incoming from port:%d\ - allowed port:%d", - portNr, - _rtpFilterPort); - return; - } - _fromPortRTCP = portNr; - } - - CriticalSectionScoped cs(*_critPacketCallback); - if (_packetCallback) - { - WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, - "Incoming RTCP packet from ip:%s port:%d", ipAddress, - portNr); - _packetCallback->IncomingRTCPPacket(rtcpPacket, rtcpPacketLength, - ipAddress, portNr); - } -} - -bool UdpTransportImpl::FilterIPAddress(const SocketAddress* fromAddress) -{ - if(fromAddress->_sockaddr_storage.sin_family == AF_INET) - { - if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET) - { - // IP is stored in sin_addr. - if (_filterIPAddress._sockaddr_in.sin_addr != 0 && - (_filterIPAddress._sockaddr_in.sin_addr != - fromAddress->_sockaddr_in.sin_addr)) - { - return false; - } - } - } - else if(fromAddress->_sockaddr_storage.sin_family == AF_INET6) - { - if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET6) - { - // IP is stored in sin_6addr. - for (WebRtc_Word32 i = 0; i < 4; i++) - { - if (_filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != 0 && - _filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != fromAddress->_sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i]) - { - return false; - } - } - } - } - else - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - _id, - "UdpTransportImpl::FilterIPAddress() unknown address family"); - return false; - } - return true; -} - -void UdpTransportImpl::CloseReceiveSockets() -{ - if(_ptrRtpSocket) - { - _ptrRtpSocket->CloseBlocking(); - _ptrRtpSocket = NULL; - } - if(_ptrRtcpSocket) - { - _ptrRtcpSocket->CloseBlocking(); - _ptrRtcpSocket = NULL; - } - _receiving = false; -} - -void UdpTransportImpl::CloseSendSockets() -{ - if(_ptrSendRtpSocket) - { - _ptrSendRtpSocket->CloseBlocking(); - _ptrSendRtpSocket = 0; - } - if(_ptrSendRtcpSocket) - { - _ptrSendRtcpSocket->CloseBlocking(); - _ptrSendRtcpSocket = 0; - } -} - -WebRtc_UWord16 UdpTransport::Htons(const WebRtc_UWord16 port) -{ - return htons(port); -} - -WebRtc_UWord32 UdpTransport::Htonl(const WebRtc_UWord32 a) -{ - return htonl(a); -} - -WebRtc_UWord32 UdpTransport::InetAddrIPV4(const WebRtc_Word8* ip) -{ - return ::inet_addr(ip); -} - -WebRtc_Word32 UdpTransport::InetPresentationToNumeric(WebRtc_Word32 af, - const WebRtc_Word8* src, - void* dst) -{ -#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - const WebRtc_Word32 result = inet_pton(af, src, dst); - return result > 0 ? 0 : -1; - -#elif defined(_WIN32) - SocketAddress temp; - int length=sizeof(SocketAddress); - - if(af == AF_INET) - { - WebRtc_Word32 result = WSAStringToAddressA( - (const LPSTR)src, - af, - 0, - reinterpret_cast(&temp), - &length); - if(result != 0) - { - return -1; - } - memcpy(dst,&(temp._sockaddr_in.sin_addr), - sizeof(temp._sockaddr_in.sin_addr)); - return 0; - } - else if(af == AF_INET6) - { - WebRtc_Word32 result = WSAStringToAddressA( - (const LPSTR)src, - af, - 0, - reinterpret_cast(&temp), - &length); - if(result !=0) - { - return -1; - } - memcpy(dst,&(temp._sockaddr_in6.sin6_addr), - sizeof(temp._sockaddr_in6.sin6_addr)); - return 0; - - }else - { - return -1; - } -#else - return -1; -#endif -} - -WebRtc_Word32 UdpTransport::LocalHostAddressIPV6(WebRtc_UWord8 localIP[16]) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, -1, "%s", __FUNCTION__); - -#if defined(_WIN32) - struct addrinfo *result = NULL; - struct addrinfo *ptr = NULL; - struct addrinfo hints; - - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_INET6; - - char szHostName[256] = ""; - if(::gethostname(szHostName, sizeof(szHostName) - 1)) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed"); - return -1; - } - - DWORD dwRetval = getaddrinfo(szHostName, NULL, &hints, &result); - if ( dwRetval != 0 ) - { - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, - "getaddrinfo failed, error:%d", dwRetval); - return -1; - } - for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) - { - switch (ptr->ai_family) - { - case AF_INET6: - { - for(int i = 0; i< 16; i++) - { - localIP[i] = (*(SocketAddress*)ptr->ai_addr)._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u8[i]; - } - bool islocalIP = true; - - for(int n = 0; n< 15; n++) - { - if(localIP[n] != 0) - { - islocalIP = false; - break; - } - } - - if(islocalIP && localIP[15] != 1) - { - islocalIP = false; - } - - if(islocalIP && ptr->ai_next) - { - continue; - } - if(localIP[0] == 0xfe && localIP[1] == 0x80 && ptr->ai_next) - { - continue; - } - freeaddrinfo(result); - } - return 0; - default: - break; - }; - } - freeaddrinfo(result); - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, - "getaddrinfo failed to find address"); - return -1; - -#elif defined(WEBRTC_MAC) - struct ifaddrs* ptrIfAddrs = NULL; - struct ifaddrs* ptrIfAddrsStart = NULL; - - getifaddrs(&ptrIfAddrsStart); - ptrIfAddrs = ptrIfAddrsStart; - while(ptrIfAddrs) - { - if(ptrIfAddrs->ifa_addr->sa_family == AF_INET6) - { - bool islocalIP = true; - for(int n = 2; n< 15; n++) - { - if(ptrIfAddrs->ifa_addr->sa_data[n+6] != 0) - { - islocalIP = false; - break; - } - } - if(islocalIP && ptrIfAddrs->ifa_addr->sa_data[15+6] != 1) - { - islocalIP = false; - } - - if(!islocalIP) - { - for(int i = 0; i< 16; i++) - { - localIP[i] = ptrIfAddrs->ifa_addr->sa_data[i+6]; - } - if(localIP[0] == 0xfe && localIP[1] == 0x80 && - ptrIfAddrs->ifa_next) - { - ptrIfAddrs = ptrIfAddrs->ifa_next; - continue; - } - freeifaddrs(ptrIfAddrsStart); - return 0; - } - } - ptrIfAddrs = ptrIfAddrs->ifa_next; - } - freeifaddrs(ptrIfAddrsStart); - return -1; -#elif defined(ANDROID) - return -1; -#else // WEBRTC_LINUX - struct - { - struct nlmsghdr n; - struct ifaddrmsg r; - } req; - - struct rtattr* rta = NULL; - int status; - char buf[16384]; // = 16 * 1024 (16 kB) - struct nlmsghdr* nlmp; - struct ifaddrmsg* rtmp; - struct rtattr* rtatp; - int rtattrlen; - struct in6_addr* in6p; - - int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - - // RTM_GETADDR is used to fetch the ip address from the kernel interface - // table. Populate the msg structure (req) the size of the message buffer - // is specified to netlinkmessage header, and flags values are set as - // NLM_F_ROOT | NLM_F_REQUEST. - // The request flag must be set for all messages requesting the data from - // kernel. The root flag is used to notify the kernel to return the full - // tabel. Another flag (not used) is NLM_F_MATCH. This is used to get only - // specified entries in the table. At the time of writing this program this - // flag is not implemented in kernel - - memset(&req, 0, sizeof(req)); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; - req.n.nlmsg_type = RTM_GETADDR; - req.r.ifa_family = AF_INET6; - - // Fill up all the attributes for the rtnetlink header. - // The lenght is very important. 16 signifies the ipv6 address. - rta = (struct rtattr*)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); - rta->rta_len = RTA_LENGTH(16); - - status = send(fd, &req, req.n.nlmsg_len, 0); - if (status < 0) - { - close(fd); - return -1; - } - status = recv(fd, buf, sizeof(buf), 0); - if (status < 0) - { - close(fd); - return -1; - } - if(status == 0) - { - close(fd); - return -1; - } - close(fd); - - // The message is stored in buff. Parse the message to get the requested - // data. - { - nlmp = (struct nlmsghdr*)buf; - int len = nlmp->nlmsg_len; - int req_len = len - sizeof(*nlmp); - - if (req_len < 0 || len > status) - { - return -1; - } - if (!NLMSG_OK_NO_WARNING(nlmp, status)) - { - return -1; - } - rtmp = (struct ifaddrmsg*)NLMSG_DATA(nlmp); - rtatp = (struct rtattr*)IFA_RTA(rtmp); - - rtattrlen = IFA_PAYLOAD(nlmp); - - for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) - { - - // Here we hit the fist chunk of the message. Time to validate the - // type. For more info on the different types see - // "man(7) rtnetlink" The table below is taken from man pages. - // Attributes - // rta_type value type description - // ------------------------------------------------------------- - // IFA_UNSPEC - unspecified. - // IFA_ADDRESS raw protocol address interface address - // IFA_LOCAL raw protocol address local address - // IFA_LABEL asciiz string name of the interface - // IFA_BROADCAST raw protocol address broadcast address. - // IFA_ANYCAST raw protocol address anycast address - // IFA_CACHEINFO struct ifa_cacheinfo Address information. - - if(rtatp->rta_type == IFA_ADDRESS) - { - bool islocalIP = true; - in6p = (struct in6_addr*)RTA_DATA(rtatp); - for(int n = 0; n< 15; n++) - { - if(in6p->s6_addr[n] != 0) - { - islocalIP = false; - break; - } - } - if(islocalIP && in6p->s6_addr[15] != 1) - { - islocalIP = false; - } - if(!islocalIP) - { - for(int i = 0; i< 16; i++) - { - localIP[i] = in6p->s6_addr[i]; - } - if(localIP[0] == 0xfe && localIP[1] == 0x80) - { - // Auto configured IP. - continue; - } - break; - } - } - } - } - return 0; -#endif -} - -WebRtc_Word32 UdpTransport::LocalHostAddress(WebRtc_UWord32& localIP) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, -1, "%s", __FUNCTION__); - #if defined(_WIN32) - hostent* localHost; - localHost = gethostbyname( "" ); - if(localHost) - { - if(localHost->h_addrtype != AF_INET) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - -1, - "LocalHostAddress can only get local IP for IP Version 4"); - return -1; - } - localIP= Htonl( - (*(struct in_addr *)localHost->h_addr_list[0]).S_un.S_addr); - return 0; - } - else - { - WebRtc_Word32 error = WSAGetLastError(); - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, - "gethostbyname failed, error:%d", error); - return -1; - } -#elif (defined(WEBRTC_MAC)) - char localname[255]; - if (gethostname(localname, 255) != -1) - { - hostent* localHost; - localHost = gethostbyname(localname); - if(localHost) - { - if(localHost->h_addrtype != AF_INET) - { - WEBRTC_TRACE( - kTraceError, - kTraceTransport, - -1, - "LocalHostAddress can only get local IP for IP Version 4"); - return -1; - } - localIP = Htonl((*(struct in_addr*)*localHost->h_addr_list).s_addr); - return 0; - } - } - WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed"); - return -1; -#else // WEBRTC_LINUX - int sockfd, size = 1; - struct ifreq* ifr; - struct ifconf ifc; - - if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) - { - return -1; - } - ifc.ifc_len = IFRSIZE; - ifc.ifc_req = NULL; - do - { - ++size; - // Buffer size needed is unknown. Try increasing it until no overflow - // occurs. - if (NULL == (ifc.ifc_req = (ifreq*)realloc(ifc.ifc_req, IFRSIZE))) { - fprintf(stderr, "Out of memory.\n"); - exit(EXIT_FAILURE); - } - ifc.ifc_len = IFRSIZE; - if (ioctl(sockfd, SIOCGIFCONF, &ifc)) - { - close(sockfd); - return -1; - } - } while (IFRSIZE <= ifc.ifc_len); - - ifr = ifc.ifc_req; - for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr) - { - if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) - { - continue; // duplicate, skip it - } - if (ioctl(sockfd, SIOCGIFFLAGS, ifr)) - { - continue; // failed to get flags, skip it - } - if(strncmp(ifr->ifr_name, "lo",3) == 0) - { - continue; - }else - { - struct sockaddr* saddr = &(ifr->ifr_addr); - SocketAddress* socket_addess = reinterpret_cast( - saddr); - localIP = Htonl(socket_addess->_sockaddr_in.sin_addr); - close(sockfd); - return 0; - } - } - close(sockfd); - return -1; -#endif -} - -WebRtc_Word32 UdpTransport::IPAddress(const SocketAddress& address, - WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort) -{ - #if defined(_WIN32) - DWORD dwIPSize = ipSize; - WebRtc_Word32 returnvalue = WSAAddressToStringA((LPSOCKADDR)(&address), - sizeof(SocketAddress), - NULL, - ip, - &dwIPSize); - if(returnvalue == -1) - { - return -1; - } - - WebRtc_UWord16 source_port = 0; - if(address._sockaddr_storage.sin_family == AF_INET) - { - // Parse IP assuming format "a.b.c.d:port". - WebRtc_Word8* ipEnd = strchr(ip,':'); - if(ipEnd != NULL) - { - *ipEnd = '\0'; - } - ipSize = (WebRtc_Word32)strlen(ip); - if(ipSize == 0) - { - return -1; - } - source_port = address._sockaddr_in.sin_port; - } - else - { - // Parse IP assuming format "[address]:port". - WebRtc_Word8* ipEnd = strchr(ip,']'); - if(ipEnd != NULL) - { - // Calculate length - WebRtc_Word32 adrSize = WebRtc_Word32(ipEnd - ip) - 1; - memmove(ip, &ip[1], adrSize); // Remove '[' - *(ipEnd - 1) = '\0'; - } - ipSize = (WebRtc_Word32)strlen(ip); - if(ipSize == 0) - { - return -1; - } - - source_port = address._sockaddr_in6.sin6_port; - } - // Convert port number to network byte order. - sourcePort = htons(source_port); - return 0; - - #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - WebRtc_Word32 ipFamily = address._sockaddr_storage.sin_family; - const void* ptrNumericIP = NULL; - - if(ipFamily == AF_INET) - { - ptrNumericIP = &(address._sockaddr_in.sin_addr); - } - else if(ipFamily == AF_INET6) - { - ptrNumericIP = &(address._sockaddr_in6.sin6_addr); - } - else - { - return -1; - } - if(inet_ntop(ipFamily, ptrNumericIP, ip, ipSize) == NULL) - { - return -1; - } - WebRtc_UWord16 source_port; - if(ipFamily == AF_INET) - { - source_port = address._sockaddr_in.sin_port; - } else - { - source_port = address._sockaddr_in6.sin6_port; - } - // Convert port number to network byte order. - sourcePort = htons(source_port); - return 0; - #else - return -1; - #endif -} - -bool UdpTransport::IsIpAddressValid(const WebRtc_Word8* ipadr, const bool ipV6) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceTransport, -1, "%s", __FUNCTION__); - if(ipV6) - { - WebRtc_Word32 len = (WebRtc_Word32)strlen(ipadr); - if( len>39 || len == 0) - { - return false; - } - - WebRtc_Word32 i; - WebRtc_Word32 colonPos[7] = {0,0,0,0,0,0,0}; - WebRtc_Word32 lastColonPos = -2; - WebRtc_Word32 nColons = 0; - WebRtc_Word32 nDubbleColons = 0; - WebRtc_Word32 nDots = 0; - WebRtc_Word32 error = 0; - WebRtc_Word8 c; - for(i = 0; i < len ; i++) - { - c=ipadr[i]; - if(isxdigit(c)) - ; - else if(c == ':') - { - if(nColons < 7) - colonPos[nColons] = i; - if((i-lastColonPos)==1) - nDubbleColons++; - lastColonPos=i; - if(nDots != 0) - { - error = 1; - } - nColons++; - } - else if(c == '.') - { - nDots++; - } - else - { - error = 1; - } - - } - if(error) - { - return false; - } - if(nDubbleColons > 1) - { - return false; - } - if(nColons > 7 || nColons < 2) - { - return false; - } - if(!(nDots == 3 || nDots == 0)) - { - return false; - } - lastColonPos = -1; - WebRtc_Word32 charsBeforeColon = 0; - for(i = 0; i < nColons; i++) - { - charsBeforeColon=colonPos[i]-lastColonPos-1; - if(charsBeforeColon > 4) - { - return false; - } - lastColonPos=colonPos[i]; - } - WebRtc_Word32 lengthAfterLastColon = len - lastColonPos - 1; - if(nDots == 0) - { - if(lengthAfterLastColon > 4) - return false; - } - if(nDots == 3 && lengthAfterLastColon > 0) - { - return IsIpAddressValid((ipadr+lastColonPos+1),false); - } - - } - else - { - WebRtc_Word32 len = (WebRtc_Word32)strlen(ipadr); - if((len>15)||(len==0)) - { - return false; - } - - // IPv4 should be [0-255].[0-255].[0-255].[0-255] - WebRtc_Word32 i; - WebRtc_Word32 nDots = 0; - WebRtc_Word32 iDotPos[4] = {0,0,0,0}; - - for (i = 0; (i < len) && (nDots < 4); i++) - { - if (ipadr[i] == (WebRtc_Word8)'.') - { - // Store index of dots and count number of dots. - iDotPos[nDots++] = i; - } - } - - bool allUnder256 = false; - // TODO (hellner): while loop seems to be abused here to get - // label like functionality. Fix later to avoid introducing bugs now. - - // Check that all numbers are smaller than 256. - do - { - if (nDots != 3 ) - { - break; - } - - if (iDotPos[0] <= 3) - { - WebRtc_Word8 nr[4]; - memset(nr,0,4); - strncpy(nr,&ipadr[0],iDotPos[0]); - WebRtc_Word32 num = atoi(nr); - if (num > 255) - { - break; - } - } else { - break; - } - - if (iDotPos[1] - iDotPos[0] <= 4) - { - WebRtc_Word8 nr[4]; - memset(nr,0,4); - strncpy(nr,&ipadr[iDotPos[0]+1], iDotPos[1] - iDotPos[0] - 1); - WebRtc_Word32 num = atoi(nr); - if (num > 255) - break; - } else { - break; - } - - if (iDotPos[2] - iDotPos[1] <= 4) - { - WebRtc_Word8 nr[4]; - memset(nr,0,4); - strncpy(nr,&ipadr[iDotPos[1]+1], iDotPos[1] - iDotPos[0] - 1); - WebRtc_Word32 num = atoi(nr); - if (num > 255) - break; - - memset(nr,0,4); - strncpy(nr,&ipadr[iDotPos[2]+1], len - iDotPos[2] -1); - num = atoi(nr); - if (num > 255) - break; - else - allUnder256 = true; - } else - break; - } while(false); - - if (nDots != 3 || !allUnder256) - { - return false; - } - } - return true; -} -} // namespace webrtc diff --git a/modules/udp_transport/source/udp_transport_impl.h b/modules/udp_transport/source/udp_transport_impl.h deleted file mode 100644 index dad4fb7be..000000000 --- a/modules/udp_transport/source/udp_transport_impl.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_TRANSPORT_IMPL_H_ -#define WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_TRANSPORT_IMPL_H_ - -#include "udp_transport.h" -#include "udp_socket_wrapper.h" - -namespace webrtc { -class CriticalSectionWrapper; -class RWLockWrapper; -class UdpSocketManager; - -class UdpTransportImpl : public UdpTransport -{ -public: - // Factory method. Constructor disabled. - UdpTransportImpl(const WebRtc_Word32 id, WebRtc_UWord8& numSocketThreads); - virtual ~UdpTransportImpl(); - - // Module functions - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - virtual WebRtc_Word32 TimeUntilNextProcess(); - virtual WebRtc_Word32 Process(); - - // UdpTransport functions - virtual WebRtc_Word32 InitializeSendSockets( - const WebRtc_Word8* ipAddr, - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0); - virtual WebRtc_Word32 InitializeReceiveSockets( - UdpTransportData* const packetCallback, - const WebRtc_UWord16 rtpPort, - const WebRtc_Word8* ipAddr = NULL, - const WebRtc_Word8* multicastIpAddr = NULL, - const WebRtc_UWord16 rtcpPort = 0); - virtual WebRtc_Word32 InitializeSourcePorts( - const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0); - virtual WebRtc_Word32 SourcePorts(WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const; - virtual WebRtc_Word32 ReceiveSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort, - WebRtc_Word8 multicastIpAddr[kIpAddressVersion6Length]) const; - virtual WebRtc_Word32 SendSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const; - virtual WebRtc_Word32 RemoteSocketInformation( - WebRtc_Word8 ipAddr[kIpAddressVersion6Length], - WebRtc_UWord16& rtpPort, - WebRtc_UWord16& rtcpPort) const; - virtual WebRtc_Word32 SetQoS(const bool QoS, - const WebRtc_Word32 serviceType, - const WebRtc_UWord32 maxBitrate = 0, - const WebRtc_Word32 overrideDSCP = 0, - const bool audio = false); - virtual WebRtc_Word32 QoS(bool& QoS, WebRtc_Word32& serviceType, - WebRtc_Word32& overrideDSCP) const; - virtual WebRtc_Word32 SetToS(const WebRtc_Word32 DSCP, - const bool useSetSockOpt = false); - virtual WebRtc_Word32 ToS(WebRtc_Word32& DSCP, - bool& useSetSockOpt) const; - virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 PCP); - virtual WebRtc_Word32 PCP(WebRtc_Word32& PCP) const; - virtual WebRtc_Word32 EnableIpV6(); - virtual bool IpV6Enabled() const; - virtual WebRtc_Word32 SetFilterIP( - const WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]); - virtual WebRtc_Word32 FilterIP( - WebRtc_Word8 filterIPAddress[kIpAddressVersion6Length]) const; - virtual WebRtc_Word32 SetFilterPorts(const WebRtc_UWord16 rtpFilterPort, - const WebRtc_UWord16 rtcpFilterPort); - virtual WebRtc_Word32 FilterPorts(WebRtc_UWord16& rtpFilterPort, - WebRtc_UWord16& rtcpFilterPort) const; - virtual WebRtc_Word32 StartReceiving( - const WebRtc_UWord32 numberOfSocketBuffers); - virtual WebRtc_Word32 StopReceiving(); - virtual bool Receiving() const; - virtual bool SendSocketsInitialized() const; - virtual bool SourcePortsInitialized() const; - virtual bool ReceiveSocketsInitialized() const; - virtual WebRtc_Word32 SendRaw(const WebRtc_Word8* data, - WebRtc_UWord32 length, WebRtc_Word32 isRTCP, - WebRtc_UWord16 portnr = 0, - const WebRtc_Word8* ip = NULL); - virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8 *data, - WebRtc_UWord32 length, - const SocketAddress& to); - virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8 *data, - WebRtc_UWord32 length, - const SocketAddress& to); - virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8 *data, - WebRtc_UWord32 length, - WebRtc_UWord16 rtpPort); - virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8 *data, - WebRtc_UWord32 length, - WebRtc_UWord16 rtcpPort); - // Transport functions - virtual int SendPacket(int channel, const void* data, int length); - virtual int SendRTCPPacket(int channel, const void* data, int length); - - // UdpTransport functions continue. - virtual WebRtc_Word32 SetSendIP(const WebRtc_Word8* ipaddr); - virtual WebRtc_Word32 SetSendPorts(const WebRtc_UWord16 rtpPort, - const WebRtc_UWord16 rtcpPort = 0); - - virtual ErrorCode LastError() const; - - virtual WebRtc_Word32 IPAddressCached(const SocketAddress& address, - WebRtc_Word8* ip, - WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort); - - WebRtc_Word32 Id() const {return _id;} -protected: - // IncomingSocketCallback signature functions for receiving callbacks from - // UdpSocketWrapper. - static void IncomingRTPCallback(CallbackObj obj, - const WebRtc_Word8* rtpPacket, - WebRtc_Word32 rtpPacketLength, - const SocketAddress* from); - static void IncomingRTCPCallback(CallbackObj obj, - const WebRtc_Word8* rtcpPacket, - WebRtc_Word32 rtcpPacketLength, - const SocketAddress* from); - - void CloseSendSockets(); - void CloseReceiveSockets(); - - // Update _remoteRTPAddr according to _destPort and _destIP - void BuildRemoteRTPAddr(); - // Update _remoteRTCPAddr according to _destPortRTCP and _destIP - void BuildRemoteRTCPAddr(); - - void BuildSockaddrIn(WebRtc_UWord16 portnr, const WebRtc_Word8* ip, - SocketAddress& remoteAddr) const; - - ErrorCode BindLocalRTPSocket(); - ErrorCode BindLocalRTCPSocket(); - - ErrorCode BindRTPSendSocket(); - ErrorCode BindRTCPSendSocket(); - - void IncomingRTPFunction(const WebRtc_Word8* rtpPacket, - WebRtc_Word32 rtpPacketLength, - const SocketAddress* from); - void IncomingRTCPFunction(const WebRtc_Word8* rtcpPacket, - WebRtc_Word32 rtcpPacketLength, - const SocketAddress* from); - - bool FilterIPAddress(const SocketAddress* fromAddress); - - bool SetSockOptUsed(); - - WebRtc_Word32 EnableQoS(WebRtc_Word32 serviceType, bool audio, - WebRtc_UWord32 maxBitrate, - WebRtc_Word32 overrideDSCP); - - WebRtc_Word32 DisableQoS(); - -private: - void GetCachedAddress(WebRtc_Word8* ip, WebRtc_UWord32& ipSize, - WebRtc_UWord16& sourcePort); - - WebRtc_Word32 _id; - // Protects the sockets from being re-configured while receiving packets. - CriticalSectionWrapper* _crit; - CriticalSectionWrapper* _critFilter; - // _packetCallback's critical section. - CriticalSectionWrapper* _critPacketCallback; - UdpSocketManager* _mgr; - ErrorCode _lastError; - - // Remote RTP and RTCP ports. - WebRtc_UWord16 _destPort; - WebRtc_UWord16 _destPortRTCP; - - // Local RTP and RTCP ports. - WebRtc_UWord16 _localPort; - WebRtc_UWord16 _localPortRTCP; - - // Local port number when the local port for receiving and local port number - // for sending are not the same. - WebRtc_UWord16 _srcPort; - WebRtc_UWord16 _srcPortRTCP; - - // Remote port from which last received packet was sent. - WebRtc_UWord16 _fromPort; - WebRtc_UWord16 _fromPortRTCP; - - WebRtc_Word8 _fromIP[kIpAddressVersion6Length]; - WebRtc_Word8 _destIP[kIpAddressVersion6Length]; - WebRtc_Word8 _localIP[kIpAddressVersion6Length]; - WebRtc_Word8 _localMulticastIP[kIpAddressVersion6Length]; - - UdpSocketWrapper* _ptrRtpSocket; - UdpSocketWrapper* _ptrRtcpSocket; - - // Local port when the local port for receiving and local port for sending - // are not the same. - UdpSocketWrapper* _ptrSendRtpSocket; - UdpSocketWrapper* _ptrSendRtcpSocket; - - SocketAddress _remoteRTPAddr; - SocketAddress _remoteRTCPAddr; - - SocketAddress _localRTPAddr; - SocketAddress _localRTCPAddr; - - WebRtc_Word32 _tos; - bool _inCallbackMode; - bool _receiving; - bool _useSetSockOpt; - bool _qos; - WebRtc_Word32 _pcp; - mutable bool _IpV6EnabledRead; - bool _ipV6Enabled; - WebRtc_Word32 _serviceType; - WebRtc_Word32 _overrideDSCP; - WebRtc_UWord32 _maxBitrate; - - // Cache used by GetCachedAddress(..). - RWLockWrapper* _cachLock; - SocketAddress _previousAddress; - WebRtc_Word8 _previousIP[kIpAddressVersion6Length]; - WebRtc_UWord32 _previousIPSize; - WebRtc_UWord16 _previousSourcePort; - - SocketAddress _filterIPAddress; - WebRtc_UWord16 _rtpFilterPort; - WebRtc_UWord16 _rtcpFilterPort; - - UdpTransportData* _packetCallback; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UDP_TRANSPORT_SOURCE_UDP_TRANSPORT_IMPL_H_ diff --git a/modules/udp_transport/test/SocketManagerTest.cpp b/modules/udp_transport/test/SocketManagerTest.cpp deleted file mode 100644 index 14ab61a68..000000000 --- a/modules/udp_transport/test/SocketManagerTest.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#ifdef _WIN32 -#include -#include -#else -#include -#define Sleep(x) usleep(x*1000) -#endif - -#include "udp_transport.h" -#include "common_types.h" -#include "trace.h" - -//#define QOS_TEST -//#define QOS_TEST_WITH_OVERRIDE // require admin on Win7 -//#define TOS_TEST // require admin on Win7 -//#define TOS_TEST_USING_SETSOCKOPT -//#define PCP_TEST - -class UdpTransportDataA: public UdpTransportData -{ -public: - UdpTransportDataA() : - _counterRTP(0), - _counterRTCP(0) - { - }; - virtual void IncomingRTPPacket(const WebRtc_Word8* incommingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) - { - _counterRTP++; - }; - - virtual void IncomingRTCPPacket(const WebRtc_Word8* incommingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) - { - _counterRTCP++; - }; - WebRtc_UWord32 _counterRTP; - WebRtc_UWord32 _counterRTCP; -}; - -class UdpTransportDataB: public UdpTransportData -{ -public: - UdpTransportDataB() : - _counterRTP(0), - _counterRTCP(0) - { - }; - virtual void IncomingRTPPacket(const WebRtc_Word8* incommingRtpPacket, - const WebRtc_Word32 rtpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) - { - _counterRTP++; - }; - - virtual void IncomingRTCPPacket(const WebRtc_Word8* incommingRtcpPacket, - const WebRtc_Word32 rtcpPacketLength, - const WebRtc_Word8* fromIP, - const WebRtc_UWord16 fromPort) - { - _counterRTCP++; - }; - WebRtc_UWord32 _counterRTP; - WebRtc_UWord32 _counterRTCP; -}; - -#ifdef _WIN32 -int _tmain(int argc, _TCHAR* argv[]) -#else -int main(int argc, char* argv[]) -#endif -{ - Trace::CreateTrace(); - Trace::SetTraceFile("testTrace.txt"); - Trace::SetEncryptedTraceFile("testTraceDebug.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - printf("Start UdpTransport test\n"); - - WebRtc_UWord8 numberOfSocketThreads = 5; - UdpTransport* client1 = UdpTransport::Create(1,numberOfSocketThreads,NULL); - numberOfSocketThreads = 0; - UdpTransport* client2 = UdpTransport::Create(2,numberOfSocketThreads,NULL); - assert(5 == numberOfSocketThreads); - - UdpTransportDataA* client1Callback = new UdpTransportDataA(); - UdpTransportDataB* client2Callback = new UdpTransportDataB(); - - WebRtc_UWord32 localIP = 0; - WebRtc_Word8 localIPAddr[64]; - assert( 0 == client1->LocalHostAddress(localIP)); // network host order aka big-endian - - sprintf(localIPAddr,"%lu.%lu.%lu.%lu",(localIP>>24)& 0x0ff,(localIP>>16)& 0x0ff ,(localIP>>8)& 0x0ff, localIP & 0x0ff); - printf("\tLocal IP:%s\n", localIPAddr); - - WebRtc_UWord8 localIPV6[16]; - WebRtc_Word8 localIPAddrV6[128]; - if( 0 == client1->LocalHostAddressIPV6(localIPV6)) - { - sprintf(localIPAddrV6,"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", localIPV6[0],localIPV6[1],localIPV6[2],localIPV6[3],localIPV6[4],localIPV6[5],localIPV6[6],localIPV6[7], localIPV6[8],localIPV6[9],localIPV6[10],localIPV6[11],localIPV6[12],localIPV6[13],localIPV6[14],localIPV6[15]); - printf("\tLocal IPV6:%s\n", localIPAddrV6); - } - - WebRtc_Word8 test[9] = "testtest"; - assert( 0 == client1->InitializeReceiveSockets(client1Callback,1234, localIPAddr)); - -#if defined QOS_TEST_WITH_OVERRIDE || defined QOS_TEST || defined TOS_TEST || defined TOS_TEST_USING_SETSOCKOPT - assert( -1 == client1->SetQoS(true, 3, 1000)); // should fail - assert( 0 == client1->InitializeSendSockets("192.168.200.1", 1236,1237)); -#else - assert( 0 == client1->InitializeSendSockets(localIPAddr, 1236,1237)); -#endif - assert( 0 == client1->StartReceiving(20)); - - assert( 0 == client2->InitializeReceiveSockets(client2Callback,1236)); - assert( 0 == client2->InitializeSendSockets(localIPAddr, 1234,1235)); - assert( 0 == client2->StartReceiving(20)); - - Sleep(10); - -#ifdef TOS_TEST - // note: you need to have QOS installed on your interface for this test - // test by doing a ethereal sniff and filter out packets with ip.dst == 192.168.200.1 - assert( 0 == client1->SetToS(2)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetToS(3)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetToS(0)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - - printf("Tested TOS \n"); - Sleep(5000); - return 0; -#endif - -#ifdef TOS_TEST_USING_SETSOCKOPT - // note: you need to have QOS installed on your interface for this test - // test by doing a ethereal sniff and filter out packets with ip.dst == 192.168.200.1 - assert( 0 == client1->SetToS(2, true)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetToS(3, true)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetToS(0, true)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - - printf("Tested TOS using setsockopt \n"); - Sleep(5000); - return 0; -#endif - -#ifdef QOS_TEST - // note: you need to have QOS installed on your interface for this test - // test by doing a ethereal sniff and filter out packets with ip.dst == 192.168.200.1 - assert( 0 == client1->SetQoS(true, 2, 1000)); // SERVICETYPE_CONTROLLEDLOAD 2 - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetQoS(true, 3, 1000)); // SERVICETYPE_GUARANTEED 3 - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetQoS(false, 0)); // - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - - printf("Tested QOS \n"); - Sleep(5000); - return 0; -#endif - -#ifdef QOS_TEST_WITH_OVERRIDE - // note: you need to have QOS installed on your interface for this test - // test by doing a ethereal sniff and filter out packets with ip.dst == 192.168.200.1 - assert( 0 == client1->SetQoS(true, 2, 1000, 1)); // SERVICETYPE_CONTROLLEDLOAD 2 - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetQoS(true, 2, 1000, 2)); // SERVICETYPE_GUARANTEED 3 - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - Sleep(10); - assert( 0 == client1->SetQoS(false, 0)); // - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - printf("Tested QOS with override \n"); - Sleep(5000); - return 0; -#endif - -#ifdef PCP_TEST - // Note: We currently don't know how to test that the bits are actually set in the frame, - // this test simply tests the API and that we can send a packet after setting PCP. - assert( -1 == client1->SetPCP(-1)); // should fail - assert( -1 == client1->SetPCP(8)); // should fail - printf("Setting PCP to 7 returned %d \n", client1->SetPCP(7)); - printf("(Failing is normal, requires the CAP_NET_ADMIN capability to succeed.) \n"); - Sleep(10); - for (int pcp = 6; pcp >= 0; --pcp) - { - assert( 0 == client1->SetPCP(pcp)); - Sleep(10); - assert( 9 == client1->SendPacket(-1, test, 9)); - } - printf("Tested PCP \n"); - Sleep(5000); - return 0; -#endif - - Sleep(10); - - assert( 9 == client1->SendPacket(-1, test, 9)); - - // test start rec after a socket has revceived data - // result: packets received before first startReceive is saved by the OS -/* - for(int i = 0; i < 100; i++) - { - assert( 9 == client1->SendPacket(-1, test, 9)); - } - Sleep(10); - assert( 0 == client2->StartReceiving(20)); - -// assert( 0 == client2->StopReceiving()); - - Sleep(10); - for(int i = 0; i < 100; i++) - { - assert( 9 == client1->SendPacket(-1, test, 9)); - } - - assert( 0 == client2->StartReceiving(20)); - - for(int i = 0; i < 100; i++) - { - assert( 9 == client1->SendPacket(-1, test, 9)); - } -*/ - Sleep(10); - - assert( 0 == client1Callback->_counterRTP); - assert( 1 == client2Callback->_counterRTP); - assert( 0 == client1Callback->_counterRTCP); - assert( 0 == client2Callback->_counterRTCP); - - printf("Sent 1 packet on one socket \n"); - - WebRtc_Word8 ipAddr[64]; - WebRtc_Word8 tempIpAddr[64]; - WebRtc_Word8 ipMulticastAddr[64]; - WebRtc_UWord16 rtpPort = 0; - WebRtc_UWord16 rtcpPort = 0; - bool reusableSocket = true; - assert( 0 == client2->RemoteSocketInformation(ipAddr, rtpPort, rtcpPort)); - assert( rtpPort == 1234); - assert( strncmp(ipAddr, localIPAddr, 16) == 0); - - assert( 0 == client2->ReceiveSocketInformation(ipAddr, rtpPort, rtcpPort, ipMulticastAddr, reusableSocket)); - assert( rtpPort == 1236); - assert( rtcpPort == 1237); - assert( strncmp(ipAddr, "0.0.0.0", 16) == 0); - assert( ipMulticastAddr[0] == 0); - assert( reusableSocket == false); - - assert( 0 == client2->SendSocketInformation(ipAddr, rtpPort, rtcpPort)); - assert( rtpPort == 1234); - assert( rtcpPort == 1235); - assert( strncmp(ipAddr,localIPAddr, 16) == 0); - - const int numberOfPackets = 1000; - int n = 0; - while(n < numberOfPackets) - { - assert( 9 == client1->SendPacket(-1, test, 9)); - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client1->SendRTCPPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - n++; - } - int loops = 0; - for(; loops < 100 && - !(client1Callback->_counterRTP == numberOfPackets && - client1Callback->_counterRTCP == numberOfPackets && - client2Callback->_counterRTP == numberOfPackets+1 && - client2Callback->_counterRTCP == numberOfPackets); - loops++) - { - Sleep(10); - } - printf("\tSent %d packets on 4 sockets in:%d ms\n", numberOfPackets, loops*10); - - assert( numberOfPackets == client1Callback->_counterRTP); - assert( numberOfPackets+1 == client2Callback->_counterRTP); - assert( numberOfPackets == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - - assert( 0 == client1->StopReceiving()); - assert( 0 == client2->StopReceiving()); - - printf("Tear down client 2\n"); - - // configure that fail - assert( -1 == client2->InitializeReceiveSockets(client2Callback,1234, localIPAddr)); // port in use - assert( !client2->ReceiveSocketsInitialized()); - assert( 0 == client2->InitializeReceiveSockets(client2Callback,1236)); - assert( 0 == client2->StartReceiving(20)); - - printf("Client 2 re-configured\n"); - - assert( client1->SendSocketsInitialized()); - assert( client1->ReceiveSocketsInitialized()); - assert( client2->SendSocketsInitialized()); - assert( client2->ReceiveSocketsInitialized()); - - assert( 9 == client1->SendPacket(-1, test, 9)); - - // this should not be received since we dont receive in client 1 - assert( 9 == client2->SendPacket(-1, test, 9)); - - Sleep(10); - - assert( numberOfPackets == client1Callback->_counterRTP); - assert( numberOfPackets+2 == client2Callback->_counterRTP); - assert( numberOfPackets == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - printf("\tSent 1 packet on one socket \n"); - - printf("Start filter test\n"); - - assert( 0 == client1->StartReceiving(20)); - - assert( 0 == client1->SetFilterPorts(1234, 1235)); // should filter out what we send - assert( 0 == client1->SetFilterIP(localIPAddr)); - - assert( 0 == client1->FilterIP(tempIpAddr)); - assert( strncmp(tempIpAddr, localIPAddr, 16) == 0); - - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - - Sleep(10); - - assert( numberOfPackets == client1Callback->_counterRTP); - assert( numberOfPackets+2 == client2Callback->_counterRTP); - assert( numberOfPackets == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - - assert( 0 == client1->SetFilterPorts(1236, 1237)); // should pass through - - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - printf("\tSent 1 packet on two sockets \n"); - - Sleep(10); - - assert( numberOfPackets+1 == client1Callback->_counterRTP); - assert( numberOfPackets+2 == client2Callback->_counterRTP); - assert( numberOfPackets+1 == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - - assert( 0 == client1->SetFilterIP("127.0.0.2")); - - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - printf("\tSent 1 packet on two sockets \n"); - - Sleep(10); - - assert( numberOfPackets+1 == client1Callback->_counterRTP); - assert( numberOfPackets+2 == client2Callback->_counterRTP); - assert( numberOfPackets+1 == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - - assert( 0 == client1->SetFilterIP(NULL)); - assert( 0 == client1->SetFilterPorts(0, 0)); - - printf("Tested filter \n"); - - assert( 0 == client2->InitializeSourcePorts(1238, 1239)); - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - printf("\tSent 1 packet on two sockets \n"); - - Sleep(10); - - assert( numberOfPackets+2 == client1Callback->_counterRTP); - assert( numberOfPackets+2 == client2Callback->_counterRTP); - assert( numberOfPackets+2 == client1Callback->_counterRTCP); - assert( numberOfPackets == client2Callback->_counterRTCP); - - assert( 0 == client1->RemoteSocketInformation(ipAddr, rtpPort, rtcpPort)); - assert( rtpPort == 1238); - assert( rtcpPort == 1239); - assert( strncmp(ipAddr, localIPAddr, 16) == 0); - - printf("Tested source port \n"); - - assert( 0 == client2->InitializeSourcePorts(1240 )); - assert( 9 == client2->SendPacket(-1, test, 9)); - assert( 9 == client2->SendRTCPPacket(-1, test, 9)); - printf("\tSent 1 packet on two sockets \n"); - - Sleep(10); - - assert( 0 == client1->RemoteSocketInformation(ipAddr, rtpPort, rtcpPort)); - assert( rtpPort == 1240); - assert( rtcpPort == 1241); - - printf("Tested SetSendPorts source port \n"); - - UdpTransport::Destroy(client1); - UdpTransport::Destroy(client2); - - printf("\n\nUdpTransport test done\n"); - - delete client1Callback; - delete client2Callback; - - Sleep(5000); - Trace::ReturnTrace(); -}; diff --git a/modules/utility/OWNERS b/modules/utility/OWNERS deleted file mode 100644 index 6ca0dffd4..000000000 --- a/modules/utility/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -hellner@google.com -pwestin@google.com -asapersson@google.com -perkj@google.com \ No newline at end of file diff --git a/modules/utility/interface/file_player.h b/modules/utility/interface/file_player.h deleted file mode 100644 index b2faa7fa2..000000000 --- a/modules/utility/interface/file_player.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_FILE_PLAYER_H_ -#define WEBRTC_MODULES_UTILITY_INTERFACE_FILE_PLAYER_H_ - -#include "common_types.h" -#include "engine_configurations.h" -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc { -class FileCallback; - -class FilePlayer -{ -public: - // The largest decoded frame size in samples (60ms with 32kHz sample rate). - enum {MAX_AUDIO_BUFFER_IN_SAMPLES = 60*32}; - enum {MAX_AUDIO_BUFFER_IN_BYTES = MAX_AUDIO_BUFFER_IN_SAMPLES*2}; - - // Note: will return NULL for video file formats (e.g. AVI) if the flag - // WEBRTC_MODULE_UTILITY_VIDEO is not defined. - static FilePlayer* CreateFilePlayer(const WebRtc_UWord32 instanceID, - const FileFormats fileFormat); - - static void DestroyFilePlayer(FilePlayer* player); - - virtual WebRtc_Word32 Get10msAudioFromFile( - WebRtc_Word16* decodedDataBuffer, - WebRtc_UWord32& decodedDataLengthInSamples, - const WebRtc_UWord32 frequencyInHz) = 0; - - // Register callback for receiving file playing notifications. - virtual WebRtc_Word32 RegisterModuleFileCallback( - FileCallback* callback) = 0; - - // API for playing audio from fileName to channel. - // Note: codecInst is used for pre-encoded files. - virtual WebRtc_Word32 StartPlayingFile( - const WebRtc_Word8* fileName, - bool loop, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition = 0, - const CodecInst* codecInst = NULL) = 0; - - // Note: codecInst is used for pre-encoded files. - virtual WebRtc_Word32 StartPlayingFile( - InStream& sourceStream, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition = 0, - const CodecInst* codecInst = NULL) = 0; - - virtual WebRtc_Word32 StopPlayingFile() = 0; - - virtual bool IsPlayingFile() const = 0; - - virtual WebRtc_Word32 GetPlayoutPosition(WebRtc_UWord32& durationMs) = 0; - - // Set audioCodec to the currently used audio codec. - virtual WebRtc_Word32 AudioCodec(CodecInst& audioCodec) const = 0; - - virtual WebRtc_Word32 Frequency() const = 0; - - // Note: scaleFactor is in the range [0.0 - 2.0] - virtual WebRtc_Word32 SetAudioScaling(float scaleFactor) = 0; - - // Return the time in ms until next video frame should be pulled (by - // calling GetVideoFromFile(..)). - // Note: this API reads one video frame from file. This means that it should - // be called exactly once per GetVideoFromFile(..) API call. - virtual WebRtc_Word32 TimeUntilNextVideoFrame() { return -1;} - - virtual WebRtc_Word32 StartPlayingVideoFile( - const WebRtc_Word8* /*fileName*/, - bool /*loop*/, - bool /*videoOnly*/) { return -1;} - - virtual WebRtc_Word32 video_codec_info(VideoCodec& /*videoCodec*/) const - {return -1;} - - virtual WebRtc_Word32 GetVideoFromFile(VideoFrame& /*videoFrame*/) - { return -1;} - - // Same as GetVideoFromFile(). videoFrame will have the resolution specified - // by the width outWidth and height outHeight in pixels. - virtual WebRtc_Word32 GetVideoFromFile(VideoFrame& /*videoFrame*/, - const WebRtc_UWord32 /*outWidth*/, - const WebRtc_UWord32 /*outHeight*/) - {return -1;} -protected: - virtual ~FilePlayer() {} - -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_INTERFACE_FILE_PLAYER_H_ diff --git a/modules/utility/interface/file_recorder.h b/modules/utility/interface/file_recorder.h deleted file mode 100644 index 147dea2b3..000000000 --- a/modules/utility/interface/file_recorder.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_FILE_RECORDER_H_ -#define WEBRTC_MODULES_UTILITY_INTERFACE_FILE_RECORDER_H_ - -#include "audio_coding_module_typedefs.h" -#include "common_types.h" -#include "engine_configurations.h" -#include "media_file_defines.h" -#include "module_common_types.h" -#include "tick_util.h" -#include "typedefs.h" - -namespace webrtc { - -class FileRecorder -{ -public: - - // Note: will return NULL for video file formats (e.g. AVI) if the flag - // WEBRTC_MODULE_UTILITY_VIDEO is not defined. - static FileRecorder* CreateFileRecorder(const WebRtc_UWord32 instanceID, - const FileFormats fileFormat); - - static void DestroyFileRecorder(FileRecorder* recorder); - - virtual WebRtc_Word32 RegisterModuleFileCallback( - FileCallback* callback) = 0; - - virtual FileFormats RecordingFileFormat() const = 0; - - virtual WebRtc_Word32 StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const CodecInst& codecInst, - WebRtc_UWord32 notification, - ACMAMRPackingFormat amrFormat = AMRFileStorage) = 0; - - virtual WebRtc_Word32 StartRecordingAudioFile( - OutStream& destStream, - const CodecInst& codecInst, - WebRtc_UWord32 notification, - ACMAMRPackingFormat amrFormat = AMRFileStorage) = 0; - - // Stop recording. - // Note: this API is for both audio and video. - virtual WebRtc_Word32 StopRecording() = 0; - - // Return true if recording. - // Note: this API is for both audio and video. - virtual bool IsRecording() const = 0; - - virtual WebRtc_Word32 codec_info(CodecInst& codecInst) const = 0; - - // Write frame to file. Frame should contain 10ms of un-ecoded audio data. - virtual WebRtc_Word32 RecordAudioToFile( - const AudioFrame& frame, - const TickTime* playoutTS = NULL) = 0; - - // Open/create the file specified by fileName for writing audio/video data - // (relative path is allowed). audioCodecInst specifies the encoding of the - // audio data. videoCodecInst specifies the encoding of the video data. - // Only video data will be recorded if videoOnly is true. amrFormat - // specifies the amr/amrwb storage format. - // Note: the file format is AVI. - virtual WebRtc_Word32 StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const CodecInst& audioCodecInst, - const VideoCodec& videoCodecInst, - ACMAMRPackingFormat amrFormat = AMRFileStorage, - bool videoOnly = false) = 0; - - // Record the video frame in videoFrame to AVI file. - virtual WebRtc_Word32 RecordVideoToFile(const VideoFrame& videoFrame) = 0; - -protected: - virtual ~FileRecorder() {} - -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_INTERFACE_FILE_RECORDER_H_ diff --git a/modules/utility/interface/process_thread.h b/modules/utility/interface/process_thread.h deleted file mode 100644 index 6c5140497..000000000 --- a/modules/utility/interface/process_thread.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_PROCESS_THREAD_H_ -#define WEBRTC_MODULES_UTILITY_INTERFACE_PROCESS_THREAD_H_ - -#include "typedefs.h" - -namespace webrtc { -class Module; - -class ProcessThread -{ -public: - static ProcessThread* CreateProcessThread(); - static void DestroyProcessThread(ProcessThread* module); - - virtual WebRtc_Word32 Start() = 0; - virtual WebRtc_Word32 Stop() = 0; - - virtual WebRtc_Word32 RegisterModule(const Module* module) = 0; - virtual WebRtc_Word32 DeRegisterModule(const Module* module) = 0; -protected: - virtual ~ProcessThread(); -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_INTERFACE_PROCESS_THREAD_H_ diff --git a/modules/utility/interface/rtp_dump.h b/modules/utility/interface/rtp_dump.h deleted file mode 100644 index 5f51990be..000000000 --- a/modules/utility/interface/rtp_dump.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This file implements a class that writes a stream of RTP and RTCP packets -// to a file according to the format specified by rtpplay. See -// http://www.cs.columbia.edu/irt/software/rtptools/. -// Notes: supported platforms are Windows, Linux and Mac OSX - -#ifndef WEBRTC_MODULES_UTILITY_INTERFACE_RTP_DUMP_H_ -#define WEBRTC_MODULES_UTILITY_INTERFACE_RTP_DUMP_H_ - -#include "typedefs.h" -#include "file_wrapper.h" - -namespace webrtc { -class RtpDump -{ -public: - // Factory method. - static RtpDump* CreateRtpDump(); - - // Delete function. Destructor disabled. - static void DestroyRtpDump(RtpDump* object); - - // Open the file fileNameUTF8 for writing RTP/RTCP packets. - // Note: this API also adds the rtpplay header. - virtual WebRtc_Word32 Start(const WebRtc_Word8* fileNameUTF8) = 0; - - // Close the existing file. No more packets will be recorded. - virtual WebRtc_Word32 Stop() = 0; - - // Return true if a file is open for recording RTP/RTCP packets. - virtual bool IsActive() const = 0; - - // Writes the RTP/RTCP packet in packet with length packetLength in bytes. - // Note: packet should contain the RTP/RTCP part of the packet. I.e. the - // first bytes of packet should be the RTP/RTCP header. - virtual WebRtc_Word32 DumpPacket(const WebRtc_UWord8* packet, - WebRtc_UWord16 packetLength) = 0; - -protected: - virtual ~RtpDump(); -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_INTERFACE_RTP_DUMP_H_ diff --git a/modules/utility/source/Android.mk b/modules/utility/source/Android.mk deleted file mode 100644 index c7fe62bf0..000000000 --- a/modules/utility/source/Android.mk +++ /dev/null @@ -1,58 +0,0 @@ -# This file is generated by gyp; do not edit. This means you! - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_utility -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := coder.cc \ - file_player_impl.cc \ - file_recorder_impl.cc \ - process_thread_impl.cc \ - rtp_dump_impl.cc \ - frame_scaler.cc \ - video_coder.cc \ - video_frames_queue.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../interface \ - $(LOCAL_PATH)/../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../media_file/interface \ - $(LOCAL_PATH)/../../video_coding/main/interface \ - $(LOCAL_PATH)/../../audio_coding/main/interface \ - $(LOCAL_PATH)/../../video_coding/codecs/interface \ - $(LOCAL_PATH)/../../../common_audio/resampler/main/interface \ - $(LOCAL_PATH)/../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := -LOCAL_LDFLAGS := -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) - - diff --git a/modules/utility/source/coder.cc b/modules/utility/source/coder.cc deleted file mode 100644 index b858da14e..000000000 --- a/modules/utility/source/coder.cc +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "coder.h" -#include "common_types.h" -#include "module_common_types.h" - -// OS independent case insensitive string comparison. -#ifdef WIN32 - #define STR_CASE_CMP(x,y) ::_stricmp(x,y) -#else - #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) -#endif - -namespace webrtc { -AudioCoder::AudioCoder(WebRtc_UWord32 instanceID) - : _instanceID(instanceID), - _acm(AudioCodingModule::Create(instanceID)), - _receiveCodec(), - _encodeTimestamp(0), - _encodedData(NULL), - _encodedLengthInBytes(0), - _decodeTimestamp(0) -{ - _acm->InitializeSender(); - _acm->InitializeReceiver(); - _acm->RegisterTransportCallback(this); -} - -AudioCoder::~AudioCoder() -{ - AudioCodingModule::Destroy(_acm); -} - -WebRtc_Word32 AudioCoder::SetEncodeCodec(const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat) -{ - if(_acm->RegisterSendCodec((CodecInst&)codecInst) == -1) - { - return -1; - } - return 0; -} - -WebRtc_Word32 AudioCoder::SetDecodeCodec(const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat) -{ - if(_acm->RegisterReceiveCodec((CodecInst&)codecInst) == -1) - { - return -1; - } - memcpy(&_receiveCodec,&codecInst,sizeof(CodecInst)); - return 0; -} - -WebRtc_Word32 AudioCoder::Decode(AudioFrame& decodedAudio, - WebRtc_UWord32 sampFreqHz, - const WebRtc_Word8* incomingPayload, - WebRtc_Word32 payloadLength) -{ - if (payloadLength > 0) - { - const WebRtc_UWord8 payloadType = _receiveCodec.pltype; - _decodeTimestamp += _receiveCodec.pacsize; - if(_acm->IncomingPayload(incomingPayload, - payloadLength, - payloadType, - _decodeTimestamp) == -1) - { - return -1; - } - } - return _acm->PlayoutData10Ms((WebRtc_UWord16)sampFreqHz, - (AudioFrame&)decodedAudio); -} - -WebRtc_Word32 AudioCoder::PlayoutData(AudioFrame& decodedAudio, - WebRtc_UWord16& sampFreqHz) -{ - return _acm->PlayoutData10Ms(sampFreqHz, (AudioFrame&)decodedAudio); -} - -WebRtc_Word32 AudioCoder::Encode(const AudioFrame& audio, - WebRtc_Word8* encodedData, - WebRtc_UWord32& encodedLengthInBytes) -{ - // Fake a timestamp in case audio doesn't contain a correct timestamp. - // Make a local copy of the audio frame since audio is const - AudioFrame audioFrame = audio; - audioFrame._timeStamp = _encodeTimestamp; - _encodeTimestamp += audioFrame._payloadDataLengthInSamples; - - // For any codec with a frame size that is longer than 10 ms the encoded - // length in bytes should be zero until a a full frame has been encoded. - _encodedLengthInBytes = 0; - if(_acm->Add10MsData((AudioFrame&)audioFrame) == -1) - { - return -1; - } - _encodedData = encodedData; - if(_acm->Process() == -1) - { - return -1; - } - encodedLengthInBytes = _encodedLengthInBytes; - return 0; -} - -WebRtc_Word32 AudioCoder::SendData( - FrameType /* frameType */, - WebRtc_UWord8 /* payloadType */, - WebRtc_UWord32 /* timeStamp */, - const WebRtc_UWord8* payloadData, - WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* /* fragmentation*/) -{ - memcpy(_encodedData,payloadData,sizeof(WebRtc_UWord8) * payloadSize); - _encodedLengthInBytes = payloadSize; - return 0; -} -} // namespace webrtc diff --git a/modules/utility/source/coder.h b/modules/utility/source/coder.h deleted file mode 100644 index e96f455ca..000000000 --- a/modules/utility/source/coder.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_CODER_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_CODER_H_ - -#include "audio_coding_module.h" -#include "common_types.h" -#include "typedefs.h" - -namespace webrtc { -class AudioFrame; - -class AudioCoder : public AudioPacketizationCallback -{ -public: - AudioCoder(WebRtc_UWord32 instanceID); - ~AudioCoder(); - - WebRtc_Word32 SetEncodeCodec( - const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); - - WebRtc_Word32 SetDecodeCodec( - const CodecInst& codecInst, - ACMAMRPackingFormat amrFormat = AMRBandwidthEfficient); - - WebRtc_Word32 Decode(AudioFrame& decodedAudio, WebRtc_UWord32 sampFreqHz, - const WebRtc_Word8* incomingPayload, - WebRtc_Word32 payloadLength); - - WebRtc_Word32 PlayoutData(AudioFrame& decodedAudio, - WebRtc_UWord16& sampFreqHz); - - WebRtc_Word32 Encode(const AudioFrame& audio, - WebRtc_Word8* encodedData, - WebRtc_UWord32& encodedLengthInBytes); - -protected: - virtual WebRtc_Word32 SendData(FrameType frameType, - WebRtc_UWord8 payloadType, - WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - WebRtc_UWord16 payloadSize, - const RTPFragmentationHeader* fragmentation); - -private: - WebRtc_UWord32 _instanceID; - - AudioCodingModule* _acm; - - CodecInst _receiveCodec; - - WebRtc_UWord32 _encodeTimestamp; - WebRtc_Word8* _encodedData; - WebRtc_UWord32 _encodedLengthInBytes; - - WebRtc_UWord32 _decodeTimestamp; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UTILITY_SOURCE_CODER_H_ diff --git a/modules/utility/source/file_player_impl.cc b/modules/utility/source/file_player_impl.cc deleted file mode 100644 index 9e3f02fff..000000000 --- a/modules/utility/source/file_player_impl.cc +++ /dev/null @@ -1,725 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "file_player_impl.h" -#include "trace.h" - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - #include "cpu_wrapper.h" - #include "frame_scaler.h" - #include "tick_util.h" - #include "video_coder.h" -#endif - -// OS independent case insensitive string comparison. -#ifdef WIN32 - #define STR_CASE_CMP(x,y) ::_stricmp(x,y) -#else - #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) -#endif - -namespace webrtc { -FilePlayer* FilePlayer::CreateFilePlayer(WebRtc_UWord32 instanceID, - FileFormats fileFormat) -{ - switch(fileFormat) - { - case kFileFormatWavFile: - case kFileFormatCompressedFile: - case kFileFormatPreencodedFile: - case kFileFormatPcm16kHzFile: - case kFileFormatPcm8kHzFile: - case kFileFormatPcm32kHzFile: - // audio formats - return new FilePlayerImpl(instanceID, fileFormat); -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - return new VideoFilePlayerImpl(instanceID, fileFormat); -#endif - default: - return NULL; - } -} - -void FilePlayer::DestroyFilePlayer(FilePlayer* player) -{ - delete player; -} - -FilePlayerImpl::FilePlayerImpl(const WebRtc_UWord32 instanceID, - const FileFormats fileFormat) - : _instanceID(instanceID), - _fileFormat(fileFormat), - _fileModule(*MediaFile::CreateMediaFile(instanceID)), - _decodedLengthInMS(0), - _audioDecoder(instanceID), - _codec(), - _numberOf10MsPerFrame(0), - _numberOf10MsInDecoder(0), - _scaling(1.0) -{ - _codec.plfreq = 0; -} - -FilePlayerImpl::~FilePlayerImpl() -{ - MediaFile::DestroyMediaFile(&_fileModule); -} - -WebRtc_Word32 FilePlayerImpl::Frequency() const -{ - if(_codec.plfreq == 0) - { - return -1; - } - // Make sure that sample rate is 8,16 or 32 kHz. E.g. WAVE files may have - // other sampling rates. - if(_codec.plfreq == 11000) - { - return 16000; - } - else if(_codec.plfreq == 22000) - { - return 32000; - } - else if(_codec.plfreq == 44000) - { - return 32000; - } - else if(_codec.plfreq == 48000) - { - return 32000; - } - else - { - return _codec.plfreq; - } -} - -WebRtc_Word32 FilePlayerImpl::AudioCodec(CodecInst& audioCodec) const -{ - audioCodec = _codec; - return 0; -} - -WebRtc_Word32 FilePlayerImpl::Get10msAudioFromFile( - WebRtc_Word16* outBuffer, - WebRtc_UWord32& lengthInSamples, - WebRtc_UWord32 frequencyInHz) -{ - if(_codec.plfreq == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::Get10msAudioFromFile() playing not started!\ - codecFreq = %d, wantedFreq = %d", - _codec.plfreq, frequencyInHz); - return -1; - } - - AudioFrame unresampledAudioFrame; - if(STR_CASE_CMP(_codec.plname, "L16") == 0) - { - unresampledAudioFrame._frequencyInHz = _codec.plfreq; - - // L16 is un-encoded data. Just pull 10 ms. - WebRtc_UWord32 lengthInBytes = - sizeof(unresampledAudioFrame._payloadData); - if (_fileModule.PlayoutAudioData( - (WebRtc_Word8*)unresampledAudioFrame._payloadData, - lengthInBytes) == -1) - { - // End of file reached. - return -1; - } - if(lengthInBytes == 0) - { - lengthInSamples = 0; - return 0; - } - // One sample is two bytes. - unresampledAudioFrame._payloadDataLengthInSamples = - (WebRtc_UWord16)lengthInBytes >> 1; - - }else { - // Decode will generate 10 ms of audio data. PlayoutAudioData(..) - // expects a full frame. If the frame size is larger than 10 ms, - // PlayoutAudioData(..) data should be called proportionally less often. - WebRtc_Word16 encodedBuffer[MAX_AUDIO_BUFFER_IN_SAMPLES]; - WebRtc_UWord32 encodedLengthInBytes = 0; - if(++_numberOf10MsInDecoder >= _numberOf10MsPerFrame) - { - _numberOf10MsInDecoder = 0; - WebRtc_UWord32 bytesFromFile = sizeof(encodedBuffer); - if (_fileModule.PlayoutAudioData((WebRtc_Word8*)encodedBuffer, - bytesFromFile) == -1) - { - // End of file reached. - return -1; - } - encodedLengthInBytes = bytesFromFile; - } - if(_audioDecoder.Decode(unresampledAudioFrame,frequencyInHz, - (WebRtc_Word8*)encodedBuffer, - encodedLengthInBytes) == -1) - { - return -1; - } - } - - int outLen = 0; - if(_resampler.ResetIfNeeded(unresampledAudioFrame._frequencyInHz, - frequencyInHz, kResamplerSynchronous)) - { - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::Get10msAudioFromFile() unexpected codec"); - - // New sampling frequency. Update state. - outLen = frequencyInHz / 100; - memset(outBuffer, 0, outLen * sizeof(WebRtc_Word16)); - return 0; - } - _resampler.Push(unresampledAudioFrame._payloadData, - unresampledAudioFrame._payloadDataLengthInSamples, - outBuffer, - MAX_AUDIO_BUFFER_IN_SAMPLES, - outLen); - - lengthInSamples = outLen; - - if(_scaling != 1.0) - { - for (int i = 0;i < outLen; i++) - { - outBuffer[i] = (WebRtc_Word16)(outBuffer[i] * _scaling); - } - } - _decodedLengthInMS += 10; - return 0; -} - -WebRtc_Word32 FilePlayerImpl::RegisterModuleFileCallback(FileCallback* callback) -{ - return _fileModule.SetModuleFileCallback(callback); -} - -WebRtc_Word32 FilePlayerImpl::SetAudioScaling(float scaleFactor) -{ - if((scaleFactor >= 0)&&(scaleFactor <= 2.0)) - { - _scaling = scaleFactor; - return 0; - } - WEBRTC_TRACE(kTraceWarning, kTraceVoice, _instanceID, - "FilePlayerImpl::SetAudioScaling() not allowed scale factor"); - return -1; -} - -WebRtc_Word32 FilePlayerImpl::StartPlayingFile(const WebRtc_Word8* fileName, - bool loop, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition, - const CodecInst* codecInst) -{ - if (_fileFormat == kFileFormatPcm16kHzFile || - _fileFormat == kFileFormatPcm8kHzFile|| - _fileFormat == kFileFormatPcm32kHzFile ) - { - CodecInst codecInstL16; - strncpy(codecInstL16.plname,"L16",32); - codecInstL16.pltype = 93; - codecInstL16.channels = 1; - - if (_fileFormat == kFileFormatPcm8kHzFile) - { - codecInstL16.rate = 128000; - codecInstL16.plfreq = 8000; - codecInstL16.pacsize = 80; - - } else if(_fileFormat == kFileFormatPcm16kHzFile) - { - codecInstL16.rate = 256000; - codecInstL16.plfreq = 16000; - codecInstL16.pacsize = 160; - - }else if(_fileFormat == kFileFormatPcm32kHzFile) - { - codecInstL16.rate = 512000; - codecInstL16.plfreq = 32000; - codecInstL16.pacsize = 160; - } else - { - WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, - "FilePlayerImpl::StartPlayingFile() sample frequency\ - specifed not supported for PCM format."); - return -1; - } - - if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, - _fileFormat, &codecInstL16, - startPosition, - stopPosition) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize file\ - %s playout.", fileName); - return -1; - } - SetAudioScaling(volumeScaling); - }else if(_fileFormat == kFileFormatPreencodedFile) - { - if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, - _fileFormat, codecInst) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingPreEncodedFile() failed to\ - initialize pre-encoded file %s playout.", - fileName); - return -1; - } - } else - { - CodecInst* no_inst = NULL; - if (_fileModule.StartPlayingAudioFile(fileName, notification, loop, - _fileFormat, no_inst, - startPosition, - stopPosition) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize file\ - %s playout.", fileName); - return -1; - } - SetAudioScaling(volumeScaling); - } - if (SetUpAudioDecoder() == -1) - { - StopPlayingFile(); - return -1; - } - return 0; -} - -WebRtc_Word32 FilePlayerImpl::StartPlayingFile(InStream& sourceStream, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition, - const CodecInst* codecInst) -{ - if (_fileFormat == kFileFormatPcm16kHzFile || - _fileFormat == kFileFormatPcm32kHzFile || - _fileFormat == kFileFormatPcm8kHzFile) - { - CodecInst codecInstL16; - strncpy(codecInstL16.plname,"L16",32); - codecInstL16.pltype = 93; - codecInstL16.channels = 1; - - if (_fileFormat == kFileFormatPcm8kHzFile) - { - codecInstL16.rate = 128000; - codecInstL16.plfreq = 8000; - codecInstL16.pacsize = 80; - - }else if (_fileFormat == kFileFormatPcm16kHzFile) - { - codecInstL16.rate = 256000; - codecInstL16.plfreq = 16000; - codecInstL16.pacsize = 160; - - }else if (_fileFormat == kFileFormatPcm32kHzFile) - { - codecInstL16.rate = 512000; - codecInstL16.plfreq = 32000; - codecInstL16.pacsize = 160; - }else - { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() sample frequency specifed\ - not supported for PCM format."); - return -1; - } - if (_fileModule.StartPlayingAudioStream(sourceStream, notification, - _fileFormat, &codecInstL16, - startPosition, - stopPosition) == -1) - { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ - playout."); - return -1; - } - - }else if(_fileFormat == kFileFormatPreencodedFile) - { - if (_fileModule.StartPlayingAudioStream(sourceStream, notification, - _fileFormat, codecInst) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize stream\ - playout."); - return -1; - } - } else { - CodecInst* no_inst = NULL; - if (_fileModule.StartPlayingAudioStream(sourceStream, notification, - _fileFormat, no_inst, - startPosition, - stopPosition) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVoice, _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to initialize\ - stream playout."); - return -1; - } - } - SetAudioScaling(volumeScaling); - - if (SetUpAudioDecoder() == -1) - { - StopPlayingFile(); - return -1; - } - return 0; -} - -WebRtc_Word32 FilePlayerImpl::StopPlayingFile() -{ - memset(&_codec, 0, sizeof(CodecInst)); - _numberOf10MsPerFrame = 0; - _numberOf10MsInDecoder = 0; - return _fileModule.StopPlaying(); -} - -bool FilePlayerImpl::IsPlayingFile() const -{ - return _fileModule.IsPlaying(); -} - -WebRtc_Word32 FilePlayerImpl::GetPlayoutPosition(WebRtc_UWord32& durationMs) -{ - return _fileModule.PlayoutPositionMs(durationMs); -} - -WebRtc_Word32 FilePlayerImpl::SetUpAudioDecoder() -{ - if ((_fileModule.codec_info(_codec) == -1)) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() failed to retrieve Codec info\ - of file data."); - return -1; - } - if( STR_CASE_CMP(_codec.plname, "L16") != 0 && - _audioDecoder.SetDecodeCodec(_codec,AMRFileStorage) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FilePlayerImpl::StartPlayingFile() codec %s not supported", - _codec.plname); - return -1; - } - _numberOf10MsPerFrame = _codec.pacsize / (_codec.plfreq / 100); - _numberOf10MsInDecoder = 0; - return 0; -} - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -VideoFilePlayerImpl::VideoFilePlayerImpl(WebRtc_UWord32 instanceID, - FileFormats fileFormat) - : FilePlayerImpl(instanceID,fileFormat), - _videoDecoder(*new VideoCoder(instanceID)), - _decodedVideoFrames(0), - _encodedData(*new EncodedVideoData()), - _frameScaler(*new FrameScaler()), - _critSec(*CriticalSectionWrapper::CreateCriticalSection()), - _accumulatedRenderTimeMs(0), - _numberOfFramesRead(0), - _videoOnly(false) -{ - memset(&video_codec_info_, 0, sizeof(video_codec_info_)); -} - -VideoFilePlayerImpl::~VideoFilePlayerImpl() -{ - delete &_critSec; - delete &_frameScaler; - delete &_videoDecoder; - delete &_encodedData; -} - -WebRtc_Word32 VideoFilePlayerImpl::StartPlayingVideoFile( - const WebRtc_Word8* fileName, - bool loop, - bool videoOnly) -{ - CriticalSectionScoped lock( _critSec); - - if(_fileModule.StartPlayingVideoFile(fileName, loop, videoOnly, - _fileFormat) != 0) - { - return -1; - } - - _decodedVideoFrames = 0; - _accumulatedRenderTimeMs = 0; - _frameLengthMS = 0; - _numberOfFramesRead = 0; - _videoOnly = videoOnly; - - // Set up video_codec_info_ according to file, - if(SetUpVideoDecoder() != 0) - { - StopPlayingFile(); - return -1; - } - if(!videoOnly) - { - // Set up _codec according to file, - if(SetUpAudioDecoder() != 0) - { - StopPlayingFile(); - return -1; - } - } - return 0; -} - -WebRtc_Word32 VideoFilePlayerImpl::StopPlayingFile() -{ - CriticalSectionScoped lock( _critSec); - - _decodedVideoFrames = 0; - _videoDecoder.Reset(); - - return FilePlayerImpl::StopPlayingFile(); -} - -WebRtc_Word32 VideoFilePlayerImpl::GetVideoFromFile(VideoFrame& videoFrame, - WebRtc_UWord32 outWidth, - WebRtc_UWord32 outHeight) -{ - CriticalSectionScoped lock( _critSec); - - WebRtc_Word32 retVal = GetVideoFromFile(videoFrame); - if(retVal != 0) - { - return retVal; - } - if( videoFrame.Length() > 0) - { - retVal = _frameScaler.ResizeFrameIfNeeded(videoFrame, outWidth, - outHeight); - } - return retVal; -} - -WebRtc_Word32 VideoFilePlayerImpl::GetVideoFromFile(VideoFrame& videoFrame) -{ - CriticalSectionScoped lock( _critSec); - // No new video data read from file. - if(_encodedData.payloadSize == 0) - { - videoFrame.SetLength(0); - return -1; - } - WebRtc_Word32 retVal = 0; - if(strncmp(video_codec_info_.plName, "I420", 5) == 0) - { - videoFrame.CopyFrame(_encodedData.payloadSize,_encodedData.payloadData); - videoFrame.SetLength(_encodedData.payloadSize); - videoFrame.SetWidth(video_codec_info_.width); - videoFrame.SetHeight(video_codec_info_.height); - }else - { - // Set the timestamp manually since there is no timestamp in the file. - // Update timestam according to 90 kHz stream. - _encodedData.timeStamp += (90000 / video_codec_info_.maxFramerate); - retVal = _videoDecoder.Decode(videoFrame, _encodedData); - } - - WebRtc_Word64 renderTimeMs = TickTime::MillisecondTimestamp(); - videoFrame.SetRenderTime(renderTimeMs); - - // Indicate that the current frame in the encoded buffer is old/has - // already been read. - _encodedData.payloadSize = 0; - if( retVal == 0) - { - _decodedVideoFrames++; - } - return retVal; -} - -WebRtc_Word32 VideoFilePlayerImpl::video_codec_info( - VideoCodec& videoCodec) const -{ - if(video_codec_info_.plName[0] == 0) - { - return -1; - } - memcpy(&videoCodec, &video_codec_info_, sizeof(VideoCodec)); - return 0; -} - -WebRtc_Word32 VideoFilePlayerImpl::TimeUntilNextVideoFrame() -{ - if(_fileFormat != kFileFormatAviFile) - { - return -1; - } - if(!_fileModule.IsPlaying()) - { - return -1; - } - if(_encodedData.payloadSize <= 0) - { - // Read next frame from file. - CriticalSectionScoped lock( _critSec); - - if(_fileFormat == kFileFormatAviFile) - { - // Get next video frame - WebRtc_UWord32 encodedBufferLengthInBytes = _encodedData.bufferSize; - if(_fileModule.PlayoutAVIVideoData( - reinterpret_cast< WebRtc_Word8*>(_encodedData.payloadData), - encodedBufferLengthInBytes) != 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::TimeUntilNextVideoFrame() error reading\ - video data"); - return -1; - } - _encodedData.payloadSize = encodedBufferLengthInBytes; - _encodedData.codec = video_codec_info_.codecType; - _numberOfFramesRead++; - - if(_accumulatedRenderTimeMs == 0) - { - _startTime = TickTime::Now(); - // This if-statement should only trigger once. - _accumulatedRenderTimeMs = 1; - } else { - // A full seconds worth of frames have been read. - if(_numberOfFramesRead % video_codec_info_.maxFramerate == 0) - { - // Frame rate is in frames per seconds. Frame length is - // calculated as an integer division which means it may - // be rounded down. Compensate for this every second. - WebRtc_UWord32 rest = 1000%_frameLengthMS; - _accumulatedRenderTimeMs += rest; - } - _accumulatedRenderTimeMs += _frameLengthMS; - } - } - } - - WebRtc_Word64 timeToNextFrame; - if(_videoOnly) - { - timeToNextFrame = _accumulatedRenderTimeMs - - (TickTime::Now() - _startTime).Milliseconds(); - - } else { - // Synchronize with the audio stream instead of system clock. - timeToNextFrame = _accumulatedRenderTimeMs - _decodedLengthInMS; - } - if(timeToNextFrame < 0) - { - return 0; - - } else if(timeToNextFrame > 0x0fffffff) - { - // Wraparound or audio stream has gone to far ahead of the video stream. - return -1; - } - return static_cast(timeToNextFrame); -} - -WebRtc_Word32 VideoFilePlayerImpl::SetUpVideoDecoder() -{ - if (_fileModule.VideoCodecInst(video_codec_info_) != 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::SetVideoDecoder() failed to retrieve Codec info of\ - file data."); - return -1; - } - - WebRtc_Word32 useNumberOfCores = 1; - if(_videoDecoder.SetDecodeCodec(video_codec_info_, useNumberOfCores) != 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - _instanceID, - "FilePlayerImpl::SetUpVideoDecoder() codec %s not supported", - video_codec_info_.plName); - return -1; - } - - if(strncmp(video_codec_info_.plName, "MP4V-ES", 8) == 0) - { - if(_videoDecoder.SetCodecConfigParameters( - video_codec_info_.plType, - video_codec_info_.codecSpecific.MPEG4.configParameters, - video_codec_info_.codecSpecific.MPEG4.configParametersSize) != - 0) - { - return -1; - } - } - - _frameLengthMS = 1000/video_codec_info_.maxFramerate; - - // Size of unencoded data (I420) should be the largest possible frame size - // in a file. - const WebRtc_UWord32 KReadBufferSize = 3 * video_codec_info_.width * - video_codec_info_.height / 2; - _encodedData.VerifyAndAllocate(KReadBufferSize); - _encodedData.encodedHeight = video_codec_info_.height; - _encodedData.encodedWidth = video_codec_info_.width; - _encodedData.payloadType = video_codec_info_.plType; - _encodedData.timeStamp = 0; - return 0; -} -#endif // WEBRTC_MODULE_UTILITY_VIDEO -} // namespace webrtc diff --git a/modules/utility/source/file_player_impl.h b/modules/utility/source/file_player_impl.h deleted file mode 100644 index bd8286172..000000000 --- a/modules/utility/source/file_player_impl.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_FILE_PLAYER_IMPL_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_FILE_PLAYER_IMPL_H_ - -#include "coder.h" -#include "common_types.h" -#include "critical_section_wrapper.h" -#include "engine_configurations.h" -#include "file_player.h" -#include "media_file_defines.h" -#include "media_file.h" -#include "resampler.h" -#include "tick_util.h" -#include "typedefs.h" - -namespace webrtc { -class VideoCoder; -class FrameScaler; - -class FilePlayerImpl : public FilePlayer -{ -public: - FilePlayerImpl(WebRtc_UWord32 instanceID, FileFormats fileFormat); - ~FilePlayerImpl(); - - // FilePlayer functions. - virtual WebRtc_Word32 Get10msAudioFromFile( - WebRtc_Word16* decodedDataBuffer, - WebRtc_UWord32& decodedDataLengthInSamples, - const WebRtc_UWord32 frequencyInHz); - virtual WebRtc_Word32 RegisterModuleFileCallback(FileCallback* callback); - virtual WebRtc_Word32 StartPlayingFile( - const WebRtc_Word8* fileName, - bool loop, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition = 0, - const CodecInst* codecInst = NULL); - virtual WebRtc_Word32 StartPlayingFile( - InStream& sourceStream, - WebRtc_UWord32 startPosition, - float volumeScaling, - WebRtc_UWord32 notification, - WebRtc_UWord32 stopPosition = 0, - const CodecInst* codecInst = NULL); - virtual WebRtc_Word32 StopPlayingFile(); - virtual bool IsPlayingFile() const; - virtual WebRtc_Word32 GetPlayoutPosition(WebRtc_UWord32& durationMs); - virtual WebRtc_Word32 AudioCodec(CodecInst& audioCodec) const; - virtual WebRtc_Word32 Frequency() const; - virtual WebRtc_Word32 SetAudioScaling(float scaleFactor); - -protected: - WebRtc_Word32 SetUpAudioDecoder(); - - WebRtc_UWord32 _instanceID; - const FileFormats _fileFormat; - MediaFile& _fileModule; - - WebRtc_UWord32 _decodedLengthInMS; - -private: - WebRtc_Word16 _decodedAudioBuffer[MAX_AUDIO_BUFFER_IN_SAMPLES]; - AudioCoder _audioDecoder; - - CodecInst _codec; - WebRtc_Word32 _numberOf10MsPerFrame; - WebRtc_Word32 _numberOf10MsInDecoder; - - Resampler _resampler; - float _scaling; -}; - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -class VideoFilePlayerImpl: public FilePlayerImpl -{ -public: - VideoFilePlayerImpl(WebRtc_UWord32 instanceID, FileFormats fileFormat); - ~VideoFilePlayerImpl(); - - // FilePlayer functions. - virtual WebRtc_Word32 TimeUntilNextVideoFrame(); - virtual WebRtc_Word32 StartPlayingVideoFile(const WebRtc_Word8* fileName, - bool loop, - bool videoOnly); - virtual WebRtc_Word32 StopPlayingFile(); - virtual WebRtc_Word32 video_codec_info(VideoCodec& videoCodec) const; - virtual WebRtc_Word32 GetVideoFromFile(VideoFrame& videoFrame); - virtual WebRtc_Word32 GetVideoFromFile(VideoFrame& videoFrame, - const WebRtc_UWord32 outWidth, - const WebRtc_UWord32 outHeight); - -private: - WebRtc_Word32 SetUpVideoDecoder(); - - VideoCoder& _videoDecoder; - VideoCodec video_codec_info_; - WebRtc_Word32 _decodedVideoFrames; - - EncodedVideoData& _encodedData; - - FrameScaler& _frameScaler; - CriticalSectionWrapper& _critSec; - TickTime _startTime; - WebRtc_Word64 _accumulatedRenderTimeMs; - WebRtc_UWord32 _frameLengthMS; - - WebRtc_Word32 _numberOfFramesRead; - bool _videoOnly; -}; -#endif //WEBRTC_MODULE_UTILITY_VIDEO - -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_SOURCE_FILE_PLAYER_IMPL_H_ diff --git a/modules/utility/source/file_recorder_impl.cc b/modules/utility/source/file_recorder_impl.cc deleted file mode 100644 index 3aaae44ab..000000000 --- a/modules/utility/source/file_recorder_impl.cc +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#include "file_recorder_impl.h" -#include "media_file.h" -#include "trace.h" - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - #include "cpu_wrapper.h" - #include "critical_section_wrapper.h" - #include "frame_scaler.h" - #include "video_coder.h" - #include "video_frames_queue.h" -#endif - -// OS independent case insensitive string comparison. -#ifdef WIN32 - #define STR_CASE_CMP(x,y) ::_stricmp(x,y) -#else - #define STR_CASE_CMP(x,y) ::strcasecmp(x,y) -#endif - -namespace webrtc { -FileRecorder* FileRecorder::CreateFileRecorder(WebRtc_UWord32 instanceID, - FileFormats fileFormat) -{ - switch(fileFormat) - { - case kFileFormatWavFile: - case kFileFormatCompressedFile: - case kFileFormatPreencodedFile: - case kFileFormatPcm16kHzFile: - case kFileFormatPcm8kHzFile: - case kFileFormatPcm32kHzFile: - return new FileRecorderImpl(instanceID, fileFormat); -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - case kFileFormatAviFile: - return new AviRecorder(instanceID, fileFormat); -#endif - default: - return NULL; - } -} - -void FileRecorder::DestroyFileRecorder(FileRecorder* recorder) -{ - delete recorder; -} - -FileRecorderImpl::FileRecorderImpl(WebRtc_UWord32 instanceID, - FileFormats fileFormat) - : _fileFormat(fileFormat), - _instanceID(instanceID), - _audioEncoder(instanceID), - _amrFormat(AMRFileStorage), - _moduleFile(MediaFile::CreateMediaFile(_instanceID)) -{ -} - -FileRecorderImpl::~FileRecorderImpl() -{ - MediaFile::DestroyMediaFile(_moduleFile); -} - -FileFormats FileRecorderImpl::RecordingFileFormat() const -{ - return _fileFormat; -} - -WebRtc_Word32 FileRecorderImpl::RegisterModuleFileCallback( - FileCallback* callback) -{ - if(_moduleFile == NULL) - { - return -1; - } - return _moduleFile->SetModuleFileCallback(callback); -} - -WebRtc_Word32 FileRecorderImpl::StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const CodecInst& codecInst, - WebRtc_UWord32 notificationTimeMs, - ACMAMRPackingFormat amrFormat) -{ - if(_moduleFile == NULL) - { - return -1; - } - codec_info_ = codecInst; - _amrFormat = amrFormat; - - WebRtc_Word32 retVal = 0; - if(_fileFormat != kFileFormatAviFile) - { - // AVI files should be started using StartRecordingVideoFile(..) all - // other formats should use this API. - retVal =_moduleFile->StartRecordingAudioFile(fileName, _fileFormat, - codecInst, - notificationTimeMs); - } - - if( retVal == 0) - { - retVal = SetUpAudioEncoder(); - } - if( retVal != 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() failed to initialize file %s for\ - recording.", - fileName); - - if(IsRecording()) - { - StopRecording(); - } - } - return retVal; -} - -WebRtc_Word32 FileRecorderImpl::StartRecordingAudioFile( - OutStream& destStream, - const CodecInst& codecInst, - WebRtc_UWord32 notificationTimeMs, - ACMAMRPackingFormat amrFormat) -{ - codec_info_ = codecInst; - _amrFormat = amrFormat; - - WebRtc_Word32 retVal = _moduleFile->StartRecordingAudioStream( - destStream, - _fileFormat, - codecInst, - notificationTimeMs); - - if( retVal == 0) - { - retVal = SetUpAudioEncoder(); - } - if( retVal != 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() failed to initialize outStream for\ - recording."); - - if(IsRecording()) - { - StopRecording(); - } - } - return retVal; -} - -WebRtc_Word32 FileRecorderImpl::StopRecording() -{ - memset(&codec_info_, 0, sizeof(CodecInst)); - return _moduleFile->StopRecording(); -} - -bool FileRecorderImpl::IsRecording() const -{ - return _moduleFile->IsRecording(); -} - -WebRtc_Word32 FileRecorderImpl::RecordAudioToFile( - const AudioFrame& incomingAudioFrame, - const TickTime* playoutTS) -{ - if (codec_info_.plfreq == 0) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::RecordAudioToFile() recording audio is not turned\ - on"); - return -1; - } - AudioFrame tempAudioFrame; - tempAudioFrame._payloadDataLengthInSamples = 0; - if( incomingAudioFrame._audioChannel == 2 && - !_moduleFile->IsStereo()) - { - // Recording mono but incoming audio is (interleaved) stereo. - tempAudioFrame._audioChannel = 1; - tempAudioFrame._frequencyInHz = incomingAudioFrame._frequencyInHz; - for (WebRtc_UWord16 i = 0; - i < (incomingAudioFrame._payloadDataLengthInSamples >> 1); i++) - { - // Sample value is the average of left and right buffer rounded to - // closest integer value. Note samples can be either 1 or 2 byte. - tempAudioFrame._payloadData[i] = - ((incomingAudioFrame._payloadData[2 * i] + - incomingAudioFrame._payloadData[(2 * i) + 1] + 1) >> 1); - } - tempAudioFrame._payloadDataLengthInSamples = - incomingAudioFrame._payloadDataLengthInSamples / 2; - } - - const AudioFrame* ptrAudioFrame = &incomingAudioFrame; - if(tempAudioFrame._payloadDataLengthInSamples != 0) - { - // If ptrAudioFrame is not empty it contains the audio to be recorded. - ptrAudioFrame = &tempAudioFrame; - } - - // Encode the audio data before writing to file. Don't encode if the codec - // is PCM. - // NOTE: stereo recording is only supported for WAV files. - // TODO (hellner): WAV expect PCM in little endian byte order. Not - // "encoding" with PCM coder should be a problem for big endian systems. - WebRtc_UWord32 encodedLenInBytes = 0; - if (_fileFormat == kFileFormatPreencodedFile || - STR_CASE_CMP(codec_info_.plname, "L16") != 0) - { - if (_audioEncoder.Encode(*ptrAudioFrame, _audioBuffer, - encodedLenInBytes) == -1) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVoice, - _instanceID, - "FileRecorder::RecordAudioToFile() codec %s not supported or\ - failed to encode stream", - codec_info_.plname); - return -1; - } - } else { - int outLen = 0; - if(ptrAudioFrame->_audioChannel == 2) - { - // ptrAudioFrame contains interleaved stereo audio. - _audioResampler.ResetIfNeeded(ptrAudioFrame->_frequencyInHz, - codec_info_.plfreq, - kResamplerSynchronousStereo); - _audioResampler.Push(ptrAudioFrame->_payloadData, - ptrAudioFrame->_payloadDataLengthInSamples, - (WebRtc_Word16*)_audioBuffer, - MAX_AUDIO_BUFFER_IN_BYTES, outLen); - } else { - _audioResampler.ResetIfNeeded(ptrAudioFrame->_frequencyInHz, - codec_info_.plfreq, - kResamplerSynchronous); - _audioResampler.Push(ptrAudioFrame->_payloadData, - ptrAudioFrame->_payloadDataLengthInSamples, - (WebRtc_Word16*)_audioBuffer, - MAX_AUDIO_BUFFER_IN_BYTES, outLen); - } - encodedLenInBytes = outLen*2; - } - - // Codec may not be operating at a frame rate of 10 ms. Whenever enough - // 10 ms chunks of data has been pushed to the encoder an encoded frame - // will be available. Wait until then. - if (encodedLenInBytes) - { - WebRtc_UWord16 msOfData = - ptrAudioFrame->_payloadDataLengthInSamples / - WebRtc_UWord16(ptrAudioFrame->_frequencyInHz / 1000); - if (WriteEncodedAudioData(_audioBuffer, - (WebRtc_UWord16)encodedLenInBytes, - msOfData, playoutTS) == -1) - { - return -1; - } - } - return 0; -} - -WebRtc_Word32 FileRecorderImpl::SetUpAudioEncoder() -{ - if (_fileFormat == kFileFormatPreencodedFile || - STR_CASE_CMP(codec_info_.plname, "L16") != 0) - { - if(_audioEncoder.SetEncodeCodec(codec_info_,_amrFormat) == -1) - { - WEBRTC_TRACE( - kTraceError, - kTraceVoice, - _instanceID, - "FileRecorder::StartRecording() codec %s not supported", - codec_info_.plname); - return -1; - } - } - return 0; -} - -WebRtc_Word32 FileRecorderImpl::codec_info(CodecInst& codecInst) const -{ - if(codec_info_.plfreq == 0) - { - return -1; - } - codecInst = codec_info_; - return 0; -} - -WebRtc_Word32 FileRecorderImpl::WriteEncodedAudioData( - const WebRtc_Word8* audioBuffer, - WebRtc_UWord16 bufferLength, - WebRtc_UWord16 /*millisecondsOfData*/, - const TickTime* /*playoutTS*/) -{ - return _moduleFile->IncomingAudioData(audioBuffer, bufferLength); -} - - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -class AudioFrameFileInfo -{ - public: - AudioFrameFileInfo(const WebRtc_Word8* audioData, - const WebRtc_UWord16 audioSize, - const WebRtc_UWord16 audioMS, - const TickTime& playoutTS) - : _audioSize(audioSize), _audioMS(audioMS) ,_playoutTS(playoutTS) - { - if(audioSize > MAX_AUDIO_BUFFER_IN_BYTES) - { - assert(false); - _audioSize = 0; - return; - } - memcpy(_audioData, audioData, audioSize); - }; - // TODO (hellner): either turn into a struct or provide get/set functions. - WebRtc_Word8 _audioData[MAX_AUDIO_BUFFER_IN_BYTES]; - WebRtc_UWord16 _audioSize; - WebRtc_UWord16 _audioMS; - TickTime _playoutTS; -}; - -AviRecorder::AviRecorder(WebRtc_UWord32 instanceID, FileFormats fileFormat) - : FileRecorderImpl(instanceID, fileFormat), - _thread( 0), - _videoOnly(false), - _timeEvent(*EventWrapper::Create()), - _critSec(*CriticalSectionWrapper::CreateCriticalSection()), - _writtenVideoFramesCounter(0), - _writtenAudioMS(0), - _writtenVideoMS(0) -{ - _videoEncoder = new VideoCoder(instanceID); - _frameScaler = new FrameScaler(); - _videoFramesQueue = new VideoFramesQueue(); - _thread = ThreadWrapper::CreateThread(Run, this, kNormalPriority, - "AviRecorder()"); -} - -AviRecorder::~AviRecorder( ) -{ - StopRecording( ); - - delete _videoEncoder; - delete _frameScaler; - delete _videoFramesQueue; - delete _thread; - delete &_timeEvent; - delete &_critSec; -} - -WebRtc_Word32 AviRecorder::StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const CodecInst& audioCodecInst, - const VideoCodec& videoCodecInst, - ACMAMRPackingFormat amrFormat, - bool videoOnly) -{ - _firstAudioFrameReceived = false; - _videoCodecInst = videoCodecInst; - _videoOnly = videoOnly; - - if(_moduleFile->StartRecordingVideoFile(fileName, _fileFormat, - audioCodecInst, videoCodecInst, - videoOnly) != 0) - { - return -1; - } - - if(!videoOnly) - { - if(FileRecorderImpl::StartRecordingAudioFile(fileName,audioCodecInst, 0, - amrFormat) !=0) - { - StopRecording(); - return -1; - } - } - if( SetUpVideoEncoder() != 0) - { - StopRecording(); - return -1; - } - if(_videoOnly) - { - // Writing to AVI file is non-blocking. - // Start non-blocking timer if video only. If recording both video and - // audio let the pushing of audio frames be the timer. - _timeEvent.StartTimer(true, 1000 / _videoCodecInst.maxFramerate); - } - StartThread(); - return 0; -} - -WebRtc_Word32 AviRecorder::StopRecording() -{ - _timeEvent.StopTimer(); - - StopThread(); - _videoEncoder->Reset(); - return FileRecorderImpl::StopRecording(); -} - -WebRtc_Word32 AviRecorder::CalcI420FrameSize( ) const -{ - return 3 * _videoCodecInst.width * _videoCodecInst.height / 2; -} - -WebRtc_Word32 AviRecorder::SetUpVideoEncoder() -{ - // Size of unencoded data (I420) should be the largest possible frame size - // in a file. - _videoMaxPayloadSize = CalcI420FrameSize(); - _videoEncodedData.VerifyAndAllocate(_videoMaxPayloadSize); - - _videoCodecInst.plType = _videoEncoder->DefaultPayloadType( - _videoCodecInst.plName); - - WebRtc_Word32 useNumberOfCores = 1; - // Set the max payload size to 16000. This means that the codec will try to - // create slices that will fit in 16000 kByte packets. However, the - // Encode() call will still generate one full frame. - if(_videoEncoder->SetEncodeCodec(_videoCodecInst, useNumberOfCores, - 16000)) - { - return -1; - } - return 0; -} - -WebRtc_Word32 AviRecorder::RecordVideoToFile(const VideoFrame& videoFrame) -{ - CriticalSectionScoped lock(_critSec); - - if(!IsRecording() || ( videoFrame.Length() == 0)) - { - return -1; - } - // The frame is written to file in AviRecorder::Process(). - WebRtc_Word32 retVal = _videoFramesQueue->AddFrame(videoFrame); - if(retVal != 0) - { - StopRecording(); - } - return retVal; -} - -bool AviRecorder::StartThread() -{ - unsigned int id; - if( _thread == 0) - { - return false; - } - - return _thread->Start(id); -} - -bool AviRecorder::StopThread() -{ - _critSec.Enter(); - - if(_thread) - { - _thread->SetNotAlive(); - - ThreadWrapper* thread = _thread; - _thread = NULL; - - _timeEvent.Set(); - - _critSec.Leave(); - - if(thread->Stop()) - { - delete thread; - } else { - return false; - } - } else { - _critSec.Leave(); - } - return true; -} - -bool AviRecorder::Run( ThreadObj threadObj) -{ - return static_cast( threadObj)->Process(); -} - -WebRtc_Word32 AviRecorder::ProcessAudio() -{ - if (_writtenVideoFramesCounter == 0) - { - // Get the most recent frame that is due for writing to file. Since - // frames are unencoded it's safe to throw away frames if necessary - // for synchronizing audio and video. - VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord(); - if(frameToProcess) - { - // Syncronize audio to the current frame to process by throwing away - // audio samples with older timestamp than the video frame. - WebRtc_UWord32 numberOfAudioElements = - _audioFramesToWrite.GetSize(); - for (WebRtc_UWord32 i = 0; i < numberOfAudioElements; ++i) - { - AudioFrameFileInfo* frameInfo = - (AudioFrameFileInfo*)_audioFramesToWrite.First()->GetItem(); - if(frameInfo) - { - if(TickTime::TicksToMilliseconds( - frameInfo->_playoutTS.Ticks()) < - frameToProcess->RenderTimeMs()) - { - delete frameInfo; - _audioFramesToWrite.PopFront(); - } else - { - break; - } - } - } - } - } - // Write all audio up to current timestamp. - WebRtc_Word32 error = 0; - WebRtc_UWord32 numberOfAudioElements = _audioFramesToWrite.GetSize(); - for (WebRtc_UWord32 i = 0; i < numberOfAudioElements; ++i) - { - AudioFrameFileInfo* frameInfo = - (AudioFrameFileInfo*)_audioFramesToWrite.First()->GetItem(); - if(frameInfo) - { - if((TickTime::Now() - frameInfo->_playoutTS).Milliseconds() > 0) - { - _moduleFile->IncomingAudioData(frameInfo->_audioData, - frameInfo->_audioSize); - _writtenAudioMS += frameInfo->_audioMS; - delete frameInfo; - _audioFramesToWrite.PopFront(); - } else { - break; - } - } else { - _audioFramesToWrite.PopFront(); - } - } - return error; -} - -bool AviRecorder::Process() -{ - switch(_timeEvent.Wait(500)) - { - case kEventSignaled: - if(_thread == NULL) - { - return false; - } - break; - case kEventError: - return false; - case kEventTimeout: - // No events triggered. No work to do. - return true; - } - CriticalSectionScoped lock( _critSec); - - // Get the most recent frame to write to file (if any). Synchronize it with - // the audio stream (if any). Synchronization the video based on its render - // timestamp (i.e. VideoFrame::RenderTimeMS()) - VideoFrame* frameToProcess = _videoFramesQueue->FrameToRecord(); - if( frameToProcess == NULL) - { - return true; - } - WebRtc_Word32 error = 0; - if(!_videoOnly) - { - if(!_firstAudioFrameReceived) - { - // Video and audio can only be synchronized if both have been - // received. - return true; - } - error = ProcessAudio(); - - while (_writtenAudioMS > _writtenVideoMS) - { - error = EncodeAndWriteVideoToFile( *frameToProcess); - if( error != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "AviRecorder::Process() error writing to file."); - break; - } else { - WebRtc_UWord32 frameLengthMS = 1000 / - _videoCodecInst.maxFramerate; - _writtenVideoFramesCounter++; - _writtenVideoMS += frameLengthMS; - // A full seconds worth of frames have been written. - if(_writtenVideoFramesCounter%_videoCodecInst.maxFramerate == 0) - { - // Frame rate is in frames per seconds. Frame length is - // calculated as an integer division which means it may - // be rounded down. Compensate for this every second. - WebRtc_UWord32 rest = 1000 % frameLengthMS; - _writtenVideoMS += rest; - } - } - } - } else { - // Frame rate is in frames per seconds. Frame length is calculated as an - // integer division which means it may be rounded down. This introduces - // drift. Once a full frame worth of drift has happened, skip writing - // one frame. Note that frame rate is in frames per second so the - // drift is completely compensated for. - WebRtc_UWord32 frameLengthMS = 1000/_videoCodecInst.maxFramerate; - WebRtc_UWord32 restMS = 1000 % frameLengthMS; - WebRtc_UWord32 frameSkip = (_videoCodecInst.maxFramerate * - frameLengthMS) / restMS; - - _writtenVideoFramesCounter++; - if(_writtenVideoFramesCounter % frameSkip == 0) - { - _writtenVideoMS += frameLengthMS; - return true; - } - - error = EncodeAndWriteVideoToFile( *frameToProcess); - if(error != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "AviRecorder::Process() error writing to file."); - } else { - _writtenVideoMS += frameLengthMS; - } - } - return error == 0; -} - -WebRtc_Word32 AviRecorder::EncodeAndWriteVideoToFile(VideoFrame& videoFrame) -{ - if(!IsRecording() || (videoFrame.Length() == 0)) - { - return -1; - } - - if(_frameScaler->ResizeFrameIfNeeded(videoFrame, _videoCodecInst.width, - _videoCodecInst.height) != 0) - { - return -1; - } - - _videoEncodedData.payloadSize = 0; - - if( STR_CASE_CMP(_videoCodecInst.plName, "I420") == 0) - { - _videoEncodedData.VerifyAndAllocate(videoFrame.Length()); - - // I420 is raw data. No encoding needed (each sample is represented by - // 1 byte so there is no difference depending on endianness). - memcpy(_videoEncodedData.payloadData, videoFrame.Buffer(), - videoFrame.Length()); - - _videoEncodedData.payloadSize = videoFrame.Length(); - _videoEncodedData.frameType = kVideoFrameKey; - }else { - if( _videoEncoder->Encode(videoFrame, _videoEncodedData) != 0) - { - return -1; - } - } - - if(_videoEncodedData.payloadSize > 0) - { - if(_moduleFile->IncomingAVIVideoData( - (WebRtc_Word8*)(_videoEncodedData.payloadData), - _videoEncodedData.payloadSize)) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, _instanceID, - "Error writing AVI file"); - return -1; - } - } else { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - _instanceID, - "FileRecorder::RecordVideoToFile() frame dropped by encoder bitrate\ - likely to low."); - } - return 0; -} - -// Store audio frame in the _audioFramesToWrite buffer. The writing to file -// happens in AviRecorder::Process(). -WebRtc_Word32 AviRecorder::WriteEncodedAudioData( - const WebRtc_Word8* audioBuffer, - WebRtc_UWord16 bufferLength, - WebRtc_UWord16 millisecondsOfData, - const TickTime* playoutTS) -{ - if (!IsRecording()) - { - return -1; - } - if (bufferLength > MAX_AUDIO_BUFFER_IN_BYTES) - { - return -1; - } - if (_videoOnly) - { - return -1; - } - if (_audioFramesToWrite.GetSize() > kMaxAudioBufferQueueLength) - { - StopRecording(); - return -1; - } - _firstAudioFrameReceived = true; - - if(playoutTS) - { - _audioFramesToWrite.PushBack(new AudioFrameFileInfo(audioBuffer, - bufferLength, - millisecondsOfData, - *playoutTS)); - } else { - _audioFramesToWrite.PushBack(new AudioFrameFileInfo(audioBuffer, - bufferLength, - millisecondsOfData, - TickTime::Now())); - } - _timeEvent.Set(); - return 0; -} - -#endif // WEBRTC_MODULE_UTILITY_VIDEO -} // namespace webrtc diff --git a/modules/utility/source/file_recorder_impl.h b/modules/utility/source/file_recorder_impl.h deleted file mode 100644 index 6b32b3076..000000000 --- a/modules/utility/source/file_recorder_impl.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This file contains a class that can write audio and/or video to file in -// multiple file formats. The unencoded input data is written to file in the -// encoded format specified. - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_FILE_RECORDER_IMPL_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_FILE_RECORDER_IMPL_H_ - -#include "coder.h" -#include "common_types.h" -#include "engine_configurations.h" -#include "event_wrapper.h" -#include "file_recorder.h" -#include "media_file_defines.h" -#include "media_file.h" -#include "module_common_types.h" -#include "resampler.h" -#include "thread_wrapper.h" -#include "tick_util.h" -#include "typedefs.h" - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - #include "frame_scaler.h" - #include "video_coder.h" - #include "video_frames_queue.h" -#endif - -namespace webrtc { -// The largest decoded frame size in samples (60ms with 32kHz sample rate). -enum { MAX_AUDIO_BUFFER_IN_SAMPLES = 60*32}; -enum { MAX_AUDIO_BUFFER_IN_BYTES = MAX_AUDIO_BUFFER_IN_SAMPLES*2}; -enum { kMaxAudioBufferQueueLength = 100 }; - -class FileRecorderImpl : public FileRecorder -{ -public: - FileRecorderImpl(WebRtc_UWord32 instanceID, FileFormats fileFormat); - virtual ~FileRecorderImpl(); - - // FileRecorder functions. - virtual WebRtc_Word32 RegisterModuleFileCallback(FileCallback* callback); - virtual FileFormats RecordingFileFormat() const; - virtual WebRtc_Word32 StartRecordingAudioFile( - const WebRtc_Word8* fileName, - const CodecInst& codecInst, - WebRtc_UWord32 notificationTimeMs, - ACMAMRPackingFormat amrFormat = AMRFileStorage); - virtual WebRtc_Word32 StartRecordingAudioFile( - OutStream& destStream, - const CodecInst& codecInst, - WebRtc_UWord32 notificationTimeMs, - ACMAMRPackingFormat amrFormat = AMRFileStorage); - virtual WebRtc_Word32 StopRecording(); - virtual bool IsRecording() const; - virtual WebRtc_Word32 codec_info(CodecInst& codecInst) const; - virtual WebRtc_Word32 RecordAudioToFile( - const AudioFrame& frame, - const TickTime* playoutTS = NULL); - virtual WebRtc_Word32 StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const CodecInst& audioCodecInst, - const VideoCodec& videoCodecInst, - ACMAMRPackingFormat amrFormat = AMRFileStorage, - bool videoOnly = false) - { - return -1; - } - virtual WebRtc_Word32 RecordVideoToFile(const VideoFrame& videoFrame) - { - return -1; - } - -protected: - virtual WebRtc_Word32 WriteEncodedAudioData( - const WebRtc_Word8* audioBuffer, - WebRtc_UWord16 bufferLength, - WebRtc_UWord16 millisecondsOfData, - const TickTime* playoutTS); - - WebRtc_Word32 SetUpAudioEncoder(); - - WebRtc_UWord32 _instanceID; - FileFormats _fileFormat; - MediaFile* _moduleFile; - -private: - OutStream* _stream; - CodecInst codec_info_; - ACMAMRPackingFormat _amrFormat; - - WebRtc_Word8 _audioBuffer[MAX_AUDIO_BUFFER_IN_BYTES]; - AudioCoder _audioEncoder; - Resampler _audioResampler; -}; - - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -class AviRecorder : public FileRecorderImpl -{ -public: - AviRecorder(WebRtc_UWord32 instanceID, FileFormats fileFormat); - virtual ~AviRecorder(); - - // FileRecorder functions. - virtual WebRtc_Word32 StartRecordingVideoFile( - const WebRtc_Word8* fileName, - const CodecInst& audioCodecInst, - const VideoCodec& videoCodecInst, - ACMAMRPackingFormat amrFormat = AMRFileStorage, - bool videoOnly = false); - virtual WebRtc_Word32 StopRecording(); - virtual WebRtc_Word32 RecordVideoToFile(const VideoFrame& videoFrame); - -protected: - virtual WebRtc_Word32 WriteEncodedAudioData( - const WebRtc_Word8* audioBuffer, - WebRtc_UWord16 bufferLength, - WebRtc_UWord16 millisecondsOfData, - const TickTime* playoutTS); -private: - static bool Run(ThreadObj threadObj); - bool Process(); - - bool StartThread(); - bool StopThread(); - - WebRtc_Word32 EncodeAndWriteVideoToFile(VideoFrame& videoFrame); - WebRtc_Word32 ProcessAudio(); - - WebRtc_Word32 CalcI420FrameSize() const; - WebRtc_Word32 SetUpVideoEncoder(); - - VideoCodec _videoCodecInst; - bool _videoOnly; - - ListWrapper _audioFramesToWrite; - bool _firstAudioFrameReceived; - - VideoFramesQueue* _videoFramesQueue; - - FrameScaler* _frameScaler; - VideoCoder* _videoEncoder; - WebRtc_Word32 _videoMaxPayloadSize; - EncodedVideoData _videoEncodedData; - - ThreadWrapper* _thread; - EventWrapper& _timeEvent; - CriticalSectionWrapper& _critSec; - WebRtc_Word64 _writtenVideoFramesCounter; - WebRtc_Word64 _writtenAudioMS; - WebRtc_Word64 _writtenVideoMS; -}; -#endif // WEBRTC_MODULE_UTILITY_VIDEO -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_SOURCE_FILE_RECORDER_IMPL_H_ diff --git a/modules/utility/source/frame_scaler.cc b/modules/utility/source/frame_scaler.cc deleted file mode 100644 index e3ec0b076..000000000 --- a/modules/utility/source/frame_scaler.cc +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO -#include "frame_scaler.h" - -#include "trace.h" -#include "vplib.h" - -#ifndef NO_INTERPOLATOR - #include "InterpolatorInterface.h" -#endif - -namespace webrtc { -FrameScaler::FrameScaler() - : _ptrVideoInterpolator(0), - _outWidth(0), - _outHeight(0), - _inWidth(0), - _inHeight(0) -{ -} - -FrameScaler::~FrameScaler( ) -{ -#ifndef NO_INTERPOLATOR - if( _ptrVideoInterpolator != 0) - { - deleteInterpolator(_ptrVideoInterpolator); - } - #endif -} - -WebRtc_Word32 FrameScaler::ResizeFrameIfNeeded(VideoFrame& videoFrame, - WebRtc_UWord32 outWidth, - WebRtc_UWord32 outHeight) -{ - if( videoFrame.Length( ) == 0) - { - return -1; - } - - if((videoFrame.Width() != outWidth) || ( videoFrame.Height() != outHeight)) - { - // Scale down by factor 2-4. - if(videoFrame.Width() % outWidth == 0 && - videoFrame.Height() % outHeight == 0 && - (videoFrame.Width() / outWidth) == (videoFrame.Height() / outHeight)) - { - const WebRtc_Word32 multiple = videoFrame.Width() / outWidth; - WebRtc_UWord32 scaledWidth; - WebRtc_UWord32 scaledHeight; - switch(multiple) - { - case 2: - ScaleI420FrameQuarter(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer()); - - videoFrame.SetLength(outWidth * outHeight * 3 / 2); - videoFrame.SetWidth( outWidth); - videoFrame.SetHeight(outHeight); - return 0; - case 3: - ScaleI420Down1_3(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer(), videoFrame.Size(), - scaledWidth, scaledHeight); - videoFrame.SetLength((outWidth * outHeight * 3) / 2); - videoFrame.SetWidth(outWidth); - videoFrame.SetHeight(outHeight); - return 0; - case 4: - ScaleI420FrameQuarter(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer()); - - ScaleI420FrameQuarter(videoFrame.Width() >> 1, - videoFrame.Height() >> 1, - videoFrame.Buffer()); - - videoFrame.SetLength((outWidth * outHeight * 3)/ 2); - videoFrame.SetWidth(outWidth); - videoFrame.SetHeight(outHeight); - return 0; - default: - break; - } - } - // Scale up by factor 2-4. - if(outWidth % videoFrame.Width() == 0 && - outHeight % videoFrame.Height() == 0 && - (outWidth / videoFrame.Width()) == (outHeight / videoFrame.Height())) - { - const WebRtc_Word32 multiple = outWidth / videoFrame.Width(); - WebRtc_UWord32 scaledWidth = 0; - WebRtc_UWord32 scaledHeight = 0; - switch(multiple) - { - case 2: - videoFrame.VerifyAndAllocate((outHeight * outWidth * 3) / 2); - ScaleI420Up2(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer(), videoFrame.Size(), - scaledWidth, scaledHeight); - videoFrame.SetLength((outWidth * outHeight * 3) / 2); - videoFrame.SetWidth(outWidth); - videoFrame.SetHeight(outHeight); - return 0; - case 3: - videoFrame.VerifyAndAllocate((outWidth * outHeight * 3) / 2); - ScaleI420Up2(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer(), videoFrame.Size(), - scaledWidth, scaledHeight); - - ScaleI420Up3_2(scaledWidth, scaledHeight, videoFrame.Buffer(), - videoFrame.Size(), scaledWidth, scaledHeight); - videoFrame.SetLength((outWidth * outHeight * 3) / 2); - videoFrame.SetWidth(outWidth); - videoFrame.SetHeight(outHeight); - return 0; - case 4: - videoFrame.VerifyAndAllocate((outWidth * outHeight * 3) / 2); - ScaleI420Up2(videoFrame.Width(), videoFrame.Height(), - videoFrame.Buffer(), videoFrame.Size(), - scaledWidth, scaledHeight); - ScaleI420Up2(scaledWidth, scaledHeight, videoFrame.Buffer(), - videoFrame.Size(), scaledWidth, scaledHeight); - videoFrame.SetLength((outWidth * outHeight * 3) / 2); - videoFrame.SetWidth(outWidth); - videoFrame.SetHeight(outHeight); - return 0; - default: - break; - } - } - // Use interpolator -#ifdef NO_INTERPOLATOR - assert(!"Interpolation not available"); -#else - // Create new interpolator if the scaling changed. - if((_outWidth != outWidth) || (_outHeight != outHeight) || - (_inWidth != videoFrame.Width()) || - (_inHeight != videoFrame.Height())) - { - if(_ptrVideoInterpolator != 0) - { - deleteInterpolator(_ptrVideoInterpolator); - _ptrVideoInterpolator = 0; - } - - _outWidth = outWidth; - _outHeight = outHeight; - _inWidth = videoFrame.Width(); - _inHeight = videoFrame.Height(); - } - - - if (!_ptrVideoInterpolator) - { - InterpolatorType interpolator = BiCubicBSpline; - - if((_inWidth > ( _outWidth * 2)) || - (_inWidth < ( _outWidth / 2)) || - (_inHeight > ( _outHeight * 2)) || - (_inHeight < ( _outHeight / 2))) - - { - interpolator = BiCubicSine; - } - - VideoFrameFormat inputFormat; - VideoFrameFormat outputFormat; - - inputFormat.videoType = YUV420P; - inputFormat.xChannels = static_cast(_inWidth); - inputFormat.yChannels = static_cast(_inHeight); - - outputFormat.videoType = YUV420P; - outputFormat.xChannels = static_cast(_outWidth); - outputFormat.yChannels = static_cast(_outHeight); - - _interpolatorBuffer.VerifyAndAllocate(_outWidth * _outHeight * - 3 / 2); - - _ptrVideoInterpolator = createInterpolator( - interpolator, - &inputFormat, - &outputFormat); - if (_ptrVideoInterpolator == NULL) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "FrameScaler::ResizeFrame(): Could not create\ - interpolator"); - return -1; - } - } - - interpolateFrame(_ptrVideoInterpolator, videoFrame.Buffer(), - _interpolatorBuffer.Buffer()); - - videoFrame.VerifyAndAllocate(_interpolatorBuffer.Size()); - videoFrame.SetLength(_outWidth * _outHeight * 3 / 2); - videoFrame.CopyFrame(videoFrame.Length(), _interpolatorBuffer.Buffer()); - videoFrame.SetWidth(_outWidth); - videoFrame.SetHeight(_outHeight); -#endif // NO_INTERPOLATOR - } - return 0; -} -} // namespace webrtc - -#endif // WEBRTC_MODULE_UTILITY_VIDEO diff --git a/modules/utility/source/frame_scaler.h b/modules/utility/source/frame_scaler.h deleted file mode 100644 index 2f295c72a..000000000 --- a/modules/utility/source/frame_scaler.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This file implements a class that can be used for scaling frames. -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_FRAME_SCALER_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_FRAME_SCALER_H_ - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - -#include "engine_configurations.h" -#include "module_common_types.h" -#include "typedefs.h" - -namespace webrtc -{ -// TODO (perkj): add interpolator. Current implementation only support scaling -// (up or down) where the width and height are scaled by a constant factor 2-4. -// Also remove NO_INTERPOLATOR. - -// Disable usage of the old intertpolator implementation. -#define NO_INTERPOLATOR 1 - - -class VideoFrame; -class FrameScaler -{ -public: - FrameScaler(); - ~FrameScaler(); - - // Re-size videoFrame so that it has the width outWidth and height - // outHeight. - WebRtc_Word32 ResizeFrameIfNeeded(VideoFrame& videoFrame, - WebRtc_UWord32 outWidth, - WebRtc_UWord32 outHeight); -private: - typedef WebRtc_Word8* VideoInterpolator; - VideoInterpolator*_ptrVideoInterpolator; - - VideoFrame _interpolatorBuffer; - WebRtc_UWord32 _outWidth; - WebRtc_UWord32 _outHeight; - WebRtc_UWord32 _inWidth; - WebRtc_UWord32 _inHeight; - -}; -#endif // WEBRTC_MODULE_UTILITY_VIDEO -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_SOURCE_FRAME_SCALER_H_ diff --git a/modules/utility/source/process_thread_impl.cc b/modules/utility/source/process_thread_impl.cc deleted file mode 100644 index 57fe1cd0e..000000000 --- a/modules/utility/source/process_thread_impl.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "process_thread_impl.h" -#include "module.h" -#include "trace.h" - -namespace webrtc { -ProcessThread::~ProcessThread() -{ -} - -ProcessThread* ProcessThread::CreateProcessThread() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "CreateProcessThread()"); - return new ProcessThreadImpl(); -} - -void ProcessThread::DestroyProcessThread(ProcessThread* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "DestroyProcessThread()"); - delete module; -} - -ProcessThreadImpl::ProcessThreadImpl() - : _timeEvent(*EventWrapper::Create()), - _critSectModules(*CriticalSectionWrapper::CreateCriticalSection()), - _thread(NULL) -{ - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__); -} - -ProcessThreadImpl::~ProcessThreadImpl() -{ - delete &_critSectModules; - delete &_timeEvent; - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__); -} - -WebRtc_Word32 ProcessThreadImpl::Start() -{ - CriticalSectionScoped lock(_critSectModules); - if(_thread) - { - return -1; - } - _thread = ThreadWrapper::CreateThread(Run, this, kNormalPriority, - "ProcessThread"); - unsigned int id; - WebRtc_Word32 retVal = _thread->Start(id); - if(retVal >= 0) - { - return 0; - } - delete _thread; - _thread = NULL; - return -1; -} - -WebRtc_Word32 ProcessThreadImpl::Stop() -{ - _critSectModules.Enter(); - if(_thread) - { - _thread->SetNotAlive(); - - ThreadWrapper* thread = _thread; - _thread = NULL; - - _timeEvent.Set(); - _critSectModules.Leave(); - - if(thread->Stop()) - { - delete thread; - } else { - return -1; - } - } else { - _critSectModules.Leave(); - } - return 0; -} - -WebRtc_Word32 ProcessThreadImpl::RegisterModule(const Module* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, - "RegisterModule(module:0x%x)", module); - CriticalSectionScoped lock(_critSectModules); - - // Only allow module to be registered once. - ListItem* item = _modules.First(); - for(WebRtc_UWord32 i = 0; i < _modules.GetSize() && item; i++) - { - if(module == item->GetItem()) - { - return -1; - } - item = _modules.Next(item); - } - - _modules.PushFront(module); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, - "number of registered modules has increased to %d", - _modules.GetSize()); - // Wake the thread calling ProcessThreadImpl::Process() to update the - // waiting time. The waiting time for the just registered module may be - // shorter than all other registered modules. - _timeEvent.Set(); - return 0; -} - -WebRtc_Word32 ProcessThreadImpl::DeRegisterModule(const Module* module) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, - "DeRegisterModule(module:0x%x)", module); - CriticalSectionScoped lock(_critSectModules); - - ListItem* item = _modules.First(); - for(WebRtc_UWord32 i = 0; i < _modules.GetSize() && item; i++) - { - if(module == item->GetItem()) - { - int res = _modules.Erase(item); - WEBRTC_TRACE(kTraceInfo, kTraceUtility, -1, - "number of registered modules has decreased to %d", - _modules.GetSize()); - return res; - } - item = _modules.Next(item); - } - return -1; -} - -bool ProcessThreadImpl::Run(void* obj) -{ - return static_cast(obj)->Process(); -} - -bool ProcessThreadImpl::Process() -{ - // Wait for the module that should be called next, but don't block thread - // longer than 100 ms. - WebRtc_Word32 minTimeToNext = 100; - { - CriticalSectionScoped lock(_critSectModules); - ListItem* item = _modules.First(); - for(WebRtc_UWord32 i = 0; i < _modules.GetSize() && item; i++) - { - WebRtc_Word32 timeToNext = - static_cast(item->GetItem())->TimeUntilNextProcess(); - if(minTimeToNext > timeToNext) - { - minTimeToNext = timeToNext; - } - item = _modules.Next(item); - } - } - - if(minTimeToNext > 0) - { - if(kEventError == _timeEvent.Wait(minTimeToNext)) - { - return true; - } - if(!_thread) - { - return false; - } - } - { - CriticalSectionScoped lock(_critSectModules); - ListItem* item = _modules.First(); - for(WebRtc_UWord32 i = 0; i < _modules.GetSize() && item; i++) - { - WebRtc_Word32 timeToNext = - static_cast(item->GetItem())->TimeUntilNextProcess(); - if(timeToNext < 1) - { - static_cast(item->GetItem())->Process(); - } - item = _modules.Next(item); - } - } - return true; -} -} // namespace webrtc diff --git a/modules/utility/source/process_thread_impl.h b/modules/utility/source/process_thread_impl.h deleted file mode 100644 index a712ffc06..000000000 --- a/modules/utility/source/process_thread_impl.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ - -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "list_wrapper.h" -#include "process_thread.h" -#include "thread_wrapper.h" -#include "typedefs.h" - -namespace webrtc { -class ProcessThreadImpl : public ProcessThread -{ -public: - ProcessThreadImpl(); - virtual ~ProcessThreadImpl(); - - virtual WebRtc_Word32 Start(); - virtual WebRtc_Word32 Stop(); - - virtual WebRtc_Word32 RegisterModule(const Module* module); - virtual WebRtc_Word32 DeRegisterModule(const Module* module); - -protected: - static bool Run(void* obj); - - bool Process(); - -private: - EventWrapper& _timeEvent; - CriticalSectionWrapper& _critSectModules; - ListWrapper _modules; - ThreadWrapper* _thread; -}; -} // namespace webrtc - -#endif // WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_ diff --git a/modules/utility/source/rtp_dump_impl.cc b/modules/utility/source/rtp_dump_impl.cc deleted file mode 100644 index e5fe38a8d..000000000 --- a/modules/utility/source/rtp_dump_impl.cc +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_dump_impl.h" - -#include -#include - -#include "critical_section_wrapper.h" -#include "trace.h" - -#if defined(_WIN32) -#include -#include -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) -#include -#include -#include -#endif - -#if (defined(_DEBUG) && defined(_WIN32)) -#define DEBUG_PRINT(expr) OutputDebugString(##expr) -#define DEBUG_PRINTP(expr, p) \ -{ \ - char msg[128]; \ - sprintf(msg, ##expr, p); \ - OutputDebugString(msg); \ -} -#else -#define DEBUG_PRINT(expr) ((void)0) -#define DEBUG_PRINTP(expr,p) ((void)0) -#endif // defined(_DEBUG) && defined(_WIN32) - -namespace webrtc { -const WebRtc_Word8* RTPFILE_VERSION = "1.0"; -const WebRtc_UWord32 MAX_UWORD32 = 0xffffffff; - -// This stucture is specified in the rtpdump documentation. -// This struct corresponds to RD_packet_t in -// http://www.cs.columbia.edu/irt/software/rtptools/ -typedef struct -{ - // Length of packet, including this header (may be smaller than plen if not - // whole packet recorded). - WebRtc_UWord16 length; - // Actual header+payload length for RTP, 0 for RTCP. - WebRtc_UWord16 plen; - // Milliseconds since the start of recording. - WebRtc_UWord32 offset; -} rtpDumpPktHdr_t; - -RtpDump* RtpDump::CreateRtpDump() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "CreateRtpDump()"); - return new RtpDumpImpl(); -} - -void RtpDump::DestroyRtpDump(RtpDump* object) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "DestroyRtpDump()"); - delete object; -} - -RtpDumpImpl::RtpDumpImpl() - : _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _file(*FileWrapper::Create()), - _startTime(0) -{ - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s created", __FUNCTION__); -} - -RtpDump::~RtpDump() -{ -} - -RtpDumpImpl::~RtpDumpImpl() -{ - _file.Flush(); - _file.CloseFile(); - delete &_file; - delete &_critSect; - WEBRTC_TRACE(kTraceMemory, kTraceUtility, -1, "%s deleted", __FUNCTION__); -} - -WebRtc_Word32 RtpDumpImpl::Start(const WebRtc_Word8* fileNameUTF8) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "Start()"); - - if (fileNameUTF8 == NULL) - { - return -1; - } - - CriticalSectionScoped lock(_critSect); - _file.Flush(); - _file.CloseFile(); - if (_file.OpenFile(fileNameUTF8, false, false, false) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceUtility, -1, - "failed to open the specified file"); - return -1; - } - - // Store start of RTP dump (to be used for offset calculation later). - _startTime = GetTimeInMS(); - - // All rtp dump files start with #!rtpplay. - WebRtc_Word8 magic[16]; - sprintf(magic, "#!rtpplay%s \n", RTPFILE_VERSION); - _file.WriteText(magic); - - // The header according to the rtpdump documentation is sizeof(RD_hdr_t) - // which is 8 + 4 + 2 = 14 bytes for 32-bit architecture (and 22 bytes on - // 64-bit architecture). However, Wireshark use 16 bytes for the header - // regardless of if the binary is 32-bit or 64-bit. Go by the same approach - // as Wireshark since it makes more sense. - // http://wiki.wireshark.org/rtpdump explains that an additional 2 bytes - // of padding should be added to the header. - WebRtc_Word8 dummyHdr[16]; - memset(dummyHdr, 0, 16); - _file.Write(dummyHdr, sizeof(dummyHdr)); - return 0; -} - -WebRtc_Word32 RtpDumpImpl::Stop() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceUtility, -1, "Stop()"); - CriticalSectionScoped lock(_critSect); - _file.Flush(); - _file.CloseFile(); - return 0; -} - -bool RtpDumpImpl::IsActive() const -{ - CriticalSectionScoped lock(_critSect); - return _file.Open(); -} - -WebRtc_Word32 RtpDumpImpl::DumpPacket(const WebRtc_UWord8* packet, - WebRtc_UWord16 packetLength) -{ - CriticalSectionScoped lock(_critSect); - if (!IsActive()) - { - return 0; - } - - if (packet == NULL) - { - return -1; - } - - if (packetLength < 1) - { - return -1; - } - - // If the packet doesn't contain a valid RTCP header the packet will be - // considered RTP (without further verification). - bool isRTCP = RTCP(packet); - - rtpDumpPktHdr_t hdr; - WebRtc_UWord32 offset; - - // Offset is relative to when recording was started. - offset = GetTimeInMS(); - if (offset < _startTime) - { - // Compensate for wraparound. - offset += MAX_UWORD32 - _startTime + 1; - } else { - offset -= _startTime; - } - hdr.offset = RtpDumpHtonl(offset); - - hdr.length = RtpDumpHtons((WebRtc_UWord16)(packetLength + sizeof(hdr))); - if (isRTCP) - { - hdr.plen = 0; - } - else - { - hdr.plen = RtpDumpHtons((WebRtc_UWord16)packetLength); - } - _file.Write(&hdr, sizeof(hdr)); - _file.Write(packet, packetLength); - return 0; -} - -bool RtpDumpImpl::RTCP(const WebRtc_UWord8* packet) const -{ - const WebRtc_UWord8 payloadType = packet[1]; - bool is_rtcp = false; - - switch(payloadType) - { - case 192: - is_rtcp = true; - break; - case 193: case 195: - break; - case 200: case 201: case 202: case 203: - case 204: case 205: case 206: case 207: - is_rtcp = true; - break; - } - return is_rtcp; -} - -// TODO (hellner): why is TickUtil not used here? -inline WebRtc_UWord32 RtpDumpImpl::GetTimeInMS() const -{ -#if defined(_WIN32) - return timeGetTime(); -#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) - struct timeval tv; - struct timezone tz; - unsigned long val; - - gettimeofday(&tv, &tz); - val = tv.tv_sec * 1000 + tv.tv_usec / 1000; - return val; -#else - #error Either _WIN32 or LINUX or WEBRTC_MAC has to be defined! - assert(false); - return 0; -#endif -} - -inline WebRtc_UWord32 RtpDumpImpl::RtpDumpHtonl(WebRtc_UWord32 x) const -{ -#if defined(WEBRTC_BIG_ENDIAN) - return x; -#elif defined(WEBRTC_LITTLE_ENDIAN) - return (x >> 24) + ((((x >> 16) & 0xFF) << 8) + ((((x >> 8) & 0xFF) << 16) + - ((x & 0xFF) << 24))); -#else -#error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined! - assert(false); - return 0; -#endif -} - -inline WebRtc_UWord16 RtpDumpImpl::RtpDumpHtons(WebRtc_UWord16 x) const -{ -#if defined(WEBRTC_BIG_ENDIAN) - return x; -#elif defined(WEBRTC_LITTLE_ENDIAN) - return (x >> 8) + ((x & 0xFF) << 8); -#else - #error Either WEBRTC_BIG_ENDIAN or WEBRTC_LITTLE_ENDIAN has to be defined! - assert(false); - return 0; -#endif -} -} // namespace webrtc diff --git a/modules/utility/source/rtp_dump_impl.h b/modules/utility/source/rtp_dump_impl.h deleted file mode 100644 index a84e5985b..000000000 --- a/modules/utility/source/rtp_dump_impl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_RTP_DUMP_IMPL_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_RTP_DUMP_IMPL_H_ - -#include "rtp_dump.h" - -namespace webrtc { -class CriticalSectionWrapper; -class FileWrapper; -class RtpDumpImpl : public RtpDump -{ -public: - RtpDumpImpl(); - virtual ~RtpDumpImpl(); - - virtual WebRtc_Word32 Start(const WebRtc_Word8* fileNameUTF8); - virtual WebRtc_Word32 Stop(); - virtual bool IsActive() const; - virtual WebRtc_Word32 DumpPacket(const WebRtc_UWord8* packet, - WebRtc_UWord16 packetLength); -private: - // Return the system time in ms. - inline WebRtc_UWord32 GetTimeInMS() const; - // Return x in network byte order (big endian). - inline WebRtc_UWord32 RtpDumpHtonl(WebRtc_UWord32 x) const; - // Return x in network byte order (big endian). - inline WebRtc_UWord16 RtpDumpHtons(WebRtc_UWord16 x) const; - - // Return true if the packet starts with a valid RTCP header. - // Note: See ModuleRTPUtility::RTPHeaderParser::RTCP() for details on how - // to determine if the packet is an RTCP packet. - bool RTCP(const WebRtc_UWord8* packet) const; - -private: - CriticalSectionWrapper& _critSect; - FileWrapper& _file; - WebRtc_UWord32 _startTime; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_UTILITY_SOURCE_RTP_DUMP_IMPL_H_ diff --git a/modules/utility/source/utility.gyp b/modules/utility/source/utility.gyp deleted file mode 100644 index cd48c2dfd..000000000 --- a/modules/utility/source/utility.gyp +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'webrtc_utility', - 'type': '<(library)', - 'dependencies': [ - '../../audio_coding/main/source/audio_coding_module.gyp:audio_coding_module', - '../../video_coding/main/source/video_coding.gyp:webrtc_video_coding', - '../../../common_audio/resampler/main/source/resampler.gyp:resampler', - '../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'defines': [ - 'WEBRTC_MODULE_UTILITY_VIDEO', # for compiling support for video recording - ], - 'include_dirs': [ - '../interface', - '../../interface', - '../../../common_video/vplib/main/interface', - '../../media_file/interface', - '../../video_coding/main/interface' - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../interface', - '../../audio_coding/main/interface', - ], - }, - 'sources': [ - '../interface/file_player.h', - '../interface/file_recorder.h', - '../interface/process_thread.h', - '../interface/rtp_dump.h', - 'coder.cc', - 'coder.h', - 'file_player_impl.cc', - 'file_player_impl.h', - 'file_recorder_impl.cc', - 'file_recorder_impl.h', - 'process_thread_impl.cc', - 'process_thread_impl.h', - 'rtp_dump_impl.cc', - 'rtp_dump_impl.h', - # Video only - # TODO: Use some variable for building for video and voice or voice only - 'frame_scaler.cc', - 'video_coder.cc', - 'video_frames_queue.cc', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/utility/source/video_coder.cc b/modules/utility/source/video_coder.cc deleted file mode 100644 index 3cb54ab9b..000000000 --- a/modules/utility/source/video_coder.cc +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - -#include "video_coder.h" - -namespace webrtc { -VideoCoder::VideoCoder(WebRtc_UWord32 instanceID) - : _instanceID( instanceID), - _vcm(VideoCodingModule::Create(instanceID)), - _decodedVideo(0) -{ - _vcm->InitializeSender(); - _vcm->InitializeReceiver(); - - _vcm->RegisterTransportCallback(this); - _vcm->RegisterReceiveCallback(this); -} - -VideoCoder::~VideoCoder() -{ - VideoCodingModule::Destroy(_vcm); -} - -WebRtc_Word32 VideoCoder::Reset() -{ - _vcm->ResetDecoder(); - _vcm->ResetEncoder(); - - _vcm->InitializeSender(); - _vcm->InitializeReceiver(); - - _vcm->RegisterTransportCallback(this); - _vcm->RegisterReceiveCallback(this); - return 0; -} - -WebRtc_Word32 VideoCoder::SetEncodeCodec(VideoCodec& videoCodecInst, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize) -{ - if(_vcm->RegisterSendCodec(&videoCodecInst, numberOfCores, - maxPayloadSize) != VCM_OK) - { - return -1; - } - return 0; -} - - -WebRtc_Word32 VideoCoder::SetDecodeCodec(VideoCodec& videoCodecInst, - WebRtc_Word32 numberOfCores) -{ - if (videoCodecInst.plType == 0) - { - WebRtc_Word8 plType = DefaultPayloadType(videoCodecInst.plName); - if (plType == -1) - { - return -1; - } - videoCodecInst.plType = plType; - } - - if(_vcm->RegisterReceiveCodec(&videoCodecInst, numberOfCores) != VCM_OK) - { - return -1; - } - return 0; -} - - -WebRtc_Word32 VideoCoder::CodecConfigParameters(WebRtc_UWord8* buffer, - WebRtc_Word32 size) -{ - return _vcm->CodecConfigParameters(buffer, size); -} - -WebRtc_Word32 VideoCoder::SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length) -{ - return _vcm->SetCodecConfigParameters(payloadType, buffer, length); -} - -WebRtc_Word32 VideoCoder::Decode(VideoFrame& decodedVideo, - const EncodedVideoData& encodedData) -{ - decodedVideo.SetLength(0); - if(encodedData.payloadSize <= 0) - { - return -1; - } - - _decodedVideo = &decodedVideo; - if(_vcm->DecodeFromStorage(encodedData) != VCM_OK) - { - return -1; - } - return 0; -} - - -WebRtc_Word32 VideoCoder::Encode(const VideoFrame& videoFrame, - EncodedVideoData& videoEncodedData) -{ - // The AddVideoFrame(..) call will (indirectly) call SendData(). Store a - // pointer to videoFrame so that it can be updated. - _videoEncodedData = &videoEncodedData; - videoEncodedData.payloadSize = 0; - if(_vcm->AddVideoFrame(videoFrame) != VCM_OK) - { - return -1; - } - return 0; -} - -WebRtc_Word8 VideoCoder::DefaultPayloadType(const WebRtc_Word8* plName) -{ - VideoCodec tmpCodec; - WebRtc_Word32 numberOfCodecs = _vcm->NumberOfCodecs(); - for (WebRtc_UWord8 i = 0; i < numberOfCodecs; i++) - { - _vcm->Codec(i, &tmpCodec); - if(strncmp(tmpCodec.plName, plName, kPayloadNameSize) == 0) - { - return tmpCodec.plType; - } - } - return -1; -} - -WebRtc_Word32 VideoCoder::FrameToRender(VideoFrame& videoFrame) -{ - return _decodedVideo->CopyFrame(videoFrame); -} - -WebRtc_Word32 VideoCoder::SendData( - FrameType frameType, - WebRtc_UWord8 payloadType, - WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader, - const RTPVideoTypeHeader* rtpTypeHdr) -{ - // Store the data in _videoEncodedData which is a pointer to videoFrame in - // Encode(..) - _videoEncodedData->VerifyAndAllocate(payloadSize); - _videoEncodedData->frameType = frameType; - _videoEncodedData->payloadType = payloadType; - _videoEncodedData->timeStamp = timeStamp; - _videoEncodedData->fragmentationHeader = fragmentationHeader; - memcpy(_videoEncodedData->payloadData, payloadData, - sizeof(WebRtc_UWord8) * payloadSize); - _videoEncodedData->payloadSize = payloadSize; - return 0; -} -} // namespace webrtc -#endif // WEBRTC_MODULE_UTILITY_VIDEO diff --git a/modules/utility/source/video_coder.h b/modules/utility/source/video_coder.h deleted file mode 100644 index 2f3b769c1..000000000 --- a/modules/utility/source/video_coder.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_CODER_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_CODER_H_ - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - -#include "engine_configurations.h" -#include "video_coding.h" - -namespace webrtc { -class VideoCoder : public VCMPacketizationCallback, public VCMReceiveCallback -{ -public: - VideoCoder(WebRtc_UWord32 instanceID); - ~VideoCoder(); - - WebRtc_Word32 Reset(); - - WebRtc_Word32 SetEncodeCodec(VideoCodec& videoCodecInst, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize); - - - // Select the codec that should be used for decoding. videoCodecInst.plType - // will be set to the codec's default payload type. - WebRtc_Word32 SetDecodeCodec(VideoCodec& videoCodecInst, - WebRtc_Word32 numberOfCores); - - WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, - WebRtc_Word32 size); - - WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length); - - WebRtc_Word32 Decode(VideoFrame& decodedVideo, - const EncodedVideoData& encodedData); - - WebRtc_Word32 Encode(const VideoFrame& videoFrame, - EncodedVideoData& videoEncodedData); - - WebRtc_Word8 DefaultPayloadType(const WebRtc_Word8* plName); - -private: - // VCMReceiveCallback function. - // Note: called by VideoCodingModule when decoding finished. - WebRtc_Word32 FrameToRender(VideoFrame& videoFrame); - - // VCMPacketizationCallback function. - // Note: called by VideoCodingModule when encoding finished. - WebRtc_Word32 SendData( - const FrameType /*frameType*/, - const WebRtc_UWord8 /*payloadType*/, - const WebRtc_UWord32 /*timeStamp*/, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& /* fragmentationHeader*/, - const RTPVideoTypeHeader* rtpTypeHdr); - - WebRtc_UWord32 _instanceID; - VideoCodingModule* _vcm; - VideoFrame* _decodedVideo; - EncodedVideoData* _videoEncodedData; -}; -} // namespace webrtc -#endif // WEBRTC_MODULE_UTILITY_VIDEO -#endif // WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_CODER_H_ diff --git a/modules/utility/source/video_frames_queue.cc b/modules/utility/source/video_frames_queue.cc deleted file mode 100644 index ab590c451..000000000 --- a/modules/utility/source/video_frames_queue.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_frames_queue.h" - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - -#include - -#include "module_common_types.h" -#include "tick_util.h" -#include "trace.h" - -namespace webrtc { -VideoFramesQueue::VideoFramesQueue() - : _incomingFrames(), - _renderDelayMs(10) -{ -} - -VideoFramesQueue::~VideoFramesQueue() -{ - while (!_incomingFrames.Empty()) - { - ListItem* item = _incomingFrames.First(); - if (item) - { - VideoFrame* ptrFrame = static_cast(item->GetItem()); - assert(ptrFrame != NULL); - ptrFrame->Free(); - delete ptrFrame; - } - _incomingFrames.Erase(item); - } - while (!_emptyFrames.Empty()) - { - ListItem* item = _emptyFrames.First(); - if (item) - { - VideoFrame* ptrFrame = static_cast(item->GetItem()); - assert(ptrFrame != NULL); - ptrFrame->Free(); - delete ptrFrame; - } - _emptyFrames.Erase(item); - } -} - -WebRtc_Word32 VideoFramesQueue::AddFrame(const VideoFrame& newFrame) -{ - VideoFrame* ptrFrameToAdd = NULL; - // Try to re-use a VideoFrame. Only allocate new memory if it is necessary. - if (!_emptyFrames.Empty()) - { - ListItem* item = _emptyFrames.First(); - if (item) - { - ptrFrameToAdd = static_cast(item->GetItem()); - _emptyFrames.Erase(item); - } - } - if (!ptrFrameToAdd) - { - if (_emptyFrames.GetSize() + _incomingFrames.GetSize() > - KMaxNumberOfFrames) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, - "%s: too many frames, limit: %d", __FUNCTION__, - KMaxNumberOfFrames); - return -1; - } - - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, -1, - "%s: allocating buffer %d", __FUNCTION__, - _emptyFrames.GetSize() + _incomingFrames.GetSize()); - - ptrFrameToAdd = new VideoFrame(); - if (!ptrFrameToAdd) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "%s: could not create new frame for", __FUNCTION__); - return -1; - } - } - ptrFrameToAdd->CopyFrame(newFrame); - _incomingFrames.PushBack(ptrFrameToAdd); - return 0; -} - -// Find the most recent frame that has a VideoFrame::RenderTimeMs() that is -// lower than current time in ms (TickTime::MillisecondTimestamp()). -// Note _incomingFrames is sorted so that the oldest frame is first. -// Recycle all frames that are older than the most recent frame. -VideoFrame* VideoFramesQueue::FrameToRecord() -{ - VideoFrame* ptrRenderFrame = NULL; - ListItem* item = _incomingFrames.First(); - while(item) - { - VideoFrame* ptrOldestFrameInList = - static_cast(item->GetItem()); - if (ptrOldestFrameInList->RenderTimeMs() <= - TickTime::MillisecondTimestamp() + _renderDelayMs) - { - if (ptrRenderFrame) - { - // List is traversed beginning to end. If ptrRenderFrame is not - // NULL it must be the first, and thus oldest, VideoFrame in the - // queue. It can be recycled. - ReturnFrame(ptrRenderFrame); - _incomingFrames.PopFront(); - } - item = _incomingFrames.Next(item); - ptrRenderFrame = ptrOldestFrameInList; - }else - { - // All VideoFrames following this one will be even newer. No match - // will be found. - break; - } - } - return ptrRenderFrame; -} - -WebRtc_Word32 VideoFramesQueue::ReturnFrame(VideoFrame* ptrOldFrame) -{ - ptrOldFrame->SetTimeStamp(0); - ptrOldFrame->SetWidth(0); - ptrOldFrame->SetHeight(0); - ptrOldFrame->SetRenderTime(0); - ptrOldFrame->SetLength(0); - _emptyFrames.PushBack(ptrOldFrame); - return 0; -} - -// -WebRtc_Word32 VideoFramesQueue::SetRenderDelay(WebRtc_UWord32 renderDelay) -{ - _renderDelayMs = renderDelay; - return 0; -} -} // namespace webrtc -#endif // WEBRTC_MODULE_UTILITY_VIDEO diff --git a/modules/utility/source/video_frames_queue.h b/modules/utility/source/video_frames_queue.h deleted file mode 100644 index 6c9be1c87..000000000 --- a/modules/utility/source/video_frames_queue.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_FRAMES_QUEUE_H_ -#define WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_FRAMES_QUEUE_H_ - -#ifdef WEBRTC_MODULE_UTILITY_VIDEO - -#include "engine_configurations.h" -#include "list_wrapper.h" -#include "typedefs.h" - -namespace webrtc { -class VideoFrame; - -class VideoFramesQueue -{ -public: - VideoFramesQueue(); - ~VideoFramesQueue(); - - // Put newFrame (last) in the queue. - WebRtc_Word32 AddFrame(const VideoFrame& newFrame); - - // Return the most current frame. I.e. the frame with the highest - // VideoFrame::RenderTimeMs() that is lower than - // TickTime::MillisecondTimestamp(). - VideoFrame* FrameToRecord(); - - // Set the render delay estimate to renderDelay ms. - WebRtc_Word32 SetRenderDelay(WebRtc_UWord32 renderDelay); - -protected: - // Make ptrOldFrame available for re-use. I.e. put it in the empty frames - // queue. - WebRtc_Word32 ReturnFrame(VideoFrame* ptrOldFrame); - -private: - // Don't allow the buffer to expand beyond KMaxNumberOfFrames VideoFrames. - // 300 frames correspond to 10 seconds worth of frames at 30 fps. - enum {KMaxNumberOfFrames = 300}; - - // List of VideoFrame pointers. The list is sorted in the order of when the - // VideoFrame was inserted into the list. The first VideoFrame in the list - // was inserted first. - ListWrapper _incomingFrames; - // A list of frames that are free to be re-used. - ListWrapper _emptyFrames; - - // Estimated render delay. - WebRtc_UWord32 _renderDelayMs; -}; -} // namespace webrtc -#endif // WEBRTC_MODULE_UTILITY_VIDEO -#endif // WEBRTC_MODULES_UTILITY_SOURCE_VIDEO_FRAMES_QUEUE_H_ diff --git a/modules/utility/test/testAPI.cpp b/modules/utility/test/testAPI.cpp deleted file mode 100644 index 96664a3e1..000000000 --- a/modules/utility/test/testAPI.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// testAPI.cpp : Defines the entry point for the console application. -// -// NOTES: -// 1. MediaFile library and testAPI.cpp must be built in DEBUG mode for testing. -// - -#include -#include -#include - -#ifdef WIN32 - #include - #include -#endif - -#include "common_types.h" -#include "trace.h" - -#include "Engineconfigurations.h" -#include "media_file.h" -#include "file_player.h" -#include "file_recorder.h" - - -bool notify = false, playing = false, recording = false; - -// callback class for FileModule -class MyFileModuleCallback : public FileCallback -{ -public: - virtual void PlayNotification( const WebRtc_Word32 id, - const WebRtc_UWord32 durationMs ) - { - printf("\tReceived PlayNotification from module %ld, durationMs = %ld\n", - id, durationMs); - notify = true; - }; - - virtual void RecordNotification( const WebRtc_Word32 id, - const WebRtc_UWord32 durationMs ) - { - printf("\tReceived RecordNotification from module %ld, durationMs = %ld\n", - id, durationMs); - notify = true; - }; - - virtual void PlayFileEnded(const WebRtc_Word32 id) - { - printf("\tReceived PlayFileEnded notification from module %ld.\n", id); - playing = false; - }; - - virtual void RecordFileEnded(const WebRtc_Word32 id) - { - printf("\tReceived RecordFileEnded notification from module %ld.\n", id); - recording = false; - } -}; - -// main test app -#ifdef WIN32 -int _tmain(int argc, _TCHAR* argv[]) -#else -int main(int /*argc*/, char** /*argv*/) -#endif -{ - Trace::CreateTrace(); - Trace::SetTraceFile("testTrace.txt"); - Trace::SetEncryptedTraceFile("testTraceDebug.txt"); - - int playId = 1; - int recordId = 2; - - printf("Welcome to test of FilePlayer and FileRecorder\n"); - - - /////////////////////////////////////////////// - // - // avi test case 1 - // - /////////////////////////////////////////////// - - - // todo PW we need more AVI tests Mp4 - - { - FilePlayer& filePlayer(*FilePlayer::CreateFilePlayer(1, webrtc::kFileFormatAviFile)); - FileRecorder& fileRecorder(*FileRecorder::CreateFileRecorder(1, webrtc::kFileFormatAviFile)); - - const char* KFileName = "./tmpAviFileTestCase1_audioI420CIF30fps.avi"; - - printf("\tReading from an avi file and writing the information to another \n"); - printf("\tin the same format (I420 CIF 30fps) \n"); - printf("\t\t check file named %s\n", KFileName); - - assert(filePlayer.StartPlayingVideoFile( - "../../../MediaFile/main/test/files/aviTestCase1_audioI420CIF30fps.avi", - false, false) == 0); - - // init codecs - webrtc::VideoCodec videoCodec; - webrtc::VideoCodec recVideoCodec; - webrtc::CodecInst audioCodec; - assert(filePlayer.VideoCodec( videoCodec ) == 0); - assert(filePlayer.AudioCodec( audioCodec) == 0); - - recVideoCodec = videoCodec; - - assert( fileRecorder.StartRecordingVideoFile(KFileName, - audioCodec, - recVideoCodec) == 0); - - assert(fileRecorder.IsRecording()); - - WebRtc_UWord32 videoReadSize = static_cast( (videoCodec.width * videoCodec.height * 3.0) / 2.0); - - webrtc::VideoFrame videoFrame; - videoFrame.VerifyAndAllocate(videoReadSize); - - int frameCount = 0; - bool audioNotDone = true; - bool videoNotDone = true; - AudioFrame audioFrame; - - while( audioNotDone || videoNotDone) - { - if(filePlayer.TimeUntilNextVideoFrame() <= 0) - { - if(filePlayer.GetVideoFromFile( videoFrame) != 0) - { - // no more video frames - break; - } - frameCount++; - videoNotDone = ( videoFrame.Length() > 0); - videoFrame.SetRenderTime(TickTime::MillisecondTimestamp()); - if( videoNotDone) - { - assert(fileRecorder.RecordVideoToFile(videoFrame) == 0); - ::Sleep(10); - } - } - WebRtc_UWord32 decodedDataLengthInSamples; - if( 0 != filePlayer.Get10msAudioFromFile( audioFrame._payloadData, decodedDataLengthInSamples, audioCodec.plfreq)) - { - audioNotDone = false; - } else - { - audioFrame._frequencyInHz = filePlayer.Frequency(); - audioFrame._payloadDataLengthInSamples = (WebRtc_UWord16)decodedDataLengthInSamples; - fileRecorder.RecordAudioToFile(audioFrame, &TickTime::Now()); - } - } - ::Sleep(100); - assert(fileRecorder.StopRecording() == 0); - assert( !fileRecorder.IsRecording()); - assert(frameCount == 135); - printf("\tGenerated %s\n\n", KFileName); - } - /////////////////////////////////////////////// - // - // avi test case 2 - // - /////////////////////////////////////////////// - { - FilePlayer& filePlayer(*FilePlayer::CreateFilePlayer(2, webrtc::kFileFormatAviFile)); - FileRecorder& fileRecorder(*FileRecorder::CreateFileRecorder(2, webrtc::kFileFormatAviFile)); - - const char* KFileName = "./tmpAviFileTestCase2_audioI420CIF20fps.avi"; - - printf("\tWriting information to a avi file and check the written file by \n"); - printf("\treopening it and control codec information.\n"); - printf("\t\t check file named %s all frames should be light green.\n", KFileName); - // init codecs - webrtc::VideoCodec videoCodec; - webrtc::CodecInst audioCodec; - - memset(&videoCodec, 0, sizeof(videoCodec)); - - const char* KVideoCodecName = "I420"; - strcpy(videoCodec.plName, KVideoCodecName); - videoCodec.plType = 124; - videoCodec.maxFramerate = 20; - videoCodec.height = 288; - videoCodec.width = 352; - - const char* KAudioCodecName = "PCMU"; - strcpy(audioCodec.plname, KAudioCodecName); - audioCodec.pltype = 0; - audioCodec.plfreq = 8000; - audioCodec.pacsize = 80; - audioCodec.channels = 1; - audioCodec.rate = 64000; - - assert( fileRecorder.StartRecordingVideoFile( - KFileName, - audioCodec, - videoCodec) == 0); - - assert(fileRecorder.IsRecording()); - - const WebRtc_UWord32 KVideoWriteSize = static_cast< WebRtc_UWord32>( (videoCodec.width * videoCodec.height * 3) / 2); - webrtc::VideoFrame videoFrame; - - // 10 ms - AudioFrame audioFrame; - audioFrame._payloadDataLengthInSamples = audioCodec.plfreq/100; - memset(audioFrame._payloadData, 0, 2*audioFrame._payloadDataLengthInSamples); - audioFrame._frequencyInHz = 8000; - - // prepare the video frame - videoFrame.VerifyAndAllocate(KVideoWriteSize); - memset(videoFrame.Buffer(), 127, videoCodec.width * videoCodec.height); - memset(videoFrame.Buffer() +(videoCodec.width * videoCodec.height), 0, videoCodec.width * videoCodec.height/2); - videoFrame.SetLength(KVideoWriteSize); - videoFrame.SetHeight(videoCodec.height); - videoFrame.SetWidth(videoCodec.width); - - // write avi file, with 20 video frames - const int KWriteNumFrames = 20; - int writeFrameCount = 0; - while(writeFrameCount < KWriteNumFrames) - { - // add a video frame - assert(fileRecorder.RecordVideoToFile(videoFrame) == 0); - - // add 50 ms of audio - for(int i=0; i<5; i++) - { - assert( fileRecorder.RecordAudioToFile(audioFrame) == 0); - }// for i - writeFrameCount++; - } - ::Sleep(10); // enough tim eto write the queued data to the file - assert(writeFrameCount == 20); - assert(fileRecorder.StopRecording() == 0); - assert( ! fileRecorder.IsRecording()); - - assert(filePlayer.StartPlayingVideoFile(KFileName,false, false) == 0); - assert(filePlayer.IsPlayingFile( )); - - // compare codecs read from file to the ones used when writing the file - webrtc::VideoCodec readVideoCodec; - assert(filePlayer.VideoCodec( readVideoCodec ) == 0); - assert(strcmp(readVideoCodec.plName, videoCodec.plName) == 0); - assert(readVideoCodec.width == videoCodec.width); - assert(readVideoCodec.height == videoCodec.height); - assert(readVideoCodec.maxFramerate == videoCodec.maxFramerate); - - webrtc::CodecInst readAudioCodec; - assert(filePlayer.AudioCodec( readAudioCodec) == 0); - assert(strcmp(readAudioCodec.plname, audioCodec.plname) == 0); - assert(readAudioCodec.pltype == audioCodec.pltype); - assert(readAudioCodec.plfreq == audioCodec.plfreq); - assert(readAudioCodec.pacsize == audioCodec.pacsize); - assert(readAudioCodec.channels == audioCodec.channels); - assert(readAudioCodec.rate == audioCodec.rate); - - assert(filePlayer.StopPlayingFile() == 0); - assert( ! filePlayer.IsPlayingFile()); - printf("\tGenerated %s\n\n", KFileName); - } - /////////////////////////////////////////////// - // - // avi test case 3 - // - /////////////////////////////////////////////// - - { - FilePlayer& filePlayer(*FilePlayer::CreateFilePlayer(2, webrtc::kFileFormatAviFile)); - FileRecorder& fileRecorder(*FileRecorder::CreateFileRecorder(3, webrtc::kFileFormatAviFile)); - - printf("\tReading from an avi file and writing the information to another \n"); - printf("\tin a different format (H.263 CIF 30fps) \n"); - printf("\t\t check file named tmpAviFileTestCase1_audioH263CIF30fps.avi\n"); - - assert(filePlayer.StartPlayingVideoFile( - "../../../MediaFile/main/test/files/aviTestCase1_audioI420CIF30fps.avi", - false, - false) == 0); - - // init codecs - webrtc::VideoCodec videoCodec; - webrtc::VideoCodec recVideoCodec; - webrtc::CodecInst audioCodec; - assert(filePlayer.VideoCodec( videoCodec ) == 0); - assert(filePlayer.AudioCodec( audioCodec) == 0); - recVideoCodec = videoCodec; - - memcpy(recVideoCodec.plName, "H263",5); - recVideoCodec.startBitrate = 1000; - recVideoCodec.codecSpecific.H263.quality = 1; - recVideoCodec.plType = 34; - recVideoCodec.codecType = webrtc::kVideoCodecH263; - - assert( fileRecorder.StartRecordingVideoFile( - "./tmpAviFileTestCase1_audioH263CIF30fps.avi", - audioCodec, - recVideoCodec) == 0); - - assert(fileRecorder.IsRecording()); - - WebRtc_UWord32 videoReadSize = static_cast( (videoCodec.width * videoCodec.height * 3.0) / 2.0); - - webrtc::VideoFrame videoFrame; - videoFrame.VerifyAndAllocate(videoReadSize); - - int videoFrameCount = 0; - int audioFrameCount = 0; - bool audioNotDone = true; - bool videoNotDone = true; - AudioFrame audioFrame; - - while( audioNotDone || videoNotDone) - { - if(filePlayer.TimeUntilNextVideoFrame() <= 0) - { - if(filePlayer.GetVideoFromFile( videoFrame) != 0) - { - break; - } - videoFrameCount++; - videoNotDone = ( videoFrame.Length() > 0); - if( videoNotDone) - { - assert(fileRecorder.RecordVideoToFile(videoFrame) == 0); - } - } - - WebRtc_UWord32 decodedDataLengthInSamples; - if( 0 != filePlayer.Get10msAudioFromFile( audioFrame._payloadData, decodedDataLengthInSamples, audioCodec.plfreq)) - { - audioNotDone = false; - - } else - { - ::Sleep(5); - audioFrame._frequencyInHz = filePlayer.Frequency(); - audioFrame._payloadDataLengthInSamples = (WebRtc_UWord16)decodedDataLengthInSamples; - assert(0 == fileRecorder.RecordAudioToFile(audioFrame)); - - audioFrameCount++; - } - } - assert(videoFrameCount == 135); - assert(audioFrameCount == 446); // we will start & stop with a video frame - - assert(fileRecorder.StopRecording() == 0); - assert( !fileRecorder.IsRecording()); - printf("\tGenerated ./tmpAviFileTestCase1_audioH263CIF30fps.avi\n\n"); - } - - - printf("\nTEST completed.\n"); - - Trace::ReturnTrace(); - return 0; -} diff --git a/modules/video_capture/OWNERS b/modules/video_capture/OWNERS deleted file mode 100644 index 568363eab..000000000 --- a/modules/video_capture/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -mflodman@google.com -perkj@google.com diff --git a/modules/video_capture/main/interface/video_capture.h b/modules/video_capture/main/interface/video_capture.h deleted file mode 100644 index 76a6c1626..000000000 --- a/modules/video_capture/main/interface/video_capture.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_H_ - -/* - * video_capture.h - */ - -#include "module.h" -#include "video_capture_defines.h" - -namespace webrtc { -// Class definitions -class VideoCaptureModule: public Module -{ -public: - /* - * Create a video capture module object - * - * id - unique identifier of this video capture module object - * deviceUniqueIdUTF8 - name of the device. Available names can be found by using GetDeviceName - */ - static VideoCaptureModule* Create(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - - /* - * Create a video capture module object used for external capture. - * - * id - unique identifier of this video capture module object - * externalCapture - [out] interface to call when a new frame is captured. - */ - static VideoCaptureModule* Create(const WebRtc_Word32 id, - VideoCaptureExternal*& externalCapture); - - /* - * destroy a video capture module object - * - * module - object to destroy - */ - static void Destroy(VideoCaptureModule* module); - - /* Android specific function - * Set global Android VM*/ - static WebRtc_Word32 SetAndroidObjects(void* javaVM, void* javaContext); - - /* - * Returns version of the module and its components - * - * version - buffer to which the version will be written - * remainingBufferInBytes - remaining number of WebRtc_Word8 in the version buffer - * position - position of the next empty WebRtc_Word8 in the version buffer - */ - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - /* - * Change the unique identifier of this object - * - * id - new unique identifier of this video capture module object - */ - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - /************************************************************************** - * - * Device Information - * - **************************************************************************/ - - class DeviceInfo - { - public: - - virtual WebRtc_UWord32 NumberOfDevices()=0; - - /* - * Returns the available capture devices. - * deviceNumber -[in] index of capture device - * deviceNameUTF8 - friendly name of the capture device - * deviceUniqueIdUTF8 - unique name of the capture device if it exist. Otherwise same as deviceNameUTF8 - * productUniqueIdUTF8 - unique product id if it exist. Null terminated otherwise. - */ - virtual WebRtc_Word32 GetDeviceName(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8 = 0, - WebRtc_UWord32 productUniqueIdUTF8Length = 0) = 0; - - /* - * Returns the number of capabilities for this device - */ - virtual WebRtc_Word32 NumberOfCapabilities(const WebRtc_UWord8* deviceUniqueIdUTF8)=0; - - /* - * Gets the capabilities of the named device - */ - virtual WebRtc_Word32 GetCapability(const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability) = 0; - - /* - * Gets clockwise angle the captured frames should be rotated in order to be displayed - * correctly on a normally rotated display. - */ - virtual WebRtc_Word32 GetOrientation(const WebRtc_UWord8* deviceUniqueIdUTF8, - VideoCaptureRotation& orientation)=0; - - /* - * Gets the capability that best matches the requested width, height and frame rate. - * Returns the deviceCapabilityNumber on success. - */ - virtual WebRtc_Word32 GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting) = 0; - - /* - * Display OS /capture device specific settings dialog - */ - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, - void* parentWindow, WebRtc_UWord32 positionX, - WebRtc_UWord32 positionY) = 0; - - protected: - virtual ~DeviceInfo(){} - }; - - static DeviceInfo* CreateDeviceInfo(const WebRtc_Word32 id); - static void DestroyDeviceInfo(DeviceInfo* deviceInfo); - - class VideoCaptureEncodeInterface - { - public: - virtual WebRtc_Word32 ConfigureEncoder(const VideoCodec& codec, - WebRtc_UWord32 maxPayloadSize)=0; - // Inform the encoder about the new target bit rate. - // - // - newBitRate : New target bit rate in Kbit/s - // - frameRate : The target frame rate - // - virtual WebRtc_Word32 SetRates(WebRtc_Word32 newBitRate, WebRtc_Word32 frameRate) = 0; - // Inform the encoder about the packet loss - // - // - packetLoss : Fraction lost - // (loss rate in percent = 100 * packetLoss / 255) - // - virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 packetLoss) = 0; - // Encode the next frame as key frame. - // - virtual WebRtc_Word32 EncodeFrameType(const FrameType type) =0; - protected: - virtual ~VideoCaptureEncodeInterface(){} - }; - - /************************************************************************** - * - * Observers - * - ***************************************************************************/ - - /* - * Register capture data callback - */ - virtual WebRtc_Word32 RegisterCaptureDataCallback( - VideoCaptureDataCallback& dataCallback) = 0; - - /* - * Remove capture data callback - */ - virtual WebRtc_Word32 DeRegisterCaptureDataCallback() = 0; - - /* - * Register capture callback - */ - virtual WebRtc_Word32 RegisterCaptureCallback(VideoCaptureFeedBack& callBack) = 0; - - /* - * Remove capture callback - */ - virtual WebRtc_Word32 DeRegisterCaptureCallback() = 0; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - /* - * Start capture device - */ - virtual WebRtc_Word32 StartCapture(const VideoCaptureCapability& capability) = 0; - - /* - * Stop capture device - */ - virtual WebRtc_Word32 StopCapture() = 0; - - /* - * Send an image when the capture device is not running. - */ - virtual WebRtc_Word32 StartSendImage(const VideoFrame& videoFrame, - WebRtc_Word32 frameRate = 1)=0; - - /* - * Stop send image. - */ - virtual WebRtc_Word32 StopSendImage()=0; - - /************************************************************************** - * - * Properties of the set device - * - ***************************************************************************/ - /* - * Returns the name of the device used by this module. - */ - virtual const WebRtc_UWord8* CurrentDeviceName() const =0; - - /* - * Returns true if the capture device is running - */ - virtual bool CaptureStarted() = 0; - - /* - * Gets the current configuration. - */ - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings) = 0; - - virtual WebRtc_Word32 SetCaptureDelay(WebRtc_Word32 delayMS)=0; - - /* Returns the current CaptureDelay. - Only valid when the camera is running*/ - virtual WebRtc_Word32 CaptureDelay()=0; - - /* Set the rotation of the captured frames. - If the rotation is set to the same as returned by - DeviceInfo::GetOrientation the captured frames are displayed correctly if rendered.*/ - virtual WebRtc_Word32 SetCaptureRotation(VideoCaptureRotation rotation)=0; - - /* Gets a pointer to an encode interface if the capture device supports the requested type and size. - NULL otherwise. - */ - virtual VideoCaptureEncodeInterface* GetEncodeInterface(const VideoCodec& codec)= 0; - - /************************************************************************** - * Information Callbacks - * - ***************************************************************************/ - virtual WebRtc_Word32 EnableFrameRateCallback(const bool enable) = 0; - virtual WebRtc_Word32 EnableNoPictureAlarm(const bool enable) = 0; - -}; -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_H_ diff --git a/modules/video_capture/main/interface/video_capture_defines.h b/modules/video_capture/main/interface/video_capture_defines.h deleted file mode 100644 index 0a57d0696..000000000 --- a/modules/video_capture/main/interface/video_capture_defines.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_DEFINES_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_DEFINES_H_ - -// Includes -#include "typedefs.h" -#include "module_common_types.h" - -namespace webrtc -{ -// Defines -#ifndef NULL - #define NULL 0 -#endif - -enum {kVideoCaptureUniqueNameLength =1024}; //Max unique capture device name lenght -enum {kVideoCaptureDeviceNameLength =256}; //Max capture device name lenght -enum {kVideoCaptureProductIdLength =128}; //Max product id length - -// Enums -enum VideoCaptureRotation -{ - kCameraRotate0 = 0, - kCameraRotate90 = 5, - kCameraRotate180 = 10, - kCameraRotate270 = 15 -}; - -struct VideoCaptureCapability -{ - WebRtc_UWord32 width; - WebRtc_UWord32 height; - WebRtc_UWord32 maxFPS; - WebRtc_UWord32 expectedCaptureDelay; - RawVideoType rawType; - VideoCodecType codecType; - bool interlaced; - - VideoCaptureCapability() - { - width = 0; - height = 0; - maxFPS = 0; - expectedCaptureDelay = 0; - rawType = kVideoUnknown; - codecType = kVideoCodecUnknown; - interlaced = false; - } - ; - bool operator!=(const VideoCaptureCapability &other) const - { - if (width != other.width) - return true; - if (height != other.height) - return true; - if (maxFPS != other.maxFPS) - return true; - if (rawType != other.rawType) - return true; - if (codecType != other.codecType) - return true; - if (interlaced != other.interlaced) - return true; - return false; - } - bool operator==(const VideoCaptureCapability &other) const - { - return !operator!=(other); - } -}; - -enum VideoCaptureAlarm -{ - Raised = 0, - Cleared = 1 -}; - -/* External Capture interface. Returned by Create - and implemented by the capture module. - */ -class VideoCaptureExternal -{ -public: - virtual WebRtc_Word32 IncomingFrame(WebRtc_UWord8* videoFrame, - WebRtc_Word32 videoFrameLength, - const VideoCaptureCapability& frameInfo, - WebRtc_Word64 captureTime = 0) = 0; -protected: - ~VideoCaptureExternal() {} -}; - -// Callback class to be implemented by module user -class VideoCaptureDataCallback -{ -public: - virtual void OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - VideoCodecType codecType) = 0; - virtual void OnCaptureDelayChanged(const WebRtc_Word32 id, - const WebRtc_Word32 delay) = 0; -protected: - virtual ~VideoCaptureDataCallback(){} -}; - -class VideoCaptureFeedBack -{ -public: - virtual void OnCaptureFrameRate(const WebRtc_Word32 id, - const WebRtc_UWord32 frameRate) = 0; - virtual void OnNoPictureAlarm(const WebRtc_Word32 id, - const VideoCaptureAlarm alarm) = 0; -protected: - virtual ~VideoCaptureFeedBack(){} -}; - -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_INTERFACE_VIDEO_CAPTURE_DEFINES_H_ diff --git a/modules/video_capture/main/source/Android.mk b/modules/video_capture/main/source/Android.mk deleted file mode 100644 index 178984f2c..000000000 --- a/modules/video_capture/main/source/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_capture -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := video_capture_impl.cc \ - vplib_conversions.cc \ - device_info_impl.cc \ - Android/video_capture_android.cc \ - Android/device_info_android.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../source \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../audio_coding/main/interface \ - $(LOCAL_PATH)/Android - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_capture/main/source/Android/device_info_android.cc b/modules/video_capture/main/source/Android/device_info_android.cc deleted file mode 100644 index 016546539..000000000 --- a/modules/video_capture/main/source/Android/device_info_android.cc +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "video_capture_android.h" -#include "device_info_android.h" -#include - -namespace webrtc -{ -VideoCaptureModule::DeviceInfo* VideoCaptureModule::CreateDeviceInfo ( - const WebRtc_Word32 id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "%s", __FUNCTION__); - videocapturemodule::DeviceInfoAndroid *deviceInfo = - new videocapturemodule::DeviceInfoAndroid(id); - if (deviceInfo && deviceInfo->Init() != 0) // Failed to init - { - delete deviceInfo; - deviceInfo = NULL; - } - return deviceInfo; -} - -void VideoCaptureModule::DestroyDeviceInfo( - VideoCaptureModule::DeviceInfo* deviceInfo) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, -1, - "%s", __FUNCTION__); - - videocapturemodule::DeviceInfoAndroid* devInfo = - static_cast (deviceInfo); - delete devInfo; -} - -namespace videocapturemodule -{ -DeviceInfoAndroid::DeviceInfoAndroid(const WebRtc_Word32 id) : - DeviceInfoImpl(id) -{ -} - -WebRtc_Word32 DeviceInfoAndroid::Init() -{ - return 0; -} - -DeviceInfoAndroid::~DeviceInfoAndroid() -{ -} - -WebRtc_UWord32 DeviceInfoAndroid::NumberOfDevices() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s", __FUNCTION__); - - JNIEnv *env; - jclass javaCmDevInfoClass; - jobject javaCmDevInfoObject; - bool attached = false; - if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects( - env, - javaCmDevInfoClass, - javaCmDevInfoObject, - attached) != 0) - { - return 0; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s GetMethodId", __FUNCTION__); - // get the method ID for the Android Java GetDeviceUniqueName name. - jmethodID cid = env->GetMethodID(javaCmDevInfoClass, - "NumberOfDevices", - "()I"); - - jint numberOfDevices = 0; - if (cid != NULL) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s Calling Number of devices", __FUNCTION__); - numberOfDevices = env->CallIntMethod(javaCmDevInfoObject, cid); - } - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - - if (numberOfDevices > 0) - return numberOfDevices; - return 0; -} - -WebRtc_Word32 DeviceInfoAndroid::GetDeviceName( - WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* /*productUniqueIdUTF8*/, - WebRtc_UWord32 /*productUniqueIdUTF8Length*/) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s", __FUNCTION__); - - JNIEnv *env; - jclass javaCmDevInfoClass; - jobject javaCmDevInfoObject; - WebRtc_Word32 result = 0; - bool attached = false; - if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects( - env, - javaCmDevInfoClass, - javaCmDevInfoObject, - attached)!= 0) - { - return -1; - } - - // get the method ID for the Android Java GetDeviceUniqueName name. - jmethodID cid = env->GetMethodID(javaCmDevInfoClass, "GetDeviceUniqueName", - "(I)Ljava/lang/String;"); - if (cid != NULL) - { - - jobject javaDeviceNameObj = env->CallObjectMethod(javaCmDevInfoObject, - cid, deviceNumber); - if (javaDeviceNameObj == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Failed to get device name for device %d.", - __FUNCTION__, (int) deviceNumber); - result = -1; - } - else - { - jboolean isCopy; - const char* javaDeviceNameChar = env->GetStringUTFChars( - (jstring) javaDeviceNameObj - ,&isCopy); - const jsize javaDeviceNameCharLength = env->GetStringUTFLength( - (jstring) javaDeviceNameObj); - if ((WebRtc_UWord32) javaDeviceNameCharLength < deviceUniqueIdUTF8Length) - { - memcpy(deviceUniqueIdUTF8, - javaDeviceNameChar, - javaDeviceNameCharLength + 1); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, "%s: deviceUniqueIdUTF8 to short.", - __FUNCTION__); - result = -1; - } - if ((WebRtc_UWord32) javaDeviceNameCharLength < deviceNameLength) - { - memcpy(deviceNameUTF8, - javaDeviceNameChar, - javaDeviceNameCharLength + 1); - } - env->ReleaseStringUTFChars((jstring) javaDeviceNameObj, - javaDeviceNameChar); - }//javaDeviceNameObj==NULL - - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Failed to find GetDeviceUniqueName function id", - __FUNCTION__); - result = -1; - } - - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: result %d", __FUNCTION__, (int) result); - return result; - -} - -WebRtc_Word32 DeviceInfoAndroid::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s", __FUNCTION__); - - MapItem* item = NULL; - while ((item = _captureCapabilities.Last())) - { - delete (VideoCaptureCapability*) item->GetItem(); - _captureCapabilities.Erase(item); - } - - JNIEnv *env; - jclass javaCmDevInfoClass; - jobject javaCmDevInfoObject; - bool attached = false; - if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects( - env, - javaCmDevInfoClass, - javaCmDevInfoObject, - attached) != 0) - { - return -1; - } - - // Find the capability class - jclass javaCapClassLocal = env->FindClass(AndroidJavaCaptureCapabilityClass); - if (javaCapClassLocal == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Can't find java class VideoCaptureCapabilityAndroid.", - __FUNCTION__); - return -1; - } - - // get the method ID for the Android Java GetCapabilityArray . - char signature[256]; - sprintf(signature, - "(Ljava/lang/String;)[L%s;", - AndroidJavaCaptureCapabilityClass); - jmethodID cid = env->GetMethodID(javaCmDevInfoClass, - "GetCapabilityArray", - signature); - if (cid == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Can't find method GetCapabilityArray.", __FUNCTION__); - return -1; - } - // Create a jstring so we can pass the deviceUniquName to the java method. - jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8); - - if (capureIdString == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Can't create string for method GetCapabilityArray.", - __FUNCTION__); - return -1; - } - // Call the java class and get an array with capabilities back. - jobject javaCapabilitiesObj = env->CallObjectMethod(javaCmDevInfoObject, - cid, capureIdString); - if (!javaCapabilitiesObj) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Failed to call java GetCapabilityArray.", - __FUNCTION__); - return -1; - } - - jfieldID widthField = env->GetFieldID(javaCapClassLocal, "width", "I"); - jfieldID heigtField = env->GetFieldID(javaCapClassLocal, "height", "I"); - jfieldID maxFpsField = env->GetFieldID(javaCapClassLocal, "maxFPS", "I"); - if (widthField == NULL || heigtField == NULL || maxFpsField == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Failed to get field Id.", __FUNCTION__); - return -1; - } - - const jsize numberOfCapabilities = - env->GetArrayLength((jarray) javaCapabilitiesObj); - - for (jsize i = 0; i < numberOfCapabilities; ++i) - { - VideoCaptureCapability *cap = new VideoCaptureCapability(); - jobject capabilityElement = env->GetObjectArrayElement( - (jobjectArray) javaCapabilitiesObj, - i); - - cap->width = env->GetIntField(capabilityElement, widthField); - cap->height = env->GetIntField(capabilityElement, heigtField); - cap->expectedCaptureDelay = _expectedCaptureDelay; - cap->rawType = kVideoNV21; - cap->maxFPS = env->GetIntField(capabilityElement, maxFpsField); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Cap width %d, height %d, fps %d", __FUNCTION__, - cap->width, cap->height, cap->maxFPS); - _captureCapabilities.Insert(i, cap); - } - - _lastUsedDeviceNameLength = strlen((char*) deviceUniqueIdUTF8); - _lastUsedDeviceName = (WebRtc_UWord8*) realloc(_lastUsedDeviceName, - _lastUsedDeviceNameLength + 1); - memcpy(_lastUsedDeviceName, - deviceUniqueIdUTF8, - _lastUsedDeviceNameLength + 1); - - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "CreateCapabilityMap %d", _captureCapabilities.Size()); - - return _captureCapabilities.Size(); -} - -WebRtc_Word32 DeviceInfoAndroid::GetOrientation( - const WebRtc_UWord8* deviceUniqueIdUTF8, - VideoCaptureRotation& orientation) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s", __FUNCTION__); - - JNIEnv *env; - jclass javaCmDevInfoClass; - jobject javaCmDevInfoObject; - WebRtc_Word32 result = 0; - bool attached = false; - if (VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects( - env, - javaCmDevInfoClass, - javaCmDevInfoObject, - attached) != 0) - { - return -1; - } - - // get the method ID for the Android Java GetOrientation . - jmethodID cid = env->GetMethodID(javaCmDevInfoClass, "GetOrientation", - "(Ljava/lang/String;)I"); - if (cid == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Can't find method GetOrientation.", __FUNCTION__); - return -1; - } - // Create a jstring so we can pass the deviceUniquName to the java method. - jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8); - if (capureIdString == NULL) - { - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Can't create string for method GetCapabilityArray.", - __FUNCTION__); - return -1; - } - // Call the java class and get the orientation. - jint jorientation = env->CallIntMethod(javaCmDevInfoObject, cid, - capureIdString); - VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); - - WebRtc_Word32 retValue = 0; - switch (jorientation) - { - case -1: //Error - orientation = kCameraRotate0; - retValue = -1; - break; - case 0: - orientation = kCameraRotate0; - break; - case 90: - orientation = kCameraRotate90; - break; - case 180: - orientation = kCameraRotate180; - break; - case 270: - orientation = kCameraRotate270; - break; - case 360: - orientation = kCameraRotate0; - break; - } - return retValue; -} -} // namespace videocapturemodule -} // namespace webrtc diff --git a/modules/video_capture/main/source/Android/device_info_android.h b/modules/video_capture/main/source/Android/device_info_android.h deleted file mode 100644 index 33b22c3a0..000000000 --- a/modules/video_capture/main/source/Android/device_info_android.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_DEVICE_INFO_ANDROID_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_DEVICE_INFO_ANDROID_H_ - -#include -#include "video_capture_impl.h" -#include "device_info_impl.h" - -#define AndroidJavaCaptureDeviceInfoClass "org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid" -#define AndroidJavaCaptureCapabilityClass "org/webrtc/videoengine/CaptureCapabilityAndroid" - -namespace webrtc -{ -namespace videocapturemodule -{ - -// Android logging, uncomment to print trace to logcat instead of trace file/callback -//#include -//#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) - -class DeviceInfoAndroid: public DeviceInfoImpl -{ -public: - - DeviceInfoAndroid(const WebRtc_Word32 id); - WebRtc_Word32 Init(); - virtual ~DeviceInfoAndroid(); - virtual WebRtc_UWord32 NumberOfDevices(); - virtual WebRtc_Word32 GetDeviceName(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8 = 0, - WebRtc_UWord32 productUniqueIdUTF8Length = 0); - virtual WebRtc_Word32 CreateCapabilityMap(const WebRtc_UWord8* deviceUniqueIdUTF8); - - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* /*deviceUniqueIdUTF8*/, - const WebRtc_UWord8* /*dialogTitleUTF8*/, - void* /*parentWindow*/, - WebRtc_UWord32 /*positionX*/, - WebRtc_UWord32 /*positionY*/){return -1;} - virtual WebRtc_Word32 GetOrientation(const WebRtc_UWord8* deviceUniqueIdUTF8, - VideoCaptureRotation& orientation); -private: - bool IsDeviceNameMatches(const char* name, const char* deviceUniqueIdUTF8); - enum {_expectedCaptureDelay = 190}; -}; -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_DEVICE_INFO_ANDROID_H_ diff --git a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/CaptureCapabilityAndroid.java b/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/CaptureCapabilityAndroid.java deleted file mode 100644 index 044395367..000000000 --- a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/CaptureCapabilityAndroid.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -public class CaptureCapabilityAndroid { - public int width = 0; - public int height = 0; - public int maxFPS = 0; -} diff --git a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureAndroid.java deleted file mode 100644 index a4c39a8c2..000000000 --- a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -import java.io.IOException; -import java.util.Locale; -import java.util.concurrent.locks.ReentrantLock; - -import org.webrtc.videoengine.CaptureCapabilityAndroid; -import org.webrtc.videoengine.VideoCaptureDeviceInfoAndroid.AndroidVideoCaptureDevice; - -import android.graphics.ImageFormat; -import android.graphics.PixelFormat; -import android.hardware.Camera; -import android.hardware.Camera.PreviewCallback; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceHolder.Callback; - -public class VideoCaptureAndroid implements PreviewCallback, Callback { - - private Camera camera; - private AndroidVideoCaptureDevice currentDevice = null; - public ReentrantLock previewBufferLock = new ReentrantLock(); - private int PIXEL_FORMAT = ImageFormat.NV21; - PixelFormat pixelFormat = new PixelFormat(); - // True when the C++ layer has ordered the camera to be started. - private boolean isRunning=false; - - private final int numCaptureBuffers = 3; - private int expectedFrameSize = 0; - private int orientation = 0; - private int id = 0; - // C++ callback context variable. - private long context = 0; - private SurfaceHolder localPreview = null; - // True if this class owns the preview video buffers. - private boolean ownsBuffers = false; - - // Set this to 2 for VERBOSE logging. 1 for DEBUG - private static int LOGLEVEL = 0; - private static boolean VERBOSE = LOGLEVEL > 2; - private static boolean DEBUG = LOGLEVEL > 1; - - CaptureCapabilityAndroid currentCapability = null; - - public static - void DeleteVideoCaptureAndroid(VideoCaptureAndroid captureAndroid) { - if(DEBUG) Log.d("*WEBRTC*", "DeleteVideoCaptureAndroid"); - - captureAndroid.StopCapture(); - captureAndroid.camera.release(); - captureAndroid.camera = null; - captureAndroid.context = 0; - - if(DEBUG) Log.v("*WEBRTC*", "DeleteVideoCaptureAndroid ended"); - - } - - public VideoCaptureAndroid(int in_id, - long in_context, - Camera in_camera, - AndroidVideoCaptureDevice in_device) { - id = in_id; - context = in_context; - camera = in_camera; - currentDevice = in_device; - } - - public int StartCapture(int width, int height, int frameRate) { - if(DEBUG) Log.d("*WEBRTC*", "StartCapture width" + width + - " height " + height +" frame rate " + frameRate); - try { - if (camera == null) { - Log.e("*WEBRTC*", - String.format(Locale.US,"Camera not initialized %d",id)); - return -1; - } - currentCapability = new CaptureCapabilityAndroid(); - currentCapability.width = width; - currentCapability.height = height; - currentCapability.maxFPS = frameRate; - PixelFormat.getPixelFormatInfo(PIXEL_FORMAT, pixelFormat); - - Camera.Parameters parameters = camera.getParameters(); - parameters.setPreviewSize(currentCapability.width, - currentCapability.height); - parameters.setPreviewFormat(PIXEL_FORMAT ); - parameters.setPreviewFrameRate(currentCapability.maxFPS); - camera.setParameters(parameters); - - // Get the local preview SurfaceHolder from the static render class - localPreview = ViERenderer.GetLocalRenderer(); - if(localPreview != null) { - localPreview.addCallback(this); - } - - int bufSize = width * height * pixelFormat.bitsPerPixel / 8; - if(android.os.Build.VERSION.SDK_INT >= 7) { - // According to Doc addCallbackBuffer belongs to API level 8. - // But it seems like it works on Android 2.1 as well. - // At least SE X10 and Milestone - byte[] buffer = null; - for (int i = 0; i < numCaptureBuffers; i++) { - buffer = new byte[bufSize]; - camera.addCallbackBuffer(buffer); - } - - camera.setPreviewCallbackWithBuffer(this); - ownsBuffers = true; - } - else { - camera.setPreviewCallback(this); - } - - camera.startPreview(); - previewBufferLock.lock(); - expectedFrameSize = bufSize; - isRunning = true; - previewBufferLock.unlock(); - } - catch (Exception ex) { - Log.e("*WEBRTC*", "Failed to start camera"); - return -1; - } - return 0; - } - - public int StopCapture() { - if(DEBUG) Log.d("*WEBRTC*", "StopCapture"); - try { - previewBufferLock.lock(); - isRunning = false; - previewBufferLock.unlock(); - - camera.stopPreview(); - - if(android.os.Build.VERSION.SDK_INT > 7) { - camera.setPreviewCallbackWithBuffer(null); - } - else { - camera.setPreviewCallback(null); - } - } - catch (Exception ex) { - Log.e("*WEBRTC*", "Failed to stop camera"); - return -1; - } - - if(DEBUG) { - Log.d("*WEBRTC*", "StopCapture ended"); - } - return 0; - } - - native void ProvideCameraFrame(byte[] data,int length, long captureObject); - - public void onPreviewFrame(byte[] data, Camera camera) { - previewBufferLock.lock(); - - if(VERBOSE) { - Log.v("*WEBRTC*", - String.format(Locale.US, "preview frame length %d context %x", - data.length, context)); - } - if(isRunning) { - // If StartCapture has been called but not StopCapture - // Call the C++ layer with the captured frame - if (data.length == expectedFrameSize) { - ProvideCameraFrame(data, expectedFrameSize, context); - if (VERBOSE) { - Log.v("*WEBRTC*", String.format(Locale.US, "frame delivered")); - } - if(ownsBuffers) { - // Give the video buffer to the camera service again. - camera.addCallbackBuffer(data); - } - } - } - previewBufferLock.unlock(); - } - - - public void surfaceChanged(SurfaceHolder holder, - int format, int width, int height) { - - try { - if(camera != null) { - camera.setPreviewDisplay(localPreview); - } - } catch (IOException e) { - Log.e("*WEBRTC*", - String.format(Locale.US, - "Failed to set Local preview. " + e.getMessage())); - } - } - - // Sets the rotation of the preview render window. - // Does not affect the captured video image. - public void SetPreviewRotation(int rotation) { - if(camera != null) { - previewBufferLock.lock(); - final boolean running = isRunning; - int width = 0; - int height = 0; - int framerate = 0; - - if(running) { - width = currentCapability.width; - height = currentCapability.height; - framerate = currentCapability.maxFPS; - - StopCapture(); - } - - int resultRotation = 0; - if(currentDevice.frontCameraType == - VideoCaptureDeviceInfoAndroid.FrontFacingCameraType.Android23) { - // this is a 2.3 or later front facing camera. - // SetDisplayOrientation will flip the image horizontally - // before doing the rotation. - resultRotation=(360-rotation) % 360; // compensate the mirror - } - else { - // Back facing or 2.2 or previous front camera - resultRotation=rotation; - } - if(android.os.Build.VERSION.SDK_INT>7) { - camera.setDisplayOrientation(resultRotation); - } - else { - // Android 2.1 and previous - // This rotation unfortunately does not seems to work. - // http://code.google.com/p/android/issues/detail?id=1193 - Camera.Parameters parameters = camera.getParameters(); - parameters.setRotation(resultRotation); - camera.setParameters(parameters); - } - - if(running) { - StartCapture(width, height, framerate); - } - previewBufferLock.unlock(); - } - } - - public void surfaceCreated(SurfaceHolder holder) { - } - - - public void surfaceDestroyed(SurfaceHolder holder) { - } - -} diff --git a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java b/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java deleted file mode 100644 index 4ccf06013..000000000 --- a/modules/video_capture/main/source/Android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import dalvik.system.DexClassLoader; - -import android.content.Context; -import android.hardware.Camera; -import android.hardware.Camera.Size; -import android.util.Log; - -public class VideoCaptureDeviceInfoAndroid { - - //Context - Context context; - - // Set this to 2 for VERBOSE logging. 1 for DEBUG - private static int LOGLEVEL = 0; - private static boolean VERBOSE = LOGLEVEL > 2; - private static boolean DEBUG = LOGLEVEL > 1; - - // Private class with info about all available cameras and the capabilities - public class AndroidVideoCaptureDevice { - AndroidVideoCaptureDevice() { - frontCameraType = FrontFacingCameraType.None; - index = 0; - } - - public String deviceUniqueName; - public CaptureCapabilityAndroid captureCapabilies[]; - public FrontFacingCameraType frontCameraType; - - // Orientation of camera as described in - // android.hardware.Camera.CameraInfo.Orientation - public int orientation; - // Camera index used in Camera.Open on Android 2.3 and onwards - public int index; - } - - public enum FrontFacingCameraType { - None, // This is not a front facing camera - GalaxyS, // Galaxy S front facing camera. - HTCEvo, // HTC Evo front facing camera - Android23, // Android 2.3 front facing camera. - } - - String currentDeviceUniqueId; - int id; - List deviceList; - - public static VideoCaptureDeviceInfoAndroid - CreateVideoCaptureDeviceInfoAndroid(int in_id, Context in_context) { - if(DEBUG) { - Log.d("*WEBRTC*", - String.format(Locale.US, "VideoCaptureDeviceInfoAndroid")); - } - - VideoCaptureDeviceInfoAndroid self = - new VideoCaptureDeviceInfoAndroid(in_id, in_context); - if(self != null && self.Init() == 0) { - return self; - } - else { - if(DEBUG) { - Log.d("*WEBRTC*", "Failed to create VideoCaptureDeviceInfoAndroid."); - } - } - return null; - } - - private VideoCaptureDeviceInfoAndroid(int in_id, - Context in_context) { - id = in_id; - context = in_context; - deviceList = new ArrayList(); - } - - private int Init() { - // Populate the deviceList with available cameras and their capabilities. - Camera camera = null; - try{ - if(android.os.Build.VERSION.SDK_INT > 8) { - // From Android 2.3 and onwards - for(int i = 0; i < Camera.getNumberOfCameras(); ++i) { - AndroidVideoCaptureDevice newDevice = new AndroidVideoCaptureDevice(); - - Camera.CameraInfo info = new Camera.CameraInfo(); - Camera.getCameraInfo(i, info); - newDevice.index = i; - newDevice.orientation=info.orientation; - if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { - newDevice.deviceUniqueName = - "Camera " + i +", Facing back, Orientation "+ info.orientation; - } - else { - newDevice.deviceUniqueName = - "Camera " + i +", Facing front, Orientation "+ info.orientation; - newDevice.frontCameraType = FrontFacingCameraType.Android23; - } - - camera = Camera.open(i); - Camera.Parameters parameters = camera.getParameters(); - AddDeviceInfo(newDevice, parameters); - camera.release(); - camera = null; - deviceList.add(newDevice); - } - } - else { - // Prior to Android 2.3 - AndroidVideoCaptureDevice newDevice; - Camera.Parameters parameters; - - newDevice = new AndroidVideoCaptureDevice(); - camera = Camera.open(); - parameters = camera.getParameters(); - newDevice.deviceUniqueName = "Camera 1, Facing back"; - newDevice.orientation = 90; - AddDeviceInfo(newDevice, parameters); - - deviceList.add(newDevice); - camera.release(); - camera=null; - - newDevice = new AndroidVideoCaptureDevice(); - newDevice.deviceUniqueName = "Camera 2, Facing front"; - parameters = SearchOldFrontFacingCameras(newDevice); - if(parameters != null) { - AddDeviceInfo(newDevice, parameters); - deviceList.add(newDevice); - } - } - } - catch (Exception ex) { - Log.e("*WEBRTC*", "Failed to init VideoCaptureDeviceInfo ex" + - ex.getLocalizedMessage()); - return -1; - } - VerifyCapabilities(); - return 0; - } - - // Adds the capture capabilities of the currently opened device - private void AddDeviceInfo(AndroidVideoCaptureDevice newDevice, - Camera.Parameters parameters) { - - List sizes = parameters.getSupportedPreviewSizes(); - List frameRates = parameters.getSupportedPreviewFrameRates(); - int maxFPS=0; - for(Integer frameRate:frameRates) { - if(VERBOSE) { - Log.v("*WEBRTC*", - "VideoCaptureDeviceInfoAndroid:frameRate " + frameRate); - } - if(frameRate > maxFPS) { - maxFPS = frameRate; - } - } - - newDevice.captureCapabilies = new CaptureCapabilityAndroid[sizes.size()]; - for(int i = 0; i < sizes.size(); ++i) { - Size s = sizes.get(i); - newDevice.captureCapabilies[i] = new CaptureCapabilityAndroid(); - newDevice.captureCapabilies[i].height = s.height; - newDevice.captureCapabilies[i].width = s.width; - newDevice.captureCapabilies[i].maxFPS = maxFPS; - } - } - - // Function that make sure device specific capabilities are - // in the capability list. - // Ie Galaxy S supports CIF but does not list CIF as a supported capability. - // Motorola Droid Camera does not work with frame rate above 15fps. - // http://code.google.com/p/android/issues/detail?id=5514#c0 - private void VerifyCapabilities() { - // Nexus S or Galaxy S - if(android.os.Build.DEVICE.equals("GT-I9000") || - android.os.Build.DEVICE.equals("crespo")) { - CaptureCapabilityAndroid specificCapability = - new CaptureCapabilityAndroid(); - specificCapability.width = 352; - specificCapability.height = 288; - specificCapability.maxFPS = 15; - AddDeviceSpecificCapability(specificCapability); - - specificCapability = new CaptureCapabilityAndroid(); - specificCapability.width = 176; - specificCapability.height = 144; - specificCapability.maxFPS = 15; - AddDeviceSpecificCapability(specificCapability); - - specificCapability = new CaptureCapabilityAndroid(); - specificCapability.width = 320; - specificCapability.height = 240; - specificCapability.maxFPS = 15; - AddDeviceSpecificCapability(specificCapability); - } - // Motorola Milestone Camera server does not work at 30fps - // even though it reports that it can - if(android.os.Build.MANUFACTURER.equals("motorola") && - android.os.Build.DEVICE.equals("umts_sholes")) { - for(AndroidVideoCaptureDevice device:deviceList) { - for(CaptureCapabilityAndroid capability:device.captureCapabilies) { - capability.maxFPS=15; - } - } - } - } - - private void AddDeviceSpecificCapability( - CaptureCapabilityAndroid specificCapability) { - for(AndroidVideoCaptureDevice device:deviceList) { - boolean foundCapability = false; - for(CaptureCapabilityAndroid capability:device.captureCapabilies) { - if(capability.width == specificCapability.width && - capability.height == specificCapability.height) { - foundCapability = true; - break; - } - } - if(foundCapability==false) { - CaptureCapabilityAndroid newCaptureCapabilies[]= - new CaptureCapabilityAndroid[device.captureCapabilies.length+1]; - for(int i = 0; i < device.captureCapabilies.length; ++i) { - newCaptureCapabilies[i+1] = device.captureCapabilies[i]; - } - newCaptureCapabilies[0] = specificCapability; - device.captureCapabilies = newCaptureCapabilies; - } - } - } - - // Returns the number of Capture devices that is supported - public int NumberOfDevices() { - return deviceList.size(); - } - - public String GetDeviceUniqueName(int deviceNumber) { - if(deviceNumber < 0 || deviceNumber >= deviceList.size()) { - return null; - } - return deviceList.get(deviceNumber).deviceUniqueName; - } - - public CaptureCapabilityAndroid[] GetCapabilityArray (String deviceUniqueId) - { - for (AndroidVideoCaptureDevice device: deviceList) { - if(device.deviceUniqueName.equals(deviceUniqueId)) { - return (CaptureCapabilityAndroid[]) device.captureCapabilies; - } - } - return null; - } - - // Returns the camera orientation as described by - // android.hardware.Camera.CameraInfo.orientation - public int GetOrientation(String deviceUniqueId) { - for (AndroidVideoCaptureDevice device: deviceList) { - if(device.deviceUniqueName.equals(deviceUniqueId)) { - return device.orientation; - } - } - return -1; - } - - // Returns an instance of VideoCaptureAndroid. - public VideoCaptureAndroid AllocateCamera(int id, long context, - String deviceUniqueId) { - try { - if(DEBUG) Log.d("*WEBRTC*", "AllocateCamera " + deviceUniqueId); - - Camera camera = null; - AndroidVideoCaptureDevice deviceToUse = null; - for (AndroidVideoCaptureDevice device: deviceList) { - if(device.deviceUniqueName.equals(deviceUniqueId)) { - // Found the wanted camera - deviceToUse = device; - switch(device.frontCameraType) { - case GalaxyS: - camera = AllocateGalaxySFrontCamera(); - break; - case HTCEvo: - camera = AllocateEVOFrontFacingCamera(); - break; - default: - // From Android 2.3 and onwards) - if(android.os.Build.VERSION.SDK_INT>8) - camera=Camera.open(device.index); - else - camera=Camera.open(); // Default camera - } - } - } - - if(camera == null) { - return null; - } - if(VERBOSE) { - Log.v("*WEBRTC*", "AllocateCamera - creating VideoCaptureAndroid"); - } - - return new VideoCaptureAndroid(id,context,camera,deviceToUse); - - }catch (Exception ex) { - Log.e("*WEBRTC*", "AllocateCamera Failed to open camera- ex " + - ex.getLocalizedMessage()); - } - return null; - } - - // Searches for a front facing camera device. This is device specific code. - private Camera.Parameters - SearchOldFrontFacingCameras(AndroidVideoCaptureDevice newDevice) - throws SecurityException, IllegalArgumentException, - NoSuchMethodException, ClassNotFoundException, - IllegalAccessException, InvocationTargetException { - // Check the id of the opened camera device - // Returns null on X10 and 1 on Samsung Galaxy S. - Camera camera = Camera.open(); - Camera.Parameters parameters = camera.getParameters(); - String cameraId = parameters.get("camera-id"); - if(cameraId != null && cameraId.equals("1")) { - // This might be a Samsung Galaxy S with a front facing camera. - try { - parameters.set("camera-id", 2); - camera.setParameters(parameters); - parameters = camera.getParameters(); - newDevice.frontCameraType = FrontFacingCameraType.GalaxyS; - newDevice.orientation = 0; - camera.release(); - return parameters; - } - catch (Exception ex) { - //Nope - it did not work. - Log.e("*WEBRTC*", "Init Failed to open front camera camera - ex " + - ex.getLocalizedMessage()); - } - } - camera.release(); - - //Check for Evo front facing camera - File file = - new File("/system/framework/com.htc.hardware.twinCamDevice.jar"); - boolean exists = file.exists(); - if (!exists){ - file = - new File("/system/framework/com.sprint.hardware.twinCamDevice.jar"); - exists = file.exists(); - } - if(exists) { - newDevice.frontCameraType = FrontFacingCameraType.HTCEvo; - newDevice.orientation = 0; - Camera evCamera = AllocateEVOFrontFacingCamera(); - parameters = evCamera.getParameters(); - evCamera.release(); - return parameters; - } - return null; - } - - // Returns a handle to HTC front facing camera. - // The caller is responsible to release it on completion. - private Camera AllocateEVOFrontFacingCamera() - throws SecurityException, NoSuchMethodException, - ClassNotFoundException, IllegalArgumentException, - IllegalAccessException, InvocationTargetException { - String classPath = null; - File file = - new File("/system/framework/com.htc.hardware.twinCamDevice.jar"); - classPath = "com.htc.hardware.twinCamDevice.FrontFacingCamera"; - boolean exists = file.exists(); - if (!exists){ - file = - new File("/system/framework/com.sprint.hardware.twinCamDevice.jar"); - classPath = "com.sprint.hardware.twinCamDevice.FrontFacingCamera"; - exists = file.exists(); - } - if(!exists) { - return null; - } - - String dexOutputDir = ""; - if(context != null) { - dexOutputDir = context.getFilesDir().getAbsolutePath(); - File mFilesDir = new File(dexOutputDir, "dexfiles"); - if(!mFilesDir.exists()){ - //Log.e("*WEBRTCN*", "Directory doesn't exists"); - if(!mFilesDir.mkdirs()) { - //Log.e("*WEBRTCN*", "Unable to create files directory"); - } - } - } - - dexOutputDir += "/dexfiles"; - - DexClassLoader loader = - new DexClassLoader(file.getAbsolutePath(), dexOutputDir, - null, ClassLoader.getSystemClassLoader()); - - Method method = loader.loadClass(classPath).getDeclaredMethod( - "getFrontFacingCamera", (Class[]) null); - Camera camera = (Camera) method.invoke((Object[])null,(Object[]) null); - return camera; - } - - // Returns a handle to Galaxy S front camera. - // The caller is responsible to release it on completion. - private Camera AllocateGalaxySFrontCamera() - { - Camera camera = Camera.open(); - Camera.Parameters parameters = camera.getParameters(); - parameters.set("camera-id",2); - camera.setParameters(parameters); - return camera; - } - -} diff --git a/modules/video_capture/main/source/Android/video_capture_android.cc b/modules/video_capture/main/source/Android/video_capture_android.cc deleted file mode 100644 index a2ae08e0b..000000000 --- a/modules/video_capture/main/source/Android/video_capture_android.cc +++ /dev/null @@ -1,706 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_capture_android.h" -#include -#include "critical_section_wrapper.h" - -#include "trace.h" -namespace webrtc -{ - -VideoCaptureModule* VideoCaptureModule::Create(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, -1, - "%s:", __FUNCTION__); - - videocapturemodule::VideoCaptureAndroid* newCaptureModule = - new videocapturemodule::VideoCaptureAndroid(id); - if (!newCaptureModule || newCaptureModule->Init(id, deviceUniqueIdUTF8) - != 0) - { - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - return newCaptureModule; -} - -namespace videocapturemodule -{ - -// Android logging, uncomment to print trace to logcat instead of trace file/callback -//#include -//#undef WEBRTC_TRACE -//#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) - -JavaVM* VideoCaptureAndroid::g_jvm = NULL; -jclass VideoCaptureAndroid::g_javaCmClass = NULL; //VideoCaptureAndroid.java -jclass VideoCaptureAndroid::g_javaCmDevInfoClass = NULL; //VideoCaptureDeviceInfoAndroid.java -jobject VideoCaptureAndroid::g_javaCmDevInfoObject = NULL; //static instance of VideoCaptureDeviceInfoAndroid.java -jobject VideoCaptureAndroid::g_javaContext = NULL; - -/* - * Register references to Java Capture class. - */ -WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM, - void* javaContext) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, -1, - "%s:", __FUNCTION__); - - g_jvm = static_cast (javaVM); - g_javaContext = static_cast (javaContext); - - if (javaVM) - { - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: could not get Java environment", __FUNCTION__); - return -1; - } - // get java capture class type (note path to class packet) - jclass javaCmClassLocal = env->FindClass(AndroidJavaCaptureClass); - if (!javaCmClassLocal) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: could not find java class", __FUNCTION__); - return -1; - } - // create a global reference to the class (to tell JNI that we are referencing it - // after this function has returned) - g_javaCmClass = static_cast - (env->NewGlobalRef(javaCmClassLocal)); - if (!g_javaCmClass) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: InitVideoEngineJava(): could not create" - " Java Camera class reference", - __FUNCTION__); - return -1; - } - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaCmClassLocal); - JNINativeMethod nativeFunctions = { "ProvideCameraFrame", "([BIJ)V", - (void*) &VideoCaptureAndroid::ProvideCameraFrame }; - if (env->RegisterNatives(g_javaCmClass, &nativeFunctions, 1) == 0) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: Registered native functions", __FUNCTION__); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Failed to register native functions", - __FUNCTION__); - return -1; - } - - // get java capture class type (note path to class packet) - jclass javaCmDevInfoClassLocal = env->FindClass( - AndroidJavaCaptureDeviceInfoClass); - if (!javaCmDevInfoClassLocal) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: could not find java class", __FUNCTION__); - return -1; - } - - // create a global reference to the class (to tell JNI that we are referencing it - // after this function has returned) - g_javaCmDevInfoClass = static_cast - (env->NewGlobalRef(javaCmDevInfoClassLocal)); - if (!g_javaCmDevInfoClass) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: InitVideoEngineJava(): could not create Java " - "Camera Device info class reference", - __FUNCTION__); - return -1; - } - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaCmDevInfoClassLocal); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "VideoCaptureDeviceInfoAndroid get method id"); - - // get the method ID for the Android Java CaptureClass static - //CreateVideoCaptureAndroid factory method. - jmethodID cid = env->GetStaticMethodID(g_javaCmDevInfoClass, - "CreateVideoCaptureDeviceInfoAndroid", - "(ILandroid/content/Context;)" - "Lorg/webrtc/videoengine/VideoCaptureDeviceInfoAndroid;"); - if (cid == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: could not get java VideoCaptureDeviceInfoAndroid constructor ID", - __FUNCTION__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: construct static java device object", __FUNCTION__); - - // construct the object by calling the static constructor object - jobject javaCameraDeviceInfoObjLocal = env->CallStaticObjectMethod( - g_javaCmDevInfoClass, - cid, (int) -1, - g_javaContext); - if (!javaCameraDeviceInfoObjLocal) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1, - "%s: could not create Java Capture Device info object", - __FUNCTION__); - return -1; - } - // create a reference to the object (to tell JNI that we are referencing it - // after this function has returned) - g_javaCmDevInfoObject = env->NewGlobalRef(javaCameraDeviceInfoObjLocal); - if (!g_javaCmDevInfoObject) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioDevice, -1, - "%s: could not create Java cameradevinceinfo object reference", - __FUNCTION__); - return -1; - } - // Delete local object ref, we only use the global ref - env->DeleteLocalRef(javaCameraDeviceInfoObjLocal); - return 0; - } - else - { - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: JVM is NULL, assuming deinit", __FUNCTION__); - if (!g_jvm) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: SetAndroidObjects not called with a valid JVM.", - __FUNCTION__); - return -1; - } - JNIEnv* env = NULL; - bool attached = false; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - -1, "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - attached = true; - } - env->DeleteGlobalRef(g_javaCmDevInfoObject); - env->DeleteGlobalRef(g_javaCmDevInfoClass); - env->DeleteGlobalRef(g_javaCmClass); - if (attached && g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1, - "%s: Could not detach thread from JVM", __FUNCTION__); - return -1; - } - return 0; - env = (JNIEnv *) NULL; - } - return 0; -} - -WebRtc_Word32 VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects( - JNIEnv*& env, - jclass& javaCmDevInfoClass, - jobject& javaCmDevInfoObject, - bool& attached) -{ - // get the JNI env for this thread - if (!g_jvm) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: SetAndroidObjects not called with a valid JVM.", - __FUNCTION__); - return -1; - } - attached = false; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - attached = true; - } - javaCmDevInfoClass = g_javaCmDevInfoClass; - javaCmDevInfoObject = g_javaCmDevInfoObject; - return 0; - -} - -WebRtc_Word32 VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(bool attached) -{ - if (attached && g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1, - "%s: Could not detach thread from JVM", __FUNCTION__); - return -1; - } - return 0; -} - -/* - * JNI callback from Java class. Called when the camera has a new frame to deliver - * Class: org_webrtc_capturemodule_VideoCaptureAndroid - * Method: ProvideCameraFrame - * Signature: ([BIJ)V - */ -void JNICALL VideoCaptureAndroid::ProvideCameraFrame(JNIEnv * env, - jobject, - jbyteArray javaCameraFrame, - jint length, - jlong context) -{ - VideoCaptureAndroid* captureModule=reinterpret_cast(context); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, - -1, "%s: IncomingFrame %d", __FUNCTION__,length); - jbyte* cameraFrame= env->GetByteArrayElements(javaCameraFrame,NULL); - captureModule->IncomingFrame((WebRtc_UWord8*) cameraFrame, - length,captureModule->_frameInfo,0); - env->ReleaseByteArrayElements(javaCameraFrame,cameraFrame,JNI_ABORT); -} - - - -VideoCaptureAndroid::VideoCaptureAndroid(const WebRtc_Word32 id) - : VideoCaptureImpl(id), _capInfo(id), _javaCaptureObj(NULL), - _captureStarted(false) - -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: context %x", __FUNCTION__, (int) this); -} -// ---------------------------------------------------------------------------- -// Init -// -// Initializes needed Java resources like the JNI interface to -// VideoCaptureAndroid.java -// ---------------------------------------------------------------------------- -WebRtc_Word32 VideoCaptureAndroid::Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - const int nameLength = strlen((char*) deviceUniqueIdUTF8); - if (nameLength >= kVideoCaptureUniqueNameLength) - { - return -1; - } - - // Store the device name - _deviceUniqueId = new WebRtc_UWord8[nameLength + 1]; - memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); - - if (_capInfo.Init() != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Failed to initialize CaptureDeviceInfo", __FUNCTION__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:", - __FUNCTION__); - // use the jvm that has been set - if (!g_jvm) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Not a valid Java VM pointer", __FUNCTION__); - return -1; - } - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "get method id"); - - // get the method ID for the Android Java CaptureDeviceInfoClass AllocateCamera factory method. - char signature[256]; - sprintf(signature, "(IJLjava/lang/String;)L%s;", AndroidJavaCaptureClass); - - jmethodID cid = env->GetMethodID(g_javaCmDevInfoClass, "AllocateCamera", - signature); - if (cid == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: could not get constructor ID", __FUNCTION__); - return -1; /* exception thrown */ - } - - jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8); - // construct the object by calling the static constructor object - jobject javaCameraObjLocal = env->CallObjectMethod(g_javaCmDevInfoObject, - cid, (jint) id, - (jlong) this, - capureIdString); - if (!javaCameraObjLocal) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s: could not create Java Capture object", __FUNCTION__); - return -1; - } - - // create a reference to the object (to tell JNI that we are referencing it - // after this function has returned) - _javaCaptureObj = env->NewGlobalRef(javaCameraObjLocal); - if (!_javaCaptureObj) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioDevice, _id, - "%s: could not create Java camera object reference", - __FUNCTION__); - return -1; - } - - // Delete local object ref, we only use the global ref - env->DeleteLocalRef(javaCameraObjLocal); - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - return 0; -} - -VideoCaptureAndroid::~VideoCaptureAndroid() -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:", - __FUNCTION__); - if (_javaCaptureObj == NULL || g_jvm == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Nothing to clean", __FUNCTION__); - } - else - { - bool isAttached = false; - // get the JNI env for this thread - JNIEnv *env; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - } - else - { - isAttached = true; - } - } - - // get the method ID for the Android Java CaptureClass static - // DeleteVideoCaptureAndroid method. Call this to release the camera so - // another application can use it. - jmethodID cid = env->GetStaticMethodID(g_javaCmClass, - "DeleteVideoCaptureAndroid", - "(Lorg/webrtc/videoengine/VideoCaptureAndroid;)V"); - if (cid != NULL) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: Call DeleteVideoCaptureAndroid", __FUNCTION__); - // Close the camera by calling the static destruct function. - env->CallStaticVoidMethod(g_javaCmClass, cid, _javaCaptureObj); - - // Delete global object ref to the camera. - env->DeleteGlobalRef(_javaCaptureObj); - _javaCaptureObj = NULL; - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Failed to find DeleteVideoCaptureAndroid id", - __FUNCTION__); - } - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, - _id, "%s: Could not detach thread from JVM", - __FUNCTION__); - } - } - } -} - -WebRtc_Word32 VideoCaptureAndroid::StartCapture( - const VideoCaptureCapability& capability) -{ - CriticalSectionScoped cs(_apiCs); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: ", __FUNCTION__); - - bool isAttached = false; - WebRtc_Word32 result = 0; - // get the JNI env for this thread - JNIEnv *env; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - } - else - { - isAttached = true; - } - } - - if (_capInfo.GetBestMatchedCapability(_deviceUniqueId, capability, - _frameInfo) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: GetBestMatchedCapability failed. Req cap w%d h%d", - __FUNCTION__, capability.width, capability.height); - return -1; - } - - // Store the new expected capture delay - _captureDelay = _frameInfo.expectedCaptureDelay; - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: _frameInfo w%d h%d", __FUNCTION__, _frameInfo.width, - _frameInfo.height); - - // get the method ID for the Android Java CaptureClass static StartCapture method. - jmethodID cid = env->GetMethodID(g_javaCmClass, "StartCapture", "(III)I"); - if (cid != NULL) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: Call StartCapture", __FUNCTION__); - // Close the camera by calling the static destruct function. - result = env->CallIntMethod(_javaCaptureObj, cid, _frameInfo.width, - _frameInfo.height, _frameInfo.maxFPS); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Failed to find StartCapture id", __FUNCTION__); - } - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - if (result == 0) - { - _requestedCapability = capability; - _captureStarted = true; - } - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: result %d", __FUNCTION__, result); - return result; -} -WebRtc_Word32 VideoCaptureAndroid::StopCapture() -{ - CriticalSectionScoped cs(_apiCs); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: ", __FUNCTION__); - - bool isAttached = false; - WebRtc_Word32 result = 0; - // get the JNI env for this thread - JNIEnv *env = NULL; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - } - else - { - isAttached = true; - } - } - - memset(&_requestedCapability, 0, sizeof(_requestedCapability)); - memset(&_frameInfo, 0, sizeof(_frameInfo)); - - // get the method ID for the Android Java CaptureClass StopCapture method. - jmethodID cid = env->GetMethodID(g_javaCmClass, "StopCapture", "()I"); - if (cid != NULL) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, - "%s: Call StopCapture", __FUNCTION__); - // Close the camera by calling the static destruct function. - result = env->CallIntMethod(_javaCaptureObj, cid); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: Failed to find StopCapture id", __FUNCTION__); - } - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - _captureStarted = false; - - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: result %d", __FUNCTION__, result); - return result; -} - -bool VideoCaptureAndroid::CaptureStarted() -{ - CriticalSectionScoped cs(_apiCs); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: ", __FUNCTION__); - return _captureStarted; -} -WebRtc_Word32 VideoCaptureAndroid::CaptureSettings( - VideoCaptureCapability& settings) -{ - CriticalSectionScoped cs(_apiCs); - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1, - "%s: ", __FUNCTION__); - settings = _requestedCapability; - return 0; -} - -WebRtc_Word32 VideoCaptureAndroid::SetCaptureRotation( - VideoCaptureRotation rotation) -{ - CriticalSectionScoped cs(_apiCs); - if (VideoCaptureImpl::SetCaptureRotation(rotation) == 0) - { - if (!g_jvm) - return -1; - - // get the JNI env for this thread - JNIEnv *env; - bool isAttached = false; - - // get the JNI env for this thread - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - if ((res < 0) || !env) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - jmethodID cid = env->GetMethodID(g_javaCmClass, "SetPreviewRotation", - "(I)V"); - if (cid == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: could not get java SetPreviewRotation ID", - __FUNCTION__); - return -1; - } - jint rotateFrame = 0; - switch (rotation) - { - case kCameraRotate0: - rotateFrame = 0; - break; - case kCameraRotate90: - rotateFrame = 90; - break; - case kCameraRotate180: - rotateFrame = 180; - break; - case kCameraRotate270: - rotateFrame = 270; - break; - } - env->CallVoidMethod(_javaCaptureObj, cid, rotateFrame); - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, - _id, "%s: Could not detach thread from JVM", - __FUNCTION__); - } - } - - } - return 0; -} -} //namespace videocapturemodule -} //namespace webrtc diff --git a/modules/video_capture/main/source/Android/video_capture_android.h b/modules/video_capture/main/source/Android/video_capture_android.h deleted file mode 100644 index abb4dc93e..000000000 --- a/modules/video_capture/main/source/Android/video_capture_android.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_VIDEO_CAPTURE_ANDROID_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_VIDEO_CAPTURE_ANDROID_H_ - -#include -#include "device_info_android.h" -#include "video_capture_impl.h" - -#define AndroidJavaCaptureClass "org/webrtc/videoengine/VideoCaptureAndroid" - -namespace webrtc -{ -namespace videocapturemodule -{ -class VideoCaptureAndroid: public VideoCaptureImpl -{ -public: - static WebRtc_Word32 SetAndroidObjects(void* javaVM, void* javaContext); - static WebRtc_Word32 AttachAndUseAndroidDeviceInfoObjects(JNIEnv*& env, - jclass& javaCmDevInfoClass, - jobject& javaCmDevInfoObject, - bool& attached); - static WebRtc_Word32 ReleaseAndroidDeviceInfoObjects(bool attached); - - VideoCaptureAndroid(const WebRtc_Word32 id); - virtual WebRtc_Word32 Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - - - virtual WebRtc_Word32 StartCapture(const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - virtual bool CaptureStarted(); - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - virtual WebRtc_Word32 SetCaptureRotation(VideoCaptureRotation rotation); - -protected: - virtual ~VideoCaptureAndroid(); - static void JNICALL ProvideCameraFrame (JNIEnv * env, - jobject, - jbyteArray javaCameraFrame, - jint length, jlong context); - DeviceInfoAndroid _capInfo; - jobject _javaCaptureObj; // Java Camera object. - VideoCaptureCapability _frameInfo; - bool _captureStarted; - - static JavaVM* g_jvm; - static jclass g_javaCmClass; - static jclass g_javaCmDevInfoClass; - static jobject g_javaCmDevInfoObject; //Static java object implementing the needed device info functions; - static jobject g_javaContext; // Java Application context -}; -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_ANDROID_VIDEO_CAPTURE_ANDROID_H_ diff --git a/modules/video_capture/main/source/Linux/device_info_linux.cc b/modules/video_capture/main/source/Linux/device_info_linux.cc deleted file mode 100644 index 2d053513e..000000000 --- a/modules/video_capture/main/source/Linux/device_info_linux.cc +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "device_info_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -//v4l includes -#include -namespace webrtc -{ -VideoCaptureModule::DeviceInfo* -VideoCaptureModule::CreateDeviceInfo(const WebRtc_Word32 id) -{ - videocapturemodule::DeviceInfoLinux *deviceInfo = - new videocapturemodule::DeviceInfoLinux(id); - if (!deviceInfo) - { - deviceInfo = NULL; - } - - return deviceInfo; -} - -void VideoCaptureModule::DestroyDeviceInfo(DeviceInfo* deviceInfo) -{ - videocapturemodule::DeviceInfoLinux* devInfo = - static_cast (deviceInfo); - delete devInfo; -} - -namespace videocapturemodule -{ - -DeviceInfoLinux::DeviceInfoLinux(const WebRtc_Word32 id) - : DeviceInfoImpl(id) -{ -} - -WebRtc_Word32 DeviceInfoLinux::Init() -{ - return 0; -} - -DeviceInfoLinux::~DeviceInfoLinux() -{ -} - -WebRtc_UWord32 DeviceInfoLinux::NumberOfDevices() -{ - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__); - - WebRtc_UWord32 count = 0; - char device[20]; - int fd = -1; - - /* detect /dev/video [0-63]VideoCaptureModule entries */ - for (int n = 0; n < 64; n++) - { - struct stat s; - sprintf(device, "/dev/video%d", n); - if (stat(device, &s) == 0) //check validity of path - { - if ((fd = open(device, O_RDONLY)) > 0 || errno == EBUSY) - { - close(fd); - count++; - } - } - } - - return count; -} - -WebRtc_Word32 DeviceInfoLinux::GetDeviceName( - WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* /*productUniqueIdUTF8*/, - WebRtc_UWord32 /*productUniqueIdUTF8Length*/) -{ - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCapture, _id, "%s", __FUNCTION__); - - char device[20]; - sprintf(device, "/dev/video%d", (int) deviceNumber); - int fd = -1; - - // open video device in RDONLY mode - struct stat s; - if (stat(device, &s) == 0) - { - if ((fd = open(device, O_RDONLY)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "error in opening video device. errno = %d", errno); - return -1; - } - } - - // query device capabilities - struct v4l2_capability cap; - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "error in querying the device capability for device %s. errno = %d", - device, errno); - close(fd); - return -1; - } - - close(fd); - - char cameraName[64]; - memset(deviceNameUTF8, 0, deviceNameLength); - memcpy(cameraName, cap.card, sizeof(cap.card)); - - if (deviceNameLength >= strlen(cameraName)) - { - memcpy(deviceNameUTF8, cameraName, strlen(cameraName)); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "buffer passed is too small"); - return -1; - } - - if (cap.bus_info[0] != 0) // may not available in all drivers - { - // copy device id - if (deviceUniqueIdUTF8Length >= strlen((const char*) cap.bus_info)) - { - memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length); - memcpy(deviceUniqueIdUTF8, cap.bus_info, - strlen((const char*) cap.bus_info)); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "buffer passed is too small"); - return -1; - } - } - - return 0; -} - -WebRtc_Word32 DeviceInfoLinux::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - int fd; - char device[32]; - bool found = false; - - const WebRtc_Word32 deviceUniqueIdUTF8Length = - (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); - if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "Device name too long"); - return -1; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8); - - /* detect /dev/video [0-63] entries */ - for (int n = 0; n < 64; n++) - { - struct stat s; - sprintf(device, "/dev/video%d", n); - if (stat(device, &s) == 0) //check validity of path - { - if ((fd = open(device, O_RDONLY)) > 0) - { - // query device capabilities - struct v4l2_capability cap; - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) - { - if (cap.bus_info[0] != 0) - { - if (strncmp((const char*) cap.bus_info, - (const char*) deviceUniqueIdUTF8, - strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id - { - found = true; - break; // fd matches with device unique id supplied - } - } - else //match for device name - { - if (IsDeviceNameMatches((const char*) cap.card, - (const char*) deviceUniqueIdUTF8)) - { - found = true; - break; - } - } - } - close(fd); // close since this is not the matching device - } - } - } - - if (!found) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "no matching device found"); - return -1; - } - - // now fd will point to the matching device - // reset old capability map - MapItem* item = NULL; - while ((item = _captureCapabilities.Last())) - { - delete static_cast (item->GetItem()); - _captureCapabilities.Erase(item); - } - - int size = FillCapabilityMap(fd); - close(fd); - - // Store the new used device name - _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; - _lastUsedDeviceName = (WebRtc_UWord8*) realloc(_lastUsedDeviceName, - _lastUsedDeviceNameLength + 1); - memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength + 1); - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "CreateCapabilityMap %d", - _captureCapabilities.Size()); - - return size; -} - -bool DeviceInfoLinux::IsDeviceNameMatches(const char* name, - const char* deviceUniqueIdUTF8) -{ - if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0) - return true; - return false; -} - -WebRtc_Word32 DeviceInfoLinux::FillCapabilityMap(int fd) -{ - - // set image format - struct v4l2_format video_fmt; - memset(&video_fmt, 0, sizeof(struct v4l2_format)); - - video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - video_fmt.fmt.pix.sizeimage = 0; - - int totalFmts = 2; - unsigned int videoFormats[] = { V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YUYV }; - - int sizes = 13; - unsigned int size[][2] = { { 128, 96 }, { 160, 120 }, { 176, 144 }, - { 320, 240 }, { 352, 288 }, { 640, 480 }, - { 704, 576 }, { 800, 600 }, { 960, 720 }, - { 1280, 720 }, { 1024, 768 }, { 1440, 1080 }, - { 1920, 1080 } }; - - int index = 0; - for (int fmts = 0; fmts < totalFmts; fmts++) - { - for (int i = 0; i < sizes; i++) - { - video_fmt.fmt.pix.pixelformat = videoFormats[fmts]; - video_fmt.fmt.pix.width = size[i][0]; - video_fmt.fmt.pix.height = size[i][1]; - - if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) - { - if ((video_fmt.fmt.pix.width == size[i][0]) - && (video_fmt.fmt.pix.height == size[i][1])) - { - VideoCaptureCapability *cap = new VideoCaptureCapability(); - cap->width = video_fmt.fmt.pix.width; - cap->height = video_fmt.fmt.pix.height; - cap->expectedCaptureDelay = 120; - if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) - { - cap->rawType = kVideoYUY2; - } - - // get fps of current camera mode - // V4l2 does not have a stable method of knowing so we just guess. - if(cap->width>=800) - { - cap->maxFPS = 15; - } - else - { - cap->maxFPS = 30; - } - - _captureCapabilities.Insert(index, cap); - index++; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Camera capability, width:%d height:%d type:%d fps:%d", - cap->width, cap->height, cap->rawType, cap->maxFPS); - } - } - } - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "CreateCapabilityMap %d", - _captureCapabilities.Size()); - return _captureCapabilities.Size(); -} - -// this function doesn't quite work because of work on v4l2 -// the ioctl function doesn't work on many systems -// We will return 0 as a default. Cannot use -1 because it's unsigned num -// not full implemented -// see: http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html -// for information -bool DeviceInfoLinux::GetMaxFPS(int fd, VideoCaptureCapability* cap) -{ - struct v4l2_frmivalenum video_enum; - memset(&video_enum, 0, sizeof(struct v4l2_frmivalenum)); - - // try to open control and query about frame stuff - if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &video_enum) >= 0) - { - switch (video_enum.type) - { - case V4L2_FRMIVAL_TYPE_DISCRETE: - { - v4l2_fract discrete = video_enum.discrete; - cap->maxFPS = (WebRtc_UWord32)(discrete.numerator - / (float) discrete.denominator); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "FrameSize type is DISCRETE. Numerator=%u Denominator=%u FPS=%u", - discrete.numerator, discrete.denominator, cap->maxFPS); - break; - } - case V4L2_FRMIVAL_TYPE_STEPWISE: // stepwise and continuous are close enough for what we need - case V4L2_FRMIVAL_TYPE_CONTINUOUS: - { - v4l2_frmival_stepwise stepwise = video_enum.stepwise; - v4l2_fract min = stepwise.min; - v4l2_fract max = stepwise.max; - cap->maxFPS = (WebRtc_UWord32)(max.numerator / (float) max.denominator); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, - _id, - "FrameSize type is STEPWISE or CONTINUOUS. max.num=%u max.den=%u FPS=%u", - max.numerator, max.denominator, cap->maxFPS); - break; - } - default: - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "video_enum type is unknown"); - return false; - break; - } - } - } - else - { - // having problems w/ ioctl.... get error - switch (errno) - { - case EBADF: - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Could not query capture device for framerate. Error:EBADF"); - break; - case EFAULT: - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Could not query capture device for framerate. Error:EFAULT"); - break; - case EINVAL: - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Could not query capture device for framerate. Error:EINVAL"); - break; - case ENOTTY: - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Could not query capture device for framerate. Error:ENOTTY"); - break; - default: - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Could not query capture device for framerate. Error:undocumented by \"man ioctl\""); - break; - } - } - - return false; -} -}// namespace videocapturemodule -} // namespace webrtc - diff --git a/modules/video_capture/main/source/Linux/device_info_linux.h b/modules/video_capture/main/source/Linux/device_info_linux.h deleted file mode 100644 index bf73d7aca..000000000 --- a/modules/video_capture/main/source/Linux/device_info_linux.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ - -#include "../video_capture_impl.h" -#include "../device_info_impl.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -class DeviceInfoLinux: public DeviceInfoImpl -{ -public: - DeviceInfoLinux(const WebRtc_Word32 id); - virtual ~DeviceInfoLinux(); - virtual WebRtc_UWord32 NumberOfDevices(); - virtual WebRtc_Word32 GetDeviceName(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8=0, - WebRtc_UWord32 productUniqueIdUTF8Length=0); - /* - * Fills the membervariable _captureCapabilities with capabilites for the given device name. - */ - virtual WebRtc_Word32 CreateCapabilityMap (const WebRtc_UWord8* deviceUniqueIdUTF8); - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox(const WebRtc_UWord8* /*deviceUniqueIdUTF8*/, - const WebRtc_UWord8* /*dialogTitleUTF8*/, - void* /*parentWindow*/, - WebRtc_UWord32 /*positionX*/, - WebRtc_UWord32 /*positionY*/) { return -1;} - WebRtc_Word32 FillCapabilityMap(int fd); - WebRtc_Word32 Init(); -private: - - bool IsDeviceNameMatches(const char* name, const char* deviceUniqueIdUTF8); - bool GetMaxFPS(int fd, VideoCaptureCapability* cap); -}; -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ diff --git a/modules/video_capture/main/source/Linux/video_capture_linux.cc b/modules/video_capture/main/source/Linux/video_capture_linux.cc deleted file mode 100644 index fc6d2e84e..000000000 --- a/modules/video_capture/main/source/Linux/video_capture_linux.cc +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "trace.h" -#include "thread_wrapper.h" -#include "critical_section_wrapper.h" -#include "video_capture_linux.h" - -namespace webrtc -{ -VideoCaptureModule* VideoCaptureModule::Create(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueId) -{ - - videocapturemodule::VideoCaptureModuleV4L2* interface = - new videocapturemodule::VideoCaptureModuleV4L2(id); - if (!interface || interface->Init(deviceUniqueId) != 0) - { - Destroy(interface); - interface = NULL; - } - - return interface; -} - -namespace videocapturemodule -{ -VideoCaptureModuleV4L2::VideoCaptureModuleV4L2(const WebRtc_Word32 id) - : VideoCaptureImpl(id), _captureThread(NULL), - _captureCritSect(CriticalSectionWrapper::CreateCriticalSection()), - _deviceId(-1), _currentWidth(-1), _currentHeight(-1), - _currentFrameRate(-1), _captureStarted(false), _captureVideoType(kVideoI420) -{ -} - -WebRtc_Word32 VideoCaptureModuleV4L2::Init(const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - int len = strlen((const char*) deviceUniqueIdUTF8); - _deviceUniqueId = new (std::nothrow) WebRtc_UWord8[len + 1]; - if (_deviceUniqueId) - { - memcpy(_deviceUniqueId, deviceUniqueIdUTF8, len + 1); - } - - int fd; - char device[32]; - bool found = false; - - /* detect /dev/video [0-63] entries */ - int n; - for (n = 0; n < 64; n++) - { - struct stat s; - sprintf(device, "/dev/video%d", n); - if (stat(device, &s) == 0) //check validity of path - { - if ((fd = open(device, O_RDONLY)) > 0) - { - // query device capabilities - struct v4l2_capability cap; - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) - { - if (cap.bus_info[0] != 0) - { - if (strncmp((const char*) cap.bus_info, - (const char*) deviceUniqueIdUTF8, - strlen((const char*) deviceUniqueIdUTF8)) == 0) //match with device id - { - close(fd); - found = true; - break; // fd matches with device unique id supplied - } - } - } - close(fd); // close since this is not the matching device - } - } - } - if (!found) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "no matching device found"); - return -1; - } - _deviceId = n; //store the device id - return 0; -} - -VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() -{ - StopCapture(); - if (_captureCritSect) - { - delete _captureCritSect; - } - close(_deviceFd); -} - -WebRtc_Word32 VideoCaptureModuleV4L2::StartCapture( - const VideoCaptureCapability& capability) -{ - if (_captureStarted) - { - if (capability.width == _currentWidth - && capability.height == _currentHeight - && _captureVideoType == capability.rawType) - { - return 0; - } - else - { - StopCapture(); - } - } - - CriticalSectionScoped cs(*_captureCritSect); - //first open /dev/video device - char device[20]; - sprintf(device, "/dev/video%d", (int) _deviceId); - - if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "error in opening %s errono = %d", device, errno); - return -1; - } - - int nFormats = 2; - unsigned int fmts[2] = { V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YUYV }; - - struct v4l2_format video_fmt; - memset(&video_fmt, 0, sizeof(struct v4l2_format)); - video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - video_fmt.fmt.pix.sizeimage = 0; - video_fmt.fmt.pix.width = capability.width; - video_fmt.fmt.pix.height = capability.height; - - bool formatMatch = false; - for (int i = 0; i < nFormats; i++) - { - video_fmt.fmt.pix.pixelformat = fmts[i]; - if (ioctl(_deviceFd, VIDIOC_TRY_FMT, &video_fmt) < 0) - { - continue; - } - else - { - formatMatch = true; - break; - } - } - if (!formatMatch) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "no supporting video formats found"); - return -1; - } - if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - _captureVideoType = kVideoYUY2; - else - _captureVideoType = kVideoI420; - - //set format and frame size now - if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "error in VIDIOC_S_FMT, errno = %d", errno); - return -1; - } - - // initialize current width and height - _currentWidth = video_fmt.fmt.pix.width; - _currentHeight = video_fmt.fmt.pix.height; - _currentFrameRate=30; // No way of knowing on Linux. - - if (!AllocateVideoBuffers()) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "failed to allocate video capture buffers"); - return -1; - } - - //start capture thread; - if (!_captureThread) - { - _captureThread = ThreadWrapper::CreateThread(VideoCaptureModuleV4L2::CaptureThread, - this, - kHighPriority); - unsigned int id; - _captureThread->Start(id); - } - - // Needed to start UVC camera - from the uvcview application - enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "Failed to turn on stream"); - return -1; - } - - _captureStarted = true; - return 0; -} - -WebRtc_Word32 VideoCaptureModuleV4L2::StopCapture() -{ - if (_captureThread) - _captureThread->SetNotAlive();// Make sure the capture thread stop stop using the critsect. - - - CriticalSectionScoped cs(*_captureCritSect); - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, -1, "StopCapture(), was running: %d", - _captureStarted); - - if (!_captureStarted) - { - // we were not capturing! - return 0; - } - - _captureStarted = false; - - // stop the capture thread - // Delete capture update thread and event - if (_captureThread) - { - ThreadWrapper* temp = _captureThread; - _captureThread = NULL; - temp->SetNotAlive(); - if (temp->Stop()) - { - delete temp; - } - } - - DeAllocateVideoBuffers(); - close(_deviceFd); - _deviceFd = -1; - - return 0; -} - -//critical section protected by the caller - -bool VideoCaptureModuleV4L2::AllocateVideoBuffers() -{ - struct v4l2_requestbuffers rbuffer; - memset(&rbuffer, 0, sizeof(v4l2_requestbuffers)); - - rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rbuffer.memory = V4L2_MEMORY_MMAP; - rbuffer.count = kNoOfV4L2Bufffers; - - if (ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Could not get buffers from device. errno = %d", errno); - return false; - } - - if (rbuffer.count > kNoOfV4L2Bufffers) - rbuffer.count = kNoOfV4L2Bufffers; - - _buffersAllocatedByDevice = rbuffer.count; - - //Map the buffers - pool = new Buffer[rbuffer.count]; - - for (unsigned int i = 0; i < rbuffer.count; i++) - { - struct v4l2_buffer buffer; - memset(&buffer, 0, sizeof(v4l2_buffer)); - buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buffer.memory = V4L2_MEMORY_MMAP; - buffer.index = i; - - if (ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer) < 0) - { - return false; - } - - pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, - _deviceFd, buffer.m.offset); - - if (MAP_FAILED == pool[i].start) - { - for (int j = 0; j < i; j++) - munmap(pool[j].start, pool[j].length); - return false; - } - - pool[i].length = buffer.length; - - if (ioctl(_deviceFd, VIDIOC_QBUF, &buffer) < 0) - { - return false; - } - } - return true; -} - -bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() -{ - // unmap buffers - for (int i = 0; i < _buffersAllocatedByDevice; i++) - munmap(pool[i].start, pool[i].length); - - delete[] pool; - - // turn off stream - enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_STREAMOFF, &type) < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "VIDIOC_STREAMOFF error. errno: %d", errno); - } - - return true; -} - -bool VideoCaptureModuleV4L2::CaptureStarted() -{ - return _captureStarted; -} - -bool VideoCaptureModuleV4L2::CaptureThread(void* obj) -{ - return static_cast (obj)->CaptureProcess(); -} -bool VideoCaptureModuleV4L2::CaptureProcess() -{ - int retVal = 0; - fd_set rSet; - struct timeval timeout; - - _captureCritSect->Enter(); - if (!_captureThread) - { - // terminating - _captureCritSect->Leave(); - return false; - } - - FD_ZERO(&rSet); - FD_SET(_deviceFd, &rSet); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout); - if (retVal < 0 && errno != EINTR) // continue if interrupted - { - // select failed - _captureCritSect->Leave(); - return false; - } - else if (retVal == 0) - { - // select timed out - _captureCritSect->Leave(); - return true; - } - else if (!FD_ISSET(_deviceFd, &rSet)) - { - // not event on camera handle - _captureCritSect->Leave(); - return true; - } - - if (_captureStarted) - { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(struct v4l2_buffer)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - // dequeue a buffer - repeat until dequeued properly! - while (ioctl(_deviceFd, VIDIOC_DQBUF, &buf) < 0) - { - if (errno != EINTR) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "could not sync on a buffer on device %s", strerror(errno)); - _captureCritSect->Leave(); - return true; - } - } - VideoCaptureCapability frameInfo; - frameInfo.width = _currentWidth; - frameInfo.height = _currentHeight; - frameInfo.rawType = _captureVideoType; - - // convert to to I420 if needed - IncomingFrame((unsigned char*) pool[buf.index].start, - buf.bytesused, frameInfo); - // enqueue the buffer again - if (ioctl(_deviceFd, VIDIOC_QBUF, &buf) == -1) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Failed to enqueue capture buffer"); - } - } - _captureCritSect->Leave(); - usleep(0); - return true; -} - -WebRtc_Word32 VideoCaptureModuleV4L2::CaptureSettings(VideoCaptureCapability& settings) -{ - settings.width = _currentWidth; - settings.height = _currentHeight; - settings.maxFPS = _currentFrameRate; - settings.rawType=_captureVideoType; - - return 0; -} -} // namespace videocapturemodule -} // namespace webrtc diff --git a/modules/video_capture/main/source/Linux/video_capture_linux.h b/modules/video_capture/main/source/Linux/video_capture_linux.h deleted file mode 100644 index 54c510fe3..000000000 --- a/modules/video_capture/main/source/Linux/video_capture_linux.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ - -#include "common_types.h" -#include "../video_capture_impl.h" - -namespace webrtc -{ -class CriticalSectionWrapper; -class ThreadWrapper; -namespace videocapturemodule -{ -class VideoCaptureModuleV4L2: public VideoCaptureImpl -{ -public: - VideoCaptureModuleV4L2(WebRtc_Word32 id); - virtual ~VideoCaptureModuleV4L2(); - virtual WebRtc_Word32 Init(const WebRtc_UWord8* deviceUniqueId); - virtual WebRtc_Word32 StartCapture(const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - virtual bool CaptureStarted(); - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - -private: - enum {kNoOfV4L2Bufffers=4}; - - static bool CaptureThread(void*); - bool CaptureProcess(); - bool AllocateVideoBuffers(); - bool DeAllocateVideoBuffers(); - - ThreadWrapper* _captureThread; - CriticalSectionWrapper* _captureCritSect; - - WebRtc_Word32 _deviceId; - WebRtc_Word32 _deviceFd; - - WebRtc_Word32 _buffersAllocatedByDevice; - WebRtc_Word32 _currentWidth; - WebRtc_Word32 _currentHeight; - WebRtc_Word32 _currentFrameRate; - bool _captureStarted; - RawVideoType _captureVideoType; - struct Buffer - { - void *start; - size_t length; - }; - Buffer *pool; -}; -} // namespace videocapturemodule -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.cc b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.cc deleted file mode 100644 index a99dd332c..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.cc +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_capture_qtkit.h" -#include "video_capture_qtkit_objc.h" -#include "video_capture_qtkit_info_objc.h" -#include "trace.h" -#include "critical_section_wrapper.h" -#include "../../video_capture_config.h" - -namespace webrtc -{ - -/* - * Returns version of the module and its components - * - * version - buffer to which the version will be written - * remainingBufferInBytes - remaining number of WebRtc_Word8 in the version - * buffer - * position - position of the next empty WebRtc_Word8 in the - * version buffer - */ - -WebRtc_Word32 VideoCaptureModule::GetVersion( - WebRtc_Word8* version, WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - return webrtc::videocapturemodule::VideoCaptureMacQTKit::GetVersion( - version, remainingBufferInBytes, position); -} - -namespace videocapturemodule -{ - -VideoCaptureMacQTKit::VideoCaptureMacQTKit(const WebRtc_Word32 id) : - VideoCaptureImpl(id), - _id(id), - _captureWidth(QTKIT_DEFAULT_WIDTH), - _captureHeight(QTKIT_DEFAULT_HEIGHT), - _captureFrameRate(QTKIT_DEFAULT_FRAME_RATE), - _isCapturing(false), - _frameCount(0) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "VideoCaptureMacQTKit::VideoCaptureMacQTKit() called"); - - memset(_currentDeviceNameUTF8, 0, MAX_NAME_LENGTH); - memset(_currentDeviceUniqueIdUTF8, 0, MAX_NAME_LENGTH); - memset(_currentDeviceProductUniqueIDUTF8, 0, MAX_NAME_LENGTH); -} - -VideoCaptureMacQTKit::~VideoCaptureMacQTKit() -{ - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "~VideoCaptureMacQTKit() called"); - if(_captureDevice) - { - [_captureDevice stopCapture]; - [_captureDevice release]; - } - - if(_captureInfo) - { - [_captureInfo release]; - } -} - -WebRtc_Word32 VideoCaptureMacQTKit::Init( - const WebRtc_Word32 id, const WebRtc_UWord8* iDeviceUniqueIdUTF8) -{ - CriticalSectionScoped cs(_apiCs); - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "VideoCaptureMacQTKit::Init() called with id %d and unique " - "device %s", id, iDeviceUniqueIdUTF8); - - WebRtc_Word32 result=0; - const WebRtc_Word32 nameLength = - (WebRtc_Word32) strlen((char*)iDeviceUniqueIdUTF8); - if(nameLength>kVideoCaptureUniqueNameLength) - return -1; - - // Store the device name - _deviceUniqueId = new WebRtc_UWord8[nameLength+1]; - memcpy(_deviceUniqueId, iDeviceUniqueIdUTF8,nameLength+1); - - _captureDevice = [[VideoCaptureMacQTKitObjC alloc] init]; - if(NULL == _captureDevice) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "Failed to create an instance of " - "VideoCaptureMacQTKitObjC"); - return -1; - } - - if(-1 == [[_captureDevice registerOwner:this]intValue]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "Failed to register owner for _captureDevice"); - return -1; - } - - if(0 == strcmp((char*)iDeviceUniqueIdUTF8, "")) - { - // the user doesn't want to set a capture device at this time - return 0; - } - - _captureInfo = [[VideoCaptureMacQTKitInfoObjC alloc]init]; - if(nil == _captureInfo) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, "Failed to create an instance of VideoCaptureMacQTKitInfoObjC"); - return -1; - } - - int captureDeviceCount = [[_captureInfo getCaptureDeviceCount]intValue]; - if(captureDeviceCount < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "No Capture Devices Present"); - return -1; - } - - const int NAME_LENGTH = 1024; - WebRtc_UWord8 deviceNameUTF8[1024] = ""; - WebRtc_UWord8 deviceUniqueIdUTF8[1024] = ""; - WebRtc_UWord8 deviceProductUniqueIDUTF8[1024] = ""; - - bool captureDeviceFound = false; - for(int index = 0; index < captureDeviceCount; index++){ - - memset(deviceNameUTF8, 0, NAME_LENGTH); - memset(deviceUniqueIdUTF8, 0, NAME_LENGTH); - memset(deviceProductUniqueIDUTF8, 0, NAME_LENGTH); - if(-1 == [[_captureInfo getDeviceNamesFromIndex:index - DefaultName:deviceNameUTF8 WithLength:NAME_LENGTH - AndUniqueID:deviceUniqueIdUTF8 WithLength:NAME_LENGTH - AndProductID:deviceProductUniqueIDUTF8 - WithLength:NAME_LENGTH]intValue]) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "GetDeviceName returned -1 for index %d", index); - return -1; - } - if(0 == strcmp((const char*)iDeviceUniqueIdUTF8, - (char*)deviceUniqueIdUTF8)) - { - // we have a match - captureDeviceFound = true; - break; - } - } - - if(false == captureDeviceFound) - { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Failed to find capture device unique ID %s", - iDeviceUniqueIdUTF8); - return -1; - } - - // at this point we know that the user has passed in a valid camera. Let's - // set it as the current. - if(-1 == [[_captureDevice - setCaptureDeviceByName:(char*)deviceNameUTF8]intValue]) - { - strcpy((char*)_deviceUniqueId, (char*)deviceNameUTF8); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to set capture device %s (unique ID %s) even " - "though it was a valid return from " - "VideoCaptureMacQTKitInfo"); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "successfully Init VideoCaptureMacQTKit" ); - return 0; -} - -WebRtc_Word32 VideoCaptureMacQTKit::StartCapture( - const VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "StartCapture width %d, height %d, frameRate %d", - capability.width, capability.height, capability.maxFPS); - - _captureWidth = capability.width; - _captureHeight = capability.height; - _captureFrameRate = capability.maxFPS; - - if(-1 == [[_captureDevice setCaptureHeight:_captureHeight - AndWidth:_captureWidth AndFrameRate:_captureFrameRate]intValue]) - { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Could not set width=%d height=%d frameRate=%d", - _captureWidth, _captureHeight, _captureFrameRate); - return -1; - } - - if(-1 == [[_captureDevice startCapture]intValue]) - { - return -1; - } - _isCapturing = true; - return 0; -} - -WebRtc_Word32 VideoCaptureMacQTKit::StopCapture() -{ - [_captureDevice stopCapture]; - - _isCapturing = false; - return 0; -} - -bool VideoCaptureMacQTKit::CaptureStarted() -{ - return _isCapturing; -} - -WebRtc_Word32 VideoCaptureMacQTKit::CaptureSettings(VideoCaptureCapability& settings) -{ - settings.width = _captureWidth; - settings.height = _captureHeight; - settings.maxFPS = _captureFrameRate; - return 0; -} - - -// ********** begin functions inherited from DeviceInfoImpl ********** - -struct VideoCaptureCapabilityMacQTKit:public VideoCaptureCapability -{ - VideoCaptureCapabilityMacQTKit() - { - } -}; -} // namespace videocapturemodule -} // namespace webrtc - diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.h deleted file mode 100644 index 19a9729db..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_H_ - -#import - -#include - -#include "../../video_capture_impl.h" -#include "video_capture_qtkit_utility.h" -#include "../../device_info_impl.h" -#include "vplib.h" - - -// Forward declaraion -@class VideoCaptureMacQTKitObjC; -@class VideoCaptureMacQTKitInfoObjC; - -namespace webrtc -{ -namespace videocapturemodule -{ - -class VideoCaptureMacQTKit : public VideoCaptureImpl -{ -public: - VideoCaptureMacQTKit(const WebRtc_Word32 id); - virtual ~VideoCaptureMacQTKit(); - - /* - * Create a video capture module object - * - * id - unique identifier of this video capture module object - * deviceUniqueIdUTF8 - name of the device. Available names can be found - * by using GetDeviceName - * deviceUniqueIdUTF8Length - length of deviceUniqueIdUTF8 - */ - static void Destroy(VideoCaptureModule* module); - - WebRtc_Word32 Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - - - // Start/Stop - virtual WebRtc_Word32 StartCapture( - const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - - // Properties of the set device - - virtual bool CaptureStarted(); - - WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - -protected: - // Help functions - WebRtc_Word32 SetCameraOutput(); - -private: - VideoCaptureMacQTKitObjC* _captureDevice; - VideoCaptureMacQTKitInfoObjC* _captureInfo; - bool _isCapturing; - WebRtc_Word32 _id; - WebRtc_Word32 _captureWidth; - WebRtc_Word32 _captureHeight; - WebRtc_Word32 _captureFrameRate; - WebRtc_UWord8 _currentDeviceNameUTF8[MAX_NAME_LENGTH]; - WebRtc_UWord8 _currentDeviceUniqueIdUTF8[MAX_NAME_LENGTH]; - WebRtc_UWord8 _currentDeviceProductUniqueIDUTF8[MAX_NAME_LENGTH]; - WebRtc_Word32 _frameCount; -}; -} // namespace videocapturemodule -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.cc b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.cc deleted file mode 100644 index 89c003732..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "../../video_capture_config.h" -#include "video_capture_qtkit_info_objc.h" - -#include "video_capture.h" - -namespace webrtc -{ -namespace videocapturemodule -{ - -VideoCaptureMacQTKitInfo::VideoCaptureMacQTKitInfo(const WebRtc_Word32 id) : - DeviceInfoImpl(id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - _captureInfo = [[VideoCaptureMacQTKitInfoObjC alloc] init]; -} - -VideoCaptureMacQTKitInfo::~VideoCaptureMacQTKitInfo() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - [_captureInfo release]; - -} - -WebRtc_Word32 VideoCaptureMacQTKitInfo::Init() -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - return 0; -} - -WebRtc_UWord32 VideoCaptureMacQTKitInfo::NumberOfDevices() -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - WebRtc_UWord32 captureDeviceCount = - [[_captureInfo getCaptureDeviceCount]intValue]; - return captureDeviceCount; - -} - -WebRtc_Word32 VideoCaptureMacQTKitInfo::GetDeviceName( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - int errNum = [[_captureInfo getDeviceNamesFromIndex:deviceNumber - DefaultName:deviceNameUTF8 WithLength:deviceNameLength - AndUniqueID:deviceUniqueIdUTF8 - WithLength:deviceUniqueIdUTF8Length - AndProductID:productUniqueIdUTF8 - WithLength:productUniqueIdUTF8Length]intValue]; - return errNum; -} - -WebRtc_Word32 VideoCaptureMacQTKitInfo::NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - // Not implemented. Mac doesn't use discrete steps in capabilities, rather - // "analog". QTKit will do it's best to convert frames to what ever format - // you ask for. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - - -WebRtc_Word32 VideoCaptureMacQTKitInfo::GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - // Not implemented. Mac doesn't use discrete steps in capabilities, rather - // "analog". QTKit will do it's best to convert frames to what ever format - // you ask for. - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - - -WebRtc_Word32 VideoCaptureMacQTKitInfo::GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, VideoCaptureCapability& resulting) -{ - // Not implemented. Mac doesn't use discrete steps in capabilities, rather - // "analog". QTKit will do it's best to convert frames to what ever format - // you ask for. - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - -WebRtc_Word32 VideoCaptureMacQTKitInfo::DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, void* parentWindow, - WebRtc_UWord32 positionX, WebRtc_UWord32 positionY) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - return [[_captureInfo - displayCaptureSettingsDialogBoxWithDevice:deviceUniqueIdUTF8 - AndTitle:dialogTitleUTF8 - AndParentWindow:parentWindow AtX:positionX AndY:positionY] - intValue]; -} - -WebRtc_Word32 VideoCaptureMacQTKitInfo::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - // Not implemented. Mac doesn't use discrete steps in capabilities, rather - // "analog". QTKit will do it's best to convert frames to what ever format - // you ask for. - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} -} // namespace videocapturemodule -} // namespace webrtc diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.h deleted file mode 100644 index 0c83f8a2a..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_H_ - -#include "../../video_capture_impl.h" -#include "../../device_info_impl.h" -#include "video_capture_qtkit_utility.h" - -#include "map_wrapper.h" - - -@class VideoCaptureMacQTKitInfoObjC; - -namespace webrtc -{ -namespace videocapturemodule -{ - -class VideoCaptureMacQTKitInfo: public DeviceInfoImpl -{ -public: - - VideoCaptureMacQTKitInfo(const WebRtc_Word32 id); - virtual ~VideoCaptureMacQTKitInfo(); - - WebRtc_Word32 Init(); - - virtual WebRtc_UWord32 NumberOfDevices(); - - /* - * Returns the available capture devices. - * deviceNumber -[in] index of capture device - * deviceNameUTF8 - friendly name of the capture device - * deviceUniqueIdUTF8 - unique name of the capture device if it exist. - * Otherwise same as deviceNameUTF8 - * productUniqueIdUTF8 - unique product id if it exist. Null terminated - * otherwise. - */ - virtual WebRtc_Word32 GetDeviceName( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8 = 0, - WebRtc_UWord32 productUniqueIdUTF8Length = 0); - - /* - * Returns the number of capabilities for this device - */ - virtual WebRtc_Word32 NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8); - - /* - * Gets the capabilities of the named device - */ - virtual WebRtc_Word32 GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability); - - /* - * Gets the capability that best matches the requested width, height and frame rate. - * Returns the deviceCapabilityNumber on success. - */ - virtual WebRtc_Word32 GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting); - - /* - * Display OS /capture device specific settings dialog - */ - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, void* parentWindow, - WebRtc_UWord32 positionX, WebRtc_UWord32 positionY); - -protected: - virtual WebRtc_Word32 CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8); - - VideoCaptureMacQTKitInfoObjC* _captureInfo; -}; -} // namespace videocapturemodule -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.h deleted file mode 100644 index f692b5aad..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// -// video_capture_qtkit_info_objc.h -// -// - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_OBJC_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_OBJC_H_ - -#import -#import -#include "video_capture_qtkit_utility.h" -#include "video_capture_qtkit_info.h" - - -using namespace webrtc; - -@interface VideoCaptureMacQTKitInfoObjC : NSObject{ - bool _OSSupportedInfo; - NSArray* _captureDevicesInfo; - NSAutoreleasePool* _poolInfo; - int _captureDeviceCountInfo; - -} - -/************************************************************************** - * - * The following functions are considered to be private - * - ***************************************************************************/ - -- (NSNumber*)getCaptureDevices; -- (NSNumber*)initializeVariables; -- (void)checkOSSupported; - - -/************************************************************************** - * - * The following functions are considered to be public and called by VideoCaptureMacQTKitInfo class - * - ***************************************************************************/ - -- (NSNumber*)getCaptureDeviceCount; - -- (NSNumber*)getDeviceNamesFromIndex:(WebRtc_UWord32)index - DefaultName:(WebRtc_UWord8*)deviceName - WithLength:(WebRtc_UWord32)deviceNameLength - AndUniqueID:(WebRtc_UWord8*)deviceUniqueID - WithLength:(WebRtc_UWord32)deviceUniqueIDLength - AndProductID:(WebRtc_UWord8*)deviceProductID - WithLength:(WebRtc_UWord32)deviceProductIDLength; - -- (NSNumber*)displayCaptureSettingsDialogBoxWithDevice: - (const WebRtc_UWord8*)deviceUniqueIdUTF8 - AndTitle:(const WebRtc_UWord8*)dialogTitleUTF8 - AndParentWindow:(void*) parentWindow AtX:(WebRtc_UWord32)positionX - AndY:(WebRtc_UWord32) positionY; -@end - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_INFO_OBJC_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.mm b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.mm deleted file mode 100644 index d5be0d88a..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_info_objc.mm +++ /dev/null @@ -1,202 +0,0 @@ -// -// VideoCaptureMacQTKitInfoObjC.cpp -// -// - -#pragma mark **** imports/includes - - -#import "video_capture_qtkit_info_objc.h" - -#include "trace.h" - - - -#pragma mark **** hidden class interface - - -@implementation VideoCaptureMacQTKitInfoObjC - -// ****************** over-written OS methods *********************** -#pragma mark **** over-written OS methods - -/// ***** Objective-C. Similar to C++ constructor, although invoked manually -/// ***** Potentially returns an instance of self --(id)init{ - self = [super init]; - if(nil != self){ - [self checkOSSupported]; - [self initializeVariables]; - } - else - { - return nil; - } - return self; -} - -/// ***** Objective-C. Similar to C++ destructor -/// ***** Returns nothing -- (void)dealloc { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - [super dealloc]; -} - -// ****************** public methods ****************** -#pragma mark **** public method implementations - -/// ***** Creates a message box with Cocoa framework -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)displayCaptureSettingsDialogBoxWithDevice:(const WebRtc_UWord8*)deviceUniqueIdUTF8 - AndTitle:(const WebRtc_UWord8*)dialogTitleUTF8 - AndParentWindow:(void*) parentWindow - AtX:(WebRtc_UWord32)positionX - AndY:(WebRtc_UWord32) positionY -{ - NSString* strTitle = [NSString stringWithFormat:@"%s", dialogTitleUTF8]; - NSString* strButton = @"Alright"; - NSString* strMessage = [NSString stringWithFormat:@"Device %s is capturing:\nWidth:%d\n:Height:%d\n@%dfps", deviceUniqueIdUTF8]; - NSAlert* alert = [NSAlert alertWithMessageText:strTitle - defaultButton:strButton - alternateButton:nil otherButton:nil - informativeTextWithFormat:strMessage]; - [alert setAlertStyle:NSInformationalAlertStyle]; - [alert runModal]; - return [NSNumber numberWithInt:0]; -} - -- (NSNumber*)getCaptureDeviceCount{ - [self getCaptureDevices]; - return [NSNumber numberWithInt:_captureDeviceCountInfo]; -} - - -- (NSNumber*)getDeviceNamesFromIndex:(WebRtc_UWord32)index - DefaultName:(WebRtc_UWord8*)deviceName - WithLength:(WebRtc_UWord32)deviceNameLength - AndUniqueID:(WebRtc_UWord8*)deviceUniqueID - WithLength:(WebRtc_UWord32)deviceUniqueIDLength - AndProductID:(WebRtc_UWord8*)deviceProductID - WithLength:(WebRtc_UWord32)deviceProductIDLength -{ - if(NO == _OSSupportedInfo) - { - return [NSNumber numberWithInt:0]; - } - - if(index > (WebRtc_UWord32)_captureDeviceCountInfo) - { - return [NSNumber numberWithInt:-1]; - } - - QTCaptureDevice* tempCaptureDevice = - (QTCaptureDevice*)[_captureDevicesInfo objectAtIndex:index]; - if(!tempCaptureDevice) - { - return [NSNumber numberWithInt:-1]; - } - - memset(deviceName, 0, deviceNameLength); - memset(deviceUniqueID, 0, deviceUniqueIDLength); - - bool successful = NO; - - NSString* tempString = [tempCaptureDevice localizedDisplayName]; - successful = [tempString getCString:(char*)deviceName - maxLength:deviceNameLength encoding:NSUTF8StringEncoding]; - if(NO == successful) - { - memset(deviceName, 0, deviceNameLength); - return [NSNumber numberWithInt:-1]; - } - - tempString = [tempCaptureDevice uniqueID]; - successful = [tempString getCString:(char*)deviceUniqueID - maxLength:deviceUniqueIDLength encoding:NSUTF8StringEncoding]; - if(NO == successful) - { - memset(deviceUniqueID, 0, deviceNameLength); - return [NSNumber numberWithInt:-1]; - } - - return [NSNumber numberWithInt:0]; - -} - -// ****************** "private" category functions below here ****************** -#pragma mark **** "private" method implementations - -- (NSNumber*)getCaptureDeviceWithIndex:(int)index ToString:(char*)name - WithLength:(int)length -{ - index = index; - name = name; - length = length; - return [NSNumber numberWithInt:0]; -} - -- (NSNumber*)setCaptureDeviceByIndex:(int)index -{ - index = index; - return [NSNumber numberWithInt:0]; -} - -- (NSNumber*)initializeVariables -{ - if(NO == _OSSupportedInfo) - { - return [NSNumber numberWithInt:0]; - } - - _poolInfo = [[NSAutoreleasePool alloc]init]; - _captureDeviceCountInfo = 0; - [self getCaptureDevices]; - - return [NSNumber numberWithInt:0]; -} - -// ***** Checks to see if the QTCaptureSession framework is available in the OS -// ***** If it is not, isOSSupprted = NO -// ***** Throughout the rest of the class isOSSupprted is checked and functions -// ***** are/aren't called depending -// ***** The user can use weak linking to the QTKit framework and run on older -// ***** versions of the OS -// ***** I.E. Backwards compaitibility -// ***** Returns nothing. Sets member variable -- (void)checkOSSupported -{ - Class osSupportedTest = NSClassFromString(@"QTCaptureSession"); - _OSSupportedInfo = NO; - if(nil == osSupportedTest) - { - } - _OSSupportedInfo = YES; -} - -/// ***** Retrieves the number of capture devices currently available -/// ***** Stores them in an NSArray instance -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)getCaptureDevices -{ - if(NO == _OSSupportedInfo) - { - return [NSNumber numberWithInt:0]; - } - - if(_captureDevicesInfo) - { - [_captureDevicesInfo release]; - } - _captureDevicesInfo = [[NSArray alloc] - initWithArray:[QTCaptureDevice - inputDevicesWithMediaType:QTMediaTypeVideo]]; - - _captureDeviceCountInfo = _captureDevicesInfo.count; - if(_captureDeviceCountInfo < 1){ - return [NSNumber numberWithInt:0]; - } - return [NSNumber numberWithInt:0]; -} - -@end diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.h deleted file mode 100644 index c2b173e88..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// -// video_capture_qtkit_objc.h -// -// - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_OBJC_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_OBJC_H_ - -#import -#import -#import -#import -#import -#import - - - -#import "video_capture_recursive_lock.h" - - -#include "video_capture_qtkit.h" - -using namespace webrtc; -using namespace videocapturemodule; - - - -@interface VideoCaptureMacQTKitObjC : NSObject{ - // class properties - bool _capturing; - int _counter; - int _frameRate; - int _frameWidth; - int _frameHeight; - int _framesDelivered; - int _framesRendered; - bool _OSSupported; - bool _captureInitialized; - - // WebRTC Custom classes - VideoCaptureMacQTKit* _owner; - VideoCaptureRecursiveLock* _rLock; - - // QTKit variables - QTCaptureSession* _captureSession; - QTCaptureDeviceInput* _captureVideoDeviceInput; - QTCaptureDecompressedVideoOutput* _captureDecompressedVideoOutput; - NSArray* _captureDevices; - int _captureDeviceCount; - int _captureDeviceIndex; - NSString* _captureDeviceName; - char _captureDeviceNameUTF8[1024]; - char _captureDeviceNameUniqueID[1024]; - char _captureDeviceNameProductID[1024]; - NSString* _key; - NSNumber* _val; - NSDictionary* _videoSettings; - NSString* _captureQuality; - - // other - NSAutoreleasePool* _pool; - -} -/************************************************************************** - * - * The following functions are considered to be private. - * - ***************************************************************************/ - -- (NSNumber*)getCaptureDevices; -- (NSNumber*)initializeVideoCapture; -- (NSNumber*)initializeVariables; -- (void)checkOSSupported; - - -/************************************************************************** - * - * The following functions are considered public and to be called by the VideoCaptureMacQTKit class. - * - ***************************************************************************/ - - -- (NSNumber*)getCaptureDeviceWithIndex:(int)index ToString:(char*)name WithLength:(int)length; -- (NSNumber*)registerOwner:(webrtc::videocapturemodule::VideoCaptureMacQTKit*)owner; -- (NSNumber*)setCaptureDeviceByIndex:(int)index; -- (NSNumber*)setCaptureDeviceByName:(char*)name; -- (NSNumber*)setCaptureHeight:(int)height AndWidth:(int)width AndFrameRate:(int)frameRate; -- (NSNumber*)startCapture; -- (NSNumber*)stopCapture; - -@end - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_OBJC_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.mm b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.mm deleted file mode 100644 index 463db0f54..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_objc.mm +++ /dev/null @@ -1,501 +0,0 @@ -// -// VideoCaptureMacQTKitObjC.cpp -// -// - - -#define DEFAULT_CAPTURE_DEVICE_INDEX 1 -#define DEFAULT_FRAME_RATE 30 -#define DEFAULT_FRAME_WIDTH 352 -#define DEFAULT_FRAME_HEIGHT 288 -#define ROTATE_CAPTURED_FRAME 1 -#define LOW_QUALITY 1 - - - -#import "video_capture_qtkit_objc.h" -#include "video_capture_qtkit_utility.h" -#include "trace.h" - - -@implementation VideoCaptureMacQTKitObjC - -#pragma mark **** over-written OS methods - -/// ***** Objective-C. Similar to C++ constructor, although must be invoked -/// manually. -/// ***** Potentially returns an instance of self --(id)init{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - self = [super init]; - if(nil != self) - { - [self checkOSSupported]; - [self initializeVariables]; - } - else - { - return nil; - } - return self; -} - -/// ***** Objective-C. Similar to C++ destructor -/// ***** Returns nothing -- (void)dealloc { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - if(_captureSession) - { - [_captureSession stopRunning]; - [_captureSession release]; - } - [super dealloc]; -} - -#pragma mark **** public methods - - - -/// ***** Registers the class's owner, which is where the delivered frames are -/// sent -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)registerOwner:(VideoCaptureMacQTKit*)owner{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, "%s:%d", __FUNCTION__, __LINE__); - if(!owner){ - return [NSNumber numberWithInt:-1]; - } - _owner = owner; - return [NSNumber numberWithInt:0]; -} - -/// ***** Supposed to get capture device by index. -/// ***** Currently not used -- (NSNumber*)getCaptureDeviceWithIndex:(int)index ToString:(char*)name - WithLength:(int)length{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d index=%s name=%s", __FUNCTION__, __LINE__, name); - - index = index; - name = name; - length = length; - return [NSNumber numberWithInt:0]; -} - -/// ***** Supposed to set capture device by index. -/// ***** Currently not used -- (NSNumber*)setCaptureDeviceByIndex:(int)index { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d name", __FUNCTION__, __LINE__); - index = index; - return [NSNumber numberWithInt:0]; -} - -/// ***** Sets the QTCaptureSession's input device from a char* -/// ***** Sets several member variables. Can signal the error system if one has -/// occurred -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)setCaptureDeviceByName:(char*)name{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d name=%s", __FUNCTION__, __LINE__, name); - if(NO == _OSSupported) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d OS version does not support necessary APIs", - __FUNCTION__, __LINE__); - return [NSNumber numberWithInt:0]; - } - - if(!_captureSession) - { - } - - if(!name || (0 == strcmp("", name))) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d \"\" was passed in for capture device name", - __FUNCTION__, __LINE__); - memset(_captureDeviceNameUTF8, 0, 1024); - return [NSNumber numberWithInt:0]; - } - - if(0 == strcmp(name, _captureDeviceNameUTF8)) - { - // camera already set - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d Capture device is already set to %s", __FUNCTION__, - __LINE__, _captureDeviceNameUTF8); - return [NSNumber numberWithInt:0]; - } - - bool success = NO; - QTCaptureDevice* tempCaptureDevice; - for(int index = 0; index < _captureDeviceCount; index++) - { - tempCaptureDevice = (QTCaptureDevice*)[_captureDevices - objectAtIndex:index]; - char tempCaptureDeviceName[1024] = ""; - [[tempCaptureDevice localizedDisplayName] - getCString:tempCaptureDeviceName maxLength:1024 - encoding:NSUTF8StringEncoding]; - if(0 == strcmp(name, tempCaptureDeviceName)) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d Found capture device %s as index %d", - __FUNCTION__, __LINE__, tempCaptureDeviceName, index); - success = YES; - break; - } - - } - - if(NO == success) - { - // camera not found - // nothing has been changed yet, so capture device will stay in it's - // state - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d Capture device %s was not found in list of " - "available devices", __FUNCTION__, __LINE__, - _captureDeviceNameUTF8); - return [NSNumber numberWithInt:0]; - } - - NSError* error; - success = [tempCaptureDevice open:&error]; - if(!success) - { - WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0, - "%s:%d Failed to open capture device: %s", __FUNCTION__, - __LINE__, _captureDeviceNameUTF8); - return [NSNumber numberWithInt:-1]; - } - - if(_captureVideoDeviceInput) - { - [_captureVideoDeviceInput release]; - } - _captureVideoDeviceInput = [[QTCaptureDeviceInput alloc] - initWithDevice:tempCaptureDevice]; - - success = [_captureSession addInput:_captureVideoDeviceInput error:&error]; - if(!success) - { - WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0, - "%s:%d Failed to add input from %s to the capture session", - __FUNCTION__, __LINE__, _captureDeviceNameUTF8); - return [NSNumber numberWithInt:-1]; - } - - WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, - "%s:%d successfully added capture device: %s", __FUNCTION__, - __LINE__, _captureDeviceNameUTF8); - return [NSNumber numberWithInt:0]; -} - - -/// ***** Updates the capture devices size and frequency -/// ***** Sets member variables _frame* and _captureDecompressedVideoOutput -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)setCaptureHeight:(int)height AndWidth:(int)width - AndFrameRate:(int)frameRate{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d height=%d width=%d frameRate=%d", __FUNCTION__, - __LINE__, height, width, frameRate); - if(NO == _OSSupported) - { - return [NSNumber numberWithInt:0]; - } - - _frameWidth = width; - _frameHeight = height; - _frameRate = frameRate; - - [_captureDecompressedVideoOutput - setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)_frameRate]; - NSDictionary* captureDictionary = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithDouble:_frameWidth], (id)kCVPixelBufferWidthKey, - [NSNumber numberWithDouble:_frameHeight], (id)kCVPixelBufferHeightKey, - [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB], - (id)kCVPixelBufferPixelFormatTypeKey, nil]; - [_captureDecompressedVideoOutput performSelectorOnMainThread:@selector(setPixelBufferAttributes:) withObject:captureDictionary waitUntilDone:NO]; -// [_captureDecompressedVideoOutput setPixelBufferAttributes:captureDictionary]; - - - // these methods return type void so there isn't much we can do about - // checking success - return [NSNumber numberWithInt:0]; -} - -/// ***** Starts the QTCaptureSession, assuming correct state. Also ensures that -/// an NSRunLoop is running -/// ***** Without and NSRunLoop to process events, the OS doesn't check for a -/// new frame. -/// ***** Sets member variables _capturing -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)startCapture{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - if(NO == _OSSupported) - { - return [NSNumber numberWithInt:0]; - } - - if(YES == _capturing) - { - return [NSNumber numberWithInt:0]; - } - -// NSLog(@"--------------- before ---------------"); - [[NSRunLoop mainRunLoop] runUntilDate:[NSDate distantFuture]]; -// NSLog(@"--------------- after ---------------"); - - if(NO == _captureInitialized) - { - // this should never be called..... it is initialized on class init - [self initializeVideoCapture]; - } - [_captureSession startRunning]; - - - _capturing = YES; - - return [NSNumber numberWithInt:0]; -} - -/// ***** Stops the QTCaptureSession, assuming correct state -/// ***** Sets member variables _capturing -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)stopCapture{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - if(NO == _OSSupported) - { - return [NSNumber numberWithInt:0]; - } - - if(nil == _captureSession) - { - return [NSNumber numberWithInt:0]; - } - - if(NO == _capturing) - { - return [NSNumber numberWithInt:0]; - } - - if(YES == _capturing) - { - [_captureSession stopRunning]; - } - - _capturing = NO; - return [NSNumber numberWithInt:0]; -} - -// ********** "private" functions below here ********** -#pragma mark **** "private" methods - -/// ***** Class member variables are initialized here -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)initializeVariables{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - if(NO == _OSSupported) - { - return [NSNumber numberWithInt:0]; - } - - _pool = [[NSAutoreleasePool alloc]init]; - - memset(_captureDeviceNameUTF8, 0, 1024); - _counter = 0; - _framesDelivered = 0; - _framesRendered = 0; - _captureDeviceCount = 0; - _capturing = NO; - _captureInitialized = NO; - _frameRate = DEFAULT_FRAME_RATE; - _frameWidth = DEFAULT_FRAME_WIDTH; - _frameHeight = DEFAULT_FRAME_HEIGHT; - _captureDeviceName = [[NSString alloc] initWithFormat:@""]; - _rLock = [[VideoCaptureRecursiveLock alloc] init]; - _captureSession = [[QTCaptureSession alloc] init]; - _captureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] - init]; - [_captureDecompressedVideoOutput setDelegate:self]; - - [self getCaptureDevices]; - [self initializeVideoCapture]; - - return [NSNumber numberWithInt:0]; - -} - -// Checks to see if the QTCaptureSession framework is available in the OS -// If it is not, isOSSupprted = NO. -// Throughout the rest of the class isOSSupprted is checked and functions -// are/aren't called depending -// The user can use weak linking to the QTKit framework and run on older -// versions of the OS. I.E. Backwards compaitibility -// Returns nothing. Sets member variable -- (void)checkOSSupported{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - Class osSupportedTest = NSClassFromString(@"QTCaptureSession"); - _OSSupported = NO; - if(nil == osSupportedTest) - { - } - _OSSupported = YES; -} - -/// ***** Retrieves the number of capture devices currently available -/// ***** Stores them in an NSArray instance -/// ***** Returns 0 on success, -1 otherwise. -- (NSNumber*)getCaptureDevices{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - if(NO == _OSSupported) - { - return [NSNumber numberWithInt:0]; - } - - if(_captureDevices) - { - [_captureDevices release]; - } - _captureDevices = [[NSArray alloc] initWithArray: - [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]]; - - _captureDeviceCount = _captureDevices.count; - if(_captureDeviceCount < 1) - { - return [NSNumber numberWithInt:0]; - } - return [NSNumber numberWithInt:0]; -} - -// Initializes a QTCaptureSession (member variable) to deliver frames via -// callback -// QTCapture* member variables affected -// The image format and frequency are setup here -// Returns 0 on success, -1 otherwise. -- (NSNumber*)initializeVideoCapture{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - - if(YES == _captureInitialized) - { - return [NSNumber numberWithInt:-1]; - } - - QTCaptureDevice* videoDevice = - (QTCaptureDevice*)[_captureDevices objectAtIndex:0]; - - bool success = NO; - NSError* error; - - success = [videoDevice open:&error]; - if(!success) - { - return [NSNumber numberWithInt:-1]; - } - - [_captureDecompressedVideoOutput setPixelBufferAttributes: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithDouble:_frameWidth], (id)kCVPixelBufferWidthKey, - [NSNumber numberWithDouble:_frameHeight], (id)kCVPixelBufferHeightKey, - [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB], - (id)kCVPixelBufferPixelFormatTypeKey, nil]]; - - [_captureDecompressedVideoOutput setMinimumVideoFrameInterval: - (NSTimeInterval)1/(float)_frameRate]; - [_captureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES]; - - success = [_captureSession addOutput:_captureDecompressedVideoOutput - error:&error]; - - if(!success) - { - return [NSNumber numberWithInt:-1]; - } - - _captureInitialized = YES; - - return [NSNumber numberWithInt:0]; -} - -// This is the callback that is called when the OS has a frame to deliver to us. -// Starts being called when [_captureSession startRunning] is called. Stopped -// similarly. -// Parameter videoFrame contains the image. The format, size, and frequency -// were setup earlier. -// Returns 0 on success, -1 otherwise. -- (void)captureOutput:(QTCaptureOutput *)captureOutput - didOutputVideoFrame:(CVImageBufferRef)videoFrame - withSampleBuffer:(QTSampleBuffer *)sampleBuffer - fromConnection:(QTCaptureConnection *)connection{ - - if(YES == [_rLock tryLock]) - { - [_rLock lock]; - } - else - { - return; - } - - if(NO == _OSSupported) - { - return; - } - - const int LOCK_FLAGS = 0; // documentation says to pass 0 - - // get size of the frame - CVPixelBufferLockBaseAddress(videoFrame, LOCK_FLAGS); - void* baseAddress = CVPixelBufferGetBaseAddress(videoFrame); - size_t bytesPerRow = CVPixelBufferGetBytesPerRow(videoFrame); - int frameWidth = CVPixelBufferGetWidth(videoFrame); - int frameHeight = CVPixelBufferGetHeight(videoFrame); - CVPixelBufferUnlockBaseAddress(videoFrame, LOCK_FLAGS); - - if(_owner) - { - - int frameSize = bytesPerRow * frameHeight; // 32 bit ARGB format - CVBufferRetain(videoFrame); - VideoCaptureCapability tempCaptureCapability; - tempCaptureCapability.width = _frameWidth; - tempCaptureCapability.height = _frameHeight; - tempCaptureCapability.maxFPS = _frameRate; - tempCaptureCapability.rawType = kVideoARGB; - - _owner->IncomingFrame((unsigned char*)baseAddress, - frameSize, - tempCaptureCapability, - 0); - - CVBufferRelease(videoFrame); - } - - _framesDelivered++; - _framesRendered++; - - captureOutput = captureOutput; - sampleBuffer = sampleBuffer; - connection = connection; - - if(YES == [_rLock locked]) - { - [_rLock unlock]; - } -} - -@end diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_utility.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_utility.h deleted file mode 100644 index 5ef0b96b6..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_qtkit_utility.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_qtkit_utility.h - * - */ - - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_UTILITY_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_UTILITY_H_ - -#define MAX_NAME_LENGTH 1024 - -#define QTKIT_MIN_WIDTH 0 -#define QTKIT_MAX_WIDTH 2560 -#define QTKIT_DEFAULT_WIDTH 352 - -#define QTKIT_MIN_HEIGHT 0 -#define QTKIT_MAX_HEIGHT 1440 -#define QTKIT_DEFAULT_HEIGHT 288 - -#define QTKIT_MIN_FRAME_RATE 1 -#define QTKIT_MAX_FRAME_RATE 60 -#define QTKIT_DEFAULT_FRAME_RATE 30 - -#define RELEASE_AND_CLEAR(p) if (p) { (p) -> Release () ; (p) = NULL ; } - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_QTKIT_UTILITY_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.h b/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.h deleted file mode 100644 index f4008a4d8..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// -// video_capture_recursive_lock.h -// -// - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_RECURSIVE_LOCK_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_RECURSIVE_LOCK_H_ - -#import - -@interface VideoCaptureRecursiveLock : NSRecursiveLock { - BOOL _locked; -} - -@property BOOL locked; - -- (void)lock; -- (void)unlock; - -@end - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QTKIT_VIDEO_CAPTURE_RECURSIVE_LOCK_H_ diff --git a/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.mm b/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.mm deleted file mode 100644 index d9df5cbc4..000000000 --- a/modules/video_capture/main/source/Mac/QTKit/video_capture_recursive_lock.mm +++ /dev/null @@ -1,33 +0,0 @@ -// -// video_capture_recursive_lock.mm -// -// - -#import "video_capture_recursive_lock.h" - -@implementation VideoCaptureRecursiveLock - -@synthesize locked = _locked; - -- (id)init{ - self = [super init]; - if(nil == self){ - return nil; - } - - [self setLocked:NO]; - return self; -} - -- (void)lock{ - [self setLocked:YES]; - [super lock]; -} - -- (void)unlock{ - [self setLocked:NO]; - [super unlock]; -} - - -@end diff --git a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.cc b/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.cc deleted file mode 100644 index 6889a8ca7..000000000 --- a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.cc +++ /dev/null @@ -1,1410 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_quick_time.cc - * - */ - - -#include "video_capture_quick_time.h" - -#include "CriticalSectionWrapper.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "tick_util.h" -#include "trace.h" -#include "vplib.h" -#include - -namespace webrtc -{ - -VideoCaptureMacQuickTime::VideoCaptureMacQuickTime(WebRtc_Word32 iID) : - VideoCaptureImpl(iID), // super class constructor - _id(iID), - _isCapturing(false), - _captureCapability(), - _grabberCritsect(CriticalSectionWrapper::CreateCriticalSection()), - _videoMacCritsect(CriticalSectionWrapper::CreateCriticalSection()), - _terminated(true), _grabberUpdateThread(NULL), - _grabberUpdateEvent(NULL), _captureGrabber(NULL), _captureDevice(NULL), - _captureVideoType(kVideoUnknown), _captureIsInitialized(false), - _gWorld(NULL), _captureChannel(0), _captureSequence(NULL), - _sgPrepared(false), _sgStarted(false), _trueCaptureWidth(0), - _trueCaptureHeight(0), _captureDeviceList(), - _captureDeviceListTime(0), _captureCapabilityList() - -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - _captureCapability.width = START_CODEC_WIDTH; - _captureCapability.height = START_CODEC_HEIGHT; - memset(_captureDeviceDisplayName, 0, sizeof(_captureDeviceDisplayName)); -} - -VideoCaptureMacQuickTime::~VideoCaptureMacQuickTime() -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - - VideoCaptureTerminate(); - - if (_videoMacCritsect) - { - delete _videoMacCritsect; - } - if (_grabberCritsect) - { - delete _grabberCritsect; - } - -} - -WebRtc_Word32 VideoCaptureMacQuickTime::Init( - const WebRtc_Word32 id, const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - - const WebRtc_Word32 nameLength = - (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); - if (nameLength > kVideoCaptureUniqueNameLength) - return -1; - - // Store the device name - _deviceUniqueId = new WebRtc_UWord8[nameLength + 1]; - memset(_deviceUniqueId, 0, nameLength + 1); - memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); - - // Check OSX version - OSErr err = noErr; - long version; - - _videoMacCritsect->Enter(); - if (!_terminated) - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Already Initialized", __FUNCTION__, __LINE__); - return -1; - } - - err = Gestalt(gestaltSystemVersion, &version); - if (err != noErr) - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not retrieve OS version", __FUNCTION__, - __LINE__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d OS X version: %x,", __FUNCTION__, __LINE__, version); - if (version < 0x00001040) // Older version than Mac OSX 10.4 - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d OS version not supported", __FUNCTION__, __LINE__); - return -1; - } - - err = Gestalt(gestaltQuickTime, &version); - if (err != noErr) - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not retrieve QuickTime version", - __FUNCTION__, __LINE__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d QuickTime version: %x", __FUNCTION__, __LINE__, - version); - if (version < 0x07000000) // QT v. 7.x or newer (QT 5.0.2 0x05020000) - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d QuickTime version too old. Need 7 or newer", - __FUNCTION__, __LINE__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d EnterMovies()", __FUNCTION__, __LINE__); - EnterMovies(); - - if (VideoCaptureSetCaptureDevice((char*) deviceUniqueIdUTF8, - kVideoCaptureProductIdLength) == -1) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d failed to set capture device: %s", __FUNCTION__, - __LINE__, deviceUniqueIdUTF8); - _videoMacCritsect->Leave(); - return -1; - } - - _terminated = false; - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d successful initialization", __FUNCTION__, __LINE__); - _videoMacCritsect->Leave(); - - return 0; -} - -WebRtc_Word32 VideoCaptureMacQuickTime::StartCapture( - const VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "%s:%d " - "capability.width=%d, capability.height=%d ,capability.maxFPS=%d " - "capability.expectedCaptureDelay=%d, capability.interlaced=%d", - __FUNCTION__, __LINE__, capability.width, capability.height, - capability.maxFPS, capability.expectedCaptureDelay, - capability.interlaced); - - _captureCapability.width = capability.width; - _captureCapability.height = capability.height; - - if (VideoCaptureRun() == -1) - { - return -1; - } - - return 0; -} - -WebRtc_Word32 VideoCaptureMacQuickTime::StopCapture() -{ - - if (VideoCaptureStop() == -1) - { - return -1; - } - - return 0; -} - -bool VideoCaptureMacQuickTime::CaptureStarted() -{ - return _isCapturing; -} - -WebRtc_Word32 VideoCaptureMacQuickTime::CaptureSettings( - VideoCaptureCapability& settings) -{ - settings.width = _captureCapability.width; - settings.height = _captureCapability.height; - settings.maxFPS = 0; - return 0; -} - -int VideoCaptureMacQuickTime::VideoCaptureTerminate() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - VideoCaptureStop(); - - _videoMacCritsect->Enter(); - if (_terminated) - { - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Already terminated", __FUNCTION__, __LINE__); - return -1; - } - - _grabberCritsect->Enter(); - - // Stop the camera/sequence grabber - // Resets: _captureSequence, _sgStarted - StopQuickTimeCapture(); - - // Remove local video settings - // Resets: _gWorld, _captureCapability.width, _captureCapability.height - RemoveLocalGWorld(); - DisconnectCaptureDevice(); - - if (_grabberUpdateThread) - _grabberUpdateThread->SetNotAlive(); - - _grabberCritsect->Leave(); - - if (_grabberUpdateEvent) - _grabberUpdateEvent->Set(); - - SLEEP(1); - _grabberCritsect->Enter(); - - if (_grabberUpdateThread) - { - _grabberUpdateThread->Stop(); - delete _grabberUpdateThread; - _grabberUpdateThread = NULL; - } - if (_grabberUpdateEvent) - { - delete _grabberUpdateEvent; - _grabberUpdateEvent = NULL; - } - - // Close the sequence grabber - if (_captureGrabber) - { - SGRelease(_captureGrabber); - _captureGrabber = NULL; - CloseComponent(_captureGrabber); - _captureDevice = NULL; - } - _captureVideoType = kVideoUnknown; - - // Delete capture device list - ListItem* item = _captureDeviceList.First(); - while (item) - { - delete static_cast (item->GetItem()); - _captureDeviceList.Erase(item); - item = _captureDeviceList.First(); - } - _captureDeviceListTime = 0; - - _terminated = true; - - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - - return 0; -} - -int VideoCaptureMacQuickTime::UpdateCaptureSettings(int channel, - webrtc::VideoCodec& inst, - bool def) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d channel: %d", __FUNCTION__, __LINE__, channel); - - if (channel < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Invalid channel number: %d", __FUNCTION__, - __LINE__, channel); - return -1; - } - - // the size has changed, we need to change our setup - _videoMacCritsect->Enter(); - - // Stop capturing, if we are... - _grabberCritsect->Enter(); - - bool wasCapturing = false; - StopQuickTimeCapture(&wasCapturing); - - // Create a new offline GWorld to receive captured frames - RemoveLocalGWorld(); - - if (CreateLocalGWorld(inst.width, inst.height) == -1) - { - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - // Error already logged - return -1; - } - _captureCapability.width = inst.width; - _captureCapability.height = inst.height; - - // Connect the capture device to our offline GWorld - // if we already have a capture device selected. - if (_captureDevice) - { - DisconnectCaptureDevice(); - if (ConnectCaptureDevice() == -1) - { - // Error already logged - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return -1; - } - } - - // Start capture if we did before - if (wasCapturing) - { - if (StartQuickTimeCapture() == -1) - { - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Failed to start capturing", __FUNCTION__, - __LINE__); - return -1; - } - } - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - - return 0; -} - -// Creates an off screen graphics world used for converting -// captured video frames if we can't get a format we want. -// Assumed protected by critsects -int VideoCaptureMacQuickTime::CreateLocalGWorld(int width, int height) -{ - if (_gWorld) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d GWorld already created", __FUNCTION__, __LINE__); - return -1; - } - if (width == 0 || height == 0) - { - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d Invalid dimensions width:%d height:%d", - __FUNCTION__, __LINE__, width, height); - return -1; - } - - Rect captureRect; - captureRect.left = 0; - captureRect.top = 0; - captureRect.right = width; - captureRect.bottom = height; - - // Create a GWorld in same size as we want to send to the codec - if (QTNewGWorld(&(_gWorld), k2vuyPixelFormat, &captureRect, 0, NULL, 0) - != noErr) - { - return -1; - } - _captureCapability.width = width; - _captureCapability.height = height; - - if (!LockPixels(GetGWorldPixMap(_gWorld))) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not lock pixmap. Continuing anyhow", - __FUNCTION__, __LINE__); - } - - CGrafPtr theOldPort; - GDHandle theOldDevice; - GetGWorld(&theOldPort, &theOldDevice); // Gets the result from QTGetNewGWorld - SetGWorld(_gWorld, NULL); // Sets the new GWorld - BackColor( blackColor); // Changes the color on the graphic port - ForeColor( whiteColor); - EraseRect(&captureRect); - SetGWorld(theOldPort, theOldDevice); - - return 0; -} - -// Assumed critsect protected -int VideoCaptureMacQuickTime::RemoveLocalGWorld() -{ - if (!_gWorld) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d !gWorld", __FUNCTION__, __LINE__); - return -1; - } - - DisposeGWorld(_gWorld); - _gWorld = NULL; - _captureCapability.width = START_CODEC_WIDTH; - _captureCapability.height = START_CODEC_HEIGHT; - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d GWorld has been removed", __FUNCTION__, __LINE__); - return 0; -} - -// ConnectCaptureDevice -// This function prepares the capture device -// with the wanted settings, but the capture -// device isn't started. -// -// Assumed critsect protected -int VideoCaptureMacQuickTime::ConnectCaptureDevice() -{ - // Prepare the capture grabber if a capture device is already set - if (!_captureGrabber) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d No capture device is selected", __FUNCTION__, - __LINE__); - return -1; - } - if (_captureIsInitialized) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d Capture device is already initialized", - __FUNCTION__, __LINE__); - return -1; - } - if (!_gWorld) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d No GWorld is created", __FUNCTION__, __LINE__); - return -1; - } - - OSErr err = noErr; - long flags = 0; - - // Connect the camera to our offline GWorld - // We won't use the GWorld if we get the format we want - // from the camera. - if (SGSetGWorld(_captureGrabber, _gWorld, NULL ) != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not connect capture device", __FUNCTION__, - __LINE__); - return -1; - } - if (SGSetDataRef(_captureGrabber, 0, 0, seqGrabDontMakeMovie) != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not configure capture device", __FUNCTION__, - __LINE__); - return -1; - } - - // Set our capture callback - if (SGSetDataProc(_captureGrabber, NewSGDataUPP(SendProcess), (long) this) - != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not set capture callback. Unable to receive " - "frames", __FUNCTION__, __LINE__); - return -1; - } - - // Create a video channel to the sequence grabber - if (SGNewChannel(_captureGrabber, VideoMediaType, &_captureChannel) - != noErr) // Takes time!!! - { - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not create sequence grabber channel", - __FUNCTION__, __LINE__); - return -1; - } - - // Get a list with all capture devices to choose the one we want. - SGDeviceList deviceList = NULL; - if (SGGetChannelDeviceList(_captureChannel, sgDeviceListIncludeInputs, - &deviceList) != noErr) - { - - } - - int numDevicesTypes = (*deviceList)->count; - bool captureDeviceFound = false; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Found %d channel devices", __FUNCTION__, __LINE__, - numDevicesTypes); - - // Loop through all devices to get the one we want. - for (int i = 0; i < numDevicesTypes; i++) - { - SGDeviceName deviceTypeName = (*deviceList)->entry[i]; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Inspecting device number: %d", __FUNCTION__, - __LINE__, i); - // Get the list with input devices - if (deviceTypeName.inputs) - { - SGDeviceInputList inputList = deviceTypeName.inputs; - int numInputDev = (*inputList)->count; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Device has %d inputs", __FUNCTION__, __LINE__, - numInputDev); - for (int inputDevIndex = 0; - inputDevIndex < numInputDev; - inputDevIndex++) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, - _id, "%s:%d Inspecting input number: %d", - __FUNCTION__, __LINE__, inputDevIndex); - SGDeviceInputName deviceInputName = - (*inputList)->entry[inputDevIndex]; - char devInName[64]; - memset(devInName, 0, 64); - - // SGDeviceInputName::name is a Str63, defined as a Pascal string. - // (Refer to MacTypes.h) - CFIndex devInNameLength = - PascalStringToCString(deviceInputName.name, devInName, - sizeof(devInName)); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, - _id, - "%s:%d Converted pascal string with length:%d " - "to: %s", __FUNCTION__, __LINE__, - sizeof(devInName), devInName); - if (devInNameLength < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, - "%s:%d Failed to convert device name from " - "pascal string to c string", __FUNCTION__, - __LINE__); - return -1; - } - - if (!strcmp(devInName, _captureDeviceDisplayName)) - { - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCapture, _id, - "%s:%d We have found our device: %s", - __FUNCTION__, __LINE__, - _captureDeviceDisplayName); - - if (SGSetChannelDevice(_captureChannel, deviceTypeName.name) - != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "%s:%d Could not set capture device type: " - "%s",__FUNCTION__, __LINE__, - deviceTypeName.name); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, - webrtc::kTraceVideoCapture, _id, - "%s:%d Capture device type is: %s", - __FUNCTION__, __LINE__, deviceTypeName.name); - if (SGSetChannelDeviceInput(_captureChannel, inputDevIndex) - != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "%s:%d Could not set SG device", - __FUNCTION__, __LINE__); - return -1; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, - _id, - "%s:%d Capture device: %s has successfully " - "been set", __FUNCTION__, __LINE__, - _captureDeviceDisplayName); - captureDeviceFound = true; - break; - } - } - if (captureDeviceFound) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, - _id, - "%s:%d Capture device found, breaking from loops", - __FUNCTION__, __LINE__); - break; - } - } - } - err = SGDisposeDeviceList(_captureGrabber, deviceList); - - if (!captureDeviceFound) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Failed to find capture device: %s. Returning -1", - __FUNCTION__, __LINE__, _captureDeviceDisplayName); - return -1; - } - - // Set the size we want from the capture device - Rect captureSize; - captureSize.left = 0; - captureSize.top = 0; - captureSize.right = _captureCapability.width; - captureSize.bottom = _captureCapability.height; - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Using capture rect: l:%d t:%d r:%d b:%d", __FUNCTION__, - __LINE__, captureSize.left, captureSize.top, - captureSize.right, captureSize.bottom); - - err = SGSetChannelBounds(_captureChannel, &captureSize); - if (err == noErr) - { - err = SGSetChannelUsage(_captureChannel, flags | seqGrabRecord); - } - if (err != noErr) - { - SGDisposeChannel(_captureGrabber, _captureChannel); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Error setting SG channel to device", __FUNCTION__, - __LINE__); - return -1; - } - - // Find out what video format we'll get from the capture device. - OSType compType; - err = SGGetVideoCompressorType(_captureChannel, &compType); - - // Convert the Apple video format name to a VideoCapture name. - if (compType == k2vuyPixelFormat) - { - _captureVideoType = kVideoUYVY; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Device delivers UYUV formatted frames", - __FUNCTION__, __LINE__); - } - else if (compType == kYUVSPixelFormat) - { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Device delivers YUY2 formatted frames", - __FUNCTION__, __LINE__); - _captureVideoType = kVideoYUY2; - } - else - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Device delivers frames in an unknown format: 0x%x. " - "Consult QuickdrawTypes.h", - __FUNCTION__, __LINE__, compType); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Device delivers frames in an unknown format.", - __FUNCTION__, __LINE__); - _captureVideoType = kVideoUnknown; - } - - if (SGPrepare(_captureGrabber, false, true) != noErr) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Error starting sequence grabber", __FUNCTION__, - __LINE__); - return -1; - } - - // Try to set the codec size as capture size. - err = SGSetChannelBounds(_captureChannel, &captureSize); - - // Check if we really will get the size we asked for. - ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0); - err = SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc); - - _trueCaptureWidth = (**imageDesc).width; - _trueCaptureHeight = (**imageDesc).height; - - DisposeHandle((Handle) imageDesc); - - _captureIsInitialized = true; - _sgPrepared = true; - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Success starting sequence grabber", __FUNCTION__, - __LINE__); - - return 0; -} - -// Assumed critsect protected -int VideoCaptureMacQuickTime::DisconnectCaptureDevice() -{ - if (_sgStarted) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Capture device is still running. Returning -1", - __FUNCTION__, __LINE__); - return -1; - } - if (!_sgPrepared) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d No capture device connected", __FUNCTION__, - __LINE__); - return -1; - } - - // Close the capture channel - SGStop(_captureGrabber); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d !!!! releasing sg stuff", __FUNCTION__, __LINE__); - SGDisposeChannel(_captureGrabber, _captureChannel); - SGRelease(_captureGrabber); - CloseComponent(_captureGrabber); - - // Reset all values - _captureChannel = NULL; - _captureVideoType = kVideoUnknown; - _trueCaptureWidth = 0; - _trueCaptureHeight = 0; - _captureIsInitialized = false; - _sgPrepared = false; - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Sequence grabber removed", __FUNCTION__, __LINE__); - - return 0; -} - -// StartQuickTimeCapture -// -// Actually starts the camera -// -int VideoCaptureMacQuickTime::StartQuickTimeCapture() -{ - _grabberCritsect->Enter(); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Attempting to start sequence grabber", __FUNCTION__, - __LINE__); - - if (_sgStarted) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Sequence grabber already started", __FUNCTION__, - __LINE__); - return 0; - } - if (!_sgPrepared) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Sequence grabber not prepared properly", - __FUNCTION__, __LINE__); - return 0; - } - - if (SGStartRecord(_captureGrabber) != noErr) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Error starting sequence grabber", __FUNCTION__, - __LINE__); - return -1; - } - - Rect captureRect = { 0, 0, 0, 0 }; - MatrixRecord scaleMatrix; - ImageDescriptionHandle imageDesc = (ImageDescriptionHandle) NewHandle(0); - - // Get the sample description for the channel, which is the same as for the - // capture device - if (SGGetChannelSampleDescription(_captureChannel, (Handle) imageDesc) - != noErr) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Error accessing device properties", __FUNCTION__, - __LINE__); - return -1; - } - - // Create a scale matrix to scale the captured image - // Needed if we don't get the size wanted from the camera - captureRect.right = (**imageDesc).width; - captureRect.bottom = (**imageDesc).height; - - Rect codecRect; - codecRect.left = 0; - codecRect.top = 0; - codecRect.right = _captureCapability.width; - codecRect.bottom = _captureCapability.height; - RectMatrix(&scaleMatrix, &captureRect, &codecRect); - - // Start grabbing images from the capture device to _gWorld - if (DecompressSequenceBegin(&_captureSequence, imageDesc, _gWorld, NULL, - NULL, &scaleMatrix, srcCopy, (RgnHandle) NULL, - NULL, codecNormalQuality, bestSpeedCodec) - != noErr) - { - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d Error starting decompress sequence", __FUNCTION__, - __LINE__); - return -1; - } - DisposeHandle((Handle) imageDesc); - _sgStarted = true; - _grabberCritsect->Leave(); - return 0; -} - -int VideoCaptureMacQuickTime::StopQuickTimeCapture(bool* wasCapturing) -{ - _grabberCritsect->Enter(); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "%s:%d wasCapturing=%d", __FUNCTION__, __LINE__, wasCapturing); - - if (!_sgStarted) - { - if (wasCapturing) - *wasCapturing = false; - - _grabberCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d Sequence grabber was never started", __FUNCTION__, - __LINE__); - return 0; - } - - if (wasCapturing) - *wasCapturing = true; - - OSErr error = noErr; - error = SGStop(_captureGrabber); - CDSequenceEnd(_captureSequence); - _captureSequence = NULL; - _sgStarted = false; - - _grabberCritsect->Leave(); - if (error != noErr) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not stop sequence grabber", __FUNCTION__, - __LINE__); - return -1; - } - - return 0; -} - -//------------------------------------------------- -// -// Thread/function to keep capture device working -// -//------------------------------------------------- - - -// -// GrabberUpdateThread / GrabberUpdateProcess -// -// Called at a certain time interval to tell -// the capture device / SequenceGrabber to -// actually work. -bool VideoCaptureMacQuickTime::GrabberUpdateThread(void* obj) -{ - return static_cast (obj)->GrabberUpdateProcess(); -} - -bool VideoCaptureMacQuickTime::GrabberUpdateProcess() -{ - _grabberUpdateEvent->Wait(30); - - if (_isCapturing == false) - return false; - - _grabberCritsect->Enter(); - if (_captureGrabber) - { - if (SGIdle(_captureGrabber) != noErr) - { - } - } - _grabberCritsect->Leave(); - return true; -} - -// -// VideoCaptureStop -// -// Stops the capture device -// -int VideoCaptureMacQuickTime::VideoCaptureStop() -{ - if (_grabberUpdateThread) - { - _grabberUpdateThread->Stop(); - } - - _videoMacCritsect->Enter(); - _grabberCritsect->Enter(); - int retVal = StopQuickTimeCapture(); - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - if (retVal == -1) - { - return -1; - } - - _isCapturing = false; - return 0; -} - -// -// VideoCaptureRun -// -// Starts the capture device and creates -// the update thread. -// -int VideoCaptureMacQuickTime::VideoCaptureRun() -{ - _videoMacCritsect->Enter(); - _grabberCritsect->Enter(); - - int res = StartQuickTimeCapture(); - - // Create the thread for updating sequence grabber if not created earlier - if (!_grabberUpdateThread) - { - _grabberUpdateEvent = EventWrapper::Create(); - _grabberUpdateThread = ThreadWrapper::CreateThread( - VideoCaptureMacQuickTime::GrabberUpdateThread, this, kHighPriority); - unsigned int id; - _grabberUpdateThread->Start(id); - } - else - { - unsigned int id; - _grabberUpdateThread->Start(id); - } - - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - - _isCapturing = true; - return res; -} - -// ---------------------------------------------------------------------- -// -// SendProcess -// sequence grabber data procedure -// -// This function is called by the capture device as soon as a new -// frame is available. -// -// -// SendFrame -// -// The non-static function used by the capture device callback -// -// Input: -// sgChannel: the capture device channel generating the callback -// data: the video frame -// length: the data length in bytes -// grabTime: time stamp generated by the capture device / sequece grabber -// -// ---------------------------------------------------------------------- - -OSErr VideoCaptureMacQuickTime::SendProcess(SGChannel sgChannel, Ptr p, - long len, long* /*offset*/, - long /*chRefCon*/, TimeValue time, - short /*writeType*/, long refCon) -{ - VideoCaptureMacQuickTime* videoEngine = - reinterpret_cast (refCon); - return videoEngine->SendFrame(sgChannel, (char*) p, len, time); -} - -int VideoCaptureMacQuickTime::SendFrame(SGChannel /*sgChannel*/, char* data, - long length, TimeValue /*grabTime*/) -{ - if (!_sgPrepared) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d Sequence Grabber is not initialized", __FUNCTION__, - __LINE__); - return 0; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Frame has been delivered\n", __FUNCTION__, __LINE__); - - CodecFlags ignore; - _grabberCritsect->Enter(); - if (_gWorld) - { - // Will be set to true if we don't recognize the size and/or video - // format. - bool convertFrame = false; - WebRtc_Word32 width = 352; - WebRtc_Word32 height = 288; - WebRtc_Word32 frameSize = 0; - - VideoCaptureCapability captureCapability; - captureCapability.width = width; - captureCapability.height = height; - captureCapability.maxFPS = 30; - - switch (_captureVideoType) - { - case kVideoUYVY: - captureCapability.rawType = kVideoUYVY; - break; - case kVideoYUY2: - captureCapability.rawType = kVideoYUY2; - break; - case kVideoI420: - captureCapability.rawType = kVideoI420; - break; - default: - captureCapability.rawType = kVideoI420; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, - _id, "%s:%d raw = I420 by default\n", - __FUNCTION__, __LINE__); - break; - } - - // Convert the camera video type to something VideoEngine can work with - // Check if we need to downsample the incomming frame. - switch (_captureVideoType) - { - case kVideoUYVY: - case kVideoYUY2: - frameSize = (width * height * 16) >> 3; // 16 is for YUY2 format - if (width == _captureCapability.width || height - == _captureCapability.height) - { - // Ok format and size, send the frame to super class - IncomingFrame((WebRtc_UWord8*) data, - (WebRtc_Word32) frameSize, captureCapability, - TickTime::MillisecondTimestamp()); - - } - else if (width == _trueCaptureWidth && height - == _trueCaptureHeight) - { - // We need to scale the picture to correct size... - // This happens for cameras not supporting all sizes. - // E.g. older built-in iSight doesn't support QCIF. - - // Convert the incoming frame into our GWorld. - int res = - DecompressSequenceFrameS(_captureSequence, data, - length, 0, &ignore, NULL); - if (res != noErr && res != -8976) // 8796 == black frame - { - WEBRTC_TRACE(webrtc::kTraceWarning, - webrtc::kTraceVideoCapture, _id, - "%s:%d Captured black frame. Not " - "processing it", __FUNCTION__, __LINE__); - _grabberCritsect->Leave(); - return 0; - } - - // Copy the frame from the PixMap to our video buffer - PixMapHandle pixMap = GetGWorldPixMap(_gWorld); - - // Lock the image data in the GWorld. - LockPixels(pixMap); - - // Get a pointer to the pixel data. - Ptr capturedFrame = GetPixBaseAddr(pixMap); - - // Send the converted frame out to super class - IncomingFrame((WebRtc_UWord8*) data, - (WebRtc_Word32) frameSize, captureCapability, - TickTime::MillisecondTimestamp()); - - // Unlock the image data to get ready for the next frame. - UnlockPixels(pixMap); - } - else - { - // Not a size we recognize, use the Mac internal scaling... - convertFrame = true; - WEBRTC_TRACE(webrtc::kTraceDebug, - webrtc::kTraceVideoCapture, _id, - "%s:%d Not correct incoming stream size for " - "the format and configured size", - __FUNCTION__, __LINE__); - } - break; - default: - - // Not a video format we recognize, use the Mac internal scaling - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, - _id, "%s:%d Unknown video frame format (default)", - __FUNCTION__, __LINE__); - convertFrame = true; - break; - } - - if (convertFrame) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Unrecognized frame format. Converting frame", - __FUNCTION__, __LINE__); - - // We don't recognise the input format. Convert to UYVY, I420 is not - // supported on osx. Decompress the grabbed frame into the GWorld, - // i.e from webcam format to ARGB (RGB24), and extract the frame. - int res = DecompressSequenceFrameS(_captureSequence, data, length, - 0, &ignore, NULL); - if (res != noErr && res != -8976) // 8796 means a black frame - { - _grabberCritsect->Leave(); - return 0; - } - - // Copy the frame from the PixMap to our video buffer - PixMapHandle rgbPixMap = GetGWorldPixMap(_gWorld); - LockPixels(rgbPixMap); - Ptr capturedFrame = GetPixBaseAddr(rgbPixMap); - - // Get the picture size - int width = (*rgbPixMap)->bounds.right; - int height = (*rgbPixMap)->bounds.bottom; - - // 16 is for YUY2 format. - WebRtc_Word32 frameSize = (width * height * 16) >> 3; - - // Ok format and size, send the frame to super class - IncomingFrame((WebRtc_UWord8*) data, (WebRtc_Word32) frameSize, - captureCapability, TickTime::MillisecondTimestamp()); - - UnlockPixels(rgbPixMap); - } - - // Tell the capture device it's ok to update. - SGUpdate(_captureGrabber, NULL); - } - else - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "%s:%d No GWorld created, but frames are being delivered", - __FUNCTION__, __LINE__); - } - - _grabberCritsect->Leave(); - return 0; -} - -int VideoCaptureMacQuickTime::VideoCaptureInitThreadContext() -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d ", __FUNCTION__, __LINE__); - _videoMacCritsect->Enter(); - EnterMoviesOnThread( kQTEnterMoviesFlagDontSetComponentsThreadMode); - _videoMacCritsect->Leave(); - return 0; -} - -// -// -// Functions for handling capture devices -// -// - -VideoCaptureMacQuickTime::VideoCaptureMacName::VideoCaptureMacName() : - _size(0) -{ - memset(_name, 0, kVideoCaptureMacNameMaxSize); -} - -int VideoCaptureMacQuickTime::VideoCaptureSetCaptureDevice( - const char* deviceName, int size) -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d deviceName=%s, size=%d", __FUNCTION__, __LINE__, - deviceName, size); - - _videoMacCritsect->Enter(); - bool wasCapturing = false; - - _grabberCritsect->Enter(); - if (_captureGrabber) - { - // Stop grabbing, disconnect and close the old capture device - StopQuickTimeCapture(&wasCapturing); - DisconnectCaptureDevice(); - CloseComponent(_captureGrabber); - _captureDevice = NULL; - _captureGrabber = NULL; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Old capture device removed", __FUNCTION__, - __LINE__); - } - - if (deviceName == NULL || size == 0) - { - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return 0; - } - - if (size < 0) - { - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d 'size' is not valid", __FUNCTION__, __LINE__); - return 0; - } - - ComponentDescription compCaptureType; - - // Define the component we want to open - compCaptureType.componentType = SeqGrabComponentType; - compCaptureType.componentSubType = 0; - compCaptureType.componentManufacturer = 0; - compCaptureType.componentFlags = 0; - compCaptureType.componentFlagsMask = 0; - - long numSequenceGrabbers = CountComponents(&compCaptureType); - - // loop through the available grabbers and open the first possible - for (int i = 0; i < numSequenceGrabbers; i++) - { - _captureDevice = FindNextComponent(0, &compCaptureType); - _captureGrabber = OpenComponent(_captureDevice); - if (_captureGrabber != NULL) - { - // We've found a sequencegrabber that we could open - if (SGInitialize(_captureGrabber) != noErr) - { - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, - "%s:%d Could not initialize sequence grabber", - __FUNCTION__, __LINE__); - return -1; - } - break; - } - if (i == numSequenceGrabbers - 1) - { - // Couldn't open a sequence grabber - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not open a sequence grabber", - __FUNCTION__, __LINE__); - return -1; - } - } - - if (!_gWorld) - { - // We don't have a GWorld. Create one to enable early preview - // without calling SetSendCodec - if (CreateLocalGWorld(_captureCapability.width, - _captureCapability.height) == -1) - { - // Error already logged - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return -1; - } - } - // Connect the camera with our GWorld - int cpySize = size; - if ((unsigned int) size > sizeof(_captureDeviceDisplayName)) - { - cpySize = sizeof(_captureDeviceDisplayName); - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Copying %d chars from deviceName to " - "_captureDeviceDisplayName (size=%d)\n", - __FUNCTION__, __LINE__, cpySize, size); - memcpy(_captureDeviceDisplayName, deviceName, cpySize); - if (ConnectCaptureDevice() == -1) - { - // Error already logged - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return -1; - } - - if (StartQuickTimeCapture() == -1) - { - // Error already logged - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return -1; - } - _grabberCritsect->Leave(); - _videoMacCritsect->Leave(); - return 0; -} - -bool VideoCaptureMacQuickTime::IsCaptureDeviceSelected() -{ - _grabberCritsect->Leave(); - return (_captureIsInitialized) ? true : false; - _grabberCritsect->Leave(); -} - -/** - Convert a Pascal string to a C string. - - \param[in] pascalString - Pascal string to convert. Pascal strings contain the number of - characters in the first byte and are not null-terminated. - - \param[out] cString - The C string buffer into which to copy the converted string. - - \param[in] bufferSize - The size of the C string buffer in bytes. - - \return The number of characters in the string on success and -1 on failure. - */ -CFIndex VideoCaptureMacQuickTime::PascalStringToCString( - const unsigned char* pascalString, char* cString, CFIndex bufferSize) -{ - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, - "%s:%d Converting pascal string to c string", __FUNCTION__, - __LINE__); - if (pascalString == NULL) - { - return -1; - } - - if (cString == NULL) - { - return -1; - } - - if (bufferSize == 0) - { - return -1; - } - - CFIndex cStringLength = 0; - CFIndex maxStringLength = bufferSize - 1; - - CFStringRef cfString = CFStringCreateWithPascalString( - NULL, pascalString, kCFStringEncodingMacRoman); - if (cfString == NULL) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, - "%s:%d Error in CFStringCreateWithPascalString()", - __FUNCTION__, __LINE__); - CFRelease(cfString); - return -1; - } - - CFIndex cfLength = CFStringGetLength(cfString); - cStringLength = cfLength; - if (cfLength > maxStringLength) - { - cStringLength = maxStringLength; - } - - Boolean success = CFStringGetCString(cfString, cString, bufferSize, - kCFStringEncodingMacRoman); - - // Ensure the problem isn't insufficient buffer length. - // This is fine; we will return a partial string. - if (success == false && cfLength <= maxStringLength) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, - "%s:%d Error in CFStringGetCString()", __FUNCTION__, - __LINE__); - CFRelease(cfString); - return -1; - } - - CFRelease(cfString); - return cStringLength; -} -} // namespace webrtc diff --git a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.h b/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.h deleted file mode 100644 index 84e0667e1..000000000 --- a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_quick_time.h - * - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_H_ - -#include - - -#include "../../device_info_impl.h" -#include "../../video_capture_impl.h" -#include "list_wrapper.h" - - -#define START_CODEC_WIDTH 352 -#define START_CODEC_HEIGHT 288 -#define SLEEP(x) usleep(x * 1000); - -namespace webrtc -{ -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; - -class VideoCaptureMacQuickTime : public VideoCaptureImpl -{ - -public: - VideoCaptureMacQuickTime(const WebRtc_Word32 id); - virtual ~VideoCaptureMacQuickTime(); - - static void Destroy(VideoCaptureModule* module); - - WebRtc_Word32 Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - virtual WebRtc_Word32 StartCapture( - const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - virtual bool CaptureStarted(); - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - - // TODO: remove? - int VideoCaptureInitThreadContext(); - int VideoCaptureTerminate(); - int VideoCaptureSetCaptureDevice(const char* deviceName, int size); - int UpdateCaptureSettings(int channel, webrtc::VideoCodec& inst, bool def); - int VideoCaptureRun(); - int VideoCaptureStop(); - -protected: - -private: // functions - - struct VideoCaptureMacName - { - VideoCaptureMacName(); - - enum { kVideoCaptureMacNameMaxSize = 64}; - char _name[kVideoCaptureMacNameMaxSize]; - CFIndex _size; - }; - - // Timeout value [ms] if we want to create a new device list or not - enum { kVideoCaptureDeviceListTimeout = 5000}; - // Temporary constant allowing this size from builtin iSight webcams. - enum { kYuy2_1280_1024_length = 2621440}; - -private: - - // Capture device callback - static OSErr SendProcess(SGChannel sgChannel, Ptr p, long len, long *offset, - long chRefCon, TimeValue time, short writeType, - long refCon); - int SendFrame(SGChannel sgChannel, char* data, long length, TimeValue time); - - // Capture device functions - int CreateLocalGWorld(int width, int height); - int RemoveLocalGWorld(); - int ConnectCaptureDevice(); - int DisconnectCaptureDevice(); - virtual bool IsCaptureDeviceSelected(); - - // Process to make sure the capture device won't stop - static bool GrabberUpdateThread(void*); - bool GrabberUpdateProcess(); - - // Starts and stops the capture - int StartQuickTimeCapture(); - int StopQuickTimeCapture(bool* wasCapturing = NULL); - - static CFIndex PascalStringToCString(const unsigned char* pascalString, - char* cString, - CFIndex bufferSize); - -private: // variables - WebRtc_Word32 _id; - bool _isCapturing; - VideoCaptureCapability _captureCapability; - CriticalSectionWrapper* _grabberCritsect; - CriticalSectionWrapper* _videoMacCritsect; - bool _terminated; - webrtc::ThreadWrapper* _grabberUpdateThread; - webrtc::EventWrapper* _grabberUpdateEvent; - SeqGrabComponent _captureGrabber; - Component _captureDevice; - char _captureDeviceDisplayName[64]; - RawVideoType _captureVideoType; - bool _captureIsInitialized; - GWorldPtr _gWorld; - SGChannel _captureChannel; - ImageSequence _captureSequence; - bool _sgPrepared; - bool _sgStarted; - int _trueCaptureWidth; - int _trueCaptureHeight; - ListWrapper _captureDeviceList; - unsigned long _captureDeviceListTime; - ListWrapper _captureCapabilityList; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_H_ diff --git a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.cc b/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.cc deleted file mode 100644 index 8eef158f0..000000000 --- a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.cc +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_quick_time_info.cc - * - */ - -#include "../../video_capture_config.h" -#include "video_capture_quick_time_info.h" - -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "trace.h" -#include "video_capture.h" - -namespace webrtc -{ - -VideoCaptureMacQuickTimeInfo::VideoCaptureMacQuickTimeInfo( - const WebRtc_Word32 iID) : - DeviceInfoImpl(iID), _id(iID), - _grabberCritsect(CriticalSectionWrapper::CreateCriticalSection()) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); -} - -VideoCaptureMacQuickTimeInfo::~VideoCaptureMacQuickTimeInfo() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::Init() -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - return 0; -} - -WebRtc_UWord32 VideoCaptureMacQuickTimeInfo::NumberOfDevices() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d", __FUNCTION__, __LINE__); - int numOfDevices = 0; - - // don't care about these variables... dummy vars to call GetCaptureDevices - const int kNameLength = 1024; - WebRtc_UWord8 deviceNameUTF8[kNameLength] = ""; - WebRtc_UWord8 deviceUniqueIdUTF8[kNameLength] = ""; - WebRtc_UWord8 productUniqueIdUTF8[kNameLength] = ""; - - if (GetCaptureDevices(0, deviceNameUTF8, kNameLength, deviceUniqueIdUTF8, - kNameLength, productUniqueIdUTF8, kNameLength, - numOfDevices) != 0) - { - return 0; - } - - return numOfDevices; -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::GetDeviceName( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameUTF8Length, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "%s:%d deviceNumber=\%d", __FUNCTION__, __LINE__); - - int numOfDevices = 0; // not needed for this function - return GetCaptureDevices(deviceNumber, deviceNameUTF8, - deviceNameUTF8Length, deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, productUniqueIdUTF8, - productUniqueIdUTF8Length, numOfDevices); -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, VideoCaptureCapability& resulting) -{ - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, void* parentWindow, - WebRtc_UWord32 positionX, WebRtc_UWord32 positionY) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - return -1; -} - -WebRtc_Word32 VideoCaptureMacQuickTimeInfo::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities is not supported on the Mac platform."); - return -1; -} - -int VideoCaptureMacQuickTimeInfo::GetCaptureDevices( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameUTF8Length, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length, int& numberOfDevices) -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s(wrapped):%d deviceNumber: %d", __FUNCTION__, __LINE__, - deviceNumber); - - numberOfDevices = 0; - memset(deviceNameUTF8, 0, deviceNameUTF8Length); - memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length); - memset(productUniqueIdUTF8, 0, productUniqueIdUTF8Length); - - if (deviceNumber < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Invalid deviceNumber", __FUNCTION__, __LINE__); - return -1; - } - - Component captureDevice = NULL; - SeqGrabComponent captureGrabber = NULL; - SGChannel captureChannel = NULL; - bool closeChannel = false; - - ComponentDescription compCaptureType; - - compCaptureType.componentType = SeqGrabComponentType; - compCaptureType.componentSubType = 0; - compCaptureType.componentManufacturer = 0; - compCaptureType.componentFlags = 0; - compCaptureType.componentFlagsMask = 0; - - // Get the number of sequence grabbers - long numSequenceGrabbers = CountComponents(&compCaptureType); - - if (deviceNumber > numSequenceGrabbers) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Invalid deviceNumber", __FUNCTION__, __LINE__); - return -1; - } - - if (numSequenceGrabbers <= 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d No sequence grabbers available", __FUNCTION__, - __LINE__); - return -1; - } - - // Open a sequence grabber - for (int seqGrabberIndex = 0; - seqGrabberIndex < numSequenceGrabbers; - seqGrabberIndex++) - { - captureDevice = FindNextComponent(0, &compCaptureType); - captureGrabber = OpenComponent(captureDevice); - if (captureGrabber != NULL) - { - // We've found a sequencegrabber - if (SGInitialize(captureGrabber) != noErr) - { - CloseComponent(captureGrabber); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, "%s:%d Could not init the sequence grabber", - __FUNCTION__, __LINE__); - return -1; - } - break; - } - if (seqGrabberIndex == numSequenceGrabbers - 1) - { - // Couldn't open a sequence grabber - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not open a sequence grabber", - __FUNCTION__, __LINE__); - return -1; - } - } - - // Create a temporary channel to get the names of the capture devices. - // Takes time, make this in a nother way... - if (SGNewChannel(captureGrabber, VideoMediaType, &captureChannel) != noErr) - { - // Could not create a video channel... - SGRelease(captureGrabber); - CloseComponent(captureGrabber); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not create a sequence grabber video channel", - __FUNCTION__, __LINE__); - return -1; - } - closeChannel = true; - - // Find the type of capture devices, e.g. USB-devices, Firewire, DV, ... - SGDeviceList deviceList = NULL; - if (SGGetChannelDeviceList(captureChannel, sgDeviceListIncludeInputs, - &deviceList) != noErr) - { - if (closeChannel) - SGDisposeChannel(captureGrabber, captureChannel); - if (captureGrabber) - { - SGRelease(captureGrabber); - CloseComponent(captureGrabber); - } - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "%s:%d Could not create a device list", __FUNCTION__, - __LINE__); - return -1; - } - - // Loop through all device types and all devices for each type - // and store in a list. - int numDevices = (*deviceList)->count; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d Found %d devices", __FUNCTION__, __LINE__, numDevices); - - for (int i = 0; i < numDevices; i++) - { - - SGDeviceName sgDeviceName = (*deviceList)->entry[i]; - // Get the list with input devices for this type of device - if (sgDeviceName.inputs) - { - SGDeviceInputList inputList = sgDeviceName.inputs; - int numInputDev = (*inputList)->count; - - for (int inputDevIndex = 0; - inputDevIndex < numInputDev; - inputDevIndex++) - { - // Get the name for this capture device - SGDeviceInputName deviceInputName = - (*inputList)->entry[inputDevIndex]; - - VideoCaptureMacName* deviceName = new VideoCaptureMacName(); - - deviceName->_size = PascalStringToCString( - deviceInputName.name, deviceName->_name, - sizeof(deviceName->_name)); - - if (deviceName->_size > 0) - { - WEBRTC_TRACE(webrtc::kTraceDebug,webrtc::kTraceVideoCapture, - _id, - "%s:%d Capture device %d: %s was successfully " - "set", __FUNCTION__, __LINE__, numberOfDevices, - deviceName->_name); - - if (numberOfDevices == deviceNumber) - { - strcpy((char*) deviceNameUTF8, deviceName->_name); - strcpy((char*) deviceUniqueIdUTF8, deviceName->_name); - } - numberOfDevices++; - } - else - { - delete deviceName; - - if (deviceName->_size < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "%s:%d Error in PascalStringToCString", - __FUNCTION__, __LINE__); - return -1; - } - } - } - } - } - - // clean up - SGDisposeDeviceList(captureGrabber, deviceList); - if (closeChannel) - { - SGDisposeChannel(captureGrabber, captureChannel); - } - if (captureGrabber) - { - SGRelease(captureGrabber); - CloseComponent(captureGrabber); - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - "%s:%d End function successfully", __FUNCTION__, __LINE__); - return 0; -} - -/** - Convert a Pascal string to a C string. - - \param[in] pascalString - Pascal string to convert. Pascal strings contain the number of - characters in the first byte and are not null-terminated. - - \param[out] cString - The C string buffer into which to copy the converted string. - - \param[in] bufferSize - The size of the C string buffer in bytes. - - \return The number of characters in the string on success and -1 on failure. - */ -CFIndex VideoCaptureMacQuickTimeInfo::PascalStringToCString( - const unsigned char* pascalString, char* cString, CFIndex bufferSize) -{ - if (pascalString == NULL) - { - return -1; - } - - if (cString == NULL) - { - return -1; - } - - if (bufferSize == 0) - { - return -1; - } - - CFIndex cStringLength = 0; - CFIndex maxStringLength = bufferSize - 1; - - CFStringRef cfString = CFStringCreateWithPascalString( - NULL, pascalString, kCFStringEncodingMacRoman); - if (cfString == NULL) - { - CFRelease(cfString); - return -1; - } - - CFIndex cfLength = CFStringGetLength(cfString); - cStringLength = cfLength; - if (cfLength > maxStringLength) - { - cStringLength = maxStringLength; - } - - Boolean success = CFStringGetCString(cfString, cString, bufferSize, - kCFStringEncodingMacRoman); - - // Ensure the problem isn't insufficient buffer length. - // This is fine; we will return a partial string. - if (success == false && cfLength <= maxStringLength) - { - CFRelease(cfString); - return -1; - } - - CFRelease(cfString); - return cStringLength; -} - -// -// -// Functions for handling capture devices -// -// - -VideoCaptureMacQuickTimeInfo::VideoCaptureMacName::VideoCaptureMacName() : - _size(0) -{ - memset(_name, 0, kVideoCaptureMacNameMaxSize); -} -} // namespace webrtc diff --git a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.h b/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.h deleted file mode 100644 index c099cb84c..000000000 --- a/modules/video_capture/main/source/Mac/QuickTime/video_capture_quick_time_info.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_quick_time_info.h - * - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_INFO_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_INFO_H_ - -#include - -#include "../../video_capture_impl.h" -#include "../../device_info_impl.h" -#include "list_wrapper.h" -#include "map_wrapper.h" - -class VideoRenderCallback; - -namespace webrtc -{ -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -class Trace; - -class VideoCaptureMacQuickTimeInfo: public DeviceInfoImpl -{ -public: - - static DeviceInfo* Create(const WebRtc_Word32 id); - static void Destroy(DeviceInfo* deviceInfo); - - VideoCaptureMacQuickTimeInfo(const WebRtc_Word32 id); - virtual ~VideoCaptureMacQuickTimeInfo(); - - WebRtc_Word32 Init(); - - virtual WebRtc_UWord32 NumberOfDevices(); - - /* - * Returns the available capture devices. - * deviceNumber -[in] index of capture device - * deviceNameUTF8 - friendly name of the capture device - * deviceUniqueIdUTF8 - unique name of the capture device if it exist. - * Otherwise same as deviceNameUTF8 - * productUniqueIdUTF8 - unique product id if it exist. Null terminated - * otherwise. - */ - virtual WebRtc_Word32 GetDeviceName( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8 = 0, - WebRtc_UWord32 productUniqueIdUTF8Length = 0); - - - // ************** The remaining public functions are not supported on Mac - - /* - * Returns the number of capabilities for this device - */ - virtual WebRtc_Word32 NumberOfCapabilities(const WebRtc_UWord8* deviceUniqueIdUTF8); - - /* - * Gets the capabilities of the named device - */ - virtual WebRtc_Word32 GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability); - - /* - * Gets the capability that best matches the requested width, height and frame rate. - * Returns the deviceCapabilityNumber on success. - */ - virtual WebRtc_Word32 GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting); - - /* - * Display OS /capture device specific settings dialog - */ - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, void* parentWindow, - WebRtc_UWord32 positionX, WebRtc_UWord32 positionY); - -protected: - virtual WebRtc_Word32 CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8); - -private: - - struct VideoCaptureMacName - { - VideoCaptureMacName(); - - enum - { - kVideoCaptureMacNameMaxSize = 64 - }; - char _name[kVideoCaptureMacNameMaxSize]; - CFIndex _size; - }; - - enum - { - kVideoCaptureMacDeviceListTimeout = 5000 - }; // Timeout value [ms] if we want to create a new device list or not - enum - { - kYuy2_1280_1024_length = 2621440 - }; // Temporary constant allowing this size from built-in iSight webcams. - -private: - // private methods - - int GetCaptureDevices(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameUTF8Length, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length, - int& numberOfDevices); - - static CFIndex PascalStringToCString(const unsigned char* pascalString, - char* cString, CFIndex bufferSize); - -private: - // member vars - WebRtc_Word32 _id; - bool _terminated; - CriticalSectionWrapper* _grabberCritsect; - webrtc::Trace* _trace; - webrtc::ThreadWrapper* _grabberUpdateThread; - webrtc::EventWrapper* _grabberUpdateEvent; - SeqGrabComponent _captureGrabber; - Component _captureDevice; - char _captureDeviceDisplayName[64]; - bool _captureIsInitialized; - GWorldPtr _gWorld; - SGChannel _captureChannel; - ImageSequence _captureSequence; - bool _sgPrepared; - bool _sgStarted; - int _codecWidth; - int _codecHeight; - int _trueCaptureWidth; - int _trueCaptureHeight; - ListWrapper _captureDeviceList; - WebRtc_Word64 _captureDeviceListTime; - ListWrapper _captureCapabilityList; -}; -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_QUICKTIME_VIDEO_CAPTURE_QUICK_TIME_INFO_H_ diff --git a/modules/video_capture/main/source/Mac/video_capture_mac.cc b/modules/video_capture/main/source/Mac/video_capture_mac.cc deleted file mode 100644 index 1309c0e9e..000000000 --- a/modules/video_capture/main/source/Mac/video_capture_mac.cc +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_mac.cc - * - */ - -// self header -#include "video_capture_mac.h" - -// super class stuff -#include "../video_capture_impl.h" -#include "../device_info_impl.h" -#include "../video_capture_config.h" - -#include "trace.h" - -#include - -// 10.4 support must be decided runtime. We will just decide which framework to -// use at compile time "work" classes. One for QTKit, one for QuickTime -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version -#include "QuickTime/video_capture_quick_time.h" -#include "QuickTime/video_capture_quick_time_info.h" -#else -#include "QTKit/video_capture_qtkit.h" -#include "QTKit/video_capture_qtkit_info.h" -#endif - -namespace webrtc -{ -namespace videocapturemodule -{ - -// static -bool VideoCaptureMac::CheckOSVersion() -{ - // Check OSX version - OSErr err = noErr; - - SInt32 version; - - err = Gestalt(gestaltSystemVersion, &version); - if (err != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, - "Could not get OS version"); - return false; - } - - if (version < 0x00001040) // Older version than Mac OSX 10.4 - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, - "OS version too old: 0x%x", version); - return false; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, - "OS version compatible: 0x%x", version); - - return true; -} - -// static -bool VideoCaptureMac::CheckQTVersion() -{ - // Check OSX version - OSErr err = noErr; - - SInt32 version; - - err = Gestalt(gestaltQuickTime, &version); - if (err != noErr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, - "Could not get QuickTime version"); - return false; - } - - if (version < 0x07000000) // QT v. 7.x or newer (QT 5.0.2 0x05020000) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, 0, - "QuickTime version too old: 0x%x", version); - return false; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, 0, - "QuickTime version compatible: 0x%x", version); - return true; -} -} // videocapturemodule - -/************************************************************************** - * - * Create/Destroy a VideoCaptureModule - * - ***************************************************************************/ - -/* - * Returns version of the module and its components - * - * version - buffer to which the version will be written - * remainingBufferInBytes - remaining number of WebRtc_Word8 in the version - * buffer - * position - position of the next empty WebRtc_Word8 in the - * version buffer - */ - -VideoCaptureModule* VideoCaptureModule::Create( - const WebRtc_Word32 id, const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "Create %s", deviceUniqueIdUTF8); - - if (webrtc::videocapturemodule::VideoCaptureMac::CheckOSVersion == false) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "OS version is too old. Could not create video capture " - "module. Returning NULL"); - return NULL; - } - -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - if (webrtc::videocapturemodule::VideoCaptureMac::CheckQTVersion == false) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "QuickTime version is too old. Could not create video " - "capture module. Returning NULL"); - return NULL; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "%s line %d. QTKit is not supported on this machine. Using " - "QuickTime framework to capture video", - __FILE__, __LINE__); - - webrtc::videocapturemodule::VideoCaptureMacQuickTime* newCaptureModule = - new webrtc::videocapturemodule::VideoCaptureMacQuickTime(id); - if (!newCaptureModule) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, id, - "could not Create for unique device %s, !newCaptureModule", - deviceUniqueIdUTF8); - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - - if (newCaptureModule->Init(id, deviceUniqueIdUTF8) != 0) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, id, - "could not Create for unique device %s, " - "newCaptureModule->Init()!=0", - deviceUniqueIdUTF8); - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - - // Successfully created VideoCaptureMacQuicktime. Return it - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "Module created for unique device %s. Will use QuickTime " - "framework to capture", - deviceUniqueIdUTF8); - return newCaptureModule; - -#else // QTKit version - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "Using QTKit framework to capture video", id); - - webrtc::videocapturemodule::VideoCaptureMacQTKit* newCaptureModule = - new webrtc::videocapturemodule::VideoCaptureMacQTKit(id); - if(!newCaptureModule) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, id, - "could not Create for unique device %s, !newCaptureModule", - deviceUniqueIdUTF8); - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - if(newCaptureModule->Init(id, deviceUniqueIdUTF8) != 0) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, id, - "could not Create for unique device %s, " - "newCaptureModule->Init()!=0", deviceUniqueIdUTF8); - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - - // Successfully created VideoCaptureMacQuicktime. Return it - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "Module created for unique device %s, will use QTKit " - "framework",deviceUniqueIdUTF8); - return newCaptureModule; -#endif -} - -void Destroy(VideoCaptureModule* module) -{ - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, 0, - "%s:%d Destroying GISPModuleVideoCapture", __FUNCTION__, - __LINE__); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - webrtc::videocapturemodule::VideoCaptureMacQuickTime* captureDevice = - static_cast (module); - delete captureDevice; - captureDevice = NULL; - -#else // QTKit version - webrtc::videocapturemodule::VideoCaptureMacQTKit* captureDevice = - static_cast (module); - delete captureDevice; - captureDevice = NULL; -#endif -} -/************************************************************************** - * - * End Create/Destroy VideoCaptureModule - * - ***************************************************************************/ - -// VideoCaptureMac class -namespace videocapturemodule -{ - -/************************************************************************** - * - * These will just delegate to the appropriate class - * - ***************************************************************************/ - -VideoCaptureMac::VideoCaptureMac(const WebRtc_Word32 id) : - VideoCaptureImpl(id), // super class constructor - _isFrameworkSupported(false), _captureClass(NULL) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - _isFrameworkSupported = false; - _captureClass = new VideoCaptureMacQuickTime(_id); - -#else // QTKit version - _isFrameworkSupported = true; - _captureClass = new VideoCaptureMacQTKit(_id); -#endif -} - -VideoCaptureMac::~VideoCaptureMac() -{ - delete _captureClass; -} - -WebRtc_Word32 VideoCaptureMac::Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast (_captureClass)->Init( - id, deviceUniqueIdUTF8); - -#else // QTKit version - return static_cast(_captureClass)->Init(id, deviceUniqueIdUTF8); -#endif -} - -WebRtc_Word32 VideoCaptureMac::StartCapture( - const VideoCaptureCapability& capability) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast (_captureClass)->StartCapture( - capability); - -#else // QTKit version - return static_cast - (_captureClass)->StartCapture(capability); -#endif -} - -WebRtc_Word32 VideoCaptureMac::StopCapture() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureClass)->StopCapture(); - -#else // QTKit version - return static_cast(_captureClass)->StopCapture(); -#endif -} - -bool VideoCaptureMac::CaptureStarted() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureClass)->CaptureStarted(); - -#else // QTKit version - return static_cast(_captureClass)->CaptureStarted(); -#endif -} - -WebRtc_Word32 VideoCaptureMac::CaptureSettings(VideoCaptureCapability& settings) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureClass)->CaptureSettings(settings); - -#else // QTKit version - return static_cast - (_captureClass)->CaptureSettings(settings); -#endif -} -} // namespace videocapturemodule - - -/************************************************************************** - * - * Create/Destroy a DeviceInfo - * - ***************************************************************************/ - -VideoCaptureModule::DeviceInfo* -VideoCaptureModule::CreateDeviceInfo(const WebRtc_Word32 id) -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "Create %d", id); - - if (webrtc::videocapturemodule::VideoCaptureMac::CheckOSVersion == false) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "OS version is too old. Could not create video capture " - "module. Returning NULL"); - return NULL; - } - -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - if (webrtc::videocapturemodule::VideoCaptureMac::CheckQTVersion == false) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, id, - "QuickTime version is too old. Could not create video " - "capture module. Returning NULL"); - return NULL; - } - - webrtc::videocapturemodule::VideoCaptureMacQuickTimeInfo* newCaptureInfoModule = - new webrtc::videocapturemodule::VideoCaptureMacQuickTimeInfo(id); - - if (!newCaptureInfoModule || newCaptureInfoModule->Init() != 0) - { - Destroy(newCaptureInfoModule); - newCaptureInfoModule = NULL; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "Failed to Init newCaptureInfoModule created with id %d " - "and device \"\" ", id); - return NULL; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "VideoCaptureModule created for id", id); - return newCaptureInfoModule; - -#else // QTKit version - webrtc::videocapturemodule::VideoCaptureMacQTKitInfo* newCaptureInfoModule = - new webrtc::videocapturemodule::VideoCaptureMacQTKitInfo(id); - - if(!newCaptureInfoModule || newCaptureInfoModule->Init() != 0) - { - //Destroy(newCaptureInfoModule); - delete newCaptureInfoModule; - newCaptureInfoModule = NULL; - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "Failed to Init newCaptureInfoModule created with id %d " - "and device \"\" ", id); - return NULL; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, id, - "VideoCaptureModule created for id", id); - return newCaptureInfoModule; - -#endif - -} - -void VideoCaptureModule::DestroyDeviceInfo(DeviceInfo* deviceInfo) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, - "%s:%d", __FUNCTION__, __LINE__); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - webrtc::videocapturemodule::VideoCaptureMacQuickTimeInfo* captureDeviceInfo = - static_cast (deviceInfo); - delete captureDeviceInfo; - captureDeviceInfo = NULL; - -#else // QTKit version - webrtc::videocapturemodule::VideoCaptureMacQTKitInfo* captureDeviceInfo = - static_cast (deviceInfo); - delete captureDeviceInfo; - captureDeviceInfo = NULL; -#endif - -} - -/************************************************************************** - * - * End Create/Destroy VideoCaptureModule - * - ***************************************************************************/ - -// VideoCaptureMacInfo class - -namespace videocapturemodule { - -/************************************************************************** - * - * These will just delegate to the appropriate class - * - ***************************************************************************/ - -VideoCaptureMacInfo::VideoCaptureMacInfo(const WebRtc_Word32 id) : - DeviceInfoImpl(id), _isFrameworkSupported(false)//, -//_captureInfoClass( NULL) // special init below -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - _isFrameworkSupported = false; - _captureInfoClass = new VideoCaptureMacQuickTimeInfo(_id); - -#else // QTKit version - _isFrameworkSupported = true; - _captureInfoClass = new VideoCaptureMacQTKitInfo(_id); -#endif - -} - -VideoCaptureMacInfo::~VideoCaptureMacInfo() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - delete _captureInfoClass; -#else // QTKit version - delete _captureInfoClass; -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::Init() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->Init(); -#else // QTKit version - return static_cast(_captureInfoClass)->Init(); -#endif -} - -WebRtc_UWord32 VideoCaptureMacInfo::NumberOfDevices() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->NumberOfDevices(); - -#else // QTKit version - return static_cast - (_captureInfoClass)->NumberOfDevices(); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::GetDeviceName( - WebRtc_UWord32 deviceNumber, WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->GetDeviceName(deviceNumber, deviceNameUTF8, - deviceNameLength, deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, - productUniqueIdUTF8, - productUniqueIdUTF8Length); - -#else // QTKit version - return static_cast - (_captureInfoClass)->GetDeviceName(deviceNumber, deviceNameUTF8, - deviceNameLength, deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, - productUniqueIdUTF8, - productUniqueIdUTF8Length); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->NumberOfCapabilities(deviceUniqueIdUTF8); - -#else // QTKit version - return static_cast - (_captureInfoClass)->NumberOfCapabilities(deviceUniqueIdUTF8); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->GetCapability(deviceUniqueIdUTF8, - deviceCapabilityNumber, capability); - -#else // QTKit version - return static_cast - (_captureInfoClass)->GetCapability(deviceUniqueIdUTF8, - deviceCapabilityNumber, capability); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, VideoCaptureCapability& resulting) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->GetBestMatchedCapability(deviceUniqueIdUTF8, - requested, resulting); - -#else // QTKit version - return static_cast - (_captureInfoClass)->GetBestMatchedCapability(deviceUniqueIdUTF8, - requested, resulting); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, void* parentWindow, - WebRtc_UWord32 positionX, WebRtc_UWord32 positionY) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version - return static_cast - (_captureInfoClass)->DisplayCaptureSettingsDialogBox(deviceUniqueIdUTF8, - dialogTitleUTF8, - parentWindow, - positionX, - positionY); -#else // QTKit version - return static_cast - (_captureInfoClass)->DisplayCaptureSettingsDialogBox(deviceUniqueIdUTF8, - dialogTitleUTF8, - parentWindow, - positionX, - positionY); -#endif -} - -WebRtc_Word32 VideoCaptureMacInfo::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED == __MAC_10_4 // QuickTime version -#else // QTKit version -#endif - // not supported. The call stack should never make it this deep. - // This call should be returned higher in the order - return -1; - -} -} // namespace webrtc -} // namespace videocapturemodule diff --git a/modules/video_capture/main/source/Mac/video_capture_mac.h b/modules/video_capture/main/source/Mac/video_capture_mac.h deleted file mode 100644 index 9fc5617ec..000000000 --- a/modules/video_capture/main/source/Mac/video_capture_mac.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_capture_mac.h - * - */ - -/************************************************************************** - * - * This class exists so that the correct capturing framework can be called - * at runtime for compatiblity reasons. - * - * * QTKit is the modern objective-c interface. Our capturing code was - * rewritten to use QTKit because QuickTime does not support 64-bit. - * Although QTKit exists in 10.4, it does not support all of the - * capture APIs needed - * * QuickTime is the older C++ interface. It supports - * - ***************************************************************************/ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_VIDEO_CAPTURE_MAC_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_VIDEO_CAPTURE_MAC_H_ - -#include "../video_capture_impl.h" -#include "../device_info_impl.h" -#include "../video_capture_config.h" - -namespace webrtc -{ -namespace videocapturemodule -{ - -class VideoCaptureMac: public VideoCaptureImpl -{ -public: - VideoCaptureMac(const WebRtc_Word32 id); - virtual ~VideoCaptureMac(); - - static VideoCaptureModule* Create(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - static void Destroy(VideoCaptureModule* module); - - WebRtc_Word32 Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - virtual WebRtc_Word32 StartCapture( - const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - virtual bool CaptureStarted(); - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - static bool CheckQTVersion(); - static bool CheckOSVersion(); - -private: - - bool _isFrameworkSupported; - VideoCaptureImpl* _captureClass; - WebRtc_Word32 _id; -}; - -class VideoCaptureMacInfo: public DeviceInfoImpl -{ -public: - // public methods - - static DeviceInfo* Create(const WebRtc_Word32 id); - static void Destroy(DeviceInfo* deviceInfo); - - VideoCaptureMacInfo(const WebRtc_Word32 id); - virtual ~VideoCaptureMacInfo(); - - WebRtc_Word32 Init(); - - virtual WebRtc_UWord32 NumberOfDevices(); - virtual WebRtc_Word32 GetDeviceName( - WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8 = 0, - WebRtc_UWord32 productUniqueIdUTF8Length = 0); - - virtual WebRtc_Word32 NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8); - - virtual WebRtc_Word32 GetCapability( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability); - - virtual WebRtc_Word32 GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting); - - virtual WebRtc_Word32 DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, - void* parentWindow, WebRtc_UWord32 positionX, - WebRtc_UWord32 positionY); - - virtual WebRtc_Word32 CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8); - -private: - bool _isFrameworkSupported; - DeviceInfoImpl* _captureInfoClass; - WebRtc_Word32 _id; -}; -} // namespace videocapturemodule -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_MAC_VIDEO_CAPTURE_MAC_H_ diff --git a/modules/video_capture/main/source/Windows/capture_delay_values_windows.h b/modules/video_capture/main/source/Windows/capture_delay_values_windows.h deleted file mode 100644 index a4d9da645..000000000 --- a/modules/video_capture/main/source/Windows/capture_delay_values_windows.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_CAPTURE_DELAY_VALUES_WINDOWS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_CAPTURE_DELAY_VALUES_WINDOWS_H_ - -#include "../video_capture_delay.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -const WebRtc_Word32 NoWindowsCaptureDelays=1; -const DelayValues WindowsCaptureDelays[NoWindowsCaptureDelays]= -{ - "Microsoft LifeCam Cinema","usb#vid_045e&pid_075d",{{640,480,125},{640,360,117},{424,240,111},{352,288,111},{320,240,116},{176,144,101},{160,120,109},{1280,720,166},{960,544,126},{800,448,120},{800,600,127}}, -}; - -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_CAPTURE_DELAY_VALUES_WINDOWS_H_ diff --git a/modules/video_capture/main/source/Windows/device_info_windows.cc b/modules/video_capture/main/source/Windows/device_info_windows.cc deleted file mode 100644 index ea4d70276..000000000 --- a/modules/video_capture/main/source/Windows/device_info_windows.cc +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "device_info_windows.h" - -#include "trace.h" -#include "help_functions_windows.h" -#include "../video_capture_config.h" -#include "capture_delay_values_windows.h" - -#include -#include - -namespace webrtc -{ -VideoCaptureModule::DeviceInfo* VideoCaptureModule::CreateDeviceInfo( - const WebRtc_Word32 id) -{ - videocapturemodule::DeviceInfoWindows* dsInfo = - new videocapturemodule::DeviceInfoWindows(id); - - if (!dsInfo || dsInfo->Init() != 0) - { - DestroyDeviceInfo(dsInfo); - dsInfo = NULL; - } - return dsInfo; -} - -void VideoCaptureModule::DestroyDeviceInfo(DeviceInfo* deviceInfo) -{ - videocapturemodule::DeviceInfoWindows* impl = - static_cast (deviceInfo); - delete impl; -} - -namespace videocapturemodule -{ - -DeviceInfoWindows::DeviceInfoWindows(const WebRtc_Word32 id) - : DeviceInfoImpl(id), _dsDevEnum(NULL), _dsMonikerDevEnum(NULL), - _CoUninitializeIsRequired(true) -{ - // 1) Initialize the COM library (make Windows load the DLLs). - // - // CoInitializeEx must be called at least once, and is usually called only once, - // for each thread that uses the COM library. Multiple calls to CoInitializeEx - // by the same thread are allowed as long as they pass the same concurrency flag, - // but subsequent valid calls return S_FALSE. - // To close the COM library gracefully on a thread, each successful call to - // CoInitializeEx, including any call that returns S_FALSE, must be balanced - // by a corresponding call to CoUninitialize. - // - - /*Apartment-threading, while allowing for multiple threads of execution, - serializes all incoming calls by requiring that calls to methods of objects created by this thread always run on the same thread - the apartment/thread that created them. In addition, calls can arrive only at message-queue boundaries (i.e., only during a - PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serialization, it is not typically necessary to write concurrency control into - the code for the object, other than to avoid calls to PeekMessage and SendMessage during processing that must not be interrupted by other method - invocations or calls to other objects in the same apartment/thread.*/ - - ///CoInitializeEx(NULL, COINIT_APARTMENTTHREADED ); //| COINIT_SPEED_OVER_MEMORY - HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); // Use COINIT_MULTITHREADED since Voice Engine uses COINIT_MULTITHREADED - if (FAILED(hr)) - { - // Avoid calling CoUninitialize() since CoInitializeEx() failed. - _CoUninitializeIsRequired = FALSE; - - if (hr == RPC_E_CHANGED_MODE) - { - // Calling thread has already initialized COM to be used in a single-threaded - // apartment (STA). We are then prevented from using STA. - // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is set". - // - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "VideoCaptureWindowsDSInfo::VideoCaptureWindowsDSInfo " - "CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) => " - "RPC_E_CHANGED_MODE, error 0x%x", - hr); - } - } -} - -DeviceInfoWindows::~DeviceInfoWindows() -{ - RELEASE_AND_CLEAR(_dsMonikerDevEnum); - RELEASE_AND_CLEAR(_dsDevEnum); - if (_CoUninitializeIsRequired) - { - CoUninitialize(); - } -} - -WebRtc_Word32 DeviceInfoWindows::Init() -{ - HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, - IID_ICreateDevEnum, (void **) &_dsDevEnum); - if (hr != NOERROR) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create CLSID_SystemDeviceEnum, error 0x%x", hr); - return -1; - } - return 0; -} -WebRtc_UWord32 DeviceInfoWindows::NumberOfDevices() -{ - ReadLockScoped cs(_apiLock); - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "NumberOfDevices"); - return GetDeviceInfo(0, 0, 0, 0, 0, 0, 0); - -} -WebRtc_Word32 DeviceInfoWindows::GetDeviceName( - WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "GetDeviceName"); - ReadLockScoped cs(_apiLock); - const WebRtc_Word32 result = GetDeviceInfo(deviceNumber, deviceNameUTF8, - deviceNameLength, - deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, - productUniqueIdUTF8, - productUniqueIdUTF8Length); - return result > (WebRtc_Word32) deviceNumber ? 0 : -1; -} - -WebRtc_Word32 DeviceInfoWindows::GetDeviceInfo( - WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) - -{ - - // enumerate all video capture devices - RELEASE_AND_CLEAR(_dsMonikerDevEnum); - HRESULT hr = - _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, - &_dsMonikerDevEnum, 0); - if (hr != NOERROR) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x." - " No webcam exist?", hr); - return 0; - } - - _dsMonikerDevEnum->Reset(); - ULONG cFetched; - IMoniker *pM; - int index = 0; - while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched)) - { - IPropertyBag *pBag; - hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **) &pBag); - if (S_OK == hr) - { - // Find the description or friendly name. - VARIANT varName; - VariantInit(&varName); - hr = pBag->Read(L"Description", &varName, 0); - if (FAILED(hr)) - { - hr = pBag->Read(L"FriendlyName", &varName, 0); - } - if (SUCCEEDED(hr)) - { - // ignore all VFW drivers - if ((wcsstr(varName.bstrVal, (L"(VFW)")) == NULL) && - (_wcsnicmp(varName.bstrVal, (L"Google Camera Adapter"),21) - != 0)) - { - // Found a valid device - if (index == deviceNumber) // This is the device we are interested in. - { - int convResult = 0; - if (deviceNameLength > 0) - { - convResult = WideCharToMultiByte(CP_UTF8, 0, - varName.bstrVal, -1, - (char*) deviceNameUTF8, - deviceNameLength, NULL, - NULL); - if (convResult == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "Failed to convert device name to UTF8. %d", - GetLastError()); - return -1; - } - } - if (deviceUniqueIdUTF8Length > 0) - { - hr = pBag->Read(L"DevicePath", &varName, 0); - if (FAILED(hr)) - { - strncpy_s((char *) deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, - (char *) deviceNameUTF8, convResult); - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "Failed to get deviceUniqueIdUTF8 using deviceNameUTF8"); - } - else - { - convResult = WideCharToMultiByte( - CP_UTF8, - 0, - varName.bstrVal, - -1, - (char*) deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length, - NULL, NULL); - if (convResult == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, - webrtc::kTraceVideoCapture, _id, - "Failed to convert device name to UTF8. %d", - GetLastError()); - return -1; - } - if (productUniqueIdUTF8 - && productUniqueIdUTF8Length > 0) - { - GetProductId(deviceUniqueIdUTF8, - productUniqueIdUTF8, - productUniqueIdUTF8Length); - } - } - } - - } - ++index; // increase the number of valid devices - } - } - VariantClear(&varName); - pBag->Release(); - pM->Release(); - } - - } - if (deviceNameLength) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, "%s %s", - __FUNCTION__, deviceNameUTF8); - } - return index; -} - -IBaseFilter * DeviceInfoWindows::GetDeviceFilter( - const WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ - - const WebRtc_Word32 deviceUniqueIdUTF8Length = - (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); // UTF8 is also NULL terminated - if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Device name too long"); - return NULL; - } - - // enumerate all video capture devices - RELEASE_AND_CLEAR(_dsMonikerDevEnum); - HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, - &_dsMonikerDevEnum, 0); - if (hr != NOERROR) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to enumerate CLSID_SystemDeviceEnum, error 0x%x." - " No webcam exist?", hr); - return 0; - } - _dsMonikerDevEnum->Reset(); - ULONG cFetched; - IMoniker *pM; - - IBaseFilter *captureFilter = NULL; - bool deviceFound = false; - while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) - { - IPropertyBag *pBag; - hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **) &pBag); - if (S_OK == hr) - { - // Find the description or friendly name. - VARIANT varName; - VariantInit(&varName); - if (deviceUniqueIdUTF8Length > 0) - { - hr = pBag->Read(L"DevicePath", &varName, 0); - if (FAILED(hr)) - { - hr = pBag->Read(L"Description", &varName, 0); - if (FAILED(hr)) - { - hr = pBag->Read(L"FriendlyName", &varName, 0); - } - } - if (SUCCEEDED(hr)) - { - char tempDevicePathUTF8[256]; - tempDevicePathUTF8[0] = 0; - const int compresult = - WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1, - tempDevicePathUTF8, - sizeof(tempDevicePathUTF8), NULL, - NULL); - if (strncmp(tempDevicePathUTF8, - (const char*) deviceUniqueIdUTF8, - deviceUniqueIdUTF8Length) == 0) - { - // We have found the requested device - deviceFound = true; - hr = pM->BindToObject(0, 0, IID_IBaseFilter, - (void**) &captureFilter); - if FAILED(hr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, "Failed to bind to the selected capture device %d",hr); - } - - if (productUniqueIdUTF8 - && productUniqueIdUTF8Length > 0) // Get the device name - { - - GetProductId(deviceUniqueIdUTF8, - productUniqueIdUTF8, - productUniqueIdUTF8Length); - } - - } - } - } - VariantClear(&varName); - pBag->Release(); - pM->Release(); - } - } - return captureFilter; -} - -WebRtc_Word32 DeviceInfoWindows::GetWindowsCapability( - const WebRtc_Word32 capabilityIndex, - VideoCaptureCapabilityWindows& windowsCapability) - -{ - ReadLockScoped cs(_apiLock); - // Make sure the number is valid - if (capabilityIndex >= _captureCapabilities.Size() || capabilityIndex < 0) - return -1; - - MapItem* item = _captureCapabilities.Find(capabilityIndex); - if (!item) - return -1; - - VideoCaptureCapabilityWindows* capPointer = - static_cast (item->GetItem()); - windowsCapability = *capPointer; - return 0; -} - -WebRtc_Word32 DeviceInfoWindows::CreateCapabilityMap( - const WebRtc_UWord8* deviceUniqueIdUTF8) - -{ - // Reset old capability list - MapItem* item = NULL; - while (item = _captureCapabilities.Last()) - { - delete item->GetItem(); - _captureCapabilities.Erase(item); - } - - const WebRtc_Word32 deviceUniqueIdUTF8Length = - (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); - if (deviceUniqueIdUTF8Length > kVideoCaptureUniqueNameLength) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Device name too long"); - return -1; - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "CreateCapabilityMap called for device %s", deviceUniqueIdUTF8); - - - WebRtc_UWord8 productId[kVideoCaptureProductIdLength]; - IBaseFilter* captureDevice = DeviceInfoWindows::GetDeviceFilter( - deviceUniqueIdUTF8, - productId, - kVideoCaptureProductIdLength); - if (!captureDevice) - return -1; - IPin* outputCapturePin = GetOutputPin(captureDevice); - if (!outputCapturePin) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to get capture device output pin"); - RELEASE_AND_CLEAR(captureDevice); - return -1; - } - IAMExtDevice* extDevice = NULL; - HRESULT hr = captureDevice->QueryInterface(IID_IAMExtDevice, - (void **) &extDevice); - if (SUCCEEDED(hr) && extDevice) - { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "This is an external device"); - extDevice->Release(); - } - - IAMStreamConfig* streamConfig = NULL; - hr = outputCapturePin->QueryInterface(IID_IAMStreamConfig, - (void**) &streamConfig); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to get IID_IAMStreamConfig interface from capture device"); - return -1; - } - - // this gets the FPS - IAMVideoControl* videoControlConfig = NULL; - HRESULT hrVC = captureDevice->QueryInterface(IID_IAMVideoControl, - (void**) &videoControlConfig); - if (FAILED(hrVC)) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "IID_IAMVideoControl Interface NOT SUPPORTED"); - } - - AM_MEDIA_TYPE *pmt = NULL; - VIDEO_STREAM_CONFIG_CAPS caps; - int count, size; - - hr = streamConfig->GetNumberOfCapabilities(&count, &size); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to GetNumberOfCapabilities"); - RELEASE_AND_CLEAR(videoControlConfig); - RELEASE_AND_CLEAR(streamConfig); - RELEASE_AND_CLEAR(outputCapturePin); - RELEASE_AND_CLEAR(captureDevice); - return -1; - } - - WebRtc_Word32 index = 0; // Index in created _capabilities map - // Check if the device support formattype == FORMAT_VideoInfo2 and FORMAT_VideoInfo. - // Prefer FORMAT_VideoInfo since some cameras (ZureCam) has been seen having problem with MJPEG and FORMAT_VideoInfo2 - // Interlace flag is only supported in FORMAT_VideoInfo2 - bool supportFORMAT_VideoInfo2 = false; - bool supportFORMAT_VideoInfo = false; - bool foundInterlacedFormat = false; - GUID preferedVideoFormat = FORMAT_VideoInfo; - for (WebRtc_Word32 tmp = 0; tmp < count; ++tmp) - { - hr = streamConfig->GetStreamCaps(tmp, &pmt, - reinterpret_cast (&caps)); - if (!FAILED(hr)) - { - if (pmt->majortype == MEDIATYPE_Video - && pmt->formattype == FORMAT_VideoInfo2) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - " Device support FORMAT_VideoInfo2"); - supportFORMAT_VideoInfo2 = true; - VIDEOINFOHEADER2* h = - reinterpret_cast (pmt->pbFormat); - assert(h); - foundInterlacedFormat |= h->dwInterlaceFlags - & (AMINTERLACE_IsInterlaced - | AMINTERLACE_DisplayModeBobOnly); - } - if (pmt->majortype == MEDIATYPE_Video - && pmt->formattype == FORMAT_VideoInfo) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id, - " Device support FORMAT_VideoInfo2"); - supportFORMAT_VideoInfo = true; - } - } - } - if (supportFORMAT_VideoInfo2) - { - if (supportFORMAT_VideoInfo && !foundInterlacedFormat) - { - preferedVideoFormat = FORMAT_VideoInfo; - } - else - { - preferedVideoFormat = FORMAT_VideoInfo2; - } - } - - for (WebRtc_Word32 tmp = 0; tmp < count; ++tmp) - { - hr = streamConfig->GetStreamCaps(tmp, &pmt, - reinterpret_cast (&caps)); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to GetStreamCaps"); - RELEASE_AND_CLEAR(videoControlConfig); - RELEASE_AND_CLEAR(streamConfig); - RELEASE_AND_CLEAR(outputCapturePin); - RELEASE_AND_CLEAR(captureDevice); - return -1; - } - - if (pmt->majortype == MEDIATYPE_Video - && pmt->formattype == preferedVideoFormat) - { - - VideoCaptureCapabilityWindows* capability = - new VideoCaptureCapabilityWindows(); - WebRtc_Word64 avgTimePerFrame = 0; - bool interlaced = false; - - if (pmt->formattype == FORMAT_VideoInfo) - { - VIDEOINFOHEADER* h = - reinterpret_cast (pmt->pbFormat); - assert(h); - capability->directShowCapabilityIndex = tmp; - capability->width = h->bmiHeader.biWidth; - capability->height = h->bmiHeader.biHeight; - avgTimePerFrame = h->AvgTimePerFrame; - } - if (pmt->formattype == FORMAT_VideoInfo2) - { - VIDEOINFOHEADER2* h = - reinterpret_cast (pmt->pbFormat); - assert(h); - capability->directShowCapabilityIndex = tmp; - capability->width = h->bmiHeader.biWidth; - capability->height = h->bmiHeader.biHeight; - capability->interlaced = h->dwInterlaceFlags - & (AMINTERLACE_IsInterlaced - | AMINTERLACE_DisplayModeBobOnly); - avgTimePerFrame = h->AvgTimePerFrame; - } - - if (hrVC == S_OK) - { - LONGLONG *maxFps; // array - long listSize; - SIZE size; - size.cx = capability->width; - size.cy = capability->height; - - // GetMaxAvailableFrameRate doesn't return max frame rate always - // eg: Logitech Notebook. This may be due to a bug in that API - // because GetFrameRateList array is reversed in the above camera. So - // a util method written. Can't assume the first value will return - // the max fps. - hrVC = videoControlConfig->GetFrameRateList(outputCapturePin, - tmp, size, - &listSize, - &maxFps); - - if (hrVC == S_OK && listSize > 0) - { - LONGLONG maxFPS = GetMaxOfFrameArray(maxFps, listSize); - capability->maxFPS = static_cast (10000000 - / maxFPS); - capability->supportFrameRateControl = true; - } - else // use existing method - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, - _id, - "GetMaxAvailableFrameRate NOT SUPPORTED"); - if (avgTimePerFrame > 0) - capability->maxFPS = static_cast (10000000 - / avgTimePerFrame); - else - capability->maxFPS = 0; - } - } - else // use existing method in case IAMVideoControl is not supported - { - if (avgTimePerFrame > 0) - capability->maxFPS = static_cast (10000000 - / avgTimePerFrame); - else - capability->maxFPS = 0; - } - - // can't switch MEDIATYPE :~( - if (pmt->subtype == MEDIASUBTYPE_I420) - { - capability->rawType = kVideoI420; - } - else if (pmt->subtype == MEDIASUBTYPE_IYUV) - { - capability->rawType = kVideoIYUV; - } - else if (pmt->subtype == MEDIASUBTYPE_RGB24) - { - capability->rawType = kVideoRGB24; - } - else if (pmt->subtype == MEDIASUBTYPE_YUY2) - { - capability->rawType = kVideoYUY2; - } - else if (pmt->subtype == MEDIASUBTYPE_RGB565) - { - capability->rawType = kVideoRGB565; - } - else if (pmt->subtype == MEDIASUBTYPE_MJPG) - { - capability->rawType = kVideoMJPEG; - } - else if (pmt->subtype == MEDIASUBTYPE_dvsl - || pmt->subtype == MEDIASUBTYPE_dvsd - || pmt->subtype == MEDIASUBTYPE_dvhd) // If this is an external DV camera - { - capability->rawType = kVideoYUY2;// MS DV filter seems to create this type - } - else if (pmt->subtype == MEDIASUBTYPE_UYVY) // Seen used by Declink capture cards - { - capability->rawType = kVideoUYVY; - } - else if (pmt->subtype == MEDIASUBTYPE_HDYC) // Seen used by Declink capture cards. Uses BT. 709 color. Not entiry correct to use UYVY. http://en.wikipedia.org/wiki/YCbCr - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Device support HDYC."); - capability->rawType = kVideoUYVY; - } - else - { - WCHAR strGuid[39]; - StringFromGUID2(pmt->subtype, strGuid, 39); - WEBRTC_TRACE( webrtc::kTraceWarning, - webrtc::kTraceVideoCapture, _id, - "Device support unknown media type %ls, width %d, height %d", - strGuid); - delete capability; - continue; - } - - // Get the expected capture delay from the static list - capability->expectedCaptureDelay - = GetExpectedCaptureDelay(WindowsCaptureDelays, - NoWindowsCaptureDelays, - productId, - capability->width, - capability->height); - _captureCapabilities.Insert(index++, capability); - WEBRTC_TRACE( webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Camera capability, width:%d height:%d type:%d fps:%d", - capability->width, capability->height, - capability->rawType, capability->maxFPS); - } - DeleteMediaType(pmt); - pmt = NULL; - } - RELEASE_AND_CLEAR(streamConfig); - RELEASE_AND_CLEAR(videoControlConfig); - RELEASE_AND_CLEAR(outputCapturePin); - RELEASE_AND_CLEAR(captureDevice); // Release the capture device - - // Store the new used device name - _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; - _lastUsedDeviceName = (WebRtc_UWord8*) realloc(_lastUsedDeviceName, - _lastUsedDeviceNameLength - + 1); - memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, _lastUsedDeviceNameLength+ 1); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "CreateCapabilityMap %d", _captureCapabilities.Size()); - - return _captureCapabilities.Size(); -} - -/* Constructs a product ID from the Windows DevicePath. on a USB device the devicePath contains product id and vendor id. - This seems to work for firewire as well - /* Example of device path - "\\?\usb#vid_0408&pid_2010&mi_00#7&258e7aaf&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" - "\\?\avc#sony&dv-vcr&camcorder&dv#65b2d50301460008#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" - */ -void DeviceInfoWindows::GetProductId(const WebRtc_UWord8* devicePath, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length) -{ - *productUniqueIdUTF8 = '\0'; - char* startPos = strstr((char*) devicePath, "\\\\?\\"); - if (!startPos) - { - strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "Failed to get the product Id"); - return; - } - startPos += 4; - - char* pos = strchr(startPos, '&'); - if (!pos || pos >= (char*) devicePath + strlen((char*) devicePath)) - { - strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "Failed to get the product Id"); - return; - } - // Find the second occurence - pos = strchr(pos + 1, '&'); - WebRtc_UWord32 bytesToCopy = (WebRtc_UWord32)(pos - startPos); - if (pos && (bytesToCopy <= productUniqueIdUTF8Length) && bytesToCopy - <= kVideoCaptureProductIdLength) - { - strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, - (char*) startPos, bytesToCopy); - } - else - { - strncpy_s((char*) productUniqueIdUTF8, productUniqueIdUTF8Length, "", 1); - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "Failed to get the product Id"); - } -} - -WebRtc_Word32 DeviceInfoWindows::DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, - void* parentWindow, - WebRtc_UWord32 positionX, - WebRtc_UWord32 positionY) -{ - ReadLockScoped cs(_apiLock); - HWND window = (HWND) parentWindow; - - IBaseFilter* filter = GetDeviceFilter(deviceUniqueIdUTF8, NULL, 0); - if (!filter) - return -1; - - ISpecifyPropertyPages* pPages = NULL; - CAUUID uuid; - HRESULT hr = S_OK; - - hr = filter->QueryInterface(IID_ISpecifyPropertyPages, (LPVOID*) &pPages); - if (!SUCCEEDED(hr)) - { - filter->Release(); - return -1; - } - hr = pPages->GetPages(&uuid); - if (!SUCCEEDED(hr)) - { - filter->Release(); - return -1; - } - - WCHAR tempDialogTitleWide[256]; - tempDialogTitleWide[0] = 0; - int size = 255; - - // UTF-8 to wide char - MultiByteToWideChar(CP_UTF8, 0, (char*) dialogTitleUTF8, -1, - tempDialogTitleWide, size); - - // Invoke a dialog box to display. - - hr = OleCreatePropertyFrame(window, // You must create the parent window. - positionX, // Horizontal position for the dialog box. - positionY, // Vertical position for the dialog box. - tempDialogTitleWide,// String used for the dialog box caption. - 1, // Number of pointers passed in pPlugin. - (LPUNKNOWN*) &filter, // Pointer to the filter. - uuid.cElems, // Number of property pages. - uuid.pElems, // Array of property page CLSIDs. - LOCALE_USER_DEFAULT, // Locale ID for the dialog box. - 0, NULL); // Reserved - // Release memory. - if (uuid.pElems) - { - CoTaskMemFree(uuid.pElems); - } - filter->Release(); - return 0; -} -} // namespace videocapturemodule -} // namespace webrtc diff --git a/modules/video_capture/main/source/Windows/device_info_windows.h b/modules/video_capture/main/source/Windows/device_info_windows.h deleted file mode 100644 index b05a63cdc..000000000 --- a/modules/video_capture/main/source/Windows/device_info_windows.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_WINDOWS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_WINDOWS_H_ - -#include "../video_capture_impl.h" -#include "../device_info_impl.h" - -#include -#include "map_wrapper.h" - -// forward declarations -namespace webrtc -{ -namespace videocapturemodule -{ -struct VideoCaptureCapabilityWindows: public VideoCaptureCapability -{ - WebRtc_UWord32 directShowCapabilityIndex; - bool supportFrameRateControl; - VideoCaptureCapabilityWindows() - { - directShowCapabilityIndex = 0; - supportFrameRateControl = false; - } - -}; -class DeviceInfoWindows: public DeviceInfoImpl -{ -public: - DeviceInfoWindows(const WebRtc_Word32 id); - virtual ~DeviceInfoWindows(); - - WebRtc_Word32 Init(); - virtual WebRtc_UWord32 NumberOfDevices(); - - /* - * Returns the available capture devices. - */ - virtual WebRtc_Word32 - GetDeviceName(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length); - - /* - * Display OS /capture device specific settings dialog - */ - virtual WebRtc_Word32 - DisplayCaptureSettingsDialogBox( - const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord8* dialogTitleUTF8, - void* parentWindow, - WebRtc_UWord32 positionX, - WebRtc_UWord32 positionY); - - // Windows specific - - /* Gets a capture device filter - The user of this API is responsible for releasing the filter when it not needed. - */ - IBaseFilter * GetDeviceFilter(const WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord8* productUniqueIdUTF8 = NULL, - WebRtc_UWord32 productUniqueIdUTF8Length = 0); - - WebRtc_Word32 - GetWindowsCapability(const WebRtc_Word32 capabilityIndex, - VideoCaptureCapabilityWindows& windowsCapability); - - static void GetProductId(const WebRtc_UWord8* devicePath, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length); -protected: - - WebRtc_Word32 GetDeviceInfo(WebRtc_UWord32 deviceNumber, - WebRtc_UWord8* deviceNameUTF8, - WebRtc_UWord32 deviceNameLength, - WebRtc_UWord8* deviceUniqueIdUTF8, - WebRtc_UWord32 deviceUniqueIdUTF8Length, - WebRtc_UWord8* productUniqueIdUTF8, - WebRtc_UWord32 productUniqueIdUTF8Length); - - virtual WebRtc_Word32 - CreateCapabilityMap(const WebRtc_UWord8* deviceUniqueIdUTF8); - -private: - ICreateDevEnum* _dsDevEnum; - IEnumMoniker* _dsMonikerDevEnum; - bool _CoUninitializeIsRequired; - -}; -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_WINDOWS_H_ diff --git a/modules/video_capture/main/source/Windows/help_functions_windows.cc b/modules/video_capture/main/source/Windows/help_functions_windows.cc deleted file mode 100644 index dd533a3dc..000000000 --- a/modules/video_capture/main/source/Windows/help_functions_windows.cc +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "help_functions_windows.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -// This returns minimum :), which will give max frame rate... -LONGLONG GetMaxOfFrameArray(LONGLONG *maxFps, long size) -{ - LONGLONG maxFPS = maxFps[0]; - for (int i = 0; i < size; i++) - { - if (maxFPS > maxFps[i]) - maxFPS = maxFps[i]; - } - return maxFPS; -} - -IPin* GetInputPin(IBaseFilter* filter) -{ - HRESULT hr; - IPin* pin = NULL; - IEnumPins* pPinEnum = NULL; - filter->EnumPins(&pPinEnum); - if (pPinEnum == NULL) - { - return NULL; - } - - // get first unconnected pin - hr = pPinEnum->Reset(); // set to first pin - - int count = 0; - while (S_OK == pPinEnum->Next(1, &pin, NULL)) - { - PIN_DIRECTION pPinDir; - pin->QueryDirection(&pPinDir); - if (PINDIR_INPUT == pPinDir) // This is an input pin - { - IPin* tempPin = NULL; - if (S_OK != pin->ConnectedTo(&tempPin)) // The pint is not connected - { - pPinEnum->Release(); - return pin; - } - } - pin->Release(); - } - pPinEnum->Release(); - return NULL; -} - -IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category) -{ - HRESULT hr; - IPin* pin = NULL; - IEnumPins* pPinEnum = NULL; - filter->EnumPins(&pPinEnum); - if (pPinEnum == NULL) - { - return NULL; - } - // get first unconnected pin - hr = pPinEnum->Reset(); // set to first pin - int count = 0; - while (S_OK == pPinEnum->Next(1, &pin, NULL)) - { - PIN_DIRECTION pPinDir; - pin->QueryDirection(&pPinDir); - if (PINDIR_OUTPUT == pPinDir) // This is an output pin - { - if (Category == GUID_NULL || PinMatchesCategory(pin, Category)) - { - pPinEnum->Release(); - return pin; - } - } - pin->Release(); - pin = NULL; - } - pPinEnum->Release(); - return NULL; -} - -BOOL PinMatchesCategory(IPin *pPin, REFGUID Category) -{ - BOOL bFound = FALSE; - IKsPropertySet *pKs = NULL; - HRESULT hr = pPin->QueryInterface(IID_PPV_ARGS(&pKs)); - if (SUCCEEDED(hr)) - { - GUID PinCategory; - DWORD cbReturned; - hr = pKs->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, &PinCategory, - sizeof(GUID), &cbReturned); - if (SUCCEEDED(hr) && (cbReturned == sizeof(GUID))) - { - bFound = (PinCategory == Category); - } - pKs->Release(); - } - return bFound; -} -} // namespace videocapturemodule -} // namespace webrtc - diff --git a/modules/video_capture/main/source/Windows/help_functions_windows.h b/modules/video_capture/main/source/Windows/help_functions_windows.h deleted file mode 100644 index 002087789..000000000 --- a/modules/video_capture/main/source/Windows/help_functions_windows.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_WINDOWS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_WINDOWS_H_ - -#include -#include - -DEFINE_GUID(MEDIASUBTYPE_I420, 0x30323449, 0x0000, 0x0010, 0x80, 0x00, 0x00, - 0xAA, 0x00, 0x38, 0x9B, 0x71); -DEFINE_GUID(MEDIASUBTYPE_V210, 0x30313276, 0x0000, 0x0010, 0x80, 0x00, 0x00, - 0xAA, 0x00, 0x38, 0x9B, 0x71); -DEFINE_GUID(MEDIASUBTYPE_HDYC, 0x43594448, 0x0000, 0x0010, 0x80, 0x00, 0x00, - 0xAA, 0x00, 0x38, 0x9B, 0x71); - -#define RELEASE_AND_CLEAR(p) if (p) { (p) -> Release () ; (p) = NULL ; } - -namespace webrtc -{ -namespace videocapturemodule -{ -LONGLONG GetMaxOfFrameArray(LONGLONG *maxFps, long size); - -IPin* GetInputPin(IBaseFilter* filter); -IPin* GetOutputPin(IBaseFilter* filter, REFGUID Category = GUID_NULL); -BOOL PinMatchesCategory(IPin *pPin, REFGUID Category); - -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_HELP_FUNCTIONS_WINDOWS_H_ diff --git a/modules/video_capture/main/source/Windows/sink_filter_windows.cc b/modules/video_capture/main/source/Windows/sink_filter_windows.cc deleted file mode 100644 index 9ee582d92..000000000 --- a/modules/video_capture/main/source/Windows/sink_filter_windows.cc +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "sink_filter_windows.h" - -#include "vplib.h" -#include "trace.h" -#include "help_functions_windows.h" - -#include // VIDEOINFOHEADER2 -#include - -#define DELETE_RESET(p) { delete (p) ; (p) = NULL ;} - -namespace webrtc -{ -namespace videocapturemodule -{ - -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero -} THREADNAME_INFO; - -DEFINE_GUID(CLSID_SINKFILTER, 0x88cdbbdc, 0xa73b, 0x4afa, 0xac, 0xbf, 0x15, 0xd5, - 0xe2, 0xce, 0x12, 0xc3); - -CaptureInputPin::CaptureInputPin (WebRtc_Word32 moduleId, - IN TCHAR * szName, - IN CaptureSinkFilter* pFilter, - IN CCritSec * pLock, - OUT HRESULT * pHr, - IN LPCWSTR pszName) - : CBaseInputPin (szName, pFilter, pLock, pHr, pszName), - _requestedCapability(), - _resultingCapability() -{ - _moduleId=moduleId; - _threadHandle = NULL; -} - -CaptureInputPin::~CaptureInputPin() -{ -} - -HRESULT -CaptureInputPin::GetMediaType (IN int iPosition, OUT CMediaType * pmt) -{ - // reset the thread handle - _threadHandle = NULL; - - if(iPosition < 0) - return E_INVALIDARG; - - VIDEOINFOHEADER* pvi = (VIDEOINFOHEADER*) pmt->AllocFormatBuffer( - sizeof(VIDEOINFOHEADER)); - if(NULL == pvi) - { - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, - "CheckMediaType VIDEOINFOHEADER is NULL. Returning...Line:%d\n", __LINE__); - return(E_OUTOFMEMORY); - } - - ZeroMemory(pvi, sizeof(VIDEOINFOHEADER)); - pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - pvi->bmiHeader.biPlanes = 1; - pvi->bmiHeader.biClrImportant = 0; - pvi->bmiHeader.biClrUsed = 0; - pvi->AvgTimePerFrame = 10000000/_requestedCapability.maxFPS; - - SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered. - SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle - - pmt->SetType(&MEDIATYPE_Video); - pmt->SetFormatType(&FORMAT_VideoInfo); - pmt->SetTemporalCompression(FALSE); - - WebRtc_Word32 positionOffset=1; - if(_requestedCapability.codecType!=kVideoCodecUnknown) - { - positionOffset=0; - } - - switch (iPosition+positionOffset) - { - case 0: - { - pvi->bmiHeader.biCompression = MAKEFOURCC('I','4','2','0'); - pvi->bmiHeader.biBitCount = 12; //bit per pixel - pvi->bmiHeader.biWidth = _requestedCapability.width; - pvi->bmiHeader.biHeight = _requestedCapability.height; - pvi->bmiHeader.biSizeImage = 3*_requestedCapability.height - *_requestedCapability.width/2; - pmt->SetSubtype(&MEDIASUBTYPE_I420); - } - break; - case 1: - { - pvi->bmiHeader.biCompression = MAKEFOURCC('Y','U','Y','2');; - pvi->bmiHeader.biBitCount = 16; //bit per pixel - pvi->bmiHeader.biWidth = _requestedCapability.width; - pvi->bmiHeader.biHeight = _requestedCapability.height; - pvi->bmiHeader.biSizeImage = 2*_requestedCapability.width - *_requestedCapability.height; - pmt->SetSubtype(&MEDIASUBTYPE_YUY2); - } - break; - case 2: - { - pvi->bmiHeader.biCompression = BI_RGB; - pvi->bmiHeader.biBitCount = 24; //bit per pixel - pvi->bmiHeader.biWidth = _requestedCapability.width; - pvi->bmiHeader.biHeight = _requestedCapability.height; - pvi->bmiHeader.biSizeImage = 3*_requestedCapability.height - *_requestedCapability.width; - pmt->SetSubtype(&MEDIASUBTYPE_RGB24); - } - break; - case 3: - { - pvi->bmiHeader.biCompression = MAKEFOURCC('U','Y','V','Y'); - pvi->bmiHeader.biBitCount = 16; //bit per pixel - pvi->bmiHeader.biWidth = _requestedCapability.width; - pvi->bmiHeader.biHeight = _requestedCapability.height; - pvi->bmiHeader.biSizeImage = 2*_requestedCapability.height - *_requestedCapability.width; - pmt->SetSubtype(&MEDIASUBTYPE_UYVY); - } - break; - default : - return VFW_S_NO_MORE_ITEMS; - } - pmt->SetSampleSize(pvi->bmiHeader.biSizeImage); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, - "GetMediaType position %d, width %d, height %d, biCompression 0x%x", - iPosition, _requestedCapability.width, - _requestedCapability.height,pvi->bmiHeader.biCompression); - return NOERROR; -} - -HRESULT -CaptureInputPin::CheckMediaType ( IN const CMediaType * pMediaType) -{ - // reset the thread handle - _threadHandle = NULL; - - const GUID *type = pMediaType->Type(); - if (*type != MEDIATYPE_Video) - return E_INVALIDARG; - - const GUID *formatType = pMediaType->FormatType(); - - // Check for the subtypes we support - const GUID *SubType = pMediaType->Subtype(); - if (SubType == NULL) - { - return E_INVALIDARG; - } - - if(*formatType == FORMAT_VideoInfo) - { - VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *) pMediaType->Format(); - if(pvi == NULL) - { - return E_INVALIDARG; - } - - // Store the incoming width and height - _resultingCapability.width = pvi->bmiHeader.biWidth; - _resultingCapability.height = abs(pvi->bmiHeader.biHeight); - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, - "CheckMediaType width:%d height:%d Compression:0x%x\n", - pvi->bmiHeader.biWidth,pvi->bmiHeader.biHeight, - pvi->bmiHeader.biCompression); - - if(*SubType == MEDIASUBTYPE_I420 - && pvi->bmiHeader.biCompression == MAKEFOURCC('I','4','2','0')) - { - _resultingCapability.rawType = kVideoI420; - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_YUY2 - && pvi->bmiHeader.biCompression == MAKEFOURCC('Y','U','Y','2')) - { - _resultingCapability.rawType = kVideoYUY2; - ::Sleep(60); // workaround for bad driver - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_UYVY - && pvi->bmiHeader.biCompression == MAKEFOURCC('U','Y','V','Y')) - { - _resultingCapability.rawType = kVideoUYVY; - return S_OK; // This format is acceptable. - } - - if(*SubType == MEDIASUBTYPE_HDYC) - { - _resultingCapability.rawType = kVideoUYVY; - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_RGB24 - && pvi->bmiHeader.biCompression == BI_RGB) - { - _resultingCapability.rawType = kVideoRGB24; - return S_OK; // This format is acceptable. - } - } - if(*formatType == FORMAT_VideoInfo2) - { - // VIDEOINFOHEADER2 that has dwInterlaceFlags - VIDEOINFOHEADER2 *pvi = (VIDEOINFOHEADER2 *) pMediaType->Format(); - - if(pvi == NULL) - { - return E_INVALIDARG; - } - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, - "CheckMediaType width:%d height:%d Compression:0x%x\n", - pvi->bmiHeader.biWidth,pvi->bmiHeader.biHeight, - pvi->bmiHeader.biCompression); - - _resultingCapability.width = pvi->bmiHeader.biWidth; - _resultingCapability.height = abs(pvi->bmiHeader.biHeight); - - if(*SubType == MEDIASUBTYPE_I420 - && pvi->bmiHeader.biCompression == MAKEFOURCC('I','4','2','0')) - { - _resultingCapability.rawType = kVideoI420; - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_YUY2 - && pvi->bmiHeader.biCompression == MAKEFOURCC('Y','U','Y','2')) - { - _resultingCapability.rawType = kVideoYUY2; - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_UYVY - && pvi->bmiHeader.biCompression == MAKEFOURCC('U','Y','V','Y')) - { - _resultingCapability.rawType = kVideoUYVY; - return S_OK; // This format is acceptable. - } - - if(*SubType == MEDIASUBTYPE_HDYC) - { - _resultingCapability.rawType = kVideoUYVY; - return S_OK; // This format is acceptable. - } - if(*SubType == MEDIASUBTYPE_RGB24 - && pvi->bmiHeader.biCompression == BI_RGB) - { - _resultingCapability.rawType = kVideoRGB24; - return S_OK; // This format is acceptable. - } - } - return E_INVALIDARG; -} - -HRESULT -CaptureInputPin::Receive ( IN IMediaSample * pIMediaSample ) -{ - HRESULT hr = S_OK; - - ASSERT (m_pFilter); - ASSERT (pIMediaSample); - - // get the thread handle of the delivering thread inc its priority - if( _threadHandle == NULL) - { - HANDLE handle= GetCurrentThread(); - SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST); - _threadHandle = handle; - // See http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx for details on the code - // in this function. Name od article is "Setting a Thread Name (Unmanaged)". - - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = "capture_thread"; - info.dwThreadID = (DWORD)-1; - info.dwFlags = 0; - - __try - { - RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), - (DWORD_PTR*)&info ); - } - __except (EXCEPTION_CONTINUE_EXECUTION) - { - } - - } - - reinterpret_cast (m_pFilter)->LockReceive(); - hr = CBaseInputPin::Receive (pIMediaSample); - - if (SUCCEEDED (hr)) - { - const WebRtc_Word32 length = pIMediaSample->GetActualDataLength(); - - unsigned char* pBuffer = NULL; - if(S_OK != pIMediaSample->GetPointer(&pBuffer)) - { - reinterpret_cast (m_pFilter)->UnlockReceive(); - return S_FALSE; - } - - // NOTE: filter unlocked within Send call - reinterpret_cast (m_pFilter)->ProcessCapturedFrame( - pBuffer,length,_resultingCapability); - } - else - { - reinterpret_cast (m_pFilter)->UnlockReceive(); - } - - return hr; -} - -// called under LockReceive -HRESULT CaptureInputPin::SetMatchingMediaType( - const VideoCaptureCapability& capability) -{ - - _requestedCapability = capability; - _resultingCapability = VideoCaptureCapability(); - return S_OK; -} -// ---------------------------------------------------------------------------- -CaptureSinkFilter::CaptureSinkFilter (IN TCHAR * tszName, - IN LPUNKNOWN punk, - OUT HRESULT * phr, - VideoCaptureExternal& captureObserver, - WebRtc_Word32 moduleId) - : CBaseFilter(tszName,punk,& m_crtFilter,CLSID_SINKFILTER), - m_pInput(NULL), - _captureObserver(captureObserver), - _moduleId(moduleId) -{ - (* phr) = S_OK; - m_pInput = new CaptureInputPin(moduleId,NAME ("VideoCaptureInputPin"), - this, - & m_crtFilter, - phr, L"VideoCapture"); - if (m_pInput == NULL || FAILED (* phr)) - { - (* phr) = FAILED (* phr) ? (* phr) : E_OUTOFMEMORY; - goto cleanup; - } - cleanup : - return; -} - -CaptureSinkFilter::~CaptureSinkFilter() -{ - delete m_pInput; -} - -int CaptureSinkFilter::GetPinCount() -{ - return 1; -} - -CBasePin * -CaptureSinkFilter::GetPin(IN int Index) -{ - CBasePin * pPin; - LockFilter (); - if (Index == 0) - { - pPin = m_pInput; - } - else - { - pPin = NULL; - } - UnlockFilter (); - return pPin; -} - -STDMETHODIMP CaptureSinkFilter::Pause() -{ - HRESULT hr = S_OK; - LockFilter(); - if (m_State == State_Stopped) - { - // change the state, THEN activate the input pin - m_State = State_Paused; - if (m_pInput && m_pInput->IsConnected()) - { - m_pInput->Active(); - } - if (m_pInput && !m_pInput->IsConnected()) - { - m_State = State_Running; - } - } - else if (m_State == State_Running) - { - m_State = State_Paused; - } - UnlockFilter(); - return S_OK; -} - -STDMETHODIMP CaptureSinkFilter::Stop() -{ - LockReceive(); - LockFilter(); - - // set the state - m_State = State_Stopped; - - // inactivate the pins - if (m_pInput) - m_pInput->Inactive(); - - UnlockFilter(); - UnlockReceive(); - return S_OK; -} - -void CaptureSinkFilter::SetFilterGraph(IGraphBuilder* graph) -{ - LockFilter(); - m_pGraph = graph; - UnlockFilter(); -} - -void CaptureSinkFilter::ProcessCapturedFrame(unsigned char* pBuffer, - WebRtc_Word32 length, - const VideoCaptureCapability& frameInfo) -{ - // we have the receiver lock - if (m_State == State_Running) - { - _captureObserver.IncomingFrame(pBuffer, length, frameInfo); - - // trying to hold it since it's only a memcpy - // IMPROVEMENT if this work move critsect - UnlockReceive(); - return; - } - UnlockReceive(); - return; -} - -STDMETHODIMP CaptureSinkFilter::SetMatchingMediaType( - const VideoCaptureCapability& capability) -{ - LockReceive(); - LockFilter(); - HRESULT hr; - if (m_pInput) - { - hr = m_pInput->SetMatchingMediaType(capability); - } - else - { - hr = E_UNEXPECTED; - } - UnlockFilter(); - UnlockReceive(); - return hr; -} - -STDMETHODIMP CaptureSinkFilter::GetClassID( OUT CLSID * pCLSID ) -{ - (* pCLSID) = CLSID_SINKFILTER; - return S_OK; -} - -} // namespace videocapturemodule -} //namespace webrtc diff --git a/modules/video_capture/main/source/Windows/sink_filter_windows.h b/modules/video_capture/main/source/Windows/sink_filter_windows.h deleted file mode 100644 index efeb9c77c..000000000 --- a/modules/video_capture/main/source/Windows/sink_filter_windows.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_WINDOWS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_WINDOWS_H_ - -#include // Include base DS filter header files - -#include "video_capture_defines.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -//forward declaration - -class CaptureSinkFilter; -/** - * input pin for camera input - * - */ -class CaptureInputPin: public CBaseInputPin -{ -public: - WebRtc_Word32 _moduleId; - - VideoCaptureCapability _requestedCapability; - VideoCaptureCapability _resultingCapability; - HANDLE _threadHandle; - - CaptureInputPin ( WebRtc_Word32 moduleId, - IN TCHAR* szName, - IN CaptureSinkFilter* pFilter, - IN CCritSec * pLock, - OUT HRESULT * pHr, - IN LPCWSTR pszName); - virtual ~CaptureInputPin(); - - HRESULT GetMediaType (IN int iPos, OUT CMediaType * pmt); - HRESULT CheckMediaType (IN const CMediaType * pmt); - STDMETHODIMP Receive (IN IMediaSample *); - HRESULT SetMatchingMediaType(const VideoCaptureCapability& capability); -}; - -class CaptureSinkFilter: public CBaseFilter -{ - -public: - CaptureSinkFilter (IN TCHAR * tszName, - IN LPUNKNOWN punk, - OUT HRESULT * phr, - VideoCaptureExternal& captureObserver, - WebRtc_Word32 moduleId); - virtual ~CaptureSinkFilter(); - - // -------------------------------------------------------------------- - // class methods - - void ProcessCapturedFrame(unsigned char* pBuffer, WebRtc_Word32 length, - const VideoCaptureCapability& frameInfo); - // explicit receiver lock aquisition and release - void LockReceive() { m_crtRecv.Lock();} - void UnlockReceive() {m_crtRecv.Unlock();} - // explicit filter lock aquisition and release - void LockFilter() {m_crtFilter.Lock();} - void UnlockFilter() { m_crtFilter.Unlock(); } - void SetFilterGraph(IGraphBuilder* graph); // Used if EVR - - // -------------------------------------------------------------------- - // COM interfaces -DECLARE_IUNKNOWN ; - STDMETHODIMP SetMatchingMediaType(const VideoCaptureCapability& capability); - - // -------------------------------------------------------------------- - // CBaseFilter methods - int GetPinCount (); - CBasePin * GetPin ( IN int Index); - STDMETHODIMP Pause (); - STDMETHODIMP Stop (); - STDMETHODIMP GetClassID ( OUT CLSID * pCLSID); - // -------------------------------------------------------------------- - // class factory calls this - static CUnknown * CreateInstance (IN LPUNKNOWN punk, OUT HRESULT * phr); -private: - CCritSec m_crtFilter; // filter lock - CCritSec m_crtRecv; // receiver lock; always acquire before filter lock - CaptureInputPin * m_pInput; - VideoCaptureExternal& _captureObserver; - WebRtc_Word32 _moduleId; -}; -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_SINK_FILTER_WINDOWS_H_ diff --git a/modules/video_capture/main/source/Windows/video_capture_factory_windows.cc b/modules/video_capture/main/source/Windows/video_capture_factory_windows.cc deleted file mode 100644 index 7e786f660..000000000 --- a/modules/video_capture/main/source/Windows/video_capture_factory_windows.cc +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_capture_windows.h" -#include "trace.h" - -namespace webrtc -{ -VideoCaptureModule* VideoCaptureModule::Create( - const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, "Create %s", - deviceUniqueIdUTF8); - - if (deviceUniqueIdUTF8 == NULL) - { - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, id, - "Create Invalid deviceUniqueIdUTF8"); - return NULL; - } - - WebRtc_UWord8 productId[kVideoCaptureProductIdLength]; - videocapturemodule::DeviceInfoWindows::GetProductId(deviceUniqueIdUTF8, - productId, - sizeof(productId)); - - videocapturemodule::VideoCaptureDS* newCaptureModule = NULL; - newCaptureModule = new videocapturemodule::VideoCaptureDS(id); - - if (!newCaptureModule - || newCaptureModule->Init(id, deviceUniqueIdUTF8) != 0) - { - Destroy(newCaptureModule); - newCaptureModule = NULL; - } - return newCaptureModule; -} -} //namespace webrtc diff --git a/modules/video_capture/main/source/Windows/video_capture_windows.cc b/modules/video_capture/main/source/Windows/video_capture_windows.cc deleted file mode 100644 index 728770839..000000000 --- a/modules/video_capture/main/source/Windows/video_capture_windows.cc +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_capture_windows.h" -#include "trace.h" -#include "sink_filter_windows.h" -#include "help_functions_windows.h" -#include "../video_capture_config.h" -#include "critical_section_wrapper.h" -#include // VIDEOINFOHEADER2 - -namespace webrtc -{ -namespace videocapturemodule -{ -VideoCaptureDS::VideoCaptureDS(const WebRtc_Word32 id) - : VideoCaptureImpl(id), _dsInfo(id), _captureFilter(NULL), - _graphBuilder(NULL), _mediaControl(NULL), _sinkFilter(NULL), - _inputSendPin(NULL), _outputCapturePin(NULL), _mjpgJPGFilter(NULL), - _inputMjpgPin(NULL), _outputMjpgPin(NULL), _dvFilter(NULL), - _inputDvPin(NULL), _outputDvPin(NULL) -{ -} - -VideoCaptureDS::~VideoCaptureDS() -{ - if (_mediaControl) - { - _mediaControl->Stop(); - } - if (_graphBuilder) - { - if (_sinkFilter) - _graphBuilder->RemoveFilter(_sinkFilter); - if (_captureFilter) - _graphBuilder->RemoveFilter(_captureFilter); - if (_mjpgJPGFilter) - _graphBuilder->RemoveFilter(_mjpgJPGFilter); - if (_dvFilter) - _graphBuilder->RemoveFilter(_dvFilter); - } - RELEASE_AND_CLEAR(_captureFilter); // release the capture device - RELEASE_AND_CLEAR(_sinkFilter); - RELEASE_AND_CLEAR(_mjpgJPGFilter); - RELEASE_AND_CLEAR(_dvFilter); - - RELEASE_AND_CLEAR(_mediaControl); - RELEASE_AND_CLEAR(_inputSendPin); - RELEASE_AND_CLEAR(_outputCapturePin); - - RELEASE_AND_CLEAR(_inputMjpgPin); - RELEASE_AND_CLEAR(_outputMjpgPin); - - RELEASE_AND_CLEAR(_inputDvPin); - RELEASE_AND_CLEAR(_outputDvPin); - - RELEASE_AND_CLEAR(_graphBuilder); -} - -WebRtc_Word32 VideoCaptureDS::Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WebRtc_Word32 result = 0; - - const WebRtc_Word32 nameLength = - (WebRtc_Word32) strlen((char*) deviceUniqueIdUTF8); - if (nameLength > kVideoCaptureUniqueNameLength) - return -1; - - // Store the device name - _deviceUniqueId = new (std::nothrow) WebRtc_UWord8[nameLength + 1]; - memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1); - - if (_dsInfo.Init() != 0) - return -1; - - _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8); - if (!_captureFilter) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create capture filter."); - return -1; - } - - // Get the interface for DirectShow's GraphBuilder - HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, - CLSCTX_INPROC_SERVER, IID_IGraphBuilder, - (void **) &_graphBuilder); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create graph builder."); - return -1; - } - - hr = _graphBuilder->QueryInterface(IID_IMediaControl, - (void **) &_mediaControl); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create media control builder."); - return -1; - } - hr = _graphBuilder->AddFilter(_captureFilter, CAPTURE_FILTER_NAME); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to add the capture device to the graph."); - return -1; - } - - _outputCapturePin = GetOutputPin(_captureFilter, PIN_CATEGORY_CAPTURE); - - // Create the sink filte used for receiving Captured frames. - _sinkFilter = new CaptureSinkFilter(SINK_FILTER_NAME, NULL, &hr, - *this, _id); - if (hr != S_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create send filter"); - return -1; - } - _sinkFilter->AddRef(); - - hr = _graphBuilder->AddFilter(_sinkFilter, SINK_FILTER_NAME); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to add the send filter to the graph."); - return -1; - } - _inputSendPin = GetInputPin(_sinkFilter); - - // Create MJPG filter - hr = CoCreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC, IID_IBaseFilter, - (void **) &_mjpgJPGFilter); - if (hr == S_OK) - { - _inputMjpgPin = GetInputPin(_mjpgJPGFilter); - _outputMjpgPin = GetOutputPin(_mjpgJPGFilter); - _graphBuilder->AddFilter(_mjpgJPGFilter, NULL); - } - // Temporary connect here. - // This is done so that no one else can use the capture device. - if (SetCameraOutput(_requestedCapability) != 0) - { - return -1; - } - hr = _mediaControl->Pause(); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to Pause the Capture device. Is it already occupied? %d.", - hr); - return -1; - } - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, _id, - "Capture device '%s' initialized.", deviceUniqueIdUTF8); - return 0; -} - -WebRtc_Word32 VideoCaptureDS::StartCapture( - const VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "StartCapture widht %d, height %d, frameRate %d", - capability.width, capability.height, capability.maxFPS); - CriticalSectionScoped cs(_apiCs); - - if (capability != _requestedCapability) - { - DisconnectGraph(); - - if (SetCameraOutput(capability) != 0) - { - return -1; - } - } - HRESULT hr = _mediaControl->Run(); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to start the Capture device."); - return -1; - } - return 0; -} - -WebRtc_Word32 VideoCaptureDS::StopCapture() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "StopCapture"); - CriticalSectionScoped cs(_apiCs); - - HRESULT hr = _mediaControl->Pause(); - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to stop the capture graph. %d", hr); - return -1; - } - return 0; -} -bool VideoCaptureDS::CaptureStarted() -{ - OAFilterState state = 0; - HRESULT hr = _mediaControl->GetState(1000, &state); - if (hr != S_OK && hr != VFW_S_CANT_CUE) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to get the CaptureStarted status"); - } - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "CaptureStarted %d", state); - return state == State_Running; - -} -WebRtc_Word32 VideoCaptureDS::CaptureSettings( - VideoCaptureCapability& settings) -{ - settings = _requestedCapability; - return 0; -} - -WebRtc_Word32 VideoCaptureDS::SetCameraOutput( - const VideoCaptureCapability& requestedCapability) -{ - - // Get the best matching capability - VideoCaptureCapability capability; - WebRtc_Word32 capabilityIndex; - - // Store the new requested size - _requestedCapability = requestedCapability; - // Match the requested capability with the supported. - if ((capabilityIndex = _dsInfo.GetBestMatchedCapability(_deviceUniqueId, - _requestedCapability, - capability)) < 0) - { - return -1; - } - //Reduce the frame rate if possible. - if (capability.maxFPS > requestedCapability.maxFPS) - { - capability.maxFPS = requestedCapability.maxFPS; - } - // Store the new expected capture delay - _captureDelay = capability.expectedCaptureDelay; - - // Convert it to the windows capability index since they are not nexessary - // the same - VideoCaptureCapabilityWindows windowsCapability; - if (_dsInfo.GetWindowsCapability(capabilityIndex, windowsCapability) != 0) - { - return -1; - } - - IAMStreamConfig* streamConfig = NULL; - AM_MEDIA_TYPE *pmt = NULL; - VIDEO_STREAM_CONFIG_CAPS caps; - - HRESULT hr = _outputCapturePin->QueryInterface(IID_IAMStreamConfig, - (void**) &streamConfig); - if (hr) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Can't get the Capture format settings."); - return -1; - } - - //Get the windows capability from the capture device - bool isDVCamera = false; - hr = streamConfig->GetStreamCaps( - windowsCapability.directShowCapabilityIndex, - &pmt, reinterpret_cast (&caps)); - if (!FAILED(hr)) - { - if (pmt->formattype == FORMAT_VideoInfo2) - { - VIDEOINFOHEADER2* h = - reinterpret_cast (pmt->pbFormat); - if (capability.maxFPS > 0 - && windowsCapability.supportFrameRateControl) - { - h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 - / capability.maxFPS); - } - } - else - { - VIDEOINFOHEADER* h = reinterpret_cast - (pmt->pbFormat); - if (capability.maxFPS > 0 - && windowsCapability.supportFrameRateControl) - { - h->AvgTimePerFrame = REFERENCE_TIME(10000000.0 - / capability.maxFPS); - } - - } - - // Set the sink filter to request this capability - _sinkFilter->SetMatchingMediaType(capability); - //Order the capture device to use this capability - hr += streamConfig->SetFormat(pmt); - - //Check if this is a DV camera and we need to add MS DV Filter - if (pmt->subtype == MEDIASUBTYPE_dvsl - || pmt->subtype == MEDIASUBTYPE_dvsd - || pmt->subtype == MEDIASUBTYPE_dvhd) - isDVCamera = true; // This is a DV camera. Use MS DV filter - } - RELEASE_AND_CLEAR(streamConfig); - - if (FAILED(hr)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to set capture device output format"); - return -1; - } - - if (capability.rawType == kVideoMJPEG && _mjpgJPGFilter) - { - // Connect the camera to the MJPEG decoder - hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputMjpgPin, - NULL); - // Connect the MJPEG filter to the Capture filter - hr += _graphBuilder->ConnectDirect(_outputMjpgPin, _inputSendPin, NULL); - } - else if (isDVCamera) - { - hr = ConnectDVCamera(); - } - else - { - hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputSendPin, - NULL); - } - if (hr != S_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to connect the Capture graph %d", hr); - return -1; - } - return 0; -} - -WebRtc_Word32 VideoCaptureDS::DisconnectGraph() -{ - HRESULT hr = _mediaControl->Stop(); - hr += _graphBuilder->Disconnect(_outputCapturePin); - hr += _graphBuilder->Disconnect(_inputSendPin); - - // If the _mjpg filter exist - if (_mjpgJPGFilter) - { - _graphBuilder->Disconnect(_inputMjpgPin); - _graphBuilder->Disconnect(_outputMjpgPin); - } - - //if the DV camera filter exist - if (_dvFilter) - { - _graphBuilder->Disconnect(_inputDvPin); - _graphBuilder->Disconnect(_outputDvPin); - } - if (hr != S_OK) - { - WEBRTC_TRACE( webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to Stop the Capture device for reconfiguration %d", - hr); - return -1; - } - return 0; -} -HRESULT VideoCaptureDS::ConnectDVCamera() -{ - HRESULT hr = S_OK; - - if (!_dvFilter) - { - hr = CoCreateInstance(CLSID_DVVideoCodec, NULL, CLSCTX_INPROC, - IID_IBaseFilter, (void **) &_dvFilter); - if (hr != S_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to create the dv decoder: %x", hr); - return hr; - } - hr = _graphBuilder->AddFilter(_dvFilter, L"VideoDecoderDV"); - if (hr != S_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to add the dv decoder to the graph: %x", hr); - return hr; - } - _inputDvPin = GetInputPin(_dvFilter); - if (_inputDvPin == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to get input pin from DV decoder"); - return -1; - } - _outputDvPin = GetOutputPin(_dvFilter); - if (_outputDvPin == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to get output pin from DV decoder"); - return -1; - } - } - hr = _graphBuilder->ConnectDirect(_outputCapturePin, _inputDvPin, NULL); - if (hr != S_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to connect capture device to the dv devoder: %x", - hr); - return hr; - } - - hr = _graphBuilder->ConnectDirect(_outputDvPin, _inputSendPin, NULL); - if (hr != S_OK) - { - if (hr == 0x80070004) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to connect the capture device, busy"); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to connect capture device to the send graph: 0x%x", - hr); - } - return hr; - } - return hr; -} -} // namespace videocapturemodule -} //namespace webrtc diff --git a/modules/video_capture/main/source/Windows/video_capture_windows.h b/modules/video_capture/main/source/Windows/video_capture_windows.h deleted file mode 100644 index b10e16e7e..000000000 --- a/modules/video_capture/main/source/Windows/video_capture_windows.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_WINDOWS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_WINDOWS_H_ - -#include "../video_capture_impl.h" -#include - -#include "device_info_windows.h" - -#define CAPTURE_FILTER_NAME L"VideoCaptureFilter" -#define SINK_FILTER_NAME L"SinkFilter" - -namespace webrtc -{ -namespace videocapturemodule -{ -// Forward declaraion -class CaptureSinkFilter; - -class VideoCaptureDS: public VideoCaptureImpl -{ -public: - - static VideoCaptureModule* Create(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - - VideoCaptureDS(const WebRtc_Word32 id); - - virtual WebRtc_Word32 Init(const WebRtc_Word32 id, - const WebRtc_UWord8* deviceUniqueIdUTF8); - - /************************************************************************* - * - * Start/Stop - * - *************************************************************************/ - virtual WebRtc_Word32 - StartCapture(const VideoCaptureCapability& capability); - virtual WebRtc_Word32 StopCapture(); - - /************************************************************************** - * - * Properties of the set device - * - **************************************************************************/ - - virtual bool CaptureStarted(); - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& settings); - -protected: - virtual ~VideoCaptureDS(); - - // Help functions - - WebRtc_Word32 - SetCameraOutput(const VideoCaptureCapability& requestedCapability); - WebRtc_Word32 DisconnectGraph(); - HRESULT VideoCaptureDS::ConnectDVCamera(); - - DeviceInfoWindows _dsInfo; - - IBaseFilter* _captureFilter; - IGraphBuilder* _graphBuilder; - IMediaControl* _mediaControl; - CaptureSinkFilter* _sinkFilter; - IPin* _inputSendPin; - IPin* _outputCapturePin; - - // used when using a MJPEG decoder - IBaseFilter* _mjpgJPGFilter; - IPin* _inputMjpgPin; - IPin* _outputMjpgPin; - - // Microsoft DV interface (external DV cameras) - IBaseFilter* _dvFilter; - IPin* _inputDvPin; - IPin* _outputDvPin; - -}; -} // namespace videocapturemodule -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_VIDEO_CAPTURE_WINDOWS_H_ diff --git a/modules/video_capture/main/source/device_info_impl.cc b/modules/video_capture/main/source/device_info_impl.cc deleted file mode 100644 index 16aba10aa..000000000 --- a/modules/video_capture/main/source/device_info_impl.cc +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "device_info_impl.h" -#include "video_capture_config.h" -#include "trace.h" -#include - -#ifndef abs -#define abs(a) (a>=0?a:-a) -#endif - -namespace webrtc -{ -namespace videocapturemodule -{ -DeviceInfoImpl::DeviceInfoImpl(const WebRtc_Word32 id) - : _id(id), _apiLock(*RWLockWrapper::CreateRWLock()), _lastUsedDeviceName(NULL), - _lastUsedDeviceNameLength(0) -{ -} - -DeviceInfoImpl::~DeviceInfoImpl(void) -{ - _apiLock.AcquireLockExclusive(); - // Reset old capability list - MapItem* item = NULL; - while ((item = _captureCapabilities.Last())) - { - delete (VideoCaptureCapability*) item->GetItem(); - _captureCapabilities.Erase(item); - } - free(_lastUsedDeviceName); - _apiLock.ReleaseLockExclusive(); - - delete &_apiLock; -} -WebRtc_Word32 DeviceInfoImpl::NumberOfCapabilities( - const WebRtc_UWord8* deviceUniqueIdUTF8) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "NumberOfCapabilities, uniqueID %s", deviceUniqueIdUTF8); - - if (!deviceUniqueIdUTF8) - return -1; - - _apiLock.AcquireLockShared(); - - if (_lastUsedDeviceNameLength == strlen((char*) deviceUniqueIdUTF8)) - { - // Is it the same device that is asked for again. -#if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_LINUX) - if(strncasecmp((char*)_lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength)==0) -#else - if (_strnicmp((char*) _lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength) == 0) -#endif - { - //yes - _apiLock.ReleaseLockShared(); - return _captureCapabilities.Size(); - } - } - // Need to get exclusive rights to create the new capability map. - _apiLock.ReleaseLockShared(); - WriteLockScoped cs2(_apiLock); - - WebRtc_Word32 ret = CreateCapabilityMap(deviceUniqueIdUTF8); - return ret; -} - -WebRtc_Word32 DeviceInfoImpl::GetCapability(const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "GetCapability capability number %d", deviceCapabilityNumber); - - if (!deviceUniqueIdUTF8) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "deviceUniqueIdUTF8 parameter not set in call to GetCapability"); - return -1; - } - ReadLockScoped cs(_apiLock); - - if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) -#if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_LINUX) - || (strncasecmp((char*)_lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength)!=0)) -#else - || (_strnicmp((char*) _lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength) != 0)) -#endif - - { - _apiLock.ReleaseLockShared(); - _apiLock.AcquireLockExclusive(); - if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) - { - _apiLock.ReleaseLockExclusive(); - _apiLock.AcquireLockShared(); - return -1; - } - _apiLock.ReleaseLockExclusive(); - _apiLock.AcquireLockShared(); - } - - // Make sure the number is valid - if (deviceCapabilityNumber >= (unsigned int) _captureCapabilities.Size()) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "deviceCapabilityNumber %d is invalid in call to GetCapability", - deviceCapabilityNumber); - return -1; - } - - MapItem* item = _captureCapabilities.Find(deviceCapabilityNumber); - if (!item) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to find capability number %d of %d possible", - deviceCapabilityNumber, _captureCapabilities.Size()); - return -1; - } - - VideoCaptureCapability* capPointer = static_cast - (item->GetItem()); - if (!capPointer) - { - return -1; - } - - capability = *capPointer; - return 0; -} - -WebRtc_Word32 DeviceInfoImpl::GetBestMatchedCapability( - const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting) -{ - - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "GetBestMatchedCapability unique ID %s", deviceUniqueIdUTF8); - - if (!deviceUniqueIdUTF8) - return -1; - - ReadLockScoped cs(_apiLock); - if ((_lastUsedDeviceNameLength != strlen((char*) deviceUniqueIdUTF8)) -#if defined(WEBRTC_MAC_INTEL) || defined(WEBRTC_LINUX) - || (strncasecmp((char*)_lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength)!=0)) -#else - || (_strnicmp((char*) _lastUsedDeviceName, - (char*) deviceUniqueIdUTF8, - _lastUsedDeviceNameLength) != 0)) -#endif - { - _apiLock.ReleaseLockShared(); - _apiLock.AcquireLockExclusive(); - if (-1 == CreateCapabilityMap(deviceUniqueIdUTF8)) - { - return -1; - } - _apiLock.ReleaseLockExclusive(); - _apiLock.AcquireLockShared(); - } - - WebRtc_Word32 bestformatIndex = -1; - WebRtc_Word32 bestWidth = 0; - WebRtc_Word32 bestHeight = 0; - WebRtc_Word32 bestFrameRate = 0; - RawVideoType bestRawType = kVideoUnknown; - webrtc::VideoCodecType bestCodecType = webrtc::kVideoCodecUnknown; - - const WebRtc_Word32 numberOfCapabilies = _captureCapabilities.Size(); - - for (WebRtc_Word32 tmp = 0; tmp < numberOfCapabilies; ++tmp) // Loop through all capabilities - { - MapItem* item = _captureCapabilities.Find(tmp); - if (!item) - return -1; - - VideoCaptureCapability& capability = *static_cast - (item->GetItem()); - - const WebRtc_Word32 diffWidth = capability.width - requested.width; - const WebRtc_Word32 diffHeight = capability.height - requested.height; - const WebRtc_Word32 diffFrameRate = capability.maxFPS - requested.maxFPS; - - const WebRtc_Word32 currentbestDiffWith = bestWidth - requested.width; - const WebRtc_Word32 currentbestDiffHeight = bestHeight - requested.height; - const WebRtc_Word32 currentbestDiffFrameRate = bestFrameRate - requested.maxFPS; - - if ((diffHeight >= 0 && diffHeight <= abs(currentbestDiffHeight)) // Height better or equalt that previouse. - || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight)) - { - - if (diffHeight == currentbestDiffHeight) // Found best height. Care about the width) - { - if ((diffWidth >= 0 && diffWidth <= abs(currentbestDiffWith)) // Width better or equal - || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith)) - { - if (diffWidth == currentbestDiffWith && diffHeight - == currentbestDiffHeight) // Same size as previously - { - //Also check the best frame rate if the diff is the same as previouse - if (((diffFrameRate >= 0 && - diffFrameRate <= currentbestDiffFrameRate) // Frame rate to high but better match than previouse and we have not selected IUV - || - (currentbestDiffFrameRate < 0 && - diffFrameRate >= currentbestDiffFrameRate)) // Current frame rate is lower than requested. This is better. - ) - { - if ((currentbestDiffFrameRate == diffFrameRate) // Same frame rate as previous or frame rate allready good enough - || (currentbestDiffFrameRate >= 0)) - { - if (bestRawType != requested.rawType - && requested.rawType != kVideoUnknown - && (capability.rawType == requested.rawType - || capability.rawType == kVideoI420 - || capability.rawType == kVideoYUY2 - || capability.rawType == kVideoYV12)) - { - bestCodecType = capability.codecType; - bestRawType = capability.rawType; - bestformatIndex = tmp; - } - // If width height and frame rate is full filled we can use the camera for encoding if it is supported. - if (capability.height == requested.height - && capability.width == requested.width - && capability.maxFPS >= requested.maxFPS) - { - if (capability.codecType == requested.codecType - && bestCodecType != requested.codecType) - { - bestCodecType = capability.codecType; - bestformatIndex = tmp; - } - } - } - else // Better frame rate - { - if (requested.codecType == capability.codecType) - { - - bestWidth = capability.width; - bestHeight = capability.height; - bestFrameRate = capability.maxFPS; - bestCodecType = capability.codecType; - bestRawType = capability.rawType; - bestformatIndex = tmp; - } - } - } - } - else // Better width than previously - { - if (requested.codecType == capability.codecType) - { - bestWidth = capability.width; - bestHeight = capability.height; - bestFrameRate = capability.maxFPS; - bestCodecType = capability.codecType; - bestRawType = capability.rawType; - bestformatIndex = tmp; - } - } - }// else width no good - } - else // Better height - { - if (requested.codecType == capability.codecType) - { - bestWidth = capability.width; - bestHeight = capability.height; - bestFrameRate = capability.maxFPS; - bestCodecType = capability.codecType; - bestRawType = capability.rawType; - bestformatIndex = tmp; - } - } - }// else height not good - }//end for - - WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, - "Best camera format: Width %d, Height %d, Frame rate %d, Color format %d", - bestWidth, bestHeight, bestFrameRate, bestRawType); - - // Copy the capability - MapItem* item = _captureCapabilities.Find(bestformatIndex); - if (!item) - return -1; - VideoCaptureCapability* capPointer = - static_cast (item->GetItem()); - if (!capPointer) - return -1; - - resulting = *capPointer; - - return bestformatIndex; -} - -/* Returns the expected Capture delay*/ -WebRtc_Word32 DeviceInfoImpl::GetExpectedCaptureDelay( - const DelayValues delayValues[], - const WebRtc_UWord32 sizeOfDelayValues, - const WebRtc_UWord8* productId, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - WebRtc_Word32 bestDelay = kDefaultCaptureDelay; - - for (WebRtc_UWord32 device = 0; device < sizeOfDelayValues; ++device) - { - if (delayValues[device].productId && strncmp((char*) productId, - (char*) delayValues[device].productId, - kVideoCaptureProductIdLength) == 0) - { - // We have found the camera - - WebRtc_Word32 bestWidth = 0; - WebRtc_Word32 bestHeight = 0; - - //Loop through all tested sizes and find one that seems fitting - for (WebRtc_UWord32 delayIndex = 0; delayIndex < NoOfDelayValues; ++delayIndex) - { - const DelayValue& currentValue = delayValues[device].delayValues[delayIndex]; - - const WebRtc_Word32 diffWidth = currentValue.width - width; - const WebRtc_Word32 diffHeight = currentValue.height - height; - - const WebRtc_Word32 currentbestDiffWith = bestWidth - width; - const WebRtc_Word32 currentbestDiffHeight = bestHeight - height; - - if ((diffHeight >= 0 && diffHeight <= abs(currentbestDiffHeight)) // Height better or equal than previous. - || (currentbestDiffHeight < 0 && diffHeight >= currentbestDiffHeight)) - { - - if (diffHeight == currentbestDiffHeight) // Found best height. Care about the width) - { - if ((diffWidth >= 0 && diffWidth <= abs(currentbestDiffWith)) // Width better or equal - || (currentbestDiffWith < 0 && diffWidth >= currentbestDiffWith)) - { - if (diffWidth == currentbestDiffWith && diffHeight - == currentbestDiffHeight) // Same size as previous - { - } - else // Better width than previously - { - bestWidth = currentValue.width; - bestHeight = currentValue.height; - bestDelay = currentValue.delay; - } - }// else width no good - } - else // Better height - { - bestWidth = currentValue.width; - bestHeight = currentValue.height; - bestDelay = currentValue.delay; - } - }// else height not good - }//end for - break; - } - } - if (bestDelay > kMaxCaptureDelay) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Expected capture delay too high. %dms, will use %d", bestDelay, - kMaxCaptureDelay); - bestDelay = kMaxCaptureDelay; - - } - - return bestDelay; - -} - -//Default implementation. This should be overridden by Mobile implementations. -WebRtc_Word32 DeviceInfoImpl::GetOrientation(const WebRtc_UWord8* deviceUniqueIdUTF8, - VideoCaptureRotation& orientation) -{ - orientation = kCameraRotate0; - return -1; -} -} //namespace videocapturemodule -} // namespace webrtc - - diff --git a/modules/video_capture/main/source/device_info_impl.h b/modules/video_capture/main/source/device_info_impl.h deleted file mode 100644 index c682ee83d..000000000 --- a/modules/video_capture/main/source/device_info_impl.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_DEVICE_INFO_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_DEVICE_INFO_IMPL_H_ - -#include "video_capture.h" - -#include "map_wrapper.h" -#include "rw_lock_wrapper.h" -#include "video_capture_delay.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -class DeviceInfoImpl: public VideoCaptureModule::DeviceInfo -{ -public: - DeviceInfoImpl(const WebRtc_Word32 id); - virtual ~DeviceInfoImpl(void); - virtual WebRtc_Word32 NumberOfCapabilities(const WebRtc_UWord8* deviceUniqueIdUTF8); - virtual WebRtc_Word32 GetCapability(const WebRtc_UWord8* deviceUniqueIdUTF8, - const WebRtc_UWord32 deviceCapabilityNumber, - VideoCaptureCapability& capability); - - virtual WebRtc_Word32 GetBestMatchedCapability(const WebRtc_UWord8*deviceUniqueIdUTF8, - const VideoCaptureCapability requested, - VideoCaptureCapability& resulting); - virtual WebRtc_Word32 GetOrientation(const WebRtc_UWord8* deviceUniqueIdUTF8, - VideoCaptureRotation& orientation); - -protected: - /* Initialize this object*/ - - virtual WebRtc_Word32 Init()=0; - /* - * Fills the member variable _captureCapabilities with capabilities for the given device name. - */ - virtual WebRtc_Word32 CreateCapabilityMap(const WebRtc_UWord8* deviceUniqueIdUTF8)=0; - - /* Returns the expected Capture delay*/ - WebRtc_Word32 GetExpectedCaptureDelay(const DelayValues delayValues[], - const WebRtc_UWord32 sizeOfDelayValues, - const WebRtc_UWord8* productId, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height); -protected: - // Data members - WebRtc_Word32 _id; - MapWrapper _captureCapabilities; - RWLockWrapper& _apiLock; - WebRtc_UWord8* _lastUsedDeviceName; - WebRtc_UWord32 _lastUsedDeviceNameLength; -}; -} //namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_DEVICE_INFO_IMPL_H_ diff --git a/modules/video_capture/main/source/video_capture.gyp b/modules/video_capture/main/source/video_capture.gyp deleted file mode 100644 index 7558c57ee..000000000 --- a/modules/video_capture/main/source/video_capture.gyp +++ /dev/null @@ -1,265 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'video_capture_module', - 'type': '<(library)', - 'dependencies': [ - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - '../../../../common_video/vplib/main/interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../interface', - '../../../../common_video/vplib/main/interface', - ], - }, - 'sources': [ - # interfaces - '../interface/video_capture.h', - '../interface/video_capture_defines.h', - # headers - 'video_capture_config.h', - 'video_capture_delay.h', - 'video_capture_impl.h', - 'vplib_conversions.h', - 'device_info_impl.h', - - # DEFINE PLATFORM INDEPENDENT SOURCE FILES - 'video_capture_impl.cc', - 'vplib_conversions.cc', - 'device_info_impl.cc', - ], - 'conditions': [ - # DEFINE PLATFORM SPECIFIC SOURCE FILES - ['OS=="linux" and build_with_chromium==0', { - 'include_dirs': [ - 'Linux', - ], - 'sources': [ - 'Linux/device_info_linux.h', - 'Linux/video_capture_linux.h', - 'Linux/device_info_linux.cc', - 'Linux/video_capture_linux.cc', - ], - }], - ['OS=="mac" and build_with_chromium==0', { - 'sources': [ - 'Mac/video_capture_mac.h', - 'Mac/QTKit/video_capture_recursive_lock.h', - 'Mac/QTKit/video_capture_qtkit.h', - 'Mac/QTKit/video_capture_qtkit_info.h', - 'Mac/QTKit/video_capture_qtkit_info_objc.h', - 'Mac/QTKit/video_capture_qtkit_objc.h', - 'Mac/QTKit/video_capture_qtkit_utility.h', - 'Mac/video_capture_mac.cc', - 'Mac/QTKit/video_capture_qtkit.cc', - 'Mac/QTKit/video_capture_qtkit_objc.mm', - 'Mac/QTKit/video_capture_recursive_lock.mm', - 'Mac/QTKit/video_capture_qtkit_info.cc', - 'Mac/QTKit/video_capture_qtkit_info_objc.mm', - ], - 'include_dirs': [ - 'Mac', - ], - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': '-x objective-c++', - }, - 'link_settings': { - 'xcode_settings': { - 'OTHER_LDFLAGS': [ - '-framework QTKit', - ], - }, - }, - }], - ['OS=="win" and build_with_chromium==0', { - 'include_dirs': [ - 'Windows', - '<(direct_show_base_classes)', - ], - 'defines!': [ - 'NOMINMAX', - ], - 'sources': [ - 'Windows/help_functions_windows.h', - 'Windows/sink_filter_windows.h', - 'Windows/video_capture_windows.h', - 'Windows/device_info_windows.h', - 'Windows/capture_delay_values_windows.h', - 'Windows/help_functions_windows.cc', - 'Windows/sink_filter_windows.cc', - 'Windows/video_capture_windows.cc', - 'Windows/device_info_windows.cc', - 'Windows/video_capture_factory_windows.cc', - '<(direct_show_base_classes)amextra.cpp', - '<(direct_show_base_classes)amextra.h', - '<(direct_show_base_classes)amfilter.cpp', - '<(direct_show_base_classes)amfilter.h', - '<(direct_show_base_classes)amvideo.cpp', - '<(direct_show_base_classes)arithutil.cpp', - '<(direct_show_base_classes)cache.h', - '<(direct_show_base_classes)checkbmi.h', - '<(direct_show_base_classes)combase.cpp', - '<(direct_show_base_classes)combase.h', - '<(direct_show_base_classes)cprop.cpp', - '<(direct_show_base_classes)cprop.h', - '<(direct_show_base_classes)ctlutil.cpp', - '<(direct_show_base_classes)ctlutil.h', - '<(direct_show_base_classes)ddmm.cpp', - '<(direct_show_base_classes)ddmm.h', - '<(direct_show_base_classes)dllentry.cpp', - '<(direct_show_base_classes)dllsetup.cpp', - '<(direct_show_base_classes)dllsetup.h', - '<(direct_show_base_classes)dxmperf.h', - '<(direct_show_base_classes)fourcc.h', - '<(direct_show_base_classes)measure.h', - '<(direct_show_base_classes)msgthrd.h', - '<(direct_show_base_classes)mtype.cpp', - '<(direct_show_base_classes)mtype.h', - '<(direct_show_base_classes)outputq.cpp', - '<(direct_show_base_classes)outputq.h', - '<(direct_show_base_classes)perflog.cpp', - '<(direct_show_base_classes)perflog.h', - '<(direct_show_base_classes)perfstruct.h', - '<(direct_show_base_classes)pstream.cpp', - '<(direct_show_base_classes)pstream.h', - '<(direct_show_base_classes)pullpin.cpp', - '<(direct_show_base_classes)pullpin.h', - '<(direct_show_base_classes)refclock.cpp', - '<(direct_show_base_classes)refclock.h', - '<(direct_show_base_classes)reftime.h', - '<(direct_show_base_classes)renbase.cpp', - '<(direct_show_base_classes)renbase.h', - '<(direct_show_base_classes)schedule.cpp', - '<(direct_show_base_classes)schedule.h', - '<(direct_show_base_classes)seekpt.cpp', - '<(direct_show_base_classes)seekpt.h', - '<(direct_show_base_classes)source.cpp', - '<(direct_show_base_classes)source.h', - '<(direct_show_base_classes)streams.h', - '<(direct_show_base_classes)strmctl.cpp', - '<(direct_show_base_classes)strmctl.h', - '<(direct_show_base_classes)sysclock.cpp', - '<(direct_show_base_classes)sysclock.h', - '<(direct_show_base_classes)transfrm.cpp', - '<(direct_show_base_classes)transfrm.h', - '<(direct_show_base_classes)transip.cpp', - '<(direct_show_base_classes)transip.h', - '<(direct_show_base_classes)videoctl.cpp', - '<(direct_show_base_classes)videoctl.h', - '<(direct_show_base_classes)vtrans.cpp', - '<(direct_show_base_classes)vtrans.h', - '<(direct_show_base_classes)winctrl.cpp', - '<(direct_show_base_classes)winctrl.h', - '<(direct_show_base_classes)winutil.cpp', - '<(direct_show_base_classes)winutil.h', - '<(direct_show_base_classes)wxdebug.cpp', - '<(direct_show_base_classes)wxdebug.h', - '<(direct_show_base_classes)wxlist.cpp', - '<(direct_show_base_classes)wxlist.h', - '<(direct_show_base_classes)wxutil.cpp', - '<(direct_show_base_classes)wxutil.h', - ], - 'msvs_settings': { - 'VCLibrarianTool': { - 'AdditionalDependencies': 'Strmiids.lib', - }, - }, - }], - ], # conditions - - }, - { - 'target_name': 'video_capture_module_test', - 'type': 'executable', - 'dependencies': [ - 'video_capture_module', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - '../../../video_render/main/source/video_render.gyp:video_render_module', - '../../../video_coding/main/source/video_coding.gyp:webrtc_video_coding', - ], - 'include_dirs': [ - '../interface', - ], - 'sources': [ - # sources - '../test/testAPI/cocoa_renderer.h', - '../test/testAPI/cocoa_renderer.mm', - '../test/testAPI/testDefines.h', - '../test/testAPI/testAPI.cpp', - '../test/testAPI/testCameraEncoder.cpp', - '../test/testAPI/testCameraEncoder.h', - '../test/testAPI/testExternalCapture.cpp', - '../test/testAPI/testExternalCapture.h', - '../test/testAPI/testPlatformDependent.cpp', - '../test/testAPI/testPlatformDependent.h', - '../test/testAPI/Logger.h', - '../test/testAPI/Logger.cpp', - '../test/testAPI/Renderer.h', - '../test/testAPI/Renderer.cpp', - ], # source - 'conditions': [ - ['build_with_chromium!=0', { - 'sources!': [ - '../test/testAPI/testCameraEncoder.cpp', - '../test/testAPI/testCameraEncoder.h', - '../test/testAPI/testPlatformDependent.cpp', - '../test/testAPI/testPlatformDependent.h', - ], - }], - # DEFINE PLATFORM SPECIFIC SOURCE FILES - ['OS!="mac"', { - 'sources!': [ - '../test/testAPI/cocoa_renderer.h', - '../test/testAPI/cocoa_renderer.mm', - ], - }], - # DEFINE PLATFORM SPECIFIC INCLUDE AND CFLAGS - ['OS=="mac" or OS=="linux"', { - 'cflags': [ - '-Wno-write-strings', - ], - 'ldflags': [ - '-lpthread -lm', - ], - }], - ['OS=="linux"', { - 'libraries': [ - '-lrt', - '-lXext', - '-lX11', - ], - }], - ['OS=="mac"', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': '-x objective-c++', - 'OTHER_LDFLAGS': [ - '-framework Foundation -framework AppKit -framework Cocoa -framework OpenGL -framework CoreVideo -framework CoreAudio -framework AudioToolbox', - ], - }, - }], - ] # conditions - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_capture/main/source/video_capture_config.h b/modules/video_capture/main/source/video_capture_config.h deleted file mode 100644 index c0d1c5b28..000000000 --- a/modules/video_capture/main/source/video_capture_config.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_CONFIG_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_CONFIG_H_ - -namespace webrtc -{ -namespace videocapturemodule -{ -enum {kDefaultWidth = 640}; // Start width -enum {kDefaultHeight = 480}; // Start heigt -enum {kDefaultFrameRate = 30}; // Start frame rate - -enum {kMaxFrameRate =60}; // Max allowed frame rate of the start image - -enum {kDefaultCaptureDelay = 120}; -enum {kMaxCaptureDelay = 270}; // Max capture delay allowed in the precompiled capture delay values. - -enum {kProcessInterval = 300}; -enum {kFrameRateCallbackInterval = 1000}; -enum {kFrameRateCountHistorySize = 90}; -enum {kFrameRateHistoryWindowMs = 2000}; -} //namespace videocapturemodule -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_CONFIG_H_ diff --git a/modules/video_capture/main/source/video_capture_delay.h b/modules/video_capture/main/source/video_capture_delay.h deleted file mode 100644 index 9f5b76ef3..000000000 --- a/modules/video_capture/main/source/video_capture_delay.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_DELAY_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_DELAY_H_ - -namespace webrtc -{ -namespace videocapturemodule -{ - -struct DelayValue -{ - WebRtc_Word32 width; - WebRtc_Word32 height; - WebRtc_Word32 delay; -}; - -enum { NoOfDelayValues = 40 }; -struct DelayValues -{ - char * deviceName; - char* productId; - DelayValue delayValues[NoOfDelayValues]; -}; - -} //namespace videocapturemodule -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_DELAY_H_ diff --git a/modules/video_capture/main/source/video_capture_impl.cc b/modules/video_capture/main/source/video_capture_impl.cc deleted file mode 100644 index b33e9137f..000000000 --- a/modules/video_capture/main/source/video_capture_impl.cc +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_capture_impl.h" -#include "trace.h" -#include "critical_section_wrapper.h" -#include "tick_util.h" -#include "vplib_conversions.h" -#include "video_capture_config.h" -#include "module_common_types.h" - -#ifdef ANDROID -#include "video_capture_android.h" // Need inclusion here to set Java environment. -#endif - -namespace webrtc -{ - -VideoCaptureModule* VideoCaptureModule::Create(const WebRtc_Word32 id, - VideoCaptureExternal*& externalCapture) -{ - videocapturemodule::VideoCaptureImpl* implementation = - new videocapturemodule::VideoCaptureImpl(id); - externalCapture = implementation; - return implementation; -} - - -void VideoCaptureModule::Destroy(VideoCaptureModule* module) -{ - delete module; -} - -#ifdef ANDROID -WebRtc_Word32 VideoCaptureModule::SetAndroidObjects(void* javaVM,void* javaContext) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, 0, "SetAndroidObjects"); - return videocapturemodule::VideoCaptureAndroid::SetAndroidObjects(javaVM,javaContext); -} -#endif - -namespace videocapturemodule -{ - -WebRtc_Word32 VideoCaptureImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "Version(bufferLength:%u)", - (unsigned int) remainingBufferInBytes); - return GetVersion(version, remainingBufferInBytes, position); -} - -WebRtc_Word32 VideoCaptureImpl::GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - if (version == NULL) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1, - "Invalid in argument to Version()"); - return -1; - } - WebRtc_Word8 ourVersion[] = "VideoCaptureModule 1.1.0"; - WebRtc_UWord32 ourLength = (WebRtc_UWord32) strlen(ourVersion); - if (remainingBufferInBytes < ourLength + 1) - { - return -1; - } - memcpy(version, ourVersion, ourLength); - version[ourLength] = '\0'; // null terminaion - remainingBufferInBytes -= (ourLength + 1); - position += (ourLength + 1); - return 0; -} - -const WebRtc_UWord8* VideoCaptureImpl::CurrentDeviceName() const -{ - return _deviceUniqueId; -} - -WebRtc_Word32 VideoCaptureImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -// returns the number of milliseconds until the module want a worker thread to call Process -WebRtc_Word32 VideoCaptureImpl::TimeUntilNextProcess() -{ - TickTime timeNow = TickTime::Now(); - - WebRtc_Word32 timeToNormalProcess = kProcessInterval - - (WebRtc_Word32)((TickTime::Now() - _lastProcessTime).Milliseconds()); - WebRtc_Word32 timeToStartImage = timeToNormalProcess; - if (_startImageFrameIntervall) - { - timeToStartImage = _startImageFrameIntervall - - (WebRtc_Word32)((timeNow - _lastSentStartImageTime).Milliseconds()); - if (timeToStartImage < 0) - { - timeToStartImage = 0; - } - } - return (timeToStartImage < timeToNormalProcess) - ? timeToStartImage : timeToNormalProcess; -} - -// Process any pending tasks such as timeouts -WebRtc_Word32 VideoCaptureImpl::Process() -{ - CriticalSectionScoped cs(_callBackCs); - - const TickTime now = TickTime::Now(); - _lastProcessTime = TickTime::Now(); - - // Handle No picture alarm - - if (_lastProcessFrameCount.Ticks() == _incomingFrameTimes[0].Ticks() && - _captureAlarm != Raised) - { - if (_noPictureAlarmCallBack && _captureCallBack) - { - _captureAlarm = Raised; - _captureCallBack->OnNoPictureAlarm(_id, _captureAlarm); - } - } - else if (_lastProcessFrameCount.Ticks() != _incomingFrameTimes[0].Ticks() && - _captureAlarm != Cleared) - { - if (_noPictureAlarmCallBack && _captureCallBack) - { - _captureAlarm = Cleared; - _captureCallBack->OnNoPictureAlarm(_id, _captureAlarm); - - } - } - - // Handle frame rate callback - if ((now - _lastFrameRateCallbackTime).Milliseconds() - > kFrameRateCallbackInterval) - { - if (_frameRateCallBack && _captureCallBack) - { - const WebRtc_UWord32 frameRate = CalculateFrameRate(now); - _captureCallBack->OnCaptureFrameRate(_id, frameRate); - } - _lastFrameRateCallbackTime = now; // Can be set by EnableFrameRateCallback - - } - - _lastProcessFrameCount = _incomingFrameTimes[0]; - - // Handle start image frame rates. - if (_startImageFrameIntervall - && (now - _lastSentStartImageTime).Milliseconds() >= _startImageFrameIntervall) - { - _lastSentStartImageTime = now; - if (_dataCallBack) - { - _captureFrame.CopyFrame(_startImage); - _captureFrame.SetRenderTime(TickTime::MillisecondTimestamp()); - _dataCallBack->OnIncomingCapturedFrame(_id, _captureFrame, - kVideoCodecUnknown); - } - } - return 0; -} - -VideoCaptureImpl::VideoCaptureImpl(const WebRtc_Word32 id) - : _id(id), _deviceUniqueId(NULL), _apiCs(*CriticalSectionWrapper::CreateCriticalSection()), - _captureDelay(0), _requestedCapability(), - _callBackCs(*CriticalSectionWrapper::CreateCriticalSection()), - _lastProcessTime(TickTime::Now()), - _lastFrameRateCallbackTime(TickTime::Now()), _frameRateCallBack(false), - _noPictureAlarmCallBack(false), _captureAlarm(Cleared), _setCaptureDelay(0), - _dataCallBack(NULL), _captureCallBack(NULL), _startImageFrameIntervall(0), - _lastProcessFrameCount(TickTime::Now()), _rotateFrame(kRotateNone) - -{ - _requestedCapability.width = kDefaultWidth; - _requestedCapability.height = kDefaultHeight; - _requestedCapability.maxFPS = 30; - _requestedCapability.rawType = kVideoI420; - _requestedCapability.codecType = kVideoCodecUnknown; - memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes)); -} - -VideoCaptureImpl::~VideoCaptureImpl() -{ - DeRegisterCaptureDataCallback(); - DeRegisterCaptureCallback(); - delete &_callBackCs; - delete &_apiCs; - - if (_deviceUniqueId) - delete[] _deviceUniqueId; -} - -WebRtc_Word32 VideoCaptureImpl::RegisterCaptureDataCallback( - VideoCaptureDataCallback& dataCallBack) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "RegisterCaptureDataCallback"); - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _dataCallBack = &dataCallBack; - - return 0; -} - -WebRtc_Word32 VideoCaptureImpl::DeRegisterCaptureDataCallback() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "DeRegisterCaptureDataCallback"); - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _dataCallBack = NULL; - return 0; -} -WebRtc_Word32 VideoCaptureImpl::RegisterCaptureCallback(VideoCaptureFeedBack& callBack) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "RegisterCaptureCallback %x"); - - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _captureCallBack = &callBack; - return 0; -} -WebRtc_Word32 VideoCaptureImpl::DeRegisterCaptureCallback() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "DeRegisterCaptureCallback"); - - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _captureCallBack = NULL; - return 0; - -} -WebRtc_Word32 VideoCaptureImpl::SetCaptureDelay(WebRtc_Word32 delayMS) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "SetCaptureDelay %d", - (int) delayMS); - CriticalSectionScoped cs(_apiCs); - _captureDelay = delayMS; - return 0; -} -WebRtc_Word32 VideoCaptureImpl::CaptureDelay() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, "CaptureDelay %d", - (int) _captureDelay); - CriticalSectionScoped cs(_apiCs); - return _setCaptureDelay; -} -WebRtc_Word32 VideoCaptureImpl::IncomingFrame(WebRtc_UWord8* videoFrame, - WebRtc_Word32 videoFrameLength, - const VideoCaptureCapability& frameInfo, - WebRtc_Word64 captureTime/*=0*/) -{ - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCapture, _id, - "IncomingFrame width %d, height %d", (int) frameInfo.width, - (int) frameInfo.height); - - TickTime startProcessTime = TickTime::Now(); - - CriticalSectionScoped cs(_callBackCs); - - const WebRtc_Word32 width = frameInfo.width; - const WebRtc_Word32 height = frameInfo.height; - - UpdateFrameCount();// frame count used for local frame rate callback. - - _startImageFrameIntervall = 0; // prevent the start image to be displayed. - - if (frameInfo.codecType == kVideoCodecUnknown) // None encoded. Convert to I420. - { - const VideoType vpLibType =RawVideoTypeToVplibVideoType(frameInfo.rawType); - int size = CalcBufferSize(vpLibType, width, height); - if (size != videoFrameLength) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Wrong incoming frame length."); - return -1; - } - - // Allocate I420 buffer - _captureFrame.VerifyAndAllocate(CalcBufferSize(kI420, width, height)); - if (!_captureFrame.Buffer()) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to allocate frame buffer."); - return -1; - } - - memset(_captureFrame.Buffer(), 0, _captureFrame.Size()); - const WebRtc_Word32 conversionResult = ConvertToI420(vpLibType, videoFrame, - width, height, - _captureFrame.Buffer(), - _requestedCapability.interlaced, - _rotateFrame); - if (conversionResult <= 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to convert capture frame from type %d to I420", - frameInfo.rawType); - return -1; - } - _captureFrame.SetLength(conversionResult); - } - else // Encoded format - { - if (_captureFrame.CopyFrame(videoFrameLength, videoFrame) != 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "Failed to copy captured frame of length %d", (int) videoFrameLength); - } - } - - const bool callOnCaptureDelayChanged = _setCaptureDelay != _captureDelay; - if (_setCaptureDelay != _captureDelay) // Capture delay changed - { - _setCaptureDelay = _captureDelay; - } - - // Set the capture time - if (captureTime != 0) - { - _captureFrame.SetRenderTime(captureTime); - } - else - { - _captureFrame.SetRenderTime(TickTime::MillisecondTimestamp()); - } - - _captureFrame.SetHeight(height); - _captureFrame.SetWidth(width); - - if (_dataCallBack) - { - if (callOnCaptureDelayChanged) - { - _dataCallBack->OnCaptureDelayChanged(_id, _captureDelay); - } - _dataCallBack->OnIncomingCapturedFrame(_id, _captureFrame, frameInfo.codecType); - } - - const WebRtc_UWord32 processTime = - (WebRtc_UWord32)(TickTime::Now() - startProcessTime).Milliseconds(); - if (processTime > 10) // If the process time is too long MJPG will not work well. - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id, - "Too long processing time of Incoming frame: %ums", - (unsigned int) processTime); - } - - return 0; - -} - -WebRtc_Word32 VideoCaptureImpl::SetCaptureRotation(VideoCaptureRotation rotation) -{ - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - switch (rotation) - { - case kCameraRotate0: - _rotateFrame = kRotateNone; - break; - case kCameraRotate90: - _rotateFrame = kRotateClockwise; - break; - case kCameraRotate180: - _rotateFrame = kRotate180; - break; - case kCameraRotate270: - _rotateFrame = kRotateAntiClockwise; - break; - } - return 0; -} - -WebRtc_Word32 VideoCaptureImpl::StartSendImage(const VideoFrame& videoFrame, - WebRtc_Word32 frameRate) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCapture, _id, - "StartSendImage, frameRate %d", (int) frameRate); - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - if (frameRate < 1 || frameRate > kMaxFrameRate) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, - "StartSendImage Invalid parameter. frameRate %d", (int) frameRate); - return -1;; - } - _startImage.CopyFrame(videoFrame); - _startImageFrameIntervall = 1000 / frameRate; - _lastSentStartImageTime = TickTime::Now(); - return 0; - -} -WebRtc_Word32 VideoCaptureImpl::StopSendImage() -{ - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _startImageFrameIntervall = 0; - return 0; -} - -WebRtc_Word32 VideoCaptureImpl::EnableFrameRateCallback(const bool enable) -{ - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _frameRateCallBack = enable; - if (enable) - { - _lastFrameRateCallbackTime = TickTime::Now(); - } - return 0; -} - -WebRtc_Word32 VideoCaptureImpl::EnableNoPictureAlarm(const bool enable) -{ - CriticalSectionScoped cs(_apiCs); - CriticalSectionScoped cs2(_callBackCs); - _noPictureAlarmCallBack = enable; - return 0; -} - -void VideoCaptureImpl::UpdateFrameCount() -{ - if (_incomingFrameTimes[0].MicrosecondTimestamp() == 0) - { - // first no shift - } - else - { - // shift - for (int i = (kFrameRateCountHistorySize - 2); i >= 0; i--) - { - _incomingFrameTimes[i + 1] = _incomingFrameTimes[i]; - } - } - _incomingFrameTimes[0] = TickTime::Now(); -} - -WebRtc_UWord32 VideoCaptureImpl::CalculateFrameRate(const TickTime& now) -{ - WebRtc_Word32 num = 0; - WebRtc_Word32 nrOfFrames = 0; - for (num = 1; num < (kFrameRateCountHistorySize - 1); num++) - { - if (_incomingFrameTimes[num].Ticks() <= 0 - || (now - _incomingFrameTimes[num]).Milliseconds() > kFrameRateHistoryWindowMs) // don't use data older than 2sec - { - break; - } - else - { - nrOfFrames++; - } - } - if (num > 1) - { - WebRtc_Word64 diff = (now - _incomingFrameTimes[num - 1]).Milliseconds(); - if (diff > 0) - { - return WebRtc_UWord32((nrOfFrames * 1000.0f / diff) + 0.5f); - } - } - - return nrOfFrames; -} -} //namespace videocapturemodule -} // namespace webrtc diff --git a/modules/video_capture/main/source/video_capture_impl.h b/modules/video_capture/main/source/video_capture_impl.h deleted file mode 100644 index 6f91e7d25..000000000 --- a/modules/video_capture/main/source/video_capture_impl.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_IMPL_H_ - -/* - * video_capture_impl.h - */ - -#include "video_capture.h" -#include "video_capture_config.h" -#include "tick_util.h" -#include "vplib.h" - -namespace webrtc -{ -class CriticalSectionWrapper; - -namespace videocapturemodule -{ -// Class definitions -class VideoCaptureImpl: public VideoCaptureModule, public VideoCaptureExternal -{ -public: - - VideoCaptureImpl(const WebRtc_Word32 id); - virtual ~VideoCaptureImpl(); - - static VideoCaptureModule* Create(const WebRtc_Word32 id, - VideoCaptureExternal*& externalCapture); - - static void Destroy(VideoCaptureModule* module); - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - // Implements Module declared functions. - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - //Call backs - virtual WebRtc_Word32 RegisterCaptureDataCallback(VideoCaptureDataCallback& dataCallback); - virtual WebRtc_Word32 DeRegisterCaptureDataCallback(); - virtual WebRtc_Word32 RegisterCaptureCallback(VideoCaptureFeedBack& callBack); - virtual WebRtc_Word32 DeRegisterCaptureCallback(); - - virtual WebRtc_Word32 StartSendImage(const VideoFrame& videoFrame, - WebRtc_Word32 frameRate = 1); - virtual WebRtc_Word32 StopSendImage(); - - virtual WebRtc_Word32 SetCaptureDelay(WebRtc_Word32 delayMS); - virtual WebRtc_Word32 CaptureDelay(); - virtual WebRtc_Word32 SetCaptureRotation(VideoCaptureRotation rotation); - - virtual WebRtc_Word32 EnableFrameRateCallback(const bool enable); - virtual WebRtc_Word32 EnableNoPictureAlarm(const bool enable); - - virtual const WebRtc_UWord8* CurrentDeviceName() const; - - // Module handling - virtual WebRtc_Word32 TimeUntilNextProcess(); - virtual WebRtc_Word32 Process(); - - // Implement VideoCaptureExternal - virtual WebRtc_Word32 IncomingFrame(WebRtc_UWord8* videoFrame, - WebRtc_Word32 videoFrameLength, - const VideoCaptureCapability& frameInfo, - WebRtc_Word64 captureTime = 0); - // Platform dependent - virtual WebRtc_Word32 StartCapture(const VideoCaptureCapability& capability) - { - _requestedCapability = capability; - return -1; - } - virtual WebRtc_Word32 StopCapture() { return -1; } - virtual bool CaptureStarted() {return false; } - virtual WebRtc_Word32 CaptureSettings(VideoCaptureCapability& /*settings*/) - { return -1; } - VideoCaptureEncodeInterface* GetEncodeInterface(const VideoCodec& /*codec*/) - { return NULL; } - -protected: - WebRtc_Word32 _id; // Module ID - WebRtc_UWord8* _deviceUniqueId; // current Device unique name; - CriticalSectionWrapper& _apiCs; - WebRtc_Word32 _captureDelay; // Current capture delay. May be changed of platform dependent parts. - VideoCaptureCapability _requestedCapability; // Should be set by platform dependent code in StartCapture. -private: - void UpdateFrameCount(); - WebRtc_UWord32 CalculateFrameRate(const TickTime& now); - - CriticalSectionWrapper& _callBackCs; - - TickTime _lastProcessTime; // last time the module process function was called. - TickTime _lastFrameRateCallbackTime; // last time the frame rate callback function was called. - bool _frameRateCallBack; // true if EnableFrameRateCallback - bool _noPictureAlarmCallBack; //true if EnableNoPictureAlarm - VideoCaptureAlarm _captureAlarm; // current value of the noPictureAlarm - - WebRtc_Word32 _setCaptureDelay; // The currently used capture delay - VideoCaptureDataCallback* _dataCallBack; - VideoCaptureFeedBack* _captureCallBack; - - VideoFrame _startImage; - WebRtc_Word32 _startImageFrameIntervall; - TickTime _lastSentStartImageTime; // last time the start image was sent - TickTime _lastProcessFrameCount; - TickTime _incomingFrameTimes[kFrameRateCountHistorySize];// timestamp for local captured frames - VideoRotationMode _rotateFrame; //Set if the frame should be rotated by the capture module. - - VideoFrame _captureFrame; -}; -} //namespace videocapturemodule -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VIDEO_CAPTURE_IMPL_H_ diff --git a/modules/video_capture/main/source/vplib_conversions.cc b/modules/video_capture/main/source/vplib_conversions.cc deleted file mode 100644 index 1ecc4341f..000000000 --- a/modules/video_capture/main/source/vplib_conversions.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "vplib_conversions.h" - -namespace webrtc -{ -namespace videocapturemodule -{ -VideoType RawVideoTypeToVplibVideoType(RawVideoType type) -{ - switch (type) - { - case kVideoI420: - return kI420; - case kVideoIYUV: - return kIYUV; - case kVideoRGB24: - return kRGB24; - case kVideoARGB: - return kARGB; - case kVideoARGB4444: - return kARGB4444; - case kVideoRGB565: - return kRGB565; - case kVideoARGB1555: - return kARGB1555; - case kVideoYUY2: - return kYUY2; - case kVideoYV12: - return kYV12; - case kVideoUYVY: - return kUYVY; - case kVideoNV21: - return kNV21; - case kVideoNV12: - return kNV12; - default: - assert(!"RawVideoTypeToVplibVideoType unknown type"); - } - return kUnknown; -} -} //namespace videocapturemodule -}//namespace webrtc diff --git a/modules/video_capture/main/source/vplib_conversions.h b/modules/video_capture/main/source/vplib_conversions.h deleted file mode 100644 index e3b1f0113..000000000 --- a/modules/video_capture/main/source/vplib_conversions.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VPLIB_CONVERSIONS_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VPLIB_CONVERSIONS_H_ - -#include "video_capture.h" -#include "vplib.h" - -namespace webrtc -{ -namespace videocapturemodule -{ - VideoType RawVideoTypeToVplibVideoType(RawVideoType type); -} // namespace videocapturemodule -} // namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_VPLIB_CONVERSIONS_H_ - diff --git a/modules/video_capture/main/test/Android/.classpath b/modules/video_capture/main/test/Android/.classpath deleted file mode 100644 index 841ac513c..000000000 --- a/modules/video_capture/main/test/Android/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/modules/video_capture/main/test/Android/.project b/modules/video_capture/main/test/Android/.project deleted file mode 100644 index 373da0e22..000000000 --- a/modules/video_capture/main/test/Android/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - VideoCaptureModuleAndroidTest - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/modules/video_capture/main/test/Android/AndroidManifest.xml b/modules/video_capture/main/test/Android/AndroidManifest.xml deleted file mode 100644 index 87c48cbb7..000000000 --- a/modules/video_capture/main/test/Android/AndroidManifest.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/modules/video_capture/main/test/Android/default.properties b/modules/video_capture/main/test/Android/default.properties deleted file mode 100644 index 2ad44a49f..000000000 --- a/modules/video_capture/main/test/Android/default.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-9 diff --git a/modules/video_capture/main/test/Android/gen/org/webrtc/capturemoduleandroidtest/R.java b/modules/video_capture/main/test/Android/gen/org/webrtc/capturemoduleandroidtest/R.java deleted file mode 100644 index 94bf93b3d..000000000 --- a/modules/video_capture/main/test/Android/gen/org/webrtc/capturemoduleandroidtest/R.java +++ /dev/null @@ -1,33 +0,0 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package org.webrtc.capturemoduleandroidtest; - -public final class R { - public static final class attr { - } - public static final class drawable { - public static final int icon=0x7f020000; - } - public static final class id { - public static final int Button01=0x7f050000; - public static final int Button02=0x7f050001; - public static final int Button03=0x7f050002; - public static final int Button04=0x7f050003; - public static final int renderView=0x7f050004; - } - public static final class layout { - public static final int main=0x7f030000; - } - public static final class string { - public static final int app_name=0x7f040000; - public static final int run_button=0x7f040001; - public static final int run_button2=0x7f040002; - public static final int run_button3=0x7f040003; - public static final int run_button4=0x7f040004; - } -} diff --git a/modules/video_capture/main/test/Android/jni/Android.mk b/modules/video_capture/main/test/Android/jni/Android.mk deleted file mode 100644 index 7eba756aa..000000000 --- a/modules/video_capture/main/test/Android/jni/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -# Android makefile for VideoCapture Module - -LOCAL_PATH := $(call my-dir) - -WEBRTC_INTERFACES_PATH := $(LOCAL_PATH)/../../../../../../../build/interface -WEBRTC_LIBS_PATH := $(LOCAL_PATH)/../../../../../../../build/libraries - -include $(CLEAR_VARS) - -LOCAL_MODULE := VideoCaptureModuleAndroidTestJniAPI -LOCAL_SRC_FILES := video_capture_module_android_test_jni.cc -LOCAL_CFLAGS := -DWEBRTC_TARGET_PC # For typedefs.h -LOCAL_C_INCLUDES := $(WEBRTC_INTERFACES_PATH) -LOCAL_LDLIBS := \ - $(WEBRTC_LIBS_PATH)/testVideoCaptureAPI_android_gcc.a \ - $(WEBRTC_LIBS_PATH)/VideoCaptureModuleTestApiLib_android_gcc.a \ - -llog -lgcc -lGLESv2 -include $(BUILD_SHARED_LIBRARY) - diff --git a/modules/video_capture/main/test/Android/jni/org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest.h b/modules/video_capture/main/test/Android/jni/org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest.h deleted file mode 100644 index 0320df066..000000000 --- a/modules/video_capture/main/test/Android/jni/org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest */ - -#ifndef _Included_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest -#define _Included_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest - * Method: RunTest - * Signature: (Landroid/content/Context;)I - */ -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_RunTest - (JNIEnv *, jobject, jobject); - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_RenderInit -(JNIEnv * env, jobject context,jobject surface); - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_StartCapture -(JNIEnv *, jobject); - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_StopCapture -(JNIEnv *, jobject); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/modules/video_capture/main/test/Android/jni/video_capture_module_android_test_jni.cc b/modules/video_capture/main/test/Android/jni/video_capture_module_android_test_jni.cc deleted file mode 100644 index a7f3c42cc..000000000 --- a/modules/video_capture/main/test/Android/jni/video_capture_module_android_test_jni.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include // memset -#include - -#include "org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest.h" -#include "../../../interface/video_capture.h" -#include "../../../../../video_render/main/interface/video_render.h" -#include "../../testAPI/testPlatformDependent.h" -#include "../../testAPI/testPlatformDependent.h" -#ifdef RENDER_PREVIEW -#include "../../testAPI/Renderer.h" -#endif - -using namespace webrtc; -#define WEBRTC_LOG_TAG "*WEBRTCN*" // As in WEBRTC Native... -// ADM data struct -typedef struct -{ - // Other - JavaVM* jvm; - Renderer* renderer; - VideoCaptureModule* _videoCapture; - VideoCaptureModule::DeviceInfo*_captureInfo; -} JniData; - -// Global variables visible in this file -static JniData jniData; - -////////////////////////////////////////////////////////////////// -// General functions -////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////// -// JNI_OnLoad -// -jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) -{ - __android_log_write(ANDROID_LOG_DEBUG, WEBRTC_LOG_TAG, "JNI_OnLoad"); - if (!vm) - { - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "JNI_OnLoad did not receive a valid VM pointer"); - return -1; - } - - // Get JNI - JNIEnv* env; - if (JNI_OK != vm->GetEnv(reinterpret_cast (&env), JNI_VERSION_1_4)) - { - __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG, - "JNI_OnLoad could not get JNI env"); - return -1; - } - - // Init JniData data - memset(&jniData, 0, sizeof(jniData)); - - // Store the JVM - jniData.jvm = vm; - - return JNI_VERSION_1_4; -} - -///////////////////////////////////////////// -// Run Test -// -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_RunTest( - JNIEnv * env, - jobject context, - jobject surface) -{ - __android_log_write(ANDROID_LOG_DEBUG, WEBRTC_LOG_TAG, "Run test"); - // Set instance independent Java objects - VideoCaptureModule::SetAndroidObjects(jniData.jvm, context); - - // Start test - __android_log_write(ANDROID_LOG_DEBUG, WEBRTC_LOG_TAG, - "Create testPlatformDependent"); - testPlatformDependent testPlatformDependent; - testPlatformDependent.SetRenderer(jniData.renderer); - testPlatformDependent.DoTest(); - - // Clear instance independent Java objects - VideoCaptureModule::SetAndroidObjects(NULL, NULL); - - return 0; -} - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_RenderInit( - JNIEnv * env, - jobject context, - jobject surface) -{ - VideoRender::SetAndroidObjects(jniData.jvm); -#ifdef RENDER_PREVIEW - Renderer::SetRenderWindow(surface); - jniData.renderer=new Renderer(true); -#endif - -} - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_StartCapture( - JNIEnv * env, - jobject context) -{ - if (!jniData._captureInfo) - { - VideoCaptureModule::SetAndroidObjects(jniData.jvm, context); - jniData._captureInfo = VideoCaptureModule::CreateDeviceInfo(5); - WebRtc_UWord8 id[256]; - WebRtc_UWord8 name[256]; - jniData._captureInfo->GetDeviceName(0, name, 256, id, 256); - jniData._videoCapture = VideoCaptureModule::Create(0, id); - VideoCaptureCapability capability; - - jniData._captureInfo->GetCapability(id, 0, capability); - capability.width = 176; - capability.height = 144; - capability.maxFPS = 15; - - jniData._videoCapture->StartCapture(capability); - } - return 0; - -} - -JNIEXPORT jint JNICALL Java_org_webrtc_capturemoduleandroidtest_VideoCaptureModuleTest_StopCapture( - JNIEnv * env, - jobject context) -{ - if (jniData._videoCapture) - { - jniData._videoCapture->StopCapture(); - VideoCaptureModule::DestroyDeviceInfo(jniData._captureInfo); - VideoCaptureModule::Destroy(jniData._videoCapture); - jniData._videoCapture = NULL; - jniData._captureInfo = NULL; - } - return 0; - -} diff --git a/modules/video_capture/main/test/Android/res/drawable-hdpi/icon.png b/modules/video_capture/main/test/Android/res/drawable-hdpi/icon.png deleted file mode 100644 index 8074c4c571b8cd19e27f4ee5545df367420686d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4147 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 diff --git a/modules/video_capture/main/test/Android/res/drawable-ldpi/icon.png b/modules/video_capture/main/test/Android/res/drawable-ldpi/icon.png deleted file mode 100644 index 1095584ec21f71cd0afc9e0993aa2209671b590c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1723 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 - - - - - - - - - diff --git a/modules/video_capture/main/test/Android/res/values/strings.xml b/modules/video_capture/main/test/Android/res/values/strings.xml deleted file mode 100644 index bd9a75272..000000000 --- a/modules/video_capture/main/test/Android/res/values/strings.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - VideoCaptureModuleAndroidTest -Run Test - -Run Java test -Start c++ Capture -Stop c++ Capture - - diff --git a/modules/video_capture/main/test/Android/src/org/webrtc/capturemoduleandroidtest/VideoCaptureJavaTest.java b/modules/video_capture/main/test/Android/src/org/webrtc/capturemoduleandroidtest/VideoCaptureJavaTest.java deleted file mode 100644 index 61347a344..000000000 --- a/modules/video_capture/main/test/Android/src/org/webrtc/capturemoduleandroidtest/VideoCaptureJavaTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.webrtc.capturemoduleandroidtest; - -import java.util.List; - -import android.content.Context; -import android.util.Log; - -import org.webrtc.videoengine.CaptureCapabilityAndroid; -import org.webrtc.videoengine.VideoCaptureAndroid; -import org.webrtc.videoengine.VideoCaptureDeviceInfoAndroid; - -public class VideoCaptureJavaTest { - void DoTest(Context context) - { - VideoCaptureDeviceInfoAndroid videoCaptureDeviceInfo=VideoCaptureDeviceInfoAndroid.CreateVideoCaptureDeviceInfoAndroid(5,context); - for(int i=0; i for Func tests - RunTest(_view); - } - }; - - - @Override - protected void onStart() - { - super.onStart(); - } - @Override - protected void onRestart() - { - super.onRestart(); - } - - @Override - protected void onPause() - { - super.onPause(); - } - - @Override - protected void onStop() - { - super.onStop(); - } - - // Function used to call test - private native int RunTest(Object view); - private native int RenderInit(Object view); - - private native int StartCapture(); - private native int StopCapture(); - - - static { - Log.d("*WEBRTC*", "Loading ModuleVideoCaptureModuleAndroidTest..."); - System.loadLibrary("ModuleVideoCaptureModuleAndroidTestJniAPI"); - } - - public void onClick(View v) { - LinearLayout renderLayout=(LinearLayout) findViewById(R.id.renderView); //get the handle to the layout - switch(v.getId()) - { - case R.id.Button01: - renderLayout.removeAllViews(); - _view=ViERenderer.CreateLocalRenderer(this); - if(_useOpenGL==1) - { - _view= ViERenderer.CreateRenderer(this, true); - } - else - { - _view= new SurfaceView(this); - } - renderLayout.addView(_view); // add the surfaceview to the layout, the surfaceview will be the same size as the layout (container) - RenderInit(_view); - _testThread = new Thread(_testProc); - _testThread.start(); - break; - case R.id.Button02: - _view=ViERenderer.CreateLocalRenderer(this); - renderLayout.removeAllViews(); - if(_videoCaptureJavaTest==null) - { - _videoCaptureJavaTest=new VideoCaptureJavaTest(); - _videoCaptureJavaTest.StartCapture(this); - renderLayout.addView(_view); // add the surfaceview to the layout, the surfaceview will be the same size as the layout (container) - } - else - { - _videoCaptureJavaTest.StopCapture(); - _videoCaptureJavaTest=null; - } - break; - - case R.id.Button03: - _view=ViERenderer.CreateLocalRenderer(this); - renderLayout.removeAllViews(); - StartCapture(); - renderLayout.addView(_view); // add the surfaceview to the layout, the surfaceview will be the same size as the layout (container) - break; - case R.id.Button04: - renderLayout.removeAllViews(); - StopCapture(); - break; - } - } -} \ No newline at end of file diff --git a/modules/video_capture/main/test/testAPI/Logger.cpp b/modules/video_capture/main/test/testAPI/Logger.cpp deleted file mode 100644 index 8f32ec40c..000000000 --- a/modules/video_capture/main/test/testAPI/Logger.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "Logger.h" -#include "string.h" -#include "file_wrapper.h" - -namespace webrtc -{ - -#ifdef _WIN32 -#pragma warning(disable : 4996) -#endif -Logger::Logger() : - _logFile(*FileWrapper::Create()) -{ -} - -Logger::~Logger(void) -{ - if (_logFile.Open()) - _logFile.CloseFile(); -} -void Logger::Print(char* msg) -{ - printf(msg); - if (_logFile.Open()) - { - _logFile.WriteText(msg); - } -} -#define BUFSIZE 256 - -void Logger::SetFileName(const char* fileName) -{ - _logFile.CloseFile(); - if (!fileName) - return; - _logFile.OpenFile(fileName, false, false, true); - char osVersion[BUFSIZE]; - memset(osVersion, 0, sizeof(osVersion)); - - GetOSDisplayString(osVersion); - _logFile.WriteText(osVersion); - _logFile.WriteText("\n\n"); -} - -#ifdef _WIN32 -#include -#include -#include -#include - -typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); -typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); - -bool Logger::GetOSDisplayString( void* psz) -{ - OSVERSIONINFOEX osvi; - SYSTEM_INFO si; - PGNSI pGNSI; - PGPI pGPI; - BOOL bOsVersionInfoEx; - DWORD dwType; - STRSAFE_LPWSTR pszOS = (STRSAFE_LPWSTR) psz; - size_t bufferSize = BUFSIZE/sizeof(TCHAR); - - ZeroMemory(&si, sizeof(SYSTEM_INFO)); - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) - return 1; - - // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. - - pGNSI = (PGNSI) GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), - "GetNativeSystemInfo"); - if(NULL != pGNSI) - pGNSI(&si); - else GetSystemInfo(&si); - - if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && - osvi.dwMajorVersion > 4 ) - { - StringCchCopy(pszOS, bufferSize, TEXT("Microsoft ")); - - // Test for the specific product. - - if ( osvi.dwMajorVersion == 6 ) - { - if( osvi.dwMinorVersion == 0 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, bufferSize, TEXT("Windows Vista ")); - else StringCchCat(pszOS, bufferSize, TEXT("Windows Server 2008 " )); - } - - if ( osvi.dwMinorVersion == 1 ) - { - if( osvi.wProductType == VER_NT_WORKSTATION ) - StringCchCat(pszOS, bufferSize, TEXT("Windows 7 ")); - else StringCchCat(pszOS, bufferSize, TEXT("Windows Server 2008 R2 " )); - } - - pGPI = (PGPI) GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), - "GetProductInfo"); - - pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - - switch( dwType ) - { - case PRODUCT_ULTIMATE: - StringCchCat(pszOS, bufferSize, TEXT("Ultimate Edition" )); - break; - // case PRODUCT_PROFESSIONAL: - // StringCchCat(pszOS, bufferSize, TEXT("Professional" )); - break; - case PRODUCT_HOME_PREMIUM: - StringCchCat(pszOS, bufferSize, TEXT("Home Premium Edition" )); - break; - case PRODUCT_HOME_BASIC: - StringCchCat(pszOS, bufferSize, TEXT("Home Basic Edition" )); - break; - case PRODUCT_ENTERPRISE: - StringCchCat(pszOS, bufferSize, TEXT("Enterprise Edition" )); - break; - case PRODUCT_BUSINESS: - StringCchCat(pszOS, bufferSize, TEXT("Business Edition" )); - break; - case PRODUCT_STARTER: - StringCchCat(pszOS, bufferSize, TEXT("Starter Edition" )); - break; - case PRODUCT_CLUSTER_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Cluster Server Edition" )); - break; - case PRODUCT_DATACENTER_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Datacenter Edition" )); - break; - case PRODUCT_DATACENTER_SERVER_CORE: - StringCchCat(pszOS, bufferSize, TEXT("Datacenter Edition (core installation)" )); - break; - case PRODUCT_ENTERPRISE_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Enterprise Edition" )); - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - StringCchCat(pszOS, bufferSize, TEXT("Enterprise Edition (core installation)" )); - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - StringCchCat(pszOS, bufferSize, TEXT("Enterprise Edition for Itanium-based Systems" )); - break; - case PRODUCT_SMALLBUSINESS_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Small Business Server" )); - break; - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - StringCchCat(pszOS, bufferSize, TEXT("Small Business Server Premium Edition" )); - break; - case PRODUCT_STANDARD_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Standard Edition" )); - break; - case PRODUCT_STANDARD_SERVER_CORE: - StringCchCat(pszOS, bufferSize, TEXT("Standard Edition (core installation)" )); - break; - case PRODUCT_WEB_SERVER: - StringCchCat(pszOS, bufferSize, TEXT("Web Server Edition" )); - break; - } - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) - { - if( GetSystemMetrics(SM_SERVERR2) ) - StringCchCat(pszOS, bufferSize, TEXT( "Windows Server 2003 R2, ")); - else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) - StringCchCat(pszOS, bufferSize, TEXT( "Windows Storage Server 2003")); - //else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) - - // StringCchCat(pszOS, bufferSize, TEXT( "Windows Home Server")); - - else if( osvi.wProductType == VER_NT_WORKSTATION && - si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) - { - StringCchCat(pszOS, bufferSize, TEXT( "Windows XP Professional x64 Edition")); - } - else StringCchCat(pszOS, bufferSize, TEXT("Windows Server 2003, ")); - - // Test for the server type. - if ( osvi.wProductType != VER_NT_WORKSTATION ) - { - if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, bufferSize, TEXT( "Datacenter Edition for Itanium-based Systems" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, bufferSize, TEXT( "Enterprise Edition for Itanium-based Systems" )); - } - - else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, bufferSize, TEXT( "Datacenter x64 Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, bufferSize, TEXT( "Enterprise x64 Edition" )); - else StringCchCat(pszOS, bufferSize, TEXT( "Standard x64 Edition" )); - } - - else - { - if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) - StringCchCat(pszOS, bufferSize, TEXT( "Compute Cluster Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, bufferSize, TEXT( "Datacenter Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, bufferSize, TEXT( "Enterprise Edition" )); - else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) - StringCchCat(pszOS, bufferSize, TEXT( "Web Edition" )); - else StringCchCat(pszOS, bufferSize, TEXT( "Standard Edition" )); - } - } - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) - { - StringCchCat(pszOS, bufferSize, TEXT("Windows XP ")); - if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) - StringCchCat(pszOS, bufferSize, TEXT( "Home Edition" )); - else StringCchCat(pszOS, bufferSize, TEXT( "Professional" )); - } - - if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) - { - StringCchCat(pszOS, bufferSize, TEXT("Windows 2000 ")); - - if ( osvi.wProductType == VER_NT_WORKSTATION ) - { - StringCchCat(pszOS, bufferSize, TEXT( "Professional" )); - } - else - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - StringCchCat(pszOS, bufferSize, TEXT( "Datacenter Server" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - StringCchCat(pszOS, bufferSize, TEXT( "Advanced Server" )); - else StringCchCat(pszOS, bufferSize, TEXT( "Server" )); - } - } - - // Include service pack (if any) and build number. - - if( _tcslen(osvi.szCSDVersion) > 0 ) - { - StringCchCat(pszOS, bufferSize, TEXT(" ") ); - StringCchCat(pszOS, bufferSize, osvi.szCSDVersion); - } - - TCHAR buf[80]; - - StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); - StringCchCat(pszOS, bufferSize, buf); - - if ( osvi.dwMajorVersion >= 6 ) - { - if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) - StringCchCat(pszOS, bufferSize, TEXT( ", 64-bit" )); - else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) - StringCchCat(pszOS, bufferSize, TEXT(", 32-bit")); - } - StringCchPrintf( buf, 80, TEXT(" (number of processors %d)"), si.dwNumberOfProcessors); - StringCchCat(pszOS, bufferSize, buf); - - return TRUE; - } - - else - { - printf( "This sample does not support this version of Windows.\n"); - return FALSE; - } -} - -#elif defined(WEBRTC_MAC_INTEL) -bool Logger::GetOSDisplayString(void* psz) -{} - -#elif defined(WEBRTC_LINUX) - -bool Logger::GetOSDisplayString(void* /*psz*/) -{ return true;} - -#endif -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/Logger.h b/modules/video_capture/main/test/testAPI/Logger.h deleted file mode 100644 index efbe2bf2b..000000000 --- a/modules/video_capture/main/test/testAPI/Logger.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#pragma once - -#include - -namespace webrtc -{ -class FileWrapper; -class Logger -{ -public: - Logger(void); - ~Logger(void); - void SetFileName(const char* fileName); - void Print(char* msg); -private: - static bool GetOSDisplayString(void* psz); - FileWrapper& _logFile; -}; -} //namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/Renderer.cpp b/modules/video_capture/main/test/testAPI/Renderer.cpp deleted file mode 100644 index a009357e7..000000000 --- a/modules/video_capture/main/test/testAPI/Renderer.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "Renderer.h" -#include -#include "thread_wrapper.h" -#include "tick_util.h" - -#if defined _WIN32 -#include -#endif - -namespace webrtc -{ -#if defined _WIN32 -#define SLEEP_10_SEC ::Sleep(10000) -#define GET_TIME_IN_MS timeGetTime - -LRESULT CALLBACK WinProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) -{ - switch(uMsg) - { - case WM_DESTROY: - break; - case WM_COMMAND: - break; - } - return DefWindowProc(hWnd,uMsg,wParam,lParam); -} - -int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height) -{ - HINSTANCE hinst = GetModuleHandle(0); - WNDCLASSEX wcx; - wcx.hInstance = hinst; - wcx.lpszClassName = _T(" test camera delay"); - wcx.lpfnWndProc = (WNDPROC)WinProc; - wcx.style = CS_DBLCLKS; - wcx.hIcon = LoadIcon (NULL, IDI_APPLICATION); - wcx.hIconSm = LoadIcon (NULL, IDI_APPLICATION); - wcx.hCursor = LoadCursor (NULL, IDC_ARROW); - wcx.lpszMenuName = NULL; - wcx.cbSize = sizeof (WNDCLASSEX); - wcx.cbClsExtra = 0; - wcx.cbWndExtra = 0; - wcx.hbrBackground = GetSysColorBrush(COLOR_3DFACE); - - // Register our window class with the operating system. - // If there is an error, exit program. - if ( !RegisterClassEx (&wcx) ) - { - //MessageBox( 0, TEXT("Failed to register window class!"),TEXT("Error!"), MB_OK|MB_ICONERROR ) ; - //return 0; - } - - // Create the main window. - hwndMain = CreateWindowEx( - 0, // no extended styles - wcx.lpszClassName, // class name - _T("Test Camera Delay"), // window name - WS_OVERLAPPED |WS_THICKFRAME, // overlapped window - 0, // horizontal position - 0, // vertical position - width, // width - height, // height - (HWND) NULL, // no parent or owner window - (HMENU) NULL, // class menu used - hinst, // instance handle - NULL); // no window creation data - - if (!hwndMain) - { - int error = GetLastError(); - return -1; - } - - // Show the window using the flag specified by the program - // that started the application, and send the application - // a WM_PAINT message. - - ShowWindow(hwndMain, SW_SHOWDEFAULT); - UpdateWindow(hwndMain); - return 0; -} - -void SetWindowPos(HWND &hwndMain, int x, int y, int width, int height, bool onTop) -{ - /* if(onTop) - { - SetWindowPos(hwndMain,HWND_TOPMOST,x,y,width,height,0); - } - else*/ - { - SetWindowPos(hwndMain,HWND_TOP,x,y,width,height,0); - } - -} - -#elif defined(WEBRTC_MAC_INTEL) -static int _screen = 0; -int WebRtcCreateWindow(CocoaRenderer*& cocoaRenderer,int winNum, int width, int height) -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init]; - - _screen = winNum = 0; - - // In Cocoa, rendering is not done directly to a window like in Windows and Linux. - // It is rendererd to a Subclass of NSOpenGLView - - // create cocoa container window - NSRect outWindowFrame = NSMakeRect(200, 800, width + 20, height + 20); - - NSArray* screens = [NSScreen screens]; - if(_screen >= [screens count]) - { - // requesting screen - return -1; - } - NSScreen* screen = (NSScreen*)[screens objectAtIndex:_screen]; - - NSWindow* outWindow = [[NSWindow alloc] initWithContentRect:outWindowFrame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO screen:screen]; - [outWindow orderOut:nil]; - [outWindow setTitle:@"Cocoa Renderer"]; - [outWindow setBackgroundColor:[NSColor blueColor]]; - [[outWindow contentView] setAutoresizesSubviews:YES]; - - // // ***** TODO: test screen positioning ***** - // // set to the appropriate screen - // NSArray* screens = [NSScreen screens]; - // printf("TODO: test positioning to correct screen\n"); - // switch(winNum){ - // case 0: - // if([screens count] >= 1){ - // [outWindow constrainFrameRect:outWindowFrame toScreen:(NSScreen*)[screens objectAtIndex:0]]; - // } - // break; - // case 1: - // if([screens count] >= 2){ - // [outWindow constrainFrameRect:outWindowFrame toScreen:(NSScreen*)[screens objectAtIndex:1]]; - // } - // break; - // case 2: - // if([screens count] >= 3){ - // [outWindow constrainFrameRect:outWindowFrame toScreen:(NSScreen*)[screens objectAtIndex:2]]; - // } - // break; - // case 3: - // if([screens count] >= 4){ - // [outWindow constrainFrameRect:outWindowFrame toScreen:(NSScreen*)[screens objectAtIndex:3]]; - // } - // break; - // default: - // break; - // - // }// - - // create renderer and attach to window - NSRect cocoaRendererFrame = NSMakeRect(10, 10, width, height); - cocoaRenderer = [[CocoaRenderer alloc] initWithFrame:cocoaRendererFrame]; - [[outWindow contentView] addSubview:cocoaRenderer]; - - // must tell Cocoa to draw the window, but any GUI work must be done on the main thread. - [outWindow performSelector:@selector(display) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES]; - [outWindow makeKeyAndOrderFront:NSApp]; - - [pool release]; - return 0; - -} - -void SetWindowPos(CocoaRenderer*& cocoaRenderer, int x, int y, int width, int height, bool onTop) -{ - NSWindow* ownerWindow = [cocoaRenderer window]; - NSRect ownerNewRect = NSMakeRect(x, y, width, height); - [ownerWindow setFrame:ownerNewRect display:YES]; - - //NSRect cocoaRendererNewRect = {0, 0, 500, 500}; - //NSArray* screens = [NSScreen screens]; - //NSScreen* screen = (NSScreen*)[screens objectAtIndex:_screen]; - //cocoaRendererNewRect = [cocoaRenderer constrainFrameRect:cocoaRendererNewRect toScreen:screen]; - - //[cocoaRenderer setFrame:cocoaRendererNewRect]; - //[cocoaRenderer performSelector:@selector(display) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES]; - - - //[cocoaRenderer performSelector:@selector(drawRect) onThread:[NSThread mainThread] withObject:cocoaRendererNewRect waitUntilDone:YES]; - - - // *** Setting onTop is toooo on top. Can't get to anything underneath. - // [ownerWindow setLevel:NSNormalWindowLevel]; - // if(YES == onTop){ - // [ownerWindow setLevel:NSScreenSaverWindowLevel]; - // } - // else{ - // [ownerWindow setLevel:NSNormalWindowLevel]; - // } -} -#elif defined(ANDROID) -#define nil NULL -#define NO false -jobject Renderer::g_renderWindow=NULL; - -int WebRtcCreateWindow(jobject &renderWin,int /*winNum*/, int /*width*/, int /*height*/) -{ - renderWin=Renderer::g_renderWindow; - return 0; -} -void SetWindowPos(void *& /*hwndMain*/, int /*x*/, int /*y*/, int /*width*/, int /*height*/, bool /*onTop*/) -{ - -} - -void Renderer::SetRenderWindow(jobject renderWindow) -{ - __android_log_print(ANDROID_LOG_DEBUG, "VideoCaptureModule -testAPI", "Renderer::SetRenderWindow"); - g_renderWindow=renderWindow; -} - -#elif defined(WEBRTC_LINUX) - -int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height) -{ -} -void SetWindowPos(HWND &hwndMain, int x, int y, int width, int height, bool onTop) -{ -} -#endif - -Renderer::Renderer(bool preview) : - _renderModule(NULL), _quiting(false), _renderWindow(NULL) - -{ -#ifndef _WIN32 - if (-1 == WebRtcCreateWindow(_renderWindow, 0, 352, 288)) - { - printf("ERROR** INVALID SCREEN\n"); - } -#endif // In Windows the thread running the message loop needs to create the window. - _messageThread = ThreadWrapper::CreateThread(RenderThread, this, - kLowPriority, "RenderThread"); - unsigned int threadId; - _messageThread->Start(threadId); - while (!_renderWindow) - { - SLEEP(10); - }// Wait until messageThread has created the window - - - _renderModule = VideoRender::CreateVideoRender(0, (void*) _renderWindow, - false); - -#if defined(WEBRTC_MAC_INTEL) - _pool = [[NSAutoreleasePool alloc] init]; -#endif - - _renderProvider = _renderModule->AddIncomingRenderStream(0, 0, 0.0f, 0.0f, - 1.0f, 1.0f); - - WebRtc_UWord32 width; - WebRtc_UWord32 height; - - _renderModule->GetScreenResolution(width, height); -#ifdef _WIN32 - // GetScreenResolution is currently not implemented - RECT screenRect; - GetWindowRect(GetDesktopWindow(), &screenRect); - width=screenRect.right; - height=screenRect.bottom; -#endif - - if (!preview) - { -#if defined(_WIN32) - SetWindowPos(_renderWindow,0,height/2,width,height/2,true); -#elif defined(WEBRTC_MAC_INTEL) - SetWindowPos(_renderWindow, 0, height, width, height, true); -#elif defined(WEBRTC_LINUX) - -#endif - - _videoFrame.VerifyAndAllocate(_frameWidth * _frameHeight * 3 / 2); - _videoFrame.SetHeight(_frameHeight); - _videoFrame.SetWidth(_frameWidth); - _videoFrame.SetLength(_videoFrame.Size()); - - memset(_videoFrame.Buffer(), 0, _videoFrame.Size()); - } - else // Preview window - { - -#if defined(_WIN32) - SetWindowPos(_renderWindow,width/2,0,width/2,height/2,false); -#elif defined(WEBRTC_MAC_INTEL) - SetWindowPos(_renderWindow, 0, height, width, height, false); -#elif defined(WEBRTC_LINUX) - -#endif - } - - _renderModule->StartRender(0); - -} - -Renderer::~Renderer(void) -{ - VideoRender::DestroyVideoRender(_renderModule); - _quiting = true; - while (_renderWindow) - { - SLEEP(20); - } - _messageThread->Stop(); - -#if defined(WEBRTC_MAC_INTEL) - [_pool release]; -#endif - delete _messageThread; - -} - -bool Renderer::RenderThread(ThreadObj obj) -{ - return static_cast (obj)->RenderThreadProcess(); -} - -bool Renderer::RenderThreadProcess() -{ - - if (_quiting == false && _renderWindow == NULL) // Create the render window - { - WebRtcCreateWindow(_renderWindow, 0, 352, 288); - } - -#ifdef _WIN32 - MSG msg; - if(PeekMessage(&msg, NULL, 0, 0,PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -#endif - if (_quiting == true) - { -#if defined _WIN32 - ::DestroyWindow(_renderWindow); -#endif - _renderWindow = NULL; - } - - SLEEP(50); - return true; - -} - -void Renderer::PaintGreen() -{ - - _videoFrame.VerifyAndAllocate(_frameWidth * _frameHeight * 3 / 2); - _videoFrame.SetHeight(_frameHeight); - _videoFrame.SetWidth(_frameWidth); - _videoFrame.SetLength(_videoFrame.Size()); - - memset(_videoFrame.Buffer(), 127, _videoFrame.Size()); - memset(_videoFrame.Buffer() + _videoFrame.Width() * _videoFrame.Height(), - 0, _videoFrame.Width() * _videoFrame.Height() / 2); - - _videoFrame.SetRenderTime(TickTime::MillisecondTimestamp()); - _renderProvider->RenderFrame(0,_videoFrame); - -} - -void Renderer::RenderFrame(VideoFrame& videoFrame) -{ - _renderProvider->RenderFrame(0, videoFrame); -} - -void Renderer::PaintBlue() -{ - - _videoFrame.VerifyAndAllocate(_frameWidth * _frameHeight * 3 / 2); - _videoFrame.SetHeight(_frameHeight); - _videoFrame.SetWidth(_frameWidth); - _videoFrame.SetLength(_videoFrame.Size()); - - memset(_videoFrame.Buffer(), 127, _videoFrame.Size()); - memset(_videoFrame.Buffer() + _videoFrame.Width() * _videoFrame.Height(), - 255, _videoFrame.Width() * _videoFrame.Height() / 2); - _videoFrame.SetRenderTime(TickTime::MillisecondTimestamp()); - _renderProvider->RenderFrame(0, _videoFrame); - -} -void* Renderer::GetWindow() -{ - return (void*) _renderWindow; -} - -} // namespace webrtc - - diff --git a/modules/video_capture/main/test/testAPI/Renderer.h b/modules/video_capture/main/test/testAPI/Renderer.h deleted file mode 100644 index 43f5699f3..000000000 --- a/modules/video_capture/main/test/testAPI/Renderer.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#pragma once - -#include "video_render.h" -#include "testDefines.h" - -#ifdef _WIN32 - -#include -#elif defined(WEBRTC_MAC_INTEL) -#import -//#import "CocoaWindow.h" -#import "cocoa_renderer.h" -#elif defined (ANDROID) -#include -#elif defined(WEBRTC_LINUX) -typedef void* HWND; -#endif - -#include "thread_wrapper.h" - -namespace webrtc -{ - -class Renderer -{ -public: - Renderer(bool preview = false); - ~Renderer(void); - - void RenderFrame(VideoFrame& videoFrame); - void PaintGreen(); - void PaintBlue(); - void* GetWindow(); - -#if defined (ANDROID) - static void SetRenderWindow(jobject renderWindow); -#endif - -private: - - static bool RenderThread(ThreadObj); - bool RenderThreadProcess(); - - VideoRender* _renderModule; - VideoRenderCallback* _renderProvider; - VideoFrame _videoFrame; - bool _quiting; - ThreadWrapper* _messageThread; - static int _screen; - static const WebRtc_UWord32 _frameWidth = 352; - static const WebRtc_UWord32 _frameHeight = 288; - -#if defined(_WIN32) - HWND _renderWindow; -#elif defined(WEBRTC_MAC_INTEL) - NSAutoreleasePool* _pool; - CocoaRenderer* _renderWindow; -#elif defined (ANDROID) - jobject _renderWindow; //this is a glsurface. -public: - static jobject g_renderWindow; -#elif defined(WEBRTC_LINUX) - typedef void* HWND; - HWND _renderWindow; -#endif -}; -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/cocoa_renderer.h b/modules/video_capture/main/test/testAPI/cocoa_renderer.h deleted file mode 100644 index 6970dc673..000000000 --- a/modules/video_capture/main/test/testAPI/cocoa_renderer.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_COCOA_RENDERER_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_COCOA_RENDERER_H_ - -#import -#import -#import -#import - -@interface CocoaRenderer : NSOpenGLView { - NSOpenGLContext* _nsOpenGLContext; - int _screen; -} - -@property (nonatomic, retain)NSOpenGLContext* _nsOpenGLContext; -@property int screen; - -- (void)initCocoaRenderer:(NSOpenGLPixelFormat*)fmt; - -@end - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_COCOA_RENDERER_H_ diff --git a/modules/video_capture/main/test/testAPI/cocoa_renderer.mm b/modules/video_capture/main/test/testAPI/cocoa_renderer.mm deleted file mode 100644 index 2bd20cc2d..000000000 --- a/modules/video_capture/main/test/testAPI/cocoa_renderer.mm +++ /dev/null @@ -1,19 +0,0 @@ -// -// CocoaRenderer.mm -// testCocoaCommandLine -#import -#import -#import "cocoa_renderer.h" - -@implementation CocoaRenderer -@synthesize _nsOpenGLContext; -@synthesize screen = _screen; -- (void)initCocoaRenderer:(NSOpenGLPixelFormat*)fmt{ - self = [super initWithFrame:[self frame] pixelFormat:[fmt autorelease]]; - if (self != nil) - { - _nsOpenGLContext = [self openGLContext]; - } -} -@end - diff --git a/modules/video_capture/main/test/testAPI/testAPI.cpp b/modules/video_capture/main/test/testAPI/testAPI.cpp deleted file mode 100644 index 12b8e7e6d..000000000 --- a/modules/video_capture/main/test/testAPI/testAPI.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#if defined(_WIN32) -#include -#include -#include -#include -#include -#include - -#elif defined(WEBRTC_LINUX) -#include -#include -#include -#include - -#elif defined(WEBRTC_MAC_INTEL) -#import -#import -#import -#import -#import "cocoa_renderer.h" -#include -#include -#endif - -using namespace std; - -#include -#include "testExternalCapture.h" -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER -#include "testPlatformDependent.h" -#include "testCameraEncoder.h" -#endif - -#if defined(_WIN32) -int _tmain(int argc, _TCHAR* argv[]) -#elif defined(WEBRTC_LINUX) -int main(int argc, char* argv[]) -#elif defined(WEBRTC_MAC_INTEL) -int main (int argc, const char * argv[]) -#endif -{ - -#if defined(WEBRTC_MAC_INTEL) - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; -#endif - int testResult=0; - - { - webrtc::testExternalCapture test; - testResult=test.DoTest(); - printf("\nExternal capture test result %d\n",testResult); - } - -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - { - webrtc::testPlatformDependent platformDependent; - testResult=platformDependent.DoTest(); - printf("\nPlatform dependent test result %d\n",testResult); - } - { - webrtc::testCameraEncoder cameraEncoder; - testResult=cameraEncoder.DoTest(); - printf("\nCamera encoder test result %d\n",testResult); - - } -#endif - - getchar(); - -#if defined (WEBRTC_MAC_INTEL) - [pool release]; -#endif - return 0; -} - diff --git a/modules/video_capture/main/test/testAPI/testCameraEncoder.cpp b/modules/video_capture/main/test/testAPI/testCameraEncoder.cpp deleted file mode 100644 index d3f0db0aa..000000000 --- a/modules/video_capture/main/test/testAPI/testCameraEncoder.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testCameraEncoder.h" -#include "trace.h" -#include "tick_util.h" - - -namespace webrtc -{ - - -#ifndef _DEBUG -#undef assert -#define assert(_a) { \ - if(!(_a)) \ - { \ - LOG("Failed %s",#_a); \ - } \ -} -#endif - -testCameraEncoder::testCameraEncoder(void) -{ - Trace::CreateTrace(); - Trace::SetLevelFilter(webrtc::kTraceAll); - Trace::SetTraceFile("testCameraEncoder.txt"); - _captureInfo=VideoCaptureModule::CreateDeviceInfo(5); -#ifdef RENDER_PREVIEW - _renderer=NULL; - _videoCoding=webrtc::VideoCodingModule::Createwebrtc::VideoCodingModule(5); -#endif - -} - -testCameraEncoder::~testCameraEncoder(void) -{ - VideoCaptureModule::DestroyDeviceInfo(_captureInfo); - -#ifdef RENDER_PREVIEW - if(_renderer) - delete _renderer; - if(_videoCoding) - { - webrtc::VideoCodingModule::Destroywebrtc::VideoCodingModule(_videoCoding); - } - -#endif - Trace::ReturnTrace(); -} - - -int testCameraEncoder::DoTest() -{ - -#ifdef RENDER_PREVIEW - if(!_renderer) - { - _renderer=new Renderer(true); - } - if(_videoCoding) - { - webrtc::VideoCodec inst; - memset(&inst,0,sizeof(inst)); - inst.plType=122; - inst.width=640; - inst.height=480; - inst.codecType=webrtc::kVideoCodecH264; - strcpy(inst.plName,"H264"); - _videoCoding->InitializeReceiver(); - _videoCoding->RegisterReceiveCallback(this); - assert(_videoCoding->RegisterReceiveCodec(&inst,1,false)==0); - - } - -#endif - - // Test one camera at the time - LOG("\n\nTesting Camera encoder\n"); - for (WebRtc_UWord32 i=0;i<_captureInfo->NumberOfDevices();++i) - { - WebRtc_UWord8 name[256]; - WebRtc_UWord8 uniqueID[256]; - WebRtc_UWord8 productId[256]; - _captureInfo->GetDeviceName(i,name,256,uniqueID,256,productId,256); - - _captureModule= VideoCaptureModule::Create(0,uniqueID); - _captureModule->RegisterCaptureDataCallback(*this); - - VideoCaptureCapability capability; - LOG("Encoder support for device %s",uniqueID); - for (int capIndex=0;capIndex< - _captureInfo->NumberOfCapabilities(uniqueID);++capIndex) - { - assert(_captureInfo->GetCapability(uniqueID,capIndex,capability)==0); - if(capability.codecType==webrtc::kVideoCodecH264) - { - - testCapability(capability); - } - else if(capability.codecType!=webrtc::kVideoCodecUnknown) - { - LOG("type %d width %d, height %d, framerate %d\n", - capability.codecType,capability.width,capability.height,capability.maxFPS); - testCapability(capability); - } - } - - VideoCaptureModule::Destroy(_captureModule); - } - return 0; -} - -int testCameraEncoder::testCapability(VideoCaptureCapability& capability) -{ - webrtc::VideoCodec codec; - codec.height=(unsigned short)capability.height; - codec.width=(unsigned short) capability.width; - float bitrate=(float)(capability.height*capability.width*3)/1000; //3bits per pixel - codec.startBitrate=(unsigned int)bitrate; - codec.maxBitrate=codec.startBitrate*10; - codec.codecType=webrtc::kVideoCodecH264; - codec.codecSpecific.H264.profile=webrtc::kProfileBase; - - _encodeInterface=NULL; - _encodeInterface=_captureModule->GetEncodeInterface(codec); - if(_encodeInterface) - assert(_encodeInterface); - _captureSettings.ResetAll(); - _captureSettings.capability=capability; - - - - assert(capability.width); - assert(capability.height); - assert(capability.maxFPS); - assert(capability.expectedCaptureDelay); - _captureSettings.lastRenderTimeMS=0; - _captureSettings.captureDelay=50; - - - - WebRtc_UWord32 maxPayloadSize=1460; - - - LOG("\n\nTesting H264 width %d, height %d, framerate %d bitrate %d\n", - capability.width,capability.height,capability.maxFPS,codec.startBitrate); - - _captureSettings.initStartTime=TickTime::MillisecondTimestamp(); - assert(_captureModule->StartCapture(capability)==0); - - _captureSettings.startTime=TickTime::MillisecondTimestamp(); - _captureSettings.initStopTime=TickTime::MillisecondTimestamp(); - - if(_encodeInterface) - assert(_encodeInterface->ConfigureEncoder(codec,maxPayloadSize)==0); - - WebRtc_Word32 testTime=10000; - while(TickTime::MillisecondTimestamp()-_captureSettings.startTimeSetRates(codec.startBitrate,codec.maxFramerate)==0); - - testTime=2000; - while(TickTime::MillisecondTimestamp()- - _captureSettings.bitrateMeasureTime0.8*codec.startBitrate)); - } - - _captureSettings.stopTime=TickTime::MillisecondTimestamp(); - _captureSettings.stopStartTime=TickTime::MillisecondTimestamp(); - assert(_captureModule->StopCapture()==0); - _captureSettings.stopStopTime=TickTime::MillisecondTimestamp(); - - EvaluateTestResult(); - - return 0; - -} - -void testCameraEncoder::OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - webrtc::VideoCodecType codecType) -{ - _captureSettings.incomingFrames++; - _captureSettings.noOfBytes+=videoFrame.Length(); - assert(videoFrame.Height()==_captureSettings.capability.height); - assert(videoFrame.Width()==_captureSettings.capability.width); - assert(videoFrame.RenderTimeMs()>=(TickTime::MillisecondTimestamp()-30)); // RenderTimstamp should be the time now - if((videoFrame.RenderTimeMs()>_captureSettings.lastRenderTimeMS - +(1000*1.2)/_captureSettings.capability.maxFPS - && _captureSettings.lastRenderTimeMS>0) - || - (videoFrame.RenderTimeMs()<_captureSettings.lastRenderTimeMS+(1000*0.8) - /_captureSettings.capability.maxFPS && _captureSettings.lastRenderTimeMS>0)) - { - _captureSettings.timingWarnings++; - } - - if(_captureSettings.lastRenderTimeMS==0) - { - _captureSettings.firstCapturedFrameTime=TickTime::MillisecondTimestamp(); - - } - _captureSettings.lastRenderTimeMS=videoFrame.RenderTimeMs(); - if(codecType==webrtc::kVideoCodecH264) - { - WebRtc_UWord8* ptrBuffer=videoFrame.Buffer(); - if(ptrBuffer[0]!=0 || ptrBuffer[1]!=0 || ptrBuffer[2]!=0 || ptrBuffer[3]!=1) - { - assert(!"frame does not start with NALU header"); - } - if(ptrBuffer[4]==0x67) - { - _captureSettings.idrFrames++; - LOG("Got IDR frame frame no %d. total number of IDR frames %d \n", - _captureSettings.incomingFrames,_captureSettings.idrFrames); - } - } - -#ifdef RENDER_PREVIEW - if(codecType==webrtc::kVideoCodecH264) - { - - VideoEncodedData encodedFrame; - memset(&encodedFrame,0,sizeof(encodedFrame)); - encodedFrame.codec=webrtc::kVideoCodecH264; - encodedFrame.encodedHeight=videoFrame.Height(); - encodedFrame.encodedWidth=videoFrame.Width(); - encodedFrame.renderTimeMs=videoFrame.RenderTimeMs(); - encodedFrame.timeStamp=90* (WebRtc_UWord32) videoFrame.RenderTimeMs(); - encodedFrame.payloadData=(WebRtc_UWord8*) malloc(videoFrame.Length()); - memcpy(encodedFrame.payloadData,videoFrame.Buffer(),videoFrame.Length()); - encodedFrame.payloadSize=videoFrame.Length(); - encodedFrame.bufferSize=videoFrame.Length(); - encodedFrame.payloadType=122; - _videoCoding->DecodeFromStorage(encodedFrame); - } - if(codecType==webrtc::kVideoCodecUnknown) - { - _renderer->RenderFrame(videoFrame); - } -#endif - -} - -void testCameraEncoder::OnCaptureDelayChanged(const WebRtc_Word32 id, - const WebRtc_Word32 delay) -{ - _captureSettings.captureDelay=delay; -} - -#ifdef RENDER_PREVIEW -WebRtc_Word32 testCameraEncoder::FrameToRender(VideoFrame& videoFrame) -{ - _renderer->RenderFrame(videoFrame); - return 0; -} -#endif - - -void testCameraEncoder::EvaluateTestResult() -{ - CaptureSetting& captureResult=_captureSettings; - - WebRtc_UWord64 timeToFirstFrame=captureResult.firstCapturedFrameTime-captureResult.startTime; - WebRtc_UWord64 timeToStart=captureResult.initStopTime-captureResult.initStartTime; - WebRtc_UWord64 timeToStop=captureResult.stopStopTime-captureResult.stopStartTime; - - assert(timeToStart<4000); - assert(timeToStop<3000); - - - assert((timeToFirstFrame<3500) && (timeToFirstFrame>0)); // Assert if it takes more than 3500ms to start. - WebRtc_Word64 expectedNumberOfFrames=((captureResult.stopTime - -captureResult.startTime - -timeToFirstFrame) - *captureResult.capability.maxFPS)/1000; - assert(captureResult.incomingFrames>0.6*expectedNumberOfFrames); // Make sure at least 60% of the expected frames have been received from the camera - - LOG(" No Captured %d,expected %d, \n timingWarnings %d, time to first %lu\n" - " time to start %lu, time to stop %lu\n idr frames %u\n", - captureResult.incomingFrames,(int)(expectedNumberOfFrames), - captureResult.timingWarnings, - (long) timeToFirstFrame, - (long) timeToStart, - (long) timeToStop, - _captureSettings.idrFrames); - - captureResult.ResetSettings(); -} -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/testCameraEncoder.h b/modules/video_capture/main/test/testAPI/testCameraEncoder.h deleted file mode 100644 index bcbed840c..000000000 --- a/modules/video_capture/main/test/testAPI/testCameraEncoder.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#pragma once - -#include "video_capture.h" - -//#define RENDER_PREVIEW - -#ifdef RENDER_PREVIEW - #include "Renderer.h" - #include "video_coding.h" - #include "module_common_types.h" -#endif - -#if defined (WEBRTC_MAC_INTEL) || defined (WEBRTC_LINUX) - #include "Logger.h" -#else - #include "Logger.h" -#endif - -#include "testDefines.h" - -namespace webrtc -{ - -class testCameraEncoder: private VideoCaptureDataCallback -#ifdef RENDER_PREVIEW - ,VCMReceiveCallback -#endif -{ -public: - testCameraEncoder(void); - ~testCameraEncoder(void); - - int DoTest(); - - -private: - - int testCapability(VideoCaptureCapability& capability); - - // Implement VideoCaptureDataCallback - virtual void OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - webrtc::VideoCodecType codecType); - - virtual void OnCaptureDelayChanged(const WebRtc_Word32 id, - const WebRtc_Word32 delay); - - void EvaluateTestResult(); - - - -#ifdef RENDER_PREVIEW - //Implements webrtc::VCMReceiveCallback - virtual WebRtc_Word32 FrameToRender(VideoFrame& videoFrame); -#endif - - VideoCaptureModule* _captureModule; - VideoCaptureModule::DeviceInfo* _captureInfo; - VideoCaptureModule::VideoCaptureEncodeInterface* _encodeInterface; - -#ifdef RENDER_PREVIEW - Renderer*_renderer; - webrtc::VideoCodingModule* _videoCoding; -#endif - - struct CaptureSetting - { - VideoCaptureCapability capability; - WebRtc_Word32 captureDelay; - WebRtc_Word64 lastRenderTimeMS; - - WebRtc_Word32 incomingFrames; - WebRtc_Word32 timingWarnings; - WebRtc_Word64 startTime; - WebRtc_Word64 stopTime; - WebRtc_Word64 initStartTime; - WebRtc_Word64 initStopTime; - WebRtc_Word64 stopStartTime; - WebRtc_Word64 stopStopTime; - WebRtc_Word64 bitrateMeasureTime; - WebRtc_Word32 noOfBytes; - WebRtc_Word32 idrFrames; - - WebRtc_Word64 firstCapturedFrameTime; - - CaptureSetting() - { - ResetAll(); - - - } - void ResetSettings() - { - - capability.width=0; - capability.height=0; - capability.maxFPS=0; - captureDelay=0; - lastRenderTimeMS=0; - incomingFrames=0; - timingWarnings=0; - startTime=0; - stopTime=0; - firstCapturedFrameTime=0; - noOfBytes=0; - idrFrames=0; - bitrateMeasureTime=0; - - } - void ResetAll() - { - ResetSettings(); - - initStartTime=0; - initStopTime=0; - stopStartTime=0; - stopStopTime=0; - } - - }; - - Logger _logger; - CaptureSetting _captureSettings; -}; -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/testDefines.h b/modules/video_capture/main/test/testAPI/testDefines.h deleted file mode 100644 index d032c6948..000000000 --- a/modules/video_capture/main/test/testAPI/testDefines.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * testDefines.h - */ - - - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTDEFINES_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTDEFINES_H_ - - -#if defined (ANDROID) -#include -#include -#endif - - - - -#if defined(_WIN32) - #define SLEEP(x) Sleep(x) - #define SPRINTF(x, y, z, ...) sprintf_s(x, y, z, __VA_ARGS__) - #define LOG(...) { \ - char msg[512]; \ - sprintf_s(msg,512,__VA_ARGS__); \ - _logger.Print(msg); \ - } -#elif defined (ANDROID) - #define LOG(...) { \ - char msg[512]; \ - sprintf(msg,__VA_ARGS__); \ - __android_log_print(ANDROID_LOG_DEBUG, "VideoCaptureModule -testAPI", __VA_ARGS__);\ - _logger.Print(msg); \ - } - #define SLEEP(x) usleep(x*1000) - #define SPRINTF(x, y, z, ...) sprintf(x, z, __VA_ARGS__) -#else - #include - #define SLEEP(x) usleep(x * 1000) - #define SPRINTF(x, y, z, ...) sprintf(x, z, __VA_ARGS__) - #define LOG(...) { \ - char msg[512]; \ - sprintf(msg, __VA_ARGS__); \ - printf("%s\n", msg); \ - } - -#endif - -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTDEFINES_H_ diff --git a/modules/video_capture/main/test/testAPI/testExternalCapture.cpp b/modules/video_capture/main/test/testAPI/testExternalCapture.cpp deleted file mode 100644 index d44e32f3b..000000000 --- a/modules/video_capture/main/test/testAPI/testExternalCapture.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testExternalCapture.h" -#include "tick_util.h" -#include "process_thread.h" -#include "stdio.h" - -namespace webrtc -{ - -static int testExternalCaptureResult = 0; - -#ifdef NDEBUG -#if defined(WEBRTC_MAC_INTEL) - -#else -#undef assert -#define assert(p) if(!(p)){printf("Error line %d\n",__LINE__);testExternalCaptureResult=-1;} -#endif -#endif - -void testExternalCapture::CreateInterface() -{ - _captureModule = VideoCaptureModule::Create(1, _captureInteface); -} -testExternalCapture::testExternalCapture(void) - : _captureInteface(NULL), _captureModule(NULL) -{ -} - -int testExternalCapture::CompareFrames(const VideoFrame& frame1, - const VideoFrame& frame2) -{ - assert(frame1.Length()==frame2.Length()); - assert(frame1.Width()==frame2.Width()); - assert(frame1.Height()==frame2.Height()); - //assert(frame1.RenderTimeMs()==frame2.RenderTimeMs()); - for (unsigned int i = 0; i < frame1.Length(); ++i) - assert(*(frame1.Buffer()+i)==*(frame2.Buffer()+i)); - return 0; - -} - -testExternalCapture::~testExternalCapture(void) -{ - VideoCaptureModule::Destroy(_captureModule); -} - -void testExternalCapture::OnIncomingCapturedFrame( - const WebRtc_Word32 ID, - VideoFrame& videoFrame, - webrtc::VideoCodecType codecType) -{ - - _resultFrame.CopyFrame(videoFrame); - _frameCount++; -} - -void testExternalCapture::OnCaptureDelayChanged(const WebRtc_Word32 ID, - const WebRtc_Word32 delay) -{ -} - -void testExternalCapture::OnCaptureFrameRate(const WebRtc_Word32 id, - const WebRtc_UWord32 frameRate) -{ - printf("OnCaptureFrameRate %d, frameRate %d\n", id, frameRate); - _reportedFrameRate = frameRate; -} -void testExternalCapture::OnNoPictureAlarm(const WebRtc_Word32 id, - const VideoCaptureAlarm alarm) -{ - printf("OnNoPictureAlarm %d, alarm %d\n", id, alarm); - _captureAlarm = alarm; -} - -int testExternalCapture::DoTest() -{ - int height = 288; - int width = 352; - - printf("Platform independent test\n"); - - CreateInterface(); - ProcessThread* processModule = ProcessThread::CreateProcessThread(); - processModule->Start(); - processModule->RegisterModule(_captureModule); - - _testFrame.VerifyAndAllocate(height * width * 3 / 2); - _testFrame.SetLength(height * width * 3 / 2); - _testFrame.SetHeight(height); - _testFrame.SetWidth(width); - memset(_testFrame.Buffer(), 0, 1); - assert(_captureModule->RegisterCaptureDataCallback(*this)==0); - assert(_captureModule->RegisterCaptureCallback(*this)==0); - assert(_captureModule->EnableFrameRateCallback(true)==0); - assert(_captureModule->EnableNoPictureAlarm(true)==0); - - VideoCaptureCapability frameInfo; - frameInfo.width = width; - frameInfo.height = height; - frameInfo.rawType = webrtc::kVideoYV12; - - assert(_captureInteface->IncomingFrame(_testFrame.Buffer(), - _testFrame.Length(), - frameInfo,0)==0); - CompareFrames(_testFrame, _resultFrame); - - printf(" testing local frame rate callback and no picture alarm.\n"); - - WebRtc_Word64 testTime = 3; - _reportedFrameRate = 0; - _captureAlarm = Cleared; - - TickTime startTime = TickTime::Now(); - while ((TickTime::Now() - startTime).Milliseconds() < testTime * 1000) - { - assert(_captureInteface->IncomingFrame(_testFrame.Buffer(), - _testFrame.Length(), - frameInfo,0)==0); - SLEEP(100); - } - assert(_reportedFrameRate==10); - SLEEP(500); // Make sure the no picture alarm is triggered - assert(_captureAlarm==Raised); - - testTime = 3; - startTime = TickTime::Now(); - while ((TickTime::Now() - startTime).Milliseconds() < testTime * 1000) - { - assert(_captureInteface->IncomingFrame(_testFrame.Buffer(), - _testFrame.Length(), - frameInfo,0)==0); - SLEEP(33); - } - assert(_captureAlarm==Cleared); - assert(_reportedFrameRate==30); - - //Test start image - printf(" testing start send image.\n"); - testTime = 3; - startTime = TickTime::Now(); - _frameCount = 0; - assert(_captureModule->StartSendImage(_testFrame,15)==0); - while ((TickTime::Now() - startTime).Milliseconds() < testTime * 1000) - { - SLEEP(33); - } - assert(_captureModule->StopSendImage()==0); - assert(_frameCount>=testTime*15-1 && _frameCount<=testTime*15+1); - assert(_captureAlarm==Raised); - CompareFrames(_testFrame, _resultFrame); - SLEEP(1000); - assert(_frameCount>=testTime*15-1 && _frameCount<=testTime*15+1); - - processModule->Stop(); - - ProcessThread::DestroyProcessThread(processModule); - - return testExternalCaptureResult; -} -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/testExternalCapture.h b/modules/video_capture/main/test/testAPI/testExternalCapture.h deleted file mode 100644 index 6e2ea6f64..000000000 --- a/modules/video_capture/main/test/testAPI/testExternalCapture.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTEXTERNALCAPTURE_H_ -#define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTEXTERNALCAPTURE_H_ - -#include "testDefines.h" -#include "video_capture.h" - -namespace webrtc -{ - -class testExternalCapture - : public VideoCaptureDataCallback, public VideoCaptureFeedBack -{ -public: - testExternalCapture(void); - ~testExternalCapture(void); - - void CreateInterface(); - int DoTest(); - - // from VideoCaptureDataCallback - virtual void OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - VideoCodecType = kVideoCodecUnknown); - - virtual void OnCaptureDelayChanged(const WebRtc_Word32 id, - const WebRtc_Word32 delay); - - //VideoCaptureFeedBack - virtual void OnCaptureFrameRate(const WebRtc_Word32 id, - const WebRtc_UWord32 frameRate); - //VideoCaptureFeedBack - virtual void OnNoPictureAlarm(const WebRtc_Word32 id, - const VideoCaptureAlarm alarm); - -private: - - int CompareFrames(const VideoFrame& frame1, const VideoFrame& frame2); - - VideoCaptureExternal* _captureInteface; - VideoCaptureModule* _captureModule; - - VideoFrame _testFrame; - - VideoFrame _resultFrame; - WebRtc_Word32 _reportedFrameRate; - VideoCaptureAlarm _captureAlarm; - WebRtc_Word32 _frameCount; - -}; -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_TEST_TESTAPI_TESTEXTERNALCAPTURE_H_ diff --git a/modules/video_capture/main/test/testAPI/testPlatformDependent.cpp b/modules/video_capture/main/test/testAPI/testPlatformDependent.cpp deleted file mode 100644 index 49d9895d4..000000000 --- a/modules/video_capture/main/test/testAPI/testPlatformDependent.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "testPlatformDependent.h" -#include -#include "trace.h" -#include "tick_util.h" - -namespace webrtc -{ -static int testPlatformDependentResult = 0; - -#ifdef _WIN32 -#include -#endif - -#if defined( _DEBUG) && defined (_WIN32) -//#include "vld.h" -#endif - -#ifdef NDEBUG -#if defined(WEBRTC_MAC_INTEL) - -#else -#undef assert -#define assert(p) if(!(p)){LOG("Error line %d\n",__LINE__);testPlatformDependentResult=-1;} -#endif -#endif - -testPlatformDependent::testPlatformDependent(void) : - _captureModule(NULL), _noOfCameras(0) - -{ - Trace::CreateTrace(); - Trace::SetLevelFilter(webrtc::kTraceAll); - Trace::SetTraceFile("testPlatformDependent.txt"); - _captureInfo = VideoCaptureModule::CreateDeviceInfo(5); -#ifdef RENDER_PREVIEW - memset(_renderer, 0, sizeof(_renderer)); -#endif -} - -testPlatformDependent::~testPlatformDependent(void) -{ - VideoCaptureModule::DestroyDeviceInfo(_captureInfo); - -#ifdef RENDER_PREVIEW - if (_renderer[0]) - delete _renderer[0]; - - if (_renderer[1]) - delete _renderer[1]; - if (_renderer[2]) - delete _renderer[2]; - - if (_renderer[3]) - delete _renderer[3]; - -#endif - Trace::ReturnTrace(); -} - -//FILE* file=NULL; - -void testPlatformDependent::OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - VideoCodecType /*codecType*/) -{ - VerifyResultFrame(id, videoFrame); - - //LOG("OnIncomingCapturedFrame, width %d height %d id %d length %d\n", - // videoFrame.Width(), videoFrame.Height(),id,videoFrame.Length()); - - /* if(file==NULL) - { - file = fopen("/sdcard/testPlatform.yuv","wb"); - LOG("\nOnIncomingCapturedFrame, open file\n"); - } - if(file) - { - fwrite(videoFrame.Buffer(),videoFrame.Length(),1,file); - fflush(file); - }*/ -#ifdef RENDER_PREVIEW - if (id < 4 && _renderer[id]) - { - _renderer[id]->RenderFrame(videoFrame); - } -#endif - -} - -void testPlatformDependent::OnCaptureDelayChanged( - const WebRtc_Word32 settingID, - const WebRtc_Word32 delay) -{ - - bool found = false; - - for (WebRtc_UWord32 i = 0; i < _noOfCameras; ++i) - { - - if (settingID == _captureSettings[i].settingID) - { - found = true; - _captureSettings[0].captureDelay = delay; - } - } - assert(found); -} -void testPlatformDependent::VerifyResultFrame(const WebRtc_Word32 settingID, - const VideoFrame& videoFrame) -{ - bool found = false; - for (WebRtc_UWord32 i = 0; i < _noOfCameras; ++i) - { - if (settingID == _captureSettings[i].settingID) - { - found = true; - - assert(videoFrame.Height()==_captureSettings[i].capability.height); - assert(videoFrame.Width()==_captureSettings[i].capability.width); - assert(videoFrame.RenderTimeMs()>=TickTime::MillisecondTimestamp()-30); // RenderTimstamp should be the time now - if ((videoFrame.RenderTimeMs() - > _captureSettings[i].lastRenderTimeMS + (1000 * 1.1) - / _captureSettings[i].capability.maxFPS - && _captureSettings[i].lastRenderTimeMS > 0) - || (videoFrame.RenderTimeMs() - < _captureSettings[i].lastRenderTimeMS + (1000 * 0.9) - / _captureSettings[i].capability.maxFPS - && _captureSettings[i].lastRenderTimeMS > 0)) - { - _captureSettings[i].timingWarnings++; - } - - if (_captureSettings[i].lastRenderTimeMS == 0) - { - _captureSettings[i].firstCapturedFrameTime - = TickTime::MillisecondTimestamp(); - } - _captureSettings[i].incomingFrames++; - _captureSettings[i].lastRenderTimeMS = videoFrame.RenderTimeMs(); - } - } - assert(found); -} - -WebRtc_Word32 testPlatformDependent::testCreateDelete( - const WebRtc_UWord8* uniqueID) -{ - WebRtc_Word32 testTime = 8000; - WebRtc_Word32 numOfLoops = 7; - LOG("\n\nTesting create /delete - start stop of camera %s\n",(char*)uniqueID); - for (WebRtc_Word32 i = 0; i < numOfLoops; ++i) - { - LOG("Loop %d of %d\n",(int) i, (int) numOfLoops); - _captureSettings[0].settingID = 0; -#ifndef WEBRTC_MAC - _captureInfo->GetCapability(uniqueID, 0, _captureSettings[0].capability); -#else - _captureSettings[0].capability.width = 352; - _captureSettings[0].capability.height = 288; - _captureSettings[0].capability.maxFPS = 30; - _captureSettings[0].capability.rawType = kVideoUnknown; -#endif - _captureSettings[0].startTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].initStartTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].captureModule = VideoCaptureModule::Create(0, - uniqueID); - assert(!_captureSettings[0].captureModule->CaptureStarted()); - assert(_captureSettings[0].captureModule); // Test that it is created - assert(!_captureSettings[0].captureModule->RegisterCaptureDataCallback(*this)); - - VideoCaptureCapability capability; - assert(_captureSettings[0].captureModule->CaptureSettings(capability)==0); - - assert(_captureSettings[0].captureModule->StartCapture( - _captureSettings[0].capability) ==0); - assert(_captureSettings[0].captureModule->CaptureStarted()); - assert(_captureSettings[0].captureModule->CaptureSettings( - capability) ==0); - _captureSettings[0].initStopTime = TickTime::MillisecondTimestamp(); - - assert(capability==_captureSettings[0].capability); - - WebRtc_Word64 timeNow = TickTime::MillisecondTimestamp(); - while (_captureSettings[0].incomingFrames <= 5 - && testTime > timeNow - _captureSettings[0].startTime) - { - SLEEP(100); - timeNow = TickTime::MillisecondTimestamp(); - } - _captureSettings[0].stopTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].stopStartTime = TickTime::MillisecondTimestamp(); - assert(_captureSettings[0].captureModule->StopCapture()==0); - assert(!_captureSettings[0].captureModule->CaptureStarted()); - - VideoCaptureModule::Destroy(_captureSettings[0].captureModule); - _captureSettings[0].stopStopTime = TickTime::MillisecondTimestamp(); - - assert((_captureSettings[0].incomingFrames >= 5)); // Make sure at least 5 frames has been captured - EvaluateTestResult(_captureSettings[0]); - _captureSettings[0].ResetAll(); - } - LOG("Test Done\n"); - return testPlatformDependentResult; -} - -WebRtc_Word32 testPlatformDependent::testCapabilities( - const WebRtc_UWord8* uniqueID) -{ -#ifndef WEBRTC_MAC - LOG("\n\nTesting capture capabilities\n"); - - _captureSettings[0].captureModule = VideoCaptureModule::Create(0, uniqueID); - assert(_captureSettings[0].captureModule); // Test that it is created - - assert(!_captureSettings[0].captureModule->RegisterCaptureDataCallback(*this)); - - WebRtc_Word32 numOfCapabilities = - _captureInfo->NumberOfCapabilities(uniqueID); - - assert(numOfCapabilities); - bool oneValidCap = false; - WebRtc_Word32 testTime = 4000; - - for (WebRtc_Word32 j = 0; j < numOfCapabilities; ++j) - { - VideoCaptureCapability capability; - int b = (_captureInfo->GetCapability(uniqueID, j, capability) == 0); - - assert(b); - assert(capability.width); - assert(capability.height); - assert(capability.maxFPS); - assert(capability.expectedCaptureDelay); - oneValidCap = true; - _captureSettings[0].lastRenderTimeMS = 0; - _captureSettings[0].settingID = 0; - _captureSettings[0].captureDelay = 50; - _captureSettings[0].capability = capability; - - LOG("\n\n Starting camera: capability %d, width %u, height %u," - " framerate %u, color %d.\n", - (int) j,(unsigned int)_captureSettings[0].capability.width, - (unsigned int)_captureSettings[0].capability.height, - (unsigned int) _captureSettings[0].capability.maxFPS,(int) capability.rawType); - _captureSettings[0].initStartTime = TickTime::MillisecondTimestamp(); - assert(_captureSettings[0].captureModule->StartCapture(_captureSettings[0].capability)==0); - _captureSettings[0].startTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].initStopTime = TickTime::MillisecondTimestamp(); - - while (TickTime::MillisecondTimestamp() - _captureSettings[0].startTime - < testTime && _captureSettings[0].incomingFrames < 600) - { - SLEEP(200); - } - - _captureSettings[0].stopTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].stopStartTime = TickTime::MillisecondTimestamp(); - assert(_captureSettings[0].captureModule->StopCapture()==0); - _captureSettings[0].stopStopTime = TickTime::MillisecondTimestamp(); - - EvaluateTestResult(_captureSettings[0]); - } - assert(oneValidCap); // Make sure the camera support at least one capability - VideoCaptureModule::Destroy(_captureSettings[0].captureModule); - _captureSettings[0].ResetAll(); - return testPlatformDependentResult; -#else - // GetCapability() not support on Mac - return 0; -#endif - -} - -WebRtc_Word32 testPlatformDependent::testMultipleCameras() -{ - // Test multiple cameras - LOG("\n\nTesting all cameras simultanously\n"); - _noOfCameras = _captureInfo->NumberOfDevices(); - WebRtc_Word32 testTime = 20000; - for (WebRtc_UWord32 i = 0; i < _noOfCameras; ++i) - { -#ifdef RENDER_PREVIEW - if (!_renderer[i]) - { - _renderer[i] = new Renderer(true); - } -#endif - WebRtc_UWord8 id[256]; - _captureInfo->GetDeviceName(i, _captureSettings[i].captureName, 256, - id, 256); - WebRtc_UWord8* name = _captureSettings[i].captureName; - LOG("\n\n Found capture device %u\n name %s\n unique name %s\n" - ,(unsigned int) i,(char*) name, (char*)id); - _captureSettings[i].captureModule = VideoCaptureModule::Create(i, id); - assert(_captureSettings[i].captureModule); // Test that it is created - assert(!_captureSettings[i].captureModule->RegisterCaptureDataCallback(*this)); - - _captureSettings[i].lastRenderTimeMS = 0; - _captureSettings[i].settingID = i; - _captureSettings[i].captureDelay = 0; - _captureSettings[i].capability.maxFPS = 30; - _captureSettings[i].capability.width = 640; - _captureSettings[i].capability.height = 480; - - LOG("\n\n Starting camera %s.\n",name); - _captureSettings[i].captureModule->StartCapture( - _captureSettings[i].capability); - _captureSettings[i].startTime = TickTime::MillisecondTimestamp(); - - } - - SLEEP(testTime); - for (WebRtc_UWord32 i = 0; i < _noOfCameras; ++i) - { - _captureSettings[i].stopTime = TickTime::MillisecondTimestamp(); - _captureSettings[i].captureModule->StopCapture(); - - EvaluateTestResult(_captureSettings[i]); - VideoCaptureModule::Destroy(_captureSettings[i].captureModule); - _captureSettings[i].ResetAll(); - } - return testPlatformDependentResult; -} - -void testPlatformDependent::SetRenderer(Renderer* renderer) -{ - LOG("\ntestPlatformDependent::SetRenderer()\n"); -#ifdef RENDER_PREVIEW - _renderer[0] = renderer; -#endif -} - -WebRtc_Word32 testPlatformDependent::testRotation(const WebRtc_UWord8* uniqueID) -{ - LOG("\n\nTesting capture Rotation\n"); - - _captureSettings[0].captureModule = VideoCaptureModule::Create(0, uniqueID); - assert(_captureSettings[0].captureModule); // Test that it is created - - assert(!_captureSettings[0].captureModule->RegisterCaptureDataCallback(*this)); -#ifndef WEBRTC_MAC - assert(_captureInfo->GetCapability(uniqueID,0,_captureSettings[0].capability)==0); -#else - // GetCapability not supported on Mac - _captureSettings[0].capability.width = 352; - _captureSettings[0].capability.height = 288; - _captureSettings[0].capability.maxFPS = 30; - _captureSettings[0].capability.rawType = kVideoUnknown; -#endif - - WebRtc_Word32 testTime = 4000; - - _captureSettings[0].lastRenderTimeMS = 0; - _captureSettings[0].settingID = 0; - _captureSettings[0].captureDelay = 50; - - LOG("\n\n Starting camera: width %u, height %u, framerate %u, color %d.\n", - (unsigned int)_captureSettings[0].capability.width, - (unsigned int)_captureSettings[0].capability.height, - (unsigned int) _captureSettings[0].capability.maxFPS, - (int) _captureSettings[0].capability.rawType); - - _captureSettings[0].initStartTime = TickTime::MillisecondTimestamp(); - assert(_captureSettings[0].captureModule->StartCapture(_captureSettings[0].capability)==0); - _captureSettings[0].startTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].initStopTime = TickTime::MillisecondTimestamp(); - - LOG("\nSetting capture rotation 0\n"); - assert(_captureSettings[0].captureModule->SetCaptureRotation(kCameraRotate0)==0); - while (TickTime::MillisecondTimestamp() - _captureSettings[0].startTime - < testTime) - { - SLEEP(200); - } - LOG("\nSetting capture rotation 90\n"); - assert(_captureSettings[0].captureModule->SetCaptureRotation(kCameraRotate90)==0); - while (TickTime::MillisecondTimestamp() - _captureSettings[0].startTime - < testTime * 2) - { - SLEEP(200); - } - LOG("\nSetting capture rotation 180\n"); - assert(_captureSettings[0].captureModule->SetCaptureRotation(kCameraRotate180)==0); - while (TickTime::MillisecondTimestamp() - _captureSettings[0].startTime - < testTime * 3) - { - SLEEP(200); - } - LOG("\nSetting capture rotation 270\n"); - assert(_captureSettings[0].captureModule->SetCaptureRotation(kCameraRotate270)==0); - while (TickTime::MillisecondTimestamp() - _captureSettings[0].startTime - < testTime * 4) - { - SLEEP(200); - } - - _captureSettings[0].stopTime = TickTime::MillisecondTimestamp(); - _captureSettings[0].stopStartTime = TickTime::MillisecondTimestamp(); - assert(_captureSettings[0].captureModule->StopCapture()==0); - _captureSettings[0].stopStopTime = TickTime::MillisecondTimestamp(); - - EvaluateTestResult(_captureSettings[0]); - - VideoCaptureModule::Destroy(_captureSettings[0].captureModule); - _captureSettings[0].ResetAll(); - - return testPlatformDependentResult; - -} -int testPlatformDependent::DoTest() -{ - LOG("\ntestPlatformDependent::DoTest()\n"); -#ifdef RENDER_PREVIEW - if (!_renderer[0]) - { - _renderer[0] = new Renderer(true); - } -#endif - - // Test one camera at the time - LOG("\n\nTesting one camera at the time\n"); - _noOfCameras = _captureInfo->NumberOfDevices(); - - for (WebRtc_UWord32 i = 0; i < _noOfCameras; ++i) - { - WebRtc_UWord8 name[256]; - WebRtc_UWord8 uniqueID[256]; - WebRtc_UWord8 productId[256]; - memset(productId, 0, sizeof(productId)); - _captureInfo->GetDeviceName(i, name, 256, uniqueID, 256, productId, 256); - - char logFileName[512]; - SPRINTF(logFileName,512,"testPlatformDependent%s_%s.txt",(char*)name,(char*)productId); - _logger.SetFileName(logFileName); - - WebRtc_Word32 cap = _captureInfo->NumberOfCapabilities(uniqueID); - LOG("\n\n Found capture device %u\n " - " name %s\n Capabilities %d, unique name %s \n", - (unsigned int) i,name,(int) cap,(char*) uniqueID); - testCreateDelete(uniqueID); - testCapabilities(uniqueID); - testRotation(uniqueID); - - } -#ifndef ANDROID - _logger.SetFileName("testPlatformDependent_multipleCameras.txt"); - testMultipleCameras(); -#endif - LOG("\n\ntestPlatformDependent done\n"); - return 0; -} -void testPlatformDependent::EvaluateTestResult(CaptureSetting& captureResult) -{ - WebRtc_UWord64 timeToFirstFrame = captureResult.firstCapturedFrameTime - - captureResult.startTime; - WebRtc_UWord64 timeToStart = captureResult.initStopTime - - captureResult.initStartTime; - WebRtc_UWord64 timeToStop = captureResult.stopStopTime - - captureResult.stopStartTime; - - assert(timeToStart<4000); - assert(timeToStop<3000); - - assert((timeToFirstFrame<3500) && (timeToFirstFrame>0)); // Assert if it takes more than 3500ms to start. - WebRtc_Word64 expectedNumberOfFrames = ((captureResult.stopTime - - captureResult.startTime - timeToFirstFrame) - * captureResult.capability.maxFPS) / 1000; - assert(captureResult.incomingFrames>0.50*expectedNumberOfFrames); // Make sure at least 50% of the expected frames have been received from the camera - - LOG(" Test result.\n No Captured %d,expected %d, \n timingWarnings %d," - " time to first %lu\n time to start %lu, time to stop %lu\n", - (int) captureResult.incomingFrames,(int)(expectedNumberOfFrames), - (int) captureResult.timingWarnings,(long) timeToFirstFrame, - (long) timeToStart,(long) timeToStop); - captureResult.ResetSettings(); -} -} // namespace webrtc diff --git a/modules/video_capture/main/test/testAPI/testPlatformDependent.h b/modules/video_capture/main/test/testAPI/testPlatformDependent.h deleted file mode 100644 index 8c513ed6d..000000000 --- a/modules/video_capture/main/test/testAPI/testPlatformDependent.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#pragma once - - -#include "testDefines.h" -#include "video_capture.h" -#include "Logger.h" - -//#define RENDER_PREVIEW //Does not work properly on Linux - -#ifdef RENDER_PREVIEW - #include "Renderer.h" -#else - typedef void* Renderer; -#endif - -namespace webrtc -{ - -struct CaptureSetting -{ - WebRtc_Word32 settingID; - WebRtc_UWord8 captureName[256]; - VideoCaptureCapability capability; - WebRtc_Word32 captureDelay; - WebRtc_Word64 lastRenderTimeMS; - - WebRtc_Word32 incomingFrames; - WebRtc_Word32 timingWarnings; - WebRtc_Word64 startTime; - WebRtc_Word64 stopTime; - WebRtc_Word64 initStartTime; - WebRtc_Word64 initStopTime; - WebRtc_Word64 stopStartTime; - WebRtc_Word64 stopStopTime; - - WebRtc_Word64 firstCapturedFrameTime; - - VideoCaptureModule* captureModule; - - - CaptureSetting() - { - ResetAll(); - - - } - void ResetSettings() - { - - capability.width=0; - capability.height=0; - capability.maxFPS=0; - captureDelay=0; - lastRenderTimeMS=0; - incomingFrames=0; - timingWarnings=0; - startTime=0; - stopTime=0; - firstCapturedFrameTime=0; - - } - void ResetAll() - { - ResetSettings(); - - settingID = -1; - captureModule=0; - initStartTime=0; - initStopTime=0; - stopStartTime=0; - stopStopTime=0; - } - -}; - -class testPlatformDependent: public VideoCaptureDataCallback -{ -public: - testPlatformDependent(void); - ~testPlatformDependent(void); - - - int DoTest(); - - void SetRenderer(Renderer* renderer); - - // from VideoCaptureDataCallback - virtual void OnIncomingCapturedFrame(const WebRtc_Word32 id, - VideoFrame& videoFrame, - webrtc::VideoCodecType codecType); - - virtual void OnCaptureDelayChanged(const WebRtc_Word32 id, - const WebRtc_Word32 delay); - -private: - // Test multiple create delete start stop of one module - WebRtc_Word32 testCreateDelete(const WebRtc_UWord8* uniqueID); - WebRtc_Word32 testCapabilities(const WebRtc_UWord8* uniqueID); - WebRtc_Word32 testMultipleCameras(); - WebRtc_Word32 testRotation(const WebRtc_UWord8* uniqueID); - - - void VerifyResultFrame(const WebRtc_Word32 id,const VideoFrame& videoFrame); - void EvaluateTestResult(CaptureSetting& captureResult); - - VideoCaptureModule* _captureModule; - VideoCaptureModule::DeviceInfo* _captureInfo; - - CaptureSetting _captureSettings[4]; - WebRtc_UWord32 _noOfCameras; - -#ifdef RENDER_PREVIEW - Renderer*_renderer[4]; -#endif - Logger _logger; - -}; -} // namespace webrtc diff --git a/modules/video_coding/codecs/OWNERS b/modules/video_coding/codecs/OWNERS deleted file mode 100644 index 30eee35f1..000000000 --- a/modules/video_coding/codecs/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -holmer@google.com -mikhal@google.com -marpan@google.com -hlundin@google.com diff --git a/modules/video_coding/codecs/i420/main/interface/i420.h b/modules/video_coding/codecs/i420/main/interface/i420.h deleted file mode 100644 index 75c34d96e..000000000 --- a/modules/video_coding/codecs/i420/main/interface/i420.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_ - -#include "video_codec_interface.h" -#include "typedefs.h" - -namespace webrtc -{ - -class I420Encoder : public VideoEncoder -{ -public: - - I420Encoder(); - - virtual ~I420Encoder(); - -// Initialize the encoder with the information from the VideoCodec -// -// Input: -// - codecSettings : Codec settings -// - numberOfCores : Number of cores available for the encoder -// - maxPayloadSize : The maximum size each payload is allowed -// to have. Usually MTU - overhead. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Error - virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores*/, WebRtc_UWord32 /*maxPayloadSize*/); - -// "Encode" an I420 image (as a part of a video stream). The encoded image -// will be returned to the user via the encode complete callback. -// -// Input: -// - inputImage : Image to be encoded -// - codecSpecificInfo : Pointer to codec specific data -// - frameType : Frame type to be sent (Key /Delta) . -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Error - virtual WebRtc_Word32 - Encode(const RawImage& inputImage, - const CodecSpecificInfo* /*codecSpecificInfo*/, - VideoFrameType /*frameType*/); - -// Register an encode complete callback object. -// -// Input: -// - callback : Callback object which handles encoded images. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback* callback); - -// Free encoder memory. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Release(); - -// Reset encoder state and prepare for a new call. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. -// <0 - Error - virtual WebRtc_Word32 Reset(); - - virtual WebRtc_Word32 SetRates(WebRtc_UWord32 /*newBitRate*/, WebRtc_UWord32 /*frameRate*/) {return WEBRTC_VIDEO_CODEC_OK;} - - virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 /*packetLoss*/){return WEBRTC_VIDEO_CODEC_OK;}; - - virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/){return WEBRTC_VIDEO_CODEC_OK;}; - -// Get version number for the codec. -// -// Input: -// - version : Pointer to allocated char buffer. -// - length : Length of provided char buffer. -// -// Output: -// - version : Version number string written to char buffer. -// -// Return value : >0 - Length of written string. -// <0 - Error - static WebRtc_Word32 VersionStatic(WebRtc_Word8 *version, WebRtc_Word32 length); - virtual WebRtc_Word32 Version(WebRtc_Word8 *version, WebRtc_Word32 length) const; - -private: - bool _inited; - EncodedImage _encodedImage; - EncodedImageCallback* _encodedCompleteCallback; - -}; // end of WebRtcI420DEncoder class - -class I420Decoder : public VideoDecoder -{ -public: - - I420Decoder(); - - virtual ~I420Decoder(); - -// Initialize the decoder. -// The user must notify the codec of width and height values. -// -// Return value : WEBRTC_VIDEO_CODEC_OK. -// <0 - Errors - virtual WebRtc_Word32 InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores*/); - - virtual WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/){return WEBRTC_VIDEO_CODEC_OK;}; - -// Decode encoded image (as a part of a video stream). The decoded image -// will be returned to the user through the decode complete callback. -// -// Input: -// - inputImage : Encoded image to be decoded -// - missingFrames : True if one or more frames have been lost -// since the previous decode call. -// - codecSpecificInfo : pointer to specific codec data -// - renderTimeMs : Render time in Ms -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Error - virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, bool missingFrames, - const void* /*codecSpecificInfo */, WebRtc_Word64 /*renderTimeMs*/); - -// Register a decode complete callback object. -// -// Input: -// - callback : Callback object which handles decoded images. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback* callback); - -// Free decoder memory. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Error - virtual WebRtc_Word32 Release(); - -// Reset decoder state and prepare for a new call. -// -// Return value : WEBRTC_VIDEO_CODEC_OK. -// <0 - Error - virtual WebRtc_Word32 Reset(); - -private: - - RawImage _decodedImage; - WebRtc_Word32 _width; - WebRtc_Word32 _height; - bool _inited; - DecodedImageCallback* _decodeCompleteCallback; - - -}; // end of WebRtcI420Decoder class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_I420_H_ diff --git a/modules/video_coding/codecs/i420/main/source/Android.mk b/modules/video_coding/codecs/i420/main/source/Android.mk deleted file mode 100644 index c74f57e6b..000000000 --- a/modules/video_coding/codecs/i420/main/source/Android.mk +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_i420 -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := i420.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' \ -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_coding/codecs/i420/main/source/i420.cc b/modules/video_coding/codecs/i420/main/source/i420.cc deleted file mode 100644 index 2f276f0cf..000000000 --- a/modules/video_coding/codecs/i420/main/source/i420.cc +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "i420.h" -#include - -namespace webrtc -{ - -I420Encoder::I420Encoder(): -_inited(false), -_encodedImage(), -_encodedCompleteCallback(NULL) -{ - // -} - -I420Encoder::~I420Encoder() -{ - _inited = false; - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - _encodedImage._buffer = NULL; - } -} - -WebRtc_Word32 -I420Encoder::VersionStatic(WebRtc_Word8* version, WebRtc_Word32 length) -{ - const WebRtc_Word8* str= "I420 version 1.1.0\n"; - WebRtc_Word32 verLen = (WebRtc_Word32)strlen(str); - if(verLen > length) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - strncpy(version, str,length); - return verLen; -} - -WebRtc_Word32 -I420Encoder::Version(WebRtc_Word8 *version, WebRtc_Word32 length) const -{ - return VersionStatic(version, length); -} - - - -WebRtc_Word32 -I420Encoder::Release() -{ - // should allocate an encoded frame and then release it here, for that we actaully need an init flag - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - _encodedImage._buffer = NULL; - } - _inited = false; - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -I420Encoder::Reset() -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - return WEBRTC_VIDEO_CODEC_OK; - -} - -WebRtc_Word32 -I420Encoder::InitEncode(const VideoCodec* codecSettings, - WebRtc_Word32 /*numberOfCores*/, - WebRtc_UWord32 /*maxPayloadSize */) -{ - if (codecSettings == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (codecSettings->width < 1 || codecSettings->height < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - - // allocating encoded memory - - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - _encodedImage._buffer = NULL; - _encodedImage._size = 0; - } - const WebRtc_UWord32 newSize = (3 * codecSettings->width * - codecSettings->height) >> 1; - WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize]; - if (newBuffer == NULL) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - _encodedImage._size = newSize; - _encodedImage._buffer = newBuffer; - - // if no memory allocation, no point to init - _inited = true; - return WEBRTC_VIDEO_CODEC_OK; -} - - - -WebRtc_Word32 -I420Encoder::Encode(const RawImage& inputImage, - const CodecSpecificInfo* /*codecSpecificInfo*/, - VideoFrameType /*frameTypes*/) -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (_encodedCompleteCallback == NULL) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - - _encodedImage._frameType = kKeyFrame; // no coding - _encodedImage._timeStamp = inputImage._timeStamp; - _encodedImage._encodedHeight = inputImage._height; - _encodedImage._encodedWidth = inputImage._width; - if (inputImage._length > _encodedImage._size) - { - - // allocating encoded memory - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - _encodedImage._buffer = NULL; - _encodedImage._size = 0; - } - const WebRtc_UWord32 newSize = (3 * _encodedImage._encodedWidth * _encodedImage._encodedHeight) >> 1; - WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize]; - if (newBuffer == NULL) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - _encodedImage._size = newSize; - _encodedImage._buffer = newBuffer; - } - memcpy(_encodedImage._buffer, inputImage._buffer, inputImage._length); - _encodedImage._length = inputImage._length; - _encodedCompleteCallback->Encoded(_encodedImage); - return WEBRTC_VIDEO_CODEC_OK; -} - - -WebRtc_Word32 -I420Encoder::RegisterEncodeCompleteCallback(EncodedImageCallback* callback) -{ - _encodedCompleteCallback = callback; - return WEBRTC_VIDEO_CODEC_OK; -} - - -I420Decoder::I420Decoder(): -_decodedImage(), -_width(0), -_height(0), -_inited(false), -_decodeCompleteCallback(NULL) -{ - // -} - -I420Decoder::~I420Decoder() -{ - Release(); -} - -WebRtc_Word32 -I420Decoder::Reset() -{ - return WEBRTC_VIDEO_CODEC_OK; -} - - -WebRtc_Word32 -I420Decoder::InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 /*numberOfCores */) -{ - if (codecSettings == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - else if (codecSettings->width < 1 || codecSettings->height < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - _width = codecSettings->width; - _height = codecSettings->height; - _inited = true; - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -I420Decoder::Decode(const EncodedImage& inputImage, bool /*missingFrames*/, const void* /*codecSpecificInfo*/, WebRtc_Word64 /*renderTimeMs*/) -{ - if (inputImage._buffer == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (_decodeCompleteCallback == NULL) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (inputImage._length <= 0) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - - //Allocate memory for decoded image - - if (_decodedImage._buffer != NULL) - { - delete [] _decodedImage._buffer; - _decodedImage._buffer = NULL; - _decodedImage._size = 0; - } - if (_decodedImage._buffer == NULL) - { - const WebRtc_UWord32 newSize = (3*_width*_height) >> 1; - WebRtc_UWord8* newBuffer = new WebRtc_UWord8[newSize]; - if (newBuffer == NULL) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - _decodedImage._size = newSize; - _decodedImage._buffer = newBuffer; - } - - // Set decoded image parameters - _decodedImage._height = _height; - _decodedImage._width = _width; - _decodedImage._timeStamp = inputImage._timeStamp; - memcpy(_decodedImage._buffer, inputImage._buffer, inputImage._length); - _decodedImage._length = inputImage._length; - //_decodedImage._buffer = inputImage._buffer; - - _decodeCompleteCallback->Decoded(_decodedImage); - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -I420Decoder::RegisterDecodeCompleteCallback(DecodedImageCallback* callback) -{ - _decodeCompleteCallback = callback; - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -I420Decoder::Release() -{ - if (_decodedImage._buffer != NULL) - { - delete [] _decodedImage._buffer; - _decodedImage._buffer = NULL; - } - _inited = false; - return WEBRTC_VIDEO_CODEC_OK; -} - -} diff --git a/modules/video_coding/codecs/i420/main/source/i420.gyp b/modules/video_coding/codecs/i420/main/source/i420.gyp deleted file mode 100644 index f3afcb669..000000000 --- a/modules/video_coding/codecs/i420/main/source/i420.gyp +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'webrtc_i420', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - '../../../../../../common_video/interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../../../../common_video/interface', - ], - }, - 'sources': [ - '../interface/i420.h', - 'i420.cc', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_coding/codecs/interface/video_codec_interface.h b/modules/video_coding/codecs/interface/video_codec_interface.h deleted file mode 100644 index 106039177..000000000 --- a/modules/video_coding/codecs/interface/video_codec_interface.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef VIDEO_CODEC_INTERFACE_H -#define VIDEO_CODEC_INTERFACE_H - -#include "common_types.h" -#include "typedefs.h" -#include "video_image.h" -#include "video_error_codes.h" - -namespace webrtc -{ - -class RTPFragmentationHeader; // forward declaration - -struct CodecSpecificInfoVP8 -{ - bool hasReceivedSLI; - WebRtc_UWord8 pictureIdSLI; - bool hasReceivedRPSI; - WebRtc_UWord64 pictureIdRPSI; - WebRtc_Word16 pictureId; // negative value to skip pictureId - bool nonReference; -}; - -union CodecSpecificInfoUnion -{ - CodecSpecificInfoVP8 VP8; -}; - -struct CodecSpecificInfo -{ - VideoCodecType codecType; - CodecSpecificInfoUnion codecSpecific; -}; - -class EncodedImageCallback -{ -public: - virtual ~EncodedImageCallback() {}; - - // Callback function which is called when an image has been encoded. - // - // Input: - // - encodedImage : The encoded image - // - // Return value : > 0, signals to the caller that one or more future frames - // should be dropped to keep bit rate or frame rate. - // = 0, if OK. - // < 0, on error. - virtual WebRtc_Word32 - Encoded(EncodedImage& encodedImage, - const CodecSpecificInfo* codecSpecificInfo = NULL, - const RTPFragmentationHeader* fragmentation = NULL) = 0; -}; - -class VideoEncoder -{ -public: - virtual ~VideoEncoder() {}; - - // Get the encoder version. - // - // Input: - // - length : Length of the version buffer. - // - // Output: - // - version : Buffer where the version string will be written. - // - // Return value : Number of bytes written to the version buffer. - // < 0 on failure. - virtual WebRtc_Word32 Version(WebRtc_Word8 *version, WebRtc_Word32 length) const = 0; - - // Initialize the encoder with the information from the VideoCodec. - // - // Input: - // - codecSettings : Codec settings - // - numberOfCores : Number of cores available for the encoder - // - maxPayloadSize : The maximum size each payload is allowed - // to have. Usually MTU - overhead. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings, WebRtc_Word32 numberOfCores, WebRtc_UWord32 maxPayloadSize) = 0; - - // Encode an I420 image (as a part of a video stream). The encoded image - // will be returned to the user through the encode complete callback. - // - // Input: - // - inputImage : Image to be encoded - // - codecSpecificInfo : Pointer to codec specific data - // - frameType : The frame type to encode - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 - Encode(const RawImage& inputImage, - const CodecSpecificInfo* codecSpecificInfo = NULL, - VideoFrameType frameType = kDeltaFrame) = 0; - - // Register an encode complete callback object. - // - // Input: - // - callback : Callback object which handles encoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback* callback) = 0; - - // Free encoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Release() = 0; - - // Reset encoder state and prepare for a new call. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Reset() = 0; - - // Inform the encoder about the packet loss and round trip time on the network - // used to decide the best pattern and signaling. - // - // - packetLoss : Fraction lost - // (loss rate in percent = 100 * packetLoss / 255) - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 packetLoss) = 0; - - // Inform the encoder about the new target bit rate. - // - // - newBitRate : New target bit rate - // - frameRate : The target frame rate - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate) = 0; - - // Use this function to enable or disable periodic key frames. Can be useful for codecs - // which have other ways of stopping error propagation. - // - // - enable : Enable or disable periodic key frames - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 SetPeriodicKeyFrames(bool enable) { return WEBRTC_VIDEO_CODEC_ERROR; } - - // Codec configuration data to send out-of-band, i.e. in SIP call setup - // - // - buffer : Buffer pointer to where the configuration data - // should be stored - // - size : The size of the buffer in bytes - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; } -}; - -class DecodedImageCallback -{ -public: - virtual ~DecodedImageCallback() {}; - - // Callback function which is called when an image has been decoded. - // - // Input: - // - decodedImage : The decoded image - // - // Return value : 0 if OK, < 0 otherwise. - virtual WebRtc_Word32 Decoded(RawImage& decodedImage) = 0; - - virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) {return -1;} - - virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId) {return -1;} -}; - -class VideoDecoder -{ -public: - virtual ~VideoDecoder() {}; - - // Initialize the decoder with the information from the VideoCodec. - // - // Input: - // - inst : Codec settings - // - numberOfCores : Number of cores available for the decoder - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 InitDecode(const VideoCodec* codecSettings, WebRtc_Word32 numberOfCores) = 0; - - // Decode encoded image (as a part of a video stream). The decoded image - // will be returned to the user through the decode complete callback. - // - // Input: - // - inputImage : Encoded image to be decoded - // - missingFrames : True if one or more frames have been lost - // since the previous decode call. - // - codecSpecificInfo : Pointer to codec specific data - // - renderTimeMs : System time to render in milliseconds. Only - // used by decoders with internal rendering. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, bool missingFrames, const void* codecSpecificInfo = NULL, WebRtc_Word64 renderTimeMs = -1) = 0; - - // Register an decode complete callback object. - // - // Input: - // - callback : Callback object which handles decoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback* callback) = 0; - - // Free decoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Release() = 0; - - // Reset decoder state and prepare for a new call. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Reset() = 0; - - // Codec configuration data sent out-of-band, i.e. in SIP call setup - // - // Input/Output: - // - buffer : Buffer pointer to the configuration data - // - size : The size of the configuration data in - // bytes - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/, WebRtc_Word32 /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; } - - // Create a copy of the codec and its internal state. - // - // Return value : A copy of the instance if OK, NULL otherwise. - virtual VideoDecoder* Copy() { return NULL; } -}; - -} // namespace webrtc - -#endif // VIDEO_CODEC_INTERFACE_H diff --git a/modules/video_coding/codecs/interface/video_error_codes.h b/modules/video_coding/codecs/interface/video_error_codes.h deleted file mode 100644 index 257da1be6..000000000 --- a/modules/video_coding/codecs/interface/video_error_codes.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef VIDEO_ERROR_CODES_H -#define VIDEO_ERROR_CODES_H - -// NOTE: in sync with video_coding_module_defines.h - -// Define return values - -#define WEBRTC_VIDEO_CODEC_REQUEST_SLI 2 -#define WEBRTC_VIDEO_CODEC_OK 0 -#define WEBRTC_VIDEO_CODEC_ERROR -1 -#define WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED -2 -#define WEBRTC_VIDEO_CODEC_MEMORY -3 -#define WEBRTC_VIDEO_CODEC_ERR_PARAMETER -4 -#define WEBRTC_VIDEO_CODEC_ERR_SIZE -5 -#define WEBRTC_VIDEO_CODEC_TIMEOUT -6 -#define WEBRTC_VIDEO_CODEC_UNINITIALIZED -7 -#define WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI -12 - -#endif // VIDEO_ERROR_CODES_H diff --git a/modules/video_coding/codecs/test_framework/benchmark.cc b/modules/video_coding/codecs/test_framework/benchmark.cc deleted file mode 100644 index 22b13157c..000000000 --- a/modules/video_coding/codecs/test_framework/benchmark.cc +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "benchmark.h" -#include "video_source.h" -#include "vplib.h" -#include -#include -#include -#include -#include -#if defined(_WIN32) - #include -#endif -#include "event_wrapper.h" -#include "video_codec_interface.h" - -#define SSIM_CALC 0 // by default, don't compute SSIM - -using namespace webrtc; - -Benchmark::Benchmark() -: -_resultsFileName("../../../../testFiles/benchmark.txt"), -_codecName("Default"), -NormalAsyncTest("Benchmark", "Codec benchmark over a range of test cases", 6) -{ -} - -Benchmark::Benchmark(std::string name, std::string description) -: -_resultsFileName("../../../../testFiles/benchmark.txt"), -_codecName("Default"), -NormalAsyncTest(name, description, 6) -{ -} - -Benchmark::Benchmark(std::string name, std::string description, std::string resultsFileName, std::string codecName) -: -_resultsFileName(resultsFileName), -_codecName(codecName), -NormalAsyncTest(name, description, 6) -{ -} - -void -Benchmark::Perform() -{ - std::vector sources; - std::vector::iterator it; - - // Configuration -------------------------- - sources.push_back(new const VideoSource("test/testFiles/foreman_cif.yuv", kCIF)); - sources.push_back(new const VideoSource("test/testFiles/akiyo_cif.yuv", kCIF)); - - const VideoSize size[] = {kQCIF, kCIF}; - const int frameRate[] = {10, 15, 30}; - // Specifies the framerates for which to perform a speed test. - const bool speedTestMask[] = {false, false, false}; - const int bitRate[] = {50, 100, 200, 300, 400, 500, 600, 1000}; - // Determines the number of iterations to perform to arrive at the speed result. - enum { kSpeedTestIterations = 10 }; - // ---------------------------------------- - - const int nFrameRates = sizeof(frameRate)/sizeof(*frameRate); - assert(sizeof(speedTestMask)/sizeof(*speedTestMask) == nFrameRates); - const int nBitrates = sizeof(bitRate)/sizeof(*bitRate); - int testIterations = 10; - - double psnr[nBitrates]; - double ssim[nBitrates]; - double fps[nBitrates]; - double totalEncodeTime[nBitrates]; - double totalDecodeTime[nBitrates]; - - _results.open(_resultsFileName.c_str(), std::fstream::out); - _results << GetMagicStr() << std::endl; - _results << _codecName << std::endl; - - for (it = sources.begin() ; it < sources.end(); it++) - { - for (int i = 0; i < sizeof(size)/sizeof(*size); i++) - { - for (int j = 0; j < nFrameRates; j++) - { - std::stringstream ss; - std::string strFrameRate; - std::string outFileName; - ss << frameRate[j]; - ss >> strFrameRate; - outFileName = (*it)->GetFilePath() + "/" + (*it)->GetName() + "_" + - VideoSource::GetSizeString(size[i]) + "_" + strFrameRate + ".yuv"; - - _target = new const VideoSource(outFileName, size[i], frameRate[j]); - (*it)->Convert(*_target); - if (VideoSource::FileExists(outFileName.c_str())) - { - _inname = outFileName; - } - else - { - _inname = (*it)->GetFileName(); - } - - std::cout << (*it)->GetName() << ", " << VideoSource::GetSizeString(size[i]) - << ", " << frameRate[j] << " fps" << std::endl << "Bitrate [kbps]:"; - _results << (*it)->GetName() << "," << VideoSource::GetSizeString(size[i]) - << "," << frameRate[j] << " fps" << std::endl << "Bitrate [kbps]"; - - if (speedTestMask[j]) - { - testIterations = kSpeedTestIterations; - } - else - { - testIterations = 1; - } - - for (int k = 0; k < nBitrates; k++) - { - _bitRate = (bitRate[k]); - double avgFps = 0.0; - totalEncodeTime[k] = 0; - totalDecodeTime[k] = 0; - - for (int l = 0; l < testIterations; l++) - { - PerformNormalTest(); - _appendNext = false; - - avgFps += _framecnt / (_totalEncodeTime + _totalDecodeTime); - totalEncodeTime[k] += _totalEncodeTime; - totalDecodeTime[k] += _totalDecodeTime; - - } - avgFps /= testIterations; - totalEncodeTime[k] /= testIterations; - totalDecodeTime[k] /= testIterations; - - double actualBitRate = ActualBitRate(_framecnt) / 1000.0; - std::cout << " " << actualBitRate; - _results << "," << actualBitRate; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _inst.width, - _inst.height, &psnr[k]); - if (SSIM_CALC) - { - SSIMfromFiles(_inname.c_str(), _outname.c_str(), _inst.width, - _inst.height, &ssim[k]); - - } - fps[k] = avgFps; - } - std::cout << std::endl << "Y-PSNR [dB]:"; - _results << std::endl << "Y-PSNR [dB]"; - for (int k = 0; k < nBitrates; k++) - { - std::cout << " " << psnr[k]; - _results << "," << psnr[k]; - - } - if (SSIM_CALC) - { - std::cout << std::endl << "SSIM: "; - _results << std::endl << "SSIM "; - for (int k = 0; k < nBitrates; k++) - { - std::cout << " " << ssim[k]; - _results << "," << ssim[k]; - } - - } - - std::cout << std::endl << "Encode Time[ms]:"; - _results << std::endl << "Encode Time[ms]"; - for (int k = 0; k < nBitrates; k++) - { - std::cout << " " << totalEncodeTime[k]; - _results << "," << totalEncodeTime[k]; - - } - - std::cout << std::endl << "Decode Time[ms]:"; - _results << std::endl << "Decode Time[ms]"; - for (int k = 0; k < nBitrates; k++) - { - std::cout << " " << totalDecodeTime[k]; - _results << "," << totalDecodeTime[k]; - - } - - if (speedTestMask[j]) - { - std::cout << std::endl << "Speed [fps]:"; - _results << std::endl << "Speed [fps]"; - for (int k = 0; k < nBitrates; k++) - { - std::cout << " " << static_cast(fps[k] + 0.5); - _results << "," << static_cast(fps[k] + 0.5); - } - } - std::cout << std::endl << std::endl; - _results << std::endl << std::endl; - - delete _target; - } - } - delete *it; - } - _results.close(); -} - -void -Benchmark::PerformNormalTest() -{ - _encoder = GetNewEncoder(); - _decoder = GetNewDecoder(); - CodecSettings(_target->GetWidth(), _target->GetHeight(), _target->GetFrameRate(), _bitRate); - Setup(); - EventWrapper* waitEvent = EventWrapper::Create(); - - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _encoder->InitEncode(&_inst, 4, 1440); - CodecSpecific_InitBitrate(); - _decoder->InitDecode(&_inst,1); - - FrameQueue frameQueue; - VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this); - VideoDecodeCompleteCallback decCallback(_decodedFile, *this); - _encoder->RegisterEncodeCompleteCallback(&encCallback); - _decoder->RegisterDecodeCompleteCallback(&decCallback); - - SetCodecSpecificParameters(); - - _totalEncodeTime = _totalDecodeTime = 0; - _totalEncodePipeTime = _totalDecodePipeTime = 0; - bool complete = false; - _framecnt = 0; - _encFrameCnt = 0; - _sumEncBytes = 0; - _lengthEncFrame = 0; - while (!complete) - { - complete = Encode(); - if (!frameQueue.Empty() || complete) - { - while (!frameQueue.Empty()) - { - _frameToDecode = static_cast(frameQueue.PopFrame()); - DoPacketLoss(); - int ret = Decode(); - delete _frameToDecode; - _frameToDecode = NULL; - if (ret < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", ret); - exit(EXIT_FAILURE); - } - else if (ret == 0) - { - _framecnt++; - } - else - { - fprintf(stderr, "\n\nPositive return value from decode!\n\n"); - } - } - } - waitEvent->Wait(5); - } - - _inputVideoBuffer.Free(); - //_encodedVideoBuffer.Reset(); ? - _encodedVideoBuffer.Free(); - _decodedVideoBuffer.Free(); - - _encoder->Release(); - _decoder->Release(); - delete waitEvent; - delete _encoder; - delete _decoder; - Teardown(); -} - -void -Benchmark::CodecSpecific_InitBitrate() -{ - if (_bitRate == 0) - { - _encoder->SetRates(600, _inst.maxFramerate); - } - else - { - _encoder->SetRates(_bitRate, _inst.maxFramerate); - } -} - diff --git a/modules/video_coding/codecs/test_framework/benchmark.h b/modules/video_coding/codecs/test_framework/benchmark.h deleted file mode 100644 index 57806e59f..000000000 --- a/modules/video_coding/codecs/test_framework/benchmark.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ - -#include "normal_async_test.h" - -class VideoSource; - -class Benchmark : public NormalAsyncTest -{ -public: - Benchmark(); - virtual void Perform(); - -protected: - Benchmark(std::string name, std::string description); - Benchmark(std::string name, std::string description, std::string resultsFileName, std::string codecName); - virtual webrtc::VideoEncoder* GetNewEncoder() = 0; - virtual webrtc::VideoDecoder* GetNewDecoder() = 0; - virtual void PerformNormalTest(); - virtual void CodecSpecific_InitBitrate(); - static const char* GetMagicStr() { return "#!benchmark1.0"; } - - const VideoSource* _target; - std::string _resultsFileName; - std::ofstream _results; - std::string _codecName; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ - diff --git a/modules/video_coding/codecs/test_framework/exportfig.m b/modules/video_coding/codecs/test_framework/exportfig.m deleted file mode 100644 index d0d5ed9e4..000000000 --- a/modules/video_coding/codecs/test_framework/exportfig.m +++ /dev/null @@ -1,500 +0,0 @@ -function exportfig(varargin) -%EXPORTFIG Export a figure to Encapsulated Postscript. -% EXPORTFIG(H, FILENAME) writes the figure H to FILENAME. H is -% a figure handle and FILENAME is a string that specifies the -% name of the output file. -% -% EXPORTFIG(...,PARAM1,VAL1,PARAM2,VAL2,...) specifies -% parameters that control various characteristics of the output -% file. -% -% Format Paramter: -% 'Format' one of the strings 'eps','eps2','jpeg','png','preview' -% specifies the output format. Defaults to 'eps'. -% The output format 'preview' does not generate an output -% file but instead creates a new figure window with a -% preview of the exported figure. In this case the -% FILENAME parameter is ignored. -% -% 'Preview' one of the strings 'none', 'tiff' -% specifies a preview for EPS files. Defaults to 'none'. -% -% Size Parameters: -% 'Width' a positive scalar -% specifies the width in the figure's PaperUnits -% 'Height' a positive scalar -% specifies the height in the figure's PaperUnits -% -% Specifying only one dimension sets the other dimension -% so that the exported aspect ratio is the same as the -% figure's current aspect ratio. -% If neither dimension is specified the size defaults to -% the width and height from the figure's PaperPosition. -% -% Rendering Parameters: -% 'Color' one of the strings 'bw', 'gray', 'cmyk' -% 'bw' specifies that lines and text are exported in -% black and all other objects in grayscale -% 'gray' specifies that all objects are exported in grayscale -% 'cmyk' specifies that all objects are exported in color -% using the CMYK color space -% 'Renderer' one of the strings 'painters', 'zbuffer', 'opengl' -% specifies the renderer to use -% 'Resolution' a positive scalar -% specifies the resolution in dots-per-inch. -% -% The default color setting is 'bw'. -% -% Font Parameters: -% 'FontMode' one of the strings 'scaled', 'fixed' -% 'FontSize' a positive scalar -% in 'scaled' mode multiplies with the font size of each -% text object to obtain the exported font size -% in 'fixed' mode specifies the font size of all text -% objects in points -% 'FontEncoding' one of the strings 'latin1', 'adobe' -% specifies the character encoding of the font -% -% If FontMode is 'scaled' but FontSize is not specified then a -% scaling factor is computed from the ratio of the size of the -% exported figure to the size of the actual figure. The minimum -% font size allowed after scaling is 5 points. -% If FontMode is 'fixed' but FontSize is not specified then the -% exported font sizes of all text objects is 7 points. -% -% The default 'FontMode' setting is 'scaled'. -% -% Line Width Parameters: -% 'LineMode' one of the strings 'scaled', 'fixed' -% 'LineWidth' a positive scalar -% the semantics of LineMode and LineWidth are exactly the -% same as FontMode and FontSize, except that they apply -% to line widths instead of font sizes. The minumum line -% width allowed after scaling is 0.5 points. -% If LineMode is 'fixed' but LineWidth is not specified -% then the exported line width of all line objects is 1 -% point. -% -% Examples: -% exportfig(gcf,'fig1.eps','height',3); -% Exports the current figure to the file named 'fig1.eps' with -% a height of 3 inches (assuming the figure's PaperUnits is -% inches) and an aspect ratio the same as the figure's aspect -% ratio on screen. -% -% exportfig(gcf, 'fig2.eps', 'FontMode', 'fixed',... -% 'FontSize', 10, 'color', 'cmyk' ); -% Exports the current figure to 'fig2.eps' in color with all -% text in 10 point fonts. The size of the exported figure is -% the figure's PaperPostion width and height. - - -if (nargin < 2) - error('Too few input arguments'); -end - -% exportfig(H, filename, ...) -H = varargin{1}; -if ~ishandle(H) | ~strcmp(get(H,'type'), 'figure') - error('First argument must be a handle to a figure.'); -end -filename = varargin{2}; -if ~ischar(filename) - error('Second argument must be a string.'); -end -paramPairs = varargin(3:end); - -% Do some validity checking on param-value pairs -if (rem(length(paramPairs),2) ~= 0) - error(['Invalid input syntax. Optional parameters and values' ... - ' must be in pairs.']); -end - -format = 'eps'; -preview = 'none'; -width = -1; -height = -1; -color = 'bw'; -fontsize = -1; -fontmode='scaled'; -linewidth = -1; -linemode=[]; -fontencoding = 'latin1'; -renderer = []; -resolution = []; - -% Process param-value pairs -args = {}; -for k = 1:2:length(paramPairs) - param = lower(paramPairs{k}); - if (~ischar(param)) - error('Optional parameter names must be strings'); - end - value = paramPairs{k+1}; - - switch (param) - case 'format' - format = value; - if (~strcmp(format,{'eps','eps2','jpeg','png','preview'})) - error(['Format must be ''eps'', ''eps2'', ''jpeg'', ''png'' or' ... - ' ''preview''.']); - end - case 'preview' - preview = value; - if (~strcmp(preview,{'none','tiff'})) - error('Preview must be ''none'' or ''tiff''.'); - end - case 'width' - width = LocalToNum(value); - if(~LocalIsPositiveScalar(width)) - error('Width must be a numeric scalar > 0'); - end - case 'height' - height = LocalToNum(value); - if(~LocalIsPositiveScalar(height)) - error('Height must be a numeric scalar > 0'); - end - case 'color' - color = lower(value); - if (~strcmp(color,{'bw','gray','cmyk'})) - error('Color must be ''bw'', ''gray'' or ''cmyk''.'); - end - case 'fontmode' - fontmode = lower(value); - if (~strcmp(fontmode,{'scaled','fixed'})) - error('FontMode must be ''scaled'' or ''fixed''.'); - end - case 'fontsize' - fontsize = LocalToNum(value); - if(~LocalIsPositiveScalar(fontsize)) - error('FontSize must be a numeric scalar > 0'); - end - case 'fontencoding' - fontencoding = lower(value); - if (~strcmp(fontencoding,{'latin1','adobe'})) - error('FontEncoding must be ''latin1'' or ''adobe''.'); - end - case 'linemode' - linemode = lower(value); - if (~strcmp(linemode,{'scaled','fixed'})) - error('LineMode must be ''scaled'' or ''fixed''.'); - end - case 'linewidth' - linewidth = LocalToNum(value); - if(~LocalIsPositiveScalar(linewidth)) - error('LineWidth must be a numeric scalar > 0'); - end - case 'renderer' - renderer = lower(value); - if (~strcmp(renderer,{'painters','zbuffer','opengl'})) - error('Renderer must be ''painters'', ''zbuffer'' or ''opengl''.'); - end - case 'resolution' - resolution = LocalToNum(value); - if ~(isnumeric(value) & (prod(size(value)) == 1) & (value >= 0)); - error('Resolution must be a numeric scalar >= 0'); - end - otherwise - error(['Unrecognized option ' param '.']); - end -end - -allLines = findall(H, 'type', 'line'); -allText = findall(H, 'type', 'text'); -allAxes = findall(H, 'type', 'axes'); -allImages = findall(H, 'type', 'image'); -allLights = findall(H, 'type', 'light'); -allPatch = findall(H, 'type', 'patch'); -allSurf = findall(H, 'type', 'surface'); -allRect = findall(H, 'type', 'rectangle'); -allFont = [allText; allAxes]; -allColor = [allLines; allText; allAxes; allLights]; -allMarker = [allLines; allPatch; allSurf]; -allEdge = [allPatch; allSurf]; -allCData = [allImages; allPatch; allSurf]; - -old.objs = {}; -old.prop = {}; -old.values = {}; - -% Process format and preview parameter -showPreview = strcmp(format,'preview'); -if showPreview - format = 'png'; - filename = [tempName '.png']; -end -if strncmp(format,'eps',3) & ~strcmp(preview,'none') - args = {args{:}, ['-' preview]}; -end - -hadError = 0; -try - % Process size parameters - paperPos = get(H, 'PaperPosition'); - old = LocalPushOldData(old, H, 'PaperPosition', paperPos); - figureUnits = get(H, 'Units'); - set(H, 'Units', get(H,'PaperUnits')); - figurePos = get(H, 'Position'); - aspectRatio = figurePos(3)/figurePos(4); - set(H, 'Units', figureUnits); - if (width == -1) & (height == -1) - width = paperPos(3); - height = paperPos(4); - elseif (width == -1) - width = height * aspectRatio; - elseif (height == -1) - height = width / aspectRatio; - end - set(H, 'PaperPosition', [0 0 width height]); - paperPosMode = get(H, 'PaperPositionMode'); - old = LocalPushOldData(old, H, 'PaperPositionMode', paperPosMode); - set(H, 'PaperPositionMode', 'manual'); - - % Process rendering parameters - switch (color) - case {'bw', 'gray'} - if ~strcmp(color,'bw') & strncmp(format,'eps',3) - format = [format 'c']; - end - args = {args{:}, ['-d' format]}; - - %compute and set gray colormap - oldcmap = get(H,'Colormap'); - newgrays = 0.30*oldcmap(:,1) + 0.59*oldcmap(:,2) + 0.11*oldcmap(:,3); - newcmap = [newgrays newgrays newgrays]; - old = LocalPushOldData(old, H, 'Colormap', oldcmap); - set(H, 'Colormap', newcmap); - - %compute and set ColorSpec and CData properties - old = LocalUpdateColors(allColor, 'color', old); - old = LocalUpdateColors(allAxes, 'xcolor', old); - old = LocalUpdateColors(allAxes, 'ycolor', old); - old = LocalUpdateColors(allAxes, 'zcolor', old); - old = LocalUpdateColors(allMarker, 'MarkerEdgeColor', old); - old = LocalUpdateColors(allMarker, 'MarkerFaceColor', old); - old = LocalUpdateColors(allEdge, 'EdgeColor', old); - old = LocalUpdateColors(allEdge, 'FaceColor', old); - old = LocalUpdateColors(allCData, 'CData', old); - - case 'cmyk' - if strncmp(format,'eps',3) - format = [format 'c']; - args = {args{:}, ['-d' format], '-cmyk'}; - else - args = {args{:}, ['-d' format]}; - end - otherwise - error('Invalid Color parameter'); - end - if (~isempty(renderer)) - args = {args{:}, ['-' renderer]}; - end - if (~isempty(resolution)) | ~strncmp(format,'eps',3) - if isempty(resolution) - resolution = 0; - end - args = {args{:}, ['-r' int2str(resolution)]}; - end - - % Process font parameters - if (~isempty(fontmode)) - oldfonts = LocalGetAsCell(allFont,'FontSize'); - switch (fontmode) - case 'fixed' - oldfontunits = LocalGetAsCell(allFont,'FontUnits'); - old = LocalPushOldData(old, allFont, {'FontUnits'}, oldfontunits); - set(allFont,'FontUnits','points'); - if (fontsize == -1) - set(allFont,'FontSize',7); - else - set(allFont,'FontSize',fontsize); - end - case 'scaled' - if (fontsize == -1) - wscale = width/figurePos(3); - hscale = height/figurePos(4); - scale = min(wscale, hscale); - else - scale = fontsize; - end - newfonts = LocalScale(oldfonts,scale,5); - set(allFont,{'FontSize'},newfonts); - otherwise - error('Invalid FontMode parameter'); - end - % make sure we push the size after the units - old = LocalPushOldData(old, allFont, {'FontSize'}, oldfonts); - end - if strcmp(fontencoding,'adobe') & strncmp(format,'eps',3) - args = {args{:}, '-adobecset'}; - end - - % Process linewidth parameters - if (~isempty(linemode)) - oldlines = LocalGetAsCell(allMarker,'LineWidth'); - old = LocalPushOldData(old, allMarker, {'LineWidth'}, oldlines); - switch (linemode) - case 'fixed' - if (linewidth == -1) - set(allMarker,'LineWidth',1); - else - set(allMarker,'LineWidth',linewidth); - end - case 'scaled' - if (linewidth == -1) - wscale = width/figurePos(3); - hscale = height/figurePos(4); - scale = min(wscale, hscale); - else - scale = linewidth; - end - newlines = LocalScale(oldlines, scale, 0.5); - set(allMarker,{'LineWidth'},newlines); - otherwise - error('Invalid LineMode parameter'); - end - end - - % Export - print(H, filename, args{:}); - -catch - hadError = 1; -end - -% Restore figure settings -for n=1:length(old.objs) - set(old.objs{n}, old.prop{n}, old.values{n}); -end - -if hadError - error(deblank(lasterr)); -end - -% Show preview if requested -if showPreview - X = imread(filename,'png'); - delete(filename); - f = figure( 'Name', 'Preview', ... - 'Menubar', 'none', ... - 'NumberTitle', 'off', ... - 'Visible', 'off'); - image(X); - axis image; - ax = findobj(f, 'type', 'axes'); - set(ax, 'Units', get(H,'PaperUnits'), ... - 'Position', [0 0 width height], ... - 'Visible', 'off'); - set(ax, 'Units', 'pixels'); - axesPos = get(ax,'Position'); - figPos = get(f,'Position'); - rootSize = get(0,'ScreenSize'); - figPos(3:4) = axesPos(3:4); - if figPos(1) + figPos(3) > rootSize(3) - figPos(1) = rootSize(3) - figPos(3) - 50; - end - if figPos(2) + figPos(4) > rootSize(4) - figPos(2) = rootSize(4) - figPos(4) - 50; - end - set(f, 'Position',figPos, ... - 'Visible', 'on'); -end - -% -% Local Functions -% - -function outData = LocalPushOldData(inData, objs, prop, values) -outData.objs = {inData.objs{:}, objs}; -outData.prop = {inData.prop{:}, prop}; -outData.values = {inData.values{:}, values}; - -function cellArray = LocalGetAsCell(fig,prop); -cellArray = get(fig,prop); -if (~isempty(cellArray)) & (~iscell(cellArray)) - cellArray = {cellArray}; -end - -function newArray = LocalScale(inArray, scale, minValue) -n = length(inArray); -newArray = cell(n,1); -for k=1:n - newArray{k} = max(minValue,scale*inArray{k}(1)); -end - -function newArray = LocalMapToGray(inArray); -n = length(inArray); -newArray = cell(n,1); -for k=1:n - color = inArray{k}; - if (~isempty(color)) - if ischar(color) - switch color(1) - case 'y' - color = [1 1 0]; - case 'm' - color = [1 0 1]; - case 'c' - color = [0 1 1]; - case 'r' - color = [1 0 0]; - case 'g' - color = [0 1 0]; - case 'b' - color = [0 0 1]; - case 'w' - color = [1 1 1]; - case 'k' - color = [0 0 0]; - otherwise - newArray{k} = color; - end - end - if ~ischar(color) - color = 0.30*color(1) + 0.59*color(2) + 0.11*color(3); - end - end - if isempty(color) | ischar(color) - newArray{k} = color; - else - newArray{k} = [color color color]; - end -end - -function newArray = LocalMapCData(inArray); -n = length(inArray); -newArray = cell(n,1); -for k=1:n - color = inArray{k}; - if (ndims(color) == 3) & isa(color,'double') - gray = 0.30*color(:,:,1) + 0.59*color(:,:,2) + 0.11*color(:,:,3); - color(:,:,1) = gray; - color(:,:,2) = gray; - color(:,:,3) = gray; - end - newArray{k} = color; -end - -function outData = LocalUpdateColors(inArray, prop, inData) -value = LocalGetAsCell(inArray,prop); -outData.objs = {inData.objs{:}, inArray}; -outData.prop = {inData.prop{:}, {prop}}; -outData.values = {inData.values{:}, value}; -if (~isempty(value)) - if strcmp(prop,'CData') - value = LocalMapCData(value); - else - value = LocalMapToGray(value); - end - set(inArray,{prop},value); -end - -function bool = LocalIsPositiveScalar(value) -bool = isnumeric(value) & ... - prod(size(value)) == 1 & ... - value > 0; - -function value = LocalToNum(value) -if ischar(value) - value = str2num(value); -end diff --git a/modules/video_coding/codecs/test_framework/normal_async_test.cc b/modules/video_coding/codecs/test_framework/normal_async_test.cc deleted file mode 100644 index 9141cb006..000000000 --- a/modules/video_coding/codecs/test_framework/normal_async_test.cc +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "normal_async_test.h" -#include "typedefs.h" -#include -#include -#include -#include -#include "tick_util.h" - -using namespace webrtc; - -NormalAsyncTest::NormalAsyncTest() -: -_requestKeyFrame(false), -_testNo(1), -_appendNext(false), -_decFrameCnt(0), -_encFrameCnt(0), -_missingFrames(false), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_rttFrames(0), -_hasReceivedSLI(false), -_hasReceivedPLI(false), -_waitForKey(false), -NormalTest("Async Normal Test 1", "A test of normal execution of the codec", - _testNo) -{ -} - -NormalAsyncTest::NormalAsyncTest(WebRtc_UWord32 bitRate) -: -_requestKeyFrame(false), -_testNo(1), -_appendNext(false), -_decFrameCnt(0), -_encFrameCnt(0), -_missingFrames(false), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_rttFrames(0), -_hasReceivedSLI(false), -_hasReceivedPLI(false), -_waitForKey(false), -NormalTest("Async Normal Test 1", "A test of normal execution of the codec", - bitRate, _testNo) -{ -} - -NormalAsyncTest::NormalAsyncTest(std::string name, std::string description, - unsigned int testNo) -: -_requestKeyFrame(false), -_testNo(testNo), -_lengthEncFrame(0), -_appendNext(false), -_decFrameCnt(0), -_encFrameCnt(0), -_missingFrames(false), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_rttFrames(0), -_hasReceivedSLI(false), -_hasReceivedPLI(false), -_waitForKey(false), -NormalTest(name, description, _testNo) -{ -} - -NormalAsyncTest::NormalAsyncTest(std::string name, std::string description, - WebRtc_UWord32 bitRate, unsigned int testNo) -: -_requestKeyFrame(false), -_testNo(testNo), -_lengthEncFrame(0), -_appendNext(false), -_decFrameCnt(0), -_encFrameCnt(0), -_missingFrames(false), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_rttFrames(0), -_hasReceivedSLI(false), -_hasReceivedPLI(false), -_waitForKey(false), -NormalTest(name, description, bitRate, _testNo) -{ -} - -NormalAsyncTest::NormalAsyncTest(std::string name, std::string description, - WebRtc_UWord32 bitRate, unsigned int testNo, - unsigned int rttFrames) -: -_requestKeyFrame(false), -_testNo(testNo), -_lengthEncFrame(0), -_appendNext(false), -_decFrameCnt(0), -_encFrameCnt(0), -_missingFrames(false), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_rttFrames(rttFrames), -_hasReceivedSLI(false), -_hasReceivedPLI(false), -_waitForKey(false), -NormalTest(name, description, bitRate, _testNo) -{ -} - -void -NormalAsyncTest::Setup() -{ - Test::Setup(); - std::stringstream ss; - std::string strTestNo; - ss << _testNo; - ss >> strTestNo; - - // Check if settings exist. Otherwise use defaults. - if (_outname == "") - { - _outname = "../../out_normaltest" + strTestNo + ".yuv"; - } - - if (_encodedName == "") - { - _encodedName = "../../encoded_normaltest" + strTestNo + ".yuv"; - } - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - - char mode[3] = "wb"; - if (_appendNext) - { - strncpy(mode, "ab", 3); - } - - if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL) - { - printf("Cannot write file %s.\n", _outname.c_str()); - exit(1); - } - - _appendNext = true; -} - -void -NormalAsyncTest::Teardown() -{ - Test::Teardown(); - fclose(_sourceFile); - fclose(_decodedFile); -} - -FrameQueueTuple::~FrameQueueTuple() -{ - if (_codecSpecificInfo != NULL) - { - // TODO(holmer): implement virtual function for deleting this and - // remove warnings - delete _codecSpecificInfo; - } - if (_frame != NULL) - { - delete _frame; - } -} - -void FrameQueue::PushFrame(TestVideoEncodedBuffer *frame, - void* codecSpecificInfo) -{ - WriteLockScoped cs(_queueRWLock); - _frameBufferQueue.push(new FrameQueueTuple(frame, codecSpecificInfo)); -} - -FrameQueueTuple* FrameQueue::PopFrame() -{ - WriteLockScoped cs(_queueRWLock); - if (_frameBufferQueue.empty()) - { - return NULL; - } - FrameQueueTuple* tuple = _frameBufferQueue.front(); - _frameBufferQueue.pop(); - return tuple; -} - -bool FrameQueue::Empty() -{ - ReadLockScoped cs(_queueRWLock); - return _frameBufferQueue.empty(); -} - -WebRtc_UWord32 VideoEncodeCompleteCallback::EncodedBytes() -{ - return _encodedBytes; -} - -WebRtc_Word32 -VideoEncodeCompleteCallback::Encoded(EncodedImage& encodedImage, - const void* codecSpecificInfo, - const webrtc::RTPFragmentationHeader* - fragmentation) -{ - _test.Encoded(encodedImage); - TestVideoEncodedBuffer *newBuffer = new TestVideoEncodedBuffer(); - //newBuffer->VerifyAndAllocate(encodedImage._length); - newBuffer->VerifyAndAllocate(encodedImage._size); - _encodedBytes += encodedImage._length; - // If _frameQueue would have been a fixed sized buffer we could have asked - // it for an empty frame and then just do: - // emptyFrame->SwapBuffers(encodedBuffer); - // This is how it should be done in Video Engine to save in on memcpys - void* codecSpecificInfoCopy = - _test.CopyCodecSpecificInfo(codecSpecificInfo); - _test.CopyEncodedImage(*newBuffer, encodedImage, codecSpecificInfoCopy); - if (_encodedFile != NULL) - { - fwrite(newBuffer->GetBuffer(), 1, newBuffer->GetLength(), _encodedFile); - } - _frameQueue->PushFrame(newBuffer, codecSpecificInfoCopy); - return 0; -} - -WebRtc_UWord32 VideoDecodeCompleteCallback::DecodedBytes() -{ - return _decodedBytes; -} - -WebRtc_Word32 -VideoDecodeCompleteCallback::Decoded(RawImage& image) -{ - _test.Decoded(image); - _decodedBytes += image._length; - if (_decodedFile != NULL) - { - fwrite(image._buffer, 1, image._length, _decodedFile); - } - return 0; -} - -WebRtc_Word32 -VideoDecodeCompleteCallback::ReceivedDecodedReferenceFrame( - const WebRtc_UWord64 pictureId) -{ - return _test.ReceivedDecodedReferenceFrame(pictureId); -} - -WebRtc_Word32 -VideoDecodeCompleteCallback::ReceivedDecodedFrame( - const WebRtc_UWord64 pictureId) -{ - return _test.ReceivedDecodedFrame(pictureId); -} - -void -NormalAsyncTest::Encoded(const EncodedImage& encodedImage) -{ - _encodeCompleteTime = tGetTime(); - _encFrameCnt++; - _totalEncodePipeTime += _encodeCompleteTime - - _encodeTimes[encodedImage._timeStamp]; -} - -void -NormalAsyncTest::Decoded(const RawImage& decodedImage) -{ - _decodeCompleteTime = tGetTime(); - _decFrameCnt++; - _totalDecodePipeTime += _decodeCompleteTime - - _decodeTimes[decodedImage._timeStamp]; - _decodedWidth = decodedImage._width; - _decodedHeight = decodedImage._height; -} - -void -NormalAsyncTest::Perform() -{ - _inname = "test/testFiles/foreman_cif.yuv"; - CodecSettings(352, 288, 30, _bitRate); - Setup(); - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - if(_encoder->InitEncode(&_inst, 1, 1440) < 0) - { - exit(EXIT_FAILURE); - } - _decoder->InitDecode(&_inst, 1); - FrameQueue frameQueue; - VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this); - VideoDecodeCompleteCallback decCallback(_decodedFile, *this); - _encoder->RegisterEncodeCompleteCallback(&encCallback); - _decoder->RegisterDecodeCompleteCallback(&decCallback); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - _totalEncodeTime = _totalDecodeTime = 0; - _totalEncodePipeTime = _totalDecodePipeTime = 0; - bool complete = false; - _framecnt = 0; - _encFrameCnt = 0; - _decFrameCnt = 0; - _sumEncBytes = 0; - _lengthEncFrame = 0; - double starttime = tGetTime(); - while (!complete) - { - CodecSpecific_InitBitrate(); - complete = Encode(); - if (!frameQueue.Empty() || complete) - { - while (!frameQueue.Empty()) - { - _frameToDecode = - static_cast(frameQueue.PopFrame()); - int lost = DoPacketLoss(); - if (lost == 2) - { - // Lost the whole frame, continue - _missingFrames = true; - delete _frameToDecode; - _frameToDecode = NULL; - continue; - } - int ret = Decode(lost); - delete _frameToDecode; - _frameToDecode = NULL; - if (ret < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", ret); - exit(EXIT_FAILURE); - } - else if (ret == 0) - { - _framecnt++; - } - else - { - fprintf(stderr, - "\n\nPositive return value from decode!\n\n"); - } - } - } - } - double endtime = tGetTime(); - double totalExecutionTime = endtime - starttime; - printf("Total execution time: %.1f s\n", totalExecutionTime); - _sumEncBytes = encCallback.EncodedBytes(); - double actualBitRate = ActualBitRate(_encFrameCnt) / 1000.0; - double avgEncTime = _totalEncodeTime / _encFrameCnt; - double avgDecTime = _totalDecodeTime / _decFrameCnt; - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Average encode time: %.1f ms\n", 1000 * avgEncTime); - printf("Average decode time: %.1f ms\n", 1000 * avgDecTime); - printf("Average encode pipeline time: %.1f ms\n", - 1000 * _totalEncodePipeTime / _encFrameCnt); - printf("Average decode pipeline time: %.1f ms\n", - 1000 * _totalDecodePipeTime / _decFrameCnt); - printf("Number of encoded frames: %u\n", _encFrameCnt); - printf("Number of decoded frames: %u\n", _decFrameCnt); - (*_log) << "Actual bitrate: " << actualBitRate << " kbps\tTarget: " << - _bitRate << " kbps" << std::endl; - (*_log) << "Average encode time: " << avgEncTime << " s" << std::endl; - (*_log) << "Average decode time: " << avgDecTime << " s" << std::endl; - _encoder->Release(); - _decoder->Release(); - Teardown(); -} - -bool -NormalAsyncTest::Encode() -{ - _lengthEncFrame = 0; - fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile); - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); - _inputVideoBuffer.SetTimeStamp((unsigned int) - (_encFrameCnt * 9e4 / _inst.maxFramerate)); - _inputVideoBuffer.SetWidth(_inst.width); - _inputVideoBuffer.SetHeight(_inst.height); - RawImage rawImage; - VideoBufferToRawImage(_inputVideoBuffer, rawImage); - if (feof(_sourceFile) != 0) - { - return true; - } - _encodeCompleteTime = 0; - _encodeTimes[rawImage._timeStamp] = tGetTime(); - VideoFrameType frameType = kDeltaFrame; - - // check SLI queue - _hasReceivedSLI = false; - while (!_signalSLI.empty() && _signalSLI.front().delay == 0) - { - // SLI message has arrived at sender side - _hasReceivedSLI = true; - _pictureIdSLI = _signalSLI.front().id; - _signalSLI.pop_front(); - } - // decrement SLI queue times - for (std::list::iterator it = _signalSLI.begin(); - it !=_signalSLI.end(); it++) - { - (*it).delay--; - } - - // check PLI queue - _hasReceivedPLI = false; - while (!_signalPLI.empty() && _signalPLI.front().delay == 0) - { - // PLI message has arrived at sender side - _hasReceivedPLI = true; - _signalPLI.pop_front(); - } - // decrement PLI queue times - for (std::list::iterator it = _signalPLI.begin(); - it != _signalPLI.end(); it++) - { - (*it).delay--; - } - - if (_hasReceivedPLI) - { - // respond to PLI by encoding a key frame - frameType = kKeyFrame; - _hasReceivedPLI = false; - _hasReceivedSLI = false; // don't trigger both at once - } - - void* codecSpecificInfo = CreateEncoderSpecificInfo(); - int ret = _encoder->Encode(rawImage, codecSpecificInfo, frameType); - if (codecSpecificInfo != NULL) - { - // TODO(holmer): implement virtual function for deleting this and - // remove warnings - delete codecSpecificInfo; - codecSpecificInfo = NULL; - } - if (_encodeCompleteTime > 0) - { - _totalEncodeTime += _encodeCompleteTime - - _encodeTimes[rawImage._timeStamp]; - } - else - { - _totalEncodeTime += tGetTime() - _encodeTimes[rawImage._timeStamp]; - } - assert(ret >= 0); - return false; -} - -int -NormalAsyncTest::Decode(int lossValue) -{ - _sumEncBytes += _frameToDecode->_frame->GetLength(); - double starttime = 0; - EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage); - encodedImage._completeFrame = !lossValue; - _decodeCompleteTime = 0; - _decodeTimes[encodedImage._timeStamp] = tGetTime(); - int ret = WEBRTC_VIDEO_CODEC_OK; - if (!_waitForKey || encodedImage._frameType == kKeyFrame) - { - _waitForKey = false; - ret = _decoder->Decode(encodedImage, _missingFrames, - _frameToDecode->_codecSpecificInfo); - - if (ret >= 0) - { - _missingFrames = false; - } - } - - // check for SLI - if (ret == WEBRTC_VIDEO_CODEC_REQUEST_SLI) - { - // add an SLI feedback to the feedback "queue" - // to be delivered to encoder with _rttFrames delay - _signalSLI.push_back(fbSignal(_rttFrames, - static_cast((_lastDecPictureId) & 0x3f))); // 6 lsb - - ret = WEBRTC_VIDEO_CODEC_OK; - } - else if (ret == WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI) - { - // add an SLI feedback to the feedback "queue" - // to be delivered to encoder with _rttFrames delay - _signalSLI.push_back(fbSignal(_rttFrames, - static_cast((_lastDecPictureId + 1) & 0x3f)));//6 lsb - - ret = WEBRTC_VIDEO_CODEC_OK; - } - else if (ret == WEBRTC_VIDEO_CODEC_ERROR) - { - // wait for new key frame - // add an PLI feedback to the feedback "queue" - // to be delivered to encoder with _rttFrames delay - _signalPLI.push_back(fbSignal(_rttFrames, 0 /* picId not used*/)); - _waitForKey = true; - - ret = WEBRTC_VIDEO_CODEC_OK; - } - - if (_decodeCompleteTime > 0) - { - _totalDecodeTime += _decodeCompleteTime - - _decodeTimes[encodedImage._timeStamp]; - } - else - { - _totalDecodeTime += tGetTime() - _decodeTimes[encodedImage._timeStamp]; - } - return ret; -} - -void NormalAsyncTest::CodecSpecific_InitBitrate() -{ - if (_bitRate == 0) - { - _encoder->SetRates(600, _inst.maxFramerate); - } - else - { - _encoder->SetRates(_bitRate, _inst.maxFramerate); - } -} - -void NormalAsyncTest::CopyEncodedImage(TestVideoEncodedBuffer& dest, - EncodedImage& src, - void* /*codecSpecificInfo*/) const -{ - dest.CopyBuffer(src._length, src._buffer); - dest.SetFrameType(src._frameType); - dest.SetCaptureWidth((WebRtc_UWord16)src._encodedWidth); - dest.SetCaptureHeight((WebRtc_UWord16)src._encodedHeight); - dest.SetTimeStamp(src._timeStamp); -} -double -NormalAsyncTest::tGetTime() -{// return time in sec - return ((double) (TickTime::MillisecondTimestamp())/1000); - } diff --git a/modules/video_coding/codecs/test_framework/normal_async_test.h b/modules/video_coding/codecs/test_framework/normal_async_test.h deleted file mode 100644 index 65506c393..000000000 --- a/modules/video_coding/codecs/test_framework/normal_async_test.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_ - -#include "common_types.h" - -#include "normal_test.h" -#include "rw_lock_wrapper.h" -#include -#include -#include - -class FrameQueueTuple -{ -public: - FrameQueueTuple(TestVideoEncodedBuffer *frame, - const void* codecSpecificInfo = NULL) - : - _frame(frame), - _codecSpecificInfo(codecSpecificInfo) - {}; - ~FrameQueueTuple(); - TestVideoEncodedBuffer* _frame; - const void* _codecSpecificInfo; -}; - -class FrameQueue -{ -public: - FrameQueue() - : - _queueRWLock(*webrtc::RWLockWrapper::CreateRWLock()), - _prevTS(-1) - { - } - - ~FrameQueue() - { - delete &_queueRWLock; - } - - void PushFrame(TestVideoEncodedBuffer *frame, - void* codecSpecificInfo = NULL); - FrameQueueTuple* PopFrame(); - bool Empty(); - -private: - webrtc::RWLockWrapper& _queueRWLock; - std::queue _frameBufferQueue; - WebRtc_Word64 _prevTS; -}; - -// feedback signal to encoder -struct fbSignal -{ - fbSignal(int d, WebRtc_UWord8 pid) : delay(d), id(pid) {}; - int delay; - WebRtc_UWord8 id; -}; - -class NormalAsyncTest : public NormalTest -{ -public: - NormalAsyncTest(); - NormalAsyncTest(WebRtc_UWord32 bitRate); - NormalAsyncTest(std::string name, std::string description, - unsigned int testNo); - NormalAsyncTest(std::string name, std::string description, - WebRtc_UWord32 bitRate, unsigned int testNo); - NormalAsyncTest(std::string name, std::string description, - WebRtc_UWord32 bitRate, unsigned int testNo, - unsigned int rttFrames); - virtual ~NormalAsyncTest() {}; - virtual void Perform(); - virtual void Encoded(const webrtc::EncodedImage& encodedImage); - virtual void Decoded(const webrtc::RawImage& decodedImage); - virtual void* - CopyCodecSpecificInfo(const void* /*codecSpecificInfo */) const - { return NULL; }; - virtual void CopyEncodedImage(TestVideoEncodedBuffer& dest, - webrtc::EncodedImage& src, - void* /*codecSpecificInfo*/) const; - virtual void* CreateEncoderSpecificInfo() const { return NULL; }; - virtual WebRtc_Word32 - ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) { return 0;}; - virtual WebRtc_Word32 - ReceivedDecodedFrame(const WebRtc_UWord64 pictureId) { return 0;}; - -protected: - virtual void Setup(); - virtual void Teardown(); - virtual bool Encode(); - virtual int Decode(int lossValue = 0); - virtual void CodecSpecific_InitBitrate(); - virtual int SetCodecSpecificParameters() {return 0;}; - double tGetTime();// return time in sec - - FILE* _sourceFile; - FILE* _decodedFile; - WebRtc_UWord32 _decodedWidth; - WebRtc_UWord32 _decodedHeight; - double _totalEncodeTime; - double _totalDecodeTime; - double _decodeCompleteTime; - double _encodeCompleteTime; - double _totalEncodePipeTime; - double _totalDecodePipeTime; - int _framecnt; - int _encFrameCnt; - int _decFrameCnt; - bool _requestKeyFrame; - unsigned int _testNo; - unsigned int _lengthEncFrame; - FrameQueueTuple* _frameToDecode; - bool _appendNext; - std::map _encodeTimes; - std::map _decodeTimes; - bool _missingFrames; - std::list _signalSLI; - int _rttFrames; - mutable bool _hasReceivedSLI; - WebRtc_UWord8 _pictureIdSLI; - WebRtc_UWord64 _lastDecPictureId; - std::list _signalPLI; - bool _hasReceivedPLI; - bool _waitForKey; -}; - -class VideoEncodeCompleteCallback : public webrtc::EncodedImageCallback -{ -public: - VideoEncodeCompleteCallback(FILE* encodedFile, FrameQueue *frameQueue, - NormalAsyncTest& test) - : - _encodedFile(encodedFile), - _frameQueue(frameQueue), - _test(test), - _encodedBytes(0) - {} - - WebRtc_Word32 - Encoded(webrtc::EncodedImage& encodedImage, - const void* codecSpecificInfo = NULL, - const webrtc::RTPFragmentationHeader* fragmentation = NULL); - WebRtc_UWord32 EncodedBytes(); -private: - FILE* _encodedFile; - FrameQueue* _frameQueue; - NormalAsyncTest& _test; - WebRtc_UWord32 _encodedBytes; -}; - -class VideoDecodeCompleteCallback : public webrtc::DecodedImageCallback -{ -public: - VideoDecodeCompleteCallback(FILE* decodedFile, NormalAsyncTest& test) - : - _decodedFile(decodedFile), - _test(test), - _decodedBytes(0) - {} - - virtual WebRtc_Word32 Decoded(webrtc::RawImage& decodedImage); - virtual WebRtc_Word32 - ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId); - virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId); - - WebRtc_UWord32 DecodedBytes(); -private: - FILE* _decodedFile; - NormalAsyncTest& _test; - WebRtc_UWord32 _decodedBytes; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_ASYNC_TEST_H_ diff --git a/modules/video_coding/codecs/test_framework/normal_test.cc b/modules/video_coding/codecs/test_framework/normal_test.cc deleted file mode 100644 index 917eb0c3d..000000000 --- a/modules/video_coding/codecs/test_framework/normal_test.cc +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "normal_test.h" -#include -#include -#include - -NormalTest::NormalTest() -: -_testNo(1), -_lengthEncFrame(0), -_appendNext(false), -Test("Normal Test 1", "A test of normal execution of the codec") -{ -} - -NormalTest::NormalTest(std::string name, std::string description, unsigned int testNo) -: -_requestKeyFrame(false), -_testNo(testNo), -_lengthEncFrame(0), -_appendNext(false), -Test(name, description) -{ -} - -NormalTest::NormalTest(std::string name, std::string description, WebRtc_UWord32 bitRate, unsigned int testNo) -: -_requestKeyFrame(false), -_testNo(testNo), -_lengthEncFrame(0), -_appendNext(false), -Test(name, description, bitRate) -{ -} - -void -NormalTest::Setup() -{ - Test::Setup(); - std::stringstream ss; - std::string strTestNo; - ss << _testNo; - ss >> strTestNo; - - // Check if settings exist. Otherwise use defaults. - if (_outname == "") - { - _outname = "../../out_normaltest" + strTestNo + ".yuv"; - } - - if (_encodedName == "") - { - _encodedName = "../../encoded_normaltest" + strTestNo + ".yuv"; - } - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - - char mode[3] = "wb"; - if (_appendNext) - { - strncpy(mode, "ab", 3); - } - - if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL) - { - printf("Cannot write file %s.\n", _outname.c_str()); - exit(1); - } - - _appendNext = true; -} - -void -NormalTest::Teardown() -{ - Test::Teardown(); - fclose(_sourceFile); - fclose(_decodedFile); -} - -void -NormalTest::Perform() -{ - _inname = "../../../../testFiles/foreman.yuv"; - CodecSettings(352, 288, 30, _bitRate); - Setup(); - - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - - _encoder->InitEncode(&_inst, 1, 1460); - CodecSpecific_InitBitrate(); - _decoder->InitDecode(&_inst,1); - - _totalEncodeTime = _totalDecodeTime = 0; - _framecnt = 0; - _sumEncBytes = 0; - _lengthEncFrame = 0; - int decodeLength = 0; - while (!Encode()) - { - DoPacketLoss(); - _encodedVideoBuffer.UpdateLength(_encodedVideoBuffer.GetLength()); - fwrite(_encodedVideoBuffer.GetBuffer(), 1, _encodedVideoBuffer.GetLength(), _encodedFile); - decodeLength = Decode(); - if (decodeLength < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", decodeLength); - exit(EXIT_FAILURE); - } - fwrite(_decodedVideoBuffer.GetBuffer(), 1, decodeLength, _decodedFile); - CodecSpecific_InitBitrate(); - _framecnt++; - } - - // Ensure we empty the decoding queue. - while (decodeLength > 0) - { - decodeLength = Decode(); - if (decodeLength < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", decodeLength); - exit(EXIT_FAILURE); - } - fwrite(_decodedVideoBuffer.GetBuffer(), 1, decodeLength, _decodedFile); - } - - double actualBitRate = ActualBitRate(_framecnt) / 1000.0; - double avgEncTime = _totalEncodeTime / _framecnt; - double avgDecTime = _totalDecodeTime / _framecnt; - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Average encode time: %f s\n", avgEncTime); - printf("Average decode time: %f s\n", avgDecTime); - (*_log) << "Actual bitrate: " << actualBitRate << " kbps\tTarget: " << _bitRate << " kbps" << std::endl; - (*_log) << "Average encode time: " << avgEncTime << " s" << std::endl; - (*_log) << "Average decode time: " << avgDecTime << " s" << std::endl; - - _inputVideoBuffer.Free(); - _encodedVideoBuffer.Reset(); - _decodedVideoBuffer.Free(); - - _encoder->Release(); - _decoder->Release(); - - Teardown(); -} - -bool -NormalTest::Encode() -{ - _lengthEncFrame = 0; - fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile); - if (feof(_sourceFile) != 0) - { - return true; - } - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); - _inputVideoBuffer.SetTimeStamp(_framecnt); - - // This multiple attempt ridiculousness is to accomodate VP7: - // 1. The wrapper can unilaterally reduce the framerate for low bitrates. - // 2. The codec inexplicably likes to reject some frames. Perhaps there - // is a good reason for this... - int encodingAttempts = 0; - double starttime = 0; - double endtime = 0; - while (_lengthEncFrame == 0) - { - starttime = clock()/(double)CLOCKS_PER_SEC; - - _inputVideoBuffer.SetWidth(_inst.width); - _inputVideoBuffer.SetHeight(_inst.height); - //_lengthEncFrame = _encoder->Encode(_inputVideoBuffer, _encodedVideoBuffer, _frameInfo, - // _inst.frameRate, _requestKeyFrame && !(_framecnt%50)); - - endtime = clock()/(double)CLOCKS_PER_SEC; - - _encodedVideoBuffer.SetCaptureHeight(_inst.height); - _encodedVideoBuffer.SetCaptureWidth(_inst.width); - if (_lengthEncFrame < 0) - { - (*_log) << "Error in encoder: " << _lengthEncFrame << std::endl; - fprintf(stderr,"\n\nError in encoder: %d\n\n", _lengthEncFrame); - exit(EXIT_FAILURE); - } - _sumEncBytes += _lengthEncFrame; - - encodingAttempts++; - if (encodingAttempts > 50) - { - (*_log) << "Unable to encode frame: " << _framecnt << std::endl; - fprintf(stderr,"\n\nUnable to encode frame: %d\n\n", _framecnt); - exit(EXIT_FAILURE); - } - } - _totalEncodeTime += endtime - starttime; - - if (encodingAttempts > 1) - { - (*_log) << encodingAttempts << " attempts required to encode frame: " << - _framecnt + 1 << std::endl; - fprintf(stderr,"\n%d attempts required to encode frame: %d\n", encodingAttempts, - _framecnt + 1); - } - - return false; -} - -int -NormalTest::Decode() -{ - double starttime = clock()/(double)CLOCKS_PER_SEC; - _encodedVideoBuffer.SetWidth(_inst.width); - _encodedVideoBuffer.SetHeight(_inst.height); - int lengthDecFrame = 0; - //int lengthDecFrame = _decoder->Decode(_encodedVideoBuffer, _decodedVideoBuffer); - //_totalDecodeTime += (double)((clock()/(double)CLOCKS_PER_SEC) - starttime); - if (lengthDecFrame < 0) - { - return lengthDecFrame; - } - _encodedVideoBuffer.Reset(); - _encodedVideoBuffer.UpdateLength(0); - return lengthDecFrame; -} - diff --git a/modules/video_coding/codecs/test_framework/normal_test.h b/modules/video_coding/codecs/test_framework/normal_test.h deleted file mode 100644 index c7bb676f6..000000000 --- a/modules/video_coding/codecs/test_framework/normal_test.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_ - -#include "test.h" - -class NormalTest : public Test -{ -public: - NormalTest(); - NormalTest(std::string name, std::string description, unsigned int testNo); - NormalTest(std::string name, std::string description, WebRtc_UWord32 bitRate, unsigned int testNo); - virtual ~NormalTest() {}; - virtual void Perform(); - -protected: - virtual void Setup(); - virtual void Teardown(); - virtual bool Encode(); - virtual int Decode(); - virtual void CodecSpecific_InitBitrate()=0; - virtual int DoPacketLoss() {return 0;}; - - FILE* _sourceFile; - FILE* _decodedFile; - FILE* _encodedFile; - double _totalEncodeTime; - double _totalDecodeTime; - unsigned int _framecnt; - bool _requestKeyFrame; - unsigned int _testNo; - int _lengthEncFrame; - bool _appendNext; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_NORMAL_TEST_H_ - diff --git a/modules/video_coding/codecs/test_framework/packet_loss_test.cc b/modules/video_coding/codecs/test_framework/packet_loss_test.cc deleted file mode 100644 index 5b7f9c436..000000000 --- a/modules/video_coding/codecs/test_framework/packet_loss_test.cc +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "packet_loss_test.h" -#include "video_source.h" -#include -#include -#include - -using namespace webrtc; - -PacketLossTest::PacketLossTest() -: -_lossRate(0.1), -_lossProbability(0.1), -_lastFrame(NULL), -_lastFrameLength(0), -NormalAsyncTest("PacketLossTest", "Encode, remove lost packets, decode", 300, 5) -{ -} - -PacketLossTest::PacketLossTest(std::string name, std::string description) -: -_lossRate(0.1), -_lossProbability(0.1), -_lastFrame(NULL), -_lastFrameLength(0), -NormalAsyncTest(name, description, 300, 5) -{ -} - -PacketLossTest::PacketLossTest(std::string name, std::string description, double lossRate, bool useNack, unsigned int rttFrames /* = 0*/) -: -_lossRate(lossRate), -_lastFrame(NULL), -_lastFrameLength(0), -NormalAsyncTest(name, description, 300, 5, rttFrames) -{ - assert(lossRate >= 0 && lossRate <= 1); - if (useNack) - { - _lossProbability = 0; - } - else - { - _lossProbability = lossRate; - } -} - -void -PacketLossTest::Encoded(const EncodedImage& encodedImage) -{ - // push timestamp to queue - _frameQueue.push_back(encodedImage._timeStamp); - NormalAsyncTest::Encoded(encodedImage); -} - -void -PacketLossTest::Decoded(const RawImage& decodedImage) -{ - // check the frame queue if any frames have gone missing - assert(!_frameQueue.empty()); // decoded frame is not in the queue - while(_frameQueue.front() < decodedImage._timeStamp) - { - // this frame is missing - // write previous decoded frame again (frame freeze) - if (_decodedFile && _lastFrame) - { - fwrite(_lastFrame, 1, _lastFrameLength, _decodedFile); - } - - // remove frame from queue - _frameQueue.pop_front(); - } - assert(_frameQueue.front() == decodedImage._timeStamp); // decoded frame is not in the queue - - // pop the current frame - _frameQueue.pop_front(); - - // save image for future freeze-frame - if (_lastFrameLength < decodedImage._length) - { - if (_lastFrame) delete [] _lastFrame; - - _lastFrame = new WebRtc_UWord8[decodedImage._length]; - } - memcpy(_lastFrame, decodedImage._buffer, decodedImage._length); - _lastFrameLength = decodedImage._length; - - NormalAsyncTest::Decoded(decodedImage); -} - -void -PacketLossTest::Teardown() -{ - if (_totalKept + _totalThrown > 0) - { - printf("Target packet loss rate: %.4f\n", _lossProbability); - printf("Actual packet loss rate: %.4f\n", (_totalThrown * 1.0f) / (_totalKept + _totalThrown)); - printf("Channel rate: %.2f kbps\n", - 0.001 * 8.0 * _sumChannelBytes / ((_framecnt * 1.0f) / _inst.maxFramerate)); - } - else - { - printf("No packet losses inflicted\n"); - } - - NormalAsyncTest::Teardown(); -} - -void -PacketLossTest::Setup() -{ - const VideoSource source(_inname, _inst.width, _inst.height, _inst.maxFramerate); - - std::stringstream ss; - std::string lossRateStr; - ss << _lossRate; - ss >> lossRateStr; - _encodedName = "../../" + source.GetName() + "-" + lossRateStr; - _outname = "../../out-" + source.GetName() + "-" + lossRateStr; - - if (_lossProbability != _lossRate) - { - _encodedName += "-nack"; - _outname += "-nack"; - } - _encodedName += ".vp8"; - _outname += ".yuv"; - - _totalKept = 0; - _totalThrown = 0; - _sumChannelBytes = 0; - - NormalAsyncTest::Setup(); -} - -void -PacketLossTest::CodecSpecific_InitBitrate() -{ - assert(_bitRate > 0); - WebRtc_UWord32 simulatedBitRate; - if (_lossProbability != _lossRate) - { - // Simulating NACK - simulatedBitRate = WebRtc_UWord32(_bitRate / (1 + _lossRate)); - } - else - { - simulatedBitRate = _bitRate; - } - _encoder->SetPacketLoss((WebRtc_UWord32)(_lossProbability * 255.0)); - _encoder->SetRates(simulatedBitRate, _inst.maxFramerate); -} - -int PacketLossTest::DoPacketLoss() -{ - // Only packet loss for delta frames - if (_frameToDecode->_frame->GetLength() == 0 || _frameToDecode->_frame->GetFrameType() != kDeltaFrame) - { - _sumChannelBytes += _frameToDecode->_frame->GetLength(); - return 0; - } - //printf("Encoded: %d bytes\n", _encodedVideoBuffer.GetLength()); - unsigned char *packet = NULL; - TestVideoEncodedBuffer newEncBuf; - newEncBuf.VerifyAndAllocate(_lengthSourceFrame); - _inBufIdx = 0; - _outBufIdx = 0; - int size = 1; - int kept = 0; - int thrown = 0; - int count = 0; - while ((size = NextPacket(1500, &packet)) > 0) - { - if (!PacketLoss(_lossProbability)) - { - InsertPacket(&newEncBuf, packet, size); - kept++; - } - else - { - // Use the ByteLoss function if you want to lose only - // parts of a packet, and not the whole packet. - - //int size2 = ByteLoss(size, packet, 15); - thrown++; - //if (size2 != size) - //{ - // InsertPacket(&newEncBuf, packet, size2); - //} - } - } - int lossResult = (thrown!=0); // 0 = no loss 1 = loss(es) - if (lossResult) - { - lossResult += (kept==0); // 2 = all lost = full frame - } - _frameToDecode->_frame->CopyBuffer(newEncBuf.GetLength(), newEncBuf.GetBuffer()); - _sumChannelBytes += newEncBuf.GetLength(); - _totalKept += kept; - _totalThrown += thrown; - return lossResult; - //printf("Threw away: %d out of %d packets\n", thrown, thrown + kept); - //printf("Encoded left: %d bytes\n", _encodedVideoBuffer.GetLength()); -} - -int PacketLossTest::NextPacket(int mtu, unsigned char **pkg) -{ - unsigned char *buf = _frameToDecode->_frame->GetBuffer(); - *pkg = buf + _inBufIdx; - if (static_cast(_frameToDecode->_frame->GetLength()) - _inBufIdx <= mtu) - { - int size = _frameToDecode->_frame->GetLength() - _inBufIdx; - _inBufIdx = _frameToDecode->_frame->GetLength(); - return size; - } - _inBufIdx += mtu; - return mtu; -} - -int PacketLossTest::ByteLoss(int size, unsigned char *pkg, int bytesToLose) -{ - return size; -} - -void PacketLossTest::InsertPacket(TestVideoEncodedBuffer *buf, unsigned char *pkg, int size) -{ - if (static_cast(buf->GetSize()) - _outBufIdx < size) - { - printf("InsertPacket error!\n"); - return; - } - memcpy(buf->GetBuffer() + _outBufIdx, pkg, size); - buf->UpdateLength(buf->GetLength() + size); - _outBufIdx += size; -} diff --git a/modules/video_coding/codecs/test_framework/packet_loss_test.h b/modules/video_coding/codecs/test_framework/packet_loss_test.h deleted file mode 100644 index ea37681c1..000000000 --- a/modules/video_coding/codecs/test_framework/packet_loss_test.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_ - -#include - -#include "normal_async_test.h" - -class PacketLossTest : public NormalAsyncTest -{ -public: - PacketLossTest(); - virtual ~PacketLossTest() {if(_lastFrame) {delete [] _lastFrame; _lastFrame = NULL;}} - virtual void Encoded(const webrtc::EncodedImage& encodedImage); - virtual void Decoded(const webrtc::RawImage& decodedImage); -protected: - PacketLossTest(std::string name, std::string description); - PacketLossTest(std::string name, - std::string description, - double lossRate, - bool useNack, - unsigned int rttFrames = 0); - - virtual void Setup(); - virtual void Teardown(); - virtual void CodecSpecific_InitBitrate(); - virtual int DoPacketLoss(); - virtual int NextPacket(int size, unsigned char **pkg); - virtual int ByteLoss(int size, unsigned char *pkg, int bytesToLose); - virtual void InsertPacket(TestVideoEncodedBuffer *buf, unsigned char *pkg, int size); - int _inBufIdx; - int _outBufIdx; - - // When NACK is being simulated _lossProbabilty is zero, - // otherwise it is set equal to _lossRate. - // Desired channel loss rate. - double _lossRate; - // Probability used to simulate packet drops. - double _lossProbability; - - int _totalKept; - int _totalThrown; - int _sumChannelBytes; - std::list _frameQueue; - WebRtc_UWord8* _lastFrame; - WebRtc_UWord32 _lastFrameLength; -}; - - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PACKET_LOSS_TEST_H_ diff --git a/modules/video_coding/codecs/test_framework/performance_test.cc b/modules/video_coding/codecs/test_framework/performance_test.cc deleted file mode 100644 index b1606257f..000000000 --- a/modules/video_coding/codecs/test_framework/performance_test.cc +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "performance_test.h" -#include "tick_util.h" -#include - -using namespace webrtc; - -#define NUM_FRAMES 300 - -PerformanceTest::PerformanceTest(WebRtc_UWord32 bitRate) -: -_numCodecs(0), -_tests(NULL), -_encoders(NULL), -_decoders(NULL), -_threads(NULL), -_rawImageLock(NULL), -_encodeEvents(new EventWrapper*[1]), -_stopped(true), -_encodeCompleteCallback(NULL), -_decodeCompleteCallback(NULL), -NormalAsyncTest(bitRate) -{ -} - -PerformanceTest::PerformanceTest(WebRtc_UWord32 bitRate, WebRtc_UWord8 numCodecs) -: -_numCodecs(numCodecs), -_tests(new PerformanceTest*[_numCodecs]), -_encoders(new VideoEncoder*[_numCodecs]), -_decoders(new VideoDecoder*[_numCodecs]), -_threads(new ThreadWrapper*[_numCodecs]), -_rawImageLock(RWLockWrapper::CreateRWLock()), -_encodeEvents(new EventWrapper*[_numCodecs]), -_stopped(true), -_encodeCompleteCallback(NULL), -_decodeCompleteCallback(NULL), -NormalAsyncTest(bitRate) -{ - for (int i=0; i < _numCodecs; i++) - { - _tests[i] = new PerformanceTest(bitRate); - _encodeEvents[i] = EventWrapper::Create(); - } -} - -PerformanceTest::~PerformanceTest() -{ - if (_encoders != NULL) - { - delete [] _encoders; - } - if (_decoders != NULL) - { - delete [] _decoders; - } - if (_tests != NULL) - { - delete [] _tests; - } - if (_threads != NULL) - { - delete [] _threads; - } - if (_rawImageLock != NULL) - { - delete _rawImageLock; - } - if (_encodeEvents != NULL) - { - delete [] _encodeEvents; - } -} - -void -PerformanceTest::Setup() -{ - _inname = "../../../../testFiles/foreman.yuv"; - NormalAsyncTest::Setup(); // Setup input and output files - CodecSettings(352, 288, 30, _bitRate); // common to all codecs - for (int i=0; i < _numCodecs; i++) - { - _encoders[i] = CreateEncoder(); - _decoders[i] = CreateDecoder(); - if (_encoders[i] == NULL) - { - printf("Must create a codec specific test!\n"); - exit(EXIT_FAILURE); - } - if(_encoders[i]->InitEncode(&_inst, 4, 1440) < 0) - { - exit(EXIT_FAILURE); - } - if (_decoders[i]->InitDecode(&_inst, 1)) - { - exit(EXIT_FAILURE); - } - _tests[i]->SetEncoder(_encoders[i]); - _tests[i]->SetDecoder(_decoders[i]); - _tests[i]->_rawImageLock = _rawImageLock; - _encodeEvents[i]->Reset(); - _tests[i]->_encodeEvents[0] = _encodeEvents[i]; - _tests[i]->_inst = _inst; - _threads[i] = ThreadWrapper::CreateThread(PerformanceTest::RunThread, _tests[i]); - unsigned int id = 0; - _tests[i]->_stopped = false; - _threads[i]->Start(id); - } -} - -void -PerformanceTest::Perform() -{ - Setup(); - EventWrapper& sleepEvent = *EventWrapper::Create(); - const WebRtc_Word64 startTime = TickTime::MillisecondTimestamp(); - for (int i=0; i < NUM_FRAMES; i++) - { - { - // Read a new frame from file - WriteLockScoped imageLock(*_rawImageLock); - _lengthEncFrame = 0; - fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile); - if (feof(_sourceFile) != 0) - { - rewind(_sourceFile); - } - _inputVideoBuffer.VerifyAndAllocate(_inst.width*_inst.height*3/2); - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); - _inputVideoBuffer.SetTimeStamp((unsigned int) (_encFrameCnt * 9e4 / static_cast(_inst.maxFramerate))); - _inputVideoBuffer.SetWidth(_inst.width); - _inputVideoBuffer.SetHeight(_inst.height); - for (int i=0; i < _numCodecs; i++) - { - _tests[i]->_inputVideoBuffer.CopyPointer(_inputVideoBuffer); - _encodeEvents[i]->Set(); - } - } - if (i < NUM_FRAMES - 1) - { - sleepEvent.Wait(33); - } - } - for (int i=0; i < _numCodecs; i++) - { - _tests[i]->_stopped = true; - _encodeEvents[i]->Set(); - _threads[i]->Stop(); - } - const WebRtc_UWord32 totalTime = - static_cast(TickTime::MillisecondTimestamp() - startTime); - printf("Total time: %u\n", totalTime); - delete &sleepEvent; - Teardown(); -} - -void PerformanceTest::Teardown() -{ - if (_encodeCompleteCallback != NULL) - { - delete _encodeCompleteCallback; - } - if (_decodeCompleteCallback != NULL) - { - delete _decodeCompleteCallback; - } - // main test only, all others have numCodecs = 0: - if (_numCodecs > 0) - { - WriteLockScoped imageLock(*_rawImageLock); - _inputVideoBuffer.Free(); - NormalAsyncTest::Teardown(); - } - for (int i=0; i < _numCodecs; i++) - { - _encoders[i]->Release(); - delete _encoders[i]; - _decoders[i]->Release(); - delete _decoders[i]; - _tests[i]->_inputVideoBuffer.ClearPointer(); - _tests[i]->_rawImageLock = NULL; - _tests[i]->Teardown(); - delete _tests[i]; - delete _encodeEvents[i]; - delete _threads[i]; - } -} - -bool -PerformanceTest::RunThread(void* obj) -{ - PerformanceTest& test = *static_cast(obj); - return test.PerformSingleTest(); -} - -bool -PerformanceTest::PerformSingleTest() -{ - if (_encodeCompleteCallback == NULL) - { - _encodeCompleteCallback = new VideoEncodeCompleteCallback(NULL, &_frameQueue, *this); - _encoder->RegisterEncodeCompleteCallback(_encodeCompleteCallback); - } - if (_decodeCompleteCallback == NULL) - { - _decodeCompleteCallback = new VideoDecodeCompleteCallback(NULL, *this); - _decoder->RegisterDecodeCompleteCallback(_decodeCompleteCallback); - } - (*_encodeEvents)->Wait(WEBRTC_EVENT_INFINITE); // The first event is used for every single test - CodecSpecific_InitBitrate(); - bool complete = false; - { - ReadLockScoped imageLock(*_rawImageLock); - complete = Encode(); - } - if (!_frameQueue.Empty() || complete) - { - while (!_frameQueue.Empty()) - { - _frameToDecode = static_cast(_frameQueue.PopFrame()); - int lost = DoPacketLoss(); - if (lost == 2) - { - // Lost the whole frame, continue - _missingFrames = true; - delete _frameToDecode; - _frameToDecode = NULL; - continue; - } - int ret = Decode(lost); - delete _frameToDecode; - _frameToDecode = NULL; - if (ret < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", ret); - return false; - } - else if (ret < 0) - { - fprintf(stderr, "\n\nPositive return value from decode!\n\n"); - return false; - } - } - } - if (_stopped) - { - return false; - } - return true; -} - -bool PerformanceTest::Encode() -{ - RawImage rawImage; - VideoBufferToRawImage(_inputVideoBuffer, rawImage); - VideoFrameType frameType = kDeltaFrame; - if (_requestKeyFrame && !(_encFrameCnt%50)) - { - frameType = kKeyFrame; - } - void* codecSpecificInfo = CreateEncoderSpecificInfo(); - int ret = _encoder->Encode(rawImage, codecSpecificInfo, frameType); - if (codecSpecificInfo != NULL) - { - // TODO(holmer): implement virtual function for deleting this and remove warnings - delete codecSpecificInfo; - codecSpecificInfo = NULL; - } - assert(ret >= 0); - return false; -} - -int PerformanceTest::Decode(int lossValue) -{ - EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage); - encodedImage._completeFrame = !lossValue; - int ret = _decoder->Decode(encodedImage, _missingFrames, _frameToDecode->_codecSpecificInfo); - _missingFrames = false; - return ret; -} diff --git a/modules/video_coding/codecs/test_framework/performance_test.h b/modules/video_coding/codecs/test_framework/performance_test.h deleted file mode 100644 index d06083250..000000000 --- a/modules/video_coding/codecs/test_framework/performance_test.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_ - -#include "normal_async_test.h" -#include "thread_wrapper.h" -#include "rw_lock_wrapper.h" -#include "event_wrapper.h" - -class PerformanceTest : public NormalAsyncTest -{ -public: - PerformanceTest(WebRtc_UWord32 bitRate, WebRtc_UWord8 numCodecs); - virtual ~PerformanceTest(); - - virtual void Perform(); - virtual void Print() {}; - -protected: - PerformanceTest(WebRtc_UWord32 bitRate); - virtual void Setup(); - virtual bool Encode(); - virtual int Decode(int lossValue = 0); - virtual void Teardown(); - static bool RunThread(void* obj); - bool PerformSingleTest(); - - virtual webrtc::VideoEncoder* CreateEncoder() const { return NULL; }; - virtual webrtc::VideoDecoder* CreateDecoder() const { return NULL; }; - - WebRtc_UWord8 _numCodecs; - PerformanceTest** _tests; - webrtc::VideoEncoder** _encoders; - webrtc::VideoDecoder** _decoders; - webrtc::ThreadWrapper** _threads; - webrtc::RWLockWrapper* _rawImageLock; - webrtc::EventWrapper** _encodeEvents; - FrameQueue _frameQueue; - bool _stopped; - webrtc::EncodedImageCallback* _encodeCompleteCallback; - webrtc::DecodedImageCallback* _decodeCompleteCallback; - FILE* _outFile; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_PERFORMANCE_TEST_H_ diff --git a/modules/video_coding/codecs/test_framework/plotBenchmark.m b/modules/video_coding/codecs/test_framework/plotBenchmark.m deleted file mode 100644 index 33c8eb64f..000000000 --- a/modules/video_coding/codecs/test_framework/plotBenchmark.m +++ /dev/null @@ -1,427 +0,0 @@ -function plotBenchmark(fileNames, export) -%PLOTBENCHMARK Plots and exports video codec benchmarking results. -% PLOTBENCHMARK(FILENAMES, EXPORT) parses the video codec benchmarking result -% files given by the cell array of strings FILENAME. It plots the results and -% optionally exports each plot to an appropriately named file. -% -% EXPORT parameter: -% 'none' No file exports. -% 'eps' Exports to eps files (default). -% 'pdf' Exports to eps files and uses the command-line utility -% epstopdf to obtain pdf files. -% -% Example: -% plotBenchmark({'H264Benchmark.txt' 'LSVXBenchmark.txt'}, 'pdf') - -if (nargin < 1) - error('Too few input arguments'); -elseif (nargin < 2) - export = 'eps'; -end - -if ~iscell(fileNames) - if ischar(fileNames) - % one single file name as a string is ok - if size(fileNames,1) > 1 - % this is a char matrix, not ok - error('First argument must not be a char matrix'); - end - % wrap in a cell array - fileNames = {fileNames}; - else - error('First argument must be a cell array of strings'); - end -end - -if ~ischar(export) - error('Second argument must be a string'); -end - -outpath = 'BenchmarkPlots'; -[status, errMsg] = mkdir(outpath); -if status == 0 - error(errMsg); -end - -nCases = 0; -testCases = []; -% Read each test result file -for fileIdx = 1:length(fileNames) - if ~isstr(fileNames{fileIdx}) - error('First argument must be a cell array of strings'); - end - - fid = fopen(fileNames{fileIdx}, 'rt'); - if fid == -1 - error(['Unable to open ' fileNames{fileIdx}]); - end - - version = '1.0'; - if ~strcmp(fgetl(fid), ['#!benchmark' version]) - fclose(fid); - error(['Requires benchmark file format version ' version]); - end - - % Parse results file into testCases struct - codec = fgetl(fid); - tline = fgetl(fid); - while(tline ~= -1) - nCases = nCases + 1; - - delim = strfind(tline, ','); - name = tline(1:delim(1)-1); - % Drop underscored suffix from name - underscore = strfind(name, '_'); - if ~isempty(underscore) - name = name(1:underscore(1)-1); - end - - resolution = tline(delim(1)+1:delim(2)-1); - frameRate = tline(delim(2)+1:end); - - tline = fgetl(fid); - delim = strfind(tline, ','); - bitrateLabel = tline(1:delim(1)-1); - bitrate = sscanf(tline(delim(1):end),',%f'); - - tline = fgetl(fid); - delim = strfind(tline, ','); - psnrLabel = tline(1:delim(1)-1); - psnr = sscanf(tline(delim(1):end),',%f'); - - - % Default data for the optional lines - speedLabel = 'Default'; - speed = 0; - ssimLabel = 'Default'; - ssim = 0; - - tline = fgetl(fid); - delim = strfind(tline, ','); - - while ~isempty(delim) - % More data - % Check type of data - if strncmp(lower(tline), 'speed', 5) - % Speed data included - speedLabel = tline(1:delim(1)-1); - speed = sscanf(tline(delim(1):end), ',%f'); - - tline = fgetl(fid); - - elseif strncmp(lower(tline), 'encode time', 11) - % Encode and decode times included - % TODO: take care of the data - - % pop two lines from file - tline = fgetl(fid); - tline = fgetl(fid); - - elseif strncmp(tline, 'SSIM', 4) - % SSIM data included - ssimLabel = tline(1:delim(1)-1); - ssim = sscanf(tline(delim(1):end), ',%f'); - - tline = fgetl(fid); - end - delim = strfind(tline, ','); - end - - testCases = [testCases struct('codec', codec, 'name', name, 'resolution', ... - resolution, 'frameRate', frameRate, 'bitrate', bitrate, 'psnr', psnr, ... - 'speed', speed, 'bitrateLabel', bitrateLabel, 'psnrLabel', psnrLabel, ... - 'speedLabel', speedLabel, ... - 'ssim', ssim, 'ssimLabel', ssimLabel)]; - - tline = fgetl(fid); - end - - fclose(fid); -end - -i = 0; -casesPsnr = testCases; -while ~isempty(casesPsnr) - i = i + 1; - casesPsnr = plotOnePsnr(casesPsnr, i, export, outpath); -end - -casesSSIM = testCases; -while ~isempty(casesSSIM) - i = i + 1; - casesSSIM = plotOneSSIM(casesSSIM, i, export, outpath); -end - -casesSpeed = testCases; -while ~isempty(casesSpeed) - if casesSpeed(1).speed == 0 - casesSpeed = casesSpeed(2:end); - else - i = i + 1; - casesSpeed = plotOneSpeed(casesSpeed, i, export, outpath); - end -end - - - -%%%%%%%%%%%%%%%%%% -%% SUBFUNCTIONS %% -%%%%%%%%%%%%%%%%%% - -function casesOut = plotOnePsnr(cases, num, export, outpath) -% Find matching specs -plotIdx = 1; -for i = 2:length(cases) - if strcmp(cases(1).resolution, cases(i).resolution) & ... - strcmp(cases(1).frameRate, cases(i).frameRate) - plotIdx = [plotIdx i]; - end -end - -% Return unplotted cases -casesOut = cases(setdiff(1:length(cases), plotIdx)); -cases = cases(plotIdx); - -% Prune similar results -for i = 1:length(cases) - simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); - while ~isempty(simIndx) - diffIndx = setdiff(1:length(cases(i).bitrate), simIndx); - cases(i).psnr = cases(i).psnr(diffIndx); - cases(i).bitrate = cases(i).bitrate(diffIndx); - simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); - end -end - -% Prepare figure with axis labels and so on -hFig = figure(num); -clf; -hold on; -grid on; -axis([0 1100 20 50]); -set(gca, 'XTick', 0:200:1000); -set(gca, 'YTick', 20:10:60); -xlabel(cases(1).bitrateLabel); -ylabel(cases(1).psnrLabel); -res = cases(1).resolution; -frRate = cases(1).frameRate; -title([res ', ' frRate]); - -hLines = []; -codecs = {}; -sequences = {}; -i = 0; -while ~isempty(cases) - i = i + 1; - [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'psnr', i, sequences, 1); - - % Stored to generate the legend - hLines = [hLines ; hLine]; - codecs = {codecs{:} codec}; -end -legend(hLines, codecs, 4); -hold off; - -if ~strcmp(export, 'none') - % Export figure to an eps file - res = stripws(res); - frRate = stripws(frRate); - exportName = [outpath '/psnr-' res '-' frRate]; - exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); -end - -if strcmp(export, 'pdf') - % Use the epstopdf utility to convert to pdf - system(['epstopdf ' exportName '.eps']); -end - - -function casesOut = plotOneSSIM(cases, num, export, outpath) -% Find matching specs -plotIdx = 1; -for i = 2:length(cases) - if strcmp(cases(1).resolution, cases(i).resolution) & ... - strcmp(cases(1).frameRate, cases(i).frameRate) - plotIdx = [plotIdx i]; - end -end - -% Return unplotted cases -casesOut = cases(setdiff(1:length(cases), plotIdx)); -cases = cases(plotIdx); - -% Prune similar results -for i = 1:length(cases) - simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); - while ~isempty(simIndx) - diffIndx = setdiff(1:length(cases(i).bitrate), simIndx); - cases(i).ssim = cases(i).ssim(diffIndx); - cases(i).bitrate = cases(i).bitrate(diffIndx); - simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); - end -end - -% Prepare figure with axis labels and so on -hFig = figure(num); -clf; -hold on; -grid on; -axis([0 1100 0.5 1]); % y-limit are set to 'auto' below -set(gca, 'XTick', 0:200:1000); -%set(gca, 'YTick', 20:10:60); -xlabel(cases(1).bitrateLabel); -ylabel(cases(1).ssimLabel); -res = cases(1).resolution; -frRate = cases(1).frameRate; -title([res ', ' frRate]); - -hLines = []; -codecs = {}; -sequences = {}; -i = 0; -while ~isempty(cases) - i = i + 1; - [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'ssim', i, sequences, 1); - - % Stored to generate the legend - hLines = [hLines ; hLine]; - codecs = {codecs{:} codec}; -end -%set(gca,'YLimMode','auto') -set(gca,'YLim',[0.5 1]) -set(gca,'YScale','log') -legend(hLines, codecs, 4); -hold off; - -if ~strcmp(export, 'none') - % Export figure to an eps file - res = stripws(res); - frRate = stripws(frRate); - exportName = [outpath '/psnr-' res '-' frRate]; - exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); -end - -if strcmp(export, 'pdf') - % Use the epstopdf utility to convert to pdf - system(['epstopdf ' exportName '.eps']); -end - - -function casesOut = plotOneSpeed(cases, num, export, outpath) -% Find matching specs -plotIdx = 1; -for i = 2:length(cases) - if strcmp(cases(1).resolution, cases(i).resolution) & ... - strcmp(cases(1).frameRate, cases(i).frameRate) & ... - strcmp(cases(1).name, cases(i).name) - plotIdx = [plotIdx i]; - end -end - -% Return unplotted cases -casesOut = cases(setdiff(1:length(cases), plotIdx)); -cases = cases(plotIdx); - -% Prune similar results -for i = 1:length(cases) - simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25); - while ~isempty(simIndx) - diffIndx = setdiff(1:length(cases(i).psnr), simIndx); - cases(i).psnr = cases(i).psnr(diffIndx); - cases(i).speed = cases(i).speed(diffIndx); - simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25); - end -end - -hFig = figure(num); -clf; -hold on; -%grid on; -xlabel(cases(1).psnrLabel); -ylabel(cases(1).speedLabel); -res = cases(1).resolution; -name = cases(1).name; -frRate = cases(1).frameRate; -title([name ', ' res ', ' frRate]); - -hLines = []; -codecs = {}; -sequences = {}; -i = 0; -while ~isempty(cases) - i = i + 1; - [cases, hLine, codec, sequences] = plotOneCodec(cases, 'psnr', 'speed', i, sequences, 0); - - % Stored to generate the legend - hLines = [hLines ; hLine]; - codecs = {codecs{:} codec}; -end -legend(hLines, codecs, 1); -hold off; - -if ~strcmp(export, 'none') - % Export figure to an eps file - res = stripws(res); - frRate = stripws(frRate); - exportName = [outpath '/speed-' name '-' res '-' frRate]; - exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); -end - -if strcmp(export, 'pdf') - % Use the epstopdf utility to convert to pdf - system(['epstopdf ' exportName '.eps']); -end - - -function [casesOut, hLine, codec, sequences] = plotOneCodec(cases, xfield, yfield, num, sequences, annotatePlot) -plotStr = {'gx-', 'bo-', 'r^-', 'kd-', 'cx-', 'go--', 'b^--'}; -% Find matching codecs -plotIdx = 1; -for i = 2:length(cases) - if strcmp(cases(1).codec, cases(i).codec) - plotIdx = [plotIdx i]; - end -end - -% Return unplotted cases -casesOut = cases(setdiff(1:length(cases), plotIdx)); -cases = cases(plotIdx); - -for i = 1:length(cases) - % Plot a single case - hLine = plot(getfield(cases(i), xfield), getfield(cases(i), yfield), plotStr{num}, ... - 'LineWidth', 1.1, 'MarkerSize', 6); -end - -% hLine handle and codec are returned to construct the legend afterwards -codec = cases(1).codec; - -if annotatePlot == 0 - return; -end - -for i = 1:length(cases) - % Print the codec name as a text label - % Ensure each codec is only printed once - sequencePlotted = 0; - for j = 1:length(sequences) - if strcmp(cases(i).name, sequences{j}) - sequencePlotted = 1; - break; - end - end - - if sequencePlotted == 0 - text(getfield(cases(i), xfield, {1}), getfield(cases(i), yfield, {1}), ... - [' ' cases(i).name]); - sequences = {sequences{:} cases(i).name}; - end -end - - -% Strip whitespace from string -function str = stripws(str) -if ~isstr(str) - error('String required'); -end -str = str(setdiff(1:length(str), find(isspace(str) == 1))); diff --git a/modules/video_coding/codecs/test_framework/test.cc b/modules/video_coding/codecs/test_framework/test.cc deleted file mode 100644 index b84cd1976..000000000 --- a/modules/video_coding/codecs/test_framework/test.cc +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "test.h" -#include "video_source.h" -#include "vplib.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace webrtc; - -long filesize(const char *filename); // local function defined at end of file - -struct SSIMcontext -{ - SSIMcontext() : - refFileName(NULL), testFileName(NULL), width(0), height(0), - SSIMptr(NULL), startFrame(-1), endFrame(-1), evnt(NULL) {}; - SSIMcontext(const char *ref, const char *test, int w, int h, double *Sptr, - int start, int end, EventWrapper* ev) : - refFileName(ref), testFileName(test), width(w), height(h), - SSIMptr(Sptr), startFrame(start), endFrame(end), evnt(ev) {}; - const char *refFileName; - const char *testFileName; - int width; - int height; - double *SSIMptr; - int startFrame; - int endFrame; - EventWrapper* evnt; -}; - -Test::Test(std::string name, std::string description) -: -_name(name), -_description(description), -_bitRate(0), -_inname(""), -_outname(""), -_encodedName("") -{ - memset(&_inst, 0, sizeof(_inst)); - unsigned int seed = static_cast(0); - std::srand(seed); -} - -Test::Test(std::string name, std::string description, WebRtc_UWord32 bitRate) -: -_name(name), -_description(description), -_bitRate(bitRate), -_inname(""), -_outname(""), -_encodedName("") -{ - memset(&_inst, 0, sizeof(_inst)); - unsigned int seed = static_cast(0); - std::srand(seed); -} - -void -Test::Print() -{ - std::cout << _name << " completed!" << std::endl; - (*_log) << _name << std::endl; - (*_log) << _description << std::endl; - (*_log) << "Input file: " << _inname << std::endl; - (*_log) << "Output file: " << _outname << std::endl; - double psnr = -1.0, ssim = -1.0; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &psnr); - ssim = SSIMfromFilesMT(4 /* number of threads*/); - - (*_log) << "PSNR: " << psnr << std::endl; - std::cout << "PSNR: " << psnr << std::endl << std::endl; - (*_log) << "SSIM: " << ssim << std::endl; - std::cout << "SSIM: " << ssim << std::endl << std::endl; - (*_log) << std::endl; -} - -void -Test::Setup() -{ - int widhei = _inst.width*_inst.height; - _lengthSourceFrame = 3*widhei/2; - _sourceBuffer = new unsigned char[_lengthSourceFrame]; -} - -void -Test::CodecSettings(int width, int height, WebRtc_UWord32 frameRate /*=30*/, WebRtc_UWord32 bitRate /*=0*/) -{ - if (bitRate > 0) - { - _bitRate = bitRate; - } - else if (_bitRate == 0) - { - _bitRate = 600; - } - _inst.maxFramerate = (unsigned char)frameRate; - _inst.startBitrate = (int)_bitRate; - _inst.maxBitrate = 8000; - _inst.width = width; - _inst.height = height; -} - -void -Test::Teardown() -{ - delete [] _sourceBuffer; -} - -void -Test::SetEncoder(webrtc::VideoEncoder*encoder) -{ - _encoder = encoder; -} - -void -Test::SetDecoder(VideoDecoder*decoder) -{ - _decoder = decoder; -} - -void -Test::SetLog(std::fstream* log) -{ - _log = log; -} - -int -Test::PSNRfromFiles(const char *refFileName, const char *testFileName, int width, int height, double *YPSNRptr) -{ - FILE *refFp = fopen(refFileName, "rb"); - if( refFp == NULL ) { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if( testFp == NULL ) { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - double mse = 0.0; - double mseLogSum = 0.0; - int frames = 0; - - int frameBytes = 3*width*height/2; // bytes in one frame I420 - unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420 - unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420 - - int refBytes = (int) fread(ref, 1, frameBytes, refFp); - int testBytes = (int) fread(test, 1, frameBytes, testFp); - - while( refBytes == frameBytes && testBytes == frameBytes ) - { - mse = 0.0; - - // calculate Y sum-square-difference - for( int k = 0; k < width * height; k++ ) - { - mse += (test[k] - ref[k]) * (test[k] - ref[k]); - } - - // divide by number of pixels - mse /= (double) (width * height); - - // accumulate for total average - mseLogSum += std::log10( mse ); - frames++; - - refBytes = (int) fread(ref, 1, frameBytes, refFp); - testBytes = (int) fread(test, 1, frameBytes, testFp); - } - - // ypsnrAvg = sum( 10 log (255^2 / MSE) ) / frames - // = 20 * log(255) - 10 * mseLogSum / frames - *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames; - - delete [] ref; - delete [] test; - - fclose(refFp); - fclose(testFp); - - return 0; -} -int -Test::SSIMfromFiles(const char *refFileName, const char *testFileName, int width, int height, double *SSIMptr, - int startFrame /*= -1*/, int endFrame /*= -1*/) -{ - FILE *refFp = fopen(refFileName, "rb"); - if( refFp == NULL ) { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if( testFp == NULL ) { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - int frames = 0; - - int frameBytes = 3*width*height/2; // bytes in one frame I420 - unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420 - unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420 - - if (startFrame >= 0) - { - if (fseek(refFp, frameBytes * startFrame, SEEK_SET) != 0){ - fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, refFileName); - return -1; - } - if (fseek(testFp, frameBytes * startFrame, SEEK_SET) != 0){ - fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, testFileName); - return -1; - } - } - - int refBytes = (int) fread(ref, 1, frameBytes, refFp); - int testBytes = (int) fread(test, 1, frameBytes, testFp); - - // - // SSIM: variable definition, window function, initialization - int window = 10; - int flag_window = 0; //0 for uniform window filter, 1 for gaussian symmetric window - float variance_window = 2.0; //variance for window function - float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1) - //statistics per column of window (#columns = window+1), 0 element for avg over all columns - float avgTest[12]; - float avgRef[12]; - float contrastTest[12]; - float contrastRef[12]; - float crossCorr[12]; - // - //offsets for stability - float offset1 = 0.1f; - float offset2 = 0.1f; - float offset3 = offset2/2; - // - //define window for SSIM: take uniform filter for now - float sumfil = 0.0; - int nn=-1; - for(int j=-window/2;j<=window/2;j++) - for(int i=-window/2;i<=window/2;i++) - { - nn+=1; - if (flag_window == 0) - ssimFilter[nn] = 1.0; - else - { - float dist = (float)(i*i) + (float)(j*j); - float tmp = 0.5f*dist/variance_window; - ssimFilter[nn] = exp(-tmp); - } - sumfil +=ssimFilter[nn]; - } - //normalize window - nn=-1; - for(int j=-window/2;j<=window/2;j++) - for(int i=-window/2;i<=window/2;i++) - { - nn+=1; - ssimFilter[nn] = ssimFilter[nn]/((float)sumfil); - } - // - float ssimScene = 0.0; //avgerage SSIM for sequence - // - //SSIM: done with variables and defintion - // - - while( refBytes == frameBytes && testBytes == frameBytes && - !(endFrame >= 0 && frames > endFrame - startFrame)) - { - float ssimFrame = 0.0; - int sh = window/2+1; - int numPixels = 0; - for(int i=sh;irefFileName, ctx->testFileName, ctx->width, ctx->height, ctx->SSIMptr, ctx->startFrame, ctx->endFrame); - ctx->evnt->Set(); - return false; -} - -double Test::SSIMfromFilesMT(const int numThreads) -{ - int numFrames = filesize(_inname.c_str()) / _lengthSourceFrame; - std::vector nFramesVec(numThreads); - std::vector ssimVec(numThreads); - int framesPerCore = (numFrames + numThreads - 1) / numThreads; // rounding up - int i = 0; - int nFrames; - for (nFrames = numFrames; nFrames >= framesPerCore; nFrames -= framesPerCore) - { - nFramesVec[i++] = framesPerCore; - } - if (nFrames > 0) - { - assert(i == numThreads - 1); - nFramesVec[i] = nFrames; // remainder - } - - int frameIx = 0; - std::vector eventVec(numThreads); - std::vector threadVec(numThreads); - std::vector ctxVec(numThreads); - for (i = 0; i < numThreads; i++) - { - eventVec[i] = EventWrapper::Create(); - ctxVec[i] = SSIMcontext(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &ssimVec[i], frameIx, frameIx + nFramesVec[i] - 1, eventVec[i]); - threadVec[i] = ThreadWrapper::CreateThread(SSIMthread, &(ctxVec[i]), kLowPriority); - unsigned int id; - threadVec[i]->Start(id); - frameIx += nFramesVec[i]; - } - - // wait for all events - for (i = 0; i < numThreads; i++) { - eventVec[i]->Wait(100000 /* ms*/); - threadVec[i]->Stop(); - delete threadVec[i]; - delete eventVec[i]; - } - - double avgSsim = 0; - for (i = 0; i < numThreads; i++) - { - avgSsim += (ssimVec[i] * nFramesVec[i]); - } - - avgSsim /= numFrames; - return avgSsim; -} - - -double Test::ActualBitRate(int nFrames) -{ - return 8.0 * _sumEncBytes / (nFrames / _inst.maxFramerate); -} - -bool Test::PacketLoss(double lossRate) -{ - return RandUniform() < lossRate; -} - -void -Test::VideoBufferToRawImage(TestVideoBuffer& videoBuffer, RawImage &image) -{ - image._buffer = videoBuffer.GetBuffer(); - image._size = videoBuffer.GetSize(); - image._length = videoBuffer.GetLength(); - image._width = videoBuffer.GetWidth(); - image._height = videoBuffer.GetHeight(); - image._timeStamp = videoBuffer.GetTimeStamp(); -} -void -Test::VideoEncodedBufferToEncodedImage(TestVideoEncodedBuffer& videoBuffer, EncodedImage &image) -{ - image._buffer = videoBuffer.GetBuffer(); - image._length = videoBuffer.GetLength(); - image._size = videoBuffer.GetSize(); - image._frameType = static_cast(videoBuffer.GetFrameType()); - image._timeStamp = videoBuffer.GetTimeStamp(); - image._encodedWidth = videoBuffer.GetCaptureWidth(); - image._encodedHeight = videoBuffer.GetCaptureHeight(); - image._completeFrame = true; -} - -long filesize(const char *filename) -{ -FILE *f = fopen(filename,"rb"); /* open the file in read only */ - -long size = 0; - if (fseek(f,0,SEEK_END)==0) /* seek was successful */ - size = ftell(f); - fclose(f); - return size; -} diff --git a/modules/video_coding/codecs/test_framework/test.h b/modules/video_coding/codecs/test_framework/test.h deleted file mode 100644 index 3d9842b74..000000000 --- a/modules/video_coding/codecs/test_framework/test.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_ - -#include "video_codec_interface.h" -#include "video_buffer.h" -#include -#include -#include - -class Test -{ -public: - Test(std::string name, std::string description); - Test(std::string name, std::string description, WebRtc_UWord32 bitRate); - virtual ~Test() {}; - virtual void Perform()=0; - virtual void Print(); - void SetEncoder(webrtc::VideoEncoder *encoder); - void SetDecoder(webrtc::VideoDecoder *decoder); - void SetLog(std::fstream* log); - -protected: - virtual void Setup(); - virtual void CodecSettings(int width, - int height, - WebRtc_UWord32 frameRate=30, - WebRtc_UWord32 bitRate=0); - virtual void Teardown(); - static int PSNRfromFiles(const char *refFileName, - const char *testFileName, - int width, - int height, - double *YPSNRptr); - static int SSIMfromFiles(const char *refFileName, - const char *testFileName, - int width, - int height, - double *SSIMptr, - int startByte = -1, int endByte = -1); - double SSIMfromFilesMT(int numThreads); - static bool SSIMthread(void *ctx); - - double ActualBitRate(int nFrames); - static bool PacketLoss(double lossRate); - static double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); } - static void VideoBufferToRawImage(TestVideoBuffer& videoBuffer, - webrtc::RawImage &image); - static void VideoEncodedBufferToEncodedImage(TestVideoEncodedBuffer& videoBuffer, - webrtc::EncodedImage &image); - - webrtc::VideoEncoder* _encoder; - webrtc::VideoDecoder* _decoder; - WebRtc_UWord32 _bitRate; - unsigned int _lengthSourceFrame; - unsigned char* _sourceBuffer; - TestVideoBuffer _inputVideoBuffer; - TestVideoEncodedBuffer _encodedVideoBuffer; - TestVideoBuffer _decodedVideoBuffer; - webrtc::VideoCodec _inst; - std::fstream* _log; - std::string _inname; - std::string _outname; - std::string _encodedName; - int _sumEncBytes; - -private: - std::string _name; - std::string _description; - -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_TEST_H_ diff --git a/modules/video_coding/codecs/test_framework/test_framework.gyp b/modules/video_coding/codecs/test_framework/test_framework.gyp deleted file mode 100644 index 861e1a415..000000000 --- a/modules/video_coding/codecs/test_framework/test_framework.gyp +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'test_framework', - 'type': '<(library)', - - 'dependencies': [ - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - ], - - 'include_dirs': [ - '../interface', - '../../../../common_video/interface', - ], - - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - - 'sources': [ - # header files - 'benchmark.h', - 'normal_async_test.h', - 'normal_test.h', - 'packet_loss_test.h', - 'performance_test.h', - 'test.h', - 'unit_test.h', - 'video_buffer.h', - 'video_source.h', - - # source files - 'benchmark.cc', - 'normal_async_test.cc', - 'normal_test.cc', - 'packet_loss_test.cc', - 'performance_test.cc', - 'test.cc', - 'unit_test.cc', - 'video_buffer.cc', - 'video_source.cc', - - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_coding/codecs/test_framework/unit_test.cc b/modules/video_coding/codecs/test_framework/unit_test.cc deleted file mode 100644 index ba6a6479a..000000000 --- a/modules/video_coding/codecs/test_framework/unit_test.cc +++ /dev/null @@ -1,815 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "video_source.h" -#include "tick_util.h" -#include -#include -#include -#include - -using namespace webrtc; - -UnitTest::UnitTest() -: -Test("UnitTest", "Unit test"), -_tests(0), -_errors(0), -_source(NULL), -_refFrame(NULL), -_refEncFrame(NULL), -_refDecFrame(NULL), -_refEncFrameLength(0), -_sourceFile(NULL), -_encodeCompleteCallback(NULL), -_decodeCompleteCallback(NULL) -{ -} - -UnitTest::UnitTest(std::string name, std::string description) -: -_tests(0), -_errors(0), -_source(NULL), -_refFrame(NULL), -_refEncFrame(NULL), -_refDecFrame(NULL), -_refEncFrameLength(0), -_sourceFile(NULL), -_encodeCompleteCallback(NULL), -_decodeCompleteCallback(NULL), -Test(name, description) -{ -} - -UnitTest::~UnitTest() -{ - if (_encodeCompleteCallback) { - delete _encodeCompleteCallback; - } - - if (_decodeCompleteCallback) { - delete _decodeCompleteCallback; - } - - if (_source) { - delete _source; - } - - if (_refFrame) { - delete [] _refFrame; - } - - if (_refDecFrame) { - delete [] _refDecFrame; - } - - if (_sourceBuffer) { - delete [] _sourceBuffer; - } - - if (_sourceFile) { - fclose(_sourceFile); - } - - if (_refEncFrame) { - delete [] _refEncFrame; - } -} - -WebRtc_Word32 -UnitTestEncodeCompleteCallback::Encoded(EncodedImage& encodedImage, - const void* codecSpecificInfo, - const webrtc::RTPFragmentationHeader* - fragmentation) -{ - _encodedVideoBuffer->VerifyAndAllocate(encodedImage._size); - _encodedVideoBuffer->CopyBuffer(encodedImage._size, encodedImage._buffer); - _encodedVideoBuffer->UpdateLength(encodedImage._length); - _encodedVideoBuffer->SetFrameType(encodedImage._frameType); - _encodedVideoBuffer->SetCaptureWidth( - (WebRtc_UWord16)encodedImage._encodedWidth); - _encodedVideoBuffer->SetCaptureHeight( - (WebRtc_UWord16)encodedImage._encodedHeight); - _encodedVideoBuffer->SetTimeStamp(encodedImage._timeStamp); - _encodeComplete = true; - _encodedFrameType = encodedImage._frameType; - return 0; -} - -WebRtc_Word32 UnitTestDecodeCompleteCallback::Decoded(RawImage& image) -{ - _decodedVideoBuffer->VerifyAndAllocate(image._length); - _decodedVideoBuffer->CopyBuffer(image._length, image._buffer); - _decodedVideoBuffer->SetWidth(image._width); - _decodedVideoBuffer->SetHeight(image._height); - _decodedVideoBuffer->SetTimeStamp(image._timeStamp); - _decodeComplete = true; - return 0; -} - -bool -UnitTestEncodeCompleteCallback::EncodeComplete() -{ - if (_encodeComplete) - { - _encodeComplete = false; - return true; - } - return false; -} - -VideoFrameType -UnitTestEncodeCompleteCallback::EncodedFrameType() const -{ - return _encodedFrameType; -} - -bool -UnitTestDecodeCompleteCallback::DecodeComplete() -{ - if (_decodeComplete) - { - _decodeComplete = false; - return true; - } - return false; -} - -WebRtc_UWord32 -UnitTest::WaitForEncodedFrame() const -{ - WebRtc_Word64 startTime = TickTime::MillisecondTimestamp(); - while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitEncTimeMs) - { - if (_encodeCompleteCallback->EncodeComplete()) - { - return _encodedVideoBuffer.GetLength(); - } - } - return 0; -} - -WebRtc_UWord32 -UnitTest::WaitForDecodedFrame() const -{ - WebRtc_Word64 startTime = TickTime::MillisecondTimestamp(); - while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitDecTimeMs) - { - if (_decodeCompleteCallback->DecodeComplete()) - { - return _decodedVideoBuffer.GetLength(); - } - } - return 0; -} - -WebRtc_UWord32 -UnitTest::CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate, - WebRtc_UWord32 /* frameRate */) -{ - return _encoder->SetRates(bitRate, _inst.maxFramerate); -} - -void -UnitTest::Setup() -{ - // Use _sourceFile as a check to prevent multiple Setup() calls. - if (_sourceFile != NULL) - { - return; - } - - if (_encodeCompleteCallback == NULL) - { - _encodeCompleteCallback = - new UnitTestEncodeCompleteCallback(&_encodedVideoBuffer); - } - if (_decodeCompleteCallback == NULL) - { - _decodeCompleteCallback = - new UnitTestDecodeCompleteCallback(&_decodedVideoBuffer); - } - - _encoder->RegisterEncodeCompleteCallback(_encodeCompleteCallback); - _decoder->RegisterDecodeCompleteCallback(_decodeCompleteCallback); - - _source = new VideoSource("test/testFiles/foreman_cif.yuv", kCIF); - - _lengthSourceFrame = _source->GetFrameLength(); - _refFrame = new unsigned char[_lengthSourceFrame]; - _refDecFrame = new unsigned char[_lengthSourceFrame]; - _sourceBuffer = new unsigned char [_lengthSourceFrame]; - _sourceFile = fopen(_source->GetFileName().c_str(), "rb"); - VIDEO_TEST_EXIT_ON_ERR(_sourceFile != NULL); - - _inst.maxFramerate = _source->GetFrameRate(); - _bitRate = 300; - _inst.startBitrate = 300; - _inst.maxBitrate = 4000; - _inst.width = _source->GetWidth(); - _inst.height = _source->GetHeight(); - - // Get input frame. - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - VIDEO_TEST_EXIT_ON_ERR(fread(_refFrame, 1, _lengthSourceFrame, _sourceFile) - == _lengthSourceFrame); - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame); - rewind(_sourceFile); - - // Get a reference encoded frame. - _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - - RawImage image; - VideoBufferToRawImage(_inputVideoBuffer, image); - - // Ensures our initial parameters are valid. - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - _encoder->Encode(image, NULL); - _refEncFrameLength = WaitForEncodedFrame(); - VIDEO_TEST_EXIT_ON_ERR(_refEncFrameLength > 0); - _refEncFrame = new unsigned char[_refEncFrameLength]; - memcpy(_refEncFrame, _encodedVideoBuffer.GetBuffer(), _refEncFrameLength); - - // Get a reference decoded frame. - _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - - int frameLength = 0; - int i=0; - while (frameLength == 0) - { - if (i > 0) - { - // Insert yet another frame - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - VIDEO_TEST_EXIT_ON_ERR(fread(_refFrame, 1, _lengthSourceFrame, - _sourceFile) == _lengthSourceFrame); - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame); - _inputVideoBuffer.SetWidth(_source->GetWidth()); - _inputVideoBuffer.SetHeight(_source->GetHeight()); - VideoBufferToRawImage(_inputVideoBuffer, image); - _encoder->Encode(image, NULL); - VIDEO_TEST_EXIT_ON_ERR(WaitForEncodedFrame() > 0); - } - EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - VIDEO_TEST_EXIT_ON_ERR(_decoder->Decode(encodedImage, 0, NULL) - == WEBRTC_VIDEO_CODEC_OK); - frameLength = WaitForDecodedFrame(); - _encodedVideoBuffer.Reset(); - _encodedVideoBuffer.UpdateLength(0); - i++; - } - rewind(_sourceFile); - VIDEO_TEST(frameLength == _lengthSourceFrame); - memcpy(_refDecFrame, _decodedVideoBuffer.GetBuffer(), _lengthSourceFrame); -} - -void -UnitTest::Teardown() -{ - // Use _sourceFile as a check to prevent multiple Teardown() calls. - if (_sourceFile == NULL) - { - return; - } - - _encoder->Release(); - _decoder->Release(); - - fclose(_sourceFile); - _sourceFile = NULL; - delete [] _refFrame; - _refFrame = NULL; - delete [] _refEncFrame; - _refEncFrame = NULL; - delete [] _refDecFrame; - _refDecFrame = NULL; - delete [] _sourceBuffer; - _sourceBuffer = NULL; -} - -void -UnitTest::Print() -{ - printf("Unit Test\n\n%i tests completed\n", _tests); - if (_errors > 0) - { - printf("%i FAILED\n\n", _errors); - } - else - { - printf("ALL PASSED\n\n"); - } -} - -int -UnitTest::DecodeWithoutAssert() -{ - EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - int ret = _decoder->Decode(encodedImage, 0, NULL); - int frameLength = WaitForDecodedFrame(); - _encodedVideoBuffer.Reset(); - _encodedVideoBuffer.UpdateLength(0); - return ret == WEBRTC_VIDEO_CODEC_OK ? frameLength : ret; -} - -int -UnitTest::Decode() -{ - EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - if (encodedImage._length == 0) - { - return WEBRTC_VIDEO_CODEC_OK; - } - int ret = _decoder->Decode(encodedImage, 0, NULL); - int frameLength = WaitForDecodedFrame(); - assert(ret == WEBRTC_VIDEO_CODEC_OK && (frameLength == 0 || frameLength - == _lengthSourceFrame)); - VIDEO_TEST(ret == WEBRTC_VIDEO_CODEC_OK && (frameLength == 0 || frameLength - == _lengthSourceFrame)); - _encodedVideoBuffer.Reset(); - _encodedVideoBuffer.UpdateLength(0); - return ret == WEBRTC_VIDEO_CODEC_OK ? frameLength : ret; -} - -// Test pure virtual VideoEncoder and VideoDecoder APIs. -void -UnitTest::Perform() -{ - UnitTest::Setup(); - int frameLength; - RawImage inputImage; - EncodedImage encodedImage; - EventWrapper& sleepEvent = *EventWrapper::Create(); - - //----- Encoder parameter tests ----- - - //-- Calls before InitEncode() -- - // We want to revert the initialization done in Setup(). - VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VideoBufferToRawImage(_inputVideoBuffer, inputImage); - VIDEO_TEST(_encoder->Encode(inputImage, NULL) - == WEBRTC_VIDEO_CODEC_UNINITIALIZED); - VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_UNINITIALIZED); - - //-- InitEncode() errors -- - // Null pointer. - VIDEO_TEST(_encoder->InitEncode(NULL, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - // bit rate exceeds max bit rate - WebRtc_Word32 tmpBitRate = _inst.startBitrate; - WebRtc_Word32 tmpMaxBitRate = _inst.maxBitrate; - _inst.startBitrate = 4000; - _inst.maxBitrate = 3000; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _inst.startBitrate = tmpBitRate; - _inst.maxBitrate = tmpMaxBitRate; //unspecified value - - // Bad framerate. - _inst.maxFramerate = 0; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - // Seems like we should allow any framerate in range [0, 255]. - //_inst.frameRate = 100; - //VIDEO_TEST(_encoder->InitEncode(&_inst, 1) == -1); // FAILS - _inst.maxFramerate = 30; - - // Bad bitrate. - _inst.startBitrate = -1; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _inst.maxBitrate = _inst.startBitrate - 1; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _inst.maxBitrate = 0; - _inst.startBitrate = 300; - - // Bad maxBitRate. - _inst.maxBitrate = 200; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _inst.maxBitrate = 4000; - - // Bad width. - _inst.width = 0; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) < 0); - // Should there be a width and height cap? - //_inst.width = 10000; - //VIDEO_TEST(_encoder->InitEncode(&_inst, 1) == -1); - _inst.width = _source->GetWidth(); - - // Bad height. - _inst.height = 0; - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) < 0); - _inst.height = _source->GetHeight(); - - // Bad number of cores. - VIDEO_TEST(_encoder->InitEncode(&_inst, -1, 1440) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - - //-- Encode() errors -- - - // inputVideoBuffer unallocated. - _inputVideoBuffer.Free(); - VideoBufferToRawImage(_inputVideoBuffer, inputImage); - VIDEO_TEST(_encoder->Encode(inputImage, NULL) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _refFrame); - - //----- Encoder stress tests ----- - - // Vary frame rate and I-frame request. - VideoBufferToRawImage(_inputVideoBuffer, inputImage); - for (int i = 1; i <= 60; i++) - { - VideoFrameType frameType = !(i % 2) ? kKeyFrame : kDeltaFrame; - VIDEO_TEST(_encoder->Encode(inputImage, NULL, frameType) == - WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(WaitForEncodedFrame() > 0); - sleepEvent.Wait(10); // Allow the encoder's queue to realize it's empty. - } - - // Init then encode. - _encodedVideoBuffer.UpdateLength(0); - _encodedVideoBuffer.Reset(); - VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(WaitForEncodedFrame() > 0); - - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - _encoder->Encode(inputImage, NULL); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST(frameLength > 0); - VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength, - _encodedVideoBuffer.GetBuffer(), frameLength) == true); - - // Reset then encode. - _encodedVideoBuffer.UpdateLength(0); - _encodedVideoBuffer.Reset(); - VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK); - WaitForEncodedFrame(); - VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - _encoder->Encode(inputImage, NULL); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST(frameLength > 0); - VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength, - _encodedVideoBuffer.GetBuffer(), frameLength) == true); - - // Release then encode. - _encodedVideoBuffer.UpdateLength(0); - _encodedVideoBuffer.Reset(); - VIDEO_TEST(_encoder->Encode(inputImage, NULL) == WEBRTC_VIDEO_CODEC_OK); - WaitForEncodedFrame(); - VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - _encoder->Encode(inputImage, NULL); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST(frameLength > 0); - VIDEO_TEST(CheckIfBitExact(_refEncFrame, _refEncFrameLength, - _encodedVideoBuffer.GetBuffer(), frameLength) == true); - - //----- Decoder parameter tests ----- - - //-- Calls before InitDecode() -- - // We want to revert the initialization done in Setup(). - VIDEO_TEST(_decoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - VIDEO_TEST(_decoder->Decode(encodedImage, false, NULL) == - WEBRTC_VIDEO_CODEC_UNINITIALIZED); - WaitForDecodedFrame(); - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_UNINITIALIZED); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - - //-- Decode() errors -- - // Unallocated encodedVideoBuffer. - _encodedVideoBuffer.Free(); - //_encodedVideoBuffer.UpdateLength(10); // Buffer NULL but length > 0 - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - VIDEO_TEST(_decoder->Decode(encodedImage, false, NULL) == - WEBRTC_VIDEO_CODEC_ERR_PARAMETER); - _encodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - - //----- Decoder stress tests ----- - unsigned char* tmpBuf = new unsigned char[_lengthSourceFrame]; - - // "Random" and zero data. - // We either expect an error, or at the least, no output. - // This relies on the codec's ability to detect an erroneous bitstream. - /* - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - for (int i = 0; i < 100; i++) - { - VIDEO_TEST_EXIT_ON_ERR(fread(tmpBuf, 1, _refEncFrameLength, _sourceFile) - == _refEncFrameLength); - _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, tmpBuf); - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - FillDecoderSpecificInfo(encodedImage); - int ret = _decoder->Decode(encodedImage, false, _decoderSpecificInfo); - VIDEO_TEST(ret <= 0); - if (ret == 0) - { - VIDEO_TEST(WaitForDecodedFrame() == 0); - } - - memset(tmpBuf, 0, _refEncFrameLength); - _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, tmpBuf); - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - FillDecoderSpecificInfo(encodedImage); - ret = _decoder->Decode(encodedImage, false, _decoderSpecificInfo); - VIDEO_TEST(ret <= 0); - if (ret == 0) - { - VIDEO_TEST(WaitForDecodedFrame() == 0); - } - } - */ - rewind(_sourceFile); - - _encodedVideoBuffer.UpdateLength(_refEncFrameLength); - _encodedVideoBuffer.CopyBuffer(_refEncFrameLength, _refEncFrame); - - // Init then decode. - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - frameLength = 0; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - while (frameLength == 0) - { - _decoder->Decode(encodedImage, false, NULL); - frameLength = WaitForDecodedFrame(); - } - VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength, - _refDecFrame, _lengthSourceFrame) == true); - - // Reset then decode. - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - frameLength = 0; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - while (frameLength == 0) - { - _decoder->Decode(encodedImage, false, NULL); - frameLength = WaitForDecodedFrame(); - } - VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength, - _refDecFrame, _lengthSourceFrame) == true); - - // Decode with other size, reset, then decode with original size again - // to verify that decoder is reset to a "fresh" state upon Reset(). - { - // assert that input frame size is a factor of two, so that we can use - // quarter size below - VIDEO_TEST((_inst.width % 2 == 0) && (_inst.height % 2 == 0)); - - VideoCodec tempInst; - memcpy(&tempInst, &_inst, sizeof(VideoCodec)); - tempInst.width /= 2; - tempInst.height /= 2; - - // Encode reduced (quarter) frame size - VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&tempInst, 1, 1440) == - WEBRTC_VIDEO_CODEC_OK); - RawImage tempInput(inputImage._buffer, inputImage._length/4, - inputImage._size/4); - _encoder->Encode(tempInput, NULL); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST(frameLength > 0); - - // Reset then decode. - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - frameLength = 0; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - while (frameLength == 0) - { - _decoder->Decode(encodedImage, false, NULL); - frameLength = WaitForDecodedFrame(); - } - - // Encode original frame again - VIDEO_TEST(_encoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == - WEBRTC_VIDEO_CODEC_OK); - _encoder->Encode(inputImage, NULL); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST(frameLength > 0); - - // Reset then decode original frame again. - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - frameLength = 0; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - while (frameLength == 0) - { - _decoder->Decode(encodedImage, false, NULL); - frameLength = WaitForDecodedFrame(); - } - - // check that decoded frame matches with reference - VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength, - _refDecFrame, _lengthSourceFrame) == true); - - } - - // Release then decode. - VIDEO_TEST(_decoder->Release() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - frameLength = 0; - VideoEncodedBufferToEncodedImage(_encodedVideoBuffer, encodedImage); - while (frameLength == 0) - { - _decoder->Decode(encodedImage, false, NULL); - frameLength = WaitForDecodedFrame(); - } - VIDEO_TEST(CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), frameLength, - _refDecFrame, _lengthSourceFrame) == true); - _encodedVideoBuffer.UpdateLength(0); - _encodedVideoBuffer.Reset(); - - delete [] tmpBuf; - - //----- Function tests ----- - int frames = 0; - // Do not specify maxBitRate (as in ViE). - _inst.maxBitrate = 0; - - //-- Timestamp propagation -- - VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - - printf("\nTimestamp propagation test...\n"); - frames = 0; - int frameDelay = 0; - int encTimeStamp; - _decodedVideoBuffer.SetTimeStamp(0); - while (fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile) == - _lengthSourceFrame) - { - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); - _inputVideoBuffer.SetTimeStamp(frames); - VideoBufferToRawImage(_inputVideoBuffer, inputImage); - VIDEO_TEST_EXIT_ON_ERR(_encoder->Encode(inputImage, NULL) == - WEBRTC_VIDEO_CODEC_OK); - frameLength = WaitForEncodedFrame(); - //VIDEO_TEST_EXIT_ON_ERR(frameLength); - VIDEO_TEST(frameLength > 0); - encTimeStamp = _encodedVideoBuffer.GetTimeStamp(); - VIDEO_TEST(_inputVideoBuffer.GetTimeStamp() == encTimeStamp); - - frameLength = Decode(); - if (frameLength == 0) - { - frameDelay++; - } - - encTimeStamp -= frameDelay; - if (encTimeStamp < 0) - { - encTimeStamp = 0; - } - VIDEO_TEST(_decodedVideoBuffer.GetTimeStamp() == encTimeStamp); - frames++; - sleepEvent.Wait(33); - } - delete &sleepEvent; - VIDEO_TEST_EXIT_ON_ERR(feof(_sourceFile) != 0); - rewind(_sourceFile); - - RateControlTests(); - - Teardown(); -} - -void -UnitTest::RateControlTests() -{ - FILE *outFile = NULL; - std::string outFileName; - int frames = 0; - RawImage inputImage; - WebRtc_UWord32 frameLength; - EventWrapper& sleepEvent = *EventWrapper::Create(); - - // Do not specify maxBitRate (as in ViE). - _inst.maxBitrate = 0; - //-- Verify rate control -- - VIDEO_TEST(_encoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_encoder->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->Reset() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(_decoder->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - // add: should also be 0, and 1 - const int bitRate[] = - {100, 200, 300, 400, 500, 600, 800, 1000, 2000, 3000, 4000, 10000}; - const int nBitrates = sizeof(bitRate)/sizeof(*bitRate); - - printf("\nRate control test\n"); - for (int i = 0; i < nBitrates; i++) - { - _bitRate = bitRate[i]; - int totalBytes = 0; - _encoder->Reset(); - _inst.startBitrate = _bitRate; - _encoder->InitEncode(&_inst, 4, 1440); - _decoder->Reset(); - _decoder->InitDecode(&_inst, 1); - frames = 0; - - if (_bitRate > _inst.maxBitrate) - { - CodecSpecific_SetBitrate(_bitRate, _inst.maxFramerate); - } - else - { - CodecSpecific_SetBitrate(_bitRate, _inst.maxFramerate); - } - - while (fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile) == - _lengthSourceFrame) - { - _inputVideoBuffer.CopyBuffer(_lengthSourceFrame, _sourceBuffer); - _inputVideoBuffer.SetTimeStamp(_inputVideoBuffer.GetTimeStamp() + - static_cast(9e4 / - static_cast(_inst.maxFramerate))); - VideoBufferToRawImage(_inputVideoBuffer, inputImage); - VIDEO_TEST_EXIT_ON_ERR(_encoder->Encode(inputImage, NULL) == - WEBRTC_VIDEO_CODEC_OK); - frameLength = WaitForEncodedFrame(); - VIDEO_TEST_EXIT_ON_ERR(frameLength > 0); - //VIDEO_TEST(frameLength > 0); - totalBytes += frameLength; - frames++; - - _encodedVideoBuffer.UpdateLength(0); - _encodedVideoBuffer.Reset(); - - sleepEvent.Wait(10); - } - WebRtc_UWord32 actualBitrate = - (totalBytes / frames * _inst.maxFramerate * 8)/1000; - printf("Target bitrate: %d kbps, actual bitrate: %d kbps\n", _bitRate, - actualBitrate); - // Test for close match over reasonable range. - if (_bitRate >= 100 && _bitRate <= 4000) - { - //VIDEO_TEST(fabs(actualBitrate - _bitRate) < 0.05 * _bitRate); - VIDEO_TEST(abs(WebRtc_Word32(actualBitrate - _bitRate)) < - 0.1 * _bitRate); // for VP8 - } - VIDEO_TEST_EXIT_ON_ERR(feof(_sourceFile) != 0); - rewind(_sourceFile); - } -} - -bool -UnitTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes, - const void* ptrB, unsigned int bLengthBytes) -{ - if (aLengthBytes != bLengthBytes) - { - return false; - } - - return memcmp(ptrA, ptrB, aLengthBytes) == 0; -} diff --git a/modules/video_coding/codecs/test_framework/unit_test.h b/modules/video_coding/codecs/test_framework/unit_test.h deleted file mode 100644 index 875b584c9..000000000 --- a/modules/video_coding/codecs/test_framework/unit_test.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_ - -#include "test.h" -#include "event_wrapper.h" - -// Disable "conditional expression is constant" warnings on the perfectly -// acceptable -// do { ... } while (0) constructions below. -// Refer to http://stackoverflow.com/questions/1946445/ -// is-there-better-way-to-write-do-while0-construct-to-avoid-compiler-warnings -// for some discussion of the issue. -#pragma warning(disable : 4127) - -#define VIDEO_TEST(expr) \ - do \ - { \ - _tests++; \ - if (!(expr)) \ - { \ - fprintf(stderr, "Error at line %i of %s\nAssertion failed: %s\n\n",\ - __LINE__, __FILE__, #expr); \ - _errors++; \ - } \ - } while (0) - -#define VIDEO_TEST_EXIT_ON_ERR(expr) \ - do \ - { \ - if (!(expr)) \ - { \ - fprintf(stderr, "Error at line %i of %s\nAssertion failed: %s\n", \ - __LINE__, __FILE__, #expr); \ - fprintf(stderr, "Exiting...\n\n"); \ - exit(EXIT_FAILURE); \ - } \ - } while (0) - -class VideoSource; -class UnitTestEncodeCompleteCallback; -class UnitTestDecodeCompleteCallback; - -class UnitTest : public Test -{ -public: - UnitTest(); - virtual ~UnitTest(); - virtual void Perform(); - virtual void Print(); - -protected: - UnitTest(std::string name, std::string description); - virtual WebRtc_UWord32 CodecSpecific_SetBitrate( - WebRtc_UWord32 bitRate, - WebRtc_UWord32 /* frameRate */); - virtual void Setup(); - virtual void Teardown(); - virtual void RateControlTests(); - virtual int Decode(); - virtual int DecodeWithoutAssert(); - virtual int SetCodecSpecificParameters() {return 0;}; - - virtual bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes, - const void *ptrB, unsigned int bLengthBytes); - - WebRtc_UWord32 WaitForEncodedFrame() const; - WebRtc_UWord32 WaitForDecodedFrame() const; - - int _tests; - int _errors; - - VideoSource* _source; - unsigned char* _refFrame; - unsigned char* _refEncFrame; - unsigned char* _refDecFrame; - int _refEncFrameLength; - FILE* _sourceFile; - - UnitTestEncodeCompleteCallback* _encodeCompleteCallback; - UnitTestDecodeCompleteCallback* _decodeCompleteCallback; - enum { kMaxWaitEncTimeMs = 100 }; - enum { kMaxWaitDecTimeMs = 25 }; -}; - -class UnitTestEncodeCompleteCallback : public webrtc::EncodedImageCallback -{ -public: - UnitTestEncodeCompleteCallback(TestVideoEncodedBuffer* buffer, - WebRtc_UWord32 decoderSpecificSize = 0, - void* decoderSpecificInfo = NULL) : - _encodedVideoBuffer(buffer), - _decoderSpecificSize(decoderSpecificSize), - _decoderSpecificInfo(decoderSpecificInfo), - _encodeComplete(false) {} - WebRtc_Word32 Encoded(webrtc::EncodedImage& encodedImage, - const void* codecSpecificInfo, - const webrtc::RTPFragmentationHeader* - fragmentation = NULL); - bool EncodeComplete(); - // Note that this only makes sense if an encode has been completed - webrtc::VideoFrameType EncodedFrameType() const; -private: - TestVideoEncodedBuffer* _encodedVideoBuffer; - void* _decoderSpecificInfo; - WebRtc_UWord32 _decoderSpecificSize; - bool _encodeComplete; - webrtc::VideoFrameType _encodedFrameType; -}; - -class UnitTestDecodeCompleteCallback : public webrtc::DecodedImageCallback -{ -public: - UnitTestDecodeCompleteCallback(TestVideoBuffer* buffer) : - _decodedVideoBuffer(buffer), _decodeComplete(false) {} - WebRtc_Word32 Decoded(webrtc::RawImage& image); - bool DecodeComplete(); -private: - TestVideoBuffer* _decodedVideoBuffer; - bool _decodeComplete; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_UNIT_TEST_H_ - diff --git a/modules/video_coding/codecs/test_framework/video_buffer.cc b/modules/video_coding/codecs/test_framework/video_buffer.cc deleted file mode 100644 index 3958e908f..000000000 --- a/modules/video_coding/codecs/test_framework/video_buffer.cc +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include "video_buffer.h" - -using namespace webrtc; - -TestVideoBuffer::TestVideoBuffer(): -_buffer(0), -_bufferSize(0), -_bufferLength(0), -_startOffset(0), -_timeStamp(0), -_width(0), -_height(0) -{ - // -} - - -TestVideoBuffer::~TestVideoBuffer() -{ - _timeStamp = 0; - _startOffset = 0; - _bufferLength = 0; - _bufferSize = 0; - - if(_buffer) - { - delete [] _buffer; - _buffer = 0; - } -} - -TestVideoBuffer::TestVideoBuffer(const TestVideoBuffer& rhs) -: -_buffer(0), -_bufferSize(rhs._bufferSize), -_bufferLength(rhs._bufferLength), -_startOffset(rhs._startOffset), -_timeStamp(rhs._timeStamp), -_width(rhs._width), -_height(rhs._height) -{ - // make sure that our buffer is big enough - _buffer = new unsigned char[_bufferSize]; - - // only copy required length - memcpy(_buffer + _startOffset, rhs._buffer, _bufferLength); // GetBuffer() includes _startOffset -} - -void TestVideoBuffer::SetTimeStamp(unsigned int timeStamp) -{ - _timeStamp = timeStamp; -} - -unsigned int -TestVideoBuffer::GetWidth() const -{ - return _width; -} - -unsigned int -TestVideoBuffer::GetHeight() const -{ - return _height; -} - -void -TestVideoBuffer::SetWidth(unsigned int width) -{ - _width = width; -} - -void -TestVideoBuffer::SetHeight(unsigned int height) -{ - _height = height; -} - - -void TestVideoBuffer::Free() -{ - _timeStamp = 0; - _startOffset = 0; - _bufferLength = 0; - _bufferSize = 0; - _height = 0; - _width = 0; - - if(_buffer) - { - delete [] _buffer; - _buffer = 0; - } -} - -void TestVideoBuffer::VerifyAndAllocate(unsigned int minimumSize) -{ - if(minimumSize > _bufferSize) - { - // make sure that our buffer is big enough - unsigned char * newBufferBuffer = new unsigned char[minimumSize]; - if(_buffer) - { - // copy the old data - memcpy(newBufferBuffer, _buffer, _bufferSize); - delete [] _buffer; - } - _buffer = newBufferBuffer; - _bufferSize = minimumSize; - } -} - -int TestVideoBuffer::SetOffset(unsigned int length) -{ - if (length > _bufferSize || - length > _bufferLength) - { - return -1; - } - - unsigned int oldOffset = _startOffset; - - if(oldOffset > length) - { - unsigned int newLength = _bufferLength + (oldOffset-length);// increase by the diff - assert(newLength <= _bufferSize); - _bufferLength = newLength; - } - if(oldOffset < length) - { - if(_bufferLength > (length-oldOffset)) - { - _bufferLength -= (length-oldOffset); // decrease by the diff - } - } - _startOffset = length; // update - - return 0; -} - -void TestVideoBuffer::UpdateLength(unsigned int newLength) -{ - assert(newLength +_startOffset <= _bufferSize); - _bufferLength = newLength; -} - -void TestVideoBuffer::CopyBuffer(unsigned int length, const unsigned char* buffer) -{ - assert(length+_startOffset <= _bufferSize); - memcpy(_buffer+_startOffset, buffer, length); - _bufferLength = length; -} - -void TestVideoBuffer::CopyBuffer(TestVideoBuffer& fromVideoBuffer) -{ - assert(fromVideoBuffer.GetLength() + fromVideoBuffer.GetStartOffset() <= _bufferSize); - assert(fromVideoBuffer.GetSize() <= _bufferSize); - - _bufferLength = fromVideoBuffer.GetLength(); - _startOffset = fromVideoBuffer.GetStartOffset(); - _timeStamp = fromVideoBuffer.GetTimeStamp(); - _height = fromVideoBuffer.GetHeight(); - _width = fromVideoBuffer.GetWidth(); - - // only copy required length - memcpy(_buffer+_startOffset, fromVideoBuffer.GetBuffer(), fromVideoBuffer.GetLength()); // GetBuffer() includes _startOffset - -} - -void TestVideoBuffer::CopyPointer(const TestVideoBuffer& fromVideoBuffer) -{ - _bufferSize = fromVideoBuffer.GetSize(); - _bufferLength = fromVideoBuffer.GetLength(); - _startOffset = fromVideoBuffer.GetStartOffset(); - _timeStamp = fromVideoBuffer.GetTimeStamp(); - _height = fromVideoBuffer.GetHeight(); - _width = fromVideoBuffer.GetWidth(); - - _buffer = fromVideoBuffer.GetBuffer(); -} - -void TestVideoBuffer::ClearPointer() -{ - _buffer = NULL; -} - -void TestVideoBuffer::SwapBuffers(TestVideoBuffer& videoBuffer) -{ - unsigned char* tempBuffer = _buffer; - unsigned int tempSize = _bufferSize; - unsigned int tempLength =_bufferLength; - unsigned int tempOffset = _startOffset; - unsigned int tempTime = _timeStamp; - unsigned int tempHeight = _height; - unsigned int tempWidth = _width; - - _buffer = videoBuffer.GetBuffer(); - _bufferSize = videoBuffer.GetSize(); - _bufferLength = videoBuffer.GetLength(); - _startOffset = videoBuffer.GetStartOffset(); - _timeStamp = videoBuffer.GetTimeStamp(); - _height = videoBuffer.GetHeight(); - _width = videoBuffer.GetWidth(); - - - videoBuffer.Set(tempBuffer, tempSize, tempLength, tempOffset, tempTime); - videoBuffer.SetHeight(tempHeight); - videoBuffer.SetWidth(tempWidth); -} - -void TestVideoBuffer::Set(unsigned char* tempBuffer,unsigned int tempSize,unsigned int tempLength, unsigned int tempOffset,unsigned int timeStamp) -{ - _buffer = tempBuffer; - _bufferSize = tempSize; - _bufferLength = tempLength; - _startOffset = tempOffset; - _timeStamp = timeStamp; -} - -unsigned char* TestVideoBuffer::GetBuffer() const -{ - return _buffer+_startOffset; -} - -unsigned int TestVideoBuffer::GetStartOffset() const -{ - return _startOffset; -} - -unsigned int TestVideoBuffer::GetSize() const -{ - return _bufferSize; -} - -unsigned int TestVideoBuffer::GetLength() const -{ - return _bufferLength; -} - -unsigned int TestVideoBuffer::GetTimeStamp() const -{ - return _timeStamp; -} - -/** -* TestVideoEncodedBuffer -* -*/ - -TestVideoEncodedBuffer::TestVideoEncodedBuffer() : - _captureWidth(0), - _captureHeight(0), - _frameRate(-1) -{ - _frameType = kDeltaFrame; -} - -TestVideoEncodedBuffer::~TestVideoEncodedBuffer() -{ -} - -void TestVideoEncodedBuffer::SetCaptureWidth(unsigned short width) -{ - _captureWidth = width; -} - -void TestVideoEncodedBuffer::SetCaptureHeight(unsigned short height) -{ - _captureHeight = height; -} - -unsigned short TestVideoEncodedBuffer::GetCaptureWidth() -{ - return _captureWidth; -} - -unsigned short TestVideoEncodedBuffer::GetCaptureHeight() -{ - return _captureHeight; -} - -VideoFrameType TestVideoEncodedBuffer::GetFrameType() -{ - return _frameType; -} - -void TestVideoEncodedBuffer::SetFrameType(VideoFrameType frametype) -{ - _frameType = frametype; -} - -void TestVideoEncodedBuffer::Reset() -{ - _captureWidth = 0; - _captureHeight = 0; - _frameRate = -1; - _frameType = kDeltaFrame; -} - -void TestVideoEncodedBuffer::SetFrameRate(float frameRate) -{ - _frameRate = frameRate; -} - -float TestVideoEncodedBuffer::GetFrameRate() -{ - return _frameRate; -} diff --git a/modules/video_coding/codecs/test_framework/video_buffer.h b/modules/video_coding/codecs/test_framework/video_buffer.h deleted file mode 100644 index 824440e8e..000000000 --- a/modules/video_coding/codecs/test_framework/video_buffer.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_ - -#include "typedefs.h" -#include "video_image.h" - -class TestVideoBuffer -{ -public: - TestVideoBuffer(); - - virtual ~TestVideoBuffer(); - - TestVideoBuffer(const TestVideoBuffer& rhs); - - /** - * Verifies that current allocated buffer size is larger than or equal to the input size. - * If the current buffer size is smaller, a new allocation is made and the old buffer data is copied to the new buffer. - */ - void VerifyAndAllocate(unsigned int minimumSize); - - void UpdateLength(unsigned int newLength); - - void SwapBuffers(TestVideoBuffer& videoBuffer); - - void CopyBuffer(unsigned int length, const unsigned char* fromBuffer); - - void CopyBuffer(TestVideoBuffer& fromVideoBuffer); - - // Use with care, and remember to call ClearPointer() when done. - void CopyPointer(const TestVideoBuffer& fromVideoBuffer); - - void ClearPointer(); - - int SetOffset(unsigned int length); // Sets offset to beginning of frame in buffer - - void Free(); // Deletes frame buffer and resets members to zero - - void SetTimeStamp(unsigned int timeStamp); // Sets timestamp of frame (90kHz) - - /** - * Gets pointer to frame buffer - */ - unsigned char* GetBuffer() const; - - /** - * Gets allocated buffer size - */ - unsigned int GetSize() const; - - /** - * Gets length of frame - */ - unsigned int GetLength() const; - - /** - * Gets timestamp of frame (90kHz) - */ - unsigned int GetTimeStamp() const; - - unsigned int GetWidth() const; - unsigned int GetHeight() const; - - void SetWidth(unsigned int width); - void SetHeight(unsigned int height); - -private: - TestVideoBuffer& operator=(const TestVideoBuffer& inBuffer); - -private: - void Set(unsigned char* buffer,unsigned int size,unsigned int length,unsigned int offset, unsigned int timeStamp); - unsigned int GetStartOffset() const; - - unsigned char* _buffer; // Pointer to frame buffer - unsigned int _bufferSize; // Allocated buffer size - unsigned int _bufferLength; // Length (in bytes) of frame - unsigned int _startOffset; // Offset (in bytes) to beginning of frame in buffer - unsigned int _timeStamp; // Timestamp of frame (90kHz) - unsigned int _width; - unsigned int _height; -}; - -class TestVideoEncodedBuffer: public TestVideoBuffer -{ -public: - TestVideoEncodedBuffer(); - ~TestVideoEncodedBuffer(); - - void SetCaptureWidth(unsigned short width); - void SetCaptureHeight(unsigned short height); - unsigned short GetCaptureWidth(); - unsigned short GetCaptureHeight(); - - webrtc::VideoFrameType GetFrameType(); - void SetFrameType(webrtc::VideoFrameType frametype); - - void Reset(); - - void SetFrameRate(float frameRate); - float GetFrameRate(); - -private: - TestVideoEncodedBuffer& operator=(const TestVideoEncodedBuffer& inBuffer); - -private: - unsigned short _captureWidth; - unsigned short _captureHeight; - webrtc::VideoFrameType _frameType; - float _frameRate; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_BUFFER_H_ diff --git a/modules/video_coding/codecs/test_framework/video_source.cc b/modules/video_coding/codecs/test_framework/video_source.cc deleted file mode 100644 index 2045bd9d2..000000000 --- a/modules/video_coding/codecs/test_framework/video_source.cc +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_source.h" -#include "vplib.h" -#include -#include - -VideoSource::VideoSource() -: -_fileName("../../../../testFiles/foreman.yuv"), -_width(352), -_height(288), -_type(webrtc::kI420), -_frameRate(30) -{ -} - -VideoSource::VideoSource(std::string fileName, VideoSize size, - int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/) -: -_fileName(fileName), -_type(type), -_frameRate(frameRate) -{ - assert(size != kUndefined && size != kNumberOfVideoSizes); - assert(type != webrtc::kUnknown); - assert(frameRate > 0); - assert(GetWidthHeight(size, _width, _height) == 0); -} - -VideoSource::VideoSource(std::string fileName, int width, int height, - int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/) -: -_fileName(fileName), -_width(width), -_height(height), -_type(type), -_frameRate(frameRate) -{ - assert(width > 0); - assert(height > 0); - assert(type != webrtc::kUnknown); - assert(frameRate > 0); -} - -VideoSize -VideoSource::GetSize() const -{ - return GetSize(_width, _height); -} - -VideoSize -VideoSource::GetSize(WebRtc_UWord16 width, WebRtc_UWord16 height) -{ - if(width == 128 && height == 96) - { - return kSQCIF; - }else if(width == 160 && height == 120) - { - return kQQVGA; - }else if(width == 176 && height == 144) - { - return kQCIF; - }else if(width == 320 && height == 240) - { - return kQVGA; - }else if(width == 352 && height == 288) - { - return kCIF; - }else if(width == 640 && height == 480) - { - return kVGA; - }else if(width == 720 && height == 480) - { - return kNTSC; - }else if(width == 704 && height == 576) - { - return k4CIF; - }else if(width == 800 && height == 600) - { - return kSVGA; - }else if(width == 960 && height == 720) - { - return kHD; - }else if(width == 1024 && height == 768) - { - return kXGA; - }else if(width == 1440 && height == 1080) - { - return kFullHD; - }else if(width == 400 && height == 240) - { - return kWQVGA; - }else if(width == 800 && height == 480) - { - return kWVGA; - }else if(width == 1280 && height == 720) - { - return kWHD; - }else if(width == 1920 && height == 1080) - { - return kWFullHD; - } - return kUndefined; -} - -unsigned int -VideoSource::GetFrameLength() const -{ - return webrtc::CalcBufferSize(_type, _width, _height); -} - -const char* -VideoSource::GetMySizeString() const -{ - return VideoSource::GetSizeString(GetSize()); -} - -const char* -VideoSource::GetSizeString(VideoSize size) -{ - switch (size) - { - case kSQCIF: - return "SQCIF"; - case kQQVGA: - return "QQVGA"; - case kQCIF: - return "QCIF"; - case kQVGA: - return "QVGA"; - case kCIF: - return "CIF"; - case kVGA: - return "VGA"; - case kNTSC: - return "NTSC"; - case k4CIF: - return "4CIF"; - case kSVGA: - return "SVGA"; - case kHD: - return "HD"; - case kXGA: - return "XGA"; - case kFullHD: - return "Full_HD"; - case kWQVGA: - return "WQVGA"; - case kWHD: - return "WHD"; - case kWFullHD: - return "WFull_HD"; - default: - return "Undefined"; - } -} - -std::string -VideoSource::GetFilePath() const -{ - size_t slashPos = _fileName.find_last_of("/\\"); - if (slashPos == std::string::npos) - { - return "."; - } - - return _fileName.substr(0, slashPos); -} - -std::string -VideoSource::GetName() const -{ - // Remove path. - size_t slashPos = _fileName.find_last_of("/\\"); - if (slashPos == std::string::npos) - { - slashPos = 0; - } - else - { - slashPos++; - } - - // Remove extension and underscored suffix if it exists. - return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"), - _fileName.find_last_of(".")) - slashPos); -} - -void -VideoSource::Convert(const VideoSource &target, bool force /* = false */) const -{ - // Ensure target rate is less than or equal to source - // (i.e. we are only temporally downsampling). - assert(target.GetFrameRate() <= _frameRate); - // Only supports YUV420 currently. - assert(_type == webrtc::kI420 && target.GetType() == webrtc::kI420); - if (!force && (FileExists(target.GetFileName().c_str()) || - (target.GetWidth() == _width && target.GetHeight() == _height && target.GetFrameRate() == _frameRate))) - { - // Assume that the filename uniquely defines the content. - // If the file already exists, it is the correct file. - return; - } - FILE *inFile = NULL; - FILE *outFile = NULL; - - inFile = fopen(_fileName.c_str(), "rb"); - assert(inFile != NULL); - - outFile = fopen(target.GetFileName().c_str(), "wb"); - assert(outFile != NULL); - - FrameDropper fd; - fd.SetFrameRate(target.GetFrameRate(), _frameRate); - - const size_t lengthOutFrame = webrtc::CalcBufferSize(target.GetType(), - target.GetWidth(), target.GetHeight()); - assert(lengthOutFrame > 0); - unsigned char *outFrame = new unsigned char[lengthOutFrame]; - - const size_t lengthInFrame = webrtc::CalcBufferSize(_type, _width, _height); - assert(lengthInFrame > 0); - unsigned char *inFrame = new unsigned char[lengthInFrame]; - - while (fread(inFrame, 1, lengthInFrame, inFile) == lengthInFrame) - { - if (!fd.DropFrame()) - { - assert(target.GetWidth() == _width && - target.GetHeight() == _height); // Add video interpolator here! - fwrite(outFrame, 1, lengthOutFrame, outFile); - } - } - - delete inFrame; - delete outFrame; - fclose(inFile); - fclose(outFile); -} - -bool VideoSource::FileExists(const char* fileName) -{ - FILE* fp = NULL; - fp = fopen(fileName, "rb"); - if(fp != NULL) - { - fclose(fp); - return true; - } - return false; -} - - -int -VideoSource::GetWidthHeight( VideoSize size, int & width, int& height) -{ - switch(size) - { - case kSQCIF: - width = 128; - height = 96; - return 0; - case kQQVGA: - width = 160; - height = 120; - return 0; - case kQCIF: - width = 176; - height = 144; - return 0; - case kCGA: - width = 320; - height = 200; - return 0; - case kQVGA: - width = 320; - height = 240; - return 0; - case kSIF: - width = 352; - height = 240; - return 0; - case kWQVGA: - width = 400; - height = 240; - return 0; - case kCIF: - width = 352; - height = 288; - return 0; - case kW288p: - width = 512; - height = 288; - return 0; - case k448p: - width = 576; - height = 448; - return 0; - case kVGA: - width = 640; - height = 480; - return 0; - case k432p: - width = 720; - height = 432; - return 0; - case kW432p: - width = 768; - height = 432; - return 0; - case k4SIF: - width = 704; - height = 480; - return 0; - case kW448p: - width = 768; - height = 448; - return 0; - case kNTSC: - width = 720; - height = 480; - return 0; - case kFW448p: - width = 800; - height = 448; - return 0; - case kWVGA: - width = 800; - height = 480; - return 0; - case k4CIF: - width = 704; - height = 576; - return 0; - case kSVGA: - width = 800; - height = 600; - return 0; - case kW544p: - width = 960; - height = 544; - return 0; - case kW576p: - width = 1024; - height = 576; - return 0; - case kHD: - width = 960; - height = 720; - return 0; - case kXGA: - width = 1024; - height = 768; - return 0; - case kFullHD: - width = 1440; - height = 1080; - return 0; - case kWHD: - width = 1280; - height = 720; - return 0; - case kWFullHD: - width = 1920; - height = 1080; - return 0; - default: - return -1; - } -} - -FrameDropper::FrameDropper() -: -_dropsBetweenRenders(0), -_frameCounter(0) -{ -} - -bool -FrameDropper::DropFrame() -{ - _frameCounter++; - if (_frameCounter > _dropsBetweenRenders) - { - _frameCounter = 0; - return false; - } - return true; -} - -unsigned int -FrameDropper::DropsBetweenRenders() -{ - return _dropsBetweenRenders; -} - -void -FrameDropper::SetFrameRate(double frameRate, double maxFrameRate) -{ - if (frameRate >= 1.0) - { - _dropsBetweenRenders = static_cast(maxFrameRate / frameRate + 0.5) - 1; - } - else - { - _dropsBetweenRenders = 0; - } -} diff --git a/modules/video_coding/codecs/test_framework/video_source.h b/modules/video_coding/codecs/test_framework/video_source.h deleted file mode 100644 index 18b51c2cf..000000000 --- a/modules/video_coding/codecs/test_framework/video_source.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ - -#include - -#include "vplib.h" - -enum VideoSize - { - kUndefined, - kSQCIF, // 128*96 = 12 288 - kQQVGA, // 160*120 = 19 200 - kQCIF, // 176*144 = 25 344 - kCGA, // 320*200 = 64 000 - kQVGA, // 320*240 = 76 800 - kSIF, // 352*240 = 84 480 - kWQVGA, // 400*240 = 96 000 - kCIF, // 352*288 = 101 376 - kW288p, // 512*288 = 147 456 (WCIF) - k448p, // 576*448 = 281 088 - kVGA, // 640*480 = 307 200 - k432p, // 720*432 = 311 040 - kW432p, // 768*432 = 331 776 - k4SIF, // 704*480 = 337 920 - kW448p, // 768*448 = 344 064 - kNTSC, // 720*480 = 345 600 - kFW448p, // 800*448 = 358 400 - kWVGA, // 800*480 = 384 000 - k4CIF, // 704�576 = 405 504 - kSVGA, // 800*600 = 480 000 - kW544p, // 960*544 = 522 240 - kW576p, // 1024*576 = 589 824 (W4CIF) - kHD, // 960*720 = 691 200 - kXGA, // 1024*768 = 786 432 - kWHD, // 1280*720 = 921 600 - kFullHD, // 1440*1080 = 1 555 200 - kWFullHD, // 1920*1080 = 2 073 600 - - kNumberOfVideoSizes - }; - -class VideoSource -{ -public: - VideoSource(); - VideoSource(std::string fileName, VideoSize size, int frameRate = 30, - webrtc::VideoType type = webrtc::kI420); - VideoSource(std::string fileName, int width, int height, int frameRate = 30, - webrtc::VideoType type = webrtc::kI420); - - std::string GetFileName() const { return _fileName; } - int GetWidth() const { return _width; } - int GetHeight() const { return _height; } - webrtc::VideoType GetType() const { return _type; } - int GetFrameRate() const { return _frameRate; } - - // Returns the file path without a trailing slash. - std::string GetFilePath() const; - - // Returns the filename with the path (including the leading slash) removed. - std::string GetName() const; - - VideoSize GetSize() const; - static VideoSize GetSize(WebRtc_UWord16 width, WebRtc_UWord16 height); - unsigned int GetFrameLength() const; - - // Returns a human-readable size string. - static const char* GetSizeString(VideoSize size); - const char* GetMySizeString() const; - - // Opens the video source, converting and writing to the specified target. - // If force is true, the conversion will be done even if the target file - // already exists. - void Convert(const VideoSource& target, bool force = false) const; - static bool FileExists(const char* fileName); -private: - static int GetWidthHeight( VideoSize size, int& width, int& height); - std::string _fileName; - int _width; - int _height; - webrtc::VideoType _type; - int _frameRate; -}; - -class FrameDropper -{ -public: - FrameDropper(); - bool DropFrame(); - unsigned int DropsBetweenRenders(); - void SetFrameRate(double frameRate, double maxFrameRate); - -private: - unsigned int _dropsBetweenRenders; - unsigned int _frameCounter; -}; - - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ - diff --git a/modules/video_coding/codecs/vp8/main/interface/vp8.h b/modules/video_coding/codecs/vp8/main/interface/vp8.h deleted file mode 100644 index ab82f3495..000000000 --- a/modules/video_coding/codecs/vp8/main/interface/vp8.h +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * vp8.h - * WEBRTC VP8 wrapper interface - */ - - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_ - -#include "video_codec_interface.h" - -// VPX forward declaration -typedef struct vpx_codec_ctx vpx_codec_ctx_t; -typedef struct vpx_codec_ctx vpx_dec_ctx_t; -typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t; -typedef struct vpx_image vpx_image_t; -typedef struct vpx_ref_frame vpx_ref_frame_t; - -//#define VP8_LATEST - -namespace webrtc -{ - -/******************************/ -/* VP8Encoder class */ -/******************************/ -class VP8Encoder : public VideoEncoder -{ -public: - VP8Encoder(); - virtual ~VP8Encoder(); - -// Free encoder memory. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 Release(); - -// Reset encoder state and prepare for a new call. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERR_PARAMETER -// WEBRTC_VIDEO_CODEC_ERROR - virtual WebRtc_Word32 Reset(); - -// Initialize the encoder with the information from the codecSettings -// -// Input: -// - codecSettings : Codec settings -// - numberOfCores : Number of cores available for the encoder -// - maxPayloadSize : The maximum size each payload is allowed -// to have. Usually MTU - overhead. -// -// Return value : Set bit rate if OK -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERR_PARAMETER -// WEBRTC_VIDEO_CODEC_ERR_SIZE -// WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED -// WEBRTC_VIDEO_CODEC_MEMORY -// WEBRTC_VIDEO_CODEC_ERROR - virtual WebRtc_Word32 InitEncode(const VideoCodec* codecSettings, - WebRtc_Word32 numberOfCores, - WebRtc_UWord32 maxPayloadSize); - -// Encode an I420 image (as a part of a video stream). The encoded image -// will be returned to the user through the encode complete callback. -// -// Input: -// - inputImage : Image to be encoded -// - frameTypes : Frame type to be generated by the encoder. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERR_PARAMETER -// WEBRTC_VIDEO_CODEC_MEMORY -// WEBRTC_VIDEO_CODEC_ERROR -// WEBRTC_VIDEO_CODEC_TIMEOUT - - virtual WebRtc_Word32 Encode(const RawImage& inputImage, - const CodecSpecificInfo* codecSpecificInfo, - VideoFrameType frameType); - -// Register an encode complete callback object. -// -// Input: -// - callback : Callback object which handles encoded images. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterEncodeCompleteCallback(EncodedImageCallback* - callback); - -// Inform the encoder of the new packet loss rate in the network -// -// - packetLoss : Fraction lost -// (loss rate in percent = 100 * packetLoss / 255) -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERROR -// - virtual WebRtc_Word32 SetPacketLoss(WebRtc_UWord32 packetLoss); - -// Inform the encoder about the new target bit rate. -// -// - newBitRate : New target bit rate -// - frameRate : The target frame rate -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRateKbit, - WebRtc_UWord32 frameRate); - -// Get version number for the codec. -// -// Input: -// - version : Pointer to allocated char buffer. -// - buflen : Length of provided char buffer. -// -// Output: -// - version : Version number string written to char buffer. -// -// Return value : >0 - Length of written string. -// <0 - WEBRTC_VIDEO_CODEC_ERR_SIZE - virtual WebRtc_Word32 Version(WebRtc_Word8 *version, - WebRtc_Word32 length) const; - static WebRtc_Word32 VersionStatic(WebRtc_Word8 *version, - WebRtc_Word32 length); - -private: -// Call encoder initialize function and set speed. - WebRtc_Word32 InitAndSetSpeed(); - -// Determine maximum target for Intra frames -// -// Input: -// - optimalBuffersize : Optimal buffer size -// Return Value : Max target size for Intra frames represented as -// percentage of the per frame bandwidth -#ifdef VP8_LATEST - WebRtc_Word32 MaxIntraTarget(WebRtc_Word32 optimalBuffersize); -#endif - EncodedImage _encodedImage; - EncodedImageCallback* _encodedCompleteCallback; - WebRtc_Word32 _width; - WebRtc_Word32 _height; - WebRtc_Word32 _maxBitRateKbit; - int _maxFrameRate; - bool _inited; - WebRtc_UWord32 _timeStamp; - WebRtc_UWord16 _pictureID; - bool _pictureLossIndicationOn; - bool _feedbackModeOn; - bool _nextRefIsGolden; - bool _lastAcknowledgedIsGolden; - bool _haveReceivedAcknowledgement; - WebRtc_UWord16 _pictureIDLastSentRef; - WebRtc_UWord16 _pictureIDLastAcknowledgedRef; - int _cpuSpeed; - - vpx_codec_ctx_t* _encoder; - vpx_codec_enc_cfg_t* _cfg; - vpx_image_t* _raw; -};// end of VP8Encoder class - -/******************************/ -/* VP8Decoder class */ -/******************************/ -class VP8Decoder : public VideoDecoder -{ -public: - VP8Decoder(); - virtual ~VP8Decoder(); - -// Initialize the decoder. -// -// Return value : WEBRTC_VIDEO_CODEC_OK. -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERROR - virtual WebRtc_Word32 InitDecode(const VideoCodec* inst, - WebRtc_Word32 numberOfCores); - -// Decode encoded image (as a part of a video stream). The decoded image -// will be returned to the user through the decode complete callback. -// -// Input: -// - inputImage : Encoded image to be decoded -// - missingFrames : True if one or more frames have been lost -// since the previous decode call. -// - codecSpecificInfo : pointer to specific codec data -// - renderTimeMs : Render time in Ms -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERROR -// WEBRTC_VIDEO_CODEC_ERR_PARAMETER - virtual WebRtc_Word32 Decode(const EncodedImage& inputImage, - bool missingFrames, - const void* /*codecSpecificInfo*/, - WebRtc_Word64 /*renderTimeMs*/); - -// Register a decode complete callback object. -// -// Input: -// - callback : Callback object which handles decoded images. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. - virtual WebRtc_Word32 RegisterDecodeCompleteCallback(DecodedImageCallback* - callback); - -// Free decoder memory. -// -// Return value : WEBRTC_VIDEO_CODEC_OK if OK -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_ERROR - virtual WebRtc_Word32 Release(); - -// Reset decoder state and prepare for a new call. -// -// Return value : WEBRTC_VIDEO_CODEC_OK. -// <0 - Errors: -// WEBRTC_VIDEO_CODEC_UNINITIALIZED -// WEBRTC_VIDEO_CODEC_ERROR - virtual WebRtc_Word32 Reset(); - virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8* /*buffer*/, - WebRtc_Word32 /*size*/) - { return -1; } - -// Create a copy of the codec and its internal state. -// -// Return value : A copy of the instance if OK, NULL otherwise. - virtual VideoDecoder* Copy(); - -private: -// Copy reference image from this _decoder to the _decoder in copyTo. Set which -// frame type to copy in _refFrame->frame_type before the call to this function. - int CopyReference(VP8Decoder* copyTo); - - RawImage _decodedImage; - DecodedImageCallback* _decodeCompleteCallback; - bool _inited; - bool _feedbackModeOn; - vpx_dec_ctx_t* _decoder; - VideoCodec* _inst; - WebRtc_Word32 _numCores; - EncodedImage _lastKeyFrame; - int _imageFormat; - vpx_ref_frame_t* _refFrame; - -};// end of VP8Decoder class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_ diff --git a/modules/video_coding/codecs/vp8/main/source/Android.mk b/modules/video_coding/codecs/vp8/main/source/Android.mk deleted file mode 100644 index 65599151b..000000000 --- a/modules/video_coding/codecs/vp8/main/source/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_vp8 -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := vp8.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../../../modules/interface \ - $(LOCAL_PATH)/../../../../../../system_wrappers/interface \ - external/libvpx - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_coding/codecs/vp8/main/source/vp8.cc b/modules/video_coding/codecs/vp8/main/source/vp8.cc deleted file mode 100644 index c9403ab9e..000000000 --- a/modules/video_coding/codecs/vp8/main/source/vp8.cc +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * vp8.cc - * - * This file contains the WEBRTC VP8 wrapper implementation - * - */ -#include "vp8.h" -#include "tick_util.h" - -#include "vpx/vpx_encoder.h" -#include "vpx/vpx_decoder.h" -#include "vpx/vp8cx.h" -#include "vpx/vp8dx.h" - -#include -#include -#include - -#include "module_common_types.h" - -#define VP8_FREQ_HZ 90000 -//#define DEV_PIC_LOSS - -namespace webrtc -{ - -VP8Encoder::VP8Encoder(): - _encodedImage(), - _encodedCompleteCallback(NULL), - _width(0), - _height(0), - _maxBitRateKbit(0), - _inited(false), - _timeStamp(0), - _pictureID(0), - _pictureLossIndicationOn(false), - _feedbackModeOn(false), - _nextRefIsGolden(true), - _lastAcknowledgedIsGolden(true), - _haveReceivedAcknowledgement(false), - _pictureIDLastSentRef(0), - _pictureIDLastAcknowledgedRef(0), - _cpuSpeed(-6), // default value - _encoder(NULL), - _cfg(NULL), - _raw(NULL) -{ - srand((WebRtc_UWord32)TickTime::MillisecondTimestamp()); -} - -VP8Encoder::~VP8Encoder() -{ - Release(); -} - -WebRtc_Word32 -VP8Encoder::VersionStatic(WebRtc_Word8* version, WebRtc_Word32 length) -{ - const WebRtc_Word8* str = "WebM/VP8 version 1.0.0\n"; // Bali - WebRtc_Word32 verLen = (WebRtc_Word32)strlen(str); - if (verLen > length) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - strncpy(version, str, length); - return verLen; -} - -WebRtc_Word32 -VP8Encoder::Version(WebRtc_Word8 *version, WebRtc_Word32 length) const -{ - return VersionStatic(version, length); -} - -WebRtc_Word32 -VP8Encoder::Release() -{ - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - _encodedImage._buffer = NULL; - } - if (_encoder != NULL) - { - if (vpx_codec_destroy(_encoder)) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - delete _encoder; - _encoder = NULL; - } - if (_cfg != NULL) - { - delete _cfg; - _cfg = NULL; - } - if (_raw != NULL) - { - vpx_img_free(_raw); - delete _raw; - _raw = NULL; - } - _inited = false; - - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Encoder::Reset() -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - - if (_encoder != NULL) - { - if (vpx_codec_destroy(_encoder)) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - delete _encoder; - _encoder = NULL; - } - - _timeStamp = 0; - - _encoder = new vpx_codec_ctx_t; - - return InitAndSetSpeed(); -} - -WebRtc_Word32 -VP8Encoder::SetRates(WebRtc_UWord32 newBitRateKbit, WebRtc_UWord32 newFrameRate) -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (_encoder->err) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - if (newFrameRate < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - - // update bit rate - if (_maxBitRateKbit > 0 && - newBitRateKbit > static_cast(_maxBitRateKbit)) - { - newBitRateKbit = _maxBitRateKbit; - } - _cfg->rc_target_bitrate = newBitRateKbit; // in kbit/s - - // update frame rate - if (newFrameRate != _maxFrameRate) - { - _maxFrameRate = static_cast(newFrameRate); - _cfg->g_timebase.num = 1; - _cfg->g_timebase.den = _maxFrameRate; - } - - // update encoder context - if (vpx_codec_enc_config_set(_encoder, _cfg)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Encoder::InitEncode(const VideoCodec* inst, - WebRtc_Word32 numberOfCores, - WebRtc_UWord32 /*maxPayloadSize */) -{ - if (inst == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (inst->maxFramerate < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (inst->startBitrate < 0 || inst->maxBitrate < 0) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - // allow zero to represent an unspecified maxBitRate - if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (inst->width < 1 || inst->height < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (numberOfCores < 1) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } -#ifdef DEV_PIC_LOSS - // we need to know if we use feedback - _feedbackModeOn = inst->codecSpecific.VP8.feedbackModeOn; - _pictureLossIndicationOn = inst->codecSpecific.VP8.pictureLossIndicationOn; -#endif - - WebRtc_Word32 retVal = Release(); - if (retVal < 0) - { - return retVal; - } - if (_encoder == NULL) - { - _encoder = new vpx_codec_ctx_t; - } - if (_cfg == NULL) - { - _cfg = new vpx_codec_enc_cfg_t; - } - if (_raw == NULL) - { - _raw = new vpx_image_t; - } - - _timeStamp = 0; - _maxBitRateKbit = inst->maxBitrate; - _maxFrameRate = inst->maxFramerate; - _width = inst->width; - _height = inst->height; - - // random start 16 bits is enough - _pictureID = ((WebRtc_UWord16)rand()) % 0x7FFF; - - // allocate memory for encoded image - if (_encodedImage._buffer != NULL) - { - delete [] _encodedImage._buffer; - } - _encodedImage._size = (3 * inst->width * inst->height) >> 1; - _encodedImage._buffer = new WebRtc_UWord8[_encodedImage._size]; - - vpx_img_alloc(_raw, IMG_FMT_I420, inst->width, inst->height, 1); - // populate encoder configuration with default values - if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), _cfg, 0)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - - _cfg->g_w = inst->width; - _cfg->g_h = inst->height; - if (_maxBitRateKbit > 0 && - inst->startBitrate > static_cast(_maxBitRateKbit)) - { - _cfg->rc_target_bitrate = _maxBitRateKbit; - } - else - { - _cfg->rc_target_bitrate = inst->startBitrate; // in kbit/s - } - - // setting the time base of the codec - _cfg->g_timebase.num = 1; - _cfg->g_timebase.den = _maxFrameRate; - - _cfg->g_error_resilient = 1; //enabled - _cfg->g_lag_in_frames = 0; // 0- no frame lagging - - _cfg->g_threads = numberOfCores; - - // rate control settings - _cfg->rc_dropframe_thresh = 0; - _cfg->rc_end_usage = VPX_CBR; - _cfg->g_pass = VPX_RC_ONE_PASS; - _cfg->rc_resize_allowed = 0; - _cfg->rc_min_quantizer = 4; - _cfg->rc_max_quantizer = 56; - _cfg->rc_undershoot_pct = 100; - _cfg->rc_overshoot_pct = 15; - _cfg->rc_buf_initial_sz = 500; - _cfg->rc_buf_optimal_sz = 600; - _cfg->rc_buf_sz = 1000; -#ifdef VP8_LATEST - _cfg->rc_max_intra_bitrate_pct = MaxIntraTarget(_cfg->rc_buf_optimal_sz); -#endif - - -#ifdef DEV_PIC_LOSS - // this can only be off if we know we use feedback - if (_pictureLossIndicationOn) - { - // don't generate key frame unless we tell you - _cfg->kf_mode = VPX_KF_DISABLED; - } - else -#endif - { - _cfg->kf_mode = VPX_KF_AUTO; - _cfg->kf_max_dist = 300; - } - - switch (inst->codecSpecific.VP8.complexity) - { - case kComplexityHigh: - { - _cpuSpeed = -5; - break; - } - case kComplexityHigher: - { - _cpuSpeed = -4; - break; - } - case kComplexityMax: - { - _cpuSpeed = -3; - break; - } - default: - { - _cpuSpeed = -6; - break; - } - } - - return InitAndSetSpeed(); -} - -WebRtc_Word32 -VP8Encoder::InitAndSetSpeed() -{ - // construct encoder context - vpx_codec_enc_cfg_t cfg_copy = *_cfg; - if (vpx_codec_enc_init(_encoder, vpx_codec_vp8_cx(), _cfg, 0)) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - - vpx_codec_control(_encoder, VP8E_SET_CPUUSED, _cpuSpeed); - - *_cfg = cfg_copy; - - _inited = true; - return WEBRTC_VIDEO_CODEC_OK; -} - -#ifdef VP8_LATEST -WebRtc_Word32 -VP8Encoder::MaxIntraTarget(WebRtc_Word32 optimalBuffersize) -{ - // Set max to 1 / 2 of the optimal buffer level (normalize by target BR). - // Max target size = 0.5 * optimalBufferSize * targetBR[Kbps]. - // This values is presented in percentage of perFrameBw. - // perFrameBw = targetBR[Kbps] * 1000 / frameRate. - // The target in % is as follows: - WebRtc_Word32 targetPct = (optimalBuffersize >> 1) * _maxFrameRate / 10; - - // Don't go below 3 times the per frame bandwidth. - const WebRtc_Word32 minIntraTh = 300; - targetPct = (targetPct < minIntraTh) ? minIntraTh: targetPct; - - return targetPct; -} -#endif - -WebRtc_Word32 -VP8Encoder::Encode(const RawImage& inputImage, - const CodecSpecificInfo* codecSpecificInfo, - VideoFrameType frameTypes) -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (inputImage._buffer == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (_encodedCompleteCallback == NULL) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - - vpx_codec_iter_t iter = NULL; - - // image in vpx_image_t format - _raw->planes[PLANE_Y] = inputImage._buffer; - _raw->planes[PLANE_U] = &inputImage._buffer[_height * _width]; - _raw->planes[PLANE_V] = &inputImage._buffer[_height * _width * 5 >> 2]; - - int flags = 0; - if (frameTypes == kKeyFrame) - { - flags |= VPX_EFLAG_FORCE_KF; // will update both golden and altref - _encodedImage._frameType = kKeyFrame; - _pictureIDLastSentRef = _pictureID; - } - else - { -#ifdef DEV_PIC_LOSS - if (_feedbackModeOn && codecSpecificInfo) - { - const CodecSpecificInfo* info = static_cast(codecSpecificInfo); - if (info->codecType == kVideoCodecVP8) - { - // codecSpecificInfo will contain received RPSI and SLI - // picture IDs. This will help us decide on when to switch type - // of reference frame - - // if we receive SLI - // force using an old golden or altref as a reference - - if (info->codecSpecific.VP8.hasReceivedSLI) - { - // if this is older than my last acked ref we can ignore it - // info->codecSpecific.VP8.pictureIdSLI valid 6 bits => 64 frames - - // since picture id can wrap check if in between our last sent and last acked - - bool sendRefresh = false; - // check for a wrap in picture ID - if ((_pictureIDLastAcknowledgedRef & 0x3f) > (_pictureID & 0x3f)) - { - // we have a wrap - if ( info->codecSpecific.VP8.pictureIdSLI > (_pictureIDLastAcknowledgedRef&0x3f)|| - info->codecSpecific.VP8.pictureIdSLI < (_pictureID & 0x3f)) - { - sendRefresh = true; - } - } - else if (info->codecSpecific.VP8.pictureIdSLI > (_pictureIDLastAcknowledgedRef&0x3f)&& - info->codecSpecific.VP8.pictureIdSLI < (_pictureID & 0x3f)) - { - sendRefresh = true; - } - - // right now we could also ignore it if it's older than our last sent ref since - // last sent ref only refers back to last acked - // _pictureIDLastSentRef; - if (sendRefresh) - { - flags |= VP8_EFLAG_NO_REF_LAST; // Don't reference the last frame - - if (_haveReceivedAcknowledgement) - { - // we cant set this if we refer to a key frame - if (_lastAcknowledgedIsGolden) - { - flags |= VP8_EFLAG_NO_REF_ARF; // Don't reference the alternate reference frame - } - else - { - flags |= VP8_EFLAG_NO_REF_GF; // Don't reference the golden frame - } - } - } - } - if (info->codecSpecific.VP8.hasReceivedRPSI) - { - if ((info->codecSpecific.VP8.pictureIdRPSI & 0x3fff) == (_pictureIDLastSentRef & 0x3fff)) // compare 14 bits - { - // remote peer have received our last reference frame - // switch frame type - _haveReceivedAcknowledgement = true; - _nextRefIsGolden = !_nextRefIsGolden; - _pictureIDLastAcknowledgedRef = _pictureIDLastSentRef; - } - } - } - const WebRtc_UWord16 periodX = 64; // we need a period X to decide on the distance between golden and altref - if (_pictureID % periodX == 0) - { - // only required if we have had a loss - // however we don't acknowledge a SLI so if that is lost it's no good - flags |= VP8_EFLAG_NO_REF_LAST; // Don't reference the last frame - - if (_nextRefIsGolden) - { - flags |= VP8_EFLAG_FORCE_GF; // force a golden - flags |= VP8_EFLAG_NO_UPD_ARF; // don't update altref - if (_haveReceivedAcknowledgement) - { - // we can't set this if we refer to a key frame - // pw temporary as proof of concept - flags |= VP8_EFLAG_NO_REF_GF; // Don't reference the golden frame - } - } - else - { - flags |= VP8_EFLAG_FORCE_ARF; // force an altref - flags |= VP8_EFLAG_NO_UPD_GF; // Don't update golden - if (_haveReceivedAcknowledgement) - { - // we can't set this if we refer to a key frame - // pw temporary as proof of concept - flags |= VP8_EFLAG_NO_REF_ARF; // Don't reference the alternate reference frame - } - } - // remember our last reference frame - _pictureIDLastSentRef = _pictureID; - } - else - { - flags |= VP8_EFLAG_NO_UPD_GF; // don't update golden - flags |= VP8_EFLAG_NO_UPD_ARF; // don't update altref - } - } -#endif - _encodedImage._frameType = kDeltaFrame; - } - - if (vpx_codec_encode(_encoder, _raw, _timeStamp, 1, flags, VPX_DL_REALTIME)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - _timeStamp++; - - const vpx_codec_cx_pkt_t *pkt= vpx_codec_get_cx_data(_encoder, &iter); // no lagging => 1 frame at a time - if (pkt == NULL && !_encoder->err) - { - // dropped frame - return WEBRTC_VIDEO_CODEC_OK; - } - else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) - { - CodecSpecificInfo codecSpecific; - codecSpecific.codecType = kVideoCodecVP8; - CodecSpecificInfoVP8 *vp8Info = &(codecSpecific.codecSpecific.VP8); - - vp8Info->pictureId = _pictureID; - vp8Info->nonReference - = (pkt->data.frame.flags & VPX_FRAME_IS_DROPPABLE); - - memcpy(_encodedImage._buffer, pkt->data.frame.buf, pkt->data.frame.sz); - _encodedImage._length = WebRtc_UWord32(pkt->data.frame.sz); - _encodedImage._encodedHeight = _raw->h; - _encodedImage._encodedWidth = _raw->w; - - // check if encoded frame is a key frame - if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) - { - _encodedImage._frameType = kKeyFrame; - } - - if (_encodedImage._length > 0) - { - _encodedImage._timeStamp = inputImage._timeStamp; - - // Figure out where partition boundaries are located. - RTPFragmentationHeader fragInfo; - fragInfo.VerifyAndAllocateFragmentationHeader(2); // two partitions: 1st and 2nd - - // First partition - fragInfo.fragmentationOffset[0] = 0; - WebRtc_UWord8 *firstByte = _encodedImage._buffer; - WebRtc_UWord32 tmpSize = (firstByte[2] << 16) | (firstByte[1] << 8) - | firstByte[0]; - fragInfo.fragmentationLength[0] = (tmpSize >> 5) & 0x7FFFF; - fragInfo.fragmentationPlType[0] = 0; // not known here - fragInfo.fragmentationTimeDiff[0] = 0; - - // Second partition - fragInfo.fragmentationOffset[1] = fragInfo.fragmentationLength[0]; - fragInfo.fragmentationLength[1] = _encodedImage._length - - fragInfo.fragmentationLength[0]; - fragInfo.fragmentationPlType[1] = 0; // not known here - fragInfo.fragmentationTimeDiff[1] = 0; - - _encodedCompleteCallback->Encoded(_encodedImage, &codecSpecific, - &fragInfo); - } - - _pictureID = (_pictureID + 1) % 0x7FFF; // prepare next - return WEBRTC_VIDEO_CODEC_OK; - } - return WEBRTC_VIDEO_CODEC_ERROR; -} - -WebRtc_Word32 -VP8Encoder::SetPacketLoss(WebRtc_UWord32 packetLoss) -{ - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Encoder::RegisterEncodeCompleteCallback(EncodedImageCallback* callback) -{ - _encodedCompleteCallback = callback; - return WEBRTC_VIDEO_CODEC_OK; -} - -VP8Decoder::VP8Decoder(): - _decodeCompleteCallback(NULL), - _inited(false), - _feedbackModeOn(false), - _decoder(NULL), - _inst(NULL), - _numCores(1), - _lastKeyFrame(), - _imageFormat(VPX_IMG_FMT_NONE), - _refFrame(NULL) -{ -} - -VP8Decoder::~VP8Decoder() -{ - _inited = true; // in order to do the actual release - Release(); -} - -WebRtc_Word32 -VP8Decoder::Reset() -{ - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - InitDecode(NULL, 1); - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Decoder::InitDecode(const VideoCodec* inst, - WebRtc_Word32 numberOfCores) -{ - vp8_postproc_cfg_t ppcfg; - WebRtc_Word32 retVal = Release(); - if (retVal < 0 ) - { - return retVal; - } - if (_decoder == NULL) - { - _decoder = new vpx_dec_ctx_t; - } -#ifdef DEV_PIC_LOSS - if(inst && inst->codecType == kVideoCodecVP8) - { - _feedbackModeOn = inst->codecSpecific.VP8.feedbackModeOn; - } -#endif - - vpx_codec_dec_cfg_t cfg; - cfg.threads = numberOfCores; - cfg.h = cfg.w = 0; // set after decode - - if(vpx_codec_dec_init(_decoder, vpx_codec_vp8_dx(), NULL, 0)) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - - // TODO(mikhal): evaluate post-proc settings - // config post-processing settings for decoder - ppcfg.post_proc_flag = VP8_DEBLOCK; - // Strength of deblocking filter. Valid range:[0,16] - ppcfg.deblocking_level = 5; - // ppcfg.NoiseLevel = 1; //Noise intensity. Valid range: [0,7] - vpx_codec_control(_decoder, VP8_SET_POSTPROC, &ppcfg); - - // Save the VideoCodec instance for later; mainly for duplicating the decoder. - if (inst) - { - if (!_inst) - { - _inst = new VideoCodec; - } - *_inst = *inst; - } - _numCores = numberOfCores; - - _inited = true; - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Decoder::Decode(const EncodedImage& inputImage, - bool missingFrames, - const void* /*codecSpecificInfo*/, - WebRtc_Word64 /*renderTimeMs*/) - { - if (!_inited) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (inputImage._buffer == NULL) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (_decodeCompleteCallback == NULL) - { - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; - } - if (inputImage._length <= 0) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - if (inputImage._completeFrame == false) - { - // future improvement - // we can't decode this frame - if (_feedbackModeOn) - { - return WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI; - } - else - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - } - vpx_dec_iter_t _iter = NULL; - vpx_image_t* img; - - // scan for number of bytes used for picture ID - WebRtc_UWord64 pictureID = inputImage._buffer[0] & 0x7F; - WebRtc_UWord8 numberOfBytes = 1; - if (inputImage._buffer[0] & 0x80) - { - pictureID <<= 8; - pictureID += inputImage._buffer[1]; - ++numberOfBytes; - } - - // check for missing frames - if (missingFrames) - { - // call decoder with zero data length to signal missing frames - if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - } - - // we remove the picture ID here - if (vpx_codec_decode(_decoder, - inputImage._buffer + numberOfBytes, - inputImage._length - numberOfBytes, - 0, - VPX_DL_REALTIME)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - - // Store encoded frame if key frame. (Used in Copy method.) - if (inputImage._frameType == kKeyFrame) - { - // Reduce size due to PictureID that we won't copy. - const int bytesToCopy = inputImage._length - numberOfBytes; - if (_lastKeyFrame._size < bytesToCopy) - { - delete [] _lastKeyFrame._buffer; - _lastKeyFrame._buffer = NULL; - _lastKeyFrame._size = 0; - } - - WebRtc_UWord8* tempBuffer = _lastKeyFrame._buffer; // Save buffer ptr. - WebRtc_UWord32 tempSize = _lastKeyFrame._size; // Save size. - _lastKeyFrame = inputImage; // Shallow copy. - _lastKeyFrame._buffer = tempBuffer; // Restore buffer ptr. - _lastKeyFrame._size = tempSize; // Restore buffer size. - if (!_lastKeyFrame._buffer) - { - // Allocate memory. - _lastKeyFrame._size = bytesToCopy; - _lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size]; - } - // Copy encoded frame. - memcpy(_lastKeyFrame._buffer, inputImage._buffer + numberOfBytes, - bytesToCopy); - _lastKeyFrame._length = bytesToCopy; - } - - int lastRefUpdates = 0; -#ifdef DEV_PIC_LOSS - if (vpx_codec_control(_decoder, VP8D_GET_LAST_REF_UPDATES, &lastRefUpdates)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - int corrupted = 0; - if (vpx_codec_control(_decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted)) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } -#endif - - img = vpx_codec_get_frame(_decoder, &_iter); - - // Allocate memory for decoded image - WebRtc_UWord32 requiredSize = (3 * img->h * img->w) >> 1; - if (_decodedImage._buffer != NULL) - { - delete [] _decodedImage._buffer; - _decodedImage._buffer = NULL; - } - if (_decodedImage._buffer == NULL) - { - _decodedImage._size = requiredSize; - _decodedImage._buffer = new WebRtc_UWord8[_decodedImage._size]; - if (_decodedImage._buffer == NULL) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - } - - WebRtc_UWord8* buf; - WebRtc_UWord32 locCnt = 0; - WebRtc_UWord32 plane, y; - - for (plane = 0; plane < 3; plane++) - { - buf = img->planes[plane]; - WebRtc_UWord32 shiftFactor = plane ? 1 : 0; - for(y = 0; y < img->d_h >> shiftFactor; y++) - { - memcpy(&_decodedImage._buffer[locCnt], buf, img->d_w >> shiftFactor); - locCnt += img->d_w >> shiftFactor; - buf += img->stride[plane]; - } - } - - // Set image parameters - _decodedImage._height = img->d_h; - _decodedImage._width = img->d_w; - _decodedImage._length = (3 * img->d_h * img->d_w) >> 1; - _decodedImage._timeStamp = inputImage._timeStamp; - _decodeCompleteCallback->Decoded(_decodedImage); - - // Remember image format for later - _imageFormat = img->fmt; - - // we need to communicate that we should send a RPSI with a specific picture ID - - // TODO(pw): how do we know it's a golden or alt reference frame? libvpx will - // provide an API for now I added it temporarily - if((lastRefUpdates & VP8_GOLD_FRAME) || (lastRefUpdates & VP8_ALTR_FRAME)) - { - if (!missingFrames && (inputImage._completeFrame == true)) - //if (!corrupted) // TODO(pw): Can we engage this line intead of the above? - { - _decodeCompleteCallback->ReceivedDecodedReferenceFrame(pictureID); - } - } - _decodeCompleteCallback->ReceivedDecodedFrame(pictureID); - -#ifdef DEV_PIC_LOSS - if (corrupted) - { - // we can decode but with artifacts - return WEBRTC_VIDEO_CODEC_REQUEST_SLI; - } -#endif - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Decoder::RegisterDecodeCompleteCallback(DecodedImageCallback* callback) -{ - _decodeCompleteCallback = callback; - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VP8Decoder::Release() -{ - if (_decodedImage._buffer != NULL) - { - delete [] _decodedImage._buffer; - _decodedImage._buffer = NULL; - } - if (_lastKeyFrame._buffer != NULL) - { - delete [] _lastKeyFrame._buffer; - _lastKeyFrame._buffer = NULL; - } - if (_decoder != NULL) - { - if(vpx_codec_destroy(_decoder)) - { - return WEBRTC_VIDEO_CODEC_MEMORY; - } - delete _decoder; - _decoder = NULL; - } - if (_inst != NULL) - { - delete _inst; - _inst = NULL; - } - if (_refFrame != NULL) - { - vpx_img_free(&_refFrame->img); - delete _refFrame; - _refFrame = NULL; - } - - _inited = false; - return WEBRTC_VIDEO_CODEC_OK; -} - -VideoDecoder* -VP8Decoder::Copy() -{ - // Sanity checks. - if (!_inited) - { - // Not initialized. - assert(false); - return NULL; - } - if (_decodedImage._buffer == NULL) - { - // Nothing has been decoded before; cannot clone. - assert(false); - return NULL; - } - if (_lastKeyFrame._buffer == NULL) - { - // Cannot clone if we have no key frame to start with. - assert(false); - return NULL; - } - - // Create a new VideoDecoder object - VP8Decoder *copyTo = new VP8Decoder; - - // Initialize the new decoder - if (copyTo->InitDecode(_inst, _numCores) != WEBRTC_VIDEO_CODEC_OK) - { - delete copyTo; - return NULL; - } - - // Inject last key frame into new decoder. - if (vpx_codec_decode(copyTo->_decoder, _lastKeyFrame._buffer, - _lastKeyFrame._length, NULL, VPX_DL_REALTIME)) - { - delete copyTo; - return NULL; - } - - // Allocate memory for reference image copy - assert(_decodedImage._width > 0); - assert(_decodedImage._height > 0); - assert(_imageFormat > VPX_IMG_FMT_NONE); - // Check if frame format has changed. - if (_refFrame && - (_decodedImage._width != _refFrame->img.d_w || - _decodedImage._height != _refFrame->img.d_h || - _imageFormat != _refFrame->img.fmt)) - { - vpx_img_free(&_refFrame->img); - delete _refFrame; - _refFrame = NULL; - } - - - if (!_refFrame) - { - _refFrame = new vpx_ref_frame_t; - - if(!vpx_img_alloc(&_refFrame->img, - static_cast(_imageFormat), - _decodedImage._width, _decodedImage._height, 1)) - { - assert(false); - delete copyTo; - return NULL; - } - } - - const vpx_ref_frame_type_t typeVec[] = { VP8_LAST_FRAME, VP8_GOLD_FRAME, - VP8_ALTR_FRAME }; - for (int ix = 0; ix < sizeof(typeVec) / sizeof(vpx_ref_frame_type_t); ++ix) - { - _refFrame->frame_type = typeVec[ix]; - if (CopyReference(copyTo) < 0) - { - delete copyTo; - return NULL; - } - } - - // Copy all member variables (that are not set in initialization). - copyTo->_feedbackModeOn = _feedbackModeOn; - copyTo->_imageFormat = _imageFormat; - copyTo->_lastKeyFrame = _lastKeyFrame; // Shallow copy. - // Allocate memory. (Discard copied _buffer pointer.) - copyTo->_lastKeyFrame._buffer = new WebRtc_UWord8[_lastKeyFrame._size]; - memcpy(copyTo->_lastKeyFrame._buffer, _lastKeyFrame._buffer, - _lastKeyFrame._length); - - return static_cast(copyTo); -} - -int VP8Decoder::CopyReference(VP8Decoder* copyTo) -{ - // The type of frame to copy should be set in _refFrame->frame_type - // before the call to this function. - if (vpx_codec_control(_decoder, VP8_COPY_REFERENCE, _refFrame) - != VPX_CODEC_OK) - { - return -1; - } - if (vpx_codec_control(copyTo->_decoder, VP8_SET_REFERENCE, _refFrame) - != VPX_CODEC_OK) - { - return -1; - } - return 0; -} - - -} // namespace webrtc diff --git a/modules/video_coding/codecs/vp8/main/source/vp8.gyp b/modules/video_coding/codecs/vp8/main/source/vp8.gyp deleted file mode 100644 index ba351fd81..000000000 --- a/modules/video_coding/codecs/vp8/main/source/vp8.gyp +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'webrtc_vp8', - 'type': '<(library)', - 'dependencies': [ - '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../../../../../common_video/interface', - '../../../interface', - '../../../../../interface', - ], - 'conditions': [ - ['build_with_chromium==1', { - 'conditions': [ - ['OS=="win"', { - 'dependencies': [ - # We don't want to link with the static library inside Chromium - # on Windows. Chromium uses the ffmpeg DLL and exports the - # necessary libvpx symbols for us. - '../../../../../../../libvpx/libvpx.gyp:libvpx_include', - ], - },{ - 'dependencies': [ - '../../../../../../../libvpx/libvpx.gyp:libvpx', - ], - 'include_dirs': [ - '../../../../../../../libvpx/source/libvpx', - ], - }], - ], - },{ - 'dependencies': [ - '../../../../../../../third_party/libvpx/libvpx.gyp:libvpx', - ], - 'include_dirs': [ - '../../../../../../../third_party/libvpx', - ], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../../../../common_video/interface', - '../../../interface', - ], - }, - 'sources': [ - '../interface/vp8.h', - 'vp8.cc', - ], - }, - - { - 'target_name': 'vp8_test', - 'type': 'executable', - 'dependencies': [ - 'webrtc_vp8', - '../../../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../test_framework/test_framework.gyp:test_framework', - '../../../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib' - ], - 'sources': [ - # header files - '../test/benchmark.h', - '../test/normal_async_test.h', - '../test/packet_loss_test.h', - '../test/unit_test.h', - '../test/dual_decoder_test.h', - - # source files - '../test/benchmark.cc', - '../test/normal_async_test.cc', - '../test/packet_loss_test.cc', - '../test/tester.cc', - '../test/unit_test.cc', - '../test/dual_decoder_test.cc', - ], - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_coding/codecs/vp8/main/test/benchmark.cc b/modules/video_coding/codecs/vp8/main/test/benchmark.cc deleted file mode 100644 index 960ba025e..000000000 --- a/modules/video_coding/codecs/vp8/main/test/benchmark.cc +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "benchmark.h" -#include "vp8.h" - -using namespace webrtc; - -VP8Benchmark::VP8Benchmark() -: -Benchmark("VP8Benchmark", "VP8 benchmark over a range of test cases", "../../VP8Benchmark.txt", "VP8") -{ -} - -VP8Benchmark::VP8Benchmark(std::string name, std::string description) -: -Benchmark(name, description, "../../VP8Benchmark.txt", "VP8") -{ -} - -VP8Benchmark::VP8Benchmark(std::string name, std::string description, std::string resultsFileName) -: -Benchmark(name, description, resultsFileName, "VP8") -{ -} - -VideoEncoder* -VP8Benchmark::GetNewEncoder() -{ - return new VP8Encoder(); -} - -VideoDecoder* -VP8Benchmark::GetNewDecoder() -{ - return new VP8Decoder(); -} diff --git a/modules/video_coding/codecs/vp8/main/test/benchmark.h b/modules/video_coding/codecs/vp8/main/test/benchmark.h deleted file mode 100644 index 5143de321..000000000 --- a/modules/video_coding/codecs/vp8/main/test/benchmark.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_ - -#include "../../../test_framework/benchmark.h" - -class VP8Benchmark : public Benchmark -{ -public: - VP8Benchmark(); - VP8Benchmark(std::string name, std::string description); - VP8Benchmark(std::string name, std::string description, std::string resultsFileName); - -protected: - virtual webrtc::VideoEncoder* GetNewEncoder(); - virtual webrtc::VideoDecoder* GetNewDecoder(); -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_BENCHMARK_H_ diff --git a/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc b/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc deleted file mode 100644 index a8cbe3081..000000000 --- a/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.cc +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "dual_decoder_test.h" - -#include -#include // memcmp -#include - -VP8DualDecoderTest::VP8DualDecoderTest(float bitRate) -: -VP8NormalAsyncTest(bitRate) -{ - _decoder2 = NULL; -} - -VP8DualDecoderTest::VP8DualDecoderTest() -: -_decoder2(NULL), -VP8NormalAsyncTest("VP8 Dual Decoder Test", "Tests VP8 dual decoder", 1) -{} - -VP8DualDecoderTest::~VP8DualDecoderTest() -{ - if(_decoder2) - { - _decoder2->Release(); - delete _decoder2; - } - - _decodedVideoBuffer2.Free(); -} - -void -VP8DualDecoderTest::Perform() -{ - _inname = "test/testFiles/foreman_cif.yuv"; - CodecSettings(352, 288, 30, _bitRate); - Setup(); - _inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _decodedVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); - _decodedVideoBuffer2.VerifyAndAllocate(_lengthSourceFrame); - if(_encoder->InitEncode(&_inst, 4, 1460) < 0) - { - exit(EXIT_FAILURE); - } - _decoder->InitDecode(&_inst,1); - - FrameQueue frameQueue; - VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this); - DualDecoderCompleteCallback decCallback(&_decodedVideoBuffer); - DualDecoderCompleteCallback decCallback2(&_decodedVideoBuffer2); - _encoder->RegisterEncodeCompleteCallback(&encCallback); - _decoder->RegisterDecodeCompleteCallback(&decCallback); - if (SetCodecSpecificParameters() != WEBRTC_VIDEO_CODEC_OK) - { - exit(EXIT_FAILURE); - } - _totalEncodeTime = _totalDecodeTime = 0; - _totalEncodePipeTime = _totalDecodePipeTime = 0; - bool complete = false; - _framecnt = 0; - _encFrameCnt = 0; - _decFrameCnt = 0; - _sumEncBytes = 0; - _lengthEncFrame = 0; - double starttime = clock()/(double)CLOCKS_PER_SEC; - while (!complete) - { - if (_encFrameCnt == 10) - { - // initialize second decoder and copy state - _decoder2 = static_cast(_decoder->Copy()); - assert(_decoder2 != NULL); - _decoder2->RegisterDecodeCompleteCallback(&decCallback2); - } - CodecSpecific_InitBitrate(); - complete = Encode(); - if (!frameQueue.Empty() || complete) - { - while (!frameQueue.Empty()) - { - _frameToDecode = - static_cast(frameQueue.PopFrame()); - int lost = DoPacketLoss(); - if (lost == 2) - { - // Lost the whole frame, continue - _missingFrames = true; - delete _frameToDecode; - _frameToDecode = NULL; - continue; - } - int ret = Decode(lost); - delete _frameToDecode; - _frameToDecode = NULL; - if (ret < 0) - { - fprintf(stderr,"\n\nError in decoder: %d\n\n", ret); - exit(EXIT_FAILURE); - } - else if (ret == 0) - { - _framecnt++; - } - else - { - fprintf(stderr, - "\n\nPositive return value from decode!\n\n"); - } - } - } - } - double endtime = clock()/(double)CLOCKS_PER_SEC; - double totalExecutionTime = endtime - starttime; - printf("Total execution time: %.1f s\n", totalExecutionTime); - _sumEncBytes = encCallback.EncodedBytes(); - double actualBitRate = ActualBitRate(_encFrameCnt) / 1000.0; - double avgEncTime = _totalEncodeTime / _encFrameCnt; - double avgDecTime = _totalDecodeTime / _decFrameCnt; - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Average encode time: %.1f ms\n", 1000 * avgEncTime); - printf("Average decode time: %.1f ms\n", 1000 * avgDecTime); - printf("Average encode pipeline time: %.1f ms\n", - 1000 * _totalEncodePipeTime / _encFrameCnt); - printf("Average decode pipeline time: %.1f ms\n", - 1000 * _totalDecodePipeTime / _decFrameCnt); - printf("Number of encoded frames: %u\n", _encFrameCnt); - printf("Number of decoded frames: %u\n", _decFrameCnt); - (*_log) << "Actual bitrate: " << actualBitRate << " kbps\tTarget: " << - _bitRate << " kbps" << std::endl; - (*_log) << "Average encode time: " << avgEncTime << " s" << std::endl; - (*_log) << "Average decode time: " << avgDecTime << " s" << std::endl; - _encoder->Release(); - _decoder->Release(); - Teardown(); -} - - -int -VP8DualDecoderTest::Decode(int lossValue) -{ - _sumEncBytes += _frameToDecode->_frame->GetLength(); - double starttime = 0; - webrtc::EncodedImage encodedImage; - VideoEncodedBufferToEncodedImage(*(_frameToDecode->_frame), encodedImage); - encodedImage._completeFrame = !lossValue; - _decodeCompleteTime = 0; - _decodeTimes[encodedImage._timeStamp] = clock()/(double)CLOCKS_PER_SEC; - int ret = _decoder->Decode(encodedImage, _missingFrames, - _frameToDecode->_codecSpecificInfo); - // second decoder - if (_decoder2) - { - int ret2 = _decoder2->Decode(encodedImage, _missingFrames, - _frameToDecode->_codecSpecificInfo, 0 /* dummy */); - - // check return values - if (ret < 0 || ret2 < 0 || ret2 != ret) - { - exit(EXIT_FAILURE); - } - - // compare decoded images - if (!CheckIfBitExact(_decodedVideoBuffer.GetBuffer(), - _decodedVideoBuffer.GetLength(), - _decodedVideoBuffer2.GetBuffer(), _decodedVideoBuffer.GetLength())) - { - fprintf(stderr,"\n\nClone output different from master.\n\n"); - exit(EXIT_FAILURE); - } - - } - - _missingFrames = false; - return ret; -} - - -bool -VP8DualDecoderTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes, - const void* ptrB, unsigned int bLengthBytes) -{ - if (aLengthBytes != bLengthBytes) - { - return false; - } - - return memcmp(ptrA, ptrB, aLengthBytes) == 0; -} - -WebRtc_Word32 DualDecoderCompleteCallback::Decoded(webrtc::RawImage& image) -{ - _decodedVideoBuffer->VerifyAndAllocate(image._length); - _decodedVideoBuffer->CopyBuffer(image._length, image._buffer); - _decodedVideoBuffer->SetWidth(image._width); - _decodedVideoBuffer->SetHeight(image._height); - _decodedVideoBuffer->SetTimeStamp(image._timeStamp); - _decodeComplete = true; - return 0; -} - -bool DualDecoderCompleteCallback::DecodeComplete() -{ - if (_decodeComplete) - { - _decodeComplete = false; - return true; - } - return false; -} - diff --git a/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h b/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h deleted file mode 100644 index 4af4e3ee7..000000000 --- a/modules/video_coding/codecs/vp8/main/test/dual_decoder_test.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DUAL_DECODER_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DUAL_DECODER_TEST_H_ - -#include "vp8.h" -#include "normal_async_test.h" - -class DualDecoderCompleteCallback; - -class VP8DualDecoderTest : public VP8NormalAsyncTest -{ -public: - VP8DualDecoderTest(float bitRate); - VP8DualDecoderTest(); - virtual ~VP8DualDecoderTest(); - virtual void Perform(); -protected: - VP8DualDecoderTest(std::string name, std::string description, - unsigned int testNo) - : VP8NormalAsyncTest(name, description, testNo) {} - virtual int Decode(int lossValue = 0); - - webrtc::VP8Decoder* _decoder2; - TestVideoBuffer _decodedVideoBuffer2; - static bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes, - const void *ptrB, unsigned int bLengthBytes); -private: -}; - -class DualDecoderCompleteCallback : public webrtc::DecodedImageCallback -{ -public: - DualDecoderCompleteCallback(TestVideoBuffer* buffer) - : _decodedVideoBuffer(buffer), _decodeComplete(false) {} - WebRtc_Word32 Decoded(webrtc::RawImage& decodedImage); - bool DecodeComplete(); -private: - TestVideoBuffer* _decodedVideoBuffer; - bool _decodeComplete; -}; - - -#endif diff --git a/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc b/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc deleted file mode 100644 index 9ec223308..000000000 --- a/modules/video_coding/codecs/vp8/main/test/normal_async_test.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "normal_async_test.h" - -using namespace webrtc; - -VP8NormalAsyncTest::VP8NormalAsyncTest(WebRtc_UWord32 bitRate) : - NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", bitRate, 1), - _hasReceivedRPSI(false) -{ -} - -VP8NormalAsyncTest::VP8NormalAsyncTest(WebRtc_UWord32 bitRate, unsigned int testNo): - NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", bitRate, testNo), - _hasReceivedRPSI(false) -{ -} - -void -VP8NormalAsyncTest::CodecSettings(int width, int height, WebRtc_UWord32 frameRate /*=30*/, WebRtc_UWord32 bitRate /*=0*/) -{ - if (bitRate > 0) - { - _bitRate = bitRate; - - }else if (_bitRate == 0) - { - _bitRate = 600; - } - _inst.codecType = kVideoCodecVP8; - _inst.codecSpecific.VP8.feedbackModeOn = true; - _inst.codecSpecific.VP8.pictureLossIndicationOn = true; - _inst.codecSpecific.VP8.complexity; - _inst.maxFramerate = (unsigned char)frameRate; - _inst.startBitrate = _bitRate; - _inst.maxBitrate = 8000; - _inst.width = width; - _inst.height = height; -} - -void -VP8NormalAsyncTest::CodecSpecific_InitBitrate() -{ - if (_bitRate == 0) - { - _encoder->SetRates(600, _inst.maxFramerate); - }else - { - _encoder->SetRates(_bitRate, _inst.maxFramerate); - } -} - -WebRtc_Word32 -VP8NormalAsyncTest::ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) -{ - _pictureIdRPSI = pictureId; - _hasReceivedRPSI = true; - return 0; -} - -void* -VP8NormalAsyncTest::CreateEncoderSpecificInfo() const -{ - CodecSpecificInfo* vp8CodecSpecificInfo = new CodecSpecificInfo(); - vp8CodecSpecificInfo->codecType = kVideoCodecVP8; - vp8CodecSpecificInfo->codecSpecific.VP8.hasReceivedRPSI = _hasReceivedRPSI; - vp8CodecSpecificInfo->codecSpecific.VP8.pictureIdRPSI = _pictureIdRPSI; - vp8CodecSpecificInfo->codecSpecific.VP8.hasReceivedSLI = false; - - _hasReceivedRPSI = false; - - return vp8CodecSpecificInfo; -} diff --git a/modules/video_coding/codecs/vp8/main/test/normal_async_test.h b/modules/video_coding/codecs/vp8/main/test/normal_async_test.h deleted file mode 100644 index ecfaa8ae6..000000000 --- a/modules/video_coding/codecs/vp8/main/test/normal_async_test.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_NORMAL_ASYNC_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_NORMAL_ASYNC_TEST_H_ - -#include "../../../test_framework/normal_async_test.h" - -class VP8NormalAsyncTest : public NormalAsyncTest -{ -public: - VP8NormalAsyncTest(WebRtc_UWord32 bitRate); - VP8NormalAsyncTest(WebRtc_UWord32 bitRate, unsigned int testNo); - VP8NormalAsyncTest() : NormalAsyncTest("VP8 Normal Test 1", "Tests VP8 normal execution", 1) {} -protected: - VP8NormalAsyncTest(std::string name, std::string description, unsigned int testNo) : NormalAsyncTest(name, description, testNo) {} - virtual void CodecSpecific_InitBitrate(); - virtual void CodecSettings(int width, int height, WebRtc_UWord32 frameRate=30, WebRtc_UWord32 bitRate=0); - virtual void* CreateEncoderSpecificInfo() const; - virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId); -private: - mutable bool _hasReceivedRPSI; - WebRtc_UWord64 _pictureIdRPSI; -}; - -#endif diff --git a/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc b/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc deleted file mode 100644 index 3a7324e26..000000000 --- a/modules/video_coding/codecs/vp8/main/test/packet_loss_test.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "packet_loss_test.h" -#include - -VP8PacketLossTest::VP8PacketLossTest() -: -PacketLossTest("VP8PacketLossTest", "Encode, remove lost packets, decode") -{ -} - -VP8PacketLossTest::VP8PacketLossTest(std::string name, std::string description) -: -PacketLossTest(name, description) -{ -} - -VP8PacketLossTest::VP8PacketLossTest(double lossRate, bool useNack) -: -PacketLossTest("VP8PacketLossTest", "Encode, remove lost packets, decode", lossRate, useNack) -{ -} - -void -VP8PacketLossTest::CodecSpecific_InitBitrate() -{ - assert(_bitRate > 0); - WebRtc_UWord32 simulatedBitRate; - if (_lossProbability != _lossRate) - { - // Simulating NACK - simulatedBitRate = (WebRtc_UWord32)(_bitRate / (1 + _lossRate)); - } - else - { - simulatedBitRate = _bitRate; - } - _encoder->SetRates(simulatedBitRate, _inst.maxFramerate); -} - -int VP8PacketLossTest::ByteLoss(int size, unsigned char* /* pkg */, int bytesToLose) -{ - int retLength = size - bytesToLose; - if (retLength < 4) - { - retLength = 4; - } - return retLength; -} diff --git a/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h b/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h deleted file mode 100644 index 96c6e885d..000000000 --- a/modules/video_coding/codecs/vp8/main/test/packet_loss_test.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_ - -#include "../../../test_framework/packet_loss_test.h" - -class VP8PacketLossTest : public PacketLossTest -{ -public: - VP8PacketLossTest(); - VP8PacketLossTest(double lossRate, bool useNack); - -protected: - VP8PacketLossTest(std::string name, std::string description); - virtual void CodecSpecific_InitBitrate(); - virtual int ByteLoss(int size, unsigned char *pkg, int bytesToLose); - -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_PACKET_LOSS_TEST_H_ diff --git a/modules/video_coding/codecs/vp8/main/test/tester.cc b/modules/video_coding/codecs/vp8/main/test/tester.cc deleted file mode 100644 index cd5bb0296..000000000 --- a/modules/video_coding/codecs/vp8/main/test/tester.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "packet_loss_test.h" -#include "benchmark.h" -#include "unit_test.h" -#include "normal_async_test.h" -#include "dual_decoder_test.h" -#include "vp8.h" -#include -#include -#include - -using namespace webrtc; - -void PopulateTests(std::vector* tests) -{ - tests->push_back(new VP8UnitTest()); -// tests->push_back(new VP8DualDecoderTest()); -// tests->push_back(new VP8Benchmark()); -// tests->push_back(new VP8PacketLossTest()); -// tests->push_back(new VP8NormalAsyncTest()); -} - -int main() -{ - VP8Encoder* enc; - VP8Decoder* dec; - std::vector tests; - PopulateTests(&tests); - std::fstream log; - log.open("../../TestLog.txt", std::fstream::out | std::fstream::app); - std::vector::iterator it; - for (it = tests.begin() ; it < tests.end(); it++) - { - enc = new VP8Encoder(); - dec = new VP8Decoder(); - (*it)->SetEncoder(enc); - (*it)->SetDecoder(dec); - (*it)->SetLog(&log); - (*it)->Perform(); - (*it)->Print(); - delete enc; - delete dec; - delete *it; - } - log.close(); - tests.pop_back(); - return 0; -} diff --git a/modules/video_coding/codecs/vp8/main/test/unit_test.cc b/modules/video_coding/codecs/vp8/main/test/unit_test.cc deleted file mode 100644 index 80171b154..000000000 --- a/modules/video_coding/codecs/vp8/main/test/unit_test.cc +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "../../../test_framework/video_source.h" -#include "vp8.h" - -#include - -using namespace webrtc; - -VP8UnitTest::VP8UnitTest() -: -UnitTest("VP8UnitTest", "Unit test") -{ -} - -VP8UnitTest::VP8UnitTest(std::string name, std::string description) -: -UnitTest(name, description) -{ -} - -void -VP8UnitTest::Print() -{ - WebRtc_Word8 versionStr[64]; - - // GetVersion tests. - - VIDEO_TEST(_encoder->Version(versionStr, sizeof(versionStr)) > 0); -// printf("\n%s", versionStr); -// UnitTest::Print(); -} - -WebRtc_UWord32 -VP8UnitTest::CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate, WebRtc_UWord32 /*frameRate*/) -{ - bitRate = _encoder->SetRates(bitRate, _inst.maxFramerate); - VIDEO_TEST_EXIT_ON_ERR(bitRate >= 0); - return bitRate; -} - -bool -VP8UnitTest::CheckIfBitExact(const void* ptrA, unsigned int aLengthBytes, - const void* ptrB, unsigned int bLengthBytes) -{ - const unsigned char* cPtrA = (const unsigned char*)ptrA; - const unsigned char* cPtrB = (const unsigned char*)ptrB; - // Skip picture ID before comparing - int aSkip = PicIdLength(cPtrA); - int bSkip = PicIdLength(cPtrB); - return UnitTest::CheckIfBitExact(cPtrA + aSkip, aLengthBytes, - cPtrB + bSkip, bLengthBytes); -} - -int -VP8UnitTest::PicIdLength(const unsigned char* ptr) -{ - WebRtc_UWord8 numberOfBytes; - WebRtc_UWord64 pictureID = 0; - for (numberOfBytes = 0; (ptr[numberOfBytes] & 0x80) && numberOfBytes < 8; numberOfBytes++) - { - pictureID += ptr[numberOfBytes] & 0x7f; - pictureID <<= 7; - } - pictureID += ptr[numberOfBytes] & 0x7f; - numberOfBytes++; - return numberOfBytes; -} - -void -VP8UnitTest::Perform() -{ - Setup(); - FILE *outFile = NULL; - std::string outFileName; - VP8Encoder* enc = (VP8Encoder*)_encoder; - VP8Decoder* dec = (VP8Decoder*)_decoder; - int frameLength = 0; - - //----- Encoder parameter tests ----- - //-- Calls before InitEncode() -- - VIDEO_TEST(enc->Release() == WEBRTC_VIDEO_CODEC_OK); - VIDEO_TEST(enc->SetRates(_bitRate, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_UNINITIALIZED); - - VIDEO_TEST(enc->SetRates(_bitRate, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_UNINITIALIZED); - // VIDEO_TEST(enc->GetCodecConfigParameters(configParameters, sizeof(configParameters)) == - // WEBRTC_VIDEO_CODEC_UNINITIALIZED); - - - VideoCodec codecInst; - strncpy(codecInst.plName, "VP8", 31); - codecInst.plType = 126; - codecInst.maxBitrate = 0; - codecInst.minBitrate = 0; - codecInst.width = 1440; - codecInst.height = 1080; - codecInst.maxFramerate = 30; - codecInst.startBitrate = 300; - codecInst.codecSpecific.VP8.complexity = kComplexityNormal; - VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - - - //-- Test two problematic level settings -- - strncpy(codecInst.plName, "VP8", 31); - codecInst.plType = 126; - codecInst.maxBitrate = 0; - codecInst.minBitrate = 0; - codecInst.width = 352; - codecInst.height = 288; - codecInst.maxFramerate = 30; - codecInst.codecSpecific.VP8.complexity = kComplexityNormal; - codecInst.startBitrate = 300; - VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - - // Settings not correct for this profile - strncpy(codecInst.plName, "VP8", 31); - codecInst.plType = 126; - codecInst.maxBitrate = 0; - codecInst.minBitrate = 0; - codecInst.width = 176; - codecInst.height = 144; - codecInst.maxFramerate = 15; - codecInst.codecSpecific.VP8.complexity = kComplexityNormal; - codecInst.startBitrate = 300; - //VIDEO_TEST(enc->InitEncode(&codecInst, 1, 1440) == WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED); - - VIDEO_TEST_EXIT_ON_ERR(enc->InitEncode(&_inst, 1, 1440) == WEBRTC_VIDEO_CODEC_OK); - - - //-- ProcessNewBitrate() errors -- - // Bad bitrate. - VIDEO_TEST(enc->SetRates(_inst.maxBitrate + 1, _inst.maxFramerate) == WEBRTC_VIDEO_CODEC_OK); - - // Signaling not used. - - // Bad packetloss. -// VIDEO_TEST(enc->SetPacketLoss(300) < 0); - - //----- Decoder parameter tests ----- - //-- Calls before InitDecode() -- - VIDEO_TEST(dec->Release() == 0); - VIDEO_TEST_EXIT_ON_ERR(dec->InitDecode(&_inst, 1) == WEBRTC_VIDEO_CODEC_OK); - - //-- SetCodecConfigParameters() errors -- - unsigned char tmpBuf[128]; - VIDEO_TEST(dec->SetCodecConfigParameters(NULL, sizeof(tmpBuf)) == -1); - VIDEO_TEST(dec->SetCodecConfigParameters(tmpBuf, 1) == -1); - // Garbage data. - VIDEO_TEST(dec->SetCodecConfigParameters(tmpBuf, sizeof(tmpBuf)) == -1); - - //----- Function tests ----- - outFileName = "../../" + _source->GetName() + "-errResTest.yuv"; - outFile = fopen(outFileName.c_str(), "wb"); - VIDEO_TEST_EXIT_ON_ERR(outFile != NULL); - - UnitTest::Perform(); - Teardown(); - -} diff --git a/modules/video_coding/codecs/vp8/main/test/unit_test.h b/modules/video_coding/codecs/vp8/main/test/unit_test.h deleted file mode 100644 index 5e6652d48..000000000 --- a/modules/video_coding/codecs/vp8/main/test/unit_test.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_ - -#include "../../../test_framework/unit_test.h" - -class VP8UnitTest : public UnitTest -{ -public: - VP8UnitTest(); - VP8UnitTest(std::string name, std::string description); - virtual void Perform(); - virtual void Print(); - -protected: - virtual WebRtc_UWord32 CodecSpecific_SetBitrate(WebRtc_UWord32 bitRate, - WebRtc_UWord32 /*frameRate*/); - virtual bool CheckIfBitExact(const void *ptrA, unsigned int aLengthBytes, - const void *ptrB, unsigned int bLengthBytes); - static int PicIdLength(const unsigned char* ptr); -}; - -//////////////////////////////////////////////////////////////// -// RESERVATIONS TO PASSING UNIT TEST ON VP8 CODEC // -// Test that will not pass: // -// 1. Check bit exact for decoded images. // -// 2. Target bitrate - Allow a margin of 10% instead of 5% // -// 3. Detecting errors in bit stream - NA. // -//////////////////////////////////////////////////////////////// - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_UNIT_TEST_H_ diff --git a/modules/video_coding/main/OWNERS b/modules/video_coding/main/OWNERS deleted file mode 100644 index 30eee35f1..000000000 --- a/modules/video_coding/main/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -holmer@google.com -mikhal@google.com -marpan@google.com -hlundin@google.com diff --git a/modules/video_coding/main/interface/video_coding.h b/modules/video_coding/main/interface/video_coding.h deleted file mode 100644 index 5b0cc1812..000000000 --- a/modules/video_coding/main/interface/video_coding.h +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_ -#define WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_ - -#include "module.h" -#include "module_common_types.h" -#include "video_coding_defines.h" - -namespace webrtc -{ - -class VideoEncoder; -class VideoDecoder; -struct CodecSpecificInfo; - -class VideoCodingModule : public Module -{ -public: - static VideoCodingModule* Create(const WebRtc_Word32 id); - - static void Destroy(VideoCodingModule* module); - - // Get number of supported codecs - // - // Return value : Number of supported codecs - static WebRtc_UWord8 NumberOfCodecs(); - - // Get supported codec settings with using id - // - // Input: - // - listId : Id or index of the codec to look up - // - codec : Memory where the codec settings will be stored - // - // Return value : VCM_OK, on success - // VCM_PARAMETER_ERROR if codec not supported or id too high - static WebRtc_Word32 Codec(const WebRtc_UWord8 listId, VideoCodec* codec); - - // Get supported codec settings using codec type - // - // Input: - // - codecType : The codec type to get settings for - // - codec : Memory where the codec settings will be stored - // - // Return value : VCM_OK, on success - // VCM_PARAMETER_ERROR if codec not supported - static WebRtc_Word32 Codec(VideoCodecType codecType, VideoCodec* codec); - - /* - * Sender - */ - - // Any encoder-related state of VCM will be initialized to the - // same state as when the VCM was created. This will not interrupt - // or effect decoding functionality of VCM. VCM will lose all the - // encoding-related settings by calling this function. - // For instance, a send codec has to be registered again. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 InitializeSender() = 0; - - // Resets the encoder state to the same state as when the encoder - // was created. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 ResetEncoder() = 0; - - // Registers a codec to be used for encoding. Calling this - // API multiple times overwrites any previously registered codecs. - // - // Input: - // - sendCodec : Settings for the codec to be registered. - // - numberOfCores : The number of cores the codec is allowed - // to use. - // - maxPayloadSize : The maximum size each payload is allowed - // to have. Usually MTU - overhead. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize) = 0; - - // API to get the current send codec in use. - // - // Input: - // - currentSendCodec : Address where the sendCodec will be written. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const = 0; - - // API to get the current send codec type - // - // Return value : Codec type, on success. - // kVideoCodecUnknown, on error or if no send codec is set - virtual VideoCodecType SendCodec() const = 0; - - // Register an external encoder object. This can not be used together with - // external decoder callbacks. - // - // Input: - // - externalEncoder : Encoder object to be used for encoding frames inserted - // with the AddVideoFrame API. - // - payloadType : The payload type bound which this encoder is bound to. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder, - WebRtc_UWord8 payloadType, - bool internalSource = false) = 0; - - // API to get codec config parameters to be sent out-of-band to a receiver. - // - // Input: - // - buffer : Memory where the codec config parameters should be written. - // - size : Size of the memory available. - // - // Return value : Number of bytes written, on success. - // < 0, on error. - virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size) = 0; - - // API to get currently configured encoder target bit rate. - // - // Return value : The encoder target bit rate, on success. - // < 0, on error. - virtual WebRtc_UWord32 Bitrate() const = 0; - - // API to get currently configured encoder target frame rate. - // - // Return value : The encoder target frame rate, on success. - // < 0, on error. - virtual WebRtc_UWord32 FrameRate() const = 0; - - // Sets the parameters describing the send channel. These parameters are inputs to the - // Media Optimization inside the VCM and also specifies the target bit rate for the - // encoder. Bit rate used by NACK should already be compensated for by the user. - // - // Input: - // - availableBandWidth : Band width available for the VCM in kbit/s. - // - lossRate : Fractions of lost packets the past second. - // (loss rate in percent = 100 * packetLoss / 255) - // - RTT : Current round-trip time in ms. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetChannelParameters(WebRtc_UWord32 availableBandWidth, - WebRtc_UWord8 lossRate, - WebRtc_UWord32 RTT) = 0; - - // Sets the parameters describing the receive channel. These parameters are inputs to the - // Media Optimization inside the VCM. - // - // Input: - // - RTT : Current round-trip time in ms. - // with the most amount available bandwidth in a conference - // scenario - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetReceiveChannelParameters(WebRtc_UWord32 RTT) = 0; - - // Register a transport callback which will be called to deliver the encoded data and - // side information. - // - // Input: - // - transport : The callback object to register. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterTransportCallback(VCMPacketizationCallback* transport) = 0; - - // Register video output information callback which will be called to deliver information - // about the video stream produced by the encoder, for instance the average frame rate and - // bit rate. - // - // Input: - // - outputInformation : The callback object to register. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterSendStatisticsCallback( - VCMSendStatisticsCallback* sendStats) = 0; - - // Register a video quality settings callback which will be called when - // frame rate/dimensions need to be updated for video quality optimization - // - // Input: - // - videoQMSettings : The callback object to register. - // - // Return value : VCM_OK, on success. - // < 0, on error - virtual WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings) = 0; - - // Register a video protection callback which will be called to deliver - // the requested FEC rate and NACK status (on/off). - // - // Input: - // - protection : The callback object to register. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protection) = 0; - - // Enable or disable a video protection method. - // - // Input: - // - videoProtection : The method to enable or disable. - // - enable : True if the method should be enabled, false if - // it should be disabled. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetVideoProtection(VCMVideoProtection videoProtection, - bool enable) = 0; - - // Add one raw video frame to the encoder. This function does all the necessary - // processing, then decides what frame type to encode, or if the frame should be - // dropped. If the frame should be encoded it passes the frame to the encoder - // before it returns. - // - // Input: - // - videoFrame : Video frame to encode. - // - codecSpecificInfo : Extra codec information, e.g., pre-parsed in-band signaling. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 AddVideoFrame( - const VideoFrame& videoFrame, - const VideoContentMetrics* _contentMetrics = NULL, - const CodecSpecificInfo* codecSpecificInfo = NULL) = 0; - - // Next frame encoded should be of the type frameType. - // - // Input: - // - frameType : The frame type to encode next time a VideoFrame - // is added to the module. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 FrameTypeRequest(FrameType frameType) = 0; - - // Frame Dropper enable. Can be used to disable the frame dropping when the encoder - // over-uses its bit rate. This API is designed to be used when the encoded frames - // are supposed to be stored to an AVI file, or when the I420 codec is used and the - // target bit rate shouldn't affect the frame rate. - // - // Input: - // - enable : True to enable the setting, false to disable it. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 EnableFrameDropper(bool enable) = 0; - - // Sent frame counters - virtual WebRtc_Word32 SentFrameCount(VCMFrameCount& frameCount) const = 0; - - /* - * Receiver - */ - - // The receiver state of the VCM will be initialized to the - // same state as when the VCM was created. This will not interrupt - // or effect the send side functionality of VCM. VCM will lose all the - // decoding-related settings by calling this function. All frames - // inside the jitter buffer are flushed and the delay is reset. - // For instance, a receive codec has to be registered again. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 InitializeReceiver() = 0; - - // Register possible receive codecs, can be called multiple times for different codecs. - // The module will automatically switch between registered codecs depending on the - // payload type of incoming frames. The actual decoder will be created when needed. - // - // Input: - // - receiveCodec : Settings for the codec to be registered. - // - numberOfCores : Number of CPU cores that the decoder is allowed to use. - // - requireKeyFrame : Set this to true if you don't want any delta frames - // to be decoded until the first key frame has been decoded. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec, - WebRtc_Word32 numberOfCores, - bool requireKeyFrame = false) = 0; - - // Register an externally defined decoder/renderer object. Can be a decoder only or a - // decoder coupled with a renderer. Note that RegisterReceiveCodec must be called to - // be used for decoding incoming streams. - // - // Input: - // - externalDecoder : The external decoder/renderer object. - // - payloadType : The payload type which this decoder should be - // registered to. - // - internalRenderTiming : True if the internal renderer (if any) of the decoder - // object can make sure to render at a given time in ms. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder, - WebRtc_UWord8 payloadType, - bool internalRenderTiming) = 0; - - // Register a receive callback. Will be called whenever there is a new frame ready - // for rendering. - // - // Input: - // - receiveCallback : The callback object to be used by the module when a - // frame is ready for rendering. - // De-register with a NULL pointer. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterReceiveCallback(VCMReceiveCallback* receiveCallback) = 0; - - // Register a receive statistics callback which will be called to deliver information - // about the video stream received by the receiving side of the VCM, for instance the - // average frame rate and bit rate. - // - // Input: - // - receiveStats : The callback object to register. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterReceiveStatisticsCallback( - VCMReceiveStatisticsCallback* receiveStats) = 0; - - // Register a frame type request callback. This callback will be called when the - // module needs to request specific frame types from the send side. - // - // Input: - // - frameTypeCallback : The callback object to be used by the module when - // requesting a specific type of frame from the send side. - // De-register with a NULL pointer. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterFrameTypeCallback( - VCMFrameTypeCallback* frameTypeCallback) = 0; - - // Register a frame storage callback. This callback will be called right before an - // encoded frame is given to the decoder. Useful for recording the incoming video sequence. - // - // Input: - // - frameStorageCallback : The callback object used by the module - // to store a received encoded frame. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 RegisterFrameStorageCallback( - VCMFrameStorageCallback* frameStorageCallback) = 0; - - // Registers a callback which is called whenever the receive side of the VCM - // encounters holes in the packet sequence and needs packets to be retransmitted. - // - // Input: - // - callback : The callback to be registered in the VCM. - // - // Return value : VCM_OK, on success. - // <0, on error. - virtual WebRtc_Word32 RegisterPacketRequestCallback( - VCMPacketRequestCallback* callback) = 0; - - // Waits for the next frame in the jitter buffer to become complete - // (waits no longer than maxWaitTimeMs), then passes it to the decoder for decoding. - // Should be called as often as possible to get the most out of the decoder. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 Decode(WebRtc_UWord16 maxWaitTimeMs = 200) = 0; - - // Waits for the next frame in the dual jitter buffer to become complete - // (waits no longer than maxWaitTimeMs), then passes it to the dual decoder - // for decoding. This will never trigger a render callback. Should be - // called frequently, and as long as it returns 1 it should be called again - // as soon as possible. - // - // Return value : 1, if a frame was decoded - // 0, if no frame was decoded - // < 0, on error. - virtual WebRtc_Word32 DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs = 200) = 0; - - // Decodes a frame and sets an appropriate render time in ms relative to the system time. - // Should be used in conjunction with VCMFrameStorageCallback. - // - // Input: - // - frameFromStorage : Encoded frame read from file or received through - // the VCMFrameStorageCallback callback. - // - // Return value: : VCM_OK, on success - // < 0, on error - virtual WebRtc_Word32 DecodeFromStorage(const EncodedVideoData& frameFromStorage) = 0; - - // Reset the decoder state to the initial state. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 ResetDecoder() = 0; - - // API to get the codec which is currently used for decoding by the module. - // - // Input: - // - currentReceiveCodec : Settings for the codec to be registered. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const = 0; - - // API to get the codec type currently used for decoding by the module. - // - // Return value : codecy type, on success. - // kVideoCodecUnknown, on error or if no receive codec is registered - virtual VideoCodecType ReceiveCodec() const = 0; - - // Insert a parsed packet into the receiver side of the module. Will be placed in the - // jitter buffer waiting for the frame to become complete. Returns as soon as the packet - // has been placed in the jitter buffer. - // - // Input: - // - incomingPayload : Payload of the packet. - // - payloadLength : Length of the payload. - // - rtpInfo : The parsed header. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 IncomingPacket(const WebRtc_UWord8* incomingPayload, - WebRtc_UWord32 payloadLength, - const WebRtcRTPHeader& rtpInfo) = 0; - - // Sets codec config parameters received out-of-band to the currently - // selected receive codec. - // - // Input: - // - payloadType : Payload type which specifies which codec to set these - // parameters to. - // - buffer : Codec config parameters. - // - length : Length of the parameter data. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length) = 0; - - // Minimum playout delay (Used for lip-sync). This is the minimum delay required - // to sync with audio. Not included in VideoCodingModule::Delay() - // Defaults to 0 ms. - // - // Input: - // - minPlayoutDelayMs : Additional delay in ms. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs) = 0; - - // Set the time required by the renderer to render a frame. - // - // Input: - // - timeMS : The time in ms required by the renderer to render a frame. - // - // Return value : VCM_OK, on success. - // < 0, on error. - virtual WebRtc_Word32 SetRenderDelay(WebRtc_UWord32 timeMS) = 0; - - // The total delay desired by the VCM. Can be less than the minimum - // delay set with SetMinimumPlayoutDelay. - // - // Return value : Total delay in ms, on success. - // < 0, on error. - virtual WebRtc_Word32 Delay() const = 0; - - // Get the received frame counters. Keeps track of the number of each frame type - // received since the start of the call. - // - // Output: - // - frameCount : Struct to be filled with the number of frames received. - // - // Return value : VCM_OK, on success. - // <0, on error. - virtual WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const = 0; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_H_ diff --git a/modules/video_coding/main/interface/video_coding_defines.h b/modules/video_coding/main/interface/video_coding_defines.h deleted file mode 100644 index 921fb9060..000000000 --- a/modules/video_coding/main/interface/video_coding_defines.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_ -#define WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_ - -#include "typedefs.h" -#include "module_common_types.h" - -namespace webrtc -{ - -// Error codes -#define VCM_REQUEST_SLI 2 -#define VCM_MISSING_CALLBACK 1 -#define VCM_OK 0 -#define VCM_GENERAL_ERROR -1 -#define VCM_LEVEL_EXCEEDED -2 -#define VCM_MEMORY -3 -#define VCM_PARAMETER_ERROR -4 -#define VCM_UNKNOWN_PAYLOAD -5 -#define VCM_CODEC_ERROR -6 -#define VCM_UNINITIALIZED -7 -#define VCM_NO_CODEC_REGISTERED -8 -#define VCM_JITTER_BUFFER_ERROR -9 -#define VCM_OLD_PACKET_ERROR -10 -#define VCM_NO_FRAME_DECODED -11 -#define VCM_ERROR_REQUEST_SLI -12 -#define VCM_NOT_IMPLEMENTED -20 - -#define VCM_H263_PAYLOAD_TYPE 34 -#define VCM_RED_PAYLOAD_TYPE 96 -#define VCM_ULPFEC_PAYLOAD_TYPE 97 -#define VCM_H263_1998_PAYLOAD_TYPE 121 -#define VCM_VP8_PAYLOAD_TYPE 120 -#define VCM_I420_PAYLOAD_TYPE 124 - -enum VCMNackProperties -{ - kNackHistoryLength = 450 -}; - -enum VCMH263FrameDrop -{ - kDecodePFrames, - kDropPFrames -}; - -enum VCMVideoProtection -{ - kProtectionNack, // Both send-side and receive-side - kProtectionNackSender, // Send-side only - kProtectionNackReceiver, // Receive-side only - kProtectionDualDecoder, - kProtectionFEC, - kProtectionNackFEC, - kProtectionKeyOnLoss, - kProtectionKeyOnKeyLoss, - kProtectionPeriodicKeyFrames -}; - -enum VCMTemporalDecimation -{ - kBitrateOverUseDecimation, -}; - -struct VCMFrameCount -{ - WebRtc_UWord32 numKeyFrames; - WebRtc_UWord32 numDeltaFrames; -}; - - -// Callback class used for sending data ready to be packetized -class VCMPacketizationCallback -{ -public: - virtual WebRtc_Word32 SendData( - const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader, - const RTPVideoTypeHeader* rtpTypeHdr) = 0; -protected: - virtual ~VCMPacketizationCallback() {} -}; - -// Callback class used for passing decoded frames which are ready to be rendered. -class VCMFrameStorageCallback -{ -public: - virtual WebRtc_Word32 StoreReceivedFrame(const EncodedVideoData& frameToStore) = 0; - -protected: - virtual ~VCMFrameStorageCallback() {} -}; - -// Callback class used for passing decoded frames which are ready to be rendered. -class VCMReceiveCallback -{ -public: - virtual WebRtc_Word32 FrameToRender(VideoFrame& videoFrame) = 0; - virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) {return -1;} - -protected: - virtual ~VCMReceiveCallback() {} -}; - -// Callback class used for informing the user of the bit rate and frame rate produced by the -// encoder. -class VCMSendStatisticsCallback -{ -public: - virtual WebRtc_Word32 SendStatistics(const WebRtc_UWord32 bitRate, - const WebRtc_UWord32 frameRate) = 0; - -protected: - virtual ~VCMSendStatisticsCallback() {} -}; - -// Callback class used for informing the user of the incoming bit rate and frame rate. -class VCMReceiveStatisticsCallback -{ -public: - virtual WebRtc_Word32 ReceiveStatistics(const WebRtc_UWord32 bitRate, - const WebRtc_UWord32 frameRate) = 0; - -protected: - virtual ~VCMReceiveStatisticsCallback() {} -}; - -// Callback class used for telling the user about the requested amount of bit stream protection -// Key frame FEC rate, delta frame and whether NACK should be on or off. -class VCMProtectionCallback -{ -public: - virtual WebRtc_Word32 ProtectionRequest(const WebRtc_UWord8 deltaFECRate, - const WebRtc_UWord8 keyFECRate, - const bool nack) = 0; - -protected: - virtual ~VCMProtectionCallback() {} -}; - -// Callback class used for telling the user about what frame type needed to continue decoding. -// Typically a key frame when the stream has been corrupted in some way. -class VCMFrameTypeCallback -{ -public: - virtual WebRtc_Word32 FrameTypeRequest(const FrameType frameType) = 0; - virtual WebRtc_Word32 SliceLossIndicationRequest(const WebRtc_UWord64 pictureId) {return -1;} - -protected: - virtual ~VCMFrameTypeCallback() {} -}; - -// Callback class used for telling the user about which packet sequence numbers are currently -// missing and need to be resent. -class VCMPacketRequestCallback -{ -public: - virtual WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers, - WebRtc_UWord16 length) = 0; - -protected: - virtual ~VCMPacketRequestCallback() {} -}; - -// Callback used to inform the user of the the desired resolution -// as subscribed by Media Optimization (Quality Modes) -class VCMQMSettingsCallback -{ -public: - virtual WebRtc_Word32 SetVideoQMSettings(const WebRtc_UWord32 frameRate, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) = 0; - -protected: - virtual ~VCMQMSettingsCallback() {} -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_ diff --git a/modules/video_coding/main/source/Android.mk b/modules/video_coding/main/source/Android.mk deleted file mode 100644 index 947895786..000000000 --- a/modules/video_coding/main/source/Android.mk +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_coding -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := codec_database.cc \ - codec_timer.cc \ - content_metrics_processing.cc \ - encoded_frame.cc \ - exp_filter.cc \ - frame_buffer.cc \ - frame_dropper.cc \ - frame_list.cc \ - generic_decoder.cc \ - generic_encoder.cc \ - inter_frame_delay.cc \ - jitter_buffer.cc \ - jitter_estimator.cc \ - media_opt_util.cc \ - media_optimization.cc \ - packet.cc \ - qm_select.cc \ - receiver.cc \ - rtt_filter.cc \ - session_info.cc \ - timestamp_extrapolator.cc \ - timestamp_map.cc \ - timing.cc \ - video_coding_impl.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../codecs/interface \ - $(LOCAL_PATH)/../../codecs/i420/main/interface \ - $(LOCAL_PATH)/../../codecs/vp8/main/interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_coding/main/source/codec_database.cc b/modules/video_coding/main/source/codec_database.cc deleted file mode 100644 index 612b88bfc..000000000 --- a/modules/video_coding/main/source/codec_database.cc +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "codec_database.h" -#include "../../../../engine_configurations.h" -#include "internal_defines.h" -#include "trace.h" - -#if defined(_WIN32) - // VS 2005: Don't warn for default initialized arrays. See help for more info. - // Don't warn for strncpy being unsecure. - // switch statement contains 'default' but no 'case' labels -#pragma warning(disable:4351; disable:4996; disable:4065) -#endif - -// Supported codecs -#ifdef VIDEOCODEC_VP8 - #include "vp8.h" -#endif -#ifdef VIDEOCODEC_I420 - #include "i420.h" -#endif - -namespace webrtc -{ - -VCMDecoderMapItem::VCMDecoderMapItem(VideoCodec* settings, - WebRtc_UWord32 numberOfCores, - bool requireKeyFrame) -: -_settings(settings), -_numberOfCores(numberOfCores), -_requireKeyFrame(requireKeyFrame) -{ -} - -VCMExtDecoderMapItem::VCMExtDecoderMapItem(VideoDecoder* externalDecoderInstance, - WebRtc_UWord8 payloadType, - bool internalRenderTiming) -: -_payloadType(payloadType), -_externalDecoderInstance(externalDecoderInstance), -_internalRenderTiming(internalRenderTiming) -{ -} - -VCMCodecDataBase::VCMCodecDataBase(WebRtc_Word32 id): -_id(id), -_numberOfCores(0), -_maxPayloadSize(kDefaultPayloadSize), -_periodicKeyFrames(false), -_currentEncIsExternal(false), -_sendCodec(), -_receiveCodec(), -_externalPayloadType(0), -_externalEncoder(NULL), -_internalSource(false), -_ptrEncoder(NULL), -_ptrDecoder(NULL), -_currentDecIsExternal(false), -_decMap(), -_decExternalMap() -{ - // -} - -VCMCodecDataBase::~VCMCodecDataBase() -{ - Reset(); -} - -WebRtc_Word32 -VCMCodecDataBase::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - VCMGenericEncoder* encoder = NULL; - VideoCodec settings; - WebRtc_Word32 ret; - for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++) - { - ret = VCMCodecDataBase::Codec(i, &settings); - if (ret < 0) - { - return ret; - } - encoder = CreateEncoder(settings.codecType); - if (encoder == NULL) - { - return VCM_MEMORY; - } - ret = encoder->_encoder.Version(&version[position], remainingBufferInBytes); - if (ret < 0) - { - return ret; - } - remainingBufferInBytes -= ret; - position += ret; - delete &encoder->_encoder; - delete encoder; - } - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::Reset() -{ - WebRtc_Word32 ret = ResetReceiver(); - if (ret < 0) - { - return ret; - } - ret = ResetSender(); - if (ret < 0) - { - return ret; - } - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::ResetSender() -{ - DeleteEncoder(); - _periodicKeyFrames = false; - return VCM_OK; -} - -VCMGenericEncoder* -VCMCodecDataBase::CreateEncoder(VideoCodecType type) const -{ - switch(type) - { -#ifdef VIDEOCODEC_VP8 - case kVideoCodecVP8: - return new VCMGenericEncoder(*(new VP8Encoder)); - break; -#endif -#ifdef VIDEOCODEC_I420 - case kVideoCodecI420: - return new VCMGenericEncoder(*(new I420Encoder)); - break; -#endif - default: - return NULL; - break; - } -} - -void -VCMCodecDataBase::DeleteEncoder() -{ - if (_ptrEncoder) - { - _ptrEncoder->Release(); - if (!_currentEncIsExternal) - { - delete &_ptrEncoder->_encoder; - } - delete _ptrEncoder; - _ptrEncoder = NULL; - } -} - -WebRtc_UWord8 -VCMCodecDataBase::NumberOfCodecs() -{ - return VCM_NUM_VIDEO_CODECS_AVAILABLE; -} - -WebRtc_Word32 -VCMCodecDataBase::Codec(WebRtc_UWord8 listId, VideoCodec *settings) -{ - if (settings == NULL) - { - return VCM_PARAMETER_ERROR; - } - - if (listId >= VCM_NUM_VIDEO_CODECS_AVAILABLE) - { - return VCM_PARAMETER_ERROR; - } - memset(settings, 0, sizeof(VideoCodec)); - switch (listId) - { -#ifdef VIDEOCODEC_VP8 - case VCM_VP8_IDX: - { - strncpy(settings->plName, "VP8", 3); - settings->codecType = kVideoCodecVP8; - // 96 to 127 dynamic payload types for video codecs - settings->plType = VCM_VP8_PAYLOAD_TYPE; - settings->startBitrate = 100; - settings->minBitrate = VCM_MIN_BITRATE; - settings->maxBitrate = 0; - settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; - settings->width = VCM_DEFAULT_CODEC_WIDTH; - settings->height = VCM_DEFAULT_CODEC_HEIGHT; - break; - } -#endif -#ifdef VIDEOCODEC_I420 - case VCM_I420_IDX: - { - strncpy(settings->plName, "I420", 4); - settings->codecType = kVideoCodecI420; - // 96 to 127 dynamic payload types for video codecs - settings->plType = VCM_I420_PAYLOAD_TYPE; - // Bitrate needed for this size and framerate - settings->startBitrate = 3*VCM_DEFAULT_CODEC_WIDTH* - VCM_DEFAULT_CODEC_HEIGHT*8* - VCM_DEFAULT_FRAME_RATE/1000/2; - settings->maxBitrate = settings->startBitrate; - settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; - settings->width = VCM_DEFAULT_CODEC_WIDTH; - settings->height = VCM_DEFAULT_CODEC_HEIGHT; - settings->minBitrate = VCM_MIN_BITRATE; - break; - } -#endif - default: - { - return VCM_PARAMETER_ERROR; - } - } - - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::Codec(VideoCodecType codecType, VideoCodec* settings) -{ - for (int i = 0; i < VCMCodecDataBase::NumberOfCodecs(); i++) - { - const WebRtc_Word32 ret = VCMCodecDataBase::Codec(i, settings); - if (ret != VCM_OK) - { - return ret; - } - if (codecType == settings->codecType) - { - return VCM_OK; - } - } - return VCM_PARAMETER_ERROR; -} - -// assuming only one registered encoder - since only one used, no need for more -WebRtc_Word32 -VCMCodecDataBase::RegisterSendCodec(const VideoCodec* sendCodec, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize) - { - if (sendCodec == NULL) - { - return VCM_UNINITIALIZED; - } - if (maxPayloadSize == 0) - { - maxPayloadSize = kDefaultPayloadSize; - } - if (numberOfCores > 32) - { - return VCM_PARAMETER_ERROR; - } - if (strcmp(sendCodec->plName, "H263") == 0 && - (sendCodec->plType != 34)) - { - return VCM_PARAMETER_ERROR; - } - if (sendCodec->plType <= 0) - { - return VCM_PARAMETER_ERROR; - } - // Make sure the start bit rate is sane... - if (sendCodec->startBitrate > 1000000) - { - return VCM_PARAMETER_ERROR; - } - if (sendCodec->codecType == kVideoCodecUnknown) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - _numberOfCores = numberOfCores; - _maxPayloadSize = maxPayloadSize; - - memcpy(&_sendCodec, sendCodec, sizeof(VideoCodec)); - - if (_sendCodec.maxBitrate == 0) - { - // max is one bit per pixel - _sendCodec.maxBitrate = ((WebRtc_Word32)_sendCodec.height * - (WebRtc_Word32)_sendCodec.width * - (WebRtc_Word32)_sendCodec.maxFramerate) / 1000; - if (_sendCodec.startBitrate > _sendCodec.maxBitrate) - { - // but if the customer tries to set a higher start bit rate we will increase - // the max accordingly - _sendCodec.maxBitrate = _sendCodec.startBitrate; - } - } - - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::SendCodec(VideoCodec* currentSendCodec) const -{ - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec"); - - if(_ptrEncoder == NULL) - { - return VCM_UNINITIALIZED; - } - memcpy(currentSendCodec, &_sendCodec, sizeof(VideoCodec)); - return VCM_OK; -} - -VideoCodecType -VCMCodecDataBase::SendCodec() const -{ - WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SendCodec type"); - if (_ptrEncoder == NULL) - { - return kVideoCodecUnknown; - } - return _sendCodec.codecType; -} - -WebRtc_Word32 -VCMCodecDataBase::DeRegisterExternalEncoder(WebRtc_UWord8 payloadType, bool& wasSendCodec) -{ - wasSendCodec = false; - if (_externalPayloadType != payloadType) - { - return VCM_PARAMETER_ERROR; - } - if (_sendCodec.plType == payloadType) - { - //De-register as send codec if needed - DeleteEncoder(); - memset(&_sendCodec, 0, sizeof(VideoCodec)); - _currentEncIsExternal = false; - wasSendCodec = true; - } - _externalPayloadType = 0; - _externalEncoder = NULL; - _internalSource = false; - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::RegisterExternalEncoder(VideoEncoder* externalEncoder, - WebRtc_UWord8 payloadType, - bool internalSource) -{ - // since only one encoder can be used at a given time, - // only one external encoder can be registered/used - _externalEncoder = externalEncoder; - _externalPayloadType = payloadType; - _internalSource = internalSource; - - return VCM_OK; -} - -VCMGenericEncoder* -VCMCodecDataBase::SetEncoder(const VideoCodec* settings, - VCMEncodedFrameCallback* VCMencodedFrameCallback) - -{ - // if encoder exists, will destroy it and create new one - DeleteEncoder(); - - if (settings->plType == _externalPayloadType) - { - // External encoder - _ptrEncoder = new VCMGenericEncoder(*_externalEncoder, _internalSource); - _currentEncIsExternal = true; - } - else - { - _ptrEncoder = CreateEncoder(settings->codecType); - _currentEncIsExternal = false; - } - - VCMencodedFrameCallback->SetPayloadType(settings->plType); - - if (_ptrEncoder == NULL) - { - return NULL; - } - - if (_ptrEncoder->InitEncode(settings, _numberOfCores, _maxPayloadSize) < 0) - { - DeleteEncoder(); - return NULL; - } - else if (_ptrEncoder->RegisterEncodeCallback(VCMencodedFrameCallback) < 0) - { - DeleteEncoder(); - return NULL; - } - // Intentionally don't check return value since the encoder registration - // shouldn't fail because the codec doesn't support changing the - // periodic key frame setting. - _ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames); - return _ptrEncoder; -} - -WebRtc_Word32 -VCMCodecDataBase::SetPeriodicKeyFrames(bool enable) -{ - _periodicKeyFrames = enable; - if (_ptrEncoder != NULL) - { - return _ptrEncoder->SetPeriodicKeyFrames(_periodicKeyFrames); - } - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::RegisterReceiveCodec(const VideoCodec* receiveCodec, - WebRtc_UWord32 numberOfCores, - bool requireKeyFrame) -{ - WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCoding, VCMId(_id), - "Codec: %s, Payload type %d, Height %d, Width %d, Bitrate %d, Framerate %d.", - receiveCodec->plName, receiveCodec->plType, - receiveCodec->height, receiveCodec->width, - receiveCodec->startBitrate, receiveCodec->maxFramerate); - - // check if payload value already exists, if so - erase old and insert new - DeRegisterReceiveCodec(receiveCodec->plType); - if (receiveCodec->codecType == kVideoCodecUnknown) - { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; - } - VideoCodec* newReceiveCodec = new VideoCodec(*receiveCodec); - _decMap.Insert(receiveCodec->plType, - new VCMDecoderMapItem(newReceiveCodec, numberOfCores, requireKeyFrame)); - - return VCM_OK; -} - -WebRtc_Word32 VCMCodecDataBase::DeRegisterReceiveCodec(WebRtc_UWord8 payloadType) -{ - MapItem* item = _decMap.Find(payloadType); - if (item == NULL) - { - return VCM_PARAMETER_ERROR; - } - VCMDecoderMapItem* decItem = static_cast(item->GetItem()); - delete decItem->_settings; - delete decItem; - _decMap.Erase(item); - if (_receiveCodec.plType == payloadType) - { - // This codec is currently in use. - memset(&_receiveCodec, 0, sizeof(VideoCodec)); - _currentDecIsExternal = false; - } - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::ResetReceiver() -{ - ReleaseDecoder(_ptrDecoder); - _ptrDecoder = NULL; - memset(&_receiveCodec, 0, sizeof(VideoCodec)); - MapItem* item = _decMap.First(); - while (item != NULL) - { - VCMDecoderMapItem* decItem = static_cast(item->GetItem()); - if (decItem != NULL) - { - if (decItem->_settings != NULL) - { - delete decItem->_settings; - } - delete decItem; - } - _decMap.Erase(item); - item = _decMap.First(); - } - item = _decExternalMap.First(); - while (item != NULL) - { - VCMExtDecoderMapItem* decItem = static_cast(item->GetItem()); - if (decItem != NULL) - { - delete decItem; - } - _decExternalMap.Erase(item); - item = _decExternalMap.First(); - } - _currentDecIsExternal = false; - return VCM_OK; -} - -WebRtc_Word32 -VCMCodecDataBase::DeRegisterExternalDecoder(WebRtc_UWord8 payloadType) -{ - MapItem* item = _decExternalMap.Find(payloadType); - if (item == NULL) - { - // Not found - return VCM_PARAMETER_ERROR; - } - if (_receiveCodec.plType == payloadType) - { - // Release it if it was registered and in use - ReleaseDecoder(_ptrDecoder); - _ptrDecoder = NULL; - } - DeRegisterReceiveCodec(payloadType); - VCMExtDecoderMapItem* decItem = static_cast(item->GetItem()); - delete decItem; - _decExternalMap.Erase(item); - return VCM_OK; -} - -// Add the external encoder object to the list of external decoders. -// Won't be registered as a receive codec until RegisterReceiveCodec is called. -WebRtc_Word32 -VCMCodecDataBase::RegisterExternalDecoder(VideoDecoder* externalDecoder, - WebRtc_UWord8 payloadType, - bool internalRenderTiming) -{ - // check if payload value already exists, if so - erase old and insert new - VCMExtDecoderMapItem* extDecoder = new VCMExtDecoderMapItem(externalDecoder, - payloadType, - internalRenderTiming); - if (extDecoder == NULL) - { - return VCM_MEMORY; - } - DeRegisterExternalDecoder(payloadType); - _decExternalMap.Insert(payloadType, extDecoder); - - return VCM_OK; -} - -bool -VCMCodecDataBase::DecoderRegistered() const -{ - return (_decMap.Size() > 0); -} - -WebRtc_Word32 -VCMCodecDataBase::ReceiveCodec(VideoCodec* currentReceiveCodec) const -{ - if (_ptrDecoder == NULL) - { - return VCM_NO_FRAME_DECODED; - } - memcpy(currentReceiveCodec, &_receiveCodec, sizeof(VideoCodec)); - return VCM_OK; -} - -VideoCodecType -VCMCodecDataBase::ReceiveCodec() const -{ - if (_ptrDecoder == NULL) - { - return kVideoCodecUnknown; - } - return _receiveCodec.codecType; -} - -VCMGenericDecoder* -VCMCodecDataBase::SetDecoder(WebRtc_UWord8 payloadType, VCMDecodedFrameCallback& callback) -{ - if (payloadType == _receiveCodec.plType || payloadType == 0) - { - return _ptrDecoder; - } - // check for exisitng decoder, if exists - delete - if (_ptrDecoder) - { - ReleaseDecoder(_ptrDecoder); - _ptrDecoder = NULL; - memset(&_receiveCodec, 0, sizeof(VideoCodec)); - } - _ptrDecoder = CreateAndInitDecoder(payloadType, _receiveCodec, _currentDecIsExternal); - if (_ptrDecoder == NULL) - { - return NULL; - } - if (_ptrDecoder->RegisterDecodeCompleteCallback(&callback) < 0) - { - ReleaseDecoder(_ptrDecoder); - _ptrDecoder = NULL; - memset(&_receiveCodec, 0, sizeof(VideoCodec)); - return NULL; - } - return _ptrDecoder; -} - -VCMGenericDecoder* -VCMCodecDataBase::CreateAndInitDecoder(WebRtc_UWord8 payloadType, - VideoCodec& newCodec, - bool &external) const -{ - VCMDecoderMapItem* decoderItem = FindDecoderItem(payloadType); - if (decoderItem == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Unknown payload type: %u", payloadType); - return NULL; - } - VCMGenericDecoder* ptrDecoder = NULL; - VCMExtDecoderMapItem* externalDecItem = FindExternalDecoderItem(payloadType); - if (externalDecItem != NULL) - { - // External codec - ptrDecoder = new VCMGenericDecoder(*externalDecItem->_externalDecoderInstance, _id, - true); - external = true; - } - else - { - // create decoder - ptrDecoder = CreateDecoder(decoderItem->_settings->codecType); - external = false; - } - if (ptrDecoder == NULL) - { - return NULL; - } - - if (ptrDecoder->InitDecode(decoderItem->_settings, - decoderItem->_numberOfCores, - decoderItem->_requireKeyFrame) < 0) - { - ReleaseDecoder(ptrDecoder); - return NULL; - } - - SetCodecConfigParameters(*ptrDecoder, *decoderItem->_settings); - - memcpy(&newCodec, decoderItem->_settings, sizeof(VideoCodec)); - return ptrDecoder; -} - -VCMGenericDecoder* -VCMCodecDataBase::CreateDecoderCopy() const -{ - if (_ptrDecoder == NULL) - { - return NULL; - } - VideoDecoder* decoderCopy = _ptrDecoder->_decoder.Copy(); - if (decoderCopy == NULL) - { - return NULL; - } - return new VCMGenericDecoder(*decoderCopy, _id, _ptrDecoder->External()); -} - -void -VCMCodecDataBase::CopyDecoder(const VCMGenericDecoder& decoder) -{ - VideoDecoder* decoderCopy = decoder._decoder.Copy(); - if (decoderCopy != NULL) - { - ReleaseDecoder(_ptrDecoder); - _ptrDecoder = new VCMGenericDecoder(*decoderCopy, _id, decoder.External()); - } -} - -bool -VCMCodecDataBase::RenderTiming() const -{ - bool renderTiming = true; - if (_currentDecIsExternal) - { - VCMExtDecoderMapItem* extItem = FindExternalDecoderItem(_receiveCodec.plType); - renderTiming = extItem->_internalRenderTiming; - } - return renderTiming; -} - -void -VCMCodecDataBase::ReleaseDecoder(VCMGenericDecoder* decoder) const -{ - if (decoder != NULL) - { - decoder->Release(); - if (!decoder->External() && &decoder->_decoder != NULL) - { - delete &decoder->_decoder; - } - delete decoder; - } -} - -WebRtc_Word32 -VCMCodecDataBase::SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length) -{ - VCMDecoderMapItem* decItem = FindDecoderItem(payloadType); - if (decItem == NULL) - { - return VCM_PARAMETER_ERROR; - } - switch (decItem->_settings->codecType) - { - case kVideoCodecMPEG4: - { - memcpy(decItem->_settings->codecSpecific.MPEG4.configParameters, buffer, length); - decItem->_settings->codecSpecific.MPEG4.configParametersSize = - static_cast(length); - break; - } - default: - // This codec doesn't have codec config parameters - return VCM_GENERAL_ERROR; - } - if (_ptrDecoder != NULL && _receiveCodec.plType == decItem->_settings->plType) - { - return _ptrDecoder->SetCodecConfigParameters(buffer, length); - } - return VCM_OK; -} - -VCMDecoderMapItem* -VCMCodecDataBase::FindDecoderItem(WebRtc_UWord8 payloadType) const -{ - MapItem* item = _decMap.Find(payloadType); - if (item != NULL) - { - return static_cast(item->GetItem()); - } - return NULL; -} - -VCMExtDecoderMapItem* -VCMCodecDataBase::FindExternalDecoderItem(WebRtc_UWord8 payloadType) const -{ - MapItem* item = _decExternalMap.Find(payloadType); - if (item != NULL) - { - return static_cast(item->GetItem()); - } - return NULL; -} - -VCMGenericDecoder* -VCMCodecDataBase::CreateDecoder(VideoCodecType type) const -{ - switch(type) - { -#ifdef VIDEOCODEC_VP8 - case kVideoCodecVP8: - return new VCMGenericDecoder(*(new VP8Decoder), _id); -#endif -#ifdef VIDEOCODEC_I420 - case kVideoCodecI420: - return new VCMGenericDecoder(*(new I420Decoder), _id); -#endif - default: - return NULL; - } -} - -void -VCMCodecDataBase::SetCodecConfigParameters(VCMGenericDecoder& decoder, - const VideoCodec& settings) -{ - switch (settings.codecType) - { - case kVideoCodecMPEG4: - { - if (settings.codecSpecific.MPEG4.configParametersSize > 0) - { - decoder.SetCodecConfigParameters( - settings.codecSpecific.MPEG4.configParameters, - settings.codecSpecific.MPEG4.configParametersSize); - } - break; - } - default: - // No codec config parameters for this codec - return; - } - return; -} - -} diff --git a/modules/video_coding/main/source/codec_database.h b/modules/video_coding/main/source/codec_database.h deleted file mode 100644 index 37943e8c6..000000000 --- a/modules/video_coding/main/source/codec_database.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_ - -#include "video_coding.h" -#include "video_codec_interface.h" -#include "generic_decoder.h" -#include "generic_encoder.h" -#include "typedefs.h" -#include "map_wrapper.h" - -namespace webrtc -{ - -enum VCMCodecDBProperties -{ - kDefaultPayloadSize = 1440 -}; - -class VCMDecoderMapItem { -public: - VCMDecoderMapItem(VideoCodec* settings, - WebRtc_UWord32 numberOfCores, - bool requireKeyFrame); - - VideoCodec* _settings; - WebRtc_UWord32 _numberOfCores; - bool _requireKeyFrame; -}; - -class VCMExtDecoderMapItem { -public: - VCMExtDecoderMapItem(VideoDecoder* externalDecoderInstance, - WebRtc_UWord8 payloadType, - bool internalRenderTiming); - - WebRtc_UWord8 _payloadType; - VideoDecoder* _externalDecoderInstance; - bool _internalRenderTiming; -}; - -/*******************************/ -/* VCMCodecDataBase class */ -/*******************************/ -class VCMCodecDataBase -{ -public: - VCMCodecDataBase(WebRtc_Word32 id); - ~VCMCodecDataBase(); - /** - * Fills "version" with the version of all codecs supported. - */ - WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - /** - * Release codecdatabase - release all memory for both send and receive side - */ - WebRtc_Word32 Reset(); - /** - * Sender Side - */ - /** - * Returns the number of supported codecs (or -1 in case of error). - */ - static WebRtc_UWord8 NumberOfCodecs(); - /** - * Get supported codecs with ID - * Input Values: - * listnr : Requested codec id number - * codec_inst: Pointer to the struct in which the returned codec information is copied - * Return Values: 0 if successful, otherwise - */ - static WebRtc_Word32 Codec(WebRtc_UWord8 listId, VideoCodec* settings); - static WebRtc_Word32 Codec(VideoCodecType codecType, VideoCodec* settings); - /** - * Reset Sender side - */ - WebRtc_Word32 ResetSender(); - /** - * Setting the sender side codec and initiaiting the desired codec given the VideoCodec - * struct. - * Return Value: 0 if the codec and the settings are supported, otherwise - */ - WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize); - /** - * Get current send side codec. Relevant for internal codecs only. - */ - WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const; - /** - * Get current send side codec type. Relevant for internal codecs only. - */ - VideoCodecType SendCodec() const; - /** - * Register external encoder - current assumption - if one is registered then it will also - * be used, and therefore it is also initialized - * Return value: A pointer to the encoder on success, or null, in case of an error. - */ - WebRtc_Word32 DeRegisterExternalEncoder(WebRtc_UWord8 payloadType, bool& wasSendCodec); - WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder, - WebRtc_UWord8 payloadType, - bool internalSource); - /** - * Returns a encoder given a payloadname - to be used with internal encoders only. - * Special cases: - * Encoder exists - If payload matches, returns existing one, otherwise, - * deletes existing one and creates new one. - * No match found / Error - returns NULL. - */ - VCMGenericEncoder* SetEncoder(const VideoCodec* settings, - VCMEncodedFrameCallback* VCMencodedFrameCallback); - - WebRtc_Word32 SetPeriodicKeyFrames(bool enable); - - bool InternalSource() const; - - /* - * Receiver Side - */ - WebRtc_Word32 ResetReceiver(); - /** - * Register external decoder/render object - */ - WebRtc_Word32 DeRegisterExternalDecoder(WebRtc_UWord8 payloadType); - WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder, - WebRtc_UWord8 payloadType, - bool internalRenderTiming); - - bool DecoderRegistered() const; - /** - * Register recieve codec - */ - WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec, - WebRtc_UWord32 numberOfCores, - bool requireKeyFrame); - WebRtc_Word32 DeRegisterReceiveCodec(WebRtc_UWord8 payloadType); - /** - * Get current receive side codec. Relevant for internal codecs only. - */ - WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const; - /** - * Get current receive side codec type. Relevant for internal codecs only. - */ - VideoCodecType ReceiveCodec() const; - /** - * Returns a decoder given which matches a payload type. - * Special cases: - * Decoder exists - If payload matches, returns existing one, otherwise, deletes - * existing one, and creates new one. - * No match found / Error - returns NULL. - */ - VCMGenericDecoder* SetDecoder(WebRtc_UWord8 payloadType, VCMDecodedFrameCallback& callback); - - VCMGenericDecoder* CreateAndInitDecoder(WebRtc_UWord8 payloadType, - VideoCodec& newCodec, - bool &external) const; - - VCMGenericDecoder* CreateDecoderCopy() const; - - void ReleaseDecoder(VCMGenericDecoder* decoder) const; - - void CopyDecoder(const VCMGenericDecoder& decoder); - - bool RenderTiming() const; - - WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length); - -protected: - /** - * Create an internal Encoder given a codec type - */ - VCMGenericEncoder* CreateEncoder(VideoCodecType type) const; - - void DeleteEncoder(); - /* - * Create an internal Decoder given a codec type - */ - VCMGenericDecoder* CreateDecoder(VideoCodecType type) const; - - static void SetCodecConfigParameters(VCMGenericDecoder& decoder, - const VideoCodec& settings); - - VCMDecoderMapItem* FindDecoderItem(WebRtc_UWord8 payloadType) const; - - VCMExtDecoderMapItem* FindExternalDecoderItem(WebRtc_UWord8 payloadType) const; - -private: - WebRtc_Word32 _id; - WebRtc_UWord32 _numberOfCores; - WebRtc_UWord32 _maxPayloadSize; - bool _periodicKeyFrames; - bool _currentEncIsExternal; - VideoCodec _sendCodec; - VideoCodec _receiveCodec; - WebRtc_UWord8 _externalPayloadType; - VideoEncoder* _externalEncoder; - bool _internalSource; - VCMGenericEncoder* _ptrEncoder; - VCMGenericDecoder* _ptrDecoder; - bool _currentDecIsExternal; - MapWrapper _decMap; - MapWrapper _decExternalMap; - -}; // end of VCMCodecDataBase class definition - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODEC_DATABASE_H_ diff --git a/modules/video_coding/main/source/codec_timer.cc b/modules/video_coding/main/source/codec_timer.cc deleted file mode 100644 index 1d112fa29..000000000 --- a/modules/video_coding/main/source/codec_timer.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "codec_timer.h" - -#include - -namespace webrtc -{ - -VCMCodecTimer::VCMCodecTimer() -: -_filteredMax(0), -_firstDecodeTime(true), -_shortMax(0), -_history() -{ - Reset(); -} - -WebRtc_Word32 VCMCodecTimer::StopTimer(WebRtc_Word64 startTimeMs, WebRtc_Word64 nowMs) -{ - const WebRtc_Word32 timeDiff = static_cast(nowMs - startTimeMs); - MaxFilter(timeDiff, nowMs); - return timeDiff; -} - -void VCMCodecTimer::Reset() -{ - _filteredMax = 0; - _firstDecodeTime = true; - _shortMax = 0; - for (int i=0; i < MAX_HISTORY_SIZE; i++) - { - _history[i].shortMax = 0; - _history[i].timeMs = -1; - } -} - -// Update the max-value filter -void VCMCodecTimer::MaxFilter(WebRtc_Word32 decodeTime, WebRtc_Word64 nowMs) -{ - if (!_firstDecodeTime) - { - UpdateMaxHistory(decodeTime, nowMs); - ProcessHistory(nowMs); - } - else - { - _firstDecodeTime = false; - } -} - -void -VCMCodecTimer::UpdateMaxHistory(WebRtc_Word32 decodeTime, WebRtc_Word64 now) -{ - if (_history[0].timeMs >= 0 && - now - _history[0].timeMs < SHORT_FILTER_MS) - { - if (decodeTime > _shortMax) - { - _shortMax = decodeTime; - } - } - else - { - // Only add a new value to the history once a second - if(_history[0].timeMs == -1) - { - // First, no shift - _shortMax = decodeTime; - } - else - { - // Shift - for(int i = (MAX_HISTORY_SIZE - 2); i >= 0 ; i--) - { - _history[i+1].shortMax = _history[i].shortMax; - _history[i+1].timeMs = _history[i].timeMs; - } - } - if (_shortMax == 0) - { - _shortMax = decodeTime; - } - - _history[0].shortMax = _shortMax; - _history[0].timeMs = now; - _shortMax = 0; - } -} - -void -VCMCodecTimer::ProcessHistory(WebRtc_Word64 nowMs) -{ - _filteredMax = _shortMax; - if (_history[0].timeMs == -1) - { - return; - } - for (int i=0; i < MAX_HISTORY_SIZE; i++) - { - if (_history[i].timeMs == -1) - { - break; - } - if (nowMs - _history[i].timeMs > MAX_HISTORY_SIZE * SHORT_FILTER_MS) - { - // This sample (and all samples after this) is too old - break; - } - if (_history[i].shortMax > _filteredMax) - { - // This sample is the largest one this far into the history - _filteredMax = _history[i].shortMax; - } - } -} - -// Get the maximum observed time within a time window -WebRtc_Word32 VCMCodecTimer::RequiredDecodeTimeMs(FrameType /*frameType*/) const -{ - return _filteredMax; -} - -} diff --git a/modules/video_coding/main/source/codec_timer.h b/modules/video_coding/main/source/codec_timer.h deleted file mode 100644 index e03c5bfe9..000000000 --- a/modules/video_coding/main/source/codec_timer.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_ - -#include "typedefs.h" -#include "module_common_types.h" - -namespace webrtc -{ - -// MAX_HISTORY_SIZE * SHORT_FILTER_MS defines the window size in milliseconds -#define MAX_HISTORY_SIZE 20 -#define SHORT_FILTER_MS 1000 - -class VCMShortMaxSample -{ -public: - VCMShortMaxSample() : shortMax(0), timeMs(-1) {}; - - WebRtc_Word32 shortMax; - WebRtc_Word64 timeMs; -}; - -class VCMCodecTimer -{ -public: - VCMCodecTimer(); - - // Updates and returns the max filtered decode time. - WebRtc_Word32 StopTimer(WebRtc_Word64 startTimeMs, WebRtc_Word64 nowMs); - - // Empty the list of timers. - void Reset(); - - // Get the required decode time in ms. - WebRtc_Word32 RequiredDecodeTimeMs(FrameType frameType) const; - -private: - void UpdateMaxHistory(WebRtc_Word32 decodeTime, WebRtc_Word64 now); - void MaxFilter(WebRtc_Word32 newTime, WebRtc_Word64 nowMs); - void ProcessHistory(WebRtc_Word64 nowMs); - - WebRtc_Word32 _filteredMax; - bool _firstDecodeTime; - WebRtc_Word32 _shortMax; - VCMShortMaxSample _history[MAX_HISTORY_SIZE]; - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_CODEC_TIMER_H_ diff --git a/modules/video_coding/main/source/content_metrics_processing.cc b/modules/video_coding/main/source/content_metrics_processing.cc deleted file mode 100644 index 5206da00c..000000000 --- a/modules/video_coding/main/source/content_metrics_processing.cc +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "content_metrics_processing.h" -#include "tick_time.h" -#include "module_common_types.h" -#include "video_coding_defines.h" - -#include - -namespace webrtc { - -////////////////////////////////// -/// VCMContentMetricsProcessing // -////////////////////////////////// - -VCMContentMetricsProcessing::VCMContentMetricsProcessing(): -_frameRate(0), -_recAvgFactor(1 / 150.0f), // matched to 30fps -_frameCntRecursiveAvg(0), -_frameCntUniformAvg(0), -_avgMotionLevel(0.0f), -_avgSpatialLevel(0.0f) -{ - _recursiveAvg = new VideoContentMetrics(); - _uniformAvg = new VideoContentMetrics(); -} - -VCMContentMetricsProcessing::~VCMContentMetricsProcessing() -{ - delete _recursiveAvg; - delete _uniformAvg; -} - -WebRtc_Word32 -VCMContentMetricsProcessing::Reset() -{ - _recursiveAvg->Reset(); - _uniformAvg->Reset(); - _frameRate = 0; - _frameCntRecursiveAvg = 0; - _frameCntUniformAvg = 0; - _avgMotionLevel = 0.0f; - _avgSpatialLevel = 0.0f; - return VCM_OK; -} - -void -VCMContentMetricsProcessing::UpdateFrameRate(WebRtc_UWord32 frameRate) -{ - _frameRate = frameRate; - // Update factor for recursive averaging. - _recAvgFactor = (float) 1000.0f / ((float)(_frameRate * kQmMinIntervalMs)); - -} - -VideoContentMetrics* -VCMContentMetricsProcessing::LongTermAvgData() -{ - if (_frameCntRecursiveAvg == 0) - { - return NULL; - } - return _recursiveAvg; -} - -VideoContentMetrics* -VCMContentMetricsProcessing::ShortTermAvgData() -{ - if (_frameCntUniformAvg == 0) - { - return NULL; - } - - // Two metrics are used: motion and spatial level. - _uniformAvg->motionMagnitudeNZ = _avgMotionLevel / - (float)(_frameCntUniformAvg); - _uniformAvg->spatialPredErr = _avgSpatialLevel / - (float)(_frameCntUniformAvg); - - return _uniformAvg; -} - -void -VCMContentMetricsProcessing::ResetShortTermAvgData() -{ - // Reset - _avgMotionLevel = 0.0f; - _avgSpatialLevel = 0.0f; - _frameCntUniformAvg = 0; -} - -WebRtc_Word32 -VCMContentMetricsProcessing::UpdateContentData(const VideoContentMetrics *contentMetrics) -{ - if (contentMetrics == NULL) - { - return VCM_OK; - } - return ProcessContent(contentMetrics); - -} - -WebRtc_UWord32 -VCMContentMetricsProcessing::ProcessContent(const VideoContentMetrics *contentMetrics) -{ - // Update the recursive averaged metrics - // average is over longer window of time: over QmMinIntervalMs ms. - UpdateRecursiveAvg(contentMetrics); - - // Update the uniform averaged metrics: - // average is over shorter window of time: based on ~RTCP reports. - UpdateUniformAvg(contentMetrics); - - return VCM_OK; -} - -void -VCMContentMetricsProcessing::UpdateUniformAvg(const VideoContentMetrics *contentMetrics) -{ - - // Update frame counter - _frameCntUniformAvg += 1; - - // Update averaged metrics: motion and spatial level are used. - _avgMotionLevel += contentMetrics->motionMagnitudeNZ; - _avgSpatialLevel += contentMetrics->spatialPredErr; - - return; - -} -void -VCMContentMetricsProcessing::UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics) -{ - - // Threshold for size of zero motion cluster: - // Use for updating 3 motion vector derived metrics: - // motion magnitude, cluster distortion, and horizontalness. - float nonZeroMvThr = 0.1f; - - float tmpRecAvgFactor = _recAvgFactor; - - // Take value as is for first frame (no motion search in frame zero). - if (_frameCntRecursiveAvg < 1) - { - tmpRecAvgFactor = 1; - } - - _recursiveAvg->motionPredErr = (1 - tmpRecAvgFactor) * - _recursiveAvg->motionPredErr + - tmpRecAvgFactor * contentMetrics->motionPredErr; - - _recursiveAvg->sizeZeroMotion = (1 - tmpRecAvgFactor) * - _recursiveAvg->sizeZeroMotion + - tmpRecAvgFactor * contentMetrics->sizeZeroMotion; - - _recursiveAvg->spatialPredErr = (1 - tmpRecAvgFactor) * - _recursiveAvg->spatialPredErr + - tmpRecAvgFactor * contentMetrics->spatialPredErr; - - _recursiveAvg->spatialPredErrH = (1 - tmpRecAvgFactor) * - _recursiveAvg->spatialPredErrH + - tmpRecAvgFactor * contentMetrics->spatialPredErrH; - - _recursiveAvg->spatialPredErrV = (1 - tmpRecAvgFactor) * - _recursiveAvg->spatialPredErrV + - tmpRecAvgFactor * contentMetrics->spatialPredErrV; - - // motionMag metric is derived from NFD (normalized frame difference). - if (kNfdMetric == 1) - { - _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) * - _recursiveAvg->motionMagnitudeNZ + - tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ; - } - - if (contentMetrics->sizeZeroMotion > nonZeroMvThr) - { - _recursiveAvg->motionClusterDistortion = (1 - tmpRecAvgFactor) * - _recursiveAvg->motionClusterDistortion + - tmpRecAvgFactor *contentMetrics->motionClusterDistortion; - - _recursiveAvg->motionHorizontalness = (1 - _recAvgFactor) * - _recursiveAvg->motionHorizontalness + - tmpRecAvgFactor * contentMetrics->motionHorizontalness; - - // motionMag metric is derived from motion vectors. - if (kNfdMetric == 0) - { - _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) * - _recursiveAvg->motionMagnitudeNZ + - tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ; - } - } - - // Update native values: - // TODO (marpan): we don't need to update this every frame. - _recursiveAvg->nativeHeight = contentMetrics->nativeHeight; - _recursiveAvg->nativeWidth = contentMetrics->nativeWidth; - _recursiveAvg->nativeFrameRate = contentMetrics->nativeFrameRate; - - _frameCntRecursiveAvg++; - - return; -} -} //end of namespace diff --git a/modules/video_coding/main/source/content_metrics_processing.h b/modules/video_coding/main/source/content_metrics_processing.h deleted file mode 100644 index 155c4adc6..000000000 --- a/modules/video_coding/main/source/content_metrics_processing.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_ -#define WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_ - -#include "typedefs.h" - -namespace webrtc -{ - -struct VideoContentMetrics; - -// QM interval time (in ms) -enum { kQmMinIntervalMs = 10000 }; - -// Flag for NFD metric vs motion metric -enum { kNfdMetric = 1 }; - -/**********************************/ -/* Content Metrics Processing */ -/**********************************/ -class VCMContentMetricsProcessing -{ -public: - VCMContentMetricsProcessing(); - ~VCMContentMetricsProcessing(); - - // Update class with latest metrics - WebRtc_Word32 UpdateContentData(const VideoContentMetrics *contentMetrics); - - // Reset the short-term averaged content data - void ResetShortTermAvgData(); - - // Initialize to - WebRtc_Word32 Reset(); - - // Inform class of current frame rate - void UpdateFrameRate(WebRtc_UWord32 frameRate); - - // Returns the long-term averaged content data: - // recursive average over longer time scale - VideoContentMetrics* LongTermAvgData(); - - // Returns the short-term averaged content data: - // uniform average over shorter time scale - VideoContentMetrics* ShortTermAvgData(); -private: - - // Compute working avg - WebRtc_UWord32 ProcessContent(const VideoContentMetrics *contentMetrics); - - // Update the recursive averaged metrics: longer time average (~5/10 secs). - void UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics); - - // Update the uniform averaged metrics: shorter time average (~RTCP reports). - void UpdateUniformAvg(const VideoContentMetrics *contentMetrics); - - VideoContentMetrics* _recursiveAvg; - VideoContentMetrics* _uniformAvg; - WebRtc_UWord32 _frameRate; - float _recAvgFactor; - WebRtc_UWord32 _frameCntRecursiveAvg; - WebRtc_UWord32 _frameCntUniformAvg; - float _avgMotionLevel; - float _avgSpatialLevel; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_ diff --git a/modules/video_coding/main/source/encoded_frame.cc b/modules/video_coding/main/source/encoded_frame.cc deleted file mode 100644 index 4515ca0f8..000000000 --- a/modules/video_coding/main/source/encoded_frame.cc +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "encoded_frame.h" -#include "generic_encoder.h" -#include "jitter_buffer_common.h" -#include "video_coding_defines.h" - -namespace webrtc { - -VCMEncodedFrame::VCMEncodedFrame() -: -webrtc::EncodedImage(), -_renderTimeMs(-1), -_payloadType(0), -_missingFrame(false), -_codecSpecificInfo(NULL), -_codecSpecificInfoLength(0), -_codec(kVideoCodecUnknown) -{ -} - -VCMEncodedFrame::VCMEncodedFrame(const webrtc::EncodedImage& rhs) -: -webrtc::EncodedImage(rhs), -_renderTimeMs(-1), -_payloadType(0), -_missingFrame(false), -_codecSpecificInfo(NULL), -_codecSpecificInfoLength(0), -_codec(kVideoCodecUnknown) -{ - _buffer = NULL; - _size = NULL; - _length = NULL; - if (rhs._buffer != NULL) - { - VerifyAndAllocate(rhs._length); - memcpy(_buffer, rhs._buffer, rhs._length); - } -} - -VCMEncodedFrame::VCMEncodedFrame(const VCMEncodedFrame& rhs) -: -webrtc::EncodedImage(rhs), -_renderTimeMs(rhs._renderTimeMs), -_payloadType(rhs._payloadType), -_missingFrame(rhs._missingFrame), -_codecSpecificInfo(NULL), -_codecSpecificInfoLength(0), -_codec(rhs._codec) -{ - _buffer = NULL; - _size = NULL; - _length = NULL; - if (rhs._buffer != NULL) - { - VerifyAndAllocate(rhs._size); - memcpy(_buffer, rhs._buffer, rhs._length); - } -} - -VCMEncodedFrame::~VCMEncodedFrame() -{ - Free(); -} - -void VCMEncodedFrame::Free() -{ - Reset(); - if (_buffer != NULL) - { - delete [] _buffer; - _buffer = NULL; - } -} - -void VCMEncodedFrame::Reset() -{ - _renderTimeMs = -1; - _timeStamp = 0; - _payloadType = 0; - _codecSpecificInfo = NULL; - _codecSpecificInfoLength = 0; - _frameType = kDeltaFrame; - _encodedWidth = 0; - _encodedHeight = 0; - _completeFrame = false; - _missingFrame = false; - _length = 0; - _codec = kVideoCodecUnknown; -} - -WebRtc_Word32 -VCMEncodedFrame::Store(VCMFrameStorageCallback& storeCallback) const -{ - EncodedVideoData frameToStore; - frameToStore.codec = _codec; - if (_buffer != NULL) - { - frameToStore.VerifyAndAllocate(_length); - memcpy(frameToStore.payloadData, _buffer, _length); - frameToStore.payloadSize = _length; - } - frameToStore.completeFrame = _completeFrame; - frameToStore.encodedWidth = _encodedWidth; - frameToStore.encodedHeight = _encodedHeight; - frameToStore.frameType = ConvertFrameType(_frameType); - frameToStore.missingFrame = _missingFrame; - frameToStore.payloadType = _payloadType; - frameToStore.renderTimeMs = _renderTimeMs; - frameToStore.timeStamp = _timeStamp; - storeCallback.StoreReceivedFrame(frameToStore); - return VCM_OK; -} - -WebRtc_Word32 -VCMEncodedFrame::VerifyAndAllocate(const WebRtc_UWord32 minimumSize) -{ - if(minimumSize > _size) - { - // create buffer of sufficient size - WebRtc_UWord8* newBuffer = new WebRtc_UWord8[minimumSize]; - if (newBuffer == NULL) - { - return -1; - } - if(_buffer) - { - // copy old data - memcpy(newBuffer, _buffer, _size); - delete [] _buffer; - } - _buffer = newBuffer; - _size = minimumSize; - } - return 0; -} - -webrtc::FrameType VCMEncodedFrame::ConvertFrameType(VideoFrameType frameType) -{ - switch(frameType) - { - case kKeyFrame: - { - return kVideoFrameKey; - } - case kDeltaFrame: - { - return kVideoFrameDelta; - } - case kGoldenFrame: - { - return kVideoFrameGolden; - } - case kAltRefFrame: - { - return kVideoFrameAltRef; - } - default: - { - return kVideoFrameDelta; - } - } -} - -VideoFrameType VCMEncodedFrame::ConvertFrameType(webrtc::FrameType frameType) -{ - switch (frameType) - { - case kVideoFrameKey: - { - return kKeyFrame; - } - case kVideoFrameDelta: - { - return kDeltaFrame; - } - case kVideoFrameGolden: - { - return kGoldenFrame; - } - case kVideoFrameAltRef: - { - return kAltRefFrame; - } - default: - { - return kDeltaFrame; - } - } -} - -} diff --git a/modules/video_coding/main/source/encoded_frame.h b/modules/video_coding/main/source/encoded_frame.h deleted file mode 100644 index 89cbd5e3d..000000000 --- a/modules/video_coding/main/source/encoded_frame.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_ -#define WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_ - -#include "module_common_types.h" -#include "common_types.h" -#include "video_coding_defines.h" -#include "video_image.h" - -namespace webrtc -{ - -class VCMEncodedFrame : protected EncodedImage -{ -public: - VCMEncodedFrame(); - VCMEncodedFrame(const webrtc::EncodedImage& rhs); - VCMEncodedFrame(const VCMEncodedFrame& rhs); - - ~VCMEncodedFrame(); - /** - * Delete VideoFrame and resets members to zero - */ - void Free(); - /** - * Set render time in milliseconds - */ - void SetRenderTime(const WebRtc_Word64 renderTimeMs) {_renderTimeMs = renderTimeMs;} - - /** - * Set the encoded frame size - */ - void SetEncodedSize(WebRtc_UWord32 width, WebRtc_UWord32 height) - { _encodedWidth = width; _encodedHeight = height; } - /** - * Get the encoded image - */ - const webrtc::EncodedImage& EncodedImage() const - { return static_cast(*this); } - /** - * Get pointer to frame buffer - */ - const WebRtc_UWord8* Buffer() const {return _buffer;} - /** - * Get frame length - */ - WebRtc_UWord32 Length() const {return _length;} - /** - * Get frame timestamp (90kHz) - */ - WebRtc_UWord32 TimeStamp() const {return _timeStamp;} - /** - * Get render time in milliseconds - */ - WebRtc_Word64 RenderTimeMs() const {return _renderTimeMs;} - /** - * Get frame type - */ - webrtc::FrameType FrameType() const {return ConvertFrameType(_frameType);} - /** - * True if this frame is complete, false otherwise - */ - bool Complete() const { return _completeFrame; } - /** - * True if there's a frame missing before this frame - */ - bool MissingFrame() const { return _missingFrame; } - /** - * Payload type of the encoded payload - */ - WebRtc_UWord8 PayloadType() const { return _payloadType; } - /** - * Get codec specific info - */ - const void* CodecSpecificInfo() const {return _codecSpecificInfo;} - - WebRtc_Word32 Store(VCMFrameStorageCallback& storeCallback) const; - - static webrtc::FrameType ConvertFrameType(VideoFrameType frameType); - static VideoFrameType ConvertFrameType(webrtc::FrameType frameType); - -protected: - /** - * Verifies that current allocated buffer size is larger than or equal to the input size. - * If the current buffer size is smaller, a new allocation is made and the old buffer data - * is copied to the new buffer. - * Buffer size is updated to minimumSize. - */ - WebRtc_Word32 VerifyAndAllocate(const WebRtc_UWord32 minimumSize); - - void Reset(); - - WebRtc_Word64 _renderTimeMs; - WebRtc_UWord8 _payloadType; - bool _missingFrame; - void* _codecSpecificInfo; - WebRtc_UWord32 _codecSpecificInfoLength; - webrtc::VideoCodecType _codec; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_ENCODED_FRAME_H_ diff --git a/modules/video_coding/main/source/er_tables_xor.h b/modules/video_coding/main/source/er_tables_xor.h deleted file mode 100644 index 7dc5191b6..000000000 --- a/modules/video_coding/main/source/er_tables_xor.h +++ /dev/null @@ -1,38728 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_ - -namespace webrtc -{ - -// Table for average FEC recovery from packet loss, for XOR code. -// From RPL model of random loss. -// Input is the received packet loss (up to 50%), and FEC code parameters (up to 24x24): -// i.e., averageFECRecoveryRS[k] where k= code_i*129 + loss_j; -// code_i=1x1,2x1,2x2,..24x24, loss_j=0,1,..128. -const unsigned char VCMAvgFECRecoveryXOR[38700] = { -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -30, -30, -31, -32, -33, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -40, -40, -41, -41, -42, -43, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -52, -53, -53, -54, -54, -54, -55, -55, -55, -56, -56, -56, -57, -57, -57, -58, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -60, -61, -61, -61, -61, -61, -62, -62, -62, -62, -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -14, -14, -15, -16, -16, -17, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -34, -34, -35, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -32, -32, -31, -31, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -39, -40, -41, -41, -42, -43, -43, -44, -45, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -57, -57, -58, -58, -58, -59, -59, -59, -60, -60, -60, -60, -61, -61, -61, -61, -62, -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, -63, -63, -63, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -23, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -20, -19, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -16, -15, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -41, -41, -41, -42, -42, -43, -43, -43, -44, -44, -44, -45, -45, -45, -45, -46, -46, -46, -46, -47, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -47, -46, -46, -46, -46, -46, -45, -45, -45, -45, -44, -44, -44, -44, -43, -43, -43, -42, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -34, -35, -36, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, -52, -53, -53, -54, -54, -55, -55, -56, -56, -57, -57, -58, -58, -59, -59, -60, -60, -61, -61, -61, -62, -62, -62, -63, -63, -63, -63, -64, -64, -64, -64, -65, -65, -65, -65, -65, -65, -65, -65, -65, -66, -66, -66, -66, -66, -66, -66, -65, -65, -65, -65, -65, -65, -65, -65, -65, -64, -64, -64, -64, -63, -63, -63, -63, -62, -62, -62, -61, -61, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -18, -18, -19, -19, -19, -19, -19, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -17, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -24, -25, -26, -26, -27, -27, -28, -29, -29, -30, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -35, -36, -36, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -36, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -33, -32, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -25, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -48, -49, -49, -50, -50, -50, -51, -51, -51, -51, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -53, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -53, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -52, -51, -51, -51, -51, -50, -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, -47, -46, -46, -45, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -43, -44, -45, -46, -47, -47, -48, -49, -49, -50, -51, -52, -52, -53, -54, -54, -55, -55, -56, -57, -57, -58, -58, -59, -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -64, -64, -64, -65, -65, -65, -65, -66, -66, -66, -66, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -68, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -66, -66, -66, -66, -65, -65, -65, -65, -64, -64, -64, -63, -63, -63, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -10, -10, -11, -11, -12, -12, -12, -13, -13, -14, -14, -14, -14, -15, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -24, -24, -25, -25, -26, -26, -26, -27, -27, -28, -28, -28, -28, -29, -29, -29, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -26, -25, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -39, -40, -40, -41, -41, -41, -42, -42, -42, -43, -43, -43, -43, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -46, -46, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -44, -44, -43, -43, -43, -43, -42, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -39, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -34, -34, -34, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -37, -38, -39, -40, -40, -41, -42, -43, -43, -44, -44, -45, -46, -46, -47, -47, -48, -49, -49, -50, -50, -51, -51, -51, -52, -52, -53, -53, -53, -54, -54, -54, -55, -55, -55, -55, -56, -56, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -56, -56, -55, -55, -55, -55, -55, -54, -54, -54, -53, -53, -53, -52, -52, -52, -51, -51, -50, -50, -49, -49, -49, -48, -48, -47, -47, -46, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -52, -52, -53, -54, -54, -55, -56, -56, -57, -57, -58, -58, -59, -60, -60, -61, -61, -61, -62, -62, -63, -63, -64, -64, -64, -65, -65, -65, -66, -66, -66, -66, -67, -67, -67, -67, -67, -67, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -67, -67, -67, -67, -67, -67, -66, -66, -66, -66, -65, -65, -65, -64, -64, -64, -63, -63, -0, -0, -1, -2, -3, -4, -5, -5, -6, -7, -7, -8, -8, -9, -9, -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -13, -13, -13, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -19, -19, -20, -20, -21, -21, -22, -22, -22, -23, -23, -24, -24, -24, -25, -25, -25, -25, -26, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -26, -25, -25, -25, -25, -25, -24, -24, -24, -24, -23, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -11, -11, -11, -11, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -37, -37, -37, -37, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -36, -35, -35, -35, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -26, -25, -25, -25, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -48, -48, -48, -49, -49, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -49, -49, -48, -48, -48, -48, -47, -47, -47, -47, -46, -46, -45, -45, -45, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -33, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -48, -49, -50, -50, -51, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -57, -57, -57, -58, -58, -58, -58, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -58, -58, -58, -58, -57, -57, -57, -56, -56, -56, -55, -55, -55, -54, -54, -53, -53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -46, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -50, -50, -51, -52, -53, -53, -54, -55, -55, -56, -57, -57, -58, -58, -59, -60, -60, -61, -61, -62, -62, -63, -63, -63, -64, -64, -64, -65, -65, -65, -66, -66, -66, -66, -66, -66, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -66, -66, -66, -66, -66, -66, -65, -65, -65, -64, -64, -64, -63, -63, -63, -62, -62, -61, -61, -60, -60, -59, -59, -58, -57, -57, -56, -55, -55, -54, -0, -0, -1, -2, -3, -4, -5, -5, -6, -6, -7, -8, -8, -9, -9, -9, -10, -10, -10, -11, -11, -11, -11, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -16, -17, -17, -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -22, -23, -23, -23, -23, -23, -23, -23, -23, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -20, -20, -20, -20, -19, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -32, -32, -32, -32, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -33, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -23, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -23, -23, -24, -25, -26, -27, -27, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -38, -38, -39, -39, -39, -40, -40, -41, -41, -41, -42, -42, -42, -43, -43, -43, -44, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -44, -44, -43, -43, -43, -43, -42, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -38, -38, -38, -37, -37, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -50, -51, -51, -51, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -52, -51, -51, -51, -50, -50, -50, -49, -49, -49, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36, -35, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -39, -40, -40, -41, -42, -43, -44, -45, -45, -46, -47, -48, -48, -49, -50, -50, -51, -52, -52, -53, -54, -54, -55, -55, -56, -56, -57, -57, -58, -58, -59, -59, -59, -60, -60, -60, -61, -61, -61, -61, -62, -62, -62, -62, -62, -62, -62, -62, -63, -63, -63, -63, -62, -62, -62, -62, -62, -62, -62, -62, -61, -61, -61, -61, -60, -60, -60, -59, -59, -58, -58, -58, -57, -57, -56, -56, -55, -55, -54, -54, -53, -52, -52, -51, -51, -50, -49, -49, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -51, -52, -52, -53, -54, -55, -55, -56, -57, -57, -58, -59, -59, -60, -61, -61, -62, -62, -63, -63, -64, -64, -65, -65, -65, -66, -66, -66, -67, -67, -67, -68, -68, -68, -68, -68, -68, -69, -69, -69, -69, -69, -69, -69, -69, -68, -68, -68, -68, -68, -68, -67, -67, -67, -67, -66, -66, -65, -65, -65, -64, -64, -63, -63, -62, -62, -61, -61, -60, -59, -59, -58, -57, -57, -56, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -7, -7, -8, -8, -8, -9, -9, -9, -10, -10, -10, -10, -10, -10, -10, -10, -10, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -11, -11, -12, -13, -13, -14, -14, -15, -15, -16, -16, -17, -17, -18, -18, -18, -19, -19, -19, -19, -20, -20, -20, -20, -20, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, -20, -20, -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, -18, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -24, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -28, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -29, -30, -31, -31, -32, -32, -33, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -40, -40, -40, -40, -40, -40, -40, -40, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -20, -19, -19, -18, -18, -17, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -39, -40, -40, -41, -41, -42, -43, -43, -43, -44, -44, -45, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -46, -46, -46, -46, -45, -45, -45, -44, -44, -44, -43, -43, -42, -42, -41, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -27, -26, -25, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -35, -36, -36, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -45, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -53, -53, -53, -54, -54, -54, -55, -55, -55, -55, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -55, -55, -55, -55, -54, -54, -54, -54, -53, -53, -53, -52, -52, -51, -51, -50, -50, -50, -49, -49, -48, -48, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -41, -41, -40, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -39, -40, -41, -42, -42, -43, -44, -45, -46, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -53, -54, -55, -55, -56, -56, -57, -57, -58, -58, -59, -59, -60, -60, -60, -61, -61, -61, -62, -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -62, -62, -62, -62, -61, -61, -61, -60, -60, -60, -59, -59, -58, -58, -57, -57, -56, -56, -55, -55, -54, -54, -53, -52, -52, -51, -50, -50, -49, -48, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -55, -56, -56, -57, -58, -59, -59, -60, -61, -61, -62, -63, -63, -64, -65, -65, -66, -66, -67, -67, -68, -68, -68, -69, -69, -69, -70, -70, -70, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -70, -70, -70, -69, -69, -69, -68, -68, -67, -67, -66, -66, -65, -65, -64, -64, -63, -62, -62, -61, -60, -59, -59, -58, -57, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -6, -7, -7, -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -16, -17, -17, -17, -17, -18, -18, -18, -18, -18, -18, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -14, -14, -15, -16, -16, -17, -18, -18, -19, -20, -20, -21, -21, -22, -22, -23, -23, -24, -24, -24, -25, -25, -25, -26, -26, -26, -26, -27, -27, -27, -27, -27, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -25, -25, -25, -25, -24, -24, -24, -24, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -11, -11, -11, -11, -10, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -34, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -16, -15, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -41, -41, -42, -42, -42, -42, -43, -43, -43, -43, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -43, -43, -43, -43, -43, -42, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -37, -36, -36, -35, -35, -34, -34, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -43, -44, -44, -45, -45, -46, -47, -47, -48, -48, -48, -49, -49, -50, -50, -50, -51, -51, -51, -51, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -51, -51, -51, -51, -50, -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -33, -32, -31, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -38, -39, -40, -40, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -49, -49, -50, -50, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -57, -57, -57, -58, -58, -58, -58, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, -58, -58, -58, -58, -57, -57, -57, -56, -56, -56, -55, -55, -54, -54, -53, -53, -52, -52, -51, -51, -50, -49, -49, -48, -48, -47, -46, -46, -45, -44, -44, -43, -42, -42, -41, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59, -59, -60, -60, -61, -61, -62, -62, -63, -63, -63, -64, -64, -64, -64, -65, -65, -65, -65, -65, -66, -66, -66, -66, -66, -66, -66, -66, -66, -65, -65, -65, -65, -65, -65, -64, -64, -64, -63, -63, -63, -62, -62, -61, -61, -60, -60, -59, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -52, -51, -50, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -50, -51, -51, -52, -53, -54, -55, -55, -56, -57, -58, -58, -59, -60, -61, -61, -62, -62, -63, -64, -64, -65, -65, -66, -66, -67, -67, -68, -68, -68, -69, -69, -69, -70, -70, -70, -70, -70, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, -70, -70, -70, -70, -70, -69, -69, -69, -68, -68, -67, -67, -66, -66, -65, -65, -64, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -0, -0, -1, -2, -3, -4, -4, -5, -5, -6, -6, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -14, -14, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -22, -23, -23, -23, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -24, -23, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -27, -28, -28, -29, -29, -29, -30, -30, -30, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26, -25, -25, -25, -24, -24, -23, -23, -22, -22, -22, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -16, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -39, -39, -39, -39, -40, -40, -40, -40, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, -40, -40, -40, -40, -40, -40, -39, -39, -39, -38, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -34, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -40, -40, -41, -41, -42, -42, -43, -43, -44, -44, -45, -45, -45, -46, -46, -46, -46, -47, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -40, -39, -39, -39, -38, -38, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -29, -29, -28, -28, -27, -27, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -43, -44, -45, -45, -46, -47, -47, -48, -48, -49, -49, -50, -50, -50, -51, -51, -52, -52, -52, -53, -53, -53, -53, -53, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, -53, -53, -53, -53, -52, -52, -52, -51, -51, -51, -50, -50, -50, -49, -49, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36, -35, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -45, -46, -47, -48, -48, -49, -50, -50, -51, -52, -52, -53, -53, -54, -55, -55, -56, -56, -57, -57, -57, -58, -58, -59, -59, -59, -60, -60, -60, -60, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -61, -60, -60, -60, -60, -59, -59, -59, -58, -58, -57, -57, -57, -56, -56, -55, -55, -54, -53, -53, -52, -52, -51, -51, -50, -49, -49, -48, -47, -47, -46, -45, -44, -44, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -51, -52, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59, -59, -60, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -63, -63, -63, -64, -64, -64, -64, -64, -64, -63, -63, -63, -63, -63, -62, -62, -62, -62, -61, -61, -60, -60, -59, -59, -58, -58, -57, -57, -56, -55, -55, -54, -53, -53, -52, -51, -50, -50, -49, -48, -47, -46, -45, -44, -44, -43, -42, -41, -40, -39, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -51, -52, -53, -54, -54, -55, -56, -57, -58, -59, -59, -60, -61, -62, -62, -63, -64, -64, -65, -65, -66, -67, -67, -68, -68, -69, -69, -70, -70, -71, -71, -71, -72, -72, -72, -72, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, -72, -72, -72, -72, -71, -71, -70, -70, -70, -69, -69, -68, -68, -67, -66, -66, -65, -64, -64, -63, -62, -61, -61, -60, -59, -58, -0, -0, -1, -2, -3, -4, -4, -5, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -22, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -18, -18, -19, -20, -21, -21, -22, -23, -23, -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -28, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, -22, -22, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -16, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -34, -34, -34, -35, -35, -36, -36, -36, -36, -37, -37, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -37, -36, -36, -36, -36, -35, -35, -35, -35, -34, -34, -34, -33, -33, -32, -32, -32, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -17, -16, -16, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -44, -44, -44, -44, -44, -44, -45, -45, -45, -45, -45, -44, -44, -44, -44, -44, -44, -44, -44, -43, -43, -43, -42, -42, -42, -42, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -47, -48, -48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -49, -48, -48, -48, -47, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -39, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -53, -53, -54, -54, -54, -55, -55, -55, -56, -56, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -56, -56, -56, -55, -55, -55, -54, -54, -54, -53, -53, -53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -43, -42, -42, -41, -40, -40, -39, -38, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -44, -45, -45, -46, -47, -48, -49, -49, -50, -51, -52, -52, -53, -54, -54, -55, -55, -56, -56, -57, -57, -58, -58, -59, -59, -59, -60, -60, -60, -61, -61, -61, -61, -61, -61, -62, -62, -62, -62, -62, -61, -61, -61, -61, -61, -61, -60, -60, -60, -60, -59, -59, -58, -58, -57, -57, -57, -56, -55, -55, -54, -54, -53, -52, -52, -51, -50, -50, -49, -48, -47, -47, -46, -45, -44, -44, -43, -42, -41, -40, -39, -39, -38, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -46, -47, -48, -49, -50, -50, -51, -52, -53, -54, -54, -55, -56, -57, -57, -58, -59, -59, -60, -61, -61, -62, -62, -63, -63, -64, -64, -65, -65, -65, -66, -66, -66, -67, -67, -67, -67, -67, -68, -68, -68, -68, -68, -68, -68, -67, -67, -67, -67, -67, -67, -66, -66, -66, -65, -65, -64, -64, -64, -63, -63, -62, -61, -61, -60, -60, -59, -58, -57, -57, -56, -55, -54, -54, -53, -52, -51, -50, -49, -49, -48, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -51, -52, -53, -54, -55, -55, -56, -57, -58, -59, -59, -60, -61, -62, -62, -63, -64, -64, -65, -66, -66, -67, -67, -68, -68, -69, -69, -70, -70, -70, -71, -71, -71, -72, -72, -72, -72, -72, -72, -73, -73, -73, -73, -73, -72, -72, -72, -72, -72, -72, -71, -71, -71, -70, -70, -70, -69, -69, -68, -68, -67, -66, -66, -65, -65, -64, -63, -62, -62, -61, -60, -59, -58, -58, -57, -56, -0, -0, -1, -2, -3, -3, -4, -5, -5, -5, -6, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -9, -10, -10, -11, -11, -12, -12, -12, -13, -13, -13, -13, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -17, -18, -18, -18, -19, -19, -20, -20, -20, -20, -21, -21, -21, -21, -21, -21, -21, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -21, -21, -21, -20, -20, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -22, -23, -23, -24, -24, -25, -25, -26, -26, -26, -27, -27, -27, -28, -28, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -27, -28, -29, -29, -30, -30, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -20, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -32, -32, -33, -33, -34, -35, -35, -36, -36, -37, -37, -38, -38, -38, -39, -39, -40, -40, -40, -40, -41, -41, -41, -41, -41, -41, -41, -41, -42, -42, -42, -41, -41, -41, -41, -41, -41, -41, -41, -40, -40, -40, -40, -39, -39, -39, -39, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -47, -47, -47, -48, -48, -48, -47, -47, -47, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -45, -44, -44, -44, -43, -43, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -36, -35, -35, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -23, -22, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -43, -44, -44, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -50, -51, -51, -51, -52, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -52, -51, -51, -51, -51, -50, -50, -50, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -37, -36, -35, -35, -34, -34, -33, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -36, -37, -38, -39, -40, -41, -42, -42, -43, -44, -45, -45, -46, -47, -48, -48, -49, -50, -50, -51, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -56, -57, -57, -57, -57, -57, -58, -58, -58, -58, -58, -58, -58, -58, -57, -57, -57, -57, -57, -56, -56, -56, -55, -55, -55, -54, -54, -53, -53, -52, -52, -51, -51, -50, -50, -49, -48, -48, -47, -46, -46, -45, -44, -43, -43, -42, -41, -40, -40, -39, -38, -37, -37, -36, -35, -34, -34, -33, -32, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -51, -51, -52, -53, -53, -54, -55, -55, -56, -57, -57, -58, -58, -59, -59, -60, -60, -60, -61, -61, -62, -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -62, -62, -62, -61, -61, -61, -60, -60, -60, -59, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -52, -51, -50, -49, -49, -48, -47, -46, -45, -45, -44, -43, -42, -41, -40, -39, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -45, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -55, -56, -56, -57, -58, -58, -59, -60, -60, -61, -62, -62, -63, -63, -64, -64, -65, -65, -66, -66, -67, -67, -67, -68, -68, -68, -68, -68, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -68, -68, -68, -68, -67, -67, -67, -66, -66, -65, -65, -64, -64, -63, -63, -62, -61, -61, -60, -59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -50, -49, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -51, -52, -53, -54, -54, -55, -56, -57, -58, -58, -59, -60, -61, -62, -62, -63, -64, -64, -65, -66, -66, -67, -67, -68, -68, -69, -69, -70, -70, -71, -71, -72, -72, -72, -73, -73, -73, -73, -73, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -73, -73, -73, -73, -73, -72, -72, -72, -71, -71, -70, -70, -69, -69, -68, -67, -67, -66, -66, -65, -64, -63, -63, -62, -61, -60, -59, -0, -0, -1, -2, -3, -3, -4, -4, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -5, -6, -7, -7, -8, -9, -9, -9, -10, -10, -11, -11, -11, -12, -12, -12, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -14, -14, -15, -15, -16, -16, -17, -17, -17, -18, -18, -18, -19, -19, -19, -19, -19, -19, -20, -20, -20, -20, -20, -20, -20, -20, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -17, -17, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -23, -24, -24, -25, -25, -25, -25, -26, -26, -26, -26, -26, -26, -26, -26, -27, -27, -26, -26, -26, -26, -26, -26, -26, -26, -26, -25, -25, -25, -25, -24, -24, -24, -23, -23, -23, -23, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -31, -31, -31, -30, -30, -30, -30, -29, -29, -28, -28, -28, -27, -27, -26, -26, -26, -25, -25, -24, -24, -23, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -36, -36, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -39, -39, -39, -39, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -42, -43, -43, -43, -43, -44, -44, -44, -44, -44, -44, -44, -44, -44, -43, -43, -43, -43, -43, -42, -42, -42, -41, -41, -41, -40, -40, -39, -39, -39, -38, -38, -37, -36, -36, -35, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -39, -39, -40, -41, -41, -42, -43, -43, -44, -45, -45, -46, -46, -47, -47, -48, -48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -50, -50, -50, -50, -50, -49, -49, -49, -49, -48, -48, -47, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -41, -40, -39, -39, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -43, -44, -45, -45, -46, -47, -47, -48, -49, -49, -50, -50, -51, -51, -52, -52, -52, -53, -53, -53, -54, -54, -54, -54, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -54, -54, -54, -54, -53, -53, -53, -52, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -46, -46, -45, -44, -44, -43, -42, -42, -41, -40, -40, -39, -38, -37, -37, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -28, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -36, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -46, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -53, -54, -54, -55, -55, -56, -56, -57, -57, -58, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, -59, -59, -59, -59, -58, -58, -58, -57, -57, -56, -56, -55, -55, -54, -54, -53, -53, -52, -51, -51, -50, -49, -49, -48, -47, -47, -46, -45, -44, -43, -43, -42, -41, -40, -39, -39, -38, -37, -36, -35, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -51, -52, -52, -53, -54, -54, -55, -56, -57, -57, -58, -58, -59, -60, -60, -61, -61, -62, -62, -62, -63, -63, -63, -64, -64, -64, -64, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -64, -64, -64, -64, -63, -63, -63, -62, -62, -61, -61, -60, -60, -59, -59, -58, -57, -57, -56, -55, -55, -54, -53, -52, -52, -51, -50, -49, -48, -47, -47, -46, -45, -44, -43, -42, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -51, -52, -53, -54, -54, -55, -56, -57, -58, -58, -59, -60, -61, -61, -62, -63, -63, -64, -64, -65, -65, -66, -66, -67, -67, -68, -68, -69, -69, -69, -69, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -69, -69, -69, -68, -68, -68, -67, -67, -66, -66, -65, -65, -64, -63, -63, -62, -61, -60, -60, -59, -58, -57, -56, -55, -55, -54, -53, -52, -51, -50, -49, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -51, -52, -53, -54, -55, -56, -57, -58, -58, -59, -60, -61, -62, -62, -63, -64, -64, -65, -66, -66, -67, -68, -68, -69, -69, -70, -70, -71, -71, -72, -72, -72, -73, -73, -73, -73, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -73, -73, -73, -73, -72, -72, -72, -71, -71, -70, -70, -69, -69, -68, -67, -67, -66, -65, -64, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -0, -0, -1, -2, -3, -3, -4, -4, -5, -5, -5, -5, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -9, -10, -10, -10, -11, -11, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -17, -17, -17, -17, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -19, -19, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -23, -23, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -16, -16, -16, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -26, -27, -27, -28, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -27, -27, -27, -27, -26, -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -25, -25, -26, -27, -28, -28, -29, -29, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, -35, -35, -34, -34, -34, -33, -33, -33, -32, -32, -31, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, -36, -36, -37, -37, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -36, -37, -38, -38, -39, -40, -40, -41, -41, -42, -43, -43, -44, -44, -45, -45, -45, -46, -46, -46, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -46, -46, -46, -45, -45, -45, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -23, -22, -21, -21, -20, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -49, -49, -50, -50, -50, -50, -50, -51, -51, -51, -51, -51, -51, -50, -50, -50, -50, -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -39, -40, -40, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -48, -49, -50, -50, -51, -51, -52, -52, -53, -53, -53, -54, -54, -55, -55, -55, -55, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -55, -55, -55, -55, -54, -54, -54, -53, -53, -53, -52, -52, -51, -51, -50, -50, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -32, -31, -30, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -51, -52, -53, -53, -54, -54, -55, -55, -55, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -55, -55, -55, -54, -54, -53, -53, -52, -52, -51, -50, -50, -49, -48, -48, -47, -46, -45, -44, -44, -43, -42, -41, -40, -39, -39, -38, -37, -36, -35, -34, -33, -32, -32, -31, -30, -29, -28, -27, -27, -26, -25, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -45, -46, -47, -48, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -57, -57, -58, -59, -59, -60, -60, -61, -61, -62, -62, -63, -63, -64, -64, -64, -65, -65, -65, -65, -65, -65, -66, -66, -66, -66, -66, -65, -65, -65, -65, -65, -65, -64, -64, -64, -63, -63, -62, -62, -61, -61, -60, -59, -59, -58, -57, -57, -56, -55, -54, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -45, -44, -43, -42, -41, -40, -39, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -50, -51, -52, -53, -53, -54, -55, -56, -57, -58, -58, -59, -60, -60, -61, -62, -63, -63, -64, -64, -65, -66, -66, -67, -67, -67, -68, -68, -69, -69, -69, -70, -70, -70, -70, -70, -70, -71, -71, -71, -71, -70, -70, -70, -70, -70, -70, -69, -69, -69, -68, -68, -67, -67, -66, -66, -65, -65, -64, -63, -63, -62, -61, -60, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -51, -52, -53, -54, -55, -56, -57, -58, -58, -59, -60, -61, -62, -62, -63, -64, -65, -65, -66, -67, -67, -68, -68, -69, -70, -70, -71, -71, -72, -72, -72, -73, -73, -73, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -73, -73, -73, -72, -72, -72, -71, -71, -70, -69, -69, -68, -67, -67, -66, -65, -64, -63, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -0, -0, -1, -2, -3, -3, -4, -4, -4, -5, -5, -5, -5, -5, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -5, -6, -7, -7, -8, -8, -9, -9, -10, -10, -10, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -18, -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -21, -22, -22, -22, -22, -22, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -19, -20, -21, -21, -22, -23, -23, -24, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -23, -23, -24, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -31, -32, -32, -32, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -35, -35, -36, -36, -37, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -22, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -39, -40, -40, -41, -41, -42, -42, -43, -43, -43, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -44, -43, -43, -43, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -32, -32, -31, -31, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -21, -20, -19, -19, -18, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -48, -49, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -22, -22, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -44, -44, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -50, -51, -51, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -51, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -23, -22, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -57, -57, -57, -58, -58, -58, -58, -58, -59, -59, -59, -59, -59, -59, -59, -59, -59, -58, -58, -58, -58, -57, -57, -57, -56, -56, -56, -55, -55, -54, -54, -53, -52, -52, -51, -51, -50, -49, -48, -48, -47, -46, -45, -45, -44, -43, -42, -41, -41, -40, -39, -38, -37, -36, -36, -35, -34, -33, -32, -31, -30, -30, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -52, -53, -53, -54, -54, -55, -55, -56, -56, -57, -57, -58, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -60, -60, -60, -60, -60, -59, -59, -59, -59, -58, -58, -58, -57, -57, -56, -56, -55, -55, -54, -54, -53, -52, -52, -51, -50, -49, -49, -48, -47, -46, -45, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -55, -55, -56, -57, -57, -58, -59, -59, -60, -61, -61, -62, -62, -63, -63, -63, -64, -64, -64, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -65, -64, -64, -64, -63, -63, -63, -62, -62, -61, -60, -60, -59, -58, -58, -57, -56, -55, -55, -54, -53, -52, -51, -50, -49, -48, -47, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -53, -54, -55, -55, -56, -57, -58, -59, -59, -60, -61, -62, -62, -63, -64, -64, -65, -65, -66, -66, -67, -67, -68, -68, -69, -69, -69, -69, -70, -70, -70, -70, -70, -71, -71, -71, -71, -71, -70, -70, -70, -70, -70, -69, -69, -69, -68, -68, -68, -67, -67, -66, -65, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -52, -51, -50, -49, -48, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -54, -55, -56, -57, -58, -59, -60, -60, -61, -62, -63, -64, -64, -65, -66, -66, -67, -68, -68, -69, -69, -70, -70, -71, -71, -72, -72, -73, -73, -73, -73, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -73, -73, -73, -72, -72, -71, -71, -70, -70, -69, -69, -68, -67, -67, -66, -65, -64, -63, -62, -61, -61, -60, -59, -58, -57, -56, -54, -53, -52, -51, -0, -0, -1, -2, -3, -3, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -5, -6, -7, -7, -8, -8, -9, -9, -9, -10, -10, -10, -10, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -17, -17, -17, -17, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, -17, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -22, -23, -23, -24, -24, -25, -25, -25, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -26, -26, -25, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -23, -23, -24, -25, -26, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -34, -34, -34, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -36, -37, -37, -36, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, -35, -34, -34, -34, -33, -33, -33, -32, -32, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, -36, -37, -37, -38, -38, -38, -39, -39, -40, -40, -40, -40, -41, -41, -41, -41, -41, -41, -42, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -41, -41, -40, -40, -40, -40, -39, -39, -39, -38, -38, -37, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -19, -19, -18, -18, -17, -17, -17, -16, -16, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -39, -40, -41, -41, -42, -42, -43, -43, -44, -44, -45, -45, -45, -45, -46, -46, -46, -46, -46, -46, -46, -47, -46, -46, -46, -46, -46, -46, -46, -45, -45, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -44, -44, -45, -46, -46, -47, -47, -48, -48, -49, -49, -49, -50, -50, -50, -50, -51, -51, -51, -51, -51, -51, -51, -51, -51, -50, -50, -50, -50, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -44, -44, -43, -42, -42, -41, -40, -40, -39, -38, -37, -37, -36, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -15, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -42, -42, -43, -44, -45, -45, -46, -47, -47, -48, -48, -49, -49, -50, -50, -51, -51, -51, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -52, -51, -51, -50, -50, -50, -49, -49, -48, -47, -47, -46, -46, -45, -44, -43, -43, -42, -41, -40, -40, -39, -38, -37, -36, -35, -35, -34, -33, -32, -31, -30, -29, -29, -28, -27, -26, -25, -24, -24, -23, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, -52, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -56, -55, -55, -54, -54, -53, -53, -52, -52, -51, -50, -50, -49, -48, -48, -47, -46, -45, -44, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -25, -24, -23, -22, -21, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -56, -57, -57, -58, -58, -59, -59, -60, -60, -60, -61, -61, -61, -61, -61, -62, -62, -62, -62, -62, -62, -61, -61, -61, -61, -61, -60, -60, -60, -59, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -31, -30, -29, -28, -27, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -53, -54, -55, -55, -56, -57, -58, -58, -59, -60, -60, -61, -62, -62, -63, -63, -64, -64, -65, -65, -65, -66, -66, -66, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, -66, -66, -66, -65, -65, -65, -64, -64, -63, -62, -62, -61, -61, -60, -59, -58, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -0, -1, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -55, -56, -57, -57, -58, -59, -60, -61, -61, -62, -63, -63, -64, -65, -65, -66, -66, -67, -67, -68, -68, -68, -69, -69, -69, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, -69, -69, -69, -68, -68, -68, -67, -67, -66, -66, -65, -64, -64, -63, -62, -61, -60, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -45, -44, -43, -42, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, -59, -60, -61, -62, -62, -63, -64, -65, -65, -66, -67, -67, -68, -69, -69, -70, -70, -71, -71, -72, -72, -73, -73, -73, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -73, -73, -73, -72, -72, -71, -71, -70, -69, -69, -68, -67, -66, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -54, -53, -52, -51, -50, -49, -0, -0, -1, -2, -3, -3, -4, -4, -4, -4, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -5, -6, -6, -7, -7, -8, -8, -9, -9, -9, -9, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -13, -14, -14, -15, -16, -16, -17, -17, -17, -18, -18, -18, -18, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -22, -23, -23, -23, -24, -24, -24, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -27, -27, -27, -28, -28, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -23, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -34, -34, -35, -35, -35, -35, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -35, -35, -35, -35, -34, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -34, -35, -36, -36, -37, -38, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -43, -43, -44, -44, -44, -43, -43, -43, -43, -43, -43, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -45, -45, -45, -44, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -29, -28, -27, -26, -25, -24, -24, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -43, -43, -44, -45, -46, -46, -47, -47, -48, -49, -49, -50, -50, -50, -51, -51, -52, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -51, -51, -51, -50, -50, -49, -49, -48, -48, -47, -46, -46, -45, -44, -44, -43, -42, -41, -40, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -30, -29, -28, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -15, -15, -14, -13, -13, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -16, -17, -18, -18, -19, -20, -20, -21, -21, -22, -23, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -45, -46, -47, -47, -48, -49, -49, -50, -50, -50, -51, -51, -51, -51, -52, -52, -52, -52, -52, -51, -51, -51, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -42, -41, -41, -40, -39, -38, -37, -36, -35, -34, -33, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -15, -15, -14, -13, -0, -0, -2, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -53, -54, -55, -55, -56, -56, -56, -57, -57, -57, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, -57, -57, -57, -56, -56, -55, -55, -54, -53, -53, -52, -51, -51, -50, -49, -48, -47, -46, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -17, -0, -0, -2, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -52, -53, -54, -54, -55, -55, -56, -57, -57, -57, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -60, -60, -60, -60, -60, -59, -59, -59, -59, -58, -58, -57, -57, -56, -56, -55, -55, -54, -53, -53, -52, -51, -50, -49, -48, -47, -47, -46, -45, -44, -43, -42, -41, -40, -39, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -0, -1, -2, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -52, -53, -54, -54, -55, -56, -56, -57, -57, -58, -58, -58, -59, -59, -59, -59, -60, -60, -60, -60, -60, -60, -60, -60, -60, -59, -59, -59, -59, -58, -58, -57, -57, -56, -56, -55, -55, -54, -53, -52, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -0, -0, -1, -2, -3, -3, -3, -4, -4, -4, -4, -4, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -7, -7, -8, -8, -8, -9, -9, -9, -9, -9, -9, -10, -10, -10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -14, -14, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -18, -18, -18, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -26, -27, -27, -28, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -28, -27, -27, -27, -27, -26, -26, -25, -25, -25, -24, -24, -23, -23, -22, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -31, -31, -32, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -21, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -41, -41, -41, -41, -41, -41, -42, -42, -42, -41, -41, -41, -41, -41, -41, -40, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -33, -33, -32, -32, -31, -30, -30, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -33, -33, -33, -33, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -41, -41, -41, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -40, -40, -39, -39, -39, -38, -38, -37, -37, -36, -35, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -36, -37, -38, -38, -39, -39, -40, -41, -41, -42, -42, -42, -43, -43, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -37, -37, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -36, -37, -38, -39, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -46, -46, -46, -46, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -47, -46, -46, -46, -46, -45, -45, -45, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -14, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -37, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -39, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -14, -14, -15, -16, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, -52, -52, -52, -53, -53, -53, -53, -53, -53, -53, -52, -52, -52, -52, -52, -51, -51, -50, -50, -50, -49, -49, -48, -47, -47, -46, -46, -45, -44, -43, -43, -42, -41, -40, -40, -39, -38, -37, -36, -35, -34, -34, -33, -32, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -0, -0, -1, -2, -2, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -10, -11, -11, -12, -12, -12, -13, -13, -13, -13, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -13, -14, -14, -15, -15, -16, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -21, -21, -21, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -21, -21, -22, -23, -23, -24, -24, -25, -25, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -25, -25, -25, -24, -24, -23, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -24, -24, -25, -26, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -31, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -33, -34, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -41, -41, -41, -41, -41, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -35, -35, -34, -34, -33, -32, -31, -31, -30, -29, -29, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -37, -38, -39, -39, -40, -41, -41, -42, -42, -43, -43, -43, -44, -44, -44, -44, -44, -45, -45, -45, -45, -45, -44, -44, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -29, -28, -27, -26, -25, -25, -24, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -47, -48, -48, -48, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -48, -48, -48, -47, -47, -47, -46, -46, -45, -45, -44, -43, -43, -42, -41, -41, -40, -39, -38, -37, -37, -36, -35, -34, -33, -32, -31, -31, -30, -29, -28, -27, -26, -25, -24, -24, -23, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -47, -47, -48, -48, -48, -49, -49, -49, -49, -49, -50, -50, -50, -50, -49, -49, -49, -49, -49, -48, -48, -48, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -40, -39, -38, -38, -37, -36, -35, -34, -33, -32, -32, -31, -30, -29, -28, -27, -26, -25, -25, -24, -23, -22, -21, -20, -20, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -20, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -52, -52, -53, -53, -54, -54, -54, -55, -55, -55, -55, -55, -55, -55, -55, -55, -54, -54, -54, -54, -53, -53, -52, -52, -51, -50, -50, -49, -48, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -17, -16, -16, -15, -14, -13, -0, -1, -2, -3, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -53, -54, -54, -55, -55, -56, -56, -56, -56, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -57, -56, -56, -56, -55, -55, -54, -54, -53, -53, -52, -51, -50, -50, -49, -48, -47, -46, -45, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -16, -16, -15, -14, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -15, -16, -16, -16, -16, -17, -17, -18, -18, -18, -19, -20, -20, -21, -22, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -33, -34, -35, -36, -37, -38, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -50, -50, -51, -52, -52, -52, -53, -53, -53, -54, -54, -54, -54, -54, -54, -53, -53, -53, -53, -52, -52, -51, -51, -50, -50, -49, -48, -48, -47, -46, -45, -44, -43, -42, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -16, -15, -14, -14, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -19, -20, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, -40, -42, -43, -44, -45, -46, -47, -48, -49, -49, -50, -51, -52, -52, -53, -53, -54, -54, -54, -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, -54, -54, -53, -53, -53, -52, -51, -51, -50, -49, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -18, -17, -16, -15, -14, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -16, -16, -17, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -24, -25, -26, -27, -29, -30, -31, -32, -33, -35, -36, -37, -38, -40, -41, -42, -43, -44, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -54, -55, -56, -56, -57, -57, -57, -57, -58, -58, -58, -58, -58, -58, -58, -57, -57, -57, -56, -56, -56, -55, -55, -54, -53, -53, -52, -51, -50, -49, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -0, -0, -1, -2, -2, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -7, -8, -9, -9, -10, -10, -11, -11, -12, -12, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -12, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -18, -18, -19, -19, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -19, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -18, -19, -20, -20, -21, -22, -22, -23, -23, -24, -24, -25, -25, -25, -26, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -23, -23, -24, -25, -25, -26, -26, -27, -27, -28, -28, -29, -29, -29, -30, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -26, -26, -25, -25, -25, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -24, -25, -26, -26, -27, -27, -27, -28, -28, -28, -29, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -29, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -26, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -23, -24, -24, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -33, -34, -34, -35, -35, -36, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -35, -35, -34, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -17, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -23, -24, -25, -25, -26, -26, -27, -27, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -32, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -26, -27, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, -36, -37, -37, -38, -38, -38, -39, -39, -39, -39, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -39, -39, -39, -39, -38, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -17, -18, -19, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -32, -32, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -40, -40, -41, -41, -41, -41, -42, -42, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -40, -40, -39, -39, -39, -38, -38, -37, -36, -36, -35, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -14, -14, -15, -15, -16, -16, -16, -17, -17, -18, -18, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -24, -25, -26, -26, -27, -28, -29, -30, -30, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -32, -32, -31, -30, -29, -29, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -14, -14, -15, -15, -16, -17, -17, -18, -18, -19, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -47, -47, -47, -47, -46, -46, -45, -45, -45, -44, -43, -43, -42, -42, -41, -40, -40, -39, -38, -37, -37, -36, -35, -34, -33, -32, -32, -31, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -13, -13, -14, -15, -15, -16, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -24, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -32, -33, -34, -35, -35, -36, -37, -37, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -42, -42, -42, -41, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -36, -35, -34, -34, -33, -32, -32, -31, -30, -29, -29, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -20, -21, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -47, -48, -48, -48, -48, -48, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -44, -44, -43, -43, -42, -41, -41, -40, -39, -39, -38, -37, -36, -36, -35, -34, -33, -32, -32, -31, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -17, -18, -19, -19, -20, -21, -22, -23, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -36, -37, -38, -38, -39, -40, -40, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -45, -45, -45, -45, -44, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -11, -12, -12, -12, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -14, -14, -15, -15, -16, -16, -17, -18, -19, -20, -21, -21, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -40, -40, -39, -39, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -28, -28, -27, -26, -25, -24, -23, -23, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -0, -0, -1, -2, -2, -3, -3, -3, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -6, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -10, -10, -10, -11, -11, -11, -12, -12, -12, -12, -12, -12, -13, -13, -12, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -14, -14, -15, -15, -16, -16, -17, -17, -18, -18, -18, -18, -19, -19, -19, -19, -19, -19, -19, -19, -19, -18, -18, -18, -18, -17, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -22, -23, -23, -24, -24, -24, -24, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, -24, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -20, -20, -20, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -28, -28, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -28, -28, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -23, -24, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -22, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -41, -41, -41, -41, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -36, -36, -37, -37, -37, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -37, -37, -36, -36, -35, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -33, -34, -35, -36, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -41, -41, -41, -41, -41, -42, -42, -42, -41, -41, -41, -41, -41, -41, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -35, -35, -34, -33, -33, -32, -31, -30, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -33, -34, -35, -36, -37, -37, -38, -39, -40, -40, -41, -41, -42, -42, -43, -43, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -14, -15, -15, -15, -16, -16, -16, -16, -17, -17, -17, -18, -18, -18, -19, -19, -20, -20, -21, -21, -22, -23, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -32, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -39, -39, -38, -38, -37, -37, -36, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -18, -18, -19, -20, -20, -21, -22, -22, -23, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -44, -45, -46, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -50, -50, -50, -49, -49, -48, -47, -47, -46, -45, -45, -44, -43, -42, -41, -40, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -16, -16, -15, -14, -13, -13, -12, -12, -11, -10, -10, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -26, -27, -28, -29, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -45, -46, -47, -47, -48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -48, -48, -47, -47, -46, -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -17, -16, -16, -15, -14, -13, -13, -12, -12, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -16, -16, -17, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -24, -25, -26, -27, -28, -30, -31, -32, -33, -34, -35, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -48, -48, -49, -50, -50, -51, -51, -51, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -51, -51, -51, -50, -50, -49, -49, -48, -47, -47, -46, -45, -44, -43, -42, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -16, -15, -14, -13, -13, -12, -11, -11, -10, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -15, -16, -17, -17, -17, -18, -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -23, -24, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -33, -34, -35, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -46, -47, -48, -49, -49, -50, -50, -51, -51, -51, -52, -52, -52, -52, -52, -52, -52, -52, -51, -51, -51, -50, -50, -49, -48, -48, -47, -46, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -16, -15, -14, -13, -13, -12, -11, -11, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -14, -14, -14, -15, -15, -15, -16, -16, -17, -17, -18, -19, -19, -20, -21, -22, -23, -24, -25, -26, -27, -29, -30, -31, -32, -34, -35, -36, -37, -39, -40, -41, -42, -43, -45, -46, -47, -48, -49, -49, -50, -51, -52, -53, -53, -54, -54, -55, -55, -55, -56, -56, -56, -56, -56, -56, -56, -56, -55, -55, -55, -54, -54, -54, -53, -52, -52, -51, -50, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -16, -15, -14, -13, -13, -12, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -9, -9, -10, -10, -11, -12, -13, -14, -15, -16, -17, -18, -20, -21, -23, -24, -26, -27, -29, -30, -32, -33, -35, -36, -38, -39, -40, -42, -43, -44, -46, -47, -48, -49, -50, -51, -52, -52, -53, -54, -54, -55, -55, -55, -56, -56, -56, -56, -56, -56, -56, -56, -55, -55, -54, -54, -54, -53, -52, -52, -51, -50, -49, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, -29, -28, -27, -25, -24, -23, -22, -21, -20, -19, -19, -18, -17, -16, -15, -14, -13, -13, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -8, -8, -8, -8, -9, -9, -10, -11, -11, -12, -13, -14, -15, -17, -18, -19, -21, -22, -24, -25, -27, -28, -30, -31, -33, -35, -36, -38, -39, -41, -42, -43, -45, -46, -47, -48, -49, -51, -52, -52, -53, -54, -55, -56, -56, -57, -57, -58, -58, -58, -59, -59, -59, -59, -59, -59, -59, -58, -58, -58, -58, -57, -57, -56, -55, -55, -54, -53, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -0, -0, -1, -2, -2, -3, -3, -3, -3, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -5, -6, -6, -7, -7, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -9, -10, -10, -11, -11, -11, -11, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -10, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -15, -15, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -15, -15, -15, -15, -15, -14, -14, -14, -13, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -22, -23, -23, -23, -23, -23, -23, -24, -24, -24, -24, -24, -23, -23, -23, -23, -23, -23, -22, -22, -22, -22, -21, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -17, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -21, -22, -23, -23, -24, -24, -25, -25, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -18, -19, -20, -21, -21, -22, -22, -23, -23, -24, -24, -25, -25, -26, -26, -26, -26, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, -26, -26, -26, -26, -25, -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -25, -26, -27, -28, -29, -29, -30, -31, -31, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -36, -36, -37, -37, -37, -36, -36, -36, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -31, -30, -29, -29, -28, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -36, -36, -37, -37, -37, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -37, -37, -36, -36, -36, -35, -34, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -23, -23, -24, -25, -26, -27, -27, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -38, -38, -37, -37, -37, -36, -36, -35, -35, -35, -34, -34, -33, -32, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -19, -20, -21, -22, -23, -23, -24, -25, -26, -26, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -13, -13, -14, -14, -14, -14, -15, -15, -15, -15, -15, -15, -16, -16, -16, -16, -17, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -14, -14, -14, -14, -14, -15, -15, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -19, -20, -20, -21, -22, -22, -23, -24, -25, -25, -26, -27, -28, -28, -29, -30, -31, -31, -32, -32, -33, -34, -34, -35, -35, -35, -36, -36, -36, -36, -36, -37, -37, -37, -36, -36, -36, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -15, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -18, -18, -19, -19, -20, -21, -22, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -35, -36, -37, -38, -39, -39, -40, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -43, -43, -43, -42, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -30, -29, -29, -28, -27, -26, -25, -24, -24, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -12, -13, -13, -13, -13, -14, -14, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -18, -18, -19, -20, -20, -21, -22, -23, -24, -24, -25, -26, -27, -28, -29, -29, -30, -31, -32, -33, -33, -34, -35, -35, -36, -37, -37, -38, -38, -39, -39, -39, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -39, -39, -39, -38, -38, -37, -37, -36, -36, -35, -35, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -12, -12, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -14, -14, -14, -15, -15, -16, -17, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -28, -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, -40, -40, -41, -42, -43, -43, -44, -44, -45, -45, -45, -46, -46, -46, -46, -46, -46, -46, -45, -45, -45, -44, -44, -43, -43, -42, -42, -41, -40, -40, -39, -38, -38, -37, -36, -35, -34, -34, -33, -32, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -9, -9, -9, -10, -10, -11, -11, -12, -13, -14, -14, -15, -16, -17, -18, -20, -21, -22, -23, -24, -25, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -38, -39, -40, -40, -41, -41, -42, -42, -43, -43, -43, -43, -43, -43, -44, -43, -43, -43, -43, -43, -43, -42, -42, -42, -41, -41, -40, -40, -39, -39, -38, -37, -37, -36, -35, -35, -34, -33, -32, -32, -31, -30, -29, -28, -28, -27, -26, -25, -24, -24, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -8, -8, -8, -9, -9, -10, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -21, -22, -23, -24, -26, -27, -28, -30, -31, -32, -33, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -43, -44, -45, -45, -45, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, -45, -45, -44, -44, -44, -43, -42, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -34, -34, -33, -32, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, -12, -12, -11, -10, -10, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -9, -10, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -23, -24, -25, -26, -27, -28, -30, -31, -32, -33, -34, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -42, -43, -43, -44, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -31, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -22, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -9, -10, -10, -11, -12, -13, -14, -15, -16, -18, -19, -20, -22, -23, -25, -26, -28, -29, -30, -32, -33, -35, -36, -37, -39, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -49, -50, -50, -50, -51, -51, -51, -51, -51, -52, -51, -51, -51, -51, -51, -51, -50, -50, -50, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -42, -42, -41, -40, -39, -38, -37, -36, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -25, -24, -23, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -0, -0, -1, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -5, -6, -6, -7, -7, -7, -7, -7, -7, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -9, -10, -10, -10, -11, -11, -11, -11, -11, -12, -12, -12, -12, -12, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -16, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -15, -16, -16, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, -16, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -16, -16, -17, -17, -18, -19, -19, -20, -20, -20, -21, -21, -21, -22, -22, -22, -22, -22, -22, -22, -23, -23, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -16, -16, -16, -15, -15, -14, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, -20, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -27, -27, -28, -28, -28, -28, -28, -29, -29, -29, -29, -28, -28, -28, -28, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -21, -22, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -29, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -23, -24, -25, -26, -27, -27, -28, -29, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -26, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -23, -24, -24, -24, -25, -25, -26, -26, -27, -27, -28, -28, -28, -29, -29, -29, -29, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -26, -25, -25, -24, -24, -23, -22, -22, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -15, -16, -16, -17, -18, -18, -19, -19, -20, -21, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -32, -33, -34, -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -34, -33, -32, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -14, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -22, -22, -23, -23, -24, -25, -25, -26, -27, -27, -28, -28, -29, -30, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -34, -34, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -15, -15, -15, -16, -16, -16, -16, -17, -17, -17, -18, -18, -18, -19, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, -31, -32, -32, -33, -33, -33, -34, -34, -34, -34, -35, -35, -35, -35, -34, -34, -34, -34, -34, -33, -33, -32, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -17, -16, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -3, -3, -3, -3, -3, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -15, -16, -16, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -22, -22, -23, -24, -24, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -33, -34, -34, -34, -35, -35, -35, -35, -35, -35, -35, -35, -35, -34, -34, -34, -33, -33, -33, -32, -31, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -13, -14, -14, -14, -15, -15, -15, -15, -16, -16, -16, -17, -17, -18, -18, -19, -20, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -47, -47, -47, -46, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -40, -39, -38, -37, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -16, -16, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -14, -14, -14, -14, -14, -15, -15, -16, -16, -17, -18, -19, -19, -20, -21, -22, -23, -24, -25, -27, -28, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -42, -43, -43, -44, -44, -44, -44, -44, -44, -44, -44, -44, -43, -43, -42, -42, -42, -41, -40, -40, -39, -38, -37, -37, -36, -35, -34, -33, -32, -31, -30, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -20, -19, -18, -17, -16, -16, -15, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -12, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -14, -14, -14, -15, -15, -16, -16, -17, -18, -18, -19, -20, -21, -22, -24, -25, -26, -27, -28, -30, -31, -32, -34, -35, -36, -37, -39, -40, -41, -42, -43, -44, -45, -45, -46, -47, -48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -48, -48, -47, -47, -46, -45, -45, -44, -43, -42, -41, -40, -39, -38, -38, -37, -36, -35, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -16, -15, -15, -14, -13, -13, -12, -11, -11, -10, -9, -9, -8, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -9, -9, -10, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -21, -22, -24, -25, -26, -28, -29, -31, -32, -34, -35, -36, -38, -39, -40, -41, -42, -44, -44, -45, -46, -47, -48, -48, -49, -49, -50, -50, -51, -51, -51, -51, -51, -51, -51, -50, -50, -50, -49, -49, -49, -48, -47, -47, -46, -45, -44, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, -12, -11, -11, -10, -10, -9, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -7, -8, -8, -9, -10, -11, -12, -13, -14, -15, -16, -18, -19, -21, -22, -23, -25, -26, -28, -29, -31, -32, -34, -35, -37, -38, -39, -40, -42, -43, -44, -45, -46, -46, -47, -48, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -49, -49, -49, -48, -48, -47, -46, -46, -45, -44, -43, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -18, -17, -16, -15, -14, -13, -13, -12, -11, -11, -10, -9, -9, -8, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -10, -10, -10, -10, -10, -11, -11, -11, -12, -12, -13, -13, -14, -15, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -26, -27, -28, -29, -31, -32, -33, -34, -36, -37, -38, -39, -40, -41, -42, -43, -44, -45, -46, -47, -47, -48, -49, -49, -49, -50, -50, -50, -50, -50, -50, -50, -50, -50, -49, -49, -49, -48, -48, -47, -46, -46, -45, -44, -43, -42, -41, -40, -40, -39, -38, -37, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -16, -15, -14, -13, -12, -12, -11, -10, -10, -9, -9, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -9, -9, -10, -11, -11, -12, -13, -14, -16, -17, -18, -20, -21, -23, -24, -26, -27, -29, -31, -32, -34, -35, -37, -39, -40, -42, -43, -44, -46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -57, -58, -58, -59, -59, -59, -59, -59, -59, -59, -59, -59, -58, -58, -57, -57, -56, -56, -55, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -38, -37, -36, -35, -34, -32, -31, -30, -29, -28, -27, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -15, -14, -13, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -8, -8, -9, -10, -11, -12, -13, -14, -16, -17, -18, -20, -21, -23, -25, -26, -28, -30, -31, -33, -35, -36, -38, -39, -41, -42, -44, -45, -46, -48, -49, -50, -51, -52, -53, -54, -55, -55, -56, -56, -57, -57, -58, -58, -58, -58, -58, -58, -58, -58, -57, -57, -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49, -48, -47, -46, -45, -43, -42, -41, -40, -39, -38, -36, -35, -34, -33, -32, -31, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -7, -7, -7, -8, -8, -9, -9, -10, -11, -12, -13, -14, -15, -16, -17, -19, -20, -21, -23, -24, -26, -27, -29, -30, -32, -34, -35, -37, -38, -40, -41, -43, -44, -46, -47, -48, -50, -51, -52, -53, -54, -55, -56, -57, -58, -59, -59, -60, -61, -61, -61, -62, -62, -62, -62, -62, -62, -62, -62, -62, -62, -61, -61, -60, -60, -59, -58, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -31, -29, -28, -27, -26, -25, -24, -22, -21, -20, -19, -18, -0, -0, -1, -2, -2, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -4, -5, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -8, -8, -9, -9, -9, -10, -10, -10, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -10, -10, -11, -12, -12, -13, -13, -13, -14, -14, -14, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -14, -14, -14, -14, -14, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -12, -12, -13, -14, -14, -15, -15, -15, -16, -16, -16, -16, -16, -17, -17, -17, -17, -17, -16, -16, -16, -16, -16, -15, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -12, -13, -14, -15, -15, -16, -17, -17, -18, -18, -19, -19, -19, -20, -20, -20, -20, -21, -21, -21, -21, -21, -21, -21, -21, -20, -20, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -12, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -15, -16, -17, -18, -18, -19, -20, -20, -21, -21, -22, -22, -22, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -13, -14, -15, -15, -16, -16, -16, -17, -17, -18, -18, -18, -18, -19, -19, -19, -19, -20, -20, -20, -20, -21, -21, -21, -21, -21, -21, -21, -21, -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, -21, -21, -21, -21, -21, -21, -20, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -27, -28, -28, -29, -29, -29, -30, -30, -30, -30, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -30, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -16, -17, -18, -19, -20, -20, -21, -22, -23, -23, -24, -25, -26, -26, -27, -27, -28, -29, -29, -30, -30, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, -33, -33, -33, -33, -32, -32, -31, -31, -31, -30, -30, -29, -29, -28, -27, -27, -26, -26, -25, -25, -24, -23, -23, -22, -22, -21, -20, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -5, -4, -4, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -11, -12, -13, -14, -14, -15, -15, -16, -16, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -22, -23, -24, -24, -25, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -33, -32, -32, -32, -32, -31, -31, -30, -30, -29, -29, -28, -28, -27, -27, -26, -26, -25, -24, -24, -23, -22, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -4, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19, -20, -20, -21, -21, -22, -23, -23, -24, -24, -25, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -31, -32, -32, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -37, -37, -38, -38, -38, -38, -37, -37, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28, -27, -27, -26, -25, -25, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -14, -15, -15, -15, -15, -16, -16, -16, -16, -17, -17, -18, -18, -19, -19, -20, -20, -21, -21, -22, -23, -23, -24, -25, -25, -26, -26, -27, -27, -28, -28, -29, -29, -29, -30, -30, -30, -30, -30, -31, -31, -31, -30, -30, -30, -30, -30, -29, -29, -29, -28, -28, -27, -27, -27, -26, -25, -25, -24, -24, -23, -23, -22, -21, -21, -20, -19, -19, -18, -18, -17, -16, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -14, -14, -14, -14, -15, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -19, -19, -20, -21, -21, -22, -22, -23, -24, -24, -25, -26, -26, -27, -28, -28, -29, -29, -30, -30, -30, -31, -31, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3, -3, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -9, -10, -11, -11, -12, -12, -12, -13, -13, -13, -13, -13, -14, -14, -14, -14, -14, -15, -15, -15, -16, -16, -17, -17, -18, -19, -19, -20, -21, -21, -22, -23, -24, -25, -26, -27, -28, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -37, -38, -38, -38, -38, -38, -38, -37, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -27, -26, -25, -24, -24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -16, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -9, -8, -8, -7, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -12, -12, -13, -13, -13, -14, -14, -14, -14, -15, -15, -16, -16, -16, -17, -18, -18, -19, -20, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -38, -38, -39, -39, -40, -40, -41, -41, -41, -42, -42, -42, -42, -42, -42, -42, -42, -41, -41, -41, -41, -40, -40, -39, -39, -38, -38, -37, -36, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -7, -6, -6, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -10, -10, -11, -11, -12, -12, -13, -14, -15, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -27, -28, -29, -30, -31, -32, -32, -33, -34, -34, -35, -35, -36, -36, -37, -37, -37, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -34, -34, -33, -33, -32, -31, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -14, -13, -13, -12, -11, -11, -10, -10, -9, -9, -8, -8, -8, -7, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -8, -8, -8, -8, -8, -8, -9, -9, -9, -9, -9, -9, -10, -10, -11, -11, -12, -12, -13, -14, -15, -16, -17, -18, -19, -20, -22, -23, -24, -25, -27, -28, -29, -30, -31, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -42, -42, -43, -43, -44, -44, -44, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, -44, -44, -43, -43, -43, -42, -42, -41, -40, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -31, -30, -29, -29, -28, -27, -26, -25, -24, -23, -23, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -6, -7, -7, -8, -8, -9, -10, -10, -11, -12, -13, -14, -16, -17, -18, -19, -20, -22, -23, -24, -25, -26, -28, -29, -30, -31, -32, -33, -34, -34, -35, -36, -37, -37, -38, -38, -38, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -38, -38, -38, -37, -37, -36, -35, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -25, -24, -24, -23, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -10, -10, -9, -9, -8, -8, -8, -7, -7, -6, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -7, -7, -7, -8, -8, -8, -9, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -21, -22, -23, -25, -26, -27, -29, -30, -31, -32, -34, -35, -36, -37, -38, -39, -39, -40, -41, -41, -42, -43, -43, -43, -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -40, -39, -39, -38, -37, -37, -36, -35, -34, -33, -33, -32, -31, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -10, -9, -9, -8, -8, -7, -7, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5, -6, -6, -6, -7, -7, -8, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -19, -20, -21, -22, -24, -25, -26, -27, -29, -30, -31, -32, -33, -34, -35, -36, -37, -38, -39, -39, -40, -41, -41, -42, -42, -42, -43, -43, -43, -43, -43, -43, -43, -43, -43, -43, -42, -42, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -37, -36, -35, -34, -34, -33, -32, -31, -31, -30, -29, -28, -27, -26, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -15, -15, -14, -13, -13, -12, -12, -11, -10, -10, -0, -0, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -7, -7, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -5, -5, -5, -6, -7, -7, -8, -9, -10, -11, -12, -13, -14, -16, -17, -18, -20, -21, -22, -24, -25, -26, -28, -29, -30, -32, -33, -34, -35, -36, -37, -38, -39, -40, -41, -41, -42, -43, -43, -44, -44, -44, -44, -44, -45, -45, -44, -44, -44, -44, -44, -43, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -36, -35, -34, -34, -33, -32, -31, -30, -29, -28, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, -12, -12, -11, -11, -10, -0, -0, -1, -2, -3, -4, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -5, -5, -6, -6, -7, -8, -8, -9, -10, -11, -12, -13, -15, -16, -17, -18, -20, -21, -23, -24, -25, -27, -28, -29, -31, -32, -33, -34, -35, -37, -38, -39, -40, -40, -41, -42, -43, -43, -44, -45, -45, -45, -46, -46, -46, -47, -47, -47, -47, -47, -47, -47, -46, -46, -46, -46, -45, -45, -44, -44, -44, -43, -42, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -34, -34, -33, -32, -31, -30, -29, -28, -28, -27, -26, -25, -24, -23, -22, -21, -21, -20, -19, -18, -17, -17, -16, -15, -0, -0, -1, -2, -3, -4, -5, -6, -6, -6, -7, -7, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -5, -5, -6, -6, -7, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -20, -21, -22, -23, -25, -26, -27, -29, -30, -31, -32, -33, -35, -36, -37, -38, -39, -40, -41, -42, -43, -43, -44, -45, -45, -46, -46, -47, -47, -47, -47, -48, -48, -48, -48, -48, -47, -47, -47, -47, -46, -46, -45, -45, -44, -44, -43, -42, -42, -41, -40, -39, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -18, -17, -16, - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_ER_TABLES_XOR_H_ diff --git a/modules/video_coding/main/source/event.h b/modules/video_coding/main/source/event.h deleted file mode 100644 index 39fd49485..000000000 --- a/modules/video_coding/main/source/event.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_EVENT_H_ -#define WEBRTC_MODULES_VIDEO_CODING_EVENT_H_ - -#include "event_wrapper.h" - -namespace webrtc -{ - -//#define EVENT_DEBUG - -class VCMEvent : public EventWrapper -{ -public: - VCMEvent() : _event(*EventWrapper::Create()) {}; - - virtual ~VCMEvent() { delete &_event; }; - - /** - * Release waiting threads - */ - bool Set() { return _event.Set(); }; - - bool Reset() { return _event.Reset(); }; - - /** - * Wait for this event - */ - EventTypeWrapper Wait(unsigned long maxTime) - { -#ifdef EVENT_DEBUG - return kEventTimeout; -#else - return _event.Wait(maxTime); -#endif - }; - - /** - * Start a timer - */ - bool StartTimer(bool periodic, unsigned long time) - { return _event.StartTimer(periodic, time); }; - /** - * Stop the timer - */ - bool StopTimer() { return _event.StopTimer(); }; - -private: - EventWrapper& _event; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_EVENT_H_ diff --git a/modules/video_coding/main/source/exp_filter.cc b/modules/video_coding/main/source/exp_filter.cc deleted file mode 100644 index 1d6f9a7c2..000000000 --- a/modules/video_coding/main/source/exp_filter.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "exp_filter.h" - -#include - -namespace webrtc { - -void -VCMExpFilter::Reset(float alpha) -{ - _alpha = alpha; - _filtered = -1.0; -} - -float -VCMExpFilter::Apply(float exp, float sample) -{ - if (_filtered == -1.0) - { - // Initialize filtered bit rates - _filtered = sample; - } - else if (exp == 1.0) - { - _filtered = _alpha * _filtered + (1 - _alpha) * sample; - } - else - { - float alpha = pow(_alpha, exp); - _filtered = alpha * _filtered + (1 - alpha) * sample; - } - if (_max != -1 && _filtered > _max) - { - _filtered = _max; - } - return _filtered; -} - -void -VCMExpFilter::UpdateBase(float alpha) -{ - _alpha = alpha; -} - -float -VCMExpFilter::Value() const -{ - return _filtered; -} - -} diff --git a/modules/video_coding/main/source/exp_filter.h b/modules/video_coding/main/source/exp_filter.h deleted file mode 100644 index 46d206a1f..000000000 --- a/modules/video_coding/main/source/exp_filter.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_ - -namespace webrtc -{ - -/**********************/ -/* ExpFilter class */ -/**********************/ - -class VCMExpFilter -{ -public: - VCMExpFilter(float alpha, float max = -1.0) : _alpha(alpha), _filtered(-1.0), _max(max) {} - - // Resets the filter to its initial state, and resets alpha to the given value - // - // Input: - // - alpha : the new value of the filter factor base. - void Reset(float alpha); - - // Applies the filter with the given exponent on the provided sample - // - // Input: - // - exp : Exponent T in y(k) = alpha^T * y(k-1) + (1 - alpha^T) * x(k) - // - sample : x(k) in the above filter equation - float Apply(float exp, float sample); - - // Return current filtered value: y(k) - // - // Return value : The current filter output - float Value() const; - - // Change the filter factor base - // - // Input: - // - alpha : The new filter factor base. - void UpdateBase(float alpha); - -private: - float _alpha; // Filter factor base - float _filtered; // Current filter output - const float _max; -}; // end of ExpFilter class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_EXP_FILTER_H_ diff --git a/modules/video_coding/main/source/fec_tables_xor.h b/modules/video_coding/main/source/fec_tables_xor.h deleted file mode 100644 index 3c9eaeb45..000000000 --- a/modules/video_coding/main/source/fec_tables_xor.h +++ /dev/null @@ -1,6478 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_ - -namespace webrtc -{ - -// Table for Protection factor (code rate) of delta frames, for the XOR FEC. -// Input is the packet loss and average bits/frame (bitRate/frame_rate): -// i.e., codeRateXORTable[k] where k = rate_i*129 + loss_j; loss_j=0,1,..128, -// and rate_i varies over some range -const unsigned char VCMCodeRateXORTable[6450] = { -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -0, -0, -0, -0, -0, -0, -0, -0, -31, -31, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -0, -0, -0, -31, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -0, -0, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -25, -25, -25, -25, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -0, -0, -0, -0, -25, -25, -25, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -0, -0, -0, -0, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -0, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -42, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -0, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -0, -0, -0, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -0, -0, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -28, -28, -28, -28, -28, -28, -28, -28, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -0, -0, -28, -28, -28, -28, -28, -28, -28, -28, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -0, -0, -25, -25, -25, -25, -25, -25, -25, -51, -51, -51, -51, -51, -51, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -0, -25, -25, -25, -25, -25, -25, -51, -51, -51, -51, -51, -51, -51, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -23, -23, -23, -23, -23, -23, -23, -46, -46, -46, -46, -46, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -0, -23, -23, -23, -23, -23, -23, -23, -46, -46, -46, -46, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -0, -23, -23, -23, -23, -23, -23, -46, -46, -46, -46, -46, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -0, -21, -21, -21, -21, -21, -42, -42, -42, -42, -42, -42, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -21, -21, -21, -21, -21, -42, -42, -42, -42, -42, -42, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -19, -19, -19, -19, -19, -39, -39, -39, -39, -58, -58, -58, -58, -58, -58, -58, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -0, -19, -19, -19, -19, -39, -39, -39, -39, -39, -58, -58, -58, -58, -58, -58, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -117, -0, -18, -18, -18, -18, -36, -36, -36, -54, -54, -54, -54, -54, -54, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -18, -18, -18, -18, -36, -36, -36, -54, -54, -54, -54, -54, -54, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -18, -18, -18, -18, -36, -36, -36, -54, -54, -54, -54, -54, -54, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -17, -17, -17, -17, -34, -34, -51, -51, -51, -51, -51, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -0, -17, -17, -17, -17, -34, -34, -51, -51, -51, -51, -51, -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -119, -0, -15, -15, -15, -31, -31, -31, -47, -47, -47, -47, -63, -63, -63, -63, -63, -63, -63, -63, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -15, -15, -15, -31, -31, -31, -47, -47, -47, -47, -63, -63, -63, -63, -63, -63, -63, -63, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -15, -15, -15, -30, -30, -30, -45, -45, -45, -60, -60, -60, -60, -60, -60, -60, -60, -75, -75, -75, -75, -75, -75, -75, -75, -75, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -0, -15, -15, -15, -30, -30, -30, -45, -45, -45, -60, -60, -60, -60, -60, -60, -60, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -0, -15, -15, -15, -30, -30, -45, -45, -45, -45, -60, -60, -60, -60, -60, -60, -60, -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -0, -14, -14, -28, -28, -28, -42, -42, -56, -56, -56, -56, -56, -70, -70, -70, -70, -70, -70, -70, -70, -70, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -14, -14, -28, -28, -28, -42, -42, -56, -56, -56, -56, -56, -70, -70, -70, -70, -70, -70, -70, -70, -70, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -113, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -13, -13, -26, -26, -40, -40, -40, -40, -53, -53, -67, -67, -67, -67, -67, -67, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -0, -13, -13, -26, -26, -40, -40, -40, -40, -53, -53, -67, -67, -67, -67, -67, -67, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -120, -0, -12, -12, -25, -25, -38, -38, -38, -38, -63, -63, -63, -63, -63, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -12, -12, -25, -25, -38, -38, -38, -38, -63, -63, -63, -63, -63, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -76, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -12, -12, -24, -24, -36, -36, -48, -48, -48, -48, -60, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -0, -12, -12, -24, -24, -36, -36, -48, -48, -48, -48, -60, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -0, -12, -12, -24, -24, -36, -48, -48, -48, -48, -48, -60, -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -109, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -0, -11, -11, -23, -34, -34, -34, -46, -46, -57, -57, -57, -57, -57, -57, -69, -69, -69, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -11, -11, -23, -34, -34, -34, -46, -46, -57, -57, -57, -57, -57, -57, -69, -69, -69, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -104, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -115, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -0, -11, -11, -22, -33, -33, -44, -44, -44, -55, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -99, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -110, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, -121, - - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_FEC_TABLES_XOR_H_ diff --git a/modules/video_coding/main/source/frame_buffer.cc b/modules/video_coding/main/source/frame_buffer.cc deleted file mode 100644 index df7b37b4c..000000000 --- a/modules/video_coding/main/source/frame_buffer.cc +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "../../../../engine_configurations.h" -#include "frame_buffer.h" -#include "packet.h" - -#include -#include - -#if defined(_WIN32) - // VS 2005: Don't warn for default initialized arrays. See help for more info. - #pragma warning(disable:4351) -#endif - -namespace webrtc { - -// Constructor -VCMFrameBuffer::VCMFrameBuffer() : - _state(kStateFree), - _frameCounted(false), - _nackCount(0), - _latestPacketTimeMs(-1) -{ -} - -// Destructor -VCMFrameBuffer::~VCMFrameBuffer() -{ - Reset(); -} - -VCMFrameBuffer::VCMFrameBuffer(VCMFrameBuffer& rhs) -: -VCMEncodedFrame(rhs), -_state(rhs._state), -_frameCounted(rhs._frameCounted), -_sessionInfo(), -_nackCount(rhs._nackCount), -_latestPacketTimeMs(rhs._latestPacketTimeMs) -{ - _sessionInfo = rhs._sessionInfo; -} - -webrtc::FrameType -VCMFrameBuffer::FrameType() const -{ - return _sessionInfo.FrameType(); -} - -void -VCMFrameBuffer::SetPreviousFrameLoss() -{ - _sessionInfo.SetPreviousFrameLoss(); -} - -WebRtc_Word32 -VCMFrameBuffer::GetLowSeqNum() -{ - return _sessionInfo.GetLowSeqNum(); -} - -// Get highest sequence number for complete sessions -WebRtc_Word32 -VCMFrameBuffer::GetHighSeqNumComplete() -{ - if (_sessionInfo.IsSessionComplete()) - { - return _sessionInfo.GetHighSeqNum(); - } - return -1; -} - -WebRtc_Word32 -VCMFrameBuffer::GetHighSeqNum() -{ - return _sessionInfo.GetHighSeqNum(); -} - -bool -VCMFrameBuffer::IsSessionComplete() -{ - return _sessionInfo.IsSessionComplete(); -} - -// Insert packet -VCMFrameBufferEnum -VCMFrameBuffer::InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs) -{ - if (_state == kStateDecoding) - { - // Do not insert packet - return kIncomplete; - } - - // Sanity to check if the frame has been freed. (Too old for example) - if(_state == kStateFree) - { - return kStateError; - } - - // is this packet part of this frame - if (TimeStamp() && (TimeStamp() != packet.timestamp)) - { - return kTimeStampError; - } - - // sanity checks - if (_size + packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0 ) - > kMaxJBFrameSizeBytes) - { - return kSizeError; - } - if (NULL == packet.dataPtr && packet.sizeBytes > 0) - { - return kSizeError; - } - if ((packet.frameType != kFrameEmpty) && - (!_sessionInfo.HaveStartSeqNumber())) - { - _sessionInfo.SetStartSeqNumber(packet.seqNum); - } - if (packet.dataPtr != NULL) - { - _payloadType = packet.payloadType; - } - - if (kStateEmpty == _state) - { - // First packet (empty and/or media) inserted into this frame. - // store some info and set some initial values. - _timeStamp = packet.timestamp; - _codec = packet.codec; - if (packet.frameType != kFrameEmpty) - { - // first media packet - SetState(kStateIncomplete); - } - } - - WebRtc_UWord32 requiredSizeBytes = Length() + packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); - if (requiredSizeBytes >= _size) - { - const WebRtc_UWord32 increments = requiredSizeBytes / - kBufferIncStepSizeBytes + - (requiredSizeBytes % - kBufferIncStepSizeBytes > 0); - const WebRtc_UWord32 newSize = _size + - increments * kBufferIncStepSizeBytes; - if (newSize > kMaxJBFrameSizeBytes) - { - return kSizeError; - } - if (VerifyAndAllocate(newSize) == -1) - { - return kSizeError; - } - } - WebRtc_Word64 retVal = _sessionInfo.InsertPacket(packet, _buffer); - if (retVal == -1) - { - return kSizeError; - } - else if (retVal == -2) - { - return kDuplicatePacket; - } - // update length - _length = Length() + static_cast(retVal); - - _latestPacketTimeMs = timeInMs; - - if(_sessionInfo.IsSessionComplete()) - { - return kCompleteSession; - } - else - { - // this layer is not complete - if (_state == kStateComplete) - { - // we already have a complete layer - // wait for all independent layers belonging to the same frame - _state = kStateIncomplete; - } - } - return kIncomplete; -} - -WebRtc_Word64 VCMFrameBuffer::LatestPacketTimeMs() -{ - return _latestPacketTimeMs; -} - -// Zero out all entries in list up to and including the (first) entry equal to _lowSeqNum -WebRtc_Word32 VCMFrameBuffer::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num) -{ - if(_sessionInfo.ZeroOutSeqNum(list, num) != 0) - { - return -1; - } - return 0; -} - -// Zero out all entries in list up to and including the (first) entry equal to -// _lowSeqNum. Hybrid mode: 1. Don't NACK FEC packets 2. Make a smart decision -// on whether to NACK or not - -WebRtc_Word32 VCMFrameBuffer::ZeroOutSeqNumHybrid(WebRtc_Word32* list, - WebRtc_Word32 num, - float rttScore) -{ - return _sessionInfo.ZeroOutSeqNumHybrid(list, num, rttScore); -} - -void VCMFrameBuffer::IncrementNackCount() -{ - _nackCount++; -} - -WebRtc_Word16 VCMFrameBuffer::GetNackCount() const -{ - return _nackCount; -} - -bool VCMFrameBuffer::HaveLastPacket() -{ - return _sessionInfo.HaveLastPacket(); -} - -bool -VCMFrameBuffer::ForceSetHaveLastPacket() -{ - _sessionInfo.ForceSetHaveLastPacket(); - return _sessionInfo.IsSessionComplete(); -} - -void VCMFrameBuffer::Reset() -{ - _length = 0; - _timeStamp = 0; - _sessionInfo.Reset(); - _frameCounted = false; - _payloadType = 0; - _nackCount = 0; - _latestPacketTimeMs = -1; - _state = kStateFree; - VCMEncodedFrame::Reset(); -} - -// Makes sure the session contains a decodable stream. -void -VCMFrameBuffer::MakeSessionDecodable() -{ - WebRtc_Word32 retVal = _sessionInfo.MakeSessionDecodable(_buffer); - // update length - _length -= retVal; -} - -// Set state of frame -void -VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state) -{ - if(_state == state) - { - return; - } - switch (state) - { - case kStateFree: - // Reset everything - // We can go to this state from all other states. - // The one setting the state to free must ensure - // that the frame is removed from the timestamp - // ordered frame list in the jb. - Reset(); - break; - - case kStateIncomplete: - // we can go to this state from state kStateEmpty - assert(_state == kStateEmpty || - _state == kStateDecoding); - - // Do nothing, we received a packet - break; - - case kStateComplete: - assert(_state == kStateEmpty || - _state == kStateIncomplete || - _state == kStateDecodable); - - break; - - case kStateEmpty: - assert(_state == kStateFree); - // Do nothing - break; - - case kStateDecoding: - // we can go to this state from state kStateComplete kStateIncomplete - assert(_state == kStateComplete || _state == kStateIncomplete || - _state == kStateDecodable); - // Transfer frame information to EncodedFrame and create any codec specific information - RestructureFrameInformation(); - break; - - case kStateDecodable: - if (_state == kStateComplete) - { - // if complete, obviously decodable, keep as is. - return; - } - assert(_state == kStateEmpty || - _state == kStateIncomplete); - break; - - default: - // Should never happen - assert(!"FrameBuffer::SetState Incorrect frame buffer state as input"); - return; - } - _state = state; -} - -void -VCMFrameBuffer::RestructureFrameInformation() -{ - PrepareForDecode(); - _frameType = ConvertFrameType(_sessionInfo.FrameType()); - _completeFrame = _sessionInfo.IsSessionComplete(); - _missingFrame = _sessionInfo.PreviousFrameLoss(); -} - -WebRtc_Word32 -VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage) -{ - _frameType = ConvertFrameType(frameFromStorage.frameType); - _timeStamp = frameFromStorage.timeStamp; - _payloadType = frameFromStorage.payloadType; - _encodedWidth = frameFromStorage.encodedWidth; - _encodedHeight = frameFromStorage.encodedHeight; - _missingFrame = frameFromStorage.missingFrame; - _completeFrame = frameFromStorage.completeFrame; - _renderTimeMs = frameFromStorage.renderTimeMs; - _codec = frameFromStorage.codec; - if (VerifyAndAllocate(frameFromStorage.payloadSize) < 0) - { - return VCM_MEMORY; - } - memcpy(_buffer, frameFromStorage.payloadData, frameFromStorage.payloadSize); - _length = frameFromStorage.payloadSize; - return VCM_OK; -} - -// Set counted status (as counted by JB or not) -void VCMFrameBuffer::SetCountedFrame(bool frameCounted) -{ - _frameCounted = frameCounted; -} - -bool VCMFrameBuffer::GetCountedFrame() -{ - return _frameCounted; -} - -// Get current state of frame -VCMFrameBufferStateEnum -VCMFrameBuffer::GetState() const -{ - return _state; -} - -// Get current state of frame -VCMFrameBufferStateEnum -VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const -{ - timeStamp = TimeStamp(); - return GetState(); -} - -bool -VCMFrameBuffer::IsRetransmitted() -{ - return _sessionInfo.IsRetransmitted(); -} - -void -VCMFrameBuffer::PrepareForDecode() -{ - _length = _sessionInfo.PrepareForDecode(_buffer, _codec); -} - -} diff --git a/modules/video_coding/main/source/frame_buffer.h b/modules/video_coding/main/source/frame_buffer.h deleted file mode 100644 index 65c56a3dc..000000000 --- a/modules/video_coding/main/source/frame_buffer.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_ - -#include "typedefs.h" -#include "module_common_types.h" - -#include "encoded_frame.h" -#include "frame_list.h" -#include "jitter_buffer_common.h" -#include "session_info.h" - -namespace webrtc -{ - -class VCMFrameBuffer : public VCMEncodedFrame -{ -public: - VCMFrameBuffer(); - virtual ~VCMFrameBuffer(); - - VCMFrameBuffer(VCMFrameBuffer& rhs); - - virtual void Reset(); - - VCMFrameBufferEnum InsertPacket(const VCMPacket& packet, WebRtc_Word64 timeInMs); - - // State - // Get current state of frame - VCMFrameBufferStateEnum GetState() const; - // Get current state and timestamp of frame - VCMFrameBufferStateEnum GetState(WebRtc_UWord32& timeStamp) const; - void SetState(VCMFrameBufferStateEnum state); // Set state of frame - - bool IsRetransmitted(); - bool IsSessionComplete(); - bool HaveLastPacket(); - bool ForceSetHaveLastPacket(); - // Makes sure the session contain a decodable stream. - void MakeSessionDecodable(); - - // Sequence numbers - // Get lowest packet sequence number in frame - WebRtc_Word32 GetLowSeqNum(); - // Get highest packet sequence number in frame - WebRtc_Word32 GetHighSeqNum(); - - // Get highest sequence number of complete session - WebRtc_Word32 GetHighSeqNumComplete(); - - // Set counted status (as counted by JB or not) - void SetCountedFrame(bool frameCounted); - bool GetCountedFrame(); - - // NACK - // Zero out all entries in list up to and including the entry equal to _lowSeqNum - WebRtc_Word32 ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num); - // Hybrid extension: only NACK important packets, discard FEC packets - WebRtc_Word32 ZeroOutSeqNumHybrid(WebRtc_Word32* list, - WebRtc_Word32 num, - float rttScore); - void IncrementNackCount(); - WebRtc_Word16 GetNackCount() const; - - WebRtc_Word64 LatestPacketTimeMs(); - - webrtc::FrameType FrameType() const; - void SetPreviousFrameLoss(); - - WebRtc_Word32 ExtractFromStorage(const EncodedVideoData& frameFromStorage); - -protected: - void RestructureFrameInformation(); - void PrepareForDecode(); - -private: - VCMFrameBufferStateEnum _state; // Current state of the frame - bool _frameCounted; // If this frame has been counted by JB - VCMSessionInfo _sessionInfo; - WebRtc_UWord16 _nackCount; - WebRtc_Word64 _latestPacketTimeMs; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_ diff --git a/modules/video_coding/main/source/frame_dropper.cc b/modules/video_coding/main/source/frame_dropper.cc deleted file mode 100644 index 065e4523e..000000000 --- a/modules/video_coding/main/source/frame_dropper.cc +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "frame_dropper.h" -#include "internal_defines.h" -#include "trace.h" - -namespace webrtc -{ - -VCMFrameDropper::VCMFrameDropper(WebRtc_Word32 vcmId) -: -_vcmId(vcmId), -_keyFrameSizeAvgKbits(0.9f), -_keyFrameRatio(0.99f), -_dropRatio(0.9f, 0.96f) -{ - Reset(); -} - -void -VCMFrameDropper::Reset() -{ - _keyFrameRatio.Reset(0.99f); - _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps - _keyFrameSizeAvgKbits.Reset(0.9f); - _keyFrameCount = 0; - _accumulator = 0.0f; - _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window - _targetBitRate = 300.0f; - _userFrameRate = 30; - _keyFrameSpreadFrames = 0.5f * _userFrameRate; - _dropNext = false; - _dropRatio.Reset(0.9f); - _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0 - _dropCount = 0; - _windowSize = 0.5f; - _wasBelowMax = true; - _enabled = true; - _fastMode = false; // start with normal (non-aggressive) mode -} - -void -VCMFrameDropper::Enable(bool enable) -{ - _enabled = enable; -} - -void -VCMFrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame) -{ - if (!_enabled) - { - return; - } - float frameSizeKbits = 8.0f * static_cast(frameSizeBytes) / 1000.0f; - if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different - { - _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits); - _keyFrameRatio.Apply(1.0, 1.0); - if (frameSizeKbits > _keyFrameSizeAvgKbits.Value()) - { - // Remove the average key frame size since we - // compensate for key frames when adding delta - // frames. - frameSizeKbits -= _keyFrameSizeAvgKbits.Value(); - } - else - { - // Shouldn't be negative, so zero is the lower bound. - frameSizeKbits = 0; - } - if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) - { - // We are sending key frames more often than our upper bound for - // how much we allow the key frame compensation to be spread - // out in time. Therefor we must use the key frame ratio rather - // than keyFrameSpreadFrames. - _keyFrameCount = static_cast(1 / _keyFrameRatio.Value() + 0.5); - } - else - { - // Compensate for the key frame the following frames - _keyFrameCount = static_cast(_keyFrameSpreadFrames + 0.5); - } - } - else - { - // Decrease the keyFrameRatio - _keyFrameRatio.Apply(1.0, 0.0); - } - // Change the level of the accumulator (bucket) - _accumulator += frameSizeKbits; -} - -void -VCMFrameDropper::Leak(WebRtc_UWord32 inputFrameRate) -{ - if (!_enabled) - { - return; - } - if (inputFrameRate < 1) - { - return; - } - if (_targetBitRate < 0.0f) - { - return; - } - _keyFrameSpreadFrames = 0.5f * inputFrameRate; - // T is the expected bits per frame (target). If all frames were the same size, - // we would get T bits per frame. Notice that T is also weighted to be able to - // force a lower frame rate if wanted. - float T = _targetBitRate / inputFrameRate; - if (_keyFrameCount > 0) - { - // Perform the key frame compensation - if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) - { - T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value(); - } - else - { - T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames; - } - _keyFrameCount--; - } - _accumulator -= T; - UpdateRatio(); - -} - -void -VCMFrameDropper::UpdateNack(WebRtc_UWord32 nackBytes) -{ - if (!_enabled) - { - return; - } - _accumulator += static_cast(nackBytes) * 8.0f / 1000.0f; -} - -void -VCMFrameDropper::FillBucket(float inKbits, float outKbits) -{ - _accumulator += (inKbits - outKbits); -} - -void -VCMFrameDropper::UpdateRatio() -{ - if (_accumulator > 1.3f * _accumulatorMax) - { - // Too far above accumulator max, react faster - _dropRatio.UpdateBase(0.8f); - } - else - { - // Go back to normal reaction - _dropRatio.UpdateBase(0.9f); - } - if (_accumulator > _accumulatorMax) - { - // We are above accumulator max, and should ideally - // drop a frame. Increase the dropRatio and drop - // the frame later. - if (_wasBelowMax) - { - _dropNext = true; - } - if (_fastMode) - { - // always drop in aggressive mode - _dropNext = true; - } - - _dropRatio.Apply(1.0f, 1.0f); - _dropRatio.UpdateBase(0.9f); - } - else - { - _dropRatio.Apply(1.0f, 0.0f); - } - if (_accumulator < 0.0f) - { - _accumulator = 0.0f; - } - _wasBelowMax = _accumulator < _accumulatorMax; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId), "FrameDropper: dropRatio = %f accumulator = %f, accumulatorMax = %f", _dropRatio.Value(), _accumulator, _accumulatorMax); -} - -// This function signals when to drop frames to the caller. It makes use of the dropRatio -// to smooth out the drops over time. -bool -VCMFrameDropper::DropFrame() -{ - if (!_enabled) - { - return false; - } - if (_dropNext) - { - _dropNext = false; - _dropCount = 0; - } - - if (_dropRatio.Value() >= 0.5f) // Drops per keep - { - // limit is the number of frames we should drop between each kept frame - // to keep our drop ratio. limit is positive in this case. - float denom = 1.0f - _dropRatio.Value(); - if (denom < 1e-5) - { - denom = (float)1e-5; - } - WebRtc_Word32 limit = static_cast(1.0f / denom - 1.0f + 0.5f); - if (_dropCount < 0) - { - // Reset the _dropCount since it was negative and should be positive. - if (_dropRatio.Value() > 0.4f) - { - _dropCount = -_dropCount; - } - else - { - _dropCount = 0; - } - } - if (_dropCount < limit) - { - // As long we are below the limit we should drop frames. - _dropCount++; - return true; - } - else - { - // Only when we reset _dropCount a frame should be kept. - _dropCount = 0; - return false; - } - } - else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop - { - // limit is the number of frames we should keep between each drop - // in order to keep the drop ratio. limit is negative in this case, - // and the _dropCount is also negative. - float denom = _dropRatio.Value(); - if (denom < 1e-5) - { - denom = (float)1e-5; - } - WebRtc_Word32 limit = -static_cast(1.0f / denom - 1.0f + 0.5f); - if (_dropCount > 0) - { - // Reset the _dropCount since we have a positive - // _dropCount, and it should be negative. - if (_dropRatio.Value() < 0.6f) - { - _dropCount = -_dropCount; - } - else - { - _dropCount = 0; - } - } - if (_dropCount > limit) - { - if (_dropCount == 0) - { - // Drop frames when we reset _dropCount. - _dropCount--; - return true; - } - else - { - // Keep frames as long as we haven't reached limit. - _dropCount--; - return false; - } - } - else - { - _dropCount = 0; - return false; - } - } - _dropCount = 0; - return false; - - // A simpler version, unfiltered and quicker - //bool dropNext = _dropNext; - //_dropNext = false; - //return dropNext; -} - -void -VCMFrameDropper::SetRates(float bitRate, float userFrameRate) -{ - // Bit rate of -1 means infinite bandwidth. - _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds) - if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax) - { - // Rescale the accumulator level if the accumulator max decreases - _accumulator = bitRate / _targetBitRate * _accumulator; - } - _targetBitRate = bitRate; - if (userFrameRate > 0.0f) - { - _userFrameRate = userFrameRate; - } -} - -float -VCMFrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const -{ - if (!_enabled) - { - return static_cast(inputFrameRate); - } - return inputFrameRate * (1.0f - _dropRatio.Value()); -} - -} diff --git a/modules/video_coding/main/source/frame_dropper.h b/modules/video_coding/main/source/frame_dropper.h deleted file mode 100644 index 5e7e8a16b..000000000 --- a/modules/video_coding/main/source/frame_dropper.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_ - -#include "exp_filter.h" -#include "typedefs.h" - -namespace webrtc -{ - -/******************************/ -/* VCMFrameDropper class */ -/****************************/ -// The Frame Dropper implements a variant of the leaky bucket algorithm -// for keeping track of when to drop frames to avoid bit rate -// over use when the encoder can't keep its bit rate. -class VCMFrameDropper -{ -public: - VCMFrameDropper(WebRtc_Word32 vcmId = 0); - // Resets the FrameDropper to its initial state. - // This means that the frameRateWeight is set to its - // default value as well. - void Reset(); - - void Enable(bool enable); - // Answers the question if it's time to drop a frame - // if we want to reach a given frame rate. Must be - // called for every frame. - // - // Return value : True if we should drop the current frame - bool DropFrame(); - // Updates the FrameDropper with the size of the latest encoded - // frame. The FrameDropper calculates a new drop ratio (can be - // seen as the probability to drop a frame) and updates its - // internal statistics. - // - // Input: - // - frameSizeBytes : The size of the latest frame - // returned from the encoder. - // - deltaFrame : True if the encoder returned - // a key frame. - void Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame); - - void Leak(WebRtc_UWord32 inputFrameRate); - - void UpdateNack(WebRtc_UWord32 nackBytes); - - // Sets the target bit rate and the frame rate produced by - // the camera. - // - // Input: - // - bitRate : The target bit rate - void SetRates(float bitRate, float userFrameRate); - - // Return value : The current average frame rate produced - // if the DropFrame() function is used as - // instruction of when to drop frames. - float ActualFrameRate(WebRtc_UWord32 inputFrameRate) const; - -private: - void FillBucket(float inKbits, float outKbits); - void UpdateRatio(); - - WebRtc_Word32 _vcmId; - VCMExpFilter _keyFrameSizeAvgKbits; - VCMExpFilter _keyFrameRatio; - float _keyFrameSpreadFrames; - WebRtc_Word32 _keyFrameCount; - float _accumulator; - float _accumulatorMax; - float _targetBitRate; - bool _dropNext; - VCMExpFilter _dropRatio; - WebRtc_Word32 _dropCount; - float _windowSize; - float _userFrameRate; - bool _wasBelowMax; - bool _enabled; - bool _fastMode; -}; // end of VCMFrameDropper class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_DROPPER_H_ diff --git a/modules/video_coding/main/source/frame_list.cc b/modules/video_coding/main/source/frame_list.cc deleted file mode 100644 index e79dc9153..000000000 --- a/modules/video_coding/main/source/frame_list.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "frame_list.h" -#include "frame_buffer.h" -#include "jitter_buffer.h" -#include - -namespace webrtc { - -VCMFrameListTimestampOrderAsc::~VCMFrameListTimestampOrderAsc() -{ - Flush(); -} - -void -VCMFrameListTimestampOrderAsc::Flush() -{ - while(Erase(First()) != -1) { } -} - -// Inserts frame in timestamp order, with the oldest timestamp first. Takes wrap arounds into account -WebRtc_Word32 -VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame) -{ - VCMFrameListItem* item = static_cast(First()); - VCMFrameListItem* newItem = new VCMFrameListItem(frame); - bool inserted = false; - if (newItem == NULL) - { - return -1; - } - while (item != NULL) - { - const WebRtc_UWord32 itemTimestamp = item->GetItem()->TimeStamp(); - if (VCMJitterBuffer::LatestTimestamp(itemTimestamp, frame->TimeStamp()) == itemTimestamp) - { - if (InsertBefore(item, newItem) < 0) - { - delete newItem; - return -1; - } - inserted = true; - break; - } - item = Next(item); - } - if (!inserted && ListWrapper::Insert(ListWrapper::Last(), newItem) < 0) - { - delete newItem; - return -1; - } - return 0; -} - -VCMFrameBuffer* -VCMFrameListTimestampOrderAsc::FirstFrame() const -{ - VCMFrameListItem* item = First(); - if (item != NULL) - { - return item->GetItem(); - } - return NULL; -} - -VCMFrameListItem* -VCMFrameListTimestampOrderAsc::FindFrameListItem(FindFrameCriteria criteria, - const void* compareWith, - VCMFrameListItem* startItem) const -{ - if (startItem == NULL) - { - startItem = First(); - } - if (criteria == NULL) - { - return NULL; - } - while (startItem != NULL) - { - if (criteria(startItem->GetItem(), compareWith)) - { - return startItem; - } - startItem = Next(startItem); - } - // No frame found - return NULL; -} - -VCMFrameBuffer* -VCMFrameListTimestampOrderAsc::FindFrame(FindFrameCriteria criteria, - const void* compareWith, - VCMFrameListItem* startItem) const -{ - const VCMFrameListItem* frameListItem = FindFrameListItem(criteria, compareWith, startItem); - if (frameListItem == NULL) - { - return NULL; - } - return frameListItem->GetItem(); -} - -} - diff --git a/modules/video_coding/main/source/frame_list.h b/modules/video_coding/main/source/frame_list.h deleted file mode 100644 index cc5d053e1..000000000 --- a/modules/video_coding/main/source/frame_list.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_ - -#include "list_wrapper.h" -#include "typedefs.h" -#include - -namespace webrtc -{ - -class VCMFrameBuffer; - -typedef bool (*FindFrameCriteria)(VCMFrameBuffer*, const void*); - -class VCMFrameListItem : public ListItem -{ - friend class VCMFrameListTimestampOrderAsc; -public: - VCMFrameListItem(const VCMFrameBuffer* ptr) : ListItem(ptr) {} - ~VCMFrameListItem() {}; - - VCMFrameBuffer* GetItem() const - { return static_cast(ListItem::GetItem()); } -}; - -class VCMFrameListTimestampOrderAsc : public ListWrapper -{ -public: - VCMFrameListTimestampOrderAsc() : ListWrapper() {}; - ~VCMFrameListTimestampOrderAsc(); - - void Flush(); - - // Inserts frame in timestamp order, with the oldest timestamp first. - // Takes wrap arounds into account. - WebRtc_Word32 Insert(VCMFrameBuffer* frame); - VCMFrameBuffer* FirstFrame() const; - VCMFrameListItem* Next(VCMFrameListItem* item) const - { return static_cast(ListWrapper::Next(item)); } - VCMFrameListItem* Previous(VCMFrameListItem* item) const - { return static_cast(ListWrapper::Previous(item)); } - VCMFrameListItem* First() const - { return static_cast(ListWrapper::First()); } - VCMFrameListItem* Last() const - { return static_cast(ListWrapper::Last()); } - VCMFrameListItem* FindFrameListItem(FindFrameCriteria criteria, - const void* compareWith = NULL, - VCMFrameListItem* startItem = NULL) const; - VCMFrameBuffer* FindFrame(FindFrameCriteria criteria, - const void* compareWith = NULL, - VCMFrameListItem* startItem = NULL) const; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_ diff --git a/modules/video_coding/main/source/generic_decoder.cc b/modules/video_coding/main/source/generic_decoder.cc deleted file mode 100644 index 1fddb5222..000000000 --- a/modules/video_coding/main/source/generic_decoder.cc +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_coding.h" -#include "trace.h" -#include "generic_decoder.h" -#include "internal_defines.h" -#include "tick_time.h" - -namespace webrtc { - -VCMDecodedFrameCallback::VCMDecodedFrameCallback(VCMTiming& timing) -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_receiveCallback(NULL), -_timing(timing), -_timestampMap(kDecoderFrameMemoryLength) -{ -} - -VCMDecodedFrameCallback::~VCMDecodedFrameCallback() -{ - delete &_critSect; -} - -void VCMDecodedFrameCallback::SetUserReceiveCallback(VCMReceiveCallback* receiveCallback) -{ - CriticalSectionScoped cs(_critSect); - _receiveCallback = receiveCallback; -} - -WebRtc_Word32 VCMDecodedFrameCallback::Decoded(RawImage& decodedImage) -{ - CriticalSectionScoped cs(_critSect); - VCMFrameInformation* frameInfo = static_cast(_timestampMap.Pop(decodedImage._timeStamp)); - if (frameInfo == NULL) - { - return WEBRTC_VIDEO_CODEC_ERROR; - } - - WebRtc_Word32 ret = _timing.StopDecodeTimer(decodedImage._timeStamp, frameInfo->decodeStartTimeMs, VCMTickTime::MillisecondTimestamp()); - - if (_receiveCallback != NULL) - { - _frame.Swap(decodedImage._buffer, decodedImage._length, decodedImage._size); - _frame.SetWidth(decodedImage._width); - _frame.SetHeight(decodedImage._height); - _frame.SetTimeStamp(decodedImage._timeStamp); - _frame.SetRenderTime(frameInfo->renderTimeMs); - // Convert raw image to video frame - WebRtc_Word32 callbackReturn = _receiveCallback->FrameToRender(_frame); - if (callbackReturn < 0) - { - return callbackReturn; - } - } - if (ret < 0) - { - return ret; - } - return WEBRTC_VIDEO_CODEC_OK; -} - -WebRtc_Word32 -VCMDecodedFrameCallback::ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId) -{ - CriticalSectionScoped cs(_critSect); - if (_receiveCallback != NULL) - { - return _receiveCallback->ReceivedDecodedReferenceFrame(pictureId); - } - return -1; -} - -WebRtc_Word32 -VCMDecodedFrameCallback::ReceivedDecodedFrame(const WebRtc_UWord64 pictureId) -{ - _lastReceivedPictureID = pictureId; - return 0; -} - -WebRtc_UWord64 VCMDecodedFrameCallback::LastReceivedPictureID() const -{ - return _lastReceivedPictureID; -} - -WebRtc_Word32 VCMDecodedFrameCallback::Map(WebRtc_UWord32 timestamp, VCMFrameInformation* frameInfo) -{ - CriticalSectionScoped cs(_critSect); - return _timestampMap.Add(timestamp, frameInfo); -} - -WebRtc_Word32 VCMDecodedFrameCallback::Pop(WebRtc_UWord32 timestamp) -{ - CriticalSectionScoped cs(_critSect); - if (_timestampMap.Pop(timestamp) == NULL) - { - return VCM_GENERAL_ERROR; - } - return VCM_OK; -} - -VCMGenericDecoder::VCMGenericDecoder(VideoDecoder& decoder, WebRtc_Word32 id, bool isExternal) -: -_id(id), -_callback(NULL), -_frameInfos(), -_nextFrameInfoIdx(0), -_decoder(decoder), -_codecType(kVideoCodecUnknown), -_isExternal(isExternal), -_requireKeyFrame(false), -_keyFrameDecoded(false) -{ -} - -VCMGenericDecoder::~VCMGenericDecoder() -{ -} - -WebRtc_Word32 VCMGenericDecoder::InitDecode(const VideoCodec* settings, WebRtc_Word32 numberOfCores, bool requireKeyFrame) -{ - _requireKeyFrame = requireKeyFrame; - _keyFrameDecoded = false; - _codecType = settings->codecType; - - return _decoder.InitDecode(settings, numberOfCores); -} - -WebRtc_Word32 VCMGenericDecoder::Decode(const VCMEncodedFrame& frame) -{ - if (_requireKeyFrame && - !_keyFrameDecoded && - frame.FrameType() != kVideoFrameKey && - frame.FrameType() != kVideoFrameGolden) - { - // Require key frame is enabled, meaning that one key frame must be decoded - // before we can decode delta frames. - return VCM_CODEC_ERROR; - } - _frameInfos[_nextFrameInfoIdx].decodeStartTimeMs = VCMTickTime::MillisecondTimestamp(); - _frameInfos[_nextFrameInfoIdx].renderTimeMs = frame.RenderTimeMs(); - _callback->Map(frame.TimeStamp(), &_frameInfos[_nextFrameInfoIdx]); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_id), - "Decoding timestamp %u", - frame.TimeStamp()); - - _nextFrameInfoIdx = (_nextFrameInfoIdx + 1) % kDecoderFrameMemoryLength; - - WebRtc_Word32 ret = _decoder.Decode(frame.EncodedImage(), - frame.MissingFrame(), - frame.CodecSpecificInfo(), - frame.RenderTimeMs()); - - if (ret < WEBRTC_VIDEO_CODEC_OK) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), "Decoder error: %d\n", ret); - _callback->Pop(frame.TimeStamp()); - return ret; - } - // Update the key frame decoded variable so that we know whether or not we've decoded a key frame since reset. - _keyFrameDecoded = (frame.FrameType() == kVideoFrameKey || frame.FrameType() == kVideoFrameGolden); - return ret; -} - -WebRtc_Word32 -VCMGenericDecoder::Release() -{ - _keyFrameDecoded = false; - return _decoder.Release(); -} - -WebRtc_Word32 VCMGenericDecoder::Reset() -{ - _keyFrameDecoded = false; - return _decoder.Reset(); -} - -WebRtc_Word32 VCMGenericDecoder::SetCodecConfigParameters(const WebRtc_UWord8* buffer, WebRtc_Word32 size) -{ - return _decoder.SetCodecConfigParameters(buffer, size); -} - -WebRtc_Word32 VCMGenericDecoder::RegisterDecodeCompleteCallback(VCMDecodedFrameCallback* callback) -{ - _callback = callback; - return _decoder.RegisterDecodeCompleteCallback(callback); -} - -bool VCMGenericDecoder::External() const -{ - return _isExternal; -} - -} diff --git a/modules/video_coding/main/source/generic_decoder.h b/modules/video_coding/main/source/generic_decoder.h deleted file mode 100644 index 956905572..000000000 --- a/modules/video_coding/main/source/generic_decoder.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_ - -#include "timing.h" -#include "timestamp_map.h" -#include "video_codec_interface.h" -#include "encoded_frame.h" -#include "module_common_types.h" - -namespace webrtc -{ - -class VCMReceiveCallback; - -enum { kDecoderFrameMemoryLength = 10 }; - -struct VCMFrameInformation -{ - WebRtc_Word64 renderTimeMs; - WebRtc_Word64 decodeStartTimeMs; - void* userData; -}; - -class VCMDecodedFrameCallback : public DecodedImageCallback -{ -public: - VCMDecodedFrameCallback(VCMTiming& timing); - virtual ~VCMDecodedFrameCallback(); - void SetUserReceiveCallback(VCMReceiveCallback* receiveCallback); - - virtual WebRtc_Word32 Decoded(RawImage& decodedImage); - virtual WebRtc_Word32 ReceivedDecodedReferenceFrame(const WebRtc_UWord64 pictureId); - virtual WebRtc_Word32 ReceivedDecodedFrame(const WebRtc_UWord64 pictureId); - - WebRtc_UWord64 LastReceivedPictureID() const; - - WebRtc_Word32 Map(WebRtc_UWord32 timestamp, VCMFrameInformation* frameInfo); - WebRtc_Word32 Pop(WebRtc_UWord32 timestamp); - -private: - CriticalSectionWrapper& _critSect; - VideoFrame _frame; - VCMReceiveCallback* _receiveCallback; - VCMTiming& _timing; - VCMTimestampMap _timestampMap; - WebRtc_UWord64 _lastReceivedPictureID; -}; - - -class VCMGenericDecoder -{ - friend class VCMCodecDataBase; -public: - VCMGenericDecoder(VideoDecoder& decoder, WebRtc_Word32 id = 0, bool isExternal = false); - ~VCMGenericDecoder(); - - /** - * Initialize the decoder with the information from the VideoCodec - */ - WebRtc_Word32 InitDecode(const VideoCodec* settings, - WebRtc_Word32 numberOfCores, - bool requireKeyFrame); - - /** - * Decode to a raw I420 frame, - * - * inputVideoBuffer reference to encoded video frame - */ - WebRtc_Word32 Decode(const VCMEncodedFrame& inputFrame); - - /** - * Free the decoder memory - */ - WebRtc_Word32 Release(); - - /** - * Reset the decoder state, prepare for a new call - */ - WebRtc_Word32 Reset(); - - /** - * Codec configuration data sent out-of-band, i.e. in SIP call setup - * - * buffer pointer to the configuration data - * size the size of the configuration data in bytes - */ - WebRtc_Word32 SetCodecConfigParameters(const WebRtc_UWord8* /*buffer*/, - WebRtc_Word32 /*size*/); - - WebRtc_Word32 RegisterDecodeCompleteCallback(VCMDecodedFrameCallback* callback); - - bool External() const; - -protected: - - WebRtc_Word32 _id; - VCMDecodedFrameCallback* _callback; - VCMFrameInformation _frameInfos[kDecoderFrameMemoryLength]; - WebRtc_UWord32 _nextFrameInfoIdx; - VideoDecoder& _decoder; - VideoCodecType _codecType; - bool _isExternal; - bool _requireKeyFrame; - bool _keyFrameDecoded; - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_GENERIC_DECODER_H_ diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc deleted file mode 100644 index 44d287086..000000000 --- a/modules/video_coding/main/source/generic_encoder.cc +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "encoded_frame.h" -#include "generic_encoder.h" -#include "media_optimization.h" -#include "../../../../engine_configurations.h" - -namespace webrtc { - -//#define DEBUG_ENCODER_BIT_STREAM - -VCMGenericEncoder::VCMGenericEncoder(VideoEncoder& encoder, bool internalSource /*= false*/) -: -_encoder(encoder), -_codecType(kVideoCodecUnknown), -_VCMencodedFrameCallback(NULL), -_bitRate(0), -_frameRate(0), -_internalSource(false) -{ -} - - -VCMGenericEncoder::~VCMGenericEncoder() -{ -} - -WebRtc_Word32 -VCMGenericEncoder::Reset() -{ - _bitRate = 0; - _frameRate = 0; - _VCMencodedFrameCallback = NULL; - return _encoder.Reset(); -} - -WebRtc_Word32 VCMGenericEncoder::Release() -{ - _bitRate = 0; - _frameRate = 0; - _VCMencodedFrameCallback = NULL; - return _encoder.Release(); -} - -WebRtc_Word32 -VCMGenericEncoder::InitEncode(const VideoCodec* settings, WebRtc_Word32 numberOfCores, WebRtc_UWord32 maxPayloadSize) -{ - _bitRate = settings->startBitrate; - _frameRate = settings->maxFramerate; - _codecType = settings->codecType; - if (_VCMencodedFrameCallback != NULL) - { - _VCMencodedFrameCallback->SetCodecType(_codecType); - } - return _encoder.InitEncode(settings, numberOfCores, maxPayloadSize); -} - -WebRtc_Word32 -VCMGenericEncoder::Encode(const VideoFrame& inputFrame, - const CodecSpecificInfo* codecSpecificInfo, - FrameType frameType) -{ - RawImage rawImage(inputFrame.Buffer(), inputFrame.Length(), inputFrame.Size()); - rawImage._width = inputFrame.Width(); - rawImage._height = inputFrame.Height(); - rawImage._timeStamp = inputFrame.TimeStamp(); - - WebRtc_Word32 ret = _encoder.Encode(rawImage, codecSpecificInfo, VCMEncodedFrame::ConvertFrameType(frameType)); - - return ret; -} - -WebRtc_Word32 -VCMGenericEncoder::SetPacketLoss(WebRtc_Word32 packetLoss) -{ - return _encoder.SetPacketLoss(packetLoss); -} - -WebRtc_Word32 -VCMGenericEncoder::SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate) -{ - WebRtc_Word32 ret = _encoder.SetRates(newBitRate, frameRate); - if (ret < 0) - { - return ret; - } - _bitRate = newBitRate; - _frameRate = frameRate; - return VCM_OK; -} - -WebRtc_Word32 -VCMGenericEncoder::CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size) -{ - WebRtc_Word32 ret = _encoder.CodecConfigParameters(buffer, size); - if (ret < 0) - { - return ret; - } - return ret; -} - -WebRtc_UWord32 VCMGenericEncoder::BitRate() const -{ - return _bitRate; -} - -WebRtc_UWord32 VCMGenericEncoder::FrameRate() const -{ - return _frameRate; -} - -WebRtc_Word32 -VCMGenericEncoder::SetPeriodicKeyFrames(bool enable) -{ - return _encoder.SetPeriodicKeyFrames(enable); -} - -WebRtc_Word32 -VCMGenericEncoder::RequestFrame(FrameType frameType) -{ - RawImage image; - return _encoder.Encode(image, NULL, VCMEncodedFrame::ConvertFrameType(frameType)); -} - -WebRtc_Word32 -VCMGenericEncoder::RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback) -{ - _VCMencodedFrameCallback = VCMencodedFrameCallback; - - _VCMencodedFrameCallback->SetCodecType(_codecType); - _VCMencodedFrameCallback->SetInternalSource(_internalSource); - return _encoder.RegisterEncodeCompleteCallback(_VCMencodedFrameCallback); -} - -bool -VCMGenericEncoder::InternalSource() const -{ - return _internalSource; -} - - /*************************** - * Callback Implementation - ***************************/ -VCMEncodedFrameCallback::VCMEncodedFrameCallback(): -_sendCallback(), -_encodedBytes(0), -_payloadType(0), -_bitStreamAfterEncoder(NULL) -{ -#ifdef DEBUG_ENCODER_BIT_STREAM - _bitStreamAfterEncoder = fopen("encoderBitStream.bit", "wb"); -#endif -} - -VCMEncodedFrameCallback::~VCMEncodedFrameCallback() -{ -#ifdef DEBUG_ENCODER_BIT_STREAM - fclose(_bitStreamAfterEncoder); -#endif -} - -WebRtc_Word32 -VCMEncodedFrameCallback::SetTransportCallback(VCMPacketizationCallback* transport) -{ - _sendCallback = transport; - return VCM_OK; -} - -WebRtc_Word32 -VCMEncodedFrameCallback::Encoded( - EncodedImage &encodedImage, - const CodecSpecificInfo* codecSpecificInfo, - const RTPFragmentationHeader* fragmentationHeader) -{ - FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType); - - WebRtc_UWord32 encodedBytes = 0; - if (_sendCallback != NULL) - { - encodedBytes = encodedImage._length; - - if (_bitStreamAfterEncoder != NULL) - { - fwrite(encodedImage._buffer, 1, encodedImage._length, _bitStreamAfterEncoder); - } - - RTPVideoTypeHeader rtpTypeHeader; - RTPVideoTypeHeader* rtpTypeHeaderPtr = &rtpTypeHeader; - if (codecSpecificInfo) - { - CopyCodecSpecific(*codecSpecificInfo, &rtpTypeHeaderPtr); - } - else - { - rtpTypeHeaderPtr = NULL; - } - - WebRtc_Word32 callbackReturn = _sendCallback->SendData( - frameType, - _payloadType, - encodedImage._timeStamp, - encodedImage._buffer, - encodedBytes, - *fragmentationHeader, - rtpTypeHeaderPtr); - if (callbackReturn < 0) - { - return callbackReturn; - } - } - else - { - return VCM_UNINITIALIZED; - } - _encodedBytes = encodedBytes; - _mediaOpt->UpdateWithEncodedData(_encodedBytes, frameType); - if (_internalSource) - { - return _mediaOpt->DropFrame(); // Signal to encoder to drop next frame - } - - return VCM_OK; -} - -WebRtc_UWord32 -VCMEncodedFrameCallback::EncodedBytes() -{ - return _encodedBytes; -} - -void -VCMEncodedFrameCallback::SetMediaOpt(VCMMediaOptimization *mediaOpt) -{ - _mediaOpt = mediaOpt; -} - -void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info, - RTPVideoTypeHeader** rtp) { - switch (info.codecType) - { - case kVideoCodecVP8: { - if (info.codecSpecific.VP8.pictureId < 0) - { - (*rtp)->VP8.pictureId = kNoPictureId; - } - else - { - (*rtp)->VP8.pictureId = info.codecSpecific.VP8.pictureId; - } - (*rtp)->VP8.nonReference = info.codecSpecific.VP8.nonReference; - return; - } - default: { - // No codec specific info. Change RTP header pointer to NULL. - *rtp = NULL; - return; - } - - } -} - -} // namespace webrtc diff --git a/modules/video_coding/main/source/generic_encoder.h b/modules/video_coding/main/source/generic_encoder.h deleted file mode 100644 index 26d19dff5..000000000 --- a/modules/video_coding/main/source/generic_encoder.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_ - -#include "video_codec_interface.h" - -#include - -namespace webrtc -{ - -class VCMMediaOptimization; - -/*************************************/ -/* VCMEncodeFrameCallback class */ -/***********************************/ -class VCMEncodedFrameCallback : public EncodedImageCallback -{ -public: - VCMEncodedFrameCallback(); - virtual ~VCMEncodedFrameCallback(); - - /* - * Callback implementation - codec encode complete - */ - WebRtc_Word32 Encoded( - EncodedImage& encodedImage, - const CodecSpecificInfo* codecSpecificInfo = NULL, - const RTPFragmentationHeader* fragmentationHeader = NULL); - /* - * Get number of encoded bytes - */ - WebRtc_UWord32 EncodedBytes(); - /* - * Callback implementation - generic encoder encode complete - */ - WebRtc_Word32 SetTransportCallback(VCMPacketizationCallback* transport); - /** - * Set media Optimization - */ - void SetMediaOpt (VCMMediaOptimization* mediaOpt); - - void SetPayloadType(WebRtc_UWord8 payloadType) { _payloadType = payloadType; }; - void SetCodecType(VideoCodecType codecType) {_codecType = codecType;}; - void SetInternalSource(bool internalSource) { _internalSource = internalSource; }; - -private: - /* - * Map information from info into rtp. If no relevant information is found - * in info, rtp is set to NULL. - */ - static void CopyCodecSpecific(const CodecSpecificInfo& info, - RTPVideoTypeHeader** rtp); - - VCMPacketizationCallback* _sendCallback; - VCMMediaOptimization* _mediaOpt; - WebRtc_UWord32 _encodedBytes; - WebRtc_UWord8 _payloadType; - VideoCodecType _codecType; - bool _internalSource; - FILE* _bitStreamAfterEncoder; -};// end of VCMEncodeFrameCallback class - - -/******************************/ -/* VCMGenericEncoder class */ -/******************************/ -class VCMGenericEncoder -{ - friend class VCMCodecDataBase; -public: - VCMGenericEncoder(VideoEncoder& encoder, bool internalSource = false); - ~VCMGenericEncoder(); - /** - * Reset the encoder state, prepare for a new call - */ - WebRtc_Word32 Reset(); - /** - * Free encoder memory - */ - WebRtc_Word32 Release(); - /** - * Initialize the encoder with the information from the VideoCodec - */ - WebRtc_Word32 InitEncode(const VideoCodec* settings, - WebRtc_Word32 numberOfCores, - WebRtc_UWord32 maxPayloadSize); - /** - * Encode raw image - * inputFrame : Frame containing raw image - * codecSpecificInfo : Specific codec data - * cameraFrameRate : request or information from the remote side - * frameType : The requested frame type to encode - */ - WebRtc_Word32 Encode(const VideoFrame& inputFrame, - const CodecSpecificInfo* codecSpecificInfo, - FrameType frameType); - /** - * Set new target bit rate and frame rate - * Return Value: new bit rate if OK, otherwise <0s - */ - WebRtc_Word32 SetRates(WebRtc_UWord32 newBitRate, WebRtc_UWord32 frameRate); - /** - * Set a new packet loss rate - */ - WebRtc_Word32 SetPacketLoss(WebRtc_Word32 packetLoss); - WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size); - /** - * Register a transport callback which will be called to deliver the encoded buffers - */ - WebRtc_Word32 RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback); - /** - * Get encoder bit rate - */ - WebRtc_UWord32 BitRate() const; - /** - * Get encoder frame rate - */ - WebRtc_UWord32 FrameRate() const; - - WebRtc_Word32 SetPeriodicKeyFrames(bool enable); - - WebRtc_Word32 RequestFrame(FrameType frameType); - - bool InternalSource() const; - -private: - VideoEncoder& _encoder; - VideoCodecType _codecType; - VCMEncodedFrameCallback* _VCMencodedFrameCallback; - WebRtc_UWord32 _bitRate; - WebRtc_UWord32 _frameRate; - bool _internalSource; -}; // end of VCMGenericEncoder class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_GENERIC_ENCODER_H_ diff --git a/modules/video_coding/main/source/inter_frame_delay.cc b/modules/video_coding/main/source/inter_frame_delay.cc deleted file mode 100644 index f3bc0134b..000000000 --- a/modules/video_coding/main/source/inter_frame_delay.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "inter_frame_delay.h" -#include "tick_time.h" - -namespace webrtc { - -VCMInterFrameDelay::VCMInterFrameDelay() -{ - Reset(); -} - -// Resets the delay estimate -void -VCMInterFrameDelay::Reset() -{ - _zeroWallClock = VCMTickTime::MillisecondTimestamp(); - _wrapArounds = 0; - _prevWallClock = 0; - _prevTimestamp = 0; - _dTS = 0; -} - -// Calculates the delay of a frame with the given timestamp. -// This method is called when the frame is complete. -bool -VCMInterFrameDelay::CalculateDelay(WebRtc_UWord32 timestamp, - WebRtc_Word64 *delay, - WebRtc_Word64 currentWallClock /* = -1 */) -{ - if (currentWallClock <= -1) - { - currentWallClock = VCMTickTime::MillisecondTimestamp(); - } - - if (_prevWallClock == 0) - { - // First set of data, initialization, wait for next frame - _prevWallClock = currentWallClock; - _prevTimestamp = timestamp; - *delay = 0; - return true; - } - - WebRtc_Word32 prevWrapArounds = _wrapArounds; - CheckForWrapArounds(timestamp); - - // This will be -1 for backward wrap arounds and +1 for forward wrap arounds - WebRtc_Word32 wrapAroundsSincePrev = _wrapArounds - prevWrapArounds; - - // Account for reordering in jitter variance estimate in the future? - // Note that this also captures incomplete frames which are grabbed - // for decoding after a later frame has been complete, i.e. real - // packet losses. - if ((wrapAroundsSincePrev == 0 && timestamp < _prevTimestamp) || wrapAroundsSincePrev < 0) - { - *delay = 0; - return false; - } - - // Compute the compensated timestamp difference and convert it to ms and - // round it to closest integer. - _dTS = static_cast((timestamp + wrapAroundsSincePrev * - (static_cast(1)<<32) - _prevTimestamp) / 90.0 + 0.5); - - // frameDelay is the difference of dT and dTS -- i.e. the difference of - // the wall clock time difference and the timestamp difference between - // two following frames. - *delay = static_cast(currentWallClock - _prevWallClock - _dTS); - - _prevTimestamp = timestamp; - _prevWallClock = currentWallClock; - - return true; -} - -// Returns the current difference between incoming timestamps -WebRtc_UWord32 VCMInterFrameDelay::CurrentTimeStampDiffMs() const -{ - if (_dTS < 0) - { - return 0; - } - return static_cast(_dTS); -} - -// Investigates if the timestamp clock has overflowed since the last timestamp and -// keeps track of the number of wrap arounds since reset. -void -VCMInterFrameDelay::CheckForWrapArounds(WebRtc_UWord32 timestamp) -{ - if (timestamp < _prevTimestamp) - { - // This difference will probably be less than -2^31 if we have had a wrap around - // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is cast to a Word32, - // it should be positive. - if (static_cast(timestamp - _prevTimestamp) > 0) - { - // Forward wrap around - _wrapArounds++; - } - } - // This difference will probably be less than -2^31 if we have had a backward wrap around. - // Since it is cast to a Word32, it should be positive. - else if (static_cast(_prevTimestamp - timestamp) > 0) - { - // Backward wrap around - _wrapArounds--; - } -} - -} diff --git a/modules/video_coding/main/source/inter_frame_delay.h b/modules/video_coding/main/source/inter_frame_delay.h deleted file mode 100644 index 7a976a4f0..000000000 --- a/modules/video_coding/main/source/inter_frame_delay.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_ -#define WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_ - -#include "typedefs.h" - -namespace webrtc -{ - -class VCMInterFrameDelay -{ -public: - VCMInterFrameDelay(); - - // Resets the estimate. Zeros are given as parameters. - void Reset(); - - // Calculates the delay of a frame with the given timestamp. - // This method is called when the frame is complete. - // - // Input: - // - timestamp : RTP timestamp of a received frame - // - *delay : Pointer to memory where the result should be stored - // - currentWallClock : The current time in milliseconds. - // Should be -1 for normal operation, only used for testing. - // Return value : true if OK, false when reordered timestamps - bool CalculateDelay(WebRtc_UWord32 timestamp, - WebRtc_Word64 *delay, - WebRtc_Word64 currentWallClock = -1); - - // Returns the current difference between incoming timestamps - // - // Return value : Wrap-around compensated difference between incoming - // timestamps. - WebRtc_UWord32 CurrentTimeStampDiffMs() const; - -private: - // Controls if the RTP timestamp counter has had a wrap around - // between the current and the previously received frame. - // - // Input: - // - timestmap : RTP timestamp of the current frame. - void CheckForWrapArounds(WebRtc_UWord32 timestamp); - - WebRtc_Word64 _zeroWallClock; // Local timestamp of the first video packet received - WebRtc_Word32 _wrapArounds; // Number of wrapArounds detected - // The previous timestamp passed to the delay estimate - WebRtc_UWord32 _prevTimestamp; - // The previous wall clock timestamp used by the delay estimate - WebRtc_Word64 _prevWallClock; - // Wrap-around compensated difference between incoming timestamps - WebRtc_Word64 _dTS; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_INTER_FRAME_DELAY_H_ diff --git a/modules/video_coding/main/source/internal_defines.h b/modules/video_coding/main/source/internal_defines.h deleted file mode 100644 index 781e668f1..000000000 --- a/modules/video_coding/main/source/internal_defines.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_ - -#include "typedefs.h" - -namespace webrtc -{ - -#define MASK_32_BITS(x) (0xFFFFFFFF & (x)) - -inline WebRtc_UWord32 MaskWord64ToUWord32(WebRtc_Word64 w64) -{ - return static_cast(MASK_32_BITS(w64)); -} - -#define VCM_MAX(a, b) ((a) > (b)) ? (a) : (b) -#define VCM_MIN(a, b) ((a) < (b)) ? (a) : (b) - -#define VCM_DEFAULT_CODEC_WIDTH 352 -#define VCM_DEFAULT_CODEC_HEIGHT 288 -#define VCM_DEFAULT_FRAME_RATE 30 -#define VCM_MIN_BITRATE 30 - -// Helper macros for creating the static codec list -#define VCM_NO_CODEC_IDX -1 -#ifdef VIDEOCODEC_VP8 - #define VCM_VP8_IDX VCM_NO_CODEC_IDX + 1 -#else - #define VCM_VP8_IDX VCM_NO_CODEC_IDX -#endif -#ifdef VIDEOCODEC_I420 - #define VCM_I420_IDX VCM_VP8_IDX + 1 -#else - #define VCM_I420_IDX VCM_VP8_IDX -#endif -#define VCM_NUM_VIDEO_CODECS_AVAILABLE VCM_I420_IDX + 1 - -#define VCM_NO_RECEIVER_ID 0 - -inline WebRtc_Word32 VCMId(const WebRtc_Word32 vcmId, const WebRtc_Word32 receiverId = 0) -{ - return static_cast((vcmId << 16) + receiverId); -} - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_INTERNAL_DEFINES_H_ diff --git a/modules/video_coding/main/source/jitter_buffer.cc b/modules/video_coding/main/source/jitter_buffer.cc deleted file mode 100644 index d4d7ab9aa..000000000 --- a/modules/video_coding/main/source/jitter_buffer.cc +++ /dev/null @@ -1,2015 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "critical_section_wrapper.h" - -#include "frame_buffer.h" -#include "inter_frame_delay.h" -#include "internal_defines.h" -#include "jitter_buffer.h" -#include "jitter_buffer_common.h" -#include "jitter_estimator.h" -#include "media_optimization.h" // hybrid NACK/FEC thresholds. -#include "packet.h" - -#include "event.h" -#include "trace.h" -#include "tick_time.h" -#include "list_wrapper.h" - -#include -#include -#include - -#if defined(_WIN32) - // VS 2005: Don't warn for default initialized arrays. See help for more info. - #pragma warning(disable:4351) -#endif - -namespace webrtc { - -// Criteria used when searching for frames in the frame buffer list -bool -VCMJitterBuffer::FrameEqualTimestamp(VCMFrameBuffer* frame, const void* timestamp) -{ - if (timestamp == NULL) - { - return false; - } - return (*static_cast(timestamp)) == frame->TimeStamp(); -} - -bool -VCMJitterBuffer::CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame, - const void* /*notUsed*/) -{ - const VCMFrameBufferStateEnum state = frame->GetState(); - // We can decode key frame or decodable/complete frames. - return (frame->FrameType() == kVideoFrameKey) && - ((state == kStateComplete) - || (state == kStateDecodable)); -} - -// Constructor -VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId, - bool master) : - _vcmId(vcmId), - _receiverId(receiverId), - _running(false), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _master(master), - _frameEvent(), - _packetEvent(), - _maxNumberOfFrames(kStartNumberOfFrames), - _frameBuffers(), - _frameBuffersTSOrder(), - _lastDecodedSeqNum(), - _lastDecodedTimeStamp(-1), - _receiveStatistics(), - _incomingFrameRate(0), - _incomingFrameCount(0), - _timeLastIncomingFrameCount(0), - _incomingBitCount(0), - _dropCount(0), - _numConsecutiveOldFrames(0), - _numConsecutiveOldPackets(0), - _jitterEstimate(vcmId, receiverId), - _rttMs(0), - _nackMode(kNoNack), - _NACKSeqNum(), - _NACKSeqNumLength(0), - _missingMarkerBits(false), - _firstPacket(true) -{ - memset(_frameBuffers, 0, sizeof(_frameBuffers)); - memset(_receiveStatistics, 0, sizeof(_receiveStatistics)); - _lastDecodedSeqNum = -1; - memset(_NACKSeqNumInternal, -1, sizeof(_NACKSeqNumInternal)); - - for (int i = 0; i< kStartNumberOfFrames; i++) - { - _frameBuffers[i] = new VCMFrameBuffer(); - } -} - -// Destructor -VCMJitterBuffer::~VCMJitterBuffer() -{ - Stop(); - for (int i = 0; i< kMaxNumberOfFrames; i++) - { - if (_frameBuffers[i]) - { - delete _frameBuffers[i]; - } - } - delete &_critSect; -} - -VCMJitterBuffer& -VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs) -{ - if (this != &rhs) - { - _critSect.Enter(); - rhs._critSect.Enter(); - _vcmId = rhs._vcmId; - _receiverId = rhs._receiverId; - _running = rhs._running; - _master = !rhs._master; - _maxNumberOfFrames = rhs._maxNumberOfFrames; - _lastDecodedTimeStamp = rhs._lastDecodedTimeStamp; - _incomingFrameRate = rhs._incomingFrameRate; - _incomingFrameCount = rhs._incomingFrameCount; - _timeLastIncomingFrameCount = rhs._timeLastIncomingFrameCount; - _incomingBitCount = rhs._incomingBitCount; - _dropCount = rhs._dropCount; - _numConsecutiveOldFrames = rhs._numConsecutiveOldFrames; - _numConsecutiveOldPackets = rhs._numConsecutiveOldPackets; - _jitterEstimate = rhs._jitterEstimate; - _delayEstimate = rhs._delayEstimate; - _waitingForCompletion = rhs._waitingForCompletion; - _nackMode = rhs._nackMode; - _rttMs = rhs._rttMs; - _NACKSeqNumLength = rhs._NACKSeqNumLength; - _missingMarkerBits = rhs._missingMarkerBits; - _firstPacket = rhs._firstPacket; - _lastDecodedSeqNum = rhs._lastDecodedSeqNum; - memcpy(_receiveStatistics, rhs._receiveStatistics, sizeof(_receiveStatistics)); - memcpy(_NACKSeqNumInternal, rhs._NACKSeqNumInternal, sizeof(_NACKSeqNumInternal)); - memcpy(_NACKSeqNum, rhs._NACKSeqNum, sizeof(_NACKSeqNum)); - for (int i = 0; i < kMaxNumberOfFrames; i++) - { - if (_frameBuffers[i] != NULL) - { - delete _frameBuffers[i]; - _frameBuffers[i] = NULL; - } - } - while(_frameBuffersTSOrder.Erase(_frameBuffersTSOrder.First()) != -1) { } - for (int i = 0; i < _maxNumberOfFrames; i++) - { - _frameBuffers[i] = new VCMFrameBuffer(*(rhs._frameBuffers[i])); - if (_frameBuffers[i]->Length() > 0) - { - _frameBuffersTSOrder.Insert(_frameBuffers[i]); - } - } - rhs._critSect.Leave(); - _critSect.Leave(); - } - return *this; -} - -WebRtc_UWord32 -VCMJitterBuffer::LatestTimestamp(const WebRtc_UWord32 existingTimestamp, - const WebRtc_UWord32 newTimestamp) -{ - bool wrap = (newTimestamp < 0x0000ffff && existingTimestamp > 0xffff0000) || - (newTimestamp > 0xffff0000 && existingTimestamp < 0x0000ffff); - if (existingTimestamp > newTimestamp && !wrap) - { - return existingTimestamp; - } - else if (existingTimestamp <= newTimestamp && !wrap) - { - return newTimestamp; - } - else if (existingTimestamp < newTimestamp && wrap) - { - return existingTimestamp; - } - else - { - return newTimestamp; - } -} - -// Start jitter buffer -void -VCMJitterBuffer::Start() -{ - CriticalSectionScoped cs(_critSect); - _running = true; - _incomingFrameCount = 0; - _incomingFrameRate = 0; - _incomingBitCount = 0; - _timeLastIncomingFrameCount = VCMTickTime::MillisecondTimestamp(); - memset(_receiveStatistics, 0, sizeof(_receiveStatistics)); - - _numConsecutiveOldFrames = 0; - _numConsecutiveOldPackets = 0; - - _frameEvent.Reset(); // start in a non-signaled state - _packetEvent.Reset(); // start in a non-signaled state - _waitingForCompletion.frameSize = 0; - _waitingForCompletion.timestamp = 0; - _waitingForCompletion.latestPacketTime = -1; - _missingMarkerBits = false; - _firstPacket = true; - _NACKSeqNumLength = 0; - _rttMs = 0; - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, - _receiverId), "JB(0x%x): Jitter buffer: start", this); -} - - -// Stop jitter buffer -void -VCMJitterBuffer::Stop() -{ - _critSect.Enter(); - _running = false; - _lastDecodedTimeStamp = -1; - _lastDecodedSeqNum = -1; - _frameBuffersTSOrder.Flush(); - for (int i = 0; i < kMaxNumberOfFrames; i++) - { - if (_frameBuffers[i] != NULL) - { - static_cast(_frameBuffers[i])->SetState(kStateFree); - } - } - - _critSect.Leave(); - _frameEvent.Set(); // Make sure we exit from trying to get a frame to decoder - _packetEvent.Set(); // Make sure we exit from trying to get a sequence number - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, - _receiverId), "JB(0x%x): Jitter buffer: stop", this); -} - -bool -VCMJitterBuffer::Running() const -{ - CriticalSectionScoped cs(_critSect); - return _running; -} - -// Flush jitter buffer -void -VCMJitterBuffer::Flush() -{ - CriticalSectionScoped cs(_critSect); - FlushInternal(); -} - -// Must be called under the critical section _critSect -void -VCMJitterBuffer::FlushInternal() -{ - // Erase all frames from the sorted list and set their state to free. - _frameBuffersTSOrder.Flush(); - for (int i = 0; i < _maxNumberOfFrames; i++) - { - ReleaseFrameInternal(_frameBuffers[i]); - } - _lastDecodedSeqNum = -1; - _lastDecodedTimeStamp = -1; - - _frameEvent.Reset(); - _packetEvent.Reset(); - - _numConsecutiveOldFrames = 0; - _numConsecutiveOldPackets = 0; - - // Also reset the jitter and delay estimates - _jitterEstimate.Reset(); - _delayEstimate.Reset(); - - _waitingForCompletion.frameSize = 0; - _waitingForCompletion.timestamp = 0; - _waitingForCompletion.latestPacketTime = -1; - - _missingMarkerBits = false; - _firstPacket = true; - - _NACKSeqNumLength = 0; - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, - _receiverId), "JB(0x%x): Jitter buffer: flush", this); -} - -// Set the frame state to free and remove it from the sorted -// frame list. Must be called from inside the critical section _critSect. -void -VCMJitterBuffer::ReleaseFrameInternal(VCMFrameBuffer* frame) -{ - if (frame != NULL) - { - frame->SetState(kStateFree); - } -} - -// Update frame state (set as complete if conditions are met) -// Doing it here increases the degree of freedom for e.g. future -// reconstructability of separate layers. Must be called under the -// critical section _critSect. -void -VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame) -{ - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), "JB(0x%x) FB(0x%x): " - "UpdateFrameState NULL frame pointer", this, frame); - return; - } - - int length = frame->Length(); - if (_master) - { - // Only trace the primary jitter buffer to make it possible to parse and plot the trace file. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "JB(0x%x) FB(0x%x): Complete frame added to jitter buffer, size:%d type %d", - this, frame,length,frame->FrameType()); - } - - if (length != 0 && !frame->GetCountedFrame()) - { - // ignore Ack frames - _incomingFrameCount++; - frame->SetCountedFrame(true); - } - - // Check if we should drop frame - // an old complete frame can arrive too late - if(_lastDecodedTimeStamp > 0 && - LatestTimestamp(static_cast(_lastDecodedTimeStamp), - frame->TimeStamp()) == _lastDecodedTimeStamp) - { - // Frame is older than the latest decoded frame, drop it. - // This will trigger a release in CleanUpSizeZeroFrames later. - frame->Reset(); - frame->SetState(kStateEmpty); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "JB(0x%x) FB(0x%x): Dropping old frame in Jitter buffer", this, frame); - _dropCount++; - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Jitter buffer drop count: %d, consecutive drops: %u", _dropCount, _numConsecutiveOldFrames); - // Flush() if this happens consistently. - _numConsecutiveOldFrames++; - if (_numConsecutiveOldFrames > kMaxConsecutiveOldFrames) - { - FlushInternal(); - } - return; - } - _numConsecutiveOldFrames = 0; - frame->SetState(kStateComplete); - - - // Update receive statistics. We count all layers, thus when you use layers - // adding all key and delta frames might differ from frame count - if (frame->IsSessionComplete()) - { - switch (frame->FrameType()) - { - case kVideoFrameKey: - { - _receiveStatistics[0]++; - break; - } - case kVideoFrameDelta: - { - _receiveStatistics[1]++; - break; - } - case kVideoFrameGolden: - { - _receiveStatistics[2]++; - break; - } - case kVideoFrameAltRef: - { - _receiveStatistics[3]++; - break; - } - default: - assert(false); - - } - } - const VCMFrameListItem* oldFrameListItem = FindOldestCompleteContinuousFrame(); - VCMFrameBuffer* oldFrame = NULL; - if (oldFrameListItem != NULL) - { - oldFrame = oldFrameListItem->GetItem(); - } - - // Only signal if this is the oldest frame. - // Not necessary the case due to packet reordering or NACK. - if (!WaitForNack() || (oldFrame != NULL && oldFrame == frame)) - { - _frameEvent.Set(); - } -} - - -// Get received key and delta frames -WebRtc_Word32 -VCMJitterBuffer::GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames, - WebRtc_UWord32& receivedKeyFrames) const -{ - { - CriticalSectionScoped cs(_critSect); - receivedDeltaFrames = _receiveStatistics[1] + _receiveStatistics[3]; - receivedKeyFrames = _receiveStatistics[0] + _receiveStatistics[2]; - } - return 0; -} - -// Gets frame to use for this timestamp. If no match, get empty frame. -WebRtc_Word32 -VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMEncodedFrame*& frame) -{ - if (!_running) // don't accept incoming packets until we are started - { - return VCM_UNINITIALIZED; - } - - _critSect.Enter(); - if (LatestTimestamp(static_cast(_lastDecodedTimeStamp), packet.timestamp) - == _lastDecodedTimeStamp - && packet.sizeBytes > 0) // Make sure that old filler packets are inserted. - { - // Trying to get an old frame. - _numConsecutiveOldPackets++; - if (_numConsecutiveOldPackets > kMaxConsecutiveOldPackets) - { - FlushInternal(); - } - _critSect.Leave(); - return VCM_OLD_PACKET_ERROR; - } - _numConsecutiveOldPackets = 0; - - frame = _frameBuffersTSOrder.FindFrame(FrameEqualTimestamp, &packet.timestamp); - _critSect.Leave(); - - if (frame != NULL) - { - return VCM_OK; - } - - // No match, return empty frame - frame = GetEmptyFrame(); - if (frame != NULL) - { - return VCM_OK; - } - // No free frame! Try to reclaim some... - _critSect.Enter(); - RecycleFramesUntilKeyFrame(); - _critSect.Leave(); - - frame = GetEmptyFrame(); - if (frame != NULL) - { - return VCM_OK; - } - return VCM_JITTER_BUFFER_ERROR; -} - -// Deprecated! Kept for testing purposes. -VCMEncodedFrame* -VCMJitterBuffer::GetFrame(const VCMPacket& packet) -{ - VCMEncodedFrame* frame = NULL; - if (GetFrame(packet, frame) < 0) - { - return NULL; - } - return frame; -} - -// Get empty frame, creates new (i.e. increases JB size) if necessary -VCMFrameBuffer* -VCMJitterBuffer::GetEmptyFrame() -{ - if (!_running) // don't accept incoming packets until we are started - { - return NULL; - } - - _critSect.Enter(); - - for (int i = 0; i <_maxNumberOfFrames; ++i) - { - if (kStateFree == _frameBuffers[i]->GetState()) - { - // found a free buffer - _frameBuffers[i]->SetState(kStateEmpty); - _critSect.Leave(); - return _frameBuffers[i]; - } - } - - // Check if we can increase JB size - if (_maxNumberOfFrames < kMaxNumberOfFrames) - { - VCMFrameBuffer* ptrNewBuffer = new VCMFrameBuffer(); - ptrNewBuffer->SetState(kStateEmpty); - _frameBuffers[_maxNumberOfFrames] = ptrNewBuffer; - _maxNumberOfFrames++; - - _critSect.Leave(); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), "JB(0x%x) FB(0x%x): Jitter buffer " - "increased to:%d frames", this, ptrNewBuffer, _maxNumberOfFrames); - return ptrNewBuffer; - } - _critSect.Leave(); - - // We have reached max size, cannot increase JB size - return NULL; -} - -// Must be called under the critical section _critSect. -VCMFrameListItem* -VCMJitterBuffer::FindOldestSequenceNum() const -{ - WebRtc_UWord16 currentLow = 0xffff; - VCMFrameBufferStateEnum state = kStateFree; - WebRtc_UWord16 sequenceNumber = 0; - bool first = true; - VCMFrameListItem* frameListItem = _frameBuffersTSOrder.First(); - VCMFrameListItem* oldestFrameListItem = NULL; - - while (frameListItem != NULL) - { - // if we have more than one frame done since last time, - // pick oldest - VCMFrameBuffer* ptrFrame = NULL; - ptrFrame = frameListItem->GetItem(); - state = ptrFrame->GetState(); - sequenceNumber = static_cast(ptrFrame->GetLowSeqNum()); - - // Find the oldest, hence lowest, using sequence numbers - if (first) - { - currentLow = sequenceNumber; - oldestFrameListItem = frameListItem; - first = false; - } - else if ((currentLow < 0x0fff) && (sequenceNumber > 0xf000)) - { - // We have a wrap and this one is older - currentLow = sequenceNumber; - oldestFrameListItem = frameListItem; - } - else if ((sequenceNumber < 0x0fff) && (currentLow > 0xf000)) - { - // This one is after a wrap, leave as is - } - else if (currentLow > sequenceNumber) - { - // Normal case, this one is lower. - currentLow = sequenceNumber; - oldestFrameListItem = frameListItem; - } - frameListItem = _frameBuffersTSOrder.Next(frameListItem); - } - return oldestFrameListItem; -} - -// Find oldest complete frame used for getting next frame to decode -// Must be called under critical section -// Based on sequence number -// Return NULL for lost packets -VCMFrameListItem* -VCMJitterBuffer::FindOldestCompleteContinuousFrame() -{ - // if we have more than one frame done since last time, pick oldest - VCMFrameBuffer* oldestFrame = NULL; - int currentLow = -1; - - VCMFrameListItem* oldestFrameItem = _frameBuffersTSOrder.First(); - if (oldestFrameItem != NULL) - { - oldestFrame = oldestFrameItem->GetItem(); - } - // is the frame complete? - if (oldestFrame != NULL) - { - if (kStateComplete != oldestFrame->GetState()) - { - // Try to see if the frame is complete even though the state is not - // complete. Can happen if markerbit is not set. - if (!CheckForCompleteFrame(oldestFrameItem)) - { - oldestFrame = NULL; - } - } - else - { - // we have a complete frame - currentLow = oldestFrame->GetLowSeqNum(); - } - } - if (oldestFrame == NULL) - { - // no complete frame no point to continue - return NULL; - } - - // we have a complete frame - // check if it's continuous, otherwise we are missing a full frame - // Use seqNum not timestamp since a full frame might be lost - if (_lastDecodedSeqNum != -1) - { - // it's not enough that we have complete frame we need the seq numbers - // to be continuous too for layers it's not enough that we have complete - // frame we need the layers to be continuous too - currentLow = oldestFrame->GetLowSeqNum(); - - WebRtc_UWord16 lastDecodedSeqNum = (WebRtc_UWord16)_lastDecodedSeqNum; - - // we could have received the first packet of the last frame before a - // long period if drop, that case is handled by GetNackList - if (((WebRtc_UWord16)(lastDecodedSeqNum + 1)) != currentLow) - { - // wait since we want a complete continuous frame - return NULL; - } - } - return oldestFrameItem; -} - -// Check if the oldest frame is complete even though it isn't complete. -// This can happen when makerbit is not set -// Must be called under the critical section _critSect. -// Return false for lost packets -bool -VCMJitterBuffer::CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem) -{ - const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameItem); - VCMFrameBuffer* oldestFrame = NULL; - if (oldestFrameItem != NULL) - { - oldestFrame = oldestFrameItem->GetItem(); - } - if (nextFrameItem != NULL) - { - // We have received at least one packet from a later frame. - if(!oldestFrame->HaveLastPacket()) // If we don't have the markerbit - { - VCMFrameBuffer* nextFrame = nextFrameItem->GetItem(); - // Verify that we have received the first packet of the next frame. - // This is the only way we can be sure we're not missing the last packet. - if (nextFrame != NULL && nextFrame->GetLowSeqNum() == - static_cast(oldestFrame->GetHighSeqNum() + 1)) - { - _missingMarkerBits = true; - bool completeSession = oldestFrame->ForceSetHaveLastPacket(); - if (completeSession) - { - UpdateFrameState(oldestFrame); - } - const VCMFrameBufferStateEnum state = oldestFrame->GetState(); - if (state == kStateComplete) - { - if(oldestFrame->Length() > 0) - { - UpdateJitterAndDelayEstimates(*oldestFrame, false); - } - return true; - } - } - } - } - return false; -} - -// Call from inside the critical section _critSect -void -VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame) -{ - if (frame == NULL) - { - return; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "JB(0x%x) FB(0x%x): RecycleFrame, size:%d", - this, frame, frame->Length()); - - ReleaseFrameInternal(frame); -} - - -// Calculate frame and bit rates -WebRtc_Word32 -VCMJitterBuffer::GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate) -{ - CriticalSectionScoped cs(_critSect); - const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - WebRtc_Word64 diff = now - _timeLastIncomingFrameCount; - if (diff < 1000 && _incomingFrameRate > 0 && _incomingBitRate > 0) - { - // Make sure we report something even though less than - // 1 second has passed since last update. - frameRate = _incomingFrameRate; - bitRate = _incomingBitRate; - } - else if (_incomingFrameCount != 0) - { - // We have received frame(s) since last call to this function - - // Prepare calculations - if (diff <= 0) - { - diff = 1; - } - // we add 0.5f for rounding - float rate = 0.5f + ((_incomingFrameCount * 1000.0f) / diff); - if (rate < 1.0f) // don't go below 1, can crash - { - rate = 1.0f; - } - - // Calculate frame rate - // Let r be rate. - // r(0) = 1000*framecount/delta_time. (I.e. frames per second since last calculation.) - // frameRate = r(0)/2 + r(-1)/2 (I.e. fr/s average this and the previous calculation.) - frameRate = (_incomingFrameRate + (WebRtc_Word32)rate) >> 1; - _incomingFrameRate = (WebRtc_UWord8)rate; - - // Calculate bit rate - if (_incomingBitCount == 0) - { - bitRate = 0; - } - else - { - bitRate = 10 * ((100 * _incomingBitCount) / static_cast(diff)); - } - _incomingBitRate = bitRate; - - // Reset count - _incomingFrameCount = 0; - _incomingBitCount = 0; - _timeLastIncomingFrameCount = now; - - } - else - { - // No frames since last call - _timeLastIncomingFrameCount = VCMTickTime::MillisecondTimestamp(); - frameRate = 0; - bitRate = 0; - _incomingBitRate = 0; - } - - return 0; -} - -// Returns immediately or a X ms event hang waiting for a decodable frame, X decided by caller -VCMEncodedFrame* -VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS) -{ - if (!_running) - { - return NULL; - } - - _critSect.Enter(); - - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - VCMFrameListItem* oldestFrameListItem = FindOldestCompleteContinuousFrame(); - VCMFrameBuffer* oldestFrame = NULL; - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - - if (oldestFrame == NULL) - { - if (maxWaitTimeMS == 0) - { - _critSect.Leave(); - return NULL; - } - const WebRtc_Word64 endWaitTimeMs = VCMTickTime::MillisecondTimestamp() - + maxWaitTimeMS; - WebRtc_Word64 waitTimeMs = maxWaitTimeMS; - while (waitTimeMs > 0) - { - _critSect.Leave(); - const EventTypeWrapper ret = _frameEvent.Wait(static_cast(waitTimeMs)); - _critSect.Enter(); - if (ret == kEventSignaled) - { - // are we closing down the Jitter buffer - if (!_running) - { - _critSect.Leave(); - return NULL; - } - - // Finding oldest frame ready for decoder, but check sequence number and size - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - oldestFrameListItem = FindOldestCompleteContinuousFrame(); - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - if (oldestFrame == NULL) - { - waitTimeMs = endWaitTimeMs - VCMTickTime::MillisecondTimestamp(); - } - else - { - break; - } - } - else - { - _critSect.Leave(); - return NULL; - } - } - // Inside critSect - } - else - { - // we already have a frame reset the event - _frameEvent.Reset(); - } - - if (oldestFrame == NULL) - { - // Even after signaling we're still missing a complete _continuous_ frame - _critSect.Leave(); - return NULL; - } - - // we have a frame - // store seqnum - _lastDecodedSeqNum = oldestFrame->GetHighSeqNum(); - // store current timestamp - _lastDecodedTimeStamp = oldestFrame->TimeStamp(); - - // Update jitter estimate - const bool retransmitted = (oldestFrame->GetNackCount() > 0); - if (retransmitted) - { - _jitterEstimate.FrameNacked(); - } - else if (oldestFrame->Length() > 0) - { - // Ignore retransmitted and empty frames. - UpdateJitterAndDelayEstimates(*oldestFrame, false); - } - - // This needs to be done before we clean up old frames, - // otherwise we'll remove ourselves... - oldestFrame->SetState(kStateDecoding); - _frameBuffersTSOrder.Erase(oldestFrameListItem); - oldestFrameListItem = NULL; - - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - _critSect.Leave(); - - return oldestFrame; -} - -WebRtc_UWord32 -VCMJitterBuffer::GetEstimatedJitterMS() -{ - CriticalSectionScoped cs(_critSect); - return GetEstimatedJitterMsInternal(); -} - -WebRtc_UWord32 -VCMJitterBuffer::GetEstimatedJitterMsInternal() -{ - WebRtc_UWord32 estimate = VCMJitterEstimator::OPERATING_SYSTEM_JITTER; - - // compute RTT multiplier for estimation - double rttMult = 1.0f; - if (_nackMode == kNackHybrid && _rttMs > kLowRttNackMs) - { - // from here we count on FEC - rttMult = 0.0f; - } - estimate += static_cast - (_jitterEstimate.GetJitterEstimate(rttMult) + 0.5); - if (_missingMarkerBits) - { - // Since the incoming packets are all missing marker bits we have to - // wait until the first packet of the next frame arrives, before we can - // safely say that the frame is complete. Therefore we have to compensate - // the jitter buffer level with one frame period. - // TODO(holmer): The timestamp diff should probably be filtered - // (max filter) since the diff can alternate between e.g. 3000 and 6000 - // if we have a frame rate between 15 and 30 frames per seconds. - estimate += _delayEstimate.CurrentTimeStampDiffMs(); - } - return estimate; -} - -void -VCMJitterBuffer::UpdateRtt(WebRtc_UWord32 rttMs) -{ - CriticalSectionScoped cs(_critSect); - _rttMs = rttMs; - _jitterEstimate.UpdateRtt(rttMs); -} - -// wait for the first packet in the next frame to arrive -WebRtc_Word64 -VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS, - FrameType& incomingFrameType, - WebRtc_Word64& renderTimeMs) -{ - if (!_running) - { - return -1; - } - - _critSect.Enter(); - - // Finding oldest frame ready for decoder, but check sequence number and size - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - VCMFrameBuffer* oldestFrame = _frameBuffersTSOrder.FirstFrame(); - - if (oldestFrame == NULL) - { - _critSect.Leave(); - if(_packetEvent.Wait(maxWaitTimeMS) == kEventSignaled) - { - // are we closing down the Jitter buffer - if (!_running) - { - return -1; - } - _critSect.Enter(); - - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - oldestFrame = _frameBuffersTSOrder.FirstFrame(); - }else - { - _critSect.Enter(); - } - } - _packetEvent.Reset(); - - if (oldestFrame == NULL) - { - _critSect.Leave(); - return -1; - } - // we have a frame - - // return frame type - incomingFrameType = oldestFrame->FrameType(); // All layers are assumed to have the same type - - renderTimeMs = oldestFrame->RenderTimeMs(); - - const WebRtc_UWord32 timestamp = oldestFrame->TimeStamp(); - - _critSect.Leave(); - - // return current time - return timestamp; -} - -// Answers the question: -// Will the packet sequence be complete if the next frame is grabbed for decoding right now? -// That is, have we lost a frame between the last decoded frame and the next, or is the next -// frame missing one or more packets? -bool -VCMJitterBuffer::CompleteSequenceWithNextFrame() -{ - CriticalSectionScoped cs(_critSect); - // Finding oldest frame ready for decoder, but check sequence number and size - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); - if (oldestFrameListItem == NULL) - { - // No frame found - return true; - } - - VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem(); - const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameListItem); - if (nextFrameItem == NULL && !oldestFrame->HaveLastPacket()) - { - // Frame not ready to be decoded. - return true; - } - - // See if we have lost a frame before this one. - if (_lastDecodedSeqNum == -1) - { - // The sequence is not complete since we haven't yet. - if (oldestFrame->FrameType() != kVideoFrameKey) - { - return false; - } - } - else if (oldestFrame->GetLowSeqNum() == -1) - { - return false; - } - else if (oldestFrame->GetLowSeqNum() != (_lastDecodedSeqNum + 1) % 0x00010000) - { - return false; - } - return true; -} - -// Returns immediately -VCMEncodedFrame* -VCMJitterBuffer::GetFrameForDecoding() -{ - CriticalSectionScoped cs(_critSect); - if (!_running) - { - return NULL; - } - - if (WaitForNack()) - { - return GetFrameForDecodingNACK(); - } - - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); - if (oldestFrameListItem == NULL) - { - return NULL; - } - VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem(); - - const VCMFrameListItem* nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameListItem); - if (nextFrameItem == NULL && !oldestFrame->HaveLastPacket()) - { - return NULL; - } - - // Incomplete frame pulled out from jitter buffer, - // update the jitter estimate with what we currently know. - // This frame shouldn't have been retransmitted, but if we recently - // turned off NACK this might still happen. - const bool retransmitted = (oldestFrame->GetNackCount() > 0); - if (retransmitted) - { - _jitterEstimate.FrameNacked(); - } - else if (oldestFrame->Length() > 0) - { - // Ignore retransmitted and empty frames. - // Update with the previous incomplete frame first - if (_waitingForCompletion.latestPacketTime >= 0) - { - UpdateJitterAndDelayEstimates(_waitingForCompletion, true); - } - // Then wait for this one to get complete - _waitingForCompletion.frameSize = oldestFrame->Length(); - _waitingForCompletion.latestPacketTime = oldestFrame->LatestPacketTimeMs(); - _waitingForCompletion.timestamp = oldestFrame->TimeStamp(); - oldestFrame->SetState(kStateDecoding); - } - _frameBuffersTSOrder.Erase(oldestFrameListItem); - oldestFrameListItem = NULL; - - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - VerifyAndSetPreviousFrameLost(*oldestFrame); - - // store current time - _lastDecodedTimeStamp = oldestFrame->TimeStamp(); - - // store seqnum - _lastDecodedSeqNum = oldestFrame->GetHighSeqNum(); - - return oldestFrame; -} - -VCMEncodedFrame* -VCMJitterBuffer::GetFrameForDecodingNACK() -{ - // when we use NACK we don't release non complete frames - // unless we have a complete key frame. - // In hybrid mode, we may release decodable frames (non-complete) - - // Clean up old frames and empty frames - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - // First look for a complete _continuous_ frame. - VCMFrameListItem* oldestFrameListItem = FindOldestCompleteContinuousFrame(); - VCMFrameBuffer* oldestFrame = NULL; - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - bool continuous = true; - if (oldestFrame == NULL) - { - continuous = false; - // If we didn't find one we're good with a complete key/decodable frame. - oldestFrameListItem = _frameBuffersTSOrder.FindFrameListItem( - CompleteDecodableKeyFrameCriteria); - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - if (oldestFrame == NULL) - { - return NULL; - } - } - - // We have a complete/decodable continuous frame, decode it. - // store seqnum - _lastDecodedSeqNum = oldestFrame->GetHighSeqNum(); - // store current time - _lastDecodedTimeStamp = oldestFrame->TimeStamp(); - - // Update jitter estimate - const bool retransmitted = (oldestFrame->GetNackCount() > 0); - if (retransmitted) - { - _jitterEstimate.FrameNacked(); - } - else if (oldestFrame->Length() > 0) - { - // Ignore retransmitted and empty frames. - UpdateJitterAndDelayEstimates(*oldestFrame, false); - } - - // This needs to be done before we clean up old frames, - // otherwise we might release the frame we want to decode right now. - oldestFrame->SetState(kStateDecoding); - _frameBuffersTSOrder.Erase(oldestFrameListItem); - - // Clean up old frames and empty frames - CleanUpOldFrames(); - CleanUpSizeZeroFrames(); - - return oldestFrame; -} - -// Must be called under the critical section _critSect. Should never be called with -// retransmitted frames, they must be filtered out before this function is called. -void -VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMJitterSample& sample, bool incompleteFrame) -{ - if (sample.latestPacketTime == -1) - { - return; - } - if (incompleteFrame) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Received incomplete frame timestamp %u frame size %u at time %u", - sample.timestamp, sample.frameSize, - MaskWord64ToUWord32(sample.latestPacketTime)); - } - else - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Received complete frame timestamp %u frame size %u at time %u", - sample.timestamp, sample.frameSize, - MaskWord64ToUWord32(sample.latestPacketTime)); - } - UpdateJitterAndDelayEstimates(sample.latestPacketTime, - sample.timestamp, - sample.frameSize, - incompleteFrame); -} - -// Must be called under the critical section _critSect. Should never be called with -// retransmitted frames, they must be filtered out before this function is called. -void -VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame, bool incompleteFrame) -{ - if (frame.LatestPacketTimeMs() == -1) - { - return; - } - // No retransmitted frames should be a part of the jitter - // estimate. - if (incompleteFrame) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Received incomplete frame timestamp %u frame type %d frame size %u" - " at time %u, jitter estimate was %u", - frame.TimeStamp(), frame.FrameType(), frame.Length(), - MaskWord64ToUWord32(frame.LatestPacketTimeMs()), - GetEstimatedJitterMsInternal()); - } - else - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Received complete frame timestamp %u frame type %d frame size %u " - "at time %u, jitter estimate was %u", - frame.TimeStamp(), frame.FrameType(), frame.Length(), - MaskWord64ToUWord32(frame.LatestPacketTimeMs()), - GetEstimatedJitterMsInternal()); - } - UpdateJitterAndDelayEstimates(frame.LatestPacketTimeMs(), frame.TimeStamp(), - frame.Length(), incompleteFrame); -} - -// Must be called under the critical section _critSect. Should never be called with -// retransmitted frames, they must be filtered out before this function is called. -void -VCMJitterBuffer::UpdateJitterAndDelayEstimates(WebRtc_Word64 latestPacketTimeMs, - WebRtc_UWord32 timestamp, - WebRtc_UWord32 frameSize, - bool incompleteFrame) -{ - if (latestPacketTimeMs == -1) - { - return; - } - WebRtc_Word64 frameDelay; - // Calculate the delay estimate - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Packet received and sent to jitter estimate with: timestamp=%u wallClock=%u", - timestamp, MaskWord64ToUWord32(latestPacketTimeMs)); - bool notReordered = _delayEstimate.CalculateDelay(timestamp, &frameDelay, latestPacketTimeMs); - // Filter out frames which have been reordered in time by the network - if (notReordered) - { - // Update the jitter estimate with the new samples - _jitterEstimate.UpdateEstimate(frameDelay, frameSize, incompleteFrame); - } -} - -WebRtc_UWord16* -VCMJitterBuffer::GetNackList(WebRtc_UWord16& nackSize,bool& listExtended) -{ - return CreateNackList(nackSize,listExtended); -} - -// Assume called internally with critsect -WebRtc_Word32 -VCMJitterBuffer::GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum, - WebRtc_Word32& highSeqNum) const -{ - int i = 0; - int seqNum = -1; - - highSeqNum = -1; - lowSeqNum = _lastDecodedSeqNum; - - // find highest seq numbers - for (i = 0; i < _maxNumberOfFrames; ++i) - { - seqNum = _frameBuffers[i]->GetHighSeqNum(); - - // Ignore free / empty frames - VCMFrameBufferStateEnum state = _frameBuffers[i]->GetState(); - - if ((kStateFree != state) && - (kStateEmpty != state) && - (kStateDecoding != state) && - seqNum != -1) - { - if (highSeqNum == -1) - { - // first - highSeqNum = seqNum; - } - else if (seqNum < 0x0fff && highSeqNum > 0xf000) - { - // wrap - highSeqNum = seqNum; - } - else if(seqNum > 0xf000 && highSeqNum < 0x0fff) - { - // Do nothing since it is a wrap and this one is older - } - else if (seqNum > highSeqNum) - { - highSeqNum = seqNum; - } - } - } // for - return 0; -} - - -WebRtc_UWord16* -VCMJitterBuffer::CreateNackList(WebRtc_UWord16& nackSize, bool& listExtended) -{ - CriticalSectionScoped cs(_critSect); - int i = 0; - WebRtc_Word32 lowSeqNum = -1; - WebRtc_Word32 highSeqNum = -1; - listExtended = false; - - // don't create list, if we won't wait for it - if (!WaitForNack()) - { - nackSize = 0; - return NULL; - } - - // Find the lowest (last decoded) sequence number and - // the highest (highest sequence number of the newest frame) - // sequence number. The nack list is a subset of the range - // between those two numbers. - GetLowHighSequenceNumbers(lowSeqNum, highSeqNum); - - // write a list of all seq num we have - if (lowSeqNum == -1 || highSeqNum == -1) - { - //This happens if we lose the first packet, nothing is popped - if (highSeqNum == -1) - { - // we have not received any packets yet - nackSize = 0; - } - else - { - // signal that we want a key frame request to be sent - nackSize = 0xffff; - } - return NULL; - } - - int numberOfSeqNum = 0; - if (lowSeqNum > highSeqNum) - { - if (lowSeqNum - highSeqNum > 0x00ff) - { - // wrap - numberOfSeqNum = (0xffff-lowSeqNum) + highSeqNum + 1; - } - } - else - { - numberOfSeqNum = highSeqNum - lowSeqNum; - } - - if (numberOfSeqNum > kNackHistoryLength) - { - // Nack list is too big, flush and try to restart. - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), - "Nack list too large, try to find a key frame and restart " - "from seq: %d. Lowest seq in jb %d", highSeqNum,lowSeqNum); - - // This nack size will trigger a key request... - bool foundIFrame = false; - - while (numberOfSeqNum > kNackHistoryLength) - { - foundIFrame = RecycleFramesUntilKeyFrame(); - - if (!foundIFrame) - { - break; - } - - // Check if we still have too many packets in JB - lowSeqNum = -1; - highSeqNum = -1; - GetLowHighSequenceNumbers(lowSeqNum, highSeqNum); - - if (highSeqNum == -1) - { - assert(lowSeqNum != -1); // This should never happen - // We can't calculate the nack list length... - return NULL; - } - - numberOfSeqNum = 0; - if (lowSeqNum > highSeqNum) - { - if (lowSeqNum - highSeqNum > 0x00ff) - { - // wrap - numberOfSeqNum = (0xffff-lowSeqNum) + highSeqNum + 1; - highSeqNum=lowSeqNum; - } - } - else - { - numberOfSeqNum = highSeqNum - lowSeqNum; - } - - } // end while - - if (!foundIFrame) - { - // No I frame in JB. - - // Set the last decoded sequence number to current high. - // This is to not get a large nack list again right away - _lastDecodedSeqNum = highSeqNum; - // Set to trigger key frame signal - nackSize = 0xffff; - listExtended = true; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, - "\tNo key frame found, request one. _lastDecodedSeqNum[0] %d", - _lastDecodedSeqNum); - } - else - { - // We have cleaned up the jb and found a key frame - // The function itself has set last decoded seq. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, - "\tKey frame found. _lastDecodedSeqNum[0] %d", - _lastDecodedSeqNum); - nackSize = 0; - } - - return NULL; - } - - WebRtc_UWord16 seqNumberIterator = (WebRtc_UWord16)(lowSeqNum + 1); - for (i = 0; i < numberOfSeqNum; i++) - { - _NACKSeqNumInternal[i] = seqNumberIterator; - seqNumberIterator++; - } - - // now we have a list of all sequence numbers that could have been sent - - // zero out the ones we have received - for (i = 0; i < _maxNumberOfFrames; i++) - { - // loop all created frames - // We don't need to check if frame is decoding since lowSeqNum is based - // on _lastDecodedSeqNum - // Ignore free frames - VCMFrameBufferStateEnum state = _frameBuffers[i]->GetState(); - - if ((kStateFree != state) && - (kStateEmpty != state) && - (kStateDecoding != state)) - { - // Reaching thus far means we are going to update the nack list - // When in hybrid mode, we also need to check empty frames, so as - // not to add empty packets to the nack list - if (_nackMode == kNackHybrid) - { - // build external rttScore based on RTT value - float rttScore = 1.0f; - _frameBuffers[i]->ZeroOutSeqNumHybrid(_NACKSeqNumInternal, - numberOfSeqNum, - rttScore); - if (_frameBuffers[i]->IsRetransmitted() == false) - { - // if no retransmission required,set the state to decodable - // meaning that we will not wait for NACK - _frameBuffers[i]->SetState(kStateDecodable); - } - } - else - { - // used when the frame is being processed by the decoding thread - // don't need to use that info in this loop - _frameBuffers[i]->ZeroOutSeqNum(_NACKSeqNumInternal, - numberOfSeqNum); - } - } - } - - // compress list - int emptyIndex = -1; - for (i = 0; i < numberOfSeqNum; i++) - { - if (_NACKSeqNumInternal[i] == -1 || _NACKSeqNumInternal[i] == -2 ) - { - // this is empty - if (emptyIndex == -1) - { - // no empty index before, remember this position - emptyIndex = i; - } - } - else - { - // this is not empty - if (emptyIndex == -1) - { - // no empty index, continue - } - else - { - _NACKSeqNumInternal[emptyIndex] = _NACKSeqNumInternal[i]; - _NACKSeqNumInternal[i] = -1; - emptyIndex++; - } - } - } // for - - if (emptyIndex == -1) - { - // no empty - nackSize = numberOfSeqNum; - } - else - { - nackSize = emptyIndex; - } - // convert to unsigned short 16 bit and store in a list to be used externally. - if (nackSize > _NACKSeqNumLength) - { - // Larger list means that the nack list was extended since the last call. - listExtended = true; - } - - for (WebRtc_UWord32 j = 0; j < nackSize; j++) - { - // Check if the list has been extended since it was last created. I.e, - // new items have been added - if (_NACKSeqNumLength > j && !listExtended) - { - WebRtc_UWord32 k = 0; - for (k = j; k < _NACKSeqNumLength; k++) - { - // Found the item in the last list, i.e, no new items found yet. - if (_NACKSeqNum[k] == (WebRtc_UWord16)_NACKSeqNumInternal[j]) - { - break; - } - } - if (k == _NACKSeqNumLength) // New item not found in last list. - { - listExtended = true; - } - } - else - { - listExtended = true; - } - _NACKSeqNum[j] = (WebRtc_UWord16)_NACKSeqNumInternal[j]; - } - - _NACKSeqNumLength = nackSize; - - return _NACKSeqNum; -} - -// Release frame (when done with decoding), forwards to internal function -void -VCMJitterBuffer::ReleaseFrame(VCMEncodedFrame* frame) -{ - CriticalSectionScoped cs(_critSect); - ReleaseFrameInternal(static_cast(frame)); -} - -WebRtc_Word64 -VCMJitterBuffer::LastPacketTime(VCMEncodedFrame* frame, bool& retransmitted) const -{ - CriticalSectionScoped cs(_critSect); - retransmitted = (static_cast(frame)->GetNackCount() > 0); - return static_cast(frame)->LatestPacketTimeMs(); -} - -WebRtc_Word64 -VCMJitterBuffer::LastDecodedTimestamp() const -{ - CriticalSectionScoped cs(_critSect); - return _lastDecodedTimeStamp; -} - -// Insert packet -// Takes crit sect, and inserts packet in frame buffer, possibly does logging -VCMFrameBufferEnum -VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet) -{ - CriticalSectionScoped cs(_critSect); - WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); - VCMFrameBufferEnum bufferReturn = kSizeError; - VCMFrameBufferEnum ret = kSizeError; - VCMFrameBuffer* frame = static_cast(buffer); - - // Empty packets may bias the jitter estimate (lacking size component), - // therefore don't let empty packet trigger the following updates: - if (packet.frameType != kFrameEmpty) - { - if (_firstPacket) - { - // Now it's time to start estimating jitter - // reset the delay estimate. - _delayEstimate.Reset(); - _firstPacket = false; - } - - if (_waitingForCompletion.timestamp == packet.timestamp) - { - // This can get bad if we have a lot of duplicate packets, - // we will then count some packet multiple times. - _waitingForCompletion.frameSize += packet.sizeBytes; - _waitingForCompletion.latestPacketTime = nowMs; - } - else if (_waitingForCompletion.latestPacketTime >= 0 && - _waitingForCompletion.latestPacketTime + 2000 <= nowMs) - { - // A packet should never be more than two seconds late - UpdateJitterAndDelayEstimates(_waitingForCompletion, true); - _waitingForCompletion.latestPacketTime = -1; - _waitingForCompletion.frameSize = 0; - _waitingForCompletion.timestamp = 0; - } - } - - if (frame != NULL) - { - VCMFrameBufferStateEnum state = frame->GetState(); - if (state == kStateDecoding && packet.sizeBytes == 0) - { - // Filler packet, make sure we update the last decoded seq num - // since this packet should've been a part of the frame being decoded. - // If the filler packet belongs to a very old frame (already decoded - // and freed) a new frame will be created for the filler packet. - // That frame will be empty and later on cleaned up. - UpdateLastDecodedWithFiller(packet); - } - - // Insert packet - bufferReturn = frame->InsertPacket(packet, nowMs); - ret = bufferReturn; - - if (bufferReturn > 0) - { - _incomingBitCount += packet.sizeBytes << 3; - - // Has this packet been nacked or is it about to be nacked? - if (IsPacketRetransmitted(packet)) - { - frame->IncrementNackCount(); - } - - // First packet of a frame - if (state == kStateEmpty) - { - if (bufferReturn > 0) - { - ret = kFirstPacket; - } - _frameBuffersTSOrder.Insert(frame); - } - } - } - switch(bufferReturn) - { - case kStateError: - case kTimeStampError: - case kSizeError: - { - // This will trigger a release in CleanUpSizeZeroFrames - if (frame != NULL) - { - frame->Reset(); - frame->SetState(kStateEmpty); - } - break; - } - case kCompleteSession: - { - UpdateFrameState(frame); - // Signal that we have a received packet - _packetEvent.Set(); - break; - } - case kIncomplete: - { - // Signal that we have a received packet - _packetEvent.Set(); - break; - } - case kNoError: - case kDuplicatePacket: - { - break; - } - default: - { - assert(!"JitterBuffer::InsertPacket: Undefined value"); - } - } - - return ret; -} - -void -VCMJitterBuffer::UpdateLastDecodedWithFiller(const VCMPacket& packet) -{ - // Empty packet (filler) inserted to a frame which - // is already decoding. Update the last decoded seq no. - if (_lastDecodedTimeStamp == packet.timestamp && - (packet.seqNum > _lastDecodedSeqNum || - (packet.seqNum < 0x0fff && _lastDecodedSeqNum > 0xf000))) - { - _lastDecodedSeqNum = packet.seqNum; - } -} - -// Must be called from within _critSect -void -VCMJitterBuffer::UpdateOldJitterSample(const VCMPacket& packet) -{ - if (_waitingForCompletion.timestamp != packet.timestamp && - LatestTimestamp(_waitingForCompletion.timestamp, packet.timestamp) == - packet.timestamp) - { - // This is a newer frame than the one waiting for completion. - _waitingForCompletion.frameSize = packet.sizeBytes; - _waitingForCompletion.timestamp = packet.timestamp; - } - else - { - // This can get bad if we have a lot of duplicate packets, - // we will then count some packet multiple times. - _waitingForCompletion.frameSize += packet.sizeBytes; - _jitterEstimate.UpdateMaxFrameSize(_waitingForCompletion.frameSize); - } -} - -// Must be called from within _critSect -bool -VCMJitterBuffer::IsPacketRetransmitted(const VCMPacket& packet) const -{ - if (_NACKSeqNum && _NACKSeqNumLength > 0) - { - for (WebRtc_UWord16 i = 0; i < _NACKSeqNumLength; i++) - { - if (packet.seqNum == _NACKSeqNum[i]) - { - return true; - } - } - } - return false; -} - -// Get nack status (enabled/disabled) -VCMNackMode -VCMJitterBuffer::GetNackMode() const -{ - CriticalSectionScoped cs(_critSect); - return _nackMode; -} - -// Set NACK mode -void -VCMJitterBuffer::SetNackMode(VCMNackMode mode) -{ - CriticalSectionScoped cs(_critSect); - _nackMode = mode; - if (_nackMode == kNoNack) - { - _jitterEstimate.ResetNackCount(); - } -} - - -// Recycle oldest frames up to a key frame, used if JB is completely full -bool -VCMJitterBuffer::RecycleFramesUntilKeyFrame() -{ - // Throw at least one frame. - VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); - VCMFrameBuffer* oldestFrame = NULL; - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - - // Remove up to oldest key frame - bool foundIFrame = false; - while (oldestFrameListItem != NULL && !foundIFrame) - { - // Throw at least one frame. - _dropCount++; - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), - "Jitter buffer drop count:%d, lowSeq %d", _dropCount, - oldestFrame->GetLowSeqNum()); - _frameBuffersTSOrder.Erase(oldestFrameListItem); - RecycleFrame(oldestFrame); - - oldestFrameListItem = _frameBuffersTSOrder.First(); - if (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - } - - if(oldestFrame != NULL) - { - foundIFrame = foundIFrame || - (oldestFrame->FrameType() != kVideoFrameDelta); - if (foundIFrame) - { - // fake the last played out to match the start of this key frame - _lastDecodedSeqNum = (WebRtc_UWord16)((WebRtc_UWord16) - (oldestFrame->GetLowSeqNum()) - 1); - _lastDecodedTimeStamp = (WebRtc_UWord32) - (oldestFrame->TimeStamp() - 1); - break; - } - } - } - _lastDecodedSeqNum = -1; - return foundIFrame; -} - -// Must be called under the critical section _critSect. -void -VCMJitterBuffer::CleanUpOldFrames() -{ - VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); - VCMFrameBuffer* oldestFrame = NULL; - - if (_lastDecodedTimeStamp == -1) - { - return; - } - - while (oldestFrameListItem != NULL) - { - oldestFrame = oldestFrameListItem->GetItem(); - WebRtc_UWord32 frameTimeStamp = oldestFrame->TimeStamp(); - - // Release the frame if it's older than the last decoded frame. - if (_lastDecodedTimeStamp > -1 && - LatestTimestamp(static_cast(_lastDecodedTimeStamp), - frameTimeStamp) - == static_cast(_lastDecodedTimeStamp)) - { - const WebRtc_Word32 frameLowSeqNum = oldestFrame->GetLowSeqNum(); - const WebRtc_Word32 frameHighSeqNum = oldestFrame->GetHighSeqNum(); - if (frameTimeStamp == _lastDecodedTimeStamp && - ((frameLowSeqNum == (_lastDecodedSeqNum + 1)) || - ((frameLowSeqNum == 0) && - (_lastDecodedSeqNum == 0xffff)))) - { - // Could happen when sending filler data. - // Filler packet (size = 0) belonging to last decoded frame. - // Frame: | packet | packet | packet M=1 | - // filler data (size = 0) | filler data (size = 0)| ... - - // This frame follows the last decoded frame - _lastDecodedSeqNum = frameHighSeqNum; - } - - _frameBuffersTSOrder.Erase(oldestFrameListItem); - ReleaseFrameInternal(oldestFrame); - oldestFrameListItem = _frameBuffersTSOrder.First(); - } - else - { - break; - } - } -} - -// This function has changed to use sequence numbers -// Using timestamp won't work since can get -// nack requests with a higher time stamp than -// the following encoded frame, but with a lower sequence number. -// Must be called under _critSect. -void -VCMJitterBuffer::CleanUpSizeZeroFrames() -{ - VCMFrameListItem* frameListItem = FindOldestSequenceNum(); - - while (frameListItem != NULL) - { - VCMFrameBuffer* ptrTempBuffer = frameListItem->GetItem(); - - // pop frame if its size zero but store seqnum - if (ptrTempBuffer->Length() == 0) - { - WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum(); - if (frameHighSeqNum == -1) - { - // This frame has been Reset for this function to clean it up - _frameBuffersTSOrder.Erase(frameListItem); - ReleaseFrameInternal(ptrTempBuffer); - frameListItem = FindOldestSequenceNum(); - } - else - { - bool releaseFrame = false; - const WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum(); - const WebRtc_Word32 frameLowSeqNum = ptrTempBuffer->GetLowSeqNum(); - - if ((frameLowSeqNum == (_lastDecodedSeqNum + 1)) || // Frame is next in line - ((frameLowSeqNum == 0) && (_lastDecodedSeqNum== 0xffff))) - { - // This frame follows the last decoded frame, release it. - _lastDecodedSeqNum = frameHighSeqNum; - releaseFrame = true; - } - // If frameHighSeqNum < _lastDecodedSeqNum but need to take wrap into account. - else if(frameHighSeqNum < _lastDecodedSeqNum) - { - if (frameHighSeqNum < 0x0fff && - _lastDecodedSeqNum> 0xf000) - { - // Wrap, we don't want release this one. It's newer... - } - else - { - // This frame has lower seq than last decoded, - // and we have no wrap -> it's older. - releaseFrame = true; - } - } - else if(frameHighSeqNum > _lastDecodedSeqNum && - _lastDecodedSeqNum < 0x0fff && - frameHighSeqNum > 0xf000) - { - // Higher seq than last decoded, - // but last decoded has recently wrapped. - releaseFrame = true; - } - - if (releaseFrame) - { - _frameBuffersTSOrder.Erase(frameListItem); - ReleaseFrameInternal(ptrTempBuffer); - frameListItem = FindOldestSequenceNum(); - } - else - { - // We couldn't release this one and we're using nack, - // stop trying... - frameListItem = NULL; - } - } - } - else - { - // we have a length - break; - } - } -} - -// used in GetFrameForDecoding -void -VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame) -{ - frame.MakeSessionDecodable(); // make sure the session can be decoded. - if (_lastDecodedSeqNum == -1) - { - // First frame - frame.SetPreviousFrameLoss(); - } - else if (frame.GetLowSeqNum() != ((WebRtc_UWord16)_lastDecodedSeqNum + - (WebRtc_UWord16)1)) - { - // Frame loss - frame.SetPreviousFrameLoss(); - } -} - -bool -VCMJitterBuffer::WaitForNack() -{ - // NACK disabled -> can't wait - if (_nackMode == kNoNack) - { - return false; - } - // NACK only -> always wait - else if (_nackMode == kNackInfinite) - { - return true; - } - // else: hybrid mode, evaluate - // RTT high, don't wait - if (_rttMs >= kHighRttNackMs) - { - return false; - } - // Either NACK only or hybrid - return true; -} - - -} diff --git a/modules/video_coding/main/source/jitter_buffer.h b/modules/video_coding/main/source/jitter_buffer.h deleted file mode 100644 index ab88f38bc..000000000 --- a/modules/video_coding/main/source/jitter_buffer.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_ - -#include "typedefs.h" -#include "critical_section_wrapper.h" -#include "module_common_types.h" -#include "video_coding_defines.h" -#include "inter_frame_delay.h" -#include "event.h" -#include "frame_list.h" -#include "jitter_buffer_common.h" -#include "jitter_estimator.h" - -namespace webrtc -{ - -enum VCMNackMode -{ - kNackInfinite, - kNackHybrid, - kNoNack -}; - -// forward declarations -class VCMFrameBuffer; -class VCMPacket; -class VCMEncodedFrame; - -class VCMJitterSample -{ -public: - VCMJitterSample() : timestamp(0), frameSize(0), latestPacketTime(-1) {} - WebRtc_UWord32 timestamp; - WebRtc_UWord32 frameSize; - WebRtc_Word64 latestPacketTime; -}; - -class VCMJitterBuffer -{ -public: - VCMJitterBuffer(WebRtc_Word32 vcmId = -1, - WebRtc_Word32 receiverId = -1, - bool master = true); - virtual ~VCMJitterBuffer(); - - VCMJitterBuffer& operator=(const VCMJitterBuffer& rhs); - - // We need a start and stop to break out of the wait event - // used in GetCompleteFrameForDecoding - void Start(); - void Stop(); - bool Running() const; - - // Empty the Jitter buffer of all its data - void Flush(); - - // Statistics, Get received key and delta frames - WebRtc_Word32 GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames, - WebRtc_UWord32& receivedKeyFrames) const; - - // Statistics, Calculate frame and bit rates - WebRtc_Word32 GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate); - - // Wait for the first packet in the next frame to arrive, blocks for <= maxWaitTimeMS ms - WebRtc_Word64 GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS, - FrameType& incomingFrameType, - WebRtc_Word64& renderTimeMs); - - // Will the packet sequence be complete if the next frame is grabbed - // for decoding right now? That is, have we lost a frame between the - // last decoded frame and the next, or is the next frame missing one - // or more packets? - bool CompleteSequenceWithNextFrame(); - - // Wait maxWaitTimeMS for a complete frame to arrive. After timeout NULL is returned. - VCMEncodedFrame* GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS); - - // Get a frame for decoding (even an incomplete) without delay. - VCMEncodedFrame* GetFrameForDecoding(); - - VCMEncodedFrame* GetFrameForDecodingNACK(); - - // Release frame (when done with decoding) - void ReleaseFrame(VCMEncodedFrame* frame); - - // Get frame to use for this timestamp - WebRtc_Word32 GetFrame(const VCMPacket& packet, VCMEncodedFrame*&); - VCMEncodedFrame* GetFrame(const VCMPacket& packet); // deprecated - - // Returns the time in ms when the latest packet was inserted into the frame. - // Retransmitted is set to true if any of the packets belonging to the frame - // has been retransmitted. - WebRtc_Word64 LastPacketTime(VCMEncodedFrame* frame, bool& retransmitted) const; - - // Insert a packet into a frame - VCMFrameBufferEnum InsertPacket(VCMEncodedFrame* frame, const VCMPacket& packet); - - // Sync - WebRtc_UWord32 GetEstimatedJitterMS(); - void UpdateRtt(WebRtc_UWord32 rttMs); - - // NACK - void SetNackMode(VCMNackMode mode); // Enable/disable nack - VCMNackMode GetNackMode() const; // Get nack mode - // Get list of missing sequence numbers (size in number of elements) - WebRtc_UWord16* GetNackList(WebRtc_UWord16& nackSize, bool& listExtended); - - WebRtc_Word64 LastDecodedTimestamp() const; - static WebRtc_UWord32 LatestTimestamp(const WebRtc_UWord32 existingTimestamp, - const WebRtc_UWord32 newTimestamp); - -protected: - - // Misc help functions - // Recycle (release) frame, used if we didn't receive whole frame - void RecycleFrame(VCMFrameBuffer* frame); - void ReleaseFrameInternal(VCMFrameBuffer* frame); - // Flush and reset the jitter buffer. Call under critical section. - void FlushInternal(); - VCMFrameListItem* FindOldestSequenceNum() const; - - // Help functions for insert packet - // Get empty frame, creates new (i.e. increases JB size) if necessary - VCMFrameBuffer* GetEmptyFrame(); - // Recycle oldest frames up to a key frame, used if JB is completely full - bool RecycleFramesUntilKeyFrame(); - // Update frame state (set as complete or reconstructable if conditions are met) - void UpdateFrameState(VCMFrameBuffer* frameListItem); - - // Help functions for getting a frame - // Find oldest complete frame, used for getting next frame to decode - VCMFrameListItem* FindOldestCompleteContinuousFrame(); - - // Check if a frame is missing the markerbit but is complete - bool CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem); - - - void CleanUpOldFrames(); - void CleanUpSizeZeroFrames(); - - void VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame); - bool IsPacketRetransmitted(const VCMPacket& packet) const; - void UpdateJitterAndDelayEstimates(VCMJitterSample& sample, bool incompleteFrame); - void UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame, bool incompleteFrame); - void UpdateJitterAndDelayEstimates(WebRtc_Word64 latestPacketTimeMs, - WebRtc_UWord32 timestamp, - WebRtc_UWord32 frameSize, - bool incompleteFrame); - void UpdateOldJitterSample(const VCMPacket& packet); - WebRtc_UWord32 GetEstimatedJitterMsInternal(); - - // NACK help - WebRtc_UWord16* CreateNackList(WebRtc_UWord16& nackSize, bool& listExtended); - WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum, - WebRtc_Word32& highSeqNum) const; - - void UpdateLastDecodedWithFiller(const VCMPacket& packet); - -private: - - static bool FrameEqualTimestamp(VCMFrameBuffer* frame, const void* timestamp); - static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame, - const void* notUsed); - // Decide whether should wait for NACK (mainly relevant for hybrid mode) - bool WaitForNack(); - - WebRtc_Word32 _vcmId; - WebRtc_Word32 _receiverId; - // If we are running (have started) or not - bool _running; - CriticalSectionWrapper& _critSect; - bool _master; - // Event to signal when we have a frame ready for decoder - VCMEvent _frameEvent; - // Event to signal when we have received a packet - VCMEvent _packetEvent; - // Number of allocated frames - WebRtc_Word32 _maxNumberOfFrames; - // Array of pointers to the frames in JB - VCMFrameBuffer* _frameBuffers[kMaxNumberOfFrames]; - VCMFrameListTimestampOrderAsc _frameBuffersTSOrder; - - // timing - // Sequence number of last frame that was given to decoder - WebRtc_Word32 _lastDecodedSeqNum; - // Timestamp of last frame that was given to decoder - WebRtc_Word64 _lastDecodedTimeStamp; - - // Statistics - // Frame counter for each type (key, delta, golden, key-delta) - WebRtc_UWord8 _receiveStatistics[4]; - // Latest calculated frame rates of incoming stream - WebRtc_UWord8 _incomingFrameRate; - // Frame counter, reset in GetUpdate - WebRtc_UWord32 _incomingFrameCount; - // Real time for last _frameCount reset - WebRtc_Word64 _timeLastIncomingFrameCount; - // Received bits counter, reset in GetUpdate - WebRtc_UWord32 _incomingBitCount; - WebRtc_UWord32 _incomingBitRate; - WebRtc_UWord32 _dropCount; // Frame drop counter - // Number of frames in a row that have been too old - WebRtc_UWord32 _numConsecutiveOldFrames; - // Number of packets in a row that have been too old - WebRtc_UWord32 _numConsecutiveOldPackets; - // Filters for estimating jitter - VCMJitterEstimator _jitterEstimate; - // Calculates network delays used for jitter calculations - VCMInterFrameDelay _delayEstimate; - VCMJitterSample _waitingForCompletion; - WebRtc_UWord32 _rttMs; - - // NACK - VCMNackMode _nackMode; - // Holds the internal nack list (the missing seqence numbers) - WebRtc_Word32 _NACKSeqNumInternal[kNackHistoryLength]; - WebRtc_UWord16 _NACKSeqNum[kNackHistoryLength]; - WebRtc_UWord32 _NACKSeqNumLength; - - bool _missingMarkerBits; - bool _firstPacket; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_ diff --git a/modules/video_coding/main/source/jitter_buffer_common.h b/modules/video_coding/main/source/jitter_buffer_common.h deleted file mode 100644 index 035a3c117..000000000 --- a/modules/video_coding/main/source/jitter_buffer_common.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_ -#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_ - -namespace webrtc -{ - -enum { kMaxNumberOfFrames = 100 }; -enum { kStartNumberOfFrames = 6 }; // in packets, 6 packets are approximately 198 ms, - // we need at least one more for process -enum { kMaxVideoDelayMs = 2000 }; // in ms - -enum VCMJitterBufferEnum -{ - kMaxConsecutiveOldFrames = 60, - kMaxConsecutiveOldPackets = 300, - kMaxPacketsInJitterBuffer = 800, - kBufferIncStepSizeBytes = 30000, // >20 packets - kMaxJBFrameSizeBytes = 4000000 // sanity don't go above 4Mbyte -}; - -enum VCMFrameBufferEnum -{ - kStateError = -4, - kTimeStampError = -2, - kSizeError = -1, - kNoError = 0, - kIncomplete = 1, // Frame incomplete - kFirstPacket = 2, - kCompleteSession = 3, // at least one layer in the frame complete - kDuplicatePacket = 5 // We're receiving a duplicate packet. -}; - -enum VCMFrameBufferStateEnum -{ - kStateFree, // Unused frame in the JB - kStateEmpty, // frame popped by the RTP receiver - kStateIncomplete, // frame that have one or more packet(s) stored - kStateComplete, // frame that have all packets - kStateDecoding, // frame popped by the decoding thread - kStateDecodable // Hybrid mode - frame can be decoded -}; - -enum { kH264StartCodeLengthBytes = 4}; - -// Used to indicate if a received packet contain a complete NALU (or equivalent) -enum VCMNaluCompleteness -{ - kNaluUnset = 0, //Packet has not been filled. - kNaluComplete = 1, //Packet can be decoded as is. - kNaluStart, // Packet contain beginning of NALU - kNaluIncomplete, //Packet is not beginning or end of NALU - kNaluEnd, // Packet is the end of a NALU -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_ diff --git a/modules/video_coding/main/source/jitter_estimator.cc b/modules/video_coding/main/source/jitter_estimator.cc deleted file mode 100644 index 233fad4cd..000000000 --- a/modules/video_coding/main/source/jitter_estimator.cc +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "internal_defines.h" -#include "jitter_estimator.h" -#include "rtt_filter.h" -#include "tick_time.h" - -#include -#include -#include - -namespace webrtc { - -VCMJitterEstimator::VCMJitterEstimator(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId) : -_vcmId(vcmId), -_receiverId(receiverId), -_phi(0.97), -_psi(0.9999), -_alphaCountMax(400), -_beta(0.9994), -_thetaLow(0.000001), -_nackLimit(3), -_numStdDevDelayOutlier(15), -_numStdDevFrameSizeOutlier(3), -_noiseStdDevs(2.33), // ~Less than 1% chance - // (look up in normal distribution table)... -_noiseStdDevOffset(30.0), // ...of getting 30 ms freezes -_rttFilter(vcmId, receiverId) -{ - Reset(); -} - -VCMJitterEstimator& -VCMJitterEstimator::operator=(const VCMJitterEstimator& rhs) -{ - if (this != &rhs) - { - memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov)); - memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov)); - - _vcmId = rhs._vcmId; - _receiverId = rhs._receiverId; - _avgFrameSize = rhs._avgFrameSize; - _varFrameSize = rhs._varFrameSize; - _maxFrameSize = rhs._maxFrameSize; - _fsSum = rhs._fsSum; - _fsCount = rhs._fsCount; - _lastUpdateT = rhs._lastUpdateT; - _prevEstimate = rhs._prevEstimate; - _prevFrameSize = rhs._prevFrameSize; - _avgNoise = rhs._avgNoise; - _alphaCount = rhs._alphaCount; - _filterJitterEstimate = rhs._filterJitterEstimate; - _startupCount = rhs._startupCount; - _latestNackTimestamp = rhs._latestNackTimestamp; - _nackCount = rhs._nackCount; - _rttFilter = rhs._rttFilter; - } - return *this; -} - -// Resets the JitterEstimate -void -VCMJitterEstimator::Reset() -{ - _theta[0] = 1/(512e3/8); - _theta[1] = 0; - _varNoise = 4.0; - - _thetaCov[0][0] = 1e-4; - _thetaCov[1][1] = 1e2; - _thetaCov[0][1] = _thetaCov[1][0] = 0; - _Qcov[0][0] = 2.5e-10; - _Qcov[1][1] = 1e-10; - _Qcov[0][1] = _Qcov[1][0] = 0; - _avgFrameSize = 500; - _maxFrameSize = 500; - _varFrameSize = 100; - _lastUpdateT = -1; - _prevEstimate = -1.0; - _prevFrameSize = 0; - _avgNoise = 0.0; - _alphaCount = 1; - _filterJitterEstimate = 0.0; - _latestNackTimestamp = 0; - _nackCount = 0; - _fsSum = 0; - _fsCount = 0; - _startupCount = 0; - _rttFilter.Reset(); -} - -void -VCMJitterEstimator::ResetNackCount() -{ - _nackCount = 0; -} - -// Updates the estimates with the new measurements -void -VCMJitterEstimator::UpdateEstimate(WebRtc_Word64 frameDelayMS, WebRtc_UWord32 frameSizeBytes, - bool incompleteFrame /* = false */) -{ - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, - VCMId(_vcmId, _receiverId), - "Jitter estimate updated with: frameSize=%d frameDelayMS=%d", - frameSizeBytes, frameDelayMS); - if (frameSizeBytes == 0) - { - return; - } - int deltaFS = frameSizeBytes - _prevFrameSize; - if (_fsCount < kFsAccuStartupSamples) - { - _fsSum += frameSizeBytes; - _fsCount++; - } - else if (_fsCount == kFsAccuStartupSamples) - { - // Give the frame size filter - _avgFrameSize = static_cast(_fsSum) / - static_cast(_fsCount); - _fsCount++; - } - if (!incompleteFrame || frameSizeBytes > _avgFrameSize) - { - double avgFrameSize = _phi * _avgFrameSize + - (1 - _phi) * frameSizeBytes; - if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) - { - // Only update the average frame size if this sample wasn't a - // key frame - _avgFrameSize = avgFrameSize; - } - // Update the variance anyway since we want to capture cases where we only get - // key frames. - _varFrameSize = VCM_MAX(_phi * _varFrameSize + (1 - _phi) * - (frameSizeBytes - avgFrameSize) * - (frameSizeBytes - avgFrameSize), 1.0); - } - - // Update max frameSize estimate - _maxFrameSize = VCM_MAX(_psi * _maxFrameSize, static_cast(frameSizeBytes)); - - if (_prevFrameSize == 0) - { - _prevFrameSize = frameSizeBytes; - return; - } - _prevFrameSize = frameSizeBytes; - - // Only update the Kalman filter if the sample is not considered - // an extreme outlier. Even if it is an extreme outlier from a - // delay point of view, if the frame size also is large the - // deviation is probably due to an incorrect line slope. - double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS); - - if (abs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) || - frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) - { - // Update the variance of the deviation from the - // line given by the Kalman filter - EstimateRandomJitter(deviation, incompleteFrame); - // Prevent updating with frames which have been congested by a large - // frame, and therefore arrives almost at the same time as that frame. - // This can occur when we receive a large frame (key frame) which - // has been delayed. The next frame is of normal size (delta frame), - // and thus deltaFS will be << 0. This removes all frame samples - // which arrives after a key frame. - if ((!incompleteFrame || deviation >= 0.0) && - static_cast(deltaFS) > - 0.25 * _maxFrameSize) - { - // Update the Kalman filter with the new data - KalmanEstimateChannel(frameDelayMS, deltaFS); - } - } - else - { - int nStdDev = (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier; - EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame); - } - // Post process the total estimated jitter - if (_startupCount >= kStartupDelaySamples) - { - PostProcessEstimate(); - } - else - { - _startupCount++; - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Framesize statistics: max=%f average=%f", _maxFrameSize, _avgFrameSize); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "The estimated slope is: theta=(%f, %f)", _theta[0], _theta[1]); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Random jitter: mean=%f variance=%f", _avgNoise, _varNoise); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Current jitter estimate: %f", _filterJitterEstimate); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Current max RTT: %u", _rttFilter.RttMs()); -} - -// Updates the nack/packet ratio -void -VCMJitterEstimator::FrameNacked() -{ - // Wait until _nackLimit retransmissions has been received, - // then always add ~1 RTT delay. - // TODO(holmer): Should we ever remove the additional delay if the - // the packet losses seem to have stopped? We could for instance scale - // the number of RTTs to add with the amount of retransmissions in a given - // time interval, or similar. - if (_nackCount < _nackLimit) - { - _nackCount++; - } -} - -// Updates Kalman estimate of the channel -// The caller is expected to sanity check the inputs. -void -VCMJitterEstimator::KalmanEstimateChannel(WebRtc_Word64 frameDelayMS, - WebRtc_Word32 deltaFSBytes) -{ - double Mh[2]; - double hMh_sigma; - double kalmanGain[2]; - double measureRes; - double t00, t01; - - // Kalman filtering - - // Prediction - // M = M + Q - _thetaCov[0][0] += _Qcov[0][0]; - _thetaCov[0][1] += _Qcov[0][1]; - _thetaCov[1][0] += _Qcov[1][0]; - _thetaCov[1][1] += _Qcov[1][1]; - - // Kalman gain - // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h') - // h = [dFS 1] - // Mh = M*h' - // hMh_sigma = h*M*h' + R - Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1]; - Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1]; - // sigma weights measurements with a small deltaFS as noisy and - // measurements with large deltaFS as good - if (_maxFrameSize < 1.0) - { - return; - } - double sigma = (300.0 * exp(-abs(static_cast(deltaFSBytes)) / - (1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise); - if (sigma < 1.0) - { - sigma = 1.0; - } - hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma; - if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0)) - { - assert(false); - return; - } - kalmanGain[0] = Mh[0] / hMh_sigma; - kalmanGain[1] = Mh[1] / hMh_sigma; - - // Correction - // theta = theta + K*(dT - h*theta) - measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]); - _theta[0] += kalmanGain[0] * measureRes; - _theta[1] += kalmanGain[1] * measureRes; - - if (_theta[0] < _thetaLow) - { - _theta[0] = _thetaLow; - } - - // M = (I - K*h)*M - t00 = _thetaCov[0][0]; - t01 = _thetaCov[0][1]; - _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 - - kalmanGain[0] * _thetaCov[1][0]; - _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 - - kalmanGain[0] * _thetaCov[1][1]; - _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t00; - _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t01; - - // Covariance matrix, must be positive semi-definite - assert(_thetaCov[0][0] + _thetaCov[1][1] >= 0 && - _thetaCov[0][0] * _thetaCov[1][1] - _thetaCov[0][1] * _thetaCov[1][0] >= 0 && - _thetaCov[0][0] >= 0); -} - -// Calculate difference in delay between a sample and the -// expected delay estimated by the Kalman filter -double -VCMJitterEstimator::DeviationFromExpectedDelay(WebRtc_Word64 frameDelayMS, - WebRtc_Word32 deltaFSBytes) const -{ - return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]); -} - -// Estimates the random jitter by calculating the variance of the -// sample distance from the line given by theta. -void -VCMJitterEstimator::EstimateRandomJitter(double d_dT, bool incompleteFrame) -{ - double alpha; - if (_alphaCount == 0) - { - assert(_alphaCount > 0); - return; - } - alpha = static_cast(_alphaCount - 1) / static_cast(_alphaCount); - _alphaCount++; - if (_alphaCount > _alphaCountMax) - { - _alphaCount = _alphaCountMax; - } - double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT; - double varNoise = alpha * _varNoise + - (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise); - if (!incompleteFrame || varNoise > _varNoise) - { - _avgNoise = avgNoise; - _varNoise = varNoise; - } - if (_varNoise < 1.0) - { - // The variance should never be zero, since we might get - // stuck and consider all samples as outliers. - _varNoise = 1.0; - } -} - -double -VCMJitterEstimator::NoiseThreshold() const -{ - double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset; - if (noiseThreshold < 1.0) - { - noiseThreshold = 1.0; - } - return noiseThreshold; -} - -// Calculates the current jitter estimate from the filtered estimates -double -VCMJitterEstimator::CalculateEstimate() -{ - double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold(); - - // A very low estimate (or negative) is neglected - if (ret < 1.0) { - if (_prevEstimate <= 0.01) - { - ret = 1.0; - } - else - { - ret = _prevEstimate; - } - } - if (ret > 10000.0) // Sanity - { - ret = 10000.0; - } - _prevEstimate = ret; - return ret; -} - -void -VCMJitterEstimator::PostProcessEstimate() -{ - _filterJitterEstimate = CalculateEstimate(); -} - -void -VCMJitterEstimator::UpdateRtt(WebRtc_UWord32 rttMs) -{ - _rttFilter.Update(rttMs); -} - -void -VCMJitterEstimator::UpdateMaxFrameSize(WebRtc_UWord32 frameSizeBytes) -{ - if (_maxFrameSize < frameSizeBytes) - { - _maxFrameSize = frameSizeBytes; - } -} - -// Returns the current filtered estimate if available, -// otherwise tries to calculate an estimate. -double -VCMJitterEstimator::GetJitterEstimate(double rttMultiplier) -{ - double jitterMS = CalculateEstimate(); - if (_filterJitterEstimate > jitterMS) - { - jitterMS = _filterJitterEstimate; - } - if (_nackCount >= _nackLimit) - { - return jitterMS + _rttFilter.RttMs() * rttMultiplier; - } - return jitterMS; -} - -} diff --git a/modules/video_coding/main/source/jitter_estimator.h b/modules/video_coding/main/source/jitter_estimator.h deleted file mode 100644 index 6fc47030a..000000000 --- a/modules/video_coding/main/source/jitter_estimator.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ -#define WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ - -#include "typedefs.h" -#include "rtt_filter.h" - -namespace webrtc -{ - -class VCMJitterEstimator -{ -public: - VCMJitterEstimator(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0); - - VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs); - - // Resets the estimate to the initial state - void Reset(); - void ResetNackCount(); - - // Updates the jitter estimate with the new data. - // - // Input: - // - frameDelay : Delay-delta calculated by UTILDelayEstimate in milliseconds - // - frameSize : Frame size of the current frame. - // - incompleteFrame : Flags if the frame is used to update the estimate before it - // was complete. Default is false. - void UpdateEstimate(WebRtc_Word64 frameDelayMS, - WebRtc_UWord32 frameSizeBytes, - bool incompleteFrame = false); - - // Returns the current jitter estimate in milliseconds and adds - // also adds an RTT dependent term in cases of retransmission. - // Input: - // - rttMultiplier : RTT param multiplier (when applicable). - // - // Return value : Jitter estimate in milliseconds - double GetJitterEstimate(double rttMultiplier); - - // Updates the nack counter. - void FrameNacked(); - - // Updates the RTT filter. - // - // Input: - // - rttMs : RTT in ms - void UpdateRtt(WebRtc_UWord32 rttMs); - - void UpdateMaxFrameSize(WebRtc_UWord32 frameSizeBytes); - - // A constant describing the delay from the jitter buffer - // to the delay on the receiving side which is not accounted - // for by the jitter buffer nor the decoding delay estimate. - static const WebRtc_UWord32 OPERATING_SYSTEM_JITTER = 10; - -protected: - // These are protected for better testing possibilities - double _theta[2]; // Estimated line parameters (slope, offset) - double _varNoise; // Variance of the time-deviation from the line - -private: - // Updates the Kalman filter for the line describing - // the frame size dependent jitter. - // - // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in milliseconds - // - deltaFSBytes : Frame size delta, i.e. - // : frame size at time T minus frame size at time T-1 - void KalmanEstimateChannel(WebRtc_Word64 frameDelayMS, WebRtc_Word32 deltaFSBytes); - - // Updates the random jitter estimate, i.e. the variance - // of the time deviations from the line given by the Kalman filter. - // - // Input: - // - d_dT : The deviation from the kalman estimate - // - incompleteFrame : True if the frame used to update the estimate - // with was incomplete - void EstimateRandomJitter(double d_dT, bool incompleteFrame); - - double NoiseThreshold() const; - - // Calculates the current jitter estimate. - // - // Return value : The current jitter estimate in milliseconds - double CalculateEstimate(); - - // Post process the calculated estimate - void PostProcessEstimate(); - - // Calculates the difference in delay between a sample and the - // expected delay estimated by the Kalman filter. - // - // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in milliseconds - // - deltaFS : Frame size delta, i.e. frame size at time - // T minus frame size at time T-1 - // - // Return value : The difference in milliseconds - double DeviationFromExpectedDelay(WebRtc_Word64 frameDelayMS, - WebRtc_Word32 deltaFSBytes) const; - - // Constants, filter parameters - WebRtc_Word32 _vcmId; - WebRtc_Word32 _receiverId; - const double _phi; - const double _psi; - const WebRtc_UWord32 _alphaCountMax; - const double _beta; - const double _thetaLow; - const WebRtc_UWord32 _nackLimit; - const WebRtc_Word32 _numStdDevDelayOutlier; - const WebRtc_Word32 _numStdDevFrameSizeOutlier; - const double _noiseStdDevs; - const double _noiseStdDevOffset; - - double _thetaCov[2][2]; // Estimate covariance - double _Qcov[2][2]; // Process noise covariance - double _avgFrameSize; // Average frame size - double _varFrameSize; // Frame size variance - double _maxFrameSize; // Largest frame size received (descending - // with a factor _psi) - WebRtc_UWord32 _fsSum; - WebRtc_UWord32 _fsCount; - - WebRtc_Word64 _lastUpdateT; - double _prevEstimate; // The previously returned jitter estimate - WebRtc_UWord32 _prevFrameSize; // Frame size of the previous frame - double _avgNoise; // Average of the random jitter - WebRtc_UWord32 _alphaCount; - double _filterJitterEstimate; // The filtered sum of jitter estimates - - WebRtc_UWord32 _startupCount; - - WebRtc_Word64 _latestNackTimestamp; // Timestamp in ms when the latest nack was seen - WebRtc_UWord32 _nackCount; // Keeps track of the number of nacks received, - // but never goes above _nackLimit - VCMRttFilter _rttFilter; - - enum { kStartupDelaySamples = 30 }; - enum { kFsAccuStartupSamples = 5 }; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ diff --git a/modules/video_coding/main/source/media_opt_util.cc b/modules/video_coding/main/source/media_opt_util.cc deleted file mode 100644 index a0937b66e..000000000 --- a/modules/video_coding/main/source/media_opt_util.cc +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_coding_defines.h" -#include "fec_tables_xor.h" -#include "er_tables_xor.h" -#include "nack_fec_tables.h" -#include "qm_select_data.h" -#include "media_opt_util.h" - -#include -#include -#include - -namespace webrtc { - -bool -VCMProtectionMethod::BetterThan(VCMProtectionMethod *pm) -{ - if (pm == NULL) - { - return true; - } - return pm->_score > _score; -} - -bool -VCMNackFecMethod::ProtectionFactor(const VCMProtectionParameters* /*parameters*/) -{ - // use FEC model with modification with RTT for now - return true; -} - -bool -VCMNackFecMethod::EffectivePacketLoss(const - VCMProtectionParameters* /*parameters*/) -{ - // use FEC model with modification with RTT for now - return true; -} - -bool -VCMNackFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) -{ - // Hybrid Nack FEC has three operational modes: - // 1. Low RTT - Nack only (Set FEC rates to zero) - // 2. High RTT - FEC Only - // 3. Medium RTT values - Hybrid ; in hybrid mode, we will only nack the - // residual following the decoding of the FEC (refer to JB logic) - - // Low RTT - NACK only mode - if (parameters->rtt < kLowRttNackMs) - { - // Set the FEC parameters to 0 - _protectionFactorK = 0; - _protectionFactorD = 0; - - // assume packets will be restored via NACK - // TODO: relax this assumption? - _effectivePacketLoss = 0; - _score = _efficiency; - return true; - } - // otherwise: we count on FEC; if the RTT is below a threshold, then we can - // nack the residual, based on a decision made in the JB. - // TODO(mikhal): adapt the FEC rate based on the RTT, i.e. the the level on - // which we will rely on NACK, e.g. less as we approach upper threshold. - VCMFecMethod fecMethod; - - const WebRtc_UWord8 plossMax = 129; - - // Compute the protection factor - fecMethod.ProtectionFactor(parameters); - - // Compute the effective packet loss - fecMethod.EffectivePacketLoss(parameters); - - WebRtc_UWord8 protFactorK = fecMethod._protectionFactorK; - WebRtc_UWord8 protFactorD = fecMethod._protectionFactorD; - WebRtc_UWord8 effPacketLoss = fecMethod._effectivePacketLoss; - float resPacketLoss = fecMethod._residualPacketLoss; - - // Correct FEC rates based on the RTT ( NACK effectiveness) - WebRtc_Word16 rttIndex= (WebRtc_UWord16) parameters->rtt; - float softnessRtt = 1.0; - if (parameters->rtt < kHighRttNackMs) - { - // TODO(mikhal): update table - softnessRtt = (float)VCMNackFecTable[rttIndex] / (float)4096.0; - - // soften ER with NACK on - // table depends on RTT relative to rttMax (NACK Threshold) - _effectivePacketLoss = (WebRtc_UWord8)(effPacketLoss * softnessRtt); - - // soften FEC with NACK on - // table depends on RTT relative to rttMax (NACK Threshold) - _protectionFactorK = (WebRtc_UWord8) (protFactorK * softnessRtt); - _protectionFactorD = (WebRtc_UWord8) (protFactorD * softnessRtt); - } - // else - NACK is disabled, rely on FEC only - - - // make sure I frame protection is at least larger than P frame protection, - // and at least as high as received loss - WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr); - _protectionFactorK = static_cast (VCM_MAX(packetLoss, - VCM_MAX(_scaleProtKey * protFactorD, protFactorK))); - - // check limit on amount of protection for I frame: 50% is max - if (_protectionFactorK >= plossMax) - _protectionFactorK = plossMax - 1; - - // Bit cost for NackFec - - // NACK cost: based on residual packet loss (since we should only NACK - // packets not recovered by FEC) - _efficiency = 0.0f; - if (parameters->rtt < kHighRttNackMs) - { - _efficiency = parameters->bitRate * resPacketLoss / - (1.0f + resPacketLoss); - } - else - { - // efficiency based on FEC only - // add FEC cost: ignore I frames for now - float fecRate = static_cast (_protectionFactorD) / 255.0f; - if (fecRate >= 0.0f) - _efficiency += parameters->bitRate * fecRate; - } - _score = _efficiency; - - // Protection/fec rates obtained above are defined relative to total number - // of packets (total rate: source + fec) FEC in RTP module assumes - // protection factor is defined relative to source number of packets so we - // should convert the factor to reduce mismatch between mediaOpt's rate and - // the actual one - WebRtc_UWord8 codeRate = protFactorK; - _protectionFactorK = fecMethod.ConvertFECRate(codeRate); - codeRate = protFactorD; - _protectionFactorD = fecMethod.ConvertFECRate(codeRate); - - return true; -} - -bool -VCMNackMethod::EffectivePacketLoss(WebRtc_UWord8 effPacketLoss, - WebRtc_UWord16 rttTime) -{ - WebRtc_UWord16 rttMax = MaxRttNack(); - - // For large RTT, we should rely on some Error Resilience, so we set - // packetLossEnc = 0 for RTT less than the NACK threshold - if (rttTime < rttMax) - { - effPacketLoss = 0; //may want a softer transition here - } - _effectivePacketLoss = effPacketLoss; - - return true; -} - -bool -VCMNackMethod::UpdateParameters(const VCMProtectionParameters* parameters) -{ - // Compute the effective packet loss for ER - WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr); - WebRtc_UWord16 rttTime = (WebRtc_UWord16) parameters->rtt; - EffectivePacketLoss(effPacketLoss, rttTime); - - // Compute the NACK bit cost - _efficiency = parameters->bitRate * parameters->lossPr / - (1.0f + parameters->lossPr); - _score = _efficiency; - if (parameters->rtt > _NACK_MAX_RTT) - { - _score = 0.0f; - return false; - } - return true; -} - -WebRtc_UWord8 -VCMFecMethod::BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta, - WebRtc_UWord8 packetFrameKey) const -{ - WebRtc_UWord8 boostRateKey = 2; - // default: ratio scales the FEC protection up for I frames - WebRtc_UWord8 ratio = 1; - - if (packetFrameDelta > 0) - { - ratio = (WebRtc_Word8) (packetFrameKey / packetFrameDelta); - } - ratio = VCM_MAX(boostRateKey, ratio); - - return ratio; -} - -WebRtc_UWord8 -VCMFecMethod::ConvertFECRate(WebRtc_UWord8 codeRateRTP) const -{ - return static_cast (VCM_MIN(255,(0.5 + 255.0 * codeRateRTP / - (float)(255 - codeRateRTP)))); -} - -// AvgRecoveryFEC: average recovery from FEC, assuming random packet loss model -// Computed offline for a range of FEC code parameters and loss rates -float -VCMFecMethod::AvgRecoveryFEC(const VCMProtectionParameters* parameters) const -{ - // Total (avg) bits available per frame: total rate over actual/sent frame - // rate units are kbits/frame - const WebRtc_UWord16 bitRatePerFrame = static_cast - (parameters->bitRate / (parameters->frameRate)); - - // Total (avg) number of packets per frame (source and fec): - const WebRtc_UWord8 avgTotPackets = 1 + - (WebRtc_UWord8) ((float) bitRatePerFrame * 1000.0 - / (float) (8.0 * _maxPayloadSize) + 0.5); - - // parameters for tables - const WebRtc_UWord8 codeSize = 24; - const WebRtc_UWord8 plossMax = 129; - const WebRtc_UWord16 maxErTableSize = 38700; - - // Get index for table - const float protectionFactor = (float) _protectionFactorD / (float) 255; - WebRtc_UWord8 fecPacketsPerFrame = (WebRtc_UWord8) (0.5 + protectionFactor - * avgTotPackets); - WebRtc_UWord8 sourcePacketsPerFrame = avgTotPackets - fecPacketsPerFrame; - - if (fecPacketsPerFrame == 0) - { - return 0.0; // no protection, so avg. recov from FEC == 0 - } - - // table defined up to codeSizexcodeSize code - if (sourcePacketsPerFrame > codeSize) - { - sourcePacketsPerFrame = codeSize; - } - - // check: protection factor is maxed at 50%, so this should never happen - if (sourcePacketsPerFrame < 1) - { - assert("average number of source packets below 1\n"); - } - - // index for ER tables: up to codeSizexcodeSize mask - WebRtc_UWord16 codeIndexTable[codeSize * codeSize]; - WebRtc_UWord16 k = -1; - for (WebRtc_UWord8 i = 1; i <= codeSize; i++) - { - for (WebRtc_UWord8 j = 1; j <= i; j++) - { - k += 1; - codeIndexTable[(j - 1) * codeSize + i - 1] = k; - } - } - - const WebRtc_UWord8 lossRate = (WebRtc_UWord8) (255.0 * - parameters->lossPr + 0.5f); - - const WebRtc_UWord16 codeIndex = (fecPacketsPerFrame - 1) * codeSize - + (sourcePacketsPerFrame - 1); - const WebRtc_UWord16 indexTable = codeIndexTable[codeIndex] * plossMax - + lossRate; - - const WebRtc_UWord16 codeIndex2 = (fecPacketsPerFrame) * codeSize - + (sourcePacketsPerFrame); - WebRtc_UWord16 indexTable2 = codeIndexTable[codeIndex2] * plossMax - + lossRate; - - // checks on table index - if (indexTable >= maxErTableSize) - { - assert("ER table index too large\n"); - } - - if (indexTable2 >= maxErTableSize) - { - indexTable2 = indexTable; - } - - // Get the average effective packet loss recovery from FEC - // this is from tables, computed using random loss model - WebRtc_UWord8 avgFecRecov1 = 0; - WebRtc_UWord8 avgFecRecov2 = 0; - float avgFecRecov = 0; - - if (fecPacketsPerFrame > 0) - { - avgFecRecov1 = VCMAvgFECRecoveryXOR[indexTable]; - avgFecRecov2 = VCMAvgFECRecoveryXOR[indexTable2]; - } - - // interpolate over two FEC codes - const float weightRpl = (float) (0.5 + protectionFactor * avgTotPackets) - - (float) fecPacketsPerFrame; - avgFecRecov = (float) weightRpl * (float) avgFecRecov2 + (float) - (1.0 - weightRpl) * (float) avgFecRecov1; - - return avgFecRecov; -} - -bool -VCMFecMethod::ProtectionFactor(const VCMProtectionParameters* parameters) -{ - // FEC PROTECTION SETTINGS: varies with packet loss and bitrate - - WebRtc_UWord8 packetLoss = (WebRtc_UWord8) (255 * parameters->lossPr); - - // No protection if (filtered) packetLoss is 0 - if (packetLoss == 0) - { - _protectionFactorK = 0; - _protectionFactorD = 0; - return true; - } - - // Size of tables - const WebRtc_UWord16 maxFecTableSize = 6450; - // Parameters for range of rate and packet loss for tables - const WebRtc_UWord8 ratePar1 = 5; - const WebRtc_UWord8 ratePar2 = 49; - const WebRtc_UWord8 plossMax = 129; - - const float bitRate = parameters->bitRate; - - // Total (avg) bits available per frame: total rate over actual/frame_rate. - // Units are kbits/frame - const WebRtc_UWord16 bitRatePerFrame = static_cast - (bitRate / - (parameters->frameRate)); - - // TODO (marpan): Incorporate frame size (bpp) into FEC setting - - // Total (avg) number of packets per frame (source and fec): - const WebRtc_UWord8 avgTotPackets = 1 + (WebRtc_UWord8) - ((float) bitRatePerFrame * 1000.0 - / (float) (8.0 * _maxPayloadSize) + 0.5); - - - // First partition protection: ~ 20% - WebRtc_UWord8 firstPartitionProt = (WebRtc_UWord8) (255 * 0.20); - - // Threshold on packetLoss and bitRrate/frameRate (=average #packets), - // above which we allocate protection to cover at least roughly - // first partition size. - WebRtc_UWord8 lossThr = 0; - WebRtc_UWord8 packetNumThr = 1; - - // Modulation of protection with available bits/frame (or avgTotpackets) - float weight1 = 0.5; - float weight2 = 0.5; - if (avgTotPackets > 4) - { - weight1 = 0.75; - weight2 = 0.25; - } - if (avgTotPackets > 6) - { - weight1 = 1.5; - weight2 = 0.; - } - - // FEC rate parameters: for P and I frame - WebRtc_UWord8 codeRateDelta = 0; - WebRtc_UWord8 codeRateKey = 0; - - // Get index for table: the FEC protection depends on the (average) - // available bits/frame. The range on the rate index corresponds to rates - // (bps) from 200k to 8000k, for 30fps - WebRtc_UWord8 rateIndexTable = - (WebRtc_UWord8) VCM_MAX(VCM_MIN((bitRatePerFrame - ratePar1) / - ratePar1, ratePar2), 0); - - // Restrict packet loss range to 50: - // current tables defined only up to 50% - if (packetLoss >= plossMax) - { - packetLoss = plossMax - 1; - } - WebRtc_UWord16 indexTable = rateIndexTable * plossMax + packetLoss; - - // Check on table index - assert(indexTable < maxFecTableSize); - - // Protection factor for P frame - codeRateDelta = VCMCodeRateXORTable[indexTable]; - - if (packetLoss > lossThr && avgTotPackets > packetNumThr) - { - // Average with minimum protection level given by (average) total - // number of packets - codeRateDelta = static_cast((weight1 * - (float) codeRateDelta + weight2 * 255.0 / (float) avgTotPackets)); - - // Set a minimum based on first partition size. - if (codeRateDelta < firstPartitionProt) - { - codeRateDelta = firstPartitionProt; - } - } - - // Check limit on amount of protection for P frame; 50% is max. - if (codeRateDelta >= plossMax) - { - codeRateDelta = plossMax - 1; - } - - // For Key frame: - // Effectively at a higher rate, so we scale/boost the rate - // The boost factor may depend on several factors: ratio of packet - // number of I to P frames, how much protection placed on P frames, etc. - const WebRtc_UWord8 packetFrameDelta = (WebRtc_UWord8) - (0.5 + parameters->packetsPerFrame); - const WebRtc_UWord8 packetFrameKey = (WebRtc_UWord8) - (0.5 + parameters->packetsPerFrameKey); - const WebRtc_UWord8 boostKey = BoostCodeRateKey(packetFrameDelta, - packetFrameKey); - - rateIndexTable = (WebRtc_UWord8) VCM_MAX(VCM_MIN( - 1 + (boostKey * bitRatePerFrame - ratePar1) / - ratePar1,ratePar2),0); - WebRtc_UWord16 indexTableKey = rateIndexTable * plossMax + packetLoss; - - indexTableKey = VCM_MIN(indexTableKey, maxFecTableSize); - - // Check on table index - assert(indexTableKey < maxFecTableSize); - - // Protection factor for I frame - codeRateKey = VCMCodeRateXORTable[indexTableKey]; - - // Boosting for Key frame. - WebRtc_UWord32 boostKeyProt = _scaleProtKey * codeRateDelta; - if ( boostKeyProt >= plossMax) - { - boostKeyProt = plossMax - 1; - } - - // Make sure I frame protection is at least larger than P frame protection, - // and at least as high as filtered packet loss. - codeRateKey = static_cast (VCM_MAX(packetLoss, - VCM_MAX(boostKeyProt, codeRateKey))); - - // Check limit on amount of protection for I frame: 50% is max. - if (codeRateKey >= plossMax) - { - codeRateKey = plossMax - 1; - } - - _protectionFactorK = codeRateKey; - _protectionFactorD = codeRateDelta; - - // DONE WITH FEC PROTECTION SETTINGS - return true; -} - -bool -VCMFecMethod::EffectivePacketLoss(const VCMProtectionParameters* parameters) -{ - // ER SETTINGS: - // Effective packet loss to encoder is based on RPL (residual packet loss) - // this is a soft setting based on degree of FEC protection - // RPL = received/input packet loss - average_FEC_recovery - // note: received/input packet loss may be filtered based on FilteredLoss - - // The input packet loss: - WebRtc_UWord8 effPacketLoss = (WebRtc_UWord8) (255 * parameters->lossPr); - - float scaleErRS = 0.5; - float scaleErXOR = 0.5; - float minErLevel = (float) 0.025; - // float scaleErRS = 1.0; - // float scaleErXOR = 1.0; - // float minErLevel = (float) 0.0; - - float avgFecRecov = 0.; - // Effective packet loss for ER: - float scaleEr = scaleErXOR; - avgFecRecov = AvgRecoveryFEC(parameters); - - // Residual Packet Loss: - _residualPacketLoss = (float) (effPacketLoss - avgFecRecov) / (float) 255.0; - - //Effective Packet Loss for encoder: - _effectivePacketLoss = 0; - if (effPacketLoss > 0) { - _effectivePacketLoss = VCM_MAX((effPacketLoss - - (WebRtc_UWord8)(scaleEr * avgFecRecov)), - static_cast(minErLevel * 255)); - } - - // DONE WITH ER SETTING - return true; -} - -bool -VCMFecMethod::UpdateParameters(const VCMProtectionParameters* parameters) -{ - // Compute the protection factor - ProtectionFactor(parameters); - - // Compute the effective packet loss - EffectivePacketLoss(parameters); - - // Compute the bit cost - // Ignore key frames for now. - float fecRate = static_cast (_protectionFactorD) / 255.0f; - if (fecRate >= 0.0f) - { - // use this formula if the fecRate (protection factor) is defined - // relative to number of source packets - // this is the case for the previous tables: - // _efficiency = parameters->bitRate * ( 1.0 - 1.0 / (1.0 + fecRate)); - - // in the new tables, the fecRate is defined relative to total number of - // packets (total rate), so overhead cost is: - _efficiency = parameters->bitRate * fecRate; - } - else - { - _efficiency = 0.0f; - } - _score = _efficiency; - - // Protection/fec rates obtained above is defined relative to total number - // of packets (total rate: source+fec) FEC in RTP module assumes protection - // factor is defined relative to source number of packets so we should - // convert the factor to reduce mismatch between mediaOpt suggested rate and - // the actual rate - _protectionFactorK = ConvertFECRate(_protectionFactorK); - _protectionFactorD = ConvertFECRate(_protectionFactorD); - - return true; -} - -bool -VCMIntraReqMethod::UpdateParameters(const VCMProtectionParameters* parameters) -{ - float packetRate = parameters->packetsPerFrame * parameters->frameRate; - // Assume that all lost packets cohere to different frames - float lossRate = parameters->lossPr * packetRate; - if (parameters->keyFrameSize <= 1e-3) - { - _score = FLT_MAX; - return false; - } - _efficiency = lossRate * parameters->keyFrameSize; - _score = _efficiency; - if (parameters->lossPr >= 1.0f / parameters->keyFrameSize || - parameters->rtt > _IREQ_MAX_RTT) - { - return false; - } - return true; -} - -bool -VCMPeriodicIntraMethod::UpdateParameters(const - VCMProtectionParameters* /*parameters*/) -{ - // Periodic I-frames. The last thing we want to use. - _efficiency = 0.0f; - _score = FLT_MAX; - return true; -} - -bool -VCMMbIntraRefreshMethod::UpdateParameters(const - VCMProtectionParameters* parameters) -{ - // Assume optimal for now. - _efficiency = parameters->bitRate * parameters->lossPr / - (1.0f + parameters->lossPr); - _score = _efficiency; - if (parameters->bitRate < _MBREF_MIN_BITRATE) - { - return false; - } - return true; -} - -WebRtc_UWord16 -VCMNackMethod::MaxRttNack() const -{ - return _NACK_MAX_RTT; -} - -VCMLossProtectionLogic::~VCMLossProtectionLogic() -{ - ClearLossProtections(); -} - -void -VCMLossProtectionLogic::ClearLossProtections() -{ - ListItem *item; - while ((item = _availableMethods.First()) != 0) { - VCMProtectionMethod *method = static_cast - (item->GetItem()); - if (method != NULL) - { - delete method; - } - _availableMethods.PopFront(); - } - _selectedMethod = NULL; -} - -bool -VCMLossProtectionLogic::AddMethod(VCMProtectionMethod *newMethod) -{ - VCMProtectionMethod *method; - ListItem *item; - if (newMethod == NULL) - { - return false; - } - for (item = _availableMethods.First(); item != NULL; - item = _availableMethods.Next(item)) - { - method = static_cast (item->GetItem()); - if (method != NULL && method->Type() == newMethod->Type()) - { - return false; - } - } - _availableMethods.PushBack(newMethod); - return true; -} -bool -VCMLossProtectionLogic::RemoveMethod(VCMProtectionMethodEnum methodType) -{ - VCMProtectionMethod *method; - ListItem *item; - bool foundAndRemoved = false; - for (item = _availableMethods.First(); item != NULL; - item = _availableMethods.Next(item)) - { - method = static_cast (item->GetItem()); - if (method != NULL && method->Type() == methodType) - { - if (_selectedMethod != NULL && - _selectedMethod->Type() == method->Type()) - { - _selectedMethod = NULL; - } - _availableMethods.Erase(item); - item = NULL; - delete method; - foundAndRemoved = true; - } - } - return foundAndRemoved; -} - -VCMProtectionMethod* -VCMLossProtectionLogic::FindMethod(VCMProtectionMethodEnum methodType) const -{ - VCMProtectionMethod *method; - ListItem *item; - for (item = _availableMethods.First(); item != NULL; - item = _availableMethods.Next(item)) - { - method = static_cast (item->GetItem()); - if (method != NULL && method->Type() == methodType) - { - return method; - } - } - return NULL; -} - -float -VCMLossProtectionLogic::HighestOverhead() const -{ - VCMProtectionMethod *method; - ListItem *item; - float highestOverhead = 0.0f; - for (item = _availableMethods.First(); item != NULL; - item = _availableMethods.Next(item)) - { - method = static_cast (item->GetItem()); - if (method != NULL && method->RequiredBitRate() > highestOverhead) - { - highestOverhead = method->RequiredBitRate(); - } - } - return highestOverhead; -} - -void -VCMLossProtectionLogic::UpdateRtt(WebRtc_UWord32 rtt) -{ - _rtt = rtt; -} - -void -VCMLossProtectionLogic::UpdateResidualPacketLoss(float residualPacketLoss) -{ - _residualPacketLoss = residualPacketLoss; -} - -void -VCMLossProtectionLogic::UpdateFecType(VCMFecTypes fecType) -{ - _fecType = fecType; -} - -void -VCMLossProtectionLogic::UpdateLossPr(WebRtc_UWord8 lossPr255) -{ - const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - UpdateMaxLossHistory(lossPr255, now); - _lossPr255.Apply(static_cast (now - _lastPrUpdateT), - static_cast (lossPr255)); - _lastPrUpdateT = now; - _lossPr = _lossPr255.Value() / 255.0f; -} - -void -VCMLossProtectionLogic::UpdateMaxLossHistory(WebRtc_UWord8 lossPr255, - WebRtc_Word64 now) -{ - if (_lossPrHistory[0].timeMs >= 0 && - now - _lossPrHistory[0].timeMs < kLossPrShortFilterWinMs) - { - if (lossPr255 > _shortMaxLossPr255) - { - _shortMaxLossPr255 = lossPr255; - } - } - else - { - // Only add a new value to the history once a second - if (_lossPrHistory[0].timeMs == -1) - { - // First, no shift - _shortMaxLossPr255 = lossPr255; - } - else - { - // Shift - for (WebRtc_Word32 i = (kLossPrHistorySize - 2); i >= 0; i--) - { - _lossPrHistory[i + 1].lossPr255 = _lossPrHistory[i].lossPr255; - _lossPrHistory[i + 1].timeMs = _lossPrHistory[i].timeMs; - } - } - if (_shortMaxLossPr255 == 0) - { - _shortMaxLossPr255 = lossPr255; - } - - _lossPrHistory[0].lossPr255 = _shortMaxLossPr255; - _lossPrHistory[0].timeMs = now; - _shortMaxLossPr255 = 0; - } -} - -WebRtc_UWord8 -VCMLossProtectionLogic::MaxFilteredLossPr(WebRtc_Word64 nowMs) const -{ - WebRtc_UWord8 maxFound = _shortMaxLossPr255; - if (_lossPrHistory[0].timeMs == -1) - { - return maxFound; - } - for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++) - { - if (_lossPrHistory[i].timeMs == -1) - { - break; - } - if (nowMs - _lossPrHistory[i].timeMs > - kLossPrHistorySize * kLossPrShortFilterWinMs) - { - // This sample (and all samples after this) is too old - break; - } - if (_lossPrHistory[i].lossPr255 > maxFound) - { - // This sample is the largest one this far into the history - maxFound = _lossPrHistory[i].lossPr255; - } - } - return maxFound; -} - -WebRtc_UWord8 -VCMLossProtectionLogic::FilteredLoss() const -{ - //take the average received loss - //return static_cast(_lossPr255.Value() + 0.5f); - - //TODO: Update for hybrid - //take the windowed max of the received loss - if (_selectedMethod != NULL && _selectedMethod->Type() == kFEC) - { - return MaxFilteredLossPr(VCMTickTime::MillisecondTimestamp()); - } - else - { - return static_cast (_lossPr255.Value() + 0.5); - } -} - -void -VCMLossProtectionLogic::UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc) -{ - _lossPr = (float) packetLossEnc / (float) 255.0; -} - -void -VCMLossProtectionLogic::UpdateBitRate(float bitRate) -{ - _bitRate = bitRate; -} - -void -VCMLossProtectionLogic::UpdatePacketsPerFrame(float nPackets) -{ - const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - _packetsPerFrame.Apply(static_cast(now - _lastPacketPerFrameUpdateT), - nPackets); - _lastPacketPerFrameUpdateT = now; -} - -void -VCMLossProtectionLogic::UpdatePacketsPerFrameKey(float nPackets) -{ - const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - _packetsPerFrameKey.Apply(static_cast(now - - _lastPacketPerFrameUpdateTKey), nPackets); - _lastPacketPerFrameUpdateTKey = now; -} - -void -VCMLossProtectionLogic::UpdateKeyFrameSize(float keyFrameSize) -{ - _keyFrameSize = keyFrameSize; -} - -void -VCMLossProtectionLogic::UpdateFrameSize(WebRtc_UWord16 width, - WebRtc_UWord16 height) -{ - _codecWidth = width; - _codecHeight = height; -} - -bool -VCMLossProtectionLogic::UpdateMethod(VCMProtectionMethod *newMethod /*=NULL */) -{ - _currentParameters.rtt = _rtt; - _currentParameters.lossPr = _lossPr; - _currentParameters.bitRate = _bitRate; - _currentParameters.frameRate = _frameRate; // rename actual frame rate? - _currentParameters.keyFrameSize = _keyFrameSize; - _currentParameters.fecRateDelta = _fecRateDelta; - _currentParameters.fecRateKey = _fecRateKey; - _currentParameters.packetsPerFrame = _packetsPerFrame.Value(); - _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value(); - _currentParameters.residualPacketLoss = _residualPacketLoss; - _currentParameters.fecType = _fecType; - _currentParameters.codecWidth = _codecWidth; - _currentParameters.codecHeight = _codecHeight; - - if (newMethod == NULL) - { - //_selectedMethod = _bestNotOkMethod = NULL; - VCMProtectionMethod *method; - ListItem *item; - for (item = _availableMethods.First(); item != NULL; - item = _availableMethods.Next(item)) - { - method = static_cast (item->GetItem()); - if (method != NULL) - { - if (method->Type() == kFEC) - { - _selectedMethod = method; - } - if (method->Type() == kNACK) - { - _selectedMethod = method; - } - if (method->Type() == kNackFec) - { - _selectedMethod = method; - } - method->UpdateParameters(&_currentParameters); - } - } - if (_selectedMethod != NULL && _selectedMethod->Type() != kFEC) - { - _selectedMethod = method; - } - } - else - { - _selectedMethod = newMethod; - _selectedMethod->UpdateParameters(&_currentParameters); - } - return true; -} - -VCMProtectionMethod* -VCMLossProtectionLogic::SelectedMethod() const -{ - return _selectedMethod; -} - -void VCMLossProtectionLogic::Reset() -{ - const WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - _lastPrUpdateT = now; - _lastPacketPerFrameUpdateT = now; - _lastPacketPerFrameUpdateTKey = now; - _lossPr255.Reset(0.9999f); - _packetsPerFrame.Reset(0.9999f); - _fecRateDelta = _fecRateKey = 0; - for (WebRtc_Word32 i = 0; i < kLossPrHistorySize; i++) - { - _lossPrHistory[i].lossPr255 = 0; - _lossPrHistory[i].timeMs = -1; - } - _shortMaxLossPr255 = 0; - ClearLossProtections(); -} - -} diff --git a/modules/video_coding/main/source/media_opt_util.h b/modules/video_coding/main/source/media_opt_util.h deleted file mode 100644 index 829840656..000000000 --- a/modules/video_coding/main/source/media_opt_util.h +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_ -#define WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_ - -#include "typedefs.h" -#include "list_wrapper.h" -#include "trace.h" -#include "exp_filter.h" -#include "internal_defines.h" -#include "tick_time.h" - -#include -#include - - -namespace webrtc -{ -class ListWrapper; - -enum { kLossPrHistorySize = 30 }; // 30 time periods -enum { kLossPrShortFilterWinMs = 1000 }; // 1000 ms, total filter length is 30 000 ms - -enum VCMFecTypes -{ - kXORFec -}; - -// Thresholds for hybrid NACK/FEC -// common to media optimization and the jitter buffer. -enum HybridNackTH { - kHighRttNackMs = 100, - kLowRttNackMs = 20 -}; - -struct VCMProtectionParameters -{ - VCMProtectionParameters() : rtt(0), lossPr(0), bitRate(0), packetsPerFrame(0), - frameRate(0), keyFrameSize(0), fecRateDelta(0), fecRateKey(0), - residualPacketLoss(0.0), fecType(kXORFec), codecWidth(0), - codecHeight(0) {} - - WebRtc_UWord32 rtt; - float lossPr; - float bitRate; - float packetsPerFrame; - float packetsPerFrameKey; - float frameRate; - float keyFrameSize; - WebRtc_UWord8 fecRateDelta; - WebRtc_UWord8 fecRateKey; - float residualPacketLoss; - VCMFecTypes fecType; - WebRtc_UWord16 codecWidth; - WebRtc_UWord16 codecHeight; - -}; - - -/******************************/ -/* VCMProtectionMethod class */ -/****************************/ - -enum VCMProtectionMethodEnum -{ - kNACK, - kFEC, - kNackFec, - kIntraRequest, // I-frame request - kPeriodicIntra, // I-frame refresh - kMBIntraRefresh, // Macro block refresh - kNone -}; - -class VCMLossProbabilitySample -{ -public: - VCMLossProbabilitySample() : lossPr255(0), timeMs(-1) {}; - - WebRtc_UWord8 lossPr255; - WebRtc_Word64 timeMs; -}; - - - -class VCMProtectionMethod -{ -public: - //friend VCMProtectionMethod; - VCMProtectionMethod(VCMProtectionMethodEnum type) : _protectionFactorK(0), - _protectionFactorD(0), _residualPacketLoss(0.0), _scaleProtKey(2.0), - _maxPayloadSize(1460), _efficiency(0), _score(0), _type(type) {} - virtual ~VCMProtectionMethod() {} - - // Updates the efficiency of the method using the parameters provided - // - // Input: - // - parameters : Parameters used to calculate the efficiency - // - // Return value : True if this method is recommended in - // the given conditions. - virtual bool UpdateParameters(const VCMProtectionParameters* parameters) = 0; - - // Returns the protection type - // - // Return value : The protection type - enum VCMProtectionMethodEnum Type() const { return _type; } - - // Evaluates if this protection method is considered - // better than the provided method. - // - // Input: - // - pm : The protection method to compare with - bool BetterThan(VCMProtectionMethod *pm); - - // Returns the bit rate required by this protection method - // during these conditions. - // - // Return value : Required bit rate - virtual float RequiredBitRate() { return _efficiency; } - - // Returns the effective packet loss for ER, required by this protection method - // - // Return value : Required effective packet loss - virtual WebRtc_UWord8 RequiredPacketLossER() { return _effectivePacketLoss; } - - // Extracts the FEC protection factor for Key frame, required by this protection method - // - // Return value : Required protectionFactor for Key frame - virtual WebRtc_UWord8 RequiredProtectionFactorK() { return _protectionFactorK; } - - // Extracts the FEC protection factor for Delta frame, required by this protection method - // - // Return value : Required protectionFactor for delta frame - virtual WebRtc_UWord8 RequiredProtectionFactorD() { return _protectionFactorD; } - - WebRtc_UWord8 _effectivePacketLoss; - WebRtc_UWord8 _protectionFactorK; - WebRtc_UWord8 _protectionFactorD; - float _residualPacketLoss; - float _scaleProtKey; - WebRtc_Word32 _maxPayloadSize; - -protected: - float _efficiency; - float _score; - -private: - const enum VCMProtectionMethodEnum _type; - -}; - -class VCMNackMethod : public VCMProtectionMethod -{ -public: - VCMNackMethod() : VCMProtectionMethod(kNACK), _NACK_MAX_RTT(200) {} - virtual ~VCMNackMethod() {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); - //get the effective packet loss for ER - bool EffectivePacketLoss(WebRtc_UWord8 effPacketLoss, WebRtc_UWord16 rttTime); - //get the threshold for NACK - WebRtc_UWord16 MaxRttNack() const; -private: - const WebRtc_UWord16 _NACK_MAX_RTT; -}; - -class VCMFecMethod : public VCMProtectionMethod -{ -public: - VCMFecMethod() : VCMProtectionMethod(kFEC) {} - virtual ~VCMFecMethod() {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); - //get the effective packet loss for ER - bool EffectivePacketLoss(const VCMProtectionParameters* parameters); - //get the FEC protection factors - bool ProtectionFactor(const VCMProtectionParameters* parameters); - //get the boost for key frame protection - WebRtc_UWord8 BoostCodeRateKey(WebRtc_UWord8 packetFrameDelta, - WebRtc_UWord8 packetFrameKey) const; - //convert the rates: defined relative to total# packets or source# packets - WebRtc_UWord8 ConvertFECRate(WebRtc_UWord8 codeRate) const; - //get the average effective recovery from FEC: for random loss model - float AvgRecoveryFEC(const VCMProtectionParameters* parameters) const; -}; - - -class VCMNackFecMethod : public VCMProtectionMethod -{ -public: - VCMNackFecMethod() : VCMProtectionMethod(kNackFec) {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); - //get the effective packet loss for ER - bool EffectivePacketLoss(const VCMProtectionParameters* parameters); - //get the FEC protection factors - bool ProtectionFactor(const VCMProtectionParameters* parameters); - -}; - - -class VCMIntraReqMethod : public VCMProtectionMethod -{ -public: - VCMIntraReqMethod() : VCMProtectionMethod(kIntraRequest), _IREQ_MAX_RTT(150) {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); -private: - const WebRtc_UWord32 _IREQ_MAX_RTT; -}; - -class VCMPeriodicIntraMethod : public VCMProtectionMethod -{ -public: - VCMPeriodicIntraMethod() : VCMProtectionMethod(kPeriodicIntra) {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); -}; - -class VCMMbIntraRefreshMethod : public VCMProtectionMethod -{ -public: - VCMMbIntraRefreshMethod() : - VCMProtectionMethod(kMBIntraRefresh), _MBREF_MIN_BITRATE(150) {} - virtual bool UpdateParameters(const VCMProtectionParameters* parameters); - virtual float RequiredBitRate() { return 0.0; } -private: - const WebRtc_UWord32 _MBREF_MIN_BITRATE; -}; - -class VCMLossProtectionLogic -{ -public: - VCMLossProtectionLogic() : _availableMethods(), _selectedMethod(NULL), - _bestNotOkMethod(NULL), _rtt(0), _lossPr(0.0f), _bitRate(0.0f), _frameRate(0.0f), - _keyFrameSize(0.0f), _fecRateKey(0), _fecRateDelta(0), _lastPrUpdateT(0), - _lossPr255(0.9999f), _lossPrHistory(), _shortMaxLossPr255(0), - _packetsPerFrame(0.9999f), _packetsPerFrameKey(0.9999f), _residualPacketLoss(0), - _boostRateKey(2), _codecWidth(0), _codecHeight(0) - { Reset(); } - - ~VCMLossProtectionLogic(); - - void ClearLossProtections(); - bool AddMethod(VCMProtectionMethod *newMethod); - bool RemoveMethod(VCMProtectionMethodEnum methodType); - VCMProtectionMethod* FindMethod(VCMProtectionMethodEnum methodType) const; - float HighestOverhead() const; - - // Update the round-trip time - // - // Input: - // - rtt : Round-trip time in seconds. - void UpdateRtt(WebRtc_UWord32 rtt); - - // Update residual packet loss - // - // Input: - // - residualPacketLoss : residual packet loss: effective loss after FEC recovery - void UpdateResidualPacketLoss(float residualPacketLoss); - - // Update fecType - // - // Input: - // - fecType : kXORFec for generic XOR FEC - void UpdateFecType(VCMFecTypes fecType); - - // Update the loss probability. - // - // Input: - // - lossPr255 : The packet loss probability in the interval [0, 255], - // reported by RTCP. - void UpdateLossPr(WebRtc_UWord8 lossPr255); - - // Update the filtered packet loss. - // - // Input: - // - packetLossEnc : The reported packet loss filtered (max window or average) - void UpdateFilteredLossPr(WebRtc_UWord8 packetLossEnc); - - // Update the current target bit rate. - // - // Input: - // - bitRate : The current target bit rate in kbits/s - void UpdateBitRate(float bitRate); - - // Update the number of packets per frame estimate, for delta frames - // - // Input: - // - nPackets : Number of packets used to send the latest frame. - void UpdatePacketsPerFrame(float nPackets); - - // Update the number of packets per frame estimate, for key frames - // - // Input: - // - nPackets : Number of packets used to send the latest frame. - void UpdatePacketsPerFrameKey(float nPackets); - - // Update the keyFrameSize estimate - // - // Input: - // - keyFrameSize : The size of the latest sent key frame. - void UpdateKeyFrameSize(float keyFrameSize); - - // Update the frame rate - // - // Input: - // - frameRate : The current target frame rate. - void UpdateFrameRate(float frameRate) { _frameRate = frameRate; } - - // Update the frame size - // - // Input: - // - width : The codec frame width. - // - height : The codec frame height. - void UpdateFrameSize(WebRtc_UWord16 width, WebRtc_UWord16 height); - - // The amount of packet loss to cover for with FEC. - // - // Input: - // - fecRateKey : Packet loss to cover for with FEC when sending key frames. - // - fecRateDelta : Packet loss to cover for with FEC when sending delta frames. - void UpdateFECRates(WebRtc_UWord8 fecRateKey, WebRtc_UWord8 fecRateDelta) - { _fecRateKey = fecRateKey; _fecRateDelta = fecRateDelta; } - - // Update the protection methods with the current VCMProtectionParameters and - // choose the best method available. The update involves computing the robustness settings - // for the protection method. - // - // Input: - // - newMethod : If not NULL, this is method will be selected by force. - // - // Return value : True if the selected method is recommended using these settings, - // false if it's the best method, but still not recommended to be used. - // E.g. if NACK is the best available, but the RTT is too large, false - // will be returned. - bool UpdateMethod(VCMProtectionMethod *newMethod = NULL); - - // Returns the method currently selected. - // - // Return value : The protection method currently selected. - VCMProtectionMethod* SelectedMethod() const; - - // Returns the filtered loss probability in the interval [0, 255]. - // - // Return value : The filtered loss probability - WebRtc_UWord8 FilteredLoss() const; - - // Get constraint on NACK - // - // return value : RTT threshold for using NACK - WebRtc_UWord8 GetNackThreshold() const; - - void Reset(); - -private: - // Sets the available loss protection methods. - void UpdateMaxLossHistory(WebRtc_UWord8 lossPr255, WebRtc_Word64 now); - WebRtc_UWord8 MaxFilteredLossPr(WebRtc_Word64 nowMs) const; - ListWrapper _availableMethods; - VCMProtectionMethod* _selectedMethod; - VCMProtectionMethod* _bestNotOkMethod; - VCMProtectionParameters _currentParameters; - WebRtc_UWord32 _rtt; - float _lossPr; - float _bitRate; - float _frameRate; - float _keyFrameSize; - WebRtc_UWord8 _fecRateKey; - WebRtc_UWord8 _fecRateDelta; - WebRtc_Word64 _lastPrUpdateT; - WebRtc_Word64 _lastPacketPerFrameUpdateT; - WebRtc_Word64 _lastPacketPerFrameUpdateTKey; - VCMExpFilter _lossPr255; - VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize]; - WebRtc_UWord8 _shortMaxLossPr255; - VCMExpFilter _packetsPerFrame; - VCMExpFilter _packetsPerFrameKey; - float _residualPacketLoss; - WebRtc_UWord8 _boostRateKey; - VCMFecTypes _fecType; - WebRtc_UWord16 _codecWidth; - WebRtc_UWord16 _codecHeight; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPT_UTIL_H_ diff --git a/modules/video_coding/main/source/media_optimization.cc b/modules/video_coding/main/source/media_optimization.cc deleted file mode 100644 index e21598615..000000000 --- a/modules/video_coding/main/source/media_optimization.cc +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "media_optimization.h" -#include "content_metrics_processing.h" -#include "frame_dropper.h" -#include "qm_select.h" - -namespace webrtc { - -VCMMediaOptimization::VCMMediaOptimization(WebRtc_Word32 id): -_id(id), -_maxBitRate(0), -_sendCodecType(kVideoCodecUnknown), -_codecWidth(0), -_codecHeight(0), -_userFrameRate(0), -_lossProtOverhead(0), -_packetLossEnc(0), -_fractionLost(0), -_sendStatisticsZeroEncode(0), -_maxPayloadSize(1460), -_lastBitRate(0), -_targetBitRate(0), -_incomingFrameRate(0), -_enableQm(false), -_videoProtectionCallback(NULL), -_videoQMSettingsCallback(NULL), -_encodedFrameSamples(), -_avgSentBitRateBps(0.0f), -_keyFrameCnt(0), -_deltaFrameCnt(0), -_lastQMUpdateTime(0), -_lastChangeTime(0) -{ - memset(_sendStatistics, 0, sizeof(_sendStatistics)); - memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes)); - - _frameDropper = new VCMFrameDropper(_id); - _lossProtLogic = new VCMLossProtectionLogic(); - _content = new VCMContentMetricsProcessing(); - _qms = new VCMQmSelect(); -} - -VCMMediaOptimization::~VCMMediaOptimization(void) -{ - _lossProtLogic->ClearLossProtections(); - delete _lossProtLogic; - delete _frameDropper; - delete _content; - delete _qms; -} - -WebRtc_Word32 -VCMMediaOptimization::Reset() -{ - memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes)); - InputFrameRate(); // Resets _incomingFrameRate - _frameDropper->Reset(); - _lossProtLogic->Reset(); - _frameDropper->SetRates(0, 0); - _content->Reset(); - _qms->Reset(); - _lossProtLogic->UpdateFrameRate(_incomingFrameRate); - _lossProtLogic->Reset(); - _sendStatisticsZeroEncode = 0; - _lastBitRate = 0; - _targetBitRate = 0; - _lossProtOverhead = 0; - _codecWidth = 0; - _codecHeight = 0; - _userFrameRate = 0; - _keyFrameCnt = 0; - _deltaFrameCnt = 0; - _lastQMUpdateTime = 0; - _lastChangeTime = 0; - for (WebRtc_Word32 i = 0; i < kBitrateMaxFrameSamples; i++) - { - _encodedFrameSamples[i]._sizeBytes = -1; - _encodedFrameSamples[i]._timeCompleteMs = -1; - } - _avgSentBitRateBps = 0.0f; - return VCM_OK; -} - -WebRtc_UWord32 -VCMMediaOptimization::SetTargetRates(WebRtc_UWord32 bitRate, - WebRtc_UWord8 &fractionLost, - WebRtc_UWord32 roundTripTimeMs) -{ - VCMProtectionMethod *selectedMethod = _lossProtLogic->SelectedMethod(); - _lossProtLogic->UpdateBitRate(static_cast(bitRate)); - _lossProtLogic->UpdateLossPr(fractionLost); - _lossProtLogic->UpdateRtt(roundTripTimeMs); - _lossProtLogic->UpdateResidualPacketLoss(static_cast(fractionLost)); - - VCMFecTypes fecType = kXORFec; // generic FEC - _lossProtLogic->UpdateFecType(fecType); - - // Get frame rate for encoder: this is the actual/sent frame rate - float actualFrameRate = SentFrameRate(); - - // sanity - if (actualFrameRate < 1.0) - { - actualFrameRate = 1.0; - } - - // Update frame rate for the loss protection logic class: frame rate should - // be the actual/sent rate - _lossProtLogic->UpdateFrameRate(actualFrameRate); - - _fractionLost = fractionLost; - - // The effective packet loss may be the received loss or filtered, i.e., - // average or max filter may be used. - // We should think about which filter is appropriate for low/high bit rates, - // low/high loss rates, etc. - WebRtc_UWord8 packetLossEnc = _lossProtLogic->FilteredLoss(); - - //For now use the filtered loss for computing the robustness settings - _lossProtLogic->UpdateFilteredLossPr(packetLossEnc); - - // Rate cost of the protection methods - _lossProtOverhead = 0; - - if (selectedMethod && (selectedMethod->Type() == kFEC || - selectedMethod->Type() == kNackFec )) - { - - // Update method will compute the robustness settings for the given - // protection method and the overhead cost - // the protection method is set by the user via SetVideoProtection. - // The robustness settings are: the effective packet loss for ER and the - // FEC protection settings - _lossProtLogic->UpdateMethod(); - - // Get the code rate for Key frames - const WebRtc_UWord8 codeRateKeyRTP = selectedMethod->RequiredProtectionFactorK(); - - // Get the code rate for Delta frames - const WebRtc_UWord8 codeRateDeltaRTP = selectedMethod->RequiredProtectionFactorD(); - - // Get the effective packet loss for ER - packetLossEnc = selectedMethod->RequiredPacketLossER(); - - // NACK is on for NACK and NackFec protection method: off for FEC method - bool nackStatus = (selectedMethod->Type() == kNackFec || - selectedMethod->Type() == kNACK); - - if(_videoProtectionCallback) - { - _videoProtectionCallback->ProtectionRequest(codeRateDeltaRTP, - codeRateKeyRTP, - nackStatus); - } - } - - // Get the bit cost of protection method - _lossProtOverhead = static_cast(_lossProtLogic->HighestOverhead() + 0.5f); - - // Update effective packet loss for encoder: note: fractionLost was passed as reference - fractionLost = packetLossEnc; - - WebRtc_UWord32 nackBitRate=0; - if(selectedMethod && _lossProtLogic->FindMethod(kNACK) != NULL) - { - // TODO(mikhal): update frame dropper with bit rate including both nack and fec - // Make sure we don't over-use the channel momentarily. This is - // necessary for NACK since it can be very bursty. - nackBitRate = (_lastBitRate * fractionLost) / 255; - if (nackBitRate > _targetBitRate) - { - nackBitRate = _targetBitRate; - } - _frameDropper->SetRates(static_cast(bitRate - nackBitRate), 0); - } - else - { - _frameDropper->SetRates(static_cast(bitRate - _lossProtOverhead), 0); - } - - // This may be used for UpdateEncoderBitRate: lastBitRate is total rate, - // before compensation - _lastBitRate = _targetBitRate; - - //Source coding rate: total rate - protection overhead - _targetBitRate = bitRate - _lossProtOverhead; - - if (_enableQm) - { - //Update QM with rates - _qms->UpdateRates((float)_targetBitRate, _avgSentBitRateBps, - _incomingFrameRate, _fractionLost); - //Check for QM selection - bool selectQM = checkStatusForQMchange(); - if (selectQM) - { - SelectQuality(); - } - // Reset the short-term averaged content data. - _content->ResetShortTermAvgData(); - } - - return _targetBitRate; -} - - -bool -VCMMediaOptimization::DropFrame() -{ - // leak appropriate number of bytes - _frameDropper->Leak((WebRtc_UWord32)(InputFrameRate() + 0.5f)); - return _frameDropper->DropFrame(); -} - -WebRtc_Word32 -VCMMediaOptimization::SentFrameCount(VCMFrameCount &frameCount) const -{ - frameCount.numDeltaFrames = _deltaFrameCnt; - frameCount.numKeyFrames = _keyFrameCnt; - return VCM_OK; -} - -WebRtc_Word32 -VCMMediaOptimization::SetEncodingData(VideoCodecType sendCodecType, WebRtc_Word32 maxBitRate, - WebRtc_UWord32 frameRate, WebRtc_UWord32 bitRate, - WebRtc_UWord16 width, WebRtc_UWord16 height) -{ - // Everything codec specific should be reset here since this means the codec has changed. - // If native dimension values have changed, then either user initiated change, or QM - // initiated change. Will be able to determine only after the processing of the first frame - _lastChangeTime = VCMTickTime::MillisecondTimestamp(); - _content->Reset(); - _content->UpdateFrameRate(frameRate); - - _maxBitRate = maxBitRate; - _sendCodecType = sendCodecType; - _targetBitRate = bitRate; - _lossProtLogic->UpdateBitRate(static_cast(bitRate)); - _lossProtLogic->UpdateFrameRate(static_cast(frameRate)); - _lossProtLogic->UpdateFrameSize(width, height); - _frameDropper->Reset(); - _frameDropper->SetRates(static_cast(bitRate), static_cast(frameRate)); - _userFrameRate = (float)frameRate; - _codecWidth = width; - _codecHeight = height; - WebRtc_Word32 ret = VCM_OK; - ret = _qms->Initialize((float)_targetBitRate, _userFrameRate, _codecWidth, _codecHeight); - return ret; -} - -WebRtc_Word32 -VCMMediaOptimization::RegisterProtectionCallback(VCMProtectionCallback* protectionCallback) -{ - _videoProtectionCallback = protectionCallback; - return VCM_OK; - -} - - -void -VCMMediaOptimization::EnableFrameDropper(bool enable) -{ - _frameDropper->Enable(enable); -} - - -void -VCMMediaOptimization::EnableNack(bool enable) -{ - // Add NACK to the list of loss protection methods - bool updated = false; - if (enable) - { - VCMProtectionMethod *nackMethod = new VCMNackMethod(); - updated = _lossProtLogic->AddMethod(nackMethod); - if (!updated) - { - delete nackMethod; - } - } - else - { - updated = _lossProtLogic->RemoveMethod(kNACK); - } - if (updated) - { - _lossProtLogic->UpdateMethod(); - } -} - -bool -VCMMediaOptimization::IsNackEnabled() -{ - return (_lossProtLogic->FindMethod(kNACK) != NULL); -} - -void -VCMMediaOptimization::EnableFEC(bool enable) -{ - // Add FEC to the list of loss protection methods - bool updated = false; - if (enable) - { - VCMProtectionMethod *fecMethod = new VCMFecMethod(); - updated = _lossProtLogic->AddMethod(fecMethod); - if (!updated) - { - delete fecMethod; - } - } - else - { - updated = _lossProtLogic->RemoveMethod(kFEC); - } - if (updated) - { - _lossProtLogic->UpdateMethod(); - } -} -void -VCMMediaOptimization::EnableNackFEC(bool enable) -{ - // Add NackFec to the list of loss protection methods - bool updated = false; - if (enable) - { - VCMProtectionMethod *nackfecMethod = new VCMNackFecMethod(); - updated = _lossProtLogic->AddMethod(nackfecMethod); - if (!updated) - { - delete nackfecMethod; - } - } - else - { - updated = _lossProtLogic->RemoveMethod(kNackFec); - } - if (updated) - { - _lossProtLogic->UpdateMethod(); - } -} - -bool -VCMMediaOptimization::IsFecEnabled() -{ - return (_lossProtLogic->FindMethod(kFEC) != NULL); -} - -bool -VCMMediaOptimization::IsNackFecEnabled() -{ - return (_lossProtLogic->FindMethod(kNackFec) != NULL); -} - -void -VCMMediaOptimization::SetMtu(WebRtc_Word32 mtu) -{ - _maxPayloadSize = mtu; -} - -float -VCMMediaOptimization::SentFrameRate() -{ - if(_frameDropper) - { - return _frameDropper->ActualFrameRate((WebRtc_UWord32)(InputFrameRate() + 0.5f)); - } - - return VCM_CODEC_ERROR; -} - -float -VCMMediaOptimization::SentBitRate() -{ - UpdateBitRateEstimate(-1, VCMTickTime::MillisecondTimestamp()); - return _avgSentBitRateBps / 1000.0f; -} - -WebRtc_Word32 -VCMMediaOptimization::MaxBitRate() -{ - return _maxBitRate; -} - -WebRtc_Word32 -VCMMediaOptimization::UpdateWithEncodedData(WebRtc_Word32 encodedLength, - FrameType encodedFrameType) -{ - // look into the ViE version - debug mode - needs also number of layers. - UpdateBitRateEstimate(encodedLength, VCMTickTime::MillisecondTimestamp()); - if(encodedLength > 0) - { - const bool deltaFrame = (encodedFrameType != kVideoFrameKey && - encodedFrameType != kVideoFrameGolden); - - _frameDropper->Fill(encodedLength, deltaFrame); - if (_maxPayloadSize > 0 && encodedLength > 0) - { - const float minPacketsPerFrame = encodedLength / - static_cast(_maxPayloadSize); - if (deltaFrame) - { - _lossProtLogic->UpdatePacketsPerFrame(minPacketsPerFrame); - } - else - { - _lossProtLogic->UpdatePacketsPerFrameKey(minPacketsPerFrame); - } - - if (_enableQm) - { - // update quality select with encoded length - _qms->UpdateEncodedSize(encodedLength, encodedFrameType); - } - } - if (!deltaFrame && encodedLength > 0) - { - _lossProtLogic->UpdateKeyFrameSize(static_cast(encodedLength)); - } - - // updating counters - if (deltaFrame){ - _deltaFrameCnt++; - } else { - _keyFrameCnt++; - } - - } - - return VCM_OK; - -} - -void VCMMediaOptimization::UpdateBitRateEstimate(WebRtc_Word64 encodedLength, - WebRtc_Word64 nowMs) -{ - int i = kBitrateMaxFrameSamples - 1; - WebRtc_UWord32 frameSizeSum = 0; - WebRtc_Word64 timeOldest = -1; - // Find an empty slot for storing the new sample and at the same time - // accumulate the history. - for (; i >= 0; i--) - { - if (_encodedFrameSamples[i]._sizeBytes == -1) - { - // Found empty slot - break; - } - if (nowMs - _encodedFrameSamples[i]._timeCompleteMs < kBitrateAverageWinMs) - { - frameSizeSum += static_cast(_encodedFrameSamples[i]._sizeBytes); - if (timeOldest == -1) - { - timeOldest = _encodedFrameSamples[i]._timeCompleteMs; - } - } - } - if (encodedLength > 0) - { - if (i < 0) - { - // No empty slot, shift - for (i = kBitrateMaxFrameSamples - 2; i >= 0; i--) - { - _encodedFrameSamples[i + 1] = _encodedFrameSamples[i]; - } - i++; - } - // Insert new sample - _encodedFrameSamples[i]._sizeBytes = encodedLength; - _encodedFrameSamples[i]._timeCompleteMs = nowMs; - } - if (timeOldest > -1) - { - // Update average bit rate - float denom = static_cast(nowMs - timeOldest); - if (denom < 1.0) - { - denom = 1.0; - } - _avgSentBitRateBps = (frameSizeSum + encodedLength) * 8 * 1000 / denom; - } - else if (encodedLength > 0) - { - _avgSentBitRateBps = static_cast(encodedLength * 8); - } - else - { - _avgSentBitRateBps = 0; - } -} - - -WebRtc_Word32 -VCMMediaOptimization::RegisterVideoQMCallback(VCMQMSettingsCallback *videoQMSettings) -{ - _videoQMSettingsCallback = videoQMSettings; - // Callback setting controls QM - if (_videoQMSettingsCallback != NULL) - { - _enableQm = true; - } - else - { - _enableQm = false; - } - return VCM_OK; -} - -void -VCMMediaOptimization::updateContentData(const VideoContentMetrics *contentMetrics) -{ - //Updating content metrics - if (contentMetrics == NULL) - { - //No QM if metrics are NULL - _enableQm = false; - _qms->Reset(); - } - else - { - _content->UpdateContentData(contentMetrics); - } -} - -WebRtc_Word32 -VCMMediaOptimization::SelectQuality() -{ - // Reset quantities for QM select - _qms->ResetQM(); - - // Update QM will long-term averaged content metrics. - _qms->UpdateContent(_content->LongTermAvgData()); - - // Select quality mode - VCMQualityMode* qm = NULL; - WebRtc_Word32 ret = _qms->SelectQuality(&qm); - if (ret < 0) - { - return ret; - } - - // Check for updates to spatial/temporal modes - QMUpdate(qm); - - // Reset all the rate and related frame counters quantities - _qms->ResetRates(); - - // Reset counters - _lastQMUpdateTime = VCMTickTime::MillisecondTimestamp(); - - // Reset content metrics - _content->Reset(); - - return VCM_OK; -} - - -// Check timing constraints and look for significant change in: -// (1) scene content -// (2) target bit rate - -bool -VCMMediaOptimization::checkStatusForQMchange() -{ - - bool status = true; - - // Check that we do not call QMSelect too often, and that we waited some time - // (to sample the metrics) from the event lastChangeTime - // lastChangeTime is the time where user changed the size/rate/frame rate - // (via SetEncodingData) - WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - if ((now - _lastQMUpdateTime) < kQmMinIntervalMs || - (now - _lastChangeTime) < kQmMinIntervalMs) - { - status = false; - } - - return status; - -} - -bool -VCMMediaOptimization::QMUpdate(VCMQualityMode* qm) -{ - // Check for no change - if (qm->spatialHeightFact == 1 && - qm->spatialWidthFact == 1 && - qm->temporalFact == 1) - { - return false; - } - - // Content metrics hold native values - VideoContentMetrics* cm = _content->LongTermAvgData(); - - // Temporal - WebRtc_UWord32 frameRate = static_cast(_incomingFrameRate + 0.5f); - // Check if go back up in temporal resolution - if (qm->temporalFact == 0) - { - frameRate = (WebRtc_UWord32) 2 * _incomingFrameRate; - } - // go down in temporal resolution - else - { - frameRate = (WebRtc_UWord32)(_incomingFrameRate / qm->temporalFact + 1); - } - - // Spatial - WebRtc_UWord32 height = _codecHeight; - WebRtc_UWord32 width = _codecWidth; - // Check if go back up in spatial resolution - if (qm->spatialHeightFact == 0 && qm->spatialWidthFact == 0) - { - height = cm->nativeHeight; - width = cm->nativeWidth; - } - else - { - height = _codecHeight / qm->spatialHeightFact; - width = _codecWidth / qm->spatialWidthFact; - } - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, _id, - "Quality Mode Update: W = %d, H = %d, FR = %f", - width, height, frameRate); - - // Update VPM with new target frame rate and size - _videoQMSettingsCallback->SetVideoQMSettings(frameRate, width, height); - - return true; -} - - - -void -VCMMediaOptimization::UpdateIncomingFrameRate() -{ - WebRtc_Word64 now = VCMTickTime::MillisecondTimestamp(); - if(_incomingFrameTimes[0] == 0) - { - // first no shift - } else - { - // shift - for(WebRtc_Word32 i = (kFrameCountHistorySize - 2); i >= 0 ; i--) - { - _incomingFrameTimes[i+1] = _incomingFrameTimes[i]; - } - } - _incomingFrameTimes[0] = now; - ProcessIncomingFrameRate(now); -} - -// allowing VCM to keep track of incoming frame rate -void -VCMMediaOptimization::ProcessIncomingFrameRate(WebRtc_Word64 now) -{ - WebRtc_Word32 num = 0; - WebRtc_Word32 nrOfFrames = 0; - for (num = 1; num < (kFrameCountHistorySize - 1); num++) - { - if (_incomingFrameTimes[num] <= 0 || - // don't use data older than 2 s - now - _incomingFrameTimes[num] > kFrameHistoryWinMs) - { - break; - } else - { - nrOfFrames++; - } - } - if (num > 1) - { - const WebRtc_Word64 diff = now - _incomingFrameTimes[num-1]; - _incomingFrameRate = 1.0; - if(diff >0) - { - _incomingFrameRate = nrOfFrames * 1000.0f / static_cast(diff); - } - } - else - { - _incomingFrameRate = static_cast(nrOfFrames); - } -} - -WebRtc_UWord32 -VCMMediaOptimization::InputFrameRate() -{ - ProcessIncomingFrameRate(VCMTickTime::MillisecondTimestamp()); - return WebRtc_UWord32 (_incomingFrameRate + 0.5f); -} - -} diff --git a/modules/video_coding/main/source/media_optimization.h b/modules/video_coding/main/source/media_optimization.h deleted file mode 100644 index 8a9993e5f..000000000 --- a/modules/video_coding/main/source/media_optimization.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_ -#define WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_ - -#include "list_wrapper.h" -#include "module_common_types.h" -#include "video_coding.h" -#include "trace.h" -#include "media_opt_util.h" -#include "qm_select.h" - -namespace webrtc -{ - -enum { kBitrateMaxFrameSamples = 60 }; -enum { kBitrateAverageWinMs = 1000 }; - -class VCMContentMetricsProcessing; -class VCMFrameDropper; - -struct VCMEncodedFrameSample -{ - VCMEncodedFrameSample() : _sizeBytes(-1), _timeCompleteMs(-1) {} - - WebRtc_Word64 _sizeBytes; - WebRtc_Word64 _timeCompleteMs; -}; - -class VCMMediaOptimization -{ -public: - VCMMediaOptimization(WebRtc_Word32 id); - ~VCMMediaOptimization(void); - /* - * Reset the Media Optimization module - */ - WebRtc_Word32 Reset(); - /** - * Set target Rates for the encoder given the channel parameters - * Inputs: bitRate - target bitRate, in the conference case this is the rate - * between the sending client and the server - * fractionLost - packet loss in % in the network - * roundTripTimeMs - round trip time in miliseconds - * minBitRate - the bit rate of the end-point with lowest rate - * maxBitRate - the bit rate of the end-point with highest rate - */ - WebRtc_UWord32 SetTargetRates(WebRtc_UWord32 bitRate, - WebRtc_UWord8 &fractionLost, - WebRtc_UWord32 roundTripTimeMs); - - /** - * Inform media optimization of initial encoding state - */ - WebRtc_Word32 SetEncodingData(VideoCodecType sendCodecType, - WebRtc_Word32 maxBitRate, - WebRtc_UWord32 frameRate, - WebRtc_UWord32 bitRate, - WebRtc_UWord16 width, - WebRtc_UWord16 height); - /** - * Enable NACK and update error resilience parameters - */ - void EnableNack(bool enable); - /** - * Returns weather or not NACK is enabled - */ - bool IsNackEnabled(); - /** - * Enable FEC and update error resilience parameters - */ - void EnableFEC(bool enable); - /** - * Returns weather or not FEC is enabled - */ - bool IsFecEnabled(); - /** - * Returns weather or not NackFec is enabled - */ - bool IsNackFecEnabled(); - /** - * Updates the max pay load size - */ - /** - * Enable NackFec and update error resilience parameters - */ - void EnableNackFEC(bool enable); - - void SetMtu(WebRtc_Word32 mtu); - - /* - * Get actual input frame rate - */ - WebRtc_UWord32 InputFrameRate(); - - /* - * Get actual sent frame rate - */ - float SentFrameRate(); - /* - * Get actual sent bit rate - */ - float SentBitRate(); - /* - * Get maximum allowed bit rate - */ - WebRtc_Word32 MaxBitRate(); - /* - * Inform Media Optimization of encoding output: Length and frame type - */ - WebRtc_Word32 UpdateWithEncodedData(WebRtc_Word32 encodedLength, - FrameType encodedFrameType); - /* - * Register a protection callback to be used to inform the user about the - * protection methods used - */ - WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protectionCallback); - /* - * Register a quality settings callback to be used to inform VPM/user about the optimal - * quality settings (frame rate/dimension) required - */ - WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings); - void EnableFrameDropper(bool enable); - - bool DropFrame(); - - /* - * Get number of key/delta frames encoded - */ - WebRtc_Word32 SentFrameCount(VCMFrameCount &frameCount) const; - - /* - * update incoming frame rate value - */ - void UpdateIncomingFrameRate(); - - /** - * Update content metric Data - */ - void updateContentData(const VideoContentMetrics* contentMetrics); - - /** - * Compute new Quality Mode - */ - WebRtc_Word32 SelectQuality(); - -private: - - void UpdateBitRateEstimate(WebRtc_Word64 encodedLength, WebRtc_Word64 nowMs); - /* - * verify if QM settings differ from default, i.e. if an update is required - * Compute actual values, as will be sent to the encoder - */ - bool QMUpdate(VCMQualityMode* qm); - /** - * check if we should make a QM change - * will return 1 if yes, 0 otherwise - */ - bool checkStatusForQMchange(); - - void ProcessIncomingFrameRate(WebRtc_Word64 now); - - enum { kFrameCountHistorySize = 90}; - enum { kFrameHistoryWinMs = 2000}; - - WebRtc_Word32 _id; - - WebRtc_Word32 _maxBitRate; - VideoCodecType _sendCodecType; - WebRtc_UWord16 _codecWidth; - WebRtc_UWord16 _codecHeight; - float _userFrameRate; - - VCMFrameDropper* _frameDropper; - VCMLossProtectionLogic* _lossProtLogic; - WebRtc_UWord32 _lossProtOverhead; - WebRtc_UWord8 _packetLossEnc; - WebRtc_UWord8 _fractionLost; - - - WebRtc_UWord32 _sendStatistics[4]; - WebRtc_UWord32 _sendStatisticsZeroEncode; - WebRtc_Word32 _maxPayloadSize; - WebRtc_UWord32 _lastBitRate; - WebRtc_UWord32 _targetBitRate; - - float _incomingFrameRate; - WebRtc_Word64 _incomingFrameTimes[kFrameCountHistorySize]; - - bool _enableQm; - - VCMProtectionCallback* _videoProtectionCallback; - VCMQMSettingsCallback* _videoQMSettingsCallback; - - VCMEncodedFrameSample _encodedFrameSamples[kBitrateMaxFrameSamples]; - float _avgSentBitRateBps; - - WebRtc_UWord32 _keyFrameCnt; - WebRtc_UWord32 _deltaFrameCnt; - - VCMContentMetricsProcessing* _content; - VCMQmSelect* _qms; - - WebRtc_Word64 _lastQMUpdateTime; - WebRtc_Word64 _lastChangeTime; // content or user triggered - - -}; // end of VCMMediaOptimization class definition - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_MEDIA_OPTIMIZATION_H_ diff --git a/modules/video_coding/main/source/nack_fec_tables.h b/modules/video_coding/main/source/nack_fec_tables.h deleted file mode 100644 index 40cf0b5da..000000000 --- a/modules/video_coding/main/source/nack_fec_tables.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_ - -namespace webrtc -{ - -// Table for softening FEC rate for NACK/FEC protection method -const WebRtc_UWord16 VCMNackFecTable[200] = { - -27, -28, -30, -31, -33, -35, -36, -38, -40, -42, -45, -47, -49, -52, -54, -57, -60, -63, -66, -70, -73, -77, -81, -85, -89, -94, -98, -103, -108, -114, -120, -126, -132, -138, -145, -152, -160, -168, -176, -185, -194, -203, -213, -223, -234, -246, -257, -270, -283, -296, -310, -325, -340, -356, -373, -390, -408, -427, -446, -467, -488, -510, -532, -556, -581, -606, -632, -659, -688, -717, -747, -778, -810, -843, -877, -912, -948, -985, -1022, -1061, -1101, -1142, -1183, -1226, -1269, -1314, -1359, -1404, -1451, -1498, -1546, -1594, -1643, -1693, -1743, -1793, -1843, -1894, -1945, -1996, -2048, -2099, -2150, -2201, -2252, -2302, -2352, -2402, -2452, -2501, -2549, -2597, -2644, -2691, -2736, -2781, -2826, -2869, -2912, -2953, -2994, -3034, -3073, -3110, -3147, -3183, -3218, -3252, -3285, -3317, -3348, -3378, -3407, -3436, -3463, -3489, -3514, -3539, -3563, -3585, -3607, -3628, -3649, -3668, -3687, -3705, -3722, -3739, -3755, -3770, -3785, -3799, -3812, -3825, -3838, -3849, -3861, -3872, -3882, -3892, -3901, -3910, -3919, -3927, -3935, -3943, -3950, -3957, -3963, -3969, -3975, -3981, -3987, -3992, -3997, -4001, -4006, -4010, -4014, -4018, -4022, -4025, -4029, -4032, -4035, -4038, -4041, -4043, -4046, -4048, -4050, -4053, -4055, -4057, -4059, -4060, -4062, -4064, -4065, -4067, - - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_NACK_FEC_TABLES_H_ diff --git a/modules/video_coding/main/source/packet.cc b/modules/video_coding/main/source/packet.cc deleted file mode 100644 index f5077e42a..000000000 --- a/modules/video_coding/main/source/packet.cc +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "packet.h" -#include "module_common_types.h" - -#include - -namespace webrtc { - -VCMPacket::VCMPacket(const WebRtc_UWord8* ptr, - const WebRtc_UWord32 size, - const WebRtcRTPHeader& rtpHeader) : - payloadType(rtpHeader.header.payloadType), - timestamp(rtpHeader.header.timestamp), - seqNum(rtpHeader.header.sequenceNumber), - dataPtr(ptr), - sizeBytes(size), - markerBit(rtpHeader.header.markerBit), - - frameType(rtpHeader.frameType), - codec(kVideoCodecUnknown), - isFirstPacket(rtpHeader.type.Video.isFirstPacket), - completeNALU(kNaluComplete), - insertStartCode(false), - bits(false) -{ - CopyCodecSpecifics(rtpHeader.type.Video); -} - -VCMPacket::VCMPacket(const WebRtc_UWord8* ptr, WebRtc_UWord32 size, WebRtc_UWord16 seq, WebRtc_UWord32 ts, bool mBit) : - payloadType(0), - timestamp(ts), - seqNum(seq), - dataPtr(ptr), - sizeBytes(size), - markerBit(mBit), - - frameType(kVideoFrameDelta), - codec(kVideoCodecUnknown), - isFirstPacket(false), - completeNALU(kNaluComplete), - insertStartCode(false), - bits(false) -{} - -void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader) -{ - RTPVideoTypeHeader codecHeader = videoHeader.codecHeader; - switch(videoHeader.codec) - { - case kRTPVideoVP8: - { - codec = kVideoCodecVP8; - break; - } - case kRTPVideoI420: - { - codec = kVideoCodecI420; - break; - } - default: - { - codec = kVideoCodecUnknown; - break; - } - } -} - -} diff --git a/modules/video_coding/main/source/packet.h b/modules/video_coding/main/source/packet.h deleted file mode 100644 index f1fdfd1ad..000000000 --- a/modules/video_coding/main/source/packet.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_PACKET_H_ -#define WEBRTC_MODULES_VIDEO_CODING_PACKET_H_ - -#include "typedefs.h" -#include "module_common_types.h" -#include "jitter_buffer_common.h" - -namespace webrtc -{ - -class VCMPacket -{ -public: - VCMPacket(const WebRtc_UWord8* ptr, - const WebRtc_UWord32 size, - const WebRtcRTPHeader& rtpHeader); - VCMPacket(const WebRtc_UWord8* ptr, - WebRtc_UWord32 size, - WebRtc_UWord16 seqNum, - WebRtc_UWord32 timestamp, - bool markerBit); - - WebRtc_UWord8 payloadType; - WebRtc_UWord32 timestamp; - WebRtc_UWord16 seqNum; - const WebRtc_UWord8* dataPtr; - WebRtc_UWord32 sizeBytes; - bool markerBit; - - FrameType frameType; - webrtc::VideoCodecType codec; - - bool isFirstPacket; // Is this first packet in a frame. - VCMNaluCompleteness completeNALU; // Default is kNaluIncomplete. - bool insertStartCode; // True if a start code should be inserted before this - // packet. - bool bits; // The first bits of this packets are zero and the - // first - // byte should be ORed with the last packet of the - // previous frame. - -protected: - void CopyCodecSpecifics(const RTPVideoHeader& videoHeader); -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_PACKET_H_ diff --git a/modules/video_coding/main/source/qm_select.cc b/modules/video_coding/main/source/qm_select.cc deleted file mode 100644 index 3220db08a..000000000 --- a/modules/video_coding/main/source/qm_select.cc +++ /dev/null @@ -1,734 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "qm_select.h" -#include "internal_defines.h" -#include "qm_select_data.h" - -#include "module_common_types.h" -#include "video_coding_defines.h" -#include "trace.h" - -#include - -namespace webrtc { - -VCMQmSelect::VCMQmSelect() -{ - _qm = new VCMQualityMode(); - _contentMetrics = new VideoContentMetrics(); - Reset(); -} - -VCMQmSelect::~VCMQmSelect() -{ - delete _qm; - delete _contentMetrics; -} - -void -VCMQmSelect::ResetQM() -{ - _motion.Reset(); - _spatial.Reset(); - _coherence.Reset(); - _stationaryMotion = 0; - _aspectRatio = 1; - _maxRateQM = 0; - _imageType = 1; - _userResolutionPref = 50; // Neutral - _qm->Reset(); - return; -} - -void -VCMQmSelect::ResetRates() -{ - _sumEncodedBytes = 0; - _sumTargetRate = 0.0f; - _sumIncomingFrameRate = 0.0f; - _sumFrameRateMM = 0.0f; - _sumSeqRateMM = 0.0f; - _sumPacketLoss = 0.0f; - _frameCnt = 0; - _frameCntDelta = 0; - _lowBufferCnt = 0; - _updateRateCnt = 0; - return; -} - -void -VCMQmSelect::Reset() -{ - _stateDecFactorSpatial = 1; - _stateDecFactorTemp = 1; - _bufferLevel = 0.0f; - _targetBitRate = 0.0f; - _incomingFrameRate = 0.0f; - _userFrameRate = 0.0f; - _perFrameBandwidth =0.0f; - _prevTotalRate = 0.0f; - _prevRttTime = 0; - _prevPacketLoss = 0; - ResetQM(); - ResetRates(); - return; -} - -//Initialize after reset of encoder -WebRtc_Word32 -VCMQmSelect::Initialize(float bitRate, float userFrameRate, - WebRtc_UWord32 width, WebRtc_UWord32 height) -{ - if (userFrameRate == 0.0f || width == 0 || height == 0) - { - return VCM_PARAMETER_ERROR; - } - _targetBitRate = bitRate; - _userFrameRate = userFrameRate; - - // Encoder width and height - _width = width; - _height = height; - - // Initial buffer level - _bufferLevel = INIT_BUFFER_LEVEL * _targetBitRate; - - // Per-frame bandwidth - if ( _incomingFrameRate == 0 ) - { - _perFrameBandwidth = _targetBitRate / _userFrameRate; - _incomingFrameRate = _userFrameRate; - } - else - { - // Take average: this is due to delay in update of new encoder frame rate: - // userFrameRate is the new one, - // incomingFrameRate is the old one (based on previous ~ 1sec/RTCP report) - _perFrameBandwidth = 0.5 *( _targetBitRate / _userFrameRate + - _targetBitRate / _incomingFrameRate ); - } - _init = true; - - - return VCM_OK; -} - -WebRtc_Word32 -VCMQmSelect::SetPreferences(WebRtc_Word8 resolPref) -{ - // Preference setting for temporal over spatial resolution - // 100 means temporal, 0 means spatial, 50 is neutral - _userResolutionPref = resolPref; - - return VCM_OK; -} - -//Update after every encoded frame -void -VCMQmSelect::UpdateEncodedSize(WebRtc_Word64 encodedSize, - FrameType encodedFrameType) -{ - // Update encoded size; - _sumEncodedBytes += encodedSize; - _frameCnt++; - - // Convert to Kbps - float encodedSizeKbits = (float)((encodedSize * 8.0) / 1000.0); - - // Update the buffer level: - // per_frame_BW is updated when encoder is updated, every RTCP reports - _bufferLevel += _perFrameBandwidth - encodedSizeKbits; - - // Mismatch here is based on difference of actual encoded frame size and - // per-frame bandwidth, for delta frames - // This is a much stronger condition on rate mismatch than sumSeqRateMM - // Note: not used in this version - /* - const bool deltaFrame = (encodedFrameType != kVideoFrameKey && - encodedFrameType != kVideoFrameGolden); - - // Sum the frame mismatch: - if (deltaFrame) - { - _frameCntDelta++; - if (encodedSizeKbits > 0) - _sumFrameRateMM += - (float) (fabs(encodedSizeKbits - _perFrameBandwidth) / - encodedSizeKbits); - } - */ - - // Counter for occurrences of low buffer level - if (_bufferLevel <= PERC_BUFFER_THR * INIT_BUFFER_LEVEL * _targetBitRate) - { - _lowBufferCnt++; - } - -} - -//Update various quantities after SetTargetRates in MediaOpt -void -VCMQmSelect::UpdateRates(float targetBitRate, float avgSentBitRate, - float incomingFrameRate, WebRtc_UWord8 packetLoss) -{ - - // Sum the target bitrate and incoming frame rate: - // these values are the encoder rates (from previous update ~1sec), - // i.e, before the update for next ~1sec - _sumTargetRate += _targetBitRate; - _sumIncomingFrameRate += _incomingFrameRate; - _updateRateCnt++; - - // Sum the received (from RTCP reports) packet loss rates - _sumPacketLoss += (float) packetLoss / 255.0f; - - // Convert average sent bitrate to kbps - float avgSentBitRatekbps = avgSentBitRate / 1000.0f; - - // Sum the sequence rate mismatch: - // Mismatch here is based on difference between target rate the encoder - // used (in previous ~1sec) and the average actual - // encoding rate measured at current time - if (fabs(_targetBitRate - avgSentBitRatekbps) < THRESH_SUM_MM && - _targetBitRate > 0.0 ) - { - _sumSeqRateMM += (float) - (fabs(_targetBitRate - avgSentBitRatekbps) / _targetBitRate ); - } - - // Update QM with the current new target and frame rate: - // these values are ones the encoder will use for the current/next ~1sec - _targetBitRate = targetBitRate; - _incomingFrameRate = incomingFrameRate; - - // Update QM with an (average) encoder per_frame_bandwidth: - // this is the per_frame_bw for the current/next ~1sec - _perFrameBandwidth = 0.0f; - if (_incomingFrameRate > 0.0f) - { - _perFrameBandwidth = _targetBitRate / _incomingFrameRate; - } - -} - -// Adjust the FEC rate based on the content and the network state -// (packet loss rate, total rate/bandwidth, round trip time). -// Note that packetLoss here is the filtered loss value. -WebRtc_UWord8 -VCMQmSelect::AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate, - float frameRate,WebRtc_UWord16 rttTime, - WebRtc_UWord8 packetLoss) -{ - // Default: no adjustment - WebRtc_UWord8 codeRateDeltaAdjust = codeRateDelta; - float adjustFec = 1.0f; - - // TODO (marpan): - // Set FEC adjustment factor - - codeRateDeltaAdjust = static_cast(codeRateDelta * adjustFec); - - // Keep track of previous values of network state: - // adjustment may be also based on pattern of changes in network state - _prevTotalRate = totalRate; - _prevRttTime = rttTime; - _prevPacketLoss = packetLoss; - - return codeRateDeltaAdjust; -} - -void -VCMQmSelect::UpdateContent(const VideoContentMetrics* contentMetrics) -{ - _contentMetrics = contentMetrics; -} - -// Select the resolution factors: frame size and frame rate change: (QM modes) -// Selection is for going back up in resolution, or going down in. -WebRtc_Word32 -VCMQmSelect::SelectQuality(VCMQualityMode** qm) -{ - if (!_init) - { - return VCM_UNINITIALIZED; - } - if (_contentMetrics == NULL) - { - Reset(); //default values - *qm = _qm; - return VCM_OK; - } - - // Default settings - _qm->spatialWidthFact = 1; - _qm->spatialHeightFact = 1; - _qm->temporalFact = 1; - - - // Update native values - _nativeWidth = _contentMetrics->nativeWidth; - _nativeHeight = _contentMetrics->nativeHeight; - _nativeFrameRate = _contentMetrics->nativeFrameRate; - - // Aspect ratio: used for selection of 1x2,2x1,2x2 - _aspectRatio = (float)_width / (float)_height; - - float avgTargetRate = 0.0f; - float avgIncomingFrameRate = 0.0f; - float ratioBufferLow = 0.0f; - float rateMisMatch = 0.0f; - float avgPacketLoss = 0.0f; - if (_frameCnt > 0) - { - ratioBufferLow = (float)_lowBufferCnt / (float)_frameCnt; - } - if (_updateRateCnt > 0) - { - // Use seq-rate mismatch for now - rateMisMatch = (float)_sumSeqRateMM / (float)_updateRateCnt; - //rateMisMatch = (float)_sumFrameRateMM / (float)_frameCntDelta; - - // Average target and incoming frame rates - avgTargetRate = (float)_sumTargetRate / (float)_updateRateCnt; - avgIncomingFrameRate = (float)_sumIncomingFrameRate / - (float)_updateRateCnt; - - // Average received packet loss rate - avgPacketLoss = (float)_sumPacketLoss / (float)_updateRateCnt; - } - - // For QM selection below, may want to weight the average encoder rates - // with the current (for next ~1sec) rate values. - // Uniform average for now: - float w1 = 0.5f; - float w2 = 0.5f; - avgTargetRate = w1 * avgTargetRate + w2 * _targetBitRate; - avgIncomingFrameRate = w1 * avgIncomingFrameRate + w2 * _incomingFrameRate; - - // Set the maximum transitional rate and image type: - // for up-sampled spatial dimensions. - // This is needed to get the transRate for going back up in - // spatial resolution (only 2x2 allowed in this version). - SetMaxRateForQM(2 * _width, 2 * _height); - WebRtc_UWord8 imageType2 = _imageType; - WebRtc_UWord32 maxRateQM2 = _maxRateQM; - - // Set the maximum transitional rate and image type: - // for the encoder spatial dimensions. - SetMaxRateForQM(_width, _height); - - // Compute class state of the content. - MotionNFD(); - Spatial(); - - // - // Get transitional rate from table, based on image type and content class. - // - - // Get image class and content class: for going down spatially - WebRtc_UWord8 imageClass = 1; - if (_imageType <= 3) imageClass = 0; - WebRtc_UWord8 contentClass = 3 * _motion.level + _spatial.level; - WebRtc_UWord8 tableIndex = imageClass * 9 + contentClass; - float scaleTransRate = kScaleTransRateQm[tableIndex]; - - // Get image class and content class: for going up spatially - WebRtc_UWord8 imageClass2 = 1; - if (imageType2 <= 3) imageClass2 = 0; - WebRtc_UWord8 tableIndex2 = imageClass2 * 9 + contentClass; - float scaleTransRate2 = kScaleTransRateQm[tableIndex2]; - - // Transitonal rate for going down - WebRtc_UWord32 estimatedTransRateDown = static_cast - (_incomingFrameRate * scaleTransRate * _maxRateQM / 30); - - // Transitional rate for going up temporally - WebRtc_UWord32 estimatedTransRateUpT = static_cast - (TRANS_RATE_SCALE_UP_TEMP * 2 * _incomingFrameRate * - scaleTransRate * _maxRateQM / 30); - - // Transitional rate for going up spatially - WebRtc_UWord32 estimatedTransRateUpS = static_cast - (TRANS_RATE_SCALE_UP_SPATIAL * _incomingFrameRate * - scaleTransRate2 * maxRateQM2 / 30); - - // - // Done with transitional rates - // - - // - //CHECK FOR GOING BACK UP IN RESOLUTION - // - bool selectedUp = false; - // Check if native has been spatially down-sampled - if (_stateDecFactorSpatial > 1) - { - // Check conditions on buffer level and rate_mismatch - if ( (avgTargetRate > estimatedTransRateUpS) && - (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM)) - { - // width/height scaled back up: - // setting 0 indicates scaling back to native - _qm->spatialHeightFact = 0; - _qm->spatialWidthFact = 0; - selectedUp = true; - } - } - //Check if native has been temporally down-sampled - if (_stateDecFactorTemp > 1) - { - if ( (avgTargetRate > estimatedTransRateUpT) && - (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM)) - { - // temporal scale back up: - // setting 0 indicates scaling back to native - _qm->temporalFact = 0; - selectedUp = true; - } - } - - // Leave QM if we selected to go back up in either spatial or temporal - if (selectedUp == true) - { - // Update down-sampling state - // Note: only temp reduction by 2 is allowed - if (_qm->temporalFact == 0) - { - _stateDecFactorTemp = _stateDecFactorTemp / 2; - } - // Update down-sampling state - // Note: only spatial reduction by 2x2 is allowed - if (_qm->spatialHeightFact == 0 && _qm->spatialWidthFact == 0 ) - { - _stateDecFactorSpatial = _stateDecFactorSpatial / 4; - } - *qm = _qm; - return VCM_OK; - } - - // - // Done with checking for going back up in resolution - // - - // - //CHECK FOR RESOLUTION REDUCTION - // - - // Resolution reduction if: - // (1) target rate is lower than transitional rate, or - // (2) buffer level is not stable, or - // (3) rate mismatch is larger than threshold - - // Bias down-sampling based on packet loss conditions - if (avgPacketLoss > LOSS_THR) - { - estimatedTransRateDown = LOSS_RATE_FAC * estimatedTransRateDown; - } - - if ((avgTargetRate < estimatedTransRateDown ) || - (ratioBufferLow > MAX_BUFFER_LOW) - || (rateMisMatch > MAX_RATE_MM)) - { - - WebRtc_UWord8 spatialFact = 1; - WebRtc_UWord8 tempFact = 1; - - // Get the action - spatialFact = kSpatialAction[contentClass]; - tempFact = kTemporalAction[contentClass]; - - switch(spatialFact) - { - case 4: - _qm->spatialWidthFact = 2; - _qm->spatialHeightFact = 2; - break; - case 2: - //default is 1x2 (H) - _qm->spatialWidthFact = 2; - _qm->spatialHeightFact = 1; - // Select 1x2,2x1, or back to 2x2 - // Note: directional selection not used in this version - // SelectSpatialDirectionMode((float) estimatedTransRateDown); - break; - default: - _qm->spatialWidthFact = 1; - _qm->spatialHeightFact = 1; - break; - } - _qm->temporalFact = tempFact; - - // Sanity check on ST QM selection: - // override the settings for too small image size and frame rate - // Also check the limit on current down-sampling state - - // No spatial sampling if image size is too small (QCIF) - if ( (_width * _height) <= MIN_IMAGE_SIZE || - _stateDecFactorSpatial >= MAX_SPATIAL_DOWN_FACT) - { - _qm->spatialWidthFact = 1; - _qm->spatialHeightFact = 1; - } - - // No frame rate reduction below some point: - // use the (average) incoming frame rate - if ( avgIncomingFrameRate <= MIN_FRAME_RATE_QM || - _stateDecFactorTemp >= MAX_TEMP_DOWN_FACT) - { - _qm->temporalFact = 1; - } - - // No down-sampling if current downsampling state is above threshold - if (_stateDecFactorTemp * _stateDecFactorSpatial >= - MAX_SPATIAL_TEMP_DOWN_FACT) - { - _qm->spatialWidthFact = 1; - _qm->spatialHeightFact = 1; - _qm->temporalFact = 1; - } - // - // Done with sanity checks on ST QM selection - // - - // Update down-sampling states - _stateDecFactorSpatial = _stateDecFactorSpatial * _qm->spatialWidthFact - * _qm->spatialHeightFact; - _stateDecFactorTemp = _stateDecFactorTemp * _qm->temporalFact; - - if (_qm->spatialWidthFact != 1 || _qm->spatialHeightFact != 1 || - _qm->temporalFact != 1) - { - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, -1, - "Resolution reduction occurred" - "Content Metrics are: Motion = %d , Spatial = %d, " - "Rates are: Est. Trans. BR = %d, Avg.Target BR = %f", - _motion.level, _spatial.level, - estimatedTransRateDown, avgTargetRate); - } - - } - else - { - *qm = _qm; - return VCM_OK; - } - // Done with checking for resolution reduction - - *qm = _qm; - return VCM_OK; - - -} - -WebRtc_Word32 -VCMQmSelect::SelectSpatialDirectionMode(float transRate) -{ - // Default is 1x2 (H) - - // For bit rates well below transitional rate, we select 2x2 - if ( _targetBitRate < transRate * RATE_RED_SPATIAL_2X2 ) - { - _qm->spatialWidthFact = 2; - _qm->spatialHeightFact = 2; - return VCM_OK; - } - - // Otherwise check prediction errors, aspect ratio, horizontalness - - float spatialErr = _contentMetrics->spatialPredErr; - float spatialErrH = _contentMetrics->spatialPredErrH; - float spatialErrV = _contentMetrics->spatialPredErrV; - - // Favor 1x2 if aspect_ratio is 16:9 - if (_aspectRatio >= 16.0f / 9.0f ) - { - //check if 1x2 has lowest prediction error - if (spatialErrH < spatialErr && spatialErrH < spatialErrV) - { - return VCM_OK; - } - } - - // Check for 2x2 selection: favor 2x2 over 1x2 and 2x1 - if (spatialErr < spatialErrH * (1.0f + SPATIAL_ERR_2X2_VS_H) && - spatialErr < spatialErrV * (1.0f + SPATIAL_ERR_2X2_VS_V)) - { - _qm->spatialWidthFact = 2; - _qm->spatialHeightFact = 2; - return VCM_OK; - } - - // Check for 2x1 selection: - if (spatialErrV < spatialErrH * (1.0f - SPATIAL_ERR_V_VS_H) && - spatialErrV < spatialErr * (1.0f - SPATIAL_ERR_2X2_VS_V)) - { - _qm->spatialWidthFact = 1; - _qm->spatialHeightFact = 2; - return VCM_OK; - } - - return VCM_OK; -} - -void -VCMQmSelect::Coherence() -{ - float horizNZ = _contentMetrics->motionHorizontalness; - float distortionNZ = _contentMetrics->motionClusterDistortion; - - // Coherence measure: combine horizontalness with cluster distortion - _coherence.value = COH_MAX; - if (distortionNZ > 0.) - { - _coherence.value = horizNZ / distortionNZ; - } - _coherence.value = VCM_MIN(COH_MAX, _coherence.value); - - if (_coherence.value < COHERENCE_THR) - { - _coherence.level = kLow; - } - else - { - _coherence.level = kHigh; - } - -} - -void -VCMQmSelect::MotionNFD() -{ - _motion.value = _contentMetrics->motionMagnitudeNZ; - - // Determine motion level - if (_motion.value < LOW_MOTION_NFD) - { - _motion.level = kLow; - } - else if (_motion.value > HIGH_MOTION_NFD) - { - _motion.level = kHigh; - } - else - { - _motion.level = kDefault; - } - -} - -void -VCMQmSelect::Motion() -{ - - float sizeZeroMotion = _contentMetrics->sizeZeroMotion; - float motionMagNZ = _contentMetrics->motionMagnitudeNZ; - - // Take product of size and magnitude with equal weight - _motion.value = (1.0f - sizeZeroMotion) * motionMagNZ; - - // Stabilize: motionMagNZ could be large when only a - // few motion blocks are non-zero - _stationaryMotion = false; - if (sizeZeroMotion > HIGH_ZERO_MOTION_SIZE) - { - _motion.value = 0.0f; - _stationaryMotion = true; - } - // Determine motion level - if (_motion.value < LOW_MOTION) - { - _motion.level = kLow; - } - else if (_motion.value > HIGH_MOTION) - { - _motion.level = kHigh; - } - else - { - _motion.level = kDefault; - } -} - - -void -VCMQmSelect::Spatial() -{ - float spatialErr = _contentMetrics->spatialPredErr; - float spatialErrH = _contentMetrics->spatialPredErrH; - float spatialErrV = _contentMetrics->spatialPredErrV; - // Spatial measure: take average of 3 prediction errors - _spatial.value = (spatialErr + spatialErrH + spatialErrV) / 3.0f; - - float scale = 1.0f; - // Reduce thresholds for HD scenes - if (_imageType > 3) - { - scale = (float)SCALE_TEXTURE_HD; - } - - if (_spatial.value > scale * HIGH_TEXTURE) - { - _spatial.level = kHigh; - } - else if (_spatial.value < scale * LOW_TEXTURE) - { - _spatial.level = kLow; - } - else - { - _spatial.level = kDefault; - } -} - - -WebRtc_Word32 -VCMQmSelect::SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height) -{ - // Match image type - WebRtc_UWord32 imageSize = width * height; - - if (imageSize < kFrameSizeTh[0]) - { - _imageType = 0; - } - else if (imageSize < kFrameSizeTh[1]) - { - _imageType = 1; - } - else if (imageSize < kFrameSizeTh[2]) - { - _imageType = 2; - } - else if (imageSize < kFrameSizeTh[3]) - { - _imageType = 3; - } - else if (imageSize < kFrameSizeTh[4]) - { - _imageType = 4; - } - else if (imageSize < kFrameSizeTh[5]) - { - _imageType = 5; - } - else - { - _imageType = 6; - } - - // Set max rate based on image size - _maxRateQM = kMaxRateQm[_imageType]; - - return VCM_OK; -} - -} // end of namespace diff --git a/modules/video_coding/main/source/qm_select.h b/modules/video_coding/main/source/qm_select.h deleted file mode 100644 index 3bca4bce9..000000000 --- a/modules/video_coding/main/source/qm_select.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_ -#define WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_ - -#include "typedefs.h" -#include "common_types.h" -/************************/ -/* Quality Modes */ -/**********************/ - -namespace webrtc -{ - -struct VideoContentMetrics; - -struct VCMQualityMode -{ - VCMQualityMode():spatialWidthFact(1), spatialHeightFact(1), - temporalFact(1){} - void Reset() - { - spatialWidthFact = 1; - spatialHeightFact = 1; - temporalFact = 1; - } - - WebRtc_UWord16 spatialWidthFact; - WebRtc_UWord16 spatialHeightFact; - WebRtc_UWord16 temporalFact; -}; - -enum VCMMagValues -{ - kLow, - kHigh, - kDefault //default do nothing mode -}; - -struct VCMContFeature -{ - VCMContFeature(): value(0.0f), level(kDefault){} - - void Reset() - { - value = 0.0f; - level = kDefault; - } - - float value; - VCMMagValues level; -}; - -class VCMQmSelect -{ -public: - VCMQmSelect(); - ~VCMQmSelect(); - - // Initialize: - WebRtc_Word32 Initialize(float bitRate, float userFrameRate, - WebRtc_UWord32 width, WebRtc_UWord32 height); - - // Allow the user to set preferences: favor frame rate/resolution - WebRtc_Word32 SetPreferences(WebRtc_Word8 resolPref); - - // Extract ST (spatio-temporal) QM behavior and make decision - // Inputs: qm: Reference to the quality modes pointer - WebRtc_Word32 SelectQuality(VCMQualityMode** qm); - - // Update QM with actual bit rate - // (size of the latest encoded frame) and frame type. - void UpdateEncodedSize(WebRtc_Word64 encodedSize, - FrameType encodedFrameType); - - // Update QM with new bit/frame/loss rates from SetTargetRates - void UpdateRates(float targetBitRate, float avgSentRate, - float incomingFrameRate, WebRtc_UWord8 packetLoss); - - // Update QM with the content metrics - void UpdateContent(const VideoContentMetrics* contentMetrics); - - // Adjust FEC rate based on content - WebRtc_UWord8 AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate, - float frameRate, WebRtc_UWord16 rttTime, - WebRtc_UWord8 packetLoss); - - - // Select 1x2,2x2,2x2 spatial sampling mode - WebRtc_Word32 SelectSpatialDirectionMode(float transRate); - - // Reset values prior to QMSelect - void ResetQM(); - - // Reset rate quantities and counter values after every QMSelect call - void ResetRates(); - - // Reset all - void Reset(); -private: - - // Compute spatial texture magnitude and level - void Spatial(); - - // Compute motion magnitude and level - void Motion(); - - // Compute motion magnitude and level for NFD metric - void MotionNFD(); - - // Compute coherence magnitude and level - void Coherence(); - - // Set the max rate for QM selection - WebRtc_Word32 SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height); - - // Content Data - const VideoContentMetrics* _contentMetrics; - - // Encoder rate control parameters, network parameters - float _targetBitRate; - float _userFrameRate; - float _incomingFrameRate; - float _perFrameBandwidth; - float _bufferLevel; - float _sumTargetRate; - float _sumIncomingFrameRate; - float _sumSeqRateMM; - float _sumFrameRateMM; - float _sumPacketLoss; - float _prevTotalRate; - WebRtc_UWord16 _prevRttTime; - WebRtc_UWord8 _prevPacketLoss; - WebRtc_Word64 _sumEncodedBytes; - - // Encoder and native frame sizes - WebRtc_UWord32 _width; - WebRtc_UWord32 _height; - WebRtc_UWord32 _nativeWidth; - WebRtc_UWord32 _nativeHeight; - WebRtc_UWord8 _stateDecFactorSpatial; - - WebRtc_UWord32 _nativeFrameRate; - WebRtc_UWord8 _stateDecFactorTemp; - - // Counters - WebRtc_UWord32 _frameCnt; - WebRtc_UWord32 _frameCntDelta; - WebRtc_UWord32 _updateRateCnt; - WebRtc_UWord32 _lowBufferCnt; - - // Content L/M/H values - VCMContFeature _motion; - VCMContFeature _spatial; - VCMContFeature _coherence; - bool _stationaryMotion; - - // Aspect ratio - float _aspectRatio; - - // Max rate to saturate the transitionalRate - WebRtc_UWord32 _maxRateQM; - WebRtc_UWord8 _imageType; - - // User preference for resolution or qmax change - WebRtc_UWord8 _userResolutionPref; - bool _init; - VCMQualityMode* _qm; - -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_ diff --git a/modules/video_coding/main/source/qm_select_data.h b/modules/video_coding/main/source/qm_select_data.h deleted file mode 100644 index 813c1109f..000000000 --- a/modules/video_coding/main/source/qm_select_data.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_ - -/*************************************************************** -*QMSelectData.h -* This file includes parameters for content-aware media optimization -****************************************************************/ - -#include "typedefs.h" - -namespace webrtc -{ - -// -// PARAMETERS FOR RESOLUTION ADAPTATION -// - -// Initial level of buffer in secs: should corresponds to wrapper settings -#define INIT_BUFFER_LEVEL 0.5 - -// Threshold of (max) buffer size below which we consider too low (underflow) -#define PERC_BUFFER_THR 0.10 - -// Threshold on rate mismatch -#define MAX_RATE_MM 0.5 - -// Avoid outliers in seq-rate MM -#define THRESH_SUM_MM 1000 - -// Threshold on the occurrences of low buffer levels -#define MAX_BUFFER_LOW 0.5 - -// Factor for transitional rate for going back up in resolution -#define TRANS_RATE_SCALE_UP_SPATIAL 1.25 -#define TRANS_RATE_SCALE_UP_TEMP 1.25 - -// Threshold on packet loss rate, above which favor resolution reduction -#define LOSS_THR 0.1 - -// Factor for reducing transitonal bitrate under packet loss -#define LOSS_RATE_FAC 1.0 - -// Maximum possible transitional rate for down-sampling: -// (units in kbps), for 30fps -const WebRtc_UWord16 kMaxRateQm[7] = { - 100, //QCIF - 500, //CIF - 800, //VGA - 1500, //4CIF - 2000, //720 HD 4:3, - 2500, //720 HD 16:9 - 3000 //1080HD -}; - -// Scale for transitional rate: based on content class -// motion=L/H/D,spatial==L/H/D: for low, high, middle levels -const float kScaleTransRateQm[18] = { - //4CIF and lower - 0.25f, // L, L - 0.75f, // L, H - 0.75f, // L, D - 0.75f, // H ,L - 0.50f, // H, H - 0.50f, // H, D - 0.50f, // D, L - 0.63f, // D, D - 0.25f, // D, H - - //over 4CIF: WHD, HD - 0.25f, // L, L - 0.75f, // L, H - 0.75f, // L, D - 0.75f, // H ,L - 0.50f, // H, H - 0.50f, // H, D - 0.50f, // D, L - 0.63f, // D, D - 0.25f // D, H -}; - -// Action for down-sampling: -// motion=L/H/D,spatial==L/H/D: for low, high, middle levels -const WebRtc_UWord8 kSpatialAction[9] = { - 1, // L, L - 1, // L, H - 1, // L, D - 4, // H ,L - 1, // H, H - 4, // H, D - 4, // D, L - 1, // D, D - 1, // D, H -}; - -const WebRtc_UWord8 kTemporalAction[9] = { - 1, // L, L - 2, // L, H - 2, // L, D - 1, // H ,L - 2, // H, H - 1, // H, D - 1, // D, L - 2, // D, D - 1, // D, H -}; - -// Control the total amount of down-sampling allowed -#define MAX_SPATIAL_DOWN_FACT 4 -#define MAX_TEMP_DOWN_FACT 4 -#define MAX_SPATIAL_TEMP_DOWN_FACT 8 - -// Minimum image size for a spatial down-sampling: -// no spatial down-sampling if input size <= MIN_IMAGE_SIZE -#define MIN_IMAGE_SIZE 25344 //176*144 - -// Minimum frame rate for temporal down-sampling: -// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE -#define MIN_FRAME_RATE_QM 8 - -// Boundaries for the closest standard frame size -const WebRtc_UWord32 kFrameSizeTh[6] = { - 63360, //between 176*144 and 352*288 - 204288, //between 352*288 and 640*480 - 356352, //between 640*480 and 704*576 - 548352, //between 704*576 and 960*720 - 806400, //between 960*720 and 1280*720 - 1497600, // between 1280*720 and 1920*1080 -}; - - -// -// PARAMETERS FOR FEC ADJUSTMENT: TODO (marpan) -// - - -// -// PARAMETETS FOR SETTING LOW/HIGH STATES OF CONTENT METRICS: -// - -// Threshold to determine if high amount of zero_motion -#define HIGH_ZERO_MOTION_SIZE 0.95 - -// Thresholds for motion: -// motion level is derived from motion vectors: motion = size_nz*magn_nz -#define HIGH_MOTION 0.7 -#define LOW_MOTION 0.4 - -// Thresholds for motion: motion level is from NFD -#define HIGH_MOTION_NFD 0.075 -#define LOW_MOTION_NFD 0.04 - -// Thresholds for spatial prediction error: -// this is appLied on the min(2x2,1x2,2x1) -#define HIGH_TEXTURE 0.035 -#define LOW_TEXTURE 0.025 - -// Used to reduce thresholds for HD scenes: correction factor since higher -// correlation in HD scenes means lower spatial prediction error -#define SCALE_TEXTURE_HD 0.9; - -// Thresholds for distortion and horizontalness: -// applied on product: horiz_nz/dist_nz -#define COHERENCE_THR 1.0 -#define COH_MAX 10 - -// percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1 -#define RATE_RED_SPATIAL_2X2 0.6 - -#define SPATIAL_ERR_2X2_VS_H 0.1 //percentage to favor 2x2 -#define SPATIAL_ERR_2X2_VS_V 0.1 //percentage to favor 2x2 over V -#define SPATIAL_ERR_V_VS_H 0.1 //percentage to favor H over V - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_ diff --git a/modules/video_coding/main/source/receiver.cc b/modules/video_coding/main/source/receiver.cc deleted file mode 100644 index 676bab88a..000000000 --- a/modules/video_coding/main/source/receiver.cc +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_coding.h" -#include "trace.h" -#include "encoded_frame.h" -#include "internal_defines.h" -#include "receiver.h" -#include "tick_time.h" - -#include - -namespace webrtc { - -VCMReceiver::VCMReceiver(VCMTiming& timing, - WebRtc_Word32 vcmId, - WebRtc_Word32 receiverId, - bool master) -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_vcmId(vcmId), -_receiverId(receiverId), -_master(master), -_jitterBuffer(vcmId, receiverId, master), -_timing(timing), -_renderWaitEvent(*new VCMEvent()), -_state(kPassive) -{ -} - -VCMReceiver::~VCMReceiver() -{ - _renderWaitEvent.Set(); - delete &_renderWaitEvent; - delete &_critSect; -} - -WebRtc_Word32 -VCMReceiver::Initialize() -{ - CriticalSectionScoped cs(_critSect); - if (!_jitterBuffer.Running()) - { - _jitterBuffer.Start(); - } - else - { - _jitterBuffer.Flush(); - } - _renderWaitEvent.Reset(); - if (_master) - { - _state = kReceiving; - } - else - { - _state = kPassive; - SetNackMode(kNoNack); - } - return VCM_OK; -} - -void VCMReceiver::UpdateRtt(WebRtc_UWord32 rtt) -{ - _jitterBuffer.UpdateRtt(rtt); -} - -WebRtc_Word32 -VCMReceiver::InsertPacket(const VCMPacket& packet, - WebRtc_UWord16 frameWidth, - WebRtc_UWord16 frameHeight) -{ - // Find an empty frame - VCMEncodedFrame *buffer = NULL; - const WebRtc_Word32 error = _jitterBuffer.GetFrame(packet, buffer); - if (error == VCM_OLD_PACKET_ERROR) - { - return VCM_OK; - } - else if (error < 0) - { - return error; - } - - { - CriticalSectionScoped cs(_critSect); - - if (frameWidth && frameHeight) - { - buffer->SetEncodedSize(static_cast(frameWidth), - static_cast(frameHeight)); - } - - if (_master) - { - // Only trace the primary receiver to make it possible - // to parse and plot the trace file. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Packet seqNo %u of frame %u at %u", - packet.seqNum, packet.timestamp, - MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp())); - } - - const bool emptyFrame = (buffer->Length() == 0); - const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); - - WebRtc_Word64 renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs); - - if (renderTimeMs < 0) - { - // Render time error. Assume that this is due to some change in - // the incoming video stream and reset the JB and the timing. - _jitterBuffer.Flush(); - _timing.Reset(); - return VCM_OK; - } - else if (renderTimeMs < nowMs - kMaxVideoDelayMs) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "This frame should have been rendered more than %u ms ago." - "Flushing jitter buffer and resetting timing.", kMaxVideoDelayMs); - _jitterBuffer.Flush(); - _timing.Reset(); - return VCM_OK; - } - else if (_timing.TargetVideoDelay() > kMaxVideoDelayMs) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "More than %u ms target delay. Flushing jitter buffer and resetting timing.", - kMaxVideoDelayMs); - _jitterBuffer.Flush(); - _timing.Reset(); - return VCM_OK; - } - - // First packet received belonging to this frame. - if (buffer->Length() == 0) - { - const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); - if (_master) - { - // Only trace the primary receiver to make it possible to parse and plot the trace file. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "First packet of frame %u at %u", packet.timestamp, - MaskWord64ToUWord32(nowMs)); - } - renderTimeMs = _timing.RenderTimeMs(packet.timestamp, nowMs); - if (renderTimeMs >= 0) - { - buffer->SetRenderTime(renderTimeMs); - } - else - { - buffer->SetRenderTime(nowMs); - } - } - - // Insert packet into jitter buffer - // both media and empty packets - const VCMFrameBufferEnum ret = _jitterBuffer.InsertPacket(buffer, packet); - - if (ret < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Error inserting packet seqNo=%u, timeStamp=%u", - packet.seqNum, packet.timestamp); - return VCM_JITTER_BUFFER_ERROR; - } - } - return VCM_OK; -} - -VCMEncodedFrame* -VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, WebRtc_Word64& nextRenderTimeMs, - bool renderTiming, VCMReceiver* dualReceiver) -{ - // No need to enter the critical section here since the jitter buffer - // is thread-safe. - FrameType incomingFrameType = kVideoFrameDelta; - nextRenderTimeMs = -1; - const WebRtc_Word64 startTimeMs = VCMTickTime::MillisecondTimestamp(); - WebRtc_Word64 ret = _jitterBuffer.GetNextTimeStamp(maxWaitTimeMs, - incomingFrameType, - nextRenderTimeMs); - if (ret < 0) - { - // No timestamp in jitter buffer at the moment - return NULL; - } - const WebRtc_UWord32 timeStamp = static_cast(ret); - - // Update the timing - _timing.SetRequiredDelay(_jitterBuffer.GetEstimatedJitterMS()); - _timing.UpdateCurrentDelay(timeStamp); - - const WebRtc_Word32 tempWaitTime = maxWaitTimeMs - - static_cast(VCMTickTime::MillisecondTimestamp() - startTimeMs); - WebRtc_UWord16 newMaxWaitTime = static_cast(VCM_MAX(tempWaitTime, 0)); - - VCMEncodedFrame* frame = NULL; - - if (renderTiming) - { - frame = FrameForDecoding(newMaxWaitTime, nextRenderTimeMs, dualReceiver); - } - else - { - frame = FrameForRendering(newMaxWaitTime, nextRenderTimeMs, dualReceiver); - } - - if (frame != NULL) - { - bool retransmitted = false; - const WebRtc_Word64 lastPacketTimeMs = - _jitterBuffer.LastPacketTime(frame, retransmitted); - if (lastPacketTimeMs >= 0 && !retransmitted) - { - // We don't want to include timestamps which have suffered from retransmission - // here, since we compensate with extra retransmission delay within - // the jitter estimate. - _timing.IncomingTimestamp(timeStamp, lastPacketTimeMs); - } - if (dualReceiver != NULL) - { - dualReceiver->UpdateState(*frame); - } - } - return frame; -} - -VCMEncodedFrame* -VCMReceiver::FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, - WebRtc_Word64 nextRenderTimeMs, - VCMReceiver* dualReceiver) -{ - // How long can we wait until we must decode the next frame - WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs, - VCMTickTime::MillisecondTimestamp()); - - // Try to get a complete frame from the jitter buffer - VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0); - - if (frame == NULL && maxWaitTimeMs == 0 && waitTimeMs > 0) - { - // If we're not allowed to wait for frames to get complete we must calculate if - // it's time to decode, and if it's not we will just return for now. - return NULL; - } - - if (frame == NULL) - { - // Wait for a complete frame - waitTimeMs = VCM_MIN(waitTimeMs, maxWaitTimeMs); - frame = _jitterBuffer.GetCompleteFrameForDecoding(waitTimeMs); - } - if (frame == NULL) - { - // Get an incomplete frame - if (_timing.MaxWaitingTime(nextRenderTimeMs, VCMTickTime::MillisecondTimestamp()) > 0) - { - // Still time to wait for a complete frame - return NULL; - } - - // No time left to wait, we must decode this frame now. - const bool dualReceiverEnabledAndPassive = dualReceiver != NULL && - dualReceiver->State() == kPassive && - dualReceiver->NackMode() == kNackInfinite; - if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame()) - { - // Jitter buffer state might get corrupt with this frame. - dualReceiver->CopyJitterBufferStateFromReceiver(*this); - } - - frame = _jitterBuffer.GetFrameForDecoding(); - } - return frame; -} - -VCMEncodedFrame* -VCMReceiver::FrameForRendering(WebRtc_UWord16 maxWaitTimeMs, - WebRtc_Word64 nextRenderTimeMs, - VCMReceiver* dualReceiver) -{ - // How long MUST we wait until we must decode the next frame. This is different for the case - // where we have a renderer which can render at a specified time. Here we must wait as long - // as possible before giving the frame to the decoder, which will render the frame as soon - // as it has been decoded. - WebRtc_UWord32 waitTimeMs = _timing.MaxWaitingTime(nextRenderTimeMs, - VCMTickTime::MillisecondTimestamp()); - if (maxWaitTimeMs < waitTimeMs) - { - // If we're not allowed to wait until the frame is supposed to be rendered - // we will have to return NULL for now. - return NULL; - } - // Wait until it's time to render - _renderWaitEvent.Wait(waitTimeMs); - - // Get a complete frame if possible - VCMEncodedFrame* frame = _jitterBuffer.GetCompleteFrameForDecoding(0); - - if (frame == NULL) - { - // Get an incomplete frame - const bool dualReceiverEnabledAndPassive = dualReceiver != NULL && - dualReceiver->State() == kPassive && - dualReceiver->NackMode() == kNackInfinite; - if (dualReceiverEnabledAndPassive && !_jitterBuffer.CompleteSequenceWithNextFrame()) - { - // Jitter buffer state might get corrupt with this frame. - dualReceiver->CopyJitterBufferStateFromReceiver(*this); - } - - frame = _jitterBuffer.GetFrameForDecoding(); - } - return frame; -} - -void -VCMReceiver::ReleaseFrame(VCMEncodedFrame* frame) -{ - _jitterBuffer.ReleaseFrame(frame); -} - -WebRtc_Word32 -VCMReceiver::ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate) -{ - const WebRtc_Word32 ret = _jitterBuffer.GetUpdate(frameRate, bitRate); - bitRate /= 1000; // Should be in kbps - return ret; -} - -WebRtc_Word32 -VCMReceiver::ReceivedFrameCount(VCMFrameCount& frameCount) const -{ - return _jitterBuffer.GetFrameStatistics(frameCount.numDeltaFrames, - frameCount.numKeyFrames); -} - -void -VCMReceiver::SetNackMode(VCMNackMode nackMode) -{ - CriticalSectionScoped cs(_critSect); - _jitterBuffer.SetNackMode(nackMode); - if (!_master) - { - _state = kPassive; // The dual decoder defaults to passive - } -} - -VCMNackMode -VCMReceiver::NackMode() const -{ - CriticalSectionScoped cs(_critSect); - return _jitterBuffer.GetNackMode(); -} - -VCMNackStatus -VCMReceiver::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size) -{ - bool extended = false; - WebRtc_UWord16 nackListSize = 0; - WebRtc_UWord16* internalNackList = _jitterBuffer.GetNackList(nackListSize, extended); - if (internalNackList == NULL && nackListSize == 0xffff) - { - // This combination is used to trigger key frame requests. - size = 0; - return kNackKeyFrameRequest; - } - if (nackListSize > size) - { - size = nackListSize; - return kNackNeedMoreMemory; - } - memcpy(nackList, internalNackList, nackListSize * sizeof(WebRtc_UWord16)); - size = nackListSize; - return kNackOk; -} - -// Decide whether we should change decoder state. This should be done if the dual decoder -// has caught up with the decoder decoding with packet losses. -bool -VCMReceiver::DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const -{ - if (dualFrame == NULL) - { - return false; - } - if (_jitterBuffer.LastDecodedTimestamp() == dualFrame->TimeStamp()) - { - dualReceiver.UpdateState(kWaitForPrimaryDecode); - return true; - } - return false; -} - -void -VCMReceiver::CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver) -{ - _jitterBuffer = receiver._jitterBuffer; -} - -VCMReceiverState -VCMReceiver::State() const -{ - CriticalSectionScoped cs(_critSect); - return _state; -} - -void -VCMReceiver::UpdateState(VCMReceiverState newState) -{ - CriticalSectionScoped cs(_critSect); - assert(!(_state == kPassive && newState == kWaitForPrimaryDecode)); -// assert(!(_state == kReceiving && newState == kPassive)); - _state = newState; -} - -void -VCMReceiver::UpdateState(VCMEncodedFrame& frame) -{ - if (_jitterBuffer.GetNackMode() == kNoNack) - { - // Dual decoder mode has not been enabled. - return; - } - // Update the dual receiver state - if (frame.Complete() && frame.FrameType() == kVideoFrameKey) - { - UpdateState(kPassive); - } - if (State() == kWaitForPrimaryDecode && - frame.Complete() && !frame.MissingFrame()) - { - UpdateState(kPassive); - } - if (frame.MissingFrame() || !frame.Complete()) - { - // State was corrupted, enable dual receiver. - UpdateState(kReceiving); - } -} - -} diff --git a/modules/video_coding/main/source/receiver.h b/modules/video_coding/main/source/receiver.h deleted file mode 100644 index 0ca6994a1..000000000 --- a/modules/video_coding/main/source/receiver.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_ - -#include "critical_section_wrapper.h" -#include "jitter_buffer.h" -#include "timing.h" -#include "packet.h" - -namespace webrtc -{ - -class VCMEncodedFrame; - -enum VCMNackStatus -{ - kNackOk, - kNackNeedMoreMemory, - kNackKeyFrameRequest -}; - - -enum VCMReceiverState -{ - kReceiving, - kPassive, - kWaitForPrimaryDecode -}; - -class VCMReceiver -{ -public: - VCMReceiver(VCMTiming& timing, - WebRtc_Word32 vcmId = -1, - WebRtc_Word32 receiverId = -1, - bool master = true); - ~VCMReceiver(); - - WebRtc_Word32 Initialize(); - void UpdateRtt(WebRtc_UWord32 rtt); - WebRtc_Word32 InsertPacket(const VCMPacket& packet, - WebRtc_UWord16 frameWidth, - WebRtc_UWord16 frameHeight); - VCMEncodedFrame* FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, - WebRtc_Word64& nextRenderTimeMs, - bool renderTiming = true, - VCMReceiver* dualReceiver = NULL); - void ReleaseFrame(VCMEncodedFrame* frame); - WebRtc_Word32 ReceiveStatistics(WebRtc_UWord32& bitRate, WebRtc_UWord32& frameRate); - WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const; - - // NACK - void SetNackMode(VCMNackMode nackMode); - VCMNackMode NackMode() const; - VCMNackStatus NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size); - - // Dual decoder - bool DualDecoderCaughtUp(VCMEncodedFrame* dualFrame, VCMReceiver& dualReceiver) const; - VCMReceiverState State() const; - -private: - VCMEncodedFrame* FrameForDecoding(WebRtc_UWord16 maxWaitTimeMs, - WebRtc_Word64 nextrenderTimeMs, - VCMReceiver* dualReceiver); - VCMEncodedFrame* FrameForRendering(WebRtc_UWord16 maxWaitTimeMs, - WebRtc_Word64 nextrenderTimeMs, - VCMReceiver* dualReceiver); - void CopyJitterBufferStateFromReceiver(const VCMReceiver& receiver); - void UpdateState(VCMReceiverState newState); - void UpdateState(VCMEncodedFrame& frame); - static WebRtc_Word32 GenerateReceiverId(); - - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _vcmId; - WebRtc_Word32 _receiverId; - bool _master; - VCMJitterBuffer _jitterBuffer; - VCMTiming& _timing; - VCMEvent& _renderWaitEvent; - VCMReceiverState _state; - - static WebRtc_Word32 _receiverIdCounter; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_RECEIVER_H_ diff --git a/modules/video_coding/main/source/rtt_filter.cc b/modules/video_coding/main/source/rtt_filter.cc deleted file mode 100644 index 36f766085..000000000 --- a/modules/video_coding/main/source/rtt_filter.cc +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "internal_defines.h" -#include "rtt_filter.h" - -#include -#include -#include - -namespace webrtc { - -VCMRttFilter::VCMRttFilter(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId) -: -_vcmId(vcmId), -_receiverId(receiverId), -_filtFactMax(35), -_jumpStdDevs(2.5), -_driftStdDevs(3.5), -_detectThreshold(kMaxDriftJumpCount) -{ - Reset(); -} - -VCMRttFilter& -VCMRttFilter::operator=(const VCMRttFilter& rhs) -{ - if (this != &rhs) - { - _gotNonZeroUpdate = rhs._gotNonZeroUpdate; - _avgRtt = rhs._avgRtt; - _varRtt = rhs._varRtt; - _maxRtt = rhs._maxRtt; - _filtFactCount = rhs._filtFactCount; - _jumpCount = rhs._jumpCount; - _driftCount = rhs._driftCount; - memcpy(_jumpBuf, rhs._jumpBuf, sizeof(_jumpBuf)); - memcpy(_driftBuf, rhs._driftBuf, sizeof(_driftBuf)); - } - return *this; -} - -void -VCMRttFilter::Reset() -{ - _gotNonZeroUpdate = false; - _avgRtt = 0; - _varRtt = 0; - _maxRtt = 0; - _filtFactCount = 1; - _jumpCount = 0; - _driftCount = 0; - memset(_jumpBuf, 0, kMaxDriftJumpCount); - memset(_driftBuf, 0, kMaxDriftJumpCount); -} - -void -VCMRttFilter::Update(WebRtc_UWord32 rttMs) -{ - if (!_gotNonZeroUpdate) - { - if (rttMs == 0) - { - return; - } - _gotNonZeroUpdate = true; - } - - // Sanity check - if (rttMs > 3000) - { - rttMs = 3000; - } - - double filtFactor = 0; - if (_filtFactCount > 1) - { - filtFactor = static_cast(_filtFactCount - 1) / _filtFactCount; - } - _filtFactCount++; - if (_filtFactCount > _filtFactMax) - { - // This prevents filtFactor from going above - // (_filtFactMax - 1) / _filtFactMax, - // e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98 - _filtFactCount = _filtFactMax; - } - double oldAvg = _avgRtt; - double oldVar = _varRtt; - _avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs; - _varRtt = filtFactor * _varRtt + (1 - filtFactor) * - (rttMs - _avgRtt) * (rttMs - _avgRtt); - _maxRtt = VCM_MAX(rttMs, _maxRtt); - if (!JumpDetection(rttMs) || !DriftDetection(rttMs)) - { - // In some cases we don't want to update the statistics - _avgRtt = oldAvg; - _varRtt = oldVar; - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "RttFilter Update: sample=%u avgRtt=%f varRtt=%f maxRtt=%u", - rttMs, _avgRtt, _varRtt, _maxRtt); -} - -bool -VCMRttFilter::JumpDetection(WebRtc_UWord32 rttMs) -{ - double diffFromAvg = _avgRtt - rttMs; - if (abs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt)) - { - int diffSign = (diffFromAvg >= 0) ? 1 : -1; - int jumpCountSign = (_jumpCount >= 0) ? 1 : -1; - if (diffSign != jumpCountSign) - { - // Since the signs differ the samples currently - // in the buffer is useless as they represent a - // jump in a different direction. - _jumpCount = 0; - } - if (abs(_jumpCount) < kMaxDriftJumpCount) - { - // Update the buffer used for the short time - // statistics. - // The sign of the diff is used for updating the counter since - // we want to use the same buffer for keeping track of when - // the RTT jumps down and up. - _jumpBuf[abs(_jumpCount)] = rttMs; - _jumpCount += diffSign; - } - if (abs(_jumpCount) >= _detectThreshold) - { - // Detected an RTT jump - ShortRttFilter(_jumpBuf, abs(_jumpCount)); - _filtFactCount = _detectThreshold + 1; - _jumpCount = 0; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Detected an RTT jump"); - } - else - { - return false; - } - } - else - { - _jumpCount = 0; - } - return true; -} - -bool -VCMRttFilter::DriftDetection(WebRtc_UWord32 rttMs) -{ - if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt)) - { - if (_driftCount < kMaxDriftJumpCount) - { - // Update the buffer used for the short time - // statistics. - _driftBuf[_driftCount] = rttMs; - _driftCount++; - } - if (_driftCount >= _detectThreshold) - { - // Detected an RTT drift - ShortRttFilter(_driftBuf, _driftCount); - _filtFactCount = _detectThreshold + 1; - _driftCount = 0; - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _receiverId), - "Detected an RTT drift"); - } - } - else - { - _driftCount = 0; - } - return true; -} - -void -VCMRttFilter::ShortRttFilter(WebRtc_UWord32* buf, WebRtc_UWord32 length) -{ - if (length == 0) - { - return; - } - _maxRtt = 0; - _avgRtt = 0; - for (WebRtc_UWord32 i=0; i < length; i++) - { - if (buf[i] > _maxRtt) - { - _maxRtt = buf[i]; - } - _avgRtt += buf[i]; - } - _avgRtt = _avgRtt / static_cast(length); -} - -WebRtc_UWord32 -VCMRttFilter::RttMs() const -{ - return static_cast(_maxRtt + 0.5); -} - -} diff --git a/modules/video_coding/main/source/rtt_filter.h b/modules/video_coding/main/source/rtt_filter.h deleted file mode 100644 index 5ec85fd8d..000000000 --- a/modules/video_coding/main/source/rtt_filter.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_ - -#include "typedefs.h" - -namespace webrtc -{ - -class VCMRttFilter -{ -public: - VCMRttFilter(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0); - - VCMRttFilter& operator=(const VCMRttFilter& rhs); - - // Resets the filter. - void Reset(); - // Updates the filter with a new sample. - void Update(WebRtc_UWord32 rttMs); - // A getter function for the current RTT level in ms. - WebRtc_UWord32 RttMs() const; - -private: - // The size of the drift and jump memory buffers - // and thus also the detection threshold for these - // detectors in number of samples. - enum { kMaxDriftJumpCount = 5 }; - // Detects RTT jumps by comparing the difference between - // samples and average to the standard deviation. - // Returns true if the long time statistics should be updated - // and false otherwise - bool JumpDetection(WebRtc_UWord32 rttMs); - // Detects RTT drifts by comparing the difference between - // max and average to the standard deviation. - // Returns true if the long time statistics should be updated - // and false otherwise - bool DriftDetection(WebRtc_UWord32 rttMs); - // Computes the short time average and maximum of the vector buf. - void ShortRttFilter(WebRtc_UWord32* buf, WebRtc_UWord32 length); - - WebRtc_Word32 _vcmId; - WebRtc_Word32 _receiverId; - bool _gotNonZeroUpdate; - double _avgRtt; - double _varRtt; - WebRtc_UWord32 _maxRtt; - WebRtc_UWord32 _filtFactCount; - const WebRtc_UWord32 _filtFactMax; - const double _jumpStdDevs; - const double _driftStdDevs; - WebRtc_Word32 _jumpCount; - WebRtc_Word32 _driftCount; - const WebRtc_Word32 _detectThreshold; - WebRtc_UWord32 _jumpBuf[kMaxDriftJumpCount]; - WebRtc_UWord32 _driftBuf[kMaxDriftJumpCount]; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_RTT_FILTER_H_ diff --git a/modules/video_coding/main/source/session_info.cc b/modules/video_coding/main/source/session_info.cc deleted file mode 100644 index 7145b2902..000000000 --- a/modules/video_coding/main/source/session_info.cc +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "packet.h" -#include "session_info.h" - -#include -#include - -namespace webrtc { - -VCMSessionInfo::VCMSessionInfo(): - _haveFirstPacket(false), - _markerBit(false), - _sessionNACK(false), - _completeSession(false), - _frameType(kVideoFrameDelta), - _previousFrameLoss(false), - _lowSeqNum(-1), - _highSeqNum(-1), - _highestPacketIndex(0), - _emptySeqNumLow(-1), - _emptySeqNumHigh(-1), - _markerSeqNum(-1) -{ - memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); - memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness)); - memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte)); -} - -VCMSessionInfo::~VCMSessionInfo() -{ -} - -WebRtc_Word32 -VCMSessionInfo::GetLowSeqNum() const -{ - return _lowSeqNum; -} - -WebRtc_Word32 -VCMSessionInfo::GetHighSeqNum() const -{ - if (_emptySeqNumHigh != -1) - { - return _emptySeqNumHigh; - } - return _highSeqNum; -} - -void -VCMSessionInfo::Reset() -{ - _lowSeqNum = -1; - _highSeqNum = -1; - _emptySeqNumLow = -1; - _emptySeqNumHigh = -1; - _markerBit = false; - _haveFirstPacket = false; - _completeSession = false; - _frameType = kVideoFrameDelta; - _previousFrameLoss = false; - _sessionNACK = false; - _highestPacketIndex = 0; - _markerSeqNum = -1; - memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); - memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness)); - memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte)); -} - -WebRtc_UWord32 VCMSessionInfo::GetSessionLength() -{ - WebRtc_UWord32 length = 0; - for (WebRtc_Word32 i = 0; i <= _highestPacketIndex; ++i) - { - length += _packetSizeBytes[i]; - } - return length; -} - -void -VCMSessionInfo::SetStartSeqNumber(WebRtc_UWord16 seqNumber) -{ - _lowSeqNum = seqNumber; - _highSeqNum = seqNumber; -} - -bool -VCMSessionInfo::HaveStartSeqNumber() -{ - if (_lowSeqNum == -1 || _highSeqNum == -1) - { - return false; - } - return true; -} - -WebRtc_UWord32 -VCMSessionInfo::InsertBuffer(WebRtc_UWord8* ptrStartOfLayer, - WebRtc_Word32 packetIndex, - const VCMPacket& packet) -{ - WebRtc_UWord32 moveLength = 0; - WebRtc_UWord32 returnLength = 0; - int i = 0; - - // need to calc offset before updating _packetSizeBytes - WebRtc_UWord32 offset = 0; - WebRtc_UWord32 packetSize = 0; - - // Store this packet length. Add length since we could have data present - // already (e.g. multicall case). - if (packet.bits) - { - packetSize = packet.sizeBytes; - } - else - { - packetSize = packet.sizeBytes + - (packet.insertStartCode ? kH264StartCodeLengthBytes : 0); - } - - _packetSizeBytes[packetIndex] += packetSize; - - // count only the one in our layer - for (i = 0; i < packetIndex; ++i) - { - offset += _packetSizeBytes[i]; - } - for (i = packetIndex + 1; i <= _highestPacketIndex; ++i) - { - moveLength += _packetSizeBytes[i]; - } - if (moveLength > 0) - { - memmove((void*)(ptrStartOfLayer + offset + packetSize), - ptrStartOfLayer + offset, moveLength); - } - - if (packet.bits) - { - // Add the packet without ORing end and start bytes together. - // This is done when the frame is fetched for decoding by calling - // GlueTogether(). - _ORwithPrevByte[packetIndex] = true; - if (packet.dataPtr != NULL) - { - memcpy((void*)(ptrStartOfLayer + offset), packet.dataPtr, - packetSize); - } - returnLength = packetSize; - } - else - { - _ORwithPrevByte[packetIndex] = false; - if (packet.dataPtr != NULL) - { - const unsigned char startCode[] = {0, 0, 0, 1}; - if(packet.insertStartCode) - { - memcpy((void*)(ptrStartOfLayer + offset), startCode, - kH264StartCodeLengthBytes); - } - memcpy((void*)(ptrStartOfLayer + offset - + (packet.insertStartCode ? kH264StartCodeLengthBytes : 0)), - packet.dataPtr, - packet.sizeBytes); - } - returnLength = packetSize; - } - - if (packet.isFirstPacket) - { - _haveFirstPacket = true; - } - if (packet.markerBit) - { - _markerBit = true; - _markerSeqNum = packet.seqNum; - } - // Store information about if the packet is decodable as is or not. - _naluCompleteness[packetIndex] = packet.completeNALU; - - UpdateCompleteSession(); - - return returnLength; -} - -void -VCMSessionInfo::UpdateCompleteSession() -{ - if (_haveFirstPacket && _markerBit) - { - // Do we have all the packets in this session? - bool completeSession = true; - - for (int i = 0; i <= _highestPacketIndex; ++i) - { - if (_naluCompleteness[i] == kNaluUnset) - { - completeSession = false; - break; - } - } - _completeSession = completeSession; - } -} - -bool VCMSessionInfo::IsSessionComplete() -{ - return _completeSession; -} - -// Find the start and end index of packetIndex packet. -// startIndex -1 if start not found endIndex = -1 if end index not found -void -VCMSessionInfo::FindNaluBorder(WebRtc_Word32 packetIndex, - WebRtc_Word32& startIndex, - WebRtc_Word32& endIndex) -{ - if (_naluCompleteness[packetIndex] == kNaluStart || - _naluCompleteness[packetIndex] == kNaluComplete) - { - startIndex = packetIndex; - } - else // Need to find the start - { - for (startIndex = packetIndex - 1; startIndex >= 0; --startIndex) - { - - if ((_naluCompleteness[startIndex] == kNaluComplete && - _packetSizeBytes[startIndex] > 0) || - // Found previous NALU. - (_naluCompleteness[startIndex] == kNaluEnd && - startIndex > 0)) - { - startIndex++; - break; - } - // This is where the NALU start. - if (_naluCompleteness[startIndex] == kNaluStart) - { - break; - } - } - } - - if (_naluCompleteness[packetIndex] == kNaluEnd || - _naluCompleteness[packetIndex] == kNaluComplete) - { - endIndex = packetIndex; - } - else - { - // Find the next NALU - for (endIndex = packetIndex + 1; endIndex <= _highestPacketIndex; - ++endIndex) - { - if ((_naluCompleteness[endIndex] == kNaluComplete && - _packetSizeBytes[endIndex] > 0) || - // Found next NALU. - _naluCompleteness[endIndex] == kNaluStart) - { - endIndex--; - break; - } - if ( _naluCompleteness[endIndex] == kNaluEnd) - { - // This is where the NALU end. - break; - } - } - if (endIndex > _highestPacketIndex) - { - endIndex = -1; - } - } -} - -// Deletes all packets between startIndex and endIndex -WebRtc_UWord32 -VCMSessionInfo::DeletePackets(WebRtc_UWord8* ptrStartOfLayer, - WebRtc_Word32 startIndex, - WebRtc_Word32 endIndex) -{ - - //Get the number of bytes to delete. - //Clear the size of these packets. - WebRtc_UWord32 bytesToDelete = 0; /// The number of bytes to delete. - for (int j = startIndex;j <= endIndex; ++j) - { - bytesToDelete += _packetSizeBytes[j]; - _packetSizeBytes[j] = 0; - } - if (bytesToDelete > 0) - { - // Get the offset we want to move to. - int destOffset = 0; - for (int j = 0;j < startIndex;j++) - { - destOffset += _packetSizeBytes[j]; - } - - //Get the number of bytes to move - WebRtc_UWord32 numberOfBytesToMove = 0; - for (int j = endIndex + 1; j <= _highestPacketIndex; ++j) - { - numberOfBytesToMove += _packetSizeBytes[j]; - } - - memmove((void*)(ptrStartOfLayer + destOffset),(void*)(ptrStartOfLayer + - destOffset+bytesToDelete), numberOfBytesToMove); - - } - - return bytesToDelete; -} - -// Makes the layer decodable. Ie only contain decodable NALU -// return the number of bytes deleted from the session. -1 if an error occurs -WebRtc_UWord32 -VCMSessionInfo::MakeSessionDecodable(WebRtc_UWord8* ptrStartOfLayer) -{ - if (_lowSeqNum < 0) // No packets in this session - { - return 0; - } - - WebRtc_Word32 startIndex = 0; - WebRtc_Word32 endIndex = 0; - int packetIndex = 0; - WebRtc_UWord32 returnLength = 0; - for (packetIndex = 0; packetIndex <= _highestPacketIndex; ++packetIndex) - { - if (_naluCompleteness[packetIndex] == kNaluUnset) // Found a lost packet - { - FindNaluBorder(packetIndex, startIndex, endIndex); - if (startIndex == -1) - { - startIndex = 0; - } - if (endIndex == -1) - { - endIndex = _highestPacketIndex; - } - - returnLength += DeletePackets(ptrStartOfLayer, - packetIndex, endIndex); - packetIndex = endIndex; - }// end lost packet - } - - // Make sure the first packet is decodable (Either complete nalu or start - // of NALU) - if (_packetSizeBytes[0] > 0) - { - switch (_naluCompleteness[0]) - { - case kNaluComplete: // Packet can be decoded as is. - break; - - case kNaluStart: - // Packet contain beginning of NALU- No need to do anything. - break; - case kNaluIncomplete: //Packet is not beginning or end of NALU - // Need to find the end of this NALU and delete all packets. - FindNaluBorder(0,startIndex,endIndex); - if (endIndex == -1) // No end found. Delete - { - endIndex = _highestPacketIndex; - } - // Delete this NALU. - returnLength += DeletePackets(ptrStartOfLayer, 0, endIndex); - break; - case kNaluEnd: // Packet is the end of a NALU - // Delete this NALU - returnLength += DeletePackets(ptrStartOfLayer, 0, 0); - break; - default: - assert(false); - } - } - - return returnLength; -} - -WebRtc_Word32 -VCMSessionInfo::ZeroOutSeqNum(WebRtc_Word32* list, - WebRtc_Word32 numberOfSeqNum) -{ - if ((NULL == list) || (numberOfSeqNum < 1)) - { - return -1; - } - if (_lowSeqNum == -1) - { - // no packets in this frame - return 0; - } - - // Find end point (index of entry that equals _lowSeqNum) - int index = 0; - for (; index < numberOfSeqNum; index++) - { - if (list[index] == _lowSeqNum) - { - list[index] = -1; - break; - } - } - - // Zero out between first entry and end point - int i = 0; - while ( i <= _highestPacketIndex && index < numberOfSeqNum) - { - if (_naluCompleteness[i] != kNaluUnset) - { - list[index] = -1; - } - else - { - _sessionNACK = true; - } - i++; - index++; - } - if (!_haveFirstPacket) - { - _sessionNACK = true; - } - return 0; -} - -WebRtc_Word32 -VCMSessionInfo::ZeroOutSeqNumHybrid(WebRtc_Word32* list, - WebRtc_Word32 numberOfSeqNum, - float rttScore) -{ - if ((NULL == list) || (numberOfSeqNum < 1)) - { - return -1; - } - if (_lowSeqNum == -1) - { - // no media packets in this frame - return 0; - } - - WebRtc_Word32 index = 0; - // Find end point (index of entry that equals _lowSeqNum) - for (; index < numberOfSeqNum; index++) - { - if (list[index] == _lowSeqNum) - { - list[index] = -1; - break; - } - } - - // TODO(mikhal): 1. update score based on RTT value 2. add partition data - // use the previous available - bool isBaseAvailable = false; - if ((index > 0) && (list[index] == -1)) - { - // found first packet, for now let's go only one back - if ((list[index - 1] == -1) || (list[index - 1] == -2)) - { - // this is indeed the first packet, as previous packet was populated - isBaseAvailable = true; - } - } - bool allowNack = false; - if (!_haveFirstPacket || !isBaseAvailable) - { - allowNack = true; - } - - // Zero out between first entry and end point - int i = 0; - // Score place holder - based on RTT and partition (when available). - const float nackScoreTh = 0.25f; - - WebRtc_Word32 highMediaPacket; - if (_markerSeqNum != -1) - { - highMediaPacket = _markerSeqNum; - } - else - { - highMediaPacket = _emptySeqNumLow - 1 > _highSeqNum ? - _emptySeqNumLow - 1: _highSeqNum; - } - - while (list[index] <= highMediaPacket && index < numberOfSeqNum) - { - if (_naluCompleteness[i] != kNaluUnset) - { - list[index] = -1; - } - else - { - // compute score of the packet - float score = 1.0f; - // multiply internal score (importance) by external score (RTT) - score *= rttScore; - if (score > nackScoreTh) - { - allowNack = true; - } - else - { - list[index] = -1; - } - } - i++; - index++; - } - // Empty packets follow the data packets, and therefore have a higher - // sequence number. We do not want to NACK empty packets. - - if ((_emptySeqNumLow != -1) && (_emptySeqNumHigh != -1) && - (index < numberOfSeqNum)) - { - // first make sure that we are at least at the minimum value - // (if not we are missing last packet(s)) - while (list[index] < _emptySeqNumLow && index < numberOfSeqNum) - { - index++; - } - - // mark empty packets - while (list[index] <= _emptySeqNumHigh && index < numberOfSeqNum) - { - list[index] = -2; - index++; - } - } - - _sessionNACK = allowNack; - return 0; -} - -WebRtc_Word32 -VCMSessionInfo::GetHighestPacketIndex() -{ - return _highestPacketIndex; -} - -bool -VCMSessionInfo::HaveLastPacket() -{ - return _markerBit; -} - -void -VCMSessionInfo::ForceSetHaveLastPacket() -{ - _markerBit = true; - UpdateCompleteSession(); -} - -bool -VCMSessionInfo::IsRetransmitted() -{ - return _sessionNACK; -} - -void -VCMSessionInfo::UpdatePacketSize(WebRtc_Word32 packetIndex, - WebRtc_UWord32 length) -{ - // sanity - if (packetIndex >= kMaxPacketsInJitterBuffer || packetIndex < 0) - { - // not allowed - assert(!"SessionInfo::UpdatePacketSize Error: invalid packetIndex"); - return; - } - _packetSizeBytes[packetIndex] = length; -} - -WebRtc_Word64 -VCMSessionInfo::InsertPacket(const VCMPacket& packet, - WebRtc_UWord8* ptrStartOfLayer) -{ - // not allowed - assert(!packet.insertStartCode || !packet.bits); - // Check if this is first packet (only valid for some codecs) - if (packet.isFirstPacket) - { - // the first packet in the frame always signals the frametype - _frameType = packet.frameType; - } - else if (_frameType == kFrameEmpty && packet.frameType != kFrameEmpty) - { - // Update the frame type with the first media packet - _frameType = packet.frameType; - } - if (packet.frameType == kFrameEmpty) - { - // update seq number as an empty packet - InformOfEmptyPacket(packet.seqNum); - return 0; - } - - // Check sequence number and update highest and lowest sequence numbers - // received. Move data if this seq num is lower than previously lowest. - - if (packet.seqNum > _highSeqNum) - { - // This packet's seq num is higher than previously highest seq num; - // normal case if we have a wrap, only update with wrapped values - if (!(_highSeqNum < 0x00ff && packet.seqNum > 0xff00)) - { - _highSeqNum = packet.seqNum; - } - } else if (_highSeqNum > 0xff00 && packet.seqNum < 0x00ff) - { - // wrap - _highSeqNum = packet.seqNum; - } - int packetIndex = packet.seqNum - (WebRtc_UWord16)_lowSeqNum; - if (_lowSeqNum < 0x00ff && packet.seqNum > 0xff00) - { - // negative wrap - packetIndex = packet.seqNum - 0x10000 - _lowSeqNum; - } - if (packetIndex < 0) - { - if (_lowSeqNum > 0xff00 && packet.seqNum < 0x00ff) - { - // we have a false detect due to the wrap - packetIndex = (0xffff - (WebRtc_UWord16)_lowSeqNum) + packet.seqNum - + (WebRtc_UWord16)1; - } else - { - // This packet's seq num is lower than previously lowest seq num, - // but no wrap We need to move the data in all arrays indexed by - // packetIndex and insert the new packet's info - // How many packets should we leave room for (positions to shift)? - // Example - this seq num is 3 lower than previously lowest seq num - // Before: |--prev packet with lowest seq num--|--|...| - // After: |--new lowest seq num--|--|--|--prev packet with - // lowest seq num--|--|...| - - WebRtc_UWord16 positionsToShift = (WebRtc_UWord16)_lowSeqNum - - packet.seqNum; - WebRtc_UWord16 numOfPacketsToMove = _highestPacketIndex + 1; - - // sanity, do we have room for the shift? - if ((positionsToShift + numOfPacketsToMove) > - kMaxPacketsInJitterBuffer) - { - return -1; - } - - // Shift _ORwithPrevByte array - memmove(&_ORwithPrevByte[positionsToShift], - &_ORwithPrevByte[0], numOfPacketsToMove*sizeof(bool)); - memset(&_ORwithPrevByte[0], false, positionsToShift*sizeof(bool)); - - // Shift _packetSizeBytes array - memmove(&_packetSizeBytes[positionsToShift], - &_packetSizeBytes[0], - numOfPacketsToMove * sizeof(WebRtc_UWord32)); - memset(&_packetSizeBytes[0], 0, - positionsToShift * sizeof(WebRtc_UWord32)); - - // Shift _naluCompleteness - memmove(&_naluCompleteness[positionsToShift], - &_naluCompleteness[0], - numOfPacketsToMove * sizeof(WebRtc_UWord8)); - memset(&_naluCompleteness[0], kNaluUnset, - positionsToShift * sizeof(WebRtc_UWord8)); - - _highestPacketIndex += positionsToShift; - _lowSeqNum = packet.seqNum; - packetIndex = 0; // (seqNum - _lowSeqNum) = 0 - } - } // if (_lowSeqNum > seqNum) - - // sanity - if (packetIndex >= kMaxPacketsInJitterBuffer ) - { - return -1; - } - if (packetIndex < 0 ) - { - return -1; - } - - // Check for duplicate packets - if (_packetSizeBytes[packetIndex] != 0) - { - // We have already received a packet with this seq number, ignore it. - return -2; - } - - // update highest packet index - _highestPacketIndex = packetIndex > _highestPacketIndex ? - packetIndex :_highestPacketIndex; - - return InsertBuffer(ptrStartOfLayer, packetIndex, packet); -} - - -WebRtc_Word32 -VCMSessionInfo::InformOfEmptyPacket(const WebRtc_UWord16 seqNum) -{ - // Empty packets may be FEC or filler packets. They are sequential and - // follow the data packets, therefore, we should only keep track of the high - // and low sequence numbers and may assume that the packets in between are - // empty packets belonging to the same frame (timestamp). - - if (_emptySeqNumLow == -1 && _emptySeqNumHigh == -1) - { - _emptySeqNumLow = seqNum; - _emptySeqNumHigh = seqNum; - } - else - { - if (seqNum > _emptySeqNumHigh) - { - // This packet's seq num is higher than previously highest seq num; - // normal case if we have a wrap, only update with wrapped values - if (!(_emptySeqNumHigh < 0x00ff && seqNum > 0xff00)) - { - _emptySeqNumHigh = seqNum; - } - } - else if (_emptySeqNumHigh > 0xff00 && seqNum < 0x00ff) - { - // wrap - _emptySeqNumHigh = seqNum; - } - if (_emptySeqNumLow < 0x00ff && seqNum > 0xff00) - { - // negative wrap - if (seqNum - 0x10000 - _emptySeqNumLow < 0) - { - _emptySeqNumLow = seqNum; - } - } - } - return 0; -} - -WebRtc_UWord32 -VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer, - VideoCodecType codec) -{ - WebRtc_UWord32 currentPacketOffset = 0; - WebRtc_UWord32 length = GetSessionLength(); - WebRtc_UWord32 idSum = 0; - WebRtc_UWord32 realDataBytes = 0; - if (length == 0) - { - return length; - } - bool previousLost = false; - for (int i = 0; i <= _highestPacketIndex; i++) - { - if (_ORwithPrevByte[i]) - { - if (currentPacketOffset > 0) - { - WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer + - currentPacketOffset; - - if (_packetSizeBytes[i-1] == 0 || previousLost) - { - // It is be better to throw away this packet if we are - // missing the previous packet. - memset(ptrFirstByte, 0, _packetSizeBytes[i]); - previousLost = true; - } - else if (_packetSizeBytes[i] > 0) // Ignore if empty packet - { - // Glue with previous byte - // Move everything from [this packet start + 1, - // end of buffer] one byte to the left - WebRtc_UWord8* ptrPrevByte = ptrFirstByte - 1; - *ptrPrevByte = (*ptrPrevByte) | (*ptrFirstByte); - WebRtc_UWord32 lengthToEnd = length - - (currentPacketOffset + 1); - memmove((void*)ptrFirstByte, (void*)(ptrFirstByte + 1), - lengthToEnd); - _packetSizeBytes[i]--; - length--; - previousLost = false; - realDataBytes += _packetSizeBytes[i]; - } - } - else - { - memset(ptrStartOfLayer, 0, _packetSizeBytes[i]); - previousLost = true; - } - } - else if (_packetSizeBytes[i] == 0 && codec == kVideoCodecH263) - { - WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer + currentPacketOffset; - memmove(ptrFirstByte + 10, ptrFirstByte, - length - currentPacketOffset); - memset(ptrFirstByte, 0, 10); - _packetSizeBytes[i] = 10; - length += _packetSizeBytes[i]; - previousLost = true; - } - else - { - realDataBytes += _packetSizeBytes[i]; - previousLost = false; - } - currentPacketOffset += _packetSizeBytes[i]; - } - if (realDataBytes == 0) - { - // Drop the frame since all it contains are zeros - length = 0; - memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); - } - return length; -} - -} diff --git a/modules/video_coding/main/source/session_info.h b/modules/video_coding/main/source/session_info.h deleted file mode 100644 index ff71defe5..000000000 --- a/modules/video_coding/main/source/session_info.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_ -#define WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_ - -#include "typedefs.h" -#include "module_common_types.h" -#include "packet.h" - -namespace webrtc -{ - -class VCMSessionInfo -{ -public: - VCMSessionInfo(); - virtual ~VCMSessionInfo(); - - VCMSessionInfo(const VCMSessionInfo& rhs); - - WebRtc_Word32 ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 numberOfSeqNum); - // Hybrid version: Zero out seq num for NACK list - // apply a score based on the packet location and the external rttScore - WebRtc_Word32 ZeroOutSeqNumHybrid(WebRtc_Word32* list, - WebRtc_Word32 numberOfSeqNum, - float rttScore); - virtual void Reset(); - - WebRtc_Word64 InsertPacket(const VCMPacket& packet, WebRtc_UWord8* ptrStartOfLayer); - WebRtc_Word32 InformOfEmptyPacket(const WebRtc_UWord16 seqNum); - - virtual bool IsSessionComplete(); - WebRtc_UWord32 MakeSessionDecodable(WebRtc_UWord8* ptrStartOfLayer); - - WebRtc_UWord32 GetSessionLength(); - bool HaveLastPacket(); - void ForceSetHaveLastPacket(); - bool IsRetransmitted(); - webrtc::FrameType FrameType() const { return _frameType; } - - virtual WebRtc_Word32 GetHighestPacketIndex(); - virtual void UpdatePacketSize(WebRtc_Word32 packetIndex, WebRtc_UWord32 length); - - void SetStartSeqNumber(WebRtc_UWord16 seqNumber); - - bool HaveStartSeqNumber(); - - WebRtc_Word32 GetLowSeqNum() const; - // returns highest seqNum, media or empty - WebRtc_Word32 GetHighSeqNum() const; - - WebRtc_UWord32 PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer, VideoCodecType codec); - - void SetPreviousFrameLoss() { _previousFrameLoss = true; } - bool PreviousFrameLoss() const { return _previousFrameLoss; } - -protected: - WebRtc_UWord32 InsertBuffer(WebRtc_UWord8* ptrStartOfLayer, - WebRtc_Word32 packetIndex, - const VCMPacket& packet); - void FindNaluBorder(WebRtc_Word32 packetIndex, - WebRtc_Word32& startIndex, - WebRtc_Word32& endIndex); - WebRtc_UWord32 DeletePackets(WebRtc_UWord8* ptrStartOfLayer, - WebRtc_Word32 startIndex, - WebRtc_Word32 endIndex); - void UpdateCompleteSession(); - - bool _haveFirstPacket; // If we have inserted the first packet into this frame - bool _markerBit; // If we have inserted a packet with markerbit into this frame - bool _sessionNACK; // If this session has been NACKed by JB - bool _completeSession; - webrtc::FrameType _frameType; - bool _previousFrameLoss; - - WebRtc_Word32 _lowSeqNum; // Lowest packet sequence number in a session - WebRtc_Word32 _highSeqNum; // Highest packet sequence number in a session - - // Highest packet index in this frame - WebRtc_UWord16 _highestPacketIndex; - // Length of packet (used for reordering) - WebRtc_UWord32 _packetSizeBytes[kMaxPacketsInJitterBuffer]; - // Completeness of packets. Used for deciding if the frame is decodable. - WebRtc_UWord8 _naluCompleteness[kMaxPacketsInJitterBuffer]; - WebRtc_Word32 _emptySeqNumLow; - WebRtc_Word32 _emptySeqNumHigh; - // Store the sequence number that marks the last media packet - WebRtc_Word32 _markerSeqNum; - bool _ORwithPrevByte[kMaxPacketsInJitterBuffer]; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_SESSION_INFO_H_ diff --git a/modules/video_coding/main/source/tick_time.h b/modules/video_coding/main/source/tick_time.h deleted file mode 100644 index 47ac9f412..000000000 --- a/modules/video_coding/main/source/tick_time.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_ - -#include "tick_util.h" - -#include - -namespace webrtc -{ - -//#define TICK_TIME_DEBUG - -class VCMTickTime : public TickTime -{ -#ifdef TICK_TIME_DEBUG -public: - /* - * Get current time - */ - static TickTime Now() { assert(false); }; - - /* - * Get time in milli seconds - */ - static WebRtc_Word64 MillisecondTimestamp() { return _timeNowDebug; }; - - /* - * Get time in micro seconds - */ - static WebRtc_Word64 MicrosecondTimestamp() { return _timeNowDebug * 1000LL; }; - - static void IncrementDebugClock() { _timeNowDebug++; }; - -private: - static WebRtc_Word64 _timeNowDebug; - -#else -public: - static void IncrementDebugClock() { assert(false); }; -#endif -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TICK_TIME_H_ diff --git a/modules/video_coding/main/source/timestamp_extrapolator.cc b/modules/video_coding/main/source/timestamp_extrapolator.cc deleted file mode 100644 index 5f589e162..000000000 --- a/modules/video_coding/main/source/timestamp_extrapolator.cc +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "internal_defines.h" -#include "timestamp_extrapolator.h" -#include "tick_time.h" -#include "trace.h" - -namespace webrtc { - -VCMTimestampExtrapolator::VCMTimestampExtrapolator(WebRtc_Word32 vcmId, WebRtc_Word32 id) -: -_rwLock(*RWLockWrapper::CreateRWLock()), -_vcmId(vcmId), -_id(id), -_startMs(0), -_firstTimestamp(0), -_wrapArounds(0), -_prevTs90khz(0), -_lambda(1), -_firstAfterReset(true), -_packetCount(0), -_startUpFilterDelayInPackets(2), -_detectorAccumulatorPos(0), -_detectorAccumulatorNeg(0), -_alarmThreshold(60e3), -_accDrift(6600), // in timestamp ticks, i.e. 15 ms -_accMaxError(7000), -_P11(1e10) -{ - Reset(VCMTickTime::MillisecondTimestamp()); -} - -VCMTimestampExtrapolator::~VCMTimestampExtrapolator() -{ - delete &_rwLock; -} - -void -VCMTimestampExtrapolator::Reset(const WebRtc_Word64 nowMs /* = -1 */) -{ - WriteLockScoped wl(_rwLock); - if (nowMs > -1) - { - _startMs = nowMs; - } - else - { - _startMs = VCMTickTime::MillisecondTimestamp(); - } - _prevMs = _startMs; - _firstTimestamp = 0; - _w[0] = 90.0; - _w[1] = 0; - _P[0][0] = 1; - _P[1][1] = _P11; - _P[0][1] = _P[1][0] = 0; - _firstAfterReset = true; - _prevTs90khz = 0; - _wrapArounds = 0; - _packetCount = 0; - _detectorAccumulatorPos = 0; - _detectorAccumulatorNeg = 0; -} - -void -VCMTimestampExtrapolator::Update(WebRtc_Word64 tMs, WebRtc_UWord32 ts90khz, bool trace) -{ - - _rwLock.AcquireLockExclusive(); - if (tMs - _prevMs > 10e3) - { - // Ten seconds without a complete frame. - // Reset the extrapolator - _rwLock.ReleaseLockExclusive(); - Reset(); - _rwLock.AcquireLockExclusive(); - } - else - { - _prevMs = tMs; - } - - // Remove offset to prevent badly scaled matrices - tMs -= _startMs; - - WebRtc_Word32 prevWrapArounds = _wrapArounds; - CheckForWrapArounds(ts90khz); - WebRtc_Word32 wrapAroundsSincePrev = _wrapArounds - prevWrapArounds; - - if (wrapAroundsSincePrev == 0 && ts90khz < _prevTs90khz) - { - _rwLock.ReleaseLockExclusive(); - return; - } - - if (_firstAfterReset) - { - // Make an initial guess of the offset, - // should be almost correct since tMs - _startMs - // should about zero at this time. - _w[1] = -_w[0] * tMs; - _firstTimestamp = ts90khz; - _firstAfterReset = false; - } - - // Compensate for wraparounds by changing the line offset - _w[1] = _w[1] - wrapAroundsSincePrev * ((static_cast(1)<<32) - 1); - - double residual = (static_cast(ts90khz) - _firstTimestamp) - static_cast(tMs) * _w[0] - _w[1]; - if (DelayChangeDetection(residual, trace) && - _packetCount >= _startUpFilterDelayInPackets) - { - // A sudden change of average network delay has been detected. - // Force the filter to adjust its offset parameter by changing - // the offset uncertainty. Don't do this during startup. - _P[1][1] = _P11; - } - //T = [t(k) 1]'; - //that = T'*w; - //K = P*T/(lambda + T'*P*T); - double K[2]; - K[0] = _P[0][0] * tMs + _P[0][1]; - K[1] = _P[1][0] * tMs + _P[1][1]; - double TPT = _lambda + tMs * K[0] + K[1]; - K[0] /= TPT; - K[1] /= TPT; - //w = w + K*(ts(k) - that); - _w[0] = _w[0] + K[0] * residual; - _w[1] = _w[1] + K[1] * residual; - //P = 1/lambda*(P - K*T'*P); - double p00 = 1 / _lambda * (_P[0][0] - (K[0] * tMs * _P[0][0] + K[0] * _P[1][0])); - double p01 = 1 / _lambda * (_P[0][1] - (K[0] * tMs * _P[0][1] + K[0] * _P[1][1])); - _P[1][0] = 1 / _lambda * (_P[1][0] - (K[1] * tMs * _P[0][0] + K[1] * _P[1][0])); - _P[1][1] = 1 / _lambda * (_P[1][1] - (K[1] * tMs * _P[0][1] + K[1] * _P[1][1])); - _P[0][0] = p00; - _P[0][1] = p01; - if (_packetCount < _startUpFilterDelayInPackets) - { - _packetCount++; - } - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "w[0]=%f w[1]=%f ts=%u tMs=%u", _w[0], _w[1], ts90khz, tMs); - } - _rwLock.ReleaseLockExclusive(); -} - -WebRtc_UWord32 -VCMTimestampExtrapolator::ExtrapolateTimestamp(WebRtc_Word64 tMs) const -{ - ReadLockScoped rl(_rwLock); - WebRtc_UWord32 timestamp = 0; - if (_packetCount == 0) - { - timestamp = 0; - } - else if (_packetCount < _startUpFilterDelayInPackets) - { - timestamp = static_cast(90.0 * (tMs - _prevMs) + _prevTs90khz + 0.5); - } - else - { - timestamp = static_cast(_w[0] * (tMs - _startMs) + _w[1] + _firstTimestamp + 0.5); - } - return timestamp; -} - -WebRtc_Word64 -VCMTimestampExtrapolator::ExtrapolateLocalTime(WebRtc_UWord32 timestamp90khz) const -{ - ReadLockScoped rl(_rwLock); - WebRtc_Word64 localTimeMs = 0; - if (_packetCount == 0) - { - localTimeMs = -1; - } - else if (_packetCount < _startUpFilterDelayInPackets) - { - localTimeMs = _prevMs + static_cast(static_cast(timestamp90khz - _prevTs90khz) / 90.0 + 0.5); - } - else - { - if (_w[0] < 1e-3) - { - localTimeMs = _startMs; - } - else - { - double timestampDiff = static_cast(timestamp90khz) - static_cast(_firstTimestamp); - localTimeMs = static_cast(static_cast(_startMs) + (timestampDiff - _w[1]) / _w[0] + 0.5); - } - } - return localTimeMs; -} - -// Investigates if the timestamp clock has overflowed since the last timestamp and -// keeps track of the number of wrap arounds since reset. -void -VCMTimestampExtrapolator::CheckForWrapArounds(WebRtc_UWord32 ts90khz) -{ - if (_prevTs90khz == 0) - { - _prevTs90khz = ts90khz; - return; - } - if (ts90khz < _prevTs90khz) - { - // This difference will probably be less than -2^31 if we have had a wrap around - // (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is casted to a Word32, - // it should be positive. - if (static_cast(ts90khz - _prevTs90khz) > 0) - { - // Forward wrap around - _wrapArounds++; - } - } - // This difference will probably be less than -2^31 if we have had a backward wrap around. - // Since it is casted to a Word32, it should be positive. - else if (static_cast(_prevTs90khz - ts90khz) > 0) - { - // Backward wrap around - _wrapArounds--; - } - _prevTs90khz = ts90khz; -} - -bool -VCMTimestampExtrapolator::DelayChangeDetection(double error, bool trace) -{ - // CUSUM detection of sudden delay changes - error = (error > 0) ? VCM_MIN(error, _accMaxError) : VCM_MAX(error, -_accMaxError); - _detectorAccumulatorPos = VCM_MAX(_detectorAccumulatorPos + error - _accDrift, (double)0); - _detectorAccumulatorNeg = VCM_MIN(_detectorAccumulatorNeg + error + _accDrift, (double)0); - if (_detectorAccumulatorPos > _alarmThreshold || _detectorAccumulatorNeg < -_alarmThreshold) - { - // Alarm - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=1", _detectorAccumulatorPos, _detectorAccumulatorNeg); - } - _detectorAccumulatorPos = _detectorAccumulatorNeg = 0; - return true; - } - if (trace) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _id), "g1=%f g2=%f alarm=0", _detectorAccumulatorPos, _detectorAccumulatorNeg); - } - return false; -} - -} diff --git a/modules/video_coding/main/source/timestamp_extrapolator.h b/modules/video_coding/main/source/timestamp_extrapolator.h deleted file mode 100644 index a18650422..000000000 --- a/modules/video_coding/main/source/timestamp_extrapolator.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ - -#include "typedefs.h" -#include "rw_lock_wrapper.h" - -namespace webrtc -{ - -class VCMTimestampExtrapolator -{ -public: - VCMTimestampExtrapolator(WebRtc_Word32 vcmId = 0, WebRtc_Word32 receiverId = 0); - ~VCMTimestampExtrapolator(); - void Update(WebRtc_Word64 tMs, WebRtc_UWord32 ts90khz, bool trace = true); - WebRtc_UWord32 ExtrapolateTimestamp(WebRtc_Word64 tMs) const; - WebRtc_Word64 ExtrapolateLocalTime(WebRtc_UWord32 timestamp90khz) const; - void Reset(WebRtc_Word64 nowMs = -1); - -private: - void CheckForWrapArounds(WebRtc_UWord32 ts90khz); - bool DelayChangeDetection(double error, bool trace = true); - RWLockWrapper& _rwLock; - WebRtc_Word32 _vcmId; - WebRtc_Word32 _id; - bool _trace; - double _w[2]; - double _P[2][2]; - WebRtc_Word64 _startMs; - WebRtc_Word64 _prevMs; - WebRtc_UWord32 _firstTimestamp; - WebRtc_Word32 _wrapArounds; - WebRtc_UWord32 _prevTs90khz; - const double _lambda; - bool _firstAfterReset; - WebRtc_UWord32 _packetCount; - const WebRtc_UWord32 _startUpFilterDelayInPackets; - - double _detectorAccumulatorPos; - double _detectorAccumulatorNeg; - const double _alarmThreshold; - const double _accDrift; - const double _accMaxError; - const double _P11; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_EXTRAPOLATOR_H_ diff --git a/modules/video_coding/main/source/timestamp_map.cc b/modules/video_coding/main/source/timestamp_map.cc deleted file mode 100644 index f19819be0..000000000 --- a/modules/video_coding/main/source/timestamp_map.cc +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "timestamp_map.h" -#include -#include - -namespace webrtc { - -// Constructor. Optional parameter specifies maximum number of -// coexisting timers. -VCMTimestampMap::VCMTimestampMap(WebRtc_Word32 length): - _nextAddIx(0), - _nextPopIx(0) -{ - if (length <= 0) - { - // default - length = 10; - } - - _map = new VCMTimestampDataTuple[length]; - _length = length; -} - -// Destructor. -VCMTimestampMap::~VCMTimestampMap() -{ - delete [] _map; -} - -// Empty the list of timers. -void -VCMTimestampMap::Reset() -{ - _nextAddIx = 0; - _nextPopIx = 0; -} - -WebRtc_Word32 -VCMTimestampMap::Add(WebRtc_UWord32 timestamp, void* data) -{ - _map[_nextAddIx].timestamp = timestamp; - _map[_nextAddIx].data = data; - _nextAddIx = (_nextAddIx + 1) % _length; - - if (_nextAddIx == _nextPopIx) - { - // Circular list full; forget oldest entry - _nextPopIx = (_nextPopIx + 1) % _length; - return -1; - } - return 0; -} - -void* -VCMTimestampMap::Pop(WebRtc_UWord32 timestamp) -{ - while (!IsEmpty()) - { - if (_map[_nextPopIx].timestamp == timestamp) - { - // found start time for this timestamp - void* data = _map[_nextPopIx].data; - _map[_nextPopIx].data = NULL; - _nextPopIx = (_nextPopIx + 1) % _length; - return data; - } - else if (_map[_nextPopIx].timestamp > timestamp) - { - // the timestamp we are looking for is not in the list - assert(_nextPopIx < _length && _nextPopIx >= 0); - return NULL; - } - - // not in this position, check next (and forget this position) - _nextPopIx = (_nextPopIx + 1) % _length; - } - - // could not find matching timestamp in list - assert(_nextPopIx < _length && _nextPopIx >= 0); - return NULL; -} - -// Check if no timers are currently running -bool -VCMTimestampMap::IsEmpty() const -{ - return (_nextAddIx == _nextPopIx); -} - -} diff --git a/modules/video_coding/main/source/timestamp_map.h b/modules/video_coding/main/source/timestamp_map.h deleted file mode 100644 index fd532bcf4..000000000 --- a/modules/video_coding/main/source/timestamp_map.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_ - -#include "typedefs.h" - -namespace webrtc -{ - -struct VCMTimestampDataTuple -{ - WebRtc_UWord32 timestamp; - void* data; -}; - -class VCMTimestampMap -{ -public: - // Constructor. Optional parameter specifies maximum number of - // timestamps in map. - VCMTimestampMap(const WebRtc_Word32 length = 10); - - // Destructor. - ~VCMTimestampMap(); - - // Empty the map - void Reset(); - - WebRtc_Word32 Add(WebRtc_UWord32 timestamp, void* data); - void* Pop(WebRtc_UWord32 timestamp); - -private: - bool IsEmpty() const; - - VCMTimestampDataTuple* _map; - WebRtc_Word32 _nextAddIx; - WebRtc_Word32 _nextPopIx; - WebRtc_Word32 _length; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TIMESTAMP_MAP_H_ diff --git a/modules/video_coding/main/source/timing.cc b/modules/video_coding/main/source/timing.cc deleted file mode 100644 index 67214ec8c..000000000 --- a/modules/video_coding/main/source/timing.cc +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "trace.h" -#include "internal_defines.h" -#include "jitter_buffer_common.h" -#include "timing.h" -#include "timestamp_extrapolator.h" - -namespace webrtc { - -VCMTiming::VCMTiming(WebRtc_Word32 vcmId, WebRtc_Word32 timingId, VCMTiming* masterTiming) -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_vcmId(vcmId), -_timingId(timingId), -_master(false), -_tsExtrapolator(), -_codecTimer(), -_renderDelayMs(kDefaultRenderDelayMs), -_minTotalDelayMs(0), -_requiredDelayMs(0), -_currentDelayMs(0), -_prevFrameTimestamp(0) -{ - if (masterTiming == NULL) - { - _master = true; - _tsExtrapolator = new VCMTimestampExtrapolator(vcmId, timingId); - } - else - { - _tsExtrapolator = masterTiming->_tsExtrapolator; - } -} - -VCMTiming::~VCMTiming() -{ - if (_master) - { - delete _tsExtrapolator; - } - delete &_critSect; -} - -void -VCMTiming::Reset(WebRtc_Word64 nowMs /* = -1 */) -{ - CriticalSectionScoped cs(_critSect); - if (nowMs > -1) - { - _tsExtrapolator->Reset(nowMs); - } - else - { - _tsExtrapolator->Reset(); - } - _codecTimer.Reset(); - _renderDelayMs = kDefaultRenderDelayMs; - _minTotalDelayMs = 0; - _requiredDelayMs = 0; - _currentDelayMs = 0; - _prevFrameTimestamp = 0; -} - -void VCMTiming::ResetDecodeTime() -{ - _codecTimer.Reset(); -} - -void -VCMTiming::SetRenderDelay(WebRtc_UWord32 renderDelayMs) -{ - CriticalSectionScoped cs(_critSect); - _renderDelayMs = renderDelayMs; -} - -void -VCMTiming::SetMinimumTotalDelay(WebRtc_UWord32 minTotalDelayMs) -{ - CriticalSectionScoped cs(_critSect); - _minTotalDelayMs = minTotalDelayMs; -} - -void -VCMTiming::SetRequiredDelay(WebRtc_UWord32 requiredDelayMs) -{ - CriticalSectionScoped cs(_critSect); - if (requiredDelayMs != _requiredDelayMs) - { - if (_master) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Desired jitter buffer level: %u ms", requiredDelayMs); - } - _requiredDelayMs = requiredDelayMs; - } -} - -void VCMTiming::UpdateCurrentDelay(WebRtc_UWord32 frameTimestamp) -{ - CriticalSectionScoped cs(_critSect); - WebRtc_UWord32 targetDelayMs = TargetDelayInternal(); - - // Make sure we try to sync with audio - if (targetDelayMs < _minTotalDelayMs) - { - targetDelayMs = _minTotalDelayMs; - } - - if (_currentDelayMs == 0) - { - // Not initialized, set current delay to target. - _currentDelayMs = targetDelayMs; - } - else if (targetDelayMs != _currentDelayMs) - { - WebRtc_Word64 delayDiffMs = static_cast(targetDelayMs) - - _currentDelayMs; - // Never change the delay with more than 100 ms every second. If we're changing the - // delay in too large steps we will get noticable freezes. By limiting the change we - // can increase the delay in smaller steps, which will be experienced as the video is - // played in slow motion. When lowering the delay the video will be played at a faster - // pace. - WebRtc_Word64 maxChangeMs = 0; - if (frameTimestamp < 0x0000ffff && _prevFrameTimestamp > 0xffff0000) - { - // wrap - maxChangeMs = kDelayMaxChangeMsPerS * (frameTimestamp + - (static_cast(1)<<32) - _prevFrameTimestamp) / 90000; - } - else - { - maxChangeMs = kDelayMaxChangeMsPerS * - (frameTimestamp - _prevFrameTimestamp) / 90000; - } - if (maxChangeMs <= 0) - { - // Any changes less than 1 ms are truncated and - // will be postponed. Negative change will be due - // to reordering and should be ignored. - return; - } - else if (delayDiffMs < -maxChangeMs) - { - delayDiffMs = -maxChangeMs; - } - else if (delayDiffMs > maxChangeMs) - { - delayDiffMs = maxChangeMs; - } - _currentDelayMs = _currentDelayMs + static_cast(delayDiffMs); - } - _prevFrameTimestamp = frameTimestamp; -} - -void VCMTiming::UpdateCurrentDelay(WebRtc_Word64 renderTimeMs, - WebRtc_Word64 actualDecodeTimeMs) -{ - CriticalSectionScoped cs(_critSect); - WebRtc_UWord32 targetDelayMs = TargetDelayInternal(); - // Make sure we try to sync with audio - if (targetDelayMs < _minTotalDelayMs) - { - targetDelayMs = _minTotalDelayMs; - } - WebRtc_Word64 delayedMs = actualDecodeTimeMs - - (renderTimeMs - MaxDecodeTimeMs() - _renderDelayMs); - if (delayedMs < 0) - { - return; - } - else if (_currentDelayMs + delayedMs <= targetDelayMs) - { - _currentDelayMs += static_cast(delayedMs); - } - else - { - _currentDelayMs = targetDelayMs; - } -} - -WebRtc_Word32 -VCMTiming::StopDecodeTimer(WebRtc_UWord32 timeStamp, - WebRtc_Word64 startTimeMs, - WebRtc_Word64 nowMs) -{ - CriticalSectionScoped cs(_critSect); - const WebRtc_Word32 maxDecTime = MaxDecodeTimeMs(); - WebRtc_Word32 timeDiffMs = _codecTimer.StopTimer(startTimeMs, nowMs); - if (timeDiffMs < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Codec timer error: %d", timeDiffMs); - return timeDiffMs; - } - - if (_master) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Frame decoded: timeStamp=%u decTime=%d maxDecTime=%u, at %u", - timeStamp, timeDiffMs, maxDecTime, MaskWord64ToUWord32(nowMs)); - } - return 0; -} - -void -VCMTiming::IncomingTimestamp(WebRtc_UWord32 timeStamp, WebRtc_Word64 nowMs) -{ - CriticalSectionScoped cs(_critSect); - _tsExtrapolator->Update(nowMs, timeStamp, _master); -} - -WebRtc_Word64 -VCMTiming::RenderTimeMs(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const -{ - CriticalSectionScoped cs(_critSect); - const WebRtc_Word64 renderTimeMs = RenderTimeMsInternal(frameTimestamp, nowMs); - if (renderTimeMs < 0) - { - return renderTimeMs; - } - if (_master) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Render frame %u at %u. Render delay %u, required delay %u," - " max decode time %u, min total delay %u", - frameTimestamp, MaskWord64ToUWord32(renderTimeMs), _renderDelayMs, - _requiredDelayMs, MaxDecodeTimeMs(),_minTotalDelayMs); - } - return renderTimeMs; -} - -WebRtc_Word64 -VCMTiming::RenderTimeMsInternal(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const -{ - WebRtc_Word64 estimatedCompleteTimeMs = - _tsExtrapolator->ExtrapolateLocalTime(frameTimestamp); - if (estimatedCompleteTimeMs - nowMs > kMaxVideoDelayMs) - { - if (_master) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Timestamp arrived 2 seconds early, reset statistics", - frameTimestamp, estimatedCompleteTimeMs); - } - return -1; - } - if (_master) - { - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "ExtrapolateLocalTime(%u)=%u ms", - frameTimestamp, MaskWord64ToUWord32(estimatedCompleteTimeMs)); - } - if (estimatedCompleteTimeMs == -1) - { - estimatedCompleteTimeMs = nowMs; - } - - return estimatedCompleteTimeMs + _currentDelayMs; -} - -// Must be called from inside a critical section -WebRtc_Word32 -VCMTiming::MaxDecodeTimeMs(FrameType frameType /*= kVideoFrameDelta*/) const -{ - const WebRtc_Word32 decodeTimeMs = _codecTimer.RequiredDecodeTimeMs(frameType); - - if (decodeTimeMs < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_vcmId, _timingId), - "Negative maximum decode time: %d", decodeTimeMs); - return -1; - } - return decodeTimeMs; -} - -WebRtc_UWord32 -VCMTiming::MaxWaitingTime(WebRtc_Word64 renderTimeMs, WebRtc_Word64 nowMs) const -{ - CriticalSectionScoped cs(_critSect); - - const WebRtc_Word64 maxWaitTimeMs = renderTimeMs - nowMs - - MaxDecodeTimeMs() - _renderDelayMs; - - if (maxWaitTimeMs < 0) - { - return 0; - } - return static_cast(maxWaitTimeMs); -} - -bool -VCMTiming::EnoughTimeToDecode(WebRtc_UWord32 availableProcessingTimeMs) const -{ - CriticalSectionScoped cs(_critSect); - WebRtc_Word32 maxDecodeTimeMs = MaxDecodeTimeMs(); - if (maxDecodeTimeMs < 0) - { - // Haven't decoded any frames yet, try decoding one to get an estimate - // of the decode time. - return true; - } - else if (maxDecodeTimeMs == 0) - { - // Decode time is less than 1, set to 1 for now since - // we don't have any better precision. Count ticks later? - maxDecodeTimeMs = 1; - } - return static_cast(availableProcessingTimeMs) - maxDecodeTimeMs > 0; -} - -WebRtc_UWord32 -VCMTiming::TargetVideoDelay() const -{ - CriticalSectionScoped cs(_critSect); - return TargetDelayInternal(); -} - -WebRtc_UWord32 -VCMTiming::TargetDelayInternal() const -{ - return _requiredDelayMs + MaxDecodeTimeMs() + _renderDelayMs; -} - -} diff --git a/modules/video_coding/main/source/timing.h b/modules/video_coding/main/source/timing.h deleted file mode 100644 index 66d3ecebc..000000000 --- a/modules/video_coding/main/source/timing.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TIMING_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TIMING_H_ - -#include "typedefs.h" -#include "critical_section_wrapper.h" -#include "codec_timer.h" - -namespace webrtc -{ - -class VCMTimestampExtrapolator; - -class VCMTiming -{ -public: - // The primary timing component should be passed - // if this is the dual timing component. - VCMTiming(WebRtc_Word32 vcmId = 0, - WebRtc_Word32 timingId = 0, - VCMTiming* masterTiming = NULL); - ~VCMTiming(); - - // Resets the timing to the initial state. - void Reset(WebRtc_Word64 nowMs = -1); - void ResetDecodeTime(); - - // The amount of time needed to render an image. Defaults to 10 ms. - void SetRenderDelay(WebRtc_UWord32 renderDelayMs); - - // The minimum time the video must be delayed on the receiver to - // get the desired jitter buffer level. - void SetRequiredDelay(WebRtc_UWord32 requiredDelayMs); - - // Minimum total delay required to sync video with audio. - void SetMinimumTotalDelay(WebRtc_UWord32 minTotalDelayMs); - - // Increases or decreases the current delay to get closer to the target delay. - // Calculates how long it has been since the previous call to this function, - // and increases/decreases the delay in proportion to the time difference. - void UpdateCurrentDelay(WebRtc_UWord32 frameTimestamp); - - // Increases or decreases the current delay to get closer to the target delay. - // Given the actual decode time in ms and the render time in ms for a frame, this - // function calculates how late the frame is and increases the delay accordingly. - void UpdateCurrentDelay(WebRtc_Word64 renderTimeMs, WebRtc_Word64 actualDecodeTimeMs); - - // Stops the decoder timer, should be called when the decoder returns a frame - // or when the decoded frame callback is called. - WebRtc_Word32 StopDecodeTimer(WebRtc_UWord32 timeStamp, - WebRtc_Word64 startTimeMs, - WebRtc_Word64 nowMs); - - // Used to report that a frame is passed to decoding. Updates the timestamp filter - // which is used to map between timestamps and receiver system time. - void IncomingTimestamp(WebRtc_UWord32 timeStamp, WebRtc_Word64 lastPacketTimeMs); - - // Returns the receiver system time when the frame with timestamp frameTimestamp - // should be rendered, assuming that the system time currently is nowMs. - WebRtc_Word64 RenderTimeMs(WebRtc_UWord32 frameTimestamp, WebRtc_Word64 nowMs) const; - - // Returns the maximum time in ms that we can wait for a frame to become complete - // before we must pass it to the decoder. - WebRtc_UWord32 MaxWaitingTime(WebRtc_Word64 renderTimeMs, WebRtc_Word64 nowMs) const; - - // Returns the current target delay which is required delay + decode time + render - // delay. - WebRtc_UWord32 TargetVideoDelay() const; - - // Calculates whether or not there is enough time to decode a frame given a - // certain amount of processing time. - bool EnoughTimeToDecode(WebRtc_UWord32 availableProcessingTimeMs) const; - - enum { kDefaultRenderDelayMs = 10 }; - enum { kDelayMaxChangeMsPerS = 100 }; - -protected: - WebRtc_Word32 MaxDecodeTimeMs(FrameType frameType = kVideoFrameDelta) const; - WebRtc_Word64 RenderTimeMsInternal(WebRtc_UWord32 frameTimestamp, - WebRtc_Word64 nowMs) const; - WebRtc_UWord32 TargetDelayInternal() const; - -private: - CriticalSectionWrapper& _critSect; - WebRtc_Word32 _vcmId; - WebRtc_Word32 _timingId; - bool _master; - VCMTimestampExtrapolator* _tsExtrapolator; - VCMCodecTimer _codecTimer; - WebRtc_UWord32 _renderDelayMs; - WebRtc_UWord32 _minTotalDelayMs; - WebRtc_UWord32 _requiredDelayMs; - WebRtc_UWord32 _currentDelayMs; - WebRtc_UWord32 _prevFrameTimestamp; - WebRtc_Word64 _startStoragePlaybackMs; - WebRtc_Word64 _firstStoredRenderTimeMs; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_TIMING_H_ diff --git a/modules/video_coding/main/source/video_coding.gyp b/modules/video_coding/main/source/video_coding.gyp deleted file mode 100644 index f2431f4e0..000000000 --- a/modules/video_coding/main/source/video_coding.gyp +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'webrtc_video_coding', - 'type': '<(library)', - 'dependencies': [ - '../../codecs/i420/main/source/i420.gyp:webrtc_i420', - '../../codecs/vp8/main/source/vp8.gyp:webrtc_vp8', - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - ], - 'include_dirs': [ - '../interface', - '../../../interface', - '../../codecs/interface', - '../../../../common_video/interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../codecs/interface', - ], - }, - 'sources': [ - # interfaces - '../interface/video_coding.h', - '../interface/video_coding_defines.h', - - # headers - 'codec_database.h', - 'codec_timer.h', - 'content_metrics_processing.h', - 'encoded_frame.h', - 'er_tables_xor.h', - 'event.h', - 'exp_filter.h', - 'fec_tables_xor.h', - 'frame_buffer.h', - 'frame_dropper.h', - 'frame_list.h', - 'generic_decoder.h', - 'generic_encoder.h', - 'inter_frame_delay.h', - 'internal_defines.h', - 'jitter_buffer_common.h', - 'jitter_buffer.h', - 'jitter_estimator.h', - 'media_opt_util.h', - 'media_optimization.h', - 'nack_fec_tables.h', - 'packet.h', - 'qm_select_data.h', - 'qm_select.h', - 'receiver.h', - 'rtt_filter.h', - 'session_info.h', - 'tick_time.h', - 'timestamp_extrapolator.h', - 'timestamp_map.h', - 'timing.h', - 'video_coding_impl.h', - - # sources - 'codec_database.cc', - 'codec_timer.cc', - 'content_metrics_processing.cc', - 'encoded_frame.cc', - 'exp_filter.cc', - 'frame_buffer.cc', - 'frame_dropper.cc', - 'frame_list.cc', - 'generic_decoder.cc', - 'generic_encoder.cc', - 'inter_frame_delay.cc', - 'jitter_buffer.cc', - 'jitter_estimator.cc', - 'media_opt_util.cc', - 'media_optimization.cc', - 'packet.cc', - 'qm_select.cc', - 'receiver.cc', - 'rtt_filter.cc', - 'session_info.cc', - 'timestamp_extrapolator.cc', - 'timestamp_map.cc', - 'timing.cc', - 'video_coding_impl.cc', - ], # source - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_coding/main/source/video_coding_impl.cc b/modules/video_coding/main/source/video_coding_impl.cc deleted file mode 100644 index 48d2947af..000000000 --- a/modules/video_coding/main/source/video_coding_impl.cc +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_coding_impl.h" -#include "common_types.h" -#include "encoded_frame.h" -#include "jitter_buffer.h" -#include "packet.h" -#include "trace.h" -#include "video_codec_interface.h" - -namespace webrtc -{ - -//#define DEBUG_DECODER_BIT_STREAM -//#define DEBUG_ENCODER_INPUT - -WebRtc_UWord32 -VCMProcessTimer::Period() const -{ - return _periodMs; -} - -WebRtc_UWord32 -VCMProcessTimer::TimeUntilProcess() const -{ - return static_cast(VCM_MAX(static_cast(_periodMs) - - (VCMTickTime::MillisecondTimestamp() - _latestMs), 0)); -} - -void -VCMProcessTimer::Processed() -{ - _latestMs = VCMTickTime::MillisecondTimestamp(); -} - -VideoCodingModuleImpl::VideoCodingModuleImpl(const WebRtc_Word32 id) -: -_id(id), -_receiveCritSect(*CriticalSectionWrapper::CreateCriticalSection()), -_receiverInited(false), -_timing(id, 1), -_dualTiming(id, 2, &_timing), -_receiver(_timing, id, 1), -_dualReceiver(_dualTiming, id, 2, false), -_decodedFrameCallback(_timing), -_dualDecodedFrameCallback(_dualTiming), -_frameTypeCallback(NULL), -_frameStorageCallback(NULL), -_receiveStatsCallback(NULL), -_packetRequestCallback(NULL), -_decoder(NULL), -_dualDecoder(NULL), -_bitStreamBeforeDecoder(NULL), -_frameFromFile(), -_keyRequestMode(kKeyOnError), -_scheduleKeyRequest(false), - -_sendCritSect(*CriticalSectionWrapper::CreateCriticalSection()), -_encoder(), -_encodedFrameCallback(), -_nextFrameType(kVideoFrameDelta), -_mediaOpt(id), -_sendCodecType(kVideoCodecUnknown), -_sendStatsCallback(NULL), -_encoderInputFile(NULL), - -_codecDataBase(id), -_receiveStatsTimer(1000), -_sendStatsTimer(1000), -_retransmissionTimer(10), -_keyRequestTimer(500) -{ -#ifdef DEBUG_DECODER_BIT_STREAM - _bitStreamBeforeDecoder = fopen("decoderBitStream.bit", "wb"); -#endif -#ifdef DEBUG_ENCODER_INPUT - _encoderInputFile = fopen("encoderInput.yuv", "wb"); -#endif -} - -VideoCodingModuleImpl::~VideoCodingModuleImpl() -{ - if (_dualDecoder != NULL) - { - _codecDataBase.ReleaseDecoder(_dualDecoder); - } - delete &_receiveCritSect; - delete &_sendCritSect; -#ifdef DEBUG_DECODER_BIT_STREAM - fclose(_bitStreamBeforeDecoder); -#endif -#ifdef DEBUG_ENCODER_INPUT - fclose(_encoderInputFile); -#endif -} - -VideoCodingModule* -VideoCodingModule::Create(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(id), - "VideoCodingModule::Create()"); - return new VideoCodingModuleImpl(id); -} - -void -VideoCodingModule::Destroy(VideoCodingModule* module) -{ - if (module != NULL) - { - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, - static_cast(module)->Id(), - "VideoCodingModule::Destroy()"); - delete static_cast(module); - } -} - -WebRtc_Word32 -VideoCodingModuleImpl::Process() -{ - WebRtc_Word32 returnValue = VCM_OK; - - // Receive-side statistics - if (_receiveStatsTimer.TimeUntilProcess() == 0) - { - _receiveStatsTimer.Processed(); - if (_receiveStatsCallback != NULL) - { - WebRtc_UWord32 bitRate; - WebRtc_UWord32 frameRate; - const WebRtc_Word32 ret = _receiver.ReceiveStatistics(bitRate, frameRate); - if (ret == 0) - { - _receiveStatsCallback->ReceiveStatistics(bitRate, frameRate); - } - else if (returnValue == VCM_OK) - { - returnValue = ret; - } - } - } - - // Send-side statistics - if (_sendStatsTimer.TimeUntilProcess() == 0) - { - _sendStatsTimer.Processed(); - if (_sendStatsCallback != NULL) - { - WebRtc_UWord32 bitRate; - WebRtc_UWord32 frameRate; - { - CriticalSectionScoped cs(_sendCritSect); - bitRate = static_cast(_mediaOpt.SentBitRate() + 0.5f); - frameRate = static_cast(_mediaOpt.SentFrameRate() + 0.5f); - } - _sendStatsCallback->SendStatistics(bitRate, frameRate); - } - } - - // Packet retransmission requests - if (_retransmissionTimer.TimeUntilProcess() == 0) - { - _retransmissionTimer.Processed(); - if (_packetRequestCallback != NULL) - { - WebRtc_UWord16 nackList[kNackHistoryLength]; - WebRtc_UWord16 length = kNackHistoryLength; - const WebRtc_Word32 ret = NackList(nackList, length); - if (ret != VCM_OK && returnValue == VCM_OK) - { - returnValue = ret; - } - if (length > 0) - { - _packetRequestCallback->ResendPackets(nackList, length); - } - } - } - - // Key frame requests - if (_keyRequestTimer.TimeUntilProcess() == 0) - { - _keyRequestTimer.Processed(); - if (_scheduleKeyRequest && _frameTypeCallback != NULL) - { - const WebRtc_Word32 ret = RequestKeyFrame(); - if (ret != VCM_OK && returnValue == VCM_OK) - { - returnValue = ret; - } - } - } - - return returnValue; -} - -// Returns version of the module and its components -WebRtc_Word32 -VideoCodingModuleImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Version()"); - if (version == NULL) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id), - "Invalid buffer pointer in argument to Version()"); - return VCM_PARAMETER_ERROR; - } - WebRtc_Word8 ourVersion[] = "VideoCodingModule 1.1.0\n"; - WebRtc_UWord32 ourLength = (WebRtc_UWord32)strlen(ourVersion); - if (remainingBufferInBytes < ourLength) - { - return VCM_MEMORY; - } - memcpy(&version[position], ourVersion, ourLength); - remainingBufferInBytes -= ourLength; - position += ourLength; - - // Safe to truncate here. - WebRtc_Word32 ret = _codecDataBase.Version(version, remainingBufferInBytes, position); - if (ret < 0) - { - return ret; - } - // Ensure the strlen call is safe by terminating at the end of version. - version[position + remainingBufferInBytes - 1] = '\0'; - ourLength = (WebRtc_UWord32)strlen(&version[position]); - remainingBufferInBytes -= (ourLength + 1); // include null termination. - position += (ourLength + 1); - - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::Id() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Id()"); - CriticalSectionScoped receiveCs(_receiveCritSect); - { - CriticalSectionScoped sendCs(_sendCritSect); - return _id; - } -} - -// Change the unique identifier of this object -WebRtc_Word32 -VideoCodingModuleImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ChangeUniqueId()"); - CriticalSectionScoped receiveCs(_receiveCritSect); - { - CriticalSectionScoped sendCs(_sendCritSect); - _id = id; - return VCM_OK; - } -} - -// Returns the number of milliseconds until the module wants a worker thread to call Process -WebRtc_Word32 -VideoCodingModuleImpl::TimeUntilNextProcess() -{ - WebRtc_UWord32 timeUntilNextProcess = VCM_MIN(_receiveStatsTimer.TimeUntilProcess(), - _sendStatsTimer.TimeUntilProcess()); - if ((_receiver.NackMode() != kNoNack) || (_dualReceiver.State() != kPassive)) - { - // We need a Process call more often if we are relying on retransmissions - timeUntilNextProcess = VCM_MIN(timeUntilNextProcess, - _retransmissionTimer.TimeUntilProcess()); - } - timeUntilNextProcess = VCM_MIN(timeUntilNextProcess, - _keyRequestTimer.TimeUntilProcess()); - - return timeUntilNextProcess; -} - -// Get number of supported codecs -WebRtc_UWord8 -VideoCodingModule::NumberOfCodecs() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "NumberOfCodecs()"); - return VCMCodecDataBase::NumberOfCodecs(); -} - -// Get supported codec with id -WebRtc_Word32 -VideoCodingModule::Codec(WebRtc_UWord8 listId, VideoCodec* codec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "Codec()"); - if (codec == NULL) - { - return VCM_PARAMETER_ERROR; - } - return VCMCodecDataBase::Codec(listId, codec); -} - -// Get supported codec with type -WebRtc_Word32 -VideoCodingModule::Codec(VideoCodecType codecType, VideoCodec* codec) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, -1, "Codec()"); - if (codec == NULL) - { - return VCM_PARAMETER_ERROR; - } - return VCMCodecDataBase::Codec(codecType, codec); -} - -/* -* Sender -*/ - -// Reset send side to initial state - all components -WebRtc_Word32 -VideoCodingModuleImpl::InitializeSender() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "InitializeSender()"); - CriticalSectionScoped cs(_sendCritSect); - _codecDataBase.ResetSender(); - _encoder = NULL; - _encodedFrameCallback.SetTransportCallback(NULL); - // setting default bitRate and frameRate to 0 - _mediaOpt.SetEncodingData(kVideoCodecUnknown, 0, 0, 0, 0, 0); - _mediaOpt.Reset(); // Resetting frame dropper - return VCM_OK; -} - -// Makes sure the encoder is in its initial state. -WebRtc_Word32 -VideoCodingModuleImpl::ResetEncoder() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ResetEncoder()"); - CriticalSectionScoped cs(_sendCritSect); - if (_encoder != NULL) - { - return _encoder->Reset(); - } - return VCM_OK; -} - -// Register the send codec to be used. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterSendCodec(const VideoCodec* sendCodec, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "RegisterSendCodec()"); - CriticalSectionScoped cs(_sendCritSect); - if (sendCodec == NULL) - { - return VCM_PARAMETER_ERROR; - } - WebRtc_Word32 ret = _codecDataBase.RegisterSendCodec(sendCodec, - numberOfCores, - maxPayloadSize); - if (ret < 0) - { - return ret; - } - - _encoder = _codecDataBase.SetEncoder(sendCodec, &_encodedFrameCallback); - if (_encoder == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to initialize encoder"); - return VCM_CODEC_ERROR; - } - _sendCodecType = sendCodec->codecType; - _mediaOpt.SetEncodingData(_sendCodecType, - sendCodec->maxBitrate, - sendCodec->maxFramerate, - sendCodec->startBitrate, - sendCodec->width, - sendCodec->height); - _mediaOpt.SetMtu(maxPayloadSize); - - return VCM_OK; -} - -// Get current send codec -WebRtc_Word32 -VideoCodingModuleImpl::SendCodec(VideoCodec* currentSendCodec) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec()"); - CriticalSectionScoped cs(_sendCritSect); - - if (currentSendCodec == NULL) - { - return VCM_PARAMETER_ERROR; - } - return _codecDataBase.SendCodec(currentSendCodec); -} - -// Get the current send codec type -VideoCodecType -VideoCodingModuleImpl::SendCodec() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SendCodec()"); - CriticalSectionScoped cs(_sendCritSect); - - return _codecDataBase.SendCodec(); -} - -// Register an external decoder object. -// This can not be used together with external decoder callbacks. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterExternalEncoder(VideoEncoder* externalEncoder, - WebRtc_UWord8 payloadType, - bool internalSource /*= false*/) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterExternalEncoder()"); - CriticalSectionScoped cs(_sendCritSect); - - if (externalEncoder == NULL) - { - bool wasSendCodec = false; - const WebRtc_Word32 ret = _codecDataBase.DeRegisterExternalEncoder(payloadType, - wasSendCodec); - if (wasSendCodec) - { - // Make sure the VCM doesn't use the de-registered codec - _encoder = NULL; - } - return ret; - } - return _codecDataBase.RegisterExternalEncoder(externalEncoder, - payloadType, - internalSource); -} - -// Get codec config parameters -WebRtc_Word32 -VideoCodingModuleImpl::CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "CodecConfigParameters()"); - CriticalSectionScoped cs(_sendCritSect); - if (_encoder != NULL) - { - return _encoder->CodecConfigParameters(buffer, size); - } - return VCM_UNINITIALIZED; -} - -// Get encode bitrate -WebRtc_UWord32 -VideoCodingModuleImpl::Bitrate() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Bitrate()"); - CriticalSectionScoped cs(_sendCritSect); - // return the bit rate which the encoder is set to - if (_encoder != NULL) - { - return _encoder->BitRate(); - } - return VCM_UNINITIALIZED; -} - -// Get encode frame rate -WebRtc_UWord32 -VideoCodingModuleImpl::FrameRate() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "FrameRate()"); - CriticalSectionScoped cs(_sendCritSect); - // input frame rate, not compensated - if (_encoder != NULL) - { - return _encoder->FrameRate(); - } - return VCM_UNINITIALIZED; -} - -// Set channel parameters -WebRtc_Word32 -VideoCodingModuleImpl::SetChannelParameters(WebRtc_UWord32 availableBandWidth, - WebRtc_UWord8 lossRate, - WebRtc_UWord32 RTT) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetChannelParameters()"); - WebRtc_Word32 ret = 0; - { - CriticalSectionScoped sendCs(_sendCritSect); - WebRtc_UWord32 targetRate = _mediaOpt.SetTargetRates(availableBandWidth, - lossRate, - RTT); - if (_encoder != NULL) - { - ret = _encoder->SetPacketLoss(lossRate); - if (ret < 0 ) - { - return ret; - } - ret = (WebRtc_Word32)_encoder->SetRates(targetRate, _mediaOpt.InputFrameRate()); - if (ret < 0) - { - return ret; - } - } - else - { - return VCM_UNINITIALIZED; - } // encoder - }// send side - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::SetReceiveChannelParameters(WebRtc_UWord32 RTT) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetReceiveChannelParameters()"); - CriticalSectionScoped receiveCs(_receiveCritSect); - _receiver.UpdateRtt(RTT); - return 0; -} - -// Register a transport callback which will be called to deliver the encoded buffers -WebRtc_Word32 -VideoCodingModuleImpl::RegisterTransportCallback(VCMPacketizationCallback* transport) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterTransportCallback()"); - CriticalSectionScoped cs(_sendCritSect); - _encodedFrameCallback.SetMediaOpt(&_mediaOpt); - _encodedFrameCallback.SetTransportCallback(transport); - return VCM_OK; -} - -// Register video output information callback which will be called to deliver information -// about the video stream produced by the encoder, for instance the average frame rate and -// bit rate. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterSendStatisticsCallback()"); - CriticalSectionScoped cs(_sendCritSect); - _sendStatsCallback = sendStats; - return VCM_OK; -} - -// Register a video quality settings callback which will be called when frame rate/dimensions -// need to be updated for video quality optimization -WebRtc_Word32 -VideoCodingModuleImpl::RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings) -{ - CriticalSectionScoped cs(_sendCritSect); - return _mediaOpt.RegisterVideoQMCallback(videoQMSettings); -} - - -// Register a video protection callback which will be called to deliver the requested FEC rate -// and NACK status (on/off). -WebRtc_Word32 -VideoCodingModuleImpl::RegisterProtectionCallback(VCMProtectionCallback* protection) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterProtectionCallback()"); - CriticalSectionScoped cs(_sendCritSect); - _mediaOpt.RegisterProtectionCallback(protection); - return VCM_OK; -} - -// Enable or disable a video protection method. -WebRtc_Word32 -VideoCodingModuleImpl::SetVideoProtection(VCMVideoProtection videoProtection, bool enable) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetVideoProtection()"); - - switch (videoProtection) - { - - case kProtectionNack: - { - // Both send-side and receive-side - SetVideoProtection(kProtectionNackSender, enable); - SetVideoProtection(kProtectionNackReceiver, enable); - break; - } - - case kProtectionNackSender: - { - // Send-side only - CriticalSectionScoped cs(_sendCritSect); - _mediaOpt.EnableNack(enable); - break; - } - - case kProtectionNackReceiver: - { - // Receive-side only - CriticalSectionScoped cs(_receiveCritSect); - if (enable) - { - _receiver.SetNackMode(kNackInfinite); - } - else - { - _receiver.SetNackMode(kNoNack); - } - break; - } - - case kProtectionDualDecoder: - { - CriticalSectionScoped cs(_receiveCritSect); - if (enable) - { - _receiver.SetNackMode(kNoNack); - _dualReceiver.SetNackMode(kNackInfinite); - } - else - { - _dualReceiver.SetNackMode(kNoNack); - } - break; - } - - case kProtectionKeyOnLoss: - { - CriticalSectionScoped cs(_receiveCritSect); - if (enable) - { - _keyRequestMode = kKeyOnLoss; - } - else if (_keyRequestMode == kKeyOnLoss) - { - _keyRequestMode = kKeyOnError; // default mode - } - else - { - return VCM_PARAMETER_ERROR; - } - break; - } - - case kProtectionKeyOnKeyLoss: - { - CriticalSectionScoped cs(_receiveCritSect); - if (enable) - { - _keyRequestMode = kKeyOnKeyLoss; - } - else if (_keyRequestMode == kKeyOnKeyLoss) - { - _keyRequestMode = kKeyOnError; // default mode - } - else - { - return VCM_PARAMETER_ERROR; - } - break; - } - - case kProtectionNackFEC: - { - { - // Receive side - CriticalSectionScoped cs(_receiveCritSect); - if (enable) - { - _receiver.SetNackMode(kNackHybrid); - } - else - { - _receiver.SetNackMode(kNoNack); - } - } - // Send Side - { - CriticalSectionScoped cs(_sendCritSect); - _mediaOpt.EnableNackFEC(enable); - } - break; - } - - case kProtectionFEC: - { - CriticalSectionScoped cs(_sendCritSect); - _mediaOpt.EnableFEC(enable); - break; - } - - case kProtectionPeriodicKeyFrames: - { - CriticalSectionScoped cs(_sendCritSect); - return _codecDataBase.SetPeriodicKeyFrames(enable); - break; - } - - default: - return VCM_PARAMETER_ERROR; - } - return VCM_OK; -} - -// Add one raw video frame to the encoder, blocking. -WebRtc_Word32 -VideoCodingModuleImpl::AddVideoFrame(const VideoFrame& videoFrame, - const VideoContentMetrics* _contentMetrics, - const CodecSpecificInfo* codecSpecificInfo) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "AddVideoFrame()"); - CriticalSectionScoped cs(_sendCritSect); - - if (_encoder == NULL) - { - return VCM_UNINITIALIZED; - } - - if (_nextFrameType == kFrameEmpty) - { - return VCM_OK; - } - - _mediaOpt.UpdateIncomingFrameRate(); - - if (_mediaOpt.DropFrame()) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id), - "Drop frame due to bitrate"); - } - else - { - _mediaOpt.updateContentData(_contentMetrics); - const FrameType requestedFrameType = _nextFrameType; - _nextFrameType = kVideoFrameDelta; // default frame type - WebRtc_Word32 ret = _encoder->Encode(videoFrame, - codecSpecificInfo, - requestedFrameType); - if (_encoderInputFile != NULL) - { - fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _encoderInputFile); - } - if (ret < 0) - { - _nextFrameType = requestedFrameType; - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding,VCMId(_id), - "Encode error: %d", ret); - return ret; - } - } - - return VCM_OK; -} - -// Next frame encoded should be of the type frameType -// Good for only one frame -WebRtc_Word32 -VideoCodingModuleImpl::FrameTypeRequest(FrameType frameType) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "FrameTypeRequest()"); - CriticalSectionScoped cs(_sendCritSect); - _nextFrameType = frameType; - if (_encoder != NULL && _encoder->InternalSource()) - { - // Try to request the frame if we have an external encoder with internal source - // since AddVideoFrame never will be called. - if (_encoder->RequestFrame(_nextFrameType) == WEBRTC_VIDEO_CODEC_OK) - { - _nextFrameType = kVideoFrameDelta; - } - } - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::EnableFrameDropper(bool enable) -{ - CriticalSectionScoped cs(_sendCritSect); - _mediaOpt.EnableFrameDropper(enable); - return VCM_OK; -} - - -WebRtc_Word32 -VideoCodingModuleImpl::SentFrameCount(VCMFrameCount &frameCount) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "SentFrameCount()"); - CriticalSectionScoped cs(_sendCritSect); - return _mediaOpt.SentFrameCount(frameCount); -} - -// Initialize receiver, resets codec database etc -WebRtc_Word32 -VideoCodingModuleImpl::InitializeReceiver() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "InitializeReceiver()"); - CriticalSectionScoped cs(_receiveCritSect); - WebRtc_Word32 ret = _receiver.Initialize(); - if (ret < 0) - { - return ret; - } - - ret = _dualReceiver.Initialize(); - if (ret < 0) - { - return ret; - } - _codecDataBase.ResetReceiver(); - _timing.Reset(); - - _decoder = NULL; - _decodedFrameCallback.SetUserReceiveCallback(NULL); - _receiverInited = true; - _frameTypeCallback = NULL; - _frameStorageCallback = NULL; - _receiveStatsCallback = NULL; - _packetRequestCallback = NULL; - _keyRequestMode = kKeyOnError; - _scheduleKeyRequest = false; - - return VCM_OK; -} - -// Register a receive callback. Will be called whenever there is a new frame ready -// for rendering. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterReceiveCallback(VCMReceiveCallback* receiveCallback) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterReceiveCallback()"); - CriticalSectionScoped cs(_receiveCritSect); - _decodedFrameCallback.SetUserReceiveCallback(receiveCallback); - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::RegisterReceiveStatisticsCallback( - VCMReceiveStatisticsCallback* receiveStats) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterReceiveStatisticsCallback()"); - CriticalSectionScoped cs(_receiveCritSect); - _receiveStatsCallback = receiveStats; - return VCM_OK; -} - -// Register an externally defined decoder/render object. -// Can be a decoder only or a decoder coupled with a renderer. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterExternalDecoder(VideoDecoder* externalDecoder, - WebRtc_UWord8 payloadType, - bool internalRenderTiming) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, - webrtc::kTraceVideoCoding, - VCMId(_id), - "RegisterExternalDecoder()"); - CriticalSectionScoped cs(_receiveCritSect); - if (externalDecoder == NULL) - { - // Make sure the VCM updates the decoder next time it decodes. - _decoder = NULL; - return _codecDataBase.DeRegisterExternalDecoder(payloadType); - } - else - { - return _codecDataBase.RegisterExternalDecoder(externalDecoder, - payloadType, - internalRenderTiming); - } -} - -// Register a frame type request callback. -WebRtc_Word32 -VideoCodingModuleImpl::RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterFrameTypeCallback()"); - CriticalSectionScoped cs(_receiveCritSect); - _frameTypeCallback = frameTypeCallback; - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::RegisterFrameStorageCallback(VCMFrameStorageCallback* frameStorageCallback) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterFrameStorageCallback()"); - CriticalSectionScoped cs(_receiveCritSect); - _frameStorageCallback = frameStorageCallback; - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::RegisterPacketRequestCallback(VCMPacketRequestCallback* callback) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterPacketRequestCallback()"); - CriticalSectionScoped cs(_receiveCritSect); - _packetRequestCallback = callback; - return VCM_OK; -} - -// Decode next frame, blocking. -// Should be called as often as possible to get the most out of the decoder. -WebRtc_Word32 -VideoCodingModuleImpl::Decode(WebRtc_UWord16 maxWaitTimeMs) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Decode()"); - WebRtc_Word64 nextRenderTimeMs; - - { - CriticalSectionScoped cs(_receiveCritSect); - if (!_receiverInited) - { - return VCM_UNINITIALIZED; - } - if (!_codecDataBase.DecoderRegistered()) - { - return VCM_NO_CODEC_REGISTERED; - } - } - - const bool dualReceiverEnabledNotReceiving = _dualReceiver.State() != kReceiving && - _dualReceiver.NackMode() == kNackInfinite; - - VCMEncodedFrame* frame = _receiver.FrameForDecoding(maxWaitTimeMs, - nextRenderTimeMs, - _codecDataBase.RenderTiming(), - &_dualReceiver); - - if (dualReceiverEnabledNotReceiving && _dualReceiver.State() == kReceiving) - { - // Dual receiver is enabled (kNACK enabled), but was not receiving before the call to - // FrameForDecoding(). After the call the state changed to receiving, and therefore - // we must copy the primary decoder state to the dual decoder to make it possible - // for the dual decoder to start decoding retransmitted frames and recover. - CriticalSectionScoped cs(_receiveCritSect); - if (_dualDecoder != NULL) - { - _codecDataBase.ReleaseDecoder(_dualDecoder); - } - _dualDecoder = _codecDataBase.CreateDecoderCopy(); - if (_dualDecoder != NULL) - { - _dualDecoder->RegisterDecodeCompleteCallback(&_dualDecodedFrameCallback); - } - } - - if (frame != NULL) - { - CriticalSectionScoped cs(_receiveCritSect); - - const WebRtc_UWord32 timestamp = frame->TimeStamp(); - - // If this frame was too late, we should adjust the delay accordingly - _timing.UpdateCurrentDelay(frame->RenderTimeMs(), VCMTickTime::MillisecondTimestamp()); - - if (_bitStreamBeforeDecoder != NULL) - { - // Write bit stream to file for debugging purposes - fwrite(frame->Buffer(), 1, frame->Length(), _bitStreamBeforeDecoder); - } - if (_frameStorageCallback != NULL) - { - WebRtc_Word32 ret = frame->Store(*_frameStorageCallback); - if (ret < 0) - { - return ret; - } - } - - const WebRtc_Word32 ret = Decode(*frame); - _receiver.ReleaseFrame(frame); - frame = NULL; - if (ret != VCM_OK) - { - return ret; - } - } - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::RequestSliceLossIndication(const WebRtc_UWord64 pictureID) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterSliceLossIndication()"); - if (_frameTypeCallback != NULL) - { - const WebRtc_Word32 ret = _frameTypeCallback->SliceLossIndicationRequest(pictureID); - if (ret < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to request key frame"); - return ret; - } - } else - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id), - "No frame type request callback registered"); - return VCM_MISSING_CALLBACK; - } - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::RequestKeyFrame() -{ - if (_frameTypeCallback != NULL) - { - const WebRtc_Word32 ret = _frameTypeCallback->FrameTypeRequest(kVideoFrameKey); - if (ret < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to request key frame"); - return ret; - } - _scheduleKeyRequest = false; - } - else - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id), - "No frame type request callback registered"); - return VCM_MISSING_CALLBACK; - } - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "DecodeDualFrame()"); - CriticalSectionScoped cs(_receiveCritSect); - if (_dualReceiver.State() != kReceiving || _dualReceiver.NackMode() != kNackInfinite) - { - // The dual receiver is currently not receiving or dual decoder mode is disabled. - return VCM_OK; - } - WebRtc_Word64 dummyRenderTime; - WebRtc_Word32 decodeCount = 0; - VCMEncodedFrame* dualFrame = _dualReceiver.FrameForDecoding(maxWaitTimeMs, - dummyRenderTime); - if (dualFrame != NULL && _dualDecoder != NULL) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id), - "Decoding frame %u with dual decoder", dualFrame->TimeStamp()); - // Decode dualFrame and try to catch up - WebRtc_Word32 ret = _dualDecoder->Decode(*dualFrame); - if (ret != WEBRTC_VIDEO_CODEC_OK) - { - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to decode frame with dual decoder"); - _dualReceiver.ReleaseFrame(dualFrame); - return VCM_CODEC_ERROR; - } - if (_receiver.DualDecoderCaughtUp(dualFrame, _dualReceiver)) - { - // Copy the complete decoder state of the dual decoder - // to the primary decoder. - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideoCoding, VCMId(_id), - "Dual decoder caught up"); - _codecDataBase.CopyDecoder(*_dualDecoder); - _codecDataBase.ReleaseDecoder(_dualDecoder); - _dualDecoder = NULL; - } - decodeCount++; - } - _dualReceiver.ReleaseFrame(dualFrame); - return decodeCount; -} - - -// Must be called from inside the receive side critical section. -WebRtc_Word32 -VideoCodingModuleImpl::Decode(const VCMEncodedFrame& frame) -{ - // Change decoder if payload type has changed - const bool renderTimingBefore = _codecDataBase.RenderTiming(); - _decoder = _codecDataBase.SetDecoder(frame.PayloadType(), _decodedFrameCallback); - if (renderTimingBefore != _codecDataBase.RenderTiming()) - { - // Make sure we reset the decode time estimate since it will - // be zero for codecs without render timing. - _timing.ResetDecodeTime(); - } - if (_decoder == NULL) - { - return VCM_NO_CODEC_REGISTERED; - } - // Decode a frame - WebRtc_Word32 ret = _decoder->Decode(frame); - - // Check for failed decoding, run frame type request callback if needed. - if (ret < 0) - { - if (ret == VCM_ERROR_REQUEST_SLI) - { - return RequestSliceLossIndication( - _decodedFrameCallback.LastReceivedPictureID() + 1); - } - else - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to decode frame %u, requesting key frame", frame.TimeStamp()); - ret = RequestKeyFrame(); - } - } - else if (ret == VCM_REQUEST_SLI) - { - ret = RequestSliceLossIndication(_decodedFrameCallback.LastReceivedPictureID() + 1); - } - if (!frame.Complete() || frame.MissingFrame()) - { - switch (_keyRequestMode) - { - case kKeyOnKeyLoss: - { - if (frame.FrameType() == kVideoFrameKey) - { - _scheduleKeyRequest = true; - return VCM_OK; - } - break; - } - case kKeyOnLoss: - { - _scheduleKeyRequest = true; - return VCM_OK; - } - default: - break; - } - } - return ret; -} - -WebRtc_Word32 -VideoCodingModuleImpl::DecodeFromStorage(const EncodedVideoData& frameFromStorage) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "DecodeFromStorage()"); - CriticalSectionScoped cs(_receiveCritSect); - WebRtc_Word32 ret = _frameFromFile.ExtractFromStorage(frameFromStorage); - if (ret < 0) - { - return ret; - } - return Decode(_frameFromFile); -} - -// Reset the decoder state -WebRtc_Word32 -VideoCodingModuleImpl::ResetDecoder() -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ResetDecoder()"); - CriticalSectionScoped cs(_receiveCritSect); - if (_decoder != NULL) - { - _receiver.Initialize(); - _timing.Reset(); - return _decoder->Reset(); - _scheduleKeyRequest = false; - } - if (_dualReceiver.State() != kPassive) - { - _dualReceiver.Initialize(); - } - if (_dualDecoder != NULL) - { - _codecDataBase.ReleaseDecoder(_dualDecoder); - _dualDecoder = NULL; - } - return VCM_OK; -} - -// Register possible receive codecs, can be called multiple times -WebRtc_Word32 -VideoCodingModuleImpl::RegisterReceiveCodec(const VideoCodec* receiveCodec, - WebRtc_Word32 numberOfCores, - bool requireKeyFrame) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "RegisterReceiveCodec()"); - CriticalSectionScoped cs(_receiveCritSect); - if (receiveCodec == NULL) - { - return VCM_PARAMETER_ERROR; - } - return _codecDataBase.RegisterReceiveCodec(receiveCodec, numberOfCores, requireKeyFrame); -} - -// Get current received codec -WebRtc_Word32 -VideoCodingModuleImpl::ReceiveCodec(VideoCodec* currentReceiveCodec) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ReceiveCodec()"); - CriticalSectionScoped cs(_receiveCritSect); - if (currentReceiveCodec == NULL) - { - return VCM_PARAMETER_ERROR; - } - return _codecDataBase.ReceiveCodec(currentReceiveCodec); -} - -// Get current received codec -VideoCodecType -VideoCodingModuleImpl::ReceiveCodec() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "ReceiveCodec()"); - CriticalSectionScoped cs(_receiveCritSect); - return _codecDataBase.ReceiveCodec(); -} - -// Incoming packet from network parsed and ready for decode, non blocking. -WebRtc_Word32 -VideoCodingModuleImpl::IncomingPacket(const WebRtc_UWord8* incomingPayload, - WebRtc_UWord32 payloadLength, - const WebRtcRTPHeader& rtpInfo) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "IncomingPacket()"); - const VCMPacket packet(incomingPayload, payloadLength, rtpInfo); - WebRtc_Word32 ret; - if (_dualReceiver.State() != kPassive) - { - ret = _dualReceiver.InsertPacket(packet, - rtpInfo.type.Video.width, - rtpInfo.type.Video.height); - if (ret < 0) - { - return ret; - } - } - ret = _receiver.InsertPacket(packet, rtpInfo.type.Video.width, rtpInfo.type.Video.height); - if (ret < 0) - { - return ret; - } - return VCM_OK; -} - -// Set codec config parameters -WebRtc_Word32 -VideoCodingModuleImpl::SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetCodecConfigParameters()"); - CriticalSectionScoped cs(_receiveCritSect); - - WebRtc_Word32 ret = _codecDataBase.SetCodecConfigParameters(payloadType, buffer, length); - if (ret < 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "SetCodecConfigParameters() failed, %d", ret); - return ret; - } - return VCM_OK; -} - -// Minimum playout delay (used for lip-sync). This is the minimum delay required -// to sync with audio. Not included in VideoCodingModule::Delay() -// Defaults to 0 ms. -WebRtc_Word32 -VideoCodingModuleImpl::SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetMininumPlayoutDelay(%u)", minPlayoutDelayMs); - _timing.SetMinimumTotalDelay(minPlayoutDelayMs); - return VCM_OK; -} - -// The estimated delay caused by rendering, defaults to -// kDefaultRenderDelayMs = 10 ms -WebRtc_Word32 -VideoCodingModuleImpl::SetRenderDelay(WebRtc_UWord32 timeMS) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "SetRenderDelay(%u)", timeMS); - _timing.SetRenderDelay(timeMS); - return VCM_OK; -} - -// Current video delay -WebRtc_Word32 -VideoCodingModuleImpl::Delay() const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), "Delay()"); - return _timing.TargetVideoDelay(); -} - -// Nack list -WebRtc_Word32 -VideoCodingModuleImpl::NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size) -{ - VCMNackStatus nackStatus = kNackOk; - // Collect sequence numbers from the default receiver - // if in normal nack mode. Otherwise collect them from - // the dual receiver if the dual receiver is receiving. - if (_receiver.NackMode() != kNoNack) - { - nackStatus = _receiver.NackList(nackList, size); - } - else if (_dualReceiver.State() != kPassive) - { - nackStatus = _dualReceiver.NackList(nackList, size); - } - else - { - size = 0; - } - - switch (nackStatus) - { - case kNackNeedMoreMemory: - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, VCMId(_id), - "Out of memory"); - return VCM_MEMORY; - } - case kNackKeyFrameRequest: - { - CriticalSectionScoped cs(_receiveCritSect); - WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, VCMId(_id), - "Failed to get NACK list, requesting key frame"); - return RequestKeyFrame(); - } - default: - break; - } - return VCM_OK; -} - -WebRtc_Word32 -VideoCodingModuleImpl::ReceivedFrameCount(VCMFrameCount& frameCount) const -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoCoding, VCMId(_id), - "ReceivedFrameCount()"); - return _receiver.ReceivedFrameCount(frameCount); -} - -} diff --git a/modules/video_coding/main/source/video_coding_impl.h b/modules/video_coding/main/source/video_coding_impl.h deleted file mode 100644 index 69f44931f..000000000 --- a/modules/video_coding/main/source/video_coding_impl.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ - -#include "video_coding.h" -#include "critical_section_wrapper.h" -#include "frame_buffer.h" -#include "receiver.h" -#include "timing.h" -#include "jitter_buffer.h" -#include "codec_database.h" -#include "generic_decoder.h" -#include "generic_encoder.h" -#include "media_optimization.h" - -#include - -namespace webrtc -{ - -class VCMProcessTimer -{ -public: - VCMProcessTimer(WebRtc_UWord32 periodMs) : - _periodMs(periodMs), _latestMs(VCMTickTime::MillisecondTimestamp()) {} - WebRtc_UWord32 Period() const; - WebRtc_UWord32 TimeUntilProcess() const; - void Processed(); - -private: - WebRtc_UWord32 _periodMs; - WebRtc_Word64 _latestMs; -}; - -enum VCMKeyRequestMode -{ - kKeyOnError, // Normal mode, request key frames on decoder error - kKeyOnKeyLoss, // Request key frames on decoder error and on packet loss in key frames - kKeyOnLoss, // Request key frames on decoder error and on packet loss in any frame -}; - -class VideoCodingModuleImpl : public VideoCodingModule -{ -public: - VideoCodingModuleImpl(const WebRtc_Word32 id); - - virtual ~VideoCodingModuleImpl(); - - // Returns version of the module and its components - WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - WebRtc_Word32 Id() const; - - // Change the unique identifier of this object - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - // Returns the number of milliseconds until the module want a worker thread to call Process - virtual WebRtc_Word32 TimeUntilNextProcess(); - - virtual WebRtc_Word32 Process(); - - /* - * Sender - */ - - // Initialize send codec - virtual WebRtc_Word32 InitializeSender(); - - // Makes sure the encoder is in its initial state. - virtual WebRtc_Word32 ResetEncoder(); - - // Register the send codec to be used. - virtual WebRtc_Word32 RegisterSendCodec(const VideoCodec* sendCodec, - WebRtc_UWord32 numberOfCores, - WebRtc_UWord32 maxPayloadSize); - - // Get current send codec - virtual WebRtc_Word32 SendCodec(VideoCodec* currentSendCodec) const; - - // Get current send codec type - virtual VideoCodecType SendCodec() const; - - // Register an external encoder object. - virtual WebRtc_Word32 RegisterExternalEncoder(VideoEncoder* externalEncoder, - WebRtc_UWord8 payloadType, - bool internalSource = false); - - // Get codec config parameters - virtual WebRtc_Word32 CodecConfigParameters(WebRtc_UWord8* buffer, WebRtc_Word32 size); - - // Get encode bitrate - virtual WebRtc_UWord32 Bitrate() const; - - // Get encode frame rate - virtual WebRtc_UWord32 FrameRate() const; - - // Set channel parameters - virtual WebRtc_Word32 SetChannelParameters(WebRtc_UWord32 availableBandWidth, - WebRtc_UWord8 lossRate, - WebRtc_UWord32 RTT); - // Set recieve channel parameters - virtual WebRtc_Word32 SetReceiveChannelParameters(WebRtc_UWord32 RTT); - - // Register a transport callback which will be called to deliver the encoded buffers - virtual WebRtc_Word32 RegisterTransportCallback(VCMPacketizationCallback* transport); - - // Register a send statistics callback which will be called to deliver information - // about the video stream produced by the encoder, - // for instance the average frame rate and bit rate. - virtual WebRtc_Word32 RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats); - - // Register a video quality settings callback which will be called when - // frame rate/dimensions need to be updated for video quality optimization - virtual WebRtc_Word32 RegisterVideoQMCallback(VCMQMSettingsCallback* videoQMSettings); - - // Register a video protection callback which will be called to deliver - // the requested FEC rate and NACK status (on/off). - virtual WebRtc_Word32 RegisterProtectionCallback(VCMProtectionCallback* protection); - - // Enable or disable a video protection method. - virtual WebRtc_Word32 SetVideoProtection(VCMVideoProtection videoProtection, bool enable); - - // Add one raw video frame to the encoder, blocking. - virtual WebRtc_Word32 AddVideoFrame( - const VideoFrame& videoFrame, - const VideoContentMetrics* _contentMetrics = NULL, - const CodecSpecificInfo* codecSpecificInfo = NULL); - - // Next frame encoded should be of the type frameType. - virtual WebRtc_Word32 FrameTypeRequest(FrameType frameType); - - //Enable frame dropper - virtual WebRtc_Word32 EnableFrameDropper(bool enable); - - // Sent frame counters - virtual WebRtc_Word32 SentFrameCount(VCMFrameCount& frameCount) const; - - /* - * Receiver - */ - - // Initialize receiver, resets codec database etc - virtual WebRtc_Word32 InitializeReceiver(); - - // Register possible reveive codecs, can be called multiple times - virtual WebRtc_Word32 RegisterReceiveCodec(const VideoCodec* receiveCodec, - WebRtc_Word32 numberOfCores, - bool requireKeyFrame = false); - - // Register an externally defined decoder/render object. - // Can be a decoder only or a decoder coupled with a renderer. - virtual WebRtc_Word32 RegisterExternalDecoder(VideoDecoder* externalDecoder, - WebRtc_UWord8 payloadType, - bool internalRenderTiming); - - // Register a receive callback. Will be called whenever there are a new frame ready - // for rendering. - virtual WebRtc_Word32 RegisterReceiveCallback(VCMReceiveCallback* receiveCallback); - - // Register a receive statistics callback which will be called to deliver information - // about the video stream received by the receiving side of the VCM, for instance - // the average frame rate and bit rate. - virtual WebRtc_Word32 RegisterReceiveStatisticsCallback( - VCMReceiveStatisticsCallback* receiveStats); - - // Register a frame type request callback. - virtual WebRtc_Word32 RegisterFrameTypeCallback(VCMFrameTypeCallback* frameTypeCallback); - - // Register a frame storage callback. - virtual WebRtc_Word32 RegisterFrameStorageCallback( - VCMFrameStorageCallback* frameStorageCallback); - - // Nack callback - virtual WebRtc_Word32 RegisterPacketRequestCallback(VCMPacketRequestCallback* callback); - - // Decode next frame, blocks for a maximum of maxWaitTimeMs milliseconds. - // Should be called as often as possible to get the most out of the decoder. - virtual WebRtc_Word32 Decode(WebRtc_UWord16 maxWaitTimeMs = 200); - - // Decode next dual frame, blocks for a maximum of maxWaitTimeMs milliseconds. - virtual WebRtc_Word32 DecodeDualFrame(WebRtc_UWord16 maxWaitTimeMs = 200); - - // Reset the decoder state - virtual WebRtc_Word32 ResetDecoder(); - - // Get current received codec - virtual WebRtc_Word32 ReceiveCodec(VideoCodec* currentReceiveCodec) const; - - // Get current received codec type - virtual VideoCodecType ReceiveCodec() const; - - // Incoming packet from network parsed and ready for decode, non blocking. - virtual WebRtc_Word32 IncomingPacket(const WebRtc_UWord8* incomingPayload, - WebRtc_UWord32 payloadLength, - const WebRtcRTPHeader& rtpInfo); - - // A part of an encoded frame to be decoded. - // Used in conjunction with VCMFrameStorageCallback. - virtual WebRtc_Word32 DecodeFromStorage(const EncodedVideoData& frameFromStorage); - - // Set codec config parameters - virtual WebRtc_Word32 SetCodecConfigParameters(WebRtc_UWord8 payloadType, - const WebRtc_UWord8* buffer, - WebRtc_Word32 length); - - // Minimum playout delay (Used for lip-sync). This is the minimum delay required - // to sync with audio. Not included in VideoCodingModule::Delay() - // Defaults to 0 ms. - virtual WebRtc_Word32 SetMinimumPlayoutDelay(WebRtc_UWord32 minPlayoutDelayMs); - - // The estimated delay caused by rendering - virtual WebRtc_Word32 SetRenderDelay(WebRtc_UWord32 timeMS); - - // Current delay - virtual WebRtc_Word32 Delay() const; - - // Received frame counters - virtual WebRtc_Word32 ReceivedFrameCount(VCMFrameCount& frameCount) const; - -protected: - WebRtc_Word32 Decode(const webrtc::VCMEncodedFrame& frame); - WebRtc_Word32 RequestKeyFrame(); - WebRtc_Word32 RequestSliceLossIndication(const WebRtc_UWord64 pictureID) const; - WebRtc_Word32 NackList(WebRtc_UWord16* nackList, WebRtc_UWord16& size); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _receiveCritSect; // Critical section for receive side - bool _receiverInited; - VCMTiming _timing; - VCMTiming _dualTiming; - VCMReceiver _receiver; - VCMReceiver _dualReceiver; - VCMDecodedFrameCallback _decodedFrameCallback; - VCMDecodedFrameCallback _dualDecodedFrameCallback; - VCMFrameTypeCallback* _frameTypeCallback; - VCMFrameStorageCallback* _frameStorageCallback; - VCMReceiveStatisticsCallback* _receiveStatsCallback; - VCMPacketRequestCallback* _packetRequestCallback; - VCMGenericDecoder* _decoder; - VCMGenericDecoder* _dualDecoder; - FILE* _bitStreamBeforeDecoder; - VCMFrameBuffer _frameFromFile; - VCMKeyRequestMode _keyRequestMode; - bool _scheduleKeyRequest; - - CriticalSectionWrapper& _sendCritSect; // Critical section for send side - VCMGenericEncoder* _encoder; - VCMEncodedFrameCallback _encodedFrameCallback; - FrameType _nextFrameType; - VCMMediaOptimization _mediaOpt; - VideoCodecType _sendCodecType; - VCMSendStatisticsCallback* _sendStatsCallback; - FILE* _encoderInputFile; - - VCMCodecDataBase _codecDataBase; - VCMProcessTimer _receiveStatsTimer; - VCMProcessTimer _sendStatsTimer; - VCMProcessTimer _retransmissionTimer; - VCMProcessTimer _keyRequestTimer; -}; - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_VIDEO_CODING_IMPL_H_ diff --git a/modules/video_coding/main/source/video_coding_test.gyp b/modules/video_coding/main/source/video_coding_test.gyp deleted file mode 100644 index 89e62eff6..000000000 --- a/modules/video_coding/main/source/video_coding_test.gyp +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'video_coding_test', - 'type': 'executable', - 'dependencies': [ - 'video_coding.gyp:webrtc_video_coding', - '../../../rtp_rtcp/source/rtp_rtcp.gyp:rtp_rtcp', - '../../../utility/source/utility.gyp:webrtc_utility', - '../../../video_processing/main/source/video_processing.gyp:video_processing', - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - ], - 'include_dirs': [ - '../../../interface', - '../../codecs/vp8/main/interface', - '../../../../system_wrappers/interface', - '../../../../common_video/interface', - '../source', - ], - 'sources': [ - - # headers - '../test/codec_database_test.h', - '../test/generic_codec_test.h', - '../test/jitter_estimate_test.h', - '../test/media_opt_test.h', - '../test/normal_test.h', - '../test/quality_modes_test.h', - '../test/receiver_tests.h', - '../test/release_test.h', - '../test/rtp_player.h', - '../test/test_util.h', - '../test/video_source.h', - - # sources - '../test/codec_database_test.cc', - '../test/decode_from_storage_test.cc', - '../test/generic_codec_test.cc', - '../test/jitter_buffer_test.cc', - '../test/media_opt_test.cc', - '../test/mt_rx_tx_test.cc', - '../test/normal_test.cc', - '../test/quality_modes_test.cc', - '../test/receiver_timing_tests.cc', - '../test/rtp_player.cc', - '../test/test_util.cc', - '../test/tester_main.cc', - '../test/video_rtp_play_mt.cc', - '../test/video_rtp_play.cc', - '../test/video_source.cc', - - ], # source - - 'conditions': [ - - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', - ], - }], - - ], # conditions - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_coding/main/test/codec_database_test.cc b/modules/video_coding/main/test/codec_database_test.cc deleted file mode 100644 index 3faf0dd4c..000000000 --- a/modules/video_coding/main/test/codec_database_test.cc +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Implementation of codec data base test -// testing is done via the VCM module, no specific CodecDataBase module functionality. - -#include "codec_database_test.h" -#include "vp8.h" // for external codecs test -#include "../source/event.h" -#include "test_util.h" -#include "../../../../engine_configurations.h" - -#include -#include - -using namespace webrtc; - -int CodecDataBaseTest::RunTest(CmdArgs& args) -{ - VideoCodingModule* vcm = VideoCodingModule::Create(1); - CodecDataBaseTest* cdbt = new CodecDataBaseTest(vcm); - cdbt->Perform(args); - VideoCodingModule::Destroy(vcm); - delete cdbt; - return 0; - -} - -CodecDataBaseTest::CodecDataBaseTest(VideoCodingModule* vcm): -_width(0), -_height(0), -_timeStamp(0), -_lengthSourceFrame(0), -vcmMacrosTests(0), -vcmMacrosErrors(0), -_vcm(vcm) -{ - // -} -CodecDataBaseTest::~CodecDataBaseTest() -{ - // -} -void -CodecDataBaseTest::Setup(CmdArgs& args) -{ - _inname= args.inputFile; - _width = args.width; - _height = args.height; - _frameRate = args.frameRate; - _lengthSourceFrame = 3*_width*_height/2; - if (args.outputFile.compare("")) - _outname = "CDBtest_decoded.yuv"; - else - _outname = args.outputFile; - _outname = args.outputFile; - _encodedName = "../CDBtest_encoded.vp8"; - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - - if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) - { - printf("Cannot write file %s.\n", _outname.c_str()); - exit(1); - } - - return; -} - - - -WebRtc_Word32 -CodecDataBaseTest::Perform(CmdArgs& args) -{ -#ifndef VIDEOCODEC_VP8 - assert(false); -#endif - Setup(args); - EventWrapper* waitEvent = EventWrapper::Create(); - - /**************************/ - /* General Sanity Checks */ - /************************/ - VideoCodec sendCodec, receiveCodec; - TEST(VideoCodingModule::NumberOfCodecs() > 0); - WebRtc_Word8 version[512]; - WebRtc_UWord32 length = 512; - WebRtc_UWord32 position = 0; - TEST(_vcm->Version(version, length, position) == VCM_OK); - printf("%s", version); - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - VCMDecodeCompleteCallback *_decodeCallback = new VCMDecodeCompleteCallback(_decodedFile); - VCMEncodeCompleteCallback *_encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile); - _vcm->RegisterReceiveCallback(_decodeCallback); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - _encodeCompleteCallback->SetFrameDimensions(_width, _height); - // registering the callback - encode and decode with the same vcm (could be later changed) - _encodeCompleteCallback->RegisterReceiverVCM(_vcm); - // preparing a frame to be encoded - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - // Encoder registration - TEST (VideoCodingModule::NumberOfCodecs() > 0); - TEST(VideoCodingModule::Codec(-1, &sendCodec) == VCM_PARAMETER_ERROR); - TEST(VideoCodingModule::Codec(VideoCodingModule::NumberOfCodecs() + 1, &sendCodec) == VCM_PARAMETER_ERROR); - VideoCodingModule::Codec(1, &sendCodec); - sendCodec.plType = 0; // random value - TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - _vcm->RegisterReceiveCallback(_decodeCallback); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - printf(" \nNumber of Registered Codecs: %d \n\n", VideoCodingModule::NumberOfCodecs()); - printf("Registered codec names: "); - for (int i=0; i < VideoCodingModule::NumberOfCodecs(); i++) - { - VideoCodingModule::Codec(i, &sendCodec); - printf("%s ", sendCodec.plName); - } - printf("\n\nVerify that all requested codecs are used\n \n \n"); - - // testing with first codec registered - VideoCodingModule::Codec(0, &sendCodec); - _vcm->RegisterSendCodec(&sendCodec, 1, 1440); - _vcm->InitializeReceiver(); - TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK ); - _vcm->InitializeSender(); - TEST (_vcm->AddVideoFrame(sourceFrame) < 0 ); - - // Test changing frame size while keeping the same payload type - VideoCodingModule::Codec(0, &sendCodec); - sendCodec.width = 352; - sendCodec.height = 288; - VideoCodec currentSendCodec; - _vcm->RegisterSendCodec(&sendCodec, 1, 1440); - _vcm->SendCodec(¤tSendCodec); - TEST(currentSendCodec.width == sendCodec.width && - currentSendCodec.height == sendCodec.height); - sendCodec.width = 352/2; - sendCodec.height = 288/2; - _vcm->RegisterSendCodec(&sendCodec, 1, 1440); - _vcm->SendCodec(¤tSendCodec); - TEST(currentSendCodec.width == sendCodec.width && - currentSendCodec.height == sendCodec.height); - - delete _decodeCallback; - _decodeCallback = NULL; - delete _encodeCompleteCallback; - _encodeCompleteCallback = NULL; - - VCMEncodeCompleteCallback *_encodeCallback = new VCMEncodeCompleteCallback(_encodedFile); - - /*************************/ - /* External codecs */ - /*************************/ - - - _vcm->InitializeReceiver(); - VP8Decoder* decoder = new VP8Decoder; - VideoCodec vp8DecSettings; - VideoCodingModule::Codec(kVideoCodecVP8, &vp8DecSettings); - TEST(_vcm->RegisterExternalDecoder(decoder, vp8DecSettings.plType, false) == VCM_OK); - TEST(_vcm->RegisterReceiveCodec(&vp8DecSettings, 1, false) == VCM_OK); - VP8Encoder* encoder = new VP8Encoder; - VideoCodec vp8EncSettings; - VideoCodingModule::Codec(kVideoCodecVP8, &vp8EncSettings); - _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized - _encodeCallback->RegisterReceiverVCM(_vcm); - _encodeCallback->SetCodecType(kRTPVideoVP8); - TEST(_vcm->RegisterExternalEncoder(encoder, vp8EncSettings.plType) == VCM_OK); - TEST(_vcm->RegisterSendCodec(&vp8EncSettings, 4, 1440) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - waitEvent->Wait(33); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - - // De-register and try again. - TEST(_vcm->RegisterExternalDecoder(NULL, vp8DecSettings.plType, false) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() < 0); // Expect an error since we have de-registered the decoder - TEST(_vcm->RegisterExternalEncoder(NULL, vp8DecSettings.plType) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) < 0); // No send codec registered - - delete decoder; - decoder = NULL; - delete encoder; - encoder = NULL; - - /*************************************** - * Test the "require key frame" setting* - ***************************************/ - - TEST(_vcm->InitializeSender() == VCM_OK); - TEST(_vcm->InitializeReceiver() == VCM_OK); - VideoCodingModule::Codec(kVideoCodecVP8, &receiveCodec); - receiveCodec.height = _height; - receiveCodec.width = _width; - TEST(_vcm->RegisterSendCodec(&receiveCodec, 4, 1440) == VCM_OK); - TEST(_vcm->RegisterReceiveCodec(&receiveCodec, 1, true) == VCM_OK); // Require key frame - _vcm->RegisterTransportCallback(_encodeCallback); // encode returns error if callback uninitialized - _encodeCallback->RegisterReceiverVCM(_vcm); - _encodeCallback->SetCodecType(kRTPVideoVP8); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - TEST(_vcm->ResetDecoder() == VCM_OK); - waitEvent->Wait(33); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - // Try to decode a delta frame. Should get a warning since we have enabled the "require key frame" setting - // and because no frame type request callback has been registered. - TEST(_vcm->Decode() == VCM_MISSING_CALLBACK); - TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - - // Make sure we can register another codec with the same - // payload type without crash. - _vcm->InitializeReceiver(); - sendCodec.width = _width; - sendCodec.height = _height; - TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); - TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); - waitEvent->Wait(33); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); - waitEvent->Wait(33); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->FrameTypeRequest(kVideoFrameKey) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - TEST(_vcm->ResetDecoder() == VCM_OK); - - delete _encodeCallback; - - /*************************/ - /* Send/Receive Control */ - /***********************/ - /* - 1. check available codecs (N) - 2. register all corresponding decoders - 3. encode 300/N frames with each encoder, and hope to properly decode - 4. encode without a matching decoder - expect an error - */ - rewind(_sourceFile); - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - sourceFrame.Free(); - VCMDecodeCompleteCallback* decodeCallCDT = new VCMDecodeCompleteCallback(_decodedFile); - VCMEncodeCompleteCallback* encodeCallCDT = new VCMEncodeCompleteCallback(_encodedFile); - _vcm->RegisterReceiveCallback(decodeCallCDT); - _vcm->RegisterTransportCallback(encodeCallCDT); - encodeCallCDT->RegisterReceiverVCM(_vcm); - if (VideoCodingModule::NumberOfCodecs() > 0) - { - // registrating all available decoders - int i, j; - //double psnr; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - _vcm->RegisterReceiveCallback(decodeCallCDT); - for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++) - { - VideoCodingModule::Codec(i, &receiveCodec); - if (strcmp(receiveCodec.plName, "I420") == 0) - { - receiveCodec.height = _height; - receiveCodec.width = _width; - } - _vcm->RegisterReceiveCodec(&receiveCodec, 1); - } - // start encoding - iterating over available encoders - _vcm->RegisterTransportCallback(encodeCallCDT); - encodeCallCDT->RegisterReceiverVCM(_vcm); - encodeCallCDT->Initialize(); - int frameCnt = 0; - for (i=0; i < VideoCodingModule::NumberOfCodecs(); i++) - { - encodeCallCDT->ResetByteCount(); - VideoCodingModule::Codec(i, &sendCodec); - sendCodec.height = _height; - sendCodec.width = _width; - sendCodec.startBitrate = 1000; - sendCodec.maxBitrate = 8000; - encodeCallCDT->SetFrameDimensions(_width, _height); - encodeCallCDT->SetCodecType(ConvertCodecType(sendCodec.plName)); - TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) == VCM_OK); - - printf("Encoding with %s \n\n", sendCodec.plName); - for (j=0; j < int(300/VideoCodingModule::NumberOfCodecs()); j++)// assuming 300 frames, NumberOfCodecs <= 10 - { - frameCnt++; - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - // building source frame - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - sourceFrame.SetLength(_lengthSourceFrame); - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - // send frame to the encoder - TEST (_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - waitEvent->Wait(33); // was 100 - - int ret =_vcm->Decode(); - TEST(ret == 0); - if (ret < 0) - { - printf("Error #%d in frame number %d \n",ret, frameCnt); - } - // verifying matching payload types: - _vcm->SendCodec(&sendCodec); - _vcm->ReceiveCodec(&receiveCodec); - TEST(sendCodec.plType == receiveCodec.plType); - if (sendCodec.plType != receiveCodec.plType) - { - printf("frame number:%d\n",frameCnt); - } - } // end for:encode-decode - // byte count for codec specific - - printf("Total bytes encoded: %f \n\n",(8.0/1000)*(encodeCallCDT->EncodedBytes()/((int)10/VideoCodingModule::NumberOfCodecs()))); - // decode what's left in the buffer.... - _vcm->Decode(); - _vcm->Decode(); - } // end: iterate codecs - rewind(_sourceFile); - sourceFrame.Free(); - delete tmpBuffer; - delete decodeCallCDT; - delete encodeCallCDT; - // closing and calculating PSNR for prior encoder-decoder test - TearDown(); // closing open files - double psnr = 0; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); - printf(" \n @ %d KBPS: ", sendCodec.startBitrate); - printf("PSNR from encoder-decoder send-receive control test is %f \n \n", psnr); - } // end of #codecs >1 - - delete waitEvent; - Print(); - return 0; -} -void -CodecDataBaseTest::Print() -{ - printf("\nVCM Codec DataBase Test: \n\n%i tests completed\n", vcmMacrosTests); - if (vcmMacrosErrors > 0) - { - printf("%i FAILED\n\n", vcmMacrosErrors); - } - else - { - printf("ALL PASSED\n\n"); - } -} - -void -CodecDataBaseTest::TearDown() -{ - fclose(_sourceFile); - fclose(_decodedFile); - fclose(_encodedFile); - return; -} diff --git a/modules/video_coding/main/test/codec_database_test.h b/modules/video_coding/main/test/codec_database_test.h deleted file mode 100644 index 0272b14e3..000000000 --- a/modules/video_coding/main/test/codec_database_test.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_ - -#include "video_coding.h" -#include "test_macros.h" -#include "test_util.h" - -#include - -/* -Test consists of: -1. Sanity chacks: Send and Receive side (bad input, etc. ) -2. Send-side control (encoder registration etc.) -3. Decoder-side control - encode with various encoders, and verify correct decoding -*/ - -class CodecDataBaseTest -{ -public: - CodecDataBaseTest(webrtc::VideoCodingModule* vcm); - ~CodecDataBaseTest(); - static int RunTest(CmdArgs& args); - WebRtc_Word32 Perform(CmdArgs& args); -private: - void TearDown(); - void Setup(CmdArgs& args); - void Print(); - webrtc::VideoCodingModule* _vcm; - std::string _inname; - std::string _outname; - std::string _encodedName; - FILE* _sourceFile; - FILE* _decodedFile; - FILE* _encodedFile; - WebRtc_UWord16 _width; - WebRtc_UWord16 _height; - WebRtc_UWord32 _lengthSourceFrame; - WebRtc_UWord32 _timeStamp; - int vcmMacrosTests; - int vcmMacrosErrors; - float _frameRate; -}; // end of codecDBTest class definition - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_CODEC_DATABASE_TEST_H_ diff --git a/modules/video_coding/main/test/decode_from_storage_test.cc b/modules/video_coding/main/test/decode_from_storage_test.cc deleted file mode 100644 index 5ba805559..000000000 --- a/modules/video_coding/main/test/decode_from_storage_test.cc +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "receiver_tests.h" -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "trace.h" -#include "tick_time.h" -#include "../source/event.h" -#include "test_macros.h" -#include "rtp_player.h" - -using namespace webrtc; - -class FrameStorageCallback : public VCMFrameStorageCallback -{ -public: - FrameStorageCallback(VideoCodingModule* vcm) : _vcm(vcm) {} - - WebRtc_Word32 StoreReceivedFrame(const EncodedVideoData& frameToStore) - { - _vcm->DecodeFromStorage(frameToStore); - return VCM_OK; - } - -private: - VideoCodingModule* _vcm; -}; - -int DecodeFromStorageTest(CmdArgs& args) -{ - // Make sure this test isn't executed without simulated clocks -#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG) - return -1; -#endif - // BEGIN Settings - - bool protectionEnabled = false; - VCMVideoProtection protectionMethod = kProtectionNack; - WebRtc_UWord32 rttMS = 100; - float lossRate = 0.00f; - bool reordering = false; - WebRtc_UWord32 renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - const WebRtc_Word64 MAX_RUNTIME_MS = -1; - std::string rtpFilename = args.inputFile; - std::string outFilename = args.outputFile; - if (outFilename == "") - outFilename = "DecodeFromStorage.yuv"; - - FrameReceiveCallback receiveCallback(outFilename.c_str()); - - // END Settings - - Trace::CreateTrace(); - Trace::SetTraceFile("decodeFromStorageTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - - VideoCodingModule* vcm = VideoCodingModule::Create(1); - VideoCodingModule* vcmPlayback = VideoCodingModule::Create(2); - FrameStorageCallback storageCallback(vcmPlayback); - RtpDataCallback dataCallback(vcm); - WebRtc_Word32 ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - ret = vcmPlayback->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - vcm->RegisterFrameStorageCallback(&storageCallback); - vcmPlayback->RegisterReceiveCallback(&receiveCallback); - RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback); - ListWrapper payloadTypes; - payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8)); - - // Register receive codecs in VCM - ListItem* item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - VideoCodec codec; - memset(&codec, 0, sizeof(codec)); - strncpy(codec.plName, payloadType->name.c_str(), payloadType->name.length()); - codec.plName[payloadType->name.length()] = '\0'; - codec.plType = payloadType->payloadType; - codec.codecType = payloadType->codecType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - if (vcmPlayback->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } - item = payloadTypes.Next(item); - } - if (rtpStream.Initialize(payloadTypes) < 0) - { - return -1; - } - bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack || - protectionMethod == kProtectionDualDecoder); - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - rtpStream.SetReordering(reordering); - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protectionMethod, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - ret = 0; - - // RTP stream main loop - while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0) - { - if (VCMTickTime::MillisecondTimestamp() % 5 == 0) - { - ret = vcm->Decode(); - if (ret < 0) - { - return -1; - } - } - if (vcm->TimeUntilNextProcess() <= 0) - { - vcm->Process(); - } - if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS) - { - break; - } - VCMTickTime::IncrementDebugClock(); - } - - switch (ret) - { - case 1: - printf("Success\n"); - break; - case -1: - printf("Failed\n"); - break; - case 0: - printf("Timeout\n"); - break; - } - - rtpStream.Print(); - - // Tear down - item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - delete payloadType; - } - ListItem* itemToRemove = item; - item = payloadTypes.Next(item); - payloadTypes.Erase(itemToRemove); - } - VideoCodingModule::Destroy(vcm); - vcm = NULL; - VideoCodingModule::Destroy(vcmPlayback); - vcmPlayback = NULL; - Trace::ReturnTrace(); - - return 0; -} diff --git a/modules/video_coding/main/test/generic_codec_test.cc b/modules/video_coding/main/test/generic_codec_test.cc deleted file mode 100644 index 7513b563a..000000000 --- a/modules/video_coding/main/test/generic_codec_test.cc +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "generic_codec_test.h" -#include -#include -#include "tick_time.h" -#include "../source/event.h" -#include "rtp_rtcp.h" -#include "module_common_types.h" -#include "test_util.h" - -using namespace webrtc; - -int GenericCodecTest::RunTest(CmdArgs& args) -{ - // Don't run this test with debug time -#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG) - return -1; -#endif - VideoCodingModule* vcm = VideoCodingModule::Create(1); - GenericCodecTest* get = new GenericCodecTest(vcm); - Trace::CreateTrace(); - Trace::SetTraceFile("genericCodecTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - get->Perform(args); - Trace::ReturnTrace(); - delete get; - VideoCodingModule::Destroy(vcm); - return 0; -} - -GenericCodecTest::GenericCodecTest(VideoCodingModule* vcm): -_width(0), -_height(0), -_timeStamp(0), -_lengthSourceFrame(0), -_frameRate(0), -vcmMacrosTests(0), -vcmMacrosErrors(0), -_vcm(vcm) -{ -} - -GenericCodecTest::~GenericCodecTest() -{ -} - -void -GenericCodecTest::Setup(CmdArgs& args) -{ - _timeStamp = 0; - - /* Test Sequence parameters */ - - _inname= args.inputFile; - if (args.outputFile.compare("")) - _outname = "GCTest_decoded.yuv"; - else - _outname = args.outputFile; - _encodedName = "../GCTest_encoded.vp8"; - _width = args.width; - _height = args.height; - _frameRate = args.frameRate; - _lengthSourceFrame = 3*_width*_height/2; - - /* File settings */ - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) - { - printf("Cannot write file %s.\n", _outname.c_str()); - exit(1); - } - - return; -} -WebRtc_Word32 -GenericCodecTest::Perform(CmdArgs& args) -{ - WebRtc_Word32 ret; - Setup(args); - /* - 1. sanity checks - 2. encode/decoder individuality - 3. API testing - 4. Target bitrate (within a specific timespan) - 5. Pipeline Delay - */ - - /*******************************/ - /* sanity checks on inputs */ - /*****************************/ - VideoCodec sendCodec, receiveCodec; - sendCodec.maxBitrate = 8000; - TEST(_vcm->NumberOfCodecs() > 0); // This works since we now initialize the list in the constructor - TEST(_vcm->Codec(0, &sendCodec) == VCM_OK); - _vcm->InitializeSender(); - _vcm->InitializeReceiver(); - WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs(); - // registration of first codec in the list - int i = 0; - _vcm->Codec(0, &_sendCodec); - TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1440) == VCM_OK); - // sanity on encoder registration - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - _vcm->InitializeSender(); - TEST(_vcm->Codec(kVideoCodecVP8, &sendCodec) == 0); - TEST(_vcm->RegisterSendCodec(&sendCodec, -1, 1440) < 0); // bad number of cores - sendCodec.maxBitrate = 8000; - _vcm->RegisterSendCodec(&sendCodec, 1, 1440); - _vcm->InitializeSender(); - _vcm->Codec(kVideoCodecVP8, &sendCodec); - sendCodec.height = 0; - TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad height - _vcm->Codec(kVideoCodecVP8, &sendCodec); - sendCodec.startBitrate = -2; - TEST(_vcm->RegisterSendCodec(&sendCodec, 1, 1440) < 0); // bad bit rate - _vcm->Codec(kVideoCodecVP8, &sendCodec); - _vcm->InitializeSender(); - TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rate when encoder uninitialized - // register all availbale decoders -- need to have more for this test - for (i=0; i< NumberOfCodecs; i++) - { - _vcm->Codec(i, &receiveCodec); - _vcm->RegisterReceiveCodec(&receiveCodec, 1); - } - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - // building source frame - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - sourceFrame.SetTimeStamp(_timeStamp++); - // encode/decode - TEST(_vcm->AddVideoFrame(sourceFrame) < 0 ); // encoder uninitialized - _vcm->InitializeReceiver(); - TEST(_vcm->SetChannelParameters(100, 0, 0) < 0);// setting rtt when receiver uninitialized - - /**************************************/ - /* encoder/decoder individuality test */ - /**************************************/ - //Register both encoder and decoder, reset decoder - encode, set up decoder, reset encoder - decode. - rewind(_sourceFile); - sourceFrame.Free(); - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - NumberOfCodecs = _vcm->NumberOfCodecs(); - // Register VP8 - _vcm->Codec(kVideoCodecVP8, &_sendCodec); - _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); - _vcm->SendCodec(&sendCodec); - sendCodec.startBitrate = 2000; - - // Set target frame rate to half of the incoming frame rate - // to test the frame rate control in the VCM - sendCodec.maxFramerate = (WebRtc_UWord8)(_frameRate / 2); - sendCodec.width = _width; - sendCodec.height = _height; - TEST(strncmp(_sendCodec.plName, "VP8", 3) == 0); // was VP8 - - _decodeCallback = new VCMDecodeCompleteCallback(_decodedFile); - _encodeCompleteCallback = new VCMEncodeCompleteCallback(_encodedFile); - _vcm->RegisterReceiveCallback(_decodeCallback); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - _encodeCompleteCallback->RegisterReceiverVCM(_vcm); - - _vcm->RegisterSendCodec(&sendCodec, 4, 1440); - _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); - - _vcm->InitializeReceiver(); - _vcm->Process(); - - //encoding 1 second of video - for (i = 0; i < _frameRate; i++) - { - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - IncrementDebugClock(_frameRate); - _vcm->Process(); - } - sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate; - _vcm->InitializeSender(); - TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); // same codec for encode and decode - ret = 0; - i = 0; - while ((i < 25) && (ret == 0) ) - { - ret = _vcm->Decode(); - TEST(ret == VCM_OK); - if (ret < 0) - { - printf("error in frame # %d \n", i); - } - IncrementDebugClock(_frameRate); - i++; - } - //TEST((ret == 0) && (i = 50)); - if (ret == 0) - { - printf("Encoder/Decoder individuality test complete - View output files \n"); - } - // last frame - not decoded - _vcm->InitializeReceiver(); - TEST(_vcm->Decode() < 0); // frame to be encoded exists, decoder uninitialized - - - // Test key frame request on packet loss mode. - // This a frame as a key frame and fooling the receiver - // that the last packet was lost. The decoding will succeed, - // but the VCM will see a packet loss and request a new key frame. - VCMEncComplete_KeyReqTest keyReqTest_EncCompleteCallback(*_vcm); - KeyFrameReqTest frameTypeCallback; - _vcm->RegisterTransportCallback(&keyReqTest_EncCompleteCallback); - _encodeCompleteCallback->RegisterReceiverVCM(_vcm); - _vcm->RegisterSendCodec(&sendCodec, 4, 1440); - _encodeCompleteCallback->SetCodecType(ConvertCodecType(sendCodec.plName)); - TEST(_vcm->SetVideoProtection(kProtectionKeyOnKeyLoss, true) == VCM_OK); - TEST(_vcm->RegisterFrameTypeCallback(&frameTypeCallback) == VCM_OK); - TEST(_vcm->RegisterReceiveCodec(&sendCodec, 1) == VCM_OK); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - - printf("API tests complete \n"); - - /*******************/ - /* Bit Rate Tests */ - /*****************/ - /* Requirements: - * 1. OneSecReq = 15 % above/below target over a time period of 1s (_frameRate number of frames) - * 3. FullReq = 10% for total seq. (for 300 frames/seq. coincides with #1) - * 4. Test will go over all registered codecs - //NOTE: time requirements are not part of the release tests - */ - double FullReq = 0.1; - double OneSecReq = 0.15; - printf("\n RATE CONTROL TEST\n"); - // initializing.... - _vcm->InitializeSender(); - _vcm->InitializeReceiver(); - rewind(_sourceFile); - sourceFrame.Free(); - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - const float bitRate[] = {100, 400, 600, 1000, 2000, 3000}; - const float nBitrates = sizeof(bitRate)/sizeof(*bitRate); - float _bitRate; - int _frameCnt = 0; - WebRtc_Word64 startTime, currentTime, oneSecTime; - float totalBytesOneSec;//, totalBytesTenSec; - float totalBytes, actualBitrate; - VCMFrameCount frameCount; // testing frame type counters - // start test - NumberOfCodecs = _vcm->NumberOfCodecs(); - // going over all available codecs - _encodeCompleteCallback->SetFrameDimensions(_width, _height); - SendStatsTest sendStats; - for (int k = 0; k < NumberOfCodecs; k++) - //for (int k = NumberOfCodecs - 1; k >=0; k--) - {// static list starts from 0 - //just checking - _vcm->InitializeSender(); - _sendCodec.maxBitrate = 8000; - TEST(_vcm->Codec(k, &_sendCodec)== VCM_OK); - _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - _encodeCompleteCallback->SetCodecType(ConvertCodecType(_sendCodec.plName)); - printf (" \n\n Codec type = %s \n\n",_sendCodec.plName); - for (i = 0; i < nBitrates; i++) - { - _bitRate = static_cast(bitRate[i]); - // just testing - _vcm->InitializeSender(); - _sendCodec.startBitrate = (int)_bitRate; - _sendCodec.maxBitrate = 8000; - _vcm->RegisterSendCodec(&_sendCodec, 1, 1440); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - // up to here - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20); - _frameCnt = 0; - totalBytes = 0; - startTime = VCMTickTime::MicrosecondTimestamp(); - _encodeCompleteCallback->Initialize(); - sendStats.SetTargetFrameRate(static_cast(_frameRate)); - _vcm->RegisterSendStatisticsCallback(&sendStats); - while (fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile) > 0) - { - _frameCnt++; - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); - sourceFrame.SetTimeStamp(_timeStamp); - - ret = _vcm->AddVideoFrame(sourceFrame); - IncrementDebugClock(_frameRate); - // The following should be uncommneted for timing tests. Release tests only include - // compliance with full sequence bit rate. - - - //totalBytes = WaitForEncodedFrame(); - //currentTime = VCMTickTime::MillisecondTimestamp();//clock()/(double)CLOCKS_PER_SEC; - if (_frameCnt == _frameRate)// @ 1sec - { - oneSecTime = VCMTickTime::MicrosecondTimestamp(); - totalBytesOneSec = _encodeCompleteCallback->EncodedBytes();//totalBytes; - } - TEST(_vcm->TimeUntilNextProcess() >= 0); - } // video seq. encode done - TEST(_vcm->TimeUntilNextProcess() == 0); - _vcm->Process(); // Let the module calculate its send bit rate estimate - // estimating rates - // complete sequence - // bit rate assumes input frame rate is as specified - currentTime = VCMTickTime::MicrosecondTimestamp(); - totalBytes = _encodeCompleteCallback->EncodedBytes(); - actualBitrate = (float)(8.0/1000)*(totalBytes / (_frameCnt / _frameRate)); - WebRtc_Word64 timeDiff = (currentTime - startTime)/1000; - //actualBitrate = (float)(8.0*totalBytes)/timeDiff; - printf("Complete Seq.: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); - TEST((fabs(actualBitrate - _bitRate) < FullReq * _bitRate) || - (strncmp(_sendCodec.plName, "I420", 4) == 0)); - - // 1 Sec. - actualBitrate = (float)(8.0/1000)*(totalBytesOneSec); - //actualBitrate = (float)(8.0*totalBytesOneSec)/(oneSecTime - startTime); - //printf("First 1Sec: target bitrate: %.0f kbps, actual bitrate: %.1f kbps\n", _bitRate, actualBitrate); - //TEST(fabs(actualBitrate - _bitRate) < OneSecReq * _bitRate); - rewind(_sourceFile); - - //checking key/delta frame count - _vcm->SentFrameCount(frameCount); - printf("frame count: %d delta, %d key\n", frameCount.numDeltaFrames, frameCount.numKeyFrames); - }// end per codec - - } // end rate control test - /********************************/ - /* Encoder Pipeline Delay Test */ - /******************************/ - WebRtc_Word32 retVal; - _vcm->InitializeSender(); - sourceFrame.Free(); - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - NumberOfCodecs = _vcm->NumberOfCodecs(); - bool encodeComplete = false; - // going over all available codecs - for (int k = 0; k < NumberOfCodecs; k++) - { - retVal = _vcm->Codec(k, &_sendCodec); - retVal = _vcm->InitializeSender(); - _sendCodec.maxBitrate = 8000; - retVal = _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); - retVal = _vcm->RegisterTransportCallback(_encodeCompleteCallback); - - _frameCnt = 0; - encodeComplete = false; - while (encodeComplete == false) - { - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - _frameCnt++; - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); - sourceFrame.SetTimeStamp(_timeStamp); - retVal = _vcm->AddVideoFrame(sourceFrame); - encodeComplete = _encodeCompleteCallback->EncodeComplete(); - } // first frame encoded - printf ("\n Codec type = %s \n", _sendCodec.plName); - printf(" Encoder pipeline delay = %d frames\n", _frameCnt - 1); - } // end for all codecs - - /********************************/ - /* Encoder Packet Size Test */ - /********************************/ - RtpRtcp& rtpModule = *RtpRtcp::CreateRtpRtcp(1, false); - TEST(rtpModule.InitSender() == 0); - RTPSendCallback_SizeTest sendCallback; - rtpModule.RegisterSendTransport(&sendCallback); - - VCMRTPEncodeCompleteCallback encCompleteCallback(&rtpModule); - _vcm->InitializeSender(); - - // TEST DISABLED FOR NOW SINCE VP8 DOESN'T HAVE THIS FEATURE - -// sourceFrame.Free(); -// sourceFrame.VerifyAndAllocate(_lengthSourceFrame); -// NumberOfCodecs = _vcm->NumberOfCodecs(); -// WebRtc_UWord32 targetPayloadSize = 500; -// rtpModule.SetMaxTransferUnit(targetPayloadSize); -// // going over all available codecs -// for (int k = 0; k < NumberOfCodecs; k++) -// { -// _vcm->Codec(k, &_sendCodec); -// if (strncmp(_sendCodec.plName, "VP8", 3) == 0) -// { -// // Only test with VP8 -// continue; -// } -// rtpModule.RegisterSendPayload(_sendCodec.plName, _sendCodec.plType); -// // Make sure we only get one NAL unit per packet -// _vcm->InitializeSender(); -// _vcm->RegisterSendCodec(&_sendCodec, 4, targetPayloadSize); -// sendCallback.SetMaxPayloadSize(targetPayloadSize); -// _vcm->RegisterTransportCallback(&encCompleteCallback); -// sendCallback.Reset(); -// _frameCnt = 0; -// rewind(_sourceFile); -// while (!feof(_sourceFile)) -// { -// fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); -// _frameCnt++; -// sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); -// sourceFrame.SetHeight(_height); -// sourceFrame.SetWidth(_width); -// _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); -// sourceFrame.SetTimeStamp(_timeStamp); -// ret = _vcm->AddVideoFrame(sourceFrame); -// } // first frame encoded -// printf ("\n Codec type = %s \n",_sendCodec.plName); -// printf(" Average payload size = %f bytes, target = %u bytes\n", sendCallback.AveragePayloadSize(), targetPayloadSize); -// } // end for all codecs - - - // Test temporal decimation settings - for (int k = 0; k < NumberOfCodecs; k++) - { - _vcm->Codec(k, &_sendCodec); - if (strncmp(_sendCodec.plName, "I420", 4) == 0) - { - // Only test with I420 - break; - } - } - TEST(strncmp(_sendCodec.plName, "I420", 4) == 0); - _vcm->InitializeSender(); - _sendCodec.maxFramerate = static_cast(_frameRate / 2.0 + 0.5f); - _vcm->RegisterSendCodec(&_sendCodec, 4, 1440); - _vcm->SetChannelParameters(2000, 0, 0); - _vcm->RegisterTransportCallback(_encodeCompleteCallback); - // up to here - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 20); - _encodeCompleteCallback->Initialize(); - sendStats.SetTargetFrameRate(static_cast(_frameRate)); - _vcm->RegisterSendStatisticsCallback(&sendStats); - rewind(_sourceFile); - while (!feof(_sourceFile)) - { - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); - sourceFrame.SetTimeStamp(_timeStamp); - ret = _vcm->AddVideoFrame(sourceFrame); - if (_vcm->TimeUntilNextProcess() <= 0) - { - _vcm->Process(); - } - IncrementDebugClock(_frameRate); - } // first frame encoded - - RtpRtcp::DestroyRtpRtcp(&rtpModule); - Print(); - delete tmpBuffer; - delete _decodeCallback; - delete _encodeCompleteCallback; - return 0; -} - - -void -GenericCodecTest::Print() -{ - printf(" \n\n VCM Generic Encoder Test: \n\n%i tests completed\n", vcmMacrosTests); - if (vcmMacrosErrors > 0) - { - printf("%i FAILED\n\n", vcmMacrosErrors); - } - else - { - printf("ALL PASSED\n\n"); - } -} - -float -GenericCodecTest::WaitForEncodedFrame() const -{ - WebRtc_Word64 startTime = TickTime::MillisecondTimestamp(); - while (TickTime::MillisecondTimestamp() - startTime < kMaxWaitEncTimeMs*10) - { - if (_encodeCompleteCallback->EncodeComplete()) - { - return _encodeCompleteCallback->EncodedBytes(); - } - } - return 0; -} - -void -GenericCodecTest::IncrementDebugClock(float frameRate) -{ - for (int t= 0; t < 1000/frameRate; t++) - { - VCMTickTime::IncrementDebugClock(); - } - return; -} - -int -RTPSendCallback_SizeTest::SendPacket(int channel, const void *data, int len) -{ - _nPackets++; - _payloadSizeSum += len; - // Make sure no payloads (len - header size) are larger than maxPayloadSize - TEST(len > 0 && static_cast(len - 12) <= _maxPayloadSize); - return 0; -} - -void -RTPSendCallback_SizeTest::SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize) -{ - _maxPayloadSize = maxPayloadSize; -} - -void -RTPSendCallback_SizeTest::Reset() -{ - _nPackets = 0; - _payloadSizeSum = 0; -} - -float -RTPSendCallback_SizeTest::AveragePayloadSize() const -{ - if (_nPackets > 0) - { - return _payloadSizeSum / static_cast(_nPackets); - } - return 0; -} - -WebRtc_Word32 -VCMEncComplete_KeyReqTest::SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader) -{ - WebRtcRTPHeader rtpInfo; - rtpInfo.header.markerBit = true; // end of frame - rtpInfo.type.Video.codec = kRTPVideoVP8; - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.sequenceNumber = _seqNo; - _seqNo += 2; - rtpInfo.header.ssrc = 0; - rtpInfo.header.timestamp = _timeStamp; - _timeStamp += 3000; - rtpInfo.type.Video.isFirstPacket = false; - rtpInfo.frameType = kVideoFrameKey; - return _vcm.IncomingPacket(payloadData, payloadSize, rtpInfo); -} diff --git a/modules/video_coding/main/test/generic_codec_test.h b/modules/video_coding/main/test/generic_codec_test.h deleted file mode 100644 index fa1473a03..000000000 --- a/modules/video_coding/main/test/generic_codec_test.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_ - -#include "video_coding.h" -#include "test_macros.h" -#include "test_util.h" - -#include -#include - -/* -Test consists of: -1. Sanity checks -2. Bit rate validation -3. Encoder control test / General API functionality -4. Decoder control test / General API functionality - -*/ -int VCMGenericCodecTest(CmdArgs& args); - -class GenericCodecTest -{ -public: - GenericCodecTest(webrtc::VideoCodingModule* vcm); - ~GenericCodecTest(); - static int RunTest(CmdArgs& args); - WebRtc_Word32 Perform(CmdArgs& args); - float WaitForEncodedFrame() const; - -private: - void Setup(CmdArgs& args); - void Print(); - WebRtc_Word32 TearDown(); - void IncrementDebugClock(float frameRate); - - webrtc::VideoCodingModule* _vcm; - webrtc::VideoCodec _sendCodec; - webrtc::VideoCodec _receiveCodec; - std::string _inname; - std::string _outname; - std::string _encodedName; - WebRtc_Word32 _sumEncBytes; - FILE* _sourceFile; - FILE* _decodedFile; - FILE* _encodedFile; - WebRtc_UWord16 _width; - WebRtc_UWord16 _height; - float _frameRate; - WebRtc_UWord32 _lengthSourceFrame; - WebRtc_UWord32 _timeStamp; - int vcmMacrosTests; - int vcmMacrosErrors; - VCMDecodeCompleteCallback* _decodeCallback; - VCMEncodeCompleteCallback* _encodeCompleteCallback; - -}; // end of GenericCodecTest class definition - -class RTPSendCallback_SizeTest : public webrtc::Transport -{ -public: - // constructor input: (receive side) rtp module to send encoded data to - RTPSendCallback_SizeTest() : _maxPayloadSize(0), _payloadSizeSum(0), _nPackets(0) {} - virtual int SendPacket(int channel, const void *data, int len); - virtual int SendRTCPPacket(int channel, const void *data, int len) {return 0;} - void SetMaxPayloadSize(WebRtc_UWord32 maxPayloadSize); - void Reset(); - float AveragePayloadSize() const; -private: - WebRtc_UWord32 _maxPayloadSize; - WebRtc_UWord32 _payloadSizeSum; - WebRtc_UWord32 _nPackets; -}; - -class VCMEncComplete_KeyReqTest : public webrtc::VCMPacketizationCallback -{ -public: - VCMEncComplete_KeyReqTest(webrtc::VideoCodingModule &vcm) : _vcm(vcm), _seqNo(0), _timeStamp(0) {} - WebRtc_Word32 SendData(const webrtc::FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader); -private: - webrtc::VideoCodingModule& _vcm; - WebRtc_UWord16 _seqNo; - WebRtc_UWord32 _timeStamp; -}; // end of VCMEncodeCompleteCallback - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_GENERIC_CODEC_TEST_H_ diff --git a/modules/video_coding/main/test/jitter_buffer_test.cc b/modules/video_coding/main/test/jitter_buffer_test.cc deleted file mode 100644 index 87d708efc..000000000 --- a/modules/video_coding/main/test/jitter_buffer_test.cc +++ /dev/null @@ -1,2252 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "common_types.h" -#include "jitter_buffer.h" -#include "jitter_estimator.h" -#include "inter_frame_delay.h" -#include "packet.h" -#include "tick_time.h" -#include "../source/event.h" -#include "frame_buffer.h" -#include "jitter_estimate_test.h" -#include "test_macros.h" -#include "test_util.h" -#include -#include - -using namespace webrtc; - -void CheckOutFrame(VCMEncodedFrame* frameOut, int size, bool startCode) -{ - TEST(frameOut != 0); - - const WebRtc_UWord8* outData = frameOut->Buffer(); - - int i = 0; - - if(startCode) - { - TEST(outData[0] == 0); - TEST(outData[1] == 0); - TEST(outData[2] == 0); - TEST(outData[3] == 1); - i+= 4; - } - - // check the frame data - int count = 3; - int layer = 0; - - // check the frame length - TEST(frameOut->Length() == size); - - for(; i < size; i++) - { - if (outData[i] == 0 && outData[i+1] == 0 && outData[i+2] == 0x80) - { - i += 2; - } - else if(startCode && outData[i] == 0 && outData[i+1] == 0) - { - TEST(outData[i] == 0); - TEST(outData[i+1] == 0); - TEST(outData[i+2] == 0); - TEST(outData[i+3] == 1); - i += 3; - } - else - { - if (outData[i] != count) - { - int a=0; - } - TEST(outData[i] == count); - count++; - if(count == 10) - { - count = 3; - } - } - } -} - - -int JitterBufferTest(CmdArgs& args) -{ - // Don't run these tests with debug time -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - return -1; -#endif - - // Start test - WebRtc_UWord16 seqNum = 1234; - WebRtc_UWord32 timeStamp = 0; - int size = 1400; - WebRtc_UWord8 data[1500]; - VCMPacket packet(data, size, seqNum, timeStamp, true); - - VCMFrameListTimestampOrderAsc frameList; - VCMFrameBuffer* fb = NULL; - for (int i=0; i < 100; i++) - { - fb = new VCMFrameBuffer(); - fb->SetState(kStateEmpty); - packet.timestamp = 0xfffffff0 + i; - packet.seqNum = seqNum; - packet.payloadType = 126; - seqNum++; - fb->InsertPacket(packet, VCMTickTime::MillisecondTimestamp()); - TEST(frameList.Insert(fb) == 0); - } - VCMFrameListItem* item = NULL; - WebRtc_UWord32 prevTimestamp = 0; - int i = 0; - for (i=0; !frameList.Empty(); i++) - { - item = frameList.First(); - fb = static_cast(item->GetItem()); - TEST(i > 0 || fb->TimeStamp() == 0xfffffff0); // Frame 0 has no prev - TEST(prevTimestamp - fb->TimeStamp() == -1 || i == 0); - prevTimestamp = fb->TimeStamp(); - frameList.Erase(item); - delete fb; - } - TEST(i == 100); - - //printf("DONE timestamp ordered frame list\n"); - - VCMJitterBuffer jb; - - seqNum = 1234; - timeStamp = 123*90; - VCMFrameBufferEnum retVal(kNoError); - FrameType incomingFrameType(kVideoFrameKey); - VCMEncodedFrame* frameOut=NULL; - WebRtc_Word64 renderTimeMs = 0; - WebRtc_UWord8 payloadType = 0; - packet.timestamp = timeStamp; - packet.seqNum = seqNum; - - // build a data vector with 0, 0, 0x80, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0x80, 3.... - data[0] = 0; - data[1] = 0; - data[2] = 0x80; - int count = 3; - for(int i = 3; i < sizeof(data) - 3; ++i ) - { - data[i] = count; - count++; - if(count == 10) - { - data[i+1] = 0; - data[i+2] = 0; - data[i+3] = 0x80; - count = 3; - i += 3; - } - } - - // Test out of range inputs - TEST(kSizeError == jb.InsertPacket(0, packet)); - jb.ReleaseFrame(0); - - // Not started - TEST(0 == jb.GetFrame(packet)); - TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(0 == jb.GetCompleteFrameForDecoding(10)); - TEST(0 == jb.GetFrameForDecoding()); - - // Start - jb.Start(); - - // Get frame to use for this timestamp - VCMEncodedFrame* frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // No packets inserted - TEST(0 == jb.GetCompleteFrameForDecoding(10)); - - - // - // TEST single packet frame - // - // -------- - // | 1234 | - // -------- - - // packet.frameType; - // packet.dataPtr; - // packet.sizeBytes; - // packet.timestamp; - // packet.seqNum; - // packet.isFirstPacket; - // packet.markerBit; - // - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 1 packet\n"); - - // - // TEST dual packet frame - // - // ----------------- - // | 1235 | 1236 | - // ----------------- - // - - seqNum++; - timeStamp += 33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 2 packets\n"); - - - // - // TEST 100 packets frame Key frame - // - // ---------------------------------- - // | 1237 | 1238 | .... | 1336 | - // ---------------------------------- - - // insert first packet - timeStamp += 33*90; - seqNum++; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameKey); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 frames - int loop = 0; - do - { - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - loop++; - } while (loop < 98); - - // insert last packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*100, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameKey); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE key frame 100 packets\n"); - - // - // TEST 100 packets frame Delta frame - // - // ---------------------------------- - // | 1337 | 1238 | .... | 1436 | - // ---------------------------------- - - // insert first packet - timeStamp += 33*90; - seqNum++; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 frames - loop = 0; - do - { - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - loop++; - } while (loop < 98); - - // insert last packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*100, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 100 packets\n"); - - // - // TEST packet re-ordering reverse order - // - // ---------------------------------- - // | 1437 | 1238 | .... | 1536 | - // ---------------------------------- - // <---------- - - // insert "first" packet last seqnum - timeStamp += 33*90; - seqNum += 100; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 packets - loop = 0; - do - { - seqNum--; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - loop++; - } while (loop < 98); - - // insert last packet - seqNum--; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*100, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 100 packets reverse order\n"); - - seqNum+= 100; - - // - // TEST frame re-ordering 2 frames 2 packets each - // - // ----------------- ----------------- - // | 1539 | 1540 | | 1537 | 1538 | - // ----------------- ----------------- - - seqNum += 2; - timeStamp += 2* 33 * 90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // check that we fail to get frame since seqnum is not continuous - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(frameOut == 0); - - seqNum -= 3; - timeStamp -= 33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - seqNum += 2; - //printf("DONE frame re-ordering 2 frames 2 packets\n"); - - // - // TEST H.263 bits - // - // ----------------- - // | 1541 | 1542 | - // ----------------- - // sBits - - seqNum++; - timeStamp += 2*33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.bits = false; - packet.codec = kVideoCodecH263; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.bits = true; - packet.dataPtr = &(data[9]); - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, (size*2)-1, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - // restore - packet.dataPtr = data; - packet.bits = false; - packet.codec = kVideoCodecUnknown; - //printf("DONE H.263 frame 2 packets with bits\n"); - - // - // TEST duplicate packets - // - // ----------------- - // | 1543 | 1543 | - // ----------------- - // - - seqNum++; - timeStamp += 33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - packet.isFirstPacket = false; - packet.markerBit = true; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kDuplicatePacket == jb.InsertPacket(frameIn, packet)); - - seqNum++; - packet.seqNum = seqNum; - - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE test duplicate packets\n"); - - // - // TEST H.264 insert start code - // - // ----------------- - // | 1544 | 1545 | - // ----------------- - // insert start code, both packets - - seqNum++; - timeStamp += 33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.insertStartCode = true; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2+4*2, true); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - // reset - packet.insertStartCode = false; - //printf("DONE H.264 insert start code test 2 packets\n"); - - - // Temporarily do this to make the rest of the test work: - timeStamp += 33*90; - seqNum += 4; - - // - // TEST statistics - // - WebRtc_UWord32 numDeltaFrames = 0; - WebRtc_UWord32 numKeyFrames = 0; - TEST(jb.GetFrameStatistics(numDeltaFrames, numKeyFrames) == 0); - - TEST(numDeltaFrames == 9); - TEST(numKeyFrames == 1); - - WebRtc_UWord32 frameRate; - WebRtc_UWord32 bitRate; - TEST(jb.GetUpdate(frameRate, bitRate) == 0); - - // these depend on CPU speed works on a T61 - TEST(frameRate > 30); - TEST(bitRate > 10000000); - - //printf("DONE Statistics\n"); - - // - // TEST delta frame 100 packets with seqNum wrap - // - // --------------------------------------- - // | 65520 | 65521 | ... | 82 | 83 | - // --------------------------------------- - // - - // test flush - jb.Flush(); - - // insert first packet - timeStamp += 33*90; - seqNum = 0xfff0; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 packets - loop = 0; - do - { - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(2, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(2); - - // it should not be complete - TEST(frameOut == 0); - - loop++; - } while (loop < 98); - - // insert last packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*100, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 100 packets with wrap in seqNum\n"); - - // - // TEST packet re-ordering reverse order with neg seqNum warp - // - // ---------------------------------------- - // | 65447 | 65448 | ... | 9 | 10 | - // ---------------------------------------- - // <------------- - - // test flush - jb.Flush(); - - // insert "first" packet last seqnum - timeStamp += 33*90; - seqNum = 10; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 frames - loop = 0; - do - { - seqNum--; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(2, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(2); - - // it should not be complete - TEST(frameOut == 0); - - loop++; - } while (loop < 98); - - // insert last packet - seqNum--; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*100, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 100 packets reverse order with wrap in seqNum \n"); - - // test flush - jb.Flush(); - - // - // TEST packet re-ordering with seqNum wrap - // - // ----------------------- - // | 1 | 65535 | 0 | - // ----------------------- - - // insert "first" packet last seqnum - timeStamp += 33*90; - seqNum = 1; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert last packet - seqNum -= 2; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*3, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE delta frame 3 packets re-ordering with wrap in seqNum \n"); - - // test flush - jb.Flush(); - - // - // TEST insert old frame - // - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 3000 t = 2000 - - seqNum = 2; - timeStamp = 3000; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(3000 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(3000 == frameOut->TimeStamp()); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - seqNum--; - timeStamp = 2000; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - // Changed behavior, never insert packets into frames older than the - // last decoded frame. - TEST(frameIn == 0); - - //printf("DONE insert old frame\n"); - - jb.Flush(); - - // - // TEST insert old frame with wrap in timestamp - // - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 3000 t = 0xffffff00 - - seqNum = 2; - timeStamp = 3000; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(timeStamp == frameOut->TimeStamp()); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - seqNum--; - timeStamp = 0xffffff00; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - // This timestamp is old - TEST(frameIn == 0); - - jb.Flush(); - - // - // TEST wrap in timeStamp - // - // --------------- --------------- - // | 1 | 2 | | 3 | 4 | - // --------------- --------------- - // t = 0xffffff00 t = 33*90 - - seqNum = 1; - timeStamp = 0xffffff00; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - seqNum++; - timeStamp += 33*90; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size*2, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - //printf("DONE time stamp wrap 2 frames 2 packets\n"); - - jb.Flush(); - - // - // TEST insert 2 frames with wrap in timeStamp - // - // ------- ------- - // | 1 | | 2 | - // ------- ------- - // t = 0xffffff00 t = 2700 - - seqNum = 1; - timeStamp = 0xffffff00; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert first frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // Get packet notification - TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Insert next frame - seqNum++; - timeStamp = 2700; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // Get packet notification - TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get frame - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(0xffffff00 == frameOut->TimeStamp()); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // Get packet notification - TEST(2700 == jb.GetNextTimeStamp(0, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get frame - VCMEncodedFrame* frameOut2 = jb.GetCompleteFrameForDecoding(10); - TEST(2700 == frameOut2->TimeStamp()); - - CheckOutFrame(frameOut2, size, false); - - // check the frame type - TEST(frameOut2->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - jb.ReleaseFrame(frameOut2); - - //printf("DONE insert 2 frames (1 packet) with wrap in timestamp\n"); - - jb.Flush(); - - // - // TEST insert 2 frames re-ordered with wrap in timeStamp - // - // ------- ------- - // | 2 | | 1 | - // ------- ------- - // t = 2700 t = 0xffffff00 - - seqNum = 2; - timeStamp = 2700; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert first frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // Get packet notification - TEST(2700 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Insert second frame - seqNum--; - timeStamp = 0xffffff00; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // Get packet notification - TEST(0xffffff00 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get frame - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(0xffffff00 == frameOut->TimeStamp()); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameDelta); - - // get packet notification - TEST(2700 == jb.GetNextTimeStamp(0, incomingFrameType, renderTimeMs)); - TEST(kVideoFrameDelta == incomingFrameType); - - // Get frame - frameOut2 = jb.GetCompleteFrameForDecoding(10); - TEST(2700 == frameOut2->TimeStamp()); - - CheckOutFrame(frameOut2, size, false); - - // check the frame type - TEST(frameOut2->FrameType() == kVideoFrameDelta); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - jb.ReleaseFrame(frameOut2); - - //printf("DONE insert 2 frames (1 packet) re-ordered with wrap in timestamp\n"); - - // - // TEST NACK - // - // --------------------------------------------------------------------------------------------- - // | 3 | 4 | 5 | 6 | 7 | 9 | x | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | x | 21 |.....| 102 | - // --------------------------------------------------------------------------------------------- - jb.SetNackMode(kNackInfinite); - - TEST(jb.GetNackMode() == kNackInfinite); - - // insert first packet - timeStamp += 33*90; - seqNum += 2; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameKey); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 packets - loop = 0; - do - { - seqNum++; - if(seqNum % 10 != 0) - { - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - } - loop++; - } while (loop < 98); - - // insert last packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // try to get the frame, should fail - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(frameOut == 0); - - // try to get the frame, should fail - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == 0); - - WebRtc_UWord16 nackSize = 0; - bool extended = false; - WebRtc_UWord16* list = jb.GetNackList(nackSize, extended); - - TEST(nackSize == 10); - - for(int i = 0; i < nackSize; i++) - { - TEST(list[i] == (1+i)*10); - } - - jb.Stop(); - - //printf("DONE NACK\n"); - - // - // TEST NACK with wrap in seqNum - // - // ------- ----------------------------------------------------------------------------------- - // | 65532 | | 65533 | 65534 | 65535 | x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | x | 11 |.....| 96 | - // ------- ----------------------------------------------------------------------------------- - - jb.Flush(); - jb.Start(); - - // insert first frame - timeStamp = 33*90; - seqNum = 65532; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameKey); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameKey); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - - // insert first packet - timeStamp += 33*90; - seqNum++; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - - // it should not be complete - TEST(frameOut == 0); - - // insert 98 packets - loop = 0; - do - { - seqNum++; - if (seqNum % 10 != 0) - { - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // try to get the frame, should fail - frameOut = jb.GetCompleteFrameForDecoding(1); - TEST(frameOut == 0); - - // try to get the frame, should fail - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == 0); - } - loop++; - } while (loop < 98); - - // insert last packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // try to get the frame, should fail - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(frameOut == 0); - - // try to get the frame, should fail - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == 0); - - nackSize = 0; - list = jb.GetNackList(nackSize, extended); - - TEST(nackSize == 10); - - for(int i = 0; i < nackSize; i++) - { - TEST(list[i] == i*10); - } - - jb.Stop(); - - //printf("DONE NACK with wrap in seqNum\n"); - - // - // TEST delta frame with more than max number of packets - // - - jb.Start(); - - loop = 0; - packet.timestamp += 33*90; - bool firstPacket = true; - // insert kMaxPacketsInJitterBuffer into frame - do - { - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert frame - if (firstPacket) - { - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - firstPacket = false; - } - else - { - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - } - - // get packet notification - TEST(packet.timestamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - loop++; - } while (loop < kMaxPacketsInJitterBuffer); - - // Max number of packets inserted - - // Insert one more packet - seqNum++; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert the packet -> frame recycled - TEST(kSizeError == jb.InsertPacket(frameIn, packet)); - - // should fail - TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(0 == jb.GetCompleteFrameForDecoding(10)); - - //printf("DONE fill frame - packets > max number of packets\n"); - - // - // TEST fill JB with more than max number of delta frame - // - - loop = 0; - WebRtc_UWord32 timeStampStart = timeStamp + 33*90; - // insert MAX_NUMBER_OF_FRAMES frames - do - { - timeStamp += 33*90; - seqNum++; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - // Insert frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // get packet notification - TEST(timeStampStart == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - loop++; - } while (loop < kMaxNumberOfFrames); - - // Max number of frames inserted - - // Insert one more frame - timeStamp += 33*90; - seqNum++; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - - // Now, no free frame - frames will be recycled until first key frame - frameIn = jb.GetFrame(packet); - TEST(frameIn); // no key, so we have recycled all frames - - //printf("DONE fill JB - number of delta frames > max number of frames\n"); - - // - // TEST fill JB with more than max number of frame (50 delta frames + - // 51 key frames) with wrap in seqNum - // - // -------------------------------------------------------------- - // | 65485 | 65486 | 65487 | .... | 65535 | 0 | 1 | 2 | .....| 50 | - // -------------------------------------------------------------- - // |<-----------delta frames------------->|<------key frames----->| - - jb.Flush(); - - loop = 0; - seqNum = 65485; - timeStampStart = timeStamp + 33*90; - WebRtc_UWord32 timeStampFirstKey = 0; - VCMEncodedFrame* ptrLastDeltaFrame; - VCMEncodedFrame* ptrFirstKeyFrame; - // insert MAX_NUMBER_OF_FRAMES frames - do - { - timeStamp += 33*90; - seqNum++; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - frameIn = jb.GetFrame(packet); - TEST(frameIn != 0); - - if (loop == 49) // last delta - { - ptrLastDeltaFrame = frameIn; - } - if (loop == 50) // first key - { - ptrFirstKeyFrame = frameIn; - packet.frameType = kVideoFrameKey; - timeStampFirstKey = packet.timestamp; - } - - // Insert frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // Get packet notification, should be first inserted frame - TEST(timeStampStart == jb.GetNextTimeStamp(10, incomingFrameType, - renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameDelta); - - loop++; - } while (loop < kMaxNumberOfFrames); - - // Max number of frames inserted - - // Insert one more frame - timeStamp += 33*90; - seqNum++; - packet.isFirstPacket = true; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - - // Now, no free frame - frames will be recycled until first key frame - frameIn = jb.GetFrame(packet); - // ptr to last inserted delta frame should be returned - TEST(frameIn != 0 && frameIn && ptrLastDeltaFrame); - - // Insert frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - // First inserted key frame should be oldest in buffer - TEST(timeStampFirstKey == jb.GetNextTimeStamp(10, incomingFrameType, - renderTimeMs)); - - // check incoming frame type - TEST(incomingFrameType == kVideoFrameKey); - - // get the first key frame - frameOut = jb.GetCompleteFrameForDecoding(10); - TEST(ptrFirstKeyFrame == frameOut); - - CheckOutFrame(frameOut, size, false); - - // check the frame type - TEST(frameOut->FrameType() == kVideoFrameKey); - - // Release frame (when done with decoding) - jb.ReleaseFrame(frameOut); - - jb.Flush(); - - // printf("DONE fill JB - nr of delta + key frames (w/ wrap in seqNum) > - // max nr of frames\n"); - - // Test handling empty packets - // first insert 2 empty packets - jb.ReleaseFrame(frameIn); - timeStamp = 33 * 90; - seqNum = 5; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kFrameEmpty; - frameIn = jb.GetFrame(packet); - TEST(frameIn); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - seqNum = 6; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kFrameEmpty; - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - // now insert the first data packet - seqNum = 1; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kVideoFrameDelta; - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - // insert an additional data packet - seqNum = 2; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kVideoFrameDelta; - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - // insert the last packet and verify frame completness - // (even though packet 4 (empty) is missing) - seqNum = 3; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kVideoFrameDelta; - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - jb.Flush(); - - // testing that empty packets do not clog the jitter buffer - // Set hybrid mode - jb.SetNackMode(kNackHybrid); - TEST(jb.GetNackMode() == kNackHybrid); - - int maxSize = 100; - seqNum = 3; - VCMEncodedFrame* testFrame; - for (int i = 0; i < maxSize + 10; i++) - { - timeStamp += 33 * 90; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kFrameEmpty; - testFrame = jb.GetFrame(packet); - TEST(frameIn != 0); - TEST(kFirstPacket == jb.InsertPacket(testFrame, packet)); - } - // verify insertion of a data packet (old empty frames will be flushed) - timeStamp += 33 * 90; - packet.isFirstPacket = true; - packet.markerBit = false; - packet.seqNum = seqNum; - packet.timestamp = timeStamp; - packet.frameType = kFrameEmpty; - testFrame = jb.GetFrame(packet); - TEST(frameIn != 0); - - jb.SetNackMode(kNoNack); - - - // printf(DONE testing inserting empty packets to the JB) - - - // H.264 tests - //Test incomplete NALU frames - - jb.Flush(); - jb.SetNackMode(kNoNack); - seqNum ++; - timeStamp += 33*90; - int insertedLength=0; - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.completeNALU=kNaluStart; - packet.markerBit=false; - - frameIn=jb.GetFrame(packet); - - // Insert a packet into a frame - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - seqNum+=2; // Skip one packet - packet.seqNum=seqNum; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluIncomplete; - packet.markerBit=false; - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - seqNum++; - packet.seqNum=seqNum; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluEnd; - packet.markerBit=false; - - // Insert a packet into a frame - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - seqNum++; - packet.seqNum=seqNum; - packet.completeNALU=kNaluComplete; - packet.markerBit=true; // Last packet - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - - - // get packet notification - TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - frameOut = jb.GetFrameForDecoding(); - - // We can decode everything from a NALU until a packet has been lost. - // Thus we can decode the first packet of the first NALU and the second NALU - // which consists of one packet. - CheckOutFrame(frameOut, packet.sizeBytes * 2, false); - jb.ReleaseFrame(frameOut); - - // Test reordered start frame + 1 lost - seqNum +=2; // Reoreder 1 frame - timeStamp += 33*90; - insertedLength=0; - - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluEnd; - packet.markerBit=false; - - TEST(frameIn=jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - insertedLength+=packet.sizeBytes; // This packet should be decoded - - seqNum--; - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.completeNALU=kNaluStart; - packet.markerBit=false; - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - insertedLength+=packet.sizeBytes; // This packet should be decoded - - seqNum+=3; // One packet drop - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluComplete; - packet.markerBit=false; - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - insertedLength+=packet.sizeBytes; // This packet should be decoded - - seqNum+=1; - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluStart; - packet.markerBit=false; - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - // This packet should be decoded since it's the beginning of a NAL - insertedLength+=packet.sizeBytes; - - seqNum+=2; - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = false; - packet.completeNALU=kNaluEnd; - packet.markerBit=true; - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - // This packet should not be decoded because it is an incomplete NAL if it - // is the last - insertedLength+=0; - - frameOut = jb.GetFrameForDecoding(); - // Only last NALU is complete - CheckOutFrame(frameOut, insertedLength, false); - jb.ReleaseFrame(frameOut); - - - //Test to insert empty packet - seqNum+=1; - timeStamp += 33*90; - VCMPacket emptypacket(data, 0, seqNum, timeStamp, true); - emptypacket.seqNum=seqNum; - emptypacket.timestamp=timeStamp; - emptypacket.frameType = kVideoFrameKey; - emptypacket.isFirstPacket = true; - emptypacket.completeNALU=kNaluComplete; - emptypacket.markerBit=true; - TEST(frameIn=jb.GetFrame(emptypacket)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, emptypacket)); - // This packet should not be decoded because it is an incomplete NAL if it - // is the last - insertedLength+=0; - - TEST(-1 == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs)); - TEST(NULL==jb.GetFrameForDecoding()); - - - // Test that a frame can include an empty packet. - seqNum+=1; - timeStamp += 33*90; - - packet.seqNum=seqNum; - packet.timestamp=timeStamp; - packet.frameType = kVideoFrameKey; - packet.isFirstPacket = true; - packet.completeNALU=kNaluComplete; - packet.markerBit=false; - TEST(frameIn=jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - seqNum+=1; - emptypacket.seqNum=seqNum; - emptypacket.timestamp=timeStamp; - emptypacket.frameType = kVideoFrameKey; - emptypacket.isFirstPacket = true; - emptypacket.completeNALU=kNaluComplete; - emptypacket.markerBit=true; - TEST(kCompleteSession == jb.InsertPacket(frameIn, emptypacket)); - - // get the frame - frameOut = jb.GetCompleteFrameForDecoding(10); - // Only last NALU is complete - CheckOutFrame(frameOut, packet.sizeBytes, false); - jb.Flush(); - - // Three reordered H263 packets with bits. - - packet.codec = kVideoCodecH263; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = false; - packet.bits = true; - packet.seqNum += 1; - WebRtc_UWord8 oldData1 = data[0]; - WebRtc_UWord8 oldData2 = data[packet.sizeBytes - 1]; - unsigned char startByte = 0x07; - unsigned char endByte = 0xF8; - data[0] = startByte; - TEST(frameIn = jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == NULL); - - packet.seqNum -= 1; - packet.isFirstPacket = true; - packet.bits = false; - data[0] = oldData1; - data[packet.sizeBytes - 1] = endByte; - TEST(frameIn = jb.GetFrame(packet)); - TEST(kIncomplete == jb.InsertPacket(frameIn, packet)); - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == NULL); - - packet.seqNum += 2; - packet.isFirstPacket = false; - packet.markerBit = true; - data[packet.sizeBytes - 1] = oldData2; - TEST(frameIn = jb.GetFrame(packet)); - TEST(kCompleteSession == jb.InsertPacket(frameIn, packet)); - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut != NULL); - //CheckOutFrame(frameOut, packet.sizeBytes * 3 - 1, false); - const WebRtc_UWord8* buf = frameOut->Buffer(); - TEST(buf[packet.sizeBytes - 1] == (startByte | endByte)); - - // First packet lost, second packet with bits. - - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = true; - packet.bits = true; - packet.seqNum += 2; - packet.timestamp += 33*90; - data[0] = 0x07; - data[packet.sizeBytes - 1] = 0xF8; - //unsigned char startByte = packet.dataPtr[0]; - //unsigned char endByte = packet.dataPtr[packet.sizeBytes-1]; - TEST(frameIn = jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut != NULL); - TEST(frameOut->Length() == 0); - - data[0] = oldData1; - data[packet.sizeBytes - 1] = oldData2; - packet.codec = kVideoCodecUnknown; - jb.Flush(); - - // Test that a we cannot get incomplete frames from the JB if we haven't received - // the marker bit, unless we have received a packet from a later timestamp. - - packet.seqNum +=2; - packet.bits = false; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = false; - - TEST(frameIn = jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut == NULL); - - packet.seqNum += 2; - packet.timestamp += 33*90; - - TEST(frameIn = jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - frameOut = jb.GetFrameForDecoding(); - - TEST(frameOut != NULL); - CheckOutFrame(frameOut, packet.sizeBytes, false); - - jb.Flush(); - - // Test that a we can get incomplete frames from the JB if we have received - // the marker bit. - packet.seqNum +=2; - packet.frameType = kVideoFrameDelta; - packet.isFirstPacket = false; - packet.markerBit = true; - - TEST(frameIn = jb.GetFrame(packet)); - TEST(kFirstPacket == jb.InsertPacket(frameIn, packet)); - - frameOut = jb.GetFrameForDecoding(); - TEST(frameOut != NULL); - - // --- - jb.Stop(); - - printf("DONE !!!\n"); - EventWrapper* waitEvent = EventWrapper::Create(); - waitEvent->Wait(5000); - - return 0; - -} diff --git a/modules/video_coding/main/test/jitter_estimate_test.cc b/modules/video_coding/main/test/jitter_estimate_test.cc deleted file mode 100644 index 419ca3541..000000000 --- a/modules/video_coding/main/test/jitter_estimate_test.cc +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include "JitterEstimateTest.h" -#include "tick_time.h" - -using namespace webrtc; - -JitterEstimateTest::JitterEstimateTest(unsigned int frameRate) : -_frameRate(frameRate), -_capacity(2000), -_rate(500), -_jitter(5, 0), -_keyFrameRate(1.0), -_deltaFrameSize(10000, 1e6), -_counter(0), -_lossrate(0.0) -{ - // Assign to random value between 0 and max of unsigned int - _seed = static_cast(std::time(0)); - std::srand(_seed); - _prevTimestamp = static_cast((std::rand() + 1.0)/(RAND_MAX + 1.0)*(pow((float) 2, (long) sizeof(unsigned int)*8)-1)); - _prevWallClock = VCMTickTime::MillisecondTimestamp(); -} - -FrameSample -JitterEstimateTest::GenerateFrameSample() -{ - double increment = 1.0/_frameRate; - unsigned int frameSize = static_cast(_deltaFrameSize.RandValue()); - bool keyFrame = false; - bool resent = false; - _prevTimestamp += static_cast(90000*increment + 0.5); - double deltaFrameRate = _frameRate - _keyFrameRate; - double ratio = deltaFrameRate/static_cast(_keyFrameRate); - if (ratio < 1.0) - { - ratio = 1.0/ratio; - if (_counter >= ratio) - _counter = 0; - else - { - _counter++; - frameSize += static_cast(3*_deltaFrameSize.GetAverage()); - keyFrame = true; - } - } - else - { - if (_counter >= ratio) - { - frameSize += static_cast(3*_deltaFrameSize.GetAverage()); - _counter = 0; - keyFrame = true; - } - else - _counter++; - } - WebRtc_Word64 jitter = static_cast(_jitter.RandValue() + 1.0/_capacity * frameSize + 0.5); - _prevWallClock += static_cast(1000*increment + 0.5); - double rndValue = RandUniform(); - resent = (rndValue < _lossrate); - //printf("rndValue = %f\n", rndValue); - return FrameSample(_prevTimestamp, _prevWallClock + jitter, frameSize, keyFrame, resent); -} - -void -JitterEstimateTest::SetCapacity(unsigned int c) -{ - _capacity = c; -} - -void -JitterEstimateTest::SetRate(unsigned int r) -{ - _rate = r; -} - -void -JitterEstimateTest::SetJitter(double m, double v) -{ - _jitter.SetParams(m, v); -} - -void -JitterEstimateTest::SetFrameSizeStats(double m, double v) -{ - _deltaFrameSize.SetParams(m, v); -} - -void -JitterEstimateTest::SetKeyFrameRate(int rate) -{ - _keyFrameRate = rate; -} - -void -JitterEstimateTest::SetLossRate(double rate) -{ - _lossrate = rate; -} diff --git a/modules/video_coding/main/test/jitter_estimate_test.h b/modules/video_coding/main/test/jitter_estimate_test.h deleted file mode 100644 index cd7338afc..000000000 --- a/modules/video_coding/main/test/jitter_estimate_test.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_ - -#include "typedefs.h" -#include "jitter_buffer.h" -#include "jitter_estimator.h" -#include -#include - -double const pi = 4*std::atan(1.0); - -class GaussDist -{ -public: - GaussDist(double m, double v): _mu(m), _sigma(sqrt(v)) {} - - double RandValue() // returns a single normally distributed number - { - double r1 = (std::rand() + 1.0)/(RAND_MAX + 1.0); // gives equal distribution in (0, 1] - double r2 = (std::rand() + 1.0)/(RAND_MAX + 1.0); - return _mu + _sigma * std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2); - } - - double GetAverage() - { - return _mu; - } - - double GetVariance() - { - return _sigma*_sigma; - } - - void SetParams(double m, double v) - { - _mu = m; - _sigma = sqrt(v); - } - -private: - double _mu, _sigma; -}; - -class JitterEstimateTestWrapper : public webrtc::VCMJitterEstimator -{ -public: - JitterEstimateTestWrapper() : VCMJitterEstimator() {} - double GetTheta() { return _theta[0]; } - double GetVarNoise() { return _varNoise; } -}; - -class FrameSample -{ -public: - FrameSample() {FrameSample(0, 0, 0, false, false);} - FrameSample(unsigned int ts, WebRtc_Word64 wallClk, unsigned int fs, bool _keyFrame, bool _resent): - timestamp90Khz(ts), wallClockMs(wallClk), frameSize(fs), keyFrame(_keyFrame), resent(_resent) {} - - unsigned int timestamp90Khz; - WebRtc_Word64 wallClockMs; - unsigned int frameSize; - bool keyFrame; - bool resent; -}; - -class JitterEstimateTest -{ -public: - JitterEstimateTest(unsigned int frameRate); - FrameSample GenerateFrameSample(); - void SetCapacity(unsigned int c); - void SetRate(unsigned int r); - void SetJitter(double m, double v); - void SetFrameSizeStats(double m, double v); - void SetKeyFrameRate(int rate); - void SetLossRate(double rate); - -private: - double RandUniform() { return (std::rand() + 1.0)/(RAND_MAX + 1.0); } - unsigned int _frameRate; - unsigned int _capacity; - unsigned int _rate; - GaussDist _jitter; - //GaussDist _noResend; - GaussDist _deltaFrameSize; - unsigned int _prevTimestamp; - WebRtc_Word64 _prevWallClock; - unsigned int _nextDelay; - double _keyFrameRate; - unsigned int _counter; - unsigned int _seed; - double _lossrate; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_JITTER_ESTIMATE_TEST_H_ diff --git a/modules/video_coding/main/test/media_opt_test.cc b/modules/video_coding/main/test/media_opt_test.cc deleted file mode 100644 index f3a34d85f..000000000 --- a/modules/video_coding/main/test/media_opt_test.cc +++ /dev/null @@ -1,614 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Implementation of Media Optimization Test -// testing is done via the VCM module, no specific Media opt functionality. - -#include "receiver_tests.h" // receive side callbacks -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "test_util.h" // send side callback -#include "media_opt_test.h" -#include "../source/event.h" - -#include -#include -#include - -//#include -#include - -using namespace webrtc; - -int MediaOptTest::RunTest(int testNum, CmdArgs& args) -{ - Trace::CreateTrace(); - Trace::SetTraceFile("mediaOptTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - VideoCodingModule* vcm = VideoCodingModule::Create(1); - MediaOptTest* mot = new MediaOptTest(vcm); - if (testNum == 0) - { // regular - mot->Setup(0, args); - mot->GeneralSetup(); - mot->Perform(); - mot->Print(1);// print to screen - mot->TearDown(); - } - if (testNum == 1) - { // release test - mot->Setup(0, args); - mot->RTTest(); - } - if (testNum == 2) - { // release test, running from script - mot->Setup(1, args); - mot->GeneralSetup(); - mot->Perform(); - mot->Print(1);// print to screen - mot->TearDown(); - } - - VideoCodingModule::Destroy(vcm); - delete mot; - Trace::ReturnTrace(); - return 0; - -} - - -MediaOptTest::MediaOptTest(VideoCodingModule* vcm): -_vcm(vcm), -_width(0), -_height(0), -_lengthSourceFrame(0), -_timeStamp(0), -_frameRate(30.0f), -_nackEnabled(false), -_fecEnabled(false), -_rttMS(0), -_renderDelayMs(0), -_bitRate(300.0f), -_lossRate(0.0f), -_frameCnt(0), -_sumEncBytes(0), -_numFramesDropped(0), -_numberOfCores(4), -vcmMacrosTests(0), -vcmMacrosErrors(0) -{ - _rtp = RtpRtcp::CreateRtpRtcp(1, false); -} - -MediaOptTest::~MediaOptTest() -{ - RtpRtcp::DestroyRtpRtcp(_rtp); -} -void -MediaOptTest::Setup(int testType, CmdArgs& args) -{ - /*TEST USER SETTINGS*/ - // test parameters - _inname = args.inputFile; - if (args.outputFile == "") - _outname = "../MOTest_out.vp8"; - else - _outname = args.outputFile; - _actualSourcename = "../MOTestSource.yuv"; // actual source after frame dropping - _codecName = args.codecName; - _sendCodecType = args.codecType; - _width = args.width; - _height = args.height; - _frameRate = args.frameRate; - _bitRate = args.bitRate; - _numberOfCores = 4; - - // error resilience - _nackEnabled = false; - _fecEnabled = true; - _nackFecEnabled = false; - - _rttMS = 100; - _lossRate = 0.00*255; // no packet loss - - _testType = testType; - - //For multiple runs with script - if (_testType == 1) - { - float rateTest,lossTest; - int numRuns; - _fpinp = fopen("dat_inp","rb"); - _fpout = fopen("test_runs/dat_out","ab"); - _fpout2 = fopen("test_runs/dat_out2","ab"); - fscanf(_fpinp,"%f %f %d \n",&rateTest,&lossTest,&numRuns); - _bitRate = rateTest; - _lossRate = lossTest; - _testNum = 0; - - // for bit rates: 500, 1000, 2000, 3000,4000 - // for loss rates: 0, 1, 3, 5, 10% - _numParRuns = 25; - - _testNum = numRuns + 1; - if (rateTest == 0.0) _lossRate = 0.0; - else - { - if (rateTest == 4000) //final bit rate - { - if (lossTest == 0.1*255) _lossRate = 0.0; //start at 1% - else - if (lossTest == 0.05*255) _lossRate = 0.1*255; //final loss rate - else - if (lossTest == 0.0) _lossRate = 0.01*255; - else _lossRate = lossTest + 0.02*255; - } - } - - if (rateTest == 0.0 || rateTest == 4000) _bitRate = 500; //starting bit rate - else - if (rateTest == 500) _bitRate = 1000; - else _bitRate = rateTest + 1000; - } - // - - _renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - /* test settings end*/ - - _lengthSourceFrame = 3*_width*_height/2; - _log.open("../VCM_MediaOptLog.txt", std::fstream::out | std::fstream::app); - return; -} - -void -MediaOptTest::GeneralSetup() -{ - - WebRtc_UWord8 deltaFECRate = 0; - WebRtc_UWord8 keyFECRate = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - - if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) - { - printf("Cannot read file %s.\n", _outname.c_str()); - exit(1); - } - - if ((_actualSourceFile = fopen(_actualSourcename.c_str(), "wb")) == NULL) - { - printf("Cannot read file %s.\n", _actualSourcename.c_str()); - exit(1); - } - - if (_rtp->InitReceiver() < 0) - { - exit(1); - } - if (_rtp->InitSender() < 0) - { - exit(1); - } - if (_vcm->InitializeReceiver() < 0) - { - exit(1); - } - if (_vcm->InitializeSender()) - { - exit(1); - } - - // Registering codecs for the RTP module - - // Register receive payload - _rtp->RegisterReceivePayload("VP8", VCM_VP8_PAYLOAD_TYPE); - _rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE); - _rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE); - - // Register send payload - _rtp->RegisterSendPayload("VP8", VCM_VP8_PAYLOAD_TYPE); - _rtp->RegisterSendPayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE); - _rtp->RegisterSendPayload("RED", VCM_RED_PAYLOAD_TYPE); - - if (_nackFecEnabled == 1) - _rtp->SetGenericFECStatus(_nackFecEnabled, VCM_RED_PAYLOAD_TYPE, - VCM_ULPFEC_PAYLOAD_TYPE); - else - _rtp->SetGenericFECStatus(_fecEnabled, VCM_RED_PAYLOAD_TYPE, - VCM_ULPFEC_PAYLOAD_TYPE); - - // VCM: Registering codecs - VideoCodec sendCodec; - _vcm->InitializeSender(); - _vcm->InitializeReceiver(); - WebRtc_Word32 numberOfCodecs = _vcm->NumberOfCodecs(); - if (numberOfCodecs < 1) - { - exit(1); - } - - WebRtc_UWord8 i= 0; - if (_vcm->Codec(_sendCodecType, &sendCodec) != 0) - { - printf("Unknown codec\n"); - exit(1); - } - // register codec - sendCodec.startBitrate = (int) _bitRate; - sendCodec.height = _height; - sendCodec.width = _width; - sendCodec.maxFramerate = (WebRtc_UWord8)_frameRate; - _vcm->RegisterSendCodec(&sendCodec, _numberOfCores, 1440); - _vcm->RegisterReceiveCodec(&sendCodec, _numberOfCores); // same settings for encode and decode - - _vcm->SetRenderDelay(_renderDelayMs); - _vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - return; -} -// The following test shall be conducted under release tests - - - -WebRtc_Word32 -MediaOptTest::Perform() -{ - //Setup(); - EventWrapper* waitEvent = EventWrapper::Create(); - - // callback settings - VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(_rtp); - _vcm->RegisterTransportCallback(encodeCompleteCallback); - encodeCompleteCallback->SetCodecType(ConvertCodecType(_codecName.c_str())); - encodeCompleteCallback->SetFrameDimensions(_width, _height); - // frame ready to be sent to network - RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(_rtp); - _rtp->RegisterSendTransport(outgoingTransport); - //FrameReceiveCallback - VCMDecodeCompleteCallback receiveCallback(_decodedFile); - RtpDataCallback dataCallback(_vcm); - _rtp->RegisterIncomingDataCallback(&dataCallback); - - VCMTestProtectionCallback protectionCallback; - _vcm->RegisterProtectionCallback(&protectionCallback); - - // set error resilience / test parameters: - outgoingTransport->SetLossPct(_lossRate); - if (_nackFecEnabled == 1) - _vcm->SetVideoProtection(kProtectionNackFEC, _nackFecEnabled); - else - { - _vcm->SetVideoProtection(kProtectionNack, _nackEnabled); - _vcm->SetVideoProtection(kProtectionFEC, _fecEnabled); - } - - // START TEST - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, (WebRtc_UWord8)_lossRate, _rttMS); - _vcm->RegisterReceiveCallback(&receiveCallback); - - // inform RTP Module of error resilience features - _rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate()); - _rtp->SetNACKStatus(protectionCallback.NACKMethod()); - - _frameCnt = 0; - _sumEncBytes = 0.0; - _numFramesDropped = 0; - - while (feof(_sourceFile)== 0) - { - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - _frameCnt++; - - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_frameRate)); - sourceFrame.SetTimeStamp(_timeStamp); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - // inform RTP Module of error resilience features - //_rtp->SetFECCodeRate(protectionCallback.FECKeyRate(),protectionCallback.FECDeltaRate()); - //_rtp->SetNACKStatus(protectionCallback.NACKMethod()); - - WebRtc_Word32 ret = _vcm->Decode(); - if (ret < 0 ) - { - TEST(ret == 0); - printf ("Decode error in frame # %d",_frameCnt); - } - - float encBytes = encodeCompleteCallback->EncodedBytes(); - if (encBytes == 0) - { - _numFramesDropped += 1; - //printf("frame #%d dropped \n", _frameCnt ); - } - else - { - // write frame to file - fwrite(sourceFrame.Buffer(), 1, sourceFrame.Length(), _actualSourceFile); - } - - _sumEncBytes += encBytes; - //waitEvent->Wait(33); - } - - //END TEST - delete waitEvent; - delete encodeCompleteCallback; - delete outgoingTransport; - delete tmpBuffer; - -return 0; - -} - -void -MediaOptTest::RTTest() -{ - // will only calculate PSNR - not create output files for all - // SET UP - // Set bit rates - const float bitRateVec[] = {500, 1000, 2000,3000, 4000}; - //const float bitRateVec[] = {1000}; - // Set Packet loss values ([0,255]) - const double lossPctVec[] = {0.0*255, 0.0*255, 0.01*255, 0.01*255, 0.03*255, 0.03*255, 0.05*255, 0.05*255, 0.1*255, 0.1*255}; - const bool nackEnabledVec[] = {false , false, false, false, false, false, false, false , false, false}; - const bool fecEnabledVec[] = {false , true, false, true , false, true , false, true , false, true}; - // fec and nack are set according to the packet loss values - - const float nBitrates = sizeof(bitRateVec)/sizeof(*bitRateVec); - const float nlossPct = sizeof(lossPctVec)/sizeof(*lossPctVec); - - std::vector sources; - std::vector::iterator it; - - sources.push_back(new const VideoSource(_inname, _width, _height)); - int numOfSrc = 1; - - // constant settings (valid for entire run time) - _rttMS = 20; - _renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - - _outname = "../RTMOTest_out.yuv"; // same out name for all - _actualSourcename = "../RTMOTestSource.yuv"; // actual source after frame dropping - - _codecName = "VP8"; // for now just this one - later iterate over all codec types - _log.open("../VCM_RTMediaOptLog.txt", std::fstream::out | std::fstream::app); - _outputRes=fopen("../VCM_MediaOpt","ab"); - - //char filename[128]; - /* test settings end*/ - - // START TEST - // iterate over test sequences - printf("\n****START TEST OVER ALL RUNS ****\n"); - int runCnt = 0; - for (it = sources.begin() ; it < sources.end(); it++) - { - - // test set up - _inname = (*it)->GetFileName(); - _width = (*it)->GetWidth(); - _height = (*it)->GetHeight(); - _lengthSourceFrame = 3*_width*_height/2; - _frameRate = (*it)->GetFrameRate(); - //GeneralSetup(); - - - // iterate over all bit rates - for (int i = 0; i < nBitrates; i++) - { - _bitRate = static_cast(bitRateVec[i]); - // iterate over all packet loss values - for (int j = 0; j < nlossPct; j++) - { - _lossRate = static_cast(lossPctVec[j]); - _nackEnabled = static_cast(nackEnabledVec[j]); - _fecEnabled = static_cast(fecEnabledVec[j]); - - runCnt++; - printf("run #%d out of %d \n", runCnt,(int)(nlossPct*nBitrates*numOfSrc)); - - //printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate)); - - /* - int ch = sprintf(filename,"../test_mediaOpt/RTMOTest_%d_%d_%d_%d.yuv",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate)); - _outname = filename; - - printf("**FOR RUN: **%d %d %d %d \n",_nackEnabled,_fecEnabled,int(lossPctVec[j]),int(_bitRate)); - */ - if (_rtp != NULL) - { - RtpRtcp::DestroyRtpRtcp(_rtp); - } - _rtp = RtpRtcp::CreateRtpRtcp(1, false); - GeneralSetup(); - Perform(); - Print(1); - TearDown(); - RtpRtcp::DestroyRtpRtcp(_rtp); - _rtp = NULL; - - printf("\n"); - //printf("**DONE WITH RUN: **%d %d %f %d \n",_nackEnabled,_fecEnabled,lossPctVec[j],int(_bitRate)); - // - - }// end of packet loss loop - }// end of bit rate loop - delete *it; - }// end of video sequence loop - // at end of sequence - fclose(_outputRes); - printf("\nVCM Media Optimization Test: \n\n%i tests completed\n", vcmMacrosTests); - if (vcmMacrosErrors > 0) - { - printf("%i FAILED\n\n", vcmMacrosErrors); - } - else - { - printf("ALL PASSED\n\n"); - } -} - - -void -MediaOptTest::Print(int mode) -{ - double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate)); - double actualBitRate = ActualBitRate / 1000.0; - double psnr; - PSNRfromFiles(_actualSourcename.c_str(), _outname.c_str(), _width, _height, &psnr); - - (_log) << "VCM: Media Optimization Test Cycle Completed!" << std::endl; - (_log) << "Input file: " << _inname << std::endl; - (_log) << "Output file:" << _outname << std::endl; - ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; - (_log) << "Error Reslience: NACK:" << _nackEnabled << "; FEC: " << _fecEnabled << std::endl; - (_log) << "Packet Loss applied= %f " << _lossRate << std::endl; - (_log) << _numFramesDropped << " FRames were dropped" << std::endl; - ( _log) << "PSNR: " << psnr << std::endl; - (_log) << std::endl; - - if (_testType == 2) - { - fprintf(_outputRes,"************\n"); - fprintf(_outputRes,"\n\n\n"); - fprintf(_outputRes,"Actual bitrate: %f kbps\n", actualBitRate); - fprintf(_outputRes,"Target bitrate: %f kbps\n", _bitRate); - fprintf(_outputRes,"NACK: %s ",(_nackEnabled)?"true":"false"); - fprintf(_outputRes,"FEC: %s \n ",(_fecEnabled)?"true":"false"); - fprintf(_outputRes,"Packet loss applied = %f\n", _lossRate); - fprintf(_outputRes,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt); - fprintf(_outputRes,"PSNR: %f \n", psnr); - fprintf(_outputRes,"************\n"); - } - - - // - if (_testType == 1) - { - fprintf(_fpout,"************\n"); - fprintf(_fpout,"\n\n\n"); - fprintf(_fpout,"Actual bitrate: %f kbps\n", actualBitRate); - fprintf(_fpout,"Target bitrate: %f kbps\n", _bitRate); - fprintf(_fpout,"NACK: %s ",(_nackEnabled)?"true":"false"); - fprintf(_fpout,"FEC: %s \n ",(_fecEnabled)?"true":"false"); - fprintf(_fpout,"Packet loss applied = %f\n", _lossRate); - fprintf(_fpout,"%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt); - fprintf(_fpout,"PSNR: %f \n", psnr); - fprintf(_fpout,"************\n"); - - int testNum1 = _testNum/(_numParRuns +1) + 1; - int testNum2 = _testNum%_numParRuns; - if (testNum2 == 0) testNum2 = _numParRuns; - fprintf(_fpout2,"%d %d %f %f %f %f \n",testNum1,testNum2,_bitRate,actualBitRate,_lossRate,psnr); - fclose(_fpinp); - _fpinp = fopen("dat_inp","wb"); - fprintf(_fpinp,"%f %f %d \n",_bitRate,_lossRate,_testNum); - } - // - - - if (mode == 1) - { - // print to screen - printf("\n\n\n"); - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Target bitrate: %f kbps\n", _bitRate); - printf("NACK: %s ",(_nackEnabled)?"true":"false"); - printf("FEC: %s \n",(_fecEnabled)?"true":"false"); - printf("Packet loss applied = %f\n", _lossRate); - printf("%d frames were dropped, and total number of frames processed %d \n",_numFramesDropped,_frameCnt); - printf("PSNR: %f \n", psnr); - } - TEST(psnr > 10); // low becuase of possible frame dropping (need to verify that OK for all packet loss values/ rates) -} - -void -MediaOptTest::TearDown() -{ - _log.close(); - fclose(_sourceFile); - fclose(_decodedFile); - fclose(_actualSourceFile); - return; -} - - -VCMTestProtectionCallback::VCMTestProtectionCallback(): -_deltaFECRate(0), -_keyFECRate(0), -_nack(kNackOff) -{ - // -} - -VCMTestProtectionCallback::~VCMTestProtectionCallback() -{ - // -} - -WebRtc_Word32 -VCMTestProtectionCallback::ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack) -{ - _deltaFECRate = deltaFECRate; - _keyFECRate = keyFECRate; - if (nack == true) - { - _nack = kNackRtcp; - } - else - { - _nack = kNackOff; - } - return VCM_OK; - -} -NACKMethod -VCMTestProtectionCallback::NACKMethod() -{ - return _nack; -} - -WebRtc_UWord8 -VCMTestProtectionCallback::FECDeltaRate() -{ - return _deltaFECRate; -} - -WebRtc_UWord8 -VCMTestProtectionCallback::FECKeyRate() -{ - return _keyFECRate; -} - - -void -RTPFeedbackCallback::OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord16 bitrateTargetKbit, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord32 jitterMS, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax) -{ - - _vcm->SetChannelParameters(bitrateTargetKbit, fractionLost,(WebRtc_UWord8)roundTripTimeMs); -} diff --git a/modules/video_coding/main/test/media_opt_test.h b/modules/video_coding/main/test/media_opt_test.h deleted file mode 100644 index 8dbd2307d..000000000 --- a/modules/video_coding/main/test/media_opt_test.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// VCM Media Optimization Test -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_ - -#include "video_coding.h" -#include "test_macros.h" -#include "test_util.h" -#include "video_source.h" - -#include - -using namespace std; -// - -// media optimization test -// This test simulates a complete encode-decode cycle via the RTP module. -// allows error resilience tests, packet loss tests, etc. -// Does not test the media optimization deirectly, but via the VCM API only. -// The test allows two modes: -// 1 - Standard, basic settings, one run -// 2 - Release test - iterates over a number of video sequences, bit rates, packet loss values ,etc. - -class VCMTestProtectionCallback: public webrtc::VCMProtectionCallback -{ -public: - VCMTestProtectionCallback(); - virtual ~VCMTestProtectionCallback(); - WebRtc_Word32 ProtectionRequest(const WebRtc_UWord8 deltaFECRate, const WebRtc_UWord8 keyFECRate, const bool nack); - enum webrtc::NACKMethod NACKMethod(); - WebRtc_UWord8 FECDeltaRate(); - WebRtc_UWord8 FECKeyRate(); -private: - WebRtc_UWord8 _deltaFECRate; - WebRtc_UWord8 _keyFECRate; - enum webrtc::NACKMethod _nack; - -}; - - -class MediaOptTest -{ -public: - MediaOptTest(webrtc::VideoCodingModule* vcm); - ~MediaOptTest(); - - static int RunTest(int testNum, CmdArgs& args); - // perform encode-decode of an entire sequence - WebRtc_Word32 Perform(); - // Set up for a single mode test - void Setup(int testType, CmdArgs& args); - // General set up - applicable for both modes - void GeneralSetup(); - // Run release testing - void RTTest(); - void TearDown(); - // mode = 1; will print to screen, otherwise only to log file - void Print(int mode); - -private: - - webrtc::VideoCodingModule* _vcm; - webrtc::RtpRtcp* _rtp; - std::string _inname; - std::string _outname; - std::string _actualSourcename; - std::fstream _log; - FILE* _sourceFile; - FILE* _decodedFile; - FILE* _actualSourceFile; - FILE* _outputRes; - WebRtc_UWord16 _width; - WebRtc_UWord16 _height; - WebRtc_UWord32 _lengthSourceFrame; - WebRtc_UWord32 _timeStamp; - float _frameRate; - bool _nackEnabled; - bool _fecEnabled; - bool _nackFecEnabled; - WebRtc_UWord8 _rttMS; - float _bitRate; - double _lossRate; - WebRtc_UWord32 _renderDelayMs; - WebRtc_Word32 _frameCnt; - float _sumEncBytes; - WebRtc_Word32 _numFramesDropped; - string _codecName; - webrtc::VideoCodecType _sendCodecType; - WebRtc_Word32 _numberOfCores; - int vcmMacrosTests; - int vcmMacrosErrors; - - //for release test#2 - FILE* _fpinp; - FILE* _fpout; - FILE* _fpout2; - int _testType; - int _testNum; - int _numParRuns; - -}; // end of MediaOptTest class definition - - -// Feed back from the RTP Module callback -class RTPFeedbackCallback: public webrtc::RtpVideoFeedback -{ -public: - RTPFeedbackCallback(webrtc::VideoCodingModule* vcm) {_vcm = vcm;}; - void OnReceivedIntraFrameRequest(const WebRtc_Word32 id, - const WebRtc_UWord8 message = 0){}; - - void OnNetworkChanged(const WebRtc_Word32 id, - const WebRtc_UWord16 bitrateTargetKbit, - const WebRtc_UWord8 fractionLost, - const WebRtc_UWord16 roundTripTimeMs, - const WebRtc_UWord32 jitterMS, - const WebRtc_UWord16 bwEstimateKbitMin, - const WebRtc_UWord16 bwEstimateKbitMax); - -private: - webrtc::VideoCodingModule* _vcm; - -}; -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_MEDIA_OPT_TEST_H_ diff --git a/modules/video_coding/main/test/mt_rx_tx_test.cc b/modules/video_coding/main/test/mt_rx_tx_test.cc deleted file mode 100644 index 8d4d78257..000000000 --- a/modules/video_coding/main/test/mt_rx_tx_test.cc +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/************************************************* - * - * Testing multi thread - receive and send sides - * - **************************************************/ - -#include "receiver_tests.h" // shared RTP state and receive side threads -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "thread_wrapper.h" -#include "../source/event.h" -#include "test_util.h" // send side callback -#include "media_opt_test.h" - -#include - -using namespace webrtc; - -bool -MainSenderThread(void* obj) -{ - SendSharedState* state = static_cast(obj); - EventWrapper& waitEvent = *EventWrapper::Create(); - // preparing a frame for encoding - VideoFrame sourceFrame; - WebRtc_Word32 width = state->_args.width; - WebRtc_Word32 height = state->_args.height; - float frameRate = state->_args.frameRate; - WebRtc_Word32 lengthSourceFrame = 3*width*height/2; - sourceFrame.VerifyAndAllocate(lengthSourceFrame); - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[lengthSourceFrame]; - - if (state->_sourceFile == NULL) - { - state->_sourceFile = fopen(state->_args.inputFile.c_str(), "rb"); - if (state->_sourceFile == NULL) - { - printf ("Error when opening file \n"); - delete &waitEvent; - delete tmpBuffer; - return false; - } - } - if (feof(state->_sourceFile) == 0) - { - fread(tmpBuffer, 1, lengthSourceFrame,state->_sourceFile); - state->_frameCnt++; - sourceFrame.CopyFrame(lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(height); - sourceFrame.SetWidth(width); - state->_timestamp += (WebRtc_UWord32)(9e4 / frameRate); - sourceFrame.SetTimeStamp(state->_timestamp); - - WebRtc_Word32 ret = state->_vcm.AddVideoFrame(sourceFrame); - if (ret < 0) - { - printf("Add Frame error: %d\n", ret); - delete &waitEvent; - delete tmpBuffer; - return false; - } - waitEvent.Wait(33); - } - - delete &waitEvent; - delete tmpBuffer; - - return true; -} - -bool -IntSenderThread(void* obj) -{ - SendSharedState* state = static_cast(obj); - state->_vcm.SetChannelParameters(1000,30,0); - - return true; -} - - -int MTRxTxTest(CmdArgs& args) -{ - /* TEST SETTINGS */ - std::string inname = args.inputFile; - std::string outname; - if (args.outputFile == "") - outname = "../MTRxTxTest_decoded.yuv"; - else - outname = args.outputFile; - - WebRtc_UWord16 width = args.width; - WebRtc_UWord16 height = args.height; - WebRtc_UWord32 lengthSourceFrame = 3*width*height/2; - - float frameRate = args.frameRate; - float bitRate = args.bitRate; - WebRtc_Word32 numberOfCores = 1; - - // error resilience/network - // Nack support is currently not implemented in this test. - bool nackEnabled = false; - bool fecEnabled = false; - WebRtc_UWord8 rttMS = 20; - float lossRate = 0.0*255; // no packet loss - WebRtc_UWord32 renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - WebRtc_UWord8 deltaFECRate = 0; - WebRtc_UWord8 keyFECRate = 0; - - /* TEST SET-UP */ - - // Set up trace - Trace::CreateTrace(); - Trace::SetTraceFile("MTRxTxTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - FILE* sourceFile; - FILE* decodedFile; - - if ((sourceFile = fopen(inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", inname.c_str()); - return -1; - } - - if ((decodedFile = fopen(outname.c_str(), "wb")) == NULL) - { - printf("Cannot read file %s.\n", outname.c_str()); - return -1; - } - - //RTP - RtpRtcp* rtp = RtpRtcp::CreateRtpRtcp(1, false); - if (rtp->InitReceiver() < 0) - { - return -1; - } - if (rtp->InitSender() < 0) - { - return -1; - } - // registering codecs for the RTP module - TEST(rtp->RegisterReceivePayload("ULPFEC", VCM_ULPFEC_PAYLOAD_TYPE) == 0); - TEST(rtp->RegisterReceivePayload("RED", VCM_RED_PAYLOAD_TYPE) == 0); - TEST(rtp->RegisterReceivePayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE) == 0); - - // inform RTP Module of error resilience features - TEST(rtp->SetGenericFECStatus(fecEnabled, VCM_RED_PAYLOAD_TYPE, VCM_ULPFEC_PAYLOAD_TYPE) == 0); - - TEST(rtp->RegisterSendPayload(args.codecName.c_str(), VCM_VP8_PAYLOAD_TYPE, 90000, 1, 10000) == 0); - - //VCM - VideoCodingModule* vcm = VideoCodingModule::Create(1); - if (vcm->InitializeReceiver() < 0) - { - return -1; - } - if (vcm->InitializeSender()) - { - return -1; - } - // registering codecs for the VCM module - VideoCodec sendCodec; - vcm->InitializeSender(); - WebRtc_Word32 numberOfCodecs = vcm->NumberOfCodecs(); - if (numberOfCodecs < 1) - { - return -1; - } - - if (vcm->Codec(args.codecType, &sendCodec) != 0) - { - // desired codec unavailable - printf("Codec not registered\n"); - return -1; - } - // register codec - sendCodec.startBitrate = (int) bitRate; - sendCodec.height = height; - sendCodec.width = width; - sendCodec.maxFramerate = (WebRtc_UWord8)frameRate; - vcm->RegisterSendCodec(&sendCodec, numberOfCores, 1440); - vcm->RegisterReceiveCodec(&sendCodec, numberOfCores); // same settings for encode and decode - - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - // Callback Settings - - PacketRequester packetRequester(*rtp); - vcm->RegisterPacketRequestCallback(&packetRequester); - - VCMRTPEncodeCompleteCallback* encodeCompleteCallback = new VCMRTPEncodeCompleteCallback(rtp); - vcm->RegisterTransportCallback(encodeCompleteCallback); - encodeCompleteCallback->SetCodecType(ConvertCodecType(args.codecName.c_str())); - encodeCompleteCallback->SetFrameDimensions(width, height); - // frame ready to be sent to network - RTPSendCompleteCallback* outgoingTransport = new RTPSendCompleteCallback(rtp, "dump.rtp"); - rtp->RegisterSendTransport(outgoingTransport); - // FrameReceiveCallback - VCMDecodeCompleteCallback receiveCallback(decodedFile); - RtpDataCallback dataCallback(vcm); - rtp->RegisterIncomingDataCallback(&dataCallback); - vcm->RegisterReceiveCallback(&receiveCallback); - - VCMTestProtectionCallback protectionCallback; - vcm->RegisterProtectionCallback(&protectionCallback); - - outgoingTransport->SetLossPct(lossRate); - // Nack support is currently not implemented in this test - assert(nackEnabled == false); - vcm->SetVideoProtection(kProtectionNack, nackEnabled); - vcm->SetVideoProtection(kProtectionFEC, fecEnabled); - - // inform RTP Module of error resilience features - rtp->SetFECCodeRate(protectionCallback.FECKeyRate(), - protectionCallback.FECDeltaRate()); - rtp->SetNACKStatus(protectionCallback.NACKMethod()); - - vcm->SetChannelParameters((WebRtc_UWord32) bitRate, - (WebRtc_UWord8) lossRate, rttMS); - - SharedRTPState mtState(*vcm, *rtp); // receive side - SendSharedState mtSendState(*vcm, *rtp, args); // send side - - /*START TEST*/ - - // Create and start all threads - // send side threads - ThreadWrapper* mainSenderThread = ThreadWrapper::CreateThread(MainSenderThread, - &mtSendState, kNormalPriority, "MainSenderThread"); - ThreadWrapper* intSenderThread = ThreadWrapper::CreateThread(IntSenderThread, - &mtSendState, kNormalPriority, "IntThread"); - - if (MainSenderThread != NULL) - { - unsigned int tid; - mainSenderThread->Start(tid); - } - else - { - printf("Unable to start main sender thread\n"); - return -1; - } - - if (IntSenderThread != NULL) - { - unsigned int tid; - intSenderThread->Start(tid); - } - else - { - printf("Unable to start sender interference thread\n"); - return -1; - } - - // Receive side threads - ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread, - &mtState, kNormalPriority, "ProcessingThread"); - ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread, - &mtState, kNormalPriority, "DecodeThread"); - - if (processingThread != NULL) - { - unsigned int tid; - processingThread->Start(tid); - } - else - { - printf("Unable to start processing thread\n"); - return -1; - } - - if (decodeThread != NULL) - { - unsigned int tid; - decodeThread->Start(tid); - } - else - { - printf("Unable to start decode thread\n"); - return -1; - } - - EventWrapper& waitEvent = *EventWrapper::Create(); - - // Decode for 10 seconds and then tear down and exit. - waitEvent.Wait(30000); - - // Tear down - - while (!mainSenderThread->Stop()) - { - ; - } - - while (!intSenderThread->Stop()) - { - ; - } - - - while (!processingThread->Stop()) - { - ; - } - - while (!decodeThread->Stop()) - { - ; - } - - delete &waitEvent; - delete mainSenderThread; - delete intSenderThread; - delete processingThread; - delete decodeThread; - delete encodeCompleteCallback; - delete outgoingTransport; - VideoCodingModule::Destroy(vcm); - RtpRtcp::DestroyRtpRtcp(rtp); - rtp = NULL; - vcm = NULL; - Trace::ReturnTrace(); - fclose(decodedFile); - printf("Multi-Thread test Done: View output file \n"); - return 0; - -} - diff --git a/modules/video_coding/main/test/normal_test.cc b/modules/video_coding/main/test/normal_test.cc deleted file mode 100644 index 4d77a5cff..000000000 --- a/modules/video_coding/main/test/normal_test.cc +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "normal_test.h" -#include "../source/event.h" -#include "tick_time.h" -#include "common_types.h" -#include "trace.h" -#include "test_util.h" -#include -#include -#include -#include - -using namespace webrtc; - -int NormalTest::RunTest(CmdArgs& args) -{ - // Don't run this test with debug time -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - printf("SIMULATION TIME\n"); -#else - printf("REAL-TIME\n"); -#endif - Trace::CreateTrace(); - Trace::SetTraceFile("VCMNormalTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - VideoCodingModule* vcm = VideoCodingModule::Create(1); - NormalTest VCMNTest(vcm); - VCMNTest.Perform(args); - VideoCodingModule::Destroy(vcm); - Trace::ReturnTrace(); - return 0; -} - -//////////////// -// Callback Implementation -////////////// - -VCMNTEncodeCompleteCallback::VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test): -_seqNo(0), -_layerPacketId(1), -_encodedFile(encodedFile), -_encodedBytes(0), -_skipCnt(0), -_VCMReceiver(NULL), -_test(test) -{ - // -} -VCMNTEncodeCompleteCallback::~VCMNTEncodeCompleteCallback() -{ -} - -void -VCMNTEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport) -{ -} - -WebRtc_Word32 -VCMNTEncodeCompleteCallback::SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader) - -{ - // will call the VCMReceiver input packet - _frameType = frameType; - // writing encodedData into file - fwrite(payloadData, 1, payloadSize, _encodedFile); - WebRtcRTPHeader rtpInfo; - rtpInfo.header.markerBit = true; - rtpInfo.type.Video.width = 0; - rtpInfo.type.Video.height = 0; - switch (_test.VideoType()) - { - case kVideoCodecH263: - rtpInfo.type.Video.codec = kRTPVideoH263; - rtpInfo.type.Video.codecHeader.H263.bits = false; - rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false; - rtpInfo.type.Video.height = (WebRtc_UWord16)_test.Height(); - rtpInfo.type.Video.width = (WebRtc_UWord16)_test.Width(); - break; - case kVideoCodecVP8: - rtpInfo.type.Video.codec = kRTPVideoVP8; - break; - case kVideoCodecI420: - rtpInfo.type.Video.codec = kRTPVideoI420; - break; - } - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.sequenceNumber = _seqNo++; - rtpInfo.header.ssrc = 0; - rtpInfo.header.timestamp = timeStamp; - rtpInfo.frameType = frameType; - rtpInfo.type.Video.isFirstPacket = true; - // Size should also be received from that table, since the payload type - // defines the size. - - _encodedBytes += payloadSize; - if (payloadSize < 20) - { - _skipCnt++; - } - _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo); - return 0; -} -void -VCMNTEncodeCompleteCallback::RegisterReceiverVCM(VideoCodingModule *vcm) -{ - _VCMReceiver = vcm; - return; -} - WebRtc_Word32 -VCMNTEncodeCompleteCallback::EncodedBytes() -{ - return _encodedBytes; -} - -WebRtc_UWord32 -VCMNTEncodeCompleteCallback::SkipCnt() -{ - return _skipCnt; -} - -// Decoded Frame Callback Implmentation -VCMNTDecodeCompleCallback::~VCMNTDecodeCompleCallback() -{ - // -} - WebRtc_Word32 -VCMNTDecodeCompleCallback::FrameToRender(webrtc::VideoFrame& videoFrame) -{ - if (videoFrame.Width() != _currentWidth || - videoFrame.Height() != _currentHeight) - { - _currentWidth = videoFrame.Width(); - _currentHeight = videoFrame.Height(); - if (_decodedFile != NULL) - { - fclose(_decodedFile); - _decodedFile = NULL; - } - _decodedFile = fopen(_outname.c_str(), "wb"); - } - fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile); - _decodedBytes+= videoFrame.Length(); - return VCM_OK; -} - - WebRtc_Word32 -VCMNTDecodeCompleCallback::DecodedBytes() -{ - return _decodedBytes; -} - - //VCM Normal Test Class implementation - -NormalTest::NormalTest(VideoCodingModule* vcm) -: -_vcm(vcm), -_totalEncodeTime(0), -_totalDecodeTime(0), -_decodeCompleteTime(0), -_encodeCompleteTime(0), -_totalEncodePipeTime(0), -_totalDecodePipeTime(0), -_frameCnt(0), -_timeStamp(0), -_encFrameCnt(0), -_decFrameCnt(0), -_sumEncBytes(0) - -{ - // -} - -NormalTest::~NormalTest() -{ - // -} -void -NormalTest::Setup(CmdArgs& args) -{ - _inname = args.inputFile; - _encodedName = "encoded_normaltest.yuv"; - _width = args.width; - _height = args.height; - _frameRate = args.frameRate; - _bitRate = args.bitRate; - if (args.outputFile == "") - { - std::ostringstream filename; - filename << "../NormalTest_" << _width << "x" << _height << "_" << _frameRate << "Hz_P420.yuv"; - _outname = filename.str(); - } - else - { - _outname = args.outputFile; - } - _lengthSourceFrame = 3*_width*_height/2; - _videoType = args.codecType; - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - - _log.open("../TestLog.txt", std::fstream::out | std::fstream::app); - return; -} - -WebRtc_Word32 -NormalTest::Perform(CmdArgs& args) -{ - Setup(args); - EventWrapper* waitEvent = EventWrapper::Create(); - VideoCodec _sendCodec;//, _receiveCodec; // tmp - sendCodecd used as receive codec - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - TEST(VideoCodingModule::Codec(_videoType, &_sendCodec) == VCM_OK); - _sendCodec.startBitrate = (int)_bitRate; // should be later on changed via the API - _sendCodec.width = static_cast(_width); - _sendCodec.height = static_cast(_height); - _sendCodec.maxFramerate = _frameRate; - TEST(_vcm->RegisterSendCodec(&_sendCodec, 4, 1400) == VCM_OK);// will also set and init the desired codec - // register a decoder (same codec for decoder and encoder ) - TEST(_vcm->RegisterReceiveCodec(&_sendCodec, 1) == VCM_OK); - /* Callback Settings */ - VCMNTDecodeCompleCallback _decodeCallback(_outname); - _vcm->RegisterReceiveCallback(&_decodeCallback); - VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this); - _vcm->RegisterTransportCallback(&_encodeCompleteCallback); - // encode and decode with the same vcm - _encodeCompleteCallback.RegisterReceiverVCM(_vcm); - /////////////////////// - /// Start Test - /////////////////////// - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; - double startTime = clock()/(double)CLOCKS_PER_SEC; - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0); - - SendStatsTest sendStats; - sendStats.SetTargetFrameRate(static_cast(_frameRate)); - _vcm->RegisterSendStatisticsCallback(&sendStats); - - while (feof(_sourceFile)== 0) - { - WebRtc_Word64 processStartTime = VCMTickTime::MillisecondTimestamp(); - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - _frameCnt++; - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(_sendCodec.maxFramerate)); - sourceFrame.SetTimeStamp(_timeStamp); - _encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; - WebRtc_Word32 ret = _vcm->AddVideoFrame(sourceFrame); - double encodeTime = clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())]; - _totalEncodeTime += encodeTime; - if (ret < 0) - { - printf("Error in AddFrame: %d\n", ret); - //exit(1); - } - _decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode - ret = _vcm->Decode(); - _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())]; - if (ret < 0) - { - printf("Error in Decode: %d\n", ret); - //exit(1); - } - if (_vcm->TimeUntilNextProcess() <= 0) - { - _vcm->Process(); - } - WebRtc_UWord32 framePeriod = static_cast(1000.0f/static_cast(_sendCodec.maxFramerate) + 0.5f); -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - for (int i=0; i < framePeriod; i++) - { - VCMTickTime::IncrementDebugClock(); - } -#else - WebRtc_Word64 timeSpent = VCMTickTime::MillisecondTimestamp() - processStartTime; - if (timeSpent < framePeriod) - { - waitEvent->Wait(framePeriod - timeSpent); - } -#endif - } - double endTime = clock()/(double)CLOCKS_PER_SEC; - _testTotalTime = endTime - startTime; - _sumEncBytes = _encodeCompleteCallback.EncodedBytes(); - - delete tmpBuffer; - delete waitEvent; - Teardown(); - Print(); - return 0; -} - -void -NormalTest::FrameEncoded(WebRtc_UWord32 timeStamp) -{ - _encodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; - _encFrameCnt++; - _totalEncodePipeTime += _encodeCompleteTime - _encodeTimes[int(timeStamp)]; - -} - -void -NormalTest::FrameDecoded(WebRtc_UWord32 timeStamp) -{ - _decodeCompleteTime = clock()/(double)CLOCKS_PER_SEC; - _decFrameCnt++; - _totalDecodePipeTime += _decodeCompleteTime - _decodeTimes[timeStamp]; -} - -void -NormalTest::Print() -{ - std::cout << "Normal Test Completed!" << std::endl; - (_log) << "Normal Test Completed!" << std::endl; - (_log) << "Input file: " << _inname << std::endl; - (_log) << "Output file: " << _outname << std::endl; - (_log) << "Total run time: " << _testTotalTime << std::endl; - printf("Total run time: %f s \n", _testTotalTime); - double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _frameRate)); - double actualBitRate = ActualBitRate / 1000.0; - double avgEncTime = _totalEncodeTime / _frameCnt; - double avgDecTime = _totalDecodeTime / _frameCnt; - double psnr; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _width, _height, &psnr); - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Target bitrate: %f kbps\n", _bitRate); - ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; - printf("Average encode time: %f s\n", avgEncTime); - ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl; - printf("Average decode time: %f s\n", avgDecTime); - ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl; - printf("PSNR: %f \n", psnr); - ( _log) << "PSNR: " << psnr << std::endl; - (_log) << std::endl; -} -void -NormalTest::Teardown() -{ - //_log.close(); - fclose(_sourceFile); - fclose(_encodedFile); - return; -} - - - - diff --git a/modules/video_coding/main/test/normal_test.h b/modules/video_coding/main/test/normal_test.h deleted file mode 100644 index 25af1743f..000000000 --- a/modules/video_coding/main/test/normal_test.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_ - -#include "video_coding.h" -#include "test_macros.h" -#include "test_util.h" - -#include - -class NormalTest; - -//Send Side - Packetization callback - will create and send a packet to the VCMReceiver -class VCMNTEncodeCompleteCallback : public webrtc::VCMPacketizationCallback -{ -public: - // constructor input: file in which encoded data will be written - VCMNTEncodeCompleteCallback(FILE* encodedFile, NormalTest& test); - virtual ~VCMNTEncodeCompleteCallback(); - // Register transport callback - void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport); - // process encoded data received from the encoder, pass stream to the VCMReceiver module - WebRtc_Word32 SendData(const webrtc::FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader); - - // Register exisitng VCM. Currently - encode and decode with the same vcm module. - void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm); - // Return sum of encoded data (all frames in the sequence) - WebRtc_Word32 EncodedBytes(); - // return number of encoder-skipped frames - WebRtc_UWord32 SkipCnt();; - // conversion function for payload type (needed for the callback function) -// RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType); - -private: - FILE* _encodedFile; - WebRtc_UWord32 _encodedBytes; - WebRtc_UWord32 _skipCnt; - webrtc::VideoCodingModule* _VCMReceiver; - webrtc::FrameType _frameType; - WebRtc_UWord8* _payloadData; // max payload size?? - WebRtc_UWord16 _seqNo; - WebRtc_UWord8 _layerPacketId; - NormalTest& _test; - // int _vcmMacrosTests; - // int _vcmMacrosErrors; - -}; // end of VCMEncodeCompleteCallback - -class VCMNTDecodeCompleCallback: public webrtc::VCMReceiveCallback -{ -public: - VCMNTDecodeCompleCallback(std::string outname): // or should it get a name? - _outname(outname), - _decodedFile(NULL), - _decodedBytes(0), - _currentWidth(0), - _currentHeight(0) {} - virtual ~VCMNTDecodeCompleCallback(); - void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback); - // will write decoded frame into file - WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame); - WebRtc_Word32 DecodedBytes(); -private: - FILE* _decodedFile; - std::string _outname; - WebRtc_UWord32 _decodedBytes; - WebRtc_UWord32 _currentWidth; - WebRtc_UWord32 _currentHeight; - -}; // end of VCMDecodeCompleCallback class - - -class NormalTest -{ -public: - NormalTest(webrtc::VideoCodingModule* vcm); - ~NormalTest(); - static int RunTest(CmdArgs& args); - WebRtc_Word32 Perform(CmdArgs& args); - // option:: turn into private and call from perform - WebRtc_UWord32 Width() const { return _width; }; - WebRtc_UWord32 Height() const { return _height; }; - webrtc::VideoCodecType VideoType() const { return _videoType; }; - - -protected: - // test setup - open files, general initializations - void Setup(CmdArgs& args); - // close open files, delete used memory - void Teardown(); - // print results to std output and to log file - void Print(); - // calculating pipeline delay, and encoding time - void FrameEncoded(WebRtc_UWord32 timeStamp); - // calculating pipeline delay, and decoding time - void FrameDecoded(WebRtc_UWord32 timeStamp); - - webrtc::VideoCodingModule* _vcm; - webrtc::VideoCodec _sendCodec; - webrtc::VideoCodec _receiveCodec; - std::string _inname; - std::string _outname; - std::string _encodedName; - WebRtc_Word32 _sumEncBytes; - FILE* _sourceFile; - FILE* _decodedFile; - FILE* _encodedFile; - std::fstream _log; - WebRtc_UWord32 _width; - WebRtc_UWord32 _height; - float _frameRate; - float _bitRate; - WebRtc_UWord32 _lengthSourceFrame; - WebRtc_UWord32 _timeStamp; - webrtc::VideoCodecType _videoType; - double _totalEncodeTime; - double _totalDecodeTime; - double _decodeCompleteTime; - double _encodeCompleteTime; - double _totalEncodePipeTime; - double _totalDecodePipeTime; - double _testTotalTime; - std::map _encodeTimes; - std::map _decodeTimes; - WebRtc_Word32 _frameCnt; - WebRtc_Word32 _encFrameCnt; - WebRtc_Word32 _decFrameCnt; - -}; // end of VCMNormalTestClass - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_NORMAL_TEST_H_ diff --git a/modules/video_coding/main/test/plotJitterEstimate.m b/modules/video_coding/main/test/plotJitterEstimate.m deleted file mode 100644 index d6185f55d..000000000 --- a/modules/video_coding/main/test/plotJitterEstimate.m +++ /dev/null @@ -1,52 +0,0 @@ -function plotJitterEstimate(filename) - -[timestamps, framedata, slopes, randJitters, framestats, timetable, filtjitter, rtt, rttStatsVec] = jitterBufferTraceParser(filename); - -x = 1:size(framestats, 1); -%figure(2); -subfigure(3, 2, 1); -hold on; -plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)) + 3*sqrt(randJitters(x,2)), 'b'); title('Estimate ms'); -plot(x, filtjitter, 'r'); -plot(x, slopes(x, 1).*(framestats(x, 1) - framestats(x, 2)), 'g'); -subfigure(3, 2, 2); -%subplot(211); -plot(x, slopes(x, 1)); title('Line slope'); -%subplot(212); -%plot(x, slopes(x, 2)); title('Line offset'); -subfigure(3, 2, 3); hold on; -plot(x, framestats); plot(x, framedata(x, 1)); title('frame size and average frame size'); -subfigure(3, 2, 4); -plot(x, framedata(x, 2)); title('Delay'); -subfigure(3, 2, 5); -hold on; -plot(x, randJitters(x,1),'r'); -plot(x, randJitters(x,2)); title('Random jitter'); - -subfigure(3, 2, 6); -delays = framedata(:,2); -dL = [0; framedata(2:end, 1) - framedata(1:end-1, 1)]; -hold on; -plot(dL, delays, '.'); -s = [min(dL) max(dL)]; -plot(s, slopes(end, 1)*s + slopes(end, 2), 'g'); -plot(s, slopes(end, 1)*s + slopes(end, 2) + 3*sqrt(randJitters(end,2)), 'r'); -plot(s, slopes(end, 1)*s + slopes(end, 2) - 3*sqrt(randJitters(end,2)), 'r'); -title('theta(1)*x+theta(2), (dT-dTS)/dL'); -if sum(size(rttStatsVec)) > 0 - figure; hold on; - rttNstdDevsDrift = 3.5; - rttNstdDevsJump = 2.5; - rttSamples = rttStatsVec(:, 1); - rttAvgs = rttStatsVec(:, 2); - rttStdDevs = sqrt(rttStatsVec(:, 3)); - rttMax = rttStatsVec(:, 4); - plot(rttSamples, 'ko-'); - plot(rttAvgs, 'g'); - plot(rttAvgs + rttNstdDevsDrift*rttStdDevs, 'b--'); - plot(rttAvgs + rttNstdDevsJump*rttStdDevs, 'b'); - plot(rttAvgs - rttNstdDevsJump*rttStdDevs, 'b'); - plot(rttMax, 'r'); - %plot(driftRestarts*max(maxRtts), '.'); - %plot(jumpRestarts*max(maxRtts), '.'); -end \ No newline at end of file diff --git a/modules/video_coding/main/test/plotReceiveTrace.m b/modules/video_coding/main/test/plotReceiveTrace.m deleted file mode 100644 index 4d262aa16..000000000 --- a/modules/video_coding/main/test/plotReceiveTrace.m +++ /dev/null @@ -1,213 +0,0 @@ -function [t, TS] = plotReceiveTrace(filename, flat) -fid=fopen(filename); -%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; First packet of frame 1869537938 -%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:1 ; 5260; Decoding timestamp 1869534934 -%DEBUG ; ( 8:32:33:375 | 0) VIDEO:1 ; 5260; Render frame 1869534934 at 20772610 -%DEBUG ; ( 8:32:33:375 | 0) VIDEO CODING:-1 ; 5260; Frame decoded: timeStamp=1870511259 decTime=0 maxDecTime=0, at 19965 -%DEBUG ; ( 7:59:42:500 | 0) VIDEO:-1 ; 2500; Received complete frame timestamp 1870514263 frame type 1 frame size 7862 at time 19965, jitter estimate was 130 -%DEBUG ; ( 8: 5:51:774 | 0) VIDEO:-1 ; 3968; ExtrapolateLocalTime(1870967878)=24971 ms - -if nargin == 1 - flat = 0; -end -line = fgetl(fid); -estimatedArrivalTime = []; -packetTime = []; -firstPacketTime = []; -decodeTime = []; -decodeCompleteTime = []; -renderTime = []; -completeTime = []; -while ischar(line)%line ~= -1 - if length(line) == 0 - line = fgetl(fid); - continue; - end - % Parse the trace line header - [tempres, count] = sscanf(line, 'DEBUG ; (%u:%u:%u:%u |%*lu)%13c:'); - if count < 5 - line = fgetl(fid); - continue; - end - hr=tempres(1); - mn=tempres(2); - sec=tempres(3); - ms=tempres(4); - timeInMs=hr*60*60*1000 + mn*60*1000 + sec*1000 + ms; - label = tempres(5:end); - I = find(label ~= 32); - label = label(I(1):end); % remove white spaces - if ~strncmp(char(label), 'VIDEO', 5) & ~strncmp(char(label), 'VIDEO CODING', 12) - line = fgetl(fid); - continue; - end - message = line(72:end); - - % Parse message - [p, count] = sscanf(message, 'ExtrapolateLocalTime(%lu)=%lu ms'); - if count == 2 - estimatedArrivalTime = [estimatedArrivalTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'Packet seqNo %u of frame %lu at %lu'); - if count == 3 - packetTime = [packetTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'First packet of frame %lu at %lu'); - if count == 2 - firstPacketTime = [firstPacketTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'Decoding timestamp %lu at %lu'); - if count == 2 - decodeTime = [decodeTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'Render frame %lu at %lu. Render delay %lu, required delay %lu, max decode time %lu, min total delay %lu'); - if count == 6 - renderTime = [renderTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%lu, at %lu'); - if count == 4 - decodeCompleteTime = [decodeCompleteTime; p']; - line = fgetl(fid); - continue; - end - - [p, count] = sscanf(message, 'Received complete frame timestamp %lu frame type %u frame size %*u at time %lu, jitter estimate was %lu'); - if count == 4 - completeTime = [completeTime; p']; - line = fgetl(fid); - continue; - end - - line = fgetl(fid); -end -fclose(fid); - -t = completeTime(:,3); -TS = completeTime(:,1); - -figure; -subplot(211); -hold on; -slope = 0; - -if sum(size(packetTime)) > 0 - % Plot the time when each packet arrives - firstTimeStamp = packetTime(1,2); - x = (packetTime(:,2) - firstTimeStamp)/90; - if flat - slope = x; - end - firstTime = packetTime(1,3); - plot(x, packetTime(:,3) - firstTime - slope, 'b.'); -else - % Plot the time when the first packet of a frame arrives - firstTimeStamp = firstPacketTime(1,1); - x = (firstPacketTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - firstTime = firstPacketTime(1,2); - plot(x, firstPacketTime(:,2) - firstTime - slope, 'b.'); -end - -% Plot the frame complete time -if prod(size(completeTime)) > 0 - x = (completeTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, completeTime(:,3) - firstTime - slope, 'ks'); -end - -% Plot the time the decode starts -if prod(size(decodeTime)) > 0 - x = (decodeTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, decodeTime(:,2) - firstTime - slope, 'r.'); -end - -% Plot the decode complete time -if prod(size(decodeCompleteTime)) > 0 - x = (decodeCompleteTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, decodeCompleteTime(:,4) - firstTime - slope, 'g.'); -end - -if prod(size(renderTime)) > 0 - % Plot the wanted render time in ms - x = (renderTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, renderTime(:,2) - firstTime - slope, 'c-'); - - % Plot the render time if there were no render delay or decoding delay. - x = (renderTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'c--'); - - % Plot the render time if there were no render delay. - x = (renderTime(:,1) - firstTimeStamp)/90; - if flat - slope = x; - end - plot(x, renderTime(:,2) - firstTime - slope - renderTime(:, 3) - renderTime(:, 5), 'b-'); -end - -%plot(x, 90*x, 'r-'); - -xlabel('RTP timestamp (in ms)'); -ylabel('Time (ms)'); -legend('Packet arrives', 'Frame complete', 'Decode', 'Decode complete', 'Time to render', 'Only jitter', 'Must decode'); - -% subplot(312); -% hold on; -% completeTs = completeTime(:, 1); -% arrivalTs = estimatedArrivalTime(:, 1); -% [c, completeIdx, arrivalIdx] = intersect(completeTs, arrivalTs); -% %plot(completeTs(completeIdx), completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2)); -% timeUntilComplete = completeTime(completeIdx, 3) - estimatedArrivalTime(arrivalIdx, 2); -% devFromAvgCompleteTime = timeUntilComplete - mean(timeUntilComplete); -% plot(completeTs(completeIdx) - completeTs(completeIdx(1)), devFromAvgCompleteTime); -% plot(completeTime(:, 1) - completeTime(1, 1), completeTime(:, 4), 'r'); -% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 2), 'g'); -% plot(decodeCompleteTime(:, 1) - decodeCompleteTime(1, 1), decodeCompleteTime(:, 3), 'k'); -% xlabel('RTP timestamp'); -% ylabel('Time (ms)'); -% legend('Complete time - Estimated arrival time', 'Desired jitter buffer level', 'Actual decode time', 'Max decode time', 0); - -if prod(size(renderTime)) > 0 - subplot(212); - hold on; - firstTime = renderTime(1, 1); - targetDelay = max(renderTime(:, 3) + renderTime(:, 4) + renderTime(:, 5), renderTime(:, 6)); - plot(renderTime(:, 1) - firstTime, renderTime(:, 3), 'r-'); - plot(renderTime(:, 1) - firstTime, renderTime(:, 4), 'b-'); - plot(renderTime(:, 1) - firstTime, renderTime(:, 5), 'g-'); - plot(renderTime(:, 1) - firstTime, renderTime(:, 6), 'k-'); - plot(renderTime(:, 1) - firstTime, targetDelay, 'c-'); - xlabel('RTP timestamp'); - ylabel('Time (ms)'); - legend('Render delay', 'Jitter delay', 'Decode delay', 'Extra delay', 'Min total delay'); -end \ No newline at end of file diff --git a/modules/video_coding/main/test/plotTimingTest.m b/modules/video_coding/main/test/plotTimingTest.m deleted file mode 100644 index 52a6f303c..000000000 --- a/modules/video_coding/main/test/plotTimingTest.m +++ /dev/null @@ -1,62 +0,0 @@ -function plotTimingTest(filename) -fid=fopen(filename); - -%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; Stochastic test 1 -%DEBUG ; ( 9:53:33:859 | 0) VIDEO CODING:-1 ; 7132; Frame decoded: timeStamp=3000 decTime=10 at 10012 -%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStamp=3000 clock=10037 maxWaitTime=0 -%DEBUG ; ( 9:53:33:859 | 0) VIDEO:-1 ; 7132; timeStampMs=33 renderTime=54 -line = fgetl(fid); -decTime = []; -waitTime = []; -renderTime = []; -foundStart = 0; -testName = 'Stochastic test 1'; -while ischar(line) - if length(line) == 0 - line = fgetl(fid); - continue; - end - lineOrig = line; - line = line(72:end); - if ~foundStart - if strncmp(line, testName, length(testName)) - foundStart = 1; - end - line = fgetl(fid); - continue; - end - [p, count] = sscanf(line, 'Frame decoded: timeStamp=%lu decTime=%d maxDecTime=%d, at %lu'); - if count == 4 - decTime = [decTime; p']; - line = fgetl(fid); - continue; - end - [p, count] = sscanf(line, 'timeStamp=%u clock=%u maxWaitTime=%u'); - if count == 3 - waitTime = [waitTime; p']; - line = fgetl(fid); - continue; - end - [p, count] = sscanf(line, 'timeStamp=%u renderTime=%u'); - if count == 2 - renderTime = [renderTime; p']; - line = fgetl(fid); - continue; - end - line = fgetl(fid); -end -fclose(fid); - -% Compensate for wrap arounds and start counting from zero. -timeStamps = waitTime(:, 1); -tsDiff = diff(timeStamps); -wrapIdx = find(tsDiff < 0); -timeStamps(wrapIdx+1:end) = hex2dec('ffffffff') + timeStamps(wrapIdx+1:end); -timeStamps = timeStamps - timeStamps(1); - -figure; -hold on; -plot(timeStamps, decTime(:, 2), 'r'); -plot(timeStamps, waitTime(:, 3), 'g'); -plot(timeStamps(2:end), diff(renderTime(:, 2)), 'b'); -legend('Decode time', 'Max wait time', 'Render time diff'); \ No newline at end of file diff --git a/modules/video_coding/main/test/quality_modes_test.cc b/modules/video_coding/main/test/quality_modes_test.cc deleted file mode 100644 index f161fc455..000000000 --- a/modules/video_coding/main/test/quality_modes_test.cc +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "quality_modes_test.h" -#include "../source/event.h" -#include "vplib.h" - -#include -#include -#include - -using namespace webrtc; - -int qualityModeTest() -{ - // Don't run this test with debug time -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - return -1; -#endif - VideoCodingModule* vcm = VideoCodingModule::Create(1); - QualityModesTest QMTest(vcm); - QMTest.Perform(); - VideoCodingModule::Destroy(vcm); - return 0; -} - - -QualityModesTest::QualityModesTest(VideoCodingModule *vcm): -NormalTest(vcm), -_vpm() -{ - // -} - - -QualityModesTest::~QualityModesTest() -{ - // -} - -void -QualityModesTest::Setup() -{ - - - _inname= "../codecs/testFiles/database/crew_30f_4CIF.yuv"; - _outname = "../out_qmtest.yuv"; - _encodedName = "../encoded_qmtest.yuv"; - - //NATIVE/SOURCE VALUES - _nativeWidth = 2*352; - _nativeHeight = 2*288; - _nativeFrameRate = 30; - - - //TARGET/ENCODER VALUES - _width = 2*352; - _height = 2*288; - _frameRate = 30; - // - _bitRate = 400; - - _flagSSIM = false; - - _lengthSourceFrame = 3*_nativeWidth*_nativeHeight/2; - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - if ((_decodedFile = fopen(_outname.c_str(), "wb")) == NULL) - { - printf("Cannot write file %s.\n", _outname.c_str()); - exit(1); - } - - _log.open("../TestLog.txt", std::fstream::out | std::fstream::app); - return; -} - - -void -QualityModesTest::Print() -{ - std::cout << "Quality Modes Test Completed!" << std::endl; - (_log) << "Quality Modes Test Completed!" << std::endl; - (_log) << "Input file: " << _inname << std::endl; - (_log) << "Output file: " << _outname << std::endl; - (_log) << "Total run time: " << _testTotalTime << std::endl; - printf("Total run time: %f s \n", _testTotalTime); - double ActualBitRate = 8.0 *( _sumEncBytes / (_frameCnt / _nativeFrameRate)); - double actualBitRate = ActualBitRate / 1000.0; - double avgEncTime = _totalEncodeTime / _frameCnt; - double avgDecTime = _totalDecodeTime / _frameCnt; - double psnr,ssim; - PSNRfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &psnr); - printf("Actual bitrate: %f kbps\n", actualBitRate); - printf("Target bitrate: %f kbps\n", _bitRate); - ( _log) << "Actual bitrate: " << actualBitRate<< " kbps\tTarget: " << _bitRate << " kbps" << std::endl; - printf("Average encode time: %f s\n", avgEncTime); - ( _log) << "Average encode time: " << avgEncTime << " s" << std::endl; - printf("Average decode time: %f s\n", avgDecTime); - ( _log) << "Average decode time: " << avgDecTime << " s" << std::endl; - printf("PSNR: %f \n", psnr); - printf("**Number of frames dropped in VPM***%d \n",_numFramesDroppedVPM); - ( _log) << "PSNR: " << psnr << std::endl; - if (_flagSSIM == 1) - { - printf("***computing SSIM***\n"); - SSIMfromFiles(_inname.c_str(), _outname.c_str(), _nativeWidth, _nativeHeight, &ssim); - printf("SSIM: %f \n", ssim); - } - (_log) << std::endl; -} -void -QualityModesTest::Teardown() -{ - _log.close(); - fclose(_sourceFile); - fclose(_decodedFile); - fclose(_encodedFile); - return; -} - - -WebRtc_Word32 -QualityModesTest::Perform() -{ - Setup(); - // changing bit/frame rate during the test - const float bitRateUpdate[] = {1000}; - const float frameRateUpdate[] = {30}; - const int updateFrameNum[] = {10000}; // frame numbers at which an update will occur - - WebRtc_UWord32 numChanges = sizeof(updateFrameNum)/sizeof(*updateFrameNum); - WebRtc_UWord8 change = 0;// change counter - - _vpm = VideoProcessingModule::Create(1); - - EventWrapper* waitEvent = EventWrapper::Create(); - VideoCodec codec;//both send and receive - _vcm->InitializeReceiver(); - _vcm->InitializeSender(); - WebRtc_Word32 NumberOfCodecs = _vcm->NumberOfCodecs(); - for (int i = 0; i < NumberOfCodecs; i++) - { - _vcm->Codec(i, &codec); - if(strncmp(codec.plName,"VP8" , 5) == 0) - { - codec.startBitrate = (int)_bitRate; - codec.maxFramerate = (WebRtc_UWord8) _frameRate; - codec.width = (WebRtc_UWord16)_width; - codec.height = (WebRtc_UWord16)_height; - TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec - i = NumberOfCodecs; - } - } - - // register a decoder (same codec for decoder and encoder ) - TEST(_vcm->RegisterReceiveCodec(&codec, 2) == VCM_OK); - /* Callback Settings */ - VCMQMDecodeCompleCallback _decodeCallback(_decodedFile); - _vcm->RegisterReceiveCallback(&_decodeCallback); - VCMNTEncodeCompleteCallback _encodeCompleteCallback(_encodedFile, *this); - _vcm->RegisterTransportCallback(&_encodeCompleteCallback); - // encode and decode with the same vcm - _encodeCompleteCallback.RegisterReceiverVCM(_vcm); - - //quality modes callback - QMTestVideoSettingsCallback QMCallback; - QMCallback.RegisterVCM(_vcm); - QMCallback.RegisterVPM(_vpm); - _vcm->RegisterVideoQMCallback(&QMCallback); - - /////////////////////// - /// Start Test - /////////////////////// - _vpm->EnableTemporalDecimation(true); - _vpm->EnableContentAnalysis(true); - _vpm->SetInputFrameResampleMode(kFastRescaling); - - // disabling internal VCM frame dropper - _vcm->EnableFrameDropper(false); - - VideoFrame sourceFrame; - VideoFrame *decimatedFrame = NULL; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - WebRtc_UWord8* tmpBuffer = new WebRtc_UWord8[_lengthSourceFrame]; - double startTime = clock()/(double)CLOCKS_PER_SEC; - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 0); - - SendStatsTest sendStats; - sendStats.SetTargetFrameRate(static_cast(_frameRate)); - _vcm->RegisterSendStatisticsCallback(&sendStats); - - VideoContentMetrics* contentMetrics = NULL; - // setting user frame rate - _vpm->SetMaxFrameRate((WebRtc_UWord32)(_nativeFrameRate+ 0.5f)); - // for starters: keeping native values: - _vpm->SetTargetResolution(_width, _height, (WebRtc_UWord32)(_frameRate+ 0.5f)); - _decodeCallback.SetOriginalFrameDimensions(_nativeWidth, _nativeHeight); - - //tmp - disabling VPM frame dropping - _vpm->EnableTemporalDecimation(false); - - - WebRtc_Word32 ret = 0; - _numFramesDroppedVPM = 0; - - while (feof(_sourceFile)== 0) - { - fread(tmpBuffer, 1, _lengthSourceFrame, _sourceFile); - _frameCnt++; - sourceFrame.CopyFrame(_lengthSourceFrame, tmpBuffer); - sourceFrame.SetHeight(_nativeHeight); - sourceFrame.SetWidth(_nativeWidth); - - _timeStamp += (WebRtc_UWord32)(9e4 / static_cast(codec.maxFramerate)); - sourceFrame.SetTimeStamp(_timeStamp); - - ret = _vpm->PreprocessFrame(&sourceFrame, &decimatedFrame); - if (ret == 1) - { - printf("VD: frame drop %d \n",_frameCnt); - _numFramesDroppedVPM += 1; - continue; // frame drop - } - else if (ret < 0) - { - printf("Error in PreprocessFrame: %d\n", ret); - //exit(1); - } - contentMetrics = _vpm->ContentMetrics(); - if (contentMetrics == NULL) - { - printf("error: contentMetrics = NULL\n"); - } - - // counting only encoding time - _encodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; - - WebRtc_Word32 ret = _vcm->AddVideoFrame(*decimatedFrame, contentMetrics); - - _totalEncodeTime += clock()/(double)CLOCKS_PER_SEC - _encodeTimes[int(sourceFrame.TimeStamp())]; - - if (ret < 0) - { - printf("Error in AddFrame: %d\n", ret); - //exit(1); - } - _decodeTimes[int(sourceFrame.TimeStamp())] = clock()/(double)CLOCKS_PER_SEC; // same timestamp value for encode and decode - ret = _vcm->Decode(); - _totalDecodeTime += clock()/(double)CLOCKS_PER_SEC - _decodeTimes[int(sourceFrame.TimeStamp())]; - if (ret < 0) - { - printf("Error in Decode: %d\n", ret); - //exit(1); - } - if (_vcm->TimeUntilNextProcess() <= 0) - { - _vcm->Process(); - } - // mimicking setTargetRates - update every 1 sec - // this will trigger QMSelect - if (_frameCnt%((int)_frameRate) == 0) - { - _vcm->SetChannelParameters((WebRtc_UWord32)_bitRate, 0, 1); - waitEvent->Wait(33); - } - waitEvent->Wait(33); - // check for bit rate update - if (change < numChanges && _frameCnt == updateFrameNum[change]) - { - _bitRate = bitRateUpdate[change]; - _frameRate = frameRateUpdate[change]; - codec.startBitrate = (int)_bitRate; - codec.maxFramerate = (WebRtc_UWord8) _frameRate; - TEST(_vcm->RegisterSendCodec(&codec, 2, 1440) == VCM_OK);// will also set and init the desired codec - change++; - } - } - - double endTime = clock()/(double)CLOCKS_PER_SEC; - _testTotalTime = endTime - startTime; - _sumEncBytes = _encodeCompleteCallback.EncodedBytes(); - - delete tmpBuffer; - delete waitEvent; - _vpm->Reset(); - Teardown(); - Print(); - VideoProcessingModule::Destroy(_vpm); - return 0; -} - - -// implementing callback to be called from VCM to update VPM of frame rate and size -QMTestVideoSettingsCallback::QMTestVideoSettingsCallback(): -_vpm(NULL), -_vcm(NULL) -{ - // -} - -void -QMTestVideoSettingsCallback::RegisterVPM(VideoProcessingModule *vpm) -{ - _vpm = vpm; -} -void -QMTestVideoSettingsCallback::RegisterVCM(VideoCodingModule *vcm) -{ - _vcm = vcm; -} - -bool -QMTestVideoSettingsCallback::Updated() -{ - if (_updated) - { - _updated = false; - return true; - } - return false; -} - -WebRtc_Word32 -QMTestVideoSettingsCallback::SetVideoQMSettings(const WebRtc_UWord32 frameRate, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - WebRtc_Word32 retVal = 0; - printf("QM updates: W = %d, H = %d, FR = %d, \n", width, height, frameRate); - retVal = _vpm->SetTargetResolution(width, height, frameRate); - //Initialize codec with new values - is this the best place to do it? - if (!retVal) - { - // first get current settings - VideoCodec currentCodec; - _vcm->SendCodec(¤tCodec); - // now set new values: - currentCodec.height = (WebRtc_UWord16)height; - currentCodec.width = (WebRtc_UWord16)width; - currentCodec.maxFramerate = (WebRtc_UWord8)frameRate; - - // re-register encoder - retVal = _vcm->RegisterSendCodec(¤tCodec, 2, 1440); - _updated = true; - } - - return retVal; -} - - -// Decoded Frame Callback Implmentation -VCMQMDecodeCompleCallback::VCMQMDecodeCompleCallback(FILE* decodedFile): -_decodedFile(decodedFile), -_decodedBytes(0), -//_test(test), -_origWidth(0), -_origHeight(0), -_decWidth(0), -_decHeight(0), -//_interpolator(NULL), -_decBuffer(NULL), -_frameCnt(0) -{ - // -} - -VCMQMDecodeCompleCallback::~VCMQMDecodeCompleCallback() - { -// if (_interpolator != NULL) -// { -// deleteInterpolator(_interpolator); -// _interpolator = NULL; -// } - if (_decBuffer != NULL) - { - delete [] _decBuffer; - _decBuffer = NULL; - } - } -WebRtc_Word32 -VCMQMDecodeCompleCallback::FrameToRender(VideoFrame& videoFrame) -{ - if ((_origWidth == videoFrame.Width()) && (_origHeight == videoFrame.Height())) - { - fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile); - _frameCnt++; - //printf("frame dec # %d", _frameCnt); - // no need for interpolator and decBuffer - if (_decBuffer != NULL) - { - delete [] _decBuffer; - _decBuffer = NULL; - } -// if (_interpolator != NULL) -// { -// deleteInterpolator(_interpolator); -// _interpolator = NULL; -// } - _decWidth = 0; - _decHeight = 0; - } - else - { - if ((_decWidth != videoFrame.Width()) || (_decHeight != videoFrame.Height())) - { - _decWidth = videoFrame.Width(); - _decHeight = videoFrame.Height(); - buildInterpolator(); - } - -// interpolateFrame(_interpolator, videoFrame.Buffer(),_decBuffer); - fwrite(_decBuffer, 1, _origWidth*_origHeight*3/2, _decodedFile); - _frameCnt++; - } - - _decodedBytes += videoFrame.Length(); - return VCM_OK; -} - -WebRtc_Word32 -VCMQMDecodeCompleCallback::DecodedBytes() -{ - return _decodedBytes; -} - -void -VCMQMDecodeCompleCallback::SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height) -{ - _origWidth = width; - _origHeight = height; -} - -WebRtc_Word32 -VCMQMDecodeCompleCallback::buildInterpolator() -{ -// if (_interpolator != NULL) -// { -// deleteInterpolator(_interpolator); -// _interpolator = NULL; -// } - - // create decimator - WebRtc_Word32 filterPar = 4; //Lanczos (assuming sampling ratio is 1, 1.5, 2, 4) - - float HeightRatio = 0; - float WidthRatio = 0; - - WidthRatio = (float)_origWidth/(float)_decWidth; - HeightRatio = (float)_origHeight/(float)_decHeight; - - if ( (HeightRatio == 1.0 || HeightRatio == 1.5 || HeightRatio == 2 || HeightRatio == 4) && - (WidthRatio == 1.0 || WidthRatio == 1.5 || WidthRatio == 2 || WidthRatio == 4)) - { - filterPar = 4; //Lanczos - } else - { - filterPar = 0; //BiCubic - } - - - // define interpolator here - - // create interpolator here - -// if (_interpolator == NULL) -// { -// return -1; -// } - - WebRtc_UWord32 decFrameLength = _origWidth*_origHeight*3 >> 1; - if (_decBuffer != NULL) - { - delete [] _decBuffer; - } - _decBuffer = new WebRtc_UWord8[decFrameLength]; - if (_decBuffer == NULL) - { - return -1; - } - - return 0; -} diff --git a/modules/video_coding/main/test/quality_modes_test.h b/modules/video_coding/main/test/quality_modes_test.h deleted file mode 100644 index dd1833a41..000000000 --- a/modules/video_coding/main/test/quality_modes_test.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_ - -#include "video_processing.h" -#include "normal_test.h" -#include "video_coding_defines.h" - -int qualityModeTest(); - -class QualityModesTest : public NormalTest -{ -public: - QualityModesTest(webrtc::VideoCodingModule* vcm); - virtual ~QualityModesTest(); - WebRtc_Word32 Perform(); - -private: - - void Setup(); - void Print(); - void Teardown(); - void SsimComp(); - - webrtc::VideoProcessingModule* _vpm; - - WebRtc_UWord32 _width; - WebRtc_UWord32 _height; - float _frameRate; - WebRtc_UWord32 _nativeWidth; - WebRtc_UWord32 _nativeHeight; - float _nativeFrameRate; - - WebRtc_UWord32 _numFramesDroppedVPM; - bool _flagSSIM; - -}; // end of QualityModesTest class - - -class VCMQMDecodeCompleCallback: public webrtc::VCMReceiveCallback -{ -public: - VCMQMDecodeCompleCallback(FILE* decodedFile); - virtual ~VCMQMDecodeCompleCallback(); - void SetUserReceiveCallback(webrtc::VCMReceiveCallback* receiveCallback); - // will write decoded frame into file - WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame); - WebRtc_Word32 DecodedBytes(); - void SetOriginalFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height); - WebRtc_Word32 buildInterpolator(); -private: - FILE* _decodedFile; - WebRtc_UWord32 _decodedBytes; - // QualityModesTest& _test; - WebRtc_Word32 _origWidth; - WebRtc_Word32 _origHeight; - WebRtc_Word32 _decWidth; - WebRtc_Word32 _decHeight; -// VideoInterpolator* _interpolator; - WebRtc_UWord8* _decBuffer; - WebRtc_UWord32 _frameCnt; // debug - -}; // end of VCMQMDecodeCompleCallback class - -class QMTestVideoSettingsCallback : public webrtc::VCMQMSettingsCallback -{ -public: - QMTestVideoSettingsCallback(); - // update VPM with QM settings - WebRtc_Word32 SetVideoQMSettings(const WebRtc_UWord32 frameRate, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height); - // register VPM used by test - void RegisterVPM(webrtc::VideoProcessingModule* vpm); - void RegisterVCM(webrtc::VideoCodingModule* vcm); - bool Updated(); - -private: - webrtc::VideoProcessingModule* _vpm; - webrtc::VideoCodingModule* _vcm; - bool _updated; -}; - - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_QUALITY_MODSE_TEST_H_ diff --git a/modules/video_coding/main/test/receiver_tests.h b/modules/video_coding/main/test/receiver_tests.h deleted file mode 100644 index 92b90991c..000000000 --- a/modules/video_coding/main/test/receiver_tests.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_ - -#include "video_coding.h" -#include "module_common_types.h" -#include "common_types.h" -#include "rtp_rtcp.h" -#include "typedefs.h" -#include "rtp_player.h" -#include "test_util.h" - -#include -#include - -class RtpDataCallback : public webrtc::RtpData -{ -public: - RtpDataCallback(webrtc::VideoCodingModule* vcm) - : _vcm(vcm) {}; - - virtual WebRtc_Word32 OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const webrtc::WebRtcRTPHeader* rtpHeader); -private: - webrtc::VideoCodingModule* _vcm; -}; - -class FrameReceiveCallback : public webrtc::VCMReceiveCallback -{ -public: - FrameReceiveCallback(std::string outFilename) : - _outFilename(outFilename), - _outFile(NULL), - _timingFile(NULL) {} - - virtual ~FrameReceiveCallback(); - - WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame); - -private: - std::string _outFilename; - FILE* _outFile; - FILE* _timingFile; -}; - -class SharedState -{ -public: - SharedState(webrtc::VideoCodingModule& vcm, RTPPlayer& rtpPlayer) : - _rtpPlayer(rtpPlayer), - _vcm(vcm) {} - webrtc::VideoCodingModule& _vcm; - RTPPlayer& _rtpPlayer; -}; - -class SharedRTPState -{ -public: - SharedRTPState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp) : - _rtp(rtp), - _vcm(vcm) {} - webrtc::VideoCodingModule& _vcm; - webrtc::RtpRtcp& _rtp; -}; - -int RtpPlay(CmdArgs& args); -int RtpPlayMT(CmdArgs& args, - int releaseTest = 0, - webrtc::VideoCodecType releaseTestVideoType = webrtc::kVideoCodecVP8); -int ReceiverTimingTests(CmdArgs& args); -int JitterBufferTest(CmdArgs& args); -int DecodeFromStorageTest(CmdArgs& args); - -// Thread functions: -bool ProcessingThread(void* obj); -bool RtpReaderThread(void* obj); -bool DecodeThread(void* obj); -bool NackThread(void* obj); - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RECEIVER_TESTS_H_ diff --git a/modules/video_coding/main/test/receiver_timing_tests.cc b/modules/video_coding/main/test/receiver_timing_tests.cc deleted file mode 100644 index ce2c8bb51..000000000 --- a/modules/video_coding/main/test/receiver_timing_tests.cc +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "receiver_tests.h" -#include "video_coding.h" -#include "trace.h" -#include "tick_time.h" -#include "../source/event.h" -#include "../source/internal_defines.h" -#include "timing.h" -#include "test_macros.h" -#include "test_util.h" - -#include -#include -#include - -using namespace webrtc; - -float vcmFloatMax(float a, float b) -{ - return a > b ? a : b; -} - -float vcmFloatMin(float a, float b) -{ - return a < b ? a : b; -} - -double const pi = 4*std::atan(1.0); - -class GaussDist -{ -public: - static float RandValue(float m, float stdDev) // returns a single normally distributed number - { - float r1 = static_cast((std::rand() + 1.0)/(RAND_MAX + 1.0)); // gives equal distribution in (0, 1] - float r2 = static_cast((std::rand() + 1.0)/(RAND_MAX + 1.0)); - return m + stdDev * static_cast(std::sqrt(-2*std::log(r1))*std::cos(2*pi*r2)); - } -}; - -int ReceiverTimingTests(CmdArgs& args) -{ - // Make sure this test is never executed with simulated clocks -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - return -1; -#endif - - // Set up trace - Trace::CreateTrace(); - Trace::SetTraceFile("receiverTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - // A static random seed - srand(0); - - VCMTiming timing; - float clockInMs = 0.0; - WebRtc_UWord32 waitTime = 0; - WebRtc_Word32 jitterDelayMs = 0; - WebRtc_Word32 maxDecodeTimeMs = 0; - WebRtc_Word32 extraDelayMs = 0; - WebRtc_UWord32 timeStamp = 0; - - timing.Reset(static_cast(clockInMs + 0.5)); - - timing.UpdateCurrentDelay(timeStamp); - TEST(timing.MaxWaitingTime(timeStamp, static_cast(clockInMs + 0.5)) >= 0); - - timing.Reset(static_cast(clockInMs + 0.5)); - - timing.IncomingTimestamp(timeStamp, static_cast(clockInMs + 0.5)); - jitterDelayMs = 20; - timing.SetRequiredDelay(jitterDelayMs); - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - // First update initializes the render time. Since we have no decode delay - // we get waitTime = renderTime - now - renderDelay = jitter - TEST(waitTime == jitterDelayMs); - - jitterDelayMs += VCMTiming::kDelayMaxChangeMsPerS + 10; - timeStamp += 90000; - clockInMs += 1000.0f; - timing.SetRequiredDelay(jitterDelayMs); - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - // Since we gradually increase the delay we only get - // 100 ms every second. - TEST(waitTime == jitterDelayMs - 10); - - timeStamp += 90000; - clockInMs += 1000.0; - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - TEST(waitTime == jitterDelayMs); - - // 300 incoming frames without jitter, verify that this gives the exact wait time - for (int i=0; i < 300; i++) - { - clockInMs += 1000.0f/30.0f; - timeStamp += 3000; - timing.IncomingTimestamp(timeStamp, static_cast(clockInMs + 0.5)); - } - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - TEST(waitTime == jitterDelayMs); - - // Add decode time estimates - for (int i=0; i < 10; i++) - { - WebRtc_Word64 startTimeMs = static_cast(clockInMs + 0.5); - clockInMs += 10.0f; - timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast(clockInMs + 0.5)); - timeStamp += 3000; - clockInMs += 1000.0f/30.0f - 10.0f; - timing.IncomingTimestamp(timeStamp, static_cast(clockInMs + 0.5)); - } - maxDecodeTimeMs = 10; - timing.SetRequiredDelay(jitterDelayMs); - clockInMs += 1000.0f; - timeStamp += 90000; - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - TEST(waitTime == jitterDelayMs); - - WebRtc_UWord32 totalDelay1 = timing.TargetVideoDelay(); - WebRtc_UWord32 minTotalDelayMs = 200; - timing.SetMinimumTotalDelay(minTotalDelayMs); - clockInMs += 5000.0f; - timeStamp += 5*90000; - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - WebRtc_UWord32 totalDelay2 = timing.TargetVideoDelay(); - // We should at least have minTotalDelayMs - decodeTime (10) - renderTime (10) to wait - TEST(waitTime == minTotalDelayMs - maxDecodeTimeMs - 10); - // The total video delay should not increase with the extra delay, - // the extra delay should be independent. - TEST(totalDelay1 == totalDelay2); - - // Reset min total delay - timing.SetMinimumTotalDelay(0); - clockInMs += 5000.0f; - timeStamp += 5*90000; - timing.UpdateCurrentDelay(timeStamp); - - // A sudden increase in timestamp of 2.1 seconds - clockInMs += 1000.0f/30.0f; - timeStamp += static_cast(2.1*90000 + 0.5); - WebRtc_Word64 ret = timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)); - TEST(ret == -1); - timing.Reset(); - - // This test produces a trace which can be parsed with plotTimingTest.m. The plot - // can be used to see that the timing is reasonable under noise, and that the - // gradual transition between delays works as expected. - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "Stochastic test 1"); - - jitterDelayMs = 60; - maxDecodeTimeMs = 10; - extraDelayMs = 0; - - timeStamp = static_cast(-10000); // To produce a wrap - clockInMs = 10000.0f; - timing.Reset(static_cast(clockInMs + 0.5)); - - float noise = 0.0f; - for (int i=0; i < 1400; i++) - { - if (i == 400) - { - jitterDelayMs = 30; - } - else if (i == 700) - { - jitterDelayMs = 100; - } - else if (i == 1000) - { - minTotalDelayMs = 200; - timing.SetMinimumTotalDelay(minTotalDelayMs); - } - else if (i == 1200) - { - minTotalDelayMs = 0; - timing.SetMinimumTotalDelay(minTotalDelayMs); - } - WebRtc_Word64 startTimeMs = static_cast(clockInMs + 0.5); - noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 2), -10.0f), 30.0f); - clockInMs += 10.0f; - timing.StopDecodeTimer(timeStamp, startTimeMs, static_cast(clockInMs + noise + 0.5)); - timeStamp += 3000; - clockInMs += 1000.0f/30.0f - 10.0f; - noise = vcmFloatMin(vcmFloatMax(GaussDist::RandValue(0, 8), -15.0f), 15.0f); - timing.IncomingTimestamp(timeStamp, static_cast(clockInMs + noise + 0.5)); - timing.SetRequiredDelay(jitterDelayMs); - timing.UpdateCurrentDelay(timeStamp); - waitTime = timing.MaxWaitingTime(timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)), - static_cast(clockInMs + 0.5)); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "timeStamp=%u clock=%u maxWaitTime=%u", timeStamp, - static_cast(clockInMs + 0.5), waitTime); - - WebRtc_Word64 renderTimeMs = timing.RenderTimeMs(timeStamp, static_cast(clockInMs + 0.5)); - - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, - "timeStamp=%u renderTime=%u", - timeStamp, - MaskWord64ToUWord32(renderTimeMs)); - } - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, -1, "End Stochastic test 1"); - Trace::ReturnTrace(); - return 0; -} diff --git a/modules/video_coding/main/test/release_test.cc b/modules/video_coding/main/test/release_test.cc deleted file mode 100644 index 8e3a0734c..000000000 --- a/modules/video_coding/main/test/release_test.cc +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ReleaseTest.h" -#include "ReceiverTests.h" -#include "TestMacros.h" -#include "MediaOptTest.h" -#include "CodecDataBaseTest.h" -#include "GenericCodecTest.h" - - - - -int ReleaseTest() -{ - printf("VCM RELEASE TESTS \n\n"); - - // Automatic tests - - printf("Testing receive side timing...\n"); - TEST(ReceiverTimingTests() == 0); - - printf("Testing jitter buffer...\n"); - TEST(JitterBufferTest() == 0); - - printf("Testing Codec Data Base...\n"); - TEST(CodecDBTest() == 0); - - printf("Testing Media Optimization....\n"); - TEST(VCMMediaOptTest(1) == 0); - - // Tests requiring verification - - printf("Testing Multi thread send-receive....\n"); - TEST(MTRxTxTest() == 0); - printf("Verify by viewing output file MTRxTx_out.yuv \n"); - - return 0; -} \ No newline at end of file diff --git a/modules/video_coding/main/test/release_test.h b/modules/video_coding/main/test/release_test.h deleted file mode 100644 index 25781602c..000000000 --- a/modules/video_coding/main/test/release_test.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef RELEASE_TEST_H -#define RELEASE_TEST_H - -int ReleaseTest(); -int ReleaseTestPart2(); - -#endif \ No newline at end of file diff --git a/modules/video_coding/main/test/release_test_pt2.cc b/modules/video_coding/main/test/release_test_pt2.cc deleted file mode 100644 index 5ff48e593..000000000 --- a/modules/video_coding/main/test/release_test_pt2.cc +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ReleaseTest.h" -#include "ReceiverTests.h" -#include "TestMacros.h" -#include "MediaOptTest.h" -#include "CodecDataBaseTest.h" -#include "GenericCodecTest.h" - - - - -int ReleaseTestPart2() -{ - printf("Verify that TICK_TIME_DEBUG and EVENT_DEBUG are uncommented"); - // Tests requiring verification - - printf("Testing Generic Codecs...\n"); - TEST(VCMGenericCodecTest() == 0); - printf("Verify by viewing output file GCTest_out.yuv \n"); - - return 0; -} \ No newline at end of file diff --git a/modules/video_coding/main/test/resampler_test.cc b/modules/video_coding/main/test/resampler_test.cc deleted file mode 100644 index adcee1723..000000000 --- a/modules/video_coding/main/test/resampler_test.cc +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "ResamplerTest.h" -#include "video_coding.h" -#include "tick_time.h" -#include "../source/event.h" -#include "VCMSpatialResampler.h" - -#include -#include - -using namespace webrtc; - -int ResamplerTest() -{ - VideoCodingModule* vcm = VideoCodingModule::Create(1); - class ResamplerTest test(vcm); - int ret = test.Perform(); - VideoCodingModule::Destroy(vcm); - - return ret; -} - -ResamplerTest::ResamplerTest(VideoCodingModule* vcm): -_width(0), -_height(0), -_timeStamp(0), -_lengthSourceFrame(0), -_vcmMacrosTests(0), -_vcmMacrosErrors(0), -_vcm(vcm) -{ - // -} -ResamplerTest::~ResamplerTest() -{ - // -} -void -ResamplerTest::Setup() -{ - _inname= "../../../../../codecs_video/testFiles/foreman.yuv"; - _width = 352; - _height = 288; - _frameRate = 30; - _lengthSourceFrame = 3*_width*_height/2; - _encodedName = "../ResamplerTest_encoded.yuv"; - - if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) - { - printf("Cannot read file %s.\n", _inname.c_str()); - exit(1); - } - - if ((_encodedFile = fopen(_encodedName.c_str(), "wb")) == NULL) - { - printf("Cannot write encoded file.\n"); - exit(1); - } - - return; -} - -WebRtc_Word32 ResamplerTest::Perform() -{ - // Make sure this test isn't executed without simulated clocks -#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG) - return -1; -#endif - - // Setup test - Setup(); - - ResamplerStandAloneTest(); - - ResamplerVCMTest(); - - TearDown(); - return 0; -} - -void -ResamplerTest::ResamplerVCMTest() -{ - // Create the input frame and read a frame from file - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile); - sourceFrame.SetLength(_lengthSourceFrame); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - - TEST_EXIT_ON_FAIL(_vcm->InitializeReceiver() == VCM_OK); - TEST_EXIT_ON_FAIL(_vcm->InitializeSender() == VCM_OK); - - TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(true) == VCM_OK); - - TestSizeVCM(sourceFrame, 128, 80); // Cut, decimation 1x, interpolate - TestSizeVCM(sourceFrame, 352/2, 288/2); // Even decimation - TestSizeVCM(sourceFrame, 352, 288); // No resampling - TestSizeVCM(sourceFrame, 2*352, 2*288); // Upsampling 2x - TestSizeVCM(sourceFrame, 400, 256); // Upsampling 1.5x and cut - TestSizeVCM(sourceFrame, 960, 720); // Upsampling 3.5x and cut - - TEST_EXIT_ON_FAIL(_vcm->EnableInputFrameInterpolation(false) == VCM_OK); - - TestSizeVCM(sourceFrame, 320, 240); // Cropped - TestSizeVCM(sourceFrame, 1280, 720); // Padded -} - -void -ResamplerTest::TestSizeVCM(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight) -{ - assert(false); - /* - std::ostringstream filename; - filename << "../VCM_Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv"; - std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl; - FILE* decodedFile = fopen(filename.str().c_str(), "wb"); - - _timeStamp += (WebRtc_UWord32)(9e4 / _frameRate); - sourceFrame.SetTimeStamp(_timeStamp); - - VCMDecodeCompleteCallback decodeCallback(decodedFile); - VCMEncodeCompleteCallback encodeCompleteCallback(_encodedFile); - TEST_EXIT_ON_FAIL(_vcm->RegisterReceiveCallback(&decodeCallback) == VCM_OK); - TEST_EXIT_ON_FAIL(_vcm->RegisterTransportCallback(&encodeCompleteCallback) == VCM_OK); - encodeCompleteCallback.RegisterReceiverVCM(_vcm); - encodeCompleteCallback.SetCodecType(webrtc::VideoCodecVP8); - - RegisterCodec(targetWidth, targetHeight); - encodeCompleteCallback.SetFrameDimensions(targetWidth, targetHeight); - TEST(_vcm->AddVideoFrame(sourceFrame) == VCM_OK); - TEST(_vcm->Decode() == VCM_OK); - - fclose(decodedFile); - */ -} - -void -ResamplerTest::RegisterCodec(WebRtc_UWord32 width, WebRtc_UWord32 height) -{ - // Register codecs - assert(false); - /* - VideoCodec codec; - VideoCodingModule::Codec(webrtc::kVideoCodecVP8, &codec); - codec.width = static_cast(width); - codec.height = static_cast(height); - TEST(_vcm->RegisterSendCodec(&codec, 1, 1440) == VCM_OK); - TEST(_vcm->RegisterReceiveCodec(&codec, 1) == VCM_OK); - TEST(_vcm->SetChannelParameters(2000, 0, 0) == VCM_OK); - */ -} - -WebRtc_Word32 -ResamplerTest::ResamplerStandAloneTest() -{ - // Create the input frame and read a frame from file - VideoFrame sourceFrame; - sourceFrame.VerifyAndAllocate(_lengthSourceFrame); - fread(sourceFrame.Buffer(), 1, _lengthSourceFrame, _sourceFile); - sourceFrame.SetLength(_lengthSourceFrame); - sourceFrame.SetHeight(_height); - sourceFrame.SetWidth(_width); - - TestSize(sourceFrame, 100, 50); // Cut, decimation 1x, interpolate - TestSize(sourceFrame, 352/2, 288/2); // Even decimation - TestSize(sourceFrame, 352, 288); // No resampling - TestSize(sourceFrame, 2*352, 2*288); // Even upsampling - TestSize(sourceFrame, 400, 256); // Upsampling 1.5x and cut - TestSize(sourceFrame, 960, 720); // Upsampling 3.5x and cut - TestSize(sourceFrame, 1280, 720); // Upsampling 4x and cut - - sourceFrame.Free(); - return 0; -} - -void -ResamplerTest::TestSize(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight) -{ - VCMSimpleSpatialResampler resampler; - VideoFrame outFrame; - std::ostringstream filename; - filename << "../Resampler_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv"; - std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl; - FILE* standAloneFile = fopen(filename.str().c_str(), "wb"); - //resampler.EnableUpSampling(true); - resampler.EnableInterpolation(true); - TEST(resampler.SetTargetFrameSize(targetWidth, targetHeight) == VCM_OK); - TEST(resampler.ResampleFrame(sourceFrame, outFrame) == VCM_OK); - TEST(outFrame.Buffer() != NULL); - TEST(outFrame.Length() == (targetWidth * targetHeight * 3 / 2)); - - // Write to file for visual inspection - fwrite(outFrame.Buffer(), 1, outFrame.Length(), standAloneFile); - - outFrame.Free(); - fclose(standAloneFile); -} - -void -ResamplerTest::Print() -{ - printf("\nVCM Resampler Test: \n\n%i tests completed\n", _vcmMacrosTests); - if (_vcmMacrosErrors > 0) - { - printf("%i FAILED\n\n", _vcmMacrosErrors); - } - else - { - printf("ALL PASSED\n\n"); - } -} - -void -ResamplerTest::TearDown() -{ - fclose(_sourceFile); - fclose(_encodedFile); - return; -} - -void -ResamplerTest::IncrementDebugClock(float frameRate) -{ - for (int t= 0; t < 1000/frameRate; t++) - { - VCMTickTime::IncrementDebugClock(); - } - return; -} - diff --git a/modules/video_coding/main/test/rtp_player.cc b/modules/video_coding/main/test/rtp_player.cc deleted file mode 100644 index 38bff97aa..000000000 --- a/modules/video_coding/main/test/rtp_player.cc +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "rtp_player.h" -#include "../source/internal_defines.h" -#include "rtp_rtcp.h" -#include "tick_time.h" - -#include -#ifdef WIN32 -#include -#include -#else -#include -#endif - -using namespace webrtc; - -RawRtpPacket::RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len) -: -rtpData(), rtpLen(len), resendTimeMs(-1) -{ - rtpData = new WebRtc_UWord8[rtpLen]; - memcpy(rtpData, data, rtpLen); -} - -RawRtpPacket::~RawRtpPacket() -{ - delete [] rtpData; -} - -LostPackets::LostPackets() -: -_critSect(*CriticalSectionWrapper::CreateCriticalSection()), -_lossCount(0), -ListWrapper(), -_debugFile(NULL) -{ - _debugFile = fopen("PacketLossDebug.txt", "w"); -} - -LostPackets::~LostPackets() -{ - if (_debugFile) - { - fclose(_debugFile); - } - ListItem* item = First(); - while (item != NULL) - { - RawRtpPacket* packet = static_cast(item->GetItem()); - if (packet != NULL) - { - delete packet; - } - Erase(item); - item = First(); - } - delete &_critSect; -} - -WebRtc_UWord32 LostPackets::AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen) -{ - CriticalSectionScoped cs(_critSect); - RawRtpPacket* packet = new RawRtpPacket(rtpData, rtpLen); - ListItem* newItem = new ListItem(packet); - InsertBefore(First(), newItem); - const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3]; - if (_debugFile != NULL) - { - fprintf(_debugFile, "%u Lost packet: %u\n", _lossCount, seqNo); - } - _lossCount++; - return 0; -} - -WebRtc_UWord32 LostPackets::SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime) -{ - CriticalSectionScoped cs(_critSect); - ListItem* item = First(); - while (item != NULL) - { - RawRtpPacket* packet = static_cast(item->GetItem()); - const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; - const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); - if (sequenceNumber == seqNo && packet->resendTimeMs + 10 < nowMs) - { - if (_debugFile != NULL) - { - fprintf(_debugFile, "Resend %u at %u\n", seqNo, MaskWord64ToUWord32(resendTime)); - } - packet->resendTimeMs = resendTime; - return 0; - } - item = Next(item); - } - fprintf(_debugFile, "Packet not lost %u\n", sequenceNumber); - return -1; -} - -WebRtc_UWord32 LostPackets::NumberOfPacketsToResend() const -{ - CriticalSectionScoped cs(_critSect); - WebRtc_UWord32 count = 0; - ListItem* item = First(); - while (item != NULL) - { - RawRtpPacket* packet = static_cast(item->GetItem()); - if (packet->resendTimeMs >= 0) - { - count++; - } - item = Next(item); - } - return count; -} - -void LostPackets::ResentPacket(WebRtc_UWord16 seqNo) -{ - CriticalSectionScoped cs(_critSect); - if (_debugFile != NULL) - { - fprintf(_debugFile, "Resent %u at %u\n", seqNo, - MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp())); - } -} - -RTPPlayer::RTPPlayer(const char* filename, RtpData* callback) -: -_rtpModule(*RtpRtcp::CreateRtpRtcp(1, false)), -_nextRtpTime(0), -_dataCallback(callback), -_firstPacket(true), -_lossRate(0.0f), -_nackEnabled(false), -_resendPacketCount(0), -_noLossStartup(100), -_endOfFile(false), -_rttMs(0), -_firstPacketRtpTime(0), -_firstPacketTimeMs(0), -_reorderBuffer(NULL), -_reordering(false), -_nextPacket(), -_nextPacketLength(0), -_randVec(), -_randVecPos(0) -{ - _rtpFile = fopen(filename, "rb"); - memset(_nextPacket, 0, sizeof(_nextPacket)); -} - -RTPPlayer::~RTPPlayer() -{ - RtpRtcp::DestroyRtpRtcp(&_rtpModule); - if (_rtpFile != NULL) - { - fclose(_rtpFile); - } - if (_reorderBuffer != NULL) - { - delete _reorderBuffer; - _reorderBuffer = NULL; - } -} - -WebRtc_Word32 RTPPlayer::Initialize(const ListWrapper& payloadList) -{ - std::srand(321); - for (int i=0; i < RAND_VEC_LENGTH; i++) - { - _randVec[i] = rand(); - } - _randVecPos = 0; - WebRtc_Word32 ret = _rtpModule.SetNACKStatus(kNackOff); - if (ret < 0) - { - return -1; - } - ret = _rtpModule.InitReceiver(); - if (ret < 0) - { - return -1; - } - - _rtpModule.InitSender(); - _rtpModule.SetRTCPStatus(kRtcpNonCompound); - _rtpModule.SetTMMBRStatus(true); - - ret = _rtpModule.RegisterIncomingDataCallback(_dataCallback); - if (ret < 0) - { - return -1; - } - // Register payload types - ListItem* item = payloadList.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - if (_rtpModule.RegisterReceivePayload(payloadType->name.c_str(), payloadType->payloadType) < 0) - { - return -1; - } - } - item = payloadList.Next(item); - } - if (ReadHeader() < 0) - { - return -1; - } - memset(_nextPacket, 0, sizeof(_nextPacket)); - _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); - return 0; -} - -WebRtc_Word32 RTPPlayer::ReadHeader() -{ - char firstline[FIRSTLINELEN]; - if (_rtpFile == NULL) - { - return -1; - } - fgets(firstline, FIRSTLINELEN, _rtpFile); - if(strncmp(firstline,"#!rtpplay",9) == 0) { - if(strncmp(firstline,"#!rtpplay1.0",12) != 0){ - printf("ERROR: wrong rtpplay version, must be 1.0\n"); - return -1; - } - } - else if (strncmp(firstline,"#!RTPencode",11) == 0) { - if(strncmp(firstline,"#!RTPencode1.0",14) != 0){ - printf("ERROR: wrong RTPencode version, must be 1.0\n"); - return -1; - } - } - else { - printf("ERROR: wrong file format of input file\n"); - return -1; - } - - WebRtc_UWord32 start_sec; - WebRtc_UWord32 start_usec; - WebRtc_UWord32 source; - WebRtc_UWord16 port; - WebRtc_UWord16 padding; - - fread(&start_sec, 4, 1, _rtpFile); - start_sec=ntohl(start_sec); - fread(&start_usec, 4, 1, _rtpFile); - start_usec=ntohl(start_usec); - fread(&source, 4, 1, _rtpFile); - source=ntohl(source); - fread(&port, 2, 1, _rtpFile); - port=ntohs(port); - fread(&padding, 2, 1, _rtpFile); - padding=ntohs(padding); - return 0; -} - -WebRtc_UWord32 RTPPlayer::TimeUntilNextPacket() const -{ - WebRtc_Word64 timeLeft = (_nextRtpTime - _firstPacketRtpTime) - (VCMTickTime::MillisecondTimestamp() - _firstPacketTimeMs); - if (timeLeft < 0) - { - return 0; - } - return static_cast(timeLeft); -} - -WebRtc_Word32 RTPPlayer::NextPacket(const WebRtc_Word64 timeNow) -{ - // Send any packets ready to be resent - _lostPackets.Lock(); - ListItem* item = _lostPackets.First(); - _lostPackets.Unlock(); - while (item != NULL) - { - _lostPackets.Lock(); - RawRtpPacket* packet = static_cast(item->GetItem()); - _lostPackets.Unlock(); - if (timeNow >= packet->resendTimeMs && packet->resendTimeMs != -1) - { - const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; - printf("Resend: %u\n", seqNo); - WebRtc_Word32 ret = SendPacket(packet->rtpData, packet->rtpLen); - ListItem* itemToRemove = item; - _lostPackets.Lock(); - item = _lostPackets.Next(item); - _lostPackets.Erase(itemToRemove); - delete packet; - _lostPackets.Unlock(); - _resendPacketCount++; - if (ret > 0) - { - _lostPackets.ResentPacket(seqNo); - } - else if (ret < 0) - { - return ret; - } - } - else - { - _lostPackets.Lock(); - item = _lostPackets.Next(item); - _lostPackets.Unlock(); - } - } - - // Send any packets from rtp file - if (!_endOfFile && (TimeUntilNextPacket() == 0 || _firstPacket)) - { - _rtpModule.Process(); - if (_firstPacket) - { - _firstPacketRtpTime = static_cast(_nextRtpTime); - _firstPacketTimeMs = VCMTickTime::MillisecondTimestamp(); - } - if (_reordering && _reorderBuffer == NULL) - { - _reorderBuffer = new RawRtpPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); - return 0; - } - WebRtc_Word32 ret = SendPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); - if (_reordering && _reorderBuffer != NULL) - { - RawRtpPacket* rtpPacket = _reorderBuffer; - _reorderBuffer = NULL; - SendPacket(rtpPacket->rtpData, rtpPacket->rtpLen); - delete rtpPacket; - } - _firstPacket = false; - if (ret < 0) - { - return ret; - } - _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); - if (_nextPacketLength < 0) - { - _endOfFile = true; - return 0; - } - else if (_nextPacketLength == 0) - { - return 0; - } - } - if (_endOfFile && _lostPackets.NumberOfPacketsToResend() == 0) - { - return 1; - } - return 0; -} - -WebRtc_Word32 RTPPlayer::SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen) -{ - if ((_randVec[(_randVecPos++) % RAND_VEC_LENGTH] + 1.0)/(RAND_MAX + 1.0) < _lossRate && - _noLossStartup < 0) - { - if (_nackEnabled) - { - const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3]; - printf("Throw: %u\n", seqNo); - _lostPackets.AddPacket(rtpData, rtpLen); - return 0; - } - } - else - { - WebRtc_Word32 ret = _rtpModule.IncomingPacket(rtpData, rtpLen); - if (ret < 0) - { - return -1; - } - } - if (_noLossStartup >= 0) - { - _noLossStartup--; - } - return 1; -} - -WebRtc_Word32 RTPPlayer::ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset) -{ - WebRtc_UWord16 length, plen; - - if (fread(&length,2,1,_rtpFile)==0) - return(-1); - length=ntohs(length); - - if (fread(&plen,2,1,_rtpFile)==0) - return(-1); - plen=ntohs(plen); - - if (fread(offset,4,1,_rtpFile)==0) - return(-1); - *offset=ntohl(*offset); - - // Use length here because a plen of 0 specifies rtcp - length = (WebRtc_UWord16) (length - HDR_SIZE); - if (fread((unsigned short *) rtpdata,1,length,_rtpFile) != length) - return(-1); - -#ifdef JUNK_DATA - // destroy the RTP payload with random data - if (plen > 12) { // ensure that we have more than just a header - for ( int ix = 12; ix < plen; ix=ix+2 ) { - rtpdata[ix>>1] = (short) (rtpdata[ix>>1] + (short) rand()); - } - } -#endif - return plen; -} - -WebRtc_Word32 RTPPlayer::SimulatePacketLoss(float lossRate, bool enableNack, WebRtc_UWord32 rttMs) -{ - _nackEnabled = enableNack; - _lossRate = lossRate; - _rttMs = rttMs; - return 0; -} - -WebRtc_Word32 RTPPlayer::SetReordering(bool enabled) -{ - _reordering = enabled; - return 0; -} - -WebRtc_Word32 RTPPlayer::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length) -{ - if (sequenceNumbers == NULL) - { - return 0; - } - for (int i=0; i < length; i++) - { - _lostPackets.SetResendTime(sequenceNumbers[i], VCMTickTime::MillisecondTimestamp() + _rttMs); - } - return 0; -} - -void RTPPlayer::Print() const -{ - printf("Lost packets: %u, resent packets: %u\n", _lostPackets.TotalNumberOfLosses(), _resendPacketCount); - printf("Packets still lost: %u\n", _lostPackets.GetSize()); - printf("Packets waiting to be resent: %u\n", _lostPackets.NumberOfPacketsToResend()); - printf("Sequence numbers:\n"); - ListItem* item = _lostPackets.First(); - while (item != NULL) - { - RawRtpPacket* packet = static_cast(item->GetItem()); - const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; - printf("%u, ", seqNo); - item = _lostPackets.Next(item); - } - printf("\n"); -} diff --git a/modules/video_coding/main/test/rtp_player.h b/modules/video_coding/main/test/rtp_player.h deleted file mode 100644 index 53fd86749..000000000 --- a/modules/video_coding/main/test/rtp_player.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ - -#include "typedefs.h" -#include "rtp_rtcp.h" -#include "list_wrapper.h" -#include "critical_section_wrapper.h" -#include "video_coding_defines.h" - -#include -#include - -#define HDR_SIZE 8 // rtpplay packet header size in bytes -#define FIRSTLINELEN 40 -#define RAND_VEC_LENGTH 4096 - -struct RawRtpPacket -{ -public: - RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len); - ~RawRtpPacket(); - - WebRtc_UWord8* rtpData; - WebRtc_UWord16 rtpLen; - WebRtc_Word64 resendTimeMs; -}; - -class LostPackets : public webrtc::ListWrapper -{ -public: - LostPackets(); - ~LostPackets(); - - WebRtc_UWord32 AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen); - WebRtc_UWord32 SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime); - WebRtc_UWord32 TotalNumberOfLosses() const { return _lossCount; }; - WebRtc_UWord32 NumberOfPacketsToResend() const; - void ResentPacket(WebRtc_UWord16 seqNo); - void Lock() {_critSect.Enter();}; - void Unlock() {_critSect.Leave();}; -private: - webrtc::CriticalSectionWrapper& _critSect; - WebRtc_UWord32 _lossCount; - FILE* _debugFile; -}; - -struct PayloadCodecTuple -{ - PayloadCodecTuple(WebRtc_UWord8 plType, std::string codecName, webrtc::VideoCodecType type) : - name(codecName), payloadType(plType), codecType(type) {}; - const std::string name; - const WebRtc_UWord8 payloadType; - const webrtc::VideoCodecType codecType; -}; - -class RTPPlayer : public webrtc::VCMPacketRequestCallback -{ -public: - RTPPlayer(const char* filename, webrtc::RtpData* callback); - virtual ~RTPPlayer(); - - WebRtc_Word32 Initialize(const webrtc::ListWrapper& payloadList); - WebRtc_Word32 NextPacket(const WebRtc_Word64 timeNow); - WebRtc_UWord32 TimeUntilNextPacket() const; - WebRtc_Word32 SimulatePacketLoss(float lossRate, bool enableNack = false, WebRtc_UWord32 rttMs = 0); - WebRtc_Word32 SetReordering(bool enabled); - WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length); - void Print() const; - -private: - WebRtc_Word32 SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen); - WebRtc_Word32 ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset); - WebRtc_Word32 ReadHeader(); - FILE* _rtpFile; - webrtc::RtpRtcp& _rtpModule; - WebRtc_UWord32 _nextRtpTime; - webrtc::RtpData* _dataCallback; - bool _firstPacket; - float _lossRate; - bool _nackEnabled; - LostPackets _lostPackets; - WebRtc_UWord32 _resendPacketCount; - WebRtc_Word32 _noLossStartup; - bool _endOfFile; - WebRtc_UWord32 _rttMs; - WebRtc_Word64 _firstPacketRtpTime; - WebRtc_Word64 _firstPacketTimeMs; - RawRtpPacket* _reorderBuffer; - bool _reordering; - WebRtc_Word16 _nextPacket[8000]; - WebRtc_Word32 _nextPacketLength; - int _randVec[RAND_VEC_LENGTH]; - int _randVecPos; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_RTP_PLAYER_H_ diff --git a/modules/video_coding/main/test/subfigure.m b/modules/video_coding/main/test/subfigure.m deleted file mode 100644 index eadfcb69b..000000000 --- a/modules/video_coding/main/test/subfigure.m +++ /dev/null @@ -1,30 +0,0 @@ -function H = subfigure(m, n, p) -% -% H = SUBFIGURE(m, n, p) -% -% Create a new figure window and adjust position and size such that it will -% become the p-th tile in an m-by-n matrix of windows. (The interpretation of -% m, n, and p is the same as for SUBPLOT. -% -% Henrik Lundin, 2009-01-19 -% - - -h = figure; - -[j, i] = ind2sub([n m], p); -scrsz = get(0,'ScreenSize'); % get screen size -%scrsz = [1, 1, 1600, 1200]; - -taskbarSize = 58; -windowbarSize = 68; -windowBorder = 4; - -scrsz(2) = scrsz(2) + taskbarSize; -scrsz(4) = scrsz(4) - taskbarSize; - -set(h, 'position', [(j-1)/n * scrsz(3) + scrsz(1) + windowBorder,... - (m-i)/m * scrsz(4) + scrsz(2) + windowBorder, ... - scrsz(3)/n - (windowBorder + windowBorder),... - scrsz(4)/m - (windowbarSize + windowBorder + windowBorder)]); - diff --git a/modules/video_coding/main/test/test_macros.h b/modules/video_coding/main/test/test_macros.h deleted file mode 100644 index 14ae71f63..000000000 --- a/modules/video_coding/main/test/test_macros.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef VCM_TEST_MACROS_H -#define VCM_TEST_MACROS_H - -#include -#include - -static int vcmMacrosTests = 0; -static int vcmMacrosErrors = 0; - -#define PRINT_ERR_MSG(msg) \ - do { \ - fprintf(stderr, "Error at line %i of %s\n%s", \ - __LINE__, __FILE__, msg); \ - } while(0) - -#define TEST(expr) \ - do { \ - vcmMacrosTests++; \ - if (!(expr)) { \ - PRINT_ERR_MSG("Assertion failed: " #expr "\n\n"); \ - vcmMacrosErrors++; \ - } \ - } while(0) - -#define TEST_EXIT_ON_FAIL(expr) \ - do { \ - vcmMacrosTests++; \ - if (!(expr)) { \ - PRINT_ERR_MSG("Assertion failed: " #expr "\nExiting...\n\n"); \ - vcmMacrosErrors++; \ - exit(EXIT_FAILURE); \ - } \ - } while(0) - -#endif \ No newline at end of file diff --git a/modules/video_coding/main/test/test_util.cc b/modules/video_coding/main/test/test_util.cc deleted file mode 100644 index d2db97f2e..000000000 --- a/modules/video_coding/main/test/test_util.cc +++ /dev/null @@ -1,725 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "test_util.h" -#include "rtp_dump.h" -#include - -using namespace webrtc; - -/****************************** - * VCMEncodeCompleteCallback - *****************************/ -// Basic callback implementation -// passes the encoded frame directly to the encoder -// Packetization callback implmentation -VCMEncodeCompleteCallback::VCMEncodeCompleteCallback(FILE* encodedFile): -_seqNo(0), -_encodedFile(encodedFile), -_encodedBytes(0), -_VCMReceiver(NULL), -_encodeComplete(false), -_width(0), -_height(0), -_codecType(kRTPVideoNoVideo), -_layerPacketId(1) -{ - // -} -VCMEncodeCompleteCallback::~VCMEncodeCompleteCallback() -{ -} - -void -VCMEncodeCompleteCallback::RegisterTransportCallback(VCMPacketizationCallback* transport) -{ -} - -WebRtc_Word32 -VCMEncodeCompleteCallback::SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader) -{ - // will call the VCMReceiver input packet - _frameType = frameType; - // writing encodedData into file - fwrite(payloadData, 1, payloadSize, _encodedFile); - WebRtcRTPHeader rtpInfo; - rtpInfo.header.markerBit = true; // end of frame - rtpInfo.type.Video.isFirstPacket = true; - rtpInfo.type.Video.codec = _codecType; - switch (_codecType) - { - case webrtc::kRTPVideoH263: - rtpInfo.type.Video.codecHeader.H263.bits = false; - rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false; - rtpInfo.type.Video.height = (WebRtc_UWord16)_height; - rtpInfo.type.Video.width = (WebRtc_UWord16)_width; - break; - } - - rtpInfo.header.payloadType = payloadType; - rtpInfo.header.sequenceNumber = _seqNo++; - rtpInfo.header.ssrc = 0; - rtpInfo.header.timestamp = timeStamp; - rtpInfo.frameType = frameType; - // Size should also be received from that table, since the payload type - // defines the size. - - _encodedBytes += payloadSize; - // directly to receiver - _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo); - _encodeComplete = true; - - return 0; -} - - float - VCMEncodeCompleteCallback::EncodedBytes() - { - return _encodedBytes; - } - -bool -VCMEncodeCompleteCallback::EncodeComplete() -{ - if (_encodeComplete) - { - _encodeComplete = false; - return true; - } - return false; -} - -void -VCMEncodeCompleteCallback::Initialize() -{ - _encodeComplete = false; - _encodedBytes = 0; - _seqNo = 0; - return; -} - -void -VCMEncodeCompleteCallback::ResetByteCount() -{ - _encodedBytes = 0; -} - -/**********************************/ -/* VCMRTPEncodeCompleteCallback / -/********************************/ -// Encode Complete callback implementation -// passes the encoded frame via the RTP module to the decoder -// Packetization callback implmentation - -WebRtc_Word32 -VCMRTPEncodeCompleteCallback::SendData(const FrameType frameType, - const WebRtc_UWord8 payloadType, - const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, - const WebRtc_UWord32 payloadSize, - const RTPFragmentationHeader& fragmentationHeader) -{ - _frameType = frameType; - _encodedBytes+= payloadSize; - _encodeComplete = true; - //printf("encoded = %d Bytes\n", payloadSize); - return _RTPModule->SendOutgoingData(frameType, payloadType, timeStamp, payloadData, payloadSize, &fragmentationHeader); -} - - float - VCMRTPEncodeCompleteCallback::EncodedBytes() - { - // only good for one call - after which will reset value; - float tmp = _encodedBytes; - _encodedBytes = 0; - return tmp; - } - -bool -VCMRTPEncodeCompleteCallback::EncodeComplete() -{ - if (_encodeComplete) - { - _encodeComplete = false; - return true; - } - return false; -} - -// Decoded Frame Callback Implmentation - - WebRtc_Word32 - VCMDecodeCompleteCallback::FrameToRender(VideoFrame& videoFrame) - { - fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile); - _decodedBytes+= videoFrame.Length(); - // keeping last decoded frame - _lastDecodedFrame.VerifyAndAllocate(videoFrame.Size()); - _lastDecodedFrame.CopyFrame(videoFrame.Size(), videoFrame.Buffer()); - _lastDecodedFrame.SetHeight(videoFrame.Height()); - _lastDecodedFrame.SetWidth(videoFrame.Width()); - _lastDecodedFrame.SetTimeStamp(videoFrame.TimeStamp()); - - return VCM_OK; - } - -int -VCMDecodeCompleteCallback::PSNRLastFrame(const VideoFrame& sourceFrame, double *YPSNRptr) -{ - double mse = 0.0; - double mseLogSum = 0.0; - - WebRtc_Word32 frameBytes = sourceFrame.Height() * sourceFrame.Width(); // only Y - WebRtc_UWord8 *ref = sourceFrame.Buffer(); - if (_lastDecodedFrame.Height() == 0) - { - *YPSNRptr = 0; - return 0; // no new decoded frames - } - WebRtc_UWord8 *test = _lastDecodedFrame.Buffer(); - for( int k = 0; k < frameBytes; k++ ) - { - mse += (test[k] - ref[k]) * (test[k] - ref[k]); - } - - // divide by number of pixels - mse /= (double) (frameBytes); - - // accumulate for total average - mseLogSum += std::log10( mse ); - - *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum; // for only 1 frame - - _lastDecodedFrame.Free(); - _lastDecodedFrame.SetHeight(0); - return 0; -} - - WebRtc_Word32 - VCMDecodeCompleteCallback::DecodedBytes() - { - return _decodedBytes; - } - - RTPSendCompleteCallback::RTPSendCompleteCallback(RtpRtcp* rtp, const char* filename): - _rtp(rtp), - _sendCount(0), - _lossPct(0), - _rtpDump(NULL) - { - if (filename != NULL) - { - _rtpDump = RtpDump::CreateRtpDump(); - _rtpDump->Start(filename); - } - } - RTPSendCompleteCallback::~RTPSendCompleteCallback() - { - if (_rtpDump != NULL) - { - _rtpDump->Stop(); - RtpDump::DestroyRtpDump(_rtpDump); - } - } -int -RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len) -{ - _sendCount++; - // Packet Loss - randomly drop %loss packets - // don't drop I-frame packets - if(PacketLoss(_lossPct) && (_sendCount > 12)) - { - // drop - //printf("\tDrop packet, sendCount = %d\n", _sendCount); - return len; - } - if (_rtpDump != NULL) - { - if (_rtpDump->DumpPacket((const WebRtc_UWord8*)data, len) != 0) - { - return -1; - } - } - if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; - } - -int -RTPSendCompleteCallback::SendRTCPPacket(int channel, const void *data, int len) -{ - if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) - { - return len; - } - return -1; -} - -void -RTPSendCompleteCallback::SetLossPct(double lossPct) -{ - _lossPct = lossPct; - return; -} - -bool -RTPSendCompleteCallback::PacketLoss(double lossPct) -{ - double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0); - return randVal < lossPct/100; -} - -WebRtc_Word32 -PacketRequester::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length) -{ - return _rtp.SendNACK(sequenceNumbers, length); -} - -WebRtc_Word32 -PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *YPSNRptr) -{ - FILE *refFp = fopen(refFileName, "rb"); - if( refFp == NULL ) { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if( testFp == NULL ) { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - double mse = 0.0; - double mseLogSum = 0.0; - WebRtc_Word32 frames = 0; - - WebRtc_Word32 frameBytes = 3*width*height/2; // bytes in one frame I420 - WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; // space for one frame I420 - WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; // space for one frame I420 - - WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp); - WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp); - - while( refBytes == frameBytes && testBytes == frameBytes ) - { - mse = 0.0; - - int sh = 8;//boundary offset - for( int k2 = sh; k2 < height-sh;k2++) - for( int k = sh; k < width-sh;k++) - { - int kk = k2*width + k; - mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]); - } - - // divide by number of pixels - mse /= (double) (width * height); - - // accumulate for total average - mseLogSum += std::log10( mse ); - frames++; - - refBytes = (int) fread(ref, 1, frameBytes, refFp); - testBytes = (int) fread(test, 1, frameBytes, testFp); - } - // for identical reproduction: - if (mse == 0) - { - *YPSNRptr = 48; - } - else - { - *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames; - } - - - delete [] ref; - delete [] test; - - fclose(refFp); - fclose(testFp); - - return 0; -} - -WebRtc_Word32 -SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr) -{ - FILE *refFp = fopen(refFileName, "rb"); - if( refFp == NULL ) { - // cannot open reference file - fprintf(stderr, "Cannot open file %s\n", refFileName); - return -1; - } - - FILE *testFp = fopen(testFileName, "rb"); - if( testFp == NULL ) { - // cannot open test file - fprintf(stderr, "Cannot open file %s\n", testFileName); - return -2; - } - - int frames = 0; - - int frameBytes = 3*width*height/2; // bytes in one frame I420 - unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420 - unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420 - - int refBytes = (int) fread(ref, 1, frameBytes, refFp); - int testBytes = (int) fread(test, 1, frameBytes, testFp); - - float *righMostColumnAvgTest = new float[width]; - float *righMostColumnAvgRef = new float[width]; - float *righMostColumnContrastTest = new float[width]; - float *righMostColumnContrastRef = new float[width]; - float *righMostColumnCrossCorr = new float[width]; - - float term1,term2,term3,term4,term5; - - // - // SSIM: variable definition, window function, initialization - int window = 10; - // - int flag_window = 0; //0 and 1 for uniform window filter, 2 for gaussian window - // - float variance_window = 2.0; //variance for window function - float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1) - //statistics per column of window (#columns = window+1), 0 element for avg over all columns - float avgTest[12]; - float avgRef[12]; - float contrastTest[12]; - float contrastRef[12]; - float crossCorr[12]; - // - //offsets for stability - float offset1 = 1.0f; //0.1 - float offset2 = 1.0f; //0.1 - //for Guassian window: settings from paper: - //float offset1 = 6.0f; // ~ (K1*L)^2 , K1 = 0.01 - //float offset2 = 58.0f; // ~ (K1*L)^2 , K2 = 0.03 - - - float offset3 = offset2/2; - // - //define window for SSIM: take uniform filter for now - float sumfil = 0.0; - int nn=-1; - for(int j=-window/2;j<=window/2;j++) - for(int i=-window/2;i<=window/2;i++) - { - nn+=1; - if (flag_window != 2) - ssimFilter[nn] = 1.0; - else - { - float dist = (float)(i*i) + (float)(j*j); - float tmp = 0.5f*dist/variance_window; - ssimFilter[nn] = exp(-tmp); - } - sumfil +=ssimFilter[nn]; - } - //normalize window - nn=-1; - for(int j=-window/2;j<=window/2;j++) - for(int i=-window/2;i<=window/2;i++) - { - nn+=1; - ssimFilter[nn] = ssimFilter[nn]/((float)sumfil); - } - // - float ssimScene = 0.0; //avgerage SSIM for sequence - // - //SSIM: done with variables and defintion - // - - int sh = 8; //boundary offset - - while( refBytes == frameBytes && testBytes == frameBytes ) - { - float ssimFrame = 0.0; - - int numPixels = 0; - - //skip over pixels vertically and horizontally - //for window cases 1 and 2 - int skipH = 2; - int skipV = 2; - - //uniform window case, with window computation updated for each pixel horiz and vert: can't skip pixels for this case - if (flag_window == 0) - { - skipH = 1; - skipV = 1; - } - for(int i=sh;i 0 ) - { - //initialize statistics - avgTest[0] = 0.0; - avgRef[0] = 0.0; - contrastTest[0] = 0.0; - contrastRef[0] = 0.0; - crossCorr[0] = 0.0; - - int nn=-1; - //compute contrast and correlation - //windows are symmetrics - for(int jj=-window/2;jj<=window/2;jj++) - for(int ii=-window/2;ii<=window/2;ii++) - { - nn+=1; - int i2 = i+ii; - int j2 = j+jj; - float tmp1 = (float)test[i2*width+j2]; - float tmp2 = (float)ref[i2*width+j2]; - - term1 = tmp1; - term2 = tmp2; - term3 = tmp1*tmp1; - term4 = tmp2*tmp2; - term5 = tmp1*tmp2; - - //local average of each signal - avgTest[0] += ssimFilter[nn]*term1; - avgRef[0] += ssimFilter[nn]*term2; - //local correlation/contrast of each signal - contrastTest[0] += ssimFilter[nn]*term3; - contrastRef[0] += ssimFilter[nn]*term4; - //local cross correlation - crossCorr[0] += ssimFilter[nn]*term5; - - } - - } - - else - { - //for uniform window case == 0: only need to loop over whole window for first row and column, and then shift/update - if (j == sh || i == sh) - { - //initialize statistics - for(int k=0;k 0 && bitRate < 100000); - printf("VCM 1 sec: Bit rate: %u\tFrame rate: %u\n", bitRate, frameRate); - return 0; -} - -WebRtc_Word32 -KeyFrameReqTest::FrameTypeRequest(const FrameType frameType) -{ - TEST(frameType == kVideoFrameKey); - if (frameType == kVideoFrameKey) - { - printf("Key frame requested\n"); - } - else - { - printf("Non-key frame requested: %d\n", frameType); - } - return 0; -} diff --git a/modules/video_coding/main/test/test_util.h b/modules/video_coding/main/test/test_util.h deleted file mode 100644 index 42b1bf1e2..000000000 --- a/modules/video_coding/main/test/test_util.h +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TEST_UTIL_H -#define TEST_UTIL_H - -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "trace.h" -#include "module_common_types.h" -#include "tick_time.h" -#include "test_macros.h" -#include "test_util.h" - -#include -#include -#include - -enum { kMaxWaitEncTimeMs = 100 }; - -// Class used for passing command line arguments to tests -class CmdArgs -{ -public: - CmdArgs() : codecName(""), codecType(webrtc::kVideoCodecVP8), width(-1), - height(-1), bitRate(-1), frameRate(-1), - inputFile(""), outputFile(""), testNum(-1) - {} - std::string codecName; - webrtc::VideoCodecType codecType; - int width; - int height; - int bitRate; - int frameRate; - std::string inputFile; - std::string outputFile; - int testNum; -}; - -// forward declaration -int MTRxTxTest(CmdArgs& args); -namespace webrtc -{ - class RtpDump; -} - -// definition of general test function to be used by VCM tester (mainly send side) -/* - Includes the following: - 1. General Callback definition for VCM test functions - no RTP. - 2. EncodeComplete callback: - 2a. Transfer encoded data directly to the decoder - 2b. Pass encoded data via the RTP module - 3. Caluclate PSNR from file function (for now: does not deal with frame drops) - */ - -//Send Side - Packetization callback - send an encoded frame directly to the VCMReceiver -class VCMEncodeCompleteCallback: public webrtc::VCMPacketizationCallback -{ -public: - // constructor input: file in which encoded data will be written, and test parameters - VCMEncodeCompleteCallback(FILE* encodedFile); - virtual ~VCMEncodeCompleteCallback(); - // Register transport callback - void RegisterTransportCallback(webrtc::VCMPacketizationCallback* transport); - // process encoded data received from the encoder, pass stream to the VCMReceiver module - WebRtc_Word32 SendData(const webrtc::FrameType frameType, - const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader); - // Register exisitng VCM. Currently - encode and decode with the same vcm module. - void RegisterReceiverVCM(webrtc::VideoCodingModule *vcm) { _VCMReceiver = vcm; } - // Return size of last encoded frame encoded data (all frames in the sequence) - // Good for only one call - after which will reset value (to allow detection of frame drop) - float EncodedBytes(); - // return encode complete (true/false) - bool EncodeComplete(); - // Inform callback of codec used - void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; } - // inform callback of frame dimensions - void SetFrameDimensions(WebRtc_Word32 width, WebRtc_Word32 height) - { - _width = width; - _height = height; - } - //Initialize callback data - void Initialize(); - void ResetByteCount(); - - // conversion function for payload type (needed for the callback function) - // RTPVideoVideoCodecTypes ConvertPayloadType(WebRtc_UWord8 payloadType); - -private: - FILE* _encodedFile; - float _encodedBytes; - webrtc::VideoCodingModule* _VCMReceiver; - webrtc::FrameType _frameType; - WebRtc_UWord8* _payloadData; - WebRtc_UWord8 _seqNo; - bool _encodeComplete; - WebRtc_Word32 _width; - WebRtc_Word32 _height; - webrtc::RTPVideoCodecTypes _codecType; - WebRtc_UWord8 _layerPacketId; - -}; // end of VCMEncodeCompleteCallback - -//Send Side - Packetization callback - packetize an encoded frame via the RTP module -class VCMRTPEncodeCompleteCallback: public webrtc::VCMPacketizationCallback -{ -public: - VCMRTPEncodeCompleteCallback(webrtc::RtpRtcp* rtp) : - _seqNo(0), _encodedBytes(0), _RTPModule(rtp), _encodeComplete(false) {} - virtual ~VCMRTPEncodeCompleteCallback() {} - // process encoded data received from the encoder, pass stream to the RTP module - WebRtc_Word32 SendData(const webrtc::FrameType frameType, - const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timeStamp, - const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, - const webrtc::RTPFragmentationHeader& fragmentationHeader); - // Return size of last encoded frame. Value good for one call - // (resets to zero after call to inform test of frame drop) - float EncodedBytes(); - // return encode complete (true/false) - bool EncodeComplete(); - // Inform callback of codec used - void SetCodecType(webrtc::RTPVideoCodecTypes codecType) { _codecType = codecType; } - - // inform callback of frame dimensions - void SetFrameDimensions(WebRtc_Word16 width, WebRtc_Word16 height) - { - _width = width; - _height = height; - } - -private: - float _encodedBytes; - webrtc::FrameType _frameType; - WebRtc_UWord8* _payloadData; - WebRtc_UWord16 _seqNo; - bool _encodeComplete; - webrtc::RtpRtcp* _RTPModule; - WebRtc_Word16 _width; - WebRtc_Word16 _height; - webrtc::RTPVideoCodecTypes _codecType; -}; // end of VCMEncodeCompleteCallback - -class VCMDecodeCompleteCallback: public webrtc::VCMReceiveCallback -{ -public: - VCMDecodeCompleteCallback(FILE* decodedFile) : - _decodedFile(decodedFile), _decodedBytes(0) {} - virtual ~VCMDecodeCompleteCallback() {} - // will write decoded frame into file - WebRtc_Word32 FrameToRender(webrtc::VideoFrame& videoFrame); - WebRtc_Word32 DecodedBytes(); - int PSNRLastFrame(const webrtc::VideoFrame& sourceFrame, double *YPSNRptr); -private: - FILE* _decodedFile; - WebRtc_UWord32 _decodedBytes; - webrtc::VideoFrame _lastDecodedFrame; -}; // end of VCMDecodeCompleCallback class - -/// -class RTPSendCompleteCallback: public webrtc::Transport -{ -public: - // constructor input: (reeive side) rtp module to send encoded data to - RTPSendCompleteCallback(webrtc::RtpRtcp* rtp, - const char* filename = NULL); - virtual ~RTPSendCompleteCallback(); - // Send Packet to receive side RTP module - virtual int SendPacket(int channel, const void *data, int len); - // Send RTCP Packet to receive side RTP module - virtual int SendRTCPPacket(int channel, const void *data, int len); - // Set percentage of channel loss in the network - void SetLossPct(double lossPct); - // return send count - int SendCount() { return _sendCount; } -private: - // randomly decide weather to drop a packet or not, based on the channel model - bool PacketLoss(double lossPct); - - WebRtc_UWord32 _sendCount; - webrtc::RtpRtcp* _rtp; - double _lossPct; - webrtc::RtpDump* _rtpDump; -}; - -// used in multi thread test -class SendSharedState -{ -public: - SendSharedState(webrtc::VideoCodingModule& vcm, webrtc::RtpRtcp& rtp, - CmdArgs args) : - _rtp(rtp), _vcm(vcm), _args(args), _sourceFile(NULL), _frameCnt(0), - _timestamp(0) {} - - webrtc::VideoCodingModule& _vcm; - webrtc::RtpRtcp& _rtp; - CmdArgs _args; - FILE* _sourceFile; - WebRtc_Word32 _frameCnt; - WebRtc_Word32 _timestamp; -}; - -class PacketRequester: public webrtc::VCMPacketRequestCallback -{ -public: - PacketRequester(webrtc::RtpRtcp& rtp) : - _rtp(rtp) {} - WebRtc_Word32 ResendPackets(const WebRtc_UWord16* sequenceNumbers, - WebRtc_UWord16 length); - -private: - webrtc::RtpRtcp& _rtp; -}; - -// PSNR & SSIM calculations -WebRtc_Word32 -PSNRfromFiles(const WebRtc_Word8 *refFileName, - const WebRtc_Word8 *testFileName, WebRtc_Word32 width, - WebRtc_Word32 height, double *YPSNRptr); - -WebRtc_Word32 -SSIMfromFiles(const WebRtc_Word8 *refFileName, - const WebRtc_Word8 *testFileName, WebRtc_Word32 width, - WebRtc_Word32 height, double *SSIMptr); - -// codec type conversion -webrtc::RTPVideoCodecTypes -ConvertCodecType(const char* plname); - -class SendStatsTest: public webrtc::VCMSendStatisticsCallback -{ -public: - SendStatsTest() : _frameRate(15) {} - WebRtc_Word32 SendStatistics(const WebRtc_UWord32 bitRate, - const WebRtc_UWord32 frameRate); - void SetTargetFrameRate(WebRtc_UWord32 frameRate) { _frameRate = frameRate; } -private: - WebRtc_UWord32 _frameRate; -}; - -class KeyFrameReqTest: public webrtc::VCMFrameTypeCallback -{ -public: - WebRtc_Word32 FrameTypeRequest(const webrtc::FrameType frameType); -}; - -#endif diff --git a/modules/video_coding/main/test/tester_main.cc b/modules/video_coding/main/test/tester_main.cc deleted file mode 100644 index 8d8e37f57..000000000 --- a/modules/video_coding/main/test/tester_main.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "receiver_tests.h" -#include "normal_test.h" -#include "codec_database_test.h" -#include "generic_codec_test.h" -#include "../source/event.h" -#include "media_opt_test.h" -#include "quality_modes_test.h" -#include "test_util.h" - -#include -#include - -#ifdef _WIN32 -//#include "vld.h" -#endif - -using namespace webrtc; - -/* - * Build with TICK_TIME_DEBUG and EVENT_DEBUG defined - * to build the tests with simulated clock. - */ - -// TODO(holmer): How do we get debug time into the cmd line interface? -/* Debug time */ -#if defined(TICK_TIME_DEBUG) && defined(EVENT_DEBUG) -WebRtc_Word64 VCMTickTime::_timeNowDebug = 0; // current time in ms -#endif - -int ParseArguments(int argc, char **argv, CmdArgs& args) -{ - int i = 1; - while (i < argc) - { - if (argv[i][0] != '-') - { - return -1; - } - switch (argv[i][1]) - { - case 'w': - { - int w = atoi(argv[i+1]); - if (w < 1) - return -1; - args.width = w; - break; - } - case 'h': - { - int h = atoi(argv[i+1]); - if (h < 1) - return -1; - args.height = h; - break; - } - case 'b': - { - int b = atoi(argv[i+1]); - if (b < 1) - return -1; - args.bitRate = b; - break; - } - case 'f': - { - int f = atoi(argv[i+1]); - if (f < 1) - return -1; - args.frameRate = f; - break; - } - case 'c': - { - // TODO(holmer): This should be replaced with a map if more codecs - // are added - args.codecName = argv[i+1]; - if (strncmp(argv[i+1], "VP8", 3) == 0) - { - args.codecType = kVideoCodecVP8; - } - else if (strncmp(argv[i+1], "I420", 4) == 0) - { - args.codecType = kVideoCodecI420; - } - else if (strncmp(argv[i+1], "H263", 4) == 0) - { - args.codecType = kVideoCodecH263; - } - else - return -1; - - break; - } - case 'i': - { - args.inputFile = argv[i+1]; - break; - } - case 'o': - args.outputFile = argv[i+1]; - break; - case 'n': - { - int n = atoi(argv[i+1]); - if (n < 1) - return -1; - args.testNum = n; - break; - } - default: - return -1; - } - i += 2; - } - return 0; -} - -int main(int argc, char **argv) -{ - CmdArgs args; - - if (ParseArguments(argc, argv, args) != 0) - { - printf("Unable to parse input arguments\n"); - printf("args: -n -w -h -f -b -c " - " -i -o \n"); - return -1; - } - int ret = 0; - switch (args.testNum) - { - case 1: - ret = NormalTest::RunTest(args); - break; - case 2: - ret = MTRxTxTest(args); - break; - case 3: - ret = GenericCodecTest::RunTest(args); - break; - case 4: - ret = CodecDataBaseTest::RunTest(args); - break; - case 5: - // 0- normal, 1-Release test(50 runs) 2- from file - ret = MediaOptTest::RunTest(0, args); - break; - case 6: - ret = ReceiverTimingTests(args); - break; - case 7: - ret = RtpPlay(args); - break; - case 8: - ret = RtpPlayMT(args); - break; - case 9: - ret = JitterBufferTest(args); - break; - case 10: - ret = DecodeFromStorageTest(args); - break; - default: - ret = -1; - break; - } - if (ret != 0) - { - printf("Test failed!\n"); - return -1; - } - return 0; -} - - - diff --git a/modules/video_coding/main/test/video_rtp_play.cc b/modules/video_coding/main/test/video_rtp_play.cc deleted file mode 100644 index c2134fbd5..000000000 --- a/modules/video_coding/main/test/video_rtp_play.cc +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "receiver_tests.h" -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "trace.h" -#include "tick_time.h" -#include "../source/event.h" -#include "../source/internal_defines.h" -#include "test_macros.h" -#include "rtp_player.h" - -#include -#include - -using namespace webrtc; - -WebRtc_Word32 -RtpDataCallback::OnReceivedPayloadData(const WebRtc_UWord8* payloadData, - const WebRtc_UWord16 payloadSize, - const WebRtcRTPHeader* rtpHeader) -{ - return _vcm->IncomingPacket(payloadData, payloadSize, *rtpHeader); -} - -FrameReceiveCallback::~FrameReceiveCallback() -{ - if (_timingFile != NULL) - { - fclose(_timingFile); - } - if (_outFile != NULL) - { - fclose(_outFile); - } -} - -WebRtc_Word32 -FrameReceiveCallback::FrameToRender(VideoFrame& videoFrame) -{ - if (_timingFile == NULL) - { - _timingFile = fopen("renderTiming.txt", "w"); - if (_timingFile == NULL) - { - return -1; - } - } - if (_outFile == NULL) - { - _outFile = fopen(_outFilename.c_str(), "wb"); - if (_outFile == NULL) - { - return -1; - } - } - fprintf(_timingFile, "%u, %u\n", - videoFrame.TimeStamp(), - MaskWord64ToUWord32(videoFrame.RenderTimeMs())); - fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _outFile); - return 0; -} - -int RtpPlay(CmdArgs& args) -{ - // Make sure this test isn't executed without simulated clocks -#if !defined(TICK_TIME_DEBUG) || !defined(EVENT_DEBUG) - return -1; -#endif - // BEGIN Settings - - bool protectionEnabled = false; - VCMVideoProtection protectionMethod = kProtectionNack; - WebRtc_UWord32 rttMS = 10; - float lossRate = 0.0f; - bool reordering = false; - WebRtc_UWord32 renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - const WebRtc_Word64 MAX_RUNTIME_MS = -1; - std::string outFile = args.outputFile; - if (outFile == "") - outFile = "RtpPlay_decoded.yuv"; - FrameReceiveCallback receiveCallback(outFile); - VideoCodingModule* vcm = VideoCodingModule::Create(1); - RtpDataCallback dataCallback(vcm); - RTPPlayer rtpStream(args.inputFile.c_str(), &dataCallback); - - - ListWrapper payloadTypes; - payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, "VP8", kVideoCodecVP8)); - - Trace::CreateTrace(); - Trace::SetTraceFile("receiverTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - // END Settings - - // Set up - - WebRtc_Word32 ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - vcm->RegisterReceiveCallback(&receiveCallback); - vcm->RegisterPacketRequestCallback(&rtpStream); - - // Register receive codecs in VCM - ListItem* item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - VideoCodec codec; - if (VideoCodingModule::Codec(payloadType->codecType, &codec) < 0) - { - return -1; - } - codec.plType = payloadType->payloadType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } - item = payloadTypes.Next(item); - } - - if (rtpStream.Initialize(payloadTypes) < 0) - { - return -1; - } - bool nackEnabled = protectionEnabled && (protectionMethod == kProtectionNack || - protectionMethod == kProtectionDualDecoder); - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - rtpStream.SetReordering(reordering); - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protectionMethod, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - ret = 0; - - // RTP stream main loop - while ((ret = rtpStream.NextPacket(VCMTickTime::MillisecondTimestamp())) == 0) - { - if (VCMTickTime::MillisecondTimestamp() % 5 == 0) - { - ret = vcm->Decode(); - if (ret < 0) - { - return -1; - } - } - while (vcm->DecodeDualFrame(0) == 1); - if (vcm->TimeUntilNextProcess() <= 0) - { - vcm->Process(); - } - if (MAX_RUNTIME_MS > -1 && VCMTickTime::MillisecondTimestamp() >= MAX_RUNTIME_MS) - { - break; - } - VCMTickTime::IncrementDebugClock(); - } - - switch (ret) - { - case 1: - printf("Success\n"); - break; - case -1: - printf("Failed\n"); - break; - case 0: - printf("Timeout\n"); - break; - } - - rtpStream.Print(); - - // Tear down - item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - delete payloadType; - } - ListItem* itemToRemove = item; - item = payloadTypes.Next(item); - payloadTypes.Erase(itemToRemove); - } - delete vcm; - vcm = NULL; - Trace::ReturnTrace(); - return 0; -} diff --git a/modules/video_coding/main/test/video_rtp_play_mt.cc b/modules/video_coding/main/test/video_rtp_play_mt.cc deleted file mode 100644 index 7d30101fb..000000000 --- a/modules/video_coding/main/test/video_rtp_play_mt.cc +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "receiver_tests.h" -#include "video_coding.h" -#include "rtp_rtcp.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "../source/event.h" -#include "tick_time.h" -#include "test_macros.h" -#include "rtp_player.h" - -#include - -using namespace webrtc; - -bool ProcessingThread(void* obj) -{ - SharedState* state = static_cast(obj); - if (state->_vcm.TimeUntilNextProcess() <= 0) - { - if (state->_vcm.Process() < 0) - { - return false; - } - } - return true; -} - -bool RtpReaderThread(void* obj) -{ - SharedState* state = static_cast(obj); - EventWrapper& waitEvent = *EventWrapper::Create(); - // RTP stream main loop - WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); - if (state->_rtpPlayer.NextPacket(nowMs) < 0) - { - return false; - } - waitEvent.Wait(state->_rtpPlayer.TimeUntilNextPacket()); - delete &waitEvent; - return true; -} - -bool DecodeThread(void* obj) -{ - SharedState* state = static_cast(obj); - WebRtc_Word32 ret = state->_vcm.Decode(10000); - while (state->_vcm.DecodeDualFrame(0) == 1); - return true; -} - -int RtpPlayMT(CmdArgs& args, int releaseTestNo, webrtc::VideoCodecType releaseTestVideoType) -{ - // Don't run these tests with debug time -#if defined(TICK_TIME_DEBUG) || defined(EVENT_DEBUG) - return -1; -#endif - - // BEGIN Settings - - bool protectionEnabled = true; - VCMVideoProtection protection = kProtectionDualDecoder; - WebRtc_UWord8 rttMS = 50; - float lossRate = 0.05f; - WebRtc_UWord32 renderDelayMs = 0; - WebRtc_UWord32 minPlayoutDelayMs = 0; - const WebRtc_Word64 MAX_RUNTIME_MS = 10000; - std::string outFilename = args.outputFile; - if (outFilename == "") - outFilename = "RtpPlayMT_decoded.yuv"; - - bool nackEnabled = (protectionEnabled && - (protection == kProtectionDualDecoder || - protection == kProtectionNack || - kProtectionNackFEC)); - VideoCodingModule* vcm = - VideoCodingModule::Create(1); - RtpDataCallback dataCallback(vcm); - std::string rtpFilename; - rtpFilename = args.inputFile; - if (releaseTestNo > 0) - { - // Setup a release test - switch (releaseTestVideoType) - { - case webrtc::kVideoCodecVP8: - rtpFilename = args.inputFile; - outFilename = "MTReceiveTest_VP8"; - break; - default: - return -1; - } - switch (releaseTestNo) - { - case 1: - // Normal execution - protectionEnabled = false; - nackEnabled = false; - rttMS = 0; - lossRate = 0.0f; - outFilename += "_Normal.yuv"; - break; - case 2: - // Packet loss - protectionEnabled = false; - nackEnabled = false; - rttMS = 0; - lossRate = 0.05f; - outFilename += "_0.05.yuv"; - break; - case 3: - // Packet loss and NACK - protection = kProtectionNack; - nackEnabled = true; - protectionEnabled = true; - rttMS = 100; - lossRate = 0.05f; - outFilename += "_0.05_NACK_100ms.yuv"; - break; - case 4: - // Packet loss and dual decoder - // Not implemented - return 0; - break; - default: - return -1; - } - printf("Watch %s to verify that the output is reasonable\n", outFilename.c_str()); - } - RTPPlayer rtpStream(rtpFilename.c_str(), &dataCallback); - ListWrapper payloadTypes; - payloadTypes.PushFront(new PayloadCodecTuple(VCM_VP8_PAYLOAD_TYPE, - "VP8", kVideoCodecVP8)); - - Trace::CreateTrace(); - Trace::SetTraceFile("receiverTestTrace.txt"); - Trace::SetLevelFilter(webrtc::kTraceAll); - - // END Settings - - // Set up - - SharedState mtState(*vcm, rtpStream); - - if (rtpStream.Initialize(payloadTypes) < 0) - { - return -1; - } - rtpStream.SimulatePacketLoss(lossRate, nackEnabled, rttMS); - - WebRtc_Word32 ret = vcm->InitializeReceiver(); - if (ret < 0) - { - return -1; - } - - // Create and start all threads - ThreadWrapper* processingThread = ThreadWrapper::CreateThread(ProcessingThread, - &mtState, kNormalPriority, "ProcessingThread"); - ThreadWrapper* rtpReaderThread = ThreadWrapper::CreateThread(RtpReaderThread, - &mtState, kNormalPriority, "RtpReaderThread"); - ThreadWrapper* decodeThread = ThreadWrapper::CreateThread(DecodeThread, - &mtState, kNormalPriority, "DecodeThread"); - - // Register receive codecs in VCM - ListItem* item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - VideoCodec codec; - VideoCodingModule::Codec(payloadType->codecType, &codec); - codec.plType = payloadType->payloadType; - if (vcm->RegisterReceiveCodec(&codec, 1) < 0) - { - return -1; - } - } - item = payloadTypes.Next(item); - } - - if (processingThread != NULL) - { - unsigned int tid; - processingThread->Start(tid); - } - else - { - printf("Unable to start processing thread\n"); - return -1; - } - if (rtpReaderThread != NULL) - { - unsigned int tid; - rtpReaderThread->Start(tid); - } - else - { - printf("Unable to start RTP reader thread\n"); - return -1; - } - if (decodeThread != NULL) - { - unsigned int tid; - decodeThread->Start(tid); - } - else - { - printf("Unable to start decode thread\n"); - return -1; - } - - FrameReceiveCallback receiveCallback(outFilename); - vcm->RegisterReceiveCallback(&receiveCallback); - vcm->RegisterPacketRequestCallback(&rtpStream); - - vcm->SetChannelParameters(0, 0, rttMS); - vcm->SetVideoProtection(protection, protectionEnabled); - vcm->SetRenderDelay(renderDelayMs); - vcm->SetMinimumPlayoutDelay(minPlayoutDelayMs); - - EventWrapper& waitEvent = *EventWrapper::Create(); - - // Decode for 10 seconds and then tear down and exit. - waitEvent.Wait(MAX_RUNTIME_MS); - - // Tear down - item = payloadTypes.First(); - while (item != NULL) - { - PayloadCodecTuple* payloadType = static_cast(item->GetItem()); - if (payloadType != NULL) - { - delete payloadType; - } - ListItem* itemToRemove = item; - item = payloadTypes.Next(item); - payloadTypes.Erase(itemToRemove); - } - while (!processingThread->Stop()) - { - ; - } - while (!rtpReaderThread->Stop()) - { - ; - } - while (!decodeThread->Stop()) - { - ; - } - VideoCodingModule::Destroy(vcm); - vcm = NULL; - delete &waitEvent; - delete processingThread; - delete decodeThread; - delete rtpReaderThread; - rtpStream.Print(); - Trace::ReturnTrace(); - return 0; -} diff --git a/modules/video_coding/main/test/video_source.cc b/modules/video_coding/main/test/video_source.cc deleted file mode 100644 index 756e33612..000000000 --- a/modules/video_coding/main/test/video_source.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "vplib.h" -#include "video_source.h" -#include - -VideoSource::VideoSource() -: -_fileName("../../../../../codecs_video/testFiles/foreman.yuv"), -_width(352), -_height(288), -_type(webrtc::kI420), -_frameRate(30) -{ - // -} - -VideoSource::VideoSource(std::string fileName, VideoSize size, - float frameRate, webrtc::VideoType type /*= webrtc::kI420*/) -: -_fileName(fileName), -_type(type), -_frameRate(frameRate), -_width(0), -_height(0) -{ - assert(size != kUndefined && size != kNumberOfVideoSizes); - assert(type != webrtc::kUnknown); - assert(frameRate > 0); - GetWidthHeight(size); -} - -VideoSource::VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height, - float frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/) -: -_fileName(fileName), -_width(width), -_height(height), -_type(type), -_frameRate(frameRate) -{ - assert(width > 0); - assert(height > 0); - assert(type != webrtc::kUnknown); - assert(frameRate > 0); -} - -WebRtc_Word32 -VideoSource::GetFrameLength() const -{ - return webrtc::CalcBufferSize(_type, _width, _height); -} - -std::string -VideoSource::GetName() const -{ - // Remove path. - size_t slashPos = _fileName.find_last_of("/\\"); - if (slashPos == std::string::npos) - { - slashPos = 0; - } - else - { - slashPos++; - } - - // Remove extension and underscored suffix if it exists. - //return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"), - // _fileName.find_last_of(".")) - slashPos); - // MS: Removing suffix, not underscore....keeping full file name - return _fileName.substr(slashPos, _fileName.find_last_of(".") - slashPos); - -} - -int -VideoSource::GetWidthHeight( VideoSize size) -{ - switch(size) - { - case kSQCIF: - _width = 128; - _height = 96; - return 0; - case kQQVGA: - _width = 160; - _height = 120; - return 0; - case kQCIF: - _width = 176; - _height = 144; - return 0; - case kCGA: - _width = 320; - _height = 200; - return 0; - case kQVGA: - _width = 320; - _height = 240; - return 0; - case kSIF: - _width = 352; - _height = 240; - return 0; - case kWQVGA: - _width = 400; - _height = 240; - return 0; - case kCIF: - _width = 352; - _height = 288; - return 0; - case kW288p: - _width = 512; - _height = 288; - return 0; - case k448p: - _width = 576; - _height = 448; - return 0; - case kVGA: - _width = 640; - _height = 480; - return 0; - case k432p: - _width = 720; - _height = 432; - return 0; - case kW432p: - _width = 768; - _height = 432; - return 0; - case k4SIF: - _width = 704; - _height = 480; - return 0; - case kW448p: - _width = 768; - _height = 448; - return 0; - case kNTSC: - _width = 720; - _height = 480; - return 0; - case kFW448p: - _width = 800; - _height = 448; - return 0; - case kWVGA: - _width = 800; - _height = 480; - return 0; - case k4CIF: - _width = 704; - _height = 576; - return 0; - case kSVGA: - _width = 800; - _height = 600; - return 0; - case kW544p: - _width = 960; - _height = 544; - return 0; - case kW576p: - _width = 1024; - _height = 576; - return 0; - case kHD: - _width = 960; - _height = 720; - return 0; - case kXGA: - _width = 1024; - _height = 768; - return 0; - case kFullHD: - _width = 1440; - _height = 1080; - return 0; - case kWHD: - _width = 1280; - _height = 720; - return 0; - case kWFullHD: - _width = 1920; - _height = 1080; - return 0; - default: - return -1; - } -} diff --git a/modules/video_coding/main/test/video_source.h b/modules/video_coding/main/test/video_source.h deleted file mode 100644 index 0f7d5e88b..000000000 --- a/modules/video_coding/main/test/video_source.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_ -#define WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_ - -#include "vplib.h" -#include "typedefs.h" - -#include - -enum VideoSize - { - kUndefined, - kSQCIF, // 128*96 = 12 288 - kQQVGA, // 160*120 = 19 200 - kQCIF, // 176*144 = 25 344 - kCGA, // 320*200 = 64 000 - kQVGA, // 320*240 = 76 800 - kSIF, // 352*240 = 84 480 - kWQVGA, // 400*240 = 96 000 - kCIF, // 352*288 = 101 376 - kW288p, // 512*288 = 147 456 (WCIF) - k448p, // 576*448 = 281 088 - kVGA, // 640*480 = 307 200 - k432p, // 720*432 = 311 040 - kW432p, // 768*432 = 331 776 - k4SIF, // 704*480 = 337 920 - kW448p, // 768*448 = 344 064 - kNTSC, // 720*480 = 345 600 - kFW448p, // 800*448 = 358 400 - kWVGA, // 800*480 = 384 000 - k4CIF, // 704*576 = 405 504 - kSVGA, // 800*600 = 480 000 - kW544p, // 960*544 = 522 240 - kW576p, // 1024*576 = 589 824 (W4CIF) - kHD, // 960*720 = 691 200 - kXGA, // 1024*768 = 786 432 - kWHD, // 1280*720 = 921 600 - kFullHD, // 1440*1080 = 1 555 200 - kWFullHD, // 1920*1080 = 2 073 600 - - kNumberOfVideoSizes - }; - - -class VideoSource -{ -public: - VideoSource(); - VideoSource(std::string fileName, VideoSize size, float frameRate, webrtc::VideoType type = webrtc::kI420); - VideoSource(std::string fileName, WebRtc_UWord16 width, WebRtc_UWord16 height, - float frameRate = 30, webrtc::VideoType type = webrtc::kI420); - - std::string GetFileName() const { return _fileName; } - WebRtc_UWord16 GetWidth() const { return _width; } - WebRtc_UWord16 GetHeight() const { return _height; } - webrtc::VideoType GetType() const { return _type; } - float GetFrameRate() const { return _frameRate; } - int GetWidthHeight( VideoSize size); - - // Returns the filename with the path (including the leading slash) removed. - std::string GetName() const; - - WebRtc_Word32 GetFrameLength() const; - -private: - std::string _fileName; - WebRtc_UWord16 _width; - WebRtc_UWord16 _height; - webrtc::VideoType _type; - float _frameRate; -}; - -#endif // WEBRTC_MODULES_VIDEO_CODING_TEST_VIDEO_SOURCE_H_ - diff --git a/modules/video_processing/main/OWNERS b/modules/video_processing/main/OWNERS deleted file mode 100644 index 30eee35f1..000000000 --- a/modules/video_processing/main/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -holmer@google.com -mikhal@google.com -marpan@google.com -hlundin@google.com diff --git a/modules/video_processing/main/interface/video_processing.h b/modules/video_processing/main/interface/video_processing.h deleted file mode 100644 index 580758416..000000000 --- a/modules/video_processing/main/interface/video_processing.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_processing.h - * This header file contains the API required for the video - * processing module class. - */ - - -#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H -#define WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_H - -#include "module.h" -#include "module_common_types.h" -#include "video_processing_defines.h" - -/** - The module is largely intended to process video streams, except functionality - provided by static functions which operate independent of previous frames. It - is recommended, but not required that a unique instance be used for each - concurrently processed stream. Similarly, it is recommended to call Reset() - before switching to a new stream, but this is not absolutely required. - - The module provides basic thread safety by permitting only a single function to - execute concurrently. -*/ - -namespace webrtc { - -class VideoProcessingModule : public Module -{ -public: - /** - Structure to hold frame statistics. Populate it with GetFrameStats(). - */ - struct FrameStats - { - FrameStats() : - mean(0), - sum(0), - numPixels(0), - subSamplWidth(0), - subSamplHeight(0) - { - memset(hist, 0, sizeof(hist)); - } - - WebRtc_UWord32 hist[256]; /**< Histogram of frame */ - WebRtc_UWord32 mean; /**< Mean value of frame */ - WebRtc_UWord32 sum; /**< Sum of frame */ - WebRtc_UWord32 numPixels; /**< Number of pixels */ - WebRtc_UWord8 subSamplWidth; /**< Subsampling rate of width in powers of 2 */ - WebRtc_UWord8 subSamplHeight; /**< Subsampling rate of height in powers of 2 */ - }; - - /** - Specifies the warning types returned by BrightnessDetection(). - */ - enum BrightnessWarning - { - kNoWarning, /**< Frame has acceptable brightness */ - kDarkWarning, /**< Frame is too dark */ - kBrightWarning /**< Frame is too bright */ - }; - - /* - Creates a VPM object. - - \param[in] id - Unique identifier of this object. - - \return Pointer to a VPM object. - */ - static VideoProcessingModule* Create(WebRtc_Word32 id); - - /** - Destroys a VPM object. - - \param[in] module - Pointer to the VPM object to destroy. - */ - static void Destroy(VideoProcessingModule* module); - - /** - Retrieves the version of the module and its components. - - \param[in,out] version - Buffer to which the null-terminated version string will be copied. - - \param[in,out] remainingBufferInBytes - Bytes remaining between the supplied position and the end of the buffer. - This will reflect the new remaining size at return. - - \param[in,out] position - Position in bytes within the buffer to place the version string. This - will reflect the first byte position following the version string at - return. - - \return 0 on success, -1 on failure. - */ - static WebRtc_Word32 GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position); - - /** - Not supported. - */ - virtual WebRtc_Word32 TimeUntilNextProcess() { return -1; } - - /** - Not supported. - */ - virtual WebRtc_Word32 Process() { return -1; } - - /** - Resets all processing components to their initial states. This should be - called whenever a new video stream is started. - */ - virtual void Reset() = 0; - - /** - Retrieves statistics for the input frame. This function must be used to - prepare a FrameStats struct for use in certain VPM functions. - - \param[out] stats - The frame statistics will be stored here on return. - - \param[in] frame - Pointer to the video frame. - - \param[in] width - Frame width in pixels. - - \param[in] height - Frame height in pixels. - - \return 0 on success, -1 on failure. - */ - static WebRtc_Word32 GetFrameStats(FrameStats& stats, - const WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height); - - /** - \overload - */ - static WebRtc_Word32 GetFrameStats(FrameStats& stats, - const VideoFrame& frame); - - /** - Checks the validity of a FrameStats struct. Currently, valid implies only - that is had changed from its initialized state. - - \param[in] stats - Frame statistics. - - \return True on valid stats, false on invalid stats. - */ - static bool ValidFrameStats(const FrameStats& stats); - - /** - Returns a FrameStats struct to its intialized state. - - \param[in,out] stats - Frame statistics. - */ - static void ClearFrameStats(FrameStats& stats); - - /** - Enhances the color of an image through a constant mapping. Only the - chrominance is altered. Has a fixed-point implementation. - - \param[in,out] frame - Pointer to the video frame. - - \param[in] width - Frame width in pixels. - - \param[in] height - Frame height in pixels. - - \return 0 on success, -1 on failure. - */ - static WebRtc_Word32 ColorEnhancement(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height); - - /** - \overload - */ - static WebRtc_Word32 ColorEnhancement(VideoFrame& frame); - - /** - Detects and removes camera flicker from a video stream. Every frame from the - stream must be passed in. A frame will only be altered if flicker has been - detected. Has a fixed-point implementation. - - \param[in,out] frame - Pointer to the video frame. - - \param[in] width - Frame width in pixels. - - \param[in] height - Frame height in pixels. - - \param[in] timestamp - Frame timestamp in 90 kHz format. - - \param[in,out] stats - Frame statistics provided by GetFrameStats(). On return the stats will - be reset to zero if the frame was altered. Call GetFrameStats() again - if the statistics for the altered frame are required. - - \return 0 on success, -1 on failure. - */ - virtual WebRtc_Word32 Deflickering(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - WebRtc_UWord32 timestamp, - FrameStats& stats) = 0; - - /** - \overload - */ - virtual WebRtc_Word32 Deflickering(VideoFrame& frame, - FrameStats& stats) = 0; - - /** - Denoises a video frame. Every frame from the stream should be passed in. - Has a fixed-point implementation. - - \param[in,out] frame - Pointer to the video frame. - - \param[in] width - Frame width in pixels. - - \param[in] height - Frame height in pixels. - - \return The number of modified pixels on success, -1 on failure. - */ - virtual WebRtc_Word32 Denoising(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height) = 0; - - /** - \overload - */ - virtual WebRtc_Word32 Denoising(VideoFrame& frame) = 0; - - /** - Detects if a video frame is excessively bright or dark. Returns a warning if - this is the case. Multiple frames should be passed in before expecting a - warning. Has a floating-point implementation. - - \param[in] frame - Pointer to the video frame. - - \param[in] width - Frame width in pixels. - - \param[in] height - Frame height in pixels. - - \param[in] stats - Frame statistics provided by GetFrameStats(). - - \return A member of BrightnessWarning on success, -1 on error - */ - virtual WebRtc_Word32 BrightnessDetection(const WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - const FrameStats& stats) = 0; - - /** - \overload - */ - virtual WebRtc_Word32 BrightnessDetection(const VideoFrame& frame, - const FrameStats& stats) = 0; - - - /** - The following functions refer to the pre-processor unit within VPM. The pre-processor - perfoms spatial/temporal decimation and content analysis on the frames prior to encoding. - */ - - /** - Enable/disable temporal decimation - - \param[in] enable when true, temporal decimation is enabled - */ - virtual void EnableTemporalDecimation(bool enable) = 0; - - /** - Set target resolution - - \param[in] width - Target width - - \param[in] height - Target height - - \param[in] frameRate - Target frameRate - - \return VPM_OK on success, a negative value on error (see error codes) - - */ - virtual WebRtc_Word32 SetTargetResolution(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32 frameRate) = 0; - - /** - Set max frame rate - \param[in] maxFrameRate: maximum frame rate (limited to native frame rate) - - \return VPM_OK on success, a negative value on error (see error codes) - */ - virtual WebRtc_Word32 SetMaxFrameRate(WebRtc_UWord32 maxFrameRate) = 0; - - /** - Get decimated(target) frame rate - */ - virtual WebRtc_UWord32 DecimatedFrameRate() = 0; - - /** - Get decimated(target) frame width - */ - virtual WebRtc_UWord32 DecimatedWidth() const = 0; - - /** - Get decimated(target) frame height - */ - virtual WebRtc_UWord32 DecimatedHeight() const = 0 ; - - /** - Set the spatial resampling settings of the VPM: The resampler may either be disabled or one of the following: - scaling to a close to target dimension followed by crop/pad - - \param[in] resamplingMode - Set resampling mode (a member of VideoFrameResampling) - */ - virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode) = 0; - - /** - Get Processed (decimated) frame - - \param[in] frame pointer to the video frame. - - \param[in] processedFrame pointer (double) to the processed frame - - \return VPM_OK on success, a negative value on error (see error codes) - */ - virtual WebRtc_Word32 PreprocessFrame(const VideoFrame* frame, VideoFrame** processedFrame) = 0; - - /** - Return content metrics for the last processed frame - */ - virtual VideoContentMetrics* ContentMetrics() const = 0 ; - - /** - Enable content analysis - */ - virtual void EnableContentAnalysis(bool enable) = 0; - -}; - -} //namespace - -#endif - diff --git a/modules/video_processing/main/interface/video_processing_defines.h b/modules/video_processing/main/interface/video_processing_defines.h deleted file mode 100644 index a3d98b304..000000000 --- a/modules/video_processing/main/interface/video_processing_defines.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_processing_defines.h - * This header file includes the definitions used in the video processor module - */ - -#ifndef WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H -#define WEBRTC_MODULES_INTERFACE_VIDEO_PROCESSING_DEFINES_H - -#include "typedefs.h" - -namespace webrtc { - -// Error codes -#define VPM_OK 0 -#define VPM_GENERAL_ERROR -1 -#define VPM_MEMORY -2 -#define VPM_PARAMETER_ERROR -3 -#define VPM_UNINITIALIZED -4 -#define VPM_UNIMPLEMENTED -5 - -enum VideoFrameResampling -{ - kNoRescaling, // disables rescaling - kFastRescaling, // fast up/down scaling; crop/pad when needed. - kBiLinear, // bi-linear interpolation -}; - -} //namespace - -#endif diff --git a/modules/video_processing/main/source/Android.mk b/modules/video_processing/main/source/Android.mk deleted file mode 100644 index 07779d2c6..000000000 --- a/modules/video_processing/main/source/Android.mk +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_processing -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := video_processing_impl.cc \ - brightness_detection.cc \ - color_enhancement.cc \ - content_analysis.cc \ - deflickering.cc \ - denoising.cc \ - frame_preprocessor.cc \ - spatial_resampler.cc \ - video_decimator.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../../common_audio/signal_processing_library/main/interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../audio_coding/main/interface - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_processing/main/source/brightness_detection.cc b/modules/video_processing/main/source/brightness_detection.cc deleted file mode 100644 index 6840df28b..000000000 --- a/modules/video_processing/main/source/brightness_detection.cc +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_processing.h" -#include "brightness_detection.h" -#include "trace.h" - -#include - -namespace webrtc { - -VPMBrightnessDetection::VPMBrightnessDetection() : - _id(0) -{ - Reset(); -} - -VPMBrightnessDetection::~VPMBrightnessDetection() -{ -} - -WebRtc_Word32 -VPMBrightnessDetection::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return VPM_OK; -} - -void -VPMBrightnessDetection::Reset() -{ - _frameCntBright = 0; - _frameCntDark = 0; -} - -WebRtc_Word32 -VPMBrightnessDetection::ProcessFrame(const WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height, - const VideoProcessingModule::FrameStats& stats) -{ - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Null frame pointer"); - return VPM_PARAMETER_ERROR; - } - - if (width == 0 || height == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame size"); - return VPM_PARAMETER_ERROR; - } - - if (!VideoProcessingModule::ValidFrameStats(stats)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame stats"); - return VPM_PARAMETER_ERROR; - } - - const WebRtc_UWord8 frameCntAlarm = 2; - - // Get proportion in lowest bins - WebRtc_UWord8 lowTh = 20; - float propLow = 0; - for (WebRtc_UWord32 i = 0; i < lowTh; i++) - { - propLow += stats.hist[i]; - } - propLow /= stats.numPixels; - - // Get proportion in highest bins - unsigned char highTh = 230; - float propHigh = 0; - for (WebRtc_UWord32 i = highTh; i < 256; i++) - { - propHigh += stats.hist[i]; - } - propHigh /= stats.numPixels; - - if(propHigh < 0.4) - { - if (stats.mean < 90 || stats.mean > 170) - { - // Standard deviation of Y - float stdY = 0; - for (WebRtc_UWord32 h = 0; h < height; h += (1 << stats.subSamplHeight)) - { - WebRtc_UWord32 row = h*width; - for (WebRtc_UWord32 w = 0; w < width; w += (1 << stats.subSamplWidth)) - { - stdY += (frame[w + row] - stats.mean) * (frame[w + row] - stats.mean); - } - } - stdY = sqrt(stdY / stats.numPixels); - - // Get percentiles - WebRtc_UWord32 sum = 0; - WebRtc_UWord32 medianY = 140; - WebRtc_UWord32 perc05 = 0; - WebRtc_UWord32 perc95 = 255; - float posPerc05 = stats.numPixels * 0.05f; - float posMedian = stats.numPixels * 0.5f; - float posPerc95 = stats.numPixels * 0.95f; - for (WebRtc_UWord32 i = 0; i < 256; i++) - { - sum += stats.hist[i]; - - if (sum < posPerc05) - { - perc05 = i; // 5th perc - } - if (sum < posMedian) - { - medianY = i; // 50th perc - } - if (sum < posPerc95) - { - perc95 = i; // 95th perc - } - else - { - break; - } - } - - // Check if image is too dark - if ((stdY < 55) && (perc05 < 50)) - { - if (medianY < 60 || stats.mean < 80 || perc95 < 130 || propLow > 0.20) - { - _frameCntDark++; - } - else - { - _frameCntDark = 0; - } - } - else - { - _frameCntDark = 0; - } - - // Check if image is too bright - if ((stdY < 52) && (perc95 > 200) && (medianY > 160)) - { - if (medianY > 185 || stats.mean > 185 || perc05 > 140 || propHigh > 0.25) - { - _frameCntBright++; - } - else - { - _frameCntBright = 0; - } - } - else - { - _frameCntBright = 0; - } - - } - else - { - _frameCntDark = 0; - _frameCntBright = 0; - } - - } - else - { - _frameCntBright++; - _frameCntDark = 0; - } - - if (_frameCntDark > frameCntAlarm) - { - return VideoProcessingModule::kDarkWarning; - } - else if (_frameCntBright > frameCntAlarm) - { - return VideoProcessingModule::kBrightWarning; - } - else - { - return VideoProcessingModule::kNoWarning; - } -} - -} //namespace diff --git a/modules/video_processing/main/source/brightness_detection.h b/modules/video_processing/main/source/brightness_detection.h deleted file mode 100644 index 7bed556b4..000000000 --- a/modules/video_processing/main/source/brightness_detection.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * brightness_detection.h - */ -#ifndef VPM_BRIGHTNESS_DETECTION_H -#define VPM_BRIGHTNESS_DETECTION_H - -#include "typedefs.h" -#include "video_processing.h" - -namespace webrtc { - -class VPMBrightnessDetection -{ -public: - VPMBrightnessDetection(); - ~VPMBrightnessDetection(); - - WebRtc_Word32 ChangeUniqueId(WebRtc_Word32 id); - - void Reset(); - - WebRtc_Word32 ProcessFrame(const WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - const VideoProcessingModule::FrameStats& stats); - -private: - WebRtc_Word32 _id; - - WebRtc_UWord32 _frameCntBright; - WebRtc_UWord32 _frameCntDark; -}; - -} //namespace - -#endif // VPM_BRIGHTNESS_DETECTION_H diff --git a/modules/video_processing/main/source/color_enhancement.cc b/modules/video_processing/main/source/color_enhancement.cc deleted file mode 100644 index 426596fcc..000000000 --- a/modules/video_processing/main/source/color_enhancement.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "color_enhancement.h" -#include "color_enhancement_private.h" -#include "trace.h" -#include // NULL - -namespace webrtc { - -namespace VideoProcessing -{ - WebRtc_Word32 - ColorEnhancement(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) - { - // pointers to U and V color pixels - WebRtc_UWord8* ptrU; - WebRtc_UWord8* ptrV; - WebRtc_UWord8 tempChroma; - const WebRtc_UWord32 numPixels = width * height; - - - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, "Null frame pointer"); - return VPM_GENERAL_ERROR; - } - - if (width == 0 || height == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, "Invalid frame size"); - return VPM_GENERAL_ERROR; - } - - // set pointers to first U and V pixels - - // stream format: - // | numPixels bytes luminance | numPixels/4 bytes chroma U | numPixels/4 chroma V | - - ptrU = frame + numPixels; // skip luminance - ptrV = ptrU + (numPixels>>2); - - // loop through all chrominance pixels and modify color - for (WebRtc_UWord32 ix = 0; ix < (numPixels>>2); ix++) - { - tempChroma = colorTable[*ptrU][*ptrV]; - *ptrV = colorTable[*ptrV][*ptrU]; - *ptrU = tempChroma; - - // increment pointers - ptrU++; - ptrV++; - } - return VPM_OK; - } - -} //namespace - -} //namespace webrtc diff --git a/modules/video_processing/main/source/color_enhancement.h b/modules/video_processing/main/source/color_enhancement.h deleted file mode 100644 index 87fabc347..000000000 --- a/modules/video_processing/main/source/color_enhancement.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * color_enhancement.h - */ -#ifndef VPM_COLOR_ENHANCEMENT_H -#define VPM_COLOR_ENHANCEMENT_H - -#include "typedefs.h" -#include "video_processing.h" - -namespace webrtc { - -namespace VideoProcessing -{ - WebRtc_Word32 ColorEnhancement(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height); -} - -} //namespace - -#endif // VPM_COLOR_ENHANCEMENT_H diff --git a/modules/video_processing/main/source/color_enhancement_private.h b/modules/video_processing/main/source/color_enhancement_private.h deleted file mode 100644 index b88fc1a9f..000000000 --- a/modules/video_processing/main/source/color_enhancement_private.h +++ /dev/null @@ -1,273 +0,0 @@ -#ifndef VPM_COLOR_ENHANCEMENT_PRIVATE_H -#define VPM_COLOR_ENHANCEMENT_PRIVATE_H - -#include "typedefs.h" - -namespace webrtc { - -//Table created with Matlab script createTable.m -//Usage: -// Umod=colorTable[U][V] -// Vmod=colorTable[V][U] -static const WebRtc_UWord8 colorTable[256][256] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, - {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, - {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, - {6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}, - {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}, - {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, - {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, - {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, - {11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, - {12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, - {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, - {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, - {15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, - {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, - {17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}, - {18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18}, - {19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19}, - {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}, - {21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21}, - {22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22}, - {23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23}, - {24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24}, - {25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25}, - {26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26}, - {27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27}, - {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, - {29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}, - {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, - {31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31}, - {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}, - {33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33}, - {34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34}, - {35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35}, - {36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36}, - {37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37}, - {38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38}, - {39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39}, - {40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, - {41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41}, - {42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42}, - {43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43}, - {44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44}, - {45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45}, - {46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46}, - {47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47}, - {48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48}, - {49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49}, - {50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50}, - {51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51}, - {52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52}, - {53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53}, - {54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54}, - {55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55}, - {56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56}, - {57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57}, - {58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58}, - {59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, - {60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60}, - {61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61}, - {62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62}, - {63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63}, - {64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}, - {65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65}, - {66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66}, - {67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67}, - {68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68}, - {69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69}, - {70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70}, - {71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71}, - {72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72}, - {73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73}, - {74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74}, - {75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75}, - {76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76}, - {77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77}, - {78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 74, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78}, - {79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, - {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80}, - {81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 76, 76, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81}, - {82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 77, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82}, - {83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83}, - {84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84}, - {85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85}, - {86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 80, 80, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86}, - {87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 81, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87}, - {88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88}, - {89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 81, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89}, - {90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90}, - {91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91}, - {92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92}, - {93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 86, 86, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93}, - {94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94}, - {95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95}, - {96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96}, - {97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97}, - {98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98}, - {99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, - {100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, - {101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101}, - {102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102}, - {103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103}, - {104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104}, - {105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105}, - {106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106}, - {107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107}, - {108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108}, - {109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109}, - {110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110}, - {111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111}, - {112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112}, - {113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113}, - {114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114}, - {115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115}, - {116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116}, - {117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117}, - {118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118}, - {119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 119, 119, 119, 119, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119}, - {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120}, - {121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 121, 121, 122, 122, 122, 122, 123, 123, 123, 122, 122, 122, 122, 121, 121, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121}, - {122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, 123, 123, 123, 124, 124, 124, 124, 124, 123, 123, 123, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122}, - {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123}, - {124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124}, - {125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125}, - {126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126}, - {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}, - {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, - {129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 129, 129, 129, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129}, - {130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 128, 128, 128, 129, 129, 129, 129, 129, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130}, - {131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 130, 130, 130, 130, 129, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131}, - {132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 131, 131, 131, 130, 130, 130, 130, 130, 131, 131, 131, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132}, - {133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 133, 133, 132, 132, 132, 132, 131, 131, 131, 132, 132, 132, 132, 133, 133, 134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133}, - {134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134}, - {135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 135, 135, 135, 135, 136, 136, 136, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135}, - {136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136}, - {137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137}, - {138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138}, - {139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 139}, - {140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140}, - {141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, 141}, - {142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142}, - {143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143}, - {144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144}, - {145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145}, - {146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146}, - {147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147}, - {148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148}, - {149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149}, - {150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150}, - {151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151}, - {152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152}, - {153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, 153}, - {154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154}, - {155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155}, - {156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 157, 157, 157, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156, 156}, - {157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, 157}, - {158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, 158}, - {159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159}, - {160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 161, 161, 161, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160}, - {161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 162, 162, 162, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161}, - {162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 163, 163, 163, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, 162}, - {163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 164, 164, 164, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163, 163}, - {164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 165, 165, 165, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164}, - {165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 166, 166, 166, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165}, - {166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166}, - {167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167}, - {168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 169, 169, 169, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168}, - {169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 170, 170, 170, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169}, - {170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 171, 171, 171, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170}, - {171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171}, - {172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172}, - {173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173}, - {174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174}, - {175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175}, - {176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176}, - {177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177}, - {178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178}, - {179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179}, - {180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180}, - {181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181}, - {182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182}, - {183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183}, - {184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184}, - {185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185}, - {186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186}, - {187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187}, - {188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188}, - {189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189}, - {190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190}, - {191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191}, - {192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192}, - {193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193}, - {194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194}, - {195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195}, - {196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196}, - {197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197}, - {198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198}, - {199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199}, - {200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200}, - {201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201}, - {202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202}, - {203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203}, - {204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204}, - {205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205}, - {206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206}, - {207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207}, - {208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208}, - {209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209}, - {210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210}, - {211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211}, - {212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212}, - {213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213}, - {214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214}, - {215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215}, - {216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216}, - {217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217}, - {218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218}, - {219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219}, - {220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220}, - {221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221, 221}, - {222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222, 222}, - {223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223}, - {224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224}, - {225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225}, - {226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226}, - {227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227}, - {228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228}, - {229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229}, - {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230}, - {231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231}, - {232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232}, - {233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233, 233}, - {234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234}, - {235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235}, - {236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236}, - {237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237, 237}, - {238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238}, - {239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239}, - {240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240}, - {241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241}, - {242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242}, - {243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243}, - {244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244}, - {245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245}, - {246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246}, - {247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247}, - {248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248}, - {249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249}, - {250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250}, - {251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251}, - {252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252}, - {253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253}, - {254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254}, - {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} -}; - -} //namespace - -#endif // VPM_COLOR_ENHANCEMENT_PRIVATE_H diff --git a/modules/video_processing/main/source/content_analysis.cc b/modules/video_processing/main/source/content_analysis.cc deleted file mode 100644 index 8e8e840ee..000000000 --- a/modules/video_processing/main/source/content_analysis.cc +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "content_analysis.h" -#include "tick_util.h" - -#include -#include - -namespace webrtc { - -VPMContentAnalysis::VPMContentAnalysis(): -_origFrame(NULL), -_prevFrame(NULL), -_firstFrame(true), -_width(0), -_height(0), -_motionMagnitudeNZ(0.0f), -_spatialPredErr(0.0f), -_spatialPredErrH(0.0f), -_spatialPredErrV(0.0f), -_sizeZeroMotion(0.0f), -_motionPredErr(0.0f), -_motionHorizontalness(0.0f), -_motionClusterDistortion(0.0f), -_CAInit(false), -_cMetrics(NULL) -{ - Release(); -} - -VPMContentAnalysis::~VPMContentAnalysis() -{ - Release(); -} - - - -VideoContentMetrics* -VPMContentAnalysis::ComputeContentMetrics(const VideoFrame* inputFrame) -{ - if (inputFrame == NULL) - { - return NULL; - } - - //Init if needed (native dimension change) - if (_width != inputFrame->Width() || _height != inputFrame->Height()) - { - Initialize((WebRtc_UWord16)inputFrame->Width(), (WebRtc_UWord16)inputFrame->Height()); - } - - _origFrame = inputFrame->Buffer(); - - //compute spatial metrics: 3 spatial prediction errors - ComputeSpatialMetrics(); - - //compute motion metrics - if (_firstFrame == false) - ComputeMotionMetrics(); - - // saving current frame as previous one: Y only - memcpy(_prevFrame, _origFrame, _width * _height); - - _firstFrame = false; - _CAInit = true; - - return ContentMetrics(); -} - -WebRtc_Word32 -VPMContentAnalysis::Release() -{ - if (_cMetrics != NULL) - { - delete _cMetrics; - _cMetrics = NULL; - } - - if (_prevFrame != NULL) - { - delete [] _prevFrame; - _prevFrame = NULL; - } - - _width = 0; - _height = 0; - _firstFrame = true; - - return VPM_OK; -} - -WebRtc_Word32 -VPMContentAnalysis::Initialize(WebRtc_UWord16 width, WebRtc_UWord16 height) -{ - _width = width; - _height = height; - _firstFrame = true; - - if (_cMetrics != NULL) - { - delete _cMetrics; - } - _cMetrics = new VideoContentMetrics(); - if (_cMetrics == NULL) - { - return VPM_MEMORY; - } - - if (_prevFrame != NULL) - { - delete [] _prevFrame; - } - _prevFrame = new WebRtc_UWord8[_width * _height] ; // Y only - if (_prevFrame == NULL) - { - return VPM_MEMORY; - } - - return VPM_OK; -} - - -//Compute motion metrics: magnitude over non-zero motion vectors, and size of zero cluster -WebRtc_Word32 -VPMContentAnalysis::ComputeMotionMetrics() -{ - - //Motion metrics: only one is derived from normalized (MAD) temporal difference - TemporalDiffMetric(); - - return VPM_OK; -} - - -//Normalized temporal difference (MAD): used as a motion level metric -//Normalize MAD by spatial contrast: images with more contrast (pixel variance) likely have larger temporal difference -//To reduce complexity, we compute the metric for a reduced set of points. -WebRtc_Word32 -VPMContentAnalysis::TemporalDiffMetric() -{ - - //size of original frame - WebRtc_UWord16 sizei = _height; - WebRtc_UWord16 sizej = _width; - - //skip parameter: # of skipped pixels along x & y direction: for complexity reduction - WebRtc_UWord8 skipNum = 1; // 1 == all pixels, 2 == 1/4 reduction, 3 == 1/9 reduction - - //use skipNum = 2 for 4CIF, WHD - if ( (sizei >= 576) && (sizej >= 704) ) - { - skipNum = 2; - } - //use skipNum = 3 for FULLL_HD images - if ( (sizei >= 1080) && (sizej >= 1920) ) - { - skipNum = 3; - } - - float contrast = 0.0f; - float tempDiffAvg = 0.0f; - float pixelSumAvg = 0.0f; - float pixelSqSumAvg = 0.0f; - - WebRtc_UWord32 tempDiffSum = 0; - WebRtc_UWord32 pixelSum = 0; - WebRtc_UWord32 pixelSqSum = 0; - - WebRtc_UWord8 bord = 8; //avoid boundary - WebRtc_UWord32 numPixels = 0; //counter for # of pixels - WebRtc_UWord32 ssn; - - for(WebRtc_UWord16 i = bord; i < sizei - bord; i += skipNum) - for(WebRtc_UWord16 j = bord; j < sizej - bord; j += skipNum) - { - numPixels += 1; - ssn = i * sizej + j; - - WebRtc_UWord8 currPixel = _origFrame[ssn]; - WebRtc_UWord8 prevPixel = _prevFrame[ssn]; - - tempDiffSum += (WebRtc_UWord32) abs((WebRtc_Word16)(currPixel - prevPixel)); - pixelSum += (WebRtc_UWord32) _origFrame[ssn]; - pixelSqSum += (WebRtc_UWord32) (_origFrame[ssn] * _origFrame[ssn]); - } - - //default - _motionMagnitudeNZ = 0.0f; - - if (tempDiffSum == 0) - { - return VPM_OK; - } - - //normalize over all pixels - tempDiffAvg = (float)tempDiffSum / (float)(numPixels); - pixelSumAvg = (float)pixelSum / (float)(numPixels); - pixelSqSumAvg = (float)pixelSqSum / (float)(numPixels); - contrast = pixelSqSumAvg - (pixelSumAvg * pixelSumAvg); - - if (contrast > 0.0) - { - contrast = sqrt(contrast); - _motionMagnitudeNZ = tempDiffAvg/contrast; - } - - return VPM_OK; - -} - - -//Compute spatial metrics: -//To reduce complexity, we compute the metric for a reduced set of points. -//The spatial metrics are rough estimates of the prediction error cost for each QM spatial mode: 2x2,1x2,2x1 -//The metrics are a simple estimate of the up-sampling prediction error, estimated assuming sub-sampling for decimation (no filtering), -//and up-sampling back up with simple bilinear interpolation. -WebRtc_Word32 -VPMContentAnalysis::ComputeSpatialMetrics() -{ - //size of original frame - WebRtc_UWord16 sizei = _height; - WebRtc_UWord16 sizej = _width; - - //skip parameter: # of skipped pixels along x & y direction: for complexity reduction - WebRtc_UWord8 skipNum = 1; // 1 == all pixels, 2 == 1/4 reduction, 3 == 1/9 reduction - - //use skipNum = 2 for 4CIF, WHD - if ( (sizei >= 576) && (sizej >= 704) ) - { - skipNum = 2; - } - //use skipNum = 3 for FULLL_HD images - if ( (sizei >= 1080) && (sizej >= 1920) ) - { - skipNum = 3; - } - - float spatialErr = 0.0f; - float spatialErrH = 0.0f; - float spatialErrV = 0.0f; - - //pixel mean square average: used to normalize the spatial metrics - float pixelMSA = 0; - float norm = 1.0f; - - WebRtc_UWord8 bord = 8; //avoid boundary - WebRtc_UWord32 numPixels = 0; //counter for # of pixels - - WebRtc_UWord32 ssn1,ssn2,ssn3,ssn4,ssn5; - - WebRtc_UWord32 spatialErrSum = 0; - WebRtc_UWord32 spatialErrVSum = 0; - WebRtc_UWord32 spatialErrHSum = 0; - - for(WebRtc_UWord16 i = bord; i < sizei - bord; i += skipNum) - for(WebRtc_UWord16 j = bord; j < sizej - bord; j += skipNum) - { - numPixels += 1; - ssn1= i * sizej + j; - ssn2 = (i + 1) * sizej + j; //bottom - ssn3 = (i - 1) * sizej + j; //top - ssn4 = i * sizej + j + 1; //right - ssn5 = i * sizej + j - 1; //left - - WebRtc_UWord16 refPixel1 = _origFrame[ssn1] << 1; - WebRtc_UWord16 refPixel2 = _origFrame[ssn1] << 2; - - WebRtc_UWord8 bottPixel = _origFrame[ssn2]; - WebRtc_UWord8 topPixel = _origFrame[ssn3]; - WebRtc_UWord8 rightPixel = _origFrame[ssn4]; - WebRtc_UWord8 leftPixel = _origFrame[ssn5]; - - spatialErrSum += (WebRtc_UWord32) abs((WebRtc_Word16)(refPixel2 - (WebRtc_UWord16)(bottPixel + topPixel + leftPixel + rightPixel))); - spatialErrVSum += (WebRtc_UWord32) abs((WebRtc_Word16)(refPixel1 - (WebRtc_UWord16)(bottPixel + topPixel))); - spatialErrHSum += (WebRtc_UWord32) abs((WebRtc_Word16)(refPixel1 - (WebRtc_UWord16)(leftPixel + rightPixel))); - - pixelMSA += (float)_origFrame[ssn1]; - } - - //normalize over all pixels - spatialErr = (float)spatialErrSum / (float)(4 * numPixels); - spatialErrH = (float)spatialErrHSum / (float)(2 * numPixels); - spatialErrV = (float)spatialErrVSum / (float)(2 * numPixels); - norm = (float)pixelMSA / float(numPixels); - - - //normalize to RMS pixel level: use avg pixel level for now - - //2X2: - _spatialPredErr = spatialErr / (norm); - - //1X2: - _spatialPredErrH = spatialErrH / (norm); - - //2X1: - _spatialPredErrV = spatialErrV / (norm); - - - return VPM_OK; -} - - -VideoContentMetrics* -VPMContentAnalysis::ContentMetrics() -{ - if (_CAInit == false) - { - return NULL; - } - - - _cMetrics->spatialPredErr = _spatialPredErr; - _cMetrics->spatialPredErrH = _spatialPredErrH; - _cMetrics->spatialPredErrV = _spatialPredErrV; - //normalized temporal difference (MAD) - _cMetrics->motionMagnitudeNZ = _motionMagnitudeNZ; - - //Set to zero: not computed - _cMetrics->motionPredErr = _motionPredErr; - _cMetrics->sizeZeroMotion = _sizeZeroMotion; - _cMetrics->motionHorizontalness = _motionHorizontalness; - _cMetrics->motionClusterDistortion = _motionClusterDistortion; - - return _cMetrics; - -} - -} //namespace diff --git a/modules/video_processing/main/source/content_analysis.h b/modules/video_processing/main/source/content_analysis.h deleted file mode 100644 index 7bdd45e19..000000000 --- a/modules/video_processing/main/source/content_analysis.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * content_analysis.h - */ - -#ifndef VPM_CONTENT_ANALYSIS_H -#define VPM_CONTENT_ANALYSIS_H - -#include "typedefs.h" -#include "module_common_types.h" - -#include "spatial_resampler.h" - -namespace webrtc { - -class VPMContentAnalysis -{ -public: - VPMContentAnalysis(); - ~VPMContentAnalysis(); - - //Initialize ContentAnalysis - should be called prior to extractContentFeature - //Inputs: width, height - //Return value: 0 if OK, negative value upon error - WebRtc_Word32 Initialize( WebRtc_UWord16 width, WebRtc_UWord16 height); - - //Extract content Feature - main function of ContentAnalysis - //Input: new frame - //Return value: pointer to structure containing content Analysis metrics or NULL value upon error - VideoContentMetrics* ComputeContentMetrics(const VideoFrame* inputFrame); - - //Release all allocated memory - //Output: 0 if OK, negative value upon error - WebRtc_Word32 Release(); - -private: - - //return motion metrics - VideoContentMetrics* ContentMetrics(); - - //Normalized temporal difference metric: for motion magnitude - WebRtc_Word32 TemporalDiffMetric(); - - //Motion metric method: call 2 metrics (magnitude and size) - WebRtc_Word32 ComputeMotionMetrics(); - - //Spatial metric method: computes the 3 frame-average spatial prediction errors (1x2,2x1,2x2) - WebRtc_Word32 ComputeSpatialMetrics(); - - const WebRtc_UWord8* _origFrame; - WebRtc_UWord8* _prevFrame; - WebRtc_UWord16 _width; - WebRtc_UWord16 _height; - - - //Content Metrics: - //stores the local average of the metrics - float _motionMagnitudeNZ; //for motion class - float _spatialPredErr; //for spatial class - float _spatialPredErrH; //for spatial class - float _spatialPredErrV; //for spatial class - float _sizeZeroMotion; //for motion class - float _motionPredErr; //for complexity class: - float _motionHorizontalness; //for coherence class - float _motionClusterDistortion; //for coherence class - - bool _firstFrame; - bool _CAInit; - VideoContentMetrics* _cMetrics; - -}; // end of VPMContentAnalysis class definition - -} // namespace - -#endif diff --git a/modules/video_processing/main/source/deflickering.cc b/modules/video_processing/main/source/deflickering.cc deleted file mode 100644 index d0b8d3b95..000000000 --- a/modules/video_processing/main/source/deflickering.cc +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - - -#include -#include - -#include "deflickering.h" -#include "trace.h" -#include "signal_processing_library.h" -#include "sort.h" - -namespace webrtc { - -// Detection constants -enum { kFrequencyDeviation = 39 }; // (Q4) Maximum allowed deviation for detection -enum { kMinFrequencyToDetect = 32 }; // (Q4) Minimum frequency that can be detected -enum { kNumFlickerBeforeDetect = 2 }; // Number of flickers before we accept detection -enum { kMeanValueScaling = 4 }; // (Q4) In power of 2 -enum { kZeroCrossingDeadzone = 10 }; // Deadzone region in terms of pixel values - -// Deflickering constants -// Compute the quantiles over 1 / DownsamplingFactor of the image. -enum { kDownsamplingFactor = 8 }; -enum { kLog2OfDownsamplingFactor = 3 }; - -// To generate in Matlab: -// >> probUW16 = round(2^11 * [0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,0.95,0.97]); -// >> fprintf('%d, ', probUW16) -// Resolution reduced to avoid overflow when multiplying with the (potentially) large -// number of pixels. -const WebRtc_UWord16 VPMDeflickering::_probUW16[kNumProbs] = - {102, 205, 410, 614, 819, 1024, 1229, 1434, 1638, 1843, 1946, 1987}; // - -// To generate in Matlab: -// >> numQuants = 14; maxOnlyLength = 5; -// >> weightUW16 = round(2^15 * [linspace(0.5, 1.0, numQuants - maxOnlyLength)]); -// >> fprintf('%d, %d,\n ', weightUW16); -const WebRtc_UWord16 VPMDeflickering::_weightUW16[kNumQuants - kMaxOnlyLength] = - {16384, 18432, 20480, 22528, 24576, 26624, 28672, 30720, 32768}; // - -VPMDeflickering::VPMDeflickering() : - _id(0) -{ - Reset(); -} - -VPMDeflickering::~VPMDeflickering() -{ -} - -WebRtc_Word32 -VPMDeflickering::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return 0; -} - -void -VPMDeflickering::Reset() -{ - _meanBufferLength = 0; - _detectionState = 0; - _frameRate = 0; - - memset(_meanBuffer, 0, sizeof(WebRtc_Word32) * kMeanBufferLength); - memset(_timestampBuffer, 0, sizeof(WebRtc_Word32) * kMeanBufferLength); - - // Initialize the history with a uniformly distributed histogram - _quantHistUW8[0][0] = 0; - _quantHistUW8[0][kNumQuants - 1] = 255; - for (WebRtc_Word32 i = 0; i < kNumProbs; i++) - { - _quantHistUW8[0][i + 1] = static_cast((WEBRTC_SPL_UMUL_16_16( - _probUW16[i], 255) + (1 << 10)) >> 11); // Unsigned round. - } - - for (WebRtc_Word32 i = 1; i < kFrameHistorySize; i++) - { - memcpy(_quantHistUW8[i], _quantHistUW8[0], sizeof(WebRtc_UWord8) * kNumQuants); - } -} - -WebRtc_Word32 -VPMDeflickering::ProcessFrame(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height, - const WebRtc_UWord32 timestamp, - VideoProcessingModule::FrameStats& stats) -{ - WebRtc_UWord32 frameMemory; - WebRtc_UWord8 quantUW8[kNumQuants]; - WebRtc_UWord8 maxQuantUW8[kNumQuants]; - WebRtc_UWord8 minQuantUW8[kNumQuants]; - WebRtc_UWord16 targetQuantUW16[kNumQuants]; - WebRtc_UWord16 incrementUW16; - WebRtc_UWord8 mapUW8[256]; - - WebRtc_UWord16 tmpUW16; - WebRtc_UWord32 tmpUW32; - - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Null frame pointer"); - return VPM_GENERAL_ERROR; - } - - // Stricter height check due to subsampling size calculation below. - if (width == 0 || height < 2) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame size"); - return VPM_GENERAL_ERROR; - } - - if (!VideoProcessingModule::ValidFrameStats(stats)) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame stats"); - return VPM_GENERAL_ERROR; - } - - if (PreDetection(timestamp, stats) == -1) - { - return VPM_GENERAL_ERROR; - } - - // Flicker detection - WebRtc_Word32 detFlicker = DetectFlicker(); - if (detFlicker < 0) - { // Error - return VPM_GENERAL_ERROR; - } - else if (detFlicker != 1) - { - return 0; - } - - // Size of luminance component - const WebRtc_UWord32 ySize = height * width; - - const WebRtc_UWord32 ySubSize = width * (((height - 1) >> - kLog2OfDownsamplingFactor) + 1); - WebRtc_UWord8* ySorted = new WebRtc_UWord8[ySubSize]; - WebRtc_UWord32 sortRowIdx = 0; - for (WebRtc_UWord32 i = 0; i < height; i += kDownsamplingFactor) - { - memcpy(ySorted + sortRowIdx * width, frame + i * width, width); - sortRowIdx++; - } - - webrtc::Sort(ySorted, ySubSize, webrtc::TYPE_UWord8); - - WebRtc_UWord32 probIdxUW32 = 0; - quantUW8[0] = 0; - quantUW8[kNumQuants - 1] = 255; - - // Ensure we won't get an overflow below. - // In practice, the number of subsampled pixels will not become this large. - if (ySubSize > (1 << 21) - 1) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, - "Subsampled number of pixels too large"); - return -1; - } - - for (WebRtc_Word32 i = 0; i < kNumProbs; i++) - { - probIdxUW32 = WEBRTC_SPL_UMUL_32_16(ySubSize, _probUW16[i]) >> 11; // - quantUW8[i + 1] = ySorted[probIdxUW32]; - } - - delete [] ySorted; - ySorted = NULL; - - // Shift history for new frame. - memmove(_quantHistUW8[1], _quantHistUW8[0], (kFrameHistorySize - 1) * kNumQuants * - sizeof(WebRtc_UWord8)); - // Store current frame in history. - memcpy(_quantHistUW8[0], quantUW8, kNumQuants * sizeof(WebRtc_UWord8)); - - // We use a frame memory equal to the ceiling of half the frame rate to ensure we - // capture an entire period of flicker. - frameMemory = (_frameRate + (1 << 5)) >> 5; // Unsigned ceiling. - // _frameRate in Q4. - if (frameMemory > kFrameHistorySize) - { - frameMemory = kFrameHistorySize; - } - - // Get maximum and minimum. - for (WebRtc_Word32 i = 0; i < kNumQuants; i++) - { - maxQuantUW8[i] = 0; - minQuantUW8[i] = 255; - for (WebRtc_UWord32 j = 0; j < frameMemory; j++) - { - if (_quantHistUW8[j][i] > maxQuantUW8[i]) - { - maxQuantUW8[i] = _quantHistUW8[j][i]; - } - - if (_quantHistUW8[j][i] < minQuantUW8[i]) - { - minQuantUW8[i] = _quantHistUW8[j][i]; - } - } - } - - // Get target quantiles. - for (WebRtc_Word32 i = 0; i < kNumQuants - kMaxOnlyLength; i++) - { - targetQuantUW16[i] = static_cast((WEBRTC_SPL_UMUL_16_16( - _weightUW16[i], maxQuantUW8[i]) + WEBRTC_SPL_UMUL_16_16((1 << 15) - - _weightUW16[i], minQuantUW8[i])) >> 8); // - } - - for (WebRtc_Word32 i = kNumQuants - kMaxOnlyLength; i < kNumQuants; i++) - { - targetQuantUW16[i] = ((WebRtc_UWord16)maxQuantUW8[i]) << 7; - } - - // Compute the map from input to output pixels. - WebRtc_UWord16 mapUW16; // - for (WebRtc_Word32 i = 1; i < kNumQuants; i++) - { - // As quant and targetQuant are limited to UWord8, we're safe to use Q7 here. - tmpUW32 = static_cast(targetQuantUW16[i] - - targetQuantUW16[i - 1]); // - tmpUW16 = static_cast(quantUW8[i] - quantUW8[i - 1]); // - - if (tmpUW16 > 0) - { - incrementUW16 = static_cast(WebRtcSpl_DivU32U16(tmpUW32, - tmpUW16)); // - } - else - { - // The value is irrelevant; the loop below will only iterate once. - incrementUW16 = 0; - } - - mapUW16 = targetQuantUW16[i - 1]; - for (WebRtc_UWord32 j = quantUW8[i - 1]; j < (WebRtc_UWord32)(quantUW8[i] + 1); j++) - { - mapUW8[j] = (WebRtc_UWord8)((mapUW16 + (1 << 6)) >> 7); // Unsigned round. - mapUW16 += incrementUW16; - } - } - - // Map to the output frame. - for (WebRtc_UWord32 i = 0; i < ySize; i++) - { - frame[i] = mapUW8[frame[i]]; - } - - // Frame was altered, so reset stats. - VideoProcessingModule::ClearFrameStats(stats); - - return 0; -} - -/** - Performs some pre-detection operations. Must be called before - DetectFlicker(). - - \param[in] timestamp Timestamp of the current frame. - \param[in] stats Statistics of the current frame. - - \return 0: Success\n - 2: Detection not possible due to flickering frequency too close to - zero.\n - -1: Error -*/ -WebRtc_Word32 -VPMDeflickering::PreDetection(const WebRtc_UWord32 timestamp, - const VideoProcessingModule::FrameStats& stats) -{ - WebRtc_Word32 meanVal; // Mean value of frame (Q4) - WebRtc_UWord32 frameRate = 0; - WebRtc_Word32 meanBufferLength; // Temp variable - - meanVal = ((stats.sum << kMeanValueScaling) / stats.numPixels); - /* Update mean value buffer. - * This should be done even though we might end up in an unreliable detection. - */ - memmove(_meanBuffer + 1, _meanBuffer, (kMeanBufferLength - 1) * sizeof(WebRtc_Word32)); - _meanBuffer[0] = meanVal; - - /* Update timestamp buffer. - * This should be done even though we might end up in an unreliable detection. - */ - memmove(_timestampBuffer + 1, _timestampBuffer, (kMeanBufferLength - 1) * - sizeof(WebRtc_UWord32)); - _timestampBuffer[0] = timestamp; - - /* Compute current frame rate (Q4) */ - if (_timestampBuffer[kMeanBufferLength - 1] != 0) - { - frameRate = ((90000 << 4) * (kMeanBufferLength - 1)); - frameRate /= (_timestampBuffer[0] - _timestampBuffer[kMeanBufferLength - 1]); - }else if (_timestampBuffer[1] != 0) - { - frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]); - } - - /* Determine required size of mean value buffer (_meanBufferLength) */ - if (frameRate == 0) { - meanBufferLength = 1; - } - else { - meanBufferLength = (kNumFlickerBeforeDetect * frameRate) / kMinFrequencyToDetect; - } - /* Sanity check of buffer length */ - if (meanBufferLength >= kMeanBufferLength) - { - /* Too long buffer. The flickering frequency is too close to zero, which - * makes the estimation unreliable. - */ - _meanBufferLength = 0; - return 2; - } - _meanBufferLength = meanBufferLength; - - if ((_timestampBuffer[_meanBufferLength - 1] != 0) && (_meanBufferLength != 1)) - { - frameRate = ((90000 << 4) * (_meanBufferLength - 1)); - frameRate /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]); - }else if (_timestampBuffer[1] != 0) - { - frameRate = (90000 << 4) / (_timestampBuffer[0] - _timestampBuffer[1]); - } - _frameRate = frameRate; - - return 0; -} - -/** - This function detects flicker in the video stream. As a side effect the mean value - buffer is updated with the new mean value. - - \return 0: No flickering detected\n - 1: Flickering detected\n - 2: Detection not possible due to unreliable frequency interval - -1: Error -*/ -WebRtc_Word32 VPMDeflickering::DetectFlicker() -{ - /* Local variables */ - WebRtc_UWord32 i; - WebRtc_Word32 freqEst; // (Q4) Frequency estimate to base detection upon - WebRtc_Word32 retVal = -1; - - /* Sanity check for _meanBufferLength */ - if (_meanBufferLength < 2) - { - /* Not possible to estimate frequency */ - return(2); - } - /* Count zero crossings with a dead zone to be robust against noise. - * If the noise std is 2 pixel this corresponds to about 95% confidence interval. - */ - WebRtc_Word32 deadzone = (kZeroCrossingDeadzone << kMeanValueScaling); // Q4 - WebRtc_Word32 meanOfBuffer = 0; // Mean value of mean value buffer - WebRtc_Word32 numZeros = 0; // Number of zeros that cross the deadzone - WebRtc_Word32 cntState = 0; // State variable for zero crossing regions - WebRtc_Word32 cntStateOld = 0; // Previous state variable for zero crossing regions - - for (i = 0; i < _meanBufferLength; i++) - { - meanOfBuffer += _meanBuffer[i]; - } - meanOfBuffer += (_meanBufferLength >> 1); // Rounding, not truncation - meanOfBuffer /= _meanBufferLength; - - /* Count zero crossings */ - cntStateOld = (_meanBuffer[0] >= (meanOfBuffer + deadzone)); - cntStateOld -= (_meanBuffer[0] <= (meanOfBuffer - deadzone)); - for (i = 1; i < _meanBufferLength; i++) - { - cntState = (_meanBuffer[i] >= (meanOfBuffer + deadzone)); - cntState -= (_meanBuffer[i] <= (meanOfBuffer - deadzone)); - if (cntStateOld == 0) - { - cntStateOld = -cntState; - } - if (((cntState + cntStateOld) == 0) && (cntState != 0)) - { - numZeros++; - cntStateOld = cntState; - } - } - /* END count zero crossings */ - - /* Frequency estimation according to: - * freqEst = numZeros * frameRate / 2 / _meanBufferLength; - * - * Resolution is set to Q4 - */ - freqEst = ((numZeros * 90000) << 3); - freqEst /= (_timestampBuffer[0] - _timestampBuffer[_meanBufferLength - 1]); - - /* Translate frequency estimate to regions close to 100 and 120 Hz */ - WebRtc_UWord8 freqState = 0; // Current translation state; - // (0) Not in interval, - // (1) Within valid interval, - // (2) Out of range - WebRtc_Word32 freqAlias = freqEst; - if (freqEst > kMinFrequencyToDetect) - { - WebRtc_UWord8 aliasState = 1; - while(freqState == 0) - { - /* Increase frequency */ - freqAlias += (aliasState * _frameRate); - freqAlias += ((freqEst << 1) * (1 - (aliasState << 1))); - /* Compute state */ - freqState = (abs(freqAlias - (100 << 4)) <= kFrequencyDeviation); - freqState += (abs(freqAlias - (120 << 4)) <= kFrequencyDeviation); - freqState += 2 * (freqAlias > ((120 << 4) + kFrequencyDeviation)); - /* Switch alias state */ - aliasState++; - aliasState &= 0x01; - } - } - /* Is frequency estimate within detection region? */ - if (freqState == 1) - { - retVal = 1; - }else if (freqState == 0) - { - retVal = 2; - }else - { - retVal = 0; - } - return retVal; -} - -} //namespace diff --git a/modules/video_processing/main/source/deflickering.h b/modules/video_processing/main/source/deflickering.h deleted file mode 100644 index ee5f90d85..000000000 --- a/modules/video_processing/main/source/deflickering.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * deflickering.h - */ - -#ifndef VPM_DEFLICKERING_H -#define VPM_DEFLICKERING_H - -#include "typedefs.h" -#include "video_processing.h" - -#include // NULL - -namespace webrtc { - -class VPMDeflickering -{ -public: - VPMDeflickering(); - ~VPMDeflickering(); - - WebRtc_Word32 ChangeUniqueId(WebRtc_Word32 id); - - void Reset(); - - WebRtc_Word32 ProcessFrame(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - WebRtc_UWord32 timestamp, - VideoProcessingModule::FrameStats& stats); -private: - WebRtc_Word32 PreDetection(WebRtc_UWord32 timestamp, - const VideoProcessingModule::FrameStats& stats); - - WebRtc_Word32 DetectFlicker(); - - enum { kMeanBufferLength = 32 }; - enum { kFrameHistorySize = 15 }; - enum { kNumProbs = 12 }; - enum { kNumQuants = kNumProbs + 2 }; - enum { kMaxOnlyLength = 5 }; - - WebRtc_Word32 _id; - - WebRtc_UWord32 _meanBufferLength; - WebRtc_UWord8 _detectionState; // 0: No flickering - // 1: Flickering detected - // 2: In flickering - WebRtc_Word32 _meanBuffer[kMeanBufferLength]; - WebRtc_UWord32 _timestampBuffer[kMeanBufferLength]; - WebRtc_UWord32 _frameRate; - static const WebRtc_UWord16 _probUW16[kNumProbs]; - static const WebRtc_UWord16 _weightUW16[kNumQuants - kMaxOnlyLength]; - WebRtc_UWord8 _quantHistUW8[kFrameHistorySize][kNumQuants]; -}; - -} //namespace - -#endif // VPM_DEFLICKERING_H - diff --git a/modules/video_processing/main/source/denoising.cc b/modules/video_processing/main/source/denoising.cc deleted file mode 100644 index 395b00c69..000000000 --- a/modules/video_processing/main/source/denoising.cc +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "denoising.h" -#include "trace.h" - -#include - -namespace webrtc { - -enum { kSubsamplingTime = 0 }; // Down-sampling in time (unit: number of frames) -enum { kSubsamplingWidth = 3 }; // Sub-sampling in width (unit: power of 2) -enum { kSubsamplingHeight = 2 }; // Sub-sampling in height (unit: power of 2) -enum { kDenoiseFiltParam = 179 }; // (Q8) De-noising filter parameter -enum { kDenoiseFiltParamRec = 77 }; // (Q8) 1 - filter parameter -enum { kDenoiseThreshold = 19200 }; // (Q8) De-noising threshold level - -VPMDenoising::VPMDenoising() : - _id(0), - _moment1(NULL), - _moment2(NULL) -{ - Reset(); -} - -VPMDenoising::~VPMDenoising() -{ - if (_moment1) - { - delete [] _moment1; - _moment1 = NULL; - } - - if (_moment2) - { - delete [] _moment2; - _moment2 = NULL; - } -} - -WebRtc_Word32 -VPMDenoising::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return VPM_OK; -} - -void -VPMDenoising::Reset() -{ - _frameSize = 0; - _denoiseFrameCnt = 0; - - if (_moment1) - { - delete [] _moment1; - _moment1 = NULL; - } - - if (_moment2) - { - delete [] _moment2; - _moment2 = NULL; - } -} - -WebRtc_Word32 -VPMDenoising::ProcessFrame(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - WebRtc_Word32 thevar; - WebRtc_UWord32 k; - WebRtc_UWord32 jsub, ksub; - WebRtc_Word32 diff0; - WebRtc_UWord32 tmpMoment1; - WebRtc_UWord32 tmpMoment2; - WebRtc_UWord32 tmp; - WebRtc_Word32 numPixelsChanged = 0; - - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Null frame pointer"); - return VPM_GENERAL_ERROR; - } - - if (width == 0 || height == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame size"); - return VPM_GENERAL_ERROR; - } - - /* Size of luminance component */ - const WebRtc_UWord32 ysize = height * width; - - /* Initialization */ - if (ysize != _frameSize) - { - delete [] _moment1; - _moment1 = NULL; - - delete [] _moment2; - _moment2 = NULL; - } - _frameSize = ysize; - - if (!_moment1) - { - _moment1 = new WebRtc_UWord32[ysize]; - memset(_moment1, 0, sizeof(WebRtc_UWord32)*ysize); - } - - if (!_moment2) - { - _moment2 = new WebRtc_UWord32[ysize]; - memset(_moment2, 0, sizeof(WebRtc_UWord32)*ysize); - } - - /* Apply de-noising on each pixel, but update variance sub-sampled */ - for (WebRtc_UWord32 i = 0; i < height; i++) - { // Collect over height - k = i * width; - ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width; - for (WebRtc_UWord32 j = 0; j < width; j++) - { // Collect over width - jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth); - /* Update mean value for every pixel and every frame */ - tmpMoment1 = _moment1[k + j]; - tmpMoment1 *= kDenoiseFiltParam; // Q16 - tmpMoment1 += ((kDenoiseFiltParamRec * ((WebRtc_UWord32)frame[k + j])) << 8); - tmpMoment1 >>= 8; // Q8 - _moment1[k + j] = tmpMoment1; - - tmpMoment2 = _moment2[ksub + jsub]; - if ((ksub == k) && (jsub == j) && (_denoiseFrameCnt == 0)) - { - tmp = ((WebRtc_UWord32)frame[k + j] * (WebRtc_UWord32)frame[k + j]); - tmpMoment2 *= kDenoiseFiltParam; // Q16 - tmpMoment2 += ((kDenoiseFiltParamRec * tmp)<<8); - tmpMoment2 >>= 8; // Q8 - } - _moment2[k + j] = tmpMoment2; - /* Current event = deviation from mean value */ - diff0 = ((WebRtc_Word32)frame[k + j] << 8) - _moment1[k + j]; - /* Recent events = variance (variations over time) */ - thevar = _moment2[k + j]; - thevar -= ((_moment1[k + j] * _moment1[k + j]) >> 8); - /*************************************************************************** - * De-noising criteria, i.e., when should we replace a pixel by its mean - * - * 1) recent events are minor - * 2) current events are minor - ***************************************************************************/ - if ((thevar < kDenoiseThreshold) - && ((diff0 * diff0 >> 8) < kDenoiseThreshold)) - { // Replace with mean - frame[k + j] = (WebRtc_UWord8)(_moment1[k + j] >> 8); - numPixelsChanged++; - } - } - } - - /* Update frame counter */ - _denoiseFrameCnt++; - if (_denoiseFrameCnt > kSubsamplingTime) - { - _denoiseFrameCnt = 0; - } - - return numPixelsChanged; -} - -} //namespace diff --git a/modules/video_processing/main/source/denoising.h b/modules/video_processing/main/source/denoising.h deleted file mode 100644 index f53157cf4..000000000 --- a/modules/video_processing/main/source/denoising.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * denoising.h - */ -#ifndef VPM_DENOISING_H -#define VPM_DENOISING_H - -#include "typedefs.h" -#include "video_processing.h" - -namespace webrtc { - -class VPMDenoising -{ -public: - VPMDenoising(); - ~VPMDenoising(); - - WebRtc_Word32 ChangeUniqueId(WebRtc_Word32 id); - - void Reset(); - - WebRtc_Word32 ProcessFrame(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height); - -private: - WebRtc_Word32 _id; - - WebRtc_UWord32* _moment1; // (Q8) First order moment (mean) - WebRtc_UWord32* _moment2; // (Q8) Second order moment - WebRtc_UWord32 _frameSize; // Size (# of pixels) of frame - WebRtc_Word32 _denoiseFrameCnt; // Counter for subsampling in time -}; - -} //namespace - -#endif // VPM_DENOISING_H - diff --git a/modules/video_processing/main/source/frame_preprocessor.cc b/modules/video_processing/main/source/frame_preprocessor.cc deleted file mode 100644 index 57fcec38c..000000000 --- a/modules/video_processing/main/source/frame_preprocessor.cc +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "frame_preprocessor.h" -#include "trace.h" - -namespace webrtc { - -VPMFramePreprocessor::VPMFramePreprocessor(): -_id(0), -_contentMetrics(NULL), -_nativeHeight(0), -_nativeWidth(0), -_resampledFrame(), -_enableCA(false) -{ - _spatialResampler = new VPMSimpleSpatialResampler(); - _ca = new VPMContentAnalysis(); - _vd = new VPMVideoDecimator(); -} - -VPMFramePreprocessor::~VPMFramePreprocessor() -{ - Reset(); - delete _spatialResampler; - delete _ca; - delete _vd; - _resampledFrame.Free(); // is this needed? -} - -WebRtc_Word32 -VPMFramePreprocessor::ChangeUniqueId(const WebRtc_Word32 id) -{ - _id = id; - return VPM_OK; -} - -void -VPMFramePreprocessor::Reset() -{ - _nativeWidth = 0; - _nativeHeight = 0; - _ca->Release(); - _vd->Reset(); - _contentMetrics = NULL; - _spatialResampler->Reset(); - _enableCA = false; -} - - -void -VPMFramePreprocessor::EnableTemporalDecimation(bool enable) -{ - _vd->EnableTemporalDecimation(enable); -} -void -VPMFramePreprocessor::EnableContentAnalysis(bool enable) -{ - _enableCA = enable; -} - -void -VPMFramePreprocessor::SetInputFrameResampleMode(VideoFrameResampling resamplingMode) -{ - _spatialResampler->SetInputFrameResampleMode(resamplingMode); -} - - -WebRtc_Word32 -VPMFramePreprocessor::SetMaxFrameRate(WebRtc_UWord32 maxFrameRate) -{ - if (maxFrameRate == 0) - { - return VPM_PARAMETER_ERROR; - } - //Max allowed frame rate - _maxFrameRate = maxFrameRate; - - return _vd->SetMaxFrameRate(maxFrameRate); -} - - -WebRtc_Word32 -VPMFramePreprocessor::SetTargetResolution(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32 frameRate) -{ - if ( (width == 0) || (height == 0) || (frameRate == 0)) - { - return VPM_PARAMETER_ERROR; - } - WebRtc_Word32 retVal = 0; - retVal = _spatialResampler->SetTargetFrameSize(width, height); - if (retVal < 0) - { - return retVal; - } - retVal = _vd->SetTargetFrameRate(frameRate); - if (retVal < 0) - { - return retVal; - } - - return VPM_OK; -} - -void -VPMFramePreprocessor::UpdateIncomingFrameRate() -{ - _vd->UpdateIncomingFrameRate(); -} - -WebRtc_UWord32 -VPMFramePreprocessor::DecimatedFrameRate() -{ - return _vd->DecimatedFrameRate(); -} - - -WebRtc_UWord32 -VPMFramePreprocessor::DecimatedWidth() const -{ - return _spatialResampler->TargetWidth(); -} - - -WebRtc_UWord32 -VPMFramePreprocessor::DecimatedHeight() const -{ - return _spatialResampler->TargetHeight(); -} - - -WebRtc_Word32 -VPMFramePreprocessor::PreprocessFrame(const VideoFrame* frame, VideoFrame** processedFrame) -{ - if (frame == NULL) - { - return VPM_PARAMETER_ERROR; - } - else if (frame->Height() == 0 || frame->Width() == 0) - { - return VPM_PARAMETER_ERROR; - } - _vd->UpdateIncomingFrameRate(); - - if (_vd->DropFrame()) - { - WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, _id, "Drop frame due to frame rate"); - return 1;// drop 1 frame - } - - //Resizing incoming frame. - // Note that we must make a copy of it. We are not allowed to resample the input frame. - WebRtc_Word32 ret = _spatialResampler->ResampleFrame(*frame, _resampledFrame); - if (ret != VPM_OK) - { - return ret; - } - - *processedFrame = &_resampledFrame; - - // Perform content analysis on the resampled frame - if (_enableCA) - { - _contentMetrics = _ca->ComputeContentMetrics(&_resampledFrame); - //Update native values: - _contentMetrics->nativeHeight = frame->Height(); - _contentMetrics->nativeWidth = frame->Width(); - - _contentMetrics->nativeFrameRate = _maxFrameRate; // max value as set by user - } - return VPM_OK; -} - - -VideoContentMetrics* -VPMFramePreprocessor::ContentMetrics() const -{ - return _contentMetrics; -} - -} //namespace diff --git a/modules/video_processing/main/source/frame_preprocessor.h b/modules/video_processing/main/source/frame_preprocessor.h deleted file mode 100644 index cb5f97d4b..000000000 --- a/modules/video_processing/main/source/frame_preprocessor.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * frame_preprocessor.h - */ -#ifndef VPM_FRAME_PREPROCESSOR_H -#define VPM_FRAME_PREPROCESSOR_H - -#include "typedefs.h" -#include "video_processing.h" -#include "content_analysis.h" -#include "spatial_resampler.h" -#include "video_decimator.h" - -namespace webrtc { - - -class VPMFramePreprocessor -{ -public: - - VPMFramePreprocessor(); - ~VPMFramePreprocessor(); - - WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - void Reset(); - - // Enable temporal decimation - void EnableTemporalDecimation(bool enable); - - void SetInputFrameResampleMode(VideoFrameResampling resamplingMode); - - //Enable content analysis - void EnableContentAnalysis(bool enable); - - //Set max frame rate - WebRtc_Word32 SetMaxFrameRate(WebRtc_UWord32 maxFrameRate); - - //Set target resolution: frame rate and dimension - WebRtc_Word32 SetTargetResolution(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32 frameRate); - - //Update incoming frame rate/dimension - void UpdateIncomingFrameRate(); - - WebRtc_Word32 updateIncomingFrameSize(WebRtc_UWord32 width, WebRtc_UWord32 height); - - //Set decimated values: frame rate/dimension - WebRtc_UWord32 DecimatedFrameRate(); - WebRtc_UWord32 DecimatedWidth() const; - WebRtc_UWord32 DecimatedHeight() const; - - //Preprocess output: - WebRtc_Word32 PreprocessFrame(const VideoFrame* frame, VideoFrame** processedFrame); - VideoContentMetrics* ContentMetrics() const; - -private: - - WebRtc_Word32 _id; - VideoContentMetrics* _contentMetrics; - WebRtc_UWord32 _nativeHeight; - WebRtc_UWord32 _nativeWidth; - WebRtc_UWord32 _maxFrameRate; - VideoFrame _resampledFrame; - VPMSpatialResampler* _spatialResampler; - VPMContentAnalysis* _ca; - VPMVideoDecimator* _vd; - bool _enableCA; - -}; // end of VPMFramePreprocessor class definition - -} //namespace - -#endif // VPM_FRAME_PREPROCESS_H diff --git a/modules/video_processing/main/source/spatial_resampler.cc b/modules/video_processing/main/source/spatial_resampler.cc deleted file mode 100644 index 0014ef297..000000000 --- a/modules/video_processing/main/source/spatial_resampler.cc +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "spatial_resampler.h" - - -namespace webrtc { - -VPMSimpleSpatialResampler::VPMSimpleSpatialResampler() -: -_resamplingMode(kFastRescaling), -_targetWidth(0), -_targetHeight(0), -_interpolatorPtr(NULL) -{ -} - -VPMSimpleSpatialResampler::~VPMSimpleSpatialResampler() -{ - Release(); -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::Release() -{ - if (_interpolatorPtr != NULL) - { - delete _interpolatorPtr; - _interpolatorPtr = NULL; - } - return VPM_OK; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::SetTargetFrameSize(WebRtc_UWord32 width, - WebRtc_UWord32 height) -{ - if (_resamplingMode == kNoRescaling) - { - return VPM_OK; - } - - if (width < 1 || height < 1) - { - return VPM_PARAMETER_ERROR; - } - - _targetWidth = width; - _targetHeight = height; - - return VPM_OK; -} - -void -VPMSimpleSpatialResampler::SetInputFrameResampleMode(VideoFrameResampling - resamplingMode) -{ - _resamplingMode = resamplingMode; -} - -void -VPMSimpleSpatialResampler::Reset() -{ - _resamplingMode = kFastRescaling; - _targetWidth = 0; - _targetHeight = 0; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::ResampleFrame(const VideoFrame& inFrame, - VideoFrame& outFrame) -{ - WebRtc_Word32 ret; - - if (_resamplingMode == kNoRescaling) - { - return outFrame.CopyFrame(inFrame); - } - else if (_targetWidth < 1 || _targetHeight < 1) - { - return VPM_PARAMETER_ERROR; - } - - // Check if re-sampling is needed - if ((inFrame.Width() == _targetWidth) && - (inFrame.Height() == _targetHeight)) - { - return outFrame.CopyFrame(inFrame); - } - if (_resamplingMode == kBiLinear) - { - return BiLinearInterpolation(inFrame, outFrame); - } - - outFrame.SetTimeStamp(inFrame.TimeStamp()); - - WebRtc_UWord32 currentLength = inFrame.Width() * inFrame.Height() * 3 / 2; - if (_targetWidth > inFrame.Width() && - ( ExactMultiplier(inFrame.Width(), inFrame.Height()))) - { - // The codec might want to pad this later... adding 8 pixels - const WebRtc_UWord32 requiredSize = (_targetWidth + 8) * - (_targetHeight + 8) * 3 / 2; - outFrame.VerifyAndAllocate(requiredSize); - return UpsampleFrame(inFrame, outFrame); - } - else - { - // 1 cut/pad - // 2 scale factor 2X (in both cases if required) - WebRtc_UWord32 croppedWidth = inFrame.Width(); - WebRtc_UWord32 croppedHeight = inFrame.Height(); - - //Calculates cropped dimensions - CropSize(inFrame.Width(), inFrame.Height(), - croppedWidth, croppedHeight); - - VideoFrame* targetFrame; - outFrame.VerifyAndAllocate(croppedWidth * croppedHeight * 3 / 2); - targetFrame = &outFrame; - - ConvertI420ToI420(inFrame.Buffer(), inFrame.Width(), inFrame.Height(), - targetFrame->Buffer(), croppedWidth, croppedHeight); - targetFrame->SetWidth(croppedWidth); - targetFrame->SetHeight(croppedHeight); - //We have correct aspect ratio, sub-sample with a multiple of two to get - //close to the target size - ret = SubsampleMultipleOf2(*targetFrame); - - if (ret != VPM_OK) - { - return ret; - } - } - - return VPM_OK; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::UpsampleFrame(const VideoFrame& inFrame, - VideoFrame& outFrame) -{ - outFrame.CopyFrame(inFrame); - WebRtc_UWord32 currentLength = inFrame.Width() * inFrame.Height() * 3 / 2; - - float ratioWidth = _targetWidth / (float)inFrame.Width(); - float ratioHeight = _targetHeight / (float)inFrame.Height(); - - WebRtc_UWord32 scaledWidth = 0; - WebRtc_UWord32 scaledHeight = 0; - bool scaled = true; - - if(ratioWidth > 1 || ratioHeight > 1) - { - // scale up - if(ratioWidth <= 1.5 && ratioHeight <= 1.5) - { - // scale up 1.5 - currentLength = ScaleI420Up3_2(inFrame.Width(), inFrame.Height(), - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - } - else if(ratioWidth <= 2 && ratioHeight <= 2) - { - // scale up 2 - currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - } - else if(ratioWidth <= 2.25 && ratioHeight <= 2.25) - { - // scale up 2.25 - currentLength = ScaleI420Up3_2(inFrame.Width(), inFrame.Height(), - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - currentLength = ScaleI420Up3_2(scaledWidth, scaledHeight, - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - } - else if(ratioWidth <= 3 && ratioHeight <= 3) - { - // scale up 3 - currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - currentLength = ScaleI420Up3_2(scaledWidth, scaledHeight, - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - } - else if(ratioWidth <= 4 && ratioHeight <= 4) - { - // scale up 4 - currentLength = ScaleI420Up2(inFrame.Width(), inFrame.Height(), - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - currentLength = ScaleI420Up2(scaledWidth, scaledHeight, - outFrame.Buffer(), outFrame.Size(), - scaledWidth, scaledHeight); - } - - //TODO: what if ratioWidth/Height >= 8 ? - - if (scaledWidth <= 0 || scaledHeight <= 0) - { - return VPM_GENERAL_ERROR; - } - - if ((static_cast(scaledWidth) > _targetWidth) || - (static_cast(scaledHeight) > _targetHeight)) - { - currentLength = CutI420Frame(outFrame.Buffer(), scaledWidth, - scaledHeight, _targetWidth, - _targetHeight); - } - } - else - { - return VPM_GENERAL_ERROR; - } - - outFrame.SetWidth(_targetWidth); - outFrame.SetHeight(_targetHeight); - outFrame.SetLength(_targetWidth * _targetHeight * 3 / 2); - - return VPM_OK; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::CropSize(WebRtc_UWord32 width, WebRtc_UWord32 height, - WebRtc_UWord32& croppedWidth, - WebRtc_UWord32& croppedHeight) const -{ - // Crop the image to a width and height which is a - // multiple of two, so that we can do a simpler scaling. - croppedWidth = _targetWidth; - croppedHeight = _targetHeight; - - if (width >= 8 * _targetWidth && height >= 8 * _targetHeight) - { - croppedWidth = 8 * _targetWidth; - croppedHeight = 8 * _targetHeight; - } - else if (width >= 4 * _targetWidth && height >= 4 * _targetHeight) - { - croppedWidth = 4 * _targetWidth; - croppedHeight = 4 * _targetHeight; - } - else if (width >= 2 * _targetWidth && height >= 2 * _targetHeight) - { - croppedWidth = 2 * _targetWidth; - croppedHeight = 2 * _targetHeight; - } - return VPM_OK; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::SubsampleMultipleOf2(VideoFrame& frame) -{ - WebRtc_UWord32 tempWidth = frame.Width(); - WebRtc_UWord32 tempHeight = frame.Height(); - - while (tempWidth / _targetWidth >= 2 && tempHeight / _targetHeight >= 2) - { - ScaleI420FrameQuarter(tempWidth, tempHeight, frame.Buffer()); - tempWidth /= 2; - tempHeight /= 2; - } - frame.SetWidth(tempWidth); - frame.SetHeight(tempHeight); - frame.SetLength(frame.Width() * frame.Height() * 3 / 2); - - return VPM_OK; -} - - -bool -VPMSimpleSpatialResampler::ExactMultiplier(WebRtc_UWord32 width, - WebRtc_UWord32 height) const -{ - bool exactMultiplier = false; - if (_targetWidth % width == 0 && _targetHeight % height == 0) - { - // we have a multiple, is it an even multiple? - WebRtc_Word32 widthMultiple = _targetWidth / width; - WebRtc_Word32 heightMultiple = _targetHeight / height; - if (widthMultiple == 2 && heightMultiple == 2 || - widthMultiple == 4 && heightMultiple == 4 || - widthMultiple == 8 && heightMultiple == 8 || - widthMultiple == 1 && heightMultiple == 1) - { - exactMultiplier = true; - } - } - return exactMultiplier; -} - -WebRtc_Word32 -VPMSimpleSpatialResampler::BiLinearInterpolation(const VideoFrame& inFrame, - VideoFrame& outFrame) -{ - WebRtc_Word32 retVal; - - if (_interpolatorPtr == NULL) - { - _interpolatorPtr = new interpolator(); - } - // set bi-linear interpolator - retVal = _interpolatorPtr->Set(inFrame.Width(), inFrame.Height(), - _targetWidth, _targetHeight, - kI420, kI420, kBilinear ); - if (retVal < 0 ) - { - return retVal; - } - - // Verify size of output buffer - outFrame.VerifyAndAllocate(_targetHeight * _targetWidth * 3 >> 1); - WebRtc_UWord32 outSz = outFrame.Size(); - - // interpolate frame - retVal = _interpolatorPtr->Interpolate(inFrame.Buffer(), - outFrame.Buffer(), outSz); - - assert(outSz <= outFrame.Size()); - - // returns height - if (retVal < 0) - { - return retVal; - } - - // Set output frame parameters - outFrame.SetHeight(_targetHeight); - outFrame.SetWidth(_targetWidth); - outFrame.SetLength(outSz); - outFrame.SetTimeStamp(inFrame.TimeStamp()); - return VPM_OK; -} - -WebRtc_UWord32 -VPMSimpleSpatialResampler::TargetHeight() -{ - return _targetHeight; -} - -WebRtc_UWord32 -VPMSimpleSpatialResampler::TargetWidth() -{ - return _targetWidth; -} - - -} //namespace diff --git a/modules/video_processing/main/source/spatial_resampler.h b/modules/video_processing/main/source/spatial_resampler.h deleted file mode 100644 index aa9cd36ef..000000000 --- a/modules/video_processing/main/source/spatial_resampler.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * spatial_resampler.h - */ - -#ifndef VPM_SPATIAL_RESAMPLER_H -#define VPM_SPATIAL_RESAMPLER_H - -#include "typedefs.h" - -#include "module_common_types.h" -#include "video_processing_defines.h" - -#include "vplib.h" -#include "interpolator.h" - -namespace webrtc { - -class VPMSpatialResampler -{ -public: - virtual ~VPMSpatialResampler() {}; - virtual WebRtc_Word32 SetTargetFrameSize(WebRtc_UWord32 width, - WebRtc_UWord32 height) = 0; - virtual void SetInputFrameResampleMode(VideoFrameResampling - resamplingMode) = 0; - virtual void Reset() = 0; - virtual WebRtc_Word32 ResampleFrame(const VideoFrame& inFrame, - VideoFrame& outFrame) = 0; - virtual WebRtc_UWord32 TargetWidth() = 0; - virtual WebRtc_UWord32 TargetHeight() = 0; - virtual WebRtc_Word32 Release() = 0; -}; - -class VPMSimpleSpatialResampler : public VPMSpatialResampler -{ -public: - VPMSimpleSpatialResampler(); - ~VPMSimpleSpatialResampler(); - virtual WebRtc_Word32 SetTargetFrameSize(WebRtc_UWord32 width, - WebRtc_UWord32 height); - virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode); - virtual void Reset(); - virtual WebRtc_Word32 ResampleFrame(const VideoFrame& inFrame, - VideoFrame& outFrame); - virtual WebRtc_UWord32 TargetWidth(); - virtual WebRtc_UWord32 TargetHeight(); - virtual WebRtc_Word32 Release(); - -private: - WebRtc_Word32 UpsampleFrame(const VideoFrame& inFrame, VideoFrame& outFrame); - WebRtc_Word32 CropSize(WebRtc_UWord32 width, WebRtc_UWord32 height, - WebRtc_UWord32& croppedWidth, - WebRtc_UWord32& croppedHeight) const; - WebRtc_Word32 SubsampleMultipleOf2(VideoFrame& frame); - bool ExactMultiplier(WebRtc_UWord32 width, WebRtc_UWord32 height) const; - WebRtc_Word32 BiLinearInterpolation(const VideoFrame& inFrame, - VideoFrame& outFrame); - - - VideoFrameResampling _resamplingMode; - WebRtc_UWord32 _targetWidth; - WebRtc_UWord32 _targetHeight; - interpolator* _interpolatorPtr; -}; - -} //namespace - -#endif diff --git a/modules/video_processing/main/source/video_decimator.cc b/modules/video_processing/main/source/video_decimator.cc deleted file mode 100644 index 43bda0885..000000000 --- a/modules/video_processing/main/source/video_decimator.cc +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_decimator.h" -#include "tick_util.h" -#include "video_processing.h" - -#define VD_MIN(a, b) ((a) < (b)) ? (a) : (b) - -namespace webrtc { - -VPMVideoDecimator::VPMVideoDecimator() -: -_overShootModifier(0), -_dropCount(0), -_keepCount(0), -_targetFrameRate(30), -_incomingFrameRate(0.0f), -_maxFrameRate(30), -_incomingFrameTimes(), -_enableTemporalDecimation(true) -{ - Reset(); -} - -VPMVideoDecimator::~VPMVideoDecimator() -{ - // -} - -void -VPMVideoDecimator::Reset() -{ - _overShootModifier = 0; - _dropCount = 0; - _keepCount = 0; - _targetFrameRate = 30; - _incomingFrameRate = 0.0f; - _maxFrameRate = 30; - memset(_incomingFrameTimes, 0, sizeof(_incomingFrameTimes)); - _enableTemporalDecimation = true; -} - -void -VPMVideoDecimator::EnableTemporalDecimation(bool enable) -{ - _enableTemporalDecimation = enable; -} -WebRtc_Word32 -VPMVideoDecimator::SetMaxFrameRate(WebRtc_UWord32 maxFrameRate) -{ - if (maxFrameRate == 0) - { - return VPM_PARAMETER_ERROR; - } - - _maxFrameRate = maxFrameRate; - - if (_targetFrameRate > _maxFrameRate) - { - _targetFrameRate = _maxFrameRate; - - } - return VPM_OK; -} - -WebRtc_Word32 -VPMVideoDecimator::SetTargetFrameRate(WebRtc_UWord32 frameRate) -{ - if (frameRate == 0) - { - return VPM_PARAMETER_ERROR; - } - if (frameRate > _maxFrameRate) - { - //override - _targetFrameRate = _maxFrameRate; - } - else - { - _targetFrameRate = frameRate; - } - return VPM_OK; -} - -bool -VPMVideoDecimator::DropFrame() -{ - if (!_enableTemporalDecimation) - { - return false; - } - - if (_incomingFrameRate <= 0) - { - return false; - } - - const WebRtc_UWord32 incomingFrameRate = static_cast(_incomingFrameRate + 0.5f); - - if (_targetFrameRate == 0) - { - return true; - } - - bool drop = false; - if (incomingFrameRate > _targetFrameRate) - { - WebRtc_Word32 overshoot = _overShootModifier + (incomingFrameRate - _targetFrameRate); - if(overshoot < 0) - { - overshoot = 0; - _overShootModifier = 0; - } - - if (overshoot && 2 * overshoot < (WebRtc_Word32) incomingFrameRate) - { - - if (_dropCount) // Just got here so drop to be sure. - { - _dropCount = 0; - return true; - } - const WebRtc_UWord32 dropVar = incomingFrameRate / overshoot; - - if (_keepCount >= dropVar) - { - drop = true; - _overShootModifier = -((WebRtc_Word32) incomingFrameRate % overshoot) / 3; - _keepCount = 1; - } - else - { - - _keepCount++; - } - } - else - { - _keepCount = 0; - const WebRtc_UWord32 dropVar = overshoot / _targetFrameRate; - if (_dropCount < dropVar) - { - drop = true; - _dropCount++; - } - else - { - _overShootModifier = overshoot % _targetFrameRate; - drop = false; - _dropCount = 0; - } - } - } - - return drop; -} - - -WebRtc_UWord32 -VPMVideoDecimator::DecimatedFrameRate() -{ - ProcessIncomingFrameRate(TickTime::MillisecondTimestamp()); - if (!_enableTemporalDecimation) - { - return static_cast(_incomingFrameRate + 0.5f); - } - return VD_MIN(_targetFrameRate, static_cast(_incomingFrameRate + 0.5f)); -} - -WebRtc_UWord32 -VPMVideoDecimator::InputFrameRate() -{ - ProcessIncomingFrameRate(TickTime::MillisecondTimestamp()); - return static_cast(_incomingFrameRate + 0.5f); -} - -void -VPMVideoDecimator::UpdateIncomingFrameRate() -{ - WebRtc_Word64 now = TickTime::MillisecondTimestamp(); - if(_incomingFrameTimes[0] == 0) - { - // first no shift - } else - { - // shift - for(int i = (kFrameCountHistorySize - 2); i >= 0 ; i--) - { - _incomingFrameTimes[i+1] = _incomingFrameTimes[i]; - } - } - _incomingFrameTimes[0] = now; - ProcessIncomingFrameRate(now); -} - -void -VPMVideoDecimator::ProcessIncomingFrameRate(WebRtc_Word64 now) -{ - WebRtc_Word32 num = 0; - WebRtc_Word32 nrOfFrames = 0; - for(num = 1; num < (kFrameCountHistorySize - 1); num++) - { - if (_incomingFrameTimes[num] <= 0 || - now - _incomingFrameTimes[num] > kFrameHistoryWindowMs) // don't use data older than 2sec - { - break; - } else - { - nrOfFrames++; - } - } - if (num > 1) - { - WebRtc_Word64 diff = now - _incomingFrameTimes[num-1]; - _incomingFrameRate = 1.0; - if(diff >0) - { - _incomingFrameRate = nrOfFrames * 1000.0f / static_cast(diff); - } - } - else - { - _incomingFrameRate = static_cast(nrOfFrames); - } -} - -} //namespace diff --git a/modules/video_processing/main/source/video_decimator.h b/modules/video_processing/main/source/video_decimator.h deleted file mode 100644 index e152bb98b..000000000 --- a/modules/video_processing/main/source/video_decimator.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_decimator.h - */ -#ifndef VPM_VIDEO_DECIMATOR_H -#define VPM_VIDEO_DECIMATOR_H - -#include "typedefs.h" -#include "module_common_types.h" - -namespace webrtc { - -class VPMVideoDecimator -{ -public: - VPMVideoDecimator(); - ~VPMVideoDecimator(); - - void Reset(); - - void EnableTemporalDecimation(bool enable); - - WebRtc_Word32 SetMaxFrameRate(WebRtc_UWord32 maxFrameRate); - WebRtc_Word32 SetTargetFrameRate(WebRtc_UWord32 frameRate); - - bool DropFrame(); - - void UpdateIncomingFrameRate(); - - // Get Decimated Frame Rate/Dimensions - WebRtc_UWord32 DecimatedFrameRate(); - - //Get input frame rate - WebRtc_UWord32 InputFrameRate(); - -private: - void ProcessIncomingFrameRate(WebRtc_Word64 now); - - enum { kFrameCountHistorySize = 90}; - enum { kFrameHistoryWindowMs = 2000}; - - // Temporal decimation - WebRtc_Word32 _overShootModifier; - WebRtc_UWord32 _dropCount; - WebRtc_UWord32 _keepCount; - WebRtc_UWord32 _targetFrameRate; - float _incomingFrameRate; - WebRtc_UWord32 _maxFrameRate; - WebRtc_Word64 _incomingFrameTimes[kFrameCountHistorySize]; - bool _enableTemporalDecimation; - -}; - -} //namespace - -#endif diff --git a/modules/video_processing/main/source/video_processing.gyp b/modules/video_processing/main/source/video_processing.gyp deleted file mode 100644 index bdda16b2e..000000000 --- a/modules/video_processing/main/source/video_processing.gyp +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'video_processing', - 'type': '<(library)', - 'dependencies': [ - '../../../../common_audio/signal_processing_library/main/source/spl.gyp:spl', - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - ], - 'include_dirs': [ - '../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - ], - }, - 'sources': [ - # interfaces - '../interface/video_processing.h', - '../interface/video_processing_defines.h', - - # headers - 'video_processing_impl.h', - 'brightness_detection.h', - 'color_enhancement.h', - 'color_enhancement_private.h', - 'content_analysis.h', - 'deflickering.h', - 'denoising.h', - 'frame_preprocessor.h', - 'spatial_resampler.h', - 'video_decimator.h', - - # sources - 'video_processing_impl.cc', - 'brightness_detection.cc', - 'color_enhancement.cc', - 'content_analysis.cc', - 'deflickering.cc', - 'denoising.cc', - 'frame_preprocessor.cc', - 'spatial_resampler.cc', - 'video_decimator.cc', - ], # source - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_processing/main/source/video_processing_impl.cc b/modules/video_processing/main/source/video_processing_impl.cc deleted file mode 100644 index ccb6b0301..000000000 --- a/modules/video_processing/main/source/video_processing_impl.cc +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_processing_impl.h" -#include "critical_section_wrapper.h" -#include "trace.h" - -#include - -namespace webrtc { - -namespace -{ - void - SetSubSampling(VideoProcessingModule::FrameStats& stats, - const WebRtc_Word32 width, - const WebRtc_Word32 height) - { - if (width * height >= 640 * 480) - { - stats.subSamplWidth = 3; - stats.subSamplHeight = 3; - } - else if (width * height >= 352 * 288) - { - stats.subSamplWidth = 2; - stats.subSamplHeight = 2; - } - else if (width * height >= 176 * 144) - { - stats.subSamplWidth = 1; - stats.subSamplHeight = 1; - } - else - { - stats.subSamplWidth = 0; - stats.subSamplHeight = 0; - } - } -} - -VideoProcessingModule* -VideoProcessingModule::Create(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoPreocessing, id, - "VideoProcessingModule::Create()"); - - return new VideoProcessingModuleImpl(id); -} - -void -VideoProcessingModule::Destroy(VideoProcessingModule* module) -{ - if (module) - { - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoPreocessing, - static_cast(module)->Id(), - "VideoProcessingModule::destroy()"); - delete static_cast(module); - } -} - -WebRtc_Word32 -VideoProcessingModuleImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped mutex(_mutex); - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoPreocessing, _id, "ChangeUniqueId(new id:%d)", id); - _id = id; - _brightnessDetection.ChangeUniqueId(id); - _deflickering.ChangeUniqueId(id); - _denoising.ChangeUniqueId(id); - _framePreProcessor.ChangeUniqueId(id); - return VPM_OK; -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - CriticalSectionScoped mutex(_mutex); - WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceVideoPreocessing, _id, "Version(bufferLength:%d)", - remainingBufferInBytes); - return GetVersion(version, remainingBufferInBytes, position); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Id() const -{ - CriticalSectionScoped mutex(_mutex); - return _id; -} - -WebRtc_Word32 -VideoProcessingModule::GetVersion(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) -{ - if (version == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, - "Null version pointer"); - return -1; - } - WebRtc_Word8 ourVersion[] = "VideoProcessingModule 1.1.0"; - WebRtc_UWord32 ourLength = (WebRtc_UWord32)sizeof(ourVersion); // Includes null termination. - if (remainingBufferInBytes < ourLength) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, - "Buffer of insufficient length"); - return VPM_PARAMETER_ERROR; - } - memcpy(&version[position], ourVersion, ourLength); - remainingBufferInBytes -= ourLength; - position += ourLength; - - return VPM_OK; -} - -VideoProcessingModuleImpl::VideoProcessingModuleImpl(const WebRtc_Word32 id) : - _id(id), - _mutex(*CriticalSectionWrapper::CreateCriticalSection()) -{ - _brightnessDetection.ChangeUniqueId(id); - _deflickering.ChangeUniqueId(id); - _denoising.ChangeUniqueId(id); - _framePreProcessor.ChangeUniqueId(id); - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id, "Created"); -} - - -VideoProcessingModuleImpl::~VideoProcessingModuleImpl() -{ - WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideoPreocessing, _id, "Destroyed"); - - delete &_mutex; -} - -void -VideoProcessingModuleImpl::Reset() -{ - CriticalSectionScoped mutex(_mutex); - _deflickering.Reset(); - _denoising.Reset(); - _brightnessDetection.Reset(); - _framePreProcessor.Reset(); - -} - -WebRtc_Word32 -VideoProcessingModule::GetFrameStats(FrameStats& stats, - const VideoFrame& frame) -{ - return GetFrameStats(stats, frame.Buffer(), frame.Width(), frame.Height()); -} - -WebRtc_Word32 -VideoProcessingModule::GetFrameStats(FrameStats& stats, - const WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - if (frame == NULL) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, "Null frame pointer"); - return VPM_PARAMETER_ERROR; - } - - if (width == 0 || height == 0) - { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, -1, "Invalid frame size"); - return VPM_PARAMETER_ERROR; - } - - ClearFrameStats(stats); // The histogram needs to be zeroed out. - SetSubSampling(stats, width, height); - - // Compute histogram and sum of frame - for (WebRtc_UWord32 i = 0; i < height; i += (1 << stats.subSamplHeight)) - { - WebRtc_Word32 k = i * width; - for (WebRtc_UWord32 j = 0; j < width; j += (1 << stats.subSamplWidth)) - { - stats.hist[frame[k + j]]++; - stats.sum += frame[k + j]; - } - } - - stats.numPixels = (width * height) / ((1 << stats.subSamplWidth) * (1 << stats.subSamplHeight)); - assert(stats.numPixels > 0); - - // Compute mean value of frame - stats.mean = stats.sum / stats.numPixels; - - return VPM_OK; -} - -bool -VideoProcessingModule::ValidFrameStats(const FrameStats& stats) -{ - if (stats.numPixels == 0) - { - return false; - } - - return true; -} - -void -VideoProcessingModule::ClearFrameStats(FrameStats& stats) -{ - stats.mean = 0; - stats.sum = 0; - stats.numPixels = 0; - stats.subSamplWidth = 0; - stats.subSamplHeight = 0; - memset(stats.hist, 0, sizeof(stats.hist)); -} - -WebRtc_Word32 -VideoProcessingModule::ColorEnhancement(VideoFrame& frame) -{ - return ColorEnhancement(frame.Buffer(), frame.Width(), frame.Height()); -} - -WebRtc_Word32 -VideoProcessingModule::ColorEnhancement(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - return VideoProcessing::ColorEnhancement(frame, width, height); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Deflickering(VideoFrame& frame, - FrameStats& stats) -{ - return Deflickering(frame.Buffer(), frame.Width(), frame.Height(), - frame.TimeStamp(), stats); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Deflickering(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height, - const WebRtc_UWord32 timestamp, - FrameStats& stats) -{ - CriticalSectionScoped mutex(_mutex); - return _deflickering.ProcessFrame(frame, width, height, timestamp, stats); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Denoising(VideoFrame& frame) -{ - return Denoising(frame.Buffer(), frame.Width(), frame.Height()); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::Denoising(WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height) -{ - CriticalSectionScoped mutex(_mutex); - return _denoising.ProcessFrame(frame, width, height); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::BrightnessDetection(const VideoFrame& frame, - const FrameStats& stats) -{ - return BrightnessDetection(frame.Buffer(), frame.Width(), frame.Height(), stats); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::BrightnessDetection(const WebRtc_UWord8* frame, - const WebRtc_UWord32 width, - const WebRtc_UWord32 height, - const FrameStats& stats) -{ - CriticalSectionScoped mutex(_mutex); - return _brightnessDetection.ProcessFrame(frame, width, height, stats); -} - - -void -VideoProcessingModuleImpl::EnableTemporalDecimation(bool enable) -{ - CriticalSectionScoped mutex(_mutex); - _framePreProcessor.EnableTemporalDecimation(enable); -} - - -void -VideoProcessingModuleImpl::SetInputFrameResampleMode(VideoFrameResampling resamplingMode) -{ - CriticalSectionScoped cs(_mutex); - _framePreProcessor.SetInputFrameResampleMode(resamplingMode); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::SetMaxFrameRate(WebRtc_UWord32 maxFrameRate) -{ - CriticalSectionScoped cs(_mutex); - return _framePreProcessor.SetMaxFrameRate(maxFrameRate); - -} - -WebRtc_Word32 -VideoProcessingModuleImpl::SetTargetResolution(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32 frameRate) -{ - CriticalSectionScoped cs(_mutex); - return _framePreProcessor.SetTargetResolution(width, height, frameRate); -} - - -WebRtc_UWord32 -VideoProcessingModuleImpl::DecimatedFrameRate() -{ - CriticalSectionScoped cs(_mutex); - return _framePreProcessor.DecimatedFrameRate(); -} - - -WebRtc_UWord32 -VideoProcessingModuleImpl::DecimatedWidth() const -{ - CriticalSectionScoped cs(_mutex); - return _framePreProcessor.DecimatedWidth(); -} - -WebRtc_UWord32 -VideoProcessingModuleImpl::DecimatedHeight() const -{ - CriticalSectionScoped cs(_mutex); - return _framePreProcessor.DecimatedHeight(); -} - -WebRtc_Word32 -VideoProcessingModuleImpl::PreprocessFrame(const VideoFrame *frame, VideoFrame **processedFrame) -{ - CriticalSectionScoped mutex(_mutex); - return _framePreProcessor.PreprocessFrame(frame, processedFrame); -} - -VideoContentMetrics* -VideoProcessingModuleImpl::ContentMetrics() const -{ - CriticalSectionScoped mutex(_mutex); - return _framePreProcessor.ContentMetrics(); -} - - -void -VideoProcessingModuleImpl::EnableContentAnalysis(bool enable) -{ - CriticalSectionScoped mutex(_mutex); - _framePreProcessor.EnableContentAnalysis(enable); -} - -} //namespace diff --git a/modules/video_processing/main/source/video_processing_impl.h b/modules/video_processing/main/source/video_processing_impl.h deleted file mode 100644 index 6b8994236..000000000 --- a/modules/video_processing/main/source/video_processing_impl.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * video_processing_impl.h - */ -#ifndef WEBRTC_MODULE_VIDEO_PROCESSING_IMPL_H -#define WEBRTC_MODULE_VIDEO_PROCESSING_IMPL_H - -#include "video_processing.h" -#include "brightness_detection.h" -#include "color_enhancement.h" -#include "deflickering.h" -#include "denoising.h" -#include "frame_preprocessor.h" - -namespace webrtc { -class CriticalSectionWrapper; - -class VideoProcessingModuleImpl : public VideoProcessingModule -{ -public: - - VideoProcessingModuleImpl(WebRtc_Word32 id); - - virtual ~VideoProcessingModuleImpl(); - - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - WebRtc_Word32 Id() const; - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual void Reset(); - - virtual WebRtc_Word32 Deflickering(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - WebRtc_UWord32 timestamp, - FrameStats& stats); - - virtual WebRtc_Word32 Deflickering(VideoFrame& frame, - FrameStats& stats); - - virtual WebRtc_Word32 Denoising(WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height); - - virtual WebRtc_Word32 Denoising(VideoFrame& frame); - - virtual WebRtc_Word32 BrightnessDetection(const WebRtc_UWord8* frame, - WebRtc_UWord32 width, - WebRtc_UWord32 height, - const FrameStats& stats); - - virtual WebRtc_Word32 BrightnessDetection(const VideoFrame& frame, - const FrameStats& stats); - - - //Frame pre-processor functions - - //Enable temporal decimation - virtual void EnableTemporalDecimation(bool enable); - - virtual void SetInputFrameResampleMode(VideoFrameResampling resamplingMode); - - //Enable content analysis - virtual void EnableContentAnalysis(bool enable); - - //Set max frame rate - virtual WebRtc_Word32 SetMaxFrameRate(WebRtc_UWord32 maxFrameRate); - - // Set Target Resolution: frame rate and dimension - virtual WebRtc_Word32 SetTargetResolution(WebRtc_UWord32 width, WebRtc_UWord32 height, WebRtc_UWord32 frameRate); - - // Get decimated values: frame rate/dimension - virtual WebRtc_UWord32 DecimatedFrameRate(); - virtual WebRtc_UWord32 DecimatedWidth() const; - virtual WebRtc_UWord32 DecimatedHeight() const; - - // Preprocess: - virtual WebRtc_Word32 PreprocessFrame(const VideoFrame* frame, VideoFrame** processedFrame); - virtual VideoContentMetrics* ContentMetrics() const; - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _mutex; - - VPMDeflickering _deflickering; - VPMDenoising _denoising; - VPMBrightnessDetection _brightnessDetection; - VPMFramePreprocessor _framePreProcessor; -}; - -} // namespace - -#endif diff --git a/modules/video_processing/main/test/unit_test/brightness_detection_test.cc b/modules/video_processing/main/test/unit_test/brightness_detection_test.cc deleted file mode 100644 index 6510a5c4c..000000000 --- a/modules/video_processing/main/test/unit_test/brightness_detection_test.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "video_processing.h" - -using namespace webrtc; - -TEST_F(VideoProcessingModuleTest, BrightnessDetection) -{ - WebRtc_UWord32 frameNum = 0; - WebRtc_Word32 brightnessWarning = 0; - WebRtc_UWord32 warningCount = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength) - { - frameNum++; - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame, stats), 0); - if (brightnessWarning != VideoProcessingModule::kNoWarning) - { - warningCount++; - } - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - // Expect few warnings - float warningProportion = static_cast(warningCount) / frameNum * 100; - printf("\nWarning proportions:\n"); - printf("Stock foreman: %.1f %%\n", warningProportion); - EXPECT_LT(warningProportion, 10); - - rewind(_sourceFile); - frameNum = 0; - warningCount = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength && - frameNum < 300) - { - frameNum++; - - WebRtc_UWord8* frame = _videoFrame.Buffer(); - WebRtc_UWord32 yTmp = 0; - for (WebRtc_UWord32 yIdx = 0; yIdx < _width * _height; yIdx++) - { - yTmp = frame[yIdx] << 1; - if (yTmp > 255) - { - yTmp = 255; - } - frame[yIdx] = static_cast(yTmp); - } - - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame, stats), 0); - EXPECT_NE(VideoProcessingModule::kDarkWarning, brightnessWarning); - if (brightnessWarning == VideoProcessingModule::kBrightWarning) - { - warningCount++; - } - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - // Expect many brightness warnings - warningProportion = static_cast(warningCount) / frameNum * 100; - printf("Bright foreman: %.1f %%\n", warningProportion); - EXPECT_GT(warningProportion, 95); - - rewind(_sourceFile); - frameNum = 0; - warningCount = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength && - frameNum < 300) - { - frameNum++; - - WebRtc_UWord8* frame = _videoFrame.Buffer(); - WebRtc_Word32 yTmp = 0; - for (WebRtc_UWord32 yIdx = 0; yIdx < _width * _height; yIdx++) - { - yTmp = frame[yIdx] >> 1; - frame[yIdx] = static_cast(yTmp); - } - - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - ASSERT_GE(brightnessWarning = _vpm->BrightnessDetection(_videoFrame, stats), 0); - EXPECT_NE(VideoProcessingModule::kBrightWarning, brightnessWarning); - if (brightnessWarning == VideoProcessingModule::kDarkWarning) - { - warningCount++; - } - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - // Expect many darkness warnings - warningProportion = static_cast(warningCount) / frameNum * 100; - printf("Dark foreman: %.1f %%\n\n", warningProportion); - EXPECT_GT(warningProportion, 90); -} diff --git a/modules/video_processing/main/test/unit_test/color_enhancement_test.cc b/modules/video_processing/main/test/unit_test/color_enhancement_test.cc deleted file mode 100644 index 5572284d1..000000000 --- a/modules/video_processing/main/test/unit_test/color_enhancement_test.cc +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "video_processing.h" -#include "vplib.h" -#include "tick_util.h" - -#include -#include - -using namespace webrtc; - -TEST_F(VideoProcessingModuleTest, ColorEnhancement) -{ - TickTime t0; - TickTime t1; - TickInterval accTicks; - - FILE* modFile = fopen("foremanColorEnhancedVPM.yuv", "w+b"); - ASSERT_TRUE(modFile != NULL) << "Could not open output file.\n"; - - WebRtc_UWord32 frameNum = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength) - { - frameNum++; - t0 = TickTime::Now(); - ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(_videoFrame)); - t1 = TickTime::Now(); - accTicks += t1 - t0; - fwrite(_videoFrame.Buffer(), 1, _frameLength, modFile); - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - printf("\nTime per frame: %d us \n", - static_cast(accTicks.Microseconds() / frameNum)); - rewind(modFile); - - printf("Comparing files...\n\n"); - FILE* refFile = fopen("foremanColorEnhanced.yuv", "rb"); - ASSERT_TRUE(refFile != NULL) << "Cannot open reference file foremanColorEnhanced.yuv\n" - "Create the reference by running Matlab script createTable.m."; - - // get file lenghts - ASSERT_EQ(0, fseek(refFile, 0L, SEEK_END)); - long refLen = ftell(refFile); - ASSERT_NE(-1L, refLen); - rewind(refFile); - ASSERT_EQ(0, fseek(modFile, 0L, SEEK_END)); - long testLen = ftell(modFile); - ASSERT_NE(-1L, testLen); - rewind(modFile); - ASSERT_EQ(refLen, testLen) << "File lengths differ."; - - VideoFrame refVideoFrame; - refVideoFrame.VerifyAndAllocate(_frameLength); - refVideoFrame.SetWidth(_width); - refVideoFrame.SetHeight(_height); - - // Compare frame-by-frame. - while (fread(_videoFrame.Buffer(), 1, _frameLength, modFile) == _frameLength) - { - ASSERT_EQ(_frameLength, fread(refVideoFrame.Buffer(), 1, _frameLength, refFile)); - EXPECT_EQ(0, memcmp(_videoFrame.Buffer(), refVideoFrame.Buffer(), _frameLength)); - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - // Verify that all color pixels are enhanced, that no luminance values are altered, - // and that the function does not write outside the vector. - WebRtc_UWord32 safeGuard = 1000; - WebRtc_UWord32 numPixels = 352*288; // CIF size - WebRtc_UWord8 *testFrame = new WebRtc_UWord8[numPixels + (numPixels / 2) + (2 * safeGuard)]; - WebRtc_UWord8 *refFrame = new WebRtc_UWord8[numPixels + (numPixels / 2) + (2 * safeGuard)]; - - // use value 128 as probe value, since we know that this will be changed in the enhancement - memset(testFrame, 128, safeGuard); - memset(&testFrame[safeGuard], 128, numPixels); - memset(&testFrame[safeGuard + numPixels], 128, numPixels / 2); - memset(&testFrame[safeGuard + numPixels + (numPixels / 2)], 128, safeGuard); - - memcpy(refFrame, testFrame, numPixels + (numPixels / 2) + (2 * safeGuard)); - - ASSERT_EQ(0, VideoProcessingModule::ColorEnhancement(&testFrame[safeGuard], 352, 288)); - - EXPECT_EQ(0, memcmp(testFrame, refFrame, safeGuard)) << - "Function is writing outside the frame memory."; - - EXPECT_EQ(0, memcmp(&testFrame[safeGuard + numPixels + (numPixels / 2)], - &refFrame[safeGuard + numPixels + (numPixels / 2)], safeGuard)) << - "Function is writing outside the frame memory."; - - EXPECT_EQ(0, memcmp(&testFrame[safeGuard], &refFrame[safeGuard], numPixels)) << - "Function is modifying the luminance."; - - EXPECT_NE(0, memcmp(&testFrame[safeGuard + numPixels], - &refFrame[safeGuard + numPixels], numPixels / 2)) << - "Function is not modifying all chrominance pixels"; - - delete [] testFrame; - delete [] refFrame; -} - diff --git a/modules/video_processing/main/test/unit_test/createTable.m b/modules/video_processing/main/test/unit_test/createTable.m deleted file mode 100644 index 2c7fb522f..000000000 --- a/modules/video_processing/main/test/unit_test/createTable.m +++ /dev/null @@ -1,179 +0,0 @@ -% Create the color enhancement look-up table and write it to -% file colorEnhancementTable.cpp. Copy contents of that file into -% the source file for the color enhancement function. - -clear -close all - - -% First, define the color enhancement in a normalized domain - -% Compander function is defined in three radial zones. -% 1. From 0 to radius r0, the compander function -% is a second-order polynomial intersecting the points (0,0) -% and (r0, r0), and with a slope B in (0,0). -% 2. From r0 to r1, the compander is a third-order polynomial -% intersecting the points (r0, r0) and (r1, r1), and with the -% same slope as the first part in the point (r0, r0) and slope -% equal to 1 in (r1, r1). -% 3. For radii larger than r1, the compander function is the -% unity scale function (no scaling at all). - -r0=0.07; % Dead zone radius (must be > 0) -r1=0.6; % Enhancement zone radius (must be > r0 and < 1) -B=0.2; % initial slope of compander function (between 0 and 1) - -x0=linspace(0,r0).'; % zone 1 -x1=linspace(r0,r1).'; % zone 2 -x2=linspace(r1,1).'; % zone 3 - -A=(1-B)/r0; -f0=A*x0.^2+B*x0; % compander function in zone 1 - -% equation system for finding second zone parameters -M=[r0^3 r0^2 r0 1; - 3*r0^2 2*r0 1 0; - 3*r1^2 2*r1 1 0; - r1^3 r1^2 r1 1]; -m=[A*r0^2+B*r0; 2*A*r0+B; 1; r1]; -% solve equations -theta=M\m; - -% compander function in zone 1 -f1=[x1.^3 x1.^2 x1 ones(size(x1))]*theta; - -x=[x0; x1; x2]; -f=[f0; f1; x2]; - -% plot it -figure(1) -plot(x,f,x,x,':') -xlabel('Normalized radius') -ylabel('Modified radius') - - -% Now, create the look-up table in the integer color space -[U,V]=meshgrid(0:255, 0:255); % U-V space -U0=U; -V0=V; - -% Conversion matrix from normalized YUV to RGB -T=[1 0 1.13983; 1 -0.39465 -0.58060; 1 2.03211 0]; -Ylum=0.5; - -figure(2) -Z(:,:,1)=Ylum + (U-127)/256*T(1,2) + (V-127)/256*T(1,3); -Z(:,:,2)=Ylum + (U-127)/256*T(2,2) + (V-127)/256*T(2,3); -Z(:,:,3)=Ylum + (U-127)/256*T(3,2) + (V-127)/256*T(3,3); -Z=max(Z,0); -Z=min(Z,1); -subplot(121) -image(Z); -axis square -axis off -set(gcf,'color','k') - -R = sqrt((U-127).^2 + (V-127).^2); -Rnorm = R/127; -RnormMod = Rnorm; -RnormMod(RnormMod==0)=1; % avoid division with zero - -% find indices to pixels in dead-zone (zone 1) -ix=find(Rnorm<=r0); -scaleMatrix = (A*Rnorm(ix).^2 + B*Rnorm(ix))./RnormMod(ix); -U(ix)=(U(ix)-127).*scaleMatrix+127; -V(ix)=(V(ix)-127).*scaleMatrix+127; - -% find indices to pixels in zone 2 -ix=find(Rnorm>r0 & Rnorm<=r1); -scaleMatrix = (theta(1)*Rnorm(ix).^3 + theta(2)*Rnorm(ix).^2 + ... - theta(3)*Rnorm(ix) + theta(4)) ./ RnormMod(ix); -U(ix)=(U(ix)-127).*scaleMatrix + 127; -V(ix)=(V(ix)-127).*scaleMatrix + 127; - -% round to integer values and saturate -U=round(U); -V=round(V); -U=max(min(U,255),0); -V=max(min(V,255),0); - -Z(:,:,1)=Ylum + (U-127)/256*T(1,2) + (V-127)/256*T(1,3); -Z(:,:,2)=Ylum + (U-127)/256*T(2,2) + (V-127)/256*T(2,3); -Z(:,:,3)=Ylum + (U-127)/256*T(3,2) + (V-127)/256*T(3,3); -Z=max(Z,0); -Z=min(Z,1); -subplot(122) -image(Z); -axis square -axis off - -figure(3) -subplot(121) -mesh(U-U0) -subplot(122) -mesh(V-V0) - - - -% Last, write to file -% Write only one matrix, since U=V' - -fid = fopen('../out/Debug/colorEnhancementTable.h','wt'); -if fid==-1 - error('Cannot open file colorEnhancementTable.cpp'); -end - -fprintf(fid,'//colorEnhancementTable.h\n\n'); -fprintf(fid,'//Copy the constant table to the appropriate header file.\n\n'); - -fprintf(fid,'//Table created with Matlab script createTable.m\n\n'); -fprintf(fid,'//Usage:\n'); -fprintf(fid,'// Umod=colorTable[U][V]\n'); -fprintf(fid,'// Vmod=colorTable[V][U]\n'); - -fprintf(fid,'static unsigned char colorTable[%i][%i] = {\n', size(U,1), size(U,2)); - -for u=1:size(U,2) - fprintf(fid,' {%i', U(1,u)); - for v=2:size(U,1) - fprintf(fid,', %i', U(v,u)); - end - fprintf(fid,'}'); - if u -#include - -using namespace webrtc; - -TEST_F(VideoProcessingModuleTest, Deflickering) -{ - enum { NumRuns = 30 }; - WebRtc_UWord32 frameNum = 0; - const WebRtc_UWord32 frameRate = 15; - - WebRtc_Word64 minRuntime = 0; - WebRtc_Word64 avgRuntime = 0; - - // Close automatically opened Foreman. - fclose(_sourceFile); - _sourceFile = fopen("deflicker_testfile_before.yuv", "rb"); - ASSERT_TRUE(_sourceFile != NULL) << - "Cannot read input file deflicker_testfile_before.yuv\n"; - - FILE* deflickerFile = fopen("deflicker_testfile.yuv", "wb"); - ASSERT_TRUE(deflickerFile != NULL) << "Could not open output file.\n"; - - printf("\nRun time [us / frame]:\n"); - for (WebRtc_UWord32 runIdx = 0; runIdx < NumRuns; runIdx++) - { - TickTime t0; - TickTime t1; - TickInterval accTicks; - WebRtc_UWord32 timeStamp = 1; - - frameNum = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength) - { - frameNum++; - _videoFrame.SetTimeStamp(timeStamp); - - t0 = TickTime::Now(); - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - ASSERT_EQ(0, _vpm->Deflickering(_videoFrame, stats)); - t1 = TickTime::Now(); - accTicks += t1 - t0; - - if (runIdx == 0) - { - fwrite(_videoFrame.Buffer(), 1, _frameLength, deflickerFile); - } - timeStamp += (90000 / frameRate); - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - printf("%u\n", static_cast(accTicks.Microseconds() / frameNum)); - if (accTicks.Microseconds() < minRuntime || runIdx == 0) - { - minRuntime = accTicks.Microseconds(); - } - avgRuntime += accTicks.Microseconds(); - - rewind(_sourceFile); - } - - printf("\nAverage run time = %d us / frame\n", - static_cast(avgRuntime / frameNum / NumRuns)); - printf("Min run time = %d us / frame\n\n", - static_cast(minRuntime / frameNum)); -} diff --git a/modules/video_processing/main/test/unit_test/denoising_test.cc b/modules/video_processing/main/test/unit_test/denoising_test.cc deleted file mode 100644 index 0dc53ed78..000000000 --- a/modules/video_processing/main/test/unit_test/denoising_test.cc +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "video_processing.h" - -#include "tick_util.h" - -#include -#include - -using namespace webrtc; - -TEST_F(VideoProcessingModuleTest, Denoising) -{ - enum { NumRuns = 10 }; - WebRtc_UWord32 frameNum = 0; - - WebRtc_Word64 minRuntime = 0; - WebRtc_Word64 avgRuntime = 0; - - FILE* denoiseFile = fopen("denoise_testfile.yuv", "wb"); - ASSERT_TRUE(denoiseFile != NULL) << "Could not open output file.\n"; - - FILE* noiseFile = fopen("noise_testfile.yuv", "wb"); - ASSERT_TRUE(noiseFile != NULL) << "Could not open noisy file.\n"; - - printf("\nRun time [us / frame]:\n"); - for (WebRtc_UWord32 runIdx = 0; runIdx < NumRuns; runIdx++) - { - TickTime t0; - TickTime t1; - TickInterval accTicks; - WebRtc_Word32 modifiedPixels = 0; - - frameNum = 0; - while (fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile) == _frameLength) - { - frameNum++; - WebRtc_UWord8* sourceBuffer = _videoFrame.Buffer(); - - // Add noise to a part in video stream - // Random noise - // TODO: investigate the effectiveness of this test. - - //for(WebRtc_UWord32 ir = 0; ir < _frameLength; ir++) - // sourceBuffer[ir] = 128 - for (WebRtc_UWord32 ir = 0; ir < _height; ir++) - { - WebRtc_UWord32 ik = ir * _width; - for (WebRtc_UWord32 ic = 0; ic < _width; ic++) - { - WebRtc_UWord8 r = rand() % 16; - r -= 8; - if (ir < _height / 4) - r = 0; - if (ir >= 3 * _height / 4) - r = 0; - if (ic < _width / 4) - r = 0; - if (ic >= 3 * _width / 4) - r = 0; - - /*WebRtc_UWord8 pixelValue = 0; - if (ir >= _height / 2) - { // Region 3 or 4 - pixelValue = 170; - } - if (ic >= _width / 2) - { // Region 2 or 4 - pixelValue += 85; - } - pixelValue += r; - sourceBuffer[ik + ic] = pixelValue; - */ - sourceBuffer[ik + ic] += r; - } - } - - if (runIdx == 0) - { - fwrite(_videoFrame.Buffer(), 1, _frameLength, noiseFile); - } - - t0 = TickTime::Now(); - ASSERT_GE(modifiedPixels = _vpm->Denoising(_videoFrame), 0); - t1 = TickTime::Now(); - accTicks += t1 - t0; - - if (runIdx == 0) - { - fwrite(_videoFrame.Buffer(), 1, _frameLength, denoiseFile); - } - } - ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file"; - - printf("%u\n", static_cast(accTicks.Microseconds() / frameNum)); - if (accTicks.Microseconds() < minRuntime || runIdx == 0) - { - minRuntime = accTicks.Microseconds(); - } - avgRuntime += accTicks.Microseconds(); - - rewind(_sourceFile); - } - - printf("\nAverage run time = %d us / frame\n", - static_cast(avgRuntime / frameNum / NumRuns)); - printf("Min run time = %d us / frame\n\n", - static_cast(minRuntime / frameNum)); -} diff --git a/modules/video_processing/main/test/unit_test/readYUV420file.m b/modules/video_processing/main/test/unit_test/readYUV420file.m deleted file mode 100644 index 03013efd3..000000000 --- a/modules/video_processing/main/test/unit_test/readYUV420file.m +++ /dev/null @@ -1,45 +0,0 @@ -function [Y,U,V] = readYUV420file(filename, width, height) -% [Y,U,V] = readYUVfile(filename, width, height) - -fid = fopen(filename,'rb'); -if fid==-1 - error(['Cannot open file ' filename]); -end - -% Number of pixels per image -nPx=width*height; - -% nPx bytes luminance, nPx/4 bytes U, nPx/4 bytes V -frameSizeBytes = nPx*1.5; - -% calculate number of frames -fseek(fid,0,'eof'); % move to end of file -fileLen=ftell(fid); % number of bytes -fseek(fid,0,'bof'); % rewind to start - -% calculate number of frames -numFrames = floor(fileLen/frameSizeBytes); - -Y=uint8(zeros(height,width,numFrames)); -U=uint8(zeros(height/2,width/2,numFrames)); -V=uint8(zeros(height/2,width/2,numFrames)); - -[X,nBytes]=fread(fid, frameSizeBytes, 'uchar'); - -for k=1:numFrames - - % Store luminance - Y(:,:,k)=uint8(reshape(X(1:nPx), width, height).'); - - % Store U channel - U(:,:,k)=uint8(reshape(X(nPx + (1:nPx/4)), width/2, height/2).'); - - % Store V channel - V(:,:,k)=uint8(reshape(X(nPx + nPx/4 + (1:nPx/4)), width/2, height/2).'); - - % Read next frame - [X,nBytes]=fread(fid, frameSizeBytes, 'uchar'); -end - - -fclose(fid); diff --git a/modules/video_processing/main/test/unit_test/unit_test.cc b/modules/video_processing/main/test/unit_test/unit_test.cc deleted file mode 100644 index c1b83dd51..000000000 --- a/modules/video_processing/main/test/unit_test/unit_test.cc +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "unit_test.h" -#include "trace.h" -#include "tick_util.h" - -#ifdef WIN32 -#include // Visual Leak Detector -#endif - -#ifdef WIN32 -#pragma message("Using Google C++ Testing Framework") -#endif - -using namespace webrtc; - -void TestSize(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight, - WebRtc_UWord32 mode, VideoProcessingModule *vpm); - -class VPMEnvironment : public ::testing::Environment -{ -public: - virtual void SetUp() - { - Trace::CreateTrace(); - ASSERT_EQ(0, Trace::SetTraceFile("VPMTrace.txt")); - } - - virtual void TearDown() - { - Trace::ReturnTrace(); - } -}; -VideoProcessingModuleTest::VideoProcessingModuleTest() : - _vpm(NULL), - _sourceFile(NULL), - _width(352), - _height(288), - _frameLength(CalcBufferSize(kI420, 352, 288)) -{ -} - -void VideoProcessingModuleTest::SetUp() -{ - _vpm = VideoProcessingModule::Create(0); - ASSERT_TRUE(_vpm != NULL); - - ASSERT_EQ(0, _videoFrame.VerifyAndAllocate(_frameLength)); - _videoFrame.SetWidth(_width); - _videoFrame.SetHeight(_height); - - _sourceFile = fopen("testFiles/foreman_cif.yuv","rb"); - ASSERT_TRUE(_sourceFile != NULL) << - "Cannot read source file: testFiles/foreman_cif.yuv\n"; -} - -void VideoProcessingModuleTest::TearDown() -{ - if (_sourceFile != NULL) - { - ASSERT_EQ(0, fclose(_sourceFile)); - } - _sourceFile = NULL; - - if (_vpm != NULL) - { - VideoProcessingModule::Destroy(_vpm); - } - _vpm = NULL; -} - -TEST_F(VideoProcessingModuleTest, GetVersionTest) -{ - WebRtc_Word8 version[255]; - WebRtc_UWord32 remainingBuffer = sizeof(version); - WebRtc_UWord32 position = 0; - - EXPECT_EQ(-1, VideoProcessingModule::GetVersion(NULL, remainingBuffer, position)); - EXPECT_EQ(-1, _vpm->GetVersion(NULL, remainingBuffer, position)); - - WebRtc_UWord32 badRemainingBuffer = 5; - EXPECT_EQ(-3, VideoProcessingModule::GetVersion(version, badRemainingBuffer, position)); - EXPECT_EQ(-3, _vpm->GetVersion(version, badRemainingBuffer, position)); - - EXPECT_EQ(0, VideoProcessingModule::GetVersion(version, remainingBuffer, position)); - EXPECT_EQ(remainingBuffer, sizeof(version) - position); - printf("\n%s\n\n", version); - - EXPECT_EQ(0, _vpm->GetVersion(&version[position], remainingBuffer, position)); - EXPECT_EQ(remainingBuffer, sizeof(version) - position); -} - -TEST_F(VideoProcessingModuleTest, HandleNullBuffer) -{ - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - // Video frame with unallocated buffer. - VideoFrame videoFrame; - videoFrame.SetWidth(_width); - videoFrame.SetHeight(_height); - - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, NULL, _width, _height)); - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, videoFrame)); - - EXPECT_EQ(-1, _vpm->ColorEnhancement(NULL, _width, _height)); - EXPECT_EQ(-1, _vpm->ColorEnhancement(videoFrame)); - - EXPECT_EQ(-1, _vpm->Deflickering(NULL, _width, _height, 0, stats)); - EXPECT_EQ(-1, _vpm->Deflickering(videoFrame, stats)); - - EXPECT_EQ(-1, _vpm->Denoising(NULL, _width, _height)); - EXPECT_EQ(-1, _vpm->Denoising(videoFrame)); - - EXPECT_EQ(-3, _vpm->BrightnessDetection(NULL, _width, _height, stats)); - EXPECT_EQ(-3, _vpm->BrightnessDetection(videoFrame, stats)); - - EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->PreprocessFrame(NULL, NULL)); - -} - -TEST_F(VideoProcessingModuleTest, HandleBadStats) -{ - VideoProcessingModule::FrameStats stats; - - ASSERT_EQ(_frameLength, fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile)); - - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame.Buffer(), _width, _height, 0, stats)); - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame, stats)); - - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame.Buffer(), _width, _height, stats)); - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats)); -} - -TEST_F(VideoProcessingModuleTest, HandleBadSize) -{ - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - - // Bad width - _videoFrame.SetWidth(0); - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, _videoFrame.Buffer(), 0, _height)); - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, _videoFrame)); - - EXPECT_EQ(-1, _vpm->ColorEnhancement(_videoFrame.Buffer(), 0, _height)); - EXPECT_EQ(-1, _vpm->ColorEnhancement(_videoFrame)); - - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame.Buffer(), 0, _height, 0, stats)); - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame, stats)); - - EXPECT_EQ(-1, _vpm->Denoising(_videoFrame.Buffer(), 0, _height)); - EXPECT_EQ(-1, _vpm->Denoising(_videoFrame)); - - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame.Buffer(), 0, _height, stats)); - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats)); - - - // Bad height - _videoFrame.SetWidth(_width); - _videoFrame.SetHeight(0); - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, _videoFrame.Buffer(), _width, 0)); - EXPECT_EQ(-3, _vpm->GetFrameStats(stats, _videoFrame)); - - EXPECT_EQ(-1, _vpm->ColorEnhancement(_videoFrame.Buffer(), _width, 0)); - EXPECT_EQ(-1, _vpm->ColorEnhancement(_videoFrame)); - - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame.Buffer(), _width, 0, 0, stats)); - EXPECT_EQ(-1, _vpm->Deflickering(_videoFrame, stats)); - - EXPECT_EQ(-1, _vpm->Denoising(_videoFrame.Buffer(), _width, 0)); - EXPECT_EQ(-1, _vpm->Denoising(_videoFrame)); - - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame.Buffer(), _width, 0, stats)); - EXPECT_EQ(-3, _vpm->BrightnessDetection(_videoFrame, stats)); - - EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetTargetResolution(0,0,0)); - EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->SetMaxFrameRate(0)); - - VideoFrame *outFrame = NULL; - EXPECT_EQ(VPM_PARAMETER_ERROR, _vpm->PreprocessFrame(&_videoFrame, &outFrame)); - -} - -TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset) -{ - VideoFrame videoFrame2; - VideoProcessingModule::FrameStats stats; - - ASSERT_EQ(0, videoFrame2.VerifyAndAllocate(_frameLength)); - videoFrame2.SetWidth(_width); - videoFrame2.SetHeight(_height); - - // Only testing non-static functions here. - ASSERT_EQ(_frameLength, fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile)); - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - memcpy(videoFrame2.Buffer(), _videoFrame.Buffer(), _frameLength); - ASSERT_EQ(0, _vpm->Deflickering(_videoFrame, stats)); - _vpm->Reset(); - // Retrieve frame stats again in case Deflickering() has zeroed them. - ASSERT_EQ(0, _vpm->GetFrameStats(stats, videoFrame2)); - ASSERT_EQ(0, _vpm->Deflickering(videoFrame2, stats)); - EXPECT_EQ(0, memcmp(_videoFrame.Buffer(), videoFrame2.Buffer(), _frameLength)); - - ASSERT_EQ(_frameLength, fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile)); - memcpy(videoFrame2.Buffer(), _videoFrame.Buffer(), _frameLength); - ASSERT_GE(_vpm->Denoising(_videoFrame), 0); - _vpm->Reset(); - ASSERT_GE(_vpm->Denoising(videoFrame2), 0); - EXPECT_EQ(0, memcmp(_videoFrame.Buffer(), videoFrame2.Buffer(), _frameLength)); - - ASSERT_EQ(_frameLength, fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile)); - ASSERT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - memcpy(videoFrame2.Buffer(), _videoFrame.Buffer(), _frameLength); - ASSERT_EQ(0, _vpm->BrightnessDetection(_videoFrame, stats)); - _vpm->Reset(); - ASSERT_EQ(0, _vpm->BrightnessDetection(videoFrame2, stats)); - EXPECT_EQ(0, memcmp(_videoFrame.Buffer(), videoFrame2.Buffer(), _frameLength)); - -} - -TEST_F(VideoProcessingModuleTest, FrameStats) -{ - VideoProcessingModule::FrameStats stats; - ASSERT_EQ(_frameLength, fread(_videoFrame.Buffer(), 1, _frameLength, _sourceFile)); - - EXPECT_EQ(false, _vpm->ValidFrameStats(stats)); - EXPECT_EQ(0, _vpm->GetFrameStats(stats, _videoFrame)); - EXPECT_EQ(true, _vpm->ValidFrameStats(stats)); - - printf("\nFrameStats\n"); - printf("mean: %u\nnumPixels: %u\nsubSamplWidth: %u\nsumSamplHeight: %u\nsum: %u\n\n", - static_cast(stats.mean), - static_cast(stats.numPixels), - static_cast(stats.subSamplHeight), - static_cast(stats.subSamplWidth), - static_cast(stats.sum)); - - _vpm->ClearFrameStats(stats); - EXPECT_EQ(false, _vpm->ValidFrameStats(stats)); -} - -TEST_F(VideoProcessingModuleTest, PreprocessorLogic) -{ - // disable temporal sampling - _vpm->EnableTemporalDecimation(false); - ASSERT_EQ(VPM_OK, _vpm->SetMaxFrameRate(30)); - ASSERT_EQ(VPM_OK, _vpm->SetTargetResolution(100, 100, 15)); - //revert - _vpm->EnableTemporalDecimation(true); - ASSERT_EQ(VPM_OK, _vpm->SetTargetResolution(100, 100, 30)); - // disable spatial sampling - _vpm->SetInputFrameResampleMode(kNoRescaling); - ASSERT_EQ(VPM_OK, _vpm->SetTargetResolution(100, 100, 30)); - VideoFrame *outFrame = NULL; - ASSERT_EQ(VPM_OK, _vpm->PreprocessFrame(&_videoFrame, &outFrame)); - ASSERT_EQ(_videoFrame.Height() , outFrame->Height()); - ASSERT_EQ(_videoFrame.Width() , outFrame->Width()); - -} - -TEST_F(VideoProcessingModuleTest, Resampler) -{ - enum { NumRuns = 1 }; - WebRtc_UWord32 frameNum = 0; - - WebRtc_Word64 minRuntime = 0; - WebRtc_Word64 avgRuntime = 0; - - TickTime t0; - TickTime t1; - TickInterval accTicks; - WebRtc_Word32 modifiedPixels = 0; - WebRtc_Word32 height = 288; - WebRtc_Word32 width = 352; - WebRtc_Word32 lengthSourceFrame = width*height*3/2; - - rewind(_sourceFile); - ASSERT_TRUE(_sourceFile != NULL) << - "Cannot read input file \n"; - - // CA not needed here - _vpm->EnableContentAnalysis(false); - // no temporal decimation - _vpm->EnableTemporalDecimation(false); - - // reading test frame - VideoFrame sourceFrame; - ASSERT_EQ(0, sourceFrame.VerifyAndAllocate(lengthSourceFrame)); - fread(sourceFrame.Buffer(), 1, lengthSourceFrame, _sourceFile); - ASSERT_EQ(0, sourceFrame.SetLength(lengthSourceFrame)); - sourceFrame.SetHeight(height); - sourceFrame.SetWidth(width); - - for (WebRtc_UWord32 runIdx = 0; runIdx < NumRuns; runIdx++) - { - // initiate test timer - t0 = TickTime::Now(); - - // kFastRescaling - _vpm->SetInputFrameResampleMode(kFastRescaling); - //TESTING DIFFERENT SIZES - TestSize(sourceFrame, 100, 50, 1, _vpm); // Cut, decimation 1x, interpolate - TestSize(sourceFrame, 352/2, 288/2, 1, _vpm); // Even decimation - TestSize(sourceFrame, 352, 288, 1, _vpm); // No resampling - TestSize(sourceFrame, 2*352, 2*288,1, _vpm); // Even upsampling - TestSize(sourceFrame, 400, 256, 1, _vpm); // Upsampling 1.5x and cut - TestSize(sourceFrame, 960, 720, 1, _vpm); // Upsampling 3.5x and cut - TestSize(sourceFrame, 1280, 720, 1, _vpm); // Upsampling 4x and cut - - //kBiLinear - _vpm->SetInputFrameResampleMode(kBiLinear); - //TESTING DIFFERENT SIZES - TestSize(sourceFrame, 352/4, 288/4, 2, _vpm); - TestSize(sourceFrame, 352/2, 288/2, 2, _vpm); - TestSize(sourceFrame, 2*352, 2*288,2, _vpm); - TestSize(sourceFrame, 480, 640, 2, _vpm); - TestSize(sourceFrame, 960, 720, 2, _vpm); - TestSize(sourceFrame, 1280, 720, 2, _vpm); - // stop timer - t1 = TickTime::Now(); - accTicks += t1 - t0; - - if (accTicks.Microseconds() < minRuntime || runIdx == 0) - { - minRuntime = accTicks.Microseconds(); - } - avgRuntime += accTicks.Microseconds(); - } - - sourceFrame.Free(); - - printf("\nAverage run time = %d us / frame\n", - //static_cast(avgRuntime / frameNum / NumRuns)); - static_cast(avgRuntime)); - printf("Min run time = %d us / frame\n\n", - //static_cast(minRuntime / frameNum)); - static_cast(minRuntime)); - -} - -void TestSize(VideoFrame& sourceFrame, WebRtc_UWord32 targetWidth, WebRtc_UWord32 targetHeight, - WebRtc_UWord32 mode, VideoProcessingModule *vpm) -{ - VideoFrame *outFrame = NULL; - std::ostringstream filename; - filename << "Resampler_"<< mode <<"_" << targetWidth << "x" << targetHeight << "_30Hz_P420.yuv"; - std::cout << "Watch " << filename.str() << " and verify that it is okay." << std::endl; - FILE* standAloneFile = fopen(filename.str().c_str(), "wb"); - ASSERT_EQ(VPM_OK, vpm->SetTargetResolution(targetWidth, targetHeight, 30)); - ASSERT_EQ(VPM_OK, vpm->PreprocessFrame(&sourceFrame, &outFrame)); - ASSERT_EQ((targetWidth * targetHeight * 3 / 2), outFrame->Length()); - - // Write to file for visual inspection - fwrite(outFrame->Buffer(), 1, outFrame->Length(), standAloneFile); - - outFrame->Free(); - fclose(standAloneFile); -} - -int main(int argc, char** argv) -{ - - ::testing::InitGoogleTest(&argc, argv); - VPMEnvironment* env = new VPMEnvironment; - ::testing::AddGlobalTestEnvironment(env); - - return RUN_ALL_TESTS(); -} diff --git a/modules/video_processing/main/test/unit_test/unit_test.h b/modules/video_processing/main/test/unit_test/unit_test.h deleted file mode 100644 index 89e4b53d7..000000000 --- a/modules/video_processing/main/test/unit_test/unit_test.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef UNIT_TEST_H -#define UNIT_TEST_H - -#include "video_processing.h" - -#include "vplib.h" - -#include - -using namespace webrtc; - -class VideoProcessingModuleTest : public ::testing::Test -{ -protected: - VideoProcessingModuleTest(); - virtual void SetUp(); - virtual void TearDown(); - - VideoProcessingModule* _vpm; - FILE* _sourceFile; - VideoFrame _videoFrame; - const WebRtc_UWord32 _width; - const WebRtc_UWord32 _height; - const WebRtc_UWord32 _frameLength; -}; - - -#endif // UNIT_TEST_H diff --git a/modules/video_processing/main/test/unit_test/writeYUV420file.m b/modules/video_processing/main/test/unit_test/writeYUV420file.m deleted file mode 100644 index 69a880833..000000000 --- a/modules/video_processing/main/test/unit_test/writeYUV420file.m +++ /dev/null @@ -1,22 +0,0 @@ -function writeYUV420file(filename, Y, U, V) -% writeYUV420file(filename, Y, U, V) - -fid = fopen(filename,'wb'); -if fid==-1 - error(['Cannot open file ' filename]); -end - -numFrames=size(Y,3); - -for k=1:numFrames - % Write luminance - fwrite(fid,uint8(Y(:,:,k).'), 'uchar'); - - % Write U channel - fwrite(fid,uint8(U(:,:,k).'), 'uchar'); - - % Write V channel - fwrite(fid,uint8(V(:,:,k).'), 'uchar'); -end - -fclose(fid); diff --git a/modules/video_processing/main/test/vpm_tests.gyp b/modules/video_processing/main/test/vpm_tests.gyp deleted file mode 100644 index 5d6ed77d5..000000000 --- a/modules/video_processing/main/test/vpm_tests.gyp +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'VPMUnitTest', - 'type': 'executable', - 'dependencies': [ - '../source/video_processing.gyp:video_processing', - '../../../utility/source/utility.gyp:webrtc_utility', - # The tests are based on gtest - '../../../../../testing/gtest.gyp:gtest', - '../../../../../testing/gtest.gyp:gtest_main', - ], - 'include_dirs': [ - '../../../../system_wrappers/interface', - '../../../../common_video/vplib/main/interface', - ], - 'sources': [ - - # headers - 'unit_test/unit_test.h', - - # sources - 'unit_test/brightness_detection_test.cc', - 'unit_test/color_enhancement_test.cc', - 'unit_test/deflickering_test.cc', - 'unit_test/denoising_test.cc', - 'unit_test/unit_test.cc', - - ], # source - - 'conditions': [ - - ['OS=="linux"', { - 'cflags': [ - '-fexceptions', - ], - }], - - ], # conditions - }, - ], -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_render/OWNERS b/modules/video_render/OWNERS deleted file mode 100644 index 435723d10..000000000 --- a/modules/video_render/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -mflodman@google.com -perkj@google.com -ronghuawu@google.com -mallinath@google.com diff --git a/modules/video_render/main/interface/video_render.h b/modules/video_render/main/interface/video_render.h deleted file mode 100644 index 3ae77cd95..000000000 --- a/modules/video_render/main/interface/video_render.h +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_H_ - -/* - * video_render.h - * - * This header file together with module.h and module_common_types.h - * contains all of the APIs that are needed for using the video render - * module class. - * - */ - -#include "module.h" -#include "video_render_defines.h" - -namespace webrtc { -// Class definitions -class VideoRender: public Module -{ -public: - /* - * Create a video render module object - * - * id - unique identifier of this video render module object - * window - pointer to the window to render to - * fullscreen - true if this is a fullscreen renderer - * videoRenderType - type of renderer to create - */ - static VideoRender - * CreateVideoRender( - const WebRtc_Word32 id, - void* window, - const bool fullscreen, - const VideoRenderType videoRenderType = - kRenderDefault); - - /* - * Destroy a video render module object - * - * module - object to destroy - */ - static void DestroyVideoRender(VideoRender* module); - - /* - * Returns version of the module and its components - * - * version - buffer to which the version will be written - * remainingBufferInBytes - remaining number of WebRtc_Word8 in the version buffer - * position - position of the next empty WebRtc_Word8 in the version buffer - */ - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const = 0; - - /* - * Change the unique identifier of this object - * - * id - new unique identifier of this video render module object - */ - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - virtual WebRtc_Word32 TimeUntilNextProcess() = 0; - virtual WebRtc_Word32 Process() = 0; - - /************************************************************************** - * - * Window functions - * - ***************************************************************************/ - - /* - * Get window for this renderer - */ - virtual void* Window() = 0; - - /* - * Change render window - * - * window - the new render window, assuming same type as originally created. - */ - virtual WebRtc_Word32 ChangeWindow(void* window) = 0; - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - /* - * Add incoming render stream - * - * streamID - id of the stream to add - * zOrder - relative render order for the streams, 0 = on top - * left - position of the stream in the window, [0.0f, 1.0f] - * top - position of the stream in the window, [0.0f, 1.0f] - * right - position of the stream in the window, [0.0f, 1.0f] - * bottom - position of the stream in the window, [0.0f, 1.0f] - * - * Return - callback class to use for delivering new frames to render. - */ - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom) = 0; - /* - * Delete incoming render stream - * - * streamID - id of the stream to add - */ - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId) = 0; - - /* - * Add incoming render callback, used for external rendering - * - * streamID - id of the stream the callback is used for - * renderObject - the VideoRenderCallback to use for this stream, NULL to remove - * - * Return - callback class to use for delivering new frames to render. - */ - virtual WebRtc_Word32 - AddExternalRenderCallback(const WebRtc_UWord32 streamId, - VideoRenderCallback* renderObject) = 0; - - /* - * Get the porperties for an incoming render stream - * - * streamID - [in] id of the stream to get properties for - * zOrder - [out] relative render order for the streams, 0 = on top - * left - [out] position of the stream in the window, [0.0f, 1.0f] - * top - [out] position of the stream in the window, [0.0f, 1.0f] - * right - [out] position of the stream in the window, [0.0f, 1.0f] - * bottom - [out] position of the stream in the window, [0.0f, 1.0f] - */ - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const = 0; - /* - * The incoming frame rate to the module, not the rate rendered in the window. - */ - virtual WebRtc_UWord32 - GetIncomingFrameRate(const WebRtc_UWord32 streamId) = 0; - - /* - * Returns the number of incoming streams added to this render module - */ - virtual WebRtc_UWord32 GetNumIncomingRenderStreams() const = 0; - - /* - * Returns true if this render module has the streamId added, false otherwise. - */ - virtual bool - HasIncomingRenderStream(const WebRtc_UWord32 streamId) const = 0; - - /* - * Registers a callback to get raw images in the same time as sent - * to the renderer. To be used for external rendering. - */ - virtual WebRtc_Word32 - RegisterRawFrameCallback(const WebRtc_UWord32 streamId, - VideoRenderCallback* callbackObj) = 0; - - /* - * This method is usefull to get last rendered frame for the stream specified - */ - virtual WebRtc_Word32 - GetLastRenderedFrame(const WebRtc_UWord32 streamId, - VideoFrame &frame) const = 0; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - /* - * Starts rendering the specified stream - */ - virtual WebRtc_Word32 StartRender(const WebRtc_UWord32 streamId) = 0; - - /* - * Stops the renderer - */ - virtual WebRtc_Word32 StopRender(const WebRtc_UWord32 streamId) = 0; - - /* - * Resets the renderer - * No streams are removed. The state should be as after AddStream was called. - */ - virtual WebRtc_Word32 ResetRender() = 0; - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - /* - * Returns the preferred render video type - */ - virtual RawVideoType PreferredVideoType() const = 0; - - /* - * Returns true if the renderer is in fullscreen mode, otherwise false. - */ - virtual bool IsFullScreen() = 0; - - /* - * Gets screen resolution in pixels - */ - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const = 0; - - /* - * Get the actual render rate for this stream. I.e rendered frame rate, - * not frames delivered to the renderer. - */ - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId) = 0; - - /* - * Set cropping of incoming stream - */ - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom) = 0; - - /* - * re-configure renderer - */ - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable) = 0; - - virtual WebRtc_Word32 FullScreenRender(void* window, const bool enable) = 0; - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float right, const float bottom) = 0; - - /* - * Set a start image. The image is rendered before the first image has been delivered - */ - virtual WebRtc_Word32 - SetStartImage(const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame) = 0; - - /* - * Set a timout image. The image is rendered if no videoframe has been delivered - */ - virtual WebRtc_Word32 SetTimeoutImage(const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame, - const WebRtc_UWord32 timeout)= 0; - - virtual WebRtc_Word32 MirrorRenderStream(const int renderId, - const bool enable, - const bool mirrorXAxis, - const bool mirrorYAxis) = 0; - - static WebRtc_Word32 SetAndroidObjects(void* javaVM); -}; -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_H_ diff --git a/modules/video_render/main/interface/video_render_defines.h b/modules/video_render/main/interface/video_render_defines.h deleted file mode 100644 index 2a87f3f0e..000000000 --- a/modules/video_render/main/interface/video_render_defines.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_DEFINES_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_DEFINES_H_ - -// Includes -#include "common_types.h" -#include "module_common_types.h" - -namespace webrtc -{ -// Defines -#ifndef NULL -#define NULL 0 -#endif - -// Enums -enum VideoRenderType -{ - kRenderExternal = 0, // External - kRenderWindows = 1, // Windows - kRenderCocoa = 2, // Mac - kRenderCarbon = 3, - kRenderiPhone = 4, // iPhone - kRenderAndroid = 5, // Android - kRenderX11 = 6, // Linux - kRenderDefault -}; - -// Runtime errors -enum VideoRenderError -{ - kRenderShutDown = 0, - kRenderPerformanceAlarm = 1 -}; - -// The object a module user uses to send new frames to the renderer -// One object is used for each incoming stream -class VideoRenderCallback -{ -public: - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) = 0; - -protected: - virtual ~VideoRenderCallback() - { - } -}; - -// Feedback class to be implemented by module user -class VideoRenderFeedback -{ -public: - virtual void OnRenderError(const WebRtc_Word32 streamId, - const VideoRenderError error) = 0; - -protected: - virtual ~VideoRenderFeedback() - { - } -}; - -// Mobile enums -enum StretchMode -{ - kStretchToInsideEdge = 1, - kStretchToOutsideEdge = 2, - kStretchMatchWidth = 3, - kStretchMatchHeight = 4, - kStretchNone = 5 -}; - -enum Rotation -{ - kRotation0 = 0, - kRotation90 = 1, - kRotation180 = 2, - kRotation270 = 3 -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_INTERFACE_VIDEO_RENDER_DEFINES_H_ diff --git a/modules/video_render/main/source/Android.mk b/modules/video_render/main/source/Android.mk deleted file mode 100644 index b7d9ac9ed..000000000 --- a/modules/video_render/main/source/Android.mk +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_CLASS := STATIC_LIBRARIES -LOCAL_MODULE := libwebrtc_video_render -LOCAL_MODULE_TAGS := optional -LOCAL_CPP_EXTENSION := .cc -LOCAL_GENERATED_SOURCES := -LOCAL_SRC_FILES := incoming_video_stream.cc \ - video_render_frames.cc \ - video_render_impl.cc \ - external/video_render_external_impl.cc \ - Android/video_render_android_impl.cc \ - Android/video_render_android_native_opengl2.cc \ - Android/video_render_android_surface_view.cc \ - Android/video_render_opengles20.cc - -# Flags passed to both C and C++ files. -MY_CFLAGS := -MY_CFLAGS_C := -MY_DEFS := '-DNO_TCMALLOC' \ - '-DNO_HEAPCHECKER' \ - '-DWEBRTC_TARGET_PC' \ - '-DWEBRTC_LINUX' \ - '-DWEBRTC_THREAD_RR' \ - '-DWEBRTC_ANDROID' \ - '-DANDROID' - -LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS) - -# Include paths placed before CFLAGS/CPPFLAGS -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../.. \ - $(LOCAL_PATH)/. \ - $(LOCAL_PATH)/../interface \ - $(LOCAL_PATH)/../../../interface \ - $(LOCAL_PATH)/../../../../common_video/vplib/main/interface \ - $(LOCAL_PATH)/../../../../system_wrappers/interface \ - $(LOCAL_PATH)/../../../utility/interface \ - $(LOCAL_PATH)/../../../audio_coding/main/interface \ - $(LOCAL_PATH)/Android - -# Flags passed to only C++ (and not C) files. -LOCAL_CPPFLAGS := - -LOCAL_LDFLAGS := - -LOCAL_STATIC_LIBRARIES := - -LOCAL_SHARED_LIBRARIES := libcutils \ - libdl \ - libstlport -LOCAL_ADDITIONAL_DEPENDENCIES := - -include external/stlport/libstlport.mk -include $(BUILD_STATIC_LIBRARY) diff --git a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViEAndroidGLES20.java b/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViEAndroidGLES20.java deleted file mode 100644 index fc306071a..000000000 --- a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViEAndroidGLES20.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -import java.util.concurrent.locks.ReentrantLock; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.opengles.GL10; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ConfigurationInfo; -import android.opengl.GLSurfaceView; -import android.util.Log; - -public class ViEAndroidGLES20 extends GLSurfaceView - implements GLSurfaceView.Renderer { - // True if onSurfaceCreated has been called. - private boolean surfaceCreated = false; - private boolean openGLCreated = false; - // True if NativeFunctionsRegistered has been called. - private boolean nativeFunctionsRegisted = false; - private ReentrantLock nativeFunctionLock = new ReentrantLock(); - // Address of Native object that will do the drawing. - private long nativeObject = 0; - private int viewWidth = 0; - private int viewHeight = 0; - - public static boolean UseOpenGL2(Object renderWindow) { - return ViEAndroidGLES20.class.isInstance(renderWindow); - } - - public ViEAndroidGLES20(Context context) { - super(context); - - // Setup the context factory for 2.0 rendering. - // See ContextFactory class definition below - setEGLContextFactory(new ContextFactory()); - - // We need to choose an EGLConfig that matches the format of - // our surface exactly. This is going to be done in our - // custom config chooser. See ConfigChooser class definition below - // Use RGB 565 without an alpha channel. - setEGLConfigChooser( new ConfigChooser(5, 6, 5, 0, 0, 0) ); - - this.setRenderer(this); - this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - } - - // IsSupported - // Return true if this device support Open GL ES 2.0 rendering. - public static boolean IsSupported(Context context) { - ActivityManager am = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - ConfigurationInfo info = am.getDeviceConfigurationInfo(); - if(info.reqGlEsVersion >= 0x20000) { - // Open GL ES 2.0 is supported. - return true; - } - return false; - } - - public void onDrawFrame(GL10 gl) { - nativeFunctionLock.lock(); - if(!nativeFunctionsRegisted || !surfaceCreated) { - nativeFunctionLock.unlock(); - return; - } - - if(!openGLCreated) { - if(0 != CreateOpenGLNative(nativeObject, viewWidth, viewHeight)) { - return; // Failed to create OpenGL - } - openGLCreated = true; // Created OpenGL successfully - } - DrawNative(nativeObject); // Draw the new frame - nativeFunctionLock.unlock(); - } - - public void onSurfaceChanged(GL10 gl, int width, int height) { - surfaceCreated = true; - viewWidth = width; - viewHeight = height; - - nativeFunctionLock.lock(); - if(nativeFunctionsRegisted) { - if(CreateOpenGLNative(nativeObject,width,height) == 0) - openGLCreated = true; - } - nativeFunctionLock.unlock(); - } - - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - } - - public void RegisterNativeObject(long nativeObject) { - nativeFunctionLock.lock(); - nativeObject = nativeObject; - nativeFunctionsRegisted = true; - nativeFunctionLock.unlock(); - } - - public void DeRegisterNativeObject() { - nativeFunctionLock.lock(); - nativeFunctionsRegisted = false; - openGLCreated = false; - nativeObject = 0; - nativeFunctionLock.unlock(); - } - - public void ReDraw() { - if(surfaceCreated) { - // Request the renderer to redraw using the render thread context. - this.requestRender(); - } - } - - // EGL Context factory used for creating EGL 2.0 context - // on Android 2.1(and later, - // though there are simpler ways in 2.2) - // Code is from the NDK samples\hello-gl2\src\com\android\gl2jni. - private static class ContextFactory - implements GLSurfaceView.EGLContextFactory { - private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - public EGLContext createContext(EGL10 egl, - EGLDisplay display, - EGLConfig eglConfig) { - //checkEglError("Before eglCreateContext", egl); - int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - // Create an Open GL ES 2.0 context - EGLContext context = egl.eglCreateContext(display, - eglConfig, - EGL10.EGL_NO_CONTEXT, - attrib_list); - checkEglError("ContextFactory eglCreateContext", egl); - return context; - } - - public void destroyContext(EGL10 egl, EGLDisplay display, - EGLContext context) { - egl.eglDestroyContext(display, context); - } - } - - private static void checkEglError(String prompt, EGL10 egl) { - int error; - while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { - Log.e("*WEBRTC*", String.format("%s: EGL error: 0x%x", prompt, error)); - } - } - - // Code is from the NDK samples\hello-gl2\src\com\android\gl2jni - private static class ConfigChooser - implements GLSurfaceView.EGLConfigChooser { - - public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { - mRedSize = r; - mGreenSize = g; - mBlueSize = b; - mAlphaSize = a; - mDepthSize = depth; - mStencilSize = stencil; - } - - // This EGL config specification is used to specify 2.0 rendering. - // We use a minimum size of 4 bits for red/green/blue, but will - // perform actual matching in chooseConfig() below. - private static int EGL_OPENGL_ES2_BIT = 4; - private static int[] s_configAttribs2 = - { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - - // Get the number of minimally matching EGL configurations - int[] num_config = new int[1]; - egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config); - - int numConfigs = num_config[0]; - - if (numConfigs <= 0) { - throw new IllegalArgumentException("No configs match configSpec"); - } - - // Allocate then read the array of minimally matching EGL configs - EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(display, s_configAttribs2, configs, - numConfigs, num_config); - - // Now return the "best" one - return chooseConfig(egl, display, configs); - } - - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { - for(EGLConfig config : configs) { - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); - int s = findConfigAttrib(egl, display, config, - EGL10.EGL_STENCIL_SIZE, 0); - - // We need at least mDepthSize and mStencilSize bits - if (d < mDepthSize || s < mStencilSize) - continue; - - // We want an *exact* match for red/green/blue/alpha - int r = findConfigAttrib(egl, display, config, - EGL10.EGL_RED_SIZE, 0); - int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); - int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); - int a = findConfigAttrib(egl, display, config, - EGL10.EGL_ALPHA_SIZE, 0); - - if (r == mRedSize && g == mGreenSize && - b == mBlueSize && a == mAlphaSize) - return config; - } - return null; - } - - private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, - int defaultValue) { - - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { - return mValue[0]; - } - return defaultValue; - } - - // Subclasses can adjust these values: - protected int mRedSize; - protected int mGreenSize; - protected int mBlueSize; - protected int mAlphaSize; - protected int mDepthSize; - protected int mStencilSize; - private int[] mValue = new int[1]; - } - - private native int CreateOpenGLNative(long nativeObject, - int width, int height); - private native void DrawNative(long nativeObject); - -} diff --git a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViERenderer.java b/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViERenderer.java deleted file mode 100644 index 56d5261c9..000000000 --- a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViERenderer.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -import android.content.Context; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -public class ViERenderer { - - // View used for local rendering that Cameras can use for Video Overlay. - private static SurfaceHolder g_localRenderer; - - public static SurfaceView CreateRenderer(Context context) { - return CreateRenderer(context,false); - } - - public static SurfaceView CreateRenderer(Context context, - boolean useOpenGLES2) { - if(useOpenGLES2 == true && ViEAndroidGLES20.IsSupported(context)) - return new ViEAndroidGLES20(context); - else - return new SurfaceView(context); - } - - // Creates a SurfaceView to be used by Android Camera - // service to display a local preview. - // This needs to be used on Android prior to version 2.1 - // in order to run the camera. - // Call this function before ViECapture::StartCapture. - // The created view needs to be added to a visible layout - // after a camera has been allocated - // (with the call ViECapture::AllocateCaptureDevice). - // IE. - // CreateLocalRenderer - // ViECapture::AllocateCaptureDevice - // LinearLayout.addview - // ViECapture::StartCapture - public static SurfaceView CreateLocalRenderer(Context context) { - SurfaceView localRender = new SurfaceView(context); - g_localRenderer = localRender.getHolder(); - g_localRenderer.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - return localRender; - } - - public static SurfaceHolder GetLocalRenderer() { - return g_localRenderer; - } - -} diff --git a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViESurfaceRenderer.java b/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViESurfaceRenderer.java deleted file mode 100644 index 341258298..000000000 --- a/modules/video_render/main/source/Android/java/org/webrtc/videoengine/ViESurfaceRenderer.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package org.webrtc.videoengine; - -import java.nio.ByteBuffer; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.SurfaceHolder.Callback; - -public class ViESurfaceRenderer implements Callback { - - // the bitmap used for drawing. - private Bitmap bitmap = null; - private ByteBuffer byteBuffer; - private SurfaceHolder surfaceHolder; - // Rect of the source bitmap to draw - private Rect srcRect = new Rect(); - // Rect of the destination canvas to draw to - private Rect dstRect = new Rect(); - private int dstHeight = 0; - private int dstWidth = 0; - private float dstTopScale = 0; - private float dstBottomScale = 1; - private float dstLeftScale = 0; - private float dstRightScale = 1; - - public ViESurfaceRenderer(SurfaceView view) { - surfaceHolder = view.getHolder(); - if(surfaceHolder == null) - return; - - Canvas canvas = surfaceHolder.lockCanvas(); - if(canvas != null) { - Rect dst =surfaceHolder.getSurfaceFrame(); - if(dst != null) { - dstRect = dst; - dstHeight =dstRect.bottom-dstRect.top; - dstWidth = dstRect.right-dstRect.left; - } - surfaceHolder.unlockCanvasAndPost(canvas); - } - - surfaceHolder.addCallback(this); - } - - public void surfaceChanged(SurfaceHolder holder, int format, - int in_width, int in_height) { - - dstHeight = in_height; - dstWidth = in_width; - dstRect.left = (int)(dstLeftScale*dstWidth); - dstRect.top = (int)(dstTopScale*dstHeight); - dstRect.bottom = (int)(dstBottomScale*dstHeight); - dstRect.right = (int) (dstRightScale*dstWidth); - } - - public void surfaceCreated(SurfaceHolder holder) { - // TODO(leozwang) Auto-generated method stub - } - - public void surfaceDestroyed(SurfaceHolder holder) { - // TODO(leozwang) Auto-generated method stub - } - - public Bitmap CreateBitmap(int width, int height) { - if (bitmap == null) { - try { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_DISPLAY); - } - catch (Exception e) { - } - } - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - srcRect.left = 0; - srcRect.top = 0; - srcRect.bottom = height; - srcRect.right = width; - - return bitmap; - } - - public ByteBuffer CreateByteBuffer(int width, int height) { - if (bitmap == null) { - try { - android.os.Process - .setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY); - } - catch (Exception e) { - } - } - - try { - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); - byteBuffer = ByteBuffer.allocateDirect(width*height*2); - srcRect.left = 0; - srcRect.top = 0; - srcRect.bottom = height; - srcRect.right = width; - } - catch (Exception ex) { - Log.e("*WEBRTC*", "Failed to CreateByteBuffer"); - bitmap = null; - byteBuffer = null; - } - - return byteBuffer; - } - - public void SetCoordinates(float left, float top, - float right, float bottom) { - dstLeftScale = left; - dstTopScale = top; - dstRightScale = right; - dstBottomScale = bottom; - - dstRect.left = (int)(dstLeftScale*dstWidth); - dstRect.top = (int)(dstTopScale*dstHeight); - dstRect.bottom = (int)(dstBottomScale*dstHeight); - dstRect.right = (int) (dstRightScale*dstWidth); - } - - public void DrawByteBuffer() { - if(byteBuffer == null) - return; - byteBuffer.rewind(); - bitmap.copyPixelsFromBuffer(byteBuffer); - DrawBitmap(); - } - - public void DrawBitmap() { - if(bitmap == null) - return; - - Canvas canvas = surfaceHolder.lockCanvas(); - if(canvas != null) { - canvas.drawBitmap(bitmap, srcRect, dstRect, null); - surfaceHolder.unlockCanvasAndPost(canvas); - } - } - -} diff --git a/modules/video_render/main/source/Android/video_render_android_impl.cc b/modules/video_render/main/source/Android/video_render_android_impl.cc deleted file mode 100644 index e1aa60041..000000000 --- a/modules/video_render/main/source/Android/video_render_android_impl.cc +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_android_impl.h" - -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "thread_wrapper.h" -#include "tick_util.h" - -#ifdef ANDROID_LOG -#include -#include - -#undef WEBRTC_TRACE -#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) -#else -#include "trace.h" -#endif - -namespace webrtc { -JavaVM* VideoRenderAndroid::g_jvm = NULL; - -WebRtc_Word32 VideoRenderAndroid::SetAndroidEnvVariables(void* javaVM) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, "%s", __FUNCTION__); - - g_jvm = (JavaVM*) javaVM; - - return 0; - -} - -VideoRenderAndroid::VideoRenderAndroid( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool /*fullscreen*/): - _id(id), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _renderType(videoRenderType), - _ptrWindow((jobject)(window)), - _streamsMap(), - _javaShutDownFlag(false), - _javaShutdownEvent(*EventWrapper::Create()), - _javaRenderEvent(*EventWrapper::Create()), - _lastJavaRenderEvent(0), - _javaRenderJniEnv(NULL), - _javaRenderThread(NULL) -{ -} - -VideoRenderAndroid::~VideoRenderAndroid() -{ - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, - "VideoRenderAndroid dtor"); - - if (_javaRenderThread) - StopRender(); - - for (MapItem* item = _streamsMap.First(); item != NULL; item - = _streamsMap.Next(item)) - { // Delete streams - delete static_cast (item->GetItem()); - } - delete &_javaShutdownEvent; - delete &_javaRenderEvent; - delete &_critSect; -} - -WebRtc_Word32 VideoRenderAndroid::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_critSect); - _id = id; - - return 0; -} - -WebRtc_Word32 VideoRenderAndroid::ChangeWindow(void* /*window*/) -{ - return -1; -} - -VideoRenderCallback* -VideoRenderAndroid::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_critSect); - - AndroidStream* renderStream = NULL; - MapItem* item = _streamsMap.Find(streamId); - if (item) - { - renderStream = (AndroidStream*) (item->GetItem()); - if (NULL != renderStream) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, - "%s: Render stream already exists", __FUNCTION__); - return renderStream; - } - } - - renderStream = CreateAndroidRenderChannel(streamId, zOrder, left, top, - right, bottom, *this); - if (renderStream) - { - _streamsMap.Insert(streamId, renderStream); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); - return NULL; - } - return renderStream; -} - -WebRtc_Word32 VideoRenderAndroid::DeleteIncomingRenderStream( - const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_critSect); - - MapItem* item = _streamsMap.Find(streamId); - if (item) - { - delete (AndroidStream*) item->GetItem(); - _streamsMap.Erase(streamId); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); - return -1; - } - return 0; -} - -WebRtc_Word32 VideoRenderAndroid::GetIncomingRenderStreamProperties( - const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceVideoRenderer, - _id, - "%s: streamId - %d zOrder - %d left - %d top - %d right -%d and bottm - %d", - streamId, zOrder, left, top, right, bottom); - - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::StartRender() -{ - CriticalSectionScoped cs(_critSect); - - if (_javaRenderThread) - { - // StartRender is called when this stream should start render. - // However StopRender is not called when the streams stop rendering. Thus the the thread is only deleted when the renderer is removed. - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s, Render thread already exist", __FUNCTION__); - return 0; - } - - _javaRenderThread = ThreadWrapper::CreateThread(JavaRenderThreadFun, this, - kRealtimePriority, - "AndroidRenderThread"); - if (!_javaRenderThread) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No thread", __FUNCTION__); - return -1; - } - - unsigned int tId = 0; - if (_javaRenderThread->Start(tId)) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, - "%s: thread started: %u", __FUNCTION__, tId); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not start send thread", __FUNCTION__); - return -1; - } - return 0; -} - -WebRtc_Word32 VideoRenderAndroid::StopRender() -{ - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:", __FUNCTION__); - { - CriticalSectionScoped cs(_critSect); - if (!_javaRenderThread) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, no renderer", __FUNCTION__); - return -1; - } - _javaShutDownFlag = true; - _javaRenderEvent.Set(); - } - - _javaShutdownEvent.Wait(3000); - CriticalSectionScoped cs(_critSect); - _javaRenderThread->SetNotAlive(); - if (_javaRenderThread->Stop()) - { - delete _javaRenderThread; - _javaRenderThread = NULL; - } - else - { - assert(false); - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Not able to stop thread, leaking", __FUNCTION__); - _javaRenderThread = NULL; - } - return 0; -} - -void VideoRenderAndroid::ReDraw() -{ - CriticalSectionScoped cs(_critSect); - if (_lastJavaRenderEvent < TickTime::MillisecondTimestamp() - 20) // Allow redraw if it was more than 20ms since last. - { - _lastJavaRenderEvent = TickTime::MillisecondTimestamp(); - _javaRenderEvent.Set(); - } -} - -bool VideoRenderAndroid::JavaRenderThreadFun(void* obj) -{ - return static_cast (obj)->JavaRenderThreadProcess(); -} - -bool VideoRenderAndroid::JavaRenderThreadProcess() -{ - _javaRenderEvent.Wait(1000); - - CriticalSectionScoped cs(_critSect); - if (!_javaRenderJniEnv) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&_javaRenderJniEnv, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !_javaRenderJniEnv) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, _javaRenderJniEnv); - return false; - } - } - - for (MapItem* item = _streamsMap.First(); item != NULL; item - = _streamsMap.Next(item)) - { - static_cast (item->GetItem())->DeliverFrame( - _javaRenderJniEnv); - } - - if (_javaShutDownFlag) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, - "%s: Java thread detached", __FUNCTION__); - } - _javaRenderJniEnv = false; - _javaShutDownFlag = false; - _javaShutdownEvent.Set(); - return false; // Do not run this thread again. - } - return true; -} - -VideoRenderType VideoRenderAndroid::RenderType() -{ - return _renderType; -} - -RawVideoType VideoRenderAndroid::PerferedVideoType() -{ - return kVideoI420; -} - -bool VideoRenderAndroid::FullScreen() -{ - return false; -} - -WebRtc_Word32 VideoRenderAndroid::GetGraphicsMemory( - WebRtc_UWord64& /*totalGraphicsMemory*/, - WebRtc_UWord64& /*availableGraphicsMemory*/) const -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::GetScreenResolution( - WebRtc_UWord32& /*screenWidth*/, - WebRtc_UWord32& /*screenHeight*/) const -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_UWord32 VideoRenderAndroid::RenderFrameRate(const WebRtc_UWord32 /*streamId*/) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::SetStreamCropping( - const WebRtc_UWord32 /*streamId*/, - const float /*left*/, - const float /*top*/, - const float /*right*/, - const float /*bottom*/) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::SetTransparentBackground(const bool enable) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::ConfigureRenderer( - const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::SetText( - const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float rigth, const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderAndroid::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Android", __FUNCTION__); - return -1; -} -} //namespace webrtc - diff --git a/modules/video_render/main/source/Android/video_render_android_impl.h b/modules/video_render/main/source/Android/video_render_android_impl.h deleted file mode 100644 index 705887126..000000000 --- a/modules/video_render/main/source/Android/video_render_android_impl.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ - -#include -#include "i_video_render.h" -#include "map_wrapper.h" - - -namespace webrtc { - -//#define ANDROID_LOG - - -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; - - -// The object a module user uses to send new frames to the java renderer -// Base class for android render streams. - -class AndroidStream: public VideoRenderCallback -{ -public: - /* - * DeliverFrame is called from a thread connected to the Java VM. - * Used for Delivering frame for rendering. - */ - virtual void DeliverFrame(JNIEnv* jniEnv)=0; - - virtual ~AndroidStream() - { - }; -}; - -class VideoRenderAndroid: IVideoRender -{ -public: - static WebRtc_Word32 SetAndroidEnvVariables(void* javaVM); - - VideoRenderAndroid(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, void* window, - const bool fullscreen); - - virtual ~VideoRenderAndroid(); - - virtual WebRtc_Word32 Init()=0; - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const; - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - virtual void ReDraw(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 - GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float rigth, const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - -protected: - virtual AndroidStream - * CreateAndroidRenderChannel(WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, const float top, - const float right, const float bottom, - VideoRenderAndroid& renderer) = 0; - - WebRtc_Word32 _id; - CriticalSectionWrapper& _critSect; - VideoRenderType _renderType; - jobject _ptrWindow; - - static JavaVM* g_jvm; - -private: - static bool JavaRenderThreadFun(void* obj); - bool JavaRenderThreadProcess(); - - MapWrapper _streamsMap; // Map with streams to render. - bool _javaShutDownFlag; // True if the _javaRenderThread thread shall be detached from the JVM. - EventWrapper& _javaShutdownEvent; - EventWrapper& _javaRenderEvent; - WebRtc_Word64 _lastJavaRenderEvent; - JNIEnv* _javaRenderJniEnv; // JNIEnv for the java render thread. - ThreadWrapper* _javaRenderThread; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ diff --git a/modules/video_render/main/source/Android/video_render_android_native_opengl2.cc b/modules/video_render/main/source/Android/video_render_android_native_opengl2.cc deleted file mode 100644 index ad640bce8..000000000 --- a/modules/video_render/main/source/Android/video_render_android_native_opengl2.cc +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_android_native_opengl2.h" -#include "critical_section_wrapper.h" -#include "vplib.h" -#include "tick_util.h" - -#ifdef ANDROID_LOG -#include -#include - -#undef WEBRTC_TRACE -#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTC*", __VA_ARGS__) -#else -#include "trace.h" -#endif - -namespace webrtc { - -AndroidNativeOpenGl2Renderer::AndroidNativeOpenGl2Renderer( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : - VideoRenderAndroid(id, videoRenderType, window, fullscreen), - _javaRenderObj(NULL), - _javaRenderClass(NULL) -{ -} - -bool AndroidNativeOpenGl2Renderer::UseOpenGL2(void* window) -{ - if (!g_jvm) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "RendererAndroid():UseOpenGL No JVM set."); - return false; - } - bool isAttached = false; - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideoRenderer, - -1, - "RendererAndroid(): Could not attach thread to JVM (%d, %p)", - res, env); - return false; - } - isAttached = true; - } - - // get the renderer class - jclass javaRenderClassLocal = - env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); - if (!javaRenderClassLocal) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "%s: could not find ViEAndroidRenderer class", - __FUNCTION__); - return false; - } - - // get the method ID for UseOpenGL - jmethodID cidUseOpenGL = env->GetStaticMethodID(javaRenderClassLocal, - "UseOpenGL2", - "(Ljava/lang/Object;)Z"); - if (cidUseOpenGL == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "%s: could not get UseOpenGL ID", __FUNCTION__); - return false; - } - jboolean res = env->CallStaticBooleanMethod(javaRenderClassLocal, - cidUseOpenGL, (jobject) window); - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - return res; -} - -AndroidNativeOpenGl2Renderer::~AndroidNativeOpenGl2Renderer() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, - "AndroidNativeOpenGl2Renderer dtor"); - if (g_jvm) - { - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - env = NULL; - } - else - { - isAttached = true; - } - } - - env->DeleteGlobalRef(_javaRenderObj); - env->DeleteGlobalRef(_javaRenderClass); - - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Could not detach thread from JVM", - __FUNCTION__); - } - } - } -} - -WebRtc_Word32 AndroidNativeOpenGl2Renderer::Init() -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - if (!g_jvm) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "(%s): Not a valid Java VM pointer.", __FUNCTION__); - return -1; - } - if (!_ptrWindow) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "(%s): No window have been provided.", __FUNCTION__); - return -1; - } - - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - // get the ViEAndroidGLES20 class - jclass javaRenderClassLocal = - env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); - if (!javaRenderClassLocal) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not find ViEAndroidGLES20", __FUNCTION__); - return -1; - } - - // create a global reference to the class (to tell JNI that we are referencing it after this function has returned) - _javaRenderClass - = reinterpret_cast (env->NewGlobalRef(javaRenderClassLocal)); - if (!_javaRenderClass) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not create Java SurfaceHolder class reference", - __FUNCTION__); - return -1; - } - - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaRenderClassLocal); - - // create a reference to the object (to tell JNI that we are referencing it - // after this function has returned) - _javaRenderObj = env->NewGlobalRef(_ptrWindow); - if (!_javaRenderObj) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideoRenderer, - _id, - "%s: could not create Java SurfaceRender object reference", - __FUNCTION__); - return -1; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s done", - __FUNCTION__); - return 0; - -} -AndroidStream* -AndroidNativeOpenGl2Renderer::CreateAndroidRenderChannel( - WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom, - VideoRenderAndroid& renderer) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: Id %d", - __FUNCTION__, streamId); - AndroidNativeOpenGl2Channel* stream = - new AndroidNativeOpenGl2Channel(streamId, g_jvm, renderer, - _javaRenderObj); - if (stream && stream->Init(zOrder, left, top, right, bottom) == 0) - return stream; - else - { - delete stream; - } - return NULL; -} - -AndroidNativeOpenGl2Channel::AndroidNativeOpenGl2Channel(WebRtc_UWord32 streamId, - JavaVM* jvm, - VideoRenderAndroid& renderer,jobject javaRenderObj): - _id(streamId), - _renderCritSect(*CriticalSectionWrapper::CreateCriticalSection()), - _renderer(renderer), _jvm(jvm), _javaRenderObj(javaRenderObj), - _registerNativeCID(NULL), _deRegisterNativeCID(NULL), - _openGLRenderer(streamId) -{ - -} -AndroidNativeOpenGl2Channel::~AndroidNativeOpenGl2Channel() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, - "AndroidNativeOpenGl2Channel dtor"); - delete &_renderCritSect; - if (_jvm) - { - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - env = NULL; - } - else - { - isAttached = true; - } - } - if (env && _deRegisterNativeCID) - { - env->CallVoidMethod(_javaRenderObj, _deRegisterNativeCID); - } - - if (isAttached) - { - if (_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Could not detach thread from JVM", - __FUNCTION__); - } - } - } -} - -WebRtc_Word32 AndroidNativeOpenGl2Channel::Init(WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s: AndroidNativeOpenGl2Channel", __FUNCTION__); - if (!_jvm) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Not a valid Java VM pointer", __FUNCTION__); - return -1; - } - - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not attach thread to JVM (%d, %p)", - __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - jclass javaRenderClass = - env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); - if (!javaRenderClass) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not find ViESurfaceRenderer", __FUNCTION__); - return -1; - } - - // get the method ID for the ReDraw function - _redrawCid = env->GetMethodID(javaRenderClass, "ReDraw", "()V"); - if (_redrawCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not get ReDraw ID", __FUNCTION__); - return -1; - } - - _registerNativeCID = env->GetMethodID(javaRenderClass, - "RegisterNativeObject", "(J)V"); - if (_registerNativeCID == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not get RegisterNativeObject ID", __FUNCTION__); - return -1; - } - - _deRegisterNativeCID = env->GetMethodID(javaRenderClass, - "DeRegisterNativeObject", "()V"); - if (_deRegisterNativeCID == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not get DeRegisterNativeObject ID", - __FUNCTION__); - return -1; - } - - JNINativeMethod - nativeFunctions[2] = { - "DrawNative", - "(J)V", - (void*) &AndroidNativeOpenGl2Channel::DrawNativeStatic, - "CreateOpenGLNative", - "(JII)I", - (void*) &AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic }; - if (env->RegisterNatives(javaRenderClass, nativeFunctions, 2) == 0) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, - "%s: Registered native functions", __FUNCTION__); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "%s: Failed to register native functions", __FUNCTION__); - return -1; - } - - env->CallVoidMethod(_javaRenderObj, _registerNativeCID, (jlong) this); - - // Detach this thread if it was attached - if (isAttached) - { - if (_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - if (_openGLRenderer.SetCoordinates(zOrder, left, top, right, bottom) != 0) - { - return -1; - } - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s: AndroidNativeOpenGl2Channel done", __FUNCTION__); - return 0; -} - -WebRtc_Word32 AndroidNativeOpenGl2Channel::RenderFrame(const WebRtc_UWord32 /*streamId*/, - VideoFrame& videoFrame) -{ - // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s:" ,__FUNCTION__); - _renderCritSect.Enter(); - _bufferToRender.SwapFrame(videoFrame); - _renderCritSect.Leave(); - _renderer.ReDraw(); - return 0; -} - -/*Implements AndroidStream - * Calls the Java object and render the buffer in _bufferToRender - */ -void AndroidNativeOpenGl2Channel::DeliverFrame(JNIEnv* jniEnv) -{ - //TickTime timeNow=TickTime::Now(); - - //Draw the Surface - jniEnv->CallVoidMethod(_javaRenderObj, _redrawCid); - - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s: time to deliver %lld" ,__FUNCTION__,(TickTime::Now()-timeNow).Milliseconds()); -} - -/* - * JNI callback from Java class. Called when the render want to render a frame. Called from the GLRenderThread - * Method: DrawNative - * Signature: (J)V - */ -void JNICALL AndroidNativeOpenGl2Channel::DrawNativeStatic -(JNIEnv * env, jobject, jlong context) -{ - AndroidNativeOpenGl2Channel* renderChannel=reinterpret_cast(context); - renderChannel->DrawNative(); -} - -void AndroidNativeOpenGl2Channel::DrawNative() -{ - _openGLRenderer.Render(_bufferToRender); -} -/* - * JNI callback from Java class. Called when the GLSurfaceview have created a surface. Called from the GLRenderThread - * Method: CreateOpenGLNativeStatic - * Signature: (JII)I - */ -jint JNICALL AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic(JNIEnv * env, - jobject, - jlong context, - jint width, - jint height) -{ - AndroidNativeOpenGl2Channel* renderChannel = - reinterpret_cast (context); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, "%s:", __FUNCTION__); - return renderChannel->CreateOpenGLNative(width, height); -} - -jint AndroidNativeOpenGl2Channel::CreateOpenGLNative(int width, int height) -{ - - return _openGLRenderer.Setup(width, height); -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/Android/video_render_android_native_opengl2.h b/modules/video_render/main/source/Android/video_render_android_native_opengl2.h deleted file mode 100644 index 54532a6bc..000000000 --- a/modules/video_render/main/source/Android/video_render_android_native_opengl2.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ - -#include - -#include "video_render_defines.h" - -#include "video_render_android_impl.h" -#include "video_render_opengles20.h" - -namespace webrtc { -class CriticalSectionWrapper; - -class AndroidNativeOpenGl2Channel: public AndroidStream -{ - -public: - AndroidNativeOpenGl2Channel(WebRtc_UWord32 streamId,JavaVM* jvm,VideoRenderAndroid& renderer,jobject javaRenderObj); - ~AndroidNativeOpenGl2Channel(); - - WebRtc_Word32 Init(WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - //Implement VideoRenderCallback - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, VideoFrame& videoFrame); - - //Implements AndroidStream - virtual void DeliverFrame(JNIEnv* jniEnv); - -private: - static jint CreateOpenGLNativeStatic(JNIEnv * env,jobject, jlong context, jint width, jint height); - jint CreateOpenGLNative(int width, int height); - - static void DrawNativeStatic(JNIEnv * env,jobject, jlong context); - void DrawNative(); - WebRtc_UWord32 _id; - CriticalSectionWrapper& _renderCritSect; - - VideoFrame _bufferToRender; - VideoRenderAndroid& _renderer; - JavaVM* _jvm; - jobject _javaRenderObj; - - jmethodID _redrawCid; - jmethodID _registerNativeCID; - jmethodID _deRegisterNativeCID; - VideoRenderOpenGles20 _openGLRenderer; - -}; - - -class AndroidNativeOpenGl2Renderer: private VideoRenderAndroid -{ -public: - AndroidNativeOpenGl2Renderer(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen); - - ~AndroidNativeOpenGl2Renderer(); - static bool UseOpenGL2(void* window); - - WebRtc_Word32 Init(); - virtual AndroidStream* CreateAndroidRenderChannel(WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom, - VideoRenderAndroid& renderer); - -private: - jobject _javaRenderObj; - jclass _javaRenderClass; - -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ diff --git a/modules/video_render/main/source/Android/video_render_android_surface_view.cc b/modules/video_render/main/source/Android/video_render_android_surface_view.cc deleted file mode 100644 index 2b3403bcd..000000000 --- a/modules/video_render/main/source/Android/video_render_android_surface_view.cc +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_android_surface_view.h" -#include "critical_section_wrapper.h" -#include "vplib.h" -#include "tick_util.h" -#ifdef ANDROID_NDK_8_OR_ABOVE - #include -#endif - - -#ifdef ANDROID_LOG -#include -#include - -#undef WEBRTC_TRACE -#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTC*", __VA_ARGS__) -#else -#include "trace.h" -#endif - -namespace webrtc { - -AndroidSurfaceViewRenderer::AndroidSurfaceViewRenderer(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) -: - VideoRenderAndroid(id,videoRenderType,window,fullscreen), - _javaRenderObj(NULL), - _javaRenderClass(NULL) -{ -} - -AndroidSurfaceViewRenderer::~AndroidSurfaceViewRenderer() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "AndroidSurfaceViewRenderer dtor"); - if(g_jvm) - { - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not attach thread to JVM (%d, %p)", __FUNCTION__, res, env); - env=NULL; - } - else - { - isAttached = true; - } - } - env->DeleteGlobalRef(_javaRenderObj); - env->DeleteGlobalRef(_javaRenderClass); - - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - } -} - - -WebRtc_Word32 -AndroidSurfaceViewRenderer::Init() -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - if (!g_jvm) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "(%s): Not a valid Java VM pointer.", __FUNCTION__); - return -1; - } - if(!_ptrWindow) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "(%s): No window have been provided.", __FUNCTION__); - return -1; - } - - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = g_jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not attach thread to JVM (%d, %p)", __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - // get the ViESurfaceRender class - jclass javaRenderClassLocal = env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer"); - if (!javaRenderClassLocal) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not find ViESurfaceRenderer", __FUNCTION__); - return -1; - } - - // create a global reference to the class (to tell JNI that we are referencing it after this function has returned) - _javaRenderClass = reinterpret_cast(env->NewGlobalRef(javaRenderClassLocal)); - if (!_javaRenderClass) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not create Java ViESurfaceRenderer class reference", __FUNCTION__); - return -1; - } - - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaRenderClassLocal); - - // get the method ID for the constructor - jmethodID cid = env->GetMethodID(_javaRenderClass, "", "(Landroid/view/SurfaceView;)V"); - if (cid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get constructor ID", __FUNCTION__); - return -1; /* exception thrown */ - } - - // construct the object - jobject javaRenderObjLocal = env->NewObject(_javaRenderClass, cid, _ptrWindow); - if (!javaRenderObjLocal) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not create Java Render", __FUNCTION__); - return -1; - } - - // create a reference to the object (to tell JNI that we are referencing it - // after this function has returned) - _javaRenderObj = env->NewGlobalRef(javaRenderObjLocal); - if (!_javaRenderObj) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not create Java SurfaceRender object reference", __FUNCTION__); - return -1; - } - - // Detach this thread if it was attached - if (isAttached) - { - if (g_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s done", __FUNCTION__); - return 0; - -} -AndroidStream* -AndroidSurfaceViewRenderer::CreateAndroidRenderChannel(WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom, - VideoRenderAndroid& renderer) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: Id %d", __FUNCTION__,streamId); - AndroidSurfaceViewChannel* stream=new AndroidSurfaceViewChannel(streamId,g_jvm,renderer,_javaRenderObj); - if(stream && stream->Init(zOrder,left,top,right,bottom)==0) - return stream; - else - delete stream; - return NULL; -} - - - - - - -AndroidSurfaceViewChannel::AndroidSurfaceViewChannel(WebRtc_UWord32 streamId,JavaVM* jvm,VideoRenderAndroid& renderer,jobject javaRenderObj) -: -_id(streamId), -_renderCritSect(*CriticalSectionWrapper::CreateCriticalSection()), -_renderer(renderer), -_jvm(jvm), -_javaRenderObj(javaRenderObj), -_bitmapWidth(0), -_bitmapHeight(0) -{ - -} -AndroidSurfaceViewChannel::~AndroidSurfaceViewChannel() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "AndroidSurfaceViewChannel dtor"); - delete &_renderCritSect; - if(_jvm) - { - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not attach thread to JVM (%d, %p)", __FUNCTION__, res, env); - env=NULL; - } - else - { - isAttached = true; - } - } - -#ifdef ANDROID_NDK_8_OR_ABOVE - env->DeleteGlobalRef(_javaBitmapObj); -#else - env->DeleteGlobalRef(_javaByteBufferObj); -#endif - if (isAttached) - { - if (_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - } -} - -WebRtc_Word32 -AndroidSurfaceViewChannel::Init(WebRtc_Word32 /*zOrder*/, - const float left, - const float top, - const float right, - const float bottom) -{ - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: AndroidSurfaceViewChannel", __FUNCTION__); - if (!_jvm) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer,_id, "%s: Not a valid Java VM pointer", __FUNCTION__); - return -1; - } - - if((top>1 || top<0) || (right>1 || right<0) || (bottom>1 || bottom<0) || (left>1 || left<0)) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Wrong coordinates", - __FUNCTION__); - return -1; - } - - - // get the JNI env for this thread - bool isAttached = false; - JNIEnv* env = NULL; - if (_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) - { - // try to attach the thread and get the env - // Attach this thread to JVM - jint res = _jvm->AttachCurrentThread(&env, NULL); - - // Get the JNI env for this thread - if ((res < 0) || !env) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not attach thread to JVM (%d, %p)", __FUNCTION__, res, env); - return -1; - } - isAttached = true; - } - - jclass javaRenderClass = env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer"); - if (!javaRenderClass) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not find ViESurfaceRenderer", __FUNCTION__); - return -1; - } -#ifdef ANDROID_NDK_8_OR_ABOVE - // get the method ID for the CreateBitmap - _createBitmapCid = env->GetMethodID(_javaRenderClass, "CreateBitmap", "(II)Landroid/graphics/Bitmap;"); - if (_createBitmapCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get CreateBitmap ID", __FUNCTION__); - return -1; /* exception thrown */ - } - // get the method ID for the DrawBitmap function - _drawBitmapCid = env->GetMethodID(_javaRenderClass, "DrawBitmap", "()V"); - if (_drawBitmapCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get DrawBitmap ID", __FUNCTION__); - return -1; /* exception thrown */ - } -#else - // get the method ID for the CreateIntArray - _createByteBufferCid = env->GetMethodID(javaRenderClass, "CreateByteBuffer", "(II)Ljava/nio/ByteBuffer;"); - if (_createByteBufferCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get CreateByteBuffer ID", __FUNCTION__); - return -1; /* exception thrown */ - } - - // get the method ID for the DrawByteBuffer function - _drawByteBufferCid = env->GetMethodID(javaRenderClass, "DrawByteBuffer", "()V"); - if (_drawByteBufferCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get DrawByteBuffer ID", __FUNCTION__); - return -1; /* exception thrown */ - } -#endif - - // get the method ID for the SetCoordinates function - _setCoordinatesCid = env->GetMethodID(javaRenderClass, "SetCoordinates", "(FFFF)V"); - if (_setCoordinatesCid == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not get SetCoordinates ID", __FUNCTION__); - return -1; /* exception thrown */ - } - - env->CallVoidMethod(_javaRenderObj,_setCoordinatesCid,left,top,right,bottom); - - // Detach this thread if it was attached - if (isAttached) - { - if (_jvm->DetachCurrentThread() < 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s: Could not detach thread from JVM", __FUNCTION__); - } - } - - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: AndroidSurfaceViewChannel done", __FUNCTION__); - return 0; -} - - -WebRtc_Word32 AndroidSurfaceViewChannel::RenderFrame(const WebRtc_UWord32 /*streamId*/, VideoFrame& videoFrame) -{ - // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s:" ,__FUNCTION__); - _renderCritSect.Enter(); - _bufferToRender.SwapFrame(videoFrame); - _renderCritSect.Leave(); - _renderer.ReDraw(); - return 0; -} - - -/*Implements AndroidStream - * Calls the Java object and render the buffer in _bufferToRender - */ -void AndroidSurfaceViewChannel::DeliverFrame(JNIEnv* jniEnv) -{ - _renderCritSect.Enter(); -// TickTime timeNow=TickTime::Now(); - - -#ifdef ANDROID_NDK_8_OR_ABOVE - if(_bitmapWidth!=_bufferToRender.Width() || _bitmapHeight!=_bufferToRender.Height()) - { - - // Create the bitmap to write to - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: Creating bitmap %u %u", __FUNCTION__,_bufferToRender.Width(),_bufferToRender.Height()); - if(_javaBitmapObj) - { - jniEnv->DeleteGlobalRef(_javaBitmapObj); - _javaBitmapObj=NULL; - } - jobject javaBitmap=jniEnv->CallObjectMethod(_javaRenderObj,_createBitmapCid,videoFrame.Width(),videoFrame.Height()); - _javaBitmapObj = jniEnv->NewGlobalRef(javaBitmap); - if (!_javaBitmapObj) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not create Java Bitmap object reference", __FUNCTION__); - _renderCritSect.Leave(); - return; - } - else - { - _bitmapWidth=_bufferToRender.Width(); - _bitmapHeight=_bufferToRender.Height(); - } - } - void* pixels; - if (_javaBitmapObj && AndroidBitmap_lockPixels(jniEnv, _javaBitmapObj, &pixels) >= 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: Locked bitmap", __FUNCTION__); - // Convert I420 straight into the Java bitmap. - const int conversionResult=ConvertI420ToRGB565( (unsigned char* )_bufferToRender.Buffer(), (unsigned char* ) pixels, _bitmapWidth, _bitmapHeight); - if(conversionResult<=0) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Color conversion failed.", __FUNCTION__); - } - - AndroidBitmap_unlockPixels(jniEnv, _javaBitmapObj); - - //Draw the Surface - jniEnv->CallVoidMethod(_javaRenderObj,_drawCid); - - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Could not lock bitmap", __FUNCTION__); - } - _renderCritSect.Leave(); - -#else - if(_bitmapWidth!=_bufferToRender.Width() || _bitmapHeight!=_bufferToRender.Height()) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s: New render size %d %d",__FUNCTION__, _bufferToRender.Width(), _bufferToRender.Height()); - if(_javaByteBufferObj) - { - jniEnv->DeleteGlobalRef(_javaByteBufferObj); - _javaByteBufferObj=NULL; - _directBuffer=NULL; - } - jobject javaByteBufferObj=jniEnv->CallObjectMethod(_javaRenderObj,_createByteBufferCid,_bufferToRender.Width(),_bufferToRender.Height()); - _javaByteBufferObj = jniEnv->NewGlobalRef(javaByteBufferObj); - if (!_javaByteBufferObj) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not create Java ByteBuffer object reference", __FUNCTION__); - _renderCritSect.Leave(); - return; - } - else - { - _directBuffer=(unsigned char*) jniEnv->GetDirectBufferAddress(_javaByteBufferObj); - _bitmapWidth=_bufferToRender.Width(); - _bitmapHeight=_bufferToRender.Height(); - } - } - - if(_javaByteBufferObj && _bitmapWidth && _bitmapHeight) - { - const int conversionResult=ConvertI420ToRGB565Android((unsigned char* )_bufferToRender.Buffer(), _directBuffer, _bitmapWidth, _bitmapHeight); - if(conversionResult<=0) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Color conversion failed.", __FUNCTION__); - _renderCritSect.Leave(); - return; - } - } - _renderCritSect.Leave(); - //Draw the Surface - jniEnv->CallVoidMethod(_javaRenderObj,_drawByteBufferCid); -#endif - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s: time to deliver %lld" ,__FUNCTION__,(TickTime::Now()-timeNow).Milliseconds()); -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/Android/video_render_android_surface_view.h b/modules/video_render/main/source/Android/video_render_android_surface_view.h deleted file mode 100644 index f55e60bf6..000000000 --- a/modules/video_render/main/source/Android/video_render_android_surface_view.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ - -#include - -#include "video_render_defines.h" - -#include "video_render_android_impl.h" - -namespace webrtc { -class CriticalSectionWrapper; - - -class AndroidSurfaceViewChannel: public AndroidStream -{ - -public: - AndroidSurfaceViewChannel(WebRtc_UWord32 streamId, - JavaVM* jvm, - VideoRenderAndroid& renderer, - jobject javaRenderObj); - ~AndroidSurfaceViewChannel(); - - WebRtc_Word32 Init(WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - //Implement VideoRenderCallback - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - - //Implements AndroidStream - virtual void DeliverFrame(JNIEnv* jniEnv); - -private: - WebRtc_UWord32 _id; - CriticalSectionWrapper& _renderCritSect; - - VideoFrame _bufferToRender; - VideoRenderAndroid& _renderer; - JavaVM* _jvm; - jobject _javaRenderObj; - -#ifdef ANDROID_NDK_8_OR_ABOVE - jclass _javaBitmapClass; - jmethodID _createBitmapCid; - jobject _javaBitmapObj; - jmethodID _drawBitmapCid; -#else - jobject _javaByteBufferObj; - unsigned char* _directBuffer; - jmethodID _createByteBufferCid; - jmethodID _drawByteBufferCid; -#endif - jmethodID _setCoordinatesCid; - unsigned int _bitmapWidth; - unsigned int _bitmapHeight; -}; - -class AndroidSurfaceViewRenderer: private VideoRenderAndroid -{ -public: - AndroidSurfaceViewRenderer(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen); - ~AndroidSurfaceViewRenderer(); - WebRtc_Word32 Init(); - virtual AndroidStream* CreateAndroidRenderChannel(WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom, - VideoRenderAndroid& renderer); -private: - jobject _javaRenderObj; - jclass _javaRenderClass; - -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ diff --git a/modules/video_render/main/source/Android/video_render_opengles20.cc b/modules/video_render/main/source/Android/video_render_opengles20.cc deleted file mode 100644 index 8f4e5c5a6..000000000 --- a/modules/video_render/main/source/Android/video_render_opengles20.cc +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include - -#include -#include - -#include "video_render_opengles20.h" - -//#define ANDROID_LOG - -#ifdef ANDROID_LOG -#include -#include - -#undef WEBRTC_TRACE -#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) -#else -#include "trace.h" -#endif - -namespace webrtc { - -const char VideoRenderOpenGles20::g_indices[] = { 0, 3, 2, 0, 2, 1 }; - -const char VideoRenderOpenGles20::g_vertextShader[] = { - "attribute vec4 aPosition;\n" - "attribute vec2 aTextureCoord;\n" - "varying vec2 vTextureCoord;\n" - "void main() {\n" - " gl_Position = aPosition;\n" - " vTextureCoord = aTextureCoord;\n" - "}\n" }; - -// The fragment shader. -// Do YUV to RGB565 conversion. -const char VideoRenderOpenGles20::g_fragmentShader[] = { - "precision mediump float;\n" - "uniform sampler2D Ytex;\n" - "uniform sampler2D Utex,Vtex;\n" - "varying vec2 vTextureCoord;\n" - "void main(void) {\n" - " float nx,ny,r,g,b,y,u,v;\n" - " mediump vec4 txl,ux,vx;" - " nx=vTextureCoord[0];\n" - " ny=vTextureCoord[1];\n" - " y=texture2D(Ytex,vec2(nx,ny)).r;\n" - " u=texture2D(Utex,vec2(nx,ny)).r;\n" - " v=texture2D(Vtex,vec2(nx,ny)).r;\n" - - //" y = v;\n"+ - " y=1.1643*(y-0.0625);\n" - " u=u-0.5;\n" - " v=v-0.5;\n" - - " r=y+1.5958*v;\n" - " g=y-0.39173*u-0.81290*v;\n" - " b=y+2.017*u;\n" - " gl_FragColor=vec4(r,g,b,1.0);\n" - "}\n" }; - -VideoRenderOpenGles20::VideoRenderOpenGles20(WebRtc_Word32 id) : - _id(id), - _textureWidth(-1), - _textureHeight(-1) - -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", - __FUNCTION__, (int) _id); - - const GLfloat vertices[20] = { - // X, Y, Z, U, V - -1, -1, 0, 0, 1, // Bottom Left - 1, -1, 0, 1, 1, //Bottom Right - 1, 1, 0, 1, 0, //Top Right - -1, 1, 0, 0, 0 }; //Top Left - - memcpy(_vertices, vertices, sizeof(_vertices)); -} - -VideoRenderOpenGles20::~VideoRenderOpenGles20() -{ - -} - -WebRtc_Word32 VideoRenderOpenGles20::Setup(WebRtc_Word32 width, - WebRtc_Word32 height) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s: width %d, height %d", __FUNCTION__, (int) width, - (int) height); - - printGLString("Version", GL_VERSION); - printGLString("Vendor", GL_VENDOR); - printGLString("Renderer", GL_RENDERER); - printGLString("Extensions", GL_EXTENSIONS); - - int maxTextureImageUnits[2]; - int maxTextureSize[2]; - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, maxTextureImageUnits); - glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s: number of textures %d, size %d", __FUNCTION__, - (int) maxTextureImageUnits[0], (int) maxTextureSize[0]); - - _program = createProgram(g_vertextShader, g_fragmentShader); - if (!_program) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not create program", __FUNCTION__); - return -1; - } - - int positionHandle = glGetAttribLocation(_program, "aPosition"); - checkGlError("glGetAttribLocation aPosition"); - if (positionHandle == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not get aPosition handle", __FUNCTION__); - return -1; - } - int textureHandle = glGetAttribLocation(_program, "aTextureCoord"); - checkGlError("glGetAttribLocation aTextureCoord"); - if (textureHandle == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not get aTextureCoord handle", __FUNCTION__); - return -1; - } - - // set the vertices array in the shader - // _vertices contains 4 vertices with 5 coordinates. 3 for (xyz) for the vertices and 2 for the texture - glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 5 - * sizeof(GLfloat), _vertices); - checkGlError("glVertexAttribPointer aPosition"); - - glEnableVertexAttribArray(positionHandle); - checkGlError("glEnableVertexAttribArray positionHandle"); - - // set the texture coordinate array in the shader - // _vertices contains 4 vertices with 5 coordinates. 3 for (xyz) for the vertices and 2 for the texture - glVertexAttribPointer(textureHandle, 2, GL_FLOAT, false, 5 - * sizeof(GLfloat), &_vertices[3]); - checkGlError("glVertexAttribPointer maTextureHandle"); - glEnableVertexAttribArray(textureHandle); - checkGlError("glEnableVertexAttribArray textureHandle"); - - glUseProgram(_program); - int i = glGetUniformLocation(_program, "Ytex"); - checkGlError("glGetUniformLocation"); - glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */ - checkGlError("glUniform1i Ytex"); - - i = glGetUniformLocation(_program, "Utex"); - checkGlError("glGetUniformLocation Utex"); - glUniform1i(i, 1); /* Bind Utex to texture unit 1 */ - checkGlError("glUniform1i Utex"); - - i = glGetUniformLocation(_program, "Vtex"); - checkGlError("glGetUniformLocation"); - glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */ - checkGlError("glUniform1i"); - - glViewport(0, 0, width, height); - checkGlError("glViewport"); - return 0; - -} -/* - * SetCoordinates - * Sets the coordinates where the stream shall be rendered. Values must be between 0 and 1. - */ -WebRtc_Word32 VideoRenderOpenGles20::SetCoordinates(WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - if ((top > 1 || top < 0) || (right > 1 || right < 0) || (bottom > 1 - || bottom < 0) || (left > 1 || left < 0)) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Wrong coordinates", __FUNCTION__); - return -1; - } - /* - // X, Y, Z, U, V - -1, -1, 0, 0, 1, // Bottom Left - 1, -1, 0, 1, 1, //Bottom Right - 1, 1, 0, 1, 0, //Top Right - -1, 1, 0, 0, 0 }; //Top Left - */ - // Bottom Left - _vertices[0] = (left * 2) - 1; - _vertices[1] = -1 * (2 * bottom) + 1; - _vertices[2] = zOrder; - - //Bottom Right - _vertices[5] = (right * 2) - 1; - _vertices[6] = -1 * (2 * bottom) + 1; - _vertices[7] = zOrder; - - //Top Right - _vertices[10] = (right * 2) - 1; - _vertices[11] = -1 * (2 * top) + 1; - _vertices[12] = zOrder; - - //Top Left - _vertices[15] = (left * 2) - 1; - _vertices[16] = -1 * (2 * top) + 1; - _vertices[17] = zOrder; - - return 0; - -} -WebRtc_Word32 VideoRenderOpenGles20::Render(const VideoFrame& frameToRender) -{ - - if (frameToRender.Length() == 0) - { - return -1; - } - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", - __FUNCTION__, (int) _id); - - //glClearColor(0.0f, 0.0f, 1.0f, 1.0f); - //glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - - glUseProgram(_program); - checkGlError("glUseProgram"); - - if (_textureWidth != (GLsizei) frameToRender.Width() || _textureHeight - != (GLsizei) frameToRender.Height()) - { - SetupTextures(frameToRender); - } - else - { - UpdateTextures(frameToRender); - } - - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, g_indices); - checkGlError("glDrawArrays"); - - return 0; -} - -GLuint VideoRenderOpenGles20::loadShader(GLenum shaderType, - const char* pSource) -{ - GLuint shader = glCreateShader(shaderType); - if (shader) - { - glShaderSource(shader, 1, &pSource, NULL); - glCompileShader(shader); - GLint compiled = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - if (!compiled) - { - GLint infoLen = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); - if (infoLen) - { - char* buf = (char*) malloc(infoLen); - if (buf) - { - glGetShaderInfoLog(shader, infoLen, NULL, buf); - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not compile shader %d: %s", - __FUNCTION__, shaderType, buf); - free(buf); - } - glDeleteShader(shader); - shader = 0; - } - } - } - return shader; -} - -GLuint VideoRenderOpenGles20::createProgram(const char* pVertexSource, - const char* pFragmentSource) -{ - GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); - if (!vertexShader) - { - return 0; - } - - GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); - if (!pixelShader) - { - return 0; - } - - GLuint program = glCreateProgram(); - if (program) - { - glAttachShader(program, vertexShader); - checkGlError("glAttachShader"); - glAttachShader(program, pixelShader); - checkGlError("glAttachShader"); - glLinkProgram(program); - GLint linkStatus = GL_FALSE; - glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); - if (linkStatus != GL_TRUE) - { - GLint bufLength = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); - if (bufLength) - { - char* buf = (char*) malloc(bufLength); - if (buf) - { - glGetProgramInfoLog(program, bufLength, NULL, buf); - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Could not link program: %s", - __FUNCTION__, buf); - free(buf); - } - } - glDeleteProgram(program); - program = 0; - } - } - return program; -} - -void VideoRenderOpenGles20::printGLString(const char *name, GLenum s) -{ - const char *v = (const char *) glGetString(s); - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "GL %s = %s\n", - name, v); -} - -void VideoRenderOpenGles20::checkGlError(const char* op) -{ -#ifdef ANDROID_LOG - for (GLint error = glGetError(); error; error - = glGetError()) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "after %s() glError (0x%x)\n", op, error); - } -#else - return; -#endif -} - -void VideoRenderOpenGles20::SetupTextures(const VideoFrame& frameToRender) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, - "%s: width %d, height %d length %u", __FUNCTION__, - frameToRender.Width(), frameToRender.Height(), - frameToRender.Length()); - - const GLsizei width = frameToRender.Width(); - const GLsizei height = frameToRender.Height(); - - glGenTextures(3, _textureIds); //Generate the Y, U and V texture - GLuint currentTextureId = _textureIds[0]; // Y - glActiveTexture( GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, - (const GLvoid*) frameToRender.Buffer()); - - currentTextureId = _textureIds[1]; // U - glActiveTexture( GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - const WebRtc_UWord8* uComponent = frameToRender.Buffer() + width * height; - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, (const GLvoid*) uComponent); - - currentTextureId = _textureIds[2]; // V - glActiveTexture( GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - const WebRtc_UWord8* vComponent = uComponent + (width * height) / 4; - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width / 2, height / 2, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, (const GLvoid*) vComponent); - checkGlError("SetupTextures"); - - _textureWidth = width; - _textureHeight = height; -} - -void VideoRenderOpenGles20::UpdateTextures(const VideoFrame& frameToRender) -{ - const GLsizei width = frameToRender.Width(); - const GLsizei height = frameToRender.Height(); - - GLuint currentTextureId = _textureIds[0]; // Y - glActiveTexture( GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, - GL_UNSIGNED_BYTE, (const GLvoid*) frameToRender.Buffer()); - - currentTextureId = _textureIds[1]; // U - glActiveTexture( GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - const WebRtc_UWord8* uComponent = frameToRender.Buffer() + width * height; - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, - GL_LUMINANCE, GL_UNSIGNED_BYTE, (const GLvoid*) uComponent); - - currentTextureId = _textureIds[2]; // V - glActiveTexture( GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, currentTextureId); - const WebRtc_UWord8* vComponent = uComponent + (width * height) / 4; - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, - GL_LUMINANCE, GL_UNSIGNED_BYTE, (const GLvoid*) vComponent); - checkGlError("UpdateTextures"); - -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/Android/video_render_opengles20.h b/modules/video_render/main/source/Android/video_render_opengles20.h deleted file mode 100644 index 379b1e752..000000000 --- a/modules/video_render/main/source/Android/video_render_opengles20.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ - -#include "video_render_defines.h" - -#include -#include - -namespace webrtc -{ - -class VideoRenderOpenGles20 -{ -public: - VideoRenderOpenGles20(WebRtc_Word32 id); - ~VideoRenderOpenGles20(); - - WebRtc_Word32 Setup(WebRtc_Word32 widht, WebRtc_Word32 height); - WebRtc_Word32 Render(const VideoFrame& frameToRender); - WebRtc_Word32 SetCoordinates(WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - -private: - void printGLString(const char *name, GLenum s); - void checkGlError(const char* op); - GLuint loadShader(GLenum shaderType, const char* pSource); - GLuint createProgram(const char* pVertexSource, const char* pFragmentSource); - void SetupTextures(const VideoFrame& frameToRender); - void UpdateTextures(const VideoFrame& frameToRender); - - WebRtc_Word32 _id; - GLuint _textureIds[3]; // Texture id of Y,U and V texture. - GLuint _program; - GLuint _vPositionHandle; - GLsizei _textureWidth; - GLsizei _textureHeight; - - GLfloat _vertices[20]; - static const char g_indices[]; - - static const char g_vertextShader[]; - static const char g_fragmentShader[]; - -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ diff --git a/modules/video_render/main/source/external/video_render_external_impl.cc b/modules/video_render/main/source/external/video_render_external_impl.cc deleted file mode 100644 index 7abb09a06..000000000 --- a/modules/video_render/main/source/external/video_render_external_impl.cc +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_external_impl.h" - -namespace webrtc { - -VideoRenderExternalImpl::VideoRenderExternalImpl( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : - _id(id), _critSect(*CriticalSectionWrapper::CreateCriticalSection()), - _window(window), _fullscreen(fullscreen) -{ -} - -VideoRenderExternalImpl::~VideoRenderExternalImpl() -{ - delete &_critSect; -} - -WebRtc_Word32 VideoRenderExternalImpl::Init() -{ - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_critSect); - _id = id; - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::ChangeWindow(void* window) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -VideoRenderCallback* -VideoRenderExternalImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - return this; -} - -WebRtc_Word32 VideoRenderExternalImpl::DeleteIncomingRenderStream( - const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::GetIncomingRenderStreamProperties( - const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - CriticalSectionScoped cs(_critSect); - - zOrder = 0; - left = 0; - top = 0; - right = 0; - bottom = 0; - - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::StartRender() -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::StopRender() -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -VideoRenderType VideoRenderExternalImpl::RenderType() -{ - return kRenderExternal; -} - -RawVideoType VideoRenderExternalImpl::PerferedVideoType() -{ - return kVideoI420; -} - -bool VideoRenderExternalImpl::FullScreen() -{ - CriticalSectionScoped cs(_critSect); - return _fullscreen; -} - -WebRtc_Word32 VideoRenderExternalImpl::GetGraphicsMemory( - WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const -{ - totalGraphicsMemory = 0; - availableGraphicsMemory = 0; - return -1; -} - -WebRtc_Word32 VideoRenderExternalImpl::GetScreenResolution( - WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const -{ - CriticalSectionScoped cs(_critSect); - screenWidth = 0; - screenHeight = 0; - return 0; -} - -WebRtc_UWord32 VideoRenderExternalImpl::RenderFrameRate( - const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::SetStreamCropping( - const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::ConfigureRenderer( - const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::SetTransparentBackground( - const bool enable) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::SetText( - const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -WebRtc_Word32 VideoRenderExternalImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - return 0; -} - -// VideoRenderCallback -WebRtc_Word32 VideoRenderExternalImpl::RenderFrame( - const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) -{ - return 0; -} -} //namespace webrtc - diff --git a/modules/video_render/main/source/external/video_render_external_impl.h b/modules/video_render/main/source/external/video_render_external_impl.h deleted file mode 100644 index e1374f4e8..000000000 --- a/modules/video_render/main/source/external/video_render_external_impl.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ - -#include "i_video_render.h" -#include "critical_section_wrapper.h" -#include "module_common_types.h" - -namespace webrtc { - -// Class definitions -class VideoRenderExternalImpl: IVideoRender, public VideoRenderCallback -{ -public: - /* - * Constructor/destructor - */ - - VideoRenderExternalImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, const bool fullscreen); - - virtual ~VideoRenderExternalImpl(); - - virtual WebRtc_Word32 Init(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 - GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - - // VideoRenderCallback - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _critSect; - void* _window; - bool _fullscreen; -}; - -} //namespace webrtc - - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ diff --git a/modules/video_render/main/source/i_video_render.h b/modules/video_render/main/source/i_video_render.h deleted file mode 100644 index 2799a79e3..000000000 --- a/modules/video_render/main/source/i_video_render.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ - -#include "video_render.h" - -namespace webrtc { - -// Class definitions -class IVideoRender -{ -public: - /* - * Constructor/destructor - */ - - virtual ~IVideoRender() - { - }; - - virtual WebRtc_Word32 Init() = 0; - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; - - virtual WebRtc_Word32 ChangeWindow(void* window) = 0; - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId) = 0; - - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const = 0; - // Implemented in common code? - //virtual WebRtc_UWord32 GetNumIncomingRenderStreams() const = 0; - //virtual bool HasIncomingRenderStream(const WebRtc_UWord16 stramId) const = 0; - - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender() = 0; - - virtual WebRtc_Word32 StopRender() = 0; - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - virtual VideoRenderType RenderType() = 0; - - virtual RawVideoType PerferedVideoType() = 0; - - virtual bool FullScreen() = 0; - - // TODO: This should be treated in platform specific code only - virtual WebRtc_Word32 - GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const = 0; - - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const = 0; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId) = 0; - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable) = 0; - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float rigth, - const float bottom) = 0; - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) = 0; - -}; -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ diff --git a/modules/video_render/main/source/incoming_video_stream.cc b/modules/video_render/main/source/incoming_video_stream.cc deleted file mode 100644 index 63d382ca7..000000000 --- a/modules/video_render/main/source/incoming_video_stream.cc +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "incoming_video_stream.h" - -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "video_render_frames.h" -#include "tick_util.h" -#include "map_wrapper.h" -#include "vplib.h" - -#include - -// Platform specifics -#if defined(_WIN32) -#include -#elif defined(WEBRTC_LINUX) -#include -#include -#else -#include -#endif - -namespace webrtc { -IncomingVideoStream::IncomingVideoStream(const WebRtc_Word32 moduleId, - const WebRtc_UWord32 streamId) : - _moduleId(moduleId), - _streamId(streamId), - _streamCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _bufferCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _threadCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _ptrIncomingRenderThread(), - _deliverBufferEvent(*EventWrapper::Create()), - _running(false), - _ptrExternalCallback(NULL), - _ptrRenderCallback(NULL), - _renderBuffers(*(new VideoRenderFrames)), - _callbackVideoType(kVideoI420), - _callbackWidth(0), - _callbackHeight(0), - _incomingRate(0), - _lastRateCalculationTimeMs(0), - _numFramesSinceLastCalculation(0), - _lastRenderedFrame(), - _startImage(), - _timeoutImage(), - _timeoutTime(), - _mirrorFramesEnabled(false), - _mirroring(), - _transformedVideoFrame() -{ - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, _moduleId, - "%s created for stream %d", __FUNCTION__, streamId); -} - -IncomingVideoStream::~IncomingVideoStream() -{ - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, _moduleId, - "%s deleted for stream %d", __FUNCTION__, _streamId); - - Stop(); - - // _ptrIncomingRenderThread - Delete in stop - delete &_renderBuffers; - delete &_streamCritsect; - delete &_bufferCritsect; - delete &_threadCritsect; - delete &_deliverBufferEvent; - -} - -WebRtc_Word32 IncomingVideoStream::ChangeModuleId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_streamCritsect); - - _moduleId = id; - return 0; -} - -VideoRenderCallback* -IncomingVideoStream::ModuleCallback() -{ - CriticalSectionScoped cs(_streamCritsect); - return this; -} - -WebRtc_Word32 IncomingVideoStream::RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) -{ - - CriticalSectionScoped csS(_streamCritsect); - WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, _moduleId, - "%s for stream %d, render time: %u", __FUNCTION__, _streamId, - videoFrame.RenderTimeMs()); - - if (!_running) - { - WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, _moduleId, - "%s: Not running", __FUNCTION__); - return -1; - } - - if (true == _mirrorFramesEnabled) - { - _transformedVideoFrame.VerifyAndAllocate(videoFrame.Length()); - if (_mirroring.mirrorXAxis) - { - MirrorI420UpDown(videoFrame.Buffer(), - _transformedVideoFrame.Buffer(), - videoFrame.Width(), videoFrame.Height()); - _transformedVideoFrame.SetLength(videoFrame.Length()); - _transformedVideoFrame.SetWidth(videoFrame.Width()); - _transformedVideoFrame.SetHeight(videoFrame.Height()); - videoFrame.SwapFrame(_transformedVideoFrame); - } - if (_mirroring.mirrorYAxis) - { - MirrorI420LeftRight(videoFrame.Buffer(), - _transformedVideoFrame.Buffer(), - videoFrame.Width(), videoFrame.Height()); - _transformedVideoFrame.SetLength(videoFrame.Length()); - _transformedVideoFrame.SetWidth(videoFrame.Width()); - _transformedVideoFrame.SetHeight(videoFrame.Height()); - videoFrame.SwapFrame(_transformedVideoFrame); - } - } - - // Rate statistics - _numFramesSinceLastCalculation++; - WebRtc_Word64 nowMs = TickTime::MillisecondTimestamp(); - if (nowMs >= _lastRateCalculationTimeMs + KFrameRatePeriodMs) - { - _incomingRate = (WebRtc_UWord32) (1000 * _numFramesSinceLastCalculation - / (nowMs - _lastRateCalculationTimeMs)); - _numFramesSinceLastCalculation = 0; - _lastRateCalculationTimeMs = nowMs; - } - - // Insert frame - CriticalSectionScoped csB(_bufferCritsect); - if (_renderBuffers.AddFrame(&videoFrame) == 1) - _deliverBufferEvent.Set(); - - return 0; -} - -WebRtc_Word32 IncomingVideoStream::SetStartImage(const VideoFrame& videoFrame) -{ - CriticalSectionScoped csS(_threadCritsect); - return _startImage.CopyFrame(videoFrame); -} - -WebRtc_Word32 IncomingVideoStream::SetTimeoutImage(const VideoFrame& videoFrame, - const WebRtc_UWord32 timeout) -{ - CriticalSectionScoped csS(_threadCritsect); - _timeoutTime = timeout; - return _timeoutImage.CopyFrame(videoFrame); -} - -WebRtc_Word32 IncomingVideoStream::SetRenderCallback(VideoRenderCallback* renderCallback) -{ - CriticalSectionScoped cs(_streamCritsect); - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _moduleId, - "%s(%x) for stream %d", __FUNCTION__, renderCallback, - _streamId); - _ptrRenderCallback = renderCallback; - return 0; -} - -WebRtc_Word32 IncomingVideoStream::EnableMirroring(const bool enable, - const bool mirrorXAxis, - const bool mirrorYAxis) -{ - CriticalSectionScoped cs(_streamCritsect); - _mirrorFramesEnabled = enable; - _mirroring.mirrorXAxis = mirrorXAxis; - _mirroring.mirrorYAxis = mirrorYAxis; - - return 0; -} - -WebRtc_Word32 IncomingVideoStream::SetExternalCallback(VideoRenderCallback* externalCallback) -{ - CriticalSectionScoped cs(_streamCritsect); - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _moduleId, - "%s(%x) for stream %d", __FUNCTION__, externalCallback, - _streamId); - _ptrExternalCallback = externalCallback; - _callbackVideoType = kVideoI420; - _callbackWidth = 0; - _callbackHeight = 0; - return 0; -} - -WebRtc_Word32 IncomingVideoStream::Start() -{ - CriticalSectionScoped csS(_streamCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _moduleId, - "%s for stream %d", __FUNCTION__, _streamId); - if (_running) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _moduleId, - "%s: Already running", __FUNCTION__); - return 0; - } - - CriticalSectionScoped csT(_threadCritsect); - assert(_ptrIncomingRenderThread == NULL); - - _ptrIncomingRenderThread - = ThreadWrapper::CreateThread(IncomingVideoStreamThreadFun, this, - kRealtimePriority, - "IncomingVideoStreamThread"); - if (!_ptrIncomingRenderThread) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _moduleId, - "%s: No thread", __FUNCTION__); - return -1; - } - - unsigned int tId = 0; - if (_ptrIncomingRenderThread->Start(tId)) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _moduleId, - "%s: thread started: %u", __FUNCTION__, tId); - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _moduleId, - "%s: Could not start send thread", __FUNCTION__); - return -1; - } - _deliverBufferEvent.StartTimer(false, KEventStartupTimeMS); - - _running = true; - return 0; -} - -WebRtc_Word32 IncomingVideoStream::Stop() -{ - CriticalSectionScoped csStream(_streamCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _moduleId, - "%s for stream %d", __FUNCTION__, _streamId); - - if (!_running) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _moduleId, - "%s: Not running", __FUNCTION__); - return 0; - } - - _threadCritsect.Enter(); - if (_ptrIncomingRenderThread) - { - ThreadWrapper* ptrThread = _ptrIncomingRenderThread; - _ptrIncomingRenderThread = NULL; - ptrThread->SetNotAlive(); -#ifndef _WIN32 - _deliverBufferEvent.StopTimer(); -#endif - _threadCritsect.Leave(); - if (ptrThread->Stop()) - { - delete ptrThread; - } - else - { - assert(false); - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _moduleId, - "%s: Not able to stop thread, leaking", __FUNCTION__); - } - } - else - { - _threadCritsect.Leave(); - } - _running = false; - return 0; -} - -WebRtc_Word32 IncomingVideoStream::Reset() -{ - CriticalSectionScoped csStream(_streamCritsect); - CriticalSectionScoped csBuffer(_bufferCritsect); - - _renderBuffers.ReleaseAllFrames(); - return 0; -} - -WebRtc_UWord32 IncomingVideoStream::StreamId() const -{ - CriticalSectionScoped csStream(_streamCritsect); - return _streamId; -} - -WebRtc_UWord32 IncomingVideoStream::IncomingRate() const -{ - CriticalSectionScoped cs(_streamCritsect); - return _incomingRate; -} - -bool IncomingVideoStream::IncomingVideoStreamThreadFun(void* obj) -{ - return static_cast (obj)->IncomingVideoStreamProcess(); -} - -bool IncomingVideoStream::IncomingVideoStreamProcess() -{ - if (kEventError != _deliverBufferEvent.Wait(KEventMaxWaitTimeMs)) - { - if (_ptrIncomingRenderThread == NULL) - { - // Terminating - return false; - } - - _threadCritsect.Enter(); - - VideoFrame* ptrFrameToRender = NULL; - - // Get a new frame to render and the time for the frame after this one. - _bufferCritsect.Enter(); - ptrFrameToRender = _renderBuffers.FrameToRender(); - WebRtc_UWord32 waitTime = _renderBuffers.TimeToNextFrameRelease(); - _bufferCritsect.Leave(); - - // Set timer for next frame to render - if (waitTime > KEventMaxWaitTimeMs) - { - waitTime = KEventMaxWaitTimeMs; - } - _deliverBufferEvent.StartTimer(false, waitTime); - - if (!ptrFrameToRender) - { - if (_ptrRenderCallback) - { - if (_lastRenderedFrame.RenderTimeMs() == 0 - && _startImage.Size()) // And we have not rendered anything and have a start image - { - _tempFrame.CopyFrame(_startImage);// Copy the startimage if the renderer modifies the render buffer. - _ptrRenderCallback->RenderFrame(_streamId, _tempFrame); - } - else if (_timeoutImage.Size() - && _lastRenderedFrame.RenderTimeMs() + _timeoutTime - < TickTime::MillisecondTimestamp()) // We have rendered something a long time ago and have a timeout image - { - _tempFrame.CopyFrame(_timeoutImage); // Copy the timeoutImage if the renderer modifies the render buffer. - _ptrRenderCallback->RenderFrame(_streamId, _tempFrame); - } - } - - // No frame - _threadCritsect.Leave(); - return true; - } - - // Send frame for rendering - if (_ptrExternalCallback) - { - WEBRTC_TRACE(kTraceStream, - kTraceVideoRenderer, - _moduleId, - "%s: executing external renderer callback to deliver frame", - __FUNCTION__, ptrFrameToRender->RenderTimeMs()); - _ptrExternalCallback->RenderFrame(_streamId, *ptrFrameToRender); - } - else - { - if (_ptrRenderCallback) - { - WEBRTC_TRACE(kTraceStream, kTraceVideoRenderer, _moduleId, - "%s: Render frame, time: ", __FUNCTION__, - ptrFrameToRender->RenderTimeMs()); - _ptrRenderCallback->RenderFrame(_streamId, *ptrFrameToRender); - } - } - - // Release critsect before calling the module user - _threadCritsect.Leave(); - - // We're done with this frame, delete it. - if (ptrFrameToRender) - { - CriticalSectionScoped cs(_bufferCritsect); - _lastRenderedFrame.SwapFrame(*ptrFrameToRender); - _renderBuffers.ReturnFrame(ptrFrameToRender); - } - } - return true; -} -WebRtc_Word32 IncomingVideoStream::GetLastRenderedFrame(VideoFrame& videoFrame) const -{ - CriticalSectionScoped cs(_bufferCritsect); - return videoFrame.CopyFrame(_lastRenderedFrame); -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/incoming_video_stream.h b/modules/video_render/main/source/incoming_video_stream.h deleted file mode 100644 index cd2785d09..000000000 --- a/modules/video_render/main/source/incoming_video_stream.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_INCOMING_VIDEO_STREAM_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_INCOMING_VIDEO_STREAM_H_ - -#include "video_render.h" -#include "map_wrapper.h" - -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -class VideoRenderCallback; -class VideoRenderFrames; - -struct VideoMirroring -{ - bool mirrorXAxis; - bool mirrorYAxis; - VideoMirroring() : - mirrorXAxis(false), mirrorYAxis(false) - { - } -}; - -// Class definitions -class IncomingVideoStream: public VideoRenderCallback -{ -public: - /* - * VideoRenderer constructor/destructor - */ - IncomingVideoStream(const WebRtc_Word32 moduleId, - const WebRtc_UWord32 streamId); - ~IncomingVideoStream(); - - WebRtc_Word32 ChangeModuleId(const WebRtc_Word32 id); - - // Get callbck to deliver frames to the module - VideoRenderCallback* ModuleCallback(); - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - - // Set callback to the platform dependant code - WebRtc_Word32 SetRenderCallback(VideoRenderCallback* renderCallback); - - // Callback for file recording, snapshot, ... - WebRtc_Word32 SetExternalCallback(VideoRenderCallback* renderObject); - - /* - * Start/Stop - */ - WebRtc_Word32 Start(); - WebRtc_Word32 Stop(); - - // Clear all buffers - WebRtc_Word32 Reset(); - - /* - * Properties - */ - WebRtc_UWord32 StreamId() const; - WebRtc_UWord32 IncomingRate() const; - - /* - * - */ - WebRtc_Word32 GetLastRenderedFrame(VideoFrame& videoFrame) const; - - WebRtc_Word32 SetStartImage(const VideoFrame& videoFrame); - - WebRtc_Word32 SetTimeoutImage(const VideoFrame& videoFrame, - const WebRtc_UWord32 timeout); - - WebRtc_Word32 EnableMirroring(const bool enable, - const bool mirrorXAxis, - const bool mirrorYAxis); - -protected: - static bool IncomingVideoStreamThreadFun(void* obj); - bool IncomingVideoStreamProcess(); - -private: - - // Enums - enum - { - KEventStartupTimeMS = 10 - }; - enum - { - KEventMaxWaitTimeMs = 100 - }; - enum - { - KFrameRatePeriodMs = 1000 - }; - - WebRtc_Word32 _moduleId; - WebRtc_UWord32 _streamId; - CriticalSectionWrapper& _streamCritsect; // Critsects in allowed to enter order - CriticalSectionWrapper& _threadCritsect; - CriticalSectionWrapper& _bufferCritsect; - ThreadWrapper* _ptrIncomingRenderThread; - EventWrapper& _deliverBufferEvent; - bool _running; - - VideoRenderCallback* _ptrExternalCallback; - VideoRenderCallback* _ptrRenderCallback; - VideoRenderFrames& _renderBuffers; - - RawVideoType _callbackVideoType; - WebRtc_UWord32 _callbackWidth; - WebRtc_UWord32 _callbackHeight; - - WebRtc_UWord32 _incomingRate; - WebRtc_Word64 _lastRateCalculationTimeMs; - WebRtc_UWord16 _numFramesSinceLastCalculation; - VideoFrame _lastRenderedFrame; - VideoFrame _tempFrame; - VideoFrame _startImage; - VideoFrame _timeoutImage; - WebRtc_UWord32 _timeoutTime; - - bool _mirrorFramesEnabled; - VideoMirroring _mirroring; - VideoFrame _transformedVideoFrame; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_INCOMING_VIDEO_STREAM_H_ diff --git a/modules/video_render/main/source/linux/video_render_linux_impl.cc b/modules/video_render/main/source/linux/video_render_linux_impl.cc deleted file mode 100644 index a5e311abf..000000000 --- a/modules/video_render/main/source/linux/video_render_linux_impl.cc +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_linux_impl.h" - -#include "critical_section_wrapper.h" -#include "trace.h" -#include "video_x11_render.h" - -#include - -namespace webrtc { - -VideoRenderLinuxImpl::VideoRenderLinuxImpl( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, const bool fullscreen) : - _id(id), - _renderLinuxCritsect( - *CriticalSectionWrapper::CreateCriticalSection()), - _ptrWindow(window), _fullscreen(fullscreen), _ptrX11Render(NULL), - _renderType(videoRenderType) -{ -} - -VideoRenderLinuxImpl::~VideoRenderLinuxImpl() -{ - if (_ptrX11Render) - delete _ptrX11Render; - - delete &_renderLinuxCritsect; -} - -WebRtc_Word32 VideoRenderLinuxImpl::Init() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped cs(_renderLinuxCritsect); - _ptrX11Render = new VideoX11Render((Window) _ptrWindow); - if (!_ptrX11Render) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s", - "Failed to create instance of VideoX11Render object"); - return -1; - } - int retVal = _ptrX11Render->Init(); - if (retVal == -1) - { - return -1; - } - - return 0; - -} - -WebRtc_Word32 VideoRenderLinuxImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_renderLinuxCritsect); - - _id = id; - return 0; -} - -WebRtc_Word32 VideoRenderLinuxImpl::ChangeWindow(void* window) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - - CriticalSectionScoped cs(_renderLinuxCritsect); - _ptrWindow = window; - - if (_ptrX11Render) - { - return _ptrX11Render->ChangeWindow((Window) window); - } - - return -1; -} - -VideoRenderCallback* VideoRenderLinuxImpl::AddIncomingRenderStream( - const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_renderLinuxCritsect); - - VideoRenderCallback* renderCallback = NULL; - if (_ptrX11Render) - { - VideoX11Channel* renderChannel = - _ptrX11Render->CreateX11RenderChannel(streamId, zOrder, left, - top, right, bottom); - if (!renderChannel) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "Render channel creation failed for stream id: %d", - streamId); - return NULL; - } - renderCallback = (VideoRenderCallback *) renderChannel; - } - else - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "_ptrX11Render is NULL"); - return NULL; - } - return renderCallback; -} - -WebRtc_Word32 VideoRenderLinuxImpl::DeleteIncomingRenderStream( - const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_renderLinuxCritsect); - - if (_ptrX11Render) - { - return _ptrX11Render->DeleteX11RenderChannel(streamId); - } - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::GetIncomingRenderStreamProperties( - const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_renderLinuxCritsect); - - if (_ptrX11Render) - { - return _ptrX11Render->GetIncomingStreamProperties(streamId, zOrder, - left, top, right, - bottom); - } - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::StartRender() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - return 0; -} - -WebRtc_Word32 VideoRenderLinuxImpl::StopRender() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", - __FUNCTION__); - return 0; -} - -VideoRenderType VideoRenderLinuxImpl::RenderType() -{ - return kRenderX11; -} - -RawVideoType VideoRenderLinuxImpl::PerferedVideoType() -{ - return kVideoI420; -} - -bool VideoRenderLinuxImpl::FullScreen() -{ - return false; -} - -WebRtc_Word32 VideoRenderLinuxImpl::GetGraphicsMemory( - WebRtc_UWord64& /*totalGraphicsMemory*/, - WebRtc_UWord64& /*availableGraphicsMemory*/) const -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::GetScreenResolution( - WebRtc_UWord32& /*screenWidth*/, - WebRtc_UWord32& /*screenHeight*/) const -{ - return -1; -} - -WebRtc_UWord32 VideoRenderLinuxImpl::RenderFrameRate(const WebRtc_UWord32 /*streamId*/) -{ - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::SetStreamCropping( - const WebRtc_UWord32 /*streamId*/, - const float /*left*/, - const float /*top*/, - const float /*right*/, - const float /*bottom*/) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::SetTransparentBackground(const bool /*enable*/) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::ConfigureRenderer( - const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::SetText( - const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float rigth, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -WebRtc_Word32 VideoRenderLinuxImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s - not supported on Linux", __FUNCTION__); - return -1; -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/linux/video_render_linux_impl.h b/modules/video_render/main/source/linux/video_render_linux_impl.h deleted file mode 100644 index 10460ec4d..000000000 --- a/modules/video_render/main/source/linux/video_render_linux_impl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ - -#include "i_video_render.h" - -namespace webrtc { -class CriticalSectionWrapper; - -class VideoX11Render; - -// Class definitions -class VideoRenderLinuxImpl: IVideoRender -{ -public: - /* - * Constructor/destructor - */ - - VideoRenderLinuxImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, const bool fullscreen); - - virtual ~VideoRenderLinuxImpl(); - - virtual WebRtc_Word32 Init(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 - GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float rigth, const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _renderLinuxCritsect; - - void* _ptrWindow; - bool _fullscreen; - - // X11 Render - VideoX11Render* _ptrX11Render; - - VideoRenderType _renderType; - -}; - -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ diff --git a/modules/video_render/main/source/linux/video_x11_channel.cc b/modules/video_render/main/source/linux/video_x11_channel.cc deleted file mode 100644 index 8b2271ca9..000000000 --- a/modules/video_render/main/source/linux/video_x11_channel.cc +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_x11_channel.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - - -namespace webrtc { - -#define DISP_MAX 128 - -static Display *dispArray[DISP_MAX]; -static int dispCount = 0; - - -VideoX11Channel::VideoX11Channel(WebRtc_Word32 id) : - _crit(*CriticalSectionWrapper::CreateCriticalSection()), - _videoInterpolator(NULL), _display(NULL), _xvport(), _shminfo(), - _image(NULL), _window(NULL), _width(DEFAULT_RENDER_FRAME_WIDTH), - _height(DEFAULT_RENDER_FRAME_HEIGHT), _outWidth(0), _outHeight(0), - _xPos(0), _yPos(0), _prepared(false), _dispCount(0), _buffer(NULL), - _Id(id) -{ -} - -VideoX11Channel::~VideoX11Channel() -{ - if (_prepared) - { - _crit.Enter(); - RemoveRenderer(); - _crit.Leave(); - } - delete &_crit; - - if (_videoInterpolator) - { - delete _videoInterpolator; - } -} - -WebRtc_Word32 VideoX11Channel::RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) -{ - CriticalSectionScoped cs(_crit); - if (_width != (WebRtc_Word32) videoFrame.Width() || _height - != (WebRtc_Word32) videoFrame.Height()) - { - if (FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) - { - return -1; - } - } - return DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), - videoFrame.TimeStamp()); -} - -WebRtc_Word32 VideoX11Channel::FrameSizeChange(WebRtc_Word32 width, - WebRtc_Word32 height, - WebRtc_Word32 /*numberOfStreams */) -{ - CriticalSectionScoped cs(_crit); - if (_prepared) - { - RemoveRenderer(); - } - - if (CreateLocalRenderer(width, height) == -1) - { - return -1; - } - - return 0; -} - -WebRtc_Word32 VideoX11Channel::DeliverFrame(unsigned char* buffer, - WebRtc_Word32 bufferSize, - unsigned WebRtc_Word32 /*timeStamp90kHz*/) -{ - CriticalSectionScoped cs(_crit); - if (!_prepared) - { - return 0; - } - - if (!dispArray[_dispCount]) - { - return -1; - } - - unsigned char *pBuf = buffer; - // convert to RGB32 - ConvertI420ToARGB(pBuf, _buffer, _width, _height, 0); - - // put image in window - XShmPutImage(_display, _window, _gc, _image, 0, 0, _xPos, _yPos, _width, - _height, True); - - // very important for the image to update properly! - XSync(_display, false); - - return 0; - -} - -WebRtc_Word32 VideoX11Channel::GetFrameSize(WebRtc_Word32& width, - WebRtc_Word32& height) -{ - width = _width; - height = _height; - - return 0; -} - -WebRtc_Word32 VideoX11Channel::Init(Window window, float left, float top, - float right, float bottom) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_crit); - - _window = window; - _left = left; - _right = _right; - _top = top; - _bottom = _bottom; - - _display = XOpenDisplay(NULL); // Use default display - if (!_window || !_display) - { - return -1; - } - - if (dispCount < DISP_MAX) - { - dispArray[dispCount] = _display; - _dispCount = dispCount; - dispCount++; - } - else - { - return -1; - } - - if ((1 < left || left < 0) || (1 < top || top < 0) || (1 < right || right - < 0) || (1 < bottom || bottom < 0)) - { - return -1; - } - - // calculate position and size of rendered video - int x, y; - unsigned int winWidth, winHeight, borderwidth, depth; - Window rootret; - if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, - &winHeight, &borderwidth, &depth) == 0) - { - return -1; - } - - _xPos = (WebRtc_Word32) (winWidth * left); - _yPos = (WebRtc_Word32) (winHeight * top); - _outWidth = (WebRtc_Word32) (winWidth * (right - left)); - _outHeight = (WebRtc_Word32) (winHeight * (bottom - top)); - if (_outWidth % 2) - _outWidth++; // the renderer want's sizes that are multiples of two - if (_outHeight % 2) - _outHeight++; - - if (CreateLocalRenderer(winWidth, winHeight) == -1) - { - return -1; - } - return 0; - -} - -WebRtc_Word32 VideoX11Channel::ChangeWindow(Window window) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_crit); - - // Stop the rendering, if we are rendering... - RemoveRenderer(); - _window = window; - - // calculate position and size of rendered video - int x, y; - unsigned int winWidth, winHeight, borderwidth, depth; - Window rootret; - if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, - &winHeight, &borderwidth, &depth) == -1) - { - return -1; - } - _xPos = (int) (winWidth * _left); - _yPos = (int) (winHeight * _top); - _outWidth = (int) (winWidth * (_right - _left)); - _outHeight = (int) (winHeight * (_bottom - _top)); - if (_outWidth % 2) - _outWidth++; // the renderer want's sizes that are multiples of two - if (_outHeight % 2) - _outHeight++; - - // Prepare rendering using the - if (CreateLocalRenderer(_width, _height) == -1) - { - return -1; - } - return 0; -} - -WebRtc_Word32 VideoX11Channel::ReleaseWindow() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_crit); - - return RemoveRenderer(); - -} - -WebRtc_Word32 VideoX11Channel::CreateLocalRenderer(WebRtc_Word32 width, - WebRtc_Word32 height) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - CriticalSectionScoped cs(_crit); - - if (!_window || !_display) - { - return -1; - } - - if (_prepared) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _Id, - "Renderer already prepared, exits."); - return -1; - } - - _width = width; - _height = height; - - // create a graphics context in the window - _gc = XCreateGC(_display, _window, 0, 0); - - // create shared memory image - _image = XShmCreateImage(_display, CopyFromParent, 24, ZPixmap, NULL, - &_shminfo, _width, _height); // this parameter needs to be the same for some reason. - _shminfo.shmid = shmget(IPC_PRIVATE, (_image->bytes_per_line - * _image->height), IPC_CREAT | 0777); - _shminfo.shmaddr = _image->data = (char*) shmat(_shminfo.shmid, 0, 0); - _buffer = (unsigned char*) _image->data; - _shminfo.readOnly = False; - - // attach image to display - if (!XShmAttach(_display, &_shminfo)) - { - //printf("XShmAttach failed !\n"); - return -1; - } - - _prepared = true; - return 0; -} - -WebRtc_Word32 VideoX11Channel::RemoveRenderer() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - - if (!_prepared) - { - return 0; - } - _prepared = false; - - // free and closse Xwindow and XShm - XShmDetach(_display, &_shminfo); - XDestroyImage( _image ); - shmdt(_shminfo.shmaddr); - - return 0; -} - -WebRtc_Word32 VideoX11Channel::GetStreamProperties(WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, - float& bottom) const -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", - __FUNCTION__); - - zOrder = 0; // no z-order support yet - left = _left; - top = _top; - right = _right; - bottom = _bottom; - - return 0; -} - - -} //namespace webrtc - - diff --git a/modules/video_render/main/source/linux/video_x11_channel.h b/modules/video_render/main/source/linux/video_x11_channel.h deleted file mode 100644 index 6cc73349b..000000000 --- a/modules/video_render/main/source/linux/video_x11_channel.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ - -#include "video_render_defines.h" -#include "vplib.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace webrtc { -class CriticalSectionWrapper; - -#define DEFAULT_RENDER_FRAME_WIDTH 352 -#define DEFAULT_RENDER_FRAME_HEIGHT 288 - -typedef char* VideoInterpolator; - -class VideoX11Channel: public VideoRenderCallback -{ -public: - VideoX11Channel(WebRtc_Word32 id); - - virtual ~VideoX11Channel(); - - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - - WebRtc_Word32 FrameSizeChange(WebRtc_Word32 width, WebRtc_Word32 height, - WebRtc_Word32 numberOfStreams); - WebRtc_Word32 DeliverFrame(unsigned char* buffer, WebRtc_Word32 bufferSize, - unsigned WebRtc_Word32 /*timeStamp90kHz*/); - WebRtc_Word32 GetFrameSize(WebRtc_Word32& width, WebRtc_Word32& height); - WebRtc_Word32 Init(Window window, float left, float top, float right, - float bottom); - WebRtc_Word32 ChangeWindow(Window window); - WebRtc_Word32 - GetStreamProperties(WebRtc_UWord32& zOrder, float& left, - float& top, float& right, float& bottom) const; - WebRtc_Word32 ReleaseWindow(); - - bool IsPrepared() - { - return _prepared; - } - -private: - - WebRtc_Word32 - CreateLocalRenderer(WebRtc_Word32 width, WebRtc_Word32 height); - WebRtc_Word32 RemoveRenderer(); - - //FIXME a better place for this method? the GetWidthHeight no longer supported by vplib. - int GetWidthHeight(VideoType type, int bufferSize, int& width, - int& height); - - CriticalSectionWrapper& _crit; - VideoInterpolator* _videoInterpolator; - - Display* _display; - WebRtc_Word32 _xvport; - XShmSegmentInfo _shminfo; - XImage* _image; - Window _window; - GC _gc; - WebRtc_Word32 _width; // incoming frame width - WebRtc_Word32 _height; // incoming frame height - WebRtc_Word32 _outWidth; // render frame width - WebRtc_Word32 _outHeight; // render frame height - WebRtc_Word32 _xPos; // position within window - WebRtc_Word32 _yPos; - bool _prepared; // true if ready to use - WebRtc_Word32 _dispCount; - - unsigned char* _buffer; - float _top; - float _left; - float _right; - float _bottom; - - WebRtc_Word32 _Id; - -}; - - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ diff --git a/modules/video_render/main/source/linux/video_x11_render.cc b/modules/video_render/main/source/linux/video_x11_render.cc deleted file mode 100644 index a3543e39c..000000000 --- a/modules/video_render/main/source/linux/video_x11_render.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_x11_render.h" -#include "video_x11_channel.h" - -#include "critical_section_wrapper.h" -#include "trace.h" - -namespace webrtc { - -VideoX11Render::VideoX11Render(Window window) : - _window(window), - _critSect(*CriticalSectionWrapper::CreateCriticalSection()) -{ -} - -VideoX11Render::~VideoX11Render() -{ - delete &_critSect; -} - -WebRtc_Word32 VideoX11Render::Init() -{ - CriticalSectionScoped cs(_critSect); - - _streamIdToX11ChannelMap.clear(); - - return 0; -} - -WebRtc_Word32 VideoX11Render::ChangeWindow(Window window) -{ - CriticalSectionScoped cs(_critSect); - VideoX11Channel* renderChannel = NULL; - - std::map::iterator iter = - _streamIdToX11ChannelMap.begin(); - - while (iter != _streamIdToX11ChannelMap.end()) - { - renderChannel = iter->second; - if (renderChannel) - { - renderChannel->ChangeWindow(window); - } - iter++; - } - - _window = window; - - return 0; -} - -VideoX11Channel* VideoX11Render::CreateX11RenderChannel( - WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_critSect); - VideoX11Channel* renderChannel = NULL; - - std::map::iterator iter = - _streamIdToX11ChannelMap.find(streamId); - - if (iter == _streamIdToX11ChannelMap.end()) - { - renderChannel = new VideoX11Channel(streamId); - if (!renderChannel) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideoRenderer, - -1, - "Failed to create VideoX11Channel for streamId : %d", - streamId); - return NULL; - } - renderChannel->Init(_window, left, top, right, bottom); - _streamIdToX11ChannelMap[streamId] = renderChannel; - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, - "Render Channel already exists for streamId: %d", streamId); - renderChannel = iter->second; - } - - return renderChannel; -} - -WebRtc_Word32 VideoX11Render::DeleteX11RenderChannel(WebRtc_Word32 streamId) -{ - CriticalSectionScoped cs(_critSect); - - std::map::iterator iter = - _streamIdToX11ChannelMap.find(streamId); - if (iter != _streamIdToX11ChannelMap.end()) - { - VideoX11Channel *renderChannel = iter->second; - if (renderChannel) - { - renderChannel->ReleaseWindow(); - delete renderChannel; - renderChannel = NULL; - } - _streamIdToX11ChannelMap.erase(iter); - } - - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "No VideoX11Channel object exists for stream id: %d", - streamId); - return -1; -} - -WebRtc_Word32 VideoX11Render::GetIncomingStreamProperties( - WebRtc_Word32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) -{ - CriticalSectionScoped cs(_critSect); - - std::map::iterator iter = - _streamIdToX11ChannelMap.find(streamId); - if (iter != _streamIdToX11ChannelMap.end()) - { - VideoX11Channel *renderChannel = iter->second; - if (renderChannel) - { - renderChannel->GetStreamProperties(zOrder, left, top, right, bottom); - } - } - - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "No VideoX11Channel object exists for stream id: %d", - streamId); - return -1; -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/linux/video_x11_render.h b/modules/video_render/main/source/linux/video_x11_render.h deleted file mode 100644 index 9b140effa..000000000 --- a/modules/video_render/main/source/linux/video_x11_render.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ - -#include "video_render_defines.h" - -#include -#include - -namespace webrtc { -class CriticalSectionWrapper; - -class VideoX11Channel; - -class VideoX11Render -{ - -public: - VideoX11Render(Window window); - ~VideoX11Render(); - - WebRtc_Word32 Init(); - WebRtc_Word32 ChangeWindow(Window window); - - VideoX11Channel* CreateX11RenderChannel(WebRtc_Word32 streamId, - WebRtc_Word32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - WebRtc_Word32 DeleteX11RenderChannel(WebRtc_Word32 streamId); - - WebRtc_Word32 GetIncomingStreamProperties(WebRtc_Word32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom); - -private: - Window _window; - CriticalSectionWrapper& _critSect; - std::map _streamIdToX11ChannelMap; - -}; - - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ diff --git a/modules/video_render/main/source/mac/cocoa_full_screen_window.h b/modules/video_render/main/source/mac/cocoa_full_screen_window.h deleted file mode 100644 index 0638561e7..000000000 --- a/modules/video_render/main/source/mac/cocoa_full_screen_window.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// -// cocoa_full_screen_window.h -// -// - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ - -#import -//#define GRAB_ALL_SCREENS 1 - -@interface CocoaFullScreenWindow : NSObject { - NSWindow* _window; -} - --(id)init; --(void)grabFullScreen; --(void)releaseFullScreen; - - -@end - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ diff --git a/modules/video_render/main/source/mac/cocoa_full_screen_window.mm b/modules/video_render/main/source/mac/cocoa_full_screen_window.mm deleted file mode 100644 index 642231b33..000000000 --- a/modules/video_render/main/source/mac/cocoa_full_screen_window.mm +++ /dev/null @@ -1,87 +0,0 @@ -// -// CocoaFullScreenWindow.m -// - -#import "cocoa_full_screen_window.h" -#include "trace.h" - -using namespace webrtc; - -@implementation CocoaFullScreenWindow - - - --(id)init{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - - self = [super init]; - if(!self){ - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d COULD NOT CREATE INSTANCE", __FUNCTION__, __LINE__); - return nil; - } - - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, 0, "%s:%d Created instance", __FUNCTION__, __LINE__); - return self; -} - --(NSWindow*)window{ - return _window; -} - - --(void)grabFullScreen{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - -#ifdef GRAB_ALL_SCREENS - if(CGCaptureAllDisplays() != kCGErrorSuccess) -#else - if(CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess) -#endif - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not capture main level", __FUNCTION__, __LINE__); - } - - // get the shielding window level - int windowLevel = CGShieldingWindowLevel(); - - // get the screen rect of main display - NSRect screenRect = [[NSScreen mainScreen]frame]; - - _window = [[NSWindow alloc]initWithContentRect:screenRect - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO - screen:[NSScreen mainScreen]]; - - [_window setLevel:windowLevel]; - [_window setBackgroundColor:[NSColor blackColor]]; - [_window makeKeyAndOrderFront:nil]; - -} - --(void)releaseFullScreen -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - [_window orderOut:self]; - -#ifdef GRAB_ALL_SCREENS - if(CGReleaseAllDisplays() != kCGErrorSuccess) -#else - if(CGDisplayRelease(kCGDirectMainDisplay) != kCGErrorSuccess) -#endif - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not release the displays", __FUNCTION__, __LINE__); - } -} - -- (void) dealloc -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - [self releaseFullScreen]; - [super dealloc]; -} - - - -@end diff --git a/modules/video_render/main/source/mac/cocoa_render_view.h b/modules/video_render/main/source/mac/cocoa_render_view.h deleted file mode 100644 index 84a2bd5ba..000000000 --- a/modules/video_render/main/source/mac/cocoa_render_view.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// -// cocoa_render_view.h -// - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ - -#import -#import -#import -#import - - - -@interface CocoaRenderView : NSOpenGLView { - NSOpenGLContext* _nsOpenGLContext; -} - - - --(void)initCocoaRenderView:(NSOpenGLPixelFormat*)fmt; --(void)initCocoaRenderViewFullScreen:(NSOpenGLPixelFormat*)fmt; -@end - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ \ No newline at end of file diff --git a/modules/video_render/main/source/mac/cocoa_render_view.mm b/modules/video_render/main/source/mac/cocoa_render_view.mm deleted file mode 100644 index acbd3c285..000000000 --- a/modules/video_render/main/source/mac/cocoa_render_view.mm +++ /dev/null @@ -1,56 +0,0 @@ -// -// CocoaRenderView.mm -// - -#import -#import -#import "cocoa_render_view.h" -#include "trace.h" - -using namespace webrtc; - -@implementation CocoaRenderView - - --(void)initCocoaRenderView:(NSOpenGLPixelFormat*)fmt{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - - self = [super initWithFrame:[self frame] pixelFormat:[fmt autorelease]]; - if (self == nil){ - - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not create instance", __FUNCTION__, __LINE__); - } - - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d Created instance", __FUNCTION__, __LINE__); - _nsOpenGLContext = [self openGLContext]; - -} - - --(NSOpenGLContext*)nsOpenGLContext{ - return _nsOpenGLContext; -} - - - --(void)initCocoaRenderViewFullScreen:(NSOpenGLPixelFormat*)fmt{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - - NSRect screenRect = [[NSScreen mainScreen]frame]; -// [_windowRef setFrame:screenRect]; -// [_windowRef setBounds:screenRect]; - self = [super initWithFrame:screenRect pixelFormat:[fmt autorelease]]; - if (self == nil){ - - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not create instance", __FUNCTION__, __LINE__); - } - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d Created instance", __FUNCTION__, __LINE__); - _nsOpenGLContext = [self openGLContext]; - -} - -@end - - diff --git a/modules/video_render/main/source/mac/video_render_agl.cc b/modules/video_render/main/source/mac/video_render_agl.cc deleted file mode 100644 index bc0d1517b..000000000 --- a/modules/video_render/main/source/mac/video_render_agl.cc +++ /dev/null @@ -1,2005 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" - -#if defined(CARBON_RENDERING) - -#include "video_render_agl.h" - -// includes -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "vplib.h" - -namespace webrtc { - -/* - * - * VideoChannelAGL - * - */ - -#pragma mark VideoChannelAGL constructor - -VideoChannelAGL::VideoChannelAGL(AGLContext& aglContext, int iId, VideoRenderAGL* owner) : - _aglContext( aglContext), - _id( iId), - _owner( owner), - _width( 0), - _height( 0), - _stretchedWidth( 0), - _stretchedHeight( 0), - _startWidth( 0.0f), - _startHeight( 0.0f), - _stopWidth( 0.0f), - _stopHeight( 0.0f), - _xOldWidth( 0), - _yOldHeight( 0), - _oldStretchedHeight(0), - _oldStretchedWidth( 0), - _buffer( 0), - _bufferSize( 0), - _incommingBufferSize(0), - _bufferIsUpdated( false), - _sizeInitialized( false), - _numberOfStreams( 0), - _bVideoSizeStartedChanging(false), - _pixelFormat( GL_RGBA), - _pixelDataType( GL_UNSIGNED_INT_8_8_8_8), - _texture( 0) - -{ - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Constructor", __FUNCTION__, __LINE__); -} - -VideoChannelAGL::~VideoChannelAGL() -{ - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Destructor", __FUNCTION__, __LINE__); - if (_buffer) - { - delete [] _buffer; - _buffer = NULL; - } - - aglSetCurrentContext(_aglContext); - - if (_texture != 0) - { - glDeleteTextures(1, (const GLuint*) &_texture); - _texture = 0; - } -} - -WebRtc_Word32 VideoChannelAGL::RenderFrame(const WebRtc_UWord32 streamId, VideoFrame& videoFrame) -{ - _owner->LockAGLCntx(); - if(_width != videoFrame.Width() || - _height != videoFrame.Height()) - { - if(FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) - { //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d FrameSizeChange returned an error", __FUNCTION__, __LINE__); - _owner->UnlockAGLCntx(); - return -1; - } - } - - _owner->UnlockAGLCntx(); - return DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), videoFrame.TimeStamp()); -} - -int VideoChannelAGL::UpdateSize(int /*width*/, int /*height*/) -{ - _owner->LockAGLCntx(); - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelAGL::UpdateStretchSize(int stretchHeight, int stretchWidth) -{ - - _owner->LockAGLCntx(); - _stretchedHeight = stretchHeight; - _stretchedWidth = stretchWidth; - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelAGL::FrameSizeChange(int width, int height, int numberOfStreams) -{ - // We'll get a new frame size from VideoAPI, prepare the buffer - - _owner->LockAGLCntx(); - - if (width == _width && _height == height) - { - // We already have a correct buffer size - _numberOfStreams = numberOfStreams; - _owner->UnlockAGLCntx(); - return 0; - } - - _width = width; - _height = height; - - // Delete the old buffer, create a new one with correct size. - if (_buffer) - { - delete [] _buffer; - _bufferSize = 0; - } - - _incommingBufferSize = CalcBufferSize(kI420, _width, _height); - _bufferSize = CalcBufferSize(kARGB, _width, _height);//_width * _height * bytesPerPixel; - _buffer = new unsigned char [_bufferSize]; - memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); - - if (aglSetCurrentContext(_aglContext) == false) - { - _owner->UnlockAGLCntx(); - return -1; - } - - // Delete a possible old texture - if (_texture != 0) - { - glDeleteTextures(1, (const GLuint*) &_texture); - _texture = 0; - } - - // Create a new texture - glGenTextures(1, (GLuint *) &_texture); - - GLenum glErr = glGetError(); - - if (glErr != GL_NO_ERROR) - { - } - - // Do the setup for both textures - // Note: we setup two textures even if we're not running full screen - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); - - // Set texture parameters - glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); - - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); - - // Maximum width/height for a texture - GLint texSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); - - if (texSize < _width || texSize < _height) - { - // Image too big for memory - _owner->UnlockAGLCntx(); - return -1; - } - - // Set up th texture type and size - glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target - 0, // level - GL_RGBA, // internal format - _width, // width - _height, // height - 0, // border 0/1 = off/on - _pixelFormat, // format, GL_BGRA - _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 - _buffer); // pixel data - - glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - _owner->UnlockAGLCntx(); - return -1; - } - - _owner->UnlockAGLCntx(); - return 0; -} - -// Called from video engine when a new frame should be rendered. -int VideoChannelAGL::DeliverFrame(unsigned char* buffer, int bufferSize, unsigned int /*timeStamp90kHz*/) -{ - _owner->LockAGLCntx(); - - if (_texture == 0) - { - _owner->UnlockAGLCntx(); - return 0; - } - - if (bufferSize != _incommingBufferSize) - { - _owner->UnlockAGLCntx(); - return -1; - } - - int rgbLength = ConvertI420ToRGBAMac((WebRtc_UWord8*)buffer, (WebRtc_UWord8*)_buffer, (WebRtc_Word32)_width, (WebRtc_Word32)_height, 0); - if (rgbLength == -1) - { - _owner->UnlockAGLCntx(); - return -1; - } - - aglSetCurrentContext(_aglContext); - - // Put the new frame into the graphic card texture. - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); // Make sure this texture is the active one - GLenum glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - _owner->UnlockAGLCntx(); - return -1; - } - - // Copy buffer to texture - glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, - 0, // Level, not use - 0, // start point x, (low left of pic) - 0, // start point y, - _width, // width - _height, // height - _pixelFormat, // pictue format for _buffer - _pixelDataType, // data type of _buffer - (const GLvoid*) _buffer); // the pixel data - - if (glGetError() != GL_NO_ERROR) - { - _owner->UnlockAGLCntx(); - return -1; - } - - _bufferIsUpdated = true; - _owner->UnlockAGLCntx(); - - return 0; -} - -int VideoChannelAGL::RenderOffScreenBuffer() -{ - - _owner->LockAGLCntx(); - - if (_texture == 0) - { - _owner->UnlockAGLCntx(); - return 0; - } - - GLfloat xStart = 2.0f * _startWidth - 1.0f; - GLfloat xStop = 2.0f * _stopWidth - 1.0f; - GLfloat yStart = 1.0f - 2.0f * _stopHeight; - GLfloat yStop = 1.0f - 2.0f * _startHeight; - - aglSetCurrentContext(_aglContext); - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); - - if(_stretchedWidth != _oldStretchedWidth || _stretchedHeight != _oldStretchedHeight) - { - glViewport(0, 0, _stretchedWidth, _stretchedHeight); - } - _oldStretchedHeight = _stretchedHeight; - _oldStretchedWidth = _stretchedWidth; - - // Now really put the texture into the framebuffer - glLoadIdentity(); - - glEnable(GL_TEXTURE_RECTANGLE_EXT); - - glBegin(GL_POLYGON); - { - glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); - glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); - glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); - glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); - } - glEnd(); - - glDisable(GL_TEXTURE_RECTANGLE_EXT); - - _bufferIsUpdated = false; - - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelAGL::IsUpdated(bool& isUpdated) -{ - _owner->LockAGLCntx(); - isUpdated = _bufferIsUpdated; - _owner->UnlockAGLCntx(); - - return 0; -} - -int VideoChannelAGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - - _owner->LockAGLCntx(); - - _startWidth = startWidth; - _stopWidth = stopWidth; - _startHeight = startHeight; - _stopHeight = stopHeight; - - int oldWidth = _width; - int oldHeight = _height; - int oldNumberOfStreams = _numberOfStreams; - - _width = 0; - _height = 0; - - int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); - - _owner->UnlockAGLCntx(); - - return retVal; -} - -int VideoChannelAGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) -{ - return -1; -} - -#pragma mark VideoRenderAGL WindowRef constructor - -VideoRenderAGL::VideoRenderAGL(WindowRef windowRef, bool fullscreen, int iId) : -_hiviewRef( 0), -_windowRef( windowRef), -_fullScreen( fullscreen), -_id( iId), -_renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), -_screenUpdateThread( 0), -_screenUpdateEvent( 0), -_isHIViewRef( false), -_aglContext( 0), -_windowWidth( 0), -_windowHeight( 0), -_lastWindowWidth( -1), -_lastWindowHeight( -1), -_lastHiViewWidth( -1), -_lastHiViewHeight( -1), -_currentParentWindowHeight( 0), -_currentParentWindowWidth( 0), -_currentParentWindowBounds( ), -_windowHasResized( false), -_lastParentWindowBounds( ), -_currentHIViewBounds( ), -_lastHIViewBounds( ), -_windowRect( ), -_aglChannels( ), -_zOrderToChannel( ), -_hiviewEventHandlerRef( NULL), -_windowEventHandlerRef( NULL), -_currentViewBounds( ), -_lastViewBounds( ), -_renderingIsPaused( false), -_threadID( ) - -{ - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s"); - - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); - - if(!IsValidWindowPtr(_windowRef)) - { - //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Invalid WindowRef:0x%x", __FUNCTION__, __LINE__, _windowRef); - } - else - { - //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d WindowRef 0x%x is valid", __FUNCTION__, __LINE__, _windowRef); - } - - GetWindowRect(_windowRect); - - _lastViewBounds.origin.x = 0; - _lastViewBounds.origin.y = 0; - _lastViewBounds.size.width = 0; - _lastViewBounds.size.height = 0; - -} - -// this is a static function. It has been registered (in class constructor) to be called on various window redrawing or resizing. -// Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. -#pragma mark WindowRef Event Handler -pascal OSStatus VideoRenderAGL::sHandleWindowResized (EventHandlerCallRef /*nextHandler*/, - EventRef theEvent, - void* userData) -{ - WindowRef windowRef = NULL; - - int eventType = GetEventKind(theEvent); - - // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes - GetEventParameter (theEvent, - kEventParamDirectObject, - typeWindowRef, - NULL, - sizeof (WindowRef), - NULL, - &windowRef); - - VideoRenderAGL* obj = (VideoRenderAGL*)(userData); - - bool updateUI = true; - if(kEventWindowBoundsChanged == eventType) - { - } - else if(kEventWindowBoundsChanging == eventType) - { - } - else if(kEventWindowZoomed == eventType) - { - } - else if(kEventWindowExpanding == eventType) - { - } - else if(kEventWindowExpanded == eventType) - { - } - else if(kEventWindowClickResizeRgn == eventType) - { - } - else if(kEventWindowClickDragRgn == eventType) - { - } - else - { - updateUI = false; - } - - if(true == updateUI) - { - obj->ParentWindowResized(windowRef); - obj->UpdateClipping(); - obj->RenderOffScreenBuffers(); - } - - return noErr; -} - -#pragma mark VideoRenderAGL HIViewRef constructor - -VideoRenderAGL::VideoRenderAGL(HIViewRef windowRef, bool fullscreen, int iId) : -_hiviewRef( windowRef), -_windowRef( 0), -_fullScreen( fullscreen), -_id( iId), -_renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), -_screenUpdateThread( 0), -_screenUpdateEvent( 0), -_isHIViewRef( false), -_aglContext( 0), -_windowWidth( 0), -_windowHeight( 0), -_lastWindowWidth( -1), -_lastWindowHeight( -1), -_lastHiViewWidth( -1), -_lastHiViewHeight( -1), -_currentParentWindowHeight( 0), -_currentParentWindowWidth( 0), -_currentParentWindowBounds( ), -_windowHasResized( false), -_lastParentWindowBounds( ), -_currentHIViewBounds( ), -_lastHIViewBounds( ), -_windowRect( ), -_aglChannels( ), -_zOrderToChannel( ), -_hiviewEventHandlerRef( NULL), -_windowEventHandlerRef( NULL), -_currentViewBounds( ), -_lastViewBounds( ), -_renderingIsPaused( false), -_threadID( ) -{ - //WEBRTC_TRACE(kTraceDebug, "%s:%d Constructor", __FUNCTION__, __LINE__); - // _renderCritSec = CriticalSectionWrapper::CreateCriticalSection(); - - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); - - GetWindowRect(_windowRect); - - _lastViewBounds.origin.x = 0; - _lastViewBounds.origin.y = 0; - _lastViewBounds.size.width = 0; - _lastViewBounds.size.height = 0; - -#ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER - // This gets the parent window of the HIViewRef that's passed in and installs a WindowRef event handler on it - // The event handler looks for window resize events and adjusts the offset of the controls. - - //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef's parent window", __FUNCTION__, __LINE__); - - - static const EventTypeSpec windowEventTypes[] = - { - kEventClassWindow, kEventWindowBoundsChanged, - kEventClassWindow, kEventWindowBoundsChanging, - kEventClassWindow, kEventWindowZoomed, - kEventClassWindow, kEventWindowExpanded, - kEventClassWindow, kEventWindowClickResizeRgn, - kEventClassWindow, kEventWindowClickDragRgn - }; - - WindowRef parentWindow = HIViewGetWindow(windowRef); - - InstallWindowEventHandler (parentWindow, - NewEventHandlerUPP (sHandleWindowResized), - GetEventTypeCount(windowEventTypes), - windowEventTypes, - (void *) this, // this is an arbitrary parameter that will be passed on to your event handler when it is called later - &_windowEventHandlerRef); - -#endif - -#ifdef NEW_HIVIEW_EVENT_HANDLER - //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef", __FUNCTION__, __LINE__); - - static const EventTypeSpec hiviewEventTypes[] = - { - kEventClassControl, kEventControlBoundsChanged, - kEventClassControl, kEventControlDraw - // kEventControlDragLeave - // kEventControlDragReceive - // kEventControlGetFocusPart - // kEventControlApplyBackground - // kEventControlDraw - // kEventControlHit - - }; - - HIViewInstallEventHandler(_hiviewRef, - NewEventHandlerUPP(sHandleHiViewResized), - GetEventTypeCount(hiviewEventTypes), - hiviewEventTypes, - (void *) this, - &_hiviewEventHandlerRef); - -#endif -} - -// this is a static function. It has been registered (in constructor) to be called on various window redrawing or resizing. -// Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. -#pragma mark HIViewRef Event Handler -pascal OSStatus VideoRenderAGL::sHandleHiViewResized (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData) -{ - //static int callbackCounter = 1; - HIViewRef hiviewRef = NULL; - - // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes - int eventType = GetEventKind(theEvent); - OSStatus status = noErr; - status = GetEventParameter (theEvent, - kEventParamDirectObject, - typeControlRef, - NULL, - sizeof (ControlRef), - NULL, - &hiviewRef); - - VideoRenderAGL* obj = (VideoRenderAGL*)(userData); - WindowRef parentWindow = HIViewGetWindow(hiviewRef); - bool updateUI = true; - - if(kEventControlBoundsChanged == eventType) - { - } - else if(kEventControlDraw == eventType) - { - } - else - { - updateUI = false; - } - - if(true == updateUI) - { - obj->ParentWindowResized(parentWindow); - obj->UpdateClipping(); - obj->RenderOffScreenBuffers(); - } - - return status; -} - -VideoRenderAGL::~VideoRenderAGL() -{ - - //WEBRTC_TRACE(kTraceDebug, "%s:%d Destructor", __FUNCTION__, __LINE__); - - -#ifdef USE_EVENT_HANDLERS - // remove event handlers - OSStatus status; - if(_isHIViewRef) - { - status = RemoveEventHandler(_hiviewEventHandlerRef); - } - else - { - status = RemoveEventHandler(_windowEventHandlerRef); - } - if(noErr != status) - { - if(_isHIViewRef) - { - - //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); - } - else - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); - } - } - -#endif - - OSStatus status; -#ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER - if(_windowEventHandlerRef) - { - status = RemoveEventHandler(_windowEventHandlerRef); - if(status != noErr) - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); - } - } -#endif - -#ifdef NEW_HIVIEW_EVENT_HANDLER - if(_hiviewEventHandlerRef) - { - status = RemoveEventHandler(_hiviewEventHandlerRef); - if(status != noErr) - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); - } - } -#endif - - // Signal event to exit thread, then delete it - ThreadWrapper* tmpPtr = _screenUpdateThread; - _screenUpdateThread = NULL; - - if (tmpPtr) - { - tmpPtr->SetNotAlive(); - _screenUpdateEvent->Set(); - _screenUpdateEvent->StopTimer(); - - if (tmpPtr->Stop()) - { - delete tmpPtr; - } - delete _screenUpdateEvent; - _screenUpdateEvent = NULL; - } - - if (_aglContext != 0) - { - aglSetCurrentContext(_aglContext); - aglDestroyContext(_aglContext); - _aglContext = 0; - } - - // Delete all channels - std::map::iterator it = _aglChannels.begin(); - while (it!= _aglChannels.end()) - { - delete it->second; - _aglChannels.erase(it); - it = _aglChannels.begin(); - } - _aglChannels.clear(); - - // Clean the zOrder map - std::multimap::iterator zIt = _zOrderToChannel.begin(); - while(zIt != _zOrderToChannel.end()) - { - _zOrderToChannel.erase(zIt); - zIt = _zOrderToChannel.begin(); - } - _zOrderToChannel.clear(); - - //delete _renderCritSec; - - -} - -int VideoRenderAGL::GetOpenGLVersion(int& aglMajor, int& aglMinor) -{ - aglGetVersion((GLint *) &aglMajor, (GLint *) &aglMinor); - return 0; -} - -int VideoRenderAGL::Init() -{ - LockAGLCntx(); - - // Start rendering thread... - if (!_screenUpdateThread) - { - UnlockAGLCntx(); - //WEBRTC_TRACE(kTraceError, "%s:%d Thread not created", __FUNCTION__, __LINE__); - return -1; - } - unsigned int threadId; - _screenUpdateThread->Start(threadId); - - // Start the event triggering the render process - unsigned int monitorFreq = 60; - _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); - - // Create mixing textures - if (CreateMixingContext() == -1) - { - //WEBRTC_TRACE(kTraceError, "%s:%d Could not create a mixing context", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return -1; - } - - UnlockAGLCntx(); - return 0; -} - -VideoChannelAGL* VideoRenderAGL::CreateAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - - LockAGLCntx(); - - //WEBRTC_TRACE(kTraceInfo, "%s:%d Creating AGL channel: %d", __FUNCTION__, __LINE__, channel); - - if (HasChannel(channel)) - { - //WEBRTC_TRACE(kTraceError, "%s:%d Channel already exists", __FUNCTION__, __LINE__); - UnlockAGLCntx();k - return NULL; - } - - if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) - { - // There are already one channel using this zOrder - // TODO: Allow multiple channels with same zOrder - } - - VideoChannelAGL* newAGLChannel = new VideoChannelAGL(_aglContext, _id, this); - - if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) - { - if (newAGLChannel) - { - delete newAGLChannel; - newAGLChannel = NULL; - } - //WEBRTC_LOG(kTraceError, "Could not create AGL channel"); - //WEBRTC_TRACE(kTraceError, "%s:%d Could not create AGL channel", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return NULL; - } -k - _aglChannels[channel] = newAGLChannel; - _zOrderToChannel.insert(std::pair(zOrder, channel)); - - UnlockAGLCntx(); - return newAGLChannel; -} - -int VideoRenderAGL::DeleteAllAGLChannels() -{ - CriticalSectionScoped cs(_renderCritSec); - - //WEBRTC_TRACE(kTraceInfo, "%s:%d Deleting all AGL channels", __FUNCTION__, __LINE__); - //int i = 0 ; - std::map::iterator it; - it = _aglChannels.begin(); - - while (it != _aglChannels.end()) - { - VideoChannelAGL* channel = it->second; - if (channel) - delete channel; - - _aglChannels.erase(it); - it = _aglChannels.begin(); - } - _aglChannels.clear(); - return 0; -} - -int VideoRenderAGL::DeleteAGLChannel(int channel) -{ - CriticalSectionScoped cs(_renderCritSec); - //WEBRTC_TRACE(kTraceDebug, "%s:%d Deleting AGL channel %d", __FUNCTION__, __LINE__, channel); - - std::map::iterator it; - it = _aglChannels.find(channel); - if (it != _aglChannels.end()) - { - delete it->second; - _aglChannels.erase(it); - } - else - { - //WEBRTC_TRACE(kTraceWarning, "%s:%d Channel not found", __FUNCTION__, __LINE__); - return -1; - } - - std::multimap::iterator zIt = _zOrderToChannel.begin(); - while( zIt != _zOrderToChannel.end()) - { - if (zIt->second == channel) - { - _zOrderToChannel.erase(zIt); - break; - } - zIt++;// = _zOrderToChannel.begin(); - } - - return 0; -} - -int VideoRenderAGL::StopThread() -{ - CriticalSectionScoped cs(_renderCritSec); - ThreadWrapper* tmpPtr = _screenUpdateThread; - //_screenUpdateThread = NULL; - - if (tmpPtr) - { - tmpPtr->SetNotAlive(); - _screenUpdateEvent->Set(); - if (tmpPtr->Stop()) - { - delete tmpPtr; - } - } - - delete _screenUpdateEvent; - _screenUpdateEvent = NULL; - - return 0; -} - -bool VideoRenderAGL::IsFullScreen() -{ - CriticalSectionScoped cs(_renderCritSec); - return _fullScreen; -} - -bool VideoRenderAGL::HasChannels() -{ - - CriticalSectionScoped cs(_renderCritSec); - - if (_aglChannels.begin() != _aglChannels.end()) - { - return true; - } - - return false; -} - -bool VideoRenderAGL::HasChannel(int channel) -{ - CriticalSectionScoped cs(_renderCritSec); - - std::map::iterator it = _aglChannels.find(channel); - if (it != _aglChannels.end()) - { - return true; - } - - return false; -} - -int VideoRenderAGL::GetChannels(std::list& channelList) -{ - - CriticalSectionScoped cs(_renderCritSec); - std::map::iterator it = _aglChannels.begin(); - - while (it != _aglChannels.end()) - { - channelList.push_back(it->first); - it++; - } - - return 0; -} - -VideoChannelAGL* VideoRenderAGL::ConfigureAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - - CriticalSectionScoped cs(_renderCritSec); - - std::map::iterator it = _aglChannels.find(channel); - - if (it != _aglChannels.end()) - { - VideoChannelAGL* aglChannel = it->second; - if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) - { - return NULL; - } - - std::multimap::iterator it = _zOrderToChannel.begin(); - while(it != _zOrderToChannel.end()) - { - if (it->second == channel) - { - if (it->first != zOrder) - { - _zOrderToChannel.erase(it); - _zOrderToChannel.insert(std::pair(zOrder, channel)); - } - break; - } - it++; - } - return aglChannel; - } - - return NULL; -} - -bool VideoRenderAGL::ScreenUpdateThreadProc(void* obj) -{ - return static_cast(obj)->ScreenUpdateProcess(); -} - -bool VideoRenderAGL::ScreenUpdateProcess() -{ - _screenUpdateEvent->Wait(100); - - LockAGLCntx(); - - if (!_screenUpdateThread) - { - UnlockAGLCntx(); - return false; - } - - if (aglSetCurrentContext(_aglContext) == GL_FALSE) - { - UnlockAGLCntx(); - return true; - } - - if (GetWindowRect(_windowRect) == -1) - { - UnlockAGLCntx(); - return true; - } - - if (_windowWidth != (_windowRect.right - _windowRect.left) - || _windowHeight != (_windowRect.bottom - _windowRect.top)) - { - // We have a new window size, update the context. - if (aglUpdateContext(_aglContext) == GL_FALSE) - { - UnlockAGLCntx(); - return true; - } - _windowWidth = _windowRect.right - _windowRect.left; - _windowHeight = _windowRect.bottom - _windowRect.top; - } - - // this section will poll to see if the window size has changed - // this is causing problem w/invalid windowRef - // this code has been modified and exists now in the window event handler -#ifndef NEW_HIVIEW_PARENT_EVENT_HANDLER - if (_isHIViewRef) - { - - if(FALSE == HIViewIsValid(_hiviewRef)) - { - - //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalid windowRef", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return true; - } - WindowRef window = HIViewGetWindow(_hiviewRef); - - if(FALSE == IsValidWindowPtr(window)) - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalide hiviewRef", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return true; - } - if (window == NULL) - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d WindowRef = NULL", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return true; - } - - if(FALSE == MacIsWindowVisible(window)) - { - //WEBRTC_TRACE(kTraceDebug, "%s:%d MacIsWindowVisible == FALSE. Returning early", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return true; - } - - HIRect viewBounds; // Placement and size for HIView - int windowWidth = 0; // Parent window width - int windowHeight = 0; // Parent window height - - // NOTE: Calling GetWindowBounds with kWindowStructureRgn will crash intermittentaly if the OS decides it needs to push it into the back for a moment. - // To counter this, we get the titlebar height on class construction and then add it to the content region here. Content regions seems not to crash - Rect contentBounds = - { 0, 0, 0, 0}; // The bounds for the parent window - -#if defined(USE_CONTENT_RGN) - GetWindowBounds(window, kWindowContentRgn, &contentBounds); -#elif defined(USE_STRUCT_RGN) - GetWindowBounds(window, kWindowStructureRgn, &contentBounds); -#endif - - Rect globalBounds = - { 0, 0, 0, 0}; // The bounds for the parent window - globalBounds.top = contentBounds.top; - globalBounds.right = contentBounds.right; - globalBounds.bottom = contentBounds.bottom; - globalBounds.left = contentBounds.left; - - windowHeight = globalBounds.bottom - globalBounds.top; - windowWidth = globalBounds.right - globalBounds.left; - - // Get the size of the HIViewRef - HIViewGetBounds(_hiviewRef, &viewBounds); - HIViewConvertRect(&viewBounds, _hiviewRef, NULL); - - // Check if this is the first call.. - if (_lastWindowHeight == -1 && - _lastWindowWidth == -1) - { - _lastWindowWidth = windowWidth; - _lastWindowHeight = windowHeight; - - _lastViewBounds.origin.x = viewBounds.origin.x; - _lastViewBounds.origin.y = viewBounds.origin.y; - _lastViewBounds.size.width = viewBounds.size.width; - _lastViewBounds.size.height = viewBounds.size.height; - } - sfasdfasdf - - bool resized = false; - - // Check if parent window size has changed - if (windowHeight != _lastWindowHeight || - windowWidth != _lastWindowWidth) - { - resized = true; - } - - // Check if the HIView has new size or is moved in the parent window - if (_lastViewBounds.origin.x != viewBounds.origin.x || - _lastViewBounds.origin.y != viewBounds.origin.y || - _lastViewBounds.size.width != viewBounds.size.width || - _lastViewBounds.size.height != viewBounds.size.height) - { - // The HiView is resized or has moved. - resized = true; - } - - if (resized) - { - - //WEBRTC_TRACE(kTraceDebug, "%s:%d Window has resized", __FUNCTION__, __LINE__); - - // Calculate offset between the windows - // {x, y, widht, height}, x,y = lower left corner - const GLint offs[4] = - { (int)(0.5f + viewBounds.origin.x), - (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), - viewBounds.size.width, viewBounds.size.height}; - - //WEBRTC_TRACE(kTraceDebug, "%s:%d contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, __LINE__, - contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); - //WEBRTC_TRACE(kTraceDebug, "%s:%d windowHeight=%d", __FUNCTION__, __LINE__, windowHeight); - //WEBRTC_TRACE(kTraceDebug, "%s:%d offs[4] = %d, %d, %d, %d", __FUNCTION__, __LINE__, offs[0], offs[1], offs[2], offs[3]); - - aglSetDrawable (_aglContext, GetWindowPort(window)); - aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); - aglEnable(_aglContext, AGL_BUFFER_RECT); - - // We need to change the viewport too if the HIView size has changed - glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); - - } - _lastWindowWidth = windowWidth; - _lastWindowHeight = windowHeight; - - _lastViewBounds.origin.x = viewBounds.origin.x; - _lastViewBounds.origin.y = viewBounds.origin.y; - _lastViewBounds.size.width = viewBounds.size.width; - _lastViewBounds.size.height = viewBounds.size.height; - - } -#endif - if (_fullScreen) - { - // TODO - // We use double buffers, must always update - //RenderOffScreenBuffersToBackBuffer(); - } - else - { - // Check if there are any updated buffers - bool updated = false; - - // TODO: check if window size is updated! - // TODO Improvement: Walk through the zOrder Map to only render the ones in need of update - std::map::iterator it = _aglChannels.begin(); - while (it != _aglChannels.end()) - { - - VideoChannelAGL* aglChannel = it->second; - aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); - aglChannel->IsUpdated(updated); - if (updated) - { - break; - } - it++; - } - - if (updated) - { - // At least on buffers is updated, we need to repaint the texture - if (RenderOffScreenBuffers() != -1) - { - // MF - //SwapAndDisplayBuffers(); - } - else - { - // Error updating the mixing texture, don't swap. - } - } - } - - UnlockAGLCntx(); - - //WEBRTC_LOG(kTraceDebug, "Leaving ScreenUpdateProcess()"); - return true; -} - -void VideoRenderAGL::ParentWindowResized(WindowRef window) -{ - //WEBRTC_LOG(kTraceDebug, "%s HIViewRef:%d owner window has resized", __FUNCTION__, (int)_hiviewRef); - - LockAGLCntx(); -k - // set flag - _windowHasResized = false; - - if(FALSE == HIViewIsValid(_hiviewRef)) - { - //WEBRTC_LOG(kTraceDebug, "invalid windowRef"); - UnlockAGLCntx(); - return; - } - - if(FALSE == IsValidWindowPtr(window)) - { - //WEBRTC_LOG(kTraceError, "invalid windowRef"); - UnlockAGLCntx(); - return; - } - - if (window == NULL) - { - //WEBRTC_LOG(kTraceError, "windowRef = NULL"); - UnlockAGLCntx(); - return; - } - - if(FALSE == MacIsWindowVisible(window)) - { - //WEBRTC_LOG(kTraceDebug, "MacIsWindowVisible = FALSE. Returning early."); - UnlockAGLCntx(); - return; - } - - Rect contentBounds = - { 0, 0, 0, 0}; - -#if defined(USE_CONTENT_RGN) - GetWindowBounds(window, kWindowContentRgn, &contentBounds); -#elif defined(USE_STRUCT_RGN) - GetWindowBounds(window, kWindowStructureRgn, &contentBounds); -#endif - - //WEBRTC_LOG(kTraceDebug, "%s contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); - - // update global vars - _currentParentWindowBounds.top = contentBounds.top; - _currentParentWindowBounds.left = contentBounds.left; - _currentParentWindowBounds.bottom = contentBounds.bottom; - _currentParentWindowBounds.right = contentBounds.right; - - _currentParentWindowWidth = _currentParentWindowBounds.right - _currentParentWindowBounds.left; - _currentParentWindowHeight = _currentParentWindowBounds.bottom - _currentParentWindowBounds.top; - - _windowHasResized = true; - - // ********* update AGL offsets - HIRect viewBounds; - HIViewGetBounds(_hiviewRef, &viewBounds); - HIViewConvertRect(&viewBounds, _hiviewRef, NULL); - - const GLint offs[4] = - { (int)(0.5f + viewBounds.origin.x), - (int)(0.5f + _currentParentWindowHeight - (viewBounds.origin.y + viewBounds.size.height)), - viewBounds.size.width, viewBounds.size.height}; - //WEBRTC_LOG(kTraceDebug, "%s _currentParentWindowHeight=%d", __FUNCTION__, _currentParentWindowHeight); - //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); - - aglSetCurrentContext(_aglContext); - aglSetDrawable (_aglContext, GetWindowPort(window)); - aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); - aglEnable(_aglContext, AGL_BUFFER_RECT); - - // We need to change the viewport too if the HIView size has changed - glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); - - UnlockAGLCntx(); - - return; -} - -int VideoRenderAGL::CreateMixingContext() -{ - - LockAGLCntx(); - - //WEBRTC_LOG(kTraceDebug, "Entering CreateMixingContext()"); - - // Use both AGL_ACCELERATED and AGL_NO_RECOVERY to make sure - // a hardware renderer is used and not a software renderer. - - GLint attributes[] = - { - AGL_DOUBLEBUFFER, - AGL_WINDOW, - AGL_RGBA, - AGL_NO_RECOVERY, - AGL_ACCELERATED, - AGL_RED_SIZE, 8, - AGL_GREEN_SIZE, 8, - AGL_BLUE_SIZE, 8, - AGL_ALPHA_SIZE, 8, - AGL_DEPTH_SIZE, 24, - AGL_NONE, - }; - - AGLPixelFormat aglPixelFormat; - - // ***** Set up the OpenGL Context ***** - - // Get a pixel format for the attributes above - aglPixelFormat = aglChoosePixelFormat(NULL, 0, attributes); - if (NULL == aglPixelFormat) - { - //WEBRTC_LOG(kTraceError, "Could not create pixel format"); - UnlockAGLCntx(); - return -1; - } - - // Create an AGL context - _aglContext = aglCreateContext(aglPixelFormat, NULL); - if (_aglContext == NULL) - { - //WEBRTC_LOG(kTraceError, "Could no create AGL context"); - UnlockAGLCntx(); - return -1; - } - - // Release the pixel format memory - aglDestroyPixelFormat(aglPixelFormat); - - // Set the current AGL context for the rest of the settings - if (aglSetCurrentContext(_aglContext) == false) - { - //WEBRTC_LOG(kTraceError, "Could not set current context: %d", aglGetError()); - UnlockAGLCntx(); - return -1; - } - - if (_isHIViewRef) - { - //--------------------------- - // BEGIN: new test code -#if 0 - // Don't use this one! - // There seems to be an OS X bug that can't handle - // movements and resizing of the parent window - // and or the HIView - if (aglSetHIViewRef(_aglContext,_hiviewRef) == false) - { - //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); - UnlockAGLCntx(); - return -1; - } -#else - - // Get the parent window for this control - WindowRef window = GetControlOwner(_hiviewRef); - - Rect globalBounds = - { 0,0,0,0}; // The bounds for the parent window - HIRect viewBounds; // Placemnt in the parent window and size. - int windowHeight = 0; - - // Rect titleBounds = {0,0,0,0}; - // GetWindowBounds(window, kWindowTitleBarRgn, &titleBounds); - // _titleBarHeight = titleBounds.top - titleBounds.bottom; - // if(0 == _titleBarHeight) - // { - // //WEBRTC_LOG(kTraceError, "Titlebar height = 0"); - // //return -1; - // } - - - // Get the bounds for the parent window -#if defined(USE_CONTENT_RGN) - GetWindowBounds(window, kWindowContentRgn, &globalBounds); -#elif defined(USE_STRUCT_RGN) - GetWindowBounds(window, kWindowStructureRgn, &globalBounds); -#endif - windowHeight = globalBounds.bottom - globalBounds.top; - - // Get the bounds for the HIView - HIViewGetBounds(_hiviewRef, &viewBounds); - - HIViewConvertRect(&viewBounds, _hiviewRef, NULL); - - const GLint offs[4] = - { (int)(0.5f + viewBounds.origin.x), - (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), - viewBounds.size.width, viewBounds.size.height}; - - //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); - - - aglSetDrawable (_aglContext, GetWindowPort(window)); - aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); - aglEnable(_aglContext, AGL_BUFFER_RECT); - - GLint surfaceOrder = 1; // 1: above window, -1 below. - //OSStatus status = aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); - aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); - - glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); -#endif - - } - else - { - if(GL_FALSE == aglSetDrawable (_aglContext, GetWindowPort(_windowRef))) - { - //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); - UnlockAGLCntx(); - return -1; - } - } - - _windowWidth = _windowRect.right - _windowRect.left; - _windowHeight = _windowRect.bottom - _windowRect.top; - - // opaque surface - int surfaceOpacity = 1; - if (aglSetInteger(_aglContext, AGL_SURFACE_OPACITY, (const GLint *) &surfaceOpacity) == false) - { - //WEBRTC_LOG(kTraceError, "Could not set surface opacity: %d", aglGetError()); - UnlockAGLCntx(); - return -1; - } - - // 1 -> sync to screen rat, slow... - //int swapInterval = 0; // 0 don't sync with vertical trace - int swapInterval = 0; // 1 sync with vertical trace - if (aglSetInteger(_aglContext, AGL_SWAP_INTERVAL, (const GLint *) &swapInterval) == false) - { - //WEBRTC_LOG(kTraceError, "Could not set swap interval: %d", aglGetError()); - UnlockAGLCntx(); - return -1; - } - - // Update the rect with the current size - if (GetWindowRect(_windowRect) == -1) - { - //WEBRTC_LOG(kTraceError, "Could not get window size"); - UnlockAGLCntx(); - return -1; - } - - // Disable not needed functionality to increase performance - glDisable(GL_DITHER); - glDisable(GL_ALPHA_TEST); - glDisable(GL_STENCIL_TEST); - glDisable(GL_FOG); - glDisable(GL_TEXTURE_2D); - glPixelZoom(1.0, 1.0); - - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDisable(GL_CULL_FACE); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - GLenum glErr = glGetError(); - - if (glErr) - { - } - - UpdateClipping(); - - //WEBRTC_LOG(kTraceDebug, "Leaving CreateMixingContext()"); - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderAGL::RenderOffScreenBuffers() -{ - LockAGLCntx(); - - // Get the current window size, it might have changed since last render. - if (GetWindowRect(_windowRect) == -1) - { - //WEBRTC_LOG(kTraceError, "Could not get window rect"); - UnlockAGLCntx(); - return -1; - } - - if (aglSetCurrentContext(_aglContext) == false) - { - //WEBRTC_LOG(kTraceError, "Could not set current context for rendering"); - UnlockAGLCntx(); - return -1; - } - - // HERE - onl if updated! - glClear(GL_COLOR_BUFFER_BIT); - - // Loop through all channels starting highest zOrder ending with lowest. - for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); - rIt != _zOrderToChannel.rend(); - rIt++) - { - int channelId = rIt->second; - std::map::iterator it = _aglChannels.find(channelId); - - VideoChannelAGL* aglChannel = it->second; - - aglChannel->RenderOffScreenBuffer(); - } - - SwapAndDisplayBuffers(); - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderAGL::SwapAndDisplayBuffers() -{ - - LockAGLCntx(); - if (_fullScreen) - { - // TODO: - // Swap front and back buffers, rendering taking care of in the same call - //aglSwapBuffers(_aglContext); - // Update buffer index to the idx for the next rendering! - //_textureIdx = (_textureIdx + 1) & 1; - } - else - { - // Single buffer rendering, only update context. - glFlush(); - aglSwapBuffers(_aglContext); - HIViewSetNeedsDisplay(_hiviewRef, true); - } - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderAGL::GetWindowRect(Rect& rect) -{ - - LockAGLCntx(); - - if (_isHIViewRef) - { - if (_hiviewRef) - { - HIRect HIViewRect1; - if(FALSE == HIViewIsValid(_hiviewRef)) - { - rect.top = 0; - rect.left = 0; - rect.right = 0; - rect.bottom = 0; - //WEBRTC_LOG(kTraceError,"GetWindowRect() HIViewIsValid() returned false"); - UnlockAGLCntx(); - } - HIViewGetBounds(_hiviewRef,&HIViewRect1); - HIRectConvert(&HIViewRect1, 1, NULL, 2, NULL); - if(HIViewRect1.origin.x < 0) - { - rect.top = 0; - //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.top = 0"); - } - else - { - rect.top = HIViewRect1.origin.x; - } - - if(HIViewRect1.origin.y < 0) - { - rect.left = 0; - //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.left = 0"); - } - else - { - rect.left = HIViewRect1.origin.y; - } - - if(HIViewRect1.size.width < 0) - { - rect.right = 0; - //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.right = 0"); - } - else - { - rect.right = HIViewRect1.size.width; - } - - if(HIViewRect1.size.height < 0) - { - rect.bottom = 0; - //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.bottom = 0"); - } - else - { - rect.bottom = HIViewRect1.size.height; - } - - ////WEBRTC_LOG(kTraceDebug,"GetWindowRect() HIViewRef: rect.top = %d, rect.left = %d, rect.right = %d, rect.bottom =%d in GetWindowRect", rect.top,rect.left,rect.right,rect.bottom); - UnlockAGLCntx(); - } - else - { - //WEBRTC_LOG(kTraceError, "invalid HIViewRef"); - UnlockAGLCntx(); - } - } - else - { - if (_windowRef) - { - GetWindowBounds(_windowRef, kWindowContentRgn, &rect); - UnlockAGLCntx(); - } - else - { - //WEBRTC_LOG(kTraceError, "No WindowRef"); - UnlockAGLCntx(); - } - } -} - -int VideoRenderAGL::UpdateClipping() -{ - //WEBRTC_LOG(kTraceDebug, "Entering UpdateClipping()"); - LockAGLCntx(); - - if(_isHIViewRef) - { - if(FALSE == HIViewIsValid(_hiviewRef)) - { - //WEBRTC_LOG(kTraceError, "UpdateClipping() _isHIViewRef is invalid. Returning -1"); - UnlockAGLCntx(); - return -1; - } - - RgnHandle visibleRgn = NewRgn(); - SetEmptyRgn (visibleRgn); - - if(-1 == CalculateVisibleRegion((ControlRef)_hiviewRef, visibleRgn, true)) - { - } - - if(GL_FALSE == aglSetCurrentContext(_aglContext)) - { - GLenum glErr = aglGetError(); - //WEBRTC_LOG(kTraceError, "aglSetCurrentContext returned FALSE with error code %d at line %d", glErr, __LINE__); - } - - if(GL_FALSE == aglEnable(_aglContext, AGL_CLIP_REGION)) - { - GLenum glErr = aglGetError(); - //WEBRTC_LOG(kTraceError, "aglEnable returned FALSE with error code %d at line %d\n", glErr, __LINE__); - } - - if(GL_FALSE == aglSetInteger(_aglContext, AGL_CLIP_REGION, (const GLint*)visibleRgn)) - { - GLenum glErr = aglGetError(); - //WEBRTC_LOG(kTraceError, "aglSetInteger returned FALSE with error code %d at line %d\n", glErr, __LINE__); - } - - DisposeRgn(visibleRgn); - } - else - { - //WEBRTC_LOG(kTraceDebug, "Not using a hiviewref!\n"); - } - - //WEBRTC_LOG(kTraceDebug, "Leaving UpdateClipping()"); - UnlockAGLCntx(); - return true; -} - -int VideoRenderAGL::CalculateVisibleRegion(ControlRef control, RgnHandle &visibleRgn, bool clipChildren) -{ - - // LockAGLCntx(); - - //WEBRTC_LOG(kTraceDebug, "Entering CalculateVisibleRegion()"); - OSStatus osStatus = 0; - OSErr osErr = 0; - - RgnHandle tempRgn = NewRgn(); - if (IsControlVisible(control)) - { - RgnHandle childRgn = NewRgn(); - WindowRef window = GetControlOwner(control); - ControlRef rootControl; - GetRootControl(window, &rootControl); // 'wvnc' - ControlRef masterControl; - osStatus = GetSuperControl(rootControl, &masterControl); - // //WEBRTC_LOG(kTraceDebug, "IBM GetSuperControl=%d", osStatus); - - if (masterControl != NULL) - { - CheckValidRegion(visibleRgn); - // init visibleRgn with region of 'wvnc' - osStatus = GetControlRegion(rootControl, kControlStructureMetaPart, visibleRgn); - // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); - //GetSuperControl(rootControl, &rootControl); - ControlRef tempControl = control, lastControl = 0; - while (tempControl != masterControl) // current control != master - - { - CheckValidRegion(tempRgn); - - // //WEBRTC_LOG(kTraceDebug, "IBM tempControl=%d masterControl=%d", tempControl, masterControl); - ControlRef subControl; - - osStatus = GetControlRegion(tempControl, kControlStructureMetaPart, tempRgn); // intersect the region of the current control with visibleRgn - // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); - CheckValidRegion(tempRgn); - - osErr = HIViewConvertRegion(tempRgn, tempControl, rootControl); - // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d : %d", osErr, __LINE__); - CheckValidRegion(tempRgn); - - SectRgn(tempRgn, visibleRgn, visibleRgn); - CheckValidRegion(tempRgn); - CheckValidRegion(visibleRgn); - if (EmptyRgn(visibleRgn)) // if the region is empty, bail - break; - - if (clipChildren || tempControl != control) // clip children if true, cut out the tempControl if it's not one passed to this function - - { - UInt16 numChildren; - osStatus = CountSubControls(tempControl, &numChildren); // count the subcontrols - // //WEBRTC_LOG(kTraceDebug, "IBM CountSubControls=%d : %d", osStatus, __LINE__); - - // //WEBRTC_LOG(kTraceDebug, "IBM numChildren=%d", numChildren); - for (int i = 0; i < numChildren; i++) - { - osErr = GetIndexedSubControl(tempControl, numChildren - i, &subControl); // retrieve the subcontrol in order by zorder - // //WEBRTC_LOG(kTraceDebug, "IBM GetIndexedSubControls=%d : %d", osErr, __LINE__); - if ( subControl == lastControl ) // break because of zorder - - { - // //WEBRTC_LOG(kTraceDebug, "IBM breaking because of zorder %d", __LINE__); - break; - } - - if (!IsControlVisible(subControl)) // dont' clip invisible controls - - { - // //WEBRTC_LOG(kTraceDebug, "IBM continue. Control is not visible %d", __LINE__); - continue; - } - - if(!subControl) continue; - - osStatus = GetControlRegion(subControl, kControlStructureMetaPart, tempRgn); //get the region of the current control and union to childrg - // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d %d", osStatus, __LINE__); - CheckValidRegion(tempRgn); - if(osStatus != 0) - { - // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); - continue; - } - if(!tempRgn) - { - // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !tempRgn %d", osStatus, __LINE__); - continue; - } - - osStatus = HIViewConvertRegion(tempRgn, subControl, rootControl); - CheckValidRegion(tempRgn); - // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d %d", osStatus, __LINE__); - if(osStatus != 0) - { - // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); - continue; - } - if(!rootControl) - { - // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !rootControl %d", osStatus, __LINE__); - continue; - } - - UnionRgn(tempRgn, childRgn, childRgn); - CheckValidRegion(tempRgn); - CheckValidRegion(childRgn); - CheckValidRegion(visibleRgn); - if(!childRgn) - { - // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !childRgn %d", osStatus, __LINE__); - continue; - } - - } // next child control - } - lastControl = tempControl; - GetSuperControl(tempControl, &subControl); - tempControl = subControl; - } - - DiffRgn(visibleRgn, childRgn, visibleRgn); - CheckValidRegion(visibleRgn); - CheckValidRegion(childRgn); - DisposeRgn(childRgn); - } - else - { - CopyRgn(tempRgn, visibleRgn); - CheckValidRegion(tempRgn); - CheckValidRegion(visibleRgn); - } - DisposeRgn(tempRgn); - } - - //WEBRTC_LOG(kTraceDebug, "Leaving CalculateVisibleRegion()"); - //_aglCritPtr->Leave(); - return 0; -} - -bool VideoRenderAGL::CheckValidRegion(RgnHandle rHandle) -{ - - Handle hndSize = (Handle)rHandle; - long size = GetHandleSize(hndSize); - if(0 == size) - { - - OSErr memErr = MemError(); - if(noErr != memErr) - { - // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle. MemError() returned %d", memErr); - } - else - { - // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle yet MemError() returned noErr"); - } - - } - else - { - // //WEBRTC_LOG(kTraceDebug, "IBM handleSize = %d", size); - } - - if(false == IsValidRgnHandle(rHandle)) - { - // //WEBRTC_LOG(kTraceError, "IBM ERROR Invalid Region found : $%d", rHandle); - assert(false); - } - - int err = QDError(); - switch(err) - { - case 0: - break; - case -147: - //WEBRTC_LOG(kTraceError, "ERROR region too big"); - assert(false); - break; - - case -149: - //WEBRTC_LOG(kTraceError, "ERROR not enough stack"); - assert(false); - break; - - default: - //WEBRTC_LOG(kTraceError, "ERROR Unknown QDError %d", err); - assert(false); - break; - } - - return true; -} - -int VideoRenderAGL::ChangeWindow(void* newWindowRef) -{ - - LockAGLCntx(); - - UnlockAGLCntx(); - return -1; -} -WebRtc_Word32 VideoRenderAGL::ChangeUniqueID(WebRtc_Word32 id) -{ - LockAGLCntx(); - - UnlockAGLCntx(); - return -1; -} - -WebRtc_Word32 VideoRenderAGL::StartRender() -{ - - LockAGLCntx(); - const unsigned int MONITOR_FREQ = 60; - if(TRUE == _renderingIsPaused) - { - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Rendering is paused. Restarting now", __FUNCTION__, __LINE__); - - // we already have the thread. Most likely StopRender() was called and they were paused - if(FALSE == _screenUpdateThread->Start(_threadID)) - { - //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return -1; - } - if(FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) - { - //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateEvent", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return -1; - } - - return 0; - } - - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); - - if (!_screenUpdateThread) - { - //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return -1; - } - - _screenUpdateThread->Start(_threadID); - _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ); - - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Started screenUpdateThread", __FUNCTION__, __LINE__); - - UnlockAGLCntx(); - return 0; - -} - -WebRtc_Word32 VideoRenderAGL::StopRender() -{ - LockAGLCntx(); - - if(!_screenUpdateThread || !_screenUpdateEvent) - { - _renderingIsPaused = TRUE; - UnlockAGLCntx(); - return 0; - } - - if(FALSE == _screenUpdateThread->Stop() || FALSE == _screenUpdateEvent->StopTimer()) - { - _renderingIsPaused = FALSE; - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Could not stop either: screenUpdateThread or screenUpdateEvent", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return -1; - } - - _renderingIsPaused = TRUE; - - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Stopped screenUpdateThread", __FUNCTION__, __LINE__); - UnlockAGLCntx(); - return 0; -} - -WebRtc_Word32 VideoRenderAGL::DeleteAGLChannel(const WebRtc_UWord32 streamID) -{ - - LockAGLCntx(); - - std::map::iterator it; - it = _aglChannels.begin(); - - while (it != _aglChannels.end()) - { - VideoChannelAGL* channel = it->second; - //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Deleting channel %d", __FUNCTION__, __LINE__, streamID); - delete channel; - it++; - } - _aglChannels.clear(); - - UnlockAGLCntx(); - return 0; -} - -WebRtc_Word32 VideoRenderAGL::GetChannelProperties(const WebRtc_UWord16 streamId, -WebRtc_UWord32& zOrder, -float& left, -float& top, -float& right, -float& bottom) -{ - - LockAGLCntx(); - UnlockAGLCntx(); - return -1; - -} - -void VideoRenderAGL::LockAGLCntx() -{ - _renderCritSec.Enter(); -} -void VideoRenderAGL::UnlockAGLCntx() -{ - _renderCritSec.Leave(); -} - -} //namespace webrtc - -#endif // CARBON_RENDERING - diff --git a/modules/video_render/main/source/mac/video_render_agl.h b/modules/video_render/main/source/mac/video_render_agl.h deleted file mode 100644 index bdee619e7..000000000 --- a/modules/video_render/main/source/mac/video_render_agl.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" - -#if defined(CARBON_RENDERING) - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ - - -#include "video_render_defines.h" - - -#define NEW_HIVIEW_PARENT_EVENT_HANDLER 1 -#define NEW_HIVIEW_EVENT_HANDLER 1 -#define USE_STRUCT_RGN - -#include -#include -#include -#include -#include -#include -#include - -class VideoRenderAGL; - -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; - -class VideoChannelAGL : public VideoRenderCallback -{ -public: - - VideoChannelAGL(AGLContext& aglContext, int iId, VideoRenderAGL* owner); - virtual ~VideoChannelAGL(); - virtual int FrameSizeChange(int width, int height, int numberOfStreams); - virtual int DeliverFrame(unsigned char* buffer, int bufferSize, unsigned int timeStame90kHz); - virtual int UpdateSize(int width, int height); - int SetStreamSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); - int SetStreamCropSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); - int RenderOffScreenBuffer(); - int IsUpdated(bool& isUpdated); - virtual int UpdateStretchSize(int stretchHeight, int stretchWidth); - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, VideoFrame& videoFrame); - - -private: - - AGLContext _aglContext; - int _id; - VideoRenderAGL* _owner; - int _width; - int _height; - int _stretchedWidth; - int _stretchedHeight; - float _startHeight; - float _startWidth; - float _stopWidth; - float _stopHeight; - int _xOldWidth; - int _yOldHeight; - int _oldStretchedHeight; - int _oldStretchedWidth; - unsigned char* _buffer; - int _bufferSize; - int _incommingBufferSize; - bool _bufferIsUpdated; - bool _sizeInitialized; - int _numberOfStreams; - bool _bVideoSizeStartedChanging; - GLenum _pixelFormat; - GLenum _pixelDataType; - unsigned int _texture; -}; - - - - -class VideoRenderAGL -{ -public: - VideoRenderAGL(WindowRef windowRef, bool fullscreen, int iId); - VideoRenderAGL(HIViewRef windowRef, bool fullscreen, int iId); - ~VideoRenderAGL(); - - int Init(); - VideoChannelAGL* CreateAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); - VideoChannelAGL* ConfigureAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); - int DeleteAGLChannel(int channel); - int DeleteAllAGLChannels(); - int StopThread(); - bool IsFullScreen(); - bool HasChannels(); - bool HasChannel(int channel); - int GetChannels(std::list& channelList); - void LockAGLCntx(); - void UnlockAGLCntx(); - - static int GetOpenGLVersion(int& aglMajor, int& aglMinor); - - // ********** new module functions ************ // - int ChangeWindow(void* newWindowRef); - WebRtc_Word32 ChangeUniqueID(WebRtc_Word32 id); - WebRtc_Word32 StartRender(); - WebRtc_Word32 StopRender(); - WebRtc_Word32 DeleteAGLChannel(const WebRtc_UWord32 streamID); - WebRtc_Word32 GetChannelProperties(const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom); - -protected: - static bool ScreenUpdateThreadProc(void* obj); - bool ScreenUpdateProcess(); - int GetWindowRect(Rect& rect); - -private: - int CreateMixingContext(); - int RenderOffScreenBuffers(); - int SwapAndDisplayBuffers(); - int UpdateClipping(); - int CalculateVisibleRegion(ControlRef control, RgnHandle &visibleRgn, bool clipChildren); - bool CheckValidRegion(RgnHandle rHandle); - void ParentWindowResized(WindowRef window); - - // Carbon GUI event handlers - static pascal OSStatus sHandleWindowResized (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData); - static pascal OSStatus sHandleHiViewResized (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData); - - HIViewRef _hiviewRef; - WindowRef _windowRef; - bool _fullScreen; - int _id; - webrtc::CriticalSectionWrapper& _renderCritSec; - webrtc::ThreadWrapper* _screenUpdateThread; - webrtc::EventWrapper* _screenUpdateEvent; - bool _isHIViewRef; - AGLContext _aglContext; - int _windowWidth; - int _windowHeight; - int _lastWindowWidth; - int _lastWindowHeight; - int _lastHiViewWidth; - int _lastHiViewHeight; - int _currentParentWindowHeight; - int _currentParentWindowWidth; - Rect _currentParentWindowBounds; - bool _windowHasResized; - Rect _lastParentWindowBounds; - Rect _currentHIViewBounds; - Rect _lastHIViewBounds; - Rect _windowRect; - std::map _aglChannels; - std::multimap _zOrderToChannel; - EventHandlerRef _hiviewEventHandlerRef; - EventHandlerRef _windowEventHandlerRef; - HIRect _currentViewBounds; - HIRect _lastViewBounds; - bool _renderingIsPaused; - unsigned int _threadID; - - - - -}; - -} //namespace webrtc - - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ - -#endif // CARBON_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_mac_carbon_impl.cc b/modules/video_render/main/source/mac/video_render_mac_carbon_impl.cc deleted file mode 100644 index 961a68588..000000000 --- a/modules/video_render/main/source/mac/video_render_mac_carbon_impl.cc +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#if defined(CARBON_RENDERING) - -#include "video_render_mac_carbon_impl.h" -#include "critical_section_wrapper.h" -#include "video_render_agl.h" -#include "trace.h" -#include - -namespace webrtc { - -VideoRenderMacCarbonImpl::VideoRenderMacCarbonImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : -_id(id), -_renderMacCarbonCritsect(*CriticalSectionWrapper::CreateCriticalSection()), -_fullScreen(fullscreen), -_ptrWindow(window) -{ - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); - -} - -VideoRenderMacCarbonImpl::~VideoRenderMacCarbonImpl() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Destructor %s:%d", __FUNCTION__, __LINE__); - delete &_renderMacCarbonCritsect; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::Init() -{ - CriticalSectionScoped cs(_renderMacCarbonCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - if (!_ptrWindow) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); - return -1; - } - - // We don't know if the user passed us a WindowRef or a HIViewRef, so test. - bool referenceIsValid = false; - - // Check if it's a valid WindowRef - //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d _ptrWindowRef before WindowRef cast: %x", __FUNCTION__, __LINE__, _ptrWindowRef); - WindowRef* windowRef = static_cast(_ptrWindow); - //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d _ptrWindowRef after cast: %x", __FUNCTION__, __LINE__, _ptrWindowRef); - if (IsValidWindowPtr(*windowRef)) - { - _ptrCarbonRender = new VideoRenderAGL(*windowRef, _fullScreen, _id); - referenceIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Successfully initialized CarbonRenderer with WindowRef:%x", __FUNCTION__, __LINE__, *windowRef); - } - else - { - HIViewRef* hiviewRef = static_cast(_ptrWindow); - if (HIViewIsValid(*hiviewRef)) - { - _ptrCarbonRender = new VideoRenderAGL(*hiviewRef, _fullScreen, _id); - referenceIsValid = true; - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Successfully initialized CarbonRenderer with HIViewRef:%x", __FUNCTION__, __LINE__, hiviewRef); - } - } - - if(!referenceIsValid) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Invalid WindowRef/HIViewRef Returning -1", __FUNCTION__, __LINE__); - return -1; - } - - if(!_ptrCarbonRender) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to create an instance of VideoRenderAGL. Returning -1", __FUNCTION__, __LINE__); - } - - int retVal = _ptrCarbonRender->Init(); - if (retVal == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to init CarbonRenderer", __FUNCTION__, __LINE__); - return -1; - } - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - return -1; - - CriticalSectionScoped cs(_renderMacCarbonCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - _id = id; - - if(_ptrCarbonRender) - { - _ptrCarbonRender->ChangeUniqueID(_id); - } - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::ChangeWindow(void* window) -{ - return -1; - CriticalSectionScoped cs(_renderMacCarbonCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s changing ID to ", __FUNCTION__, window); - - if (window == NULL) - { - return -1; - } - _ptrWindow = window; - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - _ptrWindow = window; - - return 0; -} - -VideoRenderCallback* -VideoRenderMacCarbonImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - - CriticalSectionScoped cs(_renderMacCarbonCritsect); - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - VideoChannelAGL* AGLChannel = NULL; - - if(!_ptrWindow) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s, no window", __FUNCTION__); - } - - if(!AGLChannel) - { - AGLChannel = _ptrCocoaRender->CreateNSGLChannel(streamId, zOrder, left, top, right, bottom); - } - - return AGLChannel; - -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::DeleteIncomingRenderStream(const WebRtc_UWord32 streamId) -{ - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - CriticalSectionScoped cs(_renderMacCarbonCritsect); - _ptrCarbonRender->DeleteAGLChannel(streamId); - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - return -1; - return _ptrCarbonRender->GetChannelProperties(streamId, zOrder, left, top, right, bottom); -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::StartRender() -{ - return _ptrCarbonRender->StartRender(); -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::StopRender() -{ - return _ptrCarbonRender->StopRender(); -} - -VideoRenderType -VideoRenderMacCarbonImpl::RenderType() -{ - return kRenderCarbon; -} - -RawVideoType -VideoRenderMacCarbonImpl::PerferedVideoType() -{ - return kVideoI420; -} - -bool -VideoRenderMacCarbonImpl::FullScreen() -{ - return false; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const -{ - totalGraphicsMemory = 0; - availableGraphicsMemory = 0; - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const -{ - CriticalSectionScoped cs(_renderMacCarbonCritsect); - //NSScreen* mainScreen = [NSScreen mainScreen]; - - //NSRect frame = [mainScreen frame]; - - //screenWidth = frame.size.width; - //screenHeight = frame.size.height; - return 0; -} - -WebRtc_UWord32 -VideoRenderMacCarbonImpl::RenderFrameRate(const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_renderMacCarbonCritsect); - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCarbonImpl::ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 -VideoRenderMacCarbonImpl::SetTransparentBackground(const bool enable) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCarbonImpl::SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCarbonImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - - -} //namespace webrtc - -#endif // CARBON_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_mac_carbon_impl.h b/modules/video_render/main/source/mac/video_render_mac_carbon_impl.h deleted file mode 100644 index 3ff3d26d8..000000000 --- a/modules/video_render/main/source/mac/video_render_mac_carbon_impl.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#if defined(CARBON_RENDERING) - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ - -#include "i_video_render.h" - -namespace webrtc { - -class CriticalSectionWrapper; -class VideoRenderAGL; - -// Class definitions -class VideoRenderMacCarbonImpl : IVideoRender -{ -public: - /* - * Constructor/destructor - */ - - VideoRenderMacCarbonImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen); - - virtual ~VideoRenderMacCarbonImpl(); - - virtual WebRtc_Word32 Init(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - virtual VideoRenderCallback* AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 FullScreenRender(void* window, const bool enable) - { - // not supported in Carbon at this time - return -1; - } - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _renderMacCarbonCritsect; - bool _fullScreen; - void* _ptrWindow; - VideoRenderAGL* _ptrCarbonRender; - -}; - - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ -#endif // CARBON_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.cc b/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.cc deleted file mode 100644 index 0826539dd..000000000 --- a/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.cc +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#if defined(COCOA_RENDERING) - -#import "cocoa_render_view.h" - -#include "video_render_mac_cocoa_impl.h" -#include "critical_section_wrapper.h" -#include "video_render_nsopengl.h" -#include "trace.h" - -namespace webrtc { - -VideoRenderMacCocoaImpl::VideoRenderMacCocoaImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : -_id(id), -_renderMacCocoaCritsect(*CriticalSectionWrapper::CreateCriticalSection()), -_fullScreen(fullscreen), -_ptrWindow(window) -{ - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); -} - -VideoRenderMacCocoaImpl::~VideoRenderMacCocoaImpl() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Destructor %s:%d", __FUNCTION__, __LINE__); - delete &_renderMacCocoaCritsect; - if (_ptrCocoaRender) - { - delete _ptrCocoaRender; - _ptrCocoaRender = NULL; - } -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::Init() -{ - - CriticalSectionScoped cs(_renderMacCocoaCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - // cast ptrWindow from void* to CocoaRenderer. Void* was once NSOpenGLView, and CocoaRenderer is NSOpenGLView. - _ptrCocoaRender = new VideoRenderNSOpenGL((CocoaRenderView*)_ptrWindow, _fullScreen, _id); - if (!_ptrWindow) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); - return -1; - } - int retVal = _ptrCocoaRender->Init(); - if (retVal == -1) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Failed to init %s:%d", __FUNCTION__, __LINE__); - return -1; - } - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_renderMacCocoaCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - _id = id; - - if(_ptrCocoaRender) - { - _ptrCocoaRender->ChangeUniqueID(_id); - } - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::ChangeWindow(void* window) -{ - - CriticalSectionScoped cs(_renderMacCocoaCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s changing ID to ", __FUNCTION__, window); - - if (window == NULL) - { - return -1; - } - _ptrWindow = window; - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - _ptrWindow = window; - _ptrCocoaRender->ChangeWindow((CocoaRenderView*)_ptrWindow); - - return 0; -} - -VideoRenderCallback* -VideoRenderMacCocoaImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderMacCocoaCritsect); - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); - VideoChannelNSOpenGL* nsOpenGLChannel = NULL; - - if(!_ptrWindow) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s, no window", __FUNCTION__); - } - - if(!nsOpenGLChannel) - { - nsOpenGLChannel = _ptrCocoaRender->CreateNSGLChannel(streamId, zOrder, left, top, right, bottom); - } - - return nsOpenGLChannel; - -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::DeleteIncomingRenderStream(const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); - CriticalSectionScoped cs(_renderMacCocoaCritsect); - _ptrCocoaRender->DeleteNSGLChannel(streamId); - - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - return _ptrCocoaRender->GetChannelProperties(streamId, zOrder, left, top, right, bottom); -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::StartRender() -{ - return _ptrCocoaRender->StartRender(); -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::StopRender() -{ - return _ptrCocoaRender->StopRender(); -} - -VideoRenderType -VideoRenderMacCocoaImpl::RenderType() -{ - return kRenderCocoa; -} - -RawVideoType -VideoRenderMacCocoaImpl::PerferedVideoType() -{ - return kVideoI420; -} - -bool -VideoRenderMacCocoaImpl::FullScreen() -{ - return false; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const -{ - totalGraphicsMemory = 0; - availableGraphicsMemory = 0; - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const -{ - CriticalSectionScoped cs(_renderMacCocoaCritsect); - NSScreen* mainScreen = [NSScreen mainScreen]; - - NSRect frame = [mainScreen frame]; - - screenWidth = frame.size.width; - screenHeight = frame.size.height; - return 0; -} - -WebRtc_UWord32 -VideoRenderMacCocoaImpl::RenderFrameRate(const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_renderMacCocoaCritsect); - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCocoaImpl::ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 -VideoRenderMacCocoaImpl::SetTransparentBackground(const bool enable) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCocoaImpl::SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom) -{ - return _ptrCocoaRender->SetText(textId, text, textLength, textColorRef, backgroundColorRef, left, top, right, bottom); -} - -WebRtc_Word32 VideoRenderMacCocoaImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - return 0; -} - -WebRtc_Word32 VideoRenderMacCocoaImpl::FullScreenRender(void* window, const bool enable) -{ - return -1; -} - -} //namespace webrtc - -#endif // COCOA_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.h b/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.h deleted file mode 100644 index f9216bbb8..000000000 --- a/modules/video_render/main/source/mac/video_render_mac_cocoa_impl.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" - -#if defined(COCOA_RENDERING) - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ - -#include "i_video_render.h" - -namespace webrtc { -class CriticalSectionWrapper; -class VideoRenderNSOpenGL; - -// Class definitions -class VideoRenderMacCocoaImpl : IVideoRender -{ -public: - /* - * Constructor/destructor - */ - - VideoRenderMacCocoaImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen); - - virtual ~VideoRenderMacCocoaImpl(); - - virtual WebRtc_Word32 Init(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - virtual VideoRenderCallback* AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 FullScreenRender(void* window, const bool enable); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _renderMacCocoaCritsect; - bool _fullScreen; - void* _ptrWindow; - VideoRenderNSOpenGL* _ptrCocoaRender; - -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ -#endif // COCOA_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_nsopengl.cc b/modules/video_render/main/source/mac/video_render_nsopengl.cc deleted file mode 100644 index 8249211b1..000000000 --- a/modules/video_render/main/source/mac/video_render_nsopengl.cc +++ /dev/null @@ -1,1266 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#if defined(COCOA_RENDERING) - -#include "video_render_nsopengl.h" -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "vplib.h" - -namespace webrtc { - -VideoChannelNSOpenGL::VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner) : -_nsglContext( nsglContext), -_id( iId), -_owner( owner), -_width( 0), -_height( 0), -_startWidth( 0.0f), -_startHeight( 0.0f), -_stopWidth( 0.0f), -_stopHeight( 0.0f), -_stretchedWidth( 0), -_stretchedHeight( 0), -_oldStretchedHeight( 0), -_oldStretchedWidth( 0), -_xOldWidth( 0), -_yOldHeight( 0), -_buffer( 0), -_bufferSize( 0), -_incommingBufferSize( 0), -_bufferIsUpdated( false), -_numberOfStreams( 0), -_pixelFormat( GL_RGBA), -_pixelDataType( GL_UNSIGNED_INT_8_8_8_8), -_texture( 0), -_bVideoSizeStartedChanging(false) - -{ - -} - -VideoChannelNSOpenGL::~VideoChannelNSOpenGL() -{ - if (_buffer) - { - delete [] _buffer; - _buffer = NULL; - } - - if (_texture != 0) - { - [_nsglContext makeCurrentContext]; - glDeleteTextures(1, (const GLuint*) &_texture); - _texture = 0; - } -} - -int VideoChannelNSOpenGL::ChangeContext(NSOpenGLContext *nsglContext) -{ - _owner->UnlockAGLCntx(); - - _nsglContext = nsglContext; - [_nsglContext makeCurrentContext]; - - _owner->UnlockAGLCntx(); - return 0; - -} - -WebRtc_Word32 VideoChannelNSOpenGL::GetChannelProperties(float& left, - float& top, - float& right, - float& bottom) -{ - - _owner->LockAGLCntx(); - - left = _startWidth; - top = _startHeight; - right = _stopWidth; - bottom = _stopHeight; - - _owner->UnlockAGLCntx(); - return 0; -} - -WebRtc_Word32 VideoChannelNSOpenGL::RenderFrame(const WebRtc_UWord32 /*streamId*/, VideoFrame& videoFrame) -{ - - _owner->LockAGLCntx(); - - if(_width != (int)videoFrame.Width() || - _height != (int)videoFrame.Height()) - { - if(FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) - { - _owner->UnlockAGLCntx(); - return -1; - } - } - - int ret = DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), videoFrame.TimeStamp()); - - _owner->UnlockAGLCntx(); - return ret; -} - -int VideoChannelNSOpenGL::UpdateSize(int width, int height) -{ - _owner->LockAGLCntx(); - _width = width; - _height = height; - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::UpdateStretchSize(int stretchHeight, int stretchWidth) -{ - - _owner->LockAGLCntx(); - _stretchedHeight = stretchHeight; - _stretchedWidth = stretchWidth; - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::FrameSizeChange(int width, int height, int numberOfStreams) -{ - // We got a new frame size from VideoAPI, prepare the buffer - - _owner->LockAGLCntx(); - - if (width == _width && _height == height) - { - // We already have a correct buffer size - _numberOfStreams = numberOfStreams; - _owner->UnlockAGLCntx(); - return 0; - } - - _width = width; - _height = height; - - // Delete the old buffer, create a new one with correct size. - if (_buffer) - { - delete [] _buffer; - _bufferSize = 0; - } - - _incommingBufferSize = CalcBufferSize(kI420, _width, _height); - _bufferSize = CalcBufferSize(kARGB, _width, _height);//_width * _height * bytesPerPixel; - _buffer = new unsigned char [_bufferSize]; - memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); - - [_nsglContext makeCurrentContext]; - - if(glIsTexture(_texture)) - { - glDeleteTextures(1, (const GLuint*) &_texture); - _texture = 0; - } - - // Create a new texture - glGenTextures(1, (GLuint *) &_texture); - - GLenum glErr = glGetError(); - - if (glErr != GL_NO_ERROR) - { - - } - - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); - - GLint texSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); - - if (texSize < _width || texSize < _height) - { - _owner->UnlockAGLCntx(); - return -1; - } - - // Set up th texture type and size - glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target - 0, // level - GL_RGBA, // internal format - _width, // width - _height, // height - 0, // border 0/1 = off/on - _pixelFormat, // format, GL_RGBA - _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 - _buffer); // pixel data - - glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - _owner->UnlockAGLCntx(); - return -1; - } - - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::DeliverFrame(unsigned char* buffer, int bufferSize, unsigned int /*timeStamp90kHz*/) -{ - - _owner->LockAGLCntx(); - - if (_texture == 0) - { - _owner->UnlockAGLCntx(); - return 0; - } - - if (bufferSize != _incommingBufferSize) - { - _owner->UnlockAGLCntx(); - return -1; - } - - int rgbLength = ConvertFromI420(kRGBAMac, buffer, _width, _height, _buffer); - if (rgbLength == -1) - { - _owner->UnlockAGLCntx(); - return -1; - } - - [_nsglContext makeCurrentContext]; - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "bufferSize=%d _width=%d _height=%d", bufferSize, _width, _height); - - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); // Make sure this texture is the active one - GLenum glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "ERROR %d while calling glBindTexture", glErr); - _owner->UnlockAGLCntx(); - return -1; - } - - glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, - 0, // Level, not use - 0, // start point x, (low left of pic) - 0, // start point y, - _width, // width - _height, // height - _pixelFormat, // pictue format for _buffer - _pixelDataType, // data type of _buffer - (const GLvoid*) _buffer); // the pixel data - - glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "ERROR %d while calling glTexSubImage2d", glErr); - _owner->UnlockAGLCntx(); - return -1; - } - - _bufferIsUpdated = true; - - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::RenderOffScreenBuffer() -{ - - _owner->LockAGLCntx(); - - if (_texture == 0) - { - _owner->UnlockAGLCntx(); - return 0; - } - - // if(_fullscreen) - // { - NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; - // _width = mainDisplayRect.size.width; - // _height = mainDisplayRect.size.height; - // glViewport(0, 0, mainDisplayRect.size.width, mainDisplayRect.size.height); - // float newX = mainDisplayRect.size.width/_width; - // float newY = mainDisplayRect.size.height/_height; - - // convert from 0.0 <= size <= 1.0 to - // open gl world -1.0 < size < 1.0 - GLfloat xStart = 2.0f * _startWidth - 1.0f; - GLfloat xStop = 2.0f * _stopWidth - 1.0f; - GLfloat yStart = 1.0f - 2.0f * _stopHeight; - GLfloat yStop = 1.0f - 2.0f * _startHeight; - - [_nsglContext makeCurrentContext]; - - glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); - _oldStretchedHeight = _stretchedHeight; - _oldStretchedWidth = _stretchedWidth; - - glLoadIdentity(); - glEnable(GL_TEXTURE_RECTANGLE_EXT); - glBegin(GL_POLYGON); - { - glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); - glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); - glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); - glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); - } - glEnd(); - - glDisable(GL_TEXTURE_RECTANGLE_EXT); - - _bufferIsUpdated = false; - - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::IsUpdated(bool& isUpdated) -{ - _owner->LockAGLCntx(); - - isUpdated = _bufferIsUpdated; - - _owner->UnlockAGLCntx(); - return 0; -} - -int VideoChannelNSOpenGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - _owner->LockAGLCntx(); - - _startWidth = startWidth; - _stopWidth = stopWidth; - _startHeight = startHeight; - _stopHeight = stopHeight; - - int oldWidth = _width; - int oldHeight = _height; - int oldNumberOfStreams = _numberOfStreams; - - _width = 0; - _height = 0; - - int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); - - _owner->UnlockAGLCntx(); - return retVal; -} - -int VideoChannelNSOpenGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) -{ - return -1; -} - -/* - * - * VideoRenderNSOpenGL - * - */ - -VideoRenderNSOpenGL::VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId) : -_windowRef( (CocoaRenderView*)windowRef), -_fullScreen( fullScreen), -_id( iId), -_nsglContextCritSec( *CriticalSectionWrapper::CreateCriticalSection()), -_screenUpdateThread( 0), -_screenUpdateEvent( 0), -_nsglContext( 0), -_nsglFullScreenContext( 0), -_fullScreenWindow( nil), -_windowRect( ), -_windowWidth( 0), -_windowHeight( 0), -_nsglChannels( ), -_zOrderToChannel( ), -_threadID (0), -_renderingIsPaused (FALSE) -{ - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); -} - -int VideoRenderNSOpenGL::ChangeWindow(CocoaRenderView* newWindowRef) -{ - - LockAGLCntx(); - - _windowRef = newWindowRef; - - if(CreateMixingContext() == -1) - { - UnlockAGLCntx(); - return -1; - } - - int error = 0; - std::map::iterator it = _nsglChannels.begin(); - while (it!= _nsglChannels.end()) - { - error |= (it->second)->ChangeContext(_nsglContext); - it++; - } - if(error != 0) - { - UnlockAGLCntx(); - return -1; - } - - UnlockAGLCntx(); - return 0; -} - -/* Check if the thread and event already exist. - * If so then they will simply be restarted - * If not then create them and continue - */ -WebRtc_Word32 VideoRenderNSOpenGL::StartRender() -{ - - LockAGLCntx(); - - const unsigned int MONITOR_FREQ = 60; - if(TRUE == _renderingIsPaused) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Restarting screenUpdateThread"); - - // we already have the thread. Most likely StopRender() was called and they were paused - if(FALSE == _screenUpdateThread->Start(_threadID) || - FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); - UnlockAGLCntx(); - return -1; - } - - UnlockAGLCntx(); - return 0; - } - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Starting screenUpdateThread and screenUpdateEvent"); - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); - - if (!_screenUpdateThread) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "failed start screenUpdateThread"); - UnlockAGLCntx(); - return -1; - } - - _screenUpdateThread->Start(_threadID); - - _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ); - - UnlockAGLCntx(); - return 0; -} -WebRtc_Word32 VideoRenderNSOpenGL::StopRender() -{ - - LockAGLCntx(); - - /* The code below is functional - * but it pauses for several seconds - */ - - // pause the update thread and the event timer - if(!_screenUpdateThread || !_screenUpdateEvent) - { - _renderingIsPaused = TRUE; - - UnlockAGLCntx(); - return 0; - } - - if(FALSE == _screenUpdateThread->Stop() || FALSE == _screenUpdateEvent->StopTimer()) - { - _renderingIsPaused = FALSE; - - UnlockAGLCntx(); - return -1; - } - - _renderingIsPaused = TRUE; - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderNSOpenGL::configureNSOpenGLView() -{ - return 0; - -} - -int VideoRenderNSOpenGL::configureNSOpenGLEngine() -{ - - LockAGLCntx(); - - // Disable not needed functionality to increase performance - glDisable(GL_DITHER); - glDisable(GL_ALPHA_TEST); - glDisable(GL_STENCIL_TEST); - glDisable(GL_FOG); - glDisable(GL_TEXTURE_2D); - glPixelZoom(1.0, 1.0); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDisable(GL_CULL_FACE); - - // Set texture parameters - glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); - - if (GetWindowRect(_windowRect) == -1) - { - UnlockAGLCntx(); - return true; - } - - if (_windowWidth != (_windowRect.right - _windowRect.left) - || _windowHeight != (_windowRect.bottom - _windowRect.top)) - { - _windowWidth = _windowRect.right - _windowRect.left; - _windowHeight = _windowRect.bottom - _windowRect.top; - } - glViewport(0, 0, _windowWidth, _windowHeight); - - // Synchronize buffer swaps with vertical refresh rate - GLint swapInt = 1; - [_nsglContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderNSOpenGL::setRenderTargetWindow() -{ - LockAGLCntx(); - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - GLuint attribs[] = - { - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 16, - NSOpenGLPFAAccelerated, - 0 - }; - - NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; - - if(_windowRef) - { - [_windowRef initCocoaRenderView:fmt]; - } - else - { - UnlockAGLCntx(); - return -1; - } - - [fmt release]; - - _nsglContext = [_windowRef nsOpenGLContext]; - [_nsglContext makeCurrentContext]; - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "setRenderTargetWindow glClearColor 0.5"); - - DisplayBuffers(); - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderNSOpenGL::setRenderTargetFullScreen() -{ - LockAGLCntx(); - - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); - - GLuint attribs[] = - { - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 16, - NSOpenGLPFAAccelerated, - 0 - }; - - NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; - - // create new fullscreen window - NSRect screenRect = [[NSScreen mainScreen]frame]; - [_windowRef setFrame:screenRect]; - [_windowRef setBounds:screenRect]; - - _fullScreenWindow = [[CocoaFullScreenWindow alloc]init]; - [_fullScreenWindow grabFullScreen]; - [[[_fullScreenWindow window] contentView] addSubview:_windowRef]; - - if(_windowRef) - { - [_windowRef initCocoaRenderViewFullScreen:fmt]; - } - else - { - UnlockAGLCntx(); - return -1; - } - - [fmt release]; - - _nsglContext = [_windowRef nsOpenGLContext]; - [_nsglContext makeCurrentContext]; - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - DisplayBuffers(); - - UnlockAGLCntx(); - return 0; -} - -VideoRenderNSOpenGL::~VideoRenderNSOpenGL() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); - - if(_fullScreen) - { - if(_fullScreenWindow) - { - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, 0, "%s:%d Attempting to release fullscreen window", __FUNCTION__, __LINE__); - [_fullScreenWindow releaseFullScreen]; - } - } - - // Signal event to exit thread, then delete it - ThreadWrapper* tmpPtr = _screenUpdateThread; - _screenUpdateThread = NULL; - - if (tmpPtr) - { - tmpPtr->SetNotAlive(); - _screenUpdateEvent->Set(); - _screenUpdateEvent->StopTimer(); - - if (tmpPtr->Stop()) - { - delete tmpPtr; - } - delete _screenUpdateEvent; - _screenUpdateEvent = NULL; - } - - if (_nsglContext != 0) - { - [_nsglContext makeCurrentContext]; - _nsglContext = nil; - } - - // Delete all channels - std::map::iterator it = _nsglChannels.begin(); - while (it!= _nsglChannels.end()) - { - delete it->second; - _nsglChannels.erase(it); - it = _nsglChannels.begin(); - } - _nsglChannels.clear(); - - // Clean the zOrder map - std::multimap::iterator zIt = _zOrderToChannel.begin(); - while(zIt != _zOrderToChannel.end()) - { - _zOrderToChannel.erase(zIt); - zIt = _zOrderToChannel.begin(); - } - _zOrderToChannel.clear(); - -} - -/* static */ -int VideoRenderNSOpenGL::GetOpenGLVersion(int& /*nsglMajor*/, int& /*nsglMinor*/) -{ - return -1; -} - -int VideoRenderNSOpenGL::Init() -{ - - LockAGLCntx(); - if (!_screenUpdateThread) - { - UnlockAGLCntx(); - return -1; - } - - _screenUpdateThread->Start(_threadID); - - // Start the event triggering the render process - unsigned int monitorFreq = 60; - _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); - - if (CreateMixingContext() == -1) - { - UnlockAGLCntx(); - return -1; - } - - UnlockAGLCntx(); - return 0; -} - -VideoChannelNSOpenGL* VideoRenderNSOpenGL::CreateNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - CriticalSectionScoped cs(_nsglContextCritSec); - - if (HasChannel(channel)) - { - return NULL; - } - - if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) - { - - } - - VideoChannelNSOpenGL* newAGLChannel = new VideoChannelNSOpenGL(_nsglContext, _id, this); - if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) - { - if (newAGLChannel) - { - delete newAGLChannel; - newAGLChannel = NULL; - } - - return NULL; - } - - _nsglChannels[channel] = newAGLChannel; - _zOrderToChannel.insert(std::pair(zOrder, channel)); - - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s successfully created NSGL channel number %d", __FUNCTION__, channel); - - return newAGLChannel; -} - -int VideoRenderNSOpenGL::DeleteAllNSGLChannels() -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - std::map::iterator it; - it = _nsglChannels.begin(); - - while (it != _nsglChannels.end()) - { - VideoChannelNSOpenGL* channel = it->second; - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Deleting channel %d", __FUNCTION__, channel); - delete channel; - it++; - } - _nsglChannels.clear(); - return 0; -} - -WebRtc_Word32 VideoRenderNSOpenGL::DeleteNSGLChannel(const WebRtc_UWord32 channel) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - std::map::iterator it; - it = _nsglChannels.find(channel); - if (it != _nsglChannels.end()) - { - delete it->second; - _nsglChannels.erase(it); - } - else - { - return -1; - } - - std::multimap::iterator zIt = _zOrderToChannel.begin(); - while( zIt != _zOrderToChannel.end()) - { - if (zIt->second == (int)channel) - { - _zOrderToChannel.erase(zIt); - break; - } - zIt++; - } - - return 0; -} - -WebRtc_Word32 VideoRenderNSOpenGL::GetChannelProperties(const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - bool channelFound = false; - - // Loop through all channels until we find a match. - // From that, get zorder. - // From that, get T, L, R, B - for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); - rIt != _zOrderToChannel.rend(); - rIt++) - { - if(streamId == rIt->second) - { - channelFound = true; - - zOrder = rIt->second; - - std::map::iterator rIt = _nsglChannels.find(streamId); - VideoChannelNSOpenGL* tempChannel = rIt->second; - - if(-1 == tempChannel->GetChannelProperties(left, top, right, bottom) ) - { - return -1; - } - break; - } - } - - if(false == channelFound) - { - - return -1; - } - - return 0; -} - -int VideoRenderNSOpenGL::StopThread() -{ - - ThreadWrapper* tmpPtr = _screenUpdateThread; - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Stopping thread ", __FUNCTION__, _screenUpdateThread); - _screenUpdateThread = NULL; - - if (tmpPtr) - { - tmpPtr->SetNotAlive(); - _screenUpdateEvent->Set(); - if (tmpPtr->Stop()) - { - delete tmpPtr; - } - } - - delete _screenUpdateEvent; - _screenUpdateEvent = NULL; - - return 0; -} - -bool VideoRenderNSOpenGL::IsFullScreen() -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - return _fullScreen; -} - -bool VideoRenderNSOpenGL::HasChannels() -{ - CriticalSectionScoped cs(_nsglContextCritSec); - - if (_nsglChannels.begin() != _nsglChannels.end()) - { - return true; - } - return false; -} - -bool VideoRenderNSOpenGL::HasChannel(int channel) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - std::map::iterator it = _nsglChannels.find(channel); - - if (it != _nsglChannels.end()) - { - return true; - } - return false; -} - -int VideoRenderNSOpenGL::GetChannels(std::list& channelList) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - std::map::iterator it = _nsglChannels.begin(); - - while (it != _nsglChannels.end()) - { - channelList.push_back(it->first); - it++; - } - - return 0; -} - -VideoChannelNSOpenGL* VideoRenderNSOpenGL::ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - std::map::iterator it = _nsglChannels.find(channel); - - if (it != _nsglChannels.end()) - { - VideoChannelNSOpenGL* aglChannel = it->second; - if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s failed to set stream settings: channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", - __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); - return NULL; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Configuring channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", - __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); - - std::multimap::iterator it = _zOrderToChannel.begin(); - while(it != _zOrderToChannel.end()) - { - if (it->second == channel) - { - if (it->first != zOrder) - { - _zOrderToChannel.erase(it); - _zOrderToChannel.insert(std::pair(zOrder, channel)); - } - break; - } - it++; - } - return aglChannel; - } - - return NULL; -} - -/* - * - * Rendering process - * - */ - -bool VideoRenderNSOpenGL::ScreenUpdateThreadProc(void* obj) -{ - return static_cast(obj)->ScreenUpdateProcess(); -} - -bool VideoRenderNSOpenGL::ScreenUpdateProcess() -{ - - _screenUpdateEvent->Wait(10); - LockAGLCntx(); - - if (!_screenUpdateThread) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s no screen update thread", __FUNCTION__); - UnlockAGLCntx(); - return false; - } - - [_nsglContext makeCurrentContext]; - - if (GetWindowRect(_windowRect) == -1) - { - UnlockAGLCntx(); - return true; - } - - if (_windowWidth != (_windowRect.right - _windowRect.left) - || _windowHeight != (_windowRect.bottom - _windowRect.top)) - { - _windowWidth = _windowRect.right - _windowRect.left; - _windowHeight = _windowRect.bottom - _windowRect.top; - glViewport(0, 0, _windowWidth, _windowHeight); - } - - // Check if there are any updated buffers - bool updated = false; - std::map::iterator it = _nsglChannels.begin(); - while (it != _nsglChannels.end()) - { - - VideoChannelNSOpenGL* aglChannel = it->second; - aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); - aglChannel->IsUpdated(updated); - if (updated) - { - break; - } - it++; - } - - if (updated) - { - - // At least on buffers is updated, we need to repaint the texture - if (RenderOffScreenBuffers() != -1) - { - UnlockAGLCntx(); - return true; - } - } - // } - UnlockAGLCntx(); - return true; -} - -/* - * - * Functions for creating mixing buffers and screen settings - * - */ - -int VideoRenderNSOpenGL::CreateMixingContext() -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - if(_fullScreen) - { - if(-1 == setRenderTargetFullScreen()) - { - return -1; - } - } - else - { - - if(-1 == setRenderTargetWindow()) - { - return -1; - } - } - - configureNSOpenGLEngine(); - - DisplayBuffers(); - - GLenum glErr = glGetError(); - if (glErr) - { - } - - return 0; -} - -/* - * - * Rendering functions - * - */ - -int VideoRenderNSOpenGL::RenderOffScreenBuffers() -{ - LockAGLCntx(); - - // Get the current window size, it might have changed since last render. - if (GetWindowRect(_windowRect) == -1) - { - UnlockAGLCntx(); - return -1; - } - - [_nsglContext makeCurrentContext]; - glClear(GL_COLOR_BUFFER_BIT); - - // Loop through all channels starting highest zOrder ending with lowest. - for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); - rIt != _zOrderToChannel.rend(); - rIt++) - { - int channelId = rIt->second; - std::map::iterator it = _nsglChannels.find(channelId); - - VideoChannelNSOpenGL* aglChannel = it->second; - - aglChannel->RenderOffScreenBuffer(); - } - - DisplayBuffers(); - - UnlockAGLCntx(); - return 0; -} - -/* - * - * Help functions - * - * All help functions assumes external protections - * - */ - -int VideoRenderNSOpenGL::DisplayBuffers() -{ - - LockAGLCntx(); - - glFinish(); - [_nsglContext flushBuffer]; - - WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s glFinish and [_nsglContext flushBuffer]", __FUNCTION__); - - UnlockAGLCntx(); - return 0; -} - -int VideoRenderNSOpenGL::GetWindowRect(Rect& rect) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - - if (_windowRef) - { - if(_fullScreen) - { - NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; - rect.bottom = 0; - rect.left = 0; - rect.right = mainDisplayRect.size.width; - rect.top = mainDisplayRect.size.height; - } - else - { - rect.top = [_windowRef frame].origin.y; - rect.left = [_windowRef frame].origin.x; - rect.bottom = [_windowRef frame].origin.y + [_windowRef frame].size.height; - rect.right = [_windowRef frame].origin.x + [_windowRef frame].size.width; - } - - return 0; - } - else - { - return -1; - } -} - -WebRtc_Word32 VideoRenderNSOpenGL::ChangeUniqueID(WebRtc_Word32 id) -{ - - CriticalSectionScoped cs(_nsglContextCritSec); - _id = id; - return 0; -} - -WebRtc_Word32 VideoRenderNSOpenGL::SetText(const WebRtc_UWord8 /*textId*/, - const WebRtc_UWord8* /*text*/, - const WebRtc_Word32 /*textLength*/, - const WebRtc_UWord32 /*textColorRef*/, - const WebRtc_UWord32 /*backgroundColorRef*/, - const float /*left*/, - const float /*top*/, - const float /*right*/, - const float /*bottom*/) -{ - - return 0; - -} - -void VideoRenderNSOpenGL::LockAGLCntx() -{ - _nsglContextCritSec.Enter(); -} -void VideoRenderNSOpenGL::UnlockAGLCntx() -{ - _nsglContextCritSec.Leave(); -} - -/* - - bool VideoRenderNSOpenGL::SetFullScreen(bool fullscreen) - { - NSRect mainDisplayRect, viewRect; - - // Create a screen-sized window on the display you want to take over - // Note, mainDisplayRect has a non-zero origin if the key window is on a secondary display - mainDisplayRect = [[NSScreen mainScreen] frame]; - fullScreenWindow = [[NSWindow alloc] initWithContentRect:mainDisplayRect styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered defer:YES]; - - // Set the window level to be above the menu bar - [fullScreenWindow setLevel:NSMainMenuWindowLevel+1]; - - // Perform any other window configuration you desire - [fullScreenWindow setOpaque:YES]; - [fullScreenWindow setHidesOnDeactivate:YES]; - - // Create a view with a double-buffered OpenGL context and attach it to the window - // By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined - viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height); - fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect shareContext:[openGLView openGLContext]]; - [fullScreenWindow setContentView:fullScreenView]; - - // Show the window - [fullScreenWindow makeKeyAndOrderFront:self]; - - // Set the scene with the full-screen viewport and viewing transformation - [scene setViewportRect:viewRect]; - - // Assign the view's MainController to self - [fullScreenView setMainController:self]; - - if (!isAnimating) { - // Mark the view as needing drawing to initalize its contents - [fullScreenView setNeedsDisplay:YES]; - } - else { - // Start playing the animation - [fullScreenView startAnimation]; - } - - } - - - - */ - - -} //namespace webrtc - -#endif // COCOA_RENDERING diff --git a/modules/video_render/main/source/mac/video_render_nsopengl.h b/modules/video_render/main/source/mac/video_render_nsopengl.h deleted file mode 100644 index c0e836d89..000000000 --- a/modules/video_render/main/source/mac/video_render_nsopengl.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#if defined(COCOA_RENDERING) - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "video_render_defines.h" - -#import "cocoa_render_view.h" -#import "cocoa_full_screen_window.h" - -class Trace; - -namespace webrtc { -class EventWrapper; -class ThreadWrapper; -class VideoRenderNSOpenGL; -class CriticalSectionWrapper; - -class VideoChannelNSOpenGL : public VideoRenderCallback -{ - -public: - - VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner); - virtual ~VideoChannelNSOpenGL(); - - // A new frame is delivered - virtual int DeliverFrame(unsigned char* buffer, int bufferSize, unsigned int timeStame90kHz); - - // Called when the incomming frame size and/or number of streams in mix changes - virtual int FrameSizeChange(int width, int height, int numberOfStreams); - - virtual int UpdateSize(int width, int height); - - // Setup - int SetStreamSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); - int SetStreamCropSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); - - // Called when it's time to render the last frame for the channel - int RenderOffScreenBuffer(); - - // Returns true if a new buffer has been delivered to the texture - int IsUpdated(bool& isUpdated); - virtual int UpdateStretchSize(int stretchHeight, int stretchWidth); - - // ********** new module functions ************ // - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, VideoFrame& videoFrame); - - // ********** new module helper functions ***** // - int ChangeContext(NSOpenGLContext *nsglContext); - WebRtc_Word32 GetChannelProperties(float& left, - float& top, - float& right, - float& bottom); - -private: - - NSOpenGLContext* _nsglContext; - int _id; - VideoRenderNSOpenGL* _owner; - WebRtc_Word32 _width; - WebRtc_Word32 _height; - float _startWidth; - float _startHeight; - float _stopWidth; - float _stopHeight; - int _stretchedWidth; - int _stretchedHeight; - int _oldStretchedHeight; - int _oldStretchedWidth; - int _xOldWidth; - int _yOldHeight; - unsigned char* _buffer; - int _bufferSize; - int _incommingBufferSize; - bool _bufferIsUpdated; - int _numberOfStreams; - GLenum _pixelFormat; - GLenum _pixelDataType; - unsigned int _texture; - bool _bVideoSizeStartedChanging; -}; - -class VideoRenderNSOpenGL -{ - -public: // methods - VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId); - ~VideoRenderNSOpenGL(); - - static int GetOpenGLVersion(int& nsglMajor, int& nsglMinor); - - // Allocates textures - int Init(); - VideoChannelNSOpenGL* CreateNSGLChannel(int streamID, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); - VideoChannelNSOpenGL* ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); - int DeleteNSGLChannel(int channel); - int DeleteAllNSGLChannels(); - int StopThread(); - bool IsFullScreen(); - bool HasChannels(); - bool HasChannel(int channel); - int GetChannels(std::list& channelList); - void LockAGLCntx(); - void UnlockAGLCntx(); - - // ********** new module functions ************ // - int ChangeWindow(CocoaRenderView* newWindowRef); - WebRtc_Word32 ChangeUniqueID(WebRtc_Word32 id); - WebRtc_Word32 StartRender(); - WebRtc_Word32 StopRender(); - WebRtc_Word32 DeleteNSGLChannel(const WebRtc_UWord32 streamID); - WebRtc_Word32 GetChannelProperties(const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom); - - WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom); - - // ********** new module helper functions ***** // - int configureNSOpenGLEngine(); - int configureNSOpenGLView(); - int setRenderTargetWindow(); - int setRenderTargetFullScreen(); - -protected: // methods - static bool ScreenUpdateThreadProc(void* obj); - bool ScreenUpdateProcess(); - int GetWindowRect(Rect& rect); - -private: // methods - - int CreateMixingContext(); - int RenderOffScreenBuffers(); - int DisplayBuffers(); - -private: // variables - - - CocoaRenderView* _windowRef; - bool _fullScreen; - int _id; - CriticalSectionWrapper& _nsglContextCritSec; - ThreadWrapper* _screenUpdateThread; - EventWrapper* _screenUpdateEvent; - NSOpenGLContext* _nsglContext; - NSOpenGLContext* _nsglFullScreenContext; - CocoaFullScreenWindow* _fullScreenWindow; - Rect _windowRect; // The size of the window - int _windowWidth; - int _windowHeight; - std::map _nsglChannels; - std::multimap _zOrderToChannel; - unsigned int _threadID; - bool _renderingIsPaused; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ -#endif // COCOA_RENDERING - diff --git a/modules/video_render/main/source/video_render.gyp b/modules/video_render/main/source/video_render.gyp deleted file mode 100644 index 89287d755..000000000 --- a/modules/video_render/main/source/video_render.gyp +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2009 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'includes': [ - '../../../../common_settings.gypi', # Common settings - ], - 'targets': [ - { - 'target_name': 'video_render_module', - 'type': '<(library)', - 'dependencies': [ - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../utility/source/utility.gyp:webrtc_utility', - ], - 'include_dirs': [ - '.', - '../interface', - '../../../interface', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../interface', - '../../../interface', - ], - }, - 'sources': [ - # interfaces - '../interface/video_render.h', - '../interface/video_render_defines.h', - - # headers - 'incoming_video_stream.h', - 'video_render_frames.h', - 'video_render_impl.h', - 'i_video_render.h', - # Linux - 'linux/video_render_linux_impl.h', - 'linux/video_x11_channel.h', - 'linux/video_x11_render.h', - # Mac - 'mac/cocoa_full_screen_window.h', - 'mac/cocoa_render_view.h', - 'mac/video_render_agl.h', - 'mac/video_render_mac_carbon_impl.h', - 'mac/video_render_mac_cocoa_impl.h', - 'mac/video_render_nsopengl.h', - # Windows - 'windows/i_video_render_win.h', - 'windows/video_render_direct3d9.h', - 'windows/video_render_directdraw.h', - 'windows/video_render_windows_impl.h', - # External - 'external/video_render_external_impl.h', - - # PLATFORM INDEPENDENT SOURCE FILES - 'incoming_video_stream.cc', - 'video_render_frames.cc', - 'video_render_impl.cc', - # PLATFORM SPECIFIC SOURCE FILES - Will be filtered below - # Linux - 'linux/video_render_linux_impl.cc', - 'linux/video_x11_channel.cc', - 'linux/video_x11_render.cc', - # Mac - 'mac/video_render_nsopengl.cc', - 'mac/video_render_mac_cocoa_impl.cc', - 'mac/video_render_agl.cc', - 'mac/video_render_mac_carbon_impl.cc', - 'mac/cocoa_render_view.mm', - 'mac/cocoa_full_screen_window.mm', - # Windows - 'windows/video_render_direct3d9.cc', - 'windows/video_render_directdraw.cc', - 'windows/video_render_windows_impl.cc', - # External - 'external/video_render_external_impl.cc', - ], - 'conditions': [ - # DEFINE PLATFORM SPECIFIC SOURCE FILES - ['OS!="linux" or build_with_chromium==1', { - 'sources!': [ - 'linux/video_render_linux_impl.h', - 'linux/video_x11_channel.h', - 'linux/video_x11_render.h', - 'linux/video_render_linux_impl.cc', - 'linux/video_x11_channel.cc', - 'linux/video_x11_render.cc', - ], - }], - ['OS!="mac" or build_with_chromium==1', { - 'sources!': [ - 'mac/cocoa_full_screen_window.h', - 'mac/cocoa_render_view.h', - 'mac/video_render_agl.h', - 'mac/video_render_mac_carbon_impl.h', - 'mac/video_render_mac_cocoa_impl.h', - 'mac/video_render_nsopengl.h', - 'mac/video_render_nsopengl.cc', - 'mac/video_render_mac_cocoa_impl.cc', - 'mac/video_render_agl.cc', - 'mac/video_render_mac_carbon_impl.cc', - 'mac/cocoa_render_view.mm', - 'mac/cocoa_full_screen_window.mm', - ], - }], - ['OS!="win" or build_with_chromium==1', { - 'sources!': [ - 'windows/i_video_render_win.h', - 'windows/video_render_direct3d9.h', - 'windows/video_render_directdraw.h', - 'windows/video_render_windows_impl.h', - 'windows/video_render_direct3d9.cc', - 'windows/video_render_directdraw.cc', - 'windows/video_render_windows_impl.cc', - ], - }], - # DEFINE PLATFORM SPECIFIC INCLUDE AND CFLAGS - ['OS=="mac"', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': '-x objective-c++' - }, - }], - ] # conditions - }, # video_render_module - { - 'target_name': 'video_render_module_test', - 'type': 'executable', - 'dependencies': [ - 'video_render_module', - '../../../utility/source/utility.gyp:webrtc_utility', - '../../../../system_wrappers/source/system_wrappers.gyp:system_wrappers', - '../../../../common_video/vplib/main/source/vplib.gyp:webrtc_vplib', - ], - 'include_dirs': [ - ], - 'sources': [ - # sources - '../test/testAPI/testAPI.cpp', - ], # source - 'conditions': [ - # DEFINE PLATFORM SPECIFIC INCLUDE AND CFLAGS - ['OS=="mac" or OS=="linux"', { - 'cflags': [ - '-Wno-write-strings', - ], - 'ldflags': [ - '-lpthread -lm', - ], - }], - ['OS=="linux"', { - 'libraries': [ - '-lrt', - '-lXext', - '-lX11', - ], - }], - ['OS=="mac"', { - 'xcode_settings': { - 'OTHER_CPLUSPLUSFLAGS': '-x objective-c++', - 'OTHER_LDFLAGS': [ - '-framework Foundation -framework AppKit -framework Cocoa -framework OpenGL', - ], - }, - }], - ] # conditions - }, # video_render_module_test - ], # targets -} - -# Local Variables: -# tab-width:2 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/modules/video_render/main/source/video_render_frames.cc b/modules/video_render/main/source/video_render_frames.cc deleted file mode 100644 index 28078e304..000000000 --- a/modules/video_render/main/source/video_render_frames.cc +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_frames.h" -#include "module_common_types.h" -#include "tick_util.h" -#include "trace.h" -#include - -namespace webrtc { - -VideoRenderFrames::VideoRenderFrames() : - _incomingFrames(), _renderDelayMs(10) -{ -} - -VideoRenderFrames::~VideoRenderFrames() -{ - ReleaseAllFrames(); -} - -WebRtc_Word32 VideoRenderFrames::AddFrame(VideoFrame* ptrNewFrame) -{ - const WebRtc_Word64 timeNow = TickTime::MillisecondTimestamp(); - - if (ptrNewFrame->RenderTimeMs() + KOldRenderTimestampMS < timeNow) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, - "%s: too old frame.", __FUNCTION__); - return -1; - } - if (ptrNewFrame->RenderTimeMs() > timeNow + KFutureRenderTimestampMS) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, - "%s: frame too long into the future.", __FUNCTION__); - return -1; - } - - // Get an empty frame - VideoFrame* ptrFrameToAdd = NULL; - if (!_emptyFrames.Empty()) - { - ListItem* item = _emptyFrames.First(); - if (item) - { - ptrFrameToAdd = static_cast (item->GetItem()); - _emptyFrames.Erase(item); - } - } - if (!ptrFrameToAdd) - { - - if (_emptyFrames.GetSize() + _incomingFrames.GetSize() - > KMaxNumberOfFrames) - { - // Already allocated toom many frames... - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, - -1, "%s: too many frames, limit: %d", __FUNCTION__, - KMaxNumberOfFrames); - return -1; - } - - // Allocate new memory - WEBRTC_TRACE(kTraceMemory, kTraceVideoRenderer, -1, - "%s: allocating buffer %d", __FUNCTION__, - _emptyFrames.GetSize() + _incomingFrames.GetSize()); - - ptrFrameToAdd = new VideoFrame(); - if (!ptrFrameToAdd) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "%s: could not create new frame for", __FUNCTION__); - return -1; - } - } - - ptrFrameToAdd->VerifyAndAllocate(ptrNewFrame->Length()); - ptrFrameToAdd->SwapFrame(const_cast (*ptrNewFrame)); //remove const ness. Copying will be costly. - _incomingFrames.PushBack(ptrFrameToAdd); - - return _incomingFrames.GetSize(); -} - -VideoFrame* -VideoRenderFrames::FrameToRender() -{ - VideoFrame* ptrRenderFrame = NULL; - while (!_incomingFrames.Empty()) - { - ListItem* item = _incomingFrames.First(); - if (item) - { - VideoFrame* ptrOldestFrameInList = - static_cast (item->GetItem()); - if (ptrOldestFrameInList->RenderTimeMs() - <= TickTime::MillisecondTimestamp() + _renderDelayMs) - { - // This is the oldest one so far and it's ok to render - if (ptrRenderFrame) - { - // This one is older than the newly found frame, remove this one. - ptrRenderFrame->SetWidth(0); - ptrRenderFrame->SetHeight(0); - ptrRenderFrame->SetLength(0); - ptrRenderFrame->SetRenderTime(0); - ptrRenderFrame->SetTimeStamp(0); - _emptyFrames.PushFront(ptrRenderFrame); - } - ptrRenderFrame = ptrOldestFrameInList; - _incomingFrames.Erase(item); - } - else - { - // We can't release this one yet, we're done here. - break; - } - } - else - { - assert(false); - } - } - return ptrRenderFrame; -} - -WebRtc_Word32 VideoRenderFrames::ReturnFrame(VideoFrame* ptrOldFrame) -{ - ptrOldFrame->SetWidth(0); - ptrOldFrame->SetHeight(0); - ptrOldFrame->SetRenderTime(0); - ptrOldFrame->SetLength(0); - _emptyFrames.PushBack(ptrOldFrame); - - return 0; -} - -WebRtc_Word32 VideoRenderFrames::ReleaseAllFrames() -{ - while (!_incomingFrames.Empty()) - { - ListItem* item = _incomingFrames.First(); - if (item) - { - VideoFrame* ptrFrame = - static_cast (item->GetItem()); - assert(ptrFrame != NULL); - ptrFrame->Free(); - delete ptrFrame; - } - _incomingFrames.Erase(item); - } - while (!_emptyFrames.Empty()) - { - ListItem* item = _emptyFrames.First(); - if (item) - { - VideoFrame* ptrFrame = - static_cast (item->GetItem()); - assert(ptrFrame != NULL); - ptrFrame->Free(); - delete ptrFrame; - } - _emptyFrames.Erase(item); - } - return 0; -} - -WebRtc_Word32 KEventMaxWaitTimeMs = 200; - -WebRtc_UWord32 VideoRenderFrames::TimeToNextFrameRelease() -{ - WebRtc_Word64 timeToRelease = 0; - ListItem* item = _incomingFrames.First(); - if (item) - { - VideoFrame* oldestFrame = - static_cast (item->GetItem()); - timeToRelease = oldestFrame->RenderTimeMs() - _renderDelayMs - - TickTime::MillisecondTimestamp(); - if (timeToRelease < 0) - { - timeToRelease = 0; - } - } - else - { - timeToRelease = KEventMaxWaitTimeMs; - } - - return (WebRtc_UWord32) timeToRelease; -} - -// -WebRtc_Word32 VideoRenderFrames::SetRenderDelay( - const WebRtc_UWord32 renderDelay) -{ - _renderDelayMs = renderDelay; - return 0; -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/video_render_frames.h b/modules/video_render/main/source/video_render_frames.h deleted file mode 100644 index 84a2a1c30..000000000 --- a/modules/video_render/main/source/video_render_frames.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_FRAMES_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_FRAMES_H_ - -#include "list_wrapper.h" -#include "video_render.h" - -namespace webrtc { - -// Class definitions -class VideoRenderFrames -{ -public: - VideoRenderFrames(); - ~VideoRenderFrames(); - - /* - * Add a frame to the render queue - */ - WebRtc_Word32 AddFrame(VideoFrame* ptrNewFrame); - - /* - * Get a frame for rendering, if it's time to render. - */ - VideoFrame* FrameToRender(); - - /* - * Return an old frame - */ - WebRtc_Word32 ReturnFrame(VideoFrame* ptrOldFrame); - - /* - * Releases all frames - */ - WebRtc_Word32 ReleaseAllFrames(); - - /* - * Returns the number of ms to next frame to render - */ - WebRtc_UWord32 TimeToNextFrameRelease(); - - /* - * Sets estimates delay in renderer - */ - WebRtc_Word32 SetRenderDelay(const WebRtc_UWord32 renderDelay); - -private: - enum - { - KMaxNumberOfFrames = 300 - }; // 10 seconds for 30 fps. - enum - { - KOldRenderTimestampMS = 500 - }; //Don't render frames with timestamp older than 500ms from now. - enum - { - KFutureRenderTimestampMS = 10000 - }; //Don't render frames with timestamp more than 10s into the future. - - ListWrapper _incomingFrames; // Sorted oldest video frame first - ListWrapper _emptyFrames; // Empty frames - - WebRtc_UWord32 _renderDelayMs; // Set render delay -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_FRAMES_H_ diff --git a/modules/video_render/main/source/video_render_impl.cc b/modules/video_render/main/source/video_render_impl.cc deleted file mode 100644 index c752009f5..000000000 --- a/modules/video_render/main/source/video_render_impl.cc +++ /dev/null @@ -1,1088 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_impl.h" -#include "engine_configurations.h" -#include "critical_section_wrapper.h" -#include "video_render_defines.h" -#include "trace.h" -#include "incoming_video_stream.h" -#include "i_video_render.h" - -#include - -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - -#if defined (_WIN32) -#include "windows/video_render_windows_impl.h" -#define STANDARD_RENDERING kRenderWindows - -#elif defined(MAC_IPHONE) // MAC_IPHONE should go before WEBRTC_MAC_INTEL because WEBRTC_MAC_INTEL gets defined if MAC_IPHONE is defined -#if defined(IPHONE_GLES_RENDERING) -#define STANDARD_RENDERING kRenderiPhone -#include "iPhone/video_render_iphone_impl.h" -#endif - -#elif defined(WEBRTC_MAC) || defined(WEBRTC_MAC_INTEL) -#if defined(COCOA_RENDERING) -#define STANDARD_RENDERING kRenderCocoa -#include "mac/video_render_mac_cocoa_impl.h" -#elif defined(CARBON_RENDERING) -#define STANDARD_RENDERING kRenderCarbon -#include "mac/video_render_mac_carbon_impl.h" -#endif - -#elif defined(ANDROID) -#include "Android/video_render_android_impl.h" -#include "Android/video_render_android_surface_view.h" -#include "Android/video_render_android_native_opengl2.h" -#define STANDARD_RENDERING kRenderAndroid - -#elif defined(WEBRTC_LINUX) -#include "linux/video_render_linux_impl.h" -#define STANDARD_RENDERING kRenderX11 - -#else -//Other platforms -#endif - -#endif // WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - -// For external rendering -#include "external/video_render_external_impl.h" -#ifndef STANDARD_RENDERING -#define STANDARD_RENDERING kRenderExternal -#endif // STANDARD_RENDERING - -namespace webrtc { - -VideoRender* -VideoRender::CreateVideoRender(const WebRtc_Word32 id, - void* window, - const bool fullscreen, - const VideoRenderType videoRenderType/*=kRenderDefault*/) -{ - WEBRTC_TRACE( - kTraceModuleCall, - kTraceVideoRenderer, - id, - "CreateVideoRender(videoRenderType: %d, window: %x, fullscreen: %d)", - videoRenderType, window, fullscreen); - - VideoRenderType resultVideoRenderType = videoRenderType; - if (videoRenderType == kRenderDefault) - { - resultVideoRenderType = STANDARD_RENDERING; - } - return new ModuleVideoRenderImpl(id, resultVideoRenderType, window, - fullscreen); -} - -void VideoRender::DestroyVideoRender( - VideoRender* module) -{ - if (module) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - static_cast (module)->Id(), - "DestroyVideoRender"); - delete module; - } -} - -WebRtc_Word32 VideoRender::SetAndroidObjects(void *javaVM) -{ -#ifdef ANDROID - return VideoRenderAndroid::SetAndroidEnvVariables(javaVM); -#else - return -1; -#endif -} - -ModuleVideoRenderImpl::ModuleVideoRenderImpl( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : - _id(id), _moduleCrit(*CriticalSectionWrapper::CreateCriticalSection()), - _ptrWindow(window), _renderType(videoRenderType), - _fullScreen(fullscreen), _ptrRenderer(NULL), - _streamRenderMap(*(new MapWrapper())) -{ - - // Create platform specific renderer - switch (videoRenderType) - { -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - -#if defined(_WIN32) - case kRenderWindows: - { - VideoRenderWindowsImpl* ptrRenderer; - ptrRenderer = new VideoRenderWindowsImpl(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; - -#elif defined(MAC_IPHONE) - case kRenderiPhone: - { - VideoRenderIPhoneImpl* ptrRenderer = new VideoRenderIPhoneImpl(_id, videoRenderType, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; - -#elif defined(WEBRTC_MAC) || defined(WEBRTC_MAC_INTEL) - -#if defined(COCOA_RENDERING) - case kRenderCocoa: - { - VideoRenderMacCocoaImpl* ptrRenderer = new VideoRenderMacCocoaImpl(_id, videoRenderType, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - - break; -#elif defined(CARBON_RENDERING) - case kRenderCarbon: - { - VideoRenderMacCarbonImpl* ptrRenderer = new VideoRenderMacCarbonImpl(_id, videoRenderType, window, _fullScreen); - if(ptrRenderer) - { - _ptrRenderer = reinterpret_cast(ptrRenderer); - } - } - break; -#endif - -#elif defined(ANDROID) - case kRenderAndroid: - { - if(AndroidNativeOpenGl2Renderer::UseOpenGL2(window)) - { - AndroidNativeOpenGl2Renderer* ptrRenderer = NULL; - ptrRenderer = new AndroidNativeOpenGl2Renderer(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - else - { - AndroidSurfaceViewRenderer* ptrRenderer = NULL; - ptrRenderer = new AndroidSurfaceViewRenderer(_id, videoRenderType, window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - - } - break; -#elif defined(WEBRTC_LINUX) - case kRenderX11: - { - VideoRenderLinuxImpl* ptrRenderer = NULL; - ptrRenderer = new VideoRenderLinuxImpl(_id, videoRenderType, window, _fullScreen); - if ( ptrRenderer ) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - break; - -#else - // Other platforms -#endif - -#endif // WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - case kRenderExternal: - { - VideoRenderExternalImpl* ptrRenderer(NULL); - ptrRenderer = new VideoRenderExternalImpl(_id, videoRenderType, - window, _fullScreen); - if (ptrRenderer) - { - _ptrRenderer = reinterpret_cast (ptrRenderer); - } - } - break; - default: - // Error... - break; - } - if (_ptrRenderer) - { - if (_ptrRenderer->Init() == -1) - { - } - } -} - -ModuleVideoRenderImpl::~ModuleVideoRenderImpl() -{ - delete &_moduleCrit; - - while (_streamRenderMap.Size() > 0) - { - MapItem* item = _streamRenderMap.First(); - IncomingVideoStream* ptrIncomingStream = - static_cast (item->GetItem()); - assert(ptrIncomingStream != NULL); - delete ptrIncomingStream; - _streamRenderMap.Erase(item); - } - delete &_streamRenderMap; - - // Delete platform specific renderer - if (_ptrRenderer) - { - VideoRenderType videoRenderType = _ptrRenderer->RenderType(); - switch (videoRenderType) - { - case kRenderExternal: - { - VideoRenderExternalImpl - * ptrRenderer = - reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - -#if defined(_WIN32) - case kRenderWindows: - { - VideoRenderWindowsImpl* ptrRenderer = reinterpret_cast(_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#elif defined(WEBRTC_MAC) || defined(WEBRTC_MAC_INTEL) - -#if defined(COCOA_RENDERING) - case kRenderCocoa: - { - VideoRenderMacCocoaImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#elif defined(CARBON_RENDERING) - case kRenderCarbon: - { - VideoRenderMacCarbonImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#endif - -#elif defined(MAC_IPHONE) - case kRenderiPhone: - break; - -#elif defined(ANDROID) - case kRenderAndroid: - { - VideoRenderAndroid* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; - -#elif defined(WEBRTC_LINUX) - case kRenderX11: - { - VideoRenderLinuxImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); - _ptrRenderer = NULL; - delete ptrRenderer; - } - break; -#else - //other platforms -#endif - -#endif // WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - - default: - // Error... - break; - } - } -} - -WebRtc_Word32 ModuleVideoRenderImpl::Version( - WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - if (version == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "Version pointer is NULL"); - return -1; - } - WebRtc_Word8 ourVersion[256] = "VideoRender 1.1.0"; - WebRtc_Word32 ourLength = (WebRtc_Word32) strlen(ourVersion); - if ((WebRtc_Word32) remainingBufferInBytes < ourLength + 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, - "Version buffer not long enough"); - return -1; - } - memcpy(version, ourVersion, ourLength); - version[ourLength] = 0; // null terminaion - position += ourLength; - return 0; -} - -WebRtc_Word32 ModuleVideoRenderImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "ChangeUniqueId(new id:%d)", id); - - CriticalSectionScoped cs(_moduleCrit); - - _id = id; - - if (_ptrRenderer) - { - _ptrRenderer->ChangeUniqueId(_id); - } - - return 0; -} - -WebRtc_Word32 ModuleVideoRenderImpl::TimeUntilNextProcess() -{ - // Not used - return 50; -} -WebRtc_Word32 ModuleVideoRenderImpl::Process() -{ - // Not used - return 0; -} - -void* -ModuleVideoRenderImpl::Window() -{ - CriticalSectionScoped cs(_moduleCrit); - return _ptrWindow; -} - -WebRtc_Word32 ModuleVideoRenderImpl::ChangeWindow(void* window) -{ - - CriticalSectionScoped cs(_moduleCrit); - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - -#ifndef WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - -#if defined(MAC_IPHONE) // MAC_IPHONE must go before WEBRTC_MAC or WEBRTC_MAC_INTEL - _ptrRenderer = NULL; - delete _ptrRenderer; - - VideoRenderIPhoneImpl* ptrRenderer; - ptrRenderer = new VideoRenderIPhoneImpl(_id, kRenderiPhone, window, _fullScreen); - if (!ptrRenderer) - { - return -1; - } - _ptrRenderer = reinterpret_cast(ptrRenderer); - return _ptrRenderer->ChangeWindow(window); - -#elif defined(WEBRTC_MAC) | defined(WEBRTC_MAC_INTEL) - - _ptrRenderer = NULL; - delete _ptrRenderer; - -#if defined(COCOA_RENDERING) - VideoRenderMacCocoaImpl* ptrRenderer; - ptrRenderer = new VideoRenderMacCocoaImpl(_id, kRenderCocoa, window, _fullScreen); -#elif defined(CARBON_RENDERING) - VideoRenderMacCarbonImpl* ptrRenderer; - ptrRenderer = new VideoRenderMacCarbonImpl(_id, kRenderCarbon, window, _fullScreen); -#endif - if (!ptrRenderer) - { - return -1; - } - _ptrRenderer = reinterpret_cast(ptrRenderer); - return _ptrRenderer->ChangeWindow(window); - -#else - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - return _ptrRenderer->ChangeWindow(window); - -#endif - -#else // WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER - return -1; -#endif -} - -WebRtc_Word32 ModuleVideoRenderImpl::Id() -{ - CriticalSectionScoped cs(_moduleCrit); - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - return _id; -} - -WebRtc_UWord32 ModuleVideoRenderImpl::GetIncomingFrameRate( - const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, stream: %u", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - MapItem* mapItem = _streamRenderMap.Find(streamId); - if (mapItem == NULL) - { - // This stream doesn't exist - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return 0; - } - IncomingVideoStream* incomingStream = - static_cast (mapItem->GetItem()); - if (incomingStream == NULL) - { - // This should never happen - assert(false); - _streamRenderMap.Erase(mapItem); - return 0; - } - return incomingStream->IncomingRate(); -} - -VideoRenderCallback* -ModuleVideoRenderImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, stream: %u", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return NULL; - } - - if (_streamRenderMap.Find(streamId) != NULL) - { - // The stream already exists... - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream already exists", __FUNCTION__); - return NULL; - } - - // Create platform independant code - IncomingVideoStream* ptrIncomingStream = new IncomingVideoStream(_id, - streamId); - if (ptrIncomingStream == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Can't create incoming stream", __FUNCTION__); - return NULL; - } - - VideoRenderCallback* ptrRenderCallback = - _ptrRenderer->AddIncomingRenderStream(streamId, zOrder, left, top, - right, bottom); - if (ptrRenderCallback == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Can't create incoming stream in renderer", - __FUNCTION__); - return NULL; - } - - if (ptrIncomingStream->SetRenderCallback(ptrRenderCallback) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: Can't set render callback", __FUNCTION__); - delete ptrIncomingStream; - _ptrRenderer->DeleteIncomingRenderStream(streamId); - return NULL; - } - - VideoRenderCallback* moduleCallback = - ptrIncomingStream->ModuleCallback(); - - // Store the stream - _streamRenderMap.Insert(streamId, ptrIncomingStream); - - return moduleCallback; -} - -WebRtc_Word32 ModuleVideoRenderImpl::DeleteIncomingRenderStream( - const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, stream: %u", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - MapItem* mapItem = _streamRenderMap.Find(streamId); - if (!mapItem) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return -1; - } - - IncomingVideoStream* ptrIncomingStream = - static_cast (mapItem->GetItem()); - delete ptrIncomingStream; - ptrIncomingStream = NULL; - _ptrRenderer->DeleteIncomingRenderStream(streamId); - _streamRenderMap.Erase(mapItem); - - return 0; -} - -WebRtc_Word32 ModuleVideoRenderImpl::AddExternalRenderCallback( - const WebRtc_UWord32 streamId, - VideoRenderCallback* renderObject) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, stream: %u, callback: %x", __FUNCTION__, streamId, - renderObject); - CriticalSectionScoped cs(_moduleCrit); - - MapItem* mapItem = _streamRenderMap.Find(streamId); - if (!mapItem) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return -1; - } - - IncomingVideoStream* ptrIncomingStream = - static_cast (mapItem->GetItem()); - if (!ptrIncomingStream) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: could not get stream", __FUNCTION__); - } - return ptrIncomingStream->SetExternalCallback(renderObject); -} - -WebRtc_Word32 ModuleVideoRenderImpl::GetIncomingRenderStreamProperties( - const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, stream: %u", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - return _ptrRenderer->GetIncomingRenderStreamProperties(streamId, zOrder, - left, top, right, - bottom); -} - -WebRtc_UWord32 ModuleVideoRenderImpl::GetNumIncomingRenderStreams() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - return (WebRtc_UWord32) _streamRenderMap.Size(); -} - -bool ModuleVideoRenderImpl::HasIncomingRenderStream( - const WebRtc_UWord32 streamId) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - bool hasStream = false; - if (_streamRenderMap.Find(streamId) != NULL) - { - hasStream = true; - } - return hasStream; -} - -WebRtc_Word32 ModuleVideoRenderImpl::RegisterRawFrameCallback( - const WebRtc_UWord32 streamId, - VideoRenderCallback* callbackObj) -{ - return -1; -} - -WebRtc_Word32 ModuleVideoRenderImpl::StartRender(const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s(%u)", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - // Start the stream - MapItem* item = _streamRenderMap.Find(streamId); - if (item == NULL) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could find render stream %d", __FUNCTION__, - streamId); - return -1; - } - - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream->Start() == -1) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could not start stream %d", __FUNCTION__, - incomingStream->StreamId()); - return -1; - } - - // Start the HW renderer - if (_ptrRenderer->StartRender() == -1) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could not start renderer", __FUNCTION__); - return -1; - } - return 0; -} - -WebRtc_Word32 ModuleVideoRenderImpl::StopRender(const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s(%u)", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s(%d): No renderer", __FUNCTION__, streamId); - return -1; - } - - // Stop the incoming stream - MapItem* item = _streamRenderMap.Find(streamId); - if (item == NULL) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could find render stream %d", __FUNCTION__, - streamId); - return -1; - } - - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream->Stop() == -1) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could not start stream %d", __FUNCTION__, - incomingStream->StreamId()); - return -1; - } - - return 0; -} - -WebRtc_Word32 ModuleVideoRenderImpl::ResetRender() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - WebRtc_Word32 error = 0; - - // Loop through all incoming streams and stop them - MapItem* item = _streamRenderMap.First(); - while (item) - { - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream->Reset() == -1) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s: Could not reset stream %d", __FUNCTION__, - incomingStream->StreamId()); - error = -1; - } - item = _streamRenderMap.Next(item); - } - return error; -} - -RawVideoType ModuleVideoRenderImpl::PreferredVideoType() const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (_ptrRenderer == NULL) - { - return kVideoI420; - } - - return _ptrRenderer->PerferedVideoType(); -} - -bool ModuleVideoRenderImpl::IsFullScreen() -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->FullScreen(); -} - -WebRtc_Word32 ModuleVideoRenderImpl::GetScreenResolution( - WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->GetScreenResolution(screenWidth, screenHeight); -} - -WebRtc_UWord32 ModuleVideoRenderImpl::RenderFrameRate( - const WebRtc_UWord32 streamId) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, streamId: %u", __FUNCTION__, streamId); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->RenderFrameRate(streamId); -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetStreamCropping( - const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, l: %1.1f, t: %1.1f, r: %1.1f, b: %1.1f", __FUNCTION__, - left, top, right, bottom); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->SetStreamCropping(streamId, left, top, right, bottom); -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetTransparentBackground(const bool enable) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, enable: %d", __FUNCTION__, enable); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->SetTransparentBackground(enable); -} - -WebRtc_Word32 ModuleVideoRenderImpl::FullScreenRender(void* window, - const bool enable) -{ - return -1; -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetText( - const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - return _ptrRenderer->SetText(textId, text, textLength, textColorRef, - backgroundColorRef, left, top, right, bottom); -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - return _ptrRenderer->SetBitmap(bitMap, pictureId, colorKey, left, top, - right, bottom); -} - -WebRtc_Word32 ModuleVideoRenderImpl::GetLastRenderedFrame( - const WebRtc_UWord32 streamId, - VideoFrame &frame) const -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - MapItem *item = _streamRenderMap.Find(streamId); - if (item == NULL) - { - // This stream doesn't exist - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return 0; - } - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream == NULL) - { - // This should never happen - assert(false); - _streamRenderMap.Erase(item); - return 0; - } - return incomingStream->GetLastRenderedFrame(frame); -} - -WebRtc_Word32 ModuleVideoRenderImpl::ConfigureRenderer( - const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s, l: %1.1f, t: %1.1f, r: %1.1f, b: %1.1f", __FUNCTION__, - left, top, right, bottom); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return false; - } - return _ptrRenderer->ConfigureRenderer(streamId, zOrder, left, top, right, - bottom); -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetStartImage( - const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - MapItem *item = _streamRenderMap.Find(streamId); - if (item == NULL) - { - // This stream doesn't exist - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return -1; - } - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream == NULL) - { - // This should never happen - assert(false); - _streamRenderMap.Erase(item); - return 0; - } - return incomingStream->SetStartImage(videoFrame); - -} - -WebRtc_Word32 ModuleVideoRenderImpl::SetTimeoutImage( - const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame, - const WebRtc_UWord32 timeout) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - MapItem *item = _streamRenderMap.Find(streamId); - if (item == NULL) - { - // This stream doesn't exist - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return -1; - } - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream == NULL) - { - // This should never happen - assert(false); - _streamRenderMap.Erase(item); - return 0; - } - return incomingStream->SetTimeoutImage(videoFrame, timeout); -} - -WebRtc_Word32 ModuleVideoRenderImpl::MirrorRenderStream(const int renderId, - const bool enable, - const bool mirrorXAxis, - const bool mirrorYAxis) -{ - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, - "%s", __FUNCTION__); - CriticalSectionScoped cs(_moduleCrit); - - if (!_ptrRenderer) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: No renderer", __FUNCTION__); - return -1; - } - - MapItem *item = _streamRenderMap.Find(renderId); - if (item == NULL) - { - // This stream doesn't exist - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, - "%s: stream doesn't exist", __FUNCTION__); - return 0; - } - IncomingVideoStream* incomingStream = - static_cast (item->GetItem()); - if (incomingStream == NULL) - { - // This should never happen - assert(false); - _streamRenderMap.Erase(item); - return 0; - } - - return incomingStream->EnableMirroring(enable, mirrorXAxis, mirrorYAxis); -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/video_render_impl.h b/modules/video_render/main/source/video_render_impl.h deleted file mode 100644 index ddc0fc75f..000000000 --- a/modules/video_render/main/source/video_render_impl.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ - -#include "engine_configurations.h" -#include "video_render.h" -#include "map_wrapper.h" - -//#include "video_render_defines.h" - -namespace webrtc { -class CriticalSectionWrapper; -class IncomingVideoStream; -class IVideoRender; -class MapWrapper; - -// Class definitions -class ModuleVideoRenderImpl: public VideoRender -{ -public: - /* - * VideoRenderer constructor/destructor - */ - ModuleVideoRenderImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, const bool fullscreen); - - virtual ~ModuleVideoRenderImpl(); - - /* - * Returns version of the module and its components - */ - virtual WebRtc_Word32 Version(WebRtc_Word8* version, - WebRtc_UWord32& remainingBufferInBytes, - WebRtc_UWord32& position) const; - - /* - * Change the unique identifier of this object - */ - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 TimeUntilNextProcess(); - virtual WebRtc_Word32 Process(); - - /* - * Returns the render window - */ - virtual void* Window(); - - /* - * Change render window - */ - virtual WebRtc_Word32 ChangeWindow(void* window); - - /* - * Returns module id - */ - WebRtc_Word32 Id(); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - /* - * Add incoming render stream - */ - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom); - /* - * Delete incoming render stream - */ - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - /* - * Add incoming render callback, used for external rendering - */ - virtual WebRtc_Word32 - AddExternalRenderCallback(const WebRtc_UWord32 streamId, - VideoRenderCallback* renderObject); - - /* - * Get the porperties for an incoming render stream - */ - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const; - /* - * Incoming frame rate for the specified stream. - */ - virtual WebRtc_UWord32 GetIncomingFrameRate(const WebRtc_UWord32 streamId); - - /* - * Returns the number of incoming streams added to this render module - */ - virtual WebRtc_UWord32 GetNumIncomingRenderStreams() const; - - /* - * Returns true if this render module has the streamId added, false otherwise. - */ - virtual bool HasIncomingRenderStream(const WebRtc_UWord32 streamId) const; - - /* - * - */ - virtual WebRtc_Word32 - RegisterRawFrameCallback(const WebRtc_UWord32 streamId, - VideoRenderCallback* callbackObj); - - virtual WebRtc_Word32 GetLastRenderedFrame(const WebRtc_UWord32 streamId, - VideoFrame &frame) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - /* - * Starts rendering the specified stream - */ - virtual WebRtc_Word32 StartRender(const WebRtc_UWord32 streamId); - - /* - * Stops the renderer - */ - virtual WebRtc_Word32 StopRender(const WebRtc_UWord32 streamId); - - /* - * Sets the renderer in start state, no streams removed. - */ - virtual WebRtc_Word32 ResetRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - /* - * Returns the prefered render video type - */ - virtual RawVideoType PreferredVideoType() const; - - /* - * Returns true if the renderer is in fullscreen mode, otherwise false. - */ - virtual bool IsFullScreen(); - - /* - * Gets screen resolution in pixels - */ - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - /* - * Get the actual render rate for this stream. I.e rendered frame rate, - * not frames delivered to the renderer. - */ - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - /* - * Set cropping of incoming stream - */ - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 FullScreenRender(void* window, const bool enable); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 SetStartImage(const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame); - - virtual WebRtc_Word32 SetTimeoutImage(const WebRtc_UWord32 streamId, - const VideoFrame& videoFrame, - const WebRtc_UWord32 timeout); - - virtual WebRtc_Word32 MirrorRenderStream(const int renderId, - const bool enable, - const bool mirrorXAxis, - const bool mirrorYAxis); - -private: - WebRtc_Word32 _id; - CriticalSectionWrapper& _moduleCrit; - void* _ptrWindow; - VideoRenderType _renderType; - bool _fullScreen; - - IVideoRender* _ptrRenderer; - MapWrapper& _streamRenderMap; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ diff --git a/modules/video_render/main/source/windows/i_video_render_win.h b/modules/video_render/main/source/windows/i_video_render_win.h deleted file mode 100644 index a765134b4..000000000 --- a/modules/video_render/main/source/windows/i_video_render_win.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ - -#include "video_render.h" - -namespace webrtc { - -// Class definitions -class IVideoRenderWin -{ -public: - /************************************************************************** - * - * Constructor/destructor - * - ***************************************************************************/ - virtual ~IVideoRenderWin() - { - }; - - virtual WebRtc_Word32 Init() = 0; - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - virtual VideoRenderCallback - * CreateChannel(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 DeleteChannel(const WebRtc_UWord32 streamId) = 0; - - virtual WebRtc_Word32 GetStreamSettings(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) = 0; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender() = 0; - - virtual WebRtc_Word32 StopRender() = 0; - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual bool IsFullScreen() = 0; - - virtual WebRtc_Word32 SetCropping(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable) = 0; - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 colorText, - const WebRtc_UWord32 colorBg, - const float left, - const float top, - const float rigth, - const float bottom) = 0; - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) = 0; - - virtual WebRtc_Word32 ChangeWindow(void* window) = 0; - - virtual WebRtc_Word32 GetGraphicsMemory(WebRtc_UWord64& totalMemory, - WebRtc_UWord64& availableMemory) = 0; - -}; - -} //namespace webrtc -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ diff --git a/modules/video_render/main/source/windows/video_render_direct3d9.cc b/modules/video_render/main/source/windows/video_render_direct3d9.cc deleted file mode 100644 index 04704e5c3..000000000 --- a/modules/video_render/main/source/windows/video_render_direct3d9.cc +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// Own include file -#include "video_render_direct3d9.h" - -// System include files -#include - -// WebRtc include files -#include "critical_section_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "thread_wrapper.h" -#include "vplib.h" - -namespace webrtc { - -// A structure for our custom vertex type -struct CUSTOMVERTEX -{ - FLOAT x, y, z; - DWORD color; // The vertex color - FLOAT u, v; -}; - -// Our custom FVF, which describes our custom vertex structure -#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) - -/* - * - * D3D9Channel - * - */ -D3D9Channel::D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice, - CriticalSectionWrapper* critSect, - Trace* trace) : - _width(0), - _height(0), - _pd3dDevice(pd3DDevice), - _pTexture(NULL), - _bufferIsUpdated(false), - _critSect(critSect), - _streamId(0), - _zOrder(0), - _startWidth(0), - _startHeight(0), - _stopWidth(0), - _stopHeight(0) -{ - -} - -D3D9Channel::~D3D9Channel() -{ - //release the texture - if (_pTexture != NULL) - { - _pTexture->Release(); - _pTexture = NULL; - } -} - -void D3D9Channel::SetStreamSettings(WebRtc_UWord16 streamId, - WebRtc_UWord32 zOrder, - float startWidth, - float startHeight, - float stopWidth, - float stopHeight) -{ - _streamId = streamId; - _zOrder = zOrder; - _startWidth = startWidth; - _startHeight = startHeight; - _stopWidth = stopWidth; - _stopHeight = stopHeight; -} - -int D3D9Channel::GetStreamSettings(WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& startWidth, - float& startHeight, - float& stopWidth, - float& stopHeight) -{ - streamId = _streamId; - zOrder = _zOrder; - startWidth = _startWidth; - startHeight = _startHeight; - stopWidth = _stopWidth; - stopHeight = _stopHeight; - return 0; -} - -int D3D9Channel::GetTextureWidth() -{ - return _width; -} - -int D3D9Channel::GetTextureHeight() -{ - return _height; -} - -// Called from video engine when a the frame size changed -int D3D9Channel::FrameSizeChange(int width, int height, int numberOfStreams) -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "FrameSizeChange, wifth: %d, height: %d, streams: %d", width, - height, numberOfStreams); - - CriticalSectionScoped cs(*_critSect); - _width = width; - _height = height; - - //clean the previous texture - if (_pTexture != NULL) - { - _pTexture->Release(); - _pTexture = NULL; - } - - HRESULT ret = E_POINTER; - - if (_pd3dDevice) - ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8, - D3DPOOL_MANAGED, &_pTexture, NULL); - - if (FAILED(ret)) - { - _pTexture = NULL; - return -1; - } - - return 0; -} - -WebRtc_Word32 D3D9Channel::RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) -{ - CriticalSectionScoped cs(*_critSect); - if (_width != videoFrame.Width() || _height != videoFrame.Height()) - { - if (FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) - { - return -1; - } - } - return DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), - videoFrame.TimeStamp()); -} - -// Called from video engine when a new frame should be rendered. -int D3D9Channel::DeliverFrame(unsigned char* buffer, - int bufferSize, - unsigned int timeStamp90kHz) -{ - WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, - "DeliverFrame to D3D9Channel"); - - CriticalSectionScoped cs(*_critSect); - - //FIXME if _bufferIsUpdated is still true (not be renderred), do we what to update the texture?) - //probably not - if (_bufferIsUpdated) - { - WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, - "Last frame hasn't been rendered yet. Drop this frame."); - return -1; - } - - if (!_pd3dDevice) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "D3D for rendering not initialized."); - return -1; - } - - if (!_pTexture) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Texture for rendering not initialized."); - return -1; - } - - D3DLOCKED_RECT lr; - - if (FAILED(_pTexture->LockRect(0, &lr, NULL, 0))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to lock a texture in D3D9 Channel."); - return -1; - } - UCHAR* pRect = (UCHAR*) lr.pBits; - - ConvertI420ToARGB(buffer, pRect, _width, _height, 0); - - if (FAILED(_pTexture->UnlockRect(0))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to unlock a texture in D3D9 Channel."); - return -1; - } - - _bufferIsUpdated = true; - - return 0; -} - -// Called by d3d channel owner to indicate the frame/texture has been rendered off -int D3D9Channel::RenderOffFrame() -{ - WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, - "Frame has been rendered to the screen."); - CriticalSectionScoped cs(*_critSect); - _bufferIsUpdated = false; - return 0; -} - -// Called by d3d channel owner to check if the texture is updated -int D3D9Channel::IsUpdated(bool& isUpdated) -{ - CriticalSectionScoped cs(*_critSect); - isUpdated = _bufferIsUpdated; - return 0; -} - -// Called by d3d channel owner to get the texture -LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture() -{ - CriticalSectionScoped cs(*_critSect); - return _pTexture; -} - -int D3D9Channel::ReleaseTexture() -{ - CriticalSectionScoped cs(*_critSect); - - //release the texture - if (_pTexture != NULL) - { - _pTexture->Release(); - _pTexture = NULL; - } - _pd3dDevice = NULL; - return 0; -} - -int D3D9Channel::RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice) -{ - CriticalSectionScoped cs(*_critSect); - - _pd3dDevice = pd3DDevice; - - if (_pTexture != NULL) - { - _pTexture->Release(); - _pTexture = NULL; - } - - HRESULT ret; - - ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8, - D3DPOOL_MANAGED, &_pTexture, NULL); - - if (FAILED(ret)) - { - _pTexture = NULL; - return -1; - } - - return 0; -} - -/* - * - * VideoRenderDirect3D9 - * - */ -VideoRenderDirect3D9::VideoRenderDirect3D9(Trace* trace, - HWND hWnd, - bool fullScreen) : - _refD3DCritsect(*CriticalSectionWrapper::CreateCriticalSection()), - _trace(trace), - _hWnd(hWnd), - _fullScreen(fullScreen), - _pTextureLogo(NULL), - _pVB(NULL), - _pd3dDevice(NULL), - _pD3D(NULL), - _d3dChannels(), - _d3dZorder(), - _screenUpdateThread(NULL), - _screenUpdateEvent(NULL), - _logoLeft(0), - _logoTop(0), - _logoRight(0), - _logoBottom(0), - _pd3dSurface(NULL), - _totalMemory(-1), - _availableMemory(-1) -{ - _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, - this, kRealtimePriority); - _screenUpdateEvent = EventWrapper::Create(); - SetRect(&_originalHwndRect, 0, 0, 0, 0); -} - -VideoRenderDirect3D9::~VideoRenderDirect3D9() -{ - //NOTE: we should not enter CriticalSection in here! - - // Signal event to exit thread, then delete it - ThreadWrapper* tmpPtr = _screenUpdateThread; - _screenUpdateThread = NULL; - if (tmpPtr) - { - tmpPtr->SetNotAlive(); - _screenUpdateEvent->Set(); - _screenUpdateEvent->StopTimer(); - - if (tmpPtr->Stop()) - { - delete tmpPtr; - } - } - delete _screenUpdateEvent; - - //close d3d device - CloseDevice(); - - // Delete all channels - std::map::iterator it = _d3dChannels.begin(); - while (it != _d3dChannels.end()) - { - delete it->second; - it = _d3dChannels.erase(it); - } - // Clean the zOrder map - _d3dZorder.clear(); - - if (_fullScreen) - { - // restore hwnd to original size and position - ::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left, - _originalHwndRect.top, _originalHwndRect.right - - _originalHwndRect.left, - _originalHwndRect.bottom - _originalHwndRect.top, - SWP_FRAMECHANGED); - ::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW - | RDW_ERASE); - ::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW - | RDW_ERASE); - } - - delete &_refD3DCritsect; -} - -DWORD VideoRenderDirect3D9::GetVertexProcessingCaps() -{ - D3DCAPS9 caps; - DWORD dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING; - if (SUCCEEDED(_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, - &caps))) - { - if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) - == D3DDEVCAPS_HWTRANSFORMANDLIGHT) - { - dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING; - } - } - return dwVertexProcessing; -} - -int VideoRenderDirect3D9::InitializeD3D(HWND hWnd, - D3DPRESENT_PARAMETERS* pd3dpp) -{ - // initialize Direct3D - if (NULL == (_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) - { - return -1; - } - - // determine what type of vertex processing to use based on the device capabilities - DWORD dwVertexProcessing = GetVertexProcessingCaps(); - - // get the display mode - D3DDISPLAYMODE d3ddm; - _pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); - pd3dpp->BackBufferFormat = d3ddm.Format; - - // create the D3D device - if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, - dwVertexProcessing | D3DCREATE_MULTITHREADED - | D3DCREATE_FPU_PRESERVE, pd3dpp, - &_pd3dDevice))) - { - //try the ref device - if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, - hWnd, dwVertexProcessing - | D3DCREATE_MULTITHREADED - | D3DCREATE_FPU_PRESERVE, - pd3dpp, &_pd3dDevice))) - { - return -1; - } - } - - return 0; -} - -int VideoRenderDirect3D9::ResetDevice() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "VideoRenderDirect3D9::ResetDevice"); - - CriticalSectionScoped cs(_refD3DCritsect); - - //release the channel texture - std::map::iterator it; - it = _d3dChannels.begin(); - while (it != _d3dChannels.end()) - { - if (it->second) - { - it->second->ReleaseTexture(); - } - it++; - } - - //close d3d device - if (CloseDevice() != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "VideoRenderDirect3D9::ResetDevice failed to CloseDevice"); - return -1; - } - - //reinit d3d device - if (InitDevice() != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "VideoRenderDirect3D9::ResetDevice failed to InitDevice"); - return -1; - } - - //recreate channel texture - it = _d3dChannels.begin(); - while (it != _d3dChannels.end()) - { - if (it->second) - { - it->second->RecreateTexture(_pd3dDevice); - } - it++; - } - - return 0; -} - -int VideoRenderDirect3D9::InitDevice() -{ - // Set up the structure used to create the D3DDevice - ZeroMemory(&_d3dpp, sizeof(_d3dpp)); - _d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - _d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; - if (GetWindowRect(_hWnd, &_originalHwndRect) == 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "VideoRenderDirect3D9::InitDevice Could not get window size"); - return -1; - } - if (!_fullScreen) - { - _winWidth = _originalHwndRect.right - _originalHwndRect.left; - _winHeight = _originalHwndRect.bottom - _originalHwndRect.top; - _d3dpp.Windowed = TRUE; - _d3dpp.BackBufferHeight = 0; - _d3dpp.BackBufferWidth = 0; - } - else - { - _winWidth = (LONG) ::GetSystemMetrics(SM_CXSCREEN); - _winHeight = (LONG) ::GetSystemMetrics(SM_CYSCREEN); - _d3dpp.Windowed = FALSE; - _d3dpp.BackBufferWidth = _winWidth; - _d3dpp.BackBufferHeight = _winHeight; - _d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - } - - if (InitializeD3D(_hWnd, &_d3dpp) == -1) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "VideoRenderDirect3D9::InitDevice failed in InitializeD3D"); - return -1; - } - - // Turn off culling, so we see the front and back of the triangle - _pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - - // Turn off D3D lighting, since we are providing our own vertex colors - _pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); - - // Settings for alpha blending - _pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - _pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - _pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - //_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1); - //_pd3dDevice->SetTextureStageState(0,D3DTSS_ALPHAARG1,D3DTA_TEXTURE); - - // Initialize Vertices - CUSTOMVERTEX Vertices[] = { - //front - { -1.0f, -1.0f, 0.0f, 0xffffffff, 0, 1 }, { -1.0f, 1.0f, 0.0f, - 0xffffffff, 0, 0 }, - { 1.0f, -1.0f, 0.0f, 0xffffffff, 1, 1 }, { 1.0f, 1.0f, 0.0f, - 0xffffffff, 1, 0 } }; - - // Create the vertex buffer. - if (FAILED(_pd3dDevice->CreateVertexBuffer(sizeof(Vertices), 0, - D3DFVF_CUSTOMVERTEX, - D3DPOOL_DEFAULT, &_pVB, NULL ))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to create the vertex buffer."); - return -1; - } - - // Now we fill the vertex buffer. - VOID* pVertices; - if (FAILED(_pVB->Lock(0, sizeof(Vertices), (void**) &pVertices, 0))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to lock the vertex buffer."); - return -1; - } - memcpy(pVertices, Vertices, sizeof(Vertices)); - _pVB->Unlock(); - - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::Init() -{ - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "VideoRenderDirect3D9::Init"); - - CriticalSectionScoped cs(_refD3DCritsect); - - // Start rendering thread... - if (!_screenUpdateThread) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Thread not created"); - return -1; - } - unsigned int threadId; - _screenUpdateThread->Start(threadId); - - // Start the event triggering the render process - unsigned int monitorFreq = 60; - DEVMODE dm; - // initialize the DEVMODE structure - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm)) - { - monitorFreq = dm.dmDisplayFrequency; - } - _screenUpdateEvent->StartTimer(true, 1000 / monitorFreq); - - return InitDevice(); -} - -WebRtc_Word32 VideoRenderDirect3D9::ChangeWindow(void* window) -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return -1; -} - -int VideoRenderDirect3D9::UpdateRenderSurface() -{ - CriticalSectionScoped cs(_refD3DCritsect); - - // Check if there are any updated buffers - bool updated = false; - std::map::iterator it; - it = _d3dChannels.begin(); - while (it != _d3dChannels.end()) - { - - D3D9Channel* channel = it->second; - channel->IsUpdated(updated); - if (updated) - { - break; - } - it++; - } - //nothing is updated, continue - if (!updated) - return -1; - - // Clear the backbuffer to a black color - _pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, - 0); - - // Begin the scene - if (SUCCEEDED(_pd3dDevice->BeginScene())) - { - _pd3dDevice->SetStreamSource(0, _pVB, 0, sizeof(CUSTOMVERTEX)); - _pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); - - D3DXMATRIX matWorld; - D3DXMATRIX matWorldTemp; - - //draw all the channels - //get texture from the channels - LPDIRECT3DTEXTURE9 textureFromChannel = NULL; - DWORD textureWidth, textureHeight; - - std::multimap::reverse_iterator it; - it = _d3dZorder.rbegin(); - while (it != _d3dZorder.rend()) - { - // loop through all channels and streams in Z order - int channel = it->second & 0x0000ffff; - - std::map::iterator ddIt; - ddIt = _d3dChannels.find(channel); - if (ddIt != _d3dChannels.end()) - { - // found the channel - D3D9Channel* channelObj = ddIt->second; - if (channelObj) - { - textureFromChannel = channelObj->GetTexture(); - textureWidth = channelObj->GetTextureWidth(); - textureHeight = channelObj->GetTextureHeight(); - - WebRtc_UWord32 zOrder; - float startWidth, startHeight, stopWidth, stopHeight; - channelObj->GetStreamSettings(0, zOrder, startWidth, - startHeight, stopWidth, - stopHeight); - - //draw the video stream - UpdateVerticeBuffer(_pVB, 0, startWidth, startHeight, - stopWidth, stopHeight); - _pd3dDevice->SetTexture(0, textureFromChannel); - _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - - //Notice channel that this frame as been rendered - channelObj->RenderOffFrame(); - } - } - it++; - } - - //draw the logo - if (_pTextureLogo) - { - UpdateVerticeBuffer(_pVB, 0, _logoLeft, _logoTop, _logoRight, - _logoBottom); - _pd3dDevice->SetTexture(0, _pTextureLogo); - _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - } - - // End the scene - _pd3dDevice->EndScene(); - } - - // Present the backbuffer contents to the display - _pd3dDevice->Present(NULL, NULL, NULL, NULL ); - - return 0; -} - -//set the alpha value of the pixal with a particular colorkey as 0 -int VideoRenderDirect3D9::SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture, - DDCOLORKEY* transparentColorKey, - DWORD width, - DWORD height) -{ - D3DLOCKED_RECT lr; - if (!pTexture) - return -1; - - CriticalSectionScoped cs(_refD3DCritsect); - if (SUCCEEDED(pTexture->LockRect(0, &lr, NULL, D3DLOCK_DISCARD))) - { - for (DWORD y = 0; y < height; y++) - { - DWORD dwOffset = y * width; - - for (DWORD x = 0; x < width; x) - { - DWORD a = (DWORD) 0; - - DWORD temp = ((DWORD*) lr.pBits)[dwOffset + x]; - if ((temp & 0x00FFFFFF) - == transparentColorKey->dwColorSpaceLowValue) - { - temp &= 0x00FFFFFF; - } - else - { - temp |= 0xFF000000; - } - ((DWORD*) lr.pBits)[dwOffset + x] = temp; - x++; - } - } - pTexture->UnlockRect(0); - return 0; - } - return -1; -} - -/* - * - * Rendering process - * - */ -bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj) -{ - return static_cast (obj)->ScreenUpdateProcess(); -} - -bool VideoRenderDirect3D9::ScreenUpdateProcess() -{ - _screenUpdateEvent->Wait(100); - - if (!_screenUpdateThread) - { - //stop the thread - return false; - } - if (!_pd3dDevice) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "d3dDevice not created."); - return true; - } - - HRESULT hr = _pd3dDevice->TestCooperativeLevel(); - - if (SUCCEEDED(hr)) - { - UpdateRenderSurface(); - } - - if (hr == D3DERR_DEVICELOST) - { - //Device is lost and cannot be reset yet - - } - else if (hr == D3DERR_DEVICENOTRESET) - { - //Lost but we can reset it now - //Note: the standard way is to call Reset, however for some reason doesn't work here. - //so we will release the device and create it again. - ResetDevice(); - } - - return true; -} - -int VideoRenderDirect3D9::CloseDevice() -{ - CriticalSectionScoped cs(_refD3DCritsect); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "VideoRenderDirect3D9::CloseDevice"); - - if (_pTextureLogo != NULL) - { - _pTextureLogo->Release(); - _pTextureLogo = NULL; - } - - if (_pVB != NULL) - { - _pVB->Release(); - _pVB = NULL; - } - - if (_pd3dDevice != NULL) - { - _pd3dDevice->Release(); - _pd3dDevice = NULL; - } - - if (_pD3D != NULL) - { - _pD3D->Release(); - _pD3D = NULL; - } - - if (_pd3dSurface != NULL) - _pd3dSurface->Release(); - return 0; -} - -D3D9Channel* VideoRenderDirect3D9::GetD3DChannel(int channel) -{ - std::map::iterator ddIt; - ddIt = _d3dChannels.find(channel & 0x0000ffff); - D3D9Channel* ddobj = NULL; - if (ddIt != _d3dChannels.end()) - { - ddobj = ddIt->second; - } - if (ddobj == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D render failed to find channel"); - return NULL; - } - return ddobj; -} - -WebRtc_Word32 VideoRenderDirect3D9::DeleteChannel(const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_refD3DCritsect); - - - std::multimap::iterator it; - it = _d3dZorder.begin(); - while (it != _d3dZorder.end()) - { - if ((streamId & 0x0000ffff) == (it->second & 0x0000ffff)) - { - it = _d3dZorder.erase(it); - break; - } - it++; - } - - std::map::iterator ddIt; - ddIt = _d3dChannels.find(streamId & 0x0000ffff); - D3D9Channel* ddobj = NULL; - if (ddIt != _d3dChannels.end()) - { - delete ddIt->second; - _d3dChannels.erase(ddIt); - return 0; - } - return -1; -} - -VideoRenderCallback* VideoRenderDirect3D9::CreateChannel(const WebRtc_UWord32 channel, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_refD3DCritsect); - - //FIXME this should be done in VideoAPIWindows? stop the frame deliver first - //remove the old channel - DeleteChannel(channel); - - D3D9Channel* d3dChannel = new D3D9Channel(_pd3dDevice, - &_refD3DCritsect, _trace); - d3dChannel->SetStreamSettings(0, zOrder, left, top, right, bottom); - - // store channel - _d3dChannels[channel & 0x0000ffff] = d3dChannel; - - // store Z order - // default streamID is 0 - _d3dZorder.insert( - std::pair(zOrder, channel & 0x0000ffff)); - - return d3dChannel; -} - -WebRtc_Word32 VideoRenderDirect3D9::GetStreamSettings(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) -{ - std::map::iterator ddIt; - ddIt = _d3dChannels.find(channel & 0x0000ffff); - D3D9Channel* ddobj = NULL; - if (ddIt != _d3dChannels.end()) - { - ddobj = ddIt->second; - } - if (ddobj == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D render failed to find channel"); - return -1; - } - // Only allow one stream per channel, demuxing is - return ddobj->GetStreamSettings(0, zOrder, left, top, right, bottom); - //return ddobj->GetStreamSettings(streamId, zOrder, left, top, right, bottom); -} - -int VideoRenderDirect3D9::UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB, - int offset, - float startWidth, - float startHeight, - float stopWidth, - float stopHeight) -{ - if (pVB == NULL) - return -1; - - float left, right, top, bottom; - - //update the vertice buffer - //0,1 => -1,1 - left = startWidth * 2 - 1; - right = stopWidth * 2 - 1; - - //0,1 => 1,-1 - top = 1 - startHeight * 2; - bottom = 1 - stopHeight * 2; - - CUSTOMVERTEX newVertices[] = { - //logo - { left, bottom, 0.0f, 0xffffffff, 0, 1 }, { left, top, 0.0f, - 0xffffffff, 0, 0 }, - { right, bottom, 0.0f, 0xffffffff, 1, 1 }, { right, top, 0.0f, - 0xffffffff, 1, 0 }, }; - // Now we fill the vertex buffer. - VOID* pVertices; - if (FAILED(pVB->Lock(sizeof(CUSTOMVERTEX) * offset, sizeof(newVertices), - (void**) &pVertices, 0))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to lock the vertex buffer."); - return -1; - } - memcpy(pVertices, newVertices, sizeof(newVertices)); - pVB->Unlock(); - - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::StartRender() -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::StopRender() -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -bool VideoRenderDirect3D9::IsFullScreen() -{ - return _fullScreen; -} - -WebRtc_Word32 VideoRenderDirect3D9::SetCropping(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::SetTransparentBackground( - const bool enable) -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 colorText, - const WebRtc_UWord32 colorBg, - const float left, - const float top, - const float rigth, - const float bottom) -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - if (!bitMap) - { - if (_pTextureLogo != NULL) - { - _pTextureLogo->Release(); - _pTextureLogo = NULL; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "Remove bitmap."); - return 0; - } - - // sanity - if (left > 1.0f || left < 0.0f || - top > 1.0f || top < 0.0f || - right > 1.0f || right < 0.0f || - bottom > 1.0f || bottom < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D SetBitmap invalid parameter"); - return -1; - } - - if ((bottom <= top) || (right <= left)) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D SetBitmap invalid parameter"); - return -1; - } - - CriticalSectionScoped cs(_refD3DCritsect); - - unsigned char* srcPtr; - HGDIOBJ oldhand; - BITMAPINFO pbi; - BITMAP bmap; - HDC hdcNew; - hdcNew = CreateCompatibleDC(0); - // Fill out the BITMAP structure. - GetObject((HBITMAP)bitMap, sizeof(bmap), &bmap); - //Select the bitmap handle into the new device context. - oldhand = SelectObject(hdcNew, (HGDIOBJ) bitMap); - // we are done with this object - DeleteObject(oldhand); - pbi.bmiHeader.biSize = 40; - pbi.bmiHeader.biWidth = bmap.bmWidth; - pbi.bmiHeader.biHeight = bmap.bmHeight; - pbi.bmiHeader.biPlanes = 1; - pbi.bmiHeader.biBitCount = bmap.bmBitsPixel; - pbi.bmiHeader.biCompression = BI_RGB; - pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3; - srcPtr = new unsigned char[bmap.bmWidth * bmap.bmHeight * 4]; - // the original un-stretched image in RGB24 - int pixelHeight = GetDIBits(hdcNew, (HBITMAP)bitMap, 0, bmap.bmHeight, srcPtr, &pbi, - DIB_RGB_COLORS); - if (pixelHeight == 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D failed to GetDIBits in SetBitmap"); - return -1; - } - DeleteDC(hdcNew); - if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D failed to SetBitmap invalid bit depth"); - return -1; - } - - HRESULT ret; - //release the previous logo texture - if (_pTextureLogo != NULL) - { - _pTextureLogo->Release(); - _pTextureLogo = NULL; - } - ret = _pd3dDevice->CreateTexture(bmap.bmWidth, bmap.bmHeight, 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - &_pTextureLogo, NULL); - if (FAILED(ret)) - { - _pTextureLogo = NULL; - return -1; - } - if (!_pTextureLogo) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Texture for rendering not initialized."); - return -1; - } - - D3DLOCKED_RECT lr; - if (FAILED(_pTextureLogo->LockRect(0, &lr, NULL, 0))) - { - return -1; - } - unsigned char* dstPtr = (UCHAR*) lr.pBits; - int pitch = bmap.bmWidth * 4; - - if (pbi.bmiHeader.biBitCount == 24) - { - ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight, 0); - } - else - { - unsigned char* srcTmp = srcPtr + (bmap.bmWidth * 4) * (bmap.bmHeight - 1); - for (int i = 0; i < bmap.bmHeight; ++i) - { - memcpy(dstPtr, srcTmp, bmap.bmWidth * 4); - srcTmp -= bmap.bmWidth * 4; - dstPtr += pitch; - } - } - - delete srcPtr; - if (FAILED(_pTextureLogo->UnlockRect(0))) - { - return -1; - } - - if (colorKey) - { - DDCOLORKEY* ddColorKey = - static_cast (const_cast (colorKey)); - SetTransparentColor(_pTextureLogo, ddColorKey, bmap.bmWidth, - bmap.bmHeight); - } - - //update the vertice buffer - //0,1 => -1,1 - _logoLeft = left; - _logoRight = right; - - //0,1 => 1,-1 - _logoTop = top; - _logoBottom = bottom; - - return 0; - -} - -WebRtc_Word32 VideoRenderDirect3D9::GetGraphicsMemory(WebRtc_UWord64& totalMemory, - WebRtc_UWord64& availableMemory) -{ - if (_totalMemory == -1 || _availableMemory == -1) - { - totalMemory = 0; - availableMemory = 0; - return -1; - } - totalMemory = _totalMemory; - availableMemory = _availableMemory; - return 0; -} - -WebRtc_Word32 VideoRenderDirect3D9::ConfigureRenderer(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - std::map::iterator ddIt; - ddIt = _d3dChannels.find(channel & 0x0000ffff); - D3D9Channel* ddobj = NULL; - if (ddIt != _d3dChannels.end()) - { - ddobj = ddIt->second; - } - if (ddobj == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct3D render failed to find channel"); - return -1; - } - // Only allow one stream per channel, demuxing is - ddobj->SetStreamSettings(0, zOrder, left, top, right, bottom); - - return 0; -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/windows/video_render_direct3d9.h b/modules/video_render/main/source/windows/video_render_direct3d9.h deleted file mode 100644 index 6d6fef3d6..000000000 --- a/modules/video_render/main/source/windows/video_render_direct3d9.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ - -// WebRtc includes -#include "i_video_render_win.h" - -#include -#include -#include "ddraw.h" - -#include - -// Added -#include "video_render_defines.h" - -#pragma comment(lib, "d3d9.lib") // located in DirectX SDK - -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class Trace; -class ThreadWrapper; - -class D3D9Channel: public VideoRenderCallback -{ -public: - D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice, - CriticalSectionWrapper* critSect, Trace* trace); - - virtual ~D3D9Channel(); - - // Inherited from VideoRencerCallback, called from VideoAPI class. - // Called when the incomming frame size and/or number of streams in mix changes - virtual int FrameSizeChange(int width, int height, int numberOfStreams); - - // A new frame is delivered - virtual int DeliverFrame(unsigned char* buffer, - int bufferSize, - unsigned int timeStame90kHz); - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - - // Called to check if the video frame is updated. - int IsUpdated(bool& isUpdated); - // Called after the video frame has been render to the screen - int RenderOffFrame(); - // Called to get the texture that contains the video frame - LPDIRECT3DTEXTURE9 GetTexture(); - // Called to get the texture(video frame) size - int GetTextureWidth(); - int GetTextureHeight(); - // - void SetStreamSettings(WebRtc_UWord16 streamId, - WebRtc_UWord32 zOrder, - float startWidth, - float startHeight, - float stopWidth, - float stopHeight); - int GetStreamSettings(WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& startWidth, - float& startHeight, - float& stopWidth, - float& stopHeight); - - int ReleaseTexture(); - int RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice); - -protected: - -private: - //critical section passed from the owner - CriticalSectionWrapper* _critSect; - LPDIRECT3DDEVICE9 _pd3dDevice; - LPDIRECT3DTEXTURE9 _pTexture; - - bool _bufferIsUpdated; - // the frame size - int _width; - int _height; - //sream settings - //TODO support multiple streams in one channel - WebRtc_UWord16 _streamId; - WebRtc_UWord32 _zOrder; - float _startWidth; - float _startHeight; - float _stopWidth; - float _stopHeight; -}; - -class VideoRenderDirect3D9: IVideoRenderWin -{ -public: - VideoRenderDirect3D9(Trace* trace, HWND hWnd, bool fullScreen); - ~VideoRenderDirect3D9(); - -public: - //IVideoRenderWin - - /************************************************************************** - * - * Init - * - ***************************************************************************/ - virtual WebRtc_Word32 Init(); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - virtual VideoRenderCallback - * CreateChannel(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 DeleteChannel(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 GetStreamSettings(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom); - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual bool IsFullScreen(); - - virtual WebRtc_Word32 SetCropping(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - virtual WebRtc_Word32 GetGraphicsMemory(WebRtc_UWord64& totalMemory, - WebRtc_UWord64& availableMemory); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 colorText, - const WebRtc_UWord32 colorBg, - const float left, - const float top, - const float rigth, - const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom); - -public: - // Get a channel by channel id - D3D9Channel* GetD3DChannel(int channel); - int UpdateRenderSurface(); - -protected: - // The thread rendering the screen - static bool ScreenUpdateThreadProc(void* obj); - bool ScreenUpdateProcess(); - -private: - // Init/close the d3d device - int InitDevice(); - int CloseDevice(); - - // Transparent related functions - int SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture, - DDCOLORKEY* transparentColorKey, - DWORD width, - DWORD height); - - CriticalSectionWrapper& _refD3DCritsect; - Trace* _trace; - ThreadWrapper* _screenUpdateThread; - EventWrapper* _screenUpdateEvent; - - HWND _hWnd; - bool _fullScreen; - RECT _originalHwndRect; - //FIXME we probably don't need this since all the information can be get from _d3dChannels - int _channel; - //Window size - UINT _winWidth; - UINT _winHeight; - - // Device - LPDIRECT3D9 _pD3D; // Used to create the D3DDevice - LPDIRECT3DDEVICE9 _pd3dDevice; // Our rendering device - LPDIRECT3DVERTEXBUFFER9 _pVB; // Buffer to hold Vertices - LPDIRECT3DTEXTURE9 _pTextureLogo; - - std::map _d3dChannels; - std::multimap _d3dZorder; - - // The position where the logo will be placed - float _logoLeft; - float _logoTop; - float _logoRight; - float _logoBottom; - - typedef HRESULT (WINAPI *DIRECT3DCREATE9EX)(UINT SDKVersion, IDirect3D9Ex**); - LPDIRECT3DSURFACE9 _pd3dSurface; - - DWORD GetVertexProcessingCaps(); - int InitializeD3D(HWND hWnd, D3DPRESENT_PARAMETERS* pd3dpp); - - D3DPRESENT_PARAMETERS _d3dpp; - int ResetDevice(); - - int UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB, int offset, - float startWidth, float startHeight, - float stopWidth, float stopHeight); - - //code for providing graphics settings - DWORD _totalMemory; - DWORD _availableMemory; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ diff --git a/modules/video_render/main/source/windows/video_render_directdraw.cc b/modules/video_render/main/source/windows/video_render_directdraw.cc deleted file mode 100644 index 64a4e246d..000000000 --- a/modules/video_render/main/source/windows/video_render_directdraw.cc +++ /dev/null @@ -1,4022 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "video_render_directdraw.h" -#include "video_render_windows_impl.h" -#include "Windows.h" -#include -#include -#include -#include // timeGetTime -DEFINE_GUID( IID_IDirectDraw7,0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b ); - -#include "thread_wrapper.h" -#include "event_wrapper.h" -#include "trace.h" -#include "critical_section_wrapper.h" -//#include "VideoErrors.h" -#include "vplib.h" - -// Added -#include "module_common_types.h" - -#pragma warning(disable: 4355) // 'this' : used in base member initializer list -// picture in picture do we need overlay? answer no we can blit directly -// conference is easy since we can blt the quadrants seperatly - -// To determine if the driver supports DMA, retrieve the driver capabilities by calling the IDirectDraw::GetCaps method, -// then look for DDBLTCAPS_READSYSMEM and/or DDBLTCAPS_WRITESYSMEM. If either of these flags is set, the device supports DMA. -// Blt with SRCCOPY should do this can we use it? -// investigate DDLOCK_NOSYSLOCK - -namespace webrtc { - -#define EXTRACT_BITS_RL(the_val, bits_start, bits_len) ((the_val >> (bits_start - 1)) & ((1 << bits_len) - 1)) - -WindowsThreadCpuUsage::WindowsThreadCpuUsage() : - _lastGetCpuUsageTime(0), - _lastCpuUsageTime(0), - _hThread(::GetCurrentThread()), - _cores(0), - _lastCpuUsage(0) -{ - - DWORD_PTR pmask, smask; - DWORD access = PROCESS_QUERY_INFORMATION; - if (GetProcessAffinityMask( - OpenProcess(access, false, GetCurrentProcessId()), - &pmask, &smask) != 0) - { - - for (int i = 1; i < 33; i++) - { - if (EXTRACT_BITS_RL(pmask,i,1) == 0) - { - break; - } - _cores++; - } - //sanity - if (_cores > 32) - { - _cores = 32; - } - if (_cores < 1) - { - _cores = 1; - } - } - else - { - _cores = 1; - } - GetCpuUsage(); -} - -//in % since last call -int WindowsThreadCpuUsage::GetCpuUsage() -{ - DWORD now = timeGetTime(); - - _int64 newTime = 0; - FILETIME creationTime; - FILETIME exitTime; - _int64 kernelTime = 0; - _int64 userTime = 0; - if (GetThreadTimes(_hThread, (FILETIME*) &creationTime, &exitTime, - (FILETIME*) &kernelTime, (FILETIME*) &userTime) != 0) - { - newTime = (kernelTime + userTime); - } - if (newTime == 0) - { - _lastGetCpuUsageTime = now; - return _lastCpuUsage; - } - - // calculate the time difference since last call - const DWORD diffTime = (now - _lastGetCpuUsageTime); - _lastGetCpuUsageTime = now; - - if (newTime < _lastCpuUsageTime) - { - _lastCpuUsageTime = newTime; - return _lastCpuUsage; - } - const int cpuDiff = (int) (newTime - _lastCpuUsageTime) / 10000; - _lastCpuUsageTime = newTime; - - // calculate the CPU usage - - _lastCpuUsage = (int) (float((cpuDiff * 100)) / (diffTime * _cores) + 0.5f); - - if (_lastCpuUsage > 100) - { - _lastCpuUsage = 100; - } - return _lastCpuUsage; - -} - -DirectDrawStreamSettings::DirectDrawStreamSettings() : - _startWidth(0.0F), - _stopWidth(1.0F), - _startHeight(0.0F), - _stopHeight(1.0F), - _cropStartWidth(0.0F), - _cropStopWidth(1.0F), - _cropStartHeight(0.0F), - _cropStopHeight(1.0F) -{ -} -; - -DirectDrawBitmapSettings::DirectDrawBitmapSettings() : - _transparentBitMap(NULL), - _transparentBitmapLeft(0.0f), - _transparentBitmapRight(1.0f), - _transparentBitmapTop(0.0f), - _transparentBitmapBottom(1.0f), - _transparentBitmapWidth(0), - _transparentBitmapHeight(0), - _transparentBitmapColorKey(NULL), - _transparentBitmapSurface(NULL) -{ -} -; - -DirectDrawBitmapSettings::~DirectDrawBitmapSettings() -{ - if (_transparentBitmapColorKey) - { - delete _transparentBitmapColorKey; - } - if (_transparentBitmapSurface) - { - _transparentBitmapSurface->Release(); - } - _transparentBitmapColorKey = NULL; - _transparentBitmapSurface = NULL; -} -; - -int DirectDrawBitmapSettings::SetBitmap(Trace* _trace, - DirectDraw* directDraw) -{ - VideoFrame tempVideoBuffer; - HGDIOBJ oldhand; - BITMAPINFO pbi; - BITMAP bmap; - HDC hdcNew; - - hdcNew = CreateCompatibleDC(0); - - // Fill out the BITMAP structure. - GetObject(_transparentBitMap, sizeof(bmap), &bmap); - - //Select the bitmap handle into the new device context. - oldhand = SelectObject(hdcNew, (HGDIOBJ) _transparentBitMap); - - // we are done with this object - DeleteObject(oldhand); - - pbi.bmiHeader.biSize = 40; - pbi.bmiHeader.biWidth = bmap.bmWidth; - pbi.bmiHeader.biHeight = bmap.bmHeight; - pbi.bmiHeader.biPlanes = 1; - pbi.bmiHeader.biBitCount = bmap.bmBitsPixel; - pbi.bmiHeader.biCompression = BI_RGB; - pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3; - - tempVideoBuffer.VerifyAndAllocate(bmap.bmWidth * bmap.bmHeight * 4); - - // the original un-stretched image in RGB24 - // todo is there another struct for pbi purify reports read of 24 bytes larger than size - int pixelHeight = GetDIBits(hdcNew, _transparentBitMap, 0, bmap.bmHeight, - tempVideoBuffer.Buffer(), &pbi, DIB_RGB_COLORS); - if (pixelHeight == 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to GetDIBits in SetBitmap."); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - DeleteDC(hdcNew); - - if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetBitmap invalid bit depth"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - DirectDrawSurfaceDesc ddsd; - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; - ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; - ddsd.dwHeight = bmap.bmHeight; - ddsd.dwWidth = bmap.bmWidth; - - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - - _transparentBitmapWidth = bmap.bmWidth; - _transparentBitmapHeight = bmap.bmHeight; - - ddsd.ddpfPixelFormat.dwRGBBitCount = 32; - ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; - ddsd.ddpfPixelFormat.dwGBitMask = 0xff00; - ddsd.ddpfPixelFormat.dwBBitMask = 0xff; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - - if (_transparentBitmapSurface) - { - _transparentBitmapSurface->Release(); - _transparentBitmapSurface = NULL; - } - - HRESULT ddrval = - directDraw->CreateSurface(&ddsd, &_transparentBitmapSurface, NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _transparentBitmapSurface: 0x%x", - ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - memset(&ddsd, 0, sizeof(DDSURFACEDESC)); - ddsd.dwSize = sizeof(DDSURFACEDESC); - ddrval = _transparentBitmapSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); - if (ddrval == DDERR_SURFACELOST) - { - ddrval = _transparentBitmapSurface->Restore(); - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw failed to restore lost _transparentBitmapSurface"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw restored lost _transparentBitmapSurface"); - - ddrval - = _transparentBitmapSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, - NULL); - if (ddrval != DD_OK) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "DirectDraw lock error 0x%x _transparentBitmapSurface", - ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - } - unsigned char* dstPtr = (unsigned char*) ddsd.lpSurface; - unsigned char* srcPtr = (unsigned char*) tempVideoBuffer.Buffer(); - - int pitch = bmap.bmWidth * 4; - if (ddsd.dwFlags & DDSD_PITCH) - { - pitch = ddsd.lPitch; - } - - if (pbi.bmiHeader.biBitCount == 24) - { - ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight, - 0); - } - else - { - srcPtr += (bmap.bmWidth * 4) * (bmap.bmHeight - 1); - - for (int i = 0; i < bmap.bmHeight; ++i) - { - memcpy(dstPtr, srcPtr, bmap.bmWidth * 4); - srcPtr -= bmap.bmWidth * 4; - dstPtr += pitch; - } - } - - _transparentBitmapSurface->Unlock(NULL); - return 0; -} -/** - * - * DirectDrawTextSettings - * - */ -DirectDrawTextSettings::DirectDrawTextSettings() : - _ptrText(NULL), - _textLength(0), - _colorRefText(RGB(255, 255, 255)), // white - _colorRefBackground(RGB(0, 0, 0)), // black - _textLeft(0.0f), - _textRight(0.0f), - _textTop(0.0f), - _textBottom(0.0f), - _transparent(true) -{ -} - -DirectDrawTextSettings::~DirectDrawTextSettings() -{ - if (_ptrText) - { - delete[] _ptrText; - } -} - -int DirectDrawTextSettings::SetText(const char* text, int textLength, - COLORREF colorText, COLORREF colorBg, - float left, float top, float right, - float bottom) -{ - if (_ptrText) - { - delete[] _ptrText; - } - _ptrText = new char[textLength]; - memcpy(_ptrText, text, textLength); - _textLength = textLength; - _colorRefText = colorText; - _colorRefBackground = colorBg; - //_transparent = transparent; - _textLeft = left; - _textRight = right; - _textTop = top; - _textBottom = bottom; - return 0; -} - -/** - * - * DirectDrawChannel - * - * - */ - -// this need to have a refcount dueto multiple HWNDS demux -DirectDrawChannel::DirectDrawChannel(DirectDraw* directDraw, - VideoType blitVideoType, - VideoType incomingVideoType, - VideoType screenVideoType, - VideoRenderDirectDraw* owner) : - - _critSect(CriticalSectionWrapper::CreateCriticalSection()), _refCount(1), - _width(0), _height(0), _numberOfStreams(0), _doubleBuffer(false), - _directDraw(directDraw), _offScreenSurface(NULL), - _offScreenSurfaceNext(NULL), _incomingVideoType(incomingVideoType), - _blitVideoType(blitVideoType), - _originalBlitVideoType(blitVideoType), - _screenVideoType(screenVideoType), _deliverInScreenType(false), - _owner(owner) -{ - _directDraw->AddRef(); -} - -DirectDrawChannel::~DirectDrawChannel() -{ - if (_directDraw) - { - _directDraw->Release(); - } - if (_offScreenSurface) - { - _offScreenSurface->Release(); - } - if (_offScreenSurfaceNext) - { - _offScreenSurfaceNext->Release(); - } - std::map::iterator it = - _streamIdToSettings.begin(); - while (it != _streamIdToSettings.end()) - { - DirectDrawStreamSettings* streamSettings = it->second; - if (streamSettings) - { - delete streamSettings; - } - it = _streamIdToSettings.erase(it); - } - delete _critSect; -} - -void DirectDrawChannel::AddRef() -{ - CriticalSectionScoped cs(*_critSect); - _refCount++; -} - -void DirectDrawChannel::Release() -{ - bool deleteObj = false; - _critSect->Enter(); - _refCount--; - if (_refCount == 0) - { - deleteObj = true; - } - _critSect->Leave(); - - if (deleteObj) - { - delete this; - } -} - -void DirectDrawChannel::SetStreamSettings(VideoRenderDirectDraw* DDobj, - short streamId, float startWidth, - float startHeight, - float stopWidth, float stopHeight) -{ - // we can save 5 bits due to 16 byte alignment of the pointer - unsigned long long lookupID = reinterpret_cast (DDobj); - lookupID &= 0xffffffffffffffe0; - lookupID <<= 11; - lookupID += streamId; - - CriticalSectionScoped cs(*_critSect); - - DirectDrawStreamSettings* streamSettings = NULL; - - std::map::iterator it = - _streamIdToSettings.find(lookupID); - if (it == _streamIdToSettings.end()) - { - streamSettings = new DirectDrawStreamSettings(); - _streamIdToSettings[lookupID] = streamSettings; - } - else - { - streamSettings = it->second; - } - - streamSettings->_startHeight = startHeight; - streamSettings->_startWidth = startWidth; - streamSettings->_stopWidth = stopWidth; - streamSettings->_stopHeight = stopHeight; - - _offScreenSurfaceUpdated = false; -} - -void DirectDrawChannel::SetStreamCropSettings(VideoRenderDirectDraw* DDObj, - short streamId, - float startWidth, - float startHeight, - float stopWidth, - float stopHeight) -{ - unsigned long long lookupID = reinterpret_cast (DDObj); - lookupID &= 0xffffffffffffffe0; - lookupID <<= 11; - lookupID += streamId; - - CriticalSectionScoped cs(*_critSect); - - DirectDrawStreamSettings* streamSettings = NULL; - std::map::iterator it = - _streamIdToSettings.find(lookupID); - if (it == _streamIdToSettings.end()) - { - streamSettings = new DirectDrawStreamSettings(); - _streamIdToSettings[streamId] = streamSettings; - } - else - { - streamSettings = it->second; - } - streamSettings->_cropStartWidth = startWidth; - streamSettings->_cropStopWidth = stopWidth; - streamSettings->_cropStartHeight = startHeight; - streamSettings->_cropStopHeight = stopHeight; -} - -int DirectDrawChannel::GetStreamSettings(VideoRenderDirectDraw* DDObj, - short streamId, float& startWidth, - float& startHeight, - float& stopWidth, - float& stopHeight) -{ - CriticalSectionScoped cs(*_critSect); - - unsigned long long lookupID = reinterpret_cast (DDObj); - lookupID &= 0xffffffffffffffe0; - lookupID <<= 11; - lookupID += streamId; - - DirectDrawStreamSettings* streamSettings = NULL; - std::map::iterator it = - _streamIdToSettings.find(lookupID); - if (it == _streamIdToSettings.end()) - { - // Didn't find this stream... - return -1; - } - streamSettings = it->second; - startWidth = streamSettings->_startWidth; - startHeight = streamSettings->_startHeight; - stopWidth = streamSettings->_stopWidth; - stopHeight = streamSettings->_stopHeight; - - return 0; -} - -bool DirectDrawChannel::IsOffScreenSurfaceUpdated(VideoRenderDirectDraw* DDobj) -{ - CriticalSectionScoped cs(*_critSect); - return _offScreenSurfaceUpdated; -} - -void DirectDrawChannel::GetLargestSize(RECT* mixingRect) -{ - CriticalSectionScoped cs(*_critSect); - if (mixingRect) - { - if (mixingRect->bottom < _height) - { - mixingRect->bottom = _height; - } - if (mixingRect->right < _width) - { - mixingRect->right = _width; - } - } -} - -int DirectDrawChannel::ChangeDeliverColorFormat(bool useScreenType) -{ - _deliverInScreenType = useScreenType; - return FrameSizeChange(0, 0, 0); -} - -WebRtc_Word32 DirectDrawChannel::RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) -{ - CriticalSectionScoped cs(*_critSect); - if (_width != videoFrame.Width() || _height != videoFrame.Height()) - { - if (FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) - { - return -1; - } - } - return DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), - videoFrame.TimeStamp()); -} - -int DirectDrawChannel::FrameSizeChange(int width, int height, - int numberOfStreams) -{ - CriticalSectionScoped cs(*_critSect); - - if (_directDraw == NULL) - { - return -1; // signal that we are not ready for the change - } - if (_width == width && _height == height && _offScreenSurface - && _offScreenSurfaceNext) - { - _numberOfStreams = numberOfStreams; - return 0; - } - if (_offScreenSurface) - { - _offScreenSurface->Release(); - _offScreenSurface = NULL; - } - if (_offScreenSurfaceNext) - { - _offScreenSurfaceNext->Release(); - _offScreenSurfaceNext = NULL; - } - if (width && height) - { - _width = width; - _height = height; - _numberOfStreams = numberOfStreams; - } - - // create this channels offscreen buffer - DirectDrawSurfaceDesc ddsd; - HRESULT ddrval = DD_OK; - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; - ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; - ddsd.dwHeight = _height; - ddsd.dwWidth = _width; - /* - char logStr[256]; - _snprintf(logStr,256, "offscreen H:%d W:%d \n",_height, _width); - OutputDebugString(logStr); - */ - //Fix for bad video driver on HP Mini. If it takes to long time to deliver a frame - try to blit using the same pixel format as used by the screen. - if (_deliverInScreenType && _screenVideoType != kUnknown) - { - //The HP mini netbook, which this fix for, uses the VIA processor. - //The measuring shows that this fix will impact systems with Intel processor, including Atom. - //So let's disable it here. If we really need this for VIA processor, we should have additional logic to detect - //the processor model. - //WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDrawChannel changing to screen video type"); - //_blitVideoType=_screenVideoType; - } - else - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "DirectDrawChannel changing to originial blit video type %d", - _originalBlitVideoType); - _blitVideoType = _originalBlitVideoType; - } - - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "DirectDrawChannel::FrameSizeChange height %d, width %d, _blitVideoType %d", - ddsd.dwHeight, ddsd.dwWidth, _blitVideoType); - switch (_blitVideoType) - { - case kYV12: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y', 'V', '1', '2'); - } - break; - case kYUY2: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y', 'U', 'Y', '2'); - } - break; - case kUYVY: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('U', 'Y', 'V', 'Y'); - } - break; - case kIYUV: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('I', 'Y', 'U', 'V'); - } - break; - case kARGB: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 32; - ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; - ddsd.ddpfPixelFormat.dwGBitMask = 0xff00; - ddsd.ddpfPixelFormat.dwBBitMask = 0xff; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - } - break; - case kRGB24: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 24; - ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; - ddsd.ddpfPixelFormat.dwGBitMask = 0xff00; - ddsd.ddpfPixelFormat.dwBBitMask = 0xff; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - } - break; - case kRGB565: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 16; - ddsd.ddpfPixelFormat.dwRBitMask = 0x0000F800; - ddsd.ddpfPixelFormat.dwGBitMask = 0x000007e0; - ddsd.ddpfPixelFormat.dwBBitMask = 0x0000001F; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - } - break; - case kARGB4444: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 16; - ddsd.ddpfPixelFormat.dwRBitMask = 0x00000f00; - ddsd.ddpfPixelFormat.dwGBitMask = 0x000000f0; - ddsd.ddpfPixelFormat.dwBBitMask = 0x0000000f; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - break; - } - case kARGB1555: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 16; - ddsd.ddpfPixelFormat.dwRBitMask = 0x00007C00; - ddsd.ddpfPixelFormat.dwGBitMask = 0x3E0; - ddsd.ddpfPixelFormat.dwBBitMask = 0x1F; - ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0; - break; - } - case kI420: - { - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('I', '4', '2', '0'); - } - break; - default: - ddrval = S_FALSE; - } - - if (ddrval == DD_OK) - { - if (!_owner->IsPrimaryOrMixingSurfaceOnSystem()) - { - ddrval - = _directDraw->CreateSurface(&ddsd, &_offScreenSurface, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "CreateSurface failed for _offScreenSurface on VideoMemory, trying on System Memory"); - - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; - - ddsd.dwHeight = _height; - ddsd.dwWidth = _width; - - ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; - _blitVideoType = kARGB; - - ddrval = _directDraw->CreateSurface(&ddsd, &_offScreenSurface, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _offScreenSurface using SystemMemory: 0x%x", - ddrval); - } - ddrval = _directDraw->CreateSurface(&ddsd, - &_offScreenSurfaceNext, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x", - ddrval); - } - } - else - { - ddrval = _directDraw->CreateSurface(&ddsd, - &_offScreenSurfaceNext, - NULL); - if (ddrval == DDERR_OUTOFVIDEOMEMORY) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "CreateSurface failed for _offScreenSurfaceNext on VideoMemory, trying on System Memory"); - - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; - - ddsd.dwHeight = _height; - ddsd.dwWidth = _width; - - ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; - _blitVideoType = kARGB; - - ddrval = _directDraw->CreateSurface(&ddsd, - &_offScreenSurfaceNext, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x", - ddrval); - } - } - } - } - else - { - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; - - ddsd.dwHeight = _height; - ddsd.dwWidth = _width; - - ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; - if (_owner->CanBltFourCC()) - { - _blitVideoType = kARGB; - } - else - { - _blitVideoType = _originalBlitVideoType; - } - - ddrval - = _directDraw->CreateSurface(&ddsd, &_offScreenSurface, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _offScreenSurface using SystemMemory: 0x%x", - ddrval); - } - - ddrval = _directDraw->CreateSurface(&ddsd, &_offScreenSurfaceNext, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x", - ddrval); - } - } - } - - if (FAILED(ddrval)) - { - // failed to change size - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to CreateSurface : 0x%x", ddrval); - return -1; - } - - return 0; -} - -int DirectDrawChannel::DeliverFrame(unsigned char* buffer, int bufferSize, - unsigned int /*timeStamp90KHz*/) -{ - CriticalSectionScoped cs(*_critSect); - - if (CalcBufferSize(_incomingVideoType, _width, _height) - != bufferSize) - { - // sanity - return -1; - } - if (!_offScreenSurface || !_offScreenSurfaceNext) - { - if (_width && _height && _numberOfStreams) - { - // our surface was lost recreate it - FrameSizeChange(_width, _height, _numberOfStreams); - } - return -1; - } - if (_offScreenSurface->IsLost() == DDERR_SURFACELOST) - { - HRESULT ddrval = _offScreenSurface->Restore(); - if (ddrval != DD_OK) - { - // failed to restore our surface remove it and it will be re-created in next frame - _offScreenSurface->Release(); - _offScreenSurface = NULL; - _offScreenSurfaceNext->Release(); - _offScreenSurfaceNext = NULL; - return -1; - } - ddrval = _offScreenSurfaceNext->Restore(); - if (ddrval != DD_OK) - { - // failed to restore our surface remove it and it will be re-created in next frame - _offScreenSurface->Release(); - _offScreenSurface = NULL; - _offScreenSurfaceNext->Release(); - _offScreenSurfaceNext = NULL; - return -1; - } - } - _doubleBuffer = false; - - // check if _offScreenSurfaceUpdated is true - DirectDrawSurface* offScreenSurface = _offScreenSurface; - { - - if (_offScreenSurfaceUpdated) - { - // this frame is not yet rendered - offScreenSurface = _offScreenSurfaceNext; - _doubleBuffer = true; - } - } - - DirectDrawSurfaceDesc ddsd; - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - HRESULT ddrval = offScreenSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); - if (ddrval == DDERR_SURFACELOST) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDrawChannel::DeliverFrame offScreenSurface lost"); - ddrval = offScreenSurface->Restore(); - if (ddrval != DD_OK) - { - // failed to restore our surface remove it and it will be re-created in next frame - _offScreenSurface->Release(); - _offScreenSurface = NULL; - _offScreenSurfaceNext->Release(); - _offScreenSurfaceNext = NULL; - return -1; - } - return 0; - } - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDrawChannel::DeliverFrame failed to lock"); - // failed to lock our surface remove it and it will be re-created in next frame - _offScreenSurface->Release(); - _offScreenSurface = NULL; - _offScreenSurfaceNext->Release(); - _offScreenSurfaceNext = NULL; - return -1; - } - - unsigned char* ptr = (unsigned char*) ddsd.lpSurface; - // ddsd.lPitch; distance in bytes - - - switch (_incomingVideoType) - { - case kI420: - { - switch (_blitVideoType) - { - case kYUY2: - ConvertI420ToYUY2(buffer, ptr, _width, _height, - ddsd.lPitch); - break; - case kUYVY: - ConvertI420ToUYVY(buffer, ptr, _width, _height, - ddsd.lPitch); - break; - case kIYUV: // same as kYV12 - case kYV12: - ConvertI420ToYV12(buffer, ptr, _width, _height, - ddsd.lPitch); - break; - case kRGB24: - { - _tempRenderBuffer.VerifyAndAllocate(_width * _height * 3); - //unsigned char *ptrTempBuffer=_tempRenderBuffer.GetBuffer(); - unsigned char *ptrTempBuffer = _tempRenderBuffer.Buffer(); - //ConvertI420ToRGB24(buffer ,(int*) ptrTempBuffer, _width, _height); - ConvertI420ToRGB24(buffer, ptrTempBuffer, _width, - _height); - for (int i = 0; i < _height; i++) - { - memcpy(ptr, ptrTempBuffer, _width * 3); - ptrTempBuffer += _width * 3; - ptr += ddsd.lPitch; - } - break; - } - case kARGB: - ConvertI420ToARGB(buffer, ptr, _width, _height, - (ddsd.lPitch >> 2) - _width); - break; - case kARGB4444: - ConvertI420ToARGB4444(buffer, ptr, _width, _height, - (ddsd.lPitch >> 1) - _width); - break; - case kARGB1555: - ConvertI420ToARGB1555(buffer, ptr, _width, _height, - (ddsd.lPitch >> 1) - _width); - break; - case kRGB565: - { - _tempRenderBuffer.VerifyAndAllocate(_width * _height * 2); - //unsigned char *ptrTempBuffer=_tempRenderBuffer.GetBuffer(); - unsigned char *ptrTempBuffer = _tempRenderBuffer.Buffer(); - //ConvertI420ToRGB565(buffer ,(int*) ptrTempBuffer, _width, _height); - ConvertI420ToRGB565(buffer, ptrTempBuffer, _width, - _height); - ptr += ddsd.lPitch * (_height - 1); - for (int i = 0; i < _height; i++) - { - memcpy(ptr, ptrTempBuffer, _width * 2); - ptrTempBuffer += _width * 2; - ptr -= ddsd.lPitch; - } - break; - } - default: - assert(!"DirectDrawChannel::DeliverFrame unknown blitVideoType"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDrawChannel::DeliverFrame unknown blitVideoType %d", - _blitVideoType); - } - break; - } - default: - assert(!"DirectDrawChannel::DeliverFrame wrong incomming video type"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDrawChannel::DeliverFrame wrong incomming %d", - _incomingVideoType); - } - - _offScreenSurfaceUpdated = true; - offScreenSurface->Unlock(NULL); - return 0; -} - -int DirectDrawChannel::BlitFromOffscreenBufferToMixingBuffer( - VideoRenderDirectDraw* DDobj, - short streamID, - DirectDrawSurface* mixingSurface, - RECT &hwndRect, - bool demuxing) -{ - HRESULT ddrval; - RECT srcRect; - RECT dstRect; - DirectDrawStreamSettings* streamSettings = NULL; - unsigned long long lookupID = reinterpret_cast (DDobj); - lookupID &= 0xffffffffffffffe0; - lookupID <<= 11; - lookupID += streamID; - - CriticalSectionScoped cs(*_critSect); - - if (_offScreenSurface == NULL) - { - // The offscreen surface has been deleted but not restored yet - return 0; - } - if (mixingSurface == NULL) - { - // Not a valid input argument - return 0; - } - - std::map::iterator it = - _streamIdToSettings.find(lookupID); - if (it == _streamIdToSettings.end()) - { - // ignore this stream id - return 0; - } - streamSettings = it->second; - - int numberOfStreams = _numberOfStreams; - if (!demuxing) - { - numberOfStreams = 1; // treat as one stream if we only have one config - } - - switch (numberOfStreams) - { - case 0: - return 0; - case 1: - { - // no demux - if (streamID > 0) - return 0; - - ::SetRect(&srcRect, int(_width * streamSettings->_cropStartWidth), - int(_height * streamSettings->_cropStartHeight), - int(_width * streamSettings->_cropStopWidth), int(_height - * streamSettings->_cropStopHeight)); - - ::SetRect(&dstRect, int(hwndRect.right - * streamSettings->_startWidth), int(hwndRect.bottom - * streamSettings->_startHeight), int(hwndRect.right - * streamSettings->_stopWidth), int(hwndRect.bottom - * streamSettings->_stopHeight)); - } - break; - case 2: - case 3: - case 4: - // classic quadrant demux - { - int width = _width >> 1; - int height = _height >> 1; - ::SetRect(&srcRect, int(width * streamSettings->_cropStartWidth), - int(height * streamSettings->_cropStartHeight), int(width - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - - ::SetRect(&dstRect, int(hwndRect.right - * streamSettings->_startWidth), int(hwndRect.bottom - * streamSettings->_startHeight), int(hwndRect.right - * streamSettings->_stopWidth), int(hwndRect.bottom - * streamSettings->_stopHeight)); - - // stream id to select quadrant - if (streamID == 1) - { - ::OffsetRect(&srcRect, width, 0); - } - if (streamID == 2) - { - ::OffsetRect(&srcRect, 0, height); - } - if (streamID == 3) - { - ::OffsetRect(&srcRect, width, height); - } - } - break; - case 5: - case 6: - { - const int width = (_width / (3 * 16)) * 16; - const int widthMidCol = width + ((_width % (16 * 3)) / 16) * 16; - const int height = _height / (2 * 16) * 16; - if (streamID == 1 || streamID == 4) - { - ::SetRect(&srcRect, int(widthMidCol - * streamSettings->_cropStartWidth), int(height - * streamSettings->_cropStartHeight), int(widthMidCol - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - } - else - { - ::SetRect(&srcRect, - int(width * streamSettings->_cropStartWidth), - int(height * streamSettings->_cropStartHeight), - int(width * streamSettings->_cropStopWidth), - int(height * streamSettings->_cropStopHeight)); - } - ::SetRect(&dstRect, int(hwndRect.right - * streamSettings->_startWidth), int(hwndRect.bottom - * streamSettings->_startHeight), int(hwndRect.right - * streamSettings->_stopWidth), int(hwndRect.bottom - * streamSettings->_stopHeight)); - - // stream id to select quadrant - switch (streamID) - { - case 1: - ::OffsetRect(&srcRect, width, 0); - break; - case 2: - ::OffsetRect(&srcRect, width + widthMidCol, 0); - break; - case 3: - ::OffsetRect(&srcRect, 0, height); - break; - case 4: - ::OffsetRect(&srcRect, width, height); - break; - case 5: - ::OffsetRect(&srcRect, width + widthMidCol, height); - break; - } - } - break; - case 7: - case 8: - case 9: - - { - const int width = (_width / (3 * 16)) * 16; - const int widthMidCol = width + ((_width % (16 * 3)) / 16) * 16; - const int height = _height / (3 * 16) * 16; - const int heightMidRow = height + ((_height % (16 * 3)) / 16) * 16; - - ::SetRect(&dstRect, int(hwndRect.right - * streamSettings->_startWidth), int(hwndRect.bottom - * streamSettings->_startHeight), int(hwndRect.right - * streamSettings->_stopWidth), int(hwndRect.bottom - * streamSettings->_stopHeight)); - - switch (streamID) - { - case 0: - //Size - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), int(height - * streamSettings->_cropStartHeight), int(width - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - //Position - ::OffsetRect(&srcRect, 0, 0); - break; - case 1: - ::SetRect( - &srcRect, - int(widthMidCol * streamSettings->_cropStartWidth), - int(height * streamSettings->_cropStartHeight), - int(widthMidCol * streamSettings->_cropStopWidth), - int(height * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width, 0); - break; - case 2: - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), int(height - * streamSettings->_cropStartHeight), int(width - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width + widthMidCol, 0); - break; - case 3: - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), - int(heightMidRow - * streamSettings->_cropStartHeight), - int(width * streamSettings->_cropStopWidth), - int(heightMidRow - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, 0, height); - break; - case 4: - ::SetRect( - &srcRect, - int(widthMidCol * streamSettings->_cropStartWidth), - int(heightMidRow - * streamSettings->_cropStartHeight), - int(widthMidCol * streamSettings->_cropStopWidth), - int(heightMidRow - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width, height); - - break; - case 5: - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), - int(heightMidRow - * streamSettings->_cropStartHeight), - int(width * streamSettings->_cropStopWidth), - int(heightMidRow - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width + widthMidCol, height); - break; - case 6: - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), int(height - * streamSettings->_cropStartHeight), int(width - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, 0, height + heightMidRow); - break; - case 7: - ::SetRect( - &srcRect, - int(widthMidCol * streamSettings->_cropStartWidth), - int(height * streamSettings->_cropStartHeight), - int(widthMidCol * streamSettings->_cropStopWidth), - int(height * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width, height + heightMidRow); - break; - case 8: - ::SetRect(&srcRect, int(width - * streamSettings->_cropStartWidth), int(height - * streamSettings->_cropStartHeight), int(width - * streamSettings->_cropStopWidth), int(height - * streamSettings->_cropStopHeight)); - ::OffsetRect(&srcRect, width + widthMidCol, height - + heightMidRow); - break; - } - } - break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - default: - { - ::SetRect(&srcRect, int(_width * streamSettings->_cropStartWidth), - int(_height * streamSettings->_cropStartHeight), - int(_width * streamSettings->_cropStopWidth), int(_height - * streamSettings->_cropStopHeight)); - - ::SetRect(&dstRect, int(hwndRect.right - * streamSettings->_startWidth), int(hwndRect.bottom - * streamSettings->_startHeight), int(hwndRect.right - * streamSettings->_stopWidth), int(hwndRect.bottom - * streamSettings->_stopHeight)); - } - } - - if (dstRect.right > hwndRect.right) - { - srcRect.right -= (int) ((float) (srcRect.right - srcRect.left) - * ((float) (dstRect.right - hwndRect.right) - / (float) (dstRect.right - dstRect.left))); - dstRect.right = hwndRect.right; - } - if (dstRect.left < hwndRect.left) - { - srcRect.left += (int) ((float) (srcRect.right - srcRect.left) - * ((float) (hwndRect.left - dstRect.left) - / (float) (dstRect.right - dstRect.left))); - dstRect.left = hwndRect.left; - } - if (dstRect.bottom > hwndRect.bottom) - { - srcRect.bottom -= (int) ((float) (srcRect.bottom - srcRect.top) - * ((float) (dstRect.bottom - hwndRect.bottom) - / (float) (dstRect.bottom - dstRect.top))); - dstRect.bottom = hwndRect.bottom; - } - if (dstRect.top < hwndRect.top) - { - srcRect.top += (int) ((float) (srcRect.bottom - srcRect.top) - * ((float) (hwndRect.top - dstRect.top) - / (float) (dstRect.bottom - dstRect.top))); - dstRect.top = hwndRect.top; - } - - DDBLTFX ddbltfx; - ZeroMemory(&ddbltfx, sizeof(ddbltfx)); - ddbltfx.dwSize = sizeof(ddbltfx); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; - - // wait for the _mixingSurface to be available - ddrval = mixingSurface->Blt(&dstRect, _offScreenSurface, &srcRect, - DDBLT_WAIT | DDBLT_DDFX, &ddbltfx); - if (ddrval == DDERR_SURFACELOST) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "mixingSurface->Blt surface lost"); - ddrval = mixingSurface->Restore(); - if (ddrval != DD_OK) - { - // we dont own the surface just report the error - return -1; - } - } - else if (ddrval == DDERR_INVALIDRECT) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "mixingSurface->Blt DDERR_INVALIDRECT"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "dstRect co-ordinates - top: %d left: %d bottom: %d right: %d", - dstRect.top, dstRect.left, dstRect.bottom, dstRect.right); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "srcRect co-ordinates - top: %d left: %d bottom: %d right: %d", - srcRect.top, srcRect.left, srcRect.bottom, srcRect.right); - - // ignore - } - else if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "mixingSurface->Blt !DD_OK"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw blt mixingSurface BlitFromOffscreenBufferToMixingBuffer error 0x%x ", - ddrval); - - //logging the co-ordinates and hwnd - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "dstRect co-ordinates - top: %d left: %d bottom: %d right: %d", - dstRect.top, dstRect.left, dstRect.bottom, dstRect.right); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "srcRect co-ordinates - top: %d left: %d bottom: %d right: %d", - srcRect.top, srcRect.left, srcRect.bottom, srcRect.right); - - /* char logStr[256]; - _snprintf(logStr,256, "srcRect T:%d L:%d B:%d R:%d\n",srcRect.top, srcRect.left, srcRect.bottom, srcRect.right); - OutputDebugString(logStr); - char logStr1[256]; - _snprintf(logStr1,256, "dstRect T:%d L:%d B:%d R:%d\n",dstRect.top, dstRect.left, dstRect.bottom, dstRect.right); - OutputDebugString(logStr1); - char logStr2[256]; - _snprintf(logStr2,256, "error 0x:%x \n",ddrval); - OutputDebugString(logStr2); - */ - // we dont own the surface just report the error - return -1; - } - if (_doubleBuffer) - { - DirectDrawSurface* oldOffScreenSurface = _offScreenSurface; - _offScreenSurface = _offScreenSurfaceNext; - _offScreenSurfaceNext = oldOffScreenSurface; - _doubleBuffer = false; - } - else - { - _offScreenSurfaceUpdated = false; - } - return 0; -} - -/** - * - * VideoRenderDirectDraw - * - * - */ - -VideoRenderDirectDraw::VideoRenderDirectDraw(Trace* trace, - HWND hWnd, bool fullscreen) : - _trace(trace), - _confCritSect(CriticalSectionWrapper::CreateCriticalSection()), - _fullscreen(fullscreen), - _demuxing(false), - _transparentBackground(false), - _supportTransparency(false), - _canStretch(false), - _canMirrorLeftRight(false), - _clearMixingSurface(false), - _deliverInScreenType(false), - _renderModeWaitForCorrectScanLine(false), - _deliverInHalfFrameRate(false), - _deliverInQuarterFrameRate(false), - _bCanBltFourcc(true), - _frameChanged(false), - _processCount(0), - _hWnd(hWnd), - _screenRect(), - _mixingRect(), - - _incomingVideoType(kUnknown), - _blitVideoType(kUnknown), - _rgbVideoType(kUnknown), - - _directDraw(NULL), - _primarySurface(NULL), - _backSurface(NULL), - _mixingSurface(NULL), - _bitmapSettings(), - _textSettings(), - _directDrawChannels(), - _directDrawZorder(), - - _fullScreenWaitEvent(EventWrapper::Create()), - _screenEvent(EventWrapper::Create()), - _screenRenderThread( - ThreadWrapper::CreateThread( - RemoteRenderingThreadProc, - this, - kRealtimePriority, - "Video_directdraw_thread")), - _blit(true), _lastRenderModeCpuUsage(-1), _totalMemory(-1), - _availableMemory(-1), _systemCPUUsage(0), _maxAllowedRenderTime(0), - _nrOfTooLongRenderTimes(0), - _isPrimaryOrMixingSurfaceOnSystem(false) -{ - SetRect(&_screenRect, 0, 0, 0, 0); - SetRect(&_mixingRect, 0, 0, 0, 0); - SetRect(&_originalHwndRect, 0, 0, 0, 0); - ::GetClientRect(_hWnd, &_hwndRect); -} - -VideoRenderDirectDraw::~VideoRenderDirectDraw() -{ - ThreadWrapper* temp = _screenRenderThread; - _screenRenderThread = NULL; - if (temp) - { - temp->SetNotAlive(); - _screenEvent->Set(); - _screenEvent->StopTimer(); - _fullScreenWaitEvent->StopTimer(); - - if (temp->Stop()) - { - delete temp; - } - } - delete _screenEvent; - delete _fullScreenWaitEvent; - - std::map::iterator it; - it = _directDrawChannels.begin(); - while (it != _directDrawChannels.end()) - { - it->second->Release(); - it = _directDrawChannels.erase(it); - } - if (_primarySurface) - { - _primarySurface->Release(); - } - if (_mixingSurface) - { - _mixingSurface->Release(); - } - - std::map::iterator bitIt; - - bitIt = _bitmapSettings.begin(); - while (_bitmapSettings.end() != bitIt) - { - delete bitIt->second; - bitIt = _bitmapSettings.erase(bitIt); - } - - std::map::iterator textIt; - textIt = _textSettings.begin(); - while (_textSettings.end() != textIt) - { - delete textIt->second; - textIt = _textSettings.erase(textIt); - } - if (_directDraw) - { - _directDraw->Release(); - if (_fullscreen) - { - // restore hwnd to original size and position - ::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left, - _originalHwndRect.top, _originalHwndRect.right - - _originalHwndRect.left, - _originalHwndRect.bottom - _originalHwndRect.top, - SWP_FRAMECHANGED); - ::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW - | RDW_ERASE); - ::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW - | RDW_ERASE); - } - } - delete _confCritSect; -} - -WebRtc_Word32 VideoRenderDirectDraw::Init() -{ - int retVal = 0; - HRESULT ddrval = DirectDrawCreateEx(NULL, (void**) &_directDraw, - IID_IDirectDraw7, NULL); - if (FAILED(ddrval) || NULL == _directDraw) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Failed to created DirectDraw7 object"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - retVal = CheckCapabilities(); - if (retVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw CheckCapabilities failed"); - return retVal; - } - if (_hWnd) - { - retVal = CreatePrimarySurface(); - if (retVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to CreatePrimarySurface"); - return retVal; - } - retVal = CreateMixingSurface(); - if (retVal != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to CreateMixingSurface"); - return retVal; - } - if (_screenRenderThread) - { - unsigned int tid; - _screenRenderThread->Start(tid); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Screen Render thread started, thread id: %d", tid); - } - DWORD freq = 0; - _directDraw->GetMonitorFrequency(&freq); - if (freq == 0) - { - freq = 60; - } - // Do this now to not do it in each render process loop - _maxAllowedRenderTime = (int) (1000 / freq * 0.8F); - _nrOfTooLongRenderTimes = 0; - - _screenEvent->StartTimer(true, 1000 / freq); - - _deliverInScreenType = false; - _renderModeWaitForCorrectScanLine = false; - _deliverInHalfFrameRate = false; - _deliverInQuarterFrameRate = false; - - _lastRenderModeCpuUsage = -1; - if (_fullscreen) - { - _fullScreenWaitEvent->StartTimer(true, 1); - } - - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Screen freq %d", freq); - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Created DirectDraw object"); - return 0; -} - -WebRtc_Word32 VideoRenderDirectDraw::GetGraphicsMemory( - WebRtc_UWord64& totalMemory, - WebRtc_UWord64& availableMemory) -{ - CriticalSectionScoped cs(*_confCritSect); - - if (_totalMemory == -1 || _availableMemory == -1) - { - totalMemory = 0; - availableMemory = 0; - return -1; - } - totalMemory = _totalMemory; - availableMemory = _availableMemory; - return 0; -} - -int VideoRenderDirectDraw::GetScreenResolution(int& screenWidth, - int& screenHeight) -{ - CriticalSectionScoped cs(*_confCritSect); - - screenWidth = _screenRect.right - _screenRect.left; - screenHeight = _screenRect.bottom - _screenRect.top; - return 0; -} - -int VideoRenderDirectDraw::UpdateSystemCPUUsage(int systemCPU) -{ - CriticalSectionScoped cs(*_confCritSect); - if (systemCPU <= 100 && systemCPU >= 0) - { - _systemCPUUsage = systemCPU; - } - return 0; -} - -int VideoRenderDirectDraw::CheckCapabilities() -{ - HRESULT ddrval = DD_OK; - DDCAPS ddcaps; - DDCAPS ddcapsEmul; - memset(&ddcaps, 0, sizeof(ddcaps)); - memset(&ddcapsEmul, 0, sizeof(ddcapsEmul)); - ddcaps.dwSize = sizeof(ddcaps); - ddcapsEmul.dwSize = sizeof(ddcapsEmul); - if (_directDraw == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw object not created"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (IsRectEmpty(&_screenRect)) - { - ::GetWindowRect(GetDesktopWindow(), &_screenRect); - } - // Log Screen resolution - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "ScreenRect. Top: %d, left: %d, bottom: %d, right: %d", - _screenRect.top, _screenRect.left, _screenRect.bottom, - _screenRect.right); - - bool fullAccelerationEnabled = false; - bool badDriver = false; - VideoRenderWindowsImpl::CheckHWDriver(badDriver, fullAccelerationEnabled); - if (!fullAccelerationEnabled) - { - - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "Direct draw Hardware acceleration is not enabled."); - return -1; - //return VIDEO_DIRECT_DRAW_HWACC_NOT_ENABLED; - - } - - // ddcaps supported by the HW - // ddcapsEmul supported by the OS emulating the HW - ddrval = _directDraw->GetCaps(&ddcaps, &ddcapsEmul); - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw HW: could not get capabilities: %x", ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - unsigned int minVideoMemory = 3 * 4 * (_screenRect.right - * _screenRect.bottom); // assuming ARGB size (4 bytes) - - // Store the memory for possible calls to GetMemory() - _totalMemory = ddcaps.dwVidMemTotal; - _availableMemory = ddcaps.dwVidMemFree; - - if (ddcaps.dwVidMemFree < minVideoMemory) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw HW does not have enough memory, freeMem:%d, requiredMem:%d", - ddcaps.dwVidMemFree, minVideoMemory); - // If memory is not available on the Video Card...allocate it on RAM - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw video memory, freeMem:%d, totalMem:%d", - ddcaps.dwVidMemFree, ddcaps.dwVidMemTotal); - } - - /* - DirectDrawCaps ddsCaps ; - ZeroMemory(&ddsCaps, sizeof(ddsCaps)) ; - ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; - DWORD memTotal=0; - DWORD memFree=0; - ddrval = _directDraw->GetAvailableVidMem(&ddsCaps, &memTotal, &memFree); - if(ddrval == DD_OK) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDraw video memory, freeMem:%d, totalMem:%d", memFree, memTotal); - } - */ - // Determine if the hardware supports overlay deinterlacing - // bCanDeinterlace = (ddcaps.dwCaps2 & DDCAPS2_CANFLIPODDEVEN) ? 1 : 0; - - // this fail since we check before we set the mode - // bool bCanFlip =(ddcaps.dwCaps & DDSCAPS_FLIP) ? 1 : 0; - - // Determine if the hardware supports colorkeying - _supportTransparency = (ddcaps.dwCaps & DDCAPS_COLORKEY) ? 1 : 0; - if (_supportTransparency) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw support colorkey"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw don't support colorkey"); - } - - if (ddcaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED) - { - // required for _directDraw->FlipToGDISurface(); - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw support CANRENDERWINDOWED"); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw don't support CANRENDERWINDOWED"); - } - - // Determine if the hardware supports scaling during a blit - _canStretch = (ddcaps.dwCaps & DDCAPS_BLTSTRETCH) ? 1 : 0; - if (_canStretch) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw blit can stretch"); - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw blit can't stretch"); - } - - _canMirrorLeftRight = (ddcaps.dwFXAlphaCaps & DDBLTFX_MIRRORLEFTRIGHT) ? 1 - : 0; - if (_canMirrorLeftRight) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw mirroring is supported"); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw mirroring is not supported"); - } - - // Determine if the hardware supports color conversion during a blit - _bCanBltFourcc = (ddcaps.dwCaps & DDCAPS_BLTFOURCC) ? 1 : 0; - if (_bCanBltFourcc) - _bCanBltFourcc = (ddcaps.dwCKeyCaps & DDCKEYCAPS_DESTBLT) ? 1 : 0; - - if (_bCanBltFourcc) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw can blit Fourcc"); - DWORD i_codes; - ddrval = _directDraw->GetFourCCCodes(&i_codes, NULL); - - if (i_codes > 0) - { - DWORD* pi_codes = new DWORD[i_codes]; - - ddrval = _directDraw->GetFourCCCodes(&i_codes, pi_codes); - for (unsigned int i = 0; i < i_codes && _blitVideoType - != kI420; i++) - { - DWORD w = pi_codes[i]; - switch (w) - { - case MAKEFOURCC('I', '4', '2', '0'): - // _blitVideoType = kI420; - // not enabled since its not tested - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support I420"); - break; - case MAKEFOURCC('I', 'Y', 'U', 'V'): // same as YV12 - // _blitVideoType = kIYUV; - // not enabled since its not tested - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support IYUV"); - break; - case MAKEFOURCC('U', 'Y', 'N', 'V'): // same shit different name - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support UYNV"); - // not enabled since its not tested - break; - case MAKEFOURCC('Y', '4', '2', '2'): // same shit different name - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support Y422"); - // not enabled since its not tested - break; - case MAKEFOURCC('Y', 'U', 'N', 'V'): // same shit different name - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support YUNV"); - // not enabled since its not tested - break; - case MAKEFOURCC('Y', 'V', '1', '2'): - _blitVideoType = kYV12; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support YV12"); - break; - case MAKEFOURCC('Y', 'U', 'Y', '2'): - if (_blitVideoType != kYV12) - { - _blitVideoType = kYUY2; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support YUY2"); - break; - case MAKEFOURCC('U', 'Y', 'V', 'Y'): - if (_blitVideoType != kYV12) - { - _blitVideoType = kUYVY; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw support UYVY"); - break; - default: - WEBRTC_TRACE(kTraceInfo, kTraceVideo, - -1, "DirectDraw unknown blit type %x", w); - break; - } - } - delete[] pi_codes; - } - } - return 0; -} - -int VideoRenderDirectDraw::Stop() -{ - _confCritSect->Enter(); - - _blit = false; - - _confCritSect->Leave(); - return 0; -} - -bool VideoRenderDirectDraw::IsPrimaryOrMixingSurfaceOnSystem() -{ - return _isPrimaryOrMixingSurfaceOnSystem; -} - -int VideoRenderDirectDraw::CreatePrimarySurface() -{ - // Create the primary surface - DirectDrawSurfaceDesc ddsd; - ZeroMemory(&ddsd, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - HRESULT ddrval = DD_OK; - - if (_directDraw == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw object not created"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (_primarySurface) - { - _primarySurface->Release(); - _primarySurface = NULL; - } - - if (!_fullscreen) - { - // create a normal window - ddrval = _directDraw->SetCooperativeLevel(_hWnd, DDSCL_NORMAL); - if (FAILED(ddrval)) - { - //******** Potential workaround for D#4608 *************** Ignore error. - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw failed to set SetCooperativeLevel %x, ddrval"); - } - // we cant size the primary surface based on _hwndRect - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY; - -#ifndef NOGRAPHICSCARD_MEMORY - ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _primarySurface using VideoMemory: 0x%x", - ddrval); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d", - _hWnd, _hwndRect.top, _hwndRect.left, - _hwndRect.bottom, _hwndRect.right, ddsd.dwFlags, - __LINE__); - -#endif - //allocate using System memory - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_SYSTEMMEMORY; - ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _primarySurface using SystemMemory: 0x%x", - ddrval); - if (ddrval != 0x887600E1) - { - _directDraw->Release(); - _directDraw = 0; - } - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - _isPrimaryOrMixingSurfaceOnSystem = true; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw _primarySurface on SystemMemory"); - -#ifndef NOGRAPHICSCARD_MEMORY - } -#endif - - // Create a clipper to ensure that our drawing stays inside our window - LPDIRECTDRAWCLIPPER directDrawClipper; - ddrval = _directDraw->CreateClipper(0, &directDrawClipper, NULL ); - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to CreateClipper"); - _primarySurface->Release(); - _directDraw->Release(); - _primarySurface = 0; - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // setting it to our hwnd gives the clipper the coordinates from our window - // when using cliplist we run into problem with transparent HWNDs (such as REX) - ddrval = directDrawClipper->SetHWnd(0, _hWnd); - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetHWnd"); - _primarySurface->Release(); - _directDraw->Release(); - _primarySurface = 0; - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // attach the clipper to the primary surface - ddrval = _primarySurface->SetClipper(directDrawClipper); - directDrawClipper->Release(); // no need to keep the clipper around - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetClipper"); - _primarySurface->Release(); - _directDraw->Release(); - _primarySurface = 0; - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - } - else - { - /* The cooperative level determines how much control we have over the - * screen. This must at least be either DDSCL_EXCLUSIVE or DDSCL_NORMAL - * - * DDSCL_EXCLUSIVE allows us to change video modes, and requires - * the DDSCL_FULLSCREEN flag, which will cause the window to take over - * the fullscreen. This is the preferred DirectDraw mode because it allows - * us to have control of the whole screen without regard for GDI. - * - * DDSCL_NORMAL is used to allow the DirectDraw app to run windowed. - */ - - // Note: debuging in fullscreen mode does not work, thanks MS... - ::GetWindowRect(_hWnd, &_originalHwndRect); - - // DDSCL_NOWINDOWCHANGES prevents DD to change the window but it give us trouble too, not using it - ddrval = _directDraw->SetCooperativeLevel(_hWnd, DDSCL_EXCLUSIVE - | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT); - - if (FAILED(ddrval)) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetCooperativeLevel DDSCL_EXCLUSIVE"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d", - _hWnd, _hwndRect.top, _hwndRect.left, - _hwndRect.bottom, _hwndRect.right, ddsd.dwFlags, - __LINE__); - - _directDraw->Release(); - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP - | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY; - ddsd.dwBackBufferCount = 1; - - ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _primarySurface, fullscreen mode: 0x%x", - ddrval); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d", - _hWnd, _hwndRect.top, _hwndRect.left, - _hwndRect.bottom, _hwndRect.right, ddsd.dwFlags, - __LINE__); - - _directDraw->Release(); - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // Get a pointer to the back buffer - DirectDrawCaps ddsCaps; - ZeroMemory(&ddsCaps, sizeof(ddsCaps)); - ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_VIDEOMEMORY; - - ddrval = _primarySurface->GetAttachedSurface(&ddsCaps, &_backSurface); - if (FAILED(ddrval)) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to GetAttachedSurface, fullscreen mode "); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d", - _hWnd, _hwndRect.top, _hwndRect.left, - _hwndRect.bottom, _hwndRect.right, ddsd.dwFlags, - __LINE__); - - _primarySurface->Release(); - _directDraw->Release(); - _primarySurface = 0; - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // Get the screen size and save it as a rect - ZeroMemory(&ddsd, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - } - - ZeroMemory(&ddsd, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - - // get our prinmary surface description - ddrval = _primarySurface->GetSurfaceDesc(&ddsd); - if (!(SUCCEEDED(ddrval) && (ddsd.dwFlags & DDSD_WIDTH) && (ddsd.dwFlags - & DDSD_HEIGHT))) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to GetSurfaceDesc _primarySurface"); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d", - _hWnd, _hwndRect.top, _hwndRect.left, _hwndRect.bottom, - _hwndRect.right, ddsd.dwFlags, __LINE__); - - _primarySurface->Release(); - _directDraw->Release(); - _primarySurface = 0; - _directDraw = 0; - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // first we need to figure out the size of the primary surface - - // store screen size - ::SetRect(&_screenRect, 0, 0, ddsd.dwWidth, ddsd.dwHeight); - - // store RGB type - if (ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB) - { - // RGB surface - switch (ddsd.ddpfPixelFormat.dwRGBBitCount) - { - case 16: - switch (ddsd.ddpfPixelFormat.dwGBitMask) - { - case 0x00e0: - _rgbVideoType = kARGB4444; - break; - case 0x03e0: - _rgbVideoType = kARGB1555; - break; - case 0x07e0: - _rgbVideoType = kRGB565; - break; - } - break; - case 24: - _rgbVideoType = kRGB24; - break; - case 32: - _rgbVideoType = kARGB; - break; - } - } - switch (_blitVideoType) - { - case kI420: - case kIYUV: - case kYUY2: - case kYV12: - case kUYVY: - _incomingVideoType = kI420; - break; - case kUnknown: - _blitVideoType = _rgbVideoType; - _incomingVideoType = kI420; - break; - default: - _blitVideoType = _rgbVideoType; - _incomingVideoType = kI420; - break; - } - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "DirectDraw created _primarySurface, _blitVideoType %d, _rgbvideoType %d", - _blitVideoType, _rgbVideoType); - return 0; -} - -int VideoRenderDirectDraw::CreateMixingSurface() -{ - if (_directDraw == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw object not created"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - if (_fullscreen) - { - ::CopyRect(&_hwndRect, &_screenRect); - } - else - { - // update our _hWnd size - ::GetClientRect(_hWnd, &_hwndRect); - } - - if (_mixingSurface) - { - _mixingSurface->Release(); - _mixingSurface = NULL; - } - // create mixing surface - DirectDrawSurfaceDesc ddsd; - memset(&ddsd, 0, sizeof(ddsd)); - ddsd.dwSize = sizeof(ddsd); - ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; - ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; - ddsd.dwHeight = _hwndRect.bottom; - ddsd.dwWidth = _hwndRect.right; - - /* char logStr[256]; - _snprintf(logStr,256, "CreateMixingSurface H:%d W:%d \n",_hwndRect.bottom, _hwndRect.right); - OutputDebugString(logStr); - */ - -#ifndef NOGRAPHICSCARD_MEMORY - HRESULT ddrval = _directDraw->CreateSurface(&ddsd, &_mixingSurface, NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _mixingSurface using VideoMemory: 0x%x", - ddrval); - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d", - _hWnd, _hwndRect.top, _hwndRect.left, _hwndRect.bottom, - _hwndRect.right, ddsd.dwFlags); -#endif - - ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; - HRESULT ddrval = _directDraw->CreateSurface(&ddsd, &_mixingSurface, - NULL); - if (FAILED(ddrval)) - { - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to CreateSurface _mixingSurface on System Memory: 0x%x", - ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - _isPrimaryOrMixingSurfaceOnSystem = true; - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw CreateSurface _mixingSurface on SystemMemory"); - -#ifndef NOGRAPHICSCARD_MEMORY - } -#endif - - _clearMixingSurface = true; - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw _mixingSurface created"); - return 0; -} - -VideoRenderCallback* VideoRenderDirectDraw::CreateChannel(WebRtc_UWord32 channel, - WebRtc_UWord32 zOrder, - float startWidth, - float startHeight, - float stopWidth, - float stopHeight) -{ - if (!_canStretch) - { - if (startWidth != 0.0f || startHeight != 0.0f || stopWidth != 1.0f - || stopHeight != 1.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to CreateChannel HW don't support stretch"); - return NULL; - } - } - DirectDrawChannel* ddobj = - new DirectDrawChannel(_directDraw, _blitVideoType, - _incomingVideoType, _rgbVideoType, this); - ddobj->SetStreamSettings(this, 0, startWidth, startHeight, stopWidth, - stopHeight); - - // store channel - _directDrawChannels[channel & 0x0000ffff] = ddobj; - - // store Z order - // default streamID is 0 - _directDrawZorder.insert(ZorderPair(zOrder, channel & 0x0000ffff)); - return ddobj; -} - -int VideoRenderDirectDraw::AddDirectDrawChannel(int channel, - unsigned char streamID, - int zOrder, - DirectDrawChannel* ddObj) -{ - // Only allow one stream per channel, demuxing is done outside of DirectDraw... - streamID = 0; - unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff); - - // store channel - _directDrawChannels[channel & 0x0000ffff] = ddObj; - - _demuxing = true; // with this function it's always demux - - // store Z order - _directDrawZorder.insert(ZorderPair(zOrder, streamChannel)); - return 0; -} - -DirectDrawChannel* VideoRenderDirectDraw::ShareDirectDrawChannel( - int channel) -{ - CriticalSectionScoped cs(*_confCritSect); - - DirectDrawChannel* obj = NULL; - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - if (ddIt != _directDrawChannels.end()) - { - obj = ddIt->second; - obj->AddRef(); - } - return obj; -} - -WebRtc_Word32 VideoRenderDirectDraw::DeleteChannel(const WebRtc_UWord32 channel) -{ - CriticalSectionScoped cs(*_confCritSect); - - // Remove the old z order - - //unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff); - std::multimap::iterator it; - it = _directDrawZorder.begin(); - while (it != _directDrawZorder.end()) - { - //if(streamChannel == it->second ) - if ((channel & 0x0000ffff) == (it->second & 0x0000ffff)) - { - it = _directDrawZorder.erase(it); - break; - } - it++; - } - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - if (ddIt != _directDrawChannels.end()) - { - ddIt->second->Release(); - _directDrawChannels.erase(ddIt); - _clearMixingSurface = true; - } - - return 0; -} - -WebRtc_Word32 VideoRenderDirectDraw::GetStreamSettings(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& startWidth, - float& startHeight, - float& stopWidth, - float& stopHeight) -{ - CriticalSectionScoped cs(*_confCritSect); - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - if (ddIt == _directDrawChannels.end()) - { - // This channel doesn't exist. - return -1; - } - - DirectDrawChannel* ptrChannel = ddIt->second; - // Only support one stream per channel, is demuxing done outside if DD. - //if (ptrChannel->GetStreamSettings(this, streamId, startWidth, startHeight, stopWidth, stopHeight) == -1) - if (ptrChannel->GetStreamSettings(this, 0, startWidth, startHeight, - stopWidth, stopHeight) == -1) - { - // Error for this stream - return -1; - } - - // Get the zOrder - std::multimap::iterator it; - it = _directDrawZorder.begin(); - while (it != _directDrawZorder.end()) - { - if ((channel & 0x0000ffff) == (it->second & 0x0000ffff)) - { - // We found our channel zOrder - zOrder = (unsigned int) (it->first); - break; - } - it++; - } - - return 0; -} - -int VideoRenderDirectDraw::GetChannels(std::list& channelList) -{ - CriticalSectionScoped cs(*_confCritSect); - - std::map::iterator ddIt; - ddIt = _directDrawChannels.begin(); - - while (ddIt != _directDrawChannels.end()) - { - int channel = ddIt->first; - if (channel == 0x0000ffff) - { - channel = -1; - } - channelList.push_back(channel); - ddIt++; - } - return 0; -} - -bool VideoRenderDirectDraw::HasChannel(int channel) -{ - CriticalSectionScoped cs(*_confCritSect); - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - if (ddIt != _directDrawChannels.end()) - { - return true; - } - return false; -} - -bool VideoRenderDirectDraw::HasChannels() -{ - CriticalSectionScoped cs(*_confCritSect); - - if (_directDrawChannels.begin() != _directDrawChannels.end()) - { - return true; - } - return false; -} - -bool VideoRenderDirectDraw::IsFullScreen() -{ - return _fullscreen; -} - -VideoType VideoRenderDirectDraw::GetPerferedVideoFormat() -{ - return _incomingVideoType; -} - -// this can be called rutime from another thread -DirectDrawChannel* VideoRenderDirectDraw::ConfigureDirectDrawChannel(int channel, - unsigned char streamID, - int zOrder, - float left, - float top, - float right, - float bottom) -{ - // Only support one stream per channel, is demuxing done outside if DD. - streamID = 0; - - CriticalSectionScoped cs(*_confCritSect); - - if (!_canStretch) - { - if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to ConfigureDirectDrawChannel HW don't support stretch"); - return NULL; - } - } - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - DirectDrawChannel* ddobj = NULL; - if (ddIt != _directDrawChannels.end()) - { - ddobj = ddIt->second; - } - if (ddobj == NULL) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "DirectDraw failed to find channel"); - return NULL; - } - unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff); - // remove the old z order - std::multimap::iterator it; - it = _directDrawZorder.begin(); - while (it != _directDrawZorder.end()) - { - if (streamChannel == it->second) - { - it = _directDrawZorder.erase(it); - break; - } - it++; - } - // if this channel already are in the zOrder map it's demux - it = _directDrawZorder.begin(); - while (it != _directDrawZorder.end()) - { - if (channel == (it->second & 0x0000ffff)) - { - _demuxing = true; - break; - } - it++; - } - if (it == _directDrawZorder.end()) - { - _demuxing = false; - } - - _clearMixingSurface = true; - - if (left == 0.0f && top == 0.0f && right == 0.0f && bottom == 0.0f) - { - // remove - _directDrawChannels.erase(ddIt); - ddobj->Release(); - return NULL; - } - ddobj->SetStreamSettings(this, streamID, left, top, right, bottom); - - _directDrawZorder.insert(ZorderPair(zOrder, streamChannel)); - return ddobj; -} - -WebRtc_Word32 VideoRenderDirectDraw::SetCropping(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamID, - float left, float top, - float right, float bottom) -{ - CriticalSectionScoped cs(*_confCritSect); - if (!_canStretch) - { - if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, - "DirectDraw failed to SetCropping HW don't support stretch"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - } - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel & 0x0000ffff); - if (ddIt != _directDrawChannels.end()) - { - DirectDrawChannel* ddobj = ddIt->second; - if (ddobj) - { - // Only support one stream per channel, is demuxing done outside if DD. - ddobj->SetStreamCropSettings(this, 0, left, top, right, bottom); - //ddobj->SetStreamCropSettings(this, streamID, left, top, right, bottom); - } - } - return 0; -} - -WebRtc_Word32 VideoRenderDirectDraw::ConfigureRenderer(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - if (ConfigureDirectDrawChannel(channel, (unsigned char) streamId, zOrder, - left, top, right, bottom) == NULL) - { - if (left == 0.0f && top == 0.0f && right == 0.0f && bottom == 0.0f) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, - "ConfigureRender, removed channel:%d streamId:%d", - channel, streamId); - } - else - { - WEBRTC_TRACE( - kTraceError, - kTraceVideoRenderer, - -1, - "DirectDraw failed to ConfigureRenderer for channel: %d", - channel); - return -1; - } - } - return 0; -} - -// this can be called runtime from another thread -WebRtc_Word32 VideoRenderDirectDraw::SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 colorText, - const WebRtc_UWord32 colorBg, - const float left, - const float top, - const float right, - const float bottom) -{ - DirectDrawTextSettings* textSetting = NULL; - - CriticalSectionScoped cs(*_confCritSect); - - _frameChanged = true; - - std::map::iterator it; - it = _textSettings.find(textId); - if (it != _textSettings.end()) - { - if (it->second) - { - textSetting = it->second; - } - } - _clearMixingSurface = true; - - if (text == NULL || textLength == 0) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw remove text textId:%d", textId); - if (textSetting) - { - delete textSetting; - _textSettings.erase(it); - } - return 0; - } - - // sanity - if (left > 1.0f || left < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetText invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (top > 1.0f || top < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetText invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (right > 1.0f || right < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetText invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (bottom > 1.0f || bottom < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetText invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (textSetting == NULL) - { - textSetting = new DirectDrawTextSettings(); - } - int retVal = textSetting->SetText((const char*) text, textLength, - (COLORREF) colorText, (COLORREF) colorBg, - left, top, right, bottom); - if (retVal != 0) - { - delete textSetting; - textSetting = NULL; - _textSettings.erase(textId); - return retVal; - } - if (textSetting) - { - _textSettings[textId] = textSetting; - } - return retVal; -} - -// this can be called runtime from another thread -WebRtc_Word32 VideoRenderDirectDraw::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - DirectDrawBitmapSettings* bitmapSetting = NULL; - - CriticalSectionScoped cs(*_confCritSect); - - _frameChanged = true; - std::map::iterator it; - it = _bitmapSettings.find(pictureId); - if (it != _bitmapSettings.end()) - { - if (it->second) - { - bitmapSetting = it->second; - } - } - _clearMixingSurface = true; - - if (bitMap == NULL) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw remove bitmap pictureId:%d", pictureId); - if (bitmapSetting) - { - delete bitmapSetting; - _bitmapSettings.erase(it); - } - return 0; - } - - // sanity - if (left > 1.0f || left < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetBitmap invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (top > 1.0f || top < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetBitmap invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (right > 1.0f || right < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetBitmap invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (bottom > 1.0f || bottom < 0.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw SetBitmap invalid parameter"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (!_canStretch) - { - if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetBitmap HW don't support stretch"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - } - if (bitmapSetting == NULL) - { - bitmapSetting = new DirectDrawBitmapSettings(); - } - - bitmapSetting->_transparentBitMap = (HBITMAP) bitMap; - bitmapSetting->_transparentBitmapLeft = left; - bitmapSetting->_transparentBitmapRight = right; - bitmapSetting->_transparentBitmapTop = top; - bitmapSetting->_transparentBitmapBottom = bottom; - - // colorKey == NULL equals no transparency - if (colorKey) - { - // first remove constness - DDCOLORKEY* ddColorKey = - static_cast (const_cast (colorKey)); - if (!_supportTransparency) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to SetBitmap HW don't support transparency"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; - } - if (bitmapSetting->_transparentBitmapColorKey == NULL) - { - bitmapSetting->_transparentBitmapColorKey = new DDCOLORKEY(); - } - - if (ddColorKey) - { - bitmapSetting->_transparentBitmapColorKey->dwColorSpaceLowValue - = ddColorKey->dwColorSpaceLowValue; - bitmapSetting->_transparentBitmapColorKey->dwColorSpaceHighValue - = ddColorKey->dwColorSpaceHighValue; - } - } - int retval = bitmapSetting->SetBitmap(_trace, _directDraw); - if (retval != 0) - { - delete bitmapSetting; - bitmapSetting = NULL; - _bitmapSettings.erase(pictureId); - return retval; - } - if (bitmapSetting) - { - _bitmapSettings[pictureId] = bitmapSetting; - } - return retval; -} - -// this can be called rutime from another thread -WebRtc_Word32 VideoRenderDirectDraw::SetTransparentBackground( - const bool enable) -{ - CriticalSectionScoped cs(*_confCritSect); - - if (_supportTransparency) - { - _transparentBackground = enable; - if (enable) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw enabled TransparentBackground"); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw disabled TransparentBackground"); - } - return 0; - } - WEBRTC_TRACE( - kTraceError, - kTraceVideo, - -1, - "DirectDraw failed to EnableTransparentBackground HW don't support transparency"); - return -1; - //return VIDEO_DIRECT_DRAW_INVALID_ARG; -} - -int VideoRenderDirectDraw::FillSurface(DirectDrawSurface *pDDSurface, - RECT* rect) -{ - // sanity checks - if (NULL == pDDSurface) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (NULL == rect) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - - // Repaint the whole specified surface - HRESULT ddrval; - DDBLTFX ddFX; - - ZeroMemory(&ddFX, sizeof(ddFX)); - ddFX.dwSize = sizeof(ddFX); - ddFX.dwFillColor = RGB(0, 0, 0); - - // Draw color key on the video area of given surface - ddrval = pDDSurface->Blt(rect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, - &ddFX); - if (FAILED(ddrval)) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw failed to fill surface"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - return 0; -} - -// the real rendering thread -bool VideoRenderDirectDraw::RemoteRenderingThreadProc(void *obj) -{ - return static_cast (obj)->RemoteRenderingProcess(); -} - -bool VideoRenderDirectDraw::RemoteRenderingProcess() -{ - bool hwndChanged = false; - int waitTime = 0; - - _screenEvent->Wait(100); - - _confCritSect->Enter(); - - if (_blit == false) - { - _confCritSect->Leave(); - return true; - } - - if (!::GetForegroundWindow()) - { - //no window, i.e the user have clicked CTRL+ALT+DEL, return true and wait - _confCritSect->Leave(); - return true; - } - - // Skip to blit if last render to primare surface took too long time. - _processCount++; - if (_deliverInQuarterFrameRate) - { - if (_processCount % 4 != 0) - { - _confCritSect->Leave(); - return true; - } - } - else if (_deliverInHalfFrameRate) - { - if (_processCount % 2 != 0) - { - _confCritSect->Leave(); - return true; - } - } - - // Calculate th erender process time - unsigned int startProcessTime = timeGetTime(); - - hwndChanged = HasHWNDChanged(); - if (hwndChanged) - { - _clearMixingSurface = true; - } - - std::map::iterator it; - it = _directDrawChannels.begin(); - while (it != _directDrawChannels.end() && !_frameChanged) - { - if (it->second) - { - int channel = it->first; - _frameChanged = it->second->IsOffScreenSurfaceUpdated(this); - } - it++; - } - if (_backSurface) - { - if (hwndChanged || _frameChanged) - { - BlitFromOffscreenBuffersToMixingBuffer(); - BlitFromBitmapBuffersToMixingBuffer(); - BlitFromTextToMixingBuffer(); - } - BlitFromMixingBufferToBackBuffer(); - WaitAndFlip(waitTime); - } - else - { - if (hwndChanged || _frameChanged) - { - BlitFromOffscreenBuffersToMixingBuffer(); - BlitFromBitmapBuffersToMixingBuffer(); - BlitFromTextToMixingBuffer(); - } - BlitFromMixingBufferToFrontBuffer(hwndChanged, waitTime); - - } - // Check the total time it took processing all rendering. Don't consider waitTime. - //const int totalRenderTime=GET_TIME_IN_MS()- startProcessTime-waitTime; - const int totalRenderTime = ::timeGetTime() - startProcessTime - waitTime; - DecideBestRenderingMode(hwndChanged, totalRenderTime); - _frameChanged = false; - _confCritSect->Leave(); - - return true; -} -void VideoRenderDirectDraw::DecideBestRenderingMode(bool hwndChanged, - int totalRenderTime) -{ - /* Apply variuos fixes for bad graphic drivers. - 1. If cpu to high- test wait fix - 2. If cpu still too high render in 1/2 display update period. - 3. If RemoteRenderingProcess take to long time reduce the blit period to 1/2 display update period. - 4. If RemoteRenderingProcess still take to long time try color conversion fix. It do color conversion in VieoRenderDirectDrawChannel::DeliverFrame - 5. If RemoteRenderingProcess still take to long time reduce the blit period to 1/4 display update period and disable color conversion fix. - 6 if RemoteRenderingProcess still take to long time reduce the blit period to 1/4 display update period and enable color conversion fix again. - */ - - const int timesSinceLastCPUCheck = timeGetTime() - - _screenRenderCpuUsage.LastGetCpuTime(); - int cpu = 0; - - if (hwndChanged) // Render window changed. - { - cpu = _screenRenderCpuUsage.GetCpuUsage(); // Get CPU usage for this thread. (Called if hwndCanged just to reset the GET CPU Usage function) - _nrOfTooLongRenderTimes = 0; // Reset count of too long render times. - return; // Return - nothing more to do since the window has changed. - } - // Check total rendering times - if (_maxAllowedRenderTime > 0 && totalRenderTime > _maxAllowedRenderTime) - { - if (!_deliverInHalfFrameRate || totalRenderTime > 2 - * _maxAllowedRenderTime) - { - _nrOfTooLongRenderTimes += totalRenderTime / _maxAllowedRenderTime; //Weighted with the number of to long render times - } - } - - // If we are not using back surface (ie full screen rendering) we might try to switch BlitFromMixingBufferToFrontBuffer mode. - if (timesSinceLastCPUCheck > WindowsThreadCpuUsage::CPU_CHECK_INTERVAL) - { - cpu = _screenRenderCpuUsage.GetCpuUsage(); // Get CPU usage for this thread. (Called if hwndCanged just to reset the GET CPU Usage function) - WEBRTC_TRACE( - kTraceStream, - kTraceVideo, - -1, - "Screen render thread cpu usage. (Tid %d), cpu usage %d processTime %d, no of too long render times %d", - GetCurrentThreadId(), cpu, totalRenderTime, - _nrOfTooLongRenderTimes); - - // If this screen render thread uses more than 5% of the total CPU time and the - // 1. try waitFix - if (cpu >= 5 && _renderModeWaitForCorrectScanLine == false - && !_backSurface) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - -1, - "HIGH screen render thread cpu usage. (Tid %d), cpu usage %d, applying wait for scan line", - GetCurrentThreadId(), cpu); - _renderModeWaitForCorrectScanLine = true; - _fullScreenWaitEvent->StartTimer(true, 1); - } - else if (cpu >= 10 && _deliverInHalfFrameRate == false) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - -1, - "HIGH screen render thread cpu usage. (Tid %d), cpu usage %d, Render half rate", - GetCurrentThreadId(), cpu); - _deliverInHalfFrameRate = true; - } - else - { - // Check if rendering takes too long time - if (_nrOfTooLongRenderTimes > 15 || totalRenderTime - >= WindowsThreadCpuUsage::CPU_CHECK_INTERVAL) - { - - // The rendering is taking too long time - if (_deliverInHalfFrameRate == false) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Render half rate, tid: %d", - GetCurrentThreadId()); - _deliverInHalfFrameRate = true; - } - else if (_deliverInScreenType == false - && !_deliverInQuarterFrameRate) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "Applying deliver in screen type format, tid: %d", - GetCurrentThreadId()); - // 2. try RGB fix - std::map::iterator it; - it = _directDrawChannels.begin(); - while (it != _directDrawChannels.end()) - { - it->second->ChangeDeliverColorFormat(true); - it++; - } - _deliverInScreenType = true; - } - else if (_deliverInQuarterFrameRate == false) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "Render quarter rate and disable deliver in screen type format, tid: %d", - GetCurrentThreadId()); - _deliverInQuarterFrameRate = true; - if (_deliverInScreenType) - { - //Disable RGB fix - std::map::iterator it; - it = _directDrawChannels.begin(); - while (it != _directDrawChannels.end()) - { - it->second->ChangeDeliverColorFormat(false); - it++; - } - _deliverInScreenType = false; - } - } - else if (_deliverInQuarterFrameRate == true - && !_deliverInScreenType) - { - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "Render quarter rate and enable RGB fix, tid: %d", - GetCurrentThreadId()); - _deliverInQuarterFrameRate = true; - - //Enabe RGB fix - std::map::iterator it; - it = _directDrawChannels.begin(); - while (it != _directDrawChannels.end()) - { - it->second->ChangeDeliverColorFormat(true); - it++; - } - _deliverInScreenType = true; - } - } - } - _nrOfTooLongRenderTimes = 0; // Reset count of too long render times. - } -} - -/* - * Internal help functions for blitting - */ - -bool VideoRenderDirectDraw::HasHWNDChanged() -{ - // we check if the HWND has changed - if (!_fullscreen) - { - RECT currentRect; - ::GetClientRect(_hWnd, ¤tRect); - if (!EqualRect(¤tRect, &_hwndRect)) - { - int retVal = CreateMixingSurface(); // this will delete the old mixing surface - if (retVal != 0) - { - return false; - } - return true; - } - } - return false; -} - -int VideoRenderDirectDraw::BlitFromOffscreenBuffersToMixingBuffer() -{ - bool updateAll = false; // used to minimize the number of blt - - DDBLTFX ddbltfx; - ZeroMemory(&ddbltfx, sizeof(ddbltfx)); - ddbltfx.dwSize = sizeof(ddbltfx); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; - - if (_mixingSurface == NULL) - { - int retVal = CreateMixingSurface(); - if (retVal != 0) - { - // trace done - return retVal; - } - } - RECT mixingRect; - ::SetRectEmpty(&mixingRect); - - if (_fullscreen) - { - ::CopyRect(&mixingRect, &_screenRect); - } - else - { - ::CopyRect(&mixingRect, &_hwndRect); - // what if largest size is larger than screen - if (mixingRect.right > _screenRect.right) - { - mixingRect.right = _screenRect.right; - } - if (mixingRect.bottom > _screenRect.bottom) - { - mixingRect.bottom = _screenRect.bottom; - } - } - if (!EqualRect(&_mixingRect, &mixingRect)) - { - // size changed - CopyRect(&_mixingRect, &mixingRect); - FillSurface(_mixingSurface, &mixingRect); - updateAll = true; - } - - if (_clearMixingSurface) - { - FillSurface(_mixingSurface, &_mixingRect); - _clearMixingSurface = false; - updateAll = true; - } - - std::multimap::reverse_iterator it; - it = _directDrawZorder.rbegin(); - while (it != _directDrawZorder.rend()) - { - // loop through all channels and streams in Z order - short streamID = (it->second >> 16); - int channel = it->second & 0x0000ffff; - - std::map::iterator ddIt; - ddIt = _directDrawChannels.find(channel); - if (ddIt != _directDrawChannels.end()) - { - // found the channel - DirectDrawChannel* channelObj = ddIt->second; - if (channelObj && _mixingSurface) - { - if (updateAll || channelObj->IsOffScreenSurfaceUpdated(this)) - { - updateAll = true; - if (channelObj->BlitFromOffscreenBufferToMixingBuffer( - this, - streamID, - _mixingSurface, - _mixingRect, - _demuxing) - != 0) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, - -1, - "DirectDraw error BlitFromOffscreenBufferToMixingBuffer "); - _mixingSurface->Release(); - _mixingSurface = NULL; - } - } - } - } - it++; - } - return 0; -} - -int VideoRenderDirectDraw::BlitFromTextToMixingBuffer() -{ - if (_directDraw == NULL) - { - return -1; - } - if (!_mixingSurface) - { - return -1; - } - if (_textSettings.empty()) - { - return 0; - } - - HDC hdcDDSurface; - HRESULT res = _mixingSurface->GetDC(&hdcDDSurface); - if (res != S_OK) - { - return -1; - } - // - std::map::reverse_iterator it; - it = _textSettings.rbegin(); - - while (it != _textSettings.rend()) - { - DirectDrawTextSettings* settings = it->second; - it++; - if (settings == NULL) - { - continue; - } - SetTextColor(hdcDDSurface, settings->_colorRefText); - SetBkColor(hdcDDSurface, settings->_colorRefBackground); - - if (settings->_transparent) - { - SetBkMode(hdcDDSurface, TRANSPARENT); // do we need to call this all the time? - } - else - { - SetBkMode(hdcDDSurface, OPAQUE); // do we need to call this all the time? - } - RECT textRect; - textRect.left = int(_mixingRect.right * settings->_textLeft); - textRect.right = int(_mixingRect.right * settings->_textRight); - textRect.top = int(_mixingRect.bottom * settings->_textTop); - textRect.bottom = int(_mixingRect.bottom * settings->_textBottom); - - DrawTextA(hdcDDSurface, settings->_ptrText, settings->_textLength, - &textRect, DT_LEFT); - } - _mixingSurface->ReleaseDC(hdcDDSurface); - return 0; -} - -int VideoRenderDirectDraw::BlitFromBitmapBuffersToMixingBuffer() -{ - HRESULT ddrval; - DDBLTFX ddbltfx; - ZeroMemory(&ddbltfx, sizeof(ddbltfx)); - ddbltfx.dwSize = sizeof(ddbltfx); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; - - if (_directDraw == NULL) - { - return -1; // signal that we are not ready for the change - } - - std::map::reverse_iterator it; - it = _bitmapSettings.rbegin(); - - while (it != _bitmapSettings.rend()) - { - DirectDrawBitmapSettings* settings = it->second; - it++; - if (settings == NULL) - { - continue; - } - - // Color keying lets you set colors on a surface to be completely transparent. - // always blit _transparentBitmapSurface last - if (_mixingSurface && settings->_transparentBitmapSurface - && settings->_transparentBitmapWidth - && settings->_transparentBitmapHeight) - { - DWORD signal = DDBLT_WAIT | DDBLT_DDFX; - // Set transparent color - if (settings->_transparentBitmapColorKey) - { - signal |= DDBLT_KEYSRC; - settings->_transparentBitmapSurface->SetColorKey( - DDCKEY_SRCBLT, - settings->_transparentBitmapColorKey); - } - - // Now we can blt the transparent surface to another surface - RECT srcRect; - SetRect(&srcRect, 0, 0, settings->_transparentBitmapWidth, - settings->_transparentBitmapHeight); - - RECT dstRect; - if (settings->_transparentBitmapLeft - != settings->_transparentBitmapRight - && settings->_transparentBitmapTop - != settings->_transparentBitmapBottom) - { - CopyRect(&dstRect, &_mixingRect); - dstRect.left = (int) (dstRect.right - * settings->_transparentBitmapLeft); - dstRect.right = (int) (dstRect.right - * settings->_transparentBitmapRight); - dstRect.top = (int) (dstRect.bottom - * settings->_transparentBitmapTop); - dstRect.bottom = (int) (dstRect.bottom - * settings->_transparentBitmapBottom); - } - else - { - - // if left, right, top and bottom are describing one point use the original size - CopyRect(&dstRect, &srcRect); - POINT startp; - startp.x = (int) (_mixingRect.right - * settings->_transparentBitmapLeft); - startp.y = (int) (_mixingRect.bottom - * settings->_transparentBitmapTop); - OffsetRect(&dstRect, startp.x, startp.y); - - // make sure that we blit inside our surface - if (dstRect.bottom > _mixingRect.bottom) - { - srcRect.bottom -= dstRect.bottom - _mixingRect.bottom; - // sanity - if (srcRect.bottom < 0) - { - srcRect.bottom = 0; - } - dstRect.bottom = _mixingRect.bottom; - } - if (dstRect.right > _mixingRect.right) - { - srcRect.right -= dstRect.right - _mixingRect.right; - // sanity - if (srcRect.right < 0) - { - srcRect.right = 0; - } - dstRect.right = _mixingRect.right; - } - } - // ddbltfx.dwDDFX |= DDBLTFX_MIRRORUPDOWN; //only for test requires hw support - - // wait for the _mixingSurface to be available - ddrval = _mixingSurface->Blt(&dstRect, - settings->_transparentBitmapSurface, - &srcRect, signal, &ddbltfx); - if (ddrval == DDERR_SURFACELOST) - { - if (!::GetForegroundWindow()) - { - // no window, i.e the user have clicked CTRL+ALT+DEL - return 0; - } - // always re-creted via the SetBitmap call - settings->_transparentBitmapSurface->Release(); - settings->_transparentBitmapSurface = NULL; - - _clearMixingSurface = true; - - if (settings->_transparentBitMap) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw re-set transparent bitmap"); - settings->SetBitmap(_trace, _directDraw); - } - } - else if (ddrval != DD_OK) - { - settings->_transparentBitmapSurface->Release(); - settings->_transparentBitmapSurface = NULL; - WEBRTC_TRACE( - kTraceInfo, - kTraceVideo, - -1, - "DirectDraw blt error 0x%x _transparentBitmapSurface", - ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - } - } - return 0; -} - -/** - * normal blitting - */ -int VideoRenderDirectDraw::BlitFromMixingBufferToFrontBuffer( - bool hwndChanged, - int& waitTime) -{ - DDBLTFX ddbltfx; - ZeroMemory(&ddbltfx, sizeof(ddbltfx)); - ddbltfx.dwSize = sizeof(ddbltfx); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; - RECT rcRectDest; - - // test for changing mode - /* for(int i= 0; i< 6000000; i ++) - { - rcRectDest.left = i; - } - */ - - if (IsRectEmpty(&_mixingRect)) - { - // no error just nothing to blit - return 0; - } - if (_mixingSurface == NULL) - { - // The mixing surface has probably been deleted - // and we haven't had time to restore it yet. Wait... - return 0; - } - if (_primarySurface == NULL) - { - int retVal = CreatePrimarySurface(); - if (retVal != 0) - { - // tracing done - return retVal; - } - } - - // first we need to figure out where on the primary surface our window lives - ::GetWindowRect(_hWnd, &rcRectDest); - - DWORD signal = DDBLT_WAIT | DDBLT_DDFX; - - // Set transparent color - if (_transparentBackground) - { - signal |= DDBLT_KEYSRC; - DDCOLORKEY ColorKey; - ColorKey.dwColorSpaceLowValue = RGB(0, 0, 0); - ColorKey.dwColorSpaceHighValue = RGB(0, 0, 0); - _mixingSurface->SetColorKey(DDCKEY_SRCBLT, &ColorKey); - } - - if (_renderModeWaitForCorrectScanLine) - { - // wait for previus draw to complete - DWORD scanLines = 0; - DWORD screenLines = _screenRect.bottom - 1; // scanlines start on 0 - DWORD screenLines90 = (screenLines * 9) / 10; // % of the screen is rendered - //waitTime=GET_TIME_IN_MS(); - waitTime = ::timeGetTime(); - HRESULT hr = _directDraw->GetScanLine(&scanLines); - while (screenLines90 > scanLines && hr == DD_OK) - { - _confCritSect->Leave(); - _fullScreenWaitEvent->Wait(3); - _confCritSect->Enter(); - if (_directDraw == NULL) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - hr = _directDraw->GetScanLine(&scanLines); - } - //waitTime=GET_TIME_IN_MS()-waitTime; - waitTime = ::timeGetTime() - waitTime; - } - - HRESULT ddrval = _primarySurface->Blt(&rcRectDest, _mixingSurface, - &_mixingRect, signal, &ddbltfx); - if (ddrval == DDERR_SURFACELOST) - { - if (!::GetForegroundWindow()) - { - // no window, i.e the user have clicked CTRL+ALT+DEL - return 0; - } - ddrval = _primarySurface->Restore(); - if (ddrval == DD_OK) // Try again - { - ddrval = _primarySurface->Blt(&rcRectDest, _mixingSurface, - &_mixingRect, signal, &ddbltfx); - } - if (ddrval != DD_OK) // If restore failed or second time blt failed. Delete the surface. It will be recreated next time. - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - -1, - "DirectDraw failed to restore lost _primarySurface 0x%x", - ddrval); - _primarySurface->Release(); - _primarySurface = NULL; - if (_mixingSurface) - { - _mixingSurface->Release(); - _mixingSurface = NULL; - } - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw restored lost _primarySurface"); - } - else if (ddrval == DDERR_EXCEPTION) - { - _primarySurface->Release(); - _primarySurface = NULL; - if (_mixingSurface) - { - _mixingSurface->Release(); - _mixingSurface = NULL; - } - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw exception in _primarySurface"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (ddrval != DD_OK) - { - if (ddrval != 0x80004005) // Undefined error. Ignore - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw blt error 0x%x _primarySurface", ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - } - return 0; -} - -/** - * fullscreen mode blitting - */ - -int VideoRenderDirectDraw::WaitAndFlip(int& waitTime) -{ - if (_primarySurface == NULL) - { - // no trace, too much in the file - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (_directDraw == NULL) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - // wait for previus draw to complete - DWORD scanLines = 0; - DWORD screenLines = _screenRect.bottom - 1; // scanlines start on 0 - DWORD screenLines90 = (screenLines * 9) / 10; // % of the screen is rendered - - //waitTime=GET_TIME_IN_MS(); - waitTime = ::timeGetTime(); - HRESULT hr = _directDraw->GetScanLine(&scanLines); - while (screenLines90 > scanLines && hr == DD_OK) - { - _confCritSect->Leave(); - _fullScreenWaitEvent->Wait(3); - _confCritSect->Enter(); - if (_directDraw == NULL) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - hr = _directDraw->GetScanLine(&scanLines); - } - //waitTime=GET_TIME_IN_MS()-waitTime; - waitTime = ::timeGetTime() - waitTime; - if (screenLines > scanLines) - { - // this function sucks a lot of the CPU... but it's worth it - _directDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL); - } - - // schedule a flip - HRESULT ddrval = _primarySurface->Flip(NULL, DDFLIP_WAIT); // schedule flip DDFLIP_WAIT - if (ddrval == DDERR_SURFACELOST) - { - if (!::GetForegroundWindow()) - { - // no window, i.e the user have clicked CTRL+ALT+DEL - return 0; - } - //if(::IsIconic(_hWnd)) - //{ - // need to do this before Restore - //WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDraw our window is an icon maximize it "); - // When the full screen window is switched out by ALT-TAB or ALT-CTRL-DEL-TASKMANAGER, - // this call will hang the app. Remove it to fix the problem. - // FIXME: - // 1) Why we want to active and max the window when it was minimized? - // 2) Why this is needed before restore? We didn't do that in non full screen mode. - //::ShowWindow(_hWnd, SW_SHOWMAXIMIZED); - //} - ddrval = _primarySurface->Restore(); - if (ddrval != DD_OK) - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - -1, - "DirectDraw failed to restore _primarySurface, in flip, 0x%x", - ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw restore _primarySurface in flip"); - - } - else if (ddrval != DD_OK) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - return 0; -} - -int VideoRenderDirectDraw::BlitFromMixingBufferToBackBuffer() -{ - if (_backSurface == NULL) - { - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - if (IsRectEmpty(&_mixingRect)) - { - // nothing to blit - return 0; - } - DDBLTFX ddbltfx; - ZeroMemory(&ddbltfx, sizeof(ddbltfx)); - ddbltfx.dwSize = sizeof(ddbltfx); - ddbltfx.dwDDFX = DDBLTFX_NOTEARING; - - // wait for the _backSurface to be available - HRESULT ddrval = _backSurface->Blt(&_screenRect, _mixingSurface, - &_mixingRect, DDBLT_WAIT | DDBLT_DDFX, - &ddbltfx); - if (ddrval == DDERR_SURFACELOST) - { - if (!::GetForegroundWindow()) - { - // no window, i.e the user have clicked CTRL+ALT+DEL - return 0; - } - //if(::IsIconic(_hWnd)) - //{ - // need to do this before Restore - //WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDraw our window is an icon maximize it "); - //WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDraw show our window is an icon maximize it "); - // When the full screen window is switch out by ALT-TAB or ALT-CTRL-DEL-TASKMANAGER, - // this call will hang the app. Remove it to fix the problem. - // FIXME: - // 1) Why we want to active and max the window when it was minimized? - // 2) Why this is needed before restore? We didn't do that in non full screen mode. - //::ShowWindow(_hWnd, SW_SHOWMAXIMIZED); - //} - ddrval = _primarySurface->Restore(); - if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw failed to restore _primarySurface"); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "DirectDraw restored _primarySurface"); - - _clearMixingSurface = true; - - } - else if (ddrval != DD_OK) - { - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, - "DirectDraw blt error 0x%x _backSurface", ddrval); - return -1; - //return VIDEO_DIRECT_DRAW_FAILURE; - } - return 0; -} - -/* - Saving the code for using a clip list instead of HWND, problem was that other transparent - HWNDs caused us not to update an area or that we painted in other HWNDs area. - - RECT hWndRect; - ::GetWindowRect(_hWnd, &hWndRect); - - LPRGNDATA lpClipList = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT)); - - // now fill out all the structure fields - memcpy(lpClipList->Buffer, &hWndRect, sizeof(RECT)); - - ::CopyRect(&(lpClipList->rdh.rcBound), &hWndRect); - lpClipList->rdh.dwSize = sizeof(RGNDATAHEADER); - lpClipList->rdh.iType = RDH_RECTANGLES; - lpClipList->rdh.nCount = 1; - lpClipList->rdh.nRgnSize = sizeof(RECT) * lpClipList->rdh.nCount; - ddrval= _directDrawClipper->SetClipList(lpClipList, 0); - - void Visible(HWND hwnd, HRGN &hRgn) - { - if (!IsWindowVisible(hwnd)) // If the window is visible - { - if(CombineRgn(hRgn, hRgn, hRgn, RGN_XOR) == NULLREGION) - { - return; - } - } - // Gets the topmost window - HWND hWnd=GetTopWindow(NULL); - while (hWnd != NULL && hWnd != hwnd) // If the window is above in Z-order - { - if (IsWindowVisible(hWnd)) // If the window is visible - { - RECT Rect; - // Gets window dimension - GetWindowRect(hWnd, &Rect); - // Creates a region corresponding to the window - if(Rect.left > 0) // test fo rnow - { - HRGN hrgnWnd = CreateRectRgn(Rect.left, Rect.top, Rect.right, Rect.bottom); - // int err = GetUpdateRgn(hWnd, hrgnWnd, FALSE); - // Creates a region corresponding to region not overlapped - if(CombineRgn(hRgn, hRgn, hrgnWnd, RGN_DIFF) == COMPLEXREGION) - { - int a = 0; - } - DeleteObject(hrgnWnd); - } - } - // Loops through all windows till the specified window - hWnd = GetWindow(hWnd, GW_HWNDNEXT); - } - - HRGN region; - region = CreateRectRgn(0, 0, 500, 500); - - // Get the affected region - // if (GetUpdateRgn(_hWnd, region, FALSE) != ERROR) - HDC dc = GetDC(_hWnd); - if(GetClipRgn(dc, region) > 0) - { - int buffsize; - UINT x; - RGNDATA *buff; - POINT TopLeft; - - // Get the top-left point of the client area - TopLeft.x = 0; - TopLeft.y = 0; - if (!ClientToScreen(_hWnd, &TopLeft)) - { - int a = 0; - } - - - // Get the size of buffer required - buffsize = GetRegionData(region, 0, 0); - if (buffsize != 0) - { - buff = (RGNDATA *) new BYTE [buffsize]; - if (buff == NULL) - { - int a = 0; - } - - // Now get the region data - if(GetRegionData(region, buffsize, buff)) - { - if(buff->rdh.nCount > 0) - { - ::OffsetRect(&(buff->rdh.rcBound), TopLeft.x, TopLeft.y); - for (x=0; x<(buff->rdh.nCount); x++) - { - RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT))); - ::OffsetRect(urect, TopLeft.x, TopLeft.y); - char logStr[256]; - _snprintf(logStr,256, "rect T:%d L:%d B:%d R:%d\n",urect->top, urect->left, urect->bottom, urect->right); - OutputDebugString(logStr); - - } - OutputDebugString("\n"); - _directDrawClipper->SetClipList(buff, 0); - } - LPRGNDATA lpClipList = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT) * buff->rdh.nCount); - if(buff->rdh.nCount > 0) - { - _directDrawClipper->SetClipList(lpClipList, 0); - - lpClipList-> - DWORD size = sizeof(RGNDATAHEADER) + sizeof(RECT)* buff->rdh.nCount; - lpClipList->rdh.dwSize = sizeof(RGNDATAHEADER); - lpClipList->rdh.iType = RDH_RECTANGLES; - lpClipList->rdh.nCount = 1; - - HRESULT ddrval1 = _directDrawClipper->GetClipList(NULL, lpClipList, &size); - memcpy(lpClipList->Buffer, &rcRectDest, sizeof(RECT)); - ::CopyRect(&(lpClipList->rdh.rcBound), &rcRectDest); - _directDrawClipper->SetClipList(lpClipList, 0); - } } - - for (x=0; x<(buff->rdh.nCount); x++) - { - // Obtain the rectangles from the list - RECT *urect = (RECT *) (((BYTE *) buff) + sizeof(RGNDATAHEADER) + (x * sizeof(RECT))); - int a = 0; - - } - delete lpClipList; - } - delete buff; - } - } - */ -/* - void VideoRenderDirectDraw::Wait() - { - // wait for previus draw to complete - int count = 0; - DWORD scanLines = 0; - DWORD screenLines = _screenRect.bottom -1; // scanlines start on 0 - DWORD screenLines75 = (screenLines*3)/4; // % of the screen is rendered - HRESULT hr = DD_OK; - if(_directDraw == NULL) - { - return; - } - hr =_directDraw->GetScanLine(&scanLines); - while ( screenLines75 > scanLines && hr == DD_OK) - { - // _confCritSect->Leave(); - _screenEvent->Wait(10); - // _confCritSect->Enter(); - if(_directDraw == NULL) - { - return; - } - hr = _directDraw->GetScanLine(&scanLines); - } - } - */ - -WebRtc_Word32 VideoRenderDirectDraw::StartRender() -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirectDraw::StopRender() -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return 0; -} - -WebRtc_Word32 VideoRenderDirectDraw::ChangeWindow(void* window) -{ - WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); - return -1; -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/windows/video_render_directdraw.h b/modules/video_render/main/source/windows/video_render_directdraw.h deleted file mode 100644 index 65781c7ae..000000000 --- a/modules/video_render/main/source/windows/video_render_directdraw.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECTDRAW_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECTDRAW_H_ - -#include "typedefs.h" -#include "i_video_render_win.h" -#include "vplib.h" - -#include "ddraw.h" -#include -#include - -// Added -#include "video_render_defines.h" - -#pragma comment(lib, "ddraw.lib") // located in DirectX SDK - -namespace webrtc { -class CriticalSectionWrapper; -class EventWrapper; -class ThreadWrapper; -class Trace; - -class VideoRenderDirectDraw; - -// some typedefs to make it easy to test different versions -typedef IDirectDraw7 DirectDraw; -typedef IDirectDrawSurface7 DirectDrawSurface; -typedef DDSURFACEDESC2 DirectDrawSurfaceDesc; -typedef DDSCAPS2 DirectDrawCaps; -typedef std::pair ZorderPair; - -class WindowsThreadCpuUsage -{ -public: - WindowsThreadCpuUsage(); - int GetCpuUsage(); //in % since last call - DWORD LastGetCpuTime() - { - return _lastGetCpuUsageTime; - } - const enum - { - CPU_CHECK_INTERVAL = 1000 - }; -private: - _int64 _lastCpuUsageTime; - DWORD _lastGetCpuUsageTime; - int _lastCpuUsage; - HANDLE _hThread; - int _cores; -}; - -class DirectDrawStreamSettings -{ -public: - DirectDrawStreamSettings(); - - float _startWidth; - float _stopWidth; - float _startHeight; - float _stopHeight; - - float _cropStartWidth; - float _cropStopWidth; - float _cropStartHeight; - float _cropStopHeight; -}; - -class DirectDrawBitmapSettings -{ -public: - DirectDrawBitmapSettings(); - ~DirectDrawBitmapSettings(); - - int SetBitmap(Trace* trace, DirectDraw* directDraw); - - HBITMAP _transparentBitMap; - float _transparentBitmapLeft; - float _transparentBitmapRight; - float _transparentBitmapTop; - float _transparentBitmapBottom; - int _transparentBitmapWidth; - int _transparentBitmapHeight; - DDCOLORKEY* _transparentBitmapColorKey; - DirectDrawSurface* _transparentBitmapSurface; // size of bitmap image -}; - -class DirectDrawTextSettings -{ -public: - DirectDrawTextSettings(); - ~DirectDrawTextSettings(); - - int SetText(const char* text, int textLength, COLORREF colorText, - COLORREF colorBg, float left, float top, float right, - float bottom); - - char* _ptrText; - WebRtc_UWord32 _textLength; - COLORREF _colorRefText; - COLORREF _colorRefBackground; - float _textLeft; - float _textRight; - float _textTop; - float _textBottom; - bool _transparent; -}; - -class DirectDrawChannel: public VideoRenderCallback -{ -public: - DirectDrawChannel(DirectDraw* directDraw, - VideoType blitVideoType, - VideoType incomingVideoType, - VideoType screenVideoType, - VideoRenderDirectDraw* owner); - - int FrameSizeChange(int width, int height, int numberOfStreams); - int DeliverFrame(unsigned char* buffer, int buffeSize, - unsigned int timeStamp90KHz); - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame); - - int ChangeDeliverColorFormat(bool useScreenType); - - void AddRef(); - void Release(); - - void SetStreamSettings(VideoRenderDirectDraw* DDObj, short streamId, - float startWidth, float startHeight, - float stopWidth, float stopHeight); - void SetStreamCropSettings(VideoRenderDirectDraw* DDObj, - short streamId, float startWidth, - float startHeight, float stopWidth, - float stopHeight); - - int GetStreamSettings(VideoRenderDirectDraw* DDObj, short streamId, - float& startWidth, float& startHeight, - float& stopWidth, float& stopHeight); - - void GetLargestSize(RECT* mixingRect); - int - BlitFromOffscreenBufferToMixingBuffer( - VideoRenderDirectDraw* DDObj, - short streamID, - DirectDrawSurface* mixingSurface, - RECT &dstRect, bool demuxing); - bool IsOffScreenSurfaceUpdated(VideoRenderDirectDraw* DDobj); - -protected: - virtual ~DirectDrawChannel(); - -private: - CriticalSectionWrapper* _critSect; // protect members from change while using them - int _refCount; - int _width; - int _height; - int _numberOfStreams; - bool _deliverInScreenType; - bool _doubleBuffer; - DirectDraw* _directDraw; - DirectDrawSurface* _offScreenSurface; // size of incoming stream - DirectDrawSurface* _offScreenSurfaceNext; // size of incoming stream - VideoType _blitVideoType; - VideoType _originalBlitVideoType; - VideoType _incomingVideoType; - VideoType _screenVideoType; - enum - { - MAX_FRAMEDELIVER_TIME = 20 - }; //Maximum time it might take to deliver a frame (process time in DeliverFrame) - enum - { - MAX_NO_OF_LATE_FRAMEDELIVER_TIME = 10 - }; //No of times we allow DeliverFrame process time to exceed MAX_FRAMEDELIVER_TIME before we take action. - VideoFrame _tempRenderBuffer; - - std::map - _streamIdToSettings; - bool _offScreenSurfaceUpdated; - VideoRenderDirectDraw* _owner; -}; - -class VideoRenderDirectDraw: IVideoRenderWin -{ -public: - VideoRenderDirectDraw(Trace* trace, HWND hWnd, bool fullscreen); - ~VideoRenderDirectDraw(); -public: - //IVideoRenderWin - - /************************************************************************** - * - * Init - * - ***************************************************************************/ - virtual WebRtc_Word32 Init(); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - virtual VideoRenderCallback - * CreateChannel(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, const float left, - const float top, const float right, - const float bottom); - - virtual WebRtc_Word32 DeleteChannel(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 GetStreamSettings(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom); - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual bool IsFullScreen(); - - virtual WebRtc_Word32 SetCropping(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - virtual WebRtc_Word32 GetGraphicsMemory(WebRtc_UWord64& totalMemory, - WebRtc_UWord64& availableMemory); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 colorText, - const WebRtc_UWord32 colorBg, - const float left, const float top, - const float rigth, const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 channel, - const WebRtc_UWord16 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); -public: - - // Used for emergency stops... - int Stop(); - - DirectDrawChannel* ShareDirectDrawChannel(int channel); - DirectDrawChannel* ConfigureDirectDrawChannel(int channel, - unsigned char streamID, - int zOrder, float left, - float top, float right, - float bottom); - - int AddDirectDrawChannel(int channel, unsigned char streamID, int zOrder, - DirectDrawChannel*); - - VideoType GetPerferedVideoFormat(); - bool HasChannels(); - bool HasChannel(int channel); - bool DeliverInScreenType(); - int GetChannels(std::list& channelList); - - // code for getting graphics settings - int GetScreenResolution(int& screenWidth, int& screenHeight); - int UpdateSystemCPUUsage(int systemCPU); - - int SetBitmap(HBITMAP bitMap, unsigned char pictureId, - DDCOLORKEY* colorKey, float left, float top, float rigth, - float bottom); - - bool IsPrimaryOrMixingSurfaceOnSystem(); - bool CanBltFourCC() - { - return _bCanBltFourcc; - } - -protected: - static bool RemoteRenderingThreadProc(void* obj); - bool RemoteRenderingProcess(); - -private: - int CheckCapabilities(); - int CreateMixingSurface(); - int CreatePrimarySurface(); - - int FillSurface(DirectDrawSurface *pDDSurface, RECT* rect); - int DrawOnSurface(unsigned char* buffer, int buffeSize); - int BlitFromOffscreenBuffersToMixingBuffer(); - int BlitFromBitmapBuffersToMixingBuffer(); - int BlitFromTextToMixingBuffer(); - - bool HasHWNDChanged(); - void DecideBestRenderingMode(bool hwndChanged, int totalRenderTime); - - // in fullscreen flip mode - int WaitAndFlip(int& waitTime); - int BlitFromMixingBufferToBackBuffer(); - - // in normal window mode - int BlitFromMixingBufferToFrontBuffer(bool hwndChanged, int& waitTime); - - // private members - Trace* _trace; - CriticalSectionWrapper* _confCritSect; // protect members from change while using them - - bool _fullscreen; - bool _demuxing; - bool _transparentBackground; - bool _supportTransparency; - bool _canStretch; - bool _canMirrorLeftRight; - bool _clearMixingSurface; - bool _deliverInScreenType; - bool _renderModeWaitForCorrectScanLine; - bool _deliverInHalfFrameRate; - bool _deliverInQuarterFrameRate; - bool _bCanBltFourcc; - bool _frameChanged; // True if a frame has changed or bitmap or text has changed. - int _processCount; - HWND _hWnd; - RECT _screenRect; // whole screen as a rect - RECT _mixingRect; - RECT _originalHwndRect; - RECT _hwndRect; - - VideoType _incomingVideoType; - VideoType _blitVideoType; - VideoType _rgbVideoType; - - DirectDraw* _directDraw; - DirectDrawSurface* _primarySurface; // size of screen - DirectDrawSurface* _backSurface; // size of screen - DirectDrawSurface* _mixingSurface; // size of screen - - std::map _bitmapSettings; - std::map _textSettings; - std::map _directDrawChannels; - std::multimap _directDrawZorder; - - EventWrapper* _fullScreenWaitEvent; - EventWrapper* _screenEvent; - ThreadWrapper* _screenRenderThread; - WindowsThreadCpuUsage _screenRenderCpuUsage; - - int _lastRenderModeCpuUsage; - - // Used for emergency stop caused by OnDisplayChange - bool _blit; - - //code for providing graphics settings - DWORD _totalMemory; - DWORD _availableMemory; - int _systemCPUUsage; - - // Variables used for checking render time - int _maxAllowedRenderTime; - int _nrOfTooLongRenderTimes; - bool _isPrimaryOrMixingSurfaceOnSystem; -}; - -} //namespace webrtc - - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECTDRAW_H_ diff --git a/modules/video_render/main/source/windows/video_render_windows_impl.cc b/modules/video_render/main/source/windows/video_render_windows_impl.cc deleted file mode 100644 index a5231a020..000000000 --- a/modules/video_render/main/source/windows/video_render_windows_impl.cc +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "engine_configurations.h" -#include "video_render_windows_impl.h" - -#include "critical_section_wrapper.h" -#include "trace.h" -#ifdef DIRECTDRAW_RENDERING -#include "video_render_directdraw.h" -#endif -#ifdef DIRECT3D9_RENDERING -#include "video_render_direct3d9.h" -#endif - -#include - -namespace webrtc { - -VideoRenderWindowsImpl::VideoRenderWindowsImpl( - const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, - const bool fullscreen) : - _id(id), - _renderWindowsCritsect( - *CriticalSectionWrapper::CreateCriticalSection()), - _prtWindow(window), _fullscreen(fullscreen), _ptrRendererWin(NULL) -{ -} - -VideoRenderWindowsImpl::~VideoRenderWindowsImpl() -{ - delete &_renderWindowsCritsect; - if (_ptrRendererWin) - { - delete _ptrRendererWin; - _ptrRendererWin = NULL; - } -} - -WebRtc_Word32 VideoRenderWindowsImpl::Init() -{ - //LogOSAndHardwareDetails(); - CheckHWAcceleration(); - - _renderMethod = kVideoRenderWinD3D9; - - // Create the win renderer - switch (_renderMethod) - { - case kVideoRenderWinDd: - { -#ifdef DIRECTDRAW_RENDERING - VideoRenderDirectDraw* ptrRenderer; - ptrRenderer = new VideoRenderDirectDraw(NULL, (HWND) _prtWindow, _fullscreen); - if (ptrRenderer == NULL) - { - break; - } - _ptrRendererWin = reinterpret_cast(ptrRenderer); -#else - return NULL; -#endif //DIRECTDRAW_RENDERING - } - break; - case kVideoRenderWinD3D9: - { -#ifdef DIRECT3D9_RENDERING - VideoRenderDirect3D9* ptrRenderer; - ptrRenderer = new VideoRenderDirect3D9(NULL, (HWND) _prtWindow, _fullscreen); - if (ptrRenderer == NULL) - { - break; - } - _ptrRendererWin = reinterpret_cast(ptrRenderer); -#else - return NULL; -#endif //DIRECT3D9_RENDERING - } - break; - default: - break; - } - - //Init renderer - if (_ptrRendererWin) - return _ptrRendererWin->Init(); - else - return -1; -} - -WebRtc_Word32 VideoRenderWindowsImpl::ChangeUniqueId(const WebRtc_Word32 id) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - _id = id; - return 0; -} - -WebRtc_Word32 VideoRenderWindowsImpl::ChangeWindow(void* window) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - return -1; - } - else - { - return _ptrRendererWin->ChangeWindow(window); - } -} - -VideoRenderCallback* -VideoRenderWindowsImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - VideoRenderCallback* renderCallback = NULL; - - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - renderCallback = _ptrRendererWin->CreateChannel(streamId, zOrder, left, - top, right, bottom); - } - - return renderCallback; -} - -WebRtc_Word32 VideoRenderWindowsImpl::DeleteIncomingRenderStream( - const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->DeleteChannel(streamId); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::GetIncomingRenderStreamProperties( - const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, - float& top, - float& right, - float& bottom) const -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - zOrder = 0; - left = 0; - top = 0; - right = 0; - bottom = 0; - - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->GetStreamSettings(streamId, 0, zOrder, left, - top, right, bottom); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::StartRender() -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->StartRender(); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::StopRender() -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->StopRender(); - } - return error; -} - -VideoRenderType VideoRenderWindowsImpl::RenderType() -{ - return kRenderWindows; -} - -RawVideoType VideoRenderWindowsImpl::PerferedVideoType() -{ - return kVideoI420; -} - -bool VideoRenderWindowsImpl::FullScreen() -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - bool fullscreen = false; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - fullscreen = _ptrRendererWin->IsFullScreen(); - } - return fullscreen; -} - -WebRtc_Word32 VideoRenderWindowsImpl::GetGraphicsMemory( - WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const -{ - if (_ptrRendererWin) - { - return _ptrRendererWin->GetGraphicsMemory(totalGraphicsMemory, - availableGraphicsMemory); - } - - totalGraphicsMemory = 0; - availableGraphicsMemory = 0; - return -1; -} - -WebRtc_Word32 VideoRenderWindowsImpl::GetScreenResolution( - WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - screenWidth = 0; - screenHeight = 0; - return 0; -} - -WebRtc_UWord32 VideoRenderWindowsImpl::RenderFrameRate( - const WebRtc_UWord32 streamId) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - return 0; -} - -WebRtc_Word32 VideoRenderWindowsImpl::SetStreamCropping( - const WebRtc_UWord32 streamId, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->SetCropping(streamId, 0, left, top, right, - bottom); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::ConfigureRenderer( - const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->ConfigureRenderer(streamId, 0, zOrder, left, - top, right, bottom); - } - - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::SetTransparentBackground( - const bool enable) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->SetTransparentBackground(enable); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::SetText( - const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->SetText(textId, text, textLength, - textColorRef, backgroundColorRef, - left, top, right, bottom); - } - return error; -} - -WebRtc_Word32 VideoRenderWindowsImpl::SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, - const float left, - const float top, - const float right, - const float bottom) -{ - CriticalSectionScoped cs(_renderWindowsCritsect); - WebRtc_Word32 error = -1; - if (!_ptrRendererWin) - { - WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, - _id, "%s, no renderer", __FUNCTION__); - } - else - { - error = _ptrRendererWin->SetBitmap(bitMap, pictureId, colorKey, left, - top, right, bottom); - } - return error; -} - -void VideoRenderWindowsImpl::LogOSAndHardwareDetails() -{ - HRESULT hr; - IDxDiagProvider* m_pDxDiagProvider = NULL; - IDxDiagContainer* m_pDxDiagRoot = NULL; - - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - bool coUninitializeIsRequired = true; - if (FAILED(hr)) - { - // Avoid calling CoUninitialize() since CoInitializeEx() failed. - coUninitializeIsRequired = false; - if (hr == RPC_E_CHANGED_MODE) - { - // Calling thread has already initialized COM to be used in a single-threaded - // apartment (STA). We are then prevented from using STA. - // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is set". - // - WEBRTC_TRACE( - kTraceWarning, - kTraceVideoRenderer, - _id, - "VideoRenderWindowsImpl::LogOSAndHardwareDetails() CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) => RPC_E_CHANGED_MODE, error 0x%x", - hr); - } - } - - hr = CoCreateInstance(CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, - IID_IDxDiagProvider, (LPVOID*) &m_pDxDiagProvider); - - if (FAILED(hr) || m_pDxDiagProvider == NULL) - { - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize - // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are - // digital signed as logo'd by WHQL which may connect via internet to update - // WHQL certificates. - DXDIAG_INIT_PARAMS dxDiagInitParam; - ZeroMemory(&dxDiagInitParam, sizeof(DXDIAG_INIT_PARAMS)); - - dxDiagInitParam.dwSize = sizeof(DXDIAG_INIT_PARAMS); - dxDiagInitParam.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION; - dxDiagInitParam.bAllowWHQLChecks = TRUE; - dxDiagInitParam.pReserved = NULL; - - hr = m_pDxDiagProvider->Initialize(&dxDiagInitParam); - if (FAILED(hr)) - { - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - hr = m_pDxDiagProvider->GetRootContainer(&m_pDxDiagRoot); - if (FAILED(hr) || m_pDxDiagRoot == NULL) - { - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - IDxDiagContainer* pObject = NULL; - - hr = m_pDxDiagRoot->GetChildContainer(L"DxDiag_SystemInfo", &pObject); - if (FAILED(hr) || pObject == NULL) - { - m_pDxDiagRoot->Release(); - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - TCHAR m_szDirectXVersionLongEnglish[100]; - TCHAR m_szOSLocalized[100]; - TCHAR m_szProcessorEnglish[200]; - TCHAR m_szSystemManufacturerEnglish[200]; - - ZeroMemory(m_szDirectXVersionLongEnglish, sizeof(TCHAR) * 100); - ZeroMemory(m_szOSLocalized, sizeof(TCHAR) * 100); - ZeroMemory(m_szProcessorEnglish, sizeof(TCHAR) * 200); - ZeroMemory(m_szSystemManufacturerEnglish, sizeof(TCHAR) * 200); - - GetStringValue( pObject, L"szDirectXVersionLongEnglish", - EXPAND(m_szDirectXVersionLongEnglish) ); - GetStringValue(pObject, L"szOSLocalized", EXPAND(m_szOSLocalized) ); - GetStringValue(pObject, L"szProcessorEnglish", EXPAND(m_szProcessorEnglish) ); - GetStringValue( pObject, L"szSystemManufacturerEnglish", - EXPAND(m_szSystemManufacturerEnglish) ); - - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "System Manufacturer --- %s", - m_szSystemManufacturerEnglish); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Processor --- %s", m_szProcessorEnglish); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Operating System --- %s", m_szOSLocalized); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "DirectX Version --- %s", - m_szDirectXVersionLongEnglish); - - if (pObject) - pObject->Release(); - - struct DisplayInfo - { - TCHAR m_szDescription[200]; - TCHAR m_szManufacturer[200]; - TCHAR m_szChipType[100]; - TCHAR m_szDisplayMemoryEnglish[100]; - TCHAR m_szDisplayModeEnglish[100]; - TCHAR m_szDriverName[100]; - TCHAR m_szDriverVersion[100]; - TCHAR m_szDDStatusEnglish[100]; - TCHAR m_szD3DStatusEnglish[100]; - BOOL m_bDDAccelerationEnabled; - BOOL m_bNoHardware; - BOOL m_b3DAccelerationExists; - BOOL m_b3DAccelerationEnabled; - }; - - WCHAR wszContainer[256]; - IDxDiagContainer* pContainer = NULL; - - DWORD nInstanceCount = 0; - DWORD nItem = 0; - DWORD nCurCount = 0; - - // Get the IDxDiagContainer object called "DxDiag_DisplayDevices". - // This call may take some time while dxdiag gathers the info. - if (FAILED(hr = m_pDxDiagRoot->GetChildContainer(L"DxDiag_DisplayDevices", - &pContainer))) - { - m_pDxDiagRoot->Release(); - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - if (FAILED(hr = pContainer->GetNumberOfChildContainers(&nInstanceCount))) - { - pContainer->Release(); - m_pDxDiagRoot->Release(); - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - DisplayInfo *pDisplayInfo = new DisplayInfo; - if (pDisplayInfo == NULL) - return; - ZeroMemory(pDisplayInfo, sizeof(DisplayInfo)); - - hr = pContainer->EnumChildContainerNames(nItem, wszContainer, 256); - if (FAILED(hr)) - { - delete pDisplayInfo; - pContainer->Release(); - m_pDxDiagRoot->Release(); - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - hr = pContainer->GetChildContainer(wszContainer, &pObject); - if (FAILED(hr) || pObject == NULL) - { - delete pDisplayInfo; - pContainer->Release(); - m_pDxDiagRoot->Release(); - m_pDxDiagProvider->Release(); - if (coUninitializeIsRequired) - CoUninitialize(); - return; - } - - GetStringValue( pObject, L"szDescription", - EXPAND(pDisplayInfo->m_szDescription) ); - GetStringValue( pObject, L"szManufacturer", - EXPAND(pDisplayInfo->m_szManufacturer) ); - GetStringValue(pObject, L"szChipType", EXPAND(pDisplayInfo->m_szChipType) ); - GetStringValue( pObject, L"szDisplayMemoryEnglish", - EXPAND(pDisplayInfo->m_szDisplayMemoryEnglish) ); - GetStringValue( pObject, L"szDisplayModeEnglish", - EXPAND(pDisplayInfo->m_szDisplayModeEnglish) ); - GetStringValue( pObject, L"szDriverName", - EXPAND(pDisplayInfo->m_szDriverName) ); - GetStringValue( pObject, L"szDriverVersion", - EXPAND(pDisplayInfo->m_szDriverVersion) ); - GetBoolValue(pObject, L"bDDAccelerationEnabled", - &pDisplayInfo->m_bDDAccelerationEnabled); - GetBoolValue(pObject, L"bNoHardware", &pDisplayInfo->m_bNoHardware); - GetBoolValue(pObject, L"bDDAccelerationEnabled", - &pDisplayInfo->m_bDDAccelerationEnabled); - GetBoolValue(pObject, L"b3DAccelerationExists", - &pDisplayInfo->m_b3DAccelerationExists); - GetBoolValue(pObject, L"b3DAccelerationEnabled", - &pDisplayInfo->m_b3DAccelerationEnabled); - GetStringValue( pObject, L"szDDStatusEnglish", - EXPAND(pDisplayInfo->m_szDDStatusEnglish)); - GetStringValue( pObject, L"szD3DStatusEnglish", - EXPAND(pDisplayInfo->m_szD3DStatusEnglish)); - - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Device Name --- %s", - pDisplayInfo->m_szDescription); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Device Manufacturer --- %s", - pDisplayInfo->m_szManufacturer); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Device ChipType --- %s", - pDisplayInfo->m_szChipType); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Approx. Total Device Memory --- %s", - pDisplayInfo->m_szDisplayMemoryEnglish); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Current Display Mode --- %s", - pDisplayInfo->m_szDisplayModeEnglish); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Device Driver Name --- %s", - pDisplayInfo->m_szDriverName); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "Device Driver Version --- %s", - pDisplayInfo->m_szDriverVersion); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "DirectDraw Acceleration Enabled --- %s", - pDisplayInfo->m_szDescription ? "Enabled" : "Disabled"); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "bNoHardware --- %s", - pDisplayInfo->m_bNoHardware ? "Enabled" : "Disabled"); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "b3DAccelerationExists Enabled --- %s", - pDisplayInfo->m_b3DAccelerationExists ? "Enabled" : "Disabled"); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "b3DAccelerationEnabled Enabled --- %s", - pDisplayInfo->m_b3DAccelerationEnabled ? "Enabled" - : "Disabled"); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "DDraw Status --- %s", - pDisplayInfo->m_szDDStatusEnglish); - WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1, - "D3D Status --- %s", - pDisplayInfo->m_szD3DStatusEnglish); - - // Get OS version - OSVERSIONINFOEX osvie; - osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - GetVersionEx((LPOSVERSIONINFO) & osvie); - /* - Operating system Version number dwMajorVersion dwMinorVersion - Windows 7 6.1 6 1 - Windows Server 2008 R2 6.1 6 1 - Windows Server 2008 6.0 6 0 - Windows Vista 6.0 6 0 - Windows Server 2003 R2 5.2 5 2 - Windows Server 2003 5.2 5 2 - Windows XP 5.1 5 1 - Windows 2000 5.0 5 0 - */ - //RDP problem exists only when XP is involved - if (osvie.dwMajorVersion < 6) - { - WEBRTC_TRACE(kTraceStateInfo, kTraceVideoRenderer, _id, - "Checking for RDP driver"); - if (_tcsncmp(pDisplayInfo->m_szDriverName, _T("RDPDD.dll"), 9) == 0) - { - // - } - } - - if (pObject) - { - pObject->Release(); - pObject = NULL; - } - - if (pContainer) - pContainer->Release(); - - if (m_pDxDiagProvider) - m_pDxDiagProvider->Release(); - - if (m_pDxDiagRoot) - m_pDxDiagRoot->Release(); - - if (pDisplayInfo) - delete pDisplayInfo; - - if (coUninitializeIsRequired) - CoUninitialize(); - - return; -} - -//----------------------------------------------------------------------------- -// Name: GetStringValue() -// Desc: Get a string value from a IDxDiagContainer object -//----------------------------------------------------------------------------- -HRESULT VideoRenderWindowsImpl::GetStringValue(IDxDiagContainer* pObject, - WCHAR* wstrName, - TCHAR* strValue, int nStrLen) -{ - HRESULT hr; - VARIANT var; - VariantInit(&var); - - if (FAILED(hr = pObject->GetProp(wstrName, &var))) - return hr; - - if (var.vt != VT_BSTR) - return E_INVALIDARG; - -#ifdef _UNICODE - wcsncpy( strValue, var.bstrVal, nStrLen-1 ); -#else - wcstombs(strValue, var.bstrVal, nStrLen); -#endif - strValue[nStrLen - 1] = TEXT('\0'); - VariantClear(&var); - - return S_OK; -} - -//----------------------------------------------------------------------------- -// Name: GetBoolValue() -// Desc: Get a BOOL value from a IDxDiagContainer object -//----------------------------------------------------------------------------- -HRESULT VideoRenderWindowsImpl::GetBoolValue(IDxDiagContainer* pObject, - WCHAR* wstrName, BOOL* pbValue) -{ - HRESULT hr; - VARIANT var; - VariantInit(&var); - - if (FAILED(hr = pObject->GetProp(wstrName, &var))) - return hr; - - if (var.vt != VT_BOOL) - return E_INVALIDARG; - - *pbValue = (var.boolVal != 0); - VariantClear(&var); - - return S_OK; -} - -int VideoRenderWindowsImpl::CheckHWAcceleration() -{ - // Read the registry to check if HW acceleration is enabled or not. - HKEY regKey; - DWORD value = 0; - DWORD valueLength = 4; - - bool directDraw = true; - bool direct3D = true; - bool dci = true; - - // DirectDraw - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\DirectDraw"), - 0, KEY_QUERY_VALUE, ®Key) == ERROR_SUCCESS) - { - // We have the registry key - value = 0; - if (RegQueryValueEx(regKey, _T("EmulationOnly"), NULL, NULL, - (BYTE*) &value, &valueLength) == ERROR_SUCCESS) - { - if (value == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw acceleration is disabled"); - directDraw = false; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DirectDraw acceleration is enabled"); - } - } - else - { - // Could not get the value for this one. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not find EmulationOnly key, DirectDraw acceleration is probably enabled"); - } - RegCloseKey(regKey); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not open DirectDraw settings"); - } - - // Direct3D - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, - _T("SOFTWARE\\Microsoft\\Direct3D\\Drivers"), 0, - KEY_QUERY_VALUE, ®Key) == ERROR_SUCCESS) - { - // We have the registry key - value = 0; - if (RegQueryValueEx(regKey, _T("SoftwareOnly"), NULL, NULL, - (BYTE*) &value, &valueLength) == ERROR_SUCCESS) - { - if (value == 1) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "Direct3D acceleration is disabled"); - direct3D = false; - } - else - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "Direct3D acceleration is enabled"); - } - } - else - { - // Could not get the value for this one. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not find SoftwarOnly key, Direct3D acceleration is probably enabled"); - } - RegCloseKey(regKey); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not open Direct3D settings"); - } - - // DCI - if (RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - _T( - "SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\DCI"), - 0, KEY_QUERY_VALUE, ®Key) == ERROR_SUCCESS) - { - // We have found the registry key - value = 0; - if (RegQueryValueEx(regKey, _T("Timeout"), NULL, NULL, (BYTE*) &value, - &valueLength) == ERROR_SUCCESS) - { - if (value == 0) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DCI - DirectDraw acceleration is disabled"); - dci = false; - } - else if (value == 7) - { - WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1, - "DCI is fully enabled"); - } - else - { - WEBRTC_TRACE( - kTraceWarning, - kTraceVideo, - -1, - "DCI - DirectDraw acceleration is enabled, but short timeout: %d", - value); - } - } - else - { - // Could not get the value for this one. - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not find Timeout key"); - } - RegCloseKey(regKey); - } - else - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Could not open DCI settings"); - } - - // We don't care about Direct3D right now... - if (dci == false || directDraw == false) - { - return -1; - } - - return 0; -} - -void VideoRenderWindowsImpl::CheckHWDriver(bool& badDriver, - bool& fullAccelerationEnabled) -{ - // Read the registry to check if HW acceleration is enabled or not. - HKEY regKey; - DWORD value = 0; - DWORD valueLength = 4; - - //Assume the best - badDriver = false; - fullAccelerationEnabled = true; - - // Check the path to the currently used driver - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\VIDEO"), 0, - KEY_QUERY_VALUE, ®Key) == ERROR_SUCCESS) - { - // We have found the registry key containing the driver location - value = 0; - DWORD driverPathLen = 512; - TCHAR driverPath[512]; - memset(driverPath, 0, driverPathLen * sizeof(TCHAR)); - DWORD dwType = REG_SZ; - - long retVal = RegQueryValueEx(regKey, _T("\\Device\\Video0"), NULL, - NULL, (BYTE*) driverPath, &driverPathLen); - - // Close the key... - RegCloseKey(regKey); - - if (retVal == ERROR_SUCCESS) - { - // We have the path to the currently used video card - - // trueDriverPath = modified nameStr, from above, that works - // for RegOpenKeyEx - TCHAR trueDriverPath[512]; - memset(trueDriverPath, 0, 512 * sizeof(TCHAR)); - - // Convert the path to correct format. - // - Remove \Registry\Machine\ - // - Replace '\' with '\\' - // Should be something like this: System\\CurrentControlSet\\Control\\Video\\{F6987E15-F12C-4B15-8C84-0F635F3F09EA}\\0000" - int idx = 0; - for (DWORD i = 18; i < (driverPathLen / sizeof(TCHAR)); i++) - { - trueDriverPath[idx++] = driverPath[i]; - if (driverPath[i] == _T('\\')) - { - trueDriverPath[idx++] = driverPath[i]; - } - } - - // Open the driver key - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, trueDriverPath, 0, - KEY_QUERY_VALUE, ®Key) == ERROR_SUCCESS) - { - TCHAR driverName[64]; - memset(driverName, 0, 64 * sizeof(TCHAR)); - DWORD driverNameLength = 64; - retVal = RegQueryValueEx(regKey, _T("drv"), NULL, NULL, - (BYTE*) driverName, &driverNameLength); - if (retVal == ERROR_SUCCESS) - { - WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, - "Graphics card driver name: %s", driverName); - } - DWORD accLevel = 0; - DWORD accLevelS = sizeof(accLevel); - - RegQueryValueEx(regKey, _T("Acceleration.Level"), NULL, NULL, - (LPBYTE) & accLevel, &accLevelS); - //Don't care if the key is not found. It probably means that acceleration is enabled - if (accLevel != 0) - { - // Close the key... - RegCloseKey(regKey); - - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, trueDriverPath, 0, - KEY_SET_VALUE, ®Key) == ERROR_SUCCESS) - { - // try setting it to full - accLevel = 0; - LONG retVal; - retVal = RegSetValueEx(regKey, - _T("Acceleration.Level"), NULL, - REG_DWORD, (PBYTE) & accLevel, - sizeof(DWORD)); - if (retVal != ERROR_SUCCESS) - { - fullAccelerationEnabled = false; - } - else - { - RegQueryValueEx(regKey, _T("Acceleration.Level"), - NULL, NULL, (LPBYTE) & accLevel, - &accLevelS); - if (accLevel != 0) - { - fullAccelerationEnabled = false; - } - else - { - fullAccelerationEnabled = true; - } - } - } - else - { - fullAccelerationEnabled = false; - } - } - else - { - fullAccelerationEnabled = true; - } - - // Close the key... - RegCloseKey(regKey); - } - } - } -} - -} //namespace webrtc - diff --git a/modules/video_render/main/source/windows/video_render_windows_impl.h b/modules/video_render/main/source/windows/video_render_windows_impl.h deleted file mode 100644 index bdc0a0a40..000000000 --- a/modules/video_render/main/source/windows/video_render_windows_impl.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ -#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ - -#include -#include - -#include "i_video_render.h" -#include "i_video_render_win.h" - -namespace webrtc { -class CriticalSectionWrapper; - -#define EXPAND(x) x, sizeof(x)/sizeof(TCHAR) - -#pragma comment(lib, "dxguid.lib") - -enum VideoRenderWinMethod -{ - kVideoRenderWinDd = 0, kVideoRenderWinD3D9 = 1 -}; - -// Class definitions -class VideoRenderWindowsImpl: IVideoRender -{ -public: - /* - * Constructor/destructor - */ - - VideoRenderWindowsImpl(const WebRtc_Word32 id, - const VideoRenderType videoRenderType, - void* window, const bool fullscreen); - - virtual ~VideoRenderWindowsImpl(); - - virtual WebRtc_Word32 Init(); - - virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); - - virtual WebRtc_Word32 ChangeWindow(void* window); - - /************************************************************************** - * - * Incoming Streams - * - ***************************************************************************/ - - virtual VideoRenderCallback - * AddIncomingRenderStream(const WebRtc_UWord32 streamId, - const WebRtc_UWord32 zOrder, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 - DeleteIncomingRenderStream(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 - GetIncomingRenderStreamProperties(const WebRtc_UWord32 streamId, - WebRtc_UWord32& zOrder, - float& left, float& top, - float& right, float& bottom) const; - - /************************************************************************** - * - * Start/Stop - * - ***************************************************************************/ - - virtual WebRtc_Word32 StartRender(); - - virtual WebRtc_Word32 StopRender(); - - /************************************************************************** - * - * Properties - * - ***************************************************************************/ - - virtual VideoRenderType RenderType(); - - virtual RawVideoType PerferedVideoType(); - - virtual bool FullScreen(); - - virtual WebRtc_Word32 - GetGraphicsMemory(WebRtc_UWord64& totalGraphicsMemory, - WebRtc_UWord64& availableGraphicsMemory) const; - - virtual WebRtc_Word32 - GetScreenResolution(WebRtc_UWord32& screenWidth, - WebRtc_UWord32& screenHeight) const; - - virtual WebRtc_UWord32 RenderFrameRate(const WebRtc_UWord32 streamId); - - virtual WebRtc_Word32 SetStreamCropping(const WebRtc_UWord32 streamId, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 ConfigureRenderer(const WebRtc_UWord32 streamId, - const unsigned int zOrder, - const float left, const float top, - const float right, - const float bottom); - - virtual WebRtc_Word32 SetTransparentBackground(const bool enable); - - virtual WebRtc_Word32 SetText(const WebRtc_UWord8 textId, - const WebRtc_UWord8* text, - const WebRtc_Word32 textLength, - const WebRtc_UWord32 textColorRef, - const WebRtc_UWord32 backgroundColorRef, - const float left, const float top, - const float right, const float bottom); - - virtual WebRtc_Word32 SetBitmap(const void* bitMap, - const WebRtc_UWord8 pictureId, - const void* colorKey, const float left, - const float top, const float right, - const float bottom); - - static int CheckHWAcceleration(); - static void CheckHWDriver(bool& badDriver, bool& fullAccelerationEnabled); - -private: - - void LogOSAndHardwareDetails(); - HRESULT GetBoolValue(IDxDiagContainer* pObject, WCHAR* wstrName, - BOOL* pbValue); - HRESULT GetStringValue(IDxDiagContainer* pObject, WCHAR* wstrName, - TCHAR* strValue, int nStrLen); - - WebRtc_Word32 _id; - CriticalSectionWrapper& _renderWindowsCritsect; - - void* _prtWindow; - bool _fullscreen; - - VideoRenderWinMethod _renderMethod; - IVideoRenderWin* _ptrRendererWin; -}; - -} //namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ diff --git a/modules/video_render/main/test/testAPI/renderStartImage.bmp b/modules/video_render/main/test/testAPI/renderStartImage.bmp deleted file mode 100644 index c443a58f6cb2a7b13066f6db91819cbc54ac4cc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304182 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 wY)ZI-2do=7@HFJ_C6q~&fr!tGN - -#if defined(_WIN32) -#include -#include -#include -#include -#include -#include -#include -#include - -#elif defined(WEBRTC_LINUX) - -#include -#include -#include -#include - -#elif defined(WEBRTC_MAC_INTEL) - -#import -#import -#import -#import -#include -#include -using namespace std; - -@class CocoaRenderView; - -#endif - -#include "common_types.h" -#include "process_thread.h" -#include "module_common_types.h" -#include "video_render_defines.h" -#include "video_render.h" -#include "tick_util.h" -#include "trace.h" - -using namespace webrtc; - -void GetTestVideoFrame(WebRtc_UWord8* frame, - WebRtc_Word32 width, - WebRtc_Word32 height, - WebRtc_UWord8 startColor); -int TestSingleStream(VideoRender* renderModule); -int TestFullscreenStream(VideoRender* &renderModule, - void* window, - const VideoRenderType videoRenderType); -int TestBitmapText(VideoRender* renderModule); -int TestMultipleStreams(VideoRender* renderModule); -int TestExternalRender(VideoRender* renderModule); - -#define TEST_FRAME_RATE 30 -#define TEST_TIME_SECOND 5 -#define TEST_FRAME_NUM (TEST_FRAME_RATE*TEST_TIME_SECOND) -#define TEST_STREAM0_START_COLOR 0 -#define TEST_STREAM1_START_COLOR 64 -#define TEST_STREAM2_START_COLOR 128 -#define TEST_STREAM3_START_COLOR 192 - -#if defined(_WIN32) && defined(_DEBUG) -// #include "vld.h" -#define SLEEP(x) ::Sleep(x) -#elif defined(WEBRTC_LINUX) - -#define GET_TIME_IN_MS timeGetTime() -#define SLEEP(x) Sleep(x) - -void Sleep(unsigned long x) -{ - timespec t; - t.tv_sec = x/1000; - t.tv_nsec = (x-(x/1000)*1000)*1000000; - nanosleep(&t,NULL); -} - -unsigned long timeGetTime() -{ - struct timeval tv; - struct timezone tz; - unsigned long val; - - gettimeofday(&tv, &tz); - val= tv.tv_sec*1000+ tv.tv_usec/1000; - return(val); -} - -#elif defined(WEBRTC_MAC_INTEL) - -#define GET_TIME_IN_MS timeGetTime() -#define SLEEP(x) usleep(x * 1000) - -unsigned long timeGetTime() -{ - return 0; -} - -#else - -#define GET_TIME_IN_MS ::timeGetTime() -#define SLEEP(x) ::Sleep(x) - -#endif - -using namespace std; - -#if defined(_WIN32) -LRESULT CALLBACK WebRtcWinProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) -{ - switch(uMsg) - { - case WM_DESTROY: - break; - case WM_COMMAND: - break; - } - return DefWindowProc(hWnd,uMsg,wParam,lParam); -} - -int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height) -{ - HINSTANCE hinst = GetModuleHandle(0); - WNDCLASSEX wcx; - wcx.hInstance = hinst; - wcx.lpszClassName = TEXT("VideoRenderTest"); - wcx.lpfnWndProc = (WNDPROC)WebRtcWinProc; - wcx.style = CS_DBLCLKS; - wcx.hIcon = LoadIcon (NULL, IDI_APPLICATION); - wcx.hIconSm = LoadIcon (NULL, IDI_APPLICATION); - wcx.hCursor = LoadCursor (NULL, IDC_ARROW); - wcx.lpszMenuName = NULL; - wcx.cbSize = sizeof (WNDCLASSEX); - wcx.cbClsExtra = 0; - wcx.cbWndExtra = 0; - wcx.hbrBackground = GetSysColorBrush(COLOR_3DFACE); - - // Register our window class with the operating system. - // If there is an error, exit program. - if ( !RegisterClassEx (&wcx) ) - { - MessageBox( 0, TEXT("Failed to register window class!"),TEXT("Error!"), MB_OK|MB_ICONERROR ); - return 0; - } - - // Create the main window. - hwndMain = CreateWindowEx( - 0, // no extended styles - TEXT("VideoRenderTest"), // class name - TEXT("VideoRenderTest Window"), // window name - WS_OVERLAPPED |WS_THICKFRAME, // overlapped window - 800, // horizontal position - 0, // vertical position - width, // width - height, // height - (HWND) NULL, // no parent or owner window - (HMENU) NULL, // class menu used - hinst, // instance handle - NULL); // no window creation data - - if (!hwndMain) - { - int error = GetLastError(); - return -1; - } - - // Show the window using the flag specified by the program - // that started the application, and send the application - // a WM_PAINT message. - - ShowWindow(hwndMain, SW_SHOWDEFAULT); - UpdateWindow(hwndMain); - return 0; -} - -#elif defined(WEBRTC_LINUX) - -int WebRtcCreateWindow(Window *outWindow, Display **outDisplay, int winNum, int width, int height) // unsigned char* title, int titleLength) - -{ - int screen, xpos = 10, ypos = 10; - XEvent evnt; - XSetWindowAttributes xswa; // window attribute struct - XVisualInfo vinfo; // screen visual info struct - unsigned long mask; // attribute mask - - // get connection handle to xserver - Display* _display = XOpenDisplay( NULL ); - - // get screen number - screen = DefaultScreen(_display); - - // put desired visual info for the screen in vinfo - if( XMatchVisualInfo(_display, screen, 24, TrueColor, &vinfo) != 0 ) - { - //printf( "Screen visual info match!\n" ); - } - - // set window attributes - xswa.colormap = XCreateColormap(_display, DefaultRootWindow(_display), vinfo.visual, AllocNone); - xswa.event_mask = StructureNotifyMask | ExposureMask; - xswa.background_pixel = 0; - xswa.border_pixel = 0; - - // value mask for attributes - mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - switch( winNum ) - { - case 0: - xpos = 200; - ypos = 200; - break; - case 1: - xpos = 300; - ypos = 200; - break; - default: - break; - } - - // create a subwindow for parent (defroot) - Window _window = XCreateWindow(_display, DefaultRootWindow(_display), - xpos, ypos, - width, - height, - 0, vinfo.depth, - InputOutput, - vinfo.visual, - mask, &xswa); - - // Set window name - if( winNum == 0 ) - { - XStoreName(_display, _window, "VE MM Local Window"); - XSetIconName(_display, _window, "VE MM Local Window"); - } - else if( winNum == 1 ) - { - XStoreName(_display, _window, "VE MM Remote Window"); - XSetIconName(_display, _window, "VE MM Remote Window"); - } - - // make x report events for mask - XSelectInput(_display, _window, StructureNotifyMask); - - // map the window to the display - XMapWindow(_display, _window); - - // wait for map event - do - { - XNextEvent(_display, &evnt); - } - while (evnt.type != MapNotify || evnt.xmap.event != _window); - - *outWindow = _window; - *outDisplay = _display; - - return 0; -} - -#elif defined(WEBRTC_MAC_INTEL) // LINUX -int WebRtcCreateWindow(CocoaRenderView*& cocoaRenderer, int winNum, int width, int height) // unsigned char* title, int titleLength) - -{ - // In Cocoa, rendering is not done directly to a window like in Windows and Linux. - // It is rendererd to a Subclass of NSOpenGLView - - // create cocoa container window - NSRect outWindowFrame = NSMakeRect(200, 800, width + 20, height + 20); - NSWindow* outWindow = [[NSWindow alloc] initWithContentRect:outWindowFrame styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]; - [outWindow orderOut:nil]; - [outWindow setTitle:@"Cocoa Renderer"]; - [outWindow setBackgroundColor:[NSColor blueColor]]; - - // create renderer and attach to window - NSRect cocoaRendererFrame = NSMakeRect(10, 10, width, height); - cocoaRenderer = [[CocoaRenderView alloc] initWithFrame:cocoaRendererFrame]; - [[outWindow contentView] addSubview:cocoaRenderer]; - - [outWindow makeKeyAndOrderFront:NSApp]; - - return 0; -} -#endif - -class MyRenderCallback: public VideoRenderCallback -{ -public: - MyRenderCallback() : - _cnt(0) - { - } - ; - ~MyRenderCallback() - { - } - ; - virtual WebRtc_Word32 RenderFrame(const WebRtc_UWord32 streamId, - VideoFrame& videoFrame) - { - _cnt++; - if (_cnt % 100 == 0) - { - printf("Render callback %d \n",_cnt); - } - return 0; - } - WebRtc_Word32 _cnt; -}; - -void GetTestVideoFrame(WebRtc_UWord8* frame, - WebRtc_Word32 width, - WebRtc_Word32 height, - WebRtc_UWord8 startColor) { - // changing color - static WebRtc_UWord8 color = startColor; - - WebRtc_UWord8* destY = frame; - WebRtc_UWord8* destU = &frame[width*height]; - WebRtc_UWord8* destV = &frame[width*height*5/4]; - //Y - for (WebRtc_Word32 y=0; y<(width*height); y++) - { - destY[y] = color; - } - //U - for (WebRtc_Word32 u=0; u<(width*height/4); u++) - { - destU[u] = color; - } - //V - for (WebRtc_Word32 v=0; v<(width*height/4); v++) - { - destV[v] = color; - } - - color++; -} - -int TestSingleStream(VideoRender* renderModule) { - int error = 0; - // Add settings for a stream to render - printf("Add stream 0 to entire window\n"); - const int streamId0 = 0; - VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); - assert(renderCallback0 != NULL); - -#if defined (WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER) - MyRenderCallback externalRender; - renderModule->AddExternalRenderCallback(streamId0, &externalRender); -#endif - - printf("Start render\n"); - error = renderModule->StartRender(streamId0); - assert(error == 0); - - // Loop through an I420 file and render each frame - const WebRtc_UWord32 width = 352; - const WebRtc_UWord32 height = 288; - const WebRtc_UWord32 numBytes = (WebRtc_UWord32)(1.5 * width * height); - - VideoFrame videoFrame0; - videoFrame0.VerifyAndAllocate(numBytes); - - const WebRtc_UWord32 renderDelayMs = 500; - - for (int i=0; iRenderFrame(streamId0, videoFrame0); - SLEEP(1000/TEST_FRAME_RATE); - } - - videoFrame0.Free(); - - // Shut down - printf("Closing...\n"); - error = renderModule->StopRender(streamId0); - assert(error == 0); - - error = renderModule->DeleteIncomingRenderStream(streamId0); - assert(error == 0); - - return 0; -} - -int TestFullscreenStream(VideoRender* &renderModule, - void* window, - const VideoRenderType videoRenderType) { - int error = 0; - - VideoRender::DestroyVideoRender(renderModule); - renderModule = VideoRender::CreateVideoRender(12345, window, true, videoRenderType); - - TestSingleStream(renderModule); - - VideoRender::DestroyVideoRender(renderModule); - renderModule = VideoRender::CreateVideoRender(12345, window, false, videoRenderType); - - return 0; -} - -int TestBitmapText(VideoRender* renderModule) { -#if defined(WIN32) - - int error = 0; - // Add settings for a stream to render - printf("Add stream 0 to entire window\n"); - const int streamId0 = 0; - VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); - assert(renderCallback0 != NULL); - - printf("Adding Bitmap\n"); - DDCOLORKEY ColorKey; // black - ColorKey.dwColorSpaceHighValue = RGB(0, 0, 0); - ColorKey.dwColorSpaceLowValue = RGB(0, 0, 0); - HBITMAP hbm = (HBITMAP)LoadImage(NULL, - (LPCTSTR)_T("renderStartImage.bmp"), - IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - renderModule->SetBitmap(hbm, 0, &ColorKey, 0.0f, 0.0f, 0.3f, - 0.3f); - - printf("Adding Text\n"); - renderModule->SetText(1, (WebRtc_UWord8*) "WebRtc Render Demo App", 20, - RGB(255, 0, 0), RGB(0, 0, 0), 0.25f, 0.1f, 1.0f, - 1.0f); - - printf("Start render\n"); - error = renderModule->StartRender(streamId0); - assert(error == 0); - - // Loop through an I420 file and render each frame - const WebRtc_UWord32 width = 352; - const WebRtc_UWord32 height = 288; - const WebRtc_UWord32 numBytes = (WebRtc_UWord32)(1.5 * width * height); - - VideoFrame videoFrame0; - videoFrame0.VerifyAndAllocate(numBytes); - - const WebRtc_UWord32 renderDelayMs = 500; - - for (int i=0; iRenderFrame(streamId0, videoFrame0); - SLEEP(1000/TEST_FRAME_RATE); - } - videoFrame0.Free(); - // Sleep and let all frames be rendered before closing - SLEEP(renderDelayMs*2); - - - // Shut down - printf("Closing...\n"); - ColorKey.dwColorSpaceHighValue = RGB(0,0,0); - ColorKey.dwColorSpaceLowValue = RGB(0,0,0); - renderModule->SetBitmap(NULL, 0, &ColorKey, 0.0f, 0.0f, 0.0f, 0.0f); - renderModule->SetText(1, NULL, 20, RGB(255,255,255), - RGB(0,0,0), 0.0f, 0.0f, 0.0f, 0.0f); - - error = renderModule->StopRender(streamId0); - assert(error == 0); - - error = renderModule->DeleteIncomingRenderStream(streamId0); - assert(error == 0); -#endif - - return 0; -} - -int TestMultipleStreams(VideoRender* renderModule) { - int error = 0; - // Add settings for a stream to render - printf("Add stream 0\n"); - const int streamId0 = 0; - VideoRenderCallback* renderCallback0 = - renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 0.45f, 0.45f); - assert(renderCallback0 != NULL); - printf("Add stream 1\n"); - const int streamId1 = 1; - VideoRenderCallback* renderCallback1 = - renderModule->AddIncomingRenderStream(streamId1, 0, 0.55f, 0.0f, 1.0f, 0.45f); - assert(renderCallback1 != NULL); - printf("Add stream 2\n"); - const int streamId2 = 2; - VideoRenderCallback* renderCallback2 = - renderModule->AddIncomingRenderStream(streamId2, 0, 0.0f, 0.55f, 0.45f, 1.0f); - assert(renderCallback2 != NULL); - printf("Add stream 3\n"); - const int streamId3 = 3; - VideoRenderCallback* renderCallback3 = - renderModule->AddIncomingRenderStream(streamId3, 0, 0.55f, 0.55f, 1.0f, 1.0f); - assert(renderCallback3 != NULL); - assert(renderModule->StartRender(streamId0) == 0); - assert(renderModule->StartRender(streamId1) == 0); - assert(renderModule->StartRender(streamId2) == 0); - assert(renderModule->StartRender(streamId3) == 0); - - // Loop through an I420 file and render each frame - const WebRtc_UWord32 width = 352; - const WebRtc_UWord32 height = 288; - const WebRtc_UWord32 numBytes = (WebRtc_UWord32)(1.5 * width * height); - - VideoFrame videoFrame0; - videoFrame0.VerifyAndAllocate(numBytes); - VideoFrame videoFrame1; - videoFrame1.VerifyAndAllocate(numBytes); - VideoFrame videoFrame2; - videoFrame2.VerifyAndAllocate(numBytes); - VideoFrame videoFrame3; - videoFrame3.VerifyAndAllocate(numBytes); - - const WebRtc_UWord32 renderDelayMs = 500; - - for (int i=0; iRenderFrame(streamId0, videoFrame0); - - GetTestVideoFrame(videoFrame1.Buffer(), width, height, TEST_STREAM1_START_COLOR); - videoFrame1.SetRenderTime(TickTime::MillisecondTimestamp() + renderDelayMs); // Render this frame with the specified delay - videoFrame1.SetWidth(width); - videoFrame1.SetHeight(height); - videoFrame1.SetLength(numBytes); - renderCallback1->RenderFrame(streamId1, videoFrame1); - - GetTestVideoFrame(videoFrame2.Buffer(), width, height, TEST_STREAM2_START_COLOR); - videoFrame2.SetRenderTime(TickTime::MillisecondTimestamp() + renderDelayMs); // Render this frame with the specified delay - videoFrame2.SetWidth(width); - videoFrame2.SetHeight(height); - videoFrame2.SetLength(numBytes); - renderCallback2->RenderFrame(streamId2, videoFrame2); - - GetTestVideoFrame(videoFrame3.Buffer(), width, height, TEST_STREAM3_START_COLOR); - videoFrame3.SetRenderTime(TickTime::MillisecondTimestamp() + renderDelayMs); // Render this frame with the specified delay - videoFrame3.SetWidth(width); - videoFrame3.SetHeight(height); - videoFrame3.SetLength(numBytes); - renderCallback3->RenderFrame(streamId3, videoFrame3); - - SLEEP(1000/TEST_FRAME_RATE); - } - - videoFrame0.Free(); - videoFrame1.Free(); - videoFrame2.Free(); - videoFrame3.Free(); - - // Shut down - printf("Closing...\n"); - assert(renderModule->StopRender(streamId0) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId0) == 0); - assert(renderModule->StopRender(streamId1) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId1) == 0); - assert(renderModule->StopRender(streamId2) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId2) == 0); - assert(renderModule->StopRender(streamId3) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId3) == 0); - - return 0; -} - -int TestExternalRender(VideoRender* renderModule) { - MyRenderCallback *externalRender = new MyRenderCallback(); - - const int streamId0 = 0; - VideoRenderCallback* renderCallback0 = - renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, - 1.0f, 1.0f); - assert(renderCallback0 != NULL); - assert(renderModule->AddExternalRenderCallback(streamId0, - externalRender) == 0); - - assert(renderModule->StartRender(streamId0) == 0); - - const WebRtc_UWord32 width = 352; - const WebRtc_UWord32 height = 288; - const WebRtc_UWord32 numBytes = (WebRtc_UWord32) (1.5 * width * height); - VideoFrame videoFrame0; - videoFrame0.VerifyAndAllocate(numBytes); - - const WebRtc_UWord32 renderDelayMs = 500; - int frameCount = TEST_FRAME_NUM; - for (int i=0; iRenderFrame(streamId0, videoFrame0); - SLEEP(33); - } - - // Sleep and let all frames be rendered before closing - SLEEP(2*renderDelayMs); - videoFrame0.Free(); - - assert(renderModule->StopRender(streamId0) == 0); - assert(renderModule->DeleteIncomingRenderStream(streamId0) == 0); - assert(frameCount == externalRender->_cnt); - - delete externalRender; - externalRender = NULL; - - return 0; -} - -#if defined(_WIN32) -int _tmain(int argc, _TCHAR* argv[]) -#elif defined(WEBRTC_LINUX) -int main(int argc, char* argv[]) -#elif defined(WEBRTC_MAC_INTEL) -int main (int argc, const char * argv[]) -//int begin() -#endif -{ -#ifdef WEBRTC_MAC_INTEL - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; -#endif - - int myId = 12345; - - // Create a window for testing - void* window = NULL; -#if defined (_WIN32) - HWND testHwnd; - WebRtcCreateWindow(testHwnd, 0, 352, 288); - window = (void*)testHwnd; - VideoRenderType windowType = kRenderWindows; -#elif defined(WEBRTC_LINUX) - Window testWindow; - Display* display; - WebRtcCreateWindow(&testWindow, &display, 0, 352, 288); - VideoRenderType windowType = kRenderX11; - window = (void*)testWindow; -#elif defined(WEBRTC_MAC_INTEL) - CocoaRenderView* testWindow; - WebRtcCreateWindow(testWindow, 0, 352, 288); - VideoRenderType windowType = kRenderCocoa; - window = (void*)testWindow; -#endif - -#if defined (WEBRTC_VIDEO_EXTERNAL_CAPTURE_AND_RENDER) - windowType = kRenderExternal; -#endif - - // Create the render module - printf("Create render module\n"); - VideoRender* renderModule = NULL; - renderModule = VideoRender::CreateVideoRender(myId, - window, - false, - windowType); - assert(renderModule != NULL); - - - // ##### Test single stream rendering #### - printf("#### TestSingleStream ####\n"); - if (TestSingleStream(renderModule) != 0) { - printf ("TestSingleStream failed\n"); - } - - // ##### Test fullscreen rendering #### - printf("#### TestFullscreenStream ####\n"); - if (TestFullscreenStream(renderModule, window, windowType) != 0) { - printf ("TestFullscreenStream failed\n"); - } - - // ##### Test bitmap and text #### - printf("#### TestBitmapText ####\n"); - if (TestBitmapText(renderModule) != 0) { - printf ("TestBitmapText failed\n"); - } - - // ##### Test multiple streams #### - printf("#### TestMultipleStreams ####\n"); - if (TestMultipleStreams(renderModule) != 0) { - printf ("TestMultipleStreams failed\n"); - } - - // ##### Test multiple streams #### - printf("#### TestExternalRender ####\n"); - if (TestExternalRender(renderModule) != 0) { - printf ("TestExternalRender failed\n"); - } - - delete renderModule; - renderModule = NULL; - - printf("VideoRender unit tests passed.\n"); - -#if defined(WEBRTC_MAC_INTEL) - [pool release]; -#endif - return 0; -}

B{ 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⁣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%0Auc!+O|N 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

=3apI_7+VwpeEeJ#=GMii0kB8k0P(UF&h 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�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 z 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͙^CWPSptSfRo#|VN%hMfYrRRz3 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

+wp9N_1LDjm@%tpI!p1t$9w2^#j!r^~UOF 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

L%vq2#nGsI%ZI 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·uN$2iYpYM8-~R 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 R27b%HZyES41D|Bz{{eC{vaJ9B diff --git a/modules/audio_device/main/test/audio_short44.pcm b/modules/audio_device/main/test/audio_short44.pcm deleted file mode 100644 index 011cdce959b7f00e5a60431160df53330a33e2f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1774010 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